File indexing completed on 2024-06-16 05:01:27
0001 /* Copyright (C) 2009, 2011, 2013 by Glad Deschrijver <glad.deschrijver@gmail.com> 0002 Copyright (C) 2013 Thomas Lübking <thomas.luebking@gmail.com> 0003 Copyright (C) 2006 - 2015 Jan Kundrát <jkt@kde.org> 0004 0005 This file is part of the Trojita Qt IMAP e-mail client, 0006 http://trojita.flaska.net/ 0007 0008 This program is free software; you can redistribute it and/or 0009 modify it under the terms of the GNU General Public License as 0010 published by the Free Software Foundation; either version 2 of 0011 the License or (at your option) version 3 or any later version 0012 accepted by the membership of KDE e.V. (or its successor approved 0013 by the membership of KDE e.V.), which shall act as a proxy 0014 defined in Section 14 of version 3 of the license. 0015 0016 This program is distributed in the hope that it will be useful, 0017 but WITHOUT ANY WARRANTY; without even the implied warranty of 0018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0019 GNU General Public License for more details. 0020 0021 You should have received a copy of the GNU General Public License 0022 along with this program. If not, see <http://www.gnu.org/licenses/>. 0023 */ 0024 0025 #include <QAbstractItemView> 0026 #include <QCompleter> 0027 #include <QKeyEvent> 0028 #include "Common/InvokeMethod.h" 0029 #include "Gui/LineEdit.h" 0030 #include "UiUtils/IconLoader.h" 0031 0032 LineEdit::LineEdit(const QString &text, QWidget *parent) 0033 : QLineEdit(text, parent), m_historyEnabled(false), m_historyPosition(0) 0034 { 0035 setClearButtonEnabled(true); 0036 connect(this, &QLineEdit::editingFinished, this, [this](){ 0037 emit textEditingFinished(this->text()); 0038 }); 0039 } 0040 0041 LineEdit::LineEdit(QWidget *parent) 0042 : LineEdit(QString(), parent) 0043 { 0044 } 0045 0046 bool LineEdit::isHistoryEnabled() 0047 { 0048 return m_historyEnabled; 0049 } 0050 0051 void LineEdit::setHistoryEnabled(bool enabled) 0052 { 0053 if (enabled == m_historyEnabled) 0054 return; 0055 m_historyEnabled = enabled; 0056 if (m_historyEnabled) { 0057 // queued so we learn and update the history list *after* the user selected an item from popup 0058 connect(this, &QLineEdit::returnPressed, this, &LineEdit::learnEntry, Qt::QueuedConnection); 0059 QCompleter *completer = new QCompleter(QStringList(), this); 0060 completer->setCaseSensitivity(Qt::CaseInsensitive); 0061 completer->setCompletionMode(QCompleter::InlineCompletion); 0062 setCompleter(completer); 0063 } else { 0064 disconnect(this, &QLineEdit::returnPressed, this, &LineEdit::learnEntry); 0065 delete completer(); 0066 setCompleter(nullptr); 0067 } 0068 } 0069 0070 bool LineEdit::eventFilter(QObject *o, QEvent *e) 0071 { 0072 if (e->type() == QEvent::Hide && o == completer()->popup()) { 0073 // next event cycle, the popup is deleted by the change and "return true" does not help 0074 // connect queued to not manipulate the history *during* selection from popup, but *after* 0075 CALL_LATER_NOARG(this, restoreInlineCompletion); 0076 } 0077 return false; 0078 } 0079 0080 void LineEdit::keyReleaseEvent(QKeyEvent *ke) 0081 { 0082 if (ke->key() == Qt::Key_Escape) { 0083 QLineEdit::keyReleaseEvent(ke); 0084 emit escapePressed(); 0085 return; 0086 } 0087 0088 if (!m_historyEnabled || completer()->completionMode() == QCompleter::UnfilteredPopupCompletion || 0089 !(ke->key() == Qt::Key_Up || ke->key() == Qt::Key_Down)) { 0090 // irrelevant events -> ignore 0091 QLineEdit::keyReleaseEvent(ke); 0092 return; 0093 } 0094 0095 // this manages the shell-a-like history and 0096 // triggers a popdown (when pressing "down" arrow from current entry) 0097 0098 QAbstractItemModel *m = completer()->model(); 0099 int historyCount = m->rowCount(); 0100 0101 if (ke->key() == Qt::Key_Up) { 0102 // shell-a-like history navigation 0103 if (m_historyPosition == historyCount) { 0104 m_currentText = text(); 0105 if (historyCount && m->index(m_historyPosition - 1,0).data().toString() == m_currentText) { 0106 // user still sees entry he entered (into history) last - skip that one. 0107 --m_historyPosition; 0108 } 0109 } 0110 if (--m_historyPosition < 0) { 0111 m_historyPosition = historyCount; 0112 setText(m_currentText); 0113 } else { 0114 setText(m->index(m_historyPosition,0).data().toString()); 0115 } 0116 } else if (ke->key() == Qt::Key_Down) { 0117 if (m_historyPosition + 1 < historyCount && m->index(m_historyPosition + 1,0).data().toString() == m_currentText) { 0118 // user still sees entry he entered (into history) last - skip that one. 0119 ++m_historyPosition; 0120 } 0121 if (++m_historyPosition == historyCount) { 0122 // returning from shell-a-like journey 0123 setText(m_currentText); 0124 } else if (m_historyPosition > historyCount) { 0125 // trigger pop...down ;-) 0126 completer()->setCompletionMode(QCompleter::UnfilteredPopupCompletion); 0127 completer()->complete(); // make a popup 0128 completer()->popup()->removeEventFilter(this); // protected against accidental double filtering 0129 completer()->popup()->installEventFilter(this); // to restore inline completion when it closes 0130 m_historyPosition = historyCount; // fix value to "current" (it's usually historyCount + 1 now) 0131 } else { 0132 // shell-a-like history navigation 0133 setText(m->index(m_historyPosition,0).data().toString()); 0134 } 0135 } 0136 QLineEdit::keyReleaseEvent(ke); 0137 } 0138 0139 void LineEdit::learnEntry() 0140 { 0141 QAbstractItemModel *m = completer()->model(); 0142 int rows = m->rowCount(); 0143 for (int i = 0; i < rows; ++i) { 0144 if (m->index(i,0).data() == text()) { 0145 m->removeRow(i); 0146 --rows; 0147 break; 0148 } 0149 } 0150 m->insertRows(rows, 1); 0151 m->setData(m->index(rows, 0), text(), Qt::DisplayRole); 0152 m_historyPosition = rows + 1; 0153 } 0154 0155 void LineEdit::restoreInlineCompletion() 0156 { 0157 m_currentText = text(); // this was probably just updated by seleting from combobox 0158 completer()->setCompletionMode(QCompleter::InlineCompletion); 0159 CALL_LATER_NOARG(this, setFocus); // can't get in the second event cycle either 0160 }