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
lineheight(Tk * tk)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*
tklistbox(TkTop * t,char * arg,char ** ret)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*
tklistbcget(Tk * tk,char * arg,char ** val)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
tkfreelistb(Tk * tk)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*
tkdrawlistb(Tk * tk,Point orig)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
tklindex(Tk * tk,char * buf)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
tklistsv(Tk * tk)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
tklistsh(Tk * tk)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
tklistbgeom(Tk * tk)330 tklistbgeom(Tk *tk)
331 {
332 tklistsv(tk);
333 tklistsh(tk);
334 }
335
336 static void
listbresize(Tk * tk)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*
tklistbconf(Tk * tk,char * arg,char ** val)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
entryactivate(Tk * tk,int index)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*
tklistbactivate(Tk * tk,char * arg,char ** val)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*
tklistbnearest(Tk * tk,char * arg,char ** val)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*
tklistbindex(Tk * tk,char * arg,char ** val)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*
tklistbsize(Tk * tk,char * arg,char ** val)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*
tklistbinsert(Tk * tk,char * arg,char ** val)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
tklistbrange(Tk * tk,char * arg,int * s,int * e)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*
tklistbselection(Tk * tk,char * arg,char ** val)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*
tklistbdelete(Tk * tk,char * arg,char ** val)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*
tklistbget(Tk * tk,char * arg,char ** val)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*
tklistbcursel(Tk * tk,char * arg,char ** val)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*
tklistbview(Tk * tk,char * arg,char ** val,int nl,int * posn,int max)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
entrysee(Tk * tk,int index)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*
tklistbsee(Tk * tk,char * arg,char ** val)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*
tklistbyview(Tk * tk,char * arg,char ** val)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*
tklistbxview(Tk * tk,char * arg,char ** val)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*
entryset(TkListbox * l,int indx,int toggle)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
selectto(TkListbox * l,int indx)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*
dragto(Tk * tk,int y)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
autoselect(Tk * tk,void * v,int cancelled)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*
tklistbbutton1p(Tk * tk,char * arg,char ** val)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 *
tklistbbutton1r(Tk * tk,char * arg,char ** val)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*
tklistbbutton1m(Tk * tk,char * arg,char ** val)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*
tklistbkey(Tk * tk,char * arg,char ** val)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