2011-11-13 08:55:20 -05:00
|
|
|
/*
|
2018-11-22 05:47:31 -05:00
|
|
|
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
2011-11-13 08:55:20 -05: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/>.
|
|
|
|
*/
|
|
|
|
|
2011-12-30 12:43:24 -05:00
|
|
|
#include "DatabaseTabWidget.h"
|
2011-11-13 08:55:20 -05:00
|
|
|
|
2013-10-03 09:18:16 -04:00
|
|
|
#include <QFileInfo>
|
2017-02-24 21:40:49 -05:00
|
|
|
#include <QPushButton>
|
2018-03-31 16:01:30 -04:00
|
|
|
#include <QTabWidget>
|
2011-11-13 08:55:20 -05:00
|
|
|
|
2012-07-14 13:09:28 -04:00
|
|
|
#include "autotype/AutoType.h"
|
2018-03-31 16:01:30 -04:00
|
|
|
#include "core/AsyncTask.h"
|
2012-05-27 17:01:14 -04:00
|
|
|
#include "core/Config.h"
|
2011-11-13 08:55:20 -05:00
|
|
|
#include "core/Database.h"
|
2018-03-31 16:01:30 -04:00
|
|
|
#include "core/Global.h"
|
2012-05-02 10:22:52 -04:00
|
|
|
#include "core/Group.h"
|
2011-11-13 08:55:20 -05:00
|
|
|
#include "core/Metadata.h"
|
2018-11-23 18:57:41 -05:00
|
|
|
#include "core/Tools.h"
|
2015-07-14 16:14:34 -04:00
|
|
|
#include "format/CsvExporter.h"
|
2015-09-13 06:38:19 -04:00
|
|
|
#include "gui/Clipboard.h"
|
2011-11-13 08:55:20 -05:00
|
|
|
#include "gui/DatabaseWidget.h"
|
2014-05-17 05:16:27 -04:00
|
|
|
#include "gui/DatabaseWidgetStateSync.h"
|
2012-05-16 03:54:21 -04:00
|
|
|
#include "gui/DragTabBar.h"
|
2012-05-12 04:08:41 -04:00
|
|
|
#include "gui/FileDialog.h"
|
2013-10-08 16:09:20 -04:00
|
|
|
#include "gui/MessageBox.h"
|
2018-11-23 18:57:41 -05:00
|
|
|
#include "gui/DatabaseOpenDialog.h"
|
2012-05-16 04:16:32 -04:00
|
|
|
#include "gui/entry/EntryView.h"
|
|
|
|
#include "gui/group/GroupView.h"
|
2018-05-13 17:21:43 -04:00
|
|
|
#include "gui/wizard/NewDatabaseWizard.h"
|
2011-11-13 08:55:20 -05:00
|
|
|
|
2011-12-30 12:43:24 -05:00
|
|
|
DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
|
|
|
: QTabWidget(parent)
|
2017-02-20 20:19:11 -05:00
|
|
|
, m_dbWidgetStateSync(new DatabaseWidgetStateSync(this))
|
2018-11-23 18:57:41 -05:00
|
|
|
, m_dbWidgetPendingLock(nullptr)
|
|
|
|
, m_databaseOpenDialog(new DatabaseOpenDialog())
|
2011-11-13 08:55:20 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
auto* tabBar = new DragTabBar(this);
|
2012-05-16 03:54:21 -04:00
|
|
|
setTabBar(tabBar);
|
2015-07-18 12:27:17 -04:00
|
|
|
setDocumentMode(true);
|
2012-05-16 03:54:21 -04:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabaseTab(int)));
|
2014-05-17 05:16:27 -04:00
|
|
|
connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged()));
|
2018-11-22 05:47:31 -05:00
|
|
|
connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*)));
|
2012-07-14 13:09:28 -04:00
|
|
|
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
|
2018-02-05 07:25:16 -05:00
|
|
|
connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase()));
|
2018-11-23 18:57:41 -05:00
|
|
|
connect(autoType(), SIGNAL(autotypeRejected()), SLOT(relockPendingDatabase()));
|
2011-11-13 08:55:20 -05:00
|
|
|
}
|
|
|
|
|
2012-06-29 17:40:51 -04:00
|
|
|
DatabaseTabWidget::~DatabaseTabWidget()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-06-29 18:22:07 -04:00
|
|
|
void DatabaseTabWidget::toggleTabbar()
|
|
|
|
{
|
2012-04-20 05:29:31 -04:00
|
|
|
if (count() > 1) {
|
2017-02-14 13:17:35 -05:00
|
|
|
tabBar()->show();
|
|
|
|
} else {
|
|
|
|
tabBar()->hide();
|
2012-04-20 05:29:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-13 17:21:43 -04:00
|
|
|
/**
|
|
|
|
* Helper method for invoking the new database wizard.
|
|
|
|
* The user of this method MUST take ownership of the returned pointer.
|
|
|
|
*
|
|
|
|
* @return pointer to the configured new database, nullptr on failure
|
|
|
|
*/
|
2018-11-22 05:47:31 -05:00
|
|
|
QSharedPointer<Database> DatabaseTabWidget::execNewDatabaseWizard()
|
2018-05-13 17:21:43 -04:00
|
|
|
{
|
|
|
|
// use QScopedPointer to ensure deletion after scope ends, but still parent
|
|
|
|
// it to this to make it modal and allow easier access in unit tests
|
|
|
|
QScopedPointer<NewDatabaseWizard> wizard(new NewDatabaseWizard(this));
|
|
|
|
if (!wizard->exec()) {
|
2018-11-22 05:47:31 -05:00
|
|
|
return {};
|
2018-05-13 17:21:43 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto db = wizard->takeDatabase();
|
2018-05-13 17:21:43 -04:00
|
|
|
if (!db) {
|
2018-11-22 05:47:31 -05:00
|
|
|
return {};
|
2018-05-13 17:21:43 -04:00
|
|
|
}
|
|
|
|
Q_ASSERT(db->key());
|
|
|
|
Q_ASSERT(db->kdf());
|
|
|
|
if (!db->key() || !db->kdf()) {
|
2018-10-31 23:27:38 -04:00
|
|
|
MessageBox::critical(this,
|
|
|
|
tr("Database creation error"),
|
|
|
|
tr("The created database has no key or KDF, refusing to save it.\n"
|
|
|
|
"This is definitely a bug, please report it to the developers."),
|
|
|
|
QMessageBox::Ok,
|
|
|
|
QMessageBox::Ok);
|
2018-11-22 05:47:31 -05:00
|
|
|
return {};
|
2018-05-13 17:21:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return db;
|
|
|
|
}
|
|
|
|
|
2011-12-30 12:43:24 -05:00
|
|
|
void DatabaseTabWidget::newDatabase()
|
2011-11-16 12:47:17 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
auto db = execNewDatabaseWizard();
|
2018-05-13 17:21:43 -04:00
|
|
|
if (!db) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
addDatabaseTab(new DatabaseWidget(db, this));
|
|
|
|
db->markAsModified();
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
|
|
|
|
2011-12-30 12:43:24 -05:00
|
|
|
void DatabaseTabWidget::openDatabase()
|
2011-11-13 08:55:20 -05:00
|
|
|
{
|
2012-06-05 13:41:21 -04:00
|
|
|
QString filter = QString("%1 (*.kdbx);;%2 (*)").arg(tr("KeePass 2 Database"), tr("All files"));
|
2018-11-22 05:47:31 -05:00
|
|
|
QString fileName = fileDialog()->getOpenFileName(this, tr("Open database"), "", filter);
|
2011-11-13 08:55:20 -05:00
|
|
|
if (!fileName.isEmpty()) {
|
2018-11-22 05:47:31 -05:00
|
|
|
addDatabaseTab(fileName);
|
2011-11-13 08:55:20 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Add a new database tab or switch to an existing one if the
|
|
|
|
* database has been opened already.
|
|
|
|
*
|
|
|
|
* @param filePath database file path
|
2018-11-23 13:24:59 -05:00
|
|
|
* @param password optional, password to unlock database
|
|
|
|
* @param inBackground optional, don't focus tab after opening
|
2018-11-22 05:47:31 -05:00
|
|
|
*/
|
2018-11-23 13:24:59 -05:00
|
|
|
void DatabaseTabWidget::addDatabaseTab(const QString& filePath, bool inBackground, const QString& password)
|
2011-11-13 08:55:20 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
QFileInfo fileInfo(filePath);
|
2012-07-08 04:15:05 -04:00
|
|
|
QString canonicalFilePath = fileInfo.canonicalFilePath();
|
2012-07-28 04:19:32 -04:00
|
|
|
if (canonicalFilePath.isEmpty()) {
|
2018-11-22 05:47:31 -05:00
|
|
|
emit messageGlobal(tr("The database file does not exist or is not accessible."), MessageWidget::Error);
|
2012-07-28 04:19:32 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
for (int i = 0, c = count(); i < c; ++i) {
|
|
|
|
auto* dbWidget = databaseWidgetFromIndex(i);
|
|
|
|
Q_ASSERT(dbWidget);
|
|
|
|
if (dbWidget && dbWidget->database()->filePath() == canonicalFilePath) {
|
2018-11-23 13:24:59 -05:00
|
|
|
if (!password.isEmpty()) {
|
|
|
|
dbWidget->performUnlockDatabase(password);
|
|
|
|
}
|
|
|
|
if (!inBackground) {
|
|
|
|
// switch to existing tab if file is already open
|
|
|
|
setCurrentIndex(indexOf(dbWidget));
|
|
|
|
}
|
2012-07-08 04:15:05 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto* dbWidget = new DatabaseWidget(QSharedPointer<Database>::create(filePath), this);
|
2018-11-23 13:24:59 -05:00
|
|
|
addDatabaseTab(dbWidget, inBackground);
|
|
|
|
if (!password.isEmpty()) {
|
|
|
|
dbWidget->performUnlockDatabase(password);
|
|
|
|
}
|
2018-11-22 05:47:31 -05:00
|
|
|
updateLastDatabases(filePath);
|
|
|
|
}
|
2015-01-21 11:19:05 -05:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Add a new database tab containing the given DatabaseWidget
|
|
|
|
* @param filePath
|
2018-11-23 13:24:59 -05:00
|
|
|
* @param inBackground optional, don't focus tab after opening
|
2018-11-22 05:47:31 -05:00
|
|
|
*/
|
2018-11-23 13:24:59 -05:00
|
|
|
void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget, bool inBackground)
|
2018-11-22 05:47:31 -05:00
|
|
|
{
|
2018-11-23 13:24:59 -05:00
|
|
|
Q_ASSERT(dbWidget->database());
|
2012-06-29 17:02:44 -04:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
int index = addTab(dbWidget, "");
|
|
|
|
updateTabName(index);
|
|
|
|
toggleTabbar();
|
2018-03-31 16:01:30 -04:00
|
|
|
|
2018-11-23 13:24:59 -05:00
|
|
|
if (!inBackground) {
|
|
|
|
setCurrentIndex(index);
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
connect(dbWidget, SIGNAL(databaseFilePathChanged(QString,QString)), SLOT(updateTabName()));
|
2018-11-23 13:24:59 -05:00
|
|
|
connect(dbWidget, SIGNAL(requestOpenDatabase(QString,bool,QString)), SLOT(addDatabaseTab(QString,bool,QString)));
|
2018-11-22 05:47:31 -05:00
|
|
|
connect(dbWidget, SIGNAL(closeRequest()), SLOT(closeDatabaseTabFromSender()));
|
|
|
|
connect(dbWidget, SIGNAL(databaseModified()), SLOT(updateTabName()));
|
|
|
|
connect(dbWidget, SIGNAL(databaseSaved()), SLOT(updateTabName()));
|
|
|
|
connect(dbWidget, SIGNAL(databaseUnlocked()), SLOT(updateTabName()));
|
|
|
|
connect(dbWidget, SIGNAL(databaseUnlocked()), SLOT(emitDatabaseLockChanged()));
|
|
|
|
connect(dbWidget, SIGNAL(databaseLocked()), SLOT(updateTabName()));
|
|
|
|
connect(dbWidget, SIGNAL(databaseLocked()), SLOT(emitDatabaseLockChanged()));
|
2011-12-26 13:17:11 -05:00
|
|
|
}
|
2011-11-13 08:55:20 -05:00
|
|
|
|
2017-01-08 19:33:21 -05:00
|
|
|
void DatabaseTabWidget::importCsv()
|
|
|
|
{
|
2018-03-13 15:55:37 -04:00
|
|
|
QString filter = QString("%1 (*.csv);;%2 (*)").arg(tr("CSV file"), tr("All files"));
|
2018-11-22 05:47:31 -05:00
|
|
|
QString fileName = fileDialog()->getOpenFileName(this, tr("Select CSV file"), "", filter);
|
2017-01-08 19:33:21 -05:00
|
|
|
|
|
|
|
if (fileName.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto db = execNewDatabaseWizard();
|
2018-05-13 17:21:43 -04:00
|
|
|
if (!db) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto* dbWidget = new DatabaseWidget(db, this);
|
|
|
|
addDatabaseTab(dbWidget);
|
|
|
|
dbWidget->switchToCsvImport(fileName);
|
2017-01-08 19:33:21 -05:00
|
|
|
}
|
|
|
|
|
2016-11-07 22:37:42 -05:00
|
|
|
void DatabaseTabWidget::mergeDatabase()
|
|
|
|
{
|
2018-05-18 17:06:28 -04:00
|
|
|
auto dbWidget = currentDatabaseWidget();
|
2018-11-22 05:47:31 -05:00
|
|
|
if (dbWidget && !dbWidget->isLocked()) {
|
2018-05-18 17:06:28 -04:00
|
|
|
QString filter = QString("%1 (*.kdbx);;%2 (*)").arg(tr("KeePass 2 Database"), tr("All files"));
|
2018-10-31 23:27:38 -04:00
|
|
|
const QString fileName = fileDialog()->getOpenFileName(this, tr("Merge database"), QString(), filter);
|
2018-05-18 17:06:28 -04:00
|
|
|
if (!fileName.isEmpty()) {
|
|
|
|
mergeDatabase(fileName);
|
|
|
|
}
|
2016-11-07 22:37:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 18:57:41 -05:00
|
|
|
void DatabaseTabWidget::mergeDatabase(const QString& filePath)
|
2016-11-07 22:37:42 -05:00
|
|
|
{
|
2018-11-23 18:57:41 -05:00
|
|
|
unlockDatabaseInDialog(currentDatabaseWidget(), DatabaseOpenDialog::Intent::Merge, filePath);
|
2016-11-07 22:37:42 -05:00
|
|
|
}
|
|
|
|
|
2012-05-12 04:08:41 -04:00
|
|
|
void DatabaseTabWidget::importKeePass1Database()
|
|
|
|
{
|
2018-03-13 15:55:37 -04:00
|
|
|
QString filter = QString("%1 (*.kdb);;%2 (*)").arg(tr("KeePass 1 database"), tr("All files"));
|
2018-03-31 16:01:30 -04:00
|
|
|
QString fileName = fileDialog()->getOpenFileName(this, tr("Open KeePass 1 database"), QString(), filter);
|
2012-05-12 04:08:41 -04:00
|
|
|
|
|
|
|
if (fileName.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto db = QSharedPointer<Database>::create();
|
|
|
|
auto* dbWidget = new DatabaseWidget(db, this);
|
|
|
|
addDatabaseTab(dbWidget);
|
|
|
|
dbWidget->switchToImportKeepass1(fileName);
|
2011-11-13 08:55:20 -05:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Attempt to close the current database and remove its tab afterwards.
|
|
|
|
*
|
|
|
|
* @param index index of the database tab to close
|
|
|
|
* @return true if database was closed successully
|
|
|
|
*/
|
|
|
|
bool DatabaseTabWidget::closeCurrentDatabaseTab()
|
2011-11-13 08:55:20 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
return closeDatabaseTab(currentIndex());
|
2012-06-29 17:40:51 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Attempt to close the database tab that sent the close request.
|
|
|
|
*
|
|
|
|
* @param index index of the database tab to close
|
|
|
|
* @return true if database was closed successully
|
|
|
|
*/
|
|
|
|
bool DatabaseTabWidget::closeDatabaseTabFromSender()
|
2012-06-29 17:40:51 -04:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
return closeDatabaseTab(qobject_cast<DatabaseWidget*>(sender()));
|
2012-04-11 14:35:52 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Attempt to close a database and remove its tab afterwards.
|
|
|
|
*
|
|
|
|
* @param index index of the database tab to close
|
|
|
|
* @return true if database was closed successully
|
|
|
|
*/
|
|
|
|
bool DatabaseTabWidget::closeDatabaseTab(int index)
|
2012-06-29 18:22:07 -04:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
return closeDatabaseTab(qobject_cast<DatabaseWidget*>(widget(index)));
|
2011-11-13 08:55:20 -05:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Attempt to close a database and remove its tab afterwards.
|
|
|
|
*
|
|
|
|
* @param dbWidget \link DatabaseWidget to close
|
|
|
|
* @return true if database was closed successully
|
|
|
|
*/
|
|
|
|
bool DatabaseTabWidget::closeDatabaseTab(DatabaseWidget* dbWidget)
|
2011-11-16 12:47:17 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
int tabIndex = indexOf(dbWidget);
|
|
|
|
if (!dbWidget || tabIndex < 0) {
|
2018-05-13 17:21:43 -04:00
|
|
|
return false;
|
2011-12-24 13:22:42 -05:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
QString filePath = dbWidget->database()->filePath();
|
|
|
|
if (!dbWidget->close()) {
|
2017-11-26 22:30:18 -05:00
|
|
|
return false;
|
2015-01-11 10:20:59 -05:00
|
|
|
}
|
2018-11-22 05:47:31 -05:00
|
|
|
|
|
|
|
removeTab(tabIndex);
|
|
|
|
dbWidget->deleteLater();
|
|
|
|
toggleTabbar();
|
|
|
|
emit databaseClosed(filePath);
|
|
|
|
return true;
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Attempt to close all opened databases.
|
|
|
|
* The attempt will be aborted with the first database that cannot be closed.
|
|
|
|
*
|
|
|
|
* @return true if all databases could be closed.
|
|
|
|
*/
|
|
|
|
bool DatabaseTabWidget::closeAllDatabaseTabs()
|
2011-11-13 08:55:20 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
while (count() > 0) {
|
|
|
|
if (!closeDatabaseTab(0)) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
return true;
|
2011-11-13 08:55:20 -05:00
|
|
|
}
|
|
|
|
|
2015-01-11 10:20:59 -05:00
|
|
|
bool DatabaseTabWidget::saveDatabase(int index)
|
2011-11-16 12:47:17 -05:00
|
|
|
{
|
|
|
|
if (index == -1) {
|
2011-12-30 12:43:24 -05:00
|
|
|
index = currentIndex();
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
return databaseWidgetFromIndex(index)->save();
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
|
|
|
|
2015-01-11 10:20:59 -05:00
|
|
|
bool DatabaseTabWidget::saveDatabaseAs(int index)
|
2011-12-24 13:22:42 -05:00
|
|
|
{
|
|
|
|
if (index == -1) {
|
2011-12-30 12:43:24 -05:00
|
|
|
index = currentIndex();
|
2011-12-24 13:22:42 -05:00
|
|
|
}
|
2015-01-11 10:20:59 -05:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto* dbWidget = databaseWidgetFromIndex(index);
|
|
|
|
bool ok = dbWidget->saveAs();
|
|
|
|
if (ok) {
|
|
|
|
updateLastDatabases(dbWidget->database()->filePath());
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseTabWidget::closeDatabaseFromSender()
|
|
|
|
{
|
|
|
|
auto* dbWidget = qobject_cast<DatabaseWidget*>(sender());
|
|
|
|
Q_ASSERT(dbWidget);
|
|
|
|
closeDatabaseTab(dbWidget);
|
2011-12-24 13:22:42 -05:00
|
|
|
}
|
|
|
|
|
2015-07-14 16:14:34 -04:00
|
|
|
void DatabaseTabWidget::exportToCsv()
|
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
auto db = databaseWidgetFromIndex(currentIndex())->database();
|
2015-07-14 16:14:34 -04:00
|
|
|
if (!db) {
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-31 16:01:30 -04:00
|
|
|
QString fileName = fileDialog()->getSaveFileName(
|
2018-10-28 10:47:24 -04:00
|
|
|
this, tr("Export database to CSV file"), QString(), tr("CSV file").append(" (*.csv)"), nullptr, nullptr, "csv");
|
2015-07-14 16:14:34 -04:00
|
|
|
if (fileName.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CsvExporter csvExporter;
|
|
|
|
if (!csvExporter.exportDatabase(fileName, db)) {
|
2018-03-31 16:01:30 -04:00
|
|
|
emit messageGlobal(tr("Writing the CSV file failed.").append("\n").append(csvExporter.errorString()),
|
|
|
|
MessageWidget::Error);
|
2015-07-14 16:14:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-13 11:52:37 -05:00
|
|
|
void DatabaseTabWidget::changeMasterKey()
|
|
|
|
{
|
|
|
|
currentDatabaseWidget()->switchToMasterKeyChange();
|
|
|
|
}
|
|
|
|
|
2012-04-19 10:20:20 -04:00
|
|
|
void DatabaseTabWidget::changeDatabaseSettings()
|
|
|
|
{
|
|
|
|
currentDatabaseWidget()->switchToDatabaseSettings();
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
bool DatabaseTabWidget::isReadOnly(int index) const
|
2012-05-28 12:38:33 -04:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
if (count() == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-28 12:38:33 -04:00
|
|
|
if (index == -1) {
|
|
|
|
index = currentIndex();
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto db = databaseWidgetFromIndex(index)->database();
|
|
|
|
return db && db->isReadOnly();
|
2012-05-28 12:38:33 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
bool DatabaseTabWidget::isModified(int index) const
|
2017-10-24 13:23:55 -04:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
if (count() == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-24 13:23:55 -04:00
|
|
|
if (index == -1) {
|
|
|
|
index = currentIndex();
|
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto db = databaseWidgetFromIndex(index)->database();
|
|
|
|
return db && db->isModified();
|
2017-10-24 13:23:55 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
bool DatabaseTabWidget::canSave(int index) const
|
2017-03-20 22:29:36 -04:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
return !isReadOnly(index) && isModified(index);
|
2017-03-20 22:29:36 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
bool DatabaseTabWidget::hasLockableDatabases() const
|
2017-03-21 22:01:44 -04:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
for (int i = 0, c = count(); i < c; ++i) {
|
|
|
|
if (!databaseWidgetFromIndex(i)->isLocked()) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-03-21 22:01:44 -04:00
|
|
|
}
|
2018-11-22 05:47:31 -05:00
|
|
|
return false;
|
2017-03-21 22:01:44 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Get the tab's (original) display name without platform-specific
|
|
|
|
* mangling that may occur when reading back the actual widget's \link tabText()
|
|
|
|
*
|
|
|
|
* @param index tab index
|
|
|
|
* @return tab name
|
|
|
|
*/
|
|
|
|
QString DatabaseTabWidget::tabName(int index)
|
2011-11-13 08:55:20 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
if (index == -1 || index > count()) {
|
|
|
|
return "";
|
|
|
|
}
|
2011-11-13 08:55:20 -05:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
auto* dbWidget = databaseWidgetFromIndex(index);
|
|
|
|
|
|
|
|
auto db = dbWidget->database();
|
|
|
|
Q_ASSERT(db);
|
|
|
|
if (!db) {
|
|
|
|
return "";
|
|
|
|
}
|
2011-11-13 08:55:20 -05:00
|
|
|
|
|
|
|
QString tabName;
|
2011-11-16 12:47:17 -05:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
if (!db->filePath().isEmpty()) {
|
|
|
|
QFileInfo fileInfo(db->filePath());
|
|
|
|
|
2011-11-16 12:47:17 -05:00
|
|
|
if (db->metadata()->name().isEmpty()) {
|
2018-11-22 05:47:31 -05:00
|
|
|
tabName = fileInfo.fileName();
|
2017-11-26 16:58:54 -05:00
|
|
|
} else {
|
2011-12-25 13:47:41 -05:00
|
|
|
tabName = db->metadata()->name();
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
2011-12-25 13:47:41 -05:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
setTabToolTip(index, fileInfo.absoluteFilePath());
|
2017-11-26 16:58:54 -05:00
|
|
|
} else {
|
2011-11-16 12:47:17 -05:00
|
|
|
if (db->metadata()->name().isEmpty()) {
|
2018-11-22 05:47:31 -05:00
|
|
|
tabName = tr("New Database");
|
2017-11-26 16:58:54 -05:00
|
|
|
} else {
|
2018-11-22 05:47:31 -05:00
|
|
|
tabName = tr("%1 [New Database]", "Database tab name modifier").arg(db->metadata()->name());
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
2011-11-13 08:55:20 -05:00
|
|
|
}
|
2012-10-12 06:12:00 -04:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
if (dbWidget->isLocked()) {
|
|
|
|
tabName = tr("%1 [Locked]", "Database tab name modifier").arg(tabName);
|
2012-10-12 06:12:00 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
if (db->isReadOnly()) {
|
|
|
|
tabName = tr("%1 [Read-only]", "Database tab name modifier").arg(tabName);
|
2012-04-11 14:35:52 -04:00
|
|
|
}
|
2012-10-12 06:12:00 -04:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
if (db->isModified()) {
|
|
|
|
tabName.append("*");
|
2011-11-13 08:55:20 -05:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
return tabName;
|
2011-11-13 08:55:20 -05:00
|
|
|
}
|
2011-11-16 12:47:17 -05:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
/**
|
|
|
|
* Update of the given tab index or of the sending
|
|
|
|
* DatabaseWidget if `index` == -1.
|
|
|
|
*/
|
|
|
|
void DatabaseTabWidget::updateTabName(int index)
|
2012-05-28 12:38:33 -04:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
auto* dbWidget = databaseWidgetFromIndex(index);
|
|
|
|
if (!dbWidget) {
|
|
|
|
dbWidget = qobject_cast<DatabaseWidget*>(sender());
|
2012-05-28 12:38:33 -04:00
|
|
|
}
|
2018-11-22 05:47:31 -05:00
|
|
|
Q_ASSERT(dbWidget);
|
|
|
|
if (!dbWidget) {
|
|
|
|
return;
|
2012-04-11 14:35:52 -04:00
|
|
|
}
|
2018-11-22 05:47:31 -05:00
|
|
|
index = indexOf(dbWidget);
|
|
|
|
setTabText(index, tabName(index));
|
|
|
|
emit tabNameChanged();
|
2012-04-11 14:35:52 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
DatabaseWidget* DatabaseTabWidget::databaseWidgetFromIndex(int index) const
|
2011-11-16 12:47:17 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
return qobject_cast<DatabaseWidget*>(widget(index));
|
2011-11-16 12:47:17 -05:00
|
|
|
}
|
2011-12-29 14:03:20 -05:00
|
|
|
|
2011-12-30 12:43:24 -05:00
|
|
|
DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget()
|
2011-12-29 14:03:20 -05:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
return qobject_cast<DatabaseWidget*>(currentWidget());
|
2012-10-12 06:12:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseTabWidget::lockDatabases()
|
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
for (int i = 0, c = count(); i < c; ++i) {
|
|
|
|
if (!databaseWidgetFromIndex(i)->lock()) {
|
|
|
|
return;
|
2015-01-11 10:20:59 -05:00
|
|
|
}
|
2012-10-12 06:12:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 18:57:41 -05:00
|
|
|
/**
|
|
|
|
* Unlock a database with an unlock popup dialog.
|
|
|
|
*
|
|
|
|
* @param dbWidget DatabaseWidget which to connect signals to
|
|
|
|
* @param intent intent for unlocking
|
|
|
|
*/
|
|
|
|
void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent)
|
|
|
|
{
|
|
|
|
unlockDatabaseInDialog(dbWidget, intent, dbWidget->database()->filePath());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unlock a database with an unlock popup dialog.
|
|
|
|
*
|
|
|
|
* @param dbWidget DatabaseWidget which to connect signals to
|
|
|
|
* @param intent intent for unlocking
|
|
|
|
* @param file path of the database to be unlocked
|
|
|
|
*/
|
|
|
|
void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget, DatabaseOpenDialog::Intent intent,
|
|
|
|
const QString& filePath)
|
|
|
|
{
|
|
|
|
m_databaseOpenDialog->setTargetDatabaseWidget(dbWidget);
|
|
|
|
m_databaseOpenDialog->setIntent(intent);
|
|
|
|
m_databaseOpenDialog->setFilePath(filePath);
|
|
|
|
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
if (intent == DatabaseOpenDialog::Intent::AutoType) {
|
|
|
|
autoType()->raiseWindow();
|
|
|
|
Tools::wait(500);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_databaseOpenDialog->show();
|
|
|
|
m_databaseOpenDialog->raise();
|
|
|
|
m_databaseOpenDialog->activateWindow();
|
|
|
|
}
|
|
|
|
|
2018-02-05 07:25:16 -05:00
|
|
|
/**
|
|
|
|
* This function relock the pending database when autotype has been performed successfully
|
|
|
|
* A database is marked as pending when it's unlocked after a global Auto-Type invocation
|
|
|
|
*/
|
|
|
|
void DatabaseTabWidget::relockPendingDatabase()
|
|
|
|
{
|
2018-11-23 18:57:41 -05:00
|
|
|
if (!m_dbWidgetPendingLock || !config()->get("security/relockautotype").toBool()) {
|
2018-02-05 07:25:16 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-23 18:57:41 -05:00
|
|
|
if (m_dbWidgetPendingLock->isLocked() || !m_dbWidgetPendingLock->database()->hasKey()) {
|
|
|
|
m_dbWidgetPendingLock = nullptr;
|
2018-02-05 07:25:16 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-23 18:57:41 -05:00
|
|
|
m_dbWidgetPendingLock->lock();
|
|
|
|
m_dbWidgetPendingLock = nullptr;
|
2018-02-05 07:25:16 -05:00
|
|
|
}
|
|
|
|
|
2012-05-27 17:01:14 -04:00
|
|
|
void DatabaseTabWidget::updateLastDatabases(const QString& filename)
|
|
|
|
{
|
|
|
|
if (!config()->get("RememberLastDatabases").toBool()) {
|
|
|
|
config()->set("LastDatabases", QVariant());
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2012-05-27 17:01:14 -04:00
|
|
|
QStringList lastDatabases = config()->get("LastDatabases", QVariant()).toStringList();
|
|
|
|
lastDatabases.prepend(filename);
|
|
|
|
lastDatabases.removeDuplicates();
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
while (lastDatabases.count() > config()->get("NumberOfRememberedLastDatabases").toInt()) {
|
2012-05-27 17:01:14 -04:00
|
|
|
lastDatabases.removeLast();
|
|
|
|
}
|
|
|
|
config()->set("LastDatabases", lastDatabases);
|
|
|
|
}
|
|
|
|
}
|
2012-06-28 03:21:15 -04:00
|
|
|
|
2014-05-17 05:16:27 -04:00
|
|
|
void DatabaseTabWidget::emitActivateDatabaseChanged()
|
|
|
|
{
|
2017-03-10 09:58:42 -05:00
|
|
|
emit activateDatabaseChanged(currentDatabaseWidget());
|
2014-05-17 05:16:27 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
void DatabaseTabWidget::emitDatabaseLockChanged()
|
2016-10-08 12:55:05 -04:00
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
auto* dbWidget = qobject_cast<DatabaseWidget*>(sender());
|
|
|
|
Q_ASSERT(dbWidget);
|
|
|
|
if (!dbWidget) {
|
|
|
|
return;
|
2012-06-28 03:21:15 -04:00
|
|
|
}
|
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
if (dbWidget->isLocked()) {
|
|
|
|
emit databaseLocked(dbWidget);
|
|
|
|
} else {
|
|
|
|
emit databaseUnlocked(dbWidget);
|
|
|
|
}
|
2012-06-28 03:21:15 -04:00
|
|
|
}
|
2012-07-14 13:09:28 -04:00
|
|
|
|
|
|
|
void DatabaseTabWidget::performGlobalAutoType()
|
|
|
|
{
|
2018-11-22 05:47:31 -05:00
|
|
|
QList<QSharedPointer<Database>> unlockedDatabases;
|
2014-04-12 09:29:03 -04:00
|
|
|
|
2018-11-22 05:47:31 -05:00
|
|
|
for (int i = 0, c = count(); i < c; ++i) {
|
|
|
|
auto* dbWidget = databaseWidgetFromIndex(i);
|
|
|
|
if (!dbWidget->isLocked()) {
|
|
|
|
unlockedDatabases.append(dbWidget->database());
|
2014-04-12 09:29:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 18:57:41 -05:00
|
|
|
// TODO: allow for database selection during Auto-Type instead of using the current tab
|
2018-11-22 05:47:31 -05:00
|
|
|
if (!unlockedDatabases.isEmpty()) {
|
2016-11-11 16:26:07 -05:00
|
|
|
autoType()->performGlobalAutoType(unlockedDatabases);
|
2018-11-22 05:47:31 -05:00
|
|
|
} else if (count() > 0) {
|
2018-11-23 18:57:41 -05:00
|
|
|
if (config()->get("security/relockautotype").toBool()) {
|
|
|
|
m_dbWidgetPendingLock = currentDatabaseWidget();
|
|
|
|
}
|
|
|
|
unlockDatabaseInDialog(currentDatabaseWidget(), DatabaseOpenDialog::Intent::AutoType);
|
2016-11-11 16:26:07 -05:00
|
|
|
}
|
2012-07-14 13:09:28 -04:00
|
|
|
}
|