summaryrefslogtreecommitdiff
path: root/core/apps/embeddedkonsole/TEWidget.cpp
Unidiff
Diffstat (limited to 'core/apps/embeddedkonsole/TEWidget.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/apps/embeddedkonsole/TEWidget.cpp1243
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.
102static 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
130QColor TEWidget::getDefaultBackColor()
131{
132 return color_table[DEFAULT_BACK_COLOR].color;
133}
134
135const ColorEntry* TEWidget::getColorTable() const
136{
137 return color_table;
138}
139
140const 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
147void 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
178unsigned 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
186static 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
225static QChar identicalMap(QChar c)
226{
227 return c;
228}
229
230void 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
252void TEWidget::setVTFont(const QFont& f)
253{
254 QFrame::setFont(f);
255}
256
257QFont TEWidget::getVTFont()
258{
259 return font();
260}
261
262void 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
273TEWidget::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)
323TEWidget::~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
339void 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
375void 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 );
381HCNT("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
446void 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 );
455HCNT("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
512void TEWidget::blinkEvent()
513{
514 blinking = !blinking;
515 repaint(FALSE);
516}
517
518/* ------------------------------------------------------------------------- */
519/* */
520/* Resizing */
521/* */
522/* ------------------------------------------------------------------------- */
523
524void 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());
530HCNT("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
540void 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
573void TEWidget::scrollChanged(int)
574{
575 emit changedHistoryCursor(scrollbar->value()); //expose
576}
577
578void 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
587void 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
628void 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
667void 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
761void 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
786void 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
836void TEWidget::focusInEvent( QFocusEvent * )
837{
838 // do nothing, to prevent repainting
839}
840
841
842void TEWidget::focusOutEvent( QFocusEvent * )
843{
844 // do nothing, to prevent repainting
845}
846
847bool 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
856int 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
872void 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
886void 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
901void 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
907void TEWidget::pasteClipboard( )
908{
909 emitSelection();
910}
911
912void 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
927void 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
952void TEWidget::doScroll(int lines)
953{
954 scrollbar->setValue(scrollbar->value()+lines);
955}
956
957bool 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
1042void TEWidget::frameChanged()
1043{
1044 propagateSize();
1045 update();
1046}
1047
1048/* ------------------------------------------------------------------------- */
1049/* */
1050/* Sound */
1051/* */
1052/* ------------------------------------------------------------------------- */
1053
1054void TEWidget::Bell()
1055{
1056 QApplication::beep();
1057}
1058
1059/* ------------------------------------------------------------------------- */
1060/* */
1061/* Auxiluary */
1062/* */
1063/* ------------------------------------------------------------------------- */
1064
1065void 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
1081void 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
1115void 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
1124QSize 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
1132QSize TEWidget::sizeHint() const
1133{
1134 return size();
1135}
1136
1137void TEWidget::styleChange(QStyle &)
1138{
1139 propagateSize();
1140}
1141
1142#ifndef QT_NO_DRAGANDDROP
1143
1144/* --------------------------------------------------------------------- */
1145/* */
1146/* Drag & Drop */
1147/* */
1148/* --------------------------------------------------------------------- */
1149
1150
1151void TEWidget::dragEnterEvent(QDragEnterEvent* e)
1152{
1153 e->accept(QTextDrag::canDecode(e) ||
1154 QUriDrag::canDecode(e));
1155}
1156
1157void 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
1212void 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