#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; CFont *font; CImage *image; CImage *textcolor; CImage *bordercolor; CImage *selectcolor; Rune **line; int selectmode; uchar *selected; int nline; int align;}; enum{ Selsingle, Selmulti,}; enum{ EAdd, EAlign, EBorder, EBordercolor, EClear, EDelete, EFocus, EFont, EImage, ERect, EReplace, EScroll, ESelect, ESelectcolor, ESelectmode, EShow, ETextcolor, ETopline, EValue,}; static char *cmds[] = { [EAdd] = "add", [EAlign] = "align", [EBorder] = "border", [EBordercolor] = "bordercolor", [EClear] = "clear", [EDelete] = "delete", [EFocus] = "focus", [EFont] = "font", [EImage] = "image", [ERect] = "rect", [EReplace] = "replace", [EScroll] = "scroll", [ESelect] = "select", [ESelectcolor] = "selectcolor", [ESelectmode] = "selectmode", [EShow] = "show", [ETextcolor] = "textcolor", [ETopline] = "topline", [EValue] = "value", nil}; static void textctl(Control*, char*);static void textshow(Text*);static void texttogglei(Text*, int);static void textfree(Text*);static int texttoggle(Text*, Point); static voidtextthread(void *v){ char buf[32]; Text *t; int lastbut, sel; t = v; snprint(buf, sizeof buf, "text-%s-0x%p", t->name, t); threadsetname(buf); 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; lastbut = 0; for(;;){ switch(alt(t->alts)){ default: ctlerror("%q: unknown message", t->name); case AKey: /* ignore keystrokes */ break; case AMouse: if(lastbut != (t->m.buttons&1)){ if(t->m.buttons & 1){ sel = texttoggle(t, t->m.xy); if(sel >= 0) printctl(t->event, "%q: select %d %d", t->name, sel, t->selected[sel]); } lastbut = t->m.buttons & 1; } break; case ACtl: _ctlcontrol(t, t->str, textctl); free(t->str); break; case AWire: _ctlrewire(t); break; case AExit: textfree(t); sendul(t->exit, 0); return; } }} Control*createtext(Controlset *cs, char *name){ return _createctl(cs, "text", sizeof(Text), name, textthread, 0);} static voidtextfree(Text *t){ int i; _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; 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)/font->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, char *str){ int cmd, i, n; CParse cp; Rectangle r; Text *t; Rune *rp; t = (Text*)c; cmd = _ctlparse(&cp, str, 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 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)/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 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 ETextcolor: _ctlargcount(t, &cp, 2); _setctlimage(t, &t->textcolor, cp.args[1]); break; 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 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); t->nline++; if(t->scroll) { if (n > t->topline + t->nvis) t->topline = n - t->nvis; if (n < t->topline) t->topline = n; } 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)/font->height; i += t->topline; if(i >= t->nline) return -1; texttogglei(t, i); return i;}
#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;
CFont *font;
CImage *image;
CImage *textcolor;
CImage *bordercolor;
CImage *selectcolor;
Rune **line;
int selectmode;
uchar *selected;
int nline;
int align;
};
enum
{
Selsingle,
Selmulti,
};
enum{
EAdd,
EAlign,
EBorder,
EBordercolor,
EClear,
EDelete,
EFocus,
EFont,
EImage,
ERect,
EReplace,
EScroll,
ESelect,
ESelectcolor,
ESelectmode,
EShow,
ETextcolor,
ETopline,
EValue,
};
static char *cmds[] = {
[EAdd] = "add",
[EAlign] = "align",
[EBorder] = "border",
[EBordercolor] = "bordercolor",
[EClear] = "clear",
[EDelete] = "delete",
[EFocus] = "focus",
[EFont] = "font",
[EImage] = "image",
[ERect] = "rect",
[EReplace] = "replace",
[EScroll] = "scroll",
[ESelect] = "select",
[ESelectcolor] = "selectcolor",
[ESelectmode] = "selectmode",
[EShow] = "show",
[ETextcolor] = "textcolor",
[ETopline] = "topline",
[EValue] = "value",
nil
};
static void textctl(Control*, char*);
static void textshow(Text*);
static void texttogglei(Text*, int);
static void textfree(Text*);
static int texttoggle(Text*, Point);
static void
textthread(void *v)
{
char buf[32];
Text *t;
int lastbut, sel;
t = v;
snprint(buf, sizeof buf, "text-%s-0x%p", t->name, t);
threadsetname(buf);
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;
lastbut = 0;
for(;;){
switch(alt(t->alts)){
default:
ctlerror("%q: unknown message", t->name);
case AKey:
/* ignore keystrokes */
break;
case AMouse:
if(lastbut != (t->m.buttons&1)){
if(t->m.buttons & 1){
sel = texttoggle(t, t->m.xy);
if(sel >= 0)
printctl(t->event, "%q: select %d %d",
t->name, sel, t->selected[sel]);
}
lastbut = t->m.buttons & 1;
}
break;
case ACtl:
_ctlcontrol(t, t->str, textctl);
free(t->str);
break;
case AWire:
_ctlrewire(t);
break;
case AExit:
textfree(t);
sendul(t->exit, 0);
return;
}
}
}
Control*
createtext(Controlset *cs, char *name)
{
return _createctl(cs, "text", sizeof(Text), name, textthread, 0);
}
static void
textfree(Text *t)
{
int i;
_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;
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)/font->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, char *str)
{
int cmd, i, n;
CParse cp;
Rectangle r;
Text *t;
Rune *rp;
t = (Text*)c;
cmd = _ctlparse(&cp, str, 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 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)/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 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 ETextcolor:
_ctlargcount(t, &cp, 2);
_setctlimage(t, &t->textcolor, cp.args[1]);
break;
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 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);
t->nline++;
if(t->scroll) {
if (n > t->topline + t->nvis)
t->topline = n - t->nvis;
if (n < t->topline)
t->topline = n;
}
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)/font->height;
i += t->topline;
if(i >= t->nline)
return -1;
texttogglei(t, i);
return i;
}