aboutsummaryrefslogtreecommitdiffstats
path: root/src/savequerydialog.cpp
blob: 43c226b2f404ed9b89627f20fbe8c613b04e4715 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 * 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 <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;
    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);

    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();
    // 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;
}