1 #include "lib9.h" 2 #include "draw.h" 3 #include "keyboard.h" 4 #include "tk.h" 5 #include "listb.h" 6 7 #define O(t, e) ((long)(&((t*)0)->e)) 8 9 /* Layout constants */ 10 enum { 11 Listpadx = 2, /* X padding of text in listboxes */ 12 }; 13 14 typedef struct TkLentry TkLentry; 15 typedef struct TkListbox TkListbox; 16 17 struct TkLentry 18 { 19 TkLentry* link; 20 int flag; 21 int width; 22 char text[TKSTRUCTALIGN]; 23 }; 24 25 struct TkListbox 26 { 27 TkLentry* head; 28 TkLentry* anchor; 29 TkLentry* active; 30 int yelem; /* Y element at top of box */ 31 int xdelta; /* h-scroll position */ 32 int nitem; 33 int nwidth; 34 int selmode; 35 int sborderwidth; 36 char* xscroll; 37 char* yscroll; 38 }; 39 40 TkStab tkselmode[] = 41 { 42 "single", TKsingle, 43 "browse", TKbrowse, 44 "multiple", TKmultiple, 45 "extended", TKextended, 46 nil 47 }; 48 49 static 50 TkOption opts[] = 51 { 52 "xscrollcommand", OPTtext, O(TkListbox, xscroll), nil, 53 "yscrollcommand", OPTtext, O(TkListbox, yscroll), nil, 54 "selectmode", OPTstab, O(TkListbox, selmode), tkselmode, 55 "selectborderwidth", OPTnndist, O(TkListbox, sborderwidth), nil, 56 nil 57 }; 58 59 static 60 TkEbind b[] = 61 { 62 {TkButton1P, "%W tkListbButton1P %y"}, 63 {TkButton1R, "%W tkListbButton1R"}, 64 {TkButton1P|TkMotion, "%W tkListbButton1MP %y"}, 65 {TkMotion, ""}, 66 {TkKey, "%W tkListbKey 0x%K"}, 67 }; 68 69 70 static int 71 lineheight(Tk *tk) 72 { 73 TkListbox *l = TKobj(TkListbox, tk); 74 return tk->env->font->height+2*(l->sborderwidth+tk->highlightwidth); 75 } 76 77 char* 78 tklistbox(TkTop *t, char *arg, char **ret) 79 { 80 Tk *tk; 81 char *e; 82 TkName *names; 83 TkListbox *tkl; 84 TkOptab tko[3]; 85 86 tk = tknewobj(t, TKlistbox, sizeof(Tk)+sizeof(TkListbox)); 87 if(tk == nil) 88 return TkNomem; 89 90 tkl = TKobj(TkListbox, tk); 91 tkl->sborderwidth = 1; 92 tk->relief = TKsunken; 93 tk->borderwidth = 1; 94 tk->highlightwidth = 1; 95 tk->flag |= Tktakefocus; 96 tk->req.width = 170; 97 tk->req.height = lineheight(tk)*10; 98 99 tko[0].ptr = tk; 100 tko[0].optab = tkgeneric; 101 tko[1].ptr = tkl; 102 tko[1].optab = opts; 103 tko[2].ptr = nil; 104 105 names = nil; 106 e = tkparse(t, arg, tko, &names); 107 if(e != nil) { 108 tkfreeobj(tk); 109 return e; 110 } 111 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 112 113 e = tkbindings(t, tk, b, nelem(b)); 114 if(e != nil) { 115 tkfreeobj(tk); 116 return e; 117 } 118 119 e = tkaddchild(t, tk, &names); 120 tkfreename(names); 121 if(e != nil) { 122 tkfreeobj(tk); 123 return e; 124 } 125 tk->name->link = nil; 126 127 return tkvalue(ret, "%s", tk->name->name); 128 } 129 130 char* 131 tklistbcget(Tk *tk, char *arg, char **val) 132 { 133 TkOptab tko[3]; 134 TkListbox *tkl = TKobj(TkListbox, tk); 135 136 tko[0].ptr = tk; 137 tko[0].optab = tkgeneric; 138 tko[1].ptr = tkl; 139 tko[1].optab = opts; 140 tko[2].ptr = nil; 141 142 return tkgencget(tko, arg, val, tk->env->top); 143 } 144 145 void 146 tkfreelistb(Tk *tk) 147 { 148 TkLentry *e, *next; 149 TkListbox *l = TKobj(TkListbox, tk); 150 151 for(e = l->head; e; e = next) { 152 next = e->link; 153 free(e); 154 } 155 if(l->xscroll != nil) 156 free(l->xscroll); 157 if(l->yscroll != nil) 158 free(l->yscroll); 159 } 160 161 char* 162 tkdrawlistb(Tk *tk, Point orig) 163 { 164 Point p; 165 TkEnv *env; 166 TkLentry *e; 167 int lh, w, n, ly; 168 Rectangle r, a; 169 Image *i, *fg; 170 TkListbox *l = TKobj(TkListbox, tk); 171 172 env = tk->env; 173 174 r.min = ZP; 175 r.max.x = tk->act.width + 2*tk->borderwidth; 176 r.max.y = tk->act.height + 2*tk->borderwidth; 177 i = tkitmp(env, r.max, TkCbackgnd); 178 if(i == nil) 179 return nil; 180 181 w = tk->act.width; 182 if (w < l->nwidth) 183 w = l->nwidth; 184 lh = lineheight(tk); 185 ly = tk->borderwidth; 186 p.x = tk->borderwidth+l->sborderwidth+tk->highlightwidth+Listpadx-l->xdelta; 187 p.y = tk->borderwidth+l->sborderwidth+tk->highlightwidth; 188 n = 0; 189 for(e = l->head; e && ly < r.max.y; e = e->link) { 190 if(n++ < l->yelem) 191 continue; 192 193 a.min.x = tk->borderwidth; 194 a.min.y = ly; 195 a.max.x = a.min.x + tk->act.width; 196 a.max.y = a.min.y + lh; 197 if(e->flag & Tkactivated) { 198 draw(i, a, tkgc(env, TkCselectbgnd), nil, ZP); 199 } 200 201 if(e->flag & Tkactivated) 202 fg = tkgc(env, TkCselectfgnd); 203 else 204 fg = tkgc(env, TkCforegnd); 205 string(i, p, fg, p, env->font, e->text); 206 if((e->flag & Tkactive) && tkhaskeyfocus(tk)) { 207 a.min.x = tk->borderwidth-l->xdelta; 208 a.max.x = a.min.x+w; 209 a = insetrect(a, l->sborderwidth); 210 tkbox(i, a, tk->highlightwidth, fg); 211 } 212 ly += lh; 213 p.y += lh; 214 } 215 216 tkdrawrelief(i, tk, ZP, TkCbackgnd, tk->relief); 217 218 p.x = tk->act.x + orig.x; 219 p.y = tk->act.y + orig.y; 220 r = rectaddpt(r, p); 221 draw(tkimageof(tk), r, i, nil, ZP); 222 223 return nil; 224 } 225 226 int 227 tklindex(Tk *tk, char *buf) 228 { 229 int index; 230 TkListbox *l; 231 TkLentry *e, *s; 232 233 l = TKobj(TkListbox, tk); 234 235 if(*buf == '@') { 236 while(*buf && *buf != ',') 237 buf++; 238 index = l->yelem + atoi(buf+1)/lineheight(tk); 239 if (index < 0) 240 return 0; 241 if (index > l->nitem) 242 return l->nitem; 243 return index; 244 } 245 if(*buf >= '0' && *buf <= '9') 246 return atoi(buf); 247 248 if(strcmp(buf, "end") == 0) { 249 if(l->nitem == 0) 250 return 0; 251 return l->nitem-1; 252 } 253 254 index = 0; 255 if(strcmp(buf, "active") == 0) 256 s = l->active; 257 else 258 if(strcmp(buf, "anchor") == 0) 259 s = l->anchor; 260 else 261 return -1; 262 263 for(e = l->head; e; e = e->link) { 264 if(e == s) 265 return index; 266 index++; 267 } 268 return -1; 269 } 270 271 void 272 tklistsv(Tk *tk) 273 { 274 TkListbox *l; 275 int nl, lh, top, bot; 276 char val[Tkminitem], cmd[Tkmaxitem], *v, *e; 277 278 l = TKobj(TkListbox, tk); 279 if(l->yscroll == nil) 280 return; 281 282 top = 0; 283 bot = TKI2F(1); 284 285 if(l->nitem != 0) { 286 lh = lineheight(tk); 287 nl = tk->act.height/lh; /* Lines in the box */ 288 top = TKI2F(l->yelem)/l->nitem; 289 bot = TKI2F(l->yelem+nl)/l->nitem; 290 } 291 292 v = tkfprint(val, top); 293 *v++ = ' '; 294 tkfprint(v, bot); 295 snprint(cmd, sizeof(cmd), "%s %s", l->yscroll, val); 296 e = tkexec(tk->env->top, cmd, nil); 297 if ((e != nil) && (tk->name != nil)) 298 print("tk: yscrollcommand \"%s\": %s\n", tk->name->name, e); 299 } 300 301 void 302 tklistsh(Tk *tk) 303 { 304 int nl, top, bot; 305 char val[Tkminitem], cmd[Tkmaxitem], *v, *e; 306 TkListbox *l = TKobj(TkListbox, tk); 307 308 if(l->xscroll == nil) 309 return; 310 311 top = 0; 312 bot = TKI2F(1); 313 314 if(l->nwidth != 0) { 315 nl = tk->act.width; 316 top = TKI2F(l->xdelta)/l->nwidth; 317 bot = TKI2F(l->xdelta+nl)/l->nwidth; 318 } 319 320 v = tkfprint(val, top); 321 *v++ = ' '; 322 tkfprint(v, bot); 323 snprint(cmd, sizeof(cmd), "%s %s", l->xscroll, val); 324 e = tkexec(tk->env->top, cmd, nil); 325 if ((e != nil) && (tk->name != nil)) 326 print("tk: xscrollcommand \"%s\": %s\n", tk->name->name, e); 327 } 328 329 void 330 tklistbgeom(Tk *tk) 331 { 332 tklistsv(tk); 333 tklistsh(tk); 334 } 335 336 static void 337 listbresize(Tk *tk) 338 { 339 TkLentry *e; 340 TkListbox *l = TKobj(TkListbox, tk); 341 342 l->nwidth = 0; 343 for (e = l->head; e != nil; e = e->link) { 344 e->width = stringwidth(tk->env->font, e->text)+2*(Listpadx+l->sborderwidth+tk->highlightwidth); 345 if(e->width > l->nwidth) 346 l->nwidth = e->width; 347 } 348 tklistbgeom(tk); 349 } 350 351 352 /* Widget Commands (+ means implemented) 353 +activate 354 bbox 355 +cget 356 +configure 357 +curselection 358 +delete 359 +get 360 +index 361 +insert 362 +nearest 363 +see 364 +selection 365 +size 366 +xview 367 +yview 368 */ 369 370 char* 371 tklistbconf(Tk *tk, char *arg, char **val) 372 { 373 char *e; 374 TkGeom g; 375 int bd, sbw, hlw; 376 TkOptab tko[3]; 377 Font *f; 378 TkListbox *tkl = TKobj(TkListbox, tk); 379 380 sbw = tkl->sborderwidth; 381 hlw = tk->highlightwidth; 382 f = tk->env->font; 383 tko[0].ptr = tk; 384 tko[0].optab = tkgeneric; 385 tko[1].ptr = tkl; 386 tko[1].optab = opts; 387 tko[2].ptr = nil; 388 389 if(*arg == '\0') 390 return tkconflist(tko, val); 391 392 g = tk->req; 393 bd = tk->borderwidth; 394 e = tkparse(tk->env->top, arg, tko, nil); 395 tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd)); 396 tkgeomchg(tk, &g, bd); 397 398 if (sbw != tkl->sborderwidth || f != tk->env->font || hlw != tk->highlightwidth) 399 listbresize(tk); 400 tk->dirty = tkrect(tk, 1); 401 return e; 402 } 403 404 static void 405 entryactivate(Tk *tk, int index) 406 { 407 TkListbox *l = TKobj(TkListbox, tk); 408 TkLentry *e; 409 int flag = Tkactive; 410 411 if (l->selmode == TKbrowse) 412 flag |= Tkactivated; 413 for(e = l->head; e; e = e->link) { 414 if(index-- == 0) { 415 e->flag |= flag; 416 l->active = e; 417 } else 418 e->flag &= ~flag; 419 } 420 tk->dirty = tkrect(tk, 1); 421 } 422 423 char* 424 tklistbactivate(Tk *tk, char *arg, char **val) 425 { 426 int index; 427 char buf[Tkmaxitem]; 428 429 USED(val); 430 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 431 index = tklindex(tk, buf); 432 if(index == -1) 433 return TkBadix; 434 435 entryactivate(tk, index); 436 return nil; 437 } 438 439 char* 440 tklistbnearest(Tk *tk, char *arg, char **val) 441 { 442 int lh, y, index; 443 TkListbox *l = TKobj(TkListbox, tk); 444 445 lh = lineheight(tk); /* Line height */ 446 y = atoi(arg); 447 index = l->yelem + y/lh; 448 if(index > l->nitem) 449 index = l->nitem; 450 return tkvalue(val, "%d", index); 451 } 452 453 char* 454 tklistbindex(Tk *tk, char *arg, char **val) 455 { 456 int index; 457 char buf[Tkmaxitem]; 458 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 459 index = tklindex(tk, buf); 460 if(index == -1) 461 return TkBadix; 462 return tkvalue(val, "%d", index); 463 } 464 465 char* 466 tklistbsize(Tk *tk, char *arg, char **val) 467 { 468 TkListbox *l = TKobj(TkListbox, tk); 469 470 USED(arg); 471 return tkvalue(val, "%d", l->nitem); 472 } 473 474 char* 475 tklistbinsert(Tk *tk, char *arg, char **val) 476 { 477 int n, index; 478 TkListbox *l; 479 TkLentry *e, **el; 480 char *tbuf, buf[Tkmaxitem]; 481 482 USED(val); 483 l = TKobj(TkListbox, tk); 484 485 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 486 if(strcmp(buf, "end") == 0) { 487 el = &l->head; 488 if(*el != nil) { 489 for(e = *el; e->link; e = e->link) 490 ; 491 el = &e->link; 492 } 493 } 494 else { 495 index = tklindex(tk, buf); 496 if(index == -1) 497 return TkBadix; 498 el = &l->head; 499 for(e = *el; e && index-- > 0; e = e->link) 500 el = &e->link; 501 } 502 503 n = strlen(arg); 504 if(n > Tkmaxitem) { 505 n = (n*3)/2; 506 tbuf = malloc(n); 507 if(tbuf == nil) 508 return TkNomem; 509 } 510 else { 511 tbuf = buf; 512 n = sizeof(buf); 513 } 514 515 while(*arg) { 516 arg = tkword(tk->env->top, arg, tbuf, &tbuf[n], nil); 517 e = malloc(sizeof(TkLentry)+strlen(tbuf)+1); 518 if(e == nil) 519 return TkNomem; 520 521 e->flag = 0; 522 strcpy(e->text, tbuf); 523 e->link = *el; 524 *el = e; 525 el = &e->link; 526 e->width = stringwidth(tk->env->font, e->text)+2*(Listpadx+l->sborderwidth+tk->highlightwidth); 527 if(e->width > l->nwidth) 528 l->nwidth = e->width; 529 l->nitem++; 530 } 531 532 if(tbuf != buf) 533 free(tbuf); 534 535 tklistbgeom(tk); 536 tk->dirty = tkrect(tk, 1); 537 return nil; 538 } 539 540 int 541 tklistbrange(Tk *tk, char *arg, int *s, int *e) 542 { 543 char buf[Tkmaxitem]; 544 545 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 546 *s = tklindex(tk, buf); 547 if(*s == -1) 548 return -1; 549 *e = *s; 550 if(*arg == '\0') 551 return 0; 552 553 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 554 *e = tklindex(tk, buf); 555 if(*e == -1) 556 return -1; 557 return 0; 558 } 559 560 char* 561 tklistbselection(Tk *tk, char *arg, char **val) 562 { 563 TkTop *t; 564 TkLentry *f; 565 TkListbox *l; 566 int s, e, indx; 567 char buf[Tkmaxitem]; 568 569 l = TKobj(TkListbox, tk); 570 571 t = tk->env->top; 572 arg = tkword(t, arg, buf, buf+sizeof(buf), nil); 573 if(strcmp(buf, "includes") == 0) { 574 tkword(t, arg, buf, buf+sizeof(buf), nil); 575 indx = tklindex(tk, buf); 576 if(indx == -1) 577 return TkBadix; 578 for(f = l->head; f && indx > 0; f = f->link) 579 indx--; 580 s = 0; 581 if(f && (f->flag&Tkactivated)) 582 s = 1; 583 return tkvalue(val, "%d", s); 584 } 585 586 if(strcmp(buf, "anchor") == 0) { 587 tkword(t, arg, buf, buf+sizeof(buf), nil); 588 indx = tklindex(tk, buf); 589 if(indx == -1) 590 return TkBadix; 591 for(f = l->head; f && indx > 0; f = f->link) 592 indx--; 593 if(f != nil) 594 l->anchor = f; 595 return nil; 596 } 597 indx = 0; 598 if(strcmp(buf, "clear") == 0) { 599 if(tklistbrange(tk, arg, &s, &e) != 0) 600 return TkBadix; 601 for(f = l->head; f; f = f->link) { 602 if(indx <= e && indx++ >= s) 603 f->flag &= ~Tkactivated; 604 } 605 tk->dirty = tkrect(tk, 1); 606 return nil; 607 } 608 if(strcmp(buf, "set") == 0) { 609 if(tklistbrange(tk, arg, &s, &e) != 0) 610 return TkBadix; 611 for(f = l->head; f; f = f->link) { 612 if(indx <= e && indx++ >= s) 613 f->flag |= Tkactivated; 614 } 615 tk->dirty = tkrect(tk, 1); 616 return nil; 617 } 618 return TkBadcm; 619 } 620 621 char* 622 tklistbdelete(Tk *tk, char *arg, char **val) 623 { 624 TkLentry *e, **el; 625 int start, end, indx, bh; 626 TkListbox *l = TKobj(TkListbox, tk); 627 628 USED(val); 629 if(tklistbrange(tk, arg, &start, &end) != 0) 630 return TkBadix; 631 632 indx = 0; 633 el = &l->head; 634 for(e = l->head; e && indx < start; e = e->link) { 635 indx++; 636 el = &e->link; 637 } 638 while(e != nil && indx <= end) { 639 *el = e->link; 640 if(e->width == l->nwidth) 641 l->nwidth = 0; 642 if (e == l->anchor) 643 l->anchor = nil; 644 if (e == l->active) 645 l->active = nil; 646 free(e); 647 e = *el; 648 indx++; 649 l->nitem--; 650 } 651 if(l->nwidth == 0) { 652 for(e = l->head; e; e = e->link) 653 if(e->width > l->nwidth) 654 l->nwidth = e->width; 655 } 656 bh = tk->act.height/lineheight(tk); /* Box height */ 657 if(l->yelem + bh > l->nitem) 658 l->yelem = l->nitem - bh; 659 if(l->yelem < 0) 660 l->yelem = 0; 661 662 tklistbgeom(tk); 663 tk->dirty = tkrect(tk, 1); 664 return nil; 665 } 666 667 char* 668 tklistbget(Tk *tk, char *arg, char **val) 669 { 670 TkLentry *e; 671 char *r, *fmt; 672 int start, end, indx; 673 TkListbox *l = TKobj(TkListbox, tk); 674 675 if(tklistbrange(tk, arg, &start, &end) != 0) 676 return TkBadix; 677 678 indx = 0; 679 for(e = l->head; e && indx < start; e = e->link) 680 indx++; 681 fmt = "%s"; 682 while(e != nil && indx <= end) { 683 r = tkvalue(val, fmt, e->text); 684 if(r != nil) 685 return r; 686 indx++; 687 fmt = " %s"; 688 e = e->link; 689 } 690 return nil; 691 } 692 693 char* 694 tklistbcursel(Tk *tk, char *arg, char **val) 695 { 696 int indx; 697 TkLentry *e; 698 char *r, *fmt; 699 TkListbox *l = TKobj(TkListbox, tk); 700 701 USED(arg); 702 indx = 0; 703 fmt = "%d"; 704 for(e = l->head; e; e = e->link) { 705 if(e->flag & Tkactivated) { 706 r = tkvalue(val, fmt, indx); 707 if(r != nil) 708 return r; 709 fmt = " %d"; 710 } 711 indx++; 712 } 713 return nil; 714 } 715 716 static char* 717 tklistbview(Tk *tk, char *arg, char **val, int nl, int *posn, int max) 718 { 719 int top, bot, amount; 720 char buf[Tkmaxitem]; 721 char *v, *e; 722 723 top = 0; 724 if(*arg == '\0') { 725 if ( max <= nl || max == 0 ) { /* Double test redundant at 726 * this time, but want to 727 * protect against future 728 * calls. -- DBK */ 729 top = 0; 730 bot = TKI2F(1); 731 } 732 else { 733 top = TKI2F(*posn)/max; 734 bot = TKI2F(*posn+nl)/max; 735 } 736 v = tkfprint(buf, top); 737 *v++ = ' '; 738 tkfprint(v, bot); 739 return tkvalue(val, "%s", buf); 740 } 741 742 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 743 if(strcmp(buf, "moveto") == 0) { 744 e = tkfracword(tk->env->top, &arg, &top, nil); 745 if (e != nil) 746 return e; 747 *posn = TKF2I((top+1)*max); 748 } 749 else 750 if(strcmp(buf, "scroll") == 0) { 751 arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 752 amount = atoi(buf); 753 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 754 if(buf[0] == 'p') /* Pages */ 755 amount *= nl; 756 *posn += amount; 757 } 758 else { 759 top = tklindex(tk, buf); 760 if(top == -1) 761 return TkBadix; 762 *posn = top; 763 } 764 765 bot = max - nl; 766 if(*posn > bot) 767 *posn = bot; 768 if(*posn < 0) 769 *posn = 0; 770 771 tk->dirty = tkrect(tk, 1); 772 return nil; 773 } 774 775 static int 776 entrysee(Tk *tk, int index) 777 { 778 TkListbox *l = TKobj(TkListbox, tk); 779 int bh; 780 781 /* Box height in lines */ 782 bh = tk->act.height/lineheight(tk); 783 if (bh > l->nitem) 784 bh = l->nitem; 785 if (index >= l->nitem) 786 index = l->nitem -1; 787 if (index < 0) 788 index = 0; 789 790 /* If the item is already visible, do nothing */ 791 if (l->nitem == 0 || index >= l->yelem && index < l->yelem+bh) 792 return index; 793 794 if (index < l->yelem) 795 l->yelem = index; 796 else 797 l->yelem = index - (bh-1); 798 799 tklistsv(tk); 800 tk->dirty = tkrect(tk, 1); 801 return index; 802 } 803 804 char* 805 tklistbsee(Tk *tk, char *arg, char **val) 806 { 807 int index; 808 char buf[Tkmaxitem]; 809 810 USED(val); 811 tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); 812 index = tklindex(tk, buf); 813 if(index == -1) 814 return TkBadix; 815 816 entrysee(tk, index); 817 return nil; 818 } 819 820 char* 821 tklistbyview(Tk *tk, char *arg, char **val) 822 { 823 int bh; 824 char *e; 825 TkListbox *l = TKobj(TkListbox, tk); 826 827 bh = tk->act.height/lineheight(tk); /* Box height */ 828 e = tklistbview(tk, arg, val, bh, &l->yelem, l->nitem); 829 tklistsv(tk); 830 return e; 831 } 832 833 char* 834 tklistbxview(Tk *tk, char *arg, char **val) 835 { 836 char *e; 837 TkListbox *l = TKobj(TkListbox, tk); 838 839 e = tklistbview(tk, arg, val, tk->act.width, &l->xdelta, l->nwidth); 840 tklistsh(tk); 841 return e; 842 } 843 844 static TkLentry* 845 entryset(TkListbox *l, int indx, int toggle) 846 { 847 TkLentry *e, *anchor; 848 849 anchor = nil; 850 for(e = l->head; e; e = e->link) { 851 if (indx-- == 0) { 852 anchor = e; 853 if (toggle) { 854 e->flag ^= Tkactivated; 855 break; 856 } else 857 e->flag |= Tkactivated; 858 continue; 859 } 860 if (!toggle) 861 e->flag &= ~Tkactivated; 862 } 863 return anchor; 864 } 865 866 static void 867 selectto(TkListbox *l, int indx) 868 { 869 TkLentry *e; 870 int inrange; 871 872 if (l->anchor == nil) 873 return; 874 inrange = 0; 875 for(e = l->head; e; e = e->link) { 876 if(indx == 0) 877 inrange = !inrange; 878 if(e == l->anchor) 879 inrange = !inrange; 880 if(inrange || e == l->anchor || indx == 0) 881 e->flag |= Tkactivated; 882 else 883 e->flag &= ~Tkactivated; 884 indx--; 885 } 886 } 887 888 static char* 889 dragto(Tk *tk, int y) 890 { 891 int indx; 892 TkLentry *e; 893 TkListbox *l = TKobj(TkListbox, tk); 894 895 indx = y/lineheight(tk); 896 if (y < 0) 897 indx--; /* int division rounds towards 0 */ 898 if (y < tk->act.height && indx >= l->nitem) 899 return nil; 900 indx = entrysee(tk, l->yelem+indx); 901 entryactivate(tk, indx); 902 903 if(l->selmode == TKsingle || l->selmode == TKmultiple) 904 return nil; 905 906 if(l->selmode == TKbrowse) { 907 for(e = l->head; e; e = e->link) { 908 if(indx-- == 0) { 909 if (e == l->anchor) 910 return nil; 911 l->anchor = e; 912 e->flag |= Tkactivated; 913 } else 914 e->flag &= ~Tkactivated; 915 } 916 return nil; 917 } 918 /* extended selection mode */ 919 selectto(l, indx); 920 tk->dirty = tkrect(tk, 1); 921 return nil; 922 } 923 924 static void 925 autoselect(Tk *tk, void *v, int cancelled) 926 { 927 Point pt; 928 int y, eh, ne; 929 930 USED(v); 931 if (cancelled) 932 return; 933 934 pt = tkposn(tk); 935 pt.y += tk->borderwidth; 936 y = tk->env->top->ctxt->mstate.y; 937 y -= pt.y; 938 eh = lineheight(tk); 939 ne = tk->act.height/eh; 940 if (y >= 0 && y < eh*ne) 941 return; 942 dragto(tk, y); 943 tkdirty(tk); 944 tkupdate(tk->env->top); 945 } 946 947 static char* 948 tklistbbutton1p(Tk *tk, char *arg, char **val) 949 { 950 TkListbox *l = TKobj(TkListbox, tk); 951 int y, indx; 952 953 USED(val); 954 955 y = atoi(arg); 956 indx = y/lineheight(tk); 957 indx += l->yelem; 958 if (indx < l->nitem) { 959 l->anchor = entryset(l, indx, l->selmode == TKmultiple); 960 entryactivate(tk, indx); 961 entrysee(tk, indx); 962 } 963 tkrepeat(tk, autoselect, nil, TkRptpause, TkRptinterval); 964 return nil; 965 } 966 967 char * 968 tklistbbutton1r(Tk *tk, char *arg, char **val) 969 { 970 USED(arg); 971 USED(val); 972 tkcancelrepeat(tk); 973 return nil; 974 } 975 976 char* 977 tklistbbutton1m(Tk *tk, char *arg, char **val) 978 { 979 int y, eh, ne; 980 USED(val); 981 982 eh = lineheight(tk); 983 ne = tk->act.height/eh; 984 y = atoi(arg); 985 /* If outside the box, let autoselect handle it */ 986 if (y < 0 || y >= ne * eh) 987 return nil; 988 return dragto(tk, y); 989 } 990 991 char* 992 tklistbkey(Tk *tk, char *arg, char **val) 993 { 994 TkListbox *l = TKobj(TkListbox, tk); 995 TkLentry *e; 996 int key, active; 997 USED(val); 998 999 if(tk->flag & Tkdisabled) 1000 return nil; 1001 1002 key = strtol(arg, nil, 0); 1003 active = 0; 1004 for (e = l->head; e != nil; e = e->link) { 1005 if (e->flag & Tkactive) 1006 break; 1007 active++; 1008 } 1009 1010 if (key == '\n' || key == ' ') { 1011 l->anchor = entryset(l, active, l->selmode == TKmultiple); 1012 tk->dirty = tkrect(tk, 0); 1013 return nil; 1014 } 1015 if (key == Up) 1016 active--; 1017 else if (key == Down) 1018 active++; 1019 else 1020 return nil; 1021 1022 if (active < 0) 1023 active = 0; 1024 if (active >= l->nitem) 1025 active = l->nitem-1; 1026 entryactivate(tk, active); 1027 if (l->selmode == TKextended) { 1028 selectto(l, active); 1029 tk->dirty = tkrect(tk, 0); 1030 } 1031 entrysee(tk, active); 1032 return nil; 1033 } 1034 1035 static 1036 TkCmdtab tklistcmd[] = 1037 { 1038 "activate", tklistbactivate, 1039 "cget", tklistbcget, 1040 "configure", tklistbconf, 1041 "curselection", tklistbcursel, 1042 "delete", tklistbdelete, 1043 "get", tklistbget, 1044 "index", tklistbindex, 1045 "insert", tklistbinsert, 1046 "nearest", tklistbnearest, 1047 "selection", tklistbselection, 1048 "see", tklistbsee, 1049 "size", tklistbsize, 1050 "xview", tklistbxview, 1051 "yview", tklistbyview, 1052 "tkListbButton1P", tklistbbutton1p, 1053 "tkListbButton1R", tklistbbutton1r, 1054 "tkListbButton1MP", tklistbbutton1m, 1055 "tkListbKey", tklistbkey, 1056 nil 1057 }; 1058 1059 TkMethod listboxmethod = { 1060 "listbox", 1061 tklistcmd, 1062 tkfreelistb, 1063 tkdrawlistb, 1064 tklistbgeom 1065 }; 1066