File indexing completed on 2024-05-19 05:47:27

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