e264c6ce006db8b42a7d45fad4532c1c8b81a9cb
[quassel.git] / src / qtui / mainwin.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2010 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 #include "mainwin.h"
21
22 #ifdef HAVE_KDE
23 #  include <KAction>
24 #  include <KActionCollection>
25 #  include <KHelpMenu>
26 #  include <KMenuBar>
27 #  include <KShortcutsDialog>
28 #  include <KStatusBar>
29 #  include <KToolBar>
30 #  include <KWindowSystem>
31 #endif
32
33 #ifdef Q_WS_X11
34 #  include <QX11Info>
35 #endif
36
37 #include "aboutdlg.h"
38 #include "awaylogfilter.h"
39 #include "awaylogview.h"
40 #include "action.h"
41 #include "actioncollection.h"
42 #include "bufferhotlistfilter.h"
43 #include "buffermodel.h"
44 #include "bufferview.h"
45 #include "bufferviewoverlay.h"
46 #include "bufferviewoverlayfilter.h"
47 #include "bufferwidget.h"
48 #include "channellistdlg.h"
49 #include "chatlinemodel.h"
50 #include "chatmonitorfilter.h"
51 #include "chatmonitorview.h"
52 #include "chatview.h"
53 #include "client.h"
54 #include "clientbacklogmanager.h"
55 #include "clientbufferviewconfig.h"
56 #include "clientbufferviewmanager.h"
57 #include "clientignorelistmanager.h"
58 #include "coreconfigwizard.h"
59 #include "coreconnectdlg.h"
60 #include "coreconnection.h"
61 #include "coreconnectionstatuswidget.h"
62 #include "coreinfodlg.h"
63 #include "contextmenuactionprovider.h"
64 #include "debugbufferviewoverlay.h"
65 #include "debuglogwidget.h"
66 #include "debugmessagemodelfilter.h"
67 #include "flatproxymodel.h"
68 #include "iconloader.h"
69 #include "inputwidget.h"
70 #include "irclistmodel.h"
71 #include "ircconnectionwizard.h"
72 #include "legacysystemtray.h"
73 #include "msgprocessorstatuswidget.h"
74 #include "nicklistwidget.h"
75 #include "qtuiapplication.h"
76 #include "qtuimessageprocessor.h"
77 #include "qtuisettings.h"
78 #include "qtuistyle.h"
79 #include "settingsdlg.h"
80 #include "settingspagedlg.h"
81 #include "statusnotifieritem.h"
82 #include "toolbaractionprovider.h"
83 #include "topicwidget.h"
84 #include "verticaldock.h"
85
86 #ifndef HAVE_KDE
87 #  ifdef HAVE_PHONON
88 #    include "phononnotificationbackend.h"
89 #  endif
90 #  include "systraynotificationbackend.h"
91 #  include "taskbarnotificationbackend.h"
92 #else /* HAVE_KDE */
93 #  include "knotificationbackend.h"
94 #endif /* HAVE_KDE */
95
96 #ifdef HAVE_SSL
97 #  include "sslinfodlg.h"
98 #endif
99
100 #ifdef HAVE_INDICATEQT
101   #include "indicatornotificationbackend.h"
102 #endif
103
104 #include "settingspages/aliasessettingspage.h"
105 #include "settingspages/appearancesettingspage.h"
106 #include "settingspages/backlogsettingspage.h"
107 #include "settingspages/bufferviewsettingspage.h"
108 #include "settingspages/chatmonitorsettingspage.h"
109 #include "settingspages/chatviewsettingspage.h"
110 #include "settingspages/connectionsettingspage.h"
111 #include "settingspages/coreaccountsettingspage.h"
112 #include "settingspages/coreconnectionsettingspage.h"
113 #include "settingspages/highlightsettingspage.h"
114 #include "settingspages/identitiessettingspage.h"
115 #include "settingspages/ignorelistsettingspage.h"
116 #include "settingspages/inputwidgetsettingspage.h"
117 #include "settingspages/itemviewsettingspage.h"
118 #include "settingspages/networkssettingspage.h"
119 #include "settingspages/notificationssettingspage.h"
120 #include "settingspages/topicwidgetsettingspage.h"
121
122 #ifndef HAVE_KDE
123 #  include "settingspages/shortcutssettingspage.h"
124 #endif
125
126 MainWin::MainWin(QWidget *parent)
127 #ifdef HAVE_KDE
128   : KMainWindow(parent),
129   _kHelpMenu(new KHelpMenu(this, KGlobal::mainComponent().aboutData())),
130 #else
131   : QMainWindow(parent),
132 #endif
133     _msgProcessorStatusWidget(new MsgProcessorStatusWidget(this)),
134     _coreConnectionStatusWidget(new CoreConnectionStatusWidget(Client::coreConnection(), this)),
135     _titleSetter(this),
136     _awayLog(0),
137     _layoutLoaded(false),
138     _activeBufferViewIndex(-1)
139 {
140   setAttribute(Qt::WA_DeleteOnClose, false);  // we delete the mainwin manually
141
142   QtUiSettings uiSettings;
143   QString style = uiSettings.value("Style", QString()).toString();
144   if(!style.isEmpty()) {
145     QApplication::setStyle(style);
146   }
147
148   QApplication::setQuitOnLastWindowClosed(false);
149
150   setWindowTitle("Quassel IRC");
151   setWindowIconText("Quassel IRC");
152   updateIcon();
153 }
154
155 void MainWin::init() {
156   connect(Client::instance(), SIGNAL(networkCreated(NetworkId)), SLOT(clientNetworkCreated(NetworkId)));
157   connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), SLOT(clientNetworkRemoved(NetworkId)));
158   connect(Client::messageModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
159            SLOT(messagesInserted(const QModelIndex &, int, int)));
160   connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showChannelList(NetworkId)), SLOT(showChannelList(NetworkId)));
161   connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
162
163   connect(Client::coreConnection(), SIGNAL(startCoreSetup(QVariantList)), SLOT(showCoreConfigWizard(QVariantList)));
164   connect(Client::coreConnection(), SIGNAL(connectionErrorPopup(QString)), SLOT(handleCoreConnectionError(QString)));
165   connect(Client::coreConnection(), SIGNAL(userAuthenticationRequired(CoreAccount *, bool *, QString)), SLOT(userAuthenticationRequired(CoreAccount *, bool *, QString)));
166   connect(Client::coreConnection(), SIGNAL(handleNoSslInClient(bool*)), SLOT(handleNoSslInClient(bool *)));
167   connect(Client::coreConnection(), SIGNAL(handleNoSslInCore(bool*)), SLOT(handleNoSslInCore(bool *)));
168 #ifdef HAVE_SSL
169   connect(Client::coreConnection(), SIGNAL(handleSslErrors(const QSslSocket *, bool *, bool *)), SLOT(handleSslErrors(const QSslSocket *, bool *, bool *)));
170 #endif
171
172   // Setup Dock Areas
173   setDockNestingEnabled(true);
174   setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
175   setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
176   setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
177   setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
178
179   // Order is sometimes important
180   setupActions();
181   setupBufferWidget();
182   setupMenus();
183   setupTopicWidget();
184   setupNickWidget();
185   setupInputWidget();
186   setupChatMonitor();
187   setupStatusBar();
188   setupToolBars();
189   setupSystray();
190   setupTitleSetter();
191   setupHotList();
192
193 #ifndef HAVE_KDE
194 #  ifdef HAVE_PHONON
195   QtUi::registerNotificationBackend(new PhononNotificationBackend(this));
196 #  endif
197 #  ifndef QT_NO_SYSTEMTRAYICON
198   QtUi::registerNotificationBackend(new SystrayNotificationBackend(this));
199 #  endif
200
201   QtUi::registerNotificationBackend(new TaskbarNotificationBackend(this));
202
203 #else /* HAVE_KDE */
204   QtUi::registerNotificationBackend(new KNotificationBackend(this));
205 #endif /* HAVE_KDE */
206
207 #ifdef HAVE_INDICATEQT
208   QtUi::registerNotificationBackend(new IndicatorNotificationBackend(this));
209 #endif
210
211   // we assume that at this point, all configurable actions are defined!
212   QtUi::loadShortcuts();
213
214   connect(bufferWidget(), SIGNAL(currentChanged(BufferId)), SLOT(currentBufferChanged(BufferId)));
215
216   setDisconnectedState();  // Disable menus and stuff
217
218 #ifdef HAVE_KDE
219   setAutoSaveSettings();
220 #endif
221
222   // restore mainwin state
223   QtUiSettings s;
224   restoreStateFromSettings(s);
225
226   // restore locked state of docks
227   QtUi::actionCollection("General")->action("LockLayout")->setChecked(s.value("LockLayout", false).toBool());
228
229   CoreConnection *conn = Client::coreConnection();
230   if(!conn->connectToCore()) {
231     // No autoconnect selected (or no accounts)
232     showCoreConnectionDlg();
233   }
234 }
235
236 MainWin::~MainWin() {
237
238 }
239
240 void MainWin::quit() {
241   QtUiSettings s;
242   saveStateToSettings(s);
243   saveLayout();
244   QApplication::quit();
245 }
246
247 void MainWin::saveStateToSettings(UiSettings &s) {
248   s.setValue("MainWinSize", _normalSize);
249   s.setValue("MainWinPos", _normalPos);
250   s.setValue("MainWinState", saveState());
251   s.setValue("MainWinGeometry", saveGeometry());
252   s.setValue("MainWinMinimized", isMinimized());
253   s.setValue("MainWinMaximized", isMaximized());
254   s.setValue("MainWinHidden", !isVisible());
255   BufferId lastBufId = Client::bufferModel()->currentBuffer();
256   if(lastBufId.isValid())
257     s.setValue("LastUsedBufferId", lastBufId.toInt());
258
259 #ifdef HAVE_KDE
260   saveAutoSaveSettings();
261 #endif
262 }
263
264 void MainWin::restoreStateFromSettings(UiSettings &s) {
265   _normalSize = s.value("MainWinSize", size()).toSize();
266   _normalPos = s.value("MainWinPos", pos()).toPoint();
267   bool maximized = s.value("MainWinMaximized", false).toBool();
268
269 #ifndef HAVE_KDE
270   restoreGeometry(s.value("MainWinGeometry").toByteArray());
271
272   if(maximized) {
273     // restoreGeometry() fails if the windows was maximized, so we resize and position explicitly
274     resize(_normalSize);
275     move(_normalPos);
276   }
277
278   restoreState(s.value("MainWinState").toByteArray());
279
280 #else
281   move(_normalPos);
282 #endif
283
284   if(s.value("MainWinHidden").toBool() && QtUi::haveSystemTray())
285     QtUi::hideMainWidget();
286   else if(s.value("MainWinMinimized").toBool())
287     showMinimized();
288   else if(maximized)
289     showMaximized();
290   else
291     show();
292 }
293
294 void MainWin::updateIcon() {
295 #ifdef Q_WS_MAC
296   const int size = 128;
297 #else
298   const int size = 48;
299 #endif
300
301   QPixmap icon;
302   if(Client::isConnected())
303     icon = DesktopIcon("quassel", size);
304   else
305     icon = DesktopIcon("quassel_inactive", size);
306   setWindowIcon(icon);
307   qApp->setWindowIcon(icon);
308 }
309
310 void MainWin::setupActions() {
311   ActionCollection *coll = QtUi::actionCollection("General", tr("General"));
312   // File
313   coll->addAction("ConnectCore", new Action(SmallIcon("network-connect"), tr("&Connect to Core..."), coll,
314                                              this, SLOT(showCoreConnectionDlg())));
315   coll->addAction("DisconnectCore", new Action(SmallIcon("network-disconnect"), tr("&Disconnect from Core"), coll,
316                                                 Client::instance(), SLOT(disconnectFromCore())));
317   coll->addAction("CoreInfo", new Action(SmallIcon("help-about"), tr("Core &Info..."), coll,
318                                           this, SLOT(showCoreInfoDlg())));
319   coll->addAction("ConfigureNetworks", new Action(SmallIcon("configure"), tr("Configure &Networks..."), coll,
320                                               this, SLOT(on_actionConfigureNetworks_triggered())));
321   // FIXME: use QKeySequence::Quit once we depend on Qt 4.6
322   coll->addAction("Quit", new Action(SmallIcon("application-exit"), tr("&Quit"), coll,
323                                      this, SLOT(quit()), Qt::CTRL + Qt::Key_Q));
324
325   // View
326   coll->addAction("ConfigureBufferViews", new Action(tr("&Configure Chat Lists..."), coll,
327                                              this, SLOT(on_actionConfigureViews_triggered())));
328
329   QAction *lockAct = coll->addAction("LockLayout", new Action(tr("&Lock Layout"), coll));
330   lockAct->setCheckable(true);
331   connect(lockAct, SIGNAL(toggled(bool)), SLOT(on_actionLockLayout_toggled(bool)));
332
333   coll->addAction("ToggleSearchBar", new Action(SmallIcon("edit-find"), tr("Show &Search Bar"), coll,
334                                                 0, 0, QKeySequence::Find))->setCheckable(true);
335   coll->addAction("ShowAwayLog", new Action(tr("Show Away Log"), coll,
336                                             this, SLOT(showAwayLog())));
337   coll->addAction("ToggleMenuBar", new Action(SmallIcon("show-menu"), tr("Show &Menubar"), coll,
338                                                 0, 0, QKeySequence(Qt::CTRL + Qt::Key_M)))->setCheckable(true);
339
340   coll->addAction("ToggleStatusBar", new Action(tr("Show Status &Bar"), coll,
341                                                 0, 0))->setCheckable(true);
342
343   // Settings
344   coll->addAction("ConfigureShortcuts", new Action(SmallIcon("configure-shortcuts"), tr("Configure &Shortcuts..."), coll,
345                                                   this, SLOT(showShortcutsDlg())));
346   coll->addAction("ConfigureQuassel", new Action(SmallIcon("configure"), tr("&Configure Quassel..."), coll,
347                                                   this, SLOT(showSettingsDlg()), QKeySequence(Qt::Key_F7)));
348
349   // Help
350   coll->addAction("AboutQuassel", new Action(SmallIcon("quassel"), tr("&About Quassel"), coll,
351                                               this, SLOT(showAboutDlg())));
352   coll->addAction("AboutQt", new Action(QIcon(":/pics/qt-logo.png"), tr("About &Qt"), coll,
353                                          qApp, SLOT(aboutQt())));
354   coll->addAction("DebugNetworkModel", new Action(SmallIcon("tools-report-bug"), tr("Debug &NetworkModel"), coll,
355                                        this, SLOT(on_actionDebugNetworkModel_triggered())));
356   coll->addAction("DebugBufferViewOverlay", new Action(SmallIcon("tools-report-bug"), tr("Debug &BufferViewOverlay"), coll,
357                                        this, SLOT(on_actionDebugBufferViewOverlay_triggered())));
358   coll->addAction("DebugMessageModel", new Action(SmallIcon("tools-report-bug"), tr("Debug &MessageModel"), coll,
359                                        this, SLOT(on_actionDebugMessageModel_triggered())));
360   coll->addAction("DebugHotList", new Action(SmallIcon("tools-report-bug"), tr("Debug &HotList"), coll,
361                                        this, SLOT(on_actionDebugHotList_triggered())));
362   coll->addAction("DebugLog", new Action(SmallIcon("tools-report-bug"), tr("Debug &Log"), coll,
363                                        this, SLOT(on_actionDebugLog_triggered())));
364   coll->addAction("ReloadStyle", new Action(SmallIcon("view-refresh"), tr("Reload Stylesheet"), coll,
365                                        QtUi::style(), SLOT(reload()), QKeySequence::Refresh));
366
367   // Navigation
368   coll = QtUi::actionCollection("Navigation", tr("Navigation"));
369
370   coll->addAction("JumpHotBuffer", new Action(tr("Jump to hot chat"), coll,
371                                               this, SLOT(on_jumpHotBuffer_triggered()), QKeySequence(Qt::META + Qt::Key_A)));
372
373   // Jump keys
374 #ifdef Q_WS_MAC
375   const int bindModifier = Qt::ControlModifier | Qt::AltModifier;
376   const int jumpModifier = Qt::ControlModifier;
377 #else
378   const int bindModifier = Qt::ControlModifier;
379   const int jumpModifier = Qt::AltModifier;
380 #endif
381
382   coll->addAction("BindJumpKey0", new Action(tr("Set Quick Access #0"), coll, this, SLOT(bindJumpKey()),
383                                              QKeySequence(bindModifier + Qt::Key_0)))->setProperty("Index", 0);
384   coll->addAction("BindJumpKey1", new Action(tr("Set Quick Access #1"), coll, this, SLOT(bindJumpKey()),
385                                              QKeySequence(bindModifier + Qt::Key_1)))->setProperty("Index", 1);
386   coll->addAction("BindJumpKey2", new Action(tr("Set Quick Access #2"), coll, this, SLOT(bindJumpKey()),
387                                              QKeySequence(bindModifier + Qt::Key_2)))->setProperty("Index", 2);
388   coll->addAction("BindJumpKey3", new Action(tr("Set Quick Access #3"), coll, this, SLOT(bindJumpKey()),
389                                              QKeySequence(bindModifier + Qt::Key_3)))->setProperty("Index", 3);
390   coll->addAction("BindJumpKey4", new Action(tr("Set Quick Access #4"), coll, this, SLOT(bindJumpKey()),
391                                              QKeySequence(bindModifier + Qt::Key_4)))->setProperty("Index", 4);
392   coll->addAction("BindJumpKey5", new Action(tr("Set Quick Access #5"), coll, this, SLOT(bindJumpKey()),
393                                              QKeySequence(bindModifier + Qt::Key_5)))->setProperty("Index", 5);
394   coll->addAction("BindJumpKey6", new Action(tr("Set Quick Access #6"), coll, this, SLOT(bindJumpKey()),
395                                              QKeySequence(bindModifier + Qt::Key_6)))->setProperty("Index", 6);
396   coll->addAction("BindJumpKey7", new Action(tr("Set Quick Access #7"), coll, this, SLOT(bindJumpKey()),
397                                              QKeySequence(bindModifier + Qt::Key_7)))->setProperty("Index", 7);
398   coll->addAction("BindJumpKey8", new Action(tr("Set Quick Access #8"), coll, this, SLOT(bindJumpKey()),
399                                              QKeySequence(bindModifier + Qt::Key_8)))->setProperty("Index", 8);
400   coll->addAction("BindJumpKey9", new Action(tr("Set Quick Access #9"), coll, this, SLOT(bindJumpKey()),
401                                              QKeySequence(bindModifier + Qt::Key_9)))->setProperty("Index", 9);
402
403   coll->addAction("JumpKey0", new Action(tr("Quick Access #0"), coll, this, SLOT(onJumpKey()),
404                                          QKeySequence(jumpModifier + Qt::Key_0)))->setProperty("Index", 0);
405   coll->addAction("JumpKey1", new Action(tr("Quick Access #1"), coll, this, SLOT(onJumpKey()),
406                                          QKeySequence(jumpModifier + Qt::Key_1)))->setProperty("Index", 1);
407   coll->addAction("JumpKey2", new Action(tr("Quick Access #2"), coll, this, SLOT(onJumpKey()),
408                                          QKeySequence(jumpModifier + Qt::Key_2)))->setProperty("Index", 2);
409   coll->addAction("JumpKey3", new Action(tr("Quick Access #3"), coll, this, SLOT(onJumpKey()),
410                                          QKeySequence(jumpModifier + Qt::Key_3)))->setProperty("Index", 3);
411   coll->addAction("JumpKey4", new Action(tr("Quick Access #4"), coll, this, SLOT(onJumpKey()),
412                                          QKeySequence(jumpModifier + Qt::Key_4)))->setProperty("Index", 4);
413   coll->addAction("JumpKey5", new Action(tr("Quick Access #5"), coll, this, SLOT(onJumpKey()),
414                                          QKeySequence(jumpModifier + Qt::Key_5)))->setProperty("Index", 5);
415   coll->addAction("JumpKey6", new Action(tr("Quick Access #6"), coll, this, SLOT(onJumpKey()),
416                                          QKeySequence(jumpModifier + Qt::Key_6)))->setProperty("Index", 6);
417   coll->addAction("JumpKey7", new Action(tr("Quick Access #7"), coll, this, SLOT(onJumpKey()),
418                                          QKeySequence(jumpModifier + Qt::Key_7)))->setProperty("Index", 7);
419   coll->addAction("JumpKey8", new Action(tr("Quick Access #8"), coll, this, SLOT(onJumpKey()),
420                                          QKeySequence(jumpModifier + Qt::Key_8)))->setProperty("Index", 8);
421   coll->addAction("JumpKey9", new Action(tr("Quick Access #9"), coll, this, SLOT(onJumpKey()),
422                                          QKeySequence(jumpModifier + Qt::Key_9)))->setProperty("Index", 9);
423
424   // Buffer navigation
425   coll->addAction("NextBufferView", new Action(SmallIcon("go-next-view"), tr("Activate Next Chat List"), coll,
426                                                this, SLOT(nextBufferView()), QKeySequence(QKeySequence::Forward)));
427   coll->addAction("PreviousBufferView", new Action(SmallIcon("go-previous-view"), tr("Activate Previous Chat List"), coll,
428                                                    this, SLOT(previousBufferView()), QKeySequence::Back));
429   coll->addAction("NextBuffer", new Action(SmallIcon("go-down"), tr("Go to Next Chat"), coll,
430                                            this, SLOT(nextBuffer()), QKeySequence(Qt::ALT + Qt::Key_Down)));
431   coll->addAction("PreviousBuffer", new Action(SmallIcon("go-up"), tr("Go to Previous Chat"), coll,
432                                                this, SLOT(previousBuffer()), QKeySequence(Qt::ALT + Qt::Key_Up)));
433 }
434
435 void MainWin::setupMenus() {
436   ActionCollection *coll = QtUi::actionCollection("General");
437
438   _fileMenu = menuBar()->addMenu(tr("&File"));
439
440   static const QStringList coreActions = QStringList()
441     << "ConnectCore" << "DisconnectCore" << "CoreInfo";
442
443   QAction *coreAction;
444   foreach(QString actionName, coreActions) {
445     coreAction = coll->action(actionName);
446     _fileMenu->addAction(coreAction);
447     flagRemoteCoreOnly(coreAction);
448   }
449   flagRemoteCoreOnly(_fileMenu->addSeparator());
450
451   _networksMenu = _fileMenu->addMenu(tr("&Networks"));
452   _networksMenu->addAction(coll->action("ConfigureNetworks"));
453   _networksMenu->addSeparator();
454   _fileMenu->addSeparator();
455   _fileMenu->addAction(coll->action("Quit"));
456
457   _viewMenu = menuBar()->addMenu(tr("&View"));
458   _bufferViewsMenu = _viewMenu->addMenu(tr("&Chat Lists"));
459   _bufferViewsMenu->addAction(coll->action("ConfigureBufferViews"));
460   _toolbarMenu = _viewMenu->addMenu(tr("&Toolbars"));
461   _viewMenu->addSeparator();
462
463   _viewMenu->addAction(coll->action("ToggleMenuBar"));
464   _viewMenu->addAction(coll->action("ToggleStatusBar"));
465   _viewMenu->addAction(coll->action("ToggleSearchBar"));
466
467   coreAction = coll->action("ShowAwayLog");
468   flagRemoteCoreOnly(coreAction);
469   _viewMenu->addAction(coreAction);
470
471   _viewMenu->addSeparator();
472   _viewMenu->addAction(coll->action("LockLayout"));
473
474   _settingsMenu = menuBar()->addMenu(tr("&Settings"));
475 #ifdef HAVE_KDE
476   _settingsMenu->addAction(KStandardAction::configureNotifications(this, SLOT(showNotificationsDlg()), this));
477   _settingsMenu->addAction(KStandardAction::keyBindings(this, SLOT(showShortcutsDlg()), this));
478 #else
479   _settingsMenu->addAction(coll->action("ConfigureShortcuts"));
480 #endif
481   _settingsMenu->addAction(coll->action("ConfigureQuassel"));
482
483   _helpMenu = menuBar()->addMenu(tr("&Help"));
484   _helpMenu->addAction(coll->action("AboutQuassel"));
485 #ifndef HAVE_KDE
486   _helpMenu->addAction(coll->action("AboutQt"));
487 #else
488   _helpMenu->addAction(KStandardAction::aboutKDE(_kHelpMenu, SLOT(aboutKDE()), this));
489 #endif
490   _helpMenu->addSeparator();
491   _helpDebugMenu = _helpMenu->addMenu(SmallIcon("tools-report-bug"), tr("Debug"));
492   _helpDebugMenu->addAction(coll->action("DebugNetworkModel"));
493   _helpDebugMenu->addAction(coll->action("DebugBufferViewOverlay"));
494   _helpDebugMenu->addAction(coll->action("DebugMessageModel"));
495   _helpDebugMenu->addAction(coll->action("DebugHotList"));
496   _helpDebugMenu->addAction(coll->action("DebugLog"));
497   _helpDebugMenu->addSeparator();
498   _helpDebugMenu->addAction(coll->action("ReloadStyle"));
499
500   // Toggle visibility
501   QAction *showMenuBar = QtUi::actionCollection("General")->action("ToggleMenuBar");
502
503   QtUiSettings uiSettings;
504   bool enabled = uiSettings.value("ShowMenuBar", QVariant(true)).toBool();
505   showMenuBar->setChecked(enabled);
506   enabled ? menuBar()->show() : menuBar()->hide();
507
508   connect(showMenuBar, SIGNAL(toggled(bool)), menuBar(), SLOT(setVisible(bool)));
509   connect(showMenuBar, SIGNAL(toggled(bool)), this, SLOT(saveMenuBarStatus(bool)));
510 }
511
512 void MainWin::setupBufferWidget() {
513   _bufferWidget = new BufferWidget(this);
514   _bufferWidget->setModel(Client::bufferModel());
515   _bufferWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
516   setCentralWidget(_bufferWidget);
517 }
518
519 void MainWin::addBufferView(int bufferViewConfigId) {
520   addBufferView(Client::bufferViewManager()->clientBufferViewConfig(bufferViewConfigId));
521 }
522
523 void MainWin::addBufferView(ClientBufferViewConfig *config) {
524   if(!config)
525     return;
526
527   config->setLocked(QtUiSettings().value("LockLayout", false).toBool());
528   BufferViewDock *dock = new BufferViewDock(config, this);
529
530   //create the view and initialize it's filter
531   BufferView *view = new BufferView(dock);
532   view->setFilteredModel(Client::bufferModel(), config);
533   view->installEventFilter(_inputWidget); // for key presses
534
535   Client::bufferModel()->synchronizeView(view);
536
537   dock->setWidget(view);
538   dock->setVisible(_layoutLoaded); // don't show before state has been restored
539
540   addDockWidget(Qt::LeftDockWidgetArea, dock);
541   _bufferViewsMenu->addAction(dock->toggleViewAction());
542
543   connect(dock->toggleViewAction(), SIGNAL(toggled(bool)), this, SLOT(bufferViewToggled(bool)));
544   connect(dock, SIGNAL(visibilityChanged(bool)), SLOT(bufferViewVisibilityChanged(bool)));
545   _bufferViews.append(dock);
546
547   if(!activeBufferView())
548     nextBufferView();
549 }
550
551 void MainWin::removeBufferView(int bufferViewConfigId) {
552   QVariant actionData;
553   BufferViewDock *dock;
554   foreach(QAction *action, _bufferViewsMenu->actions()) {
555     actionData = action->data();
556     if(!actionData.isValid())
557       continue;
558
559     dock = qobject_cast<BufferViewDock *>(action->parent());
560     if(dock && actionData.toInt() == bufferViewConfigId) {
561       removeAction(action);
562       Client::bufferViewOverlay()->removeView(dock->bufferViewId());
563       _bufferViews.removeAll(dock);
564
565       if(dock->isActive()) {
566         dock->setActive(false);
567         _activeBufferViewIndex = -1;
568         nextBufferView();
569       }
570
571       dock->deleteLater();
572     }
573   }
574 }
575
576 void MainWin::bufferViewToggled(bool enabled) {
577   if(!enabled && !isVisible()) {
578     // hiding the mainwindow triggers a toggle of the bufferview (which pretty much sucks big time)
579     // since this isn't our fault and we can't do anything about it, we suppress the resulting calls
580     return;
581   }
582   QAction *action = qobject_cast<QAction *>(sender());
583   Q_ASSERT(action);
584   BufferViewDock *dock = qobject_cast<BufferViewDock *>(action->parent());
585   Q_ASSERT(dock);
586
587   // Make sure we don't toggle backlog fetch for a view we've already removed
588   if(!_bufferViews.contains(dock))
589     return;
590
591   if(enabled)
592     Client::bufferViewOverlay()->addView(dock->bufferViewId());
593   else
594     Client::bufferViewOverlay()->removeView(dock->bufferViewId());
595 }
596
597 void MainWin::bufferViewVisibilityChanged(bool visible) {
598   Q_UNUSED(visible);
599   BufferViewDock *dock = qobject_cast<BufferViewDock *>(sender());
600   Q_ASSERT(dock);
601   if((!dock->isHidden() && !activeBufferView()) || (dock->isHidden() && dock->isActive()))
602     nextBufferView();
603 }
604
605 BufferView *MainWin::allBuffersView() const {
606   // "All Buffers" is always the first dock created
607   if(_bufferViews.count() > 0)
608     return _bufferViews[0]->bufferView();
609   return 0;
610 }
611
612 BufferView *MainWin::activeBufferView() const {
613   if(_activeBufferViewIndex < 0 || _activeBufferViewIndex >= _bufferViews.count())
614     return 0;
615   BufferViewDock *dock = _bufferViews.at(_activeBufferViewIndex);
616   return dock->isActive() ? qobject_cast<BufferView*>(dock->widget()) : 0;
617 }
618
619 void MainWin::changeActiveBufferView(int bufferViewId) {
620   if(bufferViewId < 0)
621     return;
622
623   BufferView *current = activeBufferView();
624   if(current) {
625     qobject_cast<BufferViewDock*>(current->parent())->setActive(false);
626     _activeBufferViewIndex = -1;
627   }
628
629   for(int i = 0; i < _bufferViews.count(); i++) {
630     BufferViewDock *dock = _bufferViews.at(i);
631     if(dock->bufferViewId() == bufferViewId && !dock->isHidden()) {
632       _activeBufferViewIndex = i;
633       dock->setActive(true);
634       return;
635     }
636   }
637
638   nextBufferView(); // fallback
639 }
640
641 void MainWin::changeActiveBufferView(bool backwards) {
642   BufferView *current = activeBufferView();
643   if(current)
644     qobject_cast<BufferViewDock*>(current->parent())->setActive(false);
645
646   if(!_bufferViews.count())
647     return;
648
649   int c = _bufferViews.count();
650   while(c--) { // yes, this will reactivate the current active one if all others fail
651     if(backwards) {
652       if(--_activeBufferViewIndex < 0)
653         _activeBufferViewIndex = _bufferViews.count()-1;
654     } else {
655       if(++_activeBufferViewIndex >= _bufferViews.count())
656         _activeBufferViewIndex = 0;
657     }
658
659     BufferViewDock *dock = _bufferViews.at(_activeBufferViewIndex);
660     if(dock->isHidden())
661       continue;
662
663     dock->setActive(true);
664     return;
665   }
666
667   _activeBufferViewIndex = -1;
668 }
669
670 void MainWin::nextBufferView() {
671   changeActiveBufferView(false);
672 }
673
674 void MainWin::previousBufferView() {
675   changeActiveBufferView(true);
676 }
677
678 void MainWin::nextBuffer() {
679   BufferView *view = activeBufferView();
680   if(view)
681     view->nextBuffer();
682 }
683
684 void MainWin::previousBuffer() {
685   BufferView *view = activeBufferView();
686   if(view)
687     view->previousBuffer();
688 }
689
690 void MainWin::showNotificationsDlg() {
691   SettingsPageDlg dlg(new NotificationsSettingsPage(this), this);
692   dlg.exec();
693 }
694
695 void MainWin::on_actionConfigureNetworks_triggered() {
696   SettingsPageDlg dlg(new NetworksSettingsPage(this), this);
697   dlg.exec();
698 }
699
700 void MainWin::on_actionConfigureViews_triggered() {
701   SettingsPageDlg dlg(new BufferViewSettingsPage(this), this);
702   dlg.exec();
703 }
704
705 void MainWin::on_actionLockLayout_toggled(bool lock) {
706   QList<VerticalDock *> docks = findChildren<VerticalDock *>();
707   foreach(VerticalDock *dock, docks) {
708     dock->showTitle(!lock);
709   }
710   if(Client::bufferViewManager()) {
711     foreach(ClientBufferViewConfig *config, Client::bufferViewManager()->clientBufferViewConfigs()) {
712       config->setLocked(lock);
713     }
714   }
715   QtUiSettings().setValue("LockLayout", lock);
716 }
717
718 void MainWin::setupNickWidget() {
719   // create nick dock
720   NickListDock *nickDock = new NickListDock(tr("Nicks"), this);
721   nickDock->setObjectName("NickDock");
722   nickDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
723
724   _nickListWidget = new NickListWidget(nickDock);
725   nickDock->setWidget(_nickListWidget);
726
727   addDockWidget(Qt::RightDockWidgetArea, nickDock);
728   _viewMenu->addAction(nickDock->toggleViewAction());
729   nickDock->toggleViewAction()->setText(tr("Show Nick List"));
730
731   // See NickListDock::NickListDock();
732   // connect(nickDock->toggleViewAction(), SIGNAL(triggered(bool)), nickListWidget, SLOT(showWidget(bool)));
733
734   // attach the NickListWidget to the BufferModel and the default selection
735   _nickListWidget->setModel(Client::bufferModel());
736   _nickListWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
737 }
738
739 void MainWin::setupChatMonitor() {
740   VerticalDock *dock = new VerticalDock(tr("Chat Monitor"), this);
741   dock->setObjectName("ChatMonitorDock");
742
743   ChatMonitorFilter *filter = new ChatMonitorFilter(Client::messageModel(), this);
744   _chatMonitorView = new ChatMonitorView(filter, this);
745   _chatMonitorView->setFocusProxy(_inputWidget);
746   _chatMonitorView->show();
747   dock->setWidget(_chatMonitorView);
748   dock->hide();
749
750   addDockWidget(Qt::TopDockWidgetArea, dock, Qt::Vertical);
751   _viewMenu->addAction(dock->toggleViewAction());
752   dock->toggleViewAction()->setText(tr("Show Chat Monitor"));
753 }
754
755 void MainWin::setupInputWidget() {
756   VerticalDock *dock = new VerticalDock(tr("Inputline"), this);
757   dock->setObjectName("InputDock");
758
759   _inputWidget = new InputWidget(dock);
760   dock->setWidget(_inputWidget);
761
762   addDockWidget(Qt::BottomDockWidgetArea, dock);
763
764   _viewMenu->addAction(dock->toggleViewAction());
765   dock->toggleViewAction()->setText(tr("Show Input Line"));
766
767   _inputWidget->setModel(Client::bufferModel());
768   _inputWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
769
770   _bufferWidget->setFocusProxy(_inputWidget);
771
772   _inputWidget->inputLine()->installEventFilter(_bufferWidget);
773
774   connect(_topicWidget, SIGNAL(switchedPlain()), _bufferWidget, SLOT(setFocus()));
775 }
776
777 void MainWin::setupTopicWidget() {
778   VerticalDock *dock = new VerticalDock(tr("Topic"), this);
779   dock->setObjectName("TopicDock");
780   _topicWidget = new TopicWidget(dock);
781
782   dock->setWidget(_topicWidget);
783
784   _topicWidget->setModel(Client::bufferModel());
785   _topicWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
786
787   addDockWidget(Qt::TopDockWidgetArea, dock, Qt::Vertical);
788
789   _viewMenu->addAction(dock->toggleViewAction());
790   dock->toggleViewAction()->setText(tr("Show Topic Line"));
791 }
792
793 void MainWin::setupTitleSetter() {
794   _titleSetter.setModel(Client::bufferModel());
795   _titleSetter.setSelectionModel(Client::bufferModel()->standardSelectionModel());
796 }
797
798 void MainWin::setupStatusBar() {
799   // MessageProcessor progress
800   statusBar()->addPermanentWidget(_msgProcessorStatusWidget);
801
802   // Connection state
803   _coreConnectionStatusWidget->update();
804   statusBar()->addPermanentWidget(_coreConnectionStatusWidget);
805
806   QAction *showStatusbar = QtUi::actionCollection("General")->action("ToggleStatusBar");
807
808   QtUiSettings uiSettings;
809
810   bool enabled = uiSettings.value("ShowStatusBar", QVariant(true)).toBool();
811   showStatusbar->setChecked(enabled);
812   enabled ? statusBar()->show() : statusBar()->hide();
813
814   connect(showStatusbar, SIGNAL(toggled(bool)), statusBar(), SLOT(setVisible(bool)));
815   connect(showStatusbar, SIGNAL(toggled(bool)), this, SLOT(saveStatusBarStatus(bool)));
816
817   connect(Client::coreConnection(), SIGNAL(connectionMsg(QString)), statusBar(), SLOT(showMessage(QString)));
818 }
819
820 void MainWin::setupHotList() {
821   FlatProxyModel *flatProxy = new FlatProxyModel(this);
822   flatProxy->setSourceModel(Client::bufferModel());
823   _bufferHotList = new BufferHotListFilter(flatProxy);
824 }
825
826 void MainWin::saveMenuBarStatus(bool enabled) {
827   QtUiSettings uiSettings;
828   uiSettings.setValue("ShowMenuBar", enabled);
829 }
830
831 void MainWin::saveStatusBarStatus(bool enabled) {
832   QtUiSettings uiSettings;
833   uiSettings.setValue("ShowStatusBar", enabled);
834 }
835
836 void MainWin::setupSystray() {
837 #ifdef HAVE_DBUS
838   _systemTray = new StatusNotifierItem(this);
839 #elif !defined QT_NO_SYSTEMTRAYICON
840   _systemTray = new LegacySystemTray(this);
841 #else
842   _systemTray = new SystemTray(this); // dummy
843 #endif
844   _systemTray->init();
845 }
846
847 void MainWin::setupToolBars() {
848   connect(_bufferWidget, SIGNAL(currentChanged(QModelIndex)),
849           QtUi::toolBarActionProvider(), SLOT(currentBufferChanged(QModelIndex)));
850   connect(_nickListWidget, SIGNAL(nickSelectionChanged(QModelIndexList)),
851           QtUi::toolBarActionProvider(), SLOT(nickSelectionChanged(QModelIndexList)));
852
853 #ifdef Q_WS_MAC
854   setUnifiedTitleAndToolBarOnMac(true);
855 #endif
856
857 #ifdef HAVE_KDE
858   _mainToolBar = new KToolBar("MainToolBar", this, Qt::TopToolBarArea, false, true, true);
859 #else
860   _mainToolBar = new QToolBar(this);
861   _mainToolBar->setObjectName("MainToolBar");
862 #endif
863   _mainToolBar->setWindowTitle(tr("Main Toolbar"));
864   addToolBar(_mainToolBar);
865
866   QtUi::toolBarActionProvider()->addActions(_mainToolBar, ToolBarActionProvider::MainToolBar);
867   _toolbarMenu->addAction(_mainToolBar->toggleViewAction());
868 }
869
870 void MainWin::connectedToCore() {
871   Q_CHECK_PTR(Client::bufferViewManager());
872   connect(Client::bufferViewManager(), SIGNAL(bufferViewConfigAdded(int)), this, SLOT(addBufferView(int)));
873   connect(Client::bufferViewManager(), SIGNAL(bufferViewConfigDeleted(int)), this, SLOT(removeBufferView(int)));
874   connect(Client::bufferViewManager(), SIGNAL(initDone()), this, SLOT(loadLayout()));
875
876   setConnectedState();
877 }
878
879 void MainWin::setConnectedState() {
880   ActionCollection *coll = QtUi::actionCollection("General");
881
882   coll->action("ConnectCore")->setEnabled(false);
883   coll->action("DisconnectCore")->setEnabled(true);
884   coll->action("CoreInfo")->setEnabled(true);
885
886   foreach(QAction *action, _fileMenu->actions()) {
887     if(isRemoteCoreOnly(action))
888       action->setVisible(!Client::internalCore());
889   }
890
891   disconnect(Client::backlogManager(), SIGNAL(updateProgress(int, int)), _msgProcessorStatusWidget, SLOT(setProgress(int, int)));
892   disconnect(Client::backlogManager(), SIGNAL(messagesRequested(const QString &)), this, SLOT(showStatusBarMessage(const QString &)));
893   disconnect(Client::backlogManager(), SIGNAL(messagesProcessed(const QString &)), this, SLOT(showStatusBarMessage(const QString &)));
894   if(!Client::internalCore()) {
895     connect(Client::backlogManager(), SIGNAL(updateProgress(int, int)), _msgProcessorStatusWidget, SLOT(setProgress(int, int)));
896     connect(Client::backlogManager(), SIGNAL(messagesRequested(const QString &)), this, SLOT(showStatusBarMessage(const QString &)));
897     connect(Client::backlogManager(), SIGNAL(messagesProcessed(const QString &)), this, SLOT(showStatusBarMessage(const QString &)));
898   }
899
900   // _viewMenu->setEnabled(true);
901   if(!Client::internalCore())
902     statusBar()->showMessage(tr("Connected to core."));
903   else
904     statusBar()->clearMessage();
905
906   _coreConnectionStatusWidget->setVisible(!Client::internalCore());
907   updateIcon();
908   systemTray()->setState(SystemTray::Active);
909
910   if(Client::networkIds().isEmpty()) {
911     IrcConnectionWizard *wizard = new IrcConnectionWizard(this, Qt::Sheet);
912     wizard->show();
913   }
914   else {
915     QtUiSettings s;
916     BufferId lastUsedBufferId(s.value("LastUsedBufferId").toInt());
917     if(lastUsedBufferId.isValid())
918       Client::bufferModel()->switchToBuffer(lastUsedBufferId);
919   }
920 }
921
922 void MainWin::loadLayout() {
923   QtUiSettings s;
924   int accountId = Client::currentCoreAccount().accountId().toInt();
925   QByteArray state = s.value(QString("MainWinState-%1").arg(accountId)).toByteArray();
926   if(state.isEmpty()) {
927     foreach(BufferViewDock *view, _bufferViews)
928       view->show();
929     _layoutLoaded = true;
930     return;
931   }
932
933   restoreState(state, accountId);
934   int bufferViewId = s.value(QString("ActiveBufferView-%1").arg(accountId), -1).toInt();
935   if(bufferViewId >= 0)
936     changeActiveBufferView(bufferViewId);
937
938   _layoutLoaded = true;
939 }
940
941 void MainWin::saveLayout() {
942   QtUiSettings s;
943   int accountId = _bufferViews.count()? Client::currentCoreAccount().accountId().toInt() : 0; // only save if we still have a layout!
944   if(accountId > 0) {
945     s.setValue(QString("MainWinState-%1").arg(accountId) , saveState(accountId));
946     BufferView *view = activeBufferView();
947     s.setValue(QString("ActiveBufferView-%1").arg(accountId), view ? view->config()->bufferViewId() : -1);
948   }
949 }
950
951 void MainWin::disconnectedFromCore() {
952   // save core specific layout and remove bufferviews;
953   saveLayout();
954   _layoutLoaded = false;
955
956   QVariant actionData;
957   BufferViewDock *dock;
958   foreach(QAction *action, _bufferViewsMenu->actions()) {
959     actionData = action->data();
960     if(!actionData.isValid())
961       continue;
962
963     dock = qobject_cast<BufferViewDock *>(action->parent());
964     if(dock && actionData.toInt() != -1) {
965       removeAction(action);
966       _bufferViews.removeAll(dock);
967       dock->deleteLater();
968     }
969   }
970
971   QtUiSettings s;
972   restoreState(s.value("MainWinState").toByteArray());
973   setDisconnectedState();
974 }
975
976 void MainWin::setDisconnectedState() {
977   ActionCollection *coll = QtUi::actionCollection("General");
978   //ui.menuCore->setEnabled(false);
979   coll->action("ConnectCore")->setEnabled(true);
980   coll->action("DisconnectCore")->setEnabled(false);
981   coll->action("CoreInfo")->setEnabled(false);
982   //_viewMenu->setEnabled(false);
983   statusBar()->showMessage(tr("Not connected to core."));
984   if(_msgProcessorStatusWidget)
985     _msgProcessorStatusWidget->setProgress(0, 0);
986   updateIcon();
987   systemTray()->setState(SystemTray::Passive);
988 }
989
990 void MainWin::userAuthenticationRequired(CoreAccount *account, bool *valid, const QString &errorMessage) {
991   Q_UNUSED(errorMessage)
992   CoreConnectAuthDlg dlg(account, this);
993   *valid = (dlg.exec() == QDialog::Accepted);
994 }
995
996 void MainWin::handleNoSslInClient(bool *accepted) {
997   QMessageBox box(QMessageBox::Warning, tr("Unencrypted Connection"), tr("<b>Your client does not support SSL encryption</b>"),
998                   QMessageBox::Ignore|QMessageBox::Cancel, this);
999   box.setInformativeText(tr("Sensitive data, like passwords, will be transmitted unencrypted to your Quassel core."));
1000   box.setDefaultButton(QMessageBox::Ignore);
1001   *accepted = box.exec() == QMessageBox::Ignore;
1002 }
1003
1004 void MainWin::handleNoSslInCore(bool *accepted) {
1005   QMessageBox box(QMessageBox::Warning, tr("Unencrypted Connection"), tr("<b>Your core does not support SSL encryption</b>"),
1006                   QMessageBox::Ignore|QMessageBox::Cancel, this);
1007   box.setInformativeText(tr("Sensitive data, like passwords, will be transmitted unencrypted to your Quassel core."));
1008   box.setDefaultButton(QMessageBox::Ignore);
1009   *accepted = box.exec() == QMessageBox::Ignore;
1010
1011 }
1012
1013 #ifdef HAVE_SSL
1014
1015 void MainWin::handleSslErrors(const QSslSocket *socket, bool *accepted, bool *permanently) {
1016   QString errorString = "<ul>";
1017   foreach(const QSslError error, socket->sslErrors())
1018     errorString += QString("<li>%1</li>").arg(error.errorString());
1019   errorString += "</ul>";
1020
1021   QMessageBox box(QMessageBox::Warning,
1022                   tr("Untrusted Security Certificate"),
1023                   tr("<b>The SSL certificate provided by the core at %1 is untrusted for the following reasons:</b>").arg(socket->peerName()),
1024                   QMessageBox::Cancel, this);
1025   box.setInformativeText(errorString);
1026   box.addButton(tr("Continue"), QMessageBox::AcceptRole);
1027   box.setDefaultButton(box.addButton(tr("Show Certificate"), QMessageBox::HelpRole));
1028
1029   QMessageBox::ButtonRole role;
1030   do {
1031     box.exec();
1032     role = box.buttonRole(box.clickedButton());
1033     if(role == QMessageBox::HelpRole) {
1034       SslInfoDlg dlg(socket, this);
1035       dlg.exec();
1036     }
1037   } while(role == QMessageBox::HelpRole);
1038
1039   *accepted = role == QMessageBox::AcceptRole;
1040   if(*accepted) {
1041     QMessageBox box2(QMessageBox::Warning,
1042                      tr("Untrusted Security Certificate"),
1043                      tr("Would you like to accept this certificate forever without being prompted?"),
1044                      0, this);
1045     box2.setDefaultButton(box2.addButton(tr("Current Session Only"), QMessageBox::NoRole));
1046     box2.addButton(tr("Forever"), QMessageBox::YesRole);
1047     box2.exec();
1048     *permanently =  box2.buttonRole(box2.clickedButton()) == QMessageBox::YesRole;
1049   }
1050 }
1051
1052 #endif /* HAVE_SSL */
1053
1054 void MainWin::handleCoreConnectionError(const QString &error) {
1055   QMessageBox::critical(this, tr("Core Connection Error"), error, QMessageBox::Ok);
1056 }
1057
1058 void MainWin::showCoreConnectionDlg() {
1059   CoreConnectDlg dlg(this);
1060   if(dlg.exec() == QDialog::Accepted) {
1061     AccountId accId = dlg.selectedAccount();
1062     if(accId.isValid())
1063       Client::coreConnection()->connectToCore(accId);
1064   }
1065 }
1066
1067 void MainWin::showCoreConfigWizard(const QVariantList &backends) {
1068   CoreConfigWizard *wizard = new CoreConfigWizard(Client::coreConnection(), backends, this);
1069
1070   wizard->show();
1071 }
1072
1073 void MainWin::showChannelList(NetworkId netId) {
1074   ChannelListDlg *channelListDlg = new ChannelListDlg();
1075
1076   if(!netId.isValid()) {
1077     QAction *action = qobject_cast<QAction *>(sender());
1078     if(action)
1079       netId = action->data().value<NetworkId>();
1080   }
1081
1082   channelListDlg->setAttribute(Qt::WA_DeleteOnClose);
1083   channelListDlg->setNetwork(netId);
1084   channelListDlg->show();
1085 }
1086
1087 void MainWin::showIgnoreList(QString newRule) {
1088   SettingsPageDlg dlg(new IgnoreListSettingsPage(this), this);
1089   // prepare config dialog for new rule
1090   if(!newRule.isEmpty())
1091     qobject_cast<IgnoreListSettingsPage *>(dlg.currentPage())->editIgnoreRule(newRule);
1092   dlg.exec();
1093 }
1094
1095 void MainWin::showCoreInfoDlg() {
1096   CoreInfoDlg(this).exec();
1097 }
1098
1099 void MainWin::showAwayLog() {
1100   if(_awayLog)
1101     return;
1102   AwayLogFilter *filter = new AwayLogFilter(Client::messageModel());
1103   _awayLog = new AwayLogView(filter, 0);
1104   filter->setParent(_awayLog);
1105   connect(_awayLog, SIGNAL(destroyed()), this, SLOT(awayLogDestroyed()));
1106   _awayLog->setAttribute(Qt::WA_DeleteOnClose);
1107   _awayLog->show();
1108 }
1109
1110 void MainWin::awayLogDestroyed() {
1111   _awayLog = 0;
1112 }
1113
1114 void MainWin::showSettingsDlg() {
1115   SettingsDlg *dlg = new SettingsDlg();
1116
1117   //Category: Interface
1118   dlg->registerSettingsPage(new AppearanceSettingsPage(dlg));
1119   dlg->registerSettingsPage(new ChatViewSettingsPage(dlg));
1120   dlg->registerSettingsPage(new ChatMonitorSettingsPage(dlg));
1121   dlg->registerSettingsPage(new ItemViewSettingsPage(dlg));
1122   dlg->registerSettingsPage(new BufferViewSettingsPage(dlg));
1123   dlg->registerSettingsPage(new InputWidgetSettingsPage(dlg));
1124   dlg->registerSettingsPage(new TopicWidgetSettingsPage(dlg));
1125   dlg->registerSettingsPage(new HighlightSettingsPage(dlg));
1126   dlg->registerSettingsPage(new NotificationsSettingsPage(dlg));
1127   dlg->registerSettingsPage(new BacklogSettingsPage(dlg));
1128
1129   //Category: IRC
1130   dlg->registerSettingsPage(new ConnectionSettingsPage(dlg));
1131   dlg->registerSettingsPage(new IdentitiesSettingsPage(dlg));
1132   dlg->registerSettingsPage(new NetworksSettingsPage(dlg));
1133   dlg->registerSettingsPage(new AliasesSettingsPage(dlg));
1134   dlg->registerSettingsPage(new IgnoreListSettingsPage(dlg));
1135
1136   // Category: Remote Cores
1137   if(Quassel::runMode() != Quassel::Monolithic) {
1138     dlg->registerSettingsPage(new CoreAccountSettingsPage(dlg));
1139     dlg->registerSettingsPage(new CoreConnectionSettingsPage(dlg));
1140   }
1141
1142   dlg->show();
1143 }
1144
1145 void MainWin::showAboutDlg() {
1146   AboutDlg(this).exec();
1147 }
1148
1149 void MainWin::showShortcutsDlg() {
1150 #ifdef HAVE_KDE
1151   KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this);
1152   foreach(KActionCollection *coll, QtUi::actionCollections())
1153     dlg.addCollection(coll, coll->property("Category").toString());
1154   dlg.exec();
1155 #else
1156   SettingsPageDlg dlg(new ShortcutsSettingsPage(QtUi::actionCollections(), this), this);
1157   dlg.exec();
1158 #endif
1159 }
1160
1161 /********************************************************************************************************/
1162
1163 bool MainWin::event(QEvent *event) {
1164   if(event->type() == QEvent::WindowActivate) {
1165     BufferId buffer = Client::bufferModel()->currentBuffer();
1166     if(buffer.isValid())
1167       Client::instance()->markBufferAsRead(buffer);
1168   }
1169   return QMainWindow::event(event);
1170 }
1171
1172 void MainWin::moveEvent(QMoveEvent *event) {
1173   if(!(windowState() & Qt::WindowMaximized))
1174     _normalPos = event->pos();
1175
1176   QMainWindow::moveEvent(event);
1177 }
1178
1179 void MainWin::resizeEvent(QResizeEvent *event) {
1180   if(!(windowState() & Qt::WindowMaximized))
1181     _normalSize = event->size();
1182
1183   QMainWindow::resizeEvent(event);
1184 }
1185
1186 void MainWin::closeEvent(QCloseEvent *event) {
1187   QtUiSettings s;
1188   QtUiApplication* app = qobject_cast<QtUiApplication*> qApp;
1189   Q_ASSERT(app);
1190   if(!app->isAboutToQuit() && QtUi::haveSystemTray() && s.value("MinimizeOnClose").toBool()) {
1191     QtUi::hideMainWidget();
1192     event->ignore();
1193   } else {
1194     event->accept();
1195     quit();
1196   }
1197 }
1198
1199 void MainWin::messagesInserted(const QModelIndex &parent, int start, int end) {
1200   Q_UNUSED(parent);
1201
1202   bool hasFocus = QApplication::activeWindow() != 0;
1203
1204   for(int i = start; i <= end; i++) {
1205     QModelIndex idx = Client::messageModel()->index(i, ChatLineModel::ContentsColumn);
1206     if(!idx.isValid()) {
1207       qDebug() << "MainWin::messagesInserted(): Invalid model index!";
1208       continue;
1209     }
1210     Message::Flags flags = (Message::Flags)idx.data(ChatLineModel::FlagsRole).toInt();
1211     if(flags.testFlag(Message::Backlog) || flags.testFlag(Message::Self))
1212       continue;
1213
1214     BufferId bufId = idx.data(ChatLineModel::BufferIdRole).value<BufferId>();
1215     BufferInfo::Type bufType = Client::networkModel()->bufferType(bufId);
1216
1217     if(hasFocus && bufId == Client::bufferModel()->currentBuffer())
1218       continue;
1219
1220     if((flags & Message::Highlight || bufType == BufferInfo::QueryBuffer)
1221       && !(Client::ignoreListManager() && Client::ignoreListManager()->match(idx.data(MessageModel::MessageRole).value<Message>(),
1222                                                                              Client::networkModel()->networkName(bufId))))
1223     {
1224       QModelIndex senderIdx = Client::messageModel()->index(i, ChatLineModel::SenderColumn);
1225       QString sender = senderIdx.data(ChatLineModel::EditRole).toString();
1226       QString contents = idx.data(ChatLineModel::DisplayRole).toString();
1227       AbstractNotificationBackend::NotificationType type;
1228
1229       if(bufType == BufferInfo::QueryBuffer && !hasFocus)
1230         type = AbstractNotificationBackend::PrivMsg;
1231       else if(bufType == BufferInfo::QueryBuffer && hasFocus)
1232         type = AbstractNotificationBackend::PrivMsgFocused;
1233       else if(flags & Message::Highlight && !hasFocus)
1234         type = AbstractNotificationBackend::Highlight;
1235       else
1236         type = AbstractNotificationBackend::HighlightFocused;
1237
1238       QtUi::instance()->invokeNotification(bufId, type, sender, contents);
1239     }
1240   }
1241 }
1242
1243 void MainWin::currentBufferChanged(BufferId buffer) {
1244   if(buffer.isValid())
1245     Client::instance()->markBufferAsRead(buffer);
1246 }
1247
1248 void MainWin::clientNetworkCreated(NetworkId id) {
1249   const Network *net = Client::network(id);
1250   QAction *act = new QAction(net->networkName(), this);
1251   act->setObjectName(QString("NetworkAction-%1").arg(id.toInt()));
1252   act->setData(QVariant::fromValue<NetworkId>(id));
1253   connect(net, SIGNAL(updatedRemotely()), this, SLOT(clientNetworkUpdated()));
1254   connect(act, SIGNAL(triggered()), this, SLOT(connectOrDisconnectFromNet()));
1255
1256   QAction *beforeAction = 0;
1257   foreach(QAction *action, _networksMenu->actions()) {
1258     if(!action->data().isValid())  // ignore stock actions
1259       continue;
1260     if(net->networkName().localeAwareCompare(action->text()) < 0) {
1261       beforeAction = action;
1262       break;
1263     }
1264   }
1265   _networksMenu->insertAction(beforeAction, act);
1266 }
1267
1268 void MainWin::clientNetworkUpdated() {
1269   const Network *net = qobject_cast<const Network *>(sender());
1270   if(!net)
1271     return;
1272
1273   QAction *action = findChild<QAction *>(QString("NetworkAction-%1").arg(net->networkId().toInt()));
1274   if(!action)
1275     return;
1276
1277   action->setText(net->networkName());
1278
1279   switch(net->connectionState()) {
1280   case Network::Initialized:
1281     action->setIcon(SmallIcon("network-connect"));
1282     break;
1283   case Network::Disconnected:
1284     action->setIcon(SmallIcon("network-disconnect"));
1285     break;
1286   default:
1287     action->setIcon(SmallIcon("network-wired"));
1288   }
1289 }
1290
1291 void MainWin::clientNetworkRemoved(NetworkId id) {
1292   QAction *action = findChild<QAction *>(QString("NetworkAction-%1").arg(id.toInt()));
1293   if(!action)
1294     return;
1295
1296   action->deleteLater();
1297 }
1298
1299 void MainWin::connectOrDisconnectFromNet() {
1300   QAction *act = qobject_cast<QAction *>(sender());
1301   if(!act) return;
1302   const Network *net = Client::network(act->data().value<NetworkId>());
1303   if(!net) return;
1304   if(net->connectionState() == Network::Disconnected) net->requestConnect();
1305   else net->requestDisconnect();
1306 }
1307
1308 void MainWin::on_jumpHotBuffer_triggered() {
1309   if(!_bufferHotList->rowCount())
1310     return;
1311
1312   QModelIndex topIndex = _bufferHotList->index(0, 0);
1313   BufferId bufferId = _bufferHotList->data(topIndex, NetworkModel::BufferIdRole).value<BufferId>();
1314   Client::bufferModel()->switchToBuffer(bufferId);
1315 }
1316
1317 void MainWin::onJumpKey() {
1318   QAction *action = qobject_cast<QAction *>(sender());
1319   if(!action || !Client::bufferModel())
1320     return;
1321   int idx = action->property("Index").toInt();
1322
1323   if(_jumpKeyMap.isEmpty())
1324     _jumpKeyMap = CoreAccountSettings().jumpKeyMap();
1325
1326   if(!_jumpKeyMap.contains(idx))
1327     return;
1328
1329   BufferId buffer = _jumpKeyMap.value(idx);
1330   if(buffer.isValid())
1331     Client::bufferModel()->switchToBuffer(buffer);
1332
1333 }
1334
1335 void MainWin::bindJumpKey() {
1336   QAction *action = qobject_cast<QAction *>(sender());
1337   if(!action || !Client::bufferModel())
1338     return;
1339   int idx = action->property("Index").toInt();
1340
1341   _jumpKeyMap[idx] = Client::bufferModel()->currentBuffer();
1342   CoreAccountSettings().setJumpKeyMap(_jumpKeyMap);
1343 }
1344
1345 void MainWin::on_actionDebugNetworkModel_triggered() {
1346   QTreeView *view = new QTreeView;
1347   view->setAttribute(Qt::WA_DeleteOnClose);
1348   view->setWindowTitle("Debug NetworkModel View");
1349   view->setModel(Client::networkModel());
1350   view->setColumnWidth(0, 250);
1351   view->setColumnWidth(1, 250);
1352   view->setColumnWidth(2, 80);
1353   view->resize(610, 300);
1354   view->show();
1355 }
1356
1357 void MainWin::on_actionDebugHotList_triggered() {
1358   QTreeView *view = new QTreeView;
1359   view->setAttribute(Qt::WA_DeleteOnClose);
1360   view->setModel(_bufferHotList);
1361   view->show();
1362 }
1363
1364 void MainWin::on_actionDebugBufferViewOverlay_triggered() {
1365   DebugBufferViewOverlay *overlay = new DebugBufferViewOverlay(0);
1366   overlay->setAttribute(Qt::WA_DeleteOnClose);
1367   overlay->show();
1368 }
1369
1370 void MainWin::on_actionDebugMessageModel_triggered() {
1371   QTableView *view = new QTableView(0);
1372   DebugMessageModelFilter *filter = new DebugMessageModelFilter(view);
1373   filter->setSourceModel(Client::messageModel());
1374   view->setModel(filter);
1375   view->setAttribute(Qt::WA_DeleteOnClose, true);
1376   view->verticalHeader()->hide();
1377   view->horizontalHeader()->setStretchLastSection(true);
1378   view->show();
1379 }
1380
1381 void MainWin::on_actionDebugLog_triggered() {
1382   DebugLogWidget *logWidget = new DebugLogWidget(0);
1383   logWidget->show();
1384 }
1385
1386 void MainWin::showStatusBarMessage(const QString &message) {
1387   statusBar()->showMessage(message, 10000);
1388 }