return;
}
+ // Sort user modes first
+ const QStringList sortedModes = network()->sortPrefixModes(modes);
+
QStringList newNicks;
QStringList newModes;
QList<IrcUser *> newUsers;
for (int i = 0; i < users.count(); i++) {
ircuser = users[i];
if (!ircuser || _userModes.contains(ircuser)) {
- if (modes[i].count() > 1) {
+ if (sortedModes[i].count() > 1) {
// Multiple modes received, do it one at a time
// TODO Better way of syncing this without breaking protocol?
- for (int i_m = 0; i_m < modes[i].count(); ++i_m) {
- addUserMode(ircuser, modes[i][i_m]);
+ for (int i_m = 0; i_m < sortedModes[i].count(); ++i_m) {
+ addUserMode(ircuser, sortedModes[i][i_m]);
}
} else {
- addUserMode(ircuser, modes[i]);
+ addUserMode(ircuser, sortedModes[i]);
}
continue;
}
- _userModes[ircuser] = modes[i];
+ _userModes[ircuser] = sortedModes[i];
ircuser->joinChannel(this, true);
connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickSet(QString)));
// the joins are propagated by the ircuser. The signal ircUserJoined is only for convenience
newNicks << ircuser->nick();
- newModes << modes[i];
+ newModes << sortedModes[i];
newUsers << ircuser;
}
void IrcChannel::setUserModes(IrcUser *ircuser, const QString &modes)
{
if (isKnownUser(ircuser)) {
- _userModes[ircuser] = modes;
+ // Keep user modes sorted
+ _userModes[ircuser] = network()->sortPrefixModes(modes);
QString nick = ircuser->nick();
SYNC_OTHER(setUserModes, ARG(nick), ARG(modes))
emit ircUserModesSet(ircuser, modes);
return;
if (!_userModes[ircuser].contains(mode)) {
- _userModes[ircuser] += mode;
+ // Keep user modes sorted
+ _userModes[ircuser] = network()->sortPrefixModes(_userModes[ircuser] + mode);
QString nick = ircuser->nick();
SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
emit ircUserModeAdded(ircuser, mode);
return;
if (_userModes[ircuser].contains(mode)) {
+ // Removing modes shouldn't mess up ordering
_userModes[ircuser].remove(mode);
QString nick = ircuser->nick();
SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
modes << iter.value().toString();
++iter;
}
+ // joinIrcUsers handles sorting modes
joinIrcUsers(users, modes);
}
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
+#include <algorithm>
+
#include <QTextCodec>
#include "network.h"
}
+QString Network::sortPrefixModes(const QString &modes) const
+{
+ // If modes is empty or we don't have any modes, nothing can be sorted, bail out early
+ if (modes.isEmpty() || prefixModes().isEmpty()) {
+ return modes;
+ }
+
+ // Store a copy of the modes for modification
+ // QString should be efficient and not copy memory if nothing changes, but if mistaken,
+ // std::is_sorted could be called first.
+ QString sortedModes = QString(modes);
+
+ // Sort modes as if a QChar array
+ // See https://en.cppreference.com/w/cpp/algorithm/sort
+ // Defining lambda with [&] implicitly captures variables by reference
+ std::sort(sortedModes.begin(), sortedModes.end(), [&](const QChar &lmode, const QChar &rmode) {
+ // Compare characters according to prefix modes
+ // Return true if lmode comes before rmode (is "less than")
+
+ // Check for unknown modes...
+ if (!prefixModes().contains(lmode)) {
+ // Left mode not in prefix list, send to end
+ return false;
+ } else if (!prefixModes().contains(rmode)) {
+ // Right mode not in prefix list, send to end
+ return true;
+ } else {
+ // Both characters known, sort according to index in prefixModes()
+ return (prefixModes().indexOf(lmode) < prefixModes().indexOf(rmode));
+ }
+ });
+
+ return sortedModes;
+}
+
+
QStringList Network::nicks() const
{
// we don't use _ircUsers.keys() since the keys may be
}
/**@}*/
+ /**
+ * Sorts the user channelmodes according to priority set by PREFIX
+ *
+ * Given a list of channel modes, sorts according to the order of PREFIX, putting the highest
+ * modes first. Any unknown modes are moved to the end in no given order.
+ *
+ * If prefix modes cannot be determined from the network, no changes will be made.
+ *
+ * @param modes User channelmodes
+ * @return Priority-sorted user channelmodes
+ */
+ QString sortPrefixModes(const QString &modes) const;
+
+ /**@{*/
+ /**
+ * Sorts the list of users' channelmodes according to priority set by PREFIX
+ *
+ * Maintains order of the modes list.
+ *
+ * @seealso Network::sortPrefixModes()
+ *
+ * @param modesList List of users' channel modes
+ * @return Priority-sorted list of users' channel modes
+ */
+ inline QStringList sortPrefixModes(const QStringList &modesList) const {
+ QStringList sortedModesList;
+ // Sort each individual mode string, appending back
+ // Must maintain the order received!
+ for (QString modes : modesList) {
+ sortedModesList << sortPrefixModes(modes);
+ }
+ return sortedModesList;
+ }
+ /**@}*/
+
ChannelModeType channelModeType(const QString &mode);
inline ChannelModeType channelModeType(const QCharRef &mode) { return channelModeType(QString(mode)); }