diff --git a/src/SeerEditorManagerWidget.cpp b/src/SeerEditorManagerWidget.cpp index 03457ab..cebe963 100644 --- a/src/SeerEditorManagerWidget.cpp +++ b/src/SeerEditorManagerWidget.cpp @@ -902,6 +902,8 @@ void SeerEditorManagerWidget::handleOpenFileWithDetails (const QString& file, co } } + handleAddToMouseNavigation({file, fullname, cursorRow, 1, editorWidget->sourceArea()->firstDisplayLine()}); + // Ask for the breakpoint list to be resent, in case this file has breakpoints. emit refreshBreakpointsList(); @@ -1035,6 +1037,7 @@ SeerEditorWidgetSource* SeerEditorManagerWidget::createEditorWidgetTab (const QS QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addMatrixVisualizer, this, &SeerEditorManagerWidget::handleAddMatrixVisualizer); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addStructVisualizer, this, &SeerEditorManagerWidget::handleAddStructVisualizer); QObject::connect(editorWidget, &SeerEditorWidgetSource::addAlternateDirectory, this, &SeerEditorManagerWidget::handleAddAlternateDirectory); + QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addToMouseNavigation, this, &SeerEditorManagerWidget::handleAddToMouseNavigation); // Send the Editor widget the command to load the file. ??? Do better than this. editorWidget->sourceArea()->handleText(text); @@ -1097,6 +1100,7 @@ SeerEditorWidgetSource* SeerEditorManagerWidget::createEditorWidgetTab (const QS QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addMatrixVisualizer, this, &SeerEditorManagerWidget::handleAddMatrixVisualizer); QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addStructVisualizer, this, &SeerEditorManagerWidget::handleAddStructVisualizer); QObject::connect(editorWidget, &SeerEditorWidgetSource::addAlternateDirectory, this, &SeerEditorManagerWidget::handleAddAlternateDirectory); + QObject::connect(editorWidget->sourceArea(), &SeerEditorWidgetSourceArea::addToMouseNavigation, this, &SeerEditorManagerWidget::handleAddToMouseNavigation); // Load the file. editorWidget->sourceArea()->open(fullname, QFileInfo(file).fileName()); @@ -1475,10 +1479,116 @@ void SeerEditorManagerWidget::handleSessionTerminated () { } } -// Clear the stack of recently closed files whenever a new gdb session starts +// Clear the stack of recently closed files backward/forward list whenever a new gdb session starts void SeerEditorManagerWidget::handleGdbStateChanged() { while (!_stackClosedFiles.empty()) { _stackClosedFiles.pop(); } + _listForwardFiles.clear(); + _forwardFilesIndex = -1; } +/*********************************************************************************************************************** + * Function for handling mouse navigation + **********************************************************************************************************************/ +void SeerEditorManagerWidget::handleAddToMouseNavigation(const SeerEditorWidgetSourceArea::SeerCurrentFile& currentFile) +{ + // Record this file in the forward list. + if (currentFile.file != "") + { + // Check if the last added file is the same as currentFile + if (!_listForwardFiles.empty()) + { + const SeerEditorWidgetSourceArea::SeerCurrentFile& lastFile = _listForwardFiles[_listForwardFiles.size() -1]; + if (currentFile == lastFile) + { + return; + } + } + + // if _forwardFilesIndex is at the end of the list, just append + if (_forwardFilesIndex >= _listForwardFiles.size() - 1) + { + _listForwardFiles.append(currentFile); + _forwardFilesIndex ++; + } + else // else remove all entries after _forwardFilesIndex and then append + { + int removeCount = _listForwardFiles.size() - 1 - _forwardFilesIndex; + if ((currentFile == _listForwardFiles[_listForwardFiles.size() -1]) || (currentFile == _listForwardFiles[0]) + || (currentFile == _listForwardFiles[_listForwardFiles.size() - 1 - removeCount]) ) + { + return; + } + for (int i=0; i < removeCount; i++) + { + _listForwardFiles.removeLast(); + } + _listForwardFiles.append(currentFile); + _forwardFilesIndex = _listForwardFiles.size() -1; + } + } +} + +void SeerEditorManagerWidget::handleOpenForwardBackward(const SeerEditorWidgetSourceArea::SeerCurrentFile& fileInfo) { + // Get the EditorWidget for the file. Create one if needed. + SeerEditorWidgetSource* editorWidget = editorWidgetTab(fileInfo.fullname); + + if (editorWidget == 0) { + editorWidget = createEditorWidgetTab(fileInfo.fullname, fileInfo.file); + } + + // Can still be null, if the file is ignored. + if (editorWidget == 0) { + return; + } + + // Push this tab to the top only if the current one in not the "Assembly" tab. + QString tabtext = ""; + + if (tabWidget->currentIndex() >= 0) { + tabtext = tabWidget->tabText(tabWidget->currentIndex()); + } + + if (keepAssemblyTabOnTop() && tabtext == "Assembly") { + // Do nothing. The "Assembly" tab is already on top. + }else{ + tabWidget->setCurrentWidget(editorWidget); + } + + if (fileInfo.firstDisplayLine > 0) { // scroll to the previous first display line + QScrollBar *scrollBar = editorWidget->sourceArea()->verticalScrollBar(); + int step = scrollBar->singleStep(); + scrollBar->setValue(fileInfo.firstDisplayLine * step - 1); + + // Set cursor position + QTextBlock block = editorWidget->sourceArea()->document()->findBlockByLineNumber(fileInfo.cursorRow-1); + QTextCursor cursor = editorWidget->sourceArea()->textCursor(); + cursor.setPosition(block.position() + fileInfo.cursorCol - 1); + editorWidget->sourceArea()->setTextCursor(cursor); + } +} +// Handle mouse press events for navigation +void SeerEditorManagerWidget::mousePressEvent(QMouseEvent *event) +{ + // Back button (XButton1) clicked + if (event->button() == Qt::XButton1) { + if (_forwardFilesIndex - 1 < 0) + return; + --_forwardFilesIndex; + const SeerEditorWidgetSourceArea::SeerCurrentFile &info = _listForwardFiles.at(_forwardFilesIndex); + handleOpenForwardBackward(info); + } + // Forward button (XButton2) clicked + else if (event->button() == Qt::XButton2) { + if (_forwardFilesIndex + 1 > _listForwardFiles.size() - 1) + return; + ++ _forwardFilesIndex; + const SeerEditorWidgetSourceArea::SeerCurrentFile &info = _listForwardFiles.at(_forwardFilesIndex); + handleOpenForwardBackward(info); + } + else + { + QWidget::mousePressEvent(event); + } +} \ No newline at end of file diff --git a/src/SeerEditorManagerWidget.h b/src/SeerEditorManagerWidget.h index 67c92ef..3f78de6 100644 --- a/src/SeerEditorManagerWidget.h +++ b/src/SeerEditorManagerWidget.h @@ -103,6 +103,10 @@ class SeerEditorManagerWidget : public QWidget, protected Ui::SeerEditorManagerW void handleAssemblyConfigChanged (); void handleSessionTerminated (); void handleGdbStateChanged (); + void handleAddToMouseNavigation (const SeerEditorWidgetSourceArea::SeerCurrentFile& currentFile); + + protected: + void mousePressEvent (QMouseEvent *event) override; private slots: void handleFileOpenToolButtonClicked (); @@ -144,6 +148,7 @@ class SeerEditorManagerWidget : public QWidget, protected Ui::SeerEditorManagerW SeerEditorWidgetAssembly* createAssemblyWidgetTab (); void deleteAssemblyWidgetTab (); void handleOpenFileWithDetails (const QString& file, const QString& fullname, int cursorRow, int cursorCol, int firstDisplayLine); + void handleOpenForwardBackward (const SeerEditorWidgetSourceArea::SeerCurrentFile& fileInfo); SeerEditorManagerEntries _entries; SeerHighlighterSettings _editorHighlighterSettings; @@ -167,5 +172,8 @@ class SeerEditorManagerWidget : public QWidget, protected Ui::SeerEditorManagerW // list of recently closed files (Ctrl + Shift + T) QStack _stackClosedFiles; + // list of cursor positions for mouse navigation (back/forward) + QList _listForwardFiles; + int _forwardFilesIndex = -1; }; diff --git a/src/SeerEditorWidgetSource.h b/src/SeerEditorWidgetSource.h index b1650ac..ec16f01 100644 --- a/src/SeerEditorWidgetSource.h +++ b/src/SeerEditorWidgetSource.h @@ -140,6 +140,7 @@ class SeerEditorWidgetSourceArea : public SeerPlainTextEdit { void showAlternateBar (bool flag); void showReloadBar (bool flag); void highlighterSettingsChanged (); + void addToMouseNavigation (const SeerCurrentFile& currentFile); public slots: void handleText (const QString& text); @@ -154,6 +155,7 @@ class SeerEditorWidgetSourceArea : public SeerPlainTextEdit { bool event (QEvent* event); void showExpressionTooltip (); void hideExpressionTooltip (); + void mousePressEvent (QMouseEvent *event) override; private slots: void refreshExtraSelections (); @@ -163,6 +165,7 @@ class SeerEditorWidgetSourceArea : public SeerPlainTextEdit { void updateBreakPointArea (const QRect& rect, int dy); private: + void handleCursorPositionChanged (); QString _fullname; QString _file; QString _alternateDirectory; @@ -194,6 +197,8 @@ class SeerEditorWidgetSourceArea : public SeerPlainTextEdit { int _sourceTabSize; QString _externalEditorCommand; + + int _ignoreThumbMouseEvent = 0; }; class SeerEditorWidgetSourceLineNumberArea : public QWidget { diff --git a/src/SeerEditorWidgetSourceAreas.cpp b/src/SeerEditorWidgetSourceAreas.cpp index 60571dc..324b77e 100644 --- a/src/SeerEditorWidgetSourceAreas.cpp +++ b/src/SeerEditorWidgetSourceAreas.cpp @@ -69,6 +69,9 @@ SeerEditorWidgetSourceArea::SeerEditorWidgetSourceArea(QWidget* parent) : SeerPl QObject::connect(this, &SeerEditorWidgetSourceArea::updateRequest, this, &SeerEditorWidgetSourceArea::updateBreakPointArea); QObject::connect(this, &SeerEditorWidgetSourceArea::highlighterSettingsChanged, this, &SeerEditorWidgetSourceArea::handleHighlighterSettingsChanged); + // Connect cursor position changed signal. + QObject::connect(this, &QPlainTextEdit::cursorPositionChanged, this, &SeerEditorWidgetSourceArea::handleCursorPositionChanged); + setCurrentLine(0); updateMarginAreasWidth(0); @@ -2025,3 +2028,43 @@ void SeerEditorWidgetSourceBreakPointArea::mouseReleaseEvent (QMouseEvent* event QWidget::mouseReleaseEvent(event); } +/*********************************************************************************************************************** + * Functions for mouse navigation feature * + **********************************************************************************************************************/ +void SeerEditorWidgetSourceArea::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::XButton1 || event->button() == Qt::XButton2) { + // Avoid the default back/forward action + _ignoreThumbMouseEvent ++; + SeerCurrentFile firstInfo = readCurrentPosition(); + // _ignoreThumbMouseEvent will incremented in mousePressEvent when thumb mouse button is pressed but it may not be decremented + // if user just click the thumb mouse button without moving the cursor. So here we check if the position is the same as last time, + // which means cursor didn't move, then we reset _ignoreThumbMouseEvent to 0 to avoid blocking future events. + QTimer::singleShot(30, this, [this, firstInfo]() { // let's give 30 ms for cursor to move to new position + SeerCurrentFile secondInfo = readCurrentPosition(); + if (firstInfo == secondInfo) { // cursor didn't move + _ignoreThumbMouseEvent = 0; // reset to 0 + } + } ); + } + QPlainTextEdit::mousePressEvent(event); +} + +void SeerEditorWidgetSourceArea::handleCursorPositionChanged() +{ + // Emit signal to SeerEditorManagerWidget.cpp and save position + // Read current position, deploy a timer + SeerCurrentFile firstInfo = readCurrentPosition(); + if (_ignoreThumbMouseEvent > 0) + { + _ignoreThumbMouseEvent --; + return; + } + + QTimer::singleShot(2000, this, [this, firstInfo]() { + SeerCurrentFile secondInfo = readCurrentPosition(); + if (firstInfo == secondInfo) { + emit addToMouseNavigation(firstInfo); + } + } ); +}