Setting status buffer active on connection (by creating a BufferId for it in Core
[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 "bufferviewwidget.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   BufferTreeModel *model = new BufferTreeModel(this); // FIXME Where is the delete for that? :p
157   connect(model, SIGNAL(bufferSelected(Buffer *)), this, SLOT(showBuffer(Buffer *)));
158   connect(this, SIGNAL(bufferSelected(Buffer *)), model, SLOT(selectBuffer(Buffer *)));
159   connect(this, SIGNAL(bufferUpdated(Buffer *)), model, SLOT(bufferUpdated(Buffer *)));
160   connect(this, SIGNAL(bufferActivity(Buffer::ActivityLevel, Buffer *)), model, SLOT(bufferActivity(Buffer::ActivityLevel, Buffer *)));
161   
162   BufferViewDock *all = new BufferViewDock(model, tr("All Buffers"), BufferViewFilter::AllNets);
163   registerBufferViewDock(all);
164   
165   BufferViewDock *allchans = new BufferViewDock(model, tr("All Channels"), BufferViewFilter::AllNets|BufferViewFilter::NoQueries|BufferViewFilter::NoServers);
166   registerBufferViewDock(allchans);
167   
168   BufferViewDock *allqrys = new BufferViewDock(model, tr("All Queries"), BufferViewFilter::AllNets|BufferViewFilter::NoChannels|BufferViewFilter::NoServers);
169   registerBufferViewDock(allqrys);
170
171   
172   BufferViewDock *allnets = new BufferViewDock(model, tr("All Networks"), BufferViewFilter::AllNets|BufferViewFilter::NoChannels|BufferViewFilter::NoQueries);
173   registerBufferViewDock(allnets);
174
175
176   ui.menuViews->addSeparator();
177 }
178
179 void MainWin::registerBufferViewDock(BufferViewDock *dock) {
180   addDockWidget(Qt::LeftDockWidgetArea, dock);
181   dock->setAllowedAreas(Qt::RightDockWidgetArea|Qt::LeftDockWidgetArea);
182   netViews.append(dock);
183   ui.menuViews->addAction(dock->toggleViewAction());
184   
185   /*
186   connect(this, SIGNAL(bufferSelected(Buffer *)), view, SLOT(selectBuffer(Buffer *)));
187   connect(this, SIGNAL(bufferDestroyed(Buffer *)), view, SLOT(bufferDestroyed(Buffer *)));
188   connect(view, SIGNAL(bufferSelected(Buffer *)), this, SLOT(showBuffer(Buffer *)));
189   view->setBuffers(buffers.values());
190    */
191 }
192
193 void MainWin::showServerList() {
194 //  if(!serverListDlg) {
195 //    serverListDlg = new ServerListDlg(this);
196 //  }
197   serverListDlg->show();
198   serverListDlg->raise();
199 }
200
201 void MainWin::showSettingsDlg() {
202   settingsDlg->show();
203 }
204
205 void MainWin::closeEvent(QCloseEvent *event)
206 {
207   //if (userReallyWantsToQuit()) {
208     ui.bufferWidget->saveState();
209     QSettings s;
210     s.beginGroup("Geometry");
211     s.setValue("MainWinSize", size());
212     s.setValue("MainWinPos", pos());
213     s.setValue("MainWinState", saveState());
214     s.endGroup();
215     s.beginGroup("Buffers");
216     //s.setValue("CurrentNetwork", currentNetwork);
217     s.setValue("CurrentBuffer", currentBuffer);
218     s.endGroup();
219     delete systray;
220     event->accept();
221   //} else {
222     //event->ignore();
223   //}
224 }
225
226 void MainWin::showBuffer(BufferId id) {
227   showBuffer(getBuffer(id));
228 }
229
230 void MainWin::showBuffer(Buffer *b) {
231   currentBuffer = b->bufferId().groupId();
232   //emit bufferSelected(b);
233   //qApp->processEvents();
234   ui.bufferWidget->setBuffer(b);
235   emit bufferSelected(b);
236 }
237
238 void MainWin::networkConnected(QString net) {
239   connected[net] = true;
240   BufferId id = getStatusBufferId(net);
241   Buffer *b = getBuffer(id);
242   b->setActive(true);
243   //b->displayMsg(Message(id, Message::Server, tr("Connected.")));
244   // TODO buffersUpdated();
245 }
246
247 void MainWin::networkDisconnected(QString net) {
248   //getBuffer(net, "")->setActive(false);
249   foreach(BufferId id, buffers.keys()) {
250     if(id.network() != net) continue;
251     Buffer *b = getBuffer(id);
252     //b->displayMsg(Message(id, Message::Server, tr("Server disconnected."))); FIXME
253     b->setActive(false);
254   }
255   connected[net] = false;
256 }
257
258 void MainWin::updateBufferId(BufferId id) {
259   bufferIds[id.uid()] = id;  // make lookups by id faster
260   getBuffer(id);
261 }
262
263 BufferId MainWin::getBufferId(QString net, QString buf) {
264   foreach(BufferId id, buffers.keys()) {
265     if(id.network() == net && id.buffer() == buf) return id;
266   }
267   Q_ASSERT(false);
268   return BufferId();
269 }
270
271 BufferId MainWin::getStatusBufferId(QString net) {
272   return getBufferId(net, "");
273 }
274
275
276 Buffer * MainWin::getBuffer(BufferId id) {
277   if(!buffers.contains(id)) {
278     Buffer *b = new Buffer(id);
279     b->setOwnNick(ownNick[id.network()]);
280     connect(b, SIGNAL(userInput(BufferId, QString)), this, SLOT(userInput(BufferId, QString)));
281     connect(b, SIGNAL(bufferUpdated(Buffer *)), this, SIGNAL(bufferUpdated(Buffer *)));
282     connect(b, SIGNAL(bufferDestroyed(Buffer *)), this, SIGNAL(bufferDestroyed(Buffer *)));
283     buffers[id] = b;
284     emit bufferUpdated(b);
285   }
286   return buffers[id];
287 }
288
289 void MainWin::recvNetworkState(QString net, QVariant state) {
290   connected[net] = true;
291   setOwnNick(net, state.toMap()["OwnNick"].toString());
292   getBuffer(getStatusBufferId(net))->setActive(true);
293   VarMap t = state.toMap()["Topics"].toMap();
294   VarMap n = state.toMap()["Nicks"].toMap();
295   foreach(QVariant v, t.keys()) {
296     QString buf = v.toString();
297     BufferId id = getBufferId(net, buf);
298     getBuffer(id)->setActive(true);
299     setTopic(net, buf, t[buf].toString());
300   }
301   foreach(QString nick, n.keys()) {
302     addNick(net, nick, n[nick].toMap());
303   }
304 }
305
306 void MainWin::recvMessage(Message msg) {
307   /*
308   Buffer *b;
309   if(msg.flags & Message::PrivMsg) {
310   // query
311     if(msg.flags & Message::Self) b = getBuffer(net, msg.target);
312     else b = getBuffer(net, nickFromMask(msg.sender));
313   } else {
314     b = getBuffer(net, msg.target);
315   }
316   */
317   
318
319   Buffer *b = getBuffer(msg.buffer);
320   
321   Buffer::ActivityLevel level = Buffer::OtherActivity;
322   if(msg.type == Message::Plain or msg.type == Message::Notice){
323     level |= Buffer::NewMessage;
324   }
325   if(msg.flags & Message::Highlight){
326     level |= Buffer::Highlight;
327   }
328   emit bufferActivity(level, b);
329
330   //b->displayMsg(msg);
331   b->appendChatLine(new ChatLine(msg));
332 }
333
334 void MainWin::recvStatusMsg(QString net, QString msg) {
335   //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
336
337 }
338
339 void MainWin::recvBacklogData(BufferId id, QList<QVariant> msgs, bool done) {
340   foreach(QVariant v, msgs) {
341     layoutQueue.append(v.value<Message>());
342   }
343   if(!layoutTimer->isActive()) layoutTimer->start();
344 }
345
346
347 void MainWin::layoutMsg() {
348   if(layoutQueue.count()) {
349     ChatLine *line = new ChatLine(layoutQueue.takeFirst());
350     getBuffer(line->bufferId())->prependChatLine(line);
351   }
352   if(!layoutQueue.count()) layoutTimer->stop();
353 }
354
355 void MainWin::userInput(BufferId id, QString msg) {
356   emit sendInput(id, msg);
357 }
358
359 void MainWin::setTopic(QString net, QString buf, QString topic) {
360   BufferId id = getBufferId(net, buf);
361   if(!connected[id.network()]) return;
362   Buffer *b = getBuffer(id);
363   b->setTopic(topic);
364   //if(!b->isActive()) {
365   //  b->setActive(true);
366   //  buffersUpdated();
367   //}
368 }
369
370 void MainWin::addNick(QString net, QString nick, VarMap props) {
371   if(!connected[net]) return;
372   nicks[net][nick] = props;
373   VarMap chans = props["Channels"].toMap();
374   QStringList c = chans.keys();
375   foreach(QString bufname, c) {
376     getBuffer(getBufferId(net, bufname))->addNick(nick, props);
377   }
378 }
379
380 void MainWin::renameNick(QString net, QString oldnick, QString newnick) {
381   if(!connected[net]) return;
382   QStringList chans = nicks[net][oldnick]["Channels"].toMap().keys();
383   foreach(QString c, chans) {
384     getBuffer(getBufferId(net, c))->renameNick(oldnick, newnick);
385   }
386   nicks[net][newnick] = nicks[net].take(oldnick);
387 }
388
389 void MainWin::updateNick(QString net, QString nick, VarMap props) {
390   if(!connected[net]) return;
391   QStringList oldchans = nicks[net][nick]["Channels"].toMap().keys();
392   QStringList newchans = props["Channels"].toMap().keys();
393   foreach(QString c, newchans) {
394     if(oldchans.contains(c)) getBuffer(getBufferId(net, c))->updateNick(nick, props);
395     else getBuffer(getBufferId(net, c))->addNick(nick, props);
396   }
397   foreach(QString c, oldchans) {
398     if(!newchans.contains(c)) getBuffer(getBufferId(net, c))->removeNick(nick);
399   }
400   nicks[net][nick] = props;
401 }
402
403 void MainWin::removeNick(QString net, QString nick) {
404   if(!connected[net]) return;
405   VarMap chans = nicks[net][nick]["Channels"].toMap();
406   foreach(QString bufname, chans.keys()) {
407     getBuffer(getBufferId(net, bufname))->removeNick(nick);
408   }
409   nicks[net].remove(nick);
410 }
411
412 void MainWin::setOwnNick(QString net, QString nick) {
413   if(!connected[net]) return;
414   ownNick[net] = nick;
415   foreach(BufferId id, buffers.keys()) {
416     if(id.network() == net) {
417       buffers[id]->setOwnNick(nick);
418     }
419   }
420 }
421
422 void MainWin::importBacklog() {
423   if(QMessageBox::warning(this, "Import old backlog?", "Do you want to import your old file-based backlog into new the backlog database?<br>"
424                                 "<b>This will permanently delete the contents of your database!</b>",
425                                 QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes) {
426     emit importOldBacklog();
427   }
428 }