#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();
}