keepassxc/src/gui/DatabaseWidget.cpp

1184 lines
33 KiB
C++
Raw Normal View History

2010-08-24 16:26:52 -04:00
/*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
*
* 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 "DatabaseWidget.h"
#include <QAction>
#include <QDesktopServices>
#include <QHBoxLayout>
#include <QLabel>
#include <QFile>
#include <QLineEdit>
#include <QKeyEvent>
#include <QSplitter>
#include <QLabel>
2015-05-12 12:25:43 -04:00
#include <QProcess>
#include <QHeaderView>
#include <QApplication>
2010-08-24 16:26:52 -04:00
2012-07-12 16:35:51 -04:00
#include "autotype/AutoType.h"
#include "core/Config.h"
2014-05-15 17:48:54 -04:00
#include "core/EntrySearcher.h"
2012-07-18 14:44:28 -04:00
#include "core/FilePath.h"
2013-10-29 22:00:02 -04:00
#include "core/Group.h"
#include "core/Metadata.h"
#include "core/Tools.h"
#include "format/KeePass2Reader.h"
#include "gui/ChangeMasterKeyWidget.h"
#include "gui/Clipboard.h"
#include "gui/DatabaseOpenWidget.h"
#include "gui/DatabaseSettingsWidget.h"
#include "gui/KeePass1OpenWidget.h"
#include "gui/MessageBox.h"
2012-10-12 06:12:00 -04:00
#include "gui/UnlockDatabaseWidget.h"
#include "gui/UnlockDatabaseDialog.h"
#include "gui/entry/EditEntryWidget.h"
#include "gui/entry/EntryView.h"
#include "gui/group/EditGroupWidget.h"
#include "gui/group/GroupView.h"
2010-08-24 16:26:52 -04:00
DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
2010-10-06 13:40:50 -04:00
: QStackedWidget(parent)
, m_db(db)
2015-07-24 12:28:12 -04:00
, m_newGroup(nullptr)
, m_newEntry(nullptr)
, m_newParent(nullptr)
2010-08-24 16:26:52 -04:00
{
2010-10-06 13:40:50 -04:00
m_mainWidget = new QWidget(this);
QLayout* layout = new QHBoxLayout(m_mainWidget);
m_splitter = new QSplitter(m_mainWidget);
m_splitter->setChildrenCollapsible(false);
QWidget* rightHandSideWidget = new QWidget(m_splitter);
m_groupView = new GroupView(db, m_splitter);
m_groupView->setObjectName("groupView");
m_groupView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_groupView, SIGNAL(customContextMenuRequested(QPoint)),
SLOT(emitGroupContextMenuRequested(QPoint)));
m_entryView = new EntryView(rightHandSideWidget);
m_entryView->setObjectName("entryView");
m_entryView->setContextMenuPolicy(Qt::CustomContextMenu);
m_entryView->setGroup(db->rootGroup());
connect(m_entryView, SIGNAL(customContextMenuRequested(QPoint)),
SLOT(emitEntryContextMenuRequested(QPoint)));
2010-08-24 16:26:52 -04:00
// Add a notification for when we are searching
m_searchingLabel = new QLabel();
m_searchingLabel->setText(tr("Searching..."));
m_searchingLabel->setAlignment(Qt::AlignCenter);
m_searchingLabel->setStyleSheet("background-color: rgb(255, 253, 160);"
"border: 2px solid rgb(190, 190, 190);"
"border-radius: 5px;");
2012-05-16 03:56:18 -04:00
QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget);
2012-05-27 05:24:56 -04:00
vLayout->setMargin(0);
vLayout->addWidget(m_searchingLabel);
2012-05-12 07:22:41 -04:00
vLayout->addWidget(m_entryView);
2012-05-16 03:56:18 -04:00
m_searchingLabel->setVisible(false);
2012-05-16 03:56:18 -04:00
rightHandSideWidget->setLayout(vLayout);
2014-05-16 13:49:58 -04:00
setTabOrder(m_entryView, m_groupView);
m_splitter->addWidget(m_groupView);
m_splitter->addWidget(rightHandSideWidget);
m_splitter->setStretchFactor(0, 30);
m_splitter->setStretchFactor(1, 70);
layout->addWidget(m_splitter);
2010-10-06 13:40:50 -04:00
m_mainWidget->setLayout(layout);
m_editEntryWidget = new EditEntryWidget();
m_editEntryWidget->setObjectName("editEntryWidget");
2012-05-15 14:12:05 -04:00
m_historyEditEntryWidget = new EditEntryWidget();
m_editGroupWidget = new EditGroupWidget();
m_editGroupWidget->setObjectName("editGroupWidget");
m_changeMasterKeyWidget = new ChangeMasterKeyWidget();
m_changeMasterKeyWidget->headlineLabel()->setText(tr("Change master key"));
QFont headlineLabelFont = m_changeMasterKeyWidget->headlineLabel()->font();
headlineLabelFont.setBold(true);
headlineLabelFont.setPointSize(headlineLabelFont.pointSize() + 2);
m_changeMasterKeyWidget->headlineLabel()->setFont(headlineLabelFont);
m_databaseSettingsWidget = new DatabaseSettingsWidget();
m_databaseSettingsWidget->setObjectName("databaseSettingsWidget");
m_databaseOpenWidget = new DatabaseOpenWidget();
m_databaseOpenWidget->setObjectName("databaseOpenWidget");
m_databaseOpenMergeWidget = new DatabaseOpenWidget();
m_databaseOpenMergeWidget->setObjectName("databaseOpenMergeWidget");
m_keepass1OpenWidget = new KeePass1OpenWidget();
m_keepass1OpenWidget->setObjectName("keepass1OpenWidget");
2012-10-12 06:12:00 -04:00
m_unlockDatabaseWidget = new UnlockDatabaseWidget();
m_unlockDatabaseWidget->setObjectName("unlockDatabaseWidget");
m_unlockDatabaseDialog = new UnlockDatabaseDialog();
m_unlockDatabaseDialog->setObjectName("unlockDatabaseDialog");
2010-10-06 13:40:50 -04:00
addWidget(m_mainWidget);
addWidget(m_editEntryWidget);
addWidget(m_editGroupWidget);
addWidget(m_changeMasterKeyWidget);
addWidget(m_databaseSettingsWidget);
2012-05-15 14:12:05 -04:00
addWidget(m_historyEditEntryWidget);
addWidget(m_databaseOpenWidget);
addWidget(m_databaseOpenMergeWidget);
addWidget(m_keepass1OpenWidget);
2012-10-12 06:12:00 -04:00
addWidget(m_unlockDatabaseWidget);
2010-10-06 13:40:50 -04:00
connect(m_splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterSizesChanged()));
connect(m_entryView->header(), SIGNAL(sectionResized(int,int,int)), SIGNAL(entryColumnSizesChanged()));
connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(onGroupChanged(Group*)));
connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged()));
connect(m_entryView, SIGNAL(entryActivated(Entry*, EntryModel::ModelColumn)),
SLOT(entryActivationSignalReceived(Entry*, EntryModel::ModelColumn)));
connect(m_entryView, SIGNAL(entrySelectionChanged()), SIGNAL(entrySelectionChanged()));
connect(m_editEntryWidget, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
2012-05-15 14:12:05 -04:00
connect(m_editEntryWidget, SIGNAL(historyEntryActivated(Entry*)), SLOT(switchToHistoryView(Entry*)));
connect(m_historyEditEntryWidget, SIGNAL(editFinished(bool)), SLOT(switchBackToEntryEdit()));
connect(m_editGroupWidget, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
connect(m_changeMasterKeyWidget, SIGNAL(editFinished(bool)), SLOT(updateMasterKey(bool)));
connect(m_databaseSettingsWidget, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
connect(m_databaseOpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
connect(m_databaseOpenMergeWidget, SIGNAL(editFinished(bool)), SLOT(mergeDatabase(bool)));
connect(m_keepass1OpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
2012-10-12 06:12:00 -04:00
connect(m_unlockDatabaseWidget, SIGNAL(editFinished(bool)), SLOT(unlockDatabase(bool)));
connect(m_unlockDatabaseDialog, SIGNAL(unlockDone(bool)), SLOT(unlockDatabase(bool)));
connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onWatchedFileChanged()));
connect(&m_fileWatchTimer, SIGNAL(timeout()), this, SLOT(reloadDatabaseFile()));
connect(&m_ignoreWatchTimer, SIGNAL(timeout()), this, SLOT(onWatchedFileChanged()));
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
m_databaseModified = false;
m_fileWatchTimer.setSingleShot(true);
m_ignoreWatchTimer.setSingleShot(true);
m_ignoreNextAutoreload = false;
m_searchCaseSensitive = false;
2010-10-06 13:40:50 -04:00
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_mainWidget);
2010-10-06 13:40:50 -04:00
}
DatabaseWidget::~DatabaseWidget()
{
}
DatabaseWidget::Mode DatabaseWidget::currentMode() const
{
2015-07-24 12:28:12 -04:00
if (currentWidget() == nullptr) {
2012-04-25 14:22:55 -04:00
return DatabaseWidget::None;
2012-10-12 06:12:00 -04:00
}
else if (currentWidget() == m_mainWidget) {
2012-04-25 14:22:55 -04:00
return DatabaseWidget::ViewMode;
2012-10-12 06:12:00 -04:00
}
else if (currentWidget() == m_unlockDatabaseWidget ||
currentWidget() == m_databaseOpenWidget) {
2012-10-12 06:12:00 -04:00
return DatabaseWidget::LockedMode;
}
else {
2012-04-25 14:22:55 -04:00
return DatabaseWidget::EditMode;
}
}
bool DatabaseWidget::isInEditMode() const
{
return currentMode() == DatabaseWidget::EditMode;
}
bool DatabaseWidget::isEditWidgetModified() const
{
if (currentWidget() == m_editEntryWidget) {
return m_editEntryWidget->hasBeenModified();
}
else {
// other edit widget don't have a hasBeenModified() method yet
// assume that they already have been modified
return true;
}
}
QList<int> DatabaseWidget::splitterSizes() const
{
return m_splitter->sizes();
}
void DatabaseWidget::setSplitterSizes(const QList<int>& sizes)
{
m_splitter->setSizes(sizes);
}
QList<int> DatabaseWidget::entryHeaderViewSizes() const
{
QList<int> sizes;
for (int i = 0; i < m_entryView->header()->count(); i++) {
sizes.append(m_entryView->header()->sectionSize(i));
}
return sizes;
}
void DatabaseWidget::setEntryViewHeaderSizes(const QList<int>& sizes)
{
if (sizes.size() != m_entryView->header()->count()) {
Q_ASSERT(false);
return;
}
for (int i = 0; i < sizes.size(); i++) {
m_entryView->header()->resizeSection(i, sizes[i]);
}
}
void DatabaseWidget::clearAllWidgets()
{
m_editEntryWidget->clear();
m_historyEditEntryWidget->clear();
m_editGroupWidget->clear();
}
void DatabaseWidget::emitCurrentModeChanged()
{
Q_EMIT currentModeChanged(currentMode());
}
2012-07-06 13:21:19 -04:00
Database* DatabaseWidget::database()
{
return m_db;
}
2011-12-27 10:04:59 -05:00
void DatabaseWidget::createEntry()
{
if (!m_groupView->currentGroup()) {
Q_ASSERT(false);
return;
}
2011-12-27 10:04:59 -05:00
m_newEntry = new Entry();
m_newEntry->setUuid(Uuid::random());
2012-04-23 14:10:07 -04:00
m_newEntry->setUsername(m_db->metadata()->defaultUserName());
2011-12-27 10:04:59 -05:00
m_newParent = m_groupView->currentGroup();
setIconFromParent();
switchToEntryEdit(m_newEntry, true);
}
void DatabaseWidget::setIconFromParent()
{
if (!config()->get("UseGroupIconOnEntryCreation").toBool()) {
return;
}
if (m_newParent->iconNumber() == Group::DefaultIconNumber && m_newParent->iconUuid().isNull()) {
return;
}
if (m_newParent->iconUuid().isNull()) {
2013-10-29 22:00:02 -04:00
m_newEntry->setIcon(m_newParent->iconNumber());
}
else {
m_newEntry->setIcon(m_newParent->iconUuid());
}
2011-12-27 10:04:59 -05:00
}
void DatabaseWidget::replaceDatabase(Database* db)
{
Database* oldDb = m_db;
m_db = db;
m_groupView->changeDatabase(m_db);
Q_EMIT databaseChanged(m_db, m_databaseModified);
delete oldDb;
}
2012-05-15 15:10:39 -04:00
void DatabaseWidget::cloneEntry()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
Entry* entry = currentEntry->clone(Entry::CloneNewUuid | Entry::CloneResetTimeInfo | Entry::CloneRenameTitle);
2012-05-15 15:10:39 -04:00
entry->setGroup(currentEntry->group());
if (isInSearchMode())
search(m_lastSearchText);
2012-05-15 15:10:39 -04:00
m_entryView->setFocus();
m_entryView->setCurrentEntry(entry);
}
2013-10-08 15:36:01 -04:00
void DatabaseWidget::deleteEntries()
2012-04-18 14:08:54 -04:00
{
2013-10-08 15:36:01 -04:00
const QModelIndexList selected = m_entryView->selectionModel()->selectedRows();
if (selected.isEmpty()) {
Q_ASSERT(false);
return;
}
2013-10-08 15:36:01 -04:00
// get all entry pointers as the indexes change when removing multiple entries
QList<Entry*> selectedEntries;
for (const QModelIndex& index : selected) {
2013-10-08 15:36:01 -04:00
selectedEntries.append(m_entryView->entryFromIndex(index));
}
2016-12-01 23:04:42 -05:00
bool inRecycleBin = Tools::hasChild(m_db->metadata()->recycleBin(), selectedEntries.first());
if (inRecycleBin || !m_db->metadata()->recycleBinEnabled()) {
2013-10-08 15:36:01 -04:00
QMessageBox::StandardButton result;
if (selected.size() == 1) {
result = MessageBox::question(
2013-10-08 15:36:01 -04:00
this, tr("Delete entry?"),
tr("Do you really want to delete the entry \"%1\" for good?")
.arg(selectedEntries.first()->title()),
QMessageBox::Yes | QMessageBox::No);
}
else {
result = MessageBox::question(
2013-10-08 15:36:01 -04:00
this, tr("Delete entries?"),
tr("Do you really want to delete %1 entries for good?")
.arg(selected.size()),
QMessageBox::Yes | QMessageBox::No);
}
if (result == QMessageBox::Yes) {
for (Entry* entry : asConst(selectedEntries)) {
2013-10-08 15:36:01 -04:00
delete entry;
}
}
}
else {
QMessageBox::StandardButton result;
if (selected.size() == 1) {
result = MessageBox::question(
this, tr("Move entry to recycle bin?"),
tr("Do you really want to move entry \"%1\" to the recycle bin?")
.arg(selectedEntries.first()->title()),
QMessageBox::Yes | QMessageBox::No);
}
else {
result = MessageBox::question(
2013-10-08 15:36:01 -04:00
this, tr("Move entries to recycle bin?"),
2014-05-17 12:15:06 -04:00
tr("Do you really want to move %n entry(s) to the recycle bin?", 0, selected.size()),
2013-10-08 15:36:01 -04:00
QMessageBox::Yes | QMessageBox::No);
}
if (result == QMessageBox::No) {
return;
2013-10-08 15:36:01 -04:00
}
for (Entry* entry : asConst(selectedEntries)) {
2013-10-08 15:36:01 -04:00
m_db->recycleEntry(entry);
}
}
2012-04-18 14:08:54 -04:00
}
void DatabaseWidget::setFocus()
{
m_entryView->setFocus();
}
void DatabaseWidget::copyTitle()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
2013-12-01 03:59:43 -05:00
setClipboardTextAndMinimize(currentEntry->title());
}
void DatabaseWidget::copyUsername()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
2013-12-01 03:59:43 -05:00
setClipboardTextAndMinimize(currentEntry->username());
}
void DatabaseWidget::copyPassword()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
2013-12-01 03:59:43 -05:00
setClipboardTextAndMinimize(currentEntry->password());
}
void DatabaseWidget::copyURL()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
2013-12-01 03:59:43 -05:00
setClipboardTextAndMinimize(currentEntry->url());
}
void DatabaseWidget::copyNotes()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
2013-12-01 03:59:43 -05:00
setClipboardTextAndMinimize(currentEntry->notes());
}
void DatabaseWidget::copyAttribute(QAction* action)
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
2013-12-01 03:59:43 -05:00
setClipboardTextAndMinimize(currentEntry->attributes()->value(action->text()));
}
2013-12-01 03:59:43 -05:00
void DatabaseWidget::setClipboardTextAndMinimize(const QString& text)
{
clipboard()->setText(text);
if (config()->get("MinimizeOnCopy").toBool()) {
window()->showMinimized();
}
}
2012-07-12 16:35:51 -04:00
void DatabaseWidget::performAutoType()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
autoType()->performAutoType(currentEntry, window());
}
void DatabaseWidget::openUrl()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return;
}
openUrlForEntry(currentEntry);
}
void DatabaseWidget::openUrlForEntry(Entry* entry)
{
QString urlString = entry->resolveMultiplePlaceholders(entry->url());
2015-07-13 16:36:20 -04:00
if (urlString.isEmpty()) {
2015-05-12 12:25:43 -04:00
return;
2015-07-13 16:36:20 -04:00
}
2015-05-12 12:25:43 -04:00
if (urlString.startsWith("cmd://")) {
if (urlString.length() > 6) {
QProcess::startDetached(urlString.mid(6));
}
}
2015-07-13 16:36:20 -04:00
else {
QUrl url = QUrl::fromUserInput(urlString);
QDesktopServices::openUrl(url);
}
}
void DatabaseWidget::createGroup()
{
if (!m_groupView->currentGroup()) {
Q_ASSERT(false);
return;
}
m_newGroup = new Group();
m_newGroup->setUuid(Uuid::random());
2011-12-27 10:04:59 -05:00
m_newParent = m_groupView->currentGroup();
switchToGroupEdit(m_newGroup, true);
}
2012-04-21 13:06:28 -04:00
void DatabaseWidget::deleteGroup()
{
Group* currentGroup = m_groupView->currentGroup();
if (!currentGroup || !canDeleteCurrentGroup()) {
Q_ASSERT(false);
return;
}
2012-04-21 13:06:28 -04:00
2016-12-01 23:04:42 -05:00
bool inRecycleBin = Tools::hasChild(m_db->metadata()->recycleBin(), currentGroup);
bool isRecycleBin = (currentGroup == m_db->metadata()->recycleBin());
bool isRecycleBinSubgroup = Tools::hasChild(currentGroup, m_db->metadata()->recycleBin());
2016-12-01 23:04:42 -05:00
if (inRecycleBin || isRecycleBin || isRecycleBinSubgroup || !m_db->metadata()->recycleBinEnabled()) {
QMessageBox::StandardButton result = MessageBox::question(
this, tr("Delete group?"),
tr("Do you really want to delete the group \"%1\" for good?")
.arg(currentGroup->name()),
2012-04-21 13:06:28 -04:00
QMessageBox::Yes | QMessageBox::No);
if (result == QMessageBox::Yes) {
delete currentGroup;
2012-04-21 13:06:28 -04:00
}
}
else {
m_db->recycleGroup(currentGroup);
2012-04-21 13:06:28 -04:00
}
}
int DatabaseWidget::addWidget(QWidget* w)
{
w->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
int index = QStackedWidget::addWidget(w);
adjustSize();
return index;
}
void DatabaseWidget::setCurrentIndex(int index)
2012-10-12 06:12:00 -04:00
{
// use setCurrentWidget() instead
// index is not reliable
Q_UNUSED(index);
Q_ASSERT(false);
}
void DatabaseWidget::setCurrentWidget(QWidget* widget)
{
if (currentWidget()) {
currentWidget()->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}
2012-10-12 06:12:00 -04:00
QStackedWidget::setCurrentWidget(widget);
if (currentWidget()) {
currentWidget()->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
}
adjustSize();
}
void DatabaseWidget::switchToView(bool accepted)
{
if (m_newGroup) {
if (accepted) {
2011-12-27 10:04:59 -05:00
m_newGroup->setParent(m_newParent);
m_groupView->setCurrentGroup(m_newGroup);
m_groupView->expandGroup(m_newParent);
}
else {
delete m_newGroup;
}
2015-07-24 12:28:12 -04:00
m_newGroup = nullptr;
m_newParent = nullptr;
2011-12-27 10:04:59 -05:00
}
else if (m_newEntry) {
if (accepted) {
m_newEntry->setGroup(m_newParent);
2012-05-02 13:33:37 -04:00
m_entryView->setFocus();
m_entryView->setCurrentEntry(m_newEntry);
2011-12-27 10:04:59 -05:00
}
else {
delete m_newEntry;
}
2015-07-24 12:28:12 -04:00
m_newEntry = nullptr;
m_newParent = nullptr;
}
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_mainWidget);
2010-10-06 13:40:50 -04:00
}
2012-05-15 14:12:05 -04:00
void DatabaseWidget::switchToHistoryView(Entry* entry)
{
2012-10-21 15:45:54 -04:00
m_historyEditEntryWidget->loadEntry(entry, false, true, m_editEntryWidget->entryTitle(), m_db);
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_historyEditEntryWidget);
2012-05-15 14:12:05 -04:00
}
void DatabaseWidget::switchBackToEntryEdit()
{
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_editEntryWidget);
2012-05-15 14:12:05 -04:00
}
void DatabaseWidget::switchToEntryEdit(Entry* entry)
2010-10-06 13:40:50 -04:00
{
2011-12-27 10:04:59 -05:00
switchToEntryEdit(entry, false);
}
void DatabaseWidget::switchToEntryEdit(Entry* entry, bool create)
{
Group* group = currentGroup();
Q_ASSERT(group);
m_editEntryWidget->loadEntry(entry, create, false, group->name(), m_db);
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_editEntryWidget);
2010-08-24 16:26:52 -04:00
}
void DatabaseWidget::switchToGroupEdit(Group* group, bool create)
{
m_editGroupWidget->loadGroup(group, create, m_db);
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_editGroupWidget);
}
void DatabaseWidget::updateMasterKey(bool accepted)
{
if (accepted) {
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
bool result = m_db->setKey(m_changeMasterKeyWidget->newMasterKey());
QApplication::restoreOverrideCursor();
if (!result) {
MessageBox::critical(this, tr("Error"), tr("Unable to calculate master key"));
return;
}
}
2012-04-16 18:22:44 -04:00
else if (!m_db->hasKey()) {
Q_EMIT closeRequest();
return;
}
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_mainWidget);
}
void DatabaseWidget::openDatabase(bool accepted)
{
if (accepted) {
replaceDatabase(static_cast<DatabaseOpenWidget*>(sender())->database());
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_mainWidget);
2016-10-08 12:55:05 -04:00
Q_EMIT unlockedDatabase();
// We won't need those anymore and KeePass1OpenWidget closes
// the file in its dtor.
delete m_databaseOpenWidget;
2015-07-24 12:28:12 -04:00
m_databaseOpenWidget = nullptr;
delete m_keepass1OpenWidget;
2015-07-24 12:28:12 -04:00
m_keepass1OpenWidget = nullptr;
m_fileWatcher.addPath(m_filename);
}
else {
m_fileWatcher.removePath(m_filename);
if (m_databaseOpenWidget->database()) {
delete m_databaseOpenWidget->database();
}
Q_EMIT closeRequest();
}
}
void DatabaseWidget::mergeDatabase(bool accepted)
{
if (accepted) {
if (!m_db) {
MessageBox::critical(this, tr("Error"), tr("No current database."));
return;
}
Database* srcDb = static_cast<DatabaseOpenWidget*>(sender())->database();
if (!srcDb) {
MessageBox::critical(this, tr("Error"), tr("No source database, nothing to do."));
return;
}
m_db->merge(srcDb);
}
setCurrentWidget(m_mainWidget);
Q_EMIT databaseMerged(m_db);
}
2012-10-12 06:12:00 -04:00
void DatabaseWidget::unlockDatabase(bool accepted)
{
if (!accepted) {
Q_EMIT closeRequest();
return;
}
Database *db = Q_NULLPTR;
if (sender() == m_unlockDatabaseDialog) {
db = m_unlockDatabaseDialog->database();
} else if (sender() == m_unlockDatabaseWidget) {
db = m_unlockDatabaseWidget->database();
}
replaceDatabase(db);
2012-10-12 06:12:00 -04:00
restoreGroupEntryFocus(m_groupBeforeLock, m_entryBeforeLock);
m_groupBeforeLock = Uuid();
m_entryBeforeLock = Uuid();
setCurrentWidget(m_mainWidget);
m_unlockDatabaseWidget->clearForms();
2012-10-12 06:12:00 -04:00
Q_EMIT unlockedDatabase();
if (sender() == m_unlockDatabaseDialog) {
QList<Database*> dbList;
dbList.append(m_db);
autoType()->performGlobalAutoType(dbList);
}
2012-10-12 06:12:00 -04:00
}
void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column)
{
if (column == EntryModel::Url && !entry->url().isEmpty()) {
openUrlForEntry(entry);
}
else {
switchToEntryEdit(entry);
}
}
void DatabaseWidget::switchToEntryEdit()
{
Entry* entry = m_entryView->currentEntry();
Q_ASSERT(entry);
if (!entry) {
return;
}
switchToEntryEdit(entry, false);
}
void DatabaseWidget::switchToGroupEdit()
{
Group* group = m_groupView->currentGroup();
Q_ASSERT(group);
if (!group) {
return;
}
switchToGroupEdit(group, false);
}
void DatabaseWidget::switchToMasterKeyChange()
{
m_changeMasterKeyWidget->clearForms();
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_changeMasterKeyWidget);
}
void DatabaseWidget::switchToDatabaseSettings()
{
m_databaseSettingsWidget->load(m_db);
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_databaseSettingsWidget);
}
2012-07-06 12:50:52 -04:00
void DatabaseWidget::switchToOpenDatabase(const QString& fileName)
{
2012-10-12 06:12:00 -04:00
updateFilename(fileName);
2012-07-06 12:50:52 -04:00
m_databaseOpenWidget->load(fileName);
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_databaseOpenWidget);
}
2012-07-06 12:50:52 -04:00
void DatabaseWidget::switchToOpenDatabase(const QString& fileName, const QString& password,
const QString& keyFile)
{
2012-10-12 06:12:00 -04:00
updateFilename(fileName);
2012-07-06 12:50:52 -04:00
switchToOpenDatabase(fileName);
m_databaseOpenWidget->enterKey(password, keyFile);
}
void DatabaseWidget::switchToOpenMergeDatabase(const QString& fileName)
{
m_databaseOpenMergeWidget->load(fileName);
setCurrentWidget(m_databaseOpenMergeWidget);
}
void DatabaseWidget::switchToOpenMergeDatabase(const QString& fileName, const QString& password,
const QString& keyFile)
{
switchToOpenMergeDatabase(fileName);
m_databaseOpenMergeWidget->enterKey(password, keyFile);
}
2012-07-06 12:50:52 -04:00
void DatabaseWidget::switchToImportKeepass1(const QString& fileName)
{
2012-10-12 06:12:00 -04:00
updateFilename(fileName);
2012-07-06 12:50:52 -04:00
m_keepass1OpenWidget->load(fileName);
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_keepass1OpenWidget);
}
void DatabaseWidget::databaseModified()
{
m_databaseModified = true;
}
void DatabaseWidget::databaseSaved()
{
m_databaseModified = false;
}
void DatabaseWidget::search(const QString& searchtext)
{
if (searchtext.isEmpty())
{
endSearch();
return;
}
Q_EMIT searchModeAboutToActivate();
Qt::CaseSensitivity caseSensitive = m_searchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
QList<Entry*> searchResult = EntrySearcher().search(searchtext, currentGroup(), caseSensitive);
2012-05-27 08:47:15 -04:00
m_entryView->setEntryList(searchResult);
m_lastSearchText = searchtext;
2012-05-27 08:47:15 -04:00
// Display a label detailing our search results
if (searchResult.size() > 0) {
m_searchingLabel->setText(tr("Search Results (%1)").arg(searchResult.size()));
}
else {
m_searchingLabel->setText(tr("No Results"));
}
m_searchingLabel->setVisible(true);
Q_EMIT searchModeActivated();
}
void DatabaseWidget::setSearchCaseSensitive(bool state)
2012-05-12 07:22:41 -04:00
{
m_searchCaseSensitive = state;
2012-05-27 08:47:15 -04:00
if (isInSearchMode())
search(m_lastSearchText);
}
2012-05-12 07:22:41 -04:00
void DatabaseWidget::onGroupChanged(Group* group)
{
// Intercept group changes if in search mode
if (isInSearchMode())
search(m_lastSearchText);
else
m_entryView->setGroup(group);
2012-05-12 07:22:41 -04:00
}
QString DatabaseWidget::getCurrentSearch()
{
return m_lastSearchText;
}
void DatabaseWidget::endSearch()
2012-05-27 07:19:12 -04:00
{
if (isInSearchMode())
{
Q_EMIT listModeAboutToActivate();
// Show the normal entry view of the current group
m_entryView->setGroup(currentGroup());
Q_EMIT listModeActivated();
2012-05-27 07:19:12 -04:00
}
m_searchingLabel->setVisible(false);
m_searchingLabel->setText(tr("Searching..."));
m_lastSearchText.clear();
2012-05-27 07:19:12 -04:00
}
void DatabaseWidget::emitGroupContextMenuRequested(const QPoint& pos)
{
Q_EMIT groupContextMenuRequested(m_groupView->viewport()->mapToGlobal(pos));
}
void DatabaseWidget::emitEntryContextMenuRequested(const QPoint& pos)
{
Q_EMIT entryContextMenuRequested(m_entryView->viewport()->mapToGlobal(pos));
}
2014-05-15 16:53:59 -04:00
bool DatabaseWidget::dbHasKey() const
{
return m_db->hasKey();
}
2012-04-21 13:06:28 -04:00
bool DatabaseWidget::canDeleteCurrentGroup() const
2012-04-21 13:06:28 -04:00
{
bool isRootGroup = m_db->rootGroup() == m_groupView->currentGroup();
return !isRootGroup;
2012-04-21 13:06:28 -04:00
}
2014-05-15 16:53:59 -04:00
bool DatabaseWidget::isInSearchMode() const
{
return m_entryView->inEntryListMode();
}
Group* DatabaseWidget::currentGroup() const
{
return m_groupView->currentGroup();
}
2012-10-12 06:12:00 -04:00
void DatabaseWidget::lock()
{
Q_ASSERT(currentMode() != DatabaseWidget::LockedMode);
if (m_groupView->currentGroup()) {
m_groupBeforeLock = m_groupView->currentGroup()->uuid();
}
else {
m_groupBeforeLock = m_db->rootGroup()->uuid();
}
2012-10-12 06:12:00 -04:00
if (m_entryView->currentEntry()) {
m_entryBeforeLock = m_entryView->currentEntry()->uuid();
}
clearAllWidgets();
m_unlockDatabaseWidget->load(m_filename);
2012-10-12 06:12:00 -04:00
setCurrentWidget(m_unlockDatabaseWidget);
Database* newDb = new Database();
newDb->metadata()->setName(m_db->metadata()->name());
replaceDatabase(newDb);
2012-10-12 06:12:00 -04:00
}
void DatabaseWidget::updateFilename(const QString& fileName)
{
if (! m_filename.isEmpty()) {
m_fileWatcher.removePath(m_filename);
}
m_fileWatcher.addPath(fileName);
2012-10-12 06:12:00 -04:00
m_filename = fileName;
}
void DatabaseWidget::ignoreNextAutoreload()
{
m_ignoreNextAutoreload = true;
m_ignoreWatchTimer.start(100);
}
void DatabaseWidget::onWatchedFileChanged()
{
if (m_ignoreNextAutoreload) {
// Reset the watch
m_ignoreNextAutoreload = false;
m_ignoreWatchTimer.stop();
m_fileWatcher.addPath(m_filename);
}
else {
if (m_fileWatchTimer.isActive())
return;
m_fileWatchTimer.start(500);
}
}
void DatabaseWidget::reloadDatabaseFile()
{
if (m_db == nullptr)
return;
if (! config()->get("AutoReloadOnChange").toBool()) {
// Ask if we want to reload the db
QMessageBox::StandardButton mb = MessageBox::question(this, tr("Autoreload Request"),
tr("The database file has changed. Do you want to load the changes?"),
QMessageBox::Yes | QMessageBox::No);
if (mb == QMessageBox::No) {
// Notify everyone the database does not match the file
emit m_db->modified();
m_databaseModified = true;
// Rewatch the database file
m_fileWatcher.addPath(m_filename);
return;
}
}
KeePass2Reader reader;
QFile file(m_filename);
if (file.open(QIODevice::ReadOnly)) {
Database* db = reader.readDatabase(&file, database()->key());
if (db != nullptr) {
if (m_databaseModified) {
// Ask if we want to merge changes into new database
QMessageBox::StandardButton mb = MessageBox::question(this, tr("Merge Request"),
tr("The database file has changed and you have unsaved changes."
"Do you want to merge your changes?"),
QMessageBox::Yes | QMessageBox::No);
if (mb == QMessageBox::Yes) {
// Merge the old database into the new one
m_db->setEmitModified(false);
db->merge(m_db);
}
else {
// Since we are accepting the new file as-is, internally mark as unmodified
// TODO: when saving is moved out of DatabaseTabWidget, this should be replaced
m_databaseModified = false;
}
}
Uuid groupBeforeReload;
if (m_groupView && m_groupView->currentGroup()) {
groupBeforeReload = m_groupView->currentGroup()->uuid();
}
else {
groupBeforeReload = m_db->rootGroup()->uuid();
}
Uuid entryBeforeReload;
if (m_entryView && m_entryView->currentEntry()) {
entryBeforeReload = m_entryView->currentEntry()->uuid();
}
replaceDatabase(db);
restoreGroupEntryFocus(groupBeforeReload, entryBeforeReload);
}
else {
MessageBox::critical(this, tr("Autoreload Failed"),
tr("Could not parse or unlock the new database file while attempting"
2017-01-11 21:12:43 -05:00
" to autoreload this database."));
}
}
else {
MessageBox::critical(this, tr("Autoreload Failed"),
tr("Could not open the new database file while attempting to autoreload"
2017-01-11 21:12:43 -05:00
" this database."));
}
// Rewatch the database file
m_fileWatcher.addPath(m_filename);
}
2014-05-15 16:53:59 -04:00
int DatabaseWidget::numberOfSelectedEntries() const
{
return m_entryView->numberOfSelectedEntries();
}
2014-05-15 16:53:59 -04:00
QStringList DatabaseWidget::customEntryAttributes() const
{
Entry* entry = m_entryView->currentEntry();
if (!entry) {
return QStringList();
}
return entry->attributes()->customKeys();
}
/*
* Restores the focus on the group and entry that was focused
* before the database was locked or reloaded.
*/
void DatabaseWidget::restoreGroupEntryFocus(Uuid groupUuid, Uuid entryUuid)
{
Group* restoredGroup = nullptr;
const QList<Group*> groups = m_db->rootGroup()->groupsRecursive(true);
for (Group* group : groups) {
if (group->uuid() == groupUuid) {
restoredGroup = group;
break;
}
}
if (restoredGroup != nullptr) {
m_groupView->setCurrentGroup(restoredGroup);
const QList<Entry*> entries = restoredGroup->entries();
for (Entry* entry : entries) {
if (entry->uuid() == entryUuid) {
m_entryView->setCurrentEntry(entry);
break;
}
}
}
}
2014-05-15 16:53:59 -04:00
bool DatabaseWidget::isGroupSelected() const
{
2015-07-24 12:28:12 -04:00
return m_groupView->currentGroup() != nullptr;
}
bool DatabaseWidget::currentEntryHasTitle()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return false;
}
return !currentEntry->title().isEmpty();
}
bool DatabaseWidget::currentEntryHasUsername()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return false;
}
return !currentEntry->username().isEmpty();
}
bool DatabaseWidget::currentEntryHasPassword()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return false;
}
return !currentEntry->password().isEmpty();
}
bool DatabaseWidget::currentEntryHasUrl()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return false;
}
return !currentEntry->url().isEmpty();
}
bool DatabaseWidget::currentEntryHasNotes()
{
Entry* currentEntry = m_entryView->currentEntry();
if (!currentEntry) {
Q_ASSERT(false);
return false;
}
return !currentEntry->notes().isEmpty();
}
GroupView* DatabaseWidget::groupView() {
return m_groupView;
}
EntryView* DatabaseWidget::entryView() {
return m_entryView;
}
void DatabaseWidget::showUnlockDialog()
{
m_unlockDatabaseDialog->clearForms();
m_unlockDatabaseDialog->setDBFilename(m_filename);
m_unlockDatabaseDialog->show();
m_unlockDatabaseDialog->activateWindow();
}
void DatabaseWidget::closeUnlockDialog()
{
m_unlockDatabaseDialog->close();
}