From e9ad023def7e6edb3f1c6ec2f99faa3584f615eb Mon Sep 17 00:00:00 2001
From: mae <qtc-committer@nokia.com>
Date: Thu, 4 Dec 2008 18:37:02 +0100
Subject: [PATCH] block navigation and selection (bound to Ctrl+[]{}

---
 src/plugins/find/basetextfind.cpp             |   5 +-
 src/plugins/texteditor/basetexteditor.cpp     | 118 +++++++++++++++++-
 src/plugins/texteditor/basetexteditor.h       |  10 ++
 .../texteditor/texteditoractionhandler.cpp    |  94 +++++++-------
 .../texteditor/texteditoractionhandler.h      |   8 ++
 src/plugins/texteditor/texteditorconstants.h  |   6 +
 6 files changed, 189 insertions(+), 52 deletions(-)

diff --git a/src/plugins/find/basetextfind.cpp b/src/plugins/find/basetextfind.cpp
index 63740d36527..6598a47e6f3 100644
--- a/src/plugins/find/basetextfind.cpp
+++ b/src/plugins/find/basetextfind.cpp
@@ -162,7 +162,10 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after,
     QTextDocument::FindFlags findFlags)
 {
     QTextCursor editCursor = textCursor();
-    editCursor.movePosition(QTextCursor::Start);
+    if (!m_findScope.isNull())
+        editCursor.setPosition(m_findScope.selectionStart());
+    else
+        editCursor.movePosition(QTextCursor::Start);
     editCursor.beginEditBlock();
     int count = 0;
     QTextCursor found;
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index df5fe410808..a0cf9dee3b0 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -531,7 +531,6 @@ void BaseTextEditor::selectEncoding()
     }
 }
 
-
 void DocumentMarker::updateMark(ITextMark *mark)
 {
     TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(document->documentLayout());
@@ -600,6 +599,36 @@ void BaseTextEditor::slotSelectionChanged()
         d->m_blockSelectionExtraX = 0;
 }
 
+void BaseTextEditor::gotoBlockStart()
+{
+    QTextCursor cursor = textCursor();
+    if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, false))
+        setTextCursor(cursor);
+}
+
+void BaseTextEditor::gotoBlockEnd()
+{
+    QTextCursor cursor = textCursor();
+    if (TextBlockUserData::findNextClosingParenthesis(&cursor, false))
+        setTextCursor(cursor);
+}
+
+void BaseTextEditor::gotoBlockStartWithSelection()
+{
+    QTextCursor cursor = textCursor();
+    if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, true))
+        setTextCursor(cursor);
+}
+
+void BaseTextEditor::gotoBlockEndWithSelection()
+{
+    QTextCursor cursor = textCursor();
+    if (TextBlockUserData::findNextClosingParenthesis(&cursor, true))
+        setTextCursor(cursor);
+}
+
+
+
 void BaseTextEditor::keyPressEvent(QKeyEvent *e)
 {
 
@@ -633,6 +662,11 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
                 e->accept();
                 return;
             }
+        } else if (e == QKeySequence::Paste) {
+            if (!ro) {
+                d->removeBlockSelection();
+                // continue
+            }
         }
     }
 
@@ -693,6 +727,8 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
         break;
     case Qt::Key_Home:
         if (!(e == QKeySequence::MoveToStartOfDocument) && !(e == QKeySequence::SelectStartOfDocument)) {
+            if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier))
+                d->m_lastEventWasBlockSelectionEvent = true;
             handleHomeKey(e->modifiers() & Qt::ShiftModifier);
             e->accept();
             return;
@@ -708,6 +744,7 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
             return;
         }
         // fall through
+    case Qt::Key_End:
     case Qt::Key_Right:
     case Qt::Key_Left:
 #ifndef Q_OS_MAC
@@ -795,6 +832,15 @@ skip_event:
         delete e;
 }
 
+void BaseTextEditor::setTextCursor(const QTextCursor &cursor)
+{
+    // workaround for QTextControl bug
+    bool selectionChange = cursor.hasSelection() || textCursor().hasSelection();
+    QPlainTextEdit::setTextCursor(cursor);
+    if (selectionChange)
+        slotSelectionChanged();
+}
+
 void BaseTextEditor::gotoLine(int line, int column)
 {
     const int blockNumber = line - 1;
@@ -2908,6 +2954,61 @@ TextBlockUserData::MatchType TextBlockUserData::checkClosedParenthesis(QTextCurs
     }
 }
 
+
+bool TextBlockUserData::findPreviousOpenParenthesis(QTextCursor *cursor, bool select)
+{
+    QTextBlock block = cursor->block();
+    int position = cursor->position();
+    int ignore = 0;
+    while (block.isValid()) {
+        Parentheses parenList = TextEditDocumentLayout::parentheses(block);
+        if (!parenList.isEmpty()) {
+            for (int i = parenList.count()-1; i >= 0; --i) {
+                Parenthesis paren = parenList.at(i);
+                if (block == cursor->block() && position - block.position() <= paren.pos + 1)
+                    continue;
+                if (paren.type == Parenthesis::Closed) {
+                    ++ignore;
+                } else if (ignore > 0) {
+                    --ignore;
+                } else {
+                    cursor->setPosition(block.position() + paren.pos, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
+                    return true;
+                }
+            }
+        }
+        block = block.previous();
+    }
+    return false;
+}
+
+bool TextBlockUserData::findNextClosingParenthesis(QTextCursor *cursor, bool select)
+{
+    QTextBlock block = cursor->block();
+    int position = cursor->position();
+    int ignore = 0;
+    while (block.isValid()) {
+        Parentheses parenList = TextEditDocumentLayout::parentheses(block);
+        if (!parenList.isEmpty()) {
+            for (int i = 0; i < parenList.count(); ++i) {
+                Parenthesis paren = parenList.at(i);
+                if (block == cursor->block() && position - block.position() >= paren.pos)
+                    continue;
+                if (paren.type == Parenthesis::Opened) {
+                    ++ignore;
+                } else if (ignore > 0) {
+                    --ignore;
+                } else {
+                    cursor->setPosition(block.position() + paren.pos+1, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
+                    return true;
+                }
+            }
+        }
+        block = block.next();
+    }
+    return false;
+}
+
 TextBlockUserData::MatchType TextBlockUserData::matchCursorBackward(QTextCursor *cursor)
 {
     cursor->clearSelection();
@@ -3152,13 +3253,16 @@ void BaseTextEditor::collapse()
     TextEditDocumentLayout *documentLayout = qobject_cast<TextEditDocumentLayout*>(doc->documentLayout());
     Q_ASSERT(documentLayout);
     QTextBlock block = textCursor().block();
+    qDebug() << "collapse at block" << block.blockNumber();
     while (block.isValid()) {
-        if (TextBlockUserData::canCollapse(block)) {
-            if ((block.next().userState()) >> 8 == (textCursor().block().userState() >> 8))
+        qDebug() << "test block" << block.blockNumber();
+        if (TextBlockUserData::canCollapse(block) && block.next().isVisible()) {
+            if ((block.next().userState()) >> 8 <= (textCursor().block().userState() >> 8))
                 break;
         }
         block = block.previous();
     }
+    qDebug() << "found" << block.blockNumber();
     if (block.isValid()) {
         TextBlockUserData::doCollapse(block, false);
         d->moveCursorVisible();
@@ -3238,6 +3342,14 @@ void BaseTextEditor::cut()
     QPlainTextEdit::cut();
 }
 
+void BaseTextEditor::paste()
+{
+    if (d->m_inBlockSelectionMode) {
+        d->removeBlockSelection();
+    }
+    QPlainTextEdit::paste();
+}
+
 QMimeData *BaseTextEditor::createMimeDataFromSelection() const
 {
     if (d->m_inBlockSelectionMode) {
diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h
index ced22900030..54e4728c4b4 100644
--- a/src/plugins/texteditor/basetexteditor.h
+++ b/src/plugins/texteditor/basetexteditor.h
@@ -169,6 +169,8 @@ public:
     static MatchType checkClosedParenthesis(QTextCursor *cursor, QChar c);
     static MatchType matchCursorBackward(QTextCursor *cursor);
     static MatchType matchCursorForward(QTextCursor *cursor);
+    static bool findPreviousOpenParenthesis(QTextCursor *cursor, bool select = false);
+    static bool findNextClosingParenthesis(QTextCursor *cursor, bool select = false);
 
 
 private:
@@ -298,6 +300,8 @@ public:
 
     void setReadOnly(bool b);
 
+    void setTextCursor(const QTextCursor &cursor);
+
 public slots:
     void setDisplayName(const QString &title);
     virtual void setFontSettings(const TextEditor::FontSettings &);
@@ -305,6 +309,7 @@ public slots:
     virtual void unCommentSelection();
     virtual void setStorageSettings(const TextEditor::StorageSettings &);
 
+    void paste();
     void cut();
 
     void zoomIn(int range = 1);
@@ -316,6 +321,11 @@ public slots:
     void expand();
     void selectEncoding();
 
+    void gotoBlockStart();
+    void gotoBlockEnd();
+    void gotoBlockStartWithSelection();
+    void gotoBlockEndWithSelection();
+
 signals:
     void changed();
 
diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp
index 94cdca6cfda..a9e955eae36 100644
--- a/src/plugins/texteditor/texteditoractionhandler.cpp
+++ b/src/plugins/texteditor/texteditoractionhandler.cpp
@@ -67,6 +67,8 @@ TextEditorActionHandler::TextEditorActionHandler(Core::ICore *core,
                  = m_collapseAction = m_expandAction
                  = m_deleteLineAction = m_selectEncodingAction
                  = m_increaseFontSizeAction = m_decreaseFontSizeAction
+                 = m_gotoBlockStartAction = m_gotoBlockStartWithSelectionAction
+                 = m_gotoBlockEndAction = m_gotoBlockEndWithSelectionAction
                  = 0;
 
     m_contextId << m_core->uniqueIDManager()->uniqueIdentifier(context);
@@ -185,6 +187,27 @@ void TextEditorActionHandler::createActions()
     command->setDefaultKeySequence(QKeySequence(tr("Ctrl+-")));
     connect(m_decreaseFontSizeAction, SIGNAL(triggered()), this, SLOT(decreaseFontSize()));
     advancedMenu->addAction(command);
+
+    m_gotoBlockStartAction = new QAction(tr("Goto Block Start"), this);
+    command = am->registerAction(m_gotoBlockStartAction, Constants::GOTO_BLOCK_START, m_contextId);
+    command->setDefaultKeySequence(QKeySequence(tr("Ctrl+[")));
+    connect(m_gotoBlockStartAction, SIGNAL(triggered()), this, SLOT(gotoBlockStart()));
+
+    m_gotoBlockEndAction = new QAction(tr("Goto Block End"), this);
+    command = am->registerAction(m_gotoBlockEndAction, Constants::GOTO_BLOCK_END, m_contextId);
+    command->setDefaultKeySequence(QKeySequence(tr("Ctrl+]")));
+    connect(m_gotoBlockEndAction, SIGNAL(triggered()), this, SLOT(gotoBlockEnd()));
+
+    m_gotoBlockStartWithSelectionAction = new QAction(tr("Goto Block Start With Selection"), this);
+    command = am->registerAction(m_gotoBlockStartWithSelectionAction, Constants::GOTO_BLOCK_START_WITH_SELECTION, m_contextId);
+    command->setDefaultKeySequence(QKeySequence(tr("Ctrl+{")));
+    connect(m_gotoBlockStartWithSelectionAction, SIGNAL(triggered()), this, SLOT(gotoBlockStartWithSelection()));
+
+    m_gotoBlockEndWithSelectionAction = new QAction(tr("Goto Block End With Selection"), this);
+    command = am->registerAction(m_gotoBlockEndWithSelectionAction, Constants::GOTO_BLOCK_END_WITH_SELECTION, m_contextId);
+    command->setDefaultKeySequence(QKeySequence(tr("Ctrl+}")));
+    connect(m_gotoBlockEndWithSelectionAction, SIGNAL(triggered()), this, SLOT(gotoBlockEndWithSelection()));
+
 }
 
 bool TextEditorActionHandler::supportsAction(const QString & /*id */) const
@@ -365,54 +388,29 @@ void TextEditorActionHandler::setTextWrapping(bool checked)
     }
 }
 
-void TextEditorActionHandler::unCommentSelection()
-{
-    if (m_currentEditor)
-        m_currentEditor->unCommentSelection();
-}
-
-void TextEditorActionHandler::deleteLine()
-{
-    if (m_currentEditor)
-        m_currentEditor->deleteLine();
-}
-
-void TextEditorActionHandler::unCollapseAll()
-{
-    if (m_currentEditor)
-        m_currentEditor->unCollapseAll();
-}
-
-void TextEditorActionHandler::collapse()
-{
-    if (m_currentEditor)
-        m_currentEditor->collapse();
-}
-
-void TextEditorActionHandler::expand()
-{
-    if (m_currentEditor)
-        m_currentEditor->expand();
-}
-
-void TextEditorActionHandler::selectEncoding()
-{
-    if (m_currentEditor)
-        m_currentEditor->selectEncoding();
-}
-
-void TextEditorActionHandler::increaseFontSize()
-{
-    if (m_currentEditor)
-        m_currentEditor->zoomIn();
-}
-
-void TextEditorActionHandler::decreaseFontSize()
-{
-    if (m_currentEditor)
-        m_currentEditor->zoomOut();
-}
-
+#define FUNCTION(funcname) void TextEditorActionHandler::funcname ()\
+{\
+    if (m_currentEditor)\
+        m_currentEditor->funcname ();\
+}
+#define FUNCTION2(funcname, funcname2) void TextEditorActionHandler::funcname ()\
+{\
+    if (m_currentEditor)\
+        m_currentEditor->funcname2 ();\
+}
+
+FUNCTION(unCommentSelection)
+FUNCTION(deleteLine)
+FUNCTION(unCollapseAll)
+FUNCTION(collapse)
+FUNCTION(expand)
+FUNCTION2(increaseFontSize, zoomIn)
+FUNCTION2(decreaseFontSize, zoomOut)
+FUNCTION(selectEncoding)
+FUNCTION(gotoBlockStart)
+FUNCTION(gotoBlockEnd)
+FUNCTION(gotoBlockStartWithSelection)
+FUNCTION(gotoBlockEndWithSelection)
 
 void TextEditorActionHandler::updateCurrentEditor(Core::IContext *object)
 {
diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h
index 79ac67c2178..f9fe9f249af 100644
--- a/src/plugins/texteditor/texteditoractionhandler.h
+++ b/src/plugins/texteditor/texteditoractionhandler.h
@@ -109,6 +109,10 @@ private slots:
     void selectEncoding();
     void increaseFontSize();
     void decreaseFontSize();
+    void gotoBlockStart();
+    void gotoBlockEnd();
+    void gotoBlockStartWithSelection();
+    void gotoBlockEndWithSelection();
     void updateCurrentEditor(Core::IContext *object);
 
 private:
@@ -131,6 +135,10 @@ private:
     QAction *m_selectEncodingAction;
     QAction *m_increaseFontSizeAction;
     QAction *m_decreaseFontSizeAction;
+    QAction *m_gotoBlockStartAction;
+    QAction *m_gotoBlockEndAction;
+    QAction *m_gotoBlockStartWithSelectionAction;
+    QAction *m_gotoBlockEndWithSelectionAction;
 
     uint m_optionalActions;
     QPointer<BaseTextEditor> m_currentEditor;
diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h
index 8313f61fc3d..9415d732da9 100644
--- a/src/plugins/texteditor/texteditorconstants.h
+++ b/src/plugins/texteditor/texteditorconstants.h
@@ -48,9 +48,15 @@ const char * const UN_COLLAPSE_ALL       = "TextEditor.UnCollapseAll";
 const char * const AUTO_INDENT_SELECTION = "TextEditor.AutoIndentSelection";
 const char * const INCREASE_FONT_SIZE    = "TextEditor.IncreaseFontSize";
 const char * const DECREASE_FONT_SIZE    = "TextEditor.DecreaseFontSize";
+const char * const GOTO_BLOCK_START      = "TextEditor.GotoBlockStart";
+const char * const GOTO_BLOCK_START_WITH_SELECTION = "TextEditor.GotoBlockStartWithSelection";
+const char * const GOTO_BLOCK_END        = "TextEditor.GotoBlockEnd";
+const char * const GOTO_BLOCK_END_WITH_SELECTION = "TextEditor.GotoBlockEndWithSelection";
 const char * const DELETE_LINE           = "TextEditor.DeleteLine";
 const char * const DELETE_WORD           = "TextEditor.DeleteWord";
 const char * const SELECT_ENCODING       = "TextEditor.SelectEncoding";
+const char * const GOTO_OPENING_PARENTHESIS = "TextEditor.GotoOpeningParenthesis";
+const char * const GOTO_CLOSING_PARENTHESIS = "TextEditor.GotoOpeningParenthesis";
 const char * const C_TEXTEDITOR_MIMETYPE_TEXT = "text/plain";
 const char * const C_TEXTEDITOR_MIMETYPE_XML = "application/xml";
 
-- 
GitLab