Lots of new internal stuff (breaking protocol):
[quassel.git] / src / client / client.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
21 #include "client.h"
22
23 #include "bufferinfo.h"
24 #include "buffersyncer.h"
25 #include "clientbacklogmanager.h"
26 #include "global.h"
27 #include "identity.h"
28 #include "ircchannel.h"
29 #include "ircuser.h"
30 #include "message.h"
31 #include "network.h"
32 #include "networkmodel.h"
33 #include "buffermodel.h"
34 #include "quasselui.h"
35 #include "signalproxy.h"
36 #include "util.h"
37 #include "buffersettings.h"
38
39 QPointer<Client> Client::instanceptr = 0;
40 AccountId Client::_currentCoreAccount = 0;
41
42 /*** Initialization/destruction ***/
43
44 Client *Client::instance() {
45   if(!instanceptr)
46     instanceptr = new Client();
47   return instanceptr;
48 }
49
50 void Client::destroy() {
51   //delete instanceptr;
52   instanceptr->deleteLater();
53 }
54
55 void Client::init(AbstractUi *ui) {
56   instance()->mainUi = ui;
57   instance()->init();
58 }
59
60 Client::Client(QObject *parent)
61   : QObject(parent),
62     socket(0),
63     _signalProxy(new SignalProxy(SignalProxy::Client, this)),
64     mainUi(0),
65     _networkModel(0),
66     _bufferModel(0),
67     _bufferSyncer(0),
68     _backlogManager(new ClientBacklogManager(this)),
69     _connectedToCore(false),
70     _syncedToCore(false)
71 {
72   _monitorBuffer = new Buffer(BufferInfo(), this);
73   connect(_backlogManager, SIGNAL(backlog(BufferId, const QVariantList &)),
74           this, SLOT(receiveBacklog(BufferId, const QVariantList &)));
75 }
76
77 Client::~Client() {
78   disconnectFromCore();
79 }
80
81 void Client::init() {
82   _currentCoreAccount = 0;
83   _networkModel = new NetworkModel(this);
84   connect(this, SIGNAL(bufferUpdated(BufferInfo)),
85           _networkModel, SLOT(bufferUpdated(BufferInfo)));
86   connect(this, SIGNAL(networkRemoved(NetworkId)),
87           _networkModel, SLOT(networkRemoved(NetworkId)));
88
89   _bufferModel = new BufferModel(_networkModel);
90
91   SignalProxy *p = signalProxy();
92
93   p->attachSlot(SIGNAL(displayMsg(const Message &)), this, SLOT(recvMessage(const Message &)));
94   p->attachSlot(SIGNAL(displayStatusMsg(QString, QString)), this, SLOT(recvStatusMsg(QString, QString)));
95
96   p->attachSlot(SIGNAL(bufferInfoUpdated(BufferInfo)), this, SLOT(updateBufferInfo(BufferInfo)));
97   p->attachSignal(this, SIGNAL(sendInput(BufferInfo, QString)));
98   p->attachSignal(this, SIGNAL(requestNetworkStates()));
99
100   p->attachSignal(this, SIGNAL(requestCreateIdentity(const Identity &)), SIGNAL(createIdentity(const Identity &)));
101   p->attachSignal(this, SIGNAL(requestUpdateIdentity(const Identity &)), SIGNAL(updateIdentity(const Identity &)));
102   p->attachSignal(this, SIGNAL(requestRemoveIdentity(IdentityId)), SIGNAL(removeIdentity(IdentityId)));
103   p->attachSlot(SIGNAL(identityCreated(const Identity &)), this, SLOT(coreIdentityCreated(const Identity &)));
104   p->attachSlot(SIGNAL(identityRemoved(IdentityId)), this, SLOT(coreIdentityRemoved(IdentityId)));
105
106   p->attachSignal(this, SIGNAL(requestCreateNetwork(const NetworkInfo &)), SIGNAL(createNetwork(const NetworkInfo &)));
107   p->attachSignal(this, SIGNAL(requestUpdateNetwork(const NetworkInfo &)), SIGNAL(updateNetwork(const NetworkInfo &)));
108   p->attachSignal(this, SIGNAL(requestRemoveNetwork(NetworkId)), SIGNAL(removeNetwork(NetworkId)));
109   p->attachSlot(SIGNAL(networkCreated(NetworkId)), this, SLOT(coreNetworkCreated(NetworkId)));
110   p->attachSlot(SIGNAL(networkRemoved(NetworkId)), this, SLOT(coreNetworkRemoved(NetworkId)));
111
112   connect(p, SIGNAL(disconnected()), this, SLOT(disconnectFromCore()));
113
114   //connect(mainUi, SIGNAL(connectToCore(const QVariantMap &)), this, SLOT(connectToCore(const QVariantMap &)));
115   connect(mainUi, SIGNAL(disconnectFromCore()), this, SLOT(disconnectFromCore()));
116   connect(this, SIGNAL(connected()), mainUi, SLOT(connectedToCore()));
117   connect(this, SIGNAL(disconnected()), mainUi, SLOT(disconnectedFromCore()));
118
119   layoutTimer = new QTimer(this);
120   layoutTimer->setInterval(0);
121   layoutTimer->setSingleShot(false);
122   connect(layoutTimer, SIGNAL(timeout()), this, SLOT(layoutMsg()));
123
124 }
125
126 /*** public static methods ***/
127
128 AccountId Client::currentCoreAccount() {
129   return _currentCoreAccount;
130 }
131
132 void Client::setCurrentCoreAccount(AccountId id) {
133   _currentCoreAccount = id;
134 }
135
136 QList<BufferInfo> Client::allBufferInfos() {
137   QList<BufferInfo> bufferids;
138   foreach(Buffer *buffer, buffers()) {
139     bufferids << buffer->bufferInfo();
140   }
141   return bufferids;
142 }
143
144 QList<Buffer *> Client::buffers() {
145   return instance()->_buffers.values();
146 }
147
148
149 Buffer *Client::statusBuffer(const NetworkId &networkId) const {
150   if(_statusBuffers.contains(networkId))
151     return _statusBuffers[networkId];
152   else
153     return 0;
154 }
155
156 Buffer *Client::buffer(BufferId bufferId) {
157   if(instance()->_buffers.contains(bufferId))
158     return instance()->_buffers[bufferId];
159   else
160     return 0;
161 }
162
163 Buffer *Client::buffer(BufferInfo bufferInfo) {
164   Buffer *buff = buffer(bufferInfo.bufferId());
165
166   if(!buff) {
167     Client *client = Client::instance();
168     buff = new Buffer(bufferInfo, client);
169     connect(buff, SIGNAL(destroyed()), client, SLOT(bufferDestroyed()));
170     client->_buffers[bufferInfo.bufferId()] = buff;
171     if(bufferInfo.type() == BufferInfo::StatusBuffer)
172       client->_statusBuffers[bufferInfo.networkId()] = buff;
173
174     emit client->bufferUpdated(bufferInfo);
175
176     // I don't like this: but currently there isn't really a prettier way:
177     if(isSynced()) {  // this slows down syncing a lot, so disable it during sync
178       QModelIndex bufferIdx = networkModel()->bufferIndex(bufferInfo.bufferId());
179       bufferModel()->setCurrentIndex(bufferModel()->mapFromSource(bufferIdx));
180     }
181   }
182   Q_ASSERT(buff);
183   return buff;
184 }
185
186 bool Client::isConnected() {
187   return instance()->_connectedToCore;
188 }
189
190 bool Client::isSynced() {
191   return instance()->_syncedToCore;
192 }
193
194 /*** Network handling ***/
195
196 QList<NetworkId> Client::networkIds() {
197   return instance()->_networks.keys();
198 }
199
200 const Network * Client::network(NetworkId networkid) {
201   if(instance()->_networks.contains(networkid)) return instance()->_networks[networkid];
202   else return 0;
203 }
204
205 void Client::createNetwork(const NetworkInfo &info) {
206   emit instance()->requestCreateNetwork(info);
207 }
208
209 void Client::updateNetwork(const NetworkInfo &info) {
210   emit instance()->requestUpdateNetwork(info);
211 }
212
213 void Client::removeNetwork(NetworkId id) {
214   emit instance()->requestRemoveNetwork(id);
215 }
216
217 void Client::addNetwork(Network *net) {
218   net->setProxy(signalProxy());
219   signalProxy()->synchronize(net);
220   networkModel()->attachNetwork(net);
221   connect(net, SIGNAL(destroyed()), instance(), SLOT(networkDestroyed()));
222   instance()->_networks[net->networkId()] = net;
223   emit instance()->networkCreated(net->networkId());
224 }
225
226 void Client::coreNetworkCreated(NetworkId id) {
227   if(_networks.contains(id)) {
228     qWarning() << "Creation of already existing network requested!";
229     return;
230   }
231   Network *net = new Network(id, this);
232   addNetwork(net);
233 }
234
235 void Client::coreNetworkRemoved(NetworkId id) {
236   if(!_networks.contains(id)) return;
237   Network *net = _networks.take(id);
238   emit networkRemoved(net->networkId());
239   net->deleteLater();
240 }
241
242 /*** Identity handling ***/
243
244 QList<IdentityId> Client::identityIds() {
245   return instance()->_identities.keys();
246 }
247
248 const Identity * Client::identity(IdentityId id) {
249   if(instance()->_identities.contains(id)) return instance()->_identities[id];
250   else return 0;
251 }
252
253 void Client::createIdentity(const Identity &id) {
254   emit instance()->requestCreateIdentity(id);
255 }
256
257 void Client::updateIdentity(const Identity &id) {
258   emit instance()->requestUpdateIdentity(id);
259 }
260
261 void Client::removeIdentity(IdentityId id) {
262   emit instance()->requestRemoveIdentity(id);
263 }
264
265 void Client::coreIdentityCreated(const Identity &other) {
266   if(!_identities.contains(other.id())) {
267     Identity *identity = new Identity(other, this);
268     _identities[other.id()] = identity;
269     identity->setInitialized();
270     signalProxy()->synchronize(identity);
271     emit identityCreated(other.id());
272   } else {
273     qWarning() << tr("Identity already exists in client!");
274   }
275 }
276
277 void Client::coreIdentityRemoved(IdentityId id) {
278   if(_identities.contains(id)) {
279     emit identityRemoved(id);
280     Identity *i = _identities.take(id);
281     i->deleteLater();
282   }
283 }
284
285 /***  ***/
286 void Client::userInput(BufferInfo bufferInfo, QString message) {
287   emit instance()->sendInput(bufferInfo, message);
288 }
289
290 /*** core connection stuff ***/
291
292 void Client::setConnectedToCore(QIODevice *sock, AccountId id) {
293   socket = sock;
294   signalProxy()->addPeer(socket);
295   _connectedToCore = true;
296   setCurrentCoreAccount(id);
297 }
298
299 void Client::setSyncedToCore() {
300   // create buffersyncer
301   Q_ASSERT(!_bufferSyncer);
302   _bufferSyncer = new BufferSyncer(this);
303   connect(bufferSyncer(), SIGNAL(lastSeenMsgSet(BufferId, MsgId)), this, SLOT(updateLastSeenMsg(BufferId, MsgId)));
304   connect(bufferSyncer(), SIGNAL(bufferRemoved(BufferId)), this, SLOT(bufferRemoved(BufferId)));
305   connect(bufferSyncer(), SIGNAL(bufferRenamed(BufferId, QString)), this, SLOT(bufferRenamed(BufferId, QString)));
306   signalProxy()->synchronize(bufferSyncer());
307
308   // attach backlog manager
309   signalProxy()->synchronize(backlogManager());
310   
311   _syncedToCore = true;
312   emit connected();
313   emit coreConnectionStateChanged(true);
314 }
315
316 void Client::disconnectFromCore() {
317   if(!isConnected())
318     return;
319   
320   if(socket) {
321     socket->close();
322     socket->deleteLater();
323   }
324   _connectedToCore = false;
325   _syncedToCore = false;
326   setCurrentCoreAccount(0);
327   emit disconnected();
328   emit coreConnectionStateChanged(false);
329
330   // Clear internal data. Hopefully nothing relies on it at this point.
331   if(_bufferSyncer) {
332     _bufferSyncer->deleteLater();
333     _bufferSyncer = 0;
334   }
335   _networkModel->clear();
336
337   QHash<BufferId, Buffer *>::iterator bufferIter =  _buffers.begin();
338   while(bufferIter != _buffers.end()) {
339     Buffer *buffer = bufferIter.value();
340     disconnect(buffer, SIGNAL(destroyed()), this, 0);
341     bufferIter = _buffers.erase(bufferIter);
342     buffer->deleteLater();
343   }
344   Q_ASSERT(_buffers.isEmpty());
345
346   _statusBuffers.clear();
347
348   QHash<NetworkId, Network*>::iterator netIter = _networks.begin();
349   while(netIter != _networks.end()) {
350     Network *net = netIter.value();
351     emit networkRemoved(net->networkId());
352     disconnect(net, SIGNAL(destroyed()), this, 0);
353     netIter = _networks.erase(netIter);
354     net->deleteLater();
355   }
356   Q_ASSERT(_networks.isEmpty());
357
358   QHash<IdentityId, Identity*>::iterator idIter = _identities.begin();
359   while(idIter != _identities.end()) {
360     Identity *id = idIter.value();
361     emit identityRemoved(id->id());
362     idIter = _identities.erase(idIter);
363     id->deleteLater();
364   }
365   Q_ASSERT(_identities.isEmpty());
366
367   layoutQueue.clear();
368   layoutTimer->stop();
369 }
370
371 void Client::setCoreConfiguration(const QVariantMap &settings) {
372   SignalProxy::writeDataToDevice(socket, settings);
373 }
374
375 /*** ***/
376
377 void Client::updateBufferInfo(BufferInfo id) {
378   emit bufferUpdated(id);
379 }
380
381 void Client::bufferDestroyed() {
382   Buffer *buffer = static_cast<Buffer *>(sender());
383   QHash<BufferId, Buffer *>::iterator iter = _buffers.begin();
384   while(iter != _buffers.end()) {
385     if(iter.value() == buffer) {
386       iter = _buffers.erase(iter);
387       break;
388     }
389     iter++;
390   }
391
392   QHash<NetworkId, Buffer *>::iterator statusIter = _statusBuffers.begin();
393   while(statusIter != _statusBuffers.end()) {
394     if(statusIter.value() == buffer) {
395       statusIter = _statusBuffers.erase(statusIter);
396       break;
397     }
398     statusIter++;
399   }
400 }
401
402 void Client::networkDestroyed() {
403   Network *net = static_cast<Network *>(sender());
404   QHash<NetworkId, Network *>::iterator netIter = _networks.begin();
405   while(netIter != _networks.end()) {
406     if(*netIter == net) {
407       netIter = _networks.erase(netIter);
408       break;
409     } else {
410       netIter++;
411     }
412   }
413 }
414
415 void Client::recvMessage(const Message &message) {
416   Message msg = message;
417   Buffer *b;
418
419   checkForHighlight(msg);
420
421   if(msg.flags() & Message::Redirected) {
422     BufferSettings bufferSettings;
423     bool inStatus = bufferSettings.value("UserMessagesInStatusBuffer", QVariant(true)).toBool();
424     bool inQuery = bufferSettings.value("UserMessagesInQueryBuffer", QVariant(false)).toBool();
425     bool inCurrent = bufferSettings.value("UserMessagesInCurrentBuffer", QVariant(false)).toBool();
426
427     if(inStatus) {
428       b = statusBuffer(msg.bufferInfo().networkId());
429       if(b) {
430         b->appendMsg(msg);
431       } else if(!inQuery && !inCurrent) {       // make sure the message get's shown somewhere
432         b = buffer(msg.bufferInfo());
433         b->appendMsg(msg);
434       }
435     }
436
437     if(inQuery) {
438       b = buffer(msg.bufferInfo().bufferId());
439       if(b) {
440         b->appendMsg(msg);
441       } else if(!inStatus && !inCurrent) {      // make sure the message get's shown somewhere
442         b = statusBuffer(msg.bufferInfo().networkId());
443         if(!b)
444           b = buffer(msg.bufferInfo()); // seems like we have to create the buffer anyways... 
445         b->appendMsg(msg);
446       }
447     }
448
449     if(inCurrent) {
450       BufferId currentId = bufferModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>();
451       b = buffer(currentId);
452       if(b && currentId != msg.bufferInfo().bufferId() && !inQuery) {
453         b->appendMsg(msg);
454       } else if(!inStatus && !inQuery) {        // make sure the message get's shown somewhere
455         b = statusBuffer(msg.bufferInfo().networkId());
456         if(!b)
457           b = buffer(msg.bufferInfo()); // seems like we have to create the buffer anyways... 
458         b->appendMsg(msg);
459       }
460     }
461   } else {
462     // the regular case: we can deliver where it was supposed to go
463     b = buffer(msg.bufferInfo());
464     b->appendMsg(msg);
465   }
466   
467   //bufferModel()->updateBufferActivity(msg);
468
469   if(msg.type() == Message::Plain || msg.type() == Message::Notice || msg.type() == Message::Action) {
470     const Network *net = network(msg.bufferInfo().networkId());
471     QString networkName = net != 0
472       ? net->networkName() + ":"
473       : QString();
474     QString sender = networkName + msg.bufferInfo().bufferName() + ":" + msg.sender();
475     Message mmsg = Message(msg.timestamp(), msg.bufferInfo(), msg.type(), msg.text(), sender, msg.flags());
476     monitorBuffer()->appendMsg(mmsg);
477   }
478 }
479
480 void Client::recvStatusMsg(QString /*net*/, QString /*msg*/) {
481   //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
482 }
483
484 void Client::receiveBacklog(BufferId bufferId, const QVariantList &msgs) {
485   Buffer *buffer_ = buffer(bufferId);
486   if(!buffer_) {
487     qWarning() << "Client::recvBacklogData(): received Backlog for unknown Buffer:" << bufferId;
488     return;
489   }
490
491   if(msgs.isEmpty())
492     return; // no work to be done...
493   
494   QVariantList::const_iterator msgIter = msgs.constBegin();
495   QVariantList::const_iterator msgIterEnd = msgs.constEnd();
496   Message msg;
497   while(msgIter != msgIterEnd) {
498     msg = (*msgIter).value<Message>();
499     buffer_->prependMsg(msg);
500     msgIter++;
501   }
502
503   if(!layoutQueue.contains(buffer_))
504     layoutQueue.append(buffer_);
505
506   if(!layoutTimer->isActive()) {
507     layoutTimer->start();
508   }
509 }
510
511 void Client::layoutMsg() {
512   if(layoutQueue.isEmpty()) {
513     layoutTimer->stop();
514     return;
515   }
516   
517   Buffer *buffer = layoutQueue.takeFirst();
518   if(buffer->layoutMsg()) {
519     layoutQueue.append(buffer);  // Buffer has more messages in its queue --> Round Robin
520     return;
521   } 
522
523   if(layoutQueue.isEmpty())
524     layoutTimer->stop();
525 }
526
527 AbstractUiMsg *Client::layoutMsg(const Message &msg) {
528   return instance()->mainUi->layoutMsg(msg);
529 }
530
531 void Client::checkForHighlight(Message &msg) {
532   const Network *net = network(msg.bufferInfo().networkId());
533   if(net && !net->myNick().isEmpty()) {
534     QRegExp nickRegExp("^(.*\\W)?" + QRegExp::escape(net->myNick()) + "(\\W.*)?$");
535     if((msg.type() & (Message::Plain | Message::Notice | Message::Action)) && !(msg.flags() & Message::Self) && nickRegExp.exactMatch(msg.text()))
536       msg.setFlags(msg.flags() | Message::Highlight);
537   }
538 }
539
540 void Client::updateLastSeenMsg(BufferId id, const MsgId &msgId) {
541   Buffer *b = buffer(id);
542   if(!b) {
543     qWarning() << "Client::updateLastSeen(): Unknown buffer" << id;
544     return;
545   }
546   b->setLastSeenMsg(msgId);
547 }
548
549 void Client::setBufferLastSeenMsg(BufferId id, const MsgId &msgId) {
550   if(!bufferSyncer())
551     return;
552   bufferSyncer()->requestSetLastSeenMsg(id, msgId);
553 }
554
555 void Client::removeBuffer(BufferId id) {
556   if(!bufferSyncer()) return;
557   bufferSyncer()->requestRemoveBuffer(id);
558 }
559
560 void Client::bufferRemoved(BufferId bufferId) {
561   // first remove the buffer from has. this prohibits further lastSeenUpdates
562   Buffer *buff = 0;
563   if(_buffers.contains(bufferId)) {
564     buff = _buffers.take(bufferId);
565     disconnect(buff, 0, this, 0);
566   }
567
568   // then we select a sane buffer (status buffer)
569   /* we have to manually select a buffer because otherwise inconsitent changes
570    * to the model might occur:
571    * the result of a buffer removal triggers a change in the selection model.
572    * the newly selected buffer might be a channel that hasn't been selected yet
573    * and a new nickview would be created (which never heard of the "rowsAboutToBeRemoved").
574    * this new view (and/or) its sort filter will then only receive a "rowsRemoved" signal.
575    */
576   QModelIndex current = bufferModel()->currentIndex();
577   if(current.data(NetworkModel::BufferIdRole).value<BufferId>() == bufferId) {
578     bufferModel()->setCurrentIndex(current.sibling(0,0));
579   }
580
581   // and remove it from the model
582   networkModel()->removeBuffer(bufferId);
583
584   if(buff)
585     buff->deleteLater();
586 }
587
588 void Client::bufferRenamed(BufferId bufferId, const QString &newName) {
589   QModelIndex bufferIndex = networkModel()->bufferIndex(bufferId);
590   if(bufferIndex.isValid()) {
591     networkModel()->setData(bufferIndex, newName, Qt::DisplayRole);
592   }
593 }