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