TerminalDisplay.cpp 82.5 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
27
28
29
30
31
32
33
// System
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
34

Robert Knight's avatar
Robert Knight committed
35
// Qt
Dirk Mueller's avatar
Dirk Mueller committed
36
37
38
39
40
41
42
43
44
45
46
47
#include <QtGui/QApplication>
#include <QtGui/QBoxLayout>
#include <QtGui/QClipboard>
#include <QtGui/QKeyEvent>
#include <QtCore/QEvent>
#include <QtCore/QFile>
#include <QtGui/QGridLayout>
#include <QtGui/QLabel>
#include <QtGui/QLayout>
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
#include <QtGui/QLayoutItem>
48
#include <QtGui/QScrollBar>
Dirk Mueller's avatar
Dirk Mueller committed
49
50
51
#include <QtGui/QStyle>
#include <QtCore/QTimer>
#include <QtGui/QToolTip>
52

Robert Knight's avatar
Robert Knight committed
53
54
55
// KDE
#include <KRun>
#include <KCursor>
56
#include <kdebug.h>
Robert Knight's avatar
Robert Knight committed
57
#include <KLocale>
58
#include <KMenu>
Robert Knight's avatar
Robert Knight committed
59
60
61
62
63
64
65
#include <KNotification>
#include <KGlobalSettings>
#include <KShortcut>
#include <KIO/NetAccess>

// Konsole
#include "config.h"
66
#include "Filter.h"
Robert Knight's avatar
Robert Knight committed
67
#include "konsole_wcwidth.h"
68
#include "ScreenWindow.h"
69

70
71
using namespace Konsole;

72
#ifndef loc
73
#define loc(X,Y) ((Y)*_columns+(X))
74
75
76
#endif

#define yMouseScroll 1
Waldo Bastian's avatar
Waldo Bastian committed
77
78
79
80

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

82
83
// scroll increment used when dragging selection at top/bottom of window.

84
// static
85
86
bool TerminalDisplay::s_antialias = true;
bool TerminalDisplay::s_standalone = false;
87
bool TerminalDisplay::HAVE_TRANSPARENCY = false;
88

89
90
91
92
93
94
95
96
97
98
99
100
101
102
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                                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
*/

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

115
    _screenWindow = window;
116

117
118
    if ( window )
    {
119
#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?"
120
121
122
        connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) );
        connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) );
    }
123
124
}

125
void TerminalDisplay::setDefaultBackColor(const QColor& color)
126
{
127
  _defaultBgColor = color;
128

129
  QPalette p = palette();
130
  p.setColor( backgroundRole(), defaultBackColor() );
131
  setPalette( p );
132
133
}

134
QColor TerminalDisplay::defaultBackColor()
135
{
136
137
138
  if (_defaultBgColor.isValid())
    return _defaultBgColor;
  return _colorTable[DEFAULT_BACK_COLOR].color;
139
140
}

141
const ColorEntry* TerminalDisplay::colorTable() const
142
{
143
  return _colorTable;
144
145
}

146
void TerminalDisplay::setColorTable(const ColorEntry table[])
147
{
148
  for (int i = 0; i < TABLE_COLORS; i++) _colorTable[i] = table[i];
149
 
150
151
152
153
  QPalette p = palette();
  p.setColor( backgroundRole(), defaultBackColor() );
  setPalette( p );
  
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  update();
}

/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                                   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.
*/

175
static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);}
176
177
178
179
180
static inline bool isLineCharString(const QString& string)
{
		return (string.length() > 0) && (isLineChar(string.at(0).unicode()));
}
						
181

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

184
unsigned short Konsole::vt100_graphics[32] =
185
186
187
188
189
190
191
{ // 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
};

192
void TerminalDisplay::fontChange(const QFont &)
193
194
{
  QFontMetrics fm(font());
195
  _fontHeight = fm.height() + _lineSpacing;
196

197
  // waba TerminalDisplay 1.123:
198
199
  // "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
200
  // Get the width from representative normal width characters
201
  _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR));
Waldo Bastian's avatar
Waldo Bastian committed
202

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

212
213
214
215
  if (_fontWidth>200) // don't trust unrealistic value, fallback to QFontMetrics::maxWidth()
    _fontWidth=fm.maxWidth();
  if (_fontWidth<1)
    _fontWidth=1;
216

217
  _fontAscent = fm.ascent();
218

219
  emit changedFontMetricSignal( _fontHeight, _fontWidth );
220
221
222
223
  propagateSize();
  update();
}

224
void TerminalDisplay::setVTFont(const QFont& f)
225
{
226
  QFont font = f;
Robert Knight's avatar
Robert Knight committed
227
228
229
230
231
232
233

  QFontMetrics metrics(font);

  if ( metrics.height() < height() && metrics.maxWidth() < width() )
  {
    if (!s_antialias)
        font.setStyleStrategy( QFont::NoAntialias );
234
  
Robert Knight's avatar
Robert Knight committed
235
236
237
    QFrame::setFont(font);
    fontChange(font);
  }
238
239
}

240
void TerminalDisplay::setFont(const QFont &)
241
242
243
244
245
246
247
248
249
250
{
  // ignore font change request if not coming from konsole itself
}

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

251
TerminalDisplay::TerminalDisplay(QWidget *parent)
252
:QFrame(parent)
253
,_screenWindow(0)
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
,_allowBell(true)
,_gridLayout(0)
,_fontHeight(1)
,_fontWidth(1)
,_fontAscent(1)
,_lines(1)
,_columns(1)
,_usedLines(1)
,_usedColumns(1)
,_contentHeight(1)
,_contentWidth(1)
,_image(0)
,_resizing(false)
,_terminalSizeHint(false)
,_terminalSizeStartup(true)
,_bidiEnabled(false)
,_actSel(0)
,_wordSelectionMode(false)
,_lineSelectionMode(false)
,_preserveLineBreaks(true)
,_columnSelectionMode(false)
,_scrollbarLocation(SCROLLBAR_NONE)
,_wordCharacters(":@-./_~")
277
,_bellMode(BELL_SYSTEM)
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
,_blinking(false)
,_cursorBlinking(false)
,_hasBlinkingCursor(false)
,_ctrlDrag(false)
,_cutToBeginningOfLine(false)
,_isPrinting(false)
,_printerFriendly(false)
,_printerBold(false)
,_isFixedSize(false)
,_drop(0)
,_possibleTripleClick(false)
,_resizeWidget(0)
,_resizeLabel(0)
,_resizeTimer(0)
,_outputSuspendedLabel(0)
,_lineSpacing(0)
,_colorsInverted(false)
,_rimX(1)
,_rimY(1)
,_imPreeditText(QString())
,_imPreeditLength(0)
,_imStart(0)
,_imStartLine(0)
,_imEnd(0)
,_imSelStart(0)
,_imSelEnd(0)
,_cursorLine(0)
,_cursorCol(0)
,_isIMEdit(false)
,_blendColor(qRgba(0,0,0,0xff))
308
,_filterChain(new TerminalImageFilterChain())
309
,_cursorShape(BlockCursor)
310
{
311

312
  // The offsets are not yet calculated.
313
  // Do not calculate these too often to be more smoothly when resizing
314
  // konsole in opaque mode.
315
  _bY = _bX = 1;
316

317
318
  // 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
319
  _scrollBar = new QScrollBar(this);
320
  setScroll(0,0); 
321
322
  _scrollBar->setCursor( Qt::ArrowCursor );
  connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int)));
323

324
325
326
327
  _blinkTimer   = new QTimer(this);
  connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent()));
  _blinkCursorTimer   = new QTimer(this);
  connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent()));
328

Robert Knight's avatar
   
Robert Knight committed
329
  setUsesMouse(true);
330
  setColorTable(base_color_table); 
331
332
333

  KCursor::setAutoHideCursor( this, true );

334
335
  setMouseTracking(true);

Robert Knight's avatar
   
Robert Knight committed
336
  // Init DnD 
337
  setAcceptDrops(true); // attempt
338
  dragInfo.state = diNone;
339

340
  setFocusPolicy( Qt::WheelFocus );
Kazuki Ohta's avatar
Kazuki Ohta committed
341
  // im
342
  setAttribute(Qt::WA_InputMethodEnabled, true);
Stephan Kulow's avatar
Stephan Kulow committed
343

344
345
346
  // this is an important optimisation, it tells Qt
  // that TerminalDisplay will handle repainting its entire area.
  setAttribute(Qt::WA_OpaquePaintEvent);
347

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

351
  setLayout( _gridLayout ); 
352
  setLineWidth(0);
353
354
355

  //set up a warning message when the user presses Ctrl+S to avoid confusion
  connect( this,SIGNAL(flowControlKeyPressed(bool)),this,SLOT(outputSuspended(bool)) );
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
424
425
426
427
428
429
430
431
432
};

#include "linefont.h"

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
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
    _blendColor = color.rgba();
}

void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor)
{
        if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff ) 
        {
            QColor color(backgroundColor);
            color.setAlpha(qAlpha(_blendColor));

            painter.save();
            painter.setCompositionMode(QPainter::CompositionMode_Source);
            painter.fillRect(rect, color);
            painter.restore();
        } 
        else
            painter.fillRect(rect, backgroundColor);
}

572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
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
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
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);

       if ( _cursorShape == BlockCursor )
       {
            painter.drawRect(cursorRect);
            if ( hasFocus() )
            {
                painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor);
            }

            if ( !_cursorColor.isValid() )
            {
                // invert the colour used to draw the text to ensure that the character at
                // the cursor position is readable
                invertCharacterColor = true;
            }
       }
       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;
   
    // setup bold
    bool useBold = style->rendition & RE_BOLD || style->isBold(_colorTable);
    QFont font = painter.font();
    if ( font.bold() != useBold )
    {
       font.setBold(useBold);
       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) )
	  	drawLineCharString(painter,rect.x(),rect.y(),text,style);
    else
        painter.drawText(rect,text);
}

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
    if ( backgroundColor != defaultBackColor() )
        drawBackground(painter,rect,backgroundColor);

    // 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();
}

Waldo Bastian's avatar
Waldo Bastian committed
678
679
680
/*!
    Set XIM Position
*/
681
void TerminalDisplay::setCursorPos(const int curx, const int cury)
Waldo Bastian's avatar
Waldo Bastian committed
682
683
684
685
686
687
{
    QPoint tL  = contentsRect().topLeft();
    int    tLx = tL.x();
    int    tLy = tL.y();

    int xpos, ypos;
688
689
690
    ypos = _bY + tLy + _fontHeight*(cury-1) + _fontAscent;
    xpos = _bX + tLx + _fontWidth*curx;
    //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ???
Waldo Bastian's avatar
Waldo Bastian committed
691
    // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos);
692
693
    _cursorLine = cury;
    _cursorCol = curx;
Waldo Bastian's avatar
Waldo Bastian committed
694
695
}

696
// scrolls the _image by '_lines', down if _lines > 0 or up otherwise.
697
//
698
699
700
701
702
// the terminal emulation keeps track of the scrolling of the character 
// _image as it receives input, and when the view is updated, it calls scrollImage() 
// with the final scroll amount.  this improves performance because scrolling the 
// 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
703
//
704
705
706
// note:  it is important that the area of the display which is scrolled aligns properly with
// the character grid - which has a top left point at (_bX,_bY) , 
// a cell width of _fontWidth and a cell height of _fontHeight).    
707
void TerminalDisplay::scrollImage(int _lines)
708
{
709
    if ( _lines == 0 || _image == 0 || abs(_lines) >= this->_usedLines ) return;
710
711
712

    QRect scrollRect;

713
714
    //scroll internal _image
    if ( _lines > 0 )
715
    {
716
        assert( (_lines*this->_usedColumns) < _imageSize ); 
717

718
        //scroll internal _image down
719
720
721
722
        memmove( _image , &_image[_lines*this->_usedColumns] , 
                        ( this->_usedLines - _lines ) * 
                          this->_usedColumns * 
                          sizeof(Character) );
723
724
725
 
        //set region of display to scroll, making sure that
        //the region aligns correctly to the character grid 
726
727
728
        scrollRect = QRect( _bX ,_bY, 
                            this->_usedColumns * _fontWidth , 
                            (this->_usedLines - _lines) * _fontHeight );
729

730
        //qDebug() << "scrolled down " << _lines << " _lines";
731
732
733
    }
    else
    {
734
735
        //scroll internal _image up
        memmove( &_image[ abs(_lines)*this->_usedColumns] , _image , 
736
737
738
                        (this->_usedLines - abs(_lines) ) * 
                         this->_usedColumns * 
                         sizeof(Character) );
739
740
741
742

        //set region of the display to scroll, making sure that
        //the region aligns correctly to the character grid
        
743
        QPoint topPoint( _bX , _bY + abs(_lines)*_fontHeight );
744

745
746
747
        scrollRect = QRect( topPoint , 
                     QSize( this->_usedColumns*_fontWidth , 
                            (this->_usedLines - abs(_lines)) * _fontHeight ));
748
        
749
        //qDebug() << "scrolled up " << _lines << " _lines";
750
751
    }

752
753
    //scroll the display vertically to match internal _image
    scroll( 0 , _fontHeight * (-_lines) , scrollRect );
754
}
755

756
void TerminalDisplay::processFilters() 
757
{
758
759
760
761
762
763
764
    QTime t;
    t.start();

    _filterChain->setImage(_image,_lines,_columns);

    qDebug() << "Setup filters in" << t.elapsed() << "ms.";

765
    _filterChain->process();
766
767

    qDebug() << "Processed filters in" << t.elapsed() << "ms.";
768
769
}

770
void TerminalDisplay::updateImage() 
771
{
772
773
774
  if ( !_screenWindow )
      return;

775
776
  // 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
777
  // can simply be moved up or down
778
  scrollImage( _screenWindow->scrollCount() );
779
780
  _screenWindow->resetScrollCount();

781
  Character* const newimg = _screenWindow->getImage();
782
783
  int _lines = _screenWindow->windowLines();
  int _columns = _screenWindow->windowColumns();
784
785
786

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

787
788
  if (!_image)
     updateImageSize(); // Create _image
789

790
791
  assert( this->_usedLines <= this->_lines );
  assert( this->_usedColumns <= this->_columns );
792

793
  int y,x,len;
794
795
796
797

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

800
  CharacterColor cf;       // undefined
801
  CharacterColor _clipboard;       // undefined
802
  int cr  = -1;   // undefined
803

804
805
  const int linesToUpdate = qMin(this->_lines, qMax(0,_lines  ));
  const int columnsToUpdate = qMin(this->_columns,qMax(0,_columns));
806

807
  QChar *disstrU = new QChar[columnsToUpdate];
808
  char *dirtyMask = new char[columnsToUpdate+2]; 
809
  QRegion dirtyRegion;
810

811
812
  // debugging variable, this records the number of _lines that are found to
  // be 'dirty' ( ie. have changed from the old _image to the new _image ) and
Robert Knight's avatar
   
Robert Knight committed
813
  // which therefore need to be repainted
814
815
  int dirtyLineCount = 0;

816
  for (y = 0; y < linesToUpdate; y++)
817
  {
818
819
    const Character*       currentLine = &_image[y*this->_columns];
    const Character* const newLine = &newimg[y*_columns];
820

821
822
    bool updateLine = false;
    
823
    // The dirty mask indicates which characters need repainting. We also
Waldo Bastian's avatar
Waldo Bastian committed
824
825
    // mark surrounding neighbours dirty, in case the character exceeds
    // its cell boundaries
826
    memset(dirtyMask, 0, columnsToUpdate+2);
Waldo Bastian's avatar
Waldo Bastian committed
827
    // Two extra so that we don't have to have to care about start and end conditions
828
    for (x = 0; x < columnsToUpdate; x++)
Waldo Bastian's avatar
Waldo Bastian committed
829
    {
830
831
832
833
834
835
	if ( ( (_imPreeditLength > 0) && 
           ( ( _imStartLine == y ) && 
             ( ( _imStart < _imEnd ) && 
               ( ( x > _imStart ) ) && 
               ( x < _imEnd ) )
              || ( ( _imSelStart < _imSelEnd ) && ( ( x > _imSelStart ) ) ) ) )
836
            || newLine[x] != currentLine[x])
Waldo Bastian's avatar
Waldo Bastian committed
837
838
839
840
841
      {
         dirtyMask[x] = dirtyMask[x+1] = dirtyMask[x+2] = 1;
      }
    }
    dirtyMask++; // Position correctly
842

843
    if (!_resizing) // not while _resizing, we're expecting a paintEvent
844
    for (x = 0; x < columnsToUpdate; x++)
845
    {
846
      _hasBlinker |= (newLine[x].rendition & RE_BLINK);
847
    
Waldo Bastian's avatar
Waldo Bastian committed
848
849
850
851
      // 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])
852
      {
853
        quint16 c = newLine[x+0].character;
854
855
856
        if ( !c )
            continue;
        int p = 0;
Stephan Binner's avatar
Stephan Binner committed
857
        disstrU[p++] = c; //fontMap(c);
858
        bool lineDraw = isLineChar(c);
859
860
        bool doubleWidth = (newLine[x+1].character == 0);
        cr = newLine[x].rendition;
861
        _clipboard = newLine[x].backgroundColor;
862
        if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor;
863
        int lln = columnsToUpdate - x;
864
865
        for (len = 1; len < lln; len++)
        {
Robert Knight's avatar
   
Robert Knight committed
866
            const Character& ch = newLine[x+len];
867

Robert Knight's avatar
   
Robert Knight committed
868
869
870
871
            if (!ch.character)
                continue; // Skip trailing part of multi-col chars.

            if (  ch.foregroundColor != cf || 
872
                  ch.backgroundColor != _clipboard || 
Robert Knight's avatar
   
Robert Knight committed
873
874
875
876
                  ch.rendition != cr ||
                  !dirtyMask[x+len] || 
                  isLineChar(c) != lineDraw || 
                  (newLine[x+len+1].character == 0) != doubleWidth )
877
            break;
878

Stephan Binner's avatar
Stephan Binner committed
879
          disstrU[p++] = c; //fontMap(c);
880
        }
881
882

        QString unistr(disstrU, p);
883
884

        // for XIM on the spot input style
885
        _isIMEdit = _isIMSel = false;
886

887
        if ( _imStartLine == y ) 
Robert Knight's avatar
   
Robert Knight committed
888
        {
889
890
891
          if (  ( _imStart < _imEnd ) && 
                ( x >= _imStart-1 ) && 
                ( x + int( unistr.length() ) <= _imEnd ) 
Robert Knight's avatar
   
Robert Knight committed
892
             )
893
                _isIMEdit = true;
Robert Knight's avatar
   
Robert Knight committed
894

895
896
897
          if ( ( _imSelStart < _imSelEnd ) && 
               ( x >= _imStart-1 ) && 
               ( x + int( unistr.length() ) <= _imEnd ) 
Robert Knight's avatar
   
Robert Knight committed
898
             )
899
                _isIMSel = true;
Robert Knight's avatar
   
Robert Knight committed
900
	    }
901
        else if ( _imStartLine < y ) 
Robert Knight's avatar
   
Robert Knight committed
902
        {  // for word warp
903
904
          if ( _imStart < _imEnd )
            _isIMEdit = true;
905

906
907
          if (  _imSelStart < _imSelEnd )
            _isIMSel = true;
Robert Knight's avatar
   
Robert Knight committed
908
	    }
Stephan Kulow's avatar
Stephan Kulow committed
909

910
        bool save__fixedFont = _fixedFont;
911
        if (lineDraw)
912
           _fixedFont = false;
913
        if (doubleWidth)
914
           _fixedFont = false;
915

916
		updateLine = true;
917

918
		_fixedFont = save__fixedFont;
919
920
        x += len - 1;
      }
921
      
922
    }
923

924
	//both the top and bottom halves of double height _lines must always be redrawn
Robert Knight's avatar
   
Robert Knight committed
925
926
	//although both top and bottom halves contain the same characters, only 
    //the top one is actually 
927
	//drawn.
928
929
    if (_lineProperties.count() > y)
        updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT);
Robert Knight's avatar
   
Robert Knight committed
930

931
    // if the characters on the line are different in the old and the new _image
Robert Knight's avatar
   
Robert Knight committed
932
    // then this line must be repainted.    
933
934
    if (updateLine)
    {
935
        dirtyLineCount++;
Robert Knight's avatar
   
Robert Knight committed
936
937
938

        // add the area occupied by this line to the region which needs to be
        // repainted
939
940
941
942
        QRect dirtyRect = QRect( _bX+tLx , 
                                 _bY+tLy+_fontHeight*y , 
                                 _fontWidth * columnsToUpdate , 
                                 _fontHeight ); 	
943

944
        dirtyRegion |= dirtyRect;
945
    }
946

Waldo Bastian's avatar
Waldo Bastian committed
947
    dirtyMask--; // Set back
948

949
950
    // replace the line of characters in the old _image with the 
    // current line of the new _image 
951
    memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character));
952
  }
953

954
  // free the image from the screen window
955
  delete[] newimg;
956

957
  // debugging - display a count of the number of _lines that will need 
Robert Knight's avatar
   
Robert Knight committed
958
  // to be repainted
959
  //qDebug() << "dirty line count = " << dirtyLineCount;
960

961
962
963
  // if the new _image is smaller than the previous _image, then ensure that the area
  // outside the new _image is cleared 
  if ( linesToUpdate < _usedLines )
964
  {
965
966
967
968
    dirtyRegion |= QRect(   _bX+tLx , 
                            _bY+tLy+_fontHeight*linesToUpdate , 
                            _fontWidth * this->_columns , 
                            _fontHeight * (_usedLines-linesToUpdate) );
969
  }
970
  _usedLines = linesToUpdate;
971
  
972
  if ( columnsToUpdate < _usedColumns )
973
  {
974
975
976
977
    dirtyRegion |= QRect(   _bX+tLx+columnsToUpdate*_fontWidth , 
                            _bY+tLy , 
                            _fontWidth * (_usedColumns-columnsToUpdate) , 
                            _fontHeight * this->_lines );
978
  }
979
  _usedColumns = columnsToUpdate;
980

981
982
  //qDebug() << "Expecting to update: " << dirtyRegion.boundingRect();

Robert Knight's avatar
   
Robert Knight committed
983
  // update the parts of the display which have changed
984
  update(dirtyRegion);
Stephan Kulow's avatar
Stephan Kulow committed
985

986
987
  if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( BLINK_DELAY ); 
  if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; }
988
989
  delete[] dirtyMask;
  delete[] disstrU;
990

991
992
993
  showResizeNotification();
}

994
void TerminalDisplay::showResizeNotification()
995
{
996
  if (_resizing && _terminalSizeHint)
997
  {
998
999
     if (_terminalSizeStartup) {
       _terminalSizeStartup=false;
1000
       return;
For faster browsing, not all history is shown. View entire blame