File indexing completed on 2018-12-12 10:32:16

0001 /*
0002     This file is part of Konsole, a terminal emulator for KDE.
0003 
0004     Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
0005     Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
0006 
0007     This program is free software; you can redistribute it and/or modify
0008     it under the terms of the GNU General Public License as published by
0009     the Free Software Foundation; either version 2 of the License, or
0010     (at your option) any later version.
0011 
0012     This program is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015     GNU General Public License for more details.
0016 
0017     You should have received a copy of the GNU General Public License
0018     along with this program; if not, write to the Free Software
0019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0020     02110-1301  USA.
0021 */
0022 
0023 // Own
0024 #include "TerminalDisplay.h"
0025 
0026 // Config
0027 #include <config-konsole.h>
0028 
0029 // Qt
0030 #include <QApplication>
0031 #include <QtGui/QClipboard>
0032 #include <QtGui/QKeyEvent>
0033 #include <QtCore/QEvent>
0034 #include <QtCore/QFileInfo>
0035 #include <QGridLayout>
0036 #include <QAction>
0037 #include <QLabel>
0038 #include <QtGui/QPainter>
0039 #include <QtGui/QPixmap>
0040 #include <QScrollBar>
0041 #include <QStyle>
0042 #include <QtCore/QTimer>
0043 #include <QToolTip>
0044 #include <QtGui/QAccessible>
0045 
0046 // KDE
0047 #include <KShell>
0048 #include <KColorScheme>
0049 #include <KCursor>
0050 #include <KDebug>
0051 #include <KLocalizedString>
0052 #include <KNotification>
0053 #include <KGlobalSettings>
0054 #include <KIO/NetAccess>
0055 #if defined(HAVE_LIBKONQ)
0056     #include <konq_operations.h>
0057 #endif
0058 
0059 #include <KFileItem>
0060 #include <KMessageBox>
0061 
0062 // Konsole
0063 #include "Filter.h"
0064 #include "konsole_wcwidth.h"
0065 #include "TerminalCharacterDecoder.h"
0066 #include "Screen.h"
0067 #include "ScreenWindow.h"
0068 #include "LineFont.h"
0069 #include "SessionController.h"
0070 #include "ExtendedCharTable.h"
0071 #include "TerminalDisplayAccessible.h"
0072 #include "SessionManager.h"
0073 #include "Session.h"
0074 
0075 using namespace Konsole;
0076 
0077 #ifndef loc
0078 #define loc(X,Y) ((Y)*_columns+(X))
0079 #endif
0080 
0081 #define REPCHAR   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
0082     "abcdefgjijklmnopqrstuvwxyz" \
0083     "0123456789./+@"
0084 
0085 // we use this to force QPainter to display text in LTR mode
0086 // more information can be found in: http://unicode.org/reports/tr9/
0087 const QChar LTR_OVERRIDE_CHAR(0x202D);
0088 
0089 /* ------------------------------------------------------------------------- */
0090 /*                                                                           */
0091 /*                                Colors                                     */
0092 /*                                                                           */
0093 /* ------------------------------------------------------------------------- */
0094 
0095 /* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb)
0096 
0097    Code        0       1       2       3       4       5       6       7
0098    ----------- ------- ------- ------- ------- ------- ------- ------- -------
0099    ANSI  (bgr) Black   Red     Green   Yellow  Blue    Magenta Cyan    White
0100    IBMPC (rgb) Black   Blue    Green   Cyan    Red     Magenta Yellow  White
0101 */
0102 
0103 ScreenWindow* TerminalDisplay::screenWindow() const
0104 {
0105     return _screenWindow;
0106 }
0107 void TerminalDisplay::setScreenWindow(ScreenWindow* window)
0108 {
0109     // disconnect existing screen window if any
0110     if (_screenWindow) {
0111         disconnect(_screenWindow , 0 , this , 0);
0112     }
0113 
0114     _screenWindow = window;
0115 
0116     if (_screenWindow) {
0117         connect(_screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()));
0118         connect(_screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()));
0119         connect(_screenWindow , SIGNAL(currentResultLineChanged()) , this , SLOT(updateImage()));
0120         _screenWindow->setWindowLines(_lines);
0121     }
0122 }
0123 
0124 const ColorEntry* TerminalDisplay::colorTable() const
0125 {
0126     return _colorTable;
0127 }
0128 void TerminalDisplay::setBackgroundColor(const QColor& color)
0129 {
0130     _colorTable[DEFAULT_BACK_COLOR].color = color;
0131 
0132     QPalette p = palette();
0133     p.setColor(backgroundRole(), color);
0134     setPalette(p);
0135 
0136     // Avoid propagating the palette change to the scroll bar
0137     _scrollBar->setPalette(QApplication::palette());
0138 
0139     update();
0140 }
0141 QColor TerminalDisplay::getBackgroundColor() const
0142 {
0143     QPalette p = palette();
0144     return p.color(backgroundRole());
0145 }
0146 void TerminalDisplay::setForegroundColor(const QColor& color)
0147 {
0148     _colorTable[DEFAULT_FORE_COLOR].color = color;
0149 
0150     update();
0151 }
0152 void TerminalDisplay::setColorTable(const ColorEntry table[])
0153 {
0154     for (int i = 0; i < TABLE_COLORS; i++)
0155         _colorTable[i] = table[i];
0156 
0157     setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color);
0158 }
0159 
0160 /* ------------------------------------------------------------------------- */
0161 /*                                                                           */
0162 /*                                   Font                                    */
0163 /*                                                                           */
0164 /* ------------------------------------------------------------------------- */
0165 
0166 static inline bool isLineCharString(const QString& string)
0167 {
0168     if (string.length() == 0)
0169         return false;
0170 
0171     return isSupportedLineChar(string.at(0).unicode());
0172 }
0173 
0174 void TerminalDisplay::fontChange(const QFont&)
0175 {
0176     QFontMetrics fm(font());
0177     _fontHeight = fm.height() + _lineSpacing;
0178 
0179     // waba TerminalDisplay 1.123:
0180     // "Base character width on widest ASCII character. This prevents too wide
0181     //  characters in the presence of double wide (e.g. Japanese) characters."
0182     // Get the width from representative normal width characters
0183     _fontWidth = qRound((static_cast<double>(fm.width(REPCHAR)) / static_cast<double>(qstrlen(REPCHAR))));
0184 
0185     _fixedFont = true;
0186 
0187     const int fw = fm.width(REPCHAR[0]);
0188     for (unsigned int i = 1; i < qstrlen(REPCHAR); i++) {
0189         if (fw != fm.width(REPCHAR[i])) {
0190             _fixedFont = false;
0191             break;
0192         }
0193     }
0194 
0195     if (_fontWidth < 1)
0196         _fontWidth = 1;
0197 
0198     _fontAscent = fm.ascent();
0199 
0200     emit changedFontMetricSignal(_fontHeight, _fontWidth);
0201     propagateSize();
0202     update();
0203 }
0204 
0205 void TerminalDisplay::setVTFont(const QFont& f)
0206 {
0207     QFont font = f;
0208 
0209     QFontMetrics metrics(font);
0210 
0211     if (!QFontInfo(font).fixedPitch()) {
0212         kWarning() << "Using an unsupported variable-width font in the terminal.  This may produce display errors.";
0213     }
0214 
0215     if (metrics.height() < height() && metrics.maxWidth() < width()) {
0216         // hint that text should be drawn without anti-aliasing.
0217         // depending on the user's font configuration, this may not be respected
0218         if (!_antialiasText)
0219             font.setStyleStrategy(QFont::NoAntialias);
0220 
0221         // experimental optimization.  Konsole assumes that the terminal is using a
0222         // mono-spaced font, in which case kerning information should have an effect.
0223         // Disabling kerning saves some computation when rendering text.
0224         font.setKerning(false);
0225 
0226         // Konsole cannot handle non-integer font metrics
0227         font.setStyleStrategy(QFont::StyleStrategy(font.styleStrategy() | QFont::ForceIntegerMetrics));
0228 
0229         QWidget::setFont(font);
0230         fontChange(font);
0231     }
0232 }
0233 
0234 void TerminalDisplay::setFont(const QFont &)
0235 {
0236     // ignore font change request if not coming from konsole itself
0237 }
0238 
0239 void TerminalDisplay::increaseFontSize()
0240 {
0241     QFont font = getVTFont();
0242     font.setPointSizeF(font.pointSizeF() + 1);
0243     setVTFont(font);
0244 }
0245 
0246 void TerminalDisplay::decreaseFontSize()
0247 {
0248     const qreal MinimumFontSize = 6;
0249 
0250     QFont font = getVTFont();
0251     font.setPointSizeF(qMax(font.pointSizeF() - 1, MinimumFontSize));
0252     setVTFont(font);
0253 }
0254 
0255 uint TerminalDisplay::lineSpacing() const
0256 {
0257     return _lineSpacing;
0258 }
0259 
0260 void TerminalDisplay::setLineSpacing(uint i)
0261 {
0262     _lineSpacing = i;
0263     setVTFont(font()); // Trigger an update.
0264 }
0265 
0266 
0267 /* ------------------------------------------------------------------------- */
0268 /*                                                                           */
0269 /*                         Accessibility                                     */
0270 /*                                                                           */
0271 /* ------------------------------------------------------------------------- */
0272 
0273 namespace Konsole
0274 {
0275 /**
0276  * This function installs the factory function which lets Qt instantiate the QAccessibleInterface
0277  * for the TerminalDisplay.
0278  */
0279 QAccessibleInterface* accessibleInterfaceFactory(const QString &key, QObject *object)
0280 {
0281     Q_UNUSED(key)
0282     if (TerminalDisplay *display = qobject_cast<TerminalDisplay*>(object))
0283         return new TerminalDisplayAccessible(display);
0284     return 0;
0285 }
0286 }
0287 
0288 /* ------------------------------------------------------------------------- */
0289 /*                                                                           */
0290 /*                         Constructor / Destructor                          */
0291 /*                                                                           */
0292 /* ------------------------------------------------------------------------- */
0293 
0294 TerminalDisplay::TerminalDisplay(QWidget* parent)
0295     : QWidget(parent)
0296     , _screenWindow(0)
0297     , _bellMasked(false)
0298     , _gridLayout(0)
0299     , _fontHeight(1)
0300     , _fontWidth(1)
0301     , _fontAscent(1)
0302     , _boldIntense(true)
0303     , _lines(1)
0304     , _columns(1)
0305     , _usedLines(1)
0306     , _usedColumns(1)
0307     , _image(0)
0308     , _randomSeed(0)
0309     , _resizing(false)
0310     , _showTerminalSizeHint(true)
0311     , _bidiEnabled(false)
0312     , _actSel(0)
0313     , _wordSelectionMode(false)
0314     , _lineSelectionMode(false)
0315     , _preserveLineBreaks(false)
0316     , _columnSelectionMode(false)
0317     , _autoCopySelectedText(false)
0318     , _middleClickPasteMode(Enum::PasteFromX11Selection)
0319     , _scrollbarLocation(Enum::ScrollBarRight)
0320     , _scrollFullPage(false)
0321     , _wordCharacters(":@-./_~")
0322     , _bellMode(Enum::NotifyBell)
0323     , _allowBlinkingText(true)
0324     , _allowBlinkingCursor(false)
0325     , _textBlinking(false)
0326     , _cursorBlinking(false)
0327     , _hasTextBlinker(false)
0328     , _underlineLinks(true)
0329     , _openLinksByDirectClick(false)
0330     , _ctrlRequiredForDrag(true)
0331     , _tripleClickMode(Enum::SelectWholeLine)
0332     , _possibleTripleClick(false)
0333     , _resizeWidget(0)
0334     , _resizeTimer(0)
0335     , _flowControlWarningEnabled(false)
0336     , _outputSuspendedLabel(0)
0337     , _lineSpacing(0)
0338     , _blendColor(qRgba(0, 0, 0, 0xff))
0339     , _filterChain(new TerminalImageFilterChain())
0340     , _cursorShape(Enum::BlockCursor)
0341     , _antialiasText(true)
0342     , _printerFriendly(false)
0343     , _sessionController(0)
0344     , _trimTrailingSpaces(false)
0345     , _margin(1)
0346     , _centerContents(false)
0347     , _opacity(1.0)
0348 {
0349     // terminal applications are not designed with Right-To-Left in mind,
0350     // so the layout is forced to Left-To-Right
0351     setLayoutDirection(Qt::LeftToRight);
0352 
0353     _contentRect = QRect(_margin, _margin, 1, 1);
0354 
0355     // create scroll bar for scrolling output up and down
0356     _scrollBar = new QScrollBar(this);
0357     // set the scroll bar's slider to occupy the whole area of the scroll bar initially
0358     setScroll(0, 0);
0359     _scrollBar->setCursor(Qt::ArrowCursor);
0360     connect(_scrollBar, SIGNAL(valueChanged(int)),
0361             this, SLOT(scrollBarPositionChanged(int)));
0362     connect(_scrollBar, SIGNAL(sliderMoved(int)),
0363             this, SLOT(viewScrolledByUser()));
0364 
0365     // setup timers for blinking text
0366     _blinkTextTimer = new QTimer(this);
0367     _blinkTextTimer->setInterval(TEXT_BLINK_DELAY);
0368     connect(_blinkTextTimer, SIGNAL(timeout()), this, SLOT(blinkTextEvent()));
0369 
0370     // setup timers for blinking cursor
0371     _blinkCursorTimer = new QTimer(this);
0372     _blinkCursorTimer->setInterval(QApplication::cursorFlashTime() / 2);
0373     connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent()));
0374 
0375     // hide mouse cursor on keystroke or idle
0376     KCursor::setAutoHideCursor(this, true);
0377     setMouseTracking(true);
0378 
0379     setUsesMouse(true);
0380     setBracketedPasteMode(false);
0381 
0382     setColorTable(ColorScheme::defaultTable);
0383 
0384     // Enable drag and drop support
0385     setAcceptDrops(true);
0386     _dragInfo.state = diNone;
0387 
0388     setFocusPolicy(Qt::WheelFocus);
0389 
0390     // enable input method support
0391     setAttribute(Qt::WA_InputMethodEnabled, true);
0392 
0393     // this is an important optimization, it tells Qt
0394     // that TerminalDisplay will handle repainting its entire area.
0395     setAttribute(Qt::WA_OpaquePaintEvent);
0396 
0397     _gridLayout = new QGridLayout(this);
0398     _gridLayout->setContentsMargins(0, 0, 0, 0);
0399 
0400     setLayout(_gridLayout);
0401 
0402     new AutoScrollHandler(this);
0403 
0404 
0405 #ifndef QT_NO_ACCESSIBILITY
0406     QAccessible::installFactory(Konsole::accessibleInterfaceFactory);
0407 #endif
0408 }
0409 
0410 TerminalDisplay::~TerminalDisplay()
0411 {
0412     disconnect(_blinkTextTimer);
0413     disconnect(_blinkCursorTimer);
0414 
0415     delete[] _image;
0416 
0417     delete _gridLayout;
0418     delete _outputSuspendedLabel;
0419     delete _filterChain;
0420 }
0421 
0422 /* ------------------------------------------------------------------------- */
0423 /*                                                                           */
0424 /*                             Display Operations                            */
0425 /*                                                                           */
0426 /* ------------------------------------------------------------------------- */
0427 
0428 /**
0429  A table for emulating the simple (single width) unicode drawing chars.
0430  It represents the 250x - 257x glyphs. If it's zero, we can't use it.
0431  if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered
0432  0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit.
0433 
0434  Then, the pixels basically have the following interpretation:
0435  _|||_
0436  -...-
0437  -...-
0438  -...-
0439  _|||_
0440 
0441 where _ = none
0442       | = vertical line.
0443       - = horizontal line.
0444  */
0445 
0446 enum LineEncode {
0447     TopL  = (1 << 1),
0448     TopC  = (1 << 2),
0449     TopR  = (1 << 3),
0450 
0451     LeftT = (1 << 5),
0452     Int11 = (1 << 6),
0453     Int12 = (1 << 7),
0454     Int13 = (1 << 8),
0455     RightT = (1 << 9),
0456 
0457     LeftC = (1 << 10),
0458     Int21 = (1 << 11),
0459     Int22 = (1 << 12),
0460     Int23 = (1 << 13),
0461     RightC = (1 << 14),
0462 
0463     LeftB = (1 << 15),
0464     Int31 = (1 << 16),
0465     Int32 = (1 << 17),
0466     Int33 = (1 << 18),
0467     RightB = (1 << 19),
0468 
0469     BotL  = (1 << 21),
0470     BotC  = (1 << 22),
0471     BotR  = (1 << 23)
0472 };
0473 
0474 static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code)
0475 {
0476     //Calculate cell midpoints, end points.
0477     const int cx = x + w / 2;
0478     const int cy = y + h / 2;
0479     const int ex = x + w - 1;
0480     const int ey = y + h - 1;
0481 
0482     const quint32 toDraw = LineChars[code];
0483 
0484     //Top _lines:
0485     if (toDraw & TopL)
0486         paint.drawLine(cx - 1, y, cx - 1, cy - 2);
0487     if (toDraw & TopC)
0488         paint.drawLine(cx, y, cx, cy - 2);
0489     if (toDraw & TopR)
0490         paint.drawLine(cx + 1, y, cx + 1, cy - 2);
0491 
0492     //Bot _lines:
0493     if (toDraw & BotL)
0494         paint.drawLine(cx - 1, cy + 2, cx - 1, ey);
0495     if (toDraw & BotC)
0496         paint.drawLine(cx, cy + 2, cx, ey);
0497     if (toDraw & BotR)
0498         paint.drawLine(cx + 1, cy + 2, cx + 1, ey);
0499 
0500     //Left _lines:
0501     if (toDraw & LeftT)
0502         paint.drawLine(x, cy - 1, cx - 2, cy - 1);
0503     if (toDraw & LeftC)
0504         paint.drawLine(x, cy, cx - 2, cy);
0505     if (toDraw & LeftB)
0506         paint.drawLine(x, cy + 1, cx - 2, cy + 1);
0507 
0508     //Right _lines:
0509     if (toDraw & RightT)
0510         paint.drawLine(cx + 2, cy - 1, ex, cy - 1);
0511     if (toDraw & RightC)
0512         paint.drawLine(cx + 2, cy, ex, cy);
0513     if (toDraw & RightB)
0514         paint.drawLine(cx + 2, cy + 1, ex, cy + 1);
0515 
0516     //Intersection points.
0517     if (toDraw & Int11)
0518         paint.drawPoint(cx - 1, cy - 1);
0519     if (toDraw & Int12)
0520         paint.drawPoint(cx, cy - 1);
0521     if (toDraw & Int13)
0522         paint.drawPoint(cx + 1, cy - 1);
0523 
0524     if (toDraw & Int21)
0525         paint.drawPoint(cx - 1, cy);
0526     if (toDraw & Int22)
0527         paint.drawPoint(cx, cy);
0528     if (toDraw & Int23)
0529         paint.drawPoint(cx + 1, cy);
0530 
0531     if (toDraw & Int31)
0532         paint.drawPoint(cx - 1, cy + 1);
0533     if (toDraw & Int32)
0534         paint.drawPoint(cx, cy + 1);
0535     if (toDraw & Int33)
0536         paint.drawPoint(cx + 1, cy + 1);
0537 }
0538 
0539 void TerminalDisplay::drawLineCharString(QPainter& painter, int x, int y, const QString& str,
0540         const Character* attributes)
0541 {
0542     const QPen& originalPen = painter.pen();
0543 
0544     if ((attributes->rendition & RE_BOLD) && _boldIntense) {
0545         QPen boldPen(originalPen);
0546         boldPen.setWidth(3);
0547         painter.setPen(boldPen);
0548     }
0549 
0550     for (int i = 0 ; i < str.length(); i++) {
0551         const uchar code = str[i].cell();
0552         if (LineChars[code])
0553             drawLineChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
0554     }
0555 
0556     painter.setPen(originalPen);
0557 }
0558 
0559 void TerminalDisplay::setKeyboardCursorShape(Enum::CursorShapeEnum shape)
0560 {
0561     _cursorShape = shape;
0562 }
0563 Enum::CursorShapeEnum TerminalDisplay::keyboardCursorShape() const
0564 {
0565     return _cursorShape;
0566 }
0567 void TerminalDisplay::setKeyboardCursorColor(const QColor& color)
0568 {
0569     _cursorColor = color;
0570 }
0571 QColor TerminalDisplay::keyboardCursorColor() const
0572 {
0573     return _cursorColor;
0574 }
0575 
0576 void TerminalDisplay::setOpacity(qreal opacity)
0577 {
0578     QColor color(_blendColor);
0579     color.setAlphaF(opacity);
0580     _opacity = opacity;
0581 
0582     // enable automatic background filling to prevent the display
0583     // flickering if there is no transparency
0584     /*if ( color.alpha() == 255 )
0585     {
0586         setAutoFillBackground(true);
0587     }
0588     else
0589     {
0590         setAutoFillBackground(false);
0591     }*/
0592 
0593     _blendColor = color.rgba();
0594 }
0595 
0596 void TerminalDisplay::setWallpaper(ColorSchemeWallpaper::Ptr p)
0597 {
0598     _wallpaper = p;
0599 }
0600 
0601 void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting)
0602 {
0603     // the area of the widget showing the contents of the terminal display is drawn
0604     // using the background color from the color scheme set with setColorTable()
0605     //
0606     // the area of the widget behind the scroll-bar is drawn using the background
0607     // brush from the scroll-bar's palette, to give the effect of the scroll-bar
0608     // being outside of the terminal display and visual consistency with other KDE
0609     // applications.
0610     //
0611     QRect scrollBarArea = _scrollBar->isVisible() ?
0612                           rect.intersected(_scrollBar->geometry()) :
0613                           QRect();
0614     QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea);
0615     QRect contentsRect = contentsRegion.boundingRect();
0616 
0617     if (useOpacitySetting && !_wallpaper->isNull() &&
0618             _wallpaper->draw(painter, contentsRect, _opacity)) {
0619     } else if (qAlpha(_blendColor) < 0xff && useOpacitySetting) {
0620 #if defined(Q_OS_MAC)
0621         // TODO - On MacOS, using CompositionMode doesn't work.  Altering the
0622         //        transparency in the color scheme alters the brightness.
0623         painter.fillRect(contentsRect, backgroundColor);
0624 #else
0625         QColor color(backgroundColor);
0626         color.setAlpha(qAlpha(_blendColor));
0627 
0628         painter.save();
0629         painter.setCompositionMode(QPainter::CompositionMode_Source);
0630         painter.fillRect(contentsRect, color);
0631         painter.restore();
0632 #endif
0633     } else {
0634         painter.fillRect(contentsRect, backgroundColor);
0635     }
0636 
0637     painter.fillRect(scrollBarArea, _scrollBar->palette().background());
0638 }
0639 
0640 void TerminalDisplay::drawCursor(QPainter& painter,
0641                                  const QRect& rect,
0642                                  const QColor& foregroundColor,
0643                                  const QColor& /*backgroundColor*/,
0644                                  bool& invertCharacterColor)
0645 {
0646     // don't draw cursor which is currently blinking
0647     if (_cursorBlinking)
0648         return;
0649 
0650     QRect cursorRect = rect;
0651     cursorRect.setHeight(_fontHeight - _lineSpacing - 1);
0652 
0653     QColor cursorColor = _cursorColor.isValid() ? _cursorColor : foregroundColor;
0654     painter.setPen(cursorColor);
0655 
0656     if (_cursorShape == Enum::BlockCursor) {
0657         // draw the cursor outline, adjusting the area so that
0658         // it is draw entirely inside 'rect'
0659         int penWidth = qMax(1, painter.pen().width());
0660         painter.drawRect(cursorRect.adjusted(penWidth / 2,
0661                                              penWidth / 2,
0662                                              - penWidth / 2 - penWidth % 2,
0663                                              - penWidth / 2 - penWidth % 2));
0664 
0665         // draw the cursor body only when the widget has focus
0666         if (hasFocus()) {
0667             painter.fillRect(cursorRect, cursorColor);
0668 
0669             if (!_cursorColor.isValid()) {
0670                 // invert the color used to draw the text to ensure that the character at
0671                 // the cursor position is readable
0672                 invertCharacterColor = true;
0673             }
0674         }
0675     } else if (_cursorShape == Enum::UnderlineCursor) {
0676         painter.drawLine(cursorRect.left(),
0677                          cursorRect.bottom(),
0678                          cursorRect.right(),
0679                          cursorRect.bottom());
0680 
0681     } else if (_cursorShape == Enum::IBeamCursor) {
0682         painter.drawLine(cursorRect.left(),
0683                          cursorRect.top(),
0684                          cursorRect.left(),
0685                          cursorRect.bottom());
0686     }
0687 }
0688 
0689 void TerminalDisplay::drawCharacters(QPainter& painter,
0690                                      const QRect& rect,
0691                                      const QString& text,
0692                                      const Character* style,
0693                                      bool invertCharacterColor)
0694 {
0695     // don't draw text which is currently blinking
0696     if (_textBlinking && (style->rendition & RE_BLINK))
0697         return;
0698 
0699     // setup bold and underline
0700     bool useBold;
0701     ColorEntry::FontWeight weight = style->fontWeight(_colorTable);
0702     if (weight == ColorEntry::UseCurrentFormat)
0703         useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold();
0704     else
0705         useBold = (weight == ColorEntry::Bold) ? true : false;
0706     const bool useUnderline = style->rendition & RE_UNDERLINE || font().underline();
0707     const bool useItalic = style->rendition & RE_ITALIC || font().italic();
0708 
0709     QFont font = painter.font();
0710     if (font.bold() != useBold
0711             || font.underline() != useUnderline
0712             || font.italic() != useItalic) {
0713         font.setBold(useBold);
0714         font.setUnderline(useUnderline);
0715         font.setItalic(useItalic);
0716         painter.setFont(font);
0717     }
0718 
0719     // setup pen
0720     const CharacterColor& textColor = (invertCharacterColor ? style->backgroundColor : style->foregroundColor);
0721     const QColor color = textColor.color(_colorTable);
0722     QPen pen = painter.pen();
0723     if (pen.color() != color) {
0724         pen.setColor(color);
0725         painter.setPen(color);
0726     }
0727 
0728     // draw text
0729     if (isLineCharString(text)) {
0730         drawLineCharString(painter, rect.x(), rect.y(), text, style);
0731     } else {
0732         // Force using LTR as the document layout for the terminal area, because
0733         // there is no use cases for RTL emulator and RTL terminal application.
0734         //
0735         // This still allows RTL characters to be rendered in the RTL way.
0736         painter.setLayoutDirection(Qt::LeftToRight);
0737 
0738         // the drawText(rect,flags,string) overload is used here with null flags
0739         // instead of drawText(rect,string) because the (rect,string) overload causes
0740         // the application's default layout direction to be used instead of
0741         // the widget-specific layout direction, which should always be
0742         // Qt::LeftToRight for this widget
0743         //
0744         // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2
0745         if (_bidiEnabled) {
0746             painter.drawText(rect, 0, text);
0747         } else {
0748             // See bug 280896 for more info
0749 #if QT_VERSION >= 0x040800
0750             painter.drawText(rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + text);
0751 #else
0752             painter.drawText(rect, 0, LTR_OVERRIDE_CHAR + text);
0753 #endif
0754         }
0755     }
0756 }
0757 
0758 void TerminalDisplay::drawTextFragment(QPainter& painter ,
0759                                        const QRect& rect,
0760                                        const QString& text,
0761                                        const Character* style)
0762 {
0763     painter.save();
0764 
0765     // setup painter
0766     const QColor foregroundColor = style->foregroundColor.color(_colorTable);
0767     const QColor backgroundColor = style->backgroundColor.color(_colorTable);
0768 
0769     // draw background if different from the display's background color
0770     if (backgroundColor != palette().background().color())
0771         drawBackground(painter, rect, backgroundColor,
0772                        false /* do not use transparency */);
0773 
0774     // draw cursor shape if the current character is the cursor
0775     // this may alter the foreground and background colors
0776     bool invertCharacterColor = false;
0777     if (style->rendition & RE_CURSOR)
0778         drawCursor(painter, rect, foregroundColor, backgroundColor, invertCharacterColor);
0779 
0780     // draw text
0781     drawCharacters(painter, rect, text, style, invertCharacterColor);
0782 
0783     painter.restore();
0784 }
0785 
0786 void TerminalDisplay::drawPrinterFriendlyTextFragment(QPainter& painter,
0787         const QRect& rect,
0788         const QString& text,
0789         const Character* style)
0790 {
0791     painter.save();
0792 
0793     // Set the colors used to draw to black foreground and white
0794     // background for printer friendly output when printing
0795     Character print_style = *style;
0796     print_style.foregroundColor = CharacterColor(COLOR_SPACE_RGB, 0x00000000);
0797     print_style.backgroundColor = CharacterColor(COLOR_SPACE_RGB, 0xFFFFFFFF);
0798 
0799     // draw text
0800     drawCharacters(painter, rect, text, &print_style, false);
0801 
0802     painter.restore();
0803 }
0804 
0805 void TerminalDisplay::setRandomSeed(uint randomSeed)
0806 {
0807     _randomSeed = randomSeed;
0808 }
0809 uint TerminalDisplay::randomSeed() const
0810 {
0811     return _randomSeed;
0812 }
0813 
0814 // scrolls the image by 'lines', down if lines > 0 or up otherwise.
0815 //
0816 // the terminal emulation keeps track of the scrolling of the character
0817 // image as it receives input, and when the view is updated, it calls scrollImage()
0818 // with the final scroll amount.  this improves performance because scrolling the
0819 // display is much cheaper than re-rendering all the text for the
0820 // part of the image which has moved up or down.
0821 // Instead only new lines have to be drawn
0822 void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion)
0823 {
0824     // if the flow control warning is enabled this will interfere with the
0825     // scrolling optimizations and cause artifacts.  the simple solution here
0826     // is to just disable the optimization whilst it is visible
0827     if (_outputSuspendedLabel && _outputSuspendedLabel->isVisible())
0828         return;
0829 
0830     // constrain the region to the display
0831     // the bottom of the region is capped to the number of lines in the display's
0832     // internal image - 2, so that the height of 'region' is strictly less
0833     // than the height of the internal image.
0834     QRect region = screenWindowRegion;
0835     region.setBottom(qMin(region.bottom(), this->_lines - 2));
0836 
0837     // return if there is nothing to do
0838     if (lines == 0
0839             || _image == 0
0840             || !region.isValid()
0841             || (region.top() + abs(lines)) >= region.bottom()
0842             || this->_lines <= region.height()) return;
0843 
0844     // hide terminal size label to prevent it being scrolled
0845     if (_resizeWidget && _resizeWidget->isVisible())
0846         _resizeWidget->hide();
0847 
0848     // Note:  With Qt 4.4 the left edge of the scrolled area must be at 0
0849     // to get the correct (newly exposed) part of the widget repainted.
0850     //
0851     // The right edge must be before the left edge of the scroll bar to
0852     // avoid triggering a repaint of the entire widget, the distance is
0853     // given by SCROLLBAR_CONTENT_GAP
0854     //
0855     // Set the QT_FLUSH_PAINT environment variable to '1' before starting the
0856     // application to monitor repainting.
0857     //
0858     const int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->width();
0859     const int SCROLLBAR_CONTENT_GAP = 1;
0860     QRect scrollRect;
0861     if (_scrollbarLocation == Enum::ScrollBarLeft) {
0862         scrollRect.setLeft(scrollBarWidth + SCROLLBAR_CONTENT_GAP);
0863         scrollRect.setRight(width());
0864     } else {
0865         scrollRect.setLeft(0);
0866         scrollRect.setRight(width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP);
0867     }
0868     void* firstCharPos = &_image[ region.top() * this->_columns ];
0869     void* lastCharPos = &_image[(region.top() + abs(lines)) * this->_columns ];
0870 
0871     const int top = _contentRect.top() + (region.top() * _fontHeight);
0872     const int linesToMove = region.height() - abs(lines);
0873     const int bytesToMove = linesToMove * this->_columns * sizeof(Character);
0874 
0875     Q_ASSERT(linesToMove > 0);
0876     Q_ASSERT(bytesToMove > 0);
0877 
0878     //scroll internal image
0879     if (lines > 0) {
0880         // check that the memory areas that we are going to move are valid
0881         Q_ASSERT((char*)lastCharPos + bytesToMove <
0882                  (char*)(_image + (this->_lines * this->_columns)));
0883 
0884         Q_ASSERT((lines * this->_columns) < _imageSize);
0885 
0886         //scroll internal image down
0887         memmove(firstCharPos , lastCharPos , bytesToMove);
0888 
0889         //set region of display to scroll
0890         scrollRect.setTop(top);
0891     } else {
0892         // check that the memory areas that we are going to move are valid
0893         Q_ASSERT((char*)firstCharPos + bytesToMove <
0894                  (char*)(_image + (this->_lines * this->_columns)));
0895 
0896         //scroll internal image up
0897         memmove(lastCharPos , firstCharPos , bytesToMove);
0898 
0899         //set region of the display to scroll
0900         scrollRect.setTop(top + abs(lines) * _fontHeight);
0901     }
0902     scrollRect.setHeight(linesToMove * _fontHeight);
0903 
0904     Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty());
0905 
0906     //scroll the display vertically to match internal _image
0907     scroll(0 , _fontHeight * (-lines) , scrollRect);
0908 }
0909 
0910 QRegion TerminalDisplay::hotSpotRegion() const
0911 {
0912     QRegion region;
0913     foreach(Filter::HotSpot * hotSpot , _filterChain->hotSpots()) {
0914         QRect r;
0915         if (hotSpot->startLine() == hotSpot->endLine()) {
0916             r.setLeft(hotSpot->startColumn());
0917             r.setTop(hotSpot->startLine());
0918             r.setRight(hotSpot->endColumn());
0919             r.setBottom(hotSpot->endLine());
0920             region |= imageToWidget(r);
0921         } else {
0922             r.setLeft(hotSpot->startColumn());
0923             r.setTop(hotSpot->startLine());
0924             r.setRight(_columns);
0925             r.setBottom(hotSpot->startLine());
0926             region |= imageToWidget(r);
0927             for (int line = hotSpot->startLine() + 1 ; line < hotSpot->endLine() ; line++) {
0928                 r.setLeft(0);
0929                 r.setTop(line);
0930                 r.setRight(_columns);
0931                 r.setBottom(line);
0932                 region |= imageToWidget(r);
0933             }
0934             r.setLeft(0);
0935             r.setTop(hotSpot->endLine());
0936             r.setRight(hotSpot->endColumn());
0937             r.setBottom(hotSpot->endLine());
0938             region |= imageToWidget(r);
0939         }
0940     }
0941     return region;
0942 }
0943 
0944 void TerminalDisplay::processFilters()
0945 {
0946     if (!_screenWindow)
0947         return;
0948 
0949     QRegion preUpdateHotSpots = hotSpotRegion();
0950 
0951     // use _screenWindow->getImage() here rather than _image because
0952     // other classes may call processFilters() when this display's
0953     // ScreenWindow emits a scrolled() signal - which will happen before
0954     // updateImage() is called on the display and therefore _image is
0955     // out of date at this point
0956     _filterChain->setImage(_screenWindow->getImage(),
0957                            _screenWindow->windowLines(),
0958                            _screenWindow->windowColumns(),
0959                            _screenWindow->getLineProperties());
0960     _filterChain->process();
0961 
0962     QRegion postUpdateHotSpots = hotSpotRegion();
0963 
0964     update(preUpdateHotSpots | postUpdateHotSpots);
0965 }
0966 
0967 void TerminalDisplay::updateImage()
0968 {
0969     if (!_screenWindow)
0970         return;
0971 
0972     // optimization - scroll the existing image where possible and
0973     // avoid expensive text drawing for parts of the image that
0974     // can simply be moved up or down
0975     if (_wallpaper->isNull()) {
0976         scrollImage(_screenWindow->scrollCount() ,
0977                     _screenWindow->scrollRegion());
0978         _screenWindow->resetScrollCount();
0979     }
0980 
0981     if (!_image) {
0982         // Create _image.
0983         // The emitted changedContentSizeSignal also leads to getImage being recreated, so do this first.
0984         updateImageSize();
0985     }
0986 
0987     Character* const newimg = _screenWindow->getImage();
0988     const int lines = _screenWindow->windowLines();
0989     const int columns = _screenWindow->windowColumns();
0990 
0991     setScroll(_screenWindow->currentLine() , _screenWindow->lineCount());
0992 
0993     Q_ASSERT(this->_usedLines <= this->_lines);
0994     Q_ASSERT(this->_usedColumns <= this->_columns);
0995 
0996     int y, x, len;
0997 
0998     const QPoint tL  = contentsRect().topLeft();
0999     const int    tLx = tL.x();
1000     const int    tLy = tL.y();
1001     _hasTextBlinker = false;
1002 
1003     CharacterColor cf;       // undefined
1004 
1005     const int linesToUpdate = qMin(this->_lines, qMax(0, lines));
1006     const int columnsToUpdate = qMin(this->_columns, qMax(0, columns));
1007 
1008     char* dirtyMask = new char[columnsToUpdate + 2];
1009     QRegion dirtyRegion;
1010 
1011     // debugging variable, this records the number of lines that are found to
1012     // be 'dirty' ( ie. have changed from the old _image to the new _image ) and
1013     // which therefore need to be repainted
1014     int dirtyLineCount = 0;
1015 
1016     for (y = 0; y < linesToUpdate; ++y) {
1017         const Character* currentLine = &_image[y * this->_columns];
1018         const Character* const newLine = &newimg[y * columns];
1019 
1020         bool updateLine = false;
1021 
1022         // The dirty mask indicates which characters need repainting. We also
1023         // mark surrounding neighbors dirty, in case the character exceeds
1024         // its cell boundaries
1025         memset(dirtyMask, 0, columnsToUpdate + 2);
1026 
1027         for (x = 0 ; x < columnsToUpdate ; ++x) {
1028             if (newLine[x] != currentLine[x]) {
1029                 dirtyMask[x] = true;
1030             }
1031         }
1032 
1033         if (!_resizing) // not while _resizing, we're expecting a paintEvent
1034             for (x = 0; x < columnsToUpdate; ++x) {
1035                 _hasTextBlinker |= (newLine[x].rendition & RE_BLINK);
1036 
1037                 // Start drawing if this character or the next one differs.
1038                 // We also take the next one into account to handle the situation
1039                 // where characters exceed their cell width.
1040                 if (dirtyMask[x]) {
1041                     if (!newLine[x + 0].character)
1042                         continue;
1043                     const bool lineDraw = newLine[x + 0].isLineChar();
1044                     const bool doubleWidth = (x + 1 == columnsToUpdate) ? false : (newLine[x + 1].character == 0);
1045                     const quint8 cr = newLine[x].rendition;
1046                     const CharacterColor clipboard = newLine[x].backgroundColor;
1047                     if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor;
1048                     const int lln = columnsToUpdate - x;
1049                     for (len = 1; len < lln; ++len) {
1050                         const Character& ch = newLine[x + len];
1051 
1052                         if (!ch.character)
1053                             continue; // Skip trailing part of multi-col chars.
1054 
1055                         const bool nextIsDoubleWidth = (x + len + 1 == columnsToUpdate) ? false : (newLine[x + len + 1].character == 0);
1056 
1057                         if (ch.foregroundColor != cf ||
1058                                 ch.backgroundColor != clipboard ||
1059                                 (ch.rendition & ~RE_EXTENDED_CHAR) != (cr & ~RE_EXTENDED_CHAR) ||
1060                                 !dirtyMask[x + len] ||
1061                                 ch.isLineChar() != lineDraw ||
1062                                 nextIsDoubleWidth != doubleWidth)
1063                             break;
1064                     }
1065 
1066                     const bool saveFixedFont = _fixedFont;
1067                     if (lineDraw)
1068                         _fixedFont = false;
1069                     if (doubleWidth)
1070                         _fixedFont = false;
1071 
1072                     updateLine = true;
1073 
1074                     _fixedFont = saveFixedFont;
1075                     x += len - 1;
1076                 }
1077             }
1078 
1079         //both the top and bottom halves of double height _lines must always be redrawn
1080         //although both top and bottom halves contain the same characters, only
1081         //the top one is actually
1082         //drawn.
1083         if (_lineProperties.count() > y)
1084             updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT);
1085 
1086         // if the characters on the line are different in the old and the new _image
1087         // then this line must be repainted.
1088         if (updateLine) {
1089             dirtyLineCount++;
1090 
1091             // add the area occupied by this line to the region which needs to be
1092             // repainted
1093             QRect dirtyRect = QRect(_contentRect.left() + tLx ,
1094                                     _contentRect.top() + tLy + _fontHeight * y ,
1095                                     _fontWidth * columnsToUpdate ,
1096                                     _fontHeight);
1097 
1098             dirtyRegion |= dirtyRect;
1099         }
1100 
1101         // replace the line of characters in the old _image with the
1102         // current line of the new _image
1103         memcpy((void*)currentLine, (const void*)newLine, columnsToUpdate * sizeof(Character));
1104     }
1105 
1106     // if the new _image is smaller than the previous _image, then ensure that the area
1107     // outside the new _image is cleared
1108     if (linesToUpdate < _usedLines) {
1109         dirtyRegion |= QRect(_contentRect.left() + tLx ,
1110                              _contentRect.top() + tLy + _fontHeight * linesToUpdate ,
1111                              _fontWidth * this->_columns ,
1112                              _fontHeight * (_usedLines - linesToUpdate));
1113     }
1114     _usedLines = linesToUpdate;
1115 
1116     if (columnsToUpdate < _usedColumns) {
1117         dirtyRegion |= QRect(_contentRect.left() + tLx + columnsToUpdate * _fontWidth ,
1118                              _contentRect.top() + tLy ,
1119                              _fontWidth * (_usedColumns - columnsToUpdate) ,
1120                              _fontHeight * this->_lines);
1121     }
1122     _usedColumns = columnsToUpdate;
1123 
1124     dirtyRegion |= _inputMethodData.previousPreeditRect;
1125 
1126     // update the parts of the display which have changed
1127     update(dirtyRegion);
1128 
1129     if (_allowBlinkingText && _hasTextBlinker && !_blinkTextTimer->isActive()) {
1130         _blinkTextTimer->start();
1131     }
1132     if (!_hasTextBlinker && _blinkTextTimer->isActive()) {
1133         _blinkTextTimer->stop();
1134         _textBlinking = false;
1135     }
1136     delete[] dirtyMask;
1137 
1138 #if QT_VERSION >= 0x040800 // added in Qt 4.8.0
1139 #ifndef QT_NO_ACCESSIBILITY
1140     QAccessible::updateAccessibility(this, 0, QAccessible::TextUpdated);
1141     QAccessible::updateAccessibility(this, 0, QAccessible::TextCaretMoved);
1142 #endif
1143 #endif
1144 }
1145 
1146 void TerminalDisplay::showResizeNotification()
1147 {
1148     if (_showTerminalSizeHint && isVisible()) {
1149         if (!_resizeWidget) {
1150             _resizeWidget = new QLabel(i18n("Size: XXX x XXX"), this);
1151             _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(i18n("Size: XXX x XXX")));
1152             _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height());
1153             _resizeWidget->setAlignment(Qt::AlignCenter);
1154 
1155             _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)");
1156 
1157             _resizeTimer = new QTimer(this);
1158             _resizeTimer->setInterval(SIZE_HINT_DURATION);
1159             _resizeTimer->setSingleShot(true);
1160             connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide()));
1161         }
1162         QString sizeStr = i18n("Size: %1 x %2", _columns, _lines);
1163         _resizeWidget->setText(sizeStr);
1164         _resizeWidget->move((width() - _resizeWidget->width()) / 2,
1165                             (height() - _resizeWidget->height()) / 2 + 20);
1166         _resizeWidget->show();
1167         _resizeTimer->start();
1168     }
1169 }
1170 
1171 void TerminalDisplay::paintEvent(QPaintEvent* pe)
1172 {
1173     QPainter paint(this);
1174 
1175     foreach(const QRect & rect, (pe->region() & contentsRect()).rects()) {
1176         drawBackground(paint, rect, palette().background().color(),
1177                        true /* use opacity setting */);
1178         drawContents(paint, rect);
1179     }
1180     drawCurrentResultRect(paint);
1181     drawInputMethodPreeditString(paint, preeditRect());
1182     paintFilters(paint);
1183 }
1184 
1185 void TerminalDisplay::printContent(QPainter& painter, bool friendly)
1186 {
1187     // Reinitialize the font with the printers paint device so the font
1188     // measurement calculations will be done correctly
1189     QFont savedFont = getVTFont();
1190     QFont font(savedFont, painter.device());
1191     painter.setFont(font);
1192     setVTFont(font);
1193 
1194     QRect rect(0, 0, size().width(), size().height());
1195 
1196     _printerFriendly = friendly;
1197     if (!friendly) {
1198         drawBackground(painter, rect, getBackgroundColor(),
1199                        true /* use opacity setting */);
1200     }
1201     drawContents(painter, rect);
1202     _printerFriendly = false;
1203     setVTFont(savedFont);
1204 }
1205 
1206 QPoint TerminalDisplay::cursorPosition() const
1207 {
1208     if (_screenWindow)
1209         return _screenWindow->cursorPosition();
1210     else
1211         return QPoint(0, 0);
1212 }
1213 
1214 FilterChain* TerminalDisplay::filterChain() const
1215 {
1216     return _filterChain;
1217 }
1218 
1219 void TerminalDisplay::paintFilters(QPainter& painter)
1220 {
1221     // get color of character under mouse and use it to draw
1222     // lines for filters
1223     QPoint cursorPos = mapFromGlobal(QCursor::pos());
1224     int cursorLine;
1225     int cursorColumn;
1226 
1227     getCharacterPosition(cursorPos , cursorLine , cursorColumn);
1228     Character cursorCharacter = _image[loc(cursorColumn, cursorLine)];
1229 
1230     painter.setPen(QPen(cursorCharacter.foregroundColor.color(colorTable())));
1231 
1232     // iterate over hotspots identified by the display's currently active filters
1233     // and draw appropriate visuals to indicate the presence of the hotspot
1234 
1235     QList<Filter::HotSpot*> spots = _filterChain->hotSpots();
1236     foreach(Filter::HotSpot* spot, spots) {
1237         QRegion region;
1238         if (_underlineLinks && spot->type() == Filter::HotSpot::Link) {
1239             QRect r;
1240             if (spot->startLine() == spot->endLine()) {
1241                 r.setCoords(spot->startColumn()*_fontWidth + _contentRect.left(),
1242                             spot->startLine()*_fontHeight + _contentRect.top(),
1243                             (spot->endColumn())*_fontWidth + _contentRect.left() - 1,
1244                             (spot->endLine() + 1)*_fontHeight + _contentRect.top() - 1);
1245                 region |= r;
1246             } else {
1247                 r.setCoords(spot->startColumn()*_fontWidth + _contentRect.left(),
1248                             spot->startLine()*_fontHeight + _contentRect.top(),
1249                             (_columns)*_fontWidth + _contentRect.left() - 1,
1250                             (spot->startLine() + 1)*_fontHeight + _contentRect.top() - 1);
1251                 region |= r;
1252                 for (int line = spot->startLine() + 1 ; line < spot->endLine() ; line++) {
1253                     r.setCoords(0 * _fontWidth + _contentRect.left(),
1254                                 line * _fontHeight + _contentRect.top(),
1255                                 (_columns)*_fontWidth + _contentRect.left() - 1,
1256                                 (line + 1)*_fontHeight + _contentRect.top() - 1);
1257                     region |= r;
1258                 }
1259                 r.setCoords(0 * _fontWidth + _contentRect.left(),
1260                             spot->endLine()*_fontHeight + _contentRect.top(),
1261                             (spot->endColumn())*_fontWidth + _contentRect.left() - 1,
1262                             (spot->endLine() + 1)*_fontHeight + _contentRect.top() - 1);
1263                 region |= r;
1264             }
1265         }
1266 
1267         for (int line = spot->startLine() ; line <= spot->endLine() ; line++) {
1268             int startColumn = 0;
1269             int endColumn = _columns - 1; // TODO use number of _columns which are actually
1270             // occupied on this line rather than the width of the
1271             // display in _columns
1272 
1273             // Check image size so _image[] is valid (see makeImage)
1274             if (loc(endColumn, line) > _imageSize)
1275                 break;
1276 
1277             // ignore whitespace at the end of the lines
1278             while (_image[loc(endColumn, line)].isSpace() && endColumn > 0)
1279                 endColumn--;
1280 
1281             // increment here because the column which we want to set 'endColumn' to
1282             // is the first whitespace character at the end of the line
1283             endColumn++;
1284 
1285             if (line == spot->startLine())
1286                 startColumn = spot->startColumn();
1287             if (line == spot->endLine())
1288                 endColumn = spot->endColumn();
1289 
1290             // TODO: resolve this comment with the new margin/center code
1291             // subtract one pixel from
1292             // the right and bottom so that
1293             // we do not overdraw adjacent
1294             // hotspots
1295             //
1296             // subtracting one pixel from all sides also prevents an edge case where
1297             // moving the mouse outside a link could still leave it underlined
1298             // because the check below for the position of the cursor
1299             // finds it on the border of the target area
1300             QRect r;
1301             r.setCoords(startColumn * _fontWidth + _contentRect.left(),
1302                         line * _fontHeight + _contentRect.top(),
1303                         endColumn * _fontWidth + _contentRect.left() - 1,
1304                         (line + 1)*_fontHeight + _contentRect.top() - 1);
1305             // Underline link hotspots
1306             if (_underlineLinks && spot->type() == Filter::HotSpot::Link) {
1307                 QFontMetrics metrics(font());
1308 
1309                 // find the baseline (which is the invisible line that the characters in the font sit on,
1310                 // with some having tails dangling below)
1311                 const int baseline = r.bottom() - metrics.descent();
1312                 // find the position of the underline below that
1313                 const int underlinePos = baseline + metrics.underlinePos();
1314                 if (region.contains(mapFromGlobal(QCursor::pos()))) {
1315                     painter.drawLine(r.left() , underlinePos ,
1316                                      r.right() , underlinePos);
1317                 }
1318                 // Marker hotspots simply have a transparent rectangular shape
1319                 // drawn on top of them
1320             } else if (spot->type() == Filter::HotSpot::Marker) {
1321                 //TODO - Do not use a hardcoded color for this
1322                 const bool isCurrentResultLine = (_screenWindow->currentResultLine() == (spot->startLine() + _screenWindow->currentLine()));
1323                 QColor color = isCurrentResultLine ? QColor(255, 255, 0, 120) : QColor(255, 0, 0, 120);
1324                 painter.fillRect(r, color);
1325             }
1326         }
1327     }
1328 }
1329 void TerminalDisplay::drawContents(QPainter& paint, const QRect& rect)
1330 {
1331     const QPoint tL  = contentsRect().topLeft();
1332     const int    tLx = tL.x();
1333     const int    tLy = tL.y();
1334 
1335     const int lux = qMin(_usedColumns - 1, qMax(0, (rect.left()   - tLx - _contentRect.left()) / _fontWidth));
1336     const int luy = qMin(_usedLines - 1,  qMax(0, (rect.top()    - tLy - _contentRect.top()) / _fontHeight));
1337     const int rlx = qMin(_usedColumns - 1, qMax(0, (rect.right()  - tLx - _contentRect.left()) / _fontWidth));
1338     const int rly = qMin(_usedLines - 1,  qMax(0, (rect.bottom() - tLy - _contentRect.top()) / _fontHeight));
1339 
1340     const int numberOfColumns = _usedColumns;
1341     QString unistr;
1342     unistr.reserve(numberOfColumns);
1343     for (int y = luy; y <= rly; y++) {
1344         int x = lux;
1345         if (!_image[loc(lux, y)].character && x)
1346             x--; // Search for start of multi-column character
1347         for (; x <= rlx; x++) {
1348             int len = 1;
1349             int p = 0;
1350 
1351             // reset our buffer to the number of columns
1352             int bufferSize = numberOfColumns;
1353             unistr.resize(bufferSize);
1354             QChar *disstrU = unistr.data();
1355 
1356             // is this a single character or a sequence of characters ?
1357             if (_image[loc(x, y)].rendition & RE_EXTENDED_CHAR) {
1358                 // sequence of characters
1359                 ushort extendedCharLength = 0;
1360                 const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(_image[loc(x, y)].character, extendedCharLength);
1361                 if (chars) {
1362                     Q_ASSERT(extendedCharLength > 1);
1363                     bufferSize += extendedCharLength - 1;
1364                     unistr.resize(bufferSize);
1365                     disstrU = unistr.data();
1366                     for (int index = 0 ; index < extendedCharLength ; index++) {
1367                         Q_ASSERT(p < bufferSize);
1368                         disstrU[p++] = chars[index];
1369                     }
1370                 }
1371             } else {
1372                 // single character
1373                 const quint16 c = _image[loc(x, y)].character;
1374                 if (c) {
1375                     Q_ASSERT(p < bufferSize);
1376                     disstrU[p++] = c; //fontMap(c);
1377                 }
1378             }
1379 
1380             const bool lineDraw = _image[loc(x, y)].isLineChar();
1381             const bool doubleWidth = (_image[ qMin(loc(x, y) + 1, _imageSize) ].character == 0);
1382             const CharacterColor currentForeground = _image[loc(x, y)].foregroundColor;
1383             const CharacterColor currentBackground = _image[loc(x, y)].backgroundColor;
1384             const quint8 currentRendition = _image[loc(x, y)].rendition;
1385 
1386             while (x + len <= rlx &&
1387                     _image[loc(x + len, y)].foregroundColor == currentForeground &&
1388                     _image[loc(x + len, y)].backgroundColor == currentBackground &&
1389                     (_image[loc(x + len, y)].rendition & ~RE_EXTENDED_CHAR) == (currentRendition & ~RE_EXTENDED_CHAR) &&
1390                     (_image[ qMin(loc(x + len, y) + 1, _imageSize) ].character == 0) == doubleWidth &&
1391                     _image[loc(x + len, y)].isLineChar() == lineDraw) {
1392                 const quint16 c = _image[loc(x + len, y)].character;
1393                 if (_image[loc(x + len, y)].rendition & RE_EXTENDED_CHAR) {
1394                     // sequence of characters
1395                     ushort extendedCharLength = 0;
1396                     const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(c, extendedCharLength);
1397                     if (chars) {
1398                         Q_ASSERT(extendedCharLength > 1);
1399                         bufferSize += extendedCharLength - 1;
1400                         unistr.resize(bufferSize);
1401                         disstrU = unistr.data();
1402                         for (int index = 0 ; index < extendedCharLength ; index++) {
1403                             Q_ASSERT(p < bufferSize);
1404                             disstrU[p++] = chars[index];
1405                         }
1406                     }
1407                 } else {
1408                     // single character
1409                     if (c) {
1410                         Q_ASSERT(p < bufferSize);
1411                         disstrU[p++] = c; //fontMap(c);
1412                     }
1413                 }
1414 
1415                 if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition
1416                     len++; // Skip trailing part of multi-column character
1417                 len++;
1418             }
1419             if ((x + len < _usedColumns) && (!_image[loc(x + len, y)].character))
1420                 len++; // Adjust for trailing part of multi-column character
1421 
1422             const bool save__fixedFont = _fixedFont;
1423             if (lineDraw)
1424                 _fixedFont = false;
1425             if (doubleWidth)
1426                 _fixedFont = false;
1427             unistr.resize(p);
1428 
1429             // Create a text scaling matrix for double width and double height lines.
1430             QMatrix textScale;
1431 
1432             if (y < _lineProperties.size()) {
1433                 if (_lineProperties[y] & LINE_DOUBLEWIDTH)
1434                     textScale.scale(2, 1);
1435 
1436                 if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
1437                     textScale.scale(1, 2);
1438             }
1439 
1440             //Apply text scaling matrix.
1441             paint.setWorldMatrix(textScale, true);
1442 
1443             //calculate the area in which the text will be drawn
1444             QRect textArea = QRect(_contentRect.left() + tLx + _fontWidth * x , _contentRect.top() + tLy + _fontHeight * y , _fontWidth * len , _fontHeight);
1445 
1446             //move the calculated area to take account of scaling applied to the painter.
1447             //the position of the area from the origin (0,0) is scaled
1448             //by the opposite of whatever
1449             //transformation has been applied to the painter.  this ensures that
1450             //painting does actually start from textArea.topLeft()
1451             //(instead of textArea.topLeft() * painter-scale)
1452             textArea.moveTopLeft(textScale.inverted().map(textArea.topLeft()));
1453 
1454             //paint text fragment
1455             if (_printerFriendly) {
1456                 drawPrinterFriendlyTextFragment(paint,
1457                                                 textArea,
1458                                                 unistr,
1459                                                 &_image[loc(x, y)]);
1460             } else {
1461                 drawTextFragment(paint,
1462                                  textArea,
1463                                  unistr,
1464                                  &_image[loc(x, y)]);
1465             }
1466 
1467             _fixedFont = save__fixedFont;
1468 
1469             //reset back to single-width, single-height _lines
1470             paint.setWorldMatrix(textScale.inverted(), true);
1471 
1472             if (y < _lineProperties.size() - 1) {
1473                 //double-height _lines are represented by two adjacent _lines
1474                 //containing the same characters
1475                 //both _lines will have the LINE_DOUBLEHEIGHT attribute.
1476                 //If the current line has the LINE_DOUBLEHEIGHT attribute,
1477                 //we can therefore skip the next line
1478                 if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
1479                     y++;
1480             }
1481 
1482             x += len - 1;
1483         }
1484     }
1485 }
1486 
1487 void TerminalDisplay::drawCurrentResultRect(QPainter& painter)
1488 {
1489     if(_screenWindow->currentResultLine() == -1) {
1490         return;
1491     }
1492 
1493     QRect r(0, (_screenWindow->currentResultLine() - _screenWindow->currentLine())*_fontHeight,
1494             contentsRect().width(), _fontHeight);
1495     painter.fillRect(r, QColor(0, 0, 255, 80));
1496 }
1497 
1498 QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const
1499 {
1500     QRect result;
1501     result.setLeft(_contentRect.left() + _fontWidth * imageArea.left());
1502     result.setTop(_contentRect.top() + _fontHeight * imageArea.top());
1503     result.setWidth(_fontWidth * imageArea.width());
1504     result.setHeight(_fontHeight * imageArea.height());
1505 
1506     return result;
1507 }
1508 
1509 /* ------------------------------------------------------------------------- */
1510 /*                                                                           */
1511 /*                          Blinking Text & Cursor                           */
1512 /*                                                                           */
1513 /* ------------------------------------------------------------------------- */
1514 
1515 void TerminalDisplay::setBlinkingCursorEnabled(bool blink)
1516 {
1517     _allowBlinkingCursor = blink;
1518 
1519     if (!hasFocus())
1520         return;
1521 
1522     if (blink && !_blinkCursorTimer->isActive())
1523         _blinkCursorTimer->start();
1524 
1525     if (!blink && _blinkCursorTimer->isActive()) {
1526         _blinkCursorTimer->stop();
1527         if (_cursorBlinking) {
1528             // if cursor is blinking(hidden), blink it again to make it show
1529             blinkCursorEvent();
1530         }
1531         Q_ASSERT(_cursorBlinking == false);
1532     }
1533 }
1534 
1535 void TerminalDisplay::setBlinkingTextEnabled(bool blink)
1536 {
1537     _allowBlinkingText = blink;
1538 
1539     if (blink && !_blinkTextTimer->isActive())
1540         _blinkTextTimer->start();
1541 
1542     if (!blink && _blinkTextTimer->isActive()) {
1543         _blinkTextTimer->stop();
1544         _textBlinking = false;
1545     }
1546 }
1547 
1548 void TerminalDisplay::focusOutEvent(QFocusEvent*)
1549 {
1550     // trigger a repaint of the cursor so that it is both:
1551     //
1552     //   * visible (in case it was hidden during blinking)
1553     //   * drawn in a focused out state
1554     _cursorBlinking = false;
1555     updateCursor();
1556 
1557     // suppress further cursor blinking
1558     _blinkCursorTimer->stop();
1559     Q_ASSERT(_cursorBlinking == false);
1560 
1561     // if text is blinking (hidden), blink it again to make it shown
1562     if (_textBlinking)
1563         blinkTextEvent();
1564 
1565     // suppress further text blinking
1566     _blinkTextTimer->stop();
1567     Q_ASSERT(_textBlinking == false);
1568 }
1569 
1570 void TerminalDisplay::focusInEvent(QFocusEvent*)
1571 {
1572     if (_allowBlinkingCursor)
1573         _blinkCursorTimer->start();
1574 
1575     updateCursor();
1576 
1577     if (_allowBlinkingText && _hasTextBlinker)
1578         _blinkTextTimer->start();
1579 }
1580 
1581 void TerminalDisplay::blinkTextEvent()
1582 {
1583     Q_ASSERT(_allowBlinkingText);
1584 
1585     _textBlinking = !_textBlinking;
1586 
1587     // TODO: Optimize to only repaint the areas of the widget where there is
1588     // blinking text rather than repainting the whole widget.
1589     update();
1590 }
1591 
1592 void TerminalDisplay::blinkCursorEvent()
1593 {
1594     Q_ASSERT(_allowBlinkingCursor);
1595 
1596     _cursorBlinking = !_cursorBlinking;
1597     updateCursor();
1598 }
1599 
1600 void TerminalDisplay::updateCursor()
1601 {
1602     QRect cursorRect = imageToWidget(QRect(cursorPosition(), QSize(1, 1)));
1603     update(cursorRect);
1604 }
1605 
1606 /* ------------------------------------------------------------------------- */
1607 /*                                                                           */
1608 /*                          Geometry & Resizing                              */
1609 /*                                                                           */
1610 /* ------------------------------------------------------------------------- */
1611 
1612 void TerminalDisplay::resizeEvent(QResizeEvent*)
1613 {
1614     updateImageSize();
1615 }
1616 
1617 void TerminalDisplay::propagateSize()
1618 {
1619     if (_image)
1620         updateImageSize();
1621 }
1622 
1623 void TerminalDisplay::updateImageSize()
1624 {
1625     Character* oldImage = _image;
1626     const int oldLines = _lines;
1627     const int oldColumns = _columns;
1628 
1629     makeImage();
1630 
1631     if (oldImage) {
1632         // copy the old image to reduce flicker
1633         int lines = qMin(oldLines, _lines);
1634         int columns = qMin(oldColumns, _columns);
1635         for (int line = 0; line < lines; line++) {
1636             memcpy((void*)&_image[_columns * line],
1637                    (void*)&oldImage[oldColumns * line],
1638                    columns * sizeof(Character));
1639         }
1640         delete[] oldImage;
1641     }
1642 
1643     if (_screenWindow)
1644         _screenWindow->setWindowLines(_lines);
1645 
1646     _resizing = (oldLines != _lines) || (oldColumns != _columns);
1647 
1648     if (_resizing) {
1649         showResizeNotification();
1650         emit changedContentSizeSignal(_contentRect.height(), _contentRect.width()); // expose resizeEvent
1651     }
1652 
1653     _resizing = false;
1654 }
1655 
1656 void TerminalDisplay::makeImage()
1657 {
1658     _wallpaper->load();
1659 
1660     calcGeometry();
1661 
1662     // confirm that array will be of non-zero size, since the painting code
1663     // assumes a non-zero array length
1664     Q_ASSERT(_lines > 0 && _columns > 0);
1665     Q_ASSERT(_usedLines <= _lines && _usedColumns <= _columns);
1666 
1667     _imageSize = _lines * _columns;
1668 
1669     // We over-commit one character so that we can be more relaxed in dealing with
1670     // certain boundary conditions: _image[_imageSize] is a valid but unused position
1671     _image = new Character[_imageSize + 1];
1672 
1673     clearImage();
1674 }
1675 
1676 void TerminalDisplay::clearImage()
1677 {
1678     for (int i = 0; i <= _imageSize; ++i)
1679         _image[i] = Screen::DefaultChar;
1680 }
1681 
1682 void TerminalDisplay::calcGeometry()
1683 {
1684     _scrollBar->resize(_scrollBar->sizeHint().width(), contentsRect().height());
1685     _contentRect = contentsRect().adjusted(_margin, _margin, -_margin, -_margin);
1686 
1687     switch (_scrollbarLocation) {
1688     case Enum::ScrollBarHidden :
1689         break;
1690     case Enum::ScrollBarLeft :
1691         _contentRect.setLeft(_contentRect.left() + _scrollBar->width());
1692         _scrollBar->move(contentsRect().topLeft());
1693         break;
1694     case Enum::ScrollBarRight:
1695         _contentRect.setRight(_contentRect.right() - _scrollBar->width());
1696         _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width() - 1, 0));
1697         break;
1698     }
1699 
1700     // ensure that display is always at least one column wide
1701     _columns = qMax(1, _contentRect.width() / _fontWidth);
1702     _usedColumns = qMin(_usedColumns, _columns);
1703 
1704     // ensure that display is always at least one line high
1705     _lines = qMax(1, _contentRect.height() / _fontHeight);
1706     _usedLines = qMin(_usedLines, _lines);
1707 
1708     if(_centerContents) {
1709         QSize unusedPixels = _contentRect.size() - QSize(_columns * _fontWidth, _lines * _fontHeight);
1710         _contentRect.adjust(unusedPixels.width() / 2, unusedPixels.height() / 2, 0, 0);
1711     }
1712 }
1713 
1714 // calculate the needed size, this must be synced with calcGeometry()
1715 void TerminalDisplay::setSize(int columns, int lines)
1716 {
1717     const int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->sizeHint().width();
1718     const int horizontalMargin = _margin * 2;
1719     const int verticalMargin = _margin * 2;
1720 
1721     QSize newSize = QSize(horizontalMargin + scrollBarWidth + (columns * _fontWidth)  ,
1722                           verticalMargin + (lines * _fontHeight));
1723 
1724     if (newSize != size()) {
1725         _size = newSize;
1726         updateGeometry();
1727     }
1728 }
1729 
1730 QSize TerminalDisplay::sizeHint() const
1731 {
1732     return _size;
1733 }
1734 
1735 //showEvent and hideEvent are reimplemented here so that it appears to other classes that the
1736 //display has been resized when the display is hidden or shown.
1737 //
1738 //TODO: Perhaps it would be better to have separate signals for show and hide instead of using
1739 //the same signal as the one for a content size change
1740 void TerminalDisplay::showEvent(QShowEvent*)
1741 {
1742     emit changedContentSizeSignal(_contentRect.height(), _contentRect.width());
1743 }
1744 void TerminalDisplay::hideEvent(QHideEvent*)
1745 {
1746     emit changedContentSizeSignal(_contentRect.height(), _contentRect.width());
1747 }
1748 
1749 void TerminalDisplay::setMargin(int margin)
1750 {
1751     _margin = margin;
1752     updateImageSize();
1753 }
1754 
1755 void TerminalDisplay::setCenterContents(bool enable)
1756 {
1757     _centerContents = enable;
1758     calcGeometry();
1759     update();
1760 }
1761 
1762 /* ------------------------------------------------------------------------- */
1763 /*                                                                           */
1764 /*                                Scrollbar                                  */
1765 /*                                                                           */
1766 /* ------------------------------------------------------------------------- */
1767 
1768 void TerminalDisplay::setScrollBarPosition(Enum::ScrollBarPositionEnum position)
1769 {
1770     if (_scrollbarLocation == position)
1771         return;
1772 
1773     if (position == Enum::ScrollBarHidden)
1774         _scrollBar->hide();
1775     else
1776         _scrollBar->show();
1777 
1778     _scrollbarLocation = position;
1779 
1780     propagateSize();
1781     update();
1782 }
1783 
1784 void TerminalDisplay::scrollBarPositionChanged(int)
1785 {
1786     if (!_screenWindow)
1787         return;
1788 
1789     _screenWindow->scrollTo(_scrollBar->value());
1790 
1791     // if the thumb has been moved to the bottom of the _scrollBar then set
1792     // the display to automatically track new output,
1793     // that is, scroll down automatically
1794     // to how new _lines as they are added
1795     const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum());
1796     _screenWindow->setTrackOutput(atEndOfOutput);
1797 
1798     updateImage();
1799 }
1800 
1801 void TerminalDisplay::setScroll(int cursor, int slines)
1802 {
1803     // update _scrollBar if the range or value has changed,
1804     // otherwise return
1805     //
1806     // setting the range or value of a _scrollBar will always trigger
1807     // a repaint, so it should be avoided if it is not necessary
1808     if (_scrollBar->minimum() == 0                 &&
1809             _scrollBar->maximum() == (slines - _lines) &&
1810             _scrollBar->value()   == cursor) {
1811         return;
1812     }
1813 
1814     disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int)));
1815     _scrollBar->setRange(0, slines - _lines);
1816     _scrollBar->setSingleStep(1);
1817     _scrollBar->setPageStep(_lines);
1818     _scrollBar->setValue(cursor);
1819     connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int)));
1820 }
1821 
1822 void TerminalDisplay::setScrollFullPage(bool fullPage)
1823 {
1824     _scrollFullPage = fullPage;
1825 }
1826 
1827 bool TerminalDisplay::scrollFullPage() const
1828 {
1829     return _scrollFullPage;
1830 }
1831 
1832 /* ------------------------------------------------------------------------- */
1833 /*                                                                           */
1834 /*                                  Mouse                                    */
1835 /*                                                                           */
1836 /* ------------------------------------------------------------------------- */
1837 void TerminalDisplay::mousePressEvent(QMouseEvent* ev)
1838 {
1839     if (_possibleTripleClick && (ev->button() == Qt::LeftButton)) {
1840         mouseTripleClickEvent(ev);
1841         return;
1842     }
1843 
1844     if (!contentsRect().contains(ev->pos())) return;
1845 
1846     if (!_screenWindow) return;
1847 
1848     int charLine;
1849     int charColumn;
1850     getCharacterPosition(ev->pos(), charLine, charColumn);
1851     QPoint pos = QPoint(charColumn, charLine);
1852 
1853     if (ev->button() == Qt::LeftButton) {
1854         // request the software keyboard, if any
1855         if (qApp->autoSipEnabled()) {
1856             QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(
1857                         style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
1858             if (hasFocus() || behavior == QStyle::RSIP_OnMouseClick) {
1859                 QEvent event(QEvent::RequestSoftwareInputPanel);
1860                 QApplication::sendEvent(this, &event);
1861             }
1862         }
1863 
1864         _lineSelectionMode = false;
1865         _wordSelectionMode = false;
1866 
1867         // The user clicked inside selected text
1868         bool selected =  _screenWindow->isSelected(pos.x(), pos.y());
1869 
1870         // Drag only when the Control key is held
1871         if ((!_ctrlRequiredForDrag || ev->modifiers() & Qt::ControlModifier) && selected) {
1872             _dragInfo.state = diPending;
1873             _dragInfo.start = ev->pos();
1874         } else {
1875             // No reason to ever start a drag event
1876             _dragInfo.state = diNone;
1877 
1878             _preserveLineBreaks = !((ev->modifiers() & Qt::ControlModifier) && !(ev->modifiers() & Qt::AltModifier));
1879             _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier);
1880 
1881             if (_mouseMarks || (ev->modifiers() == Qt::ShiftModifier)) {
1882                 // Only extend selection for programs not interested in mouse
1883                 if (_mouseMarks && (ev->modifiers() == Qt::ShiftModifier)) {
1884                     extendSelection(ev->pos());
1885                 } else {
1886                     _screenWindow->clearSelection();
1887 
1888                     pos.ry() += _scrollBar->value();
1889                     _iPntSel = _pntSel = pos;
1890                     _actSel = 1; // left mouse button pressed but nothing selected yet.
1891                 }
1892             } else {
1893                 emit mouseSignal(0, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0);
1894             }
1895 
1896             if (_underlineLinks && (_openLinksByDirectClick || (ev->modifiers() & Qt::ControlModifier))) {
1897                 Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine, charColumn);
1898                 if (spot && spot->type() == Filter::HotSpot::Link) {
1899                     QObject action;
1900                     action.setObjectName("open-action");
1901                     spot->activate(&action);
1902                 }
1903             }
1904         }
1905     } else if (ev->button() == Qt::MidButton) {
1906         processMidButtonClick(ev);
1907     } else if (ev->button() == Qt::RightButton) {
1908         if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier))
1909             emit configureRequest(ev->pos());
1910         else
1911             emit mouseSignal(2, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0);
1912     }
1913 }
1914 
1915 QList<QAction*> TerminalDisplay::filterActions(const QPoint& position)
1916 {
1917     int charLine, charColumn;
1918     getCharacterPosition(position, charLine, charColumn);
1919 
1920     Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine, charColumn);
1921 
1922     return spot ? spot->actions() : QList<QAction*>();
1923 }
1924 
1925 void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev)
1926 {
1927     int charLine = 0;
1928     int charColumn = 0;
1929     getCharacterPosition(ev->pos(), charLine, charColumn);
1930 
1931     // handle filters
1932     // change link hot-spot appearance on mouse-over
1933     Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine, charColumn);
1934     if (spot && spot->type() == Filter::HotSpot::Link) {
1935         if (_underlineLinks) {
1936             QRegion previousHotspotArea = _mouseOverHotspotArea;
1937             _mouseOverHotspotArea = QRegion();
1938             QRect r;
1939             if (spot->startLine() == spot->endLine()) {
1940                 r.setCoords(spot->startColumn()*_fontWidth + _contentRect.left(),
1941                             spot->startLine()*_fontHeight + _contentRect.top(),
1942                             (spot->endColumn())*_fontWidth + _contentRect.left() - 1,
1943                             (spot->endLine() + 1)*_fontHeight + _contentRect.top() - 1);
1944                 _mouseOverHotspotArea |= r;
1945             } else {
1946                 r.setCoords(spot->startColumn()*_fontWidth + _contentRect.left(),
1947                             spot->startLine()*_fontHeight + _contentRect.top(),
1948                             (_columns)*_fontWidth + _contentRect.left() - 1,
1949                             (spot->startLine() + 1)*_fontHeight + _contentRect.top() - 1);
1950                 _mouseOverHotspotArea |= r;
1951                 for (int line = spot->startLine() + 1 ; line < spot->endLine() ; line++) {
1952                     r.setCoords(0 * _fontWidth + _contentRect.left(),
1953                                 line * _fontHeight + _contentRect.top(),
1954                                 (_columns)*_fontWidth + _contentRect.left() - 1,
1955                                 (line + 1)*_fontHeight + _contentRect.top() - 1);
1956                     _mouseOverHotspotArea |= r;
1957                 }
1958                 r.setCoords(0 * _fontWidth + _contentRect.left(),
1959                             spot->endLine()*_fontHeight + _contentRect.top(),
1960                             (spot->endColumn())*_fontWidth + _contentRect.left() - 1,
1961                             (spot->endLine() + 1)*_fontHeight + _contentRect.top() - 1);
1962                 _mouseOverHotspotArea |= r;
1963             }
1964 
1965             if ((_openLinksByDirectClick || (ev->modifiers() & Qt::ControlModifier)) && (cursor().shape() != Qt::PointingHandCursor))
1966                 setCursor(Qt::PointingHandCursor);
1967 
1968             update(_mouseOverHotspotArea | previousHotspotArea);
1969         }
1970     } else if (!_mouseOverHotspotArea.isEmpty()) {
1971         if ((_underlineLinks && (_openLinksByDirectClick || (ev->modifiers() & Qt::ControlModifier))) || (cursor().shape() == Qt::PointingHandCursor))
1972             setCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor);
1973 
1974         update(_mouseOverHotspotArea);
1975         // set hotspot area to an invalid rectangle
1976         _mouseOverHotspotArea = QRegion();
1977     }
1978 
1979     // for auto-hiding the cursor, we need mouseTracking
1980     if (ev->buttons() == Qt::NoButton) return;
1981 
1982     // if the terminal is interested in mouse movements
1983     // then emit a mouse movement signal, unless the shift
1984     // key is being held down, which overrides this.
1985     if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) {
1986         int button = 3;
1987         if (ev->buttons() & Qt::LeftButton)
1988             button = 0;
1989         if (ev->buttons() & Qt::MidButton)
1990             button = 1;
1991         if (ev->buttons() & Qt::RightButton)
1992             button = 2;
1993 
1994         emit mouseSignal(button,
1995                          charColumn + 1,
1996                          charLine + 1 + _scrollBar->value() - _scrollBar->maximum(),
1997                          1);
1998 
1999         return;
2000     }
2001 
2002     if (_dragInfo.state == diPending) {
2003         // we had a mouse down, but haven't confirmed a drag yet
2004         // if the mouse has moved sufficiently, we will confirm
2005 
2006         const int distance = KGlobalSettings::dndEventDelay();
2007         if (ev->x() > _dragInfo.start.x() + distance || ev->x() < _dragInfo.start.x() - distance ||
2008                 ev->y() > _dragInfo.start.y() + distance || ev->y() < _dragInfo.start.y() - distance) {
2009             // we've left the drag square, we can start a real drag operation now
2010 
2011             _screenWindow->clearSelection();
2012             doDrag();
2013         }
2014         return;
2015     } else if (_dragInfo.state == diDragging) {
2016         // this isn't technically needed because mouseMoveEvent is suppressed during
2017         // Qt drag operations, replaced by dragMoveEvent
2018         return;
2019     }
2020 
2021     if (_actSel == 0) return;
2022 
2023 // don't extend selection while pasting
2024     if (ev->buttons() & Qt::MidButton) return;
2025 
2026     extendSelection(ev->pos());
2027 }
2028 
2029 void TerminalDisplay::leaveEvent(QEvent *)
2030 {
2031     // remove underline from an active link when cursor leaves the widget area
2032     if(!_mouseOverHotspotArea.isEmpty()) {
2033         update(_mouseOverHotspotArea);
2034         _mouseOverHotspotArea = QRegion();
2035     }
2036 }
2037 
2038 void TerminalDisplay::extendSelection(const QPoint& position)
2039 {
2040     if (!_screenWindow)
2041         return;
2042 
2043     //if ( !contentsRect().contains(ev->pos()) ) return;
2044     const QPoint tL  = contentsRect().topLeft();
2045     const int    tLx = tL.x();
2046     const int    tLy = tL.y();
2047     const int    scroll = _scrollBar->value();
2048 
2049     // we're in the process of moving the mouse with the left button pressed
2050     // the mouse cursor will kept caught within the bounds of the text in
2051     // this widget.
2052 
2053     int linesBeyondWidget = 0;
2054 
2055     QRect textBounds(tLx + _contentRect.left(),
2056                      tLy + _contentRect.top(),
2057                      _usedColumns * _fontWidth - 1,
2058                      _usedLines * _fontHeight - 1);
2059 
2060     QPoint pos = position;
2061 
2062     // Adjust position within text area bounds.
2063     const QPoint oldpos = pos;
2064 
2065     pos.setX(qBound(textBounds.left(), pos.x(), textBounds.right()));
2066     pos.setY(qBound(textBounds.top(), pos.y(), textBounds.bottom()));
2067 
2068     if (oldpos.y() > textBounds.bottom()) {
2069         linesBeyondWidget = (oldpos.y() - textBounds.bottom()) / _fontHeight;
2070         _scrollBar->setValue(_scrollBar->value() + linesBeyondWidget + 1); // scrollforward
2071     }
2072     if (oldpos.y() < textBounds.top()) {
2073         linesBeyondWidget = (textBounds.top() - oldpos.y()) / _fontHeight;
2074         _scrollBar->setValue(_scrollBar->value() - linesBeyondWidget - 1); // history
2075     }
2076 
2077     int charColumn = 0;
2078     int charLine = 0;
2079     getCharacterPosition(pos, charLine, charColumn);
2080 
2081     QPoint here = QPoint(charColumn, charLine);
2082     QPoint ohere;
2083     QPoint _iPntSelCorr = _iPntSel;
2084     _iPntSelCorr.ry() -= _scrollBar->value();
2085     QPoint _pntSelCorr = _pntSel;
2086     _pntSelCorr.ry() -= _scrollBar->value();
2087     bool swapping = false;
2088 
2089     if (_wordSelectionMode) {
2090         // Extend to word boundaries
2091         const bool left_not_right = (here.y() < _iPntSelCorr.y() ||
2092                                      (here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x()));
2093         const bool old_left_not_right = (_pntSelCorr.y() < _iPntSelCorr.y() ||
2094                                          (_pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x()));
2095         swapping = left_not_right != old_left_not_right;
2096 
2097         if (left_not_right) {
2098             ohere = findWordEnd(_iPntSelCorr);
2099             here = findWordStart(here);
2100         } else {
2101             ohere = findWordStart(_iPntSelCorr);
2102             here = findWordEnd(here);
2103         }
2104         ohere.rx()++;
2105     }
2106 
2107     if (_lineSelectionMode) {
2108         // Extend to complete line
2109         const bool above_not_below = (here.y() < _iPntSelCorr.y());
2110         if (above_not_below) {
2111             ohere = findLineEnd(_iPntSelCorr);
2112             here = findLineStart(here);
2113         } else {
2114             ohere = findLineStart(_iPntSelCorr);
2115             here = findLineEnd(here);
2116         }
2117 
2118         swapping = !(_tripleSelBegin == ohere);
2119         _tripleSelBegin = ohere;
2120 
2121         ohere.rx()++;
2122     }
2123 
2124     int offset = 0;
2125     if (!_wordSelectionMode && !_lineSelectionMode) {
2126         QChar selClass;
2127 
2128         const bool left_not_right = (here.y() < _iPntSelCorr.y() ||
2129                                      (here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x()));
2130         const bool old_left_not_right = (_pntSelCorr.y() < _iPntSelCorr.y() ||
2131                                          (_pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x()));
2132         swapping = left_not_right != old_left_not_right;
2133 
2134         // Find left (left_not_right ? from here : from start)
2135         const QPoint left = left_not_right ? here : _iPntSelCorr;
2136 
2137         // Find left (left_not_right ? from start : from here)
2138         QPoint right = left_not_right ? _iPntSelCorr : here;
2139         if (right.x() > 0 && !_columnSelectionMode) {
2140             int i = loc(right.x(), right.y());
2141             if (i >= 0 && i <= _imageSize) {
2142                 selClass = charClass(_image[i - 1]);
2143                 /* if (selClass == ' ')
2144                  {
2145                    while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) &&
2146                                    !(_lineProperties[right.y()] & LINE_WRAPPED))
2147                    { i++; right.rx()++; }
2148                    if (right.x() < _usedColumns-1)
2149                      right = left_not_right ? _iPntSelCorr : here;
2150                    else
2151                      right.rx()++;  // will be balanced later because of offset=-1;
2152                  }*/
2153             }
2154         }
2155 
2156         // Pick which is start (ohere) and which is extension (here)
2157         if (left_not_right) {
2158             here = left;
2159             ohere = right;
2160             offset = 0;
2161         } else {
2162             here = right;
2163             ohere = left;
2164             offset = -1;
2165         }
2166     }
2167 
2168     if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved
2169 
2170     if (here == ohere) return; // It's not left, it's not right.
2171 
2172     if (_actSel < 2 || swapping) {
2173         if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) {
2174             _screenWindow->setSelectionStart(ohere.x() , ohere.y() , true);
2175         } else {
2176             _screenWindow->setSelectionStart(ohere.x() - 1 - offset , ohere.y() , false);
2177         }
2178     }
2179 
2180     _actSel = 2; // within selection
2181     _pntSel = here;
2182     _pntSel.ry() += _scrollBar->value();
2183 
2184     if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) {
2185         _screenWindow->setSelectionEnd(here.x() , here.y());
2186     } else {
2187         _screenWindow->setSelectionEnd(here.x() + offset , here.y());
2188     }
2189 }
2190 
2191 void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev)
2192 {
2193     if (!_screenWindow)
2194         return;
2195 
2196     int charLine;
2197     int charColumn;
2198     getCharacterPosition(ev->pos(), charLine, charColumn);
2199 
2200     if (ev->button() == Qt::LeftButton) {
2201         if (_dragInfo.state == diPending) {
2202             // We had a drag event pending but never confirmed.  Kill selection
2203             _screenWindow->clearSelection();
2204         } else {
2205             if (_actSel > 1) {
2206                 copyToX11Selection();
2207             }
2208 
2209             _actSel = 0;
2210 
2211             //FIXME: emits a release event even if the mouse is
2212             //       outside the range. The procedure used in `mouseMoveEvent'
2213             //       applies here, too.
2214 
2215             if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier))
2216                 emit mouseSignal(0,
2217                                  charColumn + 1,
2218                                  charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 2);
2219         }
2220         _dragInfo.state = diNone;
2221     }
2222 
2223     if (!_mouseMarks &&
2224             (ev->button() == Qt::RightButton || ev->button() == Qt::MidButton) &&
2225             !(ev->modifiers() & Qt::ShiftModifier)) {
2226         emit mouseSignal(ev->button() == Qt::MidButton ? 1 : 2,
2227                          charColumn + 1,
2228                          charLine + 1 + _scrollBar->value() - _scrollBar->maximum() ,
2229                          2);
2230     }
2231 }
2232 
2233 void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint, int& line, int& column) const
2234 {
2235     column = (widgetPoint.x() + _fontWidth / 2 - contentsRect().left() - _contentRect.left()) / _fontWidth;
2236     line = (widgetPoint.y() - contentsRect().top() - _contentRect.top()) / _fontHeight;
2237 
2238     if (line < 0)
2239         line = 0;
2240     if (column < 0)
2241         column = 0;
2242 
2243     if (line >= _usedLines)
2244         line = _usedLines - 1;
2245 
2246     // the column value returned can be equal to _usedColumns, which
2247     // is the position just after the last character displayed in a line.
2248     //
2249     // this is required so that the user can select characters in the right-most
2250     // column (or left-most for right-to-left input)
2251     if (column > _usedColumns)
2252         column = _usedColumns;
2253 }
2254 
2255 void TerminalDisplay::updateLineProperties()
2256 {
2257     if (!_screenWindow)
2258         return;
2259 
2260     _lineProperties = _screenWindow->getLineProperties();
2261 }
2262 
2263 void TerminalDisplay::processMidButtonClick(QMouseEvent* ev)
2264 {
2265     if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) {
2266         const bool appendEnter = ev->modifiers() & Qt::ControlModifier;
2267 
2268         if (_middleClickPasteMode == Enum::PasteFromX11Selection) {
2269             pasteFromX11Selection(appendEnter);
2270         } else if (_middleClickPasteMode == Enum::PasteFromClipboard) {
2271             pasteFromClipboard(appendEnter);
2272         } else {
2273             Q_ASSERT(false);
2274         }
2275     } else {
2276         int charLine = 0;
2277         int charColumn = 0;
2278         getCharacterPosition(ev->pos(), charLine, charColumn);
2279 
2280         emit mouseSignal(1, charColumn + 1, charLine + 1 + _scrollBar->value() - _scrollBar->maximum() , 0);
2281     }
2282 }
2283 
2284 void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev)
2285 {
2286     // Yes, successive middle click can trigger this event
2287     if (ev->button() == Qt::MidButton) {
2288         processMidButtonClick(ev);
2289         return;
2290     }
2291 
2292     if (ev->button() != Qt::LeftButton) return;
2293     if (!_screenWindow) return;
2294 
2295     int charLine = 0;
2296     int charColumn = 0;
2297 
2298     getCharacterPosition(ev->pos(), charLine, charColumn);
2299 
2300     // pass on double click as two clicks.
2301     if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) {
2302         // Send just _ONE_ click event, since the first click of the double click
2303         // was already sent by the click handler
2304         emit mouseSignal(0, charColumn + 1,
2305                          charLine + 1 + _scrollBar->value() - _scrollBar->maximum(),
2306                          0);  // left button
2307         return;
2308     }
2309 
2310     _screenWindow->clearSelection();
2311 
2312     _wordSelectionMode = true;
2313     _actSel = 2; // within selection
2314 
2315     _iPntSel = QPoint(charColumn, charLine);
2316     const QPoint bgnSel = findWordStart(_iPntSel);
2317     const QPoint endSel = findWordEnd(_iPntSel);
2318     _iPntSel.ry() += _scrollBar->value();
2319 
2320     _screenWindow->setSelectionStart(bgnSel.x() , bgnSel.y() , false);
2321     _screenWindow->setSelectionEnd(endSel.x() , endSel.y());
2322     copyToX11Selection();
2323 
2324     _possibleTripleClick = true;
2325 
2326     QTimer::singleShot(QApplication::doubleClickInterval(), this,
2327                        SLOT(tripleClickTimeout()));
2328 }
2329 
2330 void TerminalDisplay::wheelEvent(QWheelEvent* ev)
2331 {
2332     // Only vertical scrolling is supported
2333     if (ev->orientation() != Qt::Vertical)
2334         return;
2335 
2336     const int modifiers = ev->modifiers();
2337     const int delta = ev->delta();
2338 
2339     // ctrl+<wheel> for zooming, like in konqueror and firefox
2340     if ((modifiers & Qt::ControlModifier) && mouseWheelZoom()) {
2341         if (delta > 0) {
2342             // wheel-up for increasing font size
2343             increaseFontSize();
2344         } else {
2345             // wheel-down for decreasing font size
2346             decreaseFontSize();
2347         }
2348 
2349         return;
2350     }
2351 
2352     // if the terminal program is not interested with mouse events:
2353     //  * send the event to the scrollbar if the slider has room to move
2354     //  * otherwise, send simulated up / down key presses to the terminal program
2355     //    for the benefit of programs such as 'less'
2356     if (_mouseMarks) {
2357         const bool canScroll = _scrollBar->maximum() > 0;
2358         if (canScroll) {
2359             _scrollBar->event(ev);
2360             _sessionController->setSearchStartToWindowCurrentLine();
2361         } else {
2362             // assume that each Up / Down key event will cause the terminal application
2363             // to scroll by one line.
2364             //
2365             // to get a reasonable scrolling speed, scroll by one line for every 5 degrees
2366             // of mouse wheel rotation.  Mouse wheels typically move in steps of 15 degrees,
2367             // giving a scroll of 3 lines
2368             const int keyCode = delta > 0 ? Qt::Key_Up : Qt::Key_Down;
2369             QKeyEvent keyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier);
2370 
2371             // QWheelEvent::delta() gives rotation in eighths of a degree
2372             const int degrees = delta / 8;
2373             const int lines = abs(degrees) / 5;
2374 
2375             for (int i = 0; i < lines; i++)
2376                 emit keyPressedSignal(&keyEvent);
2377         }
2378     } else {
2379         // terminal program wants notification of mouse activity
2380 
2381         int charLine;
2382         int charColumn;
2383         getCharacterPosition(ev->pos() , charLine , charColumn);
2384 
2385         emit mouseSignal(delta > 0 ? 4 : 5,
2386                          charColumn + 1,
2387                          charLine + 1 + _scrollBar->value() - _scrollBar->maximum() ,
2388                          0);
2389     }
2390 }
2391 
2392 void TerminalDisplay::tripleClickTimeout()
2393 {
2394     _possibleTripleClick = false;
2395 }
2396 
2397 void TerminalDisplay::viewScrolledByUser()
2398 {
2399     _sessionController->setSearchStartToWindowCurrentLine();
2400 }
2401 
2402 /* Moving left/up from the line containing pnt, return the starting 
2403    offset point which the given line is continiously wrapped
2404    (top left corner = 0,0; previous line not visible = 0,-1).
2405 */
2406 QPoint TerminalDisplay::findLineStart(const QPoint &pnt)
2407 {
2408     const int visibleScreenLines = _lineProperties.size();
2409     const int topVisibleLine = _screenWindow->currentLine();
2410     Screen *screen = _screenWindow->screen();
2411     int line = pnt.y();
2412     int lineInHistory= line + topVisibleLine;
2413 
2414     QVector<LineProperty> lineProperties = _lineProperties;
2415 
2416     while (lineInHistory > 0) {
2417         for (; line > 0; line--, lineInHistory--) {
2418             // Does previous line wrap around?
2419             if (!(lineProperties[line - 1] & LINE_WRAPPED)) {
2420                 return QPoint(0, lineInHistory - topVisibleLine);
2421             }
2422         }
2423 
2424         if (lineInHistory < 1)
2425             break;
2426 
2427         // _lineProperties is only for the visible screen, so grab new data
2428         int newRegionStart = qMax(0, lineInHistory - visibleScreenLines);
2429         lineProperties = screen->getLineProperties(newRegionStart, lineInHistory - 1);
2430         line = lineInHistory - newRegionStart;
2431     }
2432     return QPoint(0, lineInHistory - topVisibleLine);
2433 }
2434 
2435 /* Moving right/down from the line containing pnt, return the ending 
2436    offset point which the given line is continiously wrapped.
2437 */
2438 QPoint TerminalDisplay::findLineEnd(const QPoint &pnt)
2439 {
2440     const int visibleScreenLines = _lineProperties.size();
2441     const int topVisibleLine = _screenWindow->currentLine();
2442     const int maxY = _screenWindow->lineCount() - 1;
2443     Screen *screen = _screenWindow->screen();
2444     int line = pnt.y();
2445     int lineInHistory= line + topVisibleLine;
2446 
2447     QVector<LineProperty> lineProperties = _lineProperties;
2448 
2449     while (lineInHistory < maxY) {
2450         for (; line < lineProperties.count() && lineInHistory < maxY; line++, lineInHistory++) {
2451             // Does current line wrap around?
2452             if (!(lineProperties[line] & LINE_WRAPPED)) {
2453                 return QPoint(_columns - 1, lineInHistory - topVisibleLine);
2454             }
2455         }
2456 
2457         line = 0;
2458         lineProperties = screen->getLineProperties(lineInHistory, qMin(lineInHistory + visibleScreenLines, maxY));
2459     }
2460     return QPoint(_columns - 1, lineInHistory - topVisibleLine);
2461 }
2462 
2463 QPoint TerminalDisplay::findWordStart(const QPoint &pnt)
2464 {
2465     const int regSize = qMax(_screenWindow->windowLines(), 10);
2466     const int curLine = _screenWindow->currentLine();
2467     int i = pnt.y();
2468     int x = pnt.x();
2469     int y = i + curLine;
2470     int j = loc(x, i);
2471     QVector<LineProperty> lineProperties = _lineProperties;
2472     Screen *screen = _screenWindow->screen();
2473     Character *image = _image;
2474     Character *tmp_image = NULL;
2475     const QChar selClass = charClass(image[j]);
2476     const int imageSize = regSize * _columns;
2477 
2478     while (true) {
2479         for (;;j--, x--) {
2480             if (x > 0) {
2481                 if (charClass(image[j - 1]) == selClass)
2482                     continue;
2483                 goto out;
2484             } else if (i > 0) {
2485                 if (lineProperties[i - 1] & LINE_WRAPPED &&
2486                     charClass(image[j - 1]) == selClass) {
2487                     x = _columns;
2488                     i--;
2489                     y--;
2490                     continue;
2491                 }
2492                 goto out;
2493             } else if (y > 0) {
2494                 break;
2495             } else {
2496                 goto out;
2497             }
2498         }
2499         int newRegStart = qMax(0, y - regSize);
2500         lineProperties = screen->getLineProperties(newRegStart, y - 1);
2501         i = y - newRegStart;
2502         if (!tmp_image) {
2503             tmp_image = new Character[imageSize];
2504             image = tmp_image;
2505         }
2506         screen->getImage(tmp_image, imageSize, newRegStart, y - 1);
2507         j = loc(x, i);
2508     }
2509 out:
2510     if (tmp_image) {
2511         delete[] tmp_image;
2512     }
2513     return QPoint(x, y - curLine);
2514 }
2515 
2516 QPoint TerminalDisplay::findWordEnd(const QPoint &pnt)
2517 {
2518     const int regSize = qMax(_screenWindow->windowLines(), 10);
2519     const int curLine = _screenWindow->currentLine();
2520     int i = pnt.y();
2521     int x = pnt.x();
2522     int y = i + curLine;
2523     int j = loc(x, i);
2524     QVector<LineProperty> lineProperties = _lineProperties;
2525     Screen *screen = _screenWindow->screen();
2526     Character *image = _image;
2527     Character *tmp_image = NULL;
2528     const QChar selClass = charClass(image[j]);
2529     const int imageSize = regSize * _columns;
2530     const int maxY = _screenWindow->lineCount() - 1;
2531     const int maxX = _columns - 1;
2532 
2533     while (true) {
2534         const int lineCount = lineProperties.count();
2535         for (;;j++, x++) {
2536             if (x < maxX) {
2537                 if (charClass(image[j + 1]) == selClass)
2538                     continue;
2539                 goto out;
2540             } else if (i < lineCount - 1) {
2541                 if (lineProperties[i] & LINE_WRAPPED &&
2542                     charClass(image[j + 1]) == selClass) {
2543                     x = -1;
2544                     i++;
2545                     y++;
2546                     continue;
2547                 }
2548                 goto out;
2549             } else if (y < maxY) {
2550                 if (i < lineCount && !(lineProperties[i] & LINE_WRAPPED))
2551                     goto out;
2552                 break;
2553             } else {
2554                 goto out;
2555             }
2556         }
2557         int newRegEnd = qMin(y + regSize - 1, maxY);
2558         lineProperties = screen->getLineProperties(y, newRegEnd);
2559         i = 0;
2560         if (!tmp_image) {
2561             tmp_image = new Character[imageSize];
2562             image = tmp_image;
2563         }
2564         screen->getImage(tmp_image, imageSize, y, newRegEnd);
2565         x--;
2566         j = loc(x, i);
2567     }
2568 out:
2569     y -= curLine;
2570     // In word selection mode don't select @ (64) if at end of word.
2571     if (((image[j].rendition & RE_EXTENDED_CHAR) == 0) &&
2572         (QChar(image[j].character) == '@') &&
2573         (y > pnt.y() || x > pnt.x())) {
2574         if (x > 0) {
2575             x--;
2576         } else {
2577             y--;
2578         }
2579     }
2580     if (tmp_image) {
2581         delete[] tmp_image;
2582     }
2583     return QPoint(x, y);
2584 }
2585 
2586 void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev)
2587 {
2588     if (!_screenWindow) return;
2589 
2590     int charLine;
2591     int charColumn;
2592     getCharacterPosition(ev->pos(), charLine, charColumn);
2593     selectLine(QPoint(charColumn, charLine),
2594                _tripleClickMode == Enum::SelectWholeLine);
2595 }
2596 
2597 void TerminalDisplay::selectLine(QPoint pos, bool entireLine)
2598 {
2599     _iPntSel = pos;
2600 
2601     _screenWindow->clearSelection();
2602 
2603     _lineSelectionMode = true;
2604     _wordSelectionMode = false;
2605 
2606     _actSel = 2; // within selection
2607 
2608     if (!entireLine) { // Select from cursor to end of line
2609         _tripleSelBegin = findWordStart(_iPntSel);
2610         _screenWindow->setSelectionStart(_tripleSelBegin.x(),
2611                                          _tripleSelBegin.y() , false);
2612     } else {
2613         _tripleSelBegin = findLineStart(_iPntSel);
2614         _screenWindow->setSelectionStart(0 , _tripleSelBegin.y() , false);
2615     }
2616 
2617     _iPntSel = findLineEnd(_iPntSel);
2618     _screenWindow->setSelectionEnd(_iPntSel.x() , _iPntSel.y());
2619 
2620     copyToX11Selection();
2621 
2622     _iPntSel.ry() += _scrollBar->value();
2623 }
2624 
2625 void TerminalDisplay::selectCurrentLine()
2626 {
2627     if (!_screenWindow) return;
2628 
2629     selectLine(cursorPosition(), true);
2630 }
2631 
2632 bool TerminalDisplay::focusNextPrevChild(bool next)
2633 {
2634     // for 'Tab', always disable focus switching among widgets
2635     // for 'Shift+Tab', leave the decision to higher level
2636     if (next)
2637         return false;
2638     else
2639         return QWidget::focusNextPrevChild(next);
2640 }
2641 
2642 QChar TerminalDisplay::charClass(const Character& ch) const
2643 {
2644     if (ch.rendition & RE_EXTENDED_CHAR) {
2645         ushort extendedCharLength = 0;
2646         const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(ch.character, extendedCharLength);
2647         if (chars && extendedCharLength > 0) {
2648             const QString s = QString::fromUtf16(chars, extendedCharLength);
2649             if (_wordCharacters.contains(s, Qt::CaseInsensitive))
2650                 return 'a';
2651             bool allLetterOrNumber = true;
2652             for (int i = 0; allLetterOrNumber && i < s.size(); ++i)
2653                 allLetterOrNumber = s.at(i).isLetterOrNumber();
2654             return allLetterOrNumber ? 'a' : s.at(0);
2655         }
2656         return 0;
2657     } else {
2658         const QChar qch(ch.character);
2659         if (qch.isSpace()) return ' ';
2660 
2661         if (qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive))
2662             return 'a';
2663 
2664         return qch;
2665     }
2666 }
2667 
2668 void TerminalDisplay::setWordCharacters(const QString& wc)
2669 {
2670     _wordCharacters = wc;
2671 }
2672 
2673 // FIXME: the actual value of _mouseMarks is the opposite of its semantic.
2674 // When using programs not interested with mouse(shell, less), it is true.
2675 // When using programs interested with mouse(vim,mc), it is false.
2676 void TerminalDisplay::setUsesMouse(bool on)
2677 {
2678     _mouseMarks = on;
2679     setCursor(_mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor);
2680 }
2681 bool TerminalDisplay::usesMouse() const
2682 {
2683     return _mouseMarks;
2684 }
2685 
2686 void TerminalDisplay::setBracketedPasteMode(bool on)
2687 {
2688     _bracketedPasteMode = on;
2689 }
2690 bool TerminalDisplay::bracketedPasteMode() const
2691 {
2692     return _bracketedPasteMode;
2693 }
2694 
2695 /* ------------------------------------------------------------------------- */
2696 /*                                                                           */
2697 /*                               Clipboard                                   */
2698 /*                                                                           */
2699 /* ------------------------------------------------------------------------- */
2700 
2701 void TerminalDisplay::doPaste(QString text, bool appendReturn)
2702 {
2703     if (!_screenWindow)
2704         return;
2705 
2706     if (appendReturn)
2707         text.append("\r");
2708 
2709     if (text.length() > 8000) {
2710         if (KMessageBox::warningContinueCancel(window(),
2711                         i18np("Are you sure you want to paste %1 character?",
2712                               "Are you sure you want to paste %1 characters?",
2713                               text.length()),
2714                         i18n("Confirm Paste"),
2715                         KStandardGuiItem::cont(),
2716                         KStandardGuiItem::cancel(),
2717                         "ShowPasteHugeTextWarning") == KMessageBox::Cancel)
2718             return;
2719     }
2720 
2721     if (!text.isEmpty()) {
2722         text.replace('\n', '\r');
2723         if (bracketedPasteMode()) {
2724             text.prepend("\e[200~");
2725             text.append("\e[201~");
2726         }
2727         // perform paste by simulating keypress events
2728         QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text);
2729         emit keyPressedSignal(&e);
2730     }
2731 }
2732 
2733 void TerminalDisplay::setAutoCopySelectedText(bool enabled)
2734 {
2735     _autoCopySelectedText = enabled;
2736 }
2737 
2738 void TerminalDisplay::setMiddleClickPasteMode(Enum::MiddleClickPasteModeEnum mode)
2739 {
2740     _middleClickPasteMode = mode;
2741 }
2742 
2743 void TerminalDisplay::copyToX11Selection()
2744 {
2745     if (!_screenWindow)
2746         return;
2747 
2748     QString text = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces);
2749     if (text.isEmpty())
2750         return;
2751 
2752     QApplication::clipboard()->setText(text, QClipboard::Selection);
2753 
2754     if (_autoCopySelectedText)
2755         QApplication::clipboard()->setText(text, QClipboard::Clipboard);
2756 }
2757 
2758 void TerminalDisplay::copyToClipboard()
2759 {
2760     if (!_screenWindow)
2761         return;
2762 
2763     QString text = _screenWindow->selectedText(_preserveLineBreaks, _trimTrailingSpaces);
2764     if (text.isEmpty())
2765         return;
2766 
2767     QApplication::clipboard()->setText(text, QClipboard::Clipboard);
2768 }
2769 
2770 void TerminalDisplay::pasteFromClipboard(bool appendEnter)
2771 {
2772     QString text = QApplication::clipboard()->text(QClipboard::Clipboard);
2773     doPaste(text, appendEnter);
2774 }
2775 
2776 void TerminalDisplay::pasteFromX11Selection(bool appendEnter)
2777 {
2778     QString text = QApplication::clipboard()->text(QClipboard::Selection);
2779     doPaste(text, appendEnter);
2780 }
2781 
2782 /* ------------------------------------------------------------------------- */
2783 /*                                                                           */
2784 /*                                Input Method                               */
2785 /*                                                                           */
2786 /* ------------------------------------------------------------------------- */
2787 
2788 void TerminalDisplay::inputMethodEvent(QInputMethodEvent* event)
2789 {
2790     if (!event->commitString().isEmpty()) {
2791         QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier, event->commitString());
2792         emit keyPressedSignal(&keyEvent);
2793     }
2794 
2795     _inputMethodData.preeditString = event->preeditString();
2796     update(preeditRect() | _inputMethodData.previousPreeditRect);
2797 
2798     event->accept();
2799 }
2800 
2801 QVariant TerminalDisplay::inputMethodQuery(Qt::InputMethodQuery query) const
2802 {
2803     const QPoint cursorPos = cursorPosition();
2804     switch (query) {
2805     case Qt::ImMicroFocus:
2806         return imageToWidget(QRect(cursorPos.x(), cursorPos.y(), 1, 1));
2807         break;
2808     case Qt::ImFont:
2809         return font();
2810         break;
2811     case Qt::ImCursorPosition:
2812         // return the cursor position within the current line
2813         return cursorPos.x();
2814         break;
2815     case Qt::ImSurroundingText: {
2816         // return the text from the current line
2817         QString lineText;
2818         QTextStream stream(&lineText);
2819         PlainTextDecoder decoder;
2820         decoder.begin(&stream);
2821         decoder.decodeLine(&_image[loc(0, cursorPos.y())], _usedColumns, _lineProperties[cursorPos.y()]);
2822         decoder.end();
2823         return lineText;
2824     }
2825     break;
2826     case Qt::ImCurrentSelection:
2827         return QString();
2828         break;
2829     default:
2830         break;
2831     }
2832 
2833     return QVariant();
2834 }
2835 
2836 QRect TerminalDisplay::preeditRect() const
2837 {
2838     const int preeditLength = string_width(_inputMethodData.preeditString);
2839 
2840     if (preeditLength == 0)
2841         return QRect();
2842 
2843     return QRect(_contentRect.left() + _fontWidth * cursorPosition().x(),
2844                  _contentRect.top() + _fontHeight * cursorPosition().y(),
2845                  _fontWidth * preeditLength,
2846                  _fontHeight);
2847 }
2848 
2849 void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect)
2850 {
2851     if (_inputMethodData.preeditString.isEmpty())
2852         return;
2853 
2854     const QPoint cursorPos = cursorPosition();
2855 
2856     bool invertColors = false;
2857     const QColor background = _colorTable[DEFAULT_BACK_COLOR].color;
2858     const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color;
2859     const Character* style = &_image[loc(cursorPos.x(), cursorPos.y())];
2860 
2861     drawBackground(painter, rect, background, true);
2862     drawCursor(painter, rect, foreground, background, invertColors);
2863     drawCharacters(painter, rect, _inputMethodData.preeditString, style, invertColors);
2864 
2865     _inputMethodData.previousPreeditRect = rect;
2866 }
2867 
2868 /* ------------------------------------------------------------------------- */
2869 /*                                                                           */
2870 /*                                Keyboard                                   */
2871 /*                                                                           */
2872 /* ------------------------------------------------------------------------- */
2873 
2874 void TerminalDisplay::setFlowControlWarningEnabled(bool enable)
2875 {
2876     _flowControlWarningEnabled = enable;
2877 
2878     // if the dialog is currently visible and the flow control warning has
2879     // been disabled then hide the dialog
2880     if (!enable)
2881         outputSuspended(false);
2882 }
2883 
2884 void TerminalDisplay::outputSuspended(bool suspended)
2885 {
2886     //create the label when this function is first called
2887     if (!_outputSuspendedLabel) {
2888         //This label includes a link to an English language website
2889         //describing the 'flow control' (Xon/Xoff) feature found in almost
2890         //all terminal emulators.
2891         //If there isn't a suitable article available in the target language the link
2892         //can simply be removed.
2893         _outputSuspendedLabel = new QLabel(i18n("<qt>Output has been "
2894                                                 "<a href=\"http://en.wikipedia.org/wiki/Software_flow_control\">suspended</a>"
2895                                                 " by pressing Ctrl+S."
2896                                                 "  Press <b>Ctrl+Q</b> to resume."
2897                                                 "  This message will be dismissed in 10 seconds.</qt>"),
2898                                            this);
2899 
2900         QPalette palette(_outputSuspendedLabel->palette());
2901         KColorScheme::adjustBackground(palette, KColorScheme::NeutralBackground);
2902         _outputSuspendedLabel->setPalette(palette);
2903         _outputSuspendedLabel->setAutoFillBackground(true);
2904         _outputSuspendedLabel->setBackgroundRole(QPalette::Base);
2905         _outputSuspendedLabel->setFont(KGlobalSettings::smallestReadableFont());
2906         _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5);
2907         _outputSuspendedLabel->setWordWrap(true);
2908 
2909         //enable activation of "Xon/Xoff" link in label
2910         _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse |
2911                 Qt::LinksAccessibleByKeyboard);
2912         _outputSuspendedLabel->setOpenExternalLinks(true);
2913         _outputSuspendedLabel->setVisible(false);
2914 
2915         _gridLayout->addWidget(_outputSuspendedLabel);
2916         _gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding,
2917                                              QSizePolicy::Expanding),
2918                              1, 0);
2919     }
2920     // Remove message after a few seconds
2921     if (suspended) {
2922         QTimer::singleShot(10000, this, SLOT(dismissOutputSuspendedMessage()));
2923     }
2924 
2925     _outputSuspendedLabel->setVisible(suspended);
2926 }
2927 
2928 void TerminalDisplay::dismissOutputSuspendedMessage()
2929 {
2930     outputSuspended(false);
2931 }
2932 
2933 void TerminalDisplay::scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount)
2934 {
2935     _screenWindow->scrollBy(mode, amount, _scrollFullPage);
2936     _screenWindow->setTrackOutput(_screenWindow->atEndOfOutput());
2937     updateLineProperties();
2938     updateImage();
2939     viewScrolledByUser();
2940 }
2941 
2942 void TerminalDisplay::keyPressEvent(QKeyEvent* event)
2943 {
2944     _screenWindow->screen()->setCurrentTerminalDisplay(this);
2945 
2946     _actSel = 0; // Key stroke implies a screen update, so TerminalDisplay won't
2947     // know where the current selection is.
2948 
2949     if (_allowBlinkingCursor) {
2950         _blinkCursorTimer->start();
2951         if (_cursorBlinking) {
2952             // if cursor is blinking(hidden), blink it again to show it
2953             blinkCursorEvent();
2954         }
2955         Q_ASSERT(_cursorBlinking == false);
2956     }
2957 
2958     emit keyPressedSignal(event);
2959 
2960 #if QT_VERSION >= 0x040800 // added in Qt 4.8.0
2961 #ifndef QT_NO_ACCESSIBILITY
2962     QAccessible::updateAccessibility(this, 0, QAccessible::TextCaretMoved);
2963 #endif
2964 #endif
2965 
2966     event->accept();
2967 }
2968 
2969 bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent* keyEvent)
2970 {
2971     const int modifiers = keyEvent->modifiers();
2972 
2973     //  When a possible shortcut combination is pressed,
2974     //  emit the overrideShortcutCheck() signal to allow the host
2975     //  to decide whether the terminal should override it or not.
2976     if (modifiers != Qt::NoModifier) {
2977         int modifierCount = 0;
2978         unsigned int currentModifier = Qt::ShiftModifier;
2979 
2980         while (currentModifier <= Qt::KeypadModifier) {
2981             if (modifiers & currentModifier)
2982                 modifierCount++;
2983             currentModifier <<= 1;
2984         }
2985         if (modifierCount < 2) {
2986             bool override = false;
2987             emit overrideShortcutCheck(keyEvent, override);
2988             if (override) {
2989                 keyEvent->accept();
2990                 return true;
2991             }
2992         }
2993     }
2994 
2995     // Override any of the following shortcuts because
2996     // they are needed by the terminal
2997     int keyCode = keyEvent->key() | modifiers;
2998     switch (keyCode) {
2999         // list is taken from the QLineEdit::event() code
3000     case Qt::Key_Tab:
3001     case Qt::Key_Delete:
3002     case Qt::Key_Home:
3003     case Qt::Key_End:
3004     case Qt::Key_Backspace:
3005     case Qt::Key_Left:
3006     case Qt::Key_Right:
3007     case Qt::Key_Slash:
3008     case Qt::Key_Period:
3009     case Qt::Key_Space:
3010         keyEvent->accept();
3011         return true;
3012     }
3013     return false;
3014 }
3015 
3016 bool TerminalDisplay::event(QEvent* event)
3017 {
3018     bool eventHandled = false;
3019     switch (event->type()) {
3020     case QEvent::ShortcutOverride:
3021         eventHandled = handleShortcutOverrideEvent(static_cast<QKeyEvent*>(event));
3022         break;
3023     case QEvent::PaletteChange:
3024     case QEvent::ApplicationPaletteChange:
3025         _scrollBar->setPalette(QApplication::palette());
3026         break;
3027     default:
3028         break;
3029     }
3030     return eventHandled ? true : QWidget::event(event);
3031 }
3032 
3033 void TerminalDisplay::contextMenuEvent(QContextMenuEvent* event)
3034 {
3035     // the logic for the mouse case is within MousePressEvent()
3036     if (event->reason() != QContextMenuEvent::Mouse) {
3037         emit configureRequest(mapFromGlobal(QCursor::pos()));
3038     }
3039 }
3040 
3041 /* --------------------------------------------------------------------- */
3042 /*                                                                       */
3043 /*                                  Bell                                 */
3044 /*                                                                       */
3045 /* --------------------------------------------------------------------- */
3046 
3047 void TerminalDisplay::setBellMode(int mode)
3048 {
3049     _bellMode = mode;
3050 }
3051 
3052 int TerminalDisplay::bellMode() const
3053 {
3054     return _bellMode;
3055 }
3056 
3057 void TerminalDisplay::unmaskBell()
3058 {
3059     _bellMasked = false;
3060 }
3061 
3062 void TerminalDisplay::bell(const QString& message)
3063 {
3064     if (_bellMasked)
3065         return;
3066 
3067     switch (_bellMode) {
3068     case Enum::SystemBeepBell:
3069         KNotification::beep();
3070         break;
3071     case Enum::NotifyBell:
3072         // STABLE API:
3073         //     Please note that these event names, "BellVisible" and "BellInvisible",
3074         //     should not change and should be kept stable, because other applications
3075         //     that use this code via KPart rely on these names for notifications.
3076         KNotification::event(hasFocus() ? "BellVisible" : "BellInvisible",
3077                              message, QPixmap(), this);
3078         break;
3079     case Enum::VisualBell:
3080         visualBell();
3081         break;
3082     default:
3083         break;
3084     }
3085 
3086     // limit the rate at which bells can occur.
3087     // ...mainly for sound effects where rapid bells in sequence
3088     // produce a horrible noise.
3089     _bellMasked = true;
3090     QTimer::singleShot(500, this, SLOT(unmaskBell()));
3091 }
3092 
3093 void TerminalDisplay::visualBell()
3094 {
3095     swapFGBGColors();
3096     QTimer::singleShot(200, this, SLOT(swapFGBGColors()));
3097 }
3098 
3099 void TerminalDisplay::swapFGBGColors()
3100 {
3101     // swap the default foreground & background color
3102     ColorEntry color = _colorTable[DEFAULT_BACK_COLOR];
3103     _colorTable[DEFAULT_BACK_COLOR] = _colorTable[DEFAULT_FORE_COLOR];
3104     _colorTable[DEFAULT_FORE_COLOR] = color;
3105 
3106     update();
3107 }
3108 
3109 /* --------------------------------------------------------------------- */
3110 /*                                                                       */
3111 /* Drag & Drop                                                           */
3112 /*                                                                       */
3113 /* --------------------------------------------------------------------- */
3114 
3115 void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event)
3116 {
3117     // text/plain alone is enough for KDE-apps
3118     // text/uri-list is for supporting some non-KDE apps, such as thunar
3119     //   and pcmanfm
3120     // That also applies in dropEvent()
3121     if (event->mimeData()->hasFormat("text/plain") ||
3122             event->mimeData()->hasFormat("text/uri-list")) {
3123         event->acceptProposedAction();
3124     }
3125 }
3126 
3127 void TerminalDisplay::dropEvent(QDropEvent* event)
3128 {
3129     KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
3130 
3131     QString dropText;
3132     if (!urls.isEmpty()) {
3133         for (int i = 0 ; i < urls.count() ; i++) {
3134             KUrl url = KIO::NetAccess::mostLocalUrl(urls[i] , 0);
3135             QString urlText;
3136 
3137             if (url.isLocalFile())
3138                 urlText = url.path();
3139             else
3140                 urlText = url.url();
3141 
3142             // in future it may be useful to be able to insert file names with drag-and-drop
3143             // without quoting them (this only affects paths with spaces in)
3144             urlText = KShell::quoteArg(urlText);
3145 
3146             dropText += urlText;
3147 
3148             // Each filename(including the last) should be followed by one space.
3149             dropText += ' ';
3150         }
3151 
3152 #if defined(HAVE_LIBKONQ)
3153         // If our target is local we will open a popup - otherwise the fallback kicks
3154         // in and the URLs will simply be pasted as text.
3155         if (_sessionController && _sessionController->url().isLocalFile()) {
3156             // A standard popup with Copy, Move and Link as options -
3157             // plus an additional Paste option.
3158 
3159             QAction* pasteAction = new QAction(i18n("&Paste Location"), this);
3160             pasteAction->setData(dropText);
3161             connect(pasteAction, SIGNAL(triggered()), this, SLOT(dropMenuPasteActionTriggered()));
3162 
3163             QList<QAction*> additionalActions;
3164             additionalActions.append(pasteAction);
3165 
3166             if (urls.count() == 1) {
3167                 const KUrl url = KIO::NetAccess::mostLocalUrl(urls[0] , 0);
3168 
3169                 if (url.isLocalFile()) {
3170                     const QFileInfo fileInfo(url.path());
3171 
3172                     if (fileInfo.isDir()) {
3173                         QAction* cdAction = new QAction(i18n("Change &Directory To"), this);
3174                         dropText = QLatin1String(" cd ") + dropText + QChar('\n');
3175                         cdAction->setData(dropText);
3176                         connect(cdAction, SIGNAL(triggered()), this, SLOT(dropMenuCdActionTriggered()));
3177                         additionalActions.append(cdAction);
3178                     }
3179                 }
3180             }
3181 
3182             KUrl target(_sessionController->currentDir());
3183 
3184             KonqOperations::doDrop(KFileItem(), target, event, this, additionalActions);
3185 
3186             return;
3187         }
3188 #endif
3189 
3190     } else {
3191         dropText = event->mimeData()->text();
3192     }
3193 
3194     if (event->mimeData()->hasFormat("text/plain") ||
3195             event->mimeData()->hasFormat("text/uri-list")) {
3196         emit sendStringToEmu(dropText.toLocal8Bit());
3197     }
3198 }
3199 
3200 void TerminalDisplay::dropMenuPasteActionTriggered()
3201 {
3202     if (sender()) {
3203         const QAction* action = qobject_cast<const QAction*>(sender());
3204         if (action) {
3205             emit sendStringToEmu(action->data().toString().toLocal8Bit());
3206         }
3207     }
3208 }
3209 
3210 void TerminalDisplay::dropMenuCdActionTriggered()
3211 {
3212     if (sender()) {
3213         const QAction* action = qobject_cast<const QAction*>(sender());
3214         if (action) {
3215             emit sendStringToEmu(action->data().toString().toLocal8Bit());
3216         }
3217     }
3218 }
3219 
3220 void TerminalDisplay::doDrag()
3221 {
3222     _dragInfo.state = diDragging;
3223     _dragInfo.dragObject = new QDrag(this);
3224     QMimeData* mimeData = new QMimeData;
3225     mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection));
3226     _dragInfo.dragObject->setMimeData(mimeData);
3227     _dragInfo.dragObject->exec(Qt::CopyAction);
3228 }
3229 
3230 void TerminalDisplay::setSessionController(SessionController* controller)
3231 {
3232     _sessionController = controller;
3233 }
3234 
3235 SessionController* TerminalDisplay::sessionController()
3236 {
3237     return _sessionController;
3238 }
3239 
3240 AutoScrollHandler::AutoScrollHandler(QWidget* parent)
3241     : QObject(parent)
3242     , _timerId(0)
3243 {
3244     parent->installEventFilter(this);
3245 }
3246 void AutoScrollHandler::timerEvent(QTimerEvent* event)
3247 {
3248     if (event->timerId() != _timerId)
3249         return;
3250 
3251     QMouseEvent mouseEvent(QEvent::MouseMove,
3252                            widget()->mapFromGlobal(QCursor::pos()),
3253                            Qt::NoButton,
3254                            Qt::LeftButton,
3255                            Qt::NoModifier);
3256 
3257     QApplication::sendEvent(widget(), &mouseEvent);
3258 }
3259 bool AutoScrollHandler::eventFilter(QObject* watched, QEvent* event)
3260 {
3261     Q_ASSERT(watched == parent());
3262     Q_UNUSED(watched);
3263 
3264     QMouseEvent* mouseEvent = (QMouseEvent*)event;
3265     switch (event->type()) {
3266     case QEvent::MouseMove: {
3267         bool mouseInWidget = widget()->rect().contains(mouseEvent->pos());
3268         if (mouseInWidget) {
3269             if (_timerId)
3270                 killTimer(_timerId);
3271 
3272             _timerId = 0;
3273         } else {
3274             if (!_timerId && (mouseEvent->buttons() & Qt::LeftButton))
3275                 _timerId = startTimer(100);
3276         }
3277 
3278         break;
3279     }
3280     case QEvent::MouseButtonRelease: {
3281         if (_timerId && (mouseEvent->buttons() & ~Qt::LeftButton)) {
3282             killTimer(_timerId);
3283             _timerId = 0;
3284         }
3285         break;
3286     }
3287     default:
3288         break;
3289     };
3290 
3291     return false;
3292 }
3293 
3294 #include "TerminalDisplay.moc"