File indexing completed on 2024-05-12 11:47:11

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     SPDX-FileCopyrightText: 1997 Sven Radej <sven.radej@iname.com>
0005     SPDX-FileCopyrightText: 1999 Patrick Ward <PAT_WARD@HP-USA-om5.om.hp.com>
0006     SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
0007 
0008     Re-designed for KDE 2.x by
0009     SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
0010     SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
0011 
0012     SPDX-License-Identifier: LGPL-2.0-or-later
0013 */
0014 
0015 #include "klineedit.h"
0016 #include "klineedit_p.h"
0017 
0018 #include <KAuthorized>
0019 #include <KConfigGroup>
0020 #include <KCursor>
0021 #include <KSharedConfig>
0022 #include <KStandardShortcut>
0023 #include <kcompletionbox.h>
0024 #include <lineediturldropeventfilter.h>
0025 
0026 #include <QActionGroup>
0027 #include <QApplication>
0028 #include <QClipboard>
0029 #include <QKeyEvent>
0030 #include <QMenu>
0031 #include <QTimer>
0032 #include <QToolTip>
0033 
0034 KLineEditPrivate::~KLineEditPrivate()
0035 {
0036     // causes a weird crash in KWord at least, so let Qt delete it for us.
0037     //        delete completionBox;
0038 }
0039 
0040 void KLineEditPrivate::_k_textChanged(const QString &text)
0041 {
0042     Q_Q(KLineEdit);
0043     // COMPAT (as documented): emit userTextChanged whenever textChanged is emitted
0044     if (!completionRunning && (text != userText)) {
0045         userText = text;
0046 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(4, 5)
0047         Q_EMIT q->userTextChanged(text);
0048 #endif
0049     }
0050 }
0051 
0052 // Call this when a completion operation changes the lineedit text
0053 // "as if it had been edited by the user".
0054 void KLineEditPrivate::updateUserText(const QString &text)
0055 {
0056     Q_Q(KLineEdit);
0057     if (!completionRunning && (text != userText)) {
0058         userText = text;
0059         q->setModified(true);
0060 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(4, 5)
0061         Q_EMIT q->userTextChanged(text);
0062 #endif
0063         Q_EMIT q->textEdited(text);
0064         Q_EMIT q->textChanged(text);
0065     }
0066 }
0067 
0068 bool KLineEditPrivate::s_backspacePerformsCompletion = false;
0069 bool KLineEditPrivate::s_initialized = false;
0070 
0071 void KLineEditPrivate::init()
0072 {
0073     Q_Q(KLineEdit);
0074     //---
0075     completionBox = nullptr;
0076     handleURLDrops = true;
0077     trapReturnKeyEvents = false;
0078 
0079     userSelection = true;
0080     autoSuggest = false;
0081     disableRestoreSelection = false;
0082     enableSqueezedText = false;
0083 
0084     completionRunning = false;
0085     if (!s_initialized) {
0086         KConfigGroup config(KSharedConfig::openConfig(), "General");
0087         s_backspacePerformsCompletion = config.readEntry("Backspace performs completion", false);
0088         s_initialized = true;
0089     }
0090 
0091     urlDropEventFilter = new LineEditUrlDropEventFilter(q);
0092 
0093     // i18n: Placeholder text in line edit widgets is the text appearing
0094     // before any user input, briefly explaining to the user what to type
0095     // (e.g. "Enter search pattern").
0096     // By default the text is set in italic, which may not be appropriate
0097     // for some languages and scripts (e.g. for CJK ideographs).
0098     QString metaMsg = KLineEdit::tr("1", "Italic placeholder text in line edits: 0 no, 1 yes");
0099     italicizePlaceholder = (metaMsg.trimmed() != QLatin1Char('0'));
0100     //---
0101     possibleTripleClick = false;
0102     bgRole = q->backgroundRole();
0103 
0104     // Enable the context menu by default.
0105     q->QLineEdit::setContextMenuPolicy(Qt::DefaultContextMenu);
0106     KCursor::setAutoHideCursor(q, true, true);
0107 
0108     KCompletion::CompletionMode mode = q->completionMode();
0109     autoSuggest = (mode == KCompletion::CompletionMan //
0110                    || mode == KCompletion::CompletionPopupAuto //
0111                    || mode == KCompletion::CompletionAuto);
0112     q->connect(q, &KLineEdit::selectionChanged, q, [this]() {
0113         _k_restoreSelectionColors();
0114     });
0115 
0116     if (handleURLDrops) {
0117         q->installEventFilter(urlDropEventFilter);
0118     }
0119 
0120     const QPalette p = q->palette();
0121     if (!previousHighlightedTextColor.isValid()) {
0122         previousHighlightedTextColor = p.color(QPalette::Normal, QPalette::HighlightedText);
0123     }
0124     if (!previousHighlightColor.isValid()) {
0125         previousHighlightColor = p.color(QPalette::Normal, QPalette::Highlight);
0126     }
0127 
0128     q->connect(q, &KLineEdit::textChanged, q, [this](const QString &text) {
0129         _k_textChanged(text);
0130     });
0131 }
0132 
0133 KLineEdit::KLineEdit(const QString &string, QWidget *parent)
0134     : QLineEdit(string, parent)
0135     , d_ptr(new KLineEditPrivate(this))
0136 {
0137     Q_D(KLineEdit);
0138     d->init();
0139 }
0140 
0141 KLineEdit::KLineEdit(QWidget *parent)
0142     : QLineEdit(parent)
0143     , d_ptr(new KLineEditPrivate(this))
0144 {
0145     Q_D(KLineEdit);
0146     d->init();
0147 }
0148 
0149 KLineEdit::~KLineEdit()
0150 {
0151 }
0152 
0153 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 0)
0154 QString KLineEdit::clickMessage() const
0155 {
0156     return placeholderText();
0157 }
0158 #endif
0159 
0160 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 46)
0161 void KLineEdit::setClearButtonShown(bool show)
0162 {
0163     setClearButtonEnabled(show);
0164 }
0165 #endif
0166 
0167 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 46)
0168 bool KLineEdit::isClearButtonShown() const
0169 {
0170     return isClearButtonEnabled();
0171 }
0172 #endif
0173 
0174 QSize KLineEdit::clearButtonUsedSize() const
0175 {
0176     QSize s;
0177 
0178     if (isClearButtonEnabled()) {
0179         // from qlineedit_p.cpp
0180 
0181         const int iconSize = height() < 34 ? 16 : 32;
0182         const int buttonWidth = iconSize + 6;
0183         const int buttonHeight = iconSize + 2;
0184 
0185         s = QSize(buttonWidth, buttonHeight);
0186     }
0187 
0188     return s;
0189 }
0190 
0191 void KLineEdit::setCompletionMode(KCompletion::CompletionMode mode)
0192 {
0193     Q_D(KLineEdit);
0194     KCompletion::CompletionMode oldMode = completionMode();
0195 
0196     if (oldMode != mode //
0197         && (oldMode == KCompletion::CompletionPopup || oldMode == KCompletion::CompletionPopupAuto) //
0198         && d->completionBox && d->completionBox->isVisible()) {
0199         d->completionBox->hide();
0200     }
0201 
0202     // If the widgets echo mode is not Normal, no completion
0203     // feature will be enabled even if one is requested.
0204     if (echoMode() != QLineEdit::Normal) {
0205         mode = KCompletion::CompletionNone; // Override the request.
0206     }
0207 
0208     if (!KAuthorized::authorize(QStringLiteral("lineedit_text_completion"))) {
0209         mode = KCompletion::CompletionNone;
0210     }
0211 
0212     if (mode == KCompletion::CompletionPopupAuto || mode == KCompletion::CompletionAuto || mode == KCompletion::CompletionMan) {
0213         d->autoSuggest = true;
0214     } else {
0215         d->autoSuggest = false;
0216     }
0217 
0218     KCompletionBase::setCompletionMode(mode);
0219 }
0220 
0221 void KLineEdit::setCompletionModeDisabled(KCompletion::CompletionMode mode, bool disable)
0222 {
0223     Q_D(KLineEdit);
0224     d->disableCompletionMap[mode] = disable;
0225 }
0226 
0227 void KLineEdit::setCompletedText(const QString &t, bool marked)
0228 {
0229     Q_D(KLineEdit);
0230     if (!d->autoSuggest) {
0231         return;
0232     }
0233 
0234     const QString txt = text();
0235 
0236     if (t != txt) {
0237         setText(t);
0238         if (marked) {
0239             setSelection(t.length(), txt.length() - t.length());
0240         }
0241         setUserSelection(false);
0242     } else {
0243         setUserSelection(true);
0244     }
0245 }
0246 
0247 void KLineEdit::setCompletedText(const QString &text)
0248 {
0249     KCompletion::CompletionMode mode = completionMode();
0250     const bool marked = (mode == KCompletion::CompletionAuto //
0251                          || mode == KCompletion::CompletionMan //
0252                          || mode == KCompletion::CompletionPopup //
0253                          || mode == KCompletion::CompletionPopupAuto);
0254     setCompletedText(text, marked);
0255 }
0256 
0257 void KLineEdit::rotateText(KCompletionBase::KeyBindingType type)
0258 {
0259     KCompletion *comp = compObj();
0260     if (comp && //
0261         (type == KCompletionBase::PrevCompletionMatch //
0262          || type == KCompletionBase::NextCompletionMatch)) {
0263         QString input;
0264 
0265         if (type == KCompletionBase::PrevCompletionMatch) {
0266             input = comp->previousMatch();
0267         } else {
0268             input = comp->nextMatch();
0269         }
0270 
0271         // Skip rotation if previous/next match is null or the same text
0272         if (input.isEmpty() || input == displayText()) {
0273             return;
0274         }
0275         setCompletedText(input, hasSelectedText());
0276     }
0277 }
0278 
0279 void KLineEdit::makeCompletion(const QString &text)
0280 {
0281     Q_D(KLineEdit);
0282     KCompletion *comp = compObj();
0283     KCompletion::CompletionMode mode = completionMode();
0284 
0285     if (!comp || mode == KCompletion::CompletionNone) {
0286         return; // No completion object...
0287     }
0288 
0289     const QString match = comp->makeCompletion(text);
0290 
0291     if (mode == KCompletion::CompletionPopup || mode == KCompletion::CompletionPopupAuto) {
0292         if (match.isEmpty()) {
0293             if (d->completionBox) {
0294                 d->completionBox->hide();
0295                 d->completionBox->clear();
0296             }
0297         } else {
0298             setCompletedItems(comp->allMatches(), comp->shouldAutoSuggest());
0299         }
0300     } else { // Auto,  ShortAuto (Man) and Shell
0301         // all other completion modes
0302         // If no match or the same match, simply return without completing.
0303         if (match.isEmpty() || match == text) {
0304             return;
0305         }
0306 
0307         if (mode != KCompletion::CompletionShell) {
0308             setUserSelection(false);
0309         }
0310 
0311         if (d->autoSuggest) {
0312             setCompletedText(match);
0313         }
0314     }
0315 }
0316 
0317 void KLineEdit::setReadOnly(bool readOnly)
0318 {
0319     Q_D(KLineEdit);
0320     // Do not do anything if nothing changed...
0321     if (readOnly == isReadOnly()) {
0322         return;
0323     }
0324 
0325     QLineEdit::setReadOnly(readOnly);
0326 
0327     if (readOnly) {
0328         d->bgRole = backgroundRole();
0329         setBackgroundRole(QPalette::Window);
0330         if (d->enableSqueezedText && d->squeezedText.isEmpty()) {
0331             d->squeezedText = text();
0332             d->setSqueezedText();
0333         }
0334     } else {
0335         if (!d->squeezedText.isEmpty()) {
0336             setText(d->squeezedText);
0337             d->squeezedText.clear();
0338         }
0339 
0340         setBackgroundRole(d->bgRole);
0341     }
0342 }
0343 
0344 void KLineEdit::setSqueezedText(const QString &text)
0345 {
0346     setSqueezedTextEnabled(true);
0347     setText(text);
0348 }
0349 
0350 void KLineEdit::setSqueezedTextEnabled(bool enable)
0351 {
0352     Q_D(KLineEdit);
0353     d->enableSqueezedText = enable;
0354 }
0355 
0356 bool KLineEdit::isSqueezedTextEnabled() const
0357 {
0358     Q_D(const KLineEdit);
0359     return d->enableSqueezedText;
0360 }
0361 
0362 void KLineEdit::setText(const QString &text)
0363 {
0364     Q_D(KLineEdit);
0365     if (d->enableSqueezedText && isReadOnly()) {
0366         d->squeezedText = text;
0367         d->setSqueezedText();
0368         return;
0369     }
0370 
0371     QLineEdit::setText(text);
0372 }
0373 
0374 void KLineEditPrivate::setSqueezedText()
0375 {
0376     Q_Q(KLineEdit);
0377     squeezedStart = 0;
0378     squeezedEnd = 0;
0379     const QString fullText = squeezedText;
0380     const int fullLength = fullText.length();
0381     const QFontMetrics fm(q->fontMetrics());
0382     const int labelWidth = q->size().width() - 2 * q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) - 2;
0383     const int textWidth = fm.boundingRect(fullText).width();
0384 
0385     // TODO: investigate use of QFontMetrics::elidedText for this
0386     if (textWidth > labelWidth) {
0387         const QStringView sview{fullText};
0388         // TODO: better would be "…" char (0x2026), but for that one would need to ensure it's from the main font,
0389         // otherwise if resulting in use of a new fallback font this can affect the metrics of the complete text,
0390         // resulting in shifted characters
0391         const QString ellipsisText = QStringLiteral("...");
0392         // start with the dots only
0393         QString squeezedText = ellipsisText;
0394         int squeezedWidth = fm.boundingRect(squeezedText).width();
0395 
0396         // estimate how many letters we can add to the dots on both sides
0397         int letters = fullText.length() * (labelWidth - squeezedWidth) / textWidth / 2;
0398         squeezedText = sview.left(letters) + ellipsisText + sview.right(letters);
0399         squeezedWidth = fm.boundingRect(squeezedText).width();
0400 
0401         if (squeezedWidth < labelWidth) {
0402             // we estimated too short
0403             // add letters while text < label
0404             do {
0405                 letters++;
0406                 squeezedText = sview.left(letters) + ellipsisText + sview.right(letters);
0407                 squeezedWidth = fm.boundingRect(squeezedText).width();
0408             } while (squeezedWidth < labelWidth && letters <= fullLength / 2);
0409             letters--;
0410             squeezedText = sview.left(letters) + ellipsisText + sview.right(letters);
0411         } else if (squeezedWidth > labelWidth) {
0412             // we estimated too long
0413             // remove letters while text > label
0414             do {
0415                 letters--;
0416                 squeezedText = sview.left(letters) + ellipsisText + sview.right(letters);
0417                 squeezedWidth = fm.boundingRect(squeezedText).width();
0418             } while (squeezedWidth > labelWidth && letters >= 5);
0419         }
0420 
0421         if (letters < 5) {
0422             // too few letters added -> we give up squeezing
0423             q->QLineEdit::setText(fullText);
0424         } else {
0425             q->QLineEdit::setText(squeezedText);
0426             squeezedStart = letters;
0427             squeezedEnd = fullText.length() - letters;
0428         }
0429 
0430         q->setToolTip(fullText);
0431 
0432     } else {
0433         q->QLineEdit::setText(fullText);
0434 
0435         q->setToolTip(QString());
0436         QToolTip::showText(q->pos(), QString()); // hide
0437     }
0438 
0439     q->setCursorPosition(0);
0440 }
0441 
0442 void KLineEdit::copy() const
0443 {
0444     Q_D(const KLineEdit);
0445     if (!d->copySqueezedText(true)) {
0446         QLineEdit::copy();
0447     }
0448 }
0449 
0450 bool KLineEditPrivate::copySqueezedText(bool copy) const
0451 {
0452     Q_Q(const KLineEdit);
0453     if (!squeezedText.isEmpty() && squeezedStart) {
0454         KLineEdit *that = const_cast<KLineEdit *>(q);
0455         if (!that->hasSelectedText()) {
0456             return false;
0457         }
0458         int start = q->selectionStart();
0459         int end = start + q->selectedText().length();
0460         if (start >= squeezedStart + 3) {
0461             start = start - 3 - squeezedStart + squeezedEnd;
0462         } else if (start > squeezedStart) {
0463             start = squeezedStart;
0464         }
0465         if (end >= squeezedStart + 3) {
0466             end = end - 3 - squeezedStart + squeezedEnd;
0467         } else if (end > squeezedStart) {
0468             end = squeezedEnd;
0469         }
0470         if (start == end) {
0471             return false;
0472         }
0473         QString t = squeezedText;
0474         t = t.mid(start, end - start);
0475         QApplication::clipboard()->setText(t, copy ? QClipboard::Clipboard : QClipboard::Selection);
0476         return true;
0477     }
0478     return false;
0479 }
0480 
0481 void KLineEdit::resizeEvent(QResizeEvent *ev)
0482 {
0483     Q_D(KLineEdit);
0484     if (!d->squeezedText.isEmpty()) {
0485         d->setSqueezedText();
0486     }
0487 
0488     QLineEdit::resizeEvent(ev);
0489 }
0490 
0491 void KLineEdit::keyPressEvent(QKeyEvent *e)
0492 {
0493     Q_D(KLineEdit);
0494     const int key = e->key() | e->modifiers();
0495 
0496     if (KStandardShortcut::copy().contains(key)) {
0497         copy();
0498         return;
0499     } else if (KStandardShortcut::paste().contains(key)) {
0500         // TODO:
0501         // we should restore the original text (not autocompleted), otherwise the paste
0502         // will get into troubles Bug: 134691
0503         if (!isReadOnly()) {
0504             paste();
0505         }
0506         return;
0507     } else if (KStandardShortcut::pasteSelection().contains(key)) {
0508         QString text = QApplication::clipboard()->text(QClipboard::Selection);
0509         insert(text);
0510         deselect();
0511         return;
0512     } else if (KStandardShortcut::cut().contains(key)) {
0513         if (!isReadOnly()) {
0514             cut();
0515         }
0516         return;
0517     } else if (KStandardShortcut::undo().contains(key)) {
0518         if (!isReadOnly()) {
0519             undo();
0520         }
0521         return;
0522     } else if (KStandardShortcut::redo().contains(key)) {
0523         if (!isReadOnly()) {
0524             redo();
0525         }
0526         return;
0527     } else if (KStandardShortcut::deleteWordBack().contains(key)) {
0528         cursorWordBackward(true);
0529         if (hasSelectedText() && !isReadOnly()) {
0530             del();
0531         }
0532 
0533         e->accept();
0534         return;
0535     } else if (KStandardShortcut::deleteWordForward().contains(key)) {
0536         // Workaround for QT bug where
0537         cursorWordForward(true);
0538         if (hasSelectedText() && !isReadOnly()) {
0539             del();
0540         }
0541 
0542         e->accept();
0543         return;
0544     } else if (KStandardShortcut::backwardWord().contains(key)) {
0545         cursorWordBackward(false);
0546         e->accept();
0547         return;
0548     } else if (KStandardShortcut::forwardWord().contains(key)) {
0549         cursorWordForward(false);
0550         e->accept();
0551         return;
0552     } else if (KStandardShortcut::beginningOfLine().contains(key)) {
0553         home(false);
0554         e->accept();
0555         return;
0556     } else if (KStandardShortcut::endOfLine().contains(key)) {
0557         end(false);
0558         e->accept();
0559         return;
0560     }
0561 
0562     // Filter key-events if EchoMode is normal and
0563     // completion mode is not set to CompletionNone
0564     if (echoMode() == QLineEdit::Normal && completionMode() != KCompletion::CompletionNone) {
0565         if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
0566             const bool trap = (d->completionBox && d->completionBox->isVisible());
0567             const bool stopEvent = (trap
0568                                     || (d->trapReturnKeyEvents //
0569                                         && (e->modifiers() == Qt::NoButton || //
0570                                             e->modifiers() == Qt::KeypadModifier)));
0571 
0572             if (stopEvent) {
0573                 Q_EMIT QLineEdit::returnPressed();
0574                 e->accept();
0575             }
0576 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
0577             Q_EMIT returnPressed(displayText());
0578 #endif
0579             Q_EMIT returnKeyPressed(displayText());
0580             if (trap) {
0581                 d->completionBox->hide();
0582                 deselect();
0583                 setCursorPosition(text().length());
0584             }
0585 
0586             // Eat the event if the user asked for it, or if a completionbox was visible
0587             if (stopEvent) {
0588                 return;
0589             }
0590         }
0591 
0592         const KeyBindingMap keys = keyBindingMap();
0593         const KCompletion::CompletionMode mode = completionMode();
0594         const bool noModifier = (e->modifiers() == Qt::NoButton //
0595                                  || e->modifiers() == Qt::ShiftModifier //
0596                                  || e->modifiers() == Qt::KeypadModifier);
0597 
0598         if ((mode == KCompletion::CompletionAuto //
0599              || mode == KCompletion::CompletionPopupAuto //
0600              || mode == KCompletion::CompletionMan) //
0601             && noModifier) {
0602             if (!d->userSelection && hasSelectedText() //
0603                 && (e->key() == Qt::Key_Right || e->key() == Qt::Key_Left) //
0604                 && e->modifiers() == Qt::NoButton) {
0605                 const QString old_txt = text();
0606                 d->disableRestoreSelection = true;
0607                 const int start = selectionStart();
0608 
0609                 deselect();
0610                 QLineEdit::keyPressEvent(e);
0611                 const int cPosition = cursorPosition();
0612                 setText(old_txt);
0613 
0614                 // keep cursor at cPosition
0615                 setSelection(old_txt.length(), cPosition - old_txt.length());
0616                 if (e->key() == Qt::Key_Right && cPosition > start) {
0617                     // the user explicitly accepted the autocompletion
0618                     d->updateUserText(text());
0619                 }
0620 
0621                 d->disableRestoreSelection = false;
0622                 return;
0623             }
0624 
0625             if (e->key() == Qt::Key_Escape) {
0626                 if (hasSelectedText() && !d->userSelection) {
0627                     del();
0628                     setUserSelection(true);
0629                 }
0630 
0631                 // Don't swallow the Escape press event for the case
0632                 // of dialogs, which have Escape associated to Cancel
0633                 e->ignore();
0634                 return;
0635             }
0636         }
0637 
0638         if ((mode == KCompletion::CompletionAuto //
0639              || mode == KCompletion::CompletionMan)
0640             && noModifier) {
0641             const QString keycode = e->text();
0642             if (!keycode.isEmpty()
0643                 && (keycode.unicode()->isPrint() //
0644                     || e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete)) {
0645                 const bool hasUserSelection = d->userSelection;
0646                 const bool hadSelection = hasSelectedText();
0647 
0648                 bool cursorNotAtEnd = false;
0649 
0650                 const int start = selectionStart();
0651                 const int cPos = cursorPosition();
0652 
0653                 // When moving the cursor, we want to keep the autocompletion as an
0654                 // autocompletion, so we want to process events at the cursor position
0655                 // as if there was no selection. After processing the key event, we
0656                 // can set the new autocompletion again.
0657                 if (hadSelection && !hasUserSelection && start > cPos) {
0658                     del();
0659                     setCursorPosition(cPos);
0660                     cursorNotAtEnd = true;
0661                 }
0662 
0663                 d->disableRestoreSelection = true;
0664                 QLineEdit::keyPressEvent(e);
0665                 d->disableRestoreSelection = false;
0666 
0667                 QString txt = text();
0668                 int len = txt.length();
0669                 if (!hasSelectedText() && len /*&& cursorPosition() == len */) {
0670                     if (e->key() == Qt::Key_Backspace) {
0671                         if (hadSelection && !hasUserSelection && !cursorNotAtEnd) {
0672                             backspace();
0673                             txt = text();
0674                             len = txt.length();
0675                         }
0676 
0677                         if (!d->s_backspacePerformsCompletion || !len) {
0678                             d->autoSuggest = false;
0679                         }
0680                     }
0681 
0682                     if (e->key() == Qt::Key_Delete) {
0683                         d->autoSuggest = false;
0684                     }
0685 
0686                     doCompletion(txt);
0687 
0688                     if ((e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete)) {
0689                         d->autoSuggest = true;
0690                     }
0691 
0692                     e->accept();
0693                 }
0694 
0695                 return;
0696             }
0697 
0698         } else if ((mode == KCompletion::CompletionPopup || mode == KCompletion::CompletionPopupAuto) //
0699                    && noModifier && !e->text().isEmpty()) {
0700             const QString old_txt = text();
0701             const bool hasUserSelection = d->userSelection;
0702             const bool hadSelection = hasSelectedText();
0703             bool cursorNotAtEnd = false;
0704 
0705             const int start = selectionStart();
0706             const int cPos = cursorPosition();
0707             const QString keycode = e->text();
0708 
0709             // When moving the cursor, we want to keep the autocompletion as an
0710             // autocompletion, so we want to process events at the cursor position
0711             // as if there was no selection. After processing the key event, we
0712             // can set the new autocompletion again.
0713             if (hadSelection && !hasUserSelection && start > cPos
0714                 && ((!keycode.isEmpty() && keycode.unicode()->isPrint()) //
0715                     || e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete)) {
0716                 del();
0717                 setCursorPosition(cPos);
0718                 cursorNotAtEnd = true;
0719             }
0720 
0721             const int selectedLength = selectedText().length();
0722 
0723             d->disableRestoreSelection = true;
0724             QLineEdit::keyPressEvent(e);
0725             d->disableRestoreSelection = false;
0726 
0727             if ((selectedLength != selectedText().length()) && !hasUserSelection) {
0728                 d->_k_restoreSelectionColors(); // and set userSelection to true
0729             }
0730 
0731             QString txt = text();
0732             int len = txt.length();
0733             if ((txt != old_txt || txt != e->text()) && len /* && ( cursorPosition() == len || force )*/
0734                 && ((!keycode.isEmpty() && keycode.unicode()->isPrint()) //
0735                     || e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete)) {
0736                 if (e->key() == Qt::Key_Backspace) {
0737                     if (hadSelection && !hasUserSelection && !cursorNotAtEnd) {
0738                         backspace();
0739                         txt = text();
0740                         len = txt.length();
0741                     }
0742 
0743                     if (!d->s_backspacePerformsCompletion) {
0744                         d->autoSuggest = false;
0745                     }
0746                 }
0747 
0748                 if (e->key() == Qt::Key_Delete) {
0749                     d->autoSuggest = false;
0750                 }
0751 
0752                 if (d->completionBox) {
0753                     d->completionBox->setCancelledText(txt);
0754                 }
0755 
0756                 doCompletion(txt);
0757 
0758                 if ((e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete) //
0759                     && mode == KCompletion::CompletionPopupAuto) {
0760                     d->autoSuggest = true;
0761                 }
0762 
0763                 e->accept();
0764             } else if (!len && d->completionBox && d->completionBox->isVisible()) {
0765                 d->completionBox->hide();
0766             }
0767 
0768             return;
0769         } else if (mode == KCompletion::CompletionShell) {
0770             // Handles completion.
0771             QList<QKeySequence> cut;
0772             if (keys[TextCompletion].isEmpty()) {
0773                 cut = KStandardShortcut::shortcut(KStandardShortcut::TextCompletion);
0774             } else {
0775                 cut = keys[TextCompletion];
0776             }
0777 
0778             if (cut.contains(key)) {
0779                 // Emit completion if the completion mode is CompletionShell
0780                 // and the cursor is at the end of the string.
0781                 const QString txt = text();
0782                 const int len = txt.length();
0783                 if (cursorPosition() == len && len != 0) {
0784                     doCompletion(txt);
0785                     return;
0786                 }
0787             } else if (d->completionBox) {
0788                 d->completionBox->hide();
0789             }
0790         }
0791 
0792         // handle rotation
0793         // Handles previous match
0794         QList<QKeySequence> cut;
0795         if (keys[PrevCompletionMatch].isEmpty()) {
0796             cut = KStandardShortcut::shortcut(KStandardShortcut::PrevCompletion);
0797         } else {
0798             cut = keys[PrevCompletionMatch];
0799         }
0800 
0801         if (cut.contains(key)) {
0802             if (emitSignals()) {
0803                 Q_EMIT textRotation(KCompletionBase::PrevCompletionMatch);
0804             }
0805             if (handleSignals()) {
0806                 rotateText(KCompletionBase::PrevCompletionMatch);
0807             }
0808             return;
0809         }
0810 
0811         // Handles next match
0812         if (keys[NextCompletionMatch].isEmpty()) {
0813             cut = KStandardShortcut::shortcut(KStandardShortcut::NextCompletion);
0814         } else {
0815             cut = keys[NextCompletionMatch];
0816         }
0817 
0818         if (cut.contains(key)) {
0819             if (emitSignals()) {
0820                 Q_EMIT textRotation(KCompletionBase::NextCompletionMatch);
0821             }
0822             if (handleSignals()) {
0823                 rotateText(KCompletionBase::NextCompletionMatch);
0824             }
0825             return;
0826         }
0827 
0828         // substring completion
0829         if (compObj()) {
0830             QList<QKeySequence> cut;
0831             if (keys[SubstringCompletion].isEmpty()) {
0832                 cut = KStandardShortcut::shortcut(KStandardShortcut::SubstringCompletion);
0833             } else {
0834                 cut = keys[SubstringCompletion];
0835             }
0836 
0837             if (cut.contains(key)) {
0838                 if (emitSignals()) {
0839                     Q_EMIT substringCompletion(text());
0840                 }
0841                 if (handleSignals()) {
0842                     setCompletedItems(compObj()->substringCompletion(text()));
0843                     e->accept();
0844                 }
0845                 return;
0846             }
0847         }
0848     }
0849     const int selectedLength = selectedText().length();
0850 
0851     // Let QLineEdit handle any other keys events.
0852     QLineEdit::keyPressEvent(e);
0853 
0854     if (selectedLength != selectedText().length()) {
0855         d->_k_restoreSelectionColors(); // and set userSelection to true
0856     }
0857 }
0858 
0859 void KLineEdit::mouseDoubleClickEvent(QMouseEvent *e)
0860 {
0861     Q_D(KLineEdit);
0862     if (e->button() == Qt::LeftButton) {
0863         d->possibleTripleClick = true;
0864         QTimer::singleShot(QApplication::doubleClickInterval(), this, [d]() {
0865             d->_k_tripleClickTimeout();
0866         });
0867     }
0868     QLineEdit::mouseDoubleClickEvent(e);
0869 }
0870 
0871 void KLineEdit::mousePressEvent(QMouseEvent *e)
0872 {
0873     Q_D(KLineEdit);
0874     if (e->button() == Qt::LeftButton && d->possibleTripleClick) {
0875         selectAll();
0876         e->accept();
0877         return;
0878     }
0879 
0880     // if middle clicking and if text is present in the clipboard then clear the selection
0881     // to prepare paste operation
0882     if (e->button() == Qt::MiddleButton) {
0883         if (hasSelectedText() && !isReadOnly()) {
0884             if (QApplication::clipboard()->text(QClipboard::Selection).length() > 0) {
0885                 backspace();
0886             }
0887         }
0888     }
0889 
0890     QLineEdit::mousePressEvent(e);
0891 }
0892 
0893 void KLineEdit::mouseReleaseEvent(QMouseEvent *e)
0894 {
0895     Q_D(KLineEdit);
0896     QLineEdit::mouseReleaseEvent(e);
0897 
0898     if (QApplication::clipboard()->supportsSelection()) {
0899         if (e->button() == Qt::LeftButton) {
0900             // Fix copying of squeezed text if needed
0901             d->copySqueezedText(false);
0902         }
0903     }
0904 }
0905 
0906 void KLineEditPrivate::_k_tripleClickTimeout()
0907 {
0908     possibleTripleClick = false;
0909 }
0910 
0911 QMenu *KLineEdit::createStandardContextMenu()
0912 {
0913     Q_D(KLineEdit);
0914     QMenu *popup = QLineEdit::createStandardContextMenu();
0915 
0916     if (!isReadOnly()) {
0917         // FIXME: This code depends on Qt's action ordering.
0918         const QList<QAction *> actionList = popup->actions();
0919         enum {
0920             UndoAct,
0921             RedoAct,
0922             Separator1,
0923             CutAct,
0924             CopyAct,
0925             PasteAct,
0926             DeleteAct,
0927             ClearAct,
0928             Separator2,
0929             SelectAllAct,
0930             NCountActs,
0931         };
0932         QAction *separatorAction = nullptr;
0933         // separator we want is right after Delete right now.
0934         const int idx = actionList.indexOf(actionList[DeleteAct]) + 1;
0935         if (idx < actionList.count()) {
0936             separatorAction = actionList.at(idx);
0937         }
0938         if (separatorAction) {
0939             QAction *clearAllAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-clear")), tr("C&lear", "@action:inmenu"), this);
0940             clearAllAction->setShortcuts(QKeySequence::keyBindings(QKeySequence::DeleteCompleteLine));
0941             connect(clearAllAction, &QAction::triggered, this, &QLineEdit::clear);
0942             if (text().isEmpty()) {
0943                 clearAllAction->setEnabled(false);
0944             }
0945             popup->insertAction(separatorAction, clearAllAction);
0946         }
0947     }
0948 
0949     // If a completion object is present and the input
0950     // widget is not read-only, show the Text Completion
0951     // menu item.
0952     if (compObj() && !isReadOnly() && KAuthorized::authorize(QStringLiteral("lineedit_text_completion"))) {
0953         QMenu *subMenu = popup->addMenu(QIcon::fromTheme(QStringLiteral("text-completion")), tr("Text Completion", "@title:menu"));
0954         connect(subMenu, &QMenu::triggered, this, [d](QAction *action) {
0955             d->_k_completionMenuActivated(action);
0956         });
0957 
0958         popup->addSeparator();
0959 
0960         QActionGroup *ag = new QActionGroup(this);
0961         d->noCompletionAction = ag->addAction(tr("None", "@item:inmenu Text Completion"));
0962         d->shellCompletionAction = ag->addAction(tr("Manual", "@item:inmenu Text Completion"));
0963         d->autoCompletionAction = ag->addAction(tr("Automatic", "@item:inmenu Text Completion"));
0964         d->popupCompletionAction = ag->addAction(tr("Dropdown List", "@item:inmenu Text Completion"));
0965         d->shortAutoCompletionAction = ag->addAction(tr("Short Automatic", "@item:inmenu Text Completion"));
0966         d->popupAutoCompletionAction = ag->addAction(tr("Dropdown List && Automatic", "@item:inmenu Text Completion"));
0967         subMenu->addActions(ag->actions());
0968 
0969         // subMenu->setAccel( KStandardShortcut::completion(), ShellCompletion );
0970 
0971         d->shellCompletionAction->setCheckable(true);
0972         d->noCompletionAction->setCheckable(true);
0973         d->popupCompletionAction->setCheckable(true);
0974         d->autoCompletionAction->setCheckable(true);
0975         d->shortAutoCompletionAction->setCheckable(true);
0976         d->popupAutoCompletionAction->setCheckable(true);
0977 
0978         d->shellCompletionAction->setEnabled(!d->disableCompletionMap[KCompletion::CompletionShell]);
0979         d->noCompletionAction->setEnabled(!d->disableCompletionMap[KCompletion::CompletionNone]);
0980         d->popupCompletionAction->setEnabled(!d->disableCompletionMap[KCompletion::CompletionPopup]);
0981         d->autoCompletionAction->setEnabled(!d->disableCompletionMap[KCompletion::CompletionAuto]);
0982         d->shortAutoCompletionAction->setEnabled(!d->disableCompletionMap[KCompletion::CompletionMan]);
0983         d->popupAutoCompletionAction->setEnabled(!d->disableCompletionMap[KCompletion::CompletionPopupAuto]);
0984 
0985         const KCompletion::CompletionMode mode = completionMode();
0986         d->noCompletionAction->setChecked(mode == KCompletion::CompletionNone);
0987         d->shellCompletionAction->setChecked(mode == KCompletion::CompletionShell);
0988         d->popupCompletionAction->setChecked(mode == KCompletion::CompletionPopup);
0989         d->autoCompletionAction->setChecked(mode == KCompletion::CompletionAuto);
0990         d->shortAutoCompletionAction->setChecked(mode == KCompletion::CompletionMan);
0991         d->popupAutoCompletionAction->setChecked(mode == KCompletion::CompletionPopupAuto);
0992 
0993         const KCompletion::CompletionMode defaultMode = KCompletion::CompletionPopup;
0994         if (mode != defaultMode && !d->disableCompletionMap[defaultMode]) {
0995             subMenu->addSeparator();
0996             d->defaultAction = subMenu->addAction(tr("Default", "@item:inmenu Text Completion"));
0997         }
0998     }
0999 
1000     return popup;
1001 }
1002 
1003 void KLineEdit::contextMenuEvent(QContextMenuEvent *e)
1004 {
1005     if (QLineEdit::contextMenuPolicy() != Qt::DefaultContextMenu) {
1006         return;
1007     }
1008     QMenu *popup = createStandardContextMenu();
1009 
1010     // ### do we really need this?  Yes, Please do not remove!  This
1011     // allows applications to extend the popup menu without having to
1012     // inherit from this class! (DA)
1013     Q_EMIT aboutToShowContextMenu(popup);
1014 
1015     popup->exec(e->globalPos());
1016     delete popup;
1017 }
1018 
1019 void KLineEditPrivate::_k_completionMenuActivated(QAction *act)
1020 {
1021     Q_Q(KLineEdit);
1022     KCompletion::CompletionMode oldMode = q->completionMode();
1023 
1024     if (act == noCompletionAction) {
1025         q->setCompletionMode(KCompletion::CompletionNone);
1026     } else if (act == shellCompletionAction) {
1027         q->setCompletionMode(KCompletion::CompletionShell);
1028     } else if (act == autoCompletionAction) {
1029         q->setCompletionMode(KCompletion::CompletionAuto);
1030     } else if (act == popupCompletionAction) {
1031         q->setCompletionMode(KCompletion::CompletionPopup);
1032     } else if (act == shortAutoCompletionAction) {
1033         q->setCompletionMode(KCompletion::CompletionMan);
1034     } else if (act == popupAutoCompletionAction) {
1035         q->setCompletionMode(KCompletion::CompletionPopupAuto);
1036     } else if (act == defaultAction) {
1037         q->setCompletionMode(KCompletion::CompletionPopup);
1038     } else {
1039         return;
1040     }
1041 
1042     if (oldMode != q->completionMode()) {
1043         if ((oldMode == KCompletion::CompletionPopup || oldMode == KCompletion::CompletionPopupAuto) //
1044             && completionBox //
1045             && completionBox->isVisible()) {
1046             completionBox->hide();
1047         }
1048         Q_EMIT q->completionModeChanged(q->completionMode());
1049     }
1050 }
1051 
1052 bool KLineEdit::event(QEvent *ev)
1053 {
1054     Q_D(KLineEdit);
1055     KCursor::autoHideEventFilter(this, ev);
1056     if (ev->type() == QEvent::ShortcutOverride) {
1057         QKeyEvent *e = static_cast<QKeyEvent *>(ev);
1058         if (d->overrideShortcut(e)) {
1059             ev->accept();
1060         }
1061     } else if (ev->type() == QEvent::ApplicationPaletteChange || ev->type() == QEvent::PaletteChange) {
1062         // Assume the widget uses the application's palette
1063         QPalette p = QApplication::palette();
1064         d->previousHighlightedTextColor = p.color(QPalette::Normal, QPalette::HighlightedText);
1065         d->previousHighlightColor = p.color(QPalette::Normal, QPalette::Highlight);
1066         setUserSelection(d->userSelection);
1067     } else if (ev->type() == QEvent::ChildAdded) {
1068         QObject *obj = static_cast<QChildEvent *>(ev)->child();
1069         if (obj) {
1070             connect(obj, &QObject::objectNameChanged, this, [this, obj] {
1071                 if (obj->objectName() == QLatin1String("_q_qlineeditclearaction")) {
1072                     QAction *action = qobject_cast<QAction *>(obj);
1073                     connect(action, &QAction::triggered, this, &KLineEdit::clearButtonClicked);
1074                 }
1075             });
1076         }
1077     }
1078 
1079     return QLineEdit::event(ev);
1080 }
1081 
1082 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 0)
1083 void KLineEdit::setUrlDropsEnabled(bool enable)
1084 {
1085     Q_D(KLineEdit);
1086     if (enable && !d->handleURLDrops) {
1087         installEventFilter(d->urlDropEventFilter);
1088         d->handleURLDrops = true;
1089     } else if (!enable && d->handleURLDrops) {
1090         removeEventFilter(d->urlDropEventFilter);
1091         d->handleURLDrops = false;
1092     }
1093 }
1094 #endif
1095 
1096 bool KLineEdit::urlDropsEnabled() const
1097 {
1098     Q_D(const KLineEdit);
1099     return d->handleURLDrops;
1100 }
1101 
1102 void KLineEdit::setTrapReturnKey(bool trap)
1103 {
1104     Q_D(KLineEdit);
1105     d->trapReturnKeyEvents = trap;
1106 }
1107 
1108 bool KLineEdit::trapReturnKey() const
1109 {
1110     Q_D(const KLineEdit);
1111     return d->trapReturnKeyEvents;
1112 }
1113 
1114 void KLineEdit::setUrl(const QUrl &url)
1115 {
1116     setText(url.toDisplayString());
1117 }
1118 
1119 void KLineEdit::setCompletionBox(KCompletionBox *box)
1120 {
1121     Q_D(KLineEdit);
1122     if (d->completionBox) {
1123         return;
1124     }
1125 
1126     d->completionBox = box;
1127     if (handleSignals()) {
1128         connect(d->completionBox, &KCompletionBox::currentTextChanged, this, [d](const QString &text) {
1129             d->_k_completionBoxTextChanged(text);
1130         });
1131 
1132         connect(d->completionBox, &KCompletionBox::userCancelled, this, &KLineEdit::userCancelled);
1133 
1134 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
1135         connect(d->completionBox, qOverload<const QString &>(&KCompletionBox::activated), this, &KLineEdit::completionBoxActivated);
1136         connect(d->completionBox, qOverload<const QString &>(&KCompletionBox::activated), this, &KLineEdit::textEdited);
1137 #else
1138         connect(d->completionBox, &KCompletionBox::textActivated, this, &KLineEdit::completionBoxActivated);
1139         connect(d->completionBox, &KCompletionBox::textActivated, this, &KLineEdit::textEdited);
1140 #endif
1141     }
1142 }
1143 
1144 /*
1145  * Set the line edit text without changing the modified flag. By default
1146  * calling setText resets the modified flag to false.
1147  */
1148 static void setEditText(KLineEdit *edit, const QString &text)
1149 {
1150     if (!edit) {
1151         return;
1152     }
1153 
1154     const bool wasModified = edit->isModified();
1155     edit->setText(text);
1156     edit->setModified(wasModified);
1157 }
1158 
1159 void KLineEdit::userCancelled(const QString &cancelText)
1160 {
1161     Q_D(KLineEdit);
1162     if (completionMode() != KCompletion::CompletionPopupAuto) {
1163         setEditText(this, cancelText);
1164     } else if (hasSelectedText()) {
1165         if (d->userSelection) {
1166             deselect();
1167         } else {
1168             d->autoSuggest = false;
1169             const int start = selectionStart();
1170             const QString s = text().remove(selectionStart(), selectedText().length());
1171             setEditText(this, s);
1172             setCursorPosition(start);
1173             d->autoSuggest = true;
1174         }
1175     }
1176 }
1177 
1178 bool KLineEditPrivate::overrideShortcut(const QKeyEvent *e)
1179 {
1180     Q_Q(KLineEdit);
1181     QList<QKeySequence> scKey;
1182 
1183     const int key = e->key() | e->modifiers();
1184     const KLineEdit::KeyBindingMap keys = q->keyBindingMap();
1185 
1186     if (keys[KLineEdit::TextCompletion].isEmpty()) {
1187         scKey = KStandardShortcut::shortcut(KStandardShortcut::TextCompletion);
1188     } else {
1189         scKey = keys[KLineEdit::TextCompletion];
1190     }
1191 
1192     if (scKey.contains(key)) {
1193         return true;
1194     }
1195 
1196     if (keys[KLineEdit::NextCompletionMatch].isEmpty()) {
1197         scKey = KStandardShortcut::shortcut(KStandardShortcut::NextCompletion);
1198     } else {
1199         scKey = keys[KLineEdit::NextCompletionMatch];
1200     }
1201 
1202     if (scKey.contains(key)) {
1203         return true;
1204     }
1205 
1206     if (keys[KLineEdit::PrevCompletionMatch].isEmpty()) {
1207         scKey = KStandardShortcut::shortcut(KStandardShortcut::PrevCompletion);
1208     } else {
1209         scKey = keys[KLineEdit::PrevCompletionMatch];
1210     }
1211 
1212     if (scKey.contains(key)) {
1213         return true;
1214     }
1215 
1216 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
1217     constexpr int ctrlE = QKeyCombination(Qt::CTRL | Qt::Key_E).toCombined();
1218     constexpr int ctrlU = QKeyCombination(Qt::CTRL | Qt::Key_U).toCombined();
1219 #else
1220     constexpr int ctrlE = Qt::CTRL | Qt::Key_E;
1221     constexpr int ctrlU = Qt::CTRL | Qt::Key_U;
1222 #endif
1223 
1224     // Override all the text manupilation accelerators...
1225     if (KStandardShortcut::copy().contains(key)) {
1226         return true;
1227     } else if (KStandardShortcut::paste().contains(key)) {
1228         return true;
1229     } else if (KStandardShortcut::cut().contains(key)) {
1230         return true;
1231     } else if (KStandardShortcut::undo().contains(key)) {
1232         return true;
1233     } else if (KStandardShortcut::redo().contains(key)) {
1234         return true;
1235     } else if (KStandardShortcut::deleteWordBack().contains(key)) {
1236         return true;
1237     } else if (KStandardShortcut::deleteWordForward().contains(key)) {
1238         return true;
1239     } else if (KStandardShortcut::forwardWord().contains(key)) {
1240         return true;
1241     } else if (KStandardShortcut::backwardWord().contains(key)) {
1242         return true;
1243     } else if (KStandardShortcut::beginningOfLine().contains(key)) {
1244         return true;
1245     } else if (KStandardShortcut::endOfLine().contains(key)) {
1246         return true;
1247     }
1248 
1249     // Shortcut overrides for shortcuts that QLineEdit handles
1250     // but doesn't dare force as "stronger than kaction shortcuts"...
1251     else if (e->matches(QKeySequence::SelectAll)) {
1252         return true;
1253     } else if (qApp->platformName() == QLatin1String("xcb") && (key == ctrlE || key == ctrlU)) {
1254         return true;
1255     }
1256 
1257     if (completionBox && completionBox->isVisible()) {
1258         const int key = e->key();
1259         const Qt::KeyboardModifiers modifiers = e->modifiers();
1260         if ((key == Qt::Key_Backtab || key == Qt::Key_Tab) //
1261             && (modifiers == Qt::NoModifier || (modifiers & Qt::ShiftModifier))) {
1262             return true;
1263         }
1264     }
1265 
1266     return false;
1267 }
1268 
1269 void KLineEdit::setCompletedItems(const QStringList &items, bool autoSuggest)
1270 {
1271     Q_D(KLineEdit);
1272     QString txt;
1273     if (d->completionBox && d->completionBox->isVisible()) {
1274         // The popup is visible already - do the matching on the initial string,
1275         // not on the currently selected one.
1276         txt = completionBox()->cancelledText();
1277     } else {
1278         txt = text();
1279     }
1280 
1281     if (!items.isEmpty() //
1282         && !(items.count() == 1 && txt == items.first())) {
1283         // create completion box if non-existent
1284         completionBox();
1285 
1286         if (d->completionBox->isVisible()) {
1287             QListWidgetItem *currentItem = d->completionBox->currentItem();
1288 
1289             QString currentSelection;
1290             if (currentItem != nullptr) {
1291                 currentSelection = currentItem->text();
1292             }
1293 
1294             d->completionBox->setItems(items);
1295 
1296             const QList<QListWidgetItem *> matchedItems = d->completionBox->findItems(currentSelection, Qt::MatchExactly);
1297             QListWidgetItem *matchedItem = matchedItems.isEmpty() ? nullptr : matchedItems.first();
1298 
1299             if (matchedItem) {
1300                 const bool blocked = d->completionBox->blockSignals(true);
1301                 d->completionBox->setCurrentItem(matchedItem);
1302                 d->completionBox->blockSignals(blocked);
1303             } else {
1304                 d->completionBox->setCurrentRow(-1);
1305             }
1306         } else { // completion box not visible yet -> show it
1307             if (!txt.isEmpty()) {
1308                 d->completionBox->setCancelledText(txt);
1309             }
1310             d->completionBox->setItems(items);
1311             d->completionBox->popup();
1312         }
1313 
1314         if (d->autoSuggest && autoSuggest) {
1315             const int index = items.first().indexOf(txt);
1316             const QString newText = items.first().mid(index);
1317             setUserSelection(false); // can be removed? setCompletedText sets it anyway
1318             setCompletedText(newText, true);
1319         }
1320     } else {
1321         if (d->completionBox && d->completionBox->isVisible()) {
1322             d->completionBox->hide();
1323         }
1324     }
1325 }
1326 
1327 KCompletionBox *KLineEdit::completionBox(bool create)
1328 {
1329     Q_D(KLineEdit);
1330     if (create && !d->completionBox) {
1331         setCompletionBox(new KCompletionBox(this));
1332         d->completionBox->setObjectName(QStringLiteral("completion box"));
1333         d->completionBox->setFont(font());
1334     }
1335 
1336     return d->completionBox;
1337 }
1338 
1339 void KLineEdit::setCompletionObject(KCompletion *comp, bool handle)
1340 {
1341     Q_D(class KLineEdit);
1342 
1343     KCompletion *oldComp = compObj();
1344     if (oldComp && handleSignals()) {
1345         disconnect(d->m_matchesConnection);
1346     }
1347 
1348     if (comp && handle) {
1349         d->m_matchesConnection = connect(comp, &KCompletion::matches, this, [this](const QStringList &list) {
1350             setCompletedItems(list);
1351         });
1352     }
1353 
1354     KCompletionBase::setCompletionObject(comp, handle);
1355 }
1356 
1357 void KLineEdit::setUserSelection(bool userSelection)
1358 {
1359     Q_D(KLineEdit);
1360     // if !d->userSelection && userSelection we are accepting a completion,
1361     // so trigger an update
1362 
1363     if (!d->userSelection && userSelection) {
1364         d->updateUserText(text());
1365     }
1366 
1367     QPalette p = palette();
1368 
1369     if (userSelection) {
1370         p.setColor(QPalette::Highlight, d->previousHighlightColor);
1371         p.setColor(QPalette::HighlightedText, d->previousHighlightedTextColor);
1372     } else {
1373         QColor color = p.color(QPalette::Disabled, QPalette::Text);
1374         p.setColor(QPalette::HighlightedText, color);
1375         color = p.color(QPalette::Active, QPalette::Base);
1376         p.setColor(QPalette::Highlight, color);
1377     }
1378 
1379     d->userSelection = userSelection;
1380     setPalette(p);
1381 }
1382 
1383 void KLineEditPrivate::_k_restoreSelectionColors()
1384 {
1385     Q_Q(KLineEdit);
1386     if (disableRestoreSelection) {
1387         return;
1388     }
1389 
1390     q->setUserSelection(true);
1391 }
1392 
1393 void KLineEditPrivate::_k_completionBoxTextChanged(const QString &text)
1394 {
1395     Q_Q(KLineEdit);
1396     if (!text.isEmpty()) {
1397         q->setText(text);
1398         q->setModified(true);
1399         q->end(false); // force cursor at end
1400     }
1401 }
1402 
1403 QString KLineEdit::originalText() const
1404 {
1405     Q_D(const KLineEdit);
1406     if (d->enableSqueezedText && isReadOnly()) {
1407         return d->squeezedText;
1408     }
1409 
1410     return text();
1411 }
1412 
1413 QString KLineEdit::userText() const
1414 {
1415     Q_D(const KLineEdit);
1416     return d->userText;
1417 }
1418 
1419 bool KLineEdit::autoSuggest() const
1420 {
1421     Q_D(const KLineEdit);
1422     return d->autoSuggest;
1423 }
1424 
1425 void KLineEdit::paintEvent(QPaintEvent *ev)
1426 {
1427     Q_D(KLineEdit);
1428     if (echoMode() == Password && d->threeStars) {
1429         // ### hack alert!
1430         // QLineEdit has currently no hooks to modify the displayed string.
1431         // When we call setText(), an update() is triggered and we get
1432         // into an infinite recursion.
1433         // Qt offers the setUpdatesEnabled() method, but when we re-enable
1434         // them, update() is triggered, and we get into the same recursion.
1435         // To work around this problem, we set/clear the internal Qt flag which
1436         // marks the updatesDisabled state manually.
1437         setAttribute(Qt::WA_UpdatesDisabled, true);
1438         blockSignals(true);
1439         const QString oldText = text();
1440         const bool isModifiedState = isModified(); // save modified state because setText resets it
1441         setText(oldText + oldText + oldText);
1442         QLineEdit::paintEvent(ev);
1443         setText(oldText);
1444         setModified(isModifiedState);
1445         blockSignals(false);
1446         setAttribute(Qt::WA_UpdatesDisabled, false);
1447     } else {
1448         QLineEdit::paintEvent(ev);
1449     }
1450 }
1451 
1452 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 0)
1453 void KLineEdit::setClickMessage(const QString &msg)
1454 {
1455     setPlaceholderText(msg);
1456 }
1457 #endif
1458 
1459 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(4, 5)
1460 void KLineEdit::setContextMenuEnabled(bool showMenu)
1461 {
1462     QLineEdit::setContextMenuPolicy(showMenu ? Qt::DefaultContextMenu : Qt::NoContextMenu);
1463 }
1464 #endif
1465 
1466 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(4, 5)
1467 bool KLineEdit::isContextMenuEnabled() const
1468 {
1469     return (contextMenuPolicy() == Qt::DefaultContextMenu);
1470 }
1471 #endif
1472 
1473 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 83)
1474 void KLineEdit::setPasswordMode(bool passwordMode)
1475 {
1476     Q_D(KLineEdit);
1477     if (passwordMode) {
1478         KConfigGroup cg(KSharedConfig::openConfig(), "Passwords");
1479         const QString val = cg.readEntry("EchoMode", "OneStar");
1480         if (val == QLatin1String("NoEcho")) {
1481             setEchoMode(NoEcho);
1482         } else {
1483             d->threeStars = (val == QLatin1String("ThreeStars"));
1484             setEchoMode(Password);
1485         }
1486     } else {
1487         setEchoMode(Normal);
1488     }
1489 }
1490 #endif
1491 
1492 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 83)
1493 bool KLineEdit::passwordMode() const
1494 {
1495     return echoMode() == NoEcho || echoMode() == Password;
1496 }
1497 #endif
1498 
1499 void KLineEdit::doCompletion(const QString &text)
1500 {
1501     Q_D(KLineEdit);
1502     if (emitSignals()) {
1503         Q_EMIT completion(text); // emit when requested...
1504     }
1505     d->completionRunning = true;
1506     if (handleSignals()) {
1507         makeCompletion(text); // handle when requested...
1508     }
1509     d->completionRunning = false;
1510 }
1511 
1512 #include "moc_klineedit.cpp"