Make SessionSettings derive from UiSettings
[quassel.git] / src / qtui / mainwin.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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 #endif
30
31 #ifdef Q_WS_X11
32 #  include <QX11Info>
33 #endif
34
35 #include "aboutdlg.h"
36 #include "awaylogfilter.h"
37 #include "awaylogview.h"
38 #include "action.h"
39 #include "actioncollection.h"
40 #include "buffermodel.h"
41 #include "bufferview.h"
42 #include "bufferviewoverlay.h"
43 #include "bufferviewoverlayfilter.h"
44 #include "bufferwidget.h"
45 #include "channellistdlg.h"
46 #include "chatlinemodel.h"
47 #include "chatmonitorfilter.h"
48 #include "chatmonitorview.h"
49 #include "chatview.h"
50 #include "client.h"
51 #include "clientsyncer.h"
52 #include "clientbacklogmanager.h"
53 #include "clientbufferviewconfig.h"
54 #include "clientbufferviewmanager.h"
55 #include "coreinfodlg.h"
56 #include "coreconnectdlg.h"
57 #include "contextmenuactionprovider.h"
58 #include "debuglogwidget.h"
59 #include "debugmessagemodelfilter.h"
60 #include "iconloader.h"
61 #include "inputwidget.h"
62 #include "inputline.h"
63 #include "irclistmodel.h"
64 #include "ircconnectionwizard.h"
65 #include "jumpkeyhandler.h"
66 #include "msgprocessorstatuswidget.h"
67 #include "nicklistwidget.h"
68 #include "qtuiapplication.h"
69 #include "qtuimessageprocessor.h"
70 #include "qtuisettings.h"
71 #include "settingsdlg.h"
72 #include "settingspagedlg.h"
73 #include "systemtray.h"
74 #include "toolbaractionprovider.h"
75 #include "topicwidget.h"
76 #include "verticaldock.h"
77
78 #ifndef HAVE_KDE
79 #  ifdef HAVE_DBUS
80 #    include "desktopnotificationbackend.h"
81 #  endif
82 #  ifdef HAVE_PHONON
83 #    include "phononnotificationbackend.h"
84 #  endif
85 #  include "systraynotificationbackend.h"
86 #  include "taskbarnotificationbackend.h"
87 #else /* HAVE_KDE */
88 #  include "knotificationbackend.h"
89 #endif /* HAVE_KDE */
90
91 #include "settingspages/aliasessettingspage.h"
92 #include "settingspages/appearancesettingspage.h"
93 #include "settingspages/backlogsettingspage.h"
94 #include "settingspages/bufferviewsettingspage.h"
95 #include "settingspages/chatmonitorsettingspage.h"
96 #include "settingspages/colorsettingspage.h"
97 #include "settingspages/generalsettingspage.h"
98 #include "settingspages/highlightsettingspage.h"
99 #include "settingspages/identitiessettingspage.h"
100 #include "settingspages/networkssettingspage.h"
101 #include "settingspages/notificationssettingspage.h"
102
103 MainWin::MainWin(QWidget *parent)
104 #ifdef HAVE_KDE
105   : KMainWindow(parent),
106   _kHelpMenu(new KHelpMenu(this, KGlobal::mainComponent().aboutData())),
107 #else
108   : QMainWindow(parent),
109 #endif
110     coreLagLabel(new QLabel()),
111     sslLabel(new QLabel()),
112     msgProcessorStatusWidget(new MsgProcessorStatusWidget()),
113     _titleSetter(this),
114     _awayLog(0)
115 {
116 #ifdef Q_WS_WIN
117   dwTickCount = 0;
118 #endif
119
120   QtUiSettings uiSettings;
121   QString style = uiSettings.value("Style", QString()).toString();
122   if(!style.isEmpty()) {
123     QApplication::setStyle(style);
124   }
125
126   QApplication::setQuitOnLastWindowClosed(false);
127
128   setWindowTitle("Quassel IRC");
129   setWindowIconText("Quassel IRC");
130   updateIcon();
131
132   installEventFilter(new JumpKeyHandler(this));
133
134   QtUiApplication* app = qobject_cast<QtUiApplication*> qApp;
135   connect(app, SIGNAL(saveStateToSession(const QString&)), SLOT(saveStateToSession(const QString&)));
136   connect(app, SIGNAL(saveStateToSessionSettings(SessionSettings&)), SLOT(saveStateToSessionSettings(SessionSettings&)));
137 }
138
139 void MainWin::init() {
140   QtUiSettings s;
141   if(s.value("MainWinSize").isValid())
142     resize(s.value("MainWinSize").toSize());
143   else
144     resize(QSize(800, 500));
145
146   connect(QApplication::instance(), SIGNAL(aboutToQuit()), SLOT(saveLayout()));
147   connect(Client::instance(), SIGNAL(networkCreated(NetworkId)), SLOT(clientNetworkCreated(NetworkId)));
148   connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), SLOT(clientNetworkRemoved(NetworkId)));
149   connect(Client::messageModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
150            SLOT(messagesInserted(const QModelIndex &, int, int)));
151   connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showChannelList(NetworkId)), SLOT(showChannelList(NetworkId)));
152
153   // Setup Dock Areas
154   setDockNestingEnabled(true);
155   setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
156   setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
157   setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
158   setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
159
160   // Order is sometimes important
161   setupActions();
162   setupBufferWidget();
163   setupMenus();
164   setupTopicWidget();
165   setupChatMonitor();
166   setupNickWidget();
167   setupInputWidget();
168   setupStatusBar();
169   setupToolBars();
170   setupSystray();
171   setupTitleSetter();
172
173 #ifndef HAVE_KDE
174   QtUi::registerNotificationBackend(new TaskbarNotificationBackend(this));
175   QtUi::registerNotificationBackend(new SystrayNotificationBackend(this));
176 #  ifdef HAVE_PHONON
177   QtUi::registerNotificationBackend(new PhononNotificationBackend(this));
178 #  endif
179 #  ifdef HAVE_DBUS
180   QtUi::registerNotificationBackend(new DesktopNotificationBackend(this));
181 #  endif
182
183 #else /* HAVE_KDE */
184   QtUi::registerNotificationBackend(new KNotificationBackend(this));
185 #endif /* HAVE_KDE */
186
187   // restore mainwin state
188   restoreState(s.value("MainWinState").toByteArray());
189
190   // restore locked state of docks
191   QtUi::actionCollection("General")->action("LockLayout")->setChecked(s.value("LockLayout", false).toBool());
192
193   setDisconnectedState();  // Disable menus and stuff
194
195   show();
196
197   if(Quassel::runMode() != Quassel::Monolithic) {
198     showCoreConnectionDlg(true); // autoconnect if appropriate
199   } else {
200     startInternalCore();
201   }
202 }
203
204 MainWin::~MainWin() {
205   QtUiSettings s;
206   s.setValue("MainWinSize", size());
207   s.setValue("MainWinPos", pos());
208   s.setValue("MainWinState", saveState());
209 }
210
211 void MainWin::updateIcon() {
212 #ifdef Q_WS_MAC
213   const int size = 128;
214 #else
215   const int size = 48;
216 #endif
217
218   QPixmap icon;
219   if(Client::isConnected())
220     icon = DesktopIcon("quassel", size);
221   else
222     icon = DesktopIcon("quassel_inactive", size);
223   setWindowIcon(icon);
224   qApp->setWindowIcon(icon);
225 }
226
227 void MainWin::setupActions() {
228   ActionCollection *coll = QtUi::actionCollection("General");
229   // File
230   coll->addAction("ConnectCore", new Action(SmallIcon("network-connect"), tr("&Connect to Core..."), coll,
231                                              this, SLOT(showCoreConnectionDlg())));
232   coll->addAction("DisconnectCore", new Action(SmallIcon("network-disconnect"), tr("&Disconnect from Core"), coll,
233                                                 Client::instance(), SLOT(disconnectFromCore())));
234   coll->addAction("CoreInfo", new Action(SmallIcon("help-about"), tr("Core &Info..."), coll,
235                                           this, SLOT(showCoreInfoDlg())));
236   coll->addAction("ConfigureNetworks", new Action(SmallIcon("configure"), tr("Configure &Networks..."), coll,
237                                               this, SLOT(on_actionConfigureNetworks_triggered())));
238   coll->addAction("Quit", new Action(SmallIcon("application-exit"), tr("&Quit"), coll,
239                                       qApp, SLOT(quit()), tr("Ctrl+Q")));
240
241   // View
242   coll->addAction("ConfigureBufferViews", new Action(tr("&Configure Buffer Views..."), coll,
243                                              this, SLOT(on_actionConfigureViews_triggered())));
244
245   QAction *lockAct = coll->addAction("LockLayout", new Action(tr("&Lock Layout"), coll));
246   lockAct->setCheckable(true);
247   connect(lockAct, SIGNAL(toggled(bool)), SLOT(on_actionLockLayout_toggled(bool)));
248
249   coll->addAction("ToggleSearchBar", new Action(SmallIcon("edit-find"), tr("Show &Search Bar"), coll,
250                                                 0, 0, QKeySequence::Find))->setCheckable(true);
251   coll->addAction("ShowAwayLog", new Action(tr("Show Away Log"), coll,
252                                             this, SLOT(showAwayLog())));
253   coll->addAction("ToggleStatusBar", new Action(tr("Show Status &Bar"), coll,
254                                                  0, 0))->setCheckable(true);
255
256   // Settings
257   coll->addAction("ConfigureQuassel", new Action(SmallIcon("configure"), tr("&Configure Quassel..."), coll,
258                                                   this, SLOT(showSettingsDlg()), tr("F7")));
259
260   // Help
261   coll->addAction("AboutQuassel", new Action(SmallIcon("quassel"), tr("&About Quassel"), coll,
262                                               this, SLOT(showAboutDlg())));
263   coll->addAction("AboutQt", new Action(QIcon(":/pics/qt-logo.png"), tr("About &Qt"), coll,
264                                          qApp, SLOT(aboutQt())));
265   coll->addAction("DebugNetworkModel", new Action(SmallIcon("tools-report-bug"), tr("Debug &NetworkModel"), coll,
266                                        this, SLOT(on_actionDebugNetworkModel_triggered())));
267   coll->addAction("DebugBufferViewOverlay", new Action(SmallIcon("tools-report-bug"), tr("Debug &BufferViewOverlay"), coll,
268                                        this, SLOT(on_actionDebugBufferViewOverlay_triggered())));
269   coll->addAction("DebugMessageModel", new Action(SmallIcon("tools-report-bug"), tr("Debug &MessageModel"), coll,
270                                        this, SLOT(on_actionDebugMessageModel_triggered())));
271   coll->addAction("DebugLog", new Action(SmallIcon("tools-report-bug"), tr("Debug &Log"), coll,
272                                        this, SLOT(on_actionDebugLog_triggered())));
273 }
274
275 void MainWin::setupMenus() {
276   ActionCollection *coll = QtUi::actionCollection("General");
277
278   _fileMenu = menuBar()->addMenu(tr("&File"));
279
280   static const QStringList coreActions = QStringList()
281     << "ConnectCore" << "DisconnectCore" << "CoreInfo";
282
283   QAction *coreAction;
284   foreach(QString actionName, coreActions) {
285     coreAction = coll->action(actionName);
286     _fileMenu->addAction(coreAction);
287     flagRemoteCoreOnly(coreAction);
288   }
289   flagRemoteCoreOnly(_fileMenu->addSeparator());
290
291   _networksMenu = _fileMenu->addMenu(tr("&Networks"));
292   _networksMenu->addAction(coll->action("ConfigureNetworks"));
293   _networksMenu->addSeparator();
294   _fileMenu->addSeparator();
295   _fileMenu->addAction(coll->action("Quit"));
296
297   _viewMenu = menuBar()->addMenu(tr("&View"));
298   _bufferViewsMenu = _viewMenu->addMenu(tr("&Buffer Views"));
299   _bufferViewsMenu->addAction(coll->action("ConfigureBufferViews"));
300   _toolbarMenu = _viewMenu->addMenu(tr("&Toolbars"));
301   _viewMenu->addSeparator();
302   _viewMenu->addAction(coll->action("ToggleSearchBar"));
303
304   coreAction = coll->action("ShowAwayLog");
305   flagRemoteCoreOnly(coreAction);
306   _viewMenu->addAction(coreAction);
307
308   _viewMenu->addAction(coll->action("ToggleStatusBar"));
309   _viewMenu->addSeparator();
310   _viewMenu->addAction(coll->action("LockLayout"));
311
312   _settingsMenu = menuBar()->addMenu(tr("&Settings"));
313 #ifdef HAVE_KDE
314   _settingsMenu->addAction(KStandardAction::keyBindings(this, SLOT(showShortcutsDlg()), this));
315   _settingsMenu->addAction(KStandardAction::configureNotifications(this, SLOT(showNotificationsDlg()), this));
316 #endif
317   _settingsMenu->addAction(coll->action("ConfigureQuassel"));
318
319   _helpMenu = menuBar()->addMenu(tr("&Help"));
320   _helpMenu->addAction(coll->action("AboutQuassel"));
321 #ifndef HAVE_KDE
322   _helpMenu->addAction(coll->action("AboutQt"));
323 #else
324   _helpMenu->addAction(KStandardAction::aboutKDE(_kHelpMenu, SLOT(aboutKDE()), this));
325 #endif
326   _helpMenu->addSeparator();
327   _helpDebugMenu = _helpMenu->addMenu(SmallIcon("tools-report-bug"), tr("Debug"));
328   _helpDebugMenu->addAction(coll->action("DebugNetworkModel"));
329   _helpDebugMenu->addAction(coll->action("DebugBufferViewOverlay"));
330   _helpDebugMenu->addAction(coll->action("DebugMessageModel"));
331   _helpDebugMenu->addAction(coll->action("DebugLog"));
332 }
333
334 void MainWin::setupBufferWidget() {
335   _bufferWidget = new BufferWidget(this);
336   _bufferWidget->setModel(Client::bufferModel());
337   _bufferWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
338   setCentralWidget(_bufferWidget);
339 }
340
341 void MainWin::addBufferView(int bufferViewConfigId) {
342   addBufferView(Client::bufferViewManager()->clientBufferViewConfig(bufferViewConfigId));
343 }
344
345 void MainWin::addBufferView(ClientBufferViewConfig *config) {
346   if(!config)
347     return;
348
349   config->setLocked(QtUiSettings().value("LockLayout", false).toBool());
350   BufferViewDock *dock = new BufferViewDock(config, this);
351
352   //create the view and initialize it's filter
353   BufferView *view = new BufferView(dock);
354   view->setFilteredModel(Client::bufferModel(), config);
355   view->installEventFilter(_inputWidget->inputLine()); // for key presses
356   view->show();
357
358   Client::bufferModel()->synchronizeView(view);
359
360   dock->setWidget(view);
361   dock->show();
362
363   addDockWidget(Qt::LeftDockWidgetArea, dock);
364   _bufferViewsMenu->addAction(dock->toggleViewAction());
365
366   connect(dock->toggleViewAction(), SIGNAL(toggled(bool)), this, SLOT(bufferViewToggled(bool)));
367   _bufferViews.append(dock);
368 }
369
370 void MainWin::removeBufferView(int bufferViewConfigId) {
371   QVariant actionData;
372   BufferViewDock *dock;
373   foreach(QAction *action, _bufferViewsMenu->actions()) {
374     actionData = action->data();
375     if(!actionData.isValid())
376       continue;
377
378     dock = qobject_cast<BufferViewDock *>(action->parent());
379     if(dock && actionData.toInt() == bufferViewConfigId) {
380       removeAction(action);
381       dock->deleteLater();
382     }
383   }
384 }
385
386 void MainWin::bufferViewToggled(bool enabled) {
387   QAction *action = qobject_cast<QAction *>(sender());
388   Q_ASSERT(action);
389   BufferViewDock *dock = qobject_cast<BufferViewDock *>(action->parent());
390   Q_ASSERT(dock);
391   if(enabled) {
392     Client::bufferViewOverlay()->addView(dock->bufferViewId());
393     BufferViewConfig *config = dock->config();
394     if(config && config->isInitialized()) {
395       BufferIdList buffers;
396       if(config->networkId().isValid()) {
397         foreach(BufferId bufferId, config->bufferList()) {
398           if(Client::networkModel()->networkId(bufferId) == config->networkId())
399             buffers << bufferId;
400         }
401         foreach(BufferId bufferId, config->temporarilyRemovedBuffers().toList()) {
402           if(Client::networkModel()->networkId(bufferId) == config->networkId())
403             buffers << bufferId;
404         }
405       } else {
406         buffers = BufferIdList::fromSet(config->bufferList().toSet() + config->temporarilyRemovedBuffers());
407       }
408       Client::backlogManager()->checkForBacklog(buffers);
409     }
410   } else {
411     Client::bufferViewOverlay()->removeView(dock->bufferViewId());
412   }
413 }
414
415 BufferView *MainWin::allBuffersView() const {
416   // "All Buffers" is always the first dock created
417   if(_bufferViews.count() > 0)
418     return _bufferViews[0]->bufferView();
419   return 0;
420 }
421
422 void MainWin::showNotificationsDlg() {
423   SettingsPageDlg dlg(new NotificationsSettingsPage(this), this);
424   dlg.exec();
425 }
426
427 void MainWin::on_actionConfigureNetworks_triggered() {
428   SettingsPageDlg dlg(new NetworksSettingsPage(this), this);
429   dlg.exec();
430 }
431
432 void MainWin::on_actionConfigureViews_triggered() {
433   SettingsPageDlg dlg(new BufferViewSettingsPage(this), this);
434   dlg.exec();
435 }
436
437 void MainWin::on_actionLockLayout_toggled(bool lock) {
438   QList<VerticalDock *> docks = findChildren<VerticalDock *>();
439   foreach(VerticalDock *dock, docks) {
440     dock->showTitle(!lock);
441   }
442   if(Client::bufferViewManager()) {
443     foreach(ClientBufferViewConfig *config, Client::bufferViewManager()->clientBufferViewConfigs()) {
444       config->setLocked(lock);
445     }
446   }
447   QtUiSettings().setValue("LockLayout", lock);
448 }
449
450 void MainWin::setupNickWidget() {
451   // create nick dock
452   NickListDock *nickDock = new NickListDock(tr("Nicks"), this);
453   nickDock->setObjectName("NickDock");
454   nickDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
455
456   _nickListWidget = new NickListWidget(nickDock);
457   nickDock->setWidget(_nickListWidget);
458
459   addDockWidget(Qt::RightDockWidgetArea, nickDock);
460   _viewMenu->addAction(nickDock->toggleViewAction());
461   nickDock->toggleViewAction()->setText(tr("Show Nick List"));
462
463   // See NickListDock::NickListDock();
464   // connect(nickDock->toggleViewAction(), SIGNAL(triggered(bool)), nickListWidget, SLOT(showWidget(bool)));
465
466   // attach the NickListWidget to the BufferModel and the default selection
467   _nickListWidget->setModel(Client::bufferModel());
468   _nickListWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
469 }
470
471 void MainWin::setupChatMonitor() {
472   VerticalDock *dock = new VerticalDock(tr("Chat Monitor"), this);
473   dock->setObjectName("ChatMonitorDock");
474
475   ChatMonitorFilter *filter = new ChatMonitorFilter(Client::messageModel(), this);
476   ChatMonitorView *chatView = new ChatMonitorView(filter, this);
477   chatView->show();
478   dock->setWidget(chatView);
479   dock->hide();
480
481   addDockWidget(Qt::TopDockWidgetArea, dock, Qt::Vertical);
482   _viewMenu->addAction(dock->toggleViewAction());
483   dock->toggleViewAction()->setText(tr("Show Chat Monitor"));
484 }
485
486 void MainWin::setupInputWidget() {
487   VerticalDock *dock = new VerticalDock(tr("Inputline"), this);
488   dock->setObjectName("InputDock");
489
490   _inputWidget = new InputWidget(dock);
491   dock->setWidget(_inputWidget);
492
493   addDockWidget(Qt::BottomDockWidgetArea, dock);
494
495   _viewMenu->addAction(dock->toggleViewAction());
496   dock->toggleViewAction()->setText(tr("Show Input Line"));
497
498   _inputWidget->setModel(Client::bufferModel());
499   _inputWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
500
501   _bufferWidget->setFocusProxy(_inputWidget);
502
503   _inputWidget->inputLine()->installEventFilter(_bufferWidget);
504 }
505
506 void MainWin::setupTopicWidget() {
507   VerticalDock *dock = new VerticalDock(tr("Topic"), this);
508   dock->setObjectName("TopicDock");
509   TopicWidget *topicwidget = new TopicWidget(dock);
510
511   dock->setWidget(topicwidget);
512
513   topicwidget->setModel(Client::bufferModel());
514   topicwidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
515
516   addDockWidget(Qt::TopDockWidgetArea, dock, Qt::Vertical);
517
518   _viewMenu->addAction(dock->toggleViewAction());
519   dock->toggleViewAction()->setText(tr("Show Topic Line"));
520 }
521
522 void MainWin::setupTitleSetter() {
523   _titleSetter.setModel(Client::bufferModel());
524   _titleSetter.setSelectionModel(Client::bufferModel()->standardSelectionModel());
525 }
526
527 void MainWin::setupStatusBar() {
528   // MessageProcessor progress
529   statusBar()->addPermanentWidget(msgProcessorStatusWidget);
530
531   // Core Lag:
532   updateLagIndicator();
533   statusBar()->addPermanentWidget(coreLagLabel);
534   coreLagLabel->hide();
535   connect(Client::signalProxy(), SIGNAL(lagUpdated(int)), this, SLOT(updateLagIndicator(int)));
536
537   // SSL indicator
538   sslLabel->setPixmap(QPixmap());
539   statusBar()->addPermanentWidget(sslLabel);
540   sslLabel->hide();
541
542   QAction *showStatusbar = QtUi::actionCollection("General")->action("ToggleStatusBar");
543
544   QtUiSettings uiSettings;
545
546   bool enabled = uiSettings.value("ShowStatusBar", QVariant(true)).toBool();
547   showStatusbar->setChecked(enabled);
548   enabled ? statusBar()->show() : statusBar()->hide();
549
550   connect(showStatusbar, SIGNAL(toggled(bool)), statusBar(), SLOT(setVisible(bool)));
551   connect(showStatusbar, SIGNAL(toggled(bool)), this, SLOT(saveStatusBarStatus(bool)));
552 }
553
554 void MainWin::saveStatusBarStatus(bool enabled) {
555   QtUiSettings uiSettings;
556   uiSettings.setValue("ShowStatusBar", enabled);
557 }
558
559 void MainWin::setupSystray() {
560   _systemTray = new SystemTray(this);
561 }
562
563 void MainWin::setupToolBars() {
564   connect(_bufferWidget, SIGNAL(currentChanged(QModelIndex)),
565           QtUi::toolBarActionProvider(), SLOT(currentBufferChanged(QModelIndex)));
566   connect(_nickListWidget, SIGNAL(nickSelectionChanged(QModelIndexList)),
567           QtUi::toolBarActionProvider(), SLOT(nickSelectionChanged(QModelIndexList)));
568
569 #ifdef Q_WS_MAC
570   setUnifiedTitleAndToolBarOnMac(true);
571 #endif
572   _mainToolBar = addToolBar(tr("Main Toolbar"));
573   _mainToolBar->setObjectName("MainToolBar");
574
575   QtUi::toolBarActionProvider()->addActions(_mainToolBar, ToolBarActionProvider::MainToolBar);
576   _toolbarMenu->addAction(_mainToolBar->toggleViewAction());
577
578   //_nickToolBar = addToolBar("User");
579   //_nickToolBar->setObjectName("NickToolBar");
580   //QtUi::toolBarActionProvider()->addActions(_nickToolBar, ToolBarActionProvider::NickToolBar);
581
582 #ifdef HAVE_KDE
583   _mainToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
584   //_nickToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
585 #endif
586 }
587
588 void MainWin::connectedToCore() {
589   Q_CHECK_PTR(Client::bufferViewManager());
590   connect(Client::bufferViewManager(), SIGNAL(bufferViewConfigAdded(int)), this, SLOT(addBufferView(int)));
591   connect(Client::bufferViewManager(), SIGNAL(bufferViewConfigDeleted(int)), this, SLOT(removeBufferView(int)));
592   connect(Client::bufferViewManager(), SIGNAL(initDone()), this, SLOT(loadLayout()));
593
594   setConnectedState();
595 }
596
597 void MainWin::setConnectedState() {
598   ActionCollection *coll = QtUi::actionCollection("General");
599
600   coll->action("ConnectCore")->setEnabled(false);
601   coll->action("DisconnectCore")->setEnabled(true);
602   coll->action("CoreInfo")->setEnabled(true);
603
604   foreach(QAction *action, _fileMenu->actions()) {
605     if(isRemoteCoreOnly(action))
606       action->setVisible(!Client::internalCore());
607   }
608
609   disconnect(Client::backlogManager(), SIGNAL(updateProgress(int, int)), msgProcessorStatusWidget, SLOT(setProgress(int, int)));
610   disconnect(Client::backlogManager(), SIGNAL(messagesRequested(const QString &)), this, SLOT(showStatusBarMessage(const QString &)));
611   disconnect(Client::backlogManager(), SIGNAL(messagesProcessed(const QString &)), this, SLOT(showStatusBarMessage(const QString &)));
612   if(!Client::internalCore()) {
613     connect(Client::backlogManager(), SIGNAL(updateProgress(int, int)), msgProcessorStatusWidget, SLOT(setProgress(int, int)));
614     connect(Client::backlogManager(), SIGNAL(messagesRequested(const QString &)), this, SLOT(showStatusBarMessage(const QString &)));
615     connect(Client::backlogManager(), SIGNAL(messagesProcessed(const QString &)), this, SLOT(showStatusBarMessage(const QString &)));
616   }
617
618   // _viewMenu->setEnabled(true);
619   if(!Client::internalCore())
620     statusBar()->showMessage(tr("Connected to core."));
621   else
622     statusBar()->clearMessage();
623
624   if(Client::signalProxy()->isSecure()) {
625     sslLabel->setPixmap(SmallIcon("security-high"));
626   } else {
627     sslLabel->setPixmap(SmallIcon("security-low"));
628   }
629
630   sslLabel->setVisible(!Client::internalCore());
631   coreLagLabel->setVisible(!Client::internalCore());
632   updateIcon();
633   systemTray()->setState(SystemTray::Active);
634
635   if(Client::networkIds().isEmpty()) {
636     IrcConnectionWizard *wizard = new IrcConnectionWizard(this, Qt::Sheet);
637     wizard->show();
638   }
639 }
640
641 void MainWin::loadLayout() {
642   QtUiSettings s;
643   int accountId = Client::currentCoreAccount().toInt();
644   restoreState(s.value(QString("MainWinState-%1").arg(accountId)).toByteArray(), accountId);
645 }
646
647 void MainWin::saveLayout() {
648   QtUiSettings s;
649   int accountId = Client::currentCoreAccount().toInt();
650   if(accountId > 0) s.setValue(QString("MainWinState-%1").arg(accountId) , saveState(accountId));
651 }
652
653 void MainWin::updateLagIndicator(int lag) {
654   QString text = tr("Core Lag: %1");
655   if(lag == -1)
656     text = text.arg('-');
657   else
658     text = text.arg("%1 msec").arg(lag);
659   coreLagLabel->setText(text);
660 }
661
662 void MainWin::disconnectedFromCore() {
663   // save core specific layout and remove bufferviews;
664   saveLayout();
665   QVariant actionData;
666   BufferViewDock *dock;
667   foreach(QAction *action, _bufferViewsMenu->actions()) {
668     actionData = action->data();
669     if(!actionData.isValid())
670       continue;
671
672     dock = qobject_cast<BufferViewDock *>(action->parent());
673     if(dock && actionData.toInt() != -1) {
674       removeAction(action);
675       dock->deleteLater();
676     }
677   }
678   QtUiSettings s;
679   restoreState(s.value("MainWinState").toByteArray());
680   setDisconnectedState();
681 }
682
683 void MainWin::setDisconnectedState() {
684   ActionCollection *coll = QtUi::actionCollection("General");
685   //ui.menuCore->setEnabled(false);
686   coll->action("ConnectCore")->setEnabled(true);
687   coll->action("DisconnectCore")->setEnabled(false);
688   coll->action("CoreInfo")->setEnabled(false);
689   //_viewMenu->setEnabled(false);
690   statusBar()->showMessage(tr("Not connected to core."));
691   sslLabel->setPixmap(QPixmap());
692   sslLabel->hide();
693   updateLagIndicator();
694   coreLagLabel->hide();
695   if(msgProcessorStatusWidget)
696     msgProcessorStatusWidget->setProgress(0, 0);
697   updateIcon();
698   systemTray()->setState(SystemTray::Inactive);
699 }
700
701 void MainWin::startInternalCore() {
702   ClientSyncer *syncer = new ClientSyncer();
703   Client::registerClientSyncer(syncer);
704   connect(syncer, SIGNAL(syncFinished()), syncer, SLOT(deleteLater()), Qt::QueuedConnection);
705   syncer->useInternalCore();
706 }
707
708 void MainWin::showCoreConnectionDlg(bool autoConnect) {
709   CoreConnectDlg(autoConnect, this).exec();
710 }
711
712 void MainWin::showChannelList(NetworkId netId) {
713   ChannelListDlg *channelListDlg = new ChannelListDlg();
714
715   if(!netId.isValid()) {
716     QAction *action = qobject_cast<QAction *>(sender());
717     if(action)
718       netId = action->data().value<NetworkId>();
719   }
720
721   channelListDlg->setAttribute(Qt::WA_DeleteOnClose);
722   channelListDlg->setNetwork(netId);
723   channelListDlg->show();
724 }
725
726 void MainWin::showCoreInfoDlg() {
727   CoreInfoDlg(this).exec();
728 }
729
730 void MainWin::showAwayLog() {
731   if(_awayLog)
732     return;
733   AwayLogFilter *filter = new AwayLogFilter(Client::messageModel());
734   _awayLog = new AwayLogView(filter, 0);
735   filter->setParent(_awayLog);
736   connect(_awayLog, SIGNAL(destroyed()), this, SLOT(awayLogDestroyed()));
737   _awayLog->setAttribute(Qt::WA_DeleteOnClose);
738   _awayLog->show();
739 }
740
741 void MainWin::awayLogDestroyed() {
742   _awayLog = 0;
743 }
744
745 void MainWin::showSettingsDlg() {
746   SettingsDlg *dlg = new SettingsDlg();
747
748   //Category: Appearance
749   dlg->registerSettingsPage(new AppearanceSettingsPage(dlg)); //General
750   dlg->registerSettingsPage(new ColorSettingsPage(dlg));
751   dlg->registerSettingsPage(new HighlightSettingsPage(dlg));
752   dlg->registerSettingsPage(new NotificationsSettingsPage(dlg));
753   dlg->registerSettingsPage(new BacklogSettingsPage(dlg));
754   dlg->registerSettingsPage(new BufferViewSettingsPage(dlg));
755   dlg->registerSettingsPage(new ChatMonitorSettingsPage(dlg));
756
757   //Category: Misc
758   dlg->registerSettingsPage(new GeneralSettingsPage(dlg));
759   dlg->registerSettingsPage(new IdentitiesSettingsPage(dlg));
760   dlg->registerSettingsPage(new NetworksSettingsPage(dlg));
761   dlg->registerSettingsPage(new AliasesSettingsPage(dlg));
762
763   dlg->show();
764 }
765
766 void MainWin::showAboutDlg() {
767   AboutDlg(this).exec();
768 }
769
770 #ifdef HAVE_KDE
771 void MainWin::showShortcutsDlg() {
772   KShortcutsDialog::configure(QtUi::actionCollection("General"), KShortcutsEditor::LetterShortcutsDisallowed);
773 }
774 #endif
775
776 void MainWin::closeEvent(QCloseEvent *event) {
777   QtUiSettings s;
778   QtUiApplication* app = qobject_cast<QtUiApplication*> qApp;
779   Q_ASSERT(app);
780   if(!app->aboutToQuit() && s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnClose").toBool()) {
781     toggleMinimizedToTray();
782     event->ignore();
783   } else {
784     event->accept();
785     QApplication::quit();
786   }
787 }
788
789 bool MainWin::event(QEvent *event) {
790   if(event->type() == QEvent::WindowActivate)
791     QtUi::closeNotifications();
792   return QMainWindow::event(event);
793 }
794
795 void MainWin::changeEvent(QEvent *event) {
796   if(event->type() == QEvent::WindowStateChange) {
797     if(windowState() & Qt::WindowMinimized) {
798       QtUiSettings s;
799       if(s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnMinimize").toBool()) {
800         hideToTray();
801         event->accept();
802         return;
803       }
804     }
805   }
806
807 #ifdef Q_WS_WIN
808   else if(event->type() == QEvent::ActivationChange)
809     dwTickCount = GetTickCount();  // needed for toggleMinimizedToTray()
810 #endif
811
812   event->ignore();
813 }
814
815 void MainWin::hideToTray() {
816   if(!systemTray()->isSystemTrayAvailable()) {
817     qWarning() << Q_FUNC_INFO << "was called with no SystemTray available!";
818     return;
819   }
820   hide();
821   systemTray()->setIconVisible();
822 }
823
824 void MainWin::toggleMinimizedToTray() {
825 #ifdef Q_WS_WIN
826   // the problem is that we lose focus when the systray icon is activated
827   // and we don't know the former active window
828   // therefore we watch for activation event and use our stopwatch :)
829   // courtesy: KSystemTrayIcon
830   if(GetTickCount() - dwTickCount >= 300)
831     // we weren't active in the last 300ms -> activate
832     forceActivated();
833   else
834     hideToTray();
835
836 #else
837
838   if(!isVisible() || isMinimized())
839     // restore
840     forceActivated();
841   else
842     hideToTray();
843
844 #endif
845 }
846
847 void MainWin::forceActivated() {
848 #ifdef Q_WS_X11
849   // Bypass focus stealing prevention
850   QX11Info::setAppUserTime(QX11Info::appTime());
851 #endif
852
853   if(windowState() & Qt::WindowMinimized) {
854     // restore
855     setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
856   }
857
858   move(frameGeometry().topLeft()); // avoid placement policies
859   show();
860   raise();
861   activateWindow();
862 }
863
864 void MainWin::messagesInserted(const QModelIndex &parent, int start, int end) {
865   Q_UNUSED(parent);
866
867   bool hasFocus = QApplication::activeWindow() != 0;
868
869   for(int i = start; i <= end; i++) {
870     QModelIndex idx = Client::messageModel()->index(i, ChatLineModel::ContentsColumn);
871     if(!idx.isValid()) {
872       qDebug() << "MainWin::messagesInserted(): Invalid model index!";
873       continue;
874     }
875     Message::Flags flags = (Message::Flags)idx.data(ChatLineModel::FlagsRole).toInt();
876     if(flags.testFlag(Message::Backlog) || flags.testFlag(Message::Self))
877       continue;
878     flags |= Message::Backlog;  // we only want to trigger a highlight once!
879     Client::messageModel()->setData(idx, (int)flags, ChatLineModel::FlagsRole);
880
881     BufferId bufId = idx.data(ChatLineModel::BufferIdRole).value<BufferId>();
882     BufferInfo::Type bufType = Client::networkModel()->bufferType(bufId);
883
884     if(hasFocus && bufId == _bufferWidget->currentBuffer())
885       continue;
886
887     if(flags & Message::Highlight || bufType == BufferInfo::QueryBuffer) {
888       QModelIndex senderIdx = Client::messageModel()->index(i, ChatLineModel::SenderColumn);
889       QString sender = senderIdx.data(ChatLineModel::EditRole).toString();
890       QString contents = idx.data(ChatLineModel::DisplayRole).toString();
891       AbstractNotificationBackend::NotificationType type;
892
893       if(bufType == BufferInfo::QueryBuffer && !hasFocus)
894         type = AbstractNotificationBackend::PrivMsg;
895       else if(bufType == BufferInfo::QueryBuffer && hasFocus)
896         type = AbstractNotificationBackend::PrivMsgFocused;
897       else if(flags & Message::Highlight && !hasFocus)
898         type = AbstractNotificationBackend::Highlight;
899       else
900         type = AbstractNotificationBackend::HighlightFocused;
901
902       QtUi::invokeNotification(bufId, type, sender, contents);
903     }
904   }
905 }
906
907 void MainWin::clientNetworkCreated(NetworkId id) {
908   const Network *net = Client::network(id);
909   QAction *act = new QAction(net->networkName(), this);
910   act->setObjectName(QString("NetworkAction-%1").arg(id.toInt()));
911   act->setData(QVariant::fromValue<NetworkId>(id));
912   connect(net, SIGNAL(updatedRemotely()), this, SLOT(clientNetworkUpdated()));
913   connect(act, SIGNAL(triggered()), this, SLOT(connectOrDisconnectFromNet()));
914
915   QAction *beforeAction = 0;
916   foreach(QAction *action, _networksMenu->actions()) {
917     if(!action->data().isValid())  // ignore stock actions
918       continue;
919     if(net->networkName().localeAwareCompare(action->text()) < 0) {
920       beforeAction = action;
921       break;
922     }
923   }
924   _networksMenu->insertAction(beforeAction, act);
925 }
926
927 void MainWin::clientNetworkUpdated() {
928   const Network *net = qobject_cast<const Network *>(sender());
929   if(!net)
930     return;
931
932   QAction *action = findChild<QAction *>(QString("NetworkAction-%1").arg(net->networkId().toInt()));
933   if(!action)
934     return;
935
936   action->setText(net->networkName());
937
938   switch(net->connectionState()) {
939   case Network::Initialized:
940     action->setIcon(SmallIcon("network-connect"));
941     break;
942   case Network::Disconnected:
943     action->setIcon(SmallIcon("network-disconnect"));
944     break;
945   default:
946     action->setIcon(SmallIcon("network-wired"));
947   }
948 }
949
950 void MainWin::clientNetworkRemoved(NetworkId id) {
951   QAction *action = findChild<QAction *>(QString("NetworkAction-%1").arg(id.toInt()));
952   if(!action)
953     return;
954
955   action->deleteLater();
956 }
957
958 void MainWin::connectOrDisconnectFromNet() {
959   QAction *act = qobject_cast<QAction *>(sender());
960   if(!act) return;
961   const Network *net = Client::network(act->data().value<NetworkId>());
962   if(!net) return;
963   if(net->connectionState() == Network::Disconnected) net->requestConnect();
964   else net->requestDisconnect();
965 }
966
967 void MainWin::on_actionDebugNetworkModel_triggered() {
968   QTreeView *view = new QTreeView;
969   view->setAttribute(Qt::WA_DeleteOnClose);
970   view->setWindowTitle("Debug NetworkModel View");
971   view->setModel(Client::networkModel());
972   view->setColumnWidth(0, 250);
973   view->setColumnWidth(1, 250);
974   view->setColumnWidth(2, 80);
975   view->resize(610, 300);
976   view->show();
977 }
978
979 void MainWin::on_actionDebugBufferViewOverlay_triggered() {
980   QTreeView *view = new QTreeView;
981   view->setAttribute(Qt::WA_DeleteOnClose);
982   view->setWindowTitle("Debug BufferViewOverlay View");
983   BufferViewOverlayFilter *filter = new BufferViewOverlayFilter(Client::bufferModel(), Client::bufferViewOverlay());
984   filter->setParent(view);
985   view->setModel(filter);
986   view->setColumnWidth(0, 250);
987   view->setColumnWidth(1, 250);
988   view->setColumnWidth(2, 80);
989   view->resize(610, 300);
990   view->show();
991 }
992
993 void MainWin::on_actionDebugMessageModel_triggered() {
994   QTableView *view = new QTableView(0);
995   DebugMessageModelFilter *filter = new DebugMessageModelFilter(view);
996   filter->setSourceModel(Client::messageModel());
997   view->setModel(filter);
998   view->setAttribute(Qt::WA_DeleteOnClose, true);
999   view->verticalHeader()->hide();
1000   view->horizontalHeader()->setStretchLastSection(true);
1001   view->show();
1002 }
1003
1004 void MainWin::on_actionDebugLog_triggered() {
1005   DebugLogWidget *logWidget = new DebugLogWidget(0);
1006   logWidget->show();
1007 }
1008
1009 void MainWin::saveStateToSession(const QString &sessionId) {
1010   return;
1011   SessionSettings s(sessionId);
1012
1013   s.setValue("MainWinSize", size());
1014   s.setValue("MainWinPos", pos());
1015   s.setValue("MainWinState", saveState());
1016 }
1017
1018 void MainWin::saveStateToSessionSettings(SessionSettings & s)
1019 {
1020   s.setValue("MainWinSize", size());
1021   s.setValue("MainWinPos", pos());
1022   s.setValue("MainWinState", saveState());
1023 }
1024
1025 void MainWin::showStatusBarMessage(const QString &message) {
1026   statusBar()->showMessage(message, 10000);
1027 }
1028