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