[050d67a] | 1 | /* |
---|
| 2 | This file is part of Konsole, a terminal emulator for KDE. |
---|
| 3 | |
---|
| 4 | Copyright (C) 2006-7 by Robert Knight <robertknight@gmail.com> |
---|
| 5 | Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> |
---|
| 6 | |
---|
| 7 | Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 |
---|
| 8 | |
---|
| 9 | This program is free software; you can redistribute it and/or modify |
---|
| 10 | it under the terms of the GNU General Public License as published by |
---|
| 11 | the Free Software Foundation; either version 2 of the License, or |
---|
| 12 | (at your option) any later version. |
---|
| 13 | |
---|
| 14 | This program is distributed in the hope that it will be useful, |
---|
| 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
| 17 | GNU General Public License for more details. |
---|
| 18 | |
---|
| 19 | You should have received a copy of the GNU General Public License |
---|
| 20 | along with this program; if not, write to the Free Software |
---|
| 21 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
---|
| 22 | 02110-1301 USA. |
---|
| 23 | */ |
---|
| 24 | |
---|
| 25 | // Own |
---|
| 26 | #include "TerminalDisplay.h" |
---|
| 27 | |
---|
| 28 | // Qt |
---|
| 29 | #include <QtGui/QApplication> |
---|
| 30 | #include <QtGui/QBoxLayout> |
---|
| 31 | #include <QtGui/QClipboard> |
---|
| 32 | #include <QtGui/QKeyEvent> |
---|
| 33 | #include <QtCore/QEvent> |
---|
| 34 | #include <QtCore/QTime> |
---|
| 35 | #include <QtCore/QFile> |
---|
| 36 | #include <QtGui/QGridLayout> |
---|
| 37 | #include <QtGui/QLabel> |
---|
| 38 | #include <QtGui/QLayout> |
---|
| 39 | #include <QtGui/QPainter> |
---|
| 40 | #include <QtGui/QPixmap> |
---|
| 41 | #include <QtGui/QScrollBar> |
---|
| 42 | #include <QtGui/QStyle> |
---|
| 43 | #include <QtCore> |
---|
| 44 | #include <QtGui> |
---|
| 45 | |
---|
| 46 | #include "Filter.h" |
---|
| 47 | #include "konsole_wcwidth.h" |
---|
| 48 | #include "ScreenWindow.h" |
---|
| 49 | #include "TerminalCharacterDecoder.h" |
---|
| 50 | #include "ColorTables.h" |
---|
| 51 | |
---|
| 52 | |
---|
| 53 | using namespace Konsole; |
---|
| 54 | |
---|
| 55 | #ifndef loc |
---|
| 56 | #define loc(X,Y) ((Y)*_columns+(X)) |
---|
| 57 | #endif |
---|
| 58 | |
---|
| 59 | #define yMouseScroll 1 |
---|
| 60 | |
---|
| 61 | #define REPCHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ |
---|
| 62 | "abcdefgjijklmnopqrstuvwxyz" \ |
---|
| 63 | "0123456789./+@" |
---|
| 64 | |
---|
| 65 | // scroll increment used when dragging selection at top/bottom of window. |
---|
| 66 | |
---|
| 67 | // static |
---|
| 68 | bool TerminalDisplay::_antialiasText = true; |
---|
| 69 | bool TerminalDisplay::HAVE_TRANSPARENCY = false; |
---|
| 70 | |
---|
| 71 | /* ------------------------------------------------------------------------- */ |
---|
| 72 | /* */ |
---|
| 73 | /* Colors */ |
---|
| 74 | /* */ |
---|
| 75 | /* ------------------------------------------------------------------------- */ |
---|
| 76 | |
---|
| 77 | /* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) |
---|
| 78 | |
---|
| 79 | Code 0 1 2 3 4 5 6 7 |
---|
| 80 | ----------- ------- ------- ------- ------- ------- ------- ------- ------- |
---|
| 81 | ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White |
---|
| 82 | IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White |
---|
| 83 | */ |
---|
| 84 | |
---|
| 85 | ScreenWindow* TerminalDisplay::screenWindow() const |
---|
| 86 | { |
---|
| 87 | return _screenWindow; |
---|
| 88 | } |
---|
| 89 | void TerminalDisplay::setScreenWindow(ScreenWindow* window) |
---|
| 90 | { |
---|
| 91 | // disconnect existing screen window if any |
---|
| 92 | if ( _screenWindow ) |
---|
| 93 | { |
---|
| 94 | disconnect( _screenWindow , 0 , this , 0 ); |
---|
| 95 | } |
---|
| 96 | |
---|
| 97 | _screenWindow = window; |
---|
| 98 | |
---|
| 99 | if ( window ) |
---|
| 100 | { |
---|
| 101 | //#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?" |
---|
| 102 | connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) ); |
---|
| 103 | connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) ); |
---|
| 104 | window->setWindowLines(_lines); |
---|
| 105 | } |
---|
| 106 | } |
---|
| 107 | |
---|
| 108 | const ColorEntry* TerminalDisplay::colorTable() const |
---|
| 109 | { |
---|
| 110 | return _colorTable; |
---|
| 111 | } |
---|
| 112 | |
---|
| 113 | void TerminalDisplay::setColorTable(const ColorEntry table[]) |
---|
| 114 | { |
---|
| 115 | for (int i = 0; i < TABLE_COLORS; i++) |
---|
| 116 | _colorTable[i] = table[i]; |
---|
| 117 | |
---|
| 118 | QPalette p = palette(); |
---|
| 119 | p.setColor( backgroundRole(), _colorTable[DEFAULT_BACK_COLOR].color ); |
---|
| 120 | setPalette( p ); |
---|
| 121 | |
---|
| 122 | // Avoid propagating the palette change to the scroll bar |
---|
| 123 | _scrollBar->setPalette( QApplication::palette() ); |
---|
| 124 | |
---|
| 125 | update(); |
---|
| 126 | } |
---|
| 127 | |
---|
| 128 | /* ------------------------------------------------------------------------- */ |
---|
| 129 | /* */ |
---|
| 130 | /* Font */ |
---|
| 131 | /* */ |
---|
| 132 | /* ------------------------------------------------------------------------- */ |
---|
| 133 | |
---|
| 134 | /* |
---|
| 135 | The VT100 has 32 special graphical characters. The usual vt100 extended |
---|
| 136 | xterm fonts have these at 0x00..0x1f. |
---|
| 137 | |
---|
| 138 | QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals |
---|
| 139 | come in here as proper unicode characters. |
---|
| 140 | |
---|
| 141 | We treat non-iso10646 fonts as VT100 extended and do the requiered mapping |
---|
| 142 | from unicode to 0x00..0x1f. The remaining translation is then left to the |
---|
| 143 | QCodec. |
---|
| 144 | */ |
---|
| 145 | |
---|
| 146 | static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);} |
---|
| 147 | static inline bool isLineCharString(const QString& string) |
---|
| 148 | { |
---|
| 149 | return (string.length() > 0) && (isLineChar(string.at(0).unicode())); |
---|
| 150 | } |
---|
| 151 | |
---|
| 152 | |
---|
| 153 | // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. |
---|
| 154 | |
---|
| 155 | unsigned short Konsole::vt100_graphics[32] = |
---|
| 156 | { // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 |
---|
| 157 | 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, |
---|
| 158 | 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, |
---|
| 159 | 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, |
---|
| 160 | 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 |
---|
| 161 | }; |
---|
| 162 | |
---|
| 163 | void TerminalDisplay::fontChange(const QFont&) |
---|
| 164 | { |
---|
| 165 | QFontMetrics fm(font()); |
---|
| 166 | _fontHeight = fm.height() + _lineSpacing; |
---|
| 167 | |
---|
| 168 | |
---|
| 169 | // waba TerminalDisplay 1.123: |
---|
| 170 | // "Base character width on widest ASCII character. This prevents too wide |
---|
| 171 | // characters in the presence of double wide (e.g. Japanese) characters." |
---|
| 172 | // Get the width from representative normal width characters |
---|
| 173 | _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR)); |
---|
| 174 | |
---|
| 175 | _fixedFont = true; |
---|
| 176 | |
---|
| 177 | int fw = fm.width(REPCHAR[0]); |
---|
| 178 | for(unsigned int i=1; i< strlen(REPCHAR); i++) |
---|
| 179 | { |
---|
| 180 | if (fw != fm.width(REPCHAR[i])) |
---|
| 181 | { |
---|
| 182 | _fixedFont = false; |
---|
| 183 | break; |
---|
| 184 | } |
---|
| 185 | } |
---|
| 186 | |
---|
| 187 | if (_fontWidth < 1) |
---|
| 188 | _fontWidth=1; |
---|
| 189 | |
---|
| 190 | _fontAscent = fm.ascent(); |
---|
| 191 | |
---|
| 192 | emit changedFontMetricSignal( _fontHeight, _fontWidth ); |
---|
| 193 | propagateSize(); |
---|
| 194 | update(); |
---|
| 195 | } |
---|
| 196 | |
---|
| 197 | void TerminalDisplay::setVTFont(const QFont& f) |
---|
| 198 | { |
---|
| 199 | QFont font = f; |
---|
| 200 | |
---|
| 201 | QFontMetrics metrics(font); |
---|
| 202 | |
---|
| 203 | if ( metrics.height() < height() && metrics.maxWidth() < width() ) |
---|
| 204 | { |
---|
| 205 | // hint that text should be drawn without anti-aliasing. |
---|
| 206 | // depending on the user's font configuration, this may not be respected |
---|
| 207 | if (!_antialiasText) |
---|
| 208 | font.setStyleStrategy( QFont::NoAntialias ); |
---|
| 209 | |
---|
| 210 | // experimental optimization. Konsole assumes that the terminal is using a |
---|
| 211 | // mono-spaced font, in which case kerning information should have an effect. |
---|
| 212 | // Disabling kerning saves some computation when rendering text. |
---|
| 213 | font.setKerning(false); |
---|
| 214 | |
---|
| 215 | QWidget::setFont(font); |
---|
| 216 | fontChange(font); |
---|
| 217 | } |
---|
| 218 | } |
---|
| 219 | |
---|
| 220 | void TerminalDisplay::setFont(const QFont &) |
---|
| 221 | { |
---|
| 222 | // ignore font change request if not coming from konsole itself |
---|
| 223 | } |
---|
| 224 | |
---|
| 225 | /* ------------------------------------------------------------------------- */ |
---|
| 226 | /* */ |
---|
| 227 | /* Constructor / Destructor */ |
---|
| 228 | /* */ |
---|
| 229 | /* ------------------------------------------------------------------------- */ |
---|
| 230 | |
---|
| 231 | TerminalDisplay::TerminalDisplay(QWidget *parent) |
---|
| 232 | :QWidget(parent) |
---|
| 233 | ,_screenWindow(0) |
---|
| 234 | ,_allowBell(true) |
---|
| 235 | ,_gridLayout(0) |
---|
| 236 | ,_fontHeight(1) |
---|
| 237 | ,_fontWidth(1) |
---|
| 238 | ,_fontAscent(1) |
---|
| 239 | ,_lines(1) |
---|
| 240 | ,_columns(1) |
---|
| 241 | ,_usedLines(1) |
---|
| 242 | ,_usedColumns(1) |
---|
| 243 | ,_contentHeight(1) |
---|
| 244 | ,_contentWidth(1) |
---|
| 245 | ,_image(0) |
---|
| 246 | ,_randomSeed(0) |
---|
| 247 | ,_resizing(false) |
---|
| 248 | ,_terminalSizeHint(false) |
---|
| 249 | ,_terminalSizeStartup(true) |
---|
| 250 | ,_bidiEnabled(false) |
---|
| 251 | ,_actSel(0) |
---|
| 252 | ,_wordSelectionMode(false) |
---|
| 253 | ,_lineSelectionMode(false) |
---|
| 254 | ,_preserveLineBreaks(false) |
---|
| 255 | ,_columnSelectionMode(false) |
---|
| 256 | ,_scrollbarLocation(NoScrollBar) |
---|
| 257 | ,_wordCharacters(":@-./_~") |
---|
| 258 | ,_bellMode(SystemBeepBell) |
---|
| 259 | ,_blinking(false) |
---|
| 260 | ,_cursorBlinking(false) |
---|
| 261 | ,_hasBlinkingCursor(false) |
---|
| 262 | ,_ctrlDrag(false) |
---|
| 263 | ,_tripleClickMode(SelectWholeLine) |
---|
| 264 | ,_isFixedSize(false) |
---|
| 265 | ,_possibleTripleClick(false) |
---|
| 266 | ,_resizeWidget(0) |
---|
| 267 | ,_resizeTimer(0) |
---|
| 268 | ,_flowControlWarningEnabled(false) |
---|
| 269 | ,_outputSuspendedLabel(0) |
---|
| 270 | ,_lineSpacing(0) |
---|
| 271 | ,_colorsInverted(false) |
---|
| 272 | ,_blendColor(qRgba(0,0,0,0xff)) |
---|
| 273 | ,_filterChain(new TerminalImageFilterChain()) |
---|
| 274 | ,_cursorShape(BlockCursor) |
---|
| 275 | { |
---|
| 276 | // terminal applications are not designed with Right-To-Left in mind, |
---|
| 277 | // so the layout is forced to Left-To-Right |
---|
| 278 | setLayoutDirection(Qt::LeftToRight); |
---|
| 279 | |
---|
| 280 | // The offsets are not yet calculated. |
---|
| 281 | // Do not calculate these too often to be more smoothly when resizing |
---|
| 282 | // konsole in opaque mode. |
---|
| 283 | _topMargin = DEFAULT_TOP_MARGIN; |
---|
| 284 | _leftMargin = DEFAULT_LEFT_MARGIN; |
---|
| 285 | |
---|
| 286 | // create scroll bar for scrolling output up and down |
---|
| 287 | // set the scroll bar's slider to occupy the whole area of the scroll bar initially |
---|
| 288 | _scrollBar = new QScrollBar(this); |
---|
| 289 | setScroll(0,0); |
---|
| 290 | _scrollBar->setCursor( Qt::ArrowCursor ); |
---|
| 291 | connect(_scrollBar, SIGNAL(valueChanged(int)), this, |
---|
| 292 | SLOT(scrollBarPositionChanged(int))); |
---|
| 293 | |
---|
| 294 | // setup timers for blinking cursor and text |
---|
| 295 | _blinkTimer = new QTimer(this); |
---|
| 296 | connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent())); |
---|
| 297 | _blinkCursorTimer = new QTimer(this); |
---|
| 298 | connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent())); |
---|
| 299 | |
---|
| 300 | // QCursor::setAutoHideCursor( this, true ); |
---|
| 301 | |
---|
| 302 | setUsesMouse(true); |
---|
| 303 | setColorTable(whiteonblack_color_table); |
---|
| 304 | // setColorTable(blackonlightyellow_color_table); |
---|
| 305 | setMouseTracking(true); |
---|
| 306 | |
---|
| 307 | // Enable drag and drop |
---|
| 308 | setAcceptDrops(true); // attempt |
---|
| 309 | dragInfo.state = diNone; |
---|
| 310 | |
---|
| 311 | setFocusPolicy( Qt::WheelFocus ); |
---|
| 312 | |
---|
| 313 | // enable input method support |
---|
| 314 | setAttribute(Qt::WA_InputMethodEnabled, true); |
---|
| 315 | |
---|
| 316 | // this is an important optimization, it tells Qt |
---|
| 317 | // that TerminalDisplay will handle repainting its entire area. |
---|
| 318 | setAttribute(Qt::WA_OpaquePaintEvent); |
---|
| 319 | |
---|
| 320 | _gridLayout = new QGridLayout(this); |
---|
| 321 | _gridLayout->setMargin(0); |
---|
| 322 | |
---|
| 323 | setLayout( _gridLayout ); |
---|
| 324 | |
---|
| 325 | //set up a warning message when the user presses Ctrl+S to avoid confusion |
---|
| 326 | connect( this,SIGNAL(flowControlKeyPressed(bool)),this,SLOT(outputSuspended(bool)) ); |
---|
| 327 | } |
---|
| 328 | |
---|
| 329 | TerminalDisplay::~TerminalDisplay() |
---|
| 330 | { |
---|
| 331 | qApp->removeEventFilter( this ); |
---|
| 332 | |
---|
| 333 | delete[] _image; |
---|
| 334 | |
---|
| 335 | delete _gridLayout; |
---|
| 336 | delete _outputSuspendedLabel; |
---|
| 337 | delete _filterChain; |
---|
| 338 | } |
---|
| 339 | |
---|
| 340 | /* ------------------------------------------------------------------------- */ |
---|
| 341 | /* */ |
---|
| 342 | /* Display Operations */ |
---|
| 343 | /* */ |
---|
| 344 | /* ------------------------------------------------------------------------- */ |
---|
| 345 | |
---|
| 346 | /** |
---|
| 347 | A table for emulating the simple (single width) unicode drawing chars. |
---|
| 348 | It represents the 250x - 257x glyphs. If it's zero, we can't use it. |
---|
| 349 | if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered |
---|
| 350 | 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit. |
---|
| 351 | |
---|
| 352 | Then, the pixels basically have the following interpretation: |
---|
| 353 | _|||_ |
---|
| 354 | -...- |
---|
| 355 | -...- |
---|
| 356 | -...- |
---|
| 357 | _|||_ |
---|
| 358 | |
---|
| 359 | where _ = none |
---|
| 360 | | = vertical line. |
---|
| 361 | - = horizontal line. |
---|
| 362 | */ |
---|
| 363 | |
---|
| 364 | |
---|
| 365 | enum LineEncode |
---|
| 366 | { |
---|
| 367 | TopL = (1<<1), |
---|
| 368 | TopC = (1<<2), |
---|
| 369 | TopR = (1<<3), |
---|
| 370 | |
---|
| 371 | LeftT = (1<<5), |
---|
| 372 | Int11 = (1<<6), |
---|
| 373 | Int12 = (1<<7), |
---|
| 374 | Int13 = (1<<8), |
---|
| 375 | RightT = (1<<9), |
---|
| 376 | |
---|
| 377 | LeftC = (1<<10), |
---|
| 378 | Int21 = (1<<11), |
---|
| 379 | Int22 = (1<<12), |
---|
| 380 | Int23 = (1<<13), |
---|
| 381 | RightC = (1<<14), |
---|
| 382 | |
---|
| 383 | LeftB = (1<<15), |
---|
| 384 | Int31 = (1<<16), |
---|
| 385 | Int32 = (1<<17), |
---|
| 386 | Int33 = (1<<18), |
---|
| 387 | RightB = (1<<19), |
---|
| 388 | |
---|
| 389 | BotL = (1<<21), |
---|
| 390 | BotC = (1<<22), |
---|
| 391 | BotR = (1<<23) |
---|
| 392 | }; |
---|
| 393 | |
---|
| 394 | #include "LineFont.h" |
---|
| 395 | |
---|
| 396 | static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code) |
---|
| 397 | { |
---|
| 398 | //Calculate cell midpoints, end points. |
---|
| 399 | int cx = x + w/2; |
---|
| 400 | int cy = y + h/2; |
---|
| 401 | int ex = x + w - 1; |
---|
| 402 | int ey = y + h - 1; |
---|
| 403 | |
---|
| 404 | quint32 toDraw = LineChars[code]; |
---|
| 405 | |
---|
| 406 | //Top _lines: |
---|
| 407 | if (toDraw & TopL) |
---|
| 408 | paint.drawLine(cx-1, y, cx-1, cy-2); |
---|
| 409 | if (toDraw & TopC) |
---|
| 410 | paint.drawLine(cx, y, cx, cy-2); |
---|
| 411 | if (toDraw & TopR) |
---|
| 412 | paint.drawLine(cx+1, y, cx+1, cy-2); |
---|
| 413 | |
---|
| 414 | //Bot _lines: |
---|
| 415 | if (toDraw & BotL) |
---|
| 416 | paint.drawLine(cx-1, cy+2, cx-1, ey); |
---|
| 417 | if (toDraw & BotC) |
---|
| 418 | paint.drawLine(cx, cy+2, cx, ey); |
---|
| 419 | if (toDraw & BotR) |
---|
| 420 | paint.drawLine(cx+1, cy+2, cx+1, ey); |
---|
| 421 | |
---|
| 422 | //Left _lines: |
---|
| 423 | if (toDraw & LeftT) |
---|
| 424 | paint.drawLine(x, cy-1, cx-2, cy-1); |
---|
| 425 | if (toDraw & LeftC) |
---|
| 426 | paint.drawLine(x, cy, cx-2, cy); |
---|
| 427 | if (toDraw & LeftB) |
---|
| 428 | paint.drawLine(x, cy+1, cx-2, cy+1); |
---|
| 429 | |
---|
| 430 | //Right _lines: |
---|
| 431 | if (toDraw & RightT) |
---|
| 432 | paint.drawLine(cx+2, cy-1, ex, cy-1); |
---|
| 433 | if (toDraw & RightC) |
---|
| 434 | paint.drawLine(cx+2, cy, ex, cy); |
---|
| 435 | if (toDraw & RightB) |
---|
| 436 | paint.drawLine(cx+2, cy+1, ex, cy+1); |
---|
| 437 | |
---|
| 438 | //Intersection points. |
---|
| 439 | if (toDraw & Int11) |
---|
| 440 | paint.drawPoint(cx-1, cy-1); |
---|
| 441 | if (toDraw & Int12) |
---|
| 442 | paint.drawPoint(cx, cy-1); |
---|
| 443 | if (toDraw & Int13) |
---|
| 444 | paint.drawPoint(cx+1, cy-1); |
---|
| 445 | |
---|
| 446 | if (toDraw & Int21) |
---|
| 447 | paint.drawPoint(cx-1, cy); |
---|
| 448 | if (toDraw & Int22) |
---|
| 449 | paint.drawPoint(cx, cy); |
---|
| 450 | if (toDraw & Int23) |
---|
| 451 | paint.drawPoint(cx+1, cy); |
---|
| 452 | |
---|
| 453 | if (toDraw & Int31) |
---|
| 454 | paint.drawPoint(cx-1, cy+1); |
---|
| 455 | if (toDraw & Int32) |
---|
| 456 | paint.drawPoint(cx, cy+1); |
---|
| 457 | if (toDraw & Int33) |
---|
| 458 | paint.drawPoint(cx+1, cy+1); |
---|
| 459 | |
---|
| 460 | } |
---|
| 461 | |
---|
| 462 | void TerminalDisplay::drawLineCharString( QPainter& painter, int x, int y, const QString& str, |
---|
| 463 | const Character* attributes) |
---|
| 464 | { |
---|
| 465 | const QPen& currentPen = painter.pen(); |
---|
| 466 | |
---|
| 467 | if ( attributes->rendition & RE_BOLD ) |
---|
| 468 | { |
---|
| 469 | QPen boldPen(currentPen); |
---|
| 470 | boldPen.setWidth(3); |
---|
| 471 | painter.setPen( boldPen ); |
---|
| 472 | } |
---|
| 473 | |
---|
| 474 | for (int i=0 ; i < str.length(); i++) |
---|
| 475 | { |
---|
| 476 | uchar code = str[i].cell(); |
---|
| 477 | if (LineChars[code]) |
---|
| 478 | drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code); |
---|
| 479 | } |
---|
| 480 | |
---|
| 481 | painter.setPen( currentPen ); |
---|
| 482 | } |
---|
| 483 | |
---|
| 484 | void TerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape) |
---|
| 485 | { |
---|
| 486 | _cursorShape = shape; |
---|
| 487 | } |
---|
| 488 | TerminalDisplay::KeyboardCursorShape TerminalDisplay::keyboardCursorShape() const |
---|
| 489 | { |
---|
| 490 | return _cursorShape; |
---|
| 491 | } |
---|
| 492 | void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color) |
---|
| 493 | { |
---|
| 494 | if (useForegroundColor) |
---|
| 495 | _cursorColor = QColor(); // an invalid color means that |
---|
| 496 | // the foreground color of the |
---|
| 497 | // current character should |
---|
| 498 | // be used |
---|
| 499 | |
---|
| 500 | else |
---|
| 501 | _cursorColor = color; |
---|
| 502 | } |
---|
| 503 | QColor TerminalDisplay::keyboardCursorColor() const |
---|
| 504 | { |
---|
| 505 | return _cursorColor; |
---|
| 506 | } |
---|
| 507 | |
---|
| 508 | void TerminalDisplay::setOpacity(qreal opacity) |
---|
| 509 | { |
---|
| 510 | QColor color(_blendColor); |
---|
| 511 | color.setAlphaF(opacity); |
---|
| 512 | |
---|
| 513 | // enable automatic background filling to prevent the display |
---|
| 514 | // flickering if there is no transparency |
---|
| 515 | if ( color.alpha() == 255 ) |
---|
| 516 | { |
---|
| 517 | setAutoFillBackground(true); |
---|
| 518 | } |
---|
| 519 | else |
---|
| 520 | { |
---|
| 521 | setAutoFillBackground(false); |
---|
| 522 | } |
---|
| 523 | |
---|
| 524 | _blendColor = color.rgba(); |
---|
| 525 | } |
---|
| 526 | |
---|
| 527 | void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting ) |
---|
| 528 | { |
---|
| 529 | // the area of the widget showing the contents of the terminal display is drawn |
---|
| 530 | // using the background color from the color scheme set with setColorTable() |
---|
| 531 | // |
---|
| 532 | // the area of the widget behind the scroll-bar is drawn using the background |
---|
| 533 | // brush from the scroll-bar's palette, to give the effect of the scroll-bar |
---|
| 534 | // being outside of the terminal display and visual consistency with other KDE |
---|
| 535 | // applications. |
---|
| 536 | // |
---|
| 537 | QRect scrollBarArea = _scrollBar->isVisible() ? |
---|
| 538 | rect.intersected(_scrollBar->geometry()) : |
---|
| 539 | QRect(); |
---|
| 540 | QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea); |
---|
| 541 | QRect contentsRect = contentsRegion.boundingRect(); |
---|
| 542 | |
---|
| 543 | if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting ) |
---|
| 544 | { |
---|
| 545 | QColor color(backgroundColor); |
---|
| 546 | color.setAlpha(qAlpha(_blendColor)); |
---|
| 547 | |
---|
| 548 | painter.save(); |
---|
| 549 | painter.setCompositionMode(QPainter::CompositionMode_Source); |
---|
| 550 | painter.fillRect(contentsRect, color); |
---|
| 551 | painter.restore(); |
---|
| 552 | } |
---|
| 553 | else { |
---|
| 554 | painter.fillRect(contentsRect, backgroundColor); |
---|
| 555 | } |
---|
| 556 | |
---|
| 557 | painter.fillRect(scrollBarArea,_scrollBar->palette().background()); |
---|
| 558 | } |
---|
| 559 | |
---|
| 560 | void TerminalDisplay::drawCursor(QPainter& painter, |
---|
| 561 | const QRect& rect, |
---|
| 562 | const QColor& foregroundColor, |
---|
| 563 | const QColor& /*backgroundColor*/, |
---|
| 564 | bool& invertCharacterColor) |
---|
| 565 | { |
---|
| 566 | QRect cursorRect = rect; |
---|
| 567 | cursorRect.setHeight(_fontHeight - _lineSpacing - 1); |
---|
| 568 | |
---|
| 569 | if (!_cursorBlinking) |
---|
| 570 | { |
---|
| 571 | if ( _cursorColor.isValid() ) |
---|
| 572 | painter.setPen(_cursorColor); |
---|
| 573 | else { |
---|
| 574 | painter.setPen(foregroundColor); |
---|
| 575 | } |
---|
| 576 | |
---|
| 577 | if ( _cursorShape == BlockCursor ) |
---|
| 578 | { |
---|
| 579 | // draw the cursor outline, adjusting the area so that |
---|
| 580 | // it is draw entirely inside 'rect' |
---|
| 581 | int penWidth = qMax(1,painter.pen().width()); |
---|
| 582 | |
---|
| 583 | painter.drawRect(cursorRect.adjusted(penWidth/2, |
---|
| 584 | penWidth/2, |
---|
| 585 | - penWidth/2 - penWidth%2, |
---|
| 586 | - penWidth/2 - penWidth%2)); |
---|
| 587 | if ( hasFocus() ) |
---|
| 588 | { |
---|
| 589 | painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor); |
---|
| 590 | |
---|
| 591 | if ( !_cursorColor.isValid() ) |
---|
| 592 | { |
---|
| 593 | // invert the colour used to draw the text to ensure that the character at |
---|
| 594 | // the cursor position is readable |
---|
| 595 | invertCharacterColor = true; |
---|
| 596 | } |
---|
| 597 | } |
---|
| 598 | } |
---|
| 599 | else if ( _cursorShape == UnderlineCursor ) |
---|
| 600 | painter.drawLine(cursorRect.left(), |
---|
| 601 | cursorRect.bottom(), |
---|
| 602 | cursorRect.right(), |
---|
| 603 | cursorRect.bottom()); |
---|
| 604 | else if ( _cursorShape == IBeamCursor ) |
---|
| 605 | painter.drawLine(cursorRect.left(), |
---|
| 606 | cursorRect.top(), |
---|
| 607 | cursorRect.left(), |
---|
| 608 | cursorRect.bottom()); |
---|
| 609 | |
---|
| 610 | } |
---|
| 611 | } |
---|
| 612 | |
---|
| 613 | void TerminalDisplay::drawCharacters(QPainter& painter, |
---|
| 614 | const QRect& rect, |
---|
| 615 | const QString& text, |
---|
| 616 | const Character* style, |
---|
| 617 | bool invertCharacterColor) |
---|
| 618 | { |
---|
| 619 | // don't draw text which is currently blinking |
---|
| 620 | if ( _blinking && (style->rendition & RE_BLINK) ) |
---|
| 621 | return; |
---|
| 622 | |
---|
| 623 | // setup bold and underline |
---|
| 624 | bool useBold = style->rendition & RE_BOLD || style->isBold(_colorTable) || font().bold(); |
---|
| 625 | bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); |
---|
| 626 | |
---|
| 627 | QFont font = painter.font(); |
---|
| 628 | if ( font.bold() != useBold |
---|
| 629 | || font.underline() != useUnderline ) |
---|
| 630 | { |
---|
| 631 | font.setBold(useBold); |
---|
| 632 | font.setUnderline(useUnderline); |
---|
| 633 | painter.setFont(font); |
---|
| 634 | } |
---|
| 635 | |
---|
| 636 | const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor ); |
---|
| 637 | const QColor color = textColor.color(_colorTable); |
---|
| 638 | |
---|
| 639 | QPen pen = painter.pen(); |
---|
| 640 | if ( pen.color() != color ) |
---|
| 641 | { |
---|
| 642 | pen.setColor(color); |
---|
| 643 | painter.setPen(color); |
---|
| 644 | } |
---|
| 645 | // draw text |
---|
| 646 | if ( isLineCharString(text) ) { |
---|
| 647 | drawLineCharString(painter,rect.x(),rect.y(),text,style); |
---|
| 648 | } |
---|
| 649 | else |
---|
| 650 | { |
---|
| 651 | // the drawText(rect,flags,string) overload is used here with null flags |
---|
| 652 | // instead of drawText(rect,string) because the (rect,string) overload causes |
---|
| 653 | // the application's default layout direction to be used instead of |
---|
| 654 | // the widget-specific layout direction, which should always be |
---|
| 655 | // Qt::LeftToRight for this widget |
---|
| 656 | painter.drawText(rect,0,text); |
---|
| 657 | } |
---|
| 658 | } |
---|
| 659 | |
---|
| 660 | void TerminalDisplay::drawTextFragment(QPainter& painter , |
---|
| 661 | const QRect& rect, |
---|
| 662 | const QString& text, |
---|
| 663 | const Character* style) |
---|
| 664 | { |
---|
| 665 | painter.save(); |
---|
| 666 | |
---|
| 667 | // setup painter |
---|
| 668 | const QColor foregroundColor = style->foregroundColor.color(_colorTable); |
---|
| 669 | const QColor backgroundColor = style->backgroundColor.color(_colorTable); |
---|
| 670 | |
---|
| 671 | // draw background if different from the display's background color |
---|
| 672 | if ( backgroundColor != palette().background().color() ) |
---|
| 673 | drawBackground(painter,rect,backgroundColor, false /* do not use transparency */); |
---|
| 674 | |
---|
| 675 | // draw cursor shape if the current character is the cursor |
---|
| 676 | // this may alter the foreground and background colors |
---|
| 677 | bool invertCharacterColor = false; |
---|
| 678 | |
---|
| 679 | if ( style->rendition & RE_CURSOR ) |
---|
| 680 | drawCursor(painter,rect,foregroundColor,backgroundColor,invertCharacterColor); |
---|
| 681 | // draw text |
---|
| 682 | drawCharacters(painter,rect,text,style,invertCharacterColor); |
---|
| 683 | |
---|
| 684 | painter.restore(); |
---|
| 685 | } |
---|
| 686 | |
---|
| 687 | void TerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; } |
---|
| 688 | uint TerminalDisplay::randomSeed() const { return _randomSeed; } |
---|
| 689 | |
---|
| 690 | #if 0 |
---|
| 691 | /*! |
---|
| 692 | Set XIM Position |
---|
| 693 | */ |
---|
| 694 | void TerminalDisplay::setCursorPos(const int curx, const int cury) |
---|
| 695 | { |
---|
| 696 | QPoint tL = contentsRect().topLeft(); |
---|
| 697 | int tLx = tL.x(); |
---|
| 698 | int tLy = tL.y(); |
---|
| 699 | |
---|
| 700 | int xpos, ypos; |
---|
| 701 | ypos = _topMargin + tLy + _fontHeight*(cury-1) + _fontAscent; |
---|
| 702 | xpos = _leftMargin + tLx + _fontWidth*curx; |
---|
| 703 | //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ??? |
---|
| 704 | // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos); |
---|
| 705 | _cursorLine = cury; |
---|
| 706 | _cursorCol = curx; |
---|
| 707 | } |
---|
| 708 | #endif |
---|
| 709 | |
---|
| 710 | // scrolls the image by 'lines', down if lines > 0 or up otherwise. |
---|
| 711 | // |
---|
| 712 | // the terminal emulation keeps track of the scrolling of the character |
---|
| 713 | // image as it receives input, and when the view is updated, it calls scrollImage() |
---|
| 714 | // with the final scroll amount. this improves performance because scrolling the |
---|
| 715 | // display is much cheaper than re-rendering all the text for the |
---|
| 716 | // part of the image which has moved up or down. |
---|
| 717 | // Instead only new lines have to be drawn |
---|
| 718 | // |
---|
| 719 | // note: it is important that the area of the display which is |
---|
| 720 | // scrolled aligns properly with the character grid - |
---|
| 721 | // which has a top left point at (_leftMargin,_topMargin) , |
---|
| 722 | // a cell width of _fontWidth and a cell height of _fontHeight). |
---|
| 723 | void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) |
---|
| 724 | { |
---|
| 725 | // if the flow control warning is enabled this will interfere with the |
---|
| 726 | // scrolling optimisations and cause artifacts. the simple solution here |
---|
| 727 | // is to just disable the optimisation whilst it is visible |
---|
| 728 | if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() ) { |
---|
| 729 | return; |
---|
| 730 | } |
---|
| 731 | |
---|
| 732 | // constrain the region to the display |
---|
| 733 | // the bottom of the region is capped to the number of lines in the display's |
---|
| 734 | // internal image - 2, so that the height of 'region' is strictly less |
---|
| 735 | // than the height of the internal image. |
---|
| 736 | QRect region = screenWindowRegion; |
---|
| 737 | region.setBottom( qMin(region.bottom(),this->_lines-2) ); |
---|
| 738 | |
---|
| 739 | if ( lines == 0 |
---|
| 740 | || _image == 0 |
---|
| 741 | || !region.isValid() |
---|
| 742 | || (region.top() + abs(lines)) >= region.bottom() |
---|
| 743 | || this->_lines <= region.height() ) return; |
---|
| 744 | |
---|
| 745 | QRect scrollRect; |
---|
| 746 | |
---|
| 747 | void* firstCharPos = &_image[ region.top() * this->_columns ]; |
---|
| 748 | void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ]; |
---|
| 749 | |
---|
| 750 | int top = _topMargin + (region.top() * _fontHeight); |
---|
| 751 | int linesToMove = region.height() - abs(lines); |
---|
| 752 | int bytesToMove = linesToMove * |
---|
| 753 | this->_columns * |
---|
| 754 | sizeof(Character); |
---|
| 755 | |
---|
| 756 | Q_ASSERT( linesToMove > 0 ); |
---|
| 757 | Q_ASSERT( bytesToMove > 0 ); |
---|
| 758 | |
---|
| 759 | //scroll internal image |
---|
| 760 | if ( lines > 0 ) |
---|
| 761 | { |
---|
| 762 | // check that the memory areas that we are going to move are valid |
---|
| 763 | Q_ASSERT( (char*)lastCharPos + bytesToMove < |
---|
| 764 | (char*)(_image + (this->_lines * this->_columns)) ); |
---|
| 765 | |
---|
| 766 | Q_ASSERT( (lines*this->_columns) < _imageSize ); |
---|
| 767 | |
---|
| 768 | //scroll internal image down |
---|
| 769 | memmove( firstCharPos , lastCharPos , bytesToMove ); |
---|
| 770 | |
---|
| 771 | //set region of display to scroll, making sure that |
---|
| 772 | //the region aligns correctly to the character grid |
---|
| 773 | scrollRect = QRect( _leftMargin , top, |
---|
| 774 | this->_usedColumns * _fontWidth , |
---|
| 775 | linesToMove * _fontHeight ); |
---|
| 776 | } |
---|
| 777 | else |
---|
| 778 | { |
---|
| 779 | // check that the memory areas that we are going to move are valid |
---|
| 780 | Q_ASSERT( (char*)firstCharPos + bytesToMove < |
---|
| 781 | (char*)(_image + (this->_lines * this->_columns)) ); |
---|
| 782 | |
---|
| 783 | //scroll internal image up |
---|
| 784 | memmove( lastCharPos , firstCharPos , bytesToMove ); |
---|
| 785 | |
---|
| 786 | //set region of the display to scroll, making sure that |
---|
| 787 | //the region aligns correctly to the character grid |
---|
| 788 | QPoint topPoint( _leftMargin , top + abs(lines)*_fontHeight ); |
---|
| 789 | |
---|
| 790 | scrollRect = QRect( topPoint , |
---|
| 791 | QSize( this->_usedColumns*_fontWidth , |
---|
| 792 | linesToMove * _fontHeight )); |
---|
| 793 | } |
---|
| 794 | |
---|
| 795 | //scroll the display vertically to match internal _image |
---|
| 796 | scroll( 0 , _fontHeight * (-lines) , scrollRect ); |
---|
| 797 | } |
---|
| 798 | |
---|
| 799 | QRegion TerminalDisplay::hotSpotRegion() const |
---|
| 800 | { |
---|
| 801 | QRegion region; |
---|
| 802 | foreach( Filter::HotSpot* hotSpot , _filterChain->hotSpots() ) |
---|
| 803 | { |
---|
| 804 | QRect rect; |
---|
| 805 | rect.setLeft(hotSpot->startColumn()); |
---|
| 806 | rect.setTop(hotSpot->startLine()); |
---|
| 807 | rect.setRight(hotSpot->endColumn()); |
---|
| 808 | rect.setBottom(hotSpot->endLine()); |
---|
| 809 | |
---|
| 810 | region |= imageToWidget(rect); |
---|
| 811 | } |
---|
| 812 | return region; |
---|
| 813 | } |
---|
| 814 | |
---|
| 815 | void TerminalDisplay::processFilters() |
---|
| 816 | { |
---|
| 817 | if (!_screenWindow) |
---|
| 818 | return; |
---|
| 819 | |
---|
| 820 | QRegion preUpdateHotSpots = hotSpotRegion(); |
---|
| 821 | |
---|
| 822 | // use _screenWindow->getImage() here rather than _image because |
---|
| 823 | // other classes may call processFilters() when this display's |
---|
| 824 | // ScreenWindow emits a scrolled() signal - which will happen before |
---|
| 825 | // updateImage() is called on the display and therefore _image is |
---|
| 826 | // out of date at this point |
---|
| 827 | _filterChain->setImage( _screenWindow->getImage(), |
---|
| 828 | _screenWindow->windowLines(), |
---|
| 829 | _screenWindow->windowColumns(), |
---|
| 830 | _screenWindow->getLineProperties() ); |
---|
| 831 | _filterChain->process(); |
---|
| 832 | |
---|
| 833 | QRegion postUpdateHotSpots = hotSpotRegion(); |
---|
| 834 | |
---|
| 835 | update( preUpdateHotSpots | postUpdateHotSpots ); |
---|
| 836 | } |
---|
| 837 | |
---|
| 838 | void TerminalDisplay::updateImage() |
---|
| 839 | { |
---|
| 840 | if ( !_screenWindow ) |
---|
| 841 | return; |
---|
| 842 | |
---|
| 843 | // optimization - scroll the existing image where possible and |
---|
| 844 | // avoid expensive text drawing for parts of the image that |
---|
| 845 | // can simply be moved up or down |
---|
| 846 | scrollImage( _screenWindow->scrollCount() , |
---|
| 847 | _screenWindow->scrollRegion() ); |
---|
| 848 | _screenWindow->resetScrollCount(); |
---|
| 849 | |
---|
| 850 | Character* const newimg = _screenWindow->getImage(); |
---|
| 851 | int lines = _screenWindow->windowLines(); |
---|
| 852 | int columns = _screenWindow->windowColumns(); |
---|
| 853 | |
---|
| 854 | setScroll( _screenWindow->currentLine() , _screenWindow->lineCount() ); |
---|
| 855 | |
---|
| 856 | if (!_image) |
---|
| 857 | updateImageSize(); // Create _image |
---|
| 858 | |
---|
| 859 | Q_ASSERT( this->_usedLines <= this->_lines ); |
---|
| 860 | Q_ASSERT( this->_usedColumns <= this->_columns ); |
---|
| 861 | |
---|
| 862 | int y,x,len; |
---|
| 863 | |
---|
| 864 | QPoint tL = contentsRect().topLeft(); |
---|
| 865 | |
---|
| 866 | int tLx = tL.x(); |
---|
| 867 | int tLy = tL.y(); |
---|
| 868 | _hasBlinker = false; |
---|
| 869 | |
---|
| 870 | CharacterColor cf; // undefined |
---|
| 871 | CharacterColor _clipboard; // undefined |
---|
| 872 | int cr = -1; // undefined |
---|
| 873 | |
---|
| 874 | const int linesToUpdate = qMin(this->_lines, qMax(0,lines )); |
---|
| 875 | const int columnsToUpdate = qMin(this->_columns,qMax(0,columns)); |
---|
| 876 | |
---|
| 877 | QChar *disstrU = new QChar[columnsToUpdate]; |
---|
| 878 | char *dirtyMask = new char[columnsToUpdate+2]; |
---|
| 879 | QRegion dirtyRegion; |
---|
| 880 | |
---|
| 881 | // debugging variable, this records the number of lines that are found to |
---|
| 882 | // be 'dirty' ( ie. have changed from the old _image to the new _image ) and |
---|
| 883 | // which therefore need to be repainted |
---|
| 884 | int dirtyLineCount = 0; |
---|
| 885 | |
---|
| 886 | for (y = 0; y < linesToUpdate; y++) |
---|
| 887 | { |
---|
| 888 | const Character* currentLine = &_image[y*this->_columns]; |
---|
| 889 | const Character* const newLine = &newimg[y*columns]; |
---|
| 890 | |
---|
| 891 | bool updateLine = false; |
---|
| 892 | |
---|
| 893 | // The dirty mask indicates which characters need repainting. We also |
---|
| 894 | // mark surrounding neighbours dirty, in case the character exceeds |
---|
| 895 | // its cell boundaries |
---|
| 896 | memset(dirtyMask, 0, columnsToUpdate+2); |
---|
| 897 | |
---|
| 898 | for( x = 0 ; x < columnsToUpdate ; x++) |
---|
| 899 | { |
---|
| 900 | if ( newLine[x] != currentLine[x] ) |
---|
| 901 | { |
---|
| 902 | dirtyMask[x] = true; |
---|
| 903 | } |
---|
| 904 | } |
---|
| 905 | |
---|
| 906 | if (!_resizing) // not while _resizing, we're expecting a paintEvent |
---|
| 907 | for (x = 0; x < columnsToUpdate; x++) |
---|
| 908 | { |
---|
| 909 | _hasBlinker |= (newLine[x].rendition & RE_BLINK); |
---|
| 910 | |
---|
| 911 | // Start drawing if this character or the next one differs. |
---|
| 912 | // We also take the next one into account to handle the situation |
---|
| 913 | // where characters exceed their cell width. |
---|
| 914 | if (dirtyMask[x]) |
---|
| 915 | { |
---|
| 916 | quint16 c = newLine[x+0].character; |
---|
| 917 | if ( !c ) |
---|
| 918 | continue; |
---|
| 919 | int p = 0; |
---|
| 920 | disstrU[p++] = c; //fontMap(c); |
---|
| 921 | bool lineDraw = isLineChar(c); |
---|
| 922 | bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0); |
---|
| 923 | cr = newLine[x].rendition; |
---|
| 924 | _clipboard = newLine[x].backgroundColor; |
---|
| 925 | if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor; |
---|
| 926 | int lln = columnsToUpdate - x; |
---|
| 927 | for (len = 1; len < lln; len++) |
---|
| 928 | { |
---|
| 929 | const Character& ch = newLine[x+len]; |
---|
| 930 | |
---|
| 931 | if (!ch.character) |
---|
| 932 | continue; // Skip trailing part of multi-col chars. |
---|
| 933 | |
---|
| 934 | bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0); |
---|
| 935 | |
---|
| 936 | if ( ch.foregroundColor != cf || |
---|
| 937 | ch.backgroundColor != _clipboard || |
---|
| 938 | ch.rendition != cr || |
---|
| 939 | !dirtyMask[x+len] || |
---|
| 940 | isLineChar(c) != lineDraw || |
---|
| 941 | nextIsDoubleWidth != doubleWidth ) |
---|
| 942 | break; |
---|
| 943 | |
---|
| 944 | disstrU[p++] = c; //fontMap(c); |
---|
| 945 | } |
---|
| 946 | |
---|
| 947 | QString unistr(disstrU, p); |
---|
| 948 | |
---|
| 949 | bool saveFixedFont = _fixedFont; |
---|
| 950 | if (lineDraw) |
---|
| 951 | _fixedFont = false; |
---|
| 952 | if (doubleWidth) |
---|
| 953 | _fixedFont = false; |
---|
| 954 | |
---|
| 955 | updateLine = true; |
---|
| 956 | |
---|
| 957 | _fixedFont = saveFixedFont; |
---|
| 958 | x += len - 1; |
---|
| 959 | } |
---|
| 960 | |
---|
| 961 | } |
---|
| 962 | |
---|
| 963 | //both the top and bottom halves of double height _lines must always be redrawn |
---|
| 964 | //although both top and bottom halves contain the same characters, only |
---|
| 965 | //the top one is actually |
---|
| 966 | //drawn. |
---|
| 967 | if (_lineProperties.count() > y) |
---|
| 968 | updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT); |
---|
| 969 | |
---|
| 970 | // if the characters on the line are different in the old and the new _image |
---|
| 971 | // then this line must be repainted. |
---|
| 972 | if (updateLine) |
---|
| 973 | { |
---|
| 974 | dirtyLineCount++; |
---|
| 975 | |
---|
| 976 | // add the area occupied by this line to the region which needs to be |
---|
| 977 | // repainted |
---|
| 978 | QRect dirtyRect = QRect( _leftMargin+tLx , |
---|
| 979 | _topMargin+tLy+_fontHeight*y , |
---|
| 980 | _fontWidth * columnsToUpdate , |
---|
| 981 | _fontHeight ); |
---|
| 982 | |
---|
| 983 | dirtyRegion |= dirtyRect; |
---|
| 984 | } |
---|
| 985 | |
---|
| 986 | // replace the line of characters in the old _image with the |
---|
| 987 | // current line of the new _image |
---|
| 988 | memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character)); |
---|
| 989 | } |
---|
| 990 | |
---|
| 991 | // if the new _image is smaller than the previous _image, then ensure that the area |
---|
| 992 | // outside the new _image is cleared |
---|
| 993 | if ( linesToUpdate < _usedLines ) |
---|
| 994 | { |
---|
| 995 | dirtyRegion |= QRect( _leftMargin+tLx , |
---|
| 996 | _topMargin+tLy+_fontHeight*linesToUpdate , |
---|
| 997 | _fontWidth * this->_columns , |
---|
| 998 | _fontHeight * (_usedLines-linesToUpdate) ); |
---|
| 999 | } |
---|
| 1000 | _usedLines = linesToUpdate; |
---|
| 1001 | |
---|
| 1002 | if ( columnsToUpdate < _usedColumns ) |
---|
| 1003 | { |
---|
| 1004 | dirtyRegion |= QRect( _leftMargin+tLx+columnsToUpdate*_fontWidth , |
---|
| 1005 | _topMargin+tLy , |
---|
| 1006 | _fontWidth * (_usedColumns-columnsToUpdate) , |
---|
| 1007 | _fontHeight * this->_lines ); |
---|
| 1008 | } |
---|
| 1009 | _usedColumns = columnsToUpdate; |
---|
| 1010 | |
---|
| 1011 | dirtyRegion |= _inputMethodData.previousPreeditRect; |
---|
| 1012 | |
---|
| 1013 | // update the parts of the display which have changed |
---|
| 1014 | update(dirtyRegion); |
---|
| 1015 | |
---|
| 1016 | if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( BLINK_DELAY ); |
---|
| 1017 | if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; } |
---|
| 1018 | delete[] dirtyMask; |
---|
| 1019 | delete[] disstrU; |
---|
| 1020 | |
---|
| 1021 | } |
---|
| 1022 | |
---|
| 1023 | void TerminalDisplay::showResizeNotification() |
---|
| 1024 | { |
---|
| 1025 | if (_terminalSizeHint && isVisible()) |
---|
| 1026 | { |
---|
| 1027 | if (_terminalSizeStartup) { |
---|
| 1028 | _terminalSizeStartup=false; |
---|
| 1029 | return; |
---|
| 1030 | } |
---|
| 1031 | if (!_resizeWidget) |
---|
| 1032 | { |
---|
| 1033 | _resizeWidget = new QLabel(("Size: XXX x XXX"), this); |
---|
| 1034 | _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(("Size: XXX x XXX"))); |
---|
| 1035 | _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); |
---|
| 1036 | _resizeWidget->setAlignment(Qt::AlignCenter); |
---|
| 1037 | |
---|
| 1038 | _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)"); |
---|
| 1039 | |
---|
| 1040 | _resizeTimer = new QTimer(this); |
---|
| 1041 | _resizeTimer->setSingleShot(true); |
---|
| 1042 | connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); |
---|
| 1043 | |
---|
| 1044 | } |
---|
| 1045 | QString sizeStr; |
---|
| 1046 | sizeStr.sprintf("Size: %d x %d", _columns, _lines); |
---|
| 1047 | _resizeWidget->setText(sizeStr); |
---|
| 1048 | _resizeWidget->move((width()-_resizeWidget->width())/2, |
---|
| 1049 | (height()-_resizeWidget->height())/2+20); |
---|
| 1050 | _resizeWidget->show(); |
---|
| 1051 | _resizeTimer->start(1000); |
---|
| 1052 | } |
---|
| 1053 | } |
---|
| 1054 | |
---|
| 1055 | void TerminalDisplay::setBlinkingCursor(bool blink) |
---|
| 1056 | { |
---|
| 1057 | _hasBlinkingCursor=blink; |
---|
| 1058 | |
---|
| 1059 | if (blink && !_blinkCursorTimer->isActive()) |
---|
| 1060 | _blinkCursorTimer->start(BLINK_DELAY); |
---|
| 1061 | |
---|
| 1062 | if (!blink && _blinkCursorTimer->isActive()) |
---|
| 1063 | { |
---|
| 1064 | _blinkCursorTimer->stop(); |
---|
| 1065 | if (_cursorBlinking) |
---|
| 1066 | blinkCursorEvent(); |
---|
| 1067 | else |
---|
| 1068 | _cursorBlinking = false; |
---|
| 1069 | } |
---|
| 1070 | } |
---|
| 1071 | |
---|
| 1072 | void TerminalDisplay::paintEvent( QPaintEvent* pe ) |
---|
| 1073 | { |
---|
| 1074 | //qDebug("%s %d paintEvent", __FILE__, __LINE__); |
---|
| 1075 | QPainter paint(this); |
---|
| 1076 | |
---|
| 1077 | foreach (QRect rect, (pe->region() & contentsRect()).rects()) |
---|
| 1078 | { |
---|
| 1079 | drawBackground(paint,rect,palette().background().color(), true /* use opacity setting */); |
---|
| 1080 | drawContents(paint, rect); |
---|
| 1081 | } |
---|
| 1082 | // drawBackground(paint,contentsRect(),palette().background().color(), true /* use opacity setting */); |
---|
| 1083 | // drawContents(paint, contentsRect()); |
---|
| 1084 | drawInputMethodPreeditString(paint,preeditRect()); |
---|
| 1085 | paintFilters(paint); |
---|
| 1086 | |
---|
| 1087 | paint.end(); |
---|
| 1088 | } |
---|
| 1089 | |
---|
| 1090 | QPoint TerminalDisplay::cursorPosition() const |
---|
| 1091 | { |
---|
| 1092 | if (_screenWindow) |
---|
| 1093 | return _screenWindow->cursorPosition(); |
---|
| 1094 | else |
---|
| 1095 | return QPoint(0,0); |
---|
| 1096 | } |
---|
| 1097 | |
---|
| 1098 | QRect TerminalDisplay::preeditRect() const |
---|
| 1099 | { |
---|
| 1100 | const int preeditLength = string_width(_inputMethodData.preeditString); |
---|
| 1101 | |
---|
| 1102 | if ( preeditLength == 0 ) |
---|
| 1103 | return QRect(); |
---|
| 1104 | |
---|
| 1105 | return QRect(_leftMargin + _fontWidth*cursorPosition().x(), |
---|
| 1106 | _topMargin + _fontHeight*cursorPosition().y(), |
---|
| 1107 | _fontWidth*preeditLength, |
---|
| 1108 | _fontHeight); |
---|
| 1109 | } |
---|
| 1110 | |
---|
| 1111 | void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect) |
---|
| 1112 | { |
---|
| 1113 | if ( _inputMethodData.preeditString.isEmpty() ) { |
---|
| 1114 | return; |
---|
| 1115 | } |
---|
| 1116 | const QPoint cursorPos = cursorPosition(); |
---|
| 1117 | |
---|
| 1118 | bool invertColors = false; |
---|
| 1119 | const QColor background = _colorTable[DEFAULT_BACK_COLOR].color; |
---|
| 1120 | const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color; |
---|
| 1121 | const Character* style = &_image[loc(cursorPos.x(),cursorPos.y())]; |
---|
| 1122 | |
---|
| 1123 | drawBackground(painter,rect,background,true); |
---|
| 1124 | drawCursor(painter,rect,foreground,background,invertColors); |
---|
| 1125 | drawCharacters(painter,rect,_inputMethodData.preeditString,style,invertColors); |
---|
| 1126 | |
---|
| 1127 | _inputMethodData.previousPreeditRect = rect; |
---|
| 1128 | } |
---|
| 1129 | |
---|
| 1130 | FilterChain* TerminalDisplay::filterChain() const |
---|
| 1131 | { |
---|
| 1132 | return _filterChain; |
---|
| 1133 | } |
---|
| 1134 | |
---|
| 1135 | void TerminalDisplay::paintFilters(QPainter& painter) |
---|
| 1136 | { |
---|
| 1137 | //qDebug("%s %d paintFilters", __FILE__, __LINE__); |
---|
| 1138 | |
---|
| 1139 | // get color of character under mouse and use it to draw |
---|
| 1140 | // lines for filters |
---|
| 1141 | QPoint cursorPos = mapFromGlobal(QCursor::pos()); |
---|
| 1142 | int cursorLine; |
---|
| 1143 | int cursorColumn; |
---|
| 1144 | getCharacterPosition( cursorPos , cursorLine , cursorColumn ); |
---|
| 1145 | Character cursorCharacter = _image[loc(cursorColumn,cursorLine)]; |
---|
| 1146 | |
---|
| 1147 | painter.setPen( QPen(cursorCharacter.foregroundColor.color(colorTable())) ); |
---|
| 1148 | |
---|
| 1149 | // iterate over hotspots identified by the display's currently active filters |
---|
| 1150 | // and draw appropriate visuals to indicate the presence of the hotspot |
---|
| 1151 | |
---|
| 1152 | QList<Filter::HotSpot*> spots = _filterChain->hotSpots(); |
---|
| 1153 | QListIterator<Filter::HotSpot*> iter(spots); |
---|
| 1154 | while (iter.hasNext()) |
---|
| 1155 | { |
---|
| 1156 | Filter::HotSpot* spot = iter.next(); |
---|
| 1157 | |
---|
| 1158 | for ( int line = spot->startLine() ; line <= spot->endLine() ; line++ ) |
---|
| 1159 | { |
---|
| 1160 | int startColumn = 0; |
---|
| 1161 | int endColumn = _columns-1; // TODO use number of _columns which are actually |
---|
| 1162 | // occupied on this line rather than the width of the |
---|
| 1163 | // display in _columns |
---|
| 1164 | |
---|
| 1165 | // ignore whitespace at the end of the lines |
---|
| 1166 | while ( QChar(_image[loc(endColumn,line)].character).isSpace() && endColumn > 0 ) |
---|
| 1167 | endColumn--; |
---|
| 1168 | |
---|
| 1169 | // increment here because the column which we want to set 'endColumn' to |
---|
| 1170 | // is the first whitespace character at the end of the line |
---|
| 1171 | endColumn++; |
---|
| 1172 | |
---|
| 1173 | if ( line == spot->startLine() ) |
---|
| 1174 | startColumn = spot->startColumn(); |
---|
| 1175 | if ( line == spot->endLine() ) |
---|
| 1176 | endColumn = spot->endColumn(); |
---|
| 1177 | |
---|
| 1178 | // subtract one pixel from |
---|
| 1179 | // the right and bottom so that |
---|
| 1180 | // we do not overdraw adjacent |
---|
| 1181 | // hotspots |
---|
| 1182 | // |
---|
| 1183 | // subtracting one pixel from all sides also prevents an edge case where |
---|
| 1184 | // moving the mouse outside a link could still leave it underlined |
---|
| 1185 | // because the check below for the position of the cursor |
---|
| 1186 | // finds it on the border of the target area |
---|
| 1187 | QRect r; |
---|
| 1188 | r.setCoords( startColumn*_fontWidth + 1, line*_fontHeight + 1, |
---|
| 1189 | endColumn*_fontWidth - 1, (line+1)*_fontHeight - 1 ); |
---|
| 1190 | |
---|
| 1191 | // Underline link hotspots |
---|
| 1192 | if ( spot->type() == Filter::HotSpot::Link ) |
---|
| 1193 | { |
---|
| 1194 | QFontMetrics metrics(font()); |
---|
| 1195 | |
---|
| 1196 | // find the baseline (which is the invisible line that the characters in the font sit on, |
---|
| 1197 | // with some having tails dangling below) |
---|
| 1198 | int baseline = r.bottom() - metrics.descent(); |
---|
| 1199 | // find the position of the underline below that |
---|
| 1200 | int underlinePos = baseline + metrics.underlinePos(); |
---|
| 1201 | |
---|
| 1202 | if ( r.contains( mapFromGlobal(QCursor::pos()) ) ) |
---|
| 1203 | painter.drawLine( r.left() , underlinePos , |
---|
| 1204 | r.right() , underlinePos ); |
---|
| 1205 | } |
---|
| 1206 | // Marker hotspots simply have a transparent rectanglular shape |
---|
| 1207 | // drawn on top of them |
---|
| 1208 | else if ( spot->type() == Filter::HotSpot::Marker ) |
---|
| 1209 | { |
---|
| 1210 | //TODO - Do not use a hardcoded colour for this |
---|
| 1211 | painter.fillRect(r,QBrush(QColor(255,0,0,120))); |
---|
| 1212 | } |
---|
| 1213 | } |
---|
| 1214 | } |
---|
| 1215 | } |
---|
| 1216 | void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) |
---|
| 1217 | { |
---|
| 1218 | //qDebug("%s %d drawContents and rect x=%d y=%d w=%d h=%d", __FILE__, __LINE__, rect.x(), rect.y(),rect.width(),rect.height()); |
---|
| 1219 | |
---|
| 1220 | QPoint tL = contentsRect().topLeft(); |
---|
| 1221 | // int tLx = tL.x(); |
---|
| 1222 | int tLy = tL.y(); |
---|
| 1223 | |
---|
| 1224 | int tLx = (_contentWidth - _usedColumns * _fontWidth)/2; |
---|
| 1225 | // int tLy = (_contentHeight - _usedLines * _fontHeight)/2; |
---|
| 1226 | //qDebug("%d %d %d %d", tLx, tLy, _contentWidth, _usedColumns * _fontWidth); |
---|
| 1227 | |
---|
| 1228 | int lux = qMin(_usedColumns-1, qMax(0,(rect.left() - tLx - _leftMargin ) / _fontWidth)); |
---|
| 1229 | int luy = qMin(_usedLines-1, qMax(0, (rect.top() - tLy - _topMargin ) / _fontHeight)); |
---|
| 1230 | int rlx = qMin(_usedColumns-1, qMax(0, (rect.right() - tLx - _leftMargin ) / _fontWidth)); |
---|
| 1231 | int rly = qMin(_usedLines-1, qMax(0, (rect.bottom() - tLy - _topMargin ) / _fontHeight)); |
---|
| 1232 | |
---|
| 1233 | const int bufferSize = _usedColumns; |
---|
| 1234 | QChar *disstrU = new QChar[bufferSize]; |
---|
| 1235 | for (int y = luy; y <= rly; y++) |
---|
| 1236 | { |
---|
| 1237 | quint16 c = _image[loc(lux,y)].character; |
---|
| 1238 | int x = lux; |
---|
| 1239 | if(!c && x) |
---|
| 1240 | x--; // Search for start of multi-column character |
---|
| 1241 | for (; x <= rlx; x++) |
---|
| 1242 | { |
---|
| 1243 | int len = 1; |
---|
| 1244 | int p = 0; |
---|
| 1245 | |
---|
| 1246 | // is this a single character or a sequence of characters ? |
---|
| 1247 | if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR ) |
---|
| 1248 | { |
---|
| 1249 | // sequence of characters |
---|
| 1250 | ushort extendedCharLength = 0; |
---|
| 1251 | ushort* chars = ExtendedCharTable::instance |
---|
| 1252 | .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength); |
---|
| 1253 | for ( int index = 0 ; index < extendedCharLength ; index++ ) |
---|
| 1254 | { |
---|
| 1255 | Q_ASSERT( p < bufferSize ); |
---|
| 1256 | disstrU[p++] = chars[index]; |
---|
| 1257 | } |
---|
| 1258 | } |
---|
| 1259 | else |
---|
| 1260 | { |
---|
| 1261 | // single character |
---|
| 1262 | c = _image[loc(x,y)].character; |
---|
| 1263 | if (c) |
---|
| 1264 | { |
---|
| 1265 | Q_ASSERT( p < bufferSize ); |
---|
| 1266 | disstrU[p++] = c; //fontMap(c); |
---|
| 1267 | } |
---|
| 1268 | } |
---|
| 1269 | |
---|
| 1270 | bool lineDraw = isLineChar(c); |
---|
| 1271 | bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0); |
---|
| 1272 | CharacterColor currentForeground = _image[loc(x,y)].foregroundColor; |
---|
| 1273 | CharacterColor currentBackground = _image[loc(x,y)].backgroundColor; |
---|
| 1274 | quint8 currentRendition = _image[loc(x,y)].rendition; |
---|
| 1275 | |
---|
| 1276 | while (x+len <= rlx && |
---|
| 1277 | _image[loc(x+len,y)].foregroundColor == currentForeground && |
---|
| 1278 | _image[loc(x+len,y)].backgroundColor == currentBackground && |
---|
| 1279 | _image[loc(x+len,y)].rendition == currentRendition && |
---|
| 1280 | (_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth && |
---|
| 1281 | isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment! |
---|
| 1282 | { |
---|
| 1283 | if (c) |
---|
| 1284 | disstrU[p++] = c; //fontMap(c); |
---|
| 1285 | if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition |
---|
| 1286 | len++; // Skip trailing part of multi-column character |
---|
| 1287 | len++; |
---|
| 1288 | } |
---|
| 1289 | if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character)) |
---|
| 1290 | len++; // Adjust for trailing part of multi-column character |
---|
| 1291 | |
---|
| 1292 | bool save__fixedFont = _fixedFont; |
---|
| 1293 | if (lineDraw) |
---|
| 1294 | _fixedFont = false; |
---|
| 1295 | if (doubleWidth) |
---|
| 1296 | _fixedFont = false; |
---|
| 1297 | QString unistr(disstrU,p); |
---|
| 1298 | |
---|
| 1299 | if (y < _lineProperties.size()) |
---|
| 1300 | { |
---|
| 1301 | if (_lineProperties[y] & LINE_DOUBLEWIDTH) { |
---|
| 1302 | paint.scale(2,1); |
---|
| 1303 | } |
---|
| 1304 | |
---|
| 1305 | if (_lineProperties[y] & LINE_DOUBLEHEIGHT) { |
---|
| 1306 | paint.scale(1,2); |
---|
| 1307 | } |
---|
| 1308 | } |
---|
| 1309 | |
---|
| 1310 | //calculate the area in which the text will be drawn |
---|
| 1311 | QRect textArea = QRect( _leftMargin+tLx+_fontWidth*x , |
---|
| 1312 | _topMargin+tLy+_fontHeight*y , |
---|
| 1313 | _fontWidth*len, |
---|
| 1314 | _fontHeight); |
---|
| 1315 | |
---|
| 1316 | //move the calculated area to take account of scaling applied to the painter. |
---|
| 1317 | //the position of the area from the origin (0,0) is scaled |
---|
| 1318 | //by the opposite of whatever |
---|
| 1319 | //transformation has been applied to the painter. this ensures that |
---|
| 1320 | //painting does actually start from textArea.topLeft() |
---|
| 1321 | //(instead of textArea.topLeft() * painter-scale) |
---|
| 1322 | QMatrix inverted = paint.matrix().inverted(); |
---|
| 1323 | // textArea.moveTopLeft( inverted.map(textArea.topLeft()) ); |
---|
| 1324 | textArea.moveCenter( inverted.map(textArea.center()) ); |
---|
| 1325 | |
---|
| 1326 | |
---|
| 1327 | //paint text fragment |
---|
| 1328 | drawTextFragment( paint, |
---|
| 1329 | textArea, |
---|
| 1330 | unistr, |
---|
| 1331 | &_image[loc(x,y)] ); //, |
---|
| 1332 | //0, |
---|
| 1333 | //!_isPrinting ); |
---|
| 1334 | |
---|
| 1335 | _fixedFont = save__fixedFont; |
---|
| 1336 | |
---|
| 1337 | //reset back to single-width, single-height _lines |
---|
| 1338 | paint.resetMatrix(); |
---|
| 1339 | |
---|
| 1340 | if (y < _lineProperties.size()-1) |
---|
| 1341 | { |
---|
| 1342 | //double-height _lines are represented by two adjacent _lines |
---|
| 1343 | //containing the same characters |
---|
| 1344 | //both _lines will have the LINE_DOUBLEHEIGHT attribute. |
---|
| 1345 | //If the current line has the LINE_DOUBLEHEIGHT attribute, |
---|
| 1346 | //we can therefore skip the next line |
---|
| 1347 | if (_lineProperties[y] & LINE_DOUBLEHEIGHT) |
---|
| 1348 | y++; |
---|
| 1349 | } |
---|
| 1350 | |
---|
| 1351 | x += len - 1; |
---|
| 1352 | } |
---|
| 1353 | } |
---|
| 1354 | delete [] disstrU; |
---|
| 1355 | } |
---|
| 1356 | |
---|
| 1357 | void TerminalDisplay::blinkEvent() |
---|
| 1358 | { |
---|
| 1359 | _blinking = !_blinking; |
---|
| 1360 | |
---|
| 1361 | //TODO: Optimise to only repaint the areas of the widget |
---|
| 1362 | // where there is blinking text |
---|
| 1363 | // rather than repainting the whole widget. |
---|
| 1364 | update(); |
---|
| 1365 | } |
---|
| 1366 | |
---|
| 1367 | QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const |
---|
| 1368 | { |
---|
| 1369 | //qDebug("%s %d imageToWidget", __FILE__, __LINE__); |
---|
| 1370 | QRect result; |
---|
| 1371 | result.setLeft( _leftMargin + _fontWidth * imageArea.left() ); |
---|
| 1372 | result.setTop( _topMargin + _fontHeight * imageArea.top() ); |
---|
| 1373 | result.setWidth( _fontWidth * imageArea.width() ); |
---|
| 1374 | result.setHeight( _fontHeight * imageArea.height() ); |
---|
| 1375 | |
---|
| 1376 | return result; |
---|
| 1377 | } |
---|
| 1378 | |
---|
| 1379 | void TerminalDisplay::blinkCursorEvent() |
---|
| 1380 | { |
---|
| 1381 | _cursorBlinking = !_cursorBlinking; |
---|
| 1382 | |
---|
| 1383 | QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) ); |
---|
| 1384 | |
---|
| 1385 | update(cursorRect); |
---|
| 1386 | } |
---|
| 1387 | |
---|
| 1388 | /* ------------------------------------------------------------------------- */ |
---|
| 1389 | /* */ |
---|
| 1390 | /* Resizing */ |
---|
| 1391 | /* */ |
---|
| 1392 | /* ------------------------------------------------------------------------- */ |
---|
| 1393 | |
---|
| 1394 | void TerminalDisplay::resizeEvent(QResizeEvent*) |
---|
| 1395 | { |
---|
| 1396 | updateImageSize(); |
---|
| 1397 | } |
---|
| 1398 | |
---|
| 1399 | void TerminalDisplay::propagateSize() |
---|
| 1400 | { |
---|
| 1401 | if (_isFixedSize) |
---|
| 1402 | { |
---|
| 1403 | setSize(_columns, _lines); |
---|
| 1404 | QWidget::setFixedSize(sizeHint()); |
---|
| 1405 | parentWidget()->adjustSize(); |
---|
| 1406 | parentWidget()->setFixedSize(parentWidget()->sizeHint()); |
---|
| 1407 | return; |
---|
| 1408 | } |
---|
| 1409 | if (_image) |
---|
| 1410 | updateImageSize(); |
---|
| 1411 | } |
---|
| 1412 | |
---|
| 1413 | void TerminalDisplay::updateImageSize() |
---|
| 1414 | { |
---|
| 1415 | //qDebug("%s %d updateImageSize", __FILE__, __LINE__); |
---|
| 1416 | Character* oldimg = _image; |
---|
| 1417 | int oldlin = _lines; |
---|
| 1418 | int oldcol = _columns; |
---|
| 1419 | |
---|
| 1420 | makeImage(); |
---|
| 1421 | |
---|
| 1422 | |
---|
| 1423 | // copy the old image to reduce flicker |
---|
| 1424 | int lines = qMin(oldlin,_lines); |
---|
| 1425 | int columns = qMin(oldcol,_columns); |
---|
| 1426 | |
---|
| 1427 | if (oldimg) |
---|
| 1428 | { |
---|
| 1429 | for (int line = 0; line < lines; line++) |
---|
| 1430 | { |
---|
| 1431 | memcpy((void*)&_image[_columns*line], |
---|
| 1432 | (void*)&oldimg[oldcol*line],columns*sizeof(Character)); |
---|
| 1433 | } |
---|
| 1434 | delete[] oldimg; |
---|
| 1435 | } |
---|
| 1436 | |
---|
| 1437 | if (_screenWindow) |
---|
| 1438 | _screenWindow->setWindowLines(_lines); |
---|
| 1439 | |
---|
| 1440 | _resizing = (oldlin!=_lines) || (oldcol!=_columns); |
---|
| 1441 | |
---|
| 1442 | if ( _resizing ) |
---|
| 1443 | { |
---|
| 1444 | showResizeNotification(); |
---|
| 1445 | emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent |
---|
| 1446 | } |
---|
| 1447 | |
---|
| 1448 | _resizing = false; |
---|
| 1449 | } |
---|
| 1450 | |
---|
| 1451 | //showEvent and hideEvent are reimplemented here so that it appears to other classes that the |
---|
| 1452 | //display has been resized when the display is hidden or shown. |
---|
| 1453 | // |
---|
| 1454 | //this allows |
---|
| 1455 | //TODO: Perhaps it would be better to have separate signals for show and hide instead of using |
---|
| 1456 | //the same signal as the one for a content size change |
---|
| 1457 | void TerminalDisplay::showEvent(QShowEvent*) |
---|
| 1458 | { |
---|
| 1459 | emit changedContentSizeSignal(_contentHeight,_contentWidth); |
---|
| 1460 | } |
---|
| 1461 | void TerminalDisplay::hideEvent(QHideEvent*) |
---|
| 1462 | { |
---|
| 1463 | emit changedContentSizeSignal(_contentHeight,_contentWidth); |
---|
| 1464 | } |
---|
| 1465 | |
---|
| 1466 | /* ------------------------------------------------------------------------- */ |
---|
| 1467 | /* */ |
---|
| 1468 | /* Scrollbar */ |
---|
| 1469 | /* */ |
---|
| 1470 | /* ------------------------------------------------------------------------- */ |
---|
| 1471 | |
---|
| 1472 | void TerminalDisplay::scrollBarPositionChanged(int) |
---|
| 1473 | { |
---|
| 1474 | if ( !_screenWindow ) |
---|
| 1475 | return; |
---|
| 1476 | |
---|
| 1477 | _screenWindow->scrollTo( _scrollBar->value() ); |
---|
| 1478 | |
---|
| 1479 | // if the thumb has been moved to the bottom of the _scrollBar then set |
---|
| 1480 | // the display to automatically track new output, |
---|
| 1481 | // that is, scroll down automatically |
---|
| 1482 | // to how new _lines as they are added |
---|
| 1483 | const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum()); |
---|
| 1484 | _screenWindow->setTrackOutput( atEndOfOutput ); |
---|
| 1485 | |
---|
| 1486 | updateImage(); |
---|
| 1487 | } |
---|
| 1488 | |
---|
| 1489 | void TerminalDisplay::setScroll(int cursor, int slines) |
---|
| 1490 | { |
---|
| 1491 | //qDebug("%s %d setScroll", __FILE__, __LINE__); |
---|
| 1492 | // update _scrollBar if the range or value has changed, |
---|
| 1493 | // otherwise return |
---|
| 1494 | // |
---|
| 1495 | // setting the range or value of a _scrollBar will always trigger |
---|
| 1496 | // a repaint, so it should be avoided if it is not necessary |
---|
| 1497 | if ( _scrollBar->minimum() == 0 && |
---|
| 1498 | _scrollBar->maximum() == (slines - _lines) && |
---|
| 1499 | _scrollBar->value() == cursor ) |
---|
| 1500 | { |
---|
| 1501 | return; |
---|
| 1502 | } |
---|
| 1503 | |
---|
| 1504 | disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); |
---|
| 1505 | _scrollBar->setRange(0,slines - _lines); |
---|
| 1506 | _scrollBar->setSingleStep(1); |
---|
| 1507 | _scrollBar->setPageStep(_lines); |
---|
| 1508 | _scrollBar->setValue(cursor); |
---|
| 1509 | connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); |
---|
| 1510 | } |
---|
| 1511 | |
---|
| 1512 | void TerminalDisplay::setScrollBarPosition(ScrollBarPosition position) |
---|
| 1513 | { |
---|
| 1514 | if (_scrollbarLocation == position) { |
---|
| 1515 | // return; |
---|
| 1516 | } |
---|
| 1517 | |
---|
| 1518 | if ( position == NoScrollBar ) |
---|
| 1519 | _scrollBar->hide(); |
---|
| 1520 | else |
---|
| 1521 | _scrollBar->show(); |
---|
| 1522 | |
---|
| 1523 | _topMargin = _leftMargin = 1; |
---|
| 1524 | _scrollbarLocation = position; |
---|
| 1525 | |
---|
| 1526 | propagateSize(); |
---|
| 1527 | update(); |
---|
| 1528 | } |
---|
| 1529 | |
---|
| 1530 | void TerminalDisplay::mousePressEvent(QMouseEvent* ev) |
---|
| 1531 | { |
---|
| 1532 | if ( _possibleTripleClick && (ev->button()==Qt::LeftButton) ) { |
---|
| 1533 | mouseTripleClickEvent(ev); |
---|
| 1534 | return; |
---|
| 1535 | } |
---|
| 1536 | |
---|
| 1537 | if ( !contentsRect().contains(ev->pos()) ) return; |
---|
| 1538 | |
---|
| 1539 | if ( !_screenWindow ) return; |
---|
| 1540 | |
---|
| 1541 | int charLine; |
---|
| 1542 | int charColumn; |
---|
| 1543 | getCharacterPosition(ev->pos(),charLine,charColumn); |
---|
| 1544 | QPoint pos = QPoint(charColumn,charLine); |
---|
| 1545 | |
---|
| 1546 | if ( ev->button() == Qt::LeftButton) |
---|
| 1547 | { |
---|
| 1548 | _lineSelectionMode = false; |
---|
| 1549 | _wordSelectionMode = false; |
---|
| 1550 | |
---|
| 1551 | emit isBusySelecting(true); // Keep it steady... |
---|
| 1552 | // Drag only when the Control key is hold |
---|
| 1553 | bool selected = false; |
---|
| 1554 | |
---|
| 1555 | // The receiver of the testIsSelected() signal will adjust |
---|
| 1556 | // 'selected' accordingly. |
---|
| 1557 | //emit testIsSelected(pos.x(), pos.y(), selected); |
---|
| 1558 | |
---|
| 1559 | selected = _screenWindow->isSelected(pos.x(),pos.y()); |
---|
| 1560 | |
---|
| 1561 | if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected ) { |
---|
| 1562 | // The user clicked inside selected text |
---|
| 1563 | dragInfo.state = diPending; |
---|
| 1564 | dragInfo.start = ev->pos(); |
---|
| 1565 | } |
---|
| 1566 | else { |
---|
| 1567 | // No reason to ever start a drag event |
---|
| 1568 | dragInfo.state = diNone; |
---|
| 1569 | |
---|
| 1570 | _preserveLineBreaks = !( ( ev->modifiers() & Qt::ControlModifier ) && !(ev->modifiers() & Qt::AltModifier) ); |
---|
| 1571 | _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier); |
---|
| 1572 | |
---|
| 1573 | if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) |
---|
| 1574 | { |
---|
| 1575 | _screenWindow->clearSelection(); |
---|
| 1576 | |
---|
| 1577 | //emit clearSelectionSignal(); |
---|
| 1578 | pos.ry() += _scrollBar->value(); |
---|
| 1579 | _iPntSel = _pntSel = pos; |
---|
| 1580 | _actSel = 1; // left mouse button pressed but nothing selected yet. |
---|
| 1581 | |
---|
| 1582 | } |
---|
| 1583 | else |
---|
| 1584 | { |
---|
| 1585 | emit mouseSignal( 0, charColumn + 1, charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); |
---|
| 1586 | } |
---|
| 1587 | } |
---|
| 1588 | } |
---|
| 1589 | else if ( ev->button() == Qt::MidButton ) |
---|
| 1590 | { |
---|
| 1591 | if ( _mouseMarks || (!_mouseMarks && (ev->modifiers() & Qt::ShiftModifier)) ) |
---|
| 1592 | emitSelection(true,ev->modifiers() & Qt::ControlModifier); |
---|
| 1593 | else |
---|
| 1594 | emit mouseSignal( 1, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); |
---|
| 1595 | } |
---|
| 1596 | else if ( ev->button() == Qt::RightButton ) |
---|
| 1597 | { |
---|
| 1598 | if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) |
---|
| 1599 | { |
---|
| 1600 | emit configureRequest( this, |
---|
| 1601 | ev->modifiers() & (Qt::ShiftModifier|Qt::ControlModifier), |
---|
| 1602 | ev->pos() |
---|
| 1603 | ); |
---|
| 1604 | } |
---|
| 1605 | else |
---|
| 1606 | emit mouseSignal( 2, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); |
---|
| 1607 | } |
---|
| 1608 | } |
---|
| 1609 | |
---|
| 1610 | QList<QAction*> TerminalDisplay::filterActions(const QPoint& position) |
---|
| 1611 | { |
---|
| 1612 | int charLine, charColumn; |
---|
| 1613 | getCharacterPosition(position,charLine,charColumn); |
---|
| 1614 | |
---|
| 1615 | Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); |
---|
| 1616 | |
---|
| 1617 | return spot ? spot->actions() : QList<QAction*>(); |
---|
| 1618 | } |
---|
| 1619 | |
---|
| 1620 | void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev) |
---|
| 1621 | { |
---|
| 1622 | int charLine = 0; |
---|
| 1623 | int charColumn = 0; |
---|
| 1624 | |
---|
| 1625 | getCharacterPosition(ev->pos(),charLine,charColumn); |
---|
| 1626 | |
---|
| 1627 | // handle filters |
---|
| 1628 | // change link hot-spot appearance on mouse-over |
---|
| 1629 | Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); |
---|
| 1630 | if ( spot && spot->type() == Filter::HotSpot::Link) |
---|
| 1631 | { |
---|
| 1632 | QRect previousHotspotArea = _mouseOverHotspotArea; |
---|
| 1633 | _mouseOverHotspotArea.setCoords( qMin(spot->startColumn() , spot->endColumn()) * _fontWidth, |
---|
| 1634 | spot->startLine() * _fontHeight, |
---|
| 1635 | qMax(spot->startColumn() , spot->endColumn()) * _fontHeight, |
---|
| 1636 | (spot->endLine()+1) * _fontHeight ); |
---|
| 1637 | |
---|
| 1638 | // display tooltips when mousing over links |
---|
| 1639 | // TODO: Extend this to work with filter types other than links |
---|
| 1640 | const QString& tooltip = spot->tooltip(); |
---|
| 1641 | if ( !tooltip.isEmpty() ) |
---|
| 1642 | { |
---|
| 1643 | QToolTip::showText( mapToGlobal(ev->pos()) , tooltip , this , _mouseOverHotspotArea ); |
---|
| 1644 | } |
---|
| 1645 | |
---|
| 1646 | update( _mouseOverHotspotArea | previousHotspotArea ); |
---|
| 1647 | } |
---|
| 1648 | else if ( _mouseOverHotspotArea.isValid() ) |
---|
| 1649 | { |
---|
| 1650 | update( _mouseOverHotspotArea ); |
---|
| 1651 | // set hotspot area to an invalid rectangle |
---|
| 1652 | _mouseOverHotspotArea = QRect(); |
---|
| 1653 | } |
---|
| 1654 | |
---|
| 1655 | // for auto-hiding the cursor, we need mouseTracking |
---|
| 1656 | if (ev->buttons() == Qt::NoButton ) return; |
---|
| 1657 | |
---|
| 1658 | // if the terminal is interested in mouse movements |
---|
| 1659 | // then emit a mouse movement signal, unless the shift |
---|
| 1660 | // key is being held down, which overrides this. |
---|
| 1661 | if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) |
---|
| 1662 | { |
---|
| 1663 | int button = 3; |
---|
| 1664 | if (ev->buttons() & Qt::LeftButton) |
---|
| 1665 | button = 0; |
---|
| 1666 | if (ev->buttons() & Qt::MidButton) |
---|
| 1667 | button = 1; |
---|
| 1668 | if (ev->buttons() & Qt::RightButton) |
---|
| 1669 | button = 2; |
---|
| 1670 | |
---|
| 1671 | |
---|
| 1672 | emit mouseSignal( button, |
---|
| 1673 | charColumn + 1, |
---|
| 1674 | charLine + 1 +_scrollBar->value() -_scrollBar->maximum(), |
---|
| 1675 | 1 ); |
---|
| 1676 | |
---|
| 1677 | return; |
---|
| 1678 | } |
---|
| 1679 | |
---|
| 1680 | if (dragInfo.state == diPending) |
---|
| 1681 | { |
---|
| 1682 | // we had a mouse down, but haven't confirmed a drag yet |
---|
| 1683 | // if the mouse has moved sufficiently, we will confirm |
---|
| 1684 | |
---|
| 1685 | int distance = 10; //KGlobalSettings::dndEventDelay(); |
---|
| 1686 | if ( ev->x() > dragInfo.start.x() + distance || ev->x() < dragInfo.start.x() - distance || |
---|
| 1687 | ev->y() > dragInfo.start.y() + distance || ev->y() < dragInfo.start.y() - distance) |
---|
| 1688 | { |
---|
| 1689 | // we've left the drag square, we can start a real drag operation now |
---|
| 1690 | emit isBusySelecting(false); // Ok.. we can breath again. |
---|
| 1691 | |
---|
| 1692 | _screenWindow->clearSelection(); |
---|
| 1693 | doDrag(); |
---|
| 1694 | } |
---|
| 1695 | return; |
---|
| 1696 | } |
---|
| 1697 | else if (dragInfo.state == diDragging) |
---|
| 1698 | { |
---|
| 1699 | // this isn't technically needed because mouseMoveEvent is suppressed during |
---|
| 1700 | // Qt drag operations, replaced by dragMoveEvent |
---|
| 1701 | return; |
---|
| 1702 | } |
---|
| 1703 | |
---|
| 1704 | if (_actSel == 0) return; |
---|
| 1705 | |
---|
| 1706 | // don't extend selection while pasting |
---|
| 1707 | if (ev->buttons() & Qt::MidButton) return; |
---|
| 1708 | |
---|
| 1709 | extendSelection( ev->pos() ); |
---|
| 1710 | } |
---|
| 1711 | |
---|
| 1712 | #if 0 |
---|
| 1713 | void TerminalDisplay::setSelectionEnd() |
---|
| 1714 | { |
---|
| 1715 | extendSelection( _configureRequestPoint ); |
---|
| 1716 | } |
---|
| 1717 | #endif |
---|
| 1718 | |
---|
| 1719 | void TerminalDisplay::extendSelection( const QPoint& position ) |
---|
| 1720 | { |
---|
| 1721 | QPoint pos = position; |
---|
| 1722 | |
---|
| 1723 | if ( !_screenWindow ) |
---|
| 1724 | return; |
---|
| 1725 | |
---|
| 1726 | //if ( !contentsRect().contains(ev->pos()) ) return; |
---|
| 1727 | QPoint tL = contentsRect().topLeft(); |
---|
| 1728 | int tLx = tL.x(); |
---|
| 1729 | int tLy = tL.y(); |
---|
| 1730 | int scroll = _scrollBar->value(); |
---|
| 1731 | |
---|
| 1732 | // we're in the process of moving the mouse with the left button pressed |
---|
| 1733 | // the mouse cursor will kept caught within the bounds of the text in |
---|
| 1734 | // this widget. |
---|
| 1735 | |
---|
| 1736 | // Adjust position within text area bounds. See FIXME above. |
---|
| 1737 | QPoint oldpos = pos; |
---|
| 1738 | if ( pos.x() < tLx+_leftMargin ) |
---|
| 1739 | pos.setX( tLx+_leftMargin ); |
---|
| 1740 | if ( pos.x() > tLx+_leftMargin+_usedColumns*_fontWidth-1 ) |
---|
| 1741 | pos.setX( tLx+_leftMargin+_usedColumns*_fontWidth ); |
---|
| 1742 | if ( pos.y() < tLy+_topMargin ) |
---|
| 1743 | pos.setY( tLy+_topMargin ); |
---|
| 1744 | if ( pos.y() > tLy+_topMargin+_usedLines*_fontHeight-1 ) |
---|
| 1745 | pos.setY( tLy+_topMargin+_usedLines*_fontHeight-1 ); |
---|
| 1746 | |
---|
| 1747 | if ( pos.y() == tLy+_topMargin+_usedLines*_fontHeight-1 ) |
---|
| 1748 | { |
---|
| 1749 | _scrollBar->setValue(_scrollBar->value()+yMouseScroll); // scrollforward |
---|
| 1750 | } |
---|
| 1751 | if ( pos.y() == tLy+_topMargin ) |
---|
| 1752 | { |
---|
| 1753 | _scrollBar->setValue(_scrollBar->value()-yMouseScroll); // scrollback |
---|
| 1754 | } |
---|
| 1755 | |
---|
| 1756 | int charColumn = 0; |
---|
| 1757 | int charLine = 0; |
---|
| 1758 | getCharacterPosition(pos,charLine,charColumn); |
---|
| 1759 | |
---|
| 1760 | QPoint here = QPoint(charColumn,charLine); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight); |
---|
| 1761 | QPoint ohere; |
---|
| 1762 | QPoint _iPntSelCorr = _iPntSel; |
---|
| 1763 | _iPntSelCorr.ry() -= _scrollBar->value(); |
---|
| 1764 | QPoint _pntSelCorr = _pntSel; |
---|
| 1765 | _pntSelCorr.ry() -= _scrollBar->value(); |
---|
| 1766 | bool swapping = false; |
---|
| 1767 | |
---|
| 1768 | if ( _wordSelectionMode ) |
---|
| 1769 | { |
---|
| 1770 | // Extend to word boundaries |
---|
| 1771 | int i; |
---|
| 1772 | int selClass; |
---|
| 1773 | |
---|
| 1774 | bool left_not_right = ( here.y() < _iPntSelCorr.y() || |
---|
| 1775 | here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ); |
---|
| 1776 | bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || |
---|
| 1777 | _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ); |
---|
| 1778 | swapping = left_not_right != old_left_not_right; |
---|
| 1779 | |
---|
| 1780 | // Find left (left_not_right ? from here : from start) |
---|
| 1781 | QPoint left = left_not_right ? here : _iPntSelCorr; |
---|
| 1782 | i = loc(left.x(),left.y()); |
---|
| 1783 | if (i>=0 && i<=_imageSize) { |
---|
| 1784 | selClass = charClass(_image[i].character); |
---|
| 1785 | while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) )) |
---|
| 1786 | && charClass(_image[i-1].character) == selClass ) |
---|
| 1787 | { i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} } |
---|
| 1788 | } |
---|
| 1789 | |
---|
| 1790 | // Find left (left_not_right ? from start : from here) |
---|
| 1791 | QPoint right = left_not_right ? _iPntSelCorr : here; |
---|
| 1792 | i = loc(right.x(),right.y()); |
---|
| 1793 | if (i>=0 && i<=_imageSize) { |
---|
| 1794 | selClass = charClass(_image[i].character); |
---|
| 1795 | while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) )) |
---|
| 1796 | && charClass(_image[i+1].character) == selClass ) |
---|
| 1797 | { i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } } |
---|
| 1798 | } |
---|
| 1799 | |
---|
| 1800 | // Pick which is start (ohere) and which is extension (here) |
---|
| 1801 | if ( left_not_right ) |
---|
| 1802 | { |
---|
| 1803 | here = left; ohere = right; |
---|
| 1804 | } |
---|
| 1805 | else |
---|
| 1806 | { |
---|
| 1807 | here = right; ohere = left; |
---|
| 1808 | } |
---|
| 1809 | ohere.rx()++; |
---|
| 1810 | } |
---|
| 1811 | |
---|
| 1812 | if ( _lineSelectionMode ) |
---|
| 1813 | { |
---|
| 1814 | // Extend to complete line |
---|
| 1815 | bool above_not_below = ( here.y() < _iPntSelCorr.y() ); |
---|
| 1816 | |
---|
| 1817 | QPoint above = above_not_below ? here : _iPntSelCorr; |
---|
| 1818 | QPoint below = above_not_below ? _iPntSelCorr : here; |
---|
| 1819 | |
---|
| 1820 | while (above.y()>0 && (_lineProperties[above.y()-1] & LINE_WRAPPED) ) |
---|
| 1821 | above.ry()--; |
---|
| 1822 | while (below.y()<_usedLines-1 && (_lineProperties[below.y()] & LINE_WRAPPED) ) |
---|
| 1823 | below.ry()++; |
---|
| 1824 | |
---|
| 1825 | above.setX(0); |
---|
| 1826 | below.setX(_usedColumns-1); |
---|
| 1827 | |
---|
| 1828 | // Pick which is start (ohere) and which is extension (here) |
---|
| 1829 | if ( above_not_below ) |
---|
| 1830 | { |
---|
| 1831 | here = above; ohere = below; |
---|
| 1832 | } |
---|
| 1833 | else |
---|
| 1834 | { |
---|
| 1835 | here = below; ohere = above; |
---|
| 1836 | } |
---|
| 1837 | |
---|
| 1838 | QPoint newSelBegin = QPoint( ohere.x(), ohere.y() ); |
---|
| 1839 | swapping = !(_tripleSelBegin==newSelBegin); |
---|
| 1840 | _tripleSelBegin = newSelBegin; |
---|
| 1841 | |
---|
| 1842 | ohere.rx()++; |
---|
| 1843 | } |
---|
| 1844 | |
---|
| 1845 | int offset = 0; |
---|
| 1846 | if ( !_wordSelectionMode && !_lineSelectionMode ) |
---|
| 1847 | { |
---|
| 1848 | int i; |
---|
| 1849 | int selClass; |
---|
| 1850 | |
---|
| 1851 | bool left_not_right = ( here.y() < _iPntSelCorr.y() || |
---|
| 1852 | here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ); |
---|
| 1853 | bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || |
---|
| 1854 | _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ); |
---|
| 1855 | swapping = left_not_right != old_left_not_right; |
---|
| 1856 | |
---|
| 1857 | // Find left (left_not_right ? from here : from start) |
---|
| 1858 | QPoint left = left_not_right ? here : _iPntSelCorr; |
---|
| 1859 | |
---|
| 1860 | // Find left (left_not_right ? from start : from here) |
---|
| 1861 | QPoint right = left_not_right ? _iPntSelCorr : here; |
---|
| 1862 | if ( right.x() > 0 && !_columnSelectionMode ) |
---|
| 1863 | { |
---|
| 1864 | i = loc(right.x(),right.y()); |
---|
| 1865 | if (i>=0 && i<=_imageSize) { |
---|
| 1866 | selClass = charClass(_image[i-1].character); |
---|
| 1867 | if (selClass == ' ') |
---|
| 1868 | { |
---|
| 1869 | while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) && |
---|
| 1870 | !(_lineProperties[right.y()] & LINE_WRAPPED)) |
---|
| 1871 | { i++; right.rx()++; } |
---|
| 1872 | if (right.x() < _usedColumns-1) |
---|
| 1873 | right = left_not_right ? _iPntSelCorr : here; |
---|
| 1874 | else |
---|
| 1875 | right.rx()++; // will be balanced later because of offset=-1; |
---|
| 1876 | } |
---|
| 1877 | } |
---|
| 1878 | } |
---|
| 1879 | |
---|
| 1880 | // Pick which is start (ohere) and which is extension (here) |
---|
| 1881 | if ( left_not_right ) |
---|
| 1882 | { |
---|
| 1883 | here = left; ohere = right; offset = 0; |
---|
| 1884 | } |
---|
| 1885 | else |
---|
| 1886 | { |
---|
| 1887 | here = right; ohere = left; offset = -1; |
---|
| 1888 | } |
---|
| 1889 | } |
---|
| 1890 | |
---|
| 1891 | if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved |
---|
| 1892 | |
---|
| 1893 | if (here == ohere) return; // It's not left, it's not right. |
---|
| 1894 | |
---|
| 1895 | if ( _actSel < 2 || swapping ) |
---|
| 1896 | { |
---|
| 1897 | if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) |
---|
| 1898 | { |
---|
| 1899 | _screenWindow->setSelectionStart( ohere.x() , ohere.y() , true ); |
---|
| 1900 | } |
---|
| 1901 | else |
---|
| 1902 | { |
---|
| 1903 | _screenWindow->setSelectionStart( ohere.x()-1-offset , ohere.y() , false ); |
---|
| 1904 | } |
---|
| 1905 | |
---|
| 1906 | } |
---|
| 1907 | |
---|
| 1908 | _actSel = 2; // within selection |
---|
| 1909 | _pntSel = here; |
---|
| 1910 | _pntSel.ry() += _scrollBar->value(); |
---|
| 1911 | |
---|
| 1912 | if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) |
---|
| 1913 | { |
---|
| 1914 | _screenWindow->setSelectionEnd( here.x() , here.y() ); |
---|
| 1915 | } |
---|
| 1916 | else |
---|
| 1917 | { |
---|
| 1918 | _screenWindow->setSelectionEnd( here.x()+offset , here.y() ); |
---|
| 1919 | } |
---|
| 1920 | |
---|
| 1921 | } |
---|
| 1922 | |
---|
| 1923 | void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev) |
---|
| 1924 | { |
---|
| 1925 | if ( !_screenWindow ) |
---|
| 1926 | return; |
---|
| 1927 | |
---|
| 1928 | int charLine; |
---|
| 1929 | int charColumn; |
---|
| 1930 | getCharacterPosition(ev->pos(),charLine,charColumn); |
---|
| 1931 | |
---|
| 1932 | if ( ev->button() == Qt::LeftButton) |
---|
| 1933 | { |
---|
| 1934 | emit isBusySelecting(false); |
---|
| 1935 | if(dragInfo.state == diPending) |
---|
| 1936 | { |
---|
| 1937 | // We had a drag event pending but never confirmed. Kill selection |
---|
| 1938 | _screenWindow->clearSelection(); |
---|
| 1939 | //emit clearSelectionSignal(); |
---|
| 1940 | } |
---|
| 1941 | else |
---|
| 1942 | { |
---|
| 1943 | if ( _actSel > 1 ) |
---|
| 1944 | { |
---|
| 1945 | setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); |
---|
| 1946 | } |
---|
| 1947 | |
---|
| 1948 | _actSel = 0; |
---|
| 1949 | |
---|
| 1950 | //FIXME: emits a release event even if the mouse is |
---|
| 1951 | // outside the range. The procedure used in `mouseMoveEvent' |
---|
| 1952 | // applies here, too. |
---|
| 1953 | |
---|
| 1954 | if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) |
---|
| 1955 | emit mouseSignal( 3, // release |
---|
| 1956 | charColumn + 1, |
---|
| 1957 | charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); |
---|
| 1958 | } |
---|
| 1959 | dragInfo.state = diNone; |
---|
| 1960 | } |
---|
| 1961 | |
---|
| 1962 | |
---|
| 1963 | if ( !_mouseMarks && |
---|
| 1964 | ((ev->button() == Qt::RightButton && !(ev->modifiers() & Qt::ShiftModifier)) |
---|
| 1965 | || ev->button() == Qt::MidButton) ) |
---|
| 1966 | { |
---|
| 1967 | emit mouseSignal( 3, |
---|
| 1968 | charColumn + 1, |
---|
| 1969 | charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , |
---|
| 1970 | 0); |
---|
| 1971 | } |
---|
| 1972 | } |
---|
| 1973 | |
---|
| 1974 | void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const |
---|
| 1975 | { |
---|
| 1976 | |
---|
| 1977 | column = (widgetPoint.x() + _fontWidth/2 -contentsRect().left()-_leftMargin) / _fontWidth; |
---|
| 1978 | line = (widgetPoint.y()-contentsRect().top()-_topMargin) / _fontHeight; |
---|
| 1979 | |
---|
| 1980 | if ( line < 0 ) |
---|
| 1981 | line = 0; |
---|
| 1982 | if ( column < 0 ) |
---|
| 1983 | column = 0; |
---|
| 1984 | |
---|
| 1985 | if ( line >= _usedLines ) |
---|
| 1986 | line = _usedLines-1; |
---|
| 1987 | |
---|
| 1988 | // the column value returned can be equal to _usedColumns, which |
---|
| 1989 | // is the position just after the last character displayed in a line. |
---|
| 1990 | // |
---|
| 1991 | // this is required so that the user can select characters in the right-most |
---|
| 1992 | // column (or left-most for right-to-left input) |
---|
| 1993 | if ( column > _usedColumns ) |
---|
| 1994 | column = _usedColumns; |
---|
| 1995 | } |
---|
| 1996 | |
---|
| 1997 | void TerminalDisplay::updateLineProperties() |
---|
| 1998 | { |
---|
| 1999 | if ( !_screenWindow ) |
---|
| 2000 | return; |
---|
| 2001 | |
---|
| 2002 | _lineProperties = _screenWindow->getLineProperties(); |
---|
| 2003 | } |
---|
| 2004 | |
---|
| 2005 | void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev) |
---|
| 2006 | { |
---|
| 2007 | if ( ev->button() != Qt::LeftButton) return; |
---|
| 2008 | if ( !_screenWindow ) return; |
---|
| 2009 | |
---|
| 2010 | int charLine = 0; |
---|
| 2011 | int charColumn = 0; |
---|
| 2012 | |
---|
| 2013 | getCharacterPosition(ev->pos(),charLine,charColumn); |
---|
| 2014 | |
---|
| 2015 | QPoint pos(charColumn,charLine); |
---|
| 2016 | |
---|
| 2017 | // pass on double click as two clicks. |
---|
| 2018 | if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) |
---|
| 2019 | { |
---|
| 2020 | // Send just _ONE_ click event, since the first click of the double click |
---|
| 2021 | // was already sent by the click handler |
---|
| 2022 | emit mouseSignal( 0, |
---|
| 2023 | pos.x()+1, |
---|
| 2024 | pos.y()+1 +_scrollBar->value() -_scrollBar->maximum(), |
---|
| 2025 | 0 ); // left button |
---|
| 2026 | return; |
---|
| 2027 | } |
---|
| 2028 | |
---|
| 2029 | _screenWindow->clearSelection(); |
---|
| 2030 | QPoint bgnSel = pos; |
---|
| 2031 | QPoint endSel = pos; |
---|
| 2032 | int i = loc(bgnSel.x(),bgnSel.y()); |
---|
| 2033 | _iPntSel = bgnSel; |
---|
| 2034 | _iPntSel.ry() += _scrollBar->value(); |
---|
| 2035 | |
---|
| 2036 | _wordSelectionMode = true; |
---|
| 2037 | |
---|
| 2038 | // find word boundaries... |
---|
| 2039 | int selClass = charClass(_image[i].character); |
---|
| 2040 | { |
---|
| 2041 | // find the start of the word |
---|
| 2042 | int x = bgnSel.x(); |
---|
| 2043 | while ( ((x>0) || (bgnSel.y()>0 && (_lineProperties[bgnSel.y()-1] & LINE_WRAPPED) )) |
---|
| 2044 | && charClass(_image[i-1].character) == selClass ) |
---|
| 2045 | { |
---|
| 2046 | i--; |
---|
| 2047 | if (x>0) |
---|
| 2048 | x--; |
---|
| 2049 | else |
---|
| 2050 | { |
---|
| 2051 | x=_usedColumns-1; |
---|
| 2052 | bgnSel.ry()--; |
---|
| 2053 | } |
---|
| 2054 | } |
---|
| 2055 | |
---|
| 2056 | bgnSel.setX(x); |
---|
| 2057 | _screenWindow->setSelectionStart( bgnSel.x() , bgnSel.y() , false ); |
---|
| 2058 | |
---|
| 2059 | // find the end of the word |
---|
| 2060 | i = loc( endSel.x(), endSel.y() ); |
---|
| 2061 | x = endSel.x(); |
---|
| 2062 | while( ((x<_usedColumns-1) || (endSel.y()<_usedLines-1 && (_lineProperties[endSel.y()] & LINE_WRAPPED) )) |
---|
| 2063 | && charClass(_image[i+1].character) == selClass ) |
---|
| 2064 | { |
---|
| 2065 | i++; |
---|
| 2066 | if (x<_usedColumns-1) |
---|
| 2067 | x++; |
---|
| 2068 | else |
---|
| 2069 | { |
---|
| 2070 | x=0; |
---|
| 2071 | endSel.ry()++; |
---|
| 2072 | } |
---|
| 2073 | } |
---|
| 2074 | |
---|
| 2075 | endSel.setX(x); |
---|
| 2076 | |
---|
| 2077 | // In word selection mode don't select @ (64) if at end of word. |
---|
| 2078 | if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) ) |
---|
| 2079 | endSel.setX( x - 1 ); |
---|
| 2080 | |
---|
| 2081 | |
---|
| 2082 | _actSel = 2; // within selection |
---|
| 2083 | |
---|
| 2084 | _screenWindow->setSelectionEnd( endSel.x() , endSel.y() ); |
---|
| 2085 | |
---|
| 2086 | setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); |
---|
| 2087 | } |
---|
| 2088 | |
---|
| 2089 | _possibleTripleClick=true; |
---|
| 2090 | |
---|
| 2091 | QTimer::singleShot(QApplication::doubleClickInterval(),this, |
---|
| 2092 | SLOT(tripleClickTimeout())); |
---|
| 2093 | } |
---|
| 2094 | |
---|
| 2095 | void TerminalDisplay::wheelEvent( QWheelEvent* ev ) |
---|
| 2096 | { |
---|
| 2097 | if (ev->orientation() != Qt::Vertical) |
---|
| 2098 | return; |
---|
| 2099 | |
---|
| 2100 | if ( _mouseMarks ) |
---|
| 2101 | _scrollBar->event(ev); |
---|
| 2102 | else |
---|
| 2103 | { |
---|
| 2104 | int charLine; |
---|
| 2105 | int charColumn; |
---|
| 2106 | getCharacterPosition( ev->pos() , charLine , charColumn ); |
---|
| 2107 | |
---|
| 2108 | emit mouseSignal( ev->delta() > 0 ? 4 : 5, |
---|
| 2109 | charColumn + 1, |
---|
| 2110 | charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , |
---|
| 2111 | 0); |
---|
| 2112 | } |
---|
| 2113 | } |
---|
| 2114 | |
---|
| 2115 | void TerminalDisplay::tripleClickTimeout() |
---|
| 2116 | { |
---|
| 2117 | _possibleTripleClick=false; |
---|
| 2118 | } |
---|
| 2119 | |
---|
| 2120 | void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev) |
---|
| 2121 | { |
---|
| 2122 | if ( !_screenWindow ) return; |
---|
| 2123 | |
---|
| 2124 | int charLine; |
---|
| 2125 | int charColumn; |
---|
| 2126 | getCharacterPosition(ev->pos(),charLine,charColumn); |
---|
| 2127 | _iPntSel = QPoint(charColumn,charLine); |
---|
| 2128 | |
---|
| 2129 | _screenWindow->clearSelection(); |
---|
| 2130 | |
---|
| 2131 | _lineSelectionMode = true; |
---|
| 2132 | _wordSelectionMode = false; |
---|
| 2133 | |
---|
| 2134 | _actSel = 2; // within selection |
---|
| 2135 | emit isBusySelecting(true); // Keep it steady... |
---|
| 2136 | |
---|
| 2137 | while (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) |
---|
| 2138 | _iPntSel.ry()--; |
---|
| 2139 | |
---|
| 2140 | if (_tripleClickMode == SelectForwardsFromCursor) { |
---|
| 2141 | // find word boundary start |
---|
| 2142 | int i = loc(_iPntSel.x(),_iPntSel.y()); |
---|
| 2143 | int selClass = charClass(_image[i].character); |
---|
| 2144 | int x = _iPntSel.x(); |
---|
| 2145 | |
---|
| 2146 | while ( ((x>0) || |
---|
| 2147 | (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) |
---|
| 2148 | ) |
---|
| 2149 | && charClass(_image[i-1].character) == selClass ) |
---|
| 2150 | { |
---|
| 2151 | i--; |
---|
| 2152 | if (x>0) |
---|
| 2153 | x--; |
---|
| 2154 | else |
---|
| 2155 | { |
---|
| 2156 | x=_columns-1; |
---|
| 2157 | _iPntSel.ry()--; |
---|
| 2158 | } |
---|
| 2159 | } |
---|
| 2160 | |
---|
| 2161 | _screenWindow->setSelectionStart( x , _iPntSel.y() , false ); |
---|
| 2162 | _tripleSelBegin = QPoint( x, _iPntSel.y() ); |
---|
| 2163 | } |
---|
| 2164 | else if (_tripleClickMode == SelectWholeLine) { |
---|
| 2165 | _screenWindow->setSelectionStart( 0 , _iPntSel.y() , false ); |
---|
| 2166 | _tripleSelBegin = QPoint( 0, _iPntSel.y() ); |
---|
| 2167 | } |
---|
| 2168 | |
---|
| 2169 | while (_iPntSel.y()<_lines-1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED) ) |
---|
| 2170 | _iPntSel.ry()++; |
---|
| 2171 | |
---|
| 2172 | _screenWindow->setSelectionEnd( _columns - 1 , _iPntSel.y() ); |
---|
| 2173 | |
---|
| 2174 | setSelection(_screenWindow->selectedText(_preserveLineBreaks)); |
---|
| 2175 | |
---|
| 2176 | _iPntSel.ry() += _scrollBar->value(); |
---|
| 2177 | } |
---|
| 2178 | |
---|
| 2179 | |
---|
| 2180 | bool TerminalDisplay::focusNextPrevChild( bool next ) |
---|
| 2181 | { |
---|
| 2182 | if (next) |
---|
| 2183 | return false; // This disables changing the active part in konqueror |
---|
| 2184 | // when pressing Tab |
---|
| 2185 | return QWidget::focusNextPrevChild( next ); |
---|
| 2186 | } |
---|
| 2187 | |
---|
| 2188 | |
---|
| 2189 | int TerminalDisplay::charClass(quint16 ch) const |
---|
| 2190 | { |
---|
| 2191 | QChar qch=QChar(ch); |
---|
| 2192 | if ( qch.isSpace() ) return ' '; |
---|
| 2193 | |
---|
| 2194 | if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) ) |
---|
| 2195 | return 'a'; |
---|
| 2196 | |
---|
| 2197 | // Everything else is weird |
---|
| 2198 | return 1; |
---|
| 2199 | } |
---|
| 2200 | |
---|
| 2201 | void TerminalDisplay::setWordCharacters(const QString& wc) |
---|
| 2202 | { |
---|
| 2203 | _wordCharacters = wc; |
---|
| 2204 | } |
---|
| 2205 | |
---|
| 2206 | void TerminalDisplay::setUsesMouse(bool on) |
---|
| 2207 | { |
---|
| 2208 | _mouseMarks = on; |
---|
| 2209 | setCursor( _mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor ); |
---|
| 2210 | } |
---|
| 2211 | bool TerminalDisplay::usesMouse() const |
---|
| 2212 | { |
---|
| 2213 | return _mouseMarks; |
---|
| 2214 | } |
---|
| 2215 | |
---|
| 2216 | /* ------------------------------------------------------------------------- */ |
---|
| 2217 | /* */ |
---|
| 2218 | /* Clipboard */ |
---|
| 2219 | /* */ |
---|
| 2220 | /* ------------------------------------------------------------------------- */ |
---|
| 2221 | |
---|
| 2222 | #undef KeyPress |
---|
| 2223 | |
---|
| 2224 | void TerminalDisplay::emitSelection(bool useXselection,bool appendReturn) |
---|
| 2225 | { |
---|
| 2226 | if ( !_screenWindow ) |
---|
| 2227 | return; |
---|
| 2228 | |
---|
| 2229 | // Paste Clipboard by simulating keypress events |
---|
| 2230 | QString text = QApplication::clipboard()->text(useXselection ? QClipboard::Selection : |
---|
| 2231 | QClipboard::Clipboard); |
---|
| 2232 | if(appendReturn) |
---|
| 2233 | text.append("\r"); |
---|
| 2234 | if ( ! text.isEmpty() ) |
---|
| 2235 | { |
---|
| 2236 | text.replace("\n", "\r"); |
---|
| 2237 | QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text); |
---|
| 2238 | emit keyPressedSignal(&e); // expose as a big fat keypress event |
---|
| 2239 | |
---|
| 2240 | _screenWindow->clearSelection(); |
---|
| 2241 | } |
---|
| 2242 | } |
---|
| 2243 | |
---|
| 2244 | void TerminalDisplay::setSelection(const QString& t) |
---|
| 2245 | { |
---|
| 2246 | QApplication::clipboard()->setText(t, QClipboard::Selection); |
---|
| 2247 | } |
---|
| 2248 | |
---|
| 2249 | void TerminalDisplay::copyClipboard() |
---|
| 2250 | { |
---|
| 2251 | if ( !_screenWindow ) |
---|
| 2252 | return; |
---|
| 2253 | |
---|
| 2254 | QString text = _screenWindow->selectedText(_preserveLineBreaks); |
---|
| 2255 | QApplication::clipboard()->setText(text); |
---|
| 2256 | } |
---|
| 2257 | |
---|
| 2258 | void TerminalDisplay::pasteClipboard() |
---|
| 2259 | { |
---|
| 2260 | emitSelection(false,false); |
---|
| 2261 | } |
---|
| 2262 | |
---|
| 2263 | void TerminalDisplay::pasteSelection() |
---|
| 2264 | { |
---|
| 2265 | emitSelection(true,false); |
---|
| 2266 | } |
---|
| 2267 | |
---|
| 2268 | /* ------------------------------------------------------------------------- */ |
---|
| 2269 | /* */ |
---|
| 2270 | /* Keyboard */ |
---|
| 2271 | /* */ |
---|
| 2272 | /* ------------------------------------------------------------------------- */ |
---|
| 2273 | |
---|
| 2274 | void TerminalDisplay::setFlowControlWarningEnabled( bool enable ) |
---|
| 2275 | { |
---|
| 2276 | _flowControlWarningEnabled = enable; |
---|
| 2277 | |
---|
| 2278 | // if the dialog is currently visible and the flow control warning has |
---|
| 2279 | // been disabled then hide the dialog |
---|
| 2280 | if (!enable) |
---|
| 2281 | outputSuspended(false); |
---|
| 2282 | } |
---|
| 2283 | |
---|
| 2284 | void TerminalDisplay::keyPressEvent( QKeyEvent* event ) |
---|
| 2285 | { |
---|
| 2286 | //qDebug("%s %d keyPressEvent and key is %d", __FILE__, __LINE__, event->key()); |
---|
| 2287 | |
---|
| 2288 | bool emitKeyPressSignal = true; |
---|
| 2289 | |
---|
| 2290 | // XonXoff flow control |
---|
| 2291 | if (event->modifiers() & Qt::ControlModifier && _flowControlWarningEnabled) |
---|
| 2292 | { |
---|
| 2293 | if ( event->key() == Qt::Key_S ) { |
---|
| 2294 | //qDebug("%s %d keyPressEvent, output suspended", __FILE__, __LINE__); |
---|
| 2295 | emit flowControlKeyPressed(true /*output suspended*/); |
---|
| 2296 | } |
---|
| 2297 | else if ( event->key() == Qt::Key_Q ) { |
---|
| 2298 | //qDebug("%s %d keyPressEvent, output enabled", __FILE__, __LINE__); |
---|
| 2299 | emit flowControlKeyPressed(false /*output enabled*/); |
---|
| 2300 | } |
---|
| 2301 | } |
---|
| 2302 | |
---|
| 2303 | // Keyboard-based navigation |
---|
| 2304 | if ( event->modifiers() == Qt::ShiftModifier ) |
---|
| 2305 | { |
---|
| 2306 | bool update = true; |
---|
| 2307 | |
---|
| 2308 | if ( event->key() == Qt::Key_PageUp ) |
---|
| 2309 | { |
---|
| 2310 | //qDebug("%s %d pageup", __FILE__, __LINE__); |
---|
| 2311 | _screenWindow->scrollBy( ScreenWindow::ScrollPages , -1 ); |
---|
| 2312 | } |
---|
| 2313 | else if ( event->key() == Qt::Key_PageDown ) |
---|
| 2314 | { |
---|
| 2315 | //qDebug("%s %d pagedown", __FILE__, __LINE__); |
---|
| 2316 | _screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 ); |
---|
| 2317 | } |
---|
| 2318 | else if ( event->key() == Qt::Key_Up ) |
---|
| 2319 | { |
---|
| 2320 | //qDebug("%s %d keyup", __FILE__, __LINE__); |
---|
| 2321 | _screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 ); |
---|
| 2322 | } |
---|
| 2323 | else if ( event->key() == Qt::Key_Down ) |
---|
| 2324 | { |
---|
| 2325 | //qDebug("%s %d keydown", __FILE__, __LINE__); |
---|
| 2326 | _screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 ); |
---|
| 2327 | } |
---|
| 2328 | else { |
---|
| 2329 | update = false; |
---|
| 2330 | } |
---|
| 2331 | |
---|
| 2332 | if ( update ) |
---|
| 2333 | { |
---|
| 2334 | //qDebug("%s %d updating", __FILE__, __LINE__); |
---|
| 2335 | _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() ); |
---|
| 2336 | |
---|
| 2337 | updateLineProperties(); |
---|
| 2338 | updateImage(); |
---|
| 2339 | |
---|
| 2340 | // do not send key press to terminal |
---|
| 2341 | emitKeyPressSignal = false; |
---|
| 2342 | } |
---|
| 2343 | } |
---|
| 2344 | |
---|
| 2345 | _screenWindow->setTrackOutput( true ); |
---|
| 2346 | |
---|
| 2347 | _actSel=0; // Key stroke implies a screen update, so TerminalDisplay won't |
---|
| 2348 | // know where the current selection is. |
---|
| 2349 | |
---|
| 2350 | if (_hasBlinkingCursor) |
---|
| 2351 | { |
---|
| 2352 | _blinkCursorTimer->start(BLINK_DELAY); |
---|
| 2353 | if (_cursorBlinking) |
---|
| 2354 | blinkCursorEvent(); |
---|
| 2355 | else |
---|
| 2356 | _cursorBlinking = false; |
---|
| 2357 | } |
---|
| 2358 | |
---|
| 2359 | if ( emitKeyPressSignal ) |
---|
| 2360 | emit keyPressedSignal(event); |
---|
| 2361 | |
---|
| 2362 | event->accept(); |
---|
| 2363 | } |
---|
| 2364 | |
---|
| 2365 | void TerminalDisplay::inputMethodEvent( QInputMethodEvent* event ) |
---|
| 2366 | { |
---|
| 2367 | QKeyEvent keyEvent(QEvent::KeyPress,0,Qt::NoModifier,event->commitString()); |
---|
| 2368 | emit keyPressedSignal(&keyEvent); |
---|
| 2369 | |
---|
| 2370 | _inputMethodData.preeditString = event->preeditString(); |
---|
| 2371 | update(preeditRect() | _inputMethodData.previousPreeditRect); |
---|
| 2372 | |
---|
| 2373 | event->accept(); |
---|
| 2374 | } |
---|
| 2375 | QVariant TerminalDisplay::inputMethodQuery( Qt::InputMethodQuery query ) const |
---|
| 2376 | { |
---|
| 2377 | const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0,0); |
---|
| 2378 | switch ( query ) |
---|
| 2379 | { |
---|
| 2380 | case Qt::ImMicroFocus: |
---|
| 2381 | return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1)); |
---|
| 2382 | break; |
---|
| 2383 | case Qt::ImFont: |
---|
| 2384 | return font(); |
---|
| 2385 | break; |
---|
| 2386 | case Qt::ImCursorPosition: |
---|
| 2387 | // return the cursor position within the current line |
---|
| 2388 | return cursorPos.x(); |
---|
| 2389 | break; |
---|
| 2390 | case Qt::ImSurroundingText: |
---|
| 2391 | { |
---|
| 2392 | // return the text from the current line |
---|
| 2393 | QString lineText; |
---|
| 2394 | QTextStream stream(&lineText); |
---|
| 2395 | PlainTextDecoder decoder; |
---|
| 2396 | decoder.begin(&stream); |
---|
| 2397 | decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]); |
---|
| 2398 | decoder.end(); |
---|
| 2399 | return lineText; |
---|
| 2400 | } |
---|
| 2401 | break; |
---|
| 2402 | case Qt::ImCurrentSelection: |
---|
| 2403 | return QString(); |
---|
| 2404 | break; |
---|
| 2405 | } |
---|
| 2406 | |
---|
| 2407 | return QVariant(); |
---|
| 2408 | } |
---|
| 2409 | |
---|
| 2410 | bool TerminalDisplay::event( QEvent *e ) |
---|
| 2411 | { |
---|
| 2412 | if ( e->type() == QEvent::ShortcutOverride ) |
---|
| 2413 | { |
---|
| 2414 | QKeyEvent* keyEvent = static_cast<QKeyEvent *>( e ); |
---|
| 2415 | |
---|
| 2416 | // a check to see if keyEvent->text() is empty is used |
---|
| 2417 | // to avoid intercepting the press of the modifier key on its own. |
---|
| 2418 | // |
---|
| 2419 | // this is important as it allows a press and release of the Alt key |
---|
| 2420 | // on its own to focus the menu bar, making it possible to |
---|
| 2421 | // work with the menu without using the mouse |
---|
| 2422 | if ( (keyEvent->modifiers() == Qt::AltModifier) && |
---|
| 2423 | !keyEvent->text().isEmpty() ) |
---|
| 2424 | { |
---|
| 2425 | keyEvent->accept(); |
---|
| 2426 | return true; |
---|
| 2427 | } |
---|
| 2428 | |
---|
| 2429 | // Override any of the following shortcuts because |
---|
| 2430 | // they are needed by the terminal |
---|
| 2431 | int keyCode = keyEvent->key() | keyEvent->modifiers(); |
---|
| 2432 | switch ( keyCode ) |
---|
| 2433 | { |
---|
| 2434 | // list is taken from the QLineEdit::event() code |
---|
| 2435 | case Qt::Key_Tab: |
---|
| 2436 | case Qt::Key_Delete: |
---|
| 2437 | case Qt::Key_Home: |
---|
| 2438 | case Qt::Key_End: |
---|
| 2439 | case Qt::Key_Backspace: |
---|
| 2440 | case Qt::Key_Left: |
---|
| 2441 | case Qt::Key_Right: |
---|
| 2442 | keyEvent->accept(); |
---|
| 2443 | return true; |
---|
| 2444 | } |
---|
| 2445 | } |
---|
| 2446 | return QWidget::event( e ); |
---|
| 2447 | } |
---|
| 2448 | |
---|
| 2449 | void TerminalDisplay::setBellMode(int mode) |
---|
| 2450 | { |
---|
| 2451 | _bellMode=mode; |
---|
| 2452 | } |
---|
| 2453 | |
---|
| 2454 | void TerminalDisplay::enableBell() |
---|
| 2455 | { |
---|
| 2456 | _allowBell = true; |
---|
| 2457 | } |
---|
| 2458 | |
---|
| 2459 | void TerminalDisplay::bell(const QString&) |
---|
| 2460 | { |
---|
| 2461 | if (_bellMode==NoBell) return; |
---|
| 2462 | |
---|
| 2463 | //limit the rate at which bells can occur |
---|
| 2464 | //...mainly for sound effects where rapid bells in sequence |
---|
| 2465 | //produce a horrible noise |
---|
| 2466 | if ( _allowBell ) |
---|
| 2467 | { |
---|
| 2468 | _allowBell = false; |
---|
| 2469 | QTimer::singleShot(500,this,SLOT(enableBell())); |
---|
| 2470 | |
---|
| 2471 | if (_bellMode==SystemBeepBell) |
---|
| 2472 | { |
---|
| 2473 | // KNotification::beep(); |
---|
| 2474 | } |
---|
| 2475 | else if (_bellMode==NotifyBell) |
---|
| 2476 | { |
---|
| 2477 | // KNotification::event("BellVisible", message,QPixmap(),this); |
---|
| 2478 | } |
---|
| 2479 | else if (_bellMode==VisualBell) |
---|
| 2480 | { |
---|
| 2481 | swapColorTable(); |
---|
| 2482 | QTimer::singleShot(200,this,SLOT(swapColorTable())); |
---|
| 2483 | } |
---|
| 2484 | } |
---|
| 2485 | } |
---|
| 2486 | |
---|
| 2487 | void TerminalDisplay::swapColorTable() |
---|
| 2488 | { |
---|
| 2489 | ColorEntry color = _colorTable[1]; |
---|
| 2490 | _colorTable[1]=_colorTable[0]; |
---|
| 2491 | _colorTable[0]= color; |
---|
| 2492 | _colorsInverted = !_colorsInverted; |
---|
| 2493 | update(); |
---|
| 2494 | } |
---|
| 2495 | |
---|
| 2496 | void TerminalDisplay::clearImage() |
---|
| 2497 | { |
---|
| 2498 | // We initialize _image[_imageSize] too. See makeImage() |
---|
| 2499 | for (int i = 0; i <= _imageSize; i++) |
---|
| 2500 | { |
---|
| 2501 | _image[i].character = ' '; |
---|
| 2502 | _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT, |
---|
| 2503 | DEFAULT_FORE_COLOR); |
---|
| 2504 | _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT, |
---|
| 2505 | DEFAULT_BACK_COLOR); |
---|
| 2506 | _image[i].rendition = DEFAULT_RENDITION; |
---|
| 2507 | } |
---|
| 2508 | } |
---|
| 2509 | |
---|
| 2510 | void TerminalDisplay::calcGeometry() |
---|
| 2511 | { |
---|
| 2512 | _scrollBar->resize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent), |
---|
| 2513 | contentsRect().height()); |
---|
| 2514 | switch(_scrollbarLocation) |
---|
| 2515 | { |
---|
| 2516 | case NoScrollBar : |
---|
| 2517 | _leftMargin = DEFAULT_LEFT_MARGIN; |
---|
| 2518 | _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN; |
---|
| 2519 | break; |
---|
| 2520 | case ScrollBarLeft : |
---|
| 2521 | _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width(); |
---|
| 2522 | _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); |
---|
| 2523 | _scrollBar->move(contentsRect().topLeft()); |
---|
| 2524 | break; |
---|
| 2525 | case ScrollBarRight: |
---|
| 2526 | _leftMargin = DEFAULT_LEFT_MARGIN; |
---|
| 2527 | _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); |
---|
| 2528 | _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width()-1,0)); |
---|
| 2529 | break; |
---|
| 2530 | } |
---|
| 2531 | |
---|
| 2532 | _topMargin = DEFAULT_TOP_MARGIN; |
---|
| 2533 | _contentHeight = contentsRect().height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1; |
---|
| 2534 | |
---|
| 2535 | if (!_isFixedSize) |
---|
| 2536 | { |
---|
| 2537 | // ensure that display is always at least one column wide |
---|
| 2538 | _columns = qMax(1,_contentWidth / _fontWidth); |
---|
| 2539 | _usedColumns = qMin(_usedColumns,_columns); |
---|
| 2540 | |
---|
| 2541 | // ensure that display is always at least one line high |
---|
| 2542 | _lines = qMax(1,_contentHeight / _fontHeight); |
---|
| 2543 | _usedLines = qMin(_usedLines,_lines); |
---|
| 2544 | } |
---|
| 2545 | } |
---|
| 2546 | |
---|
| 2547 | void TerminalDisplay::makeImage() |
---|
| 2548 | { |
---|
| 2549 | //qDebug("%s %d makeImage", __FILE__, __LINE__); |
---|
| 2550 | calcGeometry(); |
---|
| 2551 | |
---|
| 2552 | // confirm that array will be of non-zero size, since the painting code |
---|
| 2553 | // assumes a non-zero array length |
---|
| 2554 | Q_ASSERT( _lines > 0 && _columns > 0 ); |
---|
| 2555 | Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns ); |
---|
| 2556 | |
---|
| 2557 | _imageSize=_lines*_columns; |
---|
| 2558 | |
---|
| 2559 | // We over-commit one character so that we can be more relaxed in dealing with |
---|
| 2560 | // certain boundary conditions: _image[_imageSize] is a valid but unused position |
---|
| 2561 | _image = new Character[_imageSize+1]; |
---|
| 2562 | |
---|
| 2563 | clearImage(); |
---|
| 2564 | } |
---|
| 2565 | |
---|
| 2566 | // calculate the needed size |
---|
| 2567 | void TerminalDisplay::setSize(int columns, int lines) |
---|
| 2568 | { |
---|
| 2569 | //FIXME - Not quite correct, a small amount of additional space |
---|
| 2570 | // will be used for margins, the scrollbar etc. |
---|
| 2571 | // we need to allow for this so that '_size' does allow |
---|
| 2572 | // enough room for the specified number of columns and lines to fit |
---|
| 2573 | |
---|
| 2574 | QSize newSize = QSize( columns * _fontWidth , |
---|
| 2575 | lines * _fontHeight ); |
---|
| 2576 | |
---|
| 2577 | if ( newSize != size() ) |
---|
| 2578 | { |
---|
| 2579 | _size = newSize; |
---|
| 2580 | updateGeometry(); |
---|
| 2581 | } |
---|
| 2582 | } |
---|
| 2583 | |
---|
| 2584 | void TerminalDisplay::setFixedSize(int cols, int lins) |
---|
| 2585 | { |
---|
| 2586 | _isFixedSize = true; |
---|
| 2587 | |
---|
| 2588 | //ensure that display is at least one line by one column in size |
---|
| 2589 | _columns = qMax(1,cols); |
---|
| 2590 | _lines = qMax(1,lins); |
---|
| 2591 | _usedColumns = qMin(_usedColumns,_columns); |
---|
| 2592 | _usedLines = qMin(_usedLines,_lines); |
---|
| 2593 | |
---|
| 2594 | if (_image) |
---|
| 2595 | { |
---|
| 2596 | delete[] _image; |
---|
| 2597 | makeImage(); |
---|
| 2598 | } |
---|
| 2599 | setSize(cols, lins); |
---|
| 2600 | QWidget::setFixedSize(_size); |
---|
| 2601 | } |
---|
| 2602 | |
---|
| 2603 | QSize TerminalDisplay::sizeHint() const |
---|
| 2604 | { |
---|
| 2605 | return _size; |
---|
| 2606 | } |
---|
| 2607 | |
---|
| 2608 | |
---|
| 2609 | /* --------------------------------------------------------------------- */ |
---|
| 2610 | /* */ |
---|
| 2611 | /* Drag & Drop */ |
---|
| 2612 | /* */ |
---|
| 2613 | /* --------------------------------------------------------------------- */ |
---|
| 2614 | |
---|
| 2615 | void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event) |
---|
| 2616 | { |
---|
| 2617 | if (event->mimeData()->hasFormat("text/plain")) |
---|
| 2618 | event->acceptProposedAction(); |
---|
| 2619 | } |
---|
| 2620 | |
---|
| 2621 | void TerminalDisplay::dropEvent(QDropEvent* event) |
---|
| 2622 | { |
---|
| 2623 | // KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); |
---|
| 2624 | |
---|
| 2625 | QString dropText; |
---|
| 2626 | /* if (!urls.isEmpty()) |
---|
| 2627 | { |
---|
| 2628 | for ( int i = 0 ; i < urls.count() ; i++ ) |
---|
| 2629 | { |
---|
| 2630 | KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 ); |
---|
| 2631 | QString urlText; |
---|
| 2632 | |
---|
| 2633 | if (url.isLocalFile()) |
---|
| 2634 | urlText = url.path(); |
---|
| 2635 | else |
---|
| 2636 | urlText = url.url(); |
---|
| 2637 | |
---|
| 2638 | // in future it may be useful to be able to insert file names with drag-and-drop |
---|
| 2639 | // without quoting them (this only affects paths with spaces in) |
---|
| 2640 | urlText = KShell::quoteArg(urlText); |
---|
| 2641 | |
---|
| 2642 | dropText += urlText; |
---|
| 2643 | |
---|
| 2644 | if ( i != urls.count()-1 ) |
---|
| 2645 | dropText += ' '; |
---|
| 2646 | } |
---|
| 2647 | } |
---|
| 2648 | else |
---|
| 2649 | { |
---|
| 2650 | dropText = event->mimeData()->text(); |
---|
| 2651 | } |
---|
| 2652 | */ |
---|
| 2653 | if(event->mimeData()->hasFormat("text/plain")) |
---|
| 2654 | { |
---|
| 2655 | emit sendStringToEmu(dropText.toLocal8Bit()); |
---|
| 2656 | } |
---|
| 2657 | } |
---|
| 2658 | |
---|
| 2659 | void TerminalDisplay::doDrag() |
---|
| 2660 | { |
---|
| 2661 | dragInfo.state = diDragging; |
---|
| 2662 | dragInfo.dragObject = new QDrag(this); |
---|
| 2663 | QMimeData *mimeData = new QMimeData; |
---|
| 2664 | mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection)); |
---|
| 2665 | dragInfo.dragObject->setMimeData(mimeData); |
---|
| 2666 | dragInfo.dragObject->start(Qt::CopyAction); |
---|
| 2667 | // Don't delete the QTextDrag object. Qt will delete it when it's done with it. |
---|
| 2668 | } |
---|
| 2669 | |
---|
| 2670 | void TerminalDisplay::outputSuspended(bool suspended) |
---|
| 2671 | { |
---|
| 2672 | //create the label when this function is first called |
---|
| 2673 | if (!_outputSuspendedLabel) |
---|
| 2674 | { |
---|
| 2675 | //This label includes a link to an English language website |
---|
| 2676 | //describing the 'flow control' (Xon/Xoff) feature found in almost |
---|
| 2677 | //all terminal emulators. |
---|
| 2678 | //If there isn't a suitable article available in the target language the link |
---|
| 2679 | //can simply be removed. |
---|
| 2680 | _outputSuspendedLabel = new QLabel( ("<qt>Output has been " |
---|
| 2681 | "<a href=\"http://en.wikipedia.org/wiki/XON\">suspended</a>" |
---|
| 2682 | " by pressing Ctrl+S." |
---|
| 2683 | " Press <b>Ctrl+Q</b> to resume.</qt>"), |
---|
| 2684 | this ); |
---|
| 2685 | |
---|
| 2686 | QPalette palette(_outputSuspendedLabel->palette()); |
---|
| 2687 | |
---|
| 2688 | palette.setColor(QPalette::Normal, QPalette::WindowText, QColor(Qt::white)); |
---|
| 2689 | palette.setColor(QPalette::Normal, QPalette::Window, QColor(Qt::black)); |
---|
| 2690 | // KColorScheme::adjustForeground(palette,KColorScheme::NeutralText); |
---|
| 2691 | // KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); |
---|
| 2692 | _outputSuspendedLabel->setPalette(palette); |
---|
| 2693 | _outputSuspendedLabel->setAutoFillBackground(true); |
---|
| 2694 | _outputSuspendedLabel->setBackgroundRole(QPalette::Base); |
---|
| 2695 | _outputSuspendedLabel->setFont(QApplication::font()); |
---|
| 2696 | _outputSuspendedLabel->setMargin(5); |
---|
| 2697 | |
---|
| 2698 | //enable activation of "Xon/Xoff" link in label |
---|
| 2699 | _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | |
---|
| 2700 | Qt::LinksAccessibleByKeyboard); |
---|
| 2701 | _outputSuspendedLabel->setOpenExternalLinks(true); |
---|
| 2702 | _outputSuspendedLabel->setVisible(false); |
---|
| 2703 | |
---|
| 2704 | _gridLayout->addWidget(_outputSuspendedLabel); |
---|
| 2705 | _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding, |
---|
| 2706 | QSizePolicy::Expanding), |
---|
| 2707 | 1,0); |
---|
| 2708 | |
---|
| 2709 | } |
---|
| 2710 | |
---|
| 2711 | _outputSuspendedLabel->setVisible(suspended); |
---|
| 2712 | } |
---|
| 2713 | |
---|
| 2714 | uint TerminalDisplay::lineSpacing() const |
---|
| 2715 | { |
---|
| 2716 | return _lineSpacing; |
---|
| 2717 | } |
---|
| 2718 | |
---|
| 2719 | void TerminalDisplay::setLineSpacing(uint i) |
---|
| 2720 | { |
---|
| 2721 | _lineSpacing = i; |
---|
| 2722 | setVTFont(font()); // Trigger an update. |
---|
| 2723 | } |
---|
| 2724 | |
---|
| 2725 | //#include "moc_TerminalDisplay.cpp" |
---|