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