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