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