+
+void Network::setRejoinChannels(bool rejoin)
+{
+ _rejoinChannels = rejoin;
+ SYNC(ARG(rejoin))
+ emit configChanged();
+}
+
+
+void Network::setUseCustomMessageRate(bool useCustomRate)
+{
+ if (_useCustomMessageRate != useCustomRate) {
+ _useCustomMessageRate = useCustomRate;
+ SYNC(ARG(useCustomRate))
+ emit configChanged();
+ emit useCustomMessageRateSet(_useCustomMessageRate);
+ }
+}
+
+
+void Network::setMessageRateBurstSize(quint32 burstSize)
+{
+ if (burstSize < 1) {
+ // Can't go slower than one message at a time
+ qWarning() << "Received invalid setMessageRateBurstSize data, cannot have zero message "
+ "burst size!" << burstSize;
+ return;
+ }
+ if (_messageRateBurstSize != burstSize) {
+ _messageRateBurstSize = burstSize;
+ SYNC(ARG(burstSize))
+ emit configChanged();
+ emit messageRateBurstSizeSet(_messageRateBurstSize);
+ }
+}
+
+
+void Network::setMessageRateDelay(quint32 messageDelay)
+{
+ if (_messageRateDelay != messageDelay) {
+ _messageRateDelay = messageDelay;
+ SYNC(ARG(messageDelay))
+ emit configChanged();
+ emit messageRateDelaySet(_messageRateDelay);
+ }
+}
+
+
+void Network::setUnlimitedMessageRate(bool unlimitedRate)
+{
+ if (_unlimitedMessageRate != unlimitedRate) {
+ _unlimitedMessageRate = unlimitedRate;
+ SYNC(ARG(unlimitedRate))
+ emit configChanged();
+ emit unlimitedMessageRateSet(_unlimitedMessageRate);
+ }
+}
+
+
+void Network::addSupport(const QString ¶m, const QString &value)
+{
+ if (!_supports.contains(param)) {
+ _supports[param] = value;
+ SYNC(ARG(param), ARG(value))
+ }
+}
+
+
+void Network::removeSupport(const QString ¶m)
+{
+ if (_supports.contains(param)) {
+ _supports.remove(param);
+ SYNC(ARG(param))
+ }
+}
+
+
+QVariantMap Network::initSupports() const
+{
+ QVariantMap supports;
+ QHashIterator<QString, QString> iter(_supports);
+ while (iter.hasNext()) {
+ iter.next();
+ supports[iter.key()] = iter.value();
+ }
+ return supports;
+}
+
+void Network::addCap(const QString &capability, const QString &value)
+{
+ // IRCv3 specs all use lowercase capability names
+ QString _capLowercase = capability.toLower();
+ if (!_caps.contains(_capLowercase)) {
+ _caps[_capLowercase] = value;
+ SYNC(ARG(capability), ARG(value))
+ emit capAdded(_capLowercase);
+ }
+}
+
+void Network::acknowledgeCap(const QString &capability)
+{
+ // IRCv3 specs all use lowercase capability names
+ QString _capLowercase = capability.toLower();
+ if (!_capsEnabled.contains(_capLowercase)) {
+ _capsEnabled.append(_capLowercase);
+ SYNC(ARG(capability))
+ emit capAcknowledged(_capLowercase);
+ }
+}
+
+void Network::removeCap(const QString &capability)
+{
+ // IRCv3 specs all use lowercase capability names
+ QString _capLowercase = capability.toLower();
+ if (_caps.contains(_capLowercase)) {
+ // Remove from the list of available capabilities.
+ _caps.remove(_capLowercase);
+ // Remove it from the acknowledged list if it was previously acknowledged. The SYNC call
+ // ensures this propogates to the other side.
+ // Use removeOne() for speed; no more than one due to contains() check in acknowledgeCap().
+ _capsEnabled.removeOne(_capLowercase);
+ SYNC(ARG(capability))
+ emit capRemoved(_capLowercase);
+ }
+}
+
+void Network::clearCaps()
+{
+ // IRCv3 specs all use lowercase capability names
+ if (_caps.empty() && _capsEnabled.empty()) {
+ // Avoid the sync call if there's nothing to clear (e.g. failed reconnects)
+ return;
+ }
+ // To ease core-side configuration, loop through the list and emit capRemoved for each entry.
+ // If performance issues arise, this can be converted to a more-efficient setup without breaking
+ // protocol (in theory).
+ QString _capLowercase;
+ foreach (const QString &capability, _caps) {
+ _capLowercase = capability.toLower();
+ emit capRemoved(_capLowercase);
+ }
+ // Clear capabilities from the stored list
+ _caps.clear();
+ _capsEnabled.clear();
+
+ SYNC(NO_ARG)
+}
+
+QVariantMap Network::initCaps() const
+{
+ QVariantMap caps;
+ QHashIterator<QString, QString> iter(_caps);
+ while (iter.hasNext()) {
+ iter.next();
+ caps[iter.key()] = iter.value();
+ }
+ return caps;
+}
+
+
+// There's potentially a lot of users and channels, so it makes sense to optimize the format of this.
+// Rather than sending a thousand maps with identical keys, we convert this into one map containing lists
+// where each list index corresponds to a particular IrcUser. This saves sending the key names a thousand times.
+// Benchmarks have shown space savings of around 56%, resulting in saving several MBs worth of data on sync
+// (without compression) with a decent amount of IrcUsers.
+QVariantMap Network::initIrcUsersAndChannels() const
+{
+ QVariantMap usersAndChannels;
+
+ if (_ircUsers.count()) {
+ QHash<QString, QVariantList> users;
+ QHash<QString, IrcUser *>::const_iterator it = _ircUsers.begin();
+ QHash<QString, IrcUser *>::const_iterator end = _ircUsers.end();
+ while (it != end) {
+ const QVariantMap &map = it.value()->toVariantMap();
+ QVariantMap::const_iterator mapiter = map.begin();
+ while (mapiter != map.end()) {
+ users[mapiter.key()] << mapiter.value();
+ ++mapiter;
+ }
+ ++it;
+ }
+ // Can't have a container with a value type != QVariant in a QVariant :(
+ // However, working directly on a QVariantMap is awkward for appending, thus the detour via the hash above.
+ QVariantMap userMap;
+ foreach(const QString &key, users.keys())
+ userMap[key] = users[key];
+ usersAndChannels["Users"] = userMap;
+ }
+
+ if (_ircChannels.count()) {
+ QHash<QString, QVariantList> channels;
+ QHash<QString, IrcChannel *>::const_iterator it = _ircChannels.begin();
+ QHash<QString, IrcChannel *>::const_iterator end = _ircChannels.end();
+ while (it != end) {
+ const QVariantMap &map = it.value()->toVariantMap();
+ QVariantMap::const_iterator mapiter = map.begin();
+ while (mapiter != map.end()) {
+ channels[mapiter.key()] << mapiter.value();
+ ++mapiter;
+ }
+ ++it;
+ }
+ QVariantMap channelMap;
+ foreach(const QString &key, channels.keys())
+ channelMap[key] = channels[key];
+ usersAndChannels["Channels"] = channelMap;
+ }
+
+ return usersAndChannels;
+}
+
+
+void Network::initSetIrcUsersAndChannels(const QVariantMap &usersAndChannels)
+{
+ Q_ASSERT(proxy());
+ if (isInitialized()) {
+ qWarning() << "Network" << networkId() << "received init data for users and channels although there already are known users or channels!";
+ return;
+ }
+
+ // toMap() and toList() are cheap, so we can avoid copying to lists...
+ // However, we really have to make sure to never accidentally detach from the shared data!
+
+ const QVariantMap &users = usersAndChannels["Users"].toMap();
+
+ // sanity check
+ int count = users["nick"].toList().count();
+ foreach(const QString &key, users.keys()) {
+ if (users[key].toList().count() != count) {
+ qWarning() << "Received invalid usersAndChannels init data, sizes of attribute lists don't match!";
+ return;
+ }
+ }
+
+ // now create the individual IrcUsers
+ for(int i = 0; i < count; i++) {
+ QVariantMap map;
+ foreach(const QString &key, users.keys())
+ map[key] = users[key].toList().at(i);
+ newIrcUser(map["nick"].toString(), map); // newIrcUser() properly handles the hostmask being just the nick
+ }
+
+ // same thing for IrcChannels
+ const QVariantMap &channels = usersAndChannels["Channels"].toMap();
+
+ // sanity check
+ count = channels["name"].toList().count();
+ foreach(const QString &key, channels.keys()) {
+ if (channels[key].toList().count() != count) {
+ qWarning() << "Received invalid usersAndChannels init data, sizes of attribute lists don't match!";
+ return;
+ }
+ }
+ // now create the individual IrcChannels
+ for(int i = 0; i < count; i++) {
+ QVariantMap map;
+ foreach(const QString &key, channels.keys())
+ map[key] = channels[key].toList().at(i);
+ newIrcChannel(map["name"].toString(), map);
+ }
+}
+
+
+void Network::initSetSupports(const QVariantMap &supports)
+{
+ QMapIterator<QString, QVariant> iter(supports);
+ while (iter.hasNext()) {
+ iter.next();
+ addSupport(iter.key(), iter.value().toString());
+ }
+}
+
+
+void Network::initSetCaps(const QVariantMap &caps)
+{
+ QMapIterator<QString, QVariant> iter(caps);
+ while (iter.hasNext()) {
+ iter.next();
+ addCap(iter.key(), iter.value().toString());
+ }
+}
+
+
+IrcUser *Network::updateNickFromMask(const QString &mask)
+{
+ QString nick(nickFromMask(mask).toLower());
+ IrcUser *ircuser;
+
+ if (_ircUsers.contains(nick)) {
+ ircuser = _ircUsers[nick];
+ ircuser->updateHostmask(mask);
+ }
+ else {
+ ircuser = newIrcUser(mask);
+ }
+ return ircuser;
+}
+
+
+void Network::ircUserNickChanged(QString newnick)
+{
+ QString oldnick = _ircUsers.key(qobject_cast<IrcUser *>(sender()));
+
+ if (oldnick.isNull())
+ return;
+
+ if (newnick.toLower() != oldnick) _ircUsers[newnick.toLower()] = _ircUsers.take(oldnick);
+
+ if (myNick().toLower() == oldnick)
+ setMyNick(newnick);
+}
+
+
+void Network::emitConnectionError(const QString &errorMsg)
+{
+ emit connectionError(errorMsg);
+}
+
+