~ubuntu-branches/ubuntu/saucy/konsole/saucy-proposed

« back to all changes in this revision

Viewing changes to .pc/ControlDragSettings.diff/src/TerminalDisplay.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2012-11-19 16:01:56 UTC
  • mfrom: (1.1.18)
  • Revision ID: package-import@ubuntu.com-20121119160156-7ohwzglkgwe61gsf
Tags: 4:4.9.80-0ubuntu1
* New upstream beta release
* Remove ControlDragSettings.diff now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
    This file is part of Konsole, a terminal emulator for KDE.
3
 
 
4
 
    Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
5
 
    Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
6
 
 
7
 
    This program is free software; you can redistribute it and/or modify
8
 
    it under the terms of the GNU General Public License as published by
9
 
    the Free Software Foundation; either version 2 of the License, or
10
 
    (at your option) any later version.
11
 
 
12
 
    This program is distributed in the hope that it will be useful,
13
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
    GNU General Public License for more details.
16
 
 
17
 
    You should have received a copy of the GNU General Public License
18
 
    along with this program; if not, write to the Free Software
19
 
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20
 
    02110-1301  USA.
21
 
*/
22
 
 
23
 
// Own
24
 
#include "TerminalDisplay.h"
25
 
 
26
 
// Qt
27
 
#include <QtGui/QApplication>
28
 
#include <QtGui/QClipboard>
29
 
#include <QtGui/QKeyEvent>
30
 
#include <QtCore/QEvent>
31
 
#include <QtCore/QFileInfo>
32
 
#include <QtGui/QGridLayout>
33
 
#include <QtGui/QAction>
34
 
#include <QtGui/QLabel>
35
 
#include <QtGui/QPainter>
36
 
#include <QtGui/QPixmap>
37
 
#include <QtGui/QScrollBar>
38
 
#include <QtGui/QStyle>
39
 
#include <QtCore/QTimer>
40
 
#include <QtGui/QToolTip>
41
 
#include <QtGui/QAccessible>
42
 
 
43
 
// KDE
44
 
#include <KShell>
45
 
#include <KColorScheme>
46
 
#include <KCursor>
47
 
#include <KDebug>
48
 
#include <KLocalizedString>
49
 
#include <KNotification>
50
 
#include <KGlobalSettings>
51
 
#include <KIO/NetAccess>
52
 
#include <konq_operations.h>
53
 
#include <KFileItem>
54
 
 
55
 
// Konsole
56
 
#include "Filter.h"
57
 
#include "konsole_wcwidth.h"
58
 
#include "TerminalCharacterDecoder.h"
59
 
#include "Screen.h"
60
 
#include "ScreenWindow.h"
61
 
#include "LineFont.h"
62
 
#include "SessionController.h"
63
 
#include "ExtendedCharTable.h"
64
 
#include "TerminalDisplayAccessible.h"
65
 
#include "SessionManager.h"
66
 
#include "Session.h"
67
 
 
68
 
using namespace Konsole;
69
 
 
70
 
#ifndef loc
71
 
#define loc(X,Y) ((Y)*_columns+(X))
72
 
#endif
73
 
 
74
 
#define REPCHAR   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
75
 
    "abcdefgjijklmnopqrstuvwxyz" \
76
 
    "0123456789./+@"
77
 
 
78
 
// we use this to force QPainter to display text in LTR mode
79
 
// more information can be found in: http://unicode.org/reports/tr9/
80
 
const QChar LTR_OVERRIDE_CHAR(0x202D);
81
 
 
82
 
/* ------------------------------------------------------------------------- */
83
 
/*                                                                           */
84
 
/*                                Colors                                     */
85
 
/*                                                                           */
86
 
/* ------------------------------------------------------------------------- */
87
 
 
88
 
/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb)
89
 
 
90
 
   Code        0       1       2       3       4       5       6       7
91
 
   ----------- ------- ------- ------- ------- ------- ------- ------- -------
92
 
   ANSI  (bgr) Black   Red     Green   Yellow  Blue    Magenta Cyan    White
93
 
   IBMPC (rgb) Black   Blue    Green   Cyan    Red     Magenta Yellow  White
94
 
*/
95
 
 
96
 
ScreenWindow* TerminalDisplay::screenWindow() const
97
 
{
98
 
    return _screenWindow;
99
 
}
100
 
void TerminalDisplay::setScreenWindow(ScreenWindow* window)
101
 
{
102
 
    // disconnect existing screen window if any
103
 
    if (_screenWindow) {
104
 
        disconnect(_screenWindow , 0 , this , 0);
105
 
    }
106
 
 
107
 
    _screenWindow = window;
108
 
 
109
 
    if (_screenWindow) {
110
 
        connect(_screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()));
111
 
        connect(_screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()));
112
 
        _screenWindow->setWindowLines(_lines);
113
 
    }
114
 
}
115
 
 
116
 
const ColorEntry* TerminalDisplay::colorTable() const
117
 
{
118
 
    return _colorTable;
119
 
}
120
 
void TerminalDisplay::setBackgroundColor(const QColor& color)
121
 
{
122
 
    _colorTable[DEFAULT_BACK_COLOR].color = color;
123
 
 
124
 
    QPalette p = palette();
125
 
    p.setColor(backgroundRole(), color);
126
 
    setPalette(p);
127
 
 
128
 
    // Avoid propagating the palette change to the scroll bar
129
 
    _scrollBar->setPalette(QApplication::palette());
130
 
 
131
 
    update();
132
 
}
133
 
void TerminalDisplay::setForegroundColor(const QColor& color)
134
 
{
135
 
    _colorTable[DEFAULT_FORE_COLOR].color = color;
136
 
 
137
 
    update();
138
 
}
139
 
void TerminalDisplay::setColorTable(const ColorEntry table[])
140
 
{
141
 
    for (int i = 0; i < TABLE_COLORS; i++)
142
 
        _colorTable[i] = table[i];
143
 
 
144
 
    setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color);
145
 
}
146
 
 
147
 
/* ------------------------------------------------------------------------- */
148
 
/*                                                                           */
149
 
/*                                   Font                                    */
150
 
/*                                                                           */
151
 
/* ------------------------------------------------------------------------- */
152
 
 
153
 
static inline bool isLineCharString(const QString& string)
154
 
{
155
 
    if (string.length() == 0)
156
 
        return false;
157
 
 
158
 
    return isSupportedLineChar(string.at(0).unicode());
159
 
}
160
 
 
161
 
void TerminalDisplay::fontChange(const QFont&)
162
 
{
163
 
    QFontMetrics fm(font());
164
 
    _fontHeight = fm.height() + _lineSpacing;
165
 
 
166
 
    // waba TerminalDisplay 1.123:
167
 
    // "Base character width on widest ASCII character. This prevents too wide
168
 
    //  characters in the presence of double wide (e.g. Japanese) characters."
169
 
    // Get the width from representative normal width characters
170
 
    _fontWidth = qRound((double)fm.width(REPCHAR) / (double)qstrlen(REPCHAR));
171
 
 
172
 
    _fixedFont = true;
173
 
 
174
 
    const int fw = fm.width(REPCHAR[0]);
175
 
    for (unsigned int i = 1; i < qstrlen(REPCHAR); i++) {
176
 
        if (fw != fm.width(REPCHAR[i])) {
177
 
            _fixedFont = false;
178
 
            break;
179
 
        }
180
 
    }
181
 
 
182
 
    if (_fontWidth < 1)
183
 
        _fontWidth = 1;
184
 
 
185
 
    _fontAscent = fm.ascent();
186
 
 
187
 
    emit changedFontMetricSignal(_fontHeight, _fontWidth);
188
 
    propagateSize();
189
 
    update();
190
 
}
191
 
 
192
 
void TerminalDisplay::setVTFont(const QFont& f)
193
 
{
194
 
    QFont font = f;
195
 
 
196
 
    QFontMetrics metrics(font);
197
 
 
198
 
    if (!QFontInfo(font).fixedPitch()) {
199
 
        kWarning() << "Using an unsupported variable-width font in the terminal.  This may produce display errors.";
200
 
    }
201
 
 
202
 
    if (metrics.height() < height() && metrics.maxWidth() < width()) {
203
 
        // hint that text should be drawn without anti-aliasing.
204
 
        // depending on the user's font configuration, this may not be respected
205
 
        if (!_antialiasText)
206
 
            font.setStyleStrategy(QFont::NoAntialias);
207
 
 
208
 
        // experimental optimization.  Konsole assumes that the terminal is using a
209
 
        // mono-spaced font, in which case kerning information should have an effect.
210
 
        // Disabling kerning saves some computation when rendering text.
211
 
        font.setKerning(false);
212
 
 
213
 
        // Konsole cannot handle non-integer font metrics
214
 
        font.setStyleStrategy(QFont::StyleStrategy(font.styleStrategy() | QFont::ForceIntegerMetrics));
215
 
 
216
 
        QWidget::setFont(font);
217
 
        fontChange(font);
218
 
    }
219
 
}
220
 
 
221
 
void TerminalDisplay::setFont(const QFont &)
222
 
{
223
 
    // ignore font change request if not coming from konsole itself
224
 
}
225
 
 
226
 
void TerminalDisplay::increaseFontSize()
227
 
{
228
 
    QFont font = getVTFont();
229
 
    font.setPointSizeF(font.pointSizeF() + 1);
230
 
    setVTFont(font);
231
 
}
232
 
 
233
 
void TerminalDisplay::decreaseFontSize()
234
 
{
235
 
    const qreal MinimumFontSize = 6;
236
 
 
237
 
    QFont font = getVTFont();
238
 
    font.setPointSizeF(qMax(font.pointSizeF() - 1, MinimumFontSize));
239
 
    setVTFont(font);
240
 
}
241
 
 
242
 
uint TerminalDisplay::lineSpacing() const
243
 
{
244
 
    return _lineSpacing;
245
 
}
246
 
 
247
 
void TerminalDisplay::setLineSpacing(uint i)
248
 
{
249
 
    _lineSpacing = i;
250
 
    setVTFont(font()); // Trigger an update.
251
 
}
252
 
 
253
 
 
254
 
/* ------------------------------------------------------------------------- */
255
 
/*                                                                           */
256
 
/*                         Accessibility                                     */
257
 
/*                                                                           */
258
 
/* ------------------------------------------------------------------------- */
259
 
 
260
 
namespace Konsole {
261
 
/**
262
 
 * This function installs the factory function which lets Qt instanciate the QAccessibleInterface
263
 
 * for the TerminalDisplay.
264
 
 */
265
 
QAccessibleInterface* accessibleInterfaceFactory(const QString &key, QObject *object)
266
 
{
267
 
    Q_UNUSED(key)
268
 
    if (TerminalDisplay *display = qobject_cast<TerminalDisplay*>(object))
269
 
        return new TerminalDisplayAccessible(display);
270
 
    return 0;
271
 
}
272
 
}
273
 
 
274
 
/* ------------------------------------------------------------------------- */
275
 
/*                                                                           */
276
 
/*                         Constructor / Destructor                          */
277
 
/*                                                                           */
278
 
/* ------------------------------------------------------------------------- */
279
 
 
280
 
TerminalDisplay::TerminalDisplay(QWidget* parent)
281
 
    : QWidget(parent)
282
 
    , _screenWindow(0)
283
 
    , _bellMasked(false)
284
 
    , _gridLayout(0)
285
 
    , _fontHeight(1)
286
 
    , _fontWidth(1)
287
 
    , _fontAscent(1)
288
 
    , _boldIntense(true)
289
 
    , _lines(1)
290
 
    , _columns(1)
291
 
    , _usedLines(1)
292
 
    , _usedColumns(1)
293
 
    , _contentHeight(1)
294
 
    , _contentWidth(1)
295
 
    , _image(0)
296
 
    , _randomSeed(0)
297
 
    , _resizing(false)
298
 
    , _showTerminalSizeHint(true)
299
 
    , _bidiEnabled(false)
300
 
    , _actSel(0)
301
 
    , _wordSelectionMode(false)
302
 
    , _lineSelectionMode(false)
303
 
    , _preserveLineBreaks(false)
304
 
    , _columnSelectionMode(false)
305
 
    , _autoCopySelectedText(false)
306
 
    , _middleClickPasteMode(Enum::PasteFromX11Selection)
307
 
    , _scrollbarLocation(Enum::ScrollBarRight)
308
 
    , _wordCharacters(":@-./_~")
309
 
    , _bellMode(Enum::NotifyBell)
310
 
    , _allowBlinkingText(true)
311
 
    , _allowBlinkingCursor(false)
312
 
    , _textBlinking(false)
313
 
    , _cursorBlinking(false)
314
 
    , _hasTextBlinker(false)
315
 
    , _underlineLinks(true)
316
 
    , _isFixedSize(false)
317
 
    , _ctrlDrag(true)
318
 
    , _tripleClickMode(Enum::SelectWholeLine)
319
 
    , _possibleTripleClick(false)
320
 
    , _resizeWidget(0)
321
 
    , _resizeTimer(0)
322
 
    , _flowControlWarningEnabled(false)
323
 
    , _outputSuspendedLabel(0)
324
 
    , _lineSpacing(0)
325
 
    , _blendColor(qRgba(0, 0, 0, 0xff))
326
 
    , _filterChain(new TerminalImageFilterChain())
327
 
    , _cursorShape(Enum::BlockCursor)
328
 
    , _antialiasText(true)
329
 
    , _sessionController(0)
330
 
{
331
 
    // terminal applications are not designed with Right-To-Left in mind,
332
 
    // so the layout is forced to Left-To-Right
333
 
    setLayoutDirection(Qt::LeftToRight);
334
 
 
335
 
    _topMargin = DEFAULT_TOP_MARGIN;
336
 
    _leftMargin = DEFAULT_LEFT_MARGIN;
337
 
 
338
 
    // create scroll bar for scrolling output up and down
339
 
    _scrollBar = new QScrollBar(this);
340
 
    // set the scroll bar's slider to occupy the whole area of the scroll bar initially
341
 
    setScroll(0, 0);
342
 
    _scrollBar->setCursor(Qt::ArrowCursor);
343
 
    connect(_scrollBar, SIGNAL(valueChanged(int)),
344
 
            this, SLOT(scrollBarPositionChanged(int)));
345
 
 
346
 
    // setup timers for blinking text
347
 
    _blinkTextTimer = new QTimer(this);
348
 
    _blinkTextTimer->setInterval(TEXT_BLINK_DELAY);
349
 
    connect(_blinkTextTimer, SIGNAL(timeout()), this, SLOT(blinkTextEvent()));
350
 
 
351
 
    // setup timers for blinking cursor
352
 
    _blinkCursorTimer = new QTimer(this);
353
 
    _blinkCursorTimer->setInterval(QApplication::cursorFlashTime() / 2);
354
 
    connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent()));
355
 
 
356
 
    // hide mouse cursor on keystroke or idle
357
 
    KCursor::setAutoHideCursor(this, true);
358
 
    setMouseTracking(true);
359
 
 
360
 
    setUsesMouse(true);
361
 
 
362
 
    setColorTable(ColorScheme::defaultTable);
363
 
 
364
 
    // Enable drag and drop support
365
 
    setAcceptDrops(true);
366
 
    _dragInfo.state = diNone;
367
 
 
368
 
    setFocusPolicy(Qt::WheelFocus);
369
 
 
370
 
    // enable input method support
371
 
    setAttribute(Qt::WA_InputMethodEnabled, true);
372
 
 
373
 
    // this is an important optimization, it tells Qt
374
 
    // that TerminalDisplay will handle repainting its entire area.
375
 
    setAttribute(Qt::WA_OpaquePaintEvent);
376
 
 
377
 
    _gridLayout = new QGridLayout(this);
378
 
    _gridLayout->setContentsMargins(0, 0, 0, 0);
379
 
 
380
 
    setLayout(_gridLayout);
381
 
 
382
 
    new AutoScrollHandler(this);
383
 
 
384
 
 
385
 
#ifndef QT_NO_ACCESSIBILITY
386
 
    QAccessible::installFactory(Konsole::accessibleInterfaceFactory);
387
 
#endif
388
 
}
389
 
 
390
 
TerminalDisplay::~TerminalDisplay()
391
 
{
392
 
    disconnect(_blinkTextTimer);
393
 
    disconnect(_blinkCursorTimer);
394
 
 
395
 
    delete[] _image;
396
 
 
397
 
    delete _gridLayout;
398
 
    delete _outputSuspendedLabel;
399
 
    delete _filterChain;
400
 
}
401
 
 
402
 
/* ------------------------------------------------------------------------- */
403
 
/*                                                                           */
404
 
/*                             Display Operations                            */
405
 
/*                                                                           */
406
 
/* ------------------------------------------------------------------------- */
407
 
 
408
 
/**
409
 
 A table for emulating the simple (single width) unicode drawing chars.
410
 
 It represents the 250x - 257x glyphs. If it's zero, we can't use it.
411
 
 if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered
412
 
 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit.
413
 
 
414
 
 Then, the pixels basically have the following interpretation:
415
 
 _|||_
416
 
 -...-
417
 
 -...-
418
 
 -...-
419
 
 _|||_
420
 
 
421
 
where _ = none
422
 
      | = vertical line.
423
 
      - = horizontal line.
424
 
 */
425
 
 
426
 
enum LineEncode {
427
 
    TopL  = (1 << 1),
428
 
    TopC  = (1 << 2),
429
 
    TopR  = (1 << 3),
430
 
 
431
 
    LeftT = (1 << 5),
432
 
    Int11 = (1 << 6),
433
 
    Int12 = (1 << 7),
434
 
    Int13 = (1 << 8),
435
 
    RightT = (1 << 9),
436
 
 
437
 
    LeftC = (1 << 10),
438
 
    Int21 = (1 << 11),
439
 
    Int22 = (1 << 12),
440
 
    Int23 = (1 << 13),
441
 
    RightC = (1 << 14),
442
 
 
443
 
    LeftB = (1 << 15),
444
 
    Int31 = (1 << 16),
445
 
    Int32 = (1 << 17),
446
 
    Int33 = (1 << 18),
447
 
    RightB = (1 << 19),
448
 
 
449
 
    BotL  = (1 << 21),
450
 
    BotC  = (1 << 22),
451
 
    BotR  = (1 << 23)
452
 
};
453
 
 
454
 
static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code)
455
 
{
456
 
    //Calculate cell midpoints, end points.
457
 
    const int cx = x + w / 2;
458
 
    const int cy = y + h / 2;
459
 
    const int ex = x + w - 1;
460
 
    const int ey = y + h - 1;
461
 
 
462
 
    const quint32 toDraw = LineChars[code];
463
 
 
464
 
    //Top _lines:
465
 
    if (toDraw & TopL)
466
 
        paint.drawLine(cx - 1, y, cx - 1, cy - 2);
467
 
    if (toDraw & TopC)
468
 
        paint.drawLine(cx, y, cx, cy - 2);
469
 
    if (toDraw & TopR)
470
 
        paint.drawLine(cx + 1, y, cx + 1, cy - 2);
471
 
 
472
 
    //Bot _lines:
473
 
    if (toDraw & BotL)
474
 
        paint.drawLine(cx - 1, cy + 2, cx - 1, ey);
475
 
    if (toDraw & BotC)
476
 
        paint.drawLine(cx, cy + 2, cx, ey);
477
 
    if (toDraw & BotR)
478
 
        paint.drawLine(cx + 1, cy + 2, cx + 1, ey);
479
 
 
480
 
    //Left _lines:
481
 
    if (toDraw & LeftT)
482
 
        paint.drawLine(x, cy - 1, cx - 2, cy - 1);
483
 
    if (toDraw & LeftC)
484
 
        paint.drawLine(x, cy, cx - 2, cy);
485
 
    if (toDraw & LeftB)
486
 
        paint.drawLine(x, cy + 1, cx - 2, cy + 1);
487
 
 
488
 
    //Right _lines:
489
 
    if (toDraw & RightT)
490
 
        paint.drawLine(cx + 2, cy - 1, ex, cy - 1);
491
 
    if (toDraw & RightC)
492
 
        paint.drawLine(cx + 2, cy, ex, cy);
493
 
    if (toDraw & RightB)
494
 
        paint.drawLine(cx + 2, cy + 1, ex, cy + 1);
495
 
 
496
 
    //Intersection points.
497
 
    if (toDraw & Int11)
498
 
        paint.drawPoint(cx - 1, cy - 1);
499
 
    if (toDraw & Int12)
500
 
        paint.drawPoint(cx, cy - 1);
501
 
    if (toDraw & Int13)
502
 
        paint.drawPoint(cx + 1, cy - 1);
503
 
 
504
 
    if (toDraw & Int21)
505
 
        paint.drawPoint(cx - 1, cy);
506
 
    if (toDraw & Int22)
507
 
        paint.drawPoint(cx, cy);
508
 
    if (toDraw & Int23)
509
 
        paint.drawPoint(cx + 1, cy);
510
 
 
511
 
    if (toDraw & Int31)
512
 
        paint.drawPoint(cx - 1, cy + 1);
513
 
    if (toDraw & Int32)
514
 
        paint.drawPoint(cx, cy + 1);
515
 
    if (toDraw & Int33)
516
 
        paint.drawPoint(cx + 1, cy + 1);
517
 
}
518
 
 
519
 
void TerminalDisplay::drawLineCharString(QPainter& painter, int x, int y, const QString& str,
520
 
        const Character* attributes)
521
 
{
522
 
    const QPen& originalPen = painter.pen();
523
 
 
524
 
    if ((attributes->rendition & RE_BOLD) && _boldIntense) {
525
 
        QPen boldPen(originalPen);
526
 
        boldPen.setWidth(3);
527
 
        painter.setPen(boldPen);
528
 
    }
529
 
 
530
 
    for (int i = 0 ; i < str.length(); i++) {
531
 
        const uchar code = str[i].cell();
532
 
        if (LineChars[code])
533
 
            drawLineChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
534
 
    }
535
 
 
536
 
    painter.setPen(originalPen);
537
 
}
538
 
 
539
 
void TerminalDisplay::setKeyboardCursorShape(Enum::CursorShapeEnum shape)
540
 
{
541
 
    _cursorShape = shape;
542
 
}
543
 
Enum::CursorShapeEnum TerminalDisplay::keyboardCursorShape() const
544
 
{
545
 
    return _cursorShape;
546
 
}
547
 
void TerminalDisplay::setKeyboardCursorColor(const QColor& color)
548
 
{
549
 
    _cursorColor = color;
550
 
}
551
 
QColor TerminalDisplay::keyboardCursorColor() const
552
 
{
553
 
    return _cursorColor;
554
 
}
555
 
 
556
 
void TerminalDisplay::setOpacity(qreal opacity)
557
 
{
558
 
    QColor color(_blendColor);
559
 
    color.setAlphaF(opacity);
560
 
 
561
 
    // enable automatic background filling to prevent the display
562
 
    // flickering if there is no transparency
563
 
    /*if ( color.alpha() == 255 )
564
 
    {
565
 
        setAutoFillBackground(true);
566
 
    }
567
 
    else
568
 
    {
569
 
        setAutoFillBackground(false);
570
 
    }*/
571
 
 
572
 
    _blendColor = color.rgba();
573
 
}
574
 
 
575
 
void TerminalDisplay::setWallpaper(ColorSchemeWallpaper::Ptr p)
576
 
{
577
 
    _wallpaper = p;
578
 
}
579
 
 
580
 
void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting)
581
 
{
582
 
    // the area of the widget showing the contents of the terminal display is drawn
583
 
    // using the background color from the color scheme set with setColorTable()
584
 
    //
585
 
    // the area of the widget behind the scroll-bar is drawn using the background
586
 
    // brush from the scroll-bar's palette, to give the effect of the scroll-bar
587
 
    // being outside of the terminal display and visual consistency with other KDE
588
 
    // applications.
589
 
    //
590
 
    QRect scrollBarArea = _scrollBar->isVisible() ?
591
 
                          rect.intersected(_scrollBar->geometry()) :
592
 
                          QRect();
593
 
    QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea);
594
 
    QRect contentsRect = contentsRegion.boundingRect();
595
 
 
596
 
    if (useOpacitySetting && !_wallpaper->isNull() &&
597
 
            _wallpaper->draw(painter, contentsRect)) {
598
 
    } else if (qAlpha(_blendColor) < 0xff && useOpacitySetting) {
599
 
        // TODO - On MacOS, using CompositionMode doesn't work.  Altering the
600
 
        //        transparency in the color scheme (appears to) alter the
601
 
        //        brightness(?).  I'm not sure #ifdef is worthwhile ATM.
602
 
        QColor color(backgroundColor);
603
 
        color.setAlpha(qAlpha(_blendColor));
604
 
 
605
 
        painter.save();
606
 
        painter.setCompositionMode(QPainter::CompositionMode_Source);
607
 
        painter.fillRect(contentsRect, color);
608
 
        painter.restore();
609
 
    } else {
610
 
        painter.fillRect(contentsRect, backgroundColor);
611
 
    }
612
 
 
613
 
    painter.fillRect(scrollBarArea, _scrollBar->palette().background());
614
 
}
615
 
 
616
 
void TerminalDisplay::drawCursor(QPainter& painter,
617
 
                                 const QRect& rect,
618
 
                                 const QColor& foregroundColor,
619
 
                                 const QColor& /*backgroundColor*/,
620
 
                                 bool& invertCharacterColor)
621
 
{
622
 
    // don't draw cursor which is currently blinking
623
 
    if (_cursorBlinking)
624
 
        return;
625
 
 
626
 
    QRect cursorRect = rect;
627
 
    cursorRect.setHeight(_fontHeight - _lineSpacing - 1);
628
 
 
629
 
    QColor cursorColor = _cursorColor.isValid() ? _cursorColor : foregroundColor;
630
 
    painter.setPen(cursorColor);
631
 
 
632
 
    if (_cursorShape == Enum::BlockCursor) {
633
 
        // draw the cursor outline, adjusting the area so that
634
 
        // it is draw entirely inside 'rect'
635
 
        int penWidth = qMax(1, painter.pen().width());
636
 
        painter.drawRect(cursorRect.adjusted(penWidth / 2,
637
 
                                             penWidth / 2,
638
 
                                             - penWidth / 2 - penWidth % 2,
639
 
                                             - penWidth / 2 - penWidth % 2));
640
 
 
641
 
        // draw the cursor body only when the widget has focus
642
 
        if (hasFocus()) {
643
 
            painter.fillRect(cursorRect, cursorColor);
644
 
 
645
 
            if (!_cursorColor.isValid()) {
646
 
                // invert the color used to draw the text to ensure that the character at
647
 
                // the cursor position is readable
648
 
                invertCharacterColor = true;
649
 
            }
650
 
        }
651
 
    } else if (_cursorShape == Enum::UnderlineCursor)
652
 
        painter.drawLine(cursorRect.left(),
653
 
                         cursorRect.bottom(),
654
 
                         cursorRect.right(),
655
 
                         cursorRect.bottom());
656
 
 
657
 
    else if (_cursorShape == Enum::IBeamCursor)
658
 
        painter.drawLine(cursorRect.left(),
659
 
                         cursorRect.top(),
660
 
                         cursorRect.left(),
661
 
                         cursorRect.bottom());
662
 
}
663
 
 
664
 
void TerminalDisplay::drawCharacters(QPainter& painter,
665
 
                                     const QRect& rect,
666
 
                                     const QString& text,
667
 
                                     const Character* style,
668
 
                                     bool invertCharacterColor)
669
 
{
670
 
    // don't draw text which is currently blinking
671
 
    if (_textBlinking && (style->rendition & RE_BLINK))
672
 
        return;
673
 
 
674
 
    // setup bold and underline
675
 
    bool useBold;
676
 
    ColorEntry::FontWeight weight = style->fontWeight(_colorTable);
677
 
    if (weight == ColorEntry::UseCurrentFormat)
678
 
        useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold();
679
 
    else
680
 
        useBold = (weight == ColorEntry::Bold) ? true : false;
681
 
    const bool useUnderline = style->rendition & RE_UNDERLINE || font().underline();
682
 
 
683
 
    QFont font = painter.font();
684
 
    if (font.bold() != useBold
685
 
            || font.underline() != useUnderline) {
686
 
        font.setBold(useBold);
687
 
        font.setUnderline(useUnderline);
688
 
        painter.setFont(font);
689
 
    }
690
 
 
691
 
    // setup pen
692
 
    const CharacterColor& textColor = (invertCharacterColor ? style->backgroundColor : style->foregroundColor);
693
 
    const QColor color = textColor.color(_colorTable);
694
 
    QPen pen = painter.pen();
695
 
    if (pen.color() != color) {
696
 
        pen.setColor(color);
697
 
        painter.setPen(color);
698
 
    }
699
 
 
700
 
    // draw text
701
 
    if (isLineCharString(text)) {
702
 
        drawLineCharString(painter, rect.x(), rect.y(), text, style);
703
 
    } else {
704
 
        // Force using LTR as the document layout for the terminal area, because
705
 
        // there is no use cases for RTL emulator and RTL terminal application.
706
 
        //
707
 
        // This still allows RTL characters to be rendered in the RTL way.
708
 
        painter.setLayoutDirection(Qt::LeftToRight);
709
 
 
710
 
        // the drawText(rect,flags,string) overload is used here with null flags
711
 
        // instead of drawText(rect,string) because the (rect,string) overload causes
712
 
        // the application's default layout direction to be used instead of
713
 
        // the widget-specific layout direction, which should always be
714
 
        // Qt::LeftToRight for this widget
715
 
        //
716
 
        // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2
717
 
        if (_bidiEnabled) {
718
 
            painter.drawText(rect, 0, text);
719
 
        } else {
720
 
            // See bug 280896 for more info
721
 
#if QT_VERSION >= 0x040800
722
 
            painter.drawText(rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + text);
723
 
#else
724
 
            painter.drawText(rect, 0, LTR_OVERRIDE_CHAR + text);
725
 
#endif
726
 
        }
727
 
    }
728
 
}
729
 
 
730
 
void TerminalDisplay::drawTextFragment(QPainter& painter ,
731
 
                                       const QRect& rect,
732
 
                                       const QString& text,
733
 
                                       const Character* style)
734
 
{
735
 
    painter.save();
736
 
 
737
 
    // setup painter
738
 
    const QColor foregroundColor = style->foregroundColor.color(_colorTable);
739
 
    const QColor backgroundColor = style->backgroundColor.color(_colorTable);
740
 
 
741
 
    // draw background if different from the display's background color
742
 
    if (backgroundColor != palette().background().color())
743
 
        drawBackground(painter, rect, backgroundColor,
744
 
                       false /* do not use transparency */);
745
 
 
746
 
    // draw cursor shape if the current character is the cursor
747
 
    // this may alter the foreground and background colors
748
 
    bool invertCharacterColor = false;
749
 
    if (style->rendition & RE_CURSOR)
750
 
        drawCursor(painter, rect, foregroundColor, backgroundColor, invertCharacterColor);
751
 
 
752
 
    // draw text
753
 
    drawCharacters(painter, rect, text, style, invertCharacterColor);
754
 
 
755
 
    painter.restore();
756
 
}
757
 
 
758
 
void TerminalDisplay::setRandomSeed(uint randomSeed)
759
 
{
760
 
    _randomSeed = randomSeed;
761
 
}
762
 
uint TerminalDisplay::randomSeed() const
763
 
{
764
 
    return _randomSeed;
765
 
}
766
 
 
767
 
// scrolls the image by 'lines', down if lines > 0 or up otherwise.
768
 
//
769
 
// the terminal emulation keeps track of the scrolling of the character
770
 
// image as it receives input, and when the view is updated, it calls scrollImage()
771
 
// with the final scroll amount.  this improves performance because scrolling the
772
 
// display is much cheaper than re-rendering all the text for the
773
 
// part of the image which has moved up or down.
774
 
// Instead only new lines have to be drawn
775
 
void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion)
776
 
{
777
 
    // if the flow control warning is enabled this will interfere with the
778
 
    // scrolling optimizations and cause artifacts.  the simple solution here
779
 
    // is to just disable the optimization whilst it is visible
780
 
    if (_outputSuspendedLabel && _outputSuspendedLabel->isVisible())
781
 
        return;
782
 
 
783
 
    // constrain the region to the display
784
 
    // the bottom of the region is capped to the number of lines in the display's
785
 
    // internal image - 2, so that the height of 'region' is strictly less
786
 
    // than the height of the internal image.
787
 
    QRect region = screenWindowRegion;
788
 
    region.setBottom(qMin(region.bottom(), this->_lines - 2));
789
 
 
790
 
    // return if there is nothing to do
791
 
    if (lines == 0
792
 
            || _image == 0
793
 
            || !region.isValid()
794
 
            || (region.top() + abs(lines)) >= region.bottom()
795
 
            || this->_lines <= region.height()) return;
796
 
 
797
 
    // hide terminal size label to prevent it being scrolled
798
 
    if (_resizeWidget && _resizeWidget->isVisible())
799
 
        _resizeWidget->hide();
800
 
 
801
 
    // Note:  With Qt 4.4 the left edge of the scrolled area must be at 0
802
 
    // to get the correct (newly exposed) part of the widget repainted.
803
 
    //
804
 
    // The right edge must be before the left edge of the scroll bar to
805
 
    // avoid triggering a repaint of the entire widget, the distance is
806
 
    // given by SCROLLBAR_CONTENT_GAP
807
 
    //
808
 
    // Set the QT_FLUSH_PAINT environment variable to '1' before starting the
809
 
    // application to monitor repainting.
810
 
    //
811
 
    const int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->width();
812
 
    const int SCROLLBAR_CONTENT_GAP = 1;
813
 
    QRect scrollRect;
814
 
    if (_scrollbarLocation == Enum::ScrollBarLeft) {
815
 
        scrollRect.setLeft(scrollBarWidth + SCROLLBAR_CONTENT_GAP);
816
 
        scrollRect.setRight(width());
817
 
    } else {
818
 
        scrollRect.setLeft(0);
819
 
        scrollRect.setRight(width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP);
820
 
    }
821
 
    void* firstCharPos = &_image[ region.top() * this->_columns ];
822
 
    void* lastCharPos = &_image[(region.top() + abs(lines)) * this->_columns ];
823
 
 
824
 
    const int top = _topMargin + (region.top() * _fontHeight);
825
 
    const int linesToMove = region.height() - abs(lines);
826
 
    const int bytesToMove = linesToMove * this->_columns * sizeof(Character);
827
 
 
828
 
    Q_ASSERT(linesToMove > 0);
829
 
    Q_ASSERT(bytesToMove > 0);
830
 
 
831
 
    //scroll internal image
832
 
    if (lines > 0) {
833
 
        // check that the memory areas that we are going to move are valid
834
 
        Q_ASSERT((char*)lastCharPos + bytesToMove <
835
 
                 (char*)(_image + (this->_lines * this->_columns)));
836
 
 
837
 
        Q_ASSERT((lines * this->_columns) < _imageSize);
838
 
 
839
 
        //scroll internal image down
840
 
        memmove(firstCharPos , lastCharPos , bytesToMove);
841
 
 
842
 
        //set region of display to scroll
843
 
        scrollRect.setTop(top);
844
 
    } else {
845
 
        // check that the memory areas that we are going to move are valid
846
 
        Q_ASSERT((char*)firstCharPos + bytesToMove <
847
 
                 (char*)(_image + (this->_lines * this->_columns)));
848
 
 
849
 
        //scroll internal image up
850
 
        memmove(lastCharPos , firstCharPos , bytesToMove);
851
 
 
852
 
        //set region of the display to scroll
853
 
        scrollRect.setTop(top + abs(lines) * _fontHeight);
854
 
    }
855
 
    scrollRect.setHeight(linesToMove * _fontHeight);
856
 
 
857
 
    Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty());
858
 
 
859
 
    //scroll the display vertically to match internal _image
860
 
    scroll(0 , _fontHeight * (-lines) , scrollRect);
861
 
}
862
 
 
863
 
QRegion TerminalDisplay::hotSpotRegion() const
864
 
{
865
 
    QRegion region;
866
 
    foreach(Filter::HotSpot * hotSpot , _filterChain->hotSpots()) {
867
 
        QRect r;
868
 
        if (hotSpot->startLine() == hotSpot->endLine()) {
869
 
            r.setLeft(hotSpot->startColumn());
870
 
            r.setTop(hotSpot->startLine());
871
 
            r.setRight(hotSpot->endColumn());
872
 
            r.setBottom(hotSpot->endLine());
873
 
            region |= imageToWidget(r);
874
 
        } else {
875
 
            r.setLeft(hotSpot->startColumn());
876
 
            r.setTop(hotSpot->startLine());
877
 
            r.setRight(_columns);
878
 
            r.setBottom(hotSpot->startLine());
879
 
            region |= imageToWidget(r);
880
 
            for (int line = hotSpot->startLine() + 1 ; line < hotSpot->endLine() ; line++) {
881
 
                r.setLeft(0);
882
 
                r.setTop(line);
883
 
                r.setRight(_columns);
884
 
                r.setBottom(line);
885
 
                region |= imageToWidget(r);
886
 
            }
887
 
            r.setLeft(0);
888
 
            r.setTop(hotSpot->endLine());
889
 
            r.setRight(hotSpot->endColumn());
890
 
            r.setBottom(hotSpot->endLine());
891
 
            region |= imageToWidget(r);
892
 
        }
893
 
    }
894
 
    return region;
895
 
}
896
 
 
897
 
void TerminalDisplay::processFilters()
898
 
{
899
 
    if (!_screenWindow)
900
 
        return;
901
 
 
902
 
    QRegion preUpdateHotSpots = hotSpotRegion();
903
 
 
904
 
    // use _screenWindow->getImage() here rather than _image because
905
 
    // other classes may call processFilters() when this display's
906
 
    // ScreenWindow emits a scrolled() signal - which will happen before
907
 
    // updateImage() is called on the display and therefore _image is
908
 
    // out of date at this point
909
 
    _filterChain->setImage(_screenWindow->getImage(),
910
 
                           _screenWindow->windowLines(),
911
 
                           _screenWindow->windowColumns(),
912
 
                           _screenWindow->getLineProperties());
913
 
    _filterChain->process();
914
 
 
915
 
    QRegion postUpdateHotSpots = hotSpotRegion();
916
 
 
917
 
    update(preUpdateHotSpots | postUpdateHotSpots);
918
 
}
919
 
 
920
 
void TerminalDisplay::updateImage()
921
 
{
922
 
    if (!_screenWindow)
923
 
        return;
924
 
 
925
 
    // optimization - scroll the existing image where possible and
926
 
    // avoid expensive text drawing for parts of the image that
927
 
    // can simply be moved up or down
928
 
    if (_wallpaper->isNull()) {
929
 
        scrollImage(_screenWindow->scrollCount() ,
930
 
                    _screenWindow->scrollRegion());
931
 
        _screenWindow->resetScrollCount();
932
 
    }
933
 
 
934
 
    if (!_image) {
935
 
        // Create _image.
936
 
        // The emitted changedContentSizeSignal also leads to getImage being recreated, so do this first.
937
 
        updateImageSize();
938
 
    }
939
 
 
940
 
    Character* const newimg = _screenWindow->getImage();
941
 
    const int lines = _screenWindow->windowLines();
942
 
    const int columns = _screenWindow->windowColumns();
943
 
 
944
 
    setScroll(_screenWindow->currentLine() , _screenWindow->lineCount());
945
 
 
946
 
    Q_ASSERT(this->_usedLines <= this->_lines);
947
 
    Q_ASSERT(this->_usedColumns <= this->_columns);
948
 
 
949
 
    int y, x, len;
950
 
 
951
 
    const QPoint tL  = contentsRect().topLeft();
952
 
    const int    tLx = tL.x();
953
 
    const int    tLy = tL.y();
954
 
    _hasTextBlinker = false;
955
 
 
956
 
    CharacterColor cf;       // undefined
957
 
 
958
 
    const int linesToUpdate = qMin(this->_lines, qMax(0, lines));
959
 
    const int columnsToUpdate = qMin(this->_columns, qMax(0, columns));
960
 
 
961
 
    char* dirtyMask = new char[columnsToUpdate + 2];
962
 
    QRegion dirtyRegion;
963
 
 
964
 
    // debugging variable, this records the number of lines that are found to
965
 
    // be 'dirty' ( ie. have changed from the old _image to the new _image ) and
966
 
    // which therefore need to be repainted
967
 
    int dirtyLineCount = 0;
968
 
 
969
 
    for (y = 0; y < linesToUpdate; ++y) {
970
 
        const Character* currentLine = &_image[y * this->_columns];
971
 
        const Character* const newLine = &newimg[y * columns];
972
 
 
973
 
        bool updateLine = false;
974
 
 
975
 
        // The dirty mask indicates which characters need repainting. We also
976
 
        // mark surrounding neighbours dirty, in case the character exceeds
977
 
        // its cell boundaries
978
 
        memset(dirtyMask, 0, columnsToUpdate + 2);
979
 
 
980
 
        for (x = 0 ; x < columnsToUpdate ; ++x) {
981
 
            if (newLine[x] != currentLine[x]) {
982
 
                dirtyMask[x] = true;
983
 
            }
984
 
        }
985
 
 
986
 
        if (!_resizing) // not while _resizing, we're expecting a paintEvent
987
 
            for (x = 0; x < columnsToUpdate; ++x) {
988
 
                _hasTextBlinker |= (newLine[x].rendition & RE_BLINK);
989
 
 
990
 
                // Start drawing if this character or the next one differs.
991
 
                // We also take the next one into account to handle the situation
992
 
                // where characters exceed their cell width.
993
 
                if (dirtyMask[x]) {
994
 
                    if (!newLine[x + 0].character)
995
 
                        continue;
996
 
                    const bool lineDraw = newLine[x + 0].isLineChar();
997
 
                    const bool doubleWidth = (x + 1 == columnsToUpdate) ? false : (newLine[x + 1].character == 0);
998
 
                    const quint8 cr = newLine[x].rendition;
999
 
                    const CharacterColor clipboard = newLine[x].backgroundColor;
1000
 
                    if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor;
1001
 
                    const int lln = columnsToUpdate - x;
1002
 
                    for (len = 1; len < lln; ++len) {
1003
 
                        const Character& ch = newLine[x + len];
1004
 
 
1005
 
                        if (!ch.character)
1006
 
                            continue; // Skip trailing part of multi-col chars.
1007
 
 
1008
 
                        const bool nextIsDoubleWidth = (x + len + 1 == columnsToUpdate) ? false : (newLine[x + len + 1].character == 0);
1009
 
 
1010
 
                        if (ch.foregroundColor != cf ||
1011
 
                                ch.backgroundColor != clipboard ||
1012
 
                                (ch.rendition & ~RE_EXTENDED_CHAR) != (cr & ~RE_EXTENDED_CHAR) ||
1013
 
                                !dirtyMask[x + len] ||
1014
 
                                ch.isLineChar() != lineDraw ||
1015
 
                                nextIsDoubleWidth != doubleWidth)
1016
 
                            break;
1017
 
                    }
1018
 
 
1019
 
                    const bool saveFixedFont = _fixedFont;
1020
 
                    if (lineDraw)
1021
 
                        _fixedFont = false;
1022
 
                    if (doubleWidth)
1023
 
                        _fixedFont = false;
1024
 
 
1025
 
                    updateLine = true;
1026
 
 
1027
 
                    _fixedFont = saveFixedFont;
1028
 
                    x += len - 1;
1029
 
                }
1030
 
            }
1031
 
 
1032
 
        //both the top and bottom halves of double height _lines must always be redrawn
1033
 
        //although both top and bottom halves contain the same characters, only
1034
 
        //the top one is actually
1035
 
        //drawn.
1036
 
        if (_lineProperties.count() > y)
1037
 
            updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT);
1038
 
 
1039
 
        // if the characters on the line are different in the old and the new _image
1040
 
        // then this line must be repainted.
1041
 
        if (updateLine) {
1042
 
            dirtyLineCount++;
1043
 
 
1044
 
            // add the area occupied by this line to the region which needs to be
1045
 
            // repainted
1046
 
            QRect dirtyRect = QRect(_leftMargin + tLx ,
1047
 
                                    _topMargin + tLy + _fontHeight * y ,
1048
 
                                    _fontWidth * columnsToUpdate ,
1049
 
                                    _fontHeight);
1050
 
 
1051
 
            dirtyRegion |= dirtyRect;
1052
 
        }
1053
 
 
1054
 
        // replace the line of characters in the old _image with the
1055
 
        // current line of the new _image
1056
 
        memcpy((void*)currentLine, (const void*)newLine, columnsToUpdate * sizeof(Character));
1057
 
    }
1058
 
 
1059
 
    // if the new _image is smaller than the previous _image, then ensure that the area
1060
 
    // outside the new _image is cleared
1061
 
    if (linesToUpdate < _usedLines) {
1062
 
        dirtyRegion |= QRect(_leftMargin + tLx ,
1063
 
                             _topMargin + tLy + _fontHeight * linesToUpdate ,
1064
 
                             _fontWidth * this->_columns ,
1065
 
                             _fontHeight * (_usedLines - linesToUpdate));
1066
 
    }
1067
 
    _usedLines = linesToUpdate;
1068
 
 
1069
 
    if (columnsToUpdate < _usedColumns) {
1070
 
        dirtyRegion |= QRect(_leftMargin + tLx + columnsToUpdate * _fontWidth ,
1071
 
                             _topMargin + tLy ,
1072
 
                             _fontWidth * (_usedColumns - columnsToUpdate) ,
1073
 
                             _fontHeight * this->_lines);
1074
 
    }
1075
 
    _usedColumns = columnsToUpdate;
1076
 
 
1077
 
    dirtyRegion |= _inputMethodData.previousPreeditRect;
1078
 
 
1079
 
    // update the parts of the display which have changed
1080
 
    update(dirtyRegion);
1081
 
 
1082
 
    if (_allowBlinkingText && _hasTextBlinker && !_blinkTextTimer->isActive()) {
1083
 
        _blinkTextTimer->start();
1084
 
    }
1085
 
    if (!_hasTextBlinker && _blinkTextTimer->isActive()) {
1086
 
        _blinkTextTimer->stop();
1087
 
        _textBlinking = false;
1088
 
    }
1089
 
    delete[] dirtyMask;
1090
 
 
1091
 
#if QT_VERSION >= 0x040800 // added in Qt 4.8.0
1092
 
#ifndef QT_NO_ACCESSIBILITY
1093
 
    QAccessible::updateAccessibility(this, 0, QAccessible::TextUpdated);
1094
 
    QAccessible::updateAccessibility(this, 0, QAccessible::TextCaretMoved);
1095
 
#endif
1096
 
#endif
1097
 
}
1098
 
 
1099
 
void TerminalDisplay::showResizeNotification()
1100
 
{
1101
 
    if (_showTerminalSizeHint && isVisible()) {
1102
 
        if (!_resizeWidget) {
1103
 
            _resizeWidget = new QLabel(i18n("Size: XXX x XXX"), this);
1104
 
            _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(i18n("Size: XXX x XXX")));
1105
 
            _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height());
1106
 
            _resizeWidget->setAlignment(Qt::AlignCenter);
1107
 
 
1108
 
            _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)");
1109
 
 
1110
 
            _resizeTimer = new QTimer(this);
1111
 
            _resizeTimer->setInterval(SIZE_HINT_DURATION);
1112
 
            _resizeTimer->setSingleShot(true);
1113
 
            connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide()));
1114
 
        }
1115
 
        QString sizeStr = i18n("Size: %1 x %2", _columns, _lines);
1116
 
        _resizeWidget->setText(sizeStr);
1117
 
        _resizeWidget->move((width() - _resizeWidget->width()) / 2,
1118
 
                            (height() - _resizeWidget->height()) / 2 + 20);
1119
 
        _resizeWidget->show();
1120
 
        _resizeTimer->start();
1121
 
    }
1122
 
}
1123
 
 
1124
 
void TerminalDisplay::paintEvent(QPaintEvent* pe)
1125
 
{
1126
 
    QPainter paint(this);
1127
 
 
1128
 
    foreach(const QRect & rect, (pe->region() & contentsRect()).rects()) {
1129
 
        drawBackground(paint, rect, palette().background().color(),
1130
 
                       true /* use opacity setting */);
1131
 
        drawContents(paint, rect);
1132
 
    }
1133
 
    drawInputMethodPreeditString(paint, preeditRect());
1134
 
    paintFilters(paint);
1135
 
}
1136
 
 
1137
 
QPoint TerminalDisplay::cursorPosition() const
1138
 
{
1139
 
    if (_screenWindow)
1140
 
        return _screenWindow->cursorPosition();
1141
 
    else
1142
 
        return QPoint(0, 0);
1143
 
}
1144
 
 
1145
 
FilterChain* TerminalDisplay::filterChain() const
1146
 
{
1147
 
    return _filterChain;
1148
 
}
1149
 
 
1150
 
void TerminalDisplay::paintFilters(QPainter& painter)
1151
 
{
1152
 
    // get color of character under mouse and use it to draw
1153
 
    // lines for filters
1154
 
    QPoint cursorPos = mapFromGlobal(QCursor::pos());
1155
 
    int cursorLine;
1156
 
    int cursorColumn;
1157
 
    const int scrollBarWidth = (_scrollbarLocation == Enum::ScrollBarLeft) ? _scrollBar->width() : 0;
1158
 
 
1159
 
    getCharacterPosition(cursorPos , cursorLine , cursorColumn);
1160
 
    Character cursorCharacter = _image[loc(cursorColumn, cursorLine)];
1161
 
 
1162
 
    painter.setPen(QPen(cursorCharacter.foregroundColor.color(colorTable())));
1163
 
 
1164
 
    // iterate over hotspots identified by the display's currently active filters
1165
 
    // and draw appropriate visuals to indicate the presence of the hotspot
1166
 
 
1167
 
    QList<Filter::HotSpot*> spots = _filterChain->hotSpots();
1168
 
    foreach(Filter::HotSpot* spot, spots) {
1169
 
        QRegion region;
1170
 
        if (_underlineLinks && spot->type() == Filter::HotSpot::Link) {
1171
 
            QRect r;
1172
 
            if (spot->startLine() == spot->endLine()) {
1173
 
                r.setCoords(spot->startColumn()*_fontWidth + 1 + scrollBarWidth,
1174
 
                            spot->startLine()*_fontHeight + 1,
1175
 
                            (spot->endColumn() - 1)*_fontWidth - 1 + scrollBarWidth,
1176
 
                            (spot->endLine() + 1)*_fontHeight - 1);
1177
 
                region |= r;
1178
 
            } else {
1179
 
                r.setCoords(spot->startColumn()*_fontWidth + 1 + scrollBarWidth,
1180
 
                            spot->startLine()*_fontHeight + 1,
1181
 
                            (_columns - 1)*_fontWidth - 1 + scrollBarWidth,
1182
 
                            (spot->startLine() + 1)*_fontHeight - 1);
1183
 
                region |= r;
1184
 
                for (int line = spot->startLine() + 1 ; line < spot->endLine() ; line++) {
1185
 
                    r.setCoords(0 * _fontWidth + 1 + scrollBarWidth,
1186
 
                                line * _fontHeight + 1,
1187
 
                                (_columns - 1)*_fontWidth - 1 + scrollBarWidth,
1188
 
                                (line + 1)*_fontHeight - 1);
1189
 
                    region |= r;
1190
 
                }
1191
 
                r.setCoords(0 * _fontWidth + 1 + scrollBarWidth,
1192
 
                            spot->endLine()*_fontHeight + 1,
1193
 
                            (spot->endColumn() - 1)*_fontWidth - 1 + scrollBarWidth,
1194
 
                            (spot->endLine() + 1)*_fontHeight - 1);
1195
 
                region |= r;
1196
 
            }
1197
 
        }
1198
 
 
1199
 
        for (int line = spot->startLine() ; line <= spot->endLine() ; line++) {
1200
 
            int startColumn = 0;
1201
 
            int endColumn = _columns - 1; // TODO use number of _columns which are actually
1202
 
            // occupied on this line rather than the width of the
1203
 
            // display in _columns
1204
 
 
1205
 
            // ignore whitespace at the end of the lines
1206
 
            while (_image[loc(endColumn, line)].isSpace() && endColumn > 0)
1207
 
                endColumn--;
1208
 
 
1209
 
            // increment here because the column which we want to set 'endColumn' to
1210
 
            // is the first whitespace character at the end of the line
1211
 
            endColumn++;
1212
 
 
1213
 
            if (line == spot->startLine())
1214
 
                startColumn = spot->startColumn();
1215
 
            if (line == spot->endLine())
1216
 
                endColumn = spot->endColumn();
1217
 
 
1218
 
            // subtract one pixel from
1219
 
            // the right and bottom so that
1220
 
            // we do not overdraw adjacent
1221
 
            // hotspots
1222
 
            //
1223
 
            // subtracting one pixel from all sides also prevents an edge case where
1224
 
            // moving the mouse outside a link could still leave it underlined
1225
 
            // because the check below for the position of the cursor
1226
 
            // finds it on the border of the target area
1227
 
            QRect r;
1228
 
            r.setCoords(startColumn * _fontWidth + 1 + scrollBarWidth,
1229
 
                        line * _fontHeight + 1,
1230
 
                        endColumn * _fontWidth - 1 + scrollBarWidth,
1231
 
                        (line + 1)*_fontHeight - 1);
1232
 
            // Underline link hotspots
1233
 
            if (_underlineLinks && spot->type() == Filter::HotSpot::Link) {
1234
 
                QFontMetrics metrics(font());
1235
 
 
1236
 
                // find the baseline (which is the invisible line that the characters in the font sit on,
1237
 
                // with some having tails dangling below)
1238
 
                const int baseline = r.bottom() - metrics.descent();
1239
 
                // find the position of the underline below that
1240
 
                const int underlinePos = baseline + metrics.underlinePos();
1241
 
                if (region.contains(mapFromGlobal(QCursor::pos()))) {
1242
 
                    painter.drawLine(r.left() , underlinePos ,
1243
 
                                     r.right() , underlinePos);
1244
 
                }
1245
 
                // Marker hotspots simply have a transparent rectanglular shape
1246
 
                // drawn on top of them
1247
 
            } else if (spot->type() == Filter::HotSpot::Marker) {
1248
 
                //TODO - Do not use a hardcoded color for this
1249
 
                painter.fillRect(r, QBrush(QColor(255, 0, 0, 120)));
1250
 
            }
1251
 
        }
1252
 
    }
1253
 
}
1254
 
void TerminalDisplay::drawContents(QPainter& paint, const QRect& rect)
1255
 
{
1256
 
    const QPoint tL  = contentsRect().topLeft();
1257
 
    const int    tLx = tL.x();
1258
 
    const int    tLy = tL.y();
1259
 
 
1260
 
    const int lux = qMin(_usedColumns - 1, qMax(0, (rect.left()   - tLx - _leftMargin) / _fontWidth));
1261
 
    const int luy = qMin(_usedLines - 1,  qMax(0, (rect.top()    - tLy - _topMargin) / _fontHeight));
1262
 
    const int rlx = qMin(_usedColumns - 1, qMax(0, (rect.right()  - tLx - _leftMargin) / _fontWidth));
1263
 
    const int rly = qMin(_usedLines - 1,  qMax(0, (rect.bottom() - tLy - _topMargin) / _fontHeight));
1264
 
 
1265
 
    const int numberOfColumns = _usedColumns;
1266
 
    QString unistr;
1267
 
    unistr.reserve(numberOfColumns);
1268
 
    for (int y = luy; y <= rly; y++) {
1269
 
        int x = lux;
1270
 
        if (!_image[loc(lux, y)].character && x)
1271
 
            x--; // Search for start of multi-column character
1272
 
        for (; x <= rlx; x++) {
1273
 
            int len = 1;
1274
 
            int p = 0;
1275
 
 
1276
 
            // reset our buffer to the number of columns
1277
 
            int bufferSize = numberOfColumns;
1278
 
            unistr.resize(bufferSize);
1279
 
            QChar *disstrU = unistr.data();
1280
 
 
1281
 
            // is this a single character or a sequence of characters ?
1282
 
            if (_image[loc(x, y)].rendition & RE_EXTENDED_CHAR) {
1283
 
                // sequence of characters
1284
 
                ushort extendedCharLength = 0;
1285
 
                const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(_image[loc(x, y)].character, extendedCharLength);
1286
 
                if (chars) {
1287
 
                    Q_ASSERT(extendedCharLength > 1);
1288
 
                    bufferSize += extendedCharLength - 1;
1289
 
                    unistr.resize(bufferSize);
1290
 
                    disstrU = unistr.data();
1291
 
                    for (int index = 0 ; index < extendedCharLength ; index++) {
1292
 
                        Q_ASSERT(p < bufferSize);
1293
 
                        disstrU[p++] = chars[index];
1294
 
                    }
1295
 
                }
1296
 
            } else {
1297
 
                // single character
1298
 
                const quint16 c = _image[loc(x, y)].character;
1299
 
                if (c) {
1300
 
                    Q_ASSERT(p < bufferSize);
1301
 
                    disstrU[p++] = c; //fontMap(c);
1302
 
                }
1303
 
            }
1304
 
 
1305
 
            const bool lineDraw = _image[loc(x, y)].isLineChar();
1306
 
            const bool doubleWidth = (_image[ qMin(loc(x, y) + 1, _imageSize) ].character == 0);
1307
 
            const CharacterColor currentForeground = _image[loc(x, y)].foregroundColor;
1308
 
            const CharacterColor currentBackground = _image[loc(x, y)].backgroundColor;
1309
 
            const quint8 currentRendition = _image[loc(x, y)].rendition;
1310
 
 
1311
 
            while (x + len <= rlx &&
1312
 
                    _image[loc(x + len, y)].foregroundColor == currentForeground &&
1313
 
                    _image[loc(x + len, y)].backgroundColor == currentBackground &&
1314
 
                    (_image[loc(x + len, y)].rendition & ~RE_EXTENDED_CHAR) == (currentRendition & ~RE_EXTENDED_CHAR) &&
1315
 
                    (_image[ qMin(loc(x + len, y) + 1, _imageSize) ].character == 0) == doubleWidth &&
1316
 
                    _image[loc(x + len, y)].isLineChar() == lineDraw) {
1317
 
                const quint16 c = _image[loc(x + len, y)].character;
1318
 
                if (_image[loc(x + len, y)].rendition & RE_EXTENDED_CHAR) {
1319
 
                    // sequence of characters
1320
 
                    ushort extendedCharLength = 0;
1321
 
                    const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(c, extendedCharLength);
1322
 
                    if (chars) {
1323
 
                        Q_ASSERT(extendedCharLength > 1);
1324
 
                        bufferSize += extendedCharLength - 1;
1325
 
                        unistr.resize(bufferSize);
1326
 
                        disstrU = unistr.data();
1327
 
                        for (int index = 0 ; index < extendedCharLength ; index++) {
1328
 
                            Q_ASSERT(p < bufferSize);
1329
 
                            disstrU[p++] = chars[index];
1330
 
                        }
1331
 
                    }
1332
 
                } else {
1333
 
                    // single character
1334
 
                    if (c) {
1335
 
                        Q_ASSERT(p < bufferSize);
1336
 
                        disstrU[p++] = c; //fontMap(c);
1337
 
                    }
1338
 
                }
1339
 
 
1340
 
                if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition
1341
 
                    len++; // Skip trailing part of multi-column character
1342
 
                len++;
1343
 
            }
1344
 
            if ((x + len < _usedColumns) && (!_image[loc(x + len, y)].character))
1345
 
                len++; // Adjust for trailing part of multi-column character
1346
 
 
1347
 
            const bool save__fixedFont = _fixedFont;
1348
 
            if (lineDraw)
1349
 
                _fixedFont = false;
1350
 
            if (doubleWidth)
1351
 
                _fixedFont = false;
1352
 
            unistr.resize(p);
1353
 
 
1354
 
            // Create a text scaling matrix for double width and double height lines.
1355
 
            QMatrix textScale;
1356
 
 
1357
 
            if (y < _lineProperties.size()) {
1358
 
                if (_lineProperties[y] & LINE_DOUBLEWIDTH)
1359
 
                    textScale.scale(2, 1);
1360
 
 
1361
 
                if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
1362
 
                    textScale.scale(1, 2);
1363
 
            }
1364
 
 
1365
 
            //Apply text scaling matrix.
1366
 
            paint.setWorldMatrix(textScale, true);
1367
 
 
1368
 
            //calculate the area in which the text will be drawn
1369
 
            QRect textArea = QRect(_leftMargin + tLx + _fontWidth * x , _topMargin + tLy + _fontHeight * y , _fontWidth * len , _fontHeight);
1370
 
 
1371
 
            //move the calculated area to take account of scaling applied to the painter.
1372
 
            //the position of the area from the origin (0,0) is scaled
1373
 
            //by the opposite of whatever
1374
 
            //transformation has been applied to the painter.  this ensures that
1375
 
            //painting does actually start from textArea.topLeft()
1376
 
            //(instead of textArea.topLeft() * painter-scale)
1377
 
            textArea.moveTopLeft(textScale.inverted().map(textArea.topLeft()));
1378
 
 
1379
 
            //paint text fragment
1380
 
            drawTextFragment(paint,
1381
 
                             textArea,
1382
 
                             unistr,
1383
 
                             &_image[loc(x, y)]); //,
1384
 
            //0,
1385
 
            //!_isPrinting );
1386
 
 
1387
 
            _fixedFont = save__fixedFont;
1388
 
 
1389
 
            //reset back to single-width, single-height _lines
1390
 
            paint.setWorldMatrix(textScale.inverted(), true);
1391
 
 
1392
 
            if (y < _lineProperties.size() - 1) {
1393
 
                //double-height _lines are represented by two adjacent _lines
1394
 
                //containing the same characters
1395
 
                //both _lines will have the LINE_DOUBLEHEIGHT attribute.
1396
 
                //If the current line has the LINE_DOUBLEHEIGHT attribute,
1397
 
                //we can therefore skip the next line
1398
 
                if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
1399
 
                    y++;
1400
 
            }
1401
 
 
1402
 
            x += len - 1;
1403
 
        }
1404
 
    }
1405
 
}
1406
 
 
1407
 
QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const
1408
 
{
1409
 
    QRect result;
1410
 
    result.setLeft(_leftMargin + _fontWidth * imageArea.left());
1411
 
    result.setTop(_topMargin + _fontHeight * imageArea.top());
1412
 
    result.setWidth(_fontWidth * imageArea.width());
1413
 
    result.setHeight(_fontHeight * imageArea.height());
1414
 
 
1415
 
    return result;
1416
 
}
1417
 
 
1418
 
/* ------------------------------------------------------------------------- */
1419
 
/*                                                                           */
1420
 
/*                          Blinking Text & Cursor                           */
1421
 
/*                                                                           */
1422
 
/* ------------------------------------------------------------------------- */
1423
 
 
1424
 
void TerminalDisplay::setBlinkingCursorEnabled(bool blink)
1425
 
{
1426
 
    _allowBlinkingCursor = blink;
1427
 
 
1428
 
    if (blink && !_blinkCursorTimer->isActive())
1429
 
        _blinkCursorTimer->start();
1430
 
 
1431
 
    if (!blink && _blinkCursorTimer->isActive()) {
1432
 
        _blinkCursorTimer->stop();
1433
 
        if (_cursorBlinking) {
1434
 
            // if cursor is blinking(hidden), blink it again to make it show
1435
 
            blinkCursorEvent();
1436
 
        }
1437
 
        Q_ASSERT( _cursorBlinking == false );
1438
 
    }
1439
 
}
1440
 
 
1441
 
void TerminalDisplay::setBlinkingTextEnabled(bool blink)
1442
 
{
1443
 
    _allowBlinkingText = blink;
1444
 
 
1445
 
    if (blink && !_blinkTextTimer->isActive())
1446
 
        _blinkTextTimer->start();
1447
 
 
1448
 
    if (!blink && _blinkTextTimer->isActive()) {
1449
 
        _blinkTextTimer->stop();
1450
 
        _textBlinking = false;
1451
 
    }
1452
 
}
1453
 
 
1454
 
void TerminalDisplay::focusOutEvent(QFocusEvent*)
1455
 
{
1456
 
    // trigger a repaint of the cursor so that it is both:
1457
 
    //
1458
 
    //   * visible (in case it was hidden during blinking)
1459
 
    //   * drawn in a focused out state
1460
 
    _cursorBlinking = false;
1461
 
    updateCursor();
1462
 
 
1463
 
    // suppress further cursor blinking
1464
 
    _blinkCursorTimer->stop();
1465
 
    Q_ASSERT( _cursorBlinking == false );
1466
 
 
1467
 
    // if text is blinking (hidden), blink it again to make it shown
1468
 
    if (_textBlinking)
1469
 
        blinkTextEvent();
1470
 
 
1471
 
    // suppress further text blinking
1472
 
    _blinkTextTimer->stop();
1473
 
    Q_ASSERT( _textBlinking == false );
1474
 
}
1475
 
 
1476
 
void TerminalDisplay::focusInEvent(QFocusEvent*)
1477
 
{
1478
 
    if (_allowBlinkingCursor)
1479
 
        _blinkCursorTimer->start();
1480
 
 
1481
 
    updateCursor();
1482
 
 
1483
 
    if (_allowBlinkingText && _hasTextBlinker)
1484
 
        _blinkTextTimer->start();
1485
 
}
1486
 
 
1487
 
void TerminalDisplay::blinkTextEvent()
1488
 
{
1489
 
    Q_ASSERT(_allowBlinkingText);
1490
 
 
1491
 
    _textBlinking = !_textBlinking;
1492
 
 
1493
 
    // TODO: Optimize to only repaint the areas of the widget where there is
1494
 
    // blinking text rather than repainting the whole widget.
1495
 
    update();
1496
 
}
1497
 
 
1498
 
void TerminalDisplay::blinkCursorEvent()
1499
 
{
1500
 
    Q_ASSERT(_allowBlinkingCursor);
1501
 
 
1502
 
    _cursorBlinking = !_cursorBlinking;
1503
 
    updateCursor();
1504
 
}
1505
 
 
1506
 
void TerminalDisplay::updateCursor()
1507
 
{
1508
 
    QRect cursorRect = imageToWidget(QRect(cursorPosition(), QSize(1, 1)));
1509
 
    update(cursorRect);
1510
 
}
1511
 
 
1512
 
/* ------------------------------------------------------------------------- */
1513
 
/*                                                                           */
1514
 
/*                          Geometry & Resizing                              */
1515
 
/*                                                                           */
1516
 
/* ------------------------------------------------------------------------- */
1517
 
 
1518
 
void TerminalDisplay::resizeEvent(QResizeEvent*)
1519
 
{
1520
 
    updateImageSize();
1521
 
}
1522
 
 
1523
 
void TerminalDisplay::propagateSize()
1524
 
{
1525
 
    if (_isFixedSize) {
1526
 
        setSize(_columns, _lines);
1527
 
        QWidget::setFixedSize(sizeHint());
1528
 
        parentWidget()->adjustSize();
1529
 
        parentWidget()->setFixedSize(parentWidget()->sizeHint());
1530
 
        return;
1531
 
    }
1532
 
    if (_image)
1533
 
        updateImageSize();
1534
 
}
1535
 
 
1536
 
void TerminalDisplay::updateImageSize()
1537
 
{
1538
 
    Character* oldImage = _image;
1539
 
    const int oldLines = _lines;
1540
 
    const int oldColumns = _columns;
1541
 
 
1542
 
    makeImage();
1543
 
 
1544
 
    if (oldImage) {
1545
 
        // copy the old image to reduce flicker
1546
 
        int lines = qMin(oldLines, _lines);
1547
 
        int columns = qMin(oldColumns, _columns);
1548
 
        for (int line = 0; line < lines; line++) {
1549
 
            memcpy((void*)&_image[_columns * line],
1550
 
                   (void*)&oldImage[oldColumns * line],
1551
 
                   columns * sizeof(Character));
1552
 
        }
1553
 
        delete[] oldImage;
1554
 
    }
1555
 
 
1556
 
    if (_screenWindow)
1557
 
        _screenWindow->setWindowLines(_lines);
1558
 
 
1559
 
    _resizing = (oldLines != _lines) || (oldColumns != _columns);
1560
 
 
1561
 
    if (_resizing) {
1562
 
        showResizeNotification();
1563
 
        emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent
1564
 
    }
1565
 
 
1566
 
    _resizing = false;
1567
 
}
1568
 
 
1569
 
void TerminalDisplay::makeImage()
1570
 
{
1571
 
    _wallpaper->load();
1572
 
 
1573
 
    calcGeometry();
1574
 
 
1575
 
    // confirm that array will be of non-zero size, since the painting code
1576
 
    // assumes a non-zero array length
1577
 
    Q_ASSERT(_lines > 0 && _columns > 0);
1578
 
    Q_ASSERT(_usedLines <= _lines && _usedColumns <= _columns);
1579
 
 
1580
 
    _imageSize = _lines * _columns;
1581
 
 
1582
 
    // We over-commit one character so that we can be more relaxed in dealing with
1583
 
    // certain boundary conditions: _image[_imageSize] is a valid but unused position
1584
 
    _image = new Character[_imageSize + 1];
1585
 
 
1586
 
    clearImage();
1587
 
}
1588
 
 
1589
 
void TerminalDisplay::clearImage()
1590
 
{
1591
 
    for (int i = 0; i <= _imageSize; ++i)
1592
 
        _image[i] = Screen::DefaultChar;
1593
 
}
1594
 
 
1595
 
void TerminalDisplay::calcGeometry()
1596
 
{
1597
 
    _scrollBar->resize(_scrollBar->sizeHint().width(), contentsRect().height());
1598
 
    switch (_scrollbarLocation) {
1599
 
    case Enum::ScrollBarHidden :
1600
 
        _leftMargin = DEFAULT_LEFT_MARGIN;
1601
 
        _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN;
1602
 
        break;
1603
 
    case Enum::ScrollBarLeft :
1604
 
        _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width();
1605
 
        _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width();
1606
 
        _scrollBar->move(contentsRect().topLeft());
1607
 
        break;
1608
 
    case Enum::ScrollBarRight:
1609
 
        _leftMargin = DEFAULT_LEFT_MARGIN;
1610
 
        _contentWidth = contentsRect().width()  - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width();
1611
 
        _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width() - 1, 0));
1612
 
        break;
1613
 
    }
1614
 
 
1615
 
    _topMargin = DEFAULT_TOP_MARGIN;
1616
 
    _contentHeight = contentsRect().height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1;
1617
 
 
1618
 
    if (!_isFixedSize) {
1619
 
        // ensure that display is always at least one column wide
1620
 
        _columns = qMax(1, _contentWidth / _fontWidth);
1621
 
        _usedColumns = qMin(_usedColumns, _columns);
1622
 
 
1623
 
        // ensure that display is always at least one line high
1624
 
        _lines = qMax(1, _contentHeight / _fontHeight);
1625
 
        _usedLines = qMin(_usedLines, _lines);
1626
 
    }
1627
 
}
1628
 
 
1629
 
// calculate the needed size, this must be synced with calcGeometry()
1630
 
void TerminalDisplay::setSize(int columns, int lines)
1631
 
{
1632
 
    const int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->sizeHint().width();
1633
 
    const int horizontalMargin = 2 * DEFAULT_LEFT_MARGIN;
1634
 
    const int verticalMargin = 2 * DEFAULT_TOP_MARGIN;
1635
 
 
1636
 
    QSize newSize = QSize(horizontalMargin + scrollBarWidth + (columns * _fontWidth)  ,
1637
 
                          verticalMargin + (lines * _fontHeight));
1638
 
 
1639
 
    if (newSize != size()) {
1640
 
        _size = newSize;
1641
 
        updateGeometry();
1642
 
    }
1643
 
}
1644
 
 
1645
 
void TerminalDisplay::setFixedSize(int cols, int lins)
1646
 
{
1647
 
    _isFixedSize = true;
1648
 
 
1649
 
    //ensure that display is at least one line by one column in size
1650
 
    _columns = qMax(1, cols);
1651
 
    _lines = qMax(1, lins);
1652
 
    _usedColumns = qMin(_usedColumns, _columns);
1653
 
    _usedLines = qMin(_usedLines, _lines);
1654
 
 
1655
 
    if (_image) {
1656
 
        delete[] _image;
1657
 
        makeImage();
1658
 
    }
1659
 
    setSize(cols, lins);
1660
 
    QWidget::setFixedSize(_size);
1661
 
}
1662
 
 
1663
 
QSize TerminalDisplay::sizeHint() const
1664
 
{
1665
 
    return _size;
1666
 
}
1667
 
 
1668
 
//showEvent and hideEvent are reimplemented here so that it appears to other classes that the
1669
 
//display has been resized when the display is hidden or shown.
1670
 
//
1671
 
//TODO: Perhaps it would be better to have separate signals for show and hide instead of using
1672
 
//the same signal as the one for a content size change
1673
 
void TerminalDisplay::showEvent(QShowEvent*)
1674
 
{
1675
 
    emit changedContentSizeSignal(_contentHeight, _contentWidth);
1676
 
}
1677
 
void TerminalDisplay::hideEvent(QHideEvent*)
1678
 
{
1679
 
    emit changedContentSizeSignal(_contentHeight, _contentWidth);
1680
 
}
1681
 
 
1682
 
/* ------------------------------------------------------------------------- */
1683
 
/*                                                                           */
1684
 
/*                                Scrollbar                                  */
1685
 
/*                                                                           */
1686
 
/* ------------------------------------------------------------------------- */
1687
 
 
1688
 
void TerminalDisplay::setScrollBarPosition(Enum::ScrollBarPositionEnum position)
1689
 
{
1690
 
    if (_scrollbarLocation == position)
1691
 
        return;
1692
 
 
1693
 
    if (position == Enum::ScrollBarHidden)
1694
 
        _scrollBar->hide();
1695
 
    else
1696
 
        _scrollBar->show();
1697
 
 
1698
 
    _topMargin = _leftMargin = 1;
1699
 
    _scrollbarLocation = position;
1700
 
 
1701
 
    propagateSize();
1702
 
    update();
1703
 
}
1704
 
 
1705
 
void TerminalDisplay::scrollBarPositionChanged(int)
1706
 
{
1707
 
    if (!_screenWindow)
1708
 
        return;
1709
 
 
1710
 
    _screenWindow->scrollTo(_scrollBar->value());
1711
 
 
1712
 
    // if the thumb has been moved to the bottom of the _scrollBar then set
1713
 
    // the display to automatically track new output,
1714
 
    // that is, scroll down automatically
1715
 
    // to how new _lines as they are added
1716
 
    const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum());
1717
 
    _screenWindow->setTrackOutput(atEndOfOutput);
1718
 
 
1719
 
    updateImage();
1720
 
}
1721
 
 
1722
 
void TerminalDisplay::setScroll(int cursor, int slines)
1723
 
{
1724
 
    // update _scrollBar if the range or value has changed,
1725
 
    // otherwise return
1726
 
    //
1727
 
    // setting the range or value of a _scrollBar will always trigger
1728
 
    // a repaint, so it should be avoided if it is not necessary
1729
 
    if (_scrollBar->minimum() == 0                 &&
1730
 
            _scrollBar->maximum() == (slines - _lines) &&
1731
 
            _scrollBar->value()   == cursor) {
1732
 
        return;
1733
 
    }
1734
 
 
1735
 
    disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int)));
1736
 
    _scrollBar->setRange(0, slines - _lines);
1737
 
    _scrollBar->setSingleStep(1);
1738
 
    _scrollBar->setPageStep(_lines);
1739
 
    _scrollBar->setValue(cursor);
1740
 
    connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int)));
1741
 
}
1742
 
 
1743
 
/* ------------------------------------------------------------------------- */
1744
 
/*                                                                           */
1745
 
/*                                  Mouse                                    */
1746
 
/*                                                                           */
1747
 
/* ------------------------------------------------------------------------- */
1748
 
void TerminalDisplay::mousePressEvent(QMouseEvent* ev)
1749
 
{
1750
 
    if (_possibleTripleClick && (ev->button() == Qt::LeftButton)) {
1751
 
        mouseTripleClickEvent(ev);
1752
 
        return;
1753
 
    }
1754
 
 
1755
 
    if (!contentsRect().contains(ev->pos())) return;
1756
 
 
1757
 
    if (!_screenWindow) return;
1758
 
 
1759
 
    int charLine;
1760
 
    int charColumn;
1761
 
    getCharacterPosition(ev->pos(), charLine, charColumn);
1762
 
    QPoint pos = QPoint(charColumn, charLine);
1763
 
 
1764
 
    if (ev->button() == Qt::LeftButton) {
1765
 
        // request the software keyboard, if any
1766
 
        if (qApp->autoSipEnabled()) {
1767
 
            QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(
1768
 
                        style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
1769
 
            if (hasFocus() || behavior == QStyle::RSIP_OnMouseClick) {
1770
 
                QEvent event(QEvent::RequestSoftwareInputPanel);
1771
 
                QApplication::sendEvent(this, &event);
1772
 
            }
1773
 
        }
1774
 
 
1775
 
        _lineSelectionMode = false;
1776
 
        _wordSelectionMode = false;
1777
 
 
1778
 
        bool selected = false;
1779
 
        // The user clicked inside selected text
1780
 
        selected =  _screenWindow->isSelected(pos.x(), pos.y());
1781
 
 
1782
 
        // Drag only when the Control key is hold
1783
 
        if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected) {
1784
 
            _dragInfo.state = diPending;
1785
 
            _dragInfo.start = ev->pos();
1786
 
        } else {
1787
 
            // No reason to ever start a drag event
1788
 
            _dragInfo.state = diNone;
1789
 
 
1790
 
            _preserveLineBreaks = !((ev->modifiers() & Qt::ControlModifier) && !(ev->modifiers() & Qt::AltModifier));
1791
 
            _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier);
1792
 
 
1793
 
            if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) {
1794
 
                _screenWindow->clearSelection();
1795
 
 
1796
 
                pos.ry() += _scrollBar->value();
1797
 
                _iPntSel = _pntSel = pos;
1798
 
                _actSel = 1; // left mouse button pressed but nothing selected yet.
1799
 
 
1800
 
            } else {
1801
 
                emit mouseSignal(0, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0);
1802
 
            }
1803
 
        }
1804
 
    } else if (ev->button() == Qt::MidButton) {
1805
 
        processMidButtonClick(ev);
1806
 
    } else if (ev->button() == Qt::RightButton) {
1807
 
        if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier))
1808
 
            emit configureRequest(ev->pos());
1809
 
        else
1810
 
            emit mouseSignal(2, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0);
1811
 
    }
1812
 
}
1813
 
 
1814
 
QList<QAction*> TerminalDisplay::filterActions(const QPoint& position)
1815
 
{
1816
 
    int charLine, charColumn;
1817
 
    getCharacterPosition(position, charLine, charColumn);
1818
 
 
1819
 
    Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine, charColumn);
1820
 
 
1821
 
    return spot ? spot->actions() : QList<QAction*>();
1822
 
}
1823
 
 
1824
 
void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev)
1825
 
{
1826
 
    int charLine = 0;
1827
 
    int charColumn = 0;
1828
 
    getCharacterPosition(ev->pos(), charLine, charColumn);
1829
 
 
1830
 
    const int scrollBarWidth = (_scrollbarLocation == Enum::ScrollBarLeft) ? _scrollBar->width() : 0;
1831
 
 
1832
 
    // handle filters
1833
 
    // change link hot-spot appearance on mouse-over
1834
 
    Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine, charColumn);
1835
 
    if (_underlineLinks && spot && spot->type() == Filter::HotSpot::Link) {
1836
 
        QRegion previousHotspotArea = _mouseOverHotspotArea;
1837
 
        _mouseOverHotspotArea = QRegion();
1838
 
        QRect r;
1839
 
        if (spot->startLine() == spot->endLine()) {
1840
 
            r.setCoords(spot->startColumn()*_fontWidth + scrollBarWidth,
1841
 
                        spot->startLine()*_fontHeight,
1842
 
                        spot->endColumn()*_fontWidth + scrollBarWidth,
1843
 
                        (spot->endLine() + 1)*_fontHeight - 1);
1844
 
            _mouseOverHotspotArea |= r;
1845
 
        } else {
1846
 
            r.setCoords(spot->startColumn()*_fontWidth + scrollBarWidth,
1847
 
                        spot->startLine()*_fontHeight,
1848
 
                        _columns * _fontWidth - 1 + scrollBarWidth,
1849
 
                        (spot->startLine() + 1)*_fontHeight);
1850
 
            _mouseOverHotspotArea |= r;
1851
 
            for (int line = spot->startLine() + 1 ; line < spot->endLine() ; line++) {
1852
 
                r.setCoords(0 * _fontWidth + scrollBarWidth,
1853
 
                            line * _fontHeight,
1854
 
                            _columns * _fontWidth + scrollBarWidth,
1855
 
                            (line + 1)*_fontHeight);
1856
 
                _mouseOverHotspotArea |= r;
1857
 
            }
1858
 
            r.setCoords(0 * _fontWidth + scrollBarWidth,
1859
 
                        spot->endLine()*_fontHeight,
1860
 
                        spot->endColumn()*_fontWidth + scrollBarWidth,
1861
 
                        (spot->endLine() + 1)*_fontHeight);
1862
 
            _mouseOverHotspotArea |= r;
1863
 
        }
1864
 
 
1865
 
        update(_mouseOverHotspotArea | previousHotspotArea);
1866
 
    } else if (!_mouseOverHotspotArea.isEmpty()) {
1867
 
        update(_mouseOverHotspotArea);
1868
 
        // set hotspot area to an invalid rectangle
1869
 
        _mouseOverHotspotArea = QRegion();
1870
 
    }
1871
 
 
1872
 
    // for auto-hiding the cursor, we need mouseTracking
1873
 
    if (ev->buttons() == Qt::NoButton) return;
1874
 
 
1875
 
    // if the terminal is interested in mouse movements
1876
 
    // then emit a mouse movement signal, unless the shift
1877
 
    // key is being held down, which overrides this.
1878
 
    if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) {
1879
 
        int button = 3;
1880
 
        if (ev->buttons() & Qt::LeftButton)
1881
 
            button = 0;
1882
 
        if (ev->buttons() & Qt::MidButton)
1883
 
            button = 1;
1884
 
        if (ev->buttons() & Qt::RightButton)
1885
 
            button = 2;
1886
 
 
1887
 
        emit mouseSignal(button,
1888
 
                         charColumn + 1,
1889
 
                         charLine + 1 + _scrollBar->value() - _scrollBar->maximum(),
1890
 
                         1);
1891
 
 
1892
 
        return;
1893
 
    }
1894
 
 
1895
 
    if (_dragInfo.state == diPending) {
1896
 
        // we had a mouse down, but haven't confirmed a drag yet
1897
 
        // if the mouse has moved sufficiently, we will confirm
1898
 
 
1899
 
        const int distance = KGlobalSettings::dndEventDelay();
1900
 
        if (ev->x() > _dragInfo.start.x() + distance || ev->x() < _dragInfo.start.x() - distance ||
1901
 
                ev->y() > _dragInfo.start.y() + distance || ev->y() < _dragInfo.start.y() - distance) {
1902
 
            // we've left the drag square, we can start a real drag operation now
1903
 
 
1904
 
            _screenWindow->clearSelection();
1905
 
            doDrag();
1906
 
        }
1907
 
        return;
1908
 
    } else if (_dragInfo.state == diDragging) {
1909
 
        // this isn't technically needed because mouseMoveEvent is suppressed during
1910
 
        // Qt drag operations, replaced by dragMoveEvent
1911
 
        return;
1912
 
    }
1913
 
 
1914
 
    if (_actSel == 0) return;
1915
 
 
1916
 
// don't extend selection while pasting
1917
 
    if (ev->buttons() & Qt::MidButton) return;
1918
 
 
1919
 
    extendSelection(ev->pos());
1920
 
}
1921
 
 
1922
 
void TerminalDisplay::extendSelection(const QPoint& position)
1923
 
{
1924
 
    if (!_screenWindow)
1925
 
        return;
1926
 
 
1927
 
    //if ( !contentsRect().contains(ev->pos()) ) return;
1928
 
    const QPoint tL  = contentsRect().topLeft();
1929
 
    const int    tLx = tL.x();
1930
 
    const int    tLy = tL.y();
1931
 
    const int    scroll = _scrollBar->value();
1932
 
 
1933
 
    // we're in the process of moving the mouse with the left button pressed
1934
 
    // the mouse cursor will kept caught within the bounds of the text in
1935
 
    // this widget.
1936
 
 
1937
 
    int linesBeyondWidget = 0;
1938
 
 
1939
 
    QRect textBounds(tLx + _leftMargin,
1940
 
                     tLy + _topMargin,
1941
 
                     _usedColumns * _fontWidth - 1,
1942
 
                     _usedLines * _fontHeight - 1);
1943
 
 
1944
 
    QPoint pos = position;
1945
 
 
1946
 
    // Adjust position within text area bounds.
1947
 
    const QPoint oldpos = pos;
1948
 
 
1949
 
    pos.setX(qBound(textBounds.left(), pos.x(), textBounds.right()));
1950
 
    pos.setY(qBound(textBounds.top(), pos.y(), textBounds.bottom()));
1951
 
 
1952
 
    if (oldpos.y() > textBounds.bottom()) {
1953
 
        linesBeyondWidget = (oldpos.y() - textBounds.bottom()) / _fontHeight;
1954
 
        _scrollBar->setValue(_scrollBar->value() + linesBeyondWidget + 1); // scrollforward
1955
 
    }
1956
 
    if (oldpos.y() < textBounds.top()) {
1957
 
        linesBeyondWidget = (textBounds.top() - oldpos.y()) / _fontHeight;
1958
 
        _scrollBar->setValue(_scrollBar->value() - linesBeyondWidget - 1); // history
1959
 
    }
1960
 
 
1961
 
    int charColumn = 0;
1962
 
    int charLine = 0;
1963
 
    getCharacterPosition(pos, charLine, charColumn);
1964
 
 
1965
 
    QPoint here = QPoint(charColumn, charLine);
1966
 
    QPoint ohere;
1967
 
    QPoint _iPntSelCorr = _iPntSel;
1968
 
    _iPntSelCorr.ry() -= _scrollBar->value();
1969
 
    QPoint _pntSelCorr = _pntSel;
1970
 
    _pntSelCorr.ry() -= _scrollBar->value();
1971
 
    bool swapping = false;
1972
 
 
1973
 
    if (_wordSelectionMode) {
1974
 
        // Extend to word boundaries
1975
 
        int i;
1976
 
        QChar selClass;
1977
 
 
1978
 
        const bool left_not_right = (here.y() < _iPntSelCorr.y() ||
1979
 
                                     (here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x()));
1980
 
        const bool old_left_not_right = (_pntSelCorr.y() < _iPntSelCorr.y() ||
1981
 
                                         (_pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x()));
1982
 
        swapping = left_not_right != old_left_not_right;
1983
 
 
1984
 
        // Find left (left_not_right ? from here : from start)
1985
 
        QPoint left = left_not_right ? here : _iPntSelCorr;
1986
 
        i = loc(left.x(), left.y());
1987
 
        if (i >= 0 && i <= _imageSize) {
1988
 
            selClass = charClass(_image[i]);
1989
 
            while (((left.x() > 0) || (left.y() > 0 && (_lineProperties[left.y() - 1] & LINE_WRAPPED)))
1990
 
                    && charClass(_image[i - 1]) == selClass) {
1991
 
                i--;
1992
 
                if (left.x() > 0) left.rx()--;
1993
 
                else {
1994
 
                    left.rx() = _usedColumns - 1;
1995
 
                    left.ry()--;
1996
 
                }
1997
 
            }
1998
 
        }
1999
 
 
2000
 
        // Find left (left_not_right ? from start : from here)
2001
 
        QPoint right = left_not_right ? _iPntSelCorr : here;
2002
 
        i = loc(right.x(), right.y());
2003
 
        if (i >= 0 && i <= _imageSize) {
2004
 
            selClass = charClass(_image[i]);
2005
 
            while (((right.x() < _usedColumns - 1) || (right.y() < _usedLines - 1 && (_lineProperties[right.y()] & LINE_WRAPPED)))
2006
 
                    && charClass(_image[i + 1]) == selClass) {
2007
 
                i++;
2008
 
                if (right.x() < _usedColumns - 1) right.rx()++;
2009
 
                else {
2010
 
                    right.rx() = 0;
2011
 
                    right.ry()++;
2012
 
                }
2013
 
            }
2014
 
        }
2015
 
 
2016
 
        // Pick which is start (ohere) and which is extension (here)
2017
 
        if (left_not_right) {
2018
 
            here = left;
2019
 
            ohere = right;
2020
 
        } else {
2021
 
            here = right;
2022
 
            ohere = left;
2023
 
        }
2024
 
        ohere.rx()++;
2025
 
    }
2026
 
 
2027
 
    if (_lineSelectionMode) {
2028
 
        // Extend to complete line
2029
 
        const bool above_not_below = (here.y() < _iPntSelCorr.y());
2030
 
 
2031
 
        QPoint above = above_not_below ? here : _iPntSelCorr;
2032
 
        QPoint below = above_not_below ? _iPntSelCorr : here;
2033
 
 
2034
 
        while (above.y() > 0 && (_lineProperties[above.y() - 1] & LINE_WRAPPED))
2035
 
            above.ry()--;
2036
 
        while (below.y() < _usedLines - 1 && (_lineProperties[below.y()] & LINE_WRAPPED))
2037
 
            below.ry()++;
2038
 
 
2039
 
        above.setX(0);
2040
 
        below.setX(_usedColumns - 1);
2041
 
 
2042
 
        // Pick which is start (ohere) and which is extension (here)
2043
 
        if (above_not_below) {
2044
 
            here = above;
2045
 
            ohere = below;
2046
 
        } else {
2047
 
            here = below;
2048
 
            ohere = above;
2049
 
        }
2050
 
 
2051
 
        const QPoint newSelBegin = QPoint(ohere.x(), ohere.y());
2052
 
        swapping = !(_tripleSelBegin == newSelBegin);
2053
 
        _tripleSelBegin = newSelBegin;
2054
 
 
2055
 
        ohere.rx()++;
2056
 
    }
2057
 
 
2058
 
    int offset = 0;
2059
 
    if (!_wordSelectionMode && !_lineSelectionMode) {
2060
 
        int i;
2061
 
        QChar selClass;
2062
 
 
2063
 
        const bool left_not_right = (here.y() < _iPntSelCorr.y() ||
2064
 
                                     (here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x()));
2065
 
        const bool old_left_not_right = (_pntSelCorr.y() < _iPntSelCorr.y() ||
2066
 
                                         (_pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x()));
2067
 
        swapping = left_not_right != old_left_not_right;
2068
 
 
2069
 
        // Find left (left_not_right ? from here : from start)
2070
 
        const QPoint left = left_not_right ? here : _iPntSelCorr;
2071
 
 
2072
 
        // Find left (left_not_right ? from start : from here)
2073
 
        QPoint right = left_not_right ? _iPntSelCorr : here;
2074
 
        if (right.x() > 0 && !_columnSelectionMode) {
2075
 
            i = loc(right.x(), right.y());
2076
 
            if (i >= 0 && i <= _imageSize) {
2077
 
                selClass = charClass(_image[i - 1]);
2078
 
                /* if (selClass == ' ')
2079
 
                 {
2080
 
                   while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) &&
2081
 
                                   !(_lineProperties[right.y()] & LINE_WRAPPED))
2082
 
                   { i++; right.rx()++; }
2083
 
                   if (right.x() < _usedColumns-1)
2084
 
                     right = left_not_right ? _iPntSelCorr : here;
2085
 
                   else
2086
 
                     right.rx()++;  // will be balanced later because of offset=-1;
2087
 
                 }*/
2088
 
            }
2089
 
        }
2090
 
 
2091
 
        // Pick which is start (ohere) and which is extension (here)
2092
 
        if (left_not_right) {
2093
 
            here = left;
2094
 
            ohere = right;
2095
 
            offset = 0;
2096
 
        } else {
2097
 
            here = right;
2098
 
            ohere = left;
2099
 
            offset = -1;
2100
 
        }
2101
 
    }
2102
 
 
2103
 
    if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved
2104
 
 
2105
 
    if (here == ohere) return; // It's not left, it's not right.
2106
 
 
2107
 
    if (_actSel < 2 || swapping) {
2108
 
        if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) {
2109
 
            _screenWindow->setSelectionStart(ohere.x() , ohere.y() , true);
2110
 
        } else {
2111
 
            _screenWindow->setSelectionStart(ohere.x() - 1 - offset , ohere.y() , false);
2112
 
        }
2113
 
    }
2114
 
 
2115
 
    _actSel = 2; // within selection
2116
 
    _pntSel = here;
2117
 
    _pntSel.ry() += _scrollBar->value();
2118
 
 
2119
 
    if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) {
2120
 
        _screenWindow->setSelectionEnd(here.x() , here.y());
2121
 
    } else {
2122
 
        _screenWindow->setSelectionEnd(here.x() + offset , here.y());
2123
 
    }
2124
 
}
2125
 
 
2126
 
void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev)
2127
 
{
2128
 
    if (!_screenWindow)
2129
 
        return;
2130
 
 
2131
 
    int charLine;
2132
 
    int charColumn;
2133
 
    getCharacterPosition(ev->pos(), charLine, charColumn);
2134
 
 
2135
 
    if (ev->button() == Qt::LeftButton) {
2136
 
        if (_dragInfo.state == diPending) {
2137
 
            // We had a drag event pending but never confirmed.  Kill selection
2138
 
            _screenWindow->clearSelection();
2139
 
        } else {
2140
 
            if (_actSel > 1) {
2141
 
                copyToX11Selection();
2142
 
            }
2143
 
 
2144
 
            _actSel = 0;
2145
 
 
2146
 
            //FIXME: emits a release event even if the mouse is
2147
 
            //       outside the range. The procedure used in `mouseMoveEvent'
2148
 
            //       applies here, too.
2149
 
 
2150
 
            if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier))
2151
 
                emit mouseSignal(3,  // release
2152
 
                                 charColumn + 1,
2153
 
                                 charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0);
2154
 
        }
2155
 
        _dragInfo.state = diNone;
2156
 
    }
2157
 
 
2158
 
    if (!_mouseMarks &&
2159
 
            (ev->button() == Qt::RightButton || ev->button() == Qt::MidButton) &&
2160
 
              !(ev->modifiers() & Qt::ShiftModifier)) {
2161
 
        emit mouseSignal(3,
2162
 
                         charColumn + 1,
2163
 
                         charLine + 1 + _scrollBar->value() - _scrollBar->maximum() ,
2164
 
                         0);
2165
 
    }
2166
 
}
2167
 
 
2168
 
void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint, int& line, int& column) const
2169
 
{
2170
 
    column = (widgetPoint.x() + _fontWidth / 2 - contentsRect().left() - _leftMargin) / _fontWidth;
2171
 
    line = (widgetPoint.y() - contentsRect().top() - _topMargin) / _fontHeight;
2172
 
 
2173
 
    if (line < 0)
2174
 
        line = 0;
2175
 
    if (column < 0)
2176
 
        column = 0;
2177
 
 
2178
 
    if (line >= _usedLines)
2179
 
        line = _usedLines - 1;
2180
 
 
2181
 
    // the column value returned can be equal to _usedColumns, which
2182
 
    // is the position just after the last character displayed in a line.
2183
 
    //
2184
 
    // this is required so that the user can select characters in the right-most
2185
 
    // column (or left-most for right-to-left input)
2186
 
    if (column > _usedColumns)
2187
 
        column = _usedColumns;
2188
 
}
2189
 
 
2190
 
void TerminalDisplay::updateLineProperties()
2191
 
{
2192
 
    if (!_screenWindow)
2193
 
        return;
2194
 
 
2195
 
    _lineProperties = _screenWindow->getLineProperties();
2196
 
}
2197
 
 
2198
 
void TerminalDisplay::processMidButtonClick(QMouseEvent* ev)
2199
 
{
2200
 
    if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) {
2201
 
        const bool appendEnter = ev->modifiers() & Qt::ControlModifier;
2202
 
 
2203
 
        if ( _middleClickPasteMode == Enum::PasteFromX11Selection ) {
2204
 
            pasteFromX11Selection(appendEnter);
2205
 
        } else if ( _middleClickPasteMode == Enum::PasteFromClipboard ) {
2206
 
            pasteFromClipboard(appendEnter);
2207
 
        } else {
2208
 
            Q_ASSERT(false);
2209
 
        }
2210
 
    } else {
2211
 
        int charLine = 0;
2212
 
        int charColumn = 0;
2213
 
        getCharacterPosition(ev->pos(), charLine, charColumn);
2214
 
 
2215
 
        emit mouseSignal(1, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0);
2216
 
    }
2217
 
}
2218
 
 
2219
 
void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev)
2220
 
{
2221
 
    // Yes, successive middle click can trigger this event
2222
 
    if (ev->button() == Qt::MidButton) {
2223
 
        processMidButtonClick(ev);
2224
 
        return;
2225
 
    }
2226
 
 
2227
 
    if (ev->button() != Qt::LeftButton) return;
2228
 
    if (!_screenWindow) return;
2229
 
 
2230
 
    int charLine = 0;
2231
 
    int charColumn = 0;
2232
 
 
2233
 
    getCharacterPosition(ev->pos(), charLine, charColumn);
2234
 
 
2235
 
    QPoint pos(charColumn, charLine);
2236
 
 
2237
 
    // pass on double click as two clicks.
2238
 
    if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) {
2239
 
        // Send just _ONE_ click event, since the first click of the double click
2240
 
        // was already sent by the click handler
2241
 
        emit mouseSignal(0,
2242
 
                         pos.x() + 1,
2243
 
                         pos.y() + 1 + _scrollBar->value() - _scrollBar->maximum(),
2244
 
                         0);  // left button
2245
 
        return;
2246
 
    }
2247
 
 
2248
 
    _screenWindow->clearSelection();
2249
 
    QPoint bgnSel = pos;
2250
 
    QPoint endSel = pos;
2251
 
    int i = loc(bgnSel.x(), bgnSel.y());
2252
 
    _iPntSel = bgnSel;
2253
 
    _iPntSel.ry() += _scrollBar->value();
2254
 
 
2255
 
    _wordSelectionMode = true;
2256
 
 
2257
 
    // find word boundaries...
2258
 
    const QChar selClass = charClass(_image[i]);
2259
 
    {
2260
 
        // find the start of the word
2261
 
        int x = bgnSel.x();
2262
 
        while (((x > 0) || (bgnSel.y() > 0 && (_lineProperties[bgnSel.y() - 1] & LINE_WRAPPED)))
2263
 
                && charClass(_image[i - 1]) == selClass) {
2264
 
            i--;
2265
 
            if (x > 0)
2266
 
                x--;
2267
 
            else {
2268
 
                x = _usedColumns - 1;
2269
 
                bgnSel.ry()--;
2270
 
            }
2271
 
        }
2272
 
 
2273
 
        bgnSel.setX(x);
2274
 
        _screenWindow->setSelectionStart(bgnSel.x() , bgnSel.y() , false);
2275
 
 
2276
 
        // find the end of the word
2277
 
        i = loc(endSel.x(), endSel.y());
2278
 
        x = endSel.x();
2279
 
        while (((x < _usedColumns - 1) || (endSel.y() < _usedLines - 1 && (_lineProperties[endSel.y()] & LINE_WRAPPED)))
2280
 
                && charClass(_image[i + 1]) == selClass) {
2281
 
            i++;
2282
 
            if (x < _usedColumns - 1)
2283
 
                x++;
2284
 
            else {
2285
 
                x = 0;
2286
 
                endSel.ry()++;
2287
 
            }
2288
 
        }
2289
 
 
2290
 
        endSel.setX(x);
2291
 
 
2292
 
        // In word selection mode don't select @ (64) if at end of word.
2293
 
        if (((_image[i].rendition & RE_EXTENDED_CHAR) == 0) &&
2294
 
                (QChar(_image[i].character) == '@') &&
2295
 
                ((endSel.x() - bgnSel.x()) > 0)) {
2296
 
            endSel.setX(x - 1);
2297
 
        }
2298
 
 
2299
 
        _actSel = 2; // within selection
2300
 
 
2301
 
        _screenWindow->setSelectionEnd(endSel.x() , endSel.y());
2302
 
 
2303
 
        copyToX11Selection();
2304
 
    }
2305
 
 
2306
 
    _possibleTripleClick = true;
2307
 
 
2308
 
    QTimer::singleShot(QApplication::doubleClickInterval(), this,
2309
 
                       SLOT(tripleClickTimeout()));
2310
 
}
2311
 
 
2312
 
void TerminalDisplay::wheelEvent(QWheelEvent* ev)
2313
 
{
2314
 
    // Only vertical scrolling is supported
2315
 
    if (ev->orientation() != Qt::Vertical)
2316
 
        return;
2317
 
 
2318
 
    const int modifiers = ev->modifiers();
2319
 
    const int delta = ev->delta();
2320
 
 
2321
 
    // ctrl+<wheel> for zomming, like in konqueror and firefox
2322
 
    if (modifiers & Qt::ControlModifier) {
2323
 
        if (delta > 0) {
2324
 
            // wheel-up for increasing font size
2325
 
            increaseFontSize();
2326
 
        } else {
2327
 
            // wheel-down for decreasing font size
2328
 
            decreaseFontSize();
2329
 
        }
2330
 
 
2331
 
        return;
2332
 
    }
2333
 
 
2334
 
    // if the terminal program is not interested with mouse events:
2335
 
    //  * send the event to the scrollbar if the slider has room to move
2336
 
    //  * otherwise, send simulated up / down key presses to the terminal program
2337
 
    //    for the benefit of programs such as 'less'
2338
 
    if (_mouseMarks) {
2339
 
        const bool canScroll = _scrollBar->maximum() > 0;
2340
 
        if (canScroll) {
2341
 
            _scrollBar->event(ev);
2342
 
        } else {
2343
 
            // assume that each Up / Down key event will cause the terminal application
2344
 
            // to scroll by one line.
2345
 
            //
2346
 
            // to get a reasonable scrolling speed, scroll by one line for every 5 degrees
2347
 
            // of mouse wheel rotation.  Mouse wheels typically move in steps of 15 degrees,
2348
 
            // giving a scroll of 3 lines
2349
 
            const int keyCode = delta > 0 ? Qt::Key_Up : Qt::Key_Down;
2350
 
            QKeyEvent keyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier);
2351
 
 
2352
 
            // QWheelEvent::delta() gives rotation in eighths of a degree
2353
 
            const int degrees = delta / 8;
2354
 
            const int lines = abs(degrees) / 5;
2355
 
 
2356
 
            for (int i = 0; i < lines; i++)
2357
 
                emit keyPressedSignal(&keyEvent);
2358
 
        }
2359
 
    } else {
2360
 
        // terminal program wants notification of mouse activity
2361
 
 
2362
 
        int charLine;
2363
 
        int charColumn;
2364
 
        getCharacterPosition(ev->pos() , charLine , charColumn);
2365
 
 
2366
 
        emit mouseSignal(delta > 0 ? 4 : 5,
2367
 
                         charColumn + 1,
2368
 
                         charLine + 1 + _scrollBar->value() - _scrollBar->maximum() ,
2369
 
                         0);
2370
 
    }
2371
 
}
2372
 
 
2373
 
void TerminalDisplay::tripleClickTimeout()
2374
 
{
2375
 
    _possibleTripleClick = false;
2376
 
}
2377
 
 
2378
 
void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev)
2379
 
{
2380
 
    if (!_screenWindow) return;
2381
 
 
2382
 
    int charLine;
2383
 
    int charColumn;
2384
 
    getCharacterPosition(ev->pos(), charLine, charColumn);
2385
 
    _iPntSel = QPoint(charColumn, charLine);
2386
 
 
2387
 
    _screenWindow->clearSelection();
2388
 
 
2389
 
    _lineSelectionMode = true;
2390
 
    _wordSelectionMode = false;
2391
 
 
2392
 
    _actSel = 2; // within selection
2393
 
 
2394
 
    while (_iPntSel.y() > 0 && (_lineProperties[_iPntSel.y() - 1] & LINE_WRAPPED))
2395
 
        _iPntSel.ry()--;
2396
 
 
2397
 
    if (_tripleClickMode == Enum::SelectForwardsFromCursor) {
2398
 
        // find word boundary start
2399
 
        int i = loc(_iPntSel.x(), _iPntSel.y());
2400
 
        const QChar selClass = charClass(_image[i]);
2401
 
        int x = _iPntSel.x();
2402
 
 
2403
 
        while (((x > 0) ||
2404
 
                (_iPntSel.y() > 0 && (_lineProperties[_iPntSel.y() - 1] & LINE_WRAPPED))
2405
 
               )
2406
 
                && charClass(_image[i - 1]) == selClass) {
2407
 
            i--;
2408
 
            if (x > 0)
2409
 
                x--;
2410
 
            else {
2411
 
                x = _columns - 1;
2412
 
                _iPntSel.ry()--;
2413
 
            }
2414
 
        }
2415
 
 
2416
 
        _screenWindow->setSelectionStart(x , _iPntSel.y() , false);
2417
 
        _tripleSelBegin = QPoint(x, _iPntSel.y());
2418
 
    } else if (_tripleClickMode == Enum::SelectWholeLine) {
2419
 
        _screenWindow->setSelectionStart(0 , _iPntSel.y() , false);
2420
 
        _tripleSelBegin = QPoint(0, _iPntSel.y());
2421
 
    }
2422
 
 
2423
 
    while (_iPntSel.y() < _lines - 1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED))
2424
 
        _iPntSel.ry()++;
2425
 
 
2426
 
    _screenWindow->setSelectionEnd(_columns - 1 , _iPntSel.y());
2427
 
 
2428
 
    copyToX11Selection();
2429
 
 
2430
 
    _iPntSel.ry() += _scrollBar->value();
2431
 
}
2432
 
 
2433
 
bool TerminalDisplay::focusNextPrevChild(bool next)
2434
 
{
2435
 
    // for 'Tab', always disable focus switching among widgets
2436
 
    // for 'Shift+Tab', leave the decision to higher level
2437
 
    if (next)
2438
 
        return false;
2439
 
    else
2440
 
        return QWidget::focusNextPrevChild(next);
2441
 
}
2442
 
 
2443
 
QChar TerminalDisplay::charClass(const Character& ch) const
2444
 
{
2445
 
    if (ch.rendition & RE_EXTENDED_CHAR) {
2446
 
        ushort extendedCharLength = 0;
2447
 
        const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(ch.character, extendedCharLength);
2448
 
        if (chars && extendedCharLength > 0) {
2449
 
            const QString s = QString::fromUtf16(chars, extendedCharLength);
2450
 
            if (_wordCharacters.contains(s, Qt::CaseInsensitive))
2451
 
                return 'a';
2452
 
            bool allLetterOrNumber = true;
2453
 
            for (int i = 0; allLetterOrNumber && i < s.size(); ++i)
2454
 
                allLetterOrNumber = s.at(i).isLetterOrNumber();
2455
 
            return allLetterOrNumber ? 'a' : s.at(0);
2456
 
        }
2457
 
        return 0;
2458
 
    } else {
2459
 
        const QChar qch(ch.character);
2460
 
        if (qch.isSpace()) return ' ';
2461
 
 
2462
 
        if (qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive))
2463
 
            return 'a';
2464
 
 
2465
 
        return qch;
2466
 
    }
2467
 
}
2468
 
 
2469
 
void TerminalDisplay::setWordCharacters(const QString& wc)
2470
 
{
2471
 
    _wordCharacters = wc;
2472
 
}
2473
 
 
2474
 
// FIXME: the actual value of _mouseMarks is the opposite of its semantic.
2475
 
// When using programs not interested with mouse(shell, less), it is true.
2476
 
// When using programs interested with mouse(vim,mc), it is false.
2477
 
void TerminalDisplay::setUsesMouse(bool on)
2478
 
{
2479
 
    _mouseMarks = on;
2480
 
    setCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor);
2481
 
}
2482
 
bool TerminalDisplay::usesMouse() const
2483
 
{
2484
 
    return _mouseMarks;
2485
 
}
2486
 
 
2487
 
/* ------------------------------------------------------------------------- */
2488
 
/*                                                                           */
2489
 
/*                               Clipboard                                   */
2490
 
/*                                                                           */
2491
 
/* ------------------------------------------------------------------------- */
2492
 
 
2493
 
void TerminalDisplay::doPaste(QString text, bool appendReturn)
2494
 
{
2495
 
    if (!_screenWindow)
2496
 
        return;
2497
 
 
2498
 
    if (appendReturn)
2499
 
        text.append("\r");
2500
 
 
2501
 
    if (!text.isEmpty()) {
2502
 
        text.replace('\n', '\r');
2503
 
        // perform paste by simulating keypress events
2504
 
        QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text);
2505
 
        emit keyPressedSignal(&e);
2506
 
    }
2507
 
}
2508
 
 
2509
 
void TerminalDisplay::setAutoCopySelectedText(bool enabled)
2510
 
{
2511
 
    _autoCopySelectedText = enabled;
2512
 
}
2513
 
 
2514
 
void TerminalDisplay::setMiddleClickPasteMode(Enum::MiddleClickPasteModeEnum mode)
2515
 
{
2516
 
    _middleClickPasteMode = mode;
2517
 
}
2518
 
 
2519
 
void TerminalDisplay::copyToX11Selection()
2520
 
{
2521
 
    if (!_screenWindow)
2522
 
        return;
2523
 
 
2524
 
    QString text = _screenWindow->selectedText(_preserveLineBreaks);
2525
 
    if (text.isEmpty())
2526
 
        return;
2527
 
 
2528
 
    QApplication::clipboard()->setText(text, QClipboard::Selection);
2529
 
 
2530
 
    if (_autoCopySelectedText)
2531
 
        QApplication::clipboard()->setText(text, QClipboard::Clipboard);
2532
 
}
2533
 
 
2534
 
void TerminalDisplay::copyToClipboard()
2535
 
{
2536
 
    if (!_screenWindow)
2537
 
        return;
2538
 
 
2539
 
    QString text = _screenWindow->selectedText(_preserveLineBreaks);
2540
 
    if (text.isEmpty())
2541
 
        return;
2542
 
 
2543
 
    QApplication::clipboard()->setText(text, QClipboard::Clipboard);
2544
 
}
2545
 
 
2546
 
void TerminalDisplay::pasteFromClipboard(bool appendEnter)
2547
 
{
2548
 
    QString text = QApplication::clipboard()->text(QClipboard::Clipboard);
2549
 
    doPaste(text, appendEnter);
2550
 
}
2551
 
 
2552
 
void TerminalDisplay::pasteFromX11Selection(bool appendEnter)
2553
 
{
2554
 
    QString text = QApplication::clipboard()->text(QClipboard::Selection);
2555
 
    doPaste(text, appendEnter);
2556
 
}
2557
 
 
2558
 
/* ------------------------------------------------------------------------- */
2559
 
/*                                                                           */
2560
 
/*                                Input Method                               */
2561
 
/*                                                                           */
2562
 
/* ------------------------------------------------------------------------- */
2563
 
 
2564
 
void TerminalDisplay::inputMethodEvent(QInputMethodEvent* event)
2565
 
{
2566
 
    if (!event->commitString().isEmpty()) {
2567
 
        QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier, event->commitString());
2568
 
        emit keyPressedSignal(&keyEvent);
2569
 
    }
2570
 
 
2571
 
    _inputMethodData.preeditString = event->preeditString();
2572
 
    update(preeditRect() | _inputMethodData.previousPreeditRect);
2573
 
 
2574
 
    event->accept();
2575
 
}
2576
 
 
2577
 
QVariant TerminalDisplay::inputMethodQuery(Qt::InputMethodQuery query) const
2578
 
{
2579
 
    const QPoint cursorPos = cursorPosition();
2580
 
    switch (query) {
2581
 
    case Qt::ImMicroFocus:
2582
 
        return imageToWidget(QRect(cursorPos.x(), cursorPos.y(), 1, 1));
2583
 
        break;
2584
 
    case Qt::ImFont:
2585
 
        return font();
2586
 
        break;
2587
 
    case Qt::ImCursorPosition:
2588
 
        // return the cursor position within the current line
2589
 
        return cursorPos.x();
2590
 
        break;
2591
 
    case Qt::ImSurroundingText: {
2592
 
        // return the text from the current line
2593
 
        QString lineText;
2594
 
        QTextStream stream(&lineText);
2595
 
        PlainTextDecoder decoder;
2596
 
        decoder.begin(&stream);
2597
 
        decoder.decodeLine(&_image[loc(0, cursorPos.y())], _usedColumns, _lineProperties[cursorPos.y()]);
2598
 
        decoder.end();
2599
 
        return lineText;
2600
 
    }
2601
 
    break;
2602
 
    case Qt::ImCurrentSelection:
2603
 
        return QString();
2604
 
        break;
2605
 
    default:
2606
 
        break;
2607
 
    }
2608
 
 
2609
 
    return QVariant();
2610
 
}
2611
 
 
2612
 
QRect TerminalDisplay::preeditRect() const
2613
 
{
2614
 
    const int preeditLength = string_width(_inputMethodData.preeditString);
2615
 
 
2616
 
    if (preeditLength == 0)
2617
 
        return QRect();
2618
 
 
2619
 
    return QRect(_leftMargin + _fontWidth * cursorPosition().x(),
2620
 
                 _topMargin + _fontHeight * cursorPosition().y(),
2621
 
                 _fontWidth * preeditLength,
2622
 
                 _fontHeight);
2623
 
}
2624
 
 
2625
 
void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect)
2626
 
{
2627
 
    if (_inputMethodData.preeditString.isEmpty())
2628
 
        return;
2629
 
 
2630
 
    const QPoint cursorPos = cursorPosition();
2631
 
 
2632
 
    bool invertColors = false;
2633
 
    const QColor background = _colorTable[DEFAULT_BACK_COLOR].color;
2634
 
    const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color;
2635
 
    const Character* style = &_image[loc(cursorPos.x(), cursorPos.y())];
2636
 
 
2637
 
    drawBackground(painter, rect, background, true);
2638
 
    drawCursor(painter, rect, foreground, background, invertColors);
2639
 
    drawCharacters(painter, rect, _inputMethodData.preeditString, style, invertColors);
2640
 
 
2641
 
    _inputMethodData.previousPreeditRect = rect;
2642
 
}
2643
 
 
2644
 
/* ------------------------------------------------------------------------- */
2645
 
/*                                                                           */
2646
 
/*                                Keyboard                                   */
2647
 
/*                                                                           */
2648
 
/* ------------------------------------------------------------------------- */
2649
 
 
2650
 
void TerminalDisplay::setFlowControlWarningEnabled(bool enable)
2651
 
{
2652
 
    _flowControlWarningEnabled = enable;
2653
 
 
2654
 
    // if the dialog is currently visible and the flow control warning has
2655
 
    // been disabled then hide the dialog
2656
 
    if (!enable)
2657
 
        outputSuspended(false);
2658
 
}
2659
 
 
2660
 
void TerminalDisplay::outputSuspended(bool suspended)
2661
 
{
2662
 
    //create the label when this function is first called
2663
 
    if (!_outputSuspendedLabel) {
2664
 
        //This label includes a link to an English language website
2665
 
        //describing the 'flow control' (Xon/Xoff) feature found in almost
2666
 
        //all terminal emulators.
2667
 
        //If there isn't a suitable article available in the target language the link
2668
 
        //can simply be removed.
2669
 
        _outputSuspendedLabel = new QLabel(i18n("<qt>Output has been "
2670
 
                                                "<a href=\"http://en.wikipedia.org/wiki/Flow_control\">suspended</a>"
2671
 
                                                " by pressing Ctrl+S."
2672
 
                                                "  Press <b>Ctrl+Q</b> to resume.</qt>"),
2673
 
                                           this);
2674
 
 
2675
 
        QPalette palette(_outputSuspendedLabel->palette());
2676
 
        KColorScheme::adjustBackground(palette, KColorScheme::NeutralBackground);
2677
 
        _outputSuspendedLabel->setPalette(palette);
2678
 
        _outputSuspendedLabel->setAutoFillBackground(true);
2679
 
        _outputSuspendedLabel->setBackgroundRole(QPalette::Base);
2680
 
        _outputSuspendedLabel->setFont(KGlobalSettings::generalFont());
2681
 
        _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5);
2682
 
 
2683
 
        //enable activation of "Xon/Xoff" link in label
2684
 
        _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse |
2685
 
                Qt::LinksAccessibleByKeyboard);
2686
 
        _outputSuspendedLabel->setOpenExternalLinks(true);
2687
 
        _outputSuspendedLabel->setVisible(false);
2688
 
 
2689
 
        _gridLayout->addWidget(_outputSuspendedLabel);
2690
 
        _gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
2691
 
                                             QSizePolicy::Expanding),
2692
 
                             1, 0);
2693
 
    }
2694
 
 
2695
 
    _outputSuspendedLabel->setVisible(suspended);
2696
 
}
2697
 
 
2698
 
void TerminalDisplay::scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount)
2699
 
{
2700
 
    _screenWindow->scrollBy(mode, amount);
2701
 
    _screenWindow->setTrackOutput(_screenWindow->atEndOfOutput());
2702
 
    updateLineProperties();
2703
 
    updateImage();
2704
 
}
2705
 
 
2706
 
void TerminalDisplay::keyPressEvent(QKeyEvent* event)
2707
 
{
2708
 
    _screenWindow->screen()->setCurrentTerminalDisplay(this);
2709
 
 
2710
 
    _actSel = 0; // Key stroke implies a screen update, so TerminalDisplay won't
2711
 
    // know where the current selection is.
2712
 
 
2713
 
    if (_allowBlinkingCursor) {
2714
 
        _blinkCursorTimer->start();
2715
 
        if (_cursorBlinking) {
2716
 
            // if cursor is blinking(hidden), blink it again to show it
2717
 
            blinkCursorEvent();
2718
 
        }
2719
 
        Q_ASSERT( _cursorBlinking == false );
2720
 
    }
2721
 
 
2722
 
    emit keyPressedSignal(event);
2723
 
 
2724
 
#if QT_VERSION >= 0x040800 // added in Qt 4.8.0
2725
 
#ifndef QT_NO_ACCESSIBILITY
2726
 
    QAccessible::updateAccessibility(this, 0, QAccessible::TextCaretMoved);
2727
 
    QAccessible::updateAccessibility(this, 0, QAccessible::TextInserted);
2728
 
#endif
2729
 
#endif
2730
 
 
2731
 
    event->accept();
2732
 
}
2733
 
 
2734
 
bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent* keyEvent)
2735
 
{
2736
 
    const int modifiers = keyEvent->modifiers();
2737
 
 
2738
 
    //  When a possible shortcut combination is pressed,
2739
 
    //  emit the overrideShortcutCheck() signal to allow the host
2740
 
    //  to decide whether the terminal should override it or not.
2741
 
    if (modifiers != Qt::NoModifier) {
2742
 
        int modifierCount = 0;
2743
 
        unsigned int currentModifier = Qt::ShiftModifier;
2744
 
 
2745
 
        while (currentModifier <= Qt::KeypadModifier) {
2746
 
            if (modifiers & currentModifier)
2747
 
                modifierCount++;
2748
 
            currentModifier <<= 1;
2749
 
        }
2750
 
        if (modifierCount < 2) {
2751
 
            bool override = false;
2752
 
            emit overrideShortcutCheck(keyEvent, override);
2753
 
            if (override) {
2754
 
                keyEvent->accept();
2755
 
                return true;
2756
 
            }
2757
 
        }
2758
 
    }
2759
 
 
2760
 
    // Override any of the following shortcuts because
2761
 
    // they are needed by the terminal
2762
 
    int keyCode = keyEvent->key() | modifiers;
2763
 
    switch (keyCode) {
2764
 
        // list is taken from the QLineEdit::event() code
2765
 
    case Qt::Key_Tab:
2766
 
    case Qt::Key_Delete:
2767
 
    case Qt::Key_Home:
2768
 
    case Qt::Key_End:
2769
 
    case Qt::Key_Backspace:
2770
 
    case Qt::Key_Left:
2771
 
    case Qt::Key_Right:
2772
 
    case Qt::Key_Slash:
2773
 
    case Qt::Key_Period:
2774
 
    case Qt::Key_Space:
2775
 
        keyEvent->accept();
2776
 
        return true;
2777
 
    }
2778
 
    return false;
2779
 
}
2780
 
 
2781
 
bool TerminalDisplay::event(QEvent* event)
2782
 
{
2783
 
    bool eventHandled = false;
2784
 
    switch (event->type()) {
2785
 
    case QEvent::ShortcutOverride:
2786
 
        eventHandled = handleShortcutOverrideEvent((QKeyEvent*)event);
2787
 
        break;
2788
 
    case QEvent::PaletteChange:
2789
 
    case QEvent::ApplicationPaletteChange:
2790
 
        _scrollBar->setPalette(QApplication::palette());
2791
 
        break;
2792
 
    default:
2793
 
        break;
2794
 
    }
2795
 
    return eventHandled ? true : QWidget::event(event);
2796
 
}
2797
 
 
2798
 
void TerminalDisplay::contextMenuEvent(QContextMenuEvent* event)
2799
 
{
2800
 
    // the logic for the mouse case is within MousePressEvent()
2801
 
    if (event->reason() != QContextMenuEvent::Mouse) {
2802
 
        emit configureRequest(mapFromGlobal(QCursor::pos()));
2803
 
    }
2804
 
}
2805
 
 
2806
 
/* --------------------------------------------------------------------- */
2807
 
/*                                                                       */
2808
 
/*                                  Bell                                 */
2809
 
/*                                                                       */
2810
 
/* --------------------------------------------------------------------- */
2811
 
 
2812
 
void TerminalDisplay::setBellMode(int mode)
2813
 
{
2814
 
    _bellMode = mode;
2815
 
}
2816
 
 
2817
 
int TerminalDisplay::bellMode() const
2818
 
{
2819
 
    return _bellMode;
2820
 
}
2821
 
 
2822
 
void TerminalDisplay::unmaskBell()
2823
 
{
2824
 
    _bellMasked = false;
2825
 
}
2826
 
 
2827
 
void TerminalDisplay::bell(const QString& message)
2828
 
{
2829
 
    if (_bellMasked)
2830
 
        return;
2831
 
 
2832
 
    switch (_bellMode) {
2833
 
    case Enum::SystemBeepBell:
2834
 
        KNotification::beep();
2835
 
        break;
2836
 
    case Enum::NotifyBell:
2837
 
        KNotification::event(hasFocus() ? "BellVisible" : "BellInvisible",
2838
 
                message, QPixmap(), this);
2839
 
        break;
2840
 
    case Enum::VisualBell:
2841
 
        visualBell();
2842
 
        break;
2843
 
    default:
2844
 
        break;
2845
 
    }
2846
 
 
2847
 
    // limit the rate at which bells can occur.
2848
 
    // ...mainly for sound effects where rapid bells in sequence
2849
 
    // produce a horrible noise.
2850
 
    _bellMasked = true;
2851
 
    QTimer::singleShot(500, this, SLOT(unmaskBell()));
2852
 
}
2853
 
 
2854
 
void TerminalDisplay::visualBell()
2855
 
{
2856
 
    swapFGBGColors();
2857
 
    QTimer::singleShot(200, this, SLOT(swapFGBGColors()));
2858
 
}
2859
 
 
2860
 
void TerminalDisplay::swapFGBGColors()
2861
 
{
2862
 
    // swap the default foreground & backround color
2863
 
    ColorEntry color = _colorTable[DEFAULT_BACK_COLOR];
2864
 
    _colorTable[DEFAULT_BACK_COLOR] = _colorTable[DEFAULT_FORE_COLOR];
2865
 
    _colorTable[DEFAULT_FORE_COLOR] = color;
2866
 
 
2867
 
    update();
2868
 
}
2869
 
 
2870
 
/* --------------------------------------------------------------------- */
2871
 
/*                                                                       */
2872
 
/* Drag & Drop                                                           */
2873
 
/*                                                                       */
2874
 
/* --------------------------------------------------------------------- */
2875
 
 
2876
 
void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event)
2877
 
{
2878
 
    // text/plain alone is enough for KDE-apps
2879
 
    // text/uri-list is for supporting some non-KDE apps, such as thunar
2880
 
    //   and pcmanfm
2881
 
    // That also applies in dropEvent()
2882
 
    if (event->mimeData()->hasFormat("text/plain") ||
2883
 
            event->mimeData()->hasFormat("text/uri-list")) {
2884
 
        event->acceptProposedAction();
2885
 
    }
2886
 
}
2887
 
 
2888
 
void TerminalDisplay::dropEvent(QDropEvent* event)
2889
 
{
2890
 
    KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
2891
 
 
2892
 
    QString dropText;
2893
 
    if (!urls.isEmpty()) {
2894
 
        for (int i = 0 ; i < urls.count() ; i++) {
2895
 
            KUrl url = KIO::NetAccess::mostLocalUrl(urls[i] , 0);
2896
 
            QString urlText;
2897
 
 
2898
 
            if (url.isLocalFile())
2899
 
                urlText = url.path();
2900
 
            else
2901
 
                urlText = url.url();
2902
 
 
2903
 
            // in future it may be useful to be able to insert file names with drag-and-drop
2904
 
            // without quoting them (this only affects paths with spaces in)
2905
 
            urlText = KShell::quoteArg(urlText);
2906
 
 
2907
 
            dropText += urlText;
2908
 
 
2909
 
            // Each filename(including the last) should be followed by one space.
2910
 
            dropText += ' ';
2911
 
        }
2912
 
 
2913
 
        // If our target is local we will open a popup - otherwise the fallback kicks
2914
 
        // in and the URLs will simply be pasted as text.
2915
 
        if (_sessionController && _sessionController->url().isLocalFile()) {
2916
 
            // A standard popup with Copy, Move and Link as options -
2917
 
            // plus an additional Paste option.
2918
 
 
2919
 
            QAction* pasteAction = new QAction(i18n("&Paste Location"), this);
2920
 
            pasteAction->setData(dropText);
2921
 
            connect(pasteAction, SIGNAL(triggered()), this, SLOT(dropMenuPasteActionTriggered()));
2922
 
 
2923
 
            QList<QAction*> additionalActions;
2924
 
            additionalActions.append(pasteAction);
2925
 
 
2926
 
            if (urls.count() == 1) {
2927
 
                const KUrl url = KIO::NetAccess::mostLocalUrl(urls[0] , 0);
2928
 
 
2929
 
                if (url.isLocalFile()) {
2930
 
                    const QFileInfo fileInfo(url.path());
2931
 
 
2932
 
                    if (fileInfo.isDir()) {
2933
 
                        QAction* cdAction = new QAction(i18n("Change &Directory To"), this);
2934
 
                        dropText = QLatin1String(" cd ") + dropText + QChar('\n');
2935
 
                        cdAction->setData(dropText);
2936
 
                        connect(cdAction, SIGNAL(triggered()), this, SLOT(dropMenuCdActionTriggered()));
2937
 
                        additionalActions.append(cdAction);
2938
 
                    }
2939
 
                }
2940
 
            }
2941
 
 
2942
 
            KUrl target(_sessionController->currentDir());
2943
 
 
2944
 
            KonqOperations::doDrop(KFileItem(), target, event, this, additionalActions);
2945
 
 
2946
 
            return;
2947
 
        }
2948
 
 
2949
 
    } else {
2950
 
        dropText = event->mimeData()->text();
2951
 
    }
2952
 
 
2953
 
    if (event->mimeData()->hasFormat("text/plain") ||
2954
 
            event->mimeData()->hasFormat("text/uri-list")) {
2955
 
        emit sendStringToEmu(dropText.toLocal8Bit());
2956
 
    }
2957
 
}
2958
 
 
2959
 
void TerminalDisplay::dropMenuPasteActionTriggered()
2960
 
{
2961
 
    if (sender()) {
2962
 
        const QAction* action = qobject_cast<const QAction*>(sender());
2963
 
        if (action) {
2964
 
            emit sendStringToEmu(action->data().toString().toLocal8Bit());
2965
 
        }
2966
 
    }
2967
 
}
2968
 
 
2969
 
void TerminalDisplay::dropMenuCdActionTriggered()
2970
 
{
2971
 
    if (sender()) {
2972
 
        const QAction* action = qobject_cast<const QAction*>(sender());
2973
 
        if (action) {
2974
 
            emit sendStringToEmu(action->data().toString().toLocal8Bit());
2975
 
        }
2976
 
    }
2977
 
}
2978
 
 
2979
 
void TerminalDisplay::doDrag()
2980
 
{
2981
 
    _dragInfo.state = diDragging;
2982
 
    _dragInfo.dragObject = new QDrag(this);
2983
 
    QMimeData* mimeData = new QMimeData;
2984
 
    mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection));
2985
 
    _dragInfo.dragObject->setMimeData(mimeData);
2986
 
    _dragInfo.dragObject->exec(Qt::CopyAction);
2987
 
}
2988
 
 
2989
 
void TerminalDisplay::setSessionController(SessionController* controller)
2990
 
{
2991
 
    _sessionController = controller;
2992
 
}
2993
 
 
2994
 
AutoScrollHandler::AutoScrollHandler(QWidget* parent)
2995
 
    : QObject(parent)
2996
 
    , _timerId(0)
2997
 
{
2998
 
    parent->installEventFilter(this);
2999
 
}
3000
 
void AutoScrollHandler::timerEvent(QTimerEvent* event)
3001
 
{
3002
 
    if (event->timerId() != _timerId)
3003
 
        return;
3004
 
 
3005
 
    QMouseEvent mouseEvent(QEvent::MouseMove,
3006
 
                           widget()->mapFromGlobal(QCursor::pos()),
3007
 
                           Qt::NoButton,
3008
 
                           Qt::LeftButton,
3009
 
                           Qt::NoModifier);
3010
 
 
3011
 
    QApplication::sendEvent(widget(), &mouseEvent);
3012
 
}
3013
 
bool AutoScrollHandler::eventFilter(QObject* watched, QEvent* event)
3014
 
{
3015
 
    Q_ASSERT(watched == parent());
3016
 
    Q_UNUSED(watched);
3017
 
 
3018
 
    QMouseEvent* mouseEvent = (QMouseEvent*)event;
3019
 
    switch (event->type()) {
3020
 
    case QEvent::MouseMove: {
3021
 
        bool mouseInWidget = widget()->rect().contains(mouseEvent->pos());
3022
 
        if (mouseInWidget) {
3023
 
            if (_timerId)
3024
 
                killTimer(_timerId);
3025
 
 
3026
 
            _timerId = 0;
3027
 
        } else {
3028
 
            if (!_timerId && (mouseEvent->buttons() & Qt::LeftButton))
3029
 
                _timerId = startTimer(100);
3030
 
        }
3031
 
 
3032
 
        break;
3033
 
    }
3034
 
    case QEvent::MouseButtonRelease: {
3035
 
        if (_timerId && (mouseEvent->buttons() & ~Qt::LeftButton)) {
3036
 
            killTimer(_timerId);
3037
 
            _timerId = 0;
3038
 
        }
3039
 
        break;
3040
 
    }
3041
 
    default:
3042
 
        break;
3043
 
    };
3044
 
 
3045
 
    return false;
3046
 
}
3047
 
 
3048
 
#include "TerminalDisplay.moc"