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>
29 #include <QDockWidget>
30 #include <QFileDialog>
31 #include <QHeaderView>
32 #include <QInputDialog>
33 #include <QItemSelectionModel>
35 #include <QMessageBox>
36 #include <QProgressBar>
40 #include <QTextDocument>
42 #include <QToolButton>
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"
75 #include "ui_CreateMailboxDialog.h"
77 #include "Imap/Model/ModelTest/modeltest.h"
79 /** @short All user-facing widgets and related classes */
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>");
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()));
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);
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);
134 netExpensive
= new QAction(loadIcon(QLatin1String("network-expensive")), tr("Expensive Connection"), netPolicyGroup
);
135 netExpensive
->setCheckable(true);
137 netOnline
= new QAction(loadIcon(QLatin1String("network-online")), tr("Free Access"), netPolicyGroup
);
138 netOnline
->setCheckable(true);
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()));
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);
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
);
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
);
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());
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()));
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
);
507 QString cacheDir
= QDesktopServices::storageLocation(QDesktopServices::CacheLocation
);
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());
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());
534 if (s
.value(SettingsNames::cacheOfflineKey
).toString() == SettingsNames::cacheOfflineAll
) {
535 cache
->setRenewalThreshold(0);
538 int num
= s
.value(SettingsNames::cacheOfflineNumberDaysKey
, 30).toInt(&ok
);
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())
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
)
646 if (! index
.data(Imap::Mailbox::RoleMessageUid
).isValid())
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
)
662 if (! index
.data(Imap::Mailbox::RoleMessageUid
).isValid())
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())
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())
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
));
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);
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()));
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)
758 Imap::Mailbox::TreeItemMailbox
*mbox
= dynamic_cast<Imap::Mailbox::TreeItemMailbox
*>(
759 Imap::Mailbox::Model::realTreeItem(item
)
762 mbox
->rescanForChildMailboxes(model
);
766 /** @short Request a check for new messages in selected mailbox */
767 void MainWindow::slotResyncMbox()
769 if (! model
->isNetworkAvailable())
772 Q_FOREACH(const QModelIndex
&item
, mboxTree
->selectionModel()->selectedIndexes()) {
773 Q_ASSERT(item
.isValid());
774 if (item
.column() != 0)
776 Imap::Mailbox::TreeItemMailbox
*mbox
= dynamic_cast<Imap::Mailbox::TreeItemMailbox
*>(
777 Imap::Mailbox::Model::realTreeItem(item
)
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.
795 showImapLogger
->setChecked(true);
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
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
));
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
848 connectModelActions();
852 void MainWindow::authenticationRequested()
855 QString user
= s
.value(Common::SettingsNames::imapUserKey
).toString();
856 QString pass
= s
.value(Common::SettingsNames::imapPassKey
).toString();
857 if (m_ignoreStoredPassword
|| pass
.isEmpty()) {
859 pass
= QInputDialog::getText(this, tr("Password"),
860 tr("Please provide password for %1").arg(user
),
861 QLineEdit::Password
, QString::null
, &ok
);
863 model
->setImapUser(user
);
864 model
->setImapPassword(pass
);
866 model
->unsetImapPassword();
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
)
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);
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()) {
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
);
917 model
->setSslPolicy(certificateChain
, errors
, true);
919 model
->setSslPolicy(certificateChain
, errors
, false);
923 void MainWindow::nukeModels()
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();
936 mboxModel
->deleteLater();
938 prettyMboxModel
->deleteLater();
940 model
->deleteLater();
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)
956 if (!item
.data(Imap::Mailbox::RoleMessageUid
).isValid())
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";
966 model
->markMessagesRead(translatedIndexes
, Imap::Mailbox::FLAG_ADD
);
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
);
984 QModelIndex child
= current
.child(0, 0);
985 if (child
.isValid()) {
990 QModelIndex sibling
= current
.sibling(current
.row() + 1, 0);
991 if (sibling
.isValid()) {
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
) {
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
);
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
;
1029 current
= current
.parent();
1031 if (!current
.isValid() && !wrapped
) {
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)
1047 if (!item
.data(Imap::Mailbox::RoleMessageUid
).isValid())
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";
1057 model
->markMessagesDeleted(translatedIndexes
, Imap::Mailbox::FLAG_ADD
);
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);
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
) {
1096 parts
<< mboxPtr
->mailbox();
1097 parts
<< ui
.mailboxName
->text();
1098 if (ui
.otherMailboxes
->isChecked())
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())
1110 Imap::Mailbox::TreeItemMailbox
*mailbox
= dynamic_cast<Imap::Mailbox::TreeItemMailbox
*>(
1111 Imap::Mailbox::Model::realTreeItem(mboxTree
->currentIndex()));
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
)
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
);
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
)
1197 using namespace Imap
;
1198 QString message
= connectionStateToString(state
);
1199 enum { DURATION
= 10000 };
1200 bool transient
= false;
1203 case CONN_STATE_AUTHENTICATED
:
1204 case CONN_STATE_SELECTED
:
1208 // only the stuff above is transient
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 © 2006-2012 Jan Kundrát <jkt@flaska.net> and "
1224 "<a href=\"http://quickgit.kde.org/?p=trojita.git&a=blob&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)
1241 if (! item
.data(Imap::Mailbox::RoleMessageUid
).isValid())
1243 QModelIndex messageIndex
;
1244 Imap::Mailbox::TreeItemMessage
*message
= dynamic_cast<Imap::Mailbox::TreeItemMessage
*>(
1245 Imap::Mailbox::Model::realTreeItem(item
, 0, &messageIndex
)
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)
1285 if (! item
.data(Imap::Mailbox::RoleMessageUid
).isValid())
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()
1309 #ifdef XTUPLE_CONNECT
1310 void MainWindow::slotXtSyncCurrentMailbox()
1312 QModelIndex index
= mboxTree
->currentIndex();
1313 if (! index
.isValid())
1316 QString mailbox
= index
.data(Imap::Mailbox::RoleMailboxName
).toString();
1318 QStringList mailboxes
= s
.value(Common::SettingsNames::xtSyncMailboxList
).toStringList();
1319 if (xtIncludeMailboxInSync
->isChecked()) {
1320 if (! mailboxes
.contains(mailbox
)) {
1321 mailboxes
.append(mailbox
);
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
);
1332 void MainWindow::slotSubscribeCurrentMailbox()
1334 QModelIndex index
= mboxTree
->currentIndex();
1335 if (! index
.isValid())
1338 QString mailbox
= index
.data(Imap::Mailbox::RoleMailboxName
).toString();
1339 if (m_actionSubscribeMailbox
->isChecked()) {
1340 model
->subscribeMailbox(mailbox
);
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"...
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();
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.
1378 m_actionSortThreading
->setEnabled(true);
1379 if (!m_actionSortThreading
->isChecked())
1380 m_actionSortThreading
->trigger();
1381 m_actionSortNone
->setEnabled(false);
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);
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
);
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
;
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
;
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
)
1444 using namespace Imap::Mailbox
;
1448 case MsgListModel::SEEN
:
1449 case MsgListModel::COLUMN_COUNT
:
1450 case MsgListModel::BCC
:
1452 if (actionThreadMsgList
->isChecked())
1453 action
= m_actionSortThreading
;
1455 action
= m_actionSortNone
;
1457 case MsgListModel::SUBJECT
:
1458 action
= m_actionSortBySubject
;
1460 case MsgListModel::FROM
:
1461 action
= m_actionSortByFrom
;
1463 case MsgListModel::TO
:
1464 action
= m_actionSortByTo
;
1466 case MsgListModel::CC
:
1467 action
= m_actionSortByCc
;
1469 case MsgListModel::DATE
:
1470 action
= m_actionSortByDate
;
1472 case MsgListModel::RECEIVED_DATE
:
1473 action
= m_actionSortByArrival
;
1475 case MsgListModel::SIZE
:
1476 action
= m_actionSortBySize
;
1479 action
= m_actionSortNone
;
1482 action
->setChecked(true);
1483 if (order
== Qt::DescendingOrder
)
1484 m_actionSortDescending
->setChecked(true);
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);
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() &&
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();
1536 actionThreadMsgList
->setEnabled(false);
1539 void MainWindow::slotShowImapInfo()
1542 Q_FOREACH(const QString
&cap
, model
->capabilities()) {
1543 caps
+= tr("<li>%1</li>\n").arg(cap
);
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");
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");
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
);
1574 idString
+= tr("Server: %1").arg(serverName
);
1577 idString
+= tr(" (%1)").arg(vendor
);
1581 idString
+= tr(" on %1 %2").arg(os
, osVersion
);
1583 idString
+= tr(" on %1").arg(os
);
1585 idString
+= tr("</p>");
1587 idString
= tr("<p>The IMAP server did not return usable information about itself.</p>");
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());
1594 fullId
+= tr("<li>%1: %2</li>").arg(Qt::escape(QString::fromAscii(it
.key())), Qt::escape(QString::fromAscii(it
.value())));
1597 idString
+= tr("<ul>%1</ul>").arg(fullId
);
1599 idString
= tr("<p>The server has not provided information about its software version.</p>");
1602 QMessageBox::information(this, tr("IMAP Server Information"),
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()));
1622 setWindowTitle(trUtf8("%1 — Trojitá").arg(mailbox
.data(Imap::Mailbox::RoleShortMailboxName
).toString()));
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
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
;
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"));