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