From 0ccdbbc91d7b0fe9bf11558196ba87ccab31ed40 Mon Sep 17 00:00:00 2001
From: Leandro Melo <leandro.melo@nokia.com>
Date: Thu, 22 Jul 2010 12:00:21 +0200
Subject: [PATCH] C++ tooltip: Integration with Qt docs always on (for showing
 brief descriptions).

The old integration option is now used to switch to extended descriptions.
---
 src/libs/utils/htmldocextractor.cpp           |  55 +++++++-
 src/libs/utils/htmldocextractor.h             |   8 +-
 src/plugins/cppeditor/cpphoverhandler.cpp     | 131 +++++++++---------
 src/plugins/cppeditor/cpphoverhandler.h       |   8 +-
 src/plugins/texteditor/displaysettings.cpp    |  10 +-
 src/plugins/texteditor/displaysettings.h      |   2 +-
 .../texteditor/displaysettingspage.cpp        |   6 +-
 src/plugins/texteditor/displaysettingspage.ui |   4 +-
 8 files changed, 134 insertions(+), 90 deletions(-)

diff --git a/src/libs/utils/htmldocextractor.cpp b/src/libs/utils/htmldocextractor.cpp
index cbaa37fc477..83b8afafe3e 100644
--- a/src/libs/utils/htmldocextractor.cpp
+++ b/src/libs/utils/htmldocextractor.cpp
@@ -47,16 +47,21 @@ namespace {
 HtmlDocExtractor::HtmlDocExtractor() :
     m_lengthReference(-1),
     m_truncateAtParagraph(false),
-    m_formatContents(true)
+    m_formatContents(true),
+    m_extendedExtraction(false)
 {}
 
-void HtmlDocExtractor::setLengthReference(const int length, const bool truncateAtParagraph)
+void HtmlDocExtractor::extractFirstParagraphOnly()
+{ m_extendedExtraction = false; }
+
+void HtmlDocExtractor::extractExtendedContents(const int length, const bool truncateAtParagraph)
 {
     m_lengthReference = length;
     m_truncateAtParagraph = truncateAtParagraph;
+    m_extendedExtraction = true;
 }
 
-void HtmlDocExtractor::setFormatContents(const bool format)
+void HtmlDocExtractor::applyFormatting(const bool format)
 { m_formatContents = format; }
 
 QString HtmlDocExtractor::getClassOrNamespaceBrief(const QString &html, const QString &mark) const
@@ -66,7 +71,6 @@ QString HtmlDocExtractor::getClassOrNamespaceBrief(const QString &html, const QS
         contents.remove(QLatin1String("<a href=\"#details\">More...</a>"));
         contents.prepend(QLatin1String("<nobr>"));
         contents.append(QLatin1String("</nobr>"));
-        formatContents(&contents);
     }
 
     return contents;
@@ -75,6 +79,9 @@ QString HtmlDocExtractor::getClassOrNamespaceBrief(const QString &html, const QS
 QString HtmlDocExtractor::getClassOrNamespaceDescription(const QString &html,
                                                          const QString &mark) const
 {
+    if (!m_extendedExtraction)
+        return getClassOrNamespaceBrief(html, mark);
+
     QString contents = getContentsByMarks(html, mark + QLatin1String("-description"), mark);
     if (!contents.isEmpty() && m_formatContents) {
         contents.remove(QLatin1String("Detailed Description"));
@@ -178,8 +185,18 @@ QString HtmlDocExtractor::getContentsByMarks(const QString &html,
 
 void HtmlDocExtractor::formatContents(QString *html) const
 {
-    if (html->isEmpty())
-        return;
+    if (!m_extendedExtraction) {
+        int paragraph = html->indexOf(QLatin1String("</p>"));
+        if (paragraph != -1) {
+            paragraph += 4;
+            html->truncate(paragraph);
+        } else {
+            // Some enumerations don't have paragraphs and just a table with the items. In such
+            // cases the the html is cleared to avoid showing more that desired.
+            html->clear();
+            return;
+        }
+    }
 
     if (m_formatContents) {
         stripBold(html);
@@ -192,9 +209,28 @@ void HtmlDocExtractor::formatContents(QString *html) const
         stripTagsStyles(html);
         stripHeadings(html);
         stripImagens(html);
+        stripEmptyParagraphs(html);
+
+        if (!m_extendedExtraction) {
+            if (!html->endsWith(QLatin1String(".</p>"))) {
+                // <p>For paragraphs similar to this. Example:</p>
+                const int lastDot = html->lastIndexOf(QLatin1Char('.'));
+                if (lastDot != -1) {
+                    html->truncate(lastDot);
+                    html->append(QLatin1String(".</p>"));
+                }
+            }
+        }
+
+        const int noBreakLimit = 140;
+        const int paragraph = html->indexOf(QLatin1String("<p>"));
+        if (paragraph > 0 && paragraph <= noBreakLimit) {
+            html->insert(paragraph, QLatin1String("</nobr>"));
+            html->prepend(QLatin1String("<nobr>"));
+        }
     }
 
-    if (m_lengthReference > -1 && html->length() > m_lengthReference) {
+    if (m_extendedExtraction && m_lengthReference > -1 && html->length() > m_lengthReference) {
         if (m_truncateAtParagraph) {
             const int nextBegin = html->indexOf(QLatin1String("<p>"), m_lengthReference);
             QRegExp exp = createMinimalExp(QLatin1String("</p>|<br />"));
@@ -262,6 +298,11 @@ void HtmlDocExtractor::stripBold(QString *html)
     html->remove(QLatin1String("</b>"));
 }
 
+void HtmlDocExtractor::stripEmptyParagraphs(QString *html)
+{
+    html->remove(QLatin1String("<p></p>"));
+}
+
 void HtmlDocExtractor::replaceNonStyledHeadingsForBold(QString *html)
 {
     const QRegExp &hStart = createMinimalExp(QLatin1String("<h\\d{1}>"));
diff --git a/src/libs/utils/htmldocextractor.h b/src/libs/utils/htmldocextractor.h
index 67f23df369a..02706dc15a4 100644
--- a/src/libs/utils/htmldocextractor.h
+++ b/src/libs/utils/htmldocextractor.h
@@ -41,8 +41,10 @@ class QTCREATOR_UTILS_EXPORT HtmlDocExtractor
 public:
     HtmlDocExtractor();
 
-    void setLengthReference(const int reference, const bool truncateAtParagraph);
-    void setFormatContents(const bool format);
+    void extractFirstParagraphOnly();
+    void extractExtendedContents(const int lengthReference, const bool truncateAtParagraph);
+
+    void applyFormatting(const bool format);
 
     QString getClassOrNamespaceBrief(const QString &html, const QString &mark) const;
     QString getClassOrNamespaceDescription(const QString &html, const QString &mark) const;
@@ -72,6 +74,7 @@ private:
     static void stripTeletypes(QString *html);
     static void stripImagens(QString *html);
     static void stripBold(QString *html);
+    static void stripEmptyParagraphs(QString *html);
     static void replaceNonStyledHeadingsForBold(QString *html);
     static void replaceTablesForSimpleLines(QString *html);
     static void replaceListsForSimpleLines(QString *html);
@@ -79,6 +82,7 @@ private:
     int m_lengthReference;
     bool m_truncateAtParagraph;
     bool m_formatContents;
+    bool m_extendedExtraction;
 };
 
 } // namespace Utils
diff --git a/src/plugins/cppeditor/cpphoverhandler.cpp b/src/plugins/cppeditor/cpphoverhandler.cpp
index a8848e8752b..3c1c36cb29d 100644
--- a/src/plugins/cppeditor/cpphoverhandler.cpp
+++ b/src/plugins/cppeditor/cpphoverhandler.cpp
@@ -118,8 +118,6 @@ CppHoverHandler::CppHoverHandler(QObject *parent)
     m_modelManager =
         ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
 
-    m_htmlDocExtractor.setLengthReference(1000, true);
-
     // Listen for editor opened events in order to connect to tooltip/helpid requests
     connect(ICore::instance()->editorManager(), SIGNAL(editorOpened(Core::IEditor *)),
             this, SLOT(editorOpened(Core::IEditor *)));
@@ -174,9 +172,9 @@ void CppHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint
         QToolTip::hideText();
     } else {
         if (!m_classHierarchy.isEmpty())
-            generateDiagramTooltip(baseEditor->displaySettings().m_integrateDocsIntoTooltips);
+            generateDiagramTooltip(baseEditor->displaySettings().m_extendTooltips);
         else
-            generateNormalTooltip(baseEditor->displaySettings().m_integrateDocsIntoTooltips);
+            generateNormalTooltip(baseEditor->displaySettings().m_extendTooltips);
 
         if (m_matchingHelpCandidate != -1)
             addF1ToTooltip();
@@ -384,15 +382,20 @@ bool CppHoverHandler::helpIdExists(const QString &helpId) const
     return false;
 }
 
-QString CppHoverHandler::getDocContents() const
+QString CppHoverHandler::getDocContents(const bool extended)
 {
     Q_ASSERT(m_matchingHelpCandidate >= 0);
 
-    return getDocContents(m_helpCandidates.at(m_matchingHelpCandidate));
+    return getDocContents(m_helpCandidates.at(m_matchingHelpCandidate), extended);
 }
 
-QString CppHoverHandler::getDocContents(const HelpCandidate &help) const
+QString CppHoverHandler::getDocContents(const HelpCandidate &help, const bool extended)
 {
+    if (extended)
+        m_htmlDocExtractor.extractExtendedContents(1500, true);
+    else
+        m_htmlDocExtractor.extractFirstParagraphOnly();
+
     QString contents;
     QMap<QString, QUrl> helpLinks =
         Core::HelpManager::instance()->linksForIdentifier(help.m_helpId);
@@ -427,7 +430,7 @@ QString CppHoverHandler::getDocContents(const HelpCandidate &help) const
     return contents;
 }
 
-void CppHoverHandler::generateDiagramTooltip(const bool integrateDocs)
+void CppHoverHandler::generateDiagramTooltip(const bool extendTooltips)
 {
     QString clazz = m_toolTip;
 
@@ -457,75 +460,71 @@ void CppHoverHandler::generateDiagramTooltip(const bool integrateDocs)
     diagram.append(QLatin1String("</table>"));
     m_toolTip = diagram;
 
-    if (integrateDocs) {
-        if (m_matchingHelpCandidate != -1) {
-            m_toolTip.append(getDocContents());
-        } else {
-            // Look for documented base classes. Diagram the nearest one or the nearest ones (in
-            // the case there are many at the same level).
-            int helpLevel = 0;
-            QList<int> baseClassesWithHelp;
-            for (int i = 0; i < m_classHierarchy.size(); ++i) {
-                const QStringList &hierarchy = m_classHierarchy.at(i);
-                if (helpLevel != 0 && hierarchy.size() != helpLevel)
-                    break;
-
-                const QString &name = hierarchy.last();
-                if (helpIdExists(name)) {
-                    baseClassesWithHelp.append(i);
-                    if (helpLevel == 0)
-                        helpLevel = hierarchy.size();
-                }
+    if (m_matchingHelpCandidate != -1) {
+        m_toolTip.append(getDocContents(extendTooltips));
+    } else {
+        // Look for documented base classes. Diagram the nearest one or the nearest ones (in
+        // the case there are many at the same level).
+        int helpLevel = 0;
+        QList<int> baseClassesWithHelp;
+        for (int i = 0; i < m_classHierarchy.size(); ++i) {
+            const QStringList &hierarchy = m_classHierarchy.at(i);
+            if (helpLevel != 0 && hierarchy.size() != helpLevel)
+                break;
+
+            const QString &name = hierarchy.last();
+            if (helpIdExists(name)) {
+                baseClassesWithHelp.append(i);
+                if (helpLevel == 0)
+                    helpLevel = hierarchy.size();
             }
+        }
+
+        if (!baseClassesWithHelp.isEmpty()) {
+            // Choose the first one as the help match.
+            QString base = m_classHierarchy.at(baseClassesWithHelp.at(0)).last();
+            HelpCandidate help(base, base, HelpCandidate::ClassOrNamespace);
+            m_helpCandidates.append(help);
+            m_matchingHelpCandidate = m_helpCandidates.size() - 1;
 
-            if (!baseClassesWithHelp.isEmpty()) {
-                // Choose the first one as the help match.
-                QString base = m_classHierarchy.at(baseClassesWithHelp.at(0)).last();
-                HelpCandidate help(base, base, HelpCandidate::ClassOrNamespace);
-                m_helpCandidates.append(help);
-                m_matchingHelpCandidate = m_helpCandidates.size() - 1;
-
-                if (baseClassesWithHelp.size() == 1 && helpLevel == 1) {
-                    m_toolTip.append(getDocContents(help));
-                } else {
-                    foreach (int hierarchyIndex, baseClassesWithHelp) {
-                        m_toolTip.append(QLatin1String("<p>"));
-                        const QStringList &hierarchy = m_classHierarchy.at(hierarchyIndex);
-                        Q_ASSERT(helpLevel <= hierarchy.size());
-
-                        // Following contents are inside tables so they are on the exact same
-                        // alignment as the top level diagram.
-                        diagram = QString(QLatin1String("<table><tr><td>%1</td>")).arg(clazz);
-                        for (int i = 0; i < helpLevel; ++i) {
-                            diagram.append(
-                                QLatin1String("<td><img src=\":/cppeditor/images/rightarrow.png\">"
-                                              "</td><td>") %
-                                hierarchy.at(i) %
-                                QLatin1String("</td>"));
-                        }
-                        diagram.append(QLatin1String("</tr></table>"));
-
-                        base = hierarchy.at(helpLevel - 1);
-                        QString contents =
-                            getDocContents(HelpCandidate(base, base, HelpCandidate::Brief));
-                        if (!contents.isEmpty()) {
-                            m_toolTip.append(diagram % QLatin1String("<table><tr><td>") %
-                                             contents % QLatin1String("</td></tr></table>"));
-                        }
-                        m_toolTip.append(QLatin1String("</p>"));
+            if (baseClassesWithHelp.size() == 1 && helpLevel == 1) {
+                m_toolTip.append(getDocContents(help, extendTooltips));
+            } else {
+                foreach (int hierarchyIndex, baseClassesWithHelp) {
+                    m_toolTip.append(QLatin1String("<p>"));
+                    const QStringList &hierarchy = m_classHierarchy.at(hierarchyIndex);
+                    Q_ASSERT(helpLevel <= hierarchy.size());
+
+                    // Following contents are inside tables so they are on the exact same
+                    // alignment as the top level diagram.
+                    diagram = QString(QLatin1String("<table><tr><td>%1</td>")).arg(clazz);
+                    for (int i = 0; i < helpLevel; ++i) {
+                        diagram.append(
+                            QLatin1String("<td><img src=\":/cppeditor/images/rightarrow.png\">"
+                                          "</td><td>") %
+                            hierarchy.at(i) %
+                            QLatin1String("</td>"));
+                    }
+                    diagram.append(QLatin1String("</tr></table>"));
+
+                    base = hierarchy.at(helpLevel - 1);
+                    QString contents =
+                        getDocContents(HelpCandidate(base, base, HelpCandidate::Brief), false);
+                    if (!contents.isEmpty()) {
+                        m_toolTip.append(diagram % QLatin1String("<table><tr><td>") %
+                                         contents % QLatin1String("</td></tr></table>"));
                     }
+                    m_toolTip.append(QLatin1String("</p>"));
                 }
             }
         }
     }
 }
 
-void CppHoverHandler::generateNormalTooltip(const bool integrateDocs)
+void CppHoverHandler::generateNormalTooltip(const bool extendTooltips)
 {
     if (m_matchingHelpCandidate != -1) {
-        QString contents;
-        if (integrateDocs)
-            contents = getDocContents();
+        const QString &contents = getDocContents(extendTooltips);
         if (!contents.isEmpty()) {
             HelpCandidate::Category cat = m_helpCandidates.at(m_matchingHelpCandidate).m_category;
             if (cat == HelpCandidate::ClassOrNamespace)
diff --git a/src/plugins/cppeditor/cpphoverhandler.h b/src/plugins/cppeditor/cpphoverhandler.h
index 464fda0e4f9..094bf68b4b2 100644
--- a/src/plugins/cppeditor/cpphoverhandler.h
+++ b/src/plugins/cppeditor/cpphoverhandler.h
@@ -107,11 +107,11 @@ private:
 
     void evaluateHelpCandidates();
     bool helpIdExists(const QString &helpId) const;
-    QString getDocContents() const;
-    QString getDocContents(const HelpCandidate &helpCandidate) const;
+    QString getDocContents(const bool extended);
+    QString getDocContents(const HelpCandidate &helpCandidate, const bool extended);
 
-    void generateDiagramTooltip(const bool integrateDocs);
-    void generateNormalTooltip(const bool integrateDocs);
+    void generateDiagramTooltip(const bool extendTooltips);
+    void generateNormalTooltip(const bool extendTooltips);
     void addF1ToTooltip();
 
     static TextEditor::BaseTextEditor *baseTextEditor(TextEditor::ITextEditor *editor);
diff --git a/src/plugins/texteditor/displaysettings.cpp b/src/plugins/texteditor/displaysettings.cpp
index 575d2fd2289..24232bc13b6 100644
--- a/src/plugins/texteditor/displaysettings.cpp
+++ b/src/plugins/texteditor/displaysettings.cpp
@@ -44,7 +44,7 @@ static const char * const animateMatchingParenthesesKey= "AnimateMatchingParenth
 static const char * const markTextChangesKey = "MarkTextChanges";
 static const char * const autoFoldFirstCommentKey = "AutoFoldFirstComment";
 static const char * const centerCursorOnScrollKey = "CenterCursorOnScroll";
-static const char * const integrateDocsIntoTooltips = "IntegrateDocsIntoTooltips";
+static const char * const extendTooltips = "ExtendTooltips";
 static const char * const groupPostfix = "DisplaySettings";
 
 namespace TextEditor {
@@ -62,7 +62,7 @@ DisplaySettings::DisplaySettings() :
     m_markTextChanges(true),
     m_autoFoldFirstComment(true),
     m_centerCursorOnScroll(false),
-    m_integrateDocsIntoTooltips(true)
+    m_extendTooltips(false)
 {
 }
 
@@ -84,7 +84,7 @@ void DisplaySettings::toSettings(const QString &category, QSettings *s) const
     s->setValue(QLatin1String(markTextChangesKey), m_markTextChanges);
     s->setValue(QLatin1String(autoFoldFirstCommentKey), m_autoFoldFirstComment);
     s->setValue(QLatin1String(centerCursorOnScrollKey), m_centerCursorOnScroll);
-    s->setValue(QLatin1String(integrateDocsIntoTooltips), m_integrateDocsIntoTooltips);
+    s->setValue(QLatin1String(extendTooltips), m_extendTooltips);
     s->endGroup();
 }
 
@@ -109,7 +109,7 @@ void DisplaySettings::fromSettings(const QString &category, const QSettings *s)
     m_markTextChanges = s->value(group + QLatin1String(markTextChangesKey), m_markTextChanges).toBool();
     m_autoFoldFirstComment = s->value(group + QLatin1String(autoFoldFirstCommentKey), m_autoFoldFirstComment).toBool();
     m_centerCursorOnScroll = s->value(group + QLatin1String(centerCursorOnScrollKey), m_centerCursorOnScroll).toBool();
-    m_integrateDocsIntoTooltips = s->value(group + QLatin1String(integrateDocsIntoTooltips), m_integrateDocsIntoTooltips).toBool();
+    m_extendTooltips = s->value(group + QLatin1String(extendTooltips), m_extendTooltips).toBool();
 }
 
 bool DisplaySettings::equals(const DisplaySettings &ds) const
@@ -126,7 +126,7 @@ bool DisplaySettings::equals(const DisplaySettings &ds) const
         && m_markTextChanges == ds.m_markTextChanges
         && m_autoFoldFirstComment== ds.m_autoFoldFirstComment
         && m_centerCursorOnScroll == ds.m_centerCursorOnScroll
-        && m_integrateDocsIntoTooltips == ds.m_integrateDocsIntoTooltips
+        && m_extendTooltips == ds.m_extendTooltips
         ;
 }
 
diff --git a/src/plugins/texteditor/displaysettings.h b/src/plugins/texteditor/displaysettings.h
index a421ea7ee3a..424a276e46a 100644
--- a/src/plugins/texteditor/displaysettings.h
+++ b/src/plugins/texteditor/displaysettings.h
@@ -57,7 +57,7 @@ struct TEXTEDITOR_EXPORT DisplaySettings
     bool m_markTextChanges;
     bool m_autoFoldFirstComment;
     bool m_centerCursorOnScroll;
-    bool m_integrateDocsIntoTooltips;
+    bool m_extendTooltips;
 
     bool equals(const DisplaySettings &ds) const;
 };
diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp
index 27d00766c57..92561c9510b 100644
--- a/src/plugins/texteditor/displaysettingspage.cpp
+++ b/src/plugins/texteditor/displaysettingspage.cpp
@@ -94,7 +94,7 @@ QWidget *DisplaySettingsPage::createPage(QWidget *parent)
           << ' ' << m_d->m_page.enableTextWrapping->text()
           << ' ' << m_d->m_page.autoFoldFirstComment->text()
           << ' ' << m_d->m_page.centerOnScroll->text()
-          << ' ' << m_d->m_page.integrateDocsIntoTooltips->text();
+          << ' ' << m_d->m_page.extendTooltips->text();
         m_d->m_searchKeywords.remove(QLatin1Char('&'));
     }
     return w;
@@ -122,7 +122,7 @@ void DisplaySettingsPage::settingsFromUI(DisplaySettings &displaySettings) const
     displaySettings.m_markTextChanges = m_d->m_page.markTextChanges->isChecked();
     displaySettings.m_autoFoldFirstComment = m_d->m_page.autoFoldFirstComment->isChecked();
     displaySettings.m_centerCursorOnScroll = m_d->m_page.centerOnScroll->isChecked();
-    displaySettings.m_integrateDocsIntoTooltips = m_d->m_page.integrateDocsIntoTooltips->isChecked();
+    displaySettings.m_extendTooltips = m_d->m_page.extendTooltips->isChecked();
 }
 
 void DisplaySettingsPage::settingsToUI()
@@ -140,7 +140,7 @@ void DisplaySettingsPage::settingsToUI()
     m_d->m_page.markTextChanges->setChecked(displaySettings.m_markTextChanges);
     m_d->m_page.autoFoldFirstComment->setChecked(displaySettings.m_autoFoldFirstComment);
     m_d->m_page.centerOnScroll->setChecked(displaySettings.m_centerCursorOnScroll);
-    m_d->m_page.integrateDocsIntoTooltips->setChecked(displaySettings.m_integrateDocsIntoTooltips);
+    m_d->m_page.extendTooltips->setChecked(displaySettings.m_extendTooltips);
 }
 
 const DisplaySettings &DisplaySettingsPage::displaySettings() const
diff --git a/src/plugins/texteditor/displaysettingspage.ui b/src/plugins/texteditor/displaysettingspage.ui
index 28e6b71397a..06d553c7e36 100644
--- a/src/plugins/texteditor/displaysettingspage.ui
+++ b/src/plugins/texteditor/displaysettingspage.ui
@@ -97,9 +97,9 @@
        </widget>
       </item>
       <item row="5" column="1">
-       <widget class="QCheckBox" name="integrateDocsIntoTooltips">
+       <widget class="QCheckBox" name="extendTooltips">
         <property name="text">
-         <string>Integrate Qt docs into tooltips</string>
+         <string>Extend tooltips for Qt 4.7 (or later) docs</string>
         </property>
        </widget>
       </item>
-- 
GitLab