TerminalDisplay.cpp 121 KB
Newer Older
1
/*
2
    This file is part of Konsole, a terminal emulator for KDE.
3

4 5
    Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
    Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
6

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

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

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

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

26 27 28
// Config
#include <config-konsole.h>

Robert Knight's avatar
Robert Knight committed
29
// Qt
30
#include <QApplication>
Dirk Mueller's avatar
Dirk Mueller committed
31 32 33
#include <QtGui/QClipboard>
#include <QtGui/QKeyEvent>
#include <QtCore/QEvent>
34
#include <QtCore/QFileInfo>
35 36
#include <QGridLayout>
#include <QAction>
37
#include <QFontDatabase>
38
#include <QLabel>
39
#include <QMimeData>
Dirk Mueller's avatar
Dirk Mueller committed
40 41
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
42 43
#include <QScrollBar>
#include <QStyle>
Dirk Mueller's avatar
Dirk Mueller committed
44
#include <QtCore/QTimer>
Alex Richardson's avatar
Alex Richardson committed
45
#include <QDrag>
46
#include <QDesktopServices>
47
#include <QtGui/QAccessible>
48

Robert Knight's avatar
Robert Knight committed
49
// KDE
50
#include <KShell>
Robert Knight's avatar
 
Robert Knight committed
51
#include <KColorScheme>
Robert Knight's avatar
Robert Knight committed
52
#include <KCursor>
53
#include <KLocalizedString>
Robert Knight's avatar
Robert Knight committed
54
#include <KNotification>
55 56
#include <KIO/DropJob>
#include <KJobWidgets>
57
#include <KMessageBox>
58
#include <KIO/StatJob>
Robert Knight's avatar
Robert Knight committed
59 60

// Konsole
61
#include "Filter.h"
62
#include "konsoledebug.h"
Robert Knight's avatar
Robert Knight committed
63
#include "konsole_wcwidth.h"
64
#include "TerminalCharacterDecoder.h"
65
#include "Screen.h"
Jekyll Wu's avatar
Jekyll Wu committed
66
#include "LineFont.h"
67
#include "SessionController.h"
68
#include "ExtendedCharTable.h"
69
#include "TerminalDisplayAccessible.h"
70 71
#include "SessionManager.h"
#include "Session.h"
72
#include "WindowSystemInfo.h"
73

74 75
using namespace Konsole;

76
#ifndef loc
77
#define loc(X,Y) ((Y)*_columns+(X))
78 79
#endif

Waldo Bastian's avatar
Waldo Bastian committed
80
#define REPCHAR   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
Kurt Hindenburg's avatar
Kurt Hindenburg committed
81 82
    "abcdefgjijklmnopqrstuvwxyz" \
    "0123456789./+@"
83

84
// we use this to force QPainter to display text in LTR mode
Kurt Hindenburg's avatar
Kurt Hindenburg committed
85 86
// more information can be found in: http://unicode.org/reports/tr9/
const QChar LTR_OVERRIDE_CHAR(0x202D);
87

88 89 90 91 92 93 94 95 96 97 98 99 100 101
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                                Colors                                     */
/*                                                                           */
/* ------------------------------------------------------------------------- */

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

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

102
ScreenWindow* TerminalDisplay::screenWindow() const
103 104 105
{
    return _screenWindow;
}
106
void TerminalDisplay::setScreenWindow(ScreenWindow* window)
107
{
108
    // disconnect existing screen window if any
Kurt Hindenburg's avatar
Kurt Hindenburg committed
109 110
    if (_screenWindow) {
        disconnect(_screenWindow , 0 , this , 0);
111 112
    }

113
    _screenWindow = window;
114

Kurt Hindenburg's avatar
Kurt Hindenburg committed
115
    if (_screenWindow) {
116 117 118
        connect(_screenWindow.data() , &Konsole::ScreenWindow::outputChanged , this , &Konsole::TerminalDisplay::updateLineProperties);
        connect(_screenWindow.data() , &Konsole::ScreenWindow::outputChanged , this , &Konsole::TerminalDisplay::updateImage);
        connect(_screenWindow.data() , &Konsole::ScreenWindow::currentResultLineChanged , this , &Konsole::TerminalDisplay::updateImage);
119
        connect(_screenWindow.data(), &Konsole::ScreenWindow::outputChanged, this, [this]() {
120 121
            _filterUpdateRequired = true;
        });
122
        connect(_screenWindow.data(), &Konsole::ScreenWindow::scrolled, this, [this]() {
123 124
            _filterUpdateRequired = true;
        });
Jekyll Wu's avatar
Jekyll Wu committed
125
        _screenWindow->setWindowLines(_lines);
126
    }
127 128
}

129
const ColorEntry* TerminalDisplay::colorTable() const
130
{
Jekyll Wu's avatar
Jekyll Wu committed
131
    return _colorTable;
132
}
133 134
void TerminalDisplay::setBackgroundColor(const QColor& color)
{
135
    _colorTable[DEFAULT_BACK_COLOR] = color;
Jekyll Wu's avatar
Jekyll Wu committed
136

137
    QPalette p = palette();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
138 139
    p.setColor(backgroundRole(), color);
    setPalette(p);
140

Jekyll Wu's avatar
Jekyll Wu committed
141
    // Avoid propagating the palette change to the scroll bar
Kurt Hindenburg's avatar
Kurt Hindenburg committed
142
    _scrollBar->setPalette(QApplication::palette());
143

144
    update();
145
}
146 147
QColor TerminalDisplay::getBackgroundColor() const
{
148 149 150
    QPalette p = palette();
    return p.color(backgroundRole());
}
151 152
void TerminalDisplay::setForegroundColor(const QColor& color)
{
153
    _colorTable[DEFAULT_FORE_COLOR] = color;
154

155
    update();
156
}
157
void TerminalDisplay::setColorTable(const ColorEntry table[])
158
{
Jekyll Wu's avatar
Jekyll Wu committed
159 160
    for (int i = 0; i < TABLE_COLORS; i++)
        _colorTable[i] = table[i];
161

162
    setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR]);
163 164 165 166 167 168 169 170
}

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

171 172
static inline bool isLineCharString(const QString& string)
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
173
    if (string.length() == 0)
174 175
        return false;

Kurt Hindenburg's avatar
Kurt Hindenburg committed
176
    return isSupportedLineChar(string.at(0).unicode());
177
}
178

179
void TerminalDisplay::fontChange(const QFont&)
180
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
181 182
    QFontMetrics fm(font());
    _fontHeight = fm.height() + _lineSpacing;
183

Kurt Hindenburg's avatar
Kurt Hindenburg committed
184 185 186 187
    // waba TerminalDisplay 1.123:
    // "Base character width on widest ASCII character. This prevents too wide
    //  characters in the presence of double wide (e.g. Japanese) characters."
    // Get the width from representative normal width characters
188
    _fontWidth = qRound((static_cast<double>(fm.width(REPCHAR)) / static_cast<double>(qstrlen(REPCHAR))));
Waldo Bastian's avatar
Waldo Bastian committed
189

Kurt Hindenburg's avatar
Kurt Hindenburg committed
190
    _fixedFont = true;
191

Jekyll Wu's avatar
Jekyll Wu committed
192
    const int fw = fm.width(REPCHAR[0]);
193
    for (unsigned int i = 1; i < qstrlen(REPCHAR); i++) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
194 195 196 197
        if (fw != fm.width(REPCHAR[i])) {
            _fixedFont = false;
            break;
        }
198
    }
Stephan Kulow's avatar
Stephan Kulow committed
199

Kurt Hindenburg's avatar
Kurt Hindenburg committed
200 201
    if (_fontWidth < 1)
        _fontWidth = 1;
202

Kurt Hindenburg's avatar
Kurt Hindenburg committed
203
    _fontAscent = fm.ascent();
204

Kurt Hindenburg's avatar
Kurt Hindenburg committed
205 206 207
    emit changedFontMetricSignal(_fontHeight, _fontWidth);
    propagateSize();
    update();
208 209
}

210
void TerminalDisplay::setVTFont(const QFont& f)
211
{
212 213
    QFont newFont(f);
    QFontMetrics fontMetrics(newFont);
Robert Knight's avatar
Robert Knight committed
214

215 216 217
    // This check seems extreme and semi-random
    if ((fontMetrics.height() > height()) || (fontMetrics.maxWidth() > width()))
        return;
Robert Knight's avatar
Robert Knight committed
218

219 220 221 222
    // hint that text should be drawn without anti-aliasing.
    // depending on the user's font configuration, this may not be respected
    if (!_antialiasText)
        newFont.setStyleStrategy(QFont::StyleStrategy(newFont.styleStrategy() | QFont::NoAntialias));
223

224 225 226 227
    // experimental optimization.  Konsole assumes that the terminal is using a
    // mono-spaced font, in which case kerning information should have an effect.
    // Disabling kerning saves some computation when rendering text.
    newFont.setKerning(false);
228

229 230
    // Konsole cannot handle non-integer font metrics
    newFont.setStyleStrategy(QFont::StyleStrategy(newFont.styleStrategy() | QFont::ForceIntegerMetrics));
231

232
    QFontInfo fontInfo(newFont);
Robert Knight's avatar
 
Robert Knight committed
233

234 235 236
//    if (!fontInfo.fixedPitch()) {
//        qWarning() << "Using a variable-width font - this might cause display problems";
//    }
237

238 239 240 241 242 243 244
    // QFontInfo::fixedPitch() appears to not match QFont::fixedPitch()
    // related?  https://bugreports.qt.io/browse/QTBUG-34082
    if (!fontInfo.exactMatch()) {
        const QChar comma(QLatin1Char(','));
        QString nonMatching = fontInfo.family() % comma %
            QString::number(fontInfo.pointSizeF()) % comma %
            QString::number(fontInfo.pixelSize()) % comma %
245
            QString::number(static_cast<int>(fontInfo.styleHint())) % comma %
246
            QString::number(fontInfo.weight()) % comma %
247 248 249 250 251
            QString::number(static_cast<int>(fontInfo.style())) % comma %
            QString::number(static_cast<int>(fontInfo.underline())) % comma %
            QString::number(static_cast<int>(fontInfo.strikeOut())) % comma %
            QString::number(static_cast<int>(fontInfo.fixedPitch())) % comma %
            QString::number(static_cast<int>(fontInfo.rawMode()));
252

253 254 255
        qCDebug(KonsoleDebug) << "The font to use in the terminal can not be matched exactly on your system.";
        qCDebug(KonsoleDebug)<<" Selected: "<<newFont.toString();
        qCDebug(KonsoleDebug)<<" System  : "<<nonMatching;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
256
    }
257 258 259

    QWidget::setFont(newFont);
    fontChange(newFont);
260 261
}

262
void TerminalDisplay::setFont(const QFont &)
263
{
264
    // ignore font change request if not coming from konsole itself
265 266
}

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
void TerminalDisplay::increaseFontSize()
{
    QFont font = getVTFont();
    font.setPointSizeF(font.pointSizeF() + 1);
    setVTFont(font);
}

void TerminalDisplay::decreaseFontSize()
{
    const qreal MinimumFontSize = 6;

    QFont font = getVTFont();
    font.setPointSizeF(qMax(font.pointSizeF() - 1, MinimumFontSize));
    setVTFont(font);
}

Jekyll Wu's avatar
Jekyll Wu committed
283 284 285 286 287 288 289 290 291 292 293
uint TerminalDisplay::lineSpacing() const
{
    return _lineSpacing;
}

void TerminalDisplay::setLineSpacing(uint i)
{
    _lineSpacing = i;
    setVTFont(font()); // Trigger an update.
}

294 295 296 297 298 299 300

/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                         Accessibility                                     */
/*                                                                           */
/* ------------------------------------------------------------------------- */

301 302
namespace Konsole
{
Alex Richardson's avatar
Alex Richardson committed
303 304

#ifndef QT_NO_ACCESSIBILITY
Kurt Hindenburg's avatar
Kurt Hindenburg committed
305
/**
306
 * This function installs the factory function which lets Qt instantiate the QAccessibleInterface
Kurt Hindenburg's avatar
Kurt Hindenburg committed
307 308 309 310 311 312 313 314 315
 * for the TerminalDisplay.
 */
QAccessibleInterface* accessibleInterfaceFactory(const QString &key, QObject *object)
{
    Q_UNUSED(key)
    if (TerminalDisplay *display = qobject_cast<TerminalDisplay*>(object))
        return new TerminalDisplayAccessible(display);
    return 0;
}
Alex Richardson's avatar
Alex Richardson committed
316 317

#endif
318 319
}

320 321 322 323 324 325
/* ------------------------------------------------------------------------- */
/*                                                                           */
/*                         Constructor / Destructor                          */
/*                                                                           */
/* ------------------------------------------------------------------------- */

326
TerminalDisplay::TerminalDisplay(QWidget* parent)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
327 328
    : QWidget(parent)
    , _screenWindow(0)
Jekyll Wu's avatar
Jekyll Wu committed
329
    , _bellMasked(false)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
    , _gridLayout(0)
    , _fontHeight(1)
    , _fontWidth(1)
    , _fontAscent(1)
    , _boldIntense(true)
    , _lines(1)
    , _columns(1)
    , _usedLines(1)
    , _usedColumns(1)
    , _image(0)
    , _randomSeed(0)
    , _resizing(false)
    , _showTerminalSizeHint(true)
    , _bidiEnabled(false)
    , _actSel(0)
    , _wordSelectionMode(false)
    , _lineSelectionMode(false)
    , _preserveLineBreaks(false)
    , _columnSelectionMode(false)
349 350
    , _autoCopySelectedText(false)
    , _middleClickPasteMode(Enum::PasteFromX11Selection)
351
    , _scrollbarLocation(Enum::ScrollBarRight)
352
    , _scrollFullPage(false)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
353
    , _wordCharacters(":@-./_~")
354
    , _bellMode(Enum::NotifyBell)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
355 356 357 358 359
    , _allowBlinkingText(true)
    , _allowBlinkingCursor(false)
    , _textBlinking(false)
    , _cursorBlinking(false)
    , _hasTextBlinker(false)
360
    , _urlHintsModifiers(Qt::NoModifier)
361
    , _showUrlHint(false)
362
    , _openLinksByDirectClick(false)
363
    , _ctrlRequiredForDrag(true)
364
    , _tripleClickMode(Enum::SelectWholeLine)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
365 366 367 368 369 370 371 372
    , _possibleTripleClick(false)
    , _resizeWidget(0)
    , _resizeTimer(0)
    , _flowControlWarningEnabled(false)
    , _outputSuspendedLabel(0)
    , _lineSpacing(0)
    , _blendColor(qRgba(0, 0, 0, 0xff))
    , _filterChain(new TerminalImageFilterChain())
373
    , _filterUpdateRequired(true)
374
    , _cursorShape(Enum::BlockCursor)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
375
    , _antialiasText(true)
376
    , _useFontLineCharacters(false)
377
    , _printerFriendly(false)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
378
    , _sessionController(0)
379
    , _trimTrailingSpaces(false)
380 381
    , _margin(1)
    , _centerContents(false)
382
    , _opacity(1.0)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
383 384 385 386 387
{
    // terminal applications are not designed with Right-To-Left in mind,
    // so the layout is forced to Left-To-Right
    setLayoutDirection(Qt::LeftToRight);

388
    _contentRect = QRect(_margin, _margin, 1, 1);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
389 390 391 392 393 394

    // create scroll bar for scrolling output up and down
    _scrollBar = new QScrollBar(this);
    // set the scroll bar's slider to occupy the whole area of the scroll bar initially
    setScroll(0, 0);
    _scrollBar->setCursor(Qt::ArrowCursor);
Laurent Montel's avatar
Laurent Montel committed
395 396
    connect(_scrollBar, &QScrollBar::valueChanged, this, &Konsole::TerminalDisplay::scrollBarPositionChanged);
    connect(_scrollBar, &QScrollBar::sliderMoved, this, &Konsole::TerminalDisplay::viewScrolledByUser);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
397

398 399 400
    // setup timers for blinking text
    _blinkTextTimer = new QTimer(this);
    _blinkTextTimer->setInterval(TEXT_BLINK_DELAY);
401
    connect(_blinkTextTimer, &QTimer::timeout, this, &Konsole::TerminalDisplay::blinkTextEvent);
402 403 404 405

    // setup timers for blinking cursor
    _blinkCursorTimer = new QTimer(this);
    _blinkCursorTimer->setInterval(QApplication::cursorFlashTime() / 2);
406
    connect(_blinkCursorTimer, &QTimer::timeout, this, &Konsole::TerminalDisplay::blinkCursorEvent);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
407 408 409 410 411 412

    // hide mouse cursor on keystroke or idle
    KCursor::setAutoHideCursor(this, true);
    setMouseTracking(true);

    setUsesMouse(true);
413
    setBracketedPasteMode(false);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
414

415
    setColorTable(ColorScheme::defaultTable);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
416 417 418 419 420 421 422 423 424 425 426 427 428 429

    // Enable drag and drop support
    setAcceptDrops(true);
    _dragInfo.state = diNone;

    setFocusPolicy(Qt::WheelFocus);

    // enable input method support
    setAttribute(Qt::WA_InputMethodEnabled, true);

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

430
    _gridLayout = new QGridLayout;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
431 432 433 434 435
    _gridLayout->setContentsMargins(0, 0, 0, 0);

    setLayout(_gridLayout);

    new AutoScrollHandler(this);
436 437 438


#ifndef QT_NO_ACCESSIBILITY
Kurt Hindenburg's avatar
Kurt Hindenburg committed
439
    QAccessible::installFactory(Konsole::accessibleInterfaceFactory);
440
#endif
441 442
}

443
TerminalDisplay::~TerminalDisplay()
444
{
445 446 447 448 449
    disconnect(_blinkTextTimer);
    disconnect(_blinkCursorTimer);

    delete[] _image;
    delete _filterChain;
450 451 452 453 454 455 456 457
}

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

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

464 465 466 467 468 469 470 471 472 473 474
 Then, the pixels basically have the following interpretation:
 _|||_
 -...-
 -...-
 -...-
 _|||_

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
enum LineEncode {
    TopL  = (1 << 1),
    TopC  = (1 << 2),
    TopR  = (1 << 3),

    LeftT = (1 << 5),
    Int11 = (1 << 6),
    Int12 = (1 << 7),
    Int13 = (1 << 8),
    RightT = (1 << 9),

    LeftC = (1 << 10),
    Int21 = (1 << 11),
    Int22 = (1 << 12),
    Int23 = (1 << 13),
    RightC = (1 << 14),

    LeftB = (1 << 15),
    Int31 = (1 << 16),
    Int32 = (1 << 17),
    Int33 = (1 << 18),
    RightB = (1 << 19),

    BotL  = (1 << 21),
    BotC  = (1 << 22),
    BotR  = (1 << 23)
502 503 504 505 506
};

static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code)
{
    //Calculate cell midpoints, end points.
Jekyll Wu's avatar
Jekyll Wu committed
507 508 509 510
    const int cx = x + w / 2;
    const int cy = y + h / 2;
    const int ex = x + w - 1;
    const int ey = y + h - 1;
511

Jekyll Wu's avatar
Jekyll Wu committed
512
    const quint32 toDraw = LineChars[code];
Stephan Kulow's avatar
Stephan Kulow committed
513

514
    //Top _lines:
515
    if ((toDraw & TopL) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
516
        paint.drawLine(cx - 1, y, cx - 1, cy - 2);
517
    if ((toDraw & TopC) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
518
        paint.drawLine(cx, y, cx, cy - 2);
519
    if ((toDraw & TopR) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
520
        paint.drawLine(cx + 1, y, cx + 1, cy - 2);
521

522
    //Bot _lines:
523
    if ((toDraw & BotL) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
524
        paint.drawLine(cx - 1, cy + 2, cx - 1, ey);
525
    if ((toDraw & BotC) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
526
        paint.drawLine(cx, cy + 2, cx, ey);
527
    if ((toDraw & BotR) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
528
        paint.drawLine(cx + 1, cy + 2, cx + 1, ey);
529

530
    //Left _lines:
531
    if ((toDraw & LeftT) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
532
        paint.drawLine(x, cy - 1, cx - 2, cy - 1);
533
    if ((toDraw & LeftC) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
534
        paint.drawLine(x, cy, cx - 2, cy);
535
    if ((toDraw & LeftB) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
536
        paint.drawLine(x, cy + 1, cx - 2, cy + 1);
537

538
    //Right _lines:
539
    if ((toDraw & RightT) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
540
        paint.drawLine(cx + 2, cy - 1, ex, cy - 1);
541
    if ((toDraw & RightC) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
542
        paint.drawLine(cx + 2, cy, ex, cy);
543
    if ((toDraw & RightB) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
544
        paint.drawLine(cx + 2, cy + 1, ex, cy + 1);
Stephan Kulow's avatar
Stephan Kulow committed
545

546
    //Intersection points.
547
    if ((toDraw & Int11) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
548
        paint.drawPoint(cx - 1, cy - 1);
549
    if ((toDraw & Int12) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
550
        paint.drawPoint(cx, cy - 1);
551
    if ((toDraw & Int13) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
552
        paint.drawPoint(cx + 1, cy - 1);
Stephan Kulow's avatar
Stephan Kulow committed
553

554
    if ((toDraw & Int21) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
555
        paint.drawPoint(cx - 1, cy);
556
    if ((toDraw & Int22) != 0u)
557
        paint.drawPoint(cx, cy);
558
    if ((toDraw & Int23) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
559
        paint.drawPoint(cx + 1, cy);
560

561
    if ((toDraw & Int31) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
562
        paint.drawPoint(cx - 1, cy + 1);
563
    if ((toDraw & Int32) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
564
        paint.drawPoint(cx, cy + 1);
565
    if ((toDraw & Int33) != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
566
        paint.drawPoint(cx + 1, cy + 1);
567 568
}

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
static void drawOtherChar(QPainter& paint, int x, int y, int w, int h, uchar code)
{
    //Calculate cell midpoints, end points.
    const int cx = x + w / 2;
    const int cy = y + h / 2;
    const int ex = x + w - 1;
    const int ey = y + h - 1;

    // Double dashes
    if (0x4C <= code && code <= 0x4F) {
        const int xHalfGap = qMax(w / 15, 1);
        const int yHalfGap = qMax(h / 15, 1);
        switch (code) {
        case 0x4D: // BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL
            paint.drawLine(x, cy - 1, cx - xHalfGap - 1, cy - 1);
            paint.drawLine(x, cy + 1, cx - xHalfGap - 1, cy + 1);
            paint.drawLine(cx + xHalfGap, cy - 1, ex, cy - 1);
            paint.drawLine(cx + xHalfGap, cy + 1, ex, cy + 1);
            // No break!
        case 0x4C: // BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL
            paint.drawLine(x, cy, cx - xHalfGap - 1, cy);
            paint.drawLine(cx + xHalfGap, cy, ex, cy);
            break;
        case 0x4F: // BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL
            paint.drawLine(cx - 1, y, cx - 1, cy - yHalfGap - 1);
            paint.drawLine(cx + 1, y, cx + 1, cy - yHalfGap - 1);
            paint.drawLine(cx - 1, cy + yHalfGap, cx - 1, ey);
            paint.drawLine(cx + 1, cy + yHalfGap, cx + 1, ey);
            // No break!
        case 0x4E: // BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL
            paint.drawLine(cx, y, cx, cy - yHalfGap - 1);
            paint.drawLine(cx, cy + yHalfGap, cx, ey);
            break;
        }
    }

    // Rounded corner characters
    else if (0x6D <= code && code <= 0x70) {
        const int r = w * 3 / 8;
        const int d = 2 * r;
        switch (code) {
        case 0x6D: // BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
            paint.drawLine(cx, cy + r, cx, ey);
            paint.drawLine(cx + r, cy, ex, cy);
            paint.drawArc(cx, cy, d, d, 90 * 16, 90 * 16);
            break;
        case 0x6E: // BOX DRAWINGS LIGHT ARC DOWN AND LEFT
            paint.drawLine(cx, cy + r, cx, ey);
            paint.drawLine(x, cy, cx - r, cy);
            paint.drawArc(cx - d, cy, d, d, 0 * 16, 90 * 16);
            break;
        case 0x6F: // BOX DRAWINGS LIGHT ARC UP AND LEFT
            paint.drawLine(cx, y, cx, cy - r);
            paint.drawLine(x, cy, cx - r, cy);
            paint.drawArc(cx - d, cy - d, d, d, 270 * 16, 90 * 16);
            break;
        case 0x70: // BOX DRAWINGS LIGHT ARC UP AND RIGHT
            paint.drawLine(cx, y, cx, cy - r);
            paint.drawLine(cx + r, cy, ex, cy);
            paint.drawArc(cx, cy - d, d, d, 180 * 16, 90 * 16);
            break;
        }
    }

    // Diagonals
    else if (0x71 <= code && code <= 0x73) {
        switch (code) {
        case 0x71: // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
            paint.drawLine(ex, y, x, ey);
            break;
        case 0x72: // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
            paint.drawLine(x, y, ex, ey);
            break;
        case 0x73: // BOX DRAWINGS LIGHT DIAGONAL CROSS
            paint.drawLine(ex, y, x, ey);
            paint.drawLine(x, y, ex, ey);
            break;
        }
    }
}

650
void TerminalDisplay::drawLineCharString(QPainter& painter, int x, int y, const QString& str,
Kurt Hindenburg's avatar
Kurt Hindenburg committed
651
        const Character* attributes)
652
{
653 654
    const QPen& originalPen = painter.pen();

655
    if (((attributes->rendition & RE_BOLD) != 0) && _boldIntense) {
656 657
        QPen boldPen(originalPen);
        boldPen.setWidth(3);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
658
        painter.setPen(boldPen);
659 660
    }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
661
    for (int i = 0 ; i < str.length(); i++) {
Jekyll Wu's avatar
Jekyll Wu committed
662
        const uchar code = str[i].cell();
663
        if (LineChars[code] != 0u)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
664
            drawLineChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
665 666
        else
            drawOtherChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
667 668
    }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
669
    painter.setPen(originalPen);
670 671
}

672
void TerminalDisplay::setKeyboardCursorShape(Enum::CursorShapeEnum shape)
673 674 675
{
    _cursorShape = shape;
}
676
Enum::CursorShapeEnum TerminalDisplay::keyboardCursorShape() const
677 678 679
{
    return _cursorShape;
}
680
void TerminalDisplay::setKeyboardCursorColor(const QColor& color)
681
{
682
    _cursorColor = color;
683 684 685 686 687
}
QColor TerminalDisplay::keyboardCursorColor() const
{
    return _cursorColor;
}
688

689 690 691 692
void TerminalDisplay::setOpacity(qreal opacity)
{
    QColor color(_blendColor);
    color.setAlphaF(opacity);
693
    _opacity = opacity;
694 695 696

    // enable automatic background filling to prevent the display
    // flickering if there is no transparency
Kurt Hindenburg's avatar
Kurt Hindenburg committed
697
    /*if ( color.alpha() == 255 )
698 699 700 701 702 703 704 705
    {
        setAutoFillBackground(true);
    }
    else
    {
        setAutoFillBackground(false);
    }*/

706 707 708
    _blendColor = color.rgba();
}

709 710
void TerminalDisplay::setWallpaper(ColorSchemeWallpaper::Ptr p)
{
711
    _wallpaper = p;
712 713
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
714
void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting)
715
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
    // the area of the widget showing the contents of the terminal display is drawn
    // using the background color from the color scheme set with setColorTable()
    //
    // the area of the widget behind the scroll-bar is drawn using the background
    // brush from the scroll-bar's palette, to give the effect of the scroll-bar
    // being outside of the terminal display and visual consistency with other KDE
    // applications.
    //
    QRect scrollBarArea = _scrollBar->isVisible() ?
                          rect.intersected(_scrollBar->geometry()) :
                          QRect();
    QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea);
    QRect contentsRect = contentsRegion.boundingRect();

    if (useOpacitySetting && !_wallpaper->isNull() &&
731
            _wallpaper->draw(painter, contentsRect, _opacity)) {
732
    } else if (qAlpha(_blendColor) < 0xff && useOpacitySetting) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
733
#if defined(Q_OS_OSX)
734
        // TODO - On MacOS, using CompositionMode doesn't work.  Altering the
735 736 737
        //        transparency in the color scheme alters the brightness.
        painter.fillRect(contentsRect, backgroundColor);
#else
Kurt Hindenburg's avatar
Kurt Hindenburg committed
738 739 740 741 742 743 744
        QColor color(backgroundColor);
        color.setAlpha(qAlpha(_blendColor));

        painter.save();
        painter.setCompositionMode(QPainter::CompositionMode_Source);
        painter.fillRect(contentsRect, color);
        painter.restore();
745
#endif
Kurt Hindenburg's avatar
Kurt Hindenburg committed
746 747 748
    } else {
        painter.fillRect(contentsRect, backgroundColor);
    }
749

Kurt Hindenburg's avatar
Kurt Hindenburg committed
750
    painter.fillRect(scrollBarArea, _scrollBar->palette().background());
751 752
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
753
void TerminalDisplay::drawCursor(QPainter& painter,
754 755 756 757 758
                                 const QRect& rect,
                                 const QColor& foregroundColor,
                                 const QColor& /*backgroundColor*/,
                                 bool& invertCharacterColor)
{
Jekyll Wu's avatar
Jekyll Wu committed
759
    // don't draw cursor which is currently blinking
Kurt Hindenburg's avatar
Kurt Hindenburg committed
760
    if (_cursorBlinking)
761
        return;
Jekyll Wu's avatar
Jekyll Wu committed
762

763 764 765
    // shift rectangle top down one pixel to leave some space
    // between top and bottom
    QRect cursorRect = rect.adjusted(0, 1, 0, 0);
Jekyll Wu's avatar
Jekyll Wu committed
766

767
    QColor cursorColor = _cursorColor.isValid() ? _cursorColor : foregroundColor;
Jekyll Wu's avatar
Jekyll Wu committed
768 769
    painter.setPen(cursorColor);

770
    if (_cursorShape == Enum::BlockCursor) {
Jekyll Wu's avatar
Jekyll Wu committed
771 772
        // draw the cursor outline, adjusting the area so that
        // it is draw entirely inside 'rect'
Kurt Hindenburg's avatar
Kurt Hindenburg committed
773 774 775 776 777
        int penWidth = qMax(1, painter.pen().width());
        painter.drawRect(cursorRect.adjusted(penWidth / 2,
                                             penWidth / 2,
                                             - penWidth / 2 - penWidth % 2,
                                             - penWidth / 2 - penWidth % 2));
Jekyll Wu's avatar
Jekyll Wu committed
778 779

        // draw the cursor body only when the widget has focus
Kurt Hindenburg's avatar
Kurt Hindenburg committed
780
        if (hasFocus()) {
Jekyll Wu's avatar
Jekyll Wu committed
781 782
            painter.fillRect(cursorRect, cursorColor);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
783
            if (!_cursorColor.isValid()) {
Jekyll Wu's avatar
Jekyll Wu committed
784
                // invert the color used to draw the text to ensure that the character at
Jekyll Wu's avatar
Jekyll Wu committed
785 786
                // the cursor position is readable
                invertCharacterColor = true;
787
            }
Jekyll Wu's avatar
Jekyll Wu committed
788
        }
789
    } else if (_cursorShape == Enum::UnderlineCursor) {
Jekyll Wu's avatar
Jekyll Wu committed
790 791 792 793 794
        painter.drawLine(cursorRect.left(),
                         cursorRect.bottom(),
                         cursorRect.right(),
                         cursorRect.bottom());

795
    } else if (_cursorShape == Enum::IBeamCursor) {
Jekyll Wu's avatar
Jekyll Wu committed
796 797 798 799
        painter.drawLine(cursorRect.left(),
                         cursorRect.top(),
                         cursorRect.left(),
                         cursorRect.bottom());
800
    }
801 802 803 804 805 806 807 808 809
}

void TerminalDisplay::drawCharacters(QPainter& painter,
                                     const QRect& rect,
                                     const QString& text,
                                     const Character* style,
                                     bool invertCharacterColor)
{
    // don't draw text which is currently blinking
810
    if (_textBlinking && ((style->rendition & RE_BLINK) != 0))
811 812
        return;

813
    // don't draw concealed characters
814
    if ((style->rendition & RE_CONCEAL) != 0)
815 816
        return;

Robert Knight's avatar
 
Robert Knight committed
817
    // setup bold and underline
818 819 820 821 822
    bool useBold = (((style->rendition & RE_BOLD) != 0) && _boldIntense) || font().bold();
    const bool useUnderline = ((style->rendition & RE_UNDERLINE) != 0) || font().underline();
    const bool useItalic = ((style->rendition & RE_ITALIC) != 0) || font().italic();
    const bool useStrikeOut = ((style->rendition & RE_STRIKEOUT) != 0) || font().strikeOut();
    const bool useOverline = ((style->rendition & RE_OVERLINE) != 0) || font().overline();
Robert Knight's avatar
 
Robert Knight committed
823

824
    QFont font = painter.font();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
825
    if (font.bold() != useBold
826
            || font.underline() != useUnderline
827 828 829
            || font.italic() != useItalic
            || font.strikeOut() != useStrikeOut
            || font.overline() != useOverline) {
830 831
        font.setBold(useBold);
        font.setUnderline(useUnderline);
832
        font.setItalic(useItalic);
833 834
        font.setStrikeOut(useStrikeOut);
        font.setOverline(useOverline);
835
        painter.setFont(font);
836 837 838
    }

    // setup pen
Kurt Hindenburg's avatar
Kurt Hindenburg committed
839
    const CharacterColor& textColor = (invertCharacterColor ? style->backgroundColor : style->foregroundColor);
840 841
    const QColor color = textColor.color(_colorTable);
    QPen pen = painter.pen();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
842
    if (pen.color() != color) {
843 844 845 846 847
        pen.setColor(color);
        painter.setPen(color);
    }

    // draw text
848
    if (isLineCharString(text) && !_useFontLineCharacters) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
849 850
        drawLineCharString(painter, rect.x(), rect.y(), text, style);
    } else {
851 852 853 854
        // Force using LTR as the document layout for the terminal area, because
        // there is no use cases for RTL emulator and RTL terminal application.
        //
        // This still allows RTL characters to be rendered in the RTL way.
855
        painter.setLayoutDirection(Qt::LeftToRight);
856

857
        if (_bidiEnabled) {
858
            painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, text);
859
        } else {
860
            painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, LTR_OVERRIDE_CHAR + text);
861
        }
862
    }
863 864
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
865
void TerminalDisplay::drawTextFragment(QPainter& painter ,
866
                                       const QRect& rect,
Kurt Hindenburg's avatar
Kurt Hindenburg committed
867
                                       const QString& text,
868 869 870 871
                                       const Character* style)
{
    painter.save();

Kurt Hindenburg's avatar
Kurt Hindenburg committed
872
    // setup painter
873 874
    const QColor foregroundColor = style->foregroundColor.color(_colorTable);
    const QColor backgroundColor = style->backgroundColor.color(_colorTable);
875

876
    // draw background if different from the display's background color
Kurt Hindenburg's avatar
Kurt Hindenburg committed
877 878
    if (backgroundColor != palette().background().color())
        drawBackground(painter, rect, backgroundColor,
879
                       false /* do not use transparency */);
880 881 882 883

    // draw cursor shape if the current character is the cursor
    // this may alter the foreground and background colors
    bool invertCharacterColor = false;
884
    if ((style->rendition & RE_CURSOR) != 0)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
885
        drawCursor(painter, rect, foregroundColor, backgroundColor, invertCharacterColor);
886 887

    // draw text
Kurt Hindenburg's avatar
Kurt Hindenburg committed
888
    drawCharacters(painter, rect, text, style, invertCharacterColor);
889 890 891 892

    painter.restore();
}

893
void TerminalDisplay::drawPrinterFriendlyTextFragment(QPainter& painter,
894 895 896
        const QRect& rect,
        const QString& text,
        const Character* style)
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
{
    painter.save();

    // Set the colors used to draw to black foreground and white
    // background for printer friendly output when printing
    Character print_style = *style;
    print_style.foregroundColor = CharacterColor(COLOR_SPACE_RGB, 0x00000000);
    print_style.backgroundColor = CharacterColor(COLOR_SPACE_RGB, 0xFFFFFFFF);

    // draw text
    drawCharacters(painter, rect, text, &print_style, false);

    painter.restore();
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
912 913