Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan White <support@dmapps.us>2021-06-08 06:18:37 +0300
committerJonathan White <support@dmapps.us>2021-06-08 06:18:37 +0300
commit2b262c5b2438d8ef8febcde5ede2043a4e00c192 (patch)
treebf7c1413a9033178e8dfc0bf3c452cc79f5b461e /src
parent34a78f0ec3a3c030218c71a57c6fa56e6cb0bc44 (diff)
parentec33474845942e5210d734b9e9e4c6019860aa79 (diff)
Release 2.6.52.6.5
Added - Show search bar when toolbar is hidden or in overflow [#6279] - Show countdown for clipboard clearing in status bar [#6333] - Command line option to lock all open databases [#6511] - Allow CSV import of bare TOTP secrets [#6211] - Retain file creation time when saving database [#6576] - Set permissions of saved attachments to be private to the current user [#6363] - OPVault: Use Text instead of Name for attribute names [#6334] Changed - Reports: Allow resizing of reports columns [#6435] - Reports: Toggle showing expired entries [#6534] - Save Always on Top setting [#6236] - Password generator can exclude additional lookalike characters (6/G, 8/B) [#6196] Fixed - Allow setting MSI properties in unattended install [#6196] - Update MainWindow minimum size to enable smaller verticle space [#6196] - Use application font size when setting default or monospace fonts [#6332] - Fix notes not clearing in entry preview panel in some cases [#6481] - macOS: Correct window activation when restoring from tray [#6575] - macOS: Better handling of minimize after unlock when using browser integration [#6338] - Linux: Start after the system tray is available on LXQt [#6216] - Linux: Allow selection of modal dialogs on X11 in Auto-Type [#6204] - KeeShare: prevent crash when file extension is missing [#6174]
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/autotype/AutoTypeSelectDialog.cpp12
-rw-r--r--src/autotype/xcb/AutoTypeXCB.cpp22
-rw-r--r--src/autotype/xcb/AutoTypeXCB.h3
-rw-r--r--src/browser/BrowserService.cpp6
-rw-r--r--src/browser/BrowserService.h1
-rw-r--r--src/core/Config.cpp1
-rw-r--r--src/core/Config.h1
-rw-r--r--src/core/Database.cpp19
-rw-r--r--src/core/MacPasteboard.h2
-rw-r--r--src/core/PasswordGenerator.cpp4
-rw-r--r--src/format/OpVaultReader.h2
-rw-r--r--src/format/OpVaultReaderSections.cpp21
-rw-r--r--src/gui/AboutDialog.cpp6
-rw-r--r--src/gui/Application.cpp70
-rw-r--r--src/gui/Application.h1
-rw-r--r--src/gui/Clipboard.cpp30
-rw-r--r--src/gui/Clipboard.h8
-rw-r--r--src/gui/DatabaseWidget.cpp7
-rw-r--r--src/gui/EditWidget.cpp2
-rw-r--r--src/gui/EntryPreviewWidget.cpp16
-rw-r--r--src/gui/Font.cpp14
-rw-r--r--src/gui/MainWindow.cpp58
-rw-r--r--src/gui/MainWindow.h8
-rw-r--r--src/gui/MainWindow.ui9
-rw-r--r--src/gui/PasswordGeneratorWidget.ui2
-rw-r--r--src/gui/SearchWidget.cpp21
-rw-r--r--src/gui/SearchWidget.h5
-rw-r--r--src/gui/csvImport/CsvImportWidget.cpp9
-rw-r--r--src/gui/databasekey/KeyFileEditWidget.cpp10
-rw-r--r--src/gui/databasekey/KeyFileEditWidget.ui71
-rw-r--r--src/gui/entry/AutoTypeMatchView.cpp14
-rw-r--r--src/gui/entry/EditEntryWidget.cpp11
-rw-r--r--src/gui/entry/EditEntryWidgetBrowser.ui10
-rw-r--r--src/gui/entry/EntryAttachmentsWidget.cpp3
-rw-r--r--src/gui/osutils/macutils/AppKitImpl.mm5
-rw-r--r--src/gui/osutils/macutils/MacUtils.cpp1
-rw-r--r--src/gui/osutils/nixutils/NixUtils.cpp3
-rw-r--r--src/gui/reports/ReportsWidgetHealthcheck.cpp7
-rw-r--r--src/gui/reports/ReportsWidgetHibp.cpp5
-rw-r--r--src/gui/styles/base/basestyle.qss4
-rw-r--r--src/gui/styles/dark/DarkStyle.cpp3
-rw-r--r--src/gui/styles/light/LightStyle.cpp3
-rw-r--r--src/gui/widgets/KPToolBar.cpp75
-rw-r--r--src/gui/widgets/KPToolBar.h52
-rw-r--r--src/keeshare/CMakeLists.txt2
-rw-r--r--src/keeshare/ShareExport.cpp6
-rw-r--r--src/keeshare/ShareImport.cpp5
-rw-r--r--src/main.cpp19
49 files changed, 518 insertions, 152 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0ceccdd9a..a441b475b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -173,6 +173,7 @@ set(keepassx_SOURCES
gui/osutils/OSUtilsBase.cpp
gui/settings/SettingsWidget.cpp
gui/widgets/ElidedLabel.cpp
+ gui/widgets/KPToolBar.cpp
gui/widgets/PopupHelpWidget.cpp
gui/wizard/NewDatabaseWizard.cpp
gui/wizard/NewDatabaseWizardPage.cpp
diff --git a/src/autotype/AutoTypeSelectDialog.cpp b/src/autotype/AutoTypeSelectDialog.cpp
index 3b264b7bc..af11fb8f8 100644
--- a/src/autotype/AutoTypeSelectDialog.cpp
+++ b/src/autotype/AutoTypeSelectDialog.cpp
@@ -52,18 +52,24 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
setWindowIcon(resources()->applicationIcon());
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
- QRect screenGeometry = QApplication::screenAt(QCursor::pos())->availableGeometry();
+ auto screen = QApplication::screenAt(QCursor::pos());
+ if (!screen) {
+ // screenAt can return a nullptr, default to the primary screen
+ screen = QApplication::primaryScreen();
+ }
+ QRect screenGeometry = screen->availableGeometry();
#else
QRect screenGeometry = QApplication::desktop()->availableGeometry(QCursor::pos());
#endif
+
+ // Resize to last used size
QSize size = config()->get(Config::GUI_AutoTypeSelectDialogSize).toSize();
size.setWidth(qMin(size.width(), screenGeometry.width()));
size.setHeight(qMin(size.height(), screenGeometry.height()));
resize(size);
// move dialog to the center of the screen
- QPoint screenCenter = screenGeometry.center();
- move(screenCenter.x() - (size.width() / 2), screenCenter.y() - (size.height() / 2));
+ move(screenGeometry.center().x() - (size.width() / 2), screenGeometry.center().y() - (size.height() / 2));
QVBoxLayout* layout = new QVBoxLayout(this);
diff --git a/src/autotype/xcb/AutoTypeXCB.cpp b/src/autotype/xcb/AutoTypeXCB.cpp
index d2d757b4e..2f320f73b 100644
--- a/src/autotype/xcb/AutoTypeXCB.cpp
+++ b/src/autotype/xcb/AutoTypeXCB.cpp
@@ -39,6 +39,8 @@ AutoTypePlatformX11::AutoTypePlatformX11()
m_atomString = XInternAtom(m_dpy, "STRING", True);
m_atomUtf8String = XInternAtom(m_dpy, "UTF8_STRING", True);
m_atomNetActiveWindow = XInternAtom(m_dpy, "_NET_ACTIVE_WINDOW", True);
+ m_atomTransientFor = XInternAtom(m_dpy, "WM_TRANSIENT_FOR", True);
+ m_atomWindow = XInternAtom(m_dpy, "WINDOW", True);
m_classBlacklist << "desktop_window"
<< "gnome-panel"; // Gnome
@@ -373,23 +375,31 @@ QStringList AutoTypePlatformX11::windowTitlesRecursive(Window window)
bool AutoTypePlatformX11::isTopLevelWindow(Window window)
{
+ bool result = false;
+
Atom type = None;
int format;
unsigned long nitems;
unsigned long after;
- unsigned char* data = Q_NULLPTR;
+ unsigned char* data = nullptr;
+
+ // Check if the window has WM_STATE atom and it is not Withdrawn
int retVal = XGetWindowProperty(
m_dpy, window, m_atomWmState, 0, 2, False, m_atomWmState, &type, &format, &nitems, &after, &data);
- bool result = false;
-
if (retVal == 0 && data) {
if (type == m_atomWmState && format == 32 && nitems > 0) {
- qint32 state = static_cast<qint32>(*data);
- result = (state != WithdrawnState);
+ result = (static_cast<quint32>(*data) != WithdrawnState);
}
-
XFree(data);
+ } else {
+ // See if this is a transient window without WM_STATE
+ retVal = XGetWindowProperty(
+ m_dpy, window, m_atomTransientFor, 0, 1, False, m_atomWindow, &type, &format, &nitems, &after, &data);
+ if (retVal == 0 && data) {
+ result = true;
+ XFree(data);
+ }
}
return result;
diff --git a/src/autotype/xcb/AutoTypeXCB.h b/src/autotype/xcb/AutoTypeXCB.h
index 221d2ba7b..327359d3c 100644
--- a/src/autotype/xcb/AutoTypeXCB.h
+++ b/src/autotype/xcb/AutoTypeXCB.h
@@ -77,7 +77,6 @@ private:
void updateKeymap();
bool isRemapKeycodeValid();
int AddKeysym(KeySym keysym);
- void AddModifier(KeySym keysym);
void SendKeyEvent(unsigned keycode, bool press);
void SendModifiers(unsigned int mask, bool press);
int GetKeycode(KeySym keysym, unsigned int* mask);
@@ -93,6 +92,8 @@ private:
Atom m_atomString;
Atom m_atomUtf8String;
Atom m_atomNetActiveWindow;
+ Atom m_atomTransientFor;
+ Atom m_atomWindow;
QSet<QString> m_classBlacklist;
Qt::Key m_currentGlobalKey;
Qt::KeyboardModifiers m_currentGlobalModifiers;
diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp
index 5e1173df4..703ef9612 100644
--- a/src/browser/BrowserService.cpp
+++ b/src/browser/BrowserService.cpp
@@ -55,6 +55,7 @@ static const QString KEEPASSHTTP_GROUP_NAME = QStringLiteral("KeePassHttp Passwo
const QString BrowserService::OPTION_SKIP_AUTO_SUBMIT = QStringLiteral("BrowserSkipAutoSubmit");
const QString BrowserService::OPTION_HIDE_ENTRY = QStringLiteral("BrowserHideEntry");
const QString BrowserService::OPTION_ONLY_HTTP_AUTH = QStringLiteral("BrowserOnlyHttpAuth");
+const QString BrowserService::OPTION_NOT_HTTP_AUTH = QStringLiteral("BrowserNotHttpAuth");
// Multiple URL's
const QString BrowserService::ADDITIONAL_URL = QStringLiteral("KP2A_URL");
@@ -397,6 +398,11 @@ QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
continue;
}
+ if (httpAuth && entry->customData()->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
+ && entry->customData()->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR) {
+ continue;
+ }
+
// HTTP Basic Auth always needs a confirmation
if (!ignoreHttpAuth && httpAuth) {
pwEntriesToConfirm.append(entry);
diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h
index f84bf2880..d45556a73 100644
--- a/src/browser/BrowserService.h
+++ b/src/browser/BrowserService.h
@@ -90,6 +90,7 @@ public:
static const QString OPTION_SKIP_AUTO_SUBMIT;
static const QString OPTION_HIDE_ENTRY;
static const QString OPTION_ONLY_HTTP_AUTH;
+ static const QString OPTION_NOT_HTTP_AUTH;
static const QString ADDITIONAL_URL;
signals:
diff --git a/src/core/Config.cpp b/src/core/Config.cpp
index 4c00dabbc..9c69ea578 100644
--- a/src/core/Config.cpp
+++ b/src/core/Config.cpp
@@ -92,6 +92,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::GUI_HideToolbar, {QS("GUI/HideToolbar"), Roaming, false}},
{Config::GUI_MovableToolbar, {QS("GUI/MovableToolbar"), Roaming, false}},
{Config::GUI_HidePreviewPanel, {QS("GUI/HidePreviewPanel"), Roaming, false}},
+ {Config::GUI_AlwaysOnTop, {QS("GUI/GUI_AlwaysOnTop"), Local, false}},
{Config::GUI_ToolButtonStyle, {QS("GUI/ToolButtonStyle"), Roaming, Qt::ToolButtonIconOnly}},
{Config::GUI_ShowTrayIcon, {QS("GUI/ShowTrayIcon"), Roaming, false}},
{Config::GUI_TrayIconAppearance, {QS("GUI/TrayIconAppearance"), Roaming, {}}},
diff --git a/src/core/Config.h b/src/core/Config.h
index 423e1ee81..b6a31bae7 100644
--- a/src/core/Config.h
+++ b/src/core/Config.h
@@ -74,6 +74,7 @@ public:
GUI_HideToolbar,
GUI_MovableToolbar,
GUI_HidePreviewPanel,
+ GUI_AlwaysOnTop,
GUI_ToolButtonStyle,
GUI_ShowTrayIcon,
GUI_TrayIconAppearance,
diff --git a/src/core/Database.cpp b/src/core/Database.cpp
index ccd0ffb21..f13c00796 100644
--- a/src/core/Database.cpp
+++ b/src/core/Database.cpp
@@ -272,6 +272,11 @@ bool Database::saveAs(const QString& filePath, QString* error, bool atomic, bool
bool Database::performSave(const QString& filePath, QString* error, bool atomic, bool backup)
{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+ QFileInfo info(filePath);
+ auto createTime = info.exists() ? info.birthTime() : QDateTime::currentDateTime();
+#endif
+
if (atomic) {
QSaveFile saveFile(filePath);
if (saveFile.open(QIODevice::WriteOnly)) {
@@ -284,6 +289,11 @@ bool Database::performSave(const QString& filePath, QString* error, bool atomic,
backupDatabase(filePath);
}
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+ // Retain orginal creation time
+ saveFile.setFileTime(createTime, QFile::FileBirthTime);
+#endif
+
if (saveFile.commit()) {
// successfully saved database file
return true;
@@ -318,6 +328,10 @@ bool Database::performSave(const QString& filePath, QString* error, bool atomic,
// successfully saved the database
tempFile.setAutoRemove(false);
QFile::setPermissions(filePath, perms);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+ // Retain orginal creation time
+ tempFile.setFileTime(createTime, QFile::FileBirthTime);
+#endif
return true;
} else if (!backup || !restoreDatabase(filePath)) {
// Failed to copy new database in place, and
@@ -486,8 +500,9 @@ bool Database::restoreDatabase(const QString& filePath)
// Only try to restore if the backup file actually exists
if (QFile::exists(backupFilePath)) {
QFile::remove(filePath);
- return QFile::copy(backupFilePath, filePath);
- QFile::setPermissions(filePath, perms);
+ if (QFile::copy(backupFilePath, filePath)) {
+ return QFile::setPermissions(filePath, perms);
+ }
}
return false;
}
diff --git a/src/core/MacPasteboard.h b/src/core/MacPasteboard.h
index f2a71e73f..503741ca8 100644
--- a/src/core/MacPasteboard.h
+++ b/src/core/MacPasteboard.h
@@ -18,9 +18,9 @@
#ifndef KEEPASSXC_MACPASTEBOARD_H
#define KEEPASSXC_MACPASTEBOARD_H
-#include <QMacPasteboardMime>
#include <QObject>
#include <QTextCodec>
+#include <QtMacExtras/QMacPasteboardMime>
class MacPasteboard : public QObject, public QMacPasteboardMime
{
diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp
index efe647880..bd9dcc67b 100644
--- a/src/core/PasswordGenerator.cpp
+++ b/src/core/PasswordGenerator.cpp
@@ -150,7 +150,7 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups() const
for (int i = 65; i <= (65 + 25); i++) {
- if ((m_flags & ExcludeLookAlike) && (i == 73 || i == 79)) { // "I" and "O"
+ if ((m_flags & ExcludeLookAlike) && (i == 66 || i == 71 || i == 73 || i == 79)) { //"B", "G", "I" and "O"
continue;
}
@@ -163,7 +163,7 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups() const
PasswordGroup group;
for (int i = 48; i < (48 + 10); i++) {
- if ((m_flags & ExcludeLookAlike) && (i == 48 || i == 49)) { // "0" and "1"
+ if ((m_flags & ExcludeLookAlike) && (i == 48 || i == 49 || i == 54 || i == 56)) { // "0", "1", "6", and "8"
continue;
}
diff --git a/src/format/OpVaultReader.h b/src/format/OpVaultReader.h
index 585415854..846e65dd3 100644
--- a/src/format/OpVaultReader.h
+++ b/src/format/OpVaultReader.h
@@ -98,7 +98,7 @@ private:
bool fillAttributes(Entry* entry, const QJsonObject& bandEntry);
void fillFromSection(Entry* entry, const QJsonObject& section);
- void fillFromSectionField(Entry* entry, const QString& sectionName, QJsonObject& field);
+ void fillFromSectionField(Entry* entry, const QString& sectionName, const QJsonObject& field);
QString resolveAttributeName(const QString& section, const QString& name, const QString& text);
void populateCategoryGroups(Group* rootGroup);
diff --git a/src/format/OpVaultReaderSections.cpp b/src/format/OpVaultReaderSections.cpp
index e59dd9f02..b7677ec05 100644
--- a/src/format/OpVaultReaderSections.cpp
+++ b/src/format/OpVaultReaderSections.cpp
@@ -53,12 +53,11 @@ namespace
void OpVaultReader::fillFromSection(Entry* entry, const QJsonObject& section)
{
const auto uuid = entry->uuid();
- QString sectionName = section["name"].toString();
+ auto sectionTitle = section["title"].toString();
if (!section.contains("fields")) {
- auto sectionNameLC = sectionName.toLower();
- auto sectionTitleLC = section["title"].toString("").toLower();
- if (!(sectionNameLC == "linked items" && sectionTitleLC == "related items")) {
+ auto sectionName = section["name"].toString();
+ if (!(sectionName.toLower() == "linked items" && sectionTitle.toLower() == "related items")) {
qWarning() << R"(Skipping "fields"-less Section in UUID ")" << uuid << "\": <<" << section << ">>";
}
return;
@@ -67,23 +66,17 @@ void OpVaultReader::fillFromSection(Entry* entry, const QJsonObject& section)
return;
}
- // If we have a default section name then replace with the section title if not empty
- if (sectionName.startsWith("Section_") && !section["title"].toString().isEmpty()) {
- sectionName = section["title"].toString();
- }
-
QJsonArray sectionFields = section["fields"].toArray();
for (const QJsonValue sectionField : sectionFields) {
if (!sectionField.isObject()) {
qWarning() << R"(Skipping non-Object "fields" in UUID ")" << uuid << "\": << " << sectionField << ">>";
continue;
}
- QJsonObject field = sectionField.toObject();
- fillFromSectionField(entry, sectionName, field);
+ fillFromSectionField(entry, sectionTitle, sectionField.toObject());
}
}
-void OpVaultReader::fillFromSectionField(Entry* entry, const QString& sectionName, QJsonObject& field)
+void OpVaultReader::fillFromSectionField(Entry* entry, const QString& sectionName, const QJsonObject& field)
{
if (!field.contains("v")) {
// for our purposes, we don't care if there isn't a value in the field
@@ -161,8 +154,8 @@ QString OpVaultReader::resolveAttributeName(const QString& section, const QStrin
|| lowName == "website") {
return EntryAttributes::URLKey;
}
- return name;
+ return text;
}
- return QString("%1_%2").arg(section, name);
+ return QString("%1_%2").arg(section, text);
}
diff --git a/src/gui/AboutDialog.cpp b/src/gui/AboutDialog.cpp
index 7c89b8804..93bb4f1eb 100644
--- a/src/gui/AboutDialog.cpp
+++ b/src/gui/AboutDialog.cpp
@@ -60,6 +60,10 @@ static const QString aboutContributors = R"(
<li>Micha Ober</li>
<li>PublicByte</li>
<li>Clayton Casciato</li>
+ <li>Dominik</li>
+ <li>Paul Ammann</li>
+ <li>Steve Isom</li>
+ <li>Matt Cardarelli</li>
</ul>
<h3>Notable Code Contributions:</h3>
<ul>
@@ -118,6 +122,8 @@ static const QString aboutContributors = R"(
<li>Larry Siden</li>
<li>Thammachart Chinvarapon</li>
<li>Patrick Evans</li>
+ <li>Johannes Erchen</li>
+ <li>Ralph Azucena</li>
</ul>
<h3>Translations:</h3>
<ul>
diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp
index 784528294..973e29032 100644
--- a/src/gui/Application.cpp
+++ b/src/gui/Application.cpp
@@ -286,13 +286,26 @@ void Application::socketReadyRead()
}
QStringList fileNames;
- in >> fileNames;
- for (const QString& fileName : asConst(fileNames)) {
- const QFileInfo fInfo(fileName);
- if (fInfo.isFile() && fInfo.suffix().toLower() == "kdbx") {
- emit openFile(fileName);
+ quint32 id;
+ in >> id;
+
+ // TODO: move constants to enum
+ switch (id) {
+ case 1:
+ in >> fileNames;
+ for (const QString& fileName : asConst(fileNames)) {
+ const QFileInfo fInfo(fileName);
+ if (fInfo.isFile() && fInfo.suffix().toLower() == "kdbx") {
+ emit openFile(fileName);
+ }
}
+
+ break;
+ case 2:
+ getMainWindow()->lockAllDatabases();
+ break;
}
+
socket->deleteLater();
}
@@ -305,6 +318,12 @@ bool Application::isAlreadyRunning() const
return config()->get(Config::SingleInstance).toBool() && m_alreadyRunning;
}
+/**
+ * Send to-open file names to the running UI instance
+ *
+ * @param fileNames - list of file names to open
+ * @return true if all operations succeeded (connection made, data sent, connection closed)
+ */
bool Application::sendFileNamesToRunningInstance(const QStringList& fileNames)
{
QLocalSocket client;
@@ -317,13 +336,48 @@ bool Application::sendFileNamesToRunningInstance(const QStringList& fileNames)
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_0);
- out << quint32(0) << fileNames;
+ out << quint32(0); // reserve space for block size
+ out << quint32(1); // ID for file name send. TODO: move to enum
+ out << fileNames; // send file names to be opened
+ out.device()->seek(0);
+ out << quint32(data.size() - sizeof(quint32)); // replace the previous constant 0 with block size
+
+ const bool writeOk = client.write(data) != -1 && client.waitForBytesWritten(WaitTimeoutMSec);
+ client.disconnectFromServer();
+ const bool disconnected =
+ client.state() == QLocalSocket::UnconnectedState || client.waitForDisconnected(WaitTimeoutMSec);
+ return writeOk && disconnected;
+}
+
+/**
+ * Locks all open databases in the running instance
+ *
+ * @return true if the "please lock" signal was sent successfully
+ */
+bool Application::sendLockToInstance()
+{
+ // Make a connection to avoid SIGSEGV
+ QLocalSocket client;
+ client.connectToServer(m_socketName);
+ const bool connected = client.waitForConnected(WaitTimeoutMSec);
+ if (!connected) {
+ return false;
+ }
+
+ // Send lock signal
+ QByteArray data;
+ QDataStream out(&data, QIODevice::WriteOnly);
+ out.setVersion(QDataStream::Qt_5_0);
+ out << quint32(0); // reserve space for block size
+ out << quint32(2); // ID for database lock. TODO: move to enum
out.device()->seek(0);
- out << quint32(data.size() - sizeof(quint32));
+ out << quint32(data.size() - sizeof(quint32)); // replace the previous constant 0 with block size
+ // Finish gracefully
const bool writeOk = client.write(data) != -1 && client.waitForBytesWritten(WaitTimeoutMSec);
client.disconnectFromServer();
- const bool disconnected = client.waitForDisconnected(WaitTimeoutMSec);
+ const bool disconnected =
+ client.state() == QLocalSocket::UnconnectedState || client.waitForConnected(WaitTimeoutMSec);
return writeOk && disconnected;
}
diff --git a/src/gui/Application.h b/src/gui/Application.h
index 9f694f8c3..f6c35a037 100644
--- a/src/gui/Application.h
+++ b/src/gui/Application.h
@@ -48,6 +48,7 @@ public:
bool isDarkTheme() const;
bool sendFileNamesToRunningInstance(const QStringList& fileNames);
+ bool sendLockToInstance();
void restart();
diff --git a/src/gui/Clipboard.cpp b/src/gui/Clipboard.cpp
index ab4a84cf0..804a53143 100644
--- a/src/gui/Clipboard.cpp
+++ b/src/gui/Clipboard.cpp
@@ -39,8 +39,7 @@ Clipboard::Clipboard(QObject* parent)
m_pasteboard = new MacPasteboard();
}
#endif
- m_timer->setSingleShot(true);
- connect(m_timer, SIGNAL(timeout()), SLOT(clearClipboard()));
+ connect(m_timer, SIGNAL(timeout()), SLOT(countdownTick()));
connect(qApp, SIGNAL(aboutToQuit()), SLOT(clearCopiedText()));
}
@@ -77,7 +76,9 @@ void Clipboard::setText(const QString& text, bool clear)
if (config()->get(Config::Security_ClearClipboard).toBool()) {
int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt();
if (timeout > 0) {
- m_timer->start(timeout * 1000);
+ m_secondsElapsed = -1;
+ countdownTick();
+ m_timer->start(1000);
}
}
}
@@ -85,15 +86,9 @@ void Clipboard::setText(const QString& text, bool clear)
void Clipboard::clearCopiedText()
{
- if (m_timer->isActive()) {
- m_timer->stop();
- }
-
- clearClipboard();
-}
+ m_timer->stop();
+ emit updateCountdown(-1, "");
-void Clipboard::clearClipboard()
-{
auto* clipboard = QApplication::clipboard();
if (!clipboard) {
qWarning("Unable to access the clipboard.");
@@ -108,6 +103,19 @@ void Clipboard::clearClipboard()
m_lastCopied.clear();
}
+void Clipboard::countdownTick()
+{
+ m_secondsElapsed++;
+ int timeout = config()->get(Config::Security_ClearClipboardTimeout).toInt();
+ int timeLeft = timeout - m_secondsElapsed;
+ if (timeLeft <= 0) {
+ clearCopiedText();
+ } else {
+ emit updateCountdown(100 * timeLeft / timeout,
+ QObject::tr("Clearing the clipboard in %1 second(s)…", "", timeLeft).arg(timeLeft));
+ }
+}
+
Clipboard* Clipboard::instance()
{
if (!m_instance) {
diff --git a/src/gui/Clipboard.h b/src/gui/Clipboard.h
index 7465f30a5..c97d91881 100644
--- a/src/gui/Clipboard.h
+++ b/src/gui/Clipboard.h
@@ -19,6 +19,7 @@
#ifndef KEEPASSX_CLIPBOARD_H
#define KEEPASSX_CLIPBOARD_H
+#include <QElapsedTimer>
#include <QObject>
#ifdef Q_OS_MACOS
#include "core/MacPasteboard.h"
@@ -39,8 +40,11 @@ public:
public slots:
void clearCopiedText();
+signals:
+ void updateCountdown(int percentage, QString message);
+
private slots:
- void clearClipboard();
+ void countdownTick();
private:
explicit Clipboard(QObject* parent = nullptr);
@@ -48,6 +52,8 @@ private:
static Clipboard* m_instance;
QTimer* m_timer;
+ int m_secondsElapsed = 0;
+
#ifdef Q_OS_MACOS
// This object lives for the whole program lifetime and we cannot delete it on exit,
// so ignore leak warnings. See https://bugreports.qt.io/browse/QTBUG-54832
diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp
index bb0a6ec41..0f0037ced 100644
--- a/src/gui/DatabaseWidget.cpp
+++ b/src/gui/DatabaseWidget.cpp
@@ -1176,6 +1176,10 @@ void DatabaseWidget::unlockDatabase(bool accepted)
sshAgent()->databaseUnlocked(m_db);
#endif
+ if (config()->get(Config::MinimizeAfterUnlock).toBool()) {
+ getMainWindow()->minimizeOrHide();
+ }
+
if (senderDialog && senderDialog->intent() == DatabaseOpenDialog::Intent::AutoType) {
QList<QSharedPointer<Database>> dbList;
dbList.append(m_db);
@@ -1391,9 +1395,8 @@ void DatabaseWidget::onGroupChanged()
// Intercept group changes if in search mode
if (isSearchActive() && m_searchLimitGroup) {
search(m_lastSearchText);
- } else if (isSearchActive()) {
- endSearch();
} else {
+ endSearch();
m_entryView->displayGroup(group);
}
diff --git a/src/gui/EditWidget.cpp b/src/gui/EditWidget.cpp
index fbd07a82e..c916582d2 100644
--- a/src/gui/EditWidget.cpp
+++ b/src/gui/EditWidget.cpp
@@ -102,7 +102,7 @@ void EditWidget::setPageHidden(QWidget* widget, bool hidden)
m_ui->categoryList->setCategoryHidden(index, hidden);
}
- if (index == m_ui->stackedWidget->currentIndex()) {
+ if (hidden && index == m_ui->stackedWidget->currentIndex()) {
int newIndex = m_ui->stackedWidget->currentIndex() - 1;
if (newIndex < 0) {
newIndex = m_ui->stackedWidget->count() - 1;
diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp
index e240f7ae4..141f25d4c 100644
--- a/src/gui/EntryPreviewWidget.cpp
+++ b/src/gui/EntryPreviewWidget.cpp
@@ -245,14 +245,14 @@ void EntryPreviewWidget::updateEntryGeneralTab()
m_ui->togglePasswordButton->setVisible(false);
}
- if (config()->get(Config::Security_HideNotes).toBool()) {
- setEntryNotesVisible(false);
- m_ui->toggleEntryNotesButton->setVisible(!m_ui->entryNotesTextEdit->toPlainText().isEmpty());
- m_ui->toggleEntryNotesButton->setChecked(false);
- } else {
- setEntryNotesVisible(true);
- m_ui->toggleEntryNotesButton->setVisible(false);
- }
+ auto hasNotes = !m_currentEntry->notes().isEmpty();
+ auto hideNotes = config()->get(Config::Security_HideNotes).toBool();
+
+ m_ui->entryNotesTextEdit->setVisible(hasNotes);
+ setEntryNotesVisible(hasNotes && !hideNotes);
+ m_ui->toggleEntryNotesButton->setVisible(hasNotes && hideNotes
+ && !m_ui->entryNotesTextEdit->toPlainText().isEmpty());
+ m_ui->toggleEntryNotesButton->setChecked(false);
if (config()->get(Config::GUI_MonospaceNotes).toBool()) {
m_ui->entryNotesTextEdit->setFont(Font::fixedFont());
diff --git a/src/gui/Font.cpp b/src/gui/Font.cpp
index 021380c29..8bb54863b 100644
--- a/src/gui/Font.cpp
+++ b/src/gui/Font.cpp
@@ -18,29 +18,29 @@
#include "Font.h"
#include <QFontDatabase>
+#include <QGuiApplication>
QFont Font::defaultFont()
{
- return QFontDatabase::systemFont(QFontDatabase::GeneralFont);
+ return qApp->font();
}
QFont Font::fixedFont()
{
- QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
+ auto fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
#ifdef Q_OS_WIN
// try to use Consolas on Windows, because the default Courier New has too many similar characters
- QFont consolasFont = QFontDatabase().font("Consolas", fixedFont.styleName(), fixedFont.pointSize());
- const QFont defaultFont;
- if (fixedFont != defaultFont) {
+ auto consolasFont = QFontDatabase().font("Consolas", fixedFont.styleName(), fixedFont.pointSize());
+ if (consolasFont.family().contains("consolas", Qt::CaseInsensitive)) {
fixedFont = consolasFont;
}
#endif
#ifdef Q_OS_MACOS
// Qt doesn't choose a monospace font correctly on macOS
- const QFont defaultFont;
- fixedFont = QFontDatabase().font("Menlo", defaultFont.styleName(), defaultFont.pointSize());
+ fixedFont = QFontDatabase().font("Menlo", fixedFont.styleName(), fixedFont.pointSize());
#endif
+ fixedFont.setPointSize(qApp->font().pointSize());
return fixedFont;
}
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index c08f6e677..659ac8457 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -118,6 +118,17 @@ MainWindow::MainWindow()
m_searchWidgetAction = m_ui->toolBar->addWidget(m_searchWidget);
m_searchWidgetAction->setEnabled(false);
+ new QShortcut(QKeySequence::Find, this, SLOT(focusSearchWidget()));
+
+ connect(m_searchWidget, &SearchWidget::searchCanceled, this, [this] {
+ m_ui->toolBar->setExpanded(false);
+ m_ui->toolBar->setVisible(!config()->get(Config::GUI_HideToolbar).toBool());
+ });
+ connect(m_searchWidget, &SearchWidget::lostFocus, this, [this] {
+ m_ui->toolBar->setExpanded(false);
+ m_ui->toolBar->setVisible(!config()->get(Config::GUI_HideToolbar).toBool());
+ });
+
m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size();
m_entryContextMenu = new QMenu(this);
@@ -607,6 +618,19 @@ MainWindow::MainWindow()
QObject::connect(qApp, SIGNAL(applicationActivated()), this, SLOT(bringToFront()));
QObject::connect(qApp, SIGNAL(openFile(QString)), this, SLOT(openDatabase(QString)));
QObject::connect(qApp, SIGNAL(quitSignalReceived()), this, SLOT(appExit()), Qt::DirectConnection);
+
+ statusBar()->setFixedHeight(24);
+ m_progressBarLabel = new QLabel(statusBar());
+ m_progressBarLabel->setVisible(false);
+ statusBar()->addPermanentWidget(m_progressBarLabel);
+ m_progressBar = new QProgressBar(statusBar());
+ m_progressBar->setVisible(false);
+ m_progressBar->setTextVisible(false);
+ m_progressBar->setMaximumWidth(100);
+ m_progressBar->setFixedHeight(15);
+ m_progressBar->setMaximum(100);
+ statusBar()->addPermanentWidget(m_progressBar);
+ connect(clipboard(), SIGNAL(updateCountdown(int, QString)), this, SLOT(updateProgressBar(int, QString)));
}
MainWindow::~MainWindow()
@@ -1214,7 +1238,10 @@ void MainWindow::keyPressEvent(QKeyEvent* event)
dbWidget->focusOnEntries(true);
return;
} else if (event->key() == Qt::Key_F3) {
- m_searchWidget->searchFocus();
+ focusSearchWidget();
+ return;
+ } else if (event->key() == Qt::Key_Escape && dbWidget->isSearchActive()) {
+ m_searchWidget->clearSearch();
return;
}
}
@@ -1235,13 +1262,13 @@ bool MainWindow::focusNextPrevChild(bool next)
} else if (m_ui->tabWidget->hasFocus()) {
dbWidget->setFocus(Qt::TabFocusReason);
} else {
- m_searchWidget->setFocus(Qt::TabFocusReason);
+ focusSearchWidget();
}
} else {
if (m_searchWidget->hasFocus()) {
dbWidget->setFocus(Qt::BacktabFocusReason);
} else if (m_ui->tabWidget->hasFocus()) {
- m_searchWidget->setFocus(Qt::BacktabFocusReason);
+ focusSearchWidget();
} else {
m_ui->tabWidget->setFocus(Qt::BacktabFocusReason);
}
@@ -1253,6 +1280,15 @@ bool MainWindow::focusNextPrevChild(bool next)
return QMainWindow::focusNextPrevChild(next);
}
+void MainWindow::focusSearchWidget()
+{
+ if (m_searchWidgetAction->isEnabled()) {
+ m_ui->toolBar->setVisible(true);
+ m_ui->toolBar->setExpanded(true);
+ m_searchWidget->focusSearch();
+ }
+}
+
void MainWindow::saveWindowInformation()
{
if (isVisible()) {
@@ -1342,6 +1378,19 @@ void MainWindow::updateTrayIcon()
QApplication::setQuitOnLastWindowClosed(!isTrayIconEnabled());
}
+void MainWindow::updateProgressBar(int percentage, QString message)
+{
+ if (percentage < 0) {
+ m_progressBar->setVisible(false);
+ m_progressBarLabel->setVisible(false);
+ } else {
+ m_progressBar->setValue(percentage);
+ m_progressBar->setVisible(true);
+ m_progressBarLabel->setText(message);
+ m_progressBarLabel->setVisible(true);
+ }
+}
+
void MainWindow::obtainContextFocusLock()
{
m_contextMenuFocusLock = true;
@@ -1773,6 +1822,7 @@ void MainWindow::initViewMenu()
});
connect(m_ui->actionAlwaysOnTop, &QAction::toggled, this, [this](bool checked) {
+ config()->set(Config::GUI_AlwaysOnTop, checked);
if (checked) {
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
} else {
@@ -1780,6 +1830,8 @@ void MainWindow::initViewMenu()
}
show();
});
+ // Set checked after connecting to act on a toggle in state (default state is unchecked)
+ m_ui->actionAlwaysOnTop->setChecked(config()->get(Config::GUI_AlwaysOnTop).toBool());
m_ui->actionHideUsernames->setChecked(config()->get(Config::GUI_HideUsernames).toBool());
connect(m_ui->actionHideUsernames, &QAction::toggled, this, [](bool checked) {
diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h
index 7db347fbd..a80346dfe 100644
--- a/src/gui/MainWindow.h
+++ b/src/gui/MainWindow.h
@@ -20,12 +20,16 @@
#define KEEPASSX_MAINWINDOW_H
#include <QActionGroup>
+#include <QLabel>
#include <QMainWindow>
+#include <QProgressBar>
+#include <QStatusBar>
#include <QSystemTrayIcon>
#include "core/ScreenLockListener.h"
#include "core/SignalMultiplexer.h"
#include "gui/Application.h"
+#include "gui/Clipboard.h"
#include "gui/DatabaseWidget.h"
namespace Ui
@@ -138,6 +142,8 @@ private slots:
private slots:
void updateTrayIcon();
+ void updateProgressBar(int percentage, QString message);
+ void focusSearchWidget();
private:
static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0);
@@ -169,6 +175,8 @@ private:
QPointer<QSystemTrayIcon> m_trayIcon;
QPointer<ScreenLockListener> m_screenLockListener;
QPointer<SearchWidget> m_searchWidget;
+ QPointer<QProgressBar> m_progressBar;
+ QPointer<QLabel> m_progressBarLabel;
Q_DISABLE_COPY(MainWindow)
diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui
index e44d3d217..679578f1b 100644
--- a/src/gui/MainWindow.ui
+++ b/src/gui/MainWindow.ui
@@ -16,7 +16,7 @@
<property name="minimumSize">
<size>
<width>800</width>
- <height>0</height>
+ <height>400</height>
</size>
</property>
<property name="windowTitle">
@@ -394,7 +394,7 @@
<addaction name="menuView"/>
<addaction name="menuHelp"/>
</widget>
- <widget class="QToolBar" name="toolBar">
+ <widget class="KPToolBar" name="toolBar">
<property name="contextMenuPolicy">
<enum>Qt::PreventContextMenu</enum>
</property>
@@ -1043,6 +1043,11 @@
<header>gui/WelcomeWidget.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>KPToolBar</class>
+ <extends>QToolBar</extends>
+ <header>gui/widgets/KPToolBar.h</header>
+ </customwidget>
</customwidgets>
<resources/>
<connections/>
diff --git a/src/gui/PasswordGeneratorWidget.ui b/src/gui/PasswordGeneratorWidget.ui
index b4ba97193..d00448096 100644
--- a/src/gui/PasswordGeneratorWidget.ui
+++ b/src/gui/PasswordGeneratorWidget.ui
@@ -666,7 +666,7 @@ QProgressBar::chunk {
<item>
<widget class="QCheckBox" name="checkBoxExcludeAlike">
<property name="toolTip">
- <string>Excluded characters: &quot;0&quot;, &quot;1&quot;, &quot;l&quot;, &quot;I&quot;, &quot;O&quot;, &quot;|&quot;, &quot;﹒&quot;</string>
+ <string>Excluded characters: &quot;0&quot;, &quot;O&quot;, &quot;1&quot;, &quot;l&quot;, &quot;I&quot;, &quot;|&quot;, &quot;G&quot;, &quot;6&quot;, &quot;B&quot;, &quot;8&quot;, &quot;﹒&quot;</string>
</property>
<property name="text">
<string>Exclude look-alike characters</string>
diff --git a/src/gui/SearchWidget.cpp b/src/gui/SearchWidget.cpp
index 6ade47e2e..4cf53a9ac 100644
--- a/src/gui/SearchWidget.cpp
+++ b/src/gui/SearchWidget.cpp
@@ -48,8 +48,8 @@ SearchWidget::SearchWidget(QWidget* parent)
connect(m_ui->helpIcon, SIGNAL(triggered()), SLOT(toggleHelp()));
connect(m_ui->searchIcon, SIGNAL(triggered()), SLOT(showSearchMenu()));
connect(m_searchTimer, SIGNAL(timeout()), SLOT(startSearch()));
- connect(m_clearSearchTimer, SIGNAL(timeout()), m_ui->searchEdit, SLOT(clear()));
- connect(this, SIGNAL(escapePressed()), m_ui->searchEdit, SLOT(clear()));
+ connect(m_clearSearchTimer, SIGNAL(timeout()), SLOT(clearSearch()));
+ connect(this, SIGNAL(escapePressed()), SLOT(clearSearch()));
new QShortcut(QKeySequence::Find, this, SLOT(searchFocus()), nullptr, Qt::ApplicationShortcut);
new QShortcut(Qt::Key_Escape, m_ui->searchEdit, SLOT(clear()), nullptr, Qt::ApplicationShortcut);
@@ -109,7 +109,7 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event)
return true;
}
}
- } else if (event->type() == QEvent::FocusOut && !m_ui->searchEdit->text().isEmpty()) {
+ } else if (event->type() == QEvent::FocusOut) {
if (config()->get(Config::Security_ClearSearch).toBool()) {
int timeout = config()->get(Config::Security_ClearSearchTimeout).toInt();
if (timeout > 0) {
@@ -117,6 +117,7 @@ bool SearchWidget::eventFilter(QObject* obj, QEvent* event)
m_clearSearchTimer->start(timeout * 60000); // 60 sec * 1000 ms
}
}
+ emit lostFocus();
} else if (event->type() == QEvent::FocusIn) {
// Never clear the search if we are using it
m_clearSearchTimer->stop();
@@ -133,10 +134,10 @@ void SearchWidget::connectSignals(SignalMultiplexer& mx)
mx.connect(this, SIGNAL(limitGroupChanged(bool)), SLOT(setSearchLimitGroup(bool)));
mx.connect(this, SIGNAL(copyPressed()), SLOT(copyPassword()));
mx.connect(this, SIGNAL(downPressed()), SLOT(focusOnEntries()));
- mx.connect(SIGNAL(clearSearch()), m_ui->searchEdit, SLOT(clear()));
+ mx.connect(SIGNAL(clearSearch()), this, SLOT(clearSearch()));
mx.connect(SIGNAL(entrySelectionChanged()), this, SLOT(resetSearchClearTimer()));
mx.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(resetSearchClearTimer()));
- mx.connect(SIGNAL(databaseUnlocked()), this, SLOT(searchFocus()));
+ mx.connect(SIGNAL(databaseUnlocked()), this, SLOT(focusSearch()));
mx.connect(m_ui->searchEdit, SIGNAL(returnPressed()), SLOT(switchToEntryEdit()));
}
@@ -149,7 +150,7 @@ void SearchWidget::databaseChanged(DatabaseWidget* dbWidget)
emit caseSensitiveChanged(m_actionCaseSensitive->isChecked());
emit limitGroupChanged(m_actionLimitGroup->isChecked());
} else {
- m_ui->searchEdit->clear();
+ clearSearch();
}
}
@@ -201,12 +202,18 @@ void SearchWidget::setLimitGroup(bool state)
updateLimitGroup();
}
-void SearchWidget::searchFocus()
+void SearchWidget::focusSearch()
{
m_ui->searchEdit->setFocus();
m_ui->searchEdit->selectAll();
}
+void SearchWidget::clearSearch()
+{
+ m_ui->searchEdit->clear();
+ emit searchCanceled();
+}
+
void SearchWidget::toggleHelp()
{
if (m_helpWidget->isVisible()) {
diff --git a/src/gui/SearchWidget.h b/src/gui/SearchWidget.h
index eefdff6ee..b2192f54d 100644
--- a/src/gui/SearchWidget.h
+++ b/src/gui/SearchWidget.h
@@ -52,16 +52,19 @@ protected:
signals:
void search(const QString& text);
+ void searchCanceled();
void caseSensitiveChanged(bool state);
void limitGroupChanged(bool state);
void escapePressed();
void copyPressed();
void downPressed();
void enterPressed();
+ void lostFocus();
public slots:
void databaseChanged(DatabaseWidget* dbWidget = nullptr);
- void searchFocus();
+ void focusSearch();
+ void clearSearch();
private slots:
void startSearchTimer();
diff --git a/src/gui/csvImport/CsvImportWidget.cpp b/src/gui/csvImport/CsvImportWidget.cpp
index e78e9f94a..809fc6e53 100644
--- a/src/gui/csvImport/CsvImportWidget.cpp
+++ b/src/gui/csvImport/CsvImportWidget.cpp
@@ -208,8 +208,13 @@ void CsvImportWidget::writeDatabase()
entry->setUrl(m_parserModel->data(m_parserModel->index(r, 4)).toString());
entry->setNotes(m_parserModel->data(m_parserModel->index(r, 5)).toString());
- if (m_parserModel->data(m_parserModel->index(r, 6)).isValid()) {
- auto totp = Totp::parseSettings(m_parserModel->data(m_parserModel->index(r, 6)).toString());
+ auto otpString = m_parserModel->data(m_parserModel->index(r, 6));
+ if (otpString.isValid() && !otpString.toString().isEmpty()) {
+ auto totp = Totp::parseSettings(otpString.toString());
+ if (totp->key.isEmpty()) {
+ // Bare secret, use default TOTP settings
+ totp = Totp::parseSettings({}, otpString.toString());
+ }
entry->setTotp(totp);
}
diff --git a/src/gui/databasekey/KeyFileEditWidget.cpp b/src/gui/databasekey/KeyFileEditWidget.cpp
index 2fb0b3de2..534bfc8ab 100644
--- a/src/gui/databasekey/KeyFileEditWidget.cpp
+++ b/src/gui/databasekey/KeyFileEditWidget.cpp
@@ -42,7 +42,7 @@ KeyFileEditWidget::~KeyFileEditWidget()
bool KeyFileEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
{
auto fileKey = QSharedPointer<FileKey>::create();
- QString fileKeyName = m_compUi->keyFileCombo->currentText();
+ QString fileKeyName = m_compUi->keyFileLineEdit->text();
if (!fileKey->load(fileKeyName, nullptr)) {
return false;
}
@@ -64,7 +64,7 @@ bool KeyFileEditWidget::validate(QString& errorMessage) const
{
FileKey fileKey;
QString fileKeyError;
- QString fileKeyName = m_compUi->keyFileCombo->currentText();
+ QString fileKeyName = m_compUi->keyFileLineEdit->text();
if (!fileKey.load(fileKeyName, &fileKeyError)) {
errorMessage = tr("Error loading the key file '%1'\nMessage: %2").arg(fileKeyName, fileKeyError);
return false;
@@ -87,7 +87,7 @@ void KeyFileEditWidget::initComponentEditWidget(QWidget* widget)
{
Q_UNUSED(widget);
Q_ASSERT(m_compEditWidget);
- m_compUi->keyFileCombo->setFocus();
+ m_compUi->keyFileLineEdit->setFocus();
}
void KeyFileEditWidget::createKeyFile()
@@ -108,7 +108,7 @@ void KeyFileEditWidget::createKeyFile()
tr("Unable to create key file: %1").arg(errorMsg),
QMessageBox::Button::Ok);
} else {
- m_compUi->keyFileCombo->setEditText(fileName);
+ m_compUi->keyFileLineEdit->setText(fileName);
}
}
}
@@ -143,6 +143,6 @@ void KeyFileEditWidget::browseKeyFile()
}
if (!fileName.isEmpty()) {
- m_compUi->keyFileCombo->setEditText(fileName);
+ m_compUi->keyFileLineEdit->setText(fileName);
}
}
diff --git a/src/gui/databasekey/KeyFileEditWidget.ui b/src/gui/databasekey/KeyFileEditWidget.ui
index 088995dc8..3c83a55d3 100644
--- a/src/gui/databasekey/KeyFileEditWidget.ui
+++ b/src/gui/databasekey/KeyFileEditWidget.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>370</width>
- <height>76</height>
+ <width>566</width>
+ <height>94</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
@@ -23,67 +23,68 @@
<property name="bottomMargin">
<number>0</number>
</property>
- <item row="0" column="0">
- <widget class="QComboBox" name="keyFileCombo">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="createKeyFileButton">
<property name="accessibleName">
- <string>Key file selection</string>
+ <string>Generate a new key file</string>
</property>
- <property name="editable">
- <bool>true</bool>
+ <property name="text">
+ <string>Generate</string>
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QPushButton" name="browseKeyFileButton">
- <property name="accessibleName">
- <string>Browse for key file</string>
- </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="instructions">
<property name="text">
- <string>Browse...</string>
+ <string>Generate a new key file or choose an existing one to protect your database.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QPushButton" name="createKeyFileButton">
- <property name="accessibleName">
- <string>Generate a new key file</string>
+ <item row="3" column="0">
+ <widget class="QLabel" name="instructions_2">
+ <property name="font">
+ <font>
+ <italic>true</italic>
+ </font>
</property>
<property name="text">
- <string>Generate</string>
+ <string>Note: Do NOT use a file that may change as that will prevent you from unlocking your database.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
</property>
</widget>
</item>
- <item row="2" column="0">
+ <item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>20</width>
+ <width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
- <widget class="QLabel" name="label">
- <property name="font">
- <font>
- <italic>true</italic>
- </font>
+ <widget class="QLineEdit" name="keyFileLineEdit">
+ <property name="clearButtonEnabled">
+ <bool>true</bool>
</property>
- <property name="text">
- <string>Note: Do not use a file that may change as that will prevent you from unlocking your database!</string>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="browseKeyFileButton">
+ <property name="accessibleName">
+ <string>Browse for key file</string>
</property>
- <property name="wordWrap">
- <bool>true</bool>
+ <property name="text">
+ <string>Browse…</string>
</property>
</widget>
</item>
diff --git a/src/gui/entry/AutoTypeMatchView.cpp b/src/gui/entry/AutoTypeMatchView.cpp
index 72ee32fde..cdf1f9425 100644
--- a/src/gui/entry/AutoTypeMatchView.cpp
+++ b/src/gui/entry/AutoTypeMatchView.cpp
@@ -64,14 +64,20 @@ AutoTypeMatchView::AutoTypeMatchView(QWidget* parent)
void AutoTypeMatchView::userNameCopied()
{
- clipboard()->setText(currentMatch().entry->username());
- emit matchTextCopied();
+ auto entry = currentMatch().entry;
+ if (entry) {
+ clipboard()->setText(entry->resolvePlaceholder(entry->username()));
+ emit matchTextCopied();
+ }
}
void AutoTypeMatchView::passwordCopied()
{
- clipboard()->setText(currentMatch().entry->password());
- emit matchTextCopied();
+ auto entry = currentMatch().entry;
+ if (entry) {
+ clipboard()->setText(entry->resolvePlaceholder(entry->password()));
+ emit matchTextCopied();
+ }
}
void AutoTypeMatchView::keyPressEvent(QKeyEvent* event)
diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp
index c4de82973..93b5943b4 100644
--- a/src/gui/entry/EditEntryWidget.cpp
+++ b/src/gui/entry/EditEntryWidget.cpp
@@ -279,6 +279,7 @@ void EditEntryWidget::setupBrowser()
connect(m_browserUi->skipAutoSubmitCheckbox, SIGNAL(toggled(bool)), SLOT(updateBrowserModified()));
connect(m_browserUi->hideEntryCheckbox, SIGNAL(toggled(bool)), SLOT(updateBrowserModified()));
connect(m_browserUi->onlyHttpAuthCheckbox, SIGNAL(toggled(bool)), SLOT(updateBrowserModified()));
+ connect(m_browserUi->notHttpAuthCheckbox, SIGNAL(toggled(bool)), SLOT(updateBrowserModified()));
connect(m_browserUi->addURLButton, SIGNAL(clicked()), SLOT(insertURL()));
connect(m_browserUi->removeURLButton, SIGNAL(clicked()), SLOT(removeCurrentURL()));
connect(m_browserUi->editURLButton, SIGNAL(clicked()), SLOT(editCurrentURL()));
@@ -306,9 +307,11 @@ void EditEntryWidget::updateBrowser()
auto skip = m_browserUi->skipAutoSubmitCheckbox->isChecked();
auto hide = m_browserUi->hideEntryCheckbox->isChecked();
auto onlyHttpAuth = m_browserUi->onlyHttpAuthCheckbox->isChecked();
+ auto notHttpAuth = m_browserUi->notHttpAuthCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_SKIP_AUTO_SUBMIT, (skip ? TRUE_STR : FALSE_STR));
m_customData->set(BrowserService::OPTION_HIDE_ENTRY, (hide ? TRUE_STR : FALSE_STR));
m_customData->set(BrowserService::OPTION_ONLY_HTTP_AUTH, (onlyHttpAuth ? TRUE_STR : FALSE_STR));
+ m_customData->set(BrowserService::OPTION_NOT_HTTP_AUTH, (notHttpAuth ? TRUE_STR : FALSE_STR));
}
void EditEntryWidget::insertURL()
@@ -473,6 +476,7 @@ void EditEntryWidget::setupEntryUpdate()
connect(m_browserUi->skipAutoSubmitCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
connect(m_browserUi->hideEntryCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
connect(m_browserUi->onlyHttpAuthCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
+ connect(m_browserUi->notHttpAuthCheckbox, SIGNAL(toggled(bool)), SLOT(setModified()));
connect(m_browserUi->addURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
connect(m_browserUi->removeURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
connect(m_browserUi->editURLButton, SIGNAL(toggled(bool)), SLOT(setModified()));
@@ -958,6 +962,13 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
m_browserUi->onlyHttpAuthCheckbox->setChecked(false);
}
+ if (m_customData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)) {
+ m_browserUi->notHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_NOT_HTTP_AUTH)
+ == TRUE_STR);
+ } else {
+ m_browserUi->notHttpAuthCheckbox->setChecked(false);
+ }
+
m_browserUi->addURLButton->setEnabled(!m_history);
m_browserUi->removeURLButton->setEnabled(false);
m_browserUi->editURLButton->setEnabled(false);
diff --git a/src/gui/entry/EditEntryWidgetBrowser.ui b/src/gui/entry/EditEntryWidgetBrowser.ui
index 9d1e0f511..5f117e987 100644
--- a/src/gui/entry/EditEntryWidgetBrowser.ui
+++ b/src/gui/entry/EditEntryWidgetBrowser.ui
@@ -60,6 +60,16 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QCheckBox" name="notHttpAuthCheckbox">
+ <property name="toolTip">
+ <string>Do not send this setting to the browser for HTTP Auth dialogs. If enabled, HTTP Auth dialogs will not show this entry for selection.</string>
+ </property>
+ <property name="text">
+ <string>Do not use this entry with HTTP Basic Auth</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/gui/entry/EntryAttachmentsWidget.cpp b/src/gui/entry/EntryAttachmentsWidget.cpp
index 836c8ef27..f78b64417 100644
--- a/src/gui/entry/EntryAttachmentsWidget.cpp
+++ b/src/gui/entry/EntryAttachmentsWidget.cpp
@@ -238,7 +238,8 @@ void EntryAttachmentsWidget::saveSelectedAttachments()
QFile file(attachmentPath);
const QByteArray attachmentData = m_entryAttachments->value(filename);
- const bool saveOk = file.open(QIODevice::WriteOnly) && file.write(attachmentData) == attachmentData.size();
+ const bool saveOk = file.open(QIODevice::WriteOnly) && file.setPermissions(QFile::ReadUser | QFile::WriteUser)
+ && file.write(attachmentData) == attachmentData.size();
if (!saveOk) {
errors.append(QString("%1 - %2").arg(filename, file.errorString()));
}
diff --git a/src/gui/osutils/macutils/AppKitImpl.mm b/src/gui/osutils/macutils/AppKitImpl.mm
index faf061106..31362c8e6 100644
--- a/src/gui/osutils/macutils/AppKitImpl.mm
+++ b/src/gui/osutils/macutils/AppKitImpl.mm
@@ -203,11 +203,10 @@
- (void) toggleForegroundApp:(bool) foreground
{
- ProcessSerialNumber psn = {0, kCurrentProcess};
if (foreground) {
- TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
} else {
- TransformProcessType(&psn, kProcessTransformToUIElementApplication);
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
}
}
diff --git a/src/gui/osutils/macutils/MacUtils.cpp b/src/gui/osutils/macutils/MacUtils.cpp
index ebcc627e0..9c107df77 100644
--- a/src/gui/osutils/macutils/MacUtils.cpp
+++ b/src/gui/osutils/macutils/MacUtils.cpp
@@ -69,6 +69,7 @@ bool MacUtils::raiseWindow(WId pid)
bool MacUtils::raiseOwnWindow()
{
+ m_appkit->toggleForegroundApp(true);
return m_appkit->activateProcess(m_appkit->ownProcessId());
}
diff --git a/src/gui/osutils/nixutils/NixUtils.cpp b/src/gui/osutils/nixutils/NixUtils.cpp
index eacb20060..9f147c59e 100644
--- a/src/gui/osutils/nixutils/NixUtils.cpp
+++ b/src/gui/osutils/nixutils/NixUtils.cpp
@@ -120,7 +120,8 @@ void NixUtils::setLaunchAtStartup(bool enable)
<< QStringLiteral("MimeType=application/x-keepass2;") << '\n'
<< QStringLiteral("X-GNOME-Autostart-enabled=true") << '\n'
<< QStringLiteral("X-GNOME-Autostart-Delay=2") << '\n'
- << QStringLiteral("X-KDE-autostart-after=panel") << endl;
+ << QStringLiteral("X-KDE-autostart-after=panel") << '\n'
+ << QStringLiteral("X-LXQt-Need-Tray=true") << endl;
desktopFile.close();
} else if (isLaunchAtStartupEnabled()) {
QFile::remove(getAutostartDesktopFilename());
diff --git a/src/gui/reports/ReportsWidgetHealthcheck.cpp b/src/gui/reports/ReportsWidgetHealthcheck.cpp
index bc42b1e01..aea468059 100644
--- a/src/gui/reports/ReportsWidgetHealthcheck.cpp
+++ b/src/gui/reports/ReportsWidgetHealthcheck.cpp
@@ -150,8 +150,10 @@ ReportsWidgetHealthcheck::ReportsWidgetHealthcheck(QWidget* parent)
m_modelProxy->setSortLocaleAware(true);
m_ui->healthcheckTableView->setModel(m_modelProxy.data());
m_ui->healthcheckTableView->setSelectionMode(QAbstractItemView::NoSelection);
- m_ui->healthcheckTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+ m_ui->healthcheckTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
+ m_ui->healthcheckTableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
m_ui->healthcheckTableView->setSortingEnabled(true);
+ m_ui->healthcheckTableView->setWordWrap(true);
connect(m_ui->healthcheckTableView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
connect(m_ui->healthcheckTableView, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex)));
@@ -281,7 +283,8 @@ void ReportsWidgetHealthcheck::calculateHealth()
m_ui->healthcheckTableView->sortByColumn(0, Qt::AscendingOrder);
}
- m_ui->healthcheckTableView->resizeRowsToContents();
+ m_ui->healthcheckTableView->resizeColumnsToContents();
+ m_ui->healthcheckTableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
// Show the "show known bad entries" checkbox if there's any known
// bad entry in the database.
diff --git a/src/gui/reports/ReportsWidgetHibp.cpp b/src/gui/reports/ReportsWidgetHibp.cpp
index 406c465b9..f48e7448f 100644
--- a/src/gui/reports/ReportsWidgetHibp.cpp
+++ b/src/gui/reports/ReportsWidgetHibp.cpp
@@ -79,7 +79,8 @@ ReportsWidgetHibp::ReportsWidgetHibp(QWidget* parent)
m_modelProxy->setSortLocaleAware(true);
m_ui->hibpTableView->setModel(m_modelProxy.data());
m_ui->hibpTableView->setSelectionMode(QAbstractItemView::NoSelection);
- m_ui->hibpTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+ m_ui->hibpTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
+ m_ui->hibpTableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
m_ui->hibpTableView->setSortingEnabled(true);
connect(m_ui->hibpTableView, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex)));
@@ -219,7 +220,7 @@ void ReportsWidgetHibp::makeHibpTable()
m_ui->showKnownBadCheckBox->hide();
}
- m_ui->hibpTableView->resizeRowsToContents();
+ m_ui->hibpTableView->resizeColumnsToContents();
m_ui->hibpTableView->sortByColumn(2, Qt::DescendingOrder);
m_ui->stackedWidget->setCurrentIndex(1);
diff --git a/src/gui/styles/base/basestyle.qss b/src/gui/styles/base/basestyle.qss
index ff5d915bb..e015efc25 100644
--- a/src/gui/styles/base/basestyle.qss
+++ b/src/gui/styles/base/basestyle.qss
@@ -69,3 +69,7 @@ QPlainTextEdit, QTextEdit {
background-color: palette(base);
padding-left: 4px;
}
+
+QStatusBar {
+ background-color: palette(window);
+}
diff --git a/src/gui/styles/dark/DarkStyle.cpp b/src/gui/styles/dark/DarkStyle.cpp
index 491f18d1f..74653bc98 100644
--- a/src/gui/styles/dark/DarkStyle.cpp
+++ b/src/gui/styles/dark/DarkStyle.cpp
@@ -21,6 +21,7 @@
#include <QDialog>
#include <QMainWindow>
#include <QMenuBar>
+#include <QStatusBar>
#include <QToolBar>
DarkStyle::DarkStyle()
@@ -110,7 +111,7 @@ QString DarkStyle::getAppStyleSheet() const
void DarkStyle::polish(QWidget* widget)
{
if (qobject_cast<QMainWindow*>(widget) || qobject_cast<QDialog*>(widget) || qobject_cast<QMenuBar*>(widget)
- || qobject_cast<QToolBar*>(widget)) {
+ || qobject_cast<QToolBar*>(widget) || qobject_cast<QStatusBar*>(widget)) {
auto palette = widget->palette();
#if defined(Q_OS_MACOS)
if (!osUtils->isDarkMode()) {
diff --git a/src/gui/styles/light/LightStyle.cpp b/src/gui/styles/light/LightStyle.cpp
index 483e1323b..537e483ba 100644
--- a/src/gui/styles/light/LightStyle.cpp
+++ b/src/gui/styles/light/LightStyle.cpp
@@ -22,6 +22,7 @@
#include <QDialog>
#include <QMainWindow>
#include <QMenuBar>
+#include <QStatusBar>
#include <QToolBar>
LightStyle::LightStyle()
@@ -111,7 +112,7 @@ QString LightStyle::getAppStyleSheet() const
void LightStyle::polish(QWidget* widget)
{
if (qobject_cast<QMainWindow*>(widget) || qobject_cast<QDialog*>(widget) || qobject_cast<QMenuBar*>(widget)
- || qobject_cast<QToolBar*>(widget)) {
+ || qobject_cast<QToolBar*>(widget) || qobject_cast<QStatusBar*>(widget)) {
auto palette = widget->palette();
#if defined(Q_OS_MACOS)
if (osUtils->isDarkMode()) {
diff --git a/src/gui/widgets/KPToolBar.cpp b/src/gui/widgets/KPToolBar.cpp
new file mode 100644
index 000000000..88b75b9b1
--- /dev/null
+++ b/src/gui/widgets/KPToolBar.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "KPToolBar.h"
+
+#include <QAbstractButton>
+#include <QEvent>
+#include <QLayout>
+
+KPToolBar::KPToolBar(const QString& title, QWidget* parent)
+ : QToolBar(title, parent)
+{
+ init();
+}
+
+KPToolBar::KPToolBar(QWidget* parent)
+ : QToolBar(parent)
+{
+ init();
+}
+
+void KPToolBar::init()
+{
+ m_expandButton = findChild<QAbstractButton*>("qt_toolbar_ext_button");
+ m_expandTimer.setSingleShot(true);
+ connect(&m_expandTimer, &QTimer::timeout, this, [this] { setExpanded(false); });
+}
+
+bool KPToolBar::isExpanded()
+{
+ return !canExpand() || (canExpand() && m_expandButton->isChecked());
+}
+
+bool KPToolBar::canExpand()
+{
+ return m_expandButton && m_expandButton->isVisible();
+}
+
+void KPToolBar::setExpanded(bool state)
+{
+ if (canExpand() && !QMetaObject::invokeMethod(layout(), "setExpanded", Q_ARG(bool, state))) {
+ qWarning("Toolbar: Cannot invoke setExpanded!");
+ }
+}
+
+bool KPToolBar::event(QEvent* event)
+{
+ // Override events handled by the base class for better UX when using an expandable toolbar.
+ switch (event->type()) {
+ case QEvent::Leave:
+ // Hide the toolbar after 2 seconds of mouse exit
+ m_expandTimer.start(2000);
+ return true;
+ case QEvent::Enter:
+ // Mouse came back in, stop hiding timer
+ m_expandTimer.stop();
+ return true;
+ default:
+ return QToolBar::event(event);
+ }
+}
diff --git a/src/gui/widgets/KPToolBar.h b/src/gui/widgets/KPToolBar.h
new file mode 100644
index 000000000..8883982bd
--- /dev/null
+++ b/src/gui/widgets/KPToolBar.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEEPASSXC_KPTOOLBAR_H
+#define KEEPASSXC_KPTOOLBAR_H
+
+#include <QPointer>
+#include <QTimer>
+#include <QToolBar>
+
+class QAbstractButton;
+
+class KPToolBar : public QToolBar
+{
+ Q_OBJECT
+
+public:
+ explicit KPToolBar(const QString& title, QWidget* parent = nullptr);
+ explicit KPToolBar(QWidget* parent = nullptr);
+ ~KPToolBar() override = default;
+
+ bool isExpanded();
+ bool canExpand();
+
+public slots:
+ void setExpanded(bool state);
+
+protected:
+ bool event(QEvent* event) override;
+
+private:
+ void init();
+
+ QTimer m_expandTimer;
+ QPointer<QAbstractButton> m_expandButton;
+};
+
+#endif // KEEPASSXC_KPTOOLBAR_H
diff --git a/src/keeshare/CMakeLists.txt b/src/keeshare/CMakeLists.txt
index 333f273a3..96f9bda85 100644
--- a/src/keeshare/CMakeLists.txt
+++ b/src/keeshare/CMakeLists.txt
@@ -25,7 +25,7 @@ if(WITH_XC_KEESHARE)
find_package(QuaZip)
if(QUAZIP_FOUND)
set(WITH_XC_KEESHARE_SECURE ON PARENT_SCOPE)
- target_include_directories(keeshare SYSTEM PRIVATE ${QUAZIP_INCLUDE_DIR})
+ target_include_directories(keeshare SYSTEM PRIVATE ${QUAZIP_INCLUDE_DIRS})
target_link_libraries(keeshare PRIVATE ${QUAZIP_LIBRARIES})
else()
set(WITH_XC_KEESHARE_SECURE OFF PARENT_SCOPE)
diff --git a/src/keeshare/ShareExport.cpp b/src/keeshare/ShareExport.cpp
index c1797ac6d..aedbc04af 100644
--- a/src/keeshare/ShareExport.cpp
+++ b/src/keeshare/ShareExport.cpp
@@ -224,9 +224,5 @@ ShareObserver::Result ShareExport::intoContainer(const QString& resolvedPath,
if (KeeShare::isContainerType(info, KeeShare::signedContainerFileType())) {
return intoSignedContainer(resolvedPath, reference, targetDb.data());
}
- if (KeeShare::isContainerType(info, KeeShare::unsignedContainerFileType())) {
- return intoUnsignedContainer(resolvedPath, reference, targetDb.data());
- }
- Q_ASSERT(false);
- return {reference.path, ShareObserver::Result::Error, tr("Unexpected export error occurred")};
+ return intoUnsignedContainer(resolvedPath, reference, targetDb.data());
}
diff --git a/src/keeshare/ShareImport.cpp b/src/keeshare/ShareImport.cpp
index a767ab3aa..38a477aaf 100644
--- a/src/keeshare/ShareImport.cpp
+++ b/src/keeshare/ShareImport.cpp
@@ -344,8 +344,5 @@ ShareObserver::Result ShareImport::containerInto(const QString& resolvedPath,
if (KeeShare::isContainerType(info, KeeShare::signedContainerFileType())) {
return signedContainerInto(resolvedPath, reference, targetGroup);
}
- if (KeeShare::isContainerType(info, KeeShare::unsignedContainerFileType())) {
- return unsignedContainerInto(resolvedPath, reference, targetGroup);
- }
- return {reference.path, ShareObserver::Result::Error, tr("Unknown share container type")};
+ return unsignedContainerInto(resolvedPath, reference, targetGroup);
}
diff --git a/src/main.cpp b/src/main.cpp
index b88dc41e0..43f9d0992 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -64,6 +64,7 @@ int main(int argc, char** argv)
QCommandLineOption configOption("config", QObject::tr("path to a custom config file"), "config");
QCommandLineOption localConfigOption(
"localconfig", QObject::tr("path to a custom local config file"), "localconfig");
+ QCommandLineOption lockOption("lock", QObject::tr("lock all open databases"));
QCommandLineOption keyfileOption("keyfile", QObject::tr("key file of the database"), "keyfile");
QCommandLineOption pwstdinOption("pw-stdin", QObject::tr("read password of the database from stdin"));
@@ -72,6 +73,7 @@ int main(int argc, char** argv)
QCommandLineOption debugInfoOption(QStringList() << "debug-info", QObject::tr("Displays debugging information."));
parser.addOption(configOption);
parser.addOption(localConfigOption);
+ parser.addOption(lockOption);
parser.addOption(keyfileOption);
parser.addOption(pwstdinOption);
parser.addOption(debugInfoOption);
@@ -96,12 +98,23 @@ int main(int argc, char** argv)
}
// Process single instance and early exit if already running
+ // FIXME: this is a *mess* and it is entirely my fault. --wundrweapon
const QStringList fileNames = parser.positionalArguments();
if (app.isAlreadyRunning()) {
- if (!fileNames.isEmpty()) {
- app.sendFileNamesToRunningInstance(fileNames);
+ if (parser.isSet(lockOption)) {
+ if (app.sendLockToInstance()) {
+ qInfo() << QObject::tr("Locked databases.").toUtf8().constData();
+ } else {
+ qWarning() << QObject::tr("Database failed to lock.").toUtf8().constData();
+ return EXIT_FAILURE;
+ }
+ } else {
+ if (!fileNames.isEmpty()) {
+ app.sendFileNamesToRunningInstance(fileNames);
+ }
+
+ qWarning() << QObject::tr("Another instance of KeePassXC is already running.").toUtf8().constData();
}
- qWarning() << QObject::tr("Another instance of KeePassXC is already running.").toUtf8().constData();
return EXIT_SUCCESS;
}