TerminalDisplay.cpp 86 KB
Newer Older
1
/*
2
    This file is part of Konsole, a terminal emulator for KDE.
3
    
4
    Copyright (C) 2006-7 by Robert Knight <robertknight@gmail.com>
5
    Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
6
    
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301  USA.
*/
22

23
24
25
// Own
#include "TerminalDisplay.h"

Robert Knight's avatar
Robert Knight committed
26
// Qt
Dirk Mueller's avatar
Dirk Mueller committed
27
28
29
30
31
#include <QtGui/QApplication>
#include <QtGui/QBoxLayout>
#include <QtGui/QClipboard>
#include <QtGui/QKeyEvent>
#include <QtCore/QEvent>
32
#include <QtCore/QTime>
Dirk Mueller's avatar
Dirk Mueller committed
33
34
35
36
37
38
#include <QtCore/QFile>
#include <QtGui/QGridLayout>
#include <QtGui/QLabel>
#include <QtGui/QLayout>
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
39
#include <QtGui/QScrollBar>
Dirk Mueller's avatar
Dirk Mueller committed
40
41
42
#include <QtGui/QStyle>
#include <QtCore/QTimer>
#include <QtGui/QToolTip>
43

Robert Knight's avatar
Robert Knight committed
44
// KDE
45
#include <kshell.h>
Robert Knight's avatar
   
Robert Knight committed
46
#include <KColorScheme>
Robert Knight's avatar
Robert Knight committed
47
#include <KCursor>
48
#include <kdebug.h>
Robert Knight's avatar
Robert Knight committed
49
#include <KLocale>
50
#include <KMenu>
Robert Knight's avatar
Robert Knight committed
51
52
53
54
55
56
#include <KNotification>
#include <KGlobalSettings>
#include <KShortcut>
#include <KIO/NetAccess>

// Konsole
Dirk Mueller's avatar
Dirk Mueller committed
57
#include <config-apps.h>
58
#include "Filter.h"
Robert Knight's avatar
Robert Knight committed
59
#include "konsole_wcwidth.h"
60
#include "ScreenWindow.h"
61
#include "TerminalCharacterDecoder.h"
62

63
64
using namespace Konsole;

65
#ifndef loc
66
#define loc(X,Y) ((Y)*_columns+(X))
67
68
69
#endif

#define yMouseScroll 1
Waldo Bastian's avatar
Waldo Bastian committed
70
71
72
73

#define REPCHAR   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
                  "abcdefgjijklmnopqrstuvwxyz" \
                  "0123456789./+@"
74

75
76
// scroll increment used when dragging selection at top/bottom of window.

77
// static
78
bool TerminalDisplay::_antialiasText = true;
79
bool TerminalDisplay::HAVE_TRANSPARENCY = false;
80

81
82
83
84
// we use this to force QPainter to display text in LTR mode
// more information can be found in: http://unicode.org/reports/tr9/ 
const QChar LTR_OVERRIDE_CHAR( 0x202D );

85
86
87
88
89
90
91
92
93
94
95
96
97
98
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                                Colors                                     */
/*                                                                           */
/* ------------------------------------------------------------------------- */

/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb)

   Code        0       1       2       3       4       5       6       7
   ----------- ------- ------- ------- ------- ------- ------- ------- -------
   ANSI  (bgr) Black   Red     Green   Yellow  Blue    Magenta Cyan    White
   IBMPC (rgb) Black   Blue    Green   Cyan    Red     Magenta Yellow  White
*/

99
ScreenWindow* TerminalDisplay::screenWindow() const
100
101
102
{
    return _screenWindow;
}
103
void TerminalDisplay::setScreenWindow(ScreenWindow* window)
104
{
105
106
107
108
109
110
    // disconnect existing screen window if any
    if ( _screenWindow )
    {
        disconnect( _screenWindow , 0 , this , 0 );
    }

111
    _screenWindow = window;
112

113
114
    if ( window )
    {
115
#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?"
116
117
        connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) );
        connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) );
118
		window->setWindowLines(_lines);
119
    }
120
121
}

122
const ColorEntry* TerminalDisplay::colorTable() const
123
{
124
  return _colorTable;
125
}
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
void TerminalDisplay::setBackgroundColor(const QColor& color)
{
	_colorTable[DEFAULT_BACK_COLOR].color = color;
	QPalette p = palette();
  	p.setColor( backgroundRole(), color ); 
  	setPalette( p );

  	// Avoid propagating the palette change to the scroll bar 
  	_scrollBar->setPalette( QApplication::palette() );  

	update();
}
void TerminalDisplay::setForegroundColor(const QColor& color)
{
	_colorTable[DEFAULT_FORE_COLOR].color = color;
141

142
143
	update();
}
144
void TerminalDisplay::setColorTable(const ColorEntry table[])
145
{
Robert Knight's avatar
   
Robert Knight committed
146
147
  for (int i = 0; i < TABLE_COLORS; i++)
      _colorTable[i] = table[i];
148

149
  setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color);
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
}

/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                                   Font                                    */
/*                                                                           */
/* ------------------------------------------------------------------------- */

/*
   The VT100 has 32 special graphical characters. The usual vt100 extended
   xterm fonts have these at 0x00..0x1f.

   QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals
   come in here as proper unicode characters.

   We treat non-iso10646 fonts as VT100 extended and do the requiered mapping
   from unicode to 0x00..0x1f. The remaining translation is then left to the
   QCodec.
*/

170
static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);}
171
172
173
174
175
static inline bool isLineCharString(const QString& string)
{
		return (string.length() > 0) && (isLineChar(string.at(0).unicode()));
}
						
176

177
178
// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i.

179
unsigned short Konsole::vt100_graphics[32] =
180
181
182
183
184
185
186
{ // 0/8     1/9    2/10    3/11    4/12    5/13    6/14    7/15
  0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
  0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
  0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534,
  0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7
};

187
void TerminalDisplay::fontChange(const QFont&)
188
189
{
  QFontMetrics fm(font());
190
  _fontHeight = fm.height() + _lineSpacing;
191

192
  // waba TerminalDisplay 1.123:
193
194
  // "Base character width on widest ASCII character. This prevents too wide
  //  characters in the presence of double wide (e.g. Japanese) characters."
Waldo Bastian's avatar
Waldo Bastian committed
195
  // Get the width from representative normal width characters
196
  _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR));
Waldo Bastian's avatar
Waldo Bastian committed
197

198
  _fixedFont = true;
199

200
  int fw = fm.width(REPCHAR[0]);
201
202
203
204
  for(unsigned int i=1; i< strlen(REPCHAR); i++)
  {
    if (fw != fm.width(REPCHAR[i]))
    {
205
      _fixedFont = false;
Waldo Bastian's avatar
Waldo Bastian committed
206
      break;
207
    }
208
  }
Stephan Kulow's avatar
Stephan Kulow committed
209

210
  if (_fontWidth < 1)
211
    _fontWidth=1;
212

213
  _fontAscent = fm.ascent();
214

215
  emit changedFontMetricSignal( _fontHeight, _fontWidth );
216
217
218
219
  propagateSize();
  update();
}

220
void TerminalDisplay::setVTFont(const QFont& f)
221
{
222
  QFont font = f;
Robert Knight's avatar
Robert Knight committed
223
224
225

  QFontMetrics metrics(font);

226
227
228
229
230
  if ( !QFontInfo(font).fixedPitch() )
  {
	  kWarning() << "Using an unsupported variable-width font in the terminal.  This may produce display errors.";
  }

Robert Knight's avatar
Robert Knight committed
231
232
  if ( metrics.height() < height() && metrics.maxWidth() < width() )
  {
233
234
235
    // hint that text should be drawn without anti-aliasing.  
    // depending on the user's font configuration, this may not be respected
    if (!_antialiasText)
Robert Knight's avatar
Robert Knight committed
236
        font.setStyleStrategy( QFont::NoAntialias );
Robert Knight's avatar
   
Robert Knight committed
237
238
239
240
241
242
 
    // experimental optimization.  Konsole assumes that the terminal is using a 
    // mono-spaced font, in which case kerning information should have an effect.
    // Disabling kerning saves some computation when rendering text. 
    font.setKerning(false);

243
    QWidget::setFont(font);
Robert Knight's avatar
Robert Knight committed
244
245
    fontChange(font);
  }
246
247
}

248
void TerminalDisplay::setFont(const QFont &)
249
250
251
252
253
254
255
256
257
258
{
  // ignore font change request if not coming from konsole itself
}

/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                         Constructor / Destructor                          */
/*                                                                           */
/* ------------------------------------------------------------------------- */

259
TerminalDisplay::TerminalDisplay(QWidget *parent)
260
:QWidget(parent)
261
,_screenWindow(0)
262
263
264
265
266
267
268
269
270
271
272
273
,_allowBell(true)
,_gridLayout(0)
,_fontHeight(1)
,_fontWidth(1)
,_fontAscent(1)
,_lines(1)
,_columns(1)
,_usedLines(1)
,_usedColumns(1)
,_contentHeight(1)
,_contentWidth(1)
,_image(0)
274
,_randomSeed(0)
275
276
277
278
279
280
281
,_resizing(false)
,_terminalSizeHint(false)
,_terminalSizeStartup(true)
,_bidiEnabled(false)
,_actSel(0)
,_wordSelectionMode(false)
,_lineSelectionMode(false)
282
,_preserveLineBreaks(false)
283
,_columnSelectionMode(false)
284
,_scrollbarLocation(NoScrollBar)
285
,_wordCharacters(":@-./_~")
Robert Knight's avatar
   
Robert Knight committed
286
,_bellMode(SystemBeepBell)
287
288
289
290
,_blinking(false)
,_cursorBlinking(false)
,_hasBlinkingCursor(false)
,_ctrlDrag(false)
291
,_tripleClickMode(SelectWholeLine)
292
293
294
295
,_isFixedSize(false)
,_possibleTripleClick(false)
,_resizeWidget(0)
,_resizeTimer(0)
Robert Knight's avatar
   
Robert Knight committed
296
,_flowControlWarningEnabled(false)
297
298
299
300
,_outputSuspendedLabel(0)
,_lineSpacing(0)
,_colorsInverted(false)
,_blendColor(qRgba(0,0,0,0xff))
301
,_filterChain(new TerminalImageFilterChain())
302
,_cursorShape(BlockCursor)
303
{
Robert Knight's avatar
   
Robert Knight committed
304
305
306
307
  // terminal applications are not designed with Right-To-Left in mind,
  // so the layout is forced to Left-To-Right
  setLayoutDirection(Qt::LeftToRight);

308
  // The offsets are not yet calculated.
309
  // Do not calculate these too often to be more smoothly when resizing
310
  // konsole in opaque mode.
Robert Knight's avatar
   
Robert Knight committed
311
312
  _topMargin = DEFAULT_TOP_MARGIN;
  _leftMargin = DEFAULT_LEFT_MARGIN;
313

314
315
  // create scroll bar for scrolling output up and down
  // set the scroll bar's slider to occupy the whole area of the scroll bar initially
316
  _scrollBar = new QScrollBar(this);
317
  setScroll(0,0); 
318
  _scrollBar->setCursor( Qt::ArrowCursor );
Robert Knight's avatar
   
Robert Knight committed
319
320
  connect(_scrollBar, SIGNAL(valueChanged(int)), this, 
  					  SLOT(scrollBarPositionChanged(int)));
321

Robert Knight's avatar
   
Robert Knight committed
322
  // setup timers for blinking cursor and text
323
324
325
326
  _blinkTimer   = new QTimer(this);
  connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent()));
  _blinkCursorTimer   = new QTimer(this);
  connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent()));
327

Robert Knight's avatar
   
Robert Knight committed
328
329
  KCursor::setAutoHideCursor( this, true );
  
Robert Knight's avatar
   
Robert Knight committed
330
  setUsesMouse(true);
331
  setColorTable(base_color_table); 
332
333
  setMouseTracking(true);

Robert Knight's avatar
   
Robert Knight committed
334
  // Enable drag and drop 
335
  setAcceptDrops(true); // attempt
336
  dragInfo.state = diNone;
337

338
  setFocusPolicy( Qt::WheelFocus );
339
340

  // enable input method support
341
  setAttribute(Qt::WA_InputMethodEnabled, true);
Stephan Kulow's avatar
Stephan Kulow committed
342

Robert Knight's avatar
Robert Knight committed
343
  // this is an important optimization, it tells Qt
344
345
  // that TerminalDisplay will handle repainting its entire area.
  setAttribute(Qt::WA_OpaquePaintEvent);
346

347
348
  _gridLayout = new QGridLayout(this);
  _gridLayout->setMargin(0);
349

350
  setLayout( _gridLayout ); 
351
352
353

  //set up a warning message when the user presses Ctrl+S to avoid confusion
  connect( this,SIGNAL(flowControlKeyPressed(bool)),this,SLOT(outputSuspended(bool)) );
354
355

  new AutoScrollHandler(this);
356
357
}

358
TerminalDisplay::~TerminalDisplay()
359
360
{
  qApp->removeEventFilter( this );
361
  
362
  delete[] _image;
363

364
365
  delete _gridLayout;
  delete _outputSuspendedLabel;
366
  delete _filterChain;
367
368
369
370
371
372
373
374
}

/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                             Display Operations                            */
/*                                                                           */
/* ------------------------------------------------------------------------- */

375
376
377
378
379
/**
 A table for emulating the simple (single width) unicode drawing chars.
 It represents the 250x - 257x glyphs. If it's zero, we can't use it.
 if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered
 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit.
Stephan Kulow's avatar
Stephan Kulow committed
380

381
382
383
384
385
386
387
388
389
390
391
 Then, the pixels basically have the following interpretation:
 _|||_
 -...-
 -...-
 -...-
 _|||_

where _ = none
      | = vertical line.
      - = horizontal line.
 */
Stephan Kulow's avatar
Stephan Kulow committed
392

393
394
395
396
397
398

enum LineEncode
{
    TopL  = (1<<1),
    TopC  = (1<<2),
    TopR  = (1<<3),
Stephan Kulow's avatar
Stephan Kulow committed
399

400
401
402
403
404
    LeftT = (1<<5),
    Int11 = (1<<6),
    Int12 = (1<<7),
    Int13 = (1<<8),
    RightT = (1<<9),
Stephan Kulow's avatar
Stephan Kulow committed
405

406
407
408
409
410
    LeftC = (1<<10),
    Int21 = (1<<11),
    Int22 = (1<<12),
    Int23 = (1<<13),
    RightC = (1<<14),
Stephan Kulow's avatar
Stephan Kulow committed
411

412
413
414
415
416
    LeftB = (1<<15),
    Int31 = (1<<16),
    Int32 = (1<<17),
    Int33 = (1<<18),
    RightB = (1<<19),
Stephan Kulow's avatar
Stephan Kulow committed
417

418
419
    BotL  = (1<<21),
    BotC  = (1<<22),
Dirk Mueller's avatar
Dirk Mueller committed
420
    BotR  = (1<<23)
421
422
};

423
#include "LineFont.h"
424
425
426
427
428
429
430
431
432

static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code)
{
    //Calculate cell midpoints, end points.
    int cx = x + w/2;
    int cy = y + h/2;
    int ex = x + w - 1;
    int ey = y + h - 1;

433
    quint32 toDraw = LineChars[code];
Stephan Kulow's avatar
Stephan Kulow committed
434

435
    //Top _lines:
436
437
438
439
440
441
442
    if (toDraw & TopL)
        paint.drawLine(cx-1, y, cx-1, cy-2);
    if (toDraw & TopC)
        paint.drawLine(cx, y, cx, cy-2);
    if (toDraw & TopR)
        paint.drawLine(cx+1, y, cx+1, cy-2);

443
    //Bot _lines:
444
445
446
447
448
449
450
    if (toDraw & BotL)
        paint.drawLine(cx-1, cy+2, cx-1, ey);
    if (toDraw & BotC)
        paint.drawLine(cx, cy+2, cx, ey);
    if (toDraw & BotR)
        paint.drawLine(cx+1, cy+2, cx+1, ey);

451
    //Left _lines:
452
453
454
455
456
457
458
    if (toDraw & LeftT)
        paint.drawLine(x, cy-1, cx-2, cy-1);
    if (toDraw & LeftC)
        paint.drawLine(x, cy, cx-2, cy);
    if (toDraw & LeftB)
        paint.drawLine(x, cy+1, cx-2, cy+1);

459
    //Right _lines:
460
461
462
463
464
465
    if (toDraw & RightT)
        paint.drawLine(cx+2, cy-1, ex, cy-1);
    if (toDraw & RightC)
        paint.drawLine(cx+2, cy, ex, cy);
    if (toDraw & RightB)
        paint.drawLine(cx+2, cy+1, ex, cy+1);
Stephan Kulow's avatar
Stephan Kulow committed
466

467
468
469
470
471
472
473
    //Intersection points.
    if (toDraw & Int11)
        paint.drawPoint(cx-1, cy-1);
    if (toDraw & Int12)
        paint.drawPoint(cx, cy-1);
    if (toDraw & Int13)
        paint.drawPoint(cx+1, cy-1);
Stephan Kulow's avatar
Stephan Kulow committed
474

475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
    if (toDraw & Int21)
        paint.drawPoint(cx-1, cy);
    if (toDraw & Int22)
        paint.drawPoint(cx, cy);
    if (toDraw & Int23)
        paint.drawPoint(cx+1, cy);

    if (toDraw & Int31)
        paint.drawPoint(cx-1, cy+1);
    if (toDraw & Int32)
        paint.drawPoint(cx, cy+1);
    if (toDraw & Int33)
        paint.drawPoint(cx+1, cy+1);

}

491
void TerminalDisplay::drawLineCharString(	QPainter& painter, int x, int y, const QString& str, 
492
									const Character* attributes)
493
494
495
{
		const QPen& currentPen = painter.pen();
		
496
		if ( attributes->rendition & RE_BOLD )
497
498
499
500
501
502
503
504
505
506
		{
			QPen boldPen(currentPen);
			boldPen.setWidth(3);
			painter.setPen( boldPen );
		}	
		
		for (int i=0 ; i < str.length(); i++)
		{
			uchar code = str[i].cell();
        	if (LineChars[code])
507
            	drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code);
508
509
510
511
512
		}

		painter.setPen( currentPen );
}

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
void TerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape)
{
    _cursorShape = shape;
}
TerminalDisplay::KeyboardCursorShape TerminalDisplay::keyboardCursorShape() const
{
    return _cursorShape;
}
void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color)
{
    if (useForegroundColor)
        _cursorColor = QColor(); // an invalid color means that
                                 // the foreground color of the
                                 // current character should
                                 // be used

    else
        _cursorColor = color;
}
QColor TerminalDisplay::keyboardCursorColor() const
{
    return _cursorColor;
}
536

537
538
539
540
void TerminalDisplay::setOpacity(qreal opacity)
{
    QColor color(_blendColor);
    color.setAlphaF(opacity);
541
542
543
544
545
546
547
548
549
550
551
552

    // enable automatic background filling to prevent the display
    // flickering if there is no transparency
    /*if ( color.alpha() == 255 ) 
    {
        setAutoFillBackground(true);
    }
    else
    {
        setAutoFillBackground(false);
    }*/

553
554
555
    _blendColor = color.rgba();
}

Robert Knight's avatar
   
Robert Knight committed
556
void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting )
557
{
558
559
560
561
562
563
564
565
        // the area of the widget showing the contents of the terminal display is drawn
        // using the background color from the color scheme set with setColorTable()
        //
        // the area of the widget behind the scroll-bar is drawn using the background
        // brush from the scroll-bar's palette, to give the effect of the scroll-bar
        // being outside of the terminal display and visual consistency with other KDE
        // applications.  
        //
566
567
568
        QRect scrollBarArea = _scrollBar->isVisible() ? 
                                    rect.intersected(_scrollBar->geometry()) :
                                    QRect();
569
570
571
        QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea);
        QRect contentsRect = contentsRegion.boundingRect();

Robert Knight's avatar
   
Robert Knight committed
572
        if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting ) 
573
574
575
576
577
578
        {
            QColor color(backgroundColor);
            color.setAlpha(qAlpha(_blendColor));

            painter.save();
            painter.setCompositionMode(QPainter::CompositionMode_Source);
579
            painter.fillRect(contentsRect, color);
580
581
582
            painter.restore();
        } 
        else
583
584
585
            painter.fillRect(contentsRect, backgroundColor);

        painter.fillRect(scrollBarArea,_scrollBar->palette().background());
586
587
}

588
589
590
591
592
593
594
595
596
597
598
599
600
void TerminalDisplay::drawCursor(QPainter& painter, 
                                 const QRect& rect,
                                 const QColor& foregroundColor,
                                 const QColor& /*backgroundColor*/,
                                 bool& invertCharacterColor)
{
    QRect cursorRect = rect;
    cursorRect.setHeight(_fontHeight - _lineSpacing - 1);
    
    if (!_cursorBlinking)
    {
       if ( _cursorColor.isValid() )
           painter.setPen(_cursorColor);
601
602
       else
           painter.setPen(foregroundColor);
603
604
605

       if ( _cursorShape == BlockCursor )
       {
606
607
608
609
610
611
612
613
            // draw the cursor outline, adjusting the area so that
            // it is draw entirely inside 'rect'
            int penWidth = qMax(1,painter.pen().width());

            painter.drawRect(cursorRect.adjusted(penWidth/2,
                                                 penWidth/2,
                                                 - penWidth/2 - penWidth%2,
                                                 - penWidth/2 - penWidth%2));
614
615
616
            if ( hasFocus() )
            {
                painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor);
617
618
619
620
621
622
623
            
                if ( !_cursorColor.isValid() )
                {
                    // invert the colour used to draw the text to ensure that the character at
                    // the cursor position is readable
                    invertCharacterColor = true;
                }
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
            }
       }
       else if ( _cursorShape == UnderlineCursor )
            painter.drawLine(cursorRect.left(),
                             cursorRect.bottom(),
                             cursorRect.right(),
                             cursorRect.bottom());
       else if ( _cursorShape == IBeamCursor )
            painter.drawLine(cursorRect.left(),
                             cursorRect.top(),
                             cursorRect.left(),
                             cursorRect.bottom());
    
    }
}

void TerminalDisplay::drawCharacters(QPainter& painter,
                                     const QRect& rect,
                                     const QString& text,
                                     const Character* style,
                                     bool invertCharacterColor)
{
    // don't draw text which is currently blinking
    if ( _blinking && (style->rendition & RE_BLINK) )
            return;
   
Robert Knight's avatar
   
Robert Knight committed
650
    // setup bold and underline
651
652
    bool useBold = style->rendition & RE_BOLD || style->isBold(_colorTable) || font().bold();
    bool useUnderline = style->rendition & RE_UNDERLINE || font().underline();
Robert Knight's avatar
   
Robert Knight committed
653

654
    QFont font = painter.font();
Robert Knight's avatar
   
Robert Knight committed
655
656
    if (    font.bold() != useBold 
         || font.underline() != useUnderline )
657
658
    {
       font.setBold(useBold);
Robert Knight's avatar
   
Robert Knight committed
659
       font.setUnderline(useUnderline);
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
       painter.setFont(font);
    }

    // setup pen
    const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor );
    const QColor color = textColor.color(_colorTable);
    QPen pen = painter.pen();
    if ( pen.color() != color )
    {
        pen.setColor(color);
        painter.setPen(color);
    }

    // draw text
    if ( isLineCharString(text) )
675
        drawLineCharString(painter,rect.x(),rect.y(),text,style);
676
    else
677
678
679
680
681
682
    {
        // the drawText(rect,flags,string) overload is used here with null flags
        // instead of drawText(rect,string) because the (rect,string) overload causes 
        // the application's default layout direction to be used instead of 
        // the widget-specific layout direction, which should always be
        // Qt::LeftToRight for this widget
683
	// This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2
684
685
686
        if (_bidiEnabled)
            painter.drawText(rect,0,text);
        else
687
            painter.drawText(rect,0,LTR_OVERRIDE_CHAR+text);
688
    }
689
690
691
692
693
694
695
696
697
698
699
700
701
702
}

void TerminalDisplay::drawTextFragment(QPainter& painter , 
                                       const QRect& rect,
                                       const QString& text, 
                                       const Character* style)
{
    painter.save();

    // setup painter 
    const QColor foregroundColor = style->foregroundColor.color(_colorTable);
    const QColor backgroundColor = style->backgroundColor.color(_colorTable);
    
    // draw background if different from the display's background color
703
    if ( backgroundColor != palette().background().color() )
Robert Knight's avatar
   
Robert Knight committed
704
705
        drawBackground(painter,rect,backgroundColor,
					   false /* do not use transparency */);
706
707
708
709
710
711
712
713
714
715
716
717
718

    // draw cursor shape if the current character is the cursor
    // this may alter the foreground and background colors
    bool invertCharacterColor = false;
    if ( style->rendition & RE_CURSOR )
        drawCursor(painter,rect,foregroundColor,backgroundColor,invertCharacterColor);

    // draw text
    drawCharacters(painter,rect,text,style,invertCharacterColor);

    painter.restore();
}

719
720
721
void TerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; }
uint TerminalDisplay::randomSeed() const { return _randomSeed; }

722
#if 0
Waldo Bastian's avatar
Waldo Bastian committed
723
724
725
/*!
    Set XIM Position
*/
726
void TerminalDisplay::setCursorPos(const int curx, const int cury)
Waldo Bastian's avatar
Waldo Bastian committed
727
728
729
730
731
732
{
    QPoint tL  = contentsRect().topLeft();
    int    tLx = tL.x();
    int    tLy = tL.y();

    int xpos, ypos;
Robert Knight's avatar
   
Robert Knight committed
733
734
    ypos = _topMargin + tLy + _fontHeight*(cury-1) + _fontAscent;
    xpos = _leftMargin + tLx + _fontWidth*curx;
735
    //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ???
Waldo Bastian's avatar
Waldo Bastian committed
736
    // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos);
737
738
    _cursorLine = cury;
    _cursorCol = curx;
Waldo Bastian's avatar
Waldo Bastian committed
739
}
740
#endif
Waldo Bastian's avatar
Waldo Bastian committed
741

742
// scrolls the image by 'lines', down if lines > 0 or up otherwise.
743
//
744
// the terminal emulation keeps track of the scrolling of the character 
745
// image as it receives input, and when the view is updated, it calls scrollImage() 
746
// with the final scroll amount.  this improves performance because scrolling the 
747
748
749
// display is much cheaper than re-rendering all the text for the 
// part of the image which has moved up or down.  
// Instead only new lines have to be drawn
750
void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion)
751
{
Robert Knight's avatar
   
Robert Knight committed
752
753
754
755
756
757
	// if the flow control warning is enabled this will interfere with the 
	// scrolling optimisations and cause artifacts.  the simple solution here
	// is to just disable the optimisation whilst it is visible
	if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() )
		return;

758
759
760
761
762
763
764
    // constrain the region to the display
    // the bottom of the region is capped to the number of lines in the display's
    // internal image - 2, so that the height of 'region' is strictly less
    // than the height of the internal image.
    QRect region = screenWindowRegion;
    region.setBottom( qMin(region.bottom(),this->_lines-2) ); 

765
	// return if there is nothing to do
766
    if (    lines == 0 
767
768
769
770
771
         || _image == 0
         || !region.isValid() 
         || (region.top() + abs(lines)) >= region.bottom() 
         || this->_lines <= region.height() ) return;

772
	// hide terminal size label to prevent it being scrolled
773
	if (_resizeWidget && _resizeWidget->isVisible())
774
		_resizeWidget->hide();
775

776
777
778
779
780
781
782
783
784
785
	// Note:  With Qt 4.4 the left edge of the scrolled area must be at 0
	// to get the correct (newly exposed) part of the widget repainted.
	//
	// The right edge must be before the the left edge of the scroll bar to 
	// avoid triggering a repaint of the entire widget.  
	//
	// Set the QT_FLUSH_PAINT environment variable to '1' before starting the
	// application to monitor repainting.
	//
	int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->width();
786
    QRect scrollRect;
787
788
	scrollRect.setLeft(0);
	scrollRect.setRight(width() - scrollBarWidth - 1);
789

790
791
    void* firstCharPos = &_image[ region.top() * this->_columns ];
    void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ];
792

Robert Knight's avatar
   
Robert Knight committed
793
    int top = _topMargin + (region.top() * _fontHeight);
794
795
    int linesToMove = region.height() - abs(lines);
    int bytesToMove = linesToMove * 
796
                      this->_columns *
797
798
799
800
801
                      sizeof(Character);

    Q_ASSERT( linesToMove > 0 );
    Q_ASSERT( bytesToMove > 0 );

802
    //scroll internal image
803
    if ( lines > 0 )
804
    {
805
806
        // check that the memory areas that we are going to move are valid
        Q_ASSERT( (char*)lastCharPos + bytesToMove < 
807
                  (char*)(_image + (this->_lines * this->_columns)) );
808
        
809
        Q_ASSERT( (lines*this->_columns) < _imageSize ); 
810

811
        //scroll internal image down
812
813
        memmove( firstCharPos , lastCharPos , bytesToMove ); 
      
814
815
        //set region of display to scroll
        scrollRect.setTop(top);
816
817
818
    }
    else
    {
819
820
        // check that the memory areas that we are going to move are valid
        Q_ASSERT( (char*)firstCharPos + bytesToMove < 
821
                  (char*)(_image + (this->_lines * this->_columns)) );
822
823

        //scroll internal image up
824
825
        memmove( lastCharPos , firstCharPos , bytesToMove ); 
     
826
827
        //set region of the display to scroll
        scrollRect.setTop(top + abs(lines) * _fontHeight); 
828
    }
829
    scrollRect.setHeight(linesToMove * _fontHeight );
830

831
832
	Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty());

833
    //scroll the display vertically to match internal _image
834
    scroll( 0 , _fontHeight * (-lines) , scrollRect );
835
}
836

837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
QRegion TerminalDisplay::hotSpotRegion() const 
{
	QRegion region;
	foreach( Filter::HotSpot* hotSpot , _filterChain->hotSpots() )
	{
		QRect rect;
		rect.setLeft(hotSpot->startColumn());
		rect.setTop(hotSpot->startLine());
		rect.setRight(hotSpot->endColumn());
		rect.setBottom(hotSpot->endLine());

		region |= imageToWidget(rect); 
	}
	return region;
}

853
void TerminalDisplay::processFilters() 
854
{
855
856
857
	if (!_screenWindow)
		return;

858
	QRegion preUpdateHotSpots = hotSpotRegion();
859

860
861
862
863
864
	// use _screenWindow->getImage() here rather than _image because
	// other classes may call processFilters() when this display's
	// ScreenWindow emits a scrolled() signal - which will happen before
	// updateImage() is called on the display and therefore _image is 
	// out of date at this point
865
866
867
868
	_filterChain->setImage( _screenWindow->getImage(),
							_screenWindow->windowLines(),
							_screenWindow->windowColumns(),
							_screenWindow->getLineProperties() );
869
    _filterChain->process();
870
871
872
873

	QRegion postUpdateHotSpots = hotSpotRegion();

	update( preUpdateHotSpots | postUpdateHotSpots );
874
875
}

876
void TerminalDisplay::updateImage() 
877
{
878
879
880
  if ( !_screenWindow )
      return;

881
882
  // optimization - scroll the existing image where possible and 
  // avoid expensive text drawing for parts of the image that 
Robert Knight's avatar
   
Robert Knight committed
883
  // can simply be moved up or down
884
885
  scrollImage( _screenWindow->scrollCount() ,
               _screenWindow->scrollRegion() );
886
887
  _screenWindow->resetScrollCount();

888
  Character* const newimg = _screenWindow->getImage();
889
890
  int lines = _screenWindow->windowLines();
  int columns = _screenWindow->windowColumns();
891
892
893

  setScroll( _screenWindow->currentLine() , _screenWindow->lineCount() );

894
895
  if (!_image)
     updateImageSize(); // Create _image
896

897
898
  Q_ASSERT( this->_usedLines <= this->_lines );
  Q_ASSERT( this->_usedColumns <= this->_columns );
899

900
  int y,x,len;
901
902
903
904

  QPoint tL  = contentsRect().topLeft();
  int    tLx = tL.x();
  int    tLy = tL.y();
905
  _hasBlinker = false;
906

907
  CharacterColor cf;       // undefined
908
  CharacterColor _clipboard;       // undefined
909
  int cr  = -1;   // undefined
910

911
912
  const int linesToUpdate = qMin(this->_lines, qMax(0,lines  ));
  const int columnsToUpdate = qMin(this->_columns,qMax(0,columns));
913

914
  QChar *disstrU = new QChar[columnsToUpdate];
915
  char *dirtyMask = new char[columnsToUpdate+2]; 
916
  QRegion dirtyRegion;
917

918
  // debugging variable, this records the number of lines that are found to
919
  // be 'dirty' ( ie. have changed from the old _image to the new _image ) and
Robert Knight's avatar
   
Robert Knight committed
920
  // which therefore need to be repainted
921
922
  int dirtyLineCount = 0;

923
  for (y = 0; y < linesToUpdate; y++)
924
  {
925
    const Character*       currentLine = &_image[y*this->_columns];
926
    const Character* const newLine = &newimg[y*columns];
927

928
929
    bool updateLine = false;
    
930
    // The dirty mask indicates which characters need repainting. We also
Waldo Bastian's avatar
Waldo Bastian committed
931
932
    // mark surrounding neighbours dirty, in case the character exceeds
    // its cell boundaries
933
    memset(dirtyMask, 0, columnsToUpdate+2);
934
935
   
    for( x = 0 ; x < columnsToUpdate ; x++)
Waldo Bastian's avatar
Waldo Bastian committed
936
    {
937
938
939
940
        if ( newLine[x] != currentLine[x] ) 
        {
            dirtyMask[x] = true;
        }
Waldo Bastian's avatar
Waldo Bastian committed
941
    }
942

943
    if (!_resizing) // not while _resizing, we're expecting a paintEvent
944
    for (x = 0; x < columnsToUpdate; x++)
945
    {
946
      _hasBlinker |= (newLine[x].rendition & RE_BLINK);
947
    
Waldo Bastian's avatar
Waldo Bastian committed
948
949
950
951
      // Start drawing if this character or the next one differs.
      // We also take the next one into account to handle the situation
      // where characters exceed their cell width.
      if (dirtyMask[x])
952
      {
953
        quint16 c = newLine[x+0].character;
954
955
956
        if ( !c )
            continue;
        int p = 0;
Stephan Binner's avatar
Stephan Binner committed
957
        disstrU[p++] = c; //fontMap(c);
958
        bool lineDraw = isLineChar(c);
959
        bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0);
960
        cr = newLine[x].rendition;
961
        _clipboard = newLine[x].backgroundColor;
962
        if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor;
963
        int lln = columnsToUpdate - x;
964
965
        for (len = 1; len < lln; len++)
        {
Robert Knight's avatar
   
Robert Knight committed
966
            const Character& ch = newLine[x+len];
967

Robert Knight's avatar
   
Robert Knight committed
968
969
970
            if (!ch.character)
                continue; // Skip trailing part of multi-col chars.

971
972
			bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0);

Robert Knight's avatar
   
Robert Knight committed
973
            if (  ch.foregroundColor != cf || 
974
                  ch.backgroundColor != _clipboard || 
Robert Knight's avatar
   
Robert Knight committed
975
976
977
                  ch.rendition != cr ||
                  !dirtyMask[x+len] || 
                  isLineChar(c) != lineDraw || 
978
                  nextIsDoubleWidth != doubleWidth )
979
            break;
980

Stephan Binner's avatar
Stephan Binner committed
981
          disstrU[p++] = c; //fontMap(c);
982
        }
983
984

        QString unistr(disstrU, p);
985

Robert Knight's avatar
   
Robert Knight committed
986
        bool saveFixedFont = _fixedFont;
987
        if (lineDraw)
988
           _fixedFont = false;
989
        if (doubleWidth)
990
           _fixedFont = false;
991

992
		updateLine = true;
993

Robert Knight's avatar
   
Robert Knight committed
994
		_fixedFont = saveFixedFont;
995
996
        x += len - 1;
      }
997
      
998
    }
999

1000
	//both the top and bottom halves of double height _lines must always be redrawn
Robert Knight's avatar
   
Robert Knight committed
1001
1002
	//although both top and bottom halves contain the same characters, only 
    //the top one is actually 
1003
	//drawn.
1004
1005
    if (_lineProperties.count() > y)
        updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT);
Robert Knight's avatar
   
Robert Knight committed
1006

1007
    // if the characters on the line are different in the old and the new _image
Robert Knight's avatar
   
Robert Knight committed
1008
    // then this line must be repainted.    
1009
1010
    if (updateLine)
    {
1011
        dirtyLineCount++;
Robert Knight's avatar
   
Robert Knight committed
1012
1013
1014

        // add the area occupied by this line to the region which needs to be
        // repainted
Robert Knight's avatar
   
Robert Knight committed
1015
1016
        QRect dirtyRect = QRect( _leftMargin+tLx , 
                                 _topMargin+tLy+_fontHeight*y , 
1017
1018
                                 _fontWidth * columnsToUpdate , 
                                 _fontHeight ); 	
1019

1020
        dirtyRegion |= dirtyRect;
1021
    }
1022

1023
1024
    // replace the line of characters in the old _image with the 
    // current line of the new _image