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" |
---|