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