Changeset View
Changeset View
Standalone View
Standalone View
src/TerminalDisplay.cpp
Show All 26 Lines | |||||
27 | #include <config-konsole.h> | 27 | #include <config-konsole.h> | ||
28 | 28 | | |||
29 | // Qt | 29 | // Qt | ||
30 | #include <QApplication> | 30 | #include <QApplication> | ||
31 | #include <QClipboard> | 31 | #include <QClipboard> | ||
32 | #include <QKeyEvent> | 32 | #include <QKeyEvent> | ||
33 | #include <QEvent> | 33 | #include <QEvent> | ||
34 | #include <QFileInfo> | 34 | #include <QFileInfo> | ||
35 | #include <QGridLayout> | 35 | #include <QVBoxLayout> | ||
36 | #include <QAction> | 36 | #include <QAction> | ||
37 | #include <QFontDatabase> | 37 | #include <QFontDatabase> | ||
38 | #include <QLabel> | 38 | #include <QLabel> | ||
39 | #include <QMimeData> | 39 | #include <QMimeData> | ||
40 | #include <QPainter> | 40 | #include <QPainter> | ||
41 | #include <QPixmap> | 41 | #include <QPixmap> | ||
42 | #include <QScrollBar> | 42 | #include <QScrollBar> | ||
43 | #include <QStyle> | 43 | #include <QStyle> | ||
44 | #include <QTimer> | 44 | #include <QTimer> | ||
45 | #include <QDrag> | 45 | #include <QDrag> | ||
46 | #include <QDesktopServices> | 46 | #include <QDesktopServices> | ||
47 | #include <QAccessible> | 47 | #include <QAccessible> | ||
48 | 48 | | |||
49 | // KDE | 49 | // KDE | ||
50 | #include <KShell> | 50 | #include <KShell> | ||
51 | #include <KColorScheme> | 51 | #include <KColorScheme> | ||
52 | #include <KCursor> | 52 | #include <KCursor> | ||
53 | #include <KLocalizedString> | 53 | #include <KLocalizedString> | ||
54 | #include <KNotification> | 54 | #include <KNotification> | ||
55 | #include <KIO/DropJob> | 55 | #include <KIO/DropJob> | ||
56 | #include <KJobWidgets> | 56 | #include <KJobWidgets> | ||
57 | #include <KMessageBox> | 57 | #include <KMessageBox> | ||
58 | #include <KMessageWidget> | ||||
58 | #include <KIO/StatJob> | 59 | #include <KIO/StatJob> | ||
59 | 60 | | |||
60 | // Konsole | 61 | // Konsole | ||
61 | #include "Filter.h" | 62 | #include "Filter.h" | ||
62 | #include "konsoledebug.h" | 63 | #include "konsoledebug.h" | ||
63 | #include "konsole_wcwidth.h" | 64 | #include "konsole_wcwidth.h" | ||
64 | #include "TerminalCharacterDecoder.h" | 65 | #include "TerminalCharacterDecoder.h" | ||
65 | #include "Screen.h" | 66 | #include "Screen.h" | ||
▲ Show 20 Lines • Show All 262 Lines • ▼ Show 20 Line(s) | |||||
328 | /* Constructor / Destructor */ | 329 | /* Constructor / Destructor */ | ||
329 | /* */ | 330 | /* */ | ||
330 | /* ------------------------------------------------------------------------- */ | 331 | /* ------------------------------------------------------------------------- */ | ||
331 | 332 | | |||
332 | TerminalDisplay::TerminalDisplay(QWidget* parent) | 333 | TerminalDisplay::TerminalDisplay(QWidget* parent) | ||
333 | : QWidget(parent) | 334 | : QWidget(parent) | ||
334 | , _screenWindow(nullptr) | 335 | , _screenWindow(nullptr) | ||
335 | , _bellMasked(false) | 336 | , _bellMasked(false) | ||
336 | , _gridLayout(nullptr) | 337 | , _verticalLayout(new QVBoxLayout(this)) | ||
337 | , _fontHeight(1) | 338 | , _fontHeight(1) | ||
338 | , _fontWidth(1) | 339 | , _fontWidth(1) | ||
339 | , _fontAscent(1) | 340 | , _fontAscent(1) | ||
340 | , _boldIntense(true) | 341 | , _boldIntense(true) | ||
341 | , _lines(1) | 342 | , _lines(1) | ||
342 | , _columns(1) | 343 | , _columns(1) | ||
343 | , _usedLines(1) | 344 | , _usedLines(1) | ||
344 | , _usedColumns(1) | 345 | , _usedColumns(1) | ||
Show All 23 Lines | |||||
368 | , _showUrlHint(false) | 369 | , _showUrlHint(false) | ||
369 | , _openLinksByDirectClick(false) | 370 | , _openLinksByDirectClick(false) | ||
370 | , _ctrlRequiredForDrag(true) | 371 | , _ctrlRequiredForDrag(true) | ||
371 | , _tripleClickMode(Enum::SelectWholeLine) | 372 | , _tripleClickMode(Enum::SelectWholeLine) | ||
372 | , _possibleTripleClick(false) | 373 | , _possibleTripleClick(false) | ||
373 | , _resizeWidget(nullptr) | 374 | , _resizeWidget(nullptr) | ||
374 | , _resizeTimer(nullptr) | 375 | , _resizeTimer(nullptr) | ||
375 | , _flowControlWarningEnabled(false) | 376 | , _flowControlWarningEnabled(false) | ||
376 | , _outputSuspendedLabel(nullptr) | 377 | , _outputSuspendedMessageWidget(nullptr) | ||
377 | , _lineSpacing(0) | 378 | , _lineSpacing(0) | ||
378 | , _blendColor(qRgba(0, 0, 0, 0xff)) | 379 | , _blendColor(qRgba(0, 0, 0, 0xff)) | ||
379 | , _filterChain(new TerminalImageFilterChain()) | 380 | , _filterChain(new TerminalImageFilterChain()) | ||
380 | , _filterUpdateRequired(true) | 381 | , _filterUpdateRequired(true) | ||
381 | , _cursorShape(Enum::BlockCursor) | 382 | , _cursorShape(Enum::BlockCursor) | ||
382 | , _antialiasText(true) | 383 | , _antialiasText(true) | ||
383 | , _useFontLineCharacters(false) | 384 | , _useFontLineCharacters(false) | ||
384 | , _printerFriendly(false) | 385 | , _printerFriendly(false) | ||
385 | , _sessionController(nullptr) | 386 | , _sessionController(nullptr) | ||
386 | , _trimLeadingSpaces(false) | 387 | , _trimLeadingSpaces(false) | ||
387 | , _trimTrailingSpaces(false) | 388 | , _trimTrailingSpaces(false) | ||
388 | , _margin(1) | 389 | , _margin(1) | ||
389 | , _centerContents(false) | 390 | , _centerContents(false) | ||
391 | , _readOnlyMessageWidget(nullptr) | ||||
390 | , _opacity(1.0) | 392 | , _opacity(1.0) | ||
391 | { | 393 | { | ||
392 | // terminal applications are not designed with Right-To-Left in mind, | 394 | // terminal applications are not designed with Right-To-Left in mind, | ||
393 | // so the layout is forced to Left-To-Right | 395 | // so the layout is forced to Left-To-Right | ||
394 | setLayoutDirection(Qt::LeftToRight); | 396 | setLayoutDirection(Qt::LeftToRight); | ||
395 | 397 | | |||
396 | _contentRect = QRect(_margin, _margin, 1, 1); | 398 | _contentRect = QRect(_margin, _margin, 1, 1); | ||
397 | 399 | | |||
Show All 32 Lines | |||||
430 | 432 | | |||
431 | // enable input method support | 433 | // enable input method support | ||
432 | setAttribute(Qt::WA_InputMethodEnabled, true); | 434 | setAttribute(Qt::WA_InputMethodEnabled, true); | ||
433 | 435 | | |||
434 | // this is an important optimization, it tells Qt | 436 | // this is an important optimization, it tells Qt | ||
435 | // that TerminalDisplay will handle repainting its entire area. | 437 | // that TerminalDisplay will handle repainting its entire area. | ||
436 | setAttribute(Qt::WA_OpaquePaintEvent); | 438 | setAttribute(Qt::WA_OpaquePaintEvent); | ||
437 | 439 | | |||
438 | _gridLayout = new QGridLayout; | 440 | // Add the stretch item once, the KMessageWidgets are inserted at index 0. | ||
439 | _gridLayout->setContentsMargins(0, 0, 0, 0); | 441 | _verticalLayout->addStretch(); | ||
440 | 442 | _verticalLayout->setSpacing(0); | |||
441 | setLayout(_gridLayout); | 443 | | ||
444 | setLayout(_verticalLayout); | ||||
445 | | ||||
446 | // Take the scrollbar into account and add a margin to the layout. Without the timer the scrollbar width | ||||
447 | // is garbage. | ||||
448 | QTimer::singleShot(0, this, [this]() { | ||||
449 | const int scrollBarWidth = _scrollBar->isVisible() ? geometry().intersected(_scrollBar->geometry()).width() : 0; | ||||
450 | _verticalLayout->setContentsMargins(0, 0, scrollBarWidth, 0); | ||||
451 | }); | ||||
442 | 452 | | |||
443 | new AutoScrollHandler(this); | 453 | new AutoScrollHandler(this); | ||
444 | 454 | | |||
445 | 455 | | |||
446 | #ifndef QT_NO_ACCESSIBILITY | 456 | #ifndef QT_NO_ACCESSIBILITY | ||
447 | QAccessible::installFactory(Konsole::accessibleInterfaceFactory); | 457 | QAccessible::installFactory(Konsole::accessibleInterfaceFactory); | ||
448 | #endif | 458 | #endif | ||
449 | } | 459 | } | ||
450 | 460 | | |||
451 | TerminalDisplay::~TerminalDisplay() | 461 | TerminalDisplay::~TerminalDisplay() | ||
452 | { | 462 | { | ||
453 | disconnect(_blinkTextTimer); | 463 | disconnect(_blinkTextTimer); | ||
454 | disconnect(_blinkCursorTimer); | 464 | disconnect(_blinkCursorTimer); | ||
455 | 465 | | |||
456 | delete[] _image; | 466 | delete[] _image; | ||
457 | delete _filterChain; | 467 | delete _filterChain; | ||
468 | delete _readOnlyMessageWidget; | ||||
469 | delete _outputSuspendedMessageWidget; | ||||
470 | | ||||
471 | _readOnlyMessageWidget = nullptr; | ||||
472 | _outputSuspendedMessageWidget = nullptr; | ||||
458 | } | 473 | } | ||
459 | 474 | | |||
460 | /* ------------------------------------------------------------------------- */ | 475 | /* ------------------------------------------------------------------------- */ | ||
461 | /* */ | 476 | /* */ | ||
462 | /* Display Operations */ | 477 | /* Display Operations */ | ||
463 | /* */ | 478 | /* */ | ||
464 | /* ------------------------------------------------------------------------- */ | 479 | /* ------------------------------------------------------------------------- */ | ||
465 | 480 | | |||
▲ Show 20 Lines • Show All 501 Lines • ▼ Show 20 Line(s) | |||||
967 | // display is much cheaper than re-rendering all the text for the | 982 | // display is much cheaper than re-rendering all the text for the | ||
968 | // part of the image which has moved up or down. | 983 | // part of the image which has moved up or down. | ||
969 | // Instead only new lines have to be drawn | 984 | // Instead only new lines have to be drawn | ||
970 | void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) | 985 | void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) | ||
971 | { | 986 | { | ||
972 | // if the flow control warning is enabled this will interfere with the | 987 | // if the flow control warning is enabled this will interfere with the | ||
973 | // scrolling optimizations and cause artifacts. the simple solution here | 988 | // scrolling optimizations and cause artifacts. the simple solution here | ||
974 | // is to just disable the optimization whilst it is visible | 989 | // is to just disable the optimization whilst it is visible | ||
975 | if ((_outputSuspendedLabel != nullptr) && _outputSuspendedLabel->isVisible()) { | 990 | if ((_outputSuspendedMessageWidget != nullptr) && _outputSuspendedMessageWidget->isVisible()) { | ||
991 | return; | ||||
992 | } | ||||
993 | | ||||
994 | if ((_readOnlyMessageWidget != nullptr) && _readOnlyMessageWidget->isVisible()) { | ||||
976 | return; | 995 | return; | ||
977 | } | 996 | } | ||
978 | 997 | | |||
979 | // constrain the region to the display | 998 | // constrain the region to the display | ||
980 | // the bottom of the region is capped to the number of lines in the display's | 999 | // the bottom of the region is capped to the number of lines in the display's | ||
981 | // internal image - 2, so that the height of 'region' is strictly less | 1000 | // internal image - 2, so that the height of 'region' is strictly less | ||
982 | // than the height of the internal image. | 1001 | // than the height of the internal image. | ||
983 | QRect region = screenWindowRegion; | 1002 | QRect region = screenWindowRegion; | ||
▲ Show 20 Lines • Show All 1082 Lines • ▼ Show 20 Line(s) | 2079 | { | |||
2066 | if (!contentsRect().contains(ev->pos())) { | 2085 | if (!contentsRect().contains(ev->pos())) { | ||
2067 | return; | 2086 | return; | ||
2068 | } | 2087 | } | ||
2069 | 2088 | | |||
2070 | if (_screenWindow == nullptr) { | 2089 | if (_screenWindow == nullptr) { | ||
2071 | return; | 2090 | return; | ||
2072 | } | 2091 | } | ||
2073 | 2092 | | |||
2093 | // Ignore clicks on the message widget | ||||
2094 | if (_readOnlyMessageWidget != nullptr) { | ||||
2095 | if (_readOnlyMessageWidget->isVisible() && _readOnlyMessageWidget->frameGeometry().contains(ev->pos())) { | ||||
2096 | return; | ||||
2097 | } | ||||
2098 | } | ||||
2099 | | ||||
2100 | if (_outputSuspendedMessageWidget != nullptr) { | ||||
2101 | if (_outputSuspendedMessageWidget->isVisible() && _outputSuspendedMessageWidget->frameGeometry().contains(ev->pos())) { | ||||
2102 | return; | ||||
2103 | } | ||||
2104 | } | ||||
2105 | | ||||
2074 | int charLine; | 2106 | int charLine; | ||
2075 | int charColumn; | 2107 | int charColumn; | ||
2076 | getCharacterPosition(ev->pos(), charLine, charColumn); | 2108 | getCharacterPosition(ev->pos(), charLine, charColumn); | ||
2077 | QPoint pos = QPoint(charColumn, charLine); | 2109 | QPoint pos = QPoint(charColumn, charLine); | ||
2078 | 2110 | | |||
2079 | if (ev->button() == Qt::LeftButton) { | 2111 | if (ev->button() == Qt::LeftButton) { | ||
2080 | // request the software keyboard, if any | 2112 | // request the software keyboard, if any | ||
2081 | if (qApp->autoSipEnabled()) { | 2113 | if (qApp->autoSipEnabled()) { | ||
▲ Show 20 Lines • Show All 1203 Lines • ▼ Show 20 Line(s) | 3312 | { | |||
3285 | if (!enable) { | 3317 | if (!enable) { | ||
3286 | outputSuspended(false); | 3318 | outputSuspended(false); | ||
3287 | } | 3319 | } | ||
3288 | } | 3320 | } | ||
3289 | 3321 | | |||
3290 | void TerminalDisplay::outputSuspended(bool suspended) | 3322 | void TerminalDisplay::outputSuspended(bool suspended) | ||
3291 | { | 3323 | { | ||
3292 | //create the label when this function is first called | 3324 | //create the label when this function is first called | ||
3293 | if (_outputSuspendedLabel == nullptr) { | 3325 | if (_outputSuspendedMessageWidget == nullptr) { | ||
3294 | //This label includes a link to an English language website | 3326 | //This label includes a link to an English language website | ||
3295 | //describing the 'flow control' (Xon/Xoff) feature found in almost | 3327 | //describing the 'flow control' (Xon/Xoff) feature found in almost | ||
3296 | //all terminal emulators. | 3328 | //all terminal emulators. | ||
3297 | //If there isn't a suitable article available in the target language the link | 3329 | //If there isn't a suitable article available in the target language the link | ||
3298 | //can simply be removed. | 3330 | //can simply be removed. | ||
3299 | _outputSuspendedLabel = new QLabel(i18n("<qt>Output has been " | 3331 | auto linkHandler = [this](const QString &url) { | ||
3300 | "<a href=\"http://en.wikipedia.org/wiki/Software_flow_control\">suspended</a>" | | |||
3301 | " by pressing Ctrl+S." | | |||
3302 | " Press <b>Ctrl+Q</b> to resume." | | |||
3303 | " Click <a href=\"#close\">here</a> to dismiss this message.</qt>")); | | |||
3304 | | ||||
3305 | QPalette palette(_outputSuspendedLabel->palette()); | | |||
3306 | KColorScheme::adjustBackground(palette, KColorScheme::NeutralBackground); | | |||
3307 | _outputSuspendedLabel->setPalette(palette); | | |||
3308 | _outputSuspendedLabel->setAutoFillBackground(true); | | |||
3309 | _outputSuspendedLabel->setBackgroundRole(QPalette::Base); | | |||
3310 | _outputSuspendedLabel->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); | | |||
3311 | _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); | | |||
3312 | _outputSuspendedLabel->setWordWrap(true); | | |||
3313 | _outputSuspendedLabel->setFocusProxy(this); | | |||
3314 | | ||||
3315 | connect(_outputSuspendedLabel, &QLabel::linkActivated, this, [this](const QString &url) { | | |||
3316 | if (url == QLatin1String("#close")) { | 3332 | if (url == QLatin1String("#close")) { | ||
3317 | _outputSuspendedLabel->setVisible(false); | 3333 | _outputSuspendedMessageWidget->hide(); | ||
3318 | } else { | 3334 | } else { | ||
3319 | QDesktopServices::openUrl(QUrl(url)); | 3335 | QDesktopServices::openUrl(QUrl(url)); | ||
3320 | } | 3336 | } | ||
3321 | }); | 3337 | }; | ||
3322 | 3338 | | |||
3323 | //enable activation of "Xon/Xoff" link in label | 3339 | _outputSuspendedMessageWidget = createMessageWidget(i18n("<qt>Output has been " | ||
3324 | _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | | 3340 | "<a href=\"http://en.wikipedia.org/wiki/Software_flow_control\">suspended</a>" | ||
3325 | Qt::LinksAccessibleByKeyboard); | 3341 | " by pressing Ctrl+S." | ||
3326 | _outputSuspendedLabel->setOpenExternalLinks(false); | 3342 | " Press <b>Ctrl+Q</b> to resume." | ||
3327 | _outputSuspendedLabel->setVisible(false); | 3343 | " Click <a href=\"#close\">here</a> to dismiss this message.</qt>"), linkHandler); | ||
3328 | 3344 | | |||
3329 | _gridLayout->addWidget(_outputSuspendedLabel); | 3345 | _outputSuspendedMessageWidget->setMessageType(KMessageWidget::Warning); | ||
3330 | _gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, | 3346 | _outputSuspendedMessageWidget->hide(); | ||
3331 | QSizePolicy::Expanding), | | |||
3332 | 1, 0); | | |||
3333 | } | 3347 | } | ||
3334 | 3348 | | |||
3335 | _outputSuspendedLabel->setVisible(suspended); | 3349 | _outputSuspendedMessageWidget->setVisible(suspended); | ||
3336 | } | 3350 | } | ||
3337 | 3351 | | |||
3338 | void TerminalDisplay::dismissOutputSuspendedMessage() | 3352 | void TerminalDisplay::dismissOutputSuspendedMessage() | ||
3339 | { | 3353 | { | ||
3340 | outputSuspended(false); | 3354 | outputSuspended(false); | ||
3341 | } | 3355 | } | ||
3342 | 3356 | | |||
3357 | KMessageWidget* TerminalDisplay::createMessageWidget(const QString &text, std::function<void (const QString&)> linkHandler) { | ||||
3358 | auto widget = new KMessageWidget(text); | ||||
3359 | widget->setWordWrap(true); | ||||
3360 | widget->setFocusProxy(this); | ||||
3361 | widget->setCloseButtonVisible(false); | ||||
3362 | widget->setCursor(Qt::ArrowCursor); | ||||
3363 | | ||||
3364 | connect(widget, &KMessageWidget::linkActivated, this, linkHandler); | ||||
3365 | | ||||
3366 | _verticalLayout->insertWidget(0, widget); | ||||
3367 | return widget; | ||||
3368 | } | ||||
3369 | | ||||
3370 | void TerminalDisplay::updateReadOnlyState(bool readonly) { | ||||
3371 | | ||||
3372 | if (readonly) { | ||||
3373 | // Lazy create the readonly messagewidget | ||||
3374 | if (_readOnlyMessageWidget == nullptr) { | ||||
3375 | | ||||
3376 | auto linkHandler = [this](const QString &url) { | ||||
3377 | if (url == QLatin1String("#close")) { | ||||
3378 | _readOnlyMessageWidget->hide(); | ||||
3379 | } | ||||
3380 | }; | ||||
3381 | | ||||
3382 | _readOnlyMessageWidget = createMessageWidget(i18n("<qt>This terminal is read-only. <a href=\"#close\">Dismiss</a></qt>"), linkHandler); | ||||
3383 | _readOnlyMessageWidget->setIcon(QIcon::fromTheme(QStringLiteral("object-locked"))); | ||||
3384 | } | ||||
3385 | } | ||||
3386 | | ||||
3387 | if (_readOnlyMessageWidget != nullptr) { | ||||
3388 | _readOnlyMessageWidget->setVisible(readonly); | ||||
3389 | } | ||||
3390 | } | ||||
3391 | | ||||
3343 | void TerminalDisplay::scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount) | 3392 | void TerminalDisplay::scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount) | ||
3344 | { | 3393 | { | ||
3345 | _screenWindow->scrollBy(mode, amount, _scrollFullPage); | 3394 | _screenWindow->scrollBy(mode, amount, _scrollFullPage); | ||
3346 | _screenWindow->setTrackOutput(_screenWindow->atEndOfOutput()); | 3395 | _screenWindow->setTrackOutput(_screenWindow->atEndOfOutput()); | ||
3347 | updateLineProperties(); | 3396 | updateLineProperties(); | ||
3348 | updateImage(); | 3397 | updateImage(); | ||
3349 | viewScrolledByUser(); | 3398 | viewScrolledByUser(); | ||
3350 | } | 3399 | } | ||
▲ Show 20 Lines • Show All 414 Lines • Show Last 20 Lines |