1 /*
2   SPDX-FileCopyrightText: 2012-2021 Laurent Montel <montel@kde.org>
3 
4   SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "accountconfigorderdialog.h"
8 #include "mailcommon_debug.h"
9 #include "mailcommonsettings_base.h"
10 #include "util/mailutil.h"
11 #include <KLocalizedString>
12 #include <PimCommon/PimUtil>
13 #include <QIcon>
14 #include <QPushButton>
15 #include <QVBoxLayout>
16 
17 #include <Akonadi/AgentInstance>
18 #include <Akonadi/AgentManager>
19 
20 #include <KMime/KMimeMessage>
21 
22 #include <KConfigGroup>
23 #include <QCheckBox>
24 #include <QDialogButtonBox>
25 #include <QHBoxLayout>
26 #include <QListWidget>
27 
28 using namespace MailCommon;
29 
30 struct InstanceStruct {
31     QString name;
32     QIcon icon;
isValidInstanceStruct33     Q_REQUIRED_RESULT bool isValid() const
34     {
35         return !name.isEmpty();
36     }
37 };
38 
39 class MailCommon::AccountConfigOrderDialogPrivate
40 {
41 public:
42     QListWidget *mListAccount = nullptr;
43     QPushButton *mUpButton = nullptr;
44     QPushButton *mDownButton = nullptr;
45     QCheckBox *mEnableAccountOrder = nullptr;
46     MailCommon::MailCommonSettings *mSettings = nullptr;
47 };
48 
AccountConfigOrderDialog(MailCommon::MailCommonSettings * settings,QWidget * parent)49 AccountConfigOrderDialog::AccountConfigOrderDialog(MailCommon::MailCommonSettings *settings, QWidget *parent)
50     : QDialog(parent)
51     , d(new MailCommon::AccountConfigOrderDialogPrivate)
52 {
53     d->mSettings = settings;
54     setWindowTitle(i18nc("@title:window", "Edit Accounts Order"));
55     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
56     auto mainLayout = new QVBoxLayout(this);
57 
58     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
59     okButton->setDefault(true);
60     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
61     connect(buttonBox, &QDialogButtonBox::rejected, this, &AccountConfigOrderDialog::reject);
62 
63     auto page = new QWidget(this);
64     mainLayout->addWidget(page);
65     mainLayout->addWidget(buttonBox);
66 
67     auto vbox = new QVBoxLayout;
68     vbox->setContentsMargins({});
69     page->setLayout(vbox);
70 
71     d->mEnableAccountOrder = new QCheckBox(i18n("Use custom order"), this);
72     connect(d->mEnableAccountOrder, &QCheckBox::clicked, this, &AccountConfigOrderDialog::slotEnableAccountOrder);
73     vbox->addWidget(d->mEnableAccountOrder);
74 
75     auto vlay = new QHBoxLayout;
76     vbox->addLayout(vlay);
77 
78     d->mListAccount = new QListWidget(this);
79     d->mListAccount->setDragDropMode(QAbstractItemView::InternalMove);
80     vlay->addWidget(d->mListAccount);
81 
82     auto upDownBox = new QWidget(page);
83     auto upDownBoxVBoxLayout = new QVBoxLayout(upDownBox);
84     upDownBoxVBoxLayout->setContentsMargins({});
85     d->mUpButton = new QPushButton(upDownBox);
86     upDownBoxVBoxLayout->addWidget(d->mUpButton);
87     d->mUpButton->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
88     d->mUpButton->setToolTip(i18nc("Move selected account up.", "Up"));
89     d->mUpButton->setEnabled(false); // b/c no item is selected yet
90     d->mUpButton->setFocusPolicy(Qt::StrongFocus);
91     d->mUpButton->setAutoRepeat(true);
92 
93     d->mDownButton = new QPushButton(upDownBox);
94     upDownBoxVBoxLayout->addWidget(d->mDownButton);
95     d->mDownButton->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
96     d->mDownButton->setToolTip(i18nc("Move selected account down.", "Down"));
97     d->mDownButton->setEnabled(false); // b/c no item is selected yet
98     d->mDownButton->setFocusPolicy(Qt::StrongFocus);
99     d->mDownButton->setAutoRepeat(true);
100 
101     auto spacer = new QWidget(upDownBox);
102     upDownBoxVBoxLayout->addWidget(spacer);
103     upDownBoxVBoxLayout->setStretchFactor(spacer, 100);
104     vlay->addWidget(upDownBox);
105 
106     connect(d->mUpButton, &QPushButton::clicked, this, &AccountConfigOrderDialog::slotMoveUp);
107     connect(d->mDownButton, &QPushButton::clicked, this, &AccountConfigOrderDialog::slotMoveDown);
108     connect(d->mListAccount, &QListWidget::itemSelectionChanged, this, &AccountConfigOrderDialog::slotEnableControls);
109     connect(d->mListAccount->model(), &QAbstractItemModel::rowsMoved, this, &AccountConfigOrderDialog::slotEnableControls);
110 
111     connect(okButton, &QPushButton::clicked, this, &AccountConfigOrderDialog::slotOk);
112     readConfig();
113     init();
114 }
115 
~AccountConfigOrderDialog()116 AccountConfigOrderDialog::~AccountConfigOrderDialog()
117 {
118     writeConfig();
119 }
120 
slotEnableAccountOrder(bool state)121 void AccountConfigOrderDialog::slotEnableAccountOrder(bool state)
122 {
123     d->mListAccount->setEnabled(state);
124     d->mUpButton->setEnabled(state);
125     d->mDownButton->setEnabled(state);
126     if (state) {
127         slotEnableControls();
128     }
129 }
130 
slotMoveUp()131 void AccountConfigOrderDialog::slotMoveUp()
132 {
133     if (!d->mListAccount->currentItem()) {
134         return;
135     }
136     const int pos = d->mListAccount->row(d->mListAccount->currentItem());
137     d->mListAccount->blockSignals(true);
138     QListWidgetItem *item = d->mListAccount->takeItem(pos);
139     // now selected item is at idx(idx-1), so
140     // insert the other item at idx, ie. above(below).
141     d->mListAccount->insertItem(pos - 1, item);
142     d->mListAccount->blockSignals(false);
143     d->mListAccount->setCurrentRow(pos - 1);
144 }
145 
slotMoveDown()146 void AccountConfigOrderDialog::slotMoveDown()
147 {
148     if (!d->mListAccount->currentItem()) {
149         return;
150     }
151     const int pos = d->mListAccount->row(d->mListAccount->currentItem());
152     d->mListAccount->blockSignals(true);
153     QListWidgetItem *item = d->mListAccount->takeItem(pos);
154     // now selected item is at idx(idx-1), so
155     // insert the other item at idx, ie. above(below).
156     d->mListAccount->insertItem(pos + 1, item);
157     d->mListAccount->blockSignals(false);
158     d->mListAccount->setCurrentRow(pos + 1);
159 }
160 
slotEnableControls()161 void AccountConfigOrderDialog::slotEnableControls()
162 {
163     QListWidgetItem *item = d->mListAccount->currentItem();
164 
165     d->mUpButton->setEnabled(item && d->mListAccount->currentRow() != 0);
166     d->mDownButton->setEnabled(item && d->mListAccount->currentRow() != d->mListAccount->count() - 1);
167 }
168 
init()169 void AccountConfigOrderDialog::init()
170 {
171     const QStringList listOrderAccount = d->mSettings->order();
172     QStringList instanceList;
173 
174     QMap<QString, InstanceStruct> mapInstance;
175     const Akonadi::AgentInstance::List lstInstances = Akonadi::AgentManager::self()->instances();
176     for (const Akonadi::AgentInstance &instance : lstInstances) {
177         const QString identifier = instance.identifier();
178         if (!MailCommon::Util::isMailAgent(instance) || identifier.contains(POP3_RESOURCE_IDENTIFIER)) {
179             continue;
180         }
181         instanceList << instance.identifier();
182         InstanceStruct instanceStruct;
183         instanceStruct.name = instance.name();
184         if (PimCommon::Util::isImapResource(identifier)) {
185             instanceStruct.name += QLatin1String(" (IMAP)");
186         } else if (identifier.startsWith(QLatin1String("akonadi_maildir_resource"))) {
187             instanceStruct.name += QLatin1String(" (Maildir)");
188         } else if (identifier.startsWith(QLatin1String("akonadi_mailbox_resource"))) {
189             instanceStruct.name += QLatin1String(" (Mailbox)");
190         } else if (identifier.startsWith(QLatin1String("akonadi_mixedmaildir_resource"))) {
191             instanceStruct.name += QLatin1String(" (Mixedmaildir)");
192         } else {
193             qCDebug(MAILCOMMON_LOG) << " Unknown resource type " << identifier;
194         }
195         instanceStruct.icon = instance.type().icon();
196         mapInstance.insert(instance.identifier(), instanceStruct);
197     }
198     instanceList.sort(Qt::CaseInsensitive);
199     const int numberOfList(listOrderAccount.count());
200     for (int i = 0; i < numberOfList; ++i) {
201         instanceList.removeOne(listOrderAccount.at(i));
202     }
203 
204     QStringList finalList(listOrderAccount);
205     finalList += instanceList;
206 
207     const int numberOfElement(finalList.count());
208     for (int i = 0; i < numberOfElement; ++i) {
209         const QString identifier(finalList.at(i));
210         const InstanceStruct tmp = mapInstance.value(identifier);
211         if (tmp.isValid()) {
212             auto item = new QListWidgetItem(tmp.icon, tmp.name, d->mListAccount);
213             item->setData(AccountConfigOrderDialog::IdentifierAccount, identifier);
214             d->mListAccount->addItem(item);
215         }
216     }
217     const bool enabled = d->mSettings->enableAccountOrder();
218     d->mEnableAccountOrder->setChecked(enabled);
219     slotEnableAccountOrder(enabled);
220 }
221 
slotOk()222 void AccountConfigOrderDialog::slotOk()
223 {
224     QStringList order;
225     const int numberOfItems(d->mListAccount->count());
226     order.reserve(numberOfItems);
227     for (int i = 0; i < numberOfItems; ++i) {
228         order << d->mListAccount->item(i)->data(AccountConfigOrderDialog::IdentifierAccount).toString();
229     }
230 
231     d->mSettings->setOrder(order);
232     d->mSettings->setEnableAccountOrder(d->mEnableAccountOrder->isChecked());
233     d->mSettings->save();
234     QDialog::accept();
235 }
236 
readConfig()237 void AccountConfigOrderDialog::readConfig()
238 {
239     KConfigGroup accountConfigDialog(d->mSettings->config(), "AccountConfigOrderDialog");
240     const QSize size = accountConfigDialog.readEntry("Size", QSize(600, 400));
241     if (size.isValid()) {
242         resize(size);
243     }
244 }
245 
writeConfig()246 void AccountConfigOrderDialog::writeConfig()
247 {
248     KConfigGroup accountConfigDialog(d->mSettings->config(), "AccountConfigOrderDialog");
249     accountConfigDialog.writeEntry("Size", size());
250     accountConfigDialog.sync();
251 }
252