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