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