diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bba3617..76dba6c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -96,6 +96,7 @@ set(HEADER_FILES
QClearLineEdit.h
QIndexTreeWidget.h
QStringPair.h
+ QHistoryLineEdit.h
)
set(SOURCE_FILES
@@ -174,6 +175,7 @@ set(SOURCE_FILES
QZoomChart.cpp
QClearLineEdit.cpp
QIndexTreeWidget.cpp
+ QHistoryLineEdit.cpp
)
qt5_add_resources(SOURCE_FILES resource.qrc)
diff --git a/src/QHistoryLineEdit.cpp b/src/QHistoryLineEdit.cpp
new file mode 100644
index 0000000..ebfabaf
--- /dev/null
+++ b/src/QHistoryLineEdit.cpp
@@ -0,0 +1,279 @@
+/**
+ * \file
+ *
+ * \author Mattia Basaglia
+ *
+ * \copyright Copyright (C) 2012-2020 Mattia Basaglia
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ */
+#include "QHistoryLineEdit.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+QHistoryLineEdit::QHistoryLineEdit(QWidget *parent) : QLineEdit(parent), _currentLine(0), _completer(0), _completionMinchars(1), _completionMax(0) {
+
+ QObject::connect(this, &QLineEdit::returnPressed, this, &QHistoryLineEdit::execute);
+}
+
+/**
+ * \brief Number of available lines
+ */
+int QHistoryLineEdit::lineCount() const {
+
+ return _lines.size();
+}
+
+/**
+ * \brief Overwrite the line history
+ */
+void QHistoryLineEdit::setHistory(const QStringList& history) {
+
+ _lines = history;
+ _currentLine = _lines.size();
+}
+
+/**
+ * \brief Stored history
+ */
+QStringList QHistoryLineEdit::history() const {
+
+ return _lines;
+}
+
+/**
+ * \brief Sets the completer used on a per-word completion
+ *
+ * Unlike setCompleter(), this suggests completion at every entered word
+ *
+ * If \c completer is null it will remove the current completer
+ */
+void QHistoryLineEdit::setWordCompleter(QCompleter* comp) {
+
+ if ( _completer ) {
+
+ QObject::disconnect(_completer, 0, this, 0);
+
+ _completer->setWidget(0);
+ }
+
+ _completer = comp;
+
+ if ( comp ) {
+
+ /// \todo should set these only when on focus
+ QObject::connect(_completer, QOverload::of(&QCompleter::activated), this, &QHistoryLineEdit::autoComplete);
+ QObject::connect(_completer, QOverload::of(&QCompleter::highlighted), this, &QHistoryLineEdit::autoComplete);
+
+ _completer->setWidget(this);
+ }
+}
+
+/**
+ * \brief Sets a prefix that is ignored by the word completer
+ */
+void QHistoryLineEdit::setWordCompleterPrefix(const QString& prefix) {
+ _completionPrefix = prefix;
+}
+
+/**
+ * \brief Sets the minimum number of characters required to display the word completer
+ */
+void QHistoryLineEdit::setWordCompleterMinChars(int minChars) {
+ _completionMinchars = minChars;
+}
+
+/**
+ * \brief Sets the maximum number of suggestions that the completer should show.
+ *
+ * If more than this many suggestions are found the completer isn't shown
+ */
+void QHistoryLineEdit::setWordCompleterMaxSuggestions(int max) {
+ _completionMax = max;
+}
+
+/**
+ * \brief Executes the current line
+ */
+void QHistoryLineEdit::execute() {
+
+ if ( _lines.empty() || _lines.back() != text() ) {
+ _lines << text();
+ }
+
+ _currentLine = _lines.size();
+
+ clear();
+
+ emit lineExecuted(_lines.back());
+}
+
+void QHistoryLineEdit::keyPressEvent(QKeyEvent * ev) {
+
+ if ( ev->key() == Qt::Key_Up ) {
+ previousLine();
+ return;
+
+ } else if ( ev->key() == Qt::Key_Down ) {
+ nextLine();
+ return;
+
+ } else if (_completer && _completer->popup() && _completer->popup()->isVisible()) {
+ switch (ev->key()) {
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_F4:
+ case Qt::Key_Select:
+ _completer->popup()->hide();
+ return;
+ }
+ }
+
+ QLineEdit::keyPressEvent(ev);
+
+ if (_completer) {
+
+ QString current = current_word();
+ _completer->setCompletionPrefix(current);
+
+ if ( current.size() < _completionMinchars || _completer->completionCount() == 0 || (_completionMax > 0 && _completer->completionCount() > _completionMax) ) {
+
+ _completer->popup()->hide();
+
+ } else {
+ // Get the selection status
+ int sel = selectionStart();
+ int sellength = selectedText().size();
+
+ // Get the current cursor position
+ int c = cursorPosition();
+
+ // Get the start of the current word
+ setCursorPosition(_wordStart());
+
+ // Get the cursor rectangle at the beginning of the current word
+ QRect rect = cursorRect();
+
+ // Restore cursor position (clears the selection)
+ setCursorPosition(c);
+
+ // If we had a selection
+ if ( sel != -1 ) {
+ // If the selection started at the cursor,
+ // it needs to start at the far end and go back
+ // (otherwise it moves the cursor at the end)
+ if ( sel == c )
+ setSelection(sel+sellength, -sellength);
+ else
+ setSelection(sel, sellength);
+ }
+
+ // Set the rectangle to the appropriate width
+ rect.setWidth( _completer->popup()->sizeHintForColumn(0) + _completer->popup()->verticalScrollBar()->sizeHint().width());
+
+ // Display the _completer under the rectangle
+ _completer->complete(rect);
+ }
+ }
+}
+
+void QHistoryLineEdit::wheelEvent(QWheelEvent *ev) {
+
+ if ( ev->angleDelta().y() > 0 ) {
+ previousLine();
+ }else{
+ nextLine();
+ }
+}
+
+void QHistoryLineEdit::previousLine() {
+
+ if ( _lines.empty() ) {
+ return;
+ }
+
+ if ( !text().isEmpty() && ( _currentLine >= _lines.size() || text() != _lines[_currentLine] ) ) {
+ _unfinished = text();
+ }
+
+ if ( _currentLine > 0 ) {
+ _currentLine--;
+ }
+
+ setText(_lines[_currentLine]);
+}
+
+
+void QHistoryLineEdit::nextLine() {
+
+ if ( _lines.empty() ) {
+ return;
+ }
+
+ _currentLine++;
+
+ if ( _currentLine >= _lines.size() ) {
+
+ setText(_unfinished);
+ _unfinished = "";
+ _currentLine = _lines.size();
+
+ } else {
+ setText(_lines[_currentLine]);
+ }
+}
+
+/**
+ * \brief Current word being edited (used to fire the completer)
+ */
+QString QHistoryLineEdit::current_word() const {
+
+ int completion_index = _wordStart();
+
+ return text().mid(completion_index, cursorPosition() - completion_index);
+}
+
+/**
+ * \brief Autocompletes the current word
+ */
+void QHistoryLineEdit::autoComplete(const QString& completion) {
+
+ int completion_index = _wordStart();
+
+ setText(text().replace( completion_index, cursorPosition() - completion_index, completion));
+
+ setCursorPosition(completion_index+completion.size());
+}
+
+/**
+ * \brief Returns the index of the character starting the currently edited word
+ */
+int QHistoryLineEdit::_wordStart() const {
+
+ // lastIndexOf returns the index of the last space or -1 if there are no spaces
+ // so that + 1 returns the index of the character starting the word or 0
+ int after_space = text().leftRef(cursorPosition()).lastIndexOf(' ') + 1;
+
+ if ( text().rightRef(text().size()-after_space).startsWith(_completionPrefix) ) {
+ after_space += _completionPrefix.size();
+ }
+
+ return after_space;
+}
+
diff --git a/src/QHistoryLineEdit.h b/src/QHistoryLineEdit.h
new file mode 100644
index 0000000..29bacb9
--- /dev/null
+++ b/src/QHistoryLineEdit.h
@@ -0,0 +1,75 @@
+/**
+ * \file
+ *
+ * \author Mattia Basaglia
+ *
+ * \copyright Copyright (C) 2012-2020 Mattia Basaglia
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+
+#include
+
+/**
+ * \brief Line edit providing a history of entered text
+ */
+class QHistoryLineEdit : public QLineEdit {
+
+ Q_OBJECT
+
+ public:
+ explicit QHistoryLineEdit(QWidget *parent = 0);
+
+ int lineCount () const;
+
+ void setHistory (const QStringList& history);
+ QStringList history() const;
+
+ void setWordCompleter (QCompleter* completer);
+ void setWordCompleterPrefix (const QString& prefix);
+ void setWordCompleterMinChars (int minChars);
+ void setWordCompleterMaxSuggestions (int max);
+
+ public slots:
+ void execute ();
+
+ signals:
+ void lineExecuted (QString);
+
+ protected:
+ void keyPressEvent (QKeyEvent *) Q_DECL_OVERRIDE;
+ void wheelEvent (QWheelEvent *) Q_DECL_OVERRIDE;
+
+ void previousLine ();
+ void nextLine ();
+
+ QString current_word () const;
+
+ private slots:
+ void autoComplete (const QString& completion);
+
+ private:
+ int _wordStart () const;
+
+ int _currentLine;
+ QStringList _lines;
+ QString _unfinished;
+ QCompleter* _completer;
+ QString _completionPrefix;
+ int _completionMinchars;
+ int _completionMax;
+};
+
diff --git a/src/SeerStructVisualizerWidget.cpp b/src/SeerStructVisualizerWidget.cpp
index 84fe3f3..ffdf294 100644
--- a/src/SeerStructVisualizerWidget.cpp
+++ b/src/SeerStructVisualizerWidget.cpp
@@ -31,7 +31,7 @@ SeerStructVisualizerWidget::SeerStructVisualizerWidget (QWidget* parent) : QWidg
// Connect things.
QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerStructVisualizerWidget::handleRefreshButton);
- QObject::connect(variableNameLineEdit, &QLineEdit::returnPressed, this, &SeerStructVisualizerWidget::handleVariableNameLineEdit);
+ QObject::connect(variableNameLineEdit, &QHistoryLineEdit::lineExecuted, this, &SeerStructVisualizerWidget::handleVariableNameLineEdit);
QObject::connect(variableTreeWidget, &QTreeWidget::itemEntered, this, &SeerStructVisualizerWidget::handleItemEntered);
QObject::connect(variableTreeWidget, &QTreeWidget::itemExpanded, this, &SeerStructVisualizerWidget::handleItemExpanded);
QObject::connect(variableTreeWidget, &QTreeWidget::itemCollapsed, this, &SeerStructVisualizerWidget::handleItemExpanded);
@@ -227,9 +227,9 @@ void SeerStructVisualizerWidget::handleRefreshButton () {
emit evaluateVariableExpression(_variableId, variableNameLineEdit->text());
}
-void SeerStructVisualizerWidget::handleVariableNameLineEdit () {
+void SeerStructVisualizerWidget::handleVariableNameLineEdit (const QString& text) {
- setVariableName (variableNameLineEdit->text());
+ setVariableName (text);
}
void SeerStructVisualizerWidget::writeSettings() {
diff --git a/src/SeerStructVisualizerWidget.h b/src/SeerStructVisualizerWidget.h
index 213230b..bfcfd65 100644
--- a/src/SeerStructVisualizerWidget.h
+++ b/src/SeerStructVisualizerWidget.h
@@ -22,7 +22,7 @@ class SeerStructVisualizerWidget : public QWidget, protected Ui::SeerStructVisua
protected slots:
void handleRefreshButton ();
- void handleVariableNameLineEdit ();
+ void handleVariableNameLineEdit (const QString& text);
void handleItemEntered (QTreeWidgetItem* item, int column);
void handleItemExpanded (QTreeWidgetItem* item);
diff --git a/src/SeerStructVisualizerWidget.ui b/src/SeerStructVisualizerWidget.ui
index 7a9e63c..8d96588 100644
--- a/src/SeerStructVisualizerWidget.ui
+++ b/src/SeerStructVisualizerWidget.ui
@@ -15,7 +15,7 @@
-
-
+
Enter a variable to visualize.
@@ -77,6 +77,13 @@
+
+
+ QHistoryLineEdit
+ QLineEdit
+
+
+