From: Jan Rękorajski Date: Fri, 7 Nov 2014 20:37:31 +0000 (+0100) Subject: - added update to current git master X-Git-Tag: auto/th/KeePassX-2.0-0.alpha6.1 X-Git-Url: http://git.pld-linux.org/gitweb.cgi?p=packages%2FKeePassX.git;a=commitdiff_plain;h=dddba08bce468bca70f2850ca391b20f67092472 - added update to current git master - updated BRs --- diff --git a/KeePassX.spec b/KeePassX.spec index 82645bc..6d7249b 100644 --- a/KeePassX.spec +++ b/KeePassX.spec @@ -12,15 +12,21 @@ Group: X11/Applications #Source0: http://downloads.sourceforge.net/keepassx/keepassx-%{version}.tar.gz Source0: http://www.keepassx.org/dev/attachments/download/69/keepassx-%{version}-%{pre}.tar.gz # Source0-md5: 7c1c3a42aff63abd8db3bc8df6c963f6 +Patch0: git.patch URL: http://keepassx.sourceforge.net/ -BuildRequires: ImageMagick -BuildRequires: Qt3Support-devel >= 4.0 -BuildRequires: QtGui-devel >= 4.0 -BuildRequires: QtXml-devel >= 4.0 -BuildRequires: qt4-build >= 4.3.3-3 -BuildRequires: qt4-qmake >= 4.3.3-3 +BuildRequires: QtCore-devel >= 4.6.0 +BuildRequires: QtDBus-devel >= 4.6.0 +BuildRequires: QtGui-devel >= 4.6.0 +BuildRequires: QtXml-devel >= 4.6.0 +BuildRequires: libgcrypt-devel >= 1.6 +BuildRequires: qt4-build >= 4.6.0 +BuildRequires: qt4-linguist >= 4.6.0 +BuildRequires: qt4-qmake >= 4.6.0 BuildRequires: rpmbuild(macros) >= 1.230 +BuildRequires: xorg-lib-libX11-devel +BuildRequires: xorg-lib-libXext-devel BuildRequires: xorg-lib-libXtst-devel +BuildRequires: zlib-devel Requires: desktop-file-utils Requires: hicolor-icon-theme Requires: shared-mime-info @@ -47,6 +53,7 @@ szyfrowania jakie są do tej pory znane (AES i TwoFish). %prep %setup -q -n keepassx-%{version}-%{pre} +%patch0 -p1 %build install -d build @@ -81,6 +88,12 @@ rm -rf $RPM_BUILD_ROOT %{_desktopdir}/keepassx.desktop %dir %{_datadir}/keepassx %{_datadir}/keepassx/icons +%dir %{_datadir}/keepassx/translations +%lang(de) %{_datadir}/keepassx/translations/keepassx_de.qm +%{_datadir}/keepassx/translations/keepassx_en_plurals.qm +%lang(it) %{_datadir}/keepassx/translations/keepassx_it.qm +%lang(nl) %{_datadir}/keepassx/translations/keepassx_nl_NL.qm +%lang(sv) %{_datadir}/keepassx/translations/keepassx_sv.qm %dir %{_libdir}/keepassx %attr(755,root,root) %{_libdir}/keepassx/libkeepassx-autotype-x11.so %{_iconsdir}/hicolor/*x*/apps/keepassx.png diff --git a/git.patch b/git.patch new file mode 100644 index 0000000..dfdace3 --- /dev/null +++ b/git.patch @@ -0,0 +1,9977 @@ +diff --git a/.travis.yml b/.travis.yml +new file mode 100644 +index 0000000..15af85b +--- /dev/null ++++ b/.travis.yml +@@ -0,0 +1,19 @@ ++os: ++ - linux ++ - osx ++compiler: ++ - gcc ++ - clang ++language: cpp ++install: ++ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq update; fi ++ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake libqt4-dev libgcrypt11-dev zlib1g-dev libxtst-dev; fi ++ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi ++ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install cmake qt libgcrypt; fi ++before_script: mkdir build && pushd build ++script: ++ - cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_GUI_TESTS=ON .. ++ - make ++ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui"; fi ++ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui"; fi ++ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then make test; fi +diff --git a/.tx/config b/.tx/config +new file mode 100644 +index 0000000..015acf4 +--- /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/CHANGELOG b/CHANGELOG +index 2ffae87..b61597b 100644 +--- a/CHANGELOG ++++ b/CHANGELOG +@@ -1,4 +1,4 @@ +-2.0 Alpha 6 (2014-04-06) ++2.0 Alpha 6 (2014-04-12) + ========================= + + - Add option to lock databases after user inactivity [#62] +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 35642eb..3532c46 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -32,7 +32,7 @@ include(CheckCXXSourceCompiles) + option(WITH_TESTS "Enable building of unit tests" ON) + option(WITH_GUI_TESTS "Enable building of GUI tests" OFF) + option(WITH_LTO "Enable Link Time Optimization (LTO)" OFF) +-option(WITH_CXX11 "Build with the C++ 11 standard" OFF) ++option(WITH_CXX11 "Build with the C++ 11 standard" ON) + + set(KEEPASSX_VERSION "2.0 alpha 6") + set(KEEPASSX_VERSION_NUM "1.9.85") +@@ -165,6 +165,9 @@ endif() + + find_package(Qt4 4.6.0 REQUIRED ${QT_REQUIRED_MODULES}) + include(${QT_USE_FILE}) ++# Debian sets the the build type to None for package builds. ++# Make sure we don't enable asserts there. ++set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG) + + find_package(Gcrypt REQUIRED) + if(NOT (${GCRYPT_VERSION_STRING} VERSION_LESS "1.6.0")) +diff --git a/INSTALL b/INSTALL +index 028ccff..bde991b 100644 +--- a/INSTALL ++++ b/INSTALL +@@ -2,7 +2,7 @@ Building: + ========= + mkdir build + cd build +-cmake .. [CMAKE PARAMETERS] ++cmake [CMAKE PARAMETERS] .. + make [-jX] + + Common cmake parameters: +diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt +index 7069c6c..0e2b7fa 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 0000000..b1aa878 +--- /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 0000000..5433c3c +--- /dev/null ++++ b/share/translations/keepassx_de.ts +@@ -0,0 +1,1177 @@ ++ ++ ++ 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. ++ KeePassX ist unter der GNU General Public License (GPL) version 2 (version 3) veröffentlicht. ++ ++ ++ ++ AutoType ++ ++ Auto-Type - KeePassX ++ Auto-Type - KeePassX ++ ++ ++ Couldn't find an entry that matches the window title. ++ Konnte dem Fenstertitel keinen passenden Eintrag zuordnen. ++ ++ ++ ++ AutoTypeAssociationsModel ++ ++ Window ++ Fenster ++ ++ ++ Sequence ++ Reihenfolge ++ ++ ++ Default sequence ++ Standardreihenfolge ++ ++ ++ ++ AutoTypeSelectDialog ++ ++ Auto-Type - KeePassX ++ Auto-Type - KeePassX ++ ++ ++ Select entry to Auto-Type: ++ Wählen Sie einen Eintrag für Auto-Type: ++ ++ ++ ++ ChangeMasterKeyWidget ++ ++ Password ++ Passwort ++ ++ ++ Enter password: ++ Passwort eingeben: ++ ++ ++ Repeat password: ++ Passwort wiederholen: ++ ++ ++ Key file ++ Schlüsseldatei ++ ++ ++ Browse ++ Durchsuchen ++ ++ ++ Create ++ Erstellen ++ ++ ++ Key files ++ Schlüsseldateien ++ ++ ++ All files ++ Alle Dateien ++ ++ ++ Create Key File... ++ Erzeuge eine Schlüsseldatei... ++ ++ ++ Error ++ Fehler ++ ++ ++ Unable to create Key File : ++ Erzeugen der Schlüsseldatei nicht möglich: ++ ++ ++ Select a key file ++ Schlüsseldatei auswählen ++ ++ ++ Question ++ Frage ++ ++ ++ Do you really want to use an empty string as password? ++ Wollen Sie wirklich eine leere Zeichenkette als Passwort verwenden? ++ ++ ++ Different passwords supplied. ++ Unterschiedliche Passwörter eingegeben. ++ ++ ++ ++ DatabaseOpenWidget ++ ++ Enter master key ++ Hauptschlüssel eingeben ++ ++ ++ Key File: ++ Schlüsseldatei: ++ ++ ++ Password: ++ Passwort: ++ ++ ++ Browse ++ Durchsuchen ++ ++ ++ Error ++ Fehler ++ ++ ++ Unable to open the database. ++ Öffnen der Datenbank nicht möglich. ++ ++ ++ Can't open key file ++ Schlüsseldatein kann nicht geöffnet werden ++ ++ ++ All files ++ Alle Dateien ++ ++ ++ Key files ++ Schlüsseldateien ++ ++ ++ Select key file ++ Schlüsseldatei auswählen ++ ++ ++ ++ DatabaseSettingsWidget ++ ++ Database name: ++ Datenbankname: ++ ++ ++ Database description: ++ Datenbankbeschreibung: ++ ++ ++ Transform rounds: ++ Verschlüsselungsdurchläufe: ++ ++ ++ Default username: ++ Standardbenutzername: ++ ++ ++ Use recycle bin: ++ Verwende Papierkorb: ++ ++ ++ MiB ++ MiB ++ ++ ++ Benchmark ++ Benchmark ++ ++ ++ Max. history items: ++ Max Einträge im Verlauf: ++ ++ ++ Max. history size: ++ Max. Verlaufsgröße: ++ ++ ++ ++ DatabaseTabWidget ++ ++ Root ++ Root ++ ++ ++ KeePass 2 Database ++ KeePass 2 Datenbank ++ ++ ++ All files ++ Alle Dateien ++ ++ ++ Open database ++ Datenbank öffnen ++ ++ ++ Warning ++ Warnung ++ ++ ++ File not found! ++ Datei nicht gefunden! ++ ++ ++ Open KeePass 1 database ++ KeePass 1 Datenbank öffnen ++ ++ ++ KeePass 1 database ++ KeePass 1 Datenbank ++ ++ ++ All files (*) ++ Alle Dateien (*) ++ ++ ++ Close? ++ Schließen? ++ ++ ++ "%1" is in edit mode. ++Close anyway? ++ "%1" wird bearbeitet. ++Trotzdem schließen? ++ ++ ++ Save changes? ++ Änderungen speichern? ++ ++ ++ "%1" was modified. ++Save changes? ++ "%1" wurde geändert. ++Änderungen speichern? ++ ++ ++ Error ++ Fehler ++ ++ ++ Writing the database failed. ++ Schreiben der Datenbank fehlgeschlagen. ++ ++ ++ Save database as ++ Datenbank speichern unter ++ ++ ++ New database ++ Neue Datenbank ++ ++ ++ locked ++ gesperrt ++ ++ ++ ++ DatabaseWidget ++ ++ Change master key ++ Hauptschlüssel ändern ++ ++ ++ Delete entry? ++ Eintrag löschen? ++ ++ ++ Do you really want to delete the entry "%1" for good? ++ Wollen Sie den Eintrag "%1" wirklich löschen? ++ ++ ++ Delete entries? ++ Einträge löschen? ++ ++ ++ Do you really want to delete %1 entries for good? ++ Wollen Sie die Einträge "%1" wirklich löschen? ++ ++ ++ Move entries to recycle bin? ++ Einträge in den Papierkorb verschieben? ++ ++ ++ Do you really want to move %n entry(s) to the recycle bin? ++ Wollen Sie wirklich %n Eintrag in den Papierkorb verschieben?Wollen Sie wirklich %n Einträge in den Papierkorb verschieben? ++ ++ ++ Delete group? ++ Gruppe löschen? ++ ++ ++ Do you really want to delete the group "%1" for good? ++ Wollen Sie die Gruppe "%1" wirklich löschen? ++ ++ ++ Current group ++ Aktuelle Gruppe ++ ++ ++ ++ EditEntryWidget ++ ++ Entry ++ Eintrag ++ ++ ++ Advanced ++ Fortgeschritten ++ ++ ++ Icon ++ Symbol ++ ++ ++ Auto-Type ++ Auto-Type ++ ++ ++ Properties ++ Eigenschaften ++ ++ ++ History ++ Verlauf ++ ++ ++ Entry history ++ Eintragsverlauf ++ ++ ++ Add entry ++ Eintrag hinzufügen ++ ++ ++ Edit entry ++ Eintrag bearbeiten ++ ++ ++ Error ++ Fehler ++ ++ ++ Different passwords supplied. ++ Unterschiedliche Passwörter eingegeben. ++ ++ ++ New attribute ++ Neue Eigenschaft ++ ++ ++ Select file ++ Datei wählen ++ ++ ++ Unable to open file ++ Öffnen der Datei nicht möglich ++ ++ ++ Save attachment ++ Anhang speichern ++ ++ ++ Unable to save the attachment: ++ ++ Speichern des Anhangs nicht möglich: ++ ++ ++ Tomorrow ++ Morgen ++ ++ ++ %n week(s) ++ %n Woche%n Wochen ++ ++ ++ %n month(s) ++ %n Monat%n Monaten ++ ++ ++ 1 year ++ 1 Jahr ++ ++ ++ ++ EditEntryWidgetAdvanced ++ ++ Additional attributes ++ Zusätzliche Eigenschaften ++ ++ ++ Add ++ Hinzufügen ++ ++ ++ Edit ++ Bearbeiten ++ ++ ++ Remove ++ Entfernen ++ ++ ++ Attachments ++ Anhänge ++ ++ ++ Save ++ Speichern ++ ++ ++ ++ EditEntryWidgetAutoType ++ ++ Enable Auto-Type for this entry ++ Auto-Type für diesen Eintrag aktivieren ++ ++ ++ Inherit default Auto-Type sequence from the group ++ Standard-Auto-Type-Sequenz von der Gruppe erben ++ ++ ++ Use custom Auto-Type sequence: ++ Benutzerdefinierte Auto-Type-Sequenz benutzen: ++ ++ ++ + ++ + ++ ++ ++ - ++ - ++ ++ ++ Window title: ++ Fenstertitel: ++ ++ ++ Use default sequence ++ Standardsequenz benutzen ++ ++ ++ Set custom sequence: ++ Benutzerdefinierte Sequenz verwenden: ++ ++ ++ ++ EditEntryWidgetHistory ++ ++ Show ++ Anzeigen ++ ++ ++ Restore ++ Wiederherstellen ++ ++ ++ Delete ++ Löschen ++ ++ ++ Delete all ++ Alle löschen ++ ++ ++ ++ EditEntryWidgetMain ++ ++ Title: ++ Titel: ++ ++ ++ Username: ++ Benutzername: ++ ++ ++ Password: ++ Passwort: ++ ++ ++ Repeat: ++ Wiederholen: ++ ++ ++ Gen. ++ Gen. ++ ++ ++ URL: ++ URL: ++ ++ ++ Expires ++ Erlischt ++ ++ ++ Presets ++ Vorgaben ++ ++ ++ Notes: ++ Notizen: ++ ++ ++ ++ EditGroupWidget ++ ++ Group ++ Gruppe ++ ++ ++ Icon ++ Symbol ++ ++ ++ Properties ++ Eigenschaften ++ ++ ++ Add group ++ Gruppe hinzufügen ++ ++ ++ Edit group ++ Gruppe bearbeiten ++ ++ ++ Enable ++ Aktivieren ++ ++ ++ Disable ++ Deaktivieren ++ ++ ++ Inherit from parent group (%1) ++ Von der übergeordneten Gruppe (%1) erben ++ ++ ++ ++ EditGroupWidgetMain ++ ++ Name ++ Name ++ ++ ++ Notes ++ Notizen ++ ++ ++ Expires ++ Erlischt ++ ++ ++ Search ++ Suche ++ ++ ++ Auto-type ++ Auto-type ++ ++ ++ ++ EditWidgetIcons ++ ++ Use default icon ++ Standardsymbol verwenden ++ ++ ++ Use custom icon ++ Benutzerdefiniertes Symbol verwenden ++ ++ ++ Add custom icon ++ Benutzerdefiniertes Symbol hinzufügen ++ ++ ++ Delete custom icon ++ Benutzerdefiniertes Symbol löschen ++ ++ ++ Images ++ Bilder ++ ++ ++ All files ++ Alle Dateien ++ ++ ++ Select Image ++ Bild auswählen ++ ++ ++ Can't delete icon! ++ Symbol kann nicht gelöscht werden! ++ ++ ++ Can't delete icon. Still used by %n item(s). ++ Symbol kann nicht gelöscht werden. Es wird von %n Eintrag verwendet.Symbol kann nicht gelöscht werden. Es wird von %n Einträgen verwendet. ++ ++ ++ ++ EditWidgetProperties ++ ++ Created: ++ Erstellt: ++ ++ ++ Modified: ++ Bearbeitet: ++ ++ ++ Accessed: ++ Zugegriffen: ++ ++ ++ Uuid: ++ Uuid: ++ ++ ++ ++ EntryAttributesModel ++ ++ Name ++ Name ++ ++ ++ ++ EntryHistoryModel ++ ++ Last modified ++ Zuletzt geändert ++ ++ ++ Title ++ Titel ++ ++ ++ Username ++ Benutzername ++ ++ ++ URL ++ URL ++ ++ ++ ++ EntryModel ++ ++ Group ++ Gruppe ++ ++ ++ Title ++ Titel ++ ++ ++ Username ++ Benutzername ++ ++ ++ URL ++ URL ++ ++ ++ ++ Group ++ ++ Recycle Bin ++ Papierkorb ++ ++ ++ ++ KeePass1OpenWidget ++ ++ Import KeePass1 database ++ KeePass 1 Datenbank importieren ++ ++ ++ Error ++ Fehler ++ ++ ++ Unable to open the database. ++ Öffnen der Datenbank nicht möglich. ++ ++ ++ ++ KeePass1Reader ++ ++ Unable to read keyfile. ++ Lesen der Schlüsseldatei nicht möglich. ++ ++ ++ Not a KeePass database. ++ Keine KeePass-Datenbank. ++ ++ ++ Unsupported encryption algorithm. ++ Nicht unterstützter Verschlüsselungsalgorithmus. ++ ++ ++ Unsupported KeePass database version. ++ Nicht unterstützte KeePass-Datenbankversion. ++ ++ ++ Root ++ Root ++ ++ ++ ++ KeePass2Reader ++ ++ Not a KeePass database. ++ Keine KeePass-Datenbank. ++ ++ ++ Unsupported KeePass database version. ++ Nicht unterstützte KeePass-Datenbankversion. ++ ++ ++ Wrong key or database file is corrupt. ++ Falscher Schlüssel oder die Datei ist beschädigt. ++ ++ ++ ++ MainWindow ++ ++ Database ++ Datenbank ++ ++ ++ Recent databases ++ Aktuelle Datenbanken ++ ++ ++ Help ++ Hilfe ++ ++ ++ Entries ++ Einträge ++ ++ ++ Copy attribute to clipboard ++ Eingenschaft in die Zwischenablage kopieren ++ ++ ++ Groups ++ Gruppen ++ ++ ++ Extras ++ Extras ++ ++ ++ View ++ Ansicht ++ ++ ++ Quit ++ Beenden ++ ++ ++ About ++ Über ++ ++ ++ Open database ++ Datenbank öffnen ++ ++ ++ Save database ++ Datenbank speichern ++ ++ ++ Close database ++ Datenbank schließen ++ ++ ++ New database ++ Neue Datenbank ++ ++ ++ Add new entry ++ Neuen Eintrag hinzufügen ++ ++ ++ View/Edit entry ++ Eintrag anzeigen/bearbeiten ++ ++ ++ Delete entry ++ Eintrag löschen ++ ++ ++ Add new group ++ Neue Gruppe hinzufügen ++ ++ ++ Edit group ++ Gruppe bearbeiten ++ ++ ++ Delete group ++ Gruppe löschen ++ ++ ++ Save database as ++ Datenbank speichern als ++ ++ ++ Change master key ++ Hauptschlüssel ändern ++ ++ ++ Database settings ++ Datenbankeinstellungen ++ ++ ++ Import KeePass 1 database ++ KeePass 1 Datenbank importieren ++ ++ ++ Clone entry ++ Eintrag klonen ++ ++ ++ Find ++ Suchen ++ ++ ++ Username ++ Benutzername ++ ++ ++ Copy username to clipboard ++ Benutzername in die Zwischenablage kopieren ++ ++ ++ Password ++ Passwort ++ ++ ++ Copy password to clipboard ++ Passwort in die Zwischenablage kopieren ++ ++ ++ Settings ++ Einstellungen ++ ++ ++ Perform Auto-Type ++ Auto-Type ausführen ++ ++ ++ Open URL ++ URL öffnen ++ ++ ++ Lock databases ++ Datenbank sperren ++ ++ ++ Title ++ Titel ++ ++ ++ URL ++ URL ++ ++ ++ Notes ++ Notizen ++ ++ ++ Show toolbar ++ Symbolleiste anzeigen ++ ++ ++ read-only ++ Nur Lesezugriff ++ ++ ++ ++ PasswordGeneratorWidget ++ ++ Password: ++ Passwort: ++ ++ ++ Length: ++ Länge: ++ ++ ++ Character Types ++ Zeichenarten ++ ++ ++ Upper Case Letters ++ Großbuchstaben ++ ++ ++ Lower Case Letters ++ Kleinbuchstaben ++ ++ ++ Numbers ++ Zahlen ++ ++ ++ Special Characters ++ Sonderzeichen ++ ++ ++ Exclude look-alike characters ++ Gleich aussehende Zeichen ausschließen ++ ++ ++ Ensure that the password contains characters from every group ++ Sicher stellen, dass das Passwort Zeichen aller Gruppen enthält ++ ++ ++ Accept ++ Akzeptieren ++ ++ ++ ++ QCommandLineParser ++ ++ Displays version information. ++ Versionsinformationen anzeigen. ++ ++ ++ Displays this help. ++ Zeigt diese Hilfe an. ++ ++ ++ Unknown option '%1'. ++ Unbekannte Option '%1'. ++ ++ ++ Unknown options: %1. ++ Unbekannte Optionen: '%1'. ++ ++ ++ Missing value after '%1'. ++ Fehlender Wert nach '%1'. ++ ++ ++ Unexpected value after '%1'. ++ Unerwarteter Wert nach '%1'. ++ ++ ++ [options] ++ [Optionen] ++ ++ ++ Usage: %1 ++ Verwendung: %1 ++ ++ ++ Options: ++ Optionen: ++ ++ ++ Arguments: ++ Argumente: ++ ++ ++ ++ QSaveFile ++ ++ Existing file %1 is not writable ++ Bestehende Datei(en) %1 ist nicht schreibbar ++ ++ ++ Writing canceled by application ++ Schreiben von der Applikation abgebrochen ++ ++ ++ Partial write. Partition full? ++ Unvollständiger Schreibvorgang. Partition voll? ++ ++ ++ ++ QtIOCompressor ++ ++ Internal zlib error when compressing: ++ Interner Fehler in zlib beim komprimieren: ++ ++ ++ Error writing to underlying device: ++ Fehler beim Schreiben auf das zugrunde liegende Gerät: ++ ++ ++ Error opening underlying device: ++ Fehler beim Öffnen des zugrunde liegenden Gerätes: ++ ++ ++ Error reading data from underlying device: ++ Fehler beim Lesen von Daten auf dem zugrunde liegenden Gerät: ++ ++ ++ Internal zlib error when decompressing: ++ Interner Fehler in zlib beim dekomprimieren: ++ ++ ++ ++ QtIOCompressor::open ++ ++ The gzip format not supported in this version of zlib. ++ Das gzip-Format wird von dieser zlib Version nicht unterstützt. ++ ++ ++ Internal zlib error: ++ Interner Fehler in zlib: ++ ++ ++ ++ SearchWidget ++ ++ Find: ++ Suchen nach: ++ ++ ++ Case sensitive ++ Groß-/Kleinschreibung unterscheiden ++ ++ ++ Current group ++ Aktuelle Gruppe ++ ++ ++ Root group ++ Root-Gruppe ++ ++ ++ ++ SettingsWidget ++ ++ Application Settings ++ Anwendungseinstellungen ++ ++ ++ General ++ Allgemein ++ ++ ++ Security ++ Sicherheit ++ ++ ++ ++ SettingsWidgetGeneral ++ ++ Remember last databases ++ Letzte Datenbank merken ++ ++ ++ Open previous databases on startup ++ Letzte Datenbank beim Starten öffnen ++ ++ ++ Mark as modified on expanded state changes ++ Als erweiterte Zustandsänderungen makieren ++ ++ ++ Automatically save on exit ++ Automatisch speichern beim Schließen ++ ++ ++ Automatically save after every change ++ Automatisch nach jeder Änderung speichern ++ ++ ++ Minimize when copying to clipboard ++ Minimieren beim Kopieren in die Zwischenablage ++ ++ ++ Use group icon on entry creation ++ Gruppensymbol für das Erstellen neuer Einträge verwenden ++ ++ ++ Global Auto-Type shortcut ++ Globale Tastenkombination für Auto-Type ++ ++ ++ Use entry title to match windows for global auto-type ++ Verwende den Eintragstitel für entsprechende Fenster für den globale Auto-Typ ++ ++ ++ ++ SettingsWidgetSecurity ++ ++ Clear clipboard after ++ Zwischenablage leeren nach ++ ++ ++ sec ++ sek ++ ++ ++ Lock databases after inactivity of ++ Datenbank sperren nach einer Inaktivität von ++ ++ ++ Show passwords in cleartext by default ++ Passwort standartmäßig in Klartext anzeigen ++ ++ ++ Always ask before performing auto-type ++ Immer vor einem Auto-type fragen ++ ++ ++ ++ UnlockDatabaseWidget ++ ++ Unlock database ++ Datenbank entsperren ++ ++ ++ Error ++ Fehler ++ ++ ++ Wrong key. ++ Falscher Schlüssel. ++ ++ ++ ++ WelcomeWidget ++ ++ Welcome! ++ Willkommen! ++ ++ ++ ++ main ++ ++ KeePassX - cross-platform password manager ++ KeePassX - plattformübergreifender Passwortmanager ++ ++ ++ filename of the password database to open (*.kdbx) ++ Dateiname für die zu öffnende Passwortdatenbank (*.kdbx) ++ ++ ++ path to a custom config file ++ Pfad zu einer benutzerdefinierten Konfigurationsdatei ++ ++ ++ password of the database (DANGEROUS!) ++ Passwort der Datenbank (GEFÄHRLICH!) ++ ++ ++ key file of the database ++ Schlüsseldatei der Datenbank ++ ++ ++ +\ 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 0000000..a7b5b10 +--- /dev/null ++++ b/share/translations/keepassx_en.ts +@@ -0,0 +1,1216 @@ ++ ++ ++ ++ ++ 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. ++ ++ ++ ++ ++ Main ++ ++ Fatal error while testing the cryptographic functions. ++ ++ ++ ++ KeePassX - Error ++ ++ ++ ++ ++ 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 ++ ++ ++ ++ Toggle window ++ ++ ++ ++ ++ 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 ++ ++ ++ ++ Language ++ ++ ++ ++ Show a system tray icon ++ ++ ++ ++ Hide window to system tray when minimized ++ ++ ++ ++ ++ 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 0000000..006f6f6 +--- /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/keepassx_it.ts b/share/translations/keepassx_it.ts +new file mode 100644 +index 0000000..4f91b75 +--- /dev/null ++++ b/share/translations/keepassx_it.ts +@@ -0,0 +1,1179 @@ ++ ++ ++ AboutDialog ++ ++ About KeePassX ++ A proposito di KeePassX ++ ++ ++ KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. ++ KeePassX è distribuito sotto i termini della licenza ++GNU General Public License (GPL) versione 2 o, a tua scelta, della versione 3. ++ ++ ++ ++ AutoType ++ ++ Auto-Type - KeePassX ++ Auto-Type - KeePassX ++ ++ ++ Couldn't find an entry that matches the window title. ++ Impossibile trovare una voce che corrisponda al titolo della finestra ++ ++ ++ ++ AutoTypeAssociationsModel ++ ++ Window ++ Finestra ++ ++ ++ Sequence ++ Sequenza ++ ++ ++ Default sequence ++ Sequenza predefinita ++ ++ ++ ++ AutoTypeSelectDialog ++ ++ Auto-Type - KeePassX ++ Auto-Type - KeePassX ++ ++ ++ Select entry to Auto-Type: ++ Selezionare una voce per Auto-Type: ++ ++ ++ ++ ChangeMasterKeyWidget ++ ++ Password ++ Password ++ ++ ++ Enter password: ++ Inserire password: ++ ++ ++ Repeat password: ++ Ripetere password: ++ ++ ++ Key file ++ File chiave ++ ++ ++ Browse ++ Sfogliare ++ ++ ++ Create ++ Creare ++ ++ ++ Key files ++ File chiave ++ ++ ++ All files ++ Tutti i file ++ ++ ++ Create Key File... ++ Creare file chiave... ++ ++ ++ Error ++ Errore ++ ++ ++ Unable to create Key File : ++ Impossibile creare file chiave: ++ ++ ++ Select a key file ++ Selezionare file chiave ++ ++ ++ Question ++ Domanda ++ ++ ++ Do you really want to use an empty string as password? ++ Vuoi veramente usare una stringa vuota come password? ++ ++ ++ Different passwords supplied. ++ Sono state fornite password differenti. ++ ++ ++ ++ DatabaseOpenWidget ++ ++ Enter master key ++ Inserire password ++ ++ ++ Key File: ++ File Chiave: ++ ++ ++ Password: ++ Password: ++ ++ ++ Browse ++ Sfogliare ++ ++ ++ Error ++ Errore ++ ++ ++ Unable to open the database. ++ Impossibile aprire il database. ++ ++ ++ Can't open key file ++ Impossibile aprire il file chiave ++ ++ ++ All files ++ Tutti i file ++ ++ ++ Key files ++ File chiave ++ ++ ++ Select key file ++ Selezionare file chiave ++ ++ ++ ++ DatabaseSettingsWidget ++ ++ Database name: ++ Nome database: ++ ++ ++ Database description: ++ Descrizione database: ++ ++ ++ Transform rounds: ++ Round di trasformazione: ++ ++ ++ Default username: ++ Nome utente predefinito: ++ ++ ++ Use recycle bin: ++ Utilizzare cestino: ++ ++ ++ MiB ++ MiB ++ ++ ++ Benchmark ++ Benchmark ++ ++ ++ Max. history items: ++ Max. oggetti nella cronologia: ++ ++ ++ Max. history size: ++ Max. grandezza della cronologia: ++ ++ ++ ++ DatabaseTabWidget ++ ++ Root ++ Root ++ ++ ++ KeePass 2 Database ++ Database KeePass 2 ++ ++ ++ All files ++ Tutti i file ++ ++ ++ Open database ++ Aprire database ++ ++ ++ Warning ++ Avviso ++ ++ ++ File not found! ++ File non trovato! ++ ++ ++ Open KeePass 1 database ++ Aprire database KeePass 1 ++ ++ ++ KeePass 1 database ++ Database KeePass 1 ++ ++ ++ All files (*) ++ Tutti i file (*) ++ ++ ++ Close? ++ Chiudere? ++ ++ ++ "%1" is in edit mode. ++Close anyway? ++ "%1" è in modalità modifica. ++Chiudere comunque? ++ ++ ++ Save changes? ++ Salvare modifiche? ++ ++ ++ "%1" was modified. ++Save changes? ++ "%1" è stata modificata. ++Salvare le modifiche? ++ ++ ++ Error ++ Errore ++ ++ ++ Writing the database failed. ++ Scrittura del database fallita. ++ ++ ++ Save database as ++ Salvare database come ++ ++ ++ New database ++ Nuovo database ++ ++ ++ locked ++ bloccato ++ ++ ++ ++ DatabaseWidget ++ ++ Change master key ++ Cambiare password principale ++ ++ ++ Delete entry? ++ Eliminare voce? ++ ++ ++ Do you really want to delete the entry "%1" for good? ++ Vuoi veramente eliminare la voce "%1"? ++ ++ ++ Delete entries? ++ Eliminare voci? ++ ++ ++ Do you really want to delete %1 entries for good? ++ Vuoi veramente eliminare %1 voci? ++ ++ ++ Move entries to recycle bin? ++ Muovere le voci nel cestino? ++ ++ ++ Do you really want to move %n entry(s) to the recycle bin? ++ Vuoi veramente spostare %n voce(i) nel cestino?Vuoi veramente spostare %n voce(i) nel cestino? ++ ++ ++ Delete group? ++ Eliminare gruppo? ++ ++ ++ Do you really want to delete the group "%1" for good? ++ Vuoi veramente eliminare il gruppo "%1"? ++ ++ ++ Current group ++ Gruppo corrente ++ ++ ++ ++ EditEntryWidget ++ ++ Entry ++ Voce ++ ++ ++ Advanced ++ Avanzate ++ ++ ++ Icon ++ Icona ++ ++ ++ Auto-Type ++ Auto-Type ++ ++ ++ Properties ++ Proprietà ++ ++ ++ History ++ Cronologia ++ ++ ++ Entry history ++ Cronologia voce ++ ++ ++ Add entry ++ Aggiungere voce ++ ++ ++ Edit entry ++ Modificare voce ++ ++ ++ Error ++ Errore ++ ++ ++ Different passwords supplied. ++ Sono state immesse password differenti. ++ ++ ++ New attribute ++ Nuovo attributo ++ ++ ++ Select file ++ Selezionare file ++ ++ ++ Unable to open file ++ Impossibile aprire il file ++ ++ ++ Save attachment ++ Salvare l'allegato ++ ++ ++ Unable to save the attachment: ++ ++ Impossibile salvare l'allegato ++ ++ ++ ++ Tomorrow ++ Domani ++ ++ ++ %n week(s) ++ %n settimana(e)%n settimana(e) ++ ++ ++ %n month(s) ++ %n mese(i)%n mese(i) ++ ++ ++ 1 year ++ 1 anno ++ ++ ++ ++ EditEntryWidgetAdvanced ++ ++ Additional attributes ++ Attributi addizionali ++ ++ ++ Add ++ Aggiungere ++ ++ ++ Edit ++ Modificare ++ ++ ++ Remove ++ Rimuovere ++ ++ ++ Attachments ++ Allegati ++ ++ ++ Save ++ Salvare ++ ++ ++ ++ EditEntryWidgetAutoType ++ ++ Enable Auto-Type for this entry ++ Abilitare Auto-Type per questa voce ++ ++ ++ Inherit default Auto-Type sequence from the group ++ Ereditare la sequenza predefinita di Auto-Type dal gruppo ++ ++ ++ Use custom Auto-Type sequence: ++ Usare sequenza personalizzata di Auto-Type: ++ ++ ++ + ++ + ++ ++ ++ - ++ - ++ ++ ++ Window title: ++ Titolo finestra: ++ ++ ++ Use default sequence ++ Usare sequenza predefinita ++ ++ ++ Set custom sequence: ++ Impostare sequenza personalizzata: ++ ++ ++ ++ EditEntryWidgetHistory ++ ++ Show ++ Mostrare ++ ++ ++ Restore ++ Ripristinare ++ ++ ++ Delete ++ Eliminare ++ ++ ++ Delete all ++ Eliminare tutti ++ ++ ++ ++ EditEntryWidgetMain ++ ++ Title: ++ Titolo: ++ ++ ++ Username: ++ Nome utente: ++ ++ ++ Password: ++ Password: ++ ++ ++ Repeat: ++ Ripetere: ++ ++ ++ Gen. ++ Gen. ++ ++ ++ URL: ++ URL: ++ ++ ++ Expires ++ Scade: ++ ++ ++ Presets ++ Programmare ++ ++ ++ Notes: ++ Note: ++ ++ ++ ++ EditGroupWidget ++ ++ Group ++ Gruppo ++ ++ ++ Icon ++ Icona ++ ++ ++ Properties ++ Proprietà ++ ++ ++ Add group ++ Aggiungere gruppo ++ ++ ++ Edit group ++ Modificare gruppo ++ ++ ++ Enable ++ Abilitare ++ ++ ++ Disable ++ Disabilitare ++ ++ ++ Inherit from parent group (%1) ++ Ereditare dal gruppo genitore (%1) ++ ++ ++ ++ EditGroupWidgetMain ++ ++ Name ++ Nome ++ ++ ++ Notes ++ Note ++ ++ ++ Expires ++ Scade ++ ++ ++ Search ++ Cercare ++ ++ ++ Auto-type ++ Auto-Type ++ ++ ++ ++ EditWidgetIcons ++ ++ Use default icon ++ Usare icona predefinita ++ ++ ++ Use custom icon ++ Usare icona personalizzata ++ ++ ++ Add custom icon ++ Aggiungere icona personalizzata ++ ++ ++ Delete custom icon ++ Rimuovere icona personalizzata ++ ++ ++ Images ++ Immagini ++ ++ ++ All files ++ Tutti i file ++ ++ ++ Select Image ++ Selezionare Immagine ++ ++ ++ Can't delete icon! ++ Impossibile eliminare icona! ++ ++ ++ Can't delete icon. Still used by %n item(s). ++ Impossibile eliminare l'icona in quanto è in uso da %n voce(i).Impossibile eliminare l'icona in quanto è in uso da %n voce(i). ++ ++ ++ ++ EditWidgetProperties ++ ++ Created: ++ Creato: ++ ++ ++ Modified: ++ Modificato: ++ ++ ++ Accessed: ++ Accesso: ++ ++ ++ Uuid: ++ Uuid: ++ ++ ++ ++ EntryAttributesModel ++ ++ Name ++ Nome ++ ++ ++ ++ EntryHistoryModel ++ ++ Last modified ++ Ultima modifica ++ ++ ++ Title ++ Titolo ++ ++ ++ Username ++ Nome utente ++ ++ ++ URL ++ URL ++ ++ ++ ++ EntryModel ++ ++ Group ++ Gruppo ++ ++ ++ Title ++ Titolo ++ ++ ++ Username ++ Nome Utente ++ ++ ++ URL ++ URL ++ ++ ++ ++ Group ++ ++ Recycle Bin ++ Cestino (Gruppo) ++ ++ ++ ++ KeePass1OpenWidget ++ ++ Import KeePass1 database ++ Importare database KeePass1 ++ ++ ++ Error ++ Errore ++ ++ ++ Unable to open the database. ++ Impossibile aprire il database. ++ ++ ++ ++ KeePass1Reader ++ ++ Unable to read keyfile. ++ Impossibile leggere il file chiave. ++ ++ ++ Not a KeePass database. ++ Non è un database KeePass. ++ ++ ++ Unsupported encryption algorithm. ++ Algoritmo di cifratura non supportato. ++ ++ ++ Unsupported KeePass database version. ++ Versione database non supportata ++ ++ ++ Root ++ Root (KeePass1Reader) ++ ++ ++ ++ KeePass2Reader ++ ++ Not a KeePass database. ++ Non è un database KeePass. ++ ++ ++ Unsupported KeePass database version. ++ Versione database non supportata ++ ++ ++ Wrong key or database file is corrupt. ++ Password errata o database corrotto. ++ ++ ++ ++ MainWindow ++ ++ Database ++ Database ++ ++ ++ Recent databases ++ Database recenti ++ ++ ++ Help ++ Aiuto ++ ++ ++ Entries ++ Voci ++ ++ ++ Copy attribute to clipboard ++ Copiare attributi negli appunti ++ ++ ++ Groups ++ Gruppi ++ ++ ++ Extras ++ Extra ++ ++ ++ View ++ Visualizzare ++ ++ ++ Quit ++ Uscire ++ ++ ++ About ++ A Proposito ++ ++ ++ Open database ++ Aprire database ++ ++ ++ Save database ++ Salvare database ++ ++ ++ Close database ++ Chiudere database ++ ++ ++ New database ++ Nuovo database ++ ++ ++ Add new entry ++ Aggiungere nuova voce ++ ++ ++ View/Edit entry ++ Visualizzare/Modificare voce ++ ++ ++ Delete entry ++ Eliminare voce ++ ++ ++ Add new group ++ Aggiungere nuovo gruppo ++ ++ ++ Edit group ++ Modificare gruppo ++ ++ ++ Delete group ++ Eliminare gruppo ++ ++ ++ Save database as ++ Salvare database come ++ ++ ++ Change master key ++ Cambiare password principale ++ ++ ++ Database settings ++ Impostazioni database ++ ++ ++ Import KeePass 1 database ++ Importare database KeePass 1 ++ ++ ++ Clone entry ++ Clona voce ++ ++ ++ Find ++ Trovare ++ ++ ++ Username ++ Nome Utente ++ ++ ++ Copy username to clipboard ++ Copiare nome utente negli appunti ++ ++ ++ Password ++ Password ++ ++ ++ Copy password to clipboard ++ Copiare password negli appunti ++ ++ ++ Settings ++ Impostazioni ++ ++ ++ Perform Auto-Type ++ Eseguire Auto-Type ++ ++ ++ Open URL ++ Aprire URL ++ ++ ++ Lock databases ++ Bloccare database ++ ++ ++ Title ++ Titolo ++ ++ ++ URL ++ URL ++ ++ ++ Notes ++ Note ++ ++ ++ Show toolbar ++ Mostrare barra degli strumenti ++ ++ ++ read-only ++ sola lettura ++ ++ ++ ++ PasswordGeneratorWidget ++ ++ Password: ++ Password: ++ ++ ++ Length: ++ Lunghezza: ++ ++ ++ Character Types ++ Tipi di carattere ++ ++ ++ Upper Case Letters ++ Lettere maiuscole ++ ++ ++ Lower Case Letters ++ Lettere minuscole ++ ++ ++ Numbers ++ Numeri ++ ++ ++ Special Characters ++ Caratteri speciali ++ ++ ++ Exclude look-alike characters ++ Escludere caratteri simili ++ ++ ++ Ensure that the password contains characters from every group ++ Assicurare che la password contenga caratteri di ogni gruppo ++ ++ ++ Accept ++ Accettare ++ ++ ++ ++ QCommandLineParser ++ ++ Displays version information. ++ Mostrare informazioni sulla versione. ++ ++ ++ Displays this help. ++ Mostrare questo aiuto. ++ ++ ++ Unknown option '%1'. ++ Opzione sconosciuta '%1'. ++ ++ ++ Unknown options: %1. ++ Opzioni sconosciute '%1'. ++ ++ ++ Missing value after '%1'. ++ Manca valore dopo '%1'. ++ ++ ++ Unexpected value after '%1'. ++ Valore inaspettato dopo '%1'. ++ ++ ++ [options] ++ [opzioni] ++ ++ ++ Usage: %1 ++ Uso: %1 ++ ++ ++ Options: ++ Opzioni: ++ ++ ++ Arguments: ++ Argomenti: ++ ++ ++ ++ QSaveFile ++ ++ Existing file %1 is not writable ++ Il file esistente %1 non è scrivibile ++ ++ ++ Writing canceled by application ++ Scrittura cancellata dall'applicazione ++ ++ ++ Partial write. Partition full? ++ Scrittura parziale. Partizione piena? ++ ++ ++ ++ QtIOCompressor ++ ++ Internal zlib error when compressing: ++ Errore interno di zlib durante la compressione: ++ ++ ++ Error writing to underlying device: ++ Errore durante la scrittura nel dispositivo: ++ ++ ++ Error opening underlying device: ++ Errore durante l'apertura dal dispositivo: ++ ++ ++ Error reading data from underlying device: ++ Errore durante la lettura dal dispositivo: ++ ++ ++ Internal zlib error when decompressing: ++ Errore interno di zlib durante la decompressione: ++ ++ ++ ++ QtIOCompressor::open ++ ++ The gzip format not supported in this version of zlib. ++ Formato gzip non supportato da questa versione di zlib. ++ ++ ++ Internal zlib error: ++ Errore interno di zlib: ++ ++ ++ ++ SearchWidget ++ ++ Find: ++ Trovare: ++ ++ ++ Case sensitive ++ Case sensitive ++ ++ ++ Current group ++ Gruppo corrente ++ ++ ++ Root group ++ Gruppo radice ++ ++ ++ ++ SettingsWidget ++ ++ Application Settings ++ Impostazioni applicazione ++ ++ ++ General ++ Generale ++ ++ ++ Security ++ Sicurezza ++ ++ ++ ++ SettingsWidgetGeneral ++ ++ Remember last databases ++ Ricordare ultimo database ++ ++ ++ Open previous databases on startup ++ Aprire precedente database all'avvio ++ ++ ++ Mark as modified on expanded state changes ++ Marcare come modificata quando la voce viene espansa ++ ++ ++ Automatically save on exit ++ Salvare automaticamente all'uscita ++ ++ ++ Automatically save after every change ++ Salvare automaticamente dopo ogni modifica ++ ++ ++ Minimize when copying to clipboard ++ Minimizzare quando si copia negli appunti ++ ++ ++ Use group icon on entry creation ++ Usare l'icona del gruppo alla creazione di una voce ++ ++ ++ Global Auto-Type shortcut ++ Scorciatoia Auto-Type globale ++ ++ ++ Use entry title to match windows for global auto-type ++ Utilizzare il titolo della voce per abbinare la finestra per auto-type globale ++ ++ ++ ++ SettingsWidgetSecurity ++ ++ Clear clipboard after ++ Pulire appunti dopo ++ ++ ++ sec ++ sec ++ ++ ++ Lock databases after inactivity of ++ Bloccare database dopo un'inattività di ++ ++ ++ Show passwords in cleartext by default ++ Mostrare la password in chiaro in maniera predefinita ++ ++ ++ Always ask before performing auto-type ++ Chiedere sempre prima di eseguire auto-type ++ ++ ++ ++ UnlockDatabaseWidget ++ ++ Unlock database ++ Sbloccare database ++ ++ ++ Error ++ Errore ++ ++ ++ Wrong key. ++ Password errata. ++ ++ ++ ++ WelcomeWidget ++ ++ Welcome! ++ Benvenuto/a! ++ ++ ++ ++ main ++ ++ KeePassX - cross-platform password manager ++ KeePassX - gestore di password cross-platform ++ ++ ++ filename of the password database to open (*.kdbx) ++ nome del file del database da aprire (*.kdbx) ++ ++ ++ path to a custom config file ++ percorso ad un file di configurazione personalizzato ++ ++ ++ password of the database (DANGEROUS!) ++ password del database (PERICOLOSO!) ++ ++ ++ key file of the database ++ file chiave del database ++ ++ ++ +\ No newline at end of file +diff --git a/share/translations/keepassx_nl_NL.ts b/share/translations/keepassx_nl_NL.ts +new file mode 100644 +index 0000000..aa6320e +--- /dev/null ++++ b/share/translations/keepassx_nl_NL.ts +@@ -0,0 +1,1178 @@ ++ ++ ++ AboutDialog ++ ++ About KeePassX ++ Over KeePassX ++ ++ ++ KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. ++ KeePassX wordt verspreid onder de bepalingen van de GNU General Public License (GPL) versie 2 of (als u wenst) versie 3. ++ ++ ++ ++ AutoType ++ ++ Auto-Type - KeePassX ++ Auto-typen - KeePassX ++ ++ ++ Couldn't find an entry that matches the window title. ++ Kon geen element vinden dat overeenkomt met de venstertitel. ++ ++ ++ ++ AutoTypeAssociationsModel ++ ++ Window ++ Venster ++ ++ ++ Sequence ++ Volgorde ++ ++ ++ Default sequence ++ Standaardvolgorde ++ ++ ++ ++ AutoTypeSelectDialog ++ ++ Auto-Type - KeePassX ++ Auto-typen - KeePassX ++ ++ ++ Select entry to Auto-Type: ++ Kies element om automatisch te typen: ++ ++ ++ ++ ChangeMasterKeyWidget ++ ++ Password ++ Wachtwoord ++ ++ ++ Enter password: ++ Geef wachtwoord: ++ ++ ++ Repeat password: ++ Herhaal wachtwoord: ++ ++ ++ Key file ++ Sleutelbestand ++ ++ ++ Browse ++ Bladeren ++ ++ ++ Create ++ Aanmaken ++ ++ ++ Key files ++ Sleutelbestanden ++ ++ ++ All files ++ Alle bestanden ++ ++ ++ Create Key File... ++ Genereer sleutelbestand... ++ ++ ++ Error ++ Fout ++ ++ ++ Unable to create Key File : ++ Niet mogelijk om sleutelbestand aan te maken: ++ ++ ++ Select a key file ++ Kies een sleutelbestand ++ ++ ++ Question ++ Vraag ++ ++ ++ Do you really want to use an empty string as password? ++ Weet u zeker dat u een leeg veld als wachtwoord wilt gebruiken? ++ ++ ++ Different passwords supplied. ++ Verschillende wachtwoorden opgegeven. ++ ++ ++ ++ DatabaseOpenWidget ++ ++ Enter master key ++ Geef hoofdsleutel ++ ++ ++ Key File: ++ Sleutelbestand: ++ ++ ++ Password: ++ Wachtwoord: ++ ++ ++ Browse ++ Bladeren ++ ++ ++ Error ++ Fout ++ ++ ++ Unable to open the database. ++ Niet mogelijk om de database te openen. ++ ++ ++ Can't open key file ++ Niet mogelijk om het sleutelbestand te openen ++ ++ ++ All files ++ Alle bestanden ++ ++ ++ Key files ++ Sleutelbestanden ++ ++ ++ Select key file ++ Kies sleutelbestand ++ ++ ++ ++ DatabaseSettingsWidget ++ ++ Database name: ++ Naam van de database: ++ ++ ++ Database description: ++ Beschrijving van de database: ++ ++ ++ Transform rounds: ++ Transformatierondes: ++ ++ ++ Default username: ++ Standaard gebruikersnaam: ++ ++ ++ Use recycle bin: ++ Gebruik prullenbak: ++ ++ ++ MiB ++ MiB ++ ++ ++ Benchmark ++ Test ++ ++ ++ Max. history items: ++ Max. items in geschiedenis: ++ ++ ++ Max. history size: ++ Max. grootte geschiedenis: ++ ++ ++ ++ DatabaseTabWidget ++ ++ Root ++ Alles ++ ++ ++ KeePass 2 Database ++ KeePass 2 Database ++ ++ ++ All files ++ Alle bestanden ++ ++ ++ Open database ++ Open database ++ ++ ++ Warning ++ Waarschuwing ++ ++ ++ File not found! ++ Bestand niet gevonden! ++ ++ ++ Open KeePass 1 database ++ Open KeePass 1 database ++ ++ ++ KeePass 1 database ++ KeePass 1 database ++ ++ ++ All files (*) ++ Alle bestanden (*) ++ ++ ++ Close? ++ Sluiten? ++ ++ ++ "%1" is in edit mode. ++Close anyway? ++ "%1" is in bewerkmodus. ++Toch sluiten? ++ ++ ++ Save changes? ++ Wijzigingen opslaan? ++ ++ ++ "%1" was modified. ++Save changes? ++ "%1" is gewijzigd. ++Opslaan? ++ ++ ++ Error ++ Fout ++ ++ ++ Writing the database failed. ++ Opslaan van de database is mislukt. ++ ++ ++ Save database as ++ Database opslaan als ++ ++ ++ New database ++ Nieuwe database ++ ++ ++ locked ++ vergrendeld ++ ++ ++ ++ DatabaseWidget ++ ++ Change master key ++ Wijzig hoofdsleutel ++ ++ ++ Delete entry? ++ Element verwijderen? ++ ++ ++ Do you really want to delete the entry "%1" for good? ++ Weet u zeker dat u het element "%1" wilt verwijderen? ++ ++ ++ Delete entries? ++ Elementen wissen? ++ ++ ++ Do you really want to delete %1 entries for good? ++ Weet u zeker dat u %1 elementen wilt wissen? ++ ++ ++ Move entries to recycle bin? ++ Elementen naar de prullenbak verplaatsen? ++ ++ ++ Do you really want to move %n entry(s) to the recycle bin? ++ Weet u zeker dat u %n element naar de prullenbak wilt verplaatsen?Weet u zeker dat u %n elementen naar de prullenbak wilt verplaatsen? ++ ++ ++ Delete group? ++ Groep verwijderen? ++ ++ ++ Do you really want to delete the group "%1" for good? ++ Weet u zeker dat u de groep "%1" wilt verwijderen? ++ ++ ++ Current group ++ Huidige groep ++ ++ ++ ++ EditEntryWidget ++ ++ Entry ++ Element ++ ++ ++ Advanced ++ Geavanceerd ++ ++ ++ Icon ++ Icoon ++ ++ ++ Auto-Type ++ Auto-typen - KeePassX ++ ++ ++ Properties ++ Eigenschappen ++ ++ ++ History ++ Geschiedenis ++ ++ ++ Entry history ++ Geschiedenis van element ++ ++ ++ Add entry ++ Element toevoegen ++ ++ ++ Edit entry ++ Element wijzigen ++ ++ ++ Error ++ Fout ++ ++ ++ Different passwords supplied. ++ Verschillende wachtwoorden opgegeven. ++ ++ ++ New attribute ++ Nieuwe eigenschap ++ ++ ++ Select file ++ Kies bestand ++ ++ ++ Unable to open file ++ Niet mogelijk om bestand te openen ++ ++ ++ Save attachment ++ Bijlage opslaan ++ ++ ++ Unable to save the attachment: ++ ++ Niet mogelijk om de bijlage op te slaan: ++ ++ ++ ++ Tomorrow ++ Morgen ++ ++ ++ %n week(s) ++ %n week%n weken ++ ++ ++ %n month(s) ++ %n maand%n maanden ++ ++ ++ 1 year ++ 1 jaar ++ ++ ++ ++ EditEntryWidgetAdvanced ++ ++ Additional attributes ++ Extra eigenschappen ++ ++ ++ Add ++ Toevoegen ++ ++ ++ Edit ++ Wijzigen ++ ++ ++ Remove ++ Verwijderen ++ ++ ++ Attachments ++ Bijlagen ++ ++ ++ Save ++ Opslaan ++ ++ ++ ++ EditEntryWidgetAutoType ++ ++ Enable Auto-Type for this entry ++ Auto-typen inschakelen voor dit element ++ ++ ++ Inherit default Auto-Type sequence from the group ++ Erf standaard auto-typevolgorde van de groep ++ ++ ++ Use custom Auto-Type sequence: ++ Gebruik aangepaste auto-typevolgorde: ++ ++ ++ + ++ + ++ ++ ++ - ++ - ++ ++ ++ Window title: ++ Venstertitel: ++ ++ ++ Use default sequence ++ Gebruik standaardvolgorde ++ ++ ++ Set custom sequence: ++ Aangepaste volgorde: ++ ++ ++ ++ EditEntryWidgetHistory ++ ++ Show ++ Tonen ++ ++ ++ Restore ++ Herstellen ++ ++ ++ Delete ++ Verwijderen ++ ++ ++ Delete all ++ Alles verwijderen ++ ++ ++ ++ EditEntryWidgetMain ++ ++ Title: ++ Titel: ++ ++ ++ Username: ++ Gebruikersnaam: ++ ++ ++ Password: ++ Wachtwoord: ++ ++ ++ Repeat: ++ Herhalen: ++ ++ ++ Gen. ++ Gen. ++ ++ ++ URL: ++ URL: ++ ++ ++ Expires ++ Verloopt ++ ++ ++ Presets ++ Ingebouwd ++ ++ ++ Notes: ++ Opmerkingen: ++ ++ ++ ++ EditGroupWidget ++ ++ Group ++ Groep ++ ++ ++ Icon ++ Icoon ++ ++ ++ Properties ++ Eigenschappen ++ ++ ++ Add group ++ Groep toevoegen ++ ++ ++ Edit group ++ Groep wijzigen ++ ++ ++ Enable ++ Inschakelen ++ ++ ++ Disable ++ Uitschakelen ++ ++ ++ Inherit from parent group (%1) ++ Erf van bovenliggende groep (%1) ++ ++ ++ ++ EditGroupWidgetMain ++ ++ Name ++ Naam ++ ++ ++ Notes ++ Opmerkingen ++ ++ ++ Expires ++ Verloopt ++ ++ ++ Search ++ Zoeken ++ ++ ++ Auto-type ++ Auto-typen ++ ++ ++ ++ EditWidgetIcons ++ ++ Use default icon ++ Gebruik standaardicoon ++ ++ ++ Use custom icon ++ Gebruik aangepast icoon ++ ++ ++ Add custom icon ++ Voeg icoon toe ++ ++ ++ Delete custom icon ++ Verwijder icoon ++ ++ ++ Images ++ Afbeeldingen ++ ++ ++ All files ++ Alle bestanden ++ ++ ++ Select Image ++ Kies afbeelding ++ ++ ++ Can't delete icon! ++ Kan icoon niet verwijderen! ++ ++ ++ Can't delete icon. Still used by %n item(s). ++ Kan icoon niet verwijderen. Het wordt nog gebruikt door %n element.Kan icoon niet verwijderen. Het wordt nog gebruikt door %n elementen. ++ ++ ++ ++ EditWidgetProperties ++ ++ Created: ++ Aangemaakt: ++ ++ ++ Modified: ++ Gewijzigd: ++ ++ ++ Accessed: ++ Gelezen: ++ ++ ++ Uuid: ++ Uuid: ++ ++ ++ ++ EntryAttributesModel ++ ++ Name ++ Naam ++ ++ ++ ++ EntryHistoryModel ++ ++ Last modified ++ Laatst gewijzigd ++ ++ ++ Title ++ Titel ++ ++ ++ Username ++ Gebruikersnaam ++ ++ ++ URL ++ URL ++ ++ ++ ++ EntryModel ++ ++ Group ++ Groep ++ ++ ++ Title ++ Titel ++ ++ ++ Username ++ Gebruikersnaam ++ ++ ++ URL ++ URL ++ ++ ++ ++ Group ++ ++ Recycle Bin ++ Prullenbak ++ ++ ++ ++ KeePass1OpenWidget ++ ++ Import KeePass1 database ++ Importeer Keepass 1-database ++ ++ ++ Error ++ Fout ++ ++ ++ Unable to open the database. ++ Niet mogelijk om de database te openen. ++ ++ ++ ++ KeePass1Reader ++ ++ Unable to read keyfile. ++ Niet mogelijk om sleutelbestand te lezen ++ ++ ++ Not a KeePass database. ++ Geen Keepass-database ++ ++ ++ Unsupported encryption algorithm. ++ Niet-ondersteund encryptie-algoritme ++ ++ ++ Unsupported KeePass database version. ++ Niet-ondersteunde versie van Keepass-database ++ ++ ++ Root ++ Alles ++ ++ ++ ++ KeePass2Reader ++ ++ Not a KeePass database. ++ Geen Keepass-database. ++ ++ ++ Unsupported KeePass database version. ++ Niet-ondersteunde versie van Keepass-database. ++ ++ ++ Wrong key or database file is corrupt. ++ Verkeerde sleutel of corrupte database. ++ ++ ++ ++ MainWindow ++ ++ Database ++ Database ++ ++ ++ Recent databases ++ Recente databases ++ ++ ++ Help ++ Help ++ ++ ++ Entries ++ Elementen ++ ++ ++ Copy attribute to clipboard ++ Kopieer eigenschap naar klembord ++ ++ ++ Groups ++ Groepen ++ ++ ++ Extras ++ Extra's ++ ++ ++ View ++ Beeld ++ ++ ++ Quit ++ Afsluiten ++ ++ ++ About ++ Over ++ ++ ++ Open database ++ Open database ++ ++ ++ Save database ++ Sla database op ++ ++ ++ Close database ++ Sluit database ++ ++ ++ New database ++ Nieuwe database ++ ++ ++ Add new entry ++ Voeg element toe ++ ++ ++ View/Edit entry ++ Bekijk/bewerk element ++ ++ ++ Delete entry ++ Verwijder element ++ ++ ++ Add new group ++ Voeg groep toe ++ ++ ++ Edit group ++ Bewerk groep ++ ++ ++ Delete group ++ Verwijder groep ++ ++ ++ Save database as ++ Database opslaan als ++ ++ ++ Change master key ++ Hoofdsleutel wijzigen ++ ++ ++ Database settings ++ Database-instellingen ++ ++ ++ Import KeePass 1 database ++ Importeer Keepass 1-database ++ ++ ++ Clone entry ++ Element klonen ++ ++ ++ Find ++ Vind ++ ++ ++ Username ++ Gebruikersnaam ++ ++ ++ Copy username to clipboard ++ Kopieer gebruikersnaam naar klembord ++ ++ ++ Password ++ Wachtwoord ++ ++ ++ Copy password to clipboard ++ Kopieer wachtwoord naar klembord ++ ++ ++ Settings ++ Instellingen ++ ++ ++ Perform Auto-Type ++ Voer auto-typen uit ++ ++ ++ Open URL ++ Open URL ++ ++ ++ Lock databases ++ Vergrendel databases ++ ++ ++ Title ++ Titel ++ ++ ++ URL ++ URL ++ ++ ++ Notes ++ Opmerkingen ++ ++ ++ Show toolbar ++ Werkbalk weergeven ++ ++ ++ read-only ++ alleen-lezen ++ ++ ++ ++ PasswordGeneratorWidget ++ ++ Password: ++ Wachtwoord: ++ ++ ++ Length: ++ Lengte: ++ ++ ++ Character Types ++ Tekens ++ ++ ++ Upper Case Letters ++ Hoofdletters ++ ++ ++ Lower Case Letters ++ Kleine letters ++ ++ ++ Numbers ++ Cijfers ++ ++ ++ Special Characters ++ Speciale tekens ++ ++ ++ Exclude look-alike characters ++ Geen op elkaar lijkende tekens ++ ++ ++ Ensure that the password contains characters from every group ++ Zorg dat het wachtwoord tekens uit iedere groep bevat ++ ++ ++ Accept ++ Accepteren ++ ++ ++ ++ QCommandLineParser ++ ++ Displays version information. ++ Toont versie-informatie. ++ ++ ++ Displays this help. ++ Toont deze helptekst. ++ ++ ++ Unknown option '%1'. ++ Onbekende optie '%1'. ++ ++ ++ Unknown options: %1. ++ Onbekende opties: %1. ++ ++ ++ Missing value after '%1'. ++ Ontbrekende waarde na '%1'. ++ ++ ++ Unexpected value after '%1'. ++ Onverwachte waarde na '%1'. ++ ++ ++ [options] ++ [opties] ++ ++ ++ Usage: %1 ++ Gebruik: %1 ++ ++ ++ Options: ++ Opties: ++ ++ ++ Arguments: ++ Argumenten: ++ ++ ++ ++ QSaveFile ++ ++ Existing file %1 is not writable ++ Bestaand bestand %1 is niet schrijfbaar ++ ++ ++ Writing canceled by application ++ Schrijven afgebroken door programma ++ ++ ++ Partial write. Partition full? ++ Slechts deels geschreven. Is de schijf vol? ++ ++ ++ ++ QtIOCompressor ++ ++ Internal zlib error when compressing: ++ Interne fout in zlib bij inpakken: ++ ++ ++ Error writing to underlying device: ++ Fout bij schrijven naar onderliggend apparaat: ++ ++ ++ Error opening underlying device: ++ Fout bij openen van onderliggend apparaat: ++ ++ ++ Error reading data from underlying device: ++ Fout bij lezen van gegevens van onderliggend apparaat: ++ ++ ++ Internal zlib error when decompressing: ++ Interne fout in zlib bij uitpakken: ++ ++ ++ ++ QtIOCompressor::open ++ ++ The gzip format not supported in this version of zlib. ++ Gzip wordt niet ondersteund in deze versie van zlib. ++ ++ ++ Internal zlib error: ++ Interne fout in zlib: ++ ++ ++ ++ SearchWidget ++ ++ Find: ++ Vind: ++ ++ ++ Case sensitive ++ Hoofdlettergevoelig ++ ++ ++ Current group ++ Huidige groep ++ ++ ++ Root group ++ Hoofdgroep ++ ++ ++ ++ SettingsWidget ++ ++ Application Settings ++ Programma-instellingen ++ ++ ++ General ++ Algemeen ++ ++ ++ Security ++ Beveiliging ++ ++ ++ ++ SettingsWidgetGeneral ++ ++ Remember last databases ++ Onthoud laatste databases ++ ++ ++ Open previous databases on startup ++ Open vorige databases bij starten ++ ++ ++ Mark as modified on expanded state changes ++ Markeer database als gewijzigd bij wijzigen van de status ++ ++ ++ Automatically save on exit ++ Automatisch opslaan bij afsluiten ++ ++ ++ Automatically save after every change ++ Automatisch opslaan na iedere wijziging ++ ++ ++ Minimize when copying to clipboard ++ Minimaliseer bij kopieeren naar klembord ++ ++ ++ Use group icon on entry creation ++ Gebruik icoon van de groep voor nieuwe elementen ++ ++ ++ Global Auto-Type shortcut ++ Globale sneltoets voor auto-typen ++ ++ ++ Use entry title to match windows for global auto-type ++ Gebruik naam van element als vensternaam voor auto-typen ++ ++ ++ ++ SettingsWidgetSecurity ++ ++ Clear clipboard after ++ Leeg klembord na ++ ++ ++ sec ++ sec ++ ++ ++ Lock databases after inactivity of ++ Vergrendel databases na inactiviteit van ++ ++ ++ Show passwords in cleartext by default ++ Laat wachtwoorden standaard zien ++ ++ ++ Always ask before performing auto-type ++ Altijd vragen alvorens auto-type uit te voeren ++ ++ ++ ++ UnlockDatabaseWidget ++ ++ Unlock database ++ Database ontgrendelen ++ ++ ++ Error ++ Fout ++ ++ ++ Wrong key. ++ Verkeerd wachtwoord ++ ++ ++ ++ WelcomeWidget ++ ++ Welcome! ++ Welkom! ++ ++ ++ ++ main ++ ++ KeePassX - cross-platform password manager ++ KeepassX - multi-platform wachtwoordbeheerder ++ ++ ++ filename of the password database to open (*.kdbx) ++ bestandsnaam van de te openen wachtwoorddatabase (*.kdbx) ++ ++ ++ path to a custom config file ++ pad naar een configuratiebestand ++ ++ ++ password of the database (DANGEROUS!) ++ wachtwoord van de database (GEVAARLIJK!) ++ ++ ++ key file of the database ++ sleutelbestand van de database ++ ++ ++ +\ No newline at end of file +diff --git a/share/translations/keepassx_sv.ts b/share/translations/keepassx_sv.ts +new file mode 100644 +index 0000000..2a3ba79 +--- /dev/null ++++ b/share/translations/keepassx_sv.ts +@@ -0,0 +1,1178 @@ ++ ++ ++ AboutDialog ++ ++ About KeePassX ++ Om KeePassX ++ ++ ++ KeePassX is distributed under the term of the GNU General Public License (GPL) version 2 or (at your option) version 3. ++ Keepassx distribueras enligt villkoren i GNU General Public License (GPL) version 2 eller (om du vill) version 3. ++ ++ ++ ++ AutoType ++ ++ Auto-Type - KeePassX ++ Auto-skriv - KeePassX ++ ++ ++ Couldn't find an entry that matches the window title. ++ Kunde inte hitta en post som matchar fönstertiteln. ++ ++ ++ ++ AutoTypeAssociationsModel ++ ++ Window ++ Fönster ++ ++ ++ Sequence ++ Sekvens ++ ++ ++ Default sequence ++ Standard sekvens ++ ++ ++ ++ AutoTypeSelectDialog ++ ++ Auto-Type - KeePassX ++ Auto-skriv - KeePassX ++ ++ ++ Select entry to Auto-Type: ++ Välj post att auto-skriva ++ ++ ++ ++ ChangeMasterKeyWidget ++ ++ Password ++ Lösenord ++ ++ ++ Enter password: ++ Ange lösenord: ++ ++ ++ Repeat password: ++ Repetera lösenord: ++ ++ ++ Key file ++ Nyckel-fil ++ ++ ++ Browse ++ Bläddra ++ ++ ++ Create ++ Skapa ++ ++ ++ Key files ++ Nyckel-filer ++ ++ ++ All files ++ Alla filer ++ ++ ++ Create Key File... ++ Skapa nyckel-fil... ++ ++ ++ Error ++ Fel ++ ++ ++ Unable to create Key File : ++ Kunde inte skapa nyckel-fil ++ ++ ++ Select a key file ++ Välj nyckel-fil ++ ++ ++ Question ++ FrÃ¥ga ++ ++ ++ Do you really want to use an empty string as password? ++ Vill du verkligen vill använda en tom sträng som lösenord? ++ ++ ++ Different passwords supplied. ++ Olika lösenord angivna ++ ++ ++ ++ DatabaseOpenWidget ++ ++ Enter master key ++ Ange huvud lösenord ++ ++ ++ Key File: ++ Nyckel-fil: ++ ++ ++ Password: ++ Lösenord: ++ ++ ++ Browse ++ Bläddra ++ ++ ++ Error ++ Fel ++ ++ ++ Unable to open the database. ++ Kunde inte öppna databas. ++ ++ ++ Can't open key file ++ Kan inte öppna nyckel-fil ++ ++ ++ All files ++ Alla filer ++ ++ ++ Key files ++ Nyckel-filer ++ ++ ++ Select key file ++ Välj nyckel-fil ++ ++ ++ ++ DatabaseSettingsWidget ++ ++ Database name: ++ Databasnamn: ++ ++ ++ Database description: ++ Databasbeskrivning: ++ ++ ++ Transform rounds: ++ Transformerings varv: ++ ++ ++ Default username: ++ Standard användarnamn: ++ ++ ++ Use recycle bin: ++ Använd papperskorg: ++ ++ ++ MiB ++ MiB ++ ++ ++ Benchmark ++ Benchmark ++ ++ ++ Max. history items: ++ Maxantal historik poster: ++ ++ ++ Max. history size: ++ Maximal historik storlek: ++ ++ ++ ++ DatabaseTabWidget ++ ++ Root ++ Root ++ ++ ++ KeePass 2 Database ++ KeePass 2 Databas ++ ++ ++ All files ++ Alla filer ++ ++ ++ Open database ++ Öppna databas ++ ++ ++ Warning ++ Varning ++ ++ ++ File not found! ++ Filen kunde inte hittas! ++ ++ ++ Open KeePass 1 database ++ Öppna KeePass 1 databas ++ ++ ++ KeePass 1 database ++ KeePass 1 databas ++ ++ ++ All files (*) ++ Alla filer (*) ++ ++ ++ Close? ++ Stäng? ++ ++ ++ "%1" is in edit mode. ++Close anyway? ++ "%1" är i redigerar-läge. ++Stäng ändÃ¥? ++ ++ ++ Save changes? ++ Spara ändringar? ++ ++ ++ "%1" was modified. ++Save changes? ++ "%1" har ändrats. ++Spara ändringarna? ++ ++ ++ Error ++ Fel ++ ++ ++ Writing the database failed. ++ Kunde inte skriva till databasen. ++ ++ ++ Save database as ++ Spara databas som ++ ++ ++ New database ++ Ny databas ++ ++ ++ locked ++ lÃ¥st ++ ++ ++ ++ DatabaseWidget ++ ++ Change master key ++ Ändra huvud lösenord ++ ++ ++ Delete entry? ++ Ta bort post? ++ ++ ++ Do you really want to delete the entry "%1" for good? ++ Vill du verkligen ta bort "%1" för gott? ++ ++ ++ Delete entries? ++ Ta bort poster? ++ ++ ++ Do you really want to delete %1 entries for good? ++ Vill du verkligen ta bort %1 poser för gott? ++ ++ ++ Move entries to recycle bin? ++ Lägg poster i papperskorgen? ++ ++ ++ Do you really want to move %n entry(s) to the recycle bin? ++ Vill du verkligen flytta %n post till papperskorgen?Vill du verkligen flytta %n poster till papperskorgen? ++ ++ ++ Delete group? ++ Ta bort grupp? ++ ++ ++ Do you really want to delete the group "%1" for good? ++ Vill du verkligen ta bort gruppen "%1" för gott? ++ ++ ++ Current group ++ Nuvarande grupp ++ ++ ++ ++ EditEntryWidget ++ ++ Entry ++ Post ++ ++ ++ Advanced ++ Avancerat ++ ++ ++ Icon ++ Ikon ++ ++ ++ Auto-Type ++ Auto-skriv ++ ++ ++ Properties ++ Egenskaper ++ ++ ++ History ++ Historik ++ ++ ++ Entry history ++ Posthistork ++ ++ ++ Add entry ++ Lägg till post ++ ++ ++ Edit entry ++ Ändra post ++ ++ ++ Error ++ Fel ++ ++ ++ Different passwords supplied. ++ Olika lösenord angivna ++ ++ ++ New attribute ++ Nytt attribut ++ ++ ++ Select file ++ Välj fil ++ ++ ++ Unable to open file ++ Kunde inte öppna filen. ++ ++ ++ Save attachment ++ Spara bifogad fil ++ ++ ++ Unable to save the attachment: ++ ++ Kunde inte spara bifogad fil: ++ ++ ++ ++ Tomorrow ++ Imorgon ++ ++ ++ %n week(s) ++ %n vecka%n veckor ++ ++ ++ %n month(s) ++ %n mÃ¥nad%n mÃ¥nader ++ ++ ++ 1 year ++ 1 Ã¥r ++ ++ ++ ++ EditEntryWidgetAdvanced ++ ++ Additional attributes ++ Ytterligare attribut ++ ++ ++ Add ++ Lägg till ++ ++ ++ Edit ++ Ändra ++ ++ ++ Remove ++ Ta bort ++ ++ ++ Attachments ++ Bilagor ++ ++ ++ Save ++ Spara ++ ++ ++ ++ EditEntryWidgetAutoType ++ ++ Enable Auto-Type for this entry ++ SlÃ¥ pÃ¥ auto-skriv för denna post ++ ++ ++ Inherit default Auto-Type sequence from the group ++ Ärv standard auto-skriv sekvens för grupp ++ ++ ++ Use custom Auto-Type sequence: ++ Använd egen auto-skriv sekvens: ++ ++ ++ + ++ + ++ ++ ++ - ++ - ++ ++ ++ Window title: ++ Fönster titel: ++ ++ ++ Use default sequence ++ Använd standard sekvens ++ ++ ++ Set custom sequence: ++ Egen sekvens: ++ ++ ++ ++ EditEntryWidgetHistory ++ ++ Show ++ Visa ++ ++ ++ Restore ++ Återställ ++ ++ ++ Delete ++ Ta bort ++ ++ ++ Delete all ++ Ta bort alla ++ ++ ++ ++ EditEntryWidgetMain ++ ++ Title: ++ Titel: ++ ++ ++ Username: ++ Användarnamn: ++ ++ ++ Password: ++ Lösenord: ++ ++ ++ Repeat: ++ Repetera: ++ ++ ++ Gen. ++ Gen. ++ ++ ++ URL: ++ URL: ++ ++ ++ Expires ++ GÃ¥r ut ++ ++ ++ Presets ++ Förinställningar ++ ++ ++ Notes: ++ Anteckningar: ++ ++ ++ ++ EditGroupWidget ++ ++ Group ++ Grupp ++ ++ ++ Icon ++ Ikon ++ ++ ++ Properties ++ Egenskaper ++ ++ ++ Add group ++ Lägg till grupp ++ ++ ++ Edit group ++ Ändra grupp ++ ++ ++ Enable ++ SlÃ¥ pÃ¥ ++ ++ ++ Disable ++ Stäng av ++ ++ ++ Inherit from parent group (%1) ++ Ärv frÃ¥n förälder grupp (%1) ++ ++ ++ ++ EditGroupWidgetMain ++ ++ Name ++ Namn ++ ++ ++ Notes ++ Anteckningar ++ ++ ++ Expires ++ GÃ¥r ut ++ ++ ++ Search ++ Sök ++ ++ ++ Auto-type ++ Auto-skriv ++ ++ ++ ++ EditWidgetIcons ++ ++ Use default icon ++ Använd standard ikon ++ ++ ++ Use custom icon ++ Använd egen ikon ++ ++ ++ Add custom icon ++ Lägg till egen ikon ++ ++ ++ Delete custom icon ++ Ta bort egen ikon ++ ++ ++ Images ++ Bilder ++ ++ ++ All files ++ Alla filer ++ ++ ++ Select Image ++ Välj bild ++ ++ ++ Can't delete icon! ++ Kan inte ta bort ikon! ++ ++ ++ Can't delete icon. Still used by %n item(s). ++ Kan inte ta bort ikonen. Den används fortfarande av %n postKan inte ta bort ikonen. Den används fortfarande av %n poster ++ ++ ++ ++ EditWidgetProperties ++ ++ Created: ++ Skapad: ++ ++ ++ Modified: ++ Ändrad: ++ ++ ++ Accessed: ++ Läst: ++ ++ ++ Uuid: ++ UUID: ++ ++ ++ ++ EntryAttributesModel ++ ++ Name ++ Namn ++ ++ ++ ++ EntryHistoryModel ++ ++ Last modified ++ Senast ändrad ++ ++ ++ Title ++ Titel ++ ++ ++ Username ++ Användarnamn ++ ++ ++ URL ++ URL ++ ++ ++ ++ EntryModel ++ ++ Group ++ Grupp ++ ++ ++ Title ++ Titel ++ ++ ++ Username ++ Användarnamn ++ ++ ++ URL ++ URL ++ ++ ++ ++ Group ++ ++ Recycle Bin ++ Papperskorg ++ ++ ++ ++ KeePass1OpenWidget ++ ++ Import KeePass1 database ++ Importera KeePass1 databas ++ ++ ++ Error ++ Fel ++ ++ ++ Unable to open the database. ++ Kunde inte öppna databas. ++ ++ ++ ++ KeePass1Reader ++ ++ Unable to read keyfile. ++ Kunde inte läsa nyckel-filen. ++ ++ ++ Not a KeePass database. ++ Inte en KeePass databas ++ ++ ++ Unsupported encryption algorithm. ++ Krypteringsalgoritnmen stöds ej ++ ++ ++ Unsupported KeePass database version. ++ KeePass databas versionen stöds ej. ++ ++ ++ Root ++ Root ++ ++ ++ ++ KeePass2Reader ++ ++ Not a KeePass database. ++ Inte en KeePass databas. ++ ++ ++ Unsupported KeePass database version. ++ KeePass databas versionen stöds ej. ++ ++ ++ Wrong key or database file is corrupt. ++ Fel lösenord eller korrupt databas-fil ++ ++ ++ ++ MainWindow ++ ++ Database ++ Databas ++ ++ ++ Recent databases ++ Senast använda databser ++ ++ ++ Help ++ Hjälp ++ ++ ++ Entries ++ Poster ++ ++ ++ Copy attribute to clipboard ++ Kopiera attribut ++ ++ ++ Groups ++ Grupper ++ ++ ++ Extras ++ Extra ++ ++ ++ View ++ Vy ++ ++ ++ Quit ++ Avsluta ++ ++ ++ About ++ Om ++ ++ ++ Open database ++ Öppna databas ++ ++ ++ Save database ++ Spara databas ++ ++ ++ Close database ++ Stäng databas ++ ++ ++ New database ++ Ny databas ++ ++ ++ Add new entry ++ Lägg till ny post ++ ++ ++ View/Edit entry ++ Visa/ändra post ++ ++ ++ Delete entry ++ Ta bort post ++ ++ ++ Add new group ++ Lägg till ny grupp ++ ++ ++ Edit group ++ Ändra grupp ++ ++ ++ Delete group ++ Ta bort grupp ++ ++ ++ Save database as ++ Spara databas som ++ ++ ++ Change master key ++ Ändra huvud lösenord ++ ++ ++ Database settings ++ Databasinställningar ++ ++ ++ Import KeePass 1 database ++ Importera KeePass1 databas ++ ++ ++ Clone entry ++ Klona post ++ ++ ++ Find ++ Sök ++ ++ ++ Username ++ Användarnamn ++ ++ ++ Copy username to clipboard ++ Kopiera användarnamn ++ ++ ++ Password ++ Lösenord ++ ++ ++ Copy password to clipboard ++ Kopiera lösenord ++ ++ ++ Settings ++ Inställningar ++ ++ ++ Perform Auto-Type ++ Utför auto-skriv ++ ++ ++ Open URL ++ Öppna URL ++ ++ ++ Lock databases ++ LÃ¥s databaser ++ ++ ++ Title ++ Titel ++ ++ ++ URL ++ URL ++ ++ ++ Notes ++ Anteckningar ++ ++ ++ Show toolbar ++ Visa verktygsfält ++ ++ ++ read-only ++ läs bara ++ ++ ++ ++ PasswordGeneratorWidget ++ ++ Password: ++ Lösenord: ++ ++ ++ Length: ++ Längd: ++ ++ ++ Character Types ++ Teckentyper ++ ++ ++ Upper Case Letters ++ Versaler ++ ++ ++ Lower Case Letters ++ Gemener ++ ++ ++ Numbers ++ Siffror ++ ++ ++ Special Characters ++ Specialtecken ++ ++ ++ Exclude look-alike characters ++ Uteslut liknande tecken ++ ++ ++ Ensure that the password contains characters from every group ++ Säkerställ att lösenordet innehÃ¥ller tecken frÃ¥n varje grupp ++ ++ ++ Accept ++ Acceptera ++ ++ ++ ++ QCommandLineParser ++ ++ Displays version information. ++ Visar versionsinformation. ++ ++ ++ Displays this help. ++ Visa denna hjälp. ++ ++ ++ Unknown option '%1'. ++ Okänt alternativ: '%1' ++ ++ ++ Unknown options: %1. ++ Okända alternativ: '%1' ++ ++ ++ Missing value after '%1'. ++ Saknar värde efter '%1' ++ ++ ++ Unexpected value after '%1'. ++ Oväntat värde efter '%1' ++ ++ ++ [options] ++ [alternativ] ++ ++ ++ Usage: %1 ++ Användning: %1 ++ ++ ++ Options: ++ Alternativ: ++ ++ ++ Arguments: ++ Argument: ++ ++ ++ ++ QSaveFile ++ ++ Existing file %1 is not writable ++ Den existerande filen %1 är inte skrivbar ++ ++ ++ Writing canceled by application ++ Skrivning avbruten av applikation ++ ++ ++ Partial write. Partition full? ++ Delvis skrivet. Är partitionen full? ++ ++ ++ ++ QtIOCompressor ++ ++ Internal zlib error when compressing: ++ Internt zlib fel vid komprimering: ++ ++ ++ Error writing to underlying device: ++ Fel vid skrivning till underliggande enhet: ++ ++ ++ Error opening underlying device: ++ Fel vid öppning av underliggande enhet: ++ ++ ++ Error reading data from underlying device: ++ Fel vid läsning frÃ¥n underliggande enhet: ++ ++ ++ Internal zlib error when decompressing: ++ Internt zlib fel vid extrahering: ++ ++ ++ ++ QtIOCompressor::open ++ ++ The gzip format not supported in this version of zlib. ++ Gzip formatet stöds inte av denna version av zlib. ++ ++ ++ Internal zlib error: ++ Internt zlib fel: ++ ++ ++ ++ SearchWidget ++ ++ Find: ++ Sök: ++ ++ ++ Case sensitive ++ Skiftlägeskänslig ++ ++ ++ Current group ++ Nuvarande grupp ++ ++ ++ Root group ++ Root grupp ++ ++ ++ ++ SettingsWidget ++ ++ Application Settings ++ Applikationsinställningar ++ ++ ++ General ++ Allmän ++ ++ ++ Security ++ Säkerhet ++ ++ ++ ++ SettingsWidgetGeneral ++ ++ Remember last databases ++ KomihÃ¥g senaste databasen ++ ++ ++ Open previous databases on startup ++ Öppna senaste databasen är programmet startar ++ ++ ++ Mark as modified on expanded state changes ++ Markera som ändrad när utökat läge ändras ++ ++ ++ Automatically save on exit ++ Spara automatiskt är applikationen anslutas ++ ++ ++ Automatically save after every change ++ Spara automatiskt efter varje ändring ++ ++ ++ Minimize when copying to clipboard ++ Minimera vid kopiering ++ ++ ++ Use group icon on entry creation ++ Använd gruppens ikon för nya poster ++ ++ ++ Global Auto-Type shortcut ++ Globalt auto-skriv kortkommando ++ ++ ++ Use entry title to match windows for global auto-type ++ Använda postens titel till matchning med fönster för globalt auto-skriv ++ ++ ++ ++ SettingsWidgetSecurity ++ ++ Clear clipboard after ++ Rensa urklipp efter ++ ++ ++ sec ++ sek ++ ++ ++ Lock databases after inactivity of ++ LÃ¥s databaser efter inaktivitet i ++ ++ ++ Show passwords in cleartext by default ++ Visa lösenord i klartext som standard ++ ++ ++ Always ask before performing auto-type ++ FrÃ¥ga alltid innan auto-skriv utförs ++ ++ ++ ++ UnlockDatabaseWidget ++ ++ Unlock database ++ LÃ¥s upp databas ++ ++ ++ Error ++ Fel ++ ++ ++ Wrong key. ++ Fel lösenord ++ ++ ++ ++ WelcomeWidget ++ ++ Welcome! ++ Välkommen! ++ ++ ++ ++ main ++ ++ KeePassX - cross-platform password manager ++ KeePassX - plattformsoberoende lösenordshanterare ++ ++ ++ filename of the password database to open (*.kdbx) ++ namn pÃ¥ databas fil att öppna (*.kdbx) ++ ++ ++ path to a custom config file ++ Sökväg till egen konfigurations-fil ++ ++ ++ password of the database (DANGEROUS!) ++ lösenord för databasen (FARLIGT!) ++ ++ ++ key file of the database ++ nyckel-fil för databas ++ ++ ++ +\ No newline at end of file +diff --git a/share/translations/update.sh b/share/translations/update.sh +new file mode 100755 +index 0000000..6828dc8 +--- /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 d57153e..7ffc168 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 +@@ -47,7 +48,9 @@ set(keepassx_SOURCES + core/SignalMultiplexer.cpp + core/TimeDelta.cpp + core/TimeInfo.cpp ++ core/ToDbExporter.cpp + core/Tools.cpp ++ core/Translator.cpp + core/Uuid.cpp + core/qcommandlineoption.cpp + core/qcommandlineparser.cpp +@@ -73,6 +76,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 +@@ -154,6 +158,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/autotype/AutoType.cpp b/src/autotype/AutoType.cpp +index 5c28b4d..aac0c0c 100644 +--- a/src/autotype/AutoType.cpp ++++ b/src/autotype/AutoType.cpp +@@ -23,12 +23,14 @@ + #include "autotype/AutoTypePlatformPlugin.h" + #include "autotype/AutoTypeSelectDialog.h" + #include "autotype/WildcardMatcher.h" ++#include "core/Config.h" + #include "core/Database.h" + #include "core/Entry.h" + #include "core/FilePath.h" + #include "core/Group.h" + #include "core/ListDeleter.h" + #include "core/Tools.h" ++#include "gui/MessageBox.h" + + AutoType* AutoType::m_instance = Q_NULLPTR; + +@@ -188,8 +190,12 @@ void AutoType::performGlobalAutoType(const QList& dbList) + + if (entryList.isEmpty()) { + m_inAutoType = false; ++ 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) { ++ else if ((entryList.size() == 1) && !config()->get("security/autotypeask").toBool()) { + m_inAutoType = false; + performAutoType(entryList.first(), Q_NULLPTR, sequenceHash[entryList.first()]); + } +@@ -499,6 +505,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/autotype/x11/AutoTypeX11.cpp b/src/autotype/x11/AutoTypeX11.cpp +index 06a1e32..843fbfa 100644 +--- a/src/autotype/x11/AutoTypeX11.cpp ++++ b/src/autotype/x11/AutoTypeX11.cpp +@@ -209,23 +209,26 @@ QString AutoTypePlatformX11::windowTitle(Window window, bool useBlacklist) + unsigned long after; + unsigned char* data = Q_NULLPTR; + ++ // the window manager spec says we should read _NET_WM_NAME first, then fall back to WM_NAME ++ + int retVal = XGetWindowProperty(m_dpy, window, m_atomNetWmName, 0, 1000, false, m_atomUtf8String, + &type, &format, &nitems, &after, &data); + +- if (retVal != 0 && data) { ++ if ((retVal == 0) && data) { + title = QString::fromUtf8(reinterpret_cast(data)); + } + else { + XTextProperty textProp; + retVal = XGetTextProperty(m_dpy, window, &textProp, m_atomWmName); +- if (retVal != 0 && textProp.value) { ++ if ((retVal != 0) && textProp.value) { + char** textList = Q_NULLPTR; + int count; + + if (textProp.encoding == m_atomUtf8String) { + title = QString::fromUtf8(reinterpret_cast(textProp.value)); + } +- else if (XmbTextPropertyToTextList(m_dpy, &textProp, &textList, &count) == 0 && textList && count > 0) { ++ else if ((XmbTextPropertyToTextList(m_dpy, &textProp, &textList, &count) == 0) ++ && textList && (count > 0)) { + title = QString::fromLocal8Bit(textList[0]); + } + else if (textProp.encoding == m_atomString) { +diff --git a/src/config-keepassx.h.cmake b/src/config-keepassx.h.cmake +index 9a3f495..805700a 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 3cb7634..03b5129 100644 +--- a/src/core/Config.cpp ++++ b/src/core/Config.cpp +@@ -71,7 +71,8 @@ Config::Config(QObject* parent) + userPath += "/keepassx/"; + #else + userPath = QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); +- // storageLocation() appends the application name ("/keepassx/") to the end ++ // storageLocation() appends the application name ("/keepassx") to the end ++ userPath += "/"; + #endif + + userPath += "keepassx2.ini"; +@@ -94,11 +95,17 @@ void Config::init(const QString& fileName) + m_defaults.insert("AutoSaveOnExit", false); + m_defaults.insert("ShowToolbar", true); + m_defaults.insert("MinimizeOnCopy", false); ++ m_defaults.insert("UseGroupIconOnEntryCreation", 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", true); ++ m_defaults.insert("GUI/Language", "system"); ++ m_defaults.insert("GUI/ShowTrayIcon", false); ++ m_defaults.insert("GUI/MinimizeToTray", false); + } + + Config* Config::instance() +@@ -110,7 +117,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 ee30826..ca0f74c 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/core/Database.cpp b/src/core/Database.cpp +index 0394051..4c888ea 100644 +--- a/src/core/Database.cpp ++++ b/src/core/Database.cpp +@@ -37,7 +37,7 @@ Database::Database() + { + m_data.cipher = KeePass2::CIPHER_AES; + m_data.compressionAlgo = CompressionGZip; +- m_data.transformRounds = 50000; ++ m_data.transformRounds = 100000; + m_data.hasKey = false; + + setRootGroup(new Group()); +diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp +index 55f5432..4f97791 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 c2c2938..ae07ed4 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 +new file mode 100644 +index 0000000..82a553e +--- /dev/null ++++ b/src/core/EntrySearcher.cpp +@@ -0,0 +1,65 @@ ++/* ++ * 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()) { ++ searchResult.append(matchEntry(searchTerm, entry, caseSensitivity)); ++ } ++ Q_FOREACH (Group* childGroup, group->children()) { ++ if (childGroup->searchingEnabled() != Group::Disable) { ++ searchResult.append(searchEntries(searchTerm, childGroup, caseSensitivity)); ++ } ++ } ++ ++ 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 +new file mode 100644 +index 0000000..246538c +--- /dev/null ++++ b/src/core/EntrySearcher.h +@@ -0,0 +1,37 @@ ++/* ++ * 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); ++ QList matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity); ++ bool wordMatch(const QString &word, Entry *entry, Qt::CaseSensitivity caseSensitivity); ++}; ++ ++#endif // KEEPASSX_ENTRYSEARCHER_H +diff --git a/src/core/Exporter.h b/src/core/Exporter.h +new file mode 100644 +index 0000000..dedb1c8 +--- /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/Group.cpp b/src/core/Group.cpp +index ada9e97..517f8cb 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); +@@ -612,37 +596,35 @@ void Group::recCreateDelObjects() + } + } + +-QList Group::search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity, +- bool resolveInherit) ++bool Group::resolveSearchingEnabled() const + { +- QList searchResult; +- if (includeInSearch(resolveInherit)) { +- Q_FOREACH (Entry* entry, m_entries) { +- if (entry->match(searchTerm, caseSensitivity)) { +- searchResult.append(entry); +- } ++ switch (m_data.searchingEnabled) { ++ case Inherit: ++ if (!m_parent) { ++ return true; + } +- Q_FOREACH (Group* group, m_children) { +- searchResult.append(group->search(searchTerm, caseSensitivity, false)); ++ else { ++ return m_parent->resolveSearchingEnabled(); + } ++ case Enable: ++ return true; ++ case Disable: ++ return false; ++ default: ++ Q_ASSERT(false); ++ return false; + } +- return searchResult; + } + +-bool Group::includeInSearch(bool resolveInherit) ++bool Group::resolveAutoTypeEnabled() const + { +- switch (m_data.searchingEnabled) { ++ switch (m_data.autoTypeEnabled) { + case Inherit: + if (!m_parent) { + return true; + } + else { +- if (resolveInherit) { +- return m_parent->includeInSearch(true); +- } +- else { +- return true; +- } ++ return m_parent->resolveAutoTypeEnabled(); + } + case Enable: + return true; +diff --git a/src/core/Group.h b/src/core/Group.h +index 558bebc..7391f88 100644 +--- a/src/core/Group.h ++++ b/src/core/Group.h +@@ -65,6 +65,8 @@ public: + QString defaultAutoTypeSequence() const; + Group::TriState autoTypeEnabled() const; + Group::TriState searchingEnabled() const; ++ bool resolveSearchingEnabled() const; ++ bool resolveAutoTypeEnabled() const; + Entry* lastTopVisibleEntry() const; + bool isExpired() const; + +@@ -109,10 +111,6 @@ public: + */ + Group* clone(Entry::CloneFlags entryFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo) const; + void copyDataFrom(const Group* other); +- Database* exportToDb(); +- +- QList search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity, +- bool resolveInherit = true); + + Q_SIGNALS: + void dataChanged(Group* group); +@@ -147,7 +145,6 @@ private: + void cleanupParent(); + void recCreateDelObjects(); + void updateTimeinfo(); +- bool includeInSearch(bool resolveInherit); + + QPointer m_db; + Uuid m_uuid; +diff --git a/src/core/ToDbExporter.cpp b/src/core/ToDbExporter.cpp +new file mode 100644 +index 0000000..1f76fb7 +--- /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::exportGroup(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 0000000..58c5efe +--- /dev/null ++++ b/src/core/ToDbExporter.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_TODBEXPORTER_H ++#define KEEPASSX_TODBEXPORTER_H ++ ++#include "core/Exporter.h" ++ ++class Database; ++class Group; ++ ++class ToDbExporter : Exporter ++{ ++public: ++ Database* exportGroup(Group* group); ++}; ++ ++#endif // KEEPASSX_TODBEXPORTER_H +diff --git a/src/core/Translator.cpp b/src/core/Translator.cpp +new file mode 100644 +index 0000000..bc4d2b6 +--- /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 0000000..4bc4fca +--- /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/crypto/Crypto.cpp b/src/crypto/Crypto.cpp +index 1e28002..13c3c20 100644 +--- a/src/crypto/Crypto.cpp ++++ b/src/crypto/Crypto.cpp +@@ -21,7 +21,12 @@ + + #include + ++#include "config-keepassx.h" ++#include "crypto/CryptoHash.h" ++#include "crypto/SymmetricCipher.h" ++ + bool Crypto::m_initalized(false); ++QString Crypto::m_errorStr; + + #if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600) + static int gcry_qt_mutex_init(void** p_sys) +@@ -64,11 +69,11 @@ Crypto::Crypto() + { + } + +-void Crypto::init() ++bool Crypto::init() + { + if (m_initalized) { + qWarning("Crypto::init: already initalized"); +- return; ++ return true; + } + + // libgcrypt >= 1.6 doesn't allow custom thread callbacks anymore. +@@ -78,7 +83,19 @@ void Crypto::init() + gcry_check_version(0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + ++ if (!checkAlgorithms()) { ++ return false; ++ } ++ ++ // has to be set before testing Crypto classes + m_initalized = true; ++ ++ if (!selfTest()) { ++ m_initalized = false; ++ return false; ++ } ++ ++ return true; + } + + bool Crypto::initalized() +@@ -86,7 +103,89 @@ bool Crypto::initalized() + return m_initalized; + } + +-bool Crypto::selfTest() ++QString Crypto::errorString() ++{ ++ return m_errorStr; ++} ++ ++bool Crypto::backendSelfTest() + { + return (gcry_control(GCRYCTL_SELFTEST) == 0); + } ++ ++bool Crypto::checkAlgorithms() ++{ ++ if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) { ++ m_errorStr = "GCRY_CIPHER_AES256 not found."; ++ qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); ++ return false; ++ } ++ if (gcry_cipher_algo_info(GCRY_CIPHER_TWOFISH, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) { ++ m_errorStr = "GCRY_CIPHER_TWOFISH not found."; ++ qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); ++ return false; ++ } ++#ifdef GCRYPT_HAS_SALSA20 ++ if (gcry_cipher_algo_info(GCRY_CIPHER_SALSA20, GCRYCTL_TEST_ALGO, Q_NULLPTR, Q_NULLPTR) != 0) { ++ m_errorStr = "GCRY_CIPHER_SALSA20 not found."; ++ qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); ++ return false; ++ } ++#endif ++ if (gcry_md_test_algo(GCRY_MD_SHA256) != 0) { ++ m_errorStr = "GCRY_MD_SHA256 not found."; ++ qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); ++ return false; ++ } ++ ++ return true; ++} ++ ++bool Crypto::selfTest() ++{ ++ QByteArray sha256Test = CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", ++ CryptoHash::Sha256); ++ ++ if (sha256Test != QByteArray::fromHex("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")) { ++ m_errorStr = "SHA-256 mismatch."; ++ qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); ++ return false; ++ } ++ ++ QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); ++ QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); ++ QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); ++ plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); ++ QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6"); ++ cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d")); ++ ++ SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt, key, iv); ++ if (aes256Encrypt.process(plainText) != cipherText) { ++ m_errorStr = "AES-256 encryption mismatch."; ++ qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); ++ return false; ++ } ++ ++ SymmetricCipher aes256Descrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt, key, iv); ++ if (aes256Descrypt.process(cipherText) != plainText) { ++ m_errorStr = "AES-256 decryption mismatch."; ++ qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); ++ return false; ++ } ++ ++ QByteArray salsa20Key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112"); ++ QByteArray salsa20iv = QByteArray::fromHex("0000000000000000"); ++ QByteArray salsa20Plain = QByteArray::fromHex("00000000000000000000000000000000"); ++ QByteArray salsa20Cipher = QByteArray::fromHex("B4C0AFA503BE7FC29A62058166D56F8F"); ++ ++ SymmetricCipher salsa20Stream(SymmetricCipher::Salsa20, SymmetricCipher::Stream, ++ SymmetricCipher::Encrypt, salsa20Key, salsa20iv); ++ ++ if (salsa20Stream.process(salsa20Plain) != salsa20Cipher) { ++ m_errorStr = "Salsa20 stream cipher mismatch."; ++ qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); ++ return false; ++ } ++ ++ return true; ++} +diff --git a/src/crypto/Crypto.h b/src/crypto/Crypto.h +index 63f1177..9926f14 100644 +--- a/src/crypto/Crypto.h ++++ b/src/crypto/Crypto.h +@@ -18,18 +18,25 @@ + #ifndef KEEPASSX_CRYPTO_H + #define KEEPASSX_CRYPTO_H + ++#include ++ + #include "core/Global.h" + + class Crypto + { + public: +- static void init(); ++ static bool init(); + static bool initalized(); +- static bool selfTest(); ++ static bool backendSelfTest(); ++ static QString errorString(); + + private: + Crypto(); ++ static bool checkAlgorithms(); ++ static bool selfTest(); ++ + static bool m_initalized; ++ static QString m_errorStr; + }; + + #endif // KEEPASSX_CRYPTO_H +diff --git a/src/gui/Clipboard.cpp b/src/gui/Clipboard.cpp +index eb77d2b..7d8f71f 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 bc2a19d..8b6ea69 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() { +diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp +index 9731925..37bbce7 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/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp +index 8c2ba06..7f99846 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())); + } + +@@ -189,7 +193,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?"), +@@ -503,7 +507,7 @@ DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget() + } + } + +-bool DatabaseTabWidget::hasLockableDatabases() ++bool DatabaseTabWidget::hasLockableDatabases() const + { + QHashIterator i(m_dbList); + while (i.hasNext()) { +@@ -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 9261a06..25d34f3 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; + +@@ -53,7 +54,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; + +@@ -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 56ab8b6..cc9c5fd 100644 +--- a/src/gui/DatabaseWidget.cpp ++++ b/src/gui/DatabaseWidget.cpp +@@ -28,7 +28,9 @@ + + #include "autotype/AutoType.h" + #include "core/Config.h" ++#include "core/EntrySearcher.h" + #include "core/FilePath.h" ++#include "core/Group.h" + #include "core/Metadata.h" + #include "core/Tools.h" + #include "gui/ChangeMasterKeyWidget.h" +@@ -59,12 +61,13 @@ 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); ++ m_splitter->setChildrenCollapsible(false); + +- 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)), +@@ -77,14 +80,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); +@@ -100,10 +95,17 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) + + rightHandSideWidget->setLayout(vLayout); + +- splitter->addWidget(m_groupView); +- splitter->addWidget(rightHandSideWidget); ++ setTabOrder(m_searchUi->searchRootRadioButton, m_entryView); ++ setTabOrder(m_entryView, m_groupView); ++ setTabOrder(m_groupView, m_searchWidget); ++ ++ m_splitter->addWidget(m_groupView); ++ m_splitter->addWidget(rightHandSideWidget); + +- layout->addWidget(splitter); ++ m_splitter->setStretchFactor(0, 30); ++ m_splitter->setStretchFactor(1, 70); ++ ++ layout->addWidget(m_splitter); + m_mainWidget->setLayout(layout); + + m_editEntryWidget = new EditEntryWidget(); +@@ -135,6 +137,8 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) + addWidget(m_keepass1OpenWidget); + 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*))); +@@ -166,7 +170,7 @@ DatabaseWidget::~DatabaseWidget() + { + } + +-DatabaseWidget::Mode DatabaseWidget::currentMode() ++DatabaseWidget::Mode DatabaseWidget::currentMode() const + { + if (currentWidget() == Q_NULLPTR) { + return DatabaseWidget::None; +@@ -182,19 +186,54 @@ DatabaseWidget::Mode DatabaseWidget::currentMode() + } + } + +-void DatabaseWidget::emitCurrentModeChanged() ++bool DatabaseWidget::isInEditMode() const + { +- Q_EMIT currentModeChanged(currentMode()); ++ if (currentMode() == DatabaseWidget::LockedMode) { ++ return m_widgetBeforeLock != Q_NULLPTR ++ && m_widgetBeforeLock != m_mainWidget ++ && m_widgetBeforeLock != m_unlockDatabaseWidget; ++ } ++ else { ++ return currentMode() == DatabaseWidget::EditMode; ++ } + } + +-GroupView* DatabaseWidget::groupView() ++QList DatabaseWidget::splitterSizes() const + { +- return m_groupView; ++ return m_splitter->sizes(); + } + +-EntryView* DatabaseWidget::entryView() ++void DatabaseWidget::setSplitterSizes(const QList& sizes) + { +- return m_entryView; ++ 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()); + } + + Database* DatabaseWidget::database() +@@ -213,9 +252,28 @@ void DatabaseWidget::createEntry() + m_newEntry->setUuid(Uuid::random()); + m_newEntry->setUsername(m_db->metadata()->defaultUserName()); + m_newParent = m_groupView->currentGroup(); ++ setIconFromParent(); + switchToEntryEdit(m_newEntry, true); + } + ++void DatabaseWidget::setIconFromParent() ++{ ++ if (!config()->get("UseGroupIconOnEntryCreation").toBool()) { ++ return; ++ } ++ ++ if (m_newParent->iconNumber() == Group::DefaultIconNumber && m_newParent->iconUuid().isNull()) { ++ return; ++ } ++ ++ if (m_newParent->iconUuid().isNull()) { ++ m_newEntry->setIcon(m_newParent->iconNumber()); ++ } ++ else { ++ m_newEntry->setIcon(m_newParent->iconUuid()); ++ } ++} ++ + void DatabaseWidget::cloneEntry() + { + Entry* currentEntry = m_entryView->currentEntry(); +@@ -274,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; +@@ -407,7 +464,7 @@ void DatabaseWidget::createGroup() + void DatabaseWidget::deleteGroup() + { + Group* currentGroup = m_groupView->currentGroup(); +- if (!currentGroup || !canDeleteCurrentGoup()) { ++ if (!currentGroup || !canDeleteCurrentGroup()) { + Q_ASSERT(false); + return; + } +@@ -575,7 +632,7 @@ void DatabaseWidget::unlockDatabase(bool accepted) + Q_ASSERT(accepted); + Q_UNUSED(accepted); + +- setCurrentWidget(widgetBeforeLock); ++ setCurrentWidget(m_widgetBeforeLock); + Q_EMIT unlockedDatabase(); + } + +@@ -647,8 +704,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(); +@@ -658,11 +723,19 @@ void DatabaseWidget::toggleSearch() + void DatabaseWidget::closeSearch() + { + Q_ASSERT(m_lastGroup); ++ ++ Q_EMIT listModeAboutToActivate(); ++ + m_groupView->setCurrentGroup(m_lastGroup); ++ m_searchTimer->stop(); ++ ++ Q_EMIT listModeActivated(); + } + + void DatabaseWidget::showSearch() + { ++ Q_EMIT searchModeAboutToActivate(); ++ + m_searchUi->searchEdit->blockSignals(true); + m_searchUi->searchEdit->clear(); + m_searchUi->searchEdit->blockSignals(false); +@@ -696,6 +769,8 @@ void DatabaseWidget::showSearch() + m_searchWidget->show(); + search(); + m_searchUi->searchEdit->setFocus(); ++ ++ Q_EMIT searchModeActivated(); + } + + void DatabaseWidget::search() +@@ -721,8 +796,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); + } +@@ -753,19 +828,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::canDeleteCurrentGroup() 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(); + } +@@ -782,7 +857,7 @@ void DatabaseWidget::lock() + { + Q_ASSERT(currentMode() != DatabaseWidget::LockedMode); + +- widgetBeforeLock = currentWidget(); ++ m_widgetBeforeLock = currentWidget(); + m_unlockDatabaseWidget->load(m_filename, m_db); + setCurrentWidget(m_unlockDatabaseWidget); + } +@@ -791,3 +866,23 @@ void DatabaseWidget::updateFilename(const QString& fileName) + { + m_filename = fileName; + } ++ ++int DatabaseWidget::numberOfSelectedEntries() const ++{ ++ return m_entryView->numberOfSelectedEntries(); ++} ++ ++QStringList DatabaseWidget::customEntryAttributes() const ++{ ++ Entry* entry = m_entryView->currentEntry(); ++ if (!entry) { ++ return QStringList(); ++ } ++ ++ return entry->attributes()->customKeys(); ++} ++ ++bool DatabaseWidget::isGroupSelected() const ++{ ++ return m_groupView->currentGroup() != Q_NULLPTR; ++} +diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h +index 240fe28..cbab175 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 { +@@ -59,18 +60,24 @@ public: + + explicit DatabaseWidget(Database* db, QWidget* parent = Q_NULLPTR); + ~DatabaseWidget(); +- GroupView* groupView(); +- EntryView* entryView(); + Database* database(); +- bool dbHasKey(); +- bool canDeleteCurrentGoup(); +- bool isInSearchMode(); ++ bool dbHasKey() const; ++ bool canDeleteCurrentGroup() const; ++ bool isInSearchMode() const; + 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() const; ++ QStringList customEntryAttributes() const; ++ bool isGroupSelected() const; ++ bool isInEditMode() const; ++ QList splitterSizes() const; ++ void setSplitterSizes(const QList& sizes); ++ QList entryHeaderViewSizes() const; ++ void setEntryViewHeaderSizes(const QList& sizes); + + Q_SIGNALS: + void closeRequest(); +@@ -81,6 +88,12 @@ 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(); + + public Q_SLOTS: + void createEntry(); +@@ -105,8 +118,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); +@@ -116,6 +127,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); +@@ -129,6 +142,7 @@ private Q_SLOTS: + + private: + void setClipboardTextAndMinimize(const QString& text); ++ void setIconFromParent(); + + Database* m_db; + const QScopedPointer m_searchUi; +@@ -142,6 +156,7 @@ private: + DatabaseOpenWidget* m_databaseOpenWidget; + KeePass1OpenWidget* m_keepass1OpenWidget; + UnlockDatabaseWidget* m_unlockDatabaseWidget; ++ QSplitter* m_splitter; + GroupView* m_groupView; + EntryView* m_entryView; + Group* m_newGroup; +@@ -149,7 +164,7 @@ private: + Group* m_newParent; + Group* m_lastGroup; + QTimer* m_searchTimer; +- QWidget* widgetBeforeLock; ++ QWidget* m_widgetBeforeLock; + QString m_filename; + }; + +diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp +new file mode 100644 +index 0000000..66b8492 +--- /dev/null ++++ b/src/gui/DatabaseWidgetStateSync.cpp +@@ -0,0 +1,154 @@ ++/* ++ * 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) ++ , m_blockUpdates(false) ++{ ++ m_splitterSizes = variantToIntList(config()->get("GUI/SplitterState")); ++ m_columnSizesList = variantToIntList(config()->get("GUI/EntryListColumnSizes")); ++ m_columnSizesSearch = variantToIntList(config()->get("GUI/EntrySearchColumnSizes")); ++} ++ ++DatabaseWidgetStateSync::~DatabaseWidgetStateSync() ++{ ++ 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) ++{ ++ if (m_activeDbWidget) { ++ disconnect(m_activeDbWidget, 0, this, 0); ++ } ++ ++ m_activeDbWidget = dbWidget; ++ ++ if (m_activeDbWidget) { ++ m_blockUpdates = true; ++ ++ if (!m_splitterSizes.isEmpty()) { ++ m_activeDbWidget->setSplitterSizes(m_splitterSizes); ++ } ++ ++ if (m_activeDbWidget->isGroupSelected()) { ++ restoreListView(); ++ } ++ else { ++ restoreSearchView(); ++ } ++ ++ m_blockUpdates = false; ++ ++ 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())); ++ connect(m_activeDbWidget, SIGNAL(listModeAboutToActivate()), ++ SLOT(blockUpdates())); ++ connect(m_activeDbWidget, SIGNAL(searchModeAboutToActivate()), ++ SLOT(blockUpdates())); ++ } ++} ++ ++void DatabaseWidgetStateSync::restoreListView() ++{ ++ if (!m_columnSizesList.isEmpty()) { ++ m_activeDbWidget->setEntryViewHeaderSizes(m_columnSizesList); ++ } ++ ++ m_blockUpdates = false; ++} ++ ++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(); ++ } ++ 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 +new file mode 100644 +index 0000000..f6a87cd +--- /dev/null ++++ b/src/gui/DatabaseWidgetStateSync.h +@@ -0,0 +1,54 @@ ++/* ++ * 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); ++ void restoreListView(); ++ void restoreSearchView(); ++ ++private Q_SLOTS: ++ void blockUpdates(); ++ void updateSplitterSizes(); ++ void updateColumnSizes(); ++ ++private: ++ static QList variantToIntList(const QVariant& variant); ++ static QVariant intListToVariant(const QList& list); ++ ++ DatabaseWidget* m_activeDbWidget; ++ ++ bool m_blockUpdates; ++ QList m_splitterSizes; ++ QList m_columnSizesList; ++ QList m_columnSizesSearch; ++}; ++ ++#endif // KEEPASSX_HEADERVIEWSYNC_H +diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp +index 26314d3..9e85745 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/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp +index 5f23d80..96ddf13 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/MainWindow.cpp b/src/gui/MainWindow.cpp +index 36fb656..dd77989 100644 +--- a/src/gui/MainWindow.cpp ++++ b/src/gui/MainWindow.cpp +@@ -23,24 +23,23 @@ + + #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"; + + MainWindow::MainWindow() + : m_ui(new Ui::MainWindow()) ++ , m_trayIcon(Q_NULLPTR) + { + m_ui->setupUi(this); + +- restoreGeometry(config()->get("window/Geometry").toByteArray()); ++ m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size(); ++ ++ restoreGeometry(config()->get("GUI/MainWindowGeometry").toByteArray()); + + setWindowIcon(filePath()->applicationIcon()); + QAction* toggleViewAction = m_ui->toolBar->toggleViewAction(); +@@ -203,6 +202,8 @@ MainWindow::MainWindow() + + m_actionMultiplexer.connect(m_ui->actionSearch, SIGNAL(triggered()), + SLOT(toggleSearch())); ++ ++ updateTrayIcon(); + } + + MainWindow::~MainWindow() +@@ -229,17 +230,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; 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 +276,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); +@@ -294,7 +294,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); +@@ -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); +@@ -422,15 +432,29 @@ void MainWindow::closeEvent(QCloseEvent* event) + saveWindowInformation(); + + event->accept(); ++ QApplication::quit(); + } + else { + event->ignore(); + } + } + ++void MainWindow::changeEvent(QEvent *event) ++{ ++ if ((event->type() == QEvent::WindowStateChange) && isMinimized() ++ && isTrayIconEnabled() && config()->get("GUI/MinimizeToTray").toBool()) ++ { ++ event->ignore(); ++ hide(); ++ } ++ else { ++ QMainWindow::changeEvent(event); ++ } ++} ++ + void MainWindow::saveWindowInformation() + { +- config()->set("window/Geometry", saveGeometry()); ++ config()->set("GUI/MainWindowGeometry", saveGeometry()); + } + + bool MainWindow::saveLastDatabases() +@@ -460,6 +484,35 @@ bool MainWindow::saveLastDatabases() + return accept; + } + ++void MainWindow::updateTrayIcon() ++{ ++ if (isTrayIconEnabled()) { ++ if (!m_trayIcon) { ++ m_trayIcon = new QSystemTrayIcon(filePath()->applicationIcon(), this); ++ ++ QMenu* menu = new QMenu(this); ++ ++ QAction* actionToggle = new QAction(tr("Toggle window"), menu); ++ menu->addAction(actionToggle); ++ ++ menu->addAction(m_ui->actionQuit); ++ ++ connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), ++ SLOT(trayIconTriggered(QSystemTrayIcon::ActivationReason))); ++ connect(actionToggle, SIGNAL(triggered()), SLOT(toggleWindow())); ++ ++ m_trayIcon->setContextMenu(menu); ++ m_trayIcon->show(); ++ } ++ } ++ else { ++ if (m_trayIcon) { ++ delete m_trayIcon; ++ m_trayIcon = Q_NULLPTR; ++ } ++ } ++} ++ + void MainWindow::showEntryContextMenu(const QPoint& globalPos) + { + m_ui->menuEntries->popup(globalPos); +@@ -504,4 +557,31 @@ void MainWindow::applySettingsChanges() + else { + m_inactivityTimer->deactivate(); + } ++ ++ updateTrayIcon(); ++} ++ ++void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason) ++{ ++ if (reason == QSystemTrayIcon::Trigger) { ++ toggleWindow(); ++ } ++} ++ ++void MainWindow::toggleWindow() ++{ ++ if (QApplication::activeWindow() == this) { ++ hide(); ++ } ++ else { ++ show(); ++ raise(); ++ activateWindow(); ++ } ++} ++ ++bool MainWindow::isTrayIconEnabled() const ++{ ++ return config()->get("GUI/ShowTrayIcon").toBool() ++ && QSystemTrayIcon::isSystemTrayAvailable(); + } +diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h +index 706fd2d..b966703 100644 +--- a/src/gui/MainWindow.h ++++ b/src/gui/MainWindow.h +@@ -20,6 +20,7 @@ + + #include + #include ++#include + + #include "core/SignalMultiplexer.h" + #include "gui/DatabaseWidget.h" +@@ -44,6 +45,7 @@ public Q_SLOTS: + + protected: + void closeEvent(QCloseEvent* event) Q_DECL_OVERRIDE; ++ void changeEvent(QEvent* event) Q_DECL_OVERRIDE; + + private Q_SLOTS: + void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::None); +@@ -61,6 +63,8 @@ private Q_SLOTS: + void saveToolbarState(bool value); + void rememberOpenDatabases(const QString& filePath); + void applySettingsChanges(); ++ void trayIconTriggered(QSystemTrayIcon::ActivationReason reason); ++ void toggleWindow(); + + private: + static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); +@@ -69,6 +73,8 @@ private: + + void saveWindowInformation(); + bool saveLastDatabases(); ++ void updateTrayIcon(); ++ bool isTrayIconEnabled() const; + + const QScopedPointer m_ui; + SignalMultiplexer m_actionMultiplexer; +@@ -77,6 +83,8 @@ private: + QActionGroup* m_copyAdditionalAttributeActions; + QStringList m_openDatabases; + InactivityTimer* m_inactivityTimer; ++ int m_countDefaultAttributes; ++ QSystemTrayIcon* m_trayIcon; + + Q_DISABLE_COPY(MainWindow) + }; +diff --git a/src/gui/PasswordComboBox.cpp b/src/gui/PasswordComboBox.cpp +index af8e994..f11311a 100644 +--- a/src/gui/PasswordComboBox.cpp ++++ b/src/gui/PasswordComboBox.cpp +@@ -45,7 +45,13 @@ void PasswordComboBox::setEcho(bool echo) + // add fake item to show visual indication that a popup is available + addItem(""); + +- setStyleSheet("QComboBox { font-family: monospace; }"); ++#ifdef Q_OS_MAC ++ // Qt on Mac OS doesn't seem to know the generic monospace family (tested with 4.8.6) ++ setStyleSheet("QComboBox { font-family: monospace,Menlo,Monaco; }"); ++#else ++ setStyleSheet("QComboBox { font-family: monospace,Courier; }"); ++#endif ++ + } + else { + // clear items so the combobox indicates that no popup menu is available +diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp +index 8532696..b68eef6 100644 +--- a/src/gui/PasswordEdit.cpp ++++ b/src/gui/PasswordEdit.cpp +@@ -56,7 +56,12 @@ void PasswordEdit::updateStylesheet() + QString stylesheet("QLineEdit { "); + + if (echoMode() == QLineEdit::Normal) { ++#ifdef Q_OS_MAC ++ // Qt on Mac OS doesn't seem to know the generic monospace family (tested with 4.8.6) ++ stylesheet.append("font-family: monospace,Menlo,Monaco; "); ++#else + stylesheet.append("font-family: monospace; "); ++#endif + } + + if (m_basePasswordEdit && !passwordsEqual()) { +diff --git a/src/gui/PasswordGeneratorWidget.ui b/src/gui/PasswordGeneratorWidget.ui +index 342f191..5c75ef9 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 +diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui +index c3d59b8..ce4845d 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 + + +diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp +index d8516c6..a7863ea 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) +@@ -46,6 +47,8 @@ SettingsWidget::SettingsWidget(QWidget* parent) + + connect(m_generalUi->autoSaveAfterEveryChangeCheckBox, SIGNAL(toggled(bool)), + this, SLOT(enableAutoSaveOnExit(bool))); ++ connect(m_generalUi->systrayShowCheckBox, SIGNAL(toggled(bool)), ++ m_generalUi->systrayMinimizeToTrayCheckBox, SLOT(setEnabled(bool))); + + connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)), + m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool))); +@@ -66,6 +69,21 @@ void SettingsWidget::loadSettings() + m_generalUi->autoSaveAfterEveryChangeCheckBox->setChecked(config()->get("AutoSaveAfterEveryChange").toBool()); + 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()); ++ ++ m_generalUi->languageComboBox->clear(); ++ 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); ++ } ++ ++ m_generalUi->systrayShowCheckBox->setChecked(config()->get("GUI/ShowTrayIcon").toBool()); ++ m_generalUi->systrayMinimizeToTrayCheckBox->setChecked(config()->get("GUI/MinimizeToTray").toBool()); + + if (autoType()->isAvailable()) { + m_globalAutoTypeKey = static_cast(config()->get("GlobalAutoTypeKey").toInt()); +@@ -83,6 +101,8 @@ void SettingsWidget::loadSettings() + + m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool()); + ++ m_secUi->autoTypeAskCheckBox->setChecked(config()->get("security/autotypeask").toBool()); ++ + setCurrentRow(0); + } + +@@ -97,6 +117,16 @@ void SettingsWidget::saveSettings() + m_generalUi->autoSaveAfterEveryChangeCheckBox->isChecked()); + config()->set("AutoSaveOnExit", m_generalUi->autoSaveOnExitCheckBox->isChecked()); + config()->set("MinimizeOnCopy", m_generalUi->minimizeOnCopyCheckBox->isChecked()); ++ config()->set("UseGroupIconOnEntryCreation", ++ 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()); ++ ++ config()->set("GUI/ShowTrayIcon", m_generalUi->systrayShowCheckBox->isChecked()); ++ config()->set("GUI/MinimizeToTray", m_generalUi->systrayMinimizeToTrayCheckBox->isChecked()); ++ + if (autoType()->isAvailable()) { + config()->set("GlobalAutoTypeKey", m_generalUi->autoTypeShortcutWidget->key()); + config()->set("GlobalAutoTypeModifiers", +@@ -110,6 +140,8 @@ void SettingsWidget::saveSettings() + + config()->set("security/passwordscleartext", m_secUi->passwordCleartextCheckBox->isChecked()); + ++ config()->set("security/autotypeask", m_secUi->autoTypeAskCheckBox->isChecked()); ++ + Q_EMIT editFinished(true); + } + +diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/SettingsWidgetGeneral.ui +index b723d18..cbad7e5 100644 +--- a/src/gui/SettingsWidgetGeneral.ui ++++ b/src/gui/SettingsWidgetGeneral.ui +@@ -7,10 +7,13 @@ + 0 + 0 + 456 +- 185 ++ 340 + + + ++ ++ QFormLayout::AllNonFixedFieldsGrow ++ + + + +@@ -21,6 +24,13 @@ + + + ++ ++ ++ ++ Open previous databases on startup ++ ++ ++ + + + +@@ -31,6 +41,13 @@ + + + ++ ++ ++ ++ Automatically save on exit ++ ++ ++ + + + +@@ -38,34 +55,61 @@ + + + +- +- ++ ++ + +- Automatically save on exit ++ Minimize when copying to clipboard + + + + ++ ++ ++ Use group icon on entry creation ++ ++ ++ ++ + + + Global Auto-Type shortcut + + + +- ++ + + +- +- ++ ++ + +- Open previous databases on startup ++ Use entry title to match windows for global auto-type + + + +- +- ++ ++ + +- Minimize when copying to clipboard ++ Language ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Show a system tray icon ++ ++ ++ ++ ++ ++ ++ false ++ ++ ++ Hide window to system tray when minimized + + + +diff --git a/src/gui/SettingsWidgetSecurity.ui b/src/gui/SettingsWidgetSecurity.ui +index c2a6ccc..b52e862 100644 +--- a/src/gui/SettingsWidgetSecurity.ui ++++ b/src/gui/SettingsWidgetSecurity.ui +@@ -64,6 +64,13 @@ + + + ++ ++ ++ ++ Always ask before performing auto-type ++ ++ ++ + + + +diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp +index 4a67433..465f5d4 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; + } + +@@ -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; +diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp +index f71f80b..cd2c6fb 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); +@@ -62,13 +64,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() +@@ -100,9 +113,9 @@ Entry* EntryView::currentEntry() + } + } + +-bool EntryView::isSingleEntrySelected() ++int EntryView::numberOfSelectedEntries() + { +- return (selectionModel()->selectedRows().size() == 1); ++ return selectionModel()->selectedRows().size(); + } + + void EntryView::setCurrentEntry(Entry* entry) +diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h +index b5f056a..c11d041 100644 +--- a/src/gui/entry/EntryView.h ++++ b/src/gui/entry/EntryView.h +@@ -37,11 +37,12 @@ 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); + bool inEntryListMode(); ++ int numberOfSelectedEntries(); ++ void setFirstEntryActive(); + + public Q_SLOTS: + void setGroup(Group* group); +diff --git a/src/gui/group/EditGroupWidget.cpp b/src/gui/group/EditGroupWidget.cpp +index c24afb9..b26fe4c 100644 +--- a/src/gui/group/EditGroupWidget.cpp ++++ b/src/gui/group/EditGroupWidget.cpp +@@ -37,9 +37,6 @@ EditGroupWidget::EditGroupWidget(QWidget* parent) + add(tr("Icon"), m_editGroupWidgetIcons); + add(tr("Properties"), m_editWidgetProperties); + +- addTriStateItems(m_mainUi->searchComboBox); +- addTriStateItems(m_mainUi->autotypeComboBox); +- + connect(m_mainUi->expireCheck, SIGNAL(toggled(bool)), m_mainUi->expireDatePicker, SLOT(setEnabled(bool))); + + connect(this, SIGNAL(accepted()), SLOT(save())); +@@ -62,6 +59,15 @@ void EditGroupWidget::loadGroup(Group* group, bool create, Database* database) + setHeadline(tr("Edit group")); + } + ++ if (m_group->parentGroup()) { ++ addTriStateItems(m_mainUi->searchComboBox, m_group->parentGroup()->resolveSearchingEnabled()); ++ addTriStateItems(m_mainUi->autotypeComboBox, m_group->parentGroup()->resolveAutoTypeEnabled()); ++ } ++ else { ++ addTriStateItems(m_mainUi->searchComboBox, true); ++ addTriStateItems(m_mainUi->autotypeComboBox, true); ++ } ++ + m_mainUi->editName->setText(m_group->name()); + m_mainUi->editNotes->setPlainText(m_group->notes()); + m_mainUi->expireCheck->setChecked(group->timeInfo().expires()); +@@ -120,9 +126,18 @@ void EditGroupWidget::cancel() + Q_EMIT editFinished(false); + } + +-void EditGroupWidget::addTriStateItems(QComboBox* comboBox) ++void EditGroupWidget::addTriStateItems(QComboBox* comboBox, bool inheritDefault) + { +- comboBox->addItem(tr("Inherit")); ++ QString inheritDefaultString; ++ if (inheritDefault) { ++ inheritDefaultString = tr("Enable"); ++ } ++ else { ++ inheritDefaultString = tr("Disable"); ++ } ++ ++ comboBox->clear(); ++ comboBox->addItem(tr("Inherit from parent group (%1)").arg(inheritDefaultString)); + comboBox->addItem(tr("Enable")); + comboBox->addItem(tr("Disable")); + } +diff --git a/src/gui/group/EditGroupWidget.h b/src/gui/group/EditGroupWidget.h +index de075be..971b6de 100644 +--- a/src/gui/group/EditGroupWidget.h ++++ b/src/gui/group/EditGroupWidget.h +@@ -50,7 +50,7 @@ private Q_SLOTS: + void cancel(); + + private: +- void addTriStateItems(QComboBox* comboBox); ++ void addTriStateItems(QComboBox* comboBox, bool inheritValue); + int indexFromTriState(Group::TriState triState); + Group::TriState triStateFromIndex(int index); + +diff --git a/src/gui/group/EditGroupWidgetMain.ui b/src/gui/group/EditGroupWidgetMain.ui +index c528c18..fdbf054 100644 +--- a/src/gui/group/EditGroupWidgetMain.ui ++++ b/src/gui/group/EditGroupWidgetMain.ui +@@ -66,7 +66,7 @@ + + + +- Autotype ++ Auto-type + + + +diff --git a/src/main.cpp b/src/main.cpp +index abe7ceb..2bdef5b 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -21,9 +21,11 @@ + #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" ++#include "gui/MessageBox.h" + + int main(int argc, char** argv) + { +@@ -37,7 +39,16 @@ int main(int argc, char** argv) + // don't set organizationName as that changes the return value of + // QDesktopServices::storageLocation(QDesktopServices::DataLocation) + +- Crypto::init(); ++ QApplication::setQuitOnLastWindowClosed(false); ++ ++ if (!Crypto::init()) { ++ QString error = QCoreApplication::translate("Main", ++ "Fatal error while testing the cryptographic functions."); ++ error.append("\n"); ++ error.append(Crypto::errorString()); ++ MessageBox::critical(Q_NULLPTR, QCoreApplication::translate("Main", "KeePassX - Error"), error); ++ return 1; ++ } + + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassX - cross-platform password manager")); +@@ -66,6 +77,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); +diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt +index 8df0050..c094f82 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) +@@ -165,6 +164,12 @@ 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}) ++ ++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/TestAutoType.cpp b/tests/TestAutoType.cpp +index 2fb5335..818f57c 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" +@@ -28,12 +29,17 @@ + #include "autotype/AutoType.h" + #include "autotype/AutoTypePlatformPlugin.h" + #include "autotype/test/AutoTypeTestInterface.h" ++#include "gui/MessageBox.h" ++ ++QTEST_GUILESS_MAIN(TestAutoType) + + void TestAutoType::initTestCase() + { +- Crypto::init(); +- ++ QVERIFY(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); +@@ -53,12 +59,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() +@@ -76,7 +94,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(), +@@ -87,41 +105,54 @@ 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_autoType->performGlobalAutoType(dbList); ++ m_test->setActiveWindowTitle("nomatch"); ++ MessageBox::setNextAnswer(QMessageBox::Ok); ++ 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())); + } + +-QTEST_GUILESS_MAIN(TestAutoType) ++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()); ++ ++} +diff --git a/tests/TestAutoType.h b/tests/TestAutoType.h +index fba7fde..d46a559 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 +diff --git a/tests/TestCryptoHash.cpp b/tests/TestCryptoHash.cpp +index 4f258a1..eb26ca8 100644 +--- a/tests/TestCryptoHash.cpp ++++ b/tests/TestCryptoHash.cpp +@@ -23,15 +23,17 @@ + #include "crypto/Crypto.h" + #include "crypto/CryptoHash.h" + ++QTEST_GUILESS_MAIN(TestCryptoHash) ++ + void TestCryptoHash::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestCryptoHash::test() + { + // TODO: move somewhere else +- QVERIFY(Crypto::selfTest()); ++ QVERIFY(Crypto::backendSelfTest()); + + CryptoHash cryptoHash1(CryptoHash::Sha256); + QCOMPARE(cryptoHash1.result(), +@@ -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 914096c..277dbcb 100644 +--- a/tests/TestDeletedObjects.cpp ++++ b/tests/TestDeletedObjects.cpp +@@ -26,9 +26,11 @@ + #include "format/KeePass2XmlReader.h" + #include "config-keepassx-tests.h" + ++QTEST_GUILESS_MAIN(TestDeletedObjects) ++ + void TestDeletedObjects::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestDeletedObjects::createAndDelete(Database* db, int delObjectsSize) +@@ -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 15f398f..477e83b 100644 +--- a/tests/TestEntry.cpp ++++ b/tests/TestEntry.cpp +@@ -23,9 +23,11 @@ + #include "core/Entry.h" + #include "crypto/Crypto.h" + ++QTEST_GUILESS_MAIN(TestEntry) ++ + void TestEntry::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestEntry::testHistoryItemDeletion() +@@ -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 fab63db..7ba886b 100644 +--- a/tests/TestEntryModel.cpp ++++ b/tests/TestEntryModel.cpp +@@ -33,10 +33,12 @@ + #include "gui/entry/EntryAttachmentsModel.h" + #include "gui/entry/EntryAttributesModel.h" + ++QTEST_GUILESS_MAIN(TestEntryModel) ++ + void TestEntryModel::initTestCase() + { + qRegisterMetaType("QModelIndex"); +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestEntryModel::test() +@@ -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 +new file mode 100644 +index 0000000..9f7ca13 +--- /dev/null ++++ b/tests/TestEntrySearcher.cpp +@@ -0,0 +1,144 @@ ++/* ++ * 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" ++ ++QTEST_GUILESS_MAIN(TestEntrySearcher) ++ ++void TestEntrySearcher::initTestCase() ++{ ++ m_groupRoot = new Group(); ++} ++ ++void TestEntrySearcher::cleanupTestCase() ++{ ++ delete m_groupRoot; ++} ++ ++void TestEntrySearcher::testSearch() ++{ ++ Group* group1 = new Group(); ++ Group* group2 = new Group(); ++ Group* group3 = new Group(); ++ ++ group1->setParent(m_groupRoot); ++ group2->setParent(m_groupRoot); ++ group3->setParent(m_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(m_groupRoot); ++ ++ Entry* eRoot2 = new Entry(); ++ eRoot2->setNotes("test term test"); ++ eRoot2->setGroup(m_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); ++ ++ 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); ++ QCOMPARE(m_searchResult.count(), 1); ++ ++ m_searchResult = m_entrySearcher.search("search term", group11, Qt::CaseInsensitive); ++ QCOMPARE(m_searchResult.count(), 1); ++ ++ m_searchResult = m_entrySearcher.search("search term", group1, Qt::CaseInsensitive); ++ QCOMPARE(m_searchResult.count(), 0); ++} ++ ++void TestEntrySearcher::testAndConcatenationInSearch() ++{ ++ Entry* entry = new Entry(); ++ entry->setNotes("abc def ghi"); ++ entry->setTitle("jkl"); ++ entry->setGroup(m_groupRoot); ++ ++ m_searchResult = m_entrySearcher.search("", m_groupRoot, Qt::CaseInsensitive); ++ QCOMPARE(m_searchResult.count(), 1); ++ ++ m_searchResult = m_entrySearcher.search("def", m_groupRoot, Qt::CaseInsensitive); ++ QCOMPARE(m_searchResult.count(), 1); ++ ++ m_searchResult = m_entrySearcher.search(" abc ghi ", m_groupRoot, Qt::CaseInsensitive); ++ QCOMPARE(m_searchResult.count(), 1); ++ ++ m_searchResult = m_entrySearcher.search("ghi ef", m_groupRoot, Qt::CaseInsensitive); ++ QCOMPARE(m_searchResult.count(), 1); ++ ++ m_searchResult = m_entrySearcher.search("abc ef xyz", m_groupRoot, Qt::CaseInsensitive); ++ QCOMPARE(m_searchResult.count(), 0); ++ ++ 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 +new file mode 100644 +index 0000000..7c45451 +--- /dev/null ++++ b/tests/TestEntrySearcher.h +@@ -0,0 +1,45 @@ ++/* ++ * 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 ++ ++#include "core/EntrySearcher.h" ++#include "core/Group.h" ++ ++class TestEntrySearcher : public QObject ++{ ++ Q_OBJECT ++ ++private Q_SLOTS: ++ void initTestCase(); ++ void cleanupTestCase(); ++ ++ void testAndConcatenationInSearch(); ++ void testSearch(); ++ void testAllAttributesAreSearched(); ++ ++private: ++ Group* m_groupRoot; ++ EntrySearcher m_entrySearcher; ++ QList m_searchResult; ++}; ++ ++#endif // KEEPASSX_TESTENTRYSEARCHER_H +diff --git a/tests/TestExporter.cpp b/tests/TestExporter.cpp +new file mode 100644 +index 0000000..d703e02 +--- /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() ++{ ++ QVERIFY(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().exportGroup(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 0000000..15f9a7c +--- /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 86b55b7..507cf15 100644 +--- a/tests/TestGroup.cpp ++++ b/tests/TestGroup.cpp +@@ -27,11 +27,13 @@ + #include "core/Metadata.h" + #include "crypto/Crypto.h" + ++QTEST_GUILESS_MAIN(TestGroup) ++ + void TestGroup::initTestCase() + { + qRegisterMetaType("Entry*"); + qRegisterMetaType("Group*"); +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestGroup::testParenting() +@@ -334,102 +336,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(); +@@ -536,49 +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)); + } +- +-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 895c2cc..c612a3a 100644 +--- a/tests/TestGroup.h ++++ b/tests/TestGroup.h +@@ -31,11 +31,8 @@ private Q_SLOTS: + void testEntries(); + void testDeleteSignals(); + void testCopyCustomIcon(); +- void testSearch(); +- void testAndConcatenationInSearch(); + void testClone(); + void testCopyCustomIcons(); +- void testExportToDb(); + }; + + #endif // KEEPASSX_TESTGROUP_H +diff --git a/tests/TestGroupModel.cpp b/tests/TestGroupModel.cpp +index a16386c..32a4b8e 100644 +--- a/tests/TestGroupModel.cpp ++++ b/tests/TestGroupModel.cpp +@@ -27,10 +27,12 @@ + #include "crypto/Crypto.h" + #include "gui/group/GroupModel.h" + ++QTEST_GUILESS_MAIN(TestGroupModel) ++ + void TestGroupModel::initTestCase() + { + qRegisterMetaType("QModelIndex"); +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestGroupModel::test() +@@ -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 ab7d386..09179fe 100644 +--- a/tests/TestHashedBlockStream.cpp ++++ b/tests/TestHashedBlockStream.cpp +@@ -24,9 +24,11 @@ + #include "crypto/Crypto.h" + #include "streams/HashedBlockStream.h" + ++QTEST_GUILESS_MAIN(TestHashedBlockStream) ++ + void TestHashedBlockStream::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestHashedBlockStream::testWriteRead() +@@ -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 3ec4e78..249a365 100644 +--- a/tests/TestKeePass1Reader.cpp ++++ b/tests/TestKeePass1Reader.cpp +@@ -33,9 +33,11 @@ + #include "keys/FileKey.h" + #include "keys/PasswordKey.h" + ++QTEST_GUILESS_MAIN(TestKeePass1Reader) ++ + void TestKeePass1Reader::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + + QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/basic.kdb"); + +@@ -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 74a1540..7963e9a 100644 +--- a/tests/TestKeePass2RandomStream.cpp ++++ b/tests/TestKeePass2RandomStream.cpp +@@ -26,9 +26,11 @@ + #include "format/KeePass2.h" + #include "format/KeePass2RandomStream.h" + ++QTEST_GUILESS_MAIN(TestKeePass2RandomStream) ++ + void TestKeePass2RandomStream::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestKeePass2RandomStream::test() +@@ -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 6b1ee1e..d6cb70c 100644 +--- a/tests/TestKeePass2Reader.cpp ++++ b/tests/TestKeePass2Reader.cpp +@@ -28,9 +28,11 @@ + #include "format/KeePass2Reader.h" + #include "keys/PasswordKey.h" + ++QTEST_GUILESS_MAIN(TestKeePass2Reader) ++ + void TestKeePass2Reader::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestKeePass2Reader::testNonAscii() +@@ -154,5 +156,3 @@ void TestKeePass2Reader::testFormat300() + + delete db; + } +- +-QTEST_GUILESS_MAIN(TestKeePass2Reader) +diff --git a/tests/TestKeePass2Writer.cpp b/tests/TestKeePass2Writer.cpp +index bbc4992..cf4ab1c 100644 +--- a/tests/TestKeePass2Writer.cpp ++++ b/tests/TestKeePass2Writer.cpp +@@ -29,9 +29,11 @@ + #include "format/KeePass2Writer.h" + #include "keys/PasswordKey.h" + ++QTEST_GUILESS_MAIN(TestKeePass2Writer) ++ + void TestKeePass2Writer::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + + CompositeKey key; + key.addKey(PasswordKey("test")); +@@ -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 ca57db9..8e87d67 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) +@@ -66,7 +68,7 @@ QDateTime TestKeePass2XmlReader::genDT(int year, int month, int day, int hour, i + + void TestKeePass2XmlReader::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + + KeePass2XmlReader reader; + QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"); +@@ -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 ec9a35b..d6758d6 100644 +--- a/tests/TestKeys.cpp ++++ b/tests/TestKeys.cpp +@@ -31,9 +31,11 @@ + #include "keys/FileKey.h" + #include "keys/PasswordKey.h" + ++QTEST_GUILESS_MAIN(TestKeys) ++ + void TestKeys::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestKeys::testComposite() +@@ -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 864ea1c..e275e83 100644 +--- a/tests/TestModified.cpp ++++ b/tests/TestModified.cpp +@@ -27,9 +27,11 @@ + #include "core/Tools.h" + #include "crypto/Crypto.h" + ++QTEST_GUILESS_MAIN(TestModified) ++ + void TestModified::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestModified::testSignals() +@@ -466,5 +468,3 @@ void TestModified::testHistoryItem() + + delete db; + } +- +-QTEST_GUILESS_MAIN(TestModified) +diff --git a/tests/TestQCommandLineParser.cpp b/tests/TestQCommandLineParser.cpp +index d487862..4e2c635 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 bccee0e..443db52 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: +@@ -154,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"); +@@ -197,5 +202,3 @@ QString TestQSaveFile::tmpDir() + + return dirName; + } +- +-QTEST_GUILESS_MAIN(TestQSaveFile) +diff --git a/tests/TestRandom.cpp b/tests/TestRandom.cpp +index 8ac570e..40ab702 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 b47a005..6d4e94f 100644 +--- a/tests/TestSymmetricCipher.cpp ++++ b/tests/TestSymmetricCipher.cpp +@@ -25,9 +25,11 @@ + #include "crypto/SymmetricCipher.h" + #include "streams/SymmetricCipherStream.h" + ++QTEST_GUILESS_MAIN(TestSymmetricCipher) ++ + void TestSymmetricCipher::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestSymmetricCipher::testAes256CbcEncryption() +@@ -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 e06125b..dc9991d 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) +diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp +index a4d04c5..326c349 100644 +--- a/tests/gui/TestGui.cpp ++++ b/tests/gui/TestGui.cpp +@@ -51,7 +51,7 @@ + + void TestGui::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + Config::createTempFileInstance(); + m_mainWindow = new MainWindow(); + m_tabWidget = m_mainWindow->findChild("tabWidget"); +@@ -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; +diff --git a/tests/gui/TestGuiPixmaps.cpp b/tests/gui/TestGuiPixmaps.cpp +index 401f68b..87e3f24 100644 +--- a/tests/gui/TestGuiPixmaps.cpp ++++ b/tests/gui/TestGuiPixmaps.cpp +@@ -29,7 +29,7 @@ + + void TestGuiPixmaps::initTestCase() + { +- Crypto::init(); ++ QVERIFY(Crypto::init()); + } + + void TestGuiPixmaps::testDatabaseIcons() +diff --git a/utils/kdbx-extract.cpp b/utils/kdbx-extract.cpp +index beee71d..f5d2a19 100644 +--- a/utils/kdbx-extract.cpp ++++ b/utils/kdbx-extract.cpp +@@ -38,7 +38,9 @@ int main(int argc, char **argv) + return 1; + } + +- Crypto::init(); ++ if (!Crypto::init()) { ++ qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); ++ } + + CompositeKey key; + if (QFile::exists(app.arguments().at(1))) {