1 /* 2 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com> 3 SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de> 4 5 SPDX-License-Identifier: GPL-2.0-or-later 6 */ 7 8 #ifndef TERMINALDISPLAY_H 9 #define TERMINALDISPLAY_H 10 11 // Qt 12 #include <QColor> 13 #include <QPointer> 14 #include <QWidget> 15 16 #include <memory> 17 18 // Konsole 19 #include "../characters/Character.h" 20 #include "Enumeration.h" 21 #include "ScreenWindow.h" 22 #include "ScrollState.h" 23 #include "colorscheme/ColorScheme.h" 24 #include "konsoleprivate_export.h" 25 #include "widgets/TerminalHeaderBar.h" 26 27 #include "TerminalBell.h" 28 29 class QDrag; 30 class QDragEnterEvent; 31 class QDropEvent; 32 class QLabel; 33 class QTimer; 34 class QEvent; 35 class QVBoxLayout; 36 class QKeyEvent; 37 class QShowEvent; 38 class QHideEvent; 39 class QTimerEvent; 40 class KMessageWidget; 41 42 namespace Konsole 43 { 44 class TerminalPainter; 45 class TerminalScrollBar; 46 class TerminalColor; 47 class TerminalFont; 48 49 class KonsolePrintManager; 50 51 class FilterChain; 52 class TerminalImageFilterChain; 53 class SessionController; 54 class IncrementalSearchBar; 55 class HotSpot; 56 class Profile; 57 58 /** 59 * A widget which displays output from a terminal emulation and sends input keypresses and mouse activity 60 * to the terminal. 61 * 62 * When the terminal emulation receives new output from the program running in the terminal, 63 * it will update the display by calling updateImage(). 64 * 65 * TODO More documentation 66 */ 67 class KONSOLEPRIVATE_EXPORT TerminalDisplay : public QWidget 68 { 69 Q_OBJECT 70 71 public: 72 /** Constructs a new terminal display widget with the specified parent. */ 73 explicit TerminalDisplay(QWidget *parent = nullptr); 74 ~TerminalDisplay() override; 75 76 void showDragTarget(const QPoint &cursorPos); 77 void hideDragTarget(); 78 79 void applyProfile(const QExplicitlySharedDataPointer<Profile> &profile); 80 81 /** 82 * Sets the seed used to generate random colors for the display 83 * (in color schemes that support them). 84 */ 85 void setRandomSeed(uint randomSeed); 86 /** 87 * Returns the seed used to generate random colors for the display 88 * (in color schemes that support them). 89 */ 90 uint randomSeed() const; 91 92 /** Sets the opacity of the terminal display. */ 93 void setOpacity(qreal opacity); 94 95 /** Sets the background picture */ 96 void setWallpaper(const ColorSchemeWallpaper::Ptr &p); 97 98 /** 99 * Returns the display's filter chain. When the image for the display is updated, 100 * the text is passed through each filter in the chain. Each filter can define 101 * hotspots which correspond to certain strings (such as URLs or particular words). 102 * Depending on the type of the hotspots created by the filter ( returned by Filter::Hotspot::type() ) 103 * the view will draw visual cues such as underlines on mouse-over for links or translucent 104 * rectangles for markers. 105 * 106 * To add a new filter to the view, call: 107 * viewWidget->filterChain()->addFilter( filterObject ); 108 */ 109 FilterChain *filterChain() const; 110 111 /** 112 * Updates the filters in the display's filter chain. This will cause 113 * the hotspots to be updated to match the current image. 114 * 115 * WARNING: This function can be expensive depending on the 116 * image size and number of filters in the filterChain() 117 * 118 * TODO - This API does not really allow efficient usage. Revise it so 119 * that the processing can be done in a better way. 120 * 121 * eg: 122 * - Area of interest may be known ( eg. mouse cursor hovering 123 * over an area ) 124 */ 125 void processFilters(); 126 127 /** 128 * Returns a list of menu actions created by the filters for the content 129 * at the given @p position. 130 */ 131 QSharedPointer<HotSpot> filterActions(const QPoint &position); 132 133 /** Specifies whether or not the cursor can blink. */ 134 void setBlinkingCursorEnabled(bool blink); 135 136 /** Specifies whether or not text can blink. */ 137 void setBlinkingTextEnabled(bool blink); 138 139 void setSessionController(SessionController *controller); 140 SessionController *sessionController(); 141 Session::Ptr session() const; 142 143 /** 144 * Sets the shape of the keyboard cursor. This is the cursor drawn 145 * at the position in the terminal where keyboard input will appear. 146 * 147 * In addition the terminal display widget also has a cursor for 148 * the mouse pointer, which can be set using the QWidget::setCursor() 149 * method. 150 * 151 * Defaults to BlockCursor 152 */ 153 void setKeyboardCursorShape(Enum::CursorShapeEnum shape); 154 155 /** 156 * Sets the Cursor Style (DECSCUSR) via escape sequences 157 * @p shape cursor shape 158 * @p isBlinking if true, the cursor will be set to blink 159 */ 160 void setCursorStyle(Enum::CursorShapeEnum shape, bool isBlinking); 161 /** 162 * Resets the cursor style to the current profile cursor shape and 163 * blinking settings 164 */ 165 void resetCursorStyle(); 166 167 /** 168 * Returns the number of lines of text which can be displayed in the widget. 169 * 170 * This will depend upon the height of the widget and the current font. 171 * See fontHeight() 172 */ lines()173 int lines() const 174 { 175 return _lines; 176 } 177 178 /** 179 * Returns the number of characters of text which can be displayed on 180 * each line in the widget. 181 * 182 * This will depend upon the width of the widget and the current font. 183 * See fontWidth() 184 */ columns()185 int columns() const 186 { 187 return _columns; 188 } 189 usedColumns()190 int usedColumns() const 191 { 192 return _usedColumns; 193 } 194 195 void setSize(int columns, int lines); 196 void propagateSize(); 197 198 // reimplemented 199 QSize sizeHint() const override; 200 201 /** 202 * Sets which characters, in addition to letters and numbers, 203 * are regarded as being part of a word for the purposes 204 * of selecting words in the display by double clicking on them. 205 * 206 * The word boundaries occur at the first and last characters which 207 * are either a letter, number, or a character in @p wc 208 * 209 * @param wc An array of characters which are to be considered parts 210 * of a word ( in addition to letters and numbers ). 211 */ 212 void setWordCharacters(const QString &wc); 213 headerBar()214 TerminalHeaderBar *headerBar() const 215 { 216 return _headerBar; 217 } 218 219 void resetCursor(); 220 contentRect()221 QRect contentRect() const 222 { 223 return _contentRect; 224 } openLinksByDirectClick()225 bool openLinksByDirectClick() const 226 { 227 return _openLinksByDirectClick; 228 } 229 230 /** 231 * Sets the terminal screen section which is displayed in this widget. 232 * When updateImage() is called, the display fetches the latest character image from the 233 * the associated terminal screen window. 234 * 235 * In terms of the model-view paradigm, the ScreenWindow is the model which is rendered 236 * by the TerminalDisplay. 237 */ 238 void setScreenWindow(ScreenWindow *window); 239 /** Returns the terminal screen section which is displayed in this widget. See setScreenWindow() */ screenWindow()240 QPointer<ScreenWindow> screenWindow() const 241 { 242 return _screenWindow; 243 } 244 245 // Select the current line. 246 void selectCurrentLine(); 247 248 /** 249 * Selects everything in the terminal 250 */ 251 void selectAll(); 252 253 bool bracketedPasteMode() const; 254 255 /** 256 * Returns true if the flow control warning box is enabled. 257 * See outputSuspended() and setFlowControlWarningEnabled() 258 */ flowControlWarningEnabled()259 bool flowControlWarningEnabled() const 260 { 261 return _flowControlWarningEnabled; 262 } 263 264 /** See setUsesMouseTracking() */ 265 bool usesMouseTracking() const; 266 hasCompositeFocus()267 bool hasCompositeFocus() const 268 { 269 return _hasCompositeFocus; 270 } 271 272 // maps an area in the character image to an area on the widget 273 QRect imageToWidget(const QRect &imageArea) const; 274 QRect widgetToImage(const QRect &widgetArea) const; 275 276 // maps a point on the widget to the position ( ie. line and column ) 277 // of the character at that point. When the edge is true, it maps to 278 // a character which left edge is closest to the point. 279 QPair<int, int> getCharacterPosition(const QPoint &widgetPoint, bool edge) const; 280 281 // toggle the header bar Minimize/Maximize button. 282 void setExpandedMode(bool expand); 283 scrollBar()284 TerminalScrollBar *scrollBar() const 285 { 286 return _scrollBar; 287 } 288 terminalColor()289 TerminalColor *terminalColor() const 290 { 291 return _terminalColor; 292 } 293 terminalFont()294 TerminalFont *terminalFont() const 295 { 296 return _terminalFont.get(); 297 } 298 cursorBlinking()299 bool cursorBlinking() const 300 { 301 return _cursorBlinking; 302 } 303 textBlinking()304 bool textBlinking() const 305 { 306 return _textBlinking; 307 } 308 cursorShape()309 Enum::CursorShapeEnum cursorShape() const 310 { 311 return _cursorShape; 312 } 313 bidiEnabled()314 bool bidiEnabled() const 315 { 316 return _bidiEnabled; 317 } 318 wallpaper()319 ColorSchemeWallpaper::Ptr wallpaper() const 320 { 321 return _wallpaper; 322 } 323 324 struct InputMethodData { 325 QString preeditString; 326 QRect previousPreeditRect; 327 }; 328 329 // returns true if the cursor's position is on display. 330 bool isCursorOnDisplay() const; 331 332 // returns the position of the cursor in columns and lines 333 QPoint cursorPosition() const; 334 335 // returns 0 - not selecting, 1 - pending selection (button pressed but no movement yet), 2 - selecting 336 int selectionState() const; 337 338 public Q_SLOTS: 339 /** 340 * Scrolls current ScreenWindow 341 * 342 * it's needed for proper handling scroll commands in the Vt102Emulation class 343 */ 344 void scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount); 345 346 /** 347 * Causes the terminal display to fetch the latest character image from the associated 348 * terminal screen ( see setScreenWindow() ) and redraw the display. 349 */ 350 void updateImage(); 351 352 void setAutoCopySelectedText(bool enabled); 353 354 void setCopyTextAsHTML(bool enabled); 355 356 void setMiddleClickPasteMode(Enum::MiddleClickPasteModeEnum mode); 357 358 /** Copies the selected text to the X11 Selection. */ 359 void copyToX11Selection(); 360 361 /** Copies the selected text to the system clipboard. */ 362 void copyToClipboard(); 363 364 /** 365 * Pastes the content of the clipboard into the display. 366 * 367 * URLs of local files are treated specially: 368 * - The scheme part, "file://", is removed from each URL 369 * - The URLs are pasted as a space-separated list of file paths 370 */ 371 void pasteFromClipboard(bool appendEnter = false); 372 /** 373 * Pastes the content of the X11 selection into the 374 * display. 375 */ 376 void pasteFromX11Selection(bool appendEnter = false); 377 378 /** 379 * Changes whether the flow control warning box should be shown when the flow control 380 * stop key (Ctrl+S) are pressed. 381 */ 382 void setFlowControlWarningEnabled(bool enable); 383 /** 384 * Causes the widget to display or hide a message informing the user that terminal 385 * output has been suspended (by using the flow control key combination Ctrl+S) 386 * 387 * @param suspended True if terminal output has been suspended and the warning message should 388 * be shown or false to indicate that terminal output has been resumed and that 389 * the warning message should disappear. 390 */ 391 void outputSuspended(bool suspended); 392 393 /** 394 * Sets whether the program currently running in the terminal is interested 395 * in Mouse Tracking events. 396 * 397 * When set to true, Konsole will send Mouse Tracking events. 398 * 399 * The user interaction needed to create text selections will change 400 * also, and the user will be required to hold down the Shift key to 401 * create a selection or perform other mouse activities inside the view 402 * area, since the program running in the terminal is being allowed 403 * to handle normal mouse events itself. 404 * 405 * @param on Set to true if the program running in the terminal is 406 * interested in Mouse Tracking events or false otherwise. 407 */ 408 void setUsesMouseTracking(bool on); 409 410 void setBracketedPasteMode(bool on); 411 412 /** 413 * Shows a notification that a bell event has occurred in the terminal. 414 * TODO: More documentation here 415 */ 416 void bell(const QString &message); 417 418 /** 419 * Sets the display's contents margins. 420 */ 421 void setMargin(int margin); 422 423 /** 424 * Sets whether the contents are centered between the margins. 425 */ 426 void setCenterContents(bool enable); 427 428 /** 429 * Return the current color scheme 430 */ colorScheme()431 ColorScheme const *colorScheme() const 432 { 433 return _colorScheme; 434 } 435 droppedEdge()436 Qt::Edge droppedEdge() const 437 { 438 return _overlayEdge; 439 } 440 441 // Used to show/hide the message widget 442 void updateReadOnlyState(bool readonly); 443 IncrementalSearchBar *searchBar() const; 444 445 // Used for requestPrint 446 void printScreen(); 447 Character getCursorCharacter(int column, int line); 448 449 int loc(int x, int y) const; 450 451 Q_SIGNALS: 452 void requestToggleExpansion(); 453 void requestMoveToNewTab(TerminalDisplay *display); 454 455 /** 456 * Emitted when the user presses a key whilst the terminal widget has focus. 457 */ 458 void keyPressedSignal(QKeyEvent *event); 459 460 /** 461 * A mouse event occurred. 462 * @param button The mouse button (0 for left button, 1 for middle button, 2 for right button, 3 for release) 463 * @param column The character column where the event occurred 464 * @param line The character row where the event occurred 465 * @param eventType The type of event. 0 for a mouse press / release or 1 for mouse motion 466 */ 467 void mouseSignal(int button, int column, int line, int eventType); 468 void changedContentSizeSignal(int height, int width); 469 470 /** 471 * Emitted when the user right clicks on the display, or right-clicks 472 * with the Shift key held down if usesMouseTracking() is true. 473 * 474 * This can be used to display a context menu. 475 */ 476 void configureRequest(const QPoint &position); 477 478 /** 479 * When a shortcut which is also a valid terminal key sequence is pressed while 480 * the terminal widget has focus, this signal is emitted to allow the host to decide 481 * whether the shortcut should be overridden. 482 * When the shortcut is overridden, the key sequence will be sent to the terminal emulation instead 483 * and the action associated with the shortcut will not be triggered. 484 * 485 * @p override is set to false by default and the shortcut will be triggered as normal. 486 */ 487 void overrideShortcutCheck(QKeyEvent *keyEvent, bool &override); 488 489 void sendStringToEmu(const QByteArray &local8BitString); 490 491 void compositeFocusChanged(bool focused); 492 493 void peekPrimaryRequested(bool doPeek); 494 495 void drawContents(Character *image, 496 QPainter &paint, 497 const QRect &rect, 498 bool printerFriendly, 499 int imageSize, 500 bool bidiEnabled, 501 QVector<LineProperty> lineProperties); 502 void drawCurrentResultRect(QPainter &painter, QRect searchResultRect); 503 504 void highlightScrolledLines(QPainter &painter, bool isTimerActive, QRect rect); 505 QRegion highlightScrolledLinesRegion(bool nothingChanged, TerminalScrollBar *scrollBar); 506 507 void drawBackground(QPainter &painter, const QRect &rect, const QColor &backgroundColor, bool useOpacitySetting); 508 void drawCharacters(QPainter &painter, 509 const QRect &rect, 510 const QString &text, 511 const Character *style, 512 const QColor &characterColor, 513 const LineProperty lineProperty); 514 void drawInputMethodPreeditString(QPainter &painter, const QRect &rect, TerminalDisplay::InputMethodData &inputMethodData, Character *image); 515 516 protected: 517 // events 518 bool event(QEvent *event) override; 519 void paintEvent(QPaintEvent *pe) override; 520 void showEvent(QShowEvent *event) override; 521 void hideEvent(QHideEvent *event) override; 522 void resizeEvent(QResizeEvent *event) override; 523 void contextMenuEvent(QContextMenuEvent *event) override; 524 void focusInEvent(QFocusEvent *event) override; 525 void focusOutEvent(QFocusEvent *event) override; 526 void keyPressEvent(QKeyEvent *event) override; 527 void keyReleaseEvent(QKeyEvent *event) override; 528 void leaveEvent(QEvent *event) override; 529 void mouseDoubleClickEvent(QMouseEvent *ev) override; 530 void mousePressEvent(QMouseEvent *ev) override; 531 void mouseReleaseEvent(QMouseEvent *ev) override; 532 void mouseMoveEvent(QMouseEvent *ev) override; 533 void wheelEvent(QWheelEvent *ev) override; 534 bool focusNextPrevChild(bool next) override; 535 536 void extendSelection(const QPoint &position); 537 538 // drag and drop 539 void dragEnterEvent(QDragEnterEvent *event) override; 540 void dropEvent(QDropEvent *event) override; 541 void doDrag(); 542 enum DragState { 543 diNone, 544 diPending, 545 diDragging, 546 }; 547 548 struct DragInfo { 549 DragState state; 550 QPoint start; 551 QDrag *dragObject; 552 } _dragInfo; 553 554 // classifies the 'ch' into one of three categories 555 // and returns a character to indicate which category it is in 556 // 557 // - A space (returns ' ') 558 // - Part of a word (returns 'a') 559 // - Other characters (returns the input character) 560 QChar charClass(const Character &ch) const; 561 562 void clearImage(); 563 564 void mouseTripleClickEvent(QMouseEvent *ev); 565 void selectLine(QPoint pos, bool entireLine); 566 567 // reimplemented 568 void inputMethodEvent(QInputMethodEvent *event) override; 569 QVariant inputMethodQuery(Qt::InputMethodQuery query) const override; 570 571 protected Q_SLOTS: 572 573 void blinkTextEvent(); 574 void blinkCursorEvent(); 575 576 private Q_SLOTS: 577 578 void viewScrolledByUser(); 579 580 void dismissOutputSuspendedMessage(); 581 582 private: 583 Q_DISABLE_COPY(TerminalDisplay) 584 585 // the area where the preedit string for input methods will be draw 586 QRect preeditRect() const; 587 588 // shows a notification window in the middle of the widget indicating the terminal's 589 // current size in columns and lines 590 void showResizeNotification(); 591 592 void calcGeometry(); 593 void updateImageSize(); 594 void makeImage(); 595 596 void paintFilters(QPainter &painter); 597 598 void setupHeaderVisibility(); 599 600 // redraws the cursor 601 void updateCursor(); 602 603 bool handleShortcutOverrideEvent(QKeyEvent *keyEvent); 604 605 void doPaste(QString text, bool appendReturn); 606 607 void processMidButtonClick(QMouseEvent *ev); 608 609 QPoint findLineStart(const QPoint &pnt); 610 QPoint findLineEnd(const QPoint &pnt); 611 QPoint findWordStart(const QPoint &pnt); 612 QPoint findWordEnd(const QPoint &pnt); 613 614 // Uses the current settings for trimming whitespace and preserving linebreaks to create a proper flag value for Screen 615 Screen::DecodingOptions currentDecodingOptions(); 616 617 // Boilerplate setup for MessageWidget 618 KMessageWidget *createMessageWidget(const QString &text); 619 620 // the window onto the terminal screen which this display 621 // is currently showing. 622 QPointer<ScreenWindow> _screenWindow; 623 624 bool _bellMasked; 625 626 QVBoxLayout *_verticalLayout; 627 628 int _lines; // the number of lines that can be displayed in the widget 629 int _columns; // the number of columns that can be displayed in the widget 630 631 // Character line and character column as per a previous call to 632 // getCharacterPosition() in mouseMoveEvent(). 633 int _prevCharacterLine; 634 int _prevCharacterColumn; 635 636 int _usedLines; // the number of lines that are actually being used, this will be less 637 // than 'lines' if the character image provided with setImage() is smaller 638 // than the maximum image size which can be displayed 639 640 int _usedColumns; // the number of columns that are actually being used, this will be less 641 // than 'columns' if the character image provided with setImage() is smaller 642 // than the maximum image size which can be displayed 643 644 QRect _contentRect; 645 Character *_image; // [lines][columns] 646 // only the area [usedLines][usedColumns] in the image contains valid data 647 648 int _imageSize; 649 QVector<LineProperty> _lineProperties; 650 651 QColor _colorTable[TABLE_COLORS]; 652 653 uint _randomSeed; 654 655 bool _resizing; 656 bool _showTerminalSizeHint; 657 bool _bidiEnabled; 658 bool _usesMouseTracking; 659 bool _bracketedPasteMode; 660 661 QPoint _iPntSel; // initial selection point 662 QPoint _pntSel; // current selection point 663 QPoint _tripleSelBegin; // help avoid flicker 664 int _actSel; // selection state 665 bool _wordSelectionMode; 666 bool _lineSelectionMode; 667 bool _preserveLineBreaks; 668 bool _columnSelectionMode; 669 670 bool _autoCopySelectedText; 671 bool _copyTextAsHTML; 672 Enum::MiddleClickPasteModeEnum _middleClickPasteMode; 673 674 QString _wordCharacters; 675 TerminalBell _bell; 676 677 bool _allowBlinkingText; // allow text to blink 678 bool _allowBlinkingCursor; // allow cursor to blink 679 bool _textBlinking; // text is blinking, hide it when drawing 680 bool _cursorBlinking; // cursor is blinking, hide it when drawing 681 bool _hasTextBlinker; // has characters to blink 682 QTimer *_blinkTextTimer; 683 QTimer *_blinkCursorTimer; 684 685 bool _openLinksByDirectClick; // Open URL and hosts by single mouse click 686 687 bool _ctrlRequiredForDrag; // require Ctrl key for drag selected text 688 bool _dropUrlsAsText; // always paste URLs as text without showing copy/move menu 689 690 Enum::TripleClickModeEnum _tripleClickMode; 691 bool _possibleTripleClick; // is set in mouseDoubleClickEvent and deleted 692 // after QApplication::doubleClickInterval() delay 693 694 QLabel *_resizeWidget; 695 QTimer *_resizeTimer; 696 697 bool _flowControlWarningEnabled; 698 699 // widgets related to the warning message that appears when the user presses Ctrl+S to suspend 700 // terminal output - informing them what has happened and how to resume output 701 KMessageWidget *_outputSuspendedMessageWidget; 702 703 QSize _size; 704 705 ColorScheme const *_colorScheme; 706 ColorSchemeWallpaper::Ptr _wallpaper; 707 708 // list of filters currently applied to the display. used for links and 709 // search highlight 710 TerminalImageFilterChain *_filterChain; 711 bool _filterUpdateRequired; 712 713 Enum::CursorShapeEnum _cursorShape; 714 715 InputMethodData _inputMethodData; 716 717 // the delay in milliseconds between redrawing blinking text 718 static const int TEXT_BLINK_DELAY = 500; 719 720 // the duration of the size hint in milliseconds 721 static const int SIZE_HINT_DURATION = 1000; 722 723 SessionController *_sessionController; 724 725 bool _trimLeadingSpaces; // trim leading spaces in selected text 726 bool _trimTrailingSpaces; // trim trailing spaces in selected text 727 bool _mouseWheelZoom; // enable mouse wheel zooming or not 728 729 int _margin; // the contents margin 730 bool _centerContents; // center the contents between margins 731 732 KMessageWidget *_readOnlyMessageWidget; // Message shown at the top when read-only mode gets activated 733 734 // Needed to know whether the mode really changed between update calls 735 bool _readOnly; 736 737 bool _dimWhenInactive; 738 int _dimValue; 739 740 ScrollState _scrollWheelState; 741 IncrementalSearchBar *_searchBar; 742 TerminalHeaderBar *_headerBar; 743 QRect _searchResultRect; 744 friend class TerminalDisplayAccessible; 745 746 bool _drawOverlay; 747 Qt::Edge _overlayEdge; 748 749 bool _hasCompositeFocus; 750 bool _displayVerticalLine; 751 int _displayVerticalLineAtChar; 752 753 QKeySequence _peekPrimaryShortcut; 754 755 TerminalPainter *_terminalPainter; 756 TerminalScrollBar *_scrollBar; 757 TerminalColor *_terminalColor; 758 std::unique_ptr<TerminalFont> _terminalFont; 759 760 std::unique_ptr<KonsolePrintManager> _printManager; 761 }; 762 763 } 764 765 #endif // TERMINALDISPLAY_H 766