klineedit.cpp

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 <qclipboard.h>
00029 #include <qpainter.h>
00030 #include <qtimer.h>
00031 
00032 #include <kconfig.h>
00033 #include <qtooltip.h>
00034 #include <kcursor.h>
00035 #include <klocale.h>
00036 #include <kstdaccel.h>
00037 #include <kpopupmenu.h>
00038 #include <kdebug.h>
00039 #include <kcompletionbox.h>
00040 #include <kurl.h>
00041 #include <kurldrag.h>
00042 #include <kiconloader.h>
00043 #include <kapplication.h>
00044 
00045 #include "klineedit.h"
00046 #include "klineedit.moc"
00047 
00048 
00049 class KLineEdit::KLineEditPrivate
00050 {
00051 public:
00052     KLineEditPrivate()
00053     {
00054         completionBox = 0L;
00055         handleURLDrops = true;
00056         grabReturnKeyEvents = false;
00057 
00058         userSelection = true;
00059         autoSuggest = false;
00060         disableRestoreSelection = false;
00061         enableSqueezedText = false;
00062 
00063         if ( !initialized )
00064         {
00065             KConfigGroup config( KGlobal::config(), "General" );
00066             backspacePerformsCompletion = config.readBoolEntry( "Backspace performs completion", false );
00067 
00068             initialized = true;
00069         }
00070 
00071     }
00072 
00073     ~KLineEditPrivate()
00074     {
00075 // causes a weird crash in KWord at least, so let Qt delete it for us.
00076 //        delete completionBox;
00077     }
00078 
00079     static bool initialized;
00080     static bool backspacePerformsCompletion; // Configuration option
00081 
00082     QColor previousHighlightColor;
00083     QColor previousHighlightedTextColor;
00084 
00085     bool userSelection: 1;
00086     bool autoSuggest : 1;
00087     bool disableRestoreSelection: 1;
00088     bool handleURLDrops:1;
00089     bool grabReturnKeyEvents:1;
00090     bool enableSqueezedText:1;
00091 
00092     int squeezedEnd;
00093     int squeezedStart;
00094     BackgroundMode bgMode;
00095     QString squeezedText;
00096     KCompletionBox *completionBox;
00097 
00098     QString clickMessage;
00099     bool drawClickMsg:1;
00100 };
00101 
00102 bool KLineEdit::KLineEditPrivate::backspacePerformsCompletion = false;
00103 bool KLineEdit::KLineEditPrivate::initialized = false;
00104 
00105 
00106 KLineEdit::KLineEdit( const QString &string, QWidget *parent, const char *name )
00107           :QLineEdit( string, parent, name )
00108 {
00109     init();
00110 }
00111 
00112 KLineEdit::KLineEdit( QWidget *parent, const char *name )
00113           :QLineEdit( parent, name )
00114 {
00115     init();
00116 }
00117 
00118 KLineEdit::~KLineEdit ()
00119 {
00120     delete d;
00121     d = 0;
00122 }
00123 
00124 void KLineEdit::init()
00125 {
00126     d = new KLineEditPrivate;
00127     possibleTripleClick = false;
00128     d->bgMode = backgroundMode ();
00129 
00130     // Enable the context menu by default.
00131     KLineEdit::setContextMenuEnabled( true );
00132     KCursor::setAutoHideCursor( this, true, true );
00133     installEventFilter( this );
00134 
00135     KGlobalSettings::Completion mode = completionMode();
00136     d->autoSuggest = (mode == KGlobalSettings::CompletionMan ||
00137                       mode == KGlobalSettings::CompletionPopupAuto ||
00138                       mode == KGlobalSettings::CompletionAuto);
00139     connect( this, SIGNAL(selectionChanged()), this, SLOT(slotRestoreSelectionColors()));
00140 
00141     QPalette p = palette();
00142     if ( !d->previousHighlightedTextColor.isValid() )
00143       d->previousHighlightedTextColor=p.color(QPalette::Normal,QColorGroup::HighlightedText);
00144     if ( !d->previousHighlightColor.isValid() )
00145       d->previousHighlightColor=p.color(QPalette::Normal,QColorGroup::Highlight);
00146 
00147     d->drawClickMsg = false;
00148 }
00149 
00150 void KLineEdit::setCompletionMode( KGlobalSettings::Completion mode )
00151 {
00152     KGlobalSettings::Completion oldMode = completionMode();
00153 
00154     if ( oldMode != mode && (oldMode == KGlobalSettings::CompletionPopup ||
00155          oldMode == KGlobalSettings::CompletionPopupAuto ) &&
00156          d->completionBox && d->completionBox->isVisible() )
00157       d->completionBox->hide();
00158 
00159     // If the widgets echo mode is not Normal, no completion
00160     // feature will be enabled even if one is requested.
00161     if ( echoMode() != QLineEdit::Normal )
00162         mode = KGlobalSettings::CompletionNone; // Override the request.
00163 
00164     if ( kapp && !kapp->authorize("lineedit_text_completion") )
00165         mode = KGlobalSettings::CompletionNone;
00166 
00167     if ( mode == KGlobalSettings::CompletionPopupAuto ||
00168          mode == KGlobalSettings::CompletionAuto ||
00169          mode == KGlobalSettings::CompletionMan )
00170         d->autoSuggest = true;
00171     else
00172         d->autoSuggest = false;
00173 
00174     KCompletionBase::setCompletionMode( mode );
00175 }
00176 
00177 void KLineEdit::setCompletedText( const QString& t, bool marked )
00178 {
00179     if ( !d->autoSuggest )
00180       return;
00181 
00182     QString txt = text();
00183 
00184     if ( t != txt )
00185     {
00186         int start = marked ? txt.length() : t.length();
00187         validateAndSet( t, cursorPosition(), start, t.length() );
00188         setUserSelection(false);
00189     }
00190     else
00191       setUserSelection(true);
00192 
00193 }
00194 
00195 void KLineEdit::setCompletedText( const QString& text )
00196 {
00197     KGlobalSettings::Completion mode = completionMode();
00198     bool marked = ( mode == KGlobalSettings::CompletionAuto ||
00199                     mode == KGlobalSettings::CompletionMan ||
00200                     mode == KGlobalSettings::CompletionPopup ||
00201                     mode == KGlobalSettings::CompletionPopupAuto );
00202     setCompletedText( text, marked );
00203 }
00204 
00205 void KLineEdit::rotateText( KCompletionBase::KeyBindingType type )
00206 {
00207     KCompletion* comp = compObj();
00208     if ( comp &&
00209        (type == KCompletionBase::PrevCompletionMatch ||
00210         type == KCompletionBase::NextCompletionMatch ) )
00211     {
00212        QString input;
00213 
00214        if (type == KCompletionBase::PrevCompletionMatch)
00215           comp->previousMatch();
00216        else
00217           comp->nextMatch();
00218 
00219        // Skip rotation if previous/next match is null or the same text
00220        if ( input.isNull() || input == displayText() )
00221             return;
00222        setCompletedText( input, hasSelectedText() );
00223     }
00224 }
00225 
00226 void KLineEdit::makeCompletion( const QString& text )
00227 {
00228     KCompletion *comp = compObj();
00229     KGlobalSettings::Completion mode = completionMode();
00230 
00231     if ( !comp || mode == KGlobalSettings::CompletionNone )
00232         return;  // No completion object...
00233 
00234     QString match = comp->makeCompletion( text );
00235 
00236     if ( mode == KGlobalSettings::CompletionPopup ||
00237          mode == KGlobalSettings::CompletionPopupAuto )
00238     {
00239         if ( match.isNull() )
00240         {
00241             if ( d->completionBox )
00242             {
00243                 d->completionBox->hide();
00244                 d->completionBox->clear();
00245             }
00246         }
00247         else
00248             setCompletedItems( comp->allMatches() );
00249     }
00250     else // Auto,  ShortAuto (Man) and Shell
00251     {
00252         // all other completion modes
00253         // If no match or the same match, simply return without completing.
00254         if ( match.isNull() || match == text )
00255             return;
00256 
00257         if ( mode != KGlobalSettings::CompletionShell )
00258             setUserSelection(false);
00259 
00260         if ( d->autoSuggest )
00261             setCompletedText( match );
00262     }
00263 }
00264 
00265 void KLineEdit::setReadOnly(bool readOnly)
00266 {
00267     // Do not do anything if nothing changed...
00268     if (readOnly == isReadOnly ())
00269       return;
00270 
00271     QLineEdit::setReadOnly (readOnly);
00272 
00273     if (readOnly)
00274     {
00275         d->bgMode = backgroundMode ();
00276         setBackgroundMode (Qt::PaletteBackground);
00277         if (d->enableSqueezedText && d->squeezedText.isEmpty())
00278         {
00279             d->squeezedText = text();
00280             setSqueezedText();
00281         }
00282     }
00283     else
00284     {
00285         if (!d->squeezedText.isEmpty())
00286         {
00287            setText(d->squeezedText);
00288            d->squeezedText = QString::null;
00289         }
00290         setBackgroundMode (d->bgMode);
00291     }
00292 }
00293 
00294 void KLineEdit::setSqueezedText( const QString &text)
00295 {
00296     setEnableSqueezedText(true);
00297     setText(text);
00298 }
00299 
00300 void KLineEdit::setEnableSqueezedText( bool enable )
00301 {
00302     d->enableSqueezedText = enable;
00303 }
00304 
00305 bool KLineEdit::isSqueezedTextEnabled() const
00306 {
00307     return d->enableSqueezedText;
00308 }
00309 
00310 void KLineEdit::setText( const QString& text )
00311 {
00312     d->drawClickMsg = text.isEmpty() && !d->clickMessage.isEmpty();
00313     update();
00314 
00315     if( d->enableSqueezedText && isReadOnly() )
00316     {
00317         d->squeezedText = text;
00318         setSqueezedText();
00319         return;
00320     }
00321 
00322     QLineEdit::setText( text );
00323 }
00324 
00325 void KLineEdit::setSqueezedText()
00326 {
00327     d->squeezedStart = 0;
00328     d->squeezedEnd = 0;
00329     QString fullText = d->squeezedText;
00330     QFontMetrics fm(fontMetrics());
00331     int labelWidth = size().width() - 2*frameWidth() - 2;
00332     int textWidth = fm.width(fullText);
00333 
00334     if (textWidth > labelWidth)
00335     {
00336           // start with the dots only
00337           QString squeezedText = "...";
00338           int squeezedWidth = fm.width(squeezedText);
00339 
00340           // estimate how many letters we can add to the dots on both sides
00341           int letters = fullText.length() * (labelWidth - squeezedWidth) / textWidth / 2;
00342           squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
00343           squeezedWidth = fm.width(squeezedText);
00344 
00345       if (squeezedWidth < labelWidth)
00346       {
00347              // we estimated too short
00348              // add letters while text < label
00349           do
00350           {
00351                 letters++;
00352                 squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
00353                 squeezedWidth = fm.width(squeezedText);
00354              } while (squeezedWidth < labelWidth);
00355              letters--;
00356              squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
00357       }
00358       else if (squeezedWidth > labelWidth)
00359       {
00360              // we estimated too long
00361              // remove letters while text > label
00362           do
00363           {
00364                letters--;
00365                 squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
00366                 squeezedWidth = fm.width(squeezedText);
00367              } while (squeezedWidth > labelWidth);
00368           }
00369 
00370       if (letters < 5)
00371       {
00372              // too few letters added -> we give up squeezing
00373           QLineEdit::setText(fullText);
00374       }
00375       else
00376       {
00377           QLineEdit::setText(squeezedText);
00378              d->squeezedStart = letters;
00379              d->squeezedEnd = fullText.length() - letters;
00380           }
00381 
00382           QToolTip::remove( this );
00383           QToolTip::add( this, fullText );
00384 
00385     }
00386     else
00387     {
00388       QLineEdit::setText(fullText);
00389 
00390           QToolTip::remove( this );
00391           QToolTip::hide();
00392        }
00393 
00394        setCursorPosition(0);
00395 }
00396 
00397 void KLineEdit::copy() const
00398 {
00399    if (!d->squeezedText.isEmpty() && d->squeezedStart)
00400    {
00401       int start, end;
00402       KLineEdit *that = const_cast<KLineEdit *>(this);
00403       if (!that->getSelection(&start, &end))
00404          return;
00405       if (start >= d->squeezedStart+3)
00406          start = start - 3 - d->squeezedStart + d->squeezedEnd;
00407       else if (start > d->squeezedStart)
00408          start = d->squeezedStart;
00409       if (end >= d->squeezedStart+3)
00410          end = end - 3 - d->squeezedStart + d->squeezedEnd;
00411       else if (end > d->squeezedStart)
00412          end = d->squeezedEnd;
00413       if (start == end)
00414          return;
00415       QString t = d->squeezedText;
00416       t = t.mid(start, end - start);
00417       disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
00418       QApplication::clipboard()->setText( t );
00419       connect( QApplication::clipboard(), SIGNAL(selectionChanged()), this,
00420                SLOT(clipboardChanged()) );
00421       return;
00422    }
00423 
00424    QLineEdit::copy();
00425 }
00426 
00427 void KLineEdit::resizeEvent( QResizeEvent * ev )
00428 {
00429     if (!d->squeezedText.isEmpty())
00430         setSqueezedText();
00431 
00432     QLineEdit::resizeEvent(ev);
00433 }
00434 
00435 void KLineEdit::keyPressEvent( QKeyEvent *e )
00436 {
00437     KKey key( e );
00438 
00439     if ( KStdAccel::copy().contains( key ) )
00440     {
00441         copy();
00442         return;
00443     }
00444     else if ( KStdAccel::paste().contains( key ) )
00445     {
00446         paste();
00447         return;
00448     }
00449     else if ( KStdAccel::pasteSelection().contains( key ) )
00450     {
00451         QString text = QApplication::clipboard()->text( QClipboard::Selection);
00452         insert( text );
00453         deselect();
00454         return;
00455     }
00456 
00457     else if ( KStdAccel::cut().contains( key ) )
00458     {
00459         cut();
00460         return;
00461     }
00462     else if ( KStdAccel::undo().contains( key ) )
00463     {
00464         undo();
00465         return;
00466     }
00467     else if ( KStdAccel::redo().contains( key ) )
00468     {
00469         redo();
00470         return;
00471     }
00472     else if ( KStdAccel::deleteWordBack().contains( key ) )
00473     {
00474         cursorWordBackward(true);
00475         if ( hasSelectedText() )
00476             del();
00477 
00478         e->accept();
00479         return;
00480     }
00481     else if ( KStdAccel::deleteWordForward().contains( key ) )
00482     {
00483         // Workaround for QT bug where
00484         cursorWordForward(true);
00485         if ( hasSelectedText() )
00486             del();
00487 
00488         e->accept();
00489         return;
00490     }
00491     else if ( KStdAccel::backwardWord().contains( key ) )
00492     {
00493       cursorWordBackward(false);
00494       e->accept();
00495       return;
00496     }
00497     else if ( KStdAccel::forwardWord().contains( key ) )
00498     {
00499       cursorWordForward(false);
00500       e->accept();
00501       return;
00502     }
00503     else if ( KStdAccel::beginningOfLine().contains( key ) )
00504     {
00505       home(false);
00506       e->accept();
00507       return;
00508     }
00509     else if ( KStdAccel::endOfLine().contains( key ) )
00510     {
00511       end(false);
00512       e->accept();
00513       return;
00514     }
00515 
00516 
00517     // Filter key-events if EchoMode is normal and
00518     // completion mode is not set to CompletionNone
00519     if ( echoMode() == QLineEdit::Normal &&
00520          completionMode() != KGlobalSettings::CompletionNone )
00521     {
00522         KeyBindingMap keys = getKeyBindings();
00523         KGlobalSettings::Completion mode = completionMode();
00524         bool noModifier = (e->state() == NoButton ||
00525                            e->state() == ShiftButton ||
00526                            e->state() == Keypad);
00527 
00528         if ( (mode == KGlobalSettings::CompletionAuto ||
00529               mode == KGlobalSettings::CompletionPopupAuto ||
00530               mode == KGlobalSettings::CompletionMan) && noModifier )
00531         {
00532             if ( !d->userSelection && hasSelectedText() &&
00533                  ( e->key() == Key_Right || e->key() == Key_Left ) &&
00534                  e->state()==NoButton )
00535             {
00536                 QString old_txt = text();
00537                 d->disableRestoreSelection = true;
00538                 int start,end;
00539                 getSelection(&start, &end);
00540 
00541                 deselect();
00542                 QLineEdit::keyPressEvent ( e );
00543                 int cPosition=cursorPosition();
00544                 if (e->key() ==Key_Right && cPosition > start )
00545                     validateAndSet(old_txt, cPosition, cPosition, old_txt.length());
00546                 else
00547                     validateAndSet(old_txt, cPosition, start, old_txt.length());
00548 
00549                 d->disableRestoreSelection = false;
00550                 return;
00551             }
00552 
00553             if ( e->key() == Key_Escape )
00554             {
00555                 if (hasSelectedText() && !d->userSelection )
00556                 {
00557                     del();
00558                     setUserSelection(true);
00559                 }
00560 
00561                 // Don't swallow the Escape press event for the case
00562                 // of dialogs, which have Escape associated to Cancel
00563                 e->ignore();
00564                 return;
00565             }
00566 
00567         }
00568 
00569         if ( (mode == KGlobalSettings::CompletionAuto ||
00570               mode == KGlobalSettings::CompletionMan) && noModifier )
00571         {
00572             QString keycode = e->text();
00573             if ( !keycode.isEmpty() && (keycode.unicode()->isPrint() ||
00574                 e->key() == Key_Backspace || e->key() == Key_Delete ) )
00575             {
00576                 bool hasUserSelection=d->userSelection;
00577                 bool hadSelection=hasSelectedText();
00578 
00579                 bool cursorNotAtEnd=false;
00580 
00581                 int start,end;
00582                 getSelection(&start, &end);
00583                 int cPos = cursorPosition();
00584 
00585                 // When moving the cursor, we want to keep the autocompletion as an
00586                 // autocompletion, so we want to process events at the cursor position
00587                 // as if there was no selection. After processing the key event, we
00588                 // can set the new autocompletion again.
00589                 if ( hadSelection && !hasUserSelection && start>cPos )
00590                 {
00591                     del();
00592                     setCursorPosition(cPos);
00593                     cursorNotAtEnd=true;
00594                 }
00595 
00596                 d->disableRestoreSelection = true;
00597                 QLineEdit::keyPressEvent ( e );
00598                 d->disableRestoreSelection = false;
00599 
00600                 QString txt = text();
00601                 int len = txt.length();
00602                 if ( !hasSelectedText() && len /*&& cursorPosition() == len */)
00603                 {
00604                     if ( e->key() == Key_Backspace )
00605                     {
00606                         if ( hadSelection && !hasUserSelection && !cursorNotAtEnd )
00607                         {
00608                             backspace();
00609                             txt = text();
00610                             len = txt.length();
00611                         }
00612 
00613                         if ( !d->backspacePerformsCompletion || !len )
00614                             d->autoSuggest = false;
00615                     }
00616 
00617                     if (e->key() == Key_Delete )
00618                         d->autoSuggest=false;
00619 
00620                     if ( emitSignals() )
00621                         emit completion( txt );
00622 
00623                     if ( handleSignals() )
00624                         makeCompletion( txt );
00625 
00626                     if(  (e->key() == Key_Backspace || e->key() == Key_Delete) )
00627                         d->autoSuggest=true;
00628 
00629                     e->accept();
00630                 }
00631 
00632                 return;
00633             }
00634 
00635         }
00636 
00637         else if (( mode == KGlobalSettings::CompletionPopup ||
00638                    mode == KGlobalSettings::CompletionPopupAuto ) &&
00639                    noModifier && !e->text().isEmpty() )
00640         {
00641             QString old_txt = text();
00642             bool hasUserSelection=d->userSelection;
00643             bool hadSelection=hasSelectedText();
00644             bool cursorNotAtEnd=false;
00645 
00646             int start,end;
00647             getSelection(&start, &end);
00648             int cPos = cursorPosition();
00649             QString keycode = e->text();
00650 
00651             // When moving the cursor, we want to keep the autocompletion as an
00652             // autocompletion, so we want to process events at the cursor position
00653             // as if there was no selection. After processing the key event, we
00654             // can set the new autocompletion again.
00655             if (hadSelection && !hasUserSelection && start>cPos &&
00656                ( (!keycode.isEmpty() && keycode.unicode()->isPrint()) ||
00657                  e->key() == Key_Backspace || e->key() == Key_Delete ) )
00658             {
00659                 del();
00660                 setCursorPosition(cPos);
00661                 cursorNotAtEnd=true;
00662             }
00663 
00664             uint selectedLength=selectedText().length();
00665 
00666             d->disableRestoreSelection = true;
00667             QLineEdit::keyPressEvent ( e );
00668             d->disableRestoreSelection = false;
00669 
00670             if (( selectedLength != selectedText().length() ) && !hasUserSelection )
00671                 slotRestoreSelectionColors(); // and set userSelection to true
00672 
00673             QString txt = text();
00674             int len = txt.length();
00675 
00676             if ( txt != old_txt && len/* && ( cursorPosition() == len || force )*/ &&
00677                  ( (!keycode.isEmpty() && keycode.unicode()->isPrint()) ||
00678                    e->key() == Key_Backspace || e->key() == Key_Delete) )
00679             {
00680                 if ( e->key() == Key_Backspace )
00681                 {
00682                     if ( hadSelection && !hasUserSelection && !cursorNotAtEnd )
00683                     {
00684                         backspace();
00685                         txt = text();
00686                         len = txt.length();
00687                     }
00688 
00689                     if ( !d->backspacePerformsCompletion )
00690                         d->autoSuggest = false;
00691                 }
00692 
00693                 if (e->key() == Key_Delete )
00694                     d->autoSuggest=false;
00695 
00696                 if ( d->completionBox )
00697                   d->completionBox->setCancelledText( txt );
00698     
00699                 if ( emitSignals() )
00700                   emit completion( txt ); // emit when requested...
00701 
00702                 if ( handleSignals() ) {
00703                   makeCompletion( txt );  // handle when requested...
00704                 }
00705 
00706                 if ( (e->key() == Key_Backspace || e->key() == Key_Delete ) &&
00707                     mode == KGlobalSettings::CompletionPopupAuto )
00708                   d->autoSuggest=true;
00709 
00710                 e->accept();
00711             }
00712             else if (!len && d->completionBox && d->completionBox->isVisible())
00713                 d->completionBox->hide();
00714 
00715             return;
00716         }
00717 
00718         else if ( mode == KGlobalSettings::CompletionShell )
00719         {
00720             // Handles completion.
00721             KShortcut cut;
00722             if ( keys[TextCompletion].isNull() )
00723                 cut = KStdAccel::shortcut(KStdAccel::TextCompletion);
00724             else
00725                 cut = keys[TextCompletion];
00726 
00727             if ( cut.contains( key ) )
00728             {
00729                 // Emit completion if the completion mode is CompletionShell
00730                 // and the cursor is at the end of the string.
00731                 QString txt = text();
00732                 int len = txt.length();
00733                 if ( cursorPosition() == len && len != 0 )
00734                 {
00735                     if ( emitSignals() )
00736                         emit completion( txt );
00737                     if ( handleSignals() )
00738                         makeCompletion( txt );
00739                     return;
00740                 }
00741             }
00742             else if ( d->completionBox )
00743                 d->completionBox->hide();
00744         }
00745 
00746         // handle rotation
00747         if ( mode != KGlobalSettings::CompletionNone )
00748         {
00749             // Handles previous match
00750             KShortcut cut;
00751             if ( keys[PrevCompletionMatch].isNull() )
00752                 cut = KStdAccel::shortcut(KStdAccel::PrevCompletion);
00753             else
00754                 cut = keys[PrevCompletionMatch];
00755 
00756             if ( cut.contains( key ) )
00757             {
00758                 if ( emitSignals() )
00759                     emit textRotation( KCompletionBase::PrevCompletionMatch );
00760                 if ( handleSignals() )
00761                     rotateText( KCompletionBase::PrevCompletionMatch );
00762                 return;
00763             }
00764 
00765             // Handles next match
00766             if ( keys[NextCompletionMatch].isNull() )
00767                 cut = KStdAccel::shortcut(KStdAccel::NextCompletion);
00768             else
00769                 cut = keys[NextCompletionMatch];
00770 
00771             if ( cut.contains( key ) )
00772             {
00773                 if ( emitSignals() )
00774                     emit textRotation( KCompletionBase::NextCompletionMatch );
00775                 if ( handleSignals() )
00776                     rotateText( KCompletionBase::NextCompletionMatch );
00777                 return;
00778             }
00779         }
00780 
00781         // substring completion
00782         if ( compObj() )
00783         {
00784             KShortcut cut;
00785             if ( keys[SubstringCompletion].isNull() )
00786                 cut = KStdAccel::shortcut(KStdAccel::SubstringCompletion);
00787             else
00788                 cut = keys[SubstringCompletion];
00789 
00790             if ( cut.contains( key ) )
00791             {
00792                 if ( emitSignals() )
00793                     emit substringCompletion( text() );
00794                 if ( handleSignals() )
00795                 {
00796                     setCompletedItems( compObj()->substringCompletion(text()));
00797                     e->accept();
00798                 }
00799                 return;
00800             }
00801         }
00802     }
00803 
00804     uint selectedLength = selectedText().length();
00805 
00806     // Let QLineEdit handle any other keys events.
00807     QLineEdit::keyPressEvent ( e );
00808 
00809     if ( selectedLength != selectedText().length() )
00810         slotRestoreSelectionColors(); // and set userSelection to true
00811 }
00812 
00813 void KLineEdit::mouseDoubleClickEvent( QMouseEvent* e )
00814 {
00815     if ( e->button() == Qt::LeftButton  )
00816     {
00817         possibleTripleClick=true;
00818         QTimer::singleShot( QApplication::doubleClickInterval(),this,
00819                             SLOT(tripleClickTimeout()) );
00820     }
00821     QLineEdit::mouseDoubleClickEvent( e );
00822 }
00823 
00824 void KLineEdit::mousePressEvent( QMouseEvent* e )
00825 {
00826     if ( possibleTripleClick && e->button() == Qt::LeftButton )
00827     {
00828         selectAll();
00829         e->accept();
00830         return;
00831     }
00832     QLineEdit::mousePressEvent( e );
00833 }
00834 
00835 void KLineEdit::tripleClickTimeout()
00836 {
00837     possibleTripleClick=false;
00838 }
00839 
00840 void KLineEdit::contextMenuEvent( QContextMenuEvent * e )
00841 {
00842     if ( m_bEnableMenu )
00843         QLineEdit::contextMenuEvent( e );
00844 }
00845 
00846 QPopupMenu *KLineEdit::createPopupMenu()
00847 {
00848     enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll };
00849 
00850     QPopupMenu *popup = QLineEdit::createPopupMenu();
00851 
00852       int id = popup->idAt(0);
00853       popup->changeItem( id - IdUndo, SmallIconSet("undo"), popup->text( id - IdUndo) );
00854       popup->changeItem( id - IdRedo, SmallIconSet("redo"), popup->text( id - IdRedo) );
00855       popup->changeItem( id - IdCut, SmallIconSet("editcut"), popup->text( id - IdCut) );
00856       popup->changeItem( id - IdCopy, SmallIconSet("editcopy"), popup->text( id - IdCopy) );
00857       popup->changeItem( id - IdPaste, SmallIconSet("editpaste"), popup->text( id - IdPaste) );
00858       popup->changeItem( id - IdClear, SmallIconSet("editclear"), popup->text( id - IdClear) );
00859 
00860     // If a completion object is present and the input
00861     // widget is not read-only, show the Text Completion
00862     // menu item.
00863     if ( compObj() && !isReadOnly() && kapp->authorize("lineedit_text_completion") )
00864     {
00865         QPopupMenu *subMenu = new QPopupMenu( popup );
00866         connect( subMenu, SIGNAL( activated( int ) ),
00867                  this, SLOT( completionMenuActivated( int ) ) );
00868 
00869         popup->insertSeparator();
00870         popup->insertItem( SmallIconSet("completion"), i18n("Text Completion"),
00871                            subMenu );
00872 
00873         subMenu->insertItem( i18n("None"), NoCompletion );
00874         subMenu->insertItem( i18n("Manual"), ShellCompletion );
00875         subMenu->insertItem( i18n("Automatic"), AutoCompletion );
00876         subMenu->insertItem( i18n("Dropdown List"), PopupCompletion );
00877         subMenu->insertItem( i18n("Short Automatic"), ShortAutoCompletion );
00878         subMenu->insertItem( i18n("Dropdown List && Automatic"), PopupAutoCompletion );
00879 
00880         subMenu->setAccel( KStdAccel::completion(), ShellCompletion );
00881 
00882         KGlobalSettings::Completion mode = completionMode();
00883         subMenu->setItemChecked( NoCompletion,
00884                                  mode == KGlobalSettings::CompletionNone );
00885         subMenu->setItemChecked( ShellCompletion,
00886                                  mode == KGlobalSettings::CompletionShell );
00887         subMenu->setItemChecked( PopupCompletion,
00888                                  mode == KGlobalSettings::CompletionPopup );
00889         subMenu->setItemChecked( AutoCompletion,
00890                                  mode == KGlobalSettings::CompletionAuto );
00891         subMenu->setItemChecked( ShortAutoCompletion,
00892                                  mode == KGlobalSettings::CompletionMan );
00893         subMenu->setItemChecked( PopupAutoCompletion,
00894                                  mode == KGlobalSettings::CompletionPopupAuto );
00895         if ( mode != KGlobalSettings::completionMode() )
00896         {
00897             subMenu->insertSeparator();
00898             subMenu->insertItem( i18n("Default"), Default );
00899         }
00900     }
00901 
00902     // ### do we really need this?  Yes, Please do not remove!  This
00903     // allows applications to extend the popup menu without having to
00904     // inherit from this class! (DA)
00905     emit aboutToShowContextMenu( popup );
00906 
00907     return popup;
00908 }
00909 
00910 void KLineEdit::completionMenuActivated( int id )
00911 {
00912     KGlobalSettings::Completion oldMode = completionMode();
00913 
00914     switch ( id )
00915     {
00916         case Default:
00917            setCompletionMode( KGlobalSettings::completionMode() );
00918            break;
00919         case NoCompletion:
00920            setCompletionMode( KGlobalSettings::CompletionNone );
00921            break;
00922         case AutoCompletion:
00923             setCompletionMode( KGlobalSettings::CompletionAuto );
00924             break;
00925         case ShortAutoCompletion:
00926             setCompletionMode( KGlobalSettings::CompletionMan );
00927             break;
00928         case ShellCompletion:
00929             setCompletionMode( KGlobalSettings::CompletionShell );
00930             break;
00931         case PopupCompletion:
00932             setCompletionMode( KGlobalSettings::CompletionPopup );
00933             break;
00934         case PopupAutoCompletion:
00935             setCompletionMode( KGlobalSettings::CompletionPopupAuto );
00936             break;
00937         default:
00938             return;
00939     }
00940 
00941     if ( oldMode != completionMode() )
00942     {
00943         if ( (oldMode == KGlobalSettings::CompletionPopup ||
00944               oldMode == KGlobalSettings::CompletionPopupAuto ) &&
00945               d->completionBox && d->completionBox->isVisible() )
00946             d->completionBox->hide();
00947         emit completionModeChanged( completionMode() );
00948     }
00949 }
00950 
00951 void KLineEdit::drawContents( QPainter *p )
00952 {
00953     QLineEdit::drawContents( p );
00954 
00955     if ( d->drawClickMsg && !hasFocus() ) {
00956         QPen tmp = p->pen();
00957         p->setPen( palette().color( QPalette::Disabled, QColorGroup::Text ) );
00958         QRect cr = contentsRect();
00959 
00960         // Add two pixel margin on the left side
00961         cr.rLeft() += 3;
00962         p->drawText( cr, AlignAuto | AlignVCenter, d->clickMessage );
00963         p->setPen( tmp );
00964     }
00965 }
00966 
00967 void KLineEdit::dropEvent(QDropEvent *e)
00968 {
00969     d->drawClickMsg = false;
00970     KURL::List urlList;
00971     if( d->handleURLDrops && KURLDrag::decode( e, urlList ) )
00972     {
00973         QString dropText = text();
00974         KURL::List::ConstIterator it;
00975         for( it = urlList.begin() ; it != urlList.end() ; ++it )
00976         {
00977             if(!dropText.isEmpty())
00978                 dropText+=' ';
00979 
00980             dropText += (*it).prettyURL();
00981         }
00982 
00983         validateAndSet( dropText, dropText.length(), 0, 0);
00984 
00985         e->accept();
00986     }
00987     else
00988         QLineEdit::dropEvent(e);
00989 }
00990 
00991 bool KLineEdit::eventFilter( QObject* o, QEvent* ev )
00992 {
00993     if( o == this )
00994     {
00995         KCursor::autoHideEventFilter( this, ev );
00996         if ( ev->type() == QEvent::AccelOverride )
00997         {
00998             QKeyEvent *e = static_cast<QKeyEvent *>( ev );
00999             if (overrideAccel (e))
01000             {
01001                 e->accept();
01002                 return true;
01003             }
01004         }
01005         else if( ev->type() == QEvent::KeyPress )
01006         {
01007             QKeyEvent *e = static_cast<QKeyEvent *>( ev );
01008 
01009             if( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )
01010             {
01011                 bool trap = d->completionBox && d->completionBox->isVisible();
01012 
01013                 bool stopEvent = trap || (d->grabReturnKeyEvents &&
01014                                           (e->state() == NoButton ||
01015                                            e->state() == Keypad));
01016 
01017                 // Qt will emit returnPressed() itself if we return false
01018                 if ( stopEvent )
01019                 {
01020                   emit QLineEdit::returnPressed();
01021                   e->accept ();
01022                 }
01023 
01024                 emit returnPressed( displayText() );
01025 
01026                 if ( trap )
01027                 {
01028                     d->completionBox->hide();
01029                     deselect();
01030                     setCursorPosition(text().length());
01031                 }
01032 
01033                 // Eat the event if the user asked for it, or if a completionbox was visible
01034                 return stopEvent;
01035             }
01036         }
01037     }
01038     return QLineEdit::eventFilter( o, ev );
01039 }
01040 
01041 
01042 void KLineEdit::setURLDropsEnabled(bool enable)
01043 {
01044     d->handleURLDrops=enable;
01045 }
01046 
01047 bool KLineEdit::isURLDropsEnabled() const
01048 {
01049     return d->handleURLDrops;
01050 }
01051 
01052 void KLineEdit::setTrapReturnKey( bool grab )
01053 {
01054     d->grabReturnKeyEvents = grab;
01055 }
01056 
01057 bool KLineEdit::trapReturnKey() const
01058 {
01059     return d->grabReturnKeyEvents;
01060 }
01061 
01062 void KLineEdit::setURL( const KURL& url )
01063 {
01064     setText( url.prettyURL() );
01065 }
01066 
01067 void KLineEdit::setCompletionBox( KCompletionBox *box )
01068 {
01069     if ( d->completionBox )
01070         return;
01071 
01072     d->completionBox = box;
01073     if ( handleSignals() )
01074     {
01075         connect( d->completionBox, SIGNAL(highlighted( const QString& )),
01076                  SLOT(setTextWorkaround( const QString& )) );
01077         connect( d->completionBox, SIGNAL(userCancelled( const QString& )),
01078                  SLOT(userCancelled( const QString& )) );
01079 
01080         // TODO: we need our own slot, and to call setModified(true) if Qt4 has that.
01081         connect( d->completionBox, SIGNAL( activated( const QString& )),
01082                  SIGNAL(completionBoxActivated( const QString& )) );
01083     }
01084 }
01085 
01086 void KLineEdit::userCancelled(const QString & cancelText)
01087 {
01088     if ( completionMode() != KGlobalSettings::CompletionPopupAuto )
01089     {
01090       // TODO: this sets modified==false. But maybe it was true before...
01091       setText(cancelText);
01092     }
01093     else if (hasSelectedText() )
01094     {
01095       if (d->userSelection)
01096         deselect();
01097       else
01098       {
01099         d->autoSuggest=false;
01100         int start,end;
01101         getSelection(&start, &end);
01102         QString s=text().remove(start, end-start+1);
01103         validateAndSet(s,start,s.length(),s.length());
01104         d->autoSuggest=true;
01105       }
01106     }
01107 }
01108 
01109 bool KLineEdit::overrideAccel (const QKeyEvent* e)
01110 {
01111     KShortcut scKey;
01112 
01113     KKey key( e );
01114     KeyBindingMap keys = getKeyBindings();
01115 
01116     if (keys[TextCompletion].isNull())
01117         scKey = KStdAccel::shortcut(KStdAccel::TextCompletion);
01118     else
01119         scKey = keys[TextCompletion];
01120 
01121     if (scKey.contains( key ))
01122         return true;
01123 
01124     if (keys[NextCompletionMatch].isNull())
01125         scKey = KStdAccel::shortcut(KStdAccel::NextCompletion);
01126     else
01127         scKey = keys[NextCompletionMatch];
01128 
01129     if (scKey.contains( key ))
01130         return true;
01131 
01132     if (keys[PrevCompletionMatch].isNull())
01133         scKey = KStdAccel::shortcut(KStdAccel::PrevCompletion);
01134     else
01135         scKey = keys[PrevCompletionMatch];
01136 
01137     if (scKey.contains( key ))
01138         return true;
01139 
01140     // Override all the text manupilation accelerators...
01141     if ( KStdAccel::copy().contains( key ) )
01142         return true;
01143     else if ( KStdAccel::paste().contains( key ) )
01144         return true;
01145     else if ( KStdAccel::cut().contains( key ) )
01146         return true;
01147     else if ( KStdAccel::undo().contains( key ) )
01148         return true;
01149     else if ( KStdAccel::redo().contains( key ) )
01150         return true;
01151     else if (KStdAccel::deleteWordBack().contains( key ))
01152         return true;
01153     else if (KStdAccel::deleteWordForward().contains( key ))
01154         return true;
01155     else if (KStdAccel::forwardWord().contains( key ))
01156         return true;
01157     else if (KStdAccel::backwardWord().contains( key ))
01158         return true;
01159     else if (KStdAccel::beginningOfLine().contains( key ))
01160         return true;
01161     else if (KStdAccel::endOfLine().contains( key ))
01162         return true;
01163 
01164     if (d->completionBox && d->completionBox->isVisible ())
01165     {
01166         int key = e->key();
01167         ButtonState state = e->state();
01168         if ((key == Key_Backtab || key == Key_Tab) &&
01169             (state == NoButton || (state & ShiftButton)))
01170         {
01171             return true;
01172         }
01173     }
01174 
01175 
01176     return false;
01177 }
01178 
01179 void KLineEdit::setCompletedItems( const QStringList& items )
01180 {
01181     setCompletedItems( items, true );
01182 }
01183 
01184 void KLineEdit::setCompletedItems( const QStringList& items, bool autoSuggest )
01185 {
01186     QString txt;
01187     if ( d->completionBox && d->completionBox->isVisible() ) {
01188         // The popup is visible already - do the matching on the initial string,
01189         // not on the currently selected one.
01190         txt = completionBox()->cancelledText();
01191     } else {
01192         txt = text();
01193     }
01194 
01195     if ( !items.isEmpty() &&
01196          !(items.count() == 1 && txt == items.first()) )
01197     {
01198         // create completion box if non-existent
01199         completionBox();
01200 
01201         if ( d->completionBox->isVisible() )
01202         {
01203             bool wasSelected = d->completionBox->isSelected( d->completionBox->currentItem() );
01204             const QString currentSelection = d->completionBox->currentText();
01205             d->completionBox->setItems( items );
01206             QListBoxItem* item = d->completionBox->findItem( currentSelection, Qt::ExactMatch );
01207             // If no item is selected, that means the listbox hasn't been manipulated by the user yet,
01208             // because it's not possible otherwise to have no selected item. In such case make
01209             // always the first item current and unselected, so that the current item doesn't jump.
01210             if( !item || !wasSelected )
01211             {
01212                 wasSelected = false;
01213                 item = d->completionBox->item( 0 );
01214             }
01215             if ( item )
01216             {
01217                 d->completionBox->blockSignals( true );
01218                 d->completionBox->setCurrentItem( item );
01219                 d->completionBox->setSelected( item, wasSelected );
01220                 d->completionBox->blockSignals( false );
01221             }
01222         }
01223         else // completion box not visible yet -> show it
01224         {
01225             if ( !txt.isEmpty() )
01226                 d->completionBox->setCancelledText( txt );
01227             d->completionBox->setItems( items );
01228             d->completionBox->popup();
01229         }
01230 
01231         if ( d->autoSuggest && autoSuggest )
01232         {
01233             int index = items.first().find( txt );
01234             QString newText = items.first().mid( index );
01235             setUserSelection(false);
01236             setCompletedText(newText,true);
01237         }
01238     }
01239     else
01240     {
01241         if ( d->completionBox && d->completionBox->isVisible() )
01242             d->completionBox->hide();
01243     }
01244 }
01245 
01246 KCompletionBox * KLineEdit::completionBox( bool create )
01247 {
01248     if ( create && !d->completionBox ) {
01249         setCompletionBox( new KCompletionBox( this, "completion box" ) );
01250         d->completionBox->setFont(font());
01251     }
01252 
01253     return d->completionBox;
01254 }
01255 
01256 void KLineEdit::setCompletionObject( KCompletion* comp, bool hsig )
01257 {
01258     KCompletion *oldComp = compObj();
01259     if ( oldComp && handleSignals() )
01260         disconnect( oldComp, SIGNAL( matches( const QStringList& )),
01261                     this, SLOT( setCompletedItems( const QStringList& )));
01262 
01263     if ( comp && hsig )
01264       connect( comp, SIGNAL( matches( const QStringList& )),
01265                this, SLOT( setCompletedItems( const QStringList& )));
01266 
01267     KCompletionBase::setCompletionObject( comp, hsig );
01268 }
01269 
01270 // QWidget::create() turns off mouse-Tracking which would break auto-hiding
01271 void KLineEdit::create( WId id, bool initializeWindow, bool destroyOldWindow )
01272 {
01273     QLineEdit::create( id, initializeWindow, destroyOldWindow );
01274     KCursor::setAutoHideCursor( this, true, true );
01275 }
01276 
01277 void KLineEdit::setUserSelection(bool userSelection)
01278 {
01279     QPalette p = palette();
01280 
01281     if (userSelection)
01282     {
01283         p.setColor(QColorGroup::Highlight, d->previousHighlightColor);
01284         p.setColor(QColorGroup::HighlightedText, d->previousHighlightedTextColor);
01285     }
01286     else
01287     {
01288         QColor color=p.color(QPalette::Disabled, QColorGroup::Text);
01289         p.setColor(QColorGroup::HighlightedText, color);
01290         color=p.color(QPalette::Active, QColorGroup::Base);
01291         p.setColor(QColorGroup::Highlight, color);
01292     }
01293 
01294     d->userSelection=userSelection;
01295     setPalette(p);
01296 }
01297 
01298 void KLineEdit::slotRestoreSelectionColors()
01299 {
01300     if (d->disableRestoreSelection)
01301       return;
01302 
01303     setUserSelection(true);
01304 }
01305 
01306 void KLineEdit::clear()
01307 {
01308     setText( QString::null );
01309 }
01310 
01311 void KLineEdit::setTextWorkaround( const QString& text )
01312 {
01313     setText( text );
01314     end( false ); // force cursor at end
01315 }
01316 
01317 QString KLineEdit::originalText() const
01318 {
01319     if ( d->enableSqueezedText && isReadOnly() )
01320         return d->squeezedText;
01321 
01322     return text();
01323 }
01324 
01325 void KLineEdit::focusInEvent( QFocusEvent* ev)
01326 {
01327     if ( d->drawClickMsg ) {
01328         d->drawClickMsg = false;
01329         update();
01330     }
01331 
01332     // Don't selectAll() in QLineEdit::focusInEvent if selection exists
01333     if ( ev->reason() == QFocusEvent::Tab && inputMask().isNull() && hasSelectedText() )
01334         return;
01335     
01336     QLineEdit::focusInEvent(ev);
01337 }
01338 
01339 void KLineEdit::focusOutEvent( QFocusEvent* ev)
01340 {
01341     if ( text().isEmpty() && !d->clickMessage.isEmpty() ) {
01342         d->drawClickMsg = true;
01343         update();
01344     }
01345     QLineEdit::focusOutEvent( ev );
01346 }
01347 
01348 bool KLineEdit::autoSuggest() const
01349 {
01350     return d->autoSuggest;
01351 }
01352 
01353 void KLineEdit::setClickMessage( const QString &msg )
01354 {
01355     d->clickMessage = msg;
01356     update();
01357 }
01358 
01359 QString KLineEdit::clickMessage() const
01360 {
01361     return d->clickMessage;
01362 }
01363 
01364 
01365 void KLineEdit::virtual_hook( int id, void* data )
01366 { KCompletionBase::virtual_hook( id, data ); }
KDE Home | KDE Accessibility Home | Description of Access Keys