mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-03 10:00:04 -05:00
Merge branch 'master' of https://github.com/keepassx/keepassx
Conflicts: README.md src/core/Config.cpp src/gui/DatabaseTabWidget.cpp src/gui/DatabaseTabWidget.h src/gui/DatabaseWidget.cpp src/gui/DatabaseWidget.h src/gui/MainWindow.cpp src/gui/MainWindow.h src/gui/MainWindow.ui src/gui/SearchWidget.ui src/gui/SettingsWidget.cpp src/gui/SettingsWidgetGeneral.ui
This commit is contained in:
commit
235baa3dcc
19
.travis.yml
Normal file
19
.travis.yml
Normal file
@ -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
|
8
.tx/config
Normal file
8
.tx/config
Normal file
@ -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_<lang>.ts
|
||||
source_lang = en
|
||||
type = QT
|
@ -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"))
|
||||
|
2
INSTALL
2
INSTALL
@ -2,7 +2,7 @@ Building:
|
||||
=========
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. [CMAKE PARAMETERS]
|
||||
cmake [CMAKE PARAMETERS] ..
|
||||
make [-jX]
|
||||
|
||||
Common cmake parameters:
|
||||
|
137
README.md
137
README.md
@ -1,17 +1,128 @@
|
||||
# KeePassX + keepasshttp + autotype
|
||||
# KeePassX + keepasshttp
|
||||
|
||||
This code extends the brilliant [KeePassX](https://www.keepassx.org/) program
|
||||
which accesses [KeePass](http://keepass.info/) password databases.
|
||||
## About
|
||||
|
||||
I have merged the latest version of the code with Francois Ferrand's
|
||||
[keepassx-http](https://gitorious.org/keepassx/keepassx-http/) repository.
|
||||
This adds support for the [keepasshttp](https://github.com/pfn/keepasshttp/)
|
||||
protocol, enabling automatic form-filling in web browsers. This is accomplished
|
||||
via a compatible browser plugin such as
|
||||
[PassIFox](https://passifox.appspot.com/passifox.xpi) for Mozilla Firefox and
|
||||
[chromeIPass](https://chrome.google.com/webstore/detail/chromeipass/ompiailgknfdndiefoaoiligalphfdae)
|
||||
for Google Chrome.
|
||||
KeePassX is an application for people with extremely high demands on secure personal data management.
|
||||
It has a light interface, is cross platform and published under the terms of the GNU General Public License.
|
||||
|
||||
I have also added global autotype for OSX machines and added a few other minor
|
||||
tweaks and bugfixes.
|
||||
KeePassX saves many different information e.g. user names, passwords, urls, attachments and comments in one single database.
|
||||
For a better management user-defined titles and icons can be specified for each single entry.
|
||||
Furthermore the entries are sorted in groups, which are customizable as well. The integrated search function allows to search in a single group or the complete database.
|
||||
KeePassX offers a little utility for secure password generation. The password generator is very customizable, fast and easy to use.
|
||||
Especially someone who generates passwords frequently will appreciate this feature.
|
||||
|
||||
The complete database is always encrypted with the AES (aka Rijndael) encryption algorithm using a 256 bit key.
|
||||
Therefore the saved information can be considered as quite safe. KeePassX uses a database format that is compatible with [KeePass Password Safe](http://keepass.info/).
|
||||
This makes the use of that application even more favorable.
|
||||
|
||||
## Install
|
||||
|
||||
KeePassX can be downloaded and installed using an assortment of installers available on the main [KeePassX website](http://www.keepassx.org).
|
||||
KeePassX can also be installed from the official repositories of many Linux repositories.
|
||||
If you wish to build KeePassX from source, rather than rely on the pre-compiled binaries, you may wish to read up on the _From Source_ section.
|
||||
|
||||
### Debian
|
||||
|
||||
To install KeePassX from the Debian repository:
|
||||
|
||||
```bash
|
||||
sudo apt-get install keepassx
|
||||
```
|
||||
|
||||
### Red Hat
|
||||
|
||||
Install KeePassX from the Red Hat (or CentOS) repository:
|
||||
|
||||
```bash
|
||||
sudo yum install keepassx
|
||||
```
|
||||
|
||||
### Windows / Mac OS X
|
||||
|
||||
Download the installer from the KeePassX [download](https://www.keepassx.org/downloads) page.
|
||||
Once downloaded, double click on the file to execute the installer.
|
||||
|
||||
### From Source
|
||||
|
||||
#### Build Dependencies
|
||||
|
||||
The following tools must exist within your PATH:
|
||||
|
||||
* make
|
||||
* cmake (>= 2.6.4)
|
||||
* g++ or clang++
|
||||
|
||||
The following libraries are required:
|
||||
|
||||
* Qt 4 (>= 4.6)
|
||||
* libgcrypt
|
||||
* zlib
|
||||
|
||||
On Debian you can install them with:
|
||||
|
||||
```bash
|
||||
sudo apt-get install build-essential cmake libqt4-dev libgcrypt11-dev zlib1g-dev
|
||||
```
|
||||
|
||||
#### Build Steps
|
||||
|
||||
To compile from source:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make [-jX]
|
||||
```
|
||||
|
||||
You will have the compiled KeePassX binary inside the `./build/src/` directory.
|
||||
|
||||
To install this binary execute the following:
|
||||
|
||||
```bash
|
||||
sudo make install
|
||||
```
|
||||
|
||||
More detailed instructions available in the INSTALL file.
|
||||
|
||||
## Contribute
|
||||
|
||||
Coordination of work between developers is handled through the [KeePassX development](https://www.keepassx.org/dev/) site.
|
||||
Requests for enhancements, or reports of bugs encountered, can also be reported through the KeePassX development site.
|
||||
However, members of the open-source community are encouraged to submit pull requests directly through GitHub.
|
||||
|
||||
### Clone Repository
|
||||
|
||||
Clone the repository to a suitable location where you can extend and build this project.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/keepassx/keepassx.git
|
||||
```
|
||||
|
||||
**Note:** This will clone the entire contents of the repository at the HEAD revision.
|
||||
|
||||
To update the project from within the project's folder you can run the following command:
|
||||
|
||||
```bash
|
||||
git pull
|
||||
```
|
||||
|
||||
### Feature Requests
|
||||
|
||||
We're always looking for suggestions to improve our application. If you have a suggestion for improving an existing feature,
|
||||
or would like to suggest a completely new feature for KeePassX, please file a ticket on the [KeePassX development](https://www.keepassx.org/dev/) site.
|
||||
|
||||
### Bug Reports
|
||||
|
||||
Our software isn't always perfect, but we strive to always improve our work. You may file bug reports on the [KeePassX development](https://www.keepassx.org/dev/) site.
|
||||
|
||||
### Pull Requests
|
||||
|
||||
Along with our desire to hear your feedback and suggestions, we're also interested in accepting direct assistance in the form of code.
|
||||
|
||||
Issue merge requests against our [GitHub repository](https://github.com/keepassx/keepassx).
|
||||
|
||||
### Translations
|
||||
|
||||
Translations are managed on [Transifex](https://www.transifex.com/projects/p/keepassx/) which offers a web interface.
|
||||
Please join an existing language team or request a new one if there is none.
|
||||
|
@ -13,6 +13,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(translations)
|
||||
|
||||
file(GLOB DATABASE_ICONS icons/database/*.png)
|
||||
|
||||
install(FILES ${DATABASE_ICONS} DESTINATION ${DATA_INSTALL_DIR}/icons/database)
|
||||
|
26
share/translations/CMakeLists.txt
Normal file
26
share/translations/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
1177
share/translations/keepassx_de.ts
Normal file
1177
share/translations/keepassx_de.ts
Normal file
File diff suppressed because it is too large
Load Diff
1216
share/translations/keepassx_en.ts
Normal file
1216
share/translations/keepassx_en.ts
Normal file
File diff suppressed because it is too large
Load Diff
41
share/translations/keepassx_en_plurals.ts
Normal file
41
share/translations/keepassx_en_plurals.ts
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.0" language="en_US">
|
||||
<context>
|
||||
<name>DatabaseWidget</name>
|
||||
<message numerus="yes">
|
||||
<source>Do you really want to move %n entry(s) to the recycle bin?</source>
|
||||
<translation>
|
||||
<numerusform>Do you really want to move %n entry to the recycle bin?</numerusform>
|
||||
<numerusform>Do you really want to move %n entries to the recycle bin?</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditEntryWidget</name>
|
||||
<message numerus="yes">
|
||||
<source>%n week(s)</source>
|
||||
<translation>
|
||||
<numerusform>%n week</numerusform>
|
||||
<numerusform>%n weeks</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>%n month(s)</source>
|
||||
<translation>
|
||||
<numerusform>%n month</numerusform>
|
||||
<numerusform>%n months</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditWidgetIcons</name>
|
||||
<message numerus="yes">
|
||||
<source>Can't delete icon. Still used by %n item(s).</source>
|
||||
<translation>
|
||||
<numerusform>Can't delete icon. Still used by %n item.</numerusform>
|
||||
<numerusform>Can't delete icon. Still used by %n items.</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
1179
share/translations/keepassx_it.ts
Normal file
1179
share/translations/keepassx_it.ts
Normal file
File diff suppressed because it is too large
Load Diff
1178
share/translations/keepassx_nl_NL.ts
Normal file
1178
share/translations/keepassx_nl_NL.ts
Normal file
File diff suppressed because it is too large
Load Diff
1178
share/translations/keepassx_sv.ts
Normal file
1178
share/translations/keepassx_sv.ts
Normal file
File diff suppressed because it is too large
Load Diff
8
share/translations/update.sh
Executable file
8
share/translations/update.sh
Executable file
@ -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
|
@ -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
|
||||
@ -162,6 +166,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
|
||||
|
@ -190,8 +190,10 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||
|
||||
if (entryList.isEmpty()) {
|
||||
m_inAutoType = false;
|
||||
MessageBox::information(Q_NULLPTR, tr("Auto-Type - KeePassX"),
|
||||
tr("Couldn't find an entry that matches the window title."));
|
||||
QString message = tr("Couldn't find an entry that matches the window title:");
|
||||
message.append("\n\n");
|
||||
message.append(windowTitle);
|
||||
MessageBox::information(Q_NULLPTR, tr("Auto-Type - KeePassX"), message);
|
||||
}
|
||||
else if ((entryList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
|
||||
m_inAutoType = false;
|
||||
@ -503,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();
|
||||
}
|
||||
|
@ -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<char*>(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<char*>(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) {
|
||||
|
@ -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}"
|
||||
|
||||
|
@ -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";
|
||||
@ -89,19 +90,21 @@ void Config::init(const QString& fileName)
|
||||
|
||||
m_defaults.insert("RememberLastDatabases", true);
|
||||
m_defaults.insert("OpenPreviousDatabasesOnStartup", true);
|
||||
m_defaults.insert("ModifiedOnExpandedStateChanges", true);
|
||||
m_defaults.insert("AutoSaveAfterEveryChange", false);
|
||||
m_defaults.insert("AutoSaveOnExit", false);
|
||||
m_defaults.insert("ShowToolbar", true);
|
||||
m_defaults.insert("MinimizeOnCopy", false);
|
||||
m_defaults.insert("UseGroupIconOnEntryCreation", false);
|
||||
m_defaults.insert("ReloadBehavior", 0 /*always ask*/);
|
||||
m_defaults.insert("AutoTypeEntryTitleMatch", true);
|
||||
m_defaults.insert("security/clearclipboard", true);
|
||||
m_defaults.insert("security/clearclipboardtimeout", 10);
|
||||
m_defaults.insert("security/lockdatabaseidle", false);
|
||||
m_defaults.insert("security/lockdatabaseidlesec", 10);
|
||||
m_defaults.insert("security/passwordscleartext", false);
|
||||
m_defaults.insert("security/autotypeask", false);
|
||||
m_defaults.insert("security/autotypeask", true);
|
||||
m_defaults.insert("GUI/Language", "system");
|
||||
m_defaults.insert("GUI/ShowTrayIcon", false);
|
||||
m_defaults.insert("GUI/MinimizeToTray", false);
|
||||
}
|
||||
|
||||
Config* Config::instance()
|
||||
@ -113,7 +116,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);
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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 <class T> bool set(T& property, const T& value);
|
||||
|
||||
|
@ -27,6 +27,11 @@ QList<QString> EntryAttachments::keys() const
|
||||
return m_attachments.keys();
|
||||
}
|
||||
|
||||
bool EntryAttachments::hasKey(const QString& key) const
|
||||
{
|
||||
return m_attachments.keys().contains(key);
|
||||
}
|
||||
|
||||
QList<QByteArray> EntryAttachments::values() const
|
||||
{
|
||||
return m_attachments.values();
|
||||
|
@ -30,6 +30,7 @@ class EntryAttachments : public QObject
|
||||
public:
|
||||
explicit EntryAttachments(QObject* parent = Q_NULLPTR);
|
||||
QList<QString> keys() const;
|
||||
bool hasKey(const QString& key) const;
|
||||
QList<QByteArray> values() const;
|
||||
QByteArray value(const QString& key) const;
|
||||
void set(const QString& key, const QByteArray& value);
|
||||
|
@ -36,6 +36,11 @@ QList<QString> EntryAttributes::keys() const
|
||||
return m_attributes.keys();
|
||||
}
|
||||
|
||||
bool EntryAttributes::hasKey(const QString& key) const
|
||||
{
|
||||
return m_attributes.keys().contains(key);
|
||||
}
|
||||
|
||||
QList<QString> EntryAttributes::customKeys()
|
||||
{
|
||||
QList<QString> customKeys;
|
||||
|
@ -32,6 +32,7 @@ class EntryAttributes : public QObject
|
||||
public:
|
||||
explicit EntryAttributes(QObject* parent = Q_NULLPTR);
|
||||
QList<QString> keys() const;
|
||||
bool hasKey(const QString& key) const;
|
||||
QList<QString> customKeys();
|
||||
QString value(const QString& key) const;
|
||||
bool contains(const QString& key) const;
|
||||
|
65
src/core/EntrySearcher.cpp
Normal file
65
src/core/EntrySearcher.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "EntrySearcher.h"
|
||||
|
||||
#include "core/Group.h"
|
||||
|
||||
QList<Entry*> EntrySearcher::search(const QString &searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity)
|
||||
{
|
||||
if (!group->resolveSearchingEnabled()) {
|
||||
return QList<Entry*>();
|
||||
}
|
||||
|
||||
return searchEntries(searchTerm, group, caseSensitivity);
|
||||
}
|
||||
|
||||
QList<Entry*> EntrySearcher::searchEntries(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity)
|
||||
{
|
||||
QList<Entry*> 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<Entry*> 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<Entry*>();
|
||||
}
|
||||
}
|
||||
|
||||
return QList<Entry*>() << 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);
|
||||
}
|
37
src/core/EntrySearcher.h
Normal file
37
src/core/EntrySearcher.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Florian Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_ENTRYSEARCHER_H
|
||||
#define KEEPASSX_ENTRYSEARCHER_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
|
||||
class Group;
|
||||
class Entry;
|
||||
|
||||
class EntrySearcher
|
||||
{
|
||||
public:
|
||||
QList<Entry*> search(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity);
|
||||
private:
|
||||
QList<Entry*> searchEntries(const QString& searchTerm, const Group* group, Qt::CaseSensitivity caseSensitivity);
|
||||
QList<Entry*> matchEntry(const QString& searchTerm, Entry* entry, Qt::CaseSensitivity caseSensitivity);
|
||||
bool wordMatch(const QString &word, Entry *entry, Qt::CaseSensitivity caseSensitivity);
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_ENTRYSEARCHER_H
|
14
src/core/Exporter.h
Normal file
14
src/core/Exporter.h
Normal file
@ -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
|
@ -248,9 +248,7 @@ void Group::setExpanded(bool expanded)
|
||||
if (m_data.isExpanded != expanded) {
|
||||
m_data.isExpanded = expanded;
|
||||
updateTimeinfo();
|
||||
if (config()->get("ModifiedOnExpandedStateChanges").toBool()) {
|
||||
Q_EMIT modified();
|
||||
}
|
||||
Q_EMIT modified();
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,22 +498,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<Uuid> customIcons = customIconsRecursive();
|
||||
db->metadata()->copyCustomIcons(customIcons, database()->metadata());
|
||||
|
||||
db->copyAttributesFrom(database());
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
void Group::addEntry(Entry* entry)
|
||||
{
|
||||
Q_ASSERT(entry);
|
||||
@ -612,33 +594,6 @@ void Group::recCreateDelObjects()
|
||||
}
|
||||
}
|
||||
|
||||
QList<Entry*> Group::search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity,
|
||||
bool resolveInherit)
|
||||
{
|
||||
QList<Entry*> searchResult;
|
||||
bool search;
|
||||
if (resolveInherit) {
|
||||
search = resolveSearchingEnabled();
|
||||
}
|
||||
else if (searchingEnabled() == Disable) {
|
||||
search = false;
|
||||
}
|
||||
else {
|
||||
search = true;
|
||||
}
|
||||
if (search) {
|
||||
Q_FOREACH (Entry* entry, m_entries) {
|
||||
if (entry->match(searchTerm, caseSensitivity)) {
|
||||
searchResult.append(entry);
|
||||
}
|
||||
}
|
||||
Q_FOREACH (Group* group, m_children) {
|
||||
searchResult.append(group->search(searchTerm, caseSensitivity, false));
|
||||
}
|
||||
}
|
||||
return searchResult;
|
||||
}
|
||||
|
||||
bool Group::resolveSearchingEnabled() const
|
||||
{
|
||||
switch (m_data.searchingEnabled) {
|
||||
|
@ -111,10 +111,6 @@ public:
|
||||
*/
|
||||
Group* clone(Entry::CloneFlags entryFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo) const;
|
||||
void copyDataFrom(const Group* other);
|
||||
Database* exportToDb();
|
||||
|
||||
QList<Entry*> search(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity,
|
||||
bool resolveInherit = true);
|
||||
|
||||
Q_SIGNALS:
|
||||
void dataChanged(Group* group);
|
||||
|
39
src/core/ToDbExporter.cpp
Normal file
39
src/core/ToDbExporter.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<Uuid> customIcons = group->customIconsRecursive();
|
||||
db->metadata()->copyCustomIcons(customIcons, oldDb->metadata());
|
||||
|
||||
db->copyAttributesFrom(oldDb);
|
||||
|
||||
return db;
|
||||
}
|
33
src/core/ToDbExporter.h
Normal file
33
src/core/ToDbExporter.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
120
src/core/Translator.cpp
Normal file
120
src/core/Translator.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Translator.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QLibraryInfo>
|
||||
#include <QLocale>
|
||||
#include <QRegExp>
|
||||
#include <QTranslator>
|
||||
|
||||
#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<QPair<QString, QString> > Translator::availableLanguages()
|
||||
{
|
||||
QStringList paths;
|
||||
#ifdef QT_DEBUG
|
||||
paths.append(QString("%1/share/translations").arg(KEEPASSX_BINARY_DIR));
|
||||
#endif
|
||||
paths.append(filePath()->dataPath("translations"));
|
||||
|
||||
QList<QPair<QString, QString> > languages;
|
||||
languages.append(QPair<QString, QString>("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<QString, QString>(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;
|
||||
}
|
||||
}
|
36
src/core/Translator.h
Normal file
36
src/core/Translator.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_TRANSLATOR_H
|
||||
#define KEEPASSX_TRANSLATOR_H
|
||||
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
|
||||
class Translator
|
||||
{
|
||||
public:
|
||||
static void installTranslator();
|
||||
static QList<QPair<QString, QString> > 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
|
@ -21,7 +21,12 @@
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -18,18 +18,25 @@
|
||||
#ifndef KEEPASSX_CRYPTO_H
|
||||
#define KEEPASSX_CRYPTO_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
#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
|
||||
|
@ -35,9 +35,15 @@ KeePass2XmlReader::KeePass2XmlReader()
|
||||
, m_db(Q_NULLPTR)
|
||||
, m_meta(Q_NULLPTR)
|
||||
, m_error(false)
|
||||
, m_strictMode(false)
|
||||
{
|
||||
}
|
||||
|
||||
void KeePass2XmlReader::setStrictMode(bool strictMode)
|
||||
{
|
||||
m_strictMode = strictMode;
|
||||
}
|
||||
|
||||
void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream)
|
||||
{
|
||||
m_error = false;
|
||||
@ -493,7 +499,12 @@ Group* KeePass2XmlReader::parseGroup()
|
||||
if (m_xml.name() == "UUID") {
|
||||
Uuid uuid = readUuid();
|
||||
if (uuid.isNull()) {
|
||||
raiseError("Null group uuid");
|
||||
if (m_strictMode) {
|
||||
raiseError("Null group uuid");
|
||||
}
|
||||
else {
|
||||
group->setUuid(Uuid::random());
|
||||
}
|
||||
}
|
||||
else {
|
||||
group->setUuid(uuid);
|
||||
@ -508,7 +519,9 @@ Group* KeePass2XmlReader::parseGroup()
|
||||
else if (m_xml.name() == "IconID") {
|
||||
int iconId = readNumber();
|
||||
if (iconId < 0) {
|
||||
raiseError("Invalid group icon number");
|
||||
if (m_strictMode) {
|
||||
raiseError("Invalid group icon number");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (iconId >= DatabaseIcons::IconCount) {
|
||||
@ -584,6 +597,10 @@ Group* KeePass2XmlReader::parseGroup()
|
||||
}
|
||||
}
|
||||
|
||||
if (group->uuid().isNull() && !m_strictMode) {
|
||||
group->setUuid(Uuid::random());
|
||||
}
|
||||
|
||||
if (!group->uuid().isNull()) {
|
||||
Group* tmpGroup = group;
|
||||
group = getGroup(tmpGroup->uuid());
|
||||
@ -630,7 +647,9 @@ void KeePass2XmlReader::parseDeletedObject()
|
||||
if (m_xml.name() == "UUID") {
|
||||
Uuid uuid = readUuid();
|
||||
if (uuid.isNull()) {
|
||||
raiseError("Null DeleteObject uuid");
|
||||
if (m_strictMode) {
|
||||
raiseError("Null DeleteObject uuid");
|
||||
}
|
||||
}
|
||||
else {
|
||||
delObj.uuid = uuid;
|
||||
@ -647,7 +666,7 @@ void KeePass2XmlReader::parseDeletedObject()
|
||||
if (!delObj.uuid.isNull() && !delObj.deletionTime.isNull()) {
|
||||
m_db->addDeletedObject(delObj);
|
||||
}
|
||||
else {
|
||||
else if (m_strictMode) {
|
||||
raiseError("Missing DeletedObject uuid or time");
|
||||
}
|
||||
}
|
||||
@ -665,7 +684,12 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
|
||||
if (m_xml.name() == "UUID") {
|
||||
Uuid uuid = readUuid();
|
||||
if (uuid.isNull()) {
|
||||
raiseError("Null entry uuid");
|
||||
if (m_strictMode) {
|
||||
raiseError("Null entry uuid");
|
||||
}
|
||||
else {
|
||||
entry->setUuid(Uuid::random());
|
||||
}
|
||||
}
|
||||
else {
|
||||
entry->setUuid(uuid);
|
||||
@ -674,7 +698,9 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
|
||||
else if (m_xml.name() == "IconID") {
|
||||
int iconId = readNumber();
|
||||
if (iconId < 0) {
|
||||
raiseError("Invalud entry icon number");
|
||||
if (m_strictMode) {
|
||||
raiseError("Invalud entry icon number");
|
||||
}
|
||||
}
|
||||
else {
|
||||
entry->setIcon(iconId);
|
||||
@ -726,6 +752,10 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->uuid().isNull() && !m_strictMode) {
|
||||
entry->setUuid(Uuid::random());
|
||||
}
|
||||
|
||||
if (!entry->uuid().isNull()) {
|
||||
if (history) {
|
||||
entry->setUpdateTimeinfo(false);
|
||||
@ -795,7 +825,13 @@ void KeePass2XmlReader::parseEntryString(Entry* entry)
|
||||
}
|
||||
|
||||
if (keySet && valueSet) {
|
||||
entry->attributes()->set(key, value, protect);
|
||||
// the default attributes are always there so additionally check if it's empty
|
||||
if (entry->attributes()->hasKey(key) && !entry->attributes()->value(key).isEmpty()) {
|
||||
raiseError("Duplicate custom attribute found");
|
||||
}
|
||||
else {
|
||||
entry->attributes()->set(key, value, protect);
|
||||
}
|
||||
}
|
||||
else {
|
||||
raiseError("Entry string key or value missing");
|
||||
@ -844,7 +880,12 @@ QPair<QString, QString> KeePass2XmlReader::parseEntryBinary(Entry* entry)
|
||||
}
|
||||
|
||||
if (keySet && valueSet) {
|
||||
entry->attachments()->set(key, value);
|
||||
if (entry->attachments()->hasKey(key)) {
|
||||
raiseError("Duplicate attachment found");
|
||||
}
|
||||
else {
|
||||
entry->attachments()->set(key, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
raiseError("Entry binary key or value missing");
|
||||
@ -986,7 +1027,12 @@ QDateTime KeePass2XmlReader::readDateTime()
|
||||
QDateTime dt = QDateTime::fromString(str, Qt::ISODate);
|
||||
|
||||
if (!dt.isValid()) {
|
||||
raiseError("Invalid date time value");
|
||||
if (m_strictMode) {
|
||||
raiseError("Invalid date time value");
|
||||
}
|
||||
else {
|
||||
dt = Tools::currentDateTimeUtc();
|
||||
}
|
||||
}
|
||||
|
||||
return dt;
|
||||
@ -1001,7 +1047,9 @@ QColor KeePass2XmlReader::readColor()
|
||||
}
|
||||
|
||||
if (colorStr.length() != 7 || colorStr[0] != '#') {
|
||||
raiseError("Invalid color value");
|
||||
if (m_strictMode) {
|
||||
raiseError("Invalid color value");
|
||||
}
|
||||
return QColor();
|
||||
}
|
||||
|
||||
@ -1011,7 +1059,9 @@ QColor KeePass2XmlReader::readColor()
|
||||
bool ok;
|
||||
int rgbPart = rgbPartStr.toInt(&ok, 16);
|
||||
if (!ok || rgbPart > 255) {
|
||||
raiseError("Invalid color rgb part");
|
||||
if (m_strictMode) {
|
||||
raiseError("Invalid color rgb part");
|
||||
}
|
||||
return QColor();
|
||||
}
|
||||
|
||||
@ -1043,7 +1093,9 @@ Uuid KeePass2XmlReader::readUuid()
|
||||
{
|
||||
QByteArray uuidBin = readBinary();
|
||||
if (uuidBin.length() != Uuid::Length) {
|
||||
raiseError("Invalid uuid value");
|
||||
if (m_strictMode) {
|
||||
raiseError("Invalid uuid value");
|
||||
}
|
||||
return Uuid();
|
||||
}
|
||||
else {
|
||||
|
@ -47,6 +47,7 @@ public:
|
||||
bool hasError();
|
||||
QString errorString();
|
||||
QByteArray headerHash();
|
||||
void setStrictMode(bool strictMode);
|
||||
|
||||
private:
|
||||
bool parseKeePassFile();
|
||||
@ -95,6 +96,7 @@ private:
|
||||
QByteArray m_headerHash;
|
||||
bool m_error;
|
||||
QString m_errorStr;
|
||||
bool m_strictMode;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2XMLREADER_H
|
||||
|
@ -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()
|
||||
|
@ -43,6 +43,7 @@ private:
|
||||
static Clipboard* m_instance;
|
||||
|
||||
QTimer* m_timer;
|
||||
QString m_lastCopied;
|
||||
};
|
||||
|
||||
inline Clipboard* clipboard() {
|
||||
|
@ -87,7 +87,7 @@ void DatabaseOpenWidget::enterKey(const QString& pw, const QString& keyFile)
|
||||
m_ui->editPassword->setText(pw);
|
||||
}
|
||||
if (!keyFile.isEmpty()) {
|
||||
m_ui->checkKeyFile->setText(keyFile);
|
||||
m_ui->comboKeyFile->setEditText(keyFile);
|
||||
}
|
||||
|
||||
openDatabase();
|
||||
@ -129,8 +129,8 @@ void DatabaseOpenWidget::openDatabase(const CompositeKey& masterKey)
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -150,7 +150,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);
|
||||
|
@ -19,9 +19,6 @@
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QTabWidget>
|
||||
#include <QtCore/QFileSystemWatcher>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "autotype/AutoType.h"
|
||||
#include "core/Config.h"
|
||||
@ -30,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"
|
||||
@ -48,16 +46,17 @@ DatabaseManagerStruct::DatabaseManagerStruct()
|
||||
const int DatabaseTabWidget::LastDatabasesCount = 5;
|
||||
|
||||
DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
||||
: QTabWidget(parent),
|
||||
m_fileWatcher(new QFileSystemWatcher(this))
|
||||
: 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()));
|
||||
connect(m_fileWatcher, SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString)));
|
||||
}
|
||||
|
||||
DatabaseTabWidget::~DatabaseTabWidget()
|
||||
@ -71,7 +70,16 @@ DatabaseTabWidget::~DatabaseTabWidget()
|
||||
|
||||
void DatabaseTabWidget::toggleTabbar()
|
||||
{
|
||||
tabBar()->setVisible(count() > 1);
|
||||
if (count() > 1) {
|
||||
if (!tabBar()->isVisible()) {
|
||||
tabBar()->show();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tabBar()->isVisible()) {
|
||||
tabBar()->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::newDatabase()
|
||||
@ -97,7 +105,7 @@ void DatabaseTabWidget::openDatabase()
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
|
||||
const QString& keyFile, const CompositeKey& key, int index)
|
||||
const QString& keyFile)
|
||||
{
|
||||
QFileInfo fileInfo(fileName);
|
||||
QString canonicalFilePath = fileInfo.canonicalFilePath();
|
||||
@ -141,17 +149,12 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
|
||||
dbStruct.filePath = fileInfo.absoluteFilePath();
|
||||
dbStruct.canonicalFilePath = canonicalFilePath;
|
||||
dbStruct.fileName = fileInfo.fileName();
|
||||
dbStruct.lastModified = fileInfo.lastModified();
|
||||
|
||||
insertDatabase(db, dbStruct, index);
|
||||
m_fileWatcher->addPath(dbStruct.filePath);
|
||||
insertDatabase(db, dbStruct);
|
||||
|
||||
updateRecentDatabases(dbStruct.filePath);
|
||||
updateLastDatabases(dbStruct.filePath);
|
||||
|
||||
if (!key.isEmpty()) {
|
||||
dbStruct.dbWidget->switchToOpenDatabase(dbStruct.filePath, key);
|
||||
}
|
||||
else if (!pw.isNull() || !keyFile.isEmpty()) {
|
||||
if (!pw.isNull() || !keyFile.isEmpty()) {
|
||||
dbStruct.dbWidget->switchToOpenDatabase(dbStruct.filePath, pw, keyFile);
|
||||
}
|
||||
else {
|
||||
@ -178,121 +181,6 @@ void DatabaseTabWidget::importKeePass1Database()
|
||||
dbStruct.dbWidget->switchToImportKeepass1(fileName);
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::fileChanged(const QString &fileName)
|
||||
{
|
||||
const bool wasEmpty = m_changedFiles.isEmpty();
|
||||
m_changedFiles.insert(fileName);
|
||||
bool found = false;
|
||||
Q_FOREACH (QString f, m_fileWatcher->files()) {
|
||||
if (f == fileName) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) m_fileWatcher->addPath(fileName);
|
||||
if (wasEmpty && !m_changedFiles.isEmpty())
|
||||
QTimer::singleShot(200, this, SLOT(checkReloadDatabases()));
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::expectFileChange(const DatabaseManagerStruct& dbStruct)
|
||||
{
|
||||
if (dbStruct.filePath.isEmpty())
|
||||
return;
|
||||
m_expectedFileChanges.insert(dbStruct.filePath);
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::unexpectFileChange(DatabaseManagerStruct& dbStruct)
|
||||
{
|
||||
if (dbStruct.filePath.isEmpty())
|
||||
return;
|
||||
m_expectedFileChanges.remove(dbStruct.filePath);
|
||||
dbStruct.lastModified = QFileInfo(dbStruct.filePath).lastModified();
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::checkReloadDatabases()
|
||||
{
|
||||
QSet<QString> changedFiles;
|
||||
|
||||
changedFiles = m_changedFiles.subtract(m_expectedFileChanges);
|
||||
m_changedFiles.clear();
|
||||
|
||||
if (changedFiles.isEmpty())
|
||||
return;
|
||||
|
||||
Q_FOREACH (DatabaseManagerStruct dbStruct, m_dbList) {
|
||||
QString filePath = dbStruct.filePath;
|
||||
Database * db = dbStruct.dbWidget->database();
|
||||
|
||||
if (!changedFiles.contains(filePath))
|
||||
continue;
|
||||
|
||||
QFileInfo fi(filePath);
|
||||
QDateTime lastModified = fi.lastModified();
|
||||
if (dbStruct.lastModified == lastModified)
|
||||
continue;
|
||||
|
||||
DatabaseWidget::Mode mode = dbStruct.dbWidget->currentMode();
|
||||
if (mode == DatabaseWidget::None || mode == DatabaseWidget::LockedMode || !db->hasKey())
|
||||
continue;
|
||||
|
||||
ReloadBehavior reloadBehavior = ReloadBehavior(config()->get("ReloadBehavior").toInt());
|
||||
if ( (reloadBehavior == AlwaysAsk)
|
||||
|| (reloadBehavior == ReloadUnmodified && (mode == DatabaseWidget::EditMode
|
||||
|| mode == DatabaseWidget::OpenMode))
|
||||
|| (reloadBehavior == ReloadUnmodified && dbStruct.modified)) {
|
||||
int res = QMessageBox::warning(this, fi.exists() ? tr("Database file changed") : tr("Database file removed"),
|
||||
tr("Do you want to discard your changes and reload?"),
|
||||
QMessageBox::Yes|QMessageBox::No);
|
||||
if (res == QMessageBox::No)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fi.exists()) {
|
||||
//Ignore/cancel all edits
|
||||
dbStruct.dbWidget->switchToView(false);
|
||||
dbStruct.modified = false;
|
||||
|
||||
//Save current group/entry
|
||||
Uuid currentGroup;
|
||||
if (Group* group = dbStruct.dbWidget->currentGroup())
|
||||
currentGroup = group->uuid();
|
||||
Uuid currentEntry;
|
||||
if (Entry* entry = dbStruct.dbWidget->entryView()->currentEntry())
|
||||
currentEntry = entry->uuid();
|
||||
QString searchText = dbStruct.dbWidget->searchText();
|
||||
bool caseSensitive = dbStruct.dbWidget->caseSensitiveSearch();
|
||||
bool allGroups = dbStruct.dbWidget->isAllGroupsSearch();
|
||||
|
||||
//Reload updated db
|
||||
CompositeKey key = db->key();
|
||||
int tabPos = databaseIndex(db);
|
||||
closeDatabase(db);
|
||||
openDatabase(filePath, QString(), QString(), key, tabPos);
|
||||
|
||||
//Restore current group/entry
|
||||
dbStruct = indexDatabaseManagerStruct(count() - 1);
|
||||
if (dbStruct.dbWidget && dbStruct.dbWidget->currentMode() == DatabaseWidget::ViewMode) {
|
||||
Database * db = dbStruct.dbWidget->database();
|
||||
if (!currentGroup.isNull())
|
||||
if (Group* group = db->resolveGroup(currentGroup))
|
||||
dbStruct.dbWidget->groupView()->setCurrentGroup(group);
|
||||
if (!searchText.isEmpty())
|
||||
dbStruct.dbWidget->search(searchText, caseSensitive, allGroups);
|
||||
if (!currentEntry.isNull())
|
||||
if (Entry* entry = db->resolveEntry(currentEntry))
|
||||
dbStruct.dbWidget->entryView()->setCurrentEntry(entry);
|
||||
}
|
||||
} else {
|
||||
//Ignore/cancel all edits
|
||||
dbStruct.dbWidget->switchToView(false);
|
||||
dbStruct.modified = false;
|
||||
|
||||
//Close database
|
||||
closeDatabase(dbStruct.dbWidget->database());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DatabaseTabWidget::closeDatabase(Database* db)
|
||||
{
|
||||
Q_ASSERT(db);
|
||||
@ -305,8 +193,7 @@ bool DatabaseTabWidget::closeDatabase(Database* db)
|
||||
if (dbName.right(1) == "*") {
|
||||
dbName.chop(1);
|
||||
}
|
||||
if ((dbStruct.dbWidget->currentMode() == DatabaseWidget::EditMode ||
|
||||
dbStruct.dbWidget->currentMode() == DatabaseWidget::OpenMode) && db->hasKey()) {
|
||||
if (dbStruct.dbWidget->isInEditMode() && db->hasKey()) {
|
||||
QMessageBox::StandardButton result =
|
||||
MessageBox::question(
|
||||
this, tr("Close?"),
|
||||
@ -348,7 +235,6 @@ void DatabaseTabWidget::deleteDatabase(Database* db)
|
||||
|
||||
int index = databaseIndex(db);
|
||||
|
||||
m_fileWatcher->removePath(dbStruct.filePath);
|
||||
removeTab(index);
|
||||
toggleTabbar();
|
||||
m_dbList.remove(db);
|
||||
@ -362,13 +248,6 @@ void DatabaseTabWidget::deleteDatabase(Database* db)
|
||||
|
||||
bool DatabaseTabWidget::closeAllDatabases()
|
||||
{
|
||||
QStringList reloadDatabases;
|
||||
if (config()->get("AutoReopenLastDatabases", false).toBool()) {
|
||||
for (int i = 0; i < count(); i ++)
|
||||
reloadDatabases << indexDatabaseManagerStruct(i).filePath;
|
||||
}
|
||||
config()->set("LastOpenDatabases", reloadDatabases);
|
||||
|
||||
while (!m_dbList.isEmpty()) {
|
||||
if (!closeDatabase()) {
|
||||
return false;
|
||||
@ -384,16 +263,12 @@ void DatabaseTabWidget::saveDatabase(Database* db)
|
||||
if (dbStruct.saveToFilename) {
|
||||
bool result = false;
|
||||
|
||||
expectFileChange(dbStruct);
|
||||
|
||||
QSaveFile saveFile(dbStruct.filePath);
|
||||
if (saveFile.open(QIODevice::WriteOnly)) {
|
||||
m_writer.writeDatabase(&saveFile, db);
|
||||
result = saveFile.commit();
|
||||
}
|
||||
|
||||
unexpectFileChange(dbStruct);
|
||||
|
||||
if (result) {
|
||||
dbStruct.modified = false;
|
||||
updateTabName(db);
|
||||
@ -411,12 +286,12 @@ void DatabaseTabWidget::saveDatabase(Database* db)
|
||||
void DatabaseTabWidget::saveDatabaseAs(Database* db)
|
||||
{
|
||||
DatabaseManagerStruct& dbStruct = m_dbList[db];
|
||||
QString oldFilePath;
|
||||
QString oldFileName;
|
||||
if (dbStruct.saveToFilename) {
|
||||
oldFilePath = dbStruct.filePath;
|
||||
oldFileName = dbStruct.filePath;
|
||||
}
|
||||
QString fileName = fileDialog()->getSaveFileName(this, tr("Save database as"),
|
||||
oldFilePath, tr("KeePass 2 Database").append(" (*.kdbx)"));
|
||||
oldFileName, tr("KeePass 2 Database").append(" (*.kdbx)"));
|
||||
if (!fileName.isEmpty()) {
|
||||
bool result = false;
|
||||
|
||||
@ -427,18 +302,15 @@ void DatabaseTabWidget::saveDatabaseAs(Database* db)
|
||||
}
|
||||
|
||||
if (result) {
|
||||
m_fileWatcher->removePath(oldFilePath);
|
||||
dbStruct.modified = false;
|
||||
dbStruct.saveToFilename = true;
|
||||
QFileInfo fileInfo(fileName);
|
||||
dbStruct.filePath = fileInfo.absoluteFilePath();
|
||||
dbStruct.canonicalFilePath = fileInfo.canonicalFilePath();
|
||||
dbStruct.fileName = fileInfo.fileName();
|
||||
dbStruct.lastModified = fileInfo.lastModified();
|
||||
dbStruct.dbWidget->updateFilename(dbStruct.filePath);
|
||||
updateTabName(db);
|
||||
updateRecentDatabases(dbStruct.filePath);
|
||||
m_fileWatcher->addPath(dbStruct.filePath);
|
||||
updateLastDatabases(dbStruct.filePath);
|
||||
}
|
||||
else {
|
||||
MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
|
||||
@ -609,13 +481,14 @@ Database* DatabaseTabWidget::databaseFromDatabaseWidget(DatabaseWidget* dbWidget
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct, int index)
|
||||
void DatabaseTabWidget::insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct)
|
||||
{
|
||||
m_dbList.insert(db, dbStruct);
|
||||
|
||||
index = insertTab(index, dbStruct.dbWidget, "");
|
||||
addTab(dbStruct.dbWidget, "");
|
||||
toggleTabbar();
|
||||
updateTabName(db);
|
||||
int index = databaseIndex(db);
|
||||
setCurrentIndex(index);
|
||||
connectDatabase(db);
|
||||
connect(dbStruct.dbWidget, SIGNAL(closeRequest()), SLOT(closeDatabaseFromSender()));
|
||||
@ -634,7 +507,7 @@ DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget()
|
||||
}
|
||||
}
|
||||
|
||||
bool DatabaseTabWidget::hasLockableDatabases()
|
||||
bool DatabaseTabWidget::hasLockableDatabases() const
|
||||
{
|
||||
QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
|
||||
while (i.hasNext()) {
|
||||
@ -687,7 +560,7 @@ void DatabaseTabWidget::modified()
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::updateRecentDatabases(const QString& filename)
|
||||
void DatabaseTabWidget::updateLastDatabases(const QString& filename)
|
||||
{
|
||||
if (!config()->get("RememberLastDatabases").toBool()) {
|
||||
config()->set("LastDatabases", QVariant());
|
||||
@ -719,6 +592,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) {
|
||||
|
@ -18,18 +18,16 @@
|
||||
#ifndef KEEPASSX_DATABASETABWIDGET_H
|
||||
#define KEEPASSX_DATABASETABWIDGET_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QTabWidget>
|
||||
#include <QSet>
|
||||
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "gui/DatabaseWidget.h"
|
||||
|
||||
class DatabaseWidget;
|
||||
class DatabaseWidgetStateSync;
|
||||
class DatabaseOpenWidget;
|
||||
class QFile;
|
||||
class QFileSystemWatcher;
|
||||
|
||||
struct DatabaseManagerStruct
|
||||
{
|
||||
@ -42,7 +40,6 @@ struct DatabaseManagerStruct
|
||||
bool saveToFilename;
|
||||
bool modified;
|
||||
bool readOnly;
|
||||
QDateTime lastModified;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(DatabaseManagerStruct, Q_MOVABLE_TYPE);
|
||||
@ -55,25 +52,16 @@ public:
|
||||
explicit DatabaseTabWidget(QWidget* parent = Q_NULLPTR);
|
||||
~DatabaseTabWidget();
|
||||
void openDatabase(const QString& fileName, const QString& pw = QString(),
|
||||
const QString& keyFile = QString(), const CompositeKey& key = CompositeKey(),
|
||||
int index = -1);
|
||||
const QString& keyFile = QString());
|
||||
DatabaseWidget* currentDatabaseWidget();
|
||||
bool hasLockableDatabases();
|
||||
bool hasLockableDatabases() const;
|
||||
|
||||
static const int LastDatabasesCount;
|
||||
|
||||
enum ReloadBehavior {
|
||||
AlwaysAsk,
|
||||
ReloadUnmodified,
|
||||
IgnoreAll
|
||||
};
|
||||
|
||||
public Q_SLOTS:
|
||||
void newDatabase();
|
||||
void openDatabase();
|
||||
void importKeePass1Database();
|
||||
void fileChanged(const QString& fileName);
|
||||
void checkReloadDatabases();
|
||||
void saveDatabase(int index = -1);
|
||||
void saveDatabaseAs(int index = -1);
|
||||
bool closeDatabase(int index = -1);
|
||||
@ -88,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);
|
||||
@ -96,6 +85,7 @@ private Q_SLOTS:
|
||||
void modified();
|
||||
void toggleTabbar();
|
||||
void changeDatabase(Database* newDb);
|
||||
void emitActivateDatabaseChanged();
|
||||
|
||||
private:
|
||||
void saveDatabase(Database* db);
|
||||
@ -106,17 +96,13 @@ private:
|
||||
Database* indexDatabase(int index);
|
||||
DatabaseManagerStruct indexDatabaseManagerStruct(int index);
|
||||
Database* databaseFromDatabaseWidget(DatabaseWidget* dbWidget);
|
||||
void insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct, int index = -1);
|
||||
void updateRecentDatabases(const QString& filename);
|
||||
void insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct);
|
||||
void updateLastDatabases(const QString& filename);
|
||||
void connectDatabase(Database* newDb, Database* oldDb = Q_NULLPTR);
|
||||
void expectFileChange(const DatabaseManagerStruct& dbStruct);
|
||||
void unexpectFileChange(DatabaseManagerStruct& dbStruct);
|
||||
|
||||
KeePass2Writer m_writer;
|
||||
QHash<Database*, DatabaseManagerStruct> m_dbList;
|
||||
QSet<QString> m_changedFiles;
|
||||
QSet<QString> m_expectedFileChanges;
|
||||
QFileSystemWatcher* m_fileWatcher;
|
||||
DatabaseWidgetStateSync* m_dbWidgetSateSync;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_DATABASETABWIDGET_H
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
#include "autotype/AutoType.h"
|
||||
#include "core/Config.h"
|
||||
#include "core/EntrySearcher.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
@ -52,8 +53,6 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
||||
, m_newGroup(Q_NULLPTR)
|
||||
, m_newEntry(Q_NULLPTR)
|
||||
, m_newParent(Q_NULLPTR)
|
||||
, m_searchAllGroups(false)
|
||||
, m_searchSensitivity(Qt::CaseInsensitive)
|
||||
{
|
||||
m_searchUi->setupUi(m_searchWidget);
|
||||
|
||||
@ -62,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)),
|
||||
@ -80,22 +80,13 @@ 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);
|
||||
m_searchUi->closeSearchButton->setDefaultAction(closeAction);
|
||||
m_searchUi->closeSearchButton->setShortcut(Qt::Key_Escape);
|
||||
int iconsize = style()->pixelMetric(QStyle::PM_SmallIconSize);
|
||||
m_searchUi->closeSearchButton->setIconSize(QSize(iconsize,iconsize));
|
||||
m_searchWidget->hide();
|
||||
m_searchUi->caseSensitiveCheckBox->setVisible(false);
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget);
|
||||
vLayout->setMargin(0);
|
||||
@ -104,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);
|
||||
|
||||
layout->addWidget(splitter);
|
||||
m_splitter->addWidget(m_groupView);
|
||||
m_splitter->addWidget(rightHandSideWidget);
|
||||
|
||||
m_splitter->setStretchFactor(0, 30);
|
||||
m_splitter->setStretchFactor(1, 70);
|
||||
|
||||
layout->addWidget(m_splitter);
|
||||
m_mainWidget->setLayout(layout);
|
||||
|
||||
m_editEntryWidget = new EditEntryWidget();
|
||||
@ -139,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*)));
|
||||
@ -155,7 +155,11 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
||||
connect(m_keepass1OpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
|
||||
connect(m_unlockDatabaseWidget, SIGNAL(editFinished(bool)), SLOT(unlockDatabase(bool)));
|
||||
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
|
||||
connect(m_searchUi->searchResults, SIGNAL(linkActivated(QString)), this, SLOT(onLinkActivated(QString)));
|
||||
connect(m_searchUi->searchEdit, SIGNAL(textChanged(QString)), this, SLOT(startSearchTimer()));
|
||||
connect(m_searchUi->caseSensitiveCheckBox, SIGNAL(toggled(bool)), this, SLOT(startSearch()));
|
||||
connect(m_searchUi->searchCurrentRadioButton, SIGNAL(toggled(bool)), this, SLOT(startSearch()));
|
||||
connect(m_searchUi->searchRootRadioButton, SIGNAL(toggled(bool)), this, SLOT(startSearch()));
|
||||
connect(m_searchUi->searchEdit, SIGNAL(returnPressed()), m_entryView, SLOT(setFocus()));
|
||||
connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(search()));
|
||||
connect(closeAction, SIGNAL(triggered()), this, SLOT(closeSearch()));
|
||||
|
||||
@ -166,7 +170,7 @@ DatabaseWidget::~DatabaseWidget()
|
||||
{
|
||||
}
|
||||
|
||||
DatabaseWidget::Mode DatabaseWidget::currentMode()
|
||||
DatabaseWidget::Mode DatabaseWidget::currentMode() const
|
||||
{
|
||||
if (currentWidget() == Q_NULLPTR) {
|
||||
return DatabaseWidget::None;
|
||||
@ -185,21 +189,56 @@ DatabaseWidget::Mode DatabaseWidget::currentMode()
|
||||
}
|
||||
}
|
||||
|
||||
bool DatabaseWidget::isInEditMode() const
|
||||
{
|
||||
if (currentMode() == DatabaseWidget::LockedMode) {
|
||||
return m_widgetBeforeLock != Q_NULLPTR
|
||||
&& m_widgetBeforeLock != m_mainWidget
|
||||
&& m_widgetBeforeLock != m_unlockDatabaseWidget;
|
||||
}
|
||||
else {
|
||||
return currentMode() == DatabaseWidget::EditMode;
|
||||
}
|
||||
}
|
||||
|
||||
QList<int> DatabaseWidget::splitterSizes() const
|
||||
{
|
||||
return m_splitter->sizes();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setSplitterSizes(const QList<int>& sizes)
|
||||
{
|
||||
m_splitter->setSizes(sizes);
|
||||
}
|
||||
|
||||
QList<int> DatabaseWidget::entryHeaderViewSizes() const
|
||||
{
|
||||
QList<int> 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<int>& 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());
|
||||
}
|
||||
|
||||
GroupView* DatabaseWidget::groupView()
|
||||
{
|
||||
return m_groupView;
|
||||
}
|
||||
|
||||
EntryView* DatabaseWidget::entryView()
|
||||
{
|
||||
return m_entryView;
|
||||
}
|
||||
|
||||
Database* DatabaseWidget::database()
|
||||
{
|
||||
return m_db;
|
||||
@ -296,8 +335,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;
|
||||
@ -429,7 +467,7 @@ void DatabaseWidget::createGroup()
|
||||
void DatabaseWidget::deleteGroup()
|
||||
{
|
||||
Group* currentGroup = m_groupView->currentGroup();
|
||||
if (!currentGroup || !canDeleteCurrentGoup()) {
|
||||
if (!currentGroup || !canDeleteCurrentGroup()) {
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
@ -597,7 +635,7 @@ void DatabaseWidget::unlockDatabase(bool accepted)
|
||||
Q_ASSERT(accepted);
|
||||
Q_UNUSED(accepted);
|
||||
|
||||
setCurrentWidget(widgetBeforeLock);
|
||||
setCurrentWidget(m_widgetBeforeLock);
|
||||
Q_EMIT unlockedDatabase();
|
||||
}
|
||||
|
||||
@ -660,13 +698,6 @@ void DatabaseWidget::switchToOpenDatabase(const QString& fileName, const QString
|
||||
m_databaseOpenWidget->enterKey(password, keyFile);
|
||||
}
|
||||
|
||||
void DatabaseWidget::switchToOpenDatabase(const QString &fileName, const CompositeKey& masterKey)
|
||||
{
|
||||
updateFilename(fileName);
|
||||
switchToOpenDatabase(fileName);
|
||||
m_databaseOpenWidget->enterKey(masterKey);
|
||||
}
|
||||
|
||||
void DatabaseWidget::switchToImportKeepass1(const QString& fileName)
|
||||
{
|
||||
updateFilename(fileName);
|
||||
@ -674,117 +705,100 @@ void DatabaseWidget::switchToImportKeepass1(const QString& fileName)
|
||||
setCurrentWidget(m_keepass1OpenWidget);
|
||||
}
|
||||
|
||||
void DatabaseWidget::search(const QString& searchString, bool caseSensitive, bool allGroups)
|
||||
void DatabaseWidget::openSearch()
|
||||
{
|
||||
m_searchSensitivity = caseSensitive ? Qt::CaseSensitive
|
||||
: Qt::CaseInsensitive;
|
||||
m_searchAllGroups = allGroups;
|
||||
search(searchString);
|
||||
}
|
||||
if (isInSearchMode()) {
|
||||
m_searchUi->searchEdit->selectAll();
|
||||
|
||||
void DatabaseWidget::search(const QString& text)
|
||||
{
|
||||
if (text.isEmpty()) {
|
||||
if (m_entryView->inEntryListMode())
|
||||
closeSearch();
|
||||
}
|
||||
else if (m_entryView->inEntryListMode()) {
|
||||
m_searchText = text;
|
||||
startSearchTimer();
|
||||
if (!m_searchUi->searchEdit->hasFocus()) {
|
||||
m_searchUi->searchEdit->setFocus();
|
||||
// make sure the search action is checked again
|
||||
emitCurrentModeChanged();
|
||||
}
|
||||
}
|
||||
else {
|
||||
showSearch(text);
|
||||
}
|
||||
}
|
||||
|
||||
bool DatabaseWidget::caseSensitiveSearch() const
|
||||
{
|
||||
return m_searchSensitivity == Qt::CaseSensitive;
|
||||
}
|
||||
|
||||
void DatabaseWidget::setCaseSensitiveSearch(bool caseSensitive)
|
||||
{
|
||||
if (caseSensitive != caseSensitiveSearch()) {
|
||||
m_searchSensitivity = caseSensitive ? Qt::CaseSensitive
|
||||
: Qt::CaseInsensitive;
|
||||
if (m_entryView->inEntryListMode())
|
||||
startSearchTimer();
|
||||
}
|
||||
}
|
||||
|
||||
bool DatabaseWidget::isAllGroupsSearch() const
|
||||
{
|
||||
return m_searchAllGroups;
|
||||
}
|
||||
|
||||
bool DatabaseWidget::canChooseSearchScope() const
|
||||
{
|
||||
return currentGroup() != m_db->rootGroup();
|
||||
}
|
||||
|
||||
Group*DatabaseWidget::currentGroup() const
|
||||
{
|
||||
return m_entryView->inEntryListMode() ? m_lastGroup
|
||||
: m_groupView->currentGroup();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setAllGroupsSearch(bool allGroups)
|
||||
{
|
||||
if (allGroups != isAllGroupsSearch()) {
|
||||
m_searchAllGroups = allGroups;
|
||||
if (m_entryView->inEntryListMode())
|
||||
startSearchTimer();
|
||||
showSearch();
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseWidget::closeSearch()
|
||||
{
|
||||
Q_ASSERT(m_lastGroup);
|
||||
m_searchTimer->stop();
|
||||
|
||||
Q_EMIT listModeAboutToActivate();
|
||||
|
||||
m_groupView->setCurrentGroup(m_lastGroup);
|
||||
m_searchTimer->stop();
|
||||
|
||||
Q_EMIT listModeActivated();
|
||||
}
|
||||
|
||||
void DatabaseWidget::showSearch(const QString & searchString)
|
||||
void DatabaseWidget::showSearch()
|
||||
{
|
||||
m_searchText = searchString;
|
||||
Q_EMIT searchModeAboutToActivate();
|
||||
|
||||
m_searchUi->searchEdit->blockSignals(true);
|
||||
m_searchUi->searchEdit->clear();
|
||||
m_searchUi->searchEdit->blockSignals(false);
|
||||
|
||||
m_searchUi->searchCurrentRadioButton->blockSignals(true);
|
||||
m_searchUi->searchRootRadioButton->blockSignals(true);
|
||||
m_searchUi->searchRootRadioButton->setChecked(true);
|
||||
m_searchUi->searchCurrentRadioButton->blockSignals(false);
|
||||
m_searchUi->searchRootRadioButton->blockSignals(false);
|
||||
|
||||
m_lastGroup = m_groupView->currentGroup();
|
||||
|
||||
Q_ASSERT(m_lastGroup);
|
||||
|
||||
if (m_lastGroup == m_db->rootGroup()) {
|
||||
m_searchUi->optionsWidget->hide();
|
||||
m_searchUi->searchCurrentRadioButton->hide();
|
||||
m_searchUi->searchRootRadioButton->hide();
|
||||
}
|
||||
else {
|
||||
m_searchUi->optionsWidget->show();
|
||||
m_searchUi->searchCurrentRadioButton->show();
|
||||
m_searchUi->searchRootRadioButton->show();
|
||||
m_searchUi->searchCurrentRadioButton->setText(tr("Current group")
|
||||
.append(" (")
|
||||
.append(m_lastGroup->name())
|
||||
.append(")"));
|
||||
}
|
||||
m_groupView->setCurrentIndex(QModelIndex());
|
||||
|
||||
m_searchWidget->show();
|
||||
search();
|
||||
}
|
||||
m_searchUi->searchEdit->setFocus();
|
||||
|
||||
void DatabaseWidget::onLinkActivated(const QString& link)
|
||||
{
|
||||
if (link == "searchAll")
|
||||
setAllGroupsSearch(true);
|
||||
else if (link == "searchCurrent")
|
||||
setAllGroupsSearch(false);
|
||||
Q_EMIT searchModeActivated();
|
||||
}
|
||||
|
||||
void DatabaseWidget::search()
|
||||
{
|
||||
Q_ASSERT(m_lastGroup);
|
||||
|
||||
Group* searchGroup = m_searchAllGroups ? m_db->rootGroup()
|
||||
: m_lastGroup;
|
||||
QList<Entry*> searchResult = searchGroup->search(m_searchText, m_searchSensitivity);
|
||||
|
||||
QString message;
|
||||
switch(searchResult.count()) {
|
||||
case 0: message = tr("No result found"); break;
|
||||
case 1: message = tr("1 result found"); break;
|
||||
default: message = tr("%1 results found").arg(searchResult.count()); break;
|
||||
Group* searchGroup;
|
||||
if (m_searchUi->searchCurrentRadioButton->isChecked()) {
|
||||
searchGroup = m_lastGroup;
|
||||
}
|
||||
if (searchGroup != m_db->rootGroup())
|
||||
message += tr(" in \"%1\". <a href='searchAll'>Search all groups...</a>").arg(searchGroup->name());
|
||||
else if (m_lastGroup != m_db->rootGroup())
|
||||
message += tr(". <a href='searchCurrent'>Search in \"%1\"...</a>").arg(m_lastGroup->name());
|
||||
else
|
||||
message += tr(".");
|
||||
m_searchUi->searchResults->setText(message);
|
||||
else if (m_searchUi->searchRootRadioButton->isChecked()) {
|
||||
searchGroup = m_db->rootGroup();
|
||||
}
|
||||
else {
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Qt::CaseSensitivity sensitivity;
|
||||
if (m_searchUi->caseSensitiveCheckBox->isChecked()) {
|
||||
sensitivity = Qt::CaseSensitive;
|
||||
}
|
||||
else {
|
||||
sensitivity = Qt::CaseInsensitive;
|
||||
}
|
||||
|
||||
QList<Entry*> searchResult = EntrySearcher().search(m_searchUi->searchEdit->text(), searchGroup, sensitivity);
|
||||
|
||||
m_entryView->setEntryList(searchResult);
|
||||
}
|
||||
@ -799,9 +813,6 @@ void DatabaseWidget::startSearchTimer()
|
||||
|
||||
void DatabaseWidget::startSearch()
|
||||
{
|
||||
if (!isInSearchMode())
|
||||
return;
|
||||
|
||||
if (!m_searchTimer->isActive()) {
|
||||
m_searchTimer->stop();
|
||||
}
|
||||
@ -818,12 +829,12 @@ 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();
|
||||
@ -835,11 +846,6 @@ bool DatabaseWidget::isInSearchMode() const
|
||||
return m_entryView->inEntryListMode();
|
||||
}
|
||||
|
||||
QString DatabaseWidget::searchText() const
|
||||
{
|
||||
return m_entryView->inEntryListMode() ? m_searchText : QString();
|
||||
}
|
||||
|
||||
void DatabaseWidget::clearLastGroup(Group* group)
|
||||
{
|
||||
if (group) {
|
||||
@ -852,7 +858,7 @@ void DatabaseWidget::lock()
|
||||
{
|
||||
Q_ASSERT(currentMode() != DatabaseWidget::LockedMode);
|
||||
|
||||
widgetBeforeLock = currentWidget();
|
||||
m_widgetBeforeLock = currentWidget();
|
||||
m_unlockDatabaseWidget->load(m_filename, m_db);
|
||||
setCurrentWidget(m_unlockDatabaseWidget);
|
||||
}
|
||||
@ -861,3 +867,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;
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ class GroupView;
|
||||
class KeePass1OpenWidget;
|
||||
class QFile;
|
||||
class QMenu;
|
||||
class QSplitter;
|
||||
class UnlockDatabaseWidget;
|
||||
class CompositeKey;
|
||||
|
||||
namespace Ui {
|
||||
class SearchWidget;
|
||||
@ -61,24 +61,24 @@ public:
|
||||
|
||||
explicit DatabaseWidget(Database* db, QWidget* parent = Q_NULLPTR);
|
||||
~DatabaseWidget();
|
||||
GroupView* groupView();
|
||||
EntryView* entryView();
|
||||
Database* database();
|
||||
bool dbHasKey();
|
||||
bool canDeleteCurrentGoup();
|
||||
bool dbHasKey() const;
|
||||
bool canDeleteCurrentGroup() const;
|
||||
bool isInSearchMode() const;
|
||||
QString searchText() const;
|
||||
bool caseSensitiveSearch() const;
|
||||
bool isAllGroupsSearch() const;
|
||||
bool canChooseSearchScope() const;
|
||||
Group* currentGroup() 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);
|
||||
void search(const QString & searchString, bool caseSensitive, bool allGroups);
|
||||
int numberOfSelectedEntries() const;
|
||||
QStringList customEntryAttributes() const;
|
||||
bool isGroupSelected() const;
|
||||
bool isInEditMode() const;
|
||||
QList<int> splitterSizes() const;
|
||||
void setSplitterSizes(const QList<int>& sizes);
|
||||
QList<int> entryHeaderViewSizes() const;
|
||||
void setEntryViewHeaderSizes(const QList<int>& sizes);
|
||||
|
||||
Q_SIGNALS:
|
||||
void closeRequest();
|
||||
@ -89,6 +89,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();
|
||||
@ -111,24 +117,19 @@ public Q_SLOTS:
|
||||
void switchToDatabaseSettings();
|
||||
void switchToOpenDatabase(const QString& fileName);
|
||||
void switchToOpenDatabase(const QString& fileName, const QString& password, const QString& keyFile);
|
||||
void switchToOpenDatabase(const QString &fileName, const CompositeKey &masterKey);
|
||||
void switchToImportKeepass1(const QString& fileName);
|
||||
void switchToView(bool accepted);
|
||||
void search(const QString & searchString);
|
||||
void setCaseSensitiveSearch(bool caseSensitive);
|
||||
void setAllGroupsSearch(bool allGroups);
|
||||
void emitGroupContextMenuRequested(const QPoint& pos);
|
||||
void emitEntryContextMenuRequested(const QPoint& pos);
|
||||
void openSearch();
|
||||
|
||||
private Q_SLOTS:
|
||||
void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column);
|
||||
void onLinkActivated(const QString& link);
|
||||
void showSearch(const QString & searchString = QString());
|
||||
void switchBackToEntryEdit();
|
||||
void switchToView(bool accepted);
|
||||
void switchToHistoryView(Entry* entry);
|
||||
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);
|
||||
@ -137,6 +138,7 @@ private Q_SLOTS:
|
||||
void search();
|
||||
void startSearch();
|
||||
void startSearchTimer();
|
||||
void showSearch();
|
||||
void closeSearch();
|
||||
|
||||
private:
|
||||
@ -155,6 +157,7 @@ private:
|
||||
DatabaseOpenWidget* m_databaseOpenWidget;
|
||||
KeePass1OpenWidget* m_keepass1OpenWidget;
|
||||
UnlockDatabaseWidget* m_unlockDatabaseWidget;
|
||||
QSplitter* m_splitter;
|
||||
GroupView* m_groupView;
|
||||
EntryView* m_entryView;
|
||||
Group* m_newGroup;
|
||||
@ -162,11 +165,8 @@ private:
|
||||
Group* m_newParent;
|
||||
Group* m_lastGroup;
|
||||
QTimer* m_searchTimer;
|
||||
QWidget* widgetBeforeLock;
|
||||
QWidget* m_widgetBeforeLock;
|
||||
QString m_filename;
|
||||
QString m_searchText;
|
||||
bool m_searchAllGroups;
|
||||
Qt::CaseSensitivity m_searchSensitivity;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_DATABASEWIDGET_H
|
||||
|
154
src/gui/DatabaseWidgetStateSync.cpp
Normal file
154
src/gui/DatabaseWidgetStateSync.cpp
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<int> DatabaseWidgetStateSync::variantToIntList(const QVariant& variant)
|
||||
{
|
||||
QVariantList list = variant.toList();
|
||||
QList<int> 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<int>& list)
|
||||
{
|
||||
QVariantList result;
|
||||
|
||||
Q_FOREACH (int value, list) {
|
||||
result.append(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
54
src/gui/DatabaseWidgetStateSync.h
Normal file
54
src/gui/DatabaseWidgetStateSync.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<int> variantToIntList(const QVariant& variant);
|
||||
static QVariant intListToVariant(const QList<int>& list);
|
||||
|
||||
DatabaseWidget* m_activeDbWidget;
|
||||
|
||||
bool m_blockUpdates;
|
||||
QList<int> m_splitterSizes;
|
||||
QList<int> m_columnSizesList;
|
||||
QList<int> m_columnSizesSearch;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_HEADERVIEWSYNC_H
|
@ -271,8 +271,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -20,79 +20,34 @@
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QShortcut>
|
||||
#include <QtGui/QLineEdit>
|
||||
|
||||
#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"
|
||||
|
||||
#include "http/Service.h"
|
||||
#include "http/HttpSettings.h"
|
||||
#include "http/OptionDialog.h"
|
||||
#include "gui/SettingsWidget.h"
|
||||
#include "gui/qocoa/qsearchfield.h"
|
||||
|
||||
class HttpPlugin: public ISettingsPage {
|
||||
public:
|
||||
HttpPlugin(DatabaseTabWidget * tabWidget) {
|
||||
m_service = new Service(tabWidget);
|
||||
}
|
||||
virtual ~HttpPlugin() {
|
||||
//delete m_service;
|
||||
}
|
||||
virtual QString name() {
|
||||
return QObject::tr("Http");
|
||||
}
|
||||
virtual QWidget * createWidget() {
|
||||
OptionDialog * dlg = new OptionDialog();
|
||||
QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_service, SLOT(removeSharedEncryptionKeys()));
|
||||
QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_service, SLOT(removeStoredPermissions()));
|
||||
return dlg;
|
||||
}
|
||||
virtual void loadSettings(QWidget * widget) {
|
||||
qobject_cast<OptionDialog*>(widget)->loadSettings();
|
||||
}
|
||||
virtual void saveSettings(QWidget * widget) {
|
||||
qobject_cast<OptionDialog*>(widget)->saveSettings();
|
||||
if (HttpSettings::isEnabled())
|
||||
m_service->start();
|
||||
else
|
||||
m_service->stop();
|
||||
}
|
||||
private:
|
||||
Service *m_service;
|
||||
};
|
||||
|
||||
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_ui->settingsWidget->addSettingsPage(new HttpPlugin(m_ui->tabWidget));
|
||||
m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size();
|
||||
|
||||
restoreGeometry(config()->get("GUI/MainWindowGeometry").toByteArray());
|
||||
|
||||
setWindowIcon(filePath()->applicationIcon());
|
||||
QAction* toggleViewAction = m_ui->toolBar->toggleViewAction();
|
||||
toggleViewAction->setText(tr("Show toolbar"));
|
||||
m_ui->menuView->addAction(toggleViewAction);
|
||||
int toolbarIconSize = config()->get("ToolbarIconSize", 20).toInt();
|
||||
setToolbarIconSize(toolbarIconSize);
|
||||
bool showToolbar = config()->get("ShowToolbar").toBool();
|
||||
m_ui->toolBar->setVisible(showToolbar);
|
||||
connect(m_ui->toolBar, SIGNAL(visibilityChanged(bool)), this, SLOT(saveToolbarState(bool)));
|
||||
connect(m_ui->actionToolbarIconSize16, SIGNAL(triggered()), this, SLOT(setToolbarIconSize16()));
|
||||
connect(m_ui->actionToolbarIconSize22, SIGNAL(triggered()), this, SLOT(setToolbarIconSize22()));
|
||||
connect(m_ui->actionToolbarIconSize28, SIGNAL(triggered()), this, SLOT(setToolbarIconSize28()));
|
||||
|
||||
m_clearHistoryAction = new QAction("Clear history", m_ui->menuFile);
|
||||
m_lastDatabasesActions = new QActionGroup(m_ui->menuRecentDatabases);
|
||||
@ -124,19 +79,13 @@ MainWindow::MainWindow()
|
||||
setShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W);
|
||||
m_ui->actionLockDatabases->setShortcut(Qt::CTRL + Qt::Key_L);
|
||||
setShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q);
|
||||
//TODO: do not register shortcut on Q_OS_MAC, if this is done automatically??
|
||||
const QKeySequence seq = !QKeySequence::keyBindings(QKeySequence::Find).isEmpty()
|
||||
? QKeySequence::Find
|
||||
: QKeySequence(Qt::CTRL + Qt::Key_F);
|
||||
connect(new QShortcut(seq, this), SIGNAL(activated()), m_ui->searchField, SLOT(setFocus()));
|
||||
m_ui->searchField->setContentsMargins(5,0,5,0);
|
||||
setShortcut(m_ui->actionSearch, QKeySequence::Find, Qt::CTRL + Qt::Key_F);
|
||||
m_ui->actionEntryNew->setShortcut(Qt::CTRL + Qt::Key_N);
|
||||
m_ui->actionEntryEdit->setShortcut(Qt::CTRL + Qt::Key_E);
|
||||
m_ui->actionEntryDelete->setShortcut(Qt::CTRL + Qt::Key_D);
|
||||
m_ui->actionEntryClone->setShortcut(Qt::CTRL + Qt::Key_K);
|
||||
m_ui->actionEntryCopyUsername->setShortcut(Qt::CTRL + Qt::Key_B);
|
||||
m_ui->actionEntryCopyPassword->setShortcut(Qt::CTRL + Qt::Key_C);
|
||||
m_ui->actionEntryCopyURL->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_U);
|
||||
setShortcut(m_ui->actionEntryAutoType, QKeySequence::Paste, Qt::CTRL + Qt::Key_V);
|
||||
m_ui->actionEntryOpenUrl->setShortcut(Qt::CTRL + Qt::Key_U);
|
||||
|
||||
@ -170,6 +119,8 @@ MainWindow::MainWindow()
|
||||
|
||||
m_ui->actionAbout->setIcon(filePath()->icon("actions", "help-about"));
|
||||
|
||||
m_ui->actionSearch->setIcon(filePath()->icon("actions", "system-search"));
|
||||
|
||||
m_actionMultiplexer.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)),
|
||||
this, SLOT(setMenuActionState(DatabaseWidget::Mode)));
|
||||
m_actionMultiplexer.connect(SIGNAL(groupChanged()),
|
||||
@ -249,25 +200,10 @@ MainWindow::MainWindow()
|
||||
|
||||
connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog()));
|
||||
|
||||
m_ui->searchField->setPlaceholderText(tr("Type to search"));
|
||||
m_ui->searchField->setEnabled(false);
|
||||
m_ui->toolBar->addWidget(m_ui->mySpacer);
|
||||
m_ui->toolBar->addWidget(m_ui->searchField);
|
||||
m_actionMultiplexer.connect(m_ui->searchField, SIGNAL(textChanged(QString)),
|
||||
SLOT(search(QString)));
|
||||
QMenu* searchMenu = new QMenu(this);
|
||||
searchMenu->addAction(m_ui->actionFindCaseSensitive);
|
||||
searchMenu->addSeparator();
|
||||
searchMenu->addAction(m_ui->actionFindCurrentGroup);
|
||||
searchMenu->addAction(m_ui->actionFindRootGroup);
|
||||
m_ui->searchField->setMenu(searchMenu);
|
||||
QActionGroup* group = new QActionGroup(this);
|
||||
group->addAction(m_ui->actionFindCurrentGroup);
|
||||
group->addAction(m_ui->actionFindRootGroup);
|
||||
m_actionMultiplexer.connect(m_ui->actionFindCaseSensitive, SIGNAL(toggled(bool)),
|
||||
SLOT(setCaseSensitiveSearch(bool)));
|
||||
m_actionMultiplexer.connect(m_ui->actionFindRootGroup, SIGNAL(toggled(bool)),
|
||||
SLOT(setAllGroupsSearch(bool)));
|
||||
m_actionMultiplexer.connect(m_ui->actionSearch, SIGNAL(triggered()),
|
||||
SLOT(openSearch()));
|
||||
|
||||
updateTrayIcon();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
@ -294,17 +230,16 @@ void MainWindow::updateCopyAttributesMenu()
|
||||
return;
|
||||
}
|
||||
|
||||
Entry* entry = dbWidget->entryView()->currentEntry();
|
||||
if (!entry || !dbWidget->entryView()->isSingleEntrySelected()) {
|
||||
if (dbWidget->numberOfSelectedEntries() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QAction*> 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);
|
||||
}
|
||||
@ -320,40 +255,11 @@ void MainWindow::clearLastDatabases()
|
||||
config()->set("LastDatabases", QVariant());
|
||||
}
|
||||
|
||||
void MainWindow::changeEvent(QEvent *e)
|
||||
{
|
||||
QMainWindow::changeEvent(e);
|
||||
if (e->type() == QEvent::ActivationChange) {
|
||||
if (isActiveWindow())
|
||||
m_ui->tabWidget->checkReloadDatabases();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::openDatabase(const QString& fileName, const QString& pw, const QString& keyFile)
|
||||
{
|
||||
m_ui->tabWidget->openDatabase(fileName, pw, keyFile);
|
||||
}
|
||||
|
||||
void MainWindow::updateSearchField(DatabaseWidget* dbWidget)
|
||||
{
|
||||
bool enabled = dbWidget != NULL;
|
||||
|
||||
m_ui->actionFindCaseSensitive->setChecked(enabled && dbWidget->caseSensitiveSearch());
|
||||
|
||||
m_ui->actionFindCurrentGroup->setEnabled(enabled && dbWidget->canChooseSearchScope());
|
||||
m_ui->actionFindRootGroup->setEnabled(enabled && dbWidget->canChooseSearchScope());
|
||||
if (enabled && dbWidget->isAllGroupsSearch())
|
||||
m_ui->actionFindRootGroup->setChecked(true);
|
||||
else
|
||||
m_ui->actionFindCurrentGroup->setChecked(true);
|
||||
|
||||
m_ui->searchField->setEnabled(enabled);
|
||||
if (enabled && dbWidget->isInSearchMode())
|
||||
m_ui->searchField->setText(dbWidget->searchText());
|
||||
else
|
||||
m_ui->searchField->clear();
|
||||
}
|
||||
|
||||
void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
{
|
||||
bool inDatabaseTabWidget = (m_ui->stackedWidget->currentIndex() == 0);
|
||||
@ -370,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);
|
||||
@ -388,8 +294,9 @@ 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());
|
||||
updateSearchField(dbWidget);
|
||||
m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
|
||||
// TODO: get checked state from db widget
|
||||
m_ui->actionSearch->setEnabled(true);
|
||||
m_ui->actionChangeMasterKey->setEnabled(true);
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(true);
|
||||
m_ui->actionDatabaseSave->setEnabled(true);
|
||||
@ -398,7 +305,6 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
}
|
||||
case DatabaseWidget::EditMode:
|
||||
case DatabaseWidget::LockedMode:
|
||||
case DatabaseWidget::OpenMode:
|
||||
Q_FOREACH (QAction* action, m_ui->menuEntries->actions()) {
|
||||
action->setEnabled(false);
|
||||
}
|
||||
@ -406,9 +312,14 @@ 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);
|
||||
|
||||
updateSearchField();
|
||||
m_ui->actionSearch->setEnabled(false);
|
||||
m_ui->actionChangeMasterKey->setEnabled(false);
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(false);
|
||||
m_ui->actionDatabaseSave->setEnabled(false);
|
||||
@ -427,9 +338,14 @@ 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);
|
||||
|
||||
updateSearchField();
|
||||
m_ui->actionSearch->setEnabled(false);
|
||||
m_ui->actionChangeMasterKey->setEnabled(false);
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(false);
|
||||
m_ui->actionDatabaseSave->setEnabled(false);
|
||||
@ -513,15 +429,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()
|
||||
@ -551,6 +481,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);
|
||||
@ -566,30 +525,6 @@ void MainWindow::saveToolbarState(bool value)
|
||||
config()->set("ShowToolbar", value);
|
||||
}
|
||||
|
||||
void MainWindow::setToolbarIconSize(int size)
|
||||
{
|
||||
config()->set("ToolbarIconSize", size);
|
||||
m_ui->toolBar->setIconSize(QSize(size, size));
|
||||
m_ui->actionToolbarIconSize16->setChecked(size == 16);
|
||||
m_ui->actionToolbarIconSize22->setChecked(size == 22);
|
||||
m_ui->actionToolbarIconSize28->setChecked(size == 28);
|
||||
}
|
||||
|
||||
void MainWindow::setToolbarIconSize16()
|
||||
{
|
||||
setToolbarIconSize(16);
|
||||
}
|
||||
|
||||
void MainWindow::setToolbarIconSize22()
|
||||
{
|
||||
setToolbarIconSize(22);
|
||||
}
|
||||
|
||||
void MainWindow::setToolbarIconSize28()
|
||||
{
|
||||
setToolbarIconSize(28);
|
||||
}
|
||||
|
||||
void MainWindow::setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback)
|
||||
{
|
||||
if (!QKeySequence::keyBindings(standard).isEmpty()) {
|
||||
@ -619,4 +554,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();
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <QActionGroup>
|
||||
#include <QMainWindow>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include "core/SignalMultiplexer.h"
|
||||
#include "gui/DatabaseWidget.h"
|
||||
@ -43,8 +44,8 @@ public Q_SLOTS:
|
||||
const QString& keyFile = QString());
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *e);
|
||||
void closeEvent(QCloseEvent* event) Q_DECL_OVERRIDE;
|
||||
void closeEvent(QCloseEvent* event) Q_DECL_OVERRIDE;
|
||||
void changeEvent(QEvent* event) Q_DECL_OVERRIDE;
|
||||
|
||||
private Q_SLOTS:
|
||||
void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::None);
|
||||
@ -62,19 +63,18 @@ private Q_SLOTS:
|
||||
void saveToolbarState(bool value);
|
||||
void rememberOpenDatabases(const QString& filePath);
|
||||
void applySettingsChanges();
|
||||
void setToolbarIconSize(int size);
|
||||
void setToolbarIconSize16();
|
||||
void setToolbarIconSize22();
|
||||
void setToolbarIconSize28();
|
||||
void trayIconTriggered(QSystemTrayIcon::ActivationReason reason);
|
||||
void toggleWindow();
|
||||
|
||||
private:
|
||||
void updateSearchField(DatabaseWidget* dbWidget = NULL);
|
||||
static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0);
|
||||
|
||||
static const QString BaseWindowTitle;
|
||||
|
||||
void saveWindowInformation();
|
||||
bool saveLastDatabases();
|
||||
void updateTrayIcon();
|
||||
bool isTrayIconEnabled() const;
|
||||
|
||||
const QScopedPointer<Ui::MainWindow> m_ui;
|
||||
SignalMultiplexer m_actionMultiplexer;
|
||||
@ -83,6 +83,8 @@ private:
|
||||
QActionGroup* m_copyAdditionalAttributeActions;
|
||||
QStringList m_openDatabases;
|
||||
InactivityTimer* m_inactivityTimer;
|
||||
int m_countDefaultAttributes;
|
||||
QSystemTrayIcon* m_trayIcon;
|
||||
|
||||
Q_DISABLE_COPY(MainWindow)
|
||||
};
|
||||
|
@ -70,7 +70,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>24</height>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
@ -148,15 +148,6 @@
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuToolbarIconSize">
|
||||
<property name="title">
|
||||
<string>Toolbar &Icon Size</string>
|
||||
</property>
|
||||
<addaction name="actionToolbarIconSize16"/>
|
||||
<addaction name="actionToolbarIconSize22"/>
|
||||
<addaction name="actionToolbarIconSize28"/>
|
||||
</widget>
|
||||
<addaction name="menuToolbarIconSize"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuEntries"/>
|
||||
@ -186,17 +177,7 @@
|
||||
<addaction name="actionEntryCopyPassword"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionLockDatabases"/>
|
||||
<widget class="QWidget" name="mySpacer">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="searchPanel">
|
||||
<widget class="QSearchField" name="searchField" native="true"/>
|
||||
</widget>
|
||||
<addaction name="actionSearch"/>
|
||||
</widget>
|
||||
<action name="actionQuit">
|
||||
<property name="text">
|
||||
@ -322,6 +303,14 @@
|
||||
<string>Clone entry</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSearch">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Find</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryCopyUsername">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
@ -397,54 +386,6 @@
|
||||
<string>Notes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToolbarIconSize16">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&16x16</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToolbarIconSize22">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&22x22</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToolbarIconSize28">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2&8x28</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFindCaseSensitive">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Case Sensitive</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFindCurrentGroup">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Current Group</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFindRootGroup">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Root Group</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
@ -465,12 +406,6 @@
|
||||
<header>gui/WelcomeWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QSearchField</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/qocoa/qsearchfield.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -100,7 +100,7 @@
|
||||
<string>Upper Case Letters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>A-Z</string>
|
||||
<string notr="true">A-Z</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
@ -116,7 +116,7 @@
|
||||
<string>Lower Case Letters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>a-z</string>
|
||||
<string notr="true">a-z</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
@ -132,7 +132,7 @@
|
||||
<string>Numbers</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0-9</string>
|
||||
<string notr="true">0-9</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
@ -148,7 +148,7 @@
|
||||
<string>Special Characters</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>/*_& ...</string>
|
||||
<string notr="true">/*_& ...</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
@ -7,31 +7,119 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<height>16</height>
|
||||
<height>87</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="LineEdit" name="searchEdit"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="closeSearchButton">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="searchResults"/>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Find:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QWidget" name="optionsWidget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="caseSensitiveCheckBox">
|
||||
<property name="text">
|
||||
<string>Case sensitive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="searchCurrentRadioButton">
|
||||
<property name="text">
|
||||
<string>Current group</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="searchRootRadioButton">
|
||||
<property name="text">
|
||||
<string>Root group</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>255</width>
|
||||
<height>1</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>gui/LineEdit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>closeSearchButton</tabstop>
|
||||
<tabstop>searchEdit</tabstop>
|
||||
<tabstop>caseSensitiveCheckBox</tabstop>
|
||||
<tabstop>searchCurrentRadioButton</tabstop>
|
||||
<tabstop>searchRootRadioButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -21,27 +21,7 @@
|
||||
|
||||
#include "autotype/AutoType.h"
|
||||
#include "core/Config.h"
|
||||
|
||||
class SettingsWidget::ExtraPage
|
||||
{
|
||||
public:
|
||||
ExtraPage(ISettingsPage* page, QWidget* widget): settingsPage(page), widget(widget)
|
||||
{}
|
||||
|
||||
void loadSettings() const
|
||||
{
|
||||
settingsPage->loadSettings(widget);
|
||||
}
|
||||
|
||||
void saveSettings() const
|
||||
{
|
||||
settingsPage->saveSettings(widget);
|
||||
}
|
||||
|
||||
private:
|
||||
QSharedPointer<ISettingsPage> settingsPage;
|
||||
QWidget* widget;
|
||||
};
|
||||
#include "core/Translator.h"
|
||||
|
||||
SettingsWidget::SettingsWidget(QWidget* parent)
|
||||
: EditWidget(parent)
|
||||
@ -67,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)));
|
||||
@ -78,25 +60,29 @@ SettingsWidget::~SettingsWidget()
|
||||
{
|
||||
}
|
||||
|
||||
void SettingsWidget::addSettingsPage(ISettingsPage *page)
|
||||
{
|
||||
QWidget * widget = page->createWidget();
|
||||
widget->setParent(this);
|
||||
m_extraPages.append(ExtraPage(page, widget));
|
||||
add(page->name(), widget);
|
||||
}
|
||||
|
||||
void SettingsWidget::loadSettings()
|
||||
{
|
||||
m_generalUi->rememberLastDatabasesCheckBox->setChecked(config()->get("RememberLastDatabases").toBool());
|
||||
m_generalUi->openPreviousDatabasesOnStartupCheckBox->setChecked(
|
||||
config()->get("OpenPreviousDatabasesOnStartup").toBool());
|
||||
m_generalUi->modifiedExpandedChangedCheckBox->setChecked(config()->get("ModifiedOnExpandedStateChanges").toBool());
|
||||
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->reloadBehavior->setCurrentIndex(config()->get("ReloadBehavior").toInt());
|
||||
m_generalUi->autoTypeEntryTitleMatchCheckBox->setChecked(config()->get("AutoTypeEntryTitleMatch").toBool());
|
||||
|
||||
m_generalUi->languageComboBox->clear();
|
||||
QList<QPair<QString, QString> > 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<Qt::Key>(config()->get("GlobalAutoTypeKey").toInt());
|
||||
@ -115,8 +101,7 @@ void SettingsWidget::loadSettings()
|
||||
m_secUi->passwordCleartextCheckBox->setChecked(config()->get("security/passwordscleartext").toBool());
|
||||
|
||||
m_secUi->autoTypeAskCheckBox->setChecked(config()->get("security/autotypeask").toBool());
|
||||
Q_FOREACH (const ExtraPage& page, m_extraPages)
|
||||
page.loadSettings();
|
||||
|
||||
setCurrentRow(0);
|
||||
}
|
||||
|
||||
@ -125,15 +110,20 @@ void SettingsWidget::saveSettings()
|
||||
config()->set("RememberLastDatabases", m_generalUi->rememberLastDatabasesCheckBox->isChecked());
|
||||
config()->set("OpenPreviousDatabasesOnStartup",
|
||||
m_generalUi->openPreviousDatabasesOnStartupCheckBox->isChecked());
|
||||
config()->set("ModifiedOnExpandedStateChanges",
|
||||
m_generalUi->modifiedExpandedChangedCheckBox->isChecked());
|
||||
config()->set("AutoSaveAfterEveryChange",
|
||||
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("ReloadBehavior", m_generalUi->reloadBehavior->currentIndex());
|
||||
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",
|
||||
@ -148,8 +138,6 @@ void SettingsWidget::saveSettings()
|
||||
config()->set("security/passwordscleartext", m_secUi->passwordCleartextCheckBox->isChecked());
|
||||
|
||||
config()->set("security/autotypeask", m_secUi->autoTypeAskCheckBox->isChecked());
|
||||
Q_FOREACH (const ExtraPage& page, m_extraPages)
|
||||
page.saveSettings();
|
||||
|
||||
Q_EMIT editFinished(true);
|
||||
}
|
||||
|
@ -59,8 +59,6 @@ private:
|
||||
const QScopedPointer<Ui::SettingsWidgetGeneral> m_generalUi;
|
||||
Qt::Key m_globalAutoTypeKey;
|
||||
Qt::KeyboardModifiers m_globalAutoTypeModifiers;
|
||||
class ExtraPage;
|
||||
QList<ExtraPage> m_extraPages;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_SETTINGSWIDGET_H
|
||||
|
@ -6,55 +6,24 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>481</width>
|
||||
<height>185</height>
|
||||
<width>456</width>
|
||||
<height>313</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="rememberLastDatabasesCheckBox">
|
||||
<property name="text">
|
||||
<string>Remember recent databases</string>
|
||||
<string>Remember last databases</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="modifiedExpandedChangedCheckBox">
|
||||
<property name="text">
|
||||
<string>Mark as modified on expanded state changes</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="autoSaveAfterEveryChangeCheckBox">
|
||||
<property name="text">
|
||||
<string>Automatically save after every change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="autoSaveOnExitCheckBox">
|
||||
<property name="text">
|
||||
<string>Automatically save on exit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="autoTypeShortcutLabel">
|
||||
<property name="text">
|
||||
<string>Global Auto-Type shortcut</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="ShortcutWidget" name="autoTypeShortcutWidget"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="openPreviousDatabasesOnStartupCheckBox">
|
||||
<property name="text">
|
||||
@ -62,46 +31,78 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="autoSaveOnExitCheckBox">
|
||||
<property name="text">
|
||||
<string>When database files are externally modified</string>
|
||||
<string>Automatically save on exit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QComboBox" name="reloadBehavior">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Always ask</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Reload unmodified databases</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Ignore modifications</string>
|
||||
</property>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="autoSaveAfterEveryChangeCheckBox">
|
||||
<property name="text">
|
||||
<string>Automatically save after every change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="minimizeOnCopyCheckBox">
|
||||
<property name="text">
|
||||
<string>Minimize when copying to clipboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="useGroupIconOnEntryCreationCheckBox">
|
||||
<property name="text">
|
||||
<string>Use group icon on entry creation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="autoTypeShortcutLabel">
|
||||
<property name="text">
|
||||
<string>Global Auto-Type shortcut</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="ShortcutWidget" name="autoTypeShortcutWidget"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="autoTypeEntryTitleMatchCheckBox">
|
||||
<property name="text">
|
||||
<string>Use entry title to match windows for global auto-type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="languageLabel">
|
||||
<property name="text">
|
||||
<string>Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QComboBox" name="languageComboBox"/>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="systrayShowCheckBox">
|
||||
<property name="text">
|
||||
<string>Show a system tray icon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QCheckBox" name="systrayMinimizeToTrayCheckBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hide window to system tray when minimized</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
@ -114,7 +115,6 @@
|
||||
<tabstops>
|
||||
<tabstop>rememberLastDatabasesCheckBox</tabstop>
|
||||
<tabstop>openPreviousDatabasesOnStartupCheckBox</tabstop>
|
||||
<tabstop>modifiedExpandedChangedCheckBox</tabstop>
|
||||
<tabstop>autoSaveOnExitCheckBox</tabstop>
|
||||
<tabstop>autoSaveAfterEveryChangeCheckBox</tabstop>
|
||||
<tabstop>minimizeOnCopyCheckBox</tabstop>
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <QStackedLayout>
|
||||
#include <QMenu>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include "core/Config.h"
|
||||
#include "core/Database.h"
|
||||
@ -107,7 +108,11 @@ void EditEntryWidget::setupAdvanced()
|
||||
|
||||
m_attachmentsModel->setEntryAttachments(m_entryAttachments);
|
||||
m_advancedUi->attachmentsView->setModel(m_attachmentsModel);
|
||||
connect(m_advancedUi->attachmentsView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||||
SLOT(updateAttachmentButtonsEnabled(QModelIndex)));
|
||||
connect(m_advancedUi->attachmentsView, SIGNAL(doubleClicked(QModelIndex)), SLOT(openAttachment(QModelIndex)));
|
||||
connect(m_advancedUi->saveAttachmentButton, SIGNAL(clicked()), SLOT(saveCurrentAttachment()));
|
||||
connect(m_advancedUi->openAttachmentButton, SIGNAL(clicked()), SLOT(openCurrentAttachment()));
|
||||
connect(m_advancedUi->addAttachmentButton, SIGNAL(clicked()), SLOT(insertAttachment()));
|
||||
connect(m_advancedUi->removeAttachmentButton, SIGNAL(clicked()), SLOT(removeCurrentAttachment()));
|
||||
|
||||
@ -232,6 +237,15 @@ void EditEntryWidget::useExpiryPreset(QAction* action)
|
||||
m_mainUi->expireDatePicker->setDateTime(expiryDateTime);
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateAttachmentButtonsEnabled(const QModelIndex& current)
|
||||
{
|
||||
bool enable = current.isValid();
|
||||
|
||||
m_advancedUi->saveAttachmentButton->setEnabled(enable);
|
||||
m_advancedUi->openAttachmentButton->setEnabled(enable);
|
||||
m_advancedUi->removeAttachmentButton->setEnabled(enable && !m_history);
|
||||
}
|
||||
|
||||
QString EditEntryWidget::entryTitle() const
|
||||
{
|
||||
if (m_entry) {
|
||||
@ -281,7 +295,7 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
||||
m_mainUi->tooglePasswordGeneratorButton->setChecked(false);
|
||||
m_mainUi->passwordGenerator->reset();
|
||||
m_advancedUi->addAttachmentButton->setEnabled(!m_history);
|
||||
m_advancedUi->removeAttachmentButton->setEnabled(!m_history);
|
||||
updateAttachmentButtonsEnabled(m_advancedUi->attachmentsView->currentIndex());
|
||||
m_advancedUi->addAttributeButton->setEnabled(!m_history);
|
||||
m_advancedUi->editAttributeButton->setEnabled(false);
|
||||
m_advancedUi->removeAttributeButton->setEnabled(false);
|
||||
@ -591,14 +605,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;
|
||||
}
|
||||
|
||||
@ -637,6 +651,42 @@ void EditEntryWidget::saveCurrentAttachment()
|
||||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::openAttachment(const QModelIndex& index)
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QString filename = m_attachmentsModel->keyByIndex(index);
|
||||
QByteArray attachmentData = m_entryAttachments->value(filename);
|
||||
|
||||
// tmp file will be removed once the database (or the application) has been closed
|
||||
QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename));
|
||||
QTemporaryFile* file = new QTemporaryFile(tmpFileTemplate, this);
|
||||
|
||||
if (!file->open()) {
|
||||
MessageBox::warning(this, tr("Error"),
|
||||
tr("Unable to save the attachment:\n").append(file->errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->write(attachmentData) != attachmentData.size()) {
|
||||
MessageBox::warning(this, tr("Error"),
|
||||
tr("Unable to save the attachment:\n").append(file->errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(file->fileName()));
|
||||
}
|
||||
|
||||
void EditEntryWidget::openCurrentAttachment()
|
||||
{
|
||||
QModelIndex index = m_advancedUi->attachmentsView->currentIndex();
|
||||
|
||||
openAttachment(index);
|
||||
}
|
||||
|
||||
void EditEntryWidget::removeCurrentAttachment()
|
||||
{
|
||||
Q_ASSERT(!m_history);
|
||||
@ -784,13 +834,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;
|
||||
|
@ -76,6 +76,8 @@ private Q_SLOTS:
|
||||
void updateCurrentAttribute();
|
||||
void insertAttachment();
|
||||
void saveCurrentAttachment();
|
||||
void openAttachment(const QModelIndex& index);
|
||||
void openCurrentAttachment();
|
||||
void removeCurrentAttachment();
|
||||
void updateAutoTypeEnabled();
|
||||
void insertAutoTypeAssoc();
|
||||
@ -91,6 +93,7 @@ private Q_SLOTS:
|
||||
void histEntryActivated(const QModelIndex& index);
|
||||
void updateHistoryButtons(const QModelIndex& current, const QModelIndex& previous);
|
||||
void useExpiryPreset(QAction* action);
|
||||
void updateAttachmentButtonsEnabled(const QModelIndex& current);
|
||||
|
||||
private:
|
||||
void setupMain();
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>299</height>
|
||||
<height>315</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@ -119,13 +119,29 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeAttachmentButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="openAttachmentButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="saveAttachmentButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "EntryView.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#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<Entry*>& 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)
|
||||
|
@ -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<Entry*>& entries);
|
||||
bool inEntryListMode();
|
||||
int numberOfSelectedEntries();
|
||||
void setFirstEntryActive();
|
||||
|
||||
public Q_SLOTS:
|
||||
void setGroup(Group* group);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/EntrySearcher.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "core/Uuid.h"
|
||||
#include "core/PasswordGenerator.h"
|
||||
@ -190,7 +191,7 @@ QList<Entry*> Service::searchEntries(Database* db, const QString& hostname)
|
||||
{
|
||||
QList<Entry*> entries;
|
||||
if (Group* rootGroup = db->rootGroup())
|
||||
Q_FOREACH (Entry* entry, rootGroup->search(hostname, Qt::CaseInsensitive)) {
|
||||
Q_FOREACH (Entry* entry, EntrySearcher().search(hostname, rootGroup, Qt::CaseInsensitive)) {
|
||||
QString title = entry->title();
|
||||
QString url = entry->url();
|
||||
|
||||
|
15
src/main.cpp
15
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);
|
||||
|
@ -13,7 +13,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <QTest>
|
||||
|
||||
#include "tests.h"
|
||||
#include "core/Config.h"
|
||||
#include "core/FilePath.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
@ -30,11 +31,15 @@
|
||||
#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);
|
||||
@ -54,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()
|
||||
@ -77,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(),
|
||||
@ -88,42 +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<Database*> dbList;
|
||||
dbList.append(m_db);
|
||||
|
||||
m_test->setActiveWindowTitle("nomatch");
|
||||
MessageBox::setNextAnswer(QMessageBox::Ok);
|
||||
m_autoType->performGlobalAutoType(dbList);
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
|
||||
QCOMPARE(m_test->actionChars(), QString());
|
||||
}
|
||||
|
||||
void TestAutoType::testGlobalAutoTypeWithOneMatch()
|
||||
{
|
||||
QList<Database*> 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());
|
||||
|
||||
}
|
||||
|
@ -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<Database*> m_dbList;
|
||||
Group* m_group;
|
||||
Entry* m_entry;
|
||||
Entry* m_entry1;
|
||||
Entry* m_entry2;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTAUTOTYPE_H
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
@ -88,6 +90,7 @@ void TestDeletedObjects::createAndDelete(Database* db, int delObjectsSize)
|
||||
void TestDeletedObjects::testDeletedObjectsFromFile()
|
||||
{
|
||||
KeePass2XmlReader reader;
|
||||
reader.setStrictMode(true);
|
||||
QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml");
|
||||
Database* db = reader.readDatabase(xmlFile);
|
||||
|
||||
@ -158,5 +161,3 @@ void TestDeletedObjects::testDatabaseChange()
|
||||
delete db;
|
||||
delete db2;
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestDeletedObjects)
|
||||
|
@ -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)
|
||||
|
@ -33,10 +33,12 @@
|
||||
#include "gui/entry/EntryAttachmentsModel.h"
|
||||
#include "gui/entry/EntryAttributesModel.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestEntryModel)
|
||||
|
||||
void TestEntryModel::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<QModelIndex>("QModelIndex");
|
||||
Crypto::init();
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestEntryModel::test()
|
||||
@ -341,5 +343,3 @@ void TestEntryModel::testDatabaseDelete()
|
||||
delete modelTest;
|
||||
delete model;
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestEntryModel)
|
||||
|
144
tests/TestEntrySearcher.cpp
Normal file
144
tests/TestEntrySearcher.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TestEntrySearcher.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#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);
|
||||
}
|
45
tests/TestEntrySearcher.h
Normal file
45
tests/TestEntrySearcher.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef KEEPASSX_TESTENTRYSEARCHER_H
|
||||
#define KEEPASSX_TESTENTRYSEARCHER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#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<Entry*> m_searchResult;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTENTRYSEARCHER_H
|
82
tests/TestExporter.cpp
Normal file
82
tests/TestExporter.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TestExporter.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
33
tests/TestExporter.h
Normal file
33
tests/TestExporter.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_TESTEXPORTER_H
|
||||
#define KEEPASSX_TESTEXPORTER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TestExporter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testToDbExporter();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTEXPORTER_H
|
@ -27,11 +27,13 @@
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Crypto.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestGroup)
|
||||
|
||||
void TestGroup::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<Entry*>("Entry*");
|
||||
qRegisterMetaType<Group*>("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<Entry*> 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<Entry*> 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)
|
||||
|
@ -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
|
||||
|
@ -27,10 +27,12 @@
|
||||
#include "crypto/Crypto.h"
|
||||
#include "gui/group/GroupModel.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestGroupModel)
|
||||
|
||||
void TestGroupModel::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<QModelIndex>("QModelIndex");
|
||||
Crypto::init();
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestGroupModel::test()
|
||||
@ -149,5 +151,3 @@ void TestGroupModel::test()
|
||||
delete modelTest;
|
||||
delete model;
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestGroupModel)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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,9 +68,10 @@ QDateTime TestKeePass2XmlReader::genDT(int year, int month, int day, int hour, i
|
||||
|
||||
void TestKeePass2XmlReader::initTestCase()
|
||||
{
|
||||
Crypto::init();
|
||||
QVERIFY(Crypto::init());
|
||||
|
||||
KeePass2XmlReader reader;
|
||||
reader.setStrictMode(true);
|
||||
QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml");
|
||||
m_db = reader.readDatabase(xmlFile);
|
||||
QVERIFY(m_db);
|
||||
@ -355,28 +358,44 @@ void TestKeePass2XmlReader::testDeletedObjects()
|
||||
void TestKeePass2XmlReader::testBroken()
|
||||
{
|
||||
QFETCH(QString, baseName);
|
||||
QFETCH(bool, strictMode);
|
||||
QFETCH(bool, expectError);
|
||||
|
||||
KeePass2XmlReader reader;
|
||||
reader.setStrictMode(strictMode);
|
||||
QString xmlFile = QString("%1/%2.xml").arg(KEEPASSX_TEST_DATA_DIR, baseName);
|
||||
QVERIFY(QFile::exists(xmlFile));
|
||||
QScopedPointer<Database> db(reader.readDatabase(xmlFile));
|
||||
QVERIFY(reader.hasError());
|
||||
if (reader.hasError()) {
|
||||
qWarning("Reader error: %s", qPrintable(reader.errorString()));
|
||||
}
|
||||
QCOMPARE(reader.hasError(), expectError);
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testBroken_data()
|
||||
{
|
||||
QTest::addColumn<QString>("baseName");
|
||||
QTest::addColumn<bool>("strictMode");
|
||||
QTest::addColumn<bool>("expectError");
|
||||
|
||||
QTest::newRow("BrokenNoGroupUuid") << "BrokenNoGroupUuid";
|
||||
QTest::newRow("BrokenNoEntryUuid") << "BrokenNoEntryUuid";
|
||||
QTest::newRow("BrokenNoRootGroup") << "BrokenNoRootGroup";
|
||||
QTest::newRow("BrokenTwoRoots") << "BrokenTwoRoots";
|
||||
QTest::newRow("BrokenTwoRootGroups") << "BrokenTwoRootGroups";
|
||||
// testfile strict? error?
|
||||
QTest::newRow("BrokenNoGroupUuid (strict)") << "BrokenNoGroupUuid" << true << true;
|
||||
QTest::newRow("BrokenNoGroupUuid (not strict)") << "BrokenNoGroupUuid" << false << false;
|
||||
QTest::newRow("BrokenNoEntryUuid (strict)") << "BrokenNoEntryUuid" << true << true;
|
||||
QTest::newRow("BrokenNoEntryUuid (not strict)") << "BrokenNoEntryUuid" << false << false;
|
||||
QTest::newRow("BrokenNoRootGroup (strict)") << "BrokenNoRootGroup" << true << true;
|
||||
QTest::newRow("BrokenNoRootGroup (not strict)") << "BrokenNoRootGroup" << false << true;
|
||||
QTest::newRow("BrokenTwoRoots (strict)") << "BrokenTwoRoots" << true << true;
|
||||
QTest::newRow("BrokenTwoRoots (not strict)") << "BrokenTwoRoots" << false << true;
|
||||
QTest::newRow("BrokenTwoRootGroups (strict)") << "BrokenTwoRootGroups" << true << true;
|
||||
QTest::newRow("BrokenTwoRootGroups (not strict)") << "BrokenTwoRootGroups" << false << true;
|
||||
QTest::newRow("BrokenGroupReference (strict)") << "BrokenGroupReference" << true << false;
|
||||
QTest::newRow("BrokenGroupReference (not strict)") << "BrokenGroupReference" << false << false;
|
||||
QTest::newRow("BrokenDeletedObjects (strict)") << "BrokenDeletedObjects" << true << true;
|
||||
QTest::newRow("BrokenDeletedObjects (not strict)") << "BrokenDeletedObjects" << false << false;
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::cleanupTestCase()
|
||||
{
|
||||
delete m_db;
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestKeePass2XmlReader)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include <QTest>
|
||||
|
||||
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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
27
tests/data/BrokenDeletedObjects.xml
Normal file
27
tests/data/BrokenDeletedObjects.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<KeePassFile>
|
||||
<Root>
|
||||
<Group>
|
||||
<UUID>lmU+9n0aeESKZvcEze+bRg==</UUID>
|
||||
<Name>Test</Name>
|
||||
<Entry>
|
||||
<UUID>AaUYVdXsI02h4T1RiAlgtg==</UUID>
|
||||
<String>
|
||||
<Key>Title</Key>
|
||||
<Value>Sample Entry 1</Value>
|
||||
</String>
|
||||
</Entry>
|
||||
</Group>
|
||||
<DeletedObjects>
|
||||
<DeletedObject>
|
||||
<UUID/>
|
||||
<DeletionTime>2010-08-25T16:14:12Z</DeletionTime>
|
||||
</DeletedObject>
|
||||
<DeletedObject/>
|
||||
<DeletedObject>
|
||||
<UUID>5K/bzWCSmkCv5OZxYl4N/w==</UUID>
|
||||
<DeletionTime/>
|
||||
</DeletedObject>
|
||||
</DeletedObjects>
|
||||
</Root>
|
||||
</KeePassFile>
|
20
tests/data/BrokenGroupReference.xml
Normal file
20
tests/data/BrokenGroupReference.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<KeePassFile>
|
||||
<Meta>
|
||||
<RecycleBinEnabled>True</RecycleBinEnabled>
|
||||
<RecycleBinUUID>6w7wZdhAp0qVlXjkemuCYw==</RecycleBinUUID>
|
||||
</Meta>
|
||||
<Root>
|
||||
<Group>
|
||||
<UUID>lmU+9n0aeESKZvcEze+bRg==</UUID>
|
||||
<Name>Test</Name>
|
||||
<Entry>
|
||||
<UUID>AaUYVdXsI02h4T1RiAlgtg==</UUID>
|
||||
<String>
|
||||
<Key>Title</Key>
|
||||
<Value>Sample Entry 1</Value>
|
||||
</String>
|
||||
</Entry>
|
||||
</Group>
|
||||
</Root>
|
||||
</KeePassFile>
|
@ -9,6 +9,7 @@
|
||||
<Key>Title</Key>
|
||||
<Value>Sample Entry 1</Value>
|
||||
</String>
|
||||
</Entry>
|
||||
</Group>
|
||||
</Root>
|
||||
</KeePassFile>
|
||||
|
@ -51,7 +51,7 @@
|
||||
|
||||
void TestGui::initTestCase()
|
||||
{
|
||||
Crypto::init();
|
||||
QVERIFY(Crypto::init());
|
||||
Config::createTempFileInstance();
|
||||
m_mainWindow = new MainWindow();
|
||||
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
|
||||
@ -83,7 +83,7 @@ void TestGui::testTabs()
|
||||
|
||||
void TestGui::testEditEntry()
|
||||
{
|
||||
EntryView* entryView = m_dbWidget->entryView();
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QModelIndex item = entryView->model()->index(0, 1);
|
||||
QRect itemRect = entryView->visualRect(item);
|
||||
QTest::mouseClick(entryView->viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center());
|
||||
@ -170,20 +170,35 @@ void TestGui::testSearch()
|
||||
QVERIFY(searchAction->isEnabled());
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
QWidget* searchActionWidget = toolBar->widgetForAction(searchAction);
|
||||
QVERIFY(searchActionWidget->isEnabled());
|
||||
QTest::mouseClick(searchActionWidget, Qt::LeftButton);
|
||||
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QLineEdit* searchEdit = m_dbWidget->findChild<QLineEdit*>("searchEdit");
|
||||
QToolButton* clearSearch = m_dbWidget->findChild<QToolButton*>("clearButton");
|
||||
|
||||
QVERIFY(!searchEdit->hasFocus());
|
||||
|
||||
// Enter search
|
||||
QTest::mouseClick(searchActionWidget, Qt::LeftButton);
|
||||
QTRY_VERIFY(searchEdit->hasFocus());
|
||||
// Search for "ZZZ"
|
||||
QTest::keyClicks(searchEdit, "ZZZ");
|
||||
|
||||
QTRY_COMPARE(entryView->model()->rowCount(), 0);
|
||||
|
||||
// Escape
|
||||
QTest::keyClick(m_mainWindow, Qt::Key_Escape);
|
||||
QTRY_VERIFY(!searchEdit->hasFocus());
|
||||
// Enter search again
|
||||
QTest::mouseClick(searchActionWidget, Qt::LeftButton);
|
||||
QTRY_VERIFY(searchEdit->hasFocus());
|
||||
// Input and clear
|
||||
QTest::keyClicks(searchEdit, "ZZZ");
|
||||
QTRY_COMPARE(searchEdit->text(), QString("ZZZ"));
|
||||
QTest::mouseClick(clearSearch, Qt::LeftButton);
|
||||
QTRY_COMPARE(searchEdit->text(), QString(""));
|
||||
// Triggering search should select the existing text
|
||||
QTest::keyClicks(searchEdit, "ZZZ");
|
||||
QTest::mouseClick(searchActionWidget, Qt::LeftButton);
|
||||
QTRY_VERIFY(searchEdit->hasFocus());
|
||||
// Search for "some"
|
||||
QTest::keyClicks(searchEdit, "some");
|
||||
|
||||
QTRY_COMPARE(entryView->model()->rowCount(), 4);
|
||||
|
||||
clickIndex(entryView->model()->index(0, 1), entryView, Qt::LeftButton);
|
||||
@ -237,8 +252,8 @@ void TestGui::testSearch()
|
||||
|
||||
void TestGui::testDeleteEntry()
|
||||
{
|
||||
GroupView* groupView = m_dbWidget->groupView();
|
||||
EntryView* entryView = m_dbWidget->entryView();
|
||||
GroupView* groupView = m_dbWidget->findChild<GroupView*>("groupView");
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
QAction* entryDeleteAction = m_mainWindow->findChild<QAction*>("actionEntryDelete");
|
||||
QWidget* entryDeleteWidget = toolBar->widgetForAction(entryDeleteAction);
|
||||
@ -274,7 +289,7 @@ void TestGui::testDeleteEntry()
|
||||
|
||||
void TestGui::testCloneEntry()
|
||||
{
|
||||
EntryView* entryView = m_dbWidget->entryView();
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
|
||||
QCOMPARE(entryView->model()->rowCount(), 1);
|
||||
|
||||
@ -292,8 +307,8 @@ void TestGui::testCloneEntry()
|
||||
|
||||
void TestGui::testDragAndDropEntry()
|
||||
{
|
||||
EntryView* entryView = m_dbWidget->entryView();
|
||||
GroupView* groupView = m_dbWidget->groupView();
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
GroupView* groupView = m_dbWidget->findChild<GroupView*>("groupView");
|
||||
QAbstractItemModel* groupModel = groupView->model();
|
||||
|
||||
QModelIndex sourceIndex = entryView->model()->index(0, 1);
|
||||
@ -314,7 +329,7 @@ void TestGui::testDragAndDropEntry()
|
||||
|
||||
void TestGui::testDragAndDropGroup()
|
||||
{
|
||||
QAbstractItemModel* groupModel = m_dbWidget->groupView()->model();
|
||||
QAbstractItemModel* groupModel = m_dbWidget->findChild<GroupView*>("groupView")->model();
|
||||
QModelIndex rootIndex = groupModel->index(0, 0);
|
||||
|
||||
dragAndDropGroup(groupModel->index(0, 0, rootIndex),
|
||||
@ -453,7 +468,7 @@ void TestGui::dragAndDropGroup(const QModelIndex& sourceIndex, const QModelIndex
|
||||
QVERIFY(sourceIndex.isValid());
|
||||
QVERIFY(targetIndex.isValid());
|
||||
|
||||
GroupModel* groupModel = qobject_cast<GroupModel*>(m_dbWidget->groupView()->model());
|
||||
GroupModel* groupModel = qobject_cast<GroupModel*>(m_dbWidget->findChild<GroupView*>("groupView")->model());
|
||||
|
||||
QMimeData mimeData;
|
||||
QByteArray encoded;
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
void TestGuiPixmaps::initTestCase()
|
||||
{
|
||||
Crypto::init();
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestGuiPixmaps::testDatabaseIcons()
|
||||
|
@ -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))) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user