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