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