summaryrefslogtreecommitdiffstats
path: root/kdeui/klineedit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdeui/klineedit.cpp')
-rw-r--r--kdeui/klineedit.cpp1382
1 files changed, 1382 insertions, 0 deletions
diff --git a/kdeui/klineedit.cpp b/kdeui/klineedit.cpp
new file mode 100644
index 000000000..309bd4283
--- /dev/null
+++ b/kdeui/klineedit.cpp
@@ -0,0 +1,1382 @@
+/* This file is part of the KDE libraries
+
+ Copyright (C) 1997 Sven Radej (sven.radej@iname.com)
+ Copyright (c) 1999 Patrick Ward <PAT_WARD@HP-USA-om5.om.hp.com>
+ Copyright (c) 1999 Preston Brown <pbrown@kde.org>
+
+ Re-designed for KDE 2.x by
+ Copyright (c) 2000, 2001 Dawit Alemayehu <adawit@kde.org>
+ Copyright (c) 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any later
+ version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qclipboard.h>
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include <kconfig.h>
+#include <qtooltip.h>
+#include <kcursor.h>
+#include <klocale.h>
+#include <kstdaccel.h>
+#include <kpopupmenu.h>
+#include <kdebug.h>
+#include <kcompletionbox.h>
+#include <kurl.h>
+#include <kurldrag.h>
+#include <kiconloader.h>
+#include <kapplication.h>
+
+#include "klineedit.h"
+#include "klineedit.moc"
+
+
+class KLineEdit::KLineEditPrivate
+{
+public:
+ KLineEditPrivate()
+ {
+ completionBox = 0L;
+ handleURLDrops = true;
+ grabReturnKeyEvents = false;
+
+ userSelection = true;
+ autoSuggest = false;
+ disableRestoreSelection = false;
+ enableSqueezedText = false;
+
+ if ( !initialized )
+ {
+ KConfigGroup config( KGlobal::config(), "General" );
+ backspacePerformsCompletion = config.readBoolEntry( "Backspace performs completion", false );
+
+ initialized = true;
+ }
+
+ }
+
+ ~KLineEditPrivate()
+ {
+// causes a weird crash in KWord at least, so let Qt delete it for us.
+// delete completionBox;
+ }
+
+ static bool initialized;
+ static bool backspacePerformsCompletion; // Configuration option
+
+ QColor previousHighlightColor;
+ QColor previousHighlightedTextColor;
+
+ bool userSelection: 1;
+ bool autoSuggest : 1;
+ bool disableRestoreSelection: 1;
+ bool handleURLDrops:1;
+ bool grabReturnKeyEvents:1;
+ bool enableSqueezedText:1;
+
+ int squeezedEnd;
+ int squeezedStart;
+ BackgroundMode bgMode;
+ QString squeezedText;
+ KCompletionBox *completionBox;
+
+ QString clickMessage;
+ bool drawClickMsg:1;
+};
+
+bool KLineEdit::KLineEditPrivate::backspacePerformsCompletion = false;
+bool KLineEdit::KLineEditPrivate::initialized = false;
+
+
+KLineEdit::KLineEdit( const QString &string, QWidget *parent, const char *name )
+ :QLineEdit( string, parent, name )
+{
+ init();
+}
+
+KLineEdit::KLineEdit( QWidget *parent, const char *name )
+ :QLineEdit( parent, name )
+{
+ init();
+}
+
+KLineEdit::~KLineEdit ()
+{
+ delete d;
+ d = 0;
+}
+
+void KLineEdit::init()
+{
+ d = new KLineEditPrivate;
+ possibleTripleClick = false;
+ d->bgMode = backgroundMode ();
+
+ // Enable the context menu by default.
+ KLineEdit::setContextMenuEnabled( true );
+ KCursor::setAutoHideCursor( this, true, true );
+ installEventFilter( this );
+
+ KGlobalSettings::Completion mode = completionMode();
+ d->autoSuggest = (mode == KGlobalSettings::CompletionMan ||
+ mode == KGlobalSettings::CompletionPopupAuto ||
+ mode == KGlobalSettings::CompletionAuto);
+ connect( this, SIGNAL(selectionChanged()), this, SLOT(slotRestoreSelectionColors()));
+
+ QPalette p = palette();
+ if ( !d->previousHighlightedTextColor.isValid() )
+ d->previousHighlightedTextColor=p.color(QPalette::Normal,QColorGroup::HighlightedText);
+ if ( !d->previousHighlightColor.isValid() )
+ d->previousHighlightColor=p.color(QPalette::Normal,QColorGroup::Highlight);
+
+ d->drawClickMsg = false;
+}
+
+void KLineEdit::setCompletionMode( KGlobalSettings::Completion mode )
+{
+ KGlobalSettings::Completion oldMode = completionMode();
+
+ if ( oldMode != mode && (oldMode == KGlobalSettings::CompletionPopup ||
+ oldMode == KGlobalSettings::CompletionPopupAuto ) &&
+ d->completionBox && d->completionBox->isVisible() )
+ d->completionBox->hide();
+
+ // If the widgets echo mode is not Normal, no completion
+ // feature will be enabled even if one is requested.
+ if ( echoMode() != QLineEdit::Normal )
+ mode = KGlobalSettings::CompletionNone; // Override the request.
+
+ if ( kapp && !kapp->authorize("lineedit_text_completion") )
+ mode = KGlobalSettings::CompletionNone;
+
+ if ( mode == KGlobalSettings::CompletionPopupAuto ||
+ mode == KGlobalSettings::CompletionAuto ||
+ mode == KGlobalSettings::CompletionMan )
+ d->autoSuggest = true;
+ else
+ d->autoSuggest = false;
+
+ KCompletionBase::setCompletionMode( mode );
+}
+
+void KLineEdit::setCompletedText( const QString& t, bool marked )
+{
+ if ( !d->autoSuggest )
+ return;
+
+ QString txt = text();
+
+ if ( t != txt )
+ {
+ int start = marked ? txt.length() : t.length();
+ validateAndSet( t, cursorPosition(), start, t.length() );
+ setUserSelection(false);
+ }
+ else
+ setUserSelection(true);
+
+}
+
+void KLineEdit::setCompletedText( const QString& text )
+{
+ KGlobalSettings::Completion mode = completionMode();
+ bool marked = ( mode == KGlobalSettings::CompletionAuto ||
+ mode == KGlobalSettings::CompletionMan ||
+ mode == KGlobalSettings::CompletionPopup ||
+ mode == KGlobalSettings::CompletionPopupAuto );
+ setCompletedText( text, marked );
+}
+
+void KLineEdit::rotateText( KCompletionBase::KeyBindingType type )
+{
+ KCompletion* comp = compObj();
+ if ( comp &&
+ (type == KCompletionBase::PrevCompletionMatch ||
+ type == KCompletionBase::NextCompletionMatch ) )
+ {
+ QString input;
+
+ if (type == KCompletionBase::PrevCompletionMatch)
+ comp->previousMatch();
+ else
+ comp->nextMatch();
+
+ // Skip rotation if previous/next match is null or the same text
+ if ( input.isNull() || input == displayText() )
+ return;
+ setCompletedText( input, hasSelectedText() );
+ }
+}
+
+void KLineEdit::makeCompletion( const QString& text )
+{
+ KCompletion *comp = compObj();
+ KGlobalSettings::Completion mode = completionMode();
+
+ if ( !comp || mode == KGlobalSettings::CompletionNone )
+ return; // No completion object...
+
+ QString match = comp->makeCompletion( text );
+
+ if ( mode == KGlobalSettings::CompletionPopup ||
+ mode == KGlobalSettings::CompletionPopupAuto )
+ {
+ if ( match.isNull() )
+ {
+ if ( d->completionBox )
+ {
+ d->completionBox->hide();
+ d->completionBox->clear();
+ }
+ }
+ else
+ setCompletedItems( comp->allMatches() );
+ }
+ else // Auto, ShortAuto (Man) and Shell
+ {
+ // all other completion modes
+ // If no match or the same match, simply return without completing.
+ if ( match.isNull() || match == text )
+ return;
+
+ if ( mode != KGlobalSettings::CompletionShell )
+ setUserSelection(false);
+
+ if ( d->autoSuggest )
+ setCompletedText( match );
+ }
+}
+
+void KLineEdit::setReadOnly(bool readOnly)
+{
+ // Do not do anything if nothing changed...
+ if (readOnly == isReadOnly ())
+ return;
+
+ QLineEdit::setReadOnly (readOnly);
+
+ if (readOnly)
+ {
+ d->bgMode = backgroundMode ();
+ setBackgroundMode (Qt::PaletteBackground);
+ if (d->enableSqueezedText && d->squeezedText.isEmpty())
+ {
+ d->squeezedText = text();
+ setSqueezedText();
+ }
+ }
+ else
+ {
+ if (!d->squeezedText.isEmpty())
+ {
+ setText(d->squeezedText);
+ d->squeezedText = QString::null;
+ }
+ setBackgroundMode (d->bgMode);
+ }
+}
+
+void KLineEdit::setSqueezedText( const QString &text)
+{
+ setEnableSqueezedText(true);
+ setText(text);
+}
+
+void KLineEdit::setEnableSqueezedText( bool enable )
+{
+ d->enableSqueezedText = enable;
+}
+
+bool KLineEdit::isSqueezedTextEnabled() const
+{
+ return d->enableSqueezedText;
+}
+
+void KLineEdit::setText( const QString& text )
+{
+ d->drawClickMsg = text.isEmpty() && !d->clickMessage.isEmpty();
+ update();
+
+ if( d->enableSqueezedText && isReadOnly() )
+ {
+ d->squeezedText = text;
+ setSqueezedText();
+ return;
+ }
+
+ QLineEdit::setText( text );
+}
+
+void KLineEdit::setSqueezedText()
+{
+ d->squeezedStart = 0;
+ d->squeezedEnd = 0;
+ QString fullText = d->squeezedText;
+ QFontMetrics fm(fontMetrics());
+ int labelWidth = size().width() - 2*frameWidth() - 2;
+ int textWidth = fm.width(fullText);
+
+ if (textWidth > labelWidth)
+ {
+ // start with the dots only
+ QString squeezedText = "...";
+ int squeezedWidth = fm.width(squeezedText);
+
+ // estimate how many letters we can add to the dots on both sides
+ int letters = fullText.length() * (labelWidth - squeezedWidth) / textWidth / 2;
+ squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
+ squeezedWidth = fm.width(squeezedText);
+
+ if (squeezedWidth < labelWidth)
+ {
+ // we estimated too short
+ // add letters while text < label
+ do
+ {
+ letters++;
+ squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
+ squeezedWidth = fm.width(squeezedText);
+ } while (squeezedWidth < labelWidth);
+ letters--;
+ squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
+ }
+ else if (squeezedWidth > labelWidth)
+ {
+ // we estimated too long
+ // remove letters while text > label
+ do
+ {
+ letters--;
+ squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
+ squeezedWidth = fm.width(squeezedText);
+ } while (squeezedWidth > labelWidth);
+ }
+
+ if (letters < 5)
+ {
+ // too few letters added -> we give up squeezing
+ QLineEdit::setText(fullText);
+ }
+ else
+ {
+ QLineEdit::setText(squeezedText);
+ d->squeezedStart = letters;
+ d->squeezedEnd = fullText.length() - letters;
+ }
+
+ QToolTip::remove( this );
+ QToolTip::add( this, fullText );
+
+ }
+ else
+ {
+ QLineEdit::setText(fullText);
+
+ QToolTip::remove( this );
+ QToolTip::hide();
+ }
+
+ setCursorPosition(0);
+}
+
+void KLineEdit::copy() const
+{
+ if( !copySqueezedText(true))
+ QLineEdit::copy();
+}
+
+bool KLineEdit::copySqueezedText(bool clipboard) const
+{
+ if (!d->squeezedText.isEmpty() && d->squeezedStart)
+ {
+ int start, end;
+ KLineEdit *that = const_cast<KLineEdit *>(this);
+ if (!that->getSelection(&start, &end))
+ return false;
+ if (start >= d->squeezedStart+3)
+ start = start - 3 - d->squeezedStart + d->squeezedEnd;
+ else if (start > d->squeezedStart)
+ start = d->squeezedStart;
+ if (end >= d->squeezedStart+3)
+ end = end - 3 - d->squeezedStart + d->squeezedEnd;
+ else if (end > d->squeezedStart)
+ end = d->squeezedEnd;
+ if (start == end)
+ return false;
+ QString t = d->squeezedText;
+ t = t.mid(start, end - start);
+ disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
+ QApplication::clipboard()->setText( t, clipboard ? QClipboard::Clipboard : QClipboard::Selection );
+ connect( QApplication::clipboard(), SIGNAL(selectionChanged()), this,
+ SLOT(clipboardChanged()) );
+ return true;
+ }
+ return false;
+}
+
+void KLineEdit::resizeEvent( QResizeEvent * ev )
+{
+ if (!d->squeezedText.isEmpty())
+ setSqueezedText();
+
+ QLineEdit::resizeEvent(ev);
+}
+
+void KLineEdit::keyPressEvent( QKeyEvent *e )
+{
+ KKey key( e );
+
+ if ( KStdAccel::copy().contains( key ) )
+ {
+ copy();
+ return;
+ }
+ else if ( KStdAccel::paste().contains( key ) )
+ {
+ paste();
+ return;
+ }
+ else if ( KStdAccel::pasteSelection().contains( key ) )
+ {
+ QString text = QApplication::clipboard()->text( QClipboard::Selection);
+ insert( text );
+ deselect();
+ return;
+ }
+
+ else if ( KStdAccel::cut().contains( key ) )
+ {
+ cut();
+ return;
+ }
+ else if ( KStdAccel::undo().contains( key ) )
+ {
+ undo();
+ return;
+ }
+ else if ( KStdAccel::redo().contains( key ) )
+ {
+ redo();
+ return;
+ }
+ else if ( KStdAccel::deleteWordBack().contains( key ) )
+ {
+ cursorWordBackward(true);
+ if ( hasSelectedText() )
+ del();
+
+ e->accept();
+ return;
+ }
+ else if ( KStdAccel::deleteWordForward().contains( key ) )
+ {
+ // Workaround for QT bug where
+ cursorWordForward(true);
+ if ( hasSelectedText() )
+ del();
+
+ e->accept();
+ return;
+ }
+ else if ( KStdAccel::backwardWord().contains( key ) )
+ {
+ cursorWordBackward(false);
+ e->accept();
+ return;
+ }
+ else if ( KStdAccel::forwardWord().contains( key ) )
+ {
+ cursorWordForward(false);
+ e->accept();
+ return;
+ }
+ else if ( KStdAccel::beginningOfLine().contains( key ) )
+ {
+ home(false);
+ e->accept();
+ return;
+ }
+ else if ( KStdAccel::endOfLine().contains( key ) )
+ {
+ end(false);
+ e->accept();
+ return;
+ }
+
+
+ // Filter key-events if EchoMode is normal and
+ // completion mode is not set to CompletionNone
+ if ( echoMode() == QLineEdit::Normal &&
+ completionMode() != KGlobalSettings::CompletionNone )
+ {
+ KeyBindingMap keys = getKeyBindings();
+ KGlobalSettings::Completion mode = completionMode();
+ bool noModifier = (e->state() == NoButton ||
+ e->state() == ShiftButton ||
+ e->state() == Keypad);
+
+ if ( (mode == KGlobalSettings::CompletionAuto ||
+ mode == KGlobalSettings::CompletionPopupAuto ||
+ mode == KGlobalSettings::CompletionMan) && noModifier )
+ {
+ if ( !d->userSelection && hasSelectedText() &&
+ ( e->key() == Key_Right || e->key() == Key_Left ) &&
+ e->state()==NoButton )
+ {
+ QString old_txt = text();
+ d->disableRestoreSelection = true;
+ int start,end;
+ getSelection(&start, &end);
+
+ deselect();
+ QLineEdit::keyPressEvent ( e );
+ int cPosition=cursorPosition();
+ if (e->key() ==Key_Right && cPosition > start )
+ validateAndSet(old_txt, cPosition, cPosition, old_txt.length());
+ else
+ validateAndSet(old_txt, cPosition, start, old_txt.length());
+
+ d->disableRestoreSelection = false;
+ return;
+ }
+
+ if ( e->key() == Key_Escape )
+ {
+ if (hasSelectedText() && !d->userSelection )
+ {
+ del();
+ setUserSelection(true);
+ }
+
+ // Don't swallow the Escape press event for the case
+ // of dialogs, which have Escape associated to Cancel
+ e->ignore();
+ return;
+ }
+
+ }
+
+ if ( (mode == KGlobalSettings::CompletionAuto ||
+ mode == KGlobalSettings::CompletionMan) && noModifier )
+ {
+ QString keycode = e->text();
+ if ( !keycode.isEmpty() && (keycode.unicode()->isPrint() ||
+ e->key() == Key_Backspace || e->key() == Key_Delete ) )
+ {
+ bool hasUserSelection=d->userSelection;
+ bool hadSelection=hasSelectedText();
+
+ bool cursorNotAtEnd=false;
+
+ int start,end;
+ getSelection(&start, &end);
+ int cPos = cursorPosition();
+
+ // When moving the cursor, we want to keep the autocompletion as an
+ // autocompletion, so we want to process events at the cursor position
+ // as if there was no selection. After processing the key event, we
+ // can set the new autocompletion again.
+ if ( hadSelection && !hasUserSelection && start>cPos )
+ {
+ del();
+ setCursorPosition(cPos);
+ cursorNotAtEnd=true;
+ }
+
+ d->disableRestoreSelection = true;
+ QLineEdit::keyPressEvent ( e );
+ d->disableRestoreSelection = false;
+
+ QString txt = text();
+ int len = txt.length();
+ if ( !hasSelectedText() && len /*&& cursorPosition() == len */)
+ {
+ if ( e->key() == Key_Backspace )
+ {
+ if ( hadSelection && !hasUserSelection && !cursorNotAtEnd )
+ {
+ backspace();
+ txt = text();
+ len = txt.length();
+ }
+
+ if ( !d->backspacePerformsCompletion || !len )
+ d->autoSuggest = false;
+ }
+
+ if (e->key() == Key_Delete )
+ d->autoSuggest=false;
+
+ if ( emitSignals() )
+ emit completion( txt );
+
+ if ( handleSignals() )
+ makeCompletion( txt );
+
+ if( (e->key() == Key_Backspace || e->key() == Key_Delete) )
+ d->autoSuggest=true;
+
+ e->accept();
+ }
+
+ return;
+ }
+
+ }
+
+ else if (( mode == KGlobalSettings::CompletionPopup ||
+ mode == KGlobalSettings::CompletionPopupAuto ) &&
+ noModifier && !e->text().isEmpty() )
+ {
+ QString old_txt = text();
+ bool hasUserSelection=d->userSelection;
+ bool hadSelection=hasSelectedText();
+ bool cursorNotAtEnd=false;
+
+ int start,end;
+ getSelection(&start, &end);
+ int cPos = cursorPosition();
+ QString keycode = e->text();
+
+ // When moving the cursor, we want to keep the autocompletion as an
+ // autocompletion, so we want to process events at the cursor position
+ // as if there was no selection. After processing the key event, we
+ // can set the new autocompletion again.
+ if (hadSelection && !hasUserSelection && start>cPos &&
+ ( (!keycode.isEmpty() && keycode.unicode()->isPrint()) ||
+ e->key() == Key_Backspace || e->key() == Key_Delete ) )
+ {
+ del();
+ setCursorPosition(cPos);
+ cursorNotAtEnd=true;
+ }
+
+ uint selectedLength=selectedText().length();
+
+ d->disableRestoreSelection = true;
+ QLineEdit::keyPressEvent ( e );
+ d->disableRestoreSelection = false;
+
+ if (( selectedLength != selectedText().length() ) && !hasUserSelection )
+ slotRestoreSelectionColors(); // and set userSelection to true
+
+ QString txt = text();
+ int len = txt.length();
+
+ if ( txt != old_txt && len/* && ( cursorPosition() == len || force )*/ &&
+ ( (!keycode.isEmpty() && keycode.unicode()->isPrint()) ||
+ e->key() == Key_Backspace || e->key() == Key_Delete) )
+ {
+ if ( e->key() == Key_Backspace )
+ {
+ if ( hadSelection && !hasUserSelection && !cursorNotAtEnd )
+ {
+ backspace();
+ txt = text();
+ len = txt.length();
+ }
+
+ if ( !d->backspacePerformsCompletion )
+ d->autoSuggest = false;
+ }
+
+ if (e->key() == Key_Delete )
+ d->autoSuggest=false;
+
+ if ( d->completionBox )
+ d->completionBox->setCancelledText( txt );
+
+ if ( emitSignals() )
+ emit completion( txt ); // emit when requested...
+
+ if ( handleSignals() ) {
+ makeCompletion( txt ); // handle when requested...
+ }
+
+ if ( (e->key() == Key_Backspace || e->key() == Key_Delete ) &&
+ mode == KGlobalSettings::CompletionPopupAuto )
+ d->autoSuggest=true;
+
+ e->accept();
+ }
+ else if (!len && d->completionBox && d->completionBox->isVisible())
+ d->completionBox->hide();
+
+ return;
+ }
+
+ else if ( mode == KGlobalSettings::CompletionShell )
+ {
+ // Handles completion.
+ KShortcut cut;
+ if ( keys[TextCompletion].isNull() )
+ cut = KStdAccel::shortcut(KStdAccel::TextCompletion);
+ else
+ cut = keys[TextCompletion];
+
+ if ( cut.contains( key ) )
+ {
+ // Emit completion if the completion mode is CompletionShell
+ // and the cursor is at the end of the string.
+ QString txt = text();
+ int len = txt.length();
+ if ( cursorPosition() == len && len != 0 )
+ {
+ if ( emitSignals() )
+ emit completion( txt );
+ if ( handleSignals() )
+ makeCompletion( txt );
+ return;
+ }
+ }
+ else if ( d->completionBox )
+ d->completionBox->hide();
+ }
+
+ // handle rotation
+ if ( mode != KGlobalSettings::CompletionNone )
+ {
+ // Handles previous match
+ KShortcut cut;
+ if ( keys[PrevCompletionMatch].isNull() )
+ cut = KStdAccel::shortcut(KStdAccel::PrevCompletion);
+ else
+ cut = keys[PrevCompletionMatch];
+
+ if ( cut.contains( key ) )
+ {
+ if ( emitSignals() )
+ emit textRotation( KCompletionBase::PrevCompletionMatch );
+ if ( handleSignals() )
+ rotateText( KCompletionBase::PrevCompletionMatch );
+ return;
+ }
+
+ // Handles next match
+ if ( keys[NextCompletionMatch].isNull() )
+ cut = KStdAccel::shortcut(KStdAccel::NextCompletion);
+ else
+ cut = keys[NextCompletionMatch];
+
+ if ( cut.contains( key ) )
+ {
+ if ( emitSignals() )
+ emit textRotation( KCompletionBase::NextCompletionMatch );
+ if ( handleSignals() )
+ rotateText( KCompletionBase::NextCompletionMatch );
+ return;
+ }
+ }
+
+ // substring completion
+ if ( compObj() )
+ {
+ KShortcut cut;
+ if ( keys[SubstringCompletion].isNull() )
+ cut = KStdAccel::shortcut(KStdAccel::SubstringCompletion);
+ else
+ cut = keys[SubstringCompletion];
+
+ if ( cut.contains( key ) )
+ {
+ if ( emitSignals() )
+ emit substringCompletion( text() );
+ if ( handleSignals() )
+ {
+ setCompletedItems( compObj()->substringCompletion(text()));
+ e->accept();
+ }
+ return;
+ }
+ }
+ }
+
+ uint selectedLength = selectedText().length();
+
+ // Let QLineEdit handle any other keys events.
+ QLineEdit::keyPressEvent ( e );
+
+ if ( selectedLength != selectedText().length() )
+ slotRestoreSelectionColors(); // and set userSelection to true
+}
+
+void KLineEdit::mouseDoubleClickEvent( QMouseEvent* e )
+{
+ if ( e->button() == Qt::LeftButton )
+ {
+ possibleTripleClick=true;
+ QTimer::singleShot( QApplication::doubleClickInterval(),this,
+ SLOT(tripleClickTimeout()) );
+ }
+ QLineEdit::mouseDoubleClickEvent( e );
+}
+
+void KLineEdit::mousePressEvent( QMouseEvent* e )
+{
+ if ( possibleTripleClick && e->button() == Qt::LeftButton )
+ {
+ selectAll();
+ e->accept();
+ return;
+ }
+ QLineEdit::mousePressEvent( e );
+}
+
+void KLineEdit::mouseReleaseEvent( QMouseEvent* e )
+{
+ QLineEdit::mouseReleaseEvent( e );
+ if (QApplication::clipboard()->supportsSelection() ) {
+ if ( e->button() == LeftButton ) {
+ // Fix copying of squeezed text if needed
+ copySqueezedText( false );
+ }
+ }
+}
+
+void KLineEdit::tripleClickTimeout()
+{
+ possibleTripleClick=false;
+}
+
+void KLineEdit::contextMenuEvent( QContextMenuEvent * e )
+{
+ if ( m_bEnableMenu )
+ QLineEdit::contextMenuEvent( e );
+}
+
+QPopupMenu *KLineEdit::createPopupMenu()
+{
+ enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll };
+
+ QPopupMenu *popup = QLineEdit::createPopupMenu();
+
+ int id = popup->idAt(0);
+ popup->changeItem( id - IdUndo, SmallIconSet("undo"), popup->text( id - IdUndo) );
+ popup->changeItem( id - IdRedo, SmallIconSet("redo"), popup->text( id - IdRedo) );
+ popup->changeItem( id - IdCut, SmallIconSet("editcut"), popup->text( id - IdCut) );
+ popup->changeItem( id - IdCopy, SmallIconSet("editcopy"), popup->text( id - IdCopy) );
+ popup->changeItem( id - IdPaste, SmallIconSet("editpaste"), popup->text( id - IdPaste) );
+ popup->changeItem( id - IdClear, SmallIconSet("editclear"), popup->text( id - IdClear) );
+
+ // If a completion object is present and the input
+ // widget is not read-only, show the Text Completion
+ // menu item.
+ if ( compObj() && !isReadOnly() && kapp->authorize("lineedit_text_completion") )
+ {
+ QPopupMenu *subMenu = new QPopupMenu( popup );
+ connect( subMenu, SIGNAL( activated( int ) ),
+ this, SLOT( completionMenuActivated( int ) ) );
+
+ popup->insertSeparator();
+ popup->insertItem( SmallIconSet("completion"), i18n("Text Completion"),
+ subMenu );
+
+ subMenu->insertItem( i18n("None"), NoCompletion );
+ subMenu->insertItem( i18n("Manual"), ShellCompletion );
+ subMenu->insertItem( i18n("Automatic"), AutoCompletion );
+ subMenu->insertItem( i18n("Dropdown List"), PopupCompletion );
+ subMenu->insertItem( i18n("Short Automatic"), ShortAutoCompletion );
+ subMenu->insertItem( i18n("Dropdown List && Automatic"), PopupAutoCompletion );
+
+ subMenu->setAccel( KStdAccel::completion(), ShellCompletion );
+
+ KGlobalSettings::Completion mode = completionMode();
+ subMenu->setItemChecked( NoCompletion,
+ mode == KGlobalSettings::CompletionNone );
+ subMenu->setItemChecked( ShellCompletion,
+ mode == KGlobalSettings::CompletionShell );
+ subMenu->setItemChecked( PopupCompletion,
+ mode == KGlobalSettings::CompletionPopup );
+ subMenu->setItemChecked( AutoCompletion,
+ mode == KGlobalSettings::CompletionAuto );
+ subMenu->setItemChecked( ShortAutoCompletion,
+ mode == KGlobalSettings::CompletionMan );
+ subMenu->setItemChecked( PopupAutoCompletion,
+ mode == KGlobalSettings::CompletionPopupAuto );
+ if ( mode != KGlobalSettings::completionMode() )
+ {
+ subMenu->insertSeparator();
+ subMenu->insertItem( i18n("Default"), Default );
+ }
+ }
+
+ // ### do we really need this? Yes, Please do not remove! This
+ // allows applications to extend the popup menu without having to
+ // inherit from this class! (DA)
+ emit aboutToShowContextMenu( popup );
+
+ return popup;
+}
+
+void KLineEdit::completionMenuActivated( int id )
+{
+ KGlobalSettings::Completion oldMode = completionMode();
+
+ switch ( id )
+ {
+ case Default:
+ setCompletionMode( KGlobalSettings::completionMode() );
+ break;
+ case NoCompletion:
+ setCompletionMode( KGlobalSettings::CompletionNone );
+ break;
+ case AutoCompletion:
+ setCompletionMode( KGlobalSettings::CompletionAuto );
+ break;
+ case ShortAutoCompletion:
+ setCompletionMode( KGlobalSettings::CompletionMan );
+ break;
+ case ShellCompletion:
+ setCompletionMode( KGlobalSettings::CompletionShell );
+ break;
+ case PopupCompletion:
+ setCompletionMode( KGlobalSettings::CompletionPopup );
+ break;
+ case PopupAutoCompletion:
+ setCompletionMode( KGlobalSettings::CompletionPopupAuto );
+ break;
+ default:
+ return;
+ }
+
+ if ( oldMode != completionMode() )
+ {
+ if ( (oldMode == KGlobalSettings::CompletionPopup ||
+ oldMode == KGlobalSettings::CompletionPopupAuto ) &&
+ d->completionBox && d->completionBox->isVisible() )
+ d->completionBox->hide();
+ emit completionModeChanged( completionMode() );
+ }
+}
+
+void KLineEdit::drawContents( QPainter *p )
+{
+ QLineEdit::drawContents( p );
+
+ if ( d->drawClickMsg && !hasFocus() ) {
+ QPen tmp = p->pen();
+ p->setPen( palette().color( QPalette::Disabled, QColorGroup::Text ) );
+ QRect cr = contentsRect();
+
+ // Add two pixel margin on the left side
+ cr.rLeft() += 3;
+ p->drawText( cr, AlignAuto | AlignVCenter, d->clickMessage );
+ p->setPen( tmp );
+ }
+}
+
+void KLineEdit::dropEvent(QDropEvent *e)
+{
+ d->drawClickMsg = false;
+ KURL::List urlList;
+ if( d->handleURLDrops && KURLDrag::decode( e, urlList ) )
+ {
+ QString dropText = text();
+ KURL::List::ConstIterator it;
+ for( it = urlList.begin() ; it != urlList.end() ; ++it )
+ {
+ if(!dropText.isEmpty())
+ dropText+=' ';
+
+ dropText += (*it).prettyURL();
+ }
+
+ validateAndSet( dropText, dropText.length(), 0, 0);
+
+ e->accept();
+ }
+ else
+ QLineEdit::dropEvent(e);
+}
+
+bool KLineEdit::eventFilter( QObject* o, QEvent* ev )
+{
+ if( o == this )
+ {
+ KCursor::autoHideEventFilter( this, ev );
+ if ( ev->type() == QEvent::AccelOverride )
+ {
+ QKeyEvent *e = static_cast<QKeyEvent *>( ev );
+ if (overrideAccel (e))
+ {
+ e->accept();
+ return true;
+ }
+ }
+ else if( ev->type() == QEvent::KeyPress )
+ {
+ QKeyEvent *e = static_cast<QKeyEvent *>( ev );
+
+ if( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )
+ {
+ bool trap = d->completionBox && d->completionBox->isVisible();
+
+ bool stopEvent = trap || (d->grabReturnKeyEvents &&
+ (e->state() == NoButton ||
+ e->state() == Keypad));
+
+ // Qt will emit returnPressed() itself if we return false
+ if ( stopEvent )
+ {
+ emit QLineEdit::returnPressed();
+ e->accept ();
+ }
+
+ emit returnPressed( displayText() );
+
+ if ( trap )
+ {
+ d->completionBox->hide();
+ deselect();
+ setCursorPosition(text().length());
+ }
+
+ // Eat the event if the user asked for it, or if a completionbox was visible
+ return stopEvent;
+ }
+ }
+ }
+ return QLineEdit::eventFilter( o, ev );
+}
+
+
+void KLineEdit::setURLDropsEnabled(bool enable)
+{
+ d->handleURLDrops=enable;
+}
+
+bool KLineEdit::isURLDropsEnabled() const
+{
+ return d->handleURLDrops;
+}
+
+void KLineEdit::setTrapReturnKey( bool grab )
+{
+ d->grabReturnKeyEvents = grab;
+}
+
+bool KLineEdit::trapReturnKey() const
+{
+ return d->grabReturnKeyEvents;
+}
+
+void KLineEdit::setURL( const KURL& url )
+{
+ setText( url.prettyURL() );
+}
+
+void KLineEdit::setCompletionBox( KCompletionBox *box )
+{
+ if ( d->completionBox )
+ return;
+
+ d->completionBox = box;
+ if ( handleSignals() )
+ {
+ connect( d->completionBox, SIGNAL(highlighted( const QString& )),
+ SLOT(setTextWorkaround( const QString& )) );
+ connect( d->completionBox, SIGNAL(userCancelled( const QString& )),
+ SLOT(userCancelled( const QString& )) );
+
+ // TODO: we need our own slot, and to call setModified(true) if Qt4 has that.
+ connect( d->completionBox, SIGNAL( activated( const QString& )),
+ SIGNAL(completionBoxActivated( const QString& )) );
+ }
+}
+
+void KLineEdit::userCancelled(const QString & cancelText)
+{
+ if ( completionMode() != KGlobalSettings::CompletionPopupAuto )
+ {
+ // TODO: this sets modified==false. But maybe it was true before...
+ setText(cancelText);
+ }
+ else if (hasSelectedText() )
+ {
+ if (d->userSelection)
+ deselect();
+ else
+ {
+ d->autoSuggest=false;
+ int start,end;
+ getSelection(&start, &end);
+ QString s=text().remove(start, end-start+1);
+ validateAndSet(s,start,s.length(),s.length());
+ d->autoSuggest=true;
+ }
+ }
+}
+
+bool KLineEdit::overrideAccel (const QKeyEvent* e)
+{
+ KShortcut scKey;
+
+ KKey key( e );
+ KeyBindingMap keys = getKeyBindings();
+
+ if (keys[TextCompletion].isNull())
+ scKey = KStdAccel::shortcut(KStdAccel::TextCompletion);
+ else
+ scKey = keys[TextCompletion];
+
+ if (scKey.contains( key ))
+ return true;
+
+ if (keys[NextCompletionMatch].isNull())
+ scKey = KStdAccel::shortcut(KStdAccel::NextCompletion);
+ else
+ scKey = keys[NextCompletionMatch];
+
+ if (scKey.contains( key ))
+ return true;
+
+ if (keys[PrevCompletionMatch].isNull())
+ scKey = KStdAccel::shortcut(KStdAccel::PrevCompletion);
+ else
+ scKey = keys[PrevCompletionMatch];
+
+ if (scKey.contains( key ))
+ return true;
+
+ // Override all the text manupilation accelerators...
+ if ( KStdAccel::copy().contains( key ) )
+ return true;
+ else if ( KStdAccel::paste().contains( key ) )
+ return true;
+ else if ( KStdAccel::cut().contains( key ) )
+ return true;
+ else if ( KStdAccel::undo().contains( key ) )
+ return true;
+ else if ( KStdAccel::redo().contains( key ) )
+ return true;
+ else if (KStdAccel::deleteWordBack().contains( key ))
+ return true;
+ else if (KStdAccel::deleteWordForward().contains( key ))
+ return true;
+ else if (KStdAccel::forwardWord().contains( key ))
+ return true;
+ else if (KStdAccel::backwardWord().contains( key ))
+ return true;
+ else if (KStdAccel::beginningOfLine().contains( key ))
+ return true;
+ else if (KStdAccel::endOfLine().contains( key ))
+ return true;
+
+ if (d->completionBox && d->completionBox->isVisible ())
+ {
+ int key = e->key();
+ ButtonState state = e->state();
+ if ((key == Key_Backtab || key == Key_Tab) &&
+ (state == NoButton || (state & ShiftButton)))
+ {
+ return true;
+ }
+ }
+
+
+ return false;
+}
+
+void KLineEdit::setCompletedItems( const QStringList& items )
+{
+ setCompletedItems( items, true );
+}
+
+void KLineEdit::setCompletedItems( const QStringList& items, bool autoSuggest )
+{
+ QString txt;
+ if ( d->completionBox && d->completionBox->isVisible() ) {
+ // The popup is visible already - do the matching on the initial string,
+ // not on the currently selected one.
+ txt = completionBox()->cancelledText();
+ } else {
+ txt = text();
+ }
+
+ if ( !items.isEmpty() &&
+ !(items.count() == 1 && txt == items.first()) )
+ {
+ // create completion box if non-existent
+ completionBox();
+
+ if ( d->completionBox->isVisible() )
+ {
+ bool wasSelected = d->completionBox->isSelected( d->completionBox->currentItem() );
+ const QString currentSelection = d->completionBox->currentText();
+ d->completionBox->setItems( items );
+ QListBoxItem* item = d->completionBox->findItem( currentSelection, Qt::ExactMatch );
+ // If no item is selected, that means the listbox hasn't been manipulated by the user yet,
+ // because it's not possible otherwise to have no selected item. In such case make
+ // always the first item current and unselected, so that the current item doesn't jump.
+ if( !item || !wasSelected )
+ {
+ wasSelected = false;
+ item = d->completionBox->item( 0 );
+ }
+ if ( item )
+ {
+ d->completionBox->blockSignals( true );
+ d->completionBox->setCurrentItem( item );
+ d->completionBox->setSelected( item, wasSelected );
+ d->completionBox->blockSignals( false );
+ }
+ }
+ else // completion box not visible yet -> show it
+ {
+ if ( !txt.isEmpty() )
+ d->completionBox->setCancelledText( txt );
+ d->completionBox->setItems( items );
+ d->completionBox->popup();
+ }
+
+ if ( d->autoSuggest && autoSuggest )
+ {
+ int index = items.first().find( txt );
+ QString newText = items.first().mid( index );
+ setUserSelection(false);
+ setCompletedText(newText,true);
+ }
+ }
+ else
+ {
+ if ( d->completionBox && d->completionBox->isVisible() )
+ d->completionBox->hide();
+ }
+}
+
+KCompletionBox * KLineEdit::completionBox( bool create )
+{
+ if ( create && !d->completionBox ) {
+ setCompletionBox( new KCompletionBox( this, "completion box" ) );
+ d->completionBox->setFont(font());
+ }
+
+ return d->completionBox;
+}
+
+void KLineEdit::setCompletionObject( KCompletion* comp, bool hsig )
+{
+ KCompletion *oldComp = compObj();
+ if ( oldComp && handleSignals() )
+ disconnect( oldComp, SIGNAL( matches( const QStringList& )),
+ this, SLOT( setCompletedItems( const QStringList& )));
+
+ if ( comp && hsig )
+ connect( comp, SIGNAL( matches( const QStringList& )),
+ this, SLOT( setCompletedItems( const QStringList& )));
+
+ KCompletionBase::setCompletionObject( comp, hsig );
+}
+
+// QWidget::create() turns off mouse-Tracking which would break auto-hiding
+void KLineEdit::create( WId id, bool initializeWindow, bool destroyOldWindow )
+{
+ QLineEdit::create( id, initializeWindow, destroyOldWindow );
+ KCursor::setAutoHideCursor( this, true, true );
+}
+
+void KLineEdit::setUserSelection(bool userSelection)
+{
+ QPalette p = palette();
+
+ if (userSelection)
+ {
+ p.setColor(QColorGroup::Highlight, d->previousHighlightColor);
+ p.setColor(QColorGroup::HighlightedText, d->previousHighlightedTextColor);
+ }
+ else
+ {
+ QColor color=p.color(QPalette::Disabled, QColorGroup::Text);
+ p.setColor(QColorGroup::HighlightedText, color);
+ color=p.color(QPalette::Active, QColorGroup::Base);
+ p.setColor(QColorGroup::Highlight, color);
+ }
+
+ d->userSelection=userSelection;
+ setPalette(p);
+}
+
+void KLineEdit::slotRestoreSelectionColors()
+{
+ if (d->disableRestoreSelection)
+ return;
+
+ setUserSelection(true);
+}
+
+void KLineEdit::clear()
+{
+ setText( QString::null );
+}
+
+void KLineEdit::setTextWorkaround( const QString& text )
+{
+ setText( text );
+ end( false ); // force cursor at end
+}
+
+QString KLineEdit::originalText() const
+{
+ if ( d->enableSqueezedText && isReadOnly() )
+ return d->squeezedText;
+
+ return text();
+}
+
+void KLineEdit::focusInEvent( QFocusEvent* ev)
+{
+ if ( d->drawClickMsg ) {
+ d->drawClickMsg = false;
+ update();
+ }
+
+ // Don't selectAll() in QLineEdit::focusInEvent if selection exists
+ if ( ev->reason() == QFocusEvent::Tab && inputMask().isNull() && hasSelectedText() )
+ return;
+
+ QLineEdit::focusInEvent(ev);
+}
+
+void KLineEdit::focusOutEvent( QFocusEvent* ev)
+{
+ if ( text().isEmpty() && !d->clickMessage.isEmpty() ) {
+ d->drawClickMsg = true;
+ update();
+ }
+ QLineEdit::focusOutEvent( ev );
+}
+
+bool KLineEdit::autoSuggest() const
+{
+ return d->autoSuggest;
+}
+
+void KLineEdit::setClickMessage( const QString &msg )
+{
+ d->clickMessage = msg;
+ update();
+}
+
+QString KLineEdit::clickMessage() const
+{
+ return d->clickMessage;
+}
+
+
+void KLineEdit::virtual_hook( int id, void* data )
+{ KCompletionBase::virtual_hook( id, data ); }