mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-28 00:39:43 -05:00
Grey out Apply button when there are no changes
Resolves #1313 What this commit does: * Whenever the Apply button is pressed, and if the save was successful, then the Apply button is disabled. * Each subwidget used by EditEntryWidget has now a signal called `widgetUpdated` that is emitted when the widgets' internal content changes. The EditEntryWidget subscribes to that signal to know when to enable the Apply button (by calling `entryUpdated()`). * There are some views that are not isolated in their own widgets (`m_advancedUi`, for example) so in those cases I invoked `entryUpdated()` directly whenever I detected an update: * some updates occur directly in a Qt widget like when editing the text of a QLineItem, so in that case I connected the widget's signals directly to the `entryUpdated()` slot. * some updates occur in EditEntryWidget, so in those cases the invocation to `entryUpdated()` is made as soon as the change is detected (for example when the user has confirmed an action in a dialog). A known problem: there are some situations when the Apply button will get enabled even if there are no changes, this is because the app changes the value of a field by itself so it's considered an update (for example, clicking on the "Reveal" button changes the text shown in a text field). The solution to this can be a bit complicated: disabling temporarily the `entryUpdated()` whenever the app is going to do an action with such side-effects. So I preferred to let the Apply button get enabled in those cases.
This commit is contained in:
parent
e92d5e80ee
commit
78ef6f0d04
@ -117,6 +117,14 @@ bool EditWidget::readOnly() const
|
||||
return m_readOnly;
|
||||
}
|
||||
|
||||
void EditWidget::enableApplyButton(bool enabled)
|
||||
{
|
||||
QPushButton* applyButton = m_ui->buttonBox->button(QDialogButtonBox::Apply);
|
||||
if (applyButton) {
|
||||
applyButton->setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void EditWidget::showMessage(const QString& text, MessageWidget::MessageType type)
|
||||
{
|
||||
m_ui->messageWidget->setCloseButtonVisible(false);
|
||||
|
@ -47,6 +47,7 @@ public:
|
||||
QLabel* headlineLabel();
|
||||
void setReadOnly(bool readOnly);
|
||||
bool readOnly() const;
|
||||
void enableApplyButton(bool enabled);
|
||||
|
||||
signals:
|
||||
void apply();
|
||||
|
@ -66,6 +66,13 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent)
|
||||
connect(m_ui->deleteButton, SIGNAL(clicked()), SLOT(removeCustomIcon()));
|
||||
connect(m_ui->faviconButton, SIGNAL(clicked()), SLOT(downloadFavicon()));
|
||||
|
||||
connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated()));
|
||||
connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated()));
|
||||
connect(m_ui->defaultIconsView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||
this, SIGNAL(widgetUpdated()));
|
||||
connect(m_ui->customIconsView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||
this, SIGNAL(widgetUpdated()));
|
||||
|
||||
m_ui->faviconButton->setVisible(false);
|
||||
}
|
||||
|
||||
@ -268,6 +275,8 @@ void EditWidgetIcons::addCustomIcon(const QImage& icon)
|
||||
updateRadioButtonCustomIcons();
|
||||
QModelIndex index = m_customIconModel->indexFromUuid(uuid);
|
||||
m_ui->customIconsView->setCurrentIndex(index);
|
||||
|
||||
emit widgetUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,6 +356,8 @@ void EditWidgetIcons::removeCustomIcon()
|
||||
} else {
|
||||
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(Group::DefaultIconNumber));
|
||||
}
|
||||
|
||||
emit widgetUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ public slots:
|
||||
signals:
|
||||
void messageEditEntry(QString, MessageWidget::MessageType);
|
||||
void messageEditEntryDismiss();
|
||||
void widgetUpdated();
|
||||
|
||||
private slots:
|
||||
void downloadFavicon();
|
||||
|
@ -95,6 +95,7 @@ EditEntryWidget::EditEntryWidget(QWidget* parent)
|
||||
#endif
|
||||
setupProperties();
|
||||
setupHistory();
|
||||
setupEntryUpdate();
|
||||
|
||||
connect(this, SIGNAL(accepted()), SLOT(acceptEntry()));
|
||||
connect(this, SIGNAL(rejected()), SLOT(cancel()));
|
||||
@ -227,6 +228,59 @@ void EditEntryWidget::setupHistory()
|
||||
connect(m_historyUi->deleteAllButton, SIGNAL(clicked()), SLOT(deleteAllHistoryEntries()));
|
||||
}
|
||||
|
||||
void EditEntryWidget::setupEntryUpdate()
|
||||
{
|
||||
// Entry tab
|
||||
connect(m_mainUi->titleEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_mainUi->usernameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_mainUi->passwordEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_mainUi->passwordRepeatEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_mainUi->urlEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_mainUi->expireCheck, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_mainUi->notesEnabled, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_mainUi->expireDatePicker, SIGNAL(dateTimeChanged(const QDateTime&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_mainUi->notesEdit, SIGNAL(textChanged()), this, SLOT(setUnsavedChanges()));
|
||||
|
||||
// Advanced tab
|
||||
connect(m_advancedUi->attributesEdit, SIGNAL(textChanged()), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_advancedUi->protectAttributeButton, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_advancedUi->fgColorCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_advancedUi->bgColorCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_advancedUi->attachmentsWidget, SIGNAL(widgetUpdated()), this, SLOT(setUnsavedChanges()));
|
||||
|
||||
// Icon tab
|
||||
connect(m_iconsWidget, SIGNAL(widgetUpdated()), this, SLOT(setUnsavedChanges()));
|
||||
|
||||
// Auto-Type tab
|
||||
connect(m_autoTypeUi->enableButton, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_autoTypeUi->customWindowSequenceButton, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_autoTypeUi->inheritSequenceButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_autoTypeUi->customSequenceButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_autoTypeUi->windowSequenceEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_autoTypeUi->sequenceEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_autoTypeUi->windowTitleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_autoTypeUi->windowTitleCombo, SIGNAL(editTextChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
|
||||
// Properties and History tabs don't need extra connections
|
||||
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
// SSH Agent tab
|
||||
if (config()->get("SSHAgent", false).toBool()) {
|
||||
connect(m_sshAgentUi->attachmentRadioButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->externalFileRadioButton, SIGNAL(toggled(bool)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->attachmentComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->attachmentComboBox, SIGNAL(editTextChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->externalFileEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->publicKeyEdit, SIGNAL(textChanged()), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->addKeyToAgentCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->removeKeyFromAgentCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->requireUserConfirmationCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->lifetimeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
connect(m_sshAgentUi->lifetimeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setUnsavedChanges()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void EditEntryWidget::emitHistoryEntryActivated(const QModelIndex& index)
|
||||
{
|
||||
Q_ASSERT(!m_history);
|
||||
@ -581,7 +635,6 @@ void EditEntryWidget::loadEntry(Entry* entry, bool create, bool history, const Q
|
||||
m_database = database;
|
||||
m_create = create;
|
||||
m_history = history;
|
||||
m_saved = false;
|
||||
|
||||
if (history) {
|
||||
setHeadline(QString("%1 > %2").arg(parentName, tr("Entry history")));
|
||||
@ -601,6 +654,9 @@ void EditEntryWidget::loadEntry(Entry* entry, bool create, bool history, const Q
|
||||
|
||||
setCurrentPage(0);
|
||||
setPageHidden(m_historyWidget, m_history || m_entry->historyItems().count() < 1);
|
||||
|
||||
// Force the user to Save/Apply/Discard new entries
|
||||
setUnsavedChanges(m_create);
|
||||
}
|
||||
|
||||
void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
||||
@ -780,7 +836,7 @@ bool EditEntryWidget::commitEntry()
|
||||
}
|
||||
|
||||
updateEntryData(m_entry);
|
||||
m_saved = true;
|
||||
setUnsavedChanges(false);
|
||||
|
||||
if (!m_create) {
|
||||
m_entry->endUpdate();
|
||||
@ -940,6 +996,8 @@ void EditEntryWidget::insertAttribute()
|
||||
|
||||
m_advancedUi->attributesView->setCurrentIndex(index);
|
||||
m_advancedUi->attributesView->edit(index);
|
||||
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
|
||||
void EditEntryWidget::editCurrentAttribute()
|
||||
@ -950,6 +1008,7 @@ void EditEntryWidget::editCurrentAttribute()
|
||||
|
||||
if (index.isValid()) {
|
||||
m_advancedUi->attributesView->edit(index);
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -963,6 +1022,7 @@ void EditEntryWidget::removeCurrentAttribute()
|
||||
if (MessageBox::question(this, tr("Confirm Remove"), tr("Are you sure you want to remove this attribute?"),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
m_entryAttributes->remove(m_attributesModel->keyByIndex(index));
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1047,9 +1107,11 @@ void EditEntryWidget::revealCurrentAttribute()
|
||||
if (! m_advancedUi->attributesEdit->isEnabled()) {
|
||||
QModelIndex index = m_advancedUi->attributesView->currentIndex();
|
||||
if (index.isValid()) {
|
||||
bool oldBlockSignals = m_advancedUi->attributesEdit->blockSignals(true);
|
||||
QString key = m_attributesModel->keyByIndex(index);
|
||||
m_advancedUi->attributesEdit->setPlainText(m_entryAttributes->value(key));
|
||||
m_advancedUi->attributesEdit->setEnabled(true);
|
||||
m_advancedUi->attributesEdit->blockSignals(oldBlockSignals);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1083,6 +1145,7 @@ void EditEntryWidget::insertAutoTypeAssoc()
|
||||
m_autoTypeUi->assocView->setCurrentIndex(newIndex);
|
||||
loadCurrentAssoc(newIndex);
|
||||
m_autoTypeUi->windowTitleCombo->setFocus();
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
|
||||
void EditEntryWidget::removeAutoTypeAssoc()
|
||||
@ -1091,6 +1154,7 @@ void EditEntryWidget::removeAutoTypeAssoc()
|
||||
|
||||
if (currentIndex.isValid()) {
|
||||
m_autoTypeAssoc->remove(currentIndex.row());
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1153,6 +1217,7 @@ void EditEntryWidget::restoreHistoryEntry()
|
||||
QModelIndex index = m_sortModel->mapToSource(m_historyUi->historyView->currentIndex());
|
||||
if (index.isValid()) {
|
||||
setForms(m_historyModel->entryFromIndex(index), true);
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1166,6 +1231,7 @@ void EditEntryWidget::deleteHistoryEntry()
|
||||
} else {
|
||||
m_historyUi->deleteAllButton->setEnabled(false);
|
||||
}
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1178,6 +1244,7 @@ void EditEntryWidget::deleteAllHistoryEntries()
|
||||
else {
|
||||
m_historyUi->deleteAllButton->setEnabled(false);
|
||||
}
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
|
||||
QMenu* EditEntryWidget::createPresetsMenu()
|
||||
@ -1229,5 +1296,12 @@ void EditEntryWidget::pickColor()
|
||||
QColor newColor = colorDialog.getColor(oldColor);
|
||||
if (newColor.isValid()) {
|
||||
setupColorButton(isForeground, newColor);
|
||||
setUnsavedChanges(true);
|
||||
}
|
||||
}
|
||||
|
||||
void EditEntryWidget::setUnsavedChanges(bool hasUnsaved)
|
||||
{
|
||||
m_saved = !hasUnsaved;
|
||||
enableApplyButton(hasUnsaved);
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ private slots:
|
||||
void useExpiryPreset(QAction* action);
|
||||
void toggleHideNotes(bool visible);
|
||||
void pickColor();
|
||||
void setUnsavedChanges(bool hasUnsaved = true);
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
void updateSSHAgent();
|
||||
void updateSSHAgentAttachment();
|
||||
@ -122,6 +123,7 @@ private:
|
||||
#endif
|
||||
void setupProperties();
|
||||
void setupHistory();
|
||||
void setupEntryUpdate();
|
||||
void setupColorButton(bool foreground, const QColor& color);
|
||||
|
||||
bool passwordsEqual();
|
||||
|
@ -147,6 +147,7 @@ void EntryAttachmentsWidget::insertAttachments()
|
||||
if (!insertAttachments(filenames, errorMessage)) {
|
||||
errorOccurred(errorMessage);
|
||||
}
|
||||
emit widgetUpdated();
|
||||
}
|
||||
|
||||
void EntryAttachmentsWidget::removeSelectedAttachments()
|
||||
@ -170,6 +171,7 @@ void EntryAttachmentsWidget::removeSelectedAttachments()
|
||||
keys.append(m_attachmentsModel->keyByIndex(index));
|
||||
}
|
||||
m_entryAttachments->remove(keys);
|
||||
emit widgetUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ signals:
|
||||
void errorOccurred(const QString& error);
|
||||
void readOnlyChanged(bool readOnly);
|
||||
void buttonsVisibleChanged(bool isButtonsVisible);
|
||||
void widgetUpdated();
|
||||
|
||||
private slots:
|
||||
void insertAttachments();
|
||||
|
Loading…
Reference in New Issue
Block a user