summaryrefslogtreecommitdiffstats
path: root/src/savequerydialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/savequerydialog.cpp')
-rw-r--r--src/savequerydialog.cpp174
1 files changed, 174 insertions, 0 deletions
diff --git a/src/savequerydialog.cpp b/src/savequerydialog.cpp
new file mode 100644
index 0000000..2d09986
--- /dev/null
+++ b/src/savequerydialog.cpp
@@ -0,0 +1,174 @@
+/*
+ * qtmaildir - a Qt6 mail client for notmuch-indexed Maildirs
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "savequerydialog.h"
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialogButtonBox>
+#include <QFormLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+bool SaveQueryDialog::namesAnExistingQuery(const Config &config,
+ const QString &name)
+{
+ for (const SavedQuery &saved : config.savedQueries()) {
+ if (saved.name.compare(name.trimmed(), Qt::CaseInsensitive) == 0)
+ return true;
+ }
+ return false;
+}
+
+SaveQueryDialog::SaveQueryDialog(const Config &config, const QString &query,
+ const QString &accountKey, QWidget *parent)
+ : QDialog(parent)
+ , m_config(config)
+{
+ SavedQuery initial;
+ initial.query = query;
+ initial.account = accountKey;
+ initial.pinned = true;
+ setWindowTitle(tr("Save query"));
+ build(initial);
+}
+
+SaveQueryDialog::SaveQueryDialog(const Config &config,
+ const SavedQuery &existing, QWidget *parent)
+ : QDialog(parent)
+ , m_config(config)
+ , m_originalName(existing.name)
+ , m_generated(existing.generated)
+ , m_flat(existing.flat)
+{
+ setWindowTitle(tr("Edit saved query"));
+ build(existing);
+}
+
+void SaveQueryDialog::build(const SavedQuery &initial)
+{
+ auto *layout = new QVBoxLayout(this);
+ auto *form = new QFormLayout;
+
+ m_name = new QLineEdit(initial.name, this);
+ m_name->setObjectName(QStringLiteral("saveQueryName"));
+ m_name->setPlaceholderText(tr("A name for this query"));
+ form->addRow(tr("Name"), m_name);
+
+ m_query = new QLineEdit(initial.query, this);
+ m_query->setObjectName(QStringLiteral("saveQueryQuery"));
+ if (initial.isGenerated()) {
+ // A generated entry has no stored query: it is composed from the
+ // accounts every time it runs. Shown, so the user can see what it will
+ // do, but read-only, since editing it would change nothing.
+ m_query->setText(m_config.resolvedQuery(initial));
+ m_query->setReadOnly(true);
+ m_query->setToolTip(tr("Built from your accounts and not editable. "
+ "It follows the sent folder each account "
+ "configures."));
+ form->addRow(tr("Query"), m_query);
+ } else {
+ form->addRow(tr("Query"), m_query);
+ }
+
+ // The scope is stored as an account KEY, so the entries carry the key as
+ // data exactly as the main window's dropdown does. "All accounts" is the
+ // empty key, not a missing entry, so an unscoped query is a real choice
+ // rather than the absence of one.
+ m_account = new QComboBox(this);
+ m_account->setObjectName(QStringLiteral("saveQueryAccount"));
+ m_account->addItem(tr("All accounts"), QString());
+ for (const Account &account : m_config.accounts())
+ m_account->addItem(account.key, account.key);
+ const int index = m_account->findData(initial.account);
+ m_account->setCurrentIndex(index >= 0 ? index : 0);
+ form->addRow(tr("Account"), m_account);
+
+ m_pinned = new QCheckBox(tr("Show as a button"), this);
+ m_pinned->setObjectName(QStringLiteral("saveQueryPinned"));
+ m_pinned->setChecked(initial.pinned);
+ form->addRow(QString(), m_pinned);
+
+ layout->addLayout(form);
+
+ // Says what is about to happen rather than refusing the name. Overwriting
+ // a saved query on purpose is a normal edit, and the only thing worth
+ // preventing is doing it without noticing.
+ m_notice = new QLabel(this);
+ m_notice->setObjectName(QStringLiteral("saveQueryNotice"));
+ m_notice->setWordWrap(true);
+ layout->addWidget(m_notice);
+
+ auto *buttons = new QDialogButtonBox(
+ QDialogButtonBox::Save | QDialogButtonBox::Cancel, this);
+ connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ layout->addWidget(buttons);
+
+ m_ok = buttons->button(QDialogButtonBox::Save);
+ m_ok->setObjectName(QStringLiteral("saveQueryOk"));
+
+ connect(m_name, &QLineEdit::textChanged,
+ this, &SaveQueryDialog::updateOkState);
+ connect(m_query, &QLineEdit::textChanged,
+ this, &SaveQueryDialog::updateOkState);
+ updateOkState();
+
+ m_name->setFocus();
+}
+
+void SaveQueryDialog::updateOkState()
+{
+ const QString name = m_name->text().trimmed();
+ const bool usable = !name.isEmpty() && !m_query->text().trimmed().isEmpty();
+ m_ok->setEnabled(usable);
+
+ // Ignores the entry being edited: warning that "Inbox" already exists
+ // while editing Inbox is noise, and the real case worth catching is a
+ // rename onto a name something else already holds.
+ const bool isItsOwnName =
+ !m_originalName.isEmpty()
+ && name.compare(m_originalName, Qt::CaseInsensitive) == 0;
+ if (!name.isEmpty() && !isItsOwnName
+ && namesAnExistingQuery(m_config, name)) {
+ m_notice->setText(
+ tr("A saved query named '%1' already exists and will be "
+ "replaced.").arg(name));
+ } else {
+ m_notice->clear();
+ }
+}
+
+SavedQuery SaveQueryDialog::savedQuery() const
+{
+ SavedQuery saved;
+ saved.name = m_name->text().trimmed();
+ saved.query = m_query->text().trimmed();
+ saved.account = m_account->currentData().toString();
+ saved.pinned = m_pinned->isChecked();
+ // Carried through rather than re-derived: an edit must not turn a
+ // generated entry into a plain one holding a snapshot of what it happened
+ // to resolve to today.
+ saved.generated = m_generated;
+ saved.flat = m_flat;
+ if (saved.isGenerated())
+ saved.query.clear();
+ return saved;
+}