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