#include #include /******************************************************************** * Parameters */ char *value = "The quick brown fox jumped over the lazy dog." int offset = 0; int selfrom = 0; int selto = 0; boolean editable = true; name hilite = /none; persist(value); persist(offset); persist(selfrom); persist(selto); persist(scrollthreshold); persist(scrolllinedelay); #define MARGIN 4 #define BASE 3 #define SCROLL 20 #define LSCROLL (offset) #define RSCROLL ((stringwidthonly(textfont,value) + offset) > (w - MARGIN*2)) #define LMARGIN (LSCROLL ? (OLScrollButW + 2) : 0) #define RMARGIN (RSCROLL ? (OLScrollButW + 2) : 0) #define ASCENT fontascent(textfont) #define DESCENT fontdescent(textfont) #define HEIGHT fontheight(textfont) float minwidth = (OLScrollButW + 8) * 2; float minheight = OLScrollButH; void action() { Send Action(value); } /******************************************************************** * Painting */ void PaintLScroll() { if (LSCROLL) { setcolor(BG); rectpath(0,0,OLScrollButW + 2,h); fill(); OLScrollButLeft(hilite == /left,0,0); } } void PaintRScroll() { if (RSCROLL) { setcolor(BG); rectpath(w - (OLScrollButW + 2),0,OLScrollButW + 2,h); fill(); OLScrollButRight(hilite == /right,w - OLScrollButW,0); } } void PaintText(float p1, float p4) { name state = focus_state(); float lm, rm, p2, p3; p1 = max(p1,lm = LMARGIN); p4 = min(p4,w - (rm = RMARGIN)); p2 = MARGIN + stringwidthonly(textfont,headinterval(value,selfrom)) + offset; p3 = MARGIN + stringwidthonly(textfont,headinterval(value,selto)) + offset; OLHLine(false,lm,1,w - (lm + rm)); setcolor(BG); newpath(); rectpath(p1,2,p4-p1,h); if ((selfrom < selto) && (p3 > p1) && (p2 < p4)) { p3 = min(p3,p4); gsave(); rectpath(max(p1,p2),BASE,p3-max(p1,p2),HEIGHT); eoclip(); fill(); setcolor(FG); moveto(MARGIN + offset,BASE + DESCENT); setfont(textfont); show(value); grestore(); newpath(); gsave(); rectpath(max(p1,p2),BASE,p3-max(p1,p2),HEIGHT); clip(); if (state() != /none) { setcolor(FG); fill(); setcolor(BG); } else { setcolor(BG); fill(); setcolor(FG); } moveto(p2,BASE + fontdescent(textfont)); setfont(textfont); show(getinterval(value,selfrom,selto - selfrom)); grestore(); } else { gsave(); clip(); fill(); setcolor(FG); moveto(MARGIN + offset,BASE + DESCENT); setfont(textfont); show(value); grestore(); if ((p2 > p1) && (p2 < p4) && (state != /none)) { moveto(p2,BASE + DESCENT); OLCaret(state == /active); } } newpath(); } void Paint() { PaintLScroll(); PaintRScroll(); PaintText(0,w); } void focus_change() { gexec(/Paint); } /******************************************************************** * Update */ void update_value(float off, int f, int t, char *val) { boolean r, l; gsave(); ginit(); f = min(length(val),max(0,f)); t = min(length(val),max(t,f)); off = min(0,max((w - MARGIN*2)-stringwidthonly(textfont,val),off)); if ((val != value) || (t != selto) || (f != selfrom) || (off != offset)) { r = RSCROLL; l = LSCROLL; value = val; selto = t; selfrom = f; offset = off; if (visible) { if (RSCROLL && (!r)) PaintRScroll(); if (LSCROLL && (!l)) PaintLScroll(); PaintText(0,w); } } grestore(); } /******************************************************************** * Tracking */ int trackstart = 0; void track_lscroll() { if (LSCROLL) update_value(offset + SCROLL,selfrom,selto,value); } void track_rscroll() { if (RSCROLL) update_value(offset - SCROLL,selfrom,selto,value); } void track_select() { int p; p = stringpos(textfont,value,trackx - (MARGIN + offset)); update_value(offset,min(trackstart,p),max(trackstart,p),value); } void OnSelect() { float X = mouseevent.XLocation; name state = focus_state(); if (X < LMARGIN) { if (LSCROLL) { if (state == /none) request_focus(); hilite = /left; PaintLScroll(); track_timed(scrollthreshold,scrolllinedelay,/track_lscroll); unpromote(/hilite); PaintLScroll(); } } else if (X > (w - RMARGIN)) { if (RSCROLL) { if (state == /none) request_focus(); hilite = /right; PaintRScroll(); track_timed(scrollthreshold,scrolllinedelay,/track_rscroll); unpromote(/hilite); PaintRScroll(); } } else { trackstart = stringpos(textfont,value,X - (MARGIN + offset)); if (state == /none) { request_focus(); if (track_state() == /mouse_drag) { update_value(offset,trackstart,trackstart,value); track(/track_select); } } else { update_value(offset,trackstart,trackstart,value); track(/track_select); } unpromote(/trackstart); } } float compute_offset(char *val,int i) { float W = stringwidthonly(textfont,headinterval(val,i)); if (W < -offset) return OLScrollButW + MARGIN - W; if ((W+offset) > (w - OLScrollButW)) return w/2 - W; return offset; } void setvalue(char *val) { update_value(0,0,length(val),val); } void setselect(int f, int t) { update_value(compute_offset(value,f),f,t,value); } void clearselection(int f, int t) { char *val; f = max(0,min(length(value),f)); val = append(headinterval(value,f),tailinterval(value,max(f,t))); update_value(compute_offset(val,f),f,f,val); } void insertselection(char *val) { int f = selfrom + length(val); val = append(headinterval(value,selfrom), append(val,tailinterval(value,selto))); update_value(compute_offset(val,f),f,f,val); } void onkey(int k) { gsave(); ginit(); switch (k) { case '\t': case '\n': case '\r': action(); break; case 8: case 127: if (selfrom == selto) clearselection(selfrom-1,selto); else clearselection(selfrom,selto); break; default: insertselection(cvis(k)); } grestore(); }