File indexing completed on 2024-11-24 04:53:04
0001 /* Copyright (C) 2006 - 2016 Jan Kundrát <jkt@kde.org> 0002 Copyright (C) 2014 Luke Dashjr <luke+trojita@dashjr.org> 0003 Copyright (C) 2012 Mohammed Nafees <nafees.technocool@gmail.com> 0004 Copyright (C) 2013 Pali Rohár <pali.rohar@gmail.com> 0005 0006 This file is part of the Trojita Qt IMAP e-mail client, 0007 http://trojita.flaska.net/ 0008 0009 This program is free software; you can redistribute it and/or 0010 modify it under the terms of the GNU General Public License as 0011 published by the Free Software Foundation; either version 2 of 0012 the License or (at your option) version 3 or any later version 0013 accepted by the membership of KDE e.V. (or its successor approved 0014 by the membership of KDE e.V.), which shall act as a proxy 0015 defined in Section 14 of version 3 of the license. 0016 0017 This program is distributed in the hope that it will be useful, 0018 but WITHOUT ANY WARRANTY; without even the implied warranty of 0019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0020 GNU General Public License for more details. 0021 0022 You should have received a copy of the GNU General Public License 0023 along with this program. If not, see <http://www.gnu.org/licenses/>. 0024 */ 0025 #include <QCheckBox> 0026 #include <QColorDialog> 0027 #include <QComboBox> 0028 #include <QDataWidgetMapper> 0029 #include <QDialogButtonBox> 0030 #include <QDebug> 0031 #include <QDir> 0032 #include <QFormLayout> 0033 #include <QGroupBox> 0034 #include <QInputDialog> 0035 #include <QLineEdit> 0036 #include <QListWidget> 0037 #include <QMessageBox> 0038 #include <QProcess> 0039 #include <QPushButton> 0040 #include <QRadioButton> 0041 #include <QSpinBox> 0042 #include <QStackedWidget> 0043 #include <QStandardItemModel> 0044 #include <QTabWidget> 0045 #include <QToolTip> 0046 #include <QVBoxLayout> 0047 #include "SettingsDialog.h" 0048 #include "ColoredItemDelegate.h" 0049 #include "Common/InvokeMethod.h" 0050 #include "Common/PortNumbers.h" 0051 #include "Common/SettingsNames.h" 0052 #include "Gui/Util.h" 0053 #include "Gui/Window.h" 0054 #include "Imap/Model/ImapAccess.h" 0055 #include "MSA/Account.h" 0056 #include "Plugins/AddressbookPlugin.h" 0057 #include "Plugins/PasswordPlugin.h" 0058 #include "Plugins/PluginManager.h" 0059 #include "UiUtils/IconLoader.h" 0060 #include "UiUtils/PasswordWatcher.h" 0061 #include "ShortcutHandler/ShortcutHandler.h" 0062 0063 namespace Gui 0064 { 0065 0066 QString SettingsDialog::warningStyleSheet = Util::cssWarningBorder() + QStringLiteral("font-weight: bold;"); 0067 0068 /** @short Check a text field for being non empty. If it's empty, show an error to the user. */ 0069 template<typename T> 0070 bool checkProblemWithEmptyTextField(T *field, const QString &message) 0071 { 0072 if (field->text().isEmpty()) { 0073 QToolTip::showText(field->mapToGlobal(QPoint(10, field->height() / 2)), message, 0); 0074 return true; 0075 } else { 0076 return false; 0077 } 0078 } 0079 0080 SettingsDialog::SettingsDialog(MainWindow *parent, Composer::SenderIdentitiesModel *identitiesModel, 0081 Imap::Mailbox::FavoriteTagsModel *favoriteTagsModel, QSettings *settings): 0082 QDialog(parent), mainWindow(parent), m_senderIdentities(identitiesModel), m_favoriteTags(favoriteTagsModel), 0083 m_settings(settings) 0084 { 0085 setWindowTitle(tr("Settings")); 0086 0087 QVBoxLayout *layout = new QVBoxLayout(this); 0088 stack = new QTabWidget(this); 0089 layout->addWidget(stack); 0090 stack->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 0091 0092 addPage(new GeneralPage(this, *m_settings, m_senderIdentities), tr("&General")); 0093 addPage(new ImapPage(this, *m_settings), tr("I&MAP")); 0094 addPage(new CachePage(this, *m_settings), tr("&Offline")); 0095 addPage(new OutgoingPage(this, *m_settings), tr("&SMTP")); 0096 addPage(new FavoriteTagsPage(this, *m_settings, m_favoriteTags), tr("Favorite &tags")); 0097 0098 buttons = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel, Qt::Horizontal, this); 0099 connect(buttons, &QDialogButtonBox::accepted, this, &SettingsDialog::accept); 0100 connect(buttons, &QDialogButtonBox::rejected, this, &SettingsDialog::reject); 0101 layout->addWidget(buttons); 0102 0103 EMIT_LATER_NOARG(this, reloadPasswordsRequested); 0104 } 0105 0106 void SettingsDialog::setOriginalPlugins(const QString &passwordPlugin, const QString &addressBookPlugin, 0107 const QString &spellcheckerPlugin) 0108 { 0109 m_originalPasswordPlugin = passwordPlugin; 0110 m_originalAddressbookPlugin = addressBookPlugin; 0111 m_originalSpellcheckerPlugin = spellcheckerPlugin; 0112 } 0113 0114 void SettingsDialog::adjustSizeToScrollAreas() 0115 { 0116 QScrollArea *area = qobject_cast<QScrollArea*>(sender()); 0117 Q_ASSERT(area && area->widget()); 0118 0119 // task #A: figure the "minimum" size for the tabwidget 0120 0121 // #A.1: search scrollareas and align their size to their content 0122 // update size of the widget in the tabbed scrollarea 0123 area->widget()->adjustSize(); 0124 0125 // figure the size demand of this scroll area (content + margins) 0126 const auto margins = area->contentsMargins(); 0127 QSize minSize(area->widget()->size() + QSize(margins.left() + margins.right(), margins.top() + margins.bottom())); 0128 0129 // TODO: clamp this to 640x480 or QDesktopWidget::availableGeometry() dependent? 0130 0131 // do not shrink (prevent nasty size jumps for no reason) 0132 minSize.setWidth(qMax(area->width(), minSize.width())); 0133 minSize.setHeight(qMax(area->height(), minSize.height())); 0134 0135 // task #B: find the QStackedWidget inside the QTabWidget to determine its margins 0136 Q_FOREACH(const QObject *o, stack->children()) { 0137 if (const QStackedWidget *actualStack = qobject_cast<const QStackedWidget*>(o)) { 0138 minSize.setWidth(minSize.width() + stack->width() - actualStack->width()); 0139 minSize.setHeight(minSize.height() + stack->height() - actualStack->height()); 0140 break; 0141 } 0142 } 0143 0144 // task #C: convince the dialog to the new size 0145 // #C.1: arrest the tabwidget 0146 stack->setMinimumSize(minSize); 0147 // #C.2: force a relayout of the dialog (do NOT use "adjustSize", which may still shrink) 0148 layout()->activate(); 0149 // #C.3: release the tabwidget minimum size 0150 stack->setMinimumSize(QSize(0, 0)); 0151 } 0152 0153 Plugins::PluginManager *SettingsDialog::pluginManager() 0154 { 0155 return mainWindow->pluginManager(); 0156 } 0157 0158 Imap::ImapAccess *SettingsDialog::imapAccess() 0159 { 0160 return mainWindow->imapAccess(); 0161 } 0162 0163 void SettingsDialog::accept() 0164 { 0165 m_saveSignalCount = 0; 0166 0167 Q_FOREACH(ConfigurationWidgetInterface *page, pages) { 0168 if (!page->checkValidity()) { 0169 stack->setCurrentWidget(page->asWidget()); 0170 return; 0171 } 0172 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 0173 ++m_saveSignalCount; 0174 } 0175 0176 #ifndef Q_OS_WIN 0177 // Try to wour around QSettings' inability to set umask for its file access. We don't want to set umask globally. 0178 QFile settingsFile(m_settings->fileName()); 0179 settingsFile.setPermissions(QFile::ReadUser | QFile::WriteUser); 0180 #endif 0181 0182 buttons->setEnabled(false); 0183 Q_FOREACH(ConfigurationWidgetInterface *page, pages) { 0184 page->asWidget()->setEnabled(false); 0185 } 0186 0187 Q_FOREACH(ConfigurationWidgetInterface *page, pages) { 0188 page->save(*m_settings); 0189 } 0190 0191 m_settings->sync(); 0192 #ifndef Q_OS_WIN 0193 settingsFile.setPermissions(QFile::ReadUser | QFile::WriteUser); 0194 #endif 0195 } 0196 0197 void SettingsDialog::slotAccept() 0198 { 0199 disconnect(sender(), SIGNAL(saved()), this, SLOT(slotAccept())); // new-signal-slot: we're abusing the type system a bit here, cannot use the new syntax 0200 if (--m_saveSignalCount > 0) { 0201 return; 0202 } 0203 0204 QStringList passwordFailures; 0205 Q_FOREACH(ConfigurationWidgetInterface *page, pages) { 0206 QString message; 0207 if (page->passwordFailures(message)) { 0208 passwordFailures << message; 0209 } 0210 } 0211 if (!passwordFailures.isEmpty()) { 0212 Gui::Util::messageBoxWarning(this, tr("Saving passwords failed"), 0213 tr("<p>Couldn't save passwords. These were the error messages:</p>\n<p>%1</p>") 0214 .arg(passwordFailures.join(QStringLiteral("<br/>")))); 0215 } 0216 0217 buttons->setEnabled(true); 0218 QDialog::accept(); 0219 } 0220 0221 void SettingsDialog::reject() 0222 { 0223 // The changes were performed on the live data, so we have to make sure they are discarded when user cancels 0224 #define HANDLE_PLUGIN(LOWERCASE, UPPERCASE) \ 0225 if (!m_original##UPPERCASE##Plugin.isEmpty() && pluginManager()->LOWERCASE##Plugin() != m_original##UPPERCASE##Plugin) { \ 0226 pluginManager()->set##UPPERCASE##Plugin(m_original##UPPERCASE##Plugin); \ 0227 } 0228 HANDLE_PLUGIN(addressbook, Addressbook) 0229 HANDLE_PLUGIN(password, Password) 0230 HANDLE_PLUGIN(spellchecker, Spellchecker) 0231 #undef HANDLE_PLUGIN 0232 m_senderIdentities->loadFromSettings(*m_settings); 0233 m_favoriteTags->loadFromSettings(*m_settings); 0234 QDialog::reject(); 0235 } 0236 0237 void SettingsDialog::addPage(ConfigurationWidgetInterface *page, const QString &title) 0238 { 0239 stack->addTab(page->asWidget(), title); 0240 connect(page->asWidget(), SIGNAL(widgetsUpdated()), SLOT(adjustSizeToScrollAreas())); // new-signal-slot: we're abusing the type system a bit here, cannot use the new syntax 0241 QMetaObject::invokeMethod(page->asWidget(), "updateWidgets", Qt::QueuedConnection); 0242 pages << page; 0243 } 0244 0245 FavoriteTagsPage::FavoriteTagsPage(SettingsDialog *parent, QSettings &s, Imap::Mailbox::FavoriteTagsModel *favoriteTagsModel): 0246 QScrollArea(parent), Ui_FavoriteTagsPage(), m_favoriteTagsModel(favoriteTagsModel), m_parent(parent) 0247 { 0248 Ui_FavoriteTagsPage::setupUi(this); 0249 Q_ASSERT(m_favoriteTagsModel); 0250 moveUpButton->setIcon(UiUtils::loadIcon(QStringLiteral("go-up"))); 0251 moveDownButton->setIcon(UiUtils::loadIcon(QStringLiteral("go-down"))); 0252 tagTableView->setModel(m_favoriteTagsModel); 0253 tagTableView->setItemDelegate(new ColoredItemDelegate(this)); 0254 tagTableView->setSelectionBehavior(QAbstractItemView::SelectRows); 0255 tagTableView->setSelectionMode(QAbstractItemView::SingleSelection); 0256 tagTableView->setGridStyle(Qt::NoPen); 0257 tagTableView->resizeRowsToContents(); 0258 tagTableView->horizontalHeader()->setStretchLastSection(true); 0259 // show tag name in color instead 0260 tagTableView->hideColumn(Imap::Mailbox::FavoriteTagsModel::COLUMN_COLOR); 0261 0262 connect(tagTableView, &QAbstractItemView::clicked, this, &FavoriteTagsPage::updateWidgets); 0263 connect(tagTableView, &QAbstractItemView::doubleClicked, this, &FavoriteTagsPage::editButtonClicked); 0264 connect(m_favoriteTagsModel, &QAbstractItemModel::modelReset, this, &FavoriteTagsPage::updateWidgets); 0265 connect(m_favoriteTagsModel, &QAbstractItemModel::rowsInserted, this, &FavoriteTagsPage::updateWidgets); 0266 connect(m_favoriteTagsModel, &QAbstractItemModel::rowsRemoved, this, &FavoriteTagsPage::updateWidgets); 0267 connect(m_favoriteTagsModel, &QAbstractItemModel::dataChanged, this, &FavoriteTagsPage::updateWidgets); 0268 connect(moveUpButton, &QAbstractButton::clicked, this, [this](){ FavoriteTagsPage::moveTagBy(-1); }); 0269 connect(moveDownButton, &QAbstractButton::clicked, this, [this](){ FavoriteTagsPage::moveTagBy(1); }); 0270 connect(addButton, &QAbstractButton::clicked, this, &FavoriteTagsPage::addButtonClicked); 0271 connect(editButton, &QAbstractButton::clicked, this, &FavoriteTagsPage::editButtonClicked); 0272 connect(deleteButton, &QAbstractButton::clicked, this, &FavoriteTagsPage::deleteButtonClicked); 0273 0274 updateWidgets(); 0275 } 0276 0277 void FavoriteTagsPage::updateWidgets() 0278 { 0279 bool enabled = tagTableView->currentIndex().isValid(); 0280 deleteButton->setEnabled(enabled); 0281 editButton->setEnabled(enabled); 0282 bool upEnabled = m_favoriteTagsModel->rowCount() > 0 && tagTableView->currentIndex().row() > 0; 0283 bool downEnabled = m_favoriteTagsModel->rowCount() > 0 && tagTableView->currentIndex().isValid() && 0284 tagTableView->currentIndex().row() < m_favoriteTagsModel->rowCount() - 1; 0285 moveUpButton->setEnabled(upEnabled); 0286 moveDownButton->setEnabled(downEnabled); 0287 0288 tagTableView->resizeColumnToContents(Imap::Mailbox::FavoriteTagsModel::COLUMN_INDEX); 0289 tagTableView->resizeColumnToContents(Imap::Mailbox::FavoriteTagsModel::COLUMN_NAME); 0290 0291 emit widgetsUpdated(); 0292 } 0293 0294 void FavoriteTagsPage::moveTagBy(const int offset) 0295 { 0296 int from = tagTableView->currentIndex().row(); 0297 int to = tagTableView->currentIndex().row() + offset; 0298 0299 m_favoriteTagsModel->moveTag(from, to); 0300 updateWidgets(); 0301 } 0302 0303 void FavoriteTagsPage::addButtonClicked() 0304 { 0305 m_favoriteTagsModel->appendTag(Imap::Mailbox::ItemFavoriteTagItem()); 0306 tagTableView->setCurrentIndex(m_favoriteTagsModel->index(m_favoriteTagsModel->rowCount() - 1, 0)); 0307 EditFavoriteTag *dialog = new EditFavoriteTag(this, m_favoriteTagsModel, tagTableView->currentIndex()); 0308 dialog->setDeleteOnReject(); 0309 dialog->setWindowTitle(tr("Add New Tag")); 0310 dialog->show(); 0311 updateWidgets(); 0312 } 0313 0314 void FavoriteTagsPage::editButtonClicked() 0315 { 0316 EditFavoriteTag *dialog = new EditFavoriteTag(this, m_favoriteTagsModel, tagTableView->currentIndex()); 0317 dialog->setWindowTitle(tr("Edit Tag")); 0318 dialog->show(); 0319 } 0320 0321 void FavoriteTagsPage::deleteButtonClicked() 0322 { 0323 Q_ASSERT(tagTableView->currentIndex().isValid()); 0324 m_favoriteTagsModel->removeTagAt(tagTableView->currentIndex().row()); 0325 updateWidgets(); 0326 } 0327 0328 void FavoriteTagsPage::save(QSettings &s) 0329 { 0330 m_favoriteTagsModel->saveToSettings(s); 0331 0332 emit saved(); 0333 } 0334 0335 QWidget *FavoriteTagsPage::asWidget() 0336 { 0337 return this; 0338 } 0339 0340 bool FavoriteTagsPage::checkValidity() const 0341 { 0342 return true; 0343 } 0344 0345 bool FavoriteTagsPage::passwordFailures(QString &message) const 0346 { 0347 Q_UNUSED(message); 0348 return false; 0349 } 0350 0351 GeneralPage::GeneralPage(SettingsDialog *parent, QSettings &s, Composer::SenderIdentitiesModel *identitiesModel): 0352 QScrollArea(parent), Ui_GeneralPage(), m_identitiesModel(identitiesModel), m_parent(parent) 0353 { 0354 Ui_GeneralPage::setupUi(this); 0355 Q_ASSERT(m_identitiesModel); 0356 editButton->setEnabled(false); 0357 deleteButton->setEnabled(false); 0358 moveUpButton->setIcon(UiUtils::loadIcon(QStringLiteral("go-up"))); 0359 moveDownButton->setIcon(UiUtils::loadIcon(QStringLiteral("go-down"))); 0360 moveUpButton->setEnabled(false); 0361 moveDownButton->setEnabled(false); 0362 identityTabelView->setModel(m_identitiesModel); 0363 identityTabelView->setSelectionBehavior(QAbstractItemView::SelectRows); 0364 identityTabelView->setSelectionMode(QAbstractItemView::SingleSelection); 0365 identityTabelView->setGridStyle(Qt::NoPen); 0366 identityTabelView->hideColumn(Composer::SenderIdentitiesModel::COLUMN_ORGANIZATION); 0367 identityTabelView->setColumnHidden(Composer::SenderIdentitiesModel::COLUMN_SIGNATURE, true); 0368 identityTabelView->resizeColumnToContents(Composer::SenderIdentitiesModel::COLUMN_NAME); 0369 identityTabelView->resizeRowsToContents(); 0370 identityTabelView->horizontalHeader()->setStretchLastSection(true); 0371 0372 Plugins::PluginManager *pluginManager = parent->pluginManager(); 0373 QMap<QString, QString>::const_iterator it; 0374 int i; 0375 0376 #define HANDLE_PLUGIN(LOWERCASE, UPPERCASE, DISABLE, NOTFOUND) \ 0377 const QMap<QString, QString> &LOWERCASE##Plugins = pluginManager->available##UPPERCASE##Plugins(); \ 0378 const QString &LOWERCASE##Plugin = pluginManager->LOWERCASE##Plugin(); \ 0379 int LOWERCASE##Index = -1; \ 0380 \ 0381 for (it = LOWERCASE##Plugins.constBegin(), i = 0; it != LOWERCASE##Plugins.constEnd(); ++it, ++i) { \ 0382 LOWERCASE##Box->addItem(it.value(), it.key()); \ 0383 if (LOWERCASE##Index < 0 && LOWERCASE##Plugin == it.key()) \ 0384 LOWERCASE##Index = i; \ 0385 } \ 0386 \ 0387 LOWERCASE##Box->addItem(DISABLE); \ 0388 \ 0389 if (LOWERCASE##Plugin == QLatin1String("none")) \ 0390 LOWERCASE##Index = LOWERCASE##Box->count()-1; \ 0391 \ 0392 if (LOWERCASE##Index == -1) { \ 0393 if (!LOWERCASE##Plugin.isEmpty()) \ 0394 LOWERCASE##Box->addItem(NOTFOUND.arg(LOWERCASE##Plugin), LOWERCASE##Plugin); \ 0395 LOWERCASE##Index = LOWERCASE##Box->count()-1; \ 0396 } \ 0397 \ 0398 LOWERCASE##Box->setCurrentIndex(LOWERCASE##Index); 0399 0400 QString pluginNotFound = tr("Plugin not found (%1)"); 0401 HANDLE_PLUGIN(addressbook, Addressbook, tr("Disable address book"), pluginNotFound) 0402 HANDLE_PLUGIN(password, Password, tr("Disable passwords"), pluginNotFound) 0403 HANDLE_PLUGIN(spellchecker, Spellchecker, tr("Disable spell checking"), pluginNotFound) 0404 #undef HANDLE_PLUGIN 0405 0406 m_parent->setOriginalPlugins(passwordPlugin, addressbookPlugin, spellcheckerPlugin); 0407 0408 0409 markReadCheckbox->setChecked(s.value(Common::SettingsNames::autoMarkReadEnabled, QVariant(true)).toBool()); 0410 markReadSeconds->setValue(s.value(Common::SettingsNames::autoMarkReadSeconds, QVariant(0)).toUInt()); 0411 connect(markReadCheckbox, &QAbstractButton::toggled, markReadSeconds, &QWidget::setEnabled); 0412 0413 auto mboxDropAction = s.value(Common::SettingsNames::mboxDropAction, QVariant(QStringLiteral("ask"))).toString(); 0414 0415 connect(mboxDropActionCheckbox, &QAbstractButton::toggled, mboxDropActionBox, &QWidget::setEnabled); 0416 if (mboxDropAction != QStringLiteral("ask")) 0417 mboxDropActionCheckbox->setChecked(true); 0418 0419 mboxDropActionBox->addItem(tr("Move"), QStringLiteral("move")); 0420 if (mboxDropAction == QStringLiteral("move")) 0421 mboxDropActionBox->setCurrentIndex(mboxDropActionBox->count() - 1); 0422 mboxDropActionBox->addItem(tr("Copy"), QStringLiteral("copy")); 0423 if (mboxDropAction == QStringLiteral("copy")) 0424 mboxDropActionBox->setCurrentIndex(mboxDropActionBox->count() - 1); 0425 0426 showHomepageCheckbox->setChecked(s.value(Common::SettingsNames::appLoadHomepage, QVariant(true)).toBool()); 0427 showHomepageCheckbox->setToolTip(tr("<p>If enabled, Trojitá will show its homepage upon startup.</p>" 0428 "<p>The remote server will receive the user's IP address and versions of Trojitá, the Qt library, " 0429 "and the underlying operating system. No private information, like account settings " 0430 "or IMAP server details, are collected.</p>")); 0431 0432 guiSystrayCheckbox->setChecked(s.value(Common::SettingsNames::guiShowSystray, QVariant(true)).toBool()); 0433 guiStartMinimizedCheckbox->setChecked(s.value(Common::SettingsNames::guiStartMinimized, QVariant(false)).toBool()); 0434 0435 preferPlaintextCheckbox->setChecked(s.value(Common::SettingsNames::guiPreferPlaintextRendering).toBool()); 0436 revealTrojitaVersions->setChecked(s.value(Common::SettingsNames::interopRevealVersions, QVariant(true)).toBool()); 0437 0438 connect(identityTabelView, &QAbstractItemView::clicked, this, &GeneralPage::updateWidgets); 0439 connect(identityTabelView, &QAbstractItemView::doubleClicked, this, &GeneralPage::editButtonClicked); 0440 connect(m_identitiesModel, &QAbstractItemModel::layoutChanged, this, &GeneralPage::updateWidgets); 0441 connect(m_identitiesModel, &QAbstractItemModel::rowsInserted, this, &GeneralPage::updateWidgets); 0442 connect(m_identitiesModel, &QAbstractItemModel::rowsRemoved, this, &GeneralPage::updateWidgets); 0443 connect(m_identitiesModel, &QAbstractItemModel::dataChanged, this, &GeneralPage::updateWidgets); 0444 connect(moveUpButton, &QAbstractButton::clicked, this, &GeneralPage::moveIdentityUp); 0445 connect(moveDownButton, &QAbstractButton::clicked, this, &GeneralPage::moveIdentityDown); 0446 connect(addButton, &QAbstractButton::clicked, this, &GeneralPage::addButtonClicked); 0447 connect(editButton, &QAbstractButton::clicked, this, &GeneralPage::editButtonClicked); 0448 connect(deleteButton, &QAbstractButton::clicked, this, &GeneralPage::deleteButtonClicked); 0449 connect(passwordBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &GeneralPage::passwordPluginChanged); 0450 0451 connect(this, &GeneralPage::reloadPasswords, m_parent, &SettingsDialog::reloadPasswordsRequested); 0452 0453 updateWidgets(); 0454 } 0455 0456 void GeneralPage::passwordPluginChanged() 0457 { 0458 const QString &passwordPlugin = m_parent->pluginManager()->passwordPlugin(); 0459 const QString &selectedPasswordPlugin = passwordBox->itemData(passwordBox->currentIndex()).toString(); 0460 0461 if (selectedPasswordPlugin != passwordPlugin) { 0462 m_parent->pluginManager()->setPasswordPlugin(selectedPasswordPlugin); 0463 emit reloadPasswords(); 0464 } 0465 } 0466 0467 void GeneralPage::updateWidgets() 0468 { 0469 bool enabled = identityTabelView->currentIndex().isValid(); 0470 deleteButton->setEnabled(enabled); 0471 editButton->setEnabled(enabled); 0472 bool upEnabled = m_identitiesModel->rowCount() > 0 && identityTabelView->currentIndex().row() > 0; 0473 bool downEnabled = m_identitiesModel->rowCount() > 0 && identityTabelView->currentIndex().isValid() && 0474 identityTabelView->currentIndex().row() < m_identitiesModel->rowCount() - 1; 0475 moveUpButton->setEnabled(upEnabled); 0476 moveDownButton->setEnabled(downEnabled); 0477 0478 identityTabelView->resizeColumnToContents(Composer::SenderIdentitiesModel::COLUMN_NAME); 0479 0480 emit widgetsUpdated(); 0481 } 0482 0483 void GeneralPage::moveIdentityUp() 0484 { 0485 int from = identityTabelView->currentIndex().row(); 0486 int to = identityTabelView->currentIndex().row() - 1; 0487 0488 m_identitiesModel->moveIdentity(from, to); 0489 updateWidgets(); 0490 } 0491 0492 void GeneralPage::moveIdentityDown() 0493 { 0494 int from = identityTabelView->currentIndex().row(); 0495 int to = identityTabelView->currentIndex().row() + 1; 0496 0497 m_identitiesModel->moveIdentity(from, to); 0498 updateWidgets(); 0499 } 0500 0501 void GeneralPage::addButtonClicked() 0502 { 0503 m_identitiesModel->appendIdentity(Composer::ItemSenderIdentity()); 0504 identityTabelView->setCurrentIndex(m_identitiesModel->index(m_identitiesModel->rowCount() - 1, 0)); 0505 EditIdentity *dialog = new EditIdentity(this, m_identitiesModel, identityTabelView->currentIndex()); 0506 dialog->setDeleteOnReject(); 0507 dialog->setWindowTitle(tr("Add New Identity")); 0508 dialog->show(); 0509 updateWidgets(); 0510 } 0511 0512 void GeneralPage::editButtonClicked() 0513 { 0514 EditIdentity *dialog = new EditIdentity(this, m_identitiesModel, identityTabelView->currentIndex()); 0515 dialog->setWindowTitle(tr("Edit Identity")); 0516 dialog->show(); 0517 } 0518 0519 void GeneralPage::deleteButtonClicked() 0520 { 0521 Q_ASSERT(identityTabelView->currentIndex().isValid()); 0522 QMessageBox::StandardButton answer = 0523 QMessageBox::question(this, tr("Delete Identity?"), 0524 tr("Are you sure you want to delete identity %1 <%2>?").arg( 0525 m_identitiesModel->index(identityTabelView->currentIndex().row(), 0526 Composer::SenderIdentitiesModel::COLUMN_NAME).data().toString(), 0527 m_identitiesModel->index(identityTabelView->currentIndex().row(), 0528 Composer::SenderIdentitiesModel::COLUMN_EMAIL).data().toString()), 0529 QMessageBox::Yes | QMessageBox::No); 0530 if (answer == QMessageBox::Yes) { 0531 m_identitiesModel->removeIdentityAt(identityTabelView->currentIndex().row()); 0532 updateWidgets(); 0533 } 0534 } 0535 0536 void GeneralPage::save(QSettings &s) 0537 { 0538 m_identitiesModel->saveToSettings(s); 0539 s.setValue(Common::SettingsNames::autoMarkReadEnabled, markReadCheckbox->isChecked()); 0540 s.setValue(Common::SettingsNames::autoMarkReadSeconds, markReadSeconds->value()); 0541 s.setValue(Common::SettingsNames::mboxDropAction, 0542 mboxDropActionCheckbox->isChecked() ? mboxDropActionBox->currentData() : QStringLiteral("ask")); 0543 s.setValue(Common::SettingsNames::appLoadHomepage, showHomepageCheckbox->isChecked()); 0544 s.setValue(Common::SettingsNames::guiPreferPlaintextRendering, preferPlaintextCheckbox->isChecked()); 0545 s.setValue(Common::SettingsNames::guiShowSystray, guiSystrayCheckbox->isChecked()); 0546 s.setValue(Common::SettingsNames::guiStartMinimized, guiStartMinimizedCheckbox->isChecked()); 0547 s.setValue(Common::SettingsNames::interopRevealVersions, revealTrojitaVersions->isChecked()); 0548 0549 #define HANDLE_PLUGIN(LOWERCASE, UPPERCASE) \ 0550 const QString &LOWERCASE##Plugin = m_parent->pluginManager()->LOWERCASE##Plugin(); \ 0551 const QString &selected##UPPERCASE##Plugin = LOWERCASE##Box->itemData(LOWERCASE##Box->currentIndex()).toString(); \ 0552 if (selected##UPPERCASE##Plugin != LOWERCASE##Plugin) { \ 0553 m_parent->pluginManager()->set##UPPERCASE##Plugin(selected##UPPERCASE##Plugin); \ 0554 } 0555 0556 HANDLE_PLUGIN(addressbook, Addressbook); 0557 HANDLE_PLUGIN(password, Password); 0558 HANDLE_PLUGIN(spellchecker, Spellchecker); 0559 #undef HANDLE_PLUGIN 0560 0561 emit saved(); 0562 } 0563 0564 QWidget *GeneralPage::asWidget() 0565 { 0566 return this; 0567 } 0568 0569 bool GeneralPage::checkValidity() const 0570 { 0571 if (m_identitiesModel->rowCount() < 1) { 0572 QToolTip::showText(identityTabelView->mapToGlobal(QPoint(10, identityTabelView->height() / 2)), 0573 tr("Please define some identities here"), 0); 0574 return false; 0575 } 0576 return true; 0577 } 0578 0579 bool GeneralPage::passwordFailures(QString &message) const 0580 { 0581 Q_UNUSED(message); 0582 return false; 0583 } 0584 0585 EditIdentity::EditIdentity(QWidget *parent, Composer::SenderIdentitiesModel *identitiesModel, const QModelIndex ¤tIndex): 0586 QDialog(parent), Ui_EditIdentity(), m_identitiesModel(identitiesModel), m_deleteOnReject(false) 0587 { 0588 Ui_EditIdentity::setupUi(this); 0589 m_mapper = new QDataWidgetMapper(this); 0590 m_mapper->setModel(m_identitiesModel); 0591 m_mapper->addMapping(realNameLineEdit, Composer::SenderIdentitiesModel::COLUMN_NAME); 0592 m_mapper->addMapping(emailLineEdit, Composer::SenderIdentitiesModel::COLUMN_EMAIL); 0593 m_mapper->addMapping(organisationLineEdit, Composer::SenderIdentitiesModel::COLUMN_ORGANIZATION); 0594 m_mapper->addMapping(signaturePlainTextEdit, Composer::SenderIdentitiesModel::COLUMN_SIGNATURE); 0595 m_mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); 0596 m_mapper->setCurrentIndex(currentIndex.row()); 0597 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 0598 connect(realNameLineEdit, &QLineEdit::textChanged, this, &EditIdentity::enableButton); 0599 connect(emailLineEdit, &QLineEdit::textChanged, this, &EditIdentity::enableButton); 0600 connect(organisationLineEdit, &QLineEdit::textChanged, this, &EditIdentity::enableButton); 0601 connect(signaturePlainTextEdit, &QPlainTextEdit::textChanged, this, &EditIdentity::enableButton); 0602 connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this, &QDialog::accept); 0603 connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, this, &QDialog::reject); 0604 connect(this, &QDialog::accepted, m_mapper, &QDataWidgetMapper::submit); 0605 connect(this, &QDialog::rejected, this, &EditIdentity::onReject); 0606 setModal(true); 0607 signaturePlainTextEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0608 } 0609 0610 void EditIdentity::enableButton() 0611 { 0612 buttonBox->button(QDialogButtonBox::Ok)->setEnabled( 0613 !realNameLineEdit->text().isEmpty() && !emailLineEdit->text().isEmpty()); 0614 } 0615 0616 /** @short If enabled, make sure that the current row gets deleted when the dialog is rejected */ 0617 void EditIdentity::setDeleteOnReject(const bool reject) 0618 { 0619 m_deleteOnReject = reject; 0620 } 0621 0622 void EditIdentity::onReject() 0623 { 0624 if (m_deleteOnReject) 0625 m_identitiesModel->removeIdentityAt(m_mapper->currentIndex()); 0626 } 0627 0628 EditFavoriteTag::EditFavoriteTag(QWidget *parent, Imap::Mailbox::FavoriteTagsModel *favoriteTagsModel, const QModelIndex ¤tIndex): 0629 QDialog(parent), Ui_EditFavoriteTag(), m_favoriteTagsModel(favoriteTagsModel), currentIndex(currentIndex), m_deleteOnReject(false) 0630 { 0631 Ui_EditFavoriteTag::setupUi(this); 0632 0633 nameLineEdit->setText(name()); 0634 setColorButtonColor(color()); 0635 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 0636 0637 connect(colorButton, &QAbstractButton::clicked, this, &EditFavoriteTag::colorButtonClick); 0638 connect(nameLineEdit, &QLineEdit::textChanged, this, &EditFavoriteTag::tryEnableButton); 0639 0640 connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this, &QDialog::accept); 0641 connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, this, &QDialog::reject); 0642 connect(this, &QDialog::accepted, this, &EditFavoriteTag::onAccept); 0643 connect(this, &QDialog::rejected, this, &EditFavoriteTag::onReject); 0644 setModal(true); 0645 } 0646 0647 QString EditFavoriteTag::name() 0648 { 0649 return m_favoriteTagsModel->data(m_favoriteTagsModel->index(currentIndex.row(), Imap::Mailbox::FavoriteTagsModel::COLUMN_NAME)).toString(); 0650 } 0651 0652 QString EditFavoriteTag::color() 0653 { 0654 return m_favoriteTagsModel->data(m_favoriteTagsModel->index(currentIndex.row(), Imap::Mailbox::FavoriteTagsModel::COLUMN_COLOR)).toString(); 0655 } 0656 0657 void EditFavoriteTag::setColorButtonColor(const QString color) 0658 { 0659 colorButton->setProperty("colorName", color); 0660 QPalette pal = colorButton->palette(); 0661 pal.setColor(QPalette::Button, QColor(color)); 0662 colorButton->setAutoFillBackground(true); 0663 colorButton->setPalette(pal); 0664 colorButton->setFlat(true); 0665 colorButton->update(); 0666 } 0667 0668 void EditFavoriteTag::colorButtonClick() 0669 { 0670 const QColor color = QColorDialog::getColor(QColor(colorButton->property("colorName").toString()), this, tr("Select tag color")); 0671 if (color.isValid()) { 0672 setColorButtonColor(color.name()); 0673 tryEnableButton(); 0674 } 0675 } 0676 0677 void EditFavoriteTag::tryEnableButton() 0678 { 0679 buttonBox->button(QDialogButtonBox::Ok)->setEnabled( 0680 !nameLineEdit->text().isEmpty() && QColor(colorButton->property("colorName").toString()).isValid() 0681 ); 0682 } 0683 0684 /** @short If enabled, make sure that the current row gets deleted when the dialog is rejected */ 0685 void EditFavoriteTag::setDeleteOnReject(const bool reject) 0686 { 0687 m_deleteOnReject = reject; 0688 } 0689 0690 void EditFavoriteTag::onAccept() 0691 { 0692 m_favoriteTagsModel->setData(m_favoriteTagsModel->index(currentIndex.row(), Imap::Mailbox::FavoriteTagsModel::COLUMN_NAME), 0693 nameLineEdit->text()); 0694 m_favoriteTagsModel->setData(m_favoriteTagsModel->index(currentIndex.row(), Imap::Mailbox::FavoriteTagsModel::COLUMN_COLOR), 0695 colorButton->property("colorName")); 0696 } 0697 0698 void EditFavoriteTag::onReject() 0699 { 0700 if (m_deleteOnReject) 0701 m_favoriteTagsModel->removeTagAt(currentIndex.row()); 0702 } 0703 0704 ImapPage::ImapPage(SettingsDialog *parent, QSettings &s): QScrollArea(parent), Ui_ImapPage(), m_parent(parent) 0705 { 0706 Ui_ImapPage::setupUi(this); 0707 method->insertItem(NETWORK, tr("Network Connection")); 0708 method->insertItem(PROCESS, tr("Local Process")); 0709 0710 encryption->insertItem(NONE, tr("No encryption")); 0711 encryption->insertItem(STARTTLS, tr("Use encryption (STARTTLS)")); 0712 encryption->insertItem(SSL, tr("Force encryption (TLS)")); 0713 using Common::SettingsNames; 0714 int defaultImapPort = Common::PORT_IMAPS; 0715 0716 if (s.value(SettingsNames::imapMethodKey).toString() == SettingsNames::methodTCP) { 0717 method->setCurrentIndex(NETWORK); 0718 0719 if (s.value(SettingsNames::imapStartTlsKey,true).toBool()) 0720 encryption->setCurrentIndex(STARTTLS); 0721 else 0722 encryption->setCurrentIndex(NONE); 0723 0724 defaultImapPort = Common::PORT_IMAP; 0725 } else if (s.value(SettingsNames::imapMethodKey).toString() == SettingsNames::methodSSL) { 0726 method->setCurrentIndex(NETWORK); 0727 encryption->setCurrentIndex(SSL); 0728 } else if (s.value(SettingsNames::imapMethodKey).toString() == SettingsNames::methodProcess) { 0729 method->setCurrentIndex(PROCESS); 0730 } else { 0731 // Default settings -- let's assume SSL and hope that users who just press Cancel will configure when they see 0732 // the network error... 0733 method->setCurrentIndex(NETWORK); 0734 encryption->setCurrentIndex(SSL); 0735 } 0736 0737 imapHost->setText(s.value(SettingsNames::imapHostKey).toString()); 0738 imapPort->setText(s.value(SettingsNames::imapPortKey, QString::number(defaultImapPort)).toString()); 0739 imapPort->setValidator(new QIntValidator(1, 65535, this)); 0740 connect(imapPort, &QLineEdit::textChanged, this, &ImapPage::maybeShowPortWarning); 0741 connect(encryption, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ImapPage::maybeShowPortWarning); 0742 connect(method, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ImapPage::maybeShowPortWarning); 0743 connect(encryption, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ImapPage::changePort); 0744 portWarning->setStyleSheet(SettingsDialog::warningStyleSheet); 0745 connect(imapPass, &QLineEdit::textChanged, this, &ImapPage::updateWidgets); 0746 imapUser->setText(s.value(SettingsNames::imapUserKey).toString()); 0747 processPath->setText(s.value(SettingsNames::imapProcessKey).toString()); 0748 0749 imapCapabilitiesBlacklist->setText(s.value(SettingsNames::imapBlacklistedCapabilities).toStringList().join(QStringLiteral(" "))); 0750 imapUseSystemProxy->setChecked(s.value(SettingsNames::imapUseSystemProxy, true).toBool()); 0751 imapNeedsNetwork->setChecked(s.value(SettingsNames::imapNeedsNetwork, true).toBool()); 0752 imapIdleRenewal->setValue(s.value(SettingsNames::imapIdleRenewal, QVariant(29)).toInt()); 0753 imapNumberRefreshInterval->setValue(m_parent->imapAccess()->numberRefreshInterval()); 0754 accountIcon->setText(s.value(SettingsNames::imapAccountIcon).toString()); 0755 archiveFolderName->setText(s.value(SettingsNames::imapArchiveFolderName).toString().isEmpty() ? 0756 SettingsNames::imapDefaultArchiveFolderName : s.value(SettingsNames::imapArchiveFolderName).toString()); 0757 0758 m_imapPort = s.value(SettingsNames::imapPortKey, QString::number(defaultImapPort)).value<quint16>(); 0759 0760 connect(method, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ImapPage::updateWidgets); 0761 0762 // FIXME: use another account-id 0763 m_pwWatcher = m_parent->imapAccess()->passwordWatcher(); 0764 connect(m_pwWatcher, &UiUtils::PasswordWatcher::stateChanged, this, &ImapPage::updateWidgets); 0765 connect(m_pwWatcher, &UiUtils::PasswordWatcher::savingFailed, this, &ImapPage::saved); 0766 connect(m_pwWatcher, &UiUtils::PasswordWatcher::savingDone, this, &ImapPage::saved); 0767 connect(m_pwWatcher, &UiUtils::PasswordWatcher::readingDone, this, &ImapPage::slotSetPassword); 0768 connect(m_parent, &SettingsDialog::reloadPasswordsRequested, imapPass, &QLineEdit::clear); 0769 connect(m_parent, &SettingsDialog::reloadPasswordsRequested, m_pwWatcher, &UiUtils::PasswordWatcher::reloadPassword); 0770 0771 updateWidgets(); 0772 maybeShowPortWarning(); 0773 } 0774 0775 void ImapPage::slotSetPassword() 0776 { 0777 imapPass->setText(m_pwWatcher->password()); 0778 } 0779 0780 void ImapPage::changePort() 0781 { 0782 imapPort->setText(QString::number(encryption->currentIndex() == SSL ? Common::PORT_IMAPS : Common::PORT_IMAP)); 0783 } 0784 0785 void ImapPage::updateWidgets() 0786 { 0787 QFormLayout *lay = formLayout; 0788 Q_ASSERT(lay); 0789 0790 switch (method->currentIndex()) { 0791 case NETWORK: 0792 imapHost->setVisible(true); 0793 imapPort->setVisible(true); 0794 encryption->setVisible(true); 0795 lay->labelForField(imapHost)->setVisible(true); 0796 lay->labelForField(imapPort)->setVisible(true); 0797 lay->labelForField(encryption)->setVisible(true); 0798 processPath->setVisible(false); 0799 lay->labelForField(processPath)->setVisible(false); 0800 imapUseSystemProxy->setVisible(true); 0801 lay->labelForField(imapUseSystemProxy)->setVisible(true); 0802 // the "needs network" can very well apply to accounts using "local process" via SSH, so it is not disabled here 0803 break; 0804 default: 0805 imapHost->setVisible(false); 0806 imapPort->setVisible(false); 0807 encryption->setVisible(false); 0808 lay->labelForField(imapHost)->setVisible(false); 0809 lay->labelForField(imapPort)->setVisible(false); 0810 lay->labelForField(encryption)->setVisible(false); 0811 processPath->setVisible(true); 0812 lay->labelForField(processPath)->setVisible(true); 0813 imapUseSystemProxy->setVisible(false); 0814 lay->labelForField(imapUseSystemProxy)->setVisible(false); 0815 } 0816 0817 switch (encryption->currentIndex()) { 0818 case NONE: 0819 case STARTTLS: 0820 if (imapPort->text().isEmpty() || imapPort->text() == QString::number(Common::PORT_IMAPS)) 0821 imapPort->setText(QString::number(Common::PORT_IMAP)); 0822 break; 0823 default: 0824 if (imapPort->text().isEmpty() || imapPort->text() == QString::number(Common::PORT_IMAP)) 0825 imapPort->setText(QString::number(Common::PORT_IMAPS)); 0826 } 0827 0828 if (!m_pwWatcher->isPluginAvailable()) 0829 imapPass->setText(QString()); 0830 0831 passwordWarning->setVisible(!imapPass->text().isEmpty()); 0832 if (m_pwWatcher->isStorageEncrypted()) { 0833 passwordWarning->setStyleSheet(QString()); 0834 passwordWarning->setText(tr("This password will be saved in encrypted storage. " 0835 "If you do not enter password here, Trojitá will prompt for one when needed.")); 0836 } else { 0837 passwordWarning->setStyleSheet(SettingsDialog::warningStyleSheet); 0838 passwordWarning->setText(tr("This password will be saved in clear text. " 0839 "If you do not enter password here, Trojitá will prompt for one when needed.")); 0840 } 0841 0842 passwordPluginStatus->setVisible(!m_pwWatcher->isPluginAvailable() || m_pwWatcher->isWaitingForPlugin() || !m_pwWatcher->didReadOk() || !m_pwWatcher->didWriteOk()); 0843 passwordPluginStatus->setText(m_pwWatcher->progressMessage()); 0844 0845 imapPass->setEnabled(m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin()); 0846 imapPassLabel->setEnabled(m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin()); 0847 0848 emit widgetsUpdated(); 0849 } 0850 0851 void ImapPage::save(QSettings &s) 0852 { 0853 using Common::SettingsNames; 0854 if (s.value(SettingsNames::imapHostKey) != imapHost->text()) { 0855 s.remove(Common::SettingsNames::imapSslPemPubKey); 0856 } 0857 switch (method->currentIndex()) { 0858 case NETWORK: 0859 if (imapHost->text().isEmpty()) { 0860 s.remove(SettingsNames::imapMethodKey); 0861 } else if (encryption->currentIndex() == NONE){ 0862 s.setValue(SettingsNames::imapMethodKey, SettingsNames::methodTCP); 0863 s.setValue(SettingsNames::imapStartTlsKey, false); 0864 } else if (encryption->currentIndex() == STARTTLS){ 0865 s.setValue(SettingsNames::imapMethodKey, SettingsNames::methodTCP); 0866 s.setValue(SettingsNames::imapStartTlsKey, true); 0867 } else { 0868 s.setValue(SettingsNames::imapMethodKey, SettingsNames::methodSSL); 0869 s.setValue(SettingsNames::imapStartTlsKey, true); 0870 } 0871 s.setValue(SettingsNames::imapHostKey, imapHost->text()); 0872 s.setValue(SettingsNames::imapPortKey, imapPort->text()); 0873 s.setValue(SettingsNames::imapUseSystemProxy, imapUseSystemProxy->isChecked()); 0874 break; 0875 default: 0876 if (processPath->text().isEmpty()) { 0877 s.remove(SettingsNames::imapMethodKey); 0878 } else { 0879 s.setValue(SettingsNames::imapMethodKey, SettingsNames::methodProcess); 0880 } 0881 s.setValue(SettingsNames::imapProcessKey, processPath->text()); 0882 } 0883 s.setValue(SettingsNames::imapUserKey, imapUser->text()); 0884 s.setValue(SettingsNames::imapBlacklistedCapabilities, imapCapabilitiesBlacklist->text().split(QStringLiteral(" "))); 0885 s.setValue(SettingsNames::imapNeedsNetwork, imapNeedsNetwork->isChecked()); 0886 s.setValue(SettingsNames::imapIdleRenewal, imapIdleRenewal->value()); 0887 m_parent->imapAccess()->setNumberRefreshInterval(imapNumberRefreshInterval->value()); 0888 0889 s.setValue(SettingsNames::imapAccountIcon, accountIcon->text().isEmpty() ? QVariant() : QVariant(accountIcon->text())); 0890 s.setValue(SettingsNames::imapArchiveFolderName, archiveFolderName->text()); 0891 0892 if (m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin()) { 0893 m_pwWatcher->setPassword(imapPass->text()); 0894 } else { 0895 emit saved(); 0896 } 0897 } 0898 0899 QWidget *ImapPage::asWidget() 0900 { 0901 return this; 0902 } 0903 0904 bool ImapPage::checkValidity() const 0905 { 0906 switch (method->currentIndex()) { 0907 case NETWORK: 0908 // We don't require the username, and that's on purpose. Some servers *could* possibly support PREAUTH :) 0909 if (checkProblemWithEmptyTextField(imapHost, tr("The IMAP server hostname is missing here"))) 0910 return false; 0911 break; 0912 default: 0913 // PREAUTH must definitely be supported here -- think imap-over-ssh-with-ssh-keys etc. 0914 if (checkProblemWithEmptyTextField(processPath, 0915 tr("The command line to the IMAP server is missing here. Perhaps you need to use SSL or TCP?"))) { 0916 return false; 0917 } 0918 break; 0919 } 0920 return true; 0921 } 0922 0923 void ImapPage::maybeShowPortWarning() 0924 { 0925 if (method->currentIndex() == PROCESS) { 0926 portWarning->setVisible(false); 0927 return; 0928 } 0929 0930 if (encryption->currentIndex() == SSL) { 0931 portWarning->setVisible(imapPort->text() != QString::number(Common::PORT_IMAPS)); 0932 portWarning->setText(tr("This port is nonstandard. The default port for IMAP secured over SSL/TLS is %1.").arg(Common::PORT_IMAPS)); 0933 } else { 0934 portWarning->setVisible(imapPort->text() != QString::number(Common::PORT_IMAP)); 0935 if (encryption->currentIndex() == STARTTLS) { 0936 portWarning->setText(tr("This port is nonstandard. The default port for IMAP secured via STARTTLS is %1.").arg(Common::PORT_IMAP)); 0937 } else { 0938 portWarning->setText(tr("This port is nonstandard. The default port for IMAP over cleartext is %1.").arg(Common::PORT_IMAP)); 0939 } 0940 } 0941 } 0942 0943 bool ImapPage::passwordFailures(QString &message) const 0944 { 0945 if (!m_pwWatcher->isPluginAvailable() || m_pwWatcher->isWaitingForPlugin() || m_pwWatcher->didWriteOk()) { 0946 return false; 0947 } else { 0948 message = m_pwWatcher->progressMessage(); 0949 return true; 0950 } 0951 } 0952 0953 0954 CachePage::CachePage(QWidget *parent, QSettings &s): QScrollArea(parent), Ui_CachePage() 0955 { 0956 Ui_CachePage::setupUi(this); 0957 0958 using Common::SettingsNames; 0959 0960 QString val = s.value(SettingsNames::cacheOfflineKey).toString(); 0961 if (val == SettingsNames::cacheOfflineAll) { 0962 offlineEverything->setChecked(true); 0963 } else if (val == SettingsNames::cacheOfflineNone) { 0964 offlineNope->setChecked(true); 0965 } else { 0966 offlineXDays->setChecked(true); 0967 } 0968 0969 offlineNumberOfDays->setValue(s.value(SettingsNames::cacheOfflineNumberDaysKey, QVariant(30)).toInt()); 0970 0971 val = s.value(SettingsNames::watchedFoldersKey).toString(); 0972 if (val == Common::SettingsNames::watchAll) { 0973 watchAll->setChecked(true); 0974 } else if (val == Common::SettingsNames::watchSubscribed) { 0975 watchSubscribed->setChecked(true); 0976 } else { 0977 watchInbox->setChecked(true); 0978 } 0979 0980 updateWidgets(); 0981 0982 connect(offlineNope, &QAbstractButton::clicked, this, &CachePage::updateWidgets); 0983 connect(offlineXDays, &QAbstractButton::clicked, this, &CachePage::updateWidgets); 0984 connect(offlineEverything, &QAbstractButton::clicked, this, &CachePage::updateWidgets); 0985 } 0986 0987 void CachePage::updateWidgets() 0988 { 0989 offlineNumberOfDays->setEnabled(offlineXDays->isChecked()); 0990 emit widgetsUpdated(); 0991 } 0992 0993 void CachePage::save(QSettings &s) 0994 { 0995 using Common::SettingsNames; 0996 0997 if (offlineEverything->isChecked()) 0998 s.setValue(SettingsNames::cacheOfflineKey, SettingsNames::cacheOfflineAll); 0999 else if (offlineXDays->isChecked()) 1000 s.setValue(SettingsNames::cacheOfflineKey, SettingsNames::cacheOfflineXDays); 1001 else 1002 s.setValue(SettingsNames::cacheOfflineKey, SettingsNames::cacheOfflineNone); 1003 1004 s.setValue(SettingsNames::cacheOfflineNumberDaysKey, offlineNumberOfDays->value()); 1005 1006 if (watchAll->isChecked()) { 1007 s.setValue(SettingsNames::watchedFoldersKey, SettingsNames::watchAll); 1008 } else if (watchSubscribed->isChecked()) { 1009 s.setValue(SettingsNames::watchedFoldersKey, SettingsNames::watchSubscribed); 1010 } else { 1011 s.setValue(SettingsNames::watchedFoldersKey, SettingsNames::watchOnlyInbox); 1012 } 1013 1014 emit saved(); 1015 } 1016 1017 QWidget *CachePage::asWidget() 1018 { 1019 return this; 1020 } 1021 1022 bool CachePage::checkValidity() const 1023 { 1024 // Nothing really special for this class 1025 return true; 1026 } 1027 1028 bool CachePage::passwordFailures(QString &message) const 1029 { 1030 Q_UNUSED(message); 1031 return false; 1032 } 1033 1034 OutgoingPage::OutgoingPage(SettingsDialog *parent, QSettings &s): QScrollArea(parent), Ui_OutgoingPage(), m_parent(parent) 1035 { 1036 using Common::SettingsNames; 1037 Ui_OutgoingPage::setupUi(this); 1038 // FIXME: use another account-id at some point in future 1039 // we are now using the profile to avoid overwriting passwords of 1040 // other profiles in secure storage 1041 QString profileName = QString::fromUtf8(qgetenv("TROJITA_PROFILE")); 1042 m_smtpAccountSettings = new MSA::Account(this, &s, profileName); 1043 1044 portWarningLabel->setStyleSheet(SettingsDialog::warningStyleSheet); 1045 1046 method->insertItem(NETWORK, tr("Network")); 1047 method->insertItem(SENDMAIL, tr("Local sendmail-compatible")); 1048 method->insertItem(IMAP_SENDMAIL, tr("IMAP SENDMAIL Extension"));; 1049 1050 encryption->insertItem(SMTP, tr("No encryption")); 1051 encryption->insertItem(SMTP_STARTTLS, tr("Use encryption (STARTTLS)")); 1052 encryption->insertItem(SSMTP, tr("Force encryption (TLS)")); 1053 encryption->setCurrentIndex(SSMTP); 1054 1055 connect(method, static_cast<void (QComboBox::*)(const int)>(&QComboBox::currentIndexChanged), this, &OutgoingPage::slotSetSubmissionMethod); 1056 connect(encryption, static_cast<void (QComboBox::*)(const int)>(&QComboBox::currentIndexChanged), this, &OutgoingPage::slotSetSubmissionMethod); 1057 1058 connect(m_smtpAccountSettings, &MSA::Account::submissionMethodChanged, this, &OutgoingPage::updateWidgets); 1059 connect(m_smtpAccountSettings, &MSA::Account::saveToImapChanged, this, &OutgoingPage::updateWidgets); 1060 connect(m_smtpAccountSettings, &MSA::Account::authenticateEnabledChanged, this, &OutgoingPage::updateWidgets); 1061 connect(m_smtpAccountSettings, &MSA::Account::reuseImapAuthenticationChanged, this, &OutgoingPage::updateWidgets); 1062 connect(smtpPass, &QLineEdit::textChanged, this, &OutgoingPage::updateWidgets); 1063 connect(smtpHost, &LineEdit::textEditingFinished, m_smtpAccountSettings, &MSA::Account::setServer); 1064 connect(smtpUser, &LineEdit::textEditingFinished, m_smtpAccountSettings, &MSA::Account::setUsername); 1065 connect(smtpPort, &LineEdit::textEditingFinished, this, &OutgoingPage::setPortByText); 1066 connect(m_smtpAccountSettings, &MSA::Account::showPortWarning, this, &OutgoingPage::showPortWarning); 1067 connect(smtpAuth, &QAbstractButton::toggled, m_smtpAccountSettings, &MSA::Account::setAuthenticateEnabled); 1068 connect(smtpAuthReuseImapCreds, &QAbstractButton::toggled, m_smtpAccountSettings, &MSA::Account::setReuseImapAuthentication); 1069 connect(saveToImap, &QAbstractButton::toggled, m_smtpAccountSettings, &MSA::Account::setSaveToImap); 1070 connect(saveFolderName, &LineEdit::textEditingFinished, m_smtpAccountSettings, &MSA::Account::setSentMailboxName); 1071 connect(smtpBurl, &QAbstractButton::toggled, m_smtpAccountSettings, &MSA::Account::setUseBurl); 1072 connect(sendmail, &LineEdit::textEditingFinished, m_smtpAccountSettings, &MSA::Account::setPathToSendmail); 1073 1074 m_pwWatcher = new UiUtils::PasswordWatcher(this, m_parent->pluginManager(), 1075 profileName.isEmpty() ? QStringLiteral("account-0") : profileName, 1076 QStringLiteral("smtp")); 1077 connect(m_pwWatcher, &UiUtils::PasswordWatcher::stateChanged, this, &OutgoingPage::updateWidgets); 1078 connect(m_pwWatcher, &UiUtils::PasswordWatcher::savingFailed, this, &OutgoingPage::saved); 1079 connect(m_pwWatcher, &UiUtils::PasswordWatcher::savingDone, this, &OutgoingPage::saved); 1080 connect(m_pwWatcher, &UiUtils::PasswordWatcher::readingDone, this, &OutgoingPage::slotSetPassword); 1081 connect(m_parent, &SettingsDialog::reloadPasswordsRequested, smtpPass, &QLineEdit::clear); 1082 connect(m_parent, &SettingsDialog::reloadPasswordsRequested, m_pwWatcher, &UiUtils::PasswordWatcher::reloadPassword); 1083 1084 updateWidgets(); 1085 } 1086 1087 void OutgoingPage::slotSetPassword() 1088 { 1089 smtpPass->setText(m_pwWatcher->password()); 1090 } 1091 1092 void OutgoingPage::slotSetSubmissionMethod() 1093 { 1094 switch (method->currentIndex()) { 1095 case SENDMAIL: 1096 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::SENDMAIL); 1097 break; 1098 case IMAP_SENDMAIL: 1099 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::IMAP_SENDMAIL); 1100 break; 1101 case NETWORK: 1102 switch (encryption->currentIndex()) { 1103 case SMTP: 1104 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::SMTP); 1105 break; 1106 case SMTP_STARTTLS: 1107 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::SMTP_STARTTLS); 1108 break; 1109 case SSMTP: 1110 m_smtpAccountSettings->setSubmissionMethod(MSA::Account::Method::SSMTP); 1111 break; 1112 } 1113 break; 1114 default: 1115 Q_ASSERT(false); 1116 } 1117 // Toggle the default ports upon changing the delivery method 1118 smtpPort->setText(QString::number(m_smtpAccountSettings->port())); 1119 } 1120 1121 void OutgoingPage::setPortByText(const QString &text) 1122 { 1123 m_smtpAccountSettings->setPort(text.toUShort()); 1124 } 1125 1126 void OutgoingPage::updateWidgets() 1127 { 1128 QFormLayout *lay = formLayout; 1129 Q_ASSERT(lay); 1130 1131 switch (m_smtpAccountSettings->submissionMethod()) { 1132 case MSA::Account::Method::SMTP: 1133 method->setCurrentIndex(NETWORK); 1134 encryption->setCurrentIndex(SMTP); 1135 break; 1136 case MSA::Account::Method::SMTP_STARTTLS: 1137 method->setCurrentIndex(NETWORK); 1138 encryption->setCurrentIndex(SMTP_STARTTLS); 1139 break; 1140 case MSA::Account::Method::SSMTP: 1141 method->setCurrentIndex(NETWORK); 1142 encryption->setCurrentIndex(SSMTP); 1143 break; 1144 case MSA::Account::Method::SENDMAIL: 1145 method->setCurrentIndex(SENDMAIL); 1146 encryption->setVisible(false); 1147 encryptionLabel->setVisible(false); 1148 break; 1149 case MSA::Account::Method::IMAP_SENDMAIL: 1150 method->setCurrentIndex(IMAP_SENDMAIL); 1151 encryption->setVisible(false); 1152 encryptionLabel->setVisible(false); 1153 break; 1154 } 1155 1156 switch (m_smtpAccountSettings->submissionMethod()) { 1157 case MSA::Account::Method::SMTP: 1158 case MSA::Account::Method::SMTP_STARTTLS: 1159 case MSA::Account::Method::SSMTP: 1160 { 1161 encryption->setVisible(true); 1162 encryptionLabel->setVisible(true); 1163 smtpHost->setVisible(true); 1164 lay->labelForField(smtpHost)->setVisible(true); 1165 smtpHost->setText(m_smtpAccountSettings->server()); 1166 smtpPort->setVisible(true); 1167 lay->labelForField(smtpPort)->setVisible(true); 1168 smtpPort->setText(QString::number(m_smtpAccountSettings->port())); 1169 smtpPort->setValidator(new QIntValidator(1, 65535, this)); 1170 smtpAuth->setVisible(true); 1171 lay->labelForField(smtpAuth)->setVisible(true); 1172 bool authEnabled = m_smtpAccountSettings->authenticateEnabled(); 1173 smtpAuth->setChecked(authEnabled); 1174 smtpAuthReuseImapCreds->setVisible(authEnabled); 1175 lay->labelForField(smtpAuthReuseImapCreds)->setVisible(authEnabled); 1176 bool reuseImapCreds = m_smtpAccountSettings->reuseImapAuthentication(); 1177 smtpAuthReuseImapCreds->setChecked(reuseImapCreds); 1178 smtpUser->setVisible(authEnabled && !reuseImapCreds); 1179 lay->labelForField(smtpUser)->setVisible(authEnabled && !reuseImapCreds); 1180 smtpUser->setText(m_smtpAccountSettings->username()); 1181 sendmail->setVisible(false); 1182 lay->labelForField(sendmail)->setVisible(false); 1183 saveToImap->setVisible(true); 1184 lay->labelForField(saveToImap)->setVisible(true); 1185 saveToImap->setChecked(m_smtpAccountSettings->saveToImap()); 1186 smtpBurl->setVisible(saveToImap->isChecked()); 1187 lay->labelForField(smtpBurl)->setVisible(saveToImap->isChecked()); 1188 smtpBurl->setChecked(m_smtpAccountSettings->useBurl()); 1189 1190 if (!m_pwWatcher->isPluginAvailable()) 1191 smtpPass->setText(QString()); 1192 1193 passwordWarning->setVisible(authEnabled && !reuseImapCreds && !smtpPass->text().isEmpty()); 1194 if (m_pwWatcher->isStorageEncrypted()) { 1195 passwordWarning->setStyleSheet(QString()); 1196 passwordWarning->setText(tr("This password will be saved in encrypted storage. " 1197 "If you do not enter password here, Trojitá will prompt for one when needed.")); 1198 } else { 1199 passwordWarning->setStyleSheet(SettingsDialog::warningStyleSheet); 1200 passwordWarning->setText(tr("This password will be saved in clear text. " 1201 "If you do not enter password here, Trojitá will prompt for one when needed.")); 1202 } 1203 1204 passwordPluginStatus->setVisible(authEnabled && !reuseImapCreds && 1205 (!m_pwWatcher->isPluginAvailable() || m_pwWatcher->isWaitingForPlugin() || !m_pwWatcher->didReadOk() || !m_pwWatcher->didWriteOk())); 1206 passwordPluginStatus->setText(m_pwWatcher->progressMessage()); 1207 1208 smtpPass->setVisible(authEnabled && !reuseImapCreds); 1209 smtpPass->setEnabled(m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin()); 1210 lay->labelForField(smtpPass)->setVisible(authEnabled && !reuseImapCreds); 1211 lay->labelForField(smtpPass)->setEnabled(m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin()); 1212 1213 break; 1214 } 1215 case MSA::Account::Method::SENDMAIL: 1216 case MSA::Account::Method::IMAP_SENDMAIL: 1217 encryption->setVisible(false); 1218 encryptionLabel->setVisible(false); 1219 smtpHost->setVisible(false); 1220 lay->labelForField(smtpHost)->setVisible(false); 1221 smtpPort->setVisible(false); 1222 lay->labelForField(smtpPort)->setVisible(false); 1223 showPortWarning(QString()); 1224 smtpAuth->setVisible(false); 1225 lay->labelForField(smtpAuth)->setVisible(false); 1226 smtpUser->setVisible(false); 1227 lay->labelForField(smtpUser)->setVisible(false); 1228 smtpPass->setVisible(false); 1229 lay->labelForField(smtpPass)->setVisible(false); 1230 passwordWarning->setVisible(false); 1231 passwordPluginStatus->setVisible(false); 1232 if (m_smtpAccountSettings->submissionMethod() == MSA::Account::Method::SENDMAIL) { 1233 sendmail->setVisible(true); 1234 lay->labelForField(sendmail)->setVisible(true); 1235 sendmail->setText(m_smtpAccountSettings->pathToSendmail()); 1236 if (sendmail->text().isEmpty()) 1237 sendmail->setText(Common::SettingsNames::sendmailDefaultCmd); 1238 saveToImap->setVisible(true); 1239 saveToImap->setChecked(m_smtpAccountSettings->saveToImap()); 1240 lay->labelForField(saveToImap)->setVisible(true); 1241 } else { 1242 sendmail->setVisible(false); 1243 lay->labelForField(sendmail)->setVisible(false); 1244 saveToImap->setChecked(true); 1245 saveToImap->setVisible(false); 1246 lay->labelForField(saveToImap)->setVisible(false); 1247 } 1248 smtpBurl->setVisible(false); 1249 lay->labelForField(smtpBurl)->setVisible(false); 1250 smtpBurl->setChecked(m_smtpAccountSettings->useBurl()); 1251 passwordPluginStatus->setVisible(false); 1252 } 1253 saveFolderName->setVisible(saveToImap->isChecked()); 1254 lay->labelForField(saveFolderName)->setVisible(saveToImap->isChecked()); 1255 saveFolderName->setText(m_smtpAccountSettings->sentMailboxName()); 1256 1257 emit widgetsUpdated(); 1258 1259 } 1260 1261 void OutgoingPage::save(QSettings &s) 1262 { 1263 m_smtpAccountSettings->saveSettings(); 1264 1265 if (smtpAuth->isVisibleTo(this) && smtpAuth->isChecked() && m_pwWatcher->isPluginAvailable() && !m_pwWatcher->isWaitingForPlugin()) { 1266 m_pwWatcher->setPassword(smtpPass->text()); 1267 } else { 1268 emit saved(); 1269 } 1270 } 1271 1272 void OutgoingPage::showPortWarning(const QString &warning) 1273 { 1274 if (!warning.isEmpty()) { 1275 portWarningLabel->setVisible(true); 1276 portWarningLabel->setText(warning); 1277 } else { 1278 portWarningLabel->setVisible(false); 1279 } 1280 1281 } 1282 1283 QWidget *OutgoingPage::asWidget() 1284 { 1285 return this; 1286 } 1287 1288 bool OutgoingPage::checkValidity() const 1289 { 1290 switch (m_smtpAccountSettings->submissionMethod()) { 1291 case MSA::Account::Method::SMTP: 1292 case MSA::Account::Method::SMTP_STARTTLS: 1293 case MSA::Account::Method::SSMTP: 1294 if (checkProblemWithEmptyTextField(smtpHost, tr("The SMTP server hostname is missing here"))) 1295 return false; 1296 if (smtpAuth->isChecked() && !smtpAuthReuseImapCreds->isChecked() && checkProblemWithEmptyTextField(smtpUser, tr("The SMTP username is missing here"))) 1297 return false; 1298 break; 1299 case MSA::Account::Method::SENDMAIL: 1300 if (checkProblemWithEmptyTextField(sendmail, tr("The SMTP server hostname is missing here"))) 1301 return false; 1302 break; 1303 case MSA::Account::Method::IMAP_SENDMAIL: 1304 break; 1305 } 1306 1307 if (saveToImap->isChecked() && checkProblemWithEmptyTextField(saveFolderName, tr("Please specify the folder name here"))) 1308 return false; 1309 1310 return true; 1311 } 1312 1313 bool OutgoingPage::passwordFailures(QString &message) const 1314 { 1315 // The const_cast is needed as Qt4 does not define the arguement of isVisibleTo as const 1316 if (!smtpAuth->isVisibleTo(const_cast<Gui::OutgoingPage*>(this)) || !smtpAuth->isChecked() || !m_pwWatcher->isPluginAvailable() || m_pwWatcher->isWaitingForPlugin() || m_pwWatcher->didWriteOk()) { 1317 return false; 1318 } else { 1319 message = m_pwWatcher->progressMessage(); 1320 return true; 1321 } 1322 } 1323 1324 }