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