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