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; 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 int bo, w, h, triangle; 222 223 e = tk->env; 224 225 triangle = tk->act.width - Elembw; 226 227 bo = tk->borderwidth + Elembw; 228 p[0].x = size.x/2; 229 p[0].y = bo; 230 p[1].x = p[0].x - triangle/2; 231 p[1].y = p[0].y + triangle; 232 p[2].x = p[0].x + triangle/2; 233 p[2].y = p[0].y + triangle; 234 drawarrow(tks, i, p, e, ActiveA1, ButtonA1); 235 236 tks->a1 = p[2].y; 237 h = p[2].y + Elembw; 238 239 p[0].y = size.y - bo - 1; 240 p[1].y = p[0].y - triangle; 241 p[2].y = p[0].y - triangle; 242 drawarrow(tks, i, p, e, ActiveA2, ButtonA2); 243 244 tks->a2 = p[2].y; 245 246 o.x = tk->borderwidth ; 247 o.y = bo + triangle + 2*Elembw; 248 w = size.x - 2*bo; 249 h = p[2].y - 2*Elembw - h - 2*tk->borderwidth; 250 251 o.y += TKF2I(tks->top*h); 252 h *= tks->bot - tks->top; 253 h = TKF2I(h); 254 255 tks->t1 = o.y - Elembw; 256 tks->t2 = o.y + h + Elembw; 257 258 drawslider(tks, i, o, w, h, e); 259 } 260 261 static void 262 tkhscroll(Tk *tk, TkScroll *tks, Image *i, Point size) 263 { 264 TkEnv *e; 265 Point p[3], o; 266 int bo, w, h, triangle; 267 268 e = tk->env; 269 270 triangle = tk->act.height - Elembw; 271 272 bo = tk->borderwidth + Elembw; 273 p[0].x = bo; 274 p[0].y = size.y/2; 275 p[1].x = p[0].x + triangle; 276 p[1].y = p[0].y - triangle/2 + 1; 277 p[2].x = p[0].x + triangle; 278 p[2].y = p[0].y + triangle/2 - 2; 279 drawarrow(tks, i, p, e, ActiveA1, ButtonA1); 280 281 tks->a1 = p[2].x; 282 w = p[2].x + Elembw; 283 284 p[0].x = size.x - bo - 1; 285 p[1].x = p[0].x - triangle; 286 p[2].x = p[0].x - triangle; 287 drawarrow(tks, i, p, e, ActiveA2, ButtonA2); 288 289 tks->a2 = p[2].x; 290 291 o.x = bo + triangle + 2*Elembw; 292 o.y = tk->borderwidth; 293 w = p[2].x - 2*Elembw - w - 2*tk->borderwidth; 294 h = size.y - 2*bo; 295 296 o.x += TKF2I(tks->top*w); 297 w *= tks->bot - tks->top; 298 w = TKF2I(w); 299 300 tks->t1 = o.x - Elembw; 301 tks->t2 = o.x + w + Elembw; 302 303 drawslider(tks, i, o, w, h, e); 304 } 305 306 char* 307 tkdrawscrlb(Tk *tk, Point orig) 308 { 309 Point p; 310 TkEnv *e; 311 Rectangle r; 312 Image *i, *dst; 313 TkScroll *tks = TKobj(TkScroll, tk); 314 315 e = tk->env; 316 317 dst = tkimageof(tk); 318 if(dst == nil) 319 return nil; 320 321 r.min = ZP; 322 r.max.x = tk->act.width + 2*tk->borderwidth; 323 r.max.y = tk->act.height + 2*tk->borderwidth; 324 325 i = tkitmp(e, r.max, TkCbackgnd); 326 if(i == nil) 327 return nil; 328 329 if(tks->orient == Tkvertical) 330 tkvscroll(tk, tks, i, r.max); 331 else 332 tkhscroll(tk, tks, i, r.max); 333 334 tkdrawrelief(i, tk, ZP, TkCbackgnd, tk->relief); 335 336 p.x = tk->act.x + orig.x; 337 p.y = tk->act.y + orig.y; 338 r = rectaddpt(r, p); 339 draw(dst, r, i, nil, ZP); 340 341 return nil; 342 } 343 344 /* Widget Commands (+ means implemented) 345 +activate 346 +cget 347 +configure 348 +delta 349 +fraction 350 +get 351 +identify 352 +set 353 */ 354 355 static char* 356 tkscrollconf(Tk *tk, char *arg, char **val) 357 { 358 char *e; 359 TkGeom g; 360 int bd; 361 TkOptab tko[3]; 362 TkScroll *tks = TKobj(TkScroll, tk); 363 364 tko[0].ptr = tk; 365 tko[0].optab = tkgeneric; 366 tko[1].ptr = tks; 367 tko[1].optab = opts; 368 tko[2].ptr = nil; 369 370 if(*arg == '\0') 371 return tkconflist(tko, val); 372 373 g = tk->req; 374 bd = tk->borderwidth; 375 e = tkparse(tk->env->top, arg, tko, nil); 376 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 377 tkgeomchg(tk, &g, bd); 378 379 tk->dirty = tkrect(tk, 1); 380 return e; 381 } 382 383 static char* 384 tkscrollactivate(Tk *tk, char *arg, char **val) 385 { 386 int s, gotarg; 387 char buf[Tkmaxitem]; 388 TkScroll *tks = TKobj(TkScroll, tk); 389 390 USED(val); 391 tkword(tk->env->top, arg, buf, buf+sizeof(buf), &gotarg); 392 s = tks->flag; 393 if (!gotarg) { 394 char *a; 395 if (s & ActiveA1) 396 a = "arrow1"; 397 else if (s & ActiveA2) 398 a = "arrow2"; 399 else if (s & ActiveB1) 400 a = "slider"; 401 else 402 a = ""; 403 return tkvalue(val, a); 404 } 405 tks->flag &= ~(ActiveA1 | ActiveA2 | ActiveB1); 406 if(strcmp(buf, "arrow1") == 0) 407 tks->flag |= ActiveA1; 408 else 409 if(strcmp(buf, "arrow2") == 0) 410 tks->flag |= ActiveA2; 411 else 412 if(strcmp(buf, "slider") == 0) 413 tks->flag |= ActiveB1; 414 415 if(s ^ tks->flag) 416 tk->dirty = tkrect(tk, 1); 417 return nil; 418 } 419 420 static char* 421 tkscrollset(Tk *tk, char *arg, char **val) 422 { 423 TkTop *t; 424 char *e; 425 TkScroll *tks = TKobj(TkScroll, tk); 426 427 USED(val); 428 t = tk->env->top; 429 e = tkfracword(t, &arg, &tks->top, nil); 430 if (e != nil) 431 return e; 432 e = tkfracword(t, &arg, &tks->bot, nil); 433 if (e != nil) 434 return e; 435 if(tks->top < 0) 436 tks->top = 0; 437 if(tks->top > TKI2F(1)) 438 tks->top = TKI2F(1); 439 if(tks->bot < 0) 440 tks->bot = 0; 441 if(tks->bot > TKI2F(1)) 442 tks->bot = TKI2F(1); 443 444 tk->dirty = tkrect(tk, 1); 445 return nil; 446 } 447 448 static char* 449 tkscrolldelta(Tk *tk, char *arg, char **val) 450 { 451 int l, delta; 452 char buf[Tkmaxitem]; 453 TkScroll *tks = TKobj(TkScroll, tk); 454 455 arg = tkitem(buf, arg); 456 if(tks->orient == Tkvertical) 457 tkitem(buf, arg); 458 if(*arg == '\0' || *buf == '\0') 459 return TkBadvl; 460 461 l = tks->a2-tks->a1-4*Elembw; 462 delta = TKI2F(1); 463 if(l != 0) 464 delta = TKI2F(atoi(buf)) / l; 465 tkfprint(buf, delta); 466 467 return tkvalue(val, "%s", buf); 468 } 469 470 static char* 471 tkscrollget(Tk *tk, char *arg, char **val) 472 { 473 char *v, buf[Tkmaxitem]; 474 TkScroll *tks = TKobj(TkScroll, tk); 475 476 USED(arg); 477 v = tkfprint(buf, tks->top); 478 *v++ = ' '; 479 tkfprint(v, tks->bot); 480 481 return tkvalue(val, "%s", buf); 482 } 483 484 static char* 485 tkscrollidentify(Tk *tk, char *arg, char **val) 486 { 487 int gotarg; 488 TkTop *t; 489 char *v, buf[Tkmaxitem]; 490 Point p; 491 TkScroll *tks = TKobj(TkScroll, tk); 492 493 t = tk->env->top; 494 arg = tkword(t, arg, buf, buf+sizeof(buf), &gotarg); 495 if (!gotarg) 496 return TkBadvl; 497 p.x = atoi(buf); 498 tkword(t, arg, buf, buf+sizeof(buf), &gotarg); 499 if (!gotarg) 500 return TkBadvl; 501 p.y = atoi(buf); 502 if (!ptinrect(p, tkrect(tk, 0))) 503 return nil; 504 if (tks->orient == Tkvertical) 505 p.x = p.y; 506 p.x += tk->borderwidth; 507 508 v = ""; 509 if(p.x <= tks->a1) 510 v = "arrow1"; 511 if(p.x > tks->a1 && p.x <= tks->t1) 512 v = "trough1"; 513 if(p.x > tks->t1 && p.x < tks->t2) 514 v = "slider"; 515 if(p.x >= tks->t2 && p.x < tks->a2) 516 v = "trough2"; 517 if(p.x >= tks->a2) 518 v = "arrow2"; 519 return tkvalue(val, "%s", v); 520 } 521 522 static char* 523 tkscrollfraction(Tk *tk, char *arg, char **val) 524 { 525 int len, frac, pos; 526 char buf[Tkmaxitem]; 527 TkScroll *tks = TKobj(TkScroll, tk); 528 529 arg = tkitem(buf, arg); 530 if(tks->orient == Tkvertical) 531 tkitem(buf, arg); 532 if(*arg == '\0' || *buf == '\0') 533 return TkBadvl; 534 535 pos = atoi(buf); 536 if(pos < tks->a1) 537 pos = tks->a1; 538 if(pos > tks->a2) 539 pos = tks->a2; 540 len = tks->a2 - tks->a1 - 4*Elembw; 541 frac = TKI2F(1); 542 if(len != 0) 543 frac = TKI2F(pos-tks->a1)/len; 544 tkfprint(buf, frac); 545 return tkvalue(val, "%s", buf); 546 } 547 548 static char* 549 tkScrolBut1R(Tk *tk, char *arg, char **val) 550 { 551 TkScroll *tks = TKobj(TkScroll, tk); 552 553 USED(val); 554 USED(arg); 555 tkcancelrepeat(tk); 556 tks->flag &= ~(ActiveA1|ActiveA2|ActiveB1|ButtonA1|ButtonA2|ButtonB1|Autorepeat); 557 tk->dirty = tkrect(tk, 1); 558 return nil; 559 } 560 561 /* tkScrolBut2P fraction */ 562 static char* 563 tkScrolBut2P(Tk *tk, char *arg, char **val) 564 { 565 TkTop *t; 566 char *e, buf[Tkmaxitem], fracbuf[Tkmaxitem]; 567 TkScroll *tks = TKobj(TkScroll, tk); 568 569 570 USED(val); 571 t = tk->env->top; 572 573 if(arg[0] == '\0') 574 return TkBadvl; 575 576 tkword(t, arg, fracbuf, fracbuf+sizeof(fracbuf), nil); 577 578 e = nil; 579 if(tks->cmd != nil) { 580 snprint(buf, sizeof(buf), "%s moveto %s", tks->cmd, fracbuf); 581 e = tkexec(t, buf, nil); 582 } 583 return e; 584 } 585 586 static void 587 sbrepeat(Tk *tk, void *v, int cancelled) 588 { 589 char *e, buf[Tkmaxitem]; 590 TkScroll *tks = TKobj(TkScroll, tk); 591 char *fmt = (char *)v; 592 593 if (cancelled) { 594 tks->flag &= ~Autorepeat; 595 return; 596 } 597 598 if(tks->cmd != nil && fmt != nil) { 599 snprint(buf, sizeof(buf), fmt, tks->cmd); 600 e = tkexec(tk->env->top, buf, nil); 601 if (e != nil) { 602 tks->flag &= ~Autorepeat; 603 tkcancelrepeat(tk); 604 } else 605 tkupdate(tk->env->top); 606 } 607 } 608 609 /* tkScrolBut1P %x %y */ 610 static char* 611 tkScrolBut1P(Tk *tk, char *arg, char **val) 612 { 613 int pix; 614 TkTop *t; 615 char *e, *fmt, buf[Tkmaxitem]; 616 TkScroll *tks = TKobj(TkScroll, tk); 617 618 USED(val); 619 t = tk->env->top; 620 621 if (tks->flag & Autorepeat) 622 return nil; 623 arg = tkword(t, arg, buf, buf+sizeof(buf), nil); 624 if(tks->orient == Tkvertical) 625 tkword(t, arg, buf, buf+sizeof(buf), nil); 626 if(buf[0] == '\0') 627 return TkBadvl; 628 629 pix = atoi(buf); 630 631 tks->dragpix = pix; 632 tks->dragtop = tks->top; 633 tks->dragbot = tks->bot; 634 635 pix += tk->borderwidth; 636 637 fmt = nil; 638 e = nil; 639 if(pix <= tks->a1) { 640 fmt = "%s scroll -1 unit"; 641 tks->flag |= ButtonA1; 642 } 643 if(pix > tks->a1 && pix <= tks->t1) 644 fmt = "%s scroll -1 page"; 645 if(pix > tks->t1 && pix < tks->t2) 646 tks->flag |= ButtonB1; 647 if(pix >= tks->t2 && pix < tks->a2) 648 fmt = "%s scroll 1 page"; 649 if(pix >= tks->a2) { 650 fmt = "%s scroll 1 unit"; 651 tks->flag |= ButtonA2; 652 } 653 if(tks->cmd != nil && fmt != nil) { 654 snprint(buf, sizeof(buf), fmt, tks->cmd); 655 e = tkexec(t, buf, nil); 656 tks->flag |= Autorepeat; 657 tkrepeat(tk, sbrepeat, fmt, TkRptpause, TkRptinterval); 658 } 659 tk->dirty = tkrect(tk, 1); 660 return e; 661 } 662 663 /* tkScrolDrag %x %y */ 664 static char* 665 tkScrollDrag(Tk *tk, char *arg, char **val) 666 { 667 TkTop *t; 668 int pix, delta; 669 char frac[32], buf[Tkmaxitem]; 670 TkScroll *tks = TKobj(TkScroll, tk); 671 672 USED(val); 673 t = tk->env->top; 674 675 if (tks->flag & Autorepeat) 676 return nil; 677 if((tks->flag & ButtonB1) == 0) 678 return nil; 679 680 arg = tkword(t, arg, buf, buf+sizeof(buf), nil); 681 if(tks->orient == Tkvertical) 682 tkword(t, arg, buf, buf+sizeof(buf), nil); 683 if(buf[0] == '\0') 684 return TkBadvl; 685 686 pix = atoi(buf); 687 688 delta = TKI2F(pix-tks->dragpix); 689 if ( tks->a2 == tks->a1 ) 690 return TkBadvl; 691 delta = delta/(tks->a2-tks->a1-4*Elembw); 692 if(tks->jump == BoolT) { 693 if(tks->dragtop+delta >= 0 && 694 tks->dragbot+delta <= TKI2F(1)) { 695 tks->top = tks->dragtop+delta; 696 tks->bot = tks->dragbot+delta; 697 } 698 return nil; 699 } 700 if(tks->cmd != nil) { 701 delta += tks->dragtop; 702 if(delta < 0) 703 delta = 0; 704 if(delta > TKI2F(1)) 705 delta = TKI2F(1); 706 tkfprint(frac, delta); 707 snprint(buf, sizeof(buf), "%s moveto %s", tks->cmd, frac); 708 return tkexec(t, buf, nil); 709 } 710 return nil; 711 } 712 713 TkCmdtab tkscrlbcmd[] = 714 { 715 "activate", tkscrollactivate, 716 "cget", tkscrollcget, 717 "configure", tkscrollconf, 718 "delta", tkscrolldelta, 719 "fraction", tkscrollfraction, 720 "get", tkscrollget, 721 "identify", tkscrollidentify, 722 "set", tkscrollset, 723 "tkScrollDrag", tkScrollDrag, 724 "tkScrolBut1P", tkScrolBut1P, 725 "tkScrolBut1R", tkScrolBut1R, 726 "tkScrolBut2P", tkScrolBut2P, 727 nil 728 }; 729 730 TkMethod scrollbarmethod = { 731 "scrollbar", 732 tkscrlbcmd, 733 tkfreescrlb, 734 tkdrawscrlb 735 }; 736