d90abf91f374bb9402952f4daf96e76df585d96b
[quassel.git] / gui / mainwin.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 by The Quassel Team                             *
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) any later version.                                   *
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
21 #include <QtGui>
22 #include <QtCore>
23 #include <QSqlDatabase>
24
25 #include "util.h"
26 #include "global.h"
27 #include "message.h"
28 #include "guiproxy.h"
29
30 #include "mainwin.h"
31 #include "buffer.h"
32 #include "networkview.h"
33 #include "serverlist.h"
34 #include "coreconnectdlg.h"
35 #include "settingsdlg.h"
36 #include "settingspages.h"
37
38 LayoutThread *layoutThread;
39
40 MainWin::MainWin() : QMainWindow() {
41   ui.setupUi(this);
42   //widget = 0;
43   qDebug() << "Available DB drivers: " << QSqlDatabase::drivers ();
44   setWindowTitle("Quassel IRC");
45   //setWindowTitle("Κυασελ Εγαρζη");
46   setWindowIcon(QIcon(":/qirc-icon.png"));
47   setWindowIconText("Quassel IRC");
48
49   layoutTimer = new QTimer(this);
50   layoutTimer->setInterval(0);
51   layoutTimer->setSingleShot(false);
52   connect(layoutTimer, SIGNAL(timeout()), this, SLOT(layoutMsg()));
53   //workspace = new QWorkspace(this);
54   //setCentralWidget(workspace);
55   statusBar()->showMessage(tr("Waiting for core..."));
56 }
57
58 void MainWin::init() {
59
60   connect(guiProxy, SIGNAL(csServerState(QString, QVariant)), this, SLOT(recvNetworkState(QString, QVariant)));
61   connect(guiProxy, SIGNAL(csServerConnected(QString)), this, SLOT(networkConnected(QString)));
62   connect(guiProxy, SIGNAL(csServerDisconnected(QString)), this, SLOT(networkDisconnected(QString)));
63   connect(guiProxy, SIGNAL(csDisplayMsg(Message)), this, SLOT(recvMessage(Message)));
64   connect(guiProxy, SIGNAL(csDisplayStatusMsg(QString, QString)), this, SLOT(recvStatusMsg(QString, QString)));
65   connect(guiProxy, SIGNAL(csTopicSet(QString, QString, QString)), this, SLOT(setTopic(QString, QString, QString)));
66   connect(guiProxy, SIGNAL(csNickAdded(QString, QString, VarMap)), this, SLOT(addNick(QString, QString, VarMap)));
67   connect(guiProxy, SIGNAL(csNickRemoved(QString, QString)), this, SLOT(removeNick(QString, QString)));
68   connect(guiProxy, SIGNAL(csNickRenamed(QString, QString, QString)), this, SLOT(renameNick(QString, QString, QString)));
69   connect(guiProxy, SIGNAL(csNickUpdated(QString, QString, VarMap)), this, SLOT(updateNick(QString, QString, VarMap)));
70   connect(guiProxy, SIGNAL(csOwnNickSet(QString, QString)), this, SLOT(setOwnNick(QString, QString)));
71   connect(guiProxy, SIGNAL(csBacklogData(BufferId, QList<QVariant>, bool)), this, SLOT(recvBacklogData(BufferId, QList<QVariant>, bool)));
72   connect(guiProxy, SIGNAL(csUpdateBufferId(BufferId)), this, SLOT(updateBufferId(BufferId)));
73   connect(this, SIGNAL(sendInput(BufferId, QString)), guiProxy, SLOT(gsUserInput(BufferId, QString)));
74   connect(this, SIGNAL(requestBacklog(BufferId, QVariant, QVariant)), guiProxy, SLOT(gsRequestBacklog(BufferId, QVariant, QVariant)));
75
76   //layoutThread = new LayoutThread();
77   //layoutThread->start();
78   //while(!layoutThread->isRunning()) {};
79   ui.bufferWidget->init();
80
81   show();
82   syncToCore();
83   statusBar()->showMessage(tr("Ready."));
84   systray = new QSystemTrayIcon(this);
85   systray->setIcon(QIcon(":/qirc-icon.png"));
86   systray->show();
87
88   serverListDlg = new ServerListDlg(this);
89   serverListDlg->setVisible(serverListDlg->showOnStartup());
90
91   setupSettingsDlg();
92
93   setupMenus();
94   setupViews();
95
96   QSettings s;
97   s.beginGroup("Geometry");
98   //resize(s.value("MainWinSize", QSize(500, 400)).toSize());
99   //move(s.value("MainWinPos", QPoint(50, 50)).toPoint());
100   if(s.contains("MainWinState")) restoreState(s.value("MainWinState").toByteArray());
101   s.endGroup();
102
103   /* make lookups by id faster */
104   foreach(BufferId id, coreBuffers) {
105     bufferIds[id.uid()] = id;  // make lookups by id faster
106     getBuffer(id);             // create all buffers, so we see them in the network views
107     emit requestBacklog(id, -1, -1);  // TODO: use custom settings for backlog request
108   }
109
110   s.beginGroup("Buffers");
111   QString net = s.value("CurrentNetwork", "").toString();
112   QString buf = s.value("CurrentBuffer", "").toString();
113   s.endGroup();
114   /*
115   if(!net.isEmpty()) {
116     if(buffers.contains(net)) {
117       if(buffers[net].contains(buf)) {
118         showBuffer(net, buf);
119       } else {
120         showBuffer(net, "");
121       }
122     }
123   }
124   */
125 }
126
127 MainWin::~MainWin() {
128   //typedef QHash<QString, Buffer*> BufHash;
129   //foreach(BufHash h, buffers.values()) {
130   //  foreach(Buffer *b, h.values()) {
131   //    delete b;
132   //  }
133   //}
134   foreach(Buffer *buf, buffers.values()) delete buf;
135 }
136
137 /* This is implemented in settingspages.cpp */
138 /*
139 void MainWin::setupSettingsDlg() {
140
141 }
142 */
143
144 void MainWin::setupMenus() {
145   connect(ui.actionNetworkList, SIGNAL(triggered()), this, SLOT(showServerList()));
146   connect(ui.actionEditIdentities, SIGNAL(triggered()), serverListDlg, SLOT(editIdentities()));
147   connect(ui.actionSettingsDlg, SIGNAL(triggered()), this, SLOT(showSettingsDlg()));
148   //ui.actionSettingsDlg->setEnabled(false);
149   connect(ui.actionAboutQt, SIGNAL(triggered()), QApplication::instance(), SLOT(aboutQt()));
150   // for debugging
151   connect(ui.actionImportBacklog, SIGNAL(triggered()), this, SLOT(importBacklog()));
152   connect(this, SIGNAL(importOldBacklog()), guiProxy, SLOT(gsImportBacklog()));
153 }
154
155 void MainWin::setupViews() {
156   NetworkView *all = new NetworkView(tr("All Buffers"), NetworkView::AllNets);
157   registerNetView(all);
158   addDockWidget(Qt::LeftDockWidgetArea, all);
159   NetworkView *allchans = new NetworkView(tr("All Channels"), NetworkView::AllNets|NetworkView::NoQueries|NetworkView::NoServers);
160   registerNetView(allchans);
161   addDockWidget(Qt::LeftDockWidgetArea, allchans);
162   NetworkView *allqrys = new NetworkView(tr("All Queries"), NetworkView::AllNets|NetworkView::NoChannels|NetworkView::NoServers);
163   registerNetView(allqrys);
164   addDockWidget(Qt::RightDockWidgetArea, allqrys);
165   NetworkView *allnets = new NetworkView(tr("All Networks"), NetworkView::AllNets|NetworkView::NoChannels|NetworkView::NoQueries);
166   registerNetView(allnets);
167   addDockWidget(Qt::RightDockWidgetArea, allnets);
168
169   ui.menuViews->addSeparator();
170 }
171
172 void MainWin::registerNetView(NetworkView *view) {
173   connect(this, SIGNAL(bufferSelected(Buffer *)), view, SLOT(selectBuffer(Buffer *)));
174   connect(this, SIGNAL(bufferUpdated(Buffer *)), view, SLOT(bufferUpdated(Buffer *)));
175   connect(this, SIGNAL(bufferDestroyed(Buffer *)), view, SLOT(bufferDestroyed(Buffer *)));
176   connect(view, SIGNAL(bufferSelected(Buffer *)), this, SLOT(showBuffer(Buffer *)));
177   view->setBuffers(buffers.values());
178   view->setAllowedAreas(Qt::RightDockWidgetArea|Qt::LeftDockWidgetArea);
179   netViews.append(view);
180   ui.menuViews->addAction(view->toggleViewAction());
181 }
182
183 void MainWin::showServerList() {
184 //  if(!serverListDlg) {
185 //    serverListDlg = new ServerListDlg(this);
186 //  }
187   serverListDlg->show();
188   serverListDlg->raise();
189 }
190
191 void MainWin::showSettingsDlg() {
192   settingsDlg->show();
193 }
194
195 void MainWin::closeEvent(QCloseEvent *event)
196 {
197   //if (userReallyWantsToQuit()) {
198     ui.bufferWidget->saveState();
199     QSettings s;
200     s.beginGroup("Geometry");
201     s.setValue("MainWinSize", size());
202     s.setValue("MainWinPos", pos());
203     s.setValue("MainWinState", saveState());
204     s.endGroup();
205     s.beginGroup("Buffers");
206     //s.setValue("CurrentNetwork", currentNetwork);
207     s.setValue("CurrentBuffer", currentBuffer);
208     s.endGroup();
209     delete systray;
210     event->accept();
211   //} else {
212     //event->ignore();
213   //}
214 }
215
216 void MainWin::showBuffer(BufferId id) {
217   showBuffer(getBuffer(id));
218 }
219
220 void MainWin::showBuffer(Buffer *b) {
221   currentBuffer = b->bufferId().groupId();
222   //emit bufferSelected(b);
223   //qApp->processEvents();
224   ui.bufferWidget->setBuffer(b);
225   emit bufferSelected(b);
226 }
227
228 void MainWin::networkConnected(QString net) {
229   connected[net] = true;
230   //BufferId id = getStatusBufferId(net);
231   //Buffer *b = getBuffer(id);
232   //b->setActive(true);
233   //b->displayMsg(Message(id, Message::Server, tr("Connected.")));  FIXME
234   // TODO buffersUpdated();
235 }
236
237 void MainWin::networkDisconnected(QString net) {
238   //getBuffer(net, "")->setActive(false);
239   foreach(BufferId id, buffers.keys()) {
240     if(id.network() != net) continue;
241     Buffer *b = getBuffer(id);
242     //b->displayMsg(Message(id, Message::Server, tr("Server disconnected."))); FIXME
243     b->setActive(false);
244   }
245   connected[net] = false;
246 }
247
248 void MainWin::updateBufferId(BufferId id) {
249   bufferIds[id.uid()] = id;  // make lookups by id faster
250   getBuffer(id);
251 }
252
253 BufferId MainWin::getBufferId(QString net, QString buf) {
254   foreach(BufferId id, buffers.keys()) {
255     if(id.network() == net && id.buffer() == buf) return id;
256   }
257   Q_ASSERT(false);
258   return BufferId();
259 }
260
261 BufferId MainWin::getStatusBufferId(QString net) {
262   return getBufferId(net, "");
263 }
264
265
266 Buffer * MainWin::getBuffer(BufferId id) {
267   if(!buffers.contains(id)) {
268     Buffer *b = new Buffer(id);
269     b->setOwnNick(ownNick[id.network()]);
270     connect(b, SIGNAL(userInput(BufferId, QString)), this, SLOT(userInput(BufferId, QString)));
271     connect(b, SIGNAL(bufferUpdated(Buffer *)), this, SIGNAL(bufferUpdated(Buffer *)));
272     connect(b, SIGNAL(bufferDestroyed(Buffer *)), this, SIGNAL(bufferDestroyed(Buffer *)));
273     buffers[id] = b;
274     emit bufferUpdated(b);
275   }
276   return buffers[id];
277 }
278
279 void MainWin::recvNetworkState(QString net, QVariant state) {
280   connected[net] = true;
281   setOwnNick(net, state.toMap()["OwnNick"].toString());
282   getBuffer(getStatusBufferId(net))->setActive(true);
283   VarMap t = state.toMap()["Topics"].toMap();
284   VarMap n = state.toMap()["Nicks"].toMap();
285   foreach(QVariant v, t.keys()) {
286     QString buf = v.toString();
287     BufferId id = getBufferId(net, buf);
288     getBuffer(id)->setActive(true);
289     setTopic(net, buf, t[buf].toString());
290   }
291   foreach(QString nick, n.keys()) {
292     addNick(net, nick, n[nick].toMap());
293   }
294 }
295
296 void MainWin::recvMessage(Message msg) {
297   /*
298   Buffer *b;
299   if(msg.flags & Message::PrivMsg) {
300   // query
301     if(msg.flags & Message::Self) b = getBuffer(net, msg.target);
302     else b = getBuffer(net, nickFromMask(msg.sender));
303   } else {
304     b = getBuffer(net, msg.target);
305   }
306   */
307   Buffer *b = getBuffer(msg.buffer);
308   //b->displayMsg(msg);
309   b->appendChatLine(new ChatLine(msg));
310 }
311
312 void MainWin::recvStatusMsg(QString net, QString msg) {
313   //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
314
315 }
316
317 void MainWin::recvBacklogData(BufferId id, QList<QVariant> msgs, bool done) {
318   foreach(QVariant v, msgs) {
319     layoutQueue.append(v.value<Message>());
320   }
321   if(!layoutTimer->isActive()) layoutTimer->start();
322 }
323
324
325 void MainWin::layoutMsg() {
326   if(layoutQueue.count()) {
327     ChatLine *line = new ChatLine(layoutQueue.takeFirst());
328     getBuffer(line->bufferId())->prependChatLine(line);
329   }
330   if(!layoutQueue.count()) layoutTimer->stop();
331 }
332
333 void MainWin::userInput(BufferId id, QString msg) {
334   emit sendInput(id, msg);
335 }
336
337 void MainWin::setTopic(QString net, QString buf, QString topic) {
338   BufferId id = getBufferId(net, buf);
339   if(!connected[id.network()]) return;
340   Buffer *b = getBuffer(id);
341   b->setTopic(topic);
342   //if(!b->isActive()) {
343   //  b->setActive(true);
344   //  buffersUpdated();
345   //}
346 }
347
348 void MainWin::addNick(QString net, QString nick, VarMap props) {
349   if(!connected[net]) return;
350   nicks[net][nick] = props;
351   VarMap chans = props["Channels"].toMap();
352   QStringList c = chans.keys();
353   foreach(QString bufname, c) {
354     getBuffer(getBufferId(net, bufname))->addNick(nick, props);
355   }
356 }
357
358 void MainWin::renameNick(QString net, QString oldnick, QString newnick) {
359   if(!connected[net]) return;
360   QStringList chans = nicks[net][oldnick]["Channels"].toMap().keys();
361   foreach(QString c, chans) {
362     getBuffer(getBufferId(net, c))->renameNick(oldnick, newnick);
363   }
364   nicks[net][newnick] = nicks[net].take(oldnick);
365 }
366
367 void MainWin::updateNick(QString net, QString nick, VarMap props) {
368   if(!connected[net]) return;
369   QStringList oldchans = nicks[net][nick]["Channels"].toMap().keys();
370   QStringList newchans = props["Channels"].toMap().keys();
371   foreach(QString c, newchans) {
372     if(oldchans.contains(c)) getBuffer(getBufferId(net, c))->updateNick(nick, props);
373     else getBuffer(getBufferId(net, c))->addNick(nick, props);
374   }
375   foreach(QString c, oldchans) {
376     if(!newchans.contains(c)) getBuffer(getBufferId(net, c))->removeNick(nick);
377   }
378   nicks[net][nick] = props;
379 }
380
381 void MainWin::removeNick(QString net, QString nick) {
382   if(!connected[net]) return;
383   VarMap chans = nicks[net][nick]["Channels"].toMap();
384   foreach(QString bufname, chans.keys()) {
385     getBuffer(getBufferId(net, bufname))->removeNick(nick);
386   }
387   nicks[net].remove(nick);
388 }
389
390 void MainWin::setOwnNick(QString net, QString nick) {
391   if(!connected[net]) return;
392   ownNick[net] = nick;
393   foreach(BufferId id, buffers.keys()) {
394     if(id.network() == net) {
395       buffers[id]->setOwnNick(nick);
396     }
397   }
398 }
399
400 void MainWin::importBacklog() {
401   if(QMessageBox::warning(this, "Import old backlog?", "Do you want to import your old file-based backlog into new the backlog database?<br>"
402                                 "<b>This will permanently delete the contents of your database!</b>",
403                                 QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes) {
404     emit importOldBacklog();
405   }
406 }