SMTP: Store correct password into the password manager
[trojita.git] / src / Gui / SettingsDialog.cpp
blob2eeb8b12371a3f6ea486ca6708ab36e66ed6b942
1 /* Copyright (C) 2006 - 2016 Jan Kundrát <jkt@kde.org>
2 Copyright (C) 2014 Luke Dashjr <luke+trojita@dashjr.org>
3 Copyright (C) 2012 Mohammed Nafees <nafees.technocool@gmail.com>
4 Copyright (C) 2013 Pali Rohár <pali.rohar@gmail.com>
6 This file is part of the Trojita Qt IMAP e-mail client,
7 http://trojita.flaska.net/
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of
12 the License or (at your option) version 3 or any later version
13 accepted by the membership of KDE e.V. (or its successor approved
14 by the membership of KDE e.V.), which shall act as a proxy
15 defined in Section 14 of version 3 of the license.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include <QCheckBox>
26 #include <QColorDialog>
27 #include <QComboBox>
28 #include <QDataWidgetMapper>
29 #include <QDialogButtonBox>
30 #include <QDebug>
31 #include <QDir>
32 #include <QFormLayout>
33 #include <QGroupBox>
34 #include <QInputDialog>
35 #include <QLineEdit>
36 #include <QListWidget>
37 #include <QMessageBox>
38 #include <QProcess>
39 #include <QPushButton>
40 #include <QRadioButton>
41 #include <QSpinBox>
42 #include <QStackedWidget>
43 #include <QStandardItemModel>
44 #include <QTabWidget>
45 #include <QToolTip>
46 #include <QVBoxLayout>
47 #include "SettingsDialog.h"
48 #include "ColoredItemDelegate.h"
49 #include "Common/InvokeMethod.h"
50 #include "Common/PortNumbers.h"
51 #include "Common/SettingsNames.h"
52 #include "Gui/Util.h"
53 #include "Gui/Window.h"
54 #include "Imap/Model/ImapAccess.h"
55 #include "MSA/Account.h"
56 #include "Plugins/AddressbookPlugin.h"
57 #include "Plugins/PasswordPlugin.h"
58 #include "Plugins/PluginManager.h"
59 #include "UiUtils/IconLoader.h"
60 #include "UiUtils/PasswordWatcher.h"
61 #include "ShortcutHandler/ShortcutHandler.h"
63 namespace Gui
66 QString SettingsDialog::warningStyleSheet = Util::cssWarningBorder() + QStringLiteral("font-weight: bold;");
68 /** @short Check a text field for being non empty. If it's empty, show an error to the user. */
69 template<typename T>
70 bool checkProblemWithEmptyTextField(T *field, const QString &message)
72 if (field->text().isEmpty()) {
73 QToolTip::showText(field->mapToGlobal(QPoint(10, field->height() / 2)), message, 0);
74 return true;
75 } else {
76 return false;
80 SettingsDialog::SettingsDialog(MainWindow *parent, Composer::SenderIdentitiesModel *identitiesModel,
81 Imap::Mailbox::FavoriteTagsModel *favoriteTagsModel, QSettings *settings):
82 QDialog(parent), mainWindow(parent), m_senderIdentities(identitiesModel), m_favoriteTags(favoriteTagsModel),
83 m_settings(settings)
85 setWindowTitle(tr("Settings"));
87 QVBoxLayout *layout = new QVBoxLayout(this);
88 stack = new QTabWidget(this);
89 layout->addWidget(stack);
90 stack->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
92 addPage(new GeneralPage(this, *m_settings, m_senderIdentities), tr("&General"));
93 addPage(new ImapPage(this, *m_settings), tr("I&MAP"));
94 addPage(new CachePage(this, *m_settings), tr("&Offline"));
95 addPage(new OutgoingPage(this, *m_settings), tr("&SMTP"));
96 #ifdef XTUPLE_CONNECT
97 addPage(xtConnect = new XtConnectPage(this, *m_settings, imap), tr("&xTuple"));
98 #endif
99 addPage(new FavoriteTagsPage(this, *m_settings, m_favoriteTags), tr("Favorite &tags"));
101 buttons = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel, Qt::Horizontal, this);
102 connect(buttons, &QDialogButtonBox::accepted, this, &SettingsDialog::accept);
103 connect(buttons, &QDialogButtonBox::rejected, this, &SettingsDialog::reject);
104 layout->addWidget(buttons);
106 EMIT_LATER_NOARG(this, reloadPasswordsRequested);
109 void SettingsDialog::setOriginalPlugins(const QString &passwordPlugin, const QString &addressBookPlugin,
110 const QString &spellcheckerPlugin)
112 m_originalPasswordPlugin = passwordPlugin;
113 m_originalAddressbookPlugin = addressBookPlugin;
114 m_originalSpellcheckerPlugin = spellcheckerPlugin;
117 void SettingsDialog::adjustSizeToScrollAreas()
119 QScrollArea *area = qobject_cast<QScrollArea*>(sender());
120 Q_ASSERT(area && area->widget());
122 // task #A: figure the "minimum" size for the tabwidget
124 // #A.1: search scrollareas and align their size to their content
125 // update size of the widget in the tabbed scrollarea
126 area->widget()->adjustSize();
128 // figure the size demand of this scroll area (content + margins)
129 int l,t,r,b;
130 area->getContentsMargins(&l,&r,&t,&b);
131 QSize minSize(area->widget()->size() + QSize(l+r, t+b));
133 // TODO: clamp this to 640x480 or QDesktopWidget::availableGeometry() dependent?
135 // do not shrink (prevent nasty size jumps for no reason)
136 minSize.setWidth(qMax(area->width(), minSize.width()));
137 minSize.setHeight(qMax(area->height(), minSize.height()));
139 // task #B: find the QStackedWidget inside the QTabWidget to determine its margins
140 Q_FOREACH(const QObject *o, stack->children()) {
141 if (const QStackedWidget *actualStack = qobject_cast<const QStackedWidget*>(o)) {
142 minSize.setWidth(minSize.width() + stack->width() - actualStack->width());
143 minSize.setHeight(minSize.height() + stack->height() - actualStack->height());
144 break;
148 // task #C: convince the dialog to the new size
149 // #C.1: arrest the tabwidget
150 stack->setMinimumSize(minSize);
151 // #C.2: force a relayout of the dialog (do NOT use "adjustSize", which may still shrink)
152 layout()->activate();
153 // #C.3: release the tabwidget minimum size
154 stack->setMinimumSize(QSize(0, 0));
157 Plugins::PluginManager *SettingsDialog::pluginManager()
159 return mainWindow->pluginManager();
162 Imap::ImapAccess *SettingsDialog::imapAccess()
164 return mainWindow->imapAccess();
167 void SettingsDialog::accept()
169 m_saveSignalCount = 0;
171 Q_FOREACH(ConfigurationWidgetInterface *page, pages) {
172 if (!page->checkValidity()) {
173 stack->setCurrentWidget(page->asWidget());
174 return;
176 connect(page->asWidget(), SIGNAL(saved()), this, SLOT(slotAccept())); // new-signal-slot: we're abusing the type system a bit here, cannot use the new syntax
177 ++m_saveSignalCount;
180 #ifndef Q_OS_WIN
181 // Try to wour around QSettings' inability to set umask for its file access. We don't want to set umask globally.
182 QFile settingsFile(m_settings->fileName());
183 settingsFile.setPermissions(QFile::ReadUser | QFile::WriteUser);
184 #endif
186 buttons->setEnabled(false);
187 Q_FOREACH(ConfigurationWidgetInterface *page, pages) {
188 page->asWidget()->setEnabled(false);
191 Q_FOREACH(ConfigurationWidgetInterface *page, pages) {
192 page->save(*m_settings);
195 #ifdef XTUPLE_CONNECT
196 xtConnect->save(*m_settings);
197 #endif
198 m_settings->sync();
199 #ifndef Q_OS_WIN
200 settingsFile.setPermissions(QFile::ReadUser | QFile::WriteUser);
201 #endif
204 void SettingsDialog::slotAccept()
206 disconnect(sender(), SIGNAL(saved()), this, SLOT(slotAccept())); // new-signal-slot: we're abusing the type system a bit here, cannot use the new syntax
207 if (--m_saveSignalCount > 0) {
208 return;
211 QStringList passwordFailures;
212 Q_FOREACH(ConfigurationWidgetInterface *page, pages) {
213 QString message;
214 if (page->passwordFailures(message)) {
215 passwordFailures << message;
218 if (!passwordFailures.isEmpty()) {
219 Gui::Util::messageBoxWarning(this, tr("Saving passwords failed"),
220 tr("<p>Couldn't save passwords. These were the error messages:</p>\n<p>%1</p>")
221 .arg(passwordFailures.join(QStringLiteral("<br/>"))));
224 buttons->setEnabled(true);
225 QDialog::accept();
228 void SettingsDialog::reject()
230 // The changes were performed on the live data, so we have to make sure they are discarded when user cancels
231 #define HANDLE_PLUGIN(LOWERCASE, UPPERCASE) \
232 if (!m_original##UPPERCASE##Plugin.isEmpty() && pluginManager()->LOWERCASE##Plugin() != m_original##UPPERCASE##Plugin) { \
233 pluginManager()->set##UPPERCASE##Plugin(m_original##UPPERCASE##Plugin); \
235 HANDLE_PLUGIN(addressbook, Addressbook)
236 HANDLE_PLUGIN(password, Password)
237 HANDLE_PLUGIN(spellchecker, Spellchecker)
238 #undef HANDLE_PLUGIN
239 m_senderIdentities->loadFromSettings(*m_settings);
240 m_favoriteTags->loadFromSettings(*m_settings);
241 QDialog::reject();
244 void SettingsDialog::addPage(ConfigurationWidgetInterface *page, const QString &title)
246 stack->addTab(page->asWidget(), title);
247 connect(page->asWidget(), SIGNAL(widgetsUpdated()), SLOT(adjustSizeToScrollAreas())); // new-signal-slot: we're abusing the type system a bit here, cannot use the new syntax
248 QMetaObject::invokeMethod(page->asWidget(), "updateWidgets", Qt::QueuedConnection);
249 pages << page;
252 FavoriteTagsPage::FavoriteTagsPage(SettingsDialog *parent, QSettings &s, Imap::Mailbox::FavoriteTagsModel *favoriteTagsModel):
253 QScrollArea(parent), Ui_FavoriteTagsPage(), m_favoriteTagsModel(favoriteTagsModel), m_parent(parent)
255 Ui_FavoriteTagsPage::setupUi(this);
256 Q_ASSERT(m_favoriteTagsModel);
257 moveUpButton->setIcon(UiUtils::loadIcon(QStringLiteral("go-up")));
258 moveDownButton->setIcon(UiUtils::loadIcon(QStringLiteral("go-down")));
259 tagTableView->setModel(m_favoriteTagsModel);
260 tagTableView->setItemDelegate(new ColoredItemDelegate(this));
261 tagTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
262 tagTableView->setSelectionMode(QAbstractItemView::SingleSelection);
263 tagTableView->setGridStyle(Qt::NoPen);
264 tagTableView->resizeRowsToContents();
265 tagTableView->horizontalHeader()->setStretchLastSection(true);
266 // show tag name in color instead
267 tagTableView->hideColumn(Imap::Mailbox::FavoriteTagsModel::COLUMN_COLOR);
269 connect(tagTableView, &QAbstractItemView::clicked, this, &FavoriteTagsPage::updateWidgets);
270 connect(tagTableView, &QAbstractItemView::doubleClicked, this, &FavoriteTagsPage::editButtonClicked);
271 connect(m_favoriteTagsModel, &QAbstractItemModel::modelReset, this, &FavoriteTagsPage::updateWidgets);
272 connect(m_favoriteTagsModel, &QAbstractItemModel::rowsInserted, this, &FavoriteTagsPage::updateWidgets);
273 connect(m_favoriteTagsModel, &QAbstractItemModel::rowsRemoved, this, &FavoriteTagsPage::updateWidgets);
274 connect(m_favoriteTagsModel, &QAbstractItemModel::dataChanged, this, &FavoriteTagsPage::updateWidgets);
275 connect(moveUpButton, &QAbstractButton::clicked, this, [this](){ FavoriteTagsPage::moveTagBy(-1); });
276 connect(moveDownButton, &QAbstractButton::clicked, this, [this](){ FavoriteTagsPage::moveTagBy(1); });
277 connect(addButton, &QAbstractButton::clicked, this, &FavoriteTagsPage::addButtonClicked);
278 connect(editButton, &QAbstractButton::clicked, this, &FavoriteTagsPage::editButtonClicked);
279 connect(deleteButton, &QAbstractButton::clicked, this, &FavoriteTagsPage::deleteButtonClicked);
281 updateWidgets();
284 void FavoriteTagsPage::updateWidgets()
286 bool enabled = tagTableView->currentIndex().isValid();
287 deleteButton->setEnabled(enabled);
288 editButton->setEnabled(enabled);
289 bool upEnabled = m_favoriteTagsModel->rowCount() > 0 && tagTableView->currentIndex().row() > 0;
290 bool downEnabled = m_favoriteTagsModel->rowCount() > 0 && tagTableView->currentIndex().isValid() &&
291 tagTableView->currentIndex().row() < m_favoriteTagsModel->rowCount() - 1;
292 moveUpButton->setEnabled(upEnabled);
293 moveDownButton->setEnabled(downEnabled);
295 tagTableView->resizeColumnToContents(Imap::Mailbox::FavoriteTagsModel::COLUMN_INDEX);
296 tagTableView->resizeColumnToContents(Imap::Mailbox::FavoriteTagsModel::COLUMN_NAME);
298 emit widgetsUpdated();
301 void FavoriteTagsPage::moveTagBy(const int offset)
303 int from = tagTableView->currentIndex().row();
304 int to = tagTableView->currentIndex().row() + offset;
306 m_favoriteTagsModel->moveTag(from, to);
307 updateWidgets();
310 void FavoriteTagsPage::addButtonClicked()
312 m_favoriteTagsModel->appendTag(Imap::Mailbox::ItemFavoriteTagItem());
313 tagTableView->setCurrentIndex(m_favoriteTagsModel->index(m_favoriteTagsModel->rowCount() - 1, 0));
314 EditFavoriteTag *dialog = new EditFavoriteTag(this, m_favoriteTagsModel, tagTableView->currentIndex());
315 dialog->setDeleteOnReject();
316 dialog->setWindowTitle(tr("Add New Tag"));
317 dialog->show();
318 updateWidgets();
321 void FavoriteTagsPage::editButtonClicked()
323 EditFavoriteTag *dialog = new EditFavoriteTag(this, m_favoriteTagsModel, tagTableView->currentIndex());
324 dialog->setWindowTitle(tr("Edit Tag"));
325 dialog->show();
328 void FavoriteTagsPage::deleteButtonClicked()
330 Q_ASSERT(tagTableView->currentIndex().isValid());
331 m_favoriteTagsModel->removeTagAt(tagTableView->currentIndex().row());
332 updateWidgets();
335 void FavoriteTagsPage::save(QSettings &s)
337 m_favoriteTagsModel->saveToSettings(s);
339 emit saved();
342 QWidget *FavoriteTagsPage::asWidget()
344 return this;
347 bool FavoriteTagsPage::checkValidity() const
349 return true;
352 bool FavoriteTagsPage::passwordFailures(QString &message) const
354 Q_UNUSED(message);
355 return false;
358 GeneralPage::GeneralPage(SettingsDialog *parent, QSettings &s, Composer::SenderIdentitiesModel *identitiesModel):
359 QScrollArea(parent), Ui_GeneralPage(), m_identitiesModel(identitiesModel), m_parent(parent)
361 Ui_GeneralPage::setupUi(this);
362 Q_ASSERT(m_identitiesModel);
363 editButton->setEnabled(false);
364 deleteButton->setEnabled(false);
365 moveUpButton->setIcon(UiUtils::loadIcon(QStringLiteral("go-up")));
366 moveDownButton->setIcon(UiUtils::loadIcon(QStringLiteral("go-down")));
367 moveUpButton->setEnabled(false);
368 moveDownButton->setEnabled(false);
369 identityTabelView->setModel(m_identitiesModel);
370 identityTabelView->setSelectionBehavior(QAbstractItemView::SelectRows);
371 identityTabelView->setSelectionMode(QAbstractItemView::SingleSelection);
372 identityTabelView->setGridStyle(Qt::NoPen);
373 identityTabelView->hideColumn(Composer::SenderIdentitiesModel::COLUMN_ORGANIZATION);
374 identityTabelView->setColumnHidden(Composer::SenderIdentitiesModel::COLUMN_SIGNATURE, true);
375 identityTabelView->resizeColumnToContents(Composer::SenderIdentitiesModel::COLUMN_NAME);
376 identityTabelView->resizeRowsToContents();
377 identityTabelView->horizontalHeader()->setStretchLastSection(true);
379 Plugins::PluginManager *pluginManager = parent->pluginManager();
380 QMap<QString, QString>::const_iterator it;
381 int i;
383 #define HANDLE_PLUGIN(LOWERCASE, UPPERCASE, DISABLE, NOTFOUND) \
384 const QMap<QString, QString> &LOWERCASE##Plugins = pluginManager->available##UPPERCASE##Plugins(); \
385 const QString &LOWERCASE##Plugin = pluginManager->LOWERCASE##Plugin(); \
386 int LOWERCASE##Index = -1; \
388 for (it = LOWERCASE##Plugins.constBegin(), i = 0; it != LOWERCASE##Plugins.constEnd(); ++it, ++i) { \
389 LOWERCASE##Box->addItem(it.value(), it.key()); \
390 if (LOWERCASE##Index < 0 && LOWERCASE##Plugin == it.key()) \
391 LOWERCASE##Index = i; \
394 LOWERCASE##Box->addItem(DISABLE); \
396 if (LOWERCASE##Plugin == QLatin1String("none")) \
397 LOWERCASE##Index = LOWERCASE##Box->count()-1; \
399 if (LOWERCASE##Index == -1) { \
400 if (!LOWERCASE##Plugin.isEmpty()) \
401 LOWERCASE##Box->addItem(NOTFOUND.arg(LOWERCASE##Plugin), LOWERCASE##Plugin); \
402 LOWERCASE##Index = LOWERCASE##Box->count()-1; \
405 LOWERCASE##Box->setCurrentIndex(LOWERCASE##Index);
407 QString pluginNotFound = tr("Plugin not found (%1)");
408 HANDLE_PLUGIN(addressbook, Addressbook, tr("Disable address book"), pluginNotFound)
409 HANDLE_PLUGIN(password, Password, tr("Disable passwords"), pluginNotFound)
410 HANDLE_PLUGIN(spellchecker, Spellchecker, tr("Disable spell checking"), pluginNotFound)
411 #undef HANDLE_PLUGIN
413 m_parent->setOriginalPlugins(passwordPlugin, addressbookPlugin, spellcheckerPlugin);
416 markReadCheckbox->setChecked(s.value(Common::SettingsNames::autoMarkReadEnabled, QVariant(true)).toBool());
417 markReadSeconds->setValue(s.value(Common::SettingsNames::autoMarkReadSeconds, QVariant(0)).toUInt());
418 connect(markReadCheckbox, &QAbstractButton::toggled, markReadSeconds, &QWidget::setEnabled);
420 auto mboxDropAction = s.value(Common::SettingsNames::mboxDropAction, QVariant(QStringLiteral("ask"))).toString();
422 connect(mboxDropActionCheckbox, &QAbstractButton::toggled, mboxDropActionBox, &QWidget::setEnabled);
423 if (mboxDropAction != QStringLiteral("ask"))
424 mboxDropActionCheckbox->setChecked(true);
426 mboxDropActionBox->addItem(tr("Move"), QStringLiteral("move"));
427 if (mboxDropAction == QStringLiteral("move"))
428 mboxDropActionBox->setCurrentIndex(mboxDropActionBox->count() - 1);
429 mboxDropActionBox->addItem(tr("Copy"), QStringLiteral("copy"));
430 if (mboxDropAction == QStringLiteral("copy"))
431 mboxDropActionBox->setCurrentIndex(mboxDropActionBox->count() - 1);
433 showHomepageCheckbox->setChecked(s.value(Common::SettingsNames::appLoadHomepage, QVariant(true)).toBool());
434 showHomepageCheckbox->setToolTip(trUtf8("<p>If enabled, Trojitá will show its homepage upon startup.</p>"
435 "<p>The remote server will receive the user's IP address and versions of Trojitá, the Qt library, "
436 "and the underlying operating system. No private information, like account settings "
437 "or IMAP server details, are collected.</p>"));
439 guiSystrayCheckbox->setChecked(s.value(Common::SettingsNames::guiShowSystray, QVariant(true)).toBool());
440 guiStartMinimizedCheckbox->setChecked(s.value(Common::SettingsNames::guiStartMinimized, QVariant(false)).toBool());
442 preferPlaintextCheckbox->setChecked(s.value(Common::SettingsNames::guiPreferPlaintextRendering).toBool());
443 revealTrojitaVersions->setChecked(s.value(Common::SettingsNames::interopRevealVersions, QVariant(true)).toBool());
445 connect(identityTabelView, &QAbstractItemView::clicked, this, &GeneralPage::updateWidgets);
446 connect(identityTabelView, &QAbstractItemView::doubleClicked, this, &GeneralPage::editButtonClicked);
447 connect(m_identitiesModel, &QAbstractItemModel::layoutChanged, this, &GeneralPage::updateWidgets);
448 connect(m_identitiesModel, &QAbstractItemModel::rowsInserted, this, &GeneralPage::updateWidgets);
449 connect(m_identitiesModel, &QAbstractItemModel::rowsRemoved, this, &GeneralPage::updateWidgets);
450 connect(m_identitiesModel, &QAbstractItemModel::dataChanged, this, &GeneralPage::updateWidgets);
451 connect(moveUpButton, &QAbstractButton::clicked, this, &GeneralPage::moveIdentityUp);
452 connect(moveDownButton, &QAbstractButton::clicked, this, &GeneralPage::moveIdentityDown);
453 connect(addButton, &QAbstractButton::clicked, this, &GeneralPage::addButtonClicked);
454 connect(editButton, &QAbstractButton::clicked, this, &GeneralPage::editButtonClicked);
455 connect(deleteButton, &QAbstractButton::clicked, this, &GeneralPage::deleteButtonClicked);
456 connect(passwordBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &GeneralPage::passwordPluginChanged);
458 connect(this, &GeneralPage::reloadPasswords, m_parent, &SettingsDialog::reloadPasswordsRequested);
460 updateWidgets();
463 void GeneralPage::passwordPluginChanged()
465 const QString &passwordPlugin = m_parent->pluginManager()->passwordPlugin();
466 const QString &selectedPasswordPlugin = passwordBox->itemData(passwordBox->currentIndex()).toString();
468 if (selectedPasswordPlugin != passwordPlugin) {
469 m_parent->pluginManager()->setPasswordPlugin(selectedPasswordPlugin);
470 emit reloadPasswords();
474 void GeneralPage::updateWidgets()
476 bool enabled = identityTabelView->currentIndex().isValid();
477 deleteButton->setEnabled(enabled);
478 editButton->setEnabled(enabled);
479 bool upEnabled = m_identitiesModel->rowCount() > 0 && identityTabelView->currentIndex().row() > 0;
480 bool downEnabled = m_identitiesModel->rowCount() > 0 && identityTabelView->currentIndex().isValid() &&
481 identityTabelView->currentIndex().row() < m_identitiesModel->rowCount() - 1;
482 moveUpButton->setEnabled(upEnabled);
483 moveDownButton->setEnabled(downEnabled);
485 identityTabelView->resizeColumnToContents(Composer::SenderIdentitiesModel::COLUMN_NAME);
487 emit widgetsUpdated();
490 void GeneralPage::moveIdentityUp()
492 int from = identityTabelView->currentIndex().row();
493 int to = identityTabelView->currentIndex().row() - 1;
495 m_identitiesModel->moveIdentity(from, to);
496 updateWidgets();
499 void GeneralPage::moveIdentityDown()
501 int from = identityTabelView->currentIndex().row();
502 int to = identityTabelView->currentIndex().row() + 1;
504 m_identitiesModel->moveIdentity(from, to);
505 updateWidgets();
508 void GeneralPage::addButtonClicked()
510 m_identitiesModel->appendIdentity(Composer::ItemSenderIdentity());
511 identityTabelView->setCurrentIndex(m_identitiesModel->index(m_identitiesModel->rowCount() - 1, 0));
512 EditIdentity *dialog = new EditIdentity(this, m_identitiesModel, identityTabelView->currentIndex());
513 dialog->setDeleteOnReject();
514 dialog->setWindowTitle(tr("Add New Identity"));
515 dialog->show();
516 updateWidgets();
519 void GeneralPage::editButtonClicked()
521 EditIdentity *dialog = new EditIdentity(this, m_identitiesModel, identityTabelView->currentIndex());
522 dialog->setWindowTitle(tr("Edit Identity"));
523 dialog->show();
526 void GeneralPage::deleteButtonClicked()
528 Q_ASSERT(identityTabelView->currentIndex().isValid());
529 QMessageBox::StandardButton answer =
530 QMessageBox::question(this, tr("Delete Identity?"),
531 tr("Are you sure you want to delete identity %1 <%2>?").arg(
532 m_identitiesModel->index(identityTabelView->currentIndex().row(),
533 Composer::SenderIdentitiesModel::COLUMN_NAME).data().toString(),
534 m_identitiesModel->index(identityTabelView->currentIndex().row(),
535 Composer::SenderIdentitiesModel::COLUMN_EMAIL).data().toString()),
536 QMessageBox::Yes | QMessageBox::No);
537 if (answer == QMessageBox::Yes) {
538 m_identitiesModel->removeIdentityAt(identityTabelView->currentIndex().row());
539 updateWidgets();
543 void GeneralPage::save(QSettings &s)
545 m_identitiesModel->saveToSettings(s);
546 s.setValue(Common::SettingsNames::autoMarkReadEnabled, markReadCheckbox->isChecked());
547 s.setValue(Common::SettingsNames::autoMarkReadSeconds, markReadSeconds->value());
548 s.setValue(Common::SettingsNames::mboxDropAction,
549 mboxDropActionCheckbox->isChecked() ? mboxDropActionBox->currentData() : QStringLiteral("ask"));
550 s.setValue(Common::SettingsNames::appLoadHomepage, showHomepageCheckbox->isChecked());
551 s.setValue(Common::SettingsNames::guiPreferPlaintextRendering, preferPlaintextCheckbox->isChecked());
552 s.setValue(Common::SettingsNames::guiShowSystray, guiSystrayCheckbox->isChecked());
553 s.setValue(Common::SettingsNames::guiStartMinimized, guiStartMinimizedCheckbox->isChecked());
554 s.setValue(Common::SettingsNames::interopRevealVersions, revealTrojitaVersions->isChecked());
556 #define HANDLE_PLUGIN(LOWERCASE, UPPERCASE) \
557 const QString &LOWERCASE##Plugin = m_parent->pluginManager()->LOWERCASE##Plugin(); \
558 const QString &selected##UPPERCASE##Plugin = LOWERCASE##Box->itemData(LOWERCASE##Box->currentIndex()).toString(); \
559 if (selected##UPPERCASE##Plugin != LOWERCASE##Plugin) { \
560 m_parent->pluginManager()->set##UPPERCASE##Plugin(selected##UPPERCASE##Plugin); \
563 HANDLE_PLUGIN(addressbook, Addressbook);
564 HANDLE_PLUGIN(password, Password);
565 HANDLE_PLUGIN(spellchecker, Spellchecker);
566 #undef HANDLE_PLUGIN
568 emit saved();
571 QWidget *GeneralPage::asWidget()
573 return this;
576 bool GeneralPage::checkValidity() const
578 if (m_identitiesModel->rowCount() < 1) {
579 QToolTip::showText(identityTabelView->mapToGlobal(QPoint(10, identityTabelView->height() / 2)),
580 tr("Please define some identities here"), 0);
581 return false;
583 return true;
586 bool GeneralPage::passwordFailures(QString &message) const
588 Q_UNUSED(message);
589 return false;
592 EditIdentity::EditIdentity(QWidget *parent, Composer::SenderIdentitiesModel *identitiesModel, const QModelIndex &currentIndex):
593 QDialog(parent), Ui_EditIdentity(), m_identitiesModel(identitiesModel), m_deleteOnReject(false)
595 Ui_EditIdentity::setupUi(this);
596 m_mapper = new QDataWidgetMapper(this);
597 m_mapper->setModel(m_identitiesModel);
598 m_mapper->addMapping(realNameLineEdit, Composer::SenderIdentitiesModel::COLUMN_NAME);
599 m_mapper->addMapping(emailLineEdit, Composer::SenderIdentitiesModel::COLUMN_EMAIL);
600 m_mapper->addMapping(organisationLineEdit, Composer::SenderIdentitiesModel::COLUMN_ORGANIZATION);
601 m_mapper->addMapping(signaturePlainTextEdit, Composer::SenderIdentitiesModel::COLUMN_SIGNATURE);
602 m_mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
603 m_mapper->setCurrentIndex(currentIndex.row());
604 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
605 connect(realNameLineEdit, &QLineEdit::textChanged, this, &EditIdentity::enableButton);
606 connect(emailLineEdit, &QLineEdit::textChanged, this, &EditIdentity::enableButton);
607 connect(organisationLineEdit, &QLineEdit::textChanged, this, &EditIdentity::enableButton);
608 connect(signaturePlainTextEdit, &QPlainTextEdit::textChanged, this, &EditIdentity::enableButton);
609 connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this, &QDialog::accept);
610 connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, this, &QDialog::reject);
611 connect(this, &QDialog::accepted, m_mapper, &QDataWidgetMapper::submit);
612 connect(this, &QDialog::rejected, this, &EditIdentity::onReject);
613 setModal(true);
614 signaturePlainTextEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
617 void EditIdentity::enableButton()
619 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(
620 !realNameLineEdit->text().isEmpty() && !emailLineEdit->text().isEmpty());
623 /** @short If enabled, make sure that the current row gets deleted when the dialog is rejected */
624 void EditIdentity::setDeleteOnReject(const bool reject)
626 m_deleteOnReject = reject;
629 void EditIdentity::onReject()
631 if (m_deleteOnReject)
632 m_identitiesModel->removeIdentityAt(m_mapper->currentIndex());
635 EditFavoriteTag::EditFavoriteTag(QWidget *parent, Imap::Mailbox::FavoriteTagsModel *favoriteTagsModel, const QModelIndex &currentIndex):
636 QDialog(parent), Ui_EditFavoriteTag(), m_favoriteTagsModel(favoriteTagsModel), currentIndex(currentIndex), m_deleteOnReject(false)
638 Ui_EditFavoriteTag::setupUi(this);
640 nameLineEdit->setText(name());
641 setColorButtonColor(color());
642 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
644 connect(colorButton, &QAbstractButton::clicked, this, &EditFavoriteTag::colorButtonClick);
645 connect(nameLineEdit, &QLineEdit::textChanged, this, &EditFavoriteTag::tryEnableButton);
647 connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this, &QDialog::accept);
648 connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, this, &QDialog::reject);
649 connect(this, &QDialog::accepted, this, &EditFavoriteTag::onAccept);
650 connect(this, &QDialog::rejected, this, &EditFavoriteTag::onReject);
651 setModal(true);
654 QString EditFavoriteTag::name()
656 return m_favoriteTagsModel->data(m_favoriteTagsModel->index(currentIndex.row(), Imap::Mailbox::FavoriteTagsModel::COLUMN_NAME)).toString();
659 QString EditFavoriteTag::color()
661 return m_favoriteTagsModel->data(m_favoriteTagsModel->index(currentIndex.row(), Imap::Mailbox::FavoriteTagsModel::COLUMN_COLOR)).toString();
664 void EditFavoriteTag::setColorButtonColor(const QString color)
666 colorButton->setProperty("colorName", color);
667 QPalette pal = colorButton->palette();
668 pal.setColor(QPalette::Button, QColor(color));
669 colorButton->setAutoFillBackground(true);
670 colorButton->setPalette(pal);
671 colorButton->setFlat(true);
672 colorButton->update();
675 void EditFavoriteTag::colorButtonClick()
677 const QColor color = QColorDialog::getColor(QColor(colorButton->property("colorName").toString()), this, tr("Select tag color"));
678 if (color.isValid()) {
679 setColorButtonColor(color.name());
680 tryEnableButton();
684 void EditFavoriteTag::tryEnableButton()
686 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(
687 !nameLineEdit->text().isEmpty() && QColor(colorButton->property("colorName").toString()).isValid()
691 /** @short If enabled, make sure that the current row gets deleted when the dialog is rejected */
692 void EditFavoriteTag::setDeleteOnReject(const bool reject)
694 m_deleteOnReject = reject;
697 void EditFavoriteTag::onAccept()
699 m_favoriteTagsModel->setData(m_favoriteTagsModel->index(currentIndex.row(), Imap::Mailbox::FavoriteTagsModel::COLUMN_NAME),
700 nameLineEdit->text());
701 m_favoriteTagsModel->setData(m_favoriteTagsModel->index(currentIndex.row(), Imap::Mailbox::FavoriteTagsModel::COLUMN_COLOR),
702 colorButton->property("colorName"));
705 void EditFavoriteTag::onReject()
707 if (m_deleteOnReject)
708 m_favoriteTagsModel->removeTagAt(currentIndex.row());
711 ImapPage::ImapPage(SettingsDialog *parent, QSettings &s): QScrollArea(parent), Ui_ImapPage(), m_parent(parent)
713 Ui_ImapPage::setupUi(this);
714 method->insertItem(NETWORK, tr("Network Connection"));
715 method->insertItem(PROCESS, tr("Local Process"));
717 encryption->insertItem(NONE, tr("No encryption"));
718 encryption->insertItem(STARTTLS, tr("Use encryption (STARTTLS)"));
719 encryption->insertItem(SSL, tr("Force encryption (TLS)"));
720 using Common::SettingsNames;
721 int defaultImapPort = Common::PORT_IMAPS;
723 if (s.value(SettingsNames::imapMethodKey).toString() == SettingsNames::methodTCP) {
724 method->setCurrentIndex(NETWORK);
726 if (s.value(SettingsNames::imapStartTlsKey,true).toBool())
727 encryption->setCurrentIndex(STARTTLS);
728 else
729 encryption->setCurrentIndex(NONE);
731 defaultImapPort = Common::PORT_IMAP;
732 } else if (s.value(SettingsNames::imapMethodKey).toString() == SettingsNames::methodSSL) {
733 method->setCurrentIndex(NETWORK);
734 encryption->setCurrentIndex(SSL);
735 } else if (s.value(SettingsNames::imapMethodKey).toString() == SettingsNames::methodProcess) {
736 method->setCurrentIndex(PROCESS);
737 } else {
738 // Default settings -- let's assume SSL and hope that users who just press Cancel will configure when they see
739 // the network error...
740 method->setCurrentIndex(NETWORK);
741 encryption->setCurrentIndex(SSL);
744 imapHost->setText(s.value(SettingsNames::imapHostKey).toString());
745 imapPort->setText(s.value(SettingsNames::imapPortKey, QString::number(defaultImapPort)).toString());
746 imapPort->setValidator(new QIntValidator(1, 65535, this));
747 connect(imapPort, &QLineEdit::textChanged, this, &ImapPage::maybeShowPortWarning);
748 connect(encryption, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ImapPage::maybeShowPortWarning);
749 connect(method, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ImapPage::maybeShowPortWarning);
750 connect(encryption, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ImapPage::changePort);
751 portWarning->setStyleSheet(SettingsDialog::warningStyleSheet);
752 connect(imapPass, &QLineEdit::textChanged, this, &ImapPage::updateWidgets);
753 imapUser->setText(s.value(SettingsNames::imapUserKey).toString());
754 processPath->setText(s.value(SettingsNames::imapProcessKey).toString());
756 imapCapabilitiesBlacklist->setText(s.value(SettingsNames::imapBlacklistedCapabilities).toStringList().join(QStringLiteral(" ")));
757 imapUseSystemProxy->setChecked(s.value(SettingsNames::imapUseSystemProxy, true).toBool());
758 imapNeedsNetwork->setChecked(s.value(SettingsNames::imapNeedsNetwork, true).toBool());
759 imapIdleRenewal->setValue(s.value(SettingsNames::imapIdleRenewal, QVariant(29)).toInt());
760 imapNumberRefreshInterval->setValue(m_parent->imapAccess()->numberRefreshInterval());
761 accountIcon->setText(s.value(SettingsNames::imapAccountIcon).toString());
762 archiveFolderName->setText(s.value(SettingsNames::imapArchiveFolderName).toString().isEmpty() ?
763 SettingsNames::imapDefaultArchiveFolderName : s.value(SettingsNames::imapArchiveFolderName).toString());
765 m_imapPort = s.value(SettingsNames::imapPortKey, QString::number(defaultImapPort)).value<quint16>();
767 connect(method, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ImapPage::updateWidgets);
769 // FIXME: use another account-id
770 m_pwWatcher = m_parent->imapAccess()->passwordWatcher();
771 connect(m_pwWatcher, &UiUtils::PasswordWatcher::stateChanged, this, &ImapPage::updateWidgets);
772 connect(m_pwWatcher, &UiUtils::PasswordWatcher::savingFailed, this, &ImapPage::saved);
773 connect(m_pwWatcher, &UiUtils::PasswordWatcher::savingDone, this, &ImapPage::saved);
774 connect(m_pwWatcher, &UiUtils::PasswordWatcher::readingDone, this, &ImapPage::slotSetPassword);
775 connect(m_parent, &SettingsDialog::reloadPasswordsRequested, imapPass, &QLineEdit::clear);
776 connect(m_parent, &SettingsDialog::reloadPasswordsRequested, m_pwWatcher, &UiUtils::PasswordWatcher::reloadPassword);
778 updateWidgets();
779 maybeShowPortWarning();
782 void ImapPage::slotSetPassword()
784 imapPass->setText(m_pwWatcher->password());
787 void ImapPage::changePort()
789 imapPort->setText(QString::number(encryption->currentIndex() == SSL ? Common::PORT_IMAPS : Common::PORT_IMAP));
792 void ImapPage::updateWidgets()
794 QFormLayout *lay = formLayout;
795 Q_ASSERT(lay);
797 switch (method->currentIndex()) {
798 case NETWORK:
799 imapHost->setVisible(true);
800 imapPort->setVisible(true);
801 encryption->setVisible(true);
802 lay->labelForField(imapHost)->setVisible(true);
803 lay->labelForField(imapPort)->setVisible(true);
804 lay->labelForField(encryption)->setVisible(true);
805 processPath->setVisible(false);
806 lay->labelForField(processPath)->setVisible(false);
807 imapUseSystemProxy->setVisible(true);
808 lay->labelForField(imapUseSystemProxy)->setVisible(true);
809 // the "needs network" can very well apply to accounts using "local process" via SSH, so it is not disabled here
810 break;
811 default:
812 imapHost->setVisible(false);
813 imapPort->setVisible(false);
814 encryption->setVisible(false);
815 lay->labelForField(imapHost)->setVisible(false);
816 lay->labelForField(imapPort)->setVisible(false);
817 lay->labelForField(encryption)->setVisible(false);
818 processPath->setVisible(true);
819 lay->labelForField(processPath)->setVisible(true);
820 imapUseSystemProxy->setVisible(false);
821 lay->labelForField(imapUseSystemProxy)->setVisible(false);
824 switch (encryption->currentIndex()) {
825 case NONE:
826 case STARTTLS:
827 if (imapPort->text().isEmpty() || imapPort->text() == QString::number(Common::PORT_IMAPS))
828 imapPort->setText(QString::number(Common::PORT_IMAP));
829 break;
830 default:
831 if (imapPort->text().isEmpty() || imapPort->text() == QString::number(Common::PORT_IMAP))
832 imapPort->setText(QString::number(Common::PORT_IMAPS));
835 if (!m_pwWatcher->isPluginAvailable())
836 imapPass->setText(QString());
838 passwordWarning->setVisible(!imapPass->text().isEmpty());
839 if (m_pwWatcher->isStorageEncrypted()) {
840 passwordWarning->setStyleSheet(QString());
841 passwordWarning->setText(trUtf8("This password will be saved in encrypted storage. "
842 "If you do not enter password here, Trojitá will prompt for one when needed."));
843 } else {
844 passwordWarning->setStyleSheet(SettingsDialog::warningStyleSheet);
845 passwordWarning->setText(trUtf8("This password will be saved in clear text. "
846 "If you do not enter password here, Trojitá will prompt for one when needed."));
849 passwordPluginStatus->setVisible(!m_pwWatcher->isPluginAvailable() || m_pwWatcher->isWaitingForPlugin() || !m_pwWatcher->didReadOk() || !m_pwWatcher->didWriteOk());
850 passwordPluginStatus->setText(m_pwWatcher->progressMessage());
852 imapPass->setEnabled(m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin());
853 imapPassLabel->setEnabled(m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin());
855 emit widgetsUpdated();
858 void ImapPage::save(QSettings &s)
860 using Common::SettingsNames;
861 if (s.value(SettingsNames::imapHostKey) != imapHost->text()) {
862 s.remove(Common::SettingsNames::imapSslPemPubKey);
864 switch (method->currentIndex()) {
865 case NETWORK:
866 if (imapHost->text().isEmpty()) {
867 s.remove(SettingsNames::imapMethodKey);
868 } else if (encryption->currentIndex() == NONE){
869 s.setValue(SettingsNames::imapMethodKey, SettingsNames::methodTCP);
870 s.setValue(SettingsNames::imapStartTlsKey, false);
871 } else if (encryption->currentIndex() == STARTTLS){
872 s.setValue(SettingsNames::imapMethodKey, SettingsNames::methodTCP);
873 s.setValue(SettingsNames::imapStartTlsKey, true);
874 } else {
875 s.setValue(SettingsNames::imapMethodKey, SettingsNames::methodSSL);
876 s.setValue(SettingsNames::imapStartTlsKey, true);
878 s.setValue(SettingsNames::imapHostKey, imapHost->text());
879 s.setValue(SettingsNames::imapPortKey, imapPort->text());
880 s.setValue(SettingsNames::imapUseSystemProxy, imapUseSystemProxy->isChecked());
881 break;
882 default:
883 if (processPath->text().isEmpty()) {
884 s.remove(SettingsNames::imapMethodKey);
885 } else {
886 s.setValue(SettingsNames::imapMethodKey, SettingsNames::methodProcess);
888 s.setValue(SettingsNames::imapProcessKey, processPath->text());
890 s.setValue(SettingsNames::imapUserKey, imapUser->text());
891 s.setValue(SettingsNames::imapBlacklistedCapabilities, imapCapabilitiesBlacklist->text().split(QStringLiteral(" ")));
892 s.setValue(SettingsNames::imapNeedsNetwork, imapNeedsNetwork->isChecked());
893 s.setValue(SettingsNames::imapIdleRenewal, imapIdleRenewal->value());
894 m_parent->imapAccess()->setNumberRefreshInterval(imapNumberRefreshInterval->value());
896 s.setValue(SettingsNames::imapAccountIcon, accountIcon->text().isEmpty() ? QVariant() : QVariant(accountIcon->text()));
897 s.setValue(SettingsNames::imapArchiveFolderName, archiveFolderName->text());
899 if (m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin()) {
900 m_pwWatcher->setPassword(imapPass->text());
901 } else {
902 emit saved();
906 QWidget *ImapPage::asWidget()
908 return this;
911 bool ImapPage::checkValidity() const
913 switch (method->currentIndex()) {
914 case NETWORK:
915 // We don't require the username, and that's on purpose. Some servers *could* possibly support PREAUTH :)
916 if (checkProblemWithEmptyTextField(imapHost, tr("The IMAP server hostname is missing here")))
917 return false;
918 break;
919 default:
920 // PREAUTH must definitely be supported here -- think imap-over-ssh-with-ssh-keys etc.
921 if (checkProblemWithEmptyTextField(processPath,
922 tr("The command line to the IMAP server is missing here. Perhaps you need to use SSL or TCP?"))) {
923 return false;
925 break;
927 return true;
930 void ImapPage::maybeShowPortWarning()
932 if (method->currentIndex() == PROCESS) {
933 portWarning->setVisible(false);
934 return;
937 if (encryption->currentIndex() == SSL) {
938 portWarning->setVisible(imapPort->text() != QString::number(Common::PORT_IMAPS));
939 portWarning->setText(tr("This port is nonstandard. The default port for IMAP secured over SSL/TLS is %1.").arg(Common::PORT_IMAPS));
940 } else {
941 portWarning->setVisible(imapPort->text() != QString::number(Common::PORT_IMAP));
942 if (encryption->currentIndex() == STARTTLS) {
943 portWarning->setText(tr("This port is nonstandard. The default port for IMAP secured via STARTTLS is %1.").arg(Common::PORT_IMAP));
944 } else {
945 portWarning->setText(tr("This port is nonstandard. The default port for IMAP over cleartext is %1.").arg(Common::PORT_IMAP));
950 bool ImapPage::passwordFailures(QString &message) const
952 if (!m_pwWatcher->isPluginAvailable() || m_pwWatcher->isWaitingForPlugin() || m_pwWatcher->didWriteOk()) {
953 return false;
954 } else {
955 message = m_pwWatcher->progressMessage();
956 return true;
961 CachePage::CachePage(QWidget *parent, QSettings &s): QScrollArea(parent), Ui_CachePage()
963 Ui_CachePage::setupUi(this);
965 using Common::SettingsNames;
967 QString val = s.value(SettingsNames::cacheOfflineKey).toString();
968 if (val == SettingsNames::cacheOfflineAll) {
969 offlineEverything->setChecked(true);
970 } else if (val == SettingsNames::cacheOfflineNone) {
971 offlineNope->setChecked(true);
972 } else {
973 offlineXDays->setChecked(true);
976 offlineNumberOfDays->setValue(s.value(SettingsNames::cacheOfflineNumberDaysKey, QVariant(30)).toInt());
978 val = s.value(SettingsNames::watchedFoldersKey).toString();
979 if (val == Common::SettingsNames::watchAll) {
980 watchAll->setChecked(true);
981 } else if (val == Common::SettingsNames::watchSubscribed) {
982 watchSubscribed->setChecked(true);
983 } else {
984 watchInbox->setChecked(true);
987 updateWidgets();
989 connect(offlineNope, &QAbstractButton::clicked, this, &CachePage::updateWidgets);
990 connect(offlineXDays, &QAbstractButton::clicked, this, &CachePage::updateWidgets);
991 connect(offlineEverything, &QAbstractButton::clicked, this, &CachePage::updateWidgets);
994 void CachePage::updateWidgets()
996 offlineNumberOfDays->setEnabled(offlineXDays->isChecked());
997 emit widgetsUpdated();
1000 void CachePage::save(QSettings &s)
1002 using Common::SettingsNames;
1004 if (offlineEverything->isChecked())
1005 s.setValue(SettingsNames::cacheOfflineKey, SettingsNames::cacheOfflineAll);
1006 else if (offlineXDays->isChecked())
1007 s.setValue(SettingsNames::cacheOfflineKey, SettingsNames::cacheOfflineXDays);
1008 else
1009 s.setValue(SettingsNames::cacheOfflineKey, SettingsNames::cacheOfflineNone);
1011 s.setValue(SettingsNames::cacheOfflineNumberDaysKey, offlineNumberOfDays->value());
1013 if (watchAll->isChecked()) {
1014 s.setValue(SettingsNames::watchedFoldersKey, SettingsNames::watchAll);
1015 } else if (watchSubscribed->isChecked()) {
1016 s.setValue(SettingsNames::watchedFoldersKey, SettingsNames::watchSubscribed);
1017 } else {
1018 s.setValue(SettingsNames::watchedFoldersKey, SettingsNames::watchOnlyInbox);
1021 emit saved();
1024 QWidget *CachePage::asWidget()
1026 return this;
1029 bool CachePage::checkValidity() const
1031 // Nothing really special for this class
1032 return true;
1035 bool CachePage::passwordFailures(QString &message) const
1037 Q_UNUSED(message);
1038 return false;
1041 OutgoingPage::OutgoingPage(SettingsDialog *parent, QSettings &s): QScrollArea(parent), Ui_OutgoingPage(), m_parent(parent)
1043 using Common::SettingsNames;
1044 Ui_OutgoingPage::setupUi(this);
1045 // FIXME: use another account-id at some point in future
1046 // we are now using the profile to avoid overwriting passwords of
1047 // other profiles in secure storage
1048 QString profileName = QString::fromUtf8(qgetenv("TROJITA_PROFILE"));
1049 m_smtpAccountSettings = new MSA::Account(this, &s, profileName);
1051 portWarningLabel->setStyleSheet(SettingsDialog::warningStyleSheet);
1053 method->insertItem(NETWORK, tr("Network"));
1054 method->insertItem(SENDMAIL, tr("Local sendmail-compatible"));
1055 method->insertItem(IMAP_SENDMAIL, tr("IMAP SENDMAIL Extension"));;
1057 encryption->insertItem(SMTP, tr("No encryption"));
1058 encryption->insertItem(SMTP_STARTTLS, tr("Use encryption (STARTTLS)"));
1059 encryption->insertItem(SSMTP, tr("Force encryption (TLS)"));
1060 encryption->setCurrentIndex(SSMTP);
1062 connect(method, static_cast<void (QComboBox::*)(const int)>(&QComboBox::currentIndexChanged), this, &OutgoingPage::slotSetSubmissionMethod);
1063 connect(encryption, static_cast<void (QComboBox::*)(const int)>(&QComboBox::currentIndexChanged), this, &OutgoingPage::slotSetSubmissionMethod);
1065 connect(m_smtpAccountSettings, &MSA::Account::submissionMethodChanged, this, &OutgoingPage::updateWidgets);
1066 connect(m_smtpAccountSettings, &MSA::Account::saveToImapChanged, this, &OutgoingPage::updateWidgets);
1067 connect(m_smtpAccountSettings, &MSA::Account::authenticateEnabledChanged, this, &OutgoingPage::updateWidgets);
1068 connect(m_smtpAccountSettings, &MSA::Account::reuseImapAuthenticationChanged, this, &OutgoingPage::updateWidgets);
1069 connect(smtpPass, &QLineEdit::textChanged, this, &OutgoingPage::updateWidgets);
1070 connect(smtpHost, &LineEdit::textEditingFinished, m_smtpAccountSettings, &MSA::Account::setServer);
1071 connect(smtpUser, &LineEdit::textEditingFinished, m_smtpAccountSettings, &MSA::Account::setUsername);
1072 connect(smtpPort, &LineEdit::textEditingFinished, this, &OutgoingPage::setPortByText);
1073 connect(m_smtpAccountSettings, &MSA::Account::showPortWarning, this, &OutgoingPage::showPortWarning);
1074 connect(smtpAuth, &QAbstractButton::toggled, m_smtpAccountSettings, &MSA::Account::setAuthenticateEnabled);
1075 connect(smtpAuthReuseImapCreds, &QAbstractButton::toggled, m_smtpAccountSettings, &MSA::Account::setReuseImapAuthentication);
1076 connect(saveToImap, &QAbstractButton::toggled, m_smtpAccountSettings, &MSA::Account::setSaveToImap);
1077 connect(saveFolderName, &LineEdit::textEditingFinished, m_smtpAccountSettings, &MSA::Account::setSentMailboxName);
1078 connect(smtpBurl, &QAbstractButton::toggled, m_smtpAccountSettings, &MSA::Account::setUseBurl);
1079 connect(sendmail, &LineEdit::textEditingFinished, m_smtpAccountSettings, &MSA::Account::setPathToSendmail);
1081 m_pwWatcher = new UiUtils::PasswordWatcher(this, m_parent->pluginManager(),
1082 profileName.isEmpty() ? QStringLiteral("account-0") : profileName,
1083 QStringLiteral("smtp"));
1084 connect(m_pwWatcher, &UiUtils::PasswordWatcher::stateChanged, this, &OutgoingPage::updateWidgets);
1085 connect(m_pwWatcher, &UiUtils::PasswordWatcher::savingFailed, this, &OutgoingPage::saved);
1086 connect(m_pwWatcher, &UiUtils::PasswordWatcher::savingDone, this, &OutgoingPage::saved);
1087 connect(m_pwWatcher, &UiUtils::PasswordWatcher::readingDone, this, &OutgoingPage::slotSetPassword);
1088 connect(m_parent, &SettingsDialog::reloadPasswordsRequested, smtpPass, &QLineEdit::clear);
1089 connect(m_parent, &SettingsDialog::reloadPasswordsRequested, m_pwWatcher, &UiUtils::PasswordWatcher::reloadPassword);
1091 updateWidgets();
1094 void OutgoingPage::slotSetPassword()
1096 smtpPass->setText(m_pwWatcher->password());
1099 void OutgoingPage::slotSetSubmissionMethod()
1101 switch (method->currentIndex()) {
1102 case SENDMAIL:
1103 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::SENDMAIL);
1104 break;
1105 case IMAP_SENDMAIL:
1106 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::IMAP_SENDMAIL);
1107 break;
1108 case NETWORK:
1109 switch (encryption->currentIndex()) {
1110 case SMTP:
1111 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::SMTP);
1112 break;
1113 case SMTP_STARTTLS:
1114 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::SMTP_STARTTLS);
1115 break;
1116 case SSMTP:
1117 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::SSMTP);
1118 break;
1120 break;
1121 default:
1122 Q_ASSERT(false);
1124 // Toggle the default ports upon changing the delivery method
1125 smtpPort->setText(QString::number(m_smtpAccountSettings->port()));
1128 void OutgoingPage::setPortByText(const QString &text)
1130 m_smtpAccountSettings->setPort(text.toUShort());
1133 void OutgoingPage::updateWidgets()
1135 QFormLayout *lay = formLayout;
1136 Q_ASSERT(lay);
1138 switch (m_smtpAccountSettings->submissionMethod()) {
1139 case MSA::Account::Method::SMTP:
1140 method->setCurrentIndex(NETWORK);
1141 encryption->setCurrentIndex(SMTP);
1142 break;
1143 case MSA::Account::Method::SMTP_STARTTLS:
1144 method->setCurrentIndex(NETWORK);
1145 encryption->setCurrentIndex(SMTP_STARTTLS);
1146 break;
1147 case MSA::Account::Method::SSMTP:
1148 method->setCurrentIndex(NETWORK);
1149 encryption->setCurrentIndex(SSMTP);
1150 break;
1151 case MSA::Account::Method::SENDMAIL:
1152 method->setCurrentIndex(SENDMAIL);
1153 encryption->setVisible(false);
1154 encryptionLabel->setVisible(false);
1155 break;
1156 case MSA::Account::Method::IMAP_SENDMAIL:
1157 method->setCurrentIndex(IMAP_SENDMAIL);
1158 encryption->setVisible(false);
1159 encryptionLabel->setVisible(false);
1160 break;
1163 switch (m_smtpAccountSettings->submissionMethod()) {
1164 case MSA::Account::Method::SMTP:
1165 case MSA::Account::Method::SMTP_STARTTLS:
1166 case MSA::Account::Method::SSMTP:
1168 encryption->setVisible(true);
1169 encryptionLabel->setVisible(true);
1170 smtpHost->setVisible(true);
1171 lay->labelForField(smtpHost)->setVisible(true);
1172 smtpHost->setText(m_smtpAccountSettings->server());
1173 smtpPort->setVisible(true);
1174 lay->labelForField(smtpPort)->setVisible(true);
1175 smtpPort->setText(QString::number(m_smtpAccountSettings->port()));
1176 smtpPort->setValidator(new QIntValidator(1, 65535, this));
1177 smtpAuth->setVisible(true);
1178 lay->labelForField(smtpAuth)->setVisible(true);
1179 bool authEnabled = m_smtpAccountSettings->authenticateEnabled();
1180 smtpAuth->setChecked(authEnabled);
1181 smtpAuthReuseImapCreds->setVisible(authEnabled);
1182 lay->labelForField(smtpAuthReuseImapCreds)->setVisible(authEnabled);
1183 bool reuseImapCreds = m_smtpAccountSettings->reuseImapAuthentication();
1184 smtpAuthReuseImapCreds->setChecked(reuseImapCreds);
1185 smtpUser->setVisible(authEnabled && !reuseImapCreds);
1186 lay->labelForField(smtpUser)->setVisible(authEnabled && !reuseImapCreds);
1187 smtpUser->setText(m_smtpAccountSettings->username());
1188 sendmail->setVisible(false);
1189 lay->labelForField(sendmail)->setVisible(false);
1190 saveToImap->setVisible(true);
1191 lay->labelForField(saveToImap)->setVisible(true);
1192 saveToImap->setChecked(m_smtpAccountSettings->saveToImap());
1193 smtpBurl->setVisible(saveToImap->isChecked());
1194 lay->labelForField(smtpBurl)->setVisible(saveToImap->isChecked());
1195 smtpBurl->setChecked(m_smtpAccountSettings->useBurl());
1197 if (!m_pwWatcher->isPluginAvailable())
1198 smtpPass->setText(QString());
1200 passwordWarning->setVisible(authEnabled && !reuseImapCreds && !smtpPass->text().isEmpty());
1201 if (m_pwWatcher->isStorageEncrypted()) {
1202 passwordWarning->setStyleSheet(QString());
1203 passwordWarning->setText(trUtf8("This password will be saved in encrypted storage. "
1204 "If you do not enter password here, Trojitá will prompt for one when needed."));
1205 } else {
1206 passwordWarning->setStyleSheet(SettingsDialog::warningStyleSheet);
1207 passwordWarning->setText(trUtf8("This password will be saved in clear text. "
1208 "If you do not enter password here, Trojitá will prompt for one when needed."));
1211 passwordPluginStatus->setVisible(authEnabled && !reuseImapCreds &&
1212 (!m_pwWatcher->isPluginAvailable() || m_pwWatcher->isWaitingForPlugin() || !m_pwWatcher->didReadOk() || !m_pwWatcher->didWriteOk()));
1213 passwordPluginStatus->setText(m_pwWatcher->progressMessage());
1215 smtpPass->setVisible(authEnabled && !reuseImapCreds);
1216 smtpPass->setEnabled(m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin());
1217 lay->labelForField(smtpPass)->setVisible(authEnabled && !reuseImapCreds);
1218 lay->labelForField(smtpPass)->setEnabled(m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin());
1220 break;
1222 case MSA::Account::Method::SENDMAIL:
1223 case MSA::Account::Method::IMAP_SENDMAIL:
1224 encryption->setVisible(false);
1225 encryptionLabel->setVisible(false);
1226 smtpHost->setVisible(false);
1227 lay->labelForField(smtpHost)->setVisible(false);
1228 smtpPort->setVisible(false);
1229 lay->labelForField(smtpPort)->setVisible(false);
1230 showPortWarning(QString());
1231 smtpAuth->setVisible(false);
1232 lay->labelForField(smtpAuth)->setVisible(false);
1233 smtpUser->setVisible(false);
1234 lay->labelForField(smtpUser)->setVisible(false);
1235 smtpPass->setVisible(false);
1236 lay->labelForField(smtpPass)->setVisible(false);
1237 passwordWarning->setVisible(false);
1238 passwordPluginStatus->setVisible(false);
1239 if (m_smtpAccountSettings->submissionMethod() == MSA::Account::Method::SENDMAIL) {
1240 sendmail->setVisible(true);
1241 lay->labelForField(sendmail)->setVisible(true);
1242 sendmail->setText(m_smtpAccountSettings->pathToSendmail());
1243 if (sendmail->text().isEmpty())
1244 sendmail->setText(Common::SettingsNames::sendmailDefaultCmd);
1245 saveToImap->setVisible(true);
1246 saveToImap->setChecked(m_smtpAccountSettings->saveToImap());
1247 lay->labelForField(saveToImap)->setVisible(true);
1248 } else {
1249 sendmail->setVisible(false);
1250 lay->labelForField(sendmail)->setVisible(false);
1251 saveToImap->setChecked(true);
1252 saveToImap->setVisible(false);
1253 lay->labelForField(saveToImap)->setVisible(false);
1255 smtpBurl->setVisible(false);
1256 lay->labelForField(smtpBurl)->setVisible(false);
1257 smtpBurl->setChecked(m_smtpAccountSettings->useBurl());
1258 passwordPluginStatus->setVisible(false);
1260 saveFolderName->setVisible(saveToImap->isChecked());
1261 lay->labelForField(saveFolderName)->setVisible(saveToImap->isChecked());
1262 saveFolderName->setText(m_smtpAccountSettings->sentMailboxName());
1264 emit widgetsUpdated();
1268 void OutgoingPage::save(QSettings &s)
1270 m_smtpAccountSettings->saveSettings();
1272 if (smtpAuth->isVisibleTo(this) && smtpAuth->isChecked() && m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin()) {
1273 m_pwWatcher->setPassword(smtpPass->text());
1274 } else {
1275 emit saved();
1279 void OutgoingPage::showPortWarning(const QString &warning)
1281 if (!warning.isEmpty()) {
1282 portWarningLabel->setVisible(true);
1283 portWarningLabel->setText(warning);
1284 } else {
1285 portWarningLabel->setVisible(false);
1290 QWidget *OutgoingPage::asWidget()
1292 return this;
1295 bool OutgoingPage::checkValidity() const
1297 switch (m_smtpAccountSettings->submissionMethod()) {
1298 case MSA::Account::Method::SMTP:
1299 case MSA::Account::Method::SMTP_STARTTLS:
1300 case MSA::Account::Method::SSMTP:
1301 if (checkProblemWithEmptyTextField(smtpHost, tr("The SMTP server hostname is missing here")))
1302 return false;
1303 if (smtpAuth->isChecked() && !smtpAuthReuseImapCreds->isChecked() && checkProblemWithEmptyTextField(smtpUser, tr("The SMTP username is missing here")))
1304 return false;
1305 break;
1306 case MSA::Account::Method::SENDMAIL:
1307 if (checkProblemWithEmptyTextField(sendmail, tr("The SMTP server hostname is missing here")))
1308 return false;
1309 break;
1310 case MSA::Account::Method::IMAP_SENDMAIL:
1311 break;
1314 if (saveToImap->isChecked() && checkProblemWithEmptyTextField(saveFolderName, tr("Please specify the folder name here")))
1315 return false;
1317 return true;
1320 bool OutgoingPage::passwordFailures(QString &message) const
1322 // The const_cast is needed as Qt4 does not define the arguement of isVisibleTo as const
1323 if (!smtpAuth->isVisibleTo(const_cast<Gui::OutgoingPage*>(this)) || !smtpAuth->isChecked() || !m_pwWatcher->isPluginAvailable() || m_pwWatcher->isWaitingForPlugin() || m_pwWatcher->didWriteOk()) {
1324 return false;
1325 } else {
1326 message = m_pwWatcher->progressMessage();
1327 return true;
1331 #ifdef XTUPLE_CONNECT
1332 XtConnectPage::XtConnectPage(QWidget *parent, QSettings &s, ImapPage *imapPage): QWidget(parent), imap(imapPage)
1334 // Take care not to clash with the cache of the GUI
1335 QString cacheLocation = Common::writablePath(Common::LOCATION_CACHE) + QString::fromAscii("xtconnect-trojita");
1336 QFormLayout *layout = new QFormLayout(this);
1337 cacheDir = new QLineEdit(s.value(Common::SettingsNames::xtConnectCacheDirectory, cacheLocation).toString(), this);
1338 layout->addRow(tr("Cache Directory"), cacheDir);
1340 QGroupBox *box = new QGroupBox(tr("Mailboxes to synchronize"), this);
1341 QVBoxLayout *boxLayout = new QVBoxLayout(box);
1342 QListWidget *mailboxes = new QListWidget(box);
1343 mailboxes->addItems(s.value(Common::SettingsNames::xtSyncMailboxList).toStringList());
1344 for (int i = 0; i < mailboxes->count(); ++i) {
1345 mailboxes->item(i)->setFlags(Qt::ItemIsEnabled);
1347 mailboxes->setToolTip(tr("Please use context menu inside the main application to select mailboxes to synchronize"));
1348 boxLayout->addWidget(mailboxes);
1349 layout->addRow(box);
1351 QString optionHost = s.value(Common::SettingsNames::xtDbHost).toString();
1352 int optionPort = s.value(Common::SettingsNames::xtDbPort, QVariant(5432)).toInt();
1353 QString optionDbname = s.value(Common::SettingsNames::xtDbDbName).toString();
1354 QString optionUsername = s.value(Common::SettingsNames::xtDbUser).toString();
1356 QStringList args = QCoreApplication::arguments();
1357 for (int i = 1; i < args.length(); i++) {
1358 if (args.at(i) == "-h" && args.length() > i)
1359 optionHost = args.at(++i);
1360 else if (args.at(i) == "-d" && args.length() > i)
1361 optionDbname = args.at(++i);
1362 else if (args.at(i) == "-p" && args.length() > i)
1363 optionPort = args.at(++i).toInt();
1364 else if (args.at(i) == "-U" && args.length() > i)
1365 optionUsername = args.at(++i);
1369 hostName = new QLineEdit(optionHost);
1370 layout->addRow(tr("DB Hostname"), hostName);
1371 port = new QSpinBox();
1372 port->setRange(1, 65535);
1373 port->setValue(optionPort);
1374 layout->addRow(tr("DB Port"), port);
1375 dbName = new QLineEdit(optionDbname);
1376 layout->addRow(tr("DB Name"), dbName);
1377 username = new QLineEdit(optionUsername);
1378 layout->addRow(tr("DB Username"), username);
1380 imapPasswordWarning = new QLabel(tr("Please fill in all IMAP options, including the password, at the IMAP page. "
1381 "If you do not save the password, background synchronization will not run."), this);
1382 imapPasswordWarning->setWordWrap(true);
1383 imapPasswordWarning->setStyleSheet(SettingsDialog::warningStyleSheet);
1384 layout->addRow(imapPasswordWarning);
1385 debugLog = new QCheckBox();
1386 layout->addRow(tr("Debugging"), debugLog);
1388 QPushButton *btn = new QPushButton(tr("Run xTuple Synchronization"));
1389 connect(btn, SIGNAL(clicked()), this, SLOT(runXtConnect()));
1390 layout->addRow(btn);
1393 void XtConnectPage::save(QSettings &s)
1395 s.setValue(Common::SettingsNames::xtConnectCacheDirectory, cacheDir->text());
1396 s.setValue(Common::SettingsNames::xtDbHost, hostName->text());
1397 s.setValue(Common::SettingsNames::xtDbPort, port->value());
1398 s.setValue(Common::SettingsNames::xtDbDbName, dbName->text());
1399 s.setValue(Common::SettingsNames::xtDbUser, username->text());
1400 saveXtConfig();
1401 emit saved();
1404 void XtConnectPage::saveXtConfig()
1406 QSettings s(QSettings::UserScope, QString::fromAscii("xTuple.com"), QString::fromAscii("xTuple"));
1408 // Copy the IMAP settings
1409 Q_ASSERT(imap);
1410 imap->save(s);
1412 // XtConnect-specific stuff
1413 s.setValue(Common::SettingsNames::xtConnectCacheDirectory, cacheDir->text());
1414 QStringList keys = QStringList() <<
1415 Common::SettingsNames::xtSyncMailboxList <<
1416 Common::SettingsNames::xtDbHost <<
1417 Common::SettingsNames::xtDbPort <<
1418 Common::SettingsNames::xtDbDbName <<
1419 Common::SettingsNames::xtDbUser <<
1420 Common::SettingsNames::imapSslPemPubKey;
1421 Q_FOREACH(const QString &key, keys) {
1422 s.setValue(key, QSettings().value(key));
1426 void XtConnectPage::runXtConnect()
1428 // First of all, let's save the XTuple-specific configuration to save useless debugging
1429 saveXtConfig();
1431 QString path = QCoreApplication::applicationFilePath();
1432 QStringList args;
1434 #ifdef Q_OS_WIN
1435 path = path.replace(
1436 QLatin1String("Gui/debug/trojita"),
1437 QLatin1String("XtConnect/debug/xtconnect-trojita")).replace(
1438 QLatin1String("Gui/release/trojita"),
1439 QLatin1String("XtConnect/release/xtconnect-trojita"));
1440 QString cmd = path;
1441 #else
1442 path = path.replace(QLatin1String("src/Gui/trojita"),
1443 QLatin1String("src/XtConnect/xtconnect-trojita"));
1444 args << QLatin1String("-e") << path;
1445 QString cmd = QLatin1String("xterm");
1446 #endif
1448 if (! hostName->text().isEmpty())
1449 args << QLatin1String("-h") << hostName->text();
1451 if (port->value() != 5432)
1452 args << QLatin1String("-p") << QString::number(port->value());
1454 if (! dbName->text().isEmpty())
1455 args << QLatin1String("-d") << dbName->text();
1457 if (! username->text().isEmpty())
1458 args << QLatin1String("-U") << username->text();
1460 QString password = QInputDialog::getText(this, tr("Database Connection"), tr("Password"), QLineEdit::Password);
1461 args << QLatin1String("-w") << password;
1463 if (debugLog->isChecked())
1464 args << QLatin1String("--log") << cacheDir->text() + QLatin1String("/xt-trojita-log");
1466 QProcess::startDetached(cmd, args);
1469 void XtConnectPage::showEvent(QShowEvent *event)
1471 if (imap) {
1472 imapPasswordWarning->setVisible(! imap->hasPassword());
1474 QWidget::showEvent(event);
1477 bool ImapPage::hasPassword() const
1479 return ! imapPass->text().isEmpty();
1482 #endif