diff --git a/src/plugins/git/gerrit/gerritmodel.cpp b/src/plugins/git/gerrit/gerritmodel.cpp
index 1e9351adb92b6e9ef5b8b1651c554a1195ec37c7..0db0ca2c18bba6d2d6d6467d3fc21c3f96e8bcdf 100644
--- a/src/plugins/git/gerrit/gerritmodel.cpp
+++ b/src/plugins/git/gerrit/gerritmodel.cpp
@@ -46,6 +46,7 @@
 #include <QUrl>
 #include <QTextStream>
 #include <QDesktopServices>
+#include <QTextStream>
 #include <QDebug>
 #include <QScopedPointer>
 #if QT_VERSION >= 0x050000
@@ -63,6 +64,33 @@ enum { debug = 0 };
 namespace Gerrit {
 namespace Internal {
 
+QDebug operator<<(QDebug d, const GerritApproval &a)
+{
+    d.nospace() << a.reviewer << " :" << a.approval << " ("
+                << a.type << ", " << a.description << ')';
+    return d;
+}
+
+// Sort approvals by type and reviewer
+bool gerritApprovalLessThan(const GerritApproval &a1, const GerritApproval &a2)
+{
+    return a1.type.compare(a2.type) < 0 || a1.reviewer.compare(a2.reviewer) < 0;
+}
+
+QDebug operator<<(QDebug d, const GerritPatchSet &p)
+{
+    d.nospace() << " Patch set: " << p.ref << ' ' << p.patchSetNumber
+                << ' ' << p.approvals;
+    return d;
+}
+
+QDebug operator<<(QDebug d, const GerritChange &c)
+{
+    d.nospace() << c.title << " by " << c.email
+                << ' ' << c.lastUpdated << ' ' <<  c.currentPatchSet;
+    return d;
+}
+
 // Format default Url for a change
 static inline QString defaultUrl(const QSharedPointer<GerritParameters> &p, int gerritNumber)
 {
@@ -73,59 +101,108 @@ static inline QString defaultUrl(const QSharedPointer<GerritParameters> &p, int
     return result;
 }
 
-// Format approvals as "John Doe: -1, ...".
+// Format (sorted) approvals as separate HTML table
+// lines by type listing the revievers:
+// "<tr><td>Code Review</td><td>John Doe: -1, ...</tr><tr>...Sanity Review: ...".
 QString GerritPatchSet::approvalsToolTip() const
 {
+    if (approvals.isEmpty())
+        return QString();
+
     QString result;
-    if (const int approvalsCount = approvals.size()) {
-        const QString sepComma = QLatin1String(", ");
-        const QString sepColon = QLatin1String(": ");
-        for (int i = 0; i < approvalsCount; ++ i) {
-            if (i)
-                result += sepComma;
-            result += approvals.at(i).first;
-            result += sepColon;
-            result += QString::number(approvals.at(i).second);
+    QTextStream str(&result);
+    QString lastType;
+    foreach (const GerritApproval &a, approvals) {
+        if (a.type != lastType) {
+            if (!lastType.isEmpty())
+                str << "</tr>\n";
+            str << "<tr><td>"
+                << (a.description.isEmpty() ? a.type : a.description)
+                << "</td><td>";
+            lastType = a.type;
+        } else {
+            str << ", ";
         }
+        str << a.reviewer << ": " << a.approval;
+    }
+    str << "</tr>\n";
+    return result;
+}
+
+// Determine total approval level. Negative values take preference
+// and stay.
+static inline void applyApproval(int approval, int *total)
+{
+    if (approval < *total || (*total >= 0 && approval > *total))
+        *total = approval;
+}
+
+// Format the approvals similar to the columns in the Web view
+// by a type character followed by the approval level: "C: -2, S: 1"
+QString GerritPatchSet::approvalsColumn() const
+{
+    typedef QMap<QChar, int> TypeReviewMap;
+    typedef TypeReviewMap::iterator TypeReviewMapIterator;
+    typedef TypeReviewMap::const_iterator TypeReviewMapConstIterator;
+
+    QString result;
+    if (approvals.isEmpty())
+        return result;
+
+    TypeReviewMap reviews; // Sort approvals into a map by type character
+    foreach (const GerritApproval &a, approvals) {
+        if (a.type != QLatin1String("STGN")) { // Qt-Project specific: Ignore "STGN" (Staged)
+            const QChar typeChar = a.type.at(0);
+            TypeReviewMapIterator it = reviews.find(typeChar);
+            if (it == reviews.end())
+                it = reviews.insert(typeChar, 0);
+            applyApproval(a.approval, &it.value());
+        }
+    }
+
+    QTextStream str(&result);
+    const TypeReviewMapConstIterator cend = reviews.constEnd();
+    for (TypeReviewMapConstIterator it = reviews.constBegin(); it != cend; ++it) {
+        if (!result.isEmpty())
+            str << ' ';
+        str << it.key() << ": " << it.value();
     }
     return result;
 }
 
 bool GerritPatchSet::hasApproval(const QString &userName) const
 {
-    foreach (const Approval &a, approvals)
-        if (a.first == userName)
+    foreach (const GerritApproval &a, approvals)
+        if (a.reviewer == userName)
             return true;
     return false;
 }
 
-/* Return the approval level: Negative values take preference. */
 int GerritPatchSet::approvalLevel() const
 {
-    if (approvals.isEmpty())
-        return 0;
-
-    int maxLevel = -3;
-    int minLevel = 3;
-    foreach (const Approval &a, approvals) {
-        if (a.second > maxLevel)
-            maxLevel = a.second;
-        if (a.second < minLevel)
-            minLevel = a.second;
-    }
-    return minLevel < 0 ? minLevel : maxLevel;
+    int value = 0;
+    foreach (const GerritApproval &a, approvals)
+        applyApproval(a.approval, &value);
+    return value;
 }
 
 QString GerritChange::toolTip() const
 {
     static const QString format = GerritModel::tr(
-       "Subject: %1\nNumber: %2 Id: %3\nOwner: %4 <%5>\n"
-       "Project: %6 Branch: %7 Patch set: %8\nStatus: %9, %10\n"
-       "URL: %11\nApprovals: %12");
-    return format.arg(title).arg(number).arg(id, owner, email, project, branch)
+       "<html><head/><body><table>"
+       "<tr><td>Subject</td><td>%1</td></tr>"
+       "<tr><td>Number</td><td>%2"
+       "<tr><td>Owner</td><td>%3 &lt;%4&gt;</td></tr>"
+       "<tr><td>Project</td><td>%5 (%6)</td></tr>"
+       "<tr><td>Status</td><td>%7, %8</td></tr>"
+       "<tr><td>Patch set</td><td>%9</td></tr>"
+       "%10"
+       "<tr><td>URL</td><td>%11</td></tr>"
+       "</table></body></html>");
+    return format.arg(title).arg(number).arg(owner, email, project, branch)
+           .arg(status, lastUpdated.toString(Qt::DefaultLocaleShortDate))
            .arg(currentPatchSet.patchSetNumber)
-           .arg(status, lastUpdated.toString(Qt::DefaultLocaleShortDate),
-                url, currentPatchSet.approvalsToolTip());
+           .arg(currentPatchSet.approvalsToolTip(), url);
 }
 
 QString GerritChange::filterString() const
@@ -134,9 +211,9 @@ QString GerritChange::filterString() const
     QString result = QString::number(number) + blank + title + blank
             + owner + blank + project + blank
             + branch + blank + status;
-    foreach (const GerritPatchSet::Approval &a, currentPatchSet.approvals) {
+    foreach (const GerritApproval &a, currentPatchSet.approvals) {
         result += blank;
-        result += a.first;
+        result += a.reviewer;
     }
     return result;
 }
@@ -428,6 +505,9 @@ static QList<GerritChangePtr> parseOutput(const QSharedPointer<GerritParameters>
     const QString approvalsByKey = QLatin1String("by");
     const QString lastUpdatedKey = QLatin1String("lastUpdated");
     const QList<QByteArray> lines = output.split('\n');
+    const QString approvalsTypeKey = QLatin1String("type");
+    const QString approvalsDescriptionKey = QLatin1String("description");
+
     QList<GerritChangePtr> result;
     result.reserve(lines.size());
 
@@ -454,10 +534,16 @@ static QList<GerritChangePtr> parseOutput(const QSharedPointer<GerritParameters>
         const int ac = approvalsJ.size();
         for (int a = 0; a < ac; ++a) {
             const QJsonObject ao = approvalsJ.at(a).toObject();
-            const int value = ao.value(approvalsValueKey).toString().toInt();
-            const QString by = ao.value(approvalsByKey).toObject().value(ownerNameKey).toString();
-            change->currentPatchSet.approvals.push_back(GerritChange::Approval(by, value));
+            GerritApproval a;
+            a.reviewer = ao.value(approvalsByKey).toObject().value(ownerNameKey).toString();
+            a.approval = ao.value(approvalsValueKey).toString().toInt();
+            a.type = ao.value(approvalsTypeKey).toString();
+            a.description = ao.value(approvalsDescriptionKey).toString();
+            change->currentPatchSet.approvals.push_back(a);
         }
+        qStableSort(change->currentPatchSet.approvals.begin(),
+                    change->currentPatchSet.approvals.end(),
+                    gerritApprovalLessThan);
         // Remaining
         change->number = object.value(numberKey).toString().toInt();
         change->url = object.value(urlKey).toString();
@@ -531,6 +617,8 @@ static QList<GerritChangePtr> parseOutput(const QSharedPointer<GerritParameters>
     const QString approvalsKey = QLatin1String("approvals");
     const QString approvalsValueKey = QLatin1String("value");
     const QString approvalsByKey = QLatin1String("by");
+    const QString approvalsTypeKey = QLatin1String("type");
+    const QString approvalsDescriptionKey = QLatin1String("description");
     const QString lastUpdatedKey = QLatin1String("lastUpdated");
     QList<GerritChangePtr> result;
     result.reserve(lines.size());
@@ -538,7 +626,7 @@ static QList<GerritChangePtr> parseOutput(const QSharedPointer<GerritParameters>
     foreach (const QByteArray &line, lines) {
         if (line.isEmpty())
             continue;
-        QScopedPointer<Utils::JsonValue> objectValue(Utils::JsonValue::create(QString::fromAscii(line)));
+        QScopedPointer<Utils::JsonValue> objectValue(Utils::JsonValue::create(QString::fromUtf8(line)));
         if (objectValue.isNull()) {
             qWarning("Parse error: '%s'", line.constData());
             continue;
@@ -569,8 +657,16 @@ static QList<GerritChangePtr> parseOutput(const QSharedPointer<GerritParameters>
                         QTC_ASSERT(byO, break );
                         by = jsonStringMember(byO, ownerNameKey);
                     }
-                    change->currentPatchSet.approvals.push_back(GerritChange::Approval(by, value));
+                    GerritApproval a;
+                    a.reviewer = by;
+                    a.approval = value;
+                    a.type = jsonStringMember(oc, approvalsTypeKey);
+                    a.description = jsonStringMember(oc, approvalsDescriptionKey);
+                    change->currentPatchSet.approvals.push_back(a);
                 }
+                qStableSort(change->currentPatchSet.approvals.begin(),
+                            change->currentPatchSet.approvals.end(),
+                            gerritApprovalLessThan);
             }
         } // patch set
         // Remaining
@@ -599,6 +695,11 @@ static QList<GerritChangePtr> parseOutput(const QSharedPointer<GerritParameters>
             qWarning("%s: Parse error in line '%s'.", Q_FUNC_INFO, line.constData());
         }
     }
+    if (debug) {
+        qDebug() << __FUNCTION__;
+        foreach (const GerritChangePtr &p, result)
+            qDebug() << *p;
+    }
     return result;
 }
 #endif // QT_VERSION < 0x050000
@@ -641,13 +742,7 @@ void GerritModel::queryFinished(const QByteArray &output)
                 project += QLatin1String(" (") + c->branch  + QLatin1Char(')');
             row[ProjectColumn]->setText(project);
             row[StatusColumn]->setText(c->status);
-            QString approvals;
-            foreach (const GerritChange::Approval &a, c->currentPatchSet.approvals) {
-                if (!approvals.isEmpty())
-                    approvals.append(QLatin1String(", "));
-                approvals.append(QString::number(a.second));
-            }
-            row[ApprovalsColumn]->setText(approvals);
+            row[ApprovalsColumn]->setText(c->currentPatchSet.approvalsColumn());
             // Mark changes awaiting action using a bold font.
             bool bold = false;
             switch (m_query->currentQuery()) {
diff --git a/src/plugins/git/gerrit/gerritmodel.h b/src/plugins/git/gerrit/gerritmodel.h
index 85d5ef760d651e0d1a82d2230a1f171f178a90fd..41cb1609aed6860bf16c8c1d768cf51bc7c32c76 100644
--- a/src/plugins/git/gerrit/gerritmodel.h
+++ b/src/plugins/git/gerrit/gerritmodel.h
@@ -48,25 +48,32 @@ namespace Internal {
 class GerritParameters;
 class QueryContext;
 
-class GerritPatchSet {
+class GerritApproval {
 public:
-     typedef QPair<QString, int> Approval;
+    GerritApproval() : approval(-1) {}
+
+    QString type; // Review type
+    QString description; // Type description, possibly empty
+    QString reviewer;
+    int approval;
+};
 
+class GerritPatchSet {
+public:
     GerritPatchSet() : patchSetNumber(1) {}
     QString approvalsToolTip() const;
+    QString approvalsColumn() const;
     bool hasApproval(const QString &userName) const;
     int approvalLevel() const;
 
     QString ref;
     int patchSetNumber;
-    QList<Approval> approvals;
+    QList<GerritApproval> approvals;
 };
 
 class GerritChange
 {
 public:
-    typedef QPair<QString, int> Approval;
-
     GerritChange() : number(0) {}
 
     bool isValid() const { return number && !url.isEmpty() && !project.isEmpty(); }