~verzegnassi-stefano/+junk/ubuntu-terminal-app-uitk13

« back to all changes in this revision

Viewing changes to src/plugin/qmltermwidget/qtermwidget/lib/TerminalDisplay.cpp

  • Committer: Filippo Scognamiglio
  • Date: 2014-10-25 04:42:31 UTC
  • Revision ID: flscogna@gmail.com-20141025044231-javjhusbqa171127
Initial reboot commit.

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