Merge branch 'release/2.3.0' into develop

This commit is contained in:
Janek Bevendorff 2018-02-21 09:41:20 +01:00
commit d0c583b5e2
20 changed files with 567 additions and 168 deletions

View File

@ -73,27 +73,49 @@ set(KEEPASSXC_VERSION_MINOR "3")
set(KEEPASSXC_VERSION_PATCH "0") set(KEEPASSXC_VERSION_PATCH "0")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}") set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
set(KEEPASSXC_RELEASE_BUILD OFF CACHE BOOLEAN "Remove stability warnings") set(KEEPASSXC_BUILD_TYPE "Snapshot" CACHE STRING "Set KeePassXC build type to distinguish between stable releases and snapshots")
set_property(CACHE KEEPASSXC_BUILD_TYPE PROPERTY STRINGS Snapshot Release PreRelease)
# Check if on a tag, if so build as a release # Check if on a tag, if so build as a release
execute_process(COMMAND git tag --points-at HEAD execute_process(COMMAND git tag --points-at HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_TAG) OUTPUT_VARIABLE GIT_TAG)
if(GIT_TAG) if(GIT_TAG)
set(KEEPASSXC_RELEASE_BUILD ON) string(REGEX REPLACE "\r?\n$" "" GIT_TAG "${GIT_TAG}")
elseif(NOT KEEPASSXC_RELEASE_BUILD)
if(GIT_TAG MATCHES "^[\\.0-9]+-(alpha|beta)[0-9]+$")
set(KEEPASSXC_BUILD_TYPE PreRelease)
set(KEEPASSXC_VERSION ${GIT_TAG})
elseif(GIT_TAG MATCHES "^[\\.0-9]+$")
set(KEEPASSXC_BUILD_TYPE Release)
set(KEEPASSXC_VERSION ${GIT_TAG})
endif()
endif()
if(KEEPASSXC_BUILD_TYPE STREQUAL "PreRelease" AND NOT GIT_TAG)
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-preview")
elseif(KEEPASSXC_BUILD_TYPE STREQUAL "Snapshot")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-snapshot") set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION}-snapshot")
endif() endif()
if(KEEPASSXC_BUILD_TYPE STREQUAL "Release")
set(KEEPASSXC_BUILD_TYPE_RELEASE ON)
elseif(KEEPASSXC_BUILD_TYPE STREQUAL "PreRelease")
set(KEEPASSXC_BUILD_TYPE_PRE_RELEASE ON)
else()
set(KEEPASSXC_BUILD_TYPE_SNAPSHOT ON)
endif()
message(STATUS "Setting up build for KeePassXC v${KEEPASSXC_VERSION}\n") message(STATUS "Setting up build for KeePassXC v${KEEPASSXC_VERSION}\n")
# Distribution info # Distribution info
set(KEEPASSXC_DIST True) set(KEEPASSXC_DIST ON)
set(KEEPASSXC_DIST_TYPE "Other" CACHE STRING "KeePassXC Distribution type") set(KEEPASSXC_DIST_TYPE "Other" CACHE STRING "KeePassXC Distribution Type")
set_property(CACHE KEEPASSXC_DIST_TYPE PROPERTY STRINGS Snap AppImage Other) set_property(CACHE KEEPASSXC_DIST_TYPE PROPERTY STRINGS Snap AppImage Other)
if(KEEPASSXC_DIST_TYPE STREQUAL "Snap") if(KEEPASSXC_DIST_TYPE STREQUAL "Snap")
set(KEEPASSXC_DIST_SNAP True) set(KEEPASSXC_DIST_SNAP ON)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "AppImage") elseif(KEEPASSXC_DIST_TYPE STREQUAL "AppImage")
set(KEEPASSXC_DIST_APPIMAGE True) set(KEEPASSXC_DIST_APPIMAGE ON)
elseif(KEEPASSXC_DIST_TYPE STREQUAL "Other") elseif(KEEPASSXC_DIST_TYPE STREQUAL "Other")
unset(KEEPASSXC_DIST) unset(KEEPASSXC_DIST)
endif() endif()

View File

@ -246,7 +246,7 @@ checkVersionInCMake() {
local app_name_upper="$(echo "$APP_NAME" | tr '[:lower:]' '[:upper:]')" local app_name_upper="$(echo "$APP_NAME" | tr '[:lower:]' '[:upper:]')"
local major_num="$(echo ${RELEASE_NAME} | cut -f1 -d.)" local major_num="$(echo ${RELEASE_NAME} | cut -f1 -d.)"
local minor_num="$(echo ${RELEASE_NAME} | cut -f2 -d.)" local minor_num="$(echo ${RELEASE_NAME} | cut -f2 -d.)"
local patch_num="$(echo ${RELEASE_NAME} | cut -f3 -d.)" local patch_num="$(echo ${RELEASE_NAME} | cut -f3 -d. | cut -f1 -d-)"
grep -q "${app_name_upper}_VERSION_MAJOR \"${major_num}\"" CMakeLists.txt grep -q "${app_name_upper}_VERSION_MAJOR \"${major_num}\"" CMakeLists.txt
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@ -582,19 +582,26 @@ build() {
done done
init init
checkWorkingTreeClean
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")" OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
if $BUILD_SNAPSHOT; then if ${BUILD_SNAPSHOT}; then
TAG_NAME="HEAD" TAG_NAME="HEAD"
local branch=`git rev-parse --abbrev-ref HEAD` local branch=`git rev-parse --abbrev-ref HEAD`
logInfo "Using current branch ${branch} to build..." logInfo "Using current branch ${branch} to build..."
RELEASE_NAME="${RELEASE_NAME}-snapshot" RELEASE_NAME="${RELEASE_NAME}-snapshot"
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Snapshot"
else else
checkWorkingTreeClean
if $(echo "$TAG_NAME" | grep -qP "\-(alpha|beta)\\d+\$"); then
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=PreRelease"
logInfo "Checking out pre-release tag '${TAG_NAME}'..."
else
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Release"
logInfo "Checking out release tag '${TAG_NAME}'..." logInfo "Checking out release tag '${TAG_NAME}'..."
fi
git checkout "$TAG_NAME" git checkout "$TAG_NAME"
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_RELEASE_BUILD=ON"
fi fi
logInfo "Creating output directory..." logInfo "Creating output directory..."
@ -604,7 +611,7 @@ build() {
exitError "Failed to create output directory!" exitError "Failed to create output directory!"
fi fi
if $BUILD_SOURCE_TARBALL; then if ${BUILD_SOURCE_TARBALL}; then
logInfo "Creating source tarball..." logInfo "Creating source tarball..."
local app_name_lower="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')" local app_name_lower="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')"
TARBALL_NAME="${app_name_lower}-${RELEASE_NAME}-src.tar.xz" TARBALL_NAME="${app_name_lower}-${RELEASE_NAME}-src.tar.xz"
@ -612,12 +619,20 @@ build() {
| xz -6 > "${OUTPUT_DIR}/${TARBALL_NAME}" | xz -6 > "${OUTPUT_DIR}/${TARBALL_NAME}"
fi fi
if [ -e "${OUTPUT_DIR}/build-release" ]; then
logInfo "Cleaning existing build directory..."
rm -r "${OUTPUT_DIR}/build-release" 2> /dev/null
if [ $? -ne 0 ]; then
exitError "Failed to clean existing build directory, please do it manually."
fi
fi
logInfo "Creating build directory..." logInfo "Creating build directory..."
mkdir -p "${OUTPUT_DIR}/build-release" mkdir -p "${OUTPUT_DIR}/build-release"
cd "${OUTPUT_DIR}/build-release" cd "${OUTPUT_DIR}/build-release"
logInfo "Configuring sources..." logInfo "Configuring sources..."
for p in $BUILD_PLUGINS; do for p in ${BUILD_PLUGINS}; do
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_XC_$(echo $p | tr '[:lower:]' '[:upper:]')=On" CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_XC_$(echo $p | tr '[:lower:]' '[:upper:]')=On"
done done
@ -654,13 +669,13 @@ build() {
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" $CMAKE_OPTIONS "$SRC_DIR" -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" $CMAKE_OPTIONS "$SRC_DIR"
logInfo "Compiling and packaging sources..." logInfo "Compiling and packaging sources..."
make $MAKE_OPTIONS preinstall mingw32-make $MAKE_OPTIONS preinstall
# Call cpack directly instead of calling make package. # Call cpack directly instead of calling make package.
# This is important because we want to build the MSI when making a # This is important because we want to build the MSI when making a
# release. # release.
cpack -G "NSIS;WIX;ZIP" cpack -G "NSIS;ZIP;${CPACK_GENERATORS}"
mv "./${APP_NAME}-${RELEASE_NAME}-"*.{exe,msi,zip} ../ mv "./${APP_NAME}-${RELEASE_NAME}-"*.* ../
else else
mkdir -p "${OUTPUT_DIR}/bin-release" mkdir -p "${OUTPUT_DIR}/bin-release"

View File

@ -269,8 +269,8 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
} }
/** /**
* Global Autotype entry-point funcion * Global Autotype entry-point function
* Perform global autotype on the active window * Perform global Auto-Type on the active window
*/ */
void AutoType::performGlobalAutoType(const QList<Database*>& dbList) void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
{ {
@ -304,10 +304,19 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
if (matchList.isEmpty()) { if (matchList.isEmpty()) {
m_inAutoType.unlock(); m_inAutoType.unlock();
QString message = tr("Couldn't find an entry that matches the window title:");
message.append("\n\n"); if (qobject_cast<QApplication*>(QCoreApplication::instance())) {
message.append(windowTitle); auto* msgBox = new QMessageBox();
MessageBox::information(nullptr, tr("Auto-Type - KeePassXC"), message); msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setWindowTitle(tr("Auto-Type - KeePassXC"));
msgBox->setText(tr("Couldn't find an entry that matches the window title:").append("\n\n")
.append(windowTitle));
msgBox->setIcon(QMessageBox::Information);
msgBox->setStandardButtons(QMessageBox::Ok);
msgBox->show();
msgBox->raise();
msgBox->activateWindow();
}
emit autotypeRejected(); emit autotypeRejected();
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) { } else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
@ -315,7 +324,7 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
m_inAutoType.unlock(); m_inAutoType.unlock();
} else { } else {
m_windowFromGlobal = m_plugin->activeWindow(); m_windowFromGlobal = m_plugin->activeWindow();
AutoTypeSelectDialog* selectDialog = new AutoTypeSelectDialog(); auto* selectDialog = new AutoTypeSelectDialog();
connect(selectDialog, SIGNAL(matchActivated(AutoTypeMatch)), connect(selectDialog, SIGNAL(matchActivated(AutoTypeMatch)),
SLOT(performAutoTypeFromGlobal(AutoTypeMatch))); SLOT(performAutoTypeFromGlobal(AutoTypeMatch)));
connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType())); connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType()));

View File

@ -3,14 +3,14 @@
#ifndef KEEPASSX_CONFIG_KEEPASSX_H #ifndef KEEPASSX_CONFIG_KEEPASSX_H
#define KEEPASSX_CONFIG_KEEPASSX_H #define KEEPASSX_CONFIG_KEEPASSX_H
#define KEEPASSX_VERSION "${KEEPASSXC_VERSION}" #define KEEPASSX_VERSION "@KEEPASSXC_VERSION@"
#define KEEPASSX_SOURCE_DIR "${CMAKE_SOURCE_DIR}" #define KEEPASSX_SOURCE_DIR "@CMAKE_SOURCE_DIR@"
#define KEEPASSX_BINARY_DIR "${CMAKE_BINARY_DIR}" #define KEEPASSX_BINARY_DIR "@CMAKE_BINARY_DIR@"
#define KEEPASSX_PREFIX_DIR "${CMAKE_INSTALL_PREFIX}" #define KEEPASSX_PREFIX_DIR "@CMAKE_INSTALL_PREFIX@"
#define KEEPASSX_PLUGIN_DIR "${PLUGIN_INSTALL_DIR}" #define KEEPASSX_PLUGIN_DIR "@PLUGIN_INSTALL_DIR@"
#define KEEPASSX_DATA_DIR "${DATA_INSTALL_DIR}" #define KEEPASSX_DATA_DIR "@DATA_INSTALL_DIR@"
#cmakedefine WITH_XC_AUTOTYPE #cmakedefine WITH_XC_AUTOTYPE
#cmakedefine WITH_XC_NETWORKING #cmakedefine WITH_XC_NETWORKING
@ -19,13 +19,16 @@
#cmakedefine WITH_XC_YUBIKEY #cmakedefine WITH_XC_YUBIKEY
#cmakedefine WITH_XC_SSHAGENT #cmakedefine WITH_XC_SSHAGENT
#cmakedefine KEEPASSXC_BUILD_TYPE "@KEEPASSXC_BUILD_TYPE@"
#cmakedefine KEEPASSXC_BUILD_TYPE_RELEASE
#cmakedefine KEEPASSXC_BUILD_TYPE_PRE_RELEASE
#cmakedefine KEEPASSXC_BUILD_TYPE_SNAPSHOT
#cmakedefine KEEPASSXC_DIST #cmakedefine KEEPASSXC_DIST
#cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@" #cmakedefine KEEPASSXC_DIST_TYPE "@KEEPASSXC_DIST_TYPE@"
#cmakedefine KEEPASSXC_DIST_SNAP #cmakedefine KEEPASSXC_DIST_SNAP
#cmakedefine KEEPASSXC_DIST_APPIMAGE #cmakedefine KEEPASSXC_DIST_APPIMAGE
#cmakedefine KEEPASSXC_RELEASE_BUILD
#cmakedefine HAVE_PR_SET_DUMPABLE 1 #cmakedefine HAVE_PR_SET_DUMPABLE 1
#cmakedefine HAVE_RLIMIT_CORE 1 #cmakedefine HAVE_RLIMIT_CORE 1
#cmakedefine HAVE_PT_DENY_ATTACH 1 #cmakedefine HAVE_PT_DENY_ATTACH 1

View File

@ -772,23 +772,41 @@ QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxD
QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const
{ {
if (maxDepth <= 0) {
qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", qPrintable(uuid().toHex()));
return placeholder;
}
const PlaceholderType typeOfPlaceholder = placeholderType(placeholder); const PlaceholderType typeOfPlaceholder = placeholderType(placeholder);
switch (typeOfPlaceholder) { switch (typeOfPlaceholder) {
case PlaceholderType::NotPlaceholder: case PlaceholderType::NotPlaceholder:
case PlaceholderType::Unknown: case PlaceholderType::Unknown:
return placeholder; return placeholder;
case PlaceholderType::Title: case PlaceholderType::Title:
if (placeholderType(title()) == PlaceholderType::Title) {
return title(); return title();
}
return resolvePlaceholderRecursive(title(), maxDepth - 1);
case PlaceholderType::UserName: case PlaceholderType::UserName:
if (placeholderType(username()) == PlaceholderType::UserName) {
return username(); return username();
}
return resolvePlaceholderRecursive(username(), maxDepth - 1);
case PlaceholderType::Password: case PlaceholderType::Password:
if (placeholderType(password()) == PlaceholderType::Password) {
return password(); return password();
}
return resolvePlaceholderRecursive(password(), maxDepth - 1);
case PlaceholderType::Notes: case PlaceholderType::Notes:
if (placeholderType(notes()) == PlaceholderType::Notes) {
return notes(); return notes();
case PlaceholderType::Totp: }
return totp(); return resolvePlaceholderRecursive(notes(), maxDepth - 1);
case PlaceholderType::Url: case PlaceholderType::Url:
if (placeholderType(url()) == PlaceholderType::Url) {
return url(); return url();
}
return resolvePlaceholderRecursive(url(), maxDepth - 1);
case PlaceholderType::UrlWithoutScheme: case PlaceholderType::UrlWithoutScheme:
case PlaceholderType::UrlScheme: case PlaceholderType::UrlScheme:
case PlaceholderType::UrlHost: case PlaceholderType::UrlHost:
@ -802,6 +820,9 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe
const QString strUrl = resolveMultiplePlaceholdersRecursive(url(), maxDepth - 1); const QString strUrl = resolveMultiplePlaceholdersRecursive(url(), maxDepth - 1);
return resolveUrlPlaceholder(strUrl, typeOfPlaceholder); return resolveUrlPlaceholder(strUrl, typeOfPlaceholder);
} }
case PlaceholderType::Totp:
// totp can't have placeholder inside
return totp();
case PlaceholderType::CustomAttribute: { case PlaceholderType::CustomAttribute: {
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4) const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
return attributes()->hasKey(key) ? attributes()->value(key) : QString(); return attributes()->hasKey(key) ? attributes()->value(key) : QString();
@ -815,6 +836,11 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe
QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const
{ {
if (maxDepth <= 0) {
qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", qPrintable(uuid().toHex()));
return placeholder;
}
// resolving references in format: {REF:<WantedField>@<SearchIn>:<SearchText>} // resolving references in format: {REF:<WantedField>@<SearchIn>:<SearchText>}
// using format from http://keepass.info/help/base/fieldrefs.html at the time of writing // using format from http://keepass.info/help/base/fieldrefs.html at the time of writing
@ -828,6 +854,9 @@ QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder,
const QString searchText = match.captured(EntryAttributes::SearchTextGroupName); const QString searchText = match.captured(EntryAttributes::SearchTextGroupName);
const EntryReferenceType searchInType = Entry::referenceType(searchIn); const EntryReferenceType searchInType = Entry::referenceType(searchIn);
Q_ASSERT(m_group);
Q_ASSERT(m_group->database());
const Entry* refEntry = m_group->database()->resolveEntry(searchText, searchInType); const Entry* refEntry = m_group->database()->resolveEntry(searchText, searchInType);
if (refEntry) { if (refEntry) {

View File

@ -700,7 +700,8 @@ Entry* KdbxXmlReader::parseEntry(bool history)
entry->setIcon(uuid); entry->setIcon(uuid);
} }
continue; continue;
}if (m_xml.name() == "ForegroundColor") { }
if (m_xml.name() == "ForegroundColor") {
entry->setForegroundColor(readColor()); entry->setForegroundColor(readColor());
continue; continue;
} }

View File

@ -54,8 +54,8 @@ AboutDialog::AboutDialog(QWidget* parent)
QString debugInfo = "KeePassXC - "; QString debugInfo = "KeePassXC - ";
debugInfo.append(tr("Version %1\n").arg(KEEPASSX_VERSION)); debugInfo.append(tr("Version %1\n").arg(KEEPASSX_VERSION));
#ifndef KEEPASSXC_RELEASE_BUILD #ifndef KEEPASSXC_BUILD_TYPE_RELEASE
debugInfo.append(tr("Build Type: Snapshot\n")); debugInfo.append(tr("Build Type: %1\n").arg(KEEPASSXC_BUILD_TYPE));
#endif #endif
if (!commitHash.isEmpty()) { if (!commitHash.isEmpty()) {
debugInfo.append(tr("Revision: %1").arg(commitHash.left(7)).append("\n")); debugInfo.append(tr("Revision: %1").arg(commitHash.left(7)).append("\n"));

View File

@ -147,6 +147,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>Project Maintainers:</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="sizePolicy"> <property name="sizePolicy">
@ -156,15 +163,29 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string notr="true">&lt;p&gt;Project Maintainers:&lt;/p&gt; <string notr="true">&lt;ul&gt;
&lt;ul&gt;
&lt;li&gt;droidmonkey&lt;/li&gt; &lt;li&gt;droidmonkey&lt;/li&gt;
&lt;li&gt;phoerious&lt;/li&gt; &lt;li&gt;phoerious&lt;/li&gt;
&lt;li&gt;TheZ3ro&lt;/li&gt; &lt;li&gt;TheZ3ro&lt;/li&gt;
&lt;li&gt;louib&lt;/li&gt; &lt;li&gt;louib&lt;/li&gt;
&lt;li&gt;weslly&lt;/li&gt; &lt;li&gt;weslly&lt;/li&gt;
&lt;/ul&gt; &lt;/ul&gt;</string>
&lt;p&gt;Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.&lt;/&gt;</string> </property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@ -198,8 +219,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>423</width> <width>449</width>
<height>816</height> <height>803</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_5">

View File

@ -776,21 +776,18 @@ void DatabaseWidget::switchToView(bool accepted)
m_newGroup->setParent(m_newParent); m_newGroup->setParent(m_newParent);
m_groupView->setCurrentGroup(m_newGroup); m_groupView->setCurrentGroup(m_newGroup);
m_groupView->expandGroup(m_newParent); m_groupView->expandGroup(m_newParent);
} } else {
else {
delete m_newGroup; delete m_newGroup;
} }
m_newGroup = nullptr; m_newGroup = nullptr;
m_newParent = nullptr; m_newParent = nullptr;
} } else if (m_newEntry) {
else if (m_newEntry) {
if (accepted) { if (accepted) {
m_newEntry->setGroup(m_newParent); m_newEntry->setGroup(m_newParent);
m_entryView->setFocus(); m_entryView->setFocus();
m_entryView->setCurrentEntry(m_newEntry); m_entryView->setCurrentEntry(m_newEntry);
} } else {
else {
delete m_newEntry; delete m_newEntry;
} }
@ -798,6 +795,10 @@ void DatabaseWidget::switchToView(bool accepted)
m_newParent = nullptr; m_newParent = nullptr;
} }
if (accepted) {
showMessage(tr("Entry updated successfully."), MessageWidget::Positive, false, 2000);
}
setCurrentWidget(m_mainWidget); setCurrentWidget(m_mainWidget);
} }
@ -819,7 +820,16 @@ void DatabaseWidget::switchToEntryEdit(Entry* entry)
void DatabaseWidget::switchToEntryEdit(Entry* entry, bool create) void DatabaseWidget::switchToEntryEdit(Entry* entry, bool create)
{ {
Group* group = currentGroup(); // If creating an entry, it will be in `currentGroup()` so it's
// okay to use but when editing, the entry may not be in
// `currentGroup()` so we get the entry's group.
Group* group;
if (create) {
group = currentGroup();
} else {
group = entry->group();
}
Q_ASSERT(group); Q_ASSERT(group);
m_editEntryWidget->loadEntry(entry, create, false, group->name(), m_db); m_editEntryWidget->loadEntry(entry, create, false, group->name(), m_db);

View File

@ -119,7 +119,8 @@ bool EditWidget::readOnly() const
void EditWidget::showMessage(const QString& text, MessageWidget::MessageType type) void EditWidget::showMessage(const QString& text, MessageWidget::MessageType type)
{ {
m_ui->messageWidget->showMessage(text, type); m_ui->messageWidget->setCloseButtonVisible(false);
m_ui->messageWidget->showMessage(text, type, 2000);
} }
void EditWidget::hideMessage() void EditWidget::hideMessage()

View File

@ -425,7 +425,7 @@ MainWindow::MainWindow()
} }
#endif #endif
#ifndef KEEPASSXC_RELEASE_BUILD #ifndef KEEPASSXC_BUILD_TYPE_RELEASE
m_ui->globalMessageWidget->showMessage(tr("WARNING: You are using an unstable build of KeePassXC!\n" m_ui->globalMessageWidget->showMessage(tr("WARNING: You are using an unstable build of KeePassXC!\n"
"There is a high risk of corruption, maintain a backup of your databases.\n" "There is a high risk of corruption, maintain a backup of your databases.\n"
"This version is not meant for production use."), "This version is not meant for production use."),

View File

@ -151,6 +151,11 @@ void PasswordGeneratorWidget::setStandaloneMode(bool standalone)
} }
} }
QString PasswordGeneratorWidget::getGeneratedPassword()
{
return m_ui->editNewPassword->text();
}
void PasswordGeneratorWidget::keyPressEvent(QKeyEvent* e) void PasswordGeneratorWidget::keyPressEvent(QKeyEvent* e)
{ {
if (e->key() == Qt::Key_Escape && m_standalone == true) { if (e->key() == Qt::Key_Escape && m_standalone == true) {

View File

@ -49,16 +49,18 @@ public:
void saveSettings(); void saveSettings();
void reset(); void reset();
void setStandaloneMode(bool standalone); void setStandaloneMode(bool standalone);
public Q_SLOTS: QString getGeneratedPassword();
public slots:
void regeneratePassword(); void regeneratePassword();
void applyPassword();
void copyPassword();
signals: signals:
void appliedPassword(const QString& password); void appliedPassword(const QString& password);
void dialogTerminated(); void dialogTerminated();
private slots: private slots:
void applyPassword();
void copyPassword();
void updateButtonsEnabled(const QString& password); void updateButtonsEnabled(const QString& password);
void updatePasswordStrength(const QString& password); void updatePasswordStrength(const QString& password);
void togglePasswordShown(bool hidden); void togglePasswordShown(bool hidden);

View File

@ -31,6 +31,7 @@
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QMimeData> #include <QMimeData>
#include <QEvent> #include <QEvent>
#include <QColorDialog>
#include "autotype/AutoType.h" #include "autotype/AutoType.h"
#include "core/Config.h" #include "core/Config.h"
@ -97,7 +98,7 @@ EditEntryWidget::EditEntryWidget(QWidget* parent)
connect(this, SIGNAL(accepted()), SLOT(acceptEntry())); connect(this, SIGNAL(accepted()), SLOT(acceptEntry()));
connect(this, SIGNAL(rejected()), SLOT(cancel())); connect(this, SIGNAL(rejected()), SLOT(cancel()));
connect(this, SIGNAL(apply()), SLOT(saveEntry())); connect(this, SIGNAL(apply()), SLOT(commitEntry()));
connect(m_iconsWidget, SIGNAL(messageEditEntry(QString, MessageWidget::MessageType)), SLOT(showMessage(QString, MessageWidget::MessageType))); connect(m_iconsWidget, SIGNAL(messageEditEntry(QString, MessageWidget::MessageType)), SLOT(showMessage(QString, MessageWidget::MessageType)));
connect(m_iconsWidget, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage())); connect(m_iconsWidget, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage()));
@ -127,7 +128,7 @@ void EditEntryWidget::setupMain()
QAction *action = new QAction(this); QAction *action = new QAction(this);
action->setShortcut(Qt::CTRL | Qt::Key_Return); action->setShortcut(Qt::CTRL | Qt::Key_Return);
connect(action, SIGNAL(triggered()), this, SLOT(saveEntry())); connect(action, SIGNAL(triggered()), this, SLOT(commitEntry()));
this->addAction(action); this->addAction(action);
m_mainUi->passwordGenerator->hide(); m_mainUi->passwordGenerator->hide();
@ -156,6 +157,8 @@ void EditEntryWidget::setupAdvanced()
connect(m_advancedUi->attributesView->selectionModel(), connect(m_advancedUi->attributesView->selectionModel(),
SIGNAL(currentChanged(QModelIndex,QModelIndex)), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
SLOT(updateCurrentAttribute())); SLOT(updateCurrentAttribute()));
connect(m_advancedUi->fgColorButton, SIGNAL(clicked()), SLOT(pickColor()));
connect(m_advancedUi->bgColorButton, SIGNAL(clicked()), SLOT(pickColor()));
} }
void EditEntryWidget::setupIcon() void EditEntryWidget::setupIcon()
@ -188,6 +191,8 @@ void EditEntryWidget::setupAutoType()
connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(clearCurrentAssoc())); connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(clearCurrentAssoc()));
connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)), connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)),
SLOT(applyCurrentAssoc())); SLOT(applyCurrentAssoc()));
connect(m_autoTypeUi->customWindowSequenceButton, SIGNAL(toggled(bool)),
SLOT(applyCurrentAssoc()));
connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(QString)), connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(QString)),
SLOT(applyCurrentAssoc())); SLOT(applyCurrentAssoc()));
} }
@ -591,6 +596,8 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
editTriggers = QAbstractItemView::DoubleClicked; editTriggers = QAbstractItemView::DoubleClicked;
} }
m_advancedUi->attributesView->setEditTriggers(editTriggers); m_advancedUi->attributesView->setEditTriggers(editTriggers);
setupColorButton(true, entry->foregroundColor());
setupColorButton(false, entry->backgroundColor());
m_iconsWidget->setEnabled(!m_history); m_iconsWidget->setEnabled(!m_history);
m_autoTypeUi->sequenceEdit->setReadOnly(m_history); m_autoTypeUi->sequenceEdit->setReadOnly(m_history);
m_autoTypeUi->windowTitleCombo->lineEdit()->setReadOnly(m_history); m_autoTypeUi->windowTitleCombo->lineEdit()->setReadOnly(m_history);
@ -676,20 +683,39 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
m_mainUi->titleEdit->setFocus(); m_mainUi->titleEdit->setFocus();
} }
void EditEntryWidget::saveEntry() /**
* Commit the form values to in-memory database representation
*
* @return true is commit successful, otherwise false
*/
bool EditEntryWidget::commitEntry()
{ {
if (m_history) { if (m_history) {
clear(); clear();
hideMessage(); hideMessage();
emit editFinished(false); emit editFinished(false);
return; return true;
} }
if (!passwordsEqual()) { if (!passwordsEqual()) {
showMessage(tr("Different passwords supplied."), MessageWidget::Error); showMessage(tr("Different passwords supplied."), MessageWidget::Error);
return; return false;
} }
// Ask the user to apply the generator password, if open
if (m_mainUi->togglePasswordGeneratorButton->isChecked() &&
m_mainUi->passwordGenerator->getGeneratedPassword() != m_mainUi->passwordEdit->text()) {
auto answer = MessageBox::question(this, tr("Apply generated password?"),
tr("Do you want to apply the generated password to this entry?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (answer == QMessageBox::Yes) {
m_mainUi->passwordGenerator->applyPassword();
}
}
// Hide the password generator
m_mainUi->togglePasswordGeneratorButton->setChecked(false);
if (m_advancedUi->attributesView->currentIndex().isValid() && m_advancedUi->attributesEdit->isEnabled()) { if (m_advancedUi->attributesView->currentIndex().isValid() && m_advancedUi->attributesEdit->isEnabled()) {
QString key = m_attributesModel->keyByIndex(m_advancedUi->attributesView->currentIndex()); QString key = m_attributesModel->keyByIndex(m_advancedUi->attributesView->currentIndex());
m_entryAttributes->set(key, m_advancedUi->attributesEdit->toPlainText(), m_entryAttributes->set(key, m_advancedUi->attributesEdit->toPlainText(),
@ -727,20 +753,19 @@ void EditEntryWidget::saveEntry()
updateSSHAgent(); updateSSHAgent();
} }
#endif #endif
showMessage(tr("Entry updated successfully."), MessageWidget::Positive);
return true;
} }
void EditEntryWidget::acceptEntry() void EditEntryWidget::acceptEntry()
{ {
// Check if passwords are mismatched first to prevent saving if (commitEntry()) {
if (!passwordsEqual()) {
showMessage(tr("Different passwords supplied."), MessageWidget::Error);
return;
}
saveEntry();
clear(); clear();
hideMessage();
emit editFinished(true); emit editFinished(true);
} }
}
void EditEntryWidget::updateEntryData(Entry* entry) const void EditEntryWidget::updateEntryData(Entry* entry) const
{ {
@ -756,27 +781,36 @@ void EditEntryWidget::updateEntryData(Entry* entry) const
entry->setNotes(m_mainUi->notesEdit->toPlainText()); entry->setNotes(m_mainUi->notesEdit->toPlainText());
if (m_advancedUi->fgColorCheckBox->isChecked() &&
m_advancedUi->fgColorButton->property("color").isValid()) {
entry->setForegroundColor(QColor(m_advancedUi->fgColorButton->property("color").toString()));
} else {
entry->setForegroundColor(QColor());
}
if (m_advancedUi->bgColorCheckBox->isChecked() &&
m_advancedUi->bgColorButton->property("color").isValid()) {
entry->setBackgroundColor(QColor(m_advancedUi->bgColorButton->property("color").toString()));
} else {
entry->setBackgroundColor(QColor());
}
IconStruct iconStruct = m_iconsWidget->state(); IconStruct iconStruct = m_iconsWidget->state();
if (iconStruct.number < 0) { if (iconStruct.number < 0) {
entry->setIcon(Entry::DefaultIconNumber); entry->setIcon(Entry::DefaultIconNumber);
} } else if (iconStruct.uuid.isNull()) {
else if (iconStruct.uuid.isNull()) {
entry->setIcon(iconStruct.number); entry->setIcon(iconStruct.number);
} } else {
else {
entry->setIcon(iconStruct.uuid); entry->setIcon(iconStruct.uuid);
} }
entry->setAutoTypeEnabled(m_autoTypeUi->enableButton->isChecked()); entry->setAutoTypeEnabled(m_autoTypeUi->enableButton->isChecked());
if (m_autoTypeUi->inheritSequenceButton->isChecked()) { if (m_autoTypeUi->inheritSequenceButton->isChecked()) {
entry->setDefaultAutoTypeSequence(QString()); entry->setDefaultAutoTypeSequence(QString());
} } else if (AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) {
else {
if (AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) {
entry->setDefaultAutoTypeSequence(m_autoTypeUi->sequenceEdit->text()); entry->setDefaultAutoTypeSequence(m_autoTypeUi->sequenceEdit->text());
} }
}
entry->autoTypeAssociations()->copyDataFrom(m_autoTypeAssoc); entry->autoTypeAssociations()->copyDataFrom(m_autoTypeAssoc);
} }
@ -1120,3 +1154,38 @@ QMenu* EditEntryWidget::createPresetsMenu()
expirePresetsMenu->addAction(tr("1 year"))->setData(QVariant::fromValue(TimeDelta::fromYears(1))); expirePresetsMenu->addAction(tr("1 year"))->setData(QVariant::fromValue(TimeDelta::fromYears(1)));
return expirePresetsMenu; return expirePresetsMenu;
} }
void EditEntryWidget::setupColorButton(bool foreground, const QColor& color)
{
QWidget* button = m_advancedUi->fgColorButton;
QCheckBox* checkBox = m_advancedUi->fgColorCheckBox;
if (!foreground) {
button = m_advancedUi->bgColorButton;
checkBox = m_advancedUi->bgColorCheckBox;
}
if (color.isValid()) {
button->setStyleSheet(QString("background-color:%1").arg(color.name()));
button->setProperty("color", color.name());
checkBox->setChecked(true);
} else {
button->setStyleSheet("");
button->setProperty("color", QVariant());
checkBox->setChecked(false);
}
}
void EditEntryWidget::pickColor()
{
bool isForeground = (sender() == m_advancedUi->fgColorButton);
QColor oldColor = QColor(m_advancedUi->fgColorButton->property("color").toString());
if (!isForeground) {
oldColor = QColor(m_advancedUi->bgColorButton->property("color").toString());
}
QColorDialog colorDialog(this);
QColor newColor = colorDialog.getColor(oldColor);
if (newColor.isValid()) {
setupColorButton(isForeground, newColor);
}
}

View File

@ -74,7 +74,7 @@ signals:
private slots: private slots:
void acceptEntry(); void acceptEntry();
void saveEntry(); bool commitEntry();
void cancel(); void cancel();
void togglePasswordGeneratorButton(bool checked); void togglePasswordGeneratorButton(bool checked);
void setGeneratedPassword(const QString& password); void setGeneratedPassword(const QString& password);
@ -99,6 +99,7 @@ private slots:
void updateHistoryButtons(const QModelIndex& current, const QModelIndex& previous); void updateHistoryButtons(const QModelIndex& current, const QModelIndex& previous);
void useExpiryPreset(QAction* action); void useExpiryPreset(QAction* action);
void toggleHideNotes(bool visible); void toggleHideNotes(bool visible);
void pickColor();
#ifdef WITH_XC_SSHAGENT #ifdef WITH_XC_SSHAGENT
void updateSSHAgent(); void updateSSHAgent();
void updateSSHAgentAttachment(); void updateSSHAgentAttachment();
@ -120,6 +121,7 @@ private:
#endif #endif
void setupProperties(); void setupProperties();
void setupHistory(); void setupHistory();
void setupColorButton(bool foreground, const QColor& color);
bool passwordsEqual(); bool passwordsEqual();
void setForms(const Entry* entry, bool restore = false); void setForms(const Entry* entry, bool restore = false);

View File

@ -2,19 +2,15 @@
<ui version="4.0"> <ui version="4.0">
<class>EditEntryWidgetAdvanced</class> <class>EditEntryWidgetAdvanced</class>
<widget class="QWidget" name="EditEntryWidgetAdvanced"> <widget class="QWidget" name="EditEntryWidgetAdvanced">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>532</width>
<height>364</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<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> <item>
<widget class="QGroupBox" name="attributesBox"> <widget class="QGroupBox" name="attributesBox">
<property name="title"> <property name="title">
@ -157,6 +153,96 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QWidget" name="colorsBox" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="fgColorCheckBox">
<property name="text">
<string>Foreground Color:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fgColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="bgColorCheckBox">
<property name="text">
<string>Background Color:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bgColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>

View File

@ -18,6 +18,7 @@
#include "EntryModel.h" #include "EntryModel.h"
#include <QFont> #include <QFont>
#include <QFontMetrics>
#include <QMimeData> #include <QMimeData>
#include <QPalette> #include <QPalette>
#include <QDateTime> #include <QDateTime>
@ -263,10 +264,26 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
font.setStrikeOut(true); font.setStrikeOut(true);
} }
return font; return font;
} else if (role == Qt::TextColorRole) { } else if (role == Qt::ForegroundRole) {
if (entry->hasReferences()) { if (entry->hasReferences()) {
QPalette p; QPalette p;
return QVariant(p.color(QPalette::Active, QPalette::Mid)); return QVariant(p.color(QPalette::Active, QPalette::Mid));
} else if (entry->foregroundColor().isValid()) {
return QVariant(entry->foregroundColor());
}
} else if (role == Qt::BackgroundRole) {
if (entry->backgroundColor().isValid()) {
return QVariant(entry->backgroundColor());
}
} else if (role == Qt::TextAlignmentRole) {
if (index.column() == Paperclip) {
return Qt::AlignCenter;
}
} else if (role == Qt::SizeHintRole) {
if (index.column() == Paperclip) {
QFont font;
QFontMetrics fm(font);
return fm.width(PaperClipDisplay) / 2;
} }
} }
@ -275,7 +292,9 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int role) const QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int role) const
{ {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { Q_UNUSED(orientation);
if (role == Qt::DisplayRole) {
switch (section) { switch (section) {
case ParentGroup: case ParentGroup:
return tr("Group"); return tr("Group");
@ -302,6 +321,11 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro
case Attachments: case Attachments:
return tr("Attachments"); return tr("Attachments");
} }
} else if (role == Qt::TextAlignmentRole) {
switch (section) {
case Paperclip:
return Qt::AlignCenter;
}
} }
return QVariant(); return QVariant();

View File

@ -1,4 +1,5 @@
/* /*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de> * Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -15,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QScopedPointer>
#include "TestEntry.h" #include "TestEntry.h"
#include "TestGlobal.h" #include "TestGlobal.h"
#include "crypto/Crypto.h" #include "crypto/Crypto.h"
@ -28,7 +31,7 @@ void TestEntry::initTestCase()
void TestEntry::testHistoryItemDeletion() void TestEntry::testHistoryItemDeletion()
{ {
Entry* entry = new Entry(); QScopedPointer<Entry> entry(new Entry());
QPointer<Entry> historyEntry = new Entry(); QPointer<Entry> historyEntry = new Entry();
entry->addHistoryItem(historyEntry); entry->addHistoryItem(historyEntry);
@ -39,13 +42,11 @@ void TestEntry::testHistoryItemDeletion()
entry->removeHistoryItems(historyEntriesToRemove); entry->removeHistoryItems(historyEntriesToRemove);
QCOMPARE(entry->historyItems().size(), 0); QCOMPARE(entry->historyItems().size(), 0);
QVERIFY(historyEntry.isNull()); QVERIFY(historyEntry.isNull());
delete entry;
} }
void TestEntry::testCopyDataFrom() void TestEntry::testCopyDataFrom()
{ {
Entry* entry = new Entry(); QScopedPointer<Entry> entry(new Entry());
entry->setTitle("testtitle"); entry->setTitle("testtitle");
entry->attributes()->set("attr1", "abc"); entry->attributes()->set("attr1", "abc");
@ -62,9 +63,8 @@ void TestEntry::testCopyDataFrom()
assoc.sequence = "4"; assoc.sequence = "4";
entry->autoTypeAssociations()->add(assoc); entry->autoTypeAssociations()->add(assoc);
Entry* entry2 = new Entry(); QScopedPointer<Entry> entry2(new Entry());
entry2->copyDataFrom(entry); entry2->copyDataFrom(entry.data());
delete entry;
QCOMPARE(entry2->title(), QString("testtitle")); QCOMPARE(entry2->title(), QString("testtitle"));
QCOMPARE(entry2->attributes()->value("attr1"), QString("abc")); QCOMPARE(entry2->attributes()->value("attr1"), QString("abc"));
@ -77,13 +77,11 @@ void TestEntry::testCopyDataFrom()
QCOMPARE(entry2->autoTypeAssociations()->size(), 2); QCOMPARE(entry2->autoTypeAssociations()->size(), 2);
QCOMPARE(entry2->autoTypeAssociations()->get(0).window, QString("1")); QCOMPARE(entry2->autoTypeAssociations()->get(0).window, QString("1"));
QCOMPARE(entry2->autoTypeAssociations()->get(1).window, QString("3")); QCOMPARE(entry2->autoTypeAssociations()->get(1).window, QString("3"));
delete entry2;
} }
void TestEntry::testClone() void TestEntry::testClone()
{ {
Entry* entryOrg = new Entry(); QScopedPointer<Entry> entryOrg(new Entry());
entryOrg->setUuid(Uuid::random()); entryOrg->setUuid(Uuid::random());
entryOrg->setTitle("Original Title"); entryOrg->setTitle("Original Title");
entryOrg->beginUpdate(); entryOrg->beginUpdate();
@ -96,42 +94,58 @@ void TestEntry::testClone()
entryOrgTime.setCreationTime(dateTime); entryOrgTime.setCreationTime(dateTime);
entryOrg->setTimeInfo(entryOrgTime); entryOrg->setTimeInfo(entryOrgTime);
Entry* entryCloneNone = entryOrg->clone(Entry::CloneNoFlags); QScopedPointer<Entry> entryCloneNone(entryOrg->clone(Entry::CloneNoFlags));
QCOMPARE(entryCloneNone->uuid(), entryOrg->uuid()); QCOMPARE(entryCloneNone->uuid(), entryOrg->uuid());
QCOMPARE(entryCloneNone->title(), QString("New Title")); QCOMPARE(entryCloneNone->title(), QString("New Title"));
QCOMPARE(entryCloneNone->historyItems().size(), 0); QCOMPARE(entryCloneNone->historyItems().size(), 0);
QCOMPARE(entryCloneNone->timeInfo().creationTime(), entryOrg->timeInfo().creationTime()); QCOMPARE(entryCloneNone->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
delete entryCloneNone;
Entry* entryCloneNewUuid = entryOrg->clone(Entry::CloneNewUuid); QScopedPointer<Entry> entryCloneNewUuid(entryOrg->clone(Entry::CloneNewUuid));
QVERIFY(entryCloneNewUuid->uuid() != entryOrg->uuid()); QVERIFY(entryCloneNewUuid->uuid() != entryOrg->uuid());
QVERIFY(!entryCloneNewUuid->uuid().isNull()); QVERIFY(!entryCloneNewUuid->uuid().isNull());
QCOMPARE(entryCloneNewUuid->title(), QString("New Title")); QCOMPARE(entryCloneNewUuid->title(), QString("New Title"));
QCOMPARE(entryCloneNewUuid->historyItems().size(), 0); QCOMPARE(entryCloneNewUuid->historyItems().size(), 0);
QCOMPARE(entryCloneNewUuid->timeInfo().creationTime(), entryOrg->timeInfo().creationTime()); QCOMPARE(entryCloneNewUuid->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
delete entryCloneNewUuid;
Entry* entryCloneResetTime = entryOrg->clone(Entry::CloneResetTimeInfo); QScopedPointer<Entry> entryCloneResetTime(entryOrg->clone(Entry::CloneResetTimeInfo));
QCOMPARE(entryCloneResetTime->uuid(), entryOrg->uuid()); QCOMPARE(entryCloneResetTime->uuid(), entryOrg->uuid());
QCOMPARE(entryCloneResetTime->title(), QString("New Title")); QCOMPARE(entryCloneResetTime->title(), QString("New Title"));
QCOMPARE(entryCloneResetTime->historyItems().size(), 0); QCOMPARE(entryCloneResetTime->historyItems().size(), 0);
QVERIFY(entryCloneResetTime->timeInfo().creationTime() != entryOrg->timeInfo().creationTime()); QVERIFY(entryCloneResetTime->timeInfo().creationTime() != entryOrg->timeInfo().creationTime());
delete entryCloneResetTime;
Entry* entryCloneHistory = entryOrg->clone(Entry::CloneIncludeHistory); QScopedPointer<Entry> entryCloneHistory(entryOrg->clone(Entry::CloneIncludeHistory));
QCOMPARE(entryCloneHistory->uuid(), entryOrg->uuid()); QCOMPARE(entryCloneHistory->uuid(), entryOrg->uuid());
QCOMPARE(entryCloneHistory->title(), QString("New Title")); QCOMPARE(entryCloneHistory->title(), QString("New Title"));
QCOMPARE(entryCloneHistory->historyItems().size(), 1); QCOMPARE(entryCloneHistory->historyItems().size(), 1);
QCOMPARE(entryCloneHistory->historyItems().at(0)->title(), QString("Original Title")); QCOMPARE(entryCloneHistory->historyItems().at(0)->title(), QString("Original Title"));
QCOMPARE(entryCloneHistory->timeInfo().creationTime(), entryOrg->timeInfo().creationTime()); QCOMPARE(entryCloneHistory->timeInfo().creationTime(), entryOrg->timeInfo().creationTime());
delete entryCloneHistory;
delete entryOrg; Database db;
auto* entryOrgClone = entryOrg->clone(Entry::CloneNoFlags);
entryOrgClone->setGroup(db.rootGroup());
Entry* entryCloneUserRef = entryOrgClone->clone(Entry::CloneUserAsRef);
entryCloneUserRef->setGroup(db.rootGroup());
QCOMPARE(entryCloneUserRef->uuid(), entryOrgClone->uuid());
QCOMPARE(entryCloneUserRef->title(), QString("New Title"));
QCOMPARE(entryCloneUserRef->historyItems().size(), 0);
QCOMPARE(entryCloneUserRef->timeInfo().creationTime(), entryOrgClone->timeInfo().creationTime());
QVERIFY(entryCloneUserRef->attributes()->isReference(EntryAttributes::UserNameKey));
QCOMPARE(entryCloneUserRef->resolvePlaceholder(entryCloneUserRef->username()), entryOrgClone->username());
Entry* entryClonePassRef = entryOrgClone->clone(Entry::ClonePassAsRef);
entryClonePassRef->setGroup(db.rootGroup());
QCOMPARE(entryClonePassRef->uuid(), entryOrgClone->uuid());
QCOMPARE(entryClonePassRef->title(), QString("New Title"));
QCOMPARE(entryClonePassRef->historyItems().size(), 0);
QCOMPARE(entryClonePassRef->timeInfo().creationTime(), entryOrgClone->timeInfo().creationTime());
QVERIFY(entryClonePassRef->attributes()->isReference(EntryAttributes::PasswordKey));
QCOMPARE(entryClonePassRef->resolvePlaceholder(entryCloneUserRef->password()), entryOrg->password());
} }
void TestEntry::testResolveUrl() void TestEntry::testResolveUrl()
{ {
Entry* entry = new Entry(); QScopedPointer<Entry> entry(new Entry());
QString testUrl("www.google.com"); QString testUrl("www.google.com");
QString testCmd("cmd://firefox " + testUrl); QString testCmd("cmd://firefox " + testUrl);
QString testComplexCmd("cmd://firefox --start-now --url 'http://" + testUrl + "' --quit"); QString testComplexCmd("cmd://firefox --start-now --url 'http://" + testUrl + "' --quit");
@ -152,8 +166,6 @@ void TestEntry::testResolveUrl()
QCOMPARE(entry->resolveUrl(nonHttpUrl), QString("")); QCOMPARE(entry->resolveUrl(nonHttpUrl), QString(""));
// Test no URL // Test no URL
QCOMPARE(entry->resolveUrl(noUrl), QString("")); QCOMPARE(entry->resolveUrl(noUrl), QString(""));
delete entry;
} }
void TestEntry::testResolveUrlPlaceholders() void TestEntry::testResolveUrlPlaceholders()
@ -189,9 +201,9 @@ void TestEntry::testResolveUrlPlaceholders()
void TestEntry::testResolveRecursivePlaceholders() void TestEntry::testResolveRecursivePlaceholders()
{ {
Database db; Database db;
Group* root = db.rootGroup(); auto* root = db.rootGroup();
Entry* entry1 = new Entry(); auto* entry1 = new Entry();
entry1->setGroup(root); entry1->setGroup(root);
entry1->setUuid(Uuid::random()); entry1->setUuid(Uuid::random());
entry1->setTitle("{USERNAME}"); entry1->setTitle("{USERNAME}");
@ -201,7 +213,7 @@ void TestEntry::testResolveRecursivePlaceholders()
entry1->attributes()->set("CustomTitle", "RecursiveValue"); entry1->attributes()->set("CustomTitle", "RecursiveValue");
QCOMPARE(entry1->resolveMultiplePlaceholders(entry1->title()), QString("RecursiveValue")); QCOMPARE(entry1->resolveMultiplePlaceholders(entry1->title()), QString("RecursiveValue"));
Entry* entry2 = new Entry(); auto* entry2 = new Entry();
entry2->setGroup(root); entry2->setGroup(root);
entry2->setUuid(Uuid::random()); entry2->setUuid(Uuid::random());
entry2->setTitle("Entry2Title"); entry2->setTitle("Entry2Title");
@ -213,7 +225,7 @@ void TestEntry::testResolveRecursivePlaceholders()
entry2->attributes()->set("Port", "1234"); entry2->attributes()->set("Port", "1234");
entry2->attributes()->set("Uri", "uri/path"); entry2->attributes()->set("Uri", "uri/path");
Entry* entry3 = new Entry(); auto* entry3 = new Entry();
entry3->setGroup(root); entry3->setGroup(root);
entry3->setUuid(Uuid::random()); entry3->setUuid(Uuid::random());
entry3->setTitle(QString("{REF:T@I:%1}").arg(entry2->uuid().toHex())); entry3->setTitle(QString("{REF:T@I:%1}").arg(entry2->uuid().toHex()));
@ -226,7 +238,7 @@ void TestEntry::testResolveRecursivePlaceholders()
QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->password()), QString("RecursiveValue")); QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->password()), QString("RecursiveValue"));
QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->url()), QString("http://127.0.0.1:1234/uri/path")); QCOMPARE(entry3->resolveMultiplePlaceholders(entry3->url()), QString("http://127.0.0.1:1234/uri/path"));
Entry* entry4 = new Entry(); auto* entry4 = new Entry();
entry4->setGroup(root); entry4->setGroup(root);
entry4->setUuid(Uuid::random()); entry4->setUuid(Uuid::random());
entry4->setTitle(QString("{REF:T@I:%1}").arg(entry3->uuid().toHex())); entry4->setTitle(QString("{REF:T@I:%1}").arg(entry3->uuid().toHex()));
@ -239,7 +251,7 @@ void TestEntry::testResolveRecursivePlaceholders()
QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->password()), QString("RecursiveValue")); QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->password()), QString("RecursiveValue"));
QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->url()), QString("http://127.0.0.1:1234/uri/path")); QCOMPARE(entry4->resolveMultiplePlaceholders(entry4->url()), QString("http://127.0.0.1:1234/uri/path"));
Entry* entry5 = new Entry(); auto* entry5 = new Entry();
entry5->setGroup(root); entry5->setGroup(root);
entry5->setUuid(Uuid::random()); entry5->setUuid(Uuid::random());
entry5->attributes()->set("Scheme", "http"); entry5->attributes()->set("Scheme", "http");
@ -256,14 +268,25 @@ void TestEntry::testResolveRecursivePlaceholders()
const QString url("http://username:password@host.org:2017/some/path?q=e&t=s#fragment"); const QString url("http://username:password@host.org:2017/some/path?q=e&t=s#fragment");
QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->url()), url); QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->url()), url);
QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->title()), QString("title+/some/path+fragment+title")); QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->title()), QString("title+/some/path+fragment+title"));
auto* entry6 = new Entry();
entry6->setGroup(root);
entry6->setUuid(Uuid::random());
entry6->setTitle(QString("{REF:T@I:%1}").arg(entry3->uuid().toHex()));
entry6->setUsername(QString("{TITLE}"));
entry6->setPassword(QString("{PASSWORD}"));
QCOMPARE(entry6->resolvePlaceholder(entry6->title()), QString("Entry2Title"));
QCOMPARE(entry6->resolvePlaceholder(entry6->username()), QString("Entry2Title"));
QCOMPARE(entry6->resolvePlaceholder(entry6->password()), QString("{PASSWORD}"));
} }
void TestEntry::testResolveReferencePlaceholders() void TestEntry::testResolveReferencePlaceholders()
{ {
Database db; Database db;
Group* root = db.rootGroup(); auto* root = db.rootGroup();
Entry* entry1 = new Entry(); auto* entry1 = new Entry();
entry1->setGroup(root); entry1->setGroup(root);
entry1->setUuid(Uuid::random()); entry1->setUuid(Uuid::random());
entry1->setTitle("Title1"); entry1->setTitle("Title1");
@ -273,9 +296,9 @@ void TestEntry::testResolveReferencePlaceholders()
entry1->setNotes("Notes1"); entry1->setNotes("Notes1");
entry1->attributes()->set("CustomAttribute1", "CustomAttributeValue1"); entry1->attributes()->set("CustomAttribute1", "CustomAttributeValue1");
Group* group = new Group(); auto* group = new Group();
group->setParent(root); group->setParent(root);
Entry* entry2 = new Entry(); auto* entry2 = new Entry();
entry2->setGroup(group); entry2->setGroup(group);
entry2->setUuid(Uuid::random()); entry2->setUuid(Uuid::random());
entry2->setTitle("Title2"); entry2->setTitle("Title2");
@ -285,7 +308,7 @@ void TestEntry::testResolveReferencePlaceholders()
entry2->setNotes("Notes2"); entry2->setNotes("Notes2");
entry2->attributes()->set("CustomAttribute2", "CustomAttributeValue2"); entry2->attributes()->set("CustomAttribute2", "CustomAttributeValue2");
Entry* entry3 = new Entry(); auto* entry3 = new Entry();
entry3->setGroup(group); entry3->setGroup(group);
entry3->setUuid(Uuid::random()); entry3->setUuid(Uuid::random());
entry3->setTitle("{S:AttributeTitle}"); entry3->setTitle("{S:AttributeTitle}");
@ -299,7 +322,7 @@ void TestEntry::testResolveReferencePlaceholders()
entry3->attributes()->set("AttributeUrl", "UrlValue"); entry3->attributes()->set("AttributeUrl", "UrlValue");
entry3->attributes()->set("AttributeNotes", "NotesValue"); entry3->attributes()->set("AttributeNotes", "NotesValue");
Entry* tstEntry = new Entry(); auto* tstEntry = new Entry();
tstEntry->setGroup(root); tstEntry->setGroup(root);
tstEntry->setUuid(Uuid::random()); tstEntry->setUuid(Uuid::random());
@ -356,67 +379,67 @@ void TestEntry::testResolveNonIdPlaceholdersToUuid()
Database db; Database db;
auto* root = db.rootGroup(); auto* root = db.rootGroup();
Entry referencedEntryTitle; auto* referencedEntryTitle = new Entry();
referencedEntryTitle.setGroup(root); referencedEntryTitle->setGroup(root);
referencedEntryTitle.setTitle("myTitle"); referencedEntryTitle->setTitle("myTitle");
referencedEntryTitle.setUuid(Uuid::random()); referencedEntryTitle->setUuid(Uuid::random());
Entry referencedEntryUsername; auto* referencedEntryUsername = new Entry();
referencedEntryUsername.setGroup(root); referencedEntryUsername->setGroup(root);
referencedEntryUsername.setUsername("myUser"); referencedEntryUsername->setUsername("myUser");
referencedEntryUsername.setUuid(Uuid::random()); referencedEntryUsername->setUuid(Uuid::random());
Entry referencedEntryPassword; auto* referencedEntryPassword = new Entry();
referencedEntryPassword.setGroup(root); referencedEntryPassword->setGroup(root);
referencedEntryPassword.setPassword("myPassword"); referencedEntryPassword->setPassword("myPassword");
referencedEntryPassword.setUuid(Uuid::random()); referencedEntryPassword->setUuid(Uuid::random());
Entry referencedEntryUrl; auto* referencedEntryUrl = new Entry();
referencedEntryUrl.setGroup(root); referencedEntryUrl->setGroup(root);
referencedEntryUrl.setUrl("myUrl"); referencedEntryUrl->setUrl("myUrl");
referencedEntryUrl.setUuid(Uuid::random()); referencedEntryUrl->setUuid(Uuid::random());
Entry referencedEntryNotes; auto* referencedEntryNotes = new Entry();
referencedEntryNotes.setGroup(root); referencedEntryNotes->setGroup(root);
referencedEntryNotes.setNotes("myNotes"); referencedEntryNotes->setNotes("myNotes");
referencedEntryNotes.setUuid(Uuid::random()); referencedEntryNotes->setUuid(Uuid::random());
const QList<QChar> placeholders{'T', 'U', 'P', 'A', 'N'}; const QList<QChar> placeholders{'T', 'U', 'P', 'A', 'N'};
for (const QChar searchIn : placeholders) { for (const QChar& searchIn : placeholders) {
const Entry* referencedEntry = nullptr; const Entry* referencedEntry = nullptr;
QString newEntryNotesRaw("{REF:I@%1:%2}"); QString newEntryNotesRaw("{REF:I@%1:%2}");
switch(searchIn.toLatin1()) { switch(searchIn.toLatin1()) {
case 'T': case 'T':
referencedEntry = &referencedEntryTitle; referencedEntry = referencedEntryTitle;
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->title()); newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->title());
break; break;
case 'U': case 'U':
referencedEntry = &referencedEntryUsername; referencedEntry = referencedEntryUsername;
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->username()); newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->username());
break; break;
case 'P': case 'P':
referencedEntry = &referencedEntryPassword; referencedEntry = referencedEntryPassword;
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->password()); newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->password());
break; break;
case 'A': case 'A':
referencedEntry = &referencedEntryUrl; referencedEntry = referencedEntryUrl;
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->url()); newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->url());
break; break;
case 'N': case 'N':
referencedEntry = &referencedEntryNotes; referencedEntry = referencedEntryNotes;
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->notes()); newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->notes());
break; break;
default: default:
break; break;
} }
Entry newEntry; auto* newEntry = new Entry();
newEntry.setGroup(root); newEntry->setGroup(root);
newEntry.setNotes(newEntryNotesRaw); newEntry->setNotes(newEntryNotesRaw);
const auto newEntryNotesResolved = const QString newEntryNotesResolved =
newEntry.resolveMultiplePlaceholders(newEntry.notes()); newEntry->resolveMultiplePlaceholders(newEntry->notes());
QCOMPARE(newEntryNotesResolved, QString(referencedEntry->uuid().toHex())); QCOMPARE(newEntryNotesResolved, QString(referencedEntry->uuid().toHex()));
} }
} }
@ -424,9 +447,9 @@ void TestEntry::testResolveNonIdPlaceholdersToUuid()
void TestEntry::testResolveClonedEntry() void TestEntry::testResolveClonedEntry()
{ {
Database db; Database db;
Group* root = db.rootGroup(); auto* root = db.rootGroup();
Entry* original = new Entry(); auto* original = new Entry();
original->setGroup(root); original->setGroup(root);
original->setUuid(Uuid::random()); original->setUuid(Uuid::random());
original->setTitle("Title"); original->setTitle("Title");

View File

@ -26,6 +26,7 @@
#include <QLabel> #include <QLabel>
#include <QMimeData> #include <QMimeData>
#include <QPushButton> #include <QPushButton>
#include <QCheckBox>
#include <QSpinBox> #include <QSpinBox>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QComboBox> #include <QComboBox>
@ -61,6 +62,7 @@
#include "gui/entry/EntryView.h" #include "gui/entry/EntryView.h"
#include "gui/group/GroupModel.h" #include "gui/group/GroupModel.h"
#include "gui/group/GroupView.h" #include "gui/group/GroupView.h"
#include "gui/group/EditGroupWidget.h"
#include "keys/PasswordKey.h" #include "keys/PasswordKey.h"
void TestGui::initTestCase() void TestGui::initTestCase()
@ -278,6 +280,7 @@ void TestGui::testTabs()
void TestGui::testEditEntry() void TestGui::testEditEntry()
{ {
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar"); QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
int editCount = 0;
// Select the first entry in the database // Select the first entry in the database
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView"); EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
@ -304,7 +307,24 @@ void TestGui::testEditEntry()
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton); QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton);
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
QCOMPARE(entry->title(), QString("Sample Entry_test")); QCOMPARE(entry->title(), QString("Sample Entry_test"));
QCOMPARE(entry->historyItems().size(), 1); QCOMPARE(entry->historyItems().size(), ++editCount);
// Test entry colors (simulate choosing a color)
editEntryWidget->setCurrentPage(1);
auto fgColor = QColor(Qt::red);
auto bgColor = QColor(Qt::blue);
// Set foreground color
auto colorButton = editEntryWidget->findChild<QPushButton*>("fgColorButton");
auto colorCheckBox = editEntryWidget->findChild<QCheckBox*>("fgColorCheckBox");
colorButton->setProperty("color", fgColor);
colorCheckBox->setChecked(true);
// Set background color
colorButton = editEntryWidget->findChild<QPushButton*>("bgColorButton");
colorCheckBox = editEntryWidget->findChild<QCheckBox*>("bgColorCheckBox");
colorButton->setProperty("color", bgColor);
colorCheckBox->setChecked(true);
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton);
QCOMPARE(entry->historyItems().size(), ++editCount);
// Test protected attributes // Test protected attributes
editEntryWidget->setCurrentPage(1); editEntryWidget->setCurrentPage(1);
@ -336,12 +356,68 @@ void TestGui::testEditEntry()
// Confirm edit was made // Confirm edit was made
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode); QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
QCOMPARE(entry->title(), QString("Sample Entry_test")); QCOMPARE(entry->title(), QString("Sample Entry_test"));
QCOMPARE(entry->historyItems().size(), 2); QCOMPARE(entry->foregroundColor(), fgColor);
QCOMPARE(entryItem.data(Qt::ForegroundRole), QVariant(fgColor));
QCOMPARE(entry->backgroundColor(), bgColor);
QCOMPARE(entryItem.data(Qt::BackgroundRole), QVariant(bgColor));
QCOMPARE(entry->historyItems().size(), ++editCount);
// Confirm modified indicator is showing // Confirm modified indicator is showing
QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("%1*").arg(m_dbFileName)); QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("%1*").arg(m_dbFileName));
} }
void TestGui::testSearchEditEntry()
{
// Regression test for Issue #1447 -- Uses example from issue description
// Find buttons for group creation
EditGroupWidget* editGroupWidget = m_dbWidget->findChild<EditGroupWidget*>("editGroupWidget");
QLineEdit* nameEdit = editGroupWidget->findChild<QLineEdit*>("nameEdit");
QDialogButtonBox* editGroupWidgetButtonBox = editGroupWidget->findChild<QDialogButtonBox*>("buttonBox");
// Add groups "Good" and "Bad"
m_dbWidget->createGroup();
QTest::keyClicks(nameEdit, "Good");
QTest::mouseClick(editGroupWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
m_dbWidget->groupView()->setCurrentGroup(m_db->rootGroup()); // Makes "Good" and "Bad" on the same level
m_dbWidget->createGroup();
QTest::keyClicks(nameEdit, "Bad");
QTest::mouseClick(editGroupWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
m_dbWidget->groupView()->setCurrentGroup(m_db->rootGroup());
// Find buttons for entry creation
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
QWidget* entryNewWidget = toolBar->widgetForAction(m_mainWindow->findChild<QAction*>("actionEntryNew"));
EditEntryWidget* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
QLineEdit* titleEdit = editEntryWidget->findChild<QLineEdit*>("titleEdit");
QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
// Create "Doggy" in "Good"
Group* goodGroup = m_dbWidget->currentGroup()->findChildByName(QString("Good"));
m_dbWidget->groupView()->setCurrentGroup(goodGroup);
QTest::mouseClick(entryNewWidget, Qt::LeftButton);
QTest::keyClicks(titleEdit, "Doggy");
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton);
// Select "Bad" group in groupView
Group* badGroup = m_db->rootGroup()->findChildByName(QString("Bad"));
m_dbWidget->groupView()->setCurrentGroup(badGroup);
// Search for "Doggy" entry
SearchWidget* searchWidget = toolBar->findChild<SearchWidget*>("SearchWidget");
QLineEdit* searchTextEdit = searchWidget->findChild<QLineEdit*>("searchEdit");
QTest::mouseClick(searchTextEdit, Qt::LeftButton);
QTest::keyClicks(searchTextEdit, "Doggy");
QTRY_VERIFY(m_dbWidget->isInSearchMode());
// Goto "Doggy"'s edit view
QTest::keyClick(searchTextEdit, Qt::Key_Return);
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
// Check the path in header is "parent-group > entry"
QCOMPARE(m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget")->findChild<QLabel*>("headerLabel")->text(),
QString("Good > Doggy > Edit entry"));
}
void TestGui::testAddEntry() void TestGui::testAddEntry()
{ {
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar"); QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");

View File

@ -46,6 +46,7 @@ private slots:
void testAutoreloadDatabase(); void testAutoreloadDatabase();
void testTabs(); void testTabs();
void testEditEntry(); void testEditEntry();
void testSearchEditEntry();
void testAddEntry(); void testAddEntry();
void testPasswordEntryEntropy(); void testPasswordEntryEntropy();
void testDicewareEntryEntropy(); void testDicewareEntryEntropy();