#include <u.h>#include <libc.h>#include <draw.h>#include <thread.h>#include <mouse.h>#include <keyboard.h>#include <control.h> typedef struct Text Text; struct Text{ Control; int border; int topline; int scroll; int nvis; int lastbut; CFont *font; CImage *image; CImage *textcolor; CImage *bordercolor; CImage *selectcolor; Rune **line; int selectmode; uchar *selected; int nline; int align;}; enum{ Selsingle, Selmulti,}; enum{ EAccumulate, EAdd, EAlign, EBorder, EBordercolor, EClear, EDelete, EFocus, EFont, EHide, EImage, ERect, EReplace, EReveal, EScroll, ESelect, ESelectcolor, ESelectmode, EShow, ESize, ETextcolor, ETopline, EValue,}; static char *cmds[] = { [EAccumulate] = "accumulate", [EAdd] = "add", [EAlign] = "align", [EBorder] = "border", [EBordercolor] = "bordercolor", [EClear] = "clear", [EDelete] = "delete", [EFocus] = "focus", [EFont] = "font", [EHide] = "hide", [EImage] = "image", [ERect] = "rect", [EReplace] = "replace", [EReveal] = "reveal", [EScroll] = "scroll", [ESelect] = "select", [ESelectcolor] = "selectcolor", [ESelectmode] = "selectmode", [EShow] = "show", [ESize] = "size", [ETextcolor] = "textcolor", [ETopline] = "topline", [EValue] = "value", nil}; static void textshow(Text*);static void texttogglei(Text*, int);static int texttoggle(Text*, Point); static voidtextmouse(Control *c, Mouse *m){ Text *t; int sel; t = (Text*)c; if(t->lastbut != (m->buttons&7)){ if(m->buttons & 7){ sel = texttoggle(t, m->xy); if(sel >= 0) chanprint(t->event, "%q: select %d %d", t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0); } t->lastbut = m->buttons & 7; }} static voidtextfree(Control *c){ int i; Text *t; t = (Text*)c; _putctlfont(t->font); _putctlimage(t->image); _putctlimage(t->textcolor); _putctlimage(t->bordercolor); _putctlimage(t->selectcolor); for(i=0; i<t->nline; i++) free(t->line[i]); free(t->line); free(t->selected);} static voidtextshow(Text *t){ Rectangle r, tr; Point p; int i, ntext; Font *f; Rune *text; if (t->hidden) return; r = t->rect; f = t->font->font; draw(t->screen, r, t->image->image, nil, t->image->image->r.min); if(t->border > 0){ border(t->screen, r, t->border, t->bordercolor->image, t->bordercolor->image->r.min); r = insetrect(r, t->border); } tr = r; t->nvis = Dy(r)/f->height; for(i=t->topline; i<t->nline && i<t->topline+t->nvis; i++){ text = t->line[i]; ntext = runestrlen(text); r.max.y = r.min.y+f->height; if(t->selected[i]) draw(t->screen, r, t->selectcolor->image, nil, ZP); p = _ctlalignpoint(r, runestringnwidth(f, text, ntext), f->height, t->align); _string(t->screen, p, t->textcolor->image, ZP, f, nil, text, ntext, tr, nil, ZP); r.min.y += f->height; } flushimage(display, 1);} static voidtextctl(Control *c, CParse *cp){ int cmd, i, n; Rectangle r; Text *t; Rune *rp; t = (Text*)c; cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); switch(cmd){ default: ctlerror("%q: unrecognized message '%s'", t->name, cp->str); break; case EAlign: _ctlargcount(t, cp, 2); t->align = _ctlalignment(cp->args[1]); break; case EBorder: _ctlargcount(t, cp, 2); if(cp->iargs[1] < 0) ctlerror("%q: bad border: %c", t->name, cp->str); t->border = cp->iargs[1]; break; case EBordercolor: _ctlargcount(t, cp, 2); _setctlimage(t, &t->bordercolor, cp->args[1]); break; case EClear: _ctlargcount(t, cp, 1); for(i=0; i<t->nline; i++) free(t->line[i]); free(t->line); free(t->selected); t->line = ctlmalloc(sizeof(Rune*)); t->selected = ctlmalloc(1); t->nline = 0; textshow(t); break; case EDelete: _ctlargcount(t, cp, 2); i = cp->iargs[1]; if(i<0 || i>=t->nline) ctlerror("%q: line number out of range: %s", t->name, cp->str); free(t->line[i]); memmove(t->line+i, t->line+i+1, (t->nline-(i+1))*sizeof(Rune*)); memmove(t->selected+i, t->selected+i+1, t->nline-(i+1)); t->nline--; textshow(t); break; case EFocus: break; case EFont: _ctlargcount(t, cp, 2); _setctlfont(t, &t->font, cp->args[1]); break; case EHide: _ctlargcount(t, cp, 1); t->hidden = 1; break; case EImage: _ctlargcount(t, cp, 2); _setctlimage(t, &t->image, cp->args[1]); break; case ERect: _ctlargcount(t, cp, 5); r.min.x = cp->iargs[1]; r.min.y = cp->iargs[2]; r.max.x = cp->iargs[3]; r.max.y = cp->iargs[4]; if(Dx(r)<=0 || Dy(r)<=0) ctlerror("%q: bad rectangle: %s", t->name, cp->str); t->rect = r; t->nvis = (Dy(r)-2*t->border)/t->font->font->height; break; case EReplace: _ctlargcount(t, cp, 3); i = cp->iargs[1]; if(i<0 || i>=t->nline) ctlerror("%q: line number out of range: %s", t->name, cp->str); free(t->line[i]); t->line[i] = _ctlrunestr(cp->args[2]); textshow(t); break; case EReveal: _ctlargcount(t, cp, 1); t->hidden = 0; textshow(t); break; case EScroll: _ctlargcount(t, cp, 2); t->scroll = cp->iargs[1]; break; case ESelect: if(cp->nargs!=2 && cp->nargs!=3) badselect: ctlerror("%q: bad select message: %s", t->name, cp->str); if(cp->nargs == 2){ if(strcmp(cp->args[1], "all") == 0){ memset(t->selected, 1, t->nline); break; } if(strcmp(cp->args[1], "none") == 0){ memset(t->selected, 0, t->nline); break; } if(cp->args[1][0]<'0' && '9'<cp->args[1][0]) goto badselect; texttogglei(t, cp->iargs[1]); break; } if(cp->iargs[1]<0 || cp->iargs[1]>=t->nline) ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str); if(t->selected[cp->iargs[1]] != (cp->iargs[2]!=0)) texttogglei(t, cp->iargs[1]); break; case ESelectcolor: _ctlargcount(t, cp, 2); _setctlimage(t, &t->selectcolor, cp->args[1]); break; case ESelectmode: _ctlargcount(t, cp, 2); if(strcmp(cp->args[1], "single") == 0) t->selectmode = Selsingle; else if(strncmp(cp->args[1], "multi", 5) == 0) t->selectmode = Selmulti; break; case EShow: _ctlargcount(t, cp, 1); textshow(t); break; case ESize: if (cp->nargs == 3) r.max = Pt(10000, 10000); else{ _ctlargcount(t, cp, 5); r.max.x = cp->iargs[3]; r.max.y = cp->iargs[4]; } r.min.x = cp->iargs[1]; r.min.y = cp->iargs[2]; if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y) ctlerror("%q: bad sizes: %s", t->name, cp->str); t->size.min = r.min; t->size.max = r.max; break; case ETextcolor: _ctlargcount(t, cp, 2); _setctlimage(t, &t->textcolor, cp->args[1]); break; case ETopline: _ctlargcount(t, cp, 2); i = cp->iargs[1]; if(i < 0) i = 0; if(i > t->nline) i = t->nline; if(t->topline != i){ t->topline = i; textshow(t); } break; case EValue: /* set contents to single line */ /* free existing text and fall through to add */ for(i=0; i<t->nline; i++){ free(t->line[i]); t->line[i] = nil; } t->nline = 0; t->topline = 0; /* fall through */ case EAccumulate: case EAdd: switch (cp->nargs) { default: ctlerror("%q: wrong argument count in '%s'", t->name, cp->str); case 2: n = t->nline; break; case 3: n = cp->iargs[1]; if(n<0 || n>t->nline) ctlerror("%q: line number out of range: %s", t->name, cp->str); break; } rp = _ctlrunestr(cp->args[cp->nargs-1]); t->line = ctlrealloc(t->line, (t->nline+1)*sizeof(Rune*)); memmove(t->line+n+1, t->line+n, (t->nline-n)*sizeof(Rune*)); t->line[n] = rp; t->selected = ctlrealloc(t->selected, t->nline+1); memmove(t->selected+n+1, t->selected+n, t->nline-n); t->selected[n] = (t->selectmode==Selmulti && cmd!=EAccumulate); t->nline++; if(t->scroll) { if(n > t->topline + (t->nvis - 1)){ t->topline = n - (t->nvis - 1); if(t->topline < 0) t->topline = 0; } if(n < t->topline) t->topline = n; } if(cmd != EAccumulate) if(t->scroll || t->nline<=t->topline+t->nvis) textshow(t); break; }} static voidtexttogglei(Text *t, int i){ int prev; if(t->selectmode == Selsingle){ /* clear the others */ prev = t->selected[i]; memset(t->selected, 0, t->nline); t->selected[i] = prev; } t->selected[i] ^= 1; textshow(t);} static inttexttoggle(Text *t, Point p){ Rectangle r; int i; r = t->rect; if(t->border > 0) r = insetrect(r, t->border); if(!ptinrect(p, r)) return -1; i = (p.y-r.min.y)/t->font->font->height; i += t->topline; if(i >= t->nline) return -1; texttogglei(t, i); return i;} Control*createtext(Controlset *cs, char *name){ Text *t; t = (Text*)_createctl(cs, "text", sizeof(Text), name); t->line = ctlmalloc(sizeof(Rune*)); t->selected = ctlmalloc(1); t->nline = 0; t->image = _getctlimage("white"); t->textcolor = _getctlimage("black"); t->bordercolor = _getctlimage("black"); t->selectcolor = _getctlimage("yellow"); t->font = _getctlfont("font"); t->selectmode = Selsingle; t->lastbut = 0; t->mouse = textmouse; t->ctl = textctl; t->exit = textfree; return (Control *)t;}
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include <control.h>
typedef struct Text Text;
struct Text
{
Control;
int border;
int topline;
int scroll;
int nvis;
int lastbut;
CFont *font;
CImage *image;
CImage *textcolor;
CImage *bordercolor;
CImage *selectcolor;
Rune **line;
int selectmode;
uchar *selected;
int nline;
int align;
};
enum
{
Selsingle,
Selmulti,
};
enum{
EAccumulate,
EAdd,
EAlign,
EBorder,
EBordercolor,
EClear,
EDelete,
EFocus,
EFont,
EHide,
EImage,
ERect,
EReplace,
EReveal,
EScroll,
ESelect,
ESelectcolor,
ESelectmode,
EShow,
ESize,
ETextcolor,
ETopline,
EValue,
};
static char *cmds[] = {
[EAccumulate] = "accumulate",
[EAdd] = "add",
[EAlign] = "align",
[EBorder] = "border",
[EBordercolor] = "bordercolor",
[EClear] = "clear",
[EDelete] = "delete",
[EFocus] = "focus",
[EFont] = "font",
[EHide] = "hide",
[EImage] = "image",
[ERect] = "rect",
[EReplace] = "replace",
[EReveal] = "reveal",
[EScroll] = "scroll",
[ESelect] = "select",
[ESelectcolor] = "selectcolor",
[ESelectmode] = "selectmode",
[EShow] = "show",
[ESize] = "size",
[ETextcolor] = "textcolor",
[ETopline] = "topline",
[EValue] = "value",
nil
};
static void textshow(Text*);
static void texttogglei(Text*, int);
static int texttoggle(Text*, Point);
static void
textmouse(Control *c, Mouse *m)
{
Text *t;
int sel;
t = (Text*)c;
if(t->lastbut != (m->buttons&7)){
if(m->buttons & 7){
sel = texttoggle(t, m->xy);
if(sel >= 0)
chanprint(t->event, "%q: select %d %d",
t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
}
t->lastbut = m->buttons & 7;
}
}
static void
textfree(Control *c)
{
int i;
Text *t;
t = (Text*)c;
_putctlfont(t->font);
_putctlimage(t->image);
_putctlimage(t->textcolor);
_putctlimage(t->bordercolor);
_putctlimage(t->selectcolor);
for(i=0; i<t->nline; i++)
free(t->line[i]);
free(t->line);
free(t->selected);
}
static void
textshow(Text *t)
{
Rectangle r, tr;
Point p;
int i, ntext;
Font *f;
Rune *text;
if (t->hidden)
return;
r = t->rect;
f = t->font->font;
draw(t->screen, r, t->image->image, nil, t->image->image->r.min);
if(t->border > 0){
border(t->screen, r, t->border, t->bordercolor->image, t->bordercolor->image->r.min);
r = insetrect(r, t->border);
}
tr = r;
t->nvis = Dy(r)/f->height;
for(i=t->topline; i<t->nline && i<t->topline+t->nvis; i++){
text = t->line[i];
ntext = runestrlen(text);
r.max.y = r.min.y+f->height;
if(t->selected[i])
draw(t->screen, r, t->selectcolor->image, nil, ZP);
p = _ctlalignpoint(r,
runestringnwidth(f, text, ntext),
f->height, t->align);
_string(t->screen, p, t->textcolor->image,
ZP, f, nil, text, ntext, tr,
nil, ZP);
r.min.y += f->height;
}
flushimage(display, 1);
}
static void
textctl(Control *c, CParse *cp)
{
int cmd, i, n;
Rectangle r;
Text *t;
Rune *rp;
t = (Text*)c;
cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
switch(cmd){
default:
ctlerror("%q: unrecognized message '%s'", t->name, cp->str);
break;
case EAlign:
_ctlargcount(t, cp, 2);
t->align = _ctlalignment(cp->args[1]);
break;
case EBorder:
_ctlargcount(t, cp, 2);
if(cp->iargs[1] < 0)
ctlerror("%q: bad border: %c", t->name, cp->str);
t->border = cp->iargs[1];
break;
case EBordercolor:
_ctlargcount(t, cp, 2);
_setctlimage(t, &t->bordercolor, cp->args[1]);
break;
case EClear:
_ctlargcount(t, cp, 1);
for(i=0; i<t->nline; i++)
free(t->line[i]);
free(t->line);
free(t->selected);
t->line = ctlmalloc(sizeof(Rune*));
t->selected = ctlmalloc(1);
t->nline = 0;
textshow(t);
break;
case EDelete:
_ctlargcount(t, cp, 2);
i = cp->iargs[1];
if(i<0 || i>=t->nline)
ctlerror("%q: line number out of range: %s", t->name, cp->str);
free(t->line[i]);
memmove(t->line+i, t->line+i+1, (t->nline-(i+1))*sizeof(Rune*));
memmove(t->selected+i, t->selected+i+1, t->nline-(i+1));
t->nline--;
textshow(t);
break;
case EFocus:
break;
case EFont:
_ctlargcount(t, cp, 2);
_setctlfont(t, &t->font, cp->args[1]);
break;
case EHide:
_ctlargcount(t, cp, 1);
t->hidden = 1;
break;
case EImage:
_ctlargcount(t, cp, 2);
_setctlimage(t, &t->image, cp->args[1]);
break;
case ERect:
_ctlargcount(t, cp, 5);
r.min.x = cp->iargs[1];
r.min.y = cp->iargs[2];
r.max.x = cp->iargs[3];
r.max.y = cp->iargs[4];
if(Dx(r)<=0 || Dy(r)<=0)
ctlerror("%q: bad rectangle: %s", t->name, cp->str);
t->rect = r;
t->nvis = (Dy(r)-2*t->border)/t->font->font->height;
break;
case EReplace:
_ctlargcount(t, cp, 3);
i = cp->iargs[1];
if(i<0 || i>=t->nline)
ctlerror("%q: line number out of range: %s", t->name, cp->str);
free(t->line[i]);
t->line[i] = _ctlrunestr(cp->args[2]);
textshow(t);
break;
case EReveal:
_ctlargcount(t, cp, 1);
t->hidden = 0;
textshow(t);
break;
case EScroll:
_ctlargcount(t, cp, 2);
t->scroll = cp->iargs[1];
break;
case ESelect:
if(cp->nargs!=2 && cp->nargs!=3)
badselect:
ctlerror("%q: bad select message: %s", t->name, cp->str);
if(cp->nargs == 2){
if(strcmp(cp->args[1], "all") == 0){
memset(t->selected, 1, t->nline);
break;
}
if(strcmp(cp->args[1], "none") == 0){
memset(t->selected, 0, t->nline);
break;
}
if(cp->args[1][0]<'0' && '9'<cp->args[1][0])
goto badselect;
texttogglei(t, cp->iargs[1]);
break;
}
if(cp->iargs[1]<0 || cp->iargs[1]>=t->nline)
ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
if(t->selected[cp->iargs[1]] != (cp->iargs[2]!=0))
texttogglei(t, cp->iargs[1]);
break;
case ESelectcolor:
_ctlargcount(t, cp, 2);
_setctlimage(t, &t->selectcolor, cp->args[1]);
break;
case ESelectmode:
_ctlargcount(t, cp, 2);
if(strcmp(cp->args[1], "single") == 0)
t->selectmode = Selsingle;
else if(strncmp(cp->args[1], "multi", 5) == 0)
t->selectmode = Selmulti;
break;
case EShow:
_ctlargcount(t, cp, 1);
textshow(t);
break;
case ESize:
if (cp->nargs == 3)
r.max = Pt(10000, 10000);
else{
_ctlargcount(t, cp, 5);
r.max.x = cp->iargs[3];
r.max.y = cp->iargs[4];
}
r.min.x = cp->iargs[1];
r.min.y = cp->iargs[2];
if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
ctlerror("%q: bad sizes: %s", t->name, cp->str);
t->size.min = r.min;
t->size.max = r.max;
break;
case ETextcolor:
_ctlargcount(t, cp, 2);
_setctlimage(t, &t->textcolor, cp->args[1]);
break;
case ETopline:
_ctlargcount(t, cp, 2);
i = cp->iargs[1];
if(i < 0)
i = 0;
if(i > t->nline)
i = t->nline;
if(t->topline != i){
t->topline = i;
textshow(t);
}
break;
case EValue:
/* set contents to single line */
/* free existing text and fall through to add */
for(i=0; i<t->nline; i++){
free(t->line[i]);
t->line[i] = nil;
}
t->nline = 0;
t->topline = 0;
/* fall through */
case EAccumulate:
case EAdd:
switch (cp->nargs) {
default:
ctlerror("%q: wrong argument count in '%s'", t->name, cp->str);
case 2:
n = t->nline;
break;
case 3:
n = cp->iargs[1];
if(n<0 || n>t->nline)
ctlerror("%q: line number out of range: %s", t->name, cp->str);
break;
}
rp = _ctlrunestr(cp->args[cp->nargs-1]);
t->line = ctlrealloc(t->line, (t->nline+1)*sizeof(Rune*));
memmove(t->line+n+1, t->line+n, (t->nline-n)*sizeof(Rune*));
t->line[n] = rp;
t->selected = ctlrealloc(t->selected, t->nline+1);
memmove(t->selected+n+1, t->selected+n, t->nline-n);
t->selected[n] = (t->selectmode==Selmulti && cmd!=EAccumulate);
t->nline++;
if(t->scroll) {
if(n > t->topline + (t->nvis - 1)){
t->topline = n - (t->nvis - 1);
if(t->topline < 0)
t->topline = 0;
}
if(n < t->topline)
t->topline = n;
}
if(cmd != EAccumulate)
if(t->scroll || t->nline<=t->topline+t->nvis)
textshow(t);
break;
}
}
static void
texttogglei(Text *t, int i)
{
int prev;
if(t->selectmode == Selsingle){
/* clear the others */
prev = t->selected[i];
memset(t->selected, 0, t->nline);
t->selected[i] = prev;
}
t->selected[i] ^= 1;
textshow(t);
}
static int
texttoggle(Text *t, Point p)
{
Rectangle r;
int i;
r = t->rect;
if(t->border > 0)
r = insetrect(r, t->border);
if(!ptinrect(p, r))
return -1;
i = (p.y-r.min.y)/t->font->font->height;
i += t->topline;
if(i >= t->nline)
return -1;
texttogglei(t, i);
return i;
}
Control*
createtext(Controlset *cs, char *name)
{
Text *t;
t = (Text*)_createctl(cs, "text", sizeof(Text), name);
t->line = ctlmalloc(sizeof(Rune*));
t->selected = ctlmalloc(1);
t->nline = 0;
t->image = _getctlimage("white");
t->textcolor = _getctlimage("black");
t->bordercolor = _getctlimage("black");
t->selectcolor = _getctlimage("yellow");
t->font = _getctlfont("font");
t->selectmode = Selsingle;
t->lastbut = 0;
t->mouse = textmouse;
t->ctl = textctl;
t->exit = textfree;
return (Control *)t;
}