1 /***************************************************************************
2 * Copyright (C) 2005-08 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
23 #include "bufferinfo.h"
24 #include "buffersyncer.h"
27 #include "ircchannel.h"
31 #include "networkmodel.h"
32 #include "buffermodel.h"
33 #include "quasselui.h"
34 #include "signalproxy.h"
36 #include "buffersettings.h"
38 QPointer<Client> Client::instanceptr = 0;
39 AccountId Client::_currentCoreAccount = 0;
41 /*** Initialization/destruction ***/
43 Client *Client::instance() {
45 instanceptr = new Client();
49 void Client::destroy() {
51 instanceptr->deleteLater();
54 void Client::init(AbstractUi *ui) {
55 instance()->mainUi = ui;
59 Client::Client(QObject *parent)
62 _signalProxy(new SignalProxy(SignalProxy::Client, this)),
67 _connectedToCore(false),
70 _monitorBuffer = new Buffer(BufferInfo(), this);
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)));
85 _bufferModel = new BufferModel(_networkModel);
87 SignalProxy *p = signalProxy();
89 p->attachSlot(SIGNAL(displayMsg(const Message &)), this, SLOT(recvMessage(const Message &)));
90 p->attachSlot(SIGNAL(displayStatusMsg(QString, QString)), this, SLOT(recvStatusMsg(QString, QString)));
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()));
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)));
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)));
109 connect(p, SIGNAL(disconnected()), this, SLOT(disconnectFromCore()));
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()));
116 layoutTimer = new QTimer(this);
117 layoutTimer->setInterval(0);
118 layoutTimer->setSingleShot(false);
119 connect(layoutTimer, SIGNAL(timeout()), this, SLOT(layoutMsg()));
123 /*** public static methods ***/
125 AccountId Client::currentCoreAccount() {
126 return _currentCoreAccount;
129 void Client::setCurrentCoreAccount(AccountId id) {
130 _currentCoreAccount = id;
133 QList<BufferInfo> Client::allBufferInfos() {
134 QList<BufferInfo> bufferids;
135 foreach(Buffer *buffer, buffers()) {
136 bufferids << buffer->bufferInfo();
141 QList<Buffer *> Client::buffers() {
142 return instance()->_buffers.values();
146 Buffer *Client::statusBuffer(const NetworkId &networkId) const {
147 if(_statusBuffers.contains(networkId))
148 return _statusBuffers[networkId];
153 Buffer *Client::buffer(BufferId bufferId) {
154 if(instance()->_buffers.contains(bufferId))
155 return instance()->_buffers[bufferId];
160 Buffer *Client::buffer(BufferInfo bufferInfo) {
161 Buffer *buff = buffer(bufferInfo.bufferId());
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;
171 emit client->bufferUpdated(bufferInfo);
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));
183 bool Client::isConnected() {
184 return instance()->_connectedToCore;
187 bool Client::isSynced() {
188 return instance()->_syncedToCore;
191 /*** Network handling ***/
193 QList<NetworkId> Client::networkIds() {
194 return instance()->_networks.keys();
197 const Network * Client::network(NetworkId networkid) {
198 if(instance()->_networks.contains(networkid)) return instance()->_networks[networkid];
202 void Client::createNetwork(const NetworkInfo &info) {
203 emit instance()->requestCreateNetwork(info);
206 void Client::updateNetwork(const NetworkInfo &info) {
207 emit instance()->requestUpdateNetwork(info);
210 void Client::removeNetwork(NetworkId id) {
211 emit instance()->requestRemoveNetwork(id);
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());
223 void Client::coreNetworkCreated(NetworkId id) {
224 if(_networks.contains(id)) {
225 qWarning() << "Creation of already existing network requested!";
228 Network *net = new Network(id, this);
232 void Client::coreNetworkRemoved(NetworkId id) {
233 if(!_networks.contains(id)) return;
234 Network *net = _networks.take(id);
235 emit networkRemoved(net->networkId());
239 /*** Identity handling ***/
241 QList<IdentityId> Client::identityIds() {
242 return instance()->_identities.keys();
245 const Identity * Client::identity(IdentityId id) {
246 if(instance()->_identities.contains(id)) return instance()->_identities[id];
250 void Client::createIdentity(const Identity &id) {
251 emit instance()->requestCreateIdentity(id);
254 void Client::updateIdentity(const Identity &id) {
255 emit instance()->requestUpdateIdentity(id);
258 void Client::removeIdentity(IdentityId id) {
259 emit instance()->requestRemoveIdentity(id);
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());
270 qWarning() << tr("Identity already exists in client!");
274 void Client::coreIdentityRemoved(IdentityId id) {
275 if(_identities.contains(id)) {
276 emit identityRemoved(id);
277 Identity *i = _identities.take(id);
283 void Client::userInput(BufferInfo bufferInfo, QString message) {
284 emit instance()->sendInput(bufferInfo, message);
287 /*** core connection stuff ***/
289 void Client::setConnectedToCore(QIODevice *sock, AccountId id) {
291 signalProxy()->addPeer(socket);
292 _connectedToCore = true;
293 setCurrentCoreAccount(id);
296 void Client::setSyncedToCore() {
297 // create buffersyncer
298 Q_ASSERT(!_bufferSyncer);
299 _bufferSyncer = new BufferSyncer(this);
300 connect(bufferSyncer(), SIGNAL(lastSeenSet(BufferId, const QDateTime &)), this, SLOT(updateLastSeen(BufferId, const QDateTime &)));
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());
305 _syncedToCore = true;
307 emit coreConnectionStateChanged(true);
310 void Client::disconnectFromCore() {
316 socket->deleteLater();
318 _connectedToCore = false;
319 _syncedToCore = false;
320 setCurrentCoreAccount(0);
322 emit coreConnectionStateChanged(false);
324 // Clear internal data. Hopefully nothing relies on it at this point.
326 _bufferSyncer->deleteLater();
329 _networkModel->clear();
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();
338 Q_ASSERT(_buffers.isEmpty());
340 _statusBuffers.clear();
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);
350 Q_ASSERT(_networks.isEmpty());
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);
359 Q_ASSERT(_identities.isEmpty());
365 void Client::setCoreConfiguration(const QVariantMap &settings) {
366 SignalProxy::writeDataToDevice(socket, settings);
371 void Client::updateBufferInfo(BufferInfo id) {
372 emit bufferUpdated(id);
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);
386 QHash<NetworkId, Buffer *>::iterator statusIter = _statusBuffers.begin();
387 while(statusIter != _statusBuffers.end()) {
388 if(statusIter.value() == buffer) {
389 statusIter = _statusBuffers.erase(statusIter);
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);
409 void Client::recvMessage(const Message &message) {
410 Message msg = message;
413 checkForHighlight(msg);
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();
422 b = statusBuffer(msg.bufferInfo().networkId());
425 } else if(!inQuery && !inCurrent) { // make sure the message get's shown somewhere
426 b = buffer(msg.bufferInfo());
432 b = buffer(msg.bufferInfo().bufferId());
435 } else if(!inStatus && !inCurrent) { // make sure the message get's shown somewhere
436 b = statusBuffer(msg.bufferInfo().networkId());
438 b = buffer(msg.bufferInfo()); // seems like we have to create the buffer anyways...
444 BufferId currentId = bufferModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>();
445 b = buffer(currentId);
446 if(b && currentId != msg.bufferInfo().bufferId() && !inQuery) {
448 } else if(!inStatus && !inQuery) { // make sure the message get's shown somewhere
449 b = statusBuffer(msg.bufferInfo().networkId());
451 b = buffer(msg.bufferInfo()); // seems like we have to create the buffer anyways...
456 // the regular case: we can deliver where it was supposed to go
457 b = buffer(msg.bufferInfo());
461 //bufferModel()->updateBufferActivity(msg);
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() + ":"
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);
474 void Client::recvStatusMsg(QString /*net*/, QString /*msg*/) {
475 //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
478 void Client::recvBacklogData(BufferInfo id, QVariantList msgs, bool /*done*/) {
479 Buffer *b = buffer(id);
481 qWarning() << "Client::recvBacklogData(): received Backlog for unknown Buffer:" << id;
485 foreach(QVariant v, msgs) {
486 Message msg = v.value<Message>();
487 checkForHighlight(msg);
489 //networkModel()->updateBufferActivity(msg);
490 if(!layoutQueue.contains(b)) layoutQueue.append(b);
492 if(layoutQueue.count() && !layoutTimer->isActive()) layoutTimer->start();
495 void Client::layoutMsg() {
496 if(layoutQueue.count()) {
497 Buffer *b = layoutQueue.takeFirst(); // TODO make this the current buffer
499 layoutQueue.append(b); // Buffer has more messages in its queue --> Round Robin
502 if(!layoutQueue.count())
506 AbstractUiMsg *Client::layoutMsg(const Message &msg) {
507 return instance()->mainUi->layoutMsg(msg);
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)) && nickRegExp.exactMatch(msg.text()))
515 msg.setFlags(msg.flags() | Message::Highlight);
519 void Client::updateLastSeen(BufferId id, const QDateTime &lastSeen) {
520 Buffer *b = buffer(id);
522 qWarning() << "Client::updateLastSeen(): Unknown buffer" << id;
525 b->setLastSeen(lastSeen);
528 void Client::setBufferLastSeen(BufferId id, const QDateTime &lastSeen) {
529 if(!bufferSyncer()) return;
530 bufferSyncer()->requestSetLastSeen(id, lastSeen);
533 void Client::removeBuffer(BufferId id) {
534 if(!bufferSyncer()) return;
535 bufferSyncer()->requestRemoveBuffer(id);
538 void Client::bufferRemoved(BufferId bufferId) {
539 // first remove the buffer from has. this prohibits further lastSeenUpdates
541 if(_buffers.contains(bufferId)) {
542 buff = _buffers.take(bufferId);
543 disconnect(buff, 0, this, 0);
546 // then we select a sane buffer (status buffer)
547 /* we have to manually select a buffer because otherwise inconsitent changes
548 * to the model might occur:
549 * the result of a buffer removal triggers a change in the selection model.
550 * the newly selected buffer might be a channel that hasn't been selected yet
551 * and a new nickview would be created (which never heard of the "rowsAboutToBeRemoved").
552 * this new view (and/or) its sort filter will then only receive a "rowsRemoved" signal.
554 QModelIndex current = bufferModel()->currentIndex();
555 if(current.data(NetworkModel::BufferIdRole).value<BufferId>() == bufferId) {
556 bufferModel()->setCurrentIndex(current.sibling(0,0));
559 // and remove it from the model
560 networkModel()->removeBuffer(bufferId);
566 void Client::bufferRenamed(BufferId bufferId, const QString &newName) {
567 QModelIndex bufferIndex = networkModel()->bufferIndex(bufferId);
568 if(bufferIndex.isValid()) {
569 networkModel()->setData(bufferIndex, newName, Qt::DisplayRole);