1 #include <lib9.h> 2 #include <kernel.h> 3 #include "draw.h" 4 #include "tk.h" 5 #include "label.h" 6 7 #define O(t, e) ((long)(&((t*)0)->e)) 8 9 /* Layout constants */ 10 enum { 11 CheckSpace = CheckButton + 2*CheckButtonBW + 2*ButtonBorder, 12 }; 13 14 TkOption tklabelopts[] = 15 { 16 "text", OPTtext, O(TkLabel, text), nil, 17 "label", OPTtext, O(TkLabel, text), nil, 18 "underline", OPTdist, O(TkLabel, ul), nil, 19 "justify", OPTflag, O(TkLabel, justify), tkjustify, 20 "anchor", OPTflag, O(TkLabel, anchor), tkanchor, 21 "bitmap", OPTbmap, O(TkLabel, bitmap), nil, 22 "image", OPTimag, O(TkLabel, img), nil, 23 nil 24 }; 25 26 char* 27 tklabel(TkTop *t, char *arg, char **ret) 28 { 29 Tk *tk; 30 char *e; 31 TkLabel *tkl; 32 TkName *names; 33 TkOptab tko[3]; 34 35 tk = tknewobj(t, TKlabel, sizeof(Tk)+sizeof(TkLabel)); 36 if(tk == nil) 37 return TkNomem; 38 39 tkl = TKobj(TkLabel, tk); 40 tkl->ul = -1; 41 tkl->justify = Tkleft; 42 43 tko[0].ptr = tk; 44 tko[0].optab = tkgeneric; 45 tko[1].ptr = tkl; 46 tko[1].optab = tklabelopts; 47 tko[2].ptr = nil; 48 49 names = nil; 50 e = tkparse(t, arg, tko, &names); 51 if(e != nil) { 52 tkfreeobj(tk); 53 return e; 54 } 55 56 tksizelabel(tk); 57 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 58 59 e = tkaddchild(t, tk, &names); 60 tkfreename(names); 61 if(e != nil) { 62 tkfreeobj(tk); 63 return e; 64 } 65 tk->name->link = nil; 66 67 return tkvalue(ret, "%s", tk->name->name); 68 } 69 70 static char* 71 tklabelcget(Tk *tk, char *arg, char **val) 72 { 73 TkOptab tko[3]; 74 TkLabel *tkl = TKobj(TkLabel, tk); 75 76 tko[0].ptr = tk; 77 tko[0].optab = tkgeneric; 78 tko[1].ptr = tkl; 79 tko[1].optab = tklabelopts; 80 tko[2].ptr = nil; 81 82 return tkgencget(tko, arg, val, tk->env->top); 83 } 84 85 static char* 86 tklabelconf(Tk *tk, char *arg, char **val) 87 { 88 char *e; 89 TkGeom g; 90 int bd; 91 TkOptab tko[3]; 92 TkLabel *tkl = TKobj(TkLabel, tk); 93 94 tko[0].ptr = tk; 95 tko[0].optab = tkgeneric; 96 tko[1].ptr = tkl; 97 tko[1].optab = tklabelopts; 98 tko[2].ptr = nil; 99 100 if(*arg == '\0') 101 return tkconflist(tko, val); 102 103 g = tk->req; 104 bd = tk->borderwidth; 105 e = tkparse(tk->env->top, arg, tko, nil); 106 tksizelabel(tk); 107 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 108 tkgeomchg(tk, &g, bd); 109 110 tk->dirty = tkrect(tk, 1); 111 return e; 112 } 113 114 void 115 tksizelabel(Tk *tk) 116 { 117 Point p; 118 int w, h; 119 TkLabel *tkl; 120 121 tkl = TKobj(TkLabel, tk); 122 if(tkl->anchor == 0) 123 tkl->anchor = Tkcenter; 124 125 w = 0; 126 h = 0; 127 tkl->textheight = 0; 128 if(tkl->img != nil) { 129 w = tkl->img->w + 2*Bitpadx; 130 h = tkl->img->h + 2*Bitpady; 131 } 132 else 133 if(tkl->bitmap != nil) { 134 w = Dx(tkl->bitmap->r) + 2*Bitpadx; 135 h = Dy(tkl->bitmap->r) + 2*Bitpady; 136 } 137 else 138 if(tkl->text != nil) { 139 p = tkstringsize(tk, tkl->text); 140 w = p.x + 2*Textpadx; 141 h = p.y + 2*Textpady; 142 if(tkl->ul != -1 && tkl->ul > strlen(tkl->text)) 143 tkl->ul = -1; 144 tkl->textheight = p.y; 145 } 146 147 if((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator != BoolF) { 148 w += CheckSpace; 149 if(h < CheckSpace) 150 h = CheckSpace; 151 } else if(tk->type == TKcascade) { 152 w += CheckButton + 2*CheckButtonBW; 153 if(h < CheckButton) 154 h = CheckButton; 155 } 156 w += 2*tk->highlightwidth; 157 h += 2*tk->highlightwidth; 158 tkl->w = w; 159 tkl->h = h; 160 if((tk->flag & Tksetwidth) == 0) 161 tk->req.width = w; 162 if((tk->flag & Tksetheight) == 0) 163 tk->req.height = h; 164 } 165 166 int 167 tklabelmargin(Tk *tk) 168 { 169 TkLabel *tkl; 170 Image *img; 171 172 if (tk->type == TKseparator) 173 return 0; 174 if (tk->type == TKlabel || tk->type == TKcascade) { 175 tkl = TKobj(TkLabel, tk); 176 img = nil; 177 if (tkl->img != nil) 178 img = tkl->img->img; 179 else if (tkl->bitmap != nil) 180 img = tkl->bitmap; 181 if (img != nil) 182 return Bitpadx; 183 return Textpadx; 184 } 185 return tkbuttonmargin(tk); 186 } 187 188 void 189 tkfreelabel(Tk *tk) 190 { 191 Image *i; 192 int locked; 193 Display *d; 194 TkLabel *tkl; 195 196 tkl = TKobj(TkLabel, tk); 197 198 if(tkl->text != nil) 199 free(tkl->text); 200 if(tkl->command != nil) 201 free(tkl->command); 202 if(tkl->value != nil) 203 free(tkl->value); 204 if(tkl->variable != nil) { 205 tkfreevar(tk->env->top, tkl->variable, tk->flag & Tkswept); 206 free(tkl->variable); 207 } 208 if(tkl->img != nil) 209 tkimgput(tkl->img); 210 i = tkl->bitmap; 211 if(i != nil) { 212 d = i->display; 213 locked = lockdisplay(d); 214 freeimage(i); 215 if(locked) 216 unlockdisplay(d); 217 } 218 if(tkl->menu != nil) 219 free(tkl->menu); 220 } 221 222 static void 223 tktriangle(Point u, Image *i, TkEnv *e) 224 { 225 int j; 226 Point p[3]; 227 228 u.y++; 229 p[0].x = u.x + CheckButton; 230 p[0].y = u.y + CheckButton/2; 231 p[1].x = u.x; 232 p[1].y = u.y + CheckButton; 233 p[2].x = u.x; 234 p[2].y = u.y; 235 fillpoly(i, p, 3, ~0, tkgc(e, TkCbackgnddark), p[0]); 236 for(j = 0; j < 3; j++) 237 p[j].y -= 2; 238 239 fillpoly(i, p, 3, ~0, tkgc(e, TkCbackgndlght), p[0]); 240 } 241 242 /* 243 * draw TKlabel, TKcheckbutton, TKradiobutton 244 */ 245 char* 246 tkdrawlabel(Tk *tk, Point orig) 247 { 248 TkEnv *e; 249 TkLabel *tkl; 250 Rectangle r, s, mainr, focusr; 251 int dx, dy, h; 252 Point p, u, v, *pp; 253 Image *i, *dst, *cd, *cl, *ct, *img; 254 char *o; 255 int relief, bgnd, fgnd; 256 257 e = tk->env; 258 259 dst = tkimageof(tk); 260 if(dst == nil) 261 return nil; 262 263 v.x = tk->act.width + 2*tk->borderwidth; 264 v.y = tk->act.height + 2*tk->borderwidth; 265 266 r.min = ZP; 267 r.max.x = v.x; 268 r.max.y = v.y; 269 focusr = insetrect(r, tk->borderwidth); 270 mainr = insetrect(focusr, tk->highlightwidth); 271 relief = tk->relief; 272 273 tkl = TKobj(TkLabel, tk); 274 275 fgnd = TkCforegnd; 276 bgnd = TkCbackgnd; 277 if (tk->flag & Tkdisabled) 278 fgnd = TkCdisablefgnd; 279 else if ((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator == BoolF && tkl->check) 280 bgnd = TkCselect; 281 else if (tk->flag & Tkactive) { 282 fgnd = TkCactivefgnd; 283 bgnd = TkCactivebgnd; 284 } 285 286 i = tkitmp(e, r.max, bgnd); 287 if(i == nil) 288 return nil; 289 290 if(tk->flag & Tkactive) 291 draw(i, r, tkgc(e, bgnd), nil, ZP); 292 293 p = mainr.min; 294 h = tkl->h - 2 * tk->highlightwidth; 295 296 dx = tk->act.width - tkl->w - tk->ipad.x; 297 dy = tk->act.height - tkl->h - tk->ipad.y; 298 if((tkl->anchor & (Tknorth|Tksouth)) == 0) 299 p.y += dy/2; 300 else 301 if(tkl->anchor & Tksouth) 302 p.y += dy; 303 304 if((tkl->anchor & (Tkeast|Tkwest)) == 0) 305 p.x += dx/2; 306 else 307 if(tkl->anchor & Tkeast) 308 p.x += dx; 309 310 switch(tk->type) { 311 case TKcheckbutton: 312 if (tkl->indicator == BoolF) { 313 relief = tkl->check?TKsunken:TKraised; 314 break; 315 } 316 u.x = p.x + ButtonBorder; 317 u.y = p.y + ButtonBorder + (h - CheckSpace) / 2; 318 319 cl = tkgc(e, bgnd+TkLightshade); 320 cd = tkgc(e, bgnd+TkDarkshade); 321 if(tkl->check) { 322 tkbevel(i, u, CheckButton, CheckButton, CheckButtonBW, cd, cl); 323 u.x += CheckButtonBW; 324 u.y += CheckButtonBW; 325 s.min = u; 326 s.max.x = u.x + CheckButton; 327 s.max.y = u.y + CheckButton; 328 draw(i, s, tkgc(e, TkCselect), nil, ZP); 329 } 330 else 331 tkbevel(i, u, CheckButton, CheckButton, CheckButtonBW, cl, cd); 332 break; 333 case TKradiobutton: 334 if (tkl->indicator == BoolF) { 335 relief = tkl->check?TKsunken:TKraised; 336 break; 337 } 338 u.x = p.x + ButtonBorder; 339 u.y = p.y + ButtonBorder + (h - CheckSpace) / 2; 340 pp = mallocz(4*sizeof(Point), 0); 341 if(pp == nil) 342 return TkNomem; 343 pp[0].x = u.x + CheckButton/2; 344 pp[0].y = u.y; 345 pp[1].x = u.x + CheckButton; 346 pp[1].y = u.y + CheckButton/2; 347 pp[2].x = pp[0].x; 348 pp[2].y = u.y + CheckButton; 349 pp[3].x = u.x; 350 pp[3].y = pp[1].y; 351 cl = tkgc(e, bgnd+TkLightshade); 352 cd = tkgc(e, bgnd+TkDarkshade); 353 if(tkl->check) 354 fillpoly(i, pp, 4, ~0, tkgc(e, TkCselect), pp[0]); 355 else { 356 ct = cl; 357 cl = cd; 358 cd = ct; 359 } 360 line(i, pp[0], pp[1], 0, Enddisc, CheckButtonBW/2, cd, pp[0]); 361 line(i, pp[1], pp[2], 0, Enddisc, CheckButtonBW/2, cl, pp[1]); 362 line(i, pp[2], pp[3], 0, Enddisc, CheckButtonBW/2, cl, pp[2]); 363 line(i, pp[3], pp[0], 0, Enddisc, CheckButtonBW/2, cd, pp[3]); 364 free(pp); 365 break; 366 case TKcascade: 367 u.x = mainr.max.x - CheckButton - CheckButtonBW; 368 u.y = p.y + ButtonBorder + (h-CheckSpace)/2; 369 tktriangle(u, i, e); 370 break; 371 case TKbutton: 372 if ((tk->flag & (Tkactivated|Tkactive)) == (Tkactivated|Tkactive)) 373 relief = TKsunken; 374 break; 375 } 376 377 p.x += tk->ipad.x/2; 378 p.y += tk->ipad.y/2; 379 u = ZP; 380 if(tk->type == TKbutton && relief == TKsunken) { 381 u.x++; 382 u.y++; 383 } 384 if((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator != BoolF) 385 u.x += CheckSpace; 386 387 img = nil; 388 if (tkl->img != nil && tkl->img->img != nil) 389 img = tkl->img->img; 390 else if (tkl->bitmap != nil) 391 img = tkl->bitmap; 392 if (img != nil) { 393 s.min.x = p.x + Bitpadx; 394 s.min.y = p.y + Bitpady; 395 s.max.x = s.min.x + Dx(img->r); 396 s.max.y = s.min.y + Dy(img->r); 397 s = rectaddpt(s, u); 398 if (tkchanhastype(img->chan, CGrey)) 399 draw(i, s, tkgc(e, fgnd), img, ZP); 400 else 401 draw(i, s, img, nil, ZP); 402 } else if(tkl->text != nil) { 403 u.x += Textpadx; 404 u.y += Textpady; 405 ct = tkgc(e, fgnd); 406 407 p.y += (h - tkl->textheight) / 2; 408 o = tkdrawstring(tk, i, addpt(u, p), tkl->text, tkl->ul, ct, tkl->justify); 409 if(o != nil) 410 return o; 411 } 412 413 if (tkhaskeyfocus(tk)) 414 tkbox(i, focusr, tk->highlightwidth, tkgc(e, TkChighlightfgnd)); 415 tkdrawrelief(i, tk, ZP, bgnd, relief); 416 417 p.x = tk->act.x + orig.x; 418 p.y = tk->act.y + orig.y; 419 r = rectaddpt(r, p); 420 draw(dst, r, i, nil, ZP); 421 422 return nil; 423 } 424 425 char* 426 tksetvar(TkTop *top, char *c, char *newval) 427 { 428 TkVar *v; 429 TkWin *tkw; 430 Tk *f, *m; 431 void (*vc)(Tk*, char*, char*); 432 433 if (c == nil || c[0] == '\0') 434 return nil; 435 436 v = tkmkvar(top, c, TkVstring); 437 if(v == nil) 438 return TkNomem; 439 if(v->type != TkVstring) 440 return TkNotvt; 441 442 if(newval == nil) 443 newval = ""; 444 445 if(v->value != nil) { 446 if (strcmp(v->value, newval) == 0) 447 return nil; 448 free(v->value); 449 } 450 451 v->value = strdup(newval); 452 if(v->value == nil) 453 return TkNomem; 454 455 for(f = top->root; f; f = f->siblings) { 456 if(f->type == TKmenu) { 457 tkw = TKobj(TkWin, f); 458 for(m = tkw->slave; m; m = m->next) 459 if ((vc = tkmethod[m->type]->varchanged) != nil) 460 (*vc)(m, c, newval); 461 } else 462 if ((vc = tkmethod[f->type]->varchanged) != nil) 463 (*vc)(f, c, newval); 464 } 465 466 return nil; 467 } 468 469 char* 470 tkvariable(TkTop *t, char *arg, char **ret) 471 { 472 TkVar *v; 473 char *fmt, *e, *buf, *ebuf, *val; 474 int l; 475 476 l = strlen(arg) + 2; 477 buf = malloc(l); 478 if(buf == nil) 479 return TkNomem; 480 ebuf = buf+l; 481 482 arg = tkword(t, arg, buf, ebuf, nil); 483 arg = tkskip(arg, " \t"); 484 if (*arg == '\0') { 485 if(strcmp(buf, "lasterror") == 0) { 486 free(buf); 487 if(t->err == nil) 488 return nil; 489 fmt = "%s: %s"; 490 if(strlen(t->errcmd) == sizeof(t->errcmd)-1) 491 fmt = "%s...: %s"; 492 e = tkvalue(ret, fmt, t->errcmd, t->err); 493 t->err = nil; 494 return e; 495 } 496 v = tkmkvar(t, buf, 0); 497 free(buf); 498 if(v == nil || v->value == nil) 499 return nil; 500 if(v->type != TkVstring) 501 return TkNotvt; 502 return tkvalue(ret, "%s", v->value); 503 } 504 val = buf+strlen(buf)+1; 505 tkword(t, arg, val, ebuf, nil); 506 e = tksetvar(t, buf, val); 507 free(buf); 508 return e; 509 } 510 511 void 512 tklabelgetimgs(Tk *tk, Image **image, Image **mask) 513 { 514 TkLabel *tkl; 515 516 tkl = TKobj(TkLabel, tk); 517 *mask = nil; 518 if (tkl->img != nil) 519 *image = tkl->img->img; 520 else 521 *image = tkl->bitmap; 522 } 523 524 static 525 TkCmdtab tklabelcmd[] = 526 { 527 "cget", tklabelcget, 528 "configure", tklabelconf, 529 nil 530 }; 531 532 TkMethod labelmethod = { 533 "label", 534 tklabelcmd, 535 tkfreelabel, 536 tkdrawlabel, 537 nil, 538 tklabelgetimgs 539 }; 540