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