/**
* \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
#include
QHistoryLineEdit::QHistoryLineEdit (const QString& contents, QWidget* parent) : QLineEdit(contents, parent), _currentLine(0), _completer(0), _completionMinchars(1), _completionMax(0) {
enableReturnPressedOnClear();
QObject::connect(this, &QLineEdit::returnPressed, this, &QHistoryLineEdit::execute);
}
QHistoryLineEdit::QHistoryLineEdit (QWidget* parent) : QLineEdit(parent), _currentLine(0), _completer(0), _completionMinchars(1), _completionMax(0) {
enableReturnPressedOnClear();
QObject::connect(this, &QLineEdit::returnPressed, this, &QHistoryLineEdit::execute);
}
/**
* \brief Enable "return press" signal on Clear action.
*/
void QHistoryLineEdit::enableReturnPressedOnClear() {
for (int i=0; i (children().at(i)));
if (myClearAction) {
//qDebug() << myClearAction->objectName();
// Look for only the QLineEdit clear action.
// This name could change or be different.
if (myClearAction->objectName() == "_q_qlineeditclearaction") {
connect(myClearAction, &QAction::triggered, this, &QLineEdit::returnPressed, Qt::QueuedConnection);
}
}
}
}
/**
* \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 () {
// Ignore blank lines.
if (text() == "") {
return;
}
// Add the line if it doesn't equal the previous (that is store in the history).
if ( _lines.empty() || _lines.back() != text() ) {
_lines << text();
}
// Set our current position in the history list.
_currentLine = _lines.size();
// If it wants to, the calling widget should clear the text line.
// clear();
// Emit the entered text.
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::focusOutEvent (QFocusEvent* ev) {
QLineEdit::focusOutEvent(ev);
emit lostFocus();
}
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;
}