From b2449cc578245d6f8dcc3e03ceb78ed03ba8ceb8 Mon Sep 17 00:00:00 2001 From: Petr Vanek Date: Mon, 25 Oct 2010 16:27:34 +0200 Subject: [PATCH] display stuff synced from konsole again to improve color scheme handling --- lib/CMakeLists.txt | 2 +- lib/Character.h | 191 +- lib/CharacterColor.h | 320 ++- lib/ColorTables.h | 60 +- lib/TerminalCharacterDecoder.cpp | 158 +- lib/TerminalCharacterDecoder.h | 67 +- lib/TerminalDisplay.cpp | 3723 ++++++++++++++++-------------- lib/TerminalDisplay.h | 503 ++-- lib/qtermwidget.cpp | 1 + 9 files changed, 2666 insertions(+), 2359 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b7a5c9f..aa75405 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -94,7 +94,7 @@ include_directories ( ${PROJECT_SOURCE_DIR} ) # You mark some of the headers as set ( SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share ) message ( STATUS "Share dire: ${SHARE_DIR}" ) -set ( KB_LAYOUT_DIR ${CMAKE_INSTALL_PREFIX}/share/qtermwidget/ ) +set ( KB_LAYOUT_DIR ${CMAKE_INSTALL_PREFIX}/share/qtermwidget/kb-layouts/ ) message ( STATUS "Keyboard layouts will be installed in: ${KB_LAYOUT_DIR}" ) add_definitions( -DKB_LAYOUT_DIR="\\"${KB_LAYOUT_DIR}\\"" ) diff --git a/lib/Character.h b/lib/Character.h index 3f23841..4088def 100644 --- a/lib/Character.h +++ b/lib/Character.h @@ -1,10 +1,8 @@ /* This file is part of Konsole, KDE's terminal. - - Copyright (C) 2007 by Robert Knight - Copyright (C) 1997,1998 by Lars Doelle - - Rewritten for QT4 by e_k , Copyright (C)2008 + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle 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 @@ -36,10 +34,10 @@ namespace Konsole typedef unsigned char LineProperty; -static const int LINE_DEFAULT = 0; -static const int LINE_WRAPPED = (1 << 0); -static const int LINE_DOUBLEWIDTH = (1 << 1); -static const int LINE_DOUBLEHEIGHT = (1 << 2); +static const int LINE_DEFAULT = 0; +static const int LINE_WRAPPED = (1 << 0); +static const int LINE_DOUBLEWIDTH = (1 << 1); +static const int LINE_DOUBLEHEIGHT = (1 << 2); #define DEFAULT_RENDITION 0 #define RE_BOLD (1 << 0) @@ -58,95 +56,111 @@ static const int LINE_DOUBLEHEIGHT = (1 << 2); class Character { public: - /** - * Constructs a new character. + /** + * Constructs a new character. + * + * @param _c The unicode character value of this character. + * @param _f The foreground color used to draw the character. + * @param _b The color used to draw the character's background. + * @param _r A set of rendition flags which specify how this character is to be drawn. + */ + inline Character(quint16 _c = ' ', + CharacterColor _f = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR), + CharacterColor _b = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR), + quint8 _r = DEFAULT_RENDITION) + : character(_c), rendition(_r), foregroundColor(_f), backgroundColor(_b) {} + + union + { + /** The unicode character value for this character. */ + quint16 character; + /** + * Experimental addition which allows a single Character instance to contain more than + * one unicode character. * - * @param _c The unicode character value of this character. - * @param _f The foreground color used to draw the character. - * @param _b The color used to draw the character's background. - * @param _r A set of rendition flags which specify how this character is to be drawn. + * charSequence is a hash code which can be used to look up the unicode + * character sequence in the ExtendedCharTable used to create the sequence. */ - inline Character(quint16 _c = ' ', - CharacterColor _f = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR), - CharacterColor _b = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR), - quint8 _r = DEFAULT_RENDITION) - : character(_c), rendition(_r), foregroundColor(_f), backgroundColor(_b) {} + quint16 charSequence; + }; - union { - /** The unicode character value for this character. */ - quint16 character; - /** - * Experimental addition which allows a single Character instance to contain more than - * one unicode character. - * - * charSequence is a hash code which can be used to look up the unicode - * character sequence in the ExtendedCharTable used to create the sequence. - */ - quint16 charSequence; - }; + /** A combination of RENDITION flags which specify options for drawing the character. */ + quint8 rendition; - /** A combination of RENDITION flags which specify options for drawing the character. */ - quint8 rendition; + /** The foreground color used to draw this character. */ + CharacterColor foregroundColor; + /** The color used to draw this character's background. */ + CharacterColor backgroundColor; - /** The foreground color used to draw this character. */ - CharacterColor foregroundColor; - /** The color used to draw this character's background. */ - CharacterColor backgroundColor; + /** + * Returns true if this character has a transparent background when + * it is drawn with the specified @p palette. + */ + bool isTransparent(const ColorEntry* palette) const; + /** + * Returns true if this character should always be drawn in bold when + * it is drawn with the specified @p palette, independent of whether + * or not the character has the RE_BOLD rendition flag. + */ + ColorEntry::FontWeight fontWeight(const ColorEntry* base) const; + + /** + * returns true if the format (color, rendition flag) of the compared characters is equal + */ + bool equalsFormat(const Character &other) const; - /** - * Returns true if this character has a transparent background when - * it is drawn with the specified @p palette. - */ - bool isTransparent(const ColorEntry * palette) const; - /** - * Returns true if this character should always be drawn in bold when - * it is drawn with the specified @p palette, independent of whether - * or not the character has the RE_BOLD rendition flag. - */ - bool isBold(const ColorEntry * base) const; - - /** - * Compares two characters and returns true if they have the same unicode character value, - * rendition and colors. - */ - friend bool operator == (const Character & a, const Character & b); - /** - * Compares two characters and returns true if they have different unicode character values, - * renditions or colors. - */ - friend bool operator != (const Character & a, const Character & b); + /** + * Compares two characters and returns true if they have the same unicode character value, + * rendition and colors. + */ + friend bool operator == (const Character& a, const Character& b); + /** + * Compares two characters and returns true if they have different unicode character values, + * renditions or colors. + */ + friend bool operator != (const Character& a, const Character& b); }; -inline bool operator == (const Character & a, const Character & b) -{ - return a.character == b.character && - a.rendition == b.rendition && - a.foregroundColor == b.foregroundColor && - a.backgroundColor == b.backgroundColor; +inline bool operator == (const Character& a, const Character& b) +{ + return a.character == b.character && + a.rendition == b.rendition && + a.foregroundColor == b.foregroundColor && + a.backgroundColor == b.backgroundColor; } -inline bool operator != (const Character & a, const Character & b) +inline bool operator != (const Character& a, const Character& b) { - return a.character != b.character || - a.rendition != b.rendition || - a.foregroundColor != b.foregroundColor || - a.backgroundColor != b.backgroundColor; + return a.character != b.character || + a.rendition != b.rendition || + a.foregroundColor != b.foregroundColor || + a.backgroundColor != b.backgroundColor; } -inline bool Character::isTransparent(const ColorEntry * base) const +inline bool Character::isTransparent(const ColorEntry* base) const { - return ((backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) && - base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].transparent) - || ((backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) && - base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].transparent); + return ((backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) && + base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].transparent) + || ((backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) && + base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].transparent); } -inline bool Character::isBold(const ColorEntry * base) const +inline bool Character::equalsFormat(const Character& other) const { - return ((backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) && - base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].bold) - || ((backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) && - base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].bold); + return + backgroundColor==other.backgroundColor && + foregroundColor==other.foregroundColor && + rendition==other.rendition; +} + +inline ColorEntry::FontWeight Character::fontWeight(const ColorEntry* base) const +{ + if (backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) + return base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].fontWeight; + else if (backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) + return base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].fontWeight; + else + return ColorEntry::UseCurrentFormat; } extern unsigned short vt100_graphics[32]; @@ -176,34 +190,35 @@ public: * @param unicodePoints An array of unicode character points * @param length Length of @p unicodePoints */ - ushort createExtendedChar(ushort * unicodePoints , ushort length); + ushort createExtendedChar(ushort* unicodePoints , ushort length); /** * Looks up and returns a pointer to a sequence of unicode characters * which was added to the table using createExtendedChar(). * * @param hash The hash key returned by createExtendedChar() - * @param length This variable is set to the length of the + * @param length This variable is set to the length of the * character sequence. * * @return A unicode character sequence of size @p length. */ - ushort * lookupExtendedChar(ushort hash , ushort & length) const; + ushort* lookupExtendedChar(ushort hash , ushort& length) const; /** The global ExtendedCharTable instance. */ static ExtendedCharTable instance; private: // calculates the hash key of a sequence of unicode points of size 'length' - ushort extendedCharHash(ushort * unicodePoints , ushort length) const; - // tests whether the entry in the table specified by 'hash' matches the + ushort extendedCharHash(ushort* unicodePoints , ushort length) const; + // tests whether the entry in the table specified by 'hash' matches the // character sequence 'unicodePoints' of size 'length' - bool extendedCharMatch(ushort hash , ushort * unicodePoints , ushort length) const; + bool extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const; // internal, maps hash keys to character sequence buffers. The first ushort // in each value is the length of the buffer, followed by the ushorts in the buffer // themselves. - QHash extendedCharTable; + QHash extendedCharTable; }; } +Q_DECLARE_TYPEINFO(Konsole::Character, Q_MOVABLE_TYPE); #endif // CHARACTER_H diff --git a/lib/CharacterColor.h b/lib/CharacterColor.h index 612745c..3181073 100644 --- a/lib/CharacterColor.h +++ b/lib/CharacterColor.h @@ -1,10 +1,8 @@ /* This file is part of Konsole, KDE's terminal. - - Copyright (C) 2007 by Robert Knight - Copyright (C) 1997,1998 by Lars Doelle - - Rewritten for QT4 by e_k , Copyright (C)2008 + + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle 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 @@ -28,17 +26,19 @@ // Qt #include +#include + namespace Konsole { -/** - * An entry in a terminal display's color palette. +/** + * An entry in a terminal display's color palette. * * A color palette is an array of 16 ColorEntry instances which map * system color indexes (from 0 to 15) into actual colors. * * Each entry can be set as bold, in which case any text - * drawn using the color should be drawn in bold. + * drawn using the color should be drawn in bold. * * Each entry can also be transparent, in which case the terminal * display should avoid drawing the background for any characters @@ -47,43 +47,59 @@ namespace Konsole class ColorEntry { public: - /** - * Constructs a new color palette entry. - * - * @param c The color value for this entry. - * @param tr Specifies that the color should be transparent when used as a background color. - * @param b Specifies that text drawn with this color should be bold. + /** Specifies the weight to use when drawing text with this color. */ + enum FontWeight + { + /** Always draw text in this color with a bold weight. */ + Bold, + /** Always draw text in this color with a normal weight. */ + Normal, + /** + * Use the current font weight set by the terminal application. + * This is the default behavior. */ - ColorEntry(QColor c, bool tr, bool b) : color(c), transparent(tr), bold(b) {} + UseCurrentFormat + }; - /** - * Constructs a new color palette entry with an undefined color, and - * with the transparent and bold flags set to false. - */ - ColorEntry() : transparent(false), bold(false) {} + /** + * Constructs a new color palette entry. + * + * @param c The color value for this entry. + * @param tr Specifies that the color should be transparent when used as a background color. + * @param weight Specifies the font weight to use when drawing text with this color. + */ + ColorEntry(QColor c, bool tr, FontWeight weight = UseCurrentFormat) + : color(c), transparent(tr), fontWeight(weight) {} - /** - * Sets the color, transparency and boldness of this color to those of @p rhs. - */ - void operator=(const ColorEntry & rhs) { - color = rhs.color; - transparent = rhs.transparent; - bold = rhs.bold; - } + /** + * Constructs a new color palette entry with an undefined color, and + * with the transparent and bold flags set to false. + */ + ColorEntry() : transparent(false), fontWeight(UseCurrentFormat) {} + + /** + * Sets the color, transparency and boldness of this color to those of @p rhs. + */ + void operator=(const ColorEntry& rhs) + { + color = rhs.color; + transparent = rhs.transparent; + fontWeight = rhs.fontWeight; + } - /** The color value of this entry for display. */ - QColor color; + /** The color value of this entry for display. */ + QColor color; - /** - * If true character backgrounds using this color should be transparent. - * This is not applicable when the color is used to render text. - */ - bool transparent; - /** - * If true characters drawn using this color should be bold. - * This is not applicable when the color is used to draw a character's background. - */ - bool bold; + /** + * If true character backgrounds using this color should be transparent. + * This is not applicable when the color is used to render text. + */ + bool transparent; + /** + * Specifies the font weight to use when drawing text with this color. + * This is not applicable when the color is used to draw a character's background. + */ + FontWeight fontWeight; }; @@ -101,25 +117,7 @@ public: //a standard set of colors using black text on a white background. //defined in TerminalDisplay.cpp -static const ColorEntry base_color_table[TABLE_COLORS] = -// The following are almost IBM standard color codes, with some slight -// gamma correction for the dim colors to compensate for bright X screens. -// It contains the 8 ansiterm/xterm colors in 2 intensities. -{ - // Fixme: could add faint colors here, also. - // normal - ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback - ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red - ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow - ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta - ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White - // intensiv - ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), - ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), - ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), - ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0xFF), 0, 0 ), - ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) -}; +extern const ColorEntry base_color_table[TABLE_COLORS] KDE_NO_EXPORT; /* CharacterColor is a union of the various color spaces. @@ -151,29 +149,32 @@ class CharacterColor friend class Character; public: - /** Constructs a new CharacterColor whoose color and color space are undefined. */ - CharacterColor() - : _colorSpace(COLOR_SPACE_UNDEFINED), - _u(0), - _v(0), - _w(0) {} + /** Constructs a new CharacterColor whoose color and color space are undefined. */ + CharacterColor() + : _colorSpace(COLOR_SPACE_UNDEFINED), + _u(0), + _v(0), + _w(0) + {} - /** - * Constructs a new CharacterColor using the specified @p colorSpace and with - * color value @p co - * - * The meaning of @p co depends on the @p colorSpace used. - * - * TODO : Document how @p co relates to @p colorSpace - * - * TODO : Add documentation about available color spaces. - */ - CharacterColor(quint8 colorSpace, int co) - : _colorSpace(colorSpace), - _u(0), - _v(0), - _w(0) { - switch (colorSpace) { + /** + * Constructs a new CharacterColor using the specified @p colorSpace and with + * color value @p co + * + * The meaning of @p co depends on the @p colorSpace used. + * + * TODO : Document how @p co relates to @p colorSpace + * + * TODO : Add documentation about available color spaces. + */ + CharacterColor(quint8 colorSpace, int co) + : _colorSpace(colorSpace), + _u(0), + _v(0), + _w(0) + { + switch (colorSpace) + { case COLOR_SPACE_DEFAULT: _u = co & 1; break; @@ -181,7 +182,7 @@ public: _u = co & 7; _v = (co >> 3) & 1; break; - case COLOR_SPACE_256: + case COLOR_SPACE_256: _u = co & 255; break; case COLOR_SPACE_RGB: @@ -191,114 +192,103 @@ public: break; default: _colorSpace = COLOR_SPACE_UNDEFINED; - } } + } - /** - * Returns true if this character color entry is valid. - */ - bool isValid() { + /** + * Returns true if this character color entry is valid. + */ + bool isValid() + { return _colorSpace != COLOR_SPACE_UNDEFINED; - } + } + + /** + * Toggles the value of this color between a normal system color and the corresponding intensive + * system color. + * + * This is only applicable if the color is using the COLOR_SPACE_DEFAULT or COLOR_SPACE_SYSTEM + * color spaces. + */ + void toggleIntensive(); - /** - * Toggles the value of this color between a normal system color and the corresponding intensive - * system color. - * - * This is only applicable if the color is using the COLOR_SPACE_DEFAULT or COLOR_SPACE_SYSTEM - * color spaces. - */ - void toggleIntensive(); - - /** - * Returns the color within the specified color @palette - * - * The @p palette is only used if this color is one of the 16 system colors, otherwise - * it is ignored. - */ - QColor color(const ColorEntry * palette) const; - - /** - * Compares two colors and returns true if they represent the same color value and - * use the same color space. - */ - friend bool operator == (const CharacterColor& a, const CharacterColor& b); - /** - * Compares two colors and returns true if they represent different color values - * or use different color spaces. - */ - friend bool operator != (const CharacterColor & a, const CharacterColor & b); + /** + * Returns the color within the specified color @p palette + * + * The @p palette is only used if this color is one of the 16 system colors, otherwise + * it is ignored. + */ + QColor color(const ColorEntry* palette) const; + + /** + * Compares two colors and returns true if they represent the same color value and + * use the same color space. + */ + friend bool operator == (const CharacterColor& a, const CharacterColor& b); + /** + * Compares two colors and returns true if they represent different color values + * or use different color spaces. + */ + friend bool operator != (const CharacterColor& a, const CharacterColor& b); private: - quint8 _colorSpace; + quint8 _colorSpace; - // bytes storing the character color - quint8 _u; - quint8 _v; - quint8 _w; + // bytes storing the character color + quint8 _u; + quint8 _v; + quint8 _w; }; -inline bool operator == (const CharacterColor & a, const CharacterColor & b) -{ - return a._colorSpace == b._colorSpace && - a._u == b._u && - a._v == b._v && - a._w == b._w; +inline bool operator == (const CharacterColor& a, const CharacterColor& b) +{ + return a._colorSpace == b._colorSpace && + a._u == b._u && + a._v == b._v && + a._w == b._w; } - -inline bool operator != (const CharacterColor & a, const CharacterColor & b) +inline bool operator != (const CharacterColor& a, const CharacterColor& b) { return !operator==(a,b); } -inline const QColor color256(quint8 u, const ColorEntry * base) +inline const QColor color256(quint8 u, const ColorEntry* base) { - // 0.. 16: system colors - if (u < 8) { - return base[u+2 ].color; - } - u -= 8; - if (u < 8) { - return base[u+2+BASE_COLORS].color; - } - u -= 8; + // 0.. 16: system colors + if (u < 8) return base[u+2 ].color; u -= 8; + if (u < 8) return base[u+2+BASE_COLORS].color; u -= 8; - // 16..231: 6x6x6 rgb color cube - if (u < 216) return QColor(255*((u/36)%6)/5, - 255*((u/ 6)%6)/5, - 255*((u/ 1)%6)/5); - u -= 216; - - // 232..255: gray, leaving out black and white - int gray = u*10+8; - return QColor(gray,gray,gray); + // 16..231: 6x6x6 rgb color cube + if (u < 216) return QColor(((u/36)%6) ? (40*((u/36)%6)+55) : 0, + ((u/ 6)%6) ? (40*((u/ 6)%6)+55) : 0, + ((u/ 1)%6) ? (40*((u/ 1)%6)+55) : 0); u -= 216; + + // 232..255: gray, leaving out black and white + int gray = u*10+8; return QColor(gray,gray,gray); } -inline QColor CharacterColor::color(const ColorEntry * base) const +inline QColor CharacterColor::color(const ColorEntry* base) const { - switch (_colorSpace) { - case COLOR_SPACE_DEFAULT: - return base[_u+0+(_v?BASE_COLORS:0)].color; - case COLOR_SPACE_SYSTEM: - return base[_u+2+(_v?BASE_COLORS:0)].color; - case COLOR_SPACE_256: - return color256(_u,base); - case COLOR_SPACE_RGB: - return QColor(_u,_v,_w); - case COLOR_SPACE_UNDEFINED: - return QColor(); - } + switch (_colorSpace) + { + case COLOR_SPACE_DEFAULT: return base[_u+0+(_v?BASE_COLORS:0)].color; + case COLOR_SPACE_SYSTEM: return base[_u+2+(_v?BASE_COLORS:0)].color; + case COLOR_SPACE_256: return color256(_u,base); + case COLOR_SPACE_RGB: return QColor(_u,_v,_w); + case COLOR_SPACE_UNDEFINED: return QColor(); + } - Q_ASSERT(false); // invalid color space + Q_ASSERT(false); // invalid color space - return QColor(); + return QColor(); } inline void CharacterColor::toggleIntensive() { - if (_colorSpace == COLOR_SPACE_SYSTEM || _colorSpace == COLOR_SPACE_DEFAULT) { - _v = !_v; - } + if (_colorSpace == COLOR_SPACE_SYSTEM || _colorSpace == COLOR_SPACE_DEFAULT) + { + _v = !_v; + } } diff --git a/lib/ColorTables.h b/lib/ColorTables.h index d759607..6a9ee94 100644 --- a/lib/ColorTables.h +++ b/lib/ColorTables.h @@ -7,44 +7,44 @@ using namespace Konsole; static const ColorEntry whiteonblack_color_table[TABLE_COLORS] = { // normal - ColorEntry(QColor(0xFF,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0x00,0x00,0x00), 1, 0 ), // Dfore, Dback - ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red - ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow - ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta - ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White + ColorEntry(QColor(0xFF,0xFF,0xFF), false ), ColorEntry( QColor(0x00,0x00,0x00), true ), // Dfore, Dback + ColorEntry(QColor(0x00,0x00,0x00), false ), ColorEntry( QColor(0xB2,0x18,0x18), false ), // Black, Red + ColorEntry(QColor(0x18,0xB2,0x18), false ), ColorEntry( QColor(0xB2,0x68,0x18), false ), // Green, Yellow + ColorEntry(QColor(0x18,0x18,0xB2), false ), ColorEntry( QColor(0xB2,0x18,0xB2), false ), // Blue, Magenta + ColorEntry(QColor(0x18,0xB2,0xB2), false ), ColorEntry( QColor(0xB2,0xB2,0xB2), false ), // Cyan, White // intensiv - ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), - ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), - ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), - ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0xFF), 0, 0 ), - ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) + ColorEntry(QColor(0x00,0x00,0x00), false ), ColorEntry( QColor(0xFF,0xFF,0xFF), true ), + ColorEntry(QColor(0x68,0x68,0x68), false ), ColorEntry( QColor(0xFF,0x54,0x54), false ), + ColorEntry(QColor(0x54,0xFF,0x54), false ), ColorEntry( QColor(0xFF,0xFF,0x54), false ), + ColorEntry(QColor(0x54,0x54,0xFF), false ), ColorEntry( QColor(0xFF,0x54,0xFF), false ), + ColorEntry(QColor(0x54,0xFF,0xFF), false ), ColorEntry( QColor(0xFF,0xFF,0xFF), false ) }; static const ColorEntry greenonblack_color_table[TABLE_COLORS] = { - ColorEntry(QColor( 24, 240, 24), 0, 0), ColorEntry(QColor( 0, 0, 0), 1, 0), - ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 178, 24, 24), 0, 0), - ColorEntry(QColor( 24, 178, 24), 0, 0), ColorEntry(QColor( 178, 104, 24), 0, 0), - ColorEntry(QColor( 24, 24, 178), 0, 0), ColorEntry(QColor( 178, 24, 178), 0, 0), - ColorEntry(QColor( 24, 178, 178), 0, 0), ColorEntry(QColor( 178, 178, 178), 0, 0), + ColorEntry(QColor( 24, 240, 24), false), ColorEntry(QColor( 0, 0, 0), true), + ColorEntry(QColor( 0, 0, 0), false), ColorEntry(QColor( 178, 24, 24), false), + ColorEntry(QColor( 24, 178, 24), false), ColorEntry(QColor( 178, 104, 24), false), + ColorEntry(QColor( 24, 24, 178), false), ColorEntry(QColor( 178, 24, 178), false), + ColorEntry(QColor( 24, 178, 178), false), ColorEntry(QColor( 178, 178, 178), false), // intensive colors - ColorEntry(QColor( 24, 240, 24), 0, 1 ), ColorEntry(QColor( 0, 0, 0), 1, 0 ), - ColorEntry(QColor( 104, 104, 104), 0, 0 ), ColorEntry(QColor( 255, 84, 84), 0, 0 ), - ColorEntry(QColor( 84, 255, 84), 0, 0 ), ColorEntry(QColor( 255, 255, 84), 0, 0 ), - ColorEntry(QColor( 84, 84, 255), 0, 0 ), ColorEntry(QColor( 255, 84, 255), 0, 0 ), - ColorEntry(QColor( 84, 255, 255), 0, 0 ), ColorEntry(QColor( 255, 255, 255), 0, 0 ) + ColorEntry(QColor( 24, 240, 24), false ), ColorEntry(QColor( 0, 0, 0), true ), + ColorEntry(QColor( 104, 104, 104), false ), ColorEntry(QColor( 255, 84, 84), false ), + ColorEntry(QColor( 84, 255, 84), false ), ColorEntry(QColor( 255, 255, 84), false ), + ColorEntry(QColor( 84, 84, 255), false ), ColorEntry(QColor( 255, 84, 255), false ), + ColorEntry(QColor( 84, 255, 255), false ), ColorEntry(QColor( 255, 255, 255), false ) }; static const ColorEntry blackonlightyellow_color_table[TABLE_COLORS] = { - ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 255, 255, 221), 1, 0), - ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 178, 24, 24), 0, 0), - ColorEntry(QColor( 24, 178, 24), 0, 0), ColorEntry(QColor( 178, 104, 24), 0, 0), - ColorEntry(QColor( 24, 24, 178), 0, 0), ColorEntry(QColor( 178, 24, 178), 0, 0), - ColorEntry(QColor( 24, 178, 178), 0, 0), ColorEntry(QColor( 178, 178, 178), 0, 0), - ColorEntry(QColor( 0, 0, 0), 0, 1), ColorEntry(QColor( 255, 255, 221), 1, 0), - ColorEntry(QColor(104, 104, 104), 0, 0), ColorEntry(QColor( 255, 84, 84), 0, 0), - ColorEntry(QColor( 84, 255, 84), 0, 0), ColorEntry(QColor( 255, 255, 84), 0, 0), - ColorEntry(QColor( 84, 84, 255), 0, 0), ColorEntry(QColor( 255, 84, 255), 0, 0), - ColorEntry(QColor( 84, 255, 255), 0, 0), ColorEntry(QColor( 255, 255, 255), 0, 0) + ColorEntry(QColor( 0, 0, 0), false), ColorEntry(QColor( 255, 255, 221), true), + ColorEntry(QColor( 0, 0, 0), false), ColorEntry(QColor( 178, 24, 24), false), + ColorEntry(QColor( 24, 178, 24), false), ColorEntry(QColor( 178, 104, 24), false), + ColorEntry(QColor( 24, 24, 178), false), ColorEntry(QColor( 178, 24, 178), false), + ColorEntry(QColor( 24, 178, 178), false), ColorEntry(QColor( 178, 178, 178), false), + ColorEntry(QColor( 0, 0, 0), false), ColorEntry(QColor( 255, 255, 221), true), + ColorEntry(QColor(104, 104, 104), false), ColorEntry(QColor( 255, 84, 84), false), + ColorEntry(QColor( 84, 255, 84), false), ColorEntry(QColor( 255, 255, 84), false), + ColorEntry(QColor( 84, 84, 255), false), ColorEntry(QColor( 255, 84, 255), false), + ColorEntry(QColor( 84, 255, 255), false), ColorEntry(QColor( 255, 255, 255), false) }; diff --git a/lib/TerminalCharacterDecoder.cpp b/lib/TerminalCharacterDecoder.cpp index 8b86661..c02c789 100644 --- a/lib/TerminalCharacterDecoder.cpp +++ b/lib/TerminalCharacterDecoder.cpp @@ -1,10 +1,8 @@ /* This file is part of Konsole, an X terminal. - - Copyright (C) 2006 by Robert Knight - - Rewritten for QT4 by e_k , Copyright (C)2008 - + + Copyright 2006-2008 by Robert Knight + This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -27,12 +25,14 @@ // Qt #include +// Konsole +#include "konsole_wcwidth.h" using namespace Konsole; - PlainTextDecoder::PlainTextDecoder() - : _output(0) - , _includeTrailingWhitespace(true) + : _output(0) + , _includeTrailingWhitespace(true) + , _recordLinePositions(false) { } @@ -44,19 +44,36 @@ bool PlainTextDecoder::trailingWhitespace() const { return _includeTrailingWhitespace; } -void PlainTextDecoder::begin(QTextStream * output) +void PlainTextDecoder::begin(QTextStream* output) { - _output = output; + _output = output; + if (!_linePositions.isEmpty()) + _linePositions.clear(); } void PlainTextDecoder::end() { _output = 0; } -void PlainTextDecoder::decodeLine(const Character * const characters, int count, LineProperty /*properties*/ - ) + +void PlainTextDecoder::setRecordLinePositions(bool record) +{ + _recordLinePositions = record; +} +QList PlainTextDecoder::linePositions() const +{ + return _linePositions; +} +void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ + ) { Q_ASSERT( _output ); + if (_recordLinePositions && _output->string()) + { + int pos = _output->string()->count(); + _linePositions << pos; + } + //TODO should we ignore or respect the LINE_WRAPPED line property? //note: we build up a QString and send it to the text stream rather writing into the text @@ -64,38 +81,40 @@ void PlainTextDecoder::decodeLine(const Character * const characters, int count, //(since QTextStream always deals with QStrings internally anyway) QString plainText; plainText.reserve(count); - + int outputCount = count; // if inclusion of trailing whitespace is disabled then find the end of the // line - if ( !_includeTrailingWhitespace ) { - for (int i = count-1 ; i >= 0 ; i--) { - if ( characters[i].character != ' ' ) { + if ( !_includeTrailingWhitespace ) + { + for (int i = count-1 ; i >= 0 ; i--) + { + if ( characters[i].character != ' ' ) break; - } else { + else outputCount--; - } } } - - for (int i=0; i') { - text.append(">"); - } else { - text.append(ch); - } - } else { + else if (ch == '>') + text.append(">"); + else + text.append(ch); + } + else + { text.append(" "); //HTML truncates multiple spaces, so use a space marker instead } - + } //close any remaining open inner spans - if ( _innerSpanOpen ) { + if ( _innerSpanOpen ) closeSpan(text); - } //start new line text.append("
"); - + *_output << text; } - -void HTMLDecoder::openSpan(QString & text , const QString & style) +void HTMLDecoder::openSpan(QString& text , const QString& style) { text.append( QString("").arg(style) ); } -void HTMLDecoder::closeSpan(QString & text) +void HTMLDecoder::closeSpan(QString& text) { text.append(""); } -void HTMLDecoder::setColorTable(const ColorEntry * table) +void HTMLDecoder::setColorTable(const ColorEntry* table) { _colorTable = table; } diff --git a/lib/TerminalCharacterDecoder.h b/lib/TerminalCharacterDecoder.h index bf84c31..5bdc035 100644 --- a/lib/TerminalCharacterDecoder.h +++ b/lib/TerminalCharacterDecoder.h @@ -1,10 +1,8 @@ /* This file is part of Konsole, an X terminal. - - Copyright (C) 2006-7 by Robert Knight - - Rewritten for QT4 by e_k , Copyright (C)2008 - + + Copyright 2006-2008 by Robert Knight + This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -26,6 +24,8 @@ #include "Character.h" +#include + class QTextStream; namespace Konsole @@ -38,7 +38,7 @@ namespace Konsole * and background colours and other appearance-related properties into text strings. * * Derived classes may produce either plain text with no other colour or appearance information, or - * they may produce text which incorporates these additional properties. + * they may produce text which incorporates these additional properties. */ class TerminalCharacterDecoder { @@ -46,7 +46,7 @@ public: virtual ~TerminalCharacterDecoder() {} /** Begin decoding characters. The resulting text is appended to @p output. */ - virtual void begin(QTextStream * output) = 0; + virtual void begin(QTextStream* output) = 0; /** End decoding. */ virtual void end() = 0; @@ -55,12 +55,12 @@ public: * and writes the string into an output QTextStream. * * @param characters An array of characters of length @p count. + * @param count The number of characters * @param properties Additional properties which affect all characters in the line - * @param output The output stream which receives the decoded text */ - virtual void decodeLine(const Character * const characters, + virtual void decodeLine(const Character* const characters, int count, - LineProperty properties) = 0; + LineProperty properties) = 0; }; /** @@ -70,10 +70,10 @@ public: class PlainTextDecoder : public TerminalCharacterDecoder { public: - PlainTextDecoder(); + PlainTextDecoder(); - /** - * Set whether trailing whitespace at the end of lines should be included + /** + * Set whether trailing whitespace at the end of lines should be included * in the output. * Defaults to true. */ @@ -83,18 +83,29 @@ public: * in the output. */ bool trailingWhitespace() const; + /** + * Returns of character positions in the output stream + * at which new lines where added. Returns an empty if setTrackLinePositions() is false or if + * the output device is not a string. + */ + QList linePositions() const; + /** Enables recording of character positions at which new lines are added. See linePositions() */ + void setRecordLinePositions(bool record); - virtual void begin(QTextStream * output); + virtual void begin(QTextStream* output); virtual void end(); - virtual void decodeLine(const Character * const characters, + virtual void decodeLine(const Character* const characters, int count, - LineProperty properties); - + LineProperty properties); + private: - QTextStream * _output; + QTextStream* _output; bool _includeTrailingWhitespace; + + bool _recordLinePositions; + QList _linePositions; }; /** @@ -103,7 +114,7 @@ private: class HTMLDecoder : public TerminalCharacterDecoder { public: - /** + /** * Constructs an HTML decoder using a default black-on-white color scheme. */ HTMLDecoder(); @@ -112,22 +123,22 @@ public: * Sets the colour table which the decoder uses to produce the HTML colour codes in its * output */ - void setColorTable( const ColorEntry * table ); - - virtual void decodeLine(const Character * const characters, + void setColorTable( const ColorEntry* table ); + + virtual void decodeLine(const Character* const characters, int count, LineProperty properties); - virtual void begin(QTextStream * output); + virtual void begin(QTextStream* output); virtual void end(); private: - void openSpan(QString & text , const QString & style); - void closeSpan(QString & text); + void openSpan(QString& text , const QString& style); + void closeSpan(QString& text); - QTextStream * _output; - const ColorEntry * _colorTable; - bool _innerSpanOpen; + QTextStream* _output; + const ColorEntry* _colorTable; + bool _innerSpanOpen; quint8 _lastRendition; CharacterColor _lastForeColor; CharacterColor _lastBackColor; diff --git a/lib/TerminalDisplay.cpp b/lib/TerminalDisplay.cpp index 5aec2dc..af26f9a 100644 --- a/lib/TerminalDisplay.cpp +++ b/lib/TerminalDisplay.cpp @@ -1,11 +1,11 @@ /* This file is part of Konsole, a terminal emulator for KDE. - - Copyright (C) 2006-7 by Robert Knight - Copyright (C) 1997,1998 by Lars Doelle - + + Copyright 2006-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + Rewritten for QT4 by e_k , Copyright (C)2008 - + 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 @@ -40,14 +40,15 @@ #include #include #include -#include -#include +#include +#include +#include +// Konsole #include "Filter.h" #include "konsole_wcwidth.h" #include "ScreenWindow.h" #include "TerminalCharacterDecoder.h" -#include "ColorTables.h" using namespace Konsole; @@ -61,12 +62,36 @@ using namespace Konsole; "abcdefgjijklmnopqrstuvwxyz" \ "0123456789./+@" +const ColorEntry Konsole::base_color_table[TABLE_COLORS] = +// The following are almost IBM standard color codes, with some slight +// gamma correction for the dim colors to compensate for bright X screens. +// It contains the 8 ansiterm/xterm colors in 2 intensities. +{ + // Fixme: could add faint colors here, also. + // normal + ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 1), // Dfore, Dback + ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0x18,0x18), 0), // Black, Red + ColorEntry(QColor(0x18,0xB2,0x18), 0), ColorEntry( QColor(0xB2,0x68,0x18), 0), // Green, Yellow + ColorEntry(QColor(0x18,0x18,0xB2), 0), ColorEntry( QColor(0xB2,0x18,0xB2), 0), // Blue, Magenta + ColorEntry(QColor(0x18,0xB2,0xB2), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 0), // Cyan, White + // intensiv + ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 1), + ColorEntry(QColor(0x68,0x68,0x68), 0), ColorEntry( QColor(0xFF,0x54,0x54), 0), + ColorEntry(QColor(0x54,0xFF,0x54), 0), ColorEntry( QColor(0xFF,0xFF,0x54), 0), + ColorEntry(QColor(0x54,0x54,0xFF), 0), ColorEntry( QColor(0xFF,0x54,0xFF), 0), + ColorEntry(QColor(0x54,0xFF,0xFF), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 0) +}; + // scroll increment used when dragging selection at top/bottom of window. // static bool TerminalDisplay::_antialiasText = true; bool TerminalDisplay::HAVE_TRANSPARENCY = false; +// we use this to force QPainter to display text in LTR mode +// more information can be found in: http://unicode.org/reports/tr9/ +const QChar LTR_OVERRIDE_CHAR( 0x202D ); + /* ------------------------------------------------------------------------- */ /* */ /* Colors */ @@ -81,20 +106,24 @@ bool TerminalDisplay::HAVE_TRANSPARENCY = false; IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White */ -ScreenWindow * TerminalDisplay::screenWindow() const +ScreenWindow* TerminalDisplay::screenWindow() const { return _screenWindow; } -void TerminalDisplay::setScreenWindow(ScreenWindow * window) +void TerminalDisplay::setScreenWindow(ScreenWindow* window) { // disconnect existing screen window if any - if ( _screenWindow ) { + if ( _screenWindow ) + { disconnect( _screenWindow , 0 , this , 0 ); } _screenWindow = window; - if ( window ) { + if ( window ) + { + +// TODO: Determine if this is an issue. //#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?" connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) ); connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) ); @@ -103,26 +132,35 @@ void TerminalDisplay::setScreenWindow(ScreenWindow * window) } } -const ColorEntry * TerminalDisplay::colorTable() const +const ColorEntry* TerminalDisplay::colorTable() const { - return _colorTable; + return _colorTable; } - -void TerminalDisplay::setColorTable(const ColorEntry table[]) +void TerminalDisplay::setBackgroundColor(const QColor& color) { - for (int i = 0; i < TABLE_COLORS; i++) { - _colorTable[i] = table[i]; - } - + _colorTable[DEFAULT_BACK_COLOR].color = color; QPalette p = palette(); - p.setColor( backgroundRole(), _colorTable[DEFAULT_BACK_COLOR].color ); - setPalette( p ); + p.setColor( backgroundRole(), color ); + setPalette( p ); - // Avoid propagating the palette change to the scroll bar - _scrollBar->setPalette( QApplication::palette() ); + // Avoid propagating the palette change to the scroll bar + _scrollBar->setPalette( QApplication::palette() ); update(); } +void TerminalDisplay::setForegroundColor(const QColor& color) +{ + _colorTable[DEFAULT_FORE_COLOR].color = color; + + update(); +} +void TerminalDisplay::setColorTable(const ColorEntry table[]) +{ + for (int i = 0; i < TABLE_COLORS; i++) + _colorTable[i] = table[i]; + + setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color); +} /* ------------------------------------------------------------------------- */ /* */ @@ -142,85 +180,87 @@ void TerminalDisplay::setColorTable(const ColorEntry table[]) QCodec. */ -static inline bool isLineChar(quint16 c) +static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);} +static inline bool isLineCharString(const QString& string) { - return ((c & 0xFF80) == 0x2500); + return (string.length() > 0) && (isLineChar(string.at(0).unicode())); } -static inline bool isLineCharString(const QString & string) -{ - return (string.length() > 0) && (isLineChar(string.at(0).unicode())); -} - + // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. -unsigned short Konsole::vt100_graphics[32] = { - // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 - 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, - 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, - 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, - 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 +unsigned short Konsole::vt100_graphics[32] = +{ // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 + 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, + 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, + 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, + 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 }; -void TerminalDisplay::fontChange(const QFont &) +void TerminalDisplay::fontChange(const QFont&) { - QFontMetrics fm(font()); - _fontHeight = fm.height() + _lineSpacing; + QFontMetrics fm(font()); + _fontHeight = fm.height() + _lineSpacing; + // 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 + _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR)); - // 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 - _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR)); + _fixedFont = true; - _fixedFont = true; - - int fw = fm.width(REPCHAR[0]); - for (unsigned int i=1; i< strlen(REPCHAR); i++) { - if (fw != fm.width(REPCHAR[i])) { - _fixedFont = false; - break; - } + int fw = fm.width(REPCHAR[0]); + for(unsigned int i=1; i< strlen(REPCHAR); i++) + { + if (fw != fm.width(REPCHAR[i])) + { + _fixedFont = false; + break; } + } - if (_fontWidth < 1) { - _fontWidth=1; - } + if (_fontWidth < 1) + _fontWidth=1; - _fontAscent = fm.ascent(); + _fontAscent = fm.ascent(); - emit changedFontMetricSignal( _fontHeight, _fontWidth ); - propagateSize(); - update(); + emit changedFontMetricSignal( _fontHeight, _fontWidth ); + propagateSize(); + update(); } -void TerminalDisplay::setVTFont(const QFont & f) +void TerminalDisplay::setVTFont(const QFont& f) { - QFont font = f; + QFont font = f; - QFontMetrics metrics(font); + QFontMetrics metrics(font); - if ( metrics.height() < height() && metrics.maxWidth() < width() ) { - // hint that text should be drawn without anti-aliasing. - // depending on the user's font configuration, this may not be respected - if (!_antialiasText) { - font.setStyleStrategy( QFont::NoAntialias ); - } + if ( !QFontInfo(font).fixedPitch() ) + { + qDebug() << "Using an unsupported variable-width font in the terminal. This may produce display errors."; + } - // 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. - font.setKerning(false); + if ( metrics.height() < height() && metrics.maxWidth() < width() ) + { + // hint that text should be drawn without anti-aliasing. + // depending on the user's font configuration, this may not be respected + if (!_antialiasText) + font.setStyleStrategy( QFont::NoAntialias ); + + // 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. + font.setKerning(false); - QWidget::setFont(font); - fontChange(font); - } + QWidget::setFont(font); + fontChange(font); + } } void TerminalDisplay::setFont(const QFont &) { - // ignore font change request if not coming from konsole itself + // ignore font change request if not coming from konsole itself } /* ------------------------------------------------------------------------- */ @@ -229,113 +269,116 @@ void TerminalDisplay::setFont(const QFont &) /* */ /* ------------------------------------------------------------------------- */ -TerminalDisplay::TerminalDisplay(QWidget * parent) - :QWidget(parent) - ,_screenWindow(0) - ,_allowBell(true) - ,_gridLayout(0) - ,_fontHeight(1) - ,_fontWidth(1) - ,_fontAscent(1) - ,_lines(1) - ,_columns(1) - ,_usedLines(1) - ,_usedColumns(1) - ,_contentHeight(1) - ,_contentWidth(1) - ,_image(0) - ,_randomSeed(0) - ,_resizing(false) - ,_terminalSizeHint(false) - ,_terminalSizeStartup(true) - ,_bidiEnabled(false) - ,_actSel(0) - ,_wordSelectionMode(false) - ,_lineSelectionMode(false) - ,_preserveLineBreaks(false) - ,_columnSelectionMode(false) - ,_scrollbarLocation(NoScrollBar) - ,_wordCharacters(":@-./_~") - ,_bellMode(SystemBeepBell) - ,_blinking(false) - ,_cursorBlinking(false) - ,_hasBlinkingCursor(false) - ,_ctrlDrag(false) - ,_tripleClickMode(SelectWholeLine) - ,_isFixedSize(false) - ,_possibleTripleClick(false) - ,_resizeWidget(0) - ,_resizeTimer(0) - ,_flowControlWarningEnabled(false) - ,_outputSuspendedLabel(0) - ,_lineSpacing(0) - ,_colorsInverted(false) - ,_blendColor(qRgba(0,0,0,0xff)) - ,_filterChain(new TerminalImageFilterChain()) - ,_cursorShape(BlockCursor) +TerminalDisplay::TerminalDisplay(QWidget *parent) +:QWidget(parent) +,_screenWindow(0) +,_allowBell(true) +,_gridLayout(0) +,_fontHeight(1) +,_fontWidth(1) +,_fontAscent(1) +,_boldIntense(true) +,_lines(1) +,_columns(1) +,_usedLines(1) +,_usedColumns(1) +,_contentHeight(1) +,_contentWidth(1) +,_image(0) +,_randomSeed(0) +,_resizing(false) +,_terminalSizeHint(false) +,_terminalSizeStartup(true) +,_bidiEnabled(false) +,_actSel(0) +,_wordSelectionMode(false) +,_lineSelectionMode(false) +,_preserveLineBreaks(false) +,_columnSelectionMode(false) +,_scrollbarLocation(NoScrollBar) +,_wordCharacters(":@-./_~") +,_bellMode(SystemBeepBell) +,_blinking(false) +,_hasBlinker(false) +,_cursorBlinking(false) +,_hasBlinkingCursor(false) +,_allowBlinkingText(true) +,_ctrlDrag(false) +,_tripleClickMode(SelectWholeLine) +,_isFixedSize(false) +,_possibleTripleClick(false) +,_resizeWidget(0) +,_resizeTimer(0) +,_flowControlWarningEnabled(false) +,_outputSuspendedLabel(0) +,_lineSpacing(0) +,_colorsInverted(false) +,_blendColor(qRgba(0,0,0,0xff)) +,_filterChain(new TerminalImageFilterChain()) +,_cursorShape(BlockCursor) { - // terminal applications are not designed with Right-To-Left in mind, - // so the layout is forced to Left-To-Right - setLayoutDirection(Qt::LeftToRight); + // terminal applications are not designed with Right-To-Left in mind, + // so the layout is forced to Left-To-Right + setLayoutDirection(Qt::LeftToRight); - // The offsets are not yet calculated. - // Do not calculate these too often to be more smoothly when resizing - // konsole in opaque mode. - _topMargin = DEFAULT_TOP_MARGIN; - _leftMargin = DEFAULT_LEFT_MARGIN; + // The offsets are not yet calculated. + // Do not calculate these too often to be more smoothly when resizing + // konsole in opaque mode. + _topMargin = DEFAULT_TOP_MARGIN; + _leftMargin = DEFAULT_LEFT_MARGIN; - // create scroll bar for scrolling output up and down - // set the scroll bar's slider to occupy the whole area of the scroll bar initially - _scrollBar = new QScrollBar(this); - setScroll(0,0); - _scrollBar->setCursor( Qt::ArrowCursor ); - connect(_scrollBar, SIGNAL(valueChanged(int)), this, - SLOT(scrollBarPositionChanged(int))); + // create scroll bar for scrolling output up and down + // set the scroll bar's slider to occupy the whole area of the scroll bar initially + _scrollBar = new QScrollBar(this); + setScroll(0,0); + _scrollBar->setCursor( Qt::ArrowCursor ); + connect(_scrollBar, SIGNAL(valueChanged(int)), this, + SLOT(scrollBarPositionChanged(int))); - // setup timers for blinking cursor and text - _blinkTimer = new QTimer(this); - connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent())); - _blinkCursorTimer = new QTimer(this); - connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent())); + // setup timers for blinking cursor and text + _blinkTimer = new QTimer(this); + connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent())); + _blinkCursorTimer = new QTimer(this); + connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent())); -// QCursor::setAutoHideCursor( this, true ); + //KCursor::setAutoHideCursor( this, true ); + + setUsesMouse(true); + setColorTable(base_color_table); + setMouseTracking(true); - setUsesMouse(true); - setColorTable(whiteonblack_color_table); -// setColorTable(blackonlightyellow_color_table); - setMouseTracking(true); + // Enable drag and drop + setAcceptDrops(true); // attempt + dragInfo.state = diNone; - // Enable drag and drop - setAcceptDrops(true); // attempt - dragInfo.state = diNone; + setFocusPolicy( Qt::WheelFocus ); - setFocusPolicy( Qt::WheelFocus ); + // enable input method support + setAttribute(Qt::WA_InputMethodEnabled, true); - // 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); - // this is an important optimization, it tells Qt - // that TerminalDisplay will handle repainting its entire area. - setAttribute(Qt::WA_OpaquePaintEvent); + _gridLayout = new QGridLayout(this); + _gridLayout->setContentsMargins(0, 0, 0, 0); - _gridLayout = new QGridLayout(this); - _gridLayout->setMargin(0); + setLayout( _gridLayout ); - setLayout( _gridLayout ); - - //set up a warning message when the user presses Ctrl+S to avoid confusion - connect( this,SIGNAL(flowControlKeyPressed(bool)),this,SLOT(outputSuspended(bool)) ); + new AutoScrollHandler(this); } TerminalDisplay::~TerminalDisplay() { - qApp->removeEventFilter( this ); + disconnect(_blinkTimer); + disconnect(_blinkCursorTimer); + qApp->removeEventFilter( this ); + + delete[] _image; - delete[] _image; - - delete _gridLayout; - delete _outputSuspendedLabel; - delete _filterChain; + delete _gridLayout; + delete _outputSuspendedLabel; + delete _filterChain; } /* ------------------------------------------------------------------------- */ @@ -363,7 +406,8 @@ where _ = none */ -enum LineEncode { +enum LineEncode +{ TopL = (1<<1), TopC = (1<<2), TopR = (1<<3), @@ -393,7 +437,7 @@ enum LineEncode { #include "LineFont.h" -static void drawLineChar(QPainter & paint, int x, int y, int w, int h, uchar code) +static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code) { //Calculate cell midpoints, end points. int cx = x + w/2; @@ -404,101 +448,81 @@ static void drawLineChar(QPainter & paint, int x, int y, int w, int h, uchar cod quint32 toDraw = LineChars[code]; //Top _lines: - if (toDraw & TopL) { + if (toDraw & TopL) paint.drawLine(cx-1, y, cx-1, cy-2); - } - if (toDraw & TopC) { + if (toDraw & TopC) paint.drawLine(cx, y, cx, cy-2); - } - if (toDraw & TopR) { + if (toDraw & TopR) paint.drawLine(cx+1, y, cx+1, cy-2); - } //Bot _lines: - if (toDraw & BotL) { + if (toDraw & BotL) paint.drawLine(cx-1, cy+2, cx-1, ey); - } - if (toDraw & BotC) { + if (toDraw & BotC) paint.drawLine(cx, cy+2, cx, ey); - } - if (toDraw & BotR) { + if (toDraw & BotR) paint.drawLine(cx+1, cy+2, cx+1, ey); - } //Left _lines: - if (toDraw & LeftT) { + if (toDraw & LeftT) paint.drawLine(x, cy-1, cx-2, cy-1); - } - if (toDraw & LeftC) { + if (toDraw & LeftC) paint.drawLine(x, cy, cx-2, cy); - } - if (toDraw & LeftB) { + if (toDraw & LeftB) paint.drawLine(x, cy+1, cx-2, cy+1); - } //Right _lines: - if (toDraw & RightT) { + if (toDraw & RightT) paint.drawLine(cx+2, cy-1, ex, cy-1); - } - if (toDraw & RightC) { + if (toDraw & RightC) paint.drawLine(cx+2, cy, ex, cy); - } - if (toDraw & RightB) { + if (toDraw & RightB) paint.drawLine(cx+2, cy+1, ex, cy+1); - } //Intersection points. - if (toDraw & Int11) { + if (toDraw & Int11) paint.drawPoint(cx-1, cy-1); - } - if (toDraw & Int12) { + if (toDraw & Int12) paint.drawPoint(cx, cy-1); - } - if (toDraw & Int13) { + if (toDraw & Int13) paint.drawPoint(cx+1, cy-1); - } - if (toDraw & Int21) { + if (toDraw & Int21) paint.drawPoint(cx-1, cy); - } - if (toDraw & Int22) { + if (toDraw & Int22) paint.drawPoint(cx, cy); - } - if (toDraw & Int23) { + if (toDraw & Int23) paint.drawPoint(cx+1, cy); - } - if (toDraw & Int31) { + if (toDraw & Int31) paint.drawPoint(cx-1, cy+1); - } - if (toDraw & Int32) { + if (toDraw & Int32) paint.drawPoint(cx, cy+1); - } - if (toDraw & Int33) { + if (toDraw & Int33) paint.drawPoint(cx+1, cy+1); - } } -void TerminalDisplay::drawLineCharString( QPainter & painter, int x, int y, const QString & str, - const Character * attributes) +void TerminalDisplay::drawLineCharString( QPainter& painter, int x, int y, const QString& str, + const Character* attributes) { - const QPen & currentPen = painter.pen(); - - if ( attributes->rendition & RE_BOLD ) { - QPen boldPen(currentPen); - boldPen.setWidth(3); - painter.setPen( boldPen ); - } - - for (int i=0 ; i < str.length(); i++) { - uchar code = str[i].cell(); - if (LineChars[code]) { - drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code); + const QPen& currentPen = painter.pen(); + + if ( (attributes->rendition & RE_BOLD) && _boldIntense ) + { + QPen boldPen(currentPen); + boldPen.setWidth(3); + painter.setPen( boldPen ); + } + + for (int i=0 ; i < str.length(); i++) + { + uchar code = str[i].cell(); + if (LineChars[code]) + drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code); } - } - painter.setPen( currentPen ); + painter.setPen( currentPen ); } void TerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape) @@ -509,18 +533,16 @@ TerminalDisplay::KeyboardCursorShape TerminalDisplay::keyboardCursorShape() cons { return _cursorShape; } -void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor & color) +void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color) { - if (useForegroundColor) { - _cursorColor = QColor(); // an invalid color means that - } - // the foreground color of the - // current character should - // be used + if (useForegroundColor) + _cursorColor = QColor(); // an invalid color means that + // the foreground color of the + // current character should + // be used - else { + else _cursorColor = color; - } } QColor TerminalDisplay::keyboardCursorColor() const { @@ -534,63 +556,68 @@ void TerminalDisplay::setOpacity(qreal opacity) // enable automatic background filling to prevent the display // flickering if there is no transparency - if ( color.alpha() == 255 ) { + /*if ( color.alpha() == 255 ) + { setAutoFillBackground(true); - } else { - setAutoFillBackground(false); } + else + { + setAutoFillBackground(false); + }*/ _blendColor = color.rgba(); } -void TerminalDisplay::drawBackground(QPainter & painter, const QRect & rect, const QColor & backgroundColor, bool useOpacitySetting ) +void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting ) { - // 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(); + // 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 ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting ) { - QColor color(backgroundColor); - color.setAlpha(qAlpha(_blendColor)); + if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting ) + { + QColor color(backgroundColor); + color.setAlpha(qAlpha(_blendColor)); - painter.save(); - painter.setCompositionMode(QPainter::CompositionMode_Source); - painter.fillRect(contentsRect, color); - painter.restore(); - } else { - painter.fillRect(contentsRect, backgroundColor); - } + painter.save(); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(contentsRect, color); + painter.restore(); + } + else + painter.fillRect(contentsRect, backgroundColor); - painter.fillRect(scrollBarArea,_scrollBar->palette().background()); + painter.fillRect(scrollBarArea,_scrollBar->palette().background()); } -void TerminalDisplay::drawCursor(QPainter & painter, - const QRect & rect, - const QColor & foregroundColor, +void TerminalDisplay::drawCursor(QPainter& painter, + const QRect& rect, + const QColor& foregroundColor, const QColor& /*backgroundColor*/, - bool & invertCharacterColor) + bool& invertCharacterColor) { QRect cursorRect = rect; cursorRect.setHeight(_fontHeight - _lineSpacing - 1); + + if (!_cursorBlinking) + { + if ( _cursorColor.isValid() ) + painter.setPen(_cursorColor); + else + painter.setPen(foregroundColor); - if (!_cursorBlinking) { - if ( _cursorColor.isValid() ) { - painter.setPen(_cursorColor); - } else { - painter.setPen(foregroundColor); - } - - if ( _cursorShape == BlockCursor ) { + if ( _cursorShape == BlockCursor ) + { // draw the cursor outline, adjusting the area so that // it is draw entirely inside 'rect' int penWidth = qMax(1,painter.pen().width()); @@ -599,110 +626,118 @@ void TerminalDisplay::drawCursor(QPainter & painter, penWidth/2, - penWidth/2 - penWidth%2, - penWidth/2 - penWidth%2)); - if ( hasFocus() ) { + if ( hasFocus() ) + { painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor); - - if ( !_cursorColor.isValid() ) { + + if ( !_cursorColor.isValid() ) + { // invert the colour used to draw the text to ensure that the character at // the cursor position is readable invertCharacterColor = true; } } - } else if ( _cursorShape == UnderlineCursor ) + } + else if ( _cursorShape == UnderlineCursor ) painter.drawLine(cursorRect.left(), cursorRect.bottom(), cursorRect.right(), cursorRect.bottom()); - else if ( _cursorShape == IBeamCursor ) + else if ( _cursorShape == IBeamCursor ) painter.drawLine(cursorRect.left(), cursorRect.top(), cursorRect.left(), cursorRect.bottom()); - + } } -void TerminalDisplay::drawCharacters(QPainter & painter, - const QRect & rect, - const QString & text, - const Character * style, +void TerminalDisplay::drawCharacters(QPainter& painter, + const QRect& rect, + const QString& text, + const Character* style, bool invertCharacterColor) { // don't draw text which is currently blinking - if ( _blinking && (style->rendition & RE_BLINK) ) { - return; - } - + if ( _blinking && (style->rendition & RE_BLINK) ) + return; + // setup bold and underline - bool useBold = style->rendition & RE_BOLD || style->isBold(_colorTable) || font().bold(); + bool useBold; + ColorEntry::FontWeight weight = style->fontWeight(_colorTable); + if (weight == ColorEntry::UseCurrentFormat) + useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); + else + useBold = (weight == ColorEntry::Bold) ? true : false; bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); QFont font = painter.font(); - if ( font.bold() != useBold - || font.underline() != useUnderline ) { - font.setBold(useBold); - font.setUnderline(useUnderline); - painter.setFont(font); + if ( font.bold() != useBold + || font.underline() != useUnderline ) + { + font.setBold(useBold); + font.setUnderline(useUnderline); + painter.setFont(font); } - const CharacterColor & textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor ); + // setup pen + const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor ); const QColor color = textColor.color(_colorTable); - QPen pen = painter.pen(); - if ( pen.color() != color ) { + if ( pen.color() != color ) + { pen.setColor(color); painter.setPen(color); } + // draw text - if ( isLineCharString(text) ) { + if ( isLineCharString(text) ) drawLineCharString(painter,rect.x(),rect.y(),text,style); - } else { + else + { // the drawText(rect,flags,string) overload is used here with null flags - // instead of drawText(rect,string) because the (rect,string) overload causes - // the application's default layout direction to be used instead of + // instead of drawText(rect,string) because the (rect,string) overload causes + // the application's default layout direction to be used instead of // the widget-specific layout direction, which should always be // Qt::LeftToRight for this widget - painter.drawText(rect,0,text); + // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2 + if (_bidiEnabled) + painter.drawText(rect,0,text); + else + painter.drawText(rect,0,LTR_OVERRIDE_CHAR+text); } } -void TerminalDisplay::drawTextFragment(QPainter & painter , - const QRect & rect, - const QString & text, - const Character * style) +void TerminalDisplay::drawTextFragment(QPainter& painter , + const QRect& rect, + const QString& text, + const Character* style) { painter.save(); - // setup painter + // setup painter const QColor foregroundColor = style->foregroundColor.color(_colorTable); const QColor backgroundColor = style->backgroundColor.color(_colorTable); - + // draw background if different from the display's background color - if ( backgroundColor != palette().background().color() ) { - drawBackground(painter,rect,backgroundColor, false /* do not use transparency */); - } + if ( backgroundColor != palette().background().color() ) + drawBackground(painter,rect,backgroundColor, + false /* do not use transparency */); // draw cursor shape if the current character is the cursor // this may alter the foreground and background colors bool invertCharacterColor = false; - - if ( style->rendition & RE_CURSOR ) { + if ( style->rendition & RE_CURSOR ) drawCursor(painter,rect,foregroundColor,backgroundColor,invertCharacterColor); - } + // draw text drawCharacters(painter,rect,text,style,invertCharacterColor); painter.restore(); } -void TerminalDisplay::setRandomSeed(uint randomSeed) -{ - _randomSeed = randomSeed; -} -uint TerminalDisplay::randomSeed() const -{ - return _randomSeed; -} +void TerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; } +uint TerminalDisplay::randomSeed() const { return _randomSeed; } #if 0 /*! @@ -726,49 +761,67 @@ void TerminalDisplay::setCursorPos(const int curx, const int cury) // scrolls the image by 'lines', down if lines > 0 or up otherwise. // -// the terminal emulation keeps track of the scrolling of the character -// image as it receives input, and when the view is updated, it calls scrollImage() -// with the final scroll amount. this improves performance because scrolling the -// display is much cheaper than re-rendering all the text for the -// part of the image which has moved up or down. +// the terminal emulation keeps track of the scrolling of the character +// image as it receives input, and when the view is updated, it calls scrollImage() +// with the final scroll amount. this improves performance because scrolling the +// display is much cheaper than re-rendering all the text for the +// part of the image which has moved up or down. // Instead only new lines have to be drawn -// -// note: it is important that the area of the display which is -// scrolled aligns properly with the character grid - -// which has a top left point at (_leftMargin,_topMargin) , -// a cell width of _fontWidth and a cell height of _fontHeight). -void TerminalDisplay::scrollImage(int lines , const QRect & screenWindowRegion) +void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) { - // if the flow control warning is enabled this will interfere with the - // scrolling optimisations and cause artifacts. the simple solution here - // is to just disable the optimisation whilst it is visible - if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() ) { + // if the flow control warning is enabled this will interfere with the + // scrolling optimizations and cause artifacts. the simple solution here + // is to just disable the optimization whilst it is visible + if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() ) return; - } // constrain the region to the display // the bottom of the region is capped to the number of lines in the display's // internal image - 2, so that the height of 'region' is strictly less // than the height of the internal image. QRect region = screenWindowRegion; - region.setBottom( qMin(region.bottom(),this->_lines-2) ); + region.setBottom( qMin(region.bottom(),this->_lines-2) ); - if ( lines == 0 - || _image == 0 - || !region.isValid() - || (region.top() + abs(lines)) >= region.bottom() - || this->_lines <= region.height() ) { - return; - } + // return if there is nothing to do + if ( lines == 0 + || _image == 0 + || !region.isValid() + || (region.top() + abs(lines)) >= region.bottom() + || this->_lines <= region.height() ) return; + // hide terminal size label to prevent it being scrolled + if (_resizeWidget && _resizeWidget->isVisible()) + _resizeWidget->hide(); + + // Note: With Qt 4.4 the left edge of the scrolled area must be at 0 + // to get the correct (newly exposed) part of the widget repainted. + // + // The right edge must be before the left edge of the scroll bar to + // avoid triggering a repaint of the entire widget, the distance is + // given by SCROLLBAR_CONTENT_GAP + // + // Set the QT_FLUSH_PAINT environment variable to '1' before starting the + // application to monitor repainting. + // + int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->width(); + const int SCROLLBAR_CONTENT_GAP = 1; QRect scrollRect; - - void * firstCharPos = &_image[ region.top() * this->_columns ]; - void * lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ]; + if ( _scrollbarLocation == ScrollBarLeft ) + { + scrollRect.setLeft(scrollBarWidth+SCROLLBAR_CONTENT_GAP); + scrollRect.setRight(width()); + } + else + { + scrollRect.setLeft(0); + scrollRect.setRight(width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP); + } + void* firstCharPos = &_image[ region.top() * this->_columns ]; + void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ]; int top = _topMargin + (region.top() * _fontHeight); int linesToMove = region.height() - abs(lines); - int bytesToMove = linesToMove * + int bytesToMove = linesToMove * this->_columns * sizeof(Character); @@ -776,69 +829,86 @@ void TerminalDisplay::scrollImage(int lines , const QRect & screenWindowRegion) Q_ASSERT( bytesToMove > 0 ); //scroll internal image - if ( lines > 0 ) { + if ( lines > 0 ) + { // check that the memory areas that we are going to move are valid - Q_ASSERT( (char *)lastCharPos + bytesToMove < - (char *)(_image + (this->_lines * this->_columns)) ); - - Q_ASSERT( (lines*this->_columns) < _imageSize ); + Q_ASSERT( (char*)lastCharPos + bytesToMove < + (char*)(_image + (this->_lines * this->_columns)) ); + + Q_ASSERT( (lines*this->_columns) < _imageSize ); //scroll internal image down - memmove( firstCharPos , lastCharPos , bytesToMove ); - - //set region of display to scroll, making sure that - //the region aligns correctly to the character grid - scrollRect = QRect( _leftMargin , top, - this->_usedColumns * _fontWidth , - linesToMove * _fontHeight ); - } else { + memmove( firstCharPos , lastCharPos , bytesToMove ); + + //set region of display to scroll + scrollRect.setTop(top); + } + else + { // check that the memory areas that we are going to move are valid - Q_ASSERT( (char *)firstCharPos + bytesToMove < - (char *)(_image + (this->_lines * this->_columns)) ); + Q_ASSERT( (char*)firstCharPos + bytesToMove < + (char*)(_image + (this->_lines * this->_columns)) ); //scroll internal image up - memmove( lastCharPos , firstCharPos , bytesToMove ); - - //set region of the display to scroll, making sure that - //the region aligns correctly to the character grid - QPoint topPoint( _leftMargin , top + abs(lines)*_fontHeight ); - - scrollRect = QRect( topPoint , - QSize( this->_usedColumns*_fontWidth , - linesToMove * _fontHeight )); + memmove( lastCharPos , firstCharPos , bytesToMove ); + + //set region of the display to scroll + scrollRect.setTop(top + abs(lines) * _fontHeight); } + scrollRect.setHeight(linesToMove * _fontHeight ); + + Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty()); //scroll the display vertically to match internal _image scroll( 0 , _fontHeight * (-lines) , scrollRect ); } -QRegion TerminalDisplay::hotSpotRegion() const +QRegion TerminalDisplay::hotSpotRegion() const { QRegion region; - foreach( Filter::HotSpot* hotSpot , _filterChain->hotSpots() ) { - QRect rect; - rect.setLeft(hotSpot->startColumn()); - rect.setTop(hotSpot->startLine()); - rect.setRight(hotSpot->endColumn()); - rect.setBottom(hotSpot->endLine()); - - region |= imageToWidget(rect); + foreach( Filter::HotSpot* hotSpot , _filterChain->hotSpots() ) + { + QRect r; + if (hotSpot->startLine()==hotSpot->endLine()) { + r.setLeft(hotSpot->startColumn()); + r.setTop(hotSpot->startLine()); + r.setRight(hotSpot->endColumn()); + r.setBottom(hotSpot->endLine()); + region |= imageToWidget(r);; + } else { + r.setLeft(hotSpot->startColumn()); + r.setTop(hotSpot->startLine()); + r.setRight(_columns); + r.setBottom(hotSpot->startLine()); + region |= imageToWidget(r);; + for ( int line = hotSpot->startLine()+1 ; line < hotSpot->endLine() ; line++ ) { + r.setLeft(0); + r.setTop(line); + r.setRight(_columns); + r.setBottom(line); + region |= imageToWidget(r);; + } + r.setLeft(0); + r.setTop(hotSpot->endLine()); + r.setRight(hotSpot->endColumn()); + r.setBottom(hotSpot->endLine()); + region |= imageToWidget(r);; + } } return region; } -void TerminalDisplay::processFilters() +void TerminalDisplay::processFilters() { - if (!_screenWindow) { + if (!_screenWindow) return; - } QRegion preUpdateHotSpots = hotSpotRegion(); // use _screenWindow->getImage() here rather than _image because // other classes may call processFilters() when this display's // ScreenWindow emits a scrolled() signal - which will happen before - // updateImage() is called on the display and therefore _image is + // updateImage() is called on the display and therefore _image is // out of date at this point _filterChain->setImage( _screenWindow->getImage(), _screenWindow->windowLines(), @@ -851,353 +921,411 @@ void TerminalDisplay::processFilters() update( preUpdateHotSpots | postUpdateHotSpots ); } -void TerminalDisplay::updateImage() +void TerminalDisplay::updateImage() { - if ( !_screenWindow ) { - return; + if ( !_screenWindow ) + return; + + // optimization - scroll the existing image where possible and + // avoid expensive text drawing for parts of the image that + // can simply be moved up or down + scrollImage( _screenWindow->scrollCount() , + _screenWindow->scrollRegion() ); + _screenWindow->resetScrollCount(); + + Character* const newimg = _screenWindow->getImage(); + int lines = _screenWindow->windowLines(); + int columns = _screenWindow->windowColumns(); + + setScroll( _screenWindow->currentLine() , _screenWindow->lineCount() ); + + if (!_image) + updateImageSize(); // Create _image + + Q_ASSERT( this->_usedLines <= this->_lines ); + Q_ASSERT( this->_usedColumns <= this->_columns ); + + int y,x,len; + + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + _hasBlinker = false; + + CharacterColor cf; // undefined + CharacterColor _clipboard; // undefined + int cr = -1; // undefined + + const int linesToUpdate = qMin(this->_lines, qMax(0,lines )); + const int columnsToUpdate = qMin(this->_columns,qMax(0,columns)); + + QChar *disstrU = new QChar[columnsToUpdate]; + char *dirtyMask = new char[columnsToUpdate+2]; + QRegion dirtyRegion; + + // debugging variable, this records the number of lines that are found to + // be 'dirty' ( ie. have changed from the old _image to the new _image ) and + // which therefore need to be repainted + int dirtyLineCount = 0; + + for (y = 0; y < linesToUpdate; ++y) + { + const Character* currentLine = &_image[y*this->_columns]; + const Character* const newLine = &newimg[y*columns]; + + bool updateLine = false; + + // The dirty mask indicates which characters need repainting. We also + // mark surrounding neighbours dirty, in case the character exceeds + // its cell boundaries + memset(dirtyMask, 0, columnsToUpdate+2); + + for( x = 0 ; x < columnsToUpdate ; ++x) + { + if ( newLine[x] != currentLine[x] ) + { + dirtyMask[x] = true; + } } - // optimization - scroll the existing image where possible and - // avoid expensive text drawing for parts of the image that - // can simply be moved up or down - scrollImage( _screenWindow->scrollCount() , - _screenWindow->scrollRegion() ); - _screenWindow->resetScrollCount(); + if (!_resizing) // not while _resizing, we're expecting a paintEvent + for (x = 0; x < columnsToUpdate; ++x) + { + _hasBlinker |= (newLine[x].rendition & RE_BLINK); + + // Start drawing if this character or the next one differs. + // We also take the next one into account to handle the situation + // where characters exceed their cell width. + if (dirtyMask[x]) + { + quint16 c = newLine[x+0].character; + if ( !c ) + continue; + int p = 0; + disstrU[p++] = c; //fontMap(c); + bool lineDraw = isLineChar(c); + bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0); + cr = newLine[x].rendition; + _clipboard = newLine[x].backgroundColor; + if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor; + int lln = columnsToUpdate - x; + for (len = 1; len < lln; ++len) + { + const Character& ch = newLine[x+len]; - Character * const newimg = _screenWindow->getImage(); - int lines = _screenWindow->windowLines(); - int columns = _screenWindow->windowColumns(); + if (!ch.character) + continue; // Skip trailing part of multi-col chars. - setScroll( _screenWindow->currentLine() , _screenWindow->lineCount() ); + bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0); - if (!_image) { - updateImageSize(); // Create _image - } + if ( ch.foregroundColor != cf || + ch.backgroundColor != _clipboard || + ch.rendition != cr || + !dirtyMask[x+len] || + isLineChar(c) != lineDraw || + nextIsDoubleWidth != doubleWidth ) + break; - Q_ASSERT( this->_usedLines <= this->_lines ); - Q_ASSERT( this->_usedColumns <= this->_columns ); - - int y,x,len; - - QPoint tL = contentsRect().topLeft(); - - int tLx = tL.x(); - int tLy = tL.y(); - _hasBlinker = false; - - CharacterColor cf; // undefined - CharacterColor _clipboard; // undefined - int cr = -1; // undefined - - const int linesToUpdate = qMin(this->_lines, qMax(0,lines )); - const int columnsToUpdate = qMin(this->_columns,qMax(0,columns)); - - QChar * disstrU = new QChar[columnsToUpdate]; - char * dirtyMask = new char[columnsToUpdate+2]; - QRegion dirtyRegion; - - // debugging variable, this records the number of lines that are found to - // be 'dirty' ( ie. have changed from the old _image to the new _image ) and - // which therefore need to be repainted - int dirtyLineCount = 0; - - for (y = 0; y < linesToUpdate; y++) { - const Character * currentLine = &_image[y*this->_columns]; - const Character * const newLine = &newimg[y*columns]; - - bool updateLine = false; - - // The dirty mask indicates which characters need repainting. We also - // mark surrounding neighbours dirty, in case the character exceeds - // its cell boundaries - memset(dirtyMask, 0, columnsToUpdate+2); - - for ( x = 0 ; x < columnsToUpdate ; x++) { - if ( newLine[x] != currentLine[x] ) { - dirtyMask[x] = true; - } + disstrU[p++] = c; //fontMap(c); } - if (!_resizing) // not while _resizing, we're expecting a paintEvent - for (x = 0; x < columnsToUpdate; x++) { - _hasBlinker |= (newLine[x].rendition & RE_BLINK); + QString unistr(disstrU, p); - // Start drawing if this character or the next one differs. - // We also take the next one into account to handle the situation - // where characters exceed their cell width. - if (dirtyMask[x]) { - quint16 c = newLine[x+0].character; - if ( !c ) { - continue; - } - int p = 0; - disstrU[p++] = c; //fontMap(c); - bool lineDraw = isLineChar(c); - bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0); - cr = newLine[x].rendition; - _clipboard = newLine[x].backgroundColor; - if (newLine[x].foregroundColor != cf) { - cf = newLine[x].foregroundColor; - } - int lln = columnsToUpdate - x; - for (len = 1; len < lln; len++) { - const Character & ch = newLine[x+len]; + bool saveFixedFont = _fixedFont; + if (lineDraw) + _fixedFont = false; + if (doubleWidth) + _fixedFont = false; - if (!ch.character) { - continue; // Skip trailing part of multi-col chars. - } + updateLine = true; - bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0); - - if ( ch.foregroundColor != cf || - ch.backgroundColor != _clipboard || - ch.rendition != cr || - !dirtyMask[x+len] || - isLineChar(c) != lineDraw || - nextIsDoubleWidth != doubleWidth ) { - break; - } - - disstrU[p++] = c; //fontMap(c); - } - - QString unistr(disstrU, p); - - bool saveFixedFont = _fixedFont; - if (lineDraw) { - _fixedFont = false; - } - if (doubleWidth) { - _fixedFont = false; - } - - updateLine = true; - - _fixedFont = saveFixedFont; - x += len - 1; - } - - } - - //both the top and bottom halves of double height _lines must always be redrawn - //although both top and bottom halves contain the same characters, only - //the top one is actually - //drawn. - if (_lineProperties.count() > y) { - updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT); - } - - // if the characters on the line are different in the old and the new _image - // then this line must be repainted. - if (updateLine) { - dirtyLineCount++; - - // add the area occupied by this line to the region which needs to be - // repainted - QRect dirtyRect = QRect( _leftMargin+tLx , - _topMargin+tLy+_fontHeight*y , - _fontWidth * columnsToUpdate , - _fontHeight ); - - dirtyRegion |= dirtyRect; - } - - // replace the line of characters in the old _image with the - // current line of the new _image - memcpy((void *)currentLine,(const void *)newLine,columnsToUpdate*sizeof(Character)); + _fixedFont = saveFixedFont; + x += len - 1; + } + } - // if the new _image is smaller than the previous _image, then ensure that the area - // outside the new _image is cleared - if ( linesToUpdate < _usedLines ) { - dirtyRegion |= QRect( _leftMargin+tLx , - _topMargin+tLy+_fontHeight*linesToUpdate , - _fontWidth * this->_columns , - _fontHeight * (_usedLines-linesToUpdate) ); - } - _usedLines = linesToUpdate; + //both the top and bottom halves of double height _lines must always be redrawn + //although both top and bottom halves contain the same characters, only + //the top one is actually + //drawn. + if (_lineProperties.count() > y) + updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT); - if ( columnsToUpdate < _usedColumns ) { - dirtyRegion |= QRect( _leftMargin+tLx+columnsToUpdate*_fontWidth , - _topMargin+tLy , - _fontWidth * (_usedColumns-columnsToUpdate) , - _fontHeight * this->_lines ); - } - _usedColumns = columnsToUpdate; + // if the characters on the line are different in the old and the new _image + // then this line must be repainted. + if (updateLine) + { + dirtyLineCount++; - dirtyRegion |= _inputMethodData.previousPreeditRect; + // add the area occupied by this line to the region which needs to be + // repainted + QRect dirtyRect = QRect( _leftMargin+tLx , + _topMargin+tLy+_fontHeight*y , + _fontWidth * columnsToUpdate , + _fontHeight ); - // update the parts of the display which have changed - update(dirtyRegion); + dirtyRegion |= dirtyRect; + } - if ( _hasBlinker && !_blinkTimer->isActive()) { - _blinkTimer->start( BLINK_DELAY ); - } - if (!_hasBlinker && _blinkTimer->isActive()) { - _blinkTimer->stop(); - _blinking = false; - } - delete[] dirtyMask; - delete[] disstrU; + // replace the line of characters in the old _image with the + // current line of the new _image + memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character)); + } + + // if the new _image is smaller than the previous _image, then ensure that the area + // outside the new _image is cleared + if ( linesToUpdate < _usedLines ) + { + dirtyRegion |= QRect( _leftMargin+tLx , + _topMargin+tLy+_fontHeight*linesToUpdate , + _fontWidth * this->_columns , + _fontHeight * (_usedLines-linesToUpdate) ); + } + _usedLines = linesToUpdate; + + if ( columnsToUpdate < _usedColumns ) + { + dirtyRegion |= QRect( _leftMargin+tLx+columnsToUpdate*_fontWidth , + _topMargin+tLy , + _fontWidth * (_usedColumns-columnsToUpdate) , + _fontHeight * this->_lines ); + } + _usedColumns = columnsToUpdate; + + dirtyRegion |= _inputMethodData.previousPreeditRect; + + // update the parts of the display which have changed + update(dirtyRegion); + + if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( TEXT_BLINK_DELAY ); + if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; } + delete[] dirtyMask; + delete[] disstrU; } void TerminalDisplay::showResizeNotification() { - if (_terminalSizeHint && isVisible()) { - if (_terminalSizeStartup) { - _terminalSizeStartup=false; - return; - } - if (!_resizeWidget) { - _resizeWidget = new QLabel(("Size: XXX x XXX"), this); - _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(("Size: XXX x XXX"))); - _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); - _resizeWidget->setAlignment(Qt::AlignCenter); + if (_terminalSizeHint && isVisible()) + { + if (_terminalSizeStartup) { + _terminalSizeStartup=false; + return; + } + if (!_resizeWidget) + { + _resizeWidget = new QLabel(("Size: XXX x XXX"), this); + _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(("Size: XXX x XXX"))); + _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); + _resizeWidget->setAlignment(Qt::AlignCenter); - _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)"); + _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)"); - _resizeTimer = new QTimer(this); - _resizeTimer->setSingleShot(true); - connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); - - } - QString sizeStr; - sizeStr.sprintf("Size: %d x %d", _columns, _lines); - _resizeWidget->setText(sizeStr); - _resizeWidget->move((width()-_resizeWidget->width())/2, - (height()-_resizeWidget->height())/2+20); - _resizeWidget->show(); - _resizeTimer->start(1000); - } + _resizeTimer = new QTimer(this); + _resizeTimer->setSingleShot(true); + connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); + } + QString sizeStr; + sizeStr.sprintf("Size: %d x %d", _columns, _lines); + _resizeWidget->setText(sizeStr); + _resizeWidget->move((width()-_resizeWidget->width())/2, + (height()-_resizeWidget->height())/2+20); + _resizeWidget->show(); + _resizeTimer->start(1000); + } } void TerminalDisplay::setBlinkingCursor(bool blink) { - _hasBlinkingCursor=blink; + _hasBlinkingCursor=blink; + + if (blink && !_blinkCursorTimer->isActive()) + _blinkCursorTimer->start(QApplication::cursorFlashTime() / 2); + + if (!blink && _blinkCursorTimer->isActive()) + { + _blinkCursorTimer->stop(); + if (_cursorBlinking) + blinkCursorEvent(); + else + _cursorBlinking = false; + } +} - if (blink && !_blinkCursorTimer->isActive()) { - _blinkCursorTimer->start(BLINK_DELAY); - } +void TerminalDisplay::setBlinkingTextEnabled(bool blink) +{ + _allowBlinkingText = blink; - if (!blink && _blinkCursorTimer->isActive()) { - _blinkCursorTimer->stop(); - if (_cursorBlinking) { - blinkCursorEvent(); - } else { - _cursorBlinking = false; - } + if (blink && !_blinkTimer->isActive()) + _blinkTimer->start(TEXT_BLINK_DELAY); + + if (!blink && _blinkTimer->isActive()) + { + _blinkTimer->stop(); + _blinking = false; } } -void TerminalDisplay::paintEvent( QPaintEvent * pe ) +void TerminalDisplay::focusOutEvent(QFocusEvent*) { -//qDebug("%s %d paintEvent", __FILE__, __LINE__); - QPainter paint(this); -//qDebug("%s %d paintEvent %d %d", __FILE__, __LINE__, paint.window().top(), paint.window().right()); + emit termLostFocus(); + // trigger a repaint of the cursor so that it is both visible (in case + // it was hidden during blinking) + // and drawn in a focused out state + _cursorBlinking = false; + updateCursor(); - foreach (QRect rect, (pe->region() & contentsRect()).rects()) { - drawBackground(paint,rect,palette().background().color(), true /* use opacity setting */); - drawContents(paint, rect); + _blinkCursorTimer->stop(); + if (_blinking) + blinkEvent(); + + _blinkTimer->stop(); +} +void TerminalDisplay::focusInEvent(QFocusEvent*) +{ + emit termGetFocus(); + if (_hasBlinkingCursor) + { + _blinkCursorTimer->start(); } -// drawBackground(paint,contentsRect(),palette().background().color(), true /* use opacity setting */); -// drawContents(paint, contentsRect()); - drawInputMethodPreeditString(paint,preeditRect()); - paintFilters(paint); + updateCursor(); - paint.end(); + if (_hasBlinker) + _blinkTimer->start(); +} + +void TerminalDisplay::paintEvent( QPaintEvent* pe ) +{ + QPainter paint(this); + + foreach (const QRect &rect, (pe->region() & contentsRect()).rects()) + { + drawBackground(paint,rect,palette().background().color(), + true /* use opacity setting */); + drawContents(paint, rect); + } + drawInputMethodPreeditString(paint,preeditRect()); + paintFilters(paint); } QPoint TerminalDisplay::cursorPosition() const { - if (_screenWindow) { + if (_screenWindow) return _screenWindow->cursorPosition(); - } else { + else return QPoint(0,0); - } } QRect TerminalDisplay::preeditRect() const { const int preeditLength = string_width(_inputMethodData.preeditString); - if ( preeditLength == 0 ) { + if ( preeditLength == 0 ) return QRect(); - } return QRect(_leftMargin + _fontWidth*cursorPosition().x(), _topMargin + _fontHeight*cursorPosition().y(), _fontWidth*preeditLength, _fontHeight); -} +} -void TerminalDisplay::drawInputMethodPreeditString(QPainter & painter , const QRect & rect) +void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect) { - if ( _inputMethodData.preeditString.isEmpty() ) { + if ( _inputMethodData.preeditString.isEmpty() ) return; - } - const QPoint cursorPos = cursorPosition(); + + const QPoint cursorPos = cursorPosition(); bool invertColors = false; const QColor background = _colorTable[DEFAULT_BACK_COLOR].color; const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color; - const Character * style = &_image[loc(cursorPos.x(),cursorPos.y())]; + const Character* style = &_image[loc(cursorPos.x(),cursorPos.y())]; drawBackground(painter,rect,background,true); drawCursor(painter,rect,foreground,background,invertColors); drawCharacters(painter,rect,_inputMethodData.preeditString,style,invertColors); - _inputMethodData.previousPreeditRect = rect; + _inputMethodData.previousPreeditRect = rect; } -FilterChain * TerminalDisplay::filterChain() const +FilterChain* TerminalDisplay::filterChain() const { return _filterChain; } -void TerminalDisplay::paintFilters(QPainter & painter) +void TerminalDisplay::paintFilters(QPainter& painter) { -//qDebug("%s %d paintFilters", __FILE__, __LINE__); - // get color of character under mouse and use it to draw // lines for filters QPoint cursorPos = mapFromGlobal(QCursor::pos()); int cursorLine; int cursorColumn; + int scrollBarWidth = (_scrollbarLocation == ScrollBarLeft) ? _scrollBar->width() : 0; + getCharacterPosition( cursorPos , cursorLine , cursorColumn ); Character cursorCharacter = _image[loc(cursorColumn,cursorLine)]; painter.setPen( QPen(cursorCharacter.foregroundColor.color(colorTable())) ); - // iterate over hotspots identified by the display's currently active filters + // iterate over hotspots identified by the display's currently active filters // and draw appropriate visuals to indicate the presence of the hotspot - QList spots = _filterChain->hotSpots(); - QListIterator iter(spots); - while (iter.hasNext()) { - Filter::HotSpot * spot = iter.next(); + QList spots = _filterChain->hotSpots(); + QListIterator iter(spots); + while (iter.hasNext()) + { + Filter::HotSpot* spot = iter.next(); - for ( int line = spot->startLine() ; line <= spot->endLine() ; line++ ) { + QRegion region; + if ( spot->type() == Filter::HotSpot::Link ) { + QRect r; + if (spot->startLine()==spot->endLine()) { + r.setCoords( spot->startColumn()*_fontWidth + 1 + scrollBarWidth, + spot->startLine()*_fontHeight + 1, + (spot->endColumn()-1)*_fontWidth - 1 + scrollBarWidth, + (spot->endLine()+1)*_fontHeight - 1 ); + region |= r; + } else { + r.setCoords( spot->startColumn()*_fontWidth + 1 + scrollBarWidth, + spot->startLine()*_fontHeight + 1, + (_columns-1)*_fontWidth - 1 + scrollBarWidth, + (spot->startLine()+1)*_fontHeight - 1 ); + region |= r; + for ( int line = spot->startLine()+1 ; line < spot->endLine() ; line++ ) { + r.setCoords( 0*_fontWidth + 1 + scrollBarWidth, + line*_fontHeight + 1, + (_columns-1)*_fontWidth - 1 + scrollBarWidth, + (line+1)*_fontHeight - 1 ); + region |= r; + } + r.setCoords( 0*_fontWidth + 1 + scrollBarWidth, + spot->endLine()*_fontHeight + 1, + (spot->endColumn()-1)*_fontWidth - 1 + scrollBarWidth, + (spot->endLine()+1)*_fontHeight - 1 ); + region |= r; + } + } + + for ( int line = spot->startLine() ; line <= spot->endLine() ; line++ ) + { int startColumn = 0; - int endColumn = _columns-1; // TODO use number of _columns which are actually - // occupied on this line rather than the width of the - // display in _columns + int endColumn = _columns-1; // TODO use number of _columns which are actually + // occupied on this line rather than the width of the + // display in _columns // ignore whitespace at the end of the lines - while ( QChar(_image[loc(endColumn,line)].character).isSpace() && endColumn > 0 ) { + while ( QChar(_image[loc(endColumn,line)].character).isSpace() && endColumn > 0 ) endColumn--; - } - + // increment here because the column which we want to set 'endColumn' to // is the first whitespace character at the end of the line endColumn++; - if ( line == spot->startLine() ) { + if ( line == spot->startLine() ) startColumn = spot->startColumn(); - } - if ( line == spot->endLine() ) { + if ( line == spot->endLine() ) endColumn = spot->endColumn(); - } // subtract one pixel from // the right and bottom so that @@ -1205,187 +1333,190 @@ void TerminalDisplay::paintFilters(QPainter & painter) // hotspots // // subtracting one pixel from all sides also prevents an edge case where - // moving the mouse outside a link could still leave it underlined + // moving the mouse outside a link could still leave it underlined // because the check below for the position of the cursor // finds it on the border of the target area QRect r; - r.setCoords( startColumn*_fontWidth + 1, line*_fontHeight + 1, - endColumn*_fontWidth - 1, (line+1)*_fontHeight - 1 ); - - // Underline link hotspots - if ( spot->type() == Filter::HotSpot::Link ) { + r.setCoords( startColumn*_fontWidth + 1 + scrollBarWidth, + line*_fontHeight + 1, + endColumn*_fontWidth - 1 + scrollBarWidth, + (line+1)*_fontHeight - 1 ); + // Underline link hotspots + if ( spot->type() == Filter::HotSpot::Link ) + { QFontMetrics metrics(font()); - + // find the baseline (which is the invisible line that the characters in the font sit on, // with some having tails dangling below) int baseline = r.bottom() - metrics.descent(); // find the position of the underline below that int underlinePos = baseline + metrics.underlinePos(); - - if ( r.contains( mapFromGlobal(QCursor::pos()) ) ) - painter.drawLine( r.left() , underlinePos , + if ( region.contains( mapFromGlobal(QCursor::pos()) ) ){ + painter.drawLine( r.left() , underlinePos , r.right() , underlinePos ); + } } // Marker hotspots simply have a transparent rectanglular shape // drawn on top of them - else if ( spot->type() == Filter::HotSpot::Marker ) { - //TODO - Do not use a hardcoded colour for this + else if ( spot->type() == Filter::HotSpot::Marker ) + { + //TODO - Do not use a hardcoded colour for this painter.fillRect(r,QBrush(QColor(255,0,0,120))); } } } } -void TerminalDisplay::drawContents(QPainter & paint, const QRect & rect) +void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) { -//qDebug("%s %d drawContents and rect x=%d y=%d w=%d h=%d", __FILE__, __LINE__, rect.x(), rect.y(),rect.width(),rect.height()); + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); - QPoint tL = contentsRect().topLeft(); -// int tLx = tL.x(); - int tLy = tL.y(); + int lux = qMin(_usedColumns-1, qMax(0,(rect.left() - tLx - _leftMargin ) / _fontWidth)); + int luy = qMin(_usedLines-1, qMax(0,(rect.top() - tLy - _topMargin ) / _fontHeight)); + int rlx = qMin(_usedColumns-1, qMax(0,(rect.right() - tLx - _leftMargin ) / _fontWidth)); + int rly = qMin(_usedLines-1, qMax(0,(rect.bottom() - tLy - _topMargin ) / _fontHeight)); - int tLx = (_contentWidth - _usedColumns * _fontWidth)/2; -// int tLy = (_contentHeight - _usedLines * _fontHeight)/2; -//qDebug("%d %d %d %d", tLx, tLy, _contentWidth, _usedColumns * _fontWidth); + const int bufferSize = _usedColumns; + QString unistr; + unistr.reserve(bufferSize); + for (int y = luy; y <= rly; y++) + { + quint16 c = _image[loc(lux,y)].character; + int x = lux; + if(!c && x) + x--; // Search for start of multi-column character + for (; x <= rlx; x++) + { + int len = 1; + int p = 0; - int lux = qMin(_usedColumns-1, qMax(0,(rect.left() - tLx - _leftMargin ) / _fontWidth)); - int luy = qMin(_usedLines-1, qMax(0, (rect.top() - tLy - _topMargin ) / _fontHeight)); - int rlx = qMin(_usedColumns-1, qMax(0, (rect.right() - tLx - _leftMargin ) / _fontWidth)); - int rly = qMin(_usedLines-1, qMax(0, (rect.bottom() - tLy - _topMargin ) / _fontHeight)); + // reset our buffer to the maximal size + unistr.resize(bufferSize); + QChar *disstrU = unistr.data(); - const int bufferSize = _usedColumns; - QChar * disstrU = new QChar[bufferSize]; - for (int y = luy; y <= rly; y++) { - quint16 c = _image[loc(lux,y)].character; - int x = lux; - if (!c && x) { - x--; // Search for start of multi-column character + // is this a single character or a sequence of characters ? + if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR ) + { + // sequence of characters + ushort extendedCharLength = 0; + ushort* chars = ExtendedCharTable::instance + .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength); + for ( int index = 0 ; index < extendedCharLength ; index++ ) + { + Q_ASSERT( p < bufferSize ); + disstrU[p++] = chars[index]; } - for (; x <= rlx; x++) { - int len = 1; - int p = 0; + } + else + { + // single character + c = _image[loc(x,y)].character; + if (c) + { + Q_ASSERT( p < bufferSize ); + disstrU[p++] = c; //fontMap(c); + } + } - // is this a single character or a sequence of characters ? - if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR ) { - // sequence of characters - ushort extendedCharLength = 0; - ushort * chars = ExtendedCharTable::instance - .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength); - for ( int index = 0 ; index < extendedCharLength ; index++ ) { - Q_ASSERT( p < bufferSize ); - disstrU[p++] = chars[index]; - } - } else { - // single character - c = _image[loc(x,y)].character; - if (c) { - Q_ASSERT( p < bufferSize ); - disstrU[p++] = c; //fontMap(c); - } - } - - bool lineDraw = isLineChar(c); - bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0); - CharacterColor currentForeground = _image[loc(x,y)].foregroundColor; - CharacterColor currentBackground = _image[loc(x,y)].backgroundColor; - quint8 currentRendition = _image[loc(x,y)].rendition; - - while (x+len <= rlx && - _image[loc(x+len,y)].foregroundColor == currentForeground && - _image[loc(x+len,y)].backgroundColor == currentBackground && - _image[loc(x+len,y)].rendition == currentRendition && - (_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth && - isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) { // Assignment! - if (c) { - disstrU[p++] = c; //fontMap(c); - } - if (doubleWidth) { // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition - len++; // Skip trailing part of multi-column character - } - len++; - } - if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character)) { - len++; // Adjust for trailing part of multi-column character - } + bool lineDraw = isLineChar(c); + bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0); + CharacterColor currentForeground = _image[loc(x,y)].foregroundColor; + CharacterColor currentBackground = _image[loc(x,y)].backgroundColor; + quint8 currentRendition = _image[loc(x,y)].rendition; + + while (x+len <= rlx && + _image[loc(x+len,y)].foregroundColor == currentForeground && + _image[loc(x+len,y)].backgroundColor == currentBackground && + _image[loc(x+len,y)].rendition == currentRendition && + (_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth && + isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment! + { + if (c) + disstrU[p++] = c; //fontMap(c); + if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition + len++; // Skip trailing part of multi-column character + len++; + } + if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character)) + len++; // Adjust for trailing part of multi-column character bool save__fixedFont = _fixedFont; - if (lineDraw) { - _fixedFont = false; - } - if (doubleWidth) { - _fixedFont = false; - } - QString unistr(disstrU,p); + if (lineDraw) + _fixedFont = false; + if (doubleWidth) + _fixedFont = false; + unistr.resize(p); - if (y < _lineProperties.size()) { - if (_lineProperties[y] & LINE_DOUBLEWIDTH) { - paint.scale(2,1); - } + // Create a text scaling matrix for double width and double height lines. + QMatrix textScale; - if (_lineProperties[y] & LINE_DOUBLEHEIGHT) { - paint.scale(1,2); - } - } + if (y < _lineProperties.size()) + { + if (_lineProperties[y] & LINE_DOUBLEWIDTH) + textScale.scale(2,1); - //calculate the area in which the text will be drawn - QRect textArea = QRect( _leftMargin+tLx+_fontWidth*x , - _topMargin+tLy+_fontHeight*y , - _fontWidth*len, - _fontHeight); + if (_lineProperties[y] & LINE_DOUBLEHEIGHT) + textScale.scale(1,2); + } - //move the calculated area to take account of scaling applied to the painter. - //the position of the area from the origin (0,0) is scaled - //by the opposite of whatever - //transformation has been applied to the painter. this ensures that - //painting does actually start from textArea.topLeft() - //(instead of textArea.topLeft() * painter-scale) - QMatrix inverted = paint.matrix().inverted(); -// textArea.moveTopLeft( inverted.map(textArea.topLeft()) ); - textArea.moveCenter( inverted.map(textArea.center()) ); + //Apply text scaling matrix. + paint.setWorldMatrix(textScale, true); + //calculate the area in which the text will be drawn + QRect textArea = QRect( _leftMargin+tLx+_fontWidth*x , _topMargin+tLy+_fontHeight*y , _fontWidth*len , _fontHeight); + + //move the calculated area to take account of scaling applied to the painter. + //the position of the area from the origin (0,0) is scaled + //by the opposite of whatever + //transformation has been applied to the painter. this ensures that + //painting does actually start from textArea.topLeft() + //(instead of textArea.topLeft() * painter-scale) + textArea.moveTopLeft( textScale.inverted().map(textArea.topLeft()) ); + + //paint text fragment + drawTextFragment( paint, + textArea, + unistr, + &_image[loc(x,y)] ); //, + //0, + //!_isPrinting ); + + _fixedFont = save__fixedFont; + + //reset back to single-width, single-height _lines + paint.setWorldMatrix(textScale.inverted(), true); - //paint text fragment - drawTextFragment( paint, - textArea, - unistr, - &_image[loc(x,y)] ); //, - //0, - //!_isPrinting ); - - _fixedFont = save__fixedFont; - - //reset back to single-width, single-height _lines - paint.resetMatrix(); - - if (y < _lineProperties.size()-1) { - //double-height _lines are represented by two adjacent _lines - //containing the same characters - //both _lines will have the LINE_DOUBLEHEIGHT attribute. - //If the current line has the LINE_DOUBLEHEIGHT attribute, - //we can therefore skip the next line - if (_lineProperties[y] & LINE_DOUBLEHEIGHT) { - y++; - } - } - - x += len - 1; - } + if (y < _lineProperties.size()-1) + { + //double-height _lines are represented by two adjacent _lines + //containing the same characters + //both _lines will have the LINE_DOUBLEHEIGHT attribute. + //If the current line has the LINE_DOUBLEHEIGHT attribute, + //we can therefore skip the next line + if (_lineProperties[y] & LINE_DOUBLEHEIGHT) + y++; + } + + x += len - 1; } - delete [] disstrU; + } } void TerminalDisplay::blinkEvent() { - _blinking = !_blinking; + if (!_allowBlinkingText) return; - //TODO: Optimise to only repaint the areas of the widget - // where there is blinking text - // rather than repainting the whole widget. - update(); + _blinking = !_blinking; + + //TODO: Optimize to only repaint the areas of the widget + // where there is blinking text + // rather than repainting the whole widget. + update(); } -QRect TerminalDisplay::imageToWidget(const QRect & imageArea) const +QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const { -//qDebug("%s %d imageToWidget", __FILE__, __LINE__); QRect result; result.setLeft( _leftMargin + _fontWidth * imageArea.left() ); result.setTop( _topMargin + _fontHeight * imageArea.top() ); @@ -1395,13 +1526,16 @@ QRect TerminalDisplay::imageToWidget(const QRect & imageArea) const return result; } +void TerminalDisplay::updateCursor() +{ + QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) ); + update(cursorRect); +} + void TerminalDisplay::blinkCursorEvent() { - _cursorBlinking = !_cursorBlinking; - - QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) ); - - update(cursorRect); + _cursorBlinking = !_cursorBlinking; + updateCursor(); } /* ------------------------------------------------------------------------- */ @@ -1410,72 +1544,71 @@ void TerminalDisplay::blinkCursorEvent() /* */ /* ------------------------------------------------------------------------- */ -void TerminalDisplay::resizeEvent(QResizeEvent *) +void TerminalDisplay::resizeEvent(QResizeEvent*) { - updateImageSize(); + updateImageSize(); } void TerminalDisplay::propagateSize() { - if (_isFixedSize) { - setSize(_columns, _lines); - QWidget::setFixedSize(sizeHint()); - parentWidget()->adjustSize(); - parentWidget()->setFixedSize(parentWidget()->sizeHint()); - return; - } - if (_image) { - updateImageSize(); - } + if (_isFixedSize) + { + setSize(_columns, _lines); + QWidget::setFixedSize(sizeHint()); + parentWidget()->adjustSize(); + parentWidget()->setFixedSize(parentWidget()->sizeHint()); + return; + } + if (_image) + updateImageSize(); } void TerminalDisplay::updateImageSize() { -//qDebug("%s %d updateImageSize", __FILE__, __LINE__); - Character * oldimg = _image; - int oldlin = _lines; - int oldcol = _columns; + Character* oldimg = _image; + int oldlin = _lines; + int oldcol = _columns; - makeImage(); + makeImage(); + + // copy the old image to reduce flicker + int lines = qMin(oldlin,_lines); + int columns = qMin(oldcol,_columns); - - // copy the old image to reduce flicker - int lines = qMin(oldlin,_lines); - int columns = qMin(oldcol,_columns); - - if (oldimg) { - for (int line = 0; line < lines; line++) { - memcpy((void *)&_image[_columns*line], - (void *)&oldimg[oldcol*line],columns*sizeof(Character)); - } - delete[] oldimg; + if (oldimg) + { + for (int line = 0; line < lines; line++) + { + memcpy((void*)&_image[_columns*line], + (void*)&oldimg[oldcol*line],columns*sizeof(Character)); } + delete[] oldimg; + } - if (_screenWindow) { - _screenWindow->setWindowLines(_lines); - } + if (_screenWindow) + _screenWindow->setWindowLines(_lines); - _resizing = (oldlin!=_lines) || (oldcol!=_columns); + _resizing = (oldlin!=_lines) || (oldcol!=_columns); - if ( _resizing ) { - showResizeNotification(); - emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent - } - - _resizing = false; + if ( _resizing ) + { + showResizeNotification(); + emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent + } + + _resizing = false; } -//showEvent and hideEvent are reimplemented here so that it appears to other classes that the +//showEvent and hideEvent are reimplemented here so that it appears to other classes that the //display has been resized when the display is hidden or shown. // -//this allows //TODO: Perhaps it would be better to have separate signals for show and hide instead of using -//the same signal as the one for a content size change -void TerminalDisplay::showEvent(QShowEvent *) +//the same signal as the one for a content size change +void TerminalDisplay::showEvent(QShowEvent*) { emit changedContentSizeSignal(_contentHeight,_contentWidth); } -void TerminalDisplay::hideEvent(QHideEvent *) +void TerminalDisplay::hideEvent(QHideEvent*) { emit changedContentSizeSignal(_contentHeight,_contentWidth); } @@ -1488,765 +1621,781 @@ void TerminalDisplay::hideEvent(QHideEvent *) void TerminalDisplay::scrollBarPositionChanged(int) { - if ( !_screenWindow ) { - return; - } + if ( !_screenWindow ) + return; - _screenWindow->scrollTo( _scrollBar->value() ); + _screenWindow->scrollTo( _scrollBar->value() ); - // if the thumb has been moved to the bottom of the _scrollBar then set - // the display to automatically track new output, - // that is, scroll down automatically - // to how new _lines as they are added - const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum()); - _screenWindow->setTrackOutput( atEndOfOutput ); + // if the thumb has been moved to the bottom of the _scrollBar then set + // the display to automatically track new output, + // that is, scroll down automatically + // to how new _lines as they are added + const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum()); + _screenWindow->setTrackOutput( atEndOfOutput ); - updateImage(); + updateImage(); } void TerminalDisplay::setScroll(int cursor, int slines) { -//qDebug("%s %d setScroll", __FILE__, __LINE__); - // update _scrollBar if the range or value has changed, - // otherwise return - // - // setting the range or value of a _scrollBar will always trigger - // a repaint, so it should be avoided if it is not necessary - if ( _scrollBar->minimum() == 0 && - _scrollBar->maximum() == (slines - _lines) && - _scrollBar->value() == cursor ) { + // update _scrollBar if the range or value has changed, + // otherwise return + // + // setting the range or value of a _scrollBar will always trigger + // a repaint, so it should be avoided if it is not necessary + if ( _scrollBar->minimum() == 0 && + _scrollBar->maximum() == (slines - _lines) && + _scrollBar->value() == cursor ) + { return; - } + } - disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); - _scrollBar->setRange(0,slines - _lines); - _scrollBar->setSingleStep(1); - _scrollBar->setPageStep(_lines); - _scrollBar->setValue(cursor); - connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); + disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); + _scrollBar->setRange(0,slines - _lines); + _scrollBar->setSingleStep(1); + _scrollBar->setPageStep(_lines); + _scrollBar->setValue(cursor); + connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); } void TerminalDisplay::setScrollBarPosition(ScrollBarPosition position) { - if (_scrollbarLocation == position) { -// return; - } + if (_scrollbarLocation == position) + return; + + if ( position == NoScrollBar ) + _scrollBar->hide(); + else + _scrollBar->show(); - if ( position == NoScrollBar ) { - _scrollBar->hide(); + _topMargin = _leftMargin = 1; + _scrollbarLocation = position; + + propagateSize(); + update(); +} + +void TerminalDisplay::mousePressEvent(QMouseEvent* ev) +{ + if ( _possibleTripleClick && (ev->button()==Qt::LeftButton) ) { + mouseTripleClickEvent(ev); + return; + } + + if ( !contentsRect().contains(ev->pos()) ) return; + + if ( !_screenWindow ) return; + + int charLine; + int charColumn; + getCharacterPosition(ev->pos(),charLine,charColumn); + QPoint pos = QPoint(charColumn,charLine); + + if ( ev->button() == Qt::LeftButton) + { + _lineSelectionMode = false; + _wordSelectionMode = false; + + emit isBusySelecting(true); // Keep it steady... + // Drag only when the Control key is hold + bool selected = false; + + // The receiver of the testIsSelected() signal will adjust + // 'selected' accordingly. + //emit testIsSelected(pos.x(), pos.y(), selected); + + selected = _screenWindow->isSelected(pos.x(),pos.y()); + + if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected ) { + // The user clicked inside selected text + dragInfo.state = diPending; + dragInfo.start = ev->pos(); + } + else { + // No reason to ever start a drag event + dragInfo.state = diNone; + + _preserveLineBreaks = !( ( ev->modifiers() & Qt::ControlModifier ) && !(ev->modifiers() & Qt::AltModifier) ); + _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier); + + if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) + { + _screenWindow->clearSelection(); + + //emit clearSelectionSignal(); + pos.ry() += _scrollBar->value(); + _iPntSel = _pntSel = pos; + _actSel = 1; // left mouse button pressed but nothing selected yet. + + } + else + { + emit mouseSignal( 0, charColumn + 1, charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); + } + } + } + else if ( ev->button() == Qt::MidButton ) + { + if ( _mouseMarks || (!_mouseMarks && (ev->modifiers() & Qt::ShiftModifier)) ) + emitSelection(true,ev->modifiers() & Qt::ControlModifier); + else + emit mouseSignal( 1, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); + } + else if ( ev->button() == Qt::RightButton ) + { + if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) + emit configureRequest(ev->pos()); + else + emit mouseSignal( 2, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); + } +} + +QList TerminalDisplay::filterActions(const QPoint& position) +{ + int charLine, charColumn; + getCharacterPosition(position,charLine,charColumn); + + Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); + + return spot ? spot->actions() : QList(); +} + +void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev) +{ + int charLine = 0; + int charColumn = 0; + int scrollBarWidth = (_scrollbarLocation == ScrollBarLeft) ? _scrollBar->width() : 0; + + getCharacterPosition(ev->pos(),charLine,charColumn); + + // handle filters + // change link hot-spot appearance on mouse-over + Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); + if ( spot && spot->type() == Filter::HotSpot::Link) + { + QRegion previousHotspotArea = _mouseOverHotspotArea; + _mouseOverHotspotArea = QRegion(); + QRect r; + if (spot->startLine()==spot->endLine()) { + r.setCoords( spot->startColumn()*_fontWidth + scrollBarWidth, + spot->startLine()*_fontHeight, + spot->endColumn()*_fontWidth + scrollBarWidth, + (spot->endLine()+1)*_fontHeight - 1 ); + _mouseOverHotspotArea |= r; } else { - _scrollBar->show(); - } - - _topMargin = _leftMargin = 1; - _scrollbarLocation = position; - - propagateSize(); - update(); -} - -void TerminalDisplay::mousePressEvent(QMouseEvent * ev) -{ - if ( _possibleTripleClick && (ev->button()==Qt::LeftButton) ) { - mouseTripleClickEvent(ev); - return; - } - - if ( !contentsRect().contains(ev->pos()) ) { - return; - } - - if ( !_screenWindow ) { - return; - } - - int charLine; - int charColumn; - getCharacterPosition(ev->pos(),charLine,charColumn); - QPoint pos = QPoint(charColumn,charLine); - - if ( ev->button() == Qt::LeftButton) { - _lineSelectionMode = false; - _wordSelectionMode = false; - - emit isBusySelecting(true); // Keep it steady... - // Drag only when the Control key is hold - bool selected = false; - - // The receiver of the testIsSelected() signal will adjust - // 'selected' accordingly. - //emit testIsSelected(pos.x(), pos.y(), selected); - - selected = _screenWindow->isSelected(pos.x(),pos.y()); - - if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected ) { - // The user clicked inside selected text - dragInfo.state = diPending; - dragInfo.start = ev->pos(); - } else { - // No reason to ever start a drag event - dragInfo.state = diNone; - - _preserveLineBreaks = !( ( ev->modifiers() & Qt::ControlModifier ) && !(ev->modifiers() & Qt::AltModifier) ); - _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier); - - if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) { - _screenWindow->clearSelection(); - - //emit clearSelectionSignal(); - pos.ry() += _scrollBar->value(); - _iPntSel = _pntSel = pos; - _actSel = 1; // left mouse button pressed but nothing selected yet. - - } else { - emit mouseSignal( 0, charColumn + 1, charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); - } - } - } else if ( ev->button() == Qt::MidButton ) { - if ( _mouseMarks || (!_mouseMarks && (ev->modifiers() & Qt::ShiftModifier)) ) { - emitSelection(true,ev->modifiers() & Qt::ControlModifier); - } else { - emit mouseSignal( 1, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); - } - } else if ( ev->button() == Qt::RightButton ) { - if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) { - emit configureRequest( this, - ev->modifiers() & (Qt::ShiftModifier|Qt::ControlModifier), - ev->pos() - ); - } else { - emit mouseSignal( 2, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); + r.setCoords( spot->startColumn()*_fontWidth + scrollBarWidth, + spot->startLine()*_fontHeight, + _columns*_fontWidth - 1 + scrollBarWidth, + (spot->startLine()+1)*_fontHeight ); + _mouseOverHotspotArea |= r; + for ( int line = spot->startLine()+1 ; line < spot->endLine() ; line++ ) { + r.setCoords( 0*_fontWidth + scrollBarWidth, + line*_fontHeight, + _columns*_fontWidth + scrollBarWidth, + (line+1)*_fontHeight ); + _mouseOverHotspotArea |= r; } + r.setCoords( 0*_fontWidth + scrollBarWidth, + spot->endLine()*_fontHeight, + spot->endColumn()*_fontWidth + scrollBarWidth, + (spot->endLine()+1)*_fontHeight ); + _mouseOverHotspotArea |= r; + } + // display tooltips when mousing over links + // TODO: Extend this to work with filter types other than links + const QString& tooltip = spot->tooltip(); + if ( !tooltip.isEmpty() ) + { + QToolTip::showText( mapToGlobal(ev->pos()) , tooltip , this , _mouseOverHotspotArea.boundingRect() ); } -} -QList TerminalDisplay::filterActions(const QPoint & position) -{ - int charLine, charColumn; - getCharacterPosition(position,charLine,charColumn); - - Filter::HotSpot * spot = _filterChain->hotSpotAt(charLine,charColumn); - - return spot ? spot->actions() : QList(); -} - -void TerminalDisplay::mouseMoveEvent(QMouseEvent * ev) -{ - int charLine = 0; - int charColumn = 0; - - getCharacterPosition(ev->pos(),charLine,charColumn); - - // handle filters - // change link hot-spot appearance on mouse-over - Filter::HotSpot * spot = _filterChain->hotSpotAt(charLine,charColumn); - if ( spot && spot->type() == Filter::HotSpot::Link) { - QRect previousHotspotArea = _mouseOverHotspotArea; - _mouseOverHotspotArea.setCoords( qMin(spot->startColumn() , spot->endColumn()) * _fontWidth, - spot->startLine() * _fontHeight, - qMax(spot->startColumn() , spot->endColumn()) * _fontHeight, - (spot->endLine()+1) * _fontHeight ); - - // display tooltips when mousing over links - // TODO: Extend this to work with filter types other than links - const QString & tooltip = spot->tooltip(); - if ( !tooltip.isEmpty() ) { - QToolTip::showText( mapToGlobal(ev->pos()) , tooltip , this , _mouseOverHotspotArea ); - } - - update( _mouseOverHotspotArea | previousHotspotArea ); - } else if ( _mouseOverHotspotArea.isValid() ) { + update( _mouseOverHotspotArea | previousHotspotArea ); + } + else if ( !_mouseOverHotspotArea.isEmpty() ) + { update( _mouseOverHotspotArea ); // set hotspot area to an invalid rectangle - _mouseOverHotspotArea = QRect(); + _mouseOverHotspotArea = QRegion(); + } + + // for auto-hiding the cursor, we need mouseTracking + if (ev->buttons() == Qt::NoButton ) return; + + // if the terminal is interested in mouse movements + // then emit a mouse movement signal, unless the shift + // key is being held down, which overrides this. + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + { + int button = 3; + if (ev->buttons() & Qt::LeftButton) + button = 0; + if (ev->buttons() & Qt::MidButton) + button = 1; + if (ev->buttons() & Qt::RightButton) + button = 2; + + + emit mouseSignal( button, + charColumn + 1, + charLine + 1 +_scrollBar->value() -_scrollBar->maximum(), + 1 ); + + return; + } + + if (dragInfo.state == diPending) + { + // we had a mouse down, but haven't confirmed a drag yet + // if the mouse has moved sufficiently, we will confirm + //! \todo TODO/FIXME: obtain some sane value + int distance = 10; //KGlobalSettings::dndEventDelay(); + if ( ev->x() > dragInfo.start.x() + distance || ev->x() < dragInfo.start.x() - distance || + ev->y() > dragInfo.start.y() + distance || ev->y() < dragInfo.start.y() - distance) + { + // we've left the drag square, we can start a real drag operation now + emit isBusySelecting(false); // Ok.. we can breath again. + + _screenWindow->clearSelection(); + doDrag(); } + return; + } + else if (dragInfo.state == diDragging) + { + // this isn't technically needed because mouseMoveEvent is suppressed during + // Qt drag operations, replaced by dragMoveEvent + return; + } - // for auto-hiding the cursor, we need mouseTracking - if (ev->buttons() == Qt::NoButton ) { - return; - } + if (_actSel == 0) return; - // if the terminal is interested in mouse movements - // then emit a mouse movement signal, unless the shift - // key is being held down, which overrides this. - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) { - int button = 3; - if (ev->buttons() & Qt::LeftButton) { - button = 0; - } - if (ev->buttons() & Qt::MidButton) { - button = 1; - } - if (ev->buttons() & Qt::RightButton) { - button = 2; - } + // don't extend selection while pasting + if (ev->buttons() & Qt::MidButton) return; - - emit mouseSignal( button, - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum(), - 1 ); - - return; - } - - if (dragInfo.state == diPending) { - // we had a mouse down, but haven't confirmed a drag yet - // if the mouse has moved sufficiently, we will confirm - - int distance = 10; //KGlobalSettings::dndEventDelay(); - if ( ev->x() > dragInfo.start.x() + distance || ev->x() < dragInfo.start.x() - distance || - ev->y() > dragInfo.start.y() + distance || ev->y() < dragInfo.start.y() - distance) { - // we've left the drag square, we can start a real drag operation now - emit isBusySelecting(false); // Ok.. we can breath again. - - _screenWindow->clearSelection(); - doDrag(); - } - return; - } else if (dragInfo.state == diDragging) { - // this isn't technically needed because mouseMoveEvent is suppressed during - // Qt drag operations, replaced by dragMoveEvent - return; - } - - if (_actSel == 0) { - return; - } - -// don't extend selection while pasting - if (ev->buttons() & Qt::MidButton) { - return; - } - - extendSelection( ev->pos() ); + extendSelection( ev->pos() ); } -#if 0 -void TerminalDisplay::setSelectionEnd() +void TerminalDisplay::extendSelection( const QPoint& position ) { - extendSelection( _configureRequestPoint ); -} -#endif + QPoint pos = position; -void TerminalDisplay::extendSelection(const QPoint & position) -{ - QPoint pos = position; + if ( !_screenWindow ) + return; - if (!_screenWindow) { - return; + //if ( !contentsRect().contains(ev->pos()) ) return; + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + int scroll = _scrollBar->value(); + + // we're in the process of moving the mouse with the left button pressed + // the mouse cursor will kept caught within the bounds of the text in + // this widget. + + int linesBeyondWidget = 0; + + QRect textBounds(tLx + _leftMargin, + tLy + _topMargin, + _usedColumns*_fontWidth-1, + _usedLines*_fontHeight-1); + + // Adjust position within text area bounds. + QPoint oldpos = pos; + + pos.setX( qBound(textBounds.left(),pos.x(),textBounds.right()) ); + pos.setY( qBound(textBounds.top(),pos.y(),textBounds.bottom()) ); + + if ( oldpos.y() > textBounds.bottom() ) + { + linesBeyondWidget = (oldpos.y()-textBounds.bottom()) / _fontHeight; + _scrollBar->setValue(_scrollBar->value()+linesBeyondWidget+1); // scrollforward + } + if ( oldpos.y() < textBounds.top() ) + { + linesBeyondWidget = (textBounds.top()-oldpos.y()) / _fontHeight; + _scrollBar->setValue(_scrollBar->value()-linesBeyondWidget-1); // history + } + + int charColumn = 0; + int charLine = 0; + getCharacterPosition(pos,charLine,charColumn); + + QPoint here = QPoint(charColumn,charLine); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight); + QPoint ohere; + QPoint _iPntSelCorr = _iPntSel; + _iPntSelCorr.ry() -= _scrollBar->value(); + QPoint _pntSelCorr = _pntSel; + _pntSelCorr.ry() -= _scrollBar->value(); + bool swapping = false; + + if ( _wordSelectionMode ) + { + // Extend to word boundaries + int i; + QChar selClass; + + bool left_not_right = ( here.y() < _iPntSelCorr.y() || + ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) ); + bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || + ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) ); + swapping = left_not_right != old_left_not_right; + + // Find left (left_not_right ? from here : from start) + QPoint left = left_not_right ? here : _iPntSelCorr; + i = loc(left.x(),left.y()); + if (i>=0 && i<=_imageSize) { + selClass = charClass(_image[i].character); + while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) )) + && charClass(_image[i-1].character) == selClass ) + { i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} } } - QPoint tL = contentsRect().topLeft(); - int tLx = tL.x(); - int tLy = tL.y(); - int scroll = _scrollBar->value(); - - // we're in the process of moving the mouse with the left button pressed - // the mouse cursor will kept caught within the bounds of the text in - // this widget. - - // Adjust position within text area bounds. See FIXME above. - QPoint oldpos = pos; - if (pos.x() < tLx + _leftMargin) { - pos.setX(tLx + _leftMargin); - } - if (pos.x() > tLx + _leftMargin + _usedColumns * _fontWidth - 1) { - pos.setX(tLx + _leftMargin + _usedColumns * _fontWidth); - } - if (pos.y() < tLy + _topMargin) { - pos.setY(tLy + _topMargin); - } - if (pos.y() > tLy + _topMargin + _usedLines * _fontHeight - 1) { - pos.setY(tLy + _topMargin + _usedLines * _fontHeight - 1); + // Find left (left_not_right ? from start : from here) + QPoint right = left_not_right ? _iPntSelCorr : here; + i = loc(right.x(),right.y()); + if (i>=0 && i<=_imageSize) { + selClass = charClass(_image[i].character); + while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) )) + && charClass(_image[i+1].character) == selClass ) + { i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } } } - if (pos.y() == tLy + _topMargin + _usedLines * _fontHeight - 1) { - _scrollBar->setValue(_scrollBar->value() + yMouseScroll); // scrollforward + // Pick which is start (ohere) and which is extension (here) + if ( left_not_right ) + { + here = left; ohere = right; } - if (pos.y() == tLy + _topMargin) { - _scrollBar->setValue(_scrollBar->value() - yMouseScroll); // scrollback + else + { + here = right; ohere = left; + } + ohere.rx()++; + } + + if ( _lineSelectionMode ) + { + // Extend to complete line + bool above_not_below = ( here.y() < _iPntSelCorr.y() ); + + QPoint above = above_not_below ? here : _iPntSelCorr; + QPoint below = above_not_below ? _iPntSelCorr : here; + + while (above.y()>0 && (_lineProperties[above.y()-1] & LINE_WRAPPED) ) + above.ry()--; + while (below.y()<_usedLines-1 && (_lineProperties[below.y()] & LINE_WRAPPED) ) + below.ry()++; + + above.setX(0); + below.setX(_usedColumns-1); + + // Pick which is start (ohere) and which is extension (here) + if ( above_not_below ) + { + here = above; ohere = below; + } + else + { + here = below; ohere = above; } - int charColumn = 0; - int charLine = 0; - getCharacterPosition(pos, charLine, charColumn); + QPoint newSelBegin = QPoint( ohere.x(), ohere.y() ); + swapping = !(_tripleSelBegin==newSelBegin); + _tripleSelBegin = newSelBegin; - QPoint here = QPoint(charColumn, charLine); - QPoint ohere(here); - QPoint _iPntSelCorr = _iPntSel; - _iPntSelCorr.ry() -= _scrollBar->value(); - QPoint _pntSelCorr = _pntSel; - _pntSelCorr.ry() -= _scrollBar->value(); - bool swapping = false; + ohere.rx()++; + } - if (_wordSelectionMode) { - // Extend to word boundaries - int i = 0; - int selClass = 0; + int offset = 0; + if ( !_wordSelectionMode && !_lineSelectionMode ) + { + int i; + QChar selClass; - bool left_not_right = (here.y() < _iPntSelCorr.y() || - (here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x())); - bool old_left_not_right = (_pntSelCorr.y() < _iPntSelCorr.y() || - (_pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x())); - swapping = left_not_right != old_left_not_right; + bool left_not_right = ( here.y() < _iPntSelCorr.y() || + ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) ); + bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || + ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) ); + swapping = left_not_right != old_left_not_right; - // Find left (left_not_right ? from here : from start) - QPoint left = left_not_right ? here : _iPntSelCorr; - i = loc(left.x(), left.y()); - if (i >= 0 && i <= _imageSize) { - selClass = charClass(_image[i].character); - while (((left.x() > 0) || (left.y() > 0 && (_lineProperties[left.y() - 1] & LINE_WRAPPED))) - && charClass(_image[i - 1].character) == selClass) { - i--; - if (left.x() > 0) { - left.rx()--; - } else { - left.rx() = _usedColumns - 1; - left.ry()--; - } - } - } + // Find left (left_not_right ? from here : from start) + QPoint left = left_not_right ? here : _iPntSelCorr; - // Find left (left_not_right ? from start : from here) - QPoint right = left_not_right ? _iPntSelCorr : here; - i = loc(right.x(), right.y()); - if (i >= 0 && i <= _imageSize) { - selClass = charClass(_image[i].character); - while (((right.x() < _usedColumns - 1) || (right.y() < _usedLines - 1 && (_lineProperties[right.y()] & LINE_WRAPPED))) - && charClass(_image[i + 1].character) == selClass) { - i++; - if (right.x() < _usedColumns - 1) { - right.rx()++; - } else { - right.rx() = 0; - right.ry()++; - } - } - } - - // Pick which is start (ohere) and which is extension (here) - if (left_not_right) { - here = left; - ohere = right; - } else { - here = right; - ohere = left; - } - ohere.rx()++; + // Find left (left_not_right ? from start : from here) + QPoint right = left_not_right ? _iPntSelCorr : here; + if ( right.x() > 0 && !_columnSelectionMode ) + { + i = loc(right.x(),right.y()); + if (i>=0 && i<=_imageSize) { + selClass = charClass(_image[i-1].character); + /* if (selClass == ' ') + { + while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) && + !(_lineProperties[right.y()] & LINE_WRAPPED)) + { i++; right.rx()++; } + if (right.x() < _usedColumns-1) + right = left_not_right ? _iPntSelCorr : here; + else + right.rx()++; // will be balanced later because of offset=-1; + }*/ + } } - if (_lineSelectionMode) { - // Extend to complete line - bool above_not_below = (here.y() < _iPntSelCorr.y()); + // Pick which is start (ohere) and which is extension (here) + if ( left_not_right ) + { + here = left; ohere = right; offset = 0; + } + else + { + here = right; ohere = left; offset = -1; + } + } - QPoint above = above_not_below ? here : _iPntSelCorr; - QPoint below = above_not_below ? _iPntSelCorr : here; + if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved - while (above.y() > 0 && (_lineProperties[above.y() - 1] & LINE_WRAPPED)) { - above.ry()--; - } - while (below.y() < _usedLines - 1 && (_lineProperties[below.y()] & LINE_WRAPPED)) { - below.ry()++; - } + if (here == ohere) return; // It's not left, it's not right. - above.setX(0); - below.setX(_usedColumns - 1); - - // Pick which is start (ohere) and which is extension (here) - if (above_not_below) { - here = above; - ohere = below; - } else { - here = below; - ohere = above; - } - - QPoint newSelBegin = QPoint(ohere.x(), ohere.y()); - swapping = !(_tripleSelBegin == newSelBegin); - _tripleSelBegin = newSelBegin; - - ohere.rx()++; + if ( _actSel < 2 || swapping ) + { + if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) + { + _screenWindow->setSelectionStart( ohere.x() , ohere.y() , true ); + } + else + { + _screenWindow->setSelectionStart( ohere.x()-1-offset , ohere.y() , false ); } - int offset = 0; - if (!_wordSelectionMode && !_lineSelectionMode) { - int i = 0; - int selClass = 0; + } - bool left_not_right = (here.y() < _iPntSelCorr.y() || - (here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x())); - bool old_left_not_right = (_pntSelCorr.y() < _iPntSelCorr.y() || - (_pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x())); - swapping = left_not_right != old_left_not_right; + _actSel = 2; // within selection + _pntSel = here; + _pntSel.ry() += _scrollBar->value(); - // Find left (left_not_right ? from here : from start) - QPoint left = left_not_right ? here : _iPntSelCorr; + if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) + { + _screenWindow->setSelectionEnd( here.x() , here.y() ); + } + else + { + _screenWindow->setSelectionEnd( here.x()+offset , here.y() ); + } - // Find left (left_not_right ? from start : from here) - QPoint right = left_not_right ? _iPntSelCorr : here; - if (right.x() > 0 && !_columnSelectionMode) { - i = loc(right.x(), right.y()); - if (i >= 0 && i <= _imageSize) { - selClass = charClass(_image[i - 1].character); - if (selClass == ' ') { - while (right.x() < _usedColumns - 1 && charClass(_image[i + 1].character) == selClass && (right.y() < _usedLines - 1) && - !(_lineProperties[right.y()] & LINE_WRAPPED)) { - i++; - right.rx()++; - } - if (right.x() < _usedColumns - 1) { - right = left_not_right ? _iPntSelCorr : here; - } else { - right.rx()++; // will be balanced later because of offset=-1; - } - } - } - } - - // Pick which is start (ohere) and which is extension (here) - if (left_not_right) { - here = left; - ohere = right; - offset = 0; - } else { - here = right; - ohere = left; - offset = -1; - } - } - - if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) { - return; // not moved - } - - if (here == ohere) { - return; // It's not left, it's not right. - } - - if (_actSel < 2 || swapping) { - if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) { - _screenWindow->setSelectionStart(ohere.x(), ohere.y(), true); - } else { - _screenWindow->setSelectionStart(ohere.x() - 1 - offset , ohere.y(), false); - } - - } - - _actSel = 2; // within selection - _pntSel = here; - _pntSel.ry() += _scrollBar->value(); - - if (_columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode) { - _screenWindow->setSelectionEnd(here.x(), here.y()); - } else { - _screenWindow->setSelectionEnd(here.x() + offset, here.y()); - } } -void TerminalDisplay::mouseReleaseEvent(QMouseEvent * ev) +void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev) { - if ( !_screenWindow ) { + if ( !_screenWindow ) return; - } int charLine; int charColumn; getCharacterPosition(ev->pos(),charLine,charColumn); - if ( ev->button() == Qt::LeftButton) { - emit isBusySelecting(false); - if (dragInfo.state == diPending) { - // We had a drag event pending but never confirmed. Kill selection - _screenWindow->clearSelection(); - //emit clearSelectionSignal(); - } else { - if ( _actSel > 1 ) { - setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); - } - - _actSel = 0; - - //FIXME: emits a release event even if the mouse is - // outside the range. The procedure used in `mouseMoveEvent' - // applies here, too. - - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) - emit mouseSignal( 3, // release - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); - } - dragInfo.state = diNone; + if ( ev->button() == Qt::LeftButton) + { + emit isBusySelecting(false); + if(dragInfo.state == diPending) + { + // We had a drag event pending but never confirmed. Kill selection + _screenWindow->clearSelection(); + //emit clearSelectionSignal(); } + else + { + if ( _actSel > 1 ) + { + setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); + } + _actSel = 0; - if ( !_mouseMarks && - ((ev->button() == Qt::RightButton && !(ev->modifiers() & Qt::ShiftModifier)) - || ev->button() == Qt::MidButton) ) { - emit mouseSignal( 3, - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , - 0); + //FIXME: emits a release event even if the mouse is + // outside the range. The procedure used in `mouseMoveEvent' + // applies here, too. + + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + emit mouseSignal( 3, // release + charColumn + 1, + charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); } + dragInfo.state = diNone; + } + + + if ( !_mouseMarks && + ((ev->button() == Qt::RightButton && !(ev->modifiers() & Qt::ShiftModifier)) + || ev->button() == Qt::MidButton) ) + { + emit mouseSignal( 3, + charColumn + 1, + charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , + 0); + } } -void TerminalDisplay::getCharacterPosition(const QPoint & widgetPoint,int & line,int & column) const +void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const { - column = (widgetPoint.x() + _fontWidth/2 -contentsRect().left()-_leftMargin) / _fontWidth; line = (widgetPoint.y()-contentsRect().top()-_topMargin) / _fontHeight; - if ( line < 0 ) { + if ( line < 0 ) line = 0; - } - if ( column < 0 ) { + if ( column < 0 ) column = 0; - } - if ( line >= _usedLines ) { + if ( line >= _usedLines ) line = _usedLines-1; - } // the column value returned can be equal to _usedColumns, which // is the position just after the last character displayed in a line. // // this is required so that the user can select characters in the right-most // column (or left-most for right-to-left input) - if ( column > _usedColumns ) { + if ( column > _usedColumns ) column = _usedColumns; - } } void TerminalDisplay::updateLineProperties() { - if ( !_screenWindow ) { + if ( !_screenWindow ) return; - } - _lineProperties = _screenWindow->getLineProperties(); + _lineProperties = _screenWindow->getLineProperties(); } -void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent * ev) +void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev) { - if ( ev->button() != Qt::LeftButton) { - return; - } - if ( !_screenWindow ) { - return; - } + if ( ev->button() != Qt::LeftButton) return; + if ( !_screenWindow ) return; - int charLine = 0; - int charColumn = 0; + int charLine = 0; + int charColumn = 0; - getCharacterPosition(ev->pos(),charLine,charColumn); + getCharacterPosition(ev->pos(),charLine,charColumn); - QPoint pos(charColumn,charLine); + QPoint pos(charColumn,charLine); - // pass on double click as two clicks. - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) { - // Send just _ONE_ click event, since the first click of the double click - // was already sent by the click handler - emit mouseSignal( 0, - pos.x()+1, - pos.y()+1 +_scrollBar->value() -_scrollBar->maximum(), - 0 ); // left button - return; - } + // pass on double click as two clicks. + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + { + // Send just _ONE_ click event, since the first click of the double click + // was already sent by the click handler + emit mouseSignal( 0, + pos.x()+1, + pos.y()+1 +_scrollBar->value() -_scrollBar->maximum(), + 0 ); // left button + return; + } - _screenWindow->clearSelection(); - QPoint bgnSel = pos; - QPoint endSel = pos; - int i = loc(bgnSel.x(),bgnSel.y()); - _iPntSel = bgnSel; - _iPntSel.ry() += _scrollBar->value(); + _screenWindow->clearSelection(); + QPoint bgnSel = pos; + QPoint endSel = pos; + int i = loc(bgnSel.x(),bgnSel.y()); + _iPntSel = bgnSel; + _iPntSel.ry() += _scrollBar->value(); - _wordSelectionMode = true; + _wordSelectionMode = true; - // find word boundaries... - int selClass = charClass(_image[i].character); - { - // find the start of the word - int x = bgnSel.x(); - while ( ((x>0) || (bgnSel.y()>0 && (_lineProperties[bgnSel.y()-1] & LINE_WRAPPED) )) - && charClass(_image[i-1].character) == selClass ) { - i--; - if (x>0) { - x--; - } else { - x=_usedColumns-1; - bgnSel.ry()--; - } - } + // find word boundaries... + QChar selClass = charClass(_image[i].character); + { + // find the start of the word + int x = bgnSel.x(); + while ( ((x>0) || (bgnSel.y()>0 && (_lineProperties[bgnSel.y()-1] & LINE_WRAPPED) )) + && charClass(_image[i-1].character) == selClass ) + { + i--; + if (x>0) + x--; + else + { + x=_usedColumns-1; + bgnSel.ry()--; + } + } - bgnSel.setX(x); - _screenWindow->setSelectionStart( bgnSel.x() , bgnSel.y() , false ); + bgnSel.setX(x); + _screenWindow->setSelectionStart( bgnSel.x() , bgnSel.y() , false ); - // find the end of the word - i = loc( endSel.x(), endSel.y() ); - x = endSel.x(); - while ( ((x<_usedColumns-1) || (endSel.y()<_usedLines-1 && (_lineProperties[endSel.y()] & LINE_WRAPPED) )) - && charClass(_image[i+1].character) == selClass ) { - i++; - if (x<_usedColumns-1) { - x++; - } else { - x=0; - endSel.ry()++; - } - } + // find the end of the word + i = loc( endSel.x(), endSel.y() ); + x = endSel.x(); + while( ((x<_usedColumns-1) || (endSel.y()<_usedLines-1 && (_lineProperties[endSel.y()] & LINE_WRAPPED) )) + && charClass(_image[i+1].character) == selClass ) + { + i++; + if (x<_usedColumns-1) + x++; + else + { + x=0; + endSel.ry()++; + } + } - endSel.setX(x); + endSel.setX(x); - // In word selection mode don't select @ (64) if at end of word. - if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) ) { - endSel.setX( x - 1 ); - } + // In word selection mode don't select @ (64) if at end of word. + if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) ) + endSel.setX( x - 1 ); - _actSel = 2; // within selection + _actSel = 2; // within selection + + _screenWindow->setSelectionEnd( endSel.x() , endSel.y() ); + + setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); + } - _screenWindow->setSelectionEnd( endSel.x() , endSel.y() ); + _possibleTripleClick=true; - setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); - } - - _possibleTripleClick=true; - - QTimer::singleShot(QApplication::doubleClickInterval(),this, - SLOT(tripleClickTimeout())); + QTimer::singleShot(QApplication::doubleClickInterval(),this, + SLOT(tripleClickTimeout())); } -void TerminalDisplay::wheelEvent( QWheelEvent * ev ) +void TerminalDisplay::wheelEvent( QWheelEvent* ev ) { - if (ev->orientation() != Qt::Vertical) { - return; - } + if (ev->orientation() != Qt::Vertical) + return; - if ( _mouseMarks ) { + // if the terminal program is not interested mouse events + // then send the event to the scrollbar if the slider has room to move + // or otherwise send simulated up / down key presses to the terminal program + // for the benefit of programs such as 'less' + if ( _mouseMarks ) + { + bool canScroll = _scrollBar->maximum() > 0; + if (canScroll) _scrollBar->event(ev); - } else { - int charLine; - int charColumn; - getCharacterPosition( ev->pos() , charLine , charColumn ); + else + { + // assume that each Up / Down key event will cause the terminal application + // to scroll by one line. + // + // to get a reasonable scrolling speed, scroll by one line for every 5 degrees + // of mouse wheel rotation. Mouse wheels typically move in steps of 15 degrees, + // giving a scroll of 3 lines + int key = ev->delta() > 0 ? Qt::Key_Up : Qt::Key_Down; - emit mouseSignal( ev->delta() > 0 ? 4 : 5, - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , - 0); + // QWheelEvent::delta() gives rotation in eighths of a degree + int wheelDegrees = ev->delta() / 8; + int linesToScroll = abs(wheelDegrees) / 5; + + QKeyEvent keyScrollEvent(QEvent::KeyPress,key,Qt::NoModifier); + + for (int i=0;ipos() , charLine , charColumn ); + + emit mouseSignal( ev->delta() > 0 ? 4 : 5, + charColumn + 1, + charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , + 0); + } } void TerminalDisplay::tripleClickTimeout() { - _possibleTripleClick=false; + _possibleTripleClick=false; } -void TerminalDisplay::mouseTripleClickEvent(QMouseEvent * ev) +void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev) { - if ( !_screenWindow ) { - return; + if ( !_screenWindow ) return; + + int charLine; + int charColumn; + getCharacterPosition(ev->pos(),charLine,charColumn); + _iPntSel = QPoint(charColumn,charLine); + + _screenWindow->clearSelection(); + + _lineSelectionMode = true; + _wordSelectionMode = false; + + _actSel = 2; // within selection + emit isBusySelecting(true); // Keep it steady... + + while (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) + _iPntSel.ry()--; + + if (_tripleClickMode == SelectForwardsFromCursor) { + // find word boundary start + int i = loc(_iPntSel.x(),_iPntSel.y()); + QChar selClass = charClass(_image[i].character); + int x = _iPntSel.x(); + + while ( ((x>0) || + (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) + ) + && charClass(_image[i-1].character) == selClass ) + { + i--; + if (x>0) + x--; + else + { + x=_columns-1; + _iPntSel.ry()--; + } } - int charLine; - int charColumn; - getCharacterPosition(ev->pos(),charLine,charColumn); - _iPntSel = QPoint(charColumn,charLine); + _screenWindow->setSelectionStart( x , _iPntSel.y() , false ); + _tripleSelBegin = QPoint( x, _iPntSel.y() ); + } + else if (_tripleClickMode == SelectWholeLine) { + _screenWindow->setSelectionStart( 0 , _iPntSel.y() , false ); + _tripleSelBegin = QPoint( 0, _iPntSel.y() ); + } - _screenWindow->clearSelection(); + while (_iPntSel.y()<_lines-1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED) ) + _iPntSel.ry()++; + + _screenWindow->setSelectionEnd( _columns - 1 , _iPntSel.y() ); - _lineSelectionMode = true; - _wordSelectionMode = false; + setSelection(_screenWindow->selectedText(_preserveLineBreaks)); - _actSel = 2; // within selection - emit isBusySelecting(true); // Keep it steady... - - while (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) { - _iPntSel.ry()--; - } - - if (_tripleClickMode == SelectForwardsFromCursor) { - // find word boundary start - int i = loc(_iPntSel.x(),_iPntSel.y()); - int selClass = charClass(_image[i].character); - int x = _iPntSel.x(); - - while ( ((x>0) || - (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) - ) - && charClass(_image[i-1].character) == selClass ) { - i--; - if (x>0) { - x--; - } else { - x=_columns-1; - _iPntSel.ry()--; - } - } - - _screenWindow->setSelectionStart( x , _iPntSel.y() , false ); - _tripleSelBegin = QPoint( x, _iPntSel.y() ); - } else if (_tripleClickMode == SelectWholeLine) { - _screenWindow->setSelectionStart( 0 , _iPntSel.y() , false ); - _tripleSelBegin = QPoint( 0, _iPntSel.y() ); - } - - while (_iPntSel.y()<_lines-1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED) ) { - _iPntSel.ry()++; - } - - _screenWindow->setSelectionEnd( _columns - 1 , _iPntSel.y() ); - - setSelection(_screenWindow->selectedText(_preserveLineBreaks)); - - _iPntSel.ry() += _scrollBar->value(); + _iPntSel.ry() += _scrollBar->value(); } bool TerminalDisplay::focusNextPrevChild( bool next ) { - if (next) { - return false; // This disables changing the active part in konqueror - } - // when pressing Tab - return QWidget::focusNextPrevChild( next ); + if (next) + return false; // This disables changing the active part in konqueror + // when pressing Tab + return QWidget::focusNextPrevChild( next ); } -void TerminalDisplay::focusInEvent(QFocusEvent * event) + +QChar TerminalDisplay::charClass(QChar qch) const { - emit termGetFocus(); - QWidget::focusInEvent(event); + if ( qch.isSpace() ) return ' '; + + if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) ) + return 'a'; + + return qch; } -void TerminalDisplay::focusOutEvent(QFocusEvent * event) -{ - emit termLostFocus(); - QWidget::focusOutEvent(event); -} - -int TerminalDisplay::charClass(quint16 ch) const -{ - QChar qch=QChar(ch); - if ( qch.isSpace() ) { - return ' '; - } - - if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) ) { - return 'a'; - } - - // Everything else is weird - return 1; -} - -void TerminalDisplay::setWordCharacters(const QString & wc) +void TerminalDisplay::setWordCharacters(const QString& wc) { _wordCharacters = wc; } void TerminalDisplay::setUsesMouse(bool on) { - _mouseMarks = on; - setCursor( _mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor ); + _mouseMarks = on; + setCursor( _mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor ); } bool TerminalDisplay::usesMouse() const { @@ -2263,48 +2412,47 @@ bool TerminalDisplay::usesMouse() const void TerminalDisplay::emitSelection(bool useXselection,bool appendReturn) { - if ( !_screenWindow ) { - return; - } + if ( !_screenWindow ) + return; - // Paste Clipboard by simulating keypress events - QString text = QApplication::clipboard()->text(useXselection ? QClipboard::Selection : - QClipboard::Clipboard); - if (appendReturn) { - text.append("\r"); - } - if ( ! text.isEmpty() ) { - text.replace("\n", "\r"); - QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text); - emit keyPressedSignal(&e); // expose as a big fat keypress event - - _screenWindow->clearSelection(); - } + // Paste Clipboard by simulating keypress events + QString text = QApplication::clipboard()->text(useXselection ? QClipboard::Selection : + QClipboard::Clipboard); + if(appendReturn) + text.append("\r"); + if ( ! text.isEmpty() ) + { + text.replace('\n', '\r'); + QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text); + emit keyPressedSignal(&e); // expose as a big fat keypress event + + _screenWindow->clearSelection(); + } } -void TerminalDisplay::setSelection(const QString & t) +void TerminalDisplay::setSelection(const QString& t) { - QApplication::clipboard()->setText(t, QClipboard::Selection); + QApplication::clipboard()->setText(t, QClipboard::Selection); } void TerminalDisplay::copyClipboard() { - if ( !_screenWindow ) { - return; - } + if ( !_screenWindow ) + return; - QString text = _screenWindow->selectedText(_preserveLineBreaks); + QString text = _screenWindow->selectedText(_preserveLineBreaks); + if (!text.isEmpty()) QApplication::clipboard()->setText(text); } void TerminalDisplay::pasteClipboard() { - emitSelection(false,false); + emitSelection(false,false); } void TerminalDisplay::pasteSelection() { - emitSelection(true,false); + emitSelection(true,false); } /* ------------------------------------------------------------------------- */ @@ -2316,55 +2464,45 @@ void TerminalDisplay::pasteSelection() void TerminalDisplay::setFlowControlWarningEnabled( bool enable ) { _flowControlWarningEnabled = enable; - - // if the dialog is currently visible and the flow control warning has + + // if the dialog is currently visible and the flow control warning has // been disabled then hide the dialog - if (!enable) { + if (!enable) outputSuspended(false); - } } -void TerminalDisplay::keyPressEvent( QKeyEvent * event ) +void TerminalDisplay::keyPressEvent( QKeyEvent* event ) { -//qDebug("%s %d keyPressEvent and key is %d", __FILE__, __LINE__, event->key()); - bool emitKeyPressSignal = true; - // XonXoff flow control - if (event->modifiers() & Qt::ControlModifier && _flowControlWarningEnabled) { - if ( event->key() == Qt::Key_S ) { - //qDebug("%s %d keyPressEvent, output suspended", __FILE__, __LINE__); - emit flowControlKeyPressed(true /*output suspended*/); - } else if ( event->key() == Qt::Key_Q ) { - //qDebug("%s %d keyPressEvent, output enabled", __FILE__, __LINE__); - emit flowControlKeyPressed(false /*output enabled*/); - } - } - // Keyboard-based navigation - if ( event->modifiers() == Qt::ShiftModifier ) { + if ( event->modifiers() == Qt::ShiftModifier ) + { bool update = true; - if ( event->key() == Qt::Key_PageUp ) { - //qDebug("%s %d pageup", __FILE__, __LINE__); + if ( event->key() == Qt::Key_PageUp ) + { _screenWindow->scrollBy( ScreenWindow::ScrollPages , -1 ); - } else if ( event->key() == Qt::Key_PageDown ) { - //qDebug("%s %d pagedown", __FILE__, __LINE__); - _screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 ); - } else if ( event->key() == Qt::Key_Up ) { - //qDebug("%s %d keyup", __FILE__, __LINE__); - _screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 ); - } else if ( event->key() == Qt::Key_Down ) { - //qDebug("%s %d keydown", __FILE__, __LINE__); - _screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 ); - } else { - update = false; } + else if ( event->key() == Qt::Key_PageDown ) + { + _screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 ); + } + else if ( event->key() == Qt::Key_Up ) + { + _screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 ); + } + else if ( event->key() == Qt::Key_Down ) + { + _screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 ); + } + else + update = false; - if ( update ) { - //qDebug("%s %d updating", __FILE__, __LINE__); + if ( update ) + { _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() ); - + updateLineProperties(); updateImage(); @@ -2373,109 +2511,141 @@ void TerminalDisplay::keyPressEvent( QKeyEvent * event ) } } - _screenWindow->setTrackOutput( true ); - _actSel=0; // Key stroke implies a screen update, so TerminalDisplay won't - // know where the current selection is. + // know where the current selection is. - if (_hasBlinkingCursor) { - _blinkCursorTimer->start(BLINK_DELAY); - if (_cursorBlinking) { - blinkCursorEvent(); - } else { - _cursorBlinking = false; - } + if (_hasBlinkingCursor) + { + _blinkCursorTimer->start(QApplication::cursorFlashTime() / 2); + if (_cursorBlinking) + blinkCursorEvent(); + else + _cursorBlinking = false; } - if ( emitKeyPressSignal ) { + if ( emitKeyPressSignal ) emit keyPressedSignal(event); - } event->accept(); } -void TerminalDisplay::inputMethodEvent( QInputMethodEvent * event ) +void TerminalDisplay::inputMethodEvent( QInputMethodEvent* event ) { QKeyEvent keyEvent(QEvent::KeyPress,0,Qt::NoModifier,event->commitString()); emit keyPressedSignal(&keyEvent); _inputMethodData.preeditString = event->preeditString(); update(preeditRect() | _inputMethodData.previousPreeditRect); - + event->accept(); } QVariant TerminalDisplay::inputMethodQuery( Qt::InputMethodQuery query ) const { const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0,0); - switch ( query ) { - case Qt::ImMicroFocus: - return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1)); - break; - case Qt::ImFont: - return font(); - break; - case Qt::ImCursorPosition: - // return the cursor position within the current line - return cursorPos.x(); - break; - case Qt::ImSurroundingText: { - // return the text from the current line - QString lineText; - QTextStream stream(&lineText); - PlainTextDecoder decoder; - decoder.begin(&stream); - decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]); - decoder.end(); - return lineText; - } - break; - case Qt::ImCurrentSelection: - return QString(); - break; + switch ( query ) + { + case Qt::ImMicroFocus: + return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1)); + break; + case Qt::ImFont: + return font(); + break; + case Qt::ImCursorPosition: + // return the cursor position within the current line + return cursorPos.x(); + break; + case Qt::ImSurroundingText: + { + // return the text from the current line + QString lineText; + QTextStream stream(&lineText); + PlainTextDecoder decoder; + decoder.begin(&stream); + decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]); + decoder.end(); + return lineText; + } + break; + case Qt::ImCurrentSelection: + return QString(); + break; + default: + break; } return QVariant(); } -bool TerminalDisplay::event( QEvent * e ) +bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent* keyEvent) { - if ( e->type() == QEvent::ShortcutOverride ) { - QKeyEvent * keyEvent = static_cast( e ); + int modifiers = keyEvent->modifiers(); - // a check to see if keyEvent->text() is empty is used - // to avoid intercepting the press of the modifier key on its own. - // - // this is important as it allows a press and release of the Alt key - // on its own to focus the menu bar, making it possible to - // work with the menu without using the mouse - if ( (keyEvent->modifiers() == Qt::AltModifier) && - !keyEvent->text().isEmpty() ) { - keyEvent->accept(); - return true; + // When a possible shortcut combination is pressed, + // emit the overrideShortcutCheck() signal to allow the host + // to decide whether the terminal should override it or not. + if (modifiers != Qt::NoModifier) + { + int modifierCount = 0; + unsigned int currentModifier = Qt::ShiftModifier; + + while (currentModifier <= Qt::KeypadModifier) + { + if (modifiers & currentModifier) + modifierCount++; + currentModifier <<= 1; } - - // Override any of the following shortcuts because - // they are needed by the terminal - int keyCode = keyEvent->key() | keyEvent->modifiers(); - switch ( keyCode ) { - // list is taken from the QLineEdit::event() code - case Qt::Key_Tab: - case Qt::Key_Delete: - case Qt::Key_Home: - case Qt::Key_End: - case Qt::Key_Backspace: - case Qt::Key_Left: - case Qt::Key_Right: - keyEvent->accept(); - return true; + if (modifierCount < 2) + { + bool override = false; + emit overrideShortcutCheck(keyEvent,override); + if (override) + { + keyEvent->accept(); + return true; + } } } - return QWidget::event( e ); + + // Override any of the following shortcuts because + // they are needed by the terminal + int keyCode = keyEvent->key() | modifiers; + switch ( keyCode ) + { + // list is taken from the QLineEdit::event() code + case Qt::Key_Tab: + case Qt::Key_Delete: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Backspace: + case Qt::Key_Left: + case Qt::Key_Right: + keyEvent->accept(); + return true; + } + return false; +} + +bool TerminalDisplay::event(QEvent* event) +{ + bool eventHandled = false; + switch (event->type()) + { + case QEvent::ShortcutOverride: + eventHandled = handleShortcutOverrideEvent((QKeyEvent*)event); + break; + case QEvent::PaletteChange: + case QEvent::ApplicationPaletteChange: + _scrollBar->setPalette( QApplication::palette() ); + break; + default: + break; + } + return eventHandled ? true : QWidget::event(event); } void TerminalDisplay::setBellMode(int mode) { - _bellMode=mode; + _bellMode=mode; } void TerminalDisplay::enableBell() @@ -2483,28 +2653,34 @@ void TerminalDisplay::enableBell() _allowBell = true; } -void TerminalDisplay::bell(const QString &) +void TerminalDisplay::bell(const QString& message) { - if (_bellMode==NoBell) { - return; - } + if (_bellMode==NoBell) return; - //limit the rate at which bells can occur - //...mainly for sound effects where rapid bells in sequence - //produce a horrible noise - if ( _allowBell ) { - _allowBell = false; - QTimer::singleShot(500,this,SLOT(enableBell())); - - if (_bellMode==SystemBeepBell) { -// KNotification::beep(); - } else if (_bellMode==NotifyBell) { -// KNotification::event("BellVisible", message,QPixmap(),this); - } else if (_bellMode==VisualBell) { - swapColorTable(); - QTimer::singleShot(200,this,SLOT(swapColorTable())); - } + //limit the rate at which bells can occur + //...mainly for sound effects where rapid bells in sequence + //produce a horrible noise + if ( _allowBell ) + { + _allowBell = false; + QTimer::singleShot(500,this,SLOT(enableBell())); + + if (_bellMode==SystemBeepBell) + { + //! \todo TODO/FIXME: some notifications for Qt4? + //KNotification::beep(); + } + else if (_bellMode==NotifyBell) + { + //! \todo TODO/FIXME: some notifications for Qt4? + //KNotification::event("BellVisible", message,QPixmap(),this); + } + else if (_bellMode==VisualBell) + { + swapColorTable(); + QTimer::singleShot(200,this,SLOT(swapColorTable())); } + } } void TerminalDisplay::selectionChanged() @@ -2514,118 +2690,120 @@ void TerminalDisplay::selectionChanged() void TerminalDisplay::swapColorTable() { - ColorEntry color = _colorTable[1]; - _colorTable[1]=_colorTable[0]; - _colorTable[0]= color; - _colorsInverted = !_colorsInverted; - update(); + ColorEntry color = _colorTable[1]; + _colorTable[1]=_colorTable[0]; + _colorTable[0]= color; + _colorsInverted = !_colorsInverted; + update(); } void TerminalDisplay::clearImage() { - // We initialize _image[_imageSize] too. See makeImage() - for (int i = 0; i <= _imageSize; i++) { - _image[i].character = ' '; - _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT, - DEFAULT_FORE_COLOR); - _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT, - DEFAULT_BACK_COLOR); - _image[i].rendition = DEFAULT_RENDITION; - } + // We initialize _image[_imageSize] too. See makeImage() + for (int i = 0; i <= _imageSize; i++) + { + _image[i].character = ' '; + _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT, + DEFAULT_FORE_COLOR); + _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT, + DEFAULT_BACK_COLOR); + _image[i].rendition = DEFAULT_RENDITION; + } } void TerminalDisplay::calcGeometry() { - _scrollBar->resize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent), - contentsRect().height()); - switch (_scrollbarLocation) { + _scrollBar->resize(_scrollBar->sizeHint().width(), contentsRect().height()); + switch(_scrollbarLocation) + { case NoScrollBar : - _leftMargin = DEFAULT_LEFT_MARGIN; - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN; - break; + _leftMargin = DEFAULT_LEFT_MARGIN; + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN; + break; case ScrollBarLeft : - _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width(); - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); - _scrollBar->move(contentsRect().topLeft()); - break; + _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width(); + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); + _scrollBar->move(contentsRect().topLeft()); + break; case ScrollBarRight: - _leftMargin = DEFAULT_LEFT_MARGIN; - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); - _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width()-1,0)); - break; - } + _leftMargin = DEFAULT_LEFT_MARGIN; + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); + _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width()-1,0)); + break; + } - _topMargin = DEFAULT_TOP_MARGIN; - _contentHeight = contentsRect().height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1; - - if (!_isFixedSize) { - // ensure that display is always at least one column wide - _columns = qMax(1,_contentWidth / _fontWidth); - _usedColumns = qMin(_usedColumns,_columns); - - // ensure that display is always at least one line high - _lines = qMax(1,_contentHeight / _fontHeight); - _usedLines = qMin(_usedLines,_lines); - } + _topMargin = DEFAULT_TOP_MARGIN; + _contentHeight = contentsRect().height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1; + + if (!_isFixedSize) + { + // ensure that display is always at least one column wide + _columns = qMax(1,_contentWidth / _fontWidth); + _usedColumns = qMin(_usedColumns,_columns); + + // ensure that display is always at least one line high + _lines = qMax(1,_contentHeight / _fontHeight); + _usedLines = qMin(_usedLines,_lines); + } } void TerminalDisplay::makeImage() { -//qDebug("%s %d makeImage", __FILE__, __LINE__); - calcGeometry(); + calcGeometry(); - // confirm that array will be of non-zero size, since the painting code - // assumes a non-zero array length - Q_ASSERT( _lines > 0 && _columns > 0 ); - Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns ); + // confirm that array will be of non-zero size, since the painting code + // assumes a non-zero array length + Q_ASSERT( _lines > 0 && _columns > 0 ); + Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns ); - _imageSize=_lines*_columns; + _imageSize=_lines*_columns; + + // We over-commit one character so that we can be more relaxed in dealing with + // certain boundary conditions: _image[_imageSize] is a valid but unused position + _image = new Character[_imageSize+1]; - // We over-commit one character so that we can be more relaxed in dealing with - // certain boundary conditions: _image[_imageSize] is a valid but unused position - _image = new Character[_imageSize+1]; - - clearImage(); + clearImage(); } -// calculate the needed size +// calculate the needed size, this must be synced with calcGeometry() void TerminalDisplay::setSize(int columns, int lines) { - //FIXME - Not quite correct, a small amount of additional space - // will be used for margins, the scrollbar etc. - // we need to allow for this so that '_size' does allow - // enough room for the specified number of columns and lines to fit + int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->sizeHint().width(); + int horizontalMargin = 2 * DEFAULT_LEFT_MARGIN; + int verticalMargin = 2 * DEFAULT_TOP_MARGIN; - QSize newSize = QSize( columns * _fontWidth , - lines * _fontHeight ); + QSize newSize = QSize( horizontalMargin + scrollBarWidth + (columns * _fontWidth) , + verticalMargin + (lines * _fontHeight) ); - if ( newSize != size() ) { - _size = newSize; - updateGeometry(); - } + if ( newSize != size() ) + { + _size = newSize; + updateGeometry(); + } } void TerminalDisplay::setFixedSize(int cols, int lins) { - _isFixedSize = true; + _isFixedSize = true; + + //ensure that display is at least one line by one column in size + _columns = qMax(1,cols); + _lines = qMax(1,lins); + _usedColumns = qMin(_usedColumns,_columns); + _usedLines = qMin(_usedLines,_lines); - //ensure that display is at least one line by one column in size - _columns = qMax(1,cols); - _lines = qMax(1,lins); - _usedColumns = qMin(_usedColumns,_columns); - _usedLines = qMin(_usedLines,_lines); - - if (_image) { - delete[] _image; - makeImage(); - } - setSize(cols, lins); - QWidget::setFixedSize(_size); + if (_image) + { + delete[] _image; + makeImage(); + } + setSize(cols, lins); + QWidget::setFixedSize(_size); } QSize TerminalDisplay::sizeHint() const { - return _size; + return _size; } @@ -2635,98 +2813,97 @@ QSize TerminalDisplay::sizeHint() const /* */ /* --------------------------------------------------------------------- */ -void TerminalDisplay::dragEnterEvent(QDragEnterEvent * event) +void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event) { - if (event->mimeData()->hasFormat("text/plain")) { - event->acceptProposedAction(); - } + if (event->mimeData()->hasFormat("text/plain")) + event->acceptProposedAction(); } -void TerminalDisplay::dropEvent(QDropEvent * event) +void TerminalDisplay::dropEvent(QDropEvent* event) { -// KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); + //KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); - QString dropText; - /* if (!urls.isEmpty()) - { - for ( int i = 0 ; i < urls.count() ; i++ ) - { - KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 ); - QString urlText; + QString dropText; +/* + if (!urls.isEmpty()) + { + for ( int i = 0 ; i < urls.count() ; i++ ) + { + KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 ); + QString urlText; - if (url.isLocalFile()) - urlText = url.path(); - else - urlText = url.url(); + if (url.isLocalFile()) + urlText = url.path(); + else + urlText = url.url(); + + // in future it may be useful to be able to insert file names with drag-and-drop + // without quoting them (this only affects paths with spaces in) + urlText = KShell::quoteArg(urlText); + + dropText += urlText; - // in future it may be useful to be able to insert file names with drag-and-drop - // without quoting them (this only affects paths with spaces in) - urlText = KShell::quoteArg(urlText); - - dropText += urlText; - - if ( i != urls.count()-1 ) - dropText += ' '; - } - } - else - { - dropText = event->mimeData()->text(); - } - */ - if (event->mimeData()->hasFormat("text/plain")) { - emit sendStringToEmu(dropText.toLocal8Bit()); + if ( i != urls.count()-1 ) + dropText += ' '; } + } + else + { + */ + dropText = event->mimeData()->text(); + //} + + if(event->mimeData()->hasFormat("text/plain")) + { + emit sendStringToEmu(dropText.toLocal8Bit()); + } } void TerminalDisplay::doDrag() { - dragInfo.state = diDragging; - dragInfo.dragObject = new QDrag(this); - QMimeData * mimeData = new QMimeData; - mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection)); - dragInfo.dragObject->setMimeData(mimeData); - dragInfo.dragObject->start(Qt::CopyAction); - // Don't delete the QTextDrag object. Qt will delete it when it's done with it. + dragInfo.state = diDragging; + dragInfo.dragObject = new QDrag(this); + QMimeData *mimeData = new QMimeData; + mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection)); + dragInfo.dragObject->setMimeData(mimeData); + dragInfo.dragObject->start(Qt::CopyAction); + // Don't delete the QTextDrag object. Qt will delete it when it's done with it. } void TerminalDisplay::outputSuspended(bool suspended) { //create the label when this function is first called - if (!_outputSuspendedLabel) { - //This label includes a link to an English language website - //describing the 'flow control' (Xon/Xoff) feature found in almost - //all terminal emulators. - //If there isn't a suitable article available in the target language the link - //can simply be removed. - _outputSuspendedLabel = new QLabel( ("Output has been " - "suspended" - " by pressing Ctrl+S." - " Press Ctrl+Q to resume."), - this ); + if (!_outputSuspendedLabel) + { + //This label includes a link to an English language website + //describing the 'flow control' (Xon/Xoff) feature found in almost + //all terminal emulators. + //If there isn't a suitable article available in the target language the link + //can simply be removed. + _outputSuspendedLabel = new QLabel( ("Output has been " + "suspended" + " by pressing Ctrl+S." + " Press Ctrl+Q to resume."), + this ); - QPalette palette(_outputSuspendedLabel->palette()); + QPalette palette(_outputSuspendedLabel->palette()); + //KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); + _outputSuspendedLabel->setPalette(palette); + _outputSuspendedLabel->setAutoFillBackground(true); + _outputSuspendedLabel->setBackgroundRole(QPalette::Base); + _outputSuspendedLabel->setFont(QApplication::font()); + _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); - palette.setColor(QPalette::Normal, QPalette::WindowText, QColor(Qt::white)); - palette.setColor(QPalette::Normal, QPalette::Window, QColor(Qt::black)); -// KColorScheme::adjustForeground(palette,KColorScheme::NeutralText); -// KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); - _outputSuspendedLabel->setPalette(palette); - _outputSuspendedLabel->setAutoFillBackground(true); - _outputSuspendedLabel->setBackgroundRole(QPalette::Base); - _outputSuspendedLabel->setFont(QApplication::font()); - _outputSuspendedLabel->setMargin(5); + //enable activation of "Xon/Xoff" link in label + _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | + Qt::LinksAccessibleByKeyboard); + _outputSuspendedLabel->setOpenExternalLinks(true); + _outputSuspendedLabel->setVisible(false); - //enable activation of "Xon/Xoff" link in label - _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | - Qt::LinksAccessibleByKeyboard); - _outputSuspendedLabel->setOpenExternalLinks(true); - _outputSuspendedLabel->setVisible(false); - - _gridLayout->addWidget(_outputSuspendedLabel); - _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding, - QSizePolicy::Expanding), - 1,0); + _gridLayout->addWidget(_outputSuspendedLabel); + _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding, + QSizePolicy::Expanding), + 1,0); } @@ -2735,13 +2912,69 @@ void TerminalDisplay::outputSuspended(bool suspended) uint TerminalDisplay::lineSpacing() const { - return _lineSpacing; + return _lineSpacing; } void TerminalDisplay::setLineSpacing(uint i) { - _lineSpacing = i; - setVTFont(font()); // Trigger an update. + _lineSpacing = i; + setVTFont(font()); // Trigger an update. } -//#include "moc_TerminalDisplay.cpp" +AutoScrollHandler::AutoScrollHandler(QWidget* parent) +: QObject(parent) +, _timerId(0) +{ + parent->installEventFilter(this); +} +void AutoScrollHandler::timerEvent(QTimerEvent* event) +{ + if (event->timerId() != _timerId) + return; + + QMouseEvent mouseEvent( QEvent::MouseMove, + widget()->mapFromGlobal(QCursor::pos()), + Qt::NoButton, + Qt::LeftButton, + Qt::NoModifier); + + QApplication::sendEvent(widget(),&mouseEvent); +} +bool AutoScrollHandler::eventFilter(QObject* watched,QEvent* event) +{ + Q_ASSERT( watched == parent() ); + Q_UNUSED( watched ); + + QMouseEvent* mouseEvent = (QMouseEvent*)event; + switch (event->type()) + { + case QEvent::MouseMove: + { + bool mouseInWidget = widget()->rect().contains(mouseEvent->pos()); + + if (mouseInWidget) + { + if (_timerId) + killTimer(_timerId); + _timerId = 0; + } + else + { + if (!_timerId && (mouseEvent->buttons() & Qt::LeftButton)) + _timerId = startTimer(100); + } + break; + } + case QEvent::MouseButtonRelease: + if (_timerId && (mouseEvent->buttons() & ~Qt::LeftButton)) + { + killTimer(_timerId); + _timerId = 0; + } + break; + default: + break; + }; + + return false; +} diff --git a/lib/TerminalDisplay.h b/lib/TerminalDisplay.h index b016b0f..69adc95 100644 --- a/lib/TerminalDisplay.h +++ b/lib/TerminalDisplay.h @@ -1,7 +1,7 @@ /* - Copyright (C) 2007 by Robert Knight - Copyright (C) 1997,1998 by Lars Doelle - + Copyright 2007-2008 by Robert Knight + Copyright 1997,1998 by Lars Doelle + Rewritten for QT4 by e_k , Copyright (C)2008 This program is free software; you can redistribute it and/or modify @@ -31,7 +31,6 @@ // Konsole #include "Filter.h" #include "Character.h" -#include "ColorTables.h" class QDrag; class QDragEnterEvent; @@ -39,12 +38,12 @@ class QDropEvent; class QLabel; class QTimer; class QEvent; -class QFrame; class QGridLayout; class QKeyEvent; class QScrollBar; class QShowEvent; class QHideEvent; +class QTimerEvent; class QWidget; //class KMenu; @@ -60,22 +59,22 @@ class ScreenWindow; * A widget which displays output from a terminal emulation and sends input keypresses and mouse activity * to the terminal. * - * When the terminal emulation receives new output from the program running in the terminal, + * When the terminal emulation receives new output from the program running in the terminal, * it will update the display by calling updateImage(). * * TODO More documentation */ class TerminalDisplay : public QWidget { - Q_OBJECT + Q_OBJECT public: /** Constructs a new terminal display widget with the specified parent. */ - TerminalDisplay(QWidget * parent=0); + TerminalDisplay(QWidget *parent=0); virtual ~TerminalDisplay(); /** Returns the terminal color palette used by the display. */ - const ColorEntry * colorTable() const; + const ColorEntry* colorTable() const; /** Sets the terminal color palette used by the display. */ void setColorTable(const ColorEntry table[]); /** @@ -92,24 +91,25 @@ public: /** Sets the opacity of the terminal display. */ void setOpacity(qreal opacity); - /** + /** * This enum describes the location where the scroll bar is positioned in the display widget. */ - enum ScrollBarPosition { + enum ScrollBarPosition + { /** Do not show the scroll bar. */ - NoScrollBar=0, + NoScrollBar=0, /** Show the scroll bar on the left side of the display. */ - ScrollBarLeft=1, + ScrollBarLeft=1, /** Show the scroll bar on the right side of the display. */ - ScrollBarRight=2 + ScrollBarRight=2 }; - /** + /** * Specifies whether the terminal display has a vertical scroll bar, and if so whether it * is shown on the left or right side of the display. */ void setScrollBarPosition(ScrollBarPosition position); - /** + /** * Sets the current position and range of the display's scroll bar. * * @param cursor The position of the scroll bar's thumb. @@ -117,7 +117,7 @@ public: */ void setScroll(int cursor, int lines); - /** + /** * Returns the display's filter chain. When the image for the display is updated, * the text is passed through each filter in the chain. Each filter can define * hotspots which correspond to certain strings (such as URLs or particular words). @@ -128,13 +128,13 @@ public: * To add a new filter to the view, call: * viewWidget->filterChain()->addFilter( filterObject ); */ - FilterChain * filterChain() const; + FilterChain* filterChain() const; - /** + /** * Updates the filters in the display's filter chain. This will cause * the hotspots to be updated to match the current image. * - * WARNING: This function can be expensive depending on the + * WARNING: This function can be expensive depending on the * image size and number of filters in the filterChain() * * TODO - This API does not really allow efficient usage. Revise it so @@ -143,47 +143,41 @@ public: * eg: * - Area of interest may be known ( eg. mouse cursor hovering * over an area ) - */ + */ void processFilters(); - /** + /** * Returns a list of menu actions created by the filters for the content * at the given @p position. */ - QList filterActions(const QPoint & position); + QList filterActions(const QPoint& position); /** Returns true if the cursor is set to blink or false otherwise. */ - bool blinkingCursor() { - return _hasBlinkingCursor; - } + bool blinkingCursor() { return _hasBlinkingCursor; } /** Specifies whether or not the cursor blinks. */ void setBlinkingCursor(bool blink); - void setCtrlDrag(bool enable) { - _ctrlDrag=enable; - } - bool ctrlDrag() { - return _ctrlDrag; - } + /** Specifies whether or not text can blink. */ + void setBlinkingTextEnabled(bool blink); - /** - * This enum describes the methods for selecting text when - * the user triple-clicks within the display. - */ - enum TripleClickMode { + void setCtrlDrag(bool enable) { _ctrlDrag=enable; } + bool ctrlDrag() { return _ctrlDrag; } + + /** + * This enum describes the methods for selecting text when + * the user triple-clicks within the display. + */ + enum TripleClickMode + { /** Select the whole line underneath the cursor. */ SelectWholeLine, /** Select from the current cursor position to the end of the line. */ SelectForwardsFromCursor }; - /** Sets how the text is selected when the user triple clicks within the display. */ - void setTripleClickMode(TripleClickMode mode) { - _tripleClickMode = mode; - } + /** Sets how the text is selected when the user triple clicks within the display. */ + void setTripleClickMode(TripleClickMode mode) { _tripleClickMode = mode; } /** See setTripleClickSelectionMode() */ - TripleClickMode tripleClickMode() { - return _tripleClickMode; - } + TripleClickMode tripleClickMode() { return _tripleClickMode; } void setLineSpacing(uint); uint lineSpacing() const; @@ -194,25 +188,26 @@ public: * This enum describes the available shapes for the keyboard cursor. * See setKeyboardCursorShape() */ - enum KeyboardCursorShape { + enum KeyboardCursorShape + { /** A rectangular block which covers the entire area of the cursor character. */ BlockCursor, - /** + /** * A single flat line which occupies the space at the bottom of the cursor * character's area. */ UnderlineCursor, - /** - * An cursor shaped like the capital letter 'I', similar to the IBeam + /** + * An cursor shaped like the capital letter 'I', similar to the IBeam * cursor used in Qt/KDE text editors. */ IBeamCursor }; - /** - * Sets the shape of the keyboard cursor. This is the cursor drawn + /** + * Sets the shape of the keyboard cursor. This is the cursor drawn * at the position in the terminal where keyboard input will appear. * - * In addition the terminal display widget also has a cursor for + * In addition the terminal display widget also has a cursor for * the mouse pointer, which can be set using the QWidget::setCursor() * method. * @@ -225,7 +220,7 @@ public: KeyboardCursorShape keyboardCursorShape() const; /** - * Sets the color used to draw the keyboard cursor. + * Sets the color used to draw the keyboard cursor. * * The keyboard cursor defaults to using the foreground color of the character * underneath it. @@ -237,12 +232,12 @@ public: * @param color The color to use to draw the cursor. This is only taken into * account if @p useForegroundColor is false. */ - void setKeyboardCursorColor(bool useForegroundColor , const QColor & color); + void setKeyboardCursorColor(bool useForegroundColor , const QColor& color); - /** + /** * Returns the color of the keyboard cursor, or an invalid color if the keyboard * cursor color is set to change according to the foreground color of the character - * underneath it. + * underneath it. */ QColor keyboardCursorColor() const; @@ -252,9 +247,7 @@ public: * This will depend upon the height of the widget and the current font. * See fontHeight() */ - int lines() { - return _lines; - } + int lines() { return _lines; } /** * Returns the number of characters of text which can be displayed on * each line in the widget. @@ -262,32 +255,26 @@ public: * This will depend upon the width of the widget and the current font. * See fontWidth() */ - int columns() { - return _columns; - } + int columns() { return _columns; } /** * Returns the height of the characters in the font used to draw the text in the display. */ - int fontHeight() { - return _fontHeight; - } + int fontHeight() { return _fontHeight; } /** - * Returns the width of the characters in the display. + * Returns the width of the characters in the display. * This assumes the use of a fixed-width font. */ - int fontWidth() { - return _fontWidth; - } + int fontWidth() { return _fontWidth; } void setSize(int cols, int lins); void setFixedSize(int cols, int lins); - + // reimplemented QSize sizeHint() const; /** - * Sets which characters, in addition to letters and numbers, + * Sets which characters, in addition to letters and numbers, * are regarded as being part of a word for the purposes * of selecting words in the display by double clicking on them. * @@ -297,119 +284,117 @@ public: * @param wc An array of characters which are to be considered parts * of a word ( in addition to letters and numbers ). */ - void setWordCharacters(const QString & wc); - /** - * Returns the characters which are considered part of a word for the + void setWordCharacters(const QString& wc); + /** + * Returns the characters which are considered part of a word for the * purpose of selecting words in the display with the mouse. * * @see setWordCharacters() */ - QString wordCharacters() { - return _wordCharacters; - } + QString wordCharacters() { return _wordCharacters; } - /** - * Sets the type of effect used to alert the user when a 'bell' occurs in the + /** + * Sets the type of effect used to alert the user when a 'bell' occurs in the * terminal session. * * The terminal session can trigger the bell effect by calling bell() with * the alert message. */ void setBellMode(int mode); - /** + /** * Returns the type of effect used to alert the user when a 'bell' occurs in * the terminal session. - * + * * See setBellMode() */ - int bellMode() { - return _bellMode; - } + int bellMode() { return _bellMode; } /** * This enum describes the different types of sounds and visual effects which * can be used to alert the user when a 'bell' occurs in the terminal * session. */ - enum BellMode { + enum BellMode + { /** A system beep. */ - SystemBeepBell=0, - /** + SystemBeepBell=0, + /** * KDE notification. This may play a sound, show a passive popup * or perform some other action depending on the user's settings. */ - NotifyBell=1, + NotifyBell=1, /** A silent, visual bell (eg. inverting the display's colors briefly) */ - VisualBell=2, + VisualBell=2, /** No bell effects */ - NoBell=3 + NoBell=3 }; - void setSelection(const QString & t); + void setSelection(const QString &t); - /** + /** * Reimplemented. Has no effect. Use setVTFont() to change the font * used to draw characters in the display. */ virtual void setFont(const QFont &); /** Returns the font used to draw characters in the display */ - QFont getVTFont() { - return font(); - } + QFont getVTFont() { return font(); } - /** + /** * Sets the font used to draw the display. Has no effect if @p font - * is larger than the size of the display itself. + * is larger than the size of the display itself. */ - void setVTFont(const QFont & font); + void setVTFont(const QFont& font); /** * Specified whether anti-aliasing of text in the terminal display * is enabled or not. Defaults to enabled. */ - static void setAntialias( bool antialias ) { - _antialiasText = antialias; - } - /** + static void setAntialias( bool antialias ) { _antialiasText = antialias; } + /** * Returns true if anti-aliasing of text in the terminal is enabled. */ - static bool antialias() { - return _antialiasText; - } + static bool antialias() { return _antialiasText; } /** - * Sets whether or not the current height and width of the + * Specifies whether characters with intense colors should be rendered + * as bold. Defaults to true. + */ + void setBoldIntense(bool value) { _boldIntense = value; } + /** + * Returns true if characters with intense colors are rendered in bold. + */ + bool getBoldIntense() { return _boldIntense; } + + /** + * Sets whether or not the current height and width of the * terminal in lines and columns is displayed whilst the widget * is being resized. */ - void setTerminalSizeHint(bool on) { - _terminalSizeHint=on; - } - /** + void setTerminalSizeHint(bool on) { _terminalSizeHint=on; } + /** * Returns whether or not the current height and width of * the terminal in lines and columns is displayed whilst the widget * is being resized. */ - bool terminalSizeHint() { - return _terminalSizeHint; - } - /** + bool terminalSizeHint() { return _terminalSizeHint; } + /** * Sets whether the terminal size display is shown briefly * after the widget is first shown. * * See setTerminalSizeHint() , isTerminalSizeHint() */ - void setTerminalSizeStartup(bool on) { - _terminalSizeStartup=on; - } + void setTerminalSizeStartup(bool on) { _terminalSizeStartup=on; } - void setBidiEnabled(bool set) { - _bidiEnabled=set; - } - bool isBidiEnabled() { - return _bidiEnabled; - } + /** + * Sets the status of the BiDi rendering inside the terminal display. + * Defaults to disabled. + */ + void setBidiEnabled(bool set) { _bidiEnabled=set; } + /** + * Returns the status of the BiDi rendering in this widget. + */ + bool isBidiEnabled() { return _bidiEnabled; } /** * Sets the terminal screen section which is displayed in this widget. @@ -419,29 +404,29 @@ public: * In terms of the model-view paradigm, the ScreenWindow is the model which is rendered * by the TerminalDisplay. */ - void setScreenWindow( ScreenWindow * window ); + void setScreenWindow( ScreenWindow* window ); /** Returns the terminal screen section which is displayed in this widget. See setScreenWindow() */ - ScreenWindow * screenWindow() const; + ScreenWindow* screenWindow() const; static bool HAVE_TRANSPARENCY; public slots: - /** + /** * Causes the terminal display to fetch the latest character image from the associated * terminal screen ( see setScreenWindow() ) and redraw the display. */ - void updateImage(); + void updateImage(); /** - * Causes the terminal display to fetch the latest line status flags from the - * associated terminal screen ( see setScreenWindow() ). - */ + * Causes the terminal display to fetch the latest line status flags from the + * associated terminal screen ( see setScreenWindow() ). + */ void updateLineProperties(); /** Copies the selected text to the clipboard. */ void copyClipboard(); - /** - * Pastes the content of the clipboard into the + /** + * Pastes the content of the clipboard into the * display. */ void pasteClipboard(); @@ -451,20 +436,26 @@ public slots: */ void pasteSelection(); - /** - * Changes whether the flow control warning box should be shown when the flow control - * stop key (Ctrl+S) are pressed. - */ - void setFlowControlWarningEnabled(bool enabled); - - /** - * Causes the widget to display or hide a message informing the user that terminal - * output has been suspended (by using the flow control key combination Ctrl+S) - * - * @param suspended True if terminal output has been suspended and the warning message should - * be shown or false to indicate that terminal output has been resumed and that - * the warning message should disappear. + /** + * Changes whether the flow control warning box should be shown when the flow control + * stop key (Ctrl+S) are pressed. */ + void setFlowControlWarningEnabled(bool enabled); + /** + * Returns true if the flow control warning box is enabled. + * See outputSuspended() and setFlowControlWarningEnabled() + */ + bool flowControlWarningEnabled() const + { return _flowControlWarningEnabled; } + + /** + * Causes the widget to display or hide a message informing the user that terminal + * output has been suspended (by using the flow control key combination Ctrl+S) + * + * @param suspended True if terminal output has been suspended and the warning message should + * be shown or false to indicate that terminal output has been resumed and that + * the warning message should disappear. + */ void outputSuspended(bool suspended); /** @@ -474,7 +465,7 @@ public slots: * If this is set to true, mouse signals will be emitted by the view when the user clicks, drags * or otherwise moves the mouse inside the view. * The user interaction needed to create selections will also change, and the user will be required - * to hold down the shift key to create a selection or perform other mouse activities inside the + * to hold down the shift key to create a selection or perform other mouse activities inside the * view area - since the program running in the terminal is being allowed to handle normal mouse * events itself. * @@ -482,16 +473,28 @@ public slots: * or false otherwise. */ void setUsesMouse(bool usesMouse); - + /** See setUsesMouse() */ bool usesMouse() const; - /** + /** * Shows a notification that a bell event has occurred in the terminal. * TODO: More documentation here */ - void bell(const QString & message); + void bell(const QString& message); + /** + * Sets the background of the display to the specified color. + * @see setColorTable(), setForegroundColor() + */ + void setBackgroundColor(const QColor& color); + + /** + * Sets the text of the display to the specified color. + * @see setColorTable(), setBackgroundColor() + */ + void setForegroundColor(const QColor& color); + void selectionChanged(); signals: @@ -501,17 +504,9 @@ signals: /** * Emitted when the user presses a key whilst the terminal widget has focus. */ - void keyPressedSignal(QKeyEvent * e); + void keyPressedSignal(QKeyEvent *e); - /** - * Emitted when the user presses the suspend or resume flow control key combinations - * - * @param suspend true if the user pressed Ctrl+S (the suspend output key combination) or - * false if the user pressed Ctrl+Q (the resume output key combination) - */ - void flowControlKeyPressed(bool suspend); - - /** + /** * A mouse event occurred. * @param button The mouse button (0 for left button, 1 for middle button, 2 for right button, 3 for release) * @param column The character column where the event occurred @@ -522,17 +517,28 @@ signals: void changedFontMetricSignal(int height, int width); void changedContentSizeSignal(int height, int width); - /** + /** * Emitted when the user right clicks on the display, or right-clicks with the Shift * key held down if usesMouse() is true. * * This can be used to display a context menu. */ - void configureRequest( TerminalDisplay *, int state, const QPoint & position ); + void configureRequest(const QPoint& position); - void isBusySelecting(bool); - void sendStringToEmu(const char *); + /** + * When a shortcut which is also a valid terminal key sequence is pressed while + * the terminal widget has focus, this signal is emitted to allow the host to decide + * whether the shortcut should be overridden. + * When the shortcut is overridden, the key sequence will be sent to the terminal emulation instead + * and the action associated with the shortcut will not be triggered. + * + * @p override is set to false by default and the shortcut will be triggered as normal. + */ + void overrideShortcutCheck(QKeyEvent* keyEvent,bool& override); + void isBusySelecting(bool); + void sendStringToEmu(const char*); + //! Signals to inform that the widget get or lost focus void termGetFocus(); void termLostFocus(); @@ -542,45 +548,49 @@ protected: virtual void paintEvent( QPaintEvent * ); - virtual void showEvent(QShowEvent *); - virtual void hideEvent(QHideEvent *); - virtual void resizeEvent(QResizeEvent *); + virtual void showEvent(QShowEvent*); + virtual void hideEvent(QHideEvent*); + virtual void resizeEvent(QResizeEvent*); - virtual void fontChange(const QFont & font); + virtual void fontChange(const QFont &font); + virtual void focusInEvent(QFocusEvent* event); + virtual void focusOutEvent(QFocusEvent* event); + virtual void keyPressEvent(QKeyEvent* event); + virtual void mouseDoubleClickEvent(QMouseEvent* ev); + virtual void mousePressEvent( QMouseEvent* ); + virtual void mouseReleaseEvent( QMouseEvent* ); + virtual void mouseMoveEvent( QMouseEvent* ); + virtual void extendSelection( const QPoint& pos ); + virtual void wheelEvent( QWheelEvent* ); - virtual void keyPressEvent(QKeyEvent * event); - virtual void mouseDoubleClickEvent(QMouseEvent * ev); - virtual void mousePressEvent( QMouseEvent * ); - virtual void mouseReleaseEvent( QMouseEvent * ); - virtual void mouseMoveEvent( QMouseEvent * ); - virtual void extendSelection( const QPoint & pos ); - virtual void wheelEvent( QWheelEvent * ); - - // focus handling virtual bool focusNextPrevChild( bool next ); - virtual void focusInEvent(QFocusEvent * event); - virtual void focusOutEvent(QFocusEvent * event); - + // drag and drop - virtual void dragEnterEvent(QDragEnterEvent * event); - virtual void dropEvent(QDropEvent * event); + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dropEvent(QDropEvent* event); void doDrag(); enum DragState { diNone, diPending, diDragging }; struct _dragInfo { - DragState state; - QPoint start; - QDrag * dragObject; + DragState state; + QPoint start; + QDrag *dragObject; } dragInfo; - virtual int charClass(quint16) const; + // classifies the 'ch' into one of three categories + // and returns a character to indicate which category it is in + // + // - A space (returns ' ') + // - Part of a word (returns 'a') + // - Other characters (returns the input character) + QChar charClass(QChar ch) const; void clearImage(); - void mouseTripleClickEvent(QMouseEvent * ev); + void mouseTripleClickEvent(QMouseEvent* ev); // reimplemented - virtual void inputMethodEvent ( QInputMethodEvent * event ); + virtual void inputMethodEvent ( QInputMethodEvent* event ); virtual QVariant inputMethodQuery( Qt::InputMethodQuery query ) const; protected slots: @@ -588,7 +598,7 @@ protected slots: void scrollBarPositionChanged(int value); void blinkEvent(); void blinkCursorEvent(); - + //Renables bell noises and visuals. Used to disable further bells for a short period of time //after emitting the first in a sequence of bell events. void enableBell(); @@ -604,39 +614,39 @@ private: // divides the part of the display specified by 'rect' into // fragments according to their colors and styles and calls - // drawTextFragment() to draw the fragments - void drawContents(QPainter & paint, const QRect & rect); + // drawTextFragment() to draw the fragments + void drawContents(QPainter &paint, const QRect &rect); // draws a section of text, all the text in this section // has a common color and style - void drawTextFragment(QPainter & painter, const QRect & rect, - const QString & text, const Character * style); + void drawTextFragment(QPainter& painter, const QRect& rect, + const QString& text, const Character* style); // draws the background for a text fragment // if useOpacitySetting is true then the color's alpha value will be set to // the display's transparency (set with setOpacity()), otherwise the background // will be drawn fully opaque - void drawBackground(QPainter & painter, const QRect & rect, const QColor & color, + void drawBackground(QPainter& painter, const QRect& rect, const QColor& color, bool useOpacitySetting); // draws the cursor character - void drawCursor(QPainter & painter, const QRect & rect , const QColor & foregroundColor, - const QColor & backgroundColor , bool & invertColors); + void drawCursor(QPainter& painter, const QRect& rect , const QColor& foregroundColor, + const QColor& backgroundColor , bool& invertColors); // draws the characters or line graphics in a text fragment - void drawCharacters(QPainter & painter, const QRect & rect, const QString & text, - const Character * style, bool invertCharacterColor); + void drawCharacters(QPainter& painter, const QRect& rect, const QString& text, + const Character* style, bool invertCharacterColor); // draws a string of line graphics - void drawLineCharString(QPainter & painter, int x, int y, - const QString & str, const Character * attributes); + void drawLineCharString(QPainter& painter, int x, int y, + const QString& str, const Character* attributes); // draws the preedit string for input methods - void drawInputMethodPreeditString(QPainter & painter , const QRect & rect); + void drawInputMethodPreeditString(QPainter& painter , const QRect& rect); // -- - // maps an area in the character image to an area on the widget - QRect imageToWidget(const QRect & imageArea) const; + // maps an area in the character image to an area on the widget + QRect imageToWidget(const QRect& imageArea) const; - // maps a point on the widget to the position ( ie. line and column ) + // maps a point on the widget to the position ( ie. line and column ) // of the character at that point. - void getCharacterPosition(const QPoint & widgetPoint,int & line,int & column) const; + void getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const; // the area where the preedit string for input methods will be draw QRect preeditRect() const; @@ -645,20 +655,20 @@ private: // current size in columns and lines void showResizeNotification(); - // scrolls the image by a number of lines. - // 'lines' may be positive ( to scroll the image down ) + // scrolls the image by a number of lines. + // 'lines' may be positive ( to scroll the image down ) // or negative ( to scroll the image up ) // 'region' is the part of the image to scroll - currently only // the top, bottom and height of 'region' are taken into account, // the left and right are ignored. - void scrollImage(int lines , const QRect & region); + void scrollImage(int lines , const QRect& region); void calcGeometry(); void propagateSize(); void updateImageSize(); void makeImage(); - - void paintFilters(QPainter & painter); + + void paintFilters(QPainter& painter); // returns a region covering all of the areas of the widget which contain // a hotspot @@ -667,37 +677,43 @@ private: // returns the position of the cursor in columns and lines QPoint cursorPosition() const; + // redraws the cursor + void updateCursor(); + + bool handleShortcutOverrideEvent(QKeyEvent* event); + // the window onto the terminal screen which this display - // is currently showing. + // is currently showing. QPointer _screenWindow; bool _allowBell; - QGridLayout * _gridLayout; + QGridLayout* _gridLayout; bool _fixedFont; // has fixed pitch int _fontHeight; // height int _fontWidth; // width int _fontAscent; // ascend + bool _boldIntense; // Whether intense colors should be rendered with bold font int _leftMargin; // offset int _topMargin; // offset int _lines; // the number of lines that can be displayed in the widget int _columns; // the number of columns that can be displayed in the widget - + int _usedLines; // the number of lines that are actually being used, this will be less - // than 'lines' if the character image provided with setImage() is smaller - // than the maximum image size which can be displayed + // than 'lines' if the character image provided with setImage() is smaller + // than the maximum image size which can be displayed int _usedColumns; // the number of columns that are actually being used, this will be less - // than 'columns' if the character image provided with setImage() is smaller - // than the maximum image size which can be displayed - + // than 'columns' if the character image provided with setImage() is smaller + // than the maximum image size which can be displayed + int _contentHeight; int _contentWidth; - Character * _image; // [lines][columns] - // only the area [usedLines][usedColumns] in the image contains valid data + Character* _image; // [lines][columns] + // only the area [usedLines][usedColumns] in the image contains valid data int _imageSize; QVector _lineProperties; @@ -720,8 +736,8 @@ private: bool _preserveLineBreaks; bool _columnSelectionMode; - QClipboard * _clipboard; - QScrollBar * _scrollBar; + QClipboard* _clipboard; + QScrollBar* _scrollBar; ScrollBarPosition _scrollbarLocation; QString _wordCharacters; int _bellMode; @@ -730,50 +746,52 @@ private: bool _hasBlinker; // has characters to blink bool _cursorBlinking; // hide cursor in paintEvent bool _hasBlinkingCursor; // has blinking cursor enabled + bool _allowBlinkingText; // allow text to blink bool _ctrlDrag; // require Ctrl key for drag TripleClickMode _tripleClickMode; bool _isFixedSize; //Columns / lines are locked. - QTimer * _blinkTimer; // active when hasBlinker - QTimer * _blinkCursorTimer; // active when hasBlinkingCursor + QTimer* _blinkTimer; // active when hasBlinker + QTimer* _blinkCursorTimer; // active when hasBlinkingCursor -// KMenu* _drop; + //KMenu* _drop; QString _dropText; int _dndFileCount; bool _possibleTripleClick; // is set in mouseDoubleClickEvent and deleted - // after QApplication::doubleClickInterval() delay + // after QApplication::doubleClickInterval() delay - QLabel * _resizeWidget; - QTimer * _resizeTimer; + QLabel* _resizeWidget; + QTimer* _resizeTimer; bool _flowControlWarningEnabled; //widgets related to the warning message that appears when the user presses Ctrl+S to suspend //terminal output - informing them what has happened and how to resume output - QLabel * _outputSuspendedLabel; - + QLabel* _outputSuspendedLabel; + uint _lineSpacing; bool _colorsInverted; // true during visual bell QSize _size; - + QRgb _blendColor; // list of filters currently applied to the display. used for links and // search highlight - TerminalImageFilterChain * _filterChain; - QRect _mouseOverHotspotArea; + TerminalImageFilterChain* _filterChain; + QRegion _mouseOverHotspotArea; KeyboardCursorShape _cursorShape; // custom cursor color. if this is invalid then the foreground // color of the character under the cursor is used - QColor _cursorColor; + QColor _cursorColor; - struct InputMethodData { + struct InputMethodData + { QString preeditString; QRect previousPreeditRect; }; @@ -782,16 +800,31 @@ private: static bool _antialiasText; // do we antialias or not //the delay in milliseconds between redrawing blinking text - static const int BLINK_DELAY = 500; + static const int TEXT_BLINK_DELAY = 500; static const int DEFAULT_LEFT_MARGIN = 1; static const int DEFAULT_TOP_MARGIN = 1; public: - static void setTransparencyEnabled(bool enable) { + static void setTransparencyEnabled(bool enable) + { HAVE_TRANSPARENCY = enable; } }; +class AutoScrollHandler : public QObject +{ +Q_OBJECT + +public: + AutoScrollHandler(QWidget* parent); +protected: + virtual void timerEvent(QTimerEvent* event); + virtual bool eventFilter(QObject* watched,QEvent* event); +private: + QWidget* widget() const { return static_cast(parent()); } + int _timerId; +}; + } #endif // TERMINALDISPLAY_H diff --git a/lib/qtermwidget.cpp b/lib/qtermwidget.cpp index ee92f65..231244d 100644 --- a/lib/qtermwidget.cpp +++ b/lib/qtermwidget.cpp @@ -18,6 +18,7 @@ #include "qtermwidget.h" +#include "ColorTables.h" #include "Session.h" #include "TerminalDisplay.h"