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