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