-rw-r--r-- | noncore/apps/opie-console/TEWidget.cpp | 52 | ||||
-rw-r--r-- | noncore/apps/opie-console/TEWidget.h | 6 | ||||
-rw-r--r-- | noncore/apps/opie-console/emulation_handler.cpp | 3 | ||||
-rw-r--r-- | noncore/apps/opie-console/emulation_widget.cpp | 3 | ||||
-rw-r--r-- | noncore/apps/opie-console/widget.cpp | 2 |
5 files changed, 57 insertions, 9 deletions
diff --git a/noncore/apps/opie-console/TEWidget.cpp b/noncore/apps/opie-console/TEWidget.cpp index e4415dc..bf9a313 100644 --- a/noncore/apps/opie-console/TEWidget.cpp +++ b/noncore/apps/opie-console/TEWidget.cpp @@ -1,1303 +1,1349 @@ /* ------------------------------------------------------------------------ */ /* */ /* [TEWidget.C] Terminal Emulation Widget */ /* */ /* ------------------------------------------------------------------------ */ /* */ /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ /* */ /* This file is part of Konsole - an X terminal for KDE */ /* */ /* ------------------------------------------------------------------------ */ /* */ /* Ported Konsole to Qt/Embedded */ /* */ /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ /* */ /* -------------------------------------------------------------------------- */ /*! \class TEWidget \brief Visible screen contents This class is responsible to map the `image' of a terminal emulation to the display. All the dependency of the emulation to a specific GUI or toolkit is localized here. Further, this widget has no knowledge about being part of an emulation, it simply work within the terminal emulation framework by exposing size and key events and by being ordered to show a new image. <ul> <li> The internal image has the size of the widget (evtl. rounded up) <li> The external image used in setImage can have any size. <li> (internally) the external image is simply copied to the internal when a setImage happens. During a resizeEvent no painting is done a paintEvent is expected to follow anyway. </ul> \sa TEScreen \sa Emulation */ /* FIXME: - 'image' may also be used uninitialized (it isn't in fact) in resizeEvent - 'font_a' not used in mouse events - add destructor */ /* TODO - evtl. be sensitive to `paletteChange' while using default colors. - set different 'rounding' styles? I.e. have a mode to show clipped chars? */ // #include "config.h" #include "TEWidget.h" #include "session.h" #include <qpe/config.h> #include <qapplication.h> #include <qcursor.h> #include <qregexp.h> #include <qpainter.h> #include <qclipboard.h> #include <qstyle.h> #include <qfile.h> #include <qdragobject.h> #include <qvbox.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <ctype.h> #include <sys/stat.h> #include <sys/types.h> #include <signal.h> #include <assert.h> // #include "TEWidget.moc" //#include <kapp.h> //#include <kcursor.h> //#include <kurl.h> //#include <kdebug.h> //#include <klocale.h> #define HERE printf("%s(%d): %s\n",__FILE__,__LINE__,__FUNCTION__) #define HCNT(Name) // { static int cnt = 1; printf("%s(%d): %s %d\n",__FILE__,__LINE__,Name,cnt++); } #define loc(X,Y) ((Y)*columns+(X)) //FIXME: the rim should normally be 1, 0 only when running in full screen mode. #define rimX 0 // left/right rim width #define rimY 0 // top/bottom rim high -#define SCRWIDTH 16 // width of the scrollbar - #define yMouseScroll 1 // scroll increment used when dragging selection at top/bottom of window. /* Button XPM */ namespace { static char * menu_xpm[] = { "12 12 5 1", " c None", ". c #000000", "+ c #FFFDAD", "@ c #FFFF00", "# c #E5E100", " ", " ", " ......... ", " .+++++++. ", " .+@@@@#. ", " .+@@@#. ", " .+@@#. ", " .+@#. ", " .+#. ", " .+. ", " .. ", " "}; } /* ------------------------------------------------------------------------- */ /* */ /* Colors */ /* */ /* ------------------------------------------------------------------------- */ //FIXME: the default color table is in session.C now. // We need a way to get rid of this one, here. static const ColorEntry base_color_table[TABLE_COLORS] = // The following are almost IBM standard color codes, with some slight // gamma correction for the dim colors to compensate for bright X screens. // It contains the 8 ansiterm/xterm colors in 2 intensities. { // Fixme: could add faint colors here, also. // normal ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White // intensiv ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) }; /* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) Code 0 1 2 3 4 5 6 7 ----------- ------- ------- ------- ------- ------- ------- ------- ------- ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White */ QColor TEWidget::getDefaultBackColor() { return color_table[DEFAULT_BACK_COLOR].color; } const ColorEntry* TEWidget::getColorTable() const { return color_table; } const ColorEntry* TEWidget::getdefaultColorTable() const { return base_color_table; } const QPixmap *TEWidget::backgroundPixmap() { static QPixmap *bg = new QPixmap("~/qpim/main/pics/faded_bg.xpm"); const QPixmap *pm = bg; return pm; } void TEWidget::setColorTable(const ColorEntry table[]) { for (int i = 0; i < TABLE_COLORS; i++) color_table[i] = table[i]; const QPixmap* pm = backgroundPixmap(); if (!pm) setBackgroundColor(color_table[DEFAULT_BACK_COLOR].color); update(); } //FIXME: add backgroundPixmapChanged. /* ------------------------------------------------------------------------- */ /* */ /* Font */ /* */ /* ------------------------------------------------------------------------- */ /* The VT100 has 32 special graphical characters. The usual vt100 extended xterm fonts have these at 0x00..0x1f. QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals come in here as proper unicode characters. We treat non-iso10646 fonts as VT100 extended and do the requiered mapping from unicode to 0x00..0x1f. The remaining translation is then left to the QCodec. */ // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. unsigned short vt100_graphics[32] = { // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 }; static QChar vt100extended(QChar c) { switch (c.unicode()) { case 0x25c6 : return 1; case 0x2592 : return 2; case 0x2409 : return 3; case 0x240c : return 4; case 0x240d : return 5; case 0x240a : return 6; case 0x00b0 : return 7; case 0x00b1 : return 8; case 0x2424 : return 9; case 0x240b : return 10; case 0x2518 : return 11; case 0x2510 : return 12; case 0x250c : return 13; case 0x2514 : return 14; case 0x253c : return 15; case 0xf800 : return 16; case 0xf801 : return 17; case 0x2500 : return 18; case 0xf803 : return 19; case 0xf804 : return 20; case 0x251c : return 21; case 0x2524 : return 22; case 0x2534 : return 23; case 0x252c : return 24; case 0x2502 : return 25; case 0x2264 : return 26; case 0x2265 : return 27; case 0x03c0 : return 28; case 0x2260 : return 29; case 0x00a3 : return 30; case 0x00b7 : return 31; } return c; } static QChar identicalMap(QChar c) { return c; } void TEWidget::fontChange(const QFont &) { QFontMetrics fm(font()); font_h = fm.height(); font_w = fm.maxWidth(); font_a = fm.ascent(); //printf("font_h: %d\n",font_h); //printf("font_w: %d\n",font_w); //printf("font_a: %d\n",font_a); //printf("charset: %s\n",QFont::encodingName(font().charSet()).ascii()); //printf("rawname: %s\n",font().rawName().ascii()); fontMap = #if QT_VERSION < 300 strcmp(QFont::encodingName(font().charSet()).ascii(),"iso10646") ? vt100extended : #endif identicalMap; propagateSize(); update(); } void TEWidget::setVTFont(const QFont& f) { QFrame::setFont(f); } QFont TEWidget::getVTFont() { return font(); } void TEWidget::setFont(const QFont &) { // ignore font change request if not coming from konsole itself } /* ------------------------------------------------------------------------- */ /* */ /* Constructor / Destructor */ /* */ /* ----------------------------------------------------------------------- */ TEWidget::TEWidget(QWidget *parent, const char *name) : QFrame(parent,name) { #ifndef QT_NO_CLIPBOARD cb = QApplication::clipboard(); QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), this, SLOT(onClearSelection()) ); #endif scrollbar = new QScrollBar( this ); scrollbar->setCursor( arrowCursor ); connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); + hscrollbar = new QScrollBar( Qt::Horizontal, this ); + hscrollbar->setCursor( arrowCursor ); + connect(hscrollbar, SIGNAL(valueChanged(int)), this, SLOT(hscrollChanged(int))); + m_cornerButton = new QPushButton( this ); m_cornerButton->setPixmap( QPixmap( (const char**)menu_xpm ) ); m_cornerButton->setMaximumSize( 14, 14 ); m_cornerButton->hide(); Config cfg("Konsole"); cfg.setGroup("ScrollBar"); switch( cfg.readNumEntry("Position",2)){ case 0: scrollLoc = SCRNONE; break; case 1: scrollLoc = SCRLEFT; break; case 2: scrollLoc = SCRRIGHT; break; }; blinkT = new QTimer(this); connect(blinkT, SIGNAL(timeout()), this, SLOT(blinkEvent())); // blinking = FALSE; blinking = TRUE; resizing = FALSE; actSel = 0; image = 0; lines = 1; columns = 1; font_w = 1; font_h = 1; font_a = 1; word_selection_mode = FALSE; + vcolumns = 0; setMouseMarks(TRUE); setVTFont( QFont("fixed") ); setColorTable(base_color_table); // init color table qApp->installEventFilter( this ); //FIXME: see below // KCursor::setAutoHideCursor( this, true ); // Init DnD //////////////////////////////////////////////////////////////// currentSession = NULL; // setAcceptDrops(true); // attempt // m_drop = new QPopupMenu(this); // m_drop->insertItem( QString("Paste"), 0); // m_drop->insertItem( QString("cd"), 1); // connect(m_drop, SIGNAL(activated(int)), SLOT(drop_menu_activated(int))); // we need focus so that the auto-hide cursor feature works setFocus(); setFocusPolicy( WheelFocus ); } //FIXME: make proper destructor // Here's a start (David) TEWidget::~TEWidget() { qApp->removeEventFilter( this ); if (image) free(image); } /* ------------------------------------------------------------------------- */ /* */ /* Display Operations */ /* */ /* ------------------------------------------------------------------------- */ /*! attributed string draw primitive */ void TEWidget::drawAttrStr(QPainter &paint, QRect rect, QString& str, ca attr, BOOL pm, BOOL clear) { if (pm && color_table[attr.b].transparent) { paint.setBackgroundMode( TransparentMode ); if (clear) erase(rect); } else { if (blinking) paint.fillRect(rect, color_table[attr.b].color); else { paint.setBackgroundMode( OpaqueMode ); paint.setBackgroundColor( color_table[attr.b].color ); } } if (color_table[attr.f].bold) paint.setPen(QColor( 0x8F, 0x00, 0x00 )); else paint.setPen(color_table[attr.f].color); paint.drawText(rect.x(),rect.y()+font_a, str); if (attr.r & RE_UNDERLINE) paint.drawLine(rect.left(), rect.y()+font_a+1, rect.right(),rect.y()+font_a+1 ); } /*! The image can only be set completely. The size of the new image may or may not match the size of the widget. */ void TEWidget::setImage(const ca* const newimg, int lines, int columns) { int y,x,len; const QPixmap* pm = backgroundPixmap(); QPainter paint; setUpdatesEnabled(FALSE); paint.begin( this ); HCNT("setImage"); QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); hasBlinker = FALSE; int cf = -1; // undefined int cb = -1; // undefined int cr = -1; // undefined int lins = QMIN(this->lines, QMAX(0,lines )); int cols = QMIN(this->columns,QMAX(0,columns)); QChar *disstrU = new QChar[cols]; //{ static int cnt = 0; printf("setImage %d\n",cnt++); } for (y = 0; y < lins; y++) { const ca* lcl = &image[y*this->columns]; const ca* const ext = &newimg[y*columns]; if (!resizing) // not while resizing, we're expecting a paintEvent for (x = 0; x < cols; x++) { hasBlinker |= (ext[x].r & RE_BLINK); if (ext[x] != lcl[x]) { cr = ext[x].r; cb = ext[x].b; if (ext[x].f != cf) cf = ext[x].f; int lln = cols - x; disstrU[0] = fontMap(ext[x+0].c); for (len = 1; len < lln; len++) { if (ext[x+len].f != cf || ext[x+len].b != cb || ext[x+len].r != cr || ext[x+len] == lcl[x+len] ) break; disstrU[len] = fontMap(ext[x+len].c); } QString unistr(disstrU,len); drawAttrStr(paint, QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), unistr, ext[x], pm != NULL, true); x += len - 1; } } // finally, make `image' become `newimg'. memcpy((void*)lcl,(const void*)ext,cols*sizeof(ca)); } drawFrame( &paint ); paint.end(); setUpdatesEnabled(TRUE); if ( hasBlinker && !blinkT->isActive()) blinkT->start(1000); // 1000 ms if (!hasBlinker && blinkT->isActive()) { blinkT->stop(); blinking = FALSE; } delete [] disstrU; } // paint Event //////////////////////////////////////////////////// /*! The difference of this routine vs. the `setImage' is, that the drawing does not include a difference analysis between the old and the new image. Instead, the internal image is used and the painting bound by the PaintEvent box. */ void TEWidget::paintEvent( QPaintEvent* pe ) { //{ static int cnt = 0; printf("paint %d\n",cnt++); } const QPixmap* pm = backgroundPixmap(); QPainter paint; setUpdatesEnabled(FALSE); paint.begin( this ); paint.setBackgroundMode( TransparentMode ); HCNT("paintEvent"); // Note that the actual widget size can be slightly larger // that the image (the size is truncated towards the smaller // number of characters in `resizeEvent'. The paint rectangle // can thus be larger than the image, but less then the size // of one character. QRect rect = pe->rect().intersect(contentsRect()); QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); int lux = QMIN(columns-1, QMAX(0,(rect.left() - tLx - blX ) / font_w)); int luy = QMIN(lines-1, QMAX(0,(rect.top() - tLy - bY ) / font_h)); int rlx = QMIN(columns-1, QMAX(0,(rect.right() - tLx - blX ) / font_w)); int rly = QMIN(lines-1, QMAX(0,(rect.bottom() - tLy - bY ) / font_h)); /* printf("paintEvent: %d..%d, %d..%d (%d..%d, %d..%d)\n",lux,rlx,luy,rly, rect.left(), rect.right(), rect.top(), rect.bottom()); */ // if (pm != NULL && color_table[image->b].transparent) // erase(rect); // BL: I have no idea why we need this, and it breaks the refresh. QChar *disstrU = new QChar[columns]; for (int y = luy; y <= rly; y++) for (int x = lux; x <= rlx; x++) { int len = 1; disstrU[0] = fontMap(image[loc(x,y)].c); int cf = image[loc(x,y)].f; int cb = image[loc(x,y)].b; int cr = image[loc(x,y)].r; while (x+len <= rlx && image[loc(x+len,y)].f == cf && image[loc(x+len,y)].b == cb && image[loc(x+len,y)].r == cr ) { disstrU[len] = fontMap(image[loc(x+len,y)].c); len += 1; } QString unistr(disstrU,len); drawAttrStr(paint, QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), unistr, image[loc(x,y)], pm != NULL, false); x += len - 1; } delete [] disstrU; drawFrame( &paint ); paint.end(); setUpdatesEnabled(TRUE); } void TEWidget::blinkEvent() { blinking = !blinking; repaint(FALSE); } /* ------------------------------------------------------------------------- */ /* */ /* Resizing */ /* */ /* ------------------------------------------------------------------------- */ void TEWidget::resizeEvent(QResizeEvent* ev) { // printf("resize: %d,%d\n",ev->size().width(),ev->size().height()); //printf("approx: %d,%d\n",ev->size().width()/font_w,ev->size().height()/font_h); //printf("leaves: %d,%d\n",ev->size().width()%font_w,ev->size().height()%font_h); //printf("curren: %d,%d\n",width(),height()); HCNT("resizeEvent"); // see comment in `paintEvent' concerning the rounding. //FIXME: could make a routine here; check width(),height() assert(ev->size().width() == width()); assert(ev->size().height() == height()); propagateSize(); } void TEWidget::propagateSize() { ca* oldimg = image; int oldlin = lines; int oldcol = columns; makeImage(); // we copy the old image to reduce flicker int lins = QMIN(oldlin,lines); int cols = QMIN(oldcol,columns); if (oldimg) { for (int lin = 0; lin < lins; lin++) memcpy((void*)&image[columns*lin], (void*)&oldimg[oldcol*lin],cols*sizeof(ca)); free(oldimg); //FIXME: try new,delete } else clearImage(); //NOTE: control flows from the back through the chest right into the eye. // `emu' will call back via `setImage'. resizing = TRUE; emit changedImageSizeSignal(lines, columns); // expose resizeEvent resizing = FALSE; } /* ------------------------------------------------------------------------- */ /* */ /* Scrollbar */ /* */ /* ------------------------------------------------------------------------- */ void TEWidget::scrollChanged(int) { emit changedHistoryCursor(scrollbar->value()); //expose } +void TEWidget::hscrollChanged(int loc) +{ + hposition = loc; + propagateSize(); + update(); +} + void TEWidget::setScroll(int cursor, int slines) { disconnect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); scrollbar->setRange(0,slines); scrollbar->setSteps(1,lines); scrollbar->setValue(cursor); connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); } void TEWidget::setScrollbarLocation(int loc) { if (scrollLoc == loc) return; // quickly scrollLoc = loc; propagateSize(); update(); } /* ------------------------------------------------------------------------- */ /* */ /* Mouse */ /* */ /* ------------------------------------------------------------------------- */ /*! Three different operations can be performed using the mouse, and the routines in this section serve all of them: 1) The press/release events are exposed to the application 2) Marking (press and move left button) and Pasting (press middle button) 3) The right mouse button is used from the configuration menu NOTE: During the marking process we attempt to keep the cursor within the bounds of the text as being displayed by setting the mouse position whenever the mouse has left the text area. Two reasons to do so: 1) QT does not allow the `grabMouse' to confine-to the TEWidget. Thus a `XGrapPointer' would have to be used instead. 2) Even if so, this would not help too much, since the text area of the TEWidget is normally not identical with it's bounds. The disadvantage of the current handling is, that the mouse can visibly leave the bounds of the widget and is then moved back. Because of the current construction, and the reasons mentioned above, we cannot do better without changing the overall construction. */ /*! */ void TEWidget::mousePressEvent(QMouseEvent* ev) { //printf("press [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); if ( !contentsRect().contains(ev->pos()) ) return; QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); word_selection_mode = FALSE; //printf("press top left [%d,%d] by=%d\n",tLx,tLy, bY); if ( ev->button() == LeftButton) { QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); if ( ev->state() & ControlButton ) preserve_line_breaks = FALSE ; if (mouse_marks || (ev->state() & ShiftButton)) { emit clearSelectionSignal(); iPntSel = pntSel = pos; actSel = 1; // left mouse button pressed but nothing selected yet. grabMouse( /*crossCursor*/ ); // handle with care! } else { emit mouseSignal( 0, pos.x() + 1, pos.y() + 1 ); // left button } } if ( ev->button() == MidButton ) { emitSelection(); } if ( ev->button() == RightButton ) // Configure { emit configureRequest( this, ev->state()&(ShiftButton|ControlButton), ev->x(), ev->y() ); } } void TEWidget::mouseMoveEvent(QMouseEvent* ev) { // for auto-hiding the cursor, we need mouseTracking if (ev->state() == NoButton ) return; if (actSel == 0) return; // don't extend selection while pasting if (ev->state() & MidButton) return; //if ( !contentsRect().contains(ev->pos()) ) return; QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); int scroll = scrollbar->value(); // we're in the process of moving the mouse with the left button pressed // the mouse cursor will kept catched within the bounds of the text in // this widget. // Adjust position within text area bounds. See FIXME above. QPoint pos = ev->pos(); if ( pos.x() < tLx+blX ) pos.setX( tLx+blX ); if ( pos.x() > tLx+blX+columns*font_w-1 ) pos.setX( tLx+blX+columns*font_w ); if ( pos.y() < tLy+bY ) pos.setY( tLy+bY ); if ( pos.y() > tLy+bY+lines*font_h-1 ) pos.setY( tLy+bY+lines*font_h-1 ); // check if we produce a mouse move event by this if ( pos != ev->pos() ) cursor().setPos(mapToGlobal(pos)); if ( pos.y() == tLy+bY+lines*font_h-1 ) { scrollbar->setValue(scrollbar->value()+yMouseScroll); // scrollforward } if ( pos.y() == tLy+bY ) { scrollbar->setValue(scrollbar->value()-yMouseScroll); // scrollback } QPoint here = QPoint((pos.x()-tLx-blX)/font_w,(pos.y()-tLy-bY)/font_h); QPoint ohere; bool swapping = FALSE; if ( word_selection_mode ) { // Extend to word boundaries int i; int selClass; bool left_not_right = ( here.y() < iPntSel.y() || here.y() == iPntSel.y() && here.x() < iPntSel.x() ); bool old_left_not_right = ( pntSel.y() < iPntSel.y() || pntSel.y() == iPntSel.y() && pntSel.x() < iPntSel.x() ); swapping = left_not_right != old_left_not_right; // Find left (left_not_right ? from here : from start) QPoint left = left_not_right ? here : iPntSel; i = loc(left.x(),left.y()); selClass = charClass(image[i].c); while ( left.x() > 0 && charClass(image[i-1].c) == selClass ) { i--; left.rx()--; } // Find left (left_not_right ? from start : from here) QPoint right = left_not_right ? iPntSel : here; i = loc(right.x(),right.y()); selClass = charClass(image[i].c); while ( right.x() < columns-1 && charClass(image[i+1].c) == selClass ) { i++; right.rx()++; } // Pick which is start (ohere) and which is extension (here) if ( left_not_right ) { here = left; ohere = right; } else { here = right; ohere = left; } } if (here == pntSel && scroll == scrollbar->value()) return; // not moved if ( word_selection_mode ) { if ( actSel < 2 || swapping ) { emit beginSelectionSignal( ohere.x(), ohere.y() ); } } else if ( actSel < 2 ) { emit beginSelectionSignal( pntSel.x(), pntSel.y() ); } actSel = 2; // within selection pntSel = here; emit extendSelectionSignal( here.x(), here.y() ); } void TEWidget::mouseReleaseEvent(QMouseEvent* ev) { //printf("release [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); if ( ev->button() == LeftButton) { if ( actSel > 1 ) emit endSelectionSignal(preserve_line_breaks); preserve_line_breaks = TRUE; actSel = 0; //FIXME: emits a release event even if the mouse is // outside the range. The procedure used in `mouseMoveEvent' // applies here, too. QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); if (!mouse_marks && !(ev->state() & ShiftButton)) emit mouseSignal( 3, // release (ev->x()-tLx-blX)/font_w + 1, (ev->y()-tLy-bY)/font_h + 1 ); releaseMouse(); } } void TEWidget::mouseDoubleClickEvent(QMouseEvent* ev) { if ( ev->button() != LeftButton) return; QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); // pass on double click as two clicks. if (!mouse_marks && !(ev->state() & ShiftButton)) { emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button emit mouseSignal( 3, pos.x()+1, pos.y()+1 ); // release emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button return; } emit clearSelectionSignal(); QPoint bgnSel = pos; QPoint endSel = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); int i = loc(bgnSel.x(),bgnSel.y()); iPntSel = bgnSel; word_selection_mode = TRUE; // find word boundaries... int selClass = charClass(image[i].c); { // set the start... int x = bgnSel.x(); while ( x > 0 && charClass(image[i-1].c) == selClass ) { i--; x--; } bgnSel.setX(x); emit beginSelectionSignal( bgnSel.x(), bgnSel.y() ); // set the end... i = loc( endSel.x(), endSel.y() ); x = endSel.x(); while( x < columns-1 && charClass(image[i+1].c) == selClass ) { i++; x++ ; } endSel.setX(x); actSel = 2; // within selection emit extendSelectionSignal( endSel.x(), endSel.y() ); emit endSelectionSignal(preserve_line_breaks); preserve_line_breaks = TRUE; } } void TEWidget::focusInEvent( QFocusEvent * ) { // do nothing, to prevent repainting } void TEWidget::focusOutEvent( QFocusEvent * ) { // do nothing, to prevent repainting } bool TEWidget::focusNextPrevChild( bool next ) { if (next) return false; // This disables changing the active part in konqueror // when pressing Tab return QFrame::focusNextPrevChild( next ); } int TEWidget::charClass(char ch) const { // This might seem like overkill, but imagine if ch was a Unicode // character (Qt 2.0 QChar) - it might then be sensible to separate // the different language ranges, etc. if ( isspace(ch) ) return ' '; static const char *word_characters = ":@-./_~"; if ( isalnum(ch) || strchr(word_characters, ch) ) return 'a'; // Everything else is weird return 1; } void TEWidget::setMouseMarks(bool on) { mouse_marks = on; setCursor( mouse_marks ? ibeamCursor : arrowCursor ); } /* ------------------------------------------------------------------------- */ /* */ /* Clipboard */ /* */ /* ------------------------------------------------------------------------- */ #undef KeyPress void TEWidget::emitSelection() // Paste Clipboard by simulating keypress events { #ifndef QT_NO_CLIPBOARD QString text = QApplication::clipboard()->text(); if ( ! text.isNull() ) { text.replace(QRegExp("\n"), "\r"); QKeyEvent e(QEvent::KeyPress, 0, -1, 0, text); emit keyPressedSignal(&e); // expose as a big fat keypress event emit clearSelectionSignal(); } #endif } void TEWidget::emitText(QString text) { QKeyEvent e(QEvent::KeyPress, 0, -1, 0, text); emit keyPressedSignal(&e); // expose as a big fat keypress event } void TEWidget::pasteClipboard( ) { emitSelection(); } void TEWidget::setSelection(const QString& t) { #ifndef QT_NO_CLIPBOARD // Disconnect signal while WE set the clipboard QObject *cb = QApplication::clipboard(); QObject::disconnect( cb, SIGNAL(dataChanged()), this, SLOT(onClearSelection()) ); QApplication::clipboard()->setText(t); QObject::connect( cb, SIGNAL(dataChanged()), this, SLOT(onClearSelection()) ); #endif } void TEWidget::onClearSelection() { emit clearSelectionSignal(); } /* ------------------------------------------------------------------------- */ /* */ /* Keyboard */ /* */ /* ------------------------------------------------------------------------- */ //FIXME: an `eventFilter' has been installed instead of a `keyPressEvent' // due to a bug in `QT' or the ignorance of the author to prevent // repaint events being emitted to the screen whenever one leaves // or reenters the screen to/from another application. // // Troll says one needs to change focusInEvent() and focusOutEvent(), // which would also let you have an in-focus cursor and an out-focus // cursor like xterm does. // for the auto-hide cursor feature, I added empty focusInEvent() and // focusOutEvent() so that update() isn't called. // For auto-hide, we need to get keypress-events, but we only get them when // we have focus. void TEWidget::doScroll(int lines) { scrollbar->setValue(scrollbar->value()+lines); } bool TEWidget::eventFilter( QObject *obj, QEvent *e ) { if ( (e->type() == QEvent::Accel || e->type() == QEvent::AccelAvailable ) && qApp->focusWidget() == this ) { static_cast<QKeyEvent *>( e )->ignore(); return true; } if ( obj != this /* when embedded */ && obj != parent() /* when standalone */ ) return FALSE; // not us if ( e->type() == QEvent::Wheel) { QApplication::sendEvent(scrollbar, e); } #ifdef FAKE_CTRL_AND_ALT static bool control = FALSE; static bool alt = FALSE; // qDebug(" Has a keyboard with no CTRL and ALT keys, but we fake it:"); bool dele=FALSE; if ( e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease ) { QKeyEvent* ke = (QKeyEvent*)e; bool keydown = e->type() == QEvent::KeyPress || ke->isAutoRepeat(); switch (ke->key()) { case Key_F9: // let this be "Control" control = keydown; e = new QKeyEvent(QEvent::KeyPress, Key_Control, 0, ke->state()); dele=TRUE; break; case Key_F13: // let this be "Alt" alt = keydown; e = new QKeyEvent(QEvent::KeyPress, Key_Alt, 0, ke->state()); dele=TRUE; break; default: if ( control ) { int a = toupper(ke->ascii())-64; if ( a >= 0 && a < ' ' ) { e = new QKeyEvent(e->type(), ke->key(), a, ke->state()|ControlButton, QChar(a,0)); dele=TRUE; } } if ( alt ) { e = new QKeyEvent(e->type(), ke->key(), ke->ascii(), ke->state()|AltButton, ke->text()); dele=TRUE; } } } #endif if ( e->type() == QEvent::KeyPress ) { QKeyEvent* ke = (QKeyEvent*)e; actSel=0; // Key stroke implies a screen update, so TEWidget won't // know where the current selection is. // qDebug("key pressed is 0x%x",ke->key()); if( ke->state() == ShiftButton && ke->key() == Key_Tab) { //lets hardcode this sucker // qDebug("key pressed 2 is 0x%x",ke->key()); emitText("\\"); // expose } else emit keyPressedSignal(ke); // expose ke->accept(); #ifdef FAKE_CTRL_AND_ALT if ( dele ) delete e; #endif return true; // stop the event } if ( e->type() == QEvent::Enter ) { QObject::disconnect( (QObject*)cb, SIGNAL(dataChanged()), this, SLOT(onClearSelection()) ); } if ( e->type() == QEvent::Leave ) { QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), this, SLOT(onClearSelection()) ); } return QFrame::eventFilter( obj, e ); } /* ------------------------------------------------------------------------- */ /* */ /* Frame */ /* */ /* ------------------------------------------------------------------------- */ void TEWidget::frameChanged() { propagateSize(); update(); } /* ------------------------------------------------------------------------- */ /* */ /* Sound */ /* */ /* ------------------------------------------------------------------------- */ void TEWidget::Bell() { QApplication::beep(); } /* ------------------------------------------------------------------------- */ /* */ /* Auxiluary */ /* */ /* ------------------------------------------------------------------------- */ void TEWidget::clearImage() // initialize the image // for internal use only { for (int y = 0; y < lines; y++) for (int x = 0; x < columns; x++) { image[loc(x,y)].c = 0xff; //' '; image[loc(x,y)].f = 0xff; //DEFAULT_FORE_COLOR; image[loc(x,y)].b = 0xff; //DEFAULT_BACK_COLOR; image[loc(x,y)].r = 0xff; //DEFAULT_RENDITION; } } // Create Image /////////////////////////////////////////////////////// void TEWidget::calcGeometry() { //FIXME: set rimX == rimY == 0 when running in full screen mode. + int showhscrollbar = 1; + int hwidth = 0; + + if(vcolumns == 0) showhscrollbar = 0; + if(showhscrollbar == 1) hwidth = QApplication::style().scrollBarExtent().width(); + scrollbar->resize(QApplication::style().scrollBarExtent().width(), - contentsRect().height()); + contentsRect().height() - hwidth); + + if(showhscrollbar == 1) + { + hscrollbar->resize(contentsRect().width() - hwidth, hwidth); + hscrollbar->setRange(0, 40); + + QPoint p = contentsRect().bottomLeft(); + hscrollbar->move(QPoint(p.x(), p.y() - hwidth)); + hscrollbar->show(); + } + else hscrollbar->hide(); + switch(scrollLoc) { case SCRNONE : columns = ( contentsRect().width() - 2 * rimX ) / font_w; + if(vcolumns) columns = vcolumns; blX = (contentsRect().width() - (columns*font_w) ) / 2; brX = blX; scrollbar->hide(); break; case SCRLEFT : columns = ( contentsRect().width() - 2 * rimX - scrollbar->width()) / font_w; + if(vcolumns) columns = vcolumns; brX = (contentsRect().width() - (columns*font_w) - scrollbar->width() ) / 2; blX = brX + scrollbar->width(); scrollbar->move(contentsRect().topLeft()); scrollbar->show(); break; case SCRRIGHT: columns = ( contentsRect().width() - 2 * rimX - scrollbar->width()) / font_w; + if(vcolumns) columns = vcolumns; blX = (contentsRect().width() - (columns*font_w) - scrollbar->width() ) / 2; + if(showhscrollbar) + blX = -hposition * font_w; brX = blX; scrollbar->move(contentsRect().topRight() - QPoint(scrollbar->width()-1,0)); scrollbar->show(); break; } //FIXME: support 'rounding' styles lines = ( contentsRect().height() - 2 * rimY ) / font_h; bY = (contentsRect().height() - (lines *font_h)) / 2; + + if(showhscrollbar == 1) + { + //bY = bY - 10; + lines = lines - 1; + } } void TEWidget::makeImage() //FIXME: rename 'calcGeometry? { calcGeometry(); image = (ca*) malloc(lines*columns*sizeof(ca)); clearImage(); } // calculate the needed size QSize TEWidget::calcSize(int cols, int lins) const { int frw = width() - contentsRect().width(); int frh = height() - contentsRect().height(); int scw = (scrollLoc==SCRNONE?0:scrollbar->width()); return QSize( font_w*cols + 2*rimX + frw + scw, font_h*lins + 2*rimY + frh ); } QSize TEWidget::sizeHint() const { return size(); } void TEWidget::styleChange(QStyle &) { propagateSize(); } #ifndef QT_NO_DRAGANDDROP /* --------------------------------------------------------------------- */ /* */ /* Drag & Drop */ /* */ /* --------------------------------------------------------------------- */ void TEWidget::dragEnterEvent(QDragEnterEvent* e) { e->accept(QTextDrag::canDecode(e) || QUriDrag::canDecode(e)); } void TEWidget::dropEvent(QDropEvent* event) { // The current behaviour when url(s) are dropped is // * if there is only ONE url and if it's a LOCAL one, ask for paste or cd // * in all other cases, just paste // (for non-local ones, or for a list of URLs, 'cd' is nonsense) QStrList strlist; int file_count = 0; dropText = ""; bool bPopup = true; if(QUriDrag::decode(event, strlist)) { if (strlist.count()) { for(const char* p = strlist.first(); p; p = strlist.next()) { if(file_count++ > 0) { dropText += " "; bPopup = false; // more than one file, don't popup } /* KURL url(p); if (url.isLocalFile()) { dropText += url.path(); // local URL : remove protocol } else { dropText += url.prettyURL(); bPopup = false; // a non-local file, don't popup } */ } if (bPopup) // m_drop->popup(pos() + event->pos()); m_drop->popup(mapToGlobal(event->pos())); else { if (currentSession) { currentSession->getEmulation()->sendString(dropText.local8Bit()); } // kdDebug() << "Drop:" << dropText.local8Bit() << "\n"; } } } else if(QTextDrag::decode(event, dropText)) { // kdDebug() << "Drop:" << dropText.local8Bit() << "\n"; if (currentSession) { currentSession->getEmulation()->sendString(dropText.local8Bit()); } // Paste it } } #endif void TEWidget::drop_menu_activated(int) { #ifndef QT_NO_DRAGANDDROP switch (item) { case 0: // paste currentSession->getEmulation()->sendString(dropText.local8Bit()); // KWM::activate((Window)this->winId()); break; case 1: // cd ... currentSession->getEmulation()->sendString("cd "); struct stat statbuf; if ( ::stat( QFile::encodeName( dropText ), &statbuf ) == 0 ) { if ( !S_ISDIR(statbuf.st_mode) ) { /* KURL url; url.setPath( dropText ); dropText = url.directory( true, false ); // remove filename */ } } dropText.replace(QRegExp(" "), "\\ "); // escape spaces currentSession->getEmulation()->sendString(dropText.local8Bit()); currentSession->getEmulation()->sendString("\n"); // KWM::activate((Window)this->winId()); break; } #endif } QPushButton* TEWidget::cornerButton() { return m_cornerButton; } + +void TEWidget::setWrapAt(int columns) +{ + vcolumns = columns; +} + + diff --git a/noncore/apps/opie-console/TEWidget.h b/noncore/apps/opie-console/TEWidget.h index f03a16e..6ff731b 100644 --- a/noncore/apps/opie-console/TEWidget.h +++ b/noncore/apps/opie-console/TEWidget.h @@ -1,210 +1,214 @@ /* ----------------------------------------------------------------------- */ /* */ /* [te_widget.h] Terminal Emulation Widget */ /* */ /* ----------------------------------------------------------------------- */ /* */ /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ /* */ /* This file is part of Konsole - an X terminal for KDE */ /* */ /* ----------------------------------------------------------------------- */ /* */ /* Ported Konsole to Qt/Embedded */ /* */ /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ /* */ /* -------------------------------------------------------------------------- */ #ifndef TE_WIDGET_H #define TE_WIDGET_H #include <qwidget.h> #include <qlabel.h> #include <qtimer.h> #include <qcolor.h> #include <qkeycode.h> #include <qscrollbar.h> #include <qpopupmenu.h> #include <qpushbutton.h> #include "TECommon.h" extern unsigned short vt100_graphics[32]; class TESession; // class Konsole; class TEWidget : public QFrame // a widget representing attributed text { Q_OBJECT // friend class Konsole; public: TEWidget(QWidget *parent=0, const char *name=0); virtual ~TEWidget(); public: QColor getDefaultBackColor(); QPushButton *cornerButton(); const ColorEntry* getColorTable() const; const ColorEntry* getdefaultColorTable() const; void setColorTable(const ColorEntry table[]); void setScrollbarLocation(int loc); enum { SCRNONE=0, SCRLEFT=1, SCRRIGHT=2 }; void setScroll(int cursor, int lines); void doScroll(int lines); void emitSelection(); + void setWrapAt(int columns); + public: void setImage(const ca* const newimg, int lines, int columns); int Lines() { return lines; } int Columns() { return columns; } void calcGeometry(); void propagateSize(); QSize calcSize(int cols, int lins) const; QSize sizeHint() const; public: void Bell(); void emitText(QString text); void pasteClipboard(); signals: void keyPressedSignal(QKeyEvent *e); void mouseSignal(int cb, int cx, int cy); void changedImageSizeSignal(int lines, int columns); void changedHistoryCursor(int value); void configureRequest( TEWidget*, int state, int x, int y ); void clearSelectionSignal(); void beginSelectionSignal( const int x, const int y ); void extendSelectionSignal( const int x, const int y ); void endSelectionSignal(const BOOL preserve_line_breaks); protected: virtual void styleChange( QStyle& ); bool eventFilter( QObject *, QEvent * ); void drawAttrStr(QPainter &paint, QRect rect, QString& str, ca attr, BOOL pm, BOOL clear); void paintEvent( QPaintEvent * ); void resizeEvent(QResizeEvent*); void fontChange(const QFont &font); void frameChanged(); void mouseDoubleClickEvent(QMouseEvent* ev); void mousePressEvent( QMouseEvent* ); void mouseReleaseEvent( QMouseEvent* ); void mouseMoveEvent( QMouseEvent* ); void focusInEvent( QFocusEvent * ); void focusOutEvent( QFocusEvent * ); bool focusNextPrevChild( bool next ); #ifndef QT_NO_DRAGANDDROP // Dnd void dragEnterEvent(QDragEnterEvent* event); void dropEvent(QDropEvent* event); #endif virtual int charClass(char) const; void clearImage(); public: const QPixmap *backgroundPixmap(); void setSelection(const QString &t); virtual void setFont(const QFont &); void setVTFont(const QFont &); QFont getVTFont(); void setMouseMarks(bool on); public slots: void onClearSelection(); protected slots: void scrollChanged(int value); + void hscrollChanged(int value); void blinkEvent(); private: QChar (*fontMap)(QChar); // possible vt100 font extention bool fixed_font; // has fixed pitch int font_h; // height int font_w; // width int font_a; // ascend int blX; // actual offset (left) int brX; // actual offset (right) int bY; // actual offset int lines; int columns; ca *image; // [lines][columns] ColorEntry color_table[TABLE_COLORS]; BOOL resizing; bool mouse_marks; void makeImage(); QPoint iPntSel; // initial selection point QPoint pntSel; // current selection point int actSel; // selection state BOOL word_selection_mode; BOOL preserve_line_breaks; QPushButton *m_cornerButton; QClipboard* cb; - QScrollBar* scrollbar; + QScrollBar* scrollbar, *hscrollbar; int scrollLoc; + int hposition, vcolumns; //#define SCRNONE 0 //#define SCRLEFT 1 //#define SCRRIGHT 2 BOOL blinking; // hide text in paintEvent BOOL hasBlinker; // has characters to blink QTimer* blinkT; // active when hasBlinker QPopupMenu* m_drop; QString dropText; public: // current session in this widget TESession *currentSession; private slots: void drop_menu_activated(int item); }; #endif // TE_WIDGET_H diff --git a/noncore/apps/opie-console/emulation_handler.cpp b/noncore/apps/opie-console/emulation_handler.cpp index e80168d..e0f63cd 100644 --- a/noncore/apps/opie-console/emulation_handler.cpp +++ b/noncore/apps/opie-console/emulation_handler.cpp @@ -1,198 +1,201 @@ #include <qwidget.h> #include <qpushbutton.h> #include "TEWidget.h" #include "TEmuVt102.h" #include "profile.h" #include "emulation_handler.h" #include "script.h" EmulationHandler::EmulationHandler( const Profile& prof, QWidget* parent,const char* name ) : QObject(0, name ) { m_teWid = new TEWidget( parent, "TerminalMain"); + // use setWrapAt(0) for classic behaviour (wrap at screen width, no scrollbar) + // use setWrapAt(80) for normal console with scrollbar + m_teWid->setWrapAt(prof.readNumEntry("Wrap", 0) ? 0 : 80); m_teWid->setMinimumSize(150, 70 ); m_script = 0; parent->resize( m_teWid->calcSize(80, 24 ) ); m_teEmu = new TEmuVt102(m_teWid ); connect(m_teEmu,SIGNAL(ImageSizeChanged(int, int) ), this, SIGNAL(changeSize(int, int) ) ); connect(m_teEmu, SIGNAL(sndBlock(const char*, int) ), this, SLOT(recvEmulation(const char*, int) ) ); m_teEmu->setConnect( true ); m_teEmu->setHistory( TRUE ); load( prof ); } EmulationHandler::~EmulationHandler() { if (isRecording()) clearScript(); delete m_teEmu; delete m_teWid; } void EmulationHandler::load( const Profile& prof) { m_teWid->setVTFont( font( prof.readNumEntry("Font") ) ); int num = prof.readNumEntry("Color"); setColor( foreColor(num), backColor(num) ); m_teWid->setBackgroundColor(backColor(num) ); int term = prof.readNumEntry("Terminal", 0) ; switch(term) { default: case Profile::VT102: case Profile::VT100: m_teEmu->setKeytrans("vt100.keytab"); break; case Profile::Linux: m_teEmu->setKeytrans("linux.keytab"); break; case Profile::XTerm: m_teEmu->setKeytrans("default.Keytab"); break; } } void EmulationHandler::recv( const QByteArray& ar) { m_teEmu->onRcvBlock(ar.data(), ar.count() ); } void EmulationHandler::recvEmulation(const char* src, int len ) { QByteArray ar(len); memcpy(ar.data(), src, sizeof(char) * len ); if (isRecording()) m_script->append(ar); emit send(ar); } QWidget* EmulationHandler::widget() { return m_teWid; } /* * allocate a new table of colors */ void EmulationHandler::setColor( const QColor& fore, const QColor& back ) { ColorEntry table[TABLE_COLORS]; const ColorEntry *defaultCt = m_teWid->getdefaultColorTable(); for (int i = 0; i < TABLE_COLORS; i++ ) { if ( i == 0 || i == 10 ) { table[i].color = fore; }else if ( i == 1 || i == 11 ) { table[i].color = back; table[i].transparent = 0; }else { table[i].color = defaultCt[i].color; } } m_teWid->setColorTable(table ); m_teWid->update(); } QFont EmulationHandler::font( int id ) { QString name; int size = 0; switch(id ) { default: // fall through case 0: name = QString::fromLatin1("Micro"); size = 4; break; case 1: name = QString::fromLatin1("Fixed"); size = 7; break; case 2: name = QString::fromLatin1("Fixed"); size = 12; break; } QFont font(name, size, QFont::Normal ); font.setFixedPitch(TRUE ); return font; } QColor EmulationHandler::foreColor(int col) { QColor co; /* we need to switch it */ switch( col ) { default: case Profile::White: /* color is black */ co = Qt::white; break; case Profile::Black: co = Qt::black; break; case Profile::Green: qWarning("Foreground green"); co = Qt::green; break; case Profile::Orange: qWarning("Foreground orange"); co.setRgb( 231, 184, 98 ); break; } return co; } QColor EmulationHandler::backColor(int col ) { QColor co; /* we need to switch it */ switch( col ) { default: case Profile::White: /* color is white */ co = Qt::black; break; case Profile::Black: co = Qt::white; break; case Profile::Green: qWarning("Background black"); co = Qt::black; break; case Profile::Orange: qWarning("Background black"); co = Qt::black; break; } return co; } QPushButton* EmulationHandler::cornerButton() { return m_teWid->cornerButton(); } Script *EmulationHandler::script() { return m_script; } bool EmulationHandler::isRecording() { return (m_script != 0); } void EmulationHandler::startRecording() { if (!isRecording()) m_script = new Script(); } void EmulationHandler::clearScript() { if (isRecording()) { delete m_script; m_script = 0; } } void EmulationHandler::runScript(const Script *script) { emit send(script->script()); } void EmulationHandler::copy() { m_teWid->emitSelection(); } void EmulationHandler::paste() { m_teWid->pasteClipboard(); } diff --git a/noncore/apps/opie-console/emulation_widget.cpp b/noncore/apps/opie-console/emulation_widget.cpp index d8e342b..9f95c72 100644 --- a/noncore/apps/opie-console/emulation_widget.cpp +++ b/noncore/apps/opie-console/emulation_widget.cpp @@ -1,312 +1,309 @@ // opie-console includes #include "emulation_widget.h" #include "common.h" #include "widget_layer.h" #include "profile.h" // qt includes #include <qwidget.h> #include <qarray.h> #include <qstring.h> #include <qpainter.h> #include <qrect.h> #include <qscrollbar.h> #define rimX 0 // left/right rim width #define rimY 0 // top/bottom rim high -#define SCRWIDTH 16 // width of scrollbar - - static const ColorEntry color_table[TABLE_COLORS] = { ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White // intensiv ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) }; EmulationWidget::EmulationWidget( const Profile& config, QWidget *parent, const char* name ) : WidgetLayer( config, parent, name ) { // initialize font attributes QFontMetrics fm( font() ); f_height = fm.height(); f_width = fm.maxWidth(); f_ascent = fm.ascent(); // initialize scrollbar related vars m_scrollbar = new QScrollBar( this ); m_scrollbar->setCursor( arrowCursor ); // give reasonable defaults to m_columns, m_lines calcGeometry(); // load config reloadConfig( config ); m_resizing = false; } void EmulationWidget::reloadConfig( const Profile& config ) { // nothing yet } EmulationWidget::~EmulationWidget() { //clean up } static QChar vt100extended(QChar c) { switch (c.unicode()) { case 0x25c6 : return 1; case 0x2592 : return 2; case 0x2409 : return 3; case 0x240c : return 4; case 0x240d : return 5; case 0x240a : return 6; case 0x00b0 : return 7; case 0x00b1 : return 8; case 0x2424 : return 9; case 0x240b : return 10; case 0x2518 : return 11; case 0x2510 : return 12; case 0x250c : return 13; case 0x2514 : return 14; case 0x253c : return 15; case 0xf800 : return 16; case 0xf801 : return 17; case 0x2500 : return 18; case 0xf803 : return 19; case 0xf804 : return 20; case 0x251c : return 21; case 0x2524 : return 22; case 0x2534 : return 23; case 0x252c : return 24; case 0x2502 : return 25; case 0x2264 : return 26; case 0x2265 : return 27; case 0x03c0 : return 28; case 0x2260 : return 29; case 0x00a3 : return 30; case 0x00b7 : return 31; } return c; } QSize EmulationWidget::calcSize( int cols, int lins ) const { int frw = width() - contentsRect().width(); int frh = height() - contentsRect().height(); int scw = (scrollLoc == SCRNONE? 0 : m_scrollbar->width() ); return QSize( f_width * cols + 2 * rimX + frw + scw, f_height * lins + 2 * rimY + frh ); } void EmulationWidget::setImage( QArray<Character> const newimg, int lines, int columns ) { const QPixmap* pm = backgroundPixmap(); QPainter paint; // dont allow updates, while drawing setUpdatesEnabled( false ); paint.begin( this ); QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); // hasBlinker = false; int cf = -1; int cb = -1; int cr = -1; int lins = QMIN( m_lines, QMAX( 0, lines ) ); int cols = QMIN( m_columns, QMAX( 0, columns ) ); QArray<QChar> disstrU = QArray<QChar>( cols ); for ( int y = 0; y < lins; ++y ) { int len; const Character* lcl = &m_image[y * m_columns]; const Character* ext = &newimg[y * m_columns]; if ( ! m_resizing ) for ( int x = 0; x < cols; ++x ) { // disable, till widget works, WITHOUT blinking //hasBlinker |= ( ext[x].r & RE_BLINK ); if ( ext[x] != lcl[x] ) { cr = ext[x].r; cb = ext[x].b; if ( ext[x].f != cf ) cf = ext[x].f; int lln = cols - x; disstrU[0] = vt100extended( ext[x+0].c ); for ( len = 1; len < lln; ++len ) { if ( ext[x+len].f != cf || ext[x+len].b != cb || ext[x+len].r != cr || ext[x+len] == lcl[x+len] ) break; disstrU[len] = vt100extended( ext[x+len].c ); } QString unistr( disstrU, len ); drawAttrString( unistr, paint, QRect( m_blX+tLx+f_width*x, m_bY+tLy+f_height*y, f_width*len, f_height ), ext[x], pm != NULL, true ); x += len -1; } } // make image become newimg memcpy( (void*) lcl, (const void*) ext, cols*sizeof( Character ) ); } drawFrame( &paint ); paint.end(); setUpdatesEnabled( true ); /*if ( hasBlinker && !blinkT->isActive() ) blinkT->start(1000); //ms if ( ! hasBlinker && blinkT->isActive() ) { blinkT->stop(); blinking = false; }*/ delete [] disstrU; } void EmulationWidget::paintEvent( QPaintEvent* pe ) { QPainter painter; const QPixmap* pm = backgroundPixmap(); painter.begin( this ); painter.setBackgroundMode( TransparentMode ); QRect rect = pe->rect().intersect( contentsRect() ); QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); int lux = QMIN(m_columns-1, QMAX(0,(rect.left() - tLx - m_blX ) / f_width)); int luy = QMIN(m_lines-1, QMAX(0,(rect.top() - tLy - m_bY ) / f_height)); int rlx = QMIN(m_columns-1, QMAX(0,(rect.right() - tLx - m_blX ) / f_width)); int rly = QMIN(m_lines-1, QMAX(0,(rect.bottom() - tLy - m_bY ) / f_height)); QChar *disstrU = new QChar[m_columns]; for (int y = luy; y <= rly; y++) for (int x = lux; x <= rlx; x++) { int len = 1; disstrU[0] = vt100extended(m_image[loc(x,y)].c); int cf = m_image[loc(x,y)].f; int cb = m_image[loc(x,y)].b; int cr = m_image[loc(x,y)].r; while (x+len <= rlx && m_image[loc(x+len,y)].f == cf && m_image[loc(x+len,y)].b == cb && m_image[loc(x+len,y)].r == cr ) { disstrU[len] = vt100extended(m_image[loc(x+len,y)].c); len += 1; } QString unistr(disstrU,len); drawAttrString( unistr, painter, QRect( m_blX+tLx+f_width*x,m_bY+tLy+f_height*y,f_width*len,f_height ), m_image[loc(x ,y )], pm != 0l, false ); x +=len -1; } delete [] disstrU; drawFrame( &painter ); painter.end(); } void EmulationWidget::calcGeometry() { m_scrollbar->resize(QApplication::style().scrollBarExtent().width(), contentsRect().height() ); qDebug( QString(" TEST").arg( contentsRect().width() ) ); qDebug( QString(" TEST").arg( contentsRect().height() ) ); qDebug("NEUER TESTT!!!!!!!!"); switch( scrollLoc ) { case SCRNONE : m_columns = ( contentsRect().width() -2 * rimX ) / f_width; m_blX = ( contentsRect().width() - ( m_columns*f_width ) ) / 2; m_brX = m_blX; m_scrollbar->hide(); break; case SCRLEFT : m_columns = ( contentsRect().width() - 2 * rimX - m_scrollbar->width() ) / f_width; m_brX = ( contentsRect().width() - ( m_columns*f_width ) - m_scrollbar->width() ) / 2; m_blX = m_brX + m_scrollbar->width(); m_scrollbar->move( contentsRect().topLeft() ); m_scrollbar->show(); break; case SCRIGHT: m_columns = ( contentsRect().width() - 2 * rimX - m_scrollbar->width() ) / f_width; m_blX = ( contentsRect().width() - ( m_columns*f_width ) - m_scrollbar->width() ) / 2; m_brX = m_blX; m_scrollbar->move( contentsRect().topRight() - QPoint (m_scrollbar->width()-1,0 ) ); m_scrollbar->show(); break; } m_lines = ( contentsRect().height() - 2 * rimY ) / f_height; m_bY = ( contentsRect().height() - (m_lines * f_height ) ) / 2; } void EmulationWidget::drawAttrString( QString& string, QPainter &painter, QRect rect, Character attr, bool usePixmap, bool clear ) { qWarning("Color1 %s", color_table[attr.b].color.name().latin1() ); if ( usePixmap && color_table[attr.b].transparent ) { painter.setBackgroundMode( TransparentMode ); if ( clear ) erase( rect ); } else { if ( blinking ) painter.fillRect( rect, color_table[attr.b].color ); else { painter.setBackgroundMode( OpaqueMode ); qWarning("Color %s", color_table[attr.b].color.name().latin1() ); painter.setBackgroundColor( color_table[attr.b].color ); } } if ( color_table[attr.f].bold ) painter.setPen( QColor( 0x8F, 0x00, 0x00 ) ); else painter.setPen( color_table[attr.f].color ); painter.drawText( rect.x(), rect.y() + f_ascent, string ); } /////////////////////// // scrollbar // //////////////////// void EmulationWidget::scroll( int value ) { } void EmulationWidget::setScroll( int cursor, int slines ) { } diff --git a/noncore/apps/opie-console/widget.cpp b/noncore/apps/opie-console/widget.cpp index d948179..10045c6 100644 --- a/noncore/apps/opie-console/widget.cpp +++ b/noncore/apps/opie-console/widget.cpp @@ -1,874 +1,872 @@ /* ------------------------------------------------------------------------ */ /* */ /* [TEWidget.C] Terminal Emulation Widget */ /* */ /* ------------------------------------------------------------------------ */ /* */ /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ /* */ /* This file is part of Konsole - an X terminal for KDE */ /* */ /* ------------------------------------------------------------------------ */ /* */ /* Ported Konsole to Qt/Embedded */ /* */ /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ /* */ /* -------------------------------------------------------------------------- */ /* ibot: i changed "currentSession->getEmulation()->sendString()" to "currentSession->layer()->send()" # this is not right! EmulationLayer should send it... i changed all those to use emulationLayer()->send() instead i had to create a QByteArray before... TODO: alter Widget to use only QByteArray, where applicable. */ /*! \class Widget \brief Visible screen contents This class is responsible to map the `image' of a terminal emulation to the display. All the dependency of the emulation to a specific GUI or toolkit is localized here. Further, this widget has no knowledge about being part of an emulation, it simply work within the terminal emulation framework by exposing size and key events and by being ordered to show a new image. <ul> <li> The internal image has the size of the widget (evtl. rounded up) <li> The external image used in setImage can have any size. <li> (internally) the external image is simply copied to the internal when a setImage happens. During a resizeEvent no painting is done a paintEvent is expected to follow anyway. </ul> \sa TEScreen \sa Emulation */ /* FIXME: - 'image' may also be used uninitialized (it isn't in fact) in resizeEvent - 'font_a' not used in mouse events - add destructor */ /* TODO - evtl. be sensitive to `paletteChange' while using default colors. - set different 'rounding' styles? I.e. have a mode to show clipped chars? */ // #include "config.h" #include "widget.h" #include "session.h" #include <qpe/config.h> #include <qapplication.h> #include <qcursor.h> #include <qregexp.h> #include <qpainter.h> #include <qclipboard.h> #include <qstyle.h> #include <qfile.h> #include <qdragobject.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <ctype.h> #include <sys/stat.h> #include <sys/types.h> #include <signal.h> #include <assert.h> // #include "widget.moc" //#include <kapp.h> //#include <kcursor.h> //#include <kurl.h> //#include <kdebug.h> //#include <klocale.h> #define HERE printf("%s(%d): %s\n",__FILE__,__LINE__,__FUNCTION__) #define HCNT(Name) // { static int cnt = 1; printf("%s(%d): %s %d\n",__FILE__,__LINE__,Name,cnt++); } #define loc(X,Y) ((Y)*columns+(X)) //FIXME: the rim should normally be 1, 0 only when running in full screen mode. #define rimX 0 // left/right rim width #define rimY 0 // top/bottom rim high -#define SCRWIDTH 16 // width of the scrollbar - #define yMouseScroll 1 // scroll increment used when dragging selection at top/bottom of window. /* ------------------------------------------------------------------------- */ /* */ /* Colors */ /* */ /* ------------------------------------------------------------------------- */ //FIXME: the default color table is in session.C now. // We need a way to get rid of this one, here. static const ColorEntry base_color_table[TABLE_COLORS] = // The following are almost IBM standard color codes, with some slight // gamma correction for the dim colors to compensate for bright X screens. // It contains the 8 ansiterm/xterm colors in 2 intensities. { // Fixme: could add faint colors here, also. // normal ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White // intensiv ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) }; /* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) Code 0 1 2 3 4 5 6 7 ----------- ------- ------- ------- ------- ------- ------- ------- ------- ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White */ QColor Widget::getDefaultBackColor() { return color_table[DEFAULT_BACK_COLOR].color; } const ColorEntry* Widget::getColorTable() const { return color_table; } const ColorEntry* Widget::getdefaultColorTable() const { return base_color_table; } const QPixmap *Widget::backgroundPixmap() { static QPixmap *bg = new QPixmap("~/qpim/main/pics/faded_bg.xpm"); const QPixmap *pm = bg; return pm; } void Widget::setColorTable(const ColorEntry table[]) { for (int i = 0; i < TABLE_COLORS; i++) color_table[i] = table[i]; const QPixmap* pm = backgroundPixmap(); if (!pm) setBackgroundColor(color_table[DEFAULT_BACK_COLOR].color); update(); } //FIXME: add backgroundPixmapChanged. /* ------------------------------------------------------------------------- */ /* */ /* Font */ /* */ /* ------------------------------------------------------------------------- */ /* The VT100 has 32 special graphical characters. The usual vt100 extended xterm fonts have these at 0x00..0x1f. QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals come in here as proper unicode characters. We treat non-iso10646 fonts as VT100 extended and do the requiered mapping from unicode to 0x00..0x1f. The remaining translation is then left to the QCodec. */ // assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. unsigned short vt100_graphics[32] = { // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 }; static QChar vt100extended(QChar c) { switch (c.unicode()) { case 0x25c6 : return 1; case 0x2592 : return 2; case 0x2409 : return 3; case 0x240c : return 4; case 0x240d : return 5; case 0x240a : return 6; case 0x00b0 : return 7; case 0x00b1 : return 8; case 0x2424 : return 9; case 0x240b : return 10; case 0x2518 : return 11; case 0x2510 : return 12; case 0x250c : return 13; case 0x2514 : return 14; case 0x253c : return 15; case 0xf800 : return 16; case 0xf801 : return 17; case 0x2500 : return 18; case 0xf803 : return 19; case 0xf804 : return 20; case 0x251c : return 21; case 0x2524 : return 22; case 0x2534 : return 23; case 0x252c : return 24; case 0x2502 : return 25; case 0x2264 : return 26; case 0x2265 : return 27; case 0x03c0 : return 28; case 0x2260 : return 29; case 0x00a3 : return 30; case 0x00b7 : return 31; } return c; } static QChar identicalMap(QChar c) { return c; } void Widget::fontChange(const QFont &) { QFontMetrics fm(font()); font_h = fm.height(); font_w = fm.maxWidth(); font_a = fm.ascent(); //printf("font_h: %d\n",font_h); //printf("font_w: %d\n",font_w); //printf("font_a: %d\n",font_a); //printf("charset: %s\n",QFont::encodingName(font().charSet()).ascii()); //printf("rawname: %s\n",font().rawName().ascii()); fontMap = #if QT_VERSION < 300 strcmp(QFont::encodingName(font().charSet()).ascii(),"iso10646") ? vt100extended : #endif identicalMap; propagateSize(); update(); } void Widget::setVTFont(const QFont& f) { QFrame::setFont(f); } QFont Widget::getVTFont() { return font(); } void Widget::setFont(const QFont &) { // ignore font change request if not coming from konsole itself } /* ------------------------------------------------------------------------- */ /* */ /* Constructor / Destructor */ /* */ /* ------------------------------------------------------------------------- */ Widget::Widget(QWidget *parent, const char *name) : QFrame(parent,name) { #ifndef QT_NO_CLIPBOARD cb = QApplication::clipboard(); QObject::connect( (QObject*)cb, SIGNAL(dataChanged()), this, SLOT(onClearSelection()) ); #endif scrollbar = new QScrollBar(this); scrollbar->setCursor( arrowCursor ); connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); Config cfg("Konsole"); cfg.setGroup("ScrollBar"); switch( cfg.readNumEntry("Position",2)){ case 0: scrollLoc = SCRNONE; break; case 1: scrollLoc = SCRLEFT; break; case 2: scrollLoc = SCRRIGHT; break; }; blinkT = new QTimer(this); connect(blinkT, SIGNAL(timeout()), this, SLOT(blinkEvent())); // blinking = FALSE; blinking = TRUE; resizing = FALSE; actSel = 0; image = 0; lines = 1; columns = 1; font_w = 1; font_h = 1; font_a = 1; word_selection_mode = FALSE; setMouseMarks(TRUE); setVTFont( QFont("fixed") ); setColorTable(base_color_table); // init color table qApp->installEventFilter( this ); //FIXME: see below // KCursor::setAutoHideCursor( this, true ); // Init DnD //////////////////////////////////////////////////////////////// currentSession = NULL; // setAcceptDrops(true); // attempt // m_drop = new QPopupMenu(this); // m_drop->insertItem( QString("Paste"), 0); // m_drop->insertItem( QString("cd"), 1); // connect(m_drop, SIGNAL(activated(int)), SLOT(drop_menu_activated(int))); // we need focus so that the auto-hide cursor feature works setFocus(); setFocusPolicy( WheelFocus ); } //FIXME: make proper destructor // Here's a start (David) Widget::~Widget() { qApp->removeEventFilter( this ); if (image) free(image); } /* ------------------------------------------------------------------------- */ /* */ /* Display Operations */ /* */ /* ------------------------------------------------------------------------- */ /*! attributed string draw primitive */ void Widget::drawAttrStr(QPainter &paint, QRect rect, QString& str, Character attr, BOOL pm, BOOL clear) { if (pm && color_table[attr.b].transparent) { paint.setBackgroundMode( TransparentMode ); if (clear) erase(rect); } else { if (blinking) paint.fillRect(rect, color_table[attr.b].color); else { paint.setBackgroundMode( OpaqueMode ); paint.setBackgroundColor( color_table[attr.b].color ); } } if (color_table[attr.f].bold) paint.setPen(QColor( 0x8F, 0x00, 0x00 )); else paint.setPen(color_table[attr.f].color); paint.drawText(rect.x(),rect.y()+font_a, str); if (attr.r & RE_UNDERLINE) paint.drawLine(rect.left(), rect.y()+font_a+1, rect.right(),rect.y()+font_a+1 ); } /*! The image can only be set completely. The size of the new image may or may not match the size of the widget. */ void Widget::setImage(const Character* const newimg, int lines, int columns) { int y,x,len; const QPixmap* pm = backgroundPixmap(); QPainter paint; setUpdatesEnabled(FALSE); paint.begin( this ); HCNT("setImage"); QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); hasBlinker = FALSE; int cf = -1; // undefined int cb = -1; // undefined int cr = -1; // undefined int lins = QMIN(this->lines, QMAX(0,lines )); int cols = QMIN(this->columns,QMAX(0,columns)); QChar *disstrU = new QChar[cols]; //{ static int cnt = 0; printf("setImage %d\n",cnt++); } for (y = 0; y < lins; y++) { const Character* lcl = &image[y*this->columns]; const Character* const ext = &newimg[y*columns]; if (!resizing) // not while resizing, we're expecting a paintEvent for (x = 0; x < cols; x++) { hasBlinker |= (ext[x].r & RE_BLINK); if (ext[x] != lcl[x]) { cr = ext[x].r; cb = ext[x].b; if (ext[x].f != cf) cf = ext[x].f; int lln = cols - x; disstrU[0] = fontMap(ext[x+0].c); for (len = 1; len < lln; len++) { if (ext[x+len].f != cf || ext[x+len].b != cb || ext[x+len].r != cr || ext[x+len] == lcl[x+len] ) break; disstrU[len] = fontMap(ext[x+len].c); } QString unistr(disstrU,len); drawAttrStr(paint, QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), unistr, ext[x], pm != NULL, true); x += len - 1; } } // finally, make `image' become `newimg'. memcpy((void*)lcl,(const void*)ext,cols*sizeof(Character)); } drawFrame( &paint ); paint.end(); setUpdatesEnabled(TRUE); if ( hasBlinker && !blinkT->isActive()) blinkT->start(1000); // 1000 ms if (!hasBlinker && blinkT->isActive()) { blinkT->stop(); blinking = FALSE; } delete [] disstrU; } // paint Event //////////////////////////////////////////////////// /*! The difference of this routine vs. the `setImage' is, that the drawing does not include a difference analysis between the old and the new image. Instead, the internal image is used and the painting bound by the PaintEvent box. */ void Widget::paintEvent( QPaintEvent* pe ) { //{ static int cnt = 0; printf("paint %d\n",cnt++); } const QPixmap* pm = backgroundPixmap(); QPainter paint; setUpdatesEnabled(FALSE); paint.begin( this ); paint.setBackgroundMode( TransparentMode ); HCNT("paintEvent"); // Note that the actual widget size can be slightly larger // that the image (the size is truncated towards the smaller // number of characters in `resizeEvent'. The paint rectangle // can thus be larger than the image, but less then the size // of one character. QRect rect = pe->rect().intersect(contentsRect()); QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); int lux = QMIN(columns-1, QMAX(0,(rect.left() - tLx - blX ) / font_w)); int luy = QMIN(lines-1, QMAX(0,(rect.top() - tLy - bY ) / font_h)); int rlx = QMIN(columns-1, QMAX(0,(rect.right() - tLx - blX ) / font_w)); int rly = QMIN(lines-1, QMAX(0,(rect.bottom() - tLy - bY ) / font_h)); /* printf("paintEvent: %d..%d, %d..%d (%d..%d, %d..%d)\n",lux,rlx,luy,rly, rect.left(), rect.right(), rect.top(), rect.bottom()); */ // if (pm != NULL && color_table[image->b].transparent) // erase(rect); // BL: I have no idea why we need this, and it breaks the refresh. QChar *disstrU = new QChar[columns]; for (int y = luy; y <= rly; y++) for (int x = lux; x <= rlx; x++) { int len = 1; disstrU[0] = fontMap(image[loc(x,y)].c); int cf = image[loc(x,y)].f; int cb = image[loc(x,y)].b; int cr = image[loc(x,y)].r; while (x+len <= rlx && image[loc(x+len,y)].f == cf && image[loc(x+len,y)].b == cb && image[loc(x+len,y)].r == cr ) { disstrU[len] = fontMap(image[loc(x+len,y)].c); len += 1; } QString unistr(disstrU,len); drawAttrStr(paint, QRect(blX+tLx+font_w*x,bY+tLy+font_h*y,font_w*len,font_h), unistr, image[loc(x,y)], pm != NULL, false); x += len - 1; } delete [] disstrU; drawFrame( &paint ); paint.end(); setUpdatesEnabled(TRUE); } void Widget::blinkEvent() { blinking = !blinking; repaint(FALSE); } /* ------------------------------------------------------------------------- */ /* */ /* Resizing */ /* */ /* ------------------------------------------------------------------------- */ void Widget::resizeEvent(QResizeEvent* ev) { // printf("resize: %d,%d\n",ev->size().width(),ev->size().height()); //printf("approx: %d,%d\n",ev->size().width()/font_w,ev->size().height()/font_h); //printf("leaves: %d,%d\n",ev->size().width()%font_w,ev->size().height()%font_h); //printf("curren: %d,%d\n",width(),height()); HCNT("resizeEvent"); // see comment in `paintEvent' concerning the rounding. //FIXME: could make a routine here; check width(),height() assert(ev->size().width() == width()); assert(ev->size().height() == height()); propagateSize(); } void Widget::propagateSize() { Character* oldimg = image; int oldlin = lines; int oldcol = columns; makeImage(); // we copy the old image to reduce flicker int lins = QMIN(oldlin,lines); int cols = QMIN(oldcol,columns); if (oldimg) { for (int lin = 0; lin < lins; lin++) memcpy((void*)&image[columns*lin], (void*)&oldimg[oldcol*lin],cols*sizeof(Character)); free(oldimg); //FIXME: try new,delete } else clearImage(); //NOTE: control flows from the back through the chest right into the eye. // `emu' will call back via `setImage'. resizing = TRUE; emit changedImageSizeSignal(lines, columns); // expose resizeEvent resizing = FALSE; } /* ------------------------------------------------------------------------- */ /* */ /* Scrollbar */ /* */ /* ------------------------------------------------------------------------- */ void Widget::scrollChanged(int) { emit changedHistoryCursor(scrollbar->value()); //expose } void Widget::setScroll(int cursor, int slines) { disconnect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); scrollbar->setRange(0,slines); scrollbar->setSteps(1,lines); scrollbar->setValue(cursor); connect(scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollChanged(int))); } void Widget::setScrollbarLocation(int loc) { if (scrollLoc == loc) return; // quickly scrollLoc = loc; propagateSize(); update(); } /* ------------------------------------------------------------------------- */ /* */ /* Mouse */ /* */ /* ------------------------------------------------------------------------- */ /*! Three different operations can be performed using the mouse, and the routines in this section serve all of them: 1) The press/release events are exposed to the application 2) Marking (press and move left button) and Pasting (press middle button) 3) The right mouse button is used from the configuration menu NOTE: During the marking process we attempt to keep the cursor within the bounds of the text as being displayed by setting the mouse position whenever the mouse has left the text area. Two reasons to do so: 1) QT does not allow the `grabMouse' to confine-to the Widget. Thus a `XGrapPointer' would have to be used instead. 2) Even if so, this would not help too much, since the text area of the Widget is normally not identical with it's bounds. The disadvantage of the current handling is, that the mouse can visibly leave the bounds of the widget and is then moved back. Because of the current construction, and the reasons mentioned above, we cannot do better without changing the overall construction. */ /*! */ void Widget::mousePressEvent(QMouseEvent* ev) { //printf("press [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); if ( !contentsRect().contains(ev->pos()) ) return; QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); word_selection_mode = FALSE; //printf("press top left [%d,%d] by=%d\n",tLx,tLy, bY); if ( ev->button() == LeftButton) { QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); if ( ev->state() & ControlButton ) preserve_line_breaks = FALSE ; if (mouse_marks || (ev->state() & ShiftButton)) { emit clearSelectionSignal(); iPntSel = pntSel = pos; actSel = 1; // left mouse button pressed but nothing selected yet. grabMouse( /*crossCursor*/ ); // handle with care! } else { emit mouseSignal( 0, pos.x() + 1, pos.y() + 1 ); // left button } } if ( ev->button() == MidButton ) { emitSelection(); } if ( ev->button() == RightButton ) // Configure { emit configureRequest( this, ev->state()&(ShiftButton|ControlButton), ev->x(), ev->y() ); } } void Widget::mouseMoveEvent(QMouseEvent* ev) { // for auto-hiding the cursor, we need mouseTracking if (ev->state() == NoButton ) return; if (actSel == 0) return; // don't extend selection while pasting if (ev->state() & MidButton) return; //if ( !contentsRect().contains(ev->pos()) ) return; QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); int scroll = scrollbar->value(); // we're in the process of moving the mouse with the left button pressed // the mouse cursor will kept catched within the bounds of the text in // this widget. // Adjust position within text area bounds. See FIXME above. QPoint pos = ev->pos(); if ( pos.x() < tLx+blX ) pos.setX( tLx+blX ); if ( pos.x() > tLx+blX+columns*font_w-1 ) pos.setX( tLx+blX+columns*font_w ); if ( pos.y() < tLy+bY ) pos.setY( tLy+bY ); if ( pos.y() > tLy+bY+lines*font_h-1 ) pos.setY( tLy+bY+lines*font_h-1 ); // check if we produce a mouse move event by this if ( pos != ev->pos() ) cursor().setPos(mapToGlobal(pos)); if ( pos.y() == tLy+bY+lines*font_h-1 ) { scrollbar->setValue(scrollbar->value()+yMouseScroll); // scrollforward } if ( pos.y() == tLy+bY ) { scrollbar->setValue(scrollbar->value()-yMouseScroll); // scrollback } QPoint here = QPoint((pos.x()-tLx-blX)/font_w,(pos.y()-tLy-bY)/font_h); QPoint ohere; bool swapping = FALSE; if ( word_selection_mode ) { // Extend to word boundaries int i; int selClass; bool left_not_right = ( here.y() < iPntSel.y() || here.y() == iPntSel.y() && here.x() < iPntSel.x() ); bool old_left_not_right = ( pntSel.y() < iPntSel.y() || pntSel.y() == iPntSel.y() && pntSel.x() < iPntSel.x() ); swapping = left_not_right != old_left_not_right; // Find left (left_not_right ? from here : from start) QPoint left = left_not_right ? here : iPntSel; i = loc(left.x(),left.y()); selClass = charClass(image[i].c); while ( left.x() > 0 && charClass(image[i-1].c) == selClass ) { i--; left.rx()--; } // Find left (left_not_right ? from start : from here) QPoint right = left_not_right ? iPntSel : here; i = loc(right.x(),right.y()); selClass = charClass(image[i].c); while ( right.x() < columns-1 && charClass(image[i+1].c) == selClass ) { i++; right.rx()++; } // Pick which is start (ohere) and which is extension (here) if ( left_not_right ) { here = left; ohere = right; } else { here = right; ohere = left; } } if (here == pntSel && scroll == scrollbar->value()) return; // not moved if ( word_selection_mode ) { if ( actSel < 2 || swapping ) { emit beginSelectionSignal( ohere.x(), ohere.y() ); } } else if ( actSel < 2 ) { emit beginSelectionSignal( pntSel.x(), pntSel.y() ); } actSel = 2; // within selection pntSel = here; emit extendSelectionSignal( here.x(), here.y() ); } void Widget::mouseReleaseEvent(QMouseEvent* ev) { //printf("release [%d,%d] %d\n",ev->x()/font_w,ev->y()/font_h,ev->button()); if ( ev->button() == LeftButton) { if ( actSel > 1 ) emit endSelectionSignal(preserve_line_breaks); preserve_line_breaks = TRUE; actSel = 0; //FIXME: emits a release event even if the mouse is // outside the range. The procedure used in `mouseMoveEvent' // applies here, too. QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); if (!mouse_marks && !(ev->state() & ShiftButton)) emit mouseSignal( 3, // release (ev->x()-tLx-blX)/font_w + 1, (ev->y()-tLy-bY)/font_h + 1 ); releaseMouse(); } } void Widget::mouseDoubleClickEvent(QMouseEvent* ev) { if ( ev->button() != LeftButton) return; QPoint tL = contentsRect().topLeft(); int tLx = tL.x(); int tLy = tL.y(); QPoint pos = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); // pass on double click as two clicks. if (!mouse_marks && !(ev->state() & ShiftButton)) { emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button emit mouseSignal( 3, pos.x()+1, pos.y()+1 ); // release emit mouseSignal( 0, pos.x()+1, pos.y()+1 ); // left button return; } emit clearSelectionSignal(); QPoint bgnSel = pos; QPoint endSel = QPoint((ev->x()-tLx-blX)/font_w,(ev->y()-tLy-bY)/font_h); int i = loc(bgnSel.x(),bgnSel.y()); iPntSel = bgnSel; word_selection_mode = TRUE; // find word boundaries... int selClass = charClass(image[i].c); { // set the start... int x = bgnSel.x(); while ( x > 0 && charClass(image[i-1].c) == selClass ) { i--; x--; } bgnSel.setX(x); emit beginSelectionSignal( bgnSel.x(), bgnSel.y() ); // set the end... i = loc( endSel.x(), endSel.y() ); x = endSel.x(); while( x < columns-1 && charClass(image[i+1].c) == selClass ) { i++; x++ ; } endSel.setX(x); actSel = 2; // within selection emit extendSelectionSignal( endSel.x(), endSel.y() ); emit endSelectionSignal(preserve_line_breaks); preserve_line_breaks = TRUE; } } void Widget::focusInEvent( QFocusEvent * ) { // do nothing, to prevent repainting } |