Center-align tooltips, fix QML, italicize unknown
[quassel.git] / src / client / networkmodel.cpp
index d6bd9f4..599b710 100644 (file)
@@ -76,7 +76,7 @@ QVariant NetworkItem::data(int column, int role) const
 
 QString NetworkItem::escapeHTML(const QString &string, bool useNonbreakingSpaces)
 {
-    // QString.replace() doesn't guarentee the source string will remain constant.
+    // QString.replace() doesn't guarantee the source string will remain constant.
     // Use a local variable to avoid compiler errors.
 #if QT_VERSION < 0x050000
     QString formattedString = Qt::escape(string);
@@ -224,7 +224,7 @@ QString NetworkItem::toolTip(int column) const
     Q_UNUSED(column);
     QString strTooltip;
     QTextStream tooltip( &strTooltip, QIODevice::WriteOnly );
-    tooltip << "<qt><style>.bold { font-weight: bold; }</style>";
+    tooltip << "<qt><style>.bold { font-weight: bold; } .italic { font-style: italic; }</style>";
 
     // Function to add a row to the tooltip table
     auto addRow = [&](const QString& key, const QString& value, bool condition) {
@@ -234,15 +234,18 @@ QString NetworkItem::toolTip(int column) const
     };
 
     tooltip << "<p class='bold' align='center'>" << NetworkItem::escapeHTML(networkName(), true) << "</p>";
-    tooltip << "<table cellspacing='5' cellpadding='0'>";
-    addRow(tr("Server"), NetworkItem::escapeHTML(currentServer(), true), true);
-
-    addRow(tr("Users"), QString::number(nickCount()), true);
-
-    if (_network)
-        addRow(tr("Lag"), NetworkItem::escapeHTML(tr("%1 msecs").arg(_network->latency()), true), true);
+    if (isActive()) {
+        tooltip << "<table cellspacing='5' cellpadding='0'>";
+        addRow(tr("Server"), NetworkItem::escapeHTML(currentServer(), true), !currentServer().isEmpty());
+        addRow(tr("Users"), QString::number(nickCount()), true);
+        if (_network)
+            addRow(tr("Lag"), NetworkItem::escapeHTML(tr("%1 msecs").arg(_network->latency()), true), true);
 
-    tooltip << "</table></qt>";
+        tooltip << "</table>";
+    } else {
+        tooltip << "<p class='italic' align='center'>" << tr("Not connected") << "</p>";
+    }
+    tooltip << "</qt>";
     return strTooltip;
 }
 
@@ -519,8 +522,7 @@ QString QueryBufferItem::toolTip(int column) const
     Q_UNUSED(column);
     QString strTooltip;
     QTextStream tooltip( &strTooltip, QIODevice::WriteOnly );
-    tooltip << "<qt><style>.bold { font-weight: bold; }</style>"
-            << "<style>.italic { font-style: italic; }</style>";
+    tooltip << "<qt><style>.bold { font-weight: bold; } .italic { font-style: italic; }</style>";
 
     // Keep track of whether or not information has been added
     bool infoAdded = false;
@@ -549,26 +551,66 @@ QString QueryBufferItem::toolTip(int column) const
 
         tooltip << "<table cellspacing='5' cellpadding='0'>";
         if (_ircUser->isAway()) {
-            QString awayMessage(tr("(unknown)"));
-            if(!_ircUser->awayMessage().isEmpty()) {
-                awayMessage = _ircUser->awayMessage();
+            QString awayMessageHTML = QString("<p class='italic'>%1</p>").arg(tr("Unknown"));
+
+            // If away message is known, replace with the escaped message.
+            if (!_ircUser->awayMessage().isEmpty()) {
+                awayMessageHTML = NetworkItem::escapeHTML(_ircUser->awayMessage());
             }
-            addRow(NetworkItem::escapeHTML(tr("Away message"), true), NetworkItem::escapeHTML(awayMessage), true);
+            addRow(NetworkItem::escapeHTML(tr("Away message"), true), awayMessageHTML, true);
         }
         addRow(tr("Realname"),
                NetworkItem::escapeHTML(_ircUser->realName()),
                !_ircUser->realName().isEmpty());
-        addRow(NetworkItem::escapeHTML(tr("Suser Host"), true),
-               NetworkItem::escapeHTML(_ircUser->suserHost()),
-               !_ircUser->suserHost().isEmpty());
-        addRow(NetworkItem::escapeHTML(tr("Whois Service Reply"), true),
-               NetworkItem::escapeHTML(_ircUser->whoisServiceReply()),
-               !_ircUser->whoisServiceReply().isEmpty());
+        // suserHost may return "<nick> is available for help", which should be translated.
+        // See https://www.alien.net.au/irc/irc2numerics.html
+        if(_ircUser->suserHost().endsWith("available for help")) {
+            addRow(NetworkItem::escapeHTML(tr("Help status"), true),
+                   NetworkItem::escapeHTML(tr("Available for help")),
+                   true);
+        } else {
+            addRow(NetworkItem::escapeHTML(tr("Service status"), true),
+                   NetworkItem::escapeHTML(_ircUser->suserHost()),
+                   !_ircUser->suserHost().isEmpty());
+        }
+
+        // Keep track of whether or not the account information's been added.  Don't show it twice.
+        bool accountAdded = false;
+        if(!_ircUser->account().isEmpty()) {
+            // IRCv3 account-notify is supported by the core and IRC server.
+            // Assume logged out (seems to be more common)
+            QString accountHTML = QString("<p class='italic'>%1</p>").arg(tr("Not logged in"));
+
+            // If account is logged in, replace with the escaped account name.
+            if (_ircUser->account() != "*") {
+                accountHTML = NetworkItem::escapeHTML(_ircUser->account());
+            }
+            addRow(NetworkItem::escapeHTML(tr("Account"), true),
+                   accountHTML,
+                   true);
+            // Mark the row as added
+            accountAdded = true;
+        }
+        // whoisServiceReply may return "<nick> is identified for this nick", which should be translated.
+        // See https://www.alien.net.au/irc/irc2numerics.html
+        if(_ircUser->whoisServiceReply().endsWith("identified for this nick")) {
+            addRow(NetworkItem::escapeHTML(tr("Account"), true),
+                   NetworkItem::escapeHTML(tr("Identified for this nick")),
+                   !accountAdded);
+            // Don't add the account row again if information's already added via account-notify
+            // Mark the row as added
+            accountAdded = true;
+        } else {
+            addRow(NetworkItem::escapeHTML(tr("Service Reply"), true),
+                   NetworkItem::escapeHTML(_ircUser->whoisServiceReply()),
+                   !_ircUser->whoisServiceReply().isEmpty());
+        }
         addRow(tr("Hostmask"),
                NetworkItem::escapeHTML(_ircUser->hostmask().remove(0, _ircUser->hostmask().indexOf("!") + 1)),
                !(_ircUser->hostmask().remove(0, _ircUser->hostmask().indexOf("!") + 1) == "@"));
+        // ircOperator may contain "is an" or "is a", which should be removed.
         addRow(tr("Operator"),
-               NetworkItem::escapeHTML(_ircUser->ircOperator()),
+               NetworkItem::escapeHTML(_ircUser->ircOperator().replace("is an ", "").replace("is a ", "")),
                !_ircUser->ircOperator().isEmpty());
 
         if (_ircUser->idleTime().isValid()) {
@@ -588,7 +630,7 @@ QString QueryBufferItem::toolTip(int column) const
 
     // If no further information found, offer an explanatory message
     if (!infoAdded)
-        tooltip << "<p class='italic'>" << tr("No information available") << "</p>";
+        tooltip << "<p class='italic' align='center'>" << tr("No information available") << "</p>";
 
     tooltip << "</qt>";
     return strTooltip;
@@ -649,8 +691,7 @@ QString ChannelBufferItem::toolTip(int column) const
     Q_UNUSED(column);
     QString strTooltip;
     QTextStream tooltip( &strTooltip, QIODevice::WriteOnly );
-    tooltip << "<qt><style>.bold { font-weight: bold; }</style>"
-            << "<qt><style>.italic { font-style: italic; }</style>";
+    tooltip << "<qt><style>.bold { font-weight: bold; } .italic { font-style: italic; }</style>";
 
     // Function to add a row to the tooltip table
     auto addRow = [&](const QString& key, const QString& value, bool condition) {
@@ -685,7 +726,7 @@ QString ChannelBufferItem::toolTip(int column) const
 
         tooltip << "</table>";
     } else {
-        tooltip << "<p class='italic'>" << tr("Not active, double-click to join") << "</p>";
+        tooltip << "<p class='italic' align='center'>" << tr("Not active, double-click to join") << "</p>";
     }
 
     tooltip << "</qt>";
@@ -727,6 +768,16 @@ void ChannelBufferItem::attachIrcChannel(IrcChannel *ircChannel)
     emit dataChanged();
 }
 
+QString ChannelBufferItem::nickChannelModes(const QString &nick) const
+{
+    if (!_ircChannel) {
+        qDebug() << Q_FUNC_INFO << "IrcChannel not set, can't get user modes";
+        return QString();
+    }
+
+    return _ircChannel->userModes(nick);
+}
+
 
 void ChannelBufferItem::ircChannelParted()
 {
@@ -1025,8 +1076,7 @@ QString IrcUserItem::toolTip(int column) const
     Q_UNUSED(column);
     QString strTooltip;
     QTextStream tooltip( &strTooltip, QIODevice::WriteOnly );
-    tooltip << "<qt><style>.bold { font-weight: bold; }</style>"
-            << "<style>.italic { font-style: italic; }</style>";
+    tooltip << "<qt><style>.bold { font-weight: bold; } .italic { font-style: italic; }</style>";
 
     // Keep track of whether or not information has been added
     bool infoAdded = false;
@@ -1048,27 +1098,71 @@ QString IrcUserItem::toolTip(int column) const
     };
 
     tooltip << "<table cellspacing='5' cellpadding='0'>";
+    addRow(tr("Modes"),
+           NetworkItem::escapeHTML(channelModes()),
+           !channelModes().isEmpty());
     if (_ircUser->isAway()) {
-        QString awayMessage(tr("(unknown)"));
-        if(!_ircUser->awayMessage().isEmpty()) {
-            awayMessage = _ircUser->awayMessage();
+        QString awayMessageHTML = QString("<p class='italic'>%1</p>").arg(tr("Unknown"));
+
+        // If away message is known, replace with the escaped message.
+        if (!_ircUser->awayMessage().isEmpty()) {
+            awayMessageHTML = NetworkItem::escapeHTML(_ircUser->awayMessage());
         }
-        addRow(NetworkItem::escapeHTML(tr("Away message"), true), NetworkItem::escapeHTML(awayMessage), true);
+        addRow(NetworkItem::escapeHTML(tr("Away message"), true), awayMessageHTML, true);
     }
     addRow(tr("Realname"),
            NetworkItem::escapeHTML(_ircUser->realName()),
            !_ircUser->realName().isEmpty());
-    addRow(NetworkItem::escapeHTML(tr("Suser Host"), true),
-           NetworkItem::escapeHTML(_ircUser->suserHost()),
-           !_ircUser->suserHost().isEmpty());
-    addRow(NetworkItem::escapeHTML(tr("Whois Service Reply"), true),
-           NetworkItem::escapeHTML(_ircUser->whoisServiceReply()),
-           !_ircUser->whoisServiceReply().isEmpty());
+
+    // suserHost may return "<nick> is available for help", which should be translated.
+    // See https://www.alien.net.au/irc/irc2numerics.html
+    if(_ircUser->suserHost().endsWith("available for help")) {
+        addRow(NetworkItem::escapeHTML(tr("Help status"), true),
+               NetworkItem::escapeHTML(tr("Available for help")),
+               true);
+    } else {
+        addRow(NetworkItem::escapeHTML(tr("Service status"), true),
+               NetworkItem::escapeHTML(_ircUser->suserHost()),
+               !_ircUser->suserHost().isEmpty());
+    }
+
+    // Keep track of whether or not the account information's been added.  Don't show it twice.
+    bool accountAdded = false;
+    if(!_ircUser->account().isEmpty()) {
+        // IRCv3 account-notify is supported by the core and IRC server.
+        // Assume logged out (seems to be more common)
+        QString accountHTML = QString("<p class='italic'>%1</p>").arg(tr("Not logged in"));
+
+        // If account is logged in, replace with the escaped account name.
+        if (_ircUser->account() != "*") {
+            accountHTML = NetworkItem::escapeHTML(_ircUser->account());
+        }
+        addRow(NetworkItem::escapeHTML(tr("Account"), true),
+               accountHTML,
+               true);
+        // Mark the row as added
+        accountAdded = true;
+    }
+    // whoisServiceReply may return "<nick> is identified for this nick", which should be translated.
+    // See https://www.alien.net.au/irc/irc2numerics.html
+    if(_ircUser->whoisServiceReply().endsWith("identified for this nick")) {
+        addRow(NetworkItem::escapeHTML(tr("Account"), true),
+               NetworkItem::escapeHTML(tr("Identified for this nick")),
+               !accountAdded);
+        // Don't add the account row again if information's already added via account-notify
+        // Mark the row as added
+        accountAdded = true;
+    } else {
+        addRow(NetworkItem::escapeHTML(tr("Service Reply"), true),
+               NetworkItem::escapeHTML(_ircUser->whoisServiceReply()),
+               !_ircUser->whoisServiceReply().isEmpty());
+    }
     addRow(tr("Hostmask"),
            NetworkItem::escapeHTML(_ircUser->hostmask().remove(0, _ircUser->hostmask().indexOf("!") + 1)),
            !(_ircUser->hostmask().remove(0, _ircUser->hostmask().indexOf("!") + 1) == "@"));
+    // ircOperator may contain "is an" or "is a", which should be removed.
     addRow(tr("Operator"),
-           NetworkItem::escapeHTML(_ircUser->ircOperator()),
+           NetworkItem::escapeHTML(_ircUser->ircOperator().replace("is an ", "").replace("is a ", "")),
            !_ircUser->ircOperator().isEmpty());
 
     if (_ircUser->idleTime().isValid()) {
@@ -1087,12 +1181,27 @@ QString IrcUserItem::toolTip(int column) const
 
     // If no further information found, offer an explanatory message
     if (!infoAdded)
-        tooltip << "<p class='italic'>" << tr("No information available") << "</p>";
+        tooltip << "<p class='italic' align='center'>" << tr("No information available") << "</p>";
 
     tooltip << "</qt>";
     return strTooltip;
 }
 
+QString IrcUserItem::channelModes() const
+{
+    // IrcUserItems are parented to UserCategoryItem, which are parented to ChannelBufferItem.
+    // We want the channel buffer item in order to get the channel-specific user modes.
+    UserCategoryItem *category = qobject_cast<UserCategoryItem *>(parent());
+    if (!category)
+        return QString();
+
+    ChannelBufferItem *channel = qobject_cast<ChannelBufferItem *>(category->parent());
+    if (!channel)
+        return QString();
+
+    return channel->nickChannelModes(nickName());
+}
+
 
 /*****************************************
  * NetworkModel