// // Copyright (C) 1996 Ben Ross // // You may distribute under the terms of the GNU General Public // License as specified in the COPYING file. // // $Id: XeTextField.C,v 1.6 2000/09/13 14:33:29 ben Exp $ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _SGI_SOURCE #include #endif #define STIMEOUT 15000 // microsecond interval between scrolling #define HSTOL 6 // Horizontal speed divisor XeTextField::XeTextField(XeObject *parent, const char *name, const char* className) : XeTextBase(parent, name, className) { _xinset = 3; _yinset = 3; _cutBuffer = NULL; _doingPaste = FALSE; _lineNumbers = FALSE; resize(5, 1); } XeTextField::~XeTextField(void) { } int XeTextField::doEvent(XEvent* theEvent) { switch(theEvent->type) { case KeyPress: doKey((XKeyEvent *)theEvent); break; case ButtonPress: case ButtonRelease: doButton((XButtonEvent *)theEvent); break; case MotionNotify: doMotion((XMotionEvent *)theEvent); break; default: XeTextBase::doEvent(theEvent); break; } return 0; } void XeTextField::setString(const char* str) { char* text = strdup(str); setText(text, strlen(text)); setSelection(_text->getSize(), _text->getSize(), 0, 0, FALSE); redrawLines(0, 0, 0, -1, TRUE); drawCaret(TRUE); } void XeTextField::doKey(XKeyEvent* event) { int bufsize = 20; char buffer[20], c; KeySym key; uint state = ControlMask | Mod1Mask; if(event->state & state) return; XLookupString(event, buffer, bufsize, &key, NULL); if(key==XK_Alt_L || key==XK_Alt_R || key==XK_Control_L || key==XK_Control_R || key==XK_Shift_L || key==XK_Shift_R) return; if((key==XK_Left)||(key==XK_Right)) { doArrow(event, key); return; } if((key==XK_Return) || (key==XK_KP_Enter) || (key==XK_Linefeed)) { callCallbacks(XE_CB_ACTIVATE, NULL); return; } if((key==XK_BackSpace) || (key==XK_Delete)) { c = '\b'; replaceSelection(&c, 0); } else if((key >= XK_KP_Space)&&(key <= XK_KP_9) || (key >= XK_space)&&(key <= XK_asciitilde)) { // handle normal key replaceSelection(&buffer[0], 1); } else if(key == XK_Tab) { c = '\t'; replaceSelection(&c, 1); } autoScroll(); callCallbacks(XE_CB_TEXTKEY, NULL); } void XeTextField::doArrow(XKeyEvent* , KeySym key) { switch(key) { case XK_Left: if (_sel.selStart == 0) setSelection(0, 0, 0); else setSelection(_sel.selStart-1, _sel.selStart-1, _sel.lineStart); break; case XK_Right: if ((ulong)_sel.selEnd == _text->getSize()) setSelection(_text->getSize(), _text->getSize(), 0); else setSelection(_sel.selEnd+1, _sel.selEnd+1, _sel.lineEnd); break; } autoScroll(); } void XeTextField::doButton(XButtonEvent *event) { long col, line; getPosition(event->x, event->y, line, col); if(event->type == ButtonPress && (event->button == Button1)) { setSelection(line, col); _base->takeFocus(FALSE); setFocusWindow(); takeFocus(TRUE); } else if(event->type == ButtonPress && (event->button == Button3)) { _lastCol = col - _leftColumn; } else if(event->type == ButtonPress && event->button == Button2) { if(_selOwner == this && selLength()) { int len = selLength(); _cutBuffer = new char[len + 1]; memcpy(_cutBuffer, &(*_text)[_sel.selStart], len); _cutBuffer[len] = 0; } setSelection(0, col); _doingPaste = TRUE; } if(event->type == ButtonRelease && event->button == Button1) { if(selLength()) { setXSelection(event->time); } else if(_selOwner == this) { clearXSelection(event->time); } } else if(event->type == ButtonRelease && event->button == Button2) { xpaste(); _doingPaste = FALSE; } } void XeTextField::doMotion(XMotionEvent* event) { if(event->state & Button3Mask) { dragAround(event); return; } if(event->state & Button1Mask) { dragSelect(event); autoMotionScroll(event); return; } if(event->state & Button2Mask) { long line, col; getPosition(event->x, event->y, line, col); setSelection(0, col); autoMotionScroll(event, TRUE); } } void XeTextField::dragAround(XMotionEvent *event) { XEvent current, ahead; XMotionEvent *theEvent = event; while(XEventsQueued(gDisplay, QueuedAfterReading) > 0) { XPeekEvent(gDisplay, &ahead); if(ahead.type != MotionNotify) break; if(ahead.xmotion.window != _window) break; XNextEvent(gDisplay, ¤t); theEvent = (XMotionEvent*)¤t; } int x = theEvent->x - xinset(); int newcol = x / cwidth(); int xdiff = newcol - _lastCol; if(xdiff) { scroll(0, xdiff); syncScroll(); } _lastCol = newcol; } void XeTextField::dragSelect(XMotionEvent *event) { long line, col; getPosition(event->x, event->y, line, col); extendSelection(line, col, SelChar); } void XeTextField::autoMotionScroll(XMotionEvent* event, bool paste) { // Here's a bit 'o a hack until timeout callbacks or // until Krypton! Take over the main event loop with select // so auto-scrolling happens in defined time increments. XEvent theEvent; int xfd = ConnectionNumber(gDisplay); fd_set rset; timeval time; long line, col, mask = 0xffffffff; int amt=0, offset; ScrollSide side; if(event->x < xinset()) { side = Left; offset = xinset() - event->x; } else if(event->x > xinset() + textWidth()) { side = Right; offset = event->x - xinset() - textWidth(); } else { return; } getPosition(event->x, event->y, line, col); if(side == Left || side == Right) amt = DIVPLUS(DIVPLUS(offset, cwidth()), HSTOL); while(1) { // scroll according to which side the mouse dragged // off the window. switch(side) { case Left: if(!scrollH(amt)) return; col -= amt; if(paste) setSelection(line, col); else extendSelection(line, col, SelChar); break; case Right: if(!scrollH(-amt)) return; col += amt; if(paste) setSelection(line, col); else extendSelection(line, col, SelChar); break; } XFlush(gDisplay); FD_ZERO(&rset); FD_SET(xfd, &rset); time.tv_sec = 0; time.tv_usec = STIMEOUT; // timeout microseconds while(time.tv_sec + time.tv_usec > 0) { #ifdef linux select(FD_SETSIZE, &rset, NULL, NULL, &time); #else portaselect(FD_SETSIZE, &rset, NULL, NULL, &time); #endif if(FD_ISSET(xfd, &rset)) { while(XCheckMaskEvent(gDisplay, mask, &theEvent)) { if(theEvent.type == ButtonRelease) { if(selLength()) setXSelection(theEvent.xbutton.time); return; } else if(theEvent.type == GraphicsExpose) { doExpose((XExposeEvent*)&theEvent); } else if(theEvent.type == MotionNotify) { getPosition(theEvent.xmotion.x, theEvent.xmotion.y, line, col); if(side == Left) { if(theEvent.xmotion.x >= xinset()) return; offset = xinset() - theEvent.xmotion.x; amt = DIVPLUS(DIVPLUS(offset, cwidth()), HSTOL); } else if(side == Right) { if(theEvent.xmotion.x <= xinset() + textWidth()) return; offset = theEvent.xmotion.x - xinset() - textWidth(); amt = DIVPLUS(DIVPLUS(offset, cwidth()), HSTOL); } } } } } } } // X Selection support crap void XeTextField::selectionNotify(char* data, int len) { doPaste(data, len); } void XeTextField::selectionClear(void) { if(selLength()) { setSelection(_sel.selStart, _sel.selStart, _sel.lineStart); } } void XeTextField::xpaste(void) { if(_cutBuffer) { replaceSelection(_cutBuffer, strlen(_cutBuffer)); autoScroll(); delete[] _cutBuffer; _cutBuffer = NULL; clearXSelection(CurrentTime); return; } if(_selOwner == this) { clearXSelection(CurrentTime); return; } if(_selOwner) { char* data; int len; _selOwner->getSelectionData(&data, len); doPaste(data, len); } else { getRemoteSelection(CurrentTime); } } void XeTextField::doPaste(char* data, int len) { if(!len) return; char* p = data; int newlen = 0; while(*p != '\n' && newlen < len) { p++; newlen++; } replaceSelection(data, newlen); autoScroll(); } void XeTextField::selectAll(bool xselect) { setSelection(0, _text->getSize(), 0, 0, TRUE); if (xselect && selLength() && _selOwner != this) setXSelection(CurrentTime); }