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 void 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 } 1013 1014 /* for debugging */ 1015 char* 1016 tkname(Tk *tk) 1017 { 1018 return tk ? (tk->name ? tk->name->name : "(noname)") : "(nil)"; 1019 } 1020 1021 Tk* 1022 tkdeliver(Tk *tk, int event, void *data) 1023 { 1024 Tk *dest; 1025 //print("tkdeliver %v to %s\n", event, tkname(tk)); 1026 if(tk == nil || ((tk->flag&Tkdestroy) && event != TkDestroy)) 1027 return tk; 1028 1029 if(event&(TkFocusin|TkFocusout) && (tk->flag&Tktakefocus)) 1030 tk->dirty = tkrect(tk, 1); 1031 1032 if (tkmethod[tk->type]->deliver != nil) { 1033 dest = tkmethod[tk->type]->deliver(tk, event, data); 1034 if (dest == nil) 1035 return tk; 1036 tkdirty(tk); 1037 return dest; 1038 } 1039 1040 if((tk->flag & Tkdisabled) == 0) 1041 tksubdeliver(tk, tk->binds, event, data, 0); 1042 tkdirty(tk); 1043 return tk; 1044 } 1045 1046 static int 1047 nullop(char *fmt, ...) 1048 { 1049 USED(fmt); 1050 return 0; 1051 } 1052 1053 int 1054 tksubdeliver(Tk *tk, TkAction *binds, int event, void *data, int extn) 1055 { 1056 1057 TkAction *a; 1058 int delivered, genkey, delivered2, iskey; 1059 //int (*debug)(char *fmt, ...); 1060 if (!extn) 1061 return tkextndeliver(tk, binds, event, data); 1062 1063 //debug = (tk->name && !strcmp(tk->name->name, ".cd")) ? print : nullop; 1064 //debug("subdeliver %v\n", event); 1065 1066 if (event & TkTakefocus) { 1067 if (tk->flag & Tktakefocus) 1068 tksetkeyfocus(tk->env->top, tk, 0); 1069 return TkDdelivered; 1070 } 1071 1072 delivered = TkDnone; 1073 genkey = 0; 1074 for(a = binds; a != nil; a = a->link) { 1075 if(event == a->event) { 1076 //debug(" exact match on %v\n", a->event); 1077 tkcmdbind(tk, event, a->arg, data); 1078 delivered = TkDdelivered; 1079 } else if (a->event == TkKey && (a->type>>8)==TkAadd) 1080 genkey = 1; 1081 } 1082 if(delivered != TkDnone && !((event & TkKey) && genkey)) 1083 return delivered; 1084 1085 delivered2 = delivered; 1086 for(a = binds; a != nil; a = a->link) { 1087 /* 1088 * only bind to non-specific key events; if a specific 1089 * key event has already been delivered, only deliver event if 1090 * the non-specific binding was added. (TkAadd) 1091 */ 1092 if (a->event & TkExtns) 1093 continue; 1094 iskey = (a->event & TkKey); 1095 if (iskey ^ (event & TkKey)) 1096 continue; 1097 if(iskey && (TKKEY(a->event) != 0 1098 || ((a->type>>8) != TkAadd && delivered != TkDnone))) 1099 continue; 1100 if(!iskey && (a->event & TkMotion) && (a->event&TkEpress) != 0) 1101 continue; 1102 if(!(event & TkDouble) && (a->event & TkDouble)) 1103 continue; 1104 if((event & ~TkDouble) & a->event) { 1105 //debug(" partial match on %v\n", a->event); 1106 tkcmdbind(tk, event, a->arg, data); 1107 delivered2 = TkDdelivered; 1108 } 1109 } 1110 return delivered2; 1111 } 1112 1113 void 1114 tkcancel(TkAction **l, int event) 1115 { 1116 TkAction *a; 1117 1118 for(a = *l; a; a = *l) { 1119 if(a->event == event) { 1120 *l = a->link; 1121 a->link = nil; 1122 tkfreebind(a); 1123 continue; 1124 } 1125 l = &a->link; 1126 } 1127 } 1128 1129 static void 1130 tkcancela(TkAction **l, int event, int type, char *arg) 1131 { 1132 TkAction *a; 1133 1134 for(a = *l; a; a = *l) { 1135 if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){ 1136 *l = a->link; 1137 a->link = nil; 1138 tkfreebind(a); 1139 continue; 1140 } 1141 l = &a->link; 1142 } 1143 } 1144 1145 char* 1146 tkaction(TkAction **l, int event, int type, char *arg, int how) 1147 { 1148 TkAction *a; 1149 1150 if(arg == nil) 1151 return nil; 1152 if(how == TkArepl) 1153 tkcancel(l, event); 1154 else if(how == TkAadd){ 1155 for(a = *l; a; a = a->link) 1156 if(a->event == event && strcmp(a->arg, arg) == 0 && (a->type&0xff) == type){ 1157 a->type = type + (how << 8); 1158 return nil; 1159 } 1160 } 1161 else if(how == TkAsub){ 1162 tkcancela(l, event, type, arg); 1163 if(type == TkDynamic) /* should always be the case */ 1164 free(arg); 1165 return nil; 1166 } 1167 1168 a = malloc(sizeof(TkAction)); 1169 if(a == nil) { 1170 if(type == TkDynamic) 1171 free(arg); 1172 return TkNomem; 1173 } 1174 1175 a->event = event; 1176 a->arg = arg; 1177 a->type = type + (how << 8); 1178 1179 a->link = *l; 1180 *l = a; 1181 1182 return nil; 1183 } 1184 1185 char* 1186 tkitem(char *buf, char *a) 1187 { 1188 char *e; 1189 1190 while(*a && (*a == ' ' || *a == '\t')) 1191 a++; 1192 1193 e = buf + Tkmaxitem - 1; 1194 while(*a && *a != ' ' && *a != '\t' && buf < e) 1195 *buf++ = *a++; 1196 1197 *buf = '\0'; 1198 while(*a && (*a == ' ' || *a == '\t')) 1199 a++; 1200 return a; 1201 } 1202 1203 int 1204 tkismapped(Tk *tk) 1205 { 1206 while(tk->master) 1207 tk = tk->master; 1208 1209 /* We need subwindows of text & canvas to appear mapped always 1210 * so that the geom function update are seen by the parent 1211 * widget 1212 */ 1213 if((tk->flag & Tkwindow) == 0) 1214 return 1; 1215 1216 return tk->flag & Tkmapped; 1217 } 1218 1219 /* 1220 * Return absolute screen position of tk (just outside its top-left border). 1221 * When a widget is embedded in a text or canvas widget, we need to 1222 * use the text or canvas's relpos() function instead of act{x,y}, and we 1223 * need to folow up the parent pointer rather than the master one. 1224 */ 1225 Point 1226 tkposn(Tk *tk) 1227 { 1228 Tk *f, *last; 1229 Point g; 1230 1231 last = tk; 1232 if(tk->parent != nil) { 1233 g = tkmethod[tk->parent->type]->relpos(tk); 1234 f = tk->parent; 1235 } 1236 else { 1237 g.x = tk->act.x; 1238 g.y = tk->act.y; 1239 f = tk->master; 1240 } 1241 while(f) { 1242 g.x += f->borderwidth; 1243 g.y += f->borderwidth; 1244 last = f; 1245 if(f->parent != nil) { 1246 g = addpt(g, tkmethod[f->parent->type]->relpos(f)); 1247 f = f->parent; 1248 } 1249 else { 1250 g.x += f->act.x; 1251 g.y += f->act.y; 1252 f = f->master; 1253 } 1254 } 1255 if (last->flag & Tkwindow) 1256 g = addpt(g, TKobj(TkWin, last)->req); 1257 return g; 1258 } 1259 1260 /* 1261 * convert screen coords to local widget coords 1262 */ 1263 Point 1264 tkscrn2local(Tk *tk, Point p) 1265 { 1266 p = subpt(p, tkposn(tk)); 1267 p.x -= tk->borderwidth; 1268 p.y -= tk->borderwidth; 1269 return p; 1270 } 1271 1272 int 1273 tkvisiblerect(Tk *tk, Rectangle *rr) 1274 { 1275 Rectangle r; 1276 Point g; 1277 Tk *f, *last; 1278 g = Pt(tk->borderwidth, tk->borderwidth); 1279 last = tk; 1280 if(tk->parent != nil) { 1281 g = addpt(g, tkmethod[tk->parent->type]->relpos(tk)); 1282 f = tk->parent; 1283 } else { 1284 g.x += tk->act.x; 1285 g.y += tk->act.y; 1286 f = tk->master; 1287 } 1288 if (f == nil) { 1289 *rr = tkrect(tk, 1); 1290 return 1; 1291 } 1292 r = rectaddpt(tkrect(tk, 1), g); 1293 while (f) { 1294 if (!rectclip(&r, tkrect(f, 0))) 1295 return 0; 1296 g.x = f->borderwidth; 1297 g.y = f->borderwidth; 1298 last = f; 1299 if (f->parent != nil) { 1300 g = addpt(g, tkmethod[f->parent->type]->relpos(f)); 1301 f = f->parent; 1302 } else { 1303 g.x += f->act.x; 1304 g.y += f->act.y; 1305 f = f->master; 1306 } 1307 r = rectaddpt(r, g); 1308 } 1309 if (last->flag & Tkwindow) 1310 r = rectaddpt(r, TKobj(TkWin, last)->act); 1311 /* 1312 * now we have the visible rectangle in screen coords; 1313 * subtract actx+borderwidth and we've got it back in 1314 * widget-local coords again 1315 */ 1316 r = rectsubpt(r, tkposn(tk)); 1317 *rr = rectsubpt(r, Pt(tk->borderwidth, tk->borderwidth)); 1318 return 1; 1319 } 1320 1321 Point 1322 tkanchorpoint(Rectangle r, Point size, int anchor) 1323 { 1324 int dx, dy; 1325 Point p; 1326 1327 p = r.min; 1328 dx = Dx(r) - size.x; 1329 dy = Dy(r) - size.y; 1330 if((anchor & (Tknorth|Tksouth)) == 0) 1331 p.y += dy/2; 1332 else if(anchor & Tksouth) 1333 p.y += dy; 1334 1335 if((anchor & (Tkeast|Tkwest)) == 0) 1336 p.x += dx/2; 1337 else if(anchor & Tkeast) 1338 p.x += dx; 1339 return p; 1340 } 1341 1342 static char* 1343 tkunits(char c, int *d, TkEnv *e) 1344 { 1345 switch(c) { 1346 default: 1347 if(c >= '0' || c <= '9' || c == '.') 1348 break; 1349 return TkBadvl; 1350 case '\0': 1351 break; 1352 case 'c': /* Centimeters */ 1353 *d *= (Tkdpi*100)/254; 1354 break; 1355 case 'm': /* Millimeters */ 1356 *d *= (Tkdpi*10)/254; 1357 break; 1358 case 'i': /* Inches */ 1359 *d *= Tkdpi; 1360 break; 1361 case 'p': /* Points */ 1362 *d = (*d*Tkdpi)/72; 1363 break; 1364 case 'w': /* Character width */ 1365 if(e == nil) 1366 return TkBadvl; 1367 *d = *d * e->wzero; 1368 break; 1369 case 'h': /* Character height */ 1370 if(e == nil) 1371 return TkBadvl; 1372 *d = *d * e->font->height; 1373 break; 1374 } 1375 return nil; 1376 } 1377 1378 int 1379 TKF2I(int f) 1380 { 1381 if (f >= 0) 1382 return (f + Tkfpscalar/2) / Tkfpscalar; 1383 return (f - Tkfpscalar/2) / Tkfpscalar; 1384 } 1385 1386 /* 1387 * Parse a floating point number into a decimal fixed point representation 1388 */ 1389 char* 1390 tkfrac(char **arg, int *f, TkEnv *env) 1391 { 1392 int c, minus, i, fscale, seendigit; 1393 char *p, *e; 1394 1395 seendigit = 0; 1396 1397 p = *arg; 1398 p = tkskip(p, " \t"); 1399 1400 minus = 0; 1401 if(*p == '-') { 1402 minus = 1; 1403 p++; 1404 } 1405 i = 0; 1406 while(*p) { 1407 c = *p; 1408 if(c == '.') 1409 break; 1410 if(c < '0' || c > '9') 1411 break; 1412 i = i*10 + (c - '0'); 1413 seendigit = 1; 1414 p++; 1415 } 1416 i *= Tkfpscalar; 1417 if(*p == '.') 1418 p++; 1419 fscale = Tkfpscalar; 1420 while(*p && *p >= '0' && *p <= '9') { 1421 fscale /= 10; 1422 i += fscale * (*p++ - '0'); 1423 seendigit = 1; 1424 } 1425 1426 if(minus) 1427 i = -i; 1428 1429 if(!seendigit) 1430 return TkBadvl; 1431 e = tkunits(*p, &i, env); 1432 if (e != nil) 1433 return e; 1434 while (*p && *p != ' ' && *p != '\t') 1435 p++; 1436 *arg = p; 1437 *f = i; 1438 return nil; 1439 } 1440 1441 char* 1442 tkfracword(TkTop *t, char **arg, int *f, TkEnv *env) 1443 { 1444 char *p; 1445 char buf[Tkminitem]; 1446 1447 *arg = tkword(t, *arg, buf, buf+sizeof(buf), nil); 1448 p = buf; 1449 return tkfrac(&p, f, env); 1450 } 1451 1452 char* 1453 tkfprint(char *v, int frac) 1454 { 1455 int fscale; 1456 1457 if(frac < 0) { 1458 *v++ = '-'; 1459 frac = -frac; 1460 } 1461 v += sprint(v, "%d", frac/Tkfpscalar); 1462 frac = frac%Tkfpscalar; 1463 if(frac != 0) 1464 *v++ = '.'; 1465 fscale = Tkfpscalar/10; 1466 while(frac) { 1467 *v++ = '0' + frac/fscale; 1468 frac %= fscale; 1469 fscale /= 10; 1470 } 1471 *v = '\0'; 1472 return v; 1473 } 1474 1475 char* 1476 tkvalue(char **val, char *fmt, ...) 1477 { 1478 va_list arg; 1479 Fmt fmtx; 1480 1481 if(val == nil) 1482 return nil; 1483 1484 fmtstrinit(&fmtx); 1485 if(*val != nil) 1486 if(fmtprint(&fmtx, "%s", *val) < 0) 1487 return TkNomem; 1488 va_start(arg, fmt); 1489 fmtvprint(&fmtx, fmt, arg); 1490 va_end(arg); 1491 free(*val); 1492 *val = fmtstrflush(&fmtx); 1493 if(*val == nil) 1494 return TkNomem; 1495 return nil; 1496 } 1497 1498 static char* 1499 tkwidgetcmd(TkTop *t, Tk *tk, char *arg, char **val) 1500 { 1501 TkMethod *cm; 1502 TkCmdtab *ct; 1503 int bot, top, new, r; 1504 char *e, *buf; 1505 1506 buf = mallocz(Tkmaxitem, 0); 1507 if(buf == nil) 1508 return TkNomem; 1509 1510 arg = tkword(t, arg, buf, buf+Tkmaxitem, nil); 1511 if(val != nil) 1512 *val = nil; 1513 1514 cm = tkmethod[tk->type]; 1515 1516 e = TkBadcm; 1517 bot = 0; 1518 top = cm->ncmd - 1; 1519 1520 while(bot <= top) { 1521 new = (bot + top)/2; 1522 ct = &cm->cmd[new]; 1523 r = strcmp(ct->name, buf); 1524 if(r == 0) { 1525 e = ct->fn(tk, arg, val); 1526 break; 1527 } 1528 if(r < 0) 1529 bot = new + 1; 1530 else 1531 top = new - 1; 1532 } 1533 free(buf); 1534 tkdirty(tk); 1535 return e; 1536 } 1537 1538 Rectangle 1539 tkrect(Tk *tk, int withborder) 1540 { 1541 Rectangle r; 1542 int bd; 1543 1544 bd = withborder? tk->borderwidth: 0; 1545 r.min.x = -bd; 1546 r.min.y = -bd; 1547 r.max.x = tk->act.width + bd; 1548 r.max.y = tk->act.height + bd; 1549 return r; 1550 } 1551 1552 void 1553 tkdirty(Tk *tk) 1554 { 1555 Tk *sub; 1556 Point rel; 1557 Rectangle dirty; 1558 int isdirty, transparent; 1559 1560 /* 1561 * mark as dirty all views underneath a dirty transparent widget 1562 * down to the first opaque widget. 1563 * inform parents about any dirtiness. 1564 1565 * XXX as Tksubsub never gets reset, testing against Tksubsub doesn't *exactly* test 1566 * whether we're in a canvas/text widget, but merely 1567 * whether it has ever been. Tksubsub should probably be reset on unpack. 1568 */ 1569 isdirty = Dx(tk->dirty) > 0; 1570 transparent = tk->flag & Tktransparent; 1571 sub = tk; 1572 while (isdirty && ((tk->flag&Tksubsub) || transparent)) { 1573 if (tk->master != nil) { 1574 if (transparent) { 1575 rel.x = tk->act.x + tk->borderwidth; 1576 rel.y = tk->act.y + tk->borderwidth; 1577 dirty = rectaddpt(sub->dirty, rel); 1578 sub = tk->master; 1579 combinerect(&sub->dirty, dirty); 1580 transparent = sub->flag & Tktransparent; 1581 } 1582 tk = tk->master; 1583 } else if (tk->parent != nil) { 1584 tkmethod[tk->parent->type]->dirtychild(sub); 1585 tk = sub = tk->parent; 1586 isdirty = Dx(sub->dirty) > 0; 1587 transparent = sub->flag & Tktransparent; 1588 } else 1589 break; 1590 } 1591 } 1592 1593 static int 1594 qcmdcmp(const void *a, const void *b) 1595 { 1596 return strcmp(((TkCmdtab*)a)->name, ((TkCmdtab*)b)->name); 1597 } 1598 1599 void 1600 tksorttable(void) 1601 { 1602 int i; 1603 TkMethod *c; 1604 TkCmdtab *cmd; 1605 1606 for(i = 0; i < TKwidgets; i++) { 1607 c = tkmethod[i]; 1608 if(c->cmd == nil) 1609 continue; 1610 1611 for(cmd = c->cmd; cmd->name != nil; cmd++) 1612 ; 1613 c->ncmd = cmd - c->cmd; 1614 1615 qsort(c->cmd, c->ncmd, sizeof(TkCmdtab), qcmdcmp); 1616 } 1617 } 1618 1619 static char* 1620 tksinglecmd(TkTop *t, char *arg, char **val) 1621 { 1622 Tk *tk; 1623 int bot, top, new; 1624 char *e, *buf; 1625 1626 if(t->debug) 1627 print("tk: '%s'\n", arg); 1628 1629 buf = mallocz(Tkmaxitem, 0); 1630 if(buf == nil) 1631 return TkNomem; 1632 1633 arg = tkword(t, arg, buf, buf+Tkmaxitem, nil); 1634 switch(buf[0]) { 1635 case '\0': 1636 free(buf); 1637 return nil; 1638 case '.': 1639 tk = tklook(t, buf, 0); 1640 if(tk == nil){ 1641 tkerr(t, buf); 1642 free(buf); 1643 return TkBadwp; 1644 } 1645 e = tkwidgetcmd(t, tk, arg, val); 1646 free(buf); 1647 return e; 1648 } 1649 1650 bot = 0; 1651 top = nelem(cmdmain) - 1; 1652 e = TkBadcm; 1653 while(bot <= top) { 1654 int rc; 1655 new = (bot + top)/2; 1656 rc = strcmp(cmdmain[new].name, buf); 1657 if(!rc) { 1658 e = cmdmain[new].fn(t, arg, val); 1659 break; 1660 } 1661 1662 if(rc < 0) 1663 bot = new + 1; 1664 else 1665 top = new - 1; 1666 } 1667 free(buf); 1668 return e; 1669 } 1670 1671 static char* 1672 tkmatch(int inc, int dec, char *p) 1673 { 1674 int depth, esc, c; 1675 1676 esc = 0; 1677 depth = 1; 1678 while(*p) { 1679 c = *p; 1680 if(esc == 0) { 1681 if(c == inc) 1682 depth++; 1683 if(c == dec) 1684 depth--; 1685 if(depth == 0) 1686 return p; 1687 } 1688 if(c == '\\' && esc == 0) 1689 esc = 1; 1690 else 1691 esc = 0; 1692 p++; 1693 } 1694 return nil; 1695 } 1696 1697 char* 1698 tkexec(TkTop *t, char *arg, char **val) 1699 { 1700 int cmdsz, n; 1701 char *p, *cmd, *e, *c; 1702 1703 if(t->execdepth >= 0 && ++t->execdepth > 128) 1704 return TkDepth; 1705 1706 cmd = nil; 1707 cmdsz = 0; 1708 1709 p = arg; 1710 for(;;) { 1711 switch(*p++) { 1712 case '[': 1713 p = tkmatch('[', ']', p); 1714 if(p == nil){ 1715 free(cmd); 1716 return TkSyntx; 1717 } 1718 break; 1719 case '{': 1720 p = tkmatch('{', '}', p); 1721 if(p == nil){ 1722 free(cmd); 1723 return TkSyntx; 1724 } 1725 break; 1726 case ';': 1727 n = p - arg - 1; 1728 if(cmdsz < n) 1729 cmdsz = n; 1730 c = realloc(cmd, cmdsz+1); 1731 if(c == nil){ 1732 free(cmd); 1733 return TkNomem; 1734 } 1735 cmd = c; 1736 memmove(cmd, arg, n); 1737 cmd[n] = '\0'; 1738 e = tksinglecmd(t, cmd, nil); 1739 if(e != nil) { 1740 t->err = e; 1741 strncpy(t->errcmd, cmd, sizeof(t->errcmd)); 1742 t->errcmd[sizeof(t->errcmd)-1] = '\0'; 1743 free(cmd); 1744 return e; 1745 } 1746 arg = p; 1747 break; 1748 case '\0': 1749 case '\'': 1750 free(cmd); 1751 e = tksinglecmd(t, arg, val); 1752 if(e != nil) { 1753 t->err = e; 1754 strncpy(t->errcmd, arg, sizeof(t->errcmd)); 1755 t->errcmd[sizeof(t->errcmd)-1] = '\0'; 1756 } 1757 return e; 1758 } 1759 } 1760 } 1761 1762 static struct { 1763 char *name; 1764 int mask; 1765 } events[] = { 1766 "Button1P", TkButton1P, 1767 "Button1R", TkButton1R, 1768 "Button2P", TkButton2P, 1769 "Button2R", TkButton2R, 1770 "Button3P", TkButton3P, 1771 "Button3R", TkButton3R, 1772 "Button4P", TkButton4P, 1773 "Button4R", TkButton4R, 1774 "Button5P", TkButton5P, 1775 "Button5R", TkButton5R, 1776 "Button6P", TkButton6P, 1777 "Button6R", TkButton6R, 1778 "Extn1", TkExtn1, 1779 "Extn2", TkExtn2, 1780 "Takefocus", TkTakefocus, 1781 "Destroy", TkDestroy, 1782 "Enter", TkEnter, 1783 "Leave", TkLeave, 1784 "Motion", TkMotion, 1785 "Map", TkMap, 1786 "Unmap", TkUnmap, 1787 "Key", TkKey, 1788 "Focusin", TkFocusin, 1789 "Focusout", TkFocusout, 1790 "Configure", TkConfigure, 1791 "Double", TkDouble, 1792 0 1793 }; 1794 1795 int 1796 tkeventfmt(Fmt *f) 1797 { 1798 int k, i, d; 1799 int e; 1800 1801 e = va_arg(f->args, int); 1802 1803 if ((f->flags & FmtSharp) && e == TkMotion) 1804 return 0; 1805 fmtprint(f, "<"); 1806 k = -1; 1807 if (e & TkKey) { 1808 k = e & 0xffff; 1809 e &= ~0xffff; 1810 } 1811 d = 0; 1812 for (i = 0; events[i].name; i++) { 1813 if (e & events[i].mask) { 1814 if (d++) 1815 fmtprint(f, "|"); 1816 fmtprint(f, "%s", events[i].name); 1817 } 1818 } 1819 if (k != -1) { 1820 fmtprint(f, "[%c]", k); 1821 } else if (e == 0) 1822 fmtprint(f, "Noevent"); 1823 fmtprint(f, ">"); 1824 return 0; 1825 } 1826 1827 void 1828 tkerr(TkTop *t, char *e) 1829 { 1830 if(t != nil && e != nil){ 1831 strncpy(t->errx, e, sizeof(t->errx)); 1832 t->errx[sizeof(t->errx)-1] = '\0'; 1833 } 1834 } 1835 1836 char* 1837 tkerrstr(TkTop *t, char *e) 1838 { 1839 char *s = malloc(strlen(e)+1+strlen(t->errx)+1); 1840 1841 if(s == nil) 1842 return nil; 1843 strcpy(s, e); 1844 if(*e == '!'){ 1845 strcat(s, " "); 1846 strcat(s, t->errx); 1847 } 1848 t->errx[0] = '\0'; 1849 return s; 1850 } 1851 1852 char* 1853 tksetmgrab(TkTop *t, Tk *tk) 1854 { 1855 Tk *omgrab; 1856 TkCtxt *c; 1857 c = t->ctxt; 1858 if (tk == nil) { 1859 omgrab = c->mgrab; 1860 c->mgrab = nil; 1861 /* 1862 * don't enterleave if grab reset would cause no leave event 1863 */ 1864 if (!(omgrab != nil && (omgrab->flag & Tknograb) && 1865 c->entered != nil && (c->entered->flag & Tknograb))) 1866 tkenterleave(t); 1867 } else { 1868 if (c->focused && c->mfocus != nil && c->mfocus->env->top != tk->env->top) 1869 return "!grab already taken on another toplevel"; 1870 c->mgrab = tk; 1871 if (tk->flag & Tknograb) { 1872 if (c->focused) { 1873 c->focused = 0; 1874 c->mfocus = nil; 1875 } 1876 } else if (c->focused || c->mstate.b != 0) { 1877 c->focused = 1; 1878 c->mfocus = tk; 1879 } 1880 //print("setmgrab(%s) focus now %s\n", tkname(tk), tkname(c->mfocus)); 1881 tkenterleave(t); 1882 } 1883 return nil; 1884 } 1885 1886 int 1887 tkinsidepoly(Point *poly, int np, int winding, Point p) 1888 { 1889 Point pi, pj; 1890 int i, j, hit; 1891 1892 hit = 0; 1893 j = np - 1; 1894 for(i = 0; i < np; j = i++) { 1895 pi = poly[i]; 1896 pj = poly[j]; 1897 if((pi.y <= p.y && p.y < pj.y || pj.y <= p.y && p.y < pi.y) && 1898 p.x < (pj.x - pi.x) * (p.y - pi.y) / (pj.y - pi.y) + pi.x) { 1899 if(winding == 1 || pi.y > p.y) 1900 hit++; 1901 else 1902 hit--; 1903 } 1904 } 1905 return (hit & winding) != 0; 1906 } 1907 1908 int 1909 tklinehit(Point *a, int np, int w, Point p) 1910 { 1911 Point *b; 1912 int z, nx, ny, nrm; 1913 1914 while(np-- > 1) { 1915 b = a+1; 1916 nx = a->y - b->y; 1917 ny = b->x - a->x; 1918 nrm = (nx < 0? -nx : nx) + (ny < 0? -ny : ny); 1919 if(nrm) 1920 z = (p.x-b->x)*nx/nrm + (p.y-b->y)*ny/nrm; 1921 else 1922 z = (p.x-b->x) + (p.y-b->y); 1923 if(z < 0) 1924 z = -z; 1925 if(z < w) 1926 return 1; 1927 a++; 1928 } 1929 return 0; 1930 } 1931 1932 int 1933 tkiswordchar(int c) 1934 { 1935 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c >= 0xA0; 1936 } 1937 1938 int 1939 tkhaskeyfocus(Tk *tk) 1940 { 1941 if (tk == nil || tk->env->top->focused == 0) 1942 return 0; 1943 return tk == tk->env->top->ctxt->tkkeygrab; 1944 } 1945 1946 static int 1947 rptactive(void *v) 1948 { 1949 int id = (int)v; 1950 if (id == rptid) 1951 return 1; 1952 return 0; 1953 } 1954 1955 static int 1956 ckrpt(void *v, int interval) 1957 { 1958 int id = (int)v; 1959 if (id != rptid) 1960 return -1; 1961 if (interval < rptto) 1962 return 0; 1963 return 1; 1964 } 1965 1966 static void 1967 dorpt(void *v) 1968 { 1969 int id = (int)v; 1970 1971 if (id == rptid) { 1972 rptto = rptint; 1973 (*rptcb)(rptw, rptnote, 0); 1974 if (rptint <= 0) { 1975 rptid++; 1976 rptw = nil; 1977 } 1978 } 1979 } 1980 1981 void 1982 tkcancelrepeat(Tk *tk) 1983 { 1984 if (tk == rptw) { 1985 rptid++; 1986 rptw = nil; 1987 } 1988 } 1989 1990 void 1991 tkrepeat(Tk *tk, void (*callback)(Tk*, void*, int), void *note, int pause, int interval) 1992 { 1993 rptid++; 1994 if (tk != rptw && rptw != nil) 1995 /* existing callback being replaced- report to owner */ 1996 (*rptcb)(rptw, rptnote, 1); 1997 rptw = tk; 1998 if (tk == nil || callback == nil) 1999 return; 2000 rptnote = note; 2001 rptcb = callback; 2002 rptto = pause; 2003 rptint = interval; 2004 if (!autorpt) 2005 autorpt = rptproc("autorepeat", TkRptclick, (void*)rptid, rptactive, ckrpt, dorpt); 2006 else 2007 rptwakeup((void*)rptid, autorpt); 2008 } 2009 2010 static int 2011 blinkactive(void *v) 2012 { 2013 USED(v); 2014 return blinkw != nil; 2015 } 2016 2017 static int 2018 ckblink(void *v, int interval) 2019 { 2020 USED(v); 2021 USED(interval); 2022 2023 if (blinkw == nil) 2024 return -1; 2025 if (blinkignore) { 2026 blinkignore = 0; 2027 return 0; 2028 } 2029 return 1; 2030 } 2031 2032 static void 2033 doblink(void *v) 2034 { 2035 USED(v); 2036 2037 if (blinkw == nil) 2038 return; 2039 blinkcb(blinkw, blinkon++ & 1); 2040 tkupdate(blinkw->env->top); 2041 } 2042 2043 void 2044 tkblinkreset(Tk *tk) 2045 { 2046 if (blinkw == tk) { 2047 blinkignore = 1; 2048 blinkon = 0; 2049 } 2050 } 2051 2052 void 2053 tkblink(Tk *tk, void (*callback)(Tk*, int)) 2054 { 2055 if (tk == nil || callback == nil) { 2056 blinkw = nil; 2057 return; 2058 } 2059 blinkw = tk; 2060 blinkcb = callback; 2061 if (!blinkrpt) 2062 blinkrpt = rptproc("blinker", TkBlinkinterval, nil, blinkactive, ckblink, doblink); 2063 else 2064 rptwakeup(nil, blinkrpt); 2065 } 2066