diff --git a/src/gui/ApplicationSettingsWidgetSecurity.ui b/src/gui/ApplicationSettingsWidgetSecurity.ui index 4052e5d630..344c2b81c9 100644 --- a/src/gui/ApplicationSettingsWidgetSecurity.ui +++ b/src/gui/ApplicationSettingsWidgetSecurity.ui @@ -175,7 +175,7 @@ - Show passwords in cleartext by default + Don't hide passwords when editing them diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 10f8c54b37..8e022a16d6 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1326,7 +1326,7 @@ bool DatabaseWidget::isGroupSelected() const bool DatabaseWidget::currentEntryHasFocus() { - return m_entryView->currentEntry() && m_entryView->hasFocus(); + return m_entryView->numberOfSelectedEntries() > 0 && m_entryView->hasFocus(); } bool DatabaseWidget::currentEntryHasTitle() diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index af6ffac33e..b9fa263837 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -47,12 +47,14 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent) // Entry m_ui->entryTotpButton->setIcon(filePath()->icon("actions", "chronometer")); m_ui->entryCloseButton->setIcon(filePath()->icon("actions", "dialog-close")); + m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); m_ui->entryAttachmentsWidget->setReadOnly(true); m_ui->entryAttachmentsWidget->setButtonsVisible(false); connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpWidget, SLOT(setVisible(bool))); connect(m_ui->entryCloseButton, SIGNAL(clicked()), SLOT(hide())); + connect(m_ui->togglePasswordButton, SIGNAL(clicked(bool)), SLOT(setPasswordVisible(bool))); connect(m_ui->entryTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection); connect(&m_totpTimer, SIGNAL(timeout()), this, SLOT(updateTotpLabel())); @@ -152,18 +154,40 @@ void EntryPreviewWidget::updateEntryTotp() } } +void EntryPreviewWidget::setPasswordVisible(bool state) +{ + const QString password = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()); + auto flags = m_ui->entryPasswordLabel->textInteractionFlags(); + if (state) { + m_ui->entryPasswordLabel->setRawText(password); + m_ui->entryPasswordLabel->setToolTip(password); + m_ui->entryPasswordLabel->setTextInteractionFlags(flags | Qt::TextSelectableByMouse); + } else { + m_ui->entryPasswordLabel->setTextInteractionFlags(flags & ~Qt::TextSelectableByMouse); + m_ui->entryPasswordLabel->setToolTip({}); + if (password.isEmpty() && config()->get("security/passwordemptynodots").toBool()) { + m_ui->entryPasswordLabel->setRawText(""); + } else { + m_ui->entryPasswordLabel->setRawText(QString("\u25cf").repeated(6)); + } + } +} + void EntryPreviewWidget::updateEntryGeneralTab() { Q_ASSERT(m_currentEntry); m_ui->entryUsernameLabel->setText(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->username())); - if (!config()->get("security/HidePasswordPreviewPanel").toBool()) { - const QString password = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()); - m_ui->entryPasswordLabel->setRawText(password); - m_ui->entryPasswordLabel->setToolTip(password); + if (config()->get("security/HidePasswordPreviewPanel").toBool()) { + // Hide password + setPasswordVisible(false); + // Show the password toggle button if there are dots in the label + m_ui->togglePasswordButton->setVisible(!m_ui->entryPasswordLabel->rawText().isEmpty()); + m_ui->togglePasswordButton->setChecked(false); } else { - m_ui->entryPasswordLabel->setRawText(QString("\u25cf").repeated(6)); - m_ui->entryPasswordLabel->setToolTip({}); + // Show password + setPasswordVisible(true); + m_ui->togglePasswordButton->setVisible(false); } m_ui->entryUrlLabel->setRawText(m_currentEntry->displayUrl()); diff --git a/src/gui/EntryPreviewWidget.h b/src/gui/EntryPreviewWidget.h index 88c729626b..9e687c6d8c 100644 --- a/src/gui/EntryPreviewWidget.h +++ b/src/gui/EntryPreviewWidget.h @@ -51,6 +51,7 @@ private slots: void updateEntryAttributesTab(); void updateEntryAttachmentsTab(); void updateEntryAutotypeTab(); + void setPasswordVisible(bool state); void updateGroupHeaderLine(); void updateGroupGeneralTab(); diff --git a/src/gui/EntryPreviewWidget.ui b/src/gui/EntryPreviewWidget.ui index 1fde8aa3ce..7e84d4120b 100644 --- a/src/gui/EntryPreviewWidget.ui +++ b/src/gui/EntryPreviewWidget.ui @@ -6,8 +6,8 @@ 0 0 - 280 - 267 + 573 + 330 @@ -168,7 +168,7 @@ - + 0 @@ -181,48 +181,48 @@ 0 - - - - - 0 - 0 - + + + + Qt::Horizontal - + + QSizePolicy::Fixed + + - 100 - 0 + 20 + 20 - + - - + + - + 0 0 - - - 100 - 0 - + + + 75 + true + - - PointingHandCursor + + Qt::LeftToRight - + Username - - Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + @@ -236,13 +236,16 @@ 0 + + username + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - + + 0 @@ -255,11 +258,8 @@ true - - Qt::LeftToRight - - Username + Password Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -288,7 +288,45 @@ - + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + PointingHandCursor + + + https://example.com + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse + + + + @@ -296,6 +334,9 @@ 0 + + expired + @@ -320,8 +361,47 @@ - - + + + + 6 + + + 4 + + + + + + + + true + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + password + + + + + + + Qt::Horizontal @@ -336,37 +416,34 @@ - - - - - 0 - 0 - - - - - 75 - true - + + + + Qt::Horizontal - - Password + + QSizePolicy::Fixed - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 20 + 20 + - + - - + + - Qt::Vertical + Qt::Horizontal + + + QSizePolicy::Fixed - 0 - 0 + 20 + 20 @@ -688,6 +765,12 @@ Qt::Vertical + + + 0 + 0 + + diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index ee4351a2f7..f35949afb4 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -200,12 +200,17 @@ MainWindow::MainWindow() m_ui->actionEntryOpenUrl->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_U); m_ui->actionEntryCopyURL->setShortcut(Qt::CTRL + Qt::Key_U); + // Control window state new QShortcut(Qt::CTRL + Qt::Key_M, this, SLOT(showMinimized())); 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())); new QShortcut(Qt::CTRL + Qt::Key_PageUp, this, SLOT(selectNextDatabaseTab())); new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab, this, SLOT(selectPreviousDatabaseTab())); new QShortcut(Qt::CTRL + Qt::Key_PageDown, this, SLOT(selectPreviousDatabaseTab())); + // 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")); @@ -717,6 +722,22 @@ void MainWindow::databaseTabChanged(int tabIndex) m_actionMultiplexer.setCurrentObject(m_ui->tabWidget->currentDatabaseWidget()); } +void MainWindow::togglePasswordsHidden() +{ + auto dbWidget = m_ui->tabWidget->currentDatabaseWidget(); + if (dbWidget) { + dbWidget->setPasswordsHidden(!dbWidget->isPasswordsHidden()); + } +} + +void MainWindow::toggleUsernamesHidden() +{ + auto dbWidget = m_ui->tabWidget->currentDatabaseWidget(); + if (dbWidget) { + dbWidget->setUsernamesHidden(!dbWidget->isUsernamesHidden()); + } +} + void MainWindow::closeEvent(QCloseEvent* event) { // ignore double close events (happens on macOS when closing from the dock) diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index d83b4a1ff6..b7ef04d427 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -112,6 +112,8 @@ private slots: void showErrorMessage(const QString& message); void selectNextDatabaseTab(); void selectPreviousDatabaseTab(); + void togglePasswordsHidden(); + void toggleUsernamesHidden(); private: static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index 0616374ac1..ebf6a20bec 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -468,9 +468,10 @@ bool EntryModel::isUsernamesHidden() const /** * Set state of 'Hide Usernames' setting and signal change */ -void EntryModel::setUsernamesHidden(const bool hide) +void EntryModel::setUsernamesHidden(bool hide) { m_hideUsernames = hide; + emit dataChanged(index(0, 0), index(rowCount()-1, columnCount() - 1)); emit usernamesHiddenChanged(); } @@ -485,28 +486,13 @@ bool EntryModel::isPasswordsHidden() const /** * Set state of 'Hide Passwords' setting and signal change */ -void EntryModel::setPasswordsHidden(const bool hide) +void EntryModel::setPasswordsHidden(bool hide) { m_hidePasswords = hide; + emit dataChanged(index(0, 0), index(rowCount()-1, columnCount() - 1)); emit passwordsHiddenChanged(); } -/** - * Toggle state of 'Hide Usernames' setting - */ -void EntryModel::toggleUsernamesHidden(const bool hide) -{ - setUsernamesHidden(hide); -} - -/** - * Toggle state of 'Hide Passwords' setting - */ -void EntryModel::togglePasswordsHidden(const bool hide) -{ - setPasswordsHidden(hide); -} - void EntryModel::setPaperClipPixmap(const QPixmap& paperclip) { m_paperClipPixmap = paperclip; diff --git a/src/gui/entry/EntryModel.h b/src/gui/entry/EntryModel.h index e8c90f7e4f..3e9f2824ab 100644 --- a/src/gui/entry/EntryModel.h +++ b/src/gui/entry/EntryModel.h @@ -61,13 +61,9 @@ class EntryModel : public QAbstractTableModel QMimeData* mimeData(const QModelIndexList& indexes) const override; void setEntryList(const QList& entries); - + void setPaperClipPixmap(const QPixmap& paperclip); bool isUsernamesHidden() const; - void setUsernamesHidden(const bool hide); bool isPasswordsHidden() const; - void setPasswordsHidden(const bool hide); - - void setPaperClipPixmap(const QPixmap& paperclip); signals: void switchedToListMode(); @@ -77,8 +73,8 @@ class EntryModel : public QAbstractTableModel public slots: void setGroup(Group* group); - void toggleUsernamesHidden(const bool hide); - void togglePasswordsHidden(const bool hide); + void setUsernamesHidden(bool hide); + void setPasswordsHidden(bool hide); private slots: void entryAboutToAdd(Entry* entry); diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 86ef027325..472aeffc8a 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -62,9 +62,9 @@ EntryView::EntryView(QWidget* parent) m_headerMenu->setTitle(tr("Customize View")); m_headerMenu->addSection(tr("Customize View")); - m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), m_model, SLOT(toggleUsernamesHidden(bool))); + m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), m_model, SLOT(setUsernamesHidden(bool))); m_hideUsernamesAction->setCheckable(true); - m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), m_model, SLOT(togglePasswordsHidden(bool))); + m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), m_model, SLOT(setPasswordsHidden(bool))); m_hidePasswordsAction->setCheckable(true); m_headerMenu->addSeparator(); @@ -274,6 +274,11 @@ bool EntryView::isUsernamesHidden() const */ void EntryView::setUsernamesHidden(const bool hide) { + bool block = m_hideUsernamesAction->signalsBlocked(); + m_hideUsernamesAction->blockSignals(true); + m_hideUsernamesAction->setChecked(hide); + m_hideUsernamesAction->blockSignals(block); + m_model->setUsernamesHidden(hide); } @@ -291,6 +296,11 @@ bool EntryView::isPasswordsHidden() const */ void EntryView::setPasswordsHidden(const bool hide) { + bool block = m_hidePasswordsAction->signalsBlocked(); + m_hidePasswordsAction->blockSignals(true); + m_hidePasswordsAction->setChecked(hide); + m_hidePasswordsAction->blockSignals(block); + m_model->setPasswordsHidden(hide); } diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 986d01ab29..719e4afc85 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -755,6 +755,7 @@ void TestGui::testTotp() auto* setupTotpButtonBox = setupTotpDialog->findChild("buttonBox"); QTest::mouseClick(setupTotpButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); + clickIndex(item, entryView, Qt::LeftButton); auto* entryEditAction = m_mainWindow->findChild("actionEntryEdit"); QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction); QTest::mouseClick(entryEditWidget, Qt::LeftButton);