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 r.max.y += 1; 933 draw(i, r, col, nil, ZP); 934 } 935 936 static void 937 tkul(Image *i, Point o, Image *col, int ul, Font *f, char *text) 938 { 939 char c, *v; 940 Rectangle r; 941 942 v = text+ul+1; 943 c = *v; 944 *v = '\0'; 945 r.max = stringsize(f, text); 946 r.max = addpt(r.max, o); 947 r.min = stringsize(f, v-1); 948 *v = c; 949 r.min.x = r.max.x - r.min.x; 950 r.min.y = r.max.y - 1; 951 r.max.y += 2; 952 draw(i, r, col, nil, ZP); 953 } 954 955 char* 956 tkdrawstring(Tk *tk, Image *i, Point o, char *text, int ul, Image *col, int j) 957 { 958 int n, l, maxl, sox; 959 char *q, *txt; 960 Point p; 961 TkEnv *e; 962 963 e = tk->env; 964 sox = maxl = 0; 965 if(j != Tkleft){ 966 maxl = 0; 967 txt = text; 968 while(*txt){ 969 q = strchr(txt, '\n'); 970 if(q != nil) 971 *q = '\0'; 972 l = stringwidth(e->font, txt); 973 if(l > maxl) 974 maxl = l; 975 if(q == nil) 976 break; 977 txt = q+1; 978 *q = '\n'; 979 } 980 sox = o.x; 981 } 982 while(*text) { 983 q = strchr(text, '\n'); 984 if(q != nil) 985 *q = '\0'; 986 if(j != Tkleft){ 987 o.x = sox; 988 l = stringwidth(e->font, text); 989 if(j == Tkcenter) 990 o.x += (maxl-l)/2; 991 else 992 o.x += maxl-l; 993 } 994 p = string(i, o, col, o, e->font, text); 995 if(ul >= 0) { 996 n = strlen(text); 997 if(ul < n) { 998 tkul(i, o, col, ul, e->font, text); 999 ul = -1; 1000 } else if(ul == n) { 1001 tkulall(i, o, col, e->font, text); 1002 ul = -1; 1003 } else 1004 ul -= n; 1005 } 1006 o.y += e->font->height; 1007 if(q == nil) 1008 break; 1009 text = q+1; 1010 *q = '\n'; 1011 } 1012 return nil; 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