2010-09-19 10:59:32 -04:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
2017-06-09 17:40:36 -04:00
|
|
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
2010-09-19 10:59:32 -04:00
|
|
|
*
|
|
|
|
* 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 "MainWindow.h"
|
2011-12-26 12:55:50 -05:00
|
|
|
#include "ui_MainWindow.h"
|
2010-09-19 10:59:32 -04:00
|
|
|
|
2013-10-03 09:18:16 -04:00
|
|
|
#include <QCloseEvent>
|
2018-10-31 23:27:38 -04:00
|
|
|
#include <QDesktopServices>
|
2019-01-16 22:53:29 -05:00
|
|
|
#include <QFileInfo>
|
2018-01-13 12:44:56 -05:00
|
|
|
#include <QMimeData>
|
2013-10-03 09:18:16 -04:00
|
|
|
#include <QShortcut>
|
2015-09-27 17:16:18 -04:00
|
|
|
#include <QTimer>
|
2019-06-11 18:25:17 -04:00
|
|
|
#include <QWindow>
|
2012-04-11 14:35:52 -04:00
|
|
|
|
2016-12-04 13:57:24 -05:00
|
|
|
#include "config-keepassx.h"
|
|
|
|
|
2012-07-14 13:09:28 -04:00
|
|
|
#include "autotype/AutoType.h"
|
|
|
|
#include "core/Config.h"
|
2012-07-18 14:44:28 -04:00
|
|
|
#include "core/FilePath.h"
|
2014-01-07 15:56:58 -05:00
|
|
|
#include "core/InactivityTimer.h"
|
2010-09-19 10:59:32 -04:00
|
|
|
#include "core/Metadata.h"
|
2019-01-24 22:45:05 -05:00
|
|
|
#include "core/Tools.h"
|
2012-05-02 09:37:21 -04:00
|
|
|
#include "gui/AboutDialog.h"
|
2018-03-31 16:01:30 -04:00
|
|
|
#include "gui/DatabaseWidget.h"
|
2016-11-02 21:01:02 -04:00
|
|
|
#include "gui/SearchWidget.h"
|
2019-01-16 22:53:29 -05:00
|
|
|
#include "keys/CompositeKey.h"
|
|
|
|
#include "keys/FileKey.h"
|
|
|
|
#include "keys/PasswordKey.h"
|
2010-09-19 10:59:32 -04:00
|
|
|
|
2019-04-15 14:34:16 -04:00
|
|
|
#ifdef Q_OS_MACOS
|
2020-01-28 15:42:57 -05:00
|
|
|
#include "gui/osutils/macutils/MacUtils.h"
|
2019-04-15 14:34:16 -04:00
|
|
|
#endif
|
|
|
|
|
2019-04-09 21:06:13 -04:00
|
|
|
#ifdef WITH_XC_UPDATECHECK
|
2019-01-30 09:11:50 -05:00
|
|
|
#include "gui/MessageBox.h"
|
|
|
|
#include "gui/UpdateCheckDialog.h"
|
2019-03-19 14:48:33 -04:00
|
|
|
#include "updatecheck/UpdateChecker.h"
|
2019-01-30 09:11:50 -05:00
|
|
|
#endif
|
|
|
|
|
2017-10-29 11:17:24 -04:00
|
|
|
#ifdef WITH_XC_SSHAGENT
|
|
|
|
#include "sshagent/AgentSettingsPage.h"
|
|
|
|
#include "sshagent/SSHAgent.h"
|
|
|
|
#endif
|
2019-01-03 02:46:32 -05:00
|
|
|
#if defined(WITH_XC_KEESHARE)
|
2018-10-01 10:26:24 -04:00
|
|
|
#include "keeshare/KeeShare.h"
|
|
|
|
#include "keeshare/SettingsPageKeeShare.h"
|
|
|
|
#endif
|
Add Freedesktop.org Secret Storage Spec Server Side API (Fix #1403)
This plugin implements the Secret Storage specification version 0.2.
While running KeePassXC, it acts as a Secret Service server, registered
on DBus, so clients like seahorse, python-secretstorage, or other
implementations can connect and access the exposed database in KeePassXC.
Squashed commits:
- Initial code
- Add SessionAdaptor and fix build
- The skeletons for all dbus objects are in place
- Implement collection creation and deletion
- Emit collectionChanged signal
- Implement app-wise settings page
- Implement error message on GUI
- Implement settings
- Fix uuid to dbus path
- Implement app level settings
- Add freedesktop logo
- Implement database settings page
- Change database settings to a treeview
- Move all settings read/write to one place
- Rename SecretServiceOptionsPage to SettingsWidgetFdoSecrets
- Fix selected group can not be saved if the user hasn't click on the item
- Show selected group per database in app settings
- Disable editing of various readonly widgets
- Remove unused warning about non exposed database
- Fix method signature on dbus adaptors
- Fix type derived from DBusObject not recognized as QDBusContext
- Resolve a few TODOs around error handling
- Remove const when passing DBus exposed objects
- Move dismiss to PromptBase
- Implement per collection locking/unlocking
- Fix const correctness on Item::setSecret
- Implement SecretService::getSecrets
- Rework the signal connections around collections.
- Remove generateId from DBusObject
- Per spec, use encoded label as DBus object path for collections
- Fix some corner cases around collection name changes
- Implement alias
- Fix wrong alias dbus path
- Implement encryption per spec
- Cleanup SessionCipher
- Implement searchItems for SecretService
- Use Tools::uuidToHex
- Implement Item attributes and delete
- Implement createItem
- Always check if the database is unlocked before perform any operation
- Add missing ReadAlias/SetAlias on service
- Reorganize and fix OpenSession always returning empty output
- Overhaul error handling
- Make sure default alias is always present
- Remove collection aliases early in doDelete
- Handles all content types, fix setProperties not working
- Fix sometimes there is an extraneous leading zero when converting from MPI
- Fix session encryption negotiation
- Do not expose recycle bin
- Protect against the methods not called from DBus
- Also emit collectionChanged signal when lock state changes
- Show notification when entry secret is requested
- Add a README file
- Actually close session when client disconnects
- Gracefully return alternative label when collection is locked
- Reorganize, rename secretservice to fdosecrets
- Fix issues reported by clazy
- Unify UI strings and fix icon
- Implement a setting to skip confirmation when deleting entries from DBus
- Remove some unused debugging log
- Simply ignore errors when DBus context is not available. QtDBus won't set QDBusContext when deliver property get/set, and there is no way to get a QDBusMessage in property getter/setter.
- Simplify GcryptMPI using std::unique_ptr and add unit test
- Format code in fdosecrets
- Move DBusReturnImpl to details namespace
- Fix crash when locking a database: don't modify exposedGroup setting in customData when database is deleted
- Make sure Collection::searchItems works, whether it's locked or not
- Fix FdoSecrets::Collection becomes empty after a database reload
- Fix crash when looping while modifying the list
2019-03-25 23:07:18 -04:00
|
|
|
|
|
|
|
#ifdef WITH_XC_FDOSECRETS
|
|
|
|
#include "fdosecrets/FdoSecretsPlugin.h"
|
|
|
|
#endif
|
|
|
|
|
2017-12-12 03:15:23 -05:00
|
|
|
#ifdef WITH_XC_BROWSER
|
|
|
|
#include "browser/BrowserOptionDialog.h"
|
2018-03-31 16:01:30 -04:00
|
|
|
#include "browser/BrowserSettings.h"
|
|
|
|
#include "browser/NativeMessagingHost.h"
|
2017-12-12 03:15:23 -05:00
|
|
|
#endif
|
|
|
|
|
2018-10-26 09:19:04 -04:00
|
|
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
|
2018-03-31 16:01:30 -04:00
|
|
|
#include "gui/MainWindowAdaptor.h"
|
2018-01-15 18:49:32 -05:00
|
|
|
#include <QList>
|
|
|
|
#include <QtDBus/QtDBus>
|
2017-02-10 09:42:18 -05:00
|
|
|
#endif
|
2018-01-15 18:49:32 -05:00
|
|
|
|
2018-05-13 17:21:43 -04:00
|
|
|
#include "gui/ApplicationSettingsWidget.h"
|
2018-10-31 23:27:38 -04:00
|
|
|
#include "gui/PasswordGeneratorWidget.h"
|
2017-02-10 09:42:18 -05:00
|
|
|
|
2018-04-04 11:39:26 -04:00
|
|
|
#include "touchid/TouchID.h"
|
|
|
|
|
2017-12-12 03:15:23 -05:00
|
|
|
#ifdef WITH_XC_BROWSER
|
2018-03-31 16:01:30 -04:00
|
|
|
class BrowserPlugin : public ISettingsPage
|
2017-12-12 03:15:23 -05:00
|
|
|
{
|
2018-03-31 16:01:30 -04:00
|
|
|
public:
|
2019-02-18 08:26:56 -05:00
|
|
|
explicit BrowserPlugin(DatabaseTabWidget* tabWidget)
|
2019-01-16 22:53:29 -05:00
|
|
|
{
|
|
|
|
m_nativeMessagingHost =
|
|
|
|
QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget, browserSettings()->isEnabled()));
|
|
|
|
}
|
|
|
|
|
|
|
|
~BrowserPlugin()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QString name() override
|
|
|
|
{
|
|
|
|
return QObject::tr("Browser Integration");
|
|
|
|
}
|
|
|
|
|
|
|
|
QIcon icon() override
|
|
|
|
{
|
|
|
|
return FilePath::instance()->icon("apps", "internet-web-browser");
|
|
|
|
}
|
|
|
|
|
|
|
|
QWidget* createWidget() override
|
|
|
|
{
|
|
|
|
BrowserOptionDialog* dlg = new BrowserOptionDialog();
|
|
|
|
return dlg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void loadSettings(QWidget* widget) override
|
|
|
|
{
|
|
|
|
qobject_cast<BrowserOptionDialog*>(widget)->loadSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void saveSettings(QWidget* widget) override
|
|
|
|
{
|
|
|
|
qobject_cast<BrowserOptionDialog*>(widget)->saveSettings();
|
|
|
|
if (browserSettings()->isEnabled()) {
|
|
|
|
m_nativeMessagingHost->run();
|
|
|
|
} else {
|
|
|
|
m_nativeMessagingHost->stop();
|
|
|
|
}
|
|
|
|
}
|
2017-12-12 03:15:23 -05:00
|
|
|
|
2018-03-31 16:01:30 -04:00
|
|
|
private:
|
2019-01-16 22:53:29 -05:00
|
|
|
QSharedPointer<NativeMessagingHost> m_nativeMessagingHost;
|
2017-12-12 03:15:23 -05:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2016-11-03 00:05:30 -04:00
|
|
|
const QString MainWindow::BaseWindowTitle = "KeePassXC";
|
2012-08-01 04:40:18 -04:00
|
|
|
|
2018-11-01 21:33:27 -04:00
|
|
|
MainWindow* g_MainWindow = nullptr;
|
2019-01-16 22:53:29 -05:00
|
|
|
MainWindow* getMainWindow()
|
|
|
|
{
|
|
|
|
return g_MainWindow;
|
|
|
|
}
|
2018-11-01 21:33:27 -04:00
|
|
|
|
2010-09-19 10:59:32 -04:00
|
|
|
MainWindow::MainWindow()
|
2019-01-16 22:53:29 -05:00
|
|
|
: m_ui(new Ui::MainWindow())
|
2010-09-19 10:59:32 -04:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
g_MainWindow = this;
|
2018-11-01 21:33:27 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->setupUi(this);
|
2018-03-31 16:01:30 -04:00
|
|
|
|
2018-10-26 09:19:04 -04:00
|
|
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS)
|
2019-01-16 22:53:29 -05:00
|
|
|
new MainWindowAdaptor(this);
|
|
|
|
QDBusConnection dbus = QDBusConnection::sessionBus();
|
|
|
|
dbus.registerObject("/keepassxc", this);
|
|
|
|
dbus.registerService("org.keepassxc.KeePassXC.MainWindow");
|
2018-01-15 18:49:32 -05:00
|
|
|
#endif
|
2010-09-19 10:59:32 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
setAcceptDrops(true);
|
2017-12-05 02:44:01 -05:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
// Setup the search widget in the toolbar
|
2019-06-11 03:37:23 -04:00
|
|
|
m_searchWidget = new SearchWidget();
|
|
|
|
m_searchWidget->connectSignals(m_actionMultiplexer);
|
|
|
|
m_searchWidgetAction = m_ui->toolBar->addWidget(m_searchWidget);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_searchWidgetAction->setEnabled(false);
|
2016-11-02 21:01:02 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size();
|
2014-05-15 12:05:58 -04:00
|
|
|
|
2019-10-19 10:07:09 -04:00
|
|
|
m_entryContextMenu = new QMenu(this);
|
|
|
|
m_entryContextMenu->addAction(m_ui->actionEntryCopyUsername);
|
|
|
|
m_entryContextMenu->addAction(m_ui->actionEntryCopyPassword);
|
|
|
|
m_entryContextMenu->addAction(m_ui->menuEntryCopyAttribute->menuAction());
|
|
|
|
m_entryContextMenu->addAction(m_ui->menuEntryTotp->menuAction());
|
|
|
|
m_entryContextMenu->addSeparator();
|
|
|
|
m_entryContextMenu->addAction(m_ui->actionEntryAutoType);
|
|
|
|
m_entryContextMenu->addSeparator();
|
|
|
|
m_entryContextMenu->addAction(m_ui->actionEntryEdit);
|
|
|
|
m_entryContextMenu->addAction(m_ui->actionEntryClone);
|
|
|
|
m_entryContextMenu->addAction(m_ui->actionEntryDelete);
|
|
|
|
m_entryContextMenu->addSeparator();
|
|
|
|
m_entryContextMenu->addAction(m_ui->actionEntryOpenUrl);
|
|
|
|
m_entryContextMenu->addAction(m_ui->actionEntryDownloadIcon);
|
|
|
|
|
2019-10-26 14:54:52 -04:00
|
|
|
m_entryNewContextMenu = new QMenu(this);
|
|
|
|
m_entryNewContextMenu->addAction(m_ui->actionEntryNew);
|
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
restoreGeometry(config()->get("GUI/MainWindowGeometry").toByteArray());
|
2019-01-16 11:04:32 -05:00
|
|
|
restoreState(config()->get("GUI/MainWindowState").toByteArray());
|
2018-01-16 15:45:31 -05:00
|
|
|
#ifdef WITH_XC_BROWSER
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->settingsWidget->addSettingsPage(new BrowserPlugin(m_ui->tabWidget));
|
2018-01-16 15:45:31 -05:00
|
|
|
#endif
|
2018-10-01 10:26:24 -04:00
|
|
|
|
2018-01-16 15:45:31 -05:00
|
|
|
#ifdef WITH_XC_SSHAGENT
|
2020-01-28 13:46:23 -05:00
|
|
|
connect(sshAgent(), SIGNAL(error(QString)), this, SLOT(showErrorMessage(QString)));
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage(m_ui->tabWidget));
|
2018-01-16 15:45:31 -05:00
|
|
|
#endif
|
2014-03-22 07:21:49 -04:00
|
|
|
|
2019-01-03 02:46:32 -05:00
|
|
|
#if defined(WITH_XC_KEESHARE)
|
2019-01-16 22:53:29 -05:00
|
|
|
KeeShare::init(this);
|
|
|
|
m_ui->settingsWidget->addSettingsPage(new SettingsPageKeeShare(m_ui->tabWidget));
|
|
|
|
connect(KeeShare::instance(),
|
|
|
|
SIGNAL(sharingMessage(QString, MessageWidget::MessageType)),
|
|
|
|
SLOT(displayGlobalMessage(QString, MessageWidget::MessageType)));
|
2018-10-01 10:26:24 -04:00
|
|
|
#endif
|
Add Freedesktop.org Secret Storage Spec Server Side API (Fix #1403)
This plugin implements the Secret Storage specification version 0.2.
While running KeePassXC, it acts as a Secret Service server, registered
on DBus, so clients like seahorse, python-secretstorage, or other
implementations can connect and access the exposed database in KeePassXC.
Squashed commits:
- Initial code
- Add SessionAdaptor and fix build
- The skeletons for all dbus objects are in place
- Implement collection creation and deletion
- Emit collectionChanged signal
- Implement app-wise settings page
- Implement error message on GUI
- Implement settings
- Fix uuid to dbus path
- Implement app level settings
- Add freedesktop logo
- Implement database settings page
- Change database settings to a treeview
- Move all settings read/write to one place
- Rename SecretServiceOptionsPage to SettingsWidgetFdoSecrets
- Fix selected group can not be saved if the user hasn't click on the item
- Show selected group per database in app settings
- Disable editing of various readonly widgets
- Remove unused warning about non exposed database
- Fix method signature on dbus adaptors
- Fix type derived from DBusObject not recognized as QDBusContext
- Resolve a few TODOs around error handling
- Remove const when passing DBus exposed objects
- Move dismiss to PromptBase
- Implement per collection locking/unlocking
- Fix const correctness on Item::setSecret
- Implement SecretService::getSecrets
- Rework the signal connections around collections.
- Remove generateId from DBusObject
- Per spec, use encoded label as DBus object path for collections
- Fix some corner cases around collection name changes
- Implement alias
- Fix wrong alias dbus path
- Implement encryption per spec
- Cleanup SessionCipher
- Implement searchItems for SecretService
- Use Tools::uuidToHex
- Implement Item attributes and delete
- Implement createItem
- Always check if the database is unlocked before perform any operation
- Add missing ReadAlias/SetAlias on service
- Reorganize and fix OpenSession always returning empty output
- Overhaul error handling
- Make sure default alias is always present
- Remove collection aliases early in doDelete
- Handles all content types, fix setProperties not working
- Fix sometimes there is an extraneous leading zero when converting from MPI
- Fix session encryption negotiation
- Do not expose recycle bin
- Protect against the methods not called from DBus
- Also emit collectionChanged signal when lock state changes
- Show notification when entry secret is requested
- Add a README file
- Actually close session when client disconnects
- Gracefully return alternative label when collection is locked
- Reorganize, rename secretservice to fdosecrets
- Fix issues reported by clazy
- Unify UI strings and fix icon
- Implement a setting to skip confirmation when deleting entries from DBus
- Remove some unused debugging log
- Simply ignore errors when DBus context is not available. QtDBus won't set QDBusContext when deliver property get/set, and there is no way to get a QDBusMessage in property getter/setter.
- Simplify GcryptMPI using std::unique_ptr and add unit test
- Format code in fdosecrets
- Move DBusReturnImpl to details namespace
- Fix crash when locking a database: don't modify exposedGroup setting in customData when database is deleted
- Make sure Collection::searchItems works, whether it's locked or not
- Fix FdoSecrets::Collection becomes empty after a database reload
- Fix crash when looping while modifying the list
2019-03-25 23:07:18 -04:00
|
|
|
|
|
|
|
#ifdef WITH_XC_FDOSECRETS
|
|
|
|
auto fdoSS = new FdoSecretsPlugin(m_ui->tabWidget);
|
|
|
|
connect(fdoSS, &FdoSecretsPlugin::error, this, &MainWindow::showErrorMessage);
|
|
|
|
connect(fdoSS, &FdoSecretsPlugin::requestSwitchToDatabases, this, &MainWindow::switchToDatabases);
|
|
|
|
connect(fdoSS, &FdoSecretsPlugin::requestShowNotification, this, &MainWindow::displayDesktopNotification);
|
|
|
|
fdoSS->updateServiceState();
|
|
|
|
m_ui->settingsWidget->addSettingsPage(fdoSS);
|
|
|
|
#endif
|
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
setWindowIcon(filePath()->applicationIcon());
|
|
|
|
m_ui->globalMessageWidget->setHidden(true);
|
|
|
|
// clang-format off
|
2019-11-24 09:40:16 -05:00
|
|
|
connect(m_ui->globalMessageWidget, &MessageWidget::linkActivated, &MessageWidget::openHttpUrl);
|
|
|
|
connect(m_ui->globalMessageWidget, SIGNAL(showAnimationStarted()), m_ui->globalMessageWidgetContainer, SLOT(show()));
|
|
|
|
connect(m_ui->globalMessageWidget, SIGNAL(hideAnimationFinished()), m_ui->globalMessageWidgetContainer, SLOT(hide()));
|
2019-01-16 22:53:29 -05:00
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
m_clearHistoryAction = new QAction(tr("Clear history"), m_ui->menuFile);
|
|
|
|
m_lastDatabasesActions = new QActionGroup(m_ui->menuRecentDatabases);
|
|
|
|
connect(m_clearHistoryAction, SIGNAL(triggered()), this, SLOT(clearLastDatabases()));
|
|
|
|
connect(m_lastDatabasesActions, SIGNAL(triggered(QAction*)), this, SLOT(openRecentDatabase(QAction*)));
|
|
|
|
connect(m_ui->menuRecentDatabases, SIGNAL(aboutToShow()), this, SLOT(updateLastDatabasesMenu()));
|
|
|
|
|
|
|
|
m_copyAdditionalAttributeActions = new QActionGroup(m_ui->menuEntryCopyAttribute);
|
|
|
|
m_actionMultiplexer.connect(
|
|
|
|
m_copyAdditionalAttributeActions, SIGNAL(triggered(QAction*)), SLOT(copyAttribute(QAction*)));
|
|
|
|
connect(m_ui->menuEntryCopyAttribute, SIGNAL(aboutToShow()), this, SLOT(updateCopyAttributesMenu()));
|
|
|
|
|
|
|
|
Qt::Key globalAutoTypeKey = static_cast<Qt::Key>(config()->get("GlobalAutoTypeKey").toInt());
|
|
|
|
Qt::KeyboardModifiers globalAutoTypeModifiers =
|
|
|
|
static_cast<Qt::KeyboardModifiers>(config()->get("GlobalAutoTypeModifiers").toInt());
|
|
|
|
if (globalAutoTypeKey > 0 && globalAutoTypeModifiers > 0) {
|
|
|
|
autoType()->registerGlobalShortcut(globalAutoTypeKey, globalAutoTypeModifiers);
|
|
|
|
}
|
|
|
|
|
2020-01-05 21:00:25 -05:00
|
|
|
m_ui->toolbarSeparator->setVisible(false);
|
|
|
|
m_showToolbarSeparator = config()->get("GUI/ApplicationTheme").toString() != "classic";
|
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionEntryAutoType->setVisible(autoType()->isAvailable());
|
|
|
|
|
|
|
|
m_inactivityTimer = new InactivityTimer(this);
|
|
|
|
connect(m_inactivityTimer, SIGNAL(inactivityDetected()), this, SLOT(lockDatabasesAfterInactivity()));
|
2018-04-04 11:39:26 -04:00
|
|
|
#ifdef WITH_XC_TOUCHID
|
2019-01-16 22:53:29 -05:00
|
|
|
m_touchIDinactivityTimer = new InactivityTimer(this);
|
|
|
|
connect(m_touchIDinactivityTimer, SIGNAL(inactivityDetected()), this, SLOT(forgetTouchIDAfterInactivity()));
|
2018-04-04 11:39:26 -04:00
|
|
|
#endif
|
2019-01-16 22:53:29 -05:00
|
|
|
applySettingsChanges();
|
|
|
|
|
|
|
|
m_ui->actionDatabaseNew->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_N);
|
|
|
|
setShortcut(m_ui->actionDatabaseOpen, QKeySequence::Open, Qt::CTRL + Qt::Key_O);
|
|
|
|
setShortcut(m_ui->actionDatabaseSave, QKeySequence::Save, Qt::CTRL + Qt::Key_S);
|
|
|
|
setShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs, Qt::CTRL + Qt::SHIFT + Qt::Key_S);
|
|
|
|
setShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W);
|
|
|
|
m_ui->actionLockDatabases->setShortcut(Qt::CTRL + Qt::Key_L);
|
|
|
|
setShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q);
|
|
|
|
setShortcut(m_ui->actionEntryNew, QKeySequence::New, Qt::CTRL + Qt::Key_N);
|
|
|
|
m_ui->actionEntryEdit->setShortcut(Qt::CTRL + Qt::Key_E);
|
|
|
|
m_ui->actionEntryDelete->setShortcut(Qt::CTRL + Qt::Key_D);
|
|
|
|
m_ui->actionEntryDelete->setShortcut(Qt::Key_Delete);
|
|
|
|
m_ui->actionEntryClone->setShortcut(Qt::CTRL + Qt::Key_K);
|
|
|
|
m_ui->actionEntryTotp->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_T);
|
2019-07-07 15:29:11 -04:00
|
|
|
m_ui->actionEntryDownloadIcon->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_D);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionEntryCopyTotp->setShortcut(Qt::CTRL + Qt::Key_T);
|
|
|
|
m_ui->actionEntryCopyUsername->setShortcut(Qt::CTRL + Qt::Key_B);
|
|
|
|
m_ui->actionEntryCopyPassword->setShortcut(Qt::CTRL + Qt::Key_C);
|
|
|
|
m_ui->actionEntryAutoType->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
|
|
|
|
m_ui->actionEntryOpenUrl->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_U);
|
|
|
|
m_ui->actionEntryCopyURL->setShortcut(Qt::CTRL + Qt::Key_U);
|
2018-11-16 10:00:59 -05:00
|
|
|
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
2019-01-16 22:53:29 -05:00
|
|
|
// Qt 5.10 introduced a new "feature" to hide shortcuts in context menus
|
|
|
|
// Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them
|
|
|
|
m_ui->actionEntryNew->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryEdit->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryDelete->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryClone->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryTotp->setShortcutVisibleInContextMenu(true);
|
2019-07-07 15:29:11 -04:00
|
|
|
m_ui->actionEntryDownloadIcon->setShortcutVisibleInContextMenu(true);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionEntryCopyTotp->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryCopyUsername->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryCopyPassword->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryAutoType->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryOpenUrl->setShortcutVisibleInContextMenu(true);
|
|
|
|
m_ui->actionEntryCopyURL->setShortcutVisibleInContextMenu(true);
|
2018-11-16 10:00:59 -05:00
|
|
|
#endif
|
2012-05-15 12:16:04 -04:00
|
|
|
|
2019-06-11 03:37:23 -04:00
|
|
|
connect(m_ui->menuEntries, SIGNAL(aboutToShow()), SLOT(obtainContextFocusLock()));
|
2019-05-30 17:28:47 -04:00
|
|
|
connect(m_ui->menuEntries, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
|
2019-10-26 14:54:52 -04:00
|
|
|
connect(m_entryContextMenu, SIGNAL(aboutToShow()), SLOT(obtainContextFocusLock()));
|
|
|
|
connect(m_entryContextMenu, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
|
|
|
|
connect(m_entryNewContextMenu, SIGNAL(aboutToShow()), SLOT(obtainContextFocusLock()));
|
|
|
|
connect(m_entryNewContextMenu, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
|
2019-06-11 03:37:23 -04:00
|
|
|
connect(m_ui->menuGroups, SIGNAL(aboutToShow()), SLOT(obtainContextFocusLock()));
|
2019-05-30 17:28:47 -04:00
|
|
|
connect(m_ui->menuGroups, SIGNAL(aboutToHide()), SLOT(releaseContextFocusLock()));
|
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
// Control window state
|
2020-02-24 06:33:43 -05:00
|
|
|
new QShortcut(Qt::CTRL + Qt::Key_M, this, SLOT(minimizeOrHide()));
|
2019-01-16 22:53:29 -05:00
|
|
|
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_M, this, SLOT(hideWindow()));
|
|
|
|
// Control database tabs
|
|
|
|
new QShortcut(Qt::CTRL + Qt::Key_Tab, this, SLOT(selectNextDatabaseTab()));
|
2019-04-12 16:37:13 -04:00
|
|
|
new QShortcut(Qt::CTRL + Qt::Key_PageDown, this, SLOT(selectNextDatabaseTab()));
|
2019-01-16 22:53:29 -05:00
|
|
|
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab, this, SLOT(selectPreviousDatabaseTab()));
|
2019-04-12 16:37:13 -04:00
|
|
|
new QShortcut(Qt::CTRL + Qt::Key_PageUp, this, SLOT(selectPreviousDatabaseTab()));
|
2019-12-27 07:19:52 -05:00
|
|
|
|
2020-02-09 03:06:19 -05:00
|
|
|
// Tab selection by number, Windows uses Ctrl, macOS uses Command,
|
|
|
|
// and Linux uses Alt to emulate a browser-like experience
|
|
|
|
auto dbTabModifier = Qt::CTRL;
|
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
dbTabModifier = Qt::ALT;
|
|
|
|
#endif
|
|
|
|
auto shortcut = new QShortcut(dbTabModifier + Qt::Key_1, this);
|
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(0); });
|
|
|
|
shortcut = new QShortcut(dbTabModifier + Qt::Key_2, this);
|
2019-12-27 07:19:52 -05:00
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(1); });
|
2020-02-09 03:06:19 -05:00
|
|
|
shortcut = new QShortcut(dbTabModifier + Qt::Key_3, this);
|
2019-12-27 07:19:52 -05:00
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(2); });
|
2020-02-09 03:06:19 -05:00
|
|
|
shortcut = new QShortcut(dbTabModifier + Qt::Key_4, this);
|
2019-12-27 07:19:52 -05:00
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(3); });
|
2020-02-09 03:06:19 -05:00
|
|
|
shortcut = new QShortcut(dbTabModifier + Qt::Key_5, this);
|
2019-12-27 07:19:52 -05:00
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(4); });
|
2020-02-09 03:06:19 -05:00
|
|
|
shortcut = new QShortcut(dbTabModifier + Qt::Key_6, this);
|
2019-12-27 07:19:52 -05:00
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(5); });
|
2020-02-09 03:06:19 -05:00
|
|
|
shortcut = new QShortcut(dbTabModifier + Qt::Key_7, this);
|
2019-12-27 07:19:52 -05:00
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(6); });
|
2020-02-09 03:06:19 -05:00
|
|
|
shortcut = new QShortcut(dbTabModifier + Qt::Key_8, this);
|
2019-12-27 07:19:52 -05:00
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(7); });
|
2020-02-09 03:06:19 -05:00
|
|
|
shortcut = new QShortcut(dbTabModifier + Qt::Key_9, this);
|
|
|
|
connect(shortcut, &QShortcut::activated, [this]() { selectDatabaseTab(m_ui->tabWidget->count() - 1); });
|
2019-12-27 07:19:52 -05:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
// Toggle password and username visibility in entry view
|
|
|
|
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_C, this, SLOT(togglePasswordsHidden()));
|
|
|
|
new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_B, this, SLOT(toggleUsernamesHidden()));
|
|
|
|
|
|
|
|
m_ui->actionDatabaseNew->setIcon(filePath()->icon("actions", "document-new"));
|
|
|
|
m_ui->actionDatabaseOpen->setIcon(filePath()->icon("actions", "document-open"));
|
|
|
|
m_ui->actionDatabaseSave->setIcon(filePath()->icon("actions", "document-save"));
|
|
|
|
m_ui->actionDatabaseSaveAs->setIcon(filePath()->icon("actions", "document-save-as"));
|
|
|
|
m_ui->actionDatabaseClose->setIcon(filePath()->icon("actions", "document-close"));
|
2020-02-01 08:42:34 -05:00
|
|
|
m_ui->actionReports->setIcon(filePath()->icon("actions", "help-about"));
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionChangeDatabaseSettings->setIcon(filePath()->icon("actions", "document-edit"));
|
|
|
|
m_ui->actionChangeMasterKey->setIcon(filePath()->icon("actions", "database-change-key"));
|
Add system tray icon for "Toggle Window" (#3244)
* Fixes #3145
The system tray menu used to have three items (toggle window,
lock database, quit) of which only two had an icon, which
looked strange and unintended. This commit adds an icon for
the "Toggle window" menu item.
* Use a padlock icon for the "Lock DB" tray menu item
Originally, icon `document-encryt.png` was used, however theming seems
to be getting in the way by sometimes displaying a plain "document"
icon instead. Copied the icon files to `database-lock.png` and used
that one for the "lock database" tray menu item instead.
* In Database Settings, use the "security-high" icon for "Security"
Previously, the "document-encrypt" icon was used, which should be
something like a padlock but which, due to theming, somethings
fell back to a generic document icon (page of paper).
The "document-encrypt" icon is no longer used and was removed.
2019-06-09 21:22:19 -04:00
|
|
|
m_ui->actionLockDatabases->setIcon(filePath()->icon("actions", "database-lock"));
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionQuit->setIcon(filePath()->icon("actions", "application-exit"));
|
2019-12-26 15:32:12 -05:00
|
|
|
m_ui->actionDatabaseMerge->setIcon(filePath()->icon("actions", "database-merge"));
|
2019-01-16 22:53:29 -05:00
|
|
|
|
|
|
|
m_ui->actionEntryNew->setIcon(filePath()->icon("actions", "entry-new"));
|
|
|
|
m_ui->actionEntryClone->setIcon(filePath()->icon("actions", "entry-clone"));
|
|
|
|
m_ui->actionEntryEdit->setIcon(filePath()->icon("actions", "entry-edit"));
|
|
|
|
m_ui->actionEntryDelete->setIcon(filePath()->icon("actions", "entry-delete"));
|
|
|
|
m_ui->actionEntryAutoType->setIcon(filePath()->icon("actions", "auto-type"));
|
|
|
|
m_ui->actionEntryCopyUsername->setIcon(filePath()->icon("actions", "username-copy"));
|
|
|
|
m_ui->actionEntryCopyPassword->setIcon(filePath()->icon("actions", "password-copy"));
|
|
|
|
m_ui->actionEntryCopyURL->setIcon(filePath()->icon("actions", "url-copy"));
|
2019-07-07 15:29:11 -04:00
|
|
|
m_ui->actionEntryDownloadIcon->setIcon(filePath()->icon("actions", "favicon-download"));
|
2019-12-26 15:32:12 -05:00
|
|
|
m_ui->actionGroupSortAsc->setIcon(filePath()->icon("actions", "sort-alphabetical-ascending"));
|
|
|
|
m_ui->actionGroupSortDesc->setIcon(filePath()->icon("actions", "sort-alphabetical-descending"));
|
2019-01-16 22:53:29 -05:00
|
|
|
|
|
|
|
m_ui->actionGroupNew->setIcon(filePath()->icon("actions", "group-new"));
|
|
|
|
m_ui->actionGroupEdit->setIcon(filePath()->icon("actions", "group-edit"));
|
|
|
|
m_ui->actionGroupDelete->setIcon(filePath()->icon("actions", "group-delete"));
|
|
|
|
m_ui->actionGroupEmptyRecycleBin->setIcon(filePath()->icon("actions", "group-empty-trash"));
|
2019-12-26 15:32:12 -05:00
|
|
|
m_ui->actionEntryOpenUrl->setIcon(filePath()->icon("actions", "web"));
|
2019-07-07 15:29:11 -04:00
|
|
|
m_ui->actionGroupDownloadFavicons->setIcon(filePath()->icon("actions", "favicon-download"));
|
2019-01-16 22:53:29 -05:00
|
|
|
|
|
|
|
m_ui->actionSettings->setIcon(filePath()->icon("actions", "configure"));
|
|
|
|
m_ui->actionPasswordGenerator->setIcon(filePath()->icon("actions", "password-generator"));
|
|
|
|
|
|
|
|
m_ui->actionAbout->setIcon(filePath()->icon("actions", "help-about"));
|
2019-12-26 15:32:12 -05:00
|
|
|
m_ui->actionDonate->setIcon(filePath()->icon("actions", "donate"));
|
|
|
|
m_ui->actionBugReport->setIcon(filePath()->icon("actions", "bugreport"));
|
|
|
|
m_ui->actionGettingStarted->setIcon(filePath()->icon("actions", "getting-started"));
|
|
|
|
m_ui->actionUserGuide->setIcon(filePath()->icon("actions", "user-guide"));
|
|
|
|
m_ui->actionOnlineHelp->setIcon(filePath()->icon("actions", "system-help"));
|
|
|
|
m_ui->actionKeyboardShortcuts->setIcon(filePath()->icon("actions", "keyboard-shortcuts"));
|
2019-01-30 09:11:50 -05:00
|
|
|
m_ui->actionCheckForUpdates->setIcon(filePath()->icon("actions", "system-software-update"));
|
2019-01-16 22:53:29 -05:00
|
|
|
|
|
|
|
m_actionMultiplexer.connect(
|
|
|
|
SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(setMenuActionState(DatabaseWidget::Mode)));
|
|
|
|
m_actionMultiplexer.connect(SIGNAL(groupChanged()), this, SLOT(setMenuActionState()));
|
|
|
|
m_actionMultiplexer.connect(SIGNAL(entrySelectionChanged()), this, SLOT(setMenuActionState()));
|
|
|
|
m_actionMultiplexer.connect(SIGNAL(groupContextMenuRequested(QPoint)), this, SLOT(showGroupContextMenu(QPoint)));
|
|
|
|
m_actionMultiplexer.connect(SIGNAL(entryContextMenuRequested(QPoint)), this, SLOT(showEntryContextMenu(QPoint)));
|
|
|
|
|
|
|
|
// Notify search when the active database changes or gets locked
|
|
|
|
connect(m_ui->tabWidget,
|
|
|
|
SIGNAL(activateDatabaseChanged(DatabaseWidget*)),
|
2019-06-11 03:37:23 -04:00
|
|
|
m_searchWidget,
|
2019-01-16 22:53:29 -05:00
|
|
|
SLOT(databaseChanged(DatabaseWidget*)));
|
2019-06-11 03:37:23 -04:00
|
|
|
connect(m_ui->tabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), m_searchWidget, SLOT(databaseChanged()));
|
2019-01-16 22:53:29 -05:00
|
|
|
|
|
|
|
connect(m_ui->tabWidget, SIGNAL(tabNameChanged()), SLOT(updateWindowTitle()));
|
2020-01-05 21:00:25 -05:00
|
|
|
connect(m_ui->tabWidget, SIGNAL(tabVisibilityChanged(bool)), SLOT(adjustToTabVisibilityChange(bool)));
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle()));
|
|
|
|
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(databaseTabChanged(int)));
|
|
|
|
connect(m_ui->tabWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState()));
|
|
|
|
connect(m_ui->tabWidget, SIGNAL(databaseLocked(DatabaseWidget*)), SLOT(databaseStatusChanged(DatabaseWidget*)));
|
|
|
|
connect(m_ui->tabWidget, SIGNAL(databaseUnlocked(DatabaseWidget*)), SLOT(databaseStatusChanged(DatabaseWidget*)));
|
|
|
|
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState()));
|
|
|
|
connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle()));
|
|
|
|
connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(applySettingsChanges()));
|
2019-08-30 20:18:41 -04:00
|
|
|
connect(m_ui->settingsWidget, SIGNAL(settingsReset()), SLOT(applySettingsChanges()));
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(switchToDatabases()));
|
|
|
|
connect(m_ui->settingsWidget, SIGNAL(rejected()), SLOT(switchToDatabases()));
|
|
|
|
|
|
|
|
connect(m_ui->actionDatabaseNew, SIGNAL(triggered()), m_ui->tabWidget, SLOT(newDatabase()));
|
|
|
|
connect(m_ui->actionDatabaseOpen, SIGNAL(triggered()), m_ui->tabWidget, SLOT(openDatabase()));
|
|
|
|
connect(m_ui->actionDatabaseSave, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabase()));
|
|
|
|
connect(m_ui->actionDatabaseSaveAs, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabaseAs()));
|
|
|
|
connect(m_ui->actionDatabaseClose, SIGNAL(triggered()), m_ui->tabWidget, SLOT(closeCurrentDatabaseTab()));
|
|
|
|
connect(m_ui->actionDatabaseMerge, SIGNAL(triggered()), m_ui->tabWidget, SLOT(mergeDatabase()));
|
|
|
|
connect(m_ui->actionChangeMasterKey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(changeMasterKey()));
|
2020-02-01 08:42:34 -05:00
|
|
|
connect(m_ui->actionReports, SIGNAL(triggered()), m_ui->tabWidget, SLOT(changeReports()));
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(m_ui->actionChangeDatabaseSettings, SIGNAL(triggered()), m_ui->tabWidget, SLOT(changeDatabaseSettings()));
|
|
|
|
connect(m_ui->actionImportCsv, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importCsv()));
|
|
|
|
connect(m_ui->actionImportKeePass1, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importKeePass1Database()));
|
2019-05-19 17:49:48 -04:00
|
|
|
connect(m_ui->actionImportOpVault, SIGNAL(triggered()), m_ui->tabWidget, SLOT(importOpVaultDatabase()));
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(m_ui->actionExportCsv, SIGNAL(triggered()), m_ui->tabWidget, SLOT(exportToCsv()));
|
2019-06-30 11:11:15 -04:00
|
|
|
connect(m_ui->actionExportHtml, SIGNAL(triggered()), m_ui->tabWidget, SLOT(exportToHtml()));
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(m_ui->actionLockDatabases, SIGNAL(triggered()), m_ui->tabWidget, SLOT(lockDatabases()));
|
|
|
|
connect(m_ui->actionQuit, SIGNAL(triggered()), SLOT(appExit()));
|
|
|
|
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryNew, SIGNAL(triggered()), SLOT(createEntry()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryClone, SIGNAL(triggered()), SLOT(cloneEntry()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryEdit, SIGNAL(triggered()), SLOT(switchToEntryEdit()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryDelete, SIGNAL(triggered()), SLOT(deleteSelectedEntries()));
|
|
|
|
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryTotp, SIGNAL(triggered()), SLOT(showTotp()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntrySetupTotp, SIGNAL(triggered()), SLOT(setupTotp()));
|
|
|
|
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyTotp, SIGNAL(triggered()), SLOT(copyTotp()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryTotpQRCode, SIGNAL(triggered()), SLOT(showTotpKeyQrCode()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyTitle, SIGNAL(triggered()), SLOT(copyTitle()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyUsername, SIGNAL(triggered()), SLOT(copyUsername()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyPassword, SIGNAL(triggered()), SLOT(copyPassword()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyURL, SIGNAL(triggered()), SLOT(copyURL()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryCopyNotes, SIGNAL(triggered()), SLOT(copyNotes()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryAutoType, SIGNAL(triggered()), SLOT(performAutoType()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryOpenUrl, SIGNAL(triggered()), SLOT(openUrl()));
|
2019-07-07 15:29:11 -04:00
|
|
|
m_actionMultiplexer.connect(m_ui->actionEntryDownloadIcon, SIGNAL(triggered()), SLOT(downloadSelectedFavicons()));
|
2019-01-16 22:53:29 -05:00
|
|
|
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionGroupNew, SIGNAL(triggered()), SLOT(createGroup()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionGroupEdit, SIGNAL(triggered()), SLOT(switchToGroupEdit()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionGroupDelete, SIGNAL(triggered()), SLOT(deleteGroup()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionGroupEmptyRecycleBin, SIGNAL(triggered()), SLOT(emptyRecycleBin()));
|
2019-06-18 16:58:47 -04:00
|
|
|
m_actionMultiplexer.connect(m_ui->actionGroupSortAsc, SIGNAL(triggered()), SLOT(sortGroupsAsc()));
|
|
|
|
m_actionMultiplexer.connect(m_ui->actionGroupSortDesc, SIGNAL(triggered()), SLOT(sortGroupsDesc()));
|
2019-07-07 15:29:11 -04:00
|
|
|
m_actionMultiplexer.connect(m_ui->actionGroupDownloadFavicons, SIGNAL(triggered()), SLOT(downloadAllFavicons()));
|
2019-01-16 22:53:29 -05:00
|
|
|
|
|
|
|
connect(m_ui->actionSettings, SIGNAL(toggled(bool)), SLOT(switchToSettings(bool)));
|
|
|
|
connect(m_ui->actionPasswordGenerator, SIGNAL(toggled(bool)), SLOT(switchToPasswordGen(bool)));
|
2020-03-08 22:22:01 -04:00
|
|
|
connect(m_ui->passwordGeneratorWidget, SIGNAL(closePasswordGenerator()), SLOT(closePasswordGen()));
|
2019-01-16 22:53:29 -05:00
|
|
|
|
|
|
|
connect(m_ui->welcomeWidget, SIGNAL(newDatabase()), SLOT(switchToNewDatabase()));
|
|
|
|
connect(m_ui->welcomeWidget, SIGNAL(openDatabase()), SLOT(switchToOpenDatabase()));
|
|
|
|
connect(m_ui->welcomeWidget, SIGNAL(openDatabaseFile(QString)), SLOT(switchToDatabaseFile(QString)));
|
|
|
|
connect(m_ui->welcomeWidget, SIGNAL(importKeePass1Database()), SLOT(switchToKeePass1Database()));
|
2019-05-19 17:49:48 -04:00
|
|
|
connect(m_ui->welcomeWidget, SIGNAL(importOpVaultDatabase()), SLOT(switchToOpVaultDatabase()));
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(m_ui->welcomeWidget, SIGNAL(importCsv()), SLOT(switchToCsvImport()));
|
|
|
|
|
|
|
|
connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog()));
|
|
|
|
connect(m_ui->actionDonate, SIGNAL(triggered()), SLOT(openDonateUrl()));
|
|
|
|
connect(m_ui->actionBugReport, SIGNAL(triggered()), SLOT(openBugReportUrl()));
|
2019-06-18 20:46:46 -04:00
|
|
|
connect(m_ui->actionGettingStarted, SIGNAL(triggered()), SLOT(openGettingStartedGuide()));
|
|
|
|
connect(m_ui->actionUserGuide, SIGNAL(triggered()), SLOT(openUserGuide()));
|
|
|
|
connect(m_ui->actionOnlineHelp, SIGNAL(triggered()), SLOT(openOnlineHelp()));
|
2019-08-30 22:55:02 -04:00
|
|
|
connect(m_ui->actionKeyboardShortcuts, SIGNAL(triggered()), SLOT(openKeyboardShortcuts()));
|
2019-06-18 20:46:46 -04:00
|
|
|
|
2018-10-26 09:19:04 -04:00
|
|
|
#ifdef Q_OS_MACOS
|
2019-01-16 22:53:29 -05:00
|
|
|
setUnifiedTitleAndToolBarOnMac(true);
|
2019-04-15 14:34:16 -04:00
|
|
|
if (macUtils()->isDarkMode()) {
|
|
|
|
setStyleSheet("QToolButton {color:white;}");
|
|
|
|
}
|
2017-03-01 16:32:14 -05:00
|
|
|
#endif
|
2019-01-30 09:11:50 -05:00
|
|
|
|
2019-04-09 21:06:13 -04:00
|
|
|
#ifdef WITH_XC_UPDATECHECK
|
2019-01-30 09:11:50 -05:00
|
|
|
connect(m_ui->actionCheckForUpdates, SIGNAL(triggered()), SLOT(showUpdateCheckDialog()));
|
2019-03-19 14:48:33 -04:00
|
|
|
connect(UpdateChecker::instance(),
|
|
|
|
SIGNAL(updateCheckFinished(bool, QString, bool)),
|
|
|
|
SLOT(hasUpdateAvailable(bool, QString, bool)));
|
2019-04-09 21:06:13 -04:00
|
|
|
QTimer::singleShot(500, this, SLOT(showUpdateCheckStartup()));
|
2019-01-30 09:11:50 -05:00
|
|
|
#else
|
|
|
|
m_ui->actionCheckForUpdates->setVisible(false);
|
|
|
|
#endif
|
|
|
|
|
2019-07-07 15:29:11 -04:00
|
|
|
#ifndef WITH_XC_NETWORKING
|
|
|
|
m_ui->actionGroupDownloadFavicons->setVisible(false);
|
|
|
|
m_ui->actionEntryDownloadIcon->setVisible(false);
|
|
|
|
#endif
|
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
// clang-format off
|
2019-11-24 09:40:16 -05:00
|
|
|
connect(m_ui->tabWidget, SIGNAL(messageGlobal(QString,MessageWidget::MessageType)),
|
|
|
|
SLOT(displayGlobalMessage(QString,MessageWidget::MessageType)));
|
2019-01-16 22:53:29 -05:00
|
|
|
// clang-format on
|
2018-10-31 23:27:38 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(m_ui->tabWidget, SIGNAL(messageDismissGlobal()), this, SLOT(hideGlobalMessage()));
|
2015-01-21 11:19:05 -05:00
|
|
|
|
2019-03-24 09:26:34 -04:00
|
|
|
#ifndef Q_OS_HAIKU
|
2019-01-16 22:53:29 -05:00
|
|
|
m_screenLockListener = new ScreenLockListener(this);
|
|
|
|
connect(m_screenLockListener, SIGNAL(screenLocked()), SLOT(handleScreenLock()));
|
2019-03-24 09:26:34 -04:00
|
|
|
#endif
|
2016-12-23 19:04:43 -05:00
|
|
|
|
2019-05-03 14:40:45 -04:00
|
|
|
// Tray Icon setup
|
|
|
|
connect(Application::instance(), SIGNAL(focusWindowChanged(QWindow*)), SLOT(focusWindowChanged(QWindow*)));
|
|
|
|
m_trayIconTriggerReason = QSystemTrayIcon::Unknown;
|
|
|
|
m_trayIconTriggerTimer.setSingleShot(true);
|
|
|
|
connect(&m_trayIconTriggerTimer, SIGNAL(timeout()), SLOT(processTrayIconTrigger()));
|
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
updateTrayIcon();
|
2017-03-13 22:12:00 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
if (config()->hasAccessError()) {
|
|
|
|
m_ui->globalMessageWidget->showMessage(tr("Access error for config file %1").arg(config()->getFileName()),
|
|
|
|
MessageWidget::Error);
|
|
|
|
}
|
2017-11-27 22:46:03 -05:00
|
|
|
|
2019-01-30 15:17:50 -05:00
|
|
|
#if defined(KEEPASSXC_BUILD_TYPE_SNAPSHOT)
|
2019-01-16 22:53:29 -05:00
|
|
|
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."),
|
|
|
|
MessageWidget::Warning,
|
|
|
|
-1);
|
2019-01-30 15:17:50 -05:00
|
|
|
#elif defined(KEEPASSXC_BUILD_TYPE_PRE_RELEASE)
|
|
|
|
m_ui->globalMessageWidget->showMessage(
|
|
|
|
tr("NOTE: You are using a pre-release version of KeePassXC!\n"
|
|
|
|
"Expect some bugs and minor issues, this version is not meant for production use."),
|
|
|
|
MessageWidget::Information,
|
|
|
|
15000);
|
2018-08-18 10:33:49 -04:00
|
|
|
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) && QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
|
2019-01-16 22:53:29 -05:00
|
|
|
if (!config()->get("QtErrorMessageShown", false).toBool()) {
|
|
|
|
m_ui->globalMessageWidget->showMessage(
|
|
|
|
tr("WARNING: Your Qt version may cause KeePassXC to crash with an On-Screen Keyboard!\n"
|
|
|
|
"We recommend you use the AppImage available on our downloads page."),
|
|
|
|
MessageWidget::Warning,
|
|
|
|
-1);
|
|
|
|
config()->set("QtErrorMessageShown", true);
|
|
|
|
}
|
2017-11-27 22:46:03 -05:00
|
|
|
#endif
|
2011-12-26 12:55:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow::~MainWindow()
|
|
|
|
{
|
2010-09-19 10:59:32 -04:00
|
|
|
}
|
2011-11-16 12:47:17 -05:00
|
|
|
|
2018-03-03 10:49:00 -05:00
|
|
|
void MainWindow::showErrorMessage(const QString& message)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->globalMessageWidget->showMessage(message, MessageWidget::Error);
|
2018-03-03 10:49:00 -05:00
|
|
|
}
|
|
|
|
|
2015-06-18 09:23:41 -04:00
|
|
|
void MainWindow::appExit()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_appExitCalled = true;
|
|
|
|
close();
|
2015-06-18 09:23:41 -04:00
|
|
|
}
|
|
|
|
|
2012-08-01 04:40:18 -04:00
|
|
|
void MainWindow::updateLastDatabasesMenu()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->menuRecentDatabases->clear();
|
|
|
|
|
|
|
|
const QStringList lastDatabases = config()->get("LastDatabases", QVariant()).toStringList();
|
|
|
|
for (const QString& database : lastDatabases) {
|
|
|
|
QAction* action = m_ui->menuRecentDatabases->addAction(database);
|
|
|
|
action->setData(database);
|
|
|
|
m_lastDatabasesActions->addAction(action);
|
|
|
|
}
|
|
|
|
m_ui->menuRecentDatabases->addSeparator();
|
|
|
|
m_ui->menuRecentDatabases->addAction(m_clearHistoryAction);
|
2012-07-23 15:12:19 -04:00
|
|
|
}
|
|
|
|
|
2012-10-23 18:15:23 -04:00
|
|
|
void MainWindow::updateCopyAttributesMenu()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget();
|
|
|
|
if (!dbWidget) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dbWidget->numberOfSelectedEntries() != 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QAction*> actions = m_ui->menuEntryCopyAttribute->actions();
|
|
|
|
for (int i = m_countDefaultAttributes; i < actions.size(); i++) {
|
|
|
|
delete actions[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
const QStringList customEntryAttributes = dbWidget->customEntryAttributes();
|
|
|
|
for (const QString& key : customEntryAttributes) {
|
|
|
|
QAction* action = m_ui->menuEntryCopyAttribute->addAction(key);
|
|
|
|
action->setData(QVariant(key));
|
|
|
|
m_copyAdditionalAttributeActions->addAction(action);
|
|
|
|
}
|
2012-10-23 18:15:23 -04:00
|
|
|
}
|
|
|
|
|
2012-07-23 15:12:19 -04:00
|
|
|
void MainWindow::openRecentDatabase(QAction* action)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
openDatabase(action->data().toString());
|
2012-07-23 15:12:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::clearLastDatabases()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
config()->set("LastDatabases", QVariant());
|
|
|
|
bool inWelcomeWidget = (m_ui->stackedWidget->currentIndex() == 2);
|
2017-03-26 16:55:56 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
if (inWelcomeWidget) {
|
|
|
|
m_ui->welcomeWidget->refreshLastDatabases();
|
|
|
|
}
|
2012-07-23 15:12:19 -04:00
|
|
|
}
|
|
|
|
|
2019-03-21 17:36:57 -04:00
|
|
|
void MainWindow::openDatabase(const QString& filePath, const QString& password, const QString& keyfile)
|
2012-04-24 05:47:16 -04:00
|
|
|
{
|
2019-03-21 17:36:57 -04:00
|
|
|
m_ui->tabWidget->addDatabaseTab(filePath, false, password, keyfile);
|
2012-04-24 05:47:16 -04:00
|
|
|
}
|
|
|
|
|
2012-04-24 19:32:05 -04:00
|
|
|
void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
2011-11-16 12:47:17 -05:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
int currentIndex = m_ui->stackedWidget->currentIndex();
|
|
|
|
|
|
|
|
bool inDatabaseTabWidget = (currentIndex == DatabaseTabScreen);
|
|
|
|
bool inWelcomeWidget = (currentIndex == WelcomeScreen);
|
|
|
|
bool inDatabaseTabWidgetOrWelcomeWidget = inDatabaseTabWidget || inWelcomeWidget;
|
|
|
|
|
|
|
|
m_ui->actionDatabaseMerge->setEnabled(inDatabaseTabWidget);
|
|
|
|
m_ui->actionDatabaseNew->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
|
|
|
|
m_ui->actionDatabaseOpen->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
|
|
|
|
m_ui->menuRecentDatabases->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
|
|
|
|
m_ui->menuImport->setEnabled(inDatabaseTabWidgetOrWelcomeWidget);
|
|
|
|
m_ui->actionLockDatabases->setEnabled(m_ui->tabWidget->hasLockableDatabases());
|
|
|
|
|
2020-01-05 21:00:25 -05:00
|
|
|
if (m_showToolbarSeparator) {
|
|
|
|
m_ui->toolbarSeparator->setVisible(
|
|
|
|
(!inWelcomeWidget && inDatabaseTabWidget && !m_ui->tabWidget->tabBar()->isVisible())
|
|
|
|
|| currentIndex == SettingsScreen);
|
|
|
|
}
|
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
if (inDatabaseTabWidget && m_ui->tabWidget->currentIndex() != -1) {
|
|
|
|
DatabaseWidget* dbWidget = m_ui->tabWidget->currentDatabaseWidget();
|
|
|
|
Q_ASSERT(dbWidget);
|
|
|
|
|
|
|
|
if (mode == DatabaseWidget::Mode::None) {
|
|
|
|
mode = dbWidget->currentMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case DatabaseWidget::Mode::ViewMode: {
|
2019-11-17 15:39:16 -05:00
|
|
|
bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1;
|
|
|
|
bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0;
|
2019-01-16 22:53:29 -05:00
|
|
|
bool groupSelected = dbWidget->isGroupSelected();
|
2019-06-18 16:58:47 -04:00
|
|
|
bool currentGroupHasChildren = dbWidget->currentGroup()->hasChildren();
|
2019-07-07 15:29:11 -04:00
|
|
|
bool currentGroupHasEntries = !dbWidget->currentGroup()->entries().isEmpty();
|
2019-01-16 22:53:29 -05:00
|
|
|
bool recycleBinSelected = dbWidget->isRecycleBinSelected();
|
|
|
|
|
|
|
|
m_ui->actionEntryNew->setEnabled(true);
|
|
|
|
m_ui->actionEntryClone->setEnabled(singleEntrySelected);
|
|
|
|
m_ui->actionEntryEdit->setEnabled(singleEntrySelected);
|
|
|
|
m_ui->actionEntryDelete->setEnabled(entriesSelected);
|
|
|
|
m_ui->actionEntryCopyTitle->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTitle());
|
|
|
|
m_ui->actionEntryCopyUsername->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUsername());
|
2019-09-07 09:22:59 -04:00
|
|
|
// NOTE: Copy password is enabled even if the selected entry's password is blank to prevent Ctrl+C
|
|
|
|
// from copying information from the currently selected cell in the entry view table.
|
|
|
|
m_ui->actionEntryCopyPassword->setEnabled(singleEntrySelected);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionEntryCopyURL->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl());
|
|
|
|
m_ui->actionEntryCopyNotes->setEnabled(singleEntrySelected && dbWidget->currentEntryHasNotes());
|
|
|
|
m_ui->menuEntryCopyAttribute->setEnabled(singleEntrySelected);
|
|
|
|
m_ui->menuEntryTotp->setEnabled(singleEntrySelected);
|
|
|
|
m_ui->actionEntryAutoType->setEnabled(singleEntrySelected);
|
|
|
|
m_ui->actionEntryOpenUrl->setEnabled(singleEntrySelected && dbWidget->currentEntryHasUrl());
|
|
|
|
m_ui->actionEntryTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
|
|
|
|
m_ui->actionEntryCopyTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
|
|
|
|
m_ui->actionEntrySetupTotp->setEnabled(singleEntrySelected);
|
|
|
|
m_ui->actionEntryTotpQRCode->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
|
2019-07-07 15:29:11 -04:00
|
|
|
m_ui->actionEntryDownloadIcon->setEnabled((entriesSelected && !singleEntrySelected)
|
|
|
|
|| (singleEntrySelected && dbWidget->currentEntryHasUrl()));
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionGroupNew->setEnabled(groupSelected);
|
|
|
|
m_ui->actionGroupEdit->setEnabled(groupSelected);
|
|
|
|
m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
|
2019-06-18 16:58:47 -04:00
|
|
|
m_ui->actionGroupSortAsc->setEnabled(groupSelected && currentGroupHasChildren);
|
|
|
|
m_ui->actionGroupSortDesc->setEnabled(groupSelected && currentGroupHasChildren);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionGroupEmptyRecycleBin->setVisible(recycleBinSelected);
|
|
|
|
m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected);
|
2019-07-07 15:29:11 -04:00
|
|
|
m_ui->actionGroupDownloadFavicons->setVisible(!recycleBinSelected);
|
|
|
|
m_ui->actionGroupDownloadFavicons->setEnabled(groupSelected && currentGroupHasEntries
|
|
|
|
&& !recycleBinSelected);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionChangeMasterKey->setEnabled(true);
|
2020-02-01 08:42:34 -05:00
|
|
|
m_ui->actionReports->setEnabled(true);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionChangeDatabaseSettings->setEnabled(true);
|
|
|
|
m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave());
|
|
|
|
m_ui->actionDatabaseSaveAs->setEnabled(true);
|
2019-06-30 11:11:15 -04:00
|
|
|
m_ui->menuExport->setEnabled(true);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionExportCsv->setEnabled(true);
|
2019-06-30 11:11:15 -04:00
|
|
|
m_ui->actionExportHtml->setEnabled(true);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionDatabaseMerge->setEnabled(m_ui->tabWidget->currentIndex() != -1);
|
|
|
|
|
|
|
|
m_searchWidgetAction->setEnabled(true);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DatabaseWidget::Mode::EditMode:
|
|
|
|
case DatabaseWidget::Mode::ImportMode:
|
|
|
|
case DatabaseWidget::Mode::LockedMode: {
|
2019-10-19 10:07:09 -04:00
|
|
|
// Enable select actions when editing an entry
|
2019-10-20 18:28:58 -04:00
|
|
|
bool editEntryActive = dbWidget->isEntryEditActive();
|
|
|
|
const auto editEntryActionsMask = QList<QAction*>({m_ui->actionEntryCopyUsername,
|
|
|
|
m_ui->actionEntryCopyPassword,
|
|
|
|
m_ui->actionEntryCopyURL,
|
|
|
|
m_ui->actionEntryOpenUrl,
|
|
|
|
m_ui->actionEntryAutoType,
|
|
|
|
m_ui->actionEntryDownloadIcon,
|
|
|
|
m_ui->actionEntryCopyNotes,
|
|
|
|
m_ui->actionEntryCopyTitle,
|
|
|
|
m_ui->menuEntryCopyAttribute->menuAction(),
|
|
|
|
m_ui->menuEntryTotp->menuAction(),
|
|
|
|
m_ui->actionEntrySetupTotp});
|
|
|
|
|
|
|
|
auto entryActions = m_ui->menuEntries->actions();
|
|
|
|
entryActions << m_ui->menuEntryCopyAttribute->actions();
|
|
|
|
entryActions << m_ui->menuEntryTotp->actions();
|
|
|
|
for (auto action : entryActions) {
|
|
|
|
bool enabled = editEntryActive && editEntryActionsMask.contains(action);
|
|
|
|
if (action->menu()) {
|
|
|
|
action->menu()->setEnabled(enabled);
|
|
|
|
}
|
|
|
|
action->setEnabled(enabled);
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
|
|
|
|
2019-10-20 18:28:58 -04:00
|
|
|
const auto groupActions = m_ui->menuGroups->actions();
|
|
|
|
for (auto action : groupActions) {
|
2019-01-16 22:53:29 -05:00
|
|
|
action->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ui->actionChangeMasterKey->setEnabled(false);
|
2020-02-01 08:42:34 -05:00
|
|
|
m_ui->actionReports->setEnabled(false);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionChangeDatabaseSettings->setEnabled(false);
|
|
|
|
m_ui->actionDatabaseSave->setEnabled(false);
|
|
|
|
m_ui->actionDatabaseSaveAs->setEnabled(false);
|
2019-06-30 11:11:15 -04:00
|
|
|
m_ui->menuExport->setEnabled(false);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionExportCsv->setEnabled(false);
|
2019-06-30 11:11:15 -04:00
|
|
|
m_ui->actionExportHtml->setEnabled(false);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionDatabaseMerge->setEnabled(false);
|
|
|
|
|
|
|
|
m_searchWidgetAction->setEnabled(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
|
|
|
m_ui->actionDatabaseClose->setEnabled(true);
|
|
|
|
} else {
|
2019-10-20 18:28:58 -04:00
|
|
|
const auto entryActions = m_ui->menuEntries->actions();
|
|
|
|
for (auto action : entryActions) {
|
2019-01-16 22:53:29 -05:00
|
|
|
action->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
2019-10-20 18:28:58 -04:00
|
|
|
const auto groupActions = m_ui->menuGroups->actions();
|
|
|
|
for (auto action : groupActions) {
|
2019-01-16 22:53:29 -05:00
|
|
|
action->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ui->actionChangeMasterKey->setEnabled(false);
|
2020-02-01 08:42:34 -05:00
|
|
|
m_ui->actionReports->setEnabled(false);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionChangeDatabaseSettings->setEnabled(false);
|
|
|
|
m_ui->actionDatabaseSave->setEnabled(false);
|
|
|
|
m_ui->actionDatabaseSaveAs->setEnabled(false);
|
|
|
|
m_ui->actionDatabaseClose->setEnabled(false);
|
2019-06-30 11:11:15 -04:00
|
|
|
m_ui->menuExport->setEnabled(false);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionExportCsv->setEnabled(false);
|
2019-06-30 11:11:15 -04:00
|
|
|
m_ui->actionExportHtml->setEnabled(false);
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->actionDatabaseMerge->setEnabled(false);
|
|
|
|
|
|
|
|
m_searchWidgetAction->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((currentIndex == PasswordGeneratorScreen) != m_ui->actionPasswordGenerator->isChecked()) {
|
|
|
|
bool blocked = m_ui->actionPasswordGenerator->blockSignals(true);
|
|
|
|
m_ui->actionPasswordGenerator->toggle();
|
|
|
|
m_ui->actionPasswordGenerator->blockSignals(blocked);
|
|
|
|
} else if ((currentIndex == SettingsScreen) != m_ui->actionSettings->isChecked()) {
|
|
|
|
bool blocked = m_ui->actionSettings->blockSignals(true);
|
|
|
|
m_ui->actionSettings->toggle();
|
|
|
|
m_ui->actionSettings->blockSignals(blocked);
|
|
|
|
}
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
2012-04-11 14:35:52 -04:00
|
|
|
|
2020-01-05 21:00:25 -05:00
|
|
|
void MainWindow::adjustToTabVisibilityChange(bool tabsVisible)
|
|
|
|
{
|
|
|
|
if (m_showToolbarSeparator) {
|
|
|
|
m_ui->toolbarSeparator->setVisible(!tabsVisible && m_ui->stackedWidget->currentIndex() == DatabaseTabScreen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-21 16:02:12 -04:00
|
|
|
void MainWindow::updateWindowTitle()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
QString customWindowTitlePart;
|
|
|
|
int stackedWidgetIndex = m_ui->stackedWidget->currentIndex();
|
|
|
|
int tabWidgetIndex = m_ui->tabWidget->currentIndex();
|
|
|
|
bool isModified = m_ui->tabWidget->isModified(tabWidgetIndex);
|
|
|
|
|
|
|
|
if (stackedWidgetIndex == DatabaseTabScreen && tabWidgetIndex != -1) {
|
|
|
|
customWindowTitlePart = m_ui->tabWidget->tabName(tabWidgetIndex);
|
|
|
|
if (isModified) {
|
|
|
|
// remove asterisk '*' from title
|
|
|
|
customWindowTitlePart.remove(customWindowTitlePart.size() - 1, 1);
|
|
|
|
}
|
|
|
|
m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave(tabWidgetIndex));
|
|
|
|
} else if (stackedWidgetIndex == 1) {
|
|
|
|
customWindowTitlePart = tr("Settings");
|
|
|
|
}
|
|
|
|
|
|
|
|
QString windowTitle;
|
|
|
|
if (customWindowTitlePart.isEmpty()) {
|
|
|
|
windowTitle = BaseWindowTitle;
|
|
|
|
} else {
|
|
|
|
windowTitle = QString("%1[*] - %2").arg(customWindowTitlePart, BaseWindowTitle);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (customWindowTitlePart.isEmpty() || stackedWidgetIndex == 1) {
|
|
|
|
setWindowFilePath("");
|
|
|
|
} else {
|
|
|
|
setWindowFilePath(m_ui->tabWidget->databaseWidgetFromIndex(tabWidgetIndex)->database()->filePath());
|
|
|
|
}
|
|
|
|
|
|
|
|
setWindowTitle(windowTitle);
|
|
|
|
setWindowModified(isModified);
|
2012-04-21 16:02:12 -04:00
|
|
|
}
|
|
|
|
|
2012-05-02 09:37:21 -04:00
|
|
|
void MainWindow::showAboutDialog()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
auto* aboutDialog = new AboutDialog(this);
|
|
|
|
aboutDialog->open();
|
2012-05-02 09:37:21 -04:00
|
|
|
}
|
|
|
|
|
2019-01-30 09:11:50 -05:00
|
|
|
void MainWindow::showUpdateCheckStartup()
|
|
|
|
{
|
2019-04-09 21:06:13 -04:00
|
|
|
#ifdef WITH_XC_UPDATECHECK
|
2019-01-30 09:11:50 -05:00
|
|
|
if (!config()->get("UpdateCheckMessageShown", false).toBool()) {
|
2019-03-19 14:48:33 -04:00
|
|
|
auto result =
|
|
|
|
MessageBox::question(this,
|
|
|
|
tr("Check for updates on startup?"),
|
|
|
|
tr("Would you like KeePassXC to check for updates on startup?") + "\n\n"
|
|
|
|
+ tr("You can always check for updates manually from the application menu."),
|
|
|
|
MessageBox::Yes | MessageBox::No,
|
|
|
|
MessageBox::Yes);
|
2019-01-30 09:11:50 -05:00
|
|
|
|
|
|
|
config()->set("GUI/CheckForUpdates", (result == MessageBox::Yes));
|
|
|
|
config()->set("UpdateCheckMessageShown", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config()->get("GUI/CheckForUpdates", false).toBool()) {
|
|
|
|
updateCheck()->checkForUpdates(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::hasUpdateAvailable(bool hasUpdate, const QString& version, bool isManuallyRequested)
|
|
|
|
{
|
2019-04-09 21:06:13 -04:00
|
|
|
#ifdef WITH_XC_UPDATECHECK
|
2019-01-30 09:11:50 -05:00
|
|
|
if (hasUpdate && !isManuallyRequested) {
|
|
|
|
auto* updateCheckDialog = new UpdateCheckDialog(this);
|
|
|
|
updateCheckDialog->showUpdateCheckResponse(hasUpdate, version);
|
|
|
|
updateCheckDialog->show();
|
|
|
|
}
|
2019-02-18 08:26:56 -05:00
|
|
|
#else
|
|
|
|
Q_UNUSED(hasUpdate)
|
|
|
|
Q_UNUSED(version)
|
|
|
|
Q_UNUSED(isManuallyRequested)
|
2019-01-30 09:11:50 -05:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::showUpdateCheckDialog()
|
|
|
|
{
|
2019-04-09 21:06:13 -04:00
|
|
|
#ifdef WITH_XC_UPDATECHECK
|
2019-01-30 09:11:50 -05:00
|
|
|
updateCheck()->checkForUpdates(true);
|
|
|
|
auto* updateCheckDialog = new UpdateCheckDialog(this);
|
|
|
|
updateCheckDialog->show();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-06-18 20:46:46 -04:00
|
|
|
void MainWindow::customOpenUrl(QString url)
|
|
|
|
{
|
|
|
|
QDesktopServices::openUrl(QUrl(url));
|
|
|
|
}
|
|
|
|
|
2018-08-20 18:46:46 -04:00
|
|
|
void MainWindow::openDonateUrl()
|
|
|
|
{
|
2019-06-18 20:46:46 -04:00
|
|
|
customOpenUrl("https://keepassxc.org/donate");
|
2018-08-20 18:46:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::openBugReportUrl()
|
|
|
|
{
|
2019-06-18 20:46:46 -04:00
|
|
|
customOpenUrl("https://github.com/keepassxreboot/keepassxc/issues");
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::openGettingStartedGuide()
|
|
|
|
{
|
2019-08-30 20:18:41 -04:00
|
|
|
customOpenUrl(QString("file:///%1").arg(filePath()->dataPath("docs/KeePassXC_GettingStarted.pdf")));
|
2019-06-18 20:46:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::openUserGuide()
|
|
|
|
{
|
2019-08-30 20:18:41 -04:00
|
|
|
customOpenUrl(QString("file:///%1").arg(filePath()->dataPath("docs/KeePassXC_UserGuide.pdf")));
|
2018-08-20 18:46:46 -04:00
|
|
|
}
|
|
|
|
|
2019-06-18 20:46:46 -04:00
|
|
|
void MainWindow::openOnlineHelp()
|
|
|
|
{
|
|
|
|
customOpenUrl("https://keepassxc.org/docs/");
|
|
|
|
}
|
2019-08-30 22:55:02 -04:00
|
|
|
|
|
|
|
void MainWindow::openKeyboardShortcuts()
|
|
|
|
{
|
|
|
|
customOpenUrl("https://github.com/keepassxreboot/keepassxc/blob/develop/docs/KEYBINDS.md");
|
|
|
|
}
|
|
|
|
|
2012-05-27 05:09:52 -04:00
|
|
|
void MainWindow::switchToDatabases()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (m_ui->tabWidget->currentIndex() == -1) {
|
|
|
|
m_ui->stackedWidget->setCurrentIndex(WelcomeScreen);
|
|
|
|
} else {
|
|
|
|
m_ui->stackedWidget->setCurrentIndex(DatabaseTabScreen);
|
|
|
|
}
|
2012-05-27 05:09:52 -04:00
|
|
|
}
|
|
|
|
|
2018-08-20 18:46:46 -04:00
|
|
|
void MainWindow::switchToSettings(bool enabled)
|
2012-05-27 05:09:52 -04:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (enabled) {
|
|
|
|
m_ui->settingsWidget->loadSettings();
|
|
|
|
m_ui->stackedWidget->setCurrentIndex(SettingsScreen);
|
|
|
|
} else {
|
|
|
|
switchToDatabases();
|
|
|
|
}
|
2012-05-27 05:09:52 -04:00
|
|
|
}
|
|
|
|
|
2016-11-23 21:59:24 -05:00
|
|
|
void MainWindow::switchToPasswordGen(bool enabled)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (enabled) {
|
|
|
|
m_ui->passwordGeneratorWidget->loadSettings();
|
|
|
|
m_ui->passwordGeneratorWidget->regeneratePassword();
|
|
|
|
m_ui->stackedWidget->setCurrentIndex(PasswordGeneratorScreen);
|
2019-11-17 14:33:28 -05:00
|
|
|
m_ui->passwordGeneratorWidget->setStandaloneMode(true);
|
2019-01-16 22:53:29 -05:00
|
|
|
} else {
|
|
|
|
m_ui->passwordGeneratorWidget->saveSettings();
|
|
|
|
switchToDatabases();
|
|
|
|
}
|
2016-11-23 21:59:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::closePasswordGen()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
switchToPasswordGen(false);
|
2016-11-23 21:59:24 -05:00
|
|
|
}
|
|
|
|
|
2017-02-11 13:04:37 -05:00
|
|
|
void MainWindow::switchToNewDatabase()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->tabWidget->newDatabase();
|
|
|
|
switchToDatabases();
|
2017-02-11 13:04:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::switchToOpenDatabase()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->tabWidget->openDatabase();
|
|
|
|
switchToDatabases();
|
2017-02-11 13:04:37 -05:00
|
|
|
}
|
|
|
|
|
2018-10-28 07:49:32 -04:00
|
|
|
void MainWindow::switchToDatabaseFile(const QString& file)
|
2017-02-11 13:04:37 -05:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->tabWidget->addDatabaseTab(file);
|
|
|
|
switchToDatabases();
|
2017-02-11 13:04:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::switchToKeePass1Database()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->tabWidget->importKeePass1Database();
|
|
|
|
switchToDatabases();
|
2017-02-11 13:04:37 -05:00
|
|
|
}
|
|
|
|
|
2019-05-19 17:49:48 -04:00
|
|
|
void MainWindow::switchToOpVaultDatabase()
|
|
|
|
{
|
|
|
|
m_ui->tabWidget->importOpVaultDatabase();
|
|
|
|
switchToDatabases();
|
|
|
|
}
|
|
|
|
|
2018-05-13 17:21:43 -04:00
|
|
|
void MainWindow::switchToCsvImport()
|
2017-03-19 17:04:03 -04:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->tabWidget->importCsv();
|
|
|
|
switchToDatabases();
|
2017-03-19 17:04:03 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
void MainWindow::databaseStatusChanged(DatabaseWidget* dbWidget)
|
2016-10-08 12:55:05 -04:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
Q_UNUSED(dbWidget);
|
|
|
|
updateTrayIcon();
|
2016-10-08 12:55:05 -04:00
|
|
|
}
|
|
|
|
|
2020-02-09 03:06:19 -05:00
|
|
|
/**
|
|
|
|
* Select a database tab by its index. Stays bounded to first/last tab
|
|
|
|
* on overflow unless wrap is true.
|
|
|
|
*
|
|
|
|
* @param tabIndex 0-based tab index selector
|
|
|
|
* @param wrap if true wrap around to first/last tab
|
|
|
|
*/
|
|
|
|
void MainWindow::selectDatabaseTab(int tabIndex, bool wrap)
|
2018-09-14 17:41:30 -04:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (m_ui->stackedWidget->currentIndex() == DatabaseTabScreen) {
|
2020-02-09 03:06:19 -05:00
|
|
|
if (wrap) {
|
|
|
|
if (tabIndex < 0) {
|
|
|
|
tabIndex = m_ui->tabWidget->count() - 1;
|
|
|
|
} else if (tabIndex >= m_ui->tabWidget->count()) {
|
|
|
|
tabIndex = 0;
|
|
|
|
}
|
2019-01-16 22:53:29 -05:00
|
|
|
} else {
|
2020-02-09 03:06:19 -05:00
|
|
|
tabIndex = qBound(0, tabIndex, m_ui->tabWidget->count() - 1);
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
2018-09-14 17:41:30 -04:00
|
|
|
|
2020-02-09 03:06:19 -05:00
|
|
|
m_ui->tabWidget->setCurrentIndex(tabIndex);
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
2018-09-14 17:41:30 -04:00
|
|
|
}
|
|
|
|
|
2020-02-09 03:06:19 -05:00
|
|
|
void MainWindow::selectNextDatabaseTab()
|
2019-12-27 07:19:52 -05:00
|
|
|
{
|
2020-02-09 03:06:19 -05:00
|
|
|
selectDatabaseTab(m_ui->tabWidget->currentIndex() + 1, true);
|
2019-12-27 07:19:52 -05:00
|
|
|
}
|
|
|
|
|
2020-02-09 03:06:19 -05:00
|
|
|
void MainWindow::selectPreviousDatabaseTab()
|
2019-12-27 07:19:52 -05:00
|
|
|
{
|
2020-02-09 03:06:19 -05:00
|
|
|
selectDatabaseTab(m_ui->tabWidget->currentIndex() - 1, true);
|
2019-12-27 07:19:52 -05:00
|
|
|
}
|
|
|
|
|
2012-05-28 12:49:16 -04:00
|
|
|
void MainWindow::databaseTabChanged(int tabIndex)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (tabIndex != -1 && m_ui->stackedWidget->currentIndex() == WelcomeScreen) {
|
|
|
|
m_ui->stackedWidget->setCurrentIndex(DatabaseTabScreen);
|
|
|
|
} else if (tabIndex == -1 && m_ui->stackedWidget->currentIndex() == DatabaseTabScreen) {
|
|
|
|
m_ui->stackedWidget->setCurrentIndex(WelcomeScreen);
|
|
|
|
}
|
2012-08-01 04:35:37 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
m_actionMultiplexer.setCurrentObject(m_ui->tabWidget->currentDatabaseWidget());
|
2012-05-28 12:49:16 -04:00
|
|
|
}
|
|
|
|
|
2018-11-16 10:00:59 -05:00
|
|
|
void MainWindow::togglePasswordsHidden()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
|
|
|
|
if (dbWidget) {
|
|
|
|
dbWidget->setPasswordsHidden(!dbWidget->isPasswordsHidden());
|
|
|
|
}
|
2018-11-16 10:00:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::toggleUsernamesHidden()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
|
|
|
|
if (dbWidget) {
|
|
|
|
dbWidget->setUsernamesHidden(!dbWidget->isUsernamesHidden());
|
|
|
|
}
|
2018-11-16 10:00:59 -05:00
|
|
|
}
|
|
|
|
|
2012-06-29 18:22:07 -04:00
|
|
|
void MainWindow::closeEvent(QCloseEvent* event)
|
2014-03-22 07:21:49 -04:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (m_appExiting) {
|
|
|
|
event->accept();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-26 10:14:28 -04:00
|
|
|
// Ignore event and hide to tray if this is not an actual close
|
|
|
|
// request by the system's session manager.
|
|
|
|
if (config()->get("GUI/MinimizeOnClose").toBool() && !m_appExitCalled && !isHidden() && !qApp->isSavingSession()) {
|
2019-01-16 22:53:29 -05:00
|
|
|
event->ignore();
|
|
|
|
hideWindow();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-26 10:14:28 -04:00
|
|
|
m_appExiting = saveLastDatabases();
|
|
|
|
if (m_appExiting) {
|
2019-01-16 22:53:29 -05:00
|
|
|
saveWindowInformation();
|
|
|
|
event->accept();
|
|
|
|
QApplication::quit();
|
2019-10-26 10:14:28 -04:00
|
|
|
return;
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
2019-10-26 10:14:28 -04:00
|
|
|
|
|
|
|
m_appExitCalled = false;
|
|
|
|
event->ignore();
|
2014-03-22 07:21:49 -04:00
|
|
|
}
|
|
|
|
|
2015-10-15 12:02:31 -04:00
|
|
|
void MainWindow::changeEvent(QEvent* event)
|
2014-11-02 04:15:44 -05:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if ((event->type() == QEvent::WindowStateChange) && isMinimized()) {
|
|
|
|
if (isTrayIconEnabled() && m_trayIcon && m_trayIcon->isVisible()
|
|
|
|
&& config()->get("GUI/MinimizeToTray").toBool()) {
|
|
|
|
event->ignore();
|
2020-02-24 06:33:43 -05:00
|
|
|
hide();
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (config()->get("security/lockdatabaseminimize").toBool()) {
|
|
|
|
m_ui->tabWidget->lockDatabases();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
QMainWindow::changeEvent(event);
|
|
|
|
}
|
2014-11-02 04:15:44 -05:00
|
|
|
}
|
|
|
|
|
2014-03-22 07:53:42 -04:00
|
|
|
void MainWindow::saveWindowInformation()
|
2012-06-29 18:22:07 -04:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (isVisible()) {
|
|
|
|
config()->set("GUI/MainWindowGeometry", saveGeometry());
|
2019-01-16 11:04:32 -05:00
|
|
|
config()->set("GUI/MainWindowState", saveState());
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
2014-03-22 07:53:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool MainWindow::saveLastDatabases()
|
|
|
|
{
|
2019-03-21 17:39:02 -04:00
|
|
|
if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
|
|
|
|
auto currentDbWidget = m_ui->tabWidget->currentDatabaseWidget();
|
|
|
|
if (currentDbWidget) {
|
|
|
|
config()->set("LastActiveDatabase", currentDbWidget->database()->filePath());
|
|
|
|
} else {
|
|
|
|
config()->set("LastActiveDatabase", {});
|
|
|
|
}
|
2013-04-18 16:15:01 -04:00
|
|
|
|
2019-03-21 17:39:02 -04:00
|
|
|
QStringList openDatabases;
|
2019-05-03 14:40:45 -04:00
|
|
|
for (int i = 0; i < m_ui->tabWidget->count(); ++i) {
|
2019-03-21 17:39:02 -04:00
|
|
|
auto dbWidget = m_ui->tabWidget->databaseWidgetFromIndex(i);
|
|
|
|
openDatabases.append(dbWidget->database()->filePath());
|
|
|
|
}
|
2013-04-18 16:15:01 -04:00
|
|
|
|
2019-03-21 17:39:02 -04:00
|
|
|
config()->set("LastOpenedDatabases", openDatabases);
|
|
|
|
} else {
|
|
|
|
config()->set("LastActiveDatabase", {});
|
|
|
|
config()->set("LastOpenedDatabases", {});
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
2012-05-15 12:16:04 -04:00
|
|
|
|
2019-03-21 17:39:02 -04:00
|
|
|
return m_ui->tabWidget->closeAllDatabaseTabs();
|
2014-03-22 07:21:49 -04:00
|
|
|
}
|
|
|
|
|
2014-11-02 04:15:44 -05:00
|
|
|
void MainWindow::updateTrayIcon()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (isTrayIconEnabled()) {
|
|
|
|
if (!m_trayIcon) {
|
|
|
|
m_trayIcon = new QSystemTrayIcon(this);
|
|
|
|
QMenu* menu = new QMenu(this);
|
2014-11-02 04:15:44 -05:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
QAction* actionToggle = new QAction(tr("Toggle window"), menu);
|
|
|
|
menu->addAction(actionToggle);
|
2020-01-05 21:00:25 -05:00
|
|
|
actionToggle->setIcon(filePath()->icon("apps", "keepassxc-dark", false));
|
2014-11-02 04:15:44 -05:00
|
|
|
|
2019-03-27 19:56:10 -04:00
|
|
|
menu->addAction(m_ui->actionLockDatabases);
|
|
|
|
|
2018-10-26 09:19:04 -04:00
|
|
|
#ifdef Q_OS_MACOS
|
2019-01-16 22:53:29 -05:00
|
|
|
QAction* actionQuit = new QAction(tr("Quit KeePassXC"), menu);
|
|
|
|
menu->addAction(actionQuit);
|
2017-05-18 23:42:31 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(actionQuit, SIGNAL(triggered()), SLOT(appExit()));
|
2017-05-18 23:42:31 -04:00
|
|
|
#else
|
2019-01-16 22:53:29 -05:00
|
|
|
menu->addAction(m_ui->actionQuit);
|
2014-11-02 04:15:44 -05:00
|
|
|
|
2019-10-01 04:35:13 -04:00
|
|
|
#endif
|
2019-01-16 22:53:29 -05:00
|
|
|
connect(m_trayIcon,
|
|
|
|
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
|
|
|
SLOT(trayIconTriggered(QSystemTrayIcon::ActivationReason)));
|
|
|
|
connect(actionToggle, SIGNAL(triggered()), SLOT(toggleWindow()));
|
|
|
|
|
|
|
|
m_trayIcon->setContextMenu(menu);
|
|
|
|
|
|
|
|
m_trayIcon->setIcon(filePath()->trayIcon());
|
|
|
|
m_trayIcon->show();
|
|
|
|
}
|
|
|
|
if (m_ui->tabWidget->hasLockableDatabases()) {
|
|
|
|
m_trayIcon->setIcon(filePath()->trayIconUnlocked());
|
|
|
|
} else {
|
|
|
|
m_trayIcon->setIcon(filePath()->trayIconLocked());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (m_trayIcon) {
|
|
|
|
m_trayIcon->hide();
|
|
|
|
delete m_trayIcon;
|
|
|
|
m_trayIcon = nullptr;
|
|
|
|
}
|
|
|
|
}
|
2014-11-02 04:15:44 -05:00
|
|
|
}
|
|
|
|
|
2019-06-11 03:37:23 -04:00
|
|
|
void MainWindow::obtainContextFocusLock()
|
|
|
|
{
|
|
|
|
m_contextMenuFocusLock = true;
|
|
|
|
}
|
|
|
|
|
2019-05-30 17:28:47 -04:00
|
|
|
void MainWindow::releaseContextFocusLock()
|
|
|
|
{
|
|
|
|
m_contextMenuFocusLock = false;
|
|
|
|
}
|
|
|
|
|
2012-08-01 04:35:37 -04:00
|
|
|
void MainWindow::showEntryContextMenu(const QPoint& globalPos)
|
|
|
|
{
|
2019-10-26 14:54:52 -04:00
|
|
|
bool entrySelected = false;
|
|
|
|
auto dbWidget = m_ui->tabWidget->currentDatabaseWidget();
|
|
|
|
if (dbWidget) {
|
2019-11-17 15:39:16 -05:00
|
|
|
entrySelected = dbWidget->numberOfSelectedEntries() > 0;
|
2019-10-26 14:54:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (entrySelected) {
|
|
|
|
m_entryContextMenu->popup(globalPos);
|
|
|
|
} else {
|
|
|
|
m_entryNewContextMenu->popup(globalPos);
|
|
|
|
}
|
2012-08-01 04:35:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::showGroupContextMenu(const QPoint& globalPos)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->menuGroups->popup(globalPos);
|
2012-08-01 04:35:37 -04:00
|
|
|
}
|
|
|
|
|
2012-05-15 12:16:04 -04:00
|
|
|
void MainWindow::setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (!QKeySequence::keyBindings(standard).isEmpty()) {
|
|
|
|
action->setShortcuts(standard);
|
|
|
|
} else if (fallback != 0) {
|
|
|
|
action->setShortcut(QKeySequence(fallback));
|
|
|
|
}
|
2012-05-15 12:16:04 -04:00
|
|
|
}
|
2013-03-24 18:21:59 -04:00
|
|
|
|
2014-01-07 15:56:58 -05:00
|
|
|
void MainWindow::applySettingsChanges()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
int timeout = config()->get("security/lockdatabaseidlesec").toInt() * 1000;
|
|
|
|
if (timeout <= 0) {
|
|
|
|
timeout = 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_inactivityTimer->setInactivityTimeout(timeout);
|
|
|
|
if (config()->get("security/lockdatabaseidle").toBool()) {
|
|
|
|
m_inactivityTimer->activate();
|
|
|
|
} else {
|
|
|
|
m_inactivityTimer->deactivate();
|
|
|
|
}
|
2014-11-02 04:15:44 -05:00
|
|
|
|
2018-04-04 11:39:26 -04:00
|
|
|
#ifdef WITH_XC_TOUCHID
|
2019-01-16 22:53:29 -05:00
|
|
|
// forget TouchID (in minutes)
|
|
|
|
timeout = config()->get("security/resettouchidtimeout").toInt() * 60 * 1000;
|
|
|
|
if (timeout <= 0) {
|
|
|
|
timeout = 30 * 60 * 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_touchIDinactivityTimer->setInactivityTimeout(timeout);
|
|
|
|
if (config()->get("security/resettouchid").toBool()) {
|
|
|
|
m_touchIDinactivityTimer->activate();
|
|
|
|
} else {
|
|
|
|
m_touchIDinactivityTimer->deactivate();
|
|
|
|
}
|
2018-04-04 11:39:26 -04:00
|
|
|
#endif
|
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->toolBar->setHidden(config()->get("GUI/HideToolbar").toBool());
|
2019-01-16 11:04:32 -05:00
|
|
|
m_ui->toolBar->setMovable(config()->get("GUI/MovableToolbar").toBool());
|
|
|
|
|
|
|
|
bool isOk = false;
|
|
|
|
const auto toolButtonStyle = static_cast<Qt::ToolButtonStyle>(config()->get("GUI/ToolButtonStyle").toInt(&isOk));
|
|
|
|
if (isOk) {
|
|
|
|
m_ui->toolBar->setToolButtonStyle(toolButtonStyle);
|
|
|
|
}
|
2018-04-09 15:33:08 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
updateTrayIcon();
|
2014-11-02 04:15:44 -05:00
|
|
|
}
|
|
|
|
|
2019-05-03 14:40:45 -04:00
|
|
|
void MainWindow::focusWindowChanged(QWindow* focusWindow)
|
|
|
|
{
|
|
|
|
if (focusWindow != windowHandle()) {
|
2020-02-24 06:33:43 -05:00
|
|
|
m_lastFocusOutTime = Clock::currentMilliSecondsSinceEpoch();
|
2019-05-03 14:40:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-02 04:15:44 -05:00
|
|
|
void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason)
|
|
|
|
{
|
2019-05-03 14:40:45 -04:00
|
|
|
if (!m_trayIconTriggerTimer.isActive()) {
|
|
|
|
m_trayIconTriggerTimer.start(150);
|
|
|
|
}
|
|
|
|
// Overcome Qt bug https://bugreports.qt.io/browse/QTBUG-69698
|
|
|
|
// Store last issued tray icon activation reason to properly
|
|
|
|
// capture doubleclick events
|
|
|
|
m_trayIconTriggerReason = reason;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::processTrayIconTrigger()
|
|
|
|
{
|
|
|
|
if (m_trayIconTriggerReason == QSystemTrayIcon::DoubleClick) {
|
|
|
|
// Always toggle window on double click
|
2019-01-16 22:53:29 -05:00
|
|
|
toggleWindow();
|
2019-05-03 14:40:45 -04:00
|
|
|
} else if (m_trayIconTriggerReason == QSystemTrayIcon::Trigger
|
|
|
|
|| m_trayIconTriggerReason == QSystemTrayIcon::MiddleClick) {
|
2019-10-26 10:14:28 -04:00
|
|
|
// Toggle window if is not in front.
|
2019-06-11 18:25:17 -04:00
|
|
|
#ifdef Q_OS_WIN
|
2020-02-24 06:33:43 -05:00
|
|
|
// If on Windows, check if focus switched within the 500 milliseconds since
|
2019-10-26 10:14:28 -04:00
|
|
|
// clicking the tray icon removes focus from main window.
|
2020-02-24 06:33:43 -05:00
|
|
|
if (isHidden() || (Clock::currentMilliSecondsSinceEpoch() - m_lastFocusOutTime) <= 500) {
|
2019-06-11 18:25:17 -04:00
|
|
|
#else
|
2019-10-26 10:14:28 -04:00
|
|
|
// If on Linux or macOS, check if the window has focus.
|
|
|
|
if (hasFocus() || isHidden() || windowHandle()->isActive()) {
|
2019-06-11 18:25:17 -04:00
|
|
|
#endif
|
2019-05-03 14:40:45 -04:00
|
|
|
toggleWindow();
|
|
|
|
} else {
|
|
|
|
bringToFront();
|
|
|
|
}
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
2014-11-02 04:15:44 -05:00
|
|
|
}
|
|
|
|
|
2020-02-24 06:33:43 -05:00
|
|
|
void MainWindow::show()
|
|
|
|
{
|
|
|
|
#ifndef Q_OS_WIN
|
|
|
|
m_lastShowTime = Clock::currentMilliSecondsSinceEpoch();
|
|
|
|
#endif
|
|
|
|
QMainWindow::show();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MainWindow::shouldHide()
|
|
|
|
{
|
|
|
|
#ifndef Q_OS_WIN
|
|
|
|
qint64 current_time = Clock::currentMilliSecondsSinceEpoch();
|
|
|
|
|
|
|
|
if (current_time - m_lastShowTime < 50) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::hide()
|
|
|
|
{
|
|
|
|
if (shouldHide()) {
|
|
|
|
QMainWindow::hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-01 21:08:10 -05:00
|
|
|
void MainWindow::hideWindow()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
saveWindowInformation();
|
2020-02-24 06:33:43 -05:00
|
|
|
if (QGuiApplication::platformName() != "xcb") {
|
|
|
|
// In X11 the window should NOT be minimized and hidden (i.e. not
|
|
|
|
// shown) at the same time (which would happen if both minimize on
|
|
|
|
// startup and minimize to tray are set) since otherwise it causes
|
|
|
|
// problems on restore as seen on issue #1595. Hiding it is enough.
|
|
|
|
setWindowState(windowState() | Qt::WindowMinimized);
|
|
|
|
}
|
2019-01-16 22:53:29 -05:00
|
|
|
// Only hide if tray icon is active, otherwise window will be gone forever
|
|
|
|
if (isTrayIconEnabled()) {
|
|
|
|
hide();
|
|
|
|
} else {
|
|
|
|
showMinimized();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config()->get("security/lockdatabaseminimize").toBool()) {
|
|
|
|
m_ui->tabWidget->lockDatabases();
|
|
|
|
}
|
2017-03-01 21:08:10 -05:00
|
|
|
}
|
|
|
|
|
2020-02-24 06:33:43 -05:00
|
|
|
void MainWindow::minimizeOrHide()
|
|
|
|
{
|
|
|
|
if (config()->get("GUI/MinimizeToTray").toBool()) {
|
|
|
|
hideWindow();
|
|
|
|
} else {
|
|
|
|
showMinimized();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-02 04:15:44 -05:00
|
|
|
void MainWindow::toggleWindow()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (isVisible() && !isMinimized()) {
|
|
|
|
hideWindow();
|
|
|
|
} else {
|
|
|
|
bringToFront();
|
2017-03-01 21:08:10 -05:00
|
|
|
|
2018-10-26 09:19:04 -04:00
|
|
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(QT_NO_DBUS) && (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
|
2019-01-16 22:53:29 -05:00
|
|
|
// re-register global D-Bus menu (needed on Ubuntu with Unity)
|
|
|
|
// see https://github.com/keepassxreboot/keepassxc/issues/271
|
|
|
|
// and https://bugreports.qt.io/browse/QTBUG-58723
|
|
|
|
// check for !isVisible(), because isNativeMenuBar() does not work with appmenu-qt5
|
2019-11-24 09:40:16 -05:00
|
|
|
static const auto isDesktopSessionUnity = qgetenv("XDG_CURRENT_DESKTOP") == "Unity";
|
2019-01-24 22:45:05 -05:00
|
|
|
|
|
|
|
if (isDesktopSessionUnity && Tools::qtRuntimeVersion() < QT_VERSION_CHECK(5, 9, 0)
|
|
|
|
&& !m_ui->menubar->isVisible()) {
|
|
|
|
QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("com.canonical.AppMenu.Registrar"),
|
|
|
|
QStringLiteral("/com/canonical/AppMenu/Registrar"),
|
|
|
|
QStringLiteral("com.canonical.AppMenu.Registrar"),
|
|
|
|
QStringLiteral("RegisterWindow"));
|
2019-01-16 22:53:29 -05:00
|
|
|
QList<QVariant> args;
|
|
|
|
args << QVariant::fromValue(static_cast<uint32_t>(winId()))
|
|
|
|
<< QVariant::fromValue(QDBusObjectPath("/MenuBar/1"));
|
|
|
|
msg.setArguments(args);
|
|
|
|
QDBusConnection::sessionBus().send(msg);
|
|
|
|
}
|
2017-02-09 09:28:52 -05:00
|
|
|
#endif
|
2019-01-16 22:53:29 -05:00
|
|
|
}
|
2014-11-02 04:15:44 -05:00
|
|
|
}
|
|
|
|
|
2015-07-13 15:17:11 -04:00
|
|
|
void MainWindow::lockDatabasesAfterInactivity()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
// ignore event if a modal dialog is open (such as a message box or file dialog)
|
|
|
|
if (QApplication::activeModalWidget()) {
|
|
|
|
return;
|
|
|
|
}
|
2015-07-13 15:17:11 -04:00
|
|
|
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->tabWidget->lockDatabases();
|
2015-07-13 15:17:11 -04:00
|
|
|
}
|
|
|
|
|
2018-04-04 11:39:26 -04:00
|
|
|
void MainWindow::forgetTouchIDAfterInactivity()
|
|
|
|
{
|
|
|
|
#ifdef WITH_XC_TOUCHID
|
2019-01-16 22:53:29 -05:00
|
|
|
TouchID::getInstance().reset();
|
2018-04-04 11:39:26 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-11-02 04:15:44 -05:00
|
|
|
bool MainWindow::isTrayIconEnabled() const
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
return config()->get("GUI/ShowTrayIcon").toBool() && QSystemTrayIcon::isSystemTrayAvailable();
|
2014-01-07 15:56:58 -05:00
|
|
|
}
|
2015-01-21 11:19:05 -05:00
|
|
|
|
2018-03-31 16:01:30 -04:00
|
|
|
void MainWindow::displayGlobalMessage(const QString& text,
|
2019-01-16 22:53:29 -05:00
|
|
|
MessageWidget::MessageType type,
|
|
|
|
bool showClosebutton,
|
|
|
|
int autoHideTimeout)
|
2015-01-21 11:19:05 -05:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->globalMessageWidget->setCloseButtonVisible(showClosebutton);
|
|
|
|
m_ui->globalMessageWidget->showMessage(text, type, autoHideTimeout);
|
2015-01-21 11:19:05 -05:00
|
|
|
}
|
|
|
|
|
2018-03-31 16:01:30 -04:00
|
|
|
void MainWindow::displayTabMessage(const QString& text,
|
2019-01-16 22:53:29 -05:00
|
|
|
MessageWidget::MessageType type,
|
|
|
|
bool showClosebutton,
|
|
|
|
int autoHideTimeout)
|
2015-01-21 11:19:05 -05:00
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->tabWidget->currentDatabaseWidget()->showMessage(text, type, showClosebutton, autoHideTimeout);
|
2015-01-21 11:19:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::hideGlobalMessage()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->globalMessageWidget->hideMessage();
|
2015-01-21 11:19:05 -05:00
|
|
|
}
|
|
|
|
|
2017-02-24 11:27:27 -05:00
|
|
|
void MainWindow::showYubiKeyPopup()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
displayGlobalMessage(tr("Please touch the button on your YubiKey!"),
|
|
|
|
MessageWidget::Information,
|
|
|
|
false,
|
|
|
|
MessageWidget::DisableAutoHide);
|
|
|
|
setEnabled(false);
|
2017-02-24 11:27:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::hideYubiKeyPopup()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
hideGlobalMessage();
|
|
|
|
setEnabled(true);
|
2017-02-24 11:27:27 -05:00
|
|
|
}
|
2016-12-23 19:04:43 -05:00
|
|
|
|
2017-12-05 10:25:08 -05:00
|
|
|
void MainWindow::bringToFront()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
ensurePolished();
|
2019-02-01 18:11:15 -05:00
|
|
|
setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
|
2019-01-16 22:53:29 -05:00
|
|
|
show();
|
|
|
|
raise();
|
|
|
|
activateWindow();
|
2017-12-05 10:25:08 -05:00
|
|
|
}
|
|
|
|
|
2016-12-23 19:04:43 -05:00
|
|
|
void MainWindow::handleScreenLock()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
if (config()->get("security/lockdatabasescreenlock").toBool()) {
|
|
|
|
lockDatabasesAfterInactivity();
|
|
|
|
}
|
2018-04-04 11:39:26 -04:00
|
|
|
|
|
|
|
#ifdef WITH_XC_TOUCHID
|
2019-01-16 22:53:29 -05:00
|
|
|
if (config()->get("security/resettouchidscreenlock").toBool()) {
|
|
|
|
forgetTouchIDAfterInactivity();
|
|
|
|
}
|
2018-04-04 11:39:26 -04:00
|
|
|
#endif
|
2016-12-23 19:04:43 -05:00
|
|
|
}
|
2017-12-05 02:44:01 -05:00
|
|
|
|
|
|
|
QStringList MainWindow::kdbxFilesFromUrls(const QList<QUrl>& urls)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
QStringList kdbxFiles;
|
|
|
|
for (const QUrl& url : urls) {
|
|
|
|
const QFileInfo fInfo(url.toLocalFile());
|
|
|
|
const bool isKdbxFile = fInfo.isFile() && fInfo.suffix().toLower() == "kdbx";
|
|
|
|
if (isKdbxFile) {
|
|
|
|
kdbxFiles.append(fInfo.absoluteFilePath());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return kdbxFiles;
|
2017-12-05 02:44:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::dragEnterEvent(QDragEnterEvent* event)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
const QMimeData* mimeData = event->mimeData();
|
|
|
|
if (mimeData->hasUrls()) {
|
|
|
|
const QStringList kdbxFiles = kdbxFilesFromUrls(mimeData->urls());
|
|
|
|
if (!kdbxFiles.isEmpty()) {
|
|
|
|
event->acceptProposedAction();
|
|
|
|
}
|
|
|
|
}
|
2017-12-05 02:44:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::dropEvent(QDropEvent* event)
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
const QMimeData* mimeData = event->mimeData();
|
|
|
|
if (mimeData->hasUrls()) {
|
|
|
|
const QStringList kdbxFiles = kdbxFilesFromUrls(mimeData->urls());
|
|
|
|
if (!kdbxFiles.isEmpty()) {
|
|
|
|
event->acceptProposedAction();
|
|
|
|
}
|
|
|
|
for (const QString& kdbxFile : kdbxFiles) {
|
|
|
|
openDatabase(kdbxFile);
|
|
|
|
}
|
|
|
|
}
|
2017-12-05 02:44:01 -05:00
|
|
|
}
|
2017-02-10 09:42:18 -05:00
|
|
|
|
|
|
|
void MainWindow::closeAllDatabases()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
m_ui->tabWidget->closeAllDatabaseTabs();
|
2017-02-10 09:42:18 -05:00
|
|
|
}
|
2018-01-15 18:49:32 -05:00
|
|
|
|
|
|
|
void MainWindow::lockAllDatabases()
|
|
|
|
{
|
2019-01-16 22:53:29 -05:00
|
|
|
lockDatabasesAfterInactivity();
|
2018-03-03 05:10:55 -05:00
|
|
|
}
|
2019-02-21 00:51:23 -05:00
|
|
|
|
|
|
|
void MainWindow::displayDesktopNotification(const QString& msg, QString title, int msTimeoutHint)
|
|
|
|
{
|
|
|
|
if (!m_trayIcon || !QSystemTrayIcon::supportsMessages()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (title.isEmpty()) {
|
|
|
|
title = BaseWindowTitle;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
|
|
|
|
m_trayIcon->showMessage(title, msg, filePath()->applicationIcon(), msTimeoutHint);
|
|
|
|
#else
|
|
|
|
m_trayIcon->showMessage(title, msg, QSystemTrayIcon::Information, msTimeoutHint);
|
|
|
|
#endif
|
|
|
|
}
|