File indexing completed on 2024-04-28 03:53:10

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