# # GADGETS/INPUT.D - Text Input Gadget # public subclass InputFrame from Frame { int holding; # keyboard focus held int blinking = -1; # blinking cursor position or -1 if disabled int blinkon; # blink on/off/on/off. The on state. int xstart; # start of input box (after text) int bufBeg; # first column displayed int bufIdx; # current cursor position int bufLen; # characters in buffer int maxLen; # maximum length int blocked; # blinking cursor support public char buf[257]; # input buffer (may be refined) const char *text; # may be NULL } public method void InputFrame.createInput( lvalue InputFrame @this, Frame @parent, int w, # can be specified as 0 int h, # can be specified as 0 int mount, const char *text = NULL,# may be omitted int cwidth = 20, # may be omitted (approximate) int cmax = 256 # may be omitted ) { int xstart = 2; if (w <= 0) { w = parent->textWidth("A", 1) * cwidth; if (text != NULL) { xstart = parent->textWidth(text, Str.strlen(text)) + 4; w += xstart; } w += 2; } if (h <= 0) h = parent->textHeight() + (2 + 2); this.createFrame("input", parent, w, h, mount); this->maxLen = cmax; this->xstart = xstart; this->text = text; this->refreshVisibleFrame(); } refine public method int InputFrame.mouseMoved(Event @ev) { # (drag select) # return(0); } # Deal with button and mouse presses, including mouse double click # and mouse-drag-select # refine public method int InputFrame.buttonPressed(Event @ev) { stdio.stdout->show("BUTTON"); switch(ev->keySym) { case Event.XK_Pointer_Button1: if (this.mouseInBounds(ev)) { this.holding = ev->keySym; this.setKeyFocus(); } this.refreshVisibleFrame(); break; case -Event.XK_Pointer_Button1: # (drag select) # break; default: if (this.holding == 0) break; if (ev->keyChar == 0UB) { switch(ev->keySym) { case Event.XK_Left: this.inputCursorLeft(); break; case Event.XK_Right: this.inputCursorRight(); break; case Event.XK_Home: case Event.XK_End: case Event.XK_Begin: case Event.XK_Clear: default: break; } } else if (ev->keySym > 0 && ev->keyChar) { switch(ev->keyChar) { case 8: this.inputBackspace(); break; case 127: this.inputDelete(); break; case 13: this.clearKeyFocus(); this.holding = 0; this.blinkUpdate(); Thread.wakeup(&this.blocked); this.buf[this.bufLen] = 0; this.inputExecute(&this.buf[0]); break; default: if (this.bufLen != this.maxLen) { Sys.bcopy(&this.buf[this.bufIdx], &this.buf[this.bufIdx + 1], this.bufLen - this.bufIdx); this.buf[this.bufIdx] = ev->keyChar; ++this.bufIdx; ++this.bufLen; this.refreshVisibleFrame(); } else { # XXX BEEP XXX } break; } } break; } return(0); } # This method is called when you hit backspace # # public method void InputFrame.inputBackspace() { if (this.bufIdx) { Sys.bcopy(&this.buf[this.bufIdx], &this.buf[this.bufIdx - 1], this.bufLen - this.bufIdx); --this.bufIdx; --this.bufLen; this.refreshVisibleFrame(); } } # This method is called when you hit delete # # public method void InputFrame.inputDelete() { if (this.bufIdx != this.bufLen) { Sys.bcopy(&this.buf[this.bufIdx + 1], &this.buf[this.bufIdx], this.bufLen - this.bufIdx - 1); --this.bufLen; this.refreshVisibleFrame(); } } # This method is called when you hit the left arrow # # public method void InputFrame.inputCursorLeft() { if (this.bufIdx) { --this.bufIdx; this.refreshVisibleFrame(); } } # This method is called when you hit the right arrow # # public method void InputFrame.inputCursorRight() { if (this.bufIdx != this.bufLen) { ++this.bufIdx; this.refreshVisibleFrame(); } } # This method is called when you hit in the input box # # public method void InputFrame.inputExecute(const char *text) { # user refines } # This method is called when someone grabs the focus from us # # #refine public method #void #InputFrame.lostKeyFocus() #{ # this.holding = 0; # this.refreshFrame(); #} # Regenerate the input frame graphic # # refine public method bool InputFrame.refreshFrame(int x, int y, int w, int h) { Pen @pen1; Pen @pen2; int descent; int xstart = this.xstart; const char *text; int textLen; pen1 = this.darkPen; pen2 = this.lightPen; this.pen = pen1; this.drawLine(xstart, 0, this.bounds.w - 2, 0); this.drawLine(xstart, 1, this.bounds.w - 3, 1); this.drawLine(xstart, 0, xstart, this.bounds.h - 1); this.drawLine(xstart + 1, 0, xstart + 1, this.bounds.h - 2); this.pen = pen2; this.drawLine(xstart + 1, this.bounds.h - 1, this.bounds.w - 1, this.bounds.h - 1); this.drawLine(xstart + 2, this.bounds.h - 2, this.bounds.w - 1, this.bounds.h - 2); this.drawLine(this.bounds.w - 1, 0, this.bounds.w - 1, this.bounds.h - 1); this.drawLine(this.bounds.w - 2, 1, this.bounds.w - 2, this.bounds.h - 1); this.pen = this.textPen; descent = this.textPen->textDescent(); if (this.text != NULL) this.drawText(2, this.bounds.h - 2 - descent, this.text, Str.strlen(this.text)); text = &this.buf[this.bufBeg]; textLen = this.bufLen - this.bufBeg; # XXX # this.fillText(xstart + 3, this.bounds.h - 2 - descent, this.bounds.w - xstart - 3, text, textLen); # Create a thread to handle the blinking cursor, else wakeup # the thread. # if (this.blinking < 0 && this.holding != 0) this.cursorThread(); else Thread.wakeup(&this.blocked); return(0); } public method void InputFrame.setBlinking(int y) { if (y) { if (this.blinking < 0) { this.holding = 1; this.cursorThread(); } } else if (this.blinking >= 0) { this.holding = 0; Thread.wakeup(&this.blocked); } } /* * CURSORTHREAD() * * The cursor thread is responsible for blinking the cursor and it * runs while we hold the keyboard focus. Since the thread does * not run in response to an X event we cannot count on the X output * queue being flushed after an update, so we have to do it ourselves. */ public thread soft method void InputFrame.cursorThread() { InputFrame @frame; frame = &this; # copy argument this.blinking = 1; result; # allow parent to continue while (frame->holding != 0) { frame->blinkUpdate(); frame->flush(); Thread.tsleep(&frame->blocked, 1000 / 3); frame->blinkon = 1 - frame->blinkon; } frame->blinking = -1; } /* * BLINKUPDATE() * * Update the vertical line graphic representing the cursor position. * This function does not do the actual blinking but it will reset * the blink state to on if a positional change has occured, and to * off if we no longer have the keyboard focus. */ public method void InputFrame.blinkUpdate() { int pos; pos = this.textWidth(&this.buf[this.bufBeg], this.bufIdx - this.bufBeg); if (this.holding != 0) { if (pos != this.blinking || this.blinkon == -1) { this.blinking = pos; this.blinkon = 1; } } else { this.blinkon = 0; } if (this.blinkon) { this.pen = this.lightPen; this.drawLine(this.xstart + 2 + pos, 2, this.xstart + 2 + pos, this.bounds.h - 2); } else { this.pen = this.darkPen; this.drawLine(this.xstart + 2 + pos, 2, this.xstart + 2 + pos, this.bounds.h - 2); } } public method void InputFrame.setTextFGColor(int r, int g, int b) { this.textPen->setFGColor(r, g, b); } public method void InputFrame.setTextBGColor(int r, int g, int b) { this.textPen->setBGColor(r, g, b); }