source: ogBrowser-Git/qtermwidget/src/TerminalDisplay.cpp @ ac45929

jenkinsmain
Last change on this file since ac45929 was 8ab4781, checked in by adelcastillo <adelcastillo@…>, 15 years ago

Cambiando a qmake de nuevo.

git-svn-id: https://opengnsys.es/svn/trunk@518 a21b9725-9963-47de-94b9-378ad31fedc9

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