mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-26 22:36:59 -05:00
Merge branch 'release/2.3.0' into develop
This commit is contained in:
commit
d0c583b5e2
@ -73,27 +73,49 @@ set(KEEPASSXC_VERSION_MINOR "3")
|
||||
set(KEEPASSXC_VERSION_PATCH "0")
|
||||
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
|
||||
execute_process(COMMAND git tag --points-at HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_TAG)
|
||||
if(GIT_TAG)
|
||||
set(KEEPASSXC_RELEASE_BUILD ON)
|
||||
elseif(NOT KEEPASSXC_RELEASE_BUILD)
|
||||
string(REGEX REPLACE "\r?\n$" "" GIT_TAG "${GIT_TAG}")
|
||||
|
||||
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")
|
||||
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")
|
||||
|
||||
# Distribution info
|
||||
set(KEEPASSXC_DIST True)
|
||||
set(KEEPASSXC_DIST_TYPE "Other" CACHE STRING "KeePassXC Distribution type")
|
||||
set(KEEPASSXC_DIST ON)
|
||||
set(KEEPASSXC_DIST_TYPE "Other" CACHE STRING "KeePassXC Distribution Type")
|
||||
set_property(CACHE KEEPASSXC_DIST_TYPE PROPERTY STRINGS Snap AppImage Other)
|
||||
if(KEEPASSXC_DIST_TYPE STREQUAL "Snap")
|
||||
set(KEEPASSXC_DIST_SNAP True)
|
||||
set(KEEPASSXC_DIST_SNAP ON)
|
||||
elseif(KEEPASSXC_DIST_TYPE STREQUAL "AppImage")
|
||||
set(KEEPASSXC_DIST_APPIMAGE True)
|
||||
set(KEEPASSXC_DIST_APPIMAGE ON)
|
||||
elseif(KEEPASSXC_DIST_TYPE STREQUAL "Other")
|
||||
unset(KEEPASSXC_DIST)
|
||||
endif()
|
||||
|
39
release-tool
39
release-tool
@ -246,7 +246,7 @@ checkVersionInCMake() {
|
||||
local app_name_upper="$(echo "$APP_NAME" | tr '[:lower:]' '[:upper:]')"
|
||||
local major_num="$(echo ${RELEASE_NAME} | cut -f1 -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
|
||||
if [ $? -ne 0 ]; then
|
||||
@ -582,19 +582,26 @@ build() {
|
||||
done
|
||||
|
||||
init
|
||||
checkWorkingTreeClean
|
||||
|
||||
OUTPUT_DIR="$(realpath "$OUTPUT_DIR")"
|
||||
|
||||
if $BUILD_SNAPSHOT; then
|
||||
if ${BUILD_SNAPSHOT}; then
|
||||
TAG_NAME="HEAD"
|
||||
local branch=`git rev-parse --abbrev-ref HEAD`
|
||||
logInfo "Using current branch ${branch} to build..."
|
||||
RELEASE_NAME="${RELEASE_NAME}-snapshot"
|
||||
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_BUILD_TYPE=Snapshot"
|
||||
else
|
||||
logInfo "Checking out release tag '${TAG_NAME}'..."
|
||||
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}'..."
|
||||
fi
|
||||
git checkout "$TAG_NAME"
|
||||
CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_RELEASE_BUILD=ON"
|
||||
fi
|
||||
|
||||
logInfo "Creating output directory..."
|
||||
@ -604,20 +611,28 @@ build() {
|
||||
exitError "Failed to create output directory!"
|
||||
fi
|
||||
|
||||
if $BUILD_SOURCE_TARBALL; then
|
||||
if ${BUILD_SOURCE_TARBALL}; then
|
||||
logInfo "Creating source tarball..."
|
||||
local app_name_lower="$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]')"
|
||||
TARBALL_NAME="${app_name_lower}-${RELEASE_NAME}-src.tar.xz"
|
||||
git archive --format=tar "$TAG_NAME" --prefix="${app_name_lower}-${RELEASE_NAME}/" \
|
||||
| xz -6 > "${OUTPUT_DIR}/${TARBALL_NAME}"
|
||||
| xz -6 > "${OUTPUT_DIR}/${TARBALL_NAME}"
|
||||
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..."
|
||||
mkdir -p "${OUTPUT_DIR}/build-release"
|
||||
cd "${OUTPUT_DIR}/build-release"
|
||||
|
||||
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"
|
||||
done
|
||||
|
||||
@ -654,13 +669,13 @@ build() {
|
||||
-DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" $CMAKE_OPTIONS "$SRC_DIR"
|
||||
|
||||
logInfo "Compiling and packaging sources..."
|
||||
make $MAKE_OPTIONS preinstall
|
||||
mingw32-make $MAKE_OPTIONS preinstall
|
||||
# Call cpack directly instead of calling make package.
|
||||
# This is important because we want to build the MSI when making a
|
||||
# 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
|
||||
mkdir -p "${OUTPUT_DIR}/bin-release"
|
||||
|
||||
|
@ -269,8 +269,8 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
||||
}
|
||||
|
||||
/**
|
||||
* Global Autotype entry-point funcion
|
||||
* Perform global autotype on the active window
|
||||
* Global Autotype entry-point function
|
||||
* Perform global Auto-Type on the active window
|
||||
*/
|
||||
void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||
{
|
||||
@ -304,10 +304,19 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||
|
||||
if (matchList.isEmpty()) {
|
||||
m_inAutoType.unlock();
|
||||
QString message = tr("Couldn't find an entry that matches the window title:");
|
||||
message.append("\n\n");
|
||||
message.append(windowTitle);
|
||||
MessageBox::information(nullptr, tr("Auto-Type - KeePassXC"), message);
|
||||
|
||||
if (qobject_cast<QApplication*>(QCoreApplication::instance())) {
|
||||
auto* msgBox = new QMessageBox();
|
||||
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();
|
||||
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
|
||||
@ -315,7 +324,7 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
|
||||
m_inAutoType.unlock();
|
||||
} else {
|
||||
m_windowFromGlobal = m_plugin->activeWindow();
|
||||
AutoTypeSelectDialog* selectDialog = new AutoTypeSelectDialog();
|
||||
auto* selectDialog = new AutoTypeSelectDialog();
|
||||
connect(selectDialog, SIGNAL(matchActivated(AutoTypeMatch)),
|
||||
SLOT(performAutoTypeFromGlobal(AutoTypeMatch)));
|
||||
connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType()));
|
||||
|
@ -3,14 +3,14 @@
|
||||
#ifndef 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_BINARY_DIR "${CMAKE_BINARY_DIR}"
|
||||
#define KEEPASSX_SOURCE_DIR "@CMAKE_SOURCE_DIR@"
|
||||
#define KEEPASSX_BINARY_DIR "@CMAKE_BINARY_DIR@"
|
||||
|
||||
#define KEEPASSX_PREFIX_DIR "${CMAKE_INSTALL_PREFIX}"
|
||||
#define KEEPASSX_PLUGIN_DIR "${PLUGIN_INSTALL_DIR}"
|
||||
#define KEEPASSX_DATA_DIR "${DATA_INSTALL_DIR}"
|
||||
#define KEEPASSX_PREFIX_DIR "@CMAKE_INSTALL_PREFIX@"
|
||||
#define KEEPASSX_PLUGIN_DIR "@PLUGIN_INSTALL_DIR@"
|
||||
#define KEEPASSX_DATA_DIR "@DATA_INSTALL_DIR@"
|
||||
|
||||
#cmakedefine WITH_XC_AUTOTYPE
|
||||
#cmakedefine WITH_XC_NETWORKING
|
||||
@ -19,13 +19,16 @@
|
||||
#cmakedefine WITH_XC_YUBIKEY
|
||||
#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_TYPE "@KEEPASSXC_DIST_TYPE@"
|
||||
#cmakedefine KEEPASSXC_DIST_SNAP
|
||||
#cmakedefine KEEPASSXC_DIST_APPIMAGE
|
||||
|
||||
#cmakedefine KEEPASSXC_RELEASE_BUILD
|
||||
|
||||
#cmakedefine HAVE_PR_SET_DUMPABLE 1
|
||||
#cmakedefine HAVE_RLIMIT_CORE 1
|
||||
#cmakedefine HAVE_PT_DENY_ATTACH 1
|
||||
|
@ -772,23 +772,41 @@ QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxD
|
||||
|
||||
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);
|
||||
switch (typeOfPlaceholder) {
|
||||
case PlaceholderType::NotPlaceholder:
|
||||
case PlaceholderType::Unknown:
|
||||
return placeholder;
|
||||
case PlaceholderType::Title:
|
||||
return title();
|
||||
if (placeholderType(title()) == PlaceholderType::Title) {
|
||||
return title();
|
||||
}
|
||||
return resolvePlaceholderRecursive(title(), maxDepth - 1);
|
||||
case PlaceholderType::UserName:
|
||||
return username();
|
||||
if (placeholderType(username()) == PlaceholderType::UserName) {
|
||||
return username();
|
||||
}
|
||||
return resolvePlaceholderRecursive(username(), maxDepth - 1);
|
||||
case PlaceholderType::Password:
|
||||
return password();
|
||||
if (placeholderType(password()) == PlaceholderType::Password) {
|
||||
return password();
|
||||
}
|
||||
return resolvePlaceholderRecursive(password(), maxDepth - 1);
|
||||
case PlaceholderType::Notes:
|
||||
return notes();
|
||||
case PlaceholderType::Totp:
|
||||
return totp();
|
||||
if (placeholderType(notes()) == PlaceholderType::Notes) {
|
||||
return notes();
|
||||
}
|
||||
return resolvePlaceholderRecursive(notes(), maxDepth - 1);
|
||||
case PlaceholderType::Url:
|
||||
return url();
|
||||
if (placeholderType(url()) == PlaceholderType::Url) {
|
||||
return url();
|
||||
}
|
||||
return resolvePlaceholderRecursive(url(), maxDepth - 1);
|
||||
case PlaceholderType::UrlWithoutScheme:
|
||||
case PlaceholderType::UrlScheme:
|
||||
case PlaceholderType::UrlHost:
|
||||
@ -802,6 +820,9 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe
|
||||
const QString strUrl = resolveMultiplePlaceholdersRecursive(url(), maxDepth - 1);
|
||||
return resolveUrlPlaceholder(strUrl, typeOfPlaceholder);
|
||||
}
|
||||
case PlaceholderType::Totp:
|
||||
// totp can't have placeholder inside
|
||||
return totp();
|
||||
case PlaceholderType::CustomAttribute: {
|
||||
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
|
||||
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
|
||||
{
|
||||
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>}
|
||||
// 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 EntryReferenceType searchInType = Entry::referenceType(searchIn);
|
||||
|
||||
Q_ASSERT(m_group);
|
||||
Q_ASSERT(m_group->database());
|
||||
const Entry* refEntry = m_group->database()->resolveEntry(searchText, searchInType);
|
||||
|
||||
if (refEntry) {
|
||||
|
@ -700,7 +700,8 @@ Entry* KdbxXmlReader::parseEntry(bool history)
|
||||
entry->setIcon(uuid);
|
||||
}
|
||||
continue;
|
||||
}if (m_xml.name() == "ForegroundColor") {
|
||||
}
|
||||
if (m_xml.name() == "ForegroundColor") {
|
||||
entry->setForegroundColor(readColor());
|
||||
continue;
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ AboutDialog::AboutDialog(QWidget* parent)
|
||||
|
||||
QString debugInfo = "KeePassXC - ";
|
||||
debugInfo.append(tr("Version %1\n").arg(KEEPASSX_VERSION));
|
||||
#ifndef KEEPASSXC_RELEASE_BUILD
|
||||
debugInfo.append(tr("Build Type: Snapshot\n"));
|
||||
#ifndef KEEPASSXC_BUILD_TYPE_RELEASE
|
||||
debugInfo.append(tr("Build Type: %1\n").arg(KEEPASSXC_BUILD_TYPE));
|
||||
#endif
|
||||
if (!commitHash.isEmpty()) {
|
||||
debugInfo.append(tr("Revision: %1").arg(commitHash.left(7)).append("\n"));
|
||||
|
@ -147,6 +147,13 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Project Maintainers:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
@ -156,15 +163,29 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"><p>Project Maintainers:</p>
|
||||
<ul>
|
||||
<string notr="true"><ul>
|
||||
<li>droidmonkey</li>
|
||||
<li>phoerious</li>
|
||||
<li>TheZ3ro</li>
|
||||
<li>louib</li>
|
||||
<li>weslly</li>
|
||||
</ul>
|
||||
<p>Special thanks from the KeePassXC team go to debfx for creating the original KeePassX.</></string>
|
||||
</ul></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>
|
||||
</widget>
|
||||
</item>
|
||||
@ -198,8 +219,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>423</width>
|
||||
<height>816</height>
|
||||
<width>449</width>
|
||||
<height>803</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
|
@ -776,21 +776,18 @@ void DatabaseWidget::switchToView(bool accepted)
|
||||
m_newGroup->setParent(m_newParent);
|
||||
m_groupView->setCurrentGroup(m_newGroup);
|
||||
m_groupView->expandGroup(m_newParent);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
delete m_newGroup;
|
||||
}
|
||||
|
||||
m_newGroup = nullptr;
|
||||
m_newParent = nullptr;
|
||||
}
|
||||
else if (m_newEntry) {
|
||||
} else if (m_newEntry) {
|
||||
if (accepted) {
|
||||
m_newEntry->setGroup(m_newParent);
|
||||
m_entryView->setFocus();
|
||||
m_entryView->setCurrentEntry(m_newEntry);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
delete m_newEntry;
|
||||
}
|
||||
|
||||
@ -798,6 +795,10 @@ void DatabaseWidget::switchToView(bool accepted)
|
||||
m_newParent = nullptr;
|
||||
}
|
||||
|
||||
if (accepted) {
|
||||
showMessage(tr("Entry updated successfully."), MessageWidget::Positive, false, 2000);
|
||||
}
|
||||
|
||||
setCurrentWidget(m_mainWidget);
|
||||
}
|
||||
|
||||
@ -819,7 +820,16 @@ void DatabaseWidget::switchToEntryEdit(Entry* entry)
|
||||
|
||||
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);
|
||||
|
||||
m_editEntryWidget->loadEntry(entry, create, false, group->name(), m_db);
|
||||
|
@ -119,7 +119,8 @@ bool EditWidget::readOnly() const
|
||||
|
||||
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()
|
||||
|
@ -425,7 +425,7 @@ MainWindow::MainWindow()
|
||||
}
|
||||
#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"
|
||||
"There is a high risk of corruption, maintain a backup of your databases.\n"
|
||||
"This version is not meant for production use."),
|
||||
|
@ -151,6 +151,11 @@ void PasswordGeneratorWidget::setStandaloneMode(bool standalone)
|
||||
}
|
||||
}
|
||||
|
||||
QString PasswordGeneratorWidget::getGeneratedPassword()
|
||||
{
|
||||
return m_ui->editNewPassword->text();
|
||||
}
|
||||
|
||||
void PasswordGeneratorWidget::keyPressEvent(QKeyEvent* e)
|
||||
{
|
||||
if (e->key() == Qt::Key_Escape && m_standalone == true) {
|
||||
|
@ -49,16 +49,18 @@ public:
|
||||
void saveSettings();
|
||||
void reset();
|
||||
void setStandaloneMode(bool standalone);
|
||||
public Q_SLOTS:
|
||||
QString getGeneratedPassword();
|
||||
|
||||
public slots:
|
||||
void regeneratePassword();
|
||||
void applyPassword();
|
||||
void copyPassword();
|
||||
|
||||
signals:
|
||||
void appliedPassword(const QString& password);
|
||||
void dialogTerminated();
|
||||
|
||||
private slots:
|
||||
void applyPassword();
|
||||
void copyPassword();
|
||||
void updateButtonsEnabled(const QString& password);
|
||||
void updatePasswordStrength(const QString& password);
|
||||
void togglePasswordShown(bool hidden);
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <QTemporaryFile>
|
||||
#include <QMimeData>
|
||||
#include <QEvent>
|
||||
#include <QColorDialog>
|
||||
|
||||
#include "autotype/AutoType.h"
|
||||
#include "core/Config.h"
|
||||
@ -97,7 +98,7 @@ EditEntryWidget::EditEntryWidget(QWidget* parent)
|
||||
|
||||
connect(this, SIGNAL(accepted()), SLOT(acceptEntry()));
|
||||
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(messageEditEntryDismiss()), SLOT(hideMessage()));
|
||||
|
||||
@ -127,7 +128,7 @@ void EditEntryWidget::setupMain()
|
||||
|
||||
QAction *action = new QAction(this);
|
||||
action->setShortcut(Qt::CTRL | Qt::Key_Return);
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(saveEntry()));
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(commitEntry()));
|
||||
this->addAction(action);
|
||||
|
||||
m_mainUi->passwordGenerator->hide();
|
||||
@ -156,6 +157,8 @@ void EditEntryWidget::setupAdvanced()
|
||||
connect(m_advancedUi->attributesView->selectionModel(),
|
||||
SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||||
SLOT(updateCurrentAttribute()));
|
||||
connect(m_advancedUi->fgColorButton, SIGNAL(clicked()), SLOT(pickColor()));
|
||||
connect(m_advancedUi->bgColorButton, SIGNAL(clicked()), SLOT(pickColor()));
|
||||
}
|
||||
|
||||
void EditEntryWidget::setupIcon()
|
||||
@ -188,6 +191,8 @@ void EditEntryWidget::setupAutoType()
|
||||
connect(m_autoTypeAssocModel, SIGNAL(modelReset()), SLOT(clearCurrentAssoc()));
|
||||
connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(QString)),
|
||||
SLOT(applyCurrentAssoc()));
|
||||
connect(m_autoTypeUi->customWindowSequenceButton, SIGNAL(toggled(bool)),
|
||||
SLOT(applyCurrentAssoc()));
|
||||
connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(QString)),
|
||||
SLOT(applyCurrentAssoc()));
|
||||
}
|
||||
@ -591,6 +596,8 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
||||
editTriggers = QAbstractItemView::DoubleClicked;
|
||||
}
|
||||
m_advancedUi->attributesView->setEditTriggers(editTriggers);
|
||||
setupColorButton(true, entry->foregroundColor());
|
||||
setupColorButton(false, entry->backgroundColor());
|
||||
m_iconsWidget->setEnabled(!m_history);
|
||||
m_autoTypeUi->sequenceEdit->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();
|
||||
}
|
||||
|
||||
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) {
|
||||
clear();
|
||||
hideMessage();
|
||||
emit editFinished(false);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!passwordsEqual()) {
|
||||
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()) {
|
||||
QString key = m_attributesModel->keyByIndex(m_advancedUi->attributesView->currentIndex());
|
||||
m_entryAttributes->set(key, m_advancedUi->attributesEdit->toPlainText(),
|
||||
@ -727,19 +753,18 @@ void EditEntryWidget::saveEntry()
|
||||
updateSSHAgent();
|
||||
}
|
||||
#endif
|
||||
|
||||
showMessage(tr("Entry updated successfully."), MessageWidget::Positive);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditEntryWidget::acceptEntry()
|
||||
{
|
||||
// Check if passwords are mismatched first to prevent saving
|
||||
if (!passwordsEqual()) {
|
||||
showMessage(tr("Different passwords supplied."), MessageWidget::Error);
|
||||
return;
|
||||
if (commitEntry()) {
|
||||
clear();
|
||||
hideMessage();
|
||||
emit editFinished(true);
|
||||
}
|
||||
|
||||
saveEntry();
|
||||
clear();
|
||||
emit editFinished(true);
|
||||
}
|
||||
|
||||
void EditEntryWidget::updateEntryData(Entry* entry) const
|
||||
@ -756,26 +781,35 @@ void EditEntryWidget::updateEntryData(Entry* entry) const
|
||||
|
||||
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();
|
||||
|
||||
if (iconStruct.number < 0) {
|
||||
entry->setIcon(Entry::DefaultIconNumber);
|
||||
}
|
||||
else if (iconStruct.uuid.isNull()) {
|
||||
} else if (iconStruct.uuid.isNull()) {
|
||||
entry->setIcon(iconStruct.number);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
entry->setIcon(iconStruct.uuid);
|
||||
}
|
||||
|
||||
entry->setAutoTypeEnabled(m_autoTypeUi->enableButton->isChecked());
|
||||
if (m_autoTypeUi->inheritSequenceButton->isChecked()) {
|
||||
entry->setDefaultAutoTypeSequence(QString());
|
||||
}
|
||||
else {
|
||||
if (AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) {
|
||||
entry->setDefaultAutoTypeSequence(m_autoTypeUi->sequenceEdit->text());
|
||||
}
|
||||
} else if (AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) {
|
||||
entry->setDefaultAutoTypeSequence(m_autoTypeUi->sequenceEdit->text());
|
||||
}
|
||||
|
||||
entry->autoTypeAssociations()->copyDataFrom(m_autoTypeAssoc);
|
||||
@ -1120,3 +1154,38 @@ QMenu* EditEntryWidget::createPresetsMenu()
|
||||
expirePresetsMenu->addAction(tr("1 year"))->setData(QVariant::fromValue(TimeDelta::fromYears(1)));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ signals:
|
||||
|
||||
private slots:
|
||||
void acceptEntry();
|
||||
void saveEntry();
|
||||
bool commitEntry();
|
||||
void cancel();
|
||||
void togglePasswordGeneratorButton(bool checked);
|
||||
void setGeneratedPassword(const QString& password);
|
||||
@ -99,6 +99,7 @@ private slots:
|
||||
void updateHistoryButtons(const QModelIndex& current, const QModelIndex& previous);
|
||||
void useExpiryPreset(QAction* action);
|
||||
void toggleHideNotes(bool visible);
|
||||
void pickColor();
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
void updateSSHAgent();
|
||||
void updateSSHAgentAttachment();
|
||||
@ -120,6 +121,7 @@ private:
|
||||
#endif
|
||||
void setupProperties();
|
||||
void setupHistory();
|
||||
void setupColorButton(bool foreground, const QColor& color);
|
||||
|
||||
bool passwordsEqual();
|
||||
void setForms(const Entry* entry, bool restore = false);
|
||||
|
@ -2,19 +2,15 @@
|
||||
<ui version="4.0">
|
||||
<class>EditEntryWidgetAdvanced</class>
|
||||
<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">
|
||||
<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="QGroupBox" name="attributesBox">
|
||||
<property name="title">
|
||||
@ -157,6 +153,96 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "EntryModel.h"
|
||||
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QMimeData>
|
||||
#include <QPalette>
|
||||
#include <QDateTime>
|
||||
@ -263,10 +264,26 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
|
||||
font.setStrikeOut(true);
|
||||
}
|
||||
return font;
|
||||
} else if (role == Qt::TextColorRole) {
|
||||
} else if (role == Qt::ForegroundRole) {
|
||||
if (entry->hasReferences()) {
|
||||
QPalette p;
|
||||
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
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
Q_UNUSED(orientation);
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case ParentGroup:
|
||||
return tr("Group");
|
||||
@ -302,6 +321,11 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro
|
||||
case Attachments:
|
||||
return tr("Attachments");
|
||||
}
|
||||
} else if (role == Qt::TextAlignmentRole) {
|
||||
switch (section) {
|
||||
case Paperclip:
|
||||
return Qt::AlignCenter;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include "TestEntry.h"
|
||||
#include "TestGlobal.h"
|
||||
#include "crypto/Crypto.h"
|
||||
@ -28,7 +31,7 @@ void TestEntry::initTestCase()
|
||||
|
||||
void TestEntry::testHistoryItemDeletion()
|
||||
{
|
||||
Entry* entry = new Entry();
|
||||
QScopedPointer<Entry> entry(new Entry());
|
||||
QPointer<Entry> historyEntry = new Entry();
|
||||
|
||||
entry->addHistoryItem(historyEntry);
|
||||
@ -39,13 +42,11 @@ void TestEntry::testHistoryItemDeletion()
|
||||
entry->removeHistoryItems(historyEntriesToRemove);
|
||||
QCOMPARE(entry->historyItems().size(), 0);
|
||||
QVERIFY(historyEntry.isNull());
|
||||
|
||||
delete entry;
|
||||
}
|
||||
|
||||
void TestEntry::testCopyDataFrom()
|
||||
{
|
||||
Entry* entry = new Entry();
|
||||
QScopedPointer<Entry> entry(new Entry());
|
||||
|
||||
entry->setTitle("testtitle");
|
||||
entry->attributes()->set("attr1", "abc");
|
||||
@ -62,9 +63,8 @@ void TestEntry::testCopyDataFrom()
|
||||
assoc.sequence = "4";
|
||||
entry->autoTypeAssociations()->add(assoc);
|
||||
|
||||
Entry* entry2 = new Entry();
|
||||
entry2->copyDataFrom(entry);
|
||||
delete entry;
|
||||
QScopedPointer<Entry> entry2(new Entry());
|
||||
entry2->copyDataFrom(entry.data());
|
||||
|
||||
QCOMPARE(entry2->title(), QString("testtitle"));
|
||||
QCOMPARE(entry2->attributes()->value("attr1"), QString("abc"));
|
||||
@ -77,13 +77,11 @@ void TestEntry::testCopyDataFrom()
|
||||
QCOMPARE(entry2->autoTypeAssociations()->size(), 2);
|
||||
QCOMPARE(entry2->autoTypeAssociations()->get(0).window, QString("1"));
|
||||
QCOMPARE(entry2->autoTypeAssociations()->get(1).window, QString("3"));
|
||||
|
||||
delete entry2;
|
||||
}
|
||||
|
||||
void TestEntry::testClone()
|
||||
{
|
||||
Entry* entryOrg = new Entry();
|
||||
QScopedPointer<Entry> entryOrg(new Entry());
|
||||
entryOrg->setUuid(Uuid::random());
|
||||
entryOrg->setTitle("Original Title");
|
||||
entryOrg->beginUpdate();
|
||||
@ -96,42 +94,58 @@ void TestEntry::testClone()
|
||||
entryOrgTime.setCreationTime(dateTime);
|
||||
entryOrg->setTimeInfo(entryOrgTime);
|
||||
|
||||
Entry* entryCloneNone = entryOrg->clone(Entry::CloneNoFlags);
|
||||
QScopedPointer<Entry> entryCloneNone(entryOrg->clone(Entry::CloneNoFlags));
|
||||
QCOMPARE(entryCloneNone->uuid(), entryOrg->uuid());
|
||||
QCOMPARE(entryCloneNone->title(), QString("New Title"));
|
||||
QCOMPARE(entryCloneNone->historyItems().size(), 0);
|
||||
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().isNull());
|
||||
QCOMPARE(entryCloneNewUuid->title(), QString("New Title"));
|
||||
QCOMPARE(entryCloneNewUuid->historyItems().size(), 0);
|
||||
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->title(), QString("New Title"));
|
||||
QCOMPARE(entryCloneResetTime->historyItems().size(), 0);
|
||||
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->title(), QString("New Title"));
|
||||
QCOMPARE(entryCloneHistory->historyItems().size(), 1);
|
||||
QCOMPARE(entryCloneHistory->historyItems().at(0)->title(), QString("Original Title"));
|
||||
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()
|
||||
{
|
||||
Entry* entry = new Entry();
|
||||
QScopedPointer<Entry> entry(new Entry());
|
||||
QString testUrl("www.google.com");
|
||||
QString testCmd("cmd://firefox " + testUrl);
|
||||
QString testComplexCmd("cmd://firefox --start-now --url 'http://" + testUrl + "' --quit");
|
||||
@ -152,8 +166,6 @@ void TestEntry::testResolveUrl()
|
||||
QCOMPARE(entry->resolveUrl(nonHttpUrl), QString(""));
|
||||
// Test no URL
|
||||
QCOMPARE(entry->resolveUrl(noUrl), QString(""));
|
||||
|
||||
delete entry;
|
||||
}
|
||||
|
||||
void TestEntry::testResolveUrlPlaceholders()
|
||||
@ -189,9 +201,9 @@ void TestEntry::testResolveUrlPlaceholders()
|
||||
void TestEntry::testResolveRecursivePlaceholders()
|
||||
{
|
||||
Database db;
|
||||
Group* root = db.rootGroup();
|
||||
auto* root = db.rootGroup();
|
||||
|
||||
Entry* entry1 = new Entry();
|
||||
auto* entry1 = new Entry();
|
||||
entry1->setGroup(root);
|
||||
entry1->setUuid(Uuid::random());
|
||||
entry1->setTitle("{USERNAME}");
|
||||
@ -201,7 +213,7 @@ void TestEntry::testResolveRecursivePlaceholders()
|
||||
entry1->attributes()->set("CustomTitle", "RecursiveValue");
|
||||
QCOMPARE(entry1->resolveMultiplePlaceholders(entry1->title()), QString("RecursiveValue"));
|
||||
|
||||
Entry* entry2 = new Entry();
|
||||
auto* entry2 = new Entry();
|
||||
entry2->setGroup(root);
|
||||
entry2->setUuid(Uuid::random());
|
||||
entry2->setTitle("Entry2Title");
|
||||
@ -213,7 +225,7 @@ void TestEntry::testResolveRecursivePlaceholders()
|
||||
entry2->attributes()->set("Port", "1234");
|
||||
entry2->attributes()->set("Uri", "uri/path");
|
||||
|
||||
Entry* entry3 = new Entry();
|
||||
auto* entry3 = new Entry();
|
||||
entry3->setGroup(root);
|
||||
entry3->setUuid(Uuid::random());
|
||||
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->url()), QString("http://127.0.0.1:1234/uri/path"));
|
||||
|
||||
Entry* entry4 = new Entry();
|
||||
auto* entry4 = new Entry();
|
||||
entry4->setGroup(root);
|
||||
entry4->setUuid(Uuid::random());
|
||||
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->url()), QString("http://127.0.0.1:1234/uri/path"));
|
||||
|
||||
Entry* entry5 = new Entry();
|
||||
auto* entry5 = new Entry();
|
||||
entry5->setGroup(root);
|
||||
entry5->setUuid(Uuid::random());
|
||||
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");
|
||||
QCOMPARE(entry5->resolveMultiplePlaceholders(entry5->url()), url);
|
||||
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()
|
||||
{
|
||||
Database db;
|
||||
Group* root = db.rootGroup();
|
||||
auto* root = db.rootGroup();
|
||||
|
||||
Entry* entry1 = new Entry();
|
||||
auto* entry1 = new Entry();
|
||||
entry1->setGroup(root);
|
||||
entry1->setUuid(Uuid::random());
|
||||
entry1->setTitle("Title1");
|
||||
@ -273,9 +296,9 @@ void TestEntry::testResolveReferencePlaceholders()
|
||||
entry1->setNotes("Notes1");
|
||||
entry1->attributes()->set("CustomAttribute1", "CustomAttributeValue1");
|
||||
|
||||
Group* group = new Group();
|
||||
auto* group = new Group();
|
||||
group->setParent(root);
|
||||
Entry* entry2 = new Entry();
|
||||
auto* entry2 = new Entry();
|
||||
entry2->setGroup(group);
|
||||
entry2->setUuid(Uuid::random());
|
||||
entry2->setTitle("Title2");
|
||||
@ -285,7 +308,7 @@ void TestEntry::testResolveReferencePlaceholders()
|
||||
entry2->setNotes("Notes2");
|
||||
entry2->attributes()->set("CustomAttribute2", "CustomAttributeValue2");
|
||||
|
||||
Entry* entry3 = new Entry();
|
||||
auto* entry3 = new Entry();
|
||||
entry3->setGroup(group);
|
||||
entry3->setUuid(Uuid::random());
|
||||
entry3->setTitle("{S:AttributeTitle}");
|
||||
@ -299,7 +322,7 @@ void TestEntry::testResolveReferencePlaceholders()
|
||||
entry3->attributes()->set("AttributeUrl", "UrlValue");
|
||||
entry3->attributes()->set("AttributeNotes", "NotesValue");
|
||||
|
||||
Entry* tstEntry = new Entry();
|
||||
auto* tstEntry = new Entry();
|
||||
tstEntry->setGroup(root);
|
||||
tstEntry->setUuid(Uuid::random());
|
||||
|
||||
@ -356,67 +379,67 @@ void TestEntry::testResolveNonIdPlaceholdersToUuid()
|
||||
Database db;
|
||||
auto* root = db.rootGroup();
|
||||
|
||||
Entry referencedEntryTitle;
|
||||
referencedEntryTitle.setGroup(root);
|
||||
referencedEntryTitle.setTitle("myTitle");
|
||||
referencedEntryTitle.setUuid(Uuid::random());
|
||||
auto* referencedEntryTitle = new Entry();
|
||||
referencedEntryTitle->setGroup(root);
|
||||
referencedEntryTitle->setTitle("myTitle");
|
||||
referencedEntryTitle->setUuid(Uuid::random());
|
||||
|
||||
Entry referencedEntryUsername;
|
||||
referencedEntryUsername.setGroup(root);
|
||||
referencedEntryUsername.setUsername("myUser");
|
||||
referencedEntryUsername.setUuid(Uuid::random());
|
||||
auto* referencedEntryUsername = new Entry();
|
||||
referencedEntryUsername->setGroup(root);
|
||||
referencedEntryUsername->setUsername("myUser");
|
||||
referencedEntryUsername->setUuid(Uuid::random());
|
||||
|
||||
Entry referencedEntryPassword;
|
||||
referencedEntryPassword.setGroup(root);
|
||||
referencedEntryPassword.setPassword("myPassword");
|
||||
referencedEntryPassword.setUuid(Uuid::random());
|
||||
auto* referencedEntryPassword = new Entry();
|
||||
referencedEntryPassword->setGroup(root);
|
||||
referencedEntryPassword->setPassword("myPassword");
|
||||
referencedEntryPassword->setUuid(Uuid::random());
|
||||
|
||||
Entry referencedEntryUrl;
|
||||
referencedEntryUrl.setGroup(root);
|
||||
referencedEntryUrl.setUrl("myUrl");
|
||||
referencedEntryUrl.setUuid(Uuid::random());
|
||||
auto* referencedEntryUrl = new Entry();
|
||||
referencedEntryUrl->setGroup(root);
|
||||
referencedEntryUrl->setUrl("myUrl");
|
||||
referencedEntryUrl->setUuid(Uuid::random());
|
||||
|
||||
Entry referencedEntryNotes;
|
||||
referencedEntryNotes.setGroup(root);
|
||||
referencedEntryNotes.setNotes("myNotes");
|
||||
referencedEntryNotes.setUuid(Uuid::random());
|
||||
auto* referencedEntryNotes = new Entry();
|
||||
referencedEntryNotes->setGroup(root);
|
||||
referencedEntryNotes->setNotes("myNotes");
|
||||
referencedEntryNotes->setUuid(Uuid::random());
|
||||
|
||||
const QList<QChar> placeholders{'T', 'U', 'P', 'A', 'N'};
|
||||
for (const QChar searchIn : placeholders) {
|
||||
for (const QChar& searchIn : placeholders) {
|
||||
const Entry* referencedEntry = nullptr;
|
||||
QString newEntryNotesRaw("{REF:I@%1:%2}");
|
||||
|
||||
switch(searchIn.toLatin1()) {
|
||||
case 'T':
|
||||
referencedEntry = &referencedEntryTitle;
|
||||
referencedEntry = referencedEntryTitle;
|
||||
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->title());
|
||||
break;
|
||||
case 'U':
|
||||
referencedEntry = &referencedEntryUsername;
|
||||
referencedEntry = referencedEntryUsername;
|
||||
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->username());
|
||||
break;
|
||||
case 'P':
|
||||
referencedEntry = &referencedEntryPassword;
|
||||
referencedEntry = referencedEntryPassword;
|
||||
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->password());
|
||||
break;
|
||||
case 'A':
|
||||
referencedEntry = &referencedEntryUrl;
|
||||
referencedEntry = referencedEntryUrl;
|
||||
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->url());
|
||||
break;
|
||||
case 'N':
|
||||
referencedEntry = &referencedEntryNotes;
|
||||
referencedEntry = referencedEntryNotes;
|
||||
newEntryNotesRaw = newEntryNotesRaw.arg(searchIn, referencedEntry->notes());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Entry newEntry;
|
||||
newEntry.setGroup(root);
|
||||
newEntry.setNotes(newEntryNotesRaw);
|
||||
auto* newEntry = new Entry();
|
||||
newEntry->setGroup(root);
|
||||
newEntry->setNotes(newEntryNotesRaw);
|
||||
|
||||
const auto newEntryNotesResolved =
|
||||
newEntry.resolveMultiplePlaceholders(newEntry.notes());
|
||||
const QString newEntryNotesResolved =
|
||||
newEntry->resolveMultiplePlaceholders(newEntry->notes());
|
||||
QCOMPARE(newEntryNotesResolved, QString(referencedEntry->uuid().toHex()));
|
||||
}
|
||||
}
|
||||
@ -424,9 +447,9 @@ void TestEntry::testResolveNonIdPlaceholdersToUuid()
|
||||
void TestEntry::testResolveClonedEntry()
|
||||
{
|
||||
Database db;
|
||||
Group* root = db.rootGroup();
|
||||
auto* root = db.rootGroup();
|
||||
|
||||
Entry* original = new Entry();
|
||||
auto* original = new Entry();
|
||||
original->setGroup(root);
|
||||
original->setUuid(Uuid::random());
|
||||
original->setTitle("Title");
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <QLabel>
|
||||
#include <QMimeData>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QSpinBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QComboBox>
|
||||
@ -61,6 +62,7 @@
|
||||
#include "gui/entry/EntryView.h"
|
||||
#include "gui/group/GroupModel.h"
|
||||
#include "gui/group/GroupView.h"
|
||||
#include "gui/group/EditGroupWidget.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
void TestGui::initTestCase()
|
||||
@ -278,6 +280,7 @@ void TestGui::testTabs()
|
||||
void TestGui::testEditEntry()
|
||||
{
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
int editCount = 0;
|
||||
|
||||
// Select the first entry in the database
|
||||
EntryView* entryView = m_dbWidget->findChild<EntryView*>("entryView");
|
||||
@ -304,7 +307,24 @@ void TestGui::testEditEntry()
|
||||
QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Apply), Qt::LeftButton);
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode);
|
||||
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
|
||||
editEntryWidget->setCurrentPage(1);
|
||||
@ -336,12 +356,68 @@ void TestGui::testEditEntry()
|
||||
// Confirm edit was made
|
||||
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
|
||||
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
|
||||
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()
|
||||
{
|
||||
QToolBar* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
|
||||
|
@ -46,6 +46,7 @@ private slots:
|
||||
void testAutoreloadDatabase();
|
||||
void testTabs();
|
||||
void testEditEntry();
|
||||
void testSearchEditEntry();
|
||||
void testAddEntry();
|
||||
void testPasswordEntryEntropy();
|
||||
void testDicewareEntryEntropy();
|
||||
|
Loading…
x
Reference in New Issue
Block a user