File indexing completed on 2024-04-28 04:20:48
0001 // SPDX-FileCopyrightText: 2003-2019 The KPhotoAlbum Development Team 0002 // SPDX-FileCopyrightText: 2020 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0003 // SPDX-FileCopyrightText: 2021 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0004 // SPDX-FileCopyrightText: 2022 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0005 // 0006 // SPDX-License-Identifier: GPL-2.0-or-later 0007 0008 #include "CompletableLineEdit.h" 0009 0010 #include "ListSelect.h" 0011 0012 #include <QKeyEvent> 0013 #include <QRegExp> 0014 #include <QTreeWidgetItem> 0015 #include <QTreeWidgetItemIterator> 0016 #include <kcompletion_version.h> 0017 0018 AnnotationDialog::CompletableLineEdit::CompletableLineEdit(ListSelect *parent) 0019 : KLineEdit(parent) 0020 , m_listSelect(parent) 0021 { 0022 } 0023 0024 AnnotationDialog::CompletableLineEdit::CompletableLineEdit(AnnotationDialog::ListSelect *ls, QWidget *parent) 0025 : KLineEdit(parent) 0026 , m_listSelect(ls) 0027 { 0028 } 0029 0030 void AnnotationDialog::CompletableLineEdit::setListView(QTreeWidget *listView) 0031 { 0032 m_listView = listView; 0033 } 0034 0035 void AnnotationDialog::CompletableLineEdit::setMode(UsageMode mode) 0036 { 0037 m_mode = mode; 0038 } 0039 0040 void AnnotationDialog::CompletableLineEdit::keyPressEvent(QKeyEvent *ev) 0041 { 0042 if (ev->key() == Qt::Key_Down || ev->key() == Qt::Key_Up) { 0043 selectPrevNextMatch(ev->key() == Qt::Key_Down); 0044 return; 0045 } 0046 0047 if (m_mode == SearchMode && (ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Enter)) { // Confirm autocomplete, deselect all text 0048 handleSpecialKeysInSearch(ev); 0049 m_listSelect->showOnlyItemsMatching(QString()); // Show all again after confirming autocomplete suggestion. 0050 return; 0051 } 0052 0053 if (m_mode != SearchMode && isSpecialKey(ev)) 0054 return; // Don't insert the special character. 0055 0056 if (ev->key() == Qt::Key_Space && ev->modifiers() & Qt::ControlModifier) { 0057 mergePreviousImageSelection(); 0058 return; 0059 } 0060 0061 QString prevContent = text(); 0062 0063 if (ev->text().isEmpty() || !ev->text()[0].isPrint()) { 0064 // If final Return is handled by the default implementation, 0065 // it can "leak" to other widgets. So we swallow it here: 0066 if (ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Enter) 0067 #if KCOMPLETION_VERSION >= QT_VERSION_CHECK(5, 81, 0) 0068 Q_EMIT KLineEdit::returnKeyPressed(text()); 0069 #else 0070 Q_EMIT KLineEdit::returnPressed(text()); 0071 #endif 0072 else 0073 KLineEdit::keyPressEvent(ev); 0074 if (prevContent != text()) 0075 m_listSelect->showOnlyItemsMatching(text()); 0076 return; 0077 } 0078 0079 // &,|, or ! should result in the current item being inserted 0080 if (m_mode == SearchMode && isSpecialKey(ev)) { 0081 handleSpecialKeysInSearch(ev); 0082 m_listSelect->showOnlyItemsMatching(QString()); // Show all again after a special caracter. 0083 return; 0084 } 0085 0086 int cursorPos = cursorPosition(); 0087 int selStart = selectionStart(); 0088 0089 KLineEdit::keyPressEvent(ev); 0090 0091 // Find the text of the current item 0092 int itemStart = 0; 0093 QString input = text(); 0094 if (m_mode == SearchMode) { 0095 input = input.left(cursorPosition()); 0096 itemStart = input.lastIndexOf(QRegExp(QString::fromLatin1("[!&|]"))) + 1; 0097 0098 if (itemStart > 0) { 0099 itemStart++; 0100 } 0101 0102 input = input.mid(itemStart); 0103 } 0104 0105 // Find the text in the listView 0106 QTreeWidgetItem *item = findItemInListView(input); 0107 if (!item && m_mode == SearchMode) { 0108 // revert 0109 setText(prevContent); 0110 setCursorPosition(cursorPos); 0111 item = findItemInListView(input); 0112 if (selStart >= 0) 0113 setSelection(selStart, prevContent.length()); // Reset previous selection. 0114 } 0115 0116 if (item) { 0117 selectItemAndUpdateLineEdit(item, itemStart, input); 0118 m_listSelect->showOnlyItemsMatching(input); 0119 } else if (m_mode != SearchMode) 0120 m_listSelect->showOnlyItemsMatching(input); 0121 } 0122 0123 /** 0124 * Search for the first item in the appearance order, which matches text. 0125 */ 0126 QTreeWidgetItem *AnnotationDialog::CompletableLineEdit::findItemInListView(const QString &text) 0127 { 0128 for (QTreeWidgetItemIterator itemIt(m_listView); *itemIt; ++itemIt) { 0129 // Hide the "untagged image" tag from the auto-completion 0130 if ((*itemIt)->isHidden()) { 0131 continue; 0132 } 0133 0134 if (itemMatchesText(*itemIt, text)) { 0135 return *itemIt; 0136 } 0137 } 0138 0139 return nullptr; 0140 } 0141 0142 bool AnnotationDialog::CompletableLineEdit::itemMatchesText(QTreeWidgetItem *item, const QString &text) 0143 { 0144 return item->text(0).toLower().startsWith(text.toLower()); 0145 } 0146 0147 bool AnnotationDialog::CompletableLineEdit::isSpecialKey(QKeyEvent *ev) 0148 { 0149 return (ev->text() == QString::fromLatin1("&") || ev->text() == QString::fromLatin1("|") || ev->text() == QString::fromLatin1("!") 0150 /* || ev->text() == "(" */ 0151 ); 0152 } 0153 0154 void AnnotationDialog::CompletableLineEdit::handleSpecialKeysInSearch(QKeyEvent *ev) 0155 { 0156 int cursorPos = cursorPosition(); 0157 QString txt; 0158 int additionalLength; 0159 0160 if (!isSpecialKey(ev)) { 0161 txt = text().left(cursorPos) + ev->text() + text().mid(cursorPos); 0162 additionalLength = 0; 0163 } else { 0164 txt = text() + QString::fromUtf8(" %1 ").arg(ev->text()); 0165 cursorPos += 2; 0166 additionalLength = 2; 0167 } 0168 setText(txt); 0169 0170 if (!isSpecialKey(ev)) { 0171 // Special handling for ENTER to position the cursor correctly 0172 setText(text().left(text().size() - 1)); 0173 cursorPos--; 0174 } 0175 0176 setCursorPosition(cursorPos + ev->text().length() + additionalLength); 0177 deselect(); 0178 0179 // Select the item in the listView - not perfect but acceptable for now. 0180 int start = txt.lastIndexOf(QRegExp(QString::fromLatin1("[!&|]")), cursorPosition() - 2) + 1; 0181 if (start > 0) { 0182 start++; 0183 } 0184 QString input = txt.mid(start, cursorPosition() - start); 0185 0186 if (!input.isEmpty()) { 0187 QTreeWidgetItem *item = findItemInListView(input); 0188 if (item) { 0189 item->setCheckState(0, Qt::Checked); 0190 } 0191 } 0192 } 0193 0194 void AnnotationDialog::CompletableLineEdit::selectPrevNextMatch(bool next) 0195 { 0196 QTreeWidgetItem *item { nullptr }; 0197 0198 // the current item is usually the selected one... 0199 QList<QTreeWidgetItem *> selectedItems = m_listView->selectedItems(); 0200 if (!selectedItems.isEmpty()) 0201 item = selectedItems.at(0); 0202 0203 // ...except when the selected one is filtered out: 0204 if (!item || item->isHidden()) { 0205 // in that case, we select the first item in the viewport 0206 item = m_listView->itemAt(0, 0); 0207 } 0208 if (!item) 0209 return; 0210 QTreeWidgetItem *baseItem = item; 0211 0212 // only go to the next item, if there was a "previous" selected item: 0213 if (!selectedItems.isEmpty()) { 0214 if (next) 0215 item = m_listView->itemBelow(item); 0216 else 0217 item = m_listView->itemAbove(item); 0218 } 0219 0220 // select current item if there is no next/prev item: 0221 if (!item) { 0222 item = baseItem; 0223 } 0224 0225 // extract last component of line edit 0226 int itemStart = text().lastIndexOf(QRegExp(QString::fromLatin1("[!&|]"))) + 1; 0227 selectItemAndUpdateLineEdit(item, itemStart, text().left(selectionStart())); 0228 } 0229 0230 void AnnotationDialog::CompletableLineEdit::selectItemAndUpdateLineEdit(QTreeWidgetItem *item, 0231 const int itemStart, 0232 const QString &inputText) 0233 { 0234 m_listView->setCurrentItem(item); 0235 m_listView->scrollToItem(item); 0236 0237 QString txt = text().left(itemStart) + item->text(0) + text().mid(cursorPosition()); 0238 0239 setText(txt); 0240 setSelection(itemStart + inputText.length(), item->text(0).length() - inputText.length()); 0241 } 0242 0243 void AnnotationDialog::CompletableLineEdit::mergePreviousImageSelection() 0244 { 0245 } 0246 0247 // vi:expandtab:tabstop=4 shiftwidth=4: