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