]> git.ipfire.org Git - thirdparty/newt.git/blob - checkboxtree.c
added width to CheckboxTree
[thirdparty/newt.git] / checkboxtree.c
1 #include <slang.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "newt.h"
6 #include "newt_pr.h"
7
8 struct items {
9 char * text;
10 const void *data;
11 unsigned char selected;
12 struct items *next;
13 struct items *prev;
14 struct items *branch;
15 int flags;
16 int depth;
17 };
18
19 struct CheckboxTree {
20 newtComponent sb;
21 struct items * itemlist;
22 struct items ** flatList, ** currItem, ** firstItem;
23 int flatCount;
24 int flags;
25 int sbAdjust;
26 int curWidth;
27 int userHasSetWidth;
28 char * seq;
29 char * result;
30 };
31
32 static void ctDraw(newtComponent c);
33 static void ctDestroy(newtComponent co);
34 static void ctPlace(newtComponent co, int newLeft, int newTop);
35 struct eventResult ctEvent(newtComponent co, struct event ev);
36 static void ctMapped(newtComponent co, int isMapped);
37 static struct items * findItem(struct items * items, const void * data);
38 static void buildFlatList(newtComponent co);
39 static void doBuildFlatList(struct CheckboxTree * ct, struct items * item);
40 enum countWhat { COUNT_EXPOSED=0, COUNT_SELECTED=1 };
41 static int countItems(struct items * item, enum countWhat justExposed);
42 static inline void updateWidth(newtComponent co, struct CheckboxTree * ct,
43 int maxField);
44
45 static struct componentOps ctOps = {
46 ctDraw,
47 ctEvent,
48 ctDestroy,
49 ctPlace,
50 ctMapped,
51 } ;
52
53 static inline void updateWidth(newtComponent co, struct CheckboxTree * ct,
54 int maxField) {
55 ct->curWidth = maxField;
56 co->width = ct->curWidth + ct->sbAdjust;
57
58 if (ct->sb)
59 ct->sb->left = co->left + co->width - 1;
60 }
61
62 static int countItems(struct items * item, enum countWhat what) {
63 int count = 0;
64
65 while (item) {
66 if ((!item->branch && item->selected == what) || (what == COUNT_EXPOSED))
67 count++;
68 if (item->branch || (what == COUNT_EXPOSED && item->selected))
69 count += countItems(item->branch, what);
70 item = item->next;
71 }
72
73 return count;
74 }
75
76 static void doBuildFlatList(struct CheckboxTree * ct, struct items * item) {
77 while (item) {
78 ct->flatList[ct->flatCount++] = item;
79 if (item->branch && item->selected) doBuildFlatList(ct, item->branch);
80 item = item->next;
81 }
82 }
83
84 static void buildFlatList(newtComponent co) {
85 struct CheckboxTree * ct = co->data;
86
87 if (ct->flatList) free(ct->flatList);
88 ct->flatCount = countItems(ct->itemlist, COUNT_EXPOSED);
89
90 ct->flatList = malloc(sizeof(*ct->flatList) * (ct->flatCount+1));
91 ct->flatCount = 0;
92 doBuildFlatList(ct, ct->itemlist);
93 ct->flatList[ct->flatCount] = NULL;
94 }
95
96 int newtCheckboxTreeAddItem(newtComponent co,
97 const char * text, const void * data,
98 int flags, int index, ...) {
99 va_list argList;
100 int numIndexes;
101 int * indexes;
102 int i;
103
104 va_start(argList, index);
105 numIndexes = 0;
106 i = index;
107 while (i != NEWT_ARG_LAST) {
108 numIndexes++;
109 i = va_arg(argList, int);
110 }
111
112 va_end(argList);
113
114 indexes = alloca(sizeof(*indexes) * (numIndexes + 1));
115 va_start(argList, index);
116 numIndexes = 0;
117 i = index;
118 va_start(argList, index);
119 while (i != NEWT_ARG_LAST) {
120 indexes[numIndexes++] = i;
121 i = va_arg(argList, int);
122 }
123 va_end(argList);
124
125 indexes[numIndexes++] = NEWT_ARG_LAST;
126
127 return newtCheckboxTreeAddArray(co, text, data, flags, indexes);
128 }
129
130 static int doFindItemPath(struct items * items, void * data, int * path,
131 int * len) {
132 int where = 0;
133
134 while (items) {
135 if (items->data == data) {
136 if (path) path[items->depth] = where;
137 if (len) *len = items->depth + 1;
138 return 1;
139 }
140
141 if (items->branch && doFindItemPath(items->branch, data, path, len)) {
142 if (path) path[items->depth] = where;
143 return 1;
144 }
145
146 items = items->next;
147 where++;
148 }
149
150 return 0;
151 }
152
153 int * newtCheckboxTreeFindItem(newtComponent co, void * data) {
154 int len;
155 int * path;
156 struct CheckboxTree * ct = co->data;
157
158 if (!doFindItemPath(ct->itemlist, data, NULL, &len)) return NULL;
159
160 path = malloc(sizeof(*path) * (len + 1));
161 doFindItemPath(ct->itemlist, data, path, NULL);
162 path[len] = NEWT_ARG_LAST;
163
164 return path;
165 }
166
167 int newtCheckboxTreeAddArray(newtComponent co,
168 const char * text, const void * data,
169 int flags, int * indexes) {
170 struct items * curList, * newNode, * item;
171 struct items ** listPtr = NULL;
172 int i, index, numIndexes;
173 struct CheckboxTree * ct = co->data;
174
175 numIndexes = 0;
176 while (indexes[numIndexes] != NEWT_ARG_LAST) numIndexes++;
177
178 if (!ct->itemlist) {
179 if (numIndexes > 1) return -1;
180
181 ct->itemlist = malloc(sizeof(*ct->itemlist));
182 item = ct->itemlist;
183 item->prev = NULL;
184 item->next = NULL;
185 } else {
186 curList = ct->itemlist;
187 listPtr = &ct->itemlist;
188
189 i = 0;
190 index = indexes[i];
191 while (i < numIndexes) {
192 item = curList;
193
194 if (index == NEWT_ARG_APPEND) {
195 item = NULL;
196 } else {
197 while (index && item)
198 item = item->next, index--;
199 }
200
201 i++;
202 if (i < numIndexes) {
203 curList = item->branch;
204 listPtr = &item->branch;
205 if (!curList && (i + 1 != numIndexes)) return -1;
206
207 index = indexes[i];
208 }
209 }
210
211 if (!curList) { /* create a new branch */
212 item = malloc(sizeof(*curList->prev));
213 item->next = item->prev = NULL;
214 *listPtr = item;
215 } else if (!item) { /* append to end */
216 item = curList;
217 while (item->next) item = item->next;
218 item->next = malloc(sizeof(*curList->prev));
219 item->next->prev = item;
220 item = item->next;
221 item->next = NULL;
222 } else {
223 newNode = malloc(sizeof(*newNode));
224 newNode->prev = item->prev;
225 newNode->next = item;
226
227 if (item->prev) item->prev->next = newNode;
228 item->prev = newNode;
229 item = newNode;
230 if (!item->prev) *listPtr = item;
231 }
232 }
233
234 item->text = strdup(text);
235 item->data = data;
236 if (flags & NEWT_FLAG_SELECTED) {
237 item->selected = 1;
238 } else {
239 item->selected = 0;
240 }
241 item->flags = flags;
242 item->branch = NULL;
243 item->depth = numIndexes - 1;
244
245 i = 4 + (3 * item->depth);
246
247 if ((ct->userHasSetWidth == 0) && ((strlen(text) + i + ct->sbAdjust) > co->width)) {
248 updateWidth(co, ct, strlen(text) + i);
249 }
250
251 return 0;
252 }
253
254 static struct items * findItem(struct items * items, const void * data) {
255 struct items * i;
256
257 while (items) {
258 if (items->data == data) return items;
259 if (items->branch) {
260 i = findItem(items->branch, data);
261 if (i) return i;
262 }
263
264 items = items->next;
265 }
266
267 return NULL;
268 }
269
270 static void listSelected(struct items * items, int * num, const void ** list, int seqindex) {
271 while (items) {
272 if ((seqindex ? items->selected==seqindex : items->selected) && !items->branch)
273 list[(*num)++] = (void *) items->data;
274 if (items->branch)
275 listSelected(items->branch, num, list, seqindex);
276 items = items->next;
277 }
278 }
279
280 void newtCheckboxTreeSetWidth(newtComponent co, int width) {
281 struct CheckboxTree * ct = co->data;
282
283 co->width = width;
284 ct->curWidth = co->width - ct->sbAdjust;
285 ct->userHasSetWidth = 1;
286 if (ct->sb) ct->sb->left = co->width + co->left - 1;
287 ctDraw(co);
288 }
289
290 const void ** newtCheckboxTreeGetSelection(newtComponent co, int *numitems)
291 {
292 return newtCheckboxTreeGetMultiSelection(co, numitems, 0);
293 }
294
295 const void ** newtCheckboxTreeGetMultiSelection(newtComponent co, int *numitems, char seqnum)
296 {
297 struct CheckboxTree * ct;
298 const void **retval;
299 int seqindex=0;
300
301 if(!co || !numitems) return NULL;
302
303 ct = co->data;
304
305 if (seqnum) {
306 while( ct->seq[seqindex] && ( ct->seq[seqindex] != seqnum )) seqindex++;
307 } else {
308 seqindex = 0;
309 }
310
311 *numitems = countItems(ct->itemlist, (seqindex ? seqindex : COUNT_SELECTED));
312 if (!*numitems) return NULL;
313
314 retval = malloc(*numitems * sizeof(void *));
315 *numitems = 0;
316 listSelected(ct->itemlist, numitems, retval, seqindex);
317
318 return retval;
319 }
320
321 newtComponent newtCheckboxTree(int left, int top, int height, int flags) {
322 return newtCheckboxTreeMulti(left, top, height, NULL, flags);
323 }
324
325 newtComponent newtCheckboxTreeMulti(int left, int top, int height, char *seq, int flags) {
326 newtComponent co;
327 struct CheckboxTree * ct;
328
329 co = malloc(sizeof(*co));
330 ct = malloc(sizeof(struct CheckboxTree));
331 co->callback = NULL;
332 co->data = ct;
333 co->ops = &ctOps;
334 co->takesFocus = 1;
335 co->height = height;
336 co->width = 0;
337 co->isMapped = 0;
338 ct->curWidth = 0;
339 ct->userHasSetWidth = 0;
340 ct->itemlist = NULL;
341 ct->firstItem = NULL;
342 ct->currItem = NULL;
343 ct->flatList = NULL;
344
345 ct->flags = flags;
346
347 if (seq)
348 ct->seq = strdup(seq);
349 else
350 ct->seq = strdup(" *");
351 if (flags & NEWT_FLAG_SCROLL) {
352 ct->sb = newtVerticalScrollbar(left, top, height,
353 COLORSET_LISTBOX, COLORSET_ACTLISTBOX);
354 ct->sbAdjust = 2;
355 } else {
356 ct->sb = NULL;
357 ct->sbAdjust = 0;
358 }
359
360 return co;
361 }
362
363 static void ctMapped(newtComponent co, int isMapped) {
364 struct CheckboxTree * ct = co->data;
365
366 co->isMapped = isMapped;
367 if (ct->sb)
368 ct->sb->ops->mapped(ct->sb, isMapped);
369 }
370
371 static void ctPlace(newtComponent co, int newLeft, int newTop) {
372 struct CheckboxTree * ct = co->data;
373
374 co->top = newTop;
375 co->left = newLeft;
376
377 if (ct->sb)
378 ct->sb->ops->place(ct->sb, co->left + co->width - 1, co->top);
379 }
380
381 int ctSetItem(newtComponent co, struct items *item, enum newtFlagsSense sense)
382 {
383 struct CheckboxTree * ct = co->data;
384 struct items * currItem;
385 struct items * firstItem;
386
387 if (!item)
388 return 1;
389
390 switch(sense) {
391 case NEWT_FLAGS_RESET:
392 item->selected = 0;
393 break;
394 case NEWT_FLAGS_SET:
395 item->selected = 1;
396 break;
397 case NEWT_FLAGS_TOGGLE:
398 if (item->branch)
399 item->selected = !item->selected;
400 else if (!(ct->flags & NEWT_CHECKBOXTREE_UNSELECTABLE)) {
401 item->selected++;
402 if (item->selected==strlen(ct->seq))
403 item->selected = 0;
404 }
405 break;
406 }
407
408 if (item->branch) {
409 currItem = *ct->currItem;
410 firstItem = *ct->firstItem;
411
412 buildFlatList(co);
413
414 ct->currItem = ct->flatList;
415 while (*ct->currItem != currItem) ct->currItem++;
416
417 ct->firstItem = ct->flatList;
418 if (ct->flatCount > co->height) {
419 struct items ** last = ct->flatList + ct->flatCount - co->height;
420 while (*ct->firstItem != firstItem && ct->firstItem != last)
421 ct->firstItem++;
422 }
423 }
424
425 return 0;
426 }
427
428 static void ctSetItems(struct items *item, int selected)
429 {
430 for (; item; item = item->next) {
431 if (!item->branch)
432 item->selected = selected;
433 else
434 ctSetItems(item->branch, selected);
435 }
436 }
437
438 static void ctDraw(newtComponent co) {
439 struct CheckboxTree * ct = co->data;
440 struct items ** item;
441 int i, j;
442 char * spaces;
443 int currRow;
444
445 if (!co->isMapped) return ;
446
447 if (!ct->firstItem) {
448 buildFlatList(co);
449 ct->firstItem = ct->currItem = ct->flatList;
450 }
451
452 item = ct->firstItem;
453
454 i = 0;
455
456 newtTrashScreen();
457
458 while (*item && i < co->height) {
459 newtGotorc(co->top + i, co->left);
460 if (*item == *ct->currItem) {
461 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
462 currRow = co->top + i;
463 } else
464 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
465
466 for (j = 0; j < (*item)->depth; j++)
467 SLsmg_write_string(" ");
468
469 if ((*item)->branch) {
470 if ((*item)->selected)
471 SLsmg_write_string("<-> ");
472 else
473 SLsmg_write_string("<+> ");
474 } else {
475 if (ct->flags & NEWT_CHECKBOXTREE_HIDE_BOX) {
476 if ((*item)->selected)
477 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
478 } else {
479 char tmp[5];
480 snprintf(tmp,5,"[%c] ",ct->seq[(*item)->selected]);
481 SLsmg_write_string(tmp);
482 }
483 }
484
485 SLsmg_write_nstring((*item)->text, co->width - 4 -
486 (3 * (*item)->depth));
487
488 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
489
490 item++;
491 i++;
492 }
493
494 /* There could be empty lines left (i.e. if the user closes an expanded
495 list which is the last thing in the tree, and whose elements are
496 displayed at the bottom of the screen */
497 if (i < co->height) {
498 spaces = alloca(co->width);
499 memset(spaces, ' ', co->width);
500 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
501 }
502 while (i < co->height) {
503 newtGotorc(co->top + i, co->left);
504 SLsmg_write_nstring(spaces, co->width);
505 i++;
506 }
507
508 if(ct->sb) {
509 newtScrollbarSet(ct->sb, ct->currItem - ct->flatList,
510 ct->flatCount - 1);
511 ct->sb->ops->draw(ct->sb);
512 }
513
514 newtGotorc(currRow, co->left + 1);
515 }
516
517 static void ctDestroy(newtComponent co) {
518 struct CheckboxTree * ct = co->data;
519 struct items * item, * nextitem;
520
521 nextitem = item = ct->itemlist;
522
523 while (item != NULL) {
524 nextitem = item->next;
525 free(item->text);
526 free(item);
527 item = nextitem;
528 }
529
530 free(ct->seq);
531 free(ct);
532 free(co);
533 }
534
535 struct eventResult ctEvent(newtComponent co, struct event ev) {
536 struct CheckboxTree * ct = co->data;
537 struct eventResult er;
538 struct items ** listEnd, ** lastItem;
539 int key, selnum = 1;
540
541 er.result = ER_IGNORED;
542
543 if(ev.when == EV_EARLY || ev.when == EV_LATE) {
544 return er;
545 }
546
547 switch(ev.event) {
548 case EV_KEYPRESS:
549 key = ev.u.key;
550 if (key == (char) key && key != ' ') {
551 for (selnum = 0; ct->seq[selnum]; selnum++)
552 if (key == ct->seq[selnum])
553 break;
554 if (!ct->seq[selnum])
555 switch (key) {
556 case '-': selnum = 0; break;
557 case '+':
558 case '*': selnum = 1; break;
559 }
560 if (ct->seq[selnum])
561 key = '*';
562 }
563 switch(key) {
564 case ' ':
565 case NEWT_KEY_ENTER:
566 ctSetItem(co, *ct->currItem, NEWT_FLAGS_TOGGLE);
567 er.result = ER_SWALLOWED;
568 if (!(*ct->currItem)->branch || (*ct->currItem)->selected)
569 key = NEWT_KEY_DOWN;
570 else
571 key = '*';
572 break;
573 case '*':
574 if ((*ct->currItem)->branch) {
575 ctSetItems((*ct->currItem)->branch, selnum);
576 if (!(*ct->currItem)->selected)
577 key = NEWT_KEY_DOWN;
578 } else {
579 (*ct->currItem)->selected = selnum;
580 key = NEWT_KEY_DOWN;
581 }
582 er.result = ER_SWALLOWED;
583 break;
584 }
585 switch (key) {
586 case '*':
587 ctDraw(co);
588 if(co->callback) co->callback(co, co->callbackData);
589 return er;
590 case NEWT_KEY_HOME:
591 ct->currItem = ct->flatList;
592 ct->firstItem = ct->flatList;
593 ctDraw(co);
594 if(co->callback) co->callback(co, co->callbackData);
595 er.result = ER_SWALLOWED;
596 return er;
597 case NEWT_KEY_END:
598 ct->currItem = ct->flatList + ct->flatCount - 1;
599 if (ct->flatCount <= co->height)
600 ct->firstItem = ct->flatList;
601 else
602 ct->firstItem = ct->flatList + ct->flatCount - co->height;
603 ctDraw(co);
604 if(co->callback) co->callback(co, co->callbackData);
605 er.result = ER_SWALLOWED;
606 return er;
607 case NEWT_KEY_DOWN:
608 if (ev.u.key != NEWT_KEY_DOWN) {
609 if(co->callback) co->callback(co, co->callbackData);
610 if (strlen(ct->seq) != 2) {
611 ctDraw(co);
612 return er;
613 }
614 }
615 if ((ct->currItem - ct->flatList + 1) < ct->flatCount) {
616 ct->currItem++;
617
618 if (ct->currItem - ct->firstItem >= co->height)
619 ct->firstItem++;
620
621 ctDraw(co);
622 } else if (ev.u.key != NEWT_KEY_DOWN)
623 ctDraw(co);
624 if(co->callback) co->callback(co, co->callbackData);
625 er.result = ER_SWALLOWED;
626 return er;
627 case NEWT_KEY_UP:
628 if (ct->currItem != ct->flatList) {
629 ct->currItem--;
630
631 if (ct->currItem < ct->firstItem)
632 ct->firstItem = ct->currItem;
633
634 ctDraw(co);
635 }
636 er.result = ER_SWALLOWED;
637 if(co->callback) co->callback(co, co->callbackData);
638 return er;
639 case NEWT_KEY_PGUP:
640 if (ct->firstItem - co->height < ct->flatList) {
641 ct->firstItem = ct->currItem = ct->flatList;
642 } else {
643 ct->currItem -= co->height;
644 ct->firstItem -= co->height;
645 }
646
647 ctDraw(co);
648 if(co->callback) co->callback(co, co->callbackData);
649 er.result = ER_SWALLOWED;
650 return er;
651 case NEWT_KEY_PGDN:
652 listEnd = ct->flatList + ct->flatCount - 1;
653 lastItem = ct->firstItem + co->height - 1;
654
655 if (lastItem + co->height > listEnd) {
656 ct->firstItem = listEnd - co->height + 1;
657 ct->currItem = listEnd;
658 } else {
659 ct->currItem += co->height;
660 ct->firstItem += co->height;
661 }
662
663 ctDraw(co);
664 if(co->callback) co->callback(co, co->callbackData);
665 er.result = ER_SWALLOWED;
666 return er;
667 }
668 break;
669
670 case EV_FOCUS:
671 ctDraw(co);
672 er.result = ER_SWALLOWED;
673 break;
674
675 case EV_UNFOCUS:
676 ctDraw(co);
677 er.result = ER_SWALLOWED;
678 break;
679 default:
680 break;
681 }
682
683 return er;
684 }
685
686 const void * newtCheckboxTreeGetCurrent(newtComponent co) {
687 struct CheckboxTree * ct = co->data;
688
689 if (!ct->currItem) return NULL;
690 return (*ct->currItem)->data;
691 }
692
693 void newtCheckboxTreeSetEntry(newtComponent co, const void * data, const char * text)
694 {
695 struct CheckboxTree * ct;
696 struct items * item;
697 int i;
698
699 if (!co) return;
700 ct = co->data;
701 item = findItem(ct->itemlist, data);
702 if (!item) return;
703
704 free(item->text);
705 item->text = strdup(text);
706
707 i = 4 + (3 * item->depth);
708
709 if ((ct->userHasSetWidth == 0) && ((strlen(text) + i + ct->sbAdjust) > co->width)) {
710 updateWidth(co, ct, strlen(text) + i);
711 }
712
713 ctDraw(co);
714 }
715
716 char newtCheckboxTreeGetEntryValue(newtComponent co, const void * data)
717 {
718 struct CheckboxTree * ct;
719 struct items * item;
720
721 if (!co) return -1;
722 ct = co->data;
723 item = findItem(ct->itemlist, data);
724 if (!item) return -1;
725 if (item->branch)
726 return item->selected ? NEWT_CHECKBOXTREE_EXPANDED : NEWT_CHECKBOXTREE_COLLAPSED;
727 else
728 return ct->seq[item->selected];
729 }
730
731 void newtCheckboxTreeSetEntryValue(newtComponent co, const void * data, char value)
732 {
733 struct CheckboxTree * ct;
734 struct items * item;
735 int i;
736
737 if (!co) return;
738 ct = co->data;
739 item = findItem(ct->itemlist, data);
740 if (!item || item->branch) return;
741
742 for(i = 0; ct->seq[i]; i++)
743 if (value == ct->seq[i])
744 break;
745
746 if (!ct->seq[i]) return;
747 item->selected = i;
748
749 ctDraw(co);
750 }
751
752
753 void newtCheckboxTreeSetCurrent(newtComponent co, void * data) {
754 struct CheckboxTree * ct = co->data;
755 int * path;
756 int i, j, itemsAfter;
757 struct items * treeTop, * item;
758
759 path = newtCheckboxTreeFindItem(co, data);
760 if (!path) return;
761
762 /* traverse the path and turn on all of the branches to this point */
763 for (i = 0, treeTop = ct->itemlist; path[i + 1] != NEWT_ARG_LAST; i++) {
764 for (j = 0, item = treeTop; j < path[i]; j++)
765 item = item->next;
766
767 item->selected = 1;
768 treeTop = item->branch;
769 }
770
771 buildFlatList(co);
772
773 item = findItem(ct->itemlist, data);
774
775 i = 0;
776 while (ct->flatList[i] != item) i++;
777
778 /* choose the top item */
779 j = i - (co->height / 2);
780
781 if ((j + co->height) > ct->flatCount)
782 j = ct->flatCount - co->height;
783
784 if (j < 0)
785 j = 0;
786
787 ct->firstItem = ct->flatList + j;
788 ct->currItem = ct->flatList + i;
789
790 ctDraw(co);
791 }