Added tooltip for networkItems and bufferItems
[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 //#define SPUTDEV
21
22 #include "mainwin.h"
23
24 #include "aboutdlg.h"
25 #include "chatwidget.h"
26 #include "bufferview.h"
27 #include "chatline-old.h"
28 #include "client.h"
29 #include "coreconnectdlg.h"
30 #include "networkmodel.h"
31 #include "buffermodel.h"
32 #include "nicklistwidget.h"
33 #include "settingsdlg.h"
34 #include "settingspagedlg.h"
35 #include "signalproxy.h"
36 #include "topicwidget.h"
37 #include "inputwidget.h"
38 #include "verticaldock.h"
39 #include "uisettings.h"
40 #include "qtuisettings.h"
41 #include "jumpkeyhandler.h"
42
43 #include "selectionmodelsynchronizer.h"
44 #include "mappedselectionmodel.h"
45
46 #include "settingspages/colorsettingspage.h"
47 #include "settingspages/fontssettingspage.h"
48 #include "settingspages/generalsettingspage.h"
49 #include "settingspages/identitiessettingspage.h"
50 #include "settingspages/networkssettingspage.h"
51
52
53 #include "debugconsole.h"
54
55 MainWin::MainWin(QtUi *_gui, QWidget *parent)
56   : QMainWindow(parent),
57     gui(_gui),
58     settingsDlg(new SettingsDlg(this)),
59     debugConsole(new DebugConsole(this))
60 {
61   ui.setupUi(this);
62   setWindowTitle("Quassel IRC");
63   setWindowIcon(QIcon(":icons/quassel-icon.png"));
64   setWindowIconText("Quassel IRC");
65
66   statusBar()->showMessage(tr("Waiting for core..."));
67
68   installEventFilter(new JumpKeyHandler(this));
69 }
70
71 void MainWin::init() {
72   QtUiSettings s;
73   resize(s.value("MainWinSize").toSize());
74
75   Client::signalProxy()->attachSignal(this, SIGNAL(requestBacklog(BufferInfo, QVariant, QVariant)));
76
77   connect(Client::instance(), SIGNAL(networkCreated(NetworkId)), this, SLOT(clientNetworkCreated(NetworkId)));
78   connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), this, SLOT(clientNetworkRemoved(NetworkId)));
79   ui.bufferWidget->init();
80   
81   show();
82
83   statusBar()->showMessage(tr("Not connected to core."));
84
85   // DOCK OPTIONS
86   setDockNestingEnabled(true);
87
88   setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
89   setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
90   setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
91   setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
92
93   // setup stuff...
94   setupMenus();
95   setupViews();
96   setupNickWidget();
97   setupChatMonitor();
98   setupInputWidget();
99   setupTopicWidget();
100   setupSystray();
101
102   
103   setupSettingsDlg();
104
105   // restore mainwin state
106   restoreState(s.value("MainWinState").toByteArray());
107
108   disconnectedFromCore();  // Disable menus and stuff
109   showCoreConnectionDlg(true); // autoconnect if appropriate
110
111   // attach the BufferWidget to the PropertyMapper
112   ui.bufferWidget->setModel(Client::bufferModel());
113   ui.bufferWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
114
115 #ifdef SPUTDEV
116   //showSettingsDlg();
117   //showAboutDlg();
118   showNetworkDlg();
119   exit(1);
120 #endif
121
122 }
123
124 MainWin::~MainWin() {
125   QtUiSettings s;
126   s.setValue("MainWinSize", size());
127   s.setValue("MainWinPos", pos());
128   s.setValue("MainWinState", saveState());
129 }
130
131 void MainWin::setupMenus() {
132   connect(ui.actionConnectCore, SIGNAL(triggered()), this, SLOT(showCoreConnectionDlg()));
133   connect(ui.actionDisconnectCore, SIGNAL(triggered()), Client::instance(), SLOT(disconnectFromCore()));
134   connect(ui.actionQuit, SIGNAL(triggered()), QCoreApplication::instance(), SLOT(quit()));
135   //connect(ui.actionNetworkList, SIGNAL(triggered()), this, SLOT(showServerList()));
136   connect(ui.actionSettingsDlg, SIGNAL(triggered()), this, SLOT(showSettingsDlg()));
137   connect(ui.actionDebug_Console, SIGNAL(triggered()), this, SLOT(showDebugConsole()));
138   connect(ui.actionAboutQuassel, SIGNAL(triggered()), this, SLOT(showAboutDlg()));
139   connect(ui.actionAboutQt, SIGNAL(triggered()), QApplication::instance(), SLOT(aboutQt()));
140
141   actionEditNetworks = new QAction(QIcon(":/22x22/actions/configure"), tr("Edit &Networks..."), this);
142   ui.menuNetworks->addAction(actionEditNetworks);
143   connect(actionEditNetworks, SIGNAL(triggered()), this, SLOT(showNetworkDlg()));
144 }
145
146 void MainWin::setupViews() {
147   BufferModel *model = Client::bufferModel();
148
149   addBufferView(tr("All Buffers"), model, BufferViewFilter::AllNets, QList<NetworkId>());
150   addBufferView(tr("All Channels"), model, BufferViewFilter::AllNets|BufferViewFilter::NoQueries|BufferViewFilter::NoServers, QList<NetworkId>());
151   addBufferView(tr("All Queries"), model, BufferViewFilter::AllNets|BufferViewFilter::NoChannels|BufferViewFilter::NoServers, QList<NetworkId>());
152   addBufferView(tr("All Networks"), model, BufferViewFilter::AllNets|BufferViewFilter::NoChannels|BufferViewFilter::NoQueries, QList<NetworkId>());
153   addBufferView(tr("Full Custom"), model, BufferViewFilter::FullCustom, QList<NetworkId>());
154
155   ui.menuViews->addSeparator();
156 }
157
158 QDockWidget *MainWin::addBufferView(const QString &viewname, QAbstractItemModel *model, const BufferViewFilter::Modes &mode, const QList<NetworkId> &nets) {
159   QDockWidget *dock = new QDockWidget(viewname, this);
160   dock->setObjectName(QString("ViewDock-" + viewname)); // should be unique for mainwindow state!
161   dock->setAllowedAreas(Qt::RightDockWidgetArea|Qt::LeftDockWidgetArea);
162
163   //create the view and initialize it's filter
164   BufferView *view = new BufferView(dock);
165   view->show();
166   view->setFilteredModel(model, mode, nets);
167   Client::bufferModel()->synchronizeView(view);
168   dock->setWidget(view);
169   dock->show();
170
171   addDockWidget(Qt::LeftDockWidgetArea, dock);
172   ui.menuViews->addAction(dock->toggleViewAction());
173
174   netViews.append(dock);
175   return dock;
176 }
177
178 void MainWin::setupSettingsDlg() {
179   //Category: Appearance
180   settingsDlg->registerSettingsPage(new ColorSettingsPage(settingsDlg));
181   settingsDlg->registerSettingsPage(new FontsSettingsPage(settingsDlg));
182   //Category: Behaviour
183   settingsDlg->registerSettingsPage(new GeneralSettingsPage(settingsDlg));
184   //Category: General
185   settingsDlg->registerSettingsPage(new IdentitiesSettingsPage(settingsDlg));
186   settingsDlg->registerSettingsPage(new NetworksSettingsPage(settingsDlg));
187
188
189 #ifdef SPUTDEV
190   connect(settingsDlg, SIGNAL(finished(int)), QApplication::instance(), SLOT(quit()));  // FIXME
191 #endif
192 }
193
194 void MainWin::setupNickWidget() {
195   // create nick dock
196   nickDock = new QDockWidget(tr("Nicks"), this);
197   nickDock->setObjectName("NickDock");
198   nickDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
199
200   nickListWidget = new NickListWidget(nickDock);
201   nickDock->setWidget(nickListWidget);
202
203   addDockWidget(Qt::RightDockWidgetArea, nickDock);
204   ui.menuViews->addAction(nickDock->toggleViewAction());
205
206   Client::bufferModel()->mapProperty(0, NetworkModel::BufferIdRole, nickListWidget, "currentBuffer");
207 }
208
209 void MainWin::setupChatMonitor() {
210   VerticalDock *dock = new VerticalDock(tr("Chat Monitor"), this);
211   dock->setObjectName("ChatMonitorDock");
212
213   ChatWidget *chatWidget = new ChatWidget(this);
214   chatWidget->show();
215   dock->setWidget(chatWidget);
216   dock->show();
217
218   Buffer *buf = Client::monitorBuffer();
219   if(!buf)
220     return;
221
222   chatWidget->init(BufferId(0));
223   QList<ChatLine *> lines;
224   QList<AbstractUiMsg *> msgs = buf->contents();
225   foreach(AbstractUiMsg *msg, msgs) {
226     lines.append(dynamic_cast<ChatLine*>(msg));
227   }
228   chatWidget->setContents(lines);
229   connect(buf, SIGNAL(msgAppended(AbstractUiMsg *)), chatWidget, SLOT(appendMsg(AbstractUiMsg *)));
230   connect(buf, SIGNAL(msgPrepended(AbstractUiMsg *)), chatWidget, SLOT(prependMsg(AbstractUiMsg *)));
231
232   addDockWidget(Qt::TopDockWidgetArea, dock, Qt::Vertical);
233   ui.menuViews->addAction(dock->toggleViewAction());
234 }
235
236 void MainWin::setupInputWidget() {
237   VerticalDock *dock = new VerticalDock(tr("Inputline"), this);
238   dock->setObjectName("InputDock");
239
240   InputWidget *inputWidget = new InputWidget(dock);
241   dock->setWidget(inputWidget);
242
243   addDockWidget(Qt::BottomDockWidgetArea, dock);
244
245   ui.menuViews->addAction(dock->toggleViewAction());
246
247   inputWidget->setModel(Client::bufferModel());
248   inputWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
249
250   ui.bufferWidget->setFocusProxy(inputWidget);
251 }
252
253 void MainWin::setupTopicWidget() {
254   VerticalDock *dock = new VerticalDock(tr("Topic"), this);
255   dock->setObjectName("TopicDock");
256   TopicWidget *topicwidget = new TopicWidget(dock);
257   dock->setWidget(topicwidget);
258
259   Client::bufferModel()->mapProperty(1, Qt::DisplayRole, topicwidget, "topic");
260
261   addDockWidget(Qt::TopDockWidgetArea, dock);
262
263   ui.menuViews->addAction(dock->toggleViewAction());
264 }
265
266 void MainWin::setupSystray() {
267   systray = new QSystemTrayIcon(this);
268   systray->setIcon(QIcon(":/icons/quassel-icon.png"));
269 //  systray->setToolTip("left click to minimize the quassel client to tray");
270 //  systray->setToolTip(toolTip);
271
272   systrayMenu = new QMenu(this);
273   systrayMenu->addAction(ui.actionAboutQuassel);
274   systrayMenu->addSeparator();
275   systrayMenu->addAction(ui.actionConnectCore);
276   systrayMenu->addAction(ui.actionDisconnectCore);
277   systrayMenu->addSeparator();
278   systrayMenu->addAction(ui.actionQuit);
279
280   systray->setContextMenu(systrayMenu);
281
282   QtUiSettings s;
283   if(s.value("UseSystemTrayIcon").toBool()) {
284     systray->show();
285   }
286   
287   #ifndef Q_WS_MAC
288   connect(systray, SIGNAL(activated( QSystemTrayIcon::ActivationReason )),
289           this, SLOT(systrayActivated( QSystemTrayIcon::ActivationReason )));
290   #endif
291
292 }
293
294 void MainWin::changeEvent(QEvent *event) {
295   if(event->type() == QEvent::WindowStateChange) {
296     if(windowState() & Qt::WindowMinimized) {
297       QtUiSettings s;
298       if(s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnMinimize").toBool()) {
299         toggleVisibility();
300         event->ignore();
301       }
302     }
303   }
304 }
305
306 void MainWin::connectedToCore() {
307   foreach(BufferInfo id, Client::allBufferInfos()) {
308     emit requestBacklog(id, 1000, -1);
309   }
310
311   ui.menuViews->setEnabled(true);
312   ui.menuCore->setEnabled(true);
313   ui.actionConnectCore->setEnabled(false);
314   ui.actionDisconnectCore->setEnabled(true);
315   //ui.actionNetworkList->setEnabled(true);
316   ui.bufferWidget->show();
317   statusBar()->showMessage(tr("Connected to core."));
318 }
319
320 void MainWin::disconnectedFromCore() {
321   ui.menuViews->setEnabled(false);
322   ui.menuCore->setEnabled(false);
323   ui.actionDisconnectCore->setEnabled(false);
324   //ui.actionNetworkList->setEnabled(false);
325   ui.bufferWidget->hide();
326   ui.actionConnectCore->setEnabled(true);
327   nickListWidget->reset();
328   statusBar()->showMessage(tr("Not connected to core."));
329 }
330
331 AbstractUiMsg *MainWin::layoutMsg(const Message &msg) {
332   return new ChatLine(msg);
333 }
334
335 void MainWin::showCoreConnectionDlg(bool autoConnect) {
336   coreConnectDlg = new CoreConnectDlg(this, autoConnect);
337   connect(coreConnectDlg, SIGNAL(finished(int)), this, SLOT(coreConnectionDlgFinished(int)));
338   coreConnectDlg->setModal(true);
339   coreConnectDlg->show();
340 }
341
342 void MainWin::coreConnectionDlgFinished(int /*code*/) {
343   coreConnectDlg->close();
344   //exit(1);
345 }
346
347 void MainWin::showSettingsDlg() {
348   settingsDlg->show();
349 }
350
351 void MainWin::showDebugConsole() {
352   debugConsole->show();
353 }
354
355 void MainWin::showAboutDlg() {
356   AboutDlg dlg(this);
357   dlg.exec();
358 }
359
360 void MainWin::closeEvent(QCloseEvent *event) {
361   QtUiSettings s;
362   if(s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnClose").toBool()) {
363     toggleVisibility();
364     event->ignore();
365   } else {
366     event->accept();
367   }
368 }
369
370 void MainWin::systrayActivated( QSystemTrayIcon::ActivationReason activationReason) {
371   if (activationReason == QSystemTrayIcon::Trigger) {
372     toggleVisibility();
373   }
374 }
375
376 void MainWin::toggleVisibility() {
377   if(isHidden() /*|| !isActiveWindow()*/) {
378     show();
379     if(isMinimized())
380       if (isMaximized())
381         showMaximized();
382       else
383         showNormal();
384
385     raise();
386     setFocus(Qt::ActiveWindowFocusReason);
387     // activateWindow();
388   } else {
389     if(systray->isSystemTrayAvailable ()) {
390       clearFocus();
391       hide();
392       if(!systray->isVisible()) {
393         systray->show();
394       }
395     } else {
396       lower();
397     }
398   }
399 }
400
401 void MainWin::showNetworkDlg() {
402   SettingsPageDlg dlg(new NetworksSettingsPage(this), this);
403   dlg.exec();
404 }
405
406 void MainWin::clientNetworkCreated(NetworkId id) {
407   const Network *net = Client::network(id);
408   QAction *act = new QAction(net->networkName(), this);
409   act->setData(QVariant::fromValue<NetworkId>(id));
410   connect(net, SIGNAL(updatedRemotely()), this, SLOT(clientNetworkUpdated()));
411   connect(act, SIGNAL(triggered()), this, SLOT(connectOrDisconnectFromNet()));
412   bool inserted = false;
413   for(int i = 0; i < networkActions.count(); i++) {
414     if(net->networkName().localeAwareCompare(networkActions[i]->text()) < 0) {
415       networkActions.insert(i, act);
416       inserted = true;
417       break;
418     }
419   }
420   if(!inserted) networkActions.append(act);
421   ui.menuNetworks->clear();  // why the f*** isn't there a QMenu::insertAction()???
422   foreach(QAction *a, networkActions) ui.menuNetworks->addAction(a);
423   ui.menuNetworks->addSeparator();
424   ui.menuNetworks->addAction(actionEditNetworks);
425 }
426
427 void MainWin::clientNetworkUpdated() {
428   const Network *net = qobject_cast<const Network *>(sender());
429   if(!net) return;
430   foreach(QAction *a, networkActions) {
431     if(a->data().value<NetworkId>() == net->networkId()) {
432       a->setText(net->networkName());
433       if(net->connectionState() == Network::Initialized) {
434         a->setIcon(QIcon(":/16x16/actions/network-connect"));
435         //a->setEnabled(true);
436       } else if(net->connectionState() == Network::Disconnected) {
437         a->setIcon(QIcon(":/16x16/actions/network-disconnect"));
438         //a->setEnabled(true);
439       } else {
440         a->setIcon(QIcon(":/16x16/actions/gear"));
441         //a->setEnabled(false);
442       }
443       return;
444     }
445   }
446 }
447
448 void MainWin::clientNetworkRemoved(NetworkId id) {
449   QList<QAction *>::iterator actionIter = networkActions.begin();;
450   QAction *action;
451   while(actionIter != networkActions.end()) {
452     action = *actionIter;
453     if(action->data().value<NetworkId>() == id) {
454       action->deleteLater();
455       actionIter = networkActions.erase(actionIter);
456     } else
457       actionIter++;
458   }
459 }
460
461 void MainWin::connectOrDisconnectFromNet() {
462   QAction *act = qobject_cast<QAction *>(sender());
463   if(!act) return;
464   const Network *net = Client::network(act->data().value<NetworkId>());
465   if(!net) return;
466   if(net->connectionState() == Network::Disconnected) net->requestConnect();
467   else net->requestDisconnect();
468 }
469