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"