From b718e9d8f28be186effc87e33eba044889949e75 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Sat, 3 May 2014 08:28:56 +0200 Subject: [PATCH 01/39] Make sure copy actions are disabled when database is locked. Closes #189 --- src/gui/MainWindow.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 36fb656f2..e0c6582e0 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -313,6 +313,11 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) Q_FOREACH (QAction* action, m_ui->menuGroups->actions()) { action->setEnabled(false); } + m_ui->actionEntryCopyTitle->setEnabled(false); + m_ui->actionEntryCopyUsername->setEnabled(false); + m_ui->actionEntryCopyPassword->setEnabled(false); + m_ui->actionEntryCopyURL->setEnabled(false); + m_ui->actionEntryCopyNotes->setEnabled(false); m_ui->menuEntryCopyAttribute->setEnabled(false); m_ui->actionSearch->setEnabled(false); @@ -335,6 +340,11 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) Q_FOREACH (QAction* action, m_ui->menuGroups->actions()) { action->setEnabled(false); } + m_ui->actionEntryCopyTitle->setEnabled(false); + m_ui->actionEntryCopyUsername->setEnabled(false); + m_ui->actionEntryCopyPassword->setEnabled(false); + m_ui->actionEntryCopyURL->setEnabled(false); + m_ui->actionEntryCopyNotes->setEnabled(false); m_ui->menuEntryCopyAttribute->setEnabled(false); m_ui->actionSearch->setEnabled(false); From 9363d23e09a9981de4c8f799a91ae02eb9747aa8 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 15 May 2014 18:05:58 +0200 Subject: [PATCH 02/39] Remove dependency to Group- and EntryView from MainWindow. --- src/gui/DatabaseWidget.cpp | 20 ++++++++++++++++++++ src/gui/DatabaseWidget.h | 3 +++ src/gui/MainWindow.cpp | 19 ++++++++----------- src/gui/MainWindow.h | 1 + src/gui/entry/EntryView.cpp | 7 ++++++- src/gui/entry/EntryView.h | 1 + 6 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 419028ab8..d6d48c5e0 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -811,3 +811,23 @@ void DatabaseWidget::updateFilename(const QString& fileName) { m_filename = fileName; } + +int DatabaseWidget::numberOfSelectedEntries() +{ + return m_entryView->numberOfSelectedEntries(); +} + +QStringList DatabaseWidget::customEntryAttributes() +{ + Entry* entry = m_entryView->currentEntry(); + if (!entry) { + return QStringList(); + } + + return entry->attributes()->customKeys(); +} + +bool DatabaseWidget::isGroupSelected() +{ + return m_groupView->currentGroup() != Q_NULLPTR; +} diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index b535a9b8f..d2b4e9e05 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -71,6 +71,9 @@ public: DatabaseWidget::Mode currentMode(); void lock(); void updateFilename(const QString& filename); + int numberOfSelectedEntries(); + QStringList customEntryAttributes(); + bool isGroupSelected(); Q_SIGNALS: void closeRequest(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index e0c6582e0..98bd5aee1 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -23,15 +23,11 @@ #include "autotype/AutoType.h" #include "core/Config.h" -#include "core/Database.h" -#include "core/Entry.h" #include "core/FilePath.h" #include "core/InactivityTimer.h" #include "core/Metadata.h" #include "gui/AboutDialog.h" #include "gui/DatabaseWidget.h" -#include "gui/entry/EntryView.h" -#include "gui/group/GroupView.h" const QString MainWindow::BaseWindowTitle = "KeePassX"; @@ -40,6 +36,8 @@ MainWindow::MainWindow() { m_ui->setupUi(this); + m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size(); + restoreGeometry(config()->get("window/Geometry").toByteArray()); setWindowIcon(filePath()->applicationIcon()); @@ -229,17 +227,16 @@ void MainWindow::updateCopyAttributesMenu() return; } - Entry* entry = dbWidget->entryView()->currentEntry(); - if (!entry || !dbWidget->entryView()->isSingleEntrySelected()) { + if (!dbWidget->numberOfSelectedEntries() == 1) { return; } QList actions = m_ui->menuEntryCopyAttribute->actions(); - for (int i = EntryAttributes::DefaultAttributes.size() + 1; i < actions.size(); i++) { + for (int i = m_countDefaultAttributes + 1; i < actions.size(); i++) { delete actions[i]; } - Q_FOREACH (const QString& key, entry->attributes()->customKeys()) { + Q_FOREACH (const QString& key, dbWidget->customEntryAttributes()) { QAction* action = m_ui->menuEntryCopyAttribute->addAction(key); m_copyAdditionalAttributeActions->addAction(action); } @@ -276,9 +273,9 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) switch (mode) { case DatabaseWidget::ViewMode: { bool inSearch = dbWidget->isInSearchMode(); - bool singleEntrySelected = dbWidget->entryView()->isSingleEntrySelected(); - bool entriesSelected = !dbWidget->entryView()->selectionModel()->selectedRows().isEmpty(); - bool groupSelected = dbWidget->groupView()->currentGroup(); + bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1; + bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0; + bool groupSelected = dbWidget->isGroupSelected(); m_ui->actionEntryNew->setEnabled(!inSearch); m_ui->actionEntryClone->setEnabled(singleEntrySelected && !inSearch); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 706fd2d50..e904426ae 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -77,6 +77,7 @@ private: QActionGroup* m_copyAdditionalAttributeActions; QStringList m_openDatabases; InactivityTimer* m_inactivityTimer; + int m_countDefaultAttributes; Q_DISABLE_COPY(MainWindow) }; diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index f71f80bc6..eb79ec966 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -100,9 +100,14 @@ Entry* EntryView::currentEntry() } } +int EntryView::numberOfSelectedEntries() +{ + return selectionModel()->selectedRows().size(); +} + bool EntryView::isSingleEntrySelected() { - return (selectionModel()->selectedRows().size() == 1); + return (numberOfSelectedEntries() == 1); } void EntryView::setCurrentEntry(Entry* entry) diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index b5f056aa0..2a1df439c 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -42,6 +42,7 @@ public: Entry* entryFromIndex(const QModelIndex& index); void setEntryList(const QList& entries); bool inEntryListMode(); + int numberOfSelectedEntries(); public Q_SLOTS: void setGroup(Group* group); From 147cd4ed7bc6457a2610210dd00af88a1bad8293 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 18:26:01 +0200 Subject: [PATCH 03/39] Add option to use the entry title for auto-type window matching. --- src/autotype/AutoType.cpp | 6 +++ src/core/Config.cpp | 3 +- src/core/Config.h | 2 +- src/gui/SettingsWidget.cpp | 3 ++ src/gui/SettingsWidgetGeneral.ui | 52 ++++++++++++++--------- tests/TestAutoType.cpp | 73 ++++++++++++++++++++++---------- tests/TestAutoType.h | 6 ++- 7 files changed, 98 insertions(+), 47 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 80b810821..92aae5cd7 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -503,6 +503,12 @@ QString AutoType::autoTypeSequence(const Entry* entry, const QString& windowTitl } } + if (!match && config()->get("AutoTypeEntryTitleMatch").toBool() && !entry->title().isEmpty() + && windowTitle.contains(entry->title(), Qt::CaseInsensitive)) { + sequence = entry->defaultAutoTypeSequence(); + match = true; + } + if (!match) { return QString(); } diff --git a/src/core/Config.cpp b/src/core/Config.cpp index c48bc9553..133a03a9e 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -95,6 +95,7 @@ void Config::init(const QString& fileName) m_defaults.insert("ShowToolbar", true); m_defaults.insert("MinimizeOnCopy", false); m_defaults.insert("UseGroupIconOnEntryCreation", false); + m_defaults.insert("AutoTypeEntryTitleMatch", false); m_defaults.insert("security/clearclipboard", true); m_defaults.insert("security/clearclipboardtimeout", 10); m_defaults.insert("security/lockdatabaseidle", false); @@ -112,7 +113,7 @@ Config* Config::instance() return m_instance; } -void Config::createConfigFromFile(QString file) +void Config::createConfigFromFile(const QString& file) { Q_ASSERT(!m_instance); m_instance = new Config(file, qApp); diff --git a/src/core/Config.h b/src/core/Config.h index ee3082685..ca0f74cb5 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -36,7 +36,7 @@ public: void set(const QString& key, const QVariant& value); static Config* instance(); - static void createConfigFromFile(QString file); + static void createConfigFromFile(const QString& file); static void createTempFileInstance(); private: diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index d09805939..2aea2636e 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -67,6 +67,7 @@ void SettingsWidget::loadSettings() m_generalUi->autoSaveOnExitCheckBox->setChecked(config()->get("AutoSaveOnExit").toBool()); m_generalUi->minimizeOnCopyCheckBox->setChecked(config()->get("MinimizeOnCopy").toBool()); m_generalUi->useGroupIconOnEntryCreationCheckBox->setChecked(config()->get("UseGroupIconOnEntryCreation").toBool()); + m_generalUi->autoTypeEntryTitleMatchCheckBox->setChecked(config()->get("AutoTypeEntryTitleMatch").toBool()); if (autoType()->isAvailable()) { m_globalAutoTypeKey = static_cast(config()->get("GlobalAutoTypeKey").toInt()); @@ -102,6 +103,8 @@ void SettingsWidget::saveSettings() config()->set("MinimizeOnCopy", m_generalUi->minimizeOnCopyCheckBox->isChecked()); config()->set("UseGroupIconOnEntryCreation", m_generalUi->useGroupIconOnEntryCreationCheckBox->isChecked()); + config()->set("AutoTypeEntryTitleMatch", + m_generalUi->autoTypeEntryTitleMatchCheckBox->isChecked()); if (autoType()->isAvailable()) { config()->set("GlobalAutoTypeKey", m_generalUi->autoTypeShortcutWidget->key()); config()->set("GlobalAutoTypeModifiers", diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/SettingsWidgetGeneral.ui index f9aa8b680..3d89c68e8 100644 --- a/src/gui/SettingsWidgetGeneral.ui +++ b/src/gui/SettingsWidgetGeneral.ui @@ -7,10 +7,13 @@ 0 0 456 - 185 + 230 + + QFormLayout::AllNonFixedFieldsGrow + @@ -21,6 +24,13 @@ + + + + Open previous databases on startup + + + @@ -31,13 +41,6 @@ - - - - Automatically save after every change - - - @@ -45,20 +48,10 @@ - - + + - Global Auto-Type shortcut - - - - - - - - - - Open previous databases on startup + Automatically save after every change @@ -76,6 +69,23 @@ + + + + Global Auto-Type shortcut + + + + + + + + + + Use entry title to match windows for global auto-type + + + diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index 47ac0909b..0b2f3b250 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -21,6 +21,7 @@ #include #include "tests.h" +#include "core/Config.h" #include "core/FilePath.h" #include "core/Entry.h" #include "core/Group.h" @@ -33,7 +34,7 @@ void TestAutoType::initTestCase() { Crypto::init(); - + Config::createTempFileInstance(); AutoType::createTestInstance(); QPluginLoader loader(filePath()->pluginPath("keepassx-autotype-test")); @@ -54,12 +55,24 @@ void TestAutoType::init() m_test->clearActions(); m_db = new Database(); + m_dbList.clear(); + m_dbList.append(m_db); m_group = new Group(); m_db->setRootGroup(m_group); - m_entry = new Entry(); - m_entry->setGroup(m_group); - m_entry->setUsername("myuser"); - m_entry->setPassword("mypass"); + + m_entry1 = new Entry(); + m_entry1->setGroup(m_group); + m_entry1->setUsername("myuser"); + m_entry1->setPassword("mypass"); + AutoTypeAssociations::Association association; + association.window = "custom window"; + association.sequence = "{username}association{password}"; + m_entry1->autoTypeAssociations()->add(association); + + m_entry2 = new Entry(); + m_entry2->setGroup(m_group); + m_entry2->setPassword("myuser"); + m_entry2->setTitle("entry title"); } void TestAutoType::cleanup() @@ -77,7 +90,7 @@ void TestAutoType::testInternal() void TestAutoType::testAutoTypeWithoutSequence() { - m_autoType->performAutoType(m_entry, Q_NULLPTR); + m_autoType->performAutoType(m_entry1, Q_NULLPTR); QCOMPARE(m_test->actionCount(), 14); QCOMPARE(m_test->actionChars(), @@ -88,42 +101,56 @@ void TestAutoType::testAutoTypeWithoutSequence() void TestAutoType::testAutoTypeWithSequence() { - m_autoType->performAutoType(m_entry, Q_NULLPTR, "{Username}abc{PaSsWoRd}"); + m_autoType->performAutoType(m_entry1, Q_NULLPTR, "{Username}abc{PaSsWoRd}"); QCOMPARE(m_test->actionCount(), 15); QCOMPARE(m_test->actionChars(), QString("%1abc%2") - .arg(m_entry->username()) - .arg(m_entry->password())); + .arg(m_entry1->username()) + .arg(m_entry1->password())); } void TestAutoType::testGlobalAutoTypeWithNoMatch() { - QList dbList; - dbList.append(m_db); - + m_test->setActiveWindowTitle("nomatch"); MessageBox::setNextAnswer(QMessageBox::Ok); - m_autoType->performGlobalAutoType(dbList); + m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString()); } void TestAutoType::testGlobalAutoTypeWithOneMatch() { - QList dbList; - dbList.append(m_db); - AutoTypeAssociations::Association association; - association.window = "custom window"; - association.sequence = "{username}association{password}"; - m_entry->autoTypeAssociations()->add(association); - m_test->setActiveWindowTitle("custom window"); - m_autoType->performGlobalAutoType(dbList); + m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("%1association%2") - .arg(m_entry->username()) - .arg(m_entry->password())); + .arg(m_entry1->username()) + .arg(m_entry1->password())); +} + +void TestAutoType::testGlobalAutoTypeTitleMatch() +{ + config()->set("AutoTypeEntryTitleMatch", true); + + m_test->setActiveWindowTitle("An Entry Title!"); + m_autoType->performGlobalAutoType(m_dbList); + + QCOMPARE(m_test->actionChars(), + QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter))); +} + +void TestAutoType::testGlobalAutoTypeTitleMatchDisabled() +{ + config()->set("AutoTypeEntryTitleMatch", false); + + m_test->setActiveWindowTitle("An Entry Title!"); + MessageBox::setNextAnswer(QMessageBox::Ok); + m_autoType->performGlobalAutoType(m_dbList); + + QCOMPARE(m_test->actionChars(), QString()); + } QTEST_GUILESS_MAIN(TestAutoType) diff --git a/tests/TestAutoType.h b/tests/TestAutoType.h index fba7fde1c..d46a55969 100644 --- a/tests/TestAutoType.h +++ b/tests/TestAutoType.h @@ -41,14 +41,18 @@ private Q_SLOTS: void testAutoTypeWithSequence(); void testGlobalAutoTypeWithNoMatch(); void testGlobalAutoTypeWithOneMatch(); + void testGlobalAutoTypeTitleMatch(); + void testGlobalAutoTypeTitleMatchDisabled(); private: AutoTypePlatformInterface* m_platform; AutoTypeTestInterface* m_test; AutoType* m_autoType; Database* m_db; + QList m_dbList; Group* m_group; - Entry* m_entry; + Entry* m_entry1; + Entry* m_entry2; }; #endif // KEEPASSX_TESTAUTOTYPE_H From bf39d0b1bedf1edcfe5a69aa3b836579cdc8417d Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 18:27:53 +0200 Subject: [PATCH 04/39] Enable entry title matching but always ask before performing auto-type. --- src/core/Config.cpp | 4 ++-- tests/TestAutoType.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 133a03a9e..76f0e6f8e 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -95,13 +95,13 @@ void Config::init(const QString& fileName) m_defaults.insert("ShowToolbar", true); m_defaults.insert("MinimizeOnCopy", false); m_defaults.insert("UseGroupIconOnEntryCreation", false); - m_defaults.insert("AutoTypeEntryTitleMatch", false); + m_defaults.insert("AutoTypeEntryTitleMatch", true); m_defaults.insert("security/clearclipboard", true); m_defaults.insert("security/clearclipboardtimeout", 10); m_defaults.insert("security/lockdatabaseidle", false); m_defaults.insert("security/lockdatabaseidlesec", 10); m_defaults.insert("security/passwordscleartext", false); - m_defaults.insert("security/autotypeask", false); + m_defaults.insert("security/autotypeask", true); } Config* Config::instance() diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index 0b2f3b250..c09a8366c 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -36,6 +36,8 @@ void TestAutoType::initTestCase() Crypto::init(); Config::createTempFileInstance(); AutoType::createTestInstance(); + config()->set("AutoTypeEntryTitleMatch", false); + config()->set("security/autotypeask", false); QPluginLoader loader(filePath()->pluginPath("keepassx-autotype-test")); loader.setLoadHints(QLibrary::ResolveAllSymbolsHint); From 75d3e6261bf2476e6ec9a67dab9e798e0239b70c Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 18:41:11 +0200 Subject: [PATCH 05/39] Coding style fix. --- src/gui/DatabaseWidget.cpp | 4 ++-- src/gui/DatabaseWidget.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index d6d48c5e0..c70348a9e 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -595,7 +595,7 @@ void DatabaseWidget::unlockDatabase(bool accepted) Q_ASSERT(accepted); Q_UNUSED(accepted); - setCurrentWidget(widgetBeforeLock); + setCurrentWidget(m_widgetBeforeLock); Q_EMIT unlockedDatabase(); } @@ -802,7 +802,7 @@ void DatabaseWidget::lock() { Q_ASSERT(currentMode() != DatabaseWidget::LockedMode); - widgetBeforeLock = currentWidget(); + m_widgetBeforeLock = currentWidget(); m_unlockDatabaseWidget->load(m_filename, m_db); setCurrentWidget(m_unlockDatabaseWidget); } diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index d2b4e9e05..22e3992d2 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -153,7 +153,7 @@ private: Group* m_newParent; Group* m_lastGroup; QTimer* m_searchTimer; - QWidget* widgetBeforeLock; + QWidget* m_widgetBeforeLock; QString m_filename; }; From 50cbd80925a1a7940ac32cc8373d033fa67bd924 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 15 May 2014 18:47:36 +0200 Subject: [PATCH 06/39] Remove obsolete method in EntryView. --- src/gui/entry/EntryView.cpp | 5 ----- src/gui/entry/EntryView.h | 1 - 2 files changed, 6 deletions(-) diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index eb79ec966..4691ed655 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -105,11 +105,6 @@ int EntryView::numberOfSelectedEntries() return selectionModel()->selectedRows().size(); } -bool EntryView::isSingleEntrySelected() -{ - return (numberOfSelectedEntries() == 1); -} - void EntryView::setCurrentEntry(Entry* entry) { selectionModel()->setCurrentIndex(m_sortModel->mapFromSource(m_model->indexFromEntry(entry)), diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index 2a1df439c..ba2919e91 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -37,7 +37,6 @@ public: explicit EntryView(QWidget* parent = Q_NULLPTR); void setModel(QAbstractItemModel* model) Q_DECL_OVERRIDE; Entry* currentEntry(); - bool isSingleEntrySelected(); void setCurrentEntry(Entry* entry); Entry* entryFromIndex(const QModelIndex& index); void setEntryList(const QList& entries); From cda5e990ac8ceb40aa4612e56f46d22d5558e3d7 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 22:51:13 +0200 Subject: [PATCH 07/39] Show in-edit-mode warning when database is locked. --- src/gui/DatabaseTabWidget.cpp | 2 +- src/gui/DatabaseWidget.cpp | 15 ++++++++++++++- src/gui/DatabaseWidget.h | 3 ++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 8c2ba06d9..e529cc428 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -189,7 +189,7 @@ bool DatabaseTabWidget::closeDatabase(Database* db) if (dbName.right(1) == "*") { dbName.chop(1); } - if (dbStruct.dbWidget->currentMode() == DatabaseWidget::EditMode && db->hasKey()) { + if (dbStruct.dbWidget->isInEditMode() && db->hasKey()) { QMessageBox::StandardButton result = MessageBox::question( this, tr("Close?"), diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index c70348a9e..72fe0ef7f 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -167,7 +167,7 @@ DatabaseWidget::~DatabaseWidget() { } -DatabaseWidget::Mode DatabaseWidget::currentMode() +DatabaseWidget::Mode DatabaseWidget::currentMode() const { if (currentWidget() == Q_NULLPTR) { return DatabaseWidget::None; @@ -183,6 +183,19 @@ DatabaseWidget::Mode DatabaseWidget::currentMode() } } +bool DatabaseWidget::isInEditMode() const +{ + if (currentMode() == DatabaseWidget::LockedMode) { + return m_widgetBeforeLock != Q_NULLPTR + && m_widgetBeforeLock != m_mainWidget + && m_widgetBeforeLock != m_unlockDatabaseWidget; + } + else { + return currentMode() == DatabaseWidget::EditMode; + } +} + + void DatabaseWidget::emitCurrentModeChanged() { Q_EMIT currentModeChanged(currentMode()); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 22e3992d2..b159cbd16 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -68,12 +68,13 @@ public: int addWidget(QWidget* w); void setCurrentIndex(int index); void setCurrentWidget(QWidget* widget); - DatabaseWidget::Mode currentMode(); + DatabaseWidget::Mode currentMode() const; void lock(); void updateFilename(const QString& filename); int numberOfSelectedEntries(); QStringList customEntryAttributes(); bool isGroupSelected(); + bool isInEditMode() const; Q_SIGNALS: void closeRequest(); From ce7e01a1b131ccecb28972d1728401a43421ae42 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 22:53:59 +0200 Subject: [PATCH 08/39] const-ify several methods. --- src/gui/DatabaseTabWidget.cpp | 2 +- src/gui/DatabaseTabWidget.h | 2 +- src/gui/DatabaseWidget.cpp | 12 ++++++------ src/gui/DatabaseWidget.h | 12 ++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index e529cc428..c15d16e5a 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -503,7 +503,7 @@ DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget() } } -bool DatabaseTabWidget::hasLockableDatabases() +bool DatabaseTabWidget::hasLockableDatabases() const { QHashIterator i(m_dbList); while (i.hasNext()) { diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 9261a065c..8e8fceb38 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -53,7 +53,7 @@ public: void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString()); DatabaseWidget* currentDatabaseWidget(); - bool hasLockableDatabases(); + bool hasLockableDatabases() const; static const int LastDatabasesCount; diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 72fe0ef7f..0d0ca7375 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -786,19 +786,19 @@ void DatabaseWidget::emitEntryContextMenuRequested(const QPoint& pos) Q_EMIT entryContextMenuRequested(m_entryView->viewport()->mapToGlobal(pos)); } -bool DatabaseWidget::dbHasKey() +bool DatabaseWidget::dbHasKey() const { return m_db->hasKey(); } -bool DatabaseWidget::canDeleteCurrentGoup() +bool DatabaseWidget::canDeleteCurrentGoup() const { bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup(); bool isRecycleBin = m_db->metadata()->recycleBin() == m_groupView->currentGroup(); return !isRootGroup && !isRecycleBin; } -bool DatabaseWidget::isInSearchMode() +bool DatabaseWidget::isInSearchMode() const { return m_entryView->inEntryListMode(); } @@ -825,12 +825,12 @@ void DatabaseWidget::updateFilename(const QString& fileName) m_filename = fileName; } -int DatabaseWidget::numberOfSelectedEntries() +int DatabaseWidget::numberOfSelectedEntries() const { return m_entryView->numberOfSelectedEntries(); } -QStringList DatabaseWidget::customEntryAttributes() +QStringList DatabaseWidget::customEntryAttributes() const { Entry* entry = m_entryView->currentEntry(); if (!entry) { @@ -840,7 +840,7 @@ QStringList DatabaseWidget::customEntryAttributes() return entry->attributes()->customKeys(); } -bool DatabaseWidget::isGroupSelected() +bool DatabaseWidget::isGroupSelected() const { return m_groupView->currentGroup() != Q_NULLPTR; } diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index b159cbd16..155942a57 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -62,18 +62,18 @@ public: GroupView* groupView(); EntryView* entryView(); Database* database(); - bool dbHasKey(); - bool canDeleteCurrentGoup(); - bool isInSearchMode(); + bool dbHasKey() const; + bool canDeleteCurrentGoup() const; + bool isInSearchMode() const; int addWidget(QWidget* w); void setCurrentIndex(int index); void setCurrentWidget(QWidget* widget); DatabaseWidget::Mode currentMode() const; void lock(); void updateFilename(const QString& filename); - int numberOfSelectedEntries(); - QStringList customEntryAttributes(); - bool isGroupSelected(); + int numberOfSelectedEntries() const; + QStringList customEntryAttributes() const; + bool isGroupSelected()const ; bool isInEditMode() const; Q_SIGNALS: From e361b0dd81a72c41f4516929c83fc61217094489 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 15 May 2014 22:56:36 +0200 Subject: [PATCH 09/39] Fix typo canDeleteCurrentGoup() -> canDeleteCurrentGroup(). --- src/gui/DatabaseWidget.cpp | 4 ++-- src/gui/DatabaseWidget.h | 2 +- src/gui/MainWindow.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 0d0ca7375..ba7ca747c 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -440,7 +440,7 @@ void DatabaseWidget::createGroup() void DatabaseWidget::deleteGroup() { Group* currentGroup = m_groupView->currentGroup(); - if (!currentGroup || !canDeleteCurrentGoup()) { + if (!currentGroup || !canDeleteCurrentGroup()) { Q_ASSERT(false); return; } @@ -791,7 +791,7 @@ bool DatabaseWidget::dbHasKey() const return m_db->hasKey(); } -bool DatabaseWidget::canDeleteCurrentGoup() const +bool DatabaseWidget::canDeleteCurrentGroup() const { bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup(); bool isRecycleBin = m_db->metadata()->recycleBin() == m_groupView->currentGroup(); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 155942a57..5e77f01b3 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -63,7 +63,7 @@ public: EntryView* entryView(); Database* database(); bool dbHasKey() const; - bool canDeleteCurrentGoup() const; + bool canDeleteCurrentGroup() const; bool isInSearchMode() const; int addWidget(QWidget* w); void setCurrentIndex(int index); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 98bd5aee1..c83f3167f 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -291,7 +291,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionEntryOpenUrl->setEnabled(singleEntrySelected); m_ui->actionGroupNew->setEnabled(groupSelected); m_ui->actionGroupEdit->setEnabled(groupSelected); - m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGoup()); + m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup()); m_ui->actionSearch->setEnabled(true); // TODO: get checked state from db widget m_ui->actionSearch->setChecked(inSearch); From 8bf48260034e3ffe27f3b78c128123ef7b22faa5 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 15 May 2014 23:48:54 +0200 Subject: [PATCH 10/39] Move search into separate class. --- src/CMakeLists.txt | 1 + src/core/EntrySearcher.cpp | 47 ++++++++++++ src/core/EntrySearcher.h | 35 +++++++++ src/core/Group.cpp | 27 ------- src/core/Group.h | 3 - src/gui/DatabaseWidget.cpp | 3 +- tests/CMakeLists.txt | 3 + tests/TestEntrySearcher.cpp | 141 ++++++++++++++++++++++++++++++++++++ tests/TestEntrySearcher.h | 35 +++++++++ tests/TestGroup.cpp | 96 ------------------------ tests/TestGroup.h | 2 - 11 files changed, 264 insertions(+), 129 deletions(-) create mode 100644 src/core/EntrySearcher.cpp create mode 100644 src/core/EntrySearcher.h create mode 100644 tests/TestEntrySearcher.cpp create mode 100644 tests/TestEntrySearcher.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d57153e5f..9af937ea3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ set(keepassx_SOURCES core/Entry.cpp core/EntryAttachments.cpp core/EntryAttributes.cpp + core/EntrySearcher.cpp core/FilePath.cpp core/Global.h core/Group.cpp diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp new file mode 100644 index 000000000..60cfaea45 --- /dev/null +++ b/src/core/EntrySearcher.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "EntrySearcher.h" + +#include "core/Group.h" + +QList EntrySearcher::search(const QString &searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) +{ + if (!group->resolveSearchingEnabled()) { + return QList(); + } + + return searchEntries(searchTerm, group, caseSensitivity); +} + +QList EntrySearcher::searchEntries(const QString &searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) +{ + QList searchResult; + + Q_FOREACH (Entry* entry, group->entries()) { + if (entry->match(searchTerm, caseSensitivity)) { + searchResult.append(entry); + } + } + Q_FOREACH (Group* childGroup, group->children()) { + if (childGroup->searchingEnabled() != Group::Disable) { + searchResult.append(searchEntries(searchTerm, childGroup, caseSensitivity)); + } + } + + return searchResult; +} diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h new file mode 100644 index 000000000..56ed7e4e5 --- /dev/null +++ b/src/core/EntrySearcher.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_ENTRYSEARCHER_H +#define KEEPASSX_ENTRYSEARCHER_H + +#include + + +class Group; +class Entry; + +class EntrySearcher +{ +public: + QList search(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); +private: + QList searchEntries(const QString &searchTerm, const Group *group, Qt::CaseSensitivity caseSensitivity); +}; + +#endif // KEEPASSX_ENTRYSEARCHER_H diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 4c04b4846..9ebf7bb86 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -612,33 +612,6 @@ void Group::recCreateDelObjects() } } -QList Group::search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity, - bool resolveInherit) -{ - QList searchResult; - bool search; - if (resolveInherit) { - search = resolveSearchingEnabled(); - } - else if (searchingEnabled() == Disable) { - search = false; - } - else { - search = true; - } - if (search) { - Q_FOREACH (Entry* entry, m_entries) { - if (entry->match(searchTerm, caseSensitivity)) { - searchResult.append(entry); - } - } - Q_FOREACH (Group* group, m_children) { - searchResult.append(group->search(searchTerm, caseSensitivity, false)); - } - } - return searchResult; -} - bool Group::resolveSearchingEnabled() const { switch (m_data.searchingEnabled) { diff --git a/src/core/Group.h b/src/core/Group.h index 4e08f5b3b..715403c03 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -113,9 +113,6 @@ public: void copyDataFrom(const Group* other); Database* exportToDb(); - QList search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity, - bool resolveInherit = true); - Q_SIGNALS: void dataChanged(Group* group); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index ba7ca747c..fc533a3f3 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -28,6 +28,7 @@ #include "autotype/AutoType.h" #include "core/Config.h" +#include "core/EntrySearcher.h" #include "core/FilePath.h" #include "core/Group.h" #include "core/Metadata.h" @@ -754,8 +755,8 @@ void DatabaseWidget::search() else { sensitivity = Qt::CaseInsensitive; } - QList searchResult = searchGroup->search(m_searchUi->searchEdit->text(), sensitivity); + QList searchResult = EntrySearcher().search(m_searchUi->searchEdit->text(), searchGroup, sensitivity); m_entryView->setEntryList(searchResult); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8df0050aa..be37a5def 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -165,6 +165,9 @@ add_unit_test(NAME testqcommandlineparser SOURCES TestQCommandLineParser.cpp MOC add_unit_test(NAME testrandom SOURCES TestRandom.cpp MOCS TestRandom.h LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp MOCS TestEntrySearcher.h + LIBS ${TEST_LIBRARIES}) + if(WITH_GUI_TESTS) add_subdirectory(gui) endif(WITH_GUI_TESTS) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp new file mode 100644 index 000000000..308a26e6d --- /dev/null +++ b/tests/TestEntrySearcher.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TestEntrySearcher.h" + +#include + +#include "tests.h" +#include "core/EntrySearcher.h" +#include "core/Group.h" + +QTEST_GUILESS_MAIN(TestEntrySearcher) + +void TestEntrySearcher::initTestCase() +{ + +} + +void TestEntrySearcher::testSearch() +{ + Group* groupRoot = new Group(); + Group* group1 = new Group(); + Group* group2 = new Group(); + Group* group3 = new Group(); + + group1->setParent(groupRoot); + group2->setParent(groupRoot); + group3->setParent(groupRoot); + + Group* group11 = new Group(); + + group11->setParent(group1); + + Group* group21 = new Group(); + Group* group211 = new Group(); + Group* group2111 = new Group(); + + group21->setParent(group2); + group211->setParent(group21); + group2111->setParent(group211); + + group1->setSearchingEnabled(Group::Disable); + group11->setSearchingEnabled(Group::Enable); + + Entry* eRoot = new Entry(); + eRoot->setNotes("test search term test"); + eRoot->setGroup(groupRoot); + + Entry* eRoot2 = new Entry(); + eRoot2->setNotes("test term test"); + eRoot2->setGroup(groupRoot); + + Entry* e1 = new Entry(); + e1->setNotes("test search term test"); + e1->setGroup(group1); + + Entry* e11 = new Entry(); + e11->setNotes("test search term test"); + e11->setGroup(group11); + + Entry* e2111 = new Entry(); + e2111->setNotes("test search term test"); + e2111->setGroup(group2111); + + Entry* e2111b = new Entry(); + e2111b->setNotes("test search test"); + e2111b->setGroup(group2111); + + Entry* e3 = new Entry(); + e3->setNotes("test search term test"); + e3->setGroup(group3); + + Entry* e3b = new Entry(); + e3b->setNotes("test search test"); + e3b->setGroup(group3); + + QList searchResult; + + EntrySearcher entrySearcher; + + searchResult = entrySearcher.search("search term", groupRoot, Qt::CaseInsensitive); + + QCOMPARE(searchResult.count(), 3); + + searchResult = entrySearcher.search("search term", group211, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("search term", group11, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("search term", group1, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 0); + + delete groupRoot; +} + +void TestEntrySearcher::testAndConcatenationInSearch() +{ + Group* group = new Group(); + Entry* entry = new Entry(); + entry->setNotes("abc def ghi"); + entry->setTitle("jkl"); + entry->setGroup(group); + + EntrySearcher entrySearcher; + QList searchResult; + + searchResult = entrySearcher.search("", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("def", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search(" abc ghi ", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("ghi ef", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + searchResult = entrySearcher.search("abc ef xyz", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 0); + + searchResult = entrySearcher.search("abc kl", group, Qt::CaseInsensitive); + QCOMPARE(searchResult.count(), 1); + + delete group; +} diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h new file mode 100644 index 000000000..d261d5dd6 --- /dev/null +++ b/tests/TestEntrySearcher.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef KEEPASSX_TESTENTRYSEARCHER_H +#define KEEPASSX_TESTENTRYSEARCHER_H + +#include + +class TestEntrySearcher : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + + void testAndConcatenationInSearch(); + void testSearch(); +}; + +#endif // KEEPASSX_TESTENTRYSEARCHER_H diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 86b55b706..816f7ce70 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -334,102 +334,6 @@ void TestGroup::testCopyCustomIcon() delete dbTarget; } -void TestGroup::testSearch() -{ - Group* groupRoot = new Group(); - Group* group1 = new Group(); - Group* group2 = new Group(); - Group* group3 = new Group(); - - group1->setParent(groupRoot); - group2->setParent(groupRoot); - group3->setParent(groupRoot); - - Group* group11 = new Group(); - - group11->setParent(group1); - - Group* group21 = new Group(); - Group* group211 = new Group(); - Group* group2111 = new Group(); - - group21->setParent(group2); - group211->setParent(group21); - group2111->setParent(group211); - - group1->setSearchingEnabled(Group::Disable); - group11->setSearchingEnabled(Group::Enable); - - Entry* eRoot = new Entry(); - eRoot->setNotes("test search term test"); - eRoot->setGroup(groupRoot); - - Entry* eRoot2 = new Entry(); - eRoot2->setNotes("test term test"); - eRoot2->setGroup(groupRoot); - - Entry* e1 = new Entry(); - e1->setNotes("test search term test"); - e1->setGroup(group1); - - Entry* e2111 = new Entry(); - e2111->setNotes("test search term test"); - e2111->setGroup(group2111); - - Entry* e2111b = new Entry(); - e2111b->setNotes("test search test"); - e2111b->setGroup(group2111); - - Entry* e3 = new Entry(); - e3->setNotes("test search term test"); - e3->setGroup(group3); - - Entry* e3b = new Entry(); - e3b->setNotes("test search test"); - e3b->setGroup(group3); - - QList searchResult; - - searchResult = groupRoot->search("search term", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 3); - - searchResult = group211->search("search term", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - delete groupRoot; -} - -void TestGroup::testAndConcatenationInSearch() -{ - Group* group = new Group(); - Entry* entry = new Entry(); - entry->setNotes("abc def ghi"); - entry->setTitle("jkl"); - entry->setGroup(group); - - QList searchResult; - - searchResult = group->search("", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = group->search("def", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = group->search(" abc ghi ", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = group->search("ghi ef", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = group->search("abc ef xyz", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 0); - - searchResult = group->search("abc kl", Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - delete group; -} - void TestGroup::testClone() { Database* db = new Database(); diff --git a/tests/TestGroup.h b/tests/TestGroup.h index 895c2cc5a..2e4019901 100644 --- a/tests/TestGroup.h +++ b/tests/TestGroup.h @@ -31,8 +31,6 @@ private Q_SLOTS: void testEntries(); void testDeleteSignals(); void testCopyCustomIcon(); - void testSearch(); - void testAndConcatenationInSearch(); void testClone(); void testCopyCustomIcons(); void testExportToDb(); From c90ac914bb7eca4908ac46fa0fd3516e355431e1 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Thu, 15 May 2014 23:59:26 +0200 Subject: [PATCH 11/39] Refactor TestEntrySearcher. --- tests/TestEntrySearcher.cpp | 25 ++++++++++++------------- tests/TestEntrySearcher.h | 5 +++++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index 308a26e6d..564d71a77 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -27,12 +27,16 @@ QTEST_GUILESS_MAIN(TestEntrySearcher) void TestEntrySearcher::initTestCase() { + groupRoot = new Group(); +} +void TestEntrySearcher::cleanupTestCase() +{ + delete groupRoot; } void TestEntrySearcher::testSearch() { - Group* groupRoot = new Group(); Group* group1 = new Group(); Group* group2 = new Group(); Group* group3 = new Group(); @@ -104,38 +108,33 @@ void TestEntrySearcher::testSearch() searchResult = entrySearcher.search("search term", group1, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 0); - - delete groupRoot; } void TestEntrySearcher::testAndConcatenationInSearch() { - Group* group = new Group(); Entry* entry = new Entry(); entry->setNotes("abc def ghi"); entry->setTitle("jkl"); - entry->setGroup(group); + entry->setGroup(groupRoot); EntrySearcher entrySearcher; QList searchResult; - searchResult = entrySearcher.search("", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - searchResult = entrySearcher.search("def", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("def", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - searchResult = entrySearcher.search(" abc ghi ", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search(" abc ghi ", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - searchResult = entrySearcher.search("ghi ef", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("ghi ef", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - searchResult = entrySearcher.search("abc ef xyz", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("abc ef xyz", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 0); - searchResult = entrySearcher.search("abc kl", group, Qt::CaseInsensitive); + searchResult = entrySearcher.search("abc kl", groupRoot, Qt::CaseInsensitive); QCOMPARE(searchResult.count(), 1); - - delete group; } diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index d261d5dd6..3e9d4f66d 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -21,15 +21,20 @@ #include +class Group; + class TestEntrySearcher : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); + void cleanupTestCase(); void testAndConcatenationInSearch(); void testSearch(); +private: + Group* groupRoot; }; #endif // KEEPASSX_TESTENTRYSEARCHER_H From 819cfd459a63edd4baeb6eb394dd2558c6bb1d94 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 00:19:58 +0200 Subject: [PATCH 12/39] Move match method out of entry class. --- src/core/Entry.cpp | 19 ------------------- src/core/Entry.h | 2 -- src/core/EntrySearcher.cpp | 26 ++++++++++++++++++++++---- src/core/EntrySearcher.h | 4 +++- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 55f543285..4f977915b 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -579,25 +579,6 @@ const Database* Entry::database() const } } -bool Entry::match(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity) -{ - QStringList wordList = searchTerm.split(QRegExp("\\s"), QString::SkipEmptyParts); - Q_FOREACH (const QString& word, wordList) { - if (!wordMatch(word, caseSensitivity)) { - return false; - } - } - return true; -} - -bool Entry::wordMatch(const QString& word, Qt::CaseSensitivity caseSensitivity) -{ - return title().contains(word, caseSensitivity) || - username().contains(word, caseSensitivity) || - url().contains(word, caseSensitivity) || - notes().contains(word, caseSensitivity); -} - QString Entry::resolvePlaceholders(const QString& str) const { QString result = str; diff --git a/src/core/Entry.h b/src/core/Entry.h index c2c2938c1..ae07ed453 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -141,7 +141,6 @@ public: void setGroup(Group* group); void setUpdateTimeinfo(bool value); - bool match(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity); Q_SIGNALS: /** @@ -157,7 +156,6 @@ private Q_SLOTS: void updateModifiedSinceBegin(); private: - bool wordMatch(const QString& word, Qt::CaseSensitivity caseSensitivity); const Database* database() const; template bool set(T& property, const T& value); diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp index 60cfaea45..82a553e3a 100644 --- a/src/core/EntrySearcher.cpp +++ b/src/core/EntrySearcher.cpp @@ -28,14 +28,12 @@ QList EntrySearcher::search(const QString &searchTerm, const Group* grou return searchEntries(searchTerm, group, caseSensitivity); } -QList EntrySearcher::searchEntries(const QString &searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) +QList EntrySearcher::searchEntries(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity) { QList searchResult; Q_FOREACH (Entry* entry, group->entries()) { - if (entry->match(searchTerm, caseSensitivity)) { - searchResult.append(entry); - } + searchResult.append(matchEntry(searchTerm, entry, caseSensitivity)); } Q_FOREACH (Group* childGroup, group->children()) { if (childGroup->searchingEnabled() != Group::Disable) { @@ -45,3 +43,23 @@ QList EntrySearcher::searchEntries(const QString &searchTerm, const Grou return searchResult; } + +QList EntrySearcher::matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity) +{ + QStringList wordList = searchTerm.split(QRegExp("\\s"), QString::SkipEmptyParts); + Q_FOREACH (const QString& word, wordList) { + if (!wordMatch(word, entry, caseSensitivity)) { + return QList(); + } + } + + return QList() << entry; +} + +bool EntrySearcher::wordMatch(const QString& word, Entry* entry, Qt::CaseSensitivity caseSensitivity) +{ + return entry->title().contains(word, caseSensitivity) || + entry->username().contains(word, caseSensitivity) || + entry->url().contains(word, caseSensitivity) || + entry->notes().contains(word, caseSensitivity); +} diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h index 56ed7e4e5..246538cbe 100644 --- a/src/core/EntrySearcher.h +++ b/src/core/EntrySearcher.h @@ -29,7 +29,9 @@ class EntrySearcher public: QList search(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); private: - QList searchEntries(const QString &searchTerm, const Group *group, Qt::CaseSensitivity caseSensitivity); + QList searchEntries(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity); + QList matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity); + bool wordMatch(const QString &word, Entry *entry, Qt::CaseSensitivity caseSensitivity); }; #endif // KEEPASSX_ENTRYSEARCHER_H From 4f60df029d28f7e6f89d2b17405e878be03c307e Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 00:26:09 +0200 Subject: [PATCH 13/39] Refactor TestEntrySearcher. --- tests/TestEntrySearcher.cpp | 66 ++++++++++++++++--------------------- tests/TestEntrySearcher.h | 7 ++-- 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index 564d71a77..fec2a4ae2 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -20,19 +20,18 @@ #include #include "tests.h" -#include "core/EntrySearcher.h" -#include "core/Group.h" + QTEST_GUILESS_MAIN(TestEntrySearcher) void TestEntrySearcher::initTestCase() { - groupRoot = new Group(); + m_groupRoot = new Group(); } void TestEntrySearcher::cleanupTestCase() { - delete groupRoot; + delete m_groupRoot; } void TestEntrySearcher::testSearch() @@ -41,9 +40,9 @@ void TestEntrySearcher::testSearch() Group* group2 = new Group(); Group* group3 = new Group(); - group1->setParent(groupRoot); - group2->setParent(groupRoot); - group3->setParent(groupRoot); + group1->setParent(m_groupRoot); + group2->setParent(m_groupRoot); + group3->setParent(m_groupRoot); Group* group11 = new Group(); @@ -62,11 +61,11 @@ void TestEntrySearcher::testSearch() Entry* eRoot = new Entry(); eRoot->setNotes("test search term test"); - eRoot->setGroup(groupRoot); + eRoot->setGroup(m_groupRoot); Entry* eRoot2 = new Entry(); eRoot2->setNotes("test term test"); - eRoot2->setGroup(groupRoot); + eRoot2->setGroup(m_groupRoot); Entry* e1 = new Entry(); e1->setNotes("test search term test"); @@ -92,22 +91,18 @@ void TestEntrySearcher::testSearch() e3b->setNotes("test search test"); e3b->setGroup(group3); - QList searchResult; + m_searchResult = m_entrySearcher.search("search term", m_groupRoot, Qt::CaseInsensitive); - EntrySearcher entrySearcher; + QCOMPARE(m_searchResult.count(), 3); - searchResult = entrySearcher.search("search term", groupRoot, Qt::CaseInsensitive); + m_searchResult = m_entrySearcher.search("search term", group211, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - QCOMPARE(searchResult.count(), 3); + m_searchResult = m_entrySearcher.search("search term", group11, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search("search term", group211, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = entrySearcher.search("search term", group11, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); - - searchResult = entrySearcher.search("search term", group1, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 0); + m_searchResult = m_entrySearcher.search("search term", group1, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 0); } void TestEntrySearcher::testAndConcatenationInSearch() @@ -115,26 +110,23 @@ void TestEntrySearcher::testAndConcatenationInSearch() Entry* entry = new Entry(); entry->setNotes("abc def ghi"); entry->setTitle("jkl"); - entry->setGroup(groupRoot); + entry->setGroup(m_groupRoot); - EntrySearcher entrySearcher; - QList searchResult; + m_searchResult = m_entrySearcher.search("", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search("", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("def", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search("def", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search(" abc ghi ", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search(" abc ghi ", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("ghi ef", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); - searchResult = entrySearcher.search("ghi ef", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("abc ef xyz", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 0); - searchResult = entrySearcher.search("abc ef xyz", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 0); - - searchResult = entrySearcher.search("abc kl", groupRoot, Qt::CaseInsensitive); - QCOMPARE(searchResult.count(), 1); + m_searchResult = m_entrySearcher.search("abc kl", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); } diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index 3e9d4f66d..194e29f0f 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -21,7 +21,8 @@ #include -class Group; +#include "core/EntrySearcher.h" +#include "core/Group.h" class TestEntrySearcher : public QObject { @@ -34,7 +35,9 @@ private Q_SLOTS: void testAndConcatenationInSearch(); void testSearch(); private: - Group* groupRoot; + Group* m_groupRoot; + EntrySearcher m_entrySearcher; + QList m_searchResult; }; #endif // KEEPASSX_TESTENTRYSEARCHER_H From c2940a8f18070b502eca96fc2b616d219c558332 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 10:51:22 +0200 Subject: [PATCH 14/39] Extend TestEntrySearcher. --- tests/TestEntrySearcher.cpp | 15 ++++++++++++++- tests/TestEntrySearcher.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index fec2a4ae2..b37992df9 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -92,7 +92,6 @@ void TestEntrySearcher::testSearch() e3b->setGroup(group3); m_searchResult = m_entrySearcher.search("search term", m_groupRoot, Qt::CaseInsensitive); - QCOMPARE(m_searchResult.count(), 3); m_searchResult = m_entrySearcher.search("search term", group211, Qt::CaseInsensitive); @@ -130,3 +129,17 @@ void TestEntrySearcher::testAndConcatenationInSearch() m_searchResult = m_entrySearcher.search("abc kl", m_groupRoot, Qt::CaseInsensitive); QCOMPARE(m_searchResult.count(), 1); } + +void TestEntrySearcher::testAllAttributesAreSearched() +{ + Entry* entry = new Entry(); + entry->setGroup(m_groupRoot); + + entry->setTitle("testTitle"); + entry->setUsername("testUsername"); + entry->setUrl("testUrl"); + entry->setNotes("testNote"); + + m_searchResult = m_entrySearcher.search("testTitle testUsername testUrl testNote", m_groupRoot, Qt::CaseInsensitive); + QCOMPARE(m_searchResult.count(), 1); +} diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index 194e29f0f..7c45451dc 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -34,6 +34,8 @@ private Q_SLOTS: void testAndConcatenationInSearch(); void testSearch(); + void testAllAttributesAreSearched(); + private: Group* m_groupRoot; EntrySearcher m_entrySearcher; From 204cd8d9714201a7db54f971c9e1349bd19466a3 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 11:48:30 +0200 Subject: [PATCH 15/39] Move exporter to separate class. --- src/CMakeLists.txt | 1 + src/core/Group.cpp | 16 -------- src/core/Group.h | 1 - src/core/ToDbExporter.cpp | 39 +++++++++++++++++++ src/core/ToDbExporter.h | 31 +++++++++++++++ tests/CMakeLists.txt | 3 ++ tests/TestExporter.cpp | 82 +++++++++++++++++++++++++++++++++++++++ tests/TestExporter.h | 33 ++++++++++++++++ tests/TestGroup.cpp | 44 --------------------- tests/TestGroup.h | 1 - 10 files changed, 189 insertions(+), 62 deletions(-) create mode 100644 src/core/ToDbExporter.cpp create mode 100644 src/core/ToDbExporter.h create mode 100644 tests/TestExporter.cpp create mode 100644 tests/TestExporter.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9af937ea3..b09501cf7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,7 @@ set(keepassx_SOURCES core/SignalMultiplexer.cpp core/TimeDelta.cpp core/TimeInfo.cpp + core/ToDbExporter.cpp core/Tools.cpp core/Uuid.cpp core/qcommandlineoption.cpp diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 9ebf7bb86..517f8cb0c 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -500,22 +500,6 @@ void Group::copyDataFrom(const Group* other) m_lastTopVisibleEntry = other->m_lastTopVisibleEntry; } -Database* Group::exportToDb() -{ - Q_ASSERT(database()); - - Database* db = new Database(); - Group* clonedGroup = clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory); - clonedGroup->setParent(db->rootGroup()); - - QSet customIcons = customIconsRecursive(); - db->metadata()->copyCustomIcons(customIcons, database()->metadata()); - - db->copyAttributesFrom(database()); - - return db; -} - void Group::addEntry(Entry* entry) { Q_ASSERT(entry); diff --git a/src/core/Group.h b/src/core/Group.h index 715403c03..7391f886d 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -111,7 +111,6 @@ public: */ Group* clone(Entry::CloneFlags entryFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo) const; void copyDataFrom(const Group* other); - Database* exportToDb(); Q_SIGNALS: void dataChanged(Group* group); diff --git a/src/core/ToDbExporter.cpp b/src/core/ToDbExporter.cpp new file mode 100644 index 000000000..0601ca4b8 --- /dev/null +++ b/src/core/ToDbExporter.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ToDbExporter.h" +#include "core/Database.h" +#include "core/Group.h" +#include "core/Metadata.h" + +Database* ToDbExporter::exportToDb(Group* group) +{ + Database* oldDb = group->database(); + Q_ASSERT(oldDb); + + Database* db = new Database(); + Group* clonedGroup = group->clone(Entry::CloneNewUuid | Entry::CloneIncludeHistory); + clonedGroup->setParent(db->rootGroup()); + + QSet customIcons = group->customIconsRecursive(); + db->metadata()->copyCustomIcons(customIcons, oldDb->metadata()); + + db->copyAttributesFrom(oldDb); + + return db; +} diff --git a/src/core/ToDbExporter.h b/src/core/ToDbExporter.h new file mode 100644 index 000000000..bd57a4b19 --- /dev/null +++ b/src/core/ToDbExporter.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_TODBEXPORTER_H +#define KEEPASSX_TODBEXPORTER_H + +class Database; +class Group; + +class ToDbExporter +{ +public: + Database* exportToDb(Group* group); +}; + +#endif // KEEPASSX_TODBEXPORTER_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index be37a5def..d3f51ac41 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -168,6 +168,9 @@ add_unit_test(NAME testrandom SOURCES TestRandom.cpp MOCS TestRandom.h add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp MOCS TestEntrySearcher.h LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testexporter SOURCES TestExporter.cpp MOCS TestExporter.h + LIBS ${TEST_LIBRARIES}) + if(WITH_GUI_TESTS) add_subdirectory(gui) endif(WITH_GUI_TESTS) diff --git a/tests/TestExporter.cpp b/tests/TestExporter.cpp new file mode 100644 index 000000000..73bf407e9 --- /dev/null +++ b/tests/TestExporter.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TestExporter.h" + +#include + +#include "tests.h" +#include "core/ToDbExporter.h" +#include "core/Group.h" +#include "core/Metadata.h" +#include "crypto/Crypto.h" + +QTEST_GUILESS_MAIN(TestExporter) + +void TestExporter::initTestCase() +{ + Crypto::init(); +} + +void TestExporter::testToDbExporter() +{ + QImage iconImage(1, 1, QImage::Format_RGB32); + iconImage.setPixel(0, 0, qRgb(1, 2, 3)); + Uuid iconUuid = Uuid::random(); + + QImage iconUnusedImage(1, 1, QImage::Format_RGB32); + iconUnusedImage.setPixel(0, 0, qRgb(1, 2, 3)); + Uuid iconUnusedUuid = Uuid::random(); + + Database* dbOrg = new Database(); + Group* groupOrg = new Group(); + groupOrg->setParent(dbOrg->rootGroup()); + groupOrg->setName("GTEST"); + Entry* entryOrg = new Entry(); + entryOrg->setGroup(groupOrg); + entryOrg->setTitle("ETEST"); + dbOrg->metadata()->addCustomIcon(iconUuid, iconImage); + dbOrg->metadata()->addCustomIcon(iconUnusedUuid, iconUnusedImage); + entryOrg->setIcon(iconUuid); + entryOrg->beginUpdate(); + entryOrg->setIcon(Entry::DefaultIconNumber); + entryOrg->endUpdate(); + + Database* dbExp = ToDbExporter().exportToDb(groupOrg); + + QCOMPARE(dbExp->rootGroup()->children().size(), 1); + Group* groupExp = dbExp->rootGroup()->children().first(); + QVERIFY(groupExp != groupOrg); + QCOMPARE(groupExp->name(), groupOrg->name()); + QCOMPARE(groupExp->entries().size(), 1); + + Entry* entryExp = groupExp->entries().first(); + QCOMPARE(entryExp->title(), entryOrg->title()); + QCOMPARE(dbExp->metadata()->customIcons().size(), 1); + QVERIFY(dbExp->metadata()->containsCustomIcon(iconUuid)); + QCOMPARE(entryExp->iconNumber(), entryOrg->iconNumber()); + + QCOMPARE(entryExp->historyItems().size(), 1); + QCOMPARE(entryExp->historyItems().first()->iconUuid(), iconUuid); + + delete dbOrg; + delete dbExp; +} + + + diff --git a/tests/TestExporter.h b/tests/TestExporter.h new file mode 100644 index 000000000..15f9a7c33 --- /dev/null +++ b/tests/TestExporter.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_TESTEXPORTER_H +#define KEEPASSX_TESTEXPORTER_H + +#include + +class TestExporter : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testToDbExporter(); +}; + +#endif // KEEPASSX_TESTEXPORTER_H diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 816f7ce70..0b4564f2b 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -441,48 +441,4 @@ void TestGroup::testCopyCustomIcons() QCOMPARE(metaTarget->customIcon(group2Icon).pixel(0, 0), qRgb(4, 5, 6)); } -void TestGroup::testExportToDb() -{ - QImage iconImage(1, 1, QImage::Format_RGB32); - iconImage.setPixel(0, 0, qRgb(1, 2, 3)); - Uuid iconUuid = Uuid::random(); - - QImage iconUnusedImage(1, 1, QImage::Format_RGB32); - iconUnusedImage.setPixel(0, 0, qRgb(1, 2, 3)); - Uuid iconUnusedUuid = Uuid::random(); - - Database* dbOrg = new Database(); - Group* groupOrg = new Group(); - groupOrg->setParent(dbOrg->rootGroup()); - groupOrg->setName("GTEST"); - Entry* entryOrg = new Entry(); - entryOrg->setGroup(groupOrg); - entryOrg->setTitle("ETEST"); - dbOrg->metadata()->addCustomIcon(iconUuid, iconImage); - dbOrg->metadata()->addCustomIcon(iconUnusedUuid, iconUnusedImage); - entryOrg->setIcon(iconUuid); - entryOrg->beginUpdate(); - entryOrg->setIcon(Entry::DefaultIconNumber); - entryOrg->endUpdate(); - - Database* dbExp = groupOrg->exportToDb(); - QCOMPARE(dbExp->rootGroup()->children().size(), 1); - Group* groupExp = dbExp->rootGroup()->children().first(); - QVERIFY(groupExp != groupOrg); - QCOMPARE(groupExp->name(), groupOrg->name()); - QCOMPARE(groupExp->entries().size(), 1); - - Entry* entryExp = groupExp->entries().first(); - QCOMPARE(entryExp->title(), entryOrg->title()); - QCOMPARE(dbExp->metadata()->customIcons().size(), 1); - QVERIFY(dbExp->metadata()->containsCustomIcon(iconUuid)); - QCOMPARE(entryExp->iconNumber(), entryOrg->iconNumber()); - - QCOMPARE(entryExp->historyItems().size(), 1); - QCOMPARE(entryExp->historyItems().first()->iconUuid(), iconUuid); - - delete dbOrg; - delete dbExp; -} - QTEST_GUILESS_MAIN(TestGroup) diff --git a/tests/TestGroup.h b/tests/TestGroup.h index 2e4019901..c612a3ac6 100644 --- a/tests/TestGroup.h +++ b/tests/TestGroup.h @@ -33,7 +33,6 @@ private Q_SLOTS: void testCopyCustomIcon(); void testClone(); void testCopyCustomIcons(); - void testExportToDb(); }; #endif // KEEPASSX_TESTGROUP_H From ea3375490c76ae410a7d1202cc8ce7a5e6d5f602 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 12:30:47 +0200 Subject: [PATCH 16/39] Introduce interface for exporter. --- src/core/Exporter.h | 14 ++++++++++++++ src/core/ToDbExporter.cpp | 2 +- src/core/ToDbExporter.h | 6 ++++-- tests/TestExporter.cpp | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/core/Exporter.h diff --git a/src/core/Exporter.h b/src/core/Exporter.h new file mode 100644 index 000000000..dedb1c8a5 --- /dev/null +++ b/src/core/Exporter.h @@ -0,0 +1,14 @@ +#ifndef KEEPASSX_EXPORTER_H +#define KEEPASSX_EXPORTER_H + +class Database; +class Group; + +class Exporter +{ +public: + virtual Database* exportGroup(Group* group) = 0; + virtual ~Exporter() {} +}; + +#endif // KEEPASSX_EXPORTER_H diff --git a/src/core/ToDbExporter.cpp b/src/core/ToDbExporter.cpp index 0601ca4b8..1f76fb744 100644 --- a/src/core/ToDbExporter.cpp +++ b/src/core/ToDbExporter.cpp @@ -21,7 +21,7 @@ #include "core/Group.h" #include "core/Metadata.h" -Database* ToDbExporter::exportToDb(Group* group) +Database* ToDbExporter::exportGroup(Group* group) { Database* oldDb = group->database(); Q_ASSERT(oldDb); diff --git a/src/core/ToDbExporter.h b/src/core/ToDbExporter.h index bd57a4b19..58c5efeb3 100644 --- a/src/core/ToDbExporter.h +++ b/src/core/ToDbExporter.h @@ -19,13 +19,15 @@ #ifndef KEEPASSX_TODBEXPORTER_H #define KEEPASSX_TODBEXPORTER_H +#include "core/Exporter.h" + class Database; class Group; -class ToDbExporter +class ToDbExporter : Exporter { public: - Database* exportToDb(Group* group); + Database* exportGroup(Group* group); }; #endif // KEEPASSX_TODBEXPORTER_H diff --git a/tests/TestExporter.cpp b/tests/TestExporter.cpp index 73bf407e9..16b4b23cb 100644 --- a/tests/TestExporter.cpp +++ b/tests/TestExporter.cpp @@ -57,7 +57,7 @@ void TestExporter::testToDbExporter() entryOrg->setIcon(Entry::DefaultIconNumber); entryOrg->endUpdate(); - Database* dbExp = ToDbExporter().exportToDb(groupOrg); + Database* dbExp = ToDbExporter().exportGroup(groupOrg); QCOMPARE(dbExp->rootGroup()->children().size(), 1); Group* groupExp = dbExp->rootGroup()->children().first(); From 77af79498c566be89787d206c2fa88985463a932 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 12:32:52 +0200 Subject: [PATCH 17/39] Move QTEST_GUILESS_MAIN statements before test cases. --- tests/TestAutoType.cpp | 4 ++-- tests/TestCryptoHash.cpp | 4 ++-- tests/TestDeletedObjects.cpp | 4 ++-- tests/TestEntry.cpp | 4 ++-- tests/TestEntryModel.cpp | 4 ++-- tests/TestEntrySearcher.cpp | 1 - tests/TestGroup.cpp | 4 ++-- tests/TestGroupModel.cpp | 4 ++-- tests/TestHashedBlockStream.cpp | 4 ++-- tests/TestKeePass1Reader.cpp | 4 ++-- tests/TestKeePass2RandomStream.cpp | 4 ++-- tests/TestKeePass2Reader.cpp | 4 ++-- tests/TestKeePass2Writer.cpp | 4 ++-- tests/TestKeePass2XmlReader.cpp | 4 ++-- tests/TestKeys.cpp | 4 ++-- tests/TestModified.cpp | 4 ++-- tests/TestQCommandLineParser.cpp | 4 ++-- tests/TestQSaveFile.cpp | 4 ++-- tests/TestRandom.cpp | 4 ++-- tests/TestSymmetricCipher.cpp | 4 ++-- tests/TestWildcardMatcher.cpp | 4 ++-- 21 files changed, 40 insertions(+), 41 deletions(-) diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index c09a8366c..4f4350e71 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -31,6 +31,8 @@ #include "autotype/test/AutoTypeTestInterface.h" #include "gui/MessageBox.h" +QTEST_GUILESS_MAIN(TestAutoType) + void TestAutoType::initTestCase() { Crypto::init(); @@ -154,5 +156,3 @@ void TestAutoType::testGlobalAutoTypeTitleMatchDisabled() QCOMPARE(m_test->actionChars(), QString()); } - -QTEST_GUILESS_MAIN(TestAutoType) diff --git a/tests/TestCryptoHash.cpp b/tests/TestCryptoHash.cpp index 4f258a179..d189d456c 100644 --- a/tests/TestCryptoHash.cpp +++ b/tests/TestCryptoHash.cpp @@ -23,6 +23,8 @@ #include "crypto/Crypto.h" #include "crypto/CryptoHash.h" +QTEST_GUILESS_MAIN(TestCryptoHash) + void TestCryptoHash::initTestCase() { Crypto::init(); @@ -47,5 +49,3 @@ void TestCryptoHash::test() QCOMPARE(cryptoHash3.result(), QByteArray::fromHex("0b56e5f65263e747af4a833bd7dd7ad26a64d7a4de7c68e52364893dca0766b4")); } - -QTEST_GUILESS_MAIN(TestCryptoHash) diff --git a/tests/TestDeletedObjects.cpp b/tests/TestDeletedObjects.cpp index 914096cec..5967e58ee 100644 --- a/tests/TestDeletedObjects.cpp +++ b/tests/TestDeletedObjects.cpp @@ -26,6 +26,8 @@ #include "format/KeePass2XmlReader.h" #include "config-keepassx-tests.h" +QTEST_GUILESS_MAIN(TestDeletedObjects) + void TestDeletedObjects::initTestCase() { Crypto::init(); @@ -158,5 +160,3 @@ void TestDeletedObjects::testDatabaseChange() delete db; delete db2; } - -QTEST_GUILESS_MAIN(TestDeletedObjects) diff --git a/tests/TestEntry.cpp b/tests/TestEntry.cpp index 15f398f18..481250a20 100644 --- a/tests/TestEntry.cpp +++ b/tests/TestEntry.cpp @@ -23,6 +23,8 @@ #include "core/Entry.h" #include "crypto/Crypto.h" +QTEST_GUILESS_MAIN(TestEntry) + void TestEntry::initTestCase() { Crypto::init(); @@ -121,5 +123,3 @@ void TestEntry::testClone() QCOMPARE(entryCloneHistory->historyItems().first()->title(), QString("Original Title")); QCOMPARE(entryCloneHistory->timeInfo().creationTime(), entryOrg->timeInfo().creationTime()); } - -QTEST_GUILESS_MAIN(TestEntry) diff --git a/tests/TestEntryModel.cpp b/tests/TestEntryModel.cpp index fab63db68..5347a3c52 100644 --- a/tests/TestEntryModel.cpp +++ b/tests/TestEntryModel.cpp @@ -33,6 +33,8 @@ #include "gui/entry/EntryAttachmentsModel.h" #include "gui/entry/EntryAttributesModel.h" +QTEST_GUILESS_MAIN(TestEntryModel) + void TestEntryModel::initTestCase() { qRegisterMetaType("QModelIndex"); @@ -341,5 +343,3 @@ void TestEntryModel::testDatabaseDelete() delete modelTest; delete model; } - -QTEST_GUILESS_MAIN(TestEntryModel) diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp index b37992df9..9f7ca1397 100644 --- a/tests/TestEntrySearcher.cpp +++ b/tests/TestEntrySearcher.cpp @@ -21,7 +21,6 @@ #include "tests.h" - QTEST_GUILESS_MAIN(TestEntrySearcher) void TestEntrySearcher::initTestCase() diff --git a/tests/TestGroup.cpp b/tests/TestGroup.cpp index 0b4564f2b..32398f01f 100644 --- a/tests/TestGroup.cpp +++ b/tests/TestGroup.cpp @@ -27,6 +27,8 @@ #include "core/Metadata.h" #include "crypto/Crypto.h" +QTEST_GUILESS_MAIN(TestGroup) + void TestGroup::initTestCase() { qRegisterMetaType("Entry*"); @@ -440,5 +442,3 @@ void TestGroup::testCopyCustomIcons() QCOMPARE(metaTarget->customIcon(group1Icon).pixel(0, 0), qRgb(1, 2, 3)); QCOMPARE(metaTarget->customIcon(group2Icon).pixel(0, 0), qRgb(4, 5, 6)); } - -QTEST_GUILESS_MAIN(TestGroup) diff --git a/tests/TestGroupModel.cpp b/tests/TestGroupModel.cpp index a16386c7d..fe7018e84 100644 --- a/tests/TestGroupModel.cpp +++ b/tests/TestGroupModel.cpp @@ -27,6 +27,8 @@ #include "crypto/Crypto.h" #include "gui/group/GroupModel.h" +QTEST_GUILESS_MAIN(TestGroupModel) + void TestGroupModel::initTestCase() { qRegisterMetaType("QModelIndex"); @@ -149,5 +151,3 @@ void TestGroupModel::test() delete modelTest; delete model; } - -QTEST_GUILESS_MAIN(TestGroupModel) diff --git a/tests/TestHashedBlockStream.cpp b/tests/TestHashedBlockStream.cpp index ab7d386a6..b062908f7 100644 --- a/tests/TestHashedBlockStream.cpp +++ b/tests/TestHashedBlockStream.cpp @@ -24,6 +24,8 @@ #include "crypto/Crypto.h" #include "streams/HashedBlockStream.h" +QTEST_GUILESS_MAIN(TestHashedBlockStream) + void TestHashedBlockStream::initTestCase() { Crypto::init(); @@ -69,5 +71,3 @@ void TestHashedBlockStream::testWriteRead() buffer.reset(); buffer.buffer().clear(); } - -QTEST_GUILESS_MAIN(TestHashedBlockStream) diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp index 3ec4e7830..423087c91 100644 --- a/tests/TestKeePass1Reader.cpp +++ b/tests/TestKeePass1Reader.cpp @@ -33,6 +33,8 @@ #include "keys/FileKey.h" #include "keys/PasswordKey.h" +QTEST_GUILESS_MAIN(TestKeePass1Reader) + void TestKeePass1Reader::initTestCase() { Crypto::init(); @@ -292,5 +294,3 @@ void TestKeePass1Reader::reopenDatabase(Database* db, const QString& password, c QVERIFY(!reader.hasError()); delete newDb; } - -QTEST_GUILESS_MAIN(TestKeePass1Reader) diff --git a/tests/TestKeePass2RandomStream.cpp b/tests/TestKeePass2RandomStream.cpp index 74a154069..328decb30 100644 --- a/tests/TestKeePass2RandomStream.cpp +++ b/tests/TestKeePass2RandomStream.cpp @@ -26,6 +26,8 @@ #include "format/KeePass2.h" #include "format/KeePass2RandomStream.h" +QTEST_GUILESS_MAIN(TestKeePass2RandomStream) + void TestKeePass2RandomStream::initTestCase() { Crypto::init(); @@ -77,5 +79,3 @@ void TestKeePass2RandomStream::test() QCOMPARE(cipherData, cipherDataEncrypt); QCOMPARE(randomStreamData, cipherData); } - -QTEST_GUILESS_MAIN(TestKeePass2RandomStream) diff --git a/tests/TestKeePass2Reader.cpp b/tests/TestKeePass2Reader.cpp index 6b1ee1e95..3dd87c4f4 100644 --- a/tests/TestKeePass2Reader.cpp +++ b/tests/TestKeePass2Reader.cpp @@ -28,6 +28,8 @@ #include "format/KeePass2Reader.h" #include "keys/PasswordKey.h" +QTEST_GUILESS_MAIN(TestKeePass2Reader) + void TestKeePass2Reader::initTestCase() { Crypto::init(); @@ -154,5 +156,3 @@ void TestKeePass2Reader::testFormat300() delete db; } - -QTEST_GUILESS_MAIN(TestKeePass2Reader) diff --git a/tests/TestKeePass2Writer.cpp b/tests/TestKeePass2Writer.cpp index bbc4992a0..f00b38e86 100644 --- a/tests/TestKeePass2Writer.cpp +++ b/tests/TestKeePass2Writer.cpp @@ -29,6 +29,8 @@ #include "format/KeePass2Writer.h" #include "keys/PasswordKey.h" +QTEST_GUILESS_MAIN(TestKeePass2Writer) + void TestKeePass2Writer::initTestCase() { Crypto::init(); @@ -104,5 +106,3 @@ void TestKeePass2Writer::cleanupTestCase() delete m_dbOrg; delete m_dbTest; } - -QTEST_GUILESS_MAIN(TestKeePass2Writer) diff --git a/tests/TestKeePass2XmlReader.cpp b/tests/TestKeePass2XmlReader.cpp index ca57db958..d9935804d 100644 --- a/tests/TestKeePass2XmlReader.cpp +++ b/tests/TestKeePass2XmlReader.cpp @@ -28,6 +28,8 @@ #include "format/KeePass2XmlReader.h" #include "config-keepassx-tests.h" +QTEST_GUILESS_MAIN(TestKeePass2XmlReader) + namespace QTest { template<> char* toString(const Uuid& uuid) @@ -378,5 +380,3 @@ void TestKeePass2XmlReader::cleanupTestCase() { delete m_db; } - -QTEST_GUILESS_MAIN(TestKeePass2XmlReader) diff --git a/tests/TestKeys.cpp b/tests/TestKeys.cpp index ec9a35b4b..d5cba4abc 100644 --- a/tests/TestKeys.cpp +++ b/tests/TestKeys.cpp @@ -31,6 +31,8 @@ #include "keys/FileKey.h" #include "keys/PasswordKey.h" +QTEST_GUILESS_MAIN(TestKeys) + void TestKeys::initTestCase() { Crypto::init(); @@ -184,5 +186,3 @@ void TestKeys::benchmarkTransformKey() compositeKey.transform(seed, 1e6); } } - -QTEST_GUILESS_MAIN(TestKeys) diff --git a/tests/TestModified.cpp b/tests/TestModified.cpp index 864ea1c36..85a6bf23e 100644 --- a/tests/TestModified.cpp +++ b/tests/TestModified.cpp @@ -27,6 +27,8 @@ #include "core/Tools.h" #include "crypto/Crypto.h" +QTEST_GUILESS_MAIN(TestModified) + void TestModified::initTestCase() { Crypto::init(); @@ -466,5 +468,3 @@ void TestModified::testHistoryItem() delete db; } - -QTEST_GUILESS_MAIN(TestModified) diff --git a/tests/TestQCommandLineParser.cpp b/tests/TestQCommandLineParser.cpp index d487862c0..4e2c63505 100644 --- a/tests/TestQCommandLineParser.cpp +++ b/tests/TestQCommandLineParser.cpp @@ -46,6 +46,8 @@ #include "tests.h" #include "core/qcommandlineparser.h" +QTEST_GUILESS_MAIN(TestQCommandLineParser) + Q_DECLARE_METATYPE(char**) static char *empty_argv[] = { 0 }; @@ -412,5 +414,3 @@ void TestQCommandLineParser::testSingleDashWordOptionModes() QCOMPARE(parser.value(parser.optionNames().at(i)), expectedOptionValues.at(i)); QCOMPARE(parser.unknownOptionNames(), QStringList()); } - -QTEST_GUILESS_MAIN(TestQCommandLineParser) diff --git a/tests/TestQSaveFile.cpp b/tests/TestQSaveFile.cpp index bccee0ec4..6be714efd 100644 --- a/tests/TestQSaveFile.cpp +++ b/tests/TestQSaveFile.cpp @@ -29,6 +29,8 @@ #include "tests.h" #include "core/qsavefile.h" +QTEST_GUILESS_MAIN(TestQSaveFile) + class DirCleanup { public: @@ -197,5 +199,3 @@ QString TestQSaveFile::tmpDir() return dirName; } - -QTEST_GUILESS_MAIN(TestQSaveFile) diff --git a/tests/TestRandom.cpp b/tests/TestRandom.cpp index 8ac570e1c..40ab702db 100644 --- a/tests/TestRandom.cpp +++ b/tests/TestRandom.cpp @@ -22,6 +22,8 @@ #include +QTEST_GUILESS_MAIN(TestRandom) + void TestRandom::initTestCase() { m_backend = new RandomBackendTest(); @@ -93,5 +95,3 @@ void RandomBackendTest::setNextBytes(const QByteArray& nextBytes) m_nextBytes = nextBytes; m_bytesIndex = 0; } - -QTEST_GUILESS_MAIN(TestRandom) diff --git a/tests/TestSymmetricCipher.cpp b/tests/TestSymmetricCipher.cpp index b47a00576..9f05db1bd 100644 --- a/tests/TestSymmetricCipher.cpp +++ b/tests/TestSymmetricCipher.cpp @@ -25,6 +25,8 @@ #include "crypto/SymmetricCipher.h" #include "streams/SymmetricCipherStream.h" +QTEST_GUILESS_MAIN(TestSymmetricCipher) + void TestSymmetricCipher::initTestCase() { Crypto::init(); @@ -192,5 +194,3 @@ void TestSymmetricCipher::testPadding() QByteArray decrypted = streamDec.readAll(); QCOMPARE(decrypted, plainText); } - -QTEST_GUILESS_MAIN(TestSymmetricCipher) diff --git a/tests/TestWildcardMatcher.cpp b/tests/TestWildcardMatcher.cpp index e06125b86..dc9991db9 100644 --- a/tests/TestWildcardMatcher.cpp +++ b/tests/TestWildcardMatcher.cpp @@ -22,6 +22,8 @@ #include "tests.h" #include "autotype/WildcardMatcher.h" +QTEST_GUILESS_MAIN(TestWildcardMatcher) + const QString TestWildcardMatcher::DefaultText = QString("some text"); const QString TestWildcardMatcher::AlternativeText = QString("some other text"); @@ -82,5 +84,3 @@ void TestWildcardMatcher::verifyNoMatch(QString pattern) bool matchResult = m_matcher->match(pattern); QVERIFY(!matchResult); } - -QTEST_GUILESS_MAIN(TestWildcardMatcher) From a6d44034a4bb826a7256fdd56727d9f05f5e87f4 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Fri, 16 May 2014 12:33:10 +0200 Subject: [PATCH 18/39] Put test executables into their default location. --- tests/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d3f51ac41..c094f8235 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src) add_definitions(-DQT_TEST_LIB) From 2d8ba2b394e2988ca7b6b0115c4588bb9a213a06 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Fri, 16 May 2014 12:33:29 +0200 Subject: [PATCH 19/39] Focus the search field instead of closing it when pressing the shortcut. Closes #124 --- src/gui/DatabaseWidget.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index fc533a3f3..c42dd5e07 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -681,8 +681,16 @@ void DatabaseWidget::switchToImportKeepass1(const QString& fileName) void DatabaseWidget::toggleSearch() { - if (m_entryView->inEntryListMode()) { - closeSearch(); + if (isInSearchMode()) { + if (m_searchUi->searchEdit->hasFocus()) { + closeSearch(); + } + else { + m_searchUi->searchEdit->selectAll(); + m_searchUi->searchEdit->setFocus(); + // make sure the search action is checked again + emitCurrentModeChanged(); + } } else { showSearch(); From 552ca7bf7198d75499d626c65d298d2f83a95783 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 18:56:01 +0200 Subject: [PATCH 20/39] Stop search timer when closing search. --- src/gui/DatabaseWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index c42dd5e07..c111f8d52 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -701,6 +701,7 @@ void DatabaseWidget::closeSearch() { Q_ASSERT(m_lastGroup); m_groupView->setCurrentGroup(m_lastGroup); + m_searchTimer->stop(); } void DatabaseWidget::showSearch() From 4ab887c773656737abce2c4d0225357dc45b35c5 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Fri, 16 May 2014 19:10:30 +0200 Subject: [PATCH 21/39] Initally select first entry in EntryView. Closes #104 --- src/gui/entry/EntryView.cpp | 15 +++++++++++++-- src/gui/entry/EntryView.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 4691ed655..6c43df3bf 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -62,13 +62,24 @@ void EntryView::keyPressEvent(QKeyEvent* event) void EntryView::setGroup(Group* group) { m_model->setGroup(group); - Q_EMIT entrySelectionChanged(); + setFirstEntryActive(); } void EntryView::setEntryList(const QList& entries) { m_model->setEntryList(entries); - Q_EMIT entrySelectionChanged(); + setFirstEntryActive(); +} + +void EntryView::setFirstEntryActive() +{ + if(m_model->rowCount() > 0) { + QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0)); + setCurrentEntry(m_model->entryFromIndex(index)); + } + else { + Q_EMIT entrySelectionChanged(); + } } bool EntryView::inEntryListMode() diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index ba2919e91..c11d0417d 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -42,6 +42,7 @@ public: void setEntryList(const QList& entries); bool inEntryListMode(); int numberOfSelectedEntries(); + void setFirstEntryActive(); public Q_SLOTS: void setGroup(Group* group); From 05de45dadb44e34f36c7898bb812379ee278acbe Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Fri, 16 May 2014 19:49:58 +0200 Subject: [PATCH 22/39] Improve tab order. --- src/gui/DatabaseWidget.cpp | 4 ++++ src/gui/SearchWidget.ui | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index c111f8d52..00125a1c5 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -102,6 +102,10 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) rightHandSideWidget->setLayout(vLayout); + setTabOrder(m_searchUi->searchRootRadioButton, m_entryView); + setTabOrder(m_entryView, m_groupView); + setTabOrder(m_groupView, m_searchWidget); + splitter->addWidget(m_groupView); splitter->addWidget(rightHandSideWidget); diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui index c3d59b8d1..ce4845dcd 100644 --- a/src/gui/SearchWidget.ui +++ b/src/gui/SearchWidget.ui @@ -11,7 +11,16 @@ - + + 0 + + + 0 + + + 0 + + 0 @@ -21,6 +30,9 @@ + + Qt::ClickFocus + true @@ -38,7 +50,16 @@ - + + 0 + + + 0 + + + 0 + + 0 From d874f58a394d9ca7bfe91fb8bd9218929f1b755f Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:16:27 +0200 Subject: [PATCH 23/39] Synchronize DatabaseWidget splitter sizes. --- src/CMakeLists.txt | 2 + src/gui/DatabaseTabWidget.cpp | 9 ++++ src/gui/DatabaseTabWidget.h | 4 ++ src/gui/DatabaseWidget.cpp | 22 ++++++--- src/gui/DatabaseWidget.h | 7 +++ src/gui/DatabaseWidgetStateSync.cpp | 72 +++++++++++++++++++++++++++++ src/gui/DatabaseWidgetStateSync.h | 44 ++++++++++++++++++ 7 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 src/gui/DatabaseWidgetStateSync.cpp create mode 100644 src/gui/DatabaseWidgetStateSync.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b09501cf7..3de2422a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,6 +75,7 @@ set(keepassx_SOURCES gui/DatabaseSettingsWidget.cpp gui/DatabaseTabWidget.cpp gui/DatabaseWidget.cpp + gui/DatabaseWidgetStateSync.cpp gui/DialogyWidget.cpp gui/DragTabBar.cpp gui/EditWidget.cpp @@ -156,6 +157,7 @@ set(keepassx_MOC gui/DatabaseSettingsWidget.h gui/DatabaseTabWidget.h gui/DatabaseWidget.h + gui/DatabaseWidgetStateSync.h gui/DialogyWidget.h gui/DragTabBar.h gui/EditWidget.h diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index c15d16e5a..7f9984615 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -27,6 +27,7 @@ #include "core/Metadata.h" #include "core/qsavefile.h" #include "gui/DatabaseWidget.h" +#include "gui/DatabaseWidgetStateSync.h" #include "gui/DragTabBar.h" #include "gui/FileDialog.h" #include "gui/MessageBox.h" @@ -46,12 +47,15 @@ const int DatabaseTabWidget::LastDatabasesCount = 5; DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) : QTabWidget(parent) + , m_dbWidgetSateSync(new DatabaseWidgetStateSync(this)) { DragTabBar* tabBar = new DragTabBar(this); tabBar->setDrawBase(false); setTabBar(tabBar); connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabase(int))); + connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged())); + connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetSateSync, SLOT(setActive(DatabaseWidget*))); connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType())); } @@ -584,6 +588,11 @@ void DatabaseTabWidget::changeDatabase(Database* newDb) connectDatabase(newDb, oldDb); } +void DatabaseTabWidget::emitActivateDatabaseChanged() +{ + Q_EMIT activateDatabaseChanged(currentDatabaseWidget()); +} + void DatabaseTabWidget::connectDatabase(Database* newDb, Database* oldDb) { if (oldDb) { diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 8e8fceb38..25d34f304 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -25,6 +25,7 @@ #include "gui/DatabaseWidget.h" class DatabaseWidget; +class DatabaseWidgetStateSync; class DatabaseOpenWidget; class QFile; @@ -75,6 +76,7 @@ public Q_SLOTS: Q_SIGNALS: void tabNameChanged(); void databaseWithFileClosed(QString filePath); + void activateDatabaseChanged(DatabaseWidget* dbWidget); private Q_SLOTS: void updateTabName(Database* db); @@ -83,6 +85,7 @@ private Q_SLOTS: void modified(); void toggleTabbar(); void changeDatabase(Database* newDb); + void emitActivateDatabaseChanged(); private: void saveDatabase(Database* db); @@ -99,6 +102,7 @@ private: KeePass2Writer m_writer; QHash m_dbList; + DatabaseWidgetStateSync* m_dbWidgetSateSync; }; #endif // KEEPASSX_DATABASETABWIDGET_H diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 00125a1c5..8173fe968 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -61,12 +61,12 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_mainWidget = new QWidget(this); QLayout* layout = new QHBoxLayout(m_mainWidget); - QSplitter* splitter = new QSplitter(m_mainWidget); + m_splitter = new QSplitter(m_mainWidget); - QWidget* rightHandSideWidget = new QWidget(splitter); + QWidget* rightHandSideWidget = new QWidget(m_splitter); m_searchWidget->setParent(rightHandSideWidget); - m_groupView = new GroupView(db, splitter); + m_groupView = new GroupView(db, m_splitter); m_groupView->setObjectName("groupView"); m_groupView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_groupView, SIGNAL(customContextMenuRequested(QPoint)), @@ -106,10 +106,10 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) setTabOrder(m_entryView, m_groupView); setTabOrder(m_groupView, m_searchWidget); - splitter->addWidget(m_groupView); - splitter->addWidget(rightHandSideWidget); + m_splitter->addWidget(m_groupView); + m_splitter->addWidget(rightHandSideWidget); - layout->addWidget(splitter); + layout->addWidget(m_splitter); m_mainWidget->setLayout(layout); m_editEntryWidget = new EditEntryWidget(); @@ -141,6 +141,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) addWidget(m_keepass1OpenWidget); addWidget(m_unlockDatabaseWidget); + connect(m_splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterSizesChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(clearLastGroup(Group*))); connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), m_entryView, SLOT(setGroup(Group*))); @@ -200,6 +201,15 @@ bool DatabaseWidget::isInEditMode() const } } +QList DatabaseWidget::splitterSizes() const +{ + return m_splitter->sizes(); +} + +void DatabaseWidget::setSplitterSizes(const QList& sizes) +{ + m_splitter->setSizes(sizes); +} void DatabaseWidget::emitCurrentModeChanged() { diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 5e77f01b3..628a344e9 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -38,6 +38,7 @@ class GroupView; class KeePass1OpenWidget; class QFile; class QMenu; +class QSplitter; class UnlockDatabaseWidget; namespace Ui { @@ -75,6 +76,8 @@ public: QStringList customEntryAttributes() const; bool isGroupSelected()const ; bool isInEditMode() const; + QList splitterSizes() const; + void setSplitterSizes(const QList& sizes); Q_SIGNALS: void closeRequest(); @@ -85,6 +88,9 @@ Q_SIGNALS: void groupContextMenuRequested(const QPoint& globalPos); void entryContextMenuRequested(const QPoint& globalPos); void unlockedDatabase(); + void listModeActivated(); + void searchModeActivated(); + void splitterSizesChanged(); public Q_SLOTS: void createEntry(); @@ -147,6 +153,7 @@ private: DatabaseOpenWidget* m_databaseOpenWidget; KeePass1OpenWidget* m_keepass1OpenWidget; UnlockDatabaseWidget* m_unlockDatabaseWidget; + QSplitter* m_splitter; GroupView* m_groupView; EntryView* m_entryView; Group* m_newGroup; diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp new file mode 100644 index 000000000..2a071e306 --- /dev/null +++ b/src/gui/DatabaseWidgetStateSync.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "DatabaseWidgetStateSync.h" + +#include "core/Config.h" + +DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent) + : QObject(parent) + , m_activeDbWidget(Q_NULLPTR) +{ + QVariantList variantList = config()->get("GUI/SplitterState").toList(); + Q_FOREACH (const QVariant& var, variantList) { + bool ok; + int size = var.toInt(&ok); + if (ok) { + m_splitterSizes.append(size); + } + else { + m_splitterSizes.clear(); + break; + } + } +} + +DatabaseWidgetStateSync::~DatabaseWidgetStateSync() +{ + QVariantList variantList; + Q_FOREACH (int size, m_splitterSizes) { + variantList.append(size); + } + + config()->set("GUI/SplitterState", variantList); +} + +void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) +{ + if (m_activeDbWidget) { + disconnect(m_activeDbWidget, 0, this, 0); + } + + m_activeDbWidget = dbWidget; + + if (m_activeDbWidget) { + if (!m_splitterSizes.isEmpty()) { + m_activeDbWidget->setSplitterSizes(m_splitterSizes); + } + + connect(m_activeDbWidget, SIGNAL(splitterSizesChanged()), + SLOT(updateSplitterSizes())); + } +} + +void DatabaseWidgetStateSync::updateSplitterSizes() +{ + m_splitterSizes = m_activeDbWidget->splitterSizes(); +} diff --git a/src/gui/DatabaseWidgetStateSync.h b/src/gui/DatabaseWidgetStateSync.h new file mode 100644 index 000000000..574519bd4 --- /dev/null +++ b/src/gui/DatabaseWidgetStateSync.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 Felix Geyer + * Copyright (C) 2014 Florian Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_HEADERVIEWSYNC_H +#define KEEPASSX_HEADERVIEWSYNC_H + +#include "gui/DatabaseWidget.h" + +class DatabaseWidgetStateSync : public QObject +{ + Q_OBJECT + +public: + explicit DatabaseWidgetStateSync(QObject* parent = Q_NULLPTR); + ~DatabaseWidgetStateSync(); + +public Q_SLOTS: + void setActive(DatabaseWidget* dbWidget); + +private Q_SLOTS: + void updateSplitterSizes(); + +private: + DatabaseWidget* m_activeDbWidget; + + QList m_splitterSizes; +}; + +#endif // KEEPASSX_HEADERVIEWSYNC_H From 9ac01c930d36ed9ff1e59f7315f1adabf0f5aa18 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:21:17 +0200 Subject: [PATCH 24/39] Drop DatabaseWidget::groupView() and entryView(). --- src/gui/DatabaseWidget.cpp | 10 ---------- src/gui/DatabaseWidget.h | 2 -- tests/gui/TestGui.cpp | 16 ++++++++-------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 8173fe968..f3738c734 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -216,16 +216,6 @@ void DatabaseWidget::emitCurrentModeChanged() Q_EMIT currentModeChanged(currentMode()); } -GroupView* DatabaseWidget::groupView() -{ - return m_groupView; -} - -EntryView* DatabaseWidget::entryView() -{ - return m_entryView; -} - Database* DatabaseWidget::database() { return m_db; diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 628a344e9..4a5765df9 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -60,8 +60,6 @@ public: explicit DatabaseWidget(Database* db, QWidget* parent = Q_NULLPTR); ~DatabaseWidget(); - GroupView* groupView(); - EntryView* entryView(); Database* database(); bool dbHasKey() const; bool canDeleteCurrentGroup() const; diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index a4d04c5de..51dc39e5d 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -83,7 +83,7 @@ void TestGui::testTabs() void TestGui::testEditEntry() { - EntryView* entryView = m_dbWidget->entryView(); + EntryView* entryView = m_dbWidget->findChild("entryView"); QModelIndex item = entryView->model()->index(0, 1); QRect itemRect = entryView->visualRect(item); QTest::mouseClick(entryView->viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center()); @@ -237,8 +237,8 @@ void TestGui::testSearch() void TestGui::testDeleteEntry() { - GroupView* groupView = m_dbWidget->groupView(); - EntryView* entryView = m_dbWidget->entryView(); + GroupView* groupView = m_dbWidget->findChild("groupView"); + EntryView* entryView = m_dbWidget->findChild("entryView"); QToolBar* toolBar = m_mainWindow->findChild("toolBar"); QAction* entryDeleteAction = m_mainWindow->findChild("actionEntryDelete"); QWidget* entryDeleteWidget = toolBar->widgetForAction(entryDeleteAction); @@ -274,7 +274,7 @@ void TestGui::testDeleteEntry() void TestGui::testCloneEntry() { - EntryView* entryView = m_dbWidget->entryView(); + EntryView* entryView = m_dbWidget->findChild("entryView"); QCOMPARE(entryView->model()->rowCount(), 1); @@ -292,8 +292,8 @@ void TestGui::testCloneEntry() void TestGui::testDragAndDropEntry() { - EntryView* entryView = m_dbWidget->entryView(); - GroupView* groupView = m_dbWidget->groupView(); + EntryView* entryView = m_dbWidget->findChild("entryView"); + GroupView* groupView = m_dbWidget->findChild("groupView"); QAbstractItemModel* groupModel = groupView->model(); QModelIndex sourceIndex = entryView->model()->index(0, 1); @@ -314,7 +314,7 @@ void TestGui::testDragAndDropEntry() void TestGui::testDragAndDropGroup() { - QAbstractItemModel* groupModel = m_dbWidget->groupView()->model(); + QAbstractItemModel* groupModel = m_dbWidget->findChild("groupView")->model(); QModelIndex rootIndex = groupModel->index(0, 0); dragAndDropGroup(groupModel->index(0, 0, rootIndex), @@ -453,7 +453,7 @@ void TestGui::dragAndDropGroup(const QModelIndex& sourceIndex, const QModelIndex QVERIFY(sourceIndex.isValid()); QVERIFY(targetIndex.isValid()); - GroupModel* groupModel = qobject_cast(m_dbWidget->groupView()->model()); + GroupModel* groupModel = qobject_cast(m_dbWidget->findChild("groupView")->model()); QMimeData mimeData; QByteArray encoded; From 584f4b50bff944fffba09eb6d5fcf18c9d75414e Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:21:50 +0200 Subject: [PATCH 25/39] Coding style fix. --- src/gui/DatabaseWidget.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 4a5765df9..53906f34b 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -72,7 +72,7 @@ public: void updateFilename(const QString& filename); int numberOfSelectedEntries() const; QStringList customEntryAttributes() const; - bool isGroupSelected()const ; + bool isGroupSelected() const; bool isInEditMode() const; QList splitterSizes() const; void setSplitterSizes(const QList& sizes); From 8a4100adbd5ba85eedb301220ed56ac624a00a15 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:22:45 +0200 Subject: [PATCH 26/39] Make DatabaseWidget::emit{Group,Entry}ContextMenuRequested() private. --- src/gui/DatabaseWidget.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 53906f34b..2e2077bcc 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -113,8 +113,6 @@ public Q_SLOTS: void switchToOpenDatabase(const QString& fileName, const QString& password, const QString& keyFile); void switchToImportKeepass1(const QString& fileName); void toggleSearch(); - void emitGroupContextMenuRequested(const QPoint& pos); - void emitEntryContextMenuRequested(const QPoint& pos); private Q_SLOTS: void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column); @@ -124,6 +122,8 @@ private Q_SLOTS: void switchToEntryEdit(Entry* entry); void switchToEntryEdit(Entry* entry, bool create); void switchToGroupEdit(Group* entry, bool create); + void emitGroupContextMenuRequested(const QPoint& pos); + void emitEntryContextMenuRequested(const QPoint& pos); void updateMasterKey(bool accepted); void openDatabase(bool accepted); void unlockDatabase(bool accepted); From 76da4a6cd48ce61eb519a375d4c1aed8abe74161 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:25:45 +0200 Subject: [PATCH 27/39] Use QSplitter::setStretchFactor() convenience method. --- src/gui/DatabaseWidget.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index f3738c734..131776e41 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -79,14 +79,6 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) connect(m_entryView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(emitEntryContextMenuRequested(QPoint))); - QSizePolicy policy; - policy = m_groupView->sizePolicy(); - policy.setHorizontalStretch(30); - m_groupView->setSizePolicy(policy); - policy = rightHandSideWidget->sizePolicy(); - policy.setHorizontalStretch(70); - rightHandSideWidget->setSizePolicy(policy); - QAction* closeAction = new QAction(m_searchWidget); QIcon closeIcon = filePath()->icon("actions", "dialog-close"); closeAction->setIcon(closeIcon); @@ -109,6 +101,9 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_splitter->addWidget(m_groupView); m_splitter->addWidget(rightHandSideWidget); + m_splitter->setStretchFactor(0, 30); + m_splitter->setStretchFactor(1, 70); + layout->addWidget(m_splitter); m_mainWidget->setLayout(layout); From 0e75e6ff03d27685330358806d3886590c020d7e Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:27:04 +0200 Subject: [PATCH 28/39] Make DatabaseWidget splitter non-collapsible. --- src/gui/DatabaseWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 131776e41..9a09814b7 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -62,6 +62,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_mainWidget = new QWidget(this); QLayout* layout = new QHBoxLayout(m_mainWidget); m_splitter = new QSplitter(m_mainWidget); + m_splitter->setChildrenCollapsible(false); QWidget* rightHandSideWidget = new QWidget(m_splitter); m_searchWidget->setParent(rightHandSideWidget); From a25b28ffee309f51c64a131925dac3ff047db592 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 11:38:48 +0200 Subject: [PATCH 29/39] Rename config option window/Geometry to GUI/MainWindowGeometry. --- src/gui/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c83f3167f..8c72bbff9 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -38,7 +38,7 @@ MainWindow::MainWindow() m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size(); - restoreGeometry(config()->get("window/Geometry").toByteArray()); + restoreGeometry(config()->get("GUI/MainWindowGeometry").toByteArray()); setWindowIcon(filePath()->applicationIcon()); QAction* toggleViewAction = m_ui->toolBar->toggleViewAction(); @@ -437,7 +437,7 @@ void MainWindow::closeEvent(QCloseEvent* event) void MainWindow::saveWindowInformation() { - config()->set("window/Geometry", saveGeometry()); + config()->set("GUI/MainWindowGeometry", saveGeometry()); } bool MainWindow::saveLastDatabases() From e776de8eebed90af6b41c9df34e977795bcb4468 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 12:51:16 +0200 Subject: [PATCH 30/39] Remember and synchronize entry column sizes. Closes #159 --- src/gui/DatabaseWidget.cpp | 28 +++++++++ src/gui/DatabaseWidget.h | 3 + src/gui/DatabaseWidgetStateSync.cpp | 92 +++++++++++++++++++++++------ src/gui/DatabaseWidgetStateSync.h | 8 +++ 4 files changed, 113 insertions(+), 18 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 9a09814b7..5379e1a1f 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -138,6 +138,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) addWidget(m_unlockDatabaseWidget); connect(m_splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterSizesChanged())); + connect(m_entryView->header(), SIGNAL(sectionResized(int,int,int)), SIGNAL(entryColumnSizesChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(clearLastGroup(Group*))); connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), m_entryView, SLOT(setGroup(Group*))); @@ -207,6 +208,29 @@ void DatabaseWidget::setSplitterSizes(const QList& sizes) m_splitter->setSizes(sizes); } +QList DatabaseWidget::entryHeaderViewSizes() const +{ + QList sizes; + + for (int i = 0; i < m_entryView->header()->count(); i++) { + sizes.append(m_entryView->header()->sectionSize(i)); + } + + return sizes; +} + +void DatabaseWidget::setEntryViewHeaderSizes(const QList& sizes) +{ + if (sizes.size() != m_entryView->header()->count()) { + Q_ASSERT(false); + return; + } + + for (int i = 0; i < sizes.size(); i++) { + m_entryView->header()->resizeSection(i, sizes[i]); + } +} + void DatabaseWidget::emitCurrentModeChanged() { Q_EMIT currentModeChanged(currentMode()); @@ -702,6 +726,8 @@ void DatabaseWidget::closeSearch() Q_ASSERT(m_lastGroup); m_groupView->setCurrentGroup(m_lastGroup); m_searchTimer->stop(); + + Q_EMIT listModeActivated(); } void DatabaseWidget::showSearch() @@ -739,6 +765,8 @@ void DatabaseWidget::showSearch() m_searchWidget->show(); search(); m_searchUi->searchEdit->setFocus(); + + Q_EMIT searchModeActivated(); } void DatabaseWidget::search() diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 2e2077bcc..981784d48 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -76,6 +76,8 @@ public: bool isInEditMode() const; QList splitterSizes() const; void setSplitterSizes(const QList& sizes); + QList entryHeaderViewSizes() const; + void setEntryViewHeaderSizes(const QList& sizes); Q_SIGNALS: void closeRequest(); @@ -89,6 +91,7 @@ Q_SIGNALS: void listModeActivated(); void searchModeActivated(); void splitterSizesChanged(); + void entryColumnSizesChanged(); public Q_SLOTS: void createEntry(); diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp index 2a071e306..c9e60bb93 100644 --- a/src/gui/DatabaseWidgetStateSync.cpp +++ b/src/gui/DatabaseWidgetStateSync.cpp @@ -24,28 +24,16 @@ DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent) : QObject(parent) , m_activeDbWidget(Q_NULLPTR) { - QVariantList variantList = config()->get("GUI/SplitterState").toList(); - Q_FOREACH (const QVariant& var, variantList) { - bool ok; - int size = var.toInt(&ok); - if (ok) { - m_splitterSizes.append(size); - } - else { - m_splitterSizes.clear(); - break; - } - } + m_splitterSizes = variantToIntList(config()->get("GUI/SplitterState")); + m_columnSizesList = variantToIntList(config()->get("GUI/EntryListColumnSizes")); + m_columnSizesSearch = variantToIntList(config()->get("GUI/EntrySearchColumnSizes")); } DatabaseWidgetStateSync::~DatabaseWidgetStateSync() { - QVariantList variantList; - Q_FOREACH (int size, m_splitterSizes) { - variantList.append(size); - } - - config()->set("GUI/SplitterState", variantList); + config()->set("GUI/SplitterState", intListToVariant(m_splitterSizes)); + config()->set("GUI/EntryListColumnSizes", intListToVariant(m_columnSizesList)); + config()->set("GUI/EntrySearchColumnSizes", intListToVariant(m_columnSizesSearch)); } void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) @@ -61,8 +49,35 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) m_activeDbWidget->setSplitterSizes(m_splitterSizes); } + if (m_activeDbWidget->isGroupSelected()) { + restoreListView(); + } + else { + restoreSearchView(); + } + connect(m_activeDbWidget, SIGNAL(splitterSizesChanged()), SLOT(updateSplitterSizes())); + connect(m_activeDbWidget, SIGNAL(entryColumnSizesChanged()), + SLOT(updateColumnSizes())); + connect(m_activeDbWidget, SIGNAL(listModeActivated()), + SLOT(restoreListView())); + connect(m_activeDbWidget, SIGNAL(searchModeActivated()), + SLOT(restoreSearchView())); + } +} + +void DatabaseWidgetStateSync::restoreListView() +{ + if (!m_columnSizesList.isEmpty()) { + m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesList); + } +} + +void DatabaseWidgetStateSync::restoreSearchView() +{ + if (!m_columnSizesSearch.isEmpty()) { + m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesSearch); } } @@ -70,3 +85,44 @@ void DatabaseWidgetStateSync::updateSplitterSizes() { m_splitterSizes = m_activeDbWidget->splitterSizes(); } + +void DatabaseWidgetStateSync::updateColumnSizes() +{ + if (m_activeDbWidget->isGroupSelected()) { + m_columnSizesList = m_activeDbWidget->entryHeaderViewSizes(); + } + else { + m_columnSizesSearch = m_activeDbWidget->entryHeaderViewSizes(); + } +} + +QList DatabaseWidgetStateSync::variantToIntList(const QVariant& variant) +{ + QVariantList list = variant.toList(); + QList result; + + Q_FOREACH (const QVariant& var, list) { + bool ok; + int size = var.toInt(&ok); + if (ok) { + result.append(size); + } + else { + result.clear(); + break; + } + } + + return result; +} + +QVariant DatabaseWidgetStateSync::intListToVariant(const QList& list) +{ + QVariantList result; + + Q_FOREACH (int value, list) { + result.append(value); + } + + return result; +} diff --git a/src/gui/DatabaseWidgetStateSync.h b/src/gui/DatabaseWidgetStateSync.h index 574519bd4..26911837c 100644 --- a/src/gui/DatabaseWidgetStateSync.h +++ b/src/gui/DatabaseWidgetStateSync.h @@ -31,14 +31,22 @@ public: public Q_SLOTS: void setActive(DatabaseWidget* dbWidget); + void restoreListView(); + void restoreSearchView(); private Q_SLOTS: void updateSplitterSizes(); + void updateColumnSizes(); private: + static QList variantToIntList(const QVariant& variant); + static QVariant intListToVariant(const QList& list); + DatabaseWidget* m_activeDbWidget; QList m_splitterSizes; + QList m_columnSizesList; + QList m_columnSizesSearch; }; #endif // KEEPASSX_HEADERVIEWSYNC_H From c806f9ebf4f594f1caf194d61c4fbfaeafdcef13 Mon Sep 17 00:00:00 2001 From: Florian Geyer Date: Sat, 17 May 2014 18:13:22 +0200 Subject: [PATCH 31/39] Correct tr-calls. --- src/gui/DatabaseOpenWidget.cpp | 6 +++--- src/gui/KeePass1OpenWidget.cpp | 4 ++-- src/gui/entry/EditEntryWidget.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 973192523..37bbce747 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -117,8 +117,8 @@ void DatabaseOpenWidget::openDatabase() Q_EMIT editFinished(true); } else { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.\n%1") - .arg(reader.errorString())); + MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") + .append(reader.errorString())); m_ui->editPassword->clear(); } } @@ -138,7 +138,7 @@ CompositeKey DatabaseOpenWidget::databaseKey() QString keyFilename = m_ui->comboKeyFile->currentText(); QString errorMsg; if (!key.load(keyFilename, &errorMsg)) { - MessageBox::warning(this, tr("Error"), tr("Can't open key file:\n%1").arg(errorMsg)); + MessageBox::warning(this, tr("Error"), tr("Can't open key file").append(":\n").append(errorMsg)); return CompositeKey(); } masterKey.addKey(key); diff --git a/src/gui/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp index 5f23d8070..96ddf13f2 100644 --- a/src/gui/KeePass1OpenWidget.cpp +++ b/src/gui/KeePass1OpenWidget.cpp @@ -64,8 +64,8 @@ void KeePass1OpenWidget::openDatabase() Q_EMIT editFinished(true); } else { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.\n%1") - .arg(reader.errorString())); + MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") + .append(reader.errorString())); m_ui->editPassword->clear(); } } diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 4a6743324..4a866680b 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -590,14 +590,14 @@ void EditEntryWidget::insertAttachment() QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { MessageBox::warning(this, tr("Error"), - tr("Unable to open file:\n").append(file.errorString())); + tr("Unable to open file").append(":\n").append(file.errorString())); return; } QByteArray data; if (!Tools::readAllFromDevice(&file, data)) { MessageBox::warning(this, tr("Error"), - tr("Unable to open file:\n").append(file.errorString())); + tr("Unable to open file").append(":\n").append(file.errorString())); return; } From 9391de74c7117ad40d8bd1d530e2620cedc101c1 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 18:05:02 +0200 Subject: [PATCH 32/39] Block non-user updates in DatabaseWidgetStateSync. --- src/gui/DatabaseWidget.cpp | 5 +++++ src/gui/DatabaseWidget.h | 2 ++ src/gui/DatabaseWidgetStateSync.cpp | 26 ++++++++++++++++++++++++++ src/gui/DatabaseWidgetStateSync.h | 2 ++ 4 files changed, 35 insertions(+) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 5379e1a1f..0779297ef 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -724,6 +724,9 @@ void DatabaseWidget::toggleSearch() void DatabaseWidget::closeSearch() { Q_ASSERT(m_lastGroup); + + Q_EMIT listModeAboutToActivate(); + m_groupView->setCurrentGroup(m_lastGroup); m_searchTimer->stop(); @@ -732,6 +735,8 @@ void DatabaseWidget::closeSearch() void DatabaseWidget::showSearch() { + Q_EMIT searchModeAboutToActivate(); + m_searchUi->searchEdit->blockSignals(true); m_searchUi->searchEdit->clear(); m_searchUi->searchEdit->blockSignals(false); diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 981784d48..cbab175e4 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -88,7 +88,9 @@ Q_SIGNALS: void groupContextMenuRequested(const QPoint& globalPos); void entryContextMenuRequested(const QPoint& globalPos); void unlockedDatabase(); + void listModeAboutToActivate(); void listModeActivated(); + void searchModeAboutToActivate(); void searchModeActivated(); void splitterSizesChanged(); void entryColumnSizesChanged(); diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp index c9e60bb93..66b8492e2 100644 --- a/src/gui/DatabaseWidgetStateSync.cpp +++ b/src/gui/DatabaseWidgetStateSync.cpp @@ -23,6 +23,7 @@ DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent) : QObject(parent) , m_activeDbWidget(Q_NULLPTR) + , m_blockUpdates(false) { m_splitterSizes = variantToIntList(config()->get("GUI/SplitterState")); m_columnSizesList = variantToIntList(config()->get("GUI/EntryListColumnSizes")); @@ -45,6 +46,8 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) m_activeDbWidget = dbWidget; if (m_activeDbWidget) { + m_blockUpdates = true; + if (!m_splitterSizes.isEmpty()) { m_activeDbWidget->setSplitterSizes(m_splitterSizes); } @@ -56,6 +59,8 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) restoreSearchView(); } + m_blockUpdates = false; + connect(m_activeDbWidget, SIGNAL(splitterSizesChanged()), SLOT(updateSplitterSizes())); connect(m_activeDbWidget, SIGNAL(entryColumnSizesChanged()), @@ -64,6 +69,10 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) SLOT(restoreListView())); connect(m_activeDbWidget, SIGNAL(searchModeActivated()), SLOT(restoreSearchView())); + connect(m_activeDbWidget, SIGNAL(listModeAboutToActivate()), + SLOT(blockUpdates())); + connect(m_activeDbWidget, SIGNAL(searchModeAboutToActivate()), + SLOT(blockUpdates())); } } @@ -72,6 +81,8 @@ void DatabaseWidgetStateSync::restoreListView() if (!m_columnSizesList.isEmpty()) { m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesList); } + + m_blockUpdates = false; } void DatabaseWidgetStateSync::restoreSearchView() @@ -79,15 +90,30 @@ void DatabaseWidgetStateSync::restoreSearchView() if (!m_columnSizesSearch.isEmpty()) { m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesSearch); } + + m_blockUpdates = false; +} + +void DatabaseWidgetStateSync::blockUpdates() +{ + m_blockUpdates = true; } void DatabaseWidgetStateSync::updateSplitterSizes() { + if (m_blockUpdates) { + return; + } + m_splitterSizes = m_activeDbWidget->splitterSizes(); } void DatabaseWidgetStateSync::updateColumnSizes() { + if (m_blockUpdates) { + return; + } + if (m_activeDbWidget->isGroupSelected()) { m_columnSizesList = m_activeDbWidget->entryHeaderViewSizes(); } diff --git a/src/gui/DatabaseWidgetStateSync.h b/src/gui/DatabaseWidgetStateSync.h index 26911837c..f6a87cd9a 100644 --- a/src/gui/DatabaseWidgetStateSync.h +++ b/src/gui/DatabaseWidgetStateSync.h @@ -35,6 +35,7 @@ public Q_SLOTS: void restoreSearchView(); private Q_SLOTS: + void blockUpdates(); void updateSplitterSizes(); void updateColumnSizes(); @@ -44,6 +45,7 @@ private: DatabaseWidget* m_activeDbWidget; + bool m_blockUpdates; QList m_splitterSizes; QList m_columnSizesList; QList m_columnSizesSearch; From 910788c038178dc7a1ef49d08870b3ffdaaabafc Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 18:14:13 +0200 Subject: [PATCH 33/39] Mark some strings as untranslatable. --- src/gui/PasswordGeneratorWidget.ui | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/PasswordGeneratorWidget.ui b/src/gui/PasswordGeneratorWidget.ui index 342f191a8..5c75ef9ea 100644 --- a/src/gui/PasswordGeneratorWidget.ui +++ b/src/gui/PasswordGeneratorWidget.ui @@ -100,7 +100,7 @@ Upper Case Letters - A-Z + A-Z true @@ -116,7 +116,7 @@ Lower Case Letters - a-z + a-z true @@ -132,7 +132,7 @@ Numbers - 0-9 + 0-9 true @@ -148,7 +148,7 @@ Special Characters - /*_& ... + /*_& ... true From 8cc1e6008eff3394afd365805c9aca6a2ab295b8 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 18:15:06 +0200 Subject: [PATCH 34/39] Use plurals in translations. --- src/gui/DatabaseWidget.cpp | 3 +-- src/gui/EditWidgetIcons.cpp | 3 +-- src/gui/entry/EditEntryWidget.cpp | 12 ++++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 0779297ef..cc9c5fd18 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -332,8 +332,7 @@ void DatabaseWidget::deleteEntries() if (selected.size() > 1) { QMessageBox::StandardButton result = MessageBox::question( this, tr("Move entries to recycle bin?"), - tr("Do you really want to move %1 entries to the recycle bin?") - .arg(selected.size()), + tr("Do you really want to move %n entry(s) to the recycle bin?", 0, selected.size()), QMessageBox::Yes | QMessageBox::No); if (result == QMessageBox::No) { return; diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 26314d360..9e8574535 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -194,8 +194,7 @@ void EditWidgetIcons::removeCustomIcon() } else { MessageBox::information(this, tr("Can't delete icon!"), - tr("Can't delete icon. Still used by %1 items.") - .arg(iconUsedCount)); + tr("Can't delete icon. Still used by %n item(s).", 0, iconUsedCount)); } } } diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 4a866680b..465f5d401 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -783,13 +783,13 @@ QMenu* EditEntryWidget::createPresetsMenu() QMenu* expirePresetsMenu = new QMenu(this); expirePresetsMenu->addAction(tr("Tomorrow"))->setData(QVariant::fromValue(TimeDelta::fromDays(1))); expirePresetsMenu->addSeparator(); - expirePresetsMenu->addAction(tr("1 week"))->setData(QVariant::fromValue(TimeDelta::fromDays(7))); - expirePresetsMenu->addAction(tr("2 weeks"))->setData(QVariant::fromValue(TimeDelta::fromDays(14))); - expirePresetsMenu->addAction(tr("3 weeks"))->setData(QVariant::fromValue(TimeDelta::fromDays(21))); + expirePresetsMenu->addAction(tr("%n week(s)", 0, 1))->setData(QVariant::fromValue(TimeDelta::fromDays(7))); + expirePresetsMenu->addAction(tr("%n week(s)", 0, 2))->setData(QVariant::fromValue(TimeDelta::fromDays(14))); + expirePresetsMenu->addAction(tr("%n week(s)", 0, 3))->setData(QVariant::fromValue(TimeDelta::fromDays(21))); expirePresetsMenu->addSeparator(); - expirePresetsMenu->addAction(tr("1 month"))->setData(QVariant::fromValue(TimeDelta::fromMonths(1))); - expirePresetsMenu->addAction(tr("3 months"))->setData(QVariant::fromValue(TimeDelta::fromMonths(3))); - expirePresetsMenu->addAction(tr("6 months"))->setData(QVariant::fromValue(TimeDelta::fromMonths(6))); + expirePresetsMenu->addAction(tr("%n month(s)", 0, 1))->setData(QVariant::fromValue(TimeDelta::fromMonths(1))); + expirePresetsMenu->addAction(tr("%n month(s)", 0, 3))->setData(QVariant::fromValue(TimeDelta::fromMonths(3))); + expirePresetsMenu->addAction(tr("%n month(s)", 0, 6))->setData(QVariant::fromValue(TimeDelta::fromMonths(6))); expirePresetsMenu->addSeparator(); expirePresetsMenu->addAction(tr("1 year"))->setData(QVariant::fromValue(TimeDelta::fromYears(1))); return expirePresetsMenu; From becd3a0019505eaa264de7b5b3fe27c46cb9c5a0 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 17 May 2014 19:01:43 +0200 Subject: [PATCH 35/39] Increase the EntryView default column size a bit. --- src/gui/entry/EntryView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 6c43df3bf..cd2c6fbad 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -17,6 +17,7 @@ #include "EntryView.h" +#include #include #include "gui/SortFilterHideProxyModel.h" @@ -40,6 +41,7 @@ EntryView::EntryView(QWidget* parent) setDragEnabled(true); setSortingEnabled(true); setSelectionMode(QAbstractItemView::ExtendedSelection); + header()->setDefaultSectionSize(150); // QAbstractItemView::startDrag() uses this property as the default drag action setDefaultDropAction(Qt::MoveAction); From 28694ae687173cf5039fd864390699310a7b0243 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 18 May 2014 01:33:22 +0200 Subject: [PATCH 36/39] Add initial support for translations. --- .tx/config | 8 + share/CMakeLists.txt | 2 + share/translations/CMakeLists.txt | 26 + share/translations/keepassx_de.ts | 1175 ++++++++++++++++++++ share/translations/keepassx_en.ts | 1189 +++++++++++++++++++++ share/translations/keepassx_en_plurals.ts | 41 + share/translations/update.sh | 8 + src/CMakeLists.txt | 1 + src/config-keepassx.h.cmake | 1 + src/core/Config.cpp | 1 + src/core/Translator.cpp | 120 +++ src/core/Translator.h | 36 + src/gui/SettingsWidget.cpp | 12 + src/gui/SettingsWidgetGeneral.ui | 12 +- src/main.cpp | 3 + 15 files changed, 2634 insertions(+), 1 deletion(-) create mode 100644 .tx/config create mode 100644 share/translations/CMakeLists.txt create mode 100644 share/translations/keepassx_de.ts create mode 100644 share/translations/keepassx_en.ts create mode 100644 share/translations/keepassx_en_plurals.ts create mode 100755 share/translations/update.sh create mode 100644 src/core/Translator.cpp create mode 100644 src/core/Translator.h diff --git a/.tx/config b/.tx/config new file mode 100644 index 000000000..015acf4b0 --- /dev/null +++ b/.tx/config @@ -0,0 +1,8 @@ +[main] +host = https://www.transifex.com + +[keepassx.keepassx_ents] +source_file = share/translations/keepassx_en.ts +file_filter = share/translations/keepassx_.ts +source_lang = en +type = QT diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt index 7069c6c4b..0e2b7fa99 100644 --- a/share/CMakeLists.txt +++ b/share/CMakeLists.txt @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +add_subdirectory(translations) + file(GLOB DATABASE_ICONS icons/database/*.png) install(FILES ${DATABASE_ICONS} DESTINATION ${DATA_INSTALL_DIR}/icons/database) diff --git a/share/translations/CMakeLists.txt b/share/translations/CMakeLists.txt new file mode 100644 index 000000000..b1aa8785b --- /dev/null +++ b/share/translations/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2014 Felix Geyer +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 or (at your option) +# version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +file(GLOB TRANSLATION_FILES *.ts) +get_filename_component(TRANSLATION_EN_ABS keepassx_en.ts ABSOLUTE) +list(REMOVE_ITEM TRANSLATION_FILES keepassx_en.ts) +list(REMOVE_ITEM TRANSLATION_FILES ${TRANSLATION_EN_ABS}) +message(STATUS ${TRANSLATION_FILES}) + +qt4_add_translation(QM_FILES ${TRANSLATION_FILES}) + +install(FILES ${QM_FILES} DESTINATION ${DATA_INSTALL_DIR}/translations) +add_custom_target(translations DEPENDS ${QM_FILES}) +add_dependencies(${PROGNAME} translations) diff --git a/share/translations/keepassx_de.ts b/share/translations/keepassx_de.ts new file mode 100644 index 000000000..fe074ae69 --- /dev/null +++ b/share/translations/keepassx_de.ts @@ -0,0 +1,1175 @@ + + + AboutDialog + + About KeePassX + Über KeePassX + + + KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. + + + + + AutoType + + Auto-Type - KeePassX + + + + Couldn't find an entry that matches the window title. + + + + + AutoTypeAssociationsModel + + Window + + + + Sequence + + + + Default sequence + + + + + AutoTypeSelectDialog + + Auto-Type - KeePassX + + + + Select entry to Auto-Type: + + + + + ChangeMasterKeyWidget + + Password + + + + Enter password: + + + + Repeat password: + + + + Key file + + + + Browse + + + + Create + + + + Key files + + + + All files + + + + Create Key File... + + + + Error + + + + Unable to create Key File : + + + + Select a key file + + + + Question + + + + Do you really want to use an empty string as password? + + + + Different passwords supplied. + + + + + DatabaseOpenWidget + + Enter master key + + + + Key File: + + + + Password: + + + + Browse + + + + Error + + + + Unable to open the database. + + + + Can't open key file + + + + All files + + + + Key files + + + + Select key file + + + + + DatabaseSettingsWidget + + Database name: + + + + Database description: + + + + Transform rounds: + + + + Default username: + + + + Use recycle bin: + + + + MiB + + + + Benchmark + + + + Max. history items: + + + + Max. history size: + + + + + DatabaseTabWidget + + Root + + + + KeePass 2 Database + + + + All files + + + + Open database + + + + Warning + + + + File not found! + + + + Open KeePass 1 database + + + + KeePass 1 database + + + + All files (*) + + + + Close? + + + + "%1" is in edit mode. +Close anyway? + + + + Save changes? + + + + "%1" was modified. +Save changes? + + + + Error + + + + Writing the database failed. + + + + Save database as + + + + New database + + + + locked + + + + + DatabaseWidget + + Change master key + + + + Delete entry? + + + + Do you really want to delete the entry "%1" for good? + + + + Delete entries? + + + + Do you really want to delete %1 entries for good? + + + + Move entries to recycle bin? + + + + Do you really want to move %n entry(s) to the recycle bin? + + + + Delete group? + + + + Do you really want to delete the group "%1" for good? + + + + Current group + + + + + EditEntryWidget + + Entry + + + + Advanced + + + + Icon + + + + Auto-Type + + + + Properties + + + + History + + + + Entry history + + + + Add entry + + + + Edit entry + + + + Error + + + + Different passwords supplied. + + + + New attribute + + + + Select file + + + + Unable to open file + + + + Save attachment + + + + Unable to save the attachment: + + + + + Tomorrow + + + + %n week(s) + + + + %n month(s) + + + + 1 year + + + + + EditEntryWidgetAdvanced + + Additional attributes + + + + Add + + + + Edit + + + + Remove + + + + Attachments + + + + Save + + + + + EditEntryWidgetAutoType + + Enable Auto-Type for this entry + + + + Inherit default Auto-Type sequence from the group + + + + Use custom Auto-Type sequence: + + + + + + + + + - + + + + Window title: + + + + Use default sequence + + + + Set custom sequence: + + + + + EditEntryWidgetHistory + + Show + + + + Restore + + + + Delete + + + + Delete all + + + + + EditEntryWidgetMain + + Title: + + + + Username: + + + + Password: + + + + Repeat: + + + + Gen. + + + + URL: + + + + Expires + + + + Presets + + + + Notes: + + + + + EditGroupWidget + + Group + + + + Icon + + + + Properties + + + + Add group + + + + Edit group + + + + Enable + + + + Disable + + + + Inherit from parent group (%1) + + + + + EditGroupWidgetMain + + Name + + + + Notes + + + + Expires + + + + Search + + + + Auto-type + + + + + EditWidgetIcons + + Use default icon + + + + Use custom icon + + + + Add custom icon + + + + Delete custom icon + + + + Images + + + + All files + + + + Select Image + + + + Can't delete icon! + + + + Can't delete icon. Still used by %n item(s). + + + + + EditWidgetProperties + + Created: + + + + Modified: + + + + Accessed: + + + + Uuid: + + + + + EntryAttributesModel + + Name + + + + + EntryHistoryModel + + Last modified + + + + Title + + + + Username + + + + URL + + + + + EntryModel + + Group + + + + Title + + + + Username + + + + URL + + + + + Group + + Recycle Bin + + + + + KeePass1OpenWidget + + Import KeePass1 database + + + + Error + + + + Unable to open the database. + + + + + KeePass1Reader + + Unable to read keyfile. + + + + Not a KeePass database. + + + + Unsupported encryption algorithm. + + + + Unsupported KeePass database version. + + + + Root + + + + + KeePass2Reader + + Not a KeePass database. + + + + Unsupported KeePass database version. + + + + Wrong key or database file is corrupt. + + + + + MainWindow + + Database + Datenbank + + + Recent databases + + + + Help + Hilfe + + + Entries + Einträge + + + Copy attribute to clipboard + + + + Groups + Gruppen + + + Extras + Extras + + + View + Ansicht + + + Quit + + + + About + + + + Open database + + + + Save database + + + + Close database + + + + New database + + + + Add new entry + + + + View/Edit entry + + + + Delete entry + + + + Add new group + + + + Edit group + + + + Delete group + + + + Save database as + + + + Change master key + + + + Database settings + + + + Import KeePass 1 database + + + + Clone entry + + + + Find + + + + Username + + + + Copy username to clipboard + + + + Password + + + + Copy password to clipboard + + + + Settings + + + + Perform Auto-Type + + + + Open URL + + + + Lock databases + + + + Title + + + + URL + + + + Notes + + + + Show toolbar + + + + read-only + + + + + PasswordGeneratorWidget + + Password: + + + + Length: + + + + Character Types + + + + Upper Case Letters + + + + Lower Case Letters + + + + Numbers + + + + Special Characters + + + + Exclude look-alike characters + + + + Ensure that the password contains characters from every group + + + + Accept + + + + + QCommandLineParser + + Displays version information. + + + + Displays this help. + + + + Unknown option '%1'. + + + + Unknown options: %1. + + + + Missing value after '%1'. + + + + Unexpected value after '%1'. + + + + [options] + + + + Usage: %1 + + + + Options: + + + + Arguments: + + + + + QSaveFile + + Existing file %1 is not writable + + + + Writing canceled by application + + + + Partial write. Partition full? + + + + + QtIOCompressor + + Internal zlib error when compressing: + + + + Error writing to underlying device: + + + + Error opening underlying device: + + + + Error reading data from underlying device: + + + + Internal zlib error when decompressing: + + + + + QtIOCompressor::open + + The gzip format not supported in this version of zlib. + + + + Internal zlib error: + + + + + SearchWidget + + Find: + + + + Case sensitive + + + + Current group + + + + Root group + + + + + SettingsWidget + + Application Settings + + + + General + + + + Security + + + + + SettingsWidgetGeneral + + Remember last databases + + + + Open previous databases on startup + + + + Mark as modified on expanded state changes + + + + Automatically save on exit + + + + Automatically save after every change + + + + Minimize when copying to clipboard + + + + Use group icon on entry creation + + + + Global Auto-Type shortcut + + + + Use entry title to match windows for global auto-type + + + + + SettingsWidgetSecurity + + Clear clipboard after + + + + sec + + + + Lock databases after inactivity of + + + + Show passwords in cleartext by default + + + + Always ask before performing auto-type + + + + + UnlockDatabaseWidget + + Unlock database + + + + Error + + + + Wrong key. + + + + + WelcomeWidget + + Welcome! + + + + + main + + KeePassX - cross-platform password manager + + + + filename of the password database to open (*.kdbx) + + + + path to a custom config file + + + + password of the database (DANGEROUS!) + + + + key file of the database + + + + \ No newline at end of file diff --git a/share/translations/keepassx_en.ts b/share/translations/keepassx_en.ts new file mode 100644 index 000000000..eccd5575b --- /dev/null +++ b/share/translations/keepassx_en.ts @@ -0,0 +1,1189 @@ + + + + + AboutDialog + + About KeePassX + + + + KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. + + + + + AutoType + + Auto-Type - KeePassX + + + + Couldn't find an entry that matches the window title. + + + + + AutoTypeAssociationsModel + + Window + + + + Sequence + + + + Default sequence + + + + + AutoTypeSelectDialog + + Auto-Type - KeePassX + + + + Select entry to Auto-Type: + + + + + ChangeMasterKeyWidget + + Password + + + + Enter password: + + + + Repeat password: + + + + Key file + + + + Browse + + + + Create + + + + Key files + + + + All files + + + + Create Key File... + + + + Error + + + + Unable to create Key File : + + + + Select a key file + + + + Question + + + + Do you really want to use an empty string as password? + + + + Different passwords supplied. + + + + + DatabaseOpenWidget + + Enter master key + + + + Key File: + + + + Password: + + + + Browse + + + + Error + + + + Unable to open the database. + + + + Can't open key file + + + + All files + + + + Key files + + + + Select key file + + + + + DatabaseSettingsWidget + + Database name: + + + + Database description: + + + + Transform rounds: + + + + Default username: + + + + Use recycle bin: + + + + MiB + + + + Benchmark + + + + Max. history items: + + + + Max. history size: + + + + + DatabaseTabWidget + + Root + + + + KeePass 2 Database + + + + All files + + + + Open database + + + + Warning + + + + File not found! + + + + Open KeePass 1 database + + + + KeePass 1 database + + + + All files (*) + + + + Close? + + + + "%1" is in edit mode. +Close anyway? + + + + Save changes? + + + + "%1" was modified. +Save changes? + + + + Error + + + + Writing the database failed. + + + + Save database as + + + + New database + + + + locked + + + + + DatabaseWidget + + Change master key + + + + Delete entry? + + + + Do you really want to delete the entry "%1" for good? + + + + Delete entries? + + + + Do you really want to delete %1 entries for good? + + + + Move entries to recycle bin? + + + + Do you really want to move %n entry(s) to the recycle bin? + + + + + + + Delete group? + + + + Do you really want to delete the group "%1" for good? + + + + Current group + + + + + EditEntryWidget + + Entry + + + + Advanced + + + + Icon + + + + Auto-Type + + + + Properties + + + + History + + + + Entry history + + + + Add entry + + + + Edit entry + + + + Error + + + + Different passwords supplied. + + + + New attribute + + + + Select file + + + + Unable to open file + + + + Save attachment + + + + Unable to save the attachment: + + + + + Tomorrow + + + + %n week(s) + + + + + + + %n month(s) + + + + + + + 1 year + + + + + EditEntryWidgetAdvanced + + Additional attributes + + + + Add + + + + Edit + + + + Remove + + + + Attachments + + + + Save + + + + + EditEntryWidgetAutoType + + Enable Auto-Type for this entry + + + + Inherit default Auto-Type sequence from the group + + + + Use custom Auto-Type sequence: + + + + + + + + + - + + + + Window title: + + + + Use default sequence + + + + Set custom sequence: + + + + + EditEntryWidgetHistory + + Show + + + + Restore + + + + Delete + + + + Delete all + + + + + EditEntryWidgetMain + + Title: + + + + Username: + + + + Password: + + + + Repeat: + + + + Gen. + + + + URL: + + + + Expires + + + + Presets + + + + Notes: + + + + + EditGroupWidget + + Group + + + + Icon + + + + Properties + + + + Add group + + + + Edit group + + + + Enable + + + + Disable + + + + Inherit from parent group (%1) + + + + + EditGroupWidgetMain + + Name + + + + Notes + + + + Expires + + + + Search + + + + Auto-type + + + + + EditWidgetIcons + + Use default icon + + + + Use custom icon + + + + Add custom icon + + + + Delete custom icon + + + + Images + + + + All files + + + + Select Image + + + + Can't delete icon! + + + + Can't delete icon. Still used by %n item(s). + + + + + + + + EditWidgetProperties + + Created: + + + + Modified: + + + + Accessed: + + + + Uuid: + + + + + EntryAttributesModel + + Name + + + + + EntryHistoryModel + + Last modified + + + + Title + + + + Username + + + + URL + + + + + EntryModel + + Group + + + + Title + + + + Username + + + + URL + + + + + Group + + Recycle Bin + + + + + KeePass1OpenWidget + + Import KeePass1 database + + + + Error + + + + Unable to open the database. + + + + + KeePass1Reader + + Unable to read keyfile. + + + + Not a KeePass database. + + + + Unsupported encryption algorithm. + + + + Unsupported KeePass database version. + + + + Root + + + + + KeePass2Reader + + Not a KeePass database. + + + + Unsupported KeePass database version. + + + + Wrong key or database file is corrupt. + + + + + MainWindow + + Database + + + + Recent databases + + + + Help + + + + Entries + + + + Copy attribute to clipboard + + + + Groups + + + + Extras + + + + View + + + + Quit + + + + About + + + + Open database + + + + Save database + + + + Close database + + + + New database + + + + Add new entry + + + + View/Edit entry + + + + Delete entry + + + + Add new group + + + + Edit group + + + + Delete group + + + + Save database as + + + + Change master key + + + + Database settings + + + + Import KeePass 1 database + + + + Clone entry + + + + Find + + + + Username + + + + Copy username to clipboard + + + + Password + + + + Copy password to clipboard + + + + Settings + + + + Perform Auto-Type + + + + Open URL + + + + Lock databases + + + + Title + + + + URL + + + + Notes + + + + Show toolbar + + + + read-only + + + + + PasswordGeneratorWidget + + Password: + + + + Length: + + + + Character Types + + + + Upper Case Letters + + + + Lower Case Letters + + + + Numbers + + + + Special Characters + + + + Exclude look-alike characters + + + + Ensure that the password contains characters from every group + + + + Accept + + + + + QCommandLineParser + + Displays version information. + + + + Displays this help. + + + + Unknown option '%1'. + + + + Unknown options: %1. + + + + Missing value after '%1'. + + + + Unexpected value after '%1'. + + + + [options] + + + + Usage: %1 + + + + Options: + + + + Arguments: + + + + + QSaveFile + + Existing file %1 is not writable + + + + Writing canceled by application + + + + Partial write. Partition full? + + + + + QtIOCompressor + + Internal zlib error when compressing: + + + + Error writing to underlying device: + + + + Error opening underlying device: + + + + Error reading data from underlying device: + + + + Internal zlib error when decompressing: + + + + + QtIOCompressor::open + + The gzip format not supported in this version of zlib. + + + + Internal zlib error: + + + + + SearchWidget + + Find: + + + + Case sensitive + + + + Current group + + + + Root group + + + + + SettingsWidget + + Application Settings + + + + General + + + + Security + + + + + SettingsWidgetGeneral + + Remember last databases + + + + Open previous databases on startup + + + + Mark as modified on expanded state changes + + + + Automatically save on exit + + + + Automatically save after every change + + + + Minimize when copying to clipboard + + + + Use group icon on entry creation + + + + Global Auto-Type shortcut + + + + Use entry title to match windows for global auto-type + + + + + SettingsWidgetSecurity + + Clear clipboard after + + + + sec + + + + Lock databases after inactivity of + + + + Show passwords in cleartext by default + + + + Always ask before performing auto-type + + + + + UnlockDatabaseWidget + + Unlock database + + + + Error + + + + Wrong key. + + + + + WelcomeWidget + + Welcome! + + + + + main + + KeePassX - cross-platform password manager + + + + filename of the password database to open (*.kdbx) + + + + path to a custom config file + + + + password of the database (DANGEROUS!) + + + + key file of the database + + + + diff --git a/share/translations/keepassx_en_plurals.ts b/share/translations/keepassx_en_plurals.ts new file mode 100644 index 000000000..006f6f6e4 --- /dev/null +++ b/share/translations/keepassx_en_plurals.ts @@ -0,0 +1,41 @@ + + + + + DatabaseWidget + + Do you really want to move %n entry(s) to the recycle bin? + + Do you really want to move %n entry to the recycle bin? + Do you really want to move %n entries to the recycle bin? + + + + + EditEntryWidget + + %n week(s) + + %n week + %n weeks + + + + %n month(s) + + %n month + %n months + + + + + EditWidgetIcons + + Can't delete icon. Still used by %n item(s). + + Can't delete icon. Still used by %n item. + Can't delete icon. Still used by %n items. + + + + diff --git a/share/translations/update.sh b/share/translations/update.sh new file mode 100755 index 000000000..6828dc820 --- /dev/null +++ b/share/translations/update.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +BASEDIR=$(dirname $0) + +cd $BASEDIR/../.. + +lupdate -no-ui-lines -disable-heuristic similartext -locations none -no-obsolete src -ts share/translations/keepassx_en.ts +lupdate -no-ui-lines -disable-heuristic similartext -locations none -pluralonly src -ts share/translations/keepassx_en_plurals.ts diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3de2422a4..7ffc168b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,6 +50,7 @@ set(keepassx_SOURCES core/TimeInfo.cpp core/ToDbExporter.cpp core/Tools.cpp + core/Translator.cpp core/Uuid.cpp core/qcommandlineoption.cpp core/qcommandlineparser.cpp diff --git a/src/config-keepassx.h.cmake b/src/config-keepassx.h.cmake index 9a3f49516..805700a9f 100644 --- a/src/config-keepassx.h.cmake +++ b/src/config-keepassx.h.cmake @@ -6,6 +6,7 @@ #define KEEPASSX_VERSION "${KEEPASSX_VERSION}" #define KEEPASSX_SOURCE_DIR "${CMAKE_SOURCE_DIR}" +#define KEEPASSX_BINARY_DIR "${CMAKE_BINARY_DIR}" #define KEEPASSX_PLUGIN_DIR "${PLUGIN_INSTALL_DIR}" diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 76f0e6f8e..8a455e27a 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -102,6 +102,7 @@ void Config::init(const QString& fileName) m_defaults.insert("security/lockdatabaseidlesec", 10); m_defaults.insert("security/passwordscleartext", false); m_defaults.insert("security/autotypeask", true); + m_defaults.insert("GUI/Language", "system"); } Config* Config::instance() diff --git a/src/core/Translator.cpp b/src/core/Translator.cpp new file mode 100644 index 000000000..bc4d2b629 --- /dev/null +++ b/src/core/Translator.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2014 Felix Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "Translator.h" + +#include +#include +#include +#include +#include +#include + +#include "config-keepassx.h" +#include "core/Config.h" +#include "core/FilePath.h" + +void Translator::installTranslator() +{ + QString language = config()->get("GUI/Language").toString(); + if (language == "system" || language.isEmpty()) { + language = QLocale::system().name(); + } + + if (!installTranslator(language)) { + // English fallback still needs translations for plurals + if (!installTranslator("en_plurals")) { + qWarning("Couldn't load translations."); + } + } + + installQtTranslator(language); + + availableLanguages(); +} + +QList > Translator::availableLanguages() +{ + QStringList paths; +#ifdef QT_DEBUG + paths.append(QString("%1/share/translations").arg(KEEPASSX_BINARY_DIR)); +#endif + paths.append(filePath()->dataPath("translations")); + + QList > languages; + languages.append(QPair("system", "System default")); + + QRegExp regExp("keepassx_([a-zA-Z_]+)\\.qm", Qt::CaseInsensitive, QRegExp::RegExp2); + Q_FOREACH (const QString& path, paths) { + Q_FOREACH (const QString& filename, QDir(path).entryList()) { + if (regExp.exactMatch(filename)) { + QString langcode = regExp.cap(1); + if (langcode == "en_plurals") { + langcode = "en"; + } + + languages.append(QPair(langcode, + QLocale::languageToString(QLocale(langcode).language()))); + } + } + } + + return languages; +} + +bool Translator::installTranslator(const QString& language) +{ + QStringList paths; +#ifdef QT_DEBUG + paths.append(QString("%1/share/translations").arg(KEEPASSX_BINARY_DIR)); +#endif + paths.append(filePath()->dataPath("translations")); + + Q_FOREACH (const QString& path, paths) { + if (installTranslator(language, path)) { + return true; + } + } + + return false; +} + +bool Translator::installTranslator(const QString& language, const QString& path) +{ + QTranslator* translator = new QTranslator(qApp); + if (translator->load(QString("keepassx_").append(language), path)) { + QCoreApplication::installTranslator(translator); + return true; + } + else { + delete translator; + return false; + } +} + +bool Translator::installQtTranslator(const QString& language) +{ + QTranslator* qtTranslator = new QTranslator(qApp); + if (qtTranslator->load(QString("%1/qt_%2").arg(QLibraryInfo::location(QLibraryInfo::TranslationsPath), language))) { + QCoreApplication::installTranslator(qtTranslator); + return true; + } + else { + delete qtTranslator; + return false; + } +} diff --git a/src/core/Translator.h b/src/core/Translator.h new file mode 100644 index 000000000..4bc4fca3e --- /dev/null +++ b/src/core/Translator.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 Felix Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_TRANSLATOR_H +#define KEEPASSX_TRANSLATOR_H + +#include +#include + +class Translator +{ +public: + static void installTranslator(); + static QList > availableLanguages(); + +private: + static bool installTranslator(const QString& language); + static bool installTranslator(const QString& language, const QString& path); + static bool installQtTranslator(const QString& language); +}; + +#endif // KEEPASSX_TRANSLATOR_H diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 2aea2636e..374e52d97 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -21,6 +21,7 @@ #include "autotype/AutoType.h" #include "core/Config.h" +#include "core/Translator.h" SettingsWidget::SettingsWidget(QWidget* parent) : EditWidget(parent) @@ -69,6 +70,15 @@ void SettingsWidget::loadSettings() m_generalUi->useGroupIconOnEntryCreationCheckBox->setChecked(config()->get("UseGroupIconOnEntryCreation").toBool()); m_generalUi->autoTypeEntryTitleMatchCheckBox->setChecked(config()->get("AutoTypeEntryTitleMatch").toBool()); + QList > languages = Translator::availableLanguages(); + for (int i = 0; i < languages.size(); i++) { + m_generalUi->languageComboBox->addItem(languages[i].second, languages[i].first); + } + int defaultIndex = m_generalUi->languageComboBox->findData(config()->get("GUI/Language")); + if (defaultIndex > 0) { + m_generalUi->languageComboBox->setCurrentIndex(defaultIndex); + } + if (autoType()->isAvailable()) { m_globalAutoTypeKey = static_cast(config()->get("GlobalAutoTypeKey").toInt()); m_globalAutoTypeModifiers = static_cast(config()->get("GlobalAutoTypeModifiers").toInt()); @@ -105,6 +115,8 @@ void SettingsWidget::saveSettings() m_generalUi->useGroupIconOnEntryCreationCheckBox->isChecked()); config()->set("AutoTypeEntryTitleMatch", m_generalUi->autoTypeEntryTitleMatchCheckBox->isChecked()); + int currentLangIndex = m_generalUi->languageComboBox->currentIndex(); + config()->set("GUI/Language", m_generalUi->languageComboBox->itemData(currentLangIndex).toString()); if (autoType()->isAvailable()) { config()->set("GlobalAutoTypeKey", m_generalUi->autoTypeShortcutWidget->key()); config()->set("GlobalAutoTypeModifiers", diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/SettingsWidgetGeneral.ui index 3d89c68e8..f3dc079e2 100644 --- a/src/gui/SettingsWidgetGeneral.ui +++ b/src/gui/SettingsWidgetGeneral.ui @@ -7,7 +7,7 @@ 0 0 456 - 230 + 288 @@ -86,6 +86,16 @@ + + + + Language + + + + + + diff --git a/src/main.cpp b/src/main.cpp index abe7ceb4e..d5e64b92c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ #include "core/Config.h" #include "core/qcommandlineparser.h" #include "core/Tools.h" +#include "core/Translator.h" #include "crypto/Crypto.h" #include "gui/Application.h" #include "gui/MainWindow.h" @@ -66,6 +67,8 @@ int main(int argc, char** argv) Config::createConfigFromFile(parser.value(configOption)); } + Translator::installTranslator(); + #ifdef Q_OS_MAC // Don't show menu icons on OSX QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); From 5a31e055cfd98d476721055b9d03a36be0b929da Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sun, 18 May 2014 12:08:31 +0200 Subject: [PATCH 37/39] Show the window title when no entry matches for auto-type. Closes #188 --- src/autotype/AutoType.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 92aae5cd7..aac0c0cf8 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -190,8 +190,10 @@ void AutoType::performGlobalAutoType(const QList& dbList) if (entryList.isEmpty()) { m_inAutoType = false; - MessageBox::information(Q_NULLPTR, tr("Auto-Type - KeePassX"), - tr("Couldn't find an entry that matches the window title.")); + QString message = tr("Couldn't find an entry that matches the window title:"); + message.append("\n\n"); + message.append(windowTitle); + MessageBox::information(Q_NULLPTR, tr("Auto-Type - KeePassX"), message); } else if ((entryList.size() == 1) && !config()->get("security/autotypeask").toBool()) { m_inAutoType = false; From 916ab99d6249f17271338ae711cb226ba1d0f4b5 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Mon, 26 May 2014 18:24:43 +0200 Subject: [PATCH 38/39] Skip TestQSaveFile::transactionalWriteErrorRenaming as user root. You can't deny root access to a file. Closes #201 --- tests/TestQSaveFile.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/TestQSaveFile.cpp b/tests/TestQSaveFile.cpp index 6be714efd..443db5299 100644 --- a/tests/TestQSaveFile.cpp +++ b/tests/TestQSaveFile.cpp @@ -156,6 +156,9 @@ void TestQSaveFile::transactionalWriteCanceled() void TestQSaveFile::transactionalWriteErrorRenaming() { #ifndef Q_OS_WIN + if (::geteuid() == 0) { + QSKIP("not valid running this test as root", SkipAll); + } const QString dir = tmpDir(); QVERIFY(!dir.isEmpty()); const QString targetFile = dir + QString::fromLatin1("/outfile"); From 7137990a214ba49c23d6ba038a95c2ced9b826bb Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Mon, 26 May 2014 18:41:48 +0200 Subject: [PATCH 39/39] Clear clipboard only if copied text is still present. Closes #198 --- src/gui/Clipboard.cpp | 11 +++++++++-- src/gui/Clipboard.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gui/Clipboard.cpp b/src/gui/Clipboard.cpp index eb77d2b3d..7d8f71faf 100644 --- a/src/gui/Clipboard.cpp +++ b/src/gui/Clipboard.cpp @@ -51,6 +51,7 @@ void Clipboard::setText(const QString& text) if (config()->get("security/clearclipboard").toBool()) { int timeout = config()->get("security/clearclipboardtimeout").toInt(); if (timeout > 0) { + m_lastCopied = text; m_timer->start(timeout * 1000); } } @@ -65,8 +66,12 @@ void Clipboard::clearClipboard() return; } - clipboard->clear(QClipboard::Clipboard); - if (clipboard->supportsSelection()) { + if (clipboard->text(QClipboard::Clipboard) == m_lastCopied) { + clipboard->clear(QClipboard::Clipboard); + } + + if (clipboard->supportsSelection() + && (clipboard->text(QClipboard::Selection) == m_lastCopied)) { clipboard->clear(QClipboard::Selection); } @@ -74,6 +79,8 @@ void Clipboard::clearClipboard() QDBusMessage message = QDBusMessage::createMethodCall("org.kde.klipper", "/klipper", "", "clearClipboardHistory"); QDBusConnection::sessionBus().send(message); #endif + + m_lastCopied.clear(); } void Clipboard::cleanup() diff --git a/src/gui/Clipboard.h b/src/gui/Clipboard.h index bc2a19d31..8b6ea69f5 100644 --- a/src/gui/Clipboard.h +++ b/src/gui/Clipboard.h @@ -43,6 +43,7 @@ private: static Clipboard* m_instance; QTimer* m_timer; + QString m_lastCopied; }; inline Clipboard* clipboard() {