summaryrefslogtreecommitdiff
path: root/libtk/label.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtk/label.c')
-rw-r--r--libtk/label.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/libtk/label.c b/libtk/label.c
new file mode 100644
index 00000000..79d6344a
--- /dev/null
+++ b/libtk/label.c
@@ -0,0 +1,539 @@
+#include <lib9.h>
+#include <kernel.h>
+#include "draw.h"
+#include "tk.h"
+#include "label.h"
+
+#define O(t, e) ((long)(&((t*)0)->e))
+
+/* Layout constants */
+enum {
+ CheckSpace = CheckButton + 2*CheckButtonBW + 2*ButtonBorder,
+};
+
+TkOption tklabelopts[] =
+{
+ "text", OPTtext, O(TkLabel, text), nil,
+ "label", OPTtext, O(TkLabel, text), nil,
+ "underline", OPTdist, O(TkLabel, ul), nil,
+ "justify", OPTflag, O(TkLabel, justify), tkjustify,
+ "anchor", OPTflag, O(TkLabel, anchor), tkanchor,
+ "bitmap", OPTbmap, O(TkLabel, bitmap), nil,
+ "image", OPTimag, O(TkLabel, img), nil,
+ nil
+};
+
+char*
+tklabel(TkTop *t, char *arg, char **ret)
+{
+ Tk *tk;
+ char *e;
+ TkLabel *tkl;
+ TkName *names;
+ TkOptab tko[3];
+
+ tk = tknewobj(t, TKlabel, sizeof(Tk)+sizeof(TkLabel));
+ if(tk == nil)
+ return TkNomem;
+
+ tkl = TKobj(TkLabel, tk);
+ tkl->ul = -1;
+ tkl->justify = Tkleft;
+
+ tko[0].ptr = tk;
+ tko[0].optab = tkgeneric;
+ tko[1].ptr = tkl;
+ tko[1].optab = tklabelopts;
+ tko[2].ptr = nil;
+
+ names = nil;
+ e = tkparse(t, arg, tko, &names);
+ if(e != nil) {
+ tkfreeobj(tk);
+ return e;
+ }
+
+ tksizelabel(tk);
+ tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
+
+ e = tkaddchild(t, tk, &names);
+ tkfreename(names);
+ if(e != nil) {
+ tkfreeobj(tk);
+ return e;
+ }
+ tk->name->link = nil;
+
+ return tkvalue(ret, "%s", tk->name->name);
+}
+
+static char*
+tklabelcget(Tk *tk, char *arg, char **val)
+{
+ TkOptab tko[3];
+ TkLabel *tkl = TKobj(TkLabel, tk);
+
+ tko[0].ptr = tk;
+ tko[0].optab = tkgeneric;
+ tko[1].ptr = tkl;
+ tko[1].optab = tklabelopts;
+ tko[2].ptr = nil;
+
+ return tkgencget(tko, arg, val, tk->env->top);
+}
+
+static char*
+tklabelconf(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkGeom g;
+ int bd;
+ TkOptab tko[3];
+ TkLabel *tkl = TKobj(TkLabel, tk);
+
+ tko[0].ptr = tk;
+ tko[0].optab = tkgeneric;
+ tko[1].ptr = tkl;
+ tko[1].optab = tklabelopts;
+ tko[2].ptr = nil;
+
+ if(*arg == '\0')
+ return tkconflist(tko, val);
+
+ g = tk->req;
+ bd = tk->borderwidth;
+ e = tkparse(tk->env->top, arg, tko, nil);
+ tksizelabel(tk);
+ tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
+ tkgeomchg(tk, &g, bd);
+
+ tk->dirty = tkrect(tk, 1);
+ return e;
+}
+
+void
+tksizelabel(Tk *tk)
+{
+ Point p;
+ int w, h;
+ TkLabel *tkl;
+
+ tkl = TKobj(TkLabel, tk);
+ if(tkl->anchor == 0)
+ tkl->anchor = Tkcenter;
+
+ w = 0;
+ h = 0;
+ tkl->textheight = 0;
+ if(tkl->img != nil) {
+ w = tkl->img->w + 2*Bitpadx;
+ h = tkl->img->h + 2*Bitpady;
+ }
+ else
+ if(tkl->bitmap != nil) {
+ w = Dx(tkl->bitmap->r) + 2*Bitpadx;
+ h = Dy(tkl->bitmap->r) + 2*Bitpady;
+ }
+ else
+ if(tkl->text != nil) {
+ p = tkstringsize(tk, tkl->text);
+ w = p.x + 2*Textpadx;
+ h = p.y + 2*Textpady;
+ if(tkl->ul != -1 && tkl->ul > strlen(tkl->text))
+ tkl->ul = -1;
+ tkl->textheight = p.y;
+ }
+
+ if((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator != BoolF) {
+ w += CheckSpace;
+ if(h < CheckSpace)
+ h = CheckSpace;
+ } else if(tk->type == TKcascade) {
+ w += CheckButton + 2*CheckButtonBW;
+ if(h < CheckButton)
+ h = CheckButton;
+ }
+ w += 2*tk->highlightwidth;
+ h += 2*tk->highlightwidth;
+ tkl->w = w;
+ tkl->h = h;
+ if((tk->flag & Tksetwidth) == 0)
+ tk->req.width = w;
+ if((tk->flag & Tksetheight) == 0)
+ tk->req.height = h;
+}
+
+int
+tklabelmargin(Tk *tk)
+{
+ TkLabel *tkl;
+ Image *img;
+
+ if (tk->type == TKseparator)
+ return 0;
+ if (tk->type == TKlabel || tk->type == TKcascade) {
+ tkl = TKobj(TkLabel, tk);
+ img = nil;
+ if (tkl->img != nil)
+ img = tkl->img->img;
+ else if (tkl->bitmap != nil)
+ img = tkl->bitmap;
+ if (img != nil)
+ return Bitpadx;
+ return Textpadx;
+ }
+ return tkbuttonmargin(tk);
+}
+
+void
+tkfreelabel(Tk *tk)
+{
+ Image *i;
+ int locked;
+ Display *d;
+ TkLabel *tkl;
+
+ tkl = TKobj(TkLabel, tk);
+
+ if(tkl->text != nil)
+ free(tkl->text);
+ if(tkl->command != nil)
+ free(tkl->command);
+ if(tkl->value != nil)
+ free(tkl->value);
+ if(tkl->variable != nil) {
+ tkfreevar(tk->env->top, tkl->variable, tk->flag & Tkswept);
+ free(tkl->variable);
+ }
+ if(tkl->img != nil)
+ tkimgput(tkl->img);
+ i = tkl->bitmap;
+ if(i != nil) {
+ d = i->display;
+ locked = lockdisplay(d);
+ freeimage(i);
+ if(locked)
+ unlockdisplay(d);
+ }
+ if(tkl->menu != nil)
+ free(tkl->menu);
+}
+
+static void
+tktriangle(Point u, Image *i, TkEnv *e)
+{
+ int j;
+ Point p[3];
+
+ u.y++;
+ p[0].x = u.x + CheckButton;
+ p[0].y = u.y + CheckButton/2;
+ p[1].x = u.x;
+ p[1].y = u.y + CheckButton;
+ p[2].x = u.x;
+ p[2].y = u.y;
+ fillpoly(i, p, 3, ~0, tkgc(e, TkCbackgnddark), p[0]);
+ for(j = 0; j < 3; j++)
+ p[j].y -= 2;
+
+ fillpoly(i, p, 3, ~0, tkgc(e, TkCbackgndlght), p[0]);
+}
+
+/*
+ * draw TKlabel, TKcheckbutton, TKradiobutton
+ */
+char*
+tkdrawlabel(Tk *tk, Point orig)
+{
+ TkEnv *e;
+ TkLabel *tkl;
+ Rectangle r, s, mainr, focusr;
+ int dx, dy, h;
+ Point p, u, v, *pp;
+ Image *i, *dst, *cd, *cl, *ct, *img;
+ char *o;
+ int relief, bgnd, fgnd;
+
+ e = tk->env;
+
+ dst = tkimageof(tk);
+ if(dst == nil)
+ return nil;
+
+ v.x = tk->act.width + 2*tk->borderwidth;
+ v.y = tk->act.height + 2*tk->borderwidth;
+
+ r.min = ZP;
+ r.max.x = v.x;
+ r.max.y = v.y;
+ focusr = insetrect(r, tk->borderwidth);
+ mainr = insetrect(focusr, tk->highlightwidth);
+ relief = tk->relief;
+
+ tkl = TKobj(TkLabel, tk);
+
+ fgnd = TkCforegnd;
+ bgnd = TkCbackgnd;
+ if (tk->flag & Tkdisabled)
+ fgnd = TkCdisablefgnd;
+ else if ((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator == BoolF && tkl->check)
+ bgnd = TkCselect;
+ else if (tk->flag & Tkactive) {
+ fgnd = TkCactivefgnd;
+ bgnd = TkCactivebgnd;
+ }
+
+ i = tkitmp(e, r.max, bgnd);
+ if(i == nil)
+ return nil;
+
+ if(tk->flag & Tkactive)
+ draw(i, r, tkgc(e, bgnd), nil, ZP);
+
+ p = mainr.min;
+ h = tkl->h - 2 * tk->highlightwidth;
+
+ dx = tk->act.width - tkl->w - tk->ipad.x;
+ dy = tk->act.height - tkl->h - tk->ipad.y;
+ if((tkl->anchor & (Tknorth|Tksouth)) == 0)
+ p.y += dy/2;
+ else
+ if(tkl->anchor & Tksouth)
+ p.y += dy;
+
+ if((tkl->anchor & (Tkeast|Tkwest)) == 0)
+ p.x += dx/2;
+ else
+ if(tkl->anchor & Tkeast)
+ p.x += dx;
+
+ switch(tk->type) {
+ case TKcheckbutton:
+ if (tkl->indicator == BoolF) {
+ relief = tkl->check?TKsunken:TKraised;
+ break;
+ }
+ u.x = p.x + ButtonBorder;
+ u.y = p.y + ButtonBorder + (h - CheckSpace) / 2;
+
+ cl = tkgc(e, bgnd+TkLightshade);
+ cd = tkgc(e, bgnd+TkDarkshade);
+ if(tkl->check) {
+ tkbevel(i, u, CheckButton, CheckButton, CheckButtonBW, cd, cl);
+ u.x += CheckButtonBW;
+ u.y += CheckButtonBW;
+ s.min = u;
+ s.max.x = u.x + CheckButton;
+ s.max.y = u.y + CheckButton;
+ draw(i, s, tkgc(e, TkCselect), nil, ZP);
+ }
+ else
+ tkbevel(i, u, CheckButton, CheckButton, CheckButtonBW, cl, cd);
+ break;
+ case TKradiobutton:
+ if (tkl->indicator == BoolF) {
+ relief = tkl->check?TKsunken:TKraised;
+ break;
+ }
+ u.x = p.x + ButtonBorder;
+ u.y = p.y + ButtonBorder + (h - CheckSpace) / 2;
+ pp = mallocz(4*sizeof(Point), 0);
+ if(pp == nil)
+ return TkNomem;
+ pp[0].x = u.x + CheckButton/2;
+ pp[0].y = u.y;
+ pp[1].x = u.x + CheckButton;
+ pp[1].y = u.y + CheckButton/2;
+ pp[2].x = pp[0].x;
+ pp[2].y = u.y + CheckButton;
+ pp[3].x = u.x;
+ pp[3].y = pp[1].y;
+ cl = tkgc(e, bgnd+TkLightshade);
+ cd = tkgc(e, bgnd+TkDarkshade);
+ if(tkl->check)
+ fillpoly(i, pp, 4, ~0, tkgc(e, TkCselect), pp[0]);
+ else {
+ ct = cl;
+ cl = cd;
+ cd = ct;
+ }
+ line(i, pp[0], pp[1], 0, Enddisc, CheckButtonBW/2, cd, pp[0]);
+ line(i, pp[1], pp[2], 0, Enddisc, CheckButtonBW/2, cl, pp[1]);
+ line(i, pp[2], pp[3], 0, Enddisc, CheckButtonBW/2, cl, pp[2]);
+ line(i, pp[3], pp[0], 0, Enddisc, CheckButtonBW/2, cd, pp[3]);
+ free(pp);
+ break;
+ case TKcascade:
+ u.x = mainr.max.x - CheckButton - CheckButtonBW;
+ u.y = p.y + ButtonBorder + (h-CheckSpace)/2;
+ tktriangle(u, i, e);
+ break;
+ case TKbutton:
+ if ((tk->flag & (Tkactivated|Tkactive)) == (Tkactivated|Tkactive))
+ relief = TKsunken;
+ break;
+ }
+
+ p.x += tk->ipad.x/2;
+ p.y += tk->ipad.y/2;
+ u = ZP;
+ if(tk->type == TKbutton && relief == TKsunken) {
+ u.x++;
+ u.y++;
+ }
+ if((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator != BoolF)
+ u.x += CheckSpace;
+
+ img = nil;
+ if (tkl->img != nil && tkl->img->img != nil)
+ img = tkl->img->img;
+ else if (tkl->bitmap != nil)
+ img = tkl->bitmap;
+ if (img != nil) {
+ s.min.x = p.x + Bitpadx;
+ s.min.y = p.y + Bitpady;
+ s.max.x = s.min.x + Dx(img->r);
+ s.max.y = s.min.y + Dy(img->r);
+ s = rectaddpt(s, u);
+ if (tkchanhastype(img->chan, CGrey))
+ draw(i, s, tkgc(e, fgnd), img, ZP);
+ else
+ draw(i, s, img, nil, ZP);
+ } else if(tkl->text != nil) {
+ u.x += Textpadx;
+ u.y += Textpady;
+ ct = tkgc(e, fgnd);
+
+ p.y += (h - tkl->textheight) / 2;
+ o = tkdrawstring(tk, i, addpt(u, p), tkl->text, tkl->ul, ct, tkl->justify);
+ if(o != nil)
+ return o;
+ }
+
+ if (tkhaskeyfocus(tk))
+ tkbox(i, focusr, tk->highlightwidth, tkgc(e, TkChighlightfgnd));
+ tkdrawrelief(i, tk, ZP, bgnd, relief);
+
+ p.x = tk->act.x + orig.x;
+ p.y = tk->act.y + orig.y;
+ r = rectaddpt(r, p);
+ draw(dst, r, i, nil, ZP);
+
+ return nil;
+}
+
+char*
+tksetvar(TkTop *top, char *c, char *newval)
+{
+ TkVar *v;
+ TkWin *tkw;
+ Tk *f, *m;
+ void (*vc)(Tk*, char*, char*);
+
+ if (c == nil || c[0] == '\0')
+ return nil;
+
+ v = tkmkvar(top, c, TkVstring);
+ if(v == nil)
+ return TkNomem;
+ if(v->type != TkVstring)
+ return TkNotvt;
+
+ if(newval == nil)
+ newval = "";
+
+ if(v->value != nil) {
+ if (strcmp(v->value, newval) == 0)
+ return nil;
+ free(v->value);
+ }
+
+ v->value = strdup(newval);
+ if(v->value == nil)
+ return TkNomem;
+
+ for(f = top->root; f; f = f->siblings) {
+ if(f->type == TKmenu) {
+ tkw = TKobj(TkWin, f);
+ for(m = tkw->slave; m; m = m->next)
+ if ((vc = tkmethod[m->type]->varchanged) != nil)
+ (*vc)(m, c, newval);
+ } else
+ if ((vc = tkmethod[f->type]->varchanged) != nil)
+ (*vc)(f, c, newval);
+ }
+
+ return nil;
+}
+
+char*
+tkvariable(TkTop *t, char *arg, char **ret)
+{
+ TkVar *v;
+ char *fmt, *e, *buf, *ebuf, *val;
+ int l;
+
+ l = strlen(arg) + 2;
+ buf = malloc(l);
+ if(buf == nil)
+ return TkNomem;
+ ebuf = buf+l;
+
+ arg = tkword(t, arg, buf, ebuf, nil);
+ arg = tkskip(arg, " \t");
+ if (*arg == '\0') {
+ if(strcmp(buf, "lasterror") == 0) {
+ free(buf);
+ if(t->err == nil)
+ return nil;
+ fmt = "%s: %s";
+ if(strlen(t->errcmd) == sizeof(t->errcmd)-1)
+ fmt = "%s...: %s";
+ e = tkvalue(ret, fmt, t->errcmd, t->err);
+ t->err = nil;
+ return e;
+ }
+ v = tkmkvar(t, buf, 0);
+ free(buf);
+ if(v == nil || v->value == nil)
+ return nil;
+ if(v->type != TkVstring)
+ return TkNotvt;
+ return tkvalue(ret, "%s", v->value);
+ }
+ val = buf+strlen(buf)+1;
+ tkword(t, arg, val, ebuf, nil);
+ e = tksetvar(t, buf, val);
+ free(buf);
+ return e;
+}
+
+void
+tklabelgetimgs(Tk *tk, Image **image, Image **mask)
+{
+ TkLabel *tkl;
+
+ tkl = TKobj(TkLabel, tk);
+ *mask = nil;
+ if (tkl->img != nil)
+ *image = tkl->img->img;
+ else
+ *image = tkl->bitmap;
+}
+
+static
+TkCmdtab tklabelcmd[] =
+{
+ "cget", tklabelcget,
+ "configure", tklabelconf,
+ nil
+};
+
+TkMethod labelmethod = {
+ "label",
+ tklabelcmd,
+ tkfreelabel,
+ tkdrawlabel,
+ nil,
+ tklabelgetimgs
+};