From 61ed06d58e137c3daf49f3011d1124c948a5c795 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thorbj=C3=B8rn=20Lindeijer?= <thorbjorn.lindeijer@nokia.com>
Date: Tue, 8 Sep 2009 13:03:24 +0200
Subject: [PATCH] Moved mouse navigation into BaseTextEditor so that it can be
 reused

For implementing mouse navigation in the QML editor.
---
 src/plugins/cppeditor/cppeditor.cpp       | 100 +----------------
 src/plugins/cppeditor/cppeditor.h         |  36 +------
 src/plugins/texteditor/basetexteditor.cpp | 125 +++++++++++++++++++++-
 src/plugins/texteditor/basetexteditor.h   |  50 ++++++++-
 src/plugins/texteditor/basetexteditor_p.h |   6 +-
 5 files changed, 179 insertions(+), 138 deletions(-)

diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index 5bea06a0453..597a2769e85 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -527,8 +527,6 @@ CPPEditorEditable::CPPEditorEditable(CPPEditor *editor)
 
 CPPEditor::CPPEditor(QWidget *parent)
     : TextEditor::BaseTextEditor(parent)
-    , m_mouseNavigationEnabled(true)
-    , m_showingLink(false)
     , m_currentRenameSelection(-1)
     , m_inRename(false)
 {
@@ -1073,7 +1071,7 @@ void CPPEditor::switchDeclarationDefinition()
 }
 
 CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
-                                      bool lookupDefinition)
+                                      bool resolveTarget)
 {
     Link link;
 
@@ -1153,7 +1151,7 @@ CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
 
         if (Symbol *symbol = result.second) {
             Symbol *def = 0;
-            if (lookupDefinition && !lastSymbol->isFunction())
+            if (resolveTarget && !lastSymbol->isFunction())
                 def = findDefinition(symbol);
 
             link = linkToSymbol(def ? def : symbol);
@@ -1190,7 +1188,7 @@ CPPEditor::Link CPPEditor::findLinkAt(const QTextCursor &cursor,
 
 void CPPEditor::jumpToDefinition()
 {
-    openCppEditorAt(findLinkAt(textCursor()));
+    openLink(findLinkAt(textCursor()));
 }
 
 Symbol *CPPEditor::findDefinition(Symbol *symbol)
@@ -1342,68 +1340,6 @@ void CPPEditor::contextMenuEvent(QContextMenuEvent *e)
     delete menu;
 }
 
-void CPPEditor::mouseMoveEvent(QMouseEvent *e)
-{
-    bool linkFound = false;
-
-    if (m_mouseNavigationEnabled && e->modifiers() & Qt::ControlModifier) {
-        // Link emulation behaviour for 'go to definition'
-        const QTextCursor cursor = cursorForPosition(e->pos());
-
-        // Check that the mouse was actually on the text somewhere
-        bool onText = cursorRect(cursor).right() >= e->x();
-        if (!onText) {
-            QTextCursor nextPos = cursor;
-            nextPos.movePosition(QTextCursor::Right);
-            onText = cursorRect(nextPos).right() >= e->x();
-        }
-
-        const Link link = findLinkAt(cursor, false);
-
-        if (onText && !link.fileName.isEmpty()) {
-            showLink(link);
-            linkFound = true;
-        }
-    }
-
-    if (!linkFound)
-        clearLink();
-
-    TextEditor::BaseTextEditor::mouseMoveEvent(e);
-}
-
-void CPPEditor::mouseReleaseEvent(QMouseEvent *e)
-{
-    if (m_mouseNavigationEnabled && e->modifiers() & Qt::ControlModifier
-        && !(e->modifiers() & Qt::ShiftModifier)
-        && e->button() == Qt::LeftButton) {
-
-        const QTextCursor cursor = cursorForPosition(e->pos());
-        if (openCppEditorAt(findLinkAt(cursor))) {
-            clearLink();
-            e->accept();
-            return;
-        }
-    }
-
-    TextEditor::BaseTextEditor::mouseReleaseEvent(e);
-}
-
-void CPPEditor::leaveEvent(QEvent *e)
-{
-    clearLink();
-    TextEditor::BaseTextEditor::leaveEvent(e);
-}
-
-void CPPEditor::keyReleaseEvent(QKeyEvent *e)
-{
-    // Clear link emulation when Ctrl is released
-    if (e->key() == Qt::Key_Control)
-        clearLink();
-
-    TextEditor::BaseTextEditor::keyReleaseEvent(e);
-}
-
 void CPPEditor::keyPressEvent(QKeyEvent *e)
 {
     if (m_currentRenameSelection == -1) {
@@ -1491,29 +1427,6 @@ void CPPEditor::keyPressEvent(QKeyEvent *e)
     TextEditor::BaseTextEditor::keyPressEvent(e);
 }
 
-void CPPEditor::showLink(const Link &link)
-{
-    QTextEdit::ExtraSelection sel;
-    sel.cursor = textCursor();
-    sel.cursor.setPosition(link.pos);
-    sel.cursor.setPosition(link.pos + link.length, QTextCursor::KeepAnchor);
-    sel.format = m_linkFormat;
-    sel.format.setFontUnderline(true);
-    setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
-    viewport()->setCursor(Qt::PointingHandCursor);
-    m_showingLink = true;
-}
-
-void CPPEditor::clearLink()
-{
-    if (!m_showingLink)
-        return;
-
-    setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
-    viewport()->setCursor(Qt::IBeamCursor);
-    m_showingLink = false;
-}
-
 QList<int> CPPEditorEditable::context() const
 {
     return m_context;
@@ -1557,17 +1470,10 @@ void CPPEditor::setFontSettings(const TextEditor::FontSettings &fs)
     highlighter->setFormats(formats.constBegin(), formats.constEnd());
     highlighter->rehighlight();
 
-    m_linkFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LINK));
     m_occurrencesFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES));
     m_occurrenceRenameFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_OCCURRENCES_RENAME));
 }
 
-void CPPEditor::setDisplaySettings(const TextEditor::DisplaySettings &ds)
-{
-    TextEditor::BaseTextEditor::setDisplaySettings(ds);
-    m_mouseNavigationEnabled = ds.m_mouseNavigation;
-}
-
 void CPPEditor::unCommentSelection()
 {
     Core::Utils::unCommentSelection(this);
diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h
index 27991a550f3..40e2e71972f 100644
--- a/src/plugins/cppeditor/cppeditor.h
+++ b/src/plugins/cppeditor/cppeditor.h
@@ -193,7 +193,6 @@ public:
 
 public Q_SLOTS:
     virtual void setFontSettings(const TextEditor::FontSettings &);
-    virtual void setDisplaySettings(const TextEditor::DisplaySettings &);
     void setSortedMethodOverview(bool sort);
     void switchDeclarationDefinition();
     void jumpToDefinition();
@@ -208,10 +207,6 @@ public Q_SLOTS:
 protected:
     bool event(QEvent *e);
     void contextMenuEvent(QContextMenuEvent *);
-    void mouseMoveEvent(QMouseEvent *);
-    void mouseReleaseEvent(QMouseEvent *);
-    void leaveEvent(QEvent *);
-    void keyReleaseEvent(QKeyEvent *);
     void keyPressEvent(QKeyEvent *);
 
     TextEditor::BaseTextEditorEditable *createEditableInterface();
@@ -261,36 +256,11 @@ private:
                                const QString &text = QString());
     void abortRename();
 
-    struct Link
-    {
-        Link(const QString &fileName = QString(),
-             int line = 0,
-             int column = 0)
-            : pos(-1)
-            , length(-1)
-            , fileName(fileName)
-            , line(line)
-            , column(column)
-        {}
-
-        int pos;           // Link position
-        int length;        // Link length
-
-        QString fileName;  // Target file
-        int line;          // Target line
-        int column;        // Target column
-    };
-
-    void showLink(const Link &);
-    void clearLink();
-
-    Link findLinkAt(const QTextCursor &, bool lookupDefinition = true);
-    static Link linkToSymbol(CPlusPlus::Symbol *symbol);
+    Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
+    bool openLink(const Link &link) { return openCppEditorAt(link); }
     bool openCppEditorAt(const Link &);
 
-    bool m_mouseNavigationEnabled;
-    bool m_showingLink;
-    QTextCharFormat m_linkFormat;
+    static Link linkToSymbol(CPlusPlus::Symbol *symbol);
 
     CppTools::CppModelManagerInterface *m_modelManager;
 
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index 80a1f0268cc..59c55fcb2d4 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -1337,6 +1337,16 @@ bool BaseTextEditor::codeFoldingSupported() const
     return d->m_codeFoldingSupported;
 }
 
+void BaseTextEditor::setMouseNavigationEnabled(bool b)
+{
+    d->m_mouseNavigationEnabled = b;
+}
+
+bool BaseTextEditor::mouseNavigationEnabled() const
+{
+    return d->m_mouseNavigationEnabled;
+}
+
 void BaseTextEditor::setRevisionsVisible(bool b)
 {
     d->m_revisionsVisible = b;
@@ -1372,12 +1382,14 @@ BaseTextEditorPrivate::BaseTextEditorPrivate()
     m_marksVisible(false),
     m_codeFoldingVisible(false),
     m_codeFoldingSupported(false),
+    m_mouseNavigationEnabled(true),
     m_revisionsVisible(false),
     m_lineNumbersVisible(true),
     m_highlightCurrentLine(true),
     m_requestMarkEnabled(true),
     m_lineSeparatorsAllowed(false),
     m_visibleWrapColumn(0),
+    m_showingLink(false),
     m_editable(0),
     m_actionHack(0),
     m_inBlockSelectionMode(false),
@@ -2721,6 +2733,32 @@ void BaseTextEditorPrivate::clearVisibleCollapsedBlock()
 void BaseTextEditor::mouseMoveEvent(QMouseEvent *e)
 {
     d->m_lastEventWasBlockSelectionEvent = (e->modifiers() & Qt::AltModifier);
+
+    bool linkFound = false;
+
+    if (d->m_mouseNavigationEnabled && e->modifiers() & Qt::ControlModifier) {
+        // Link emulation behaviour for 'go to definition'
+        const QTextCursor cursor = cursorForPosition(e->pos());
+
+        // Check that the mouse was actually on the text somewhere
+        bool onText = cursorRect(cursor).right() >= e->x();
+        if (!onText) {
+            QTextCursor nextPos = cursor;
+            nextPos.movePosition(QTextCursor::Right);
+            onText = cursorRect(nextPos).right() >= e->x();
+        }
+
+        const Link link = findLinkAt(cursor, false);
+
+        if (onText && link.isValid()) {
+            showLink(link);
+            linkFound = true;
+        }
+    }
+
+    if (!linkFound)
+        clearLink();
+
     if (e->buttons() == Qt::NoButton) {
         const QTextBlock collapsedBlock = collapsedBlockAt(e->pos());
         const int blockNumber = collapsedBlock.next().blockNumber();
@@ -2767,6 +2805,38 @@ void BaseTextEditor::mousePressEvent(QMouseEvent *e)
     QPlainTextEdit::mousePressEvent(e);
 }
 
+void BaseTextEditor::mouseReleaseEvent(QMouseEvent *e)
+{
+    if (d->m_mouseNavigationEnabled && e->modifiers() & Qt::ControlModifier
+        && !(e->modifiers() & Qt::ShiftModifier)
+        && e->button() == Qt::LeftButton) {
+
+        const QTextCursor cursor = cursorForPosition(e->pos());
+        if (openLink(findLinkAt(cursor))) {
+            clearLink();
+            return;
+        }
+    }
+
+    QPlainTextEdit::mouseReleaseEvent(e);
+}
+
+void BaseTextEditor::leaveEvent(QEvent *e)
+{
+    // Clear link emulation when the mouse leaves the editor
+    clearLink();
+    QPlainTextEdit::leaveEvent(e);
+}
+
+void BaseTextEditor::keyReleaseEvent(QKeyEvent *e)
+{
+    // Clear link emulation when Ctrl is released
+    if (e->key() == Qt::Key_Control)
+        clearLink();
+
+    QPlainTextEdit::keyReleaseEvent(e);
+}
+
 void BaseTextEditor::extraAreaLeaveEvent(QEvent *)
 {
     // fake missing mouse move event from Qt
@@ -3186,6 +3256,50 @@ void BaseTextEditor::indent(QTextDocument *doc, const QTextCursor &cursor, QChar
     }
 }
 
+BaseTextEditor::Link BaseTextEditor::findLinkAt(const QTextCursor &, bool)
+{
+    return Link();
+}
+
+bool BaseTextEditor::openLink(const Link &link)
+{
+    if (link.fileName.isEmpty())
+        return false;
+
+    if (baseTextDocument()->fileName() == link.fileName) {
+        Core::EditorManager *editorManager = Core::EditorManager::instance();
+        editorManager->addCurrentPositionToNavigationHistory();
+        gotoLine(link.line, link.column);
+        setFocus();
+        return true;
+    }
+
+    return openEditorAt(link.fileName, link.line, link.column);
+}
+
+void BaseTextEditor::showLink(const Link &link)
+{
+    QTextEdit::ExtraSelection sel;
+    sel.cursor = textCursor();
+    sel.cursor.setPosition(link.pos);
+    sel.cursor.setPosition(link.pos + link.length, QTextCursor::KeepAnchor);
+    sel.format = d->m_linkFormat;
+    sel.format.setFontUnderline(true);
+    setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
+    viewport()->setCursor(Qt::PointingHandCursor);
+    d->m_showingLink = true;
+}
+
+void BaseTextEditor::clearLink()
+{
+    if (!d->m_showingLink)
+        return;
+
+    setExtraSelections(OtherSelection, QList<QTextEdit::ExtraSelection>());
+    viewport()->setCursor(Qt::IBeamCursor);
+    d->m_showingLink = false;
+}
+
 void BaseTextEditorPrivate::updateMarksBlock(const QTextBlock &block)
 {
     if (const TextBlockUserData *userData = TextEditDocumentLayout::testUserData(block))
@@ -4005,8 +4119,8 @@ void BaseTextEditor::unCommentSelection()
 void BaseTextEditor::showEvent(QShowEvent* e)
 {
     if (!d->m_fontSettings.isEmpty()) {
-	setFontSettings(d->m_fontSettings);
-	d->m_fontSettings.clear();
+        setFontSettings(d->m_fontSettings);
+        d->m_fontSettings.clear();
     }
     QPlainTextEdit::showEvent(e);
 }
@@ -4015,11 +4129,12 @@ void BaseTextEditor::showEvent(QShowEvent* e)
 void BaseTextEditor::setFontSettingsIfVisible(const TextEditor::FontSettings &fs)
 {
     if (!isVisible()) {
-	d->m_fontSettings = fs;
-	return;
+        d->m_fontSettings = fs;
+        return;
     }
     setFontSettings(fs);
 }
+
 void BaseTextEditor::setFontSettings(const TextEditor::FontSettings &fs)
 {
     const QTextCharFormat textFormat = fs.toTextCharFormat(QLatin1String(Constants::C_TEXT));
@@ -4030,6 +4145,7 @@ void BaseTextEditor::setFontSettings(const TextEditor::FontSettings &fs)
     const QTextCharFormat parenthesesFormat = fs.toTextCharFormat(QLatin1String(Constants::C_PARENTHESES));
     d->m_currentLineFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE));
     d->m_currentLineNumberFormat = fs.toTextCharFormat(QLatin1String(Constants::C_CURRENT_LINE_NUMBER));
+    d->m_linkFormat = fs.toTextCharFormat(QLatin1String(TextEditor::Constants::C_LINK));
     d->m_ifdefedOutFormat = fs.toTextCharFormat(QLatin1String(Constants::C_DISABLED_CODE));
     QFont font(textFormat.font());
 
@@ -4082,6 +4198,7 @@ void BaseTextEditor::setDisplaySettings(const DisplaySettings &ds)
     setCodeFoldingVisible(ds.m_displayFoldingMarkers);
     setHighlightCurrentLine(ds.m_highlightCurrentLine);
     setRevisionsVisible(ds.m_markTextChanges);
+    setMouseNavigationEnabled(ds.m_mouseNavigation);
 
     if (d->m_displaySettings.m_visualizeWhitespace != ds.m_visualizeWhitespace) {
         if (QSyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter())
diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h
index 34554082925..17a67d3b0a9 100644
--- a/src/plugins/texteditor/basetexteditor.h
+++ b/src/plugins/texteditor/basetexteditor.h
@@ -274,8 +274,7 @@ private:
 };
 
 
-class TEXTEDITOR_EXPORT BaseTextEditor
-  : public QPlainTextEdit
+class TEXTEDITOR_EXPORT BaseTextEditor : public QPlainTextEdit
 {
     Q_OBJECT
 
@@ -345,6 +344,9 @@ public:
     void setCodeFoldingSupported(bool b);
     bool codeFoldingSupported() const;
 
+    void setMouseNavigationEnabled(bool b);
+    bool mouseNavigationEnabled() const;
+
     void setRevisionsVisible(bool b);
     bool revisionsVisible() const;
 
@@ -496,14 +498,54 @@ protected:
     void timerEvent(QTimerEvent *);
     void mouseMoveEvent(QMouseEvent *);
     void mousePressEvent(QMouseEvent *);
+    void mouseReleaseEvent(QMouseEvent *);
+    void leaveEvent(QEvent *);
+    void keyReleaseEvent(QKeyEvent *);
 
-    // Rertuns true if key triggers an indent.
+    // Returns true if key triggers an indent.
     virtual bool isElectricCharacter(const QChar &ch) const;
     // Indent a text block based on previous line. Default does nothing
     virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar);
     // Indent at cursor. Calls indentBlock for selection or current line.
     virtual void indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar);
 
+    struct Link
+    {
+        Link(const QString &fileName = QString(),
+             int line = 0,
+             int column = 0)
+            : pos(-1)
+            , length(-1)
+            , fileName(fileName)
+            , line(line)
+            , column(column)
+        {}
+
+        bool isValid() const
+        { return !(pos == -1 || length == -1); }
+
+        int pos;           // Link position
+        int length;        // Link length
+
+        QString fileName;  // Target file
+        int line;          // Target line
+        int column;        // Target column
+    };
+
+    /*!
+       Reimplement this function to enable code navigation.
+
+       \a resolveTarget is set to true when the target of the link is relevant
+       (it isn't until the link is used).
+     */
+    virtual Link findLinkAt(const QTextCursor &, bool resolveTarget = true);
+
+    /*!
+       Reimplement this function if you want to customize the way a link is
+       opened. Returns whether the link was opened succesfully.
+     */
+    virtual bool openLink(const Link &link);
+
 protected slots:
     virtual void slotUpdateExtraAreaWidth();
     virtual void slotModificationChanged(bool);
@@ -537,6 +579,8 @@ private:
 
     QTextBlock collapsedBlockAt(const QPoint &pos, QRect *box = 0) const;
 
+    void showLink(const Link &);
+    void clearLink();
 
     // parentheses matcher
 private slots:
diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h
index dbf5b66d1e8..1956f858800 100644
--- a/src/plugins/texteditor/basetexteditor_p.h
+++ b/src/plugins/texteditor/basetexteditor_p.h
@@ -194,6 +194,7 @@ public:
     uint m_marksVisible : 1;
     uint m_codeFoldingVisible : 1;
     uint m_codeFoldingSupported : 1;
+    uint m_mouseNavigationEnabled : 1;
     uint m_revisionsVisible : 1;
     uint m_lineNumbersVisible : 1;
     uint m_highlightCurrentLine : 1;
@@ -201,6 +202,9 @@ public:
     uint m_lineSeparatorsAllowed : 1;
     int m_visibleWrapColumn;
 
+    QTextCharFormat m_linkFormat;
+    bool m_showingLink;
+
     QTextCharFormat m_ifdefedOutFormat;
 
     QRegExp m_searchExpr;
@@ -226,7 +230,7 @@ public:
     QString copyBlockSelection();
     void removeBlockSelection(const QString &text = QString());
     bool m_moveLineUndoHack;
-    
+
     QTextCursor m_findScope;
     QTextCursor m_selectBlockAnchor;
 
-- 
GitLab