10e7575fa119c9b46fafe68e42cd426bc8b80445
[quassel.git] / src / client / networkmodel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2014 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  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "networkmodel.h"
22
23 #include <QAbstractItemView>
24 #include <QMimeData>
25 #include <QTextDocument>        // for Qt::escape()
26
27 #include "buffermodel.h"
28 #include "buffersettings.h"
29 #include "client.h"
30 #include "clientignorelistmanager.h"
31 #include "clientsettings.h"
32 #include "ircchannel.h"
33 #include "network.h"
34 #include "signalproxy.h"
35
36 /*****************************************
37 *  Network Items
38 *****************************************/
39 NetworkItem::NetworkItem(const NetworkId &netid, AbstractTreeItem *parent)
40     : PropertyMapItem(QList<QString>() << "networkName" << "currentServer" << "nickCount", parent),
41     _networkId(netid),
42     _statusBufferItem(0)
43 {
44     // DO NOT EMIT dataChanged() DIRECTLY IN NetworkItem
45     // use networkDataChanged() instead. Otherwise you will end up in a infinite loop
46     // as we "sync" the dataChanged() signals of NetworkItem and StatusBufferItem
47     setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
48     connect(this, SIGNAL(networkDataChanged(int)), this, SIGNAL(dataChanged(int)));
49     connect(this, SIGNAL(beginRemoveChilds(int, int)), this, SLOT(onBeginRemoveChilds(int, int)));
50 }
51
52
53 QVariant NetworkItem::data(int column, int role) const
54 {
55     switch (role) {
56     case NetworkModel::BufferIdRole:
57     case NetworkModel::BufferInfoRole:
58     case NetworkModel::BufferTypeRole:
59     case NetworkModel::BufferActivityRole:
60         if (_statusBufferItem)
61             return _statusBufferItem->data(column, role);
62         else
63             return QVariant();
64     case NetworkModel::NetworkIdRole:
65         return qVariantFromValue(_networkId);
66     case NetworkModel::ItemTypeRole:
67         return NetworkModel::NetworkItemType;
68     case NetworkModel::ItemActiveRole:
69         return isActive();
70     default:
71         return PropertyMapItem::data(column, role);
72     }
73 }
74
75
76 // FIXME shouldn't we check the bufferItemCache here?
77 BufferItem *NetworkItem::findBufferItem(BufferId bufferId)
78 {
79     BufferItem *bufferItem = 0;
80
81     for (int i = 0; i < childCount(); i++) {
82         bufferItem = qobject_cast<BufferItem *>(child(i));
83         if (!bufferItem)
84             continue;
85         if (bufferItem->bufferId() == bufferId)
86             return bufferItem;
87     }
88     return 0;
89 }
90
91
92 BufferItem *NetworkItem::bufferItem(const BufferInfo &bufferInfo)
93 {
94     BufferItem *bufferItem = findBufferItem(bufferInfo);
95     if (bufferItem)
96         return bufferItem;
97
98     switch (bufferInfo.type()) {
99     case BufferInfo::StatusBuffer:
100         _statusBufferItem = new StatusBufferItem(bufferInfo, this);
101         bufferItem = _statusBufferItem;
102         disconnect(this, SIGNAL(networkDataChanged(int)), this, SIGNAL(dataChanged(int)));
103         connect(this, SIGNAL(networkDataChanged(int)), bufferItem, SIGNAL(dataChanged(int)));
104         connect(bufferItem, SIGNAL(dataChanged(int)), this, SIGNAL(dataChanged(int)));
105         break;
106     case BufferInfo::ChannelBuffer:
107         bufferItem = new ChannelBufferItem(bufferInfo, this);
108         break;
109     case BufferInfo::QueryBuffer:
110         bufferItem = new QueryBufferItem(bufferInfo, this);
111         break;
112     default:
113         bufferItem = new BufferItem(bufferInfo, this);
114     }
115
116     newChild(bufferItem);
117
118     // postprocess... this is necessary because Qt doesn't seem to like adding children which already have children on their own
119     switch (bufferInfo.type()) {
120     case BufferInfo::ChannelBuffer:
121     {
122         ChannelBufferItem *channelBufferItem = static_cast<ChannelBufferItem *>(bufferItem);
123         if (_network) {
124             IrcChannel *ircChannel = _network->ircChannel(bufferInfo.bufferName());
125             if (ircChannel)
126                 channelBufferItem->attachIrcChannel(ircChannel);
127         }
128     }
129     break;
130     default:
131         break;
132     }
133
134     return bufferItem;
135 }
136
137
138 void NetworkItem::attachNetwork(Network *network)
139 {
140     if (!network)
141         return;
142
143     _network = network;
144
145     connect(network, SIGNAL(networkNameSet(QString)),
146         this, SLOT(setNetworkName(QString)));
147     connect(network, SIGNAL(currentServerSet(QString)),
148         this, SLOT(setCurrentServer(QString)));
149     connect(network, SIGNAL(ircChannelAdded(IrcChannel *)),
150         this, SLOT(attachIrcChannel(IrcChannel *)));
151     connect(network, SIGNAL(ircUserAdded(IrcUser *)),
152         this, SLOT(attachIrcUser(IrcUser *)));
153     connect(network, SIGNAL(connectedSet(bool)),
154         this, SIGNAL(networkDataChanged()));
155     connect(network, SIGNAL(destroyed()),
156         this, SIGNAL(networkDataChanged()));
157
158     emit networkDataChanged();
159 }
160
161
162 void NetworkItem::attachIrcChannel(IrcChannel *ircChannel)
163 {
164     ChannelBufferItem *channelItem;
165     for (int i = 0; i < childCount(); i++) {
166         channelItem = qobject_cast<ChannelBufferItem *>(child(i));
167         if (!channelItem)
168             continue;
169
170         if (channelItem->bufferName().toLower() == ircChannel->name().toLower()) {
171             channelItem->attachIrcChannel(ircChannel);
172             return;
173         }
174     }
175 }
176
177
178 void NetworkItem::attachIrcUser(IrcUser *ircUser)
179 {
180     QueryBufferItem *queryItem = 0;
181     for (int i = 0; i < childCount(); i++) {
182         queryItem = qobject_cast<QueryBufferItem *>(child(i));
183         if (!queryItem)
184             continue;
185
186         if (queryItem->bufferName().toLower() == ircUser->nick().toLower()) {
187             queryItem->setIrcUser(ircUser);
188             break;
189         }
190     }
191 }
192
193
194 void NetworkItem::setNetworkName(const QString &networkName)
195 {
196     Q_UNUSED(networkName);
197     emit networkDataChanged(0);
198 }
199
200
201 void NetworkItem::setCurrentServer(const QString &serverName)
202 {
203     Q_UNUSED(serverName);
204     emit networkDataChanged(1);
205 }
206
207
208 QString NetworkItem::toolTip(int column) const
209 {
210     Q_UNUSED(column);
211
212     QStringList toolTip(QString("<b>%1</b>").arg(Qt::escape(networkName())));
213     toolTip.append(tr("Server: %1").arg(Qt::escape(currentServer())));
214     toolTip.append(tr("Users: %1").arg(nickCount()));
215
216     if (_network) {
217         toolTip.append(tr("Lag: %1 msecs").arg(_network->latency()));
218     }
219
220     return QString("<p> %1 </p>").arg(toolTip.join("<br />"));
221 }
222
223
224 void NetworkItem::onBeginRemoveChilds(int start, int end)
225 {
226     for (int i = start; i <= end; i++) {
227         StatusBufferItem *statusBufferItem = qobject_cast<StatusBufferItem *>(child(i));
228         if (statusBufferItem) {
229             _statusBufferItem = 0;
230             break;
231         }
232     }
233 }
234
235
236 /*****************************************
237 *  Fancy Buffer Items
238 *****************************************/
239 BufferItem::BufferItem(const BufferInfo &bufferInfo, AbstractTreeItem *parent)
240     : PropertyMapItem(QStringList() << "bufferName" << "topic" << "nickCount", parent),
241     _bufferInfo(bufferInfo),
242     _activity(BufferInfo::NoActivity)
243 {
244     setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
245 }
246
247
248 void BufferItem::setActivityLevel(BufferInfo::ActivityLevel level)
249 {
250     if (_activity != level) {
251         _activity = level;
252         emit dataChanged();
253     }
254 }
255
256
257 void BufferItem::clearActivityLevel()
258 {
259     _activity = BufferInfo::NoActivity;
260     _firstUnreadMsgId = MsgId();
261
262     // FIXME remove with core proto v11
263     if (!(Client::coreFeatures() & Quassel::SynchronizedMarkerLine)) {
264         _markerLineMsgId = _lastSeenMsgId;
265     }
266
267     emit dataChanged();
268 }
269
270
271 void BufferItem::updateActivityLevel(const Message &msg)
272 {
273     if (isCurrentBuffer()) {
274         return;
275     }
276
277     if (msg.flags() & Message::Self)    // don't update activity for our own messages
278         return;
279
280     if (Client::ignoreListManager()
281         && Client::ignoreListManager()->match(msg, qobject_cast<NetworkItem *>(parent())->networkName()))
282         return;
283
284     if (msg.msgId() <= lastSeenMsgId())
285         return;
286
287     bool stateChanged = false;
288     if (!firstUnreadMsgId().isValid() || msg.msgId() < firstUnreadMsgId()) {
289         stateChanged = true;
290         _firstUnreadMsgId = msg.msgId();
291     }
292
293     BufferInfo::ActivityLevel oldLevel = activityLevel();
294
295     _activity |= BufferInfo::OtherActivity;
296     if (msg.type() & (Message::Plain | Message::Notice | Message::Action))
297         _activity |= BufferInfo::NewMessage;
298
299     if (msg.flags() & Message::Highlight)
300         _activity |= BufferInfo::Highlight;
301
302     stateChanged |= (oldLevel != _activity);
303
304     if (stateChanged)
305         emit dataChanged();
306 }
307
308
309 QVariant BufferItem::data(int column, int role) const
310 {
311     switch (role) {
312     case NetworkModel::ItemTypeRole:
313         return NetworkModel::BufferItemType;
314     case NetworkModel::BufferIdRole:
315         return qVariantFromValue(bufferInfo().bufferId());
316     case NetworkModel::NetworkIdRole:
317         return qVariantFromValue(bufferInfo().networkId());
318     case NetworkModel::BufferInfoRole:
319         return qVariantFromValue(bufferInfo());
320     case NetworkModel::BufferTypeRole:
321         return int(bufferType());
322     case NetworkModel::ItemActiveRole:
323         return isActive();
324     case NetworkModel::BufferActivityRole:
325         return (int)activityLevel();
326     case NetworkModel::BufferFirstUnreadMsgIdRole:
327         return qVariantFromValue(firstUnreadMsgId());
328     case NetworkModel::MarkerLineMsgIdRole:
329         return qVariantFromValue(markerLineMsgId());
330     default:
331         return PropertyMapItem::data(column, role);
332     }
333 }
334
335
336 bool BufferItem::setData(int column, const QVariant &value, int role)
337 {
338     switch (role) {
339     case NetworkModel::BufferActivityRole:
340         setActivityLevel((BufferInfo::ActivityLevel)value.toInt());
341         return true;
342     default:
343         return PropertyMapItem::setData(column, value, role);
344     }
345     return true;
346 }
347
348
349 void BufferItem::setBufferName(const QString &name)
350 {
351     _bufferInfo = BufferInfo(_bufferInfo.bufferId(), _bufferInfo.networkId(), _bufferInfo.type(), _bufferInfo.groupId(), name);
352     emit dataChanged(0);
353 }
354
355
356 void BufferItem::setLastSeenMsgId(MsgId msgId)
357 {
358     _lastSeenMsgId = msgId;
359
360     // FIXME remove with core protocol v11
361     if (!(Client::coreFeatures() & Quassel::SynchronizedMarkerLine)) {
362         if (!isCurrentBuffer())
363             _markerLineMsgId = msgId;
364     }
365
366     setActivityLevel(BufferInfo::NoActivity);
367 }
368
369
370 void BufferItem::setMarkerLineMsgId(MsgId msgId)
371 {
372     _markerLineMsgId = msgId;
373     emit dataChanged();
374 }
375
376
377 bool BufferItem::isCurrentBuffer() const
378 {
379     return _bufferInfo.bufferId() == Client::bufferModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>();
380 }
381
382
383 QString BufferItem::toolTip(int column) const
384 {
385     Q_UNUSED(column);
386     return tr("<p> %1 - %2 </p>").arg(bufferInfo().bufferId().toInt()).arg(bufferName());
387 }
388
389
390 /*****************************************
391 *  StatusBufferItem
392 *****************************************/
393 StatusBufferItem::StatusBufferItem(const BufferInfo &bufferInfo, NetworkItem *parent)
394     : BufferItem(bufferInfo, parent)
395 {
396 }
397
398
399 QString StatusBufferItem::toolTip(int column) const
400 {
401     NetworkItem *networkItem = qobject_cast<NetworkItem *>(parent());
402     if (networkItem)
403         return networkItem->toolTip(column);
404     else
405         return QString();
406 }
407
408
409 /*****************************************
410 *  QueryBufferItem
411 *****************************************/
412 QueryBufferItem::QueryBufferItem(const BufferInfo &bufferInfo, NetworkItem *parent)
413     : BufferItem(bufferInfo, parent),
414     _ircUser(0)
415 {
416     setFlags(flags() | Qt::ItemIsDropEnabled | Qt::ItemIsEditable);
417
418     const Network *net = Client::network(bufferInfo.networkId());
419     if (!net)
420         return;
421
422     IrcUser *ircUser = net->ircUser(bufferInfo.bufferName());
423     setIrcUser(ircUser);
424 }
425
426
427 QVariant QueryBufferItem::data(int column, int role) const
428 {
429     switch (role) {
430     case Qt::EditRole:
431         return BufferItem::data(column, Qt::DisplayRole);
432     case NetworkModel::IrcUserRole:
433         return QVariant::fromValue<QObject *>(_ircUser);
434     case NetworkModel::UserAwayRole:
435         return (bool)_ircUser ? _ircUser->isAway() : false;
436     default:
437         return BufferItem::data(column, role);
438     }
439 }
440
441
442 bool QueryBufferItem::setData(int column, const QVariant &value, int role)
443 {
444     if (column != 0)
445         return BufferItem::setData(column, value, role);
446
447     switch (role) {
448     case Qt::EditRole:
449     {
450         QString newName = value.toString();
451         if (!newName.isEmpty()) {
452             Client::renameBuffer(bufferId(), newName);
453             return true;
454         }
455         else {
456             return false;
457         }
458     }
459     break;
460     default:
461         return BufferItem::setData(column, value, role);
462     }
463 }
464
465
466 void QueryBufferItem::setBufferName(const QString &name)
467 {
468     BufferItem::setBufferName(name);
469     NetworkId netId = data(0, NetworkModel::NetworkIdRole).value<NetworkId>();
470     const Network *net = Client::network(netId);
471     if (net)
472         setIrcUser(net->ircUser(name));
473 }
474
475
476 QString QueryBufferItem::toolTip(int column) const
477 {
478     // pretty much code duplication of IrcUserItem::toolTip() but inheritance won't solve this...
479     Q_UNUSED(column);
480     QStringList toolTip;
481
482     toolTip.append(tr("<b>Query with %1</b>").arg(bufferName()));
483
484     if (_ircUser) {
485         if (_ircUser->userModes() != "") toolTip[0].append(QString(" (+%1)").arg(_ircUser->userModes()));
486         if (_ircUser->isAway()) {
487             toolTip[0].append(QString(" (away%1)").arg(!_ircUser->awayMessage().isEmpty() ? (QString(" ") + _ircUser->awayMessage()) : QString()));
488         }
489         if (!_ircUser->realName().isEmpty()) toolTip.append(_ircUser->realName());
490         if (!_ircUser->ircOperator().isEmpty()) toolTip.append(QString("%1 %2").arg(_ircUser->nick()).arg(_ircUser->ircOperator()));
491         if (!_ircUser->suserHost().isEmpty()) toolTip.append(_ircUser->suserHost());
492         if (!_ircUser->whoisServiceReply().isEmpty()) toolTip.append(_ircUser->whoisServiceReply());
493
494         toolTip.append(_ircUser->hostmask().remove(0, _ircUser->hostmask().indexOf("!")+1));
495
496         if (_ircUser->idleTime().isValid()) {
497             QDateTime now = QDateTime::currentDateTime();
498             QDateTime idle = _ircUser->idleTime();
499             int idleTime = idle.secsTo(now);
500             toolTip.append(tr("idling since %1").arg(secondsToString(idleTime)));
501         }
502         if (_ircUser->loginTime().isValid()) {
503             toolTip.append(tr("login time: %1").arg(_ircUser->loginTime().toString()));
504         }
505
506         if (!_ircUser->server().isEmpty()) toolTip.append(tr("server: %1").arg(_ircUser->server()));
507     }
508
509     return QString("<p> %1 </p>").arg(toolTip.join("<br />"));
510 }
511
512
513 void QueryBufferItem::setIrcUser(IrcUser *ircUser)
514 {
515     if (_ircUser == ircUser)
516         return;
517
518     if (_ircUser) {
519         disconnect(_ircUser, 0, this, 0);
520     }
521
522     if (ircUser) {
523         connect(ircUser, SIGNAL(quited()), this, SLOT(removeIrcUser()));
524         connect(ircUser, SIGNAL(awaySet(bool)), this, SIGNAL(dataChanged()));
525         connect(ircUser, SIGNAL(encryptedSet(bool)), this, SLOT(setEncrypted(bool)));
526     }
527
528     _ircUser = ircUser;
529     emit dataChanged();
530 }
531
532
533 void QueryBufferItem::removeIrcUser()
534 {
535     _ircUser = 0;
536     emit dataChanged();
537 }
538
539
540 /*****************************************
541 *  ChannelBufferItem
542 *****************************************/
543 ChannelBufferItem::ChannelBufferItem(const BufferInfo &bufferInfo, AbstractTreeItem *parent)
544     : BufferItem(bufferInfo, parent),
545     _ircChannel(0)
546 {
547 }
548
549
550 QVariant ChannelBufferItem::data(int column, int role) const
551 {
552     switch (role) {
553     case NetworkModel::IrcChannelRole:
554         return QVariant::fromValue<QObject *>(_ircChannel);
555     default:
556         return BufferItem::data(column, role);
557     }
558 }
559
560
561 QString ChannelBufferItem::toolTip(int column) const
562 {
563     Q_UNUSED(column);
564     QStringList toolTip;
565
566     toolTip.append(tr("<b>Channel %1</b>").arg(Qt::escape(bufferName())));
567     if (isActive()) {
568         //TODO: add channel modes
569         toolTip.append(tr("<b>Users:</b> %1").arg(nickCount()));
570         if (_ircChannel) {
571             QString channelMode = _ircChannel->channelModeString(); // channelModeString is compiled on the fly -> thus cache the result
572             if (!channelMode.isEmpty())
573                 toolTip.append(tr("<b>Mode:</b> %1").arg(channelMode));
574         }
575
576         ItemViewSettings s;
577         bool showTopic = s.displayTopicInTooltip();
578         if (showTopic) {
579             QString _topic = topic();
580             if (_topic != "") {
581                 _topic = stripFormatCodes(_topic);
582                 _topic = Qt::escape(_topic);
583                 toolTip.append(QString("<font size='-2'>&nbsp;</font>"));
584                 toolTip.append(tr("<b>Topic:</b> %1").arg(_topic));
585             }
586         }
587     }
588     else {
589         toolTip.append(tr("Not active <br /> Double-click to join"));
590     }
591
592     return tr("<p> %1 </p>").arg(toolTip.join("<br />"));
593 }
594
595
596 void ChannelBufferItem::attachIrcChannel(IrcChannel *ircChannel)
597 {
598     Q_ASSERT(!_ircChannel && ircChannel);
599
600     _ircChannel = ircChannel;
601
602     connect(ircChannel, SIGNAL(topicSet(QString)),
603         this, SLOT(setTopic(QString)));
604     connect(ircChannel, SIGNAL(encryptedSet(bool)),
605         this, SLOT(setEncrypted(bool)));
606     connect(ircChannel, SIGNAL(ircUsersJoined(QList<IrcUser *> )),
607         this, SLOT(join(QList<IrcUser *> )));
608     connect(ircChannel, SIGNAL(ircUserParted(IrcUser *)),
609         this, SLOT(part(IrcUser *)));
610     connect(ircChannel, SIGNAL(parted()),
611         this, SLOT(ircChannelParted()));
612     connect(ircChannel, SIGNAL(ircUserModesSet(IrcUser *, QString)),
613         this, SLOT(userModeChanged(IrcUser *)));
614     connect(ircChannel, SIGNAL(ircUserModeAdded(IrcUser *, QString)),
615         this, SLOT(userModeChanged(IrcUser *)));
616     connect(ircChannel, SIGNAL(ircUserModeRemoved(IrcUser *, QString)),
617         this, SLOT(userModeChanged(IrcUser *)));
618
619     if (!ircChannel->ircUsers().isEmpty())
620         join(ircChannel->ircUsers());
621
622     emit dataChanged();
623 }
624
625
626 void ChannelBufferItem::ircChannelParted()
627 {
628     Q_CHECK_PTR(_ircChannel);
629     disconnect(_ircChannel, 0, this, 0);
630     _ircChannel = 0;
631     emit dataChanged();
632     removeAllChilds();
633 }
634
635
636 void ChannelBufferItem::join(const QList<IrcUser *> &ircUsers)
637 {
638     addUsersToCategory(ircUsers);
639     emit dataChanged(2);
640 }
641
642
643 UserCategoryItem *ChannelBufferItem::findCategoryItem(int categoryId)
644 {
645     UserCategoryItem *categoryItem = 0;
646
647     for (int i = 0; i < childCount(); i++) {
648         categoryItem = qobject_cast<UserCategoryItem *>(child(i));
649         if (!categoryItem)
650             continue;
651         if (categoryItem->categoryId() == categoryId)
652             return categoryItem;
653     }
654     return 0;
655 }
656
657
658 void ChannelBufferItem::addUserToCategory(IrcUser *ircUser)
659 {
660     addUsersToCategory(QList<IrcUser *>() << ircUser);
661 }
662
663
664 void ChannelBufferItem::addUsersToCategory(const QList<IrcUser *> &ircUsers)
665 {
666     Q_ASSERT(_ircChannel);
667
668     QHash<UserCategoryItem *, QList<IrcUser *> > categories;
669
670     int categoryId = -1;
671     UserCategoryItem *categoryItem = 0;
672
673     foreach(IrcUser *ircUser, ircUsers) {
674         categoryId = UserCategoryItem::categoryFromModes(_ircChannel->userModes(ircUser));
675         categoryItem = findCategoryItem(categoryId);
676         if (!categoryItem) {
677             categoryItem = new UserCategoryItem(categoryId, this);
678             categories[categoryItem] = QList<IrcUser *>();
679             newChild(categoryItem);
680         }
681         categories[categoryItem] << ircUser;
682     }
683
684     QHash<UserCategoryItem *, QList<IrcUser *> >::const_iterator catIter = categories.constBegin();
685     while (catIter != categories.constEnd()) {
686         catIter.key()->addUsers(catIter.value());
687         catIter++;
688     }
689 }
690
691
692 void ChannelBufferItem::part(IrcUser *ircUser)
693 {
694     if (!ircUser) {
695         qWarning() << bufferName() << "ChannelBufferItem::part(): unknown User" << ircUser;
696         return;
697     }
698
699     disconnect(ircUser, 0, this, 0);
700     removeUserFromCategory(ircUser);
701     emit dataChanged(2);
702 }
703
704
705 void ChannelBufferItem::removeUserFromCategory(IrcUser *ircUser)
706 {
707     if (!_ircChannel) {
708         // If we parted the channel there might still be some ircUsers connected.
709         // in that case we just ignore the call
710         Q_ASSERT(childCount() == 0);
711         return;
712     }
713
714     UserCategoryItem *categoryItem = 0;
715     for (int i = 0; i < childCount(); i++) {
716         categoryItem = qobject_cast<UserCategoryItem *>(child(i));
717         if (categoryItem->removeUser(ircUser)) {
718             if (categoryItem->childCount() == 0)
719                 removeChild(i);
720             break;
721         }
722     }
723 }
724
725
726 void ChannelBufferItem::userModeChanged(IrcUser *ircUser)
727 {
728     Q_ASSERT(_ircChannel);
729
730     int categoryId = UserCategoryItem::categoryFromModes(_ircChannel->userModes(ircUser));
731     UserCategoryItem *categoryItem = findCategoryItem(categoryId);
732
733     if (categoryItem) {
734         if (categoryItem->findIrcUser(ircUser)) {
735             return; // already in the right category;
736         }
737     }
738     else {
739         categoryItem = new UserCategoryItem(categoryId, this);
740         newChild(categoryItem);
741     }
742
743     // find the item that needs reparenting
744     IrcUserItem *ircUserItem = 0;
745     for (int i = 0; i < childCount(); i++) {
746         UserCategoryItem *oldCategoryItem = qobject_cast<UserCategoryItem *>(child(i));
747         Q_ASSERT(oldCategoryItem);
748         IrcUserItem *userItem = oldCategoryItem->findIrcUser(ircUser);
749         if (userItem) {
750             ircUserItem = userItem;
751             break;
752         }
753     }
754
755     if (!ircUserItem) {
756         qWarning() << "ChannelBufferItem::userModeChanged(IrcUser *): unable to determine old category of" << ircUser;
757         return;
758     }
759     ircUserItem->reParent(categoryItem);
760 }
761
762
763 /*****************************************
764 *  User Category Items (like @vh etc.)
765 *****************************************/
766 // we hardcode this even though we have PREFIX in network... but that wouldn't help with mapping modes to
767 // category strings anyway.
768 const QList<QChar> UserCategoryItem::categories = QList<QChar>() << 'q' << 'a' << 'o' << 'h' << 'v';
769
770 UserCategoryItem::UserCategoryItem(int category, AbstractTreeItem *parent)
771     : PropertyMapItem(QStringList() << "categoryName", parent),
772     _category(category)
773 {
774     setFlags(Qt::ItemIsEnabled);
775     setTreeItemFlags(AbstractTreeItem::DeleteOnLastChildRemoved);
776     setObjectName(parent->data(0, Qt::DisplayRole).toString() + "/" + QString::number(category));
777 }
778
779
780 // caching this makes no sense, since we display the user number dynamically
781 QString UserCategoryItem::categoryName() const
782 {
783     int n = childCount();
784     switch (_category) {
785     case 0:
786         return tr("%n Owner(s)", 0, n);
787     case 1:
788         return tr("%n Admin(s)", 0, n);
789     case 2:
790         return tr("%n Operator(s)", 0, n);
791     case 3:
792         return tr("%n Half-Op(s)", 0, n);
793     case 4:
794         return tr("%n Voiced", 0, n);
795     default:
796         return tr("%n User(s)", 0, n);
797     }
798 }
799
800
801 IrcUserItem *UserCategoryItem::findIrcUser(IrcUser *ircUser)
802 {
803     IrcUserItem *userItem = 0;
804
805     for (int i = 0; i < childCount(); i++) {
806         userItem = qobject_cast<IrcUserItem *>(child(i));
807         if (!userItem)
808             continue;
809         if (userItem->ircUser() == ircUser)
810             return userItem;
811     }
812     return 0;
813 }
814
815
816 void UserCategoryItem::addUsers(const QList<IrcUser *> &ircUsers)
817 {
818     QList<AbstractTreeItem *> userItems;
819     foreach(IrcUser *ircUser, ircUsers)
820     userItems << new IrcUserItem(ircUser, this);
821     newChilds(userItems);
822     emit dataChanged(0);
823 }
824
825
826 bool UserCategoryItem::removeUser(IrcUser *ircUser)
827 {
828     IrcUserItem *userItem = findIrcUser(ircUser);
829     bool success = (bool)userItem;
830     if (success) {
831         removeChild(userItem);
832         emit dataChanged(0);
833     }
834     return success;
835 }
836
837
838 int UserCategoryItem::categoryFromModes(const QString &modes)
839 {
840     for (int i = 0; i < categories.count(); i++) {
841         if (modes.contains(categories[i]))
842             return i;
843     }
844     return categories.count();
845 }
846
847
848 QVariant UserCategoryItem::data(int column, int role) const
849 {
850     switch (role) {
851     case TreeModel::SortRole:
852         return _category;
853     case NetworkModel::ItemActiveRole:
854         return true;
855     case NetworkModel::ItemTypeRole:
856         return NetworkModel::UserCategoryItemType;
857     case NetworkModel::BufferIdRole:
858         return parent()->data(column, role);
859     case NetworkModel::NetworkIdRole:
860         return parent()->data(column, role);
861     case NetworkModel::BufferInfoRole:
862         return parent()->data(column, role);
863     default:
864         return PropertyMapItem::data(column, role);
865     }
866 }
867
868
869 /*****************************************
870 *  Irc User Items
871 *****************************************/
872 IrcUserItem::IrcUserItem(IrcUser *ircUser, AbstractTreeItem *parent)
873     : PropertyMapItem(QStringList() << "nickName", parent),
874     _ircUser(ircUser)
875 {
876     setObjectName(ircUser->nick());
877     connect(ircUser, SIGNAL(quited()), this, SLOT(ircUserQuited()));
878     connect(ircUser, SIGNAL(nickSet(QString)), this, SIGNAL(dataChanged()));
879     connect(ircUser, SIGNAL(awaySet(bool)), this, SIGNAL(dataChanged()));
880 }
881
882
883 QVariant IrcUserItem::data(int column, int role) const
884 {
885     switch (role) {
886     case NetworkModel::ItemActiveRole:
887         return isActive();
888     case NetworkModel::ItemTypeRole:
889         return NetworkModel::IrcUserItemType;
890     case NetworkModel::BufferIdRole:
891         return parent()->data(column, role);
892     case NetworkModel::NetworkIdRole:
893         return parent()->data(column, role);
894     case NetworkModel::BufferInfoRole:
895         return parent()->data(column, role);
896     case NetworkModel::IrcChannelRole:
897         return parent()->data(column, role);
898     case NetworkModel::IrcUserRole:
899         return QVariant::fromValue<QObject *>(_ircUser.data());
900     case NetworkModel::UserAwayRole:
901         return (bool)_ircUser ? _ircUser->isAway() : false;
902     default:
903         return PropertyMapItem::data(column, role);
904     }
905 }
906
907
908 QString IrcUserItem::toolTip(int column) const
909 {
910     Q_UNUSED(column);
911     QStringList toolTip(QString("<b>%1</b>").arg(nickName()));
912     if (_ircUser->userModes() != "") toolTip[0].append(QString(" (%1)").arg(_ircUser->userModes()));
913     if (_ircUser->isAway()) {
914         toolTip[0].append(tr(" is away"));
915         if (!_ircUser->awayMessage().isEmpty())
916             toolTip[0].append(QString(" (%1)").arg(_ircUser->awayMessage()));
917     }
918     if (!_ircUser->realName().isEmpty()) toolTip.append(_ircUser->realName());
919     if (!_ircUser->ircOperator().isEmpty()) toolTip.append(QString("%1 %2").arg(nickName()).arg(_ircUser->ircOperator()));
920     if (!_ircUser->suserHost().isEmpty()) toolTip.append(_ircUser->suserHost());
921     if (!_ircUser->whoisServiceReply().isEmpty()) toolTip.append(_ircUser->whoisServiceReply());
922
923     toolTip.append(_ircUser->hostmask().remove(0, _ircUser->hostmask().indexOf("!")+1));
924
925     if (_ircUser->idleTime().isValid()) {
926         QDateTime now = QDateTime::currentDateTime();
927         QDateTime idle = _ircUser->idleTime();
928         int idleTime = idle.secsTo(now);
929         toolTip.append(tr("idling since %1").arg(secondsToString(idleTime)));
930     }
931     if (_ircUser->loginTime().isValid()) {
932         toolTip.append(tr("login time: %1").arg(_ircUser->loginTime().toString()));
933     }
934
935     if (!_ircUser->server().isEmpty()) toolTip.append(tr("server: %1").arg(_ircUser->server()));
936
937     return QString("<p> %1 </p>").arg(toolTip.join("<br />"));
938 }
939
940
941 /*****************************************
942  * NetworkModel
943  *****************************************/
944 NetworkModel::NetworkModel(QObject *parent)
945     : TreeModel(NetworkModel::defaultHeader(), parent)
946 {
947     connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
948         this, SLOT(checkForNewBuffers(const QModelIndex &, int, int)));
949     connect(this, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
950         this, SLOT(checkForRemovedBuffers(const QModelIndex &, int, int)));
951
952     BufferSettings defaultSettings;
953     defaultSettings.notify("UserNoticesTarget", this, SLOT(messageRedirectionSettingsChanged()));
954     defaultSettings.notify("ServerNoticesTarget", this, SLOT(messageRedirectionSettingsChanged()));
955     defaultSettings.notify("ErrorMsgsTarget", this, SLOT(messageRedirectionSettingsChanged()));
956     messageRedirectionSettingsChanged();
957 }
958
959
960 QList<QVariant> NetworkModel::defaultHeader()
961 {
962     QList<QVariant> data;
963     data << tr("Chat") << tr("Topic") << tr("Nick Count");
964     return data;
965 }
966
967
968 bool NetworkModel::isBufferIndex(const QModelIndex &index) const
969 {
970     return index.data(NetworkModel::ItemTypeRole) == NetworkModel::BufferItemType;
971 }
972
973
974 int NetworkModel::networkRow(NetworkId networkId) const
975 {
976     NetworkItem *netItem = 0;
977     for (int i = 0; i < rootItem->childCount(); i++) {
978         netItem = qobject_cast<NetworkItem *>(rootItem->child(i));
979         if (!netItem)
980             continue;
981         if (netItem->networkId() == networkId)
982             return i;
983     }
984     return -1;
985 }
986
987
988 QModelIndex NetworkModel::networkIndex(NetworkId networkId)
989 {
990     int netRow = networkRow(networkId);
991     if (netRow == -1)
992         return QModelIndex();
993     else
994         return indexByItem(qobject_cast<NetworkItem *>(rootItem->child(netRow)));
995 }
996
997
998 NetworkItem *NetworkModel::findNetworkItem(NetworkId networkId) const
999 {
1000     int netRow = networkRow(networkId);
1001     if (netRow == -1)
1002         return 0;
1003     else
1004         return qobject_cast<NetworkItem *>(rootItem->child(netRow));
1005 }
1006
1007
1008 NetworkItem *NetworkModel::networkItem(NetworkId networkId)
1009 {
1010     NetworkItem *netItem = findNetworkItem(networkId);
1011
1012     if (netItem == 0) {
1013         netItem = new NetworkItem(networkId, rootItem);
1014         rootItem->newChild(netItem);
1015     }
1016     return netItem;
1017 }
1018
1019
1020 void NetworkModel::networkRemoved(const NetworkId &networkId)
1021 {
1022     int netRow = networkRow(networkId);
1023     if (netRow != -1) {
1024         rootItem->removeChild(netRow);
1025     }
1026 }
1027
1028
1029 QModelIndex NetworkModel::bufferIndex(BufferId bufferId)
1030 {
1031     if (!_bufferItemCache.contains(bufferId))
1032         return QModelIndex();
1033
1034     return indexByItem(_bufferItemCache[bufferId]);
1035 }
1036
1037
1038 BufferItem *NetworkModel::findBufferItem(BufferId bufferId) const
1039 {
1040     if (_bufferItemCache.contains(bufferId))
1041         return _bufferItemCache[bufferId];
1042     else
1043         return 0;
1044 }
1045
1046
1047 BufferItem *NetworkModel::bufferItem(const BufferInfo &bufferInfo)
1048 {
1049     if (_bufferItemCache.contains(bufferInfo.bufferId()))
1050         return _bufferItemCache[bufferInfo.bufferId()];
1051
1052     NetworkItem *netItem = networkItem(bufferInfo.networkId());
1053     return netItem->bufferItem(bufferInfo);
1054 }
1055
1056
1057 QStringList NetworkModel::mimeTypes() const
1058 {
1059     // mimetypes we accept for drops
1060     QStringList types;
1061     // comma separated list of colon separated pairs of networkid and bufferid
1062     // example: 0:1,0:2,1:4
1063     types << "application/Quassel/BufferItemList";
1064     return types;
1065 }
1066
1067
1068 bool NetworkModel::mimeContainsBufferList(const QMimeData *mimeData)
1069 {
1070     return mimeData->hasFormat("application/Quassel/BufferItemList");
1071 }
1072
1073
1074 QList<QPair<NetworkId, BufferId> > NetworkModel::mimeDataToBufferList(const QMimeData *mimeData)
1075 {
1076     QList<QPair<NetworkId, BufferId> > bufferList;
1077
1078     if (!mimeContainsBufferList(mimeData))
1079         return bufferList;
1080
1081     QStringList rawBufferList = QString::fromAscii(mimeData->data("application/Quassel/BufferItemList")).split(",");
1082     NetworkId networkId;
1083     BufferId bufferUid;
1084     foreach(QString rawBuffer, rawBufferList) {
1085         if (!rawBuffer.contains(":"))
1086             continue;
1087         networkId = rawBuffer.section(":", 0, 0).toInt();
1088         bufferUid = rawBuffer.section(":", 1, 1).toInt();
1089         bufferList.append(qMakePair(networkId, bufferUid));
1090     }
1091     return bufferList;
1092 }
1093
1094
1095 QMimeData *NetworkModel::mimeData(const QModelIndexList &indexes) const
1096 {
1097     QMimeData *mimeData = new QMimeData();
1098
1099     QStringList bufferlist;
1100     QString netid, uid, bufferid;
1101     foreach(QModelIndex index, indexes) {
1102         netid = QString::number(index.data(NetworkIdRole).value<NetworkId>().toInt());
1103         uid = QString::number(index.data(BufferIdRole).value<BufferId>().toInt());
1104         bufferid = QString("%1:%2").arg(netid).arg(uid);
1105         if (!bufferlist.contains(bufferid))
1106             bufferlist << bufferid;
1107     }
1108
1109     mimeData->setData("application/Quassel/BufferItemList", bufferlist.join(",").toAscii());
1110
1111     return mimeData;
1112 }
1113
1114
1115 void NetworkModel::attachNetwork(Network *net)
1116 {
1117     NetworkItem *netItem = networkItem(net->networkId());
1118     netItem->attachNetwork(net);
1119 }
1120
1121
1122 void NetworkModel::bufferUpdated(BufferInfo bufferInfo)
1123 {
1124     BufferItem *bufItem = bufferItem(bufferInfo);
1125     QModelIndex itemindex = indexByItem(bufItem);
1126     emit dataChanged(itemindex, itemindex);
1127 }
1128
1129
1130 void NetworkModel::removeBuffer(BufferId bufferId)
1131 {
1132     BufferItem *buffItem = findBufferItem(bufferId);
1133     if (!buffItem)
1134         return;
1135
1136     buffItem->parent()->removeChild(buffItem);
1137 }
1138
1139
1140 MsgId NetworkModel::lastSeenMsgId(BufferId bufferId) const
1141 {
1142     if (!_bufferItemCache.contains(bufferId))
1143         return MsgId();
1144
1145     return _bufferItemCache[bufferId]->lastSeenMsgId();
1146 }
1147
1148
1149 MsgId NetworkModel::markerLineMsgId(BufferId bufferId) const
1150 {
1151     if (!_bufferItemCache.contains(bufferId))
1152         return MsgId();
1153
1154     return _bufferItemCache[bufferId]->markerLineMsgId();
1155 }
1156
1157
1158 // FIXME we always seem to use this (expensive) non-const version
1159 MsgId NetworkModel::lastSeenMsgId(const BufferId &bufferId)
1160 {
1161     BufferItem *bufferItem = findBufferItem(bufferId);
1162     if (!bufferItem) {
1163         qDebug() << "NetworkModel::lastSeenMsgId(): buffer is unknown:" << bufferId;
1164         Client::purgeKnownBufferIds();
1165         return MsgId();
1166     }
1167     return bufferItem->lastSeenMsgId();
1168 }
1169
1170
1171 void NetworkModel::setLastSeenMsgId(const BufferId &bufferId, const MsgId &msgId)
1172 {
1173     BufferItem *bufferItem = findBufferItem(bufferId);
1174     if (!bufferItem) {
1175         qDebug() << "NetworkModel::setLastSeenMsgId(): buffer is unknown:" << bufferId;
1176         Client::purgeKnownBufferIds();
1177         return;
1178     }
1179     bufferItem->setLastSeenMsgId(msgId);
1180     emit lastSeenMsgSet(bufferId, msgId);
1181 }
1182
1183
1184 void NetworkModel::setMarkerLineMsgId(const BufferId &bufferId, const MsgId &msgId)
1185 {
1186     BufferItem *bufferItem = findBufferItem(bufferId);
1187     if (!bufferItem) {
1188         qDebug() << "NetworkModel::setMarkerLineMsgId(): buffer is unknown:" << bufferId;
1189         Client::purgeKnownBufferIds();
1190         return;
1191     }
1192     bufferItem->setMarkerLineMsgId(msgId);
1193     emit markerLineSet(bufferId, msgId);
1194 }
1195
1196
1197 void NetworkModel::updateBufferActivity(Message &msg)
1198 {
1199     int redirectionTarget = 0;
1200     switch (msg.type()) {
1201     case Message::Notice:
1202         if (bufferType(msg.bufferId()) != BufferInfo::ChannelBuffer) {
1203             msg.setFlags(msg.flags() | Message::Redirected);
1204             if (msg.flags() & Message::ServerMsg) {
1205                 // server notice
1206                 redirectionTarget = _serverNoticesTarget;
1207             }
1208             else {
1209                 redirectionTarget = _userNoticesTarget;
1210             }
1211         }
1212         break;
1213     case Message::Error:
1214         msg.setFlags(msg.flags() | Message::Redirected);
1215         redirectionTarget = _errorMsgsTarget;
1216         break;
1217     // Update IrcUser's last activity
1218     case Message::Plain:
1219     case Message::Action:
1220         if (bufferType(msg.bufferId()) == BufferInfo::ChannelBuffer) {
1221             const Network *net = Client::network(msg.bufferInfo().networkId());
1222             IrcUser *user = net ? net->ircUser(nickFromMask(msg.sender())) : 0;
1223             if (user)
1224                 user->setLastChannelActivity(msg.bufferId(), msg.timestamp());
1225         }
1226         break;
1227     default:
1228         break;
1229     }
1230
1231     if (msg.flags() & Message::Redirected) {
1232         if (redirectionTarget & BufferSettings::DefaultBuffer)
1233             updateBufferActivity(bufferItem(msg.bufferInfo()), msg);
1234
1235         if (redirectionTarget & BufferSettings::StatusBuffer) {
1236             const NetworkItem *netItem = findNetworkItem(msg.bufferInfo().networkId());
1237             if (netItem) {
1238                 updateBufferActivity(netItem->statusBufferItem(), msg);
1239             }
1240         }
1241     }
1242     else {
1243         updateBufferActivity(bufferItem(msg.bufferInfo()), msg);
1244     }
1245 }
1246
1247
1248 void NetworkModel::updateBufferActivity(BufferItem *bufferItem, const Message &msg)
1249 {
1250     if (!bufferItem)
1251         return;
1252
1253     bufferItem->updateActivityLevel(msg);
1254     if (bufferItem->isCurrentBuffer())
1255         emit requestSetLastSeenMsg(bufferItem->bufferId(), msg.msgId());
1256 }
1257
1258
1259 void NetworkModel::setBufferActivity(const BufferId &bufferId, BufferInfo::ActivityLevel level)
1260 {
1261     BufferItem *bufferItem = findBufferItem(bufferId);
1262     if (!bufferItem) {
1263         qDebug() << "NetworkModel::setBufferActivity(): buffer is unknown:" << bufferId;
1264         return;
1265     }
1266     bufferItem->setActivityLevel(level);
1267 }
1268
1269
1270 void NetworkModel::clearBufferActivity(const BufferId &bufferId)
1271 {
1272     BufferItem *bufferItem = findBufferItem(bufferId);
1273     if (!bufferItem) {
1274         qDebug() << "NetworkModel::clearBufferActivity(): buffer is unknown:" << bufferId;
1275         return;
1276     }
1277     bufferItem->clearActivityLevel();
1278 }
1279
1280
1281 const Network *NetworkModel::networkByIndex(const QModelIndex &index) const
1282 {
1283     QVariant netVariant = index.data(NetworkIdRole);
1284     if (!netVariant.isValid())
1285         return 0;
1286
1287     NetworkId networkId = netVariant.value<NetworkId>();
1288     return Client::network(networkId);
1289 }
1290
1291
1292 void NetworkModel::checkForRemovedBuffers(const QModelIndex &parent, int start, int end)
1293 {
1294     if (parent.data(ItemTypeRole) != NetworkItemType)
1295         return;
1296
1297     for (int row = start; row <= end; row++) {
1298         _bufferItemCache.remove(parent.child(row, 0).data(BufferIdRole).value<BufferId>());
1299     }
1300 }
1301
1302
1303 void NetworkModel::checkForNewBuffers(const QModelIndex &parent, int start, int end)
1304 {
1305     if (parent.data(ItemTypeRole) != NetworkItemType)
1306         return;
1307
1308     for (int row = start; row <= end; row++) {
1309         QModelIndex child = parent.child(row, 0);
1310         _bufferItemCache[child.data(BufferIdRole).value < BufferId > ()] = static_cast<BufferItem *>(child.internalPointer());
1311     }
1312 }
1313
1314
1315 QString NetworkModel::bufferName(BufferId bufferId) const
1316 {
1317     if (!_bufferItemCache.contains(bufferId))
1318         return QString();
1319
1320     return _bufferItemCache[bufferId]->bufferName();
1321 }
1322
1323
1324 BufferInfo::Type NetworkModel::bufferType(BufferId bufferId) const
1325 {
1326     if (!_bufferItemCache.contains(bufferId))
1327         return BufferInfo::InvalidBuffer;
1328
1329     return _bufferItemCache[bufferId]->bufferType();
1330 }
1331
1332
1333 BufferInfo NetworkModel::bufferInfo(BufferId bufferId) const
1334 {
1335     if (!_bufferItemCache.contains(bufferId))
1336         return BufferInfo();
1337
1338     return _bufferItemCache[bufferId]->bufferInfo();
1339 }
1340
1341
1342 NetworkId NetworkModel::networkId(BufferId bufferId) const
1343 {
1344     if (!_bufferItemCache.contains(bufferId))
1345         return NetworkId();
1346
1347     NetworkItem *netItem = qobject_cast<NetworkItem *>(_bufferItemCache[bufferId]->parent());
1348     if (netItem)
1349         return netItem->networkId();
1350     else
1351         return NetworkId();
1352 }
1353
1354
1355 QString NetworkModel::networkName(BufferId bufferId) const
1356 {
1357     if (!_bufferItemCache.contains(bufferId))
1358         return QString();
1359
1360     NetworkItem *netItem = qobject_cast<NetworkItem *>(_bufferItemCache[bufferId]->parent());
1361     if (netItem)
1362         return netItem->networkName();
1363     else
1364         return QString();
1365 }
1366
1367
1368 BufferId NetworkModel::bufferId(NetworkId networkId, const QString &bufferName, Qt::CaseSensitivity cs) const
1369 {
1370     const NetworkItem *netItem = findNetworkItem(networkId);
1371     if (!netItem)
1372         return BufferId();
1373
1374     for (int i = 0; i < netItem->childCount(); i++) {
1375         BufferItem *bufferItem = qobject_cast<BufferItem *>(netItem->child(i));
1376         if (bufferItem && !bufferItem->bufferName().compare(bufferName, cs))
1377             return bufferItem->bufferId();
1378     }
1379     return BufferId();
1380 }
1381
1382
1383 void NetworkModel::sortBufferIds(QList<BufferId> &bufferIds) const
1384 {
1385     QList<BufferItem *> bufferItems;
1386     foreach(BufferId bufferId, bufferIds) {
1387         if (_bufferItemCache.contains(bufferId))
1388             bufferItems << _bufferItemCache[bufferId];
1389     }
1390
1391     qSort(bufferItems.begin(), bufferItems.end(), bufferItemLessThan);
1392
1393     bufferIds.clear();
1394     foreach(BufferItem *bufferItem, bufferItems) {
1395         bufferIds << bufferItem->bufferId();
1396     }
1397 }
1398
1399
1400 QList<BufferId> NetworkModel::allBufferIdsSorted() const
1401 {
1402     QList<BufferId> bufferIds = allBufferIds();
1403     sortBufferIds(bufferIds);
1404     return bufferIds;
1405 }
1406
1407
1408 bool NetworkModel::bufferItemLessThan(const BufferItem *left, const BufferItem *right)
1409 {
1410     int leftType = left->bufferType();
1411     int rightType = right->bufferType();
1412
1413     if (leftType != rightType)
1414         return leftType < rightType;
1415     else
1416         return QString::compare(left->bufferName(), right->bufferName(), Qt::CaseInsensitive) < 0;
1417 }
1418
1419
1420 void NetworkModel::messageRedirectionSettingsChanged()
1421 {
1422     BufferSettings bufferSettings;
1423
1424     _userNoticesTarget = bufferSettings.userNoticesTarget();
1425     _serverNoticesTarget = bufferSettings.serverNoticesTarget();
1426     _errorMsgsTarget = bufferSettings.errorMsgsTarget();
1427 }