klineedit.cpp 58.5 KB
Newer Older
1
2
3
4
5
/* 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>
6

7
   Re-designed for KDE 2.x by
8
9
   Copyright (c) 2000, 2001 Dawit Alemayehu <adawit@kde.org>
   Copyright (c) 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
10
11

   This library is free software; you can redistribute it and/or
12
   modify it under the terms of the GNU Lesser General Public
Dawit Alemayehu's avatar
   
Dawit Alemayehu committed
13
14
15
   License (LGPL) as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later
   version.
16
17
18
19

   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
20
   Lesser General Public License for more details.
21

22
   You should have received a copy of the GNU Lesser General Public License
23
   along with this library; see the file COPYING.LIB.  If not, write to
Nicolas Goutte's avatar
Nicolas Goutte committed
24
25
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
26
27
*/

David Faure's avatar
David Faure committed
28
#include "klineedit.h"
29
#include "klineedit_p.h"
30

Urs Wolfer's avatar
Urs Wolfer committed
31
32
33
#include <kaction.h>
#include <kapplication.h>
#include <kauthorized.h>
34
#include <kconfig.h>
Urs Wolfer's avatar
Urs Wolfer committed
35
#include <kconfiggroup.h>
36
#include <kcursor.h>
37
#include <kdebug.h>
38
#include <kcompletionbox.h>
39
#include <kicontheme.h>
David Faure's avatar
David Faure committed
40
#include <kicon.h>
Urs Wolfer's avatar
Urs Wolfer committed
41
42
#include <klocale.h>
#include <kmenu.h>
43
#include <kstandardaction.h>
Urs Wolfer's avatar
Urs Wolfer committed
44
#include <kstandardshortcut.h>
David Faure's avatar
David Faure committed
45
46
47
48

#include <QtCore/QTimer>
#include <QtGui/QClipboard>
#include <QtGui/QStyleOption>
Urs Wolfer's avatar
Urs Wolfer committed
49
#include <QtGui/QToolTip>
50

51
52
class KLineEditStyle;

David Faure's avatar
David Faure committed
53
class KLineEditPrivate
54
55
{
public:
David Faure's avatar
David Faure committed
56
57
    KLineEditPrivate(KLineEdit* qq)
        : q(qq)
58
    {
59
        completionBox = 0L;
60
        handleURLDrops = true;
61
        grabReturnKeyEvents = false;
62
63
64
65

        userSelection = true;
        autoSuggest = false;
        disableRestoreSelection = false;
66
        enableSqueezedText = false;
67

68
69
        drawClickMsg = false;
        enableClickMsg = false;
70
        threeStars = false;
71
        completionRunning = false;
72
        if (!s_initialized) {
73
            KConfigGroup config( KGlobal::config(), "General" );
74
75
            s_backspacePerformsCompletion = config.readEntry("Backspace performs completion", false);
            s_initialized = true;
76
77
        }

78
79
        clearButton = 0;
        clickInClear = false;
80
        wideEnoughForClear = true;
81
82
83
84
85
86
87
88

        // i18n: Placeholder text in line edit widgets is the text appearing
        // before any user input, briefly explaining to the user what to type
        // (e.g. "Enter search pattern").
        // By default the text is set in italic, which may not be appropriate
        // for some languages and scripts (e.g. for CJK ideographs).
        QString metaMsg = i18nc("Italic placeholder text in line edits: 0 no, 1 yes", "1");
        italicizePlaceholder = (metaMsg.trimmed() != QString('0'));
89
    }
90

91
92
    ~KLineEditPrivate()
    {
93
94
// causes a weird crash in KWord at least, so let Qt delete it for us.
//        delete completionBox;
95
        delete style.data();
Carsten Pfeiffer's avatar
cleanup    
Carsten Pfeiffer committed
96
    }
97

98
99
100
101
102
103
104
105
    void _k_slotSettingsChanged(int category)
    {
        Q_UNUSED(category);

        if (clearButton) {
            clearButton->setAnimationsEnabled(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects);
        }
    }
106

107
    void _k_textChanged(const QString &txt)
108
    {
109
110
111
112
        // COMPAT (as documented): emit userTextChanged whenever textChanged is emitted
        // KDE5: remove userTextChanged signal, textEdited does the same...
        if (!completionRunning && (txt != userText)) {
            userText = txt;
113
#ifndef KDE_NO_DEPRECATED
114
            emit q->userTextChanged(txt);
115
#endif
116
117
118
119
120
121
122
123
124
        }
    }

    // Call this when a completion operation changes the lineedit text
    // "as if it had been edited by the user".
    void _k_updateUserText(const QString &txt)
    {
        if (!completionRunning && (txt != userText)) {
            userText = txt;
125
            q->setModified(true);
126
#ifndef KDE_NO_DEPRECATED
127
            emit q->userTextChanged(txt);
128
#endif
129
130
131
            emit q->textEdited(txt);
            emit q->textChanged(txt);
        }
132
    }
133

134
135
136
137
138
139
140
141
142
143
144
145
    // This is called when the lineedit is readonly.
    // Either from setReadOnly() itself, or when we realize that
    // we became readonly and setReadOnly() wasn't called (because it's not virtual)
    // Typical case: comboBox->lineEdit()->setReadOnly(true)
    void adjustForReadOnly()
    {
        if (style && style.data()->m_overlap) {
            style.data()->m_overlap = 0;
        }
    }


146
147
148
149
150
151
152
    /**
     * Checks whether we should/should not consume a key used as a shortcut.
     * This makes it possible to handle shortcuts in the focused widget before any
     * window-global QAction is triggered.
     */
    bool overrideShortcut(const QKeyEvent* e);

153
154
    static bool s_initialized;
    static bool s_backspacePerformsCompletion; // Configuration option
155

156
157
158
    QColor previousHighlightColor;
    QColor previousHighlightedTextColor;

159
160
161
162
163
    bool userSelection: 1;
    bool autoSuggest : 1;
    bool disableRestoreSelection: 1;
    bool handleURLDrops:1;
    bool grabReturnKeyEvents:1;
164
    bool enableSqueezedText:1;
165
    bool completionRunning:1;
166
167

    int squeezedEnd;
168
    int squeezedStart;
169
    QPalette::ColorRole bgRole;
170
    QString squeezedText;
171
    QString userText;
172
173
174

    QString clickMessage;
    bool enableClickMsg:1;
175
    bool drawClickMsg:1;
176
    bool threeStars:1;
177

178
179
    bool possibleTripleClick :1;  // set in mousePressEvent, deleted in tripleClickTimeout

180
    bool clickInClear:1;
181
    bool wideEnoughForClear:1;
182
    KLineEditButton *clearButton;
183
184
    QWeakPointer<KLineEditStyle> style;
    QString lastStyleClass;
185

186
    KCompletionBox *completionBox;
187

188
189
    bool italicizePlaceholder:1;

190
    QAction *noCompletionAction, *shellCompletionAction, *autoCompletionAction, *popupCompletionAction, *shortAutoCompletionAction, *popupAutoCompletionAction, *defaultAction;
191
192

    QMap<KGlobalSettings::Completion, bool> disableCompletionMap;
David Faure's avatar
David Faure committed
193
    KLineEdit* q;
194
195
};

196
197
198
199
200
201
202
203
204
QStyle *KLineEditStyle::style() const
{
    if (m_subStyle) {
        return m_subStyle.data();
    }

    return KdeUiProxyStyle::style();
}

205
206
QRect KLineEditStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
{
207
  if (element == SE_LineEditContents) {
208
209
      KLineEditStyle *unconstThis = const_cast<KLineEditStyle *>(this);

Andreas Hartmetz's avatar
Andreas Hartmetz committed
210
    if (m_sentinel) {
211
212
213
214
        // we are recursing: we're wrapping a style that wraps us!
        unconstThis->m_subStyle.clear();
    }

Andreas Hartmetz's avatar
Andreas Hartmetz committed
215
    unconstThis->m_sentinel = true;
216
217
    QStyle *s = m_subStyle ? m_subStyle.data() : style();
    QRect rect = s->subElementRect(SE_LineEditContents, option, widget);
Andreas Hartmetz's avatar
Andreas Hartmetz committed
218
    unconstThis->m_sentinel = false;
219

220
221
222
223
224
    if (option->direction == Qt::LeftToRight) {
        return rect.adjusted(0, 0, -m_overlap, 0);
    } else {
        return rect.adjusted(m_overlap, 0, 0, 0);
    }
225
226
227
228
229
  }

  return KdeUiProxyStyle::subElementRect(element, option, widget);
}

230
231
bool KLineEditPrivate::s_backspacePerformsCompletion = false;
bool KLineEditPrivate::s_initialized = false;
232

233

234
KLineEdit::KLineEdit( const QString &string, QWidget *parent )
David Faure's avatar
David Faure committed
235
    : QLineEdit( string, parent ), d(new KLineEditPrivate(this))
236
{
237
    init();
238
239
}

240
KLineEdit::KLineEdit( QWidget *parent )
David Faure's avatar
David Faure committed
241
    : QLineEdit( parent ), d(new KLineEditPrivate(this))
242
{
243
    init();
244
245
}

246

247
248
KLineEdit::~KLineEdit ()
{
249
    delete d;
250
251
}

252
253
void KLineEdit::init()
{
254
    d->possibleTripleClick = false;
255
    d->bgRole = backgroundRole();
256

257
    // Enable the context menu by default.
258
    QLineEdit::setContextMenuPolicy( Qt::DefaultContextMenu );
259
    KCursor::setAutoHideCursor( this, true, true );
260
261
262
263
264
265

    KGlobalSettings::Completion mode = completionMode();
    d->autoSuggest = (mode == KGlobalSettings::CompletionMan ||
                      mode == KGlobalSettings::CompletionPopupAuto ||
                      mode == KGlobalSettings::CompletionAuto);
    connect( this, SIGNAL(selectionChanged()), this, SLOT(slotRestoreSelectionColors()));
Stephan Binner's avatar
Stephan Binner committed
266

267
268
    connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), this, SLOT(_k_slotSettingsChanged(int)));

Urs Wolfer's avatar
Urs Wolfer committed
269
    const QPalette p = palette();
270
    if ( !d->previousHighlightedTextColor.isValid() )
271
      d->previousHighlightedTextColor=p.color(QPalette::Normal,QPalette::HighlightedText);
272
    if ( !d->previousHighlightColor.isValid() )
273
      d->previousHighlightColor=p.color(QPalette::Normal,QPalette::Highlight);
274

275
    d->style = new KLineEditStyle(this);
276
    setStyle(d->style.data());
277

278
    connect(this, SIGNAL(textChanged(QString)), this, SLOT(_k_textChanged(QString)));
279
280
}

281
282
283
QString KLineEdit::clickMessage() const
{
    return d->clickMessage;
284
285
}

286
287
288
289
290
291
292
void KLineEdit::setClearButtonShown(bool show)
{
    if (show) {
        if (d->clearButton) {
            return;
        }

293
        d->clearButton = new KLineEditButton(this);
294
        d->clearButton->setCursor( Qt::ArrowCursor );
295
        d->clearButton->setToolTip( i18nc( "@action:button Clear current text in the line edit", "Clear text" ) );
296

297
        updateClearButtonIcon(text());
298
        updateClearButton();
299
        connect(this, SIGNAL(textChanged(QString)), this, SLOT(updateClearButtonIcon(QString)));
300
    } else {
301
        disconnect(this, SIGNAL(textChanged(QString)), this, SLOT(updateClearButtonIcon(QString)));
302
303
304
        delete d->clearButton;
        d->clearButton = 0;
        d->clickInClear = false;
305
306
307
        if (d->style) {
            d->style.data()->m_overlap = 0;
        }
308
309
310
    }
}

311
bool KLineEdit::isClearButtonShown() const
312
313
314
315
{
    return d->clearButton != 0;
}

316
317
318
319
320
321
322
323
324
325
326
QSize KLineEdit::clearButtonUsedSize() const
{
    QSize s;
    if (d->clearButton) {
        const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this);
        s = d->clearButton->sizeHint();
        s.rwidth() += frameWidth;
    }
    return s;
}

327
// Decides whether to show or hide the icon; called when the text changes
328
329
void KLineEdit::updateClearButtonIcon(const QString& text)
{
330
331
332
333
334
    if (!d->clearButton) {
        return;
    }
    if (isReadOnly()) {
        d->adjustForReadOnly();
335
336
337
        return;
    }

338
    int clearButtonState = KIconLoader::DefaultState;
339

340
    if (d->wideEnoughForClear && text.length() > 0) {
341
        d->clearButton->animateVisible(true);
342
    } else {
343
        d->clearButton->animateVisible(false);
344
345
    }

346
    if (!d->clearButton->pixmap().isNull()) {
347
348
349
        return;
    }

350
    if (layoutDirection() == Qt::LeftToRight) {
Pino Toscano's avatar
Pino Toscano committed
351
        d->clearButton->setPixmap(SmallIcon("edit-clear-locationbar-rtl", 0, clearButtonState));
352
    } else {
353
        d->clearButton->setPixmap(SmallIcon("edit-clear-locationbar-ltr", 0, clearButtonState));
354
    }
355

356
    d->clearButton->setVisible(text.length() > 0);
357
358
}

359
// Determine geometry of clear button. Called initially, and on resizeEvent.
360
361
void KLineEdit::updateClearButton()
{
362
363
364
365
366
    if (!d->clearButton) {
        return;
    }
    if (isReadOnly()) {
        d->adjustForReadOnly();
367
368
369
        return;
    }

370
371
372
373
374
375
376
377
378
379
    const QSize geom = size();
    const int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth,0,this);
    const int buttonWidth = d->clearButton->sizeHint().width();
    const QSize newButtonSize(buttonWidth, geom.height());
    const QFontMetrics fm(font());
    const int em = fm.width("m");

    // make sure we have enough room for the clear button
    // no point in showing it if we can't also see a few characters as well
    const bool wideEnough = geom.width() > 4 * em + buttonWidth + frameWidth;
380
381
382
383
384

    if (newButtonSize != d->clearButton->size()) {
        d->clearButton->resize(newButtonSize);
    }

385
386
387
    if (d->style) {
        d->style.data()->m_overlap = wideEnough ? buttonWidth + frameWidth : 0;
    }
388

389
    if (layoutDirection() == Qt::LeftToRight ) {
390
391
392
393
        d->clearButton->move(geom.width() - frameWidth - buttonWidth - 1, 0);
    } else {
        d->clearButton->move(frameWidth + 1, 0);
    }
394
395
396
397
398
399
400
401

    if (wideEnough != d->wideEnoughForClear) {
        // we may (or may not) have been showing the button, but now our
        // positiong on that matter has shifted, so let's ensure that it
        // is properly visible (or not)
        d->wideEnoughForClear = wideEnough;
        updateClearButtonIcon(text());
    }
402
403
}

Michael Reiher's avatar
Michael Reiher committed
404
void KLineEdit::setCompletionMode( KGlobalSettings::Completion mode )
405
{
406
    KGlobalSettings::Completion oldMode = completionMode();
407

408
    if ( oldMode != mode && (oldMode == KGlobalSettings::CompletionPopup ||
409
410
         oldMode == KGlobalSettings::CompletionPopupAuto ) &&
         d->completionBox && d->completionBox->isVisible() )
411
      d->completionBox->hide();
412

413
    // If the widgets echo mode is not Normal, no completion
414
415
    // feature will be enabled even if one is requested.
    if ( echoMode() != QLineEdit::Normal )
416
        mode = KGlobalSettings::CompletionNone; // Override the request.
417

418
    if ( kapp && !KAuthorized::authorize("lineedit_text_completion") )
419
        mode = KGlobalSettings::CompletionNone;
420

421
422
423
424
425
426
427
    if ( mode == KGlobalSettings::CompletionPopupAuto ||
         mode == KGlobalSettings::CompletionAuto ||
         mode == KGlobalSettings::CompletionMan )
        d->autoSuggest = true;
    else
        d->autoSuggest = false;

428
    KCompletionBase::setCompletionMode( mode );
429
430
}

431
432
433
434
435
void KLineEdit::setCompletionModeDisabled( KGlobalSettings::Completion mode, bool disable )
{
  d->disableCompletionMap[ mode ] = disable;
}

436
void KLineEdit::setCompletedText( const QString& t, bool marked )
437
{
438
439
    if ( !d->autoSuggest )
      return;
Stephan Binner's avatar
Stephan Binner committed
440

Urs Wolfer's avatar
Urs Wolfer committed
441
    const QString txt = text();
Stephan Binner's avatar
Stephan Binner committed
442

Carsten Pfeiffer's avatar
cleanup    
Carsten Pfeiffer committed
443
444
    if ( t != txt )
    {
Heng Liu's avatar
Heng Liu committed
445
        setText(t);
446
447
        if ( marked )
            setSelection(t.length(), txt.length()-t.length());
448
        setUserSelection(false);
Carsten Pfeiffer's avatar
cleanup    
Carsten Pfeiffer committed
449
    }
450
451
452
    else
      setUserSelection(true);

453
454
455
456
457
}

void KLineEdit::setCompletedText( const QString& text )
{
    KGlobalSettings::Completion mode = completionMode();
Urs Wolfer's avatar
Urs Wolfer committed
458
    const bool marked = ( mode == KGlobalSettings::CompletionAuto ||
459
                    mode == KGlobalSettings::CompletionMan ||
460
461
                    mode == KGlobalSettings::CompletionPopup ||
                    mode == KGlobalSettings::CompletionPopupAuto );
462
463
464
    setCompletedText( text, marked );
}

465
void KLineEdit::rotateText( KCompletionBase::KeyBindingType type )
466
{
467
    KCompletion* comp = compObj();
468
    if ( comp &&
469
       (type == KCompletionBase::PrevCompletionMatch ||
470
        type == KCompletionBase::NextCompletionMatch ) )
471
    {
472
473
474
       QString input;

       if (type == KCompletionBase::PrevCompletionMatch)
475
          input = comp->previousMatch();
476
       else
477
          input = comp->nextMatch();
478

Carsten Pfeiffer's avatar
Carsten Pfeiffer committed
479
       // Skip rotation if previous/next match is null or the same text
480
       if ( input.isEmpty() || input == displayText() )
481
            return;
Dirk Mueller's avatar
Dirk Mueller committed
482
       setCompletedText( input, hasSelectedText() );
483
484
485
    }
}

486
void KLineEdit::makeCompletion( const QString& text )
487
{
488
    KCompletion *comp = compObj();
489
    KGlobalSettings::Completion mode = completionMode();
490

491
    if ( !comp || mode == KGlobalSettings::CompletionNone )
Carsten Pfeiffer's avatar
Carsten Pfeiffer committed
492
        return;  // No completion object...
493

494
    const QString match = comp->makeCompletion( text );
495
496
497

    if ( mode == KGlobalSettings::CompletionPopup ||
         mode == KGlobalSettings::CompletionPopupAuto )
498
    {
499
        if ( match.isEmpty() )
500
        {
501
502
            if ( d->completionBox )
            {
503
504
505
506
                d->completionBox->hide();
                d->completionBox->clear();
            }
        }
507
508
509
        else
            setCompletedItems( comp->allMatches() );
    }
510
    else // Auto,  ShortAuto (Man) and Shell
511
    {
512
        // all other completion modes
513
        // If no match or the same match, simply return without completing.
514
        if ( match.isEmpty() || match == text )
Carsten Pfeiffer's avatar
cleanup    
Carsten Pfeiffer committed
515
516
            return;

517
518
519
520
521
        if ( mode != KGlobalSettings::CompletionShell )
            setUserSelection(false);

        if ( d->autoSuggest )
            setCompletedText( match );
522
    }
523
524
}

525
526
void KLineEdit::setReadOnly(bool readOnly)
{
527
    // Do not do anything if nothing changed...
528
    if (readOnly == isReadOnly ()) {
529
      return;
530
    }
531

532
    QLineEdit::setReadOnly(readOnly);
533

534
    if (readOnly) {
535
536
        d->bgRole = backgroundRole();
        setBackgroundRole(QPalette::Window);
537
        if (d->enableSqueezedText && d->squeezedText.isEmpty()) {
538
539
540
            d->squeezedText = text();
            setSqueezedText();
        }
541
542

        if (d->clearButton) {
543
            d->clearButton->animateVisible(false);
544
            d->adjustForReadOnly();
545
        }
546
547
    } else {
        if (!d->squeezedText.isEmpty()) {
Waldo Bastian's avatar
Waldo Bastian committed
548
           setText(d->squeezedText);
Laurent Montel's avatar
Laurent Montel committed
549
           d->squeezedText.clear();
Waldo Bastian's avatar
Waldo Bastian committed
550
        }
551

552
553
        setBackgroundRole(d->bgRole);
        updateClearButton();
554
555
556
    }
}

Waldo Bastian's avatar
Waldo Bastian committed
557
558
void KLineEdit::setSqueezedText( const QString &text)
{
Tobias Koenig's avatar
Changed    
Tobias Koenig committed
559
    setSqueezedTextEnabled(true);
560
    setText(text);
Waldo Bastian's avatar
Waldo Bastian committed
561
562
}

Tobias Koenig's avatar
Changed    
Tobias Koenig committed
563
void KLineEdit::setSqueezedTextEnabled( bool enable )
Waldo Bastian's avatar
Waldo Bastian committed
564
{
565
566
567
    d->enableSqueezedText = enable;
}

Tobias Koenig's avatar
Tobias Koenig committed
568
bool KLineEdit::isSqueezedTextEnabled() const
569
570
571
572
573
574
{
    return d->enableSqueezedText;
}

void KLineEdit::setText( const QString& text )
{
575
576
577
    if( d->enableClickMsg )
    {
          d->drawClickMsg = text.isEmpty();
578
          update();
579
    }
580
    if( d->enableSqueezedText && isReadOnly() )
581
    {
582
583
        d->squeezedText = text;
        setSqueezedText();
584
585
        return;
    }
Stephan Binner's avatar
Stephan Binner committed
586

587
    QLineEdit::setText( text );
Waldo Bastian's avatar
Waldo Bastian committed
588
589
}

590
void KLineEdit::setSqueezedText()
591
592
593
{
    d->squeezedStart = 0;
    d->squeezedEnd = 0;
Urs Wolfer's avatar
Urs Wolfer committed
594
595
596
597
    const QString fullText = d->squeezedText;
    const QFontMetrics fm(fontMetrics());
    const int labelWidth = size().width() - 2*style()->pixelMetric(QStyle::PM_DefaultFrameWidth) - 2;
    const int textWidth = fm.width(fullText);
Stephan Binner's avatar
Stephan Binner committed
598
599

    if (textWidth > labelWidth)
600
    {
Waldo Bastian's avatar
Waldo Bastian committed
601
602
603
604
605
606
607
608
609
          // 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);

Stephan Binner's avatar
Stephan Binner committed
610
      if (squeezedWidth < labelWidth)
611
      {
Waldo Bastian's avatar
Waldo Bastian committed
612
613
             // we estimated too short
             // add letters while text < label
Stephan Binner's avatar
Stephan Binner committed
614
          do
615
          {
Waldo Bastian's avatar
Waldo Bastian committed
616
617
618
619
620
621
                letters++;
                squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
                squeezedWidth = fm.width(squeezedText);
             } while (squeezedWidth < labelWidth);
             letters--;
             squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
Stephan Binner's avatar
Stephan Binner committed
622
623
      }
      else if (squeezedWidth > labelWidth)
624
      {
Waldo Bastian's avatar
Waldo Bastian committed
625
626
             // we estimated too long
             // remove letters while text > label
Stephan Binner's avatar
Stephan Binner committed
627
          do
628
          {
Waldo Bastian's avatar
Waldo Bastian committed
629
630
631
632
633
634
               letters--;
                squeezedText = fullText.left(letters) + "..." + fullText.right(letters);
                squeezedWidth = fm.width(squeezedText);
             } while (squeezedWidth > labelWidth);
          }

Stephan Binner's avatar
Stephan Binner committed
635
      if (letters < 5)
636
      {
Waldo Bastian's avatar
Waldo Bastian committed
637
             // too few letters added -> we give up squeezing
638
          QLineEdit::setText(fullText);
Stephan Binner's avatar
Stephan Binner committed
639
640
      }
      else
641
642
      {
          QLineEdit::setText(squeezedText);
Waldo Bastian's avatar
Waldo Bastian committed
643
644
645
646
             d->squeezedStart = letters;
             d->squeezedEnd = fullText.length() - letters;
          }

Pino Toscano's avatar
Pino Toscano committed
647
          setToolTip( fullText );
Waldo Bastian's avatar
Waldo Bastian committed
648

Stephan Binner's avatar
Stephan Binner committed
649
650
    }
    else
651
652
    {
      QLineEdit::setText(fullText);
Waldo Bastian's avatar
Waldo Bastian committed
653

654
655
656
      this->setToolTip( "" );
      QToolTip::showText(pos(), QString()); // hide
    }
Stephan Binner's avatar
Stephan Binner committed
657

658
    setCursorPosition(0);
659
}
660
661

void KLineEdit::copy() const
662
{
663
664
    if( !copySqueezedText(true))
        QLineEdit::copy();
665
666
}

667
bool KLineEdit::copySqueezedText(bool clipboard) const
668
669
670
671
{
   if (!d->squeezedText.isEmpty() && d->squeezedStart)
   {
      KLineEdit *that = const_cast<KLineEdit *>(this);
Heng Liu's avatar
Heng Liu committed
672
      if (!that->hasSelectedText())
673
         return false;
Heng Liu's avatar
Heng Liu committed
674
      int start = selectionStart(), end = start + selectedText().length();
675
676
677
678
679
680
681
682
683
      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)
684
         return false;
685
686
687
      QString t = d->squeezedText;
      t = t.mid(start, end - start);
      disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
688
      QApplication::clipboard()->setText( t, clipboard ? QClipboard::Clipboard : QClipboard::Selection );
689
      connect( QApplication::clipboard(), SIGNAL(selectionChanged()), this,
690
               SLOT(_q_clipboardChanged()) );
691
      return true;
692
   }
693
   return false;
694
695
696
697
698
}

void KLineEdit::resizeEvent( QResizeEvent * ev )
{
    if (!d->squeezedText.isEmpty())
Stephan Binner's avatar
Stephan Binner committed
699
700
        setSqueezedText();

701
    updateClearButton();
Waldo Bastian's avatar
Waldo Bastian committed
702
703
704
    QLineEdit::resizeEvent(ev);
}

705

706
void KLineEdit::keyPressEvent( QKeyEvent *e )
707
{
708
    const int key = e->key() | e->modifiers();
Stephan Binner's avatar
Stephan Binner committed
709

710
    if ( KStandardShortcut::copy().contains( key ) )
711
712
713
714
    {
        copy();
        return;
    }
715
    else if ( KStandardShortcut::paste().contains( key ) )
716
    {
717
718
719
      // TODO:
      // we should restore the original text (not autocompleted), otherwise the paste
      // will get into troubles Bug: 134691
Laurent Montel's avatar
Fix:    
Laurent Montel committed
720
721
        if( !isReadOnly() )
          paste();
722
723
        return;
    }
724
    else if ( KStandardShortcut::pasteSelection().contains( key ) )
725
726
727
728
729
730
731
    {
        QString text = QApplication::clipboard()->text( QClipboard::Selection);
        insert( text );
        deselect();
        return;
    }

732
    else if ( KStandardShortcut::cut().contains( key ) )
733
    {
Laurent Montel's avatar
Fix:    
Laurent Montel committed
734
735
        if( !isReadOnly() )
           cut();
736
737
        return;
    }
738
    else if ( KStandardShortcut::undo().contains( key ) )
739
    {
Laurent Montel's avatar
Fix:    
Laurent Montel committed
740
741
        if( !isReadOnly() )
          undo();
742
743
        return;
    }
744
    else if ( KStandardShortcut::redo().contains( key ) )
745
    {
Laurent Montel's avatar
Fix:    
Laurent Montel committed
746
747
        if( !isReadOnly() )
           redo();
748
749
        return;
    }
750
    else if ( KStandardShortcut::deleteWordBack().contains( key ) )
751
752
753
754
755
756
757
758
    {
        cursorWordBackward(true);
        if ( hasSelectedText() )
            del();

        e->accept();
        return;
    }
759
    else if ( KStandardShortcut::deleteWordForward().contains( key ) )
760
761
762
763
764
765
766
767
768
    {
        // Workaround for QT bug where
        cursorWordForward(true);
        if ( hasSelectedText() )
            del();

        e->accept();
        return;
    }
769
    else if ( KStandardShortcut::backwardWord().contains( key ) )
770
771
772
773
774
    {
      cursorWordBackward(false);
      e->accept();
      return;
    }
775
    else if ( KStandardShortcut::forwardWord().contains( key ) )
776
777
778
779
780
    {
      cursorWordForward(false);
      e->accept();
      return;
    }
781
    else if ( KStandardShortcut::beginningOfLine().contains( key ) )
782
783
784
785
786
    {
      home(false);
      e->accept();
      return;
    }
787
    else if ( KStandardShortcut::endOfLine().contains( key ) )
788
789
790
791
792
    {
      end(false);
      e->accept();
      return;
    }
Stephan Binner's avatar
Stephan Binner committed
793

Laurent Montel's avatar
Laurent Montel committed
794

795
796
    // Filter key-events if EchoMode is normal and
    // completion mode is not set to CompletionNone
797
798
    if ( echoMode() == QLineEdit::Normal &&
         completionMode() != KGlobalSettings::CompletionNone )
799
    {
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
        if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
            const bool trap = (d->completionBox && d->completionBox->isVisible());
            const bool stopEvent = (trap || (d->grabReturnKeyEvents &&
                                    (e->modifiers() == Qt::NoButton ||
                                     e->modifiers() == Qt::KeypadModifier)));

            if (trap) {
                d->completionBox->hide();
                deselect();
                setCursorPosition(text().length());
            }

            emit returnPressed( displayText() );

            // Eat the event if the user asked for it, or if a completionbox was visible
            if (stopEvent) {
                emit QLineEdit::returnPressed();
                e->accept();
                return;
            }
        }

822
        const KeyBindingMap keys = getKeyBindings();
Urs Wolfer's avatar
Urs Wolfer committed
823
824
        const KGlobalSettings::Completion mode = completionMode();
        const bool noModifier = (e->modifiers() == Qt::NoButton ||
825
826
                           e->modifiers() == Qt::ShiftModifier ||
                           e->modifiers() == Qt::KeypadModifier);
Carsten Pfeiffer's avatar
cleanup    
Carsten Pfeiffer committed
827

828
829
830
831
        if ( (mode == KGlobalSettings::CompletionAuto ||
              mode == KGlobalSettings::CompletionPopupAuto ||
              mode == KGlobalSettings::CompletionMan) && noModifier )
        {
832
            if ( !d->userSelection && hasSelectedText() &&
833
                 ( e->key() == Qt::Key_Right || e->key() == Qt::Key_Left ) &&
834
                 e->modifiers()==Qt::NoButton )
835
            {
836
                const QString old_txt = text();
837
                d->disableRestoreSelection = true;
Urs Wolfer's avatar
Urs Wolfer committed
838
                const int start = selectionStart();
839
840

                deselect();
841
                QLineEdit::keyPressEvent ( e );
Urs Wolfer's avatar
Urs Wolfer committed
842
                const int cPosition=cursorPosition();
Heng Liu's avatar
Heng Liu committed
843
                setText(old_txt);
844
845
846
847
848
849
850
851

                // keep cursor at cPosition
                setSelection(old_txt.length(), cPosition - old_txt.length());
                if (e->key() == Qt::Key_Right && cPosition > start )
                {
                    //the user explicitly accepted the autocompletion
                    d->_k_updateUserText(text());
                }
852
853
854

                d->disableRestoreSelection = false;
                return;
855
            }
Laurent Montel's avatar
Laurent Montel committed
856

857
            if ( e->key() == Qt::Key_Escape )
858
859
860
861
862
863
864
865
866
            {
                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
867
                e->ignore();
868
869
870
                return;
            }

871
        }
872

873
        if ( (mode == KGlobalSettings::CompletionAuto ||
Carsten Pfeiffer's avatar
Carsten Pfeiffer committed
874
              mode == KGlobalSettings::CompletionMan) && noModifier )
875
        {