summaryrefslogtreecommitdiffstats
path: root/src/mainwindow.h
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-13 20:16:43 +0200
committerDanilo M. <danix@danix.xyz>2026-08-13 20:16:43 +0200
commitc9eb6fc86c947aea0075c1e9d279695cd9f9b7ee (patch)
tree7e5339fbb7e71d29498405f9d4b1c4188f5ab4c3 /src/mainwindow.h
parentf389db3aad498d46c95c2a95b4280ffb541043b2 (diff)
parent9be1b13b91188cf44a40c6786a83de034988cdbd (diff)
downloadqtmaildir-c9eb6fc86c947aea0075c1e9d279695cd9f9b7ee.tar.gz
qtmaildir-c9eb6fc86c947aea0075c1e9d279695cd9f9b7ee.zip
Merge branch 'saved-queries': saved queries in a file of their own
Items 23 and 82. Saved queries move out of the [queries] INI section into ~/.config/qtmaildir/queries.json, gaining the three things the INI could not express: an order, a pinned flag, and a per-query account scope. They can now be created, edited, reordered, unpinned and deleted from the UI rather than only by hand-editing a config file. The INI could not carry order at all: QSettings reads a section through childKeys(), which sorts alphabetically and never follows the file, so the buttons could not be arranged. Migration reads the old section once, marks every entry pinned so nothing moves on the first launch, and leaves the config file byte-identical, since rewriting it with QSettings would drop the user's comments and key order. Sent stops being a hardcoded button and becomes an ordinary entry carrying "generated": "sent". Its query is still composed from the accounts every time it runs, so correcting a folder name still needs no edit here, but the row now follows one rule instead of having one member the user could not move, rename or remove. Item 82 was found by hand-testing item 23 and fixed on the same branch: saving worked and nothing else did, so a saved query could be created and never changed. It also uncovered a defect that predated it, where rebuilding the row with deleteLater() left the stale row answering findChild(), which was already reachable from the save path. Hand-tested throughout: the migration against the real config, saving, unpinning, deleting, reordering, the read-only query field on a generated entry, and an unpin surviving a restart.
Diffstat (limited to 'src/mainwindow.h')
-rw-r--r--src/mainwindow.h60
1 files changed, 60 insertions, 0 deletions
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 18fbbb4..6e8501a 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -49,6 +49,8 @@ class QPlainTextEdit;
class QSplitter;
class QProgressBar;
class QTimer;
+class QToolButton;
+class QVBoxLayout;
class ThreadListModel;
class MessageView;
@@ -123,6 +125,25 @@ public:
static void setLocksPathForTesting(const QString &path);
static QString locksPath();
+ /// Suppresses the delete confirmation.
+ ///
+ /// A test seam. Deleting a saved query is destructive and not on the undo
+ /// stack, so it asks first; a test cannot answer a modal dialog without
+ /// hanging, and driving one through QTest would assert the dialog rather
+ /// than the deletion.
+ void setConfirmDeleteForTesting(bool confirm) { m_confirmDelete = confirm; }
+
+ /// Renames or replaces a stored query, as the edit dialog would on accept.
+ ///
+ /// A test seam for the rename path specifically: the dialog is modal, and
+ /// the property worth asserting is that a rename REPLACES rather than
+ /// duplicating, which is decided after the dialog returns.
+ void replaceSavedQueryForTesting(const QString &originalName,
+ const SavedQuery &replacement)
+ {
+ replaceSavedQuery(originalName, replacement);
+ }
+
/// How many commands are on the undo stack.
///
/// A test seam. The undo QAction is always enabled and checks canUndo()
@@ -231,6 +252,41 @@ private:
/// is what widgets connect to.
void runQuery(FlatResult flat);
+ /// Builds the row of saved-query buttons, the overflow menu and Sent.
+ ///
+ /// Its own row since item 23: an unbounded list of buttons sharing the
+ /// query row squeezed the field, which is the whole reason for the
+ /// pinned/unpinned split.
+ void buildSavedQueryRow(QWidget *parent, QVBoxLayout *layout);
+
+ /// Runs a saved query, taking its account scope through the dropdown.
+ ///
+ /// Not by pre-scoping the text: runQuery() already wraps the query in the
+ /// selected account's path, so a scope baked in here would be applied
+ /// twice. Setting the dropdown also shows the user what scope they are in.
+ void runSavedQuery(const SavedQuery &saved);
+
+ /// Names the current query and stores it in queries.json.
+ void saveCurrentQuery();
+
+ /// Rebuilds the saved-query row in place after the stored list changed.
+ void rebuildSavedQueryRow();
+
+ /// Hangs Edit, Pin/Unpin and Delete on a saved query's button or menu
+ /// entry. The only route to changing a stored query from the UI.
+ void addSavedQueryActions(QWidget *target, const SavedQuery &saved);
+
+ /// Replaces the entry named `originalName`, writes the file and rebuilds
+ /// the row. An empty `replacement.name` deletes it instead.
+ ///
+ /// Matched on the ORIGINAL name, not the replacement's: a rename otherwise
+ /// leaves the old entry in place and adds a second one.
+ void replaceSavedQuery(const QString &originalName,
+ const SavedQuery &replacement);
+
+ void editSavedQuery(const SavedQuery &saved);
+ void deleteSavedQuery(const SavedQuery &saved);
+
private slots:
void runCurrentQuery() { runQuery(FlatResult::No); }
@@ -604,6 +660,10 @@ private:
QUndoStack m_undoStack;
QLineEdit *m_queryEdit = nullptr;
+ /// Save query, beside the field. Driven by the save_query action.
+ QToolButton *m_saveQueryButton = nullptr;
+ /// Whether deleting a saved query asks first. Always true outside tests.
+ bool m_confirmDelete = true;
QueryCompleter *m_queryCompleter = nullptr;
/// Its own type, not the QTreeView base. The strip painting and the
/// expander column are ThreadListView's, and holding the base here only