]> git.ipfire.org Git - thirdparty/newt.git/blame - checkboxtree.c
multi-state checkboxtrees. Whee...
[thirdparty/newt.git] / checkboxtree.c
CommitLineData
f06c5a99 1#include <slang.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "newt.h"
6#include "newt_pr.h"
7
8struct items {
9 char * text;
10 const void *data;
11 unsigned char selected;
12 struct items *next;
13 struct items *prev;
14 struct items *branch;
1e0b187b 15 int flags;
16 int depth;
f06c5a99 17};
18
19struct CheckboxTree {
20 newtComponent sb;
21 int curWidth; /* size of text w/o scrollbar or border*/
22 int curHeight; /* size of text w/o border */
1e0b187b 23 struct items * itemlist;
24 struct items ** flatList, ** currItem, ** firstItem;
25 int flatCount;
f06c5a99 26 int flags;
27 int pad;
28 char * seq;
29 char * result;
30};
31
32static void ctDraw(newtComponent c);
33static void ctDestroy(newtComponent co);
34static void ctPlace(newtComponent co, int newLeft, int newTop);
35struct eventResult ctEvent(newtComponent co, struct event ev);
36static void ctMapped(newtComponent co, int isMapped);
1e0b187b 37static struct items * findItem(struct items * items, const void * data);
38static void buildFlatList(newtComponent co);
39static void doBuildFlatList(struct CheckboxTree * ct, struct items * item);
1fc80395 40enum countWhat { COUNT_EXPOSED=0, COUNT_SELECTED=1 };
1e0b187b 41static int countItems(struct items * item, enum countWhat justExposed);
f06c5a99 42
43static struct componentOps ctOps = {
44 ctDraw,
45 ctEvent,
46 ctDestroy,
47 ctPlace,
48 ctMapped,
49} ;
50
1e0b187b 51static int countItems(struct items * item, enum countWhat what) {
52 int count = 0;
53
54 while (item) {
1fc80395 55 if ((!item->branch && item->selected == what) || (what == COUNT_EXPOSED))
1e0b187b 56 count++;
788ec656 57 if (item->branch || (what == COUNT_EXPOSED && item->selected))
1e0b187b 58 count += countItems(item->branch, what);
59 item = item->next;
60 }
61
62 return count;
63}
64
65static void doBuildFlatList(struct CheckboxTree * ct, struct items * item) {
66 while (item) {
67 ct->flatList[ct->flatCount++] = item;
68 if (item->branch && item->selected) doBuildFlatList(ct, item->branch);
69 item = item->next;
70 }
71}
72
73static void buildFlatList(newtComponent co) {
f06c5a99 74 struct CheckboxTree * ct = co->data;
f06c5a99 75
1e0b187b 76 if (ct->flatList) free(ct->flatList);
77 ct->flatCount = countItems(ct->itemlist, COUNT_EXPOSED);
78
ccfdaf09 79 ct->flatList = malloc(sizeof(*ct->flatList) * (ct->flatCount+1));
1e0b187b 80 ct->flatCount = 0;
81 doBuildFlatList(ct, ct->itemlist);;
ccfdaf09 82 ct->flatList[ct->flatCount] = NULL;
1e0b187b 83}
84
85int newtCheckboxTreeAddItem(newtComponent co,
86 const char * text, const void * data,
87 int flags, int index, ...) {
88 va_list argList;
89 int numIndexes;
90 int * indexes;
91 int i;
92
93 va_start(argList, index);
94 numIndexes = 0;
95 i = index;
96 while (i != NEWT_ARG_LAST) {
97 numIndexes++;
98 i = va_arg(argList, int);
99 }
100
101 va_end(argList);
102
87103b15 103 indexes = alloca(sizeof(*indexes) * (numIndexes + 1));
1e0b187b 104 va_start(argList, index);
105 numIndexes = 0;
106 i = index;
107 va_start(argList, index);
108 while (i != NEWT_ARG_LAST) {
109 indexes[numIndexes++] = i;
110 i = va_arg(argList, int);
111 }
112 va_end(argList);
113
114 indexes[numIndexes++] = NEWT_ARG_LAST;
115
116 return newtCheckboxTreeAddArray(co, text, data, flags, indexes);
117}
118
119static int doFindItemPath(struct items * items, void * data, int * path,
120 int * len) {
121 int where = 0;
122
123 while (items) {
124 if (items->data == data) {
125 if (path) path[items->depth] = where;
126 if (len) *len = items->depth + 1;
127 return 1;
128 }
129
130 if (items->branch && doFindItemPath(items->branch, data, path, len)) {
131 if (path) path[items->depth] = where;
132 return 1;
133 }
134
135 items = items->next;
136 where++;
137 }
138
139 return 0;
140}
141
142int * newtCheckboxTreeFindItem(newtComponent co, void * data) {
143 int len;
144 int * path;
145 struct CheckboxTree * ct = co->data;
146
147 if (!doFindItemPath(ct->itemlist, data, NULL, &len)) return NULL;
148
149 path = malloc(sizeof(*path) * (len + 1));
150 doFindItemPath(ct->itemlist, data, path, NULL);
151 path[len] = NEWT_ARG_LAST;
152
153 return path;
154}
155
156int newtCheckboxTreeAddArray(newtComponent co,
157 const char * text, const void * data,
158 int flags, int * indexes) {
159 struct items * curList, * newNode, * item;
160 struct items ** listPtr = NULL;
161 int i, index, numIndexes;
162 struct CheckboxTree * ct = co->data;
163
164 numIndexes = 0;
165 while (indexes[numIndexes] != NEWT_ARG_LAST) numIndexes++;
166
167 if (!ct->itemlist) {
168 if (numIndexes > 1) return -1;
169
170 ct->itemlist = malloc(sizeof(*ct->itemlist));
171 item = ct->itemlist;
f06c5a99 172 item->prev = NULL;
1e0b187b 173 item->next = NULL;
174 } else {
175 curList = ct->itemlist;
176 listPtr = &ct->itemlist;
177
178 i = 0;
179 index = indexes[i];
180 while (i < numIndexes) {
181 item = curList;
182
183 if (index == NEWT_ARG_APPEND) {
184 item = NULL;
185 } else {
186 while (index && item)
187 item = item->next, index--;
188 }
189
190 i++;
191 if (i < numIndexes) {
192 curList = item->branch;
193 listPtr = &item->branch;
194 if (!curList && (i + 1 != numIndexes)) return -1;
195
196 index = indexes[i];
197 }
198 }
199
200 if (!curList) { /* create a new branch */
201 item = malloc(sizeof(*curList->prev));
202 item->next = item->prev = NULL;
203 *listPtr = item;
204 } else if (!item) { /* append to end */
205 item = curList;
206 while (item->next) item = item->next;
207 item->next = malloc(sizeof(*curList->prev));
208 item->next->prev = item;
209 item = item->next;
87103b15 210 item->next = NULL;
1e0b187b 211 } else {
212 newNode = malloc(sizeof(*newNode));
213 newNode->prev = item->prev;
214 newNode->next = item;
215
216 if (item->prev) item->prev->next = newNode;
217 item->prev = newNode;
218 item = newNode;
219 if (!item->prev) *listPtr = item;
220 }
221 }
222
223 item->text = strdup(text);
224 item->data = data;
225 if (flags & NEWT_FLAG_SELECTED) {
226 item->selected = 1;
227 } else {
228 item->selected = 0;
f06c5a99 229 }
1e0b187b 230 item->flags = flags;
231 item->branch = NULL;
232 item->depth = numIndexes - 1;
f06c5a99 233
5a6729cf 234 i = 4 + (3 * item->depth);
1e0b187b 235
236 if ((strlen(text) + i + ct->pad) > co->width) {
237 co->width = strlen(text) + i + ct->pad;
f06c5a99 238 }
1e0b187b 239
f06c5a99 240 return 0;
241}
242
1e0b187b 243static struct items * findItem(struct items * items, const void * data) {
244 struct items * i;
245
246 while (items) {
247 if (items->data == data) return items;
248 if (items->branch) {
249 i = findItem(items->branch, data);
250 if (i) return i;
251 }
252
253 items = items->next;
254 }
255
256 return NULL;
257}
258
1fc80395 259static void listSelected(struct items * items, int * num, void ** list, int seqindex) {
1e0b187b 260 while (items) {
1fc80395 261 if ((seqindex ? items->selected==seqindex : items->selected) && !items->branch)
1e0b187b 262 list[(*num)++] = items->data;
263 if (items->branch)
1fc80395 264 listSelected(items->branch, num, list, seqindex);
1e0b187b 265 items = items->next;
266 }
267}
268
f06c5a99 269void ** newtCheckboxTreeGetSelection(newtComponent co, int *numitems)
1fc80395 270{
271 return newtCheckboxTreeGetMultiSelection(co, numitems, 0);
272}
273
274void ** newtCheckboxTreeGetMultiSelection(newtComponent co, int *numitems, char seqnum)
f06c5a99 275{
276 struct CheckboxTree * ct;
f06c5a99 277 void **retval;
1fc80395 278 int seqindex=0;
f06c5a99 279
280 if(!co || !numitems) return NULL;
281
282 ct = co->data;
1fc80395 283
284 if (seqnum) {
285 while( ct->seq[seqindex] && ( ct->seq[seqindex] != seqnum )) seqindex++;
286 } else {
287 seqindex = 0;
288 }
1e0b187b 289
1fc80395 290 *numitems = countItems(ct->itemlist, (seqindex ? seqindex : COUNT_SELECTED));
1e0b187b 291 if (!*numitems) return NULL;
f06c5a99 292
1e0b187b 293 retval = malloc(*numitems * sizeof(void *));
294 *numitems = 0;
1fc80395 295 listSelected(ct->itemlist, numitems, retval, seqindex);
1e0b187b 296
f06c5a99 297 return retval;
298}
299
300newtComponent newtCheckboxTree(int left, int top, int height, int flags) {
1fc80395 301 return newtCheckboxTreeMulti(left, top, height, NULL, flags);
302}
303
304newtComponent newtCheckboxTreeMulti(int left, int top, int height, char *seq, int flags) {
f06c5a99 305 newtComponent co;
306 struct CheckboxTree * ct;
307
308 co = malloc(sizeof(*co));
309 ct = malloc(sizeof(struct CheckboxTree));
310 co->data = ct;
311 co->ops = &ctOps;
312 co->takesFocus = 1;
313 co->height = height;
314 co->width = 0;
315 co->isMapped = 0;
f06c5a99 316 ct->itemlist = NULL;
1e0b187b 317 ct->firstItem = NULL;
318 ct->currItem = NULL;
319 ct->flatList = NULL;
1fc80395 320 if (seq)
321 ct->seq = strdup(seq);
322 else
323 ct->seq = strdup(" *");
f06c5a99 324 if (flags & NEWT_FLAG_SCROLL) {
325 ct->sb = newtVerticalScrollbar(left, top, height,
326 COLORSET_LISTBOX, COLORSET_ACTLISTBOX);
327 ct->pad = 2;
328 } else {
329 ct->sb = NULL;
330 ct->pad = 0;
331 }
332
333 return co;
334}
335
336static void ctMapped(newtComponent co, int isMapped) {
337 struct CheckboxTree * ct = co->data;
338
339 co->isMapped = isMapped;
340 if (ct->sb)
341 ct->sb->ops->mapped(ct->sb, isMapped);
342}
343
344static void ctPlace(newtComponent co, int newLeft, int newTop) {
345 struct CheckboxTree * ct = co->data;
346
347 co->top = newTop;
348 co->left = newLeft;
349
350 if (ct->sb)
351 ct->sb->ops->place(ct->sb, co->left + co->width - 1, co->top);
352}
353
354int ctSetItem(newtComponent co, struct items *item, enum newtFlagsSense sense)
355{
356 struct CheckboxTree * ct = co->data;
5a6729cf 357 struct items * currItem;
358 struct items * firstItem;
f06c5a99 359
360 if (!item)
361 return 1;
362
f06c5a99 363 switch(sense) {
364 case NEWT_FLAGS_RESET:
365 item->selected = 0;
366 break;
367 case NEWT_FLAGS_SET:
368 item->selected = 1;
369 break;
370 case NEWT_FLAGS_TOGGLE:
1fc80395 371 if (item->branch)
372 item->selected = !item->selected;
373 else {
374 item->selected++;
375 if (item->selected==strlen(ct->seq))
376 item->selected = 0;
377 }
f06c5a99 378 break;
379 }
380
1e0b187b 381 if (item->branch) {
5a6729cf 382 currItem = *ct->currItem;
383 firstItem = *ct->firstItem;
1e0b187b 384
385 buildFlatList(co);
386
387 ct->currItem = ct->flatList;
5a6729cf 388 while (*ct->currItem != currItem) ct->currItem++;
389
390 ct->firstItem = ct->flatList;
391 while (*ct->firstItem != firstItem) ct->firstItem++;
1e0b187b 392 }
393
f06c5a99 394 return 0;
395}
396
397
398static void ctDraw(newtComponent co) {
399 struct CheckboxTree * ct = co->data;
1e0b187b 400 struct items ** item;
401 int i, curr, j;
402
403 if (!co->isMapped) return ;
404
405 if (!ct->firstItem) {
406 buildFlatList(co);
407 ct->firstItem = ct->currItem = ct->flatList;
f06c5a99 408 }
1e0b187b 409
410 item = ct->firstItem;
f06c5a99 411
412 i = 0;
1e0b187b 413 while (*item && i < co->height) {
f06c5a99 414 newtGotorc(co->top + i, co->left);
1e0b187b 415 if (*item == *ct->currItem) {
f06c5a99 416 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
f06c5a99 417 } else
418 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
1e0b187b 419
420 for (j = 0; j < (*item)->depth; j++)
421 SLsmg_write_string(" ");
422
423 if ((*item)->branch) {
424 if ((*item)->selected)
425 SLsmg_write_string("<-> ");
426 else
427 SLsmg_write_string("<+> ");
428 } else {
1fc80395 429 if ((*item)->selected) {
430 char tmp[5];
431 snprintf(tmp,5,"[%c] ",ct->seq[(*item)->selected]);
432 SLsmg_write_string(tmp);
433 }
1e0b187b 434 else
435 SLsmg_write_string("[ ] ");
436 }
437
438 SLsmg_write_nstring((*item)->text, co->width - 4 -
439 (3 * (*item)->depth));
440 item++;
f06c5a99 441 i++;
442 }
443
444 if(ct->sb) {
1e0b187b 445 newtScrollbarSet(ct->sb, ct->currItem - ct->flatList,
446 ct->flatCount - 1);
f06c5a99 447 ct->sb->ops->draw(ct->sb);
448 }
449
450 newtGotorc(co->top + curr, co->left + 1);
451}
452
453static void ctDestroy(newtComponent co) {
454 struct CheckboxTree * ct = co->data;
455 struct items * item, * nextitem;
456
457 nextitem = item = ct->itemlist;
458
459 while (item != NULL) {
460 nextitem = item->next;
461 free(item->text);
462 free(item);
463 item = nextitem;
464 }
465
1fc80395 466 free(ct->seq);
f06c5a99 467 free(ct);
468 free(co);
469}
470
471struct eventResult ctEvent(newtComponent co, struct event ev) {
472 struct CheckboxTree * ct = co->data;
473 struct eventResult er;
1e0b187b 474 struct items ** listEnd, ** lastItem;
f06c5a99 475
476 er.result = ER_IGNORED;
477
478 if(ev.when == EV_EARLY || ev.when == EV_LATE) {
479 return er;
480 }
481
482 switch(ev.event) {
483 case EV_KEYPRESS:
484 switch(ev.u.key) {
485 case ' ':
486 case NEWT_KEY_ENTER:
1e0b187b 487 if (*ct->currItem) {
488 ctSetItem(co, *ct->currItem, NEWT_FLAGS_TOGGLE);
f06c5a99 489 ctDraw(co);
490 er.result = ER_SWALLOWED;
491 }
492 break;
493 case NEWT_KEY_DOWN:
1e0b187b 494 if ((ct->currItem - ct->flatList + 1) < ct->flatCount) {
495 ct->currItem++;
496
f06c5a99 497 er.result = ER_SWALLOWED;
1e0b187b 498
499 if (ct->currItem - ct->firstItem >= co->height)
500 ct->firstItem++;
501
f06c5a99 502 ctDraw(co);
503 }
504 break;
505 case NEWT_KEY_UP:
1e0b187b 506 if (ct->currItem != ct->flatList) {
507 ct->currItem--;
f06c5a99 508 er.result = ER_SWALLOWED;
1e0b187b 509
510 if (ct->currItem < ct->firstItem)
511 ct->firstItem = ct->currItem;
512
f06c5a99 513 ctDraw(co);
514 }
515 break;
516 case NEWT_KEY_PGUP:
1e0b187b 517 if (ct->firstItem - co->height < ct->flatList) {
518 ct->firstItem = ct->currItem = ct->flatList;
519 } else {
520 ct->currItem -= co->height;
521 ct->firstItem -= co->height;
f06c5a99 522 }
1e0b187b 523
f06c5a99 524 ctDraw(co);
525 er.result = ER_SWALLOWED;
526 break;
527 case NEWT_KEY_PGDN:
1e0b187b 528 listEnd = ct->flatList + ct->flatCount - 1;
529 lastItem = ct->firstItem + co->height - 1;
530
531 if (lastItem + co->height > listEnd) {
532 ct->firstItem = listEnd - co->height + 1;
533 ct->currItem = listEnd;
534 } else {
535 ct->currItem += co->height;
536 ct->firstItem += co->height;
f06c5a99 537 }
1e0b187b 538
f06c5a99 539 ctDraw(co);
540 er.result = ER_SWALLOWED;
541 break;
542 }
543 case EV_FOCUS:
544 ctDraw(co);
545 er.result = ER_SWALLOWED;
546 break;
547
548 case EV_UNFOCUS:
549 ctDraw(co);
550 er.result = ER_SWALLOWED;
551 break;
552 default:
553 break;
554 }
555
556 return er;
557}