1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 5 struct TkCol 6 { 7 ulong rgba1; 8 ulong rgba3; /* if mixed, otherwise DNotacolor */ 9 Image* i; 10 TkCol* forw; 11 }; 12 13 extern void rptwakeup(void*, void*); 14 extern void* rptproc(char*, int, void*, int (*)(void*), int (*)(void*,int), void (*)(void*)); 15 16 typedef struct Cmd Cmd; 17 struct Cmd 18 { 19 char* name; 20 char* (*fn)(TkTop*, char*, char**); 21 }; 22 static struct Cmd cmdmain[] = 23 { 24 "bind", tkbind, 25 "button", tkbutton, 26 "canvas", tkcanvas, 27 "checkbutton", tkcheckbutton, 28 "choicebutton", tkchoicebutton, 29 "cursor", tkcursorcmd, 30 "destroy", tkdestroy, 31 "entry", tkentry, 32 "focus", tkfocus, 33 "frame", tkframe, 34 "grab", tkgrab, 35 "grid", tkgrid, 36 "image", tkimage, 37 "label", tklabel, 38 "listbox", tklistbox, 39 "lower", tklower, 40 "menu", tkmenu, 41 "menubutton", tkmenubutton, 42 "pack", tkpack, 43 "panel", tkpanel, 44 "puts", tkputs, 45 "radiobutton", tkradiobutton, 46 "raise", tkraise, 47 "scale", tkscale, 48 "scrollbar", tkscrollbar, 49 "see", tkseecmd, 50 "send", tksend, 51 "text", tktext, 52 "update", tkupdatecmd, 53 "variable", tkvariable, 54 "winfo", tkwinfo, 55 }; 56 57 char* tkfont; 58 59 /* 60 * auto-repeating support 61 * should perhaps be one rptproc per TkCtxt 62 * This is not done for the moment as there isn't 63 * a mechanism for terminating the rptproc 64 */ 65 static void *autorpt; 66 static int rptid; 67 static Tk *rptw; 68 static void *rptnote; 69 static void (*rptcb)(Tk*, void*, int); 70 static long rptto; 71 static int rptint; 72 73 /* blinking carets - should be per TkCtxt */ 74 static void *blinkrpt; 75 static Tk *blinkw; 76 static void (*blinkcb)(Tk*, int); 77 static int blinkignore; 78 static int blinkon; 79 80 81 ulong 82 tkrgba(int r, int g, int b, int a) 83 { 84 ulong p; 85 86 if(r < 0) 87 r = 0; 88 else if(r > 255) 89 r = 255; 90 if(g < 0) 91 g = 0; 92 else if(g > 255) 93 g = 255; 94 if(b < 0) 95 b = 0; 96 else if(b > 255) 97 b = 255; 98 p = (r<<24)|(g<<16)|(b<<8)|0xFF; 99 if(a == 255) 100 return p; 101 return setalpha(p, a); 102 } 103 104 /* to be replaced */ 105 static int 106 revalpha(int c, int a) 107 { 108 if (a == 0) 109 return 0; 110 return (c & 0xff) * 255 / a; 111 } 112 113 void 114 tkrgbavals(ulong rgba, int *R, int *G, int *B, int *A) 115 { 116 int a; 117 118 a = rgba & 0xff; 119 *A = a; 120 if (a != 0xff) { 121 *R = revalpha(rgba>>24, a); 122 *G = revalpha((rgba>>16) & 0xFF, a); 123 *B = revalpha((rgba >> 8) & 0xFF, a); 124 } else { 125 *R = (rgba>>24); 126 *G = ((rgba>>16) & 0xFF); 127 *B = ((rgba >> 8) & 0xFF); 128 } 129 } 130 131 static int 132 tkcachecol(TkCtxt *c, Image *i, ulong one, ulong three) 133 { 134 TkCol *cc; 135 136 cc = malloc(sizeof(*cc)); 137 if(cc == nil) 138 return 0; 139 cc->rgba1 = one; 140 cc->rgba3 = three; 141 cc->i = i; 142 cc->forw = c->chead; 143 c->chead = cc; 144 c->ncol++; 145 /* we'll do LRU management at some point */ 146 if(c->ncol > TkColcachesize){ 147 static int warn; 148 if(warn == 0){ 149 warn = 1; 150 print("tk: %d colours cached\n", TkColcachesize); 151 } 152 } 153 return 1; 154 } 155 156 static Image* 157 tkfindcol(TkCtxt *c, ulong one, ulong three) 158 { 159 TkCol *cc, **l; 160 161 for(l = &c->chead; (cc = *l) != nil; l = &cc->forw) 162 if(cc->rgba1 == one && cc->rgba3 == three){ 163 /* move it up in the list */ 164 *l = cc->forw; 165 cc->forw = c->chead; 166 c->chead = cc; 167 /* we assume it will be used right away and not stored */ 168 return cc->i; 169 } 170 return nil; 171 } 172 173 void 174 tkfreecolcache(TkCtxt *c) 175 { 176 TkCol *cc; 177 178 if(c == nil) 179 return; 180 while((cc = c->chead) != nil){ 181 c->chead = cc->forw; 182 freeimage(cc->i); 183 free(cc); 184 } 185 c->ctail = nil; 186 c->ncol = 0; 187 } 188 189 Image* 190 tkcolormix(TkCtxt *c, ulong one, ulong three) 191 { 192 Image *i; 193 Display *d; 194 195 i = tkfindcol(c, one, three); 196 if(i != nil) 197 return i; 198 d = c->display; 199 i = allocimagemix(d, one, three); 200 if(i == nil) 201 return d->black; 202 if(!tkcachecol(c, i, one, three)){ 203 freeimage(i); 204 return d->black; 205 } 206 return i; 207 } 208 209 Image* 210 tkcolor(TkCtxt *c, ulong pix) 211 { 212 Image *i; 213 Display *d; 214 Rectangle r; 215 216 d = c->display; 217 if(pix == DWhite) 218 return d->white; 219 if(pix == DBlack) 220 return d->black; 221 i = tkfindcol(c, pix, DNotacolor); 222 if(i != nil) 223 return i; 224 r.min = ZP; 225 r.max.x = 1; 226 r.max.y = 1; 227 if ((pix & 0xff) == 0xff) 228 i = allocimage(d, r, RGB24, 1, pix); 229 else 230 i = allocimage(d, r, RGBA32, 1, pix); 231 if(i == nil) 232 return d->black; 233 if(!tkcachecol(c, i, pix, DNotacolor)) { 234 freeimage(i); 235 return d->black; 236 } 237 return i; 238 } 239 240 Image* 241 tkgradient(TkCtxt *c, Rectangle r, int dir, ulong pix0, ulong pix1) 242 { 243 Display *d; 244 Image *i; 245 uchar *b, *p, *e; 246 int c0[3], c1[3], delta[3], a, j, x, y, n, locked; 247 Rectangle s; 248 249 d = c->display; 250 y = Dy(r); 251 x = Dx(r); 252 if(x <= 0 || y <= 0) { 253 r = Rect(0, 0, 1, 1); 254 x = y = 1; 255 } 256 /* TO DO: diagonal */ 257 s = r; 258 if(dir == Tkhorizontal){ 259 n = x; 260 r.max.y = r.min.y+1; 261 }else{ 262 n = y; 263 r.max.x = r.min.x+1; 264 } 265 b = mallocz(3*n, 0); 266 if(b == nil) 267 return nil; 268 locked = lockdisplay(d); 269 i = allocimage(d, r, RGB24, 1, DNofill); 270 if(i == nil) 271 goto Ret; 272 tkrgbavals(pix0, &c0[2], &c0[1], &c0[0], &a); 273 tkrgbavals(pix1, &c1[2], &c1[1], &c1[0], &a); 274 for(j = 0; j < 3; j++){ 275 c0[j] <<= 12; 276 c1[j] <<= 12; 277 delta[j] = ((c1[j]-c0[j])+(1<<11))/n; 278 } 279 e = b+3*n; 280 for(p = b; p < e; p += 3) { 281 p[0] = c0[0]>>12; 282 p[1] = c0[1]>>12; 283 p[2] = c0[2]>>12; 284 c0[0] += delta[0]; 285 c0[1] += delta[1]; 286 c0[2] += delta[2]; 287 } 288 loadimage(i, r, b, 3*n); 289 replclipr(i, 1, s); 290 Ret: 291 if(locked) 292 unlockdisplay(d); 293 free(b); 294 return i; 295 } 296 297 /* 298 * XXX should be in libdraw? 299 */ 300 int 301 tkchanhastype(ulong c, int t) 302 { 303 for(; c; c>>=8) 304 if(TYPE(c) == t) 305 return 1; 306 return 0; 307 } 308 309 void 310 tksettransparent(Tk *tk, int transparent) 311 { 312 if (transparent) 313 tk->flag |= Tktransparent; 314 else 315 tk->flag &= ~Tktransparent; 316 } 317 318 int 319 tkhasalpha(TkEnv *e, int col) 320 { 321 return (e->colors[col] & 0xff) != 0xff; 322 } 323 324 Image* 325 tkgc(TkEnv *e, int col) 326 { 327 return tkcolor(e->top->ctxt, e->colors[col]); 328 } 329 330 331 /* 332 * Todo: improve the fixed-point code 333 * the 255 scale factor is used because RGB ranges 0-255 334 */ 335 static void 336 rgb2hsv(int r, int g, int b, int *h, int *s, int *v) 337 { 338 int min, max, delta; 339 340 max = r; 341 if(g > max) 342 max = g; 343 if(b > max) 344 max = b; 345 min = r; 346 if(g < min) 347 min = g; 348 if(b < min) 349 min = b; 350 *v = max; 351 if (max != 0) 352 *s = ((max - min)*255) / max; 353 else 354 *s = 0; 355 356 if (*s == 0) { 357 *h = 0; /* undefined */ 358 } else { 359 delta = max - min; 360 if (r == max) 361 *h = (g - b)*255 / delta; 362 else if (g == max) 363 *h = (2*255) + ((b - r)*255) / delta; 364 else if (b == max) 365 *h = (4*255) + ((r - g)*255)/ delta; 366 *h *= 60; 367 if (*h < 0) 368 *h += 360*255; 369 *h /= 255; 370 } 371 } 372 373 static void 374 hsv2rgb(int h, int s, int v, int *r, int *g, int *b) 375 { 376 int i; 377 int f,p,q,t; 378 379 if (s == 0 && h == 0) { 380 *r = *g = *b = v; /* achromatic case */ 381 } else { 382 if (h >= 360) 383 h = 0; 384 i = h / 60; 385 h *= 255; 386 h /= 60; 387 388 f = h % 255; 389 p = v * (255 - s); 390 q = v * (255 - ((s * f)/255)); 391 t = v * (255- ((s * (255 - f))/255)); 392 p /= 255; 393 q /= 255; 394 t /= 255; 395 switch (i) { 396 case 0: *r = v; *g = t; *b = p; break; 397 case 1: *r = q; *g = v; *b = p; break; 398 case 2: *r = p; *g = v; *b = t; break; 399 case 3: *r = p; *g = q; *b = v; break; 400 case 4: *r = t; *g = p; *b = v; break; 401 case 5: *r = v; *g = p; *b = q; break; 402 } 403 } 404 } 405 406 enum { 407 MINDELTA = 0x10, 408 DELTA = 0x30, 409 }; 410 411 ulong 412 tkrgbashade(ulong rgba, int shade) 413 { 414 int R, G, B, A, h, s, v, vl, vd; 415 416 if (shade == TkSameshade) 417 return rgba; 418 419 tkrgbavals(rgba, &R, &G, &B, &A); 420 h = s = v = 0; 421 rgb2hsv(R, G, B, &h, &s, &v); 422 423 if (v < MINDELTA) { 424 vd = v+DELTA; 425 vl = vd+DELTA; 426 } else if (v > 255-MINDELTA) { 427 vl = v-DELTA; 428 vd = vl-DELTA; 429 } else { 430 vl = v+DELTA; 431 vd = v-DELTA; 432 } 433 434 v = (shade == TkLightshade)?vl:vd; 435 if (v < 0) 436 v = 0; 437 if (v > 255) 438 v = 255; 439 hsv2rgb(h, s, v, &R, &G, &B); 440 441 return tkrgba(R, G, B, A); 442 } 443 444 Image* 445 tkgshade(TkEnv *e, int col, int shade) 446 { 447 ulong rgba; 448 449 if (col == TkCbackgnd || col == TkCselectbgnd || col == TkCactivebgnd) 450 return tkgc(e, col+shade); 451 rgba = tkrgbashade(e->colors[col], shade); 452 return tkcolor(e->top->ctxt, rgba); 453 } 454 455 TkEnv* 456 tknewenv(TkTop *t) 457 { 458 TkEnv *e; 459 460 e = malloc(sizeof(TkEnv)); 461 if(e == nil) 462 return nil; 463 464 e->ref = 1; 465 e->top = t; 466 return e; 467 } 468 469 TkEnv* 470 tkdefaultenv(TkTop *t) 471 { 472 int locked; 473 TkEnv *env; 474 Display *d; 475 476 if(t->env != nil) { 477 t->env->ref++; 478 return t->env; 479 } 480 t->env = malloc(sizeof(TkEnv)); 481 if(t->env == nil) 482 return nil; 483 484 env = t->env; 485 env->ref = 1; 486 env->top = t; 487 488 if(tkfont == nil) 489 tkfont = "/fonts/pelm/unicode.8.font"; 490 491 d = t->display; 492 env->font = font_open(d, tkfont); 493 if(env->font == nil) { 494 static int warn; 495 if(warn == 0) { 496 warn = 1; 497 print("tk: font not found: %s\n", tkfont); 498 } 499 env->font = font_open(d, "*default*"); 500 if(env->font == nil) { 501 free(t->env); 502 t->env = nil; 503 return nil; 504 } 505 } 506 507 locked = lockdisplay(d); 508 env->wzero = stringwidth(env->font, "0"); 509 if ( env->wzero <= 0 ) 510 env->wzero = env->font->height / 2; 511 if(locked) 512 unlockdisplay(d); 513 514 tksetenvcolours(env); 515 return env; 516 } 517 518 void 519 tkputenv(TkEnv *env) 520 { 521 Display *d; 522 int locked; 523 524 if(env == nil) 525 return; 526 527 env->ref--; 528 if(env->ref != 0) 529 return; 530 531 d = env->top->display; 532 locked = lockdisplay(d); 533 534 if(env->font != nil) 535 font_close(env->font); 536 537 if(locked) 538 unlockdisplay(d); 539 540 free(env); 541 } 542 543 TkEnv* 544 tkdupenv(TkEnv **env) 545 { 546 Display *d; 547 TkEnv *e, *ne; 548 549 e = *env; 550 if(e->ref == 1) 551 return e; 552 553 ne = malloc(sizeof(TkEnv)); 554 if(ne == nil) 555 return nil; 556 557 ne->ref = 1; 558 ne->top = e->top; 559 560 d = e->top->display; 561 memmove(ne->colors, e->colors, sizeof(e->colors)); 562 ne->set = e->set; 563 ne->font = font_open(d, e->font->name); 564 ne->wzero = e->wzero; 565 566 e->ref--; 567 *env = ne; 568 return ne; 569 } 570 571 Tk* 572 tknewobj(TkTop *t, int type, int n) 573 { 574 Tk *tk; 575 576 tk = malloc(n); 577 if(tk == 0) 578 return 0; 579 580 tk->type = type; /* Defaults */ 581 tk->flag = Tktop; 582 tk->relief = TKflat; 583 tk->env = tkdefaultenv(t); 584 if(tk->env == nil) { 585 free(tk); 586 return nil; 587 } 588 589 return tk; 590 } 591 592 void 593 tkfreebind(TkAction *a) 594 { 595 TkAction *next; 596 597 while(a != nil) { 598 next = a->link; 599 if((a->type & 0xff) == TkDynamic) 600 free(a->arg); 601 free(a); 602 a = next; 603 } 604 } 605 606 void 607 tkfreename(TkName *f) 608 { 609 TkName *n; 610 611 while(f != nil) { 612 n = f->link; 613 free(f); 614 f = n; 615 } 616 } 617 618 void 619 tkfreeobj(Tk *tk) 620 { 621 TkCtxt *c; 622 623 c = tk->env->top->ctxt; 624 if(c != nil) { 625 if(c->tkkeygrab == tk) 626 c->tkkeygrab = nil; 627 if(c->mgrab == tk) 628 tksetmgrab(tk->env->top, nil); 629 if(c->mfocus == tk) 630 c->mfocus = nil; 631 if(c->entered == tk) 632 c->entered = nil; 633 } 634 635 if (tk == rptw) { 636 /* cancel the autorepeat without notifying the widget */ 637 rptid++; 638 rptw = nil; 639 } 640 if (tk == blinkw) 641 blinkw = nil; 642 tkextnfreeobj(tk); 643 tkmethod[tk->type]->free(tk); 644 tkputenv(tk->env); 645 tkfreebind(tk->binds); 646 if(tk->name != nil) 647 free(tk->name); 648 free(tk); 649 } 650 651 char* 652 tkaddchild(TkTop *t, Tk *tk, TkName **names) 653 { 654 TkName *n; 655 Tk *f, **l; 656 int found, len; 657 char *s, *ep; 658 659 n = *names; 660 if(n == nil || n->name[0] != '.'){ 661 if(n != nil) 662 tkerr(t, n->name); 663 return TkBadwp; 664 } 665 666 if (n->name[1] == '\0') 667 return TkDupli; 668 669 /* 670 * check that the name is well-formed. 671 * ep will point to end of parent component of the name. 672 */ 673 ep = nil; 674 for (s = n->name + 1; *s; s++) { 675 if (*s == '.'){ 676 tkerr(t, n->name); 677 return TkBadwp; 678 } 679 for (; *s && *s != '.'; s++) 680 ; 681 if (*s == '\0') 682 break; 683 ep = s; 684 } 685 if (ep == s - 1){ 686 tkerr(t, n->name); 687 return TkBadwp; 688 } 689 if (ep == nil) 690 ep = n->name + 1; 691 len = ep - n->name; 692 693 found = 0; 694 l = &t->root; 695 for(f = *l; f; f = f->siblings) { 696 if (f->name != nil) { 697 if (strcmp(n->name, f->name->name) == 0) 698 return TkDupli; 699 if (!found && 700 strncmp(n->name, f->name->name, len) == 0 && 701 f->name->name[len] == '\0') 702 found = 1; 703 } 704 l = &f->siblings; 705 } 706 if (0) { /* don't enable this until a reasonably major release... if ever */ 707 /* 708 * parent widget must already exist 709 */ 710 if (!found){ 711 tkerr(t, n->name); 712 return TkBadwp; 713 } 714 } 715 *l = tk; 716 tk->name = n; 717 *names = n->link; 718 719 return nil; 720 } 721 722 Tk* 723 tklook(TkTop *t, char *wp, int parent) 724 { 725 Tk *f; 726 char *p, *q; 727 728 if(wp == nil) 729 return nil; 730 731 if(parent) { 732 p = strdup(wp); 733 if(p == nil) 734 return nil; 735 q = strrchr(p, '.'); 736 if(q == nil) 737 abort(); 738 if(q == p) { 739 free(p); 740 return t->root; 741 } 742 *q = '\0'; 743 } else 744 p = wp; 745 746 for(f = t->root; f; f = f->siblings) 747 if ((f->name != nil) && (strcmp(f->name->name, p) == 0)) 748 break; 749 750 if(f != nil && (f->flag & Tkdestroy)) 751 f = nil; 752 753 if (parent) 754 free(p); 755 return f; 756 } 757 758 void 759 tktextsdraw(Image *img, Rectangle r, TkEnv *e, int sbw) 760 { 761 Image *l, *d; 762 Rectangle s; 763 764 draw(img, r, tkgc(e, TkCselectbgnd), nil, ZP); 765 s.min = r.min; 766 s.min.x -= sbw; 767 s.min.y -= sbw; 768 s.max.x = r.max.x; 769 s.max.y = r.min.y; 770 l = tkgc(e, TkCselectbgndlght); 771 draw(img, s, l, nil, ZP); 772 s.max.x = s.min.x + sbw; 773 s.max.y = r.max.y + sbw; 774 draw(img, s, l, nil, ZP); 775 s.max = r.max; 776 s.max.x += sbw; 777 s.max.y += sbw; 778 s.min.x = r.min.x; 779 s.min.y = r.max.y; 780 d = tkgc(e, TkCselectbgnddark); 781 draw(img, s, d, nil, ZP); 782 s.min.x = r.max.x; 783 s.min.y = r.min.y - sbw; 784 draw(img, s, d, nil, ZP); 785 } 786 787 void 788 tkbox(Image *i, Rectangle r, int bd, Image *fill) 789 { 790 if (bd > 0) { 791 draw(i, Rect(r.min.x, r.min.y, r.max.x, r.min.y+bd), fill, nil, ZP); 792 draw(i, Rect(r.min.x, r.min.y+bd, r.min.x+bd, r.max.y-bd), fill, nil, ZP); 793 draw(i, Rect(r.min.x, r.max.y-bd, r.max.x, r.max.y), fill, nil, ZP); 794 draw(i, Rect(r.max.x-bd, r.min.y+bd, r.max.x, r.max.y), fill, nil, ZP); 795 } 796 } 797 798 void 799 tkbevel(Image *i, Point o, int w, int h, int bw, Image *top, Image *bottom) 800 { 801 Rectangle r; 802 int x, border; 803 804 border = 2 * bw; 805 806 r.min = o; 807 r.max.x = r.min.x + w + border; 808 r.max.y = r.min.y + bw; 809 draw(i, r, top, nil, ZP); 810 811 r.max.x = r.min.x + bw; 812 r.max.y = r.min.y + h + border; 813 draw(i, r, top, nil, ZP); 814 815 r.max.x = o.x + w + border; 816 r.max.y = o.y + h + border; 817 r.min.x = o.x + bw; 818 r.min.y = r.max.y - bw; 819 for(x = 0; x < bw; x++) { 820 draw(i, r, bottom, nil, ZP); 821 r.min.x--; 822 r.min.y++; 823 } 824 r.min.x = o.x + bw + w; 825 r.min.y = o.y + bw; 826 for(x = bw; x >= 0; x--) { 827 draw(i, r, bottom, nil, ZP); 828 r.min.x++; 829 r.min.y--; 830 } 831 } 832 833 /* 834 * draw a relief border. 835 * color is an index into tk->env->colors and assumes 836 * light and dark versions following immediately after 837 * that index 838 */ 839 void 840 tkdrawrelief(Image *i, Tk *tk, Point o, int color, int rlf) 841 { 842 TkEnv *e; 843 Image *l, *d, *t; 844 int h, w, bd, bd1, bd2; 845 846 if(tk->borderwidth == 0) 847 return; 848 849 h = tk->act.height; 850 w = tk->act.width; 851 852 e = tk->env; 853 if (color == TkCbackgnd || color == TkCselectbgnd || color == TkCactivebgnd) { 854 l = tkgc(e, color+TkLightshade); 855 d = tkgc(e, color+TkDarkshade); 856 } else { 857 l = tkgshade(e, color, TkLightshade); 858 d = tkgshade(e, color, TkDarkshade); 859 } 860 bd = tk->borderwidth; 861 if(rlf < 0) 862 rlf = TKraised; 863 switch(rlf) { 864 case TKflat: 865 break; 866 case TKsunken: 867 tkbevel(i, o, w, h, bd, d, l); 868 break; 869 case TKraised: 870 tkbevel(i, o, w, h, bd, l, d); 871 break; 872 case TKgroove: 873 t = d; 874 d = l; 875 l = t; 876 /* fall through */ 877 case TKridge: 878 bd1 = bd/2; 879 bd2 = bd - bd1; 880 if(bd1 > 0) 881 tkbevel(i, o, w + 2*bd2, h + 2*bd2, bd1, l, d); 882 o.x += bd1; 883 o.y += bd1; 884 tkbevel(i, o, w, h, bd2, d, l); 885 break; 886 } 887 } 888 889 Point 890 tkstringsize(Tk *tk, char *text) 891 { 892 char *q; 893 int locked; 894 Display *d; 895 Point p, t; 896 897 if(text == nil) { 898 p.x = 0; 899 p.y = tk->env->font->height; 900 return p; 901 } 902 903 d = tk->env->top->display; 904 locked = lockdisplay(d); 905 906 p = ZP; 907 while(*text) { 908 q = strchr(text, '\n'); 909 if(q != nil) 910 *q = '\0'; 911 t = stringsize(tk->env->font, text); 912 p.y += t.y; 913 if(p.x < t.x) 914 p.x = t.x; 915 if(q == nil) 916 break; 917 text = q+1; 918 *q = '\n'; 919 } 920 if(locked) 921 unlockdisplay(d); 922 923 return p; 924 } 925 926 static void 927 tkulall(Image *i, Point o, Image *col, Font *f, char *text) 928 { 929 Rectangle r; 930 931 r.max = stringsize(f, text); 932 r.max = addpt(r.max, o); 933 r.min.x = o.x; 934 r.min.y = r.max.y - 1; 935 draw(i, r, col, nil, ZP); 936 } 937 938 static void 939 tkul(Image *i, Point o, Image *col, int ul, Font *f, char *text) 940 { 941 char c, *v; 942 Rectangle r; 943 944 v = text+ul+1; 945 c = *v; 946 *v = '\0'; 947 r.max = stringsize(f, text); 948 r.max = addpt(r.max, o); 949 r.min = stringsize(f, v-1); 950 *v = c; 951 r.min.x = r.max.x - r.min.x; 952 r.min.y = r.max.y - 1; 953 r.max.y++; 954 draw(i, r, col, nil, ZP); 955 } 956 957 void 958 tkdrawstring(Tk *tk, Image *i, Point o, char *text, int ul, Image *col, int j) 959 { 960 int n, l, maxl, sox; 961 char *q, *txt; 962 Point p; 963 TkEnv *e; 964 965 e = tk->env; 966 sox = maxl = 0; 967 if(j != Tkleft){ 968 maxl = 0; 969 txt = text; 970 while(*txt){ 971 q = strchr(txt, '\n'); 972 if(q != nil) 973 *q = '\0'; 974 l = stringwidth(e->font, txt); 975 if(l > maxl) 976 maxl = l; 977 if(q == nil) 978 break; 979 txt = q+1; 980 *q = '\n'; 981 } 982 sox = o.x; 983 } 984 while(*text) { 985 q = strchr(text, '\n'); 986 if(q != nil) 987 *q = '\0'; 988 if(j != Tkleft){ 989 o.x = sox; 990 l = stringwidth(e->font, text); 991 if(j == Tkcenter) 992 o.x += (maxl-l)/2; 993 else 994 o.x += maxl-l; 995 } 996 p = string(i, o, col, o, e->font, text); 997 if(ul >= 0) { 998 n = strlen(text); 999 if(ul < n) { 1000 tkul(i, o, col, ul, e->font, text); 1001 ul = -1; 1002 } else if(ul == n) { 1003 tkulall(i, o, col, e->font, text); 1004 ul = -1; 1005 } else 1006 ul -= n; 1007 } 1008 o.y += e->font->height; 1009 if(q == nil) 1010 break; 1011 text = q+1; 1012 *q = '\n'; 1013 } 1014 } 1015 1016 /* for debugging */ 1017 char* 1018 tkname(Tk *tk) 1019 { 1020 if(tk == nil) 1021 return "(nil)"; 1022 if(tk->name == nil) 1023 return "(noname)"; 1024 return tk->name->name; 1025 } 1026 1027 Tk* 1028 tkdeliver(Tk *tk, int event, void *data) 1029 { 1030 Tk *dest; 1031 1032 if(tk != nil && ((ulong)tk->type >= TKwidgets || (ulong)tk->name < 4096 && tk->name != nil)){ 1033 print("invalid Tk: type %d name %p\n", tk->type, tk->name); 1034 abort(); 1035 } 1036 //print("tkdeliver %v to %s\n", event, tkname(tk)); 1037 if(tk == nil || ((tk->flag&Tkdestroy) && event != TkDestroy)) 1038 return tk; 1039 if(event&(TkFocusin|TkFocusout) && (tk->flag&Tktakefocus)) 1040 tk->dirty = tkrect(tk, 1); 1041 1042 if (tkmethod[tk->type]->deliver != nil) { 1043 dest = tkmethod[tk->type]->deliver(tk, event, data); 1044 if (dest == nil) 1045 return tk; 1046 tkdirty(tk); 1047 return dest; 1048 } 1049 1050 if((tk->flag & Tkdisabled) == 0) 1051 tksubdeliver(tk, tk->binds, event, data, 0); 1052 tkdirty(tk); 1053 return tk; 1054 } 1055 1056 static int 1057 nullop(char *fmt, ...) 1058 { 1059 USED(fmt); 1060 return 0; 1061 } 1062 1063 int 1064 tksubdeliver(Tk *tk, TkAction *binds, int event, void *data, int extn) 1065 { 1066 1067 TkAction *a; 1068 int delivered, genkey, delivered2, iskey; 1069 //int (*debug)(char *fmt, ...); 1070 1071 if (!extn) 1072 return tkextndeliver(tk, binds, event, data); 1073 1074 //debug = (tk->name && !strcmp(tk->name->name, ".cd")) ? print : nullop; 1075 //debug("subdeliver %v\n", event); 1076 1077 if (event & TkTakefocus) { 1078 if (tk->flag & Tktakefocus) 1079 tksetkeyfocus(tk->env->top, tk, 0); 1080 return TkDdelivered; 1081 } 1082 1083 delivered = TkDnone; 1084 genkey = 0; 1085 for(a = binds; a != nil; a = a->link) { 1086 if(event == a->event) { 1087 //debug(" exact match on %v\n", a->event); 1088 tkcmdbind(tk, event, a->arg, data); 1089 delivered = TkDdelivered; 1090 } else if (a->event == TkKey && (a->type>>8)==TkAadd) 1091 genkey = 1; 1092 } 1093 if(delivered != TkDnone && !((event & TkKey) && genkey)) 1094 return delivered; 1095 1096 delivered2 = delivered; 1097 for(a = binds; a != nil; a = a->link) { 1098 /* 1099 * only bind to non-specific key events; if a specific 1100 * key event has already been delivered, only deliver event if 1101 * the non-specific binding was added. (TkAadd) 1102 */ 1103 if (a->event & TkExtns) 1104 continue; 1105 iskey = (a->event & TkKey); 1106 if (iskey ^ (event & TkKey)) 1107 continue; 1108 if(iskey && (TKKEY(a->event) != 0 1109 || ((a->type>>8) != TkAadd && delivered != TkDnone))) 1110 continue; 1111 if(!iskey && (a->event & TkMotion) && (a->event&TkEpress) != 0) 1112 continue; 1113 if(!(event & TkDouble) && (a->event & TkDouble)) 1114 continue; 1115 if((event & ~TkDouble) & a->event) { 1116 //debug(" partial match on %v\n", a->event); 1117 tkcmdbind(tk, event, a->arg, data); 1118 delivered2 = TkDdelivered; 1119 } 1120 } 1121 return delivered2; 1122 } 1123 1124 void 1125 tkcancel(TkAction **l, int event) 1126 { 1127 TkAction *a; 1128 1129 for(a = *l; a; a = *l) { 1130 if(a->event == event) { 1131 *l = a->link; 1132 a->link = nil; 1133 tkfreebind(a); 1134 continue; 1135 } 1136 l = &a->link; 1137 } 1138 } 1139 1140 static void 1141 tkcancela(TkAction **l, int event, int type, char *arg) 1142 { 1143 TkAction *a; 1144 1145 for(a = *l; a; a = *l) { 1146 if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){ 1147 *l = a->link; 1148 a->link = nil; 1149 tkfreebind(a); 1150 continue; 1151 } 1152 l = &a->link; 1153 } 1154 } 1155 1156 char* 1157 tkaction(TkAction **l, int event, int type, char *arg, int how) 1158 { 1159 TkAction *a; 1160 1161 if(arg == nil) 1162 return nil; 1163 if(how == TkArepl) 1164 tkcancel(l, event); 1165 else if(how == TkAadd){ 1166 for(a = *l; a; a = a->link) 1167 if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){ 1168 a->type = type + (how << 8); 1169 return nil; 1170 } 1171 } 1172 else if(how == TkAsub){ 1173 tkcancela(l, event, type, arg); 1174 if(type == TkDynamic) /* should always be the case */ 1175 free(arg); 1176 return nil; 1177 } 1178 1179 a = malloc(sizeof(TkAction)); 1180 if(a == nil) { 1181 if(type == TkDynamic) 1182 free(arg); 1183 return TkNomem; 1184 } 1185 1186 a->event = event; 1187 a->arg = arg; 1188 a->type = type + (how << 8); 1189 1190 a->link = *l; 1191 *l = a; 1192 1193 return nil; 1194 } 1195 1196 char* 1197 tkitem(char *buf, char *a) 1198 { 1199 char *e; 1200 1201 while(*a && (*a == ' ' || *a == '\t')) 1202 a++; 1203 1204 e = buf + Tkmaxitem - 1; 1205 while(*a && *a != ' ' && *a != '\t' && buf < e) 1206 *buf++ = *a++; 1207 1208 *buf = '\0'; 1209 while(*a && (*a == ' ' || *a == '\t')) 1210 a++; 1211 return a; 1212 } 1213 1214 /* 1215 * if tk is a subwindow or a descendent, return the subwindow; 1216 * return nil otherwise 1217 */ 1218 Tk* 1219 tkfindsub(Tk *tk) 1220 { 1221 for(; tk != nil; tk = tk->master){ 1222 if(tk->parent != nil) 1223 return tk; /* tk->parent is canvas or text */ 1224 } 1225 return nil; 1226 } 1227 1228 /* 1229 * Return absolute screen position of tk (just outside its top-left border). 1230 * When a widget is embedded in a text or canvas widget, we need to 1231 * use the text or canvas's relpos() function instead of act{x,y}, and we 1232 * need to folow up the parent pointer rather than the master one. 1233 */ 1234 Point 1235 tkposn(Tk *tk) 1236 { 1237 Tk *f, *last; 1238 Point g; 1239 1240 last = tk; 1241 if(tk->parent != nil) { 1242 g = tkmethod[tk->parent->type]->relpos(tk); 1243 f = tk->parent; 1244 } else { 1245 g.x = tk->act.x; 1246 g.y = tk->act.y; 1247 f = tk->master; 1248 } 1249 while(f != nil) { 1250 g.x += f->borderwidth; 1251 g.y += f->borderwidth; 1252 last = f; 1253 if(f->parent != nil) { 1254 g = addpt(g, tkmethod[f->parent->type]->relpos(f)); 1255 f = f->parent; 1256 } else { 1257 g.x += f->act.x; 1258 g.y += f->act.y; 1259 f = f->master; 1260 } 1261 } 1262 if (last->flag & Tkwindow) 1263 g = addpt(g, TKobj(TkWin, last)->req); 1264 return g; 1265 } 1266 1267 /* 1268 * convert screen coords to local widget coords 1269 */ 1270 Point 1271 tkscrn2local(Tk *tk, Point p) 1272 { 1273 p = subpt(p, tkposn(tk)); 1274 p.x -= tk->borderwidth; 1275 p.y -= tk->borderwidth; 1276 return p; 1277 } 1278 1279 int 1280 tkvisiblerect(Tk *tk, Rectangle *rr) 1281 { 1282 Rectangle r; 1283 Point g; 1284 Tk *f, *last; 1285 g = Pt(tk->borderwidth, tk->borderwidth); 1286 last = tk; 1287 if(tk->parent != nil) { 1288 g = addpt(g, tkmethod[tk->parent->type]->relpos(tk)); 1289 f = tk->parent; 1290 } else { 1291 g.x += tk->act.x; 1292 g.y += tk->act.y; 1293 f = tk->master; 1294 } 1295 if (f == nil) { 1296 *rr = tkrect(tk, 1); 1297 return 1; 1298 } 1299 r = rectaddpt(tkrect(tk, 1), g); 1300 while (f) { 1301 if (!rectclip(&r, tkrect(f, 0))) 1302 return 0; 1303 g.x = f->borderwidth; 1304 g.y = f->borderwidth; 1305 last = f; 1306 if (f->parent != nil) { 1307 g = addpt(g, tkmethod[f->parent->type]->relpos(f)); 1308 f = f->parent; 1309 } else { 1310 g.x += f->act.x; 1311 g.y += f->act.y; 1312 f = f->master; 1313 } 1314 r = rectaddpt(r, g); 1315 } 1316 if (last->flag & Tkwindow) 1317 r = rectaddpt(r, TKobj(TkWin, last)->act); 1318 /* 1319 * now we have the visible rectangle in screen coords; 1320 * subtract actx+borderwidth and we've got it back in 1321 * widget-local coords again 1322 */ 1323 r = rectsubpt(r, tkposn(tk)); 1324 *rr = rectsubpt(r, Pt(tk->borderwidth, tk->borderwidth)); 1325 return 1; 1326 } 1327 1328 Point 1329 tkanchorpoint(Rectangle r, Point size, int anchor) 1330 { 1331 int dx, dy; 1332 Point p; 1333 1334 p = r.min; 1335 dx = Dx(r) - size.x; 1336 dy = Dy(r) - size.y; 1337 if((anchor & (Tknorth|Tksouth)) == 0) 1338 p.y += dy/2; 1339 else if(anchor & Tksouth) 1340 p.y += dy; 1341 1342 if((anchor & (Tkeast|Tkwest)) == 0) 1343 p.x += dx/2; 1344 else if(anchor & Tkeast) 1345 p.x += dx; 1346 return p; 1347 } 1348 1349 static char* 1350 tkunits(char c, int *d, TkEnv *e) 1351 { 1352 switch(c) { 1353 default: 1354 if(c >= '0' || c <= '9' || c == '.') 1355 break; 1356 return TkBadvl; 1357 case '\0': 1358 break; 1359 case 'c': /* Centimeters */ 1360 *d *= (Tkdpi*100)/254; 1361 break; 1362 case 'm': /* Millimeters */ 1363 *d *= (Tkdpi*10)/254; 1364 break; 1365 case 'i': /* Inches */ 1366 *d *= Tkdpi; 1367 break; 1368 case 'p': /* Points */ 1369 *d = (*d*Tkdpi)/72; 1370 break; 1371 case 'w': /* Character width */ 1372 if(e == nil) 1373 return TkBadvl; 1374 *d = *d * e->wzero; 1375 break; 1376 case 'h': /* Character height */ 1377 if(e == nil) 1378 return TkBadvl; 1379 *d = *d * e->font->height; 1380 break; 1381 } 1382 return nil; 1383 } 1384 1385 int 1386 TKF2I(int f) 1387 { 1388 if (f >= 0) 1389 return (f + Tkfpscalar/2) / Tkfpscalar; 1390 return (f - Tkfpscalar/2) / Tkfpscalar; 1391 } 1392 1393 /* 1394 * Parse a floating point number into a decimal fixed point representation 1395 */ 1396 char* 1397 tkfrac(char **arg, int *f, TkEnv *env) 1398 { 1399 int c, minus, i, fscale, seendigit; 1400 char *p, *e; 1401 1402 seendigit = 0; 1403 1404 p = *arg; 1405 p = tkskip(p, " \t"); 1406 1407 minus = 0; 1408 if(*p == '-') { 1409 minus = 1; 1410 p++; 1411 } 1412 i = 0; 1413 while(*p) { 1414 c = *p; 1415 if(c == '.') 1416 break; 1417 if(c < '0' || c > '9') 1418 break; 1419 i = i*10 + (c - '0'); 1420 seendigit = 1; 1421 p++; 1422 } 1423 i *= Tkfpscalar; 1424 if(*p == '.') 1425 p++; 1426 fscale = Tkfpscalar; 1427 while(*p && *p >= '0' && *p <= '9') { 1428 fscale /= 10; 1429 i += fscale * (*p++ - '0'); 1430 seendigit = 1; 1431 } 1432 1433 if(minus) 1434 i = -i; 1435 1436 if(!seendigit) 1437 return TkBadvl; 1438 e = tkunits(*p, &i, env); 1439 if (e != nil) 1440 return e; 1441 while (*p && *p != ' ' && *p != '\t') 1442 p++; 1443 *arg = p; 1444 *f = i; 1445 return nil; 1446 } 1447 1448 char* 1449 tkfracword(TkTop *t, char **arg, int *f, TkEnv *env) 1450 { 1451 char *p; 1452 char buf[Tkminitem]; 1453 1454 *arg = tkword(t, *arg, buf, buf+sizeof(buf), nil); 1455 p = buf; 1456 return tkfrac(&p, f, env); 1457 } 1458 1459 char* 1460 tkfprint(char *v, int frac) 1461 { 1462 int fscale; 1463 1464 if(frac < 0) { 1465 *v++ = '-'; 1466 frac = -frac; 1467 } 1468 v += sprint(v, "%d", frac/Tkfpscalar); 1469 frac = frac%Tkfpscalar; 1470 if(frac != 0) 1471 *v++ = '.'; 1472 fscale = Tkfpscalar/10; 1473 while(frac) { 1474 *v++ = '0' + frac/fscale; 1475 frac %= fscale; 1476 fscale /= 10; 1477 } 1478 *v = '\0'; 1479 return v; 1480 } 1481 1482 char* 1483 tkvalue(char **val, char *fmt, ...) 1484 { 1485 va_list arg; 1486 Fmt fmtx; 1487 1488 if(val == nil) 1489 return nil; 1490 1491 fmtstrinit(&fmtx); 1492 if(*val != nil) 1493 if(fmtprint(&fmtx, "%s", *val) < 0) 1494 return TkNomem; 1495 va_start(arg, fmt); 1496 fmtvprint(&fmtx, fmt, arg); 1497 va_end(arg); 1498 free(*val); 1499 *val = fmtstrflush(&fmtx); 1500 if(*val == nil) 1501 return TkNomem; 1502 return nil; 1503 } 1504 1505 static char* 1506 tkwidgetcmd(TkTop *t, Tk *tk, char *arg, char **val) 1507 { 1508 TkMethod *cm; 1509 TkCmdtab *ct; 1510 int bot, top, new, r; 1511 char *e, *buf; 1512 1513 buf = mallocz(Tkmaxitem, 0); 1514 if(buf == nil) 1515 return TkNomem; 1516 1517 arg = tkword(t, arg, buf, buf+Tkmaxitem, nil); 1518 if(val != nil) 1519 *val = nil; 1520 1521 cm = tkmethod[tk->type]; 1522 1523 e = TkBadcm; 1524 bot = 0; 1525 top = cm->ncmd - 1; 1526 1527 while(bot <= top) { 1528 new = (bot + top)/2; 1529 ct = &cm->cmd[new]; 1530 r = strcmp(ct->name, buf); 1531 if(r == 0) { 1532 e = ct->fn(tk, arg, val); 1533 break; 1534 } 1535 if(r < 0) 1536 bot = new + 1; 1537 else 1538 top = new - 1; 1539 } 1540 free(buf); 1541 tkdirty(tk); 1542 return e; 1543 } 1544 1545 Rectangle 1546 tkrect(Tk *tk, int withborder) 1547 { 1548 Rectangle r; 1549 int bd; 1550 1551 bd = withborder? tk->borderwidth: 0; 1552 r.min.x = -bd; 1553 r.min.y = -bd; 1554 r.max.x = tk->act.width + bd; 1555 r.max.y = tk->act.height + bd; 1556 return r; 1557 } 1558 1559 void 1560 tkdirty(Tk *tk) 1561 { 1562 Tk *sub; 1563 Point rel; 1564 Rectangle dirty; 1565 int isdirty, transparent; 1566 1567 /* 1568 * mark as dirty all views underneath a dirty transparent widget 1569 * down to the first opaque widget. 1570 * inform parents about any dirtiness. 1571 1572 * XXX as Tksubsub never gets reset, testing against Tksubsub doesn't *exactly* test 1573 * whether we're in a canvas/text widget, but merely 1574 * whether it has ever been. Tksubsub should probably be reset on unpack. 1575 */ 1576 isdirty = Dx(tk->dirty) > 0; 1577 transparent = tk->flag & Tktransparent; 1578 sub = tk; 1579 while (isdirty && ((tk->flag&Tksubsub) || transparent)) { 1580 if (tk->master != nil) { 1581 if (transparent) { 1582 rel.x = tk->act.x + tk->borderwidth; 1583 rel.y = tk->act.y + tk->borderwidth; 1584 dirty = rectaddpt(sub->dirty, rel); 1585 sub = tk->master; 1586 combinerect(&sub->dirty, dirty); 1587 transparent = sub->flag & Tktransparent; 1588 } 1589 tk = tk->master; 1590 } else if (tk->parent != nil) { 1591 tkmethod[tk->parent->type]->dirtychild(sub); 1592 tk = sub = tk->parent; 1593 isdirty = Dx(sub->dirty) > 0; 1594 transparent = sub->flag & Tktransparent; 1595 } else 1596 break; 1597 } 1598 } 1599 1600 static int 1601 qcmdcmp(const void *a, const void *b) 1602 { 1603 return strcmp(((TkCmdtab*)a)->name, ((TkCmdtab*)b)->name); 1604 } 1605 1606 void 1607 tksorttable(void) 1608 { 1609 int i; 1610 TkMethod *c; 1611 TkCmdtab *cmd; 1612 1613 for(i = 0; i < TKwidgets; i++) { 1614 c = tkmethod[i]; 1615 if(c->cmd == nil) 1616 continue; 1617 1618 for(cmd = c->cmd; cmd->name != nil; cmd++) 1619 ; 1620 c->ncmd = cmd - c->cmd; 1621 1622 qsort(c->cmd, c->ncmd, sizeof(TkCmdtab), qcmdcmp); 1623 } 1624 } 1625 1626 static char* 1627 tksinglecmd(TkTop *t, char *arg, char **val) 1628 { 1629 Tk *tk; 1630 int bot, top, new; 1631 char *e, *buf; 1632 1633 if(t->debug) 1634 print("tk: '%s'\n", arg); 1635 1636 buf = mallocz(Tkmaxitem, 0); 1637 if(buf == nil) 1638 return TkNomem; 1639 1640 arg = tkword(t, arg, buf, buf+Tkmaxitem, nil); 1641 switch(buf[0]) { 1642 case '\0': 1643 free(buf); 1644 return nil; 1645 case '.': 1646 tk = tklook(t, buf, 0); 1647 if(tk == nil){ 1648 tkerr(t, buf); 1649 free(buf); 1650 return TkBadwp; 1651 } 1652 e = tkwidgetcmd(t, tk, arg, val); 1653 free(buf); 1654 return e; 1655 } 1656 1657 bot = 0; 1658 top = nelem(cmdmain) - 1; 1659 e = TkBadcm; 1660 while(bot <= top) { 1661 int rc; 1662 new = (bot + top)/2; 1663 rc = strcmp(cmdmain[new].name, buf); 1664 if(!rc) { 1665 e = cmdmain[new].fn(t, arg, val); 1666 break; 1667 } 1668 1669 if(rc < 0) 1670 bot = new + 1; 1671 else 1672 top = new - 1; 1673 } 1674 free(buf); 1675 return e; 1676 } 1677 1678 static char* 1679 tkmatch(int inc, int dec, char *p) 1680 { 1681 int depth, esc, c; 1682 1683 esc = 0; 1684 depth = 1; 1685 while(*p) { 1686 c = *p; 1687 if(esc == 0) { 1688 if(c == inc) 1689 depth++; 1690 if(c == dec) 1691 depth--; 1692 if(depth == 0) 1693 return p; 1694 } 1695 if(c == '\\' && esc == 0) 1696 esc = 1; 1697 else 1698 esc = 0; 1699 p++; 1700 } 1701 return nil; 1702 } 1703 1704 char* 1705 tkexec(TkTop *t, char *arg, char **val) 1706 { 1707 int cmdsz, n; 1708 char *p, *cmd, *e, *c; 1709 1710 if(t->execdepth >= 0 && ++t->execdepth > 128) 1711 return TkDepth; 1712 1713 cmd = nil; 1714 cmdsz = 0; 1715 1716 p = arg; 1717 for(;;) { 1718 switch(*p++) { 1719 case '[': 1720 p = tkmatch('[', ']', p); 1721 if(p == nil){ 1722 free(cmd); 1723 return TkSyntx; 1724 } 1725 break; 1726 case '{': 1727 p = tkmatch('{', '}', p); 1728 if(p == nil){ 1729 free(cmd); 1730 return TkSyntx; 1731 } 1732 break; 1733 case ';': 1734 n = p - arg - 1; 1735 if(cmdsz < n) 1736 cmdsz = n; 1737 c = realloc(cmd, cmdsz+1); 1738 if(c == nil){ 1739 free(cmd); 1740 return TkNomem; 1741 } 1742 cmd = c; 1743 memmove(cmd, arg, n); 1744 cmd[n] = '\0'; 1745 e = tksinglecmd(t, cmd, nil); 1746 if(e != nil) { 1747 t->err = e; 1748 strncpy(t->errcmd, cmd, sizeof(t->errcmd)); 1749 t->errcmd[sizeof(t->errcmd)-1] = '\0'; 1750 free(cmd); 1751 return e; 1752 } 1753 arg = p; 1754 break; 1755 case '\0': 1756 case '\'': 1757 free(cmd); 1758 e = tksinglecmd(t, arg, val); 1759 if(e != nil) { 1760 t->err = e; 1761 strncpy(t->errcmd, arg, sizeof(t->errcmd)); 1762 t->errcmd[sizeof(t->errcmd)-1] = '\0'; 1763 } 1764 return e; 1765 } 1766 } 1767 } 1768 1769 static struct { 1770 char *name; 1771 int mask; 1772 } events[] = { 1773 "Button1P", TkButton1P, 1774 "Button1R", TkButton1R, 1775 "Button2P", TkButton2P, 1776 "Button2R", TkButton2R, 1777 "Button3P", TkButton3P, 1778 "Button3R", TkButton3R, 1779 "Button4P", TkButton4P, 1780 "Button4R", TkButton4R, 1781 "Button5P", TkButton5P, 1782 "Button5R", TkButton5R, 1783 "Button6P", TkButton6P, 1784 "Button6R", TkButton6R, 1785 "Extn1", TkExtn1, 1786 "Extn2", TkExtn2, 1787 "Takefocus", TkTakefocus, 1788 "Destroy", TkDestroy, 1789 "Enter", TkEnter, 1790 "Leave", TkLeave, 1791 "Motion", TkMotion, 1792 "Map", TkMap, 1793 "Unmap", TkUnmap, 1794 "Key", TkKey, 1795 "Focusin", TkFocusin, 1796 "Focusout", TkFocusout, 1797 "Configure", TkConfigure, 1798 "Double", TkDouble, 1799 0 1800 }; 1801 1802 int 1803 tkeventfmt(Fmt *f) 1804 { 1805 int k, i, d; 1806 int e; 1807 1808 e = va_arg(f->args, int); 1809 1810 if ((f->flags & FmtSharp) && e == TkMotion) 1811 return 0; 1812 fmtprint(f, "<"); 1813 k = -1; 1814 if (e & TkKey) { 1815 k = e & 0xffff; 1816 e &= ~0xffff; 1817 } 1818 d = 0; 1819 for (i = 0; events[i].name; i++) { 1820 if (e & events[i].mask) { 1821 if (d++) 1822 fmtprint(f, "|"); 1823 fmtprint(f, "%s", events[i].name); 1824 } 1825 } 1826 if (k != -1) { 1827 fmtprint(f, "[%c]", k); 1828 } else if (e == 0) 1829 fmtprint(f, "Noevent"); 1830 fmtprint(f, ">"); 1831 return 0; 1832 } 1833 1834 void 1835 tkerr(TkTop *t, char *e) 1836 { 1837 if(t != nil && e != nil){ 1838 strncpy(t->errx, e, sizeof(t->errx)); 1839 t->errx[sizeof(t->errx)-1] = '\0'; 1840 } 1841 } 1842 1843 char* 1844 tkerrstr(TkTop *t, char *e) 1845 { 1846 char *s = malloc(strlen(e)+1+strlen(t->errx)+1); 1847 1848 if(s == nil) 1849 return nil; 1850 strcpy(s, e); 1851 if(*e == '!'){ 1852 strcat(s, " "); 1853 strcat(s, t->errx); 1854 } 1855 t->errx[0] = '\0'; 1856 return s; 1857 } 1858 1859 char* 1860 tksetmgrab(TkTop *t, Tk *tk) 1861 { 1862 Tk *omgrab; 1863 TkCtxt *c; 1864 c = t->ctxt; 1865 if (tk == nil) { 1866 omgrab = c->mgrab; 1867 c->mgrab = nil; 1868 /* 1869 * don't enterleave if grab reset would cause no leave event 1870 */ 1871 if (!(omgrab != nil && (omgrab->flag & Tknograb) && 1872 c->entered != nil && (c->entered->flag & Tknograb))) 1873 tkenterleave(t); 1874 } else { 1875 if (c->focused && c->mfocus != nil && c->mfocus->env->top != tk->env->top) 1876 return "!grab already taken on another toplevel"; 1877 c->mgrab = tk; 1878 if (tk->flag & Tknograb) { 1879 if (c->focused) { 1880 c->focused = 0; 1881 c->mfocus = nil; 1882 } 1883 } else if (c->focused || c->mstate.b != 0) { 1884 c->focused = 1; 1885 c->mfocus = tk; 1886 } 1887 //print("setmgrab(%s) focus now %s\n", tkname(tk), tkname(c->mfocus)); 1888 tkenterleave(t); 1889 } 1890 return nil; 1891 } 1892 1893 int 1894 tkinsidepoly(Point *poly, int np, int winding, Point p) 1895 { 1896 Point pi, pj; 1897 int i, j, hit; 1898 1899 hit = 0; 1900 j = np - 1; 1901 for(i = 0; i < np; j = i++) { 1902 pi = poly[i]; 1903 pj = poly[j]; 1904 if((pi.y <= p.y && p.y < pj.y || pj.y <= p.y && p.y < pi.y) && 1905 p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x) { 1906 if(winding == 1 || pi.y > p.y) 1907 hit++; 1908 else 1909 hit--; 1910 } 1911 } 1912 return (hit & winding) != 0; 1913 } 1914 1915 int 1916 tklinehit(Point *a, int np, int w, Point p) 1917 { 1918 Point *b; 1919 int z, nx, ny, nrm; 1920 1921 while(np-- > 1) { 1922 b = a+1; 1923 nx = a->y - b->y; 1924 ny = b->x - a->x; 1925 nrm = (nx < 0? -nx : nx) + (ny < 0? -ny : ny); 1926 if(nrm) 1927 z = (p.x-b->x)*nx/nrm + (p.y-b->y)*ny/nrm; 1928 else 1929 z = (p.x-b->x) + (p.y-b->y); 1930 if(z < 0) 1931 z = -z; 1932 if(z < w) 1933 return 1; 1934 a++; 1935 } 1936 return 0; 1937 } 1938 1939 int 1940 tkiswordchar(int c) 1941 { 1942 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c >= 0xA0; 1943 } 1944 1945 int 1946 tkhaskeyfocus(Tk *tk) 1947 { 1948 if (tk == nil || tk->env->top->focused == 0) 1949 return 0; 1950 return tk == tk->env->top->ctxt->tkkeygrab; 1951 } 1952 1953 static int 1954 rptactive(void *v) 1955 { 1956 int id = (int)v; 1957 if (id == rptid) 1958 return 1; 1959 return 0; 1960 } 1961 1962 static int 1963 ckrpt(void *v, int interval) 1964 { 1965 int id = (int)v; 1966 if (id != rptid) 1967 return -1; 1968 if (interval < rptto) 1969 return 0; 1970 return 1; 1971 } 1972 1973 static void 1974 dorpt(void *v) 1975 { 1976 int id = (int)v; 1977 1978 if (id == rptid) { 1979 rptto = rptint; 1980 (*rptcb)(rptw, rptnote, 0); 1981 if (rptint <= 0) { 1982 rptid++; 1983 rptw = nil; 1984 } 1985 } 1986 } 1987 1988 void 1989 tkcancelrepeat(Tk *tk) 1990 { 1991 if (tk == rptw) { 1992 rptid++; 1993 rptw = nil; 1994 } 1995 } 1996 1997 void 1998 tkrepeat(Tk *tk, void (*callback)(Tk*, void*, int), void *note, int pause, int interval) 1999 { 2000 rptid++; 2001 if (tk != rptw && rptw != nil) 2002 /* existing callback being replaced- report to owner */ 2003 (*rptcb)(rptw, rptnote, 1); 2004 rptw = tk; 2005 if (tk == nil || callback == nil) 2006 return; 2007 rptnote = note; 2008 rptcb = callback; 2009 rptto = pause; 2010 rptint = interval; 2011 if (!autorpt) 2012 autorpt = rptproc("autorepeat", TkRptclick, (void*)rptid, rptactive, ckrpt, dorpt); 2013 else 2014 rptwakeup((void*)rptid, autorpt); 2015 } 2016 2017 static int 2018 blinkactive(void *v) 2019 { 2020 USED(v); 2021 return blinkw != nil; 2022 } 2023 2024 static int 2025 ckblink(void *v, int interval) 2026 { 2027 USED(v); 2028 USED(interval); 2029 2030 if (blinkw == nil) 2031 return -1; 2032 if (blinkignore) { 2033 blinkignore = 0; 2034 return 0; 2035 } 2036 return 1; 2037 } 2038 2039 static void 2040 doblink(void *v) 2041 { 2042 USED(v); 2043 2044 if (blinkw == nil) 2045 return; 2046 blinkcb(blinkw, blinkon++ & 1); 2047 tkupdate(blinkw->env->top); 2048 } 2049 2050 void 2051 tkblinkreset(Tk *tk) 2052 { 2053 if (blinkw == tk) { 2054 blinkignore = 1; 2055 blinkon = 0; 2056 } 2057 } 2058 2059 void 2060 tkblink(Tk *tk, void (*callback)(Tk*, int)) 2061 { 2062 if (tk == nil || callback == nil) { 2063 blinkw = nil; 2064 return; 2065 } 2066 blinkw = tk; 2067 blinkcb = callback; 2068 if (!blinkrpt) 2069 blinkrpt = rptproc("blinker", TkBlinkinterval, nil, blinkactive, ckblink, doblink); 2070 else 2071 rptwakeup(nil, blinkrpt); 2072 } 2073 2074 /* 2075 * debugging 2076 */ 2077 void 2078 tkdump(Tk *tk) 2079 { 2080 Tk *sl; 2081 2082 if(tk == nil) 2083 return; 2084 if((uint)tk->type < TKwidgets) 2085 print("%s", tkmethod[tk->type]->name); 2086 else 2087 print("TYPE#%#ux", tk->type); 2088 if(tk->name == nil || (ulong)tk->name < 512) 2089 print(" NAME %p", tk->name); 2090 else 2091 print(" %s", tkname(tk)); 2092 print(" # tk %#p flag %#ux grid %#p", tk, tk->flag, tk->grid); 2093 if(tk->parent != nil) 2094 print(" parent [%#p %q]", tk->parent, tkname(tk->parent)); 2095 if(tk->master != nil) 2096 print(" master [%#p %q]", tk->master, tkname(tk->master)); 2097 if(tk->slave != nil){ 2098 print(" slaves"); 2099 for(sl = tk->slave; sl != nil; sl = sl->next) 2100 print(" [%#p %q]", sl, tkname(sl)); 2101 } 2102 print("\n"); 2103 if(tk->type != TKcanvas) 2104 return; 2105 tkcvsdump(tk); 2106 } 2107 2108 void 2109 tktopdump(Tk *tk) 2110 { 2111 TkTop *top; 2112 Tk *sub; 2113 2114 if(tk == nil || tk->env == nil){ 2115 print("# %#p no top\n", tk); 2116 return; 2117 } 2118 top = tk->env->top; 2119 print("# env %#p top %#p\n", tk->env, top); 2120 if(top != nil){ 2121 for(sub = top->root; sub != nil; sub = sub->siblings) 2122 tkdump(sub); 2123 } 2124 } 2125