Composer: don't pass along the sender specification
[trojita.git] / src / Gui / Window.cpp
blob82b3c4d4c176c3dc4250895de86935be3c6e9f52
1 /* Copyright (C) 2006 - 2012 Jan Kundrát <jkt@flaska.net>
3 This file is part of the Trojita Qt IMAP e-mail client,
4 http://trojita.flaska.net/
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <QAuthenticator>
24 #include <QDesktopServices>
25 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
26 # include <QStandardPaths>
27 #endif
28 #include <QDir>
29 #include <QDockWidget>
30 #include <QFileDialog>
31 #include <QHeaderView>
32 #include <QInputDialog>
33 #include <QItemSelectionModel>
34 #include <QMenuBar>
35 #include <QMessageBox>
36 #include <QProgressBar>
37 #include <QSplitter>
38 #include <QSslError>
39 #include <QStatusBar>
40 #include <QTextDocument>
41 #include <QToolBar>
42 #include <QToolButton>
43 #include <QUrl>
45 #include "Common/PortNumbers.h"
46 #include "Common/SettingsNames.h"
47 #include "Composer/SenderIdentitiesModel.h"
48 #include "Imap/Model/CombinedCache.h"
49 #include "Imap/Model/MailboxModel.h"
50 #include "Imap/Model/MailboxTree.h"
51 #include "Imap/Model/MemoryCache.h"
52 #include "Imap/Model/Model.h"
53 #include "Imap/Model/ModelWatcher.h"
54 #include "Imap/Model/MsgListModel.h"
55 #include "Imap/Model/PrettyMailboxModel.h"
56 #include "Imap/Model/PrettyMsgListModel.h"
57 #include "Imap/Model/ThreadingMsgListModel.h"
58 #include "Imap/Model/Utils.h"
59 #include "Imap/Network/FileDownloadManager.h"
60 #include "AbookAddressbook.h"
61 #include "ComposeWidget.h"
62 #include "IconLoader.h"
63 #include "MailBoxTreeView.h"
64 #include "MessageListWidget.h"
65 #include "MessageView.h"
66 #include "MsgListView.h"
67 #include "ProtocolLoggerWidget.h"
68 #include "SettingsDialog.h"
69 #include "SimplePartWidget.h"
70 #include "Streams/SocketFactory.h"
71 #include "TaskProgressIndicator.h"
72 #include "Util.h"
73 #include "Window.h"
75 #include "ui_CreateMailboxDialog.h"
77 #include "Imap/Model/ModelTest/modeltest.h"
79 /** @short All user-facing widgets and related classes */
80 namespace Gui
83 MainWindow::MainWindow(): QMainWindow(), model(0), m_actionSortNone(0), m_ignoreStoredPassword(false), m_supportsCatenate(false),
84 m_supportsGenUrlAuth(false), m_supportsImapSubmission(false)
86 qRegisterMetaType<QList<QSslCertificate> >("QList<QSslCertificate>");
87 qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
88 createWidgets();
90 migrateSettings();
91 QSettings s;
93 m_senderIdentities = new Composer::SenderIdentitiesModel(this);
94 m_senderIdentities->loadFromSettings(s);
96 if (! s.contains(Common::SettingsNames::imapMethodKey)) {
97 QTimer::singleShot(0, this, SLOT(slotShowSettings()));
101 setupModels();
102 createActions();
103 createMenus();
105 // Please note that Qt 4.6.1 really requires passing the method signature this way, *not* using the SLOT() macro
106 QDesktopServices::setUrlHandler(QLatin1String("mailto"), this, "slotComposeMailUrl");
108 slotUpdateWindowTitle();
111 void MainWindow::createActions()
113 m_mainToolbar = addToolBar(tr("Navigation"));
115 reloadMboxList = new QAction(style()->standardIcon(QStyle::SP_ArrowRight), tr("Update List of Child Mailboxes"), this);
116 connect(reloadMboxList, SIGNAL(triggered()), this, SLOT(slotReloadMboxList()));
118 resyncMbox = new QAction(loadIcon(QLatin1String("view-refresh")), tr("Check for New Messages"), this);
119 connect(resyncMbox, SIGNAL(triggered()), this, SLOT(slotResyncMbox()));
121 reloadAllMailboxes = new QAction(tr("Reload Everything"), this);
122 // connect later
124 exitAction = new QAction(loadIcon(QLatin1String("application-exit")), tr("E&xit"), this);
125 exitAction->setShortcut(tr("Ctrl+Q"));
126 exitAction->setStatusTip(tr("Exit the application"));
127 connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
129 QActionGroup *netPolicyGroup = new QActionGroup(this);
130 netPolicyGroup->setExclusive(true);
131 netOffline = new QAction(loadIcon(QLatin1String("network-offline")), tr("Offline"), netPolicyGroup);
132 netOffline->setCheckable(true);
133 // connect later
134 netExpensive = new QAction(loadIcon(QLatin1String("network-expensive")), tr("Expensive Connection"), netPolicyGroup);
135 netExpensive->setCheckable(true);
136 // connect later
137 netOnline = new QAction(loadIcon(QLatin1String("network-online")), tr("Free Access"), netPolicyGroup);
138 netOnline->setCheckable(true);
139 // connect later
141 showFullView = new QAction(loadIcon(QLatin1String("edit-find-mail")), tr("Show Full Tree Window"), this);
142 showFullView->setCheckable(true);
143 connect(showFullView, SIGNAL(triggered(bool)), allDock, SLOT(setVisible(bool)));
144 connect(allDock, SIGNAL(visibilityChanged(bool)), showFullView, SLOT(setChecked(bool)));
146 showTaskView = new QAction(tr("Show ImapTask tree"), this);
147 showTaskView->setCheckable(true);
148 connect(showTaskView, SIGNAL(triggered(bool)), taskDock, SLOT(setVisible(bool)));
149 connect(taskDock, SIGNAL(visibilityChanged(bool)), showTaskView, SLOT(setChecked(bool)));
151 showImapLogger = new QAction(tr("Show IMAP protocol log"), this);
152 showImapLogger->setCheckable(true);
153 connect(showImapLogger, SIGNAL(toggled(bool)), imapLoggerDock, SLOT(setVisible(bool)));
154 connect(imapLoggerDock, SIGNAL(visibilityChanged(bool)), showImapLogger, SLOT(setChecked(bool)));
156 logPersistent = new QAction(tr("Log into %1").arg(Imap::Mailbox::persistentLogFileName()), this);
157 logPersistent->setCheckable(true);
158 connect(logPersistent, SIGNAL(triggered(bool)), imapLogger, SLOT(slotSetPersistentLogging(bool)));
160 showImapCapabilities = new QAction(tr("IMAP Server Information..."), this);
161 connect(showImapCapabilities, SIGNAL(triggered()), this, SLOT(slotShowImapInfo()));
163 showMenuBar = new QAction(loadIcon(QLatin1String("view-list-text")), tr("Show Main Menu Bar"), this);
164 showMenuBar->setCheckable(true);
165 showMenuBar->setChecked(true);
166 showMenuBar->setShortcut(tr("Ctrl+M"));
167 addAction(showMenuBar); // otherwise it won't work with hidden menu bar
168 connect(showMenuBar, SIGNAL(triggered(bool)), menuBar(), SLOT(setVisible(bool)));
170 showToolBar = new QAction(tr("Show Toolbar"), this);
171 showToolBar->setCheckable(true);
172 showToolBar->setChecked(true);
173 connect(showToolBar, SIGNAL(triggered(bool)), m_mainToolbar, SLOT(setVisible(bool)));
175 configSettings = new QAction(loadIcon(QLatin1String("configure")), tr("Settings..."), this);
176 connect(configSettings, SIGNAL(triggered()), this, SLOT(slotShowSettings()));
178 composeMail = new QAction(loadIcon(QLatin1String("document-edit")), tr("Compose Mail..."), this);
179 connect(composeMail, SIGNAL(triggered()), this, SLOT(slotComposeMail()));
181 expunge = new QAction(loadIcon(QLatin1String("trash-empty")), tr("Expunge Mailbox"), this);
182 expunge->setShortcut(tr("Ctrl+E"));
183 connect(expunge, SIGNAL(triggered()), this, SLOT(slotExpunge()));
185 markAsRead = new QAction(loadIcon(QLatin1String("mail-mark-read")), tr("Mark as Read"), this);
186 markAsRead->setCheckable(true);
187 markAsRead->setShortcut(Qt::Key_M);
188 msgListWidget->tree->addAction(markAsRead);
189 connect(markAsRead, SIGNAL(triggered(bool)), this, SLOT(handleMarkAsRead(bool)));
191 m_nextMessage = new QAction(tr("Next Unread Message"), this);
192 m_nextMessage->setShortcut(Qt::Key_N);
193 msgListWidget->tree->addAction(m_nextMessage);
194 msgView->addAction(m_nextMessage);
195 connect(m_nextMessage, SIGNAL(triggered()), this, SLOT(slotNextUnread()));
197 m_previousMessage = new QAction(tr("Previous Unread Message"), this);
198 m_previousMessage->setShortcut(Qt::Key_P);
199 msgListWidget->tree->addAction(m_previousMessage);
200 msgView->addAction(m_previousMessage);
201 connect(m_previousMessage, SIGNAL(triggered()), this, SLOT(slotPreviousUnread()));
203 markAsDeleted = new QAction(loadIcon(QLatin1String("list-remove")), tr("Mark as Deleted"), this);
204 markAsDeleted->setCheckable(true);
205 markAsDeleted->setShortcut(Qt::Key_Delete);
206 msgListWidget->tree->addAction(markAsDeleted);
207 connect(markAsDeleted, SIGNAL(triggered(bool)), this, SLOT(handleMarkAsDeleted(bool)));
209 saveWholeMessage = new QAction(loadIcon(QLatin1String("file-save")), tr("Save Message..."), this);
210 msgListWidget->tree->addAction(saveWholeMessage);
211 connect(saveWholeMessage, SIGNAL(triggered()), this, SLOT(slotSaveCurrentMessageBody()));
213 viewMsgHeaders = new QAction(tr("View Message Headers..."), this);
214 viewMsgHeaders->setShortcut(tr("Ctrl+U"));
215 msgListWidget->tree->addAction(viewMsgHeaders);
216 connect(viewMsgHeaders, SIGNAL(triggered()), this, SLOT(slotViewMsgHeaders()));
218 createChildMailbox = new QAction(tr("Create Child Mailbox..."), this);
219 connect(createChildMailbox, SIGNAL(triggered()), this, SLOT(slotCreateMailboxBelowCurrent()));
221 createTopMailbox = new QAction(tr("Create New Mailbox..."), this);
222 connect(createTopMailbox, SIGNAL(triggered()), this, SLOT(slotCreateTopMailbox()));
224 deleteCurrentMailbox = new QAction(tr("Delete Mailbox"), this);
225 connect(deleteCurrentMailbox, SIGNAL(triggered()), this, SLOT(slotDeleteCurrentMailbox()));
227 #ifdef XTUPLE_CONNECT
228 xtIncludeMailboxInSync = new QAction(tr("Synchronize with xTuple"), this);
229 xtIncludeMailboxInSync->setCheckable(true);
230 connect(xtIncludeMailboxInSync, SIGNAL(triggered()), this, SLOT(slotXtSyncCurrentMailbox()));
231 #endif
233 replyTo = new QAction(tr("Reply..."), this);
234 replyTo->setShortcut(tr("Ctrl+R"));
235 connect(replyTo, SIGNAL(triggered()), this, SLOT(slotReplyTo()));
237 replyAll = new QAction(tr("Reply All..."), this);
238 replyAll->setShortcut(tr("Ctrl+Shift+R"));
239 connect(replyAll, SIGNAL(triggered()), this, SLOT(slotReplyAll()));
241 actionThreadMsgList = new QAction(tr("Show Messages in Threads"), this);
242 actionThreadMsgList->setCheckable(true);
243 // This action is enabled/disabled by model's capabilities
244 actionThreadMsgList->setEnabled(false);
245 if (QSettings().value(Common::SettingsNames::guiMsgListShowThreading).toBool()) {
246 actionThreadMsgList->setChecked(true);
247 // The actual threading will be performed only when model updates its capabilities
249 connect(actionThreadMsgList, SIGNAL(triggered(bool)), this, SLOT(slotThreadMsgList()));
251 QActionGroup *sortOrderGroup = new QActionGroup(this);
252 m_actionSortAscending = new QAction(tr("Ascending"), sortOrderGroup);
253 m_actionSortAscending->setCheckable(true);
254 m_actionSortAscending->setChecked(true);
255 m_actionSortDescending = new QAction(tr("Descending"), sortOrderGroup);
256 m_actionSortDescending->setCheckable(true);
257 connect(sortOrderGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotSortingPreferenceChanged()));
259 QActionGroup *sortColumnGroup = new QActionGroup(this);
260 m_actionSortNone = new QAction(tr("No sorting"), sortColumnGroup);
261 m_actionSortNone->setCheckable(true);
262 m_actionSortThreading = new QAction(tr("Sorted by Threading"), sortColumnGroup);
263 m_actionSortThreading->setCheckable(true);
264 m_actionSortByArrival = new QAction(tr("Arrival"), sortColumnGroup);
265 m_actionSortByArrival->setCheckable(true);
266 m_actionSortByCc = new QAction(tr("Cc (Carbon Copy)"), sortColumnGroup);
267 m_actionSortByCc->setCheckable(true);
268 m_actionSortByDate = new QAction(tr("Date from Message Headers"), sortColumnGroup);
269 m_actionSortByDate->setCheckable(true);
270 m_actionSortByFrom = new QAction(tr("From Address"), sortColumnGroup);
271 m_actionSortByFrom->setCheckable(true);
272 m_actionSortBySize = new QAction(tr("Size"), sortColumnGroup);
273 m_actionSortBySize->setCheckable(true);
274 m_actionSortBySubject = new QAction(tr("Subject"), sortColumnGroup);
275 m_actionSortBySubject->setCheckable(true);
276 m_actionSortByTo = new QAction(tr("To Address"), sortColumnGroup);
277 m_actionSortByTo->setCheckable(true);
278 connect(sortColumnGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotSortingPreferenceChanged()));
279 slotSortingConfirmed(-1, Qt::AscendingOrder);
281 actionHideRead = new QAction(tr("Hide Read Messages"), this);
282 actionHideRead->setCheckable(true);
283 addAction(actionHideRead);
284 if (QSettings().value(Common::SettingsNames::guiMsgListHideRead).toBool()) {
285 actionHideRead->setChecked(true);
286 prettyMsgListModel->setHideRead(true);
288 connect(actionHideRead, SIGNAL(triggered(bool)), this, SLOT(slotHideRead()));
290 QActionGroup *layoutGroup = new QActionGroup(this);
291 m_actionLayoutCompact = new QAction(tr("Compact"), layoutGroup);
292 m_actionLayoutCompact->setCheckable(true);
293 m_actionLayoutCompact->setChecked(true);
294 connect(m_actionLayoutCompact, SIGNAL(triggered()), this, SLOT(slotLayoutCompact()));
295 m_actionLayoutWide = new QAction(tr("Wide"), layoutGroup);
296 m_actionLayoutWide->setCheckable(true);
297 connect(m_actionLayoutWide, SIGNAL(triggered()), this, SLOT(slotLayoutWide()));
299 if (QSettings().value(Common::SettingsNames::guiMainWindowLayout) == Common::SettingsNames::guiMainWindowLayoutWide) {
300 m_actionLayoutWide->setChecked(true);
301 slotLayoutWide();
304 m_actionShowOnlySubscribed = new QAction(tr("Show Only Subscribed Folders"), this);
305 m_actionShowOnlySubscribed->setCheckable(true);
306 m_actionShowOnlySubscribed->setEnabled(false);
307 connect(m_actionShowOnlySubscribed, SIGNAL(toggled(bool)), this, SLOT(slotShowOnlySubscribed()));
308 m_actionSubscribeMailbox = new QAction(tr("Subscribed"), this);
309 m_actionSubscribeMailbox->setCheckable(true);
310 m_actionSubscribeMailbox->setEnabled(false);
311 connect(m_actionSubscribeMailbox, SIGNAL(triggered()), this, SLOT(slotSubscribeCurrentMailbox()));
313 aboutTrojita = new QAction(trUtf8("About Trojitá..."), this);
314 connect(aboutTrojita, SIGNAL(triggered()), this, SLOT(slotShowAboutTrojita()));
316 donateToTrojita = new QAction(tr("Donate to the project"), this);
317 connect(donateToTrojita, SIGNAL(triggered()), this, SLOT(slotDonateToTrojita()));
319 connectModelActions();
321 m_mainToolbar->addAction(composeMail);
322 m_mainToolbar->addAction(replyTo);
323 m_mainToolbar->addAction(replyAll);
324 m_mainToolbar->addAction(expunge);
325 m_mainToolbar->addSeparator();
326 m_mainToolbar->addAction(markAsRead);
327 m_mainToolbar->addAction(markAsDeleted);
328 m_mainToolbar->addSeparator();
329 m_mainToolbar->addAction(showMenuBar);
330 m_mainToolbar->addAction(configSettings);
333 void MainWindow::connectModelActions()
335 connect(reloadAllMailboxes, SIGNAL(triggered()), model, SLOT(reloadMailboxList()));
336 connect(netOffline, SIGNAL(triggered()), model, SLOT(setNetworkOffline()));
337 connect(netExpensive, SIGNAL(triggered()), model, SLOT(setNetworkExpensive()));
338 connect(netOnline, SIGNAL(triggered()), model, SLOT(setNetworkOnline()));
341 void MainWindow::createMenus()
343 QMenu *imapMenu = menuBar()->addMenu(tr("IMAP"));
344 imapMenu->addAction(composeMail);
345 imapMenu->addAction(replyTo);
346 imapMenu->addAction(replyAll);
347 imapMenu->addAction(expunge);
348 imapMenu->addSeparator()->setText(tr("Network Access"));
349 QMenu *netPolicyMenu = imapMenu->addMenu(tr("Network Access"));
350 netPolicyMenu->addAction(netOffline);
351 netPolicyMenu->addAction(netExpensive);
352 netPolicyMenu->addAction(netOnline);
353 QMenu *debugMenu = imapMenu->addMenu(tr("Debugging"));
354 debugMenu->addAction(showFullView);
355 debugMenu->addAction(showTaskView);
356 debugMenu->addAction(showImapLogger);
357 debugMenu->addAction(logPersistent);
358 debugMenu->addAction(showImapCapabilities);
359 imapMenu->addSeparator();
360 imapMenu->addAction(configSettings);
361 imapMenu->addSeparator();
362 imapMenu->addAction(exitAction);
364 QMenu *viewMenu = menuBar()->addMenu(tr("View"));
365 viewMenu->addAction(showMenuBar);
366 viewMenu->addAction(showToolBar);
367 QMenu *layoutMenu = viewMenu->addMenu(tr("Layout"));
368 layoutMenu->addAction(m_actionLayoutCompact);
369 layoutMenu->addAction(m_actionLayoutWide);
370 viewMenu->addSeparator();
371 viewMenu->addAction(m_previousMessage);
372 viewMenu->addAction(m_nextMessage);
373 viewMenu->addSeparator();
374 QMenu *sortMenu = viewMenu->addMenu(tr("Sorting"));
375 sortMenu->addAction(m_actionSortNone);
376 sortMenu->addAction(m_actionSortThreading);
377 sortMenu->addAction(m_actionSortByArrival);
378 sortMenu->addAction(m_actionSortByCc);
379 sortMenu->addAction(m_actionSortByDate);
380 sortMenu->addAction(m_actionSortByFrom);
381 sortMenu->addAction(m_actionSortBySize);
382 sortMenu->addAction(m_actionSortBySubject);
383 sortMenu->addAction(m_actionSortByTo);
384 sortMenu->addSeparator();
385 sortMenu->addAction(m_actionSortAscending);
386 sortMenu->addAction(m_actionSortDescending);
388 viewMenu->addAction(actionThreadMsgList);
389 viewMenu->addAction(actionHideRead);
390 viewMenu->addAction(m_actionShowOnlySubscribed);
392 QMenu *mailboxMenu = menuBar()->addMenu(tr("Mailbox"));
393 mailboxMenu->addAction(resyncMbox);
394 mailboxMenu->addSeparator();
395 mailboxMenu->addAction(reloadAllMailboxes);
397 QMenu *helpMenu = menuBar()->addMenu(tr("Help"));
398 helpMenu->addAction(donateToTrojita);
399 helpMenu->addSeparator();
400 helpMenu->addAction(aboutTrojita);
402 networkIndicator->setMenu(netPolicyMenu);
403 networkIndicator->setDefaultAction(netOnline);
406 void MainWindow::createWidgets()
408 mboxTree = new MailBoxTreeView();
409 connect(mboxTree, SIGNAL(customContextMenuRequested(const QPoint &)),
410 this, SLOT(showContextMenuMboxTree(const QPoint &)));
412 msgListWidget = new MessageListWidget();
413 msgListWidget->tree->setContextMenuPolicy(Qt::CustomContextMenu);
414 msgListWidget->tree->setAlternatingRowColors(true);
416 connect(msgListWidget->tree, SIGNAL(customContextMenuRequested(const QPoint &)),
417 this, SLOT(showContextMenuMsgListTree(const QPoint &)));
418 connect(msgListWidget->tree, SIGNAL(activated(const QModelIndex &)), this, SLOT(msgListActivated(const QModelIndex &)));
419 connect(msgListWidget->tree, SIGNAL(clicked(const QModelIndex &)), this, SLOT(msgListClicked(const QModelIndex &)));
420 connect(msgListWidget->tree, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(msgListDoubleClicked(const QModelIndex &)));
421 connect(msgListWidget, SIGNAL(requestingSearch(QStringList)), this, SLOT(slotSearchRequested(QStringList)));
423 msgView = new MessageView();
424 area = new QScrollArea();
425 area->setWidget(msgView);
426 area->setWidgetResizable(true);
427 connect(msgView, SIGNAL(messageChanged()), this, SLOT(scrollMessageUp()));
428 connect(msgView, SIGNAL(linkHovered(QString)), this, SLOT(slotShowLinkTarget(QString)));
429 if (QSettings().value(Common::SettingsNames::appLoadHomepage, QVariant(true)).toBool() &&
430 !QSettings().value(Common::SettingsNames::imapStartOffline).toBool()) {
431 msgView->setHomepageUrl(QUrl(QString::fromUtf8("http://welcome.trojita.flaska.net/%1").arg(QCoreApplication::applicationVersion())));
434 m_mainHSplitter = new QSplitter();
435 m_mainVSplitter = new QSplitter();
436 m_mainVSplitter->setOrientation(Qt::Vertical);
437 m_mainVSplitter->addWidget(msgListWidget);
438 m_mainVSplitter->addWidget(area);
439 m_mainHSplitter->addWidget(mboxTree);
440 m_mainHSplitter->addWidget(m_mainVSplitter);
442 // The mboxTree shall not expand...
443 m_mainHSplitter->setStretchFactor(0, 0);
444 // ...while the msgListTree shall consume all the remaining space
445 m_mainHSplitter->setStretchFactor(1, 1);
447 setCentralWidget(m_mainHSplitter);
449 allDock = new QDockWidget("Everything", this);
450 allTree = new QTreeView(allDock);
451 allDock->hide();
452 allTree->setUniformRowHeights(true);
453 allTree->setHeaderHidden(true);
454 allDock->setWidget(allTree);
455 addDockWidget(Qt::LeftDockWidgetArea, allDock);
456 taskDock = new QDockWidget("IMAP Tasks", this);
457 taskTree = new QTreeView(taskDock);
458 taskDock->hide();
459 taskTree->setHeaderHidden(true);
460 taskDock->setWidget(taskTree);
461 addDockWidget(Qt::LeftDockWidgetArea, taskDock);
463 imapLoggerDock = new QDockWidget(tr("IMAP Protocol"), this);
464 imapLogger = new ProtocolLoggerWidget(imapLoggerDock);
465 imapLoggerDock->hide();
466 imapLoggerDock->setWidget(imapLogger);
467 addDockWidget(Qt::BottomDockWidgetArea, imapLoggerDock);
469 busyParsersIndicator = new TaskProgressIndicator(this);
470 statusBar()->addPermanentWidget(busyParsersIndicator);
471 busyParsersIndicator->hide();
473 networkIndicator = new QToolButton(this);
474 networkIndicator->setPopupMode(QToolButton::InstantPopup);
475 statusBar()->addPermanentWidget(networkIndicator);
478 void MainWindow::setupModels()
480 Imap::Mailbox::SocketFactoryPtr factory;
481 Imap::Mailbox::TaskFactoryPtr taskFactory(new Imap::Mailbox::TaskFactory());
482 QSettings s;
484 using Common::SettingsNames;
485 if (s.value(SettingsNames::imapMethodKey).toString() == SettingsNames::methodTCP) {
486 factory.reset(new Imap::Mailbox::TlsAbleSocketFactory(
487 s.value(SettingsNames::imapHostKey).toString(),
488 s.value(SettingsNames::imapPortKey, QString::number(Common::PORT_IMAP)).toUInt()));
489 factory->setStartTlsRequired(s.value(SettingsNames::imapStartTlsKey, true).toBool());
490 } else if (s.value(SettingsNames::imapMethodKey).toString() == SettingsNames::methodSSL) {
491 factory.reset(new Imap::Mailbox::SslSocketFactory(
492 s.value(SettingsNames::imapHostKey).toString(),
493 s.value(SettingsNames::imapPortKey, QString::number(Common::PORT_IMAPS)).toUInt()));
494 } else {
495 QStringList args = s.value(SettingsNames::imapProcessKey).toString().split(QLatin1Char(' '));
496 if (args.isEmpty()) {
497 // it's going to fail anyway
498 args << QLatin1String("");
500 QString appName = args.takeFirst();
501 factory.reset(new Imap::Mailbox::ProcessSocketFactory(appName, args));
504 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
505 QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
506 #else
507 QString cacheDir = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
508 #endif
509 if (cacheDir.isEmpty())
510 cacheDir = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName();
511 Imap::Mailbox::AbstractCache *cache = 0;
513 bool shouldUsePersistentCache = s.value(SettingsNames::cacheOfflineKey).toString() != SettingsNames::cacheOfflineNone;
514 if (shouldUsePersistentCache) {
515 if (! QDir().mkpath(cacheDir)) {
516 QMessageBox::critical(this, tr("Cache Error"), tr("Failed to create directory %1").arg(cacheDir));
517 shouldUsePersistentCache = false;
521 //setProperty( "trojita-sqlcache-commit-period", QVariant(5000) );
522 //setProperty( "trojita-sqlcache-commit-delay", QVariant(1000) );
524 if (! shouldUsePersistentCache) {
525 cache = new Imap::Mailbox::MemoryCache(this, QString());
526 } else {
527 cache = new Imap::Mailbox::CombinedCache(this, QLatin1String("trojita-imap-cache"), cacheDir);
528 connect(cache, SIGNAL(error(QString)), this, SLOT(cacheError(QString)));
529 if (! static_cast<Imap::Mailbox::CombinedCache *>(cache)->open()) {
530 // Error message was already shown by the cacheError() slot
531 cache->deleteLater();
532 cache = new Imap::Mailbox::MemoryCache(this, QString());
533 } else {
534 if (s.value(SettingsNames::cacheOfflineKey).toString() == SettingsNames::cacheOfflineAll) {
535 cache->setRenewalThreshold(0);
536 } else {
537 bool ok;
538 int num = s.value(SettingsNames::cacheOfflineNumberDaysKey, 30).toInt(&ok);
539 if (!ok)
540 num = 30;
541 cache->setRenewalThreshold(num);
545 model = new Imap::Mailbox::Model(this, cache, factory, taskFactory, s.value(SettingsNames::imapStartOffline).toBool());
546 model->setObjectName(QLatin1String("model"));
547 model->setCapabilitiesBlacklist(s.value(SettingsNames::imapBlacklistedCapabilities).toStringList());
548 if (s.value(SettingsNames::imapEnableId, true).toBool()) {
549 model->setProperty("trojita-imap-enable-id", true);
551 mboxModel = new Imap::Mailbox::MailboxModel(this, model);
552 mboxModel->setObjectName(QLatin1String("mboxModel"));
553 prettyMboxModel = new Imap::Mailbox::PrettyMailboxModel(this, mboxModel);
554 prettyMboxModel->setObjectName(QLatin1String("prettyMboxModel"));
555 msgListModel = new Imap::Mailbox::MsgListModel(this, model);
556 msgListModel->setObjectName(QLatin1String("msgListModel"));
557 threadingMsgListModel = new Imap::Mailbox::ThreadingMsgListModel(this);
558 threadingMsgListModel->setObjectName(QLatin1String("threadingMsgListModel"));
559 threadingMsgListModel->setSourceModel(msgListModel);
560 prettyMsgListModel = new Imap::Mailbox::PrettyMsgListModel(this);
561 prettyMsgListModel->setSourceModel(threadingMsgListModel);
562 prettyMsgListModel->setObjectName(QLatin1String("prettyMsgListModel"));
564 connect(mboxTree, SIGNAL(clicked(const QModelIndex &)), msgListModel, SLOT(setMailbox(const QModelIndex &)));
565 connect(mboxTree, SIGNAL(activated(const QModelIndex &)), msgListModel, SLOT(setMailbox(const QModelIndex &)));
566 connect(msgListModel, SIGNAL(mailboxChanged()), this, SLOT(slotResizeMsgListColumns()));
567 connect(msgListModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateMessageFlags()));
568 connect(msgListModel, SIGNAL(messagesAvailable()), msgListWidget->tree, SLOT(scrollToBottom()));
569 connect(msgListModel, SIGNAL(rowsInserted(QModelIndex,int,int)), msgListWidget, SLOT(slotAutoEnableDisableSearch()));
570 connect(msgListModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), msgListWidget, SLOT(slotAutoEnableDisableSearch()));
571 connect(msgListModel, SIGNAL(layoutChanged()), msgListWidget, SLOT(slotAutoEnableDisableSearch()));
572 connect(msgListModel, SIGNAL(modelReset()), msgListWidget, SLOT(slotAutoEnableDisableSearch()));
574 connect(model, SIGNAL(alertReceived(const QString &)), this, SLOT(alertReceived(const QString &)));
575 connect(model, SIGNAL(connectionError(const QString &)), this, SLOT(connectionError(const QString &)));
576 connect(model, SIGNAL(authRequested()), this, SLOT(authenticationRequested()), Qt::QueuedConnection);
577 connect(model, SIGNAL(authAttemptFailed(QString)), this, SLOT(authenticationFailed(QString)));
578 connect(model, SIGNAL(needsSslDecision(QList<QSslCertificate>,QList<QSslError>)),
579 this, SLOT(sslErrors(QList<QSslCertificate>,QList<QSslError>)), Qt::QueuedConnection);
581 connect(model, SIGNAL(networkPolicyOffline()), this, SLOT(networkPolicyOffline()));
582 connect(model, SIGNAL(networkPolicyExpensive()), this, SLOT(networkPolicyExpensive()));
583 connect(model, SIGNAL(networkPolicyOnline()), this, SLOT(networkPolicyOnline()));
585 connect(model, SIGNAL(connectionStateChanged(QObject *,Imap::ConnectionState)),
586 this, SLOT(showConnectionStatus(QObject *,Imap::ConnectionState)));
588 connect(model, SIGNAL(mailboxDeletionFailed(QString,QString)), this, SLOT(slotMailboxDeleteFailed(QString,QString)));
589 connect(model, SIGNAL(mailboxCreationFailed(QString,QString)), this, SLOT(slotMailboxCreateFailed(QString,QString)));
591 connect(model, SIGNAL(logged(uint,Common::LogMessage)), imapLogger, SLOT(slotImapLogged(uint,Common::LogMessage)));
593 connect(model, SIGNAL(mailboxFirstUnseenMessage(QModelIndex,QModelIndex)), this, SLOT(slotScrollToUnseenMessage(QModelIndex,QModelIndex)));
595 connect(model, SIGNAL(capabilitiesUpdated(QStringList)), this, SLOT(slotCapabilitiesUpdated(QStringList)));
597 connect(msgListModel, SIGNAL(modelReset()), this, SLOT(slotUpdateWindowTitle()));
598 connect(model, SIGNAL(messageCountPossiblyChanged(QModelIndex)), this, SLOT(slotUpdateWindowTitle()));
600 connect(prettyMsgListModel, SIGNAL(sortingPreferenceChanged(int,Qt::SortOrder)), this, SLOT(slotSortingConfirmed(int,Qt::SortOrder)));
602 //Imap::Mailbox::ModelWatcher* w = new Imap::Mailbox::ModelWatcher( this );
603 //w->setModel( model );
605 //ModelTest* tester = new ModelTest( prettyMboxModel, this ); // when testing, test just one model at time
607 mboxTree->setModel(prettyMboxModel);
608 msgListWidget->tree->setModel(prettyMsgListModel);
609 connect(msgListWidget->tree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
610 this, SLOT(msgListSelectionChanged(const QItemSelection &, const QItemSelection &)));
612 allTree->setModel(model);
613 taskTree->setModel(model->taskModel());
614 connect(model->taskModel(), SIGNAL(layoutChanged()), taskTree, SLOT(expandAll()));
615 connect(model->taskModel(), SIGNAL(modelReset()), taskTree, SLOT(expandAll()));
616 connect(model->taskModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), taskTree, SLOT(expandAll()));
617 connect(model->taskModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), taskTree, SLOT(expandAll()));
618 connect(model->taskModel(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), taskTree, SLOT(expandAll()));
620 busyParsersIndicator->setImapModel(model);
622 // TODO write more addressbook backends and make this configurable
623 m_addressBook = new AbookAddressbook();
626 void MainWindow::msgListSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
628 Q_UNUSED(deselected);
629 if (selected.indexes().isEmpty())
630 return;
632 QModelIndex index = selected.indexes().front();
633 if (index.data(Imap::Mailbox::RoleMessageUid).isValid()) {
634 updateMessageFlags(index);
635 msgView->setMessage(index);
639 void MainWindow::msgListActivated(const QModelIndex &index)
641 Q_ASSERT(index.isValid());
643 if (qApp->keyboardModifiers() & Qt::ShiftModifier || qApp->keyboardModifiers() & Qt::ControlModifier)
644 return;
646 if (! index.data(Imap::Mailbox::RoleMessageUid).isValid())
647 return;
649 if (index.column() != Imap::Mailbox::MsgListModel::SEEN) {
650 msgView->setMessage(index);
651 msgListWidget->tree->setCurrentIndex(index);
655 void MainWindow::msgListClicked(const QModelIndex &index)
657 Q_ASSERT(index.isValid());
659 if (qApp->keyboardModifiers() & Qt::ShiftModifier || qApp->keyboardModifiers() & Qt::ControlModifier)
660 return;
662 if (! index.data(Imap::Mailbox::RoleMessageUid).isValid())
663 return;
665 if (index.column() == Imap::Mailbox::MsgListModel::SEEN) {
666 QModelIndex translated;
667 Imap::Mailbox::Model::realTreeItem(index, 0, &translated);
668 if (!translated.data(Imap::Mailbox::RoleIsFetched).toBool())
669 return;
670 Imap::Mailbox::FlagsOperation flagOp = translated.data(Imap::Mailbox::RoleMessageIsMarkedRead).toBool() ?
671 Imap::Mailbox::FLAG_REMOVE : Imap::Mailbox::FLAG_ADD;
672 model->markMessagesRead(QModelIndexList() << translated, flagOp);
676 void MainWindow::msgListDoubleClicked(const QModelIndex &index)
678 Q_ASSERT(index.isValid());
680 if (! index.data(Imap::Mailbox::RoleMessageUid).isValid())
681 return;
683 MessageView *newView = new MessageView(0);
684 QModelIndex realIndex;
685 const Imap::Mailbox::Model *realModel;
686 Imap::Mailbox::TreeItemMessage *message = dynamic_cast<Imap::Mailbox::TreeItemMessage *>(
687 Imap::Mailbox::Model::realTreeItem(index, &realModel, &realIndex));
688 Q_ASSERT(message);
689 Q_ASSERT(realModel == model);
690 newView->setMessage(index);
692 QScrollArea *widget = new QScrollArea();
693 widget->setFocusPolicy(Qt::StrongFocus);
694 widget->setWidget(newView);
695 widget->setWidgetResizable(true);
696 widget->setWindowTitle(message->envelope(model).subject);
697 widget->setAttribute(Qt::WA_DeleteOnClose);
698 widget->resize(800, 600);
699 widget->show();
702 void MainWindow::slotResizeMsgListColumns()
704 for (int i = 0; i < msgListWidget->tree->header()->count(); ++i)
705 msgListWidget->tree->resizeColumnToContents(i);
708 void MainWindow::showContextMenuMboxTree(const QPoint &position)
710 QList<QAction *> actionList;
711 if (mboxTree->indexAt(position).isValid()) {
712 actionList.append(createChildMailbox);
713 actionList.append(deleteCurrentMailbox);
714 actionList.append(resyncMbox);
715 actionList.append(reloadMboxList);
717 actionList.append(m_actionSubscribeMailbox);
718 m_actionSubscribeMailbox->setChecked(mboxTree->indexAt(position).data(Imap::Mailbox::RoleMailboxIsSubscribed).toBool());
720 #ifdef XTUPLE_CONNECT
721 actionList.append(xtIncludeMailboxInSync);
722 xtIncludeMailboxInSync->setChecked(
723 QSettings().value(Common::SettingsNames::xtSyncMailboxList).toStringList().contains(
724 mboxTree->indexAt(position).data(Imap::Mailbox::RoleMailboxName).toString()));
725 #endif
726 } else {
727 actionList.append(createTopMailbox);
729 actionList.append(reloadAllMailboxes);
730 actionList.append(m_actionShowOnlySubscribed);
731 QMenu::exec(actionList, mboxTree->mapToGlobal(position));
734 void MainWindow::showContextMenuMsgListTree(const QPoint &position)
736 QList<QAction *> actionList;
737 QModelIndex index = msgListWidget->tree->indexAt(position);
738 if (index.isValid()) {
739 updateMessageFlags(index);
740 actionList.append(markAsRead);
741 actionList.append(markAsDeleted);
742 actionList.append(saveWholeMessage);
743 actionList.append(viewMsgHeaders);
745 if (! actionList.isEmpty())
746 QMenu::exec(actionList, msgListWidget->tree->mapToGlobal(position));
749 /** @short Ask for an updated list of mailboxes situated below the selected one
752 void MainWindow::slotReloadMboxList()
754 Q_FOREACH(const QModelIndex &item, mboxTree->selectionModel()->selectedIndexes()) {
755 Q_ASSERT(item.isValid());
756 if (item.column() != 0)
757 continue;
758 Imap::Mailbox::TreeItemMailbox *mbox = dynamic_cast<Imap::Mailbox::TreeItemMailbox *>(
759 Imap::Mailbox::Model::realTreeItem(item)
761 Q_ASSERT(mbox);
762 mbox->rescanForChildMailboxes(model);
766 /** @short Request a check for new messages in selected mailbox */
767 void MainWindow::slotResyncMbox()
769 if (! model->isNetworkAvailable())
770 return;
772 Q_FOREACH(const QModelIndex &item, mboxTree->selectionModel()->selectedIndexes()) {
773 Q_ASSERT(item.isValid());
774 if (item.column() != 0)
775 continue;
776 Imap::Mailbox::TreeItemMailbox *mbox = dynamic_cast<Imap::Mailbox::TreeItemMailbox *>(
777 Imap::Mailbox::Model::realTreeItem(item)
779 Q_ASSERT(mbox);
780 model->resyncMailbox(item);
784 void MainWindow::alertReceived(const QString &message)
786 QMessageBox::warning(this, tr("IMAP Alert"), message);
789 void MainWindow::connectionError(const QString &message)
791 if (QSettings().contains(Common::SettingsNames::imapMethodKey)) {
792 QMessageBox::critical(this, tr("Connection Error"), message);
793 // Show the IMAP logger -- maybe some user will take that as a hint that they shall include it in the bug report.
794 // </joke>
795 showImapLogger->setChecked(true);
796 } else {
797 // hack: this slot is called even on the first run with no configuration
798 // We shouldn't have to worry about that, since the dialog is already scheduled for calling
799 // -> do nothing
803 void MainWindow::cacheError(const QString &message)
805 QMessageBox::critical(this, tr("IMAP Cache Error"),
806 tr("The caching subsystem managing a cache of the data already "
807 "downloaded from the IMAP server is having troubles. "
808 "All caching will be disabled.\n\n%1").arg(message));
809 if (model)
810 model->setCache(new Imap::Mailbox::MemoryCache(model, QString()));
813 void MainWindow::networkPolicyOffline()
815 netOffline->setChecked(true);
816 netExpensive->setChecked(false);
817 netOnline->setChecked(false);
818 updateActionsOnlineOffline(false);
819 networkIndicator->setDefaultAction(netOffline);
820 statusBar()->showMessage(tr("Offline"), 0);
823 void MainWindow::networkPolicyExpensive()
825 netOffline->setChecked(false);
826 netExpensive->setChecked(true);
827 netOnline->setChecked(false);
828 updateActionsOnlineOffline(true);
829 networkIndicator->setDefaultAction(netExpensive);
832 void MainWindow::networkPolicyOnline()
834 netOffline->setChecked(false);
835 netExpensive->setChecked(false);
836 netOnline->setChecked(true);
837 updateActionsOnlineOffline(true);
838 networkIndicator->setDefaultAction(netOnline);
841 void MainWindow::slotShowSettings()
843 SettingsDialog *dialog = new SettingsDialog(this, m_senderIdentities);
844 if (dialog->exec() == QDialog::Accepted) {
845 // FIXME: wipe cache in case we're moving between servers
846 nukeModels();
847 setupModels();
848 connectModelActions();
852 void MainWindow::authenticationRequested()
854 QSettings s;
855 QString user = s.value(Common::SettingsNames::imapUserKey).toString();
856 QString pass = s.value(Common::SettingsNames::imapPassKey).toString();
857 if (m_ignoreStoredPassword || pass.isEmpty()) {
858 bool ok;
859 pass = QInputDialog::getText(this, tr("Password"),
860 tr("Please provide password for %1").arg(user),
861 QLineEdit::Password, QString::null, &ok);
862 if (ok) {
863 model->setImapUser(user);
864 model->setImapPassword(pass);
865 } else {
866 model->unsetImapPassword();
868 } else {
869 model->setImapUser(user);
870 model->setImapPassword(pass);
874 void MainWindow::authenticationFailed(const QString &message)
876 m_ignoreStoredPassword = true;
877 QMessageBox::warning(this, tr("Login Failed"), message);
880 void MainWindow::sslErrors(const QList<QSslCertificate> &certificateChain, const QList<QSslError> &errors)
882 QSettings s;
883 QByteArray lastKnownCertPem = s.value(Common::SettingsNames::imapSslPemCertificate).toByteArray();
884 QList<QSslCertificate> lastKnownCerts = lastKnownCertPem.isEmpty() ?
885 QList<QSslCertificate>() :
886 QSslCertificate::fromData(lastKnownCertPem, QSsl::Pem);
887 if (!certificateChain.isEmpty()) {
888 if (!lastKnownCerts.isEmpty()) {
889 if (certificateChain == lastKnownCerts) {
890 // It's the same certificate as the last time; we should accept that
891 model->setSslPolicy(certificateChain, errors, true);
892 return;
897 QString message;
898 QString title;
899 Imap::Mailbox::CertificateUtils::IconType icon;
901 Imap::Mailbox::CertificateUtils::formatSslState(certificateChain, lastKnownCerts, lastKnownCertPem, errors,
902 &title, &message, &icon);
904 if (QMessageBox(static_cast<QMessageBox::Icon>(icon), title, message, QMessageBox::Yes | QMessageBox::No, this).exec() == QMessageBox::Yes) {
905 if (!certificateChain.isEmpty()) {
906 QByteArray buf;
907 Q_FOREACH(const QSslCertificate &cert, certificateChain) {
908 buf.append(cert.toPem());
910 s.setValue(Common::SettingsNames::imapSslPemCertificate, buf);
912 #ifdef XTUPLE_CONNECT
913 QSettings xtSettings(QSettings::UserScope, QString::fromAscii("xTuple.com"), QString::fromAscii("xTuple"));
914 xtSettings.setValue(Common::SettingsNames::imapSslPemCertificate, buf);
915 #endif
917 model->setSslPolicy(certificateChain, errors, true);
918 } else {
919 model->setSslPolicy(certificateChain, errors, false);
923 void MainWindow::nukeModels()
925 msgView->setEmpty();
926 mboxTree->setModel(0);
927 msgListWidget->tree->setModel(0);
928 allTree->setModel(0);
929 taskTree->setModel(0);
930 prettyMsgListModel->deleteLater();
931 prettyMsgListModel = 0;
932 threadingMsgListModel->deleteLater();
933 threadingMsgListModel = 0;
934 msgListModel->deleteLater();
935 msgListModel = 0;
936 mboxModel->deleteLater();
937 mboxModel = 0;
938 prettyMboxModel->deleteLater();
939 prettyMboxModel = 0;
940 model->deleteLater();
941 model = 0;
944 void MainWindow::slotComposeMail()
946 invokeComposeDialog();
949 void MainWindow::handleMarkAsRead(bool value)
951 QModelIndexList translatedIndexes;
952 Q_FOREACH(const QModelIndex &item, msgListWidget->tree->selectionModel()->selectedIndexes()) {
953 Q_ASSERT(item.isValid());
954 if (item.column() != 0)
955 continue;
956 if (!item.data(Imap::Mailbox::RoleMessageUid).isValid())
957 continue;
958 QModelIndex translated;
959 Imap::Mailbox::Model::realTreeItem(item, 0, &translated);
960 translatedIndexes << translated;
962 if (translatedIndexes.isEmpty()) {
963 qDebug() << "Model::handleMarkAsRead: no valid messages";
964 } else {
965 if (value)
966 model->markMessagesRead(translatedIndexes, Imap::Mailbox::FLAG_ADD);
967 else
968 model->markMessagesRead(translatedIndexes, Imap::Mailbox::FLAG_REMOVE);
972 void MainWindow::slotNextUnread()
974 QModelIndex current = msgListWidget->tree->currentIndex();
976 bool wrapped = false;
977 while (current.isValid()) {
978 if (!current.data(Imap::Mailbox::RoleMessageIsMarkedRead).toBool() && msgListWidget->tree->currentIndex() != current) {
979 msgView->setMessage(current);
980 msgListWidget->tree->setCurrentIndex(current);
981 return;
984 QModelIndex child = current.child(0, 0);
985 if (child.isValid()) {
986 current = child;
987 continue;
990 QModelIndex sibling = current.sibling(current.row() + 1, 0);
991 if (sibling.isValid()) {
992 current = sibling;
993 continue;
996 while (current.isValid() && msgListWidget->tree->model()->rowCount(current.parent()) - 1 == current.row()) {
997 current = current.parent();
999 current = current.sibling(current.row() + 1, 0);
1001 if (!current.isValid() && !wrapped) {
1002 wrapped = true;
1003 current = msgListWidget->tree->model()->index(0, 0);
1008 void MainWindow::slotPreviousUnread()
1010 QModelIndex current = msgListWidget->tree->currentIndex();
1012 bool wrapped = false;
1013 while (current.isValid()) {
1014 if (!current.data(Imap::Mailbox::RoleMessageIsMarkedRead).toBool() && msgListWidget->tree->currentIndex() != current) {
1015 msgView->setMessage(current);
1016 msgListWidget->tree->setCurrentIndex(current);
1017 return;
1020 QModelIndex candidate = current.sibling(current.row() - 1, 0);
1021 while (candidate.isValid() && current.model()->hasChildren(candidate)) {
1022 candidate = candidate.child(current.model()->rowCount(candidate) - 1, 0);
1023 Q_ASSERT(candidate.isValid());
1026 if (candidate.isValid()) {
1027 current = candidate;
1028 } else {
1029 current = current.parent();
1031 if (!current.isValid() && !wrapped) {
1032 wrapped = true;
1033 while (msgListWidget->tree->model()->hasChildren(current)) {
1034 current = msgListWidget->tree->model()->index(msgListWidget->tree->model()->rowCount(current) - 1, 0, current);
1040 void MainWindow::handleMarkAsDeleted(bool value)
1042 QModelIndexList translatedIndexes;
1043 Q_FOREACH(const QModelIndex &item, msgListWidget->tree->selectionModel()->selectedIndexes()) {
1044 Q_ASSERT(item.isValid());
1045 if (item.column() != 0)
1046 continue;
1047 if (!item.data(Imap::Mailbox::RoleMessageUid).isValid())
1048 continue;
1049 QModelIndex translated;
1050 Imap::Mailbox::Model::realTreeItem(item, 0, &translated);
1051 translatedIndexes << translated;
1053 if (translatedIndexes.isEmpty()) {
1054 qDebug() << "Model::handleMarkAsDeleted: no valid messages";
1055 } else {
1056 if (value)
1057 model->markMessagesDeleted(translatedIndexes, Imap::Mailbox::FLAG_ADD);
1058 else
1059 model->markMessagesDeleted(translatedIndexes, Imap::Mailbox::FLAG_REMOVE);
1063 void MainWindow::slotExpunge()
1065 model->expungeMailbox(msgListModel->currentMailbox());
1068 void MainWindow::slotCreateMailboxBelowCurrent()
1070 createMailboxBelow(mboxTree->currentIndex());
1073 void MainWindow::slotCreateTopMailbox()
1075 createMailboxBelow(QModelIndex());
1078 void MainWindow::createMailboxBelow(const QModelIndex &index)
1080 Imap::Mailbox::TreeItemMailbox *mboxPtr = index.isValid() ?
1081 dynamic_cast<Imap::Mailbox::TreeItemMailbox *>(
1082 Imap::Mailbox::Model::realTreeItem(index)) :
1085 Ui::CreateMailboxDialog ui;
1086 QDialog *dialog = new QDialog(this);
1087 ui.setupUi(dialog);
1089 dialog->setWindowTitle(mboxPtr ?
1090 tr("Create a Subfolder of %1").arg(mboxPtr->mailbox()) :
1091 tr("Create a Top-level Mailbox"));
1093 if (dialog->exec() == QDialog::Accepted) {
1094 QStringList parts;
1095 if (mboxPtr)
1096 parts << mboxPtr->mailbox();
1097 parts << ui.mailboxName->text();
1098 if (ui.otherMailboxes->isChecked())
1099 parts << QString();
1100 QString targetName = parts.join(mboxPtr ? mboxPtr->separator() : QString()); // FIXME: top-level separator
1101 model->createMailbox(targetName);
1105 void MainWindow::slotDeleteCurrentMailbox()
1107 if (! mboxTree->currentIndex().isValid())
1108 return;
1110 Imap::Mailbox::TreeItemMailbox *mailbox = dynamic_cast<Imap::Mailbox::TreeItemMailbox *>(
1111 Imap::Mailbox::Model::realTreeItem(mboxTree->currentIndex()));
1112 Q_ASSERT(mailbox);
1114 if (QMessageBox::question(this, tr("Delete Mailbox"),
1115 tr("Are you sure to delete mailbox %1?").arg(mailbox->mailbox()),
1116 QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
1117 model->deleteMailbox(mailbox->mailbox());
1121 void MainWindow::updateMessageFlags()
1123 updateMessageFlags(msgListWidget->tree->currentIndex());
1126 void MainWindow::updateMessageFlags(const QModelIndex &index)
1128 markAsRead->setChecked(index.data(Imap::Mailbox::RoleMessageIsMarkedRead).toBool());
1129 markAsDeleted->setChecked(index.data(Imap::Mailbox::RoleMessageIsMarkedDeleted).toBool());
1132 void MainWindow::updateActionsOnlineOffline(bool online)
1134 reloadMboxList->setEnabled(online);
1135 resyncMbox->setEnabled(online);
1136 expunge->setEnabled(online);
1137 createChildMailbox->setEnabled(online);
1138 createTopMailbox->setEnabled(online);
1139 deleteCurrentMailbox->setEnabled(online);
1140 markAsDeleted->setEnabled(online);
1141 markAsRead->setEnabled(online);
1142 showImapCapabilities->setEnabled(online);
1145 void MainWindow::scrollMessageUp()
1147 area->ensureVisible(0, 0, 0, 0);
1150 void MainWindow::slotReplyTo()
1152 msgView->reply(this, MessageView::REPLY_SENDER_ONLY);
1155 void MainWindow::slotReplyAll()
1157 msgView->reply(this, MessageView::REPLY_ALL);
1160 void MainWindow::slotComposeMailUrl(const QUrl &url)
1162 Q_ASSERT(url.scheme().toLower() == QLatin1String("mailto"));
1164 RecipientsType recipients;
1165 // FIXME: handle the display name as well, Redmine #534
1166 recipients << qMakePair<ComposeWidget::RecipientKind,QString>(Imap::Mailbox::MessageComposer::Recipient_To, url.path());
1167 invokeComposeDialog(QString(), QString(), recipients);
1170 void MainWindow::invokeComposeDialog(const QString &subject, const QString &body,
1171 const RecipientsType &recipients, const QList<QByteArray> &inReplyTo,
1172 const QList<QByteArray> &references)
1174 QSettings s;
1175 ComposeWidget *w = new ComposeWidget(this);
1176 w->setData(recipients, subject, body, inReplyTo, references);
1177 w->setAttribute(Qt::WA_DeleteOnClose, true);
1178 Util::centerWidgetOnScreen(w);
1179 w->show();
1182 void MainWindow::slotMailboxDeleteFailed(const QString &mailbox, const QString &msg)
1184 QMessageBox::warning(this, tr("Can't delete mailbox"),
1185 tr("Deleting mailbox \"%1\" failed with the following message:\n%2").arg(mailbox, msg));
1188 void MainWindow::slotMailboxCreateFailed(const QString &mailbox, const QString &msg)
1190 QMessageBox::warning(this, tr("Can't create mailbox"),
1191 tr("Creating mailbox \"%1\" failed with the following message:\n%2").arg(mailbox, msg));
1194 void MainWindow::showConnectionStatus(QObject *parser, Imap::ConnectionState state)
1196 Q_UNUSED(parser);
1197 using namespace Imap;
1198 QString message = connectionStateToString(state);
1199 enum { DURATION = 10000 };
1200 bool transient = false;
1202 switch (state) {
1203 case CONN_STATE_AUTHENTICATED:
1204 case CONN_STATE_SELECTED:
1205 transient = true;
1206 break;
1207 default:
1208 // only the stuff above is transient
1209 break;
1211 statusBar()->showMessage(message, transient ? DURATION : 0);
1214 void MainWindow::slotShowLinkTarget(const QString &link)
1216 statusBar()->showMessage(tr("Link target: %1").arg(link));
1219 void MainWindow::slotShowAboutTrojita()
1221 QMessageBox::about(this, trUtf8("About Trojitá"),
1222 trUtf8("<p>This is <b>Trojitá</b>, a fast Qt IMAP e-mail client</p>"
1223 "<p>Copyright &copy; 2006-2012 Jan Kundrát &lt;jkt@flaska.net&gt; and "
1224 "<a href=\"http://quickgit.kde.org/?p=trojita.git&amp;a=blob&amp;f=LICENSE\">others</a></p>"
1225 "<p>More information at <a href=\"http://trojita.flaska.net/\">http://trojita.flaska.net/</a></p>"
1226 "<p>You are using version %1.</p>").arg(
1227 QApplication::applicationVersion()));
1230 void MainWindow::slotDonateToTrojita()
1232 QDesktopServices::openUrl(QString(QLatin1String("http://sourceforge.net/donate/index.php?group_id=339456")));
1235 void MainWindow::slotSaveCurrentMessageBody()
1237 Q_FOREACH(const QModelIndex &item, msgListWidget->tree->selectionModel()->selectedIndexes()) {
1238 Q_ASSERT(item.isValid());
1239 if (item.column() != 0)
1240 continue;
1241 if (! item.data(Imap::Mailbox::RoleMessageUid).isValid())
1242 continue;
1243 QModelIndex messageIndex;
1244 Imap::Mailbox::TreeItemMessage *message = dynamic_cast<Imap::Mailbox::TreeItemMessage *>(
1245 Imap::Mailbox::Model::realTreeItem(item, 0, &messageIndex)
1247 Q_ASSERT(message);
1249 Imap::Network::MsgPartNetAccessManager *netAccess = new Imap::Network::MsgPartNetAccessManager(this);
1250 netAccess->setModelMessage(message->toIndex(model));
1251 Imap::Network::FileDownloadManager *fileDownloadManager =
1252 new Imap::Network::FileDownloadManager(this, netAccess, messageIndex.child(0, Imap::Mailbox::TreeItem::OFFSET_HEADER));
1253 // FIXME: change from "header" into "whole message"
1254 connect(fileDownloadManager, SIGNAL(succeeded()), fileDownloadManager, SLOT(deleteLater()));
1255 connect(fileDownloadManager, SIGNAL(transferError(QString)), fileDownloadManager, SLOT(deleteLater()));
1256 connect(fileDownloadManager, SIGNAL(fileNameRequested(QString *)),
1257 this, SLOT(slotDownloadMessageFileNameRequested(QString *)));
1258 connect(fileDownloadManager, SIGNAL(transferError(QString)),
1259 this, SLOT(slotDownloadMessageTransferError(QString)));
1260 connect(fileDownloadManager, SIGNAL(destroyed()), netAccess, SLOT(deleteLater()));
1261 fileDownloadManager->slotDownloadNow();
1265 void MainWindow::slotDownloadMessageTransferError(const QString &errorString)
1267 QMessageBox::critical(this, tr("Can't save message"),
1268 tr("Unable to save the attachment. Error:\n%1").arg(errorString));
1271 void MainWindow::slotDownloadMessageFileNameRequested(QString *fileName)
1273 *fileName = QFileDialog::getSaveFileName(this, tr("Save Message"),
1274 *fileName, QString(),
1275 0, QFileDialog::HideNameFilterDetails
1279 void MainWindow::slotViewMsgHeaders()
1281 Q_FOREACH(const QModelIndex &item, msgListWidget->tree->selectionModel()->selectedIndexes()) {
1282 Q_ASSERT(item.isValid());
1283 if (item.column() != 0)
1284 continue;
1285 if (! item.data(Imap::Mailbox::RoleMessageUid).isValid())
1286 continue;
1287 QModelIndex messageIndex;
1288 Imap::Mailbox::Model::realTreeItem(item, 0, &messageIndex);
1290 Imap::Network::MsgPartNetAccessManager *netAccess = new Imap::Network::MsgPartNetAccessManager(this);
1291 netAccess->setModelMessage(messageIndex);
1293 SimplePartWidget *headers = new SimplePartWidget(0, netAccess,
1294 messageIndex.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_HEADER, messageIndex));
1295 headers->setAttribute(Qt::WA_DeleteOnClose);
1296 connect(headers, SIGNAL(destroyed()), netAccess, SLOT(deleteLater()));
1297 QAction *close = new QAction(loadIcon(QLatin1String("window-close")), tr("Close"), headers);
1298 headers->addAction(close);
1299 close->setShortcut(tr("Ctrl+W"));
1300 connect(close, SIGNAL(triggered()), headers, SLOT(close()));
1301 headers->setWindowTitle(tr("Message headers of UID %1 in %2").arg(
1302 QString::number(messageIndex.data(Imap::Mailbox::RoleMessageUid).toUInt()),
1303 messageIndex.parent().parent().data(Imap::Mailbox::RoleMailboxName).toString()
1305 headers->show();
1309 #ifdef XTUPLE_CONNECT
1310 void MainWindow::slotXtSyncCurrentMailbox()
1312 QModelIndex index = mboxTree->currentIndex();
1313 if (! index.isValid())
1314 return;
1316 QString mailbox = index.data(Imap::Mailbox::RoleMailboxName).toString();
1317 QSettings s;
1318 QStringList mailboxes = s.value(Common::SettingsNames::xtSyncMailboxList).toStringList();
1319 if (xtIncludeMailboxInSync->isChecked()) {
1320 if (! mailboxes.contains(mailbox)) {
1321 mailboxes.append(mailbox);
1323 } else {
1324 mailboxes.removeAll(mailbox);
1326 s.setValue(Common::SettingsNames::xtSyncMailboxList, mailboxes);
1327 QSettings(QSettings::UserScope, QString::fromAscii("xTuple.com"), QString::fromAscii("xTuple")).setValue(Common::SettingsNames::xtSyncMailboxList, mailboxes);
1328 prettyMboxModel->xtConnectStatusChanged(index);
1330 #endif
1332 void MainWindow::slotSubscribeCurrentMailbox()
1334 QModelIndex index = mboxTree->currentIndex();
1335 if (! index.isValid())
1336 return;
1338 QString mailbox = index.data(Imap::Mailbox::RoleMailboxName).toString();
1339 if (m_actionSubscribeMailbox->isChecked()) {
1340 model->subscribeMailbox(mailbox);
1341 } else {
1342 model->unsubscribeMailbox(mailbox);
1346 void MainWindow::slotShowOnlySubscribed()
1348 QSettings().setValue(Common::SettingsNames::guiMailboxListShowOnlySubscribed, m_actionShowOnlySubscribed->isChecked());
1349 if (m_actionShowOnlySubscribed->isEnabled()) {
1350 prettyMboxModel->setShowOnlySubscribed(m_actionShowOnlySubscribed->isChecked());
1354 void MainWindow::slotScrollToUnseenMessage(const QModelIndex &mailbox, const QModelIndex &message)
1356 // Now this is much, much more reliable than messing around with finding out an "interesting message"...
1357 Q_UNUSED(mailbox);
1358 Q_UNUSED(message);
1359 if (!m_actionSortNone->isChecked() && !m_actionSortThreading->isChecked()) {
1360 // we're using some funky sorting, better don't scroll anywhere
1362 if (m_actionSortDescending->isChecked()) {
1363 msgListWidget->tree->scrollToTop();
1364 } else {
1365 msgListWidget->tree->scrollToBottom();
1369 void MainWindow::slotThreadMsgList()
1371 // We want to save user's preferences and not override them with "threading disabled" when the server
1372 // doesn't report them, like in initial greetings. That's why we have to check for isEnabled() here.
1373 const bool useThreading = actionThreadMsgList->isChecked();
1375 // Switching betweeb threaded/unthreaded view shall resert the sorting criteria. The goal is to make
1376 // sorting rather seldomly used as people shall instead use proper threading.
1377 if (useThreading) {
1378 m_actionSortThreading->setEnabled(true);
1379 if (!m_actionSortThreading->isChecked())
1380 m_actionSortThreading->trigger();
1381 m_actionSortNone->setEnabled(false);
1382 } else {
1383 m_actionSortNone->setEnabled(true);
1384 if (!m_actionSortNone->isChecked())
1385 m_actionSortNone->trigger();
1386 m_actionSortThreading->setEnabled(false);
1389 QPersistentModelIndex currentItem = msgListWidget->tree->currentIndex();
1391 if (useThreading && actionThreadMsgList->isEnabled()) {
1392 msgListWidget->tree->setRootIsDecorated(true);
1393 threadingMsgListModel->setUserWantsThreading(true);
1394 } else {
1395 msgListWidget->tree->setRootIsDecorated(false);
1396 threadingMsgListModel->setUserWantsThreading(false);
1398 QSettings().setValue(Common::SettingsNames::guiMsgListShowThreading, QVariant(useThreading));
1400 if (currentItem.isValid()) {
1401 msgListWidget->tree->scrollTo(currentItem);
1402 } else {
1403 // If we cannot determine current item, at least scroll to a predictable place. Without this, the view
1404 // would jump to "weird" places, probably due to some heuristics about trying to show "roughly the same"
1405 // objects as what was visible before the reshuffling.
1406 msgListWidget->tree->scrollToBottom();
1410 void MainWindow::slotSortingPreferenceChanged()
1412 Qt::SortOrder order = m_actionSortDescending->isChecked() ? Qt::DescendingOrder : Qt::AscendingOrder;
1414 using namespace Imap::Mailbox;
1416 int column = -1;
1417 if (m_actionSortByArrival->isChecked()) {
1418 column = MsgListModel::RECEIVED_DATE;
1419 } else if (m_actionSortByCc->isChecked()) {
1420 column = MsgListModel::CC;
1421 } else if (m_actionSortByDate->isChecked()) {
1422 column = MsgListModel::DATE;
1423 } else if (m_actionSortByFrom->isChecked()) {
1424 column = MsgListModel::FROM;
1425 } else if (m_actionSortBySize->isChecked()) {
1426 column = MsgListModel::SIZE;
1427 } else if (m_actionSortBySubject->isChecked()) {
1428 column = MsgListModel::SUBJECT;
1429 } else if (m_actionSortByTo->isChecked()) {
1430 column = MsgListModel::TO;
1431 } else {
1432 column = -1;
1435 msgListWidget->tree->header()->setSortIndicator(column, order);
1438 void MainWindow::slotSortingConfirmed(int column, Qt::SortOrder order)
1440 // don't do anything during initialization
1441 if (!m_actionSortNone)
1442 return;
1444 using namespace Imap::Mailbox;
1445 QAction *action;
1447 switch (column) {
1448 case MsgListModel::SEEN:
1449 case MsgListModel::COLUMN_COUNT:
1450 case MsgListModel::BCC:
1451 case -1:
1452 if (actionThreadMsgList->isChecked())
1453 action = m_actionSortThreading;
1454 else
1455 action = m_actionSortNone;
1456 break;
1457 case MsgListModel::SUBJECT:
1458 action = m_actionSortBySubject;
1459 break;
1460 case MsgListModel::FROM:
1461 action = m_actionSortByFrom;
1462 break;
1463 case MsgListModel::TO:
1464 action = m_actionSortByTo;
1465 break;
1466 case MsgListModel::CC:
1467 action = m_actionSortByCc;
1468 break;
1469 case MsgListModel::DATE:
1470 action = m_actionSortByDate;
1471 break;
1472 case MsgListModel::RECEIVED_DATE:
1473 action = m_actionSortByArrival;
1474 break;
1475 case MsgListModel::SIZE:
1476 action = m_actionSortBySize;
1477 break;
1478 default:
1479 action = m_actionSortNone;
1482 action->setChecked(true);
1483 if (order == Qt::DescendingOrder)
1484 m_actionSortDescending->setChecked(true);
1485 else
1486 m_actionSortAscending->setChecked(true);
1489 void MainWindow::slotSearchRequested(const QStringList &searchConditions)
1491 if (!searchConditions.isEmpty() && actionThreadMsgList->isChecked()) {
1492 // right now, searching and threading doesn't play well together at all
1493 actionThreadMsgList->trigger();
1495 threadingMsgListModel->setUserSearchingSortingPreference(searchConditions, threadingMsgListModel->currentSortCriterium(),
1496 threadingMsgListModel->currentSortOrder());
1499 void MainWindow::slotHideRead()
1501 const bool hideRead = actionHideRead->isChecked();
1502 prettyMsgListModel->setHideRead(hideRead);
1503 QSettings().setValue(Common::SettingsNames::guiMsgListHideRead, QVariant(hideRead));
1506 void MainWindow::slotCapabilitiesUpdated(const QStringList &capabilities)
1508 if (capabilities.contains(QLatin1String("SORT"))) {
1509 m_actionSortByDate->actionGroup()->setEnabled(true);
1510 } else {
1511 m_actionSortByDate->actionGroup()->setEnabled(false);
1514 msgListWidget->setFuzzySearchSupported(capabilities.contains(QLatin1String("SEARCH=FUZZY")));
1516 m_actionShowOnlySubscribed->setEnabled(capabilities.contains(QLatin1String("LIST-EXTENDED")));
1517 m_actionShowOnlySubscribed->setChecked(m_actionShowOnlySubscribed->isEnabled() &&
1518 QSettings().value(
1519 Common::SettingsNames::guiMailboxListShowOnlySubscribed, false).toBool());
1520 m_actionSubscribeMailbox->setEnabled(m_actionShowOnlySubscribed->isEnabled());
1522 m_supportsCatenate = capabilities.contains(QLatin1String("CATENATE"));
1523 m_supportsGenUrlAuth = capabilities.contains(QLatin1String("URLAUTH"));
1524 m_supportsImapSubmission = capabilities.contains(QLatin1String("UIDPLUS")) &&
1525 capabilities.contains(QLatin1String("X-DRAFT-I01-SENDMAIL"));
1527 const QStringList supportedCapabilities = Imap::Mailbox::ThreadingMsgListModel::supportedCapabilities();
1528 Q_FOREACH(const QString &capability, capabilities) {
1529 if (supportedCapabilities.contains(capability)) {
1530 actionThreadMsgList->setEnabled(true);
1531 if (actionThreadMsgList->isChecked())
1532 slotThreadMsgList();
1533 return;
1536 actionThreadMsgList->setEnabled(false);
1539 void MainWindow::slotShowImapInfo()
1541 QString caps;
1542 Q_FOREACH(const QString &cap, model->capabilities()) {
1543 caps += tr("<li>%1</li>\n").arg(cap);
1546 QString idString;
1547 if (!model->serverId().isEmpty() && model->capabilities().contains(QLatin1String("ID"))) {
1548 QMap<QByteArray,QByteArray> serverId = model->serverId();
1550 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
1551 #define IMAP_ID_FIELD(Var, Name) bool has_##Var = serverId.contains(Name); \
1552 QString Var = has_##Var ? QString::fromUtf8(serverId[Name]).toHtmlEscaped() : tr("Unknown");
1553 #else
1554 #define IMAP_ID_FIELD(Var, Name) bool has_##Var = serverId.contains(Name); \
1555 QString Var = has_##Var ? Qt::escape(QString::fromAscii(serverId[Name])) : tr("Unknown");
1556 #endif
1557 IMAP_ID_FIELD(serverName, "name");
1558 IMAP_ID_FIELD(serverVersion, "version");
1559 IMAP_ID_FIELD(os, "os");
1560 IMAP_ID_FIELD(osVersion, "os-version");
1561 IMAP_ID_FIELD(vendor, "vendor");
1562 IMAP_ID_FIELD(supportUrl, "support-url");
1563 IMAP_ID_FIELD(address, "address");
1564 IMAP_ID_FIELD(date, "date");
1565 IMAP_ID_FIELD(command, "command");
1566 IMAP_ID_FIELD(arguments, "arguments");
1567 IMAP_ID_FIELD(environment, "environment");
1568 #undef IMAP_ID_FIELD
1569 if (has_serverName) {
1570 idString = tr("<p>");
1571 if (has_serverVersion)
1572 idString += tr("Server: %1 %2").arg(serverName, serverVersion);
1573 else
1574 idString += tr("Server: %1").arg(serverName);
1576 if (has_vendor) {
1577 idString += tr(" (%1)").arg(vendor);
1579 if (has_os) {
1580 if (has_osVersion)
1581 idString += tr(" on %1 %2").arg(os, osVersion);
1582 else
1583 idString += tr(" on %1").arg(os);
1585 idString += tr("</p>");
1586 } else {
1587 idString = tr("<p>The IMAP server did not return usable information about itself.</p>");
1589 QString fullId;
1590 for (QMap<QByteArray,QByteArray>::const_iterator it = serverId.constBegin(); it != serverId.constEnd(); ++it) {
1591 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
1592 fullId += tr("<li>%1: %2</li>").arg(QString::fromUtf8(it.key()).toHtmlEscaped(), QString::fromUtf8(it.value()).toHtmlEscaped());
1593 #else
1594 fullId += tr("<li>%1: %2</li>").arg(Qt::escape(QString::fromAscii(it.key())), Qt::escape(QString::fromAscii(it.value())));
1595 #endif
1597 idString += tr("<ul>%1</ul>").arg(fullId);
1598 } else {
1599 idString = tr("<p>The server has not provided information about its software version.</p>");
1602 QMessageBox::information(this, tr("IMAP Server Information"),
1603 tr("%1"
1604 "<p>The following capabilities are currently advertised:</p>\n"
1605 "<ul>\n%2</ul>").arg(idString, caps));
1608 QSize MainWindow::sizeHint() const
1610 return QSize(1150, 980);
1613 void MainWindow::slotUpdateWindowTitle()
1615 QModelIndex mailbox = msgListModel->currentMailbox();
1616 if (mailbox.isValid()) {
1617 if (mailbox.data(Imap::Mailbox::RoleUnreadMessageCount).toInt()) {
1618 setWindowTitle(trUtf8("%1 — %2 unread — Trojitá")
1619 .arg(mailbox.data(Imap::Mailbox::RoleShortMailboxName).toString(),
1620 mailbox.data(Imap::Mailbox::RoleUnreadMessageCount).toString()));
1621 } else {
1622 setWindowTitle(trUtf8("%1 — Trojitá").arg(mailbox.data(Imap::Mailbox::RoleShortMailboxName).toString()));
1624 } else {
1625 setWindowTitle(trUtf8("Trojitá"));
1629 void MainWindow::slotLayoutCompact()
1631 m_mainVSplitter->addWidget(area);
1632 QSettings().setValue(Common::SettingsNames::guiMainWindowLayout, Common::SettingsNames::guiMainWindowLayoutCompact);
1633 setMinimumWidth(800);
1636 void MainWindow::slotLayoutWide()
1638 m_mainHSplitter->addWidget(area);
1639 m_mainHSplitter->setStretchFactor(0, 0);
1640 m_mainHSplitter->setStretchFactor(1, 1);
1641 m_mainHSplitter->setStretchFactor(2, 1);
1642 QSettings().setValue(Common::SettingsNames::guiMainWindowLayout, Common::SettingsNames::guiMainWindowLayoutWide);
1643 setMinimumWidth(1250);
1646 Imap::Mailbox::Model *MainWindow::imapModel() const
1648 return model;
1651 bool MainWindow::isCatenateSupported() const
1653 return m_supportsCatenate;
1656 bool MainWindow::isGenUrlAuthSupported() const
1658 return m_supportsGenUrlAuth;
1661 bool MainWindow::isImapSubmissionSupported() const
1663 return m_supportsImapSubmission;
1666 /** @short Deal with various obsolete settings */
1667 void MainWindow::migrateSettings()
1669 using Common::SettingsNames;
1670 QSettings s;
1672 // Process the obsolete settings about the "cache backend". This has been changed to "offline stuff" after v0.3.
1673 if (s.value(SettingsNames::cacheMetadataKey).toString() == SettingsNames::cacheMetadataMemory) {
1674 s.setValue(SettingsNames::cacheOfflineKey, SettingsNames::cacheOfflineNone);
1675 s.remove(SettingsNames::cacheMetadataKey);
1677 // Also remove the older values used for cache lifetime management which were not used, but set to zero by default
1678 s.remove(QLatin1String("offline.sync"));
1679 s.remove(QLatin1String("offline.sync.days"));
1680 s.remove(QLatin1String("offline.sync.messages"));