1 #include "lib9.h" 2 #include "draw.h" 3 #include "tk.h" 4 5 #define O(t, e) ((long)(&((t*)0)->e)) 6 7 /* Layout constants */ 8 enum { 9 Triangle = 10, /* Height of scroll bar triangle */ 10 Elembw = 1, /* border around elements (triangles etc.) */ 11 Scrollbw = 1, /* bevel border on scrollbar */ 12 Tribw= 1, /* shadow border on triangle */ 13 }; 14 15 typedef struct TkScroll TkScroll; 16 struct TkScroll 17 { 18 int activer; 19 int orient; /* Horitontal or Vertical */ 20 int dragpix; /* Scroll delta in button drag */ 21 int dragtop; 22 int dragbot; 23 int jump; /* Jump scroll enable */ 24 int flag; /* Display flags */ 25 int top; /* Top fraction */ 26 int bot; /* Bottom fraction */ 27 int a1; /* Pixel top/left arrow1 */ 28 int t1; /* Pixel top/left trough */ 29 int t2; /* Pixel top/left lower trough */ 30 int a2; /* Pixel top/left arrow2 */ 31 char* cmd; 32 }; 33 34 enum { 35 ActiveA1 = (1<<0), /* Scrollbar control */ 36 ActiveA2 = (1<<1), 37 ActiveB1 = (1<<2), 38 ButtonA1 = (1<<3), 39 ButtonA2 = (1<<4), 40 ButtonB1 = (1<<5), 41 Autorepeat = (1<<6) 42 }; 43 44 static 45 TkOption opts[] = 46 { 47 "activerelief", OPTstab, O(TkScroll, activer), tkrelief, 48 "command", OPTtext, O(TkScroll, cmd), nil, 49 "jump", OPTstab, O(TkScroll, jump), tkbool, 50 "orient", OPTstab, O(TkScroll, orient), tkorient, 51 nil 52 }; 53 54 static 55 TkEbind b[] = 56 { 57 {TkLeave, "%W activate {}"}, 58 {TkEnter, "%W activate [%W identify %x %y]"}, 59 {TkMotion, "%W activate [%W identify %x %y]"}, 60 {TkButton1P|TkMotion, "%W tkScrollDrag %x %y"}, 61 {TkButton1P, "%W tkScrolBut1P %x %y"}, 62 {TkButton1P|TkDouble, "%W tkScrolBut1P %x %y"}, 63 {TkButton1R, "%W tkScrolBut1R; %W activate [%W identify %x %y]"}, 64 {TkButton2P, "%W tkScrolBut2P [%W fraction %x %y]"}, 65 }; 66 67 static char* 68 tkinitscroll(Tk *tk) 69 { 70 int gap; 71 TkScroll *tks; 72 73 tks = TKobj(TkScroll, tk); 74 75 gap = 2*tk->borderwidth; 76 if(tks->orient == Tkvertical) { 77 if(tk->req.width == 0) 78 tk->req.width = Triangle + gap; 79 if(tk->req.height == 0) 80 tk->req.height = 2*Triangle + gap + 6*Elembw; 81 } 82 else { 83 if(tk->req.width == 0) 84 tk->req.width = 2*Triangle + gap + 6*Elembw; 85 if(tk->req.height == 0) 86 tk->req.height = Triangle + gap; 87 } 88 89 90 return tkbindings(tk->env->top, tk, b, nelem(b)); 91 } 92 93 char* 94 tkscrollbar(TkTop *t, char *arg, char **ret) 95 { 96 Tk *tk; 97 char *e; 98 TkName *names; 99 TkScroll *tks; 100 TkOptab tko[3]; 101 102 tk = tknewobj(t, TKscrollbar, sizeof(Tk)+sizeof(TkScroll)); 103 if(tk == nil) 104 return TkNomem; 105 106 tks = TKobj(TkScroll, tk); 107 108 tk->relief = TKflat; 109 tk->borderwidth = 1; 110 tks->activer = TKraised; 111 tks->orient = Tkvertical; 112 113 tko[0].ptr = tk; 114 tko[0].optab = tkgeneric; 115 tko[1].ptr = tks; 116 tko[1].optab = opts; 117 tko[2].ptr = nil; 118 119 names = nil; 120 e = tkparse(t, arg, tko, &names); 121 if(e != nil) { 122 tkfreeobj(tk); 123 return e; 124 } 125 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 126 127 e = tkinitscroll(tk); 128 if(e != nil) { 129 tkfreeobj(tk); 130 return e; 131 } 132 133 e = tkaddchild(t, tk, &names); 134 tkfreename(names); 135 if(e != nil) { 136 tkfreeobj(tk); 137 return e; 138 } 139 tk->name->link = nil; 140 141 return tkvalue(ret, "%s", tk->name->name); 142 } 143 144 static char* 145 tkscrollcget(Tk *tk, char *arg, char **val) 146 { 147 TkOptab tko[3]; 148 TkScroll *tks = TKobj(TkScroll, tk); 149 150 tko[0].ptr = tk; 151 tko[0].optab = tkgeneric; 152 tko[1].ptr = tks; 153 tko[1].optab = opts; 154 tko[2].ptr = nil; 155 156 return tkgencget(tko, arg, val, tk->env->top); 157 } 158 159 void 160 tkfreescrlb(Tk *tk) 161 { 162 TkScroll *tks = TKobj(TkScroll, tk); 163 164 if(tks->cmd != nil) 165 free(tks->cmd); 166 } 167 168 static void 169 drawarrow(TkScroll *tks, Image *i, Point p[3], TkEnv *e, int activef, int buttonf) 170 { 171 Image *l, *d, *t; 172 int bgnd; 173 174 bgnd = TkCbackgnd; 175 if(tks->flag & (activef|buttonf)) { 176 bgnd = TkCactivebgnd; 177 fillpoly(i, p, 3, ~0, tkgc(e, bgnd), p[0]); 178 } 179 180 l = tkgc(e, bgnd+TkLightshade); 181 d = tkgc(e, bgnd+TkDarkshade); 182 if(tks->flag & buttonf) { 183 t = d; 184 d = l; 185 l = t; 186 } 187 line(i, p[1], p[2], 0, 0, Tribw-1, d, p[1]); 188 line(i, p[2], p[0], 0, 0, Tribw-1, d, p[2]); 189 line(i, p[0], p[1], 0, 0, Tribw-1, l, p[0]); 190 } 191 192 static void 193 drawslider(TkScroll *tks, Image *i, Point o, int w, int h, TkEnv *e) 194 { 195 Image *l, *d, *t; 196 Rectangle r; 197 int bgnd; 198 199 bgnd = TkCbackgnd; 200 if(tks->flag & (ActiveB1|ButtonB1)) { 201 r.min = o; 202 r.max.x = o.x + w + Elembw*2; 203 r.max.y = o.y + h + Elembw*2; 204 bgnd = TkCactivebgnd; 205 draw(i, r, tkgc(e, bgnd), nil, ZP); 206 } 207 208 l = tkgc(e, bgnd+TkLightshade); 209 d = tkgc(e, bgnd+TkDarkshade); 210 if(tks->flag & ButtonB1) 211 tkbevel(i, o, w, h, Scrollbw, d, l); 212 else 213 tkbevel(i, o, w, h, Scrollbw, l, d); 214 } 215 216 static void 217 tkvscroll(Tk *tk, TkScroll *tks, Image *i, Point size) 218 { 219 TkEnv *e; 220 Point p[3], o; 221 Image *d, *l; 222 int bo, w, h, triangle, bgnd; 223 224 e = tk->env; 225 226 triangle = tk->act.width - Elembw; 227 228 bo = tk->borderwidth + Elembw; 229 p[0].x = size.x/2; 230 p[0].y = bo; 231 p[1].x = p[0].x - triangle/2; 232 p[1].y = p[0].y + triangle; 233 p[2].x = p[0].x + triangle/2; 234 p[2].y = p[0].y + triangle; 235 drawarrow(tks, i, p, e, ActiveA1, ButtonA1); 236 237 tks->a1 = p[2].y; 238 h = p[2].y + Elembw; 239 240 p[0].y = size.y - bo - 1; 241 p[1].y = p[0].y - triangle; 242 p[2].y = p[0].y - triangle; 243 drawarrow(tks, i, p, e, ActiveA2, ButtonA2); 244 245 tks->a2 = p[2].y; 246 247 o.x = tk->borderwidth ; 248 o.y = bo + triangle + 2*Elembw; 249 w = size.x - 2*bo; 250 h = p[2].y - 2*Elembw - h - 2*tk->borderwidth; 251 252 o.y += TKF2I(tks->top*h); 253 h *= tks->bot - tks->top; 254 h = TKF2I(h); 255 256 tks->t1 = o.y - Elembw; 257 tks->t2 = o.y + h + Elembw; 258 259 drawslider(tks, i, o, w, h, e); 260 } 261 262 static void 263 tkhscroll(Tk *tk, TkScroll *tks, Image *i, Point size) 264 { 265 TkEnv *e; 266 Point p[3], o; 267 Image *d, *l; 268 int bo, w, h, triangle, bgnd; 269 270 e = tk->env; 271 272 triangle = tk->act.height - Elembw; 273 274 bo = tk->borderwidth + Elembw; 275 p[0].x = bo; 276 p[0].y = size.y/2; 277 p[1].x = p[0].x + triangle; 278 p[1].y = p[0].y - triangle/2 + 1; 279 p[2].x = p[0].x + triangle; 280 p[2].y = p[0].y + triangle/2 - 2; 281 drawarrow(tks, i, p, e, ActiveA1, ButtonA1); 282 283 tks->a1 = p[2].x; 284 w = p[2].x + Elembw; 285 286 p[0].x = size.x - bo - 1; 287 p[1].x = p[0].x - triangle; 288 p[2].x = p[0].x - triangle; 289 drawarrow(tks, i, p, e, ActiveA2, ButtonA2); 290 291 tks->a2 = p[2].x; 292 293 o.x = bo + triangle + 2*Elembw; 294 o.y = tk->borderwidth; 295 w = p[2].x - 2*Elembw - w - 2*tk->borderwidth; 296 h = size.y - 2*bo; 297 298 o.x += TKF2I(tks->top*w); 299 w *= tks->bot - tks->top; 300 w = TKF2I(w); 301 302 tks->t1 = o.x - Elembw; 303 tks->t2 = o.x + w + Elembw; 304 305 drawslider(tks, i, o, w, h, e); 306 } 307 308 char* 309 tkdrawscrlb(Tk *tk, Point orig) 310 { 311 Point p; 312 TkEnv *e; 313 Rectangle r; 314 Image *i, *dst; 315 TkScroll *tks = TKobj(TkScroll, tk); 316 317 e = tk->env; 318 319 dst = tkimageof(tk); 320 if(dst == nil) 321 return nil; 322 323 r.min = ZP; 324 r.max.x = tk->act.width + 2*tk->borderwidth; 325 r.max.y = tk->act.height + 2*tk->borderwidth; 326 327 i = tkitmp(e, r.max, TkCbackgnd); 328 if(i == nil) 329 return nil; 330 331 if(tks->orient == Tkvertical) 332 tkvscroll(tk, tks, i, r.max); 333 else 334 tkhscroll(tk, tks, i, r.max); 335 336 tkdrawrelief(i, tk, ZP, TkCbackgnd, tk->relief); 337 338 p.x = tk->act.x + orig.x; 339 p.y = tk->act.y + orig.y; 340 r = rectaddpt(r, p); 341 draw(dst, r, i, nil, ZP); 342 343 return nil; 344 } 345 346 /* Widget Commands (+ means implemented) 347 +activate 348 +cget 349 +configure 350 +delta 351 +fraction 352 +get 353 +identify 354 +set 355 */ 356 357 static char* 358 tkscrollconf(Tk *tk, char *arg, char **val) 359 { 360 char *e; 361 TkGeom g; 362 int bd; 363 TkOptab tko[3]; 364 TkScroll *tks = TKobj(TkScroll, tk); 365 366 tko[0].ptr = tk; 367 tko[0].optab = tkgeneric; 368 tko[1].ptr = tks; 369 tko[1].optab = opts; 370 tko[2].ptr = nil; 371 372 if(*arg == '\0') 373 return tkconflist(tko, val); 374 375 g = tk->req; 376 bd = tk->borderwidth; 377 e = tkparse(tk->env->top, arg, tko, nil); 378 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 379 tkgeomchg(tk, &g, bd); 380 381 tk->dirty = tkrect(tk, 1); 382 return e; 383 } 384 385 static char* 386 tkscrollactivate(Tk *tk, char *arg, char **val) 387 { 388 int s, gotarg; 389 char buf[Tkmaxitem]; 390 TkScroll *tks = TKobj(TkScroll, tk); 391 392 USED(val); 393 tkword(tk->env->top, arg, buf, buf+sizeof(buf), &gotarg); 394 s = tks->flag; 395 if (!gotarg) { 396 char *a; 397 if (s & ActiveA1) 398 a = "arrow1"; 399 else if (s & ActiveA2) 400 a = "arrow2"; 401 else if (s & ActiveB1) 402 a = "slider"; 403 else 404 a = ""; 405 return tkvalue(val, a); 406 } 407 tks->flag &= ~(ActiveA1 | ActiveA2 | ActiveB1); 408 if(strcmp(buf, "arrow1") == 0) 409 tks->flag |= ActiveA1; 410 else 411 if(strcmp(buf, "arrow2") == 0) 412 tks->flag |= ActiveA2; 413 else 414 if(strcmp(buf, "slider") == 0) 415 tks->flag |= ActiveB1; 416 417 if(s ^ tks->flag) 418 tk->dirty = tkrect(tk, 1); 419 return nil; 420 } 421 422 static char* 423 tkscrollset(Tk *tk, char *arg, char **val) 424 { 425 TkTop *t; 426 char *e; 427 TkScroll *tks = TKobj(TkScroll, tk); 428 429 USED(val); 430 t = tk->env->top; 431 e = tkfracword(t, &arg, &tks->top, nil); 432 if (e != nil) 433 return e; 434 e = tkfracword(t, &arg, &tks->bot, nil); 435 if (e != nil) 436 return e; 437 if(tks->top < 0) 438 tks->top = 0; 439 if(tks->top > TKI2F(1)) 440 tks->top = TKI2F(1); 441 if(tks->bot < 0) 442 tks->bot = 0; 443 if(tks->bot > TKI2F(1)) 444 tks->bot = TKI2F(1); 445 446 tk->dirty = tkrect(tk, 1); 447 return nil; 448 } 449 450 static char* 451 tkscrolldelta(Tk *tk, char *arg, char **val) 452 { 453 int l, delta; 454 char buf[Tkmaxitem]; 455 TkScroll *tks = TKobj(TkScroll, tk); 456 457 arg = tkitem(buf, arg); 458 if(tks->orient == Tkvertical) 459 tkitem(buf, arg); 460 if(*arg == '\0' || *buf == '\0') 461 return TkBadvl; 462 463 l = tks->a2-tks->a1-4*Elembw; 464 delta = TKI2F(1); 465 if(l != 0) 466 delta = TKI2F(atoi(buf)) / l; 467 tkfprint(buf, delta); 468 469 return tkvalue(val, "%s", buf); 470 } 471 472 static char* 473 tkscrollget(Tk *tk, char *arg, char **val) 474 { 475 char *v, buf[Tkmaxitem]; 476 TkScroll *tks = TKobj(TkScroll, tk); 477 478 USED(arg); 479 v = tkfprint(buf, tks->top); 480 *v++ = ' '; 481 tkfprint(v, tks->bot); 482 483 return tkvalue(val, "%s", buf); 484 } 485 486 static char* 487 tkscrollidentify(Tk *tk, char *arg, char **val) 488 { 489 int gotarg; 490 TkTop *t; 491 char *v, buf[Tkmaxitem]; 492 Point p; 493 TkScroll *tks = TKobj(TkScroll, tk); 494 495 t = tk->env->top; 496 arg = tkword(t, arg, buf, buf+sizeof(buf), &gotarg); 497 if (!gotarg) 498 return TkBadvl; 499 p.x = atoi(buf); 500 tkword(t, arg, buf, buf+sizeof(buf), &gotarg); 501 if (!gotarg) 502 return TkBadvl; 503 p.y = atoi(buf); 504 if (!ptinrect(p, tkrect(tk, 0))) 505 return nil; 506 if (tks->orient == Tkvertical) 507 p.x = p.y; 508 p.x += tk->borderwidth; 509 510 v = ""; 511 if(p.x <= tks->a1) 512 v = "arrow1"; 513 if(p.x > tks->a1 && p.x <= tks->t1) 514 v = "trough1"; 515 if(p.x > tks->t1 && p.x < tks->t2) 516 v = "slider"; 517 if(p.x >= tks->t2 && p.x < tks->a2) 518 v = "trough2"; 519 if(p.x >= tks->a2) 520 v = "arrow2"; 521 return tkvalue(val, "%s", v); 522 } 523 524 static char* 525 tkscrollfraction(Tk *tk, char *arg, char **val) 526 { 527 int len, frac, pos; 528 char buf[Tkmaxitem]; 529 TkScroll *tks = TKobj(TkScroll, tk); 530 531 arg = tkitem(buf, arg); 532 if(tks->orient == Tkvertical) 533 tkitem(buf, arg); 534 if(*arg == '\0' || *buf == '\0') 535 return TkBadvl; 536 537 pos = atoi(buf); 538 if(pos < tks->a1) 539 pos = tks->a1; 540 if(pos > tks->a2) 541 pos = tks->a2; 542 len = tks->a2 - tks->a1 - 4*Elembw; 543 frac = TKI2F(1); 544 if(len != 0) 545 frac = TKI2F(pos-tks->a1)/len; 546 tkfprint(buf, frac); 547 return tkvalue(val, "%s", buf); 548 } 549 550 static char* 551 tkScrolBut1R(Tk *tk, char *arg, char **val) 552 { 553 TkScroll *tks = TKobj(TkScroll, tk); 554 555 USED(val); 556 USED(arg); 557 tkcancelrepeat(tk); 558 tks->flag &= ~(ActiveA1|ActiveA2|ActiveB1|ButtonA1|ButtonA2|ButtonB1|Autorepeat); 559 tk->dirty = tkrect(tk, 1); 560 return nil; 561 } 562 563 /* tkScrolBut2P fraction */ 564 static char* 565 tkScrolBut2P(Tk *tk, char *arg, char **val) 566 { 567 TkTop *t; 568 char *e, buf[Tkmaxitem], fracbuf[Tkmaxitem]; 569 TkScroll *tks = TKobj(TkScroll, tk); 570 571 572 USED(val); 573 t = tk->env->top; 574 575 if(arg[0] == '\0') 576 return TkBadvl; 577 578 tkword(t, arg, fracbuf, fracbuf+sizeof(fracbuf), nil); 579 580 e = nil; 581 if(tks->cmd != nil) { 582 snprint(buf, sizeof(buf), "%s moveto %s", tks->cmd, fracbuf); 583 e = tkexec(t, buf, nil); 584 } 585 return e; 586 } 587 588 static void 589 sbrepeat(Tk *tk, void *v, int cancelled) 590 { 591 char *e, buf[Tkmaxitem]; 592 TkScroll *tks = TKobj(TkScroll, tk); 593 char *fmt = (char *)v; 594 595 if (cancelled) { 596 tks->flag &= ~Autorepeat; 597 return; 598 } 599 600 if(tks->cmd != nil && fmt != nil) { 601 snprint(buf, sizeof(buf), fmt, tks->cmd); 602 e = tkexec(tk->env->top, buf, nil); 603 if (e != nil) { 604 tks->flag &= ~Autorepeat; 605 tkcancelrepeat(tk); 606 } else 607 tkupdate(tk->env->top); 608 } 609 } 610 611 /* tkScrolBut1P %x %y */ 612 static char* 613 tkScrolBut1P(Tk *tk, char *arg, char **val) 614 { 615 int pix; 616 TkTop *t; 617 char *e, *fmt, buf[Tkmaxitem]; 618 TkScroll *tks = TKobj(TkScroll, tk); 619 620 USED(val); 621 t = tk->env->top; 622 623 if (tks->flag & Autorepeat) 624 return nil; 625 arg = tkword(t, arg, buf, buf+sizeof(buf), nil); 626 if(tks->orient == Tkvertical) 627 tkword(t, arg, buf, buf+sizeof(buf), nil); 628 if(buf[0] == '\0') 629 return TkBadvl; 630 631 pix = atoi(buf); 632 633 tks->dragpix = pix; 634 tks->dragtop = tks->top; 635 tks->dragbot = tks->bot; 636 637 pix += tk->borderwidth; 638 639 fmt = nil; 640 e = nil; 641 if(pix <= tks->a1) { 642 fmt = "%s scroll -1 unit"; 643 tks->flag |= ButtonA1; 644 } 645 if(pix > tks->a1 && pix <= tks->t1) 646 fmt = "%s scroll -1 page"; 647 if(pix > tks->t1 && pix < tks->t2) 648 tks->flag |= ButtonB1; 649 if(pix >= tks->t2 && pix < tks->a2) 650 fmt = "%s scroll 1 page"; 651 if(pix >= tks->a2) { 652 fmt = "%s scroll 1 unit"; 653 tks->flag |= ButtonA2; 654 } 655 if(tks->cmd != nil && fmt != nil) { 656 snprint(buf, sizeof(buf), fmt, tks->cmd); 657 e = tkexec(t, buf, nil); 658 tks->flag |= Autorepeat; 659 tkrepeat(tk, sbrepeat, fmt, TkRptpause, TkRptinterval); 660 } 661 tk->dirty = tkrect(tk, 1); 662 return e; 663 } 664 665 /* tkScrolDrag %x %y */ 666 static char* 667 tkScrollDrag(Tk *tk, char *arg, char **val) 668 { 669 TkTop *t; 670 int pix, delta; 671 char frac[32], buf[Tkmaxitem]; 672 TkScroll *tks = TKobj(TkScroll, tk); 673 674 USED(val); 675 t = tk->env->top; 676 677 if (tks->flag & Autorepeat) 678 return nil; 679 if((tks->flag & ButtonB1) == 0) 680 return nil; 681 682 arg = tkword(t, arg, buf, buf+sizeof(buf), nil); 683 if(tks->orient == Tkvertical) 684 tkword(t, arg, buf, buf+sizeof(buf), nil); 685 if(buf[0] == '\0') 686 return TkBadvl; 687 688 pix = atoi(buf); 689 690 delta = TKI2F(pix-tks->dragpix); 691 if ( tks->a2 == tks->a1 ) 692 return TkBadvl; 693 delta = delta/(tks->a2-tks->a1-4*Elembw); 694 if(tks->jump == BoolT) { 695 if(tks->dragtop+delta >= 0 && 696 tks->dragbot+delta <= TKI2F(1)) { 697 tks->top = tks->dragtop+delta; 698 tks->bot = tks->dragbot+delta; 699 } 700 return nil; 701 } 702 if(tks->cmd != nil) { 703 delta += tks->dragtop; 704 if(delta < 0) 705 delta = 0; 706 if(delta > TKI2F(1)) 707 delta = TKI2F(1); 708 tkfprint(frac, delta); 709 snprint(buf, sizeof(buf), "%s moveto %s", tks->cmd, frac); 710 return tkexec(t, buf, nil); 711 } 712 return nil; 713 } 714 715 TkCmdtab tkscrlbcmd[] = 716 { 717 "activate", tkscrollactivate, 718 "cget", tkscrollcget, 719 "configure", tkscrollconf, 720 "delta", tkscrolldelta, 721 "fraction", tkscrollfraction, 722 "get", tkscrollget, 723 "identify", tkscrollidentify, 724 "set", tkscrollset, 725 "tkScrollDrag", tkScrollDrag, 726 "tkScrolBut1P", tkScrolBut1P, 727 "tkScrolBut1R", tkScrolBut1R, 728 "tkScrolBut2P", tkScrolBut2P, 729 nil 730 }; 731 732 TkMethod scrollbarmethod = { 733 "scrollbar", 734 tkscrlbcmd, 735 tkfreescrlb, 736 tkdrawscrlb 737 }; 738