7ccd5336ec116c1a5af147194b6bf0178cde3c66
[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   connect(topicwidget, SIGNAL(topicChanged(const QString &)), this, SLOT(changeTopic(const QString &)));
258
259   dock->setWidget(topicwidget);
260
261   Client::bufferModel()->mapProperty(1, Qt::DisplayRole, topicwidget, "topic");
262
263   addDockWidget(Qt::TopDockWidgetArea, dock);
264
265   ui.menuViews->addAction(dock->toggleViewAction());
266 }
267
268 void MainWin::setupSystray() {
269   systray = new QSystemTrayIcon(this);
270   systray->setIcon(QIcon(":/icons/quassel-icon.png"));
271 //  systray->setToolTip("left click to minimize the quassel client to tray");
272 //  systray->setToolTip(toolTip);
273
274   systrayMenu = new QMenu(this);
275   systrayMenu->addAction(ui.actionAboutQuassel);
276   systrayMenu->addSeparator();
277   systrayMenu->addAction(ui.actionConnectCore);
278   systrayMenu->addAction(ui.actionDisconnectCore);
279   systrayMenu->addSeparator();
280   systrayMenu->addAction(ui.actionQuit);
281
282   systray->setContextMenu(systrayMenu);
283
284   QtUiSettings s;
285   if(s.value("UseSystemTrayIcon", QVariant(true)).toBool()) {
286     systray->show();
287   }
288   
289   #ifndef Q_WS_MAC
290   connect(systray, SIGNAL(activated( QSystemTrayIcon::ActivationReason )),
291           this, SLOT(systrayActivated( QSystemTrayIcon::ActivationReason )));
292   #endif
293
294 }
295
296 void MainWin::changeEvent(QEvent *event) {
297   if(event->type() == QEvent::WindowStateChange) {
298     if(windowState() & Qt::WindowMinimized) {
299       QtUiSettings s;
300       if(s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnMinimize").toBool()) {
301         toggleVisibility();
302         event->ignore();
303       }
304     }
305   }
306 }
307
308 // FIXME this should be made prettier...
309 void MainWin::changeTopic(const QString &topic) {
310   BufferId id = ui.bufferWidget->currentBuffer();
311   if(!id.isValid()) return;
312   Buffer *buffer = Client::buffer(id);
313   if(buffer) Client::userInput(buffer->bufferInfo(), QString("/topic %1").arg(topic));
314 }
315
316 void MainWin::connectedToCore() {
317   foreach(BufferInfo id, Client::allBufferInfos()) {
318     emit requestBacklog(id, 1000, -1);
319   }
320
321   ui.menuViews->setEnabled(true);
322   ui.menuCore->setEnabled(true);
323   ui.actionConnectCore->setEnabled(false);
324   ui.actionDisconnectCore->setEnabled(true);
325   //ui.actionNetworkList->setEnabled(true);
326   ui.bufferWidget->show();
327   statusBar()->showMessage(tr("Connected to core."));
328 }
329
330 void MainWin::disconnectedFromCore() {
331   ui.menuViews->setEnabled(false);
332   ui.menuCore->setEnabled(false);
333   ui.actionDisconnectCore->setEnabled(false);
334   //ui.actionNetworkList->setEnabled(false);
335   ui.bufferWidget->hide();
336   ui.actionConnectCore->setEnabled(true);
337   nickListWidget->reset();
338   statusBar()->showMessage(tr("Not connected to core."));
339 }
340
341 AbstractUiMsg *MainWin::layoutMsg(const Message &msg) {
342   return new ChatLine(msg);
343 }
344
345 void MainWin::showCoreConnectionDlg(bool autoConnect) {
346   coreConnectDlg = new CoreConnectDlg(this, autoConnect);
347   connect(coreConnectDlg, SIGNAL(finished(int)), this, SLOT(coreConnectionDlgFinished(int)));
348   coreConnectDlg->setModal(true);
349   coreConnectDlg->show();
350 }
351
352 void MainWin::coreConnectionDlgFinished(int /*code*/) {
353   coreConnectDlg->close();
354   //exit(1);
355 }
356
357 void MainWin::showSettingsDlg() {
358   settingsDlg->show();
359 }
360
361 void MainWin::showDebugConsole() {
362   debugConsole->show();
363 }
364
365 void MainWin::showAboutDlg() {
366   AboutDlg dlg(this);
367   dlg.exec();
368 }
369
370 void MainWin::closeEvent(QCloseEvent *event) {
371   QtUiSettings s;
372   if(s.value("UseSystemTrayIcon").toBool() && s.value("MinimizeOnClose").toBool()) {
373     toggleVisibility();
374     event->ignore();
375   } else {
376     event->accept();
377   }
378 }
379
380 void MainWin::systrayActivated( QSystemTrayIcon::ActivationReason activationReason) {
381   if (activationReason == QSystemTrayIcon::Trigger) {
382     toggleVisibility();
383   }
384 }
385
386 void MainWin::toggleVisibility() {
387   if(isHidden() /*|| !isActiveWindow()*/) {
388     show();
389     if(isMinimized())
390       if (isMaximized())
391         showMaximized();
392       else
393         showNormal();
394
395     raise();
396     setFocus(Qt::ActiveWindowFocusReason);
397     // activateWindow();
398   } else {
399     if(systray->isSystemTrayAvailable ()) {
400       clearFocus();
401       hide();
402       if(!systray->isVisible()) {
403         systray->show();
404       }
405     } else {
406       lower();
407     }
408   }
409 }
410
411 void MainWin::showNetworkDlg() {
412   SettingsPageDlg dlg(new NetworksSettingsPage(this), this);
413   dlg.exec();
414 }
415
416 void MainWin::clientNetworkCreated(NetworkId id) {
417   const Network *net = Client::network(id);
418   QAction *act = new QAction(net->networkName(), this);
419   act->setData(QVariant::fromValue<NetworkId>(id));
420   connect(net, SIGNAL(updatedRemotely()), this, SLOT(clientNetworkUpdated()));
421   connect(act, SIGNAL(triggered()), this, SLOT(connectOrDisconnectFromNet()));
422   bool inserted = false;
423   for(int i = 0; i < networkActions.count(); i++) {
424     if(net->networkName().localeAwareCompare(networkActions[i]->text()) < 0) {
425       networkActions.insert(i, act);
426       inserted = true;
427       break;
428     }
429   }
430   if(!inserted) networkActions.append(act);
431   ui.menuNetworks->clear();  // why the f*** isn't there a QMenu::insertAction()???
432   foreach(QAction *a, networkActions) ui.menuNetworks->addAction(a);
433   ui.menuNetworks->addSeparator();
434   ui.menuNetworks->addAction(actionEditNetworks);
435 }
436
437 void MainWin::clientNetworkUpdated() {
438   const Network *net = qobject_cast<const Network *>(sender());
439   if(!net) return;
440   foreach(QAction *a, networkActions) {
441     if(a->data().value<NetworkId>() == net->networkId()) {
442       a->setText(net->networkName());
443       if(net->connectionState() == Network::Initialized) {
444         a->setIcon(QIcon(":/16x16/actions/network-connect"));
445         //a->setEnabled(true);
446       } else if(net->connectionState() == Network::Disconnected) {
447         a->setIcon(QIcon(":/16x16/actions/network-disconnect"));
448         //a->setEnabled(true);
449       } else {
450         a->setIcon(QIcon(":/16x16/actions/gear"));
451         //a->setEnabled(false);
452       }
453       return;
454     }
455   }
456 }
457
458 void MainWin::clientNetworkRemoved(NetworkId id) {
459   QList<QAction *>::iterator actionIter = networkActions.begin();;
460   QAction *action;
461   while(actionIter != networkActions.end()) {
462     action = *actionIter;
463     if(action->data().value<NetworkId>() == id) {
464       action->deleteLater();
465       actionIter = networkActions.erase(actionIter);
466     } else
467       actionIter++;
468   }
469 }
470
471 void MainWin::connectOrDisconnectFromNet() {
472   QAction *act = qobject_cast<QAction *>(sender());
473   if(!act) return;
474   const Network *net = Client::network(act->data().value<NetworkId>());
475   if(!net) return;
476   if(net->connectionState() == Network::Disconnected) net->requestConnect();
477   else net->requestDisconnect();
478 }
479