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/>.
26 #include <QColorDialog>
28 #include <QDataWidgetMapper>
29 #include <QDialogButtonBox>
32 #include <QFormLayout>
34 #include <QInputDialog>
36 #include <QListWidget>
37 #include <QMessageBox>
39 #include <QPushButton>
40 #include <QRadioButton>
42 #include <QStackedWidget>
43 #include <QStandardItemModel>
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"
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"
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. */
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);
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
),
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"));
97 addPage(xtConnect
= new XtConnectPage(this, *m_settings
, imap
), tr("&xTuple"));
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)
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());
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());
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
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
);
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
);
200 settingsFile
.setPermissions(QFile::ReadUser
| QFile::WriteUser
);
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) {
211 QStringList passwordFailures
;
212 Q_FOREACH(ConfigurationWidgetInterface
*page
, pages
) {
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);
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
)
239 m_senderIdentities
->loadFromSettings(*m_settings
);
240 m_favoriteTags
->loadFromSettings(*m_settings
);
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
);
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
);
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
);
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"));
321 void FavoriteTagsPage::editButtonClicked()
323 EditFavoriteTag
*dialog
= new EditFavoriteTag(this, m_favoriteTagsModel
, tagTableView
->currentIndex());
324 dialog
->setWindowTitle(tr("Edit Tag"));
328 void FavoriteTagsPage::deleteButtonClicked()
330 Q_ASSERT(tagTableView
->currentIndex().isValid());
331 m_favoriteTagsModel
->removeTagAt(tagTableView
->currentIndex().row());
335 void FavoriteTagsPage::save(QSettings
&s
)
337 m_favoriteTagsModel
->saveToSettings(s
);
342 QWidget
*FavoriteTagsPage::asWidget()
347 bool FavoriteTagsPage::checkValidity() const
352 bool FavoriteTagsPage::passwordFailures(QString
&message
) const
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
;
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
)
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
);
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
);
499 void GeneralPage::moveIdentityDown()
501 int from
= identityTabelView
->currentIndex().row();
502 int to
= identityTabelView
->currentIndex().row() + 1;
504 m_identitiesModel
->moveIdentity(from
, to
);
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"));
519 void GeneralPage::editButtonClicked()
521 EditIdentity
*dialog
= new EditIdentity(this, m_identitiesModel
, identityTabelView
->currentIndex());
522 dialog
->setWindowTitle(tr("Edit Identity"));
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());
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
);
571 QWidget
*GeneralPage::asWidget()
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);
586 bool GeneralPage::passwordFailures(QString
&message
) const
592 EditIdentity::EditIdentity(QWidget
*parent
, Composer::SenderIdentitiesModel
*identitiesModel
, const QModelIndex
¤tIndex
):
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
);
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
¤tIndex
):
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
);
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());
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
);
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
);
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
);
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
;
797 switch (method
->currentIndex()) {
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
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()) {
827 if (imapPort
->text().isEmpty() || imapPort
->text() == QString::number(Common::PORT_IMAPS
))
828 imapPort
->setText(QString::number(Common::PORT_IMAP
));
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."));
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()) {
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);
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());
883 if (processPath
->text().isEmpty()) {
884 s
.remove(SettingsNames::imapMethodKey
);
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());
906 QWidget
*ImapPage::asWidget()
911 bool ImapPage::checkValidity() const
913 switch (method
->currentIndex()) {
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")))
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?"))) {
930 void ImapPage::maybeShowPortWarning()
932 if (method
->currentIndex() == PROCESS
) {
933 portWarning
->setVisible(false);
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
));
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
));
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()) {
955 message
= m_pwWatcher
->progressMessage();
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);
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);
984 watchInbox
->setChecked(true);
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
);
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
);
1018 s
.setValue(SettingsNames::watchedFoldersKey
, SettingsNames::watchOnlyInbox
);
1024 QWidget
*CachePage::asWidget()
1029 bool CachePage::checkValidity() const
1031 // Nothing really special for this class
1035 bool CachePage::passwordFailures(QString
&message
) const
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
);
1094 void OutgoingPage::slotSetPassword()
1096 smtpPass
->setText(m_pwWatcher
->password());
1099 void OutgoingPage::slotSetSubmissionMethod()
1101 switch (method
->currentIndex()) {
1103 m_smtpAccountSettings
->setSubmissionMethod(MSA::Account::Method::SENDMAIL
);
1106 m_smtpAccountSettings
->setSubmissionMethod(MSA::Account::Method::IMAP_SENDMAIL
);
1109 switch (encryption
->currentIndex()) {
1111 m_smtpAccountSettings
->setSubmissionMethod(MSA::Account::Method::SMTP
);
1114 m_smtpAccountSettings
->setSubmissionMethod(MSA::Account::Method::SMTP_STARTTLS
);
1117 m_smtpAccountSettings
->setSubmissionMethod(MSA::Account::Method::SSMTP
);
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
;
1138 switch (m_smtpAccountSettings
->submissionMethod()) {
1139 case MSA::Account::Method::SMTP
:
1140 method
->setCurrentIndex(NETWORK
);
1141 encryption
->setCurrentIndex(SMTP
);
1143 case MSA::Account::Method::SMTP_STARTTLS
:
1144 method
->setCurrentIndex(NETWORK
);
1145 encryption
->setCurrentIndex(SMTP_STARTTLS
);
1147 case MSA::Account::Method::SSMTP
:
1148 method
->setCurrentIndex(NETWORK
);
1149 encryption
->setCurrentIndex(SSMTP
);
1151 case MSA::Account::Method::SENDMAIL
:
1152 method
->setCurrentIndex(SENDMAIL
);
1153 encryption
->setVisible(false);
1154 encryptionLabel
->setVisible(false);
1156 case MSA::Account::Method::IMAP_SENDMAIL
:
1157 method
->setCurrentIndex(IMAP_SENDMAIL
);
1158 encryption
->setVisible(false);
1159 encryptionLabel
->setVisible(false);
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."));
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());
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);
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());
1279 void OutgoingPage::showPortWarning(const QString
&warning
)
1281 if (!warning
.isEmpty()) {
1282 portWarningLabel
->setVisible(true);
1283 portWarningLabel
->setText(warning
);
1285 portWarningLabel
->setVisible(false);
1290 QWidget
*OutgoingPage::asWidget()
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")))
1303 if (smtpAuth
->isChecked() && !smtpAuthReuseImapCreds
->isChecked() && checkProblemWithEmptyTextField(smtpUser
, tr("The SMTP username is missing here")))
1306 case MSA::Account::Method::SENDMAIL
:
1307 if (checkProblemWithEmptyTextField(sendmail
, tr("The SMTP server hostname is missing here")))
1310 case MSA::Account::Method::IMAP_SENDMAIL
:
1314 if (saveToImap
->isChecked() && checkProblemWithEmptyTextField(saveFolderName
, tr("Please specify the folder name here")))
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()) {
1326 message
= m_pwWatcher
->progressMessage();
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());
1404 void XtConnectPage::saveXtConfig()
1406 QSettings
s(QSettings::UserScope
, QString::fromAscii("xTuple.com"), QString::fromAscii("xTuple"));
1408 // Copy the IMAP settings
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
1431 QString path
= QCoreApplication::applicationFilePath();
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"));
1442 path
= path
.replace(QLatin1String("src/Gui/trojita"),
1443 QLatin1String("src/XtConnect/xtconnect-trojita"));
1444 args
<< QLatin1String("-e") << path
;
1445 QString cmd
= QLatin1String("xterm");
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
)
1472 imapPasswordWarning
->setVisible(! imap
->hasPassword());
1474 QWidget::showEvent(event
);
1477 bool ImapPage::hasPassword() const
1479 return ! imapPass
->text().isEmpty();