TerminalDisplay.cpp 83.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
// KDE
54
#include <kshell.h>
Robert Knight's avatar
Robert Knight committed
55
#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
#include <KNotification>
#include <KGlobalSettings>
#include <KShortcut>
#include <KIO/NetAccess>

// Konsole
Dirk Mueller's avatar
Dirk Mueller committed
65
#include <config-apps.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

Robert Knight's avatar
Robert Knight committed
344
  // this is an important optimization, it tells Qt
345
346
  // 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
#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
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
// the terminal emulation keeps track of the scrolling of the character 
699
// image as it receives input, and when the view is updated, it calls scrollImage() 
700
// with the final scroll amount.  this improves performance because scrolling the 
701
702
703
// 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
704
//
705
706
707
// 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) , 
708
// a cell width of _fontWidth and a cell height of _fontHeight).    
709
void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion)
710
{
711
712
713
714
715
716
717
    // 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) ); 

718
    if (    lines == 0 
719
720
721
722
723
724
         || _image == 0
         || !region.isValid() 
         || (region.top() + abs(lines)) >= region.bottom() 
         || this->_lines <= region.height() ) return;

   
725
726
727

    QRect scrollRect;

728
729
730
731
732
    //qDebug() << "Scrolled region: top =" << region.top() 
    //         << ", bottom =" << region.bottom()
    //         << ", height =" << region.height() << "lines.  Image height ="
    //         << this->_usedLines << "lines"
    //         << ", scroll =" << lines << "lines";
733

734
735
    void* firstCharPos = &_image[ region.top() * this->_columns ];
    void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ];
736
737
738
739

    int top = _bY + (region.top() * _fontHeight);
    int linesToMove = region.height() - abs(lines);
    int bytesToMove = linesToMove * 
740
                      this->_columns *
741
742
743
744
745
                      sizeof(Character);

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

746
    //scroll internal image
747
    if ( lines > 0 )
748
    {
749
750
        // check that the memory areas that we are going to move are valid
        Q_ASSERT( (char*)lastCharPos + bytesToMove < 
751
                  (char*)(_image + (this->_lines * this->_columns)) );
752
        
753
        assert( (lines*this->_columns) < _imageSize ); 
754

755
        //scroll internal image down
756
757
        memmove( firstCharPos , lastCharPos , bytesToMove ); 
      
758
759
        //set region of display to scroll, making sure that
        //the region aligns correctly to the character grid 
760
        scrollRect = QRect( _bX , top, 
761
                            this->_usedColumns * _fontWidth , 
762
                            linesToMove * _fontHeight );
763
764
765
    }
    else
    {
766
767
        // check that the memory areas that we are going to move are valid
        Q_ASSERT( (char*)firstCharPos + bytesToMove < 
768
                  (char*)(_image + (this->_lines * this->_columns)) );
769
770

        //scroll internal image up
771
772
        memmove( lastCharPos , firstCharPos , bytesToMove ); 
     
773
774
        //set region of the display to scroll, making sure that
        //the region aligns correctly to the character grid
775
        QPoint topPoint( _bX , top + abs(lines)*_fontHeight );
776

777
        scrollRect = QRect( topPoint ,
778
                     QSize( this->_usedColumns*_fontWidth , 
779
                            linesToMove * _fontHeight ));
780
781
    }

782
    //scroll the display vertically to match internal _image
783
    scroll( 0 , _fontHeight * (-lines) , scrollRect );
784
}
785

786
void TerminalDisplay::processFilters() 
787
{
788
789
790
791
792
793
794
    QTime t;
    t.start();

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

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

795
    _filterChain->process();
796
797

    qDebug() << "Processed filters in" << t.elapsed() << "ms.";
798
799
}

800
void TerminalDisplay::updateImage() 
801
{
802
803
804
  if ( !_screenWindow )
      return;

805
806
  // 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
807
  // can simply be moved up or down
808
809
  scrollImage( _screenWindow->scrollCount() ,
               _screenWindow->scrollRegion() );
810
811
  _screenWindow->resetScrollCount();

812
  Character* const newimg = _screenWindow->getImage();
813
814
  int lines = _screenWindow->windowLines();
  int columns = _screenWindow->windowColumns();
815
816
817

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

818
819
  if (!_image)
     updateImageSize(); // Create _image
820

821
822
  assert( this->_usedLines <= this->_lines );
  assert( this->_usedColumns <= this->_columns );
823

824
  int y,x,len;
825
826
827
828

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

831
  CharacterColor cf;       // undefined
832
  CharacterColor _clipboard;       // undefined
833
  int cr  = -1;   // undefined
834

835
836
  const int linesToUpdate = qMin(this->_lines, qMax(0,lines  ));
  const int columnsToUpdate = qMin(this->_columns,qMax(0,columns));
837

838
  QChar *disstrU = new QChar[columnsToUpdate];
839
  char *dirtyMask = new char[columnsToUpdate+2]; 
840
  QRegion dirtyRegion;
841

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

847
  for (y = 0; y < linesToUpdate; y++)
848
  {
849
    const Character*       currentLine = &_image[y*this->_columns];
850
    const Character* const newLine = &newimg[y*columns];
851

852
853
    bool updateLine = false;
    
854
    // The dirty mask indicates which characters need repainting. We also
Waldo Bastian's avatar
Waldo Bastian committed
855
856
    // mark surrounding neighbours dirty, in case the character exceeds
    // its cell boundaries
857
    memset(dirtyMask, 0, columnsToUpdate+2);
Waldo Bastian's avatar
Waldo Bastian committed
858
    // Two extra so that we don't have to have to care about start and end conditions
859
    for (x = 0; x < columnsToUpdate; x++)
Waldo Bastian's avatar
Waldo Bastian committed
860
    {
861
862
863
864
865
866
	if ( ( (_imPreeditLength > 0) && 
           ( ( _imStartLine == y ) && 
             ( ( _imStart < _imEnd ) && 
               ( ( x > _imStart ) ) && 
               ( x < _imEnd ) )
              || ( ( _imSelStart < _imSelEnd ) && ( ( x > _imSelStart ) ) ) ) )
867
            || newLine[x] != currentLine[x])
Waldo Bastian's avatar
Waldo Bastian committed
868
869
870
871
872
      {
         dirtyMask[x] = dirtyMask[x+1] = dirtyMask[x+2] = 1;
      }
    }
    dirtyMask++; // Position correctly
873

874
    if (!_resizing) // not while _resizing, we're expecting a paintEvent
875
    for (x = 0; x < columnsToUpdate; x++)
876
    {
877
      _hasBlinker |= (newLine[x].rendition & RE_BLINK);
878
    
Waldo Bastian's avatar
Waldo Bastian committed
879
880
881
882
      // 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])
883
      {
884
        quint16 c = newLine[x+0].character;
885
886
887
        if ( !c )
            continue;
        int p = 0;
Stephan Binner's avatar
Stephan Binner committed
888
        disstrU[p++] = c; //fontMap(c);
889
        bool lineDraw = isLineChar(c);
890
891
        bool doubleWidth = (newLine[x+1].character == 0);
        cr = newLine[x].rendition;
892
        _clipboard = newLine[x].backgroundColor;
893
        if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor;
894
        int lln = columnsToUpdate - x;
895
896
        for (len = 1; len < lln; len++)
        {
Robert Knight's avatar
   
Robert Knight committed
897
            const Character& ch = newLine[x+len];
898

Robert Knight's avatar
   
Robert Knight committed
899
900
901
902
            if (!ch.character)
                continue; // Skip trailing part of multi-col chars.

            if (  ch.foregroundColor != cf || 
903
                  ch.backgroundColor != _clipboard || 
Robert Knight's avatar
   
Robert Knight committed
904
905
906
907
                  ch.rendition != cr ||
                  !dirtyMask[x+len] || 
                  isLineChar(c) != lineDraw || 
                  (newLine[x+len+1].character == 0) != doubleWidth )
908
            break;
909

Stephan Binner's avatar
Stephan Binner committed
910
          disstrU[p++] = c; //fontMap(c);
911
        }
912
913

        QString unistr(disstrU, p);
914
915

        // for XIM on the spot input style
916
        _isIMEdit = _isIMSel = false;
917

918
        if ( _imStartLine == y ) 
Robert Knight's avatar
   
Robert Knight committed
919
        {
920
921
922
          if (  ( _imStart < _imEnd ) && 
                ( x >= _imStart-1 ) && 
                ( x + int( unistr.length() ) <= _imEnd ) 
Robert Knight's avatar
   
Robert Knight committed
923
             )
924
                _isIMEdit = true;
Robert Knight's avatar
   
Robert Knight committed
925

926
927
928
          if ( ( _imSelStart < _imSelEnd ) && 
               ( x >= _imStart-1 ) && 
               ( x + int( unistr.length() ) <= _imEnd ) 
Robert Knight's avatar
   
Robert Knight committed
929
             )
930
                _isIMSel = true;
Robert Knight's avatar
   
Robert Knight committed
931
	    }
932
        else if ( _imStartLine < y ) 
Robert Knight's avatar
   
Robert Knight committed
933
        {  // for word warp
934
935
          if ( _imStart < _imEnd )
            _isIMEdit = true;
936

937
938
          if (  _imSelStart < _imSelEnd )
            _isIMSel = true;
Robert Knight's avatar
   
Robert Knight committed
939
	    }
Stephan Kulow's avatar
Stephan Kulow committed
940

941
        bool save__fixedFont = _fixedFont;
942
        if (lineDraw)
943
           _fixedFont = false;
944
        if (doubleWidth)
945
           _fixedFont = false;
946

947
		updateLine = true;
948

949
		_fixedFont = save__fixedFont;
950
951
        x += len - 1;
      }
952
      
953
    }
954

955
	//both the top and bottom halves of double height _lines must always be redrawn
Robert Knight's avatar
   
Robert Knight committed
956
957
	//although both top and bottom halves contain the same characters, only 
    //the top one is actually 
958
	//drawn.
959
960
    if (_lineProperties.count() > y)
        updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT);
Robert Knight's avatar
   
Robert Knight committed
961

962
    // if the characters on the line are different in the old and the new _image
Robert Knight's avatar
   
Robert Knight committed
963
    // then this line must be repainted.    
964
965
    if (updateLine)
    {
966
        dirtyLineCount++;
Robert Knight's avatar
   
Robert Knight committed
967
968
969

        // add the area occupied by this line to the region which needs to be
        // repainted
970
971
972
973
        QRect dirtyRect = QRect( _bX+tLx , 
                                 _bY+tLy+_fontHeight*y , 
                                 _fontWidth * columnsToUpdate , 
                                 _fontHeight ); 	
974

975
        dirtyRegion |= dirtyRect;
976
    }
977

Waldo Bastian's avatar
Waldo Bastian committed
978
    dirtyMask--; // Set back
979

980
981
    // replace the line of characters in the old _image with the 
    // current line of the new _image 
982
    memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character));
983
  }
984

985
  // free the image from the screen window
986
  delete[] newimg;
987

988
  // debugging - display a count of the number of lines that will need 
Robert Knight's avatar
   
Robert Knight committed
989
  // to be repainted
990
  //qDebug() << "dirty line count = " << dirtyLineCount;
991

992
993
994
  // if the new _image is smaller than the previous _image, then ensure that the area
  // outside the new _image is cleared 
  if ( linesToUpdate < _usedLines )
995
  {
996
997
998
999
    dirtyRegion |= QRect(   _bX+tLx , 
                            _bY+tLy+_fontHeight*linesToUpdate , 
                            _fontWidth * this->_columns , 
                            _fontHeight * (_usedLines-linesToUpdate) );
1000
  }
1001
  _usedLines = linesToUpdate;
1002
  
1003
  if ( columnsToUpdate < _usedColumns )
1004
  {
1005
1006
1007
1008
    dirtyRegion |= QRect(   _bX+tLx+columnsToUpdate*_fontWidth , 
                            _bY+tLy , 
                            _fontWidth * (_usedColumns-columnsToUpdate) , 
                            _fontHeight * this->_lines );
1009
  }
1010
  _usedColumns = columnsToUpdate;
1011

1012
1013
  //qDebug() << "Expecting to update: " << dirtyRegion.boundingRect();

Robert Knight's avatar
   
Robert Knight committed
1014
  // update the parts of the display which have changed
1015
  update(dirtyRegion);
Stephan Kulow's avatar
Stephan Kulow committed
1016

1017
1018
  if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( BLINK_DELAY ); 
  if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; }
1019
1020
  delete[] dirtyMask;
  delete[] disstrU;
1021

1022
1023
1024
  showResizeNotification();
}

1025
void TerminalDisplay::showResizeNotification()