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