KDEUI
klineedit.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 00003 Copyright (C) 1997 Sven Radej (sven.radej@iname.com) 00004 Copyright (c) 1999 Patrick Ward <PAT_WARD@HP-USA-om5.om.hp.com> 00005 Copyright (c) 1999 Preston Brown <pbrown@kde.org> 00006 00007 Re-designed for KDE 2.x by 00008 Copyright (c) 2000, 2001 Dawit Alemayehu <adawit@kde.org> 00009 Copyright (c) 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org> 00010 00011 This library is free software; you can redistribute it and/or 00012 modify it under the terms of the GNU Lesser General Public 00013 License (LGPL) as published by the Free Software Foundation; 00014 either version 2 of the License, or (at your option) any later 00015 version. 00016 00017 This library is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00020 Lesser General Public License for more details. 00021 00022 You should have received a copy of the GNU Lesser General Public License 00023 along with this library; see the file COPYING.LIB. If not, write to 00024 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00025 Boston, MA 02110-1301, USA. 00026 */ 00027 00028 #include "klineedit.h" 00029 #include "klineedit_p.h" 00030 00031 #include <kaction.h> 00032 #include <kapplication.h> 00033 #include <kauthorized.h> 00034 #include <kconfig.h> 00035 #include <kconfiggroup.h> 00036 #include <kcursor.h> 00037 #include <kdebug.h> 00038 #include <kcompletionbox.h> 00039 #include <kicontheme.h> 00040 #include <kicon.h> 00041 #include <klocale.h> 00042 #include <kmenu.h> 00043 #include <kstandardaction.h> 00044 #include <kstandardshortcut.h> 00045 00046 #include <QtCore/QTimer> 00047 #include <QtGui/QClipboard> 00048 #include <QtGui/QStyleOption> 00049 #include <QtGui/QToolTip> 00050 00051 class KLineEditStyle; 00052 00053 class KLineEditPrivate 00054 { 00055 public: 00056 KLineEditPrivate(KLineEdit* qq) 00057 : q(qq) 00058 { 00059 completionBox = 0L; 00060 handleURLDrops = true; 00061 grabReturnKeyEvents = false; 00062 00063 userSelection = true; 00064 autoSuggest = false; 00065 disableRestoreSelection = false; 00066 enableSqueezedText = false; 00067 00068 drawClickMsg = false; 00069 enableClickMsg = false; 00070 threeStars = false; 00071 completionRunning = false; 00072 if (!s_initialized) { 00073 KConfigGroup config( KGlobal::config(), "General" ); 00074 s_backspacePerformsCompletion = config.readEntry("Backspace performs completion", false); 00075 s_initialized = true; 00076 } 00077 00078 clearButton = 0; 00079 clickInClear = false; 00080 wideEnoughForClear = true; 00081 00082 // i18n: Placeholder text in line edit widgets is the text appearing 00083 // before any user input, briefly explaining to the user what to type 00084 // (e.g. "Enter search pattern"). 00085 // By default the text is set in italic, which may not be appropriate 00086 // for some languages and scripts (e.g. for CJK ideographs). 00087 QString metaMsg = i18nc("Italic placeholder text in line edits: 0 no, 1 yes", "1"); 00088 italicizePlaceholder = (metaMsg.trimmed() != QString('0')); 00089 } 00090 00091 ~KLineEditPrivate() 00092 { 00093 // causes a weird crash in KWord at least, so let Qt delete it for us. 00094 // delete completionBox; 00095 delete style.data(); 00096 } 00097 00098 void _k_slotSettingsChanged(int category) 00099 { 00100 Q_UNUSED(category); 00101 00102 if (clearButton) { 00103 clearButton->setAnimationsEnabled(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects); 00104 } 00105 } 00106 00107 void _k_textChanged(const QString &txt) 00108 { 00109 // COMPAT (as documented): emit userTextChanged whenever textChanged is emitted 00110 // KDE5: remove userTextChanged signal, textEdited does the same... 00111 if (!completionRunning && (txt != userText)) { 00112 userText = txt; 00113 #ifndef KDE_NO_DEPRECATED 00114 emit q->userTextChanged(txt); 00115 #endif 00116 } 00117 } 00118 00119 // Call this when a completion operation changes the lineedit text 00120 // "as if it had been edited by the user". 00121 void _k_updateUserText(const QString &txt) 00122 { 00123 if (!completionRunning && (txt != userText)) { 00124 userText = txt; 00125 q->setModified(true); 00126 #ifndef KDE_NO_DEPRECATED 00127 emit q->userTextChanged(txt); 00128 #endif 00129 emit q->textEdited(txt); 00130 emit q->textChanged(txt); 00131 } 00132 } 00133 00134 // This is called when the lineedit is readonly. 00135 // Either from setReadOnly() itself, or when we realize that 00136 // we became readonly and setReadOnly() wasn't called (because it's not virtual) 00137 // Typical case: comboBox->lineEdit()->setReadOnly(true) 00138 void adjustForReadOnly() 00139 { 00140 if (style && style.data()->m_overlap) { 00141 style.data()->m_overlap = 0; 00142 } 00143 } 00144 00145 00151 bool overrideShortcut(const QKeyEvent* e); 00152 00153 static bool s_initialized; 00154 static bool s_backspacePerformsCompletion; // Configuration option 00155 00156 QColor previousHighlightColor; 00157 QColor previousHighlightedTextColor; 00158 00159 bool userSelection: 1; 00160 bool autoSuggest : 1; 00161 bool disableRestoreSelection: 1; 00162 bool handleURLDrops:1; 00163 bool grabReturnKeyEvents:1; 00164 bool enableSqueezedText:1; 00165 bool completionRunning:1; 00166 00167 int squeezedEnd; 00168 int squeezedStart; 00169 QPalette::ColorRole bgRole; 00170 QString squeezedText; 00171 QString userText; 00172 00173 QString clickMessage; 00174 bool enableClickMsg:1; 00175 bool drawClickMsg:1; 00176 bool threeStars:1; 00177 00178 bool possibleTripleClick :1; // set in mousePressEvent, deleted in tripleClickTimeout 00179 00180 bool clickInClear:1; 00181 bool wideEnoughForClear:1; 00182 KLineEditButton *clearButton; 00183 QWeakPointer<KLineEditStyle> style; 00184 QString lastStyleClass; 00185 00186 KCompletionBox *completionBox; 00187 00188 bool italicizePlaceholder:1; 00189 00190 QAction *noCompletionAction, *shellCompletionAction, *autoCompletionAction, *popupCompletionAction, *shortAutoCompletionAction, *popupAutoCompletionAction, *defaultAction; 00191 00192 QMap<KGlobalSettings::Completion, bool> disableCompletionMap; 00193 KLineEdit* q; 00194 }; 00195 00196 QStyle *KLineEditStyle::style() const 00197 { 00198 if (m_subStyle) { 00199 return m_subStyle.data(); 00200 } 00201 00202 return KdeUiProxyStyle::style(); 00203 } 00204 00205 QRect KLineEditStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const 00206 { 00207 if (element == SE_LineEditContents) { 00208 KLineEditStyle *unconstThis = const_cast<KLineEditStyle *>(this); 00209 00210 if (m_sentinel) { 00211 // we are recursing: we're wrapping a style that wraps us! 00212 unconstThis->m_subStyle.clear(); 00213 } 00214 00215 unconstThis->m_sentinel = true; 00216 QStyle *s = m_subStyle ? m_subStyle.data() : style(); 00217 QRect rect = s->subElementRect(SE_LineEditContents, option, widget); 00218 unconstThis->m_sentinel = false; 00219 00220 if (option->direction == Qt::LeftToRight) { 00221 return rect.adjusted(0, 0, -m_overlap, 0); 00222 } else { 00223 return rect.adjusted(m_overlap, 0, 0, 0); 00224 } 00225 } 00226 00227 return KdeUiProxyStyle::subElementRect(element, option, widget); 00228 } 00229 00230 bool KLineEditPrivate::s_backspacePerformsCompletion = false; 00231 bool KLineEditPrivate::s_initialized = false; 00232 00233 00234 KLineEdit::KLineEdit( const QString &string, QWidget *parent ) 00235 : QLineEdit( string, parent ), d(new KLineEditPrivate(this)) 00236 { 00237 init(); 00238 } 00239 00240 KLineEdit::KLineEdit( QWidget *parent ) 00241 : QLineEdit( parent ), d(new KLineEditPrivate(this)) 00242 { 00243 init(); 00244 } 00245 00246 00247 KLineEdit::~KLineEdit () 00248 { 00249 delete d; 00250 } 00251 00252 void KLineEdit::init() 00253 { 00254 d->possibleTripleClick = false; 00255 d->bgRole = backgroundRole(); 00256 00257 // Enable the context menu by default. 00258 QLineEdit::setContextMenuPolicy( Qt::DefaultContextMenu ); 00259 KCursor::setAutoHideCursor( this, true, true ); 00260 00261 KGlobalSettings::Completion mode = completionMode(); 00262 d->autoSuggest = (mode == KGlobalSettings::CompletionMan || 00263 mode == KGlobalSettings::CompletionPopupAuto || 00264 mode == KGlobalSettings::CompletionAuto); 00265 connect( this, SIGNAL(selectionChanged()), this, SLOT(slotRestoreSelectionColors())); 00266 00267 connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(_k_slotSettingsChanged(int))); 00268 00269 const QPalette p = palette(); 00270 if ( !d->previousHighlightedTextColor.isValid() ) 00271 d->previousHighlightedTextColor=p.color(QPalette::Normal,QPalette::HighlightedText); 00272 if ( !d->previousHighlightColor.isValid() ) 00273 d->previousHighlightColor=p.color(QPalette::Normal,QPalette::Highlight); 00274 00275 d->style = new KLineEditStyle(this); 00276 setStyle(d->style.data()); 00277 00278 connect(this, SIGNAL(textChanged(QString)), this, SLOT(_k_textChanged(QString))); 00279 } 00280 00281 QString KLineEdit::clickMessage() const 00282 { 00283 return d->clickMessage; 00284 } 00285 00286 void KLineEdit::setClearButtonShown(bool show) 00287 { 00288 if (show) { 00289 if (d->clearButton) { 00290 return; 00291 } 00292 00293 d->clearButton = new KLineEditButton(this); 00294 d->clearButton->setObjectName("KLineEditButton"); 00295 d->clearButton->setCursor( Qt::ArrowCursor ); 00296 d->clearButton->setToolTip( i18nc( "@action:button Clear current text in the line edit", "Clear text" ) ); 00297 00298 updateClearButtonIcon(text()); 00299 updateClearButton(); 00300 connect(this, SIGNAL(textChanged(QString)), this, SLOT(updateClearButtonIcon(QString))); 00301 } else { 00302 disconnect(this, SIGNAL(textChanged(QString)), this, SLOT(updateClearButtonIcon(QString))); 00303 delete d->clearButton; 00304 d->clearButton = 0; 00305 d->clickInClear = false; 00306 if (d->style) { 00307 d->style.data()->m_overlap = 0; 00308 } 00309 } 00310 } 00311 00312 bool KLineEdit::isClearButtonShown() const 00313 { 00314 return d->clearButton != 0; 00315 } 00316 00317 QSize KLineEdit::clearButtonUsedSize() const 00318 { 00319 QSize s; 00320 if (d->clearButton) { 00321 const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this); 00322 s = d->clearButton->sizeHint(); 00323 s.rwidth() += frameWidth; 00324 } 00325 return s; 00326 } 00327 00328 // Decides whether to show or hide the icon; called when the text changes 00329 void KLineEdit::updateClearButtonIcon(const QString& text) 00330 { 00331 if (!d->clearButton) { 00332 return; 00333 } 00334 if (isReadOnly()) { 00335 d->adjustForReadOnly(); 00336 return; 00337 } 00338 00339 int clearButtonState = KIconLoader::DefaultState; 00340 00341 if (d->wideEnoughForClear && text.length() > 0) { 00342 d->clearButton->animateVisible(true); 00343 } else { 00344 d->clearButton->animateVisible(false); 00345 } 00346 00347 if (!d->clearButton->pixmap().isNull()) { 00348 return; 00349 } 00350 00351 if (layoutDirection() == Qt::LeftToRight) { 00352 d->clearButton->setPixmap(SmallIcon("edit-clear-locationbar-rtl", 0, clearButtonState)); 00353 } else { 00354 d->clearButton->setPixmap(SmallIcon("edit-clear-locationbar-ltr", 0, clearButtonState)); 00355 } 00356 00357 d->clearButton->setVisible(text.length() > 0); 00358 } 00359 00360 // Determine geometry of clear button. Called initially, and on resizeEvent. 00361 void KLineEdit::updateClearButton() 00362 { 00363 if (!d->clearButton) { 00364 return; 00365 } 00366 if (isReadOnly()) { 00367 d->adjustForReadOnly(); 00368 return; 00369 } 00370 00371 const QSize geom = size(); 00372 const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth,0,this); 00373 const int buttonWidth = d->clearButton->sizeHint().width(); 00374 const QSize newButtonSize(buttonWidth, geom.height()); 00375 const QFontMetrics fm(font()); 00376 const int em = fm.width("m"); 00377 00378 // make sure we have enough room for the clear button 00379 // no point in showing it if we can't also see a few characters as well 00380 const bool wideEnough = geom.width() > 4 * em + buttonWidth + frameWidth; 00381 00382 if (newButtonSize != d->clearButton->size()) { 00383 d->clearButton->resize(newButtonSize); 00384 } 00385 00386 if (d->style) { 00387 d->style.data()->m_overlap = wideEnough ? buttonWidth + frameWidth : 0; 00388 } 00389 00390 if (layoutDirection() == Qt::LeftToRight ) { 00391 d->clearButton->move(geom.width() - frameWidth - buttonWidth - 1, 0); 00392 } else { 00393 d->clearButton->move(frameWidth + 1, 0); 00394 } 00395 00396 if (wideEnough != d->wideEnoughForClear) { 00397 // we may (or may not) have been showing the button, but now our 00398 // positiong on that matter has shifted, so let's ensure that it 00399 // is properly visible (or not) 00400 d->wideEnoughForClear = wideEnough; 00401 updateClearButtonIcon(text()); 00402 } 00403 } 00404 00405 void KLineEdit::setCompletionMode( KGlobalSettings::Completion mode ) 00406 { 00407 KGlobalSettings::Completion oldMode = completionMode(); 00408 00409 if ( oldMode != mode && (oldMode == KGlobalSettings::CompletionPopup || 00410 oldMode == KGlobalSettings::CompletionPopupAuto ) && 00411 d->completionBox && d->completionBox->isVisible() ) 00412 d->completionBox->hide(); 00413 00414 // If the widgets echo mode is not Normal, no completion 00415 // feature will be enabled even if one is requested. 00416 if ( echoMode() != QLineEdit::Normal ) 00417 mode = KGlobalSettings::CompletionNone; // Override the request. 00418 00419 if ( kapp && !KAuthorized::authorize("lineedit_text_completion") ) 00420 mode = KGlobalSettings::CompletionNone; 00421 00422 if ( mode == KGlobalSettings::CompletionPopupAuto || 00423 mode == KGlobalSettings::CompletionAuto || 00424 mode == KGlobalSettings::CompletionMan ) 00425 d->autoSuggest = true; 00426 else 00427 d->autoSuggest = false; 00428 00429 KCompletionBase::setCompletionMode( mode ); 00430 } 00431 00432 void KLineEdit::setCompletionModeDisabled( KGlobalSettings::Completion mode, bool disable ) 00433 { 00434 d->disableCompletionMap[ mode ] = disable; 00435 } 00436 00437 void KLineEdit::setCompletedText( const QString& t, bool marked ) 00438 { 00439 if ( !d->autoSuggest ) 00440 return; 00441 00442 const QString txt = text(); 00443 00444 if ( t != txt ) 00445 { 00446 setText(t); 00447 if ( marked ) 00448 setSelection(t.length(), txt.length()-t.length()); 00449 setUserSelection(false); 00450 } 00451 else 00452 setUserSelection(true); 00453 00454 } 00455 00456 void KLineEdit::setCompletedText( const QString& text ) 00457 { 00458 KGlobalSettings::Completion mode = completionMode(); 00459 const bool marked = ( mode == KGlobalSettings::CompletionAuto || 00460 mode == KGlobalSettings::CompletionMan || 00461 mode == KGlobalSettings::CompletionPopup || 00462 mode == KGlobalSettings::CompletionPopupAuto ); 00463 setCompletedText( text, marked ); 00464 } 00465 00466 void KLineEdit::rotateText( KCompletionBase::KeyBindingType type ) 00467 { 00468 KCompletion* comp = compObj(); 00469 if ( comp && 00470 (type == KCompletionBase::PrevCompletionMatch || 00471 type == KCompletionBase::NextCompletionMatch ) ) 00472 { 00473 QString input; 00474 00475 if (type == KCompletionBase::PrevCompletionMatch) 00476 input = comp->previousMatch(); 00477 else 00478 input = comp->nextMatch(); 00479 00480 // Skip rotation if previous/next match is null or the same text 00481 if ( input.isEmpty() || input == displayText() ) 00482 return; 00483 setCompletedText( input, hasSelectedText() ); 00484 } 00485 } 00486 00487 void KLineEdit::makeCompletion( const QString& text ) 00488 { 00489 KCompletion *comp = compObj(); 00490 KGlobalSettings::Completion mode = completionMode(); 00491 00492 if ( !comp || mode == KGlobalSettings::CompletionNone ) 00493 return; // No completion object... 00494 00495 const QString match = comp->makeCompletion( text ); 00496 00497 if ( mode == KGlobalSettings::CompletionPopup || 00498 mode == KGlobalSettings::CompletionPopupAuto ) 00499 { 00500 if ( match.isEmpty() ) 00501 { 00502 if ( d->completionBox ) 00503 { 00504 d->completionBox->hide(); 00505 d->completionBox->clear(); 00506 } 00507 } 00508 else 00509 setCompletedItems( comp->allMatches() ); 00510 } 00511 else // Auto, ShortAuto (Man) and Shell 00512 { 00513 // all other completion modes 00514 // If no match or the same match, simply return without completing. 00515 if ( match.isEmpty() || match == text ) 00516 return; 00517 00518 if ( mode != KGlobalSettings::CompletionShell ) 00519 setUserSelection(false); 00520 00521 if ( d->autoSuggest ) 00522 setCompletedText( match ); 00523 } 00524 } 00525 00526 void KLineEdit::setReadOnly(bool readOnly) 00527 { 00528 // Do not do anything if nothing changed... 00529 if (readOnly == isReadOnly ()) { 00530 return; 00531 } 00532 00533 QLineEdit::setReadOnly(readOnly); 00534 00535 if (readOnly) { 00536 d->bgRole = backgroundRole(); 00537 setBackgroundRole(QPalette::Window); 00538 if (d->enableSqueezedText && d->squeezedText.isEmpty()) { 00539 d->squeezedText = text(); 00540 setSqueezedText(); 00541 } 00542 00543 if (d->clearButton) { 00544 d->clearButton->animateVisible(false); 00545 d->adjustForReadOnly(); 00546 } 00547 } else { 00548 if (!d->squeezedText.isEmpty()) { 00549 setText(d->squeezedText); 00550 d->squeezedText.clear(); 00551 } 00552 00553 setBackgroundRole(d->bgRole); 00554 updateClearButton(); 00555 } 00556 } 00557 00558 void KLineEdit::setSqueezedText( const QString &text) 00559 { 00560 setSqueezedTextEnabled(true); 00561 setText(text); 00562 } 00563 00564 void KLineEdit::setSqueezedTextEnabled( bool enable ) 00565 { 00566 d->enableSqueezedText = enable; 00567 } 00568 00569 bool KLineEdit::isSqueezedTextEnabled() const 00570 { 00571 return d->enableSqueezedText; 00572 } 00573 00574 void KLineEdit::setText( const QString& text ) 00575 { 00576 if( d->enableClickMsg ) 00577 { 00578 d->drawClickMsg = text.isEmpty(); 00579 update(); 00580 } 00581 if( d->enableSqueezedText && isReadOnly() ) 00582 { 00583 d->squeezedText = text; 00584 setSqueezedText(); 00585 return; 00586 } 00587 00588 QLineEdit::setText( text ); 00589 } 00590 00591 void KLineEdit::setSqueezedText() 00592 { 00593 d->squeezedStart = 0; 00594 d->squeezedEnd = 0; 00595 const QString fullText = d->squeezedText; 00596 const QFontMetrics fm(fontMetrics()); 00597 const int labelWidth = size().width() - 2*style()->pixelMetric(QStyle::PM_DefaultFrameWidth) - 2; 00598 const int textWidth = fm.width(fullText); 00599 00600 if (textWidth > labelWidth) 00601 { 00602 // start with the dots only 00603 QString squeezedText = "..."; 00604 int squeezedWidth = fm.width(squeezedText); 00605 00606 // estimate how many letters we can add to the dots on both sides 00607 int letters = fullText.length() * (labelWidth - squeezedWidth) / textWidth / 2; 00608 squeezedText = fullText.left(letters) + "..." + fullText.right(letters); 00609 squeezedWidth = fm.width(squeezedText); 00610 00611 if (squeezedWidth < labelWidth) 00612 { 00613 // we estimated too short 00614 // add letters while text < label 00615 do 00616 { 00617 letters++; 00618 squeezedText = fullText.left(letters) + "..." + fullText.right(letters); 00619 squeezedWidth = fm.width(squeezedText); 00620 } while (squeezedWidth < labelWidth); 00621 letters--; 00622 squeezedText = fullText.left(letters) + "..." + fullText.right(letters); 00623 } 00624 else if (squeezedWidth > labelWidth) 00625 { 00626 // we estimated too long 00627 // remove letters while text > label 00628 do 00629 { 00630 letters--; 00631 squeezedText = fullText.left(letters) + "..." + fullText.right(letters); 00632 squeezedWidth = fm.width(squeezedText); 00633 } while (squeezedWidth > labelWidth); 00634 } 00635 00636 if (letters < 5) 00637 { 00638 // too few letters added -> we give up squeezing 00639 QLineEdit::setText(fullText); 00640 } 00641 else 00642 { 00643 QLineEdit::setText(squeezedText); 00644 d->squeezedStart = letters; 00645 d->squeezedEnd = fullText.length() - letters; 00646 } 00647 00648 setToolTip( fullText ); 00649 00650 } 00651 else 00652 { 00653 QLineEdit::setText(fullText); 00654 00655 this->setToolTip( "" ); 00656 QToolTip::showText(pos(), QString()); // hide 00657 } 00658 00659 setCursorPosition(0); 00660 } 00661 00662 void KLineEdit::copy() const 00663 { 00664 if( !copySqueezedText(true)) 00665 QLineEdit::copy(); 00666 } 00667 00668 bool KLineEdit::copySqueezedText(bool clipboard) const 00669 { 00670 if (!d->squeezedText.isEmpty() && d->squeezedStart) 00671 { 00672 KLineEdit *that = const_cast<KLineEdit *>(this); 00673 if (!that->hasSelectedText()) 00674 return false; 00675 int start = selectionStart(), end = start + selectedText().length(); 00676 if (start >= d->squeezedStart+3) 00677 start = start - 3 - d->squeezedStart + d->squeezedEnd; 00678 else if (start > d->squeezedStart) 00679 start = d->squeezedStart; 00680 if (end >= d->squeezedStart+3) 00681 end = end - 3 - d->squeezedStart + d->squeezedEnd; 00682 else if (end > d->squeezedStart) 00683 end = d->squeezedEnd; 00684 if (start == end) 00685 return false; 00686 QString t = d->squeezedText; 00687 t = t.mid(start, end - start); 00688 disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); 00689 QApplication::clipboard()->setText( t, clipboard ? QClipboard::Clipboard : QClipboard::Selection ); 00690 connect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 00691 SLOT(_q_clipboardChanged()) ); 00692 return true; 00693 } 00694 return false; 00695 } 00696 00697 void KLineEdit::resizeEvent( QResizeEvent * ev ) 00698 { 00699 if (!d->squeezedText.isEmpty()) 00700 setSqueezedText(); 00701 00702 updateClearButton(); 00703 QLineEdit::resizeEvent(ev); 00704 } 00705 00706 00707 void KLineEdit::keyPressEvent( QKeyEvent *e ) 00708 { 00709 const int key = e->key() | e->modifiers(); 00710 00711 if ( KStandardShortcut::copy().contains( key ) ) 00712 { 00713 copy(); 00714 return; 00715 } 00716 else if ( KStandardShortcut::paste().contains( key ) ) 00717 { 00718 // TODO: 00719 // we should restore the original text (not autocompleted), otherwise the paste 00720 // will get into troubles Bug: 134691 00721 if( !isReadOnly() ) 00722 paste(); 00723 return; 00724 } 00725 else if ( KStandardShortcut::pasteSelection().contains( key ) ) 00726 { 00727 QString text = QApplication::clipboard()->text( QClipboard::Selection); 00728 insert( text ); 00729 deselect(); 00730 return; 00731 } 00732 00733 else if ( KStandardShortcut::cut().contains( key ) ) 00734 { 00735 if( !isReadOnly() ) 00736 cut(); 00737 return; 00738 } 00739 else if ( KStandardShortcut::undo().contains( key ) ) 00740 { 00741 if( !isReadOnly() ) 00742 undo(); 00743 return; 00744 } 00745 else if ( KStandardShortcut::redo().contains( key ) ) 00746 { 00747 if( !isReadOnly() ) 00748 redo(); 00749 return; 00750 } 00751 else if ( KStandardShortcut::deleteWordBack().contains( key ) ) 00752 { 00753 cursorWordBackward(true); 00754 if ( hasSelectedText() ) 00755 del(); 00756 00757 e->accept(); 00758 return; 00759 } 00760 else if ( KStandardShortcut::deleteWordForward().contains( key ) ) 00761 { 00762 // Workaround for QT bug where 00763 cursorWordForward(true); 00764 if ( hasSelectedText() ) 00765 del(); 00766 00767 e->accept(); 00768 return; 00769 } 00770 else if ( KStandardShortcut::backwardWord().contains( key ) ) 00771 { 00772 cursorWordBackward(false); 00773 e->accept(); 00774 return; 00775 } 00776 else if ( KStandardShortcut::forwardWord().contains( key ) ) 00777 { 00778 cursorWordForward(false); 00779 e->accept(); 00780 return; 00781 } 00782 else if ( KStandardShortcut::beginningOfLine().contains( key ) ) 00783 { 00784 home(false); 00785 e->accept(); 00786 return; 00787 } 00788 else if ( KStandardShortcut::endOfLine().contains( key ) ) 00789 { 00790 end(false); 00791 e->accept(); 00792 return; 00793 } 00794 00795 00796 // Filter key-events if EchoMode is normal and 00797 // completion mode is not set to CompletionNone 00798 if ( echoMode() == QLineEdit::Normal && 00799 completionMode() != KGlobalSettings::CompletionNone ) 00800 { 00801 if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { 00802 const bool trap = (d->completionBox && d->completionBox->isVisible()); 00803 const bool stopEvent = (trap || (d->grabReturnKeyEvents && 00804 (e->modifiers() == Qt::NoButton || 00805 e->modifiers() == Qt::KeypadModifier))); 00806 00807 if (stopEvent) { 00808 emit QLineEdit::returnPressed(); 00809 e->accept(); 00810 } 00811 00812 emit returnPressed( displayText() ); 00813 00814 if (trap) { 00815 d->completionBox->hide(); 00816 deselect(); 00817 setCursorPosition(text().length()); 00818 } 00819 00820 // Eat the event if the user asked for it, or if a completionbox was visible 00821 if (stopEvent) { 00822 return; 00823 } 00824 } 00825 00826 const KeyBindingMap keys = getKeyBindings(); 00827 const KGlobalSettings::Completion mode = completionMode(); 00828 const bool noModifier = (e->modifiers() == Qt::NoButton || 00829 e->modifiers() == Qt::ShiftModifier || 00830 e->modifiers() == Qt::KeypadModifier); 00831 00832 if ( (mode == KGlobalSettings::CompletionAuto || 00833 mode == KGlobalSettings::CompletionPopupAuto || 00834 mode == KGlobalSettings::CompletionMan) && noModifier ) 00835 { 00836 if ( !d->userSelection && hasSelectedText() && 00837 ( e->key() == Qt::Key_Right || e->key() == Qt::Key_Left ) && 00838 e->modifiers()==Qt::NoButton ) 00839 { 00840 const QString old_txt = text(); 00841 d->disableRestoreSelection = true; 00842 const int start = selectionStart(); 00843 00844 deselect(); 00845 QLineEdit::keyPressEvent ( e ); 00846 const int cPosition=cursorPosition(); 00847 setText(old_txt); 00848 00849 // keep cursor at cPosition 00850 setSelection(old_txt.length(), cPosition - old_txt.length()); 00851 if (e->key() == Qt::Key_Right && cPosition > start ) 00852 { 00853 //the user explicitly accepted the autocompletion 00854 d->_k_updateUserText(text()); 00855 } 00856 00857 d->disableRestoreSelection = false; 00858 return; 00859 } 00860 00861 if ( e->key() == Qt::Key_Escape ) 00862 { 00863 if (hasSelectedText() && !d->userSelection ) 00864 { 00865 del(); 00866 setUserSelection(true); 00867 } 00868 00869 // Don't swallow the Escape press event for the case 00870 // of dialogs, which have Escape associated to Cancel 00871 e->ignore(); 00872 return; 00873 } 00874 00875 } 00876 00877 if ( (mode == KGlobalSettings::CompletionAuto || 00878 mode == KGlobalSettings::CompletionMan) && noModifier ) 00879 { 00880 const QString keycode = e->text(); 00881 if ( !keycode.isEmpty() && (keycode.unicode()->isPrint() || 00882 e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete ) ) 00883 { 00884 const bool hasUserSelection=d->userSelection; 00885 const bool hadSelection=hasSelectedText(); 00886 00887 bool cursorNotAtEnd=false; 00888 00889 const int start = selectionStart(); 00890 const int cPos = cursorPosition(); 00891 00892 // When moving the cursor, we want to keep the autocompletion as an 00893 // autocompletion, so we want to process events at the cursor position 00894 // as if there was no selection. After processing the key event, we 00895 // can set the new autocompletion again. 00896 if ( hadSelection && !hasUserSelection && start>cPos ) 00897 { 00898 del(); 00899 setCursorPosition(cPos); 00900 cursorNotAtEnd=true; 00901 } 00902 00903 d->disableRestoreSelection = true; 00904 QLineEdit::keyPressEvent ( e ); 00905 d->disableRestoreSelection = false; 00906 00907 QString txt = text(); 00908 int len = txt.length(); 00909 if ( !hasSelectedText() && len /*&& cursorPosition() == len */) 00910 { 00911 if ( e->key() == Qt::Key_Backspace ) 00912 { 00913 if ( hadSelection && !hasUserSelection && !cursorNotAtEnd ) 00914 { 00915 backspace(); 00916 txt = text(); 00917 len = txt.length(); 00918 } 00919 00920 if (!d->s_backspacePerformsCompletion || !len) { 00921 d->autoSuggest = false; 00922 } 00923 } 00924 00925 if (e->key() == Qt::Key_Delete ) 00926 d->autoSuggest=false; 00927 00928 doCompletion(txt); 00929 00930 if( (e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete) ) 00931 d->autoSuggest=true; 00932 00933 e->accept(); 00934 } 00935 00936 return; 00937 } 00938 00939 } 00940 00941 else if (( mode == KGlobalSettings::CompletionPopup || 00942 mode == KGlobalSettings::CompletionPopupAuto ) && 00943 noModifier && !e->text().isEmpty() ) 00944 { 00945 const QString old_txt = text(); 00946 const bool hasUserSelection=d->userSelection; 00947 const bool hadSelection=hasSelectedText(); 00948 bool cursorNotAtEnd=false; 00949 00950 const int start = selectionStart(); 00951 const int cPos = cursorPosition(); 00952 const QString keycode = e->text(); 00953 00954 // When moving the cursor, we want to keep the autocompletion as an 00955 // autocompletion, so we want to process events at the cursor position 00956 // as if there was no selection. After processing the key event, we 00957 // can set the new autocompletion again. 00958 if (hadSelection && !hasUserSelection && start>cPos && 00959 ( (!keycode.isEmpty() && keycode.unicode()->isPrint()) || 00960 e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete ) ) 00961 { 00962 del(); 00963 setCursorPosition(cPos); 00964 cursorNotAtEnd=true; 00965 } 00966 00967 const int selectedLength=selectedText().length(); 00968 00969 d->disableRestoreSelection = true; 00970 QLineEdit::keyPressEvent ( e ); 00971 d->disableRestoreSelection = false; 00972 00973 if (( selectedLength != selectedText().length() ) && !hasUserSelection ) 00974 slotRestoreSelectionColors(); // and set userSelection to true 00975 00976 QString txt = text(); 00977 int len = txt.length(); 00978 if ( ( txt != old_txt || txt != e->text() ) && len/* && ( cursorPosition() == len || force )*/ && 00979 ( (!keycode.isEmpty() && keycode.unicode()->isPrint()) || 00980 e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete) ) 00981 { 00982 if ( e->key() == Qt::Key_Backspace ) 00983 { 00984 if ( hadSelection && !hasUserSelection && !cursorNotAtEnd ) 00985 { 00986 backspace(); 00987 txt = text(); 00988 len = txt.length(); 00989 } 00990 00991 if (!d->s_backspacePerformsCompletion) { 00992 d->autoSuggest = false; 00993 } 00994 } 00995 00996 if (e->key() == Qt::Key_Delete ) 00997 d->autoSuggest=false; 00998 00999 if ( d->completionBox ) 01000 d->completionBox->setCancelledText( txt ); 01001 01002 doCompletion(txt); 01003 01004 if ( (e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete ) && 01005 mode == KGlobalSettings::CompletionPopupAuto ) 01006 d->autoSuggest=true; 01007 01008 e->accept(); 01009 } 01010 else if (!len && d->completionBox && d->completionBox->isVisible()) 01011 d->completionBox->hide(); 01012 01013 return; 01014 } 01015 01016 else if ( mode == KGlobalSettings::CompletionShell ) 01017 { 01018 // Handles completion. 01019 KShortcut cut; 01020 if ( keys[TextCompletion].isEmpty() ) 01021 cut = KStandardShortcut::shortcut(KStandardShortcut::TextCompletion); 01022 else 01023 cut = keys[TextCompletion]; 01024 01025 if ( cut.contains( key ) ) 01026 { 01027 // Emit completion if the completion mode is CompletionShell 01028 // and the cursor is at the end of the string. 01029 const QString txt = text(); 01030 const int len = txt.length(); 01031 if ( cursorPosition() == len && len != 0 ) 01032 { 01033 doCompletion(txt); 01034 return; 01035 } 01036 } 01037 else if ( d->completionBox ) 01038 d->completionBox->hide(); 01039 } 01040 01041 // handle rotation 01042 if ( mode != KGlobalSettings::CompletionNone ) 01043 { 01044 // Handles previous match 01045 KShortcut cut; 01046 if ( keys[PrevCompletionMatch].isEmpty() ) 01047 cut = KStandardShortcut::shortcut(KStandardShortcut::PrevCompletion); 01048 else 01049 cut = keys[PrevCompletionMatch]; 01050 01051 if ( cut.contains( key ) ) 01052 { 01053 if ( emitSignals() ) 01054 emit textRotation( KCompletionBase::PrevCompletionMatch ); 01055 if ( handleSignals() ) 01056 rotateText( KCompletionBase::PrevCompletionMatch ); 01057 return; 01058 } 01059 01060 // Handles next match 01061 if ( keys[NextCompletionMatch].isEmpty() ) 01062 cut = KStandardShortcut::shortcut(KStandardShortcut::NextCompletion); 01063 else 01064 cut = keys[NextCompletionMatch]; 01065 01066 if ( cut.contains( key ) ) 01067 { 01068 if ( emitSignals() ) 01069 emit textRotation( KCompletionBase::NextCompletionMatch ); 01070 if ( handleSignals() ) 01071 rotateText( KCompletionBase::NextCompletionMatch ); 01072 return; 01073 } 01074 } 01075 01076 // substring completion 01077 if ( compObj() ) 01078 { 01079 KShortcut cut; 01080 if ( keys[SubstringCompletion].isEmpty() ) 01081 cut = KStandardShortcut::shortcut(KStandardShortcut::SubstringCompletion); 01082 else 01083 cut = keys[SubstringCompletion]; 01084 01085 if ( cut.contains( key ) ) 01086 { 01087 if ( emitSignals() ) 01088 emit substringCompletion( text() ); 01089 if ( handleSignals() ) 01090 { 01091 setCompletedItems( compObj()->substringCompletion(text())); 01092 e->accept(); 01093 } 01094 return; 01095 } 01096 } 01097 } 01098 const int selectedLength = selectedText().length(); 01099 01100 // Let QLineEdit handle any other keys events. 01101 QLineEdit::keyPressEvent ( e ); 01102 01103 if ( selectedLength != selectedText().length() ) 01104 slotRestoreSelectionColors(); // and set userSelection to true 01105 } 01106 01107 void KLineEdit::mouseDoubleClickEvent( QMouseEvent* e ) 01108 { 01109 if ( e->button() == Qt::LeftButton ) 01110 { 01111 d->possibleTripleClick=true; 01112 QTimer::singleShot( QApplication::doubleClickInterval(),this, 01113 SLOT(tripleClickTimeout()) ); 01114 } 01115 QLineEdit::mouseDoubleClickEvent( e ); 01116 } 01117 01118 void KLineEdit::mousePressEvent( QMouseEvent* e ) 01119 { 01120 if ( (e->button() == Qt::LeftButton || 01121 e->button() == Qt::MidButton ) && 01122 d->clearButton ) { 01123 d->clickInClear = ( d->clearButton == childAt(e->pos()) || d->clearButton->underMouse() ); 01124 01125 if ( d->clickInClear ) { 01126 d->possibleTripleClick = false; 01127 } 01128 } 01129 01130 if ( e->button() == Qt::LeftButton && d->possibleTripleClick ) { 01131 selectAll(); 01132 e->accept(); 01133 return; 01134 } 01135 01136 // if middle clicking and if text is present in the clipboard then clear the selection 01137 // to prepare paste operation 01138 if ( e->button() == Qt::MidButton ) { 01139 if ( hasSelectedText() ) { 01140 if ( QApplication::clipboard()->text( QClipboard::Selection ).length() >0 ) { 01141 backspace(); 01142 } 01143 } 01144 } 01145 01146 QLineEdit::mousePressEvent( e ); 01147 } 01148 01149 void KLineEdit::mouseReleaseEvent( QMouseEvent* e ) 01150 { 01151 if ( d->clickInClear ) { 01152 if ( d->clearButton == childAt(e->pos()) || d->clearButton->underMouse() ) { 01153 QString newText; 01154 if ( e->button() == Qt::MidButton ) { 01155 newText = QApplication::clipboard()->text( QClipboard::Selection ); 01156 setText( newText ); 01157 } else { 01158 setSelection(0, text().size()); 01159 del(); 01160 emit clearButtonClicked(); 01161 } 01162 emit textChanged( newText ); 01163 } 01164 01165 d->clickInClear = false; 01166 e->accept(); 01167 return; 01168 } 01169 01170 QLineEdit::mouseReleaseEvent( e ); 01171 01172 if (QApplication::clipboard()->supportsSelection() ) { 01173 if ( e->button() == Qt::LeftButton ) { 01174 // Fix copying of squeezed text if needed 01175 copySqueezedText( false ); 01176 } 01177 } 01178 } 01179 01180 void KLineEdit::tripleClickTimeout() 01181 { 01182 d->possibleTripleClick=false; 01183 } 01184 01185 QMenu* KLineEdit::createStandardContextMenu() 01186 { 01187 QMenu *popup = QLineEdit::createStandardContextMenu(); 01188 01189 if( !isReadOnly() ) 01190 { 01191 // FIXME: This code depends on Qt's action ordering. 01192 const QList<QAction *> actionList = popup->actions(); 01193 enum { UndoAct, RedoAct, Separator1, CutAct, CopyAct, PasteAct, DeleteAct, ClearAct, 01194 Separator2, SelectAllAct, NCountActs }; 01195 QAction *separatorAction = 0L; 01196 // separator we want is right after Delete right now. 01197 const int idx = actionList.indexOf( actionList[DeleteAct] ) + 1; 01198 if ( idx < actionList.count() ) 01199 separatorAction = actionList.at( idx ); 01200 if ( separatorAction ) 01201 { 01202 KAction *clearAllAction = KStandardAction::clear( this, SLOT( clear() ), this) ; 01203 if ( text().isEmpty() ) 01204 clearAllAction->setEnabled( false ); 01205 popup->insertAction( separatorAction, clearAllAction ); 01206 } 01207 } 01208 01209 KIconTheme::assignIconsToContextMenu( KIconTheme::TextEditor, popup->actions () ); 01210 01211 // If a completion object is present and the input 01212 // widget is not read-only, show the Text Completion 01213 // menu item. 01214 if ( compObj() && !isReadOnly() && KAuthorized::authorize("lineedit_text_completion") ) 01215 { 01216 QMenu *subMenu = popup->addMenu( KIcon("text-completion"), i18nc("@title:menu", "Text Completion") ); 01217 connect( subMenu, SIGNAL( triggered ( QAction* ) ), 01218 this, SLOT( completionMenuActivated( QAction* ) ) ); 01219 01220 popup->addSeparator(); 01221 01222 QActionGroup* ag = new QActionGroup( this ); 01223 d->noCompletionAction = ag->addAction( i18nc("@item:inmenu Text Completion", "None")); 01224 d->shellCompletionAction = ag->addAction( i18nc("@item:inmenu Text Completion", "Manual") ); 01225 d->autoCompletionAction = ag->addAction( i18nc("@item:inmenu Text Completion", "Automatic") ); 01226 d->popupCompletionAction = ag->addAction( i18nc("@item:inmenu Text Completion", "Dropdown List") ); 01227 d->shortAutoCompletionAction = ag->addAction( i18nc("@item:inmenu Text Completion", "Short Automatic") ); 01228 d->popupAutoCompletionAction = ag->addAction( i18nc("@item:inmenu Text Completion", "Dropdown List && Automatic")); 01229 subMenu->addActions( ag->actions() ); 01230 01231 //subMenu->setAccel( KStandardShortcut::completion(), ShellCompletion ); 01232 01233 d->shellCompletionAction->setCheckable( true ); 01234 d->noCompletionAction->setCheckable( true ); 01235 d->popupCompletionAction->setCheckable( true ); 01236 d->autoCompletionAction->setCheckable( true ); 01237 d->shortAutoCompletionAction->setCheckable( true ); 01238 d->popupAutoCompletionAction->setCheckable( true ); 01239 01240 d->shellCompletionAction->setEnabled( !d->disableCompletionMap[ KGlobalSettings::CompletionShell ] ); 01241 d->noCompletionAction->setEnabled( !d->disableCompletionMap[ KGlobalSettings::CompletionNone ] ); 01242 d->popupCompletionAction->setEnabled( !d->disableCompletionMap[ KGlobalSettings::CompletionPopup ] ); 01243 d->autoCompletionAction->setEnabled( !d->disableCompletionMap[ KGlobalSettings::CompletionAuto ] ); 01244 d->shortAutoCompletionAction->setEnabled( !d->disableCompletionMap[ KGlobalSettings::CompletionMan ] ); 01245 d->popupAutoCompletionAction->setEnabled( !d->disableCompletionMap[ KGlobalSettings::CompletionPopupAuto ] ); 01246 01247 const KGlobalSettings::Completion mode = completionMode(); 01248 d->noCompletionAction->setChecked( mode == KGlobalSettings::CompletionNone ); 01249 d->shellCompletionAction->setChecked( mode == KGlobalSettings::CompletionShell ); 01250 d->popupCompletionAction->setChecked( mode == KGlobalSettings::CompletionPopup ); 01251 d->autoCompletionAction->setChecked( mode == KGlobalSettings::CompletionAuto ); 01252 d->shortAutoCompletionAction->setChecked( mode == KGlobalSettings::CompletionMan ); 01253 d->popupAutoCompletionAction->setChecked( mode == KGlobalSettings::CompletionPopupAuto ); 01254 01255 const KGlobalSettings::Completion defaultMode = KGlobalSettings::completionMode(); 01256 if ( mode != defaultMode && !d->disableCompletionMap[ defaultMode ] ) 01257 { 01258 subMenu->addSeparator(); 01259 d->defaultAction = subMenu->addAction( i18nc("@item:inmenu Text Completion", "Default") ); 01260 } 01261 } 01262 01263 return popup; 01264 } 01265 01266 void KLineEdit::contextMenuEvent( QContextMenuEvent *e ) 01267 { 01268 if ( QLineEdit::contextMenuPolicy() != Qt::DefaultContextMenu ) 01269 return; 01270 QMenu *popup = createStandardContextMenu(); 01271 01272 // ### do we really need this? Yes, Please do not remove! This 01273 // allows applications to extend the popup menu without having to 01274 // inherit from this class! (DA) 01275 emit aboutToShowContextMenu( popup ); 01276 01277 popup->exec(e->globalPos()); 01278 delete popup; 01279 } 01280 01281 void KLineEdit::completionMenuActivated( QAction *act) 01282 { 01283 KGlobalSettings::Completion oldMode = completionMode(); 01284 01285 if( act == d->noCompletionAction ) 01286 { 01287 setCompletionMode( KGlobalSettings::CompletionNone ); 01288 } 01289 else if( act == d->shellCompletionAction) 01290 { 01291 setCompletionMode( KGlobalSettings::CompletionShell ); 01292 } 01293 else if( act == d->autoCompletionAction) 01294 { 01295 setCompletionMode( KGlobalSettings::CompletionAuto ); 01296 } 01297 else if( act == d->popupCompletionAction) 01298 { 01299 setCompletionMode( KGlobalSettings::CompletionPopup ); 01300 } 01301 else if( act == d->shortAutoCompletionAction) 01302 { 01303 setCompletionMode( KGlobalSettings::CompletionMan ); 01304 } 01305 else if( act == d->popupAutoCompletionAction) 01306 { 01307 setCompletionMode( KGlobalSettings::CompletionPopupAuto ); 01308 } 01309 else if( act == d->defaultAction ) 01310 { 01311 setCompletionMode( KGlobalSettings::completionMode() ); 01312 } 01313 else 01314 return; 01315 01316 if ( oldMode != completionMode() ) 01317 { 01318 if ( (oldMode == KGlobalSettings::CompletionPopup || 01319 oldMode == KGlobalSettings::CompletionPopupAuto ) && 01320 d->completionBox && d->completionBox->isVisible() ) 01321 d->completionBox->hide(); 01322 emit completionModeChanged( completionMode() ); 01323 } 01324 } 01325 01326 void KLineEdit::dropEvent(QDropEvent *e) 01327 { 01328 if( d->handleURLDrops ) 01329 { 01330 const KUrl::List urlList = KUrl::List::fromMimeData( e->mimeData() ); 01331 if ( !urlList.isEmpty() ) 01332 { 01333 // Let's replace the current text with the dropped URL(s), rather than appending. 01334 // Makes more sense in general (#188129), e.g. konq location bar and kurlrequester 01335 // can only hold one url anyway. OK this code supports multiple urls being dropped, 01336 // but that's not the common case [and it breaks if they contain spaces... this is why 01337 // kfiledialog uses double quotes around filenames in multiple-selection mode]... 01338 // 01339 // Anyway, if some apps prefer "append" then we should have a 01340 // setUrlDropsSupport( {NoUrlDrops, SingleUrlDrops, MultipleUrlDrops} ) 01341 // where Single replaces and Multiple appends. 01342 QString dropText; 01343 //QString dropText = text(); 01344 KUrl::List::ConstIterator it; 01345 for( it = urlList.begin() ; it != urlList.end() ; ++it ) 01346 { 01347 if(!dropText.isEmpty()) 01348 dropText+=' '; 01349 01350 dropText += (*it).prettyUrl(); 01351 } 01352 01353 setText(dropText); 01354 setCursorPosition(dropText.length()); 01355 01356 e->accept(); 01357 return; 01358 } 01359 } 01360 QLineEdit::dropEvent(e); 01361 } 01362 01363 bool KLineEdit::event( QEvent* ev ) 01364 { 01365 KCursor::autoHideEventFilter( this, ev ); 01366 if ( ev->type() == QEvent::ShortcutOverride ) 01367 { 01368 QKeyEvent *e = static_cast<QKeyEvent *>( ev ); 01369 if (d->overrideShortcut(e)) { 01370 ev->accept(); 01371 } 01372 } else if (ev->type() == QEvent::ApplicationPaletteChange 01373 || ev->type() == QEvent::PaletteChange) { 01374 // Assume the widget uses the application's palette 01375 QPalette p = QApplication::palette(); 01376 d->previousHighlightedTextColor=p.color(QPalette::Normal,QPalette::HighlightedText); 01377 d->previousHighlightColor=p.color(QPalette::Normal,QPalette::Highlight); 01378 setUserSelection(d->userSelection); 01379 } else if (ev->type() == QEvent::StyleChange) { 01380 // since we have our own style and it relies on this style to Get Things Right, 01381 // if a style is set specifically on the widget (which would replace our own style!) 01382 // hang on to this special style and re-instate our own style. 01383 //FIXME: Qt currently has a grave bug where already deleted QStyleSheetStyle objects 01384 // will get passed back in if we set a new style on it here. remove the qstrmcp test 01385 // when this is fixed in Qt (or a better approach is found) 01386 if (!qobject_cast<KLineEditStyle *>(style()) && 01387 qstrcmp(style()->metaObject()->className(), "QStyleSheetStyle") != 0 && 01388 QLatin1String(style()->metaObject()->className()) != d->lastStyleClass) { 01389 KLineEditStyle *kleStyle = d->style.data(); 01390 if (!kleStyle) { 01391 d->style = kleStyle = new KLineEditStyle(this); 01392 } 01393 01394 kleStyle->m_subStyle = style(); 01395 // this guards against "wrap around" where another style, e.g. QStyleSheetStyle, 01396 // is setting the style on QEvent::StyleChange 01397 d->lastStyleClass = QLatin1String(style()->metaObject()->className()); 01398 setStyle(kleStyle); 01399 d->lastStyleClass.clear(); 01400 } 01401 } 01402 01403 return QLineEdit::event( ev ); 01404 } 01405 01406 01407 void KLineEdit::setUrlDropsEnabled(bool enable) 01408 { 01409 d->handleURLDrops=enable; 01410 } 01411 01412 bool KLineEdit::urlDropsEnabled() const 01413 { 01414 return d->handleURLDrops; 01415 } 01416 01417 void KLineEdit::setTrapReturnKey( bool grab ) 01418 { 01419 d->grabReturnKeyEvents = grab; 01420 } 01421 01422 bool KLineEdit::trapReturnKey() const 01423 { 01424 return d->grabReturnKeyEvents; 01425 } 01426 01427 void KLineEdit::setUrl( const KUrl& url ) 01428 { 01429 setText( url.prettyUrl() ); 01430 } 01431 01432 void KLineEdit::setCompletionBox( KCompletionBox *box ) 01433 { 01434 if ( d->completionBox ) 01435 return; 01436 01437 d->completionBox = box; 01438 if ( handleSignals() ) 01439 { 01440 connect( d->completionBox, SIGNAL(currentTextChanged( const QString& )), 01441 SLOT(_k_slotCompletionBoxTextChanged( const QString& )) ); 01442 connect( d->completionBox, SIGNAL(userCancelled( const QString& )), 01443 SLOT(userCancelled( const QString& )) ); 01444 connect( d->completionBox, SIGNAL(activated(QString)), 01445 SIGNAL(completionBoxActivated(QString)) ); 01446 connect( d->completionBox, SIGNAL(activated(QString)), 01447 SIGNAL(textEdited(QString)) ); 01448 } 01449 } 01450 01451 /* 01452 * Set the line edit text without changing the modified flag. By default 01453 * calling setText resets the modified flag to false. 01454 */ 01455 static void setEditText(KLineEdit* edit, const QString& text) 01456 { 01457 if (!edit) { 01458 return; 01459 } 01460 01461 const bool wasModified = edit->isModified(); 01462 edit->setText(text); 01463 edit->setModified(wasModified); 01464 } 01465 01466 void KLineEdit::userCancelled(const QString & cancelText) 01467 { 01468 if ( completionMode() != KGlobalSettings::CompletionPopupAuto ) 01469 { 01470 setEditText(this, cancelText); 01471 } 01472 else if (hasSelectedText() ) 01473 { 01474 if (d->userSelection) 01475 deselect(); 01476 else 01477 { 01478 d->autoSuggest=false; 01479 const int start = selectionStart() ; 01480 const QString s = text().remove(selectionStart(), selectedText().length()); 01481 setEditText(this, s); 01482 setCursorPosition(start); 01483 d->autoSuggest=true; 01484 } 01485 } 01486 } 01487 01488 bool KLineEditPrivate::overrideShortcut(const QKeyEvent* e) 01489 { 01490 KShortcut scKey; 01491 01492 const int key = e->key() | e->modifiers(); 01493 const KLineEdit::KeyBindingMap keys = q->getKeyBindings(); 01494 01495 if (keys[KLineEdit::TextCompletion].isEmpty()) 01496 scKey = KStandardShortcut::shortcut(KStandardShortcut::TextCompletion); 01497 else 01498 scKey = keys[KLineEdit::TextCompletion]; 01499 01500 if (scKey.contains( key )) 01501 return true; 01502 01503 if (keys[KLineEdit::NextCompletionMatch].isEmpty()) 01504 scKey = KStandardShortcut::shortcut(KStandardShortcut::NextCompletion); 01505 else 01506 scKey = keys[KLineEdit::NextCompletionMatch]; 01507 01508 if (scKey.contains( key )) 01509 return true; 01510 01511 if (keys[KLineEdit::PrevCompletionMatch].isEmpty()) 01512 scKey = KStandardShortcut::shortcut(KStandardShortcut::PrevCompletion); 01513 else 01514 scKey = keys[KLineEdit::PrevCompletionMatch]; 01515 01516 if (scKey.contains( key )) 01517 return true; 01518 01519 // Override all the text manupilation accelerators... 01520 if ( KStandardShortcut::copy().contains( key ) ) 01521 return true; 01522 else if ( KStandardShortcut::paste().contains( key ) ) 01523 return true; 01524 else if ( KStandardShortcut::cut().contains( key ) ) 01525 return true; 01526 else if ( KStandardShortcut::undo().contains( key ) ) 01527 return true; 01528 else if ( KStandardShortcut::redo().contains( key ) ) 01529 return true; 01530 else if (KStandardShortcut::deleteWordBack().contains( key )) 01531 return true; 01532 else if (KStandardShortcut::deleteWordForward().contains( key )) 01533 return true; 01534 else if (KStandardShortcut::forwardWord().contains( key )) 01535 return true; 01536 else if (KStandardShortcut::backwardWord().contains( key )) 01537 return true; 01538 else if (KStandardShortcut::beginningOfLine().contains( key )) 01539 return true; 01540 else if (KStandardShortcut::endOfLine().contains( key )) 01541 return true; 01542 01543 // Shortcut overrides for shortcuts that QLineEdit handles 01544 // but doesn't dare force as "stronger than kaction shortcuts"... 01545 else if (e->matches(QKeySequence::SelectAll)) { 01546 return true; 01547 } 01548 #ifdef Q_WS_X11 01549 else if (key == Qt::CTRL + Qt::Key_E || key == Qt::CTRL + Qt::Key_U) 01550 return true; 01551 #endif 01552 01553 if (completionBox && completionBox->isVisible ()) 01554 { 01555 const int key = e->key(); 01556 const Qt::KeyboardModifiers modifiers = e->modifiers(); 01557 if ((key == Qt::Key_Backtab || key == Qt::Key_Tab) && 01558 (modifiers == Qt::NoModifier || (modifiers & Qt::ShiftModifier))) 01559 { 01560 return true; 01561 } 01562 } 01563 01564 01565 return false; 01566 } 01567 01568 void KLineEdit::setCompletedItems( const QStringList& items, bool autoSuggest ) 01569 { 01570 QString txt; 01571 if ( d->completionBox && d->completionBox->isVisible() ) { 01572 // The popup is visible already - do the matching on the initial string, 01573 // not on the currently selected one. 01574 txt = completionBox()->cancelledText(); 01575 } else { 01576 txt = text(); 01577 } 01578 01579 if ( !items.isEmpty() && 01580 !(items.count() == 1 && txt == items.first()) ) 01581 { 01582 // create completion box if non-existent 01583 completionBox(); 01584 01585 if ( d->completionBox->isVisible() ) 01586 { 01587 QListWidgetItem* currentItem = d->completionBox->currentItem(); 01588 01589 QString currentSelection; 01590 if ( currentItem != 0 ) { 01591 currentSelection = currentItem->text(); 01592 } 01593 01594 d->completionBox->setItems( items ); 01595 01596 const QList<QListWidgetItem*> matchedItems = d->completionBox->findItems(currentSelection, Qt::MatchExactly); 01597 QListWidgetItem* matchedItem = matchedItems.isEmpty() ? 0 : matchedItems.first(); 01598 01599 if (matchedItem) { 01600 const bool blocked = d->completionBox->blockSignals( true ); 01601 d->completionBox->setCurrentItem( matchedItem ); 01602 d->completionBox->blockSignals( blocked ); 01603 } else { 01604 d->completionBox->setCurrentRow(-1); 01605 } 01606 } 01607 else // completion box not visible yet -> show it 01608 { 01609 if ( !txt.isEmpty() ) 01610 d->completionBox->setCancelledText( txt ); 01611 d->completionBox->setItems( items ); 01612 d->completionBox->popup(); 01613 } 01614 01615 if ( d->autoSuggest && autoSuggest ) 01616 { 01617 const int index = items.first().indexOf( txt ); 01618 const QString newText = items.first().mid( index ); 01619 setUserSelection(false); // can be removed? setCompletedText sets it anyway 01620 setCompletedText(newText,true); 01621 } 01622 } 01623 else 01624 { 01625 if ( d->completionBox && d->completionBox->isVisible() ) 01626 d->completionBox->hide(); 01627 } 01628 } 01629 01630 KCompletionBox * KLineEdit::completionBox( bool create ) 01631 { 01632 if ( create && !d->completionBox ) { 01633 setCompletionBox( new KCompletionBox( this ) ); 01634 d->completionBox->setObjectName("completion box"); 01635 d->completionBox->setFont(font()); 01636 } 01637 01638 return d->completionBox; 01639 } 01640 01641 void KLineEdit::setCompletionObject( KCompletion* comp, bool hsig ) 01642 { 01643 KCompletion *oldComp = compObj(); 01644 if ( oldComp && handleSignals() ) 01645 disconnect( oldComp, SIGNAL( matches( const QStringList& )), 01646 this, SLOT( setCompletedItems( const QStringList& ))); 01647 01648 if ( comp && hsig ) 01649 connect( comp, SIGNAL( matches( const QStringList& )), 01650 this, SLOT( setCompletedItems( const QStringList& ))); 01651 01652 KCompletionBase::setCompletionObject( comp, hsig ); 01653 } 01654 01655 // QWidget::create() turns off mouse-Tracking which would break auto-hiding 01656 void KLineEdit::create( WId id, bool initializeWindow, bool destroyOldWindow ) 01657 { 01658 QLineEdit::create( id, initializeWindow, destroyOldWindow ); 01659 KCursor::setAutoHideCursor( this, true, true ); 01660 } 01661 01662 void KLineEdit::setUserSelection(bool userSelection) 01663 { 01664 //if !d->userSelection && userSelection we are accepting a completion, 01665 //so trigger an update 01666 01667 if (!d->userSelection && userSelection) 01668 { 01669 d->_k_updateUserText(text()); 01670 } 01671 01672 QPalette p = palette(); 01673 01674 if (userSelection) 01675 { 01676 p.setColor(QPalette::Highlight, d->previousHighlightColor); 01677 p.setColor(QPalette::HighlightedText, d->previousHighlightedTextColor); 01678 } 01679 else 01680 { 01681 QColor color=p.color(QPalette::Disabled, QPalette::Text); 01682 p.setColor(QPalette::HighlightedText, color); 01683 color=p.color(QPalette::Active, QPalette::Base); 01684 p.setColor(QPalette::Highlight, color); 01685 } 01686 01687 d->userSelection=userSelection; 01688 setPalette(p); 01689 } 01690 01691 void KLineEdit::slotRestoreSelectionColors() 01692 { 01693 if (d->disableRestoreSelection) 01694 return; 01695 01696 setUserSelection(true); 01697 } 01698 01699 void KLineEdit::clear() 01700 { 01701 setText( QString() ); 01702 } 01703 01704 void KLineEdit::_k_slotCompletionBoxTextChanged( const QString& text ) 01705 { 01706 if (!text.isEmpty()) 01707 { 01708 setText( text ); 01709 setModified(true); 01710 end( false ); // force cursor at end 01711 } 01712 } 01713 01714 QString KLineEdit::originalText() const 01715 { 01716 if ( d->enableSqueezedText && isReadOnly() ) 01717 return d->squeezedText; 01718 01719 return text(); 01720 } 01721 01722 QString KLineEdit::userText() const 01723 { 01724 return d->userText; 01725 } 01726 01727 bool KLineEdit::autoSuggest() const 01728 { 01729 return d->autoSuggest; 01730 } 01731 01732 void KLineEdit::paintEvent( QPaintEvent *ev ) 01733 { 01734 if (echoMode() == Password && d->threeStars) { 01735 // ### hack alert! 01736 // QLineEdit has currently no hooks to modify the displayed string. 01737 // When we call setText(), an update() is triggered and we get 01738 // into an infinite recursion. 01739 // Qt offers the setUpdatesEnabled() method, but when we re-enable 01740 // them, update() is triggered, and we get into the same recursion. 01741 // To work around this problem, we set/clear the internal Qt flag which 01742 // marks the updatesDisabled state manually. 01743 setAttribute(Qt::WA_UpdatesDisabled, true); 01744 blockSignals(true); 01745 const QString oldText = text(); 01746 const bool isModifiedState = isModified(); // save modified state because setText resets it 01747 setText(oldText + oldText + oldText); 01748 QLineEdit::paintEvent(ev); 01749 setText(oldText); 01750 setModified(isModifiedState); 01751 blockSignals(false); 01752 setAttribute(Qt::WA_UpdatesDisabled, false); 01753 } else { 01754 QLineEdit::paintEvent( ev ); 01755 } 01756 01757 if (d->enableClickMsg && d->drawClickMsg && !hasFocus() && text().isEmpty()) { 01758 QPainter p(this); 01759 QFont f = font(); 01760 f.setItalic(d->italicizePlaceholder); 01761 p.setFont(f); 01762 01763 QColor color(palette().color(foregroundRole())); 01764 color.setAlphaF(0.5); 01765 p.setPen(color); 01766 01767 QStyleOptionFrame opt; 01768 initStyleOption(&opt); 01769 QRect cr = style()->subElementRect(QStyle::SE_LineEditContents, &opt, this); 01770 01771 // this is copied/adapted from QLineEdit::paintEvent 01772 const int verticalMargin(1); 01773 const int horizontalMargin(2); 01774 01775 int left, top, right, bottom; 01776 getTextMargins( &left, &top, &right, &bottom ); 01777 cr.adjust( left, top, -right, -bottom ); 01778 01779 p.setClipRect(cr); 01780 01781 QFontMetrics fm = fontMetrics(); 01782 Qt::Alignment va = alignment() & Qt::AlignVertical_Mask; 01783 int vscroll; 01784 switch (va & Qt::AlignVertical_Mask) 01785 { 01786 case Qt::AlignBottom: 01787 vscroll = cr.y() + cr.height() - fm.height() - verticalMargin; 01788 break; 01789 01790 case Qt::AlignTop: 01791 vscroll = cr.y() + verticalMargin; 01792 break; 01793 01794 default: 01795 vscroll = cr.y() + (cr.height() - fm.height() + 1) / 2; 01796 break; 01797 01798 } 01799 01800 QRect lineRect(cr.x() + horizontalMargin, vscroll, cr.width() - 2*horizontalMargin, fm.height()); 01801 p.drawText(lineRect, Qt::AlignLeft|Qt::AlignVCenter, d->clickMessage); 01802 01803 } 01804 } 01805 01806 void KLineEdit::focusInEvent( QFocusEvent *ev ) 01807 { 01808 if ( d->enableClickMsg && d->drawClickMsg ) 01809 { 01810 d->drawClickMsg = false; 01811 update(); 01812 } 01813 QLineEdit::focusInEvent( ev ); 01814 } 01815 01816 void KLineEdit::focusOutEvent( QFocusEvent *ev ) 01817 { 01818 if ( d->enableClickMsg && text().isEmpty() ) 01819 { 01820 d->drawClickMsg = true; 01821 update(); 01822 } 01823 QLineEdit::focusOutEvent( ev ); 01824 } 01825 01826 void KLineEdit::setClickMessage( const QString &msg ) 01827 { 01828 d->enableClickMsg = !msg.isEmpty(); 01829 d->clickMessage = msg; 01830 d->drawClickMsg = text().isEmpty(); 01831 update(); 01832 } 01833 01834 #ifndef KDE_NO_DEPRECATED 01835 void KLineEdit::setContextMenuEnabled( bool showMenu ) 01836 { 01837 QLineEdit::setContextMenuPolicy( showMenu ? Qt::DefaultContextMenu : Qt::NoContextMenu ); 01838 } 01839 #endif 01840 01841 #ifndef KDE_NO_DEPRECATED 01842 bool KLineEdit::isContextMenuEnabled() const 01843 { 01844 return ( contextMenuPolicy() == Qt::DefaultContextMenu ); 01845 } 01846 #endif 01847 01848 void KLineEdit::setPasswordMode(bool b) 01849 { 01850 if(b) 01851 { 01852 KConfigGroup cg(KGlobal::config(), "Passwords"); 01853 const QString val = cg.readEntry("EchoMode", "OneStar"); 01854 if (val == "NoEcho") 01855 setEchoMode(NoEcho); 01856 else { 01857 d->threeStars = (val == "ThreeStars"); 01858 setEchoMode(Password); 01859 } 01860 } 01861 else 01862 { 01863 setEchoMode( Normal ); 01864 } 01865 } 01866 01867 bool KLineEdit::passwordMode() const 01868 { 01869 return echoMode() == NoEcho || echoMode() == Password; 01870 } 01871 01872 void KLineEdit::doCompletion(const QString& txt) 01873 { 01874 if (emitSignals()) { 01875 emit completion(txt); // emit when requested... 01876 } 01877 d->completionRunning = true; 01878 if (handleSignals()) { 01879 makeCompletion(txt); // handle when requested... 01880 } 01881 d->completionRunning = false; 01882 } 01883 01884 #include "klineedit.moc" 01885 #include "klineedit_p.moc"
KDE 4.7 API Reference