author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (unidiff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /core/apps/embeddedkonsole/TEWidget.cpp | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
Diffstat (limited to 'core/apps/embeddedkonsole/TEWidget.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/apps/embeddedkonsole/TEWidget.cpp | 1243 |
1 files changed, 1243 insertions, 0 deletions
diff --git a/core/apps/embeddedkonsole/TEWidget.cpp b/core/apps/embeddedkonsole/TEWidget.cpp new file mode 100644 index 0000000..dc83998 --- a/dev/null +++ b/core/apps/embeddedkonsole/TEWidget.cpp | |||
@@ -0,0 +1,1243 @@ | |||
1 | /* ------------------------------------------------------------------------ */ | ||
2 | /* */ | ||
3 | /* [TEWidget.C] Terminal Emulation Widget */ | ||
4 | /* */ | ||
5 | /* ------------------------------------------------------------------------ */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* ------------------------------------------------------------------------ */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | |||
19 | /*! \class TEWidget | ||
20 | |||
21 | \brief Visible screen contents | ||
22 | |||
23 | This class is responsible to map the `image' of a terminal emulation to the | ||
24 | display. All the dependency of the emulation to a specific GUI or toolkit is | ||
25 | localized here. Further, this widget has no knowledge about being part of an | ||
26 | emulation, it simply work within the terminal emulation framework by exposing | ||
27 | size and key events and by being ordered to show a new image. | ||
28 | |||
29 | <ul> | ||
30 | <li> The internal image has the size of the widget (evtl. rounded up) | ||
31 | <li> The external image used in setImage can have any size. | ||
32 | <li> (internally) the external image is simply copied to the internal | ||
33 | when a setImage happens. During a resizeEvent no painting is done | ||
34 | a paintEvent is expected to follow anyway. | ||
35 | </ul> | ||
36 | |||
37 | \sa TEScreen \sa Emulation | ||
38 | */ | ||
39 | |||
40 | /* FIXME: | ||
41 | - 'image' may also be used uninitialized (it isn't in fact) in resizeEvent | ||
42 | - 'font_a' not used in mouse events | ||
43 | - add destructor | ||
44 | */ | ||
45 | |||
46 | /* TODO | ||
47 | - evtl. be sensitive to `paletteChange' while using default colors. | ||
48 | - set different 'rounding' styles? I.e. have a mode to show clipped chars? | ||
49 | */ | ||
50 | |||
51 | // #include "config.h" | ||
52 | #include "TEWidget.h" | ||
53 | #include "session.h" | ||
54 | |||
55 | #include <qcursor.h> | ||
56 | #include <qregexp.h> | ||
57 | #include <qpainter.h> | ||
58 | #include <qclipboard.h> | ||
59 | #include <qstyle.h> | ||
60 | #include <qfile.h> | ||
61 | #include <qdragobject.h> | ||
62 | |||
63 | #include <stdio.h> | ||
64 | #include <stdlib.h> | ||
65 | #include <unistd.h> | ||
66 | #include <ctype.h> | ||
67 | #include <sys/stat.h> | ||
68 | #include <sys/types.h> | ||
69 | #include <signal.h> | ||
70 | |||
71 | #include <assert.h> | ||
72 | |||
73 | // #include "TEWidget.moc" | ||
74 | //#include <kapp.h> | ||
75 | //#include <kcursor.h> | ||
76 | //#include <kurl.h> | ||
77 | //#include <kdebug.h> | ||
78 | //#include <klocale.h> | ||
79 | |||
80 | #define HERE printf("%s(%d): %s\n",__FILE__,__LINE__,__FUNCTION__) | ||
81 | #define HCNT(Name) // { static int cnt = 1; printf("%s(%d): %s %d\n",__FILE__,__LINE__,Name,cnt++); } | ||
82 | |||
83 | #define loc(X,Y) ((Y)*columns+(X)) | ||
84 | |||
85 | //FIXME: the rim should normally be 1, 0 only when running in full screen mode. | ||
86 | #define rimX 0 // left/right rim width | ||
87 | #define rimY 0 // top/bottom rim high | ||
88 | |||
89 | #define SCRWIDTH 16 // width of the scrollbar | ||
90 | |||
91 | #define yMouseScroll 1 | ||
92 | // scroll increment used when dragging selection at top/bottom of window. | ||
93 | |||
94 | /* ------------------------------------------------------------------------- */ | ||
95 | /* */ | ||
96 | /* Colors */ | ||
97 | /* */ | ||
98 | /* ------------------------------------------------------------------------- */ | ||
99 | |||
100 | //FIXME: the default color table is in session.C now. | ||
101 | // We need a way to get rid of this one, here. | ||
102 | static const ColorEntry base_color_table[TABLE_COLORS] = | ||
103 | // The following are almost IBM standard color codes, with some slight | ||
104 | // gamma correction for the dim colors to compensate for bright X screens. | ||
105 | // It contains the 8 ansiterm/xterm colors in 2 intensities. | ||
106 | { | ||
107 | // Fixme: could add faint colors here, also. | ||
108 | // normal | ||
109 | ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback | ||
110 | ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red | ||
111 | ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow | ||
112 | ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta | ||
113 | ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White | ||
114 | // intensiv | ||
115 | ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), | ||
116 | ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), | ||
117 | ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), | ||
118 | ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0xFF), 0, 0 ), | ||
119 | ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) | ||
120 | }; | ||
121 | |||
122 | /* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) | ||
123 | |||
124 | Code 0 1 2 3 4 5 6 7 | ||
125 | ----------- ------- ------- ------- ------- ------- ------- ------- ------- | ||
126 | ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White | ||
127 | IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White | ||
128 | */ | ||
129 | |||
130 | QColor TEWidget::getDefaultBackColor() | ||
131 | { | ||
132 | return color_table[DEFAULT_BACK_COLOR].color; | ||
133 | } | ||
134 | |||
135 | const ColorEntry* TEWidget::getColorTable() const | ||
136 | { | ||
137 | return color_table; | ||
138 | } | ||
139 | |||
140 | const QPixmap *TEWidget::backgroundPixmap() | ||
141 | { | ||
142 | static QPixmap *bg = new QPixmap("~/qpim/main/pics/faded_bg.xpm"); | ||
143 | const QPixmap *pm = bg; | ||
144 | return pm; | ||
145 | } | ||
146 | |||
147 | void TEWidget::setColorTable(const ColorEntry table[]) | ||
148 | { | ||
149 | for (int i = 0; i < TABLE_COLORS; i++) color_table[i] = table[i]; | ||
150 | |||
151 | const QPixmap* pm = backgroundPixmap(); | ||
152 | if (!pm) setBackgroundColor(color_table[DEFAULT_BACK_COLOR].color); | ||
153 | update(); | ||
154 | } | ||
155 | |||
156 | //FIXME: add backgroundPixmapChanged. | ||
157 | |||
158 | /* ------------------------------------------------------------------------- */ | ||
159 | /* */ | ||
160 | /* Font */ | ||
161 | /* */ | ||
162 | /* ------------------------------------------------------------------------- */ | ||
163 | |||
164 | /* | ||
165 | The VT100 has 32 special graphical characters. The usual vt100 extended | ||
166 | xterm fonts have these at 0x00..0x1f. | ||
167 | |||
168 | QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals | ||
169 | come in here as proper unicode characters. | ||
170 | |||
171 | We treat non-iso10646 fonts as VT100 extended and do the requiered mapping | ||
172 | from unicode to 0x00..0x1f. The remaining translation is then left to the | ||
173 | QCodec. | ||
174 | */ | ||
175 | |||
176 | // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. | ||
177 | |||
178 | unsigned short vt100_graphics[32] = | ||
179 | { // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 | ||
180 | 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, | ||
181 | 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, | ||
182 | 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, | ||
183 | 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 | ||
184 | }; | ||
185 | |||
186 | static QChar vt100extended(QChar c) | ||
187 | { | ||
188 | switch (c.unicode()) | ||
189 | { | ||
190 | case 0x25c6 : return 1; | ||
191 | case 0x2592 : return 2; | ||
192 | case 0x2409 : return 3; | ||
193 | case 0x240c : return 4; | ||
194 | case 0x240d : return 5; | ||
195 | case 0x240a : return 6; | ||
196 | case 0x00b0 : return 7; | ||
197 | case 0x00b1 : return 8; | ||
198 | case 0x2424 : return 9; | ||
199 | case 0x240b : return 10; | ||
200 | case 0x2518 : return 11; | ||
201 | case 0x2510 : return 12; | ||
202 | case 0x250c : return 13; | ||
203 | case 0x2514 : return 14; | ||
204 | case 0x253c : return 15; | ||
205 | case 0xf800 : return 16; | ||
206 | case 0xf801 : return 17; | ||
207 | case 0x2500 : return 18; | ||
208 | case 0xf803 : return 19; | ||
209 | case 0xf804 : return 20; | ||
210 | case 0x251c : return 21; | ||
211 | case 0x2524 : return 22; | ||
212 | case 0x2534 : return 23; | ||
213 | case 0x252c : return 24; | ||
214 | case 0x2502 : return 25; | ||
215 | case 0x2264 : return 26; | ||
216 | case 0x2265 : return 27; | ||
217 | case 0x03c0 : return 28; | ||
218 | case 0x2260 : return 29; | ||
219 | case 0x00a3 : return 30; | ||
220 | case 0x00b7 : return 31; | ||
221 | } | ||
222 | return c; | ||
223 | } | ||
224 | |||
225 | static QChar identicalMap(QChar c) | ||
226 | { | ||
227 | return c; | ||
228 | } | ||
229 | |||
230 | void TEWidget::fontChange(const QFont &) | ||
231 | { | ||
232 | QFontMetrics fm(font()); | ||
233 | font_h = fm.height(); | ||
234 | font_w = fm.maxWidth(); | ||
235 | font_a = fm.ascent(); | ||
236 | //printf("font_h: %d\n",font_h); | ||
237 | //printf("font_w: %d\n",font_w); | ||
238 | //printf("font_a: %d\n",font_a); | ||
239 | //printf("charset: %s\n",QFont::encodingName(font().charSet()).ascii()); | ||
240 | //printf("rawname: %s\n",font().rawName().ascii()); | ||
241 | fontMap = | ||
242 | #if QT_VERSION < 300 | ||
243 | strcmp(QFont::encodingName(font().charSet()).ascii(),"iso10646") | ||
244 | ? vt100extended | ||
245 | : | ||
246 | #endif | ||
247 | identicalMap; | ||
248 | propagateSize(); | ||
249 | update(); | ||
250 | } | ||
251 | |||
252 | void TEWidget::setVTFont(const QFont& f) | ||
253 | { | ||
254 | QFrame::setFont(f); | ||
255 | } | ||
256 | |||
257 | QFont TEWidget::getVTFont() | ||
258 | { | ||
259 | return font(); | ||
260 | } | ||
261 | |||
262 | void TEWidget::setFont(const QFont &) | ||
263 | { | ||
264 | // ignore font change request if not coming from konsole itself | ||
265 | } | ||
266 | |||
267 | /* ------------------------------------------------------------------------- */ | ||
268 | /* */ | ||
269 | /* Constructor / Destructor */ | ||
270 | /* */ | ||
271 | /* ------------------------------------------------------------------------- */ | ||
272 | |||
273 | TEWidget::TEWidget(QWidget *parent, const char *name) : QFrame(parent,name) | ||
274 | { | ||
275 | #ifndef QT_NO_CLIPBOARD | ||
276 | cb = QApplication::clipboard(); | ||
277 | QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), | ||
278 | this, SLOT(onClearSelection()) ); | ||
279 | #endif | ||
280 | |||
281 | scrollbar = new QScrollBar(this); | ||
282 | scrollbar->setCursor( arrowCursor ); | ||
283 | connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | ||
284 | scrollLoc = SCRNONE; | ||
285 | |||
286 | blinkT = new QTimer(this); | ||
287 | connect(blinkT, SIGNAL(timeout()), this, SLOT(blinkEvent())); | ||
288 | // blinking = FALSE; | ||
289 | blinking = TRUE; | ||
290 | |||
291 | resizing = FALSE; | ||
292 | actSel = 0; | ||
293 | image = 0; | ||
294 | lines = 1; | ||
295 | columns = 1; | ||
296 | font_w = 1; | ||
297 | font_h = 1; | ||
298 | font_a = 1; | ||
299 | word_selection_mode = FALSE; | ||
300 | |||
301 | setMouseMarks(TRUE); | ||
302 | setVTFont( QFont("fixed") ); | ||
303 | setColorTable(base_color_table); // init color table | ||
304 | |||
305 | qApp->installEventFilter( this ); //FIXME: see below | ||
306 | // KCursor::setAutoHideCursor( this, true ); | ||
307 | |||
308 | // Init DnD //////////////////////////////////////////////////////////////// | ||
309 | currentSession = NULL; | ||
310 | // setAcceptDrops(true); // attempt | ||
311 | // m_drop = new QPopupMenu(this); | ||
312 | // m_drop->insertItem( QString("Paste"), 0); | ||
313 | // m_drop->insertItem( QString("cd"), 1); | ||
314 | // connect(m_drop, SIGNAL(activated(int)), SLOT(drop_menu_activated(int))); | ||
315 | |||
316 | // we need focus so that the auto-hide cursor feature works | ||
317 | setFocus(); | ||
318 | setFocusPolicy( WheelFocus ); | ||
319 | } | ||
320 | |||
321 | //FIXME: make proper destructor | ||
322 | // Here's a start (David) | ||
323 | TEWidget::~TEWidget() | ||
324 | { | ||
325 | qApp->removeEventFilter( this ); | ||
326 | if (image) free(image); | ||
327 | } | ||
328 | |||
329 | /* ------------------------------------------------------------------------- */ | ||
330 | /* */ | ||
331 | /* Display Operations */ | ||
332 | /* */ | ||
333 | /* ------------------------------------------------------------------------- */ | ||
334 | |||
335 | /*! | ||
336 | attributed string draw primitive | ||
337 | */ | ||
338 | |||
339 | void TEWidget::drawAttrStr(QPainter &paint, QRect rect, | ||
340 | QString& str, ca attr, BOOL pm, BOOL clear) | ||
341 | { | ||
342 | if (pm && color_table[attr.b].transparent) | ||
343 | { | ||
344 | paint.setBackgroundMode( TransparentMode ); | ||
345 | if (clear) erase(rect); | ||
346 | } | ||
347 | else | ||
348 | { | ||
349 | if (blinking) | ||
350 | paint.fillRect(rect, color_table[attr.b].color); | ||
351 | else | ||
352 | { | ||
353 | paint.setBackgroundMode( OpaqueMode ); | ||
354 | paint.setBackgroundColor( color_table[attr.b].color ); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | if (color_table[attr.f].bold) | ||
359 | paint.setPen(QColor( 0x8F, 0x00, 0x00 )); | ||
360 | else | ||
361 | paint.setPen(color_table[attr.f].color); | ||
362 | |||
363 | paint.drawText(rect.x(),rect.y()+font_a, str); | ||
364 | |||
365 | if (attr.r & RE_UNDERLINE) | ||
366 | paint.drawLine(rect.left(), rect.y()+font_a+1, rect.right(),rect.y()+font_a+1 ); | ||
367 | } | ||
368 | |||
369 | /*! | ||
370 | The image can only be set completely. | ||
371 | |||
372 | The size of the new image may or may not match the size of the widget. | ||
373 | */ | ||
374 | |||
375 | void TEWidget::setImage(const ca* const newimg, int lines, int columns) | ||
376 | { int y,x,len; | ||
377 | const QPixmap* pm = backgroundPixmap(); | ||
378 | QPainter paint; | ||
379 | setUpdatesEnabled(FALSE); | ||
380 | paint.begin( this ); | ||
381 | HCNT("setImage"); | ||
382 | |||
383 | QPoint tL = contentsRect().topLeft(); | ||
384 | int tLx = tL.x(); | ||
385 | int tLy = tL.y(); | ||
386 | hasBlinker = FALSE; | ||
387 | |||
388 | int cf = -1; // undefined | ||
389 | int cb = -1; // undefined | ||
390 | int cr = -1; // undefined | ||
391 | |||
392 | int lins = QMIN(this->lines, QMAX(0,lines )); | ||
393 | int cols = QMIN(this->columns,QMAX(0,columns)); | ||
394 | QChar *disstrU = new QChar[cols]; | ||
395 | |||
396 | //{ static int cnt = 0; printf("setImage %d\n",cnt++); } | ||
397 | for (y = 0; y < lins; y++) | ||
398 | { | ||
399 | const ca* lcl = &image[y*this->columns]; | ||
400 | const ca* const ext = &newimg[y*columns]; | ||
401 | if (!resizing) // not while resizing, we're expecting a paintEvent | ||
402 | for (x = 0; x < cols; x++) | ||
403 | { | ||
404 | hasBlinker |= (ext[x].r & RE_BLINK); | ||
405 | if (ext[x] != lcl[x]) | ||
406 | { | ||
407 | cr = ext[x].r; | ||
408 | cb = ext[x].b; | ||
409 | if (ext[x].f != cf) cf = ext[x].f; | ||
410 | int lln = cols - x; | ||
411 | disstrU[0] = fontMap(ext[x+0].c); | ||
412 | for (len = 1; len < lln; len++) | ||
413 | { | ||
414 | if (ext[x+len].f != cf || ext[x+len].b != cb || ext[x+len].r != cr || | ||
415 | ext[x+len] == lcl[x+len] ) | ||
416 | break; | ||
417 | disstrU[len] = fontMap(ext[x+len].c); | ||
418 | } | ||
419 | QString unistr(disstrU,len); | ||
420 | drawAttrStr(paint, | ||
421 | QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), | ||
422 | unistr, ext[x], pm != NULL, true); | ||
423 | x += len - 1; | ||
424 | } | ||
425 | } | ||
426 | // finally, make `image' become `newimg'. | ||
427 | memcpy((void*)lcl,(const void*)ext,cols*sizeof(ca)); | ||
428 | } | ||
429 | drawFrame( &paint ); | ||
430 | paint.end(); | ||
431 | setUpdatesEnabled(TRUE); | ||
432 | if ( hasBlinker && !blinkT->isActive()) blinkT->start(1000); // 1000 ms | ||
433 | if (!hasBlinker && blinkT->isActive()) { blinkT->stop(); blinking = FALSE; } | ||
434 | delete [] disstrU; | ||
435 | } | ||
436 | |||
437 | // paint Event //////////////////////////////////////////////////// | ||
438 | |||
439 | /*! | ||
440 | The difference of this routine vs. the `setImage' is, | ||
441 | that the drawing does not include a difference analysis | ||
442 | between the old and the new image. Instead, the internal | ||
443 | image is used and the painting bound by the PaintEvent box. | ||
444 | */ | ||
445 | |||
446 | void TEWidget::paintEvent( QPaintEvent* pe ) | ||
447 | { | ||
448 | |||
449 | //{ static int cnt = 0; printf("paint %d\n",cnt++); } | ||
450 | const QPixmap* pm = backgroundPixmap(); | ||
451 | QPainter paint; | ||
452 | setUpdatesEnabled(FALSE); | ||
453 | paint.begin( this ); | ||
454 | paint.setBackgroundMode( TransparentMode ); | ||
455 | HCNT("paintEvent"); | ||
456 | |||
457 | // Note that the actual widget size can be slightly larger | ||
458 | // that the image (the size is truncated towards the smaller | ||
459 | // number of characters in `resizeEvent'. The paint rectangle | ||
460 | // can thus be larger than the image, but less then the size | ||
461 | // of one character. | ||
462 | |||
463 | QRect rect = pe->rect().intersect(contentsRect()); | ||
464 | |||
465 | QPoint tL = contentsRect().topLeft(); | ||
466 | int tLx = tL.x(); | ||
467 | int tLy = tL.y(); | ||
468 | |||
469 | int lux = QMIN(columns-1, QMAX(0,(rect.left() - tLx - blX ) / font_w)); | ||
470 | int luy = QMIN(lines-1, QMAX(0,(rect.top() - tLy - bY ) / font_h)); | ||
471 | int rlx = QMIN(columns-1, QMAX(0,(rect.right() - tLx - blX ) / font_w)); | ||
472 | int rly = QMIN(lines-1, QMAX(0,(rect.bottom() - tLy - bY ) / font_h)); | ||
473 | |||
474 | /* | ||
475 | printf("paintEvent: %d..%d, %d..%d (%d..%d, %d..%d)\n",lux,rlx,luy,rly, | ||
476 | rect.left(), rect.right(), rect.top(), rect.bottom()); | ||
477 | */ | ||
478 | |||
479 | // if (pm != NULL && color_table[image->b].transparent) | ||
480 | // erase(rect); | ||
481 | // BL: I have no idea why we need this, and it breaks the refresh. | ||
482 | |||
483 | QChar *disstrU = new QChar[columns]; | ||
484 | for (int y = luy; y <= rly; y++) | ||
485 | for (int x = lux; x <= rlx; x++) | ||
486 | { | ||
487 | int len = 1; | ||
488 | disstrU[0] = fontMap(image[loc(x,y)].c); | ||
489 | int cf = image[loc(x,y)].f; | ||
490 | int cb = image[loc(x,y)].b; | ||
491 | int cr = image[loc(x,y)].r; | ||
492 | while (x+len <= rlx && | ||
493 | image[loc(x+len,y)].f == cf && | ||
494 | image[loc(x+len,y)].b == cb && | ||
495 | image[loc(x+len,y)].r == cr ) | ||
496 | { | ||
497 | disstrU[len] = fontMap(image[loc(x+len,y)].c); | ||
498 | len += 1; | ||
499 | } | ||
500 | QString unistr(disstrU,len); | ||
501 | drawAttrStr(paint, | ||
502 | QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), | ||
503 | unistr, image[loc(x,y)], pm != NULL, false); | ||
504 | x += len - 1; | ||
505 | } | ||
506 | delete [] disstrU; | ||
507 | drawFrame( &paint ); | ||
508 | paint.end(); | ||
509 | setUpdatesEnabled(TRUE); | ||
510 | } | ||
511 | |||
512 | void TEWidget::blinkEvent() | ||
513 | { | ||
514 | blinking = !blinking; | ||
515 | repaint(FALSE); | ||
516 | } | ||
517 | |||
518 | /* ------------------------------------------------------------------------- */ | ||
519 | /* */ | ||
520 | /* Resizing */ | ||
521 | /* */ | ||
522 | /* ------------------------------------------------------------------------- */ | ||
523 | |||
524 | void TEWidget::resizeEvent(QResizeEvent* ev) | ||
525 | { | ||
526 | //printf("resize: %d,%d\n",ev->size().width(),ev->size().height()); | ||
527 | //printf("approx: %d,%d\n",ev->size().width()/font_w,ev->size().height()/font_h); | ||
528 | //printf("leaves: %d,%d\n",ev->size().width()%font_w,ev->size().height()%font_h); | ||
529 | //printf("curren: %d,%d\n",width(),height()); | ||
530 | HCNT("resizeEvent"); | ||
531 | |||
532 | // see comment in `paintEvent' concerning the rounding. | ||
533 | //FIXME: could make a routine here; check width(),height() | ||
534 | assert(ev->size().width() == width()); | ||
535 | assert(ev->size().height() == height()); | ||
536 | |||
537 | propagateSize(); | ||
538 | } | ||
539 | |||
540 | void TEWidget::propagateSize() | ||
541 | { | ||
542 | ca* oldimg = image; | ||
543 | int oldlin = lines; | ||
544 | int oldcol = columns; | ||
545 | makeImage(); | ||
546 | // we copy the old image to reduce flicker | ||
547 | int lins = QMIN(oldlin,lines); | ||
548 | int cols = QMIN(oldcol,columns); | ||
549 | if (oldimg) | ||
550 | { | ||
551 | for (int lin = 0; lin < lins; lin++) | ||
552 | memcpy((void*)&image[columns*lin], | ||
553 | (void*)&oldimg[oldcol*lin],cols*sizeof(ca)); | ||
554 | free(oldimg); //FIXME: try new,delete | ||
555 | } | ||
556 | else | ||
557 | clearImage(); | ||
558 | |||
559 | //NOTE: control flows from the back through the chest right into the eye. | ||
560 | // `emu' will call back via `setImage'. | ||
561 | |||
562 | resizing = TRUE; | ||
563 | emit changedImageSizeSignal(lines, columns); // expose resizeEvent | ||
564 | resizing = FALSE; | ||
565 | } | ||
566 | |||
567 | /* ------------------------------------------------------------------------- */ | ||
568 | /* */ | ||
569 | /* Scrollbar */ | ||
570 | /* */ | ||
571 | /* ------------------------------------------------------------------------- */ | ||
572 | |||
573 | void TEWidget::scrollChanged(int) | ||
574 | { | ||
575 | emit changedHistoryCursor(scrollbar->value()); //expose | ||
576 | } | ||
577 | |||
578 | void TEWidget::setScroll(int cursor, int slines) | ||
579 | { | ||
580 | disconnect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | ||
581 | scrollbar->setRange(0,slines); | ||
582 | scrollbar->setSteps(1,lines); | ||
583 | scrollbar->setValue(cursor); | ||
584 | connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); | ||
585 | } | ||
586 | |||
587 | void TEWidget::setScrollbarLocation(int loc) | ||
588 | { | ||
589 | if (scrollLoc == loc) return; // quickly | ||
590 | scrollLoc = loc; | ||
591 | propagateSize(); | ||
592 | update(); | ||
593 | } | ||
594 | |||
595 | /* ------------------------------------------------------------------------- */ | ||
596 | /* */ | ||
597 | /* Mouse */ | ||
598 | /* */ | ||
599 | /* ------------------------------------------------------------------------- */ | ||
600 | |||
601 | /*! | ||
602 | Three different operations can be performed using the mouse, and the | ||
603 | routines in this section serve all of them: | ||
604 | |||
605 | 1) The press/release events are exposed to the application | ||
606 | 2) Marking (press and move left button) and Pasting (press middle button) | ||
607 | 3) The right mouse button is used from the configuration menu | ||
608 | |||
609 | NOTE: During the marking process we attempt to keep the cursor within | ||
610 | the bounds of the text as being displayed by setting the mouse position | ||
611 | whenever the mouse has left the text area. | ||
612 | |||
613 | Two reasons to do so: | ||
614 | 1) QT does not allow the `grabMouse' to confine-to the TEWidget. | ||
615 | Thus a `XGrapPointer' would have to be used instead. | ||
616 | 2) Even if so, this would not help too much, since the text area | ||
617 | of the TEWidget is normally not identical with it's bounds. | ||
618 | |||
619 | The disadvantage of the current handling is, that the mouse can visibly | ||
620 | leave the bounds of the widget and is then moved back. Because of the | ||
621 | current construction, and the reasons mentioned above, we cannot do better | ||
622 | without changing the overall construction. | ||
623 | */ | ||
624 | |||
625 | /*! | ||
626 | */ | ||
627 | |||
628 | void TEWidget::mousePressEvent(QMouseEvent* ev) | ||
629 | { | ||
630 | //printf("press [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); | ||
631 | if ( !contentsRect().contains(ev->pos()) ) return; | ||
632 | QPoint tL = contentsRect().topLeft(); | ||
633 | int tLx = tL.x(); | ||
634 | int tLy = tL.y(); | ||
635 | |||
636 | word_selection_mode = FALSE; | ||
637 | |||
638 | //printf("press top left [%d,%d] by=%d\n",tLx,tLy, bY); | ||
639 | if ( ev->button() == LeftButton) | ||
640 | { | ||
641 | QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); | ||
642 | |||
643 | if ( ev->state() & ControlButton ) preserve_line_breaks = FALSE ; | ||
644 | |||
645 | if (mouse_marks || (ev->state() & ShiftButton)) | ||
646 | { | ||
647 | emit clearSelectionSignal(); | ||
648 | iPntSel = pntSel = pos; | ||
649 | actSel = 1; // left mouse button pressed but nothing selected yet. | ||
650 | grabMouse( /*crossCursor*/ ); // handle with care! | ||
651 | } | ||
652 | else | ||
653 | { | ||
654 | emit mouseSignal( 0, pos.x() + 1, pos.y() + 1 ); // left button | ||
655 | } | ||
656 | } | ||
657 | if ( ev->button() == MidButton ) | ||
658 | { | ||
659 | emitSelection(); | ||
660 | } | ||
661 | if ( ev->button() == RightButton ) // Configure | ||
662 | { | ||
663 | emit configureRequest( this, ev->state()&(ShiftButton|ControlButton), ev->x(), ev->y() ); | ||
664 | } | ||
665 | } | ||
666 | |||
667 | void TEWidget::mouseMoveEvent(QMouseEvent* ev) | ||
668 | { | ||
669 | // for auto-hiding the cursor, we need mouseTracking | ||
670 | if (ev->state() == NoButton ) return; | ||
671 | |||
672 | if (actSel == 0) return; | ||
673 | |||
674 | // don't extend selection while pasting | ||
675 | if (ev->state() & MidButton) return; | ||
676 | |||
677 | //if ( !contentsRect().contains(ev->pos()) ) return; | ||
678 | QPoint tL = contentsRect().topLeft(); | ||
679 | int tLx = tL.x(); | ||
680 | int tLy = tL.y(); | ||
681 | int scroll = scrollbar->value(); | ||
682 | |||
683 | // we're in the process of moving the mouse with the left button pressed | ||
684 | // the mouse cursor will kept catched within the bounds of the text in | ||
685 | // this widget. | ||
686 | |||
687 | // Adjust position within text area bounds. See FIXME above. | ||
688 | QPoint pos = ev->pos(); | ||
689 | if ( pos.x() < tLx+blX ) pos.setX( tLx+blX ); | ||
690 | if ( pos.x() > tLx+blX+columns*font_w-1 ) pos.setX( tLx+blX+columns*font_w ); | ||
691 | if ( pos.y() < tLy+bY ) pos.setY( tLy+bY ); | ||
692 | if ( pos.y() > tLy+bY+lines*font_h-1 ) pos.setY( tLy+bY+lines*font_h-1 ); | ||
693 | // check if we produce a mouse move event by this | ||
694 | if ( pos != ev->pos() ) cursor().setPos(mapToGlobal(pos)); | ||
695 | |||
696 | if ( pos.y() == tLy+bY+lines*font_h-1 ) | ||
697 | { | ||
698 | scrollbar->setValue(scrollbar->value()+yMouseScroll); // scrollforward | ||
699 | } | ||
700 | if ( pos.y() == tLy+bY ) | ||
701 | { | ||
702 | scrollbar->setValue(scrollbar->value()-yMouseScroll); // scrollback | ||
703 | } | ||
704 | |||
705 | QPoint here = QPoint((pos.x()-tLx-blX)/font_w,(pos.y()-tLy-bY)/font_h); | ||
706 | QPoint ohere; | ||
707 | bool swapping = FALSE; | ||
708 | |||
709 | if ( word_selection_mode ) | ||
710 | { | ||
711 | // Extend to word boundaries | ||
712 | int i; | ||
713 | int selClass; | ||
714 | |||
715 | bool left_not_right = ( here.y() < iPntSel.y() || | ||
716 | here.y() == iPntSel.y() && here.x() < iPntSel.x() ); | ||
717 | bool old_left_not_right = ( pntSel.y() < iPntSel.y() || | ||
718 | pntSel.y() == iPntSel.y() && pntSel.x() < iPntSel.x() ); | ||
719 | swapping = left_not_right != old_left_not_right; | ||
720 | |||
721 | // Find left (left_not_right ? from here : from start) | ||
722 | QPoint left = left_not_right ? here : iPntSel; | ||
723 | i = loc(left.x(),left.y()); | ||
724 | selClass = charClass(image[i].c); | ||
725 | while ( left.x() > 0 && charClass(image[i-1].c) == selClass ) | ||
726 | { i--; left.rx()--; } | ||
727 | |||
728 | // Find left (left_not_right ? from start : from here) | ||
729 | QPoint right = left_not_right ? iPntSel : here; | ||
730 | i = loc(right.x(),right.y()); | ||
731 | selClass = charClass(image[i].c); | ||
732 | while ( right.x() < columns-1 && charClass(image[i+1].c) == selClass ) | ||
733 | { i++; right.rx()++; } | ||
734 | |||
735 | // Pick which is start (ohere) and which is extension (here) | ||
736 | if ( left_not_right ) | ||
737 | { | ||
738 | here = left; ohere = right; | ||
739 | } | ||
740 | else | ||
741 | { | ||
742 | here = right; ohere = left; | ||
743 | } | ||
744 | } | ||
745 | |||
746 | if (here == pntSel && scroll == scrollbar->value()) return; // not moved | ||
747 | |||
748 | if ( word_selection_mode ) { | ||
749 | if ( actSel < 2 || swapping ) { | ||
750 | emit beginSelectionSignal( ohere.x(), ohere.y() ); | ||
751 | } | ||
752 | } else if ( actSel < 2 ) { | ||
753 | emit beginSelectionSignal( pntSel.x(), pntSel.y() ); | ||
754 | } | ||
755 | |||
756 | actSel = 2; // within selection | ||
757 | pntSel = here; | ||
758 | emit extendSelectionSignal( here.x(), here.y() ); | ||
759 | } | ||
760 | |||
761 | void TEWidget::mouseReleaseEvent(QMouseEvent* ev) | ||
762 | { | ||
763 | //printf("release [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); | ||
764 | if ( ev->button() == LeftButton) | ||
765 | { | ||
766 | if ( actSel > 1 ) emit endSelectionSignal(preserve_line_breaks); | ||
767 | preserve_line_breaks = TRUE; | ||
768 | actSel = 0; | ||
769 | |||
770 | //FIXME: emits a release event even if the mouse is | ||
771 | // outside the range. The procedure used in `mouseMoveEvent' | ||
772 | // applies here, too. | ||
773 | |||
774 | QPoint tL = contentsRect().topLeft(); | ||
775 | int tLx = tL.x(); | ||
776 | int tLy = tL.y(); | ||
777 | |||
778 | if (!mouse_marks && !(ev->state() & ShiftButton)) | ||
779 | emit mouseSignal( 3, // release | ||
780 | (ev->x()-tLx-blX)/font_w + 1, | ||
781 | (ev->y()-tLy-bY)/font_h + 1 ); | ||
782 | releaseMouse(); | ||
783 | } | ||
784 | } | ||
785 | |||
786 | void TEWidget::mouseDoubleClickEvent(QMouseEvent* ev) | ||
787 | { | ||
788 | if ( ev->button() != LeftButton) return; | ||
789 | |||
790 | QPoint tL = contentsRect().topLeft(); | ||
791 | int tLx = tL.x(); | ||
792 | int tLy = tL.y(); | ||
793 | QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); | ||
794 | |||
795 | // pass on double click as two clicks. | ||
796 | if (!mouse_marks && !(ev->state() & ShiftButton)) | ||
797 | { | ||
798 | emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button | ||
799 | emit mouseSignal( 3, pos.x()+1, pos.y()+1 ); // release | ||
800 | emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button | ||
801 | return; | ||
802 | } | ||
803 | |||
804 | |||
805 | emit clearSelectionSignal(); | ||
806 | QPoint bgnSel = pos; | ||
807 | QPoint endSel = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); | ||
808 | int i = loc(bgnSel.x(),bgnSel.y()); | ||
809 | iPntSel = bgnSel; | ||
810 | |||
811 | word_selection_mode = TRUE; | ||
812 | |||
813 | // find word boundaries... | ||
814 | int selClass = charClass(image[i].c); | ||
815 | { | ||
816 | // set the start... | ||
817 | int x = bgnSel.x(); | ||
818 | while ( x > 0 && charClass(image[i-1].c) == selClass ) | ||
819 | { i--; x--; } | ||
820 | bgnSel.setX(x); | ||
821 | emit beginSelectionSignal( bgnSel.x(), bgnSel.y() ); | ||
822 | |||
823 | // set the end... | ||
824 | i = loc( endSel.x(), endSel.y() ); | ||
825 | x = endSel.x(); | ||
826 | while( x < columns-1 && charClass(image[i+1].c) == selClass ) | ||
827 | { i++; x++ ; } | ||
828 | endSel.setX(x); | ||
829 | actSel = 2; // within selection | ||
830 | emit extendSelectionSignal( endSel.x(), endSel.y() ); | ||
831 | emit endSelectionSignal(preserve_line_breaks); | ||
832 | preserve_line_breaks = TRUE; | ||
833 | } | ||
834 | } | ||
835 | |||
836 | void TEWidget::focusInEvent( QFocusEvent * ) | ||
837 | { | ||
838 | // do nothing, to prevent repainting | ||
839 | } | ||
840 | |||
841 | |||
842 | void TEWidget::focusOutEvent( QFocusEvent * ) | ||
843 | { | ||
844 | // do nothing, to prevent repainting | ||
845 | } | ||
846 | |||
847 | bool TEWidget::focusNextPrevChild( bool next ) | ||
848 | { | ||
849 | if (next) | ||
850 | return false; // This disables changing the active part in konqueror | ||
851 | // when pressing Tab | ||
852 | return QFrame::focusNextPrevChild( next ); | ||
853 | } | ||
854 | |||
855 | |||
856 | int TEWidget::charClass(char ch) const | ||
857 | { | ||
858 | // This might seem like overkill, but imagine if ch was a Unicode | ||
859 | // character (Qt 2.0 QChar) - it might then be sensible to separate | ||
860 | // the different language ranges, etc. | ||
861 | |||
862 | if ( isspace(ch) ) return ' '; | ||
863 | |||
864 | static const char *word_characters = ":@-./_~"; | ||
865 | if ( isalnum(ch) || strchr(word_characters, ch) ) | ||
866 | return 'a'; | ||
867 | |||
868 | // Everything else is weird | ||
869 | return 1; | ||
870 | } | ||
871 | |||
872 | void TEWidget::setMouseMarks(bool on) | ||
873 | { | ||
874 | mouse_marks = on; | ||
875 | setCursor( mouse_marks ? ibeamCursor : arrowCursor ); | ||
876 | } | ||
877 | |||
878 | /* ------------------------------------------------------------------------- */ | ||
879 | /* */ | ||
880 | /* Clipboard */ | ||
881 | /* */ | ||
882 | /* ------------------------------------------------------------------------- */ | ||
883 | |||
884 | #undef KeyPress | ||
885 | |||
886 | void TEWidget::emitSelection() | ||
887 | // Paste Clipboard by simulating keypress events | ||
888 | { | ||
889 | #ifndef QT_NO_CLIPBOARD | ||
890 | QString text = QApplication::clipboard()->text(); | ||
891 | if ( ! text.isNull() ) | ||
892 | { | ||
893 | text.replace(QRegExp("\n"), "\r"); | ||
894 | QKeyEvent e(QEvent::KeyPress, 0, -1, 0, text); | ||
895 | emit keyPressedSignal(&e); // expose as a big fat keypress event | ||
896 | emit clearSelectionSignal(); | ||
897 | } | ||
898 | #endif | ||
899 | } | ||
900 | |||
901 | void TEWidget::emitText(QString text) | ||
902 | { | ||
903 | QKeyEvent e(QEvent::KeyPress, 0, -1, 0, text); | ||
904 | emit keyPressedSignal(&e); // expose as a big fat keypress event | ||
905 | } | ||
906 | |||
907 | void TEWidget::pasteClipboard( ) | ||
908 | { | ||
909 | emitSelection(); | ||
910 | } | ||
911 | |||
912 | void TEWidget::setSelection(const QString& t) | ||
913 | { | ||
914 | #ifndef QT_NO_CLIPBOARD | ||
915 | // Disconnect signal while WE set the clipboard | ||
916 | QObject *cb = QApplication::clipboard(); | ||
917 | QObject::disconnect( cb, SIGNAL(dataChanged()), | ||
918 | this, SLOT(onClearSelection()) ); | ||
919 | |||
920 | QApplication::clipboard()->setText(t); | ||
921 | |||
922 | QObject::connect( cb, SIGNAL(dataChanged()), | ||
923 | this, SLOT(onClearSelection()) ); | ||
924 | #endif | ||
925 | } | ||
926 | |||
927 | void TEWidget::onClearSelection() | ||
928 | { | ||
929 | emit clearSelectionSignal(); | ||
930 | } | ||
931 | |||
932 | /* ------------------------------------------------------------------------- */ | ||
933 | /* */ | ||
934 | /* Keyboard */ | ||
935 | /* */ | ||
936 | /* ------------------------------------------------------------------------- */ | ||
937 | |||
938 | //FIXME: an `eventFilter' has been installed instead of a `keyPressEvent' | ||
939 | // due to a bug in `QT' or the ignorance of the author to prevent | ||
940 | // repaint events being emitted to the screen whenever one leaves | ||
941 | // or reenters the screen to/from another application. | ||
942 | // | ||
943 | // Troll says one needs to change focusInEvent() and focusOutEvent(), | ||
944 | // which would also let you have an in-focus cursor and an out-focus | ||
945 | // cursor like xterm does. | ||
946 | |||
947 | // for the auto-hide cursor feature, I added empty focusInEvent() and | ||
948 | // focusOutEvent() so that update() isn't called. | ||
949 | // For auto-hide, we need to get keypress-events, but we only get them when | ||
950 | // we have focus. | ||
951 | |||
952 | void TEWidget::doScroll(int lines) | ||
953 | { | ||
954 | scrollbar->setValue(scrollbar->value()+lines); | ||
955 | } | ||
956 | |||
957 | bool TEWidget::eventFilter( QObject *obj, QEvent *e ) | ||
958 | { | ||
959 | if ( (e->type() == QEvent::Accel || | ||
960 | e->type() == QEvent::AccelAvailable ) && qApp->focusWidget() == this ) | ||
961 | { | ||
962 | static_cast<QKeyEvent *>( e )->ignore(); | ||
963 | return true; | ||
964 | } | ||
965 | if ( obj != this /* when embedded */ && obj != parent() /* when standalone */ ) | ||
966 | return FALSE; // not us | ||
967 | if ( e->type() == QEvent::Wheel) | ||
968 | { | ||
969 | QApplication::sendEvent(scrollbar, e); | ||
970 | } | ||
971 | |||
972 | #ifdef FAKE_CTRL_AND_ALT | ||
973 | static bool control = FALSE; | ||
974 | static bool alt = FALSE; | ||
975 | // Has a keyboard with no CTRL and ALT keys, but we fake it: | ||
976 | bool dele=FALSE; | ||
977 | if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease ) { | ||
978 | QKeyEvent* ke = (QKeyEvent*)e; | ||
979 | bool keydown = e->type() == QEvent::KeyPress || ke->isAutoRepeat(); | ||
980 | switch (ke->key()) { | ||
981 | case Key_F9: // let this be "Control" | ||
982 | control = keydown; | ||
983 | e = new QKeyEvent(QEvent::KeyPress, Key_Control, 0, ke->state()); | ||
984 | dele=TRUE; | ||
985 | break; | ||
986 | case Key_F13: // let this be "Alt" | ||
987 | alt = keydown; | ||
988 | e = new QKeyEvent(QEvent::KeyPress, Key_Alt, 0, ke->state()); | ||
989 | dele=TRUE; | ||
990 | break; | ||
991 | default: | ||
992 | if ( control ) { | ||
993 | int a = toupper(ke->ascii())-64; | ||
994 | if ( a >= 0 && a < ' ' ) { | ||
995 | e = new QKeyEvent(e->type(), ke->key(), | ||
996 | a, ke->state()|ControlButton, QChar(a,0)); | ||
997 | dele=TRUE; | ||
998 | } | ||
999 | } | ||
1000 | if ( alt ) { | ||
1001 | e = new QKeyEvent(e->type(), ke->key(), | ||
1002 | ke->ascii(), ke->state()|AltButton, ke->text()); | ||
1003 | dele=TRUE; | ||
1004 | } | ||
1005 | } | ||
1006 | } | ||
1007 | #endif | ||
1008 | |||
1009 | if ( e->type() == QEvent::KeyPress ) | ||
1010 | { | ||
1011 | QKeyEvent* ke = (QKeyEvent*)e; | ||
1012 | |||
1013 | actSel=0; // Key stroke implies a screen update, so TEWidget won't | ||
1014 | // know where the current selection is. | ||
1015 | |||
1016 | emit keyPressedSignal(ke); // expose | ||
1017 | ke->accept(); | ||
1018 | #ifdef FAKE_CTRL_AND_ALT | ||
1019 | if ( dele ) delete e; | ||
1020 | #endif | ||
1021 | return true; // stop the event | ||
1022 | } | ||
1023 | if ( e->type() == QEvent::Enter ) | ||
1024 | { | ||
1025 | QObject::disconnect( (QObject*)cb, SIGNAL(dataChanged()), | ||
1026 | this, SLOT(onClearSelection()) ); | ||
1027 | } | ||
1028 | if ( e->type() == QEvent::Leave ) | ||
1029 | { | ||
1030 | QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), | ||
1031 | this, SLOT(onClearSelection()) ); | ||
1032 | } | ||
1033 | return QFrame::eventFilter( obj, e ); | ||
1034 | } | ||
1035 | |||
1036 | /* ------------------------------------------------------------------------- */ | ||
1037 | /* */ | ||
1038 | /* Frame */ | ||
1039 | /* */ | ||
1040 | /* ------------------------------------------------------------------------- */ | ||
1041 | |||
1042 | void TEWidget::frameChanged() | ||
1043 | { | ||
1044 | propagateSize(); | ||
1045 | update(); | ||
1046 | } | ||
1047 | |||
1048 | /* ------------------------------------------------------------------------- */ | ||
1049 | /* */ | ||
1050 | /* Sound */ | ||
1051 | /* */ | ||
1052 | /* ------------------------------------------------------------------------- */ | ||
1053 | |||
1054 | void TEWidget::Bell() | ||
1055 | { | ||
1056 | QApplication::beep(); | ||
1057 | } | ||
1058 | |||
1059 | /* ------------------------------------------------------------------------- */ | ||
1060 | /* */ | ||
1061 | /* Auxiluary */ | ||
1062 | /* */ | ||
1063 | /* ------------------------------------------------------------------------- */ | ||
1064 | |||
1065 | void TEWidget::clearImage() | ||
1066 | // initialize the image | ||
1067 | // for internal use only | ||
1068 | { | ||
1069 | for (int y = 0; y < lines; y++) | ||
1070 | for (int x = 0; x < columns; x++) | ||
1071 | { | ||
1072 | image[loc(x,y)].c = 0xff; //' '; | ||
1073 | image[loc(x,y)].f = 0xff; //DEFAULT_FORE_COLOR; | ||
1074 | image[loc(x,y)].b = 0xff; //DEFAULT_BACK_COLOR; | ||
1075 | image[loc(x,y)].r = 0xff; //DEFAULT_RENDITION; | ||
1076 | } | ||
1077 | } | ||
1078 | |||
1079 | // Create Image /////////////////////////////////////////////////////// | ||
1080 | |||
1081 | void TEWidget::calcGeometry() | ||
1082 | { | ||
1083 | //FIXME: set rimX == rimY == 0 when running in full screen mode. | ||
1084 | |||
1085 | scrollbar->resize(QApplication::style().scrollBarExtent().width(), | ||
1086 | contentsRect().height()); | ||
1087 | switch(scrollLoc) | ||
1088 | { | ||
1089 | case SCRNONE : | ||
1090 | columns = ( contentsRect().width() - 2 * rimX ) / font_w; | ||
1091 | blX = (contentsRect().width() - (columns*font_w) ) / 2; | ||
1092 | brX = blX; | ||
1093 | scrollbar->hide(); | ||
1094 | break; | ||
1095 | case SCRLEFT : | ||
1096 | columns = ( contentsRect().width() - 2 * rimX - scrollbar->width()) / font_w; | ||
1097 | brX = (contentsRect().width() - (columns*font_w) - scrollbar->width() ) / 2; | ||
1098 | blX = brX + scrollbar->width(); | ||
1099 | scrollbar->move(contentsRect().topLeft()); | ||
1100 | scrollbar->show(); | ||
1101 | break; | ||
1102 | case SCRRIGHT: | ||
1103 | columns = ( contentsRect().width() - 2 * rimX - scrollbar->width()) / font_w; | ||
1104 | blX = (contentsRect().width() - (columns*font_w) - scrollbar->width() ) / 2; | ||
1105 | brX = blX; | ||
1106 | scrollbar->move(contentsRect().topRight() - QPoint(scrollbar->width()-1,0)); | ||
1107 | scrollbar->show(); | ||
1108 | break; | ||
1109 | } | ||
1110 | //FIXME: support 'rounding' styles | ||
1111 | lines = ( contentsRect().height() - 2 * rimY ) / font_h; | ||
1112 | bY = (contentsRect().height() - (lines *font_h)) / 2; | ||
1113 | } | ||
1114 | |||
1115 | void TEWidget::makeImage() | ||
1116 | //FIXME: rename 'calcGeometry? | ||
1117 | { | ||
1118 | calcGeometry(); | ||
1119 | image = (ca*) malloc(lines*columns*sizeof(ca)); | ||
1120 | clearImage(); | ||
1121 | } | ||
1122 | |||
1123 | // calculate the needed size | ||
1124 | QSize TEWidget::calcSize(int cols, int lins) const | ||
1125 | { | ||
1126 | int frw = width() - contentsRect().width(); | ||
1127 | int frh = height() - contentsRect().height(); | ||
1128 | int scw = (scrollLoc==SCRNONE?0:scrollbar->width()); | ||
1129 | return QSize( font_w*cols + 2*rimX + frw + scw, font_h*lins + 2*rimY + frh ); | ||
1130 | } | ||
1131 | |||
1132 | QSize TEWidget::sizeHint() const | ||
1133 | { | ||
1134 | return size(); | ||
1135 | } | ||
1136 | |||
1137 | void TEWidget::styleChange(QStyle &) | ||
1138 | { | ||
1139 | propagateSize(); | ||
1140 | } | ||
1141 | |||
1142 | #ifndef QT_NO_DRAGANDDROP | ||
1143 | |||
1144 | /* --------------------------------------------------------------------- */ | ||
1145 | /* */ | ||
1146 | /* Drag & Drop */ | ||
1147 | /* */ | ||
1148 | /* --------------------------------------------------------------------- */ | ||
1149 | |||
1150 | |||
1151 | void TEWidget::dragEnterEvent(QDragEnterEvent* e) | ||
1152 | { | ||
1153 | e->accept(QTextDrag::canDecode(e) || | ||
1154 | QUriDrag::canDecode(e)); | ||
1155 | } | ||
1156 | |||
1157 | void TEWidget::dropEvent(QDropEvent* event) | ||
1158 | { | ||
1159 | // The current behaviour when url(s) are dropped is | ||
1160 | // * if there is only ONE url and if it's a LOCAL one, ask for paste or cd | ||
1161 | // * in all other cases, just paste | ||
1162 | // (for non-local ones, or for a list of URLs, 'cd' is nonsense) | ||
1163 | QStrList strlist; | ||
1164 | int file_count = 0; | ||
1165 | dropText = ""; | ||
1166 | bool bPopup = true; | ||
1167 | |||
1168 | if(QUriDrag::decode(event, strlist)) { | ||
1169 | if (strlist.count()) { | ||
1170 | for(const char* p = strlist.first(); p; p = strlist.next()) { | ||
1171 | if(file_count++ > 0) { | ||
1172 | dropText += " "; | ||
1173 | bPopup = false; // more than one file, don't popup | ||
1174 | } | ||
1175 | |||
1176 | /* | ||
1177 | KURL url(p); | ||
1178 | if (url.isLocalFile()) { | ||
1179 | dropText += url.path(); // local URL : remove protocol | ||
1180 | } | ||
1181 | else { | ||
1182 | dropText += url.prettyURL(); | ||
1183 | bPopup = false; // a non-local file, don't popup | ||
1184 | } | ||
1185 | */ | ||
1186 | |||
1187 | } | ||
1188 | |||
1189 | if (bPopup) | ||
1190 | // m_drop->popup(pos() + event->pos()); | ||
1191 | m_drop->popup(mapToGlobal(event->pos())); | ||
1192 | else | ||
1193 | { | ||
1194 | if (currentSession) { | ||
1195 | currentSession->getEmulation()->sendString(dropText.local8Bit()); | ||
1196 | } | ||
1197 | // kdDebug() << "Drop:" << dropText.local8Bit() << "\n"; | ||
1198 | } | ||
1199 | } | ||
1200 | } | ||
1201 | else if(QTextDrag::decode(event, dropText)) { | ||
1202 | // kdDebug() << "Drop:" << dropText.local8Bit() << "\n"; | ||
1203 | if (currentSession) { | ||
1204 | currentSession->getEmulation()->sendString(dropText.local8Bit()); | ||
1205 | } | ||
1206 | // Paste it | ||
1207 | } | ||
1208 | } | ||
1209 | #endif | ||
1210 | |||
1211 | |||
1212 | void TEWidget::drop_menu_activated(int item) | ||
1213 | { | ||
1214 | #ifndef QT_NO_DRAGANDDROP | ||
1215 | switch (item) | ||
1216 | { | ||
1217 | case 0: // paste | ||
1218 | currentSession->getEmulation()->sendString(dropText.local8Bit()); | ||
1219 | // KWM::activate((Window)this->winId()); | ||
1220 | break; | ||
1221 | case 1: // cd ... | ||
1222 | currentSession->getEmulation()->sendString("cd "); | ||
1223 | struct stat statbuf; | ||
1224 | if ( ::stat( QFile::encodeName( dropText ), &statbuf ) == 0 ) | ||
1225 | { | ||
1226 | if ( !S_ISDIR(statbuf.st_mode) ) | ||
1227 | { | ||
1228 | /* | ||
1229 | KURL url; | ||
1230 | url.setPath( dropText ); | ||
1231 | dropText = url.directory( true, false ); // remove filename | ||
1232 | */ | ||
1233 | } | ||
1234 | } | ||
1235 | dropText.replace(QRegExp(" "), "\\ "); // escape spaces | ||
1236 | currentSession->getEmulation()->sendString(dropText.local8Bit()); | ||
1237 | currentSession->getEmulation()->sendString("\n"); | ||
1238 | // KWM::activate((Window)this->winId()); | ||
1239 | break; | ||
1240 | } | ||
1241 | #endif | ||
1242 | } | ||
1243 | |||