comparison gui/src/terminal/TerminalDisplay.cpp @ 13506:c70511cf64ee

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