2
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, either version 2 or (at your option)
7
* version 3 of the License.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include "DatabaseOpenDialog.h"
20
#include "DatabaseOpenWidget.h"
21
#include "DatabaseTabWidget.h"
22
#include "DatabaseWidget.h"
29
#include <QtPlatformHeaders/QWindowsWindowFunctions>
32
DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent)
34
, m_view(new DatabaseOpenWidget(this))
35
, m_tabBar(new QTabBar(this))
37
setWindowTitle(tr("Unlock Database - KeePassXC"));
38
setWindowFlags(Qt::Dialog);
39
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
41
// Linux requires this to overcome some Desktop Environments (also no Quick Unlock)
42
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
44
// block input to the main window/application while the dialog is open
45
setWindowModality(Qt::ApplicationModal);
47
QWindowsWindowFunctions::setWindowActivationBehavior(QWindowsWindowFunctions::AlwaysActivateWindow);
49
connect(m_view, &DatabaseOpenWidget::dialogFinished, this, &DatabaseOpenDialog::complete);
51
m_tabBar->setAutoHide(true);
52
m_tabBar->setExpanding(false);
53
connect(m_tabBar, &QTabBar::currentChanged, this, &DatabaseOpenDialog::tabChanged);
55
auto* layout = new QVBoxLayout();
56
layout->setContentsMargins(0, 0, 0, 0);
57
layout->setSpacing(0);
58
layout->addWidget(m_tabBar);
59
layout->addWidget(m_view);
63
// set up Ctrl+PageUp / Ctrl+PageDown and Ctrl+Tab / Ctrl+Shift+Tab shortcuts to cycle tabs
64
// Ctrl+Tab is broken on Mac, so use Alt (i.e. the Option key) - https://bugreports.qt.io/browse/QTBUG-8596
65
auto dbTabModifier = Qt::CTRL;
67
dbTabModifier = Qt::ALT;
69
auto* shortcut = new QShortcut(Qt::CTRL + Qt::Key_PageUp, this);
70
shortcut->setContext(Qt::WidgetWithChildrenShortcut);
71
connect(shortcut, &QShortcut::activated, this, [this]() { selectTabOffset(-1); });
72
shortcut = new QShortcut(dbTabModifier + Qt::SHIFT + Qt::Key_Tab, this);
73
shortcut->setContext(Qt::WidgetWithChildrenShortcut);
74
connect(shortcut, &QShortcut::activated, this, [this]() { selectTabOffset(-1); });
75
shortcut = new QShortcut(Qt::CTRL + Qt::Key_PageDown, this);
76
shortcut->setContext(Qt::WidgetWithChildrenShortcut);
77
connect(shortcut, &QShortcut::activated, this, [this]() { selectTabOffset(1); });
78
shortcut = new QShortcut(dbTabModifier + Qt::Key_Tab, this);
79
shortcut->setContext(Qt::WidgetWithChildrenShortcut);
80
connect(shortcut, &QShortcut::activated, this, [this]() { selectTabOffset(1); });
83
void DatabaseOpenDialog::showEvent(QShowEvent* event)
85
QDialog::showEvent(event);
86
QTimer::singleShot(100, this, [=] {
87
if (m_view->isOnQuickUnlockScreen() && !m_view->unlockingDatabase()) {
88
m_view->triggerQuickUnlock();
93
void DatabaseOpenDialog::selectTabOffset(int offset)
95
if (offset == 0 || m_tabBar->count() <= 1) {
98
int tab = m_tabBar->currentIndex() + offset;
99
int last = m_tabBar->count() - 1;
102
} else if (tab > last) {
105
m_tabBar->setCurrentIndex(tab);
108
void DatabaseOpenDialog::addDatabaseTab(DatabaseWidget* dbWidget)
115
// important - we must add the DB widget first, because addTab will fire
116
// tabChanged immediately which will look for a dbWidget in the list
117
m_tabDbWidgets.append(dbWidget);
118
QFileInfo fileInfo(dbWidget->database()->filePath());
119
m_tabBar->addTab(fileInfo.fileName());
120
Q_ASSERT(m_tabDbWidgets.count() == m_tabBar->count());
123
void DatabaseOpenDialog::setActiveDatabaseTab(DatabaseWidget* dbWidget)
128
int index = m_tabDbWidgets.indexOf(dbWidget);
130
m_tabBar->setCurrentIndex(index);
134
void DatabaseOpenDialog::tabChanged(int index)
136
if (index < 0 || index >= m_tabDbWidgets.count()) {
140
if (m_tabDbWidgets.count() == m_tabBar->count()) {
141
DatabaseWidget* dbWidget = m_tabDbWidgets[index];
142
setTarget(dbWidget, dbWidget->database()->filePath());
144
// if these list sizes don't match, there's a bug somewhere nearby
145
qWarning("DatabaseOpenDialog: mismatch between tab count %d and DB count %d",
147
m_tabDbWidgets.count());
152
* Sets the target DB and reloads the UI.
154
void DatabaseOpenDialog::setTarget(DatabaseWidget* dbWidget, const QString& filePath)
156
// reconnect finished signal to new dbWidget, then reload the UI
157
if (m_currentDbWidget) {
158
disconnect(this, &DatabaseOpenDialog::dialogFinished, m_currentDbWidget, nullptr);
160
connect(this, &DatabaseOpenDialog::dialogFinished, dbWidget, &DatabaseWidget::unlockDatabase);
162
m_currentDbWidget = dbWidget;
163
m_view->load(filePath);
166
void DatabaseOpenDialog::setIntent(DatabaseOpenDialog::Intent intent)
171
DatabaseOpenDialog::Intent DatabaseOpenDialog::intent() const
176
void DatabaseOpenDialog::clearForms()
178
m_view->clearForms();
180
m_intent = Intent::None;
181
if (m_currentDbWidget) {
182
disconnect(this, &DatabaseOpenDialog::dialogFinished, m_currentDbWidget, nullptr);
184
m_currentDbWidget.clear();
185
m_tabDbWidgets.clear();
187
// block signals while removing tabs so that tabChanged doesn't get called
188
m_tabBar->blockSignals(true);
189
while (m_tabBar->count() > 0) {
190
m_tabBar->removeTab(0);
192
m_tabBar->blockSignals(false);
195
QSharedPointer<Database> DatabaseOpenDialog::database() const
200
void DatabaseOpenDialog::complete(bool accepted)
202
// save DB, since DatabaseOpenWidget will reset its data after accept() is called
203
m_db = m_view->database();
211
emit dialogFinished(accepted, m_currentDbWidget);