fixing BR #261 (making fields removable from the chatmonitor). Changes effect current...
[quassel.git] / src / qtui / mainwin.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 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 #include "aboutdlg.h"
23 #include "bufferview.h"
24 #include "bufferviewconfig.h"
25 #include "bufferviewfilter.h"
26 #include "bufferviewmanager.h"
27 #include "channellistdlg.h"
28 #include "chatlinemodel.h"
29 #include "chatmonitorfilter.h"
30 #include "chatmonitorview.h"
31 #include "chatview.h"
32 #include "client.h"
33 #include "clientbacklogmanager.h"
34 #include "coreinfodlg.h"
35 #include "coreconnectdlg.h"
36 #include "msgprocessorstatuswidget.h"
37 #include "qtuimessageprocessor.h"
38 #include "networkmodel.h"
39 #include "buffermodel.h"
40 #include "nicklistwidget.h"
41 #include "settingsdlg.h"
42 #include "settingspagedlg.h"
43 #include "signalproxy.h"
44 #include "topicwidget.h"
45 #include "inputwidget.h"
46 #include "irclistmodel.h"
47 #include "verticaldock.h"
48 #include "uisettings.h"
49 #include "util.h"
50 #include "qtuisettings.h"
51 #include "jumpkeyhandler.h"
52
53 #include "selectionmodelsynchronizer.h"
54 #include "mappedselectionmodel.h"
55
56 #include "settingspages/aliasessettingspage.h"
57 #include "settingspages/appearancesettingspage.h"
58 #include "settingspages/bufferviewsettingspage.h"
59 #include "settingspages/colorsettingspage.h"
60 #include "settingspages/fontssettingspage.h"
61 #include "settingspages/generalsettingspage.h"
62 #include "settingspages/highlightsettingspage.h"
63 #include "settingspages/identitiessettingspage.h"
64 #include "settingspages/networkssettingspage.h"
65 #include "settingspages/notificationssettingspage.h"
66
67 #include "debugconsole.h"
68 #include "global.h"
69 #include "qtuistyle.h"
70
71 MainWin::MainWin(QtUi *_gui, QWidget *parent)
72   : QMainWindow(parent),
73     gui(_gui),
74     coreLagLabel(new QLabel()),
75     sslLabel(new QLabel()),
76     msgProcessorStatusWidget(new MsgProcessorStatusWidget()),
77     _titleSetter(this),
78     systray(new QSystemTrayIcon(this)),
79     activeTrayIcon(":/icons/quassel-icon-active.png"),
80     onlineTrayIcon(":/icons/quassel-icon.png"),
81     offlineTrayIcon(":/icons/quassel-icon-offline.png"),
82     trayIconActive(false),
83     timer(new QTimer(this)),
84     channelListDlg(new ChannelListDlg(this)),
85     settingsDlg(new SettingsDlg(this)),
86     debugConsole(new DebugConsole(this))
87 {
88   UiSettings uiSettings;
89   loadTranslation(uiSettings.value("Locale", QLocale::system()).value<QLocale>());
90
91   QString style = uiSettings.value("Style", QString("")).toString();
92   if(style != "") {
93     QApplication::setStyle(style);
94   }
95
96   ui.setupUi(this);
97   setWindowTitle("Quassel IRC");
98   setWindowIcon(offlineTrayIcon);
99   qApp->setWindowIcon(offlineTrayIcon);
100   systray->setIcon(offlineTrayIcon);
101   setWindowIconText("Quassel IRC");
102
103   statusBar()->showMessage(tr("Waiting for core..."));
104
105   installEventFilter(new JumpKeyHandler(this));
106
107 #ifdef HAVE_DBUS
108   desktopNotifications = new org::freedesktop::Notifications(
109                             "org.freedesktop.Notifications",
110                             "/org/freedesktop/Notifications",
111                             QDBusConnection::sessionBus(), this);
112   notificationId = 0;
113   connect(desktopNotifications, SIGNAL(NotificationClosed(uint, uint)), this, SLOT(desktopNotificationClosed(uint, uint)));
114   connect(desktopNotifications, SIGNAL(ActionInvoked(uint, const QString&)), this, SLOT(desktopNotificationInvoked(uint, const QString&)));
115 #endif
116 }
117
118 void MainWin::init() {
119   QtUiSettings s;
120   if(s.value("MainWinSize").isValid())
121     resize(s.value("MainWinSize").toSize());
122   else
123     resize(QSize(800, 500));
124
125   Client::signalProxy()->attachSignal(this, SIGNAL(requestBacklog(BufferInfo, QVariant, QVariant)));
126
127   connect(QApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(saveLayout()));
128
129   connect(Client::instance(), SIGNAL(networkCreated(NetworkId)), this, SLOT(clientNetworkCreated(NetworkId)));
130   connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), this, SLOT(clientNetworkRemoved(NetworkId)));
131
132   show();
133
134   statusBar()->showMessage(tr("Not connected to core."));
135
136   // DOCK OPTIONS
137   setDockNestingEnabled(true);
138
139   setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
140   setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
141   setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
142   setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
143
144   // setup stuff...
145   setupMenus();
146   setupViews();
147   setupNickWidget();
148   setupTopicWidget();
149   setupChatMonitor();
150   setupInputWidget();
151   setupStatusBar();
152   setupSystray();
153
154   setupSettingsDlg();
155
156   // restore mainwin state
157   restoreState(s.value("MainWinState").toByteArray());
158
159   // restore locked state of docks
160   ui.actionLockDockPositions->setChecked(s.value("LockDocks", false).toBool());
161
162
163   setDisconnectedState();  // Disable menus and stuff
164   showCoreConnectionDlg(true); // autoconnect if appropriate
165
166   // attach the BufferWidget to the BufferModel and the default selection
167   ui.bufferWidget->setModel(Client::bufferModel());
168   ui.bufferWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
169
170   _titleSetter.setModel(Client::bufferModel());
171   _titleSetter.setSelectionModel(Client::bufferModel()->standardSelectionModel());
172 }
173
174 MainWin::~MainWin() {
175   QtUiSettings s;
176   s.setValue("MainWinSize", size());
177   s.setValue("MainWinPos", pos());
178   s.setValue("MainWinState", saveState());
179 }
180
181 void MainWin::setupMenus() {
182   connect(ui.actionConnectCore, SIGNAL(triggered()), this, SLOT(showCoreConnectionDlg()));
183   connect(ui.actionDisconnectCore, SIGNAL(triggered()), Client::instance(), SLOT(disconnectFromCore()));
184   connect(ui.actionCoreInfo, SIGNAL(triggered()), this, SLOT(showCoreInfoDlg()));
185   connect(ui.actionQuit, SIGNAL(triggered()), QCoreApplication::instance(), SLOT(quit()));
186   connect(ui.actionSettingsDlg, SIGNAL(triggered()), this, SLOT(showSettingsDlg()));
187   // connect(ui.actionDebug_Console, SIGNAL(triggered()), this, SLOT(showDebugConsole()));
188   connect(ui.actionAboutQuassel, SIGNAL(triggered()), this, SLOT(showAboutDlg()));
189   connect(ui.actionAboutQt, SIGNAL(triggered()), QApplication::instance(), SLOT(aboutQt()));
190 }
191
192 void MainWin::setupViews() {
193   addBufferView();
194 }
195
196 void MainWin::addBufferView(int bufferViewConfigId) {
197   addBufferView(Client::bufferViewManager()->bufferViewConfig(bufferViewConfigId));
198 }
199
200 void MainWin::addBufferView(BufferViewConfig *config) {
201   BufferViewDock *dock;
202   if(config)
203     dock = new BufferViewDock(config, this);
204   else
205     dock = new BufferViewDock(this);
206
207   //create the view and initialize it's filter
208   BufferView *view = new BufferView(dock);
209   view->setFilteredModel(Client::bufferModel(), config);
210   view->show();
211
212   connect(&view->showChannelList, SIGNAL(triggered()), this, SLOT(showChannelList()));
213
214   Client::bufferModel()->synchronizeView(view);
215
216   dock->setWidget(view);
217   dock->show();
218
219   addDockWidget(Qt::LeftDockWidgetArea, dock);
220   ui.menuBufferViews->addAction(dock->toggleViewAction());
221
222   _netViews.append(dock);
223 }
224
225 void MainWin::removeBufferView(int bufferViewConfigId) {
226   QVariant actionData;
227   BufferViewDock *dock;
228   foreach(QAction *action, ui.menuBufferViews->actions()) {
229     actionData = action->data();
230     if(!actionData.isValid())
231       continue;
232
233     dock = qobject_cast<BufferViewDock *>(action->parent());
234     if(dock && actionData.toInt() == bufferViewConfigId) {
235       removeAction(action);
236       dock->deleteLater();
237     }
238   }
239 }
240
241 void MainWin::setupSettingsDlg() {
242   //Category: Appearance
243   settingsDlg->registerSettingsPage(new ColorSettingsPage(settingsDlg));
244   settingsDlg->registerSettingsPage(new FontsSettingsPage(settingsDlg));
245   settingsDlg->registerSettingsPage(new AppearanceSettingsPage(settingsDlg)); //General
246   //Category: Behaviour
247   settingsDlg->registerSettingsPage(new GeneralSettingsPage(settingsDlg));
248   settingsDlg->registerSettingsPage(new HighlightSettingsPage(settingsDlg));
249   settingsDlg->registerSettingsPage(new AliasesSettingsPage(settingsDlg));
250   settingsDlg->registerSettingsPage(new NotificationsSettingsPage(settingsDlg));
251   //Category: General
252   settingsDlg->registerSettingsPage(new IdentitiesSettingsPage(settingsDlg));
253   settingsDlg->registerSettingsPage(new NetworksSettingsPage(settingsDlg));
254   settingsDlg->registerSettingsPage(new BufferViewSettingsPage(settingsDlg));
255 }
256
257 void MainWin::on_actionEditNetworks_triggered() {
258   SettingsPageDlg dlg(new NetworksSettingsPage(this), this);
259   dlg.exec();
260 }
261
262 void MainWin::on_actionManageViews_triggered() {
263   SettingsPageDlg dlg(new BufferViewSettingsPage(this), this);
264   dlg.exec();
265 }
266
267 void MainWin::on_actionLockDockPositions_toggled(bool lock) {
268   QList<VerticalDock *> docks = findChildren<VerticalDock *>();
269   foreach(VerticalDock *dock, docks) {
270     dock->showTitle(!lock);
271   }
272   QtUiSettings().setValue("LockDocks", lock);
273 }
274
275 void MainWin::setupNickWidget() {
276   // create nick dock
277   NickListDock *nickDock = new NickListDock(tr("Nicks"), this);
278   nickDock->setObjectName("NickDock");
279   nickDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
280
281   nickListWidget = new NickListWidget(nickDock);
282   nickDock->setWidget(nickListWidget);
283
284   addDockWidget(Qt::RightDockWidgetArea, nickDock);
285   ui.menuViews->addAction(nickDock->toggleViewAction());
286   // See NickListDock::NickListDock();
287   // connect(nickDock->toggleViewAction(), SIGNAL(triggered(bool)), nickListWidget, SLOT(showWidget(bool)));
288
289   // attach the NickListWidget to the BufferModel and the default selection
290   nickListWidget->setModel(Client::bufferModel());
291   nickListWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
292 }
293
294 void MainWin::setupChatMonitor() {
295   VerticalDock *dock = new VerticalDock(tr("Chat Monitor"), this);
296   dock->setObjectName("ChatMonitorDock");
297
298   ChatMonitorFilter *filter = new ChatMonitorFilter(Client::messageModel(), this);
299   ChatMonitorView *chatView = new ChatMonitorView(filter, this);
300   chatView->show();
301   dock->setWidget(chatView);
302   dock->show();
303
304   addDockWidget(Qt::TopDockWidgetArea, dock, Qt::Vertical);
305   ui.menuViews->addAction(dock->toggleViewAction());
306 }
307
308 void MainWin::setupInputWidget() {
309   VerticalDock *dock = new VerticalDock(tr("Inputline"), this);
310   dock->setObjectName("InputDock");
311
312   InputWidget *inputWidget = new InputWidget(dock);
313   dock->setWidget(inputWidget);
314
315   addDockWidget(Qt::BottomDockWidgetArea, dock);
316
317   ui.menuViews->addAction(dock->toggleViewAction());
318
319   inputWidget->setModel(Client::bufferModel());
320   inputWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
321
322   ui.bufferWidget->setFocusProxy(inputWidget);
323 }
324
325 void MainWin::setupTopicWidget() {
326   VerticalDock *dock = new VerticalDock(tr("Topic"), this);
327   dock->setObjectName("TopicDock");
328   TopicWidget *topicwidget = new TopicWidget(dock);
329   connect(topicwidget, SIGNAL(topicChanged(const QString &)), this, SLOT(changeTopic(const QString &)));
330
331   dock->setWidget(topicwidget);
332
333   topicwidget->setModel(Client::bufferModel());
334   topicwidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
335
336   addDockWidget(Qt::TopDockWidgetArea, dock);
337
338   ui.menuViews->addAction(dock->toggleViewAction());
339 }
340
341 void MainWin::setupStatusBar() {
342   // MessageProcessor progress
343   statusBar()->addPermanentWidget(msgProcessorStatusWidget);
344   connect(Client::messageProcessor(), SIGNAL(progressUpdated(int, int)), msgProcessorStatusWidget, SLOT(setProgress(int, int)));
345
346   // Core Lag:
347   updateLagIndicator(0);
348   statusBar()->addPermanentWidget(coreLagLabel);
349   connect(Client::signalProxy(), SIGNAL(lagUpdated(int)), this, SLOT(updateLagIndicator(int)));
350
351   // SSL indicator
352   connect(Client::instance(), SIGNAL(securedConnection()), this, SLOT(securedConnection()));
353   sslLabel->setPixmap(QPixmap());
354   statusBar()->addPermanentWidget(sslLabel);
355
356   ui.menuViews->addSeparator();
357   QAction *showStatusbar = ui.menuViews->addAction(tr("Statusbar"));
358   showStatusbar->setCheckable(true);
359
360   UiSettings uiSettings;
361
362   bool enabled = uiSettings.value("ShowStatusBar", QVariant(true)).toBool();
363   showStatusbar->setChecked(enabled);
364   enabled ? statusBar()->show() : statusBar()->hide();
365
366   connect(showStatusbar, SIGNAL(toggled(bool)), statusBar(), SLOT(setVisible(bool)));
367   connect(showStatusbar, SIGNAL(toggled(bool)), this, SLOT(saveStatusBarStatus(bool)));
368 }
369
370 void MainWin::saveStatusBarStatus(bool enabled) {
371   UiSettings uiSettings;
372   uiSettings.setValue("ShowStatusBar", enabled);
373 }
374
375 void MainWin::setupSystray() {
376   connect(timer, SIGNAL(timeout()), this, SLOT(makeTrayIconBlink()));
377   connect(Client::messageModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
378                             this, SLOT(messagesInserted(const QModelIndex &, int, int)));
379
380   systrayMenu = new QMenu(this);
381   systrayMenu->addAction(ui.actionAboutQuassel);
382   systrayMenu->addSeparator();
383   systrayMenu->addAction(ui.actionConnectCore);
384   systrayMenu->addAction(ui.actionDisconnectCore);
385   systrayMenu->addSeparator();
386   systrayMenu->addAction(ui.actionQuit);
387
388   systray->setContextMenu(systrayMenu);
389
390   UiSettings s;
391   if(s.value("UseSystemTrayIcon", QVariant(true)).toBool()) {
392     systray->show();
393   }
394
395 #ifndef Q_WS_MAC
396   connect(systray, SIGNAL(activated( QSystemTrayIcon::ActivationReason )),
397           this, SLOT(systrayActivated( QSystemTrayIcon::ActivationReason )));
398 #endif
399
400 }
401
402 void MainWin::changeEvent(QEvent *event) {
403   if(event->type() == QEvent::WindowStateChange) {
404     if(windowState() & Qt::WindowMinimized) {
405       UiSettings s;
406       if(s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnMinimize").toBool()) {
407         toggleVisibility();
408         event->ignore();
409       }
410     }
411   }
412 }
413
414 // FIXME this should be made prettier...
415 void MainWin::changeTopic(const QString &topic) {
416   BufferId id = ui.bufferWidget->currentBuffer();
417   if(!id.isValid()) return;
418   Buffer *buffer = Client::buffer(id);
419   if(buffer) Client::userInput(buffer->bufferInfo(), QString("/topic %1").arg(topic));
420 }
421
422 void MainWin::connectedToCore() {
423   Q_CHECK_PTR(Client::bufferViewManager());
424   connect(Client::bufferViewManager(), SIGNAL(bufferViewConfigAdded(int)), this, SLOT(addBufferView(int)));
425   connect(Client::bufferViewManager(), SIGNAL(bufferViewConfigDeleted(int)), this, SLOT(removeBufferView(int)));
426   connect(Client::bufferViewManager(), SIGNAL(initDone()), this, SLOT(loadLayout()));
427
428   foreach(BufferInfo id, Client::allBufferInfos()) {
429     Client::backlogManager()->requestBacklog(id.bufferId(), 500, -1);
430   }
431   setConnectedState();
432 }
433
434 void MainWin::setConnectedState() {
435   //ui.menuCore->setEnabled(true);
436   ui.actionConnectCore->setEnabled(false);
437   ui.actionDisconnectCore->setEnabled(true);
438   ui.actionCoreInfo->setEnabled(true);
439   ui.menuViews->setEnabled(true);
440   ui.bufferWidget->show();
441   statusBar()->showMessage(tr("Connected to core."));
442   setWindowIcon(onlineTrayIcon);
443   qApp->setWindowIcon(onlineTrayIcon);
444   systray->setIcon(onlineTrayIcon);
445   if(sslLabel->width() == 0)
446     sslLabel->setPixmap(QPixmap::fromImage(QImage(":/16x16/status/no-ssl")));
447 }
448
449 void MainWin::loadLayout() {
450   QtUiSettings s;
451   int accountId = Client::currentCoreAccount().toInt();
452   restoreState(s.value(QString("MainWinState-%1").arg(accountId)).toByteArray(), accountId);
453 }
454
455 void MainWin::saveLayout() {
456   QtUiSettings s;
457   int accountId = Client::currentCoreAccount().toInt();
458   if(accountId > 0) s.setValue(QString("MainWinState-%1").arg(accountId) , saveState(accountId));
459 }
460
461 void MainWin::updateLagIndicator(int lag) {
462   coreLagLabel->setText(QString(tr("Core Lag: %1 msec")).arg(lag));
463 }
464
465
466 void MainWin::securedConnection() {
467   // todo: make status bar entry
468   sslLabel->setPixmap(QPixmap::fromImage(QImage(":/16x16/status/ssl")));
469 }
470
471 void MainWin::disconnectedFromCore() {
472   // save core specific layout and remove bufferviews;
473   saveLayout();
474   QVariant actionData;
475   BufferViewDock *dock;
476   foreach(QAction *action, ui.menuBufferViews->actions()) {
477     actionData = action->data();
478     if(!actionData.isValid())
479       continue;
480
481     dock = qobject_cast<BufferViewDock *>(action->parent());
482     if(dock && actionData.toInt() != -1) {
483       removeAction(action);
484       dock->deleteLater();
485     }
486   }
487   QtUiSettings s;
488   restoreState(s.value("MainWinState").toByteArray());
489   setDisconnectedState();
490 }
491
492 void MainWin::setDisconnectedState() {
493   //ui.menuCore->setEnabled(false);
494   ui.actionConnectCore->setEnabled(true);
495   ui.actionDisconnectCore->setEnabled(false);
496   ui.actionCoreInfo->setEnabled(false);
497   ui.menuViews->setEnabled(false);
498   ui.bufferWidget->hide();
499   statusBar()->showMessage(tr("Not connected to core."));
500   setWindowIcon(offlineTrayIcon);
501   qApp->setWindowIcon(offlineTrayIcon);
502   systray->setIcon(offlineTrayIcon);
503   sslLabel->setPixmap(QPixmap());
504 }
505
506 void MainWin::showCoreConnectionDlg(bool autoConnect) {
507   coreConnectDlg = new CoreConnectDlg(this, autoConnect);
508   connect(coreConnectDlg, SIGNAL(finished(int)), this, SLOT(coreConnectionDlgFinished(int)));
509   coreConnectDlg->setModal(true);
510   coreConnectDlg->show();
511 }
512
513 void MainWin::coreConnectionDlgFinished(int /*code*/) {
514   coreConnectDlg->close();
515   //exit(1);
516 }
517
518 void MainWin::showChannelList(NetworkId netId) {
519   if(!netId.isValid()) {
520     QAction *action = qobject_cast<QAction *>(sender());
521     if(action)
522       netId = action->data().value<NetworkId>();
523   }
524   channelListDlg->setNetwork(netId);
525   channelListDlg->show();
526 }
527
528 void MainWin::showCoreInfoDlg() {
529   CoreInfoDlg dlg(this);
530   dlg.exec();
531 }
532
533 void MainWin::showSettingsDlg() {
534   settingsDlg->show();
535 }
536
537 void MainWin::showDebugConsole() {
538   debugConsole->show();
539 }
540
541 void MainWin::showAboutDlg() {
542   AboutDlg dlg(this);
543   dlg.exec();
544 }
545
546 void MainWin::closeEvent(QCloseEvent *event) {
547   UiSettings s;
548   if(s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnClose").toBool()) {
549     toggleVisibility();
550     event->ignore();
551   } else {
552     event->accept();
553   }
554 }
555
556 void MainWin::systrayActivated( QSystemTrayIcon::ActivationReason activationReason) {
557   if(activationReason == QSystemTrayIcon::Trigger) {
558     toggleVisibility();
559   }
560 }
561
562 void MainWin::toggleVisibility() {
563   if(isHidden() /*|| !isActiveWindow()*/) {
564     show();
565     if(isMinimized()) {
566       if(isMaximized())
567         showMaximized();
568       else
569         showNormal();
570     }
571
572     raise();
573     activateWindow();
574     // setFocus(); //Qt::ActiveWindowFocusReason
575
576   } else {
577     if(systray->isSystemTrayAvailable ()) {
578       clearFocus();
579       hide();
580       if(!systray->isVisible()) {
581         systray->show();
582       }
583     } else {
584       lower();
585     }
586   }
587 }
588
589 void MainWin::messagesInserted(const QModelIndex &parent, int start, int end) {
590   Q_UNUSED(parent);
591   if(QApplication::activeWindow() != 0)
592     return;
593
594   // FIXME
595   return;
596
597   for(int i = start; i <= end; i++) {
598     QModelIndex idx = Client::messageModel()->index(i, ChatLineModel::ContentsColumn);
599     if(!idx.isValid()) {
600       qDebug() << "MainWin::messagesInserted(): Invalid model index!";
601       continue;
602     }
603     Message::Flags flags = (Message::Flags)idx.data(ChatLineModel::FlagsRole).toInt();
604     BufferId bufId = idx.data(ChatLineModel::BufferIdRole).value<BufferId>();
605     BufferInfo::Type bufType = Client::networkModel()->bufferType(bufId);
606
607     if(flags & Message::Highlight || bufType == BufferInfo::QueryBuffer) {
608       QString title = Client::networkModel()->networkName(bufId) + " - " + Client::networkModel()->bufferName(bufId);
609
610       // FIXME Don't instantiate this for every highlight...
611       UiSettings uiSettings;
612
613       bool displayBubble = uiSettings.value("NotificationBubble", QVariant(true)).toBool();
614       bool displayDesktop = uiSettings.value("NotificationDesktop", QVariant(true)).toBool();
615       if(displayBubble || displayDesktop) {
616         if(uiSettings.value("DisplayPopupMessages", QVariant(true)).toBool()) {
617           QString text = idx.data(ChatLineModel::DisplayRole).toString();
618           if(displayBubble) displayTrayIconMessage(title, text);
619 #   ifdef HAVE_DBUS
620           if(displayDesktop) sendDesktopNotification(title, text);
621 #   endif
622         }
623         if(uiSettings.value("AnimateTrayIcon", QVariant(true)).toBool()) {
624           QApplication::alert(this);
625           setTrayIconActivity(true);
626         }
627       }
628     }
629   }
630 }
631
632 bool MainWin::event(QEvent *event) {
633   if(event->type() == QEvent::WindowActivate)
634     setTrayIconActivity(false);
635   return QMainWindow::event(event);
636 }
637
638 #ifdef HAVE_DBUS
639
640 /*
641 Using the notification-daemon from Freedesktop's Galago project
642 http://www.galago-project.org/specs/notification/0.9/x408.html#command-notify
643 */
644 void MainWin::sendDesktopNotification(const QString &title, const QString &message) {
645   QStringList actions;
646   QMap<QString, QVariant> hints;
647   UiSettings uiSettings;
648
649   hints["x"] = uiSettings.value("NotificationDesktopHintX", QVariant(0)).toInt(); // Standard hint: x location for the popup to show up
650   hints["y"] = uiSettings.value("NotificationDesktopHintY", QVariant(0)).toInt(); // Standard hint: y location for the popup to show up
651
652   actions << "click" << "Click Me!";
653
654   QDBusReply<uint> reply = desktopNotifications->Notify(
655                 "Quassel", // Application name
656                 notificationId, // ID of previous notification to replace
657                 "", // Icon to display
658                 title, // Summary / Header of the message to display
659                 QString("%1: %2:\n%3").arg(QTime::currentTime().toString()).arg(title).arg(message), // Body of the message to display
660                 actions, // Actions from which the user may choose
661                 hints, // Hints to the server displaying the message
662                 uiSettings.value("NotificationDesktopTimeout", QVariant(5000)).toInt() // Timeout in milliseconds
663         );
664
665   if(!reply.isValid()) {
666     /* ERROR */
667     // could also happen if no notification service runs, so... whatever :)
668     //qDebug() << "Error on sending notification..." << reply.error();
669     return;
670   }
671
672   notificationId = reply.value();
673
674   // qDebug() << "ID: " << notificationId << " Time: " << QTime::currentTime().toString();
675 }
676
677
678 void MainWin::desktopNotificationClosed(uint id, uint reason) {
679   Q_UNUSED(id); Q_UNUSED(reason);
680   // qDebug() << "OID: " << notificationId << " ID: " << id << " Reason: " << reason << " Time: " << QTime::currentTime().toString();
681   notificationId = 0;
682 }
683
684
685 void MainWin::desktopNotificationInvoked(uint id, const QString & action) {
686   Q_UNUSED(id); Q_UNUSED(action);
687   // qDebug() << "OID: " << notificationId << " ID: " << id << " Action: " << action << " Time: " << QTime::currentTime().toString();
688 }
689
690 #endif /* HAVE_DBUS */
691
692 void MainWin::displayTrayIconMessage(const QString &title, const QString &message) {
693   systray->showMessage(title, message);
694 }
695
696 void MainWin::setTrayIconActivity(bool active) {
697   if(active) {
698     if(!timer->isActive())
699       timer->start(500);
700   } else {
701     timer->stop();
702     systray->setIcon(onlineTrayIcon);
703   }
704 }
705
706 void MainWin::makeTrayIconBlink() {
707   if(trayIconActive) {
708     systray->setIcon(onlineTrayIcon);
709     trayIconActive = false;
710   } else {
711     systray->setIcon(activeTrayIcon);
712     trayIconActive = true;
713   }
714 }
715
716 void MainWin::clientNetworkCreated(NetworkId id) {
717   const Network *net = Client::network(id);
718   QAction *act = new QAction(net->networkName(), this);
719   act->setObjectName(QString("NetworkAction-%1").arg(id.toInt()));
720   act->setData(QVariant::fromValue<NetworkId>(id));
721   connect(net, SIGNAL(updatedRemotely()), this, SLOT(clientNetworkUpdated()));
722   connect(act, SIGNAL(triggered()), this, SLOT(connectOrDisconnectFromNet()));
723
724   QAction *beforeAction = 0;
725   foreach(QAction *action, ui.menuNetworks->actions()) {
726     if(action->isSeparator()) {
727       beforeAction = action;
728       break;
729     }
730     if(net->networkName().localeAwareCompare(action->text()) < 0) {
731       beforeAction = action;
732       break;
733     }
734   }
735   Q_CHECK_PTR(beforeAction);
736   ui.menuNetworks->insertAction(beforeAction, act);
737 }
738
739 void MainWin::clientNetworkUpdated() {
740   const Network *net = qobject_cast<const Network *>(sender());
741   if(!net)
742     return;
743
744   QAction *action = findChild<QAction *>(QString("NetworkAction-%1").arg(net->networkId().toInt()));
745   if(!action)
746     return;
747
748   action->setText(net->networkName());
749
750   switch(net->connectionState()) {
751   case Network::Initialized:
752     action->setIcon(QIcon(":/16x16/actions/network-connect"));
753     break;
754   case Network::Disconnected:
755     action->setIcon(QIcon(":/16x16/actions/network-disconnect"));
756     break;
757   default:
758     action->setIcon(QIcon(":/16x16/actions/gear"));
759   }
760 }
761
762 void MainWin::clientNetworkRemoved(NetworkId id) {
763   QAction *action = findChild<QAction *>(QString("NetworkAction-%1").arg(id.toInt()));
764   if(!action)
765     return;
766
767   action->deleteLater();
768 }
769
770 void MainWin::connectOrDisconnectFromNet() {
771   QAction *act = qobject_cast<QAction *>(sender());
772   if(!act) return;
773   const Network *net = Client::network(act->data().value<NetworkId>());
774   if(!net) return;
775   if(net->connectionState() == Network::Disconnected) net->requestConnect();
776   else net->requestDisconnect();
777 }
778
779
780
781 void MainWin::on_actionDebugNetworkModel_triggered(bool) {
782   QTreeView *view = new QTreeView;
783   view->setAttribute(Qt::WA_DeleteOnClose);
784   view->setWindowTitle("Debug NetworkModel View");
785   view->setModel(Client::networkModel());
786   view->setColumnWidth(0, 250);
787   view->setColumnWidth(1, 250);
788   view->setColumnWidth(2, 80);
789   view->resize(610, 300);
790   view->show();
791 }