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 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 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