Make branches/0.3 compile again for mere mortals. Should even run, since my stuff...
[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 = new MessageModel(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 void Client::recvMessage(const Message &message) {
439   Message msg = message;
440   Buffer *b;
441
442   checkForHighlight(msg);
443
444   // FIXME clean up code! (dup)
445
446   // TODO: make redirected messages show up in the correct buffer!
447
448 #ifndef SPUTDEV
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 #endif
493   
494   //bufferModel()->updateBufferActivity(msg);
495
496   // monitor buffer goes away
497 #ifndef SPUTDEV
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.text(), sender, msg.flags());
505     monitorBuffer()->appendMsg(mmsg);
506   }
507 #endif
508
509   emit messageReceived(msg);
510 }
511
512 void Client::recvStatusMsg(QString /*net*/, QString /*msg*/) {
513   //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
514 }
515
516 void Client::receiveBacklog(BufferId bufferId, const QVariantList &msgs) {
517 #ifndef SPUTDEV
518   Buffer *buffer_ = buffer(bufferId);
519   if(!buffer_) {
520     qWarning() << "Client::recvBacklogData(): received Backlog for unknown Buffer:" << bufferId;
521     return;
522   }
523 #endif
524
525   if(msgs.isEmpty())
526     return; // no work to be done...
527   
528   QVariantList::const_iterator msgIter = msgs.constBegin();
529   QVariantList::const_iterator msgIterEnd = msgs.constEnd();
530   Message msg;
531   while(msgIter != msgIterEnd) {
532     msg = (*msgIter).value<Message>();
533     checkForHighlight(msg);
534     buffer_->prependMsg(msg);
535     msgIter++;
536   }
537
538   if(!layoutQueue.contains(buffer_))
539     layoutQueue.append(buffer_);
540
541   if(!layoutTimer->isActive()) {
542     layoutTimer->start();
543   }
544 }
545
546 void Client::layoutMsg() {
547   if(layoutQueue.isEmpty()) {
548     layoutTimer->stop();
549     return;
550   }
551   
552   Buffer *buffer = layoutQueue.takeFirst();
553   if(buffer->layoutMsg()) {
554     layoutQueue.append(buffer);  // Buffer has more messages in its queue --> Round Robin
555     return;
556   } 
557
558   if(layoutQueue.isEmpty())
559     layoutTimer->stop();
560 }
561
562 AbstractUiMsg *Client::layoutMsg(const Message &msg) {
563   return instance()->mainUi->layoutMsg(msg);
564 }
565
566 void Client::checkForHighlight(Message &msg) {
567   NotificationSettings notificationSettings;
568   const Network *net = network(msg.bufferInfo().networkId());
569   if(net && !net->myNick().isEmpty()) {
570     QStringList nickList;
571     if(notificationSettings.highlightNick() == NotificationSettings::CurrentNick) {
572       nickList << net->myNick();
573     } else if(notificationSettings.highlightNick() == NotificationSettings::AllNicks) {
574       nickList = identity(net->identity())->nicks();
575     }
576     foreach(QString nickname, nickList) {
577       QRegExp nickRegExp("^(.*\\W)?" + QRegExp::escape(nickname) + "(\\W.*)?$");
578       if((msg.type() & (Message::Plain | Message::Notice | Message::Action))
579           && !(msg.flags() & Message::Self)
580           && nickRegExp.exactMatch(msg.text())) {
581         msg.setFlags(msg.flags() | Message::Highlight);
582         return;
583       }
584     }
585
586     foreach(QVariant highlight, notificationSettings.highlightList()) {
587       QVariantMap highlightRule = highlight.toMap();
588       if(!highlightRule["enable"].toBool())
589         continue;
590       Qt::CaseSensitivity caseSensitivity = highlightRule["cs"].toBool() ? Qt::CaseSensitive : Qt::CaseInsensitive;
591       QString name = highlightRule["name"].toString();
592       QRegExp userRegExp;
593       if(highlightRule["regex"].toBool()) {
594         userRegExp = QRegExp(name, caseSensitivity);
595       } else {
596         userRegExp = QRegExp("^(.*\\W)?" + QRegExp::escape(name) + "(\\W.*)?$", caseSensitivity);
597       }
598       if((msg.type() & (Message::Plain | Message::Notice | Message::Action))
599           && !(msg.flags() & Message::Self)
600           && userRegExp.exactMatch(msg.text())) {
601         msg.setFlags(msg.flags() | Message::Highlight);
602         return;
603       }
604     }
605   }
606 }
607
608 void Client::updateLastSeenMsg(BufferId id, const MsgId &msgId) {
609   Buffer *b = buffer(id);
610   if(!b) {
611     qWarning() << "Client::updateLastSeen(): Unknown buffer" << id;
612     return;
613   }
614   b->setLastSeenMsg(msgId);
615 }
616
617 void Client::setBufferLastSeenMsg(BufferId id, const MsgId &msgId) {
618   if(!bufferSyncer())
619     return;
620   bufferSyncer()->requestSetLastSeenMsg(id, msgId);
621 }
622
623 void Client::removeBuffer(BufferId id) {
624   if(!bufferSyncer()) return;
625   bufferSyncer()->requestRemoveBuffer(id);
626 }
627
628 void Client::bufferRemoved(BufferId bufferId) {
629   // first remove the buffer from has. this prohibits further lastSeenUpdates
630   Buffer *buff = 0;
631   if(_buffers.contains(bufferId)) {
632     buff = _buffers.take(bufferId);
633     disconnect(buff, 0, this, 0);
634   }
635
636   // then we select a sane buffer (status buffer)
637   /* we have to manually select a buffer because otherwise inconsitent changes
638    * to the model might occur:
639    * the result of a buffer removal triggers a change in the selection model.
640    * the newly selected buffer might be a channel that hasn't been selected yet
641    * and a new nickview would be created (which never heard of the "rowsAboutToBeRemoved").
642    * this new view (and/or) its sort filter will then only receive a "rowsRemoved" signal.
643    */
644   QModelIndex current = bufferModel()->currentIndex();
645   if(current.data(NetworkModel::BufferIdRole).value<BufferId>() == bufferId) {
646     bufferModel()->setCurrentIndex(current.sibling(0,0));
647   }
648
649   // and remove it from the model
650   networkModel()->removeBuffer(bufferId);
651
652   if(buff)
653     buff->deleteLater();
654 }
655
656 void Client::bufferRenamed(BufferId bufferId, const QString &newName) {
657   QModelIndex bufferIndex = networkModel()->bufferIndex(bufferId);
658   if(bufferIndex.isValid()) {
659     networkModel()->setData(bufferIndex, newName, Qt::DisplayRole);
660   }
661 }