diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/libcontrol |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/libcontrol')
-rwxr-xr-x | sys/src/libcontrol/box.c | 181 | ||||
-rwxr-xr-x | sys/src/libcontrol/button.c | 264 | ||||
-rwxr-xr-x | sys/src/libcontrol/cache.c | 168 | ||||
-rwxr-xr-x | sys/src/libcontrol/control.c | 809 | ||||
-rwxr-xr-x | sys/src/libcontrol/entry.c | 354 | ||||
-rwxr-xr-x | sys/src/libcontrol/group.c | 737 | ||||
-rwxr-xr-x | sys/src/libcontrol/group.h | 18 | ||||
-rwxr-xr-x | sys/src/libcontrol/keyboard.c | 536 | ||||
-rwxr-xr-x | sys/src/libcontrol/label.c | 204 | ||||
-rwxr-xr-x | sys/src/libcontrol/menu.c | 366 | ||||
-rwxr-xr-x | sys/src/libcontrol/mkfile | 37 | ||||
-rwxr-xr-x | sys/src/libcontrol/radiobutton.c | 192 | ||||
-rwxr-xr-x | sys/src/libcontrol/scribble.c | 325 | ||||
-rwxr-xr-x | sys/src/libcontrol/slider.c | 326 | ||||
-rwxr-xr-x | sys/src/libcontrol/tabs.c | 256 | ||||
-rwxr-xr-x | sys/src/libcontrol/text.c | 542 | ||||
-rwxr-xr-x | sys/src/libcontrol/textbutton.c | 344 | ||||
-rwxr-xr-x | sys/src/libcontrol/textbutton3.c | 393 |
18 files changed, 6052 insertions, 0 deletions
diff --git a/sys/src/libcontrol/box.c b/sys/src/libcontrol/box.c new file mode 100755 index 000000000..64a16d877 --- /dev/null +++ b/sys/src/libcontrol/box.c @@ -0,0 +1,181 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Box Box; + +struct Box +{ + Control; + int border; + CImage *bordercolor; + CImage *image; + int align; +}; + +enum{ + EAlign, + EBorder, + EBordercolor, + EFocus, + EHide, + EImage, + ERect, + EReveal, + EShow, + ESize, +}; + +static char *cmds[] = { + [EAlign] = "align", + [EBorder] = "border", + [EBordercolor] ="bordercolor", + [EFocus] = "focus", + [EHide] = "hide", + [EImage] = "image", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + nil +}; + +static void +boxkey(Control *c, Rune *rp) +{ + Box *b; + + b = (Box*)c; + chanprint(b->event, "%q: key 0x%x", b->name, rp[0]); +} + +static void +boxmouse(Control *c, Mouse *m) +{ + Box *b; + + b = (Box*)c; + if (ptinrect(m->xy,b->rect)) + chanprint(b->event, "%q: mouse %P %d %ld", b->name, + m->xy, m->buttons, m->msec); +} + +static void +boxfree(Control *c) +{ + _putctlimage(((Box*)c)->image); +} + +static void +boxshow(Box *b) +{ + Image *i; + Rectangle r; + + if(b->hidden) + return; + if(b->border > 0){ + border(b->screen, b->rect, b->border, b->bordercolor->image, ZP); + r = insetrect(b->rect, b->border); + }else + r = b->rect; + i = b->image->image; + /* BUG: ALIGNMENT AND CLIPPING */ + draw(b->screen, r, i, nil, ZP); +} + +static void +boxctl(Control *c, CParse *cp) +{ + int cmd; + Rectangle r; + Box *b; + + b = (Box*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + ctlerror("%q: unrecognized message '%s'", b->name, cp->str); + break; + case EAlign: + _ctlargcount(b, cp, 2); + b->align = _ctlalignment(cp->args[1]); + break; + case EBorder: + _ctlargcount(b, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: bad border: %c", b->name, cp->str); + b->border = cp->iargs[1]; + break; + case EBordercolor: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->bordercolor, cp->args[1]); + break; + case EFocus: + _ctlargcount(b, cp, 2); + chanprint(b->event, "%q: focus %s", b->name, cp->args[1]); + break; + case EHide: + _ctlargcount(b, cp, 1); + b->hidden = 1; + break; + case EImage: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->image, cp->args[1]); + break; + case ERect: + _ctlargcount(b, 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", b->name, cp->str); + b->rect = r; + break; + case EReveal: + _ctlargcount(b, cp, 1); + b->hidden = 0; + boxshow(b); + break; + case EShow: + _ctlargcount(b, cp, 1); + boxshow(b); + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(b, 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", b->name, cp->str); + b->size.min = r.min; + b->size.max = r.max; + break; + } +} + +Control* +createbox(Controlset *cs, char *name) +{ + Box *b; + + b = (Box *)_createctl(cs, "box", sizeof(Box), name); + b->image = _getctlimage("white"); + b->bordercolor = _getctlimage("black"); + b->align = Aupperleft; + b->key = boxkey; + b->mouse = boxmouse; + b->ctl = boxctl; + b->exit = boxfree; + return (Control *)b; +} diff --git a/sys/src/libcontrol/button.c b/sys/src/libcontrol/button.c new file mode 100755 index 000000000..1d6b23d73 --- /dev/null +++ b/sys/src/libcontrol/button.c @@ -0,0 +1,264 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Button Button; + +struct Button +{ + Control; + CImage *image; + CImage *mask; + CImage *light; + CImage *pale; + CImage *bordercolor; + int pressed; + int lastbut; + int lastshow; + int border; + int align; + int off; + int prepress; +}; + +enum{ + EAlign, + EBorder, + EBordercolor, + EFocus, + EFormat, + EHide, + EImage, + ELight, + EMask, + EPale, + ERect, + EReveal, + EShow, + ESize, + EValue, +}; + +static char *cmds[] = { + [EAlign] = "align", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EFocus] = "focus", + [EFormat] = "format", + [EHide] = "hide", + [EImage] = "image", + [ELight] = "light", + [EMask] = "mask", + [EPale] = "pale", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + [EValue] = "value", + nil +}; + +static void +buttonfree(Control *c) +{ + Button *b; + + b = (Button *)c; + _putctlimage(b->image); + _putctlimage(b->mask); + _putctlimage(b->light); + _putctlimage(b->pale); + _putctlimage(b->bordercolor); +} + +static void +buttonshow(Button *b) +{ + Rectangle r; + + if (b->hidden) + return; + r = b->rect; + if(b->border > 0){ + border(b->screen, r, b->border, b->bordercolor->image, ZP); + r = insetrect(b->rect, b->border); + } + draw(b->screen, r, b->image->image, nil, b->image->image->r.min); + if(b->off) + draw(b->screen, r, b->pale->image, b->mask->image, b->mask->image->r.min); + else if(b->pressed) + draw(b->screen, r, b->light->image, b->mask->image, b->mask->image->r.min); + b->lastshow = b->pressed; + flushimage(display, 1); +} + +static void +buttonmouse(Control *c, Mouse *m) +{ + Button *b; + + b = (Button*)c; + + if(m->buttons&7) { + if (ptinrect(m->xy,b->rect)) { + if (b->off) { + b->off = 0; + buttonshow(b); + } + } else { + if (!b->off) { + b->off = 1; + buttonshow(b); + } + } + } + if((m->buttons&7) != b->lastbut){ + if(m->buttons & 7){ + b->prepress = b->pressed; + if (b->pressed) + b->pressed = 0; + else + b->pressed = m->buttons & 7; + buttonshow(b); + }else /* generate event on button up */ + if (ptinrect(m->xy,b->rect)) + chanprint(b->event, b->format, b->name, b->pressed); + else { + b->off = 0; + b->pressed = b->prepress; + buttonshow(b); + } + } + b->lastbut = m->buttons & 7; +} + +static void +buttonctl(Control *c, CParse *cp) +{ + int cmd; + Rectangle r; + Button *b; + + b = (Button*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + ctlerror("%q: unrecognized message '%s'", b->name, cp->str); + break; + case EAlign: + _ctlargcount(b, cp, 2); + b->align = _ctlalignment(cp->args[1]); + b->lastshow = -1; /* force redraw */ + break; + case EBorder: + _ctlargcount(b, cp, 2); + b->border = cp->iargs[1]; + b->lastshow = -1; /* force redraw */ + break; + case EBordercolor: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->bordercolor, cp->args[1]); + b->lastshow = -1; /* force redraw */ + break; + case EFocus: + /* ignore focus change */ + break; + case EFormat: + _ctlargcount(b, cp, 2); + b->format = ctlstrdup(cp->args[1]); + break; + case EHide: + _ctlargcount(b, cp, 1); + b->hidden = 1; + break; + case EImage: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->image, cp->args[1]); + b->lastshow = -1; /* force redraw */ + break; + case ELight: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->light, cp->args[1]); + b->lastshow = -1; /* force redraw */ + break; + case EMask: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->mask, cp->args[1]); + b->lastshow = -1; /* force redraw */ + break; + case EPale: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->pale, cp->args[1]); + b->lastshow = -1; /* force redraw */ + break; + case ERect: + _ctlargcount(b, 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", b->name, cp->str); + b->rect = r; + b->lastshow = -1; /* force redraw */ + break; + case EReveal: + _ctlargcount(b, cp, 1); + b->hidden = 0; + buttonshow(b); + break; + case EShow: + _ctlargcount(b, cp, 1); + buttonshow(b); + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(b, 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", b->name, cp->str); + b->size.min = r.min; + b->size.max = r.max; + break; + case EValue: + _ctlargcount(b, cp, 2); + if((cp->iargs[1]!=0) != b->pressed){ + b->pressed ^= 1; + buttonshow(b); + } + break; + } +} + +Control* +createbutton(Controlset *cs, char *name) +{ + Button *b; + b = (Button*)_createctl(cs, "button", sizeof(Button), name); + b->image = _getctlimage("white"); + b->mask = _getctlimage("opaque"); + b->light = _getctlimage("yellow"); + b->pale = _getctlimage("paleyellow"); + b->bordercolor = _getctlimage("black"); + b->format = ctlstrdup("%q: value %d"); + b->lastshow = -1; + b->border = 0; + b->align = Aupperleft; + b->ctl = buttonctl; + b->mouse = buttonmouse; + b->key = nil; + b->exit = buttonfree; + b->off = 0; + b->prepress = 0; + return (Control*)b; +} diff --git a/sys/src/libcontrol/cache.c b/sys/src/libcontrol/cache.c new file mode 100755 index 000000000..ab38d3084 --- /dev/null +++ b/sys/src/libcontrol/cache.c @@ -0,0 +1,168 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Cache Cache; + +struct Cache +{ + char *name; + CCache **cache; + int ncache; +}; + +static struct Cache imagecache = {"image"}; +static struct Cache fontcache = {"font"}; + +static CCache* +getcacheitem(Cache *c, char *name) +{ + int i; + + for(i=0; i<c->ncache; i++) + if(c->cache[i]!=nil && strcmp(c->cache[i]->name, name)==0){ + c->cache[i]->ref++; + return c->cache[i]; + } + return nil; +} + +static int +namecacheitem(Cache *c, void *image, char *name) +{ + int i, free; + CCache *cc; + + free = -1; + for(i=0; i<c->ncache; i++){ + if(c->cache[i] == nil){ + free = i; + continue; + } + if(strcmp(c->cache[i]->name, name) == 0){ + werrstr("%s name %q already in use", c->name, name); + return -1; + } + } + cc = ctlmalloc(sizeof(CCache)); + cc->image = image; + cc->name = ctlstrdup(name); + if(free >= 0){ + cc->index = free; + c->cache[free] = cc; + }else{ + cc->index = c->ncache; + c->cache = ctlrealloc(c->cache, (c->ncache+1)*sizeof(CCache*)); + c->cache[c->ncache++] = cc; + } + cc->ref = 1; + return 1; +} + +static int +freecacheitem(Cache *c, char *name) +{ + CCache *cc; + + cc = getcacheitem(c, name); + if(cc == nil){ + werrstr("%s name %q not in use", c->name, name); + return -1; + } + cc->ref--; /* getcacheitem increments ref */ + if(cc->ref-- == 1){ + /* client must free object itself */ + free(cc->name); + c->cache[cc->index] = nil; + free(cc); + } + return 0; +} + +static void +putcacheitem(CCache *cc) +{ + if(cc == nil) + return; + cc->ref--; +} + +static void +setcacheitemptr(Cache *c, Control *ctl, CCache **cp, char *s) +{ + CCache *ci; + + ci = getcacheitem(c, s); + if(ci == nil) + ctlerror("%q: %s name %q not defined", ctl->name, c->name, s); + putcacheitem(*cp); + *cp = ci; +} + +/* Images */ + +CImage* +_getctlimage(char *name) +{ + return getcacheitem(&imagecache, name); +} + +void +_putctlimage(CImage *c) +{ + putcacheitem(c); +} + +int +namectlimage(Image *image, char *name) +{ + return namecacheitem(&imagecache, image, name); +} + +int +freectlimage(char *name) +{ + return freecacheitem(&imagecache, name); +} + +void +_setctlimage(Control *c, CImage **cp, char *s) +{ + setcacheitemptr(&imagecache, c, cp, s); +} + +/* Fonts */ + +CFont* +_getctlfont(char *name) +{ + return getcacheitem(&fontcache, name); +} + +void +_putctlfont(CFont *c) +{ + putcacheitem(c); +} + +int +namectlfont(Font *font, char *name) +{ + return namecacheitem(&fontcache, font, name); +} + +int +freectlfont(char *name) +{ + return freecacheitem(&fontcache, name); +} + +void +_setctlfont(Control *c, CFont **cp, char *s) +{ + setcacheitemptr(&fontcache, c, cp, s); +} diff --git a/sys/src/libcontrol/control.c b/sys/src/libcontrol/control.c new file mode 100755 index 000000000..f30d6fe57 --- /dev/null +++ b/sys/src/libcontrol/control.c @@ -0,0 +1,809 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +static int debug = 0; + +enum /* alts */ +{ + AKey, + AMouse, + ACtl, + AExit, + NALT +}; + +static Controlset **controlset; +int ncontrolset; +int ctldeletequits; + +char *alignnames[Nalignments] = { + [Aupperleft] = "upperleft", + [Auppercenter] = "uppercenter", + [Aupperright] = "upperright", + [Acenterleft] = "centerleft", + [Acenter] = "center", + [Acenterright] = "centerright", + [Alowerleft] = "lowerleft", + [Alowercenter] = "lowercenter", + [Alowerright] = "lowerright", +}; + +char *ctltypenames[Ntypes] = { + [Ctlunknown] = "unknown", + [Ctlbox] = "box", + [Ctlbutton] = "button", + [Ctlentry] = "entry", + [Ctlkeyboard] = "keyboard", + [Ctllabel] = "label", + [Ctlmenu] = "menu", + [Ctlradio] = "radio", + [Ctlscribble] = "scribble", + [Ctlslider] = "slider", + [Ctltabs] = "tabs", + [Ctltext] = "text", + [Ctltextbutton] = "textbutton", + [Ctltextbutton3] = "textbutton3", + [Ctlgroup] = "group", // divider between controls and metacontrols + [Ctlboxbox] = "boxbox", + [Ctlcolumn] = "column", + [Ctlrow] = "row", + [Ctlstack] = "stack", + [Ctltab] = "tab", +}; + +static void _ctlcmd(Controlset*, char*); +static void _ctlcontrol(Controlset*, char*); + +static char* +_mkctlcmd(Control *c, char *fmt, va_list arg) +{ + char *name, *p, *both; + + name = quotestrdup(c->name); + if(name == nil) + ctlerror("quotestrdup in ctlprint failed"); + p = vsmprint(fmt, arg); + if(p == nil){ + free(name); + ctlerror("vsmprint1 in ctlprint failed"); + } + both = ctlmalloc(strlen(name)+strlen(p)+2); + strcpy(both, name); + strcat(both, " "); + strcat(both, p); + free(name); + free(p); + return both; +} + +int +ctlprint(Control *c, char *fmt, ...) +{ + int n; + char *p; + va_list arg; + + va_start(arg, fmt); + p = _mkctlcmd(c, fmt, arg); + va_end(arg); + n = sendp(c->controlset->ctl, p); + yield(); + return n; +} + +void +_ctlprint(Control *c, char *fmt, ...) +{ + char *p; + va_list arg; + + va_start(arg, fmt); + p = _mkctlcmd(c, fmt, arg); + va_end(arg); + _ctlcmd(c->controlset, p); + free(p); +} + +int +_ctllookup(char *s, char *tab[], int ntab) +{ + int i; + + for(i=0; i<ntab; i++) + if(tab[i] != nil && strcmp(s, tab[i]) == 0) + return i; + return -1; +} + +static Control* +_newcontrol(Controlset *cs, uint n, char *name, char *type) +{ + Control *c; + + for(c=cs->controls; c; c=c->next) + if(strcmp(c->name, name) == 0){ + werrstr("control %q already defined", name); + return nil; + } + c = ctlmalloc(n); + c->screen = cs->screen; + c->name = ctlstrdup(name); + c->type = _ctllookup(type, ctltypenames, Ntypes); + if (c->type < 0) + ctlerror("unknown type: %s", type); + c->event = chancreate(sizeof(char*), 64); + c->data = chancreate(sizeof(char*), 0); + c->size = Rect(1, 1, _Ctlmaxsize, _Ctlmaxsize); + c->hidden = 0; + c->ctl = nil; + c->mouse = nil; + c->key = nil; + c->exit = nil; + c->setsize = nil; + + c->controlset = cs; + c->next = cs->controls; + cs->controls = c; + return c; +} + +static void +controlsetthread(void *v) +{ + Controlset *cs; + Mouse mouse; + Control *f; + int prevbut, n, i; + Alt alts[NALT+1]; + char tmp[64], *str; + Rune buf[2][20], *rp; + + cs = v; + snprint(tmp, sizeof tmp, "controlsetthread 0x%p", cs); + threadsetname(tmp); + + alts[AKey].c = cs->kbdc; + alts[AKey].v = &rp; + alts[AKey].op = CHANRCV; + alts[AMouse].c = cs->mousec; + alts[AMouse].v = &mouse; + alts[AMouse].op = CHANRCV; + alts[ACtl].c = cs->ctl; + alts[ACtl].v = &str; + alts[ACtl].op = CHANRCV; + alts[AExit].c = cs->csexitc; + alts[AExit].v = nil; + alts[AExit].op = CHANRCV; + alts[NALT].op = CHANEND; + + cs->focus = nil; + prevbut=0; + n = 0; + for(;;){ + /* toggle so we can receive in one buffer while client processes the other */ + alts[AKey].v = buf[n]; + rp = buf[n]; + n = 1-n; + switch(alt(alts)){ + case AKey: + if(ctldeletequits && rp[0]=='\177') + ctlerror("delete"); + for(i=1; i<nelem(buf[0])-1; i++) + if(nbrecv(cs->kbdc, rp+i) <= 0) + break; + rp[i] = L'\0'; + if(cs->focus && cs->focus->key) + cs->focus->key(cs->focus, rp); + break; + case AMouse: + /* is this a focus change? */ + if(prevbut) /* don't change focus if button was down */ + goto Send; + if(cs->focus!=nil && cs->focus->hidden == 0 && ptinrect(mouse.xy, cs->focus->rect)) + goto Send; + if(cs->clicktotype == 0) + goto Change; + /* click to type: only change if button is down */ + if(mouse.buttons == 0) + goto Send; + Change: + /* change of focus */ + if(cs->focus != nil) + _ctlprint(cs->focus, "focus 0"); + cs->focus = nil; + for(f=cs->actives; f!=nil; f=f->nextactive) + if(f->hidden == 0 && f->mouse && ptinrect(mouse.xy, f->rect)){ + cs->focus = f; + _ctlprint(f, "focus 1"); + if (f->mouse) { + if (debug) fprint(2, "f->mouse %s\n", f->name); + f->mouse(f, &mouse); + } + break; + } + Send: + if(cs->focus && cs->focus->mouse) { + if (debug) fprint(2, "cs->focus->mouse %s\n", cs->focus->name); + cs->focus->mouse(cs->focus, &mouse); + } + prevbut=mouse.buttons; + break; + case ACtl: + _ctlcontrol(cs, str); + free(str); + break; + case AExit: + threadexits(nil); + } + } +} + +Control* +_createctl(Controlset *cs, char *type, uint size, char *name) +{ + Control *c; + + c = _newcontrol(cs, size, name, type); + if(c == nil) + ctlerror("can't create %s control %q: %r", type, name); + return c; +} + +void +closecontrol(Control *c) +{ + Control *prev, *p; + + if(c == nil) + return; + if (c == c->controlset->focus) + c->controlset->focus = nil; + if(c->exit) + c->exit(c); + + prev = nil; + for(p=c->controlset->controls; p; p=p->next){ + if(p == c) + break; + prev = p; + } + if(p == nil) + ctlerror("closecontrol: no such control %q %p\n", c->name, c); + if(prev == nil) + c->controlset->controls = c->next; + else + prev->next = c->next; + + /* is it active? if so, delete from active list */ + prev = nil; + for(p=c->controlset->actives; p; p=p->nextactive){ + if(p == c) + break; + prev = p; + } + if(p != nil){ + if(prev == nil) + c->controlset->actives = c->nextactive; + else + prev->nextactive = c->nextactive; + } + + if(!c->wevent) + chanfree(c->event); + if(!c->wdata) + chanfree(c->data); + free(c->name); + free(c->format); + free(c); +} + +Control* +controlcalled(char *name) +{ + Control *c; + int i; + + for(i=0; i<ncontrolset; i++) + for(c=controlset[i]->controls; c; c=c->next) + if(strcmp(c->name, name) == 0) + return c; + return nil; +} + +void +ctlerror(char *fmt, ...) +{ + va_list arg; + char buf[256]; + + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); + write(2, "\n", 1); + threadexitsall(buf); +} + +Rune* +_ctlrunestr(char *s) +{ + Rune *r, *ret; + + ret = r = ctlmalloc((utflen(s)+1)*sizeof(Rune)); + while(*s != '\0') + s += chartorune(r++, s); + *r = L'\0'; + return ret; +} + +char* +_ctlstrrune(Rune *r) +{ + char *s; + s = ctlmalloc(runestrlen(r)*UTFmax+1); + sprint(s, "%S", r); + return s; +} + +void* +ctlmalloc(uint n) +{ + void *p; + + p = mallocz(n, 1); + if(p == nil) + ctlerror("control allocation failed: %r"); + return p; +} + +void* +ctlrealloc(void *p, uint n) +{ + p = realloc(p, n); + if(p == nil) + ctlerror("control reallocation failed: %r"); + return p; +} + +char* +ctlstrdup(char *s) +{ + char *t; + + t = strdup(s); + if(t == nil) + ctlerror("control strdup(%q) failed: %r", s); + return t; +} + +static void +ctokenize(char *s, CParse *cp) +{ + snprint(cp->str, sizeof cp->str, "%s", s); + cp->args = cp->pargs; + cp->nargs = tokenize(s, cp->args, nelem(cp->pargs)); +} + +static int +ctlparse(CParse *cp, char *s, int hasreceiver) +{ + int i; + char *t; + + /* keep original string for good error messages */ + strncpy(cp->str, s, sizeof cp->str); + cp->str[sizeof cp->str - 1] = '\0'; + ctokenize(s, cp); + if(cp->nargs == 0) + return -1; + /* strip leading sender name if present */ + cp->sender = nil; + i = strlen(cp->args[0])-1; + if(cp->args[0][i] == ':'){ + cp->sender = cp->args[0]; + cp->sender[i] = '\0'; + cp->args++; + cp->nargs--; + } + if(hasreceiver){ + if(cp->nargs-- == 0) + return -1; + cp->receiver = *cp->args++; + }else + cp->receiver = nil; + for(i=0; i<cp->nargs; i++){ + t = cp->args[i]; + while(*t == '[') /* %R gives [0 0] [1 1]; atoi will stop at closing ] */ + t++; + cp->iargs[i] = atoi(t); + } + return cp->nargs; +} + +void +_ctlargcount(Control *c, CParse *cp, int n) +{ + if(cp->nargs != n) + ctlerror("%q: wrong argument count in '%s'", c->name, cp->str); +} + +static void +_ctlcmd(Controlset *cs, char*s) +{ + CParse cp; + char *rcvrs[32]; + int ircvrs[32], n, i, hit; + Control *c; + +// fprint(2, "_ctlcmd: %s\n", s); + cp.args = cp.pargs; + if (ctlparse(&cp, s, 1) < 0) + ctlerror("bad command string: %q", cp.str); + if (cp.nargs == 0 && strcmp(cp.receiver, "sync") == 0){ + chanprint(cs->data, "sync"); + return; + } + if (cp.nargs == 0) + ctlerror("no command in command string: %q", cp.str); + + n = tokenize(cp.receiver, rcvrs, nelem(rcvrs)); + + // lookup type names: a receiver can be a named type or a named control + for (i = 0; i < n; i++) + ircvrs[i] = _ctllookup(rcvrs[i], ctltypenames, Ntypes); + + for(c = cs->controls; c != nil; c = c->next){ + /* if a control matches on more than one receiver element, + * make sure it gets processed once; hence loop through controls + * in the outer loop + */ + hit = 0; + for (i = 0; i < n; i++) + if(strcmp(c->name, rcvrs[i]) == 0 || c->type == ircvrs[i]) + hit++; + if (hit && c->ctl) + c->ctl(c, &cp); + } +} + +static void +_ctlcontrol(Controlset *cs, char *s) +{ + char *lines[16]; + int i, n; + char *l; + +// fprint(2, "_ctlcontrol: %s\n", s); + n = gettokens(s, lines, nelem(lines), "\n"); + for(i=0; i<n; i++){ + l = lines[i]; + while(*l==' ' || *l=='\t') + l++; + if(*l != '\0') + _ctlcmd(cs, l); + } +} + +Rune* +_ctlgetsnarf(void) +{ + int i, n; + char *sn, buf[512]; + Rune *snarf; + + if(_ctlsnarffd < 0) + return nil; + sn = nil; + i = 0; + seek(_ctlsnarffd, 0, 0); + while((n = read(_ctlsnarffd, buf, sizeof buf)) > 0){ + sn = ctlrealloc(sn, i+n+1); + memmove(sn+i, buf, n); + i += n; + sn[i] = 0; + } + snarf = nil; + if(i > 0){ + snarf = _ctlrunestr(sn); + free(sn); + } + return snarf; +} + +void +_ctlputsnarf(Rune *snarf) +{ + int fd, i, n, nsnarf; + + if(_ctlsnarffd<0 || snarf[0]==0) + return; + fd = open("/dev/snarf", OWRITE); + if(fd < 0) + return; + nsnarf = runestrlen(snarf); + /* snarf buffer could be huge, so fprint will truncate; do it in blocks */ + for(i=0; i<nsnarf; i+=n){ + n = nsnarf-i; + if(n >= 256) + n = 256; + if(fprint(fd, "%.*S", n, snarf+i) < 0) + break; + } + close(fd); +} + +int +_ctlalignment(char *s) +{ + int i; + + i = _ctllookup(s, alignnames, Nalignments); + if (i < 0) + ctlerror("unknown alignment: %s", s); + return i; +} + +Point +_ctlalignpoint(Rectangle r, int dx, int dy, int align) +{ + Point p; + + p = r.min; /* in case of trouble */ + switch(align%3){ + case 0: /* left */ + p.x = r.min.x; + break; + case 1: /* center */ + p.x = r.min.x+(Dx(r)-dx)/2; + break; + case 2: /* right */ + p.x = r.max.x-dx; + break; + } + switch((align/3)%3){ + case 0: /* top */ + p.y = r.min.y; + break; + case 1: /* center */ + p.y = r.min.y+(Dy(r)-dy)/2; + break; + case 2: /* bottom */ + p.y = r.max.y - dy; + break; + } + return p; +} + +void +controlwire(Control *cfrom, char *name, Channel *chan) +{ + Channel **p; + + p = nil; + if(strcmp(name, "event") == 0){ + p = &cfrom->event; + cfrom->wevent = 1; + }else if(strcmp(name, "data") == 0){ + p = &cfrom->data; + cfrom->wdata = 1; + }else + ctlerror("%q: unknown controlwire channel %s", cfrom->name, name); + chanfree(*p); + *p = chan; +} + +void +_ctlfocus(Control *me, int set) +{ + Controlset *cs; + + cs = me->controlset; + if(set){ + if(cs->focus == me) + return; + if(cs->focus != nil) + _ctlprint(cs->focus, "focus 0"); + cs->focus = me; + }else{ + if(cs->focus != me) + return; + cs->focus = nil; + } +} + +static void +resizethread(void *v) +{ + Controlset *cs; + char buf[64]; + Alt alts[3]; + + cs = v; + snprint(buf, sizeof buf, "resizethread0x%p", cs); + threadsetname(buf); + + alts[0].c = cs->resizec; + alts[0].v = nil; + alts[0].op = CHANRCV; + alts[1].c = cs->resizeexitc; + alts[1].v = nil; + alts[1].op = CHANRCV; + alts[2].op = CHANEND; + + for(;;){ + switch(alt(alts)){ + case 0: + resizecontrolset(cs); + break; + case 1: + return; + } + } +} + +void +activate(Control *a) +{ + Control *c; + + for(c=a->controlset->actives; c; c=c->nextactive) + if(c == a) + ctlerror("%q already active\n", a->name); + + if (a->activate){ + a->activate(a, 1); + return; + } + /* prepend */ + a->nextactive = a->controlset->actives; + a->controlset->actives = a; +} + +void +deactivate(Control *a) +{ + Control *c, *prev; + + /* if group, first deactivate kids, then self */ + if (a->activate){ + a->activate(a, 0); + return; + } + prev = nil; + for(c=a->controlset->actives; c; c=c->nextactive){ + if(c == a){ + if(a->controlset->focus == a) + a->controlset->focus = nil; + if(prev != nil) + prev->nextactive = a->nextactive; + else + a->controlset->actives = a->nextactive; + return; + } + prev = c; + } + ctlerror("%q not active\n", a->name); +} + +static struct +{ + char *name; + ulong color; +}coltab[] = { + "red", DRed, + "green", DGreen, + "blue", DBlue, + "cyan", DCyan, + "magenta", DMagenta, + "yellow", DYellow, + "paleyellow", DPaleyellow, + "darkyellow", DDarkyellow, + "darkgreen", DDarkgreen, + "palegreen", DPalegreen, + "medgreen", DMedgreen, + "darkblue", DDarkblue, + "palebluegreen", DPalebluegreen, + "paleblue", DPaleblue, + "bluegreen", DBluegreen, + "greygreen", DGreygreen, + "palegreygreen", DPalegreygreen, + "yellowgreen", DYellowgreen, + "medblue", DMedblue, + "greyblue", DGreyblue, + "palegreyblue", DPalegreyblue, + "purpleblue", DPurpleblue, + nil, 0 +}; + +void +initcontrols(void) +{ + int i; + Image *im; + + quotefmtinstall(); + namectlimage(display->opaque, "opaque"); + namectlimage(display->transparent, "transparent"); + namectlimage(display->white, "white"); + namectlimage(display->black, "black"); + for(i=0; coltab[i].name!=nil; i++){ + im = allocimage(display, Rect(0,0,1,1), RGB24, 1, coltab[i].color); + namectlimage(im, coltab[i].name); + } + namectlfont(font, "font"); + _ctlsnarffd = open("/dev/snarf", OREAD); +} + +Controlset* +newcontrolset(Image *im, Channel *kbdc, Channel *mousec, Channel *resizec) +{ + Controlset *cs; + + if(im == nil) + im = screen; + if((mousec==nil && resizec!=nil) || (mousec!=nil && resizec==nil)) + ctlerror("must specify either or both of mouse and resize channels"); + + cs = ctlmalloc(sizeof(Controlset)); + cs->screen = im; + + if(kbdc == nil){ + cs->keyboardctl = initkeyboard(nil); + if(cs->keyboardctl == nil) + ctlerror("can't initialize keyboard: %r"); + kbdc = cs->keyboardctl->c; + } + cs ->kbdc = kbdc; + + if(mousec == nil){ + cs->mousectl = initmouse(nil, im); + if(cs->mousectl == nil) + ctlerror("can't initialize mouse: %r"); + mousec = cs->mousectl->c; + resizec = cs->mousectl->resizec; + } + cs->mousec = mousec; + cs->resizec = resizec; + cs->ctl = chancreate(sizeof(char*), 64); /* buffer to prevent deadlock */ + cs->data = chancreate(sizeof(char*), 0); + cs->resizeexitc = chancreate(sizeof(int), 0); + cs->csexitc = chancreate(sizeof(int), 0); + + threadcreate(resizethread, cs, 32*1024); + threadcreate(controlsetthread, cs, 32*1024); + + controlset = ctlrealloc(controlset, (ncontrolset+1)*sizeof(Controlset*)); + controlset[ncontrolset++] = cs; + return cs; +} + +void +closecontrolset(Controlset *cs) +{ + int i; + + sendul(cs->resizeexitc, 0); + chanfree(cs->resizeexitc); + sendul(cs->csexitc, 0); + chanfree(cs->csexitc); + chanfree(cs->ctl); + chanfree(cs->data); + + for(i=0; i<ncontrolset; i++) + if(cs == controlset[i]){ + memmove(controlset+i, controlset+i+1, (ncontrolset-(i+1))*sizeof(Controlset*)); + ncontrolset--; + goto Found; + } + + if(i == ncontrolset) + ctlerror("closecontrolset: control set not found"); + + Found: + while(cs->controls != nil) + closecontrol(cs->controls); +} diff --git a/sys/src/libcontrol/entry.c b/sys/src/libcontrol/entry.c new file mode 100755 index 000000000..0b786592d --- /dev/null +++ b/sys/src/libcontrol/entry.c @@ -0,0 +1,354 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Entry Entry; + +struct Entry +{ + Control; + int border; + CFont *font; + CImage *image; + CImage *textcolor; + CImage *bordercolor; + Rune *text; + int ntext; + int cursor; + int align; + int hasfocus; + int lastbut; +}; + +enum{ + EAlign, + EBorder, + EBordercolor, + EData, + EFocus, + EFont, + EFormat, + EHide, + EImage, + ERect, + EReveal, + EShow, + ESize, + ETextcolor, + EValue, +}; + +static char *cmds[] = { + [EAlign] = "align", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EData] = "data", + [EFocus] = "focus", + [EFont] = "font", + [EFormat] = "format", + [EHide] = "hide", + [EImage] = "image", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + [ETextcolor] = "textcolor", + [EValue] = "value", + nil +}; + +static void +entryfree(Control *c) +{ + Entry *e; + + e = (Entry *)c; + _putctlfont(e->font); + _putctlimage(e->image); + _putctlimage(e->textcolor); + _putctlimage(e->bordercolor); + free(e->text); +} + +static Point +entrypoint(Entry *e, int c) +{ + Point p; + Rectangle r; + + r = e->rect; + if(e->border > 0) + r = insetrect(r, e->border); + p = _ctlalignpoint(r, + runestringnwidth(e->font->font, e->text, e->ntext), + e->font->font->height, e->align); + if(c > e->ntext) + c = e->ntext; + p.x += runestringnwidth(e->font->font, e->text, c); + return p; +} + +static void +entryshow(Entry *e) +{ + Rectangle r, dr; + Point p; + + if (e->hidden) + return; + r = e->rect; + draw(e->screen, r, e->image->image, nil, e->image->image->r.min); + if(e->border > 0){ + border(e->screen, r, e->border, e->bordercolor->image, e->bordercolor->image->r.min); + dr = insetrect(r, e->border); + }else + dr = r; + p = entrypoint(e, 0); + _string(e->screen, p, e->textcolor->image, + ZP, e->font->font, nil, e->text, e->ntext, + dr, nil, ZP, SoverD); + if(e->hasfocus){ + p = entrypoint(e, e->cursor); + r.min = p; + r.max.x = p.x+1; + r.max.y = p.y+e->font->font->height; + if(rectclip(&r, dr)) + draw(e->screen, r, e->textcolor->image, nil, ZP); + } + flushimage(display, 1); +} + +static void +entrysetpoint(Entry *e, Point cp) +{ + Point p; + int i; + + if(!ptinrect(cp, insetrect(e->rect, e->border))) + return; + p = entrypoint(e, 0); + for(i=0; i<e->ntext; i++){ + p.x += runestringnwidth(e->font->font, e->text+i, 1); + if(p.x > cp.x) + break; + } + e->cursor = i; + entryshow(e); +} + +static void +entrymouse(Control *c, Mouse *m) +{ + Entry *e; + + e = (Entry*)c; + if(m->buttons==1 && e->lastbut==0) + entrysetpoint(e, m->xy); + e->lastbut = m->buttons; +} + +static void +entryctl(Control *c, CParse *cp) +{ + int cmd; + Rectangle r; + Entry *e; + Rune *rp; + + e = (Entry*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + ctlerror("%q: unrecognized message '%s'", e->name, cp->str); + break; + case EAlign: + _ctlargcount(e, cp, 2); + e->align = _ctlalignment(cp->args[1]); + break; + case EBorder: + _ctlargcount(e, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: bad border: %c", e->name, cp->str); + e->border = cp->iargs[1]; + break; + case EBordercolor: + _ctlargcount(e, cp, 2); + _setctlimage(e, &e->bordercolor, cp->args[1]); + break; + case EData: + _ctlargcount(e, cp, 1); + chanprint(e->data, "%S", e->text); + break; + case EFocus: + _ctlargcount(e, cp, 2); + e->hasfocus = cp->iargs[1]; + e->lastbut = 0; + entryshow(e); + break; + case EFont: + _ctlargcount(e, cp, 2); + _setctlfont(e, &e->font, cp->args[1]); + break; + case EFormat: + _ctlargcount(e, cp, 2); + e->format = ctlstrdup(cp->args[1]); + break; + case EHide: + _ctlargcount(e, cp, 1); + e->hidden = 1; + break; + case EImage: + _ctlargcount(e, cp, 2); + _setctlimage(e, &e->image, cp->args[1]); + break; + case ERect: + _ctlargcount(e, 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", e->name, cp->str); + e->rect = r; + break; + case EReveal: + _ctlargcount(e, cp, 1); + e->hidden = 0; + entryshow(e); + break; + case EShow: + _ctlargcount(e, cp, 1); + entryshow(e); + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(e, 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", e->name, cp->str); + e->size.min = r.min; + e->size.max = r.max; + break; + case ETextcolor: + _ctlargcount(e, cp, 2); + _setctlimage(e, &e->textcolor, cp->args[1]); + break; + case EValue: + _ctlargcount(e, cp, 2); + rp = _ctlrunestr(cp->args[1]); + if(runestrcmp(rp, e->text) != 0){ + free(e->text); + e->text = rp; + e->ntext = runestrlen(e->text); + e->cursor = e->ntext; + entryshow(e); + }else + free(rp); + break; + } +} + +static void +entrykey(Entry *e, Rune r) +{ + Rune *s; + int n; + char *p; + + switch(r){ + default: + e->text = ctlrealloc(e->text, (e->ntext+1+1)*sizeof(Rune)); + memmove(e->text+e->cursor+1, e->text+e->cursor, + (e->ntext+1-e->cursor)*sizeof(Rune)); + e->text[e->cursor++] = r; + e->ntext++; + break; + case L'\n': /* newline: return value */ + p = _ctlstrrune(e->text); + chanprint(e->event, e->format, e->name, p); + free(p); + return; + case L'\b': + if(e->cursor > 0){ + memmove(e->text+e->cursor-1, e->text+e->cursor, + (e->ntext+1-e->cursor)*sizeof(Rune)); + e->cursor--; + e->ntext--; + } + break; + case Kright: + if(e->cursor < e->ntext) + e->cursor++; + break; + case Kleft: + if(e->cursor > 0) + e->cursor--; + break; + case 0x01: /* control A: beginning of line */ + e->cursor = 0; + break; + case 0x05: /* control E: end of line */ + e->cursor = e->ntext; + break; + case 0x15: /* control U: kill line */ + e->cursor = 0; + e->ntext = 0; + break; + case 0x16: /* control V: paste (append snarf buffer) */ + s = _ctlgetsnarf(); + if(s != nil){ + n = runestrlen(s); + e->text = ctlrealloc(e->text, (e->ntext+n+1)*sizeof(Rune)); + memmove(e->text+e->cursor+n, e->text+e->cursor, + (e->ntext+1-e->cursor)*sizeof(Rune)); + memmove(e->text+e->cursor, s, n*sizeof(Rune)); + e->cursor += n; + e->ntext += n; + } + break; + } + e->text[e->ntext] = L'\0'; +} + +static void +entrykeys(Control *c, Rune *rp) +{ + Entry *e; + int i; + + e = (Entry *)c; + for(i=0; rp[i]!=L'\0'; i++) + entrykey(e, rp[i]); + entryshow(e); +} + +Control* +createentry(Controlset *cs, char *name) +{ + Entry *e; + + e = (Entry*) _createctl(cs, "entry", sizeof(Entry), name); + e->text = ctlmalloc(sizeof(Rune)); + e->ntext = 0; + e->image = _getctlimage("white"); + e->textcolor = _getctlimage("black"); + e->bordercolor = _getctlimage("black"); + e->font = _getctlfont("font"); + e->format = ctlstrdup("%q: value %q"); + e->border = 0; + e->ctl = entryctl; + e->mouse = entrymouse; + e->key = entrykeys; + e->exit = entryfree; + return (Control *)e; +} diff --git a/sys/src/libcontrol/group.c b/sys/src/libcontrol/group.c new file mode 100755 index 000000000..1d13f2f68 --- /dev/null +++ b/sys/src/libcontrol/group.c @@ -0,0 +1,737 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> +#include "group.h" + +static int debug = 0; +static int debugm = 0; +static int debugr = 0; + +enum{ + EAdd, + EBorder, + EBordercolor, + EFocus, + EHide, + EImage, + ERect, + ERemove, + EReveal, + ESeparation, + EShow, + ESize, +}; + +static char *cmds[] = { + [EAdd] = "add", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EFocus] = "focus", + [EHide] = "hide", + [EImage] = "image", + [ERect] = "rect", + [ERemove] = "remove", + [EReveal] = "reveal", + [ESeparation] = "separation", + [EShow] = "show", + [ESize] = "size", +}; + +static void boxboxresize(Group*, Rectangle); +static void columnresize(Group*, Rectangle); +static void groupctl(Control *c, CParse *cp); +static void groupfree(Control*); +static void groupmouse(Control *, Mouse *); +static void groupsize(Control *c); +static void removegroup(Group*, int); +static void rowresize(Group*, Rectangle); +static void stackresize(Group*, Rectangle); + +static void +groupinit(Group *g) +{ + g->bordercolor = _getctlimage("black"); + g->image = _getctlimage("white"); + g->border = 0; + g->mansize = 0; + g->separation = 0; + g->selected = -1; + g->lastkid = -1; + g->kids = nil; + g->separators = nil; + g->nkids = 0; + g->nseparators = 0; + g->ctl = groupctl; + g->mouse = groupmouse; + g->exit = groupfree; +} + +static void +groupctl(Control *c, CParse *cp) +{ + int cmd, i, n; + + Rectangle r; + Group *g; + + g = (Group*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + case EAdd: + for (i = 1; i < cp->nargs; i++){ + c = controlcalled(cp->args[i]); + if (c == nil) + ctlerror("%q: no such control: %s", g->name, cp->args[i]); + _ctladdgroup(g, c); + } + if (g->setsize) + g->setsize((Control*)g); + break; + case EBorder: + _ctlargcount(g, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: bad border: %c", g->name, cp->str); + g->border = cp->iargs[1]; + break; + case EBordercolor: + _ctlargcount(g, cp, 2); + _setctlimage(g, &g->bordercolor, cp->args[1]); + break; + case EFocus: + /* ignore focus change */ + break; + case EHide: + _ctlargcount(g, cp, 1); + for (i = 0; i < g->nkids; i++) + if (g->kids[i]->ctl) + _ctlprint(g->kids[i], "hide"); + g->hidden = 1; + break; + case EImage: + _ctlargcount(g, cp, 2); + _setctlimage(g, &g->image, cp->args[1]); + break; + case ERect: + _ctlargcount(g, 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", g->name, cp->str); + g->rect = r; + r = insetrect(r, g->border); + if (g->nkids == 0) + return; + switch(g->type){ + case Ctlboxbox: + boxboxresize(g, r); + break; + case Ctlcolumn: + columnresize(g, r); + break; + case Ctlrow: + rowresize(g, r); + break; + case Ctlstack: + stackresize(g, r); + break; + } + break; + case ERemove: + _ctlargcount(g, cp, 2); + for (n = 0; n < g->nkids; n++) + if (strcmp(cp->args[1], g->kids[n]->name) == 0) + break; + if (n == g->nkids) + ctlerror("%s: remove nonexistent control: %q", g->name, cp->args[1]); + removegroup(g, n); + if (g->setsize) + g->setsize((Control*)g); + break; + case EReveal: + g->hidden = 0; + if (debugr) fprint(2, "reveal %s\n", g->name); + if (g->type == Ctlstack){ + if (cp->nargs == 2){ + if (cp->iargs[1] < 0 || cp->iargs[1] >= g->nkids) + ctlerror("%s: control out of range: %q", g->name, cp->str); + g->selected = cp->iargs[1]; + }else + _ctlargcount(g, cp, 1); + for (i = 0; i < g->nkids; i++) + if (g->kids[i]->ctl){ + if (g->selected == i){ + if (debugr) fprint(2, "reveal %s: reveal kid %s\n", g->name, g->kids[i]->name); + _ctlprint(g->kids[i], "reveal"); + }else{ + if (debugr) fprint(2, "reveal %s: hide kid %s\n", g->name, g->kids[i]->name); + _ctlprint(g->kids[i], "hide"); + } + } + break; + } + _ctlargcount(g, cp, 1); + if (debug) fprint(2, "reveal %s: border %R/%d\n", g->name, g->rect, g->border); + border(g->screen, g->rect, g->border, g->bordercolor->image, g->bordercolor->image->r.min); + r = insetrect(g->rect, g->border); + if (debug) fprint(2, "reveal %s: draw %R\n", g->name, r); + draw(g->screen, r, g->image->image, nil, g->image->image->r.min); + for (i = 0; i < g->nkids; i++) + if (g->kids[i]->ctl) + _ctlprint(g->kids[i], "reveal"); + break; + case EShow: + _ctlargcount(g, cp, 1); + if (g->hidden) + break; + // pass it on to the kiddies + if (debug) fprint(2, "show %s: border %R/%d\n", g->name, g->rect, g->border); + border(g->screen, g->rect, g->border, g->bordercolor->image, g->bordercolor->image->r.min); + r = insetrect(g->rect, g->border); + if (debug) fprint(2, "show %s: draw %R\n", g->name, r); + draw(g->screen, r, g->image->image, nil, g->image->image->r.min); + for (i = 0; i < g->nkids; i++) + if (g->kids[i]->ctl){ + if (debug) fprint(2, "show %s: kid %s: %q\n", g->name, g->kids[i]->name, cp->str); + _ctlprint(g->kids[i], "show"); + } + flushimage(display, 1); + break; + case ESize: + r.max = Pt(_Ctlmaxsize, _Ctlmaxsize); + if (g->type == Ctlboxbox) + _ctlargcount(g, cp, 5); + switch(cp->nargs){ + default: + ctlerror("%s: args of %q", g->name, cp->str); + case 1: + /* recursively set size */ + g->mansize = 0; + if (g->setsize) + g->setsize((Control*)g); + break; + case 5: + _ctlargcount(g, cp, 5); + r.max.x = cp->iargs[3]; + r.max.y = cp->iargs[4]; + /* fall through */ + case 3: + 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", g->name, cp->str); + g->size = r; + g->mansize = 1; + break; + } + break; + case ESeparation: + if (g->type != Ctlstack){ + _ctlargcount(g, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: illegal value: %c", g->name, cp->str); + g->separation = cp->iargs[1]; + break; + } + // fall through for Ctlstack + default: + ctlerror("%q: unrecognized message '%s'", g->name, cp->str); + break; + } +} + +static void +groupfree(Control *c) +{ + Group *g; + + g = (Group*)c; + _putctlimage(g->bordercolor); + free(g->kids); +} + +static void +groupmouse(Control *c, Mouse *m) +{ + Group *g; + int i, lastkid; + + g = (Group*)c; + if (g->type == Ctlstack){ + i = g->selected; + if (i >= 0 && g->kids[i]->mouse && + ( ( ((m->buttons == 0) || (g->lastbut == 0)) && + ptinrect(m->xy, g->kids[i]->rect) ) || + ( ((m->buttons != 0) || (g->lastbut != 0)) && + (g->lastkid == i) ) ) ) { + if (debugm) fprint(2, "groupmouse %s mouse kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n", + g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut, + ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0); + (g->kids[i]->mouse)(g->kids[i], m); + g->lastkid = i; + g->lastbut = m->buttons; + } else { + if (debugm) fprint(2, "groupmouse %s skip kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n", + g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut, + ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0); + } + return; + } + + lastkid = -1; + for(i=0; i<g->nkids; i++) { + if(g->kids[i]->mouse && + ( ( ((m->buttons == 0) || (g->lastbut == 0)) && + ptinrect(m->xy, g->kids[i]->rect) ) || + ( ((m->buttons != 0) || (g->lastbut != 0)) && + (g->lastkid == i) ) ) ) { + if (debugm) fprint(2, "groupmouse %s mouse kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n", + g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut, + ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0); + (g->kids[i]->mouse)(g->kids[i], m); + lastkid = i; + } else { + if (debugm) fprint(2, "groupmouse %s skip kid %s i=%d lastkid=%d buttons=%d lastbut=%d inrect=%d\n", + g->name, g->kids[i]->name, i, g->lastkid, m->buttons, g->lastbut, + ptinrect(m->xy, g->kids[i]->rect) ? 1 : 0); + } + } + g->lastkid = lastkid; + g->lastbut = m->buttons; + +#ifdef notdef + if(m->buttons == 0){ + /* buttons now up */ + g->lastbut = 0; + return; + } + if(g->lastbut == 0 && m->buttons != 0){ + /* button went down, start tracking border */ + switch(g->stacking){ + default: + return; + case Vertical: + p = Pt(m->xy.x, middle_of_border.y); + p0 = Pt(g->r.min.x, m->xy.y); + p1 = Pt(g->r.max.x, m->xy.y); + break; + case Horizontal: + p = Pt(middle_of_border.x, m->xy.y); + p0 = Pt(m->xy.x, g->r.min.y); + p1 = Pt(m->xy.x, g->r.max.y); + break; + } + // setcursor(); + oi = nil; + } else if (g->lastbut != 0 && s->m.buttons != 0){ + /* button is down, keep tracking border */ + if(!eqpt(s->m.xy, p)){ + p = onscreen(s->m.xy); + r = canonrect(Rpt(p0, p)); + if(Dx(r)>5 && Dy(r)>5){ + i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */ + freeimage(oi); + if(i == nil) + goto Rescue; + oi = i; + border(i, r, Selborder, red, ZP); + flushimage(display, 1); + } + } + } else if (g->lastbut != 0 && s->m.buttons == 0){ + /* button went up, resize kiddies */ + } + g->lastbut = s->m.buttons; +#endif +} + +static void +activategroup(Control *c, int act) +{ + int i; + Group *g; + + g = (Group*)c; + for (i = 0; i < g->nkids; i++) + if (act) + activate(g->kids[i]); + else + deactivate(g->kids[i]); +} + +Control * +createrow(Controlset *cs, char *name) +{ + Control *c; + c = _createctl(cs, "row", sizeof(Group), name); + groupinit((Group*)c); + c->setsize = groupsize; + c->activate = activategroup; + return c; +} + +Control * +createcolumn(Controlset *cs, char *name) +{ + Control *c; + c = _createctl(cs, "column", sizeof(Group), name); + groupinit((Group*)c); + c->setsize = groupsize; + c->activate = activategroup; + return c; +} + +Control * +createboxbox(Controlset *cs, char *name) +{ + Control *c; + c = _createctl(cs, "boxbox", sizeof(Group), name); + groupinit((Group*)c); + c->activate = activategroup; + return c; +} + +Control * +createstack(Controlset *cs, char *name) +{ + Control *c; + c = _createctl(cs, "stack", sizeof(Group), name); + groupinit((Group*)c); + c->setsize = groupsize; + return c; +} + +void +_ctladdgroup(Control *c, Control *q) +{ + Group *g = (Group*)c; + + g->kids = ctlrealloc(g->kids, sizeof(Group*)*(g->nkids+1)); + g->kids[g->nkids++] = q; +} + +static void +removegroup(Group *g, int n) +{ + int i; + + if (g->selected == n) + g->selected = -1; + else if (g->selected > n) + g->selected--; + + for (i = n+1; i < g->nkids; i++) + g->kids[i-1] = g->kids[i]; + g->nkids--; +} + +static void +groupsize(Control *c) +{ + Rectangle r; + int i; + Control *q; + Group *g; + + g = (Group*)c; + assert(g->type == Ctlcolumn || g->type == Ctlrow || g->type == Ctlstack); + if (g->mansize) return; + r = Rect(1, 1, 1, 1); + if (debug) fprint(2, "groupsize %q\n", g->name); + for (i = 0; i < g->nkids; i++){ + q = g->kids[i]; + if (q->setsize) + q->setsize(q); + if (q->size.min.x == 0 || q->size.min.y == 0 || q->size.max.x == 0 || q->size.max.y == 0) + ctlerror("%q: bad size %R", q->name, q->size); + if (debug) fprint(2, "groupsize %q: [%d %q]: %R\n", g->name, i, q->name, q->size); + switch(g->type){ + case Ctlrow: + if (i) + r.min.x += q->size.min.x + g->border; + else + r.min.x = q->size.min.x; + if (i) + r.max.x += q->size.max.x + g->border; + else + r.max.x = q->size.max.x; + if (r.min.y < q->size.min.y) r.min.y = q->size.min.y; + if (r.max.y < q->size.max.y) r.max.y = q->size.max.y; + break; + case Ctlcolumn: + if (r.min.x < q->size.min.x) r.min.x = q->size.min.x; + if (r.max.x < q->size.max.x) r.max.x = q->size.max.x; + if (i) + r.min.y += q->size.min.y + g->border; + else + r.min.y = q->size.min.y; + if (i) + r.max.y += q->size.max.y + g->border; + else + r.max.y = q->size.max.y; + break; + case Ctlstack: + if (r.min.x < q->size.min.x) r.min.x = q->size.min.x; + if (r.max.x < q->size.max.x) r.max.x = q->size.max.x; + if (r.min.y < q->size.min.y) r.min.y = q->size.min.y; + if (r.max.y < q->size.max.y) r.max.y = q->size.max.y; + break; + } + } + g->size = rectaddpt(r, Pt(g->border, g->border)); + if (debug) fprint(2, "groupsize %q: %R\n", g->name, g->size); +} + +static void +boxboxresize(Group *g, Rectangle r) +{ + int rows, cols, ht, wid, i, hpad, wpad; + Rectangle rr; + + if(debug) fprint(2, "boxboxresize %q %R (%d×%d) min/max %R separation %d\n", g->name, r, Dx(r), Dy(r), g->size, g->separation); + ht = 0; + for(i=0; i<g->nkids; i++){ + if (g->kids[i]->size.min.y > ht) + ht = g->kids[i]->size.min.y; + } + if (ht == 0) + ctlerror("boxboxresize: height"); + rows = Dy(r) / (ht+g->separation); + hpad = (Dy(r) % (ht+g->separation)) / g->nkids; + cols = (g->nkids+rows-1)/rows; + wid = Dx(r) / cols - g->separation; + for(i=0; i<g->nkids; i++){ + if (g->kids[i]->size.max.x < wid) + wid = g->kids[i]->size.max.x; + } + for(i=0; i<g->nkids; i++){ + if (g->kids[i]->size.min.x > wid) + wid = g->kids[i]->size.min.x; + } + if (wid > Dx(r) / cols) + ctlerror("can't fit controls in boxbox"); + wpad = (Dx(r) % (wid+g->separation)) / g->nkids; + rr = rectaddpt(Rect(0,0,wid, ht), addpt(r.min, Pt(g->separation/2, g->separation/2))); + if(debug) fprint(2, "boxboxresize rows %d, cols %d, wid %d, ht %d, wpad %d, hpad %d\n", rows, cols, wid, ht, wpad, hpad); + for(i=0; i<g->nkids; i++){ + if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, g->kids[i]->name, rr, Dx(rr), Dy(rr)); + _ctlprint(g->kids[i], "rect %R", + rectaddpt(rr, Pt((wpad+wid+g->separation)*(i/rows), (hpad+ht+g->separation)*(i%rows)))); + } + g->nseparators = rows + cols - 2; + g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle)); + rr = r; + rr.max.y = rr.min.y + g->separation+hpad; + for (i = 1; i < rows; i++){ + g->separators[i-1] = rectaddpt(rr, Pt(0, (hpad+ht+g->separation)*i-g->separation-hpad)); + if(debug) fprint(2, "row separation %d [%d]: %R\n", i, i-1, rectaddpt(rr, Pt(0, (hpad+ht+g->separation)*i-g->separation))); + } + rr = r; + rr.max.x = rr.min.x + g->separation+wpad; + for (i = 1; i < cols; i++){ + g->separators[i+rows-2] = rectaddpt(rr, Pt((wpad+wid+g->separation)*i-g->separation-wpad, 0)); + if(debug) fprint(2, "col separation %d [%d]: %R\n", i, i+rows-2, rectaddpt(rr, Pt((wpad+wid+g->separation)*i-g->separation, 0))); + } +} + +static void +columnresize(Group *g, Rectangle r) +{ + int x, y, *d, *p, i, j, t; + Rectangle rr; + Control *q; + + x = Dx(r); + y = Dy(r); + if(debug) fprint(2, "columnresize %q %R (%d×%d) min/max %R separation %d\n", g->name, r, Dx(r), Dy(r), g->size, g->separation); + if (x < g->size.min.x) { + werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x); + r.max.x = r.min.x + g->size.min.x; + } + if (y < g->size.min.y) { + werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y); + r.max.y = r.min.y + g->size.min.y; + y = Dy(r); + } + d = ctlmalloc(g->nkids*sizeof(int)); + p = ctlmalloc(g->nkids*sizeof(int)); + if(debug) fprint(2, "kiddies: "); + for (i = 0; i < g->nkids; i++) { + q = g->kids[i]; + if(debug) fprint(2, "[%q]: %d⋯%d\t", q->name, q->size.min.y, q->size.max.y); + d[i] = q->size.min.y; + y -= d[i]; + p[i] = q->size.max.y - q->size.min.y; + } + if(debug) fprint(2, "\n"); + y -= (g->nkids-1) * g->separation; + if(y < 0){ + if (debug) fprint(2, "columnresize: y == %d\n", y); + y = 0; + } + if (y >= g->size.max.y - g->size.min.y) { + // all rects can be maximum width + for (i = 0; i < g->nkids; i++) + d[i] += p[i]; + y -= g->size.max.y - g->size.min.y; + } else { + // rects can't be max width, divide up the rest + j = y; + for (i = 0; i < g->nkids; i++) { + t = p[i] * y/(g->size.max.y - g->size.min.y); + d[i] += t; + j -= t; + } + d[0] += j; + y = 0; + } + g->nseparators = g->nkids-1; + g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle)); + j = 0; + rr = r; + for (i = 0; i < g->nkids; i++) { + q = g->kids[i]; + if (i < g->nkids - 1){ + g->separators[i].min.x = r.min.x; + g->separators[i].max.x = r.max.x; + } + t = y / (g->nkids - i); + y -= t; + j += t/2; + rr.min.y = r.min.y + j; + if (i) + g->separators[i-1].max.y = rr.min.y; + j += d[i]; + rr.max.y = r.min.y + j; + if (i < g->nkids - 1) + g->separators[i].min.y = rr.max.y; + j += g->separation + t - t/2; + _ctlprint(q, "rect %R", rr); + if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, rr, Dx(rr), Dy(rr)); + } + free(d); + free(p); +} + +static void +rowresize(Group *g, Rectangle r) +{ + int x, y, *d, *p, i, j, t; + Rectangle rr; + Control *q; + + x = Dx(r); + y = Dy(r); + if(debug) fprint(2, "rowresize %q %R (%d×%d), separation %d\n", g->name, r, Dx(r), Dy(r), g->separation); + if (x < g->size.min.x) { + werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x); + r.max.x = r.min.x + g->size.min.x; + x = Dx(r); + } + if (y < g->size.min.y) { + werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y); + r.max.y = r.min.y + g->size.min.y; + } + d = ctlmalloc(g->nkids*sizeof(int)); + p = ctlmalloc(g->nkids*sizeof(int)); + if(debug) fprint(2, "kiddies: "); + for (i = 0; i < g->nkids; i++) { + q = g->kids[i]; + if(debug) fprint(2, "[%q]: %d⋯%d\t", q->name, q->size.min.x, q->size.max.x); + d[i] = q->size.min.x; + x -= d[i]; + p[i] = q->size.max.x - q->size.min.x; + } + if(debug) fprint(2, "\n"); + x -= (g->nkids-1) * g->separation; + if(x < 0){ + if (debug) fprint(2, "rowresize: x == %d\n", x); + x = 0; + } + if (x >= g->size.max.x - g->size.min.x) { + if (debug) fprint(2, "max: %d > %d - %d", x, g->size.max.x, g->size.min.x); + // all rects can be maximum width + for (i = 0; i < g->nkids; i++) + d[i] += p[i]; + x -= g->size.max.x - g->size.min.x; + } else { + if (debug) fprint(2, "divvie up: %d < %d - %d", x, g->size.max.x, g->size.min.x); + // rects can't be max width, divide up the rest + j = x; + for (i = 0; i < g->nkids; i++) { + t = p[i] * x/(g->size.max.x - g->size.min.x); + d[i] += t; + j -= t; + } + d[0] += j; + x = 0; + } + j = 0; + g->nseparators = g->nkids-1; + g->separators = realloc(g->separators, g->nseparators*sizeof(Rectangle)); + rr = r; + for (i = 0; i < g->nkids; i++) { + q = g->kids[i]; + if (i < g->nkids - 1){ + g->separators[i].min.y = r.min.y; + g->separators[i].max.y = r.max.y; + } + t = x / (g->nkids - i); + x -= t; + j += t/2; + rr.min.x = r.min.x + j; + if (i) + g->separators[i-1].max.x = rr.min.x; + j += d[i]; + rr.max.x = r.min.x + j; + if (i < g->nkids - 1) + g->separators[i].min.x = rr.max.x; + j += g->separation + t - t/2; + _ctlprint(q, "rect %R", rr); + if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, rr, Dx(rr), Dy(rr)); + } + free(d); + free(p); +} + +static void +stackresize(Group *g, Rectangle r) +{ + int x, y, i; + Control *q; + + x = Dx(r); + y = Dy(r); + if(debug) fprint(2, "stackresize %q %R (%d×%d)\n", g->name, r, Dx(r), Dy(r)); + if (x < g->size.min.x){ + werrstr("resize %s: too narrow: need %d, have %d", g->name, g->size.min.x, x); + return; + } + if (y < g->size.min.y){ + werrstr("resize %s: too short: need %d, have %d", g->name, g->size.min.y, y); + return; + } + if (x > g->size.max.x) { + x = (x - g->size.max.x)/2; + r.min.x += x; + r.max.x -= x; + } + if (y > g->size.max.y) { + y = (y - g->size.max.y)/2; + r.min.y += y; + r.max.y -= y; + } + for (i = 0; i < g->nkids; i++){ + q = g->kids[i]; + if(debug) fprint(2, " %d %q: %R (%d×%d)\n", i, q->name, r, Dx(r), Dy(r)); + } + for (i = 0; i < g->nkids; i++){ + q = g->kids[i]; + _ctlprint(q, "rect %R", r); + } +} diff --git a/sys/src/libcontrol/group.h b/sys/src/libcontrol/group.h new file mode 100755 index 000000000..b6a2fe7b8 --- /dev/null +++ b/sys/src/libcontrol/group.h @@ -0,0 +1,18 @@ + +typedef struct Group Group; + +struct Group { + Control; + int lastbut; + int border; + int mansize; /* size was set manually */ + int separation; + int selected; + int lastkid; + CImage *bordercolor; + CImage *image; + int nkids; + Control **kids; /* mallocated */ + Rectangle *separators; /* mallocated */ + int nseparators; +}; diff --git a/sys/src/libcontrol/keyboard.c b/sys/src/libcontrol/keyboard.c new file mode 100755 index 000000000..9bfc594e4 --- /dev/null +++ b/sys/src/libcontrol/keyboard.c @@ -0,0 +1,536 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Keyboard Keyboard; + +enum{ + SRegular = 0, + SShift = 1, + SCaps = 2, + SMask = 3, + Nstate = 4, + SControl = 4, +}; + +struct Keyboard +{ + Control; + CImage *image; + CImage *mask; + CImage *light; + CImage *textcolor; + CImage *bordercolor; + CFont *font; + CFont *ctlfont; + Image *im[Nstate]; + int border; + int lastbut; + int state; + char *key; +}; + +enum{ + EBorder, + EBordercolor, + EFocus, + EFont, + EFormat, + EHide, + EImage, + ELight, + EMask, + ERect, + EReveal, + EShow, + ESize, +}; + +static char *cmds[] = { + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EFocus] = "focus", + [EFont] = "font", + [EFormat] = "format", + [EHide] = "hide", + [EImage] = "image", + [ELight] = "light", + [EMask] = "mask", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + nil +}; + +enum +{ + Nrow = 5 +}; + +static uchar wid [Nrow][16] = { + {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 30, }, + {24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, }, + {32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, }, + {40, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, }, + {30, 30, 80, 40, 42, 24, }, +}; + +static char *keyregular[Nrow] = { + "`\0001\0002\0003\0004\0005\0006\0007\0008\0009\0000\0-\0=\0\\\0<-\0\0", + "->\0q\0w\0e\0r\0t\0y\0u\0i\0o\0p\0[\0]\0Del\0\0", + "Caps\0a\0s\0d\0f\0g\0h\0j\0k\0l\0;\0'\0Enter\0\0", + "Shift\0z\0x\0c\0v\0b\0n\0m\0,\0.\0/\0Shift\0\0", + "Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0" +}; + +static char *keyshift[Nrow] = { + "~\0!\0@\0#\0$\0%\0^\0&\0*\0(\0)\0_\0+\0|\0<-\0\0", + "->\0Q\0W\0E\0R\0T\0Y\0U\0I\0O\0P\0{\0}\0Del\0\0", + "Caps\0A\0S\0D\0F\0G\0H\0J\0K\0L\0:\0\"\0Enter\0\0", + "Shift\0Z\0X\0C\0V\0B\0N\0M\0<\0>\0?\0Shift\0\0", + "Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0" +}; + +static char *keycaps[Nrow] = { + "`\0001\0002\0003\0004\0005\0006\0007\0008\0009\0000\0-\0=\0\\\0<-\0\0", + "->\0Q\0W\0E\0R\0T\0Y\0U\0I\0O\0P\0[\0]\0Del\0\0", + "Caps\0A\0S\0D\0F\0G\0H\0J\0K\0L\0;\0'\0Enter\0\0", + "Shift\0Z\0X\0C\0V\0B\0N\0M\0,\0.\0/\0Shift\0\0", + "Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0" +}; + +static char *keycapsshift[Nrow] = { + "~\0!\0@\0#\0$\0%\0^\0&\0*\0(\0)\0_\0+\0|\0<-\0\0", + "->\0q\0w\0e\0r\0t\0y\0u\0i\0o\0p\0{\0}\0Del\0\0", + "Caps\0a\0s\0d\0f\0g\0h\0j\0k\0l\0:\0\"\0Enter\0\0", + "Shift\0z\0x\0c\0v\0b\0n\0m\0<\0>\0?\0Shift\0\0", + "Ctrl\0Alt\0 \0Scrib\0Menu\0Esc\0\0" +}; + +struct{ + char *name; + int val; +}keytab[] = { + "Shift", 0, + "Ctrl", 0, + "Alt", 0, + "Caps", 0, + "Del", '\177', + "Enter", '\n', + "Esc", '\033', + "<-", '\b', + "->", '\t', + "Scrib", 0x10000, + "Menu", 0x10001, + nil, 0, +}; + +static char **keyset[Nstate] = { + keyregular, + keyshift, + keycaps, + keycapsshift, +}; + +static void keyboardshow(Keyboard*); +static void keyup(Keyboard*, Point); +static void keydown(Keyboard*, Point); +static void keyresize(Keyboard*); + +static void +keyboardmouse(Control *c, Mouse *m) +{ + Keyboard *k; + + k = (Keyboard *)c; + if(m->buttons==1) + keydown(k, m->xy); + else if(k->lastbut==1 && m->buttons==0) + keyup(k, m->xy); + k->lastbut = m->buttons; +} + +static void +keyboardfree(Control *c) +{ + int i; + Keyboard *k; + + k = (Keyboard *)c; + _putctlimage(k->image); + _putctlimage(k->mask); + _putctlimage(k->light); + _putctlimage(k->textcolor); + _putctlimage(k->bordercolor); + _putctlfont(k->font); + _putctlfont(k->ctlfont); + for(i=0; i<nelem(k->im); i++) + freeimage(k->im[i]); + free(k->format); +} + +static int +keyboardy(Keyboard *k, int row) +{ + int dy; + + if(row >= Nrow) + return k->rect.max.y-k->border; + dy = Dy(k->rect)-2*k->border; + return k->rect.min.y+k->border+(row*dy+Nrow-1)/Nrow; +} + +static char* +whichkey(Keyboard *k, Point p, int *rowp, int *colp, Rectangle *rp) +{ + uchar *wp; + char *kp; + int row, col, dx, dy, x, n, maxx; + Rectangle r; + + r = insetrect(k->rect, k->border); + if(!ptinrect(p, r)) + return nil; + maxx = r.max.x; + dx = Dx(r); + dy = Dy(r); + row = (p.y - r.min.y)*Nrow/dy; + if(row >= Nrow) + row = Nrow-1; + r.min.y = keyboardy(k, row); + r.max.y = keyboardy(k, row+1); + x = r.min.x; + kp = keyset[k->state&SMask][row]; + wp = wid[row]; + for(col=0; *kp; col++,kp+=n+1){ + n = strlen(kp); + r.min.x = x; + r.max.x = x + (wp[col]*dx+255)/256; + if(kp[n+1] == '\0') + r.max.x = maxx; + if(r.max.x > p.x) + break; + x = r.max.x; + } + *rp = insetrect(r, 1); + *rowp = row; + *colp = col; + return kp; +} + +static Rectangle +keyrect(Keyboard *k, int row, int col) +{ + uchar *wp; + char *kp; + int i, x, n, dx; + Rectangle r; + Point p; + + r = insetrect(k->rect, k->border); + p = r.min; + dx = Dx(r); + r.min.y = keyboardy(k, row); + r.max.y = keyboardy(k, row+1); + x = r.min.x; + kp = keyset[0][row]; + wp = wid[row]; + for(i=0; *kp; i++,kp+=n+1){ + n = strlen(kp); + r.min.x = x; + r.max.x = x + (wp[i]*dx+255)/256; + if(kp[n+1] == '\0') + r.max.x = p.x+dx; + if(i >= col) + break; + x = r.max.x; + } + return insetrect(r, 1); +} + +static void +keydraw(Keyboard *k, int state) +{ + Point p, q; + int row, col, x, dx, dy, nexty, n; + uchar *wp; + char *kp; + Rectangle r; + Font *f, *f1, *f2; + Image *im; + + freeimage(k->im[state]); + k->im[state] = nil; + if(Dx(k->rect)-2*k->border <= 0) + return; + + im = allocimage(display, k->rect, screen->chan, 0, ~0); + if(im == nil) + return; + k->im[state] = im; + + r = insetrect(k->rect, k->border); + border(im, k->rect, k->border, k->bordercolor->image, ZP); + draw(im, r, k->image->image, nil, ZP); + dx = Dx(r); + dy = Dy(r); + p = r.min; + f1 = k->font->font; + f2 = k->ctlfont->font; + nexty = p.y; + for(row=0; row<Nrow; row++){ + x = p.x; + kp = keyset[state][row]; + wp = wid[row]; + r.min.y = nexty; + nexty = keyboardy(k, row+1); + r.max.y = nexty; + for(col=0; *kp; col++,kp+=n+1){ + r.min.x = x; + r.max.x = x + (wp[col]*dx+255)/256; + n = strlen(kp); + if(kp[n+1] == '\0') + r.max.x = p.x+dx; + if(row == Nrow-1) + r.max.y = p.y+dy; + if(n > 1) + f = f2; + else + f = f1; + q = _ctlalignpoint(r, stringnwidth(f, kp, n), f->height, Acenter); + _string(im, q, k->textcolor->image, + ZP, f, kp, nil, n, r, + nil, ZP, SoverD); + x = r.max.x; + if(kp[n+1]) + draw(im, Rect(x, r.min.y, x+1, r.max.y), + k->textcolor->image, nil, ZP); + } + if(row != Nrow-1) + draw(im, Rect(p.x, r.max.y, p.x+dx, r.max.y+1), + k->textcolor->image, nil, ZP); + } +} + +static void +keyresize(Keyboard *k) +{ + int i; + + for(i=0; i<Nstate; i++) + keydraw(k, i); +} + +static void +keyboardshow(Keyboard *k) +{ + Rectangle r; + + if (k->hidden) + return; + if(k->im[0]==nil || !eqrect(k->im[0]->r, k->rect)) + keyresize(k); + if(k->im[k->state&SMask] == nil) + return; + draw(k->screen, k->rect, k->im[k->state&SMask], nil, k->rect.min); + if(k->state & SShift){ + r = keyrect(k, 3, 0); + draw(k->screen, r, k->light->image, k->mask->image, ZP); + r = keyrect(k, 3, 11); + draw(k->screen, r, k->light->image, k->mask->image, ZP); + } + if(k->state & SCaps){ + r = keyrect(k, 2, 0); + draw(k->screen, r, k->light->image, k->mask->image, ZP); + } + if(k->state & SControl){ + r = keyrect(k, 4, 0); + draw(k->screen, r, k->light->image, k->mask->image, ZP); + } + flushimage(display, 1); +} + +static void +keydown(Keyboard *k, Point p) +{ + int row, col; + Rectangle r; + char *s; + + s = whichkey(k, p, &row, &col, &r); + if(s == k->key) + return; + keyboardshow(k); + if(s != nil) + draw(k->screen, r, k->light->image, k->mask->image, ZP); + flushimage(display, 1); + k->key = s; +} + +static int +keylookup(char *s) +{ + int i; + + for(i=0; keytab[i].name; i++) + if(strcmp(s, keytab[i].name) == 0) + return keytab[i].val; + return s[0]; +} + +static void +keyup(Keyboard *k, Point p) +{ + int row, col; + Rectangle r; + char *s; + int val; + + s = whichkey(k, p, &row, &col, &r); + if(s == nil) + return; + val = keylookup(s); + if(k->state & SControl) + if(' '<val && val<0177) + val &= ~0x60; + if(strcmp(s, "Alt") == 0) + {;} + if(strcmp(s, "Ctrl") == 0){ + k->state ^= SControl; + }else + k->state &= ~SControl; + if(strcmp(s, "Shift")==0 || strcmp(s, "Caps")==0){ + if(strcmp(s, "Shift") == 0) + k->state ^= SShift; + if(strcmp(s, "Caps") == 0) + k->state ^= SCaps; + }else + k->state &= ~SShift; + keyboardshow(k); + if(val) + chanprint(k->event, k->format, k->name, val); + k->key = nil; +} + +static void +keyboardctl(Control *c, CParse *cp) +{ + int cmd; + Rectangle r; + Keyboard *k; + + k = (Keyboard*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + ctlerror("%q: unrecognized message '%s'", k->name, cp->str); + break; + case EBorder: + _ctlargcount(k, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: bad border: %c", k->name, cp->str); + k->border = cp->iargs[1]; + break; + case EBordercolor: + _ctlargcount(k, cp, 2); + _setctlimage(k, &k->bordercolor, cp->args[1]); + break; + case EFocus: + /* ignore focus change */ + break; + case EFont: + if(cp->nargs!=2 && cp->nargs!=3) + ctlerror("%q: bad font message '%s'", k->name, cp->str); + _setctlfont(k, &k->font, cp->args[1]); + if(cp->nargs == 3) + _setctlfont(k, &k->ctlfont, cp->args[2]); + else + _setctlfont(k, &k->ctlfont, cp->args[1]); + break; + case EFormat: + _ctlargcount(k, cp, 2); + k->format = ctlstrdup(cp->args[1]); + break; + case EHide: + _ctlargcount(k, cp, 1); + k->hidden = 1; + break; + case EImage: + _ctlargcount(k, cp, 2); + _setctlimage(k, &k->image, cp->args[1]); + break; + case ELight: + _ctlargcount(k, cp, 2); + _setctlimage(k, &k->light, cp->args[1]); + break; + case EMask: + _ctlargcount(k, cp, 2); + _setctlimage(k, &k->mask, cp->args[1]); + break; + case ERect: + _ctlargcount(k, 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", k->name, cp->str); + k->rect = r; + keyboardshow(k); + break; + case EReveal: + _ctlargcount(k, cp, 1); + k->hidden = 0; + keyboardshow(k); + break; + case EShow: + _ctlargcount(k, cp, 1); + keyboardshow(k); + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(k, 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", k->name, cp->str); + k->size.min = r.min; + k->size.max = r.max; + break; + } +} + +Control* +createkeyboard(Controlset *cs, char *name) +{ + Keyboard *k; + + k = (Keyboard *)_createctl(cs, "keyboard", sizeof(Keyboard), name); + k->image = _getctlimage("white"); + k->mask = _getctlimage("opaque"); + k->light = _getctlimage("yellow"); + k->bordercolor = _getctlimage("black"); + k->textcolor = _getctlimage("black"); + k->font = _getctlfont("font"); + k->ctlfont = _getctlfont("font"); + k->format = ctlstrdup("%q: value 0x%x"); + k->border = 0; + k->lastbut = 0; + k->key = nil; + k->state = SRegular; + k->ctl = keyboardctl; + k->mouse = keyboardmouse; + k->exit = keyboardfree; + k->size = Rect(246, 2 + 5 * (k->font->font->height + 1), 512, 256); + return k; +} diff --git a/sys/src/libcontrol/label.c b/sys/src/libcontrol/label.c new file mode 100755 index 000000000..d924f6dd9 --- /dev/null +++ b/sys/src/libcontrol/label.c @@ -0,0 +1,204 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Label Label; + +struct Label +{ + Control; + int border; + CFont *font; + CImage *image; + CImage *textcolor; + CImage *bordercolor; + char *text; + int align; +}; + +enum{ + EAlign, + EBorder, + EBordercolor, + EFocus, + EFont, + EHide, + EImage, + ERect, + EReveal, + EShow, + ESize, + ETextcolor, + EValue, +}; + +static char *cmds[] = { + [EAlign] = "align", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EFocus] = "focus", + [EFont] = "font", + [EHide] = "hide", + [EImage] = "image", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + [ETextcolor] = "textcolor", + [EValue] = "value", + nil +}; + +static void labelshow(Label*); + +static void +labelfree(Control *c) +{ + Label *l; + + l = (Label*)c; + _putctlfont(l->font); + _putctlimage(l->image); + _putctlimage(l->textcolor); + _putctlimage(l->bordercolor); +} + + +static void +labelshow(Label *l) +{ + Rectangle r; + Point p; + + if (l->hidden) + return; + r = l->rect; + draw(l->screen, r, l->image->image, nil, l->image->image->r.min); + if(l->border > 0){ + border(l->screen, r, l->border, l->bordercolor->image, l->bordercolor->image->r.min); + r = insetrect(r, l->border); + } + p = _ctlalignpoint(r, + stringwidth(l->font->font, l->text), + l->font->font->height, l->align); + _string(l->screen, p, l->textcolor->image, + ZP, l->font->font, l->text, nil, strlen(l->text), + r, nil, ZP, SoverD); + flushimage(display, 1); +} + +static void +labelctl(Control *c, CParse *cp) +{ + int cmd; + Rectangle r; + Label *l; + + l = (Label*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + ctlerror("%q: unrecognized message '%s'", l->name, cp->str); + break; + case EAlign: + _ctlargcount(l, cp, 2); + l->align = _ctlalignment(cp->args[1]); + break; + case EBorder: + _ctlargcount(l, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: bad border: %c", l->name, cp->str); + l->border = cp->iargs[1]; + break; + case EBordercolor: + _ctlargcount(l, cp, 2); + _setctlimage(l, &l->bordercolor, cp->args[1]); + break; + case EFocus: + /* ignore focus change */ + break; + case EFont: + _ctlargcount(l, cp, 2); + _setctlfont(l, &l->font, cp->args[1]); + break; + case EHide: + _ctlargcount(l, cp, 1); + l->hidden = 1; + break; + case EImage: + _ctlargcount(l, cp, 2); + _setctlimage(l, &l->image, cp->args[1]); + break; + case ERect: + _ctlargcount(l, 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", l->name, cp->str); + l->rect = r; + break; + case EReveal: + _ctlargcount(l, cp, 1); + l->hidden = 0; + labelshow(l); + break; + case EShow: + _ctlargcount(l, cp, 1); + labelshow(l); + /* + _ctlargcount(l, cp, 2); + _setctlimage(l, &l->textcolor, cp->args[1]); + */ + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(l, 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", l->name, cp->str); + l->size.min = r.min; + l->size.max = r.max; + break; + case ETextcolor: + _ctlargcount(l, cp, 2); + _setctlimage(l, &l->textcolor, cp->args[1]); + break; + case EValue: + _ctlargcount(l, cp, 2); + if(strcmp(cp->args[1], l->text) != 0){ + free(l->text); + l->text = ctlstrdup(cp->args[1]); + labelshow(l); + } + break; + } +} + +Control* +createlabel(Controlset *cs, char *name) +{ + Label *l; + + l = (Label*)_createctl(cs, "label", sizeof(Label), name); + l->text = ctlstrdup(""); + l->image = _getctlimage("white"); + l->textcolor = _getctlimage("black"); + l->bordercolor = _getctlimage("black"); + l->font = _getctlfont("font"); + l->border = 0; + l->ctl = labelctl; + l->exit = labelfree; + return (Control *)l; +} diff --git a/sys/src/libcontrol/menu.c b/sys/src/libcontrol/menu.c new file mode 100755 index 000000000..56b915a4c --- /dev/null +++ b/sys/src/libcontrol/menu.c @@ -0,0 +1,366 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Menu0 Menu0; /* Menu is taken by mouse.h */ + +struct Menu0 +{ + Control; + CImage *image; + CImage *bordercolor; + CImage *textcolor; + CImage *selectcolor; + CImage *selecttextcolor; + CFont *font; + char **line; + int nline; + int border; + int align; + Image *window; + int visible; /* state of menu */ + int selection; /* currently selected line; -1 == none */ + int prevsel; /* previous selection */ + int lastbut; /* previous state of mouse button */ +}; + +enum{ + EAdd, + EAlign, + EBorder, + EBordercolor, + EFocus, + EFont, + EFormat, + EHide, + EImage, + ERect, + EReveal, + ESelectcolor, + ESelecttextcolor, + EShow, + ESize, + ETextcolor, + EWindow, +}; + +static char *cmds[] = { + [EAdd] = "add", + [EAlign] = "align", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EFocus] = "focus", + [EFont] = "font", + [EFormat] = "format", + [EHide] = "hide", + [EImage] = "image", + [ERect] = "rect", + [EReveal] = "reveal", + [ESelectcolor] = "selectcolor", + [ESelecttextcolor] = "selecttextcolor", + [EShow] = "show", + [ESize] = "size", + [ETextcolor] = "textcolor", + [EWindow] = "window", + nil +}; + +static void menushow(Menu0*); +static void menuhide(Menu0*); + +static void +menufree(Control *c) +{ + Menu0 *m; + + m = (Menu0*)c; + _putctlfont(m->font); + _putctlimage(m->image); + _putctlimage(m->textcolor); + _putctlimage(m->bordercolor); + _putctlimage(m->selectcolor); + _putctlimage(m->selecttextcolor); +} + +static void +menushow(Menu0 *m) +{ + Rectangle r, clipr; + int i, dx, dy, w; + Font *f; + Point p, q; + Image *im, *c; + + if(m->hidden || m->window == nil) + return; + + m->visible = 1; + f = m->font->font; + draw(m->window, m->rect, m->image->image, nil, m->image->image->r.min); + if(m->border > 0) + border(m->window, m->rect, m->border, m->bordercolor->image, ZP); + /* text goes here */ + dx = 0; + for(i=0; i<m->nline; i++){ + w = stringwidth(f, m->line[i]); + if(dx < w) + dx = w; + } + dy = m->nline*f->height; + clipr = insetrect(m->rect, m->border); + p = _ctlalignpoint(clipr, dx, dy, m->align); + im = m->textcolor->image; +// if(m->pressed) +// im = m->pressedtextcolor->image; + for(i=0; i<m->nline; i++){ + r.min = p; + r.max.x = p.x+dx; + r.max.y = p.y+f->height; + c = im; + if(i == m->selection){ + draw(m->window, r, m->selectcolor->image, nil, ZP); + c = m->selecttextcolor->image; + } + q = _ctlalignpoint(r, stringwidth(f, m->line[i]), f->height, m->align%3); + _string(m->window, q, c, + ZP, f, m->line[i], nil, strlen(m->line[i]), + clipr, nil, ZP, SoverD); + p.y += f->height; + } +// if(m->pressed) +// draw(m->screen, m->rect, m->lighm->image, m->mask->image, m->mask->image->r.min); + flushimage(display, 1); +} + +static Point +menusize(Menu0 *m) +{ + int x, y; + int i; + Point p; + Font *f; + + x = 0; + y = 0; + f = m->font->font; + for(i=0; i<m->nline; i++){ + p = stringsize(f, m->line[i]); + if(p.x > x) + x = p.x; + y += f->height; + } + + return Pt(x+2*m->border, y+2*m->border); +} + +static void +menuhide(Menu0 *m) +{ + freeimage(m->window); + m->window = nil; + m->rect.max.y = m->rect.min.y; /* go to zero size */ + m->lastbut = 0; + m->visible = 0; + if(m->selection >= 0) + m->prevsel = m->selection; + m->selection = -1; + _ctlfocus(m, 0); +} + +static void +menutrack(Control *c, Mouse *ms) +{ + Rectangle r; + int s; + Menu0 *m; + + m = (Menu0*)c; + if(m->window == nil) + return; + if(m->lastbut && ms->buttons==0){ /* menu was released */ + chanprint(m->event, "%q: value %d", m->name, m->selection); + menuhide(m); + return; + } + m->lastbut = ms->buttons; + r = insetrect(m->rect, m->border); + if(!ptinrect(ms->xy, r)) + s = -1; + else{ + s = (ms->xy.y - r.min.y)/m->font->font->height; + if(s < 0 || s >= m->nline) + s = -1; + } + if(m->visible== 0 || s!=m->selection){ + m->selection = s; + menushow(m); + } +} + +static void +menuctl(Control *c, CParse *cp) +{ + int up, cmd, h; + Rectangle r; + Menu0 *m; + Point diag; + + m = (Menu0*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + ctlerror("%q: unrecognized message '%s'", m->name, cp->str); + break; + case EAdd: + _ctlargcount(m, cp, 2); + m->line = ctlrealloc(m->line, (m->nline+1)*sizeof(char*)); + m->line[m->nline++] = ctlstrdup(cp->args[1]); + menushow(m); + break; + case EAlign: + _ctlargcount(m, cp, 2); + m->align = _ctlalignment(cp->args[1]); + menushow(m); + break; + case EBorder: + _ctlargcount(m, cp, 2); + m->border = cp->iargs[1]; + menushow(m); + break; + case EBordercolor: + _ctlargcount(m, cp, 2); + _setctlimage(m, &m->bordercolor, cp->args[1]); + menushow(m); + break; + case EFocus: + _ctlargcount(m, cp, 2); + if(atoi(cp->args[1]) == 0) + menuhide(m); + break; + case EFont: + _ctlargcount(m, cp, 2); + _setctlfont(m, &m->font, cp->args[1]); + break; + case EFormat: + _ctlargcount(m, cp, 2); + m->format = ctlstrdup(cp->args[1]); + break; + case EHide: + _ctlargcount(m, cp, 1); + m->hidden = 1; + break; + case EImage: + _ctlargcount(m, cp, 2); + _setctlimage(m, &m->image, cp->args[1]); + menushow(m); + break; + case ERect: + _ctlargcount(m, 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", m->name, cp->str); + m->rect = r; + menushow(m); + break; + case EReveal: + _ctlargcount(m, cp, 1); + m->hidden = 0; + menushow(m); + break; + case ESelectcolor: + _ctlargcount(m, cp, 2); + _setctlimage(m, &m->selectcolor, cp->args[1]); + menushow(m); + break; + case ESelecttextcolor: + _ctlargcount(m, cp, 2); + _setctlimage(m, &m->selecttextcolor, cp->args[1]); + menushow(m); + break; + case EShow: + _ctlargcount(m, cp, 1); + menushow(m); + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(m, 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", m->name, cp->str); + m->size.min = r.min; + m->size.max = r.max; + break; + case ETextcolor: + _ctlargcount(m, cp, 2); + _setctlimage(m, &m->textcolor, cp->args[1]); + menushow(m); + break; + case EWindow: + /* no args == toggle; otherwise 0 or 1 for state of window */ + if(cp->nargs >= 2) + up = cp->iargs[1]; + else + up = (m->window == nil); + if(!up){ /* take window down */ + if(m->window) + menuhide(m); + break; + } + if(m->window != nil) + break; + diag = menusize(m); + m->rect.max.x = m->rect.min.x + diag.x; + m->rect.max.y = m->rect.min.y + diag.y; + m->window = allocwindow(_screen, m->rect, Refbackup, DWhite); + if(m->window == nil) + m->window = m->screen; + up = m->prevsel; + if(up<0 || up>=m->nline) + up = 0; + m->selection = up; + menushow(m); + h = m->font->font->height; + moveto(m->controlset->mousectl, + Pt(m->rect.min.x+Dx(m->rect)/2, m->rect.min.y+up*h+h/2)); +// _ctlfocus(m, 1); + break; + } +} + +Control* +createmenu(Controlset *cs, char *name) +{ + Menu0 *m; + + m = (Menu0*)_createctl(cs, "menu", sizeof(Menu0), name); + m->font = _getctlfont("font"); + m->image = _getctlimage("white"); + m->textcolor = _getctlimage("black"); + m->selectcolor = _getctlimage("yellow"); + m->selecttextcolor = _getctlimage("black"); + m->bordercolor = _getctlimage("black"); + m->format = ctlstrdup("%q: value %d"); + m->border = 0; + m->align = Aupperleft; + m->visible = 0; + m->window = nil; + m->lastbut = 0; + m->selection = -1; + m->mouse = menutrack; + m->ctl = menuctl; + m->exit = menufree; + return (Control *)m; +} diff --git a/sys/src/libcontrol/mkfile b/sys/src/libcontrol/mkfile new file mode 100755 index 000000000..f86d47229 --- /dev/null +++ b/sys/src/libcontrol/mkfile @@ -0,0 +1,37 @@ +</$objtype/mkfile + +LIB=/$objtype/lib/libcontrol.a + +OFILES=\ + box.$O\ + button.$O\ + cache.$O\ + control.$O\ + entry.$O\ + group.$O\ + keyboard.$O\ + label.$O\ + menu.$O\ + radiobutton.$O\ + scribble.$O\ + slider.$O\ + tabs.$O\ + text.$O\ + textbutton.$O\ + textbutton3.$O\ + +HFILES=/sys/include/draw.h\ + /sys/include/mouse.h\ + /sys/include/keyboard.h\ + /sys/include/control.h\ + group.h + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + ${LIB:/$objtype/%=/386/%}\ + +</sys/src/cmd/mksyslib + +$OFILES: $HFILES diff --git a/sys/src/libcontrol/radiobutton.c b/sys/src/libcontrol/radiobutton.c new file mode 100755 index 000000000..133730a50 --- /dev/null +++ b/sys/src/libcontrol/radiobutton.c @@ -0,0 +1,192 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Radio Radio; + +struct Radio +{ + Control; + int value; + int lastbut; + Control **buttons; + int nbuttons; + char *eventstr; +}; + +enum{ + EAdd, + EButton, + EFocus, + EFormat, + EHide, + ERect, + EReveal, + EShow, + ESize, + EValue, +}; + +static char *cmds[] = { + [EAdd] = "add", + [EButton] = "button", + [EFocus] = "focus", + [EFormat] = "format", + [EHide] = "hide", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + [EValue] = "value", + nil +}; + +static void radioshow(Radio*); +static void radiofree(Radio*); + +static void +radiomouse(Control *c, Mouse *m) +{ + Radio *r; + int i; + + r = (Radio*)c; + for(i=0; i<r->nbuttons; i++) + if(ptinrect(m->xy, r->buttons[i]->rect) && r->buttons[i]->mouse){ + (r->buttons[i]->mouse)(r->buttons[i], m); + break; + } +} + +static void +radiofree(Radio*) +{ +} + +static void +radioshow(Radio *r) +{ + int i; + + if (r->hidden) + return; + for(i=0; i<r->nbuttons; i++){ + _ctlprint(r->buttons[i], "value %d", (r->value==i)); + _ctlprint(r->buttons[i], "show"); + } +} + +static void +radioctl(Control *c, CParse *cp) +{ + int cmd, i; + Rectangle rect; + Radio *r; + char fmt[256]; + + r = (Radio*)c; + + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + ctlerror("%q: unrecognized message '%s'", r->name, cp->str); + break; + case EAdd: + _ctlargcount(r, cp, 2); + c = controlcalled(cp->args[1]); + if(c == nil) + ctlerror("%q: can't add %s: %r", r->name, cp->args[1]); + snprint(fmt, sizeof fmt, "%%q: %q button %%d", r->name); + _ctlprint(c, "format %q", fmt); + controlwire(c, "event", c->controlset->ctl); + r->buttons = ctlrealloc(r->buttons, (r->nbuttons+1)*sizeof(Control*)); + r->buttons[r->nbuttons] = c; + r->nbuttons++; + r->value = -1; + radioshow(r); + break; + case EButton: + if (cp->sender == nil) + ctlerror("%q: senderless buttonevent: %q", r->name, cp->str); + c = controlcalled(cp->sender); + for(i=0; i<r->nbuttons; i++) + if (c == r->buttons[i]) + break; + if (i == r->nbuttons) + ctlerror("%q: not my event: %q", r->name, cp->str); + if(cp->iargs[1] == 0){ + /* button was turned off; turn it back on */ + _ctlprint(c, "value 1"); + }else{ + r->value = i; + chanprint(r->event, r->format, r->name, i); + radioshow(r); + } + break; + case EFormat: + _ctlargcount(r, cp, 2); + r->format = ctlstrdup(cp->args[1]); + break; + case EHide: + _ctlargcount(r, cp, 1); + r->hidden = 1; + break; + case EFocus: + /* ignore focus change */ + break; + case ERect: + _ctlargcount(r, cp, 5); + rect.min.x = cp->iargs[1]; + rect.min.y = cp->iargs[2]; + rect.max.x = cp->iargs[3]; + rect.max.y = cp->iargs[4]; + r->rect = rect; + break; + case EReveal: + _ctlargcount(r, cp, 1); + r->hidden = 0; + radioshow(r); + break; + case EShow: + _ctlargcount(r, cp, 1); + radioshow(r); + break; + case ESize: + if (cp->nargs == 3) + rect.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(r, cp, 5); + rect.max.x = cp->iargs[3]; + rect.max.y = cp->iargs[4]; + } + rect.min.x = cp->iargs[1]; + rect.min.y = cp->iargs[2]; + if(rect.min.x<=0 || rect.min.y<=0 || rect.max.x<=0 || rect.max.y<=0 || rect.max.x < rect.min.x || rect.max.y < rect.min.y) + ctlerror("%q: bad sizes: %s", r->name, cp->str); + r->size.min = rect.min; + r->size.max = rect.max; + break; + case EValue: + _ctlargcount(r, cp, 2); + r->value = cp->iargs[1]; + radioshow(r); + break; + } +} + +Control* +createradiobutton(Controlset *cs, char *name) +{ + Radio *r; + + r = (Radio*)_createctl(cs, "label", sizeof(Radio), name); + r->format = ctlstrdup("%q: value %d"); + r->value = -1; /* nobody set at first */ + r->mouse = radiomouse; + r->ctl = radioctl; + return (Control*)r; +} diff --git a/sys/src/libcontrol/scribble.c b/sys/src/libcontrol/scribble.c new file mode 100755 index 000000000..4e10ab68f --- /dev/null +++ b/sys/src/libcontrol/scribble.c @@ -0,0 +1,325 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> +#include <scribble.h> + +typedef struct Scrib Scrib; + +struct Scrib +{ + Control; + int border; + CImage *image; + CImage *color; + CImage *bordercolor; + CFont *font; + int align; + int lastbut; + char lastchar[8]; + Scribble *scrib; +}; + +enum{ + EAlign, + EBorder, + EBordercolor, + EFocus, + EFont, + EHide, + EImage, + ELinecolor, + ERect, + EReveal, + EShow, + ESize, +}; + +static char *cmds[] = { + [EAlign] = "align", + [EBorder] = "border", + [EBordercolor] ="bordercolor", + [EFocus] = "focus", + [EFont] = "font", + [EHide] = "hide", + [EImage] = "image", + [ELinecolor] = "linecolor", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + nil +}; + +static void scribshow(Scrib*); +static void scribchar(Scrib*, Rune); + +static void resetstroke(Scrib *w); +static void displaystroke(Scrib *w); +static void displaylast(Scrib *w); +static void addpoint(Scrib *w, Point p); + +static void +scribmouse(Control *c, Mouse *m) +{ + Scrib *b; + Rune r; + + b = (Scrib*)c; + if (m->buttons & 0x1) { + if ((b->lastbut & 0x1) == 0) { + /* mouse went down */ + resetstroke(b); + } + /* mouse is down */ + if (ptinrect(m->xy,b->rect)) + addpoint(b, m->xy); + } else if (b->lastbut & 0x1) { + /* mouse went up */ + if (ptinrect(m->xy,b->rect)) { + r = recognize(b->scrib); + scribchar(b, r); + scribshow(b); + if (r) chanprint(b->event, b->format, b->name, r); + } + } + b->lastbut = m->buttons; +} + +static void +scribfree(Control *c) +{ + Scrib *b; + + b = (Scrib*)c; + _putctlimage(b->image); + _putctlimage(b->color); + _putctlimage(b->bordercolor); + _putctlfont(b->font); +// scribblefree(b->scrib); +} + +static void +scribchar(Scrib *b, Rune r) +{ + if(r == 0) + b->lastchar[0] = '\0'; + else if(r == ' ') + strcpy(b->lastchar, "' '"); + else if(r < ' ') + sprint(b->lastchar, "ctl-%c", r+'@'); + else + sprint(b->lastchar, "%C", r); +} + + +static void +scribshow(Scrib *b) +{ + Image *i; + Rectangle r; + char *mode; + Scribble *s = b->scrib; + char buf[32]; + + if (b->hidden) + return; + if(b->border > 0){ + r = insetrect(b->rect, b->border); + border(b->screen, b->rect, b->border, b->bordercolor->image, ZP); + }else + r = b->rect; + + i = b->image->image; + draw(b->screen, r, i, nil, i->r.min); + + if (s->ctrlShift) + mode = " ^C"; + else if (s->puncShift) + mode = " #&^"; + else if (s->curCharSet == CS_DIGITS) + mode = " 123"; + else if (s->capsLock) + mode = " ABC"; + else if (s->tmpShift) + mode = " Abc"; + else + mode = " abc"; + + snprint(buf, sizeof buf, "%s %s", mode, b->lastchar); + + string(b->screen, r.min, b->color->image, ZP, b->font->font, buf); + flushimage(display, 1); +} + +static void +scribctl(Control *c, CParse *cp) +{ + int cmd; + Rectangle r; + Scrib *b; + + b = (Scrib*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + abort(); + ctlerror("%q: unrecognized message '%s'", b->name, cp->str); + break; + case EAlign: + _ctlargcount(b, cp, 2); + b->align = _ctlalignment(cp->args[1]); + break; + case EBorder: + _ctlargcount(b, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: bad border: %c", b->name, cp->str); + b->border = cp->iargs[1]; + break; + case EBordercolor: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->bordercolor, cp->args[1]); + break; + case EFocus: + break; + case EImage: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->image, cp->args[1]); + break; + case ELinecolor: + _ctlargcount(b, cp, 2); + _setctlimage(b, &b->bordercolor, cp->args[1]); + break; + case ERect: + _ctlargcount(b, 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", b->name, cp->str); + b->rect = r; + break; + case EReveal: + _ctlargcount(b, cp, 1); + b->hidden = 0; + scribshow(b); + break; + case EShow: + _ctlargcount(b, cp, 1); + scribshow(b); + break; + case EFont: + _ctlargcount(b, cp, 2); + _setctlfont(b, &b->font, cp->args[1]); + break; + case EHide: + _ctlargcount(b, cp, 1); + b->hidden = 1; + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(b, 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", b->name, cp->str); + b->size.min = r.min; + b->size.max = r.max; + break; + } +} + +static void +resetstroke(Scrib *w) +{ + Scribble *s = w->scrib; + + s->ps.npts = 0; + scribshow(w); +} + +static void +displaystroke(Scrib *b) +{ + Scribble *s = b->scrib; + + poly(b->screen, s->pt, s->ps.npts, Endsquare, Endsquare, 0, b->color->image, ZP); + flushimage(display, 1); +} + +static void +displaylast(Scrib *w) +{ + int npt; + Scribble *s = w->scrib; + + npt = s->ps.npts; + if (npt > 2) + npt = 2; + poly(w->screen, s->pt + (s->ps.npts - npt), npt, Endsquare, Endsquare, + 0, w->color->image, ZP); + flushimage(display, 1); +} + +static void +addpoint(Scrib *w, Point p) +{ + pen_point *ppa; + Point *pt; + int ppasize; + Scribble *s = w->scrib; + + if (s->ps.npts == s->ppasize) { + ppasize = s->ppasize + 100; + ppa = malloc ((sizeof (pen_point) + sizeof (Point)) * ppasize); + if (!ppa) + return; + pt = (Point *) (ppa + ppasize); + memmove(ppa, s->ps.pts, s->ppasize * sizeof (pen_point)); + memmove(pt, s->pt, s->ppasize * sizeof (Point)); + free (s->ps.pts); + s->ps.pts = ppa; + s->pt = pt; + s->ppasize = ppasize; + } + ppa = &s->ps.pts[s->ps.npts]; + ppa->Point = p; + + pt = &s->pt[s->ps.npts]; + *pt = p; + + s->ps.npts++; + + displaylast(w); +} + +Control* +createscribble(Controlset *cs, char *name) +{ + Scrib *b; + + b = (Scrib*)_createctl(cs, "scribble", sizeof(Scrib), name); + b->image = _getctlimage("white"); + b->color = _getctlimage("black"); + b->bordercolor = _getctlimage("black"); + b->align = Aupperleft; + b->format = ctlstrdup("%q: value 0x%x"); + b->font = _getctlfont("font"); + b->scrib = scribblealloc(); + b->lastbut = 0; + b->bordercolor = _getctlimage("black"); + b->border = 0; + b->ctl = scribctl; + b->mouse = scribmouse; + b->exit = scribfree; + return (Control*)b; +} diff --git a/sys/src/libcontrol/slider.c b/sys/src/libcontrol/slider.c new file mode 100755 index 000000000..d7948cd2c --- /dev/null +++ b/sys/src/libcontrol/slider.c @@ -0,0 +1,326 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Slider Slider; + +struct Slider +{ + Control; + int border; + CImage *image; + CImage *textcolor; + CImage *bordercolor; + CImage *indicatorcolor; + int absolute; + int max; + int vis; + int value; + int clamphigh; + int clamplow; + int horizontal; + int lastbut; +}; + +enum{ + EAbsolute, + EBorder, + EBordercolor, + EClamp, + EFocus, + EFormat, + EHide, + EImage, + EIndicatorcolor, + EMax, + EOrient, + ERect, + EReveal, + EShow, + ESize, + EValue, + EVis, +}; + +static char *cmds[] = { + [EAbsolute] = "absolute", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EClamp] = "clamp", + [EFocus] = "focus", + [EFormat] = "format", + [EHide] = "hide", + [EImage] = "image", + [EIndicatorcolor] = "indicatorcolor", + [EMax] = "max", + [EOrient] = "orient", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + [EValue] = "value", + [EVis] = "vis", +}; + +static void +sliderfree(Control *c) +{ + Slider *s; + + s = (Slider*)c; + _putctlimage(s->image); + _putctlimage(s->textcolor); + _putctlimage(s->bordercolor); + _putctlimage(s->indicatorcolor); +} + +static void +slidershow(Slider *s) +{ + Rectangle r, t; + int l, h, d; + + if (s->hidden) + return; + r = s->rect; + draw(s->screen, r, s->image->image, nil, s->image->image->r.min); + if(s->border > 0){ + border(s->screen, r, s->border, s->bordercolor->image, s->bordercolor->image->r.min); + r = insetrect(r, s->border); + } + if(s->max <= 0) + return; + if(s->horizontal) + d = Dx(r); + else + d = Dy(r); + l = muldiv(s->value, d, s->max); + h = muldiv(s->value+s->vis, d, s->max); + if(s->clamplow && s->clamphigh){ + l = 0; + h = d; + }else if(s->clamplow){ + h = l; + l = 0; + }else if(s->clamphigh) + h = d; + t = r; + if(s->horizontal){ + r.max.x = r.min.x+h; + r.min.x += l; + }else{ + r.max.y = r.min.y+h; + r.min.y += l; + } + if(rectclip(&r, t)) + draw(s->screen, r, s->indicatorcolor->image, nil, s->indicatorcolor->image->r.min); + flushimage(display, 1); +} + +static void +sliderctl(Control *c, CParse *cp) +{ + int cmd, prev; + Rectangle r; + Slider *s; + + s = (Slider*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + default: + ctlerror("%q: unrecognized message '%s'", s->name, cp->str); + break; + case EAbsolute: + _ctlargcount(s, cp, 2); + s->absolute = cp->iargs[1]; + break; + case EBorder: + _ctlargcount(s, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: bad border: %c", s->name, cp->str); + s->border = cp->iargs[1]; + break; + case EBordercolor: + _ctlargcount(s, cp, 2); + _setctlimage(s, &s->bordercolor, cp->args[1]); + break; + case EClamp: + _ctlargcount(s, cp, 3); + if(strcmp(cp->args[1], "high") == 0) + s->clamphigh = cp->iargs[2]; + else if(strcmp(cp->args[1], "low") == 0) + s->clamplow = cp->iargs[2]; + else + ctlerror("%q: unrecognized clamp: %s", s->name, cp->str); + break; + case EFocus: + /* ignore focus change */ + break; + case EFormat: + _ctlargcount(s, cp, 2); + s->format = ctlstrdup(cp->args[1]); + break; + case EHide: + _ctlargcount(s, cp, 1); + s->hidden = 1; + break; + case EImage: + _ctlargcount(s, cp, 2); + _setctlimage(s, &s->image, cp->args[1]); + break; + case EIndicatorcolor: + _ctlargcount(s, cp, 2); + _setctlimage(s, &s->indicatorcolor, cp->args[1]); + break; + case EMax: + _ctlargcount(s, cp, 2); + if(cp->iargs[1] < 0) + ctlerror("%q: negative max value: %s", s->name, cp->str); + if(s->max != cp->iargs[1]){ + s->max = cp->iargs[1]; + slidershow(s); + } + break; + case EOrient: + _ctlargcount(s, cp, 2); + prev = s->horizontal; + if(strncmp(cp->args[1], "hor", 3) == 0) + s->horizontal = 1; + else if(strncmp(cp->args[1], "ver", 3) == 0) + s->horizontal = 0; + else + ctlerror("%q: unrecognized orientation: %s", s->name, cp->str); + if(s->horizontal != prev) + slidershow(s); + break; + case ERect: + _ctlargcount(s, 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", s->name, cp->str); + s->rect = r; + break; + case EReveal: + _ctlargcount(s, cp, 1); + s->hidden = 0; + slidershow(s); + break; + case EShow: + _ctlargcount(s, cp, 1); + slidershow(s); + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + else{ + _ctlargcount(s, 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", s->name, cp->str); + s->size.min = r.min; + s->size.max = r.max; + break; + case EValue: + _ctlargcount(s, cp, 2); + if(s->value != cp->iargs[1]){ + s->value = cp->iargs[1]; + slidershow(s); + } + break; + case EVis: + _ctlargcount(s, cp, 2); + if(s->vis != cp->iargs[1]){ + s->vis = cp->iargs[1]; + slidershow(s); + } + break; + } +} + +static void +slidermouse(Control *c, Mouse *m) +{ + Rectangle r; + int v, l, d, b; + Slider *s; + + s =(Slider*)c; + if(m->buttons == 0){ + /* buttons now up */ + s->lastbut = 0; + return; + } + if(!s->absolute && s->lastbut==m->buttons && s->lastbut!=2){ + /* clicks only on buttons 1 & 3; continuous motion on 2 (or when absolute) */ + return; + } + if(s->lastbut!=0 && m->buttons!=s->lastbut){ + /* buttons down have changed; wait for button up */ + return; + } + s->lastbut = m->buttons; + + r = insetrect(s->rect, s->border); + if(s->horizontal){ + v = m->xy.x - r.min.x; + d = Dx(r); + }else{ + v = m->xy.y - r.min.y; + d = Dy(r); + } + if(s->absolute) + b = 2; + else + b = m->buttons; + switch(b){ + default: + return; + case 1: + l = s->value - muldiv(v, s->vis, d); + break; + case 2: + l = muldiv(v, s->max, d); + break; + case 4: + l = s->value + muldiv(v, s->vis, d); + break; + } + if(l < 0) + l = 0; + if(l > s->max) + l = s->max; + if(l != s->value){ + s->value = l; + chanprint(s->event, s->format, s->name, s->value); + slidershow(s); + } +} + +Control* +createslider(Controlset *cs, char *name) +{ + Slider *s; + + s = (Slider*)_createctl(cs, "slider", sizeof(Slider), name); + s->image = _getctlimage("white"); + s->textcolor = _getctlimage("black"); + s->bordercolor = _getctlimage("black"); + s->indicatorcolor = _getctlimage("black"); + s->format = ctlstrdup("%q: value %d"); + s->border = 0; + s->mouse = slidermouse; + s->ctl = sliderctl; + s->exit = sliderfree; + return (Control*)s; +} diff --git a/sys/src/libcontrol/tabs.c b/sys/src/libcontrol/tabs.c new file mode 100755 index 000000000..41737c2f4 --- /dev/null +++ b/sys/src/libcontrol/tabs.c @@ -0,0 +1,256 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> +#include "group.h" + +typedef struct Tab Tab; + +struct Tab { + Control; + int border; + int selected; + int separation; + char *format; + CImage *bordercolor; + CImage *image; + Control *tabrow; + Control *tabstack; + Control *tabcolumn; + int ntabs; + int nbuttons; + Control **buttons; +}; + +enum{ + EAdd, + EBorder, + EBordercolor, + EButton, + EFocus, + EFormat, + EHide, + EImage, + ERect, + EReveal, + ESeparation, + ESeparatorcolor, + EShow, + ESize, + EValue, +}; + +static char *cmds[] = { + [EAdd] = "add", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EButton] = "button", + [EFocus] = "focus", + [EFormat] = "format", + [EHide] = "hide", + [EImage] = "image", + [ERect] = "rect", + [EReveal] = "reveal", + [ESeparation] = "separation", + [ESeparatorcolor] = "separatorcolor", + [EShow] = "show", + [ESize] = "size", + [EValue] = "value", +}; + +static void +tabshow(Tab *t) +{ + int i; + Rectangle r; + Group *g; + + if (t->hidden) + return; + for(i=0; i<t->nbuttons; i++){ + _ctlprint(t->buttons[i], "value %d", (t->selected==i)); + } + _ctlprint(t->tabstack, "reveal %d", t->selected); + _ctlprint(t->tabcolumn, "show"); + if (t->selected < 0) + return; + g = (Group*)t->tabcolumn; + if (g->nseparators == 0){ + return; + } + r = g->separators[0]; + r.min.x = t->buttons[t->selected]->rect.min.x; + r.max.x = t->buttons[t->selected]->rect.max.x; + draw(t->screen, r, t->image->image, nil, t->image->image->r.min); + flushimage(display, 1); +} + +static void +tabsize(Control *c) +{ + Tab *t = (Tab*)c; + if (t->tabcolumn && t->tabcolumn->setsize) + t->tabcolumn->setsize((Control*)t->tabcolumn); +} + +static void +tabctl(Control *c, CParse *cp) +{ + int cmd, i; + Control *cbut, *cwin; + Tab *t; + Rectangle r; + + t = (Tab*)c; + cmd = _ctllookup(cp->args[0], cmds, nelem(cmds)); + switch(cmd){ + case EAdd: + if ((cp->nargs & 1) == 0) + ctlerror("%q: arg count: %s", t->name, cp->args[1]); + for (i = 1; i < cp->nargs; i += 2){ + cbut = controlcalled(cp->args[i]); + if (cbut == nil) + ctlerror("%q: no such control: %s", t->name, cp->args[i]); + cwin = controlcalled(cp->args[i+1]); + if (cwin == nil) + ctlerror("%q: no such control: %s", t->name, cp->args[i+1]); + _ctladdgroup(t->tabrow, cbut); + _ctlprint(t->tabstack, "add %q", cp->args[i+1]); + _ctlprint(cbut, "format '%%s: %q button %%d'", t->name); + controlwire(cbut, "event", t->controlset->ctl); + t->buttons = ctlrealloc(t->buttons, (t->nbuttons+1)*sizeof(Control*)); + t->buttons[t->nbuttons] = cbut; + t->nbuttons++; + t->selected = -1; + } + _ctlprint(t->tabcolumn, "size"); + t->size = t->tabcolumn->size; + 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 EButton: + _ctlargcount(t, cp, 2); + if (cp->sender == nil) + ctlerror("%q: senderless buttonevent: %q", t->name, cp->str); + cbut = controlcalled(cp->sender); + for(i=0; i<t->nbuttons; i++) + if (cbut == t->buttons[i]) + break; + if (i == t->nbuttons) + ctlerror("%q: not my event: %q", t->name, cp->str); + if(cp->iargs[1] == 0){ + /* button was turned off; turn it back on */ + _ctlprint(cbut, "value 1"); + }else{ + t->selected = i; + if (t->format) + chanprint(t->event, t->format, t->name, i); + tabshow(t); + } + break; + case EFocus: + /* ignore focus change */ + break; + case EFormat: + _ctlargcount(t, cp, 2); + t->format = ctlstrdup(cp->args[1]); + break; + case EImage: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->image, cp->args[1]); + break; + case ESeparation: + t->tabrow->ctl(t->tabrow, cp); + t->tabcolumn->ctl(t->tabcolumn, cp); + 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; + r = insetrect(r, t->border); + _ctlprint(t->tabcolumn, "rect %R", r); + break; + case EReveal: + _ctlargcount(t, cp, 1); + case EHide: + case ESize: + t->tabcolumn->ctl(t->tabcolumn, cp); + break; + case EShow: + tabshow(t); + break; + case EValue: + _ctlargcount(t, cp, 2); + if (cp->iargs[1] < 0 || cp->iargs[1] >= t->nbuttons) + ctlerror("%q: illegal value '%s'", t->name, cp->str); + t->selected = cp->iargs[1]; + tabshow(t); + break; + default: + ctlerror("%q: unrecognized message '%s'", t->name, cp->str); + break; + } +} + +static void +tabfree(Control*c) +{ + Tab *t = (Tab*)c; + t->nbuttons = 0; + free(t->buttons); + t->buttons = 0; +} + +static void +activatetab(Control *c, int act) +{ + Tab *t; + + t = (Tab*)c; + if (act) + activate(t->tabcolumn); + else + deactivate(t->tabcolumn); +} + +Control * +createtab(Controlset *cs, char *name) +{ + char s[128]; + + Tab *t; + t = (Tab*)_createctl(cs, "tab", sizeof(Tab), name); + t->selected = -1; + t->nbuttons = 0; + t->ctl = tabctl; + t->mouse = nil; + t->exit = tabfree; + snprint(s, sizeof s, "_%s-row", name); + t->tabrow = createrow(cs, s); + snprint(s, sizeof s, "_%s-stack", name); + t->tabstack = createstack(cs, s); + snprint(s, sizeof s, "_%s-column", name); + t->tabcolumn = createcolumn(cs, s); + ctlprint(t->tabcolumn, "add %q %q", t->tabrow->name, t->tabstack->name); + t->bordercolor = _getctlimage("black"); + t->image = _getctlimage("white"); + t->setsize = tabsize; + t->activate = activatetab; + return (Control*)t; +} diff --git a/sys/src/libcontrol/text.c b/sys/src/libcontrol/text.c new file mode 100755 index 000000000..7c3d9f86a --- /dev/null +++ b/sys/src/libcontrol/text.c @@ -0,0 +1,542 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +static int debug = 0; + +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; + CImage *selectingcolor; + Rune **line; + int selectmode; // Selsingle, Selmulti + int selectstyle; // Seldown, Selup (use Selup only with Selsingle) + uchar *selected; + int nline; + int warp; + int align; + int sel; // line nr of selection made by last button down + int but; // last button down (still being hold) + int offsel; // we are on selection +}; + +enum +{ + Selsingle, + Selmulti, + Seldown, + Selup, +}; + +enum{ + EAccumulate, + EAdd, + EAlign, + EBorder, + EBordercolor, + EClear, + EDelete, + EFocus, + EFont, + EHide, + EImage, + ERect, + EReplace, + EReveal, + EScroll, + ESelect, + ESelectcolor, + ESelectingcolor, + ESelectmode, + ESelectstyle, + EShow, + ESize, + ETextcolor, + ETopline, + EValue, + EWarp, +}; + +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", + [ESelectingcolor] = "selectingcolor", + [ESelectmode] = "selectmode", + [ESelectstyle] = "selectstyle", + [EShow] = "show", + [ESize] = "size", + [ETextcolor] = "textcolor", + [ETopline] = "topline", + [EValue] = "value", + [EWarp] = "warp", + nil +}; + +static void textshow(Text*); +static void texttogglei(Text*, int); +static int textline(Text*, Point); +static int texttoggle(Text*, Point); + +static void +textmouse(Control *c, Mouse *m) +{ + Text *t; + int sel; + + t = (Text*)c; + if (debug) fprint(2, "textmouse %s t->lastbut %d; m->buttons %d\n", t->name, t->lastbut, m->buttons); + if (t->warp >= 0) + return; + if ((t->selectstyle == Selup) && (m->buttons&7)) { + sel = textline(t, m->xy); + if (t->sel >= 0) { +// if (debug) fprint(2, "textmouse Selup %q sel=%d t->sel=%d t->but=%d\n", +// t->name, sel, t->sel, t->but); + t->offsel = (sel == t->sel) ? 0 : 1; + if ((sel == t->sel && + ((t->selected[t->sel] && !t->but) || + ((!t->selected[t->sel]) && t->but))) || + (sel != t->sel && + ((t->selected[t->sel] && t->but) || + ((!t->selected[t->sel]) && (!t->but))))) { + texttogglei(t, t->sel); + } + } + } + if(t->lastbut != (m->buttons&7)){ + if(m->buttons & 7){ + sel = texttoggle(t, m->xy); + if(sel >= 0) { + if (t->selectstyle == Seldown) { + chanprint(t->event, "%q: select %d %d", + t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0); + if (debug) fprint(2, "textmouse Seldown event %q: select %d %d\n", + t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0); + } else { + if (debug) fprint(2, "textmouse Selup no event yet %q: select %d %d\n", + t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0); + t->sel = sel; + t->but = t->selected[sel] ? (m->buttons & 7) : 0; + } + } + } else if (t->selectstyle == Selup) { + sel = textline(t, m->xy); + t->offsel = 0; + if ((sel >= 0) && (sel == t->sel)) { + chanprint(t->event, "%q: select %d %d", + t->name, sel, t->but); + if (debug) fprint(2, "textmouse Selup event %q: select %d %d\n", + t->name, sel, t->but); + } else if (sel != t->sel) { + if ((t->selected[t->sel] && t->but) || + ((!t->selected[t->sel]) && (!t->but))) { + texttogglei(t, t->sel); + } else { + textshow(t); + } + if (debug) fprint(2, "textmouse Selup cancel %q: select %d %d\n", + t->name, sel, t->but); + } + t->sel = -1; + t->but = 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); + _putctlimage(t->selectingcolor); + 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->sel == i && t->offsel) + draw(t->screen, r, t->selectingcolor->image, nil, ZP); + else if(t->selected[i]) + draw(t->screen, r, t->selectcolor->image, nil, ZP); + p = _ctlalignpoint(r, + runestringnwidth(f, text, ntext), + f->height, t->align); + if(t->warp == i) { + Point p2; + p2.x = p.x + 0.5*runestringnwidth(f, text, ntext); + p2.y = p.y + 0.5*f->height; + moveto(t->controlset->mousectl, p2); + t->warp = -1; + } + _string(t->screen, p, t->textcolor->image, + ZP, f, nil, text, ntext, tr, + nil, ZP, SoverD); + 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 ESelectstyle: + _ctlargcount(t, cp, 2); + if(strcmp(cp->args[1], "down") == 0) + t->selectstyle = Seldown; + else if(strcmp(cp->args[1], "up") == 0) + t->selectstyle = Selup; + 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; + case EWarp: + _ctlargcount(t, cp, 2); + i = cp->iargs[1]; + if(i <0 || i>=t->nline) + ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str); + if(i < t->topline || i >= t->topline+t->nvis){ + t->topline = i; + } + t->warp = cp->iargs[1]; + textshow(t); + t->warp = -1; + 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 +textline(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; + return i; +} + +static int +texttoggle(Text *t, Point p) +{ + int i; + + i = textline(t, p); + if (i >= 0) + 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->selectingcolor = _getctlimage("paleyellow"); + t->font = _getctlfont("font"); + t->selectmode = Selsingle; + t->selectstyle = Selup; // Seldown; + t->lastbut = 0; + t->mouse = textmouse; + t->ctl = textctl; + t->exit = textfree; + t->warp = -1; + t->sel = -1; + t->offsel = 0; + t->but = 0; + return (Control *)t; +} diff --git a/sys/src/libcontrol/textbutton.c b/sys/src/libcontrol/textbutton.c new file mode 100755 index 000000000..9f1730762 --- /dev/null +++ b/sys/src/libcontrol/textbutton.c @@ -0,0 +1,344 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Textbutton Textbutton; + +struct Textbutton +{ + Control; + CFont *font; + CImage *image; + CImage *mask; + CImage *light; + CImage *bordercolor; + CImage *textcolor; + CImage *pressedtextcolor; + CImage *paletextcolor; + int pressed; + int lastbut; + int lastshow; + char **line; + int nline; + int align; + int border; + int off; + int showoff; + int prepress; +}; + +enum{ + EAlign, + EBorder, + EBordercolor, + EFocus, + EFont, + EFormat, + EHide, + EImage, + ELight, + EMask, + EPaletextcolor, + EPressedtextcolor, + ERect, + EReveal, + EShow, + ESize, + EText, + ETextcolor, + EValue, +}; + +static char *cmds[] = { + [EAlign] = "align", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EFocus] = "focus", + [EFont] = "font", + [EFormat] = "format", + [EHide] = "hide", + [EImage] = "image", + [ELight] = "light", + [EMask] = "mask", + [EPaletextcolor] ="paletextcolor", + [EPressedtextcolor] ="pressedtextcolor", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + [EText] = "text", + [ETextcolor] = "textcolor", + [EValue] = "value", + nil +}; + +static void textbuttonshow(Textbutton*); + +static void +textbuttonmouse(Control *c, Mouse *m) +{ + Textbutton *t; + + t = (Textbutton *)c; + if(m->buttons&7) { + if (ptinrect(m->xy,t->rect)) { + if (t->off) { + t->off = 0; + textbuttonshow(t); + } + } else { + if (!t->off) { + t->off = 1; + textbuttonshow(t); + } + } + } + if((m->buttons&7) != t->lastbut){ + if(m->buttons & 7){ + t->prepress = t->pressed; + if (t->pressed) + t->pressed = 0; + else + t->pressed = 1; + textbuttonshow(t); + }else{ /* generate event on button up */ + if (ptinrect(m->xy,t->rect)) + chanprint(t->event, t->format, t->name, t->pressed); + else { + t->off = 0; + t->pressed = t->prepress; + textbuttonshow(t); + } + } + } + t->lastbut = m->buttons & 7; +} + +static void +textbuttonfree(Control *c) +{ + int i; + Textbutton *t; + + t = (Textbutton*)c; + _putctlfont(t->font); + _putctlimage(t->image); + _putctlimage(t->light); + _putctlimage(t->mask); + _putctlimage(t->textcolor); + _putctlimage(t->bordercolor); + _putctlimage(t->paletextcolor); + _putctlimage(t->pressedtextcolor); + for(i=0; i<t->nline; i++) + free(t->line[i]); + free(t->line); +} + +static void +textbuttonshow(Textbutton *t) +{ + Rectangle r, clipr; + int i, dx, dy, w; + Font *f; + Point p, q; + Image *im; + + if(t->hidden || (t->lastshow == t->pressed && t->showoff == t->off)) + return; + f = t->font->font; + draw(t->screen, t->rect, t->image->image, nil, t->image->image->r.min); + if(t->border > 0) + border(t->screen, t->rect, t->border, t->bordercolor->image, ZP); + /* text goes here */ + dx = 0; + for(i=0; i<t->nline; i++){ + w = stringwidth(f, t->line[i]); + if(dx < w) + dx = w; + } + dy = t->nline*f->height; + clipr = insetrect(t->rect, t->border); + p = _ctlalignpoint(clipr, dx, dy, t->align); + im = t->textcolor->image; + if(t->off) + im = t->paletextcolor->image; + else if(t->pressed) + im = t->pressedtextcolor->image; + for(i=0; i<t->nline; i++){ + r.min = p; + r.max.x = p.x+dx; + r.max.y = p.y+f->height; + q = _ctlalignpoint(r, stringwidth(f, t->line[i]), f->height, t->align%3); + _string(t->screen, q, im, + ZP, f, t->line[i], nil, strlen(t->line[i]), + clipr, nil, ZP, SoverD); + p.y += f->height; + } + if(t->off || t->pressed) + draw(t->screen, t->rect, t->light->image, t->mask->image, t->mask->image->r.min); + t->lastshow = t->pressed; + t->showoff = t->off; + flushimage(display, 1); +} + +static void +textbuttonctl(Control *c, CParse *cp) +{ + int cmd, i; + Rectangle r; + Textbutton *t; + + t = (Textbutton*)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]); + t->lastshow = -1; /* force redraw */ + break; + case EBorder: + _ctlargcount(t, cp, 2); + t->border = cp->iargs[1]; + t->lastshow = -1; /* force redraw */ + break; + case EBordercolor: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->bordercolor, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EFocus: + break; + case EFont: + _ctlargcount(t, cp, 2); + _setctlfont(t, &t->font, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EFormat: + _ctlargcount(t, cp, 2); + t->format = ctlstrdup(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]); + t->lastshow = -1; /* force redraw */ + break; + case ELight: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->light, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EMask: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->mask, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EPaletextcolor: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->paletextcolor, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EPressedtextcolor: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->pressedtextcolor, cp->args[1]); + t->lastshow = -1; /* force redraw */ + 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->lastshow = -1; /* force redraw */ + break; + case EReveal: + _ctlargcount(t, cp, 1); + t->hidden = 0; + t->lastshow = -1; /* force redraw */ + textbuttonshow(t); + break; + case EShow: + _ctlargcount(t, cp, 1); + t->lastshow = -1; /* force redraw */ + textbuttonshow(t); + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + 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 EText: + /* free existing text */ + for(i=0; i<t->nline; i++) + free(t->line[i]); + t->nline = cp->nargs-1; + t->line = ctlrealloc(t->line, t->nline*sizeof(char*)); + for(i=0; i<t->nline; i++) + t->line[i] = ctlstrdup(cp->args[i+1]); + t->lastshow = -1; /* force redraw */ + textbuttonshow(t); + break; + case ETextcolor: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->textcolor, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EValue: + _ctlargcount(t, cp, 2); + if((cp->iargs[1]!=0) != t->pressed){ + t->pressed ^= 1; + textbuttonshow(t); + } + break; + } +} + +Control* +createtextbutton(Controlset *cs, char *name) +{ + Textbutton *t; + + t = (Textbutton *)_createctl(cs, "textbutton", sizeof(Textbutton), name); + t->line = ctlmalloc(sizeof(char*)); + t->nline = 0; + t->image = _getctlimage("white"); + t->light = _getctlimage("yellow"); + t->mask = _getctlimage("opaque"); + t->bordercolor = _getctlimage("black"); + t->textcolor = _getctlimage("black"); + t->pressedtextcolor = _getctlimage("black"); + t->paletextcolor = _getctlimage("paleyellow"); + t->font = _getctlfont("font"); + t->format = ctlstrdup("%q: value %d"); + t->lastshow = -1; + t->mouse = textbuttonmouse; + t->ctl = textbuttonctl; + t->exit = textbuttonfree; + t->prepress = 0; + t->off = 0; + t->showoff = -1; + return (Control *)t; +} diff --git a/sys/src/libcontrol/textbutton3.c b/sys/src/libcontrol/textbutton3.c new file mode 100755 index 000000000..1a3cd1307 --- /dev/null +++ b/sys/src/libcontrol/textbutton3.c @@ -0,0 +1,393 @@ +/* use button 3 for a proper function to the application, that is not for plumber + * as default control(2) supposes. + * codes are mostly from /sys/src/libcontrol/textbutton.c + */ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <thread.h> +#include <mouse.h> +#include <keyboard.h> +#include <control.h> + +typedef struct Textbutton3 Textbutton3; + +struct Textbutton3 +{ + Control; + CFont *font; + CImage *image; + CImage *mask; + CImage *light; + CImage *bordercolor; + CImage *textcolor; + CImage *pressedtextcolor; + int pressed; + int lastbut; + int lastshow; + char **line; + int nline; + int align; + int border; + int left; + int middle; + int right; + int toggle; + int gettextflg; +}; + +enum{ + EAlign, + EBorder, + EBordercolor, + EFocus, + EFont, + EFormat, + EHide, + EImage, + ELight, + EMask, + EPressedtextcolor, + ERect, + EReveal, + EShow, + ESize, + EText, + ETextcolor, + EEnable, + EDisable, + EToggle, + EGettext, + EValue, +}; + +static char *cmds[] = { + [EAlign] = "align", + [EBorder] = "border", + [EBordercolor] = "bordercolor", + [EFocus] = "focus", + [EFont] = "font", + [EFormat] = "format", + [EHide] = "hide", + [EImage] = "image", + [ELight] = "light", + [EMask] = "mask", + [EPressedtextcolor] ="pressedtextcolor", + [ERect] = "rect", + [EReveal] = "reveal", + [EShow] = "show", + [ESize] = "size", + [EText] = "text", + [ETextcolor] = "textcolor", + [EEnable] = "enable", + [EDisable] = "disable", + [EToggle] = "toggle", + [EGettext] = "gettext", + [EValue] = "value", + nil +}; + +static void textbutton3show(Textbutton3 *); + +static void +textbutton3mouse(Control *c, Mouse *m) +{ + Textbutton3 *t; + + t = (Textbutton3 *)c; + if(t->left == 1) { + if((m->buttons&1) == 1 && (t->lastbut&1) == 0){ + t->pressed ^= 1; + textbutton3show(t); + t->lastbut = m->buttons & 1; + }else if((m->buttons&1) == 0 && (t->lastbut&1) == 1){ + if(t->gettextflg == 0) + chanprint(t->event, t->format, t->name, t->pressed, m->xy.x, m->xy.y); + else + chanprint(t->event, "%q: value %q", t->name, t->line[0]); + t->pressed ^= 1; + textbutton3show(t); + t->lastbut = m->buttons & 1; + } + } + if(t->middle == 1) { + if((m->buttons&2) == 2 && (t->lastbut&2) == 0){ + t->pressed ^= 2; + textbutton3show(t); + t->lastbut = m->buttons & 2; + }else if((m->buttons&2) == 0 && (t->lastbut&2) == 2){ + if(t->gettextflg == 0) + chanprint(t->event, t->format, t->name, t->pressed, m->xy.x, m->xy.y); + else + chanprint(t->event, "%q: value %q", t->name, t->line[0]); + t->pressed ^= 2; + textbutton3show(t); + t->lastbut = m->buttons & 2; + } + } + if(t->right == 1) { + if((m->buttons&4) == 4 && (t->lastbut&4) == 0){ + t->pressed ^= 4; + textbutton3show(t); + t->lastbut = m->buttons & 4; + }else if((m->buttons&4) == 0 && (t->lastbut&4) == 4){ + if(t->gettextflg == 0) + chanprint(t->event, t->format, t->name, t->pressed, m->xy.x, m->xy.y); + else + chanprint(t->event, "%q: value %q", t->name, t->line[0]); + t->pressed ^= 4; + textbutton3show(t); + t->lastbut = m->buttons & 4; + } + } +} + +static void +textbutton3free(Control *c) +{ + int i; + Textbutton3 *t; + + t = (Textbutton3*)c; + _putctlfont(t->font); + _putctlimage(t->image); + _putctlimage(t->light); + _putctlimage(t->mask); + _putctlimage(t->textcolor); + _putctlimage(t->bordercolor); + _putctlimage(t->pressedtextcolor); + for(i=0; i<t->nline; i++) + free(t->line[i]); + free(t->line); +} + +static void +textbutton3show(Textbutton3 *t) +{ + Rectangle r, clipr; + int i, dx, dy, w; + Font *f; + Point p, q; + Image *im; + + if(t->hidden || t->lastshow == t->pressed) + return; + f = t->font->font; + draw(t->screen, t->rect, t->image->image, nil, t->image->image->r.min); + if(t->pressed || t->toggle) + draw(t->screen, t->rect, t->light->image, t->mask->image, t->mask->image->r.min); + if(t->border > 0) + border(t->screen, t->rect, t->border, t->bordercolor->image, ZP); + /* text goes here */ + dx = 0; + for(i=0; i<t->nline; i++){ + w = stringwidth(f, t->line[i]); /*****/ + if(dx < w) + dx = w; + } + dy = t->nline*f->height; + clipr = insetrect(t->rect, t->border); + p = _ctlalignpoint(clipr, dx, dy, t->align); + im = t->textcolor->image; + if(t->pressed) + im = t->pressedtextcolor->image; + for(i=0; i<t->nline; i++){ + r.min = p; + r.max.x = p.x+dx; + r.max.y = p.y+f->height; + q = _ctlalignpoint(r, stringwidth(f, t->line[i]), f->height, t->align%3); + _string(t->screen, q, im, + ZP, f, t->line[i], nil, strlen(t->line[i]), + clipr, nil, ZP, SoverD); + p.y += f->height; + } + t->lastshow = t->pressed; + flushimage(display, 1); +} + +static void +textbutton3ctl(Control *c, CParse *cp) +{ + int cmd, i; + Rectangle r; + Textbutton3 *t; + + t = (Textbutton3*)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]); + t->lastshow = -1; /* force redraw */ + break; + case EBorder: + _ctlargcount(t, cp, 2); + t->border = cp->iargs[1]; + t->lastshow = -1; /* force redraw */ + break; + case EBordercolor: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->bordercolor, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EFocus: + break; + case EFont: + _ctlargcount(t, cp, 2); + _setctlfont(t, &t->font, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EFormat: + _ctlargcount(t, cp, 2); + t->format = ctlstrdup(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]); + t->lastshow = -1; /* force redraw */ + break; + case ELight: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->light, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EMask: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->mask, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EPressedtextcolor: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->pressedtextcolor, cp->args[1]); + t->lastshow = -1; /* force redraw */ + 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->lastshow = -1; /* force redraw */ + break; + case EReveal: + _ctlargcount(t, cp, 1); + t->hidden = 0; + t->lastshow = -1; /* force redraw */ + textbutton3show(t); + break; + case EShow: + _ctlargcount(t, cp, 1); + t->lastshow = -1; /* force redraw */ + textbutton3show(t); + break; + case ESize: + if (cp->nargs == 3) + r.max = Pt(0x7fffffff, 0x7fffffff); + 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 EText: + /* free existing text */ + for(i=0; i<t->nline; i++) + free(t->line[i]); + t->nline = cp->nargs-1; + t->line = ctlrealloc(t->line, t->nline*sizeof(char*)); + for(i=0; i<t->nline; i++) + t->line[i] = ctlstrdup(cp->args[i+1]); + t->lastshow = -1; /* force redraw */ + textbutton3show(t); + break; + case ETextcolor: + _ctlargcount(t, cp, 2); + _setctlimage(t, &t->textcolor, cp->args[1]); + t->lastshow = -1; /* force redraw */ + break; + case EEnable: + _ctlargcount(t, cp, 2); + if(strcmp(cp->args[1], "left") == 0) + t->left = 1; + else if(strcmp(cp->args[1], "middle") == 0) + t->middle = 1; + else if(strcmp(cp->args[1], "right") == 0) + t->right = 1; + break; + case EDisable: + _ctlargcount(t, cp, 2); + if(strcmp(cp->args[1], "left") == 0) + t->left = 0; + else if(strcmp(cp->args[1], "middle") == 0) + t->middle = 0; + else if(strcmp(cp->args[1], "right") == 0) + t->right = 0; + break; + case EToggle: + _ctlargcount(t, cp, 2); + if(strcmp(cp->args[1], "on") == 0) + t->toggle = 1; + else if(strcmp(cp->args[1], "off") == 0) + t->toggle = 0; + t->lastshow = -1; /* force redraw */ + break; + case EGettext: + _ctlargcount(t, cp, 2); + if(strcmp(cp->args[1], "on") == 0) + t->gettextflg = 1; + else if(strcmp(cp->args[1], "off") == 0) + t->gettextflg = 0; + break; + case EValue: + _ctlargcount(t, cp, 2); + if((cp->iargs[1]!=0) != t->pressed){ + t->pressed ^= 1; + textbutton3show(t); + } + break; + } +} + +Control* +createtextbutton3(Controlset *cs, char *name) +{ + Textbutton3 *t; + + t = (Textbutton3 *)_createctl(cs, "textbutton3", sizeof(Textbutton3), name); + t->line = ctlmalloc(sizeof(char*)); + t->nline = 0; + t->image = _getctlimage("white"); + t->light = _getctlimage("yellow"); + t->mask = _getctlimage("opaque"); + t->bordercolor = _getctlimage("black"); + t->textcolor = _getctlimage("black"); + t->pressedtextcolor = _getctlimage("black"); + t->font = _getctlfont("font"); + t->format = ctlstrdup("%q: value %d %d %d"); + t->lastshow = -1; + t->mouse = textbutton3mouse; + t->ctl = textbutton3ctl; + t->exit = textbutton3free; + t->left = 1; + t->middle = 1; + t->right = 1; + t->toggle = 0; + t->gettextflg = 0; + return (Control *)t; +} |