]> git.ipfire.org Git - thirdparty/newt.git/blob - listbox.c
changes
[thirdparty/newt.git] / listbox.c
1 /* This goofed-up box whacked into shape by Elliot Lee <sopwith@cuc.edu>
2 (from the original listbox by Erik Troan <ewt@redhat.com>)
3 and contributed to newt for use under the LGPL license.
4 Copyright (C) 1996, 1997 Elliot Lee */
5
6 #include <slang.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11
12 #include "newt.h"
13 #include "newt_pr.h"
14
15
16 /* Linked list of items in the listbox */
17 struct items {
18 char * text;
19 const void *data;
20 unsigned char isSelected;
21 struct items *next;
22 };
23
24 /* Holds all the relevant information for this listbox */
25 struct listbox {
26 newtComponent sb; /* Scrollbar on right side of listbox */
27 int curWidth; /* size of text w/o scrollbar or border*/
28 int curHeight; /* size of text w/o border */
29 int sbAdjust;
30 int bdxAdjust, bdyAdjust;
31 int numItems, numSelected;
32 int userHasSetWidth;
33 int currItem, startShowItem; /* startShowItem is the first item displayed
34 on the screen */
35 int isActive; /* If we handle key events all the time, it seems
36 to do things even when they are supposed to be for
37 another button/whatever */
38 struct items *boxItems;
39 int grow;
40 int flags; /* flags for this listbox, right now just
41 NEWT_FLAG_RETURNEXIT */
42 };
43
44 static void listboxDraw(newtComponent co);
45 static void listboxDestroy(newtComponent co);
46 static struct eventResult listboxEvent(newtComponent co, struct event ev);
47 static void newtListboxRealSetCurrent(newtComponent co);
48 static void listboxPlace(newtComponent co, int newLeft, int newTop);
49 static inline void updateWidth(newtComponent co, struct listbox * li,
50 int maxField);
51 static void listboxMapped(newtComponent co, int isMapped);
52
53 static struct componentOps listboxOps = {
54 listboxDraw,
55 listboxEvent,
56 listboxDestroy,
57 listboxPlace,
58 listboxMapped,
59 };
60
61 static void listboxMapped(newtComponent co, int isMapped) {
62 struct listbox * li = co->data;
63
64 co->isMapped = isMapped;
65 if (li->sb)
66 li->sb->ops->mapped(li->sb, isMapped);
67 }
68
69 static void listboxPlace(newtComponent co, int newLeft, int newTop) {
70 struct listbox * li = co->data;
71
72 co->top = newTop;
73 co->left = newLeft;
74
75 if (li->sb)
76 li->sb->ops->place(li->sb, co->left + co->width - li->bdxAdjust - 1,
77 co->top);
78 }
79
80 newtComponent newtListbox(int left, int top, int height, int flags) {
81 newtComponent co, sb;
82 struct listbox * li;
83
84 if (!(co = malloc(sizeof(*co))))
85 return NULL;
86
87 if (!(li = malloc(sizeof(struct listbox)))) {
88 free(co);
89 return NULL;
90 }
91
92 li->boxItems = NULL;
93 li->numItems = 0;
94 li->currItem = 0;
95 li->numSelected = 0;
96 li->isActive = 0;
97 li->userHasSetWidth = 0;
98 li->startShowItem = 0;
99 li->sbAdjust = 0;
100 li->bdxAdjust = 0;
101 li->bdyAdjust = 0;
102 li->flags = flags & (NEWT_FLAG_RETURNEXIT | NEWT_FLAG_BORDER |
103 NEWT_FLAG_MULTIPLE);
104
105 if (li->flags & NEWT_FLAG_BORDER) {
106 li->bdxAdjust = 2;
107 li->bdyAdjust = 1;
108 }
109
110 co->height = height;
111 li->curHeight = co->height - (2 * li->bdyAdjust);
112
113 if (height) {
114 li->grow = 0;
115 if (flags & NEWT_FLAG_SCROLL) {
116 sb = newtVerticalScrollbar(left, top + li->bdyAdjust,
117 li->curHeight,
118 COLORSET_LISTBOX, COLORSET_ACTLISTBOX);
119 li->sbAdjust = 3;
120 } else {
121 sb = NULL;
122 }
123 } else {
124 li->grow = 1;
125 sb = NULL;
126 }
127
128 li->sb = sb;
129 co->data = li;
130 co->isMapped = 0;
131 co->left = left;
132 co->top = top;
133 co->ops = &listboxOps;
134 co->takesFocus = 1;
135 co->callback = NULL;
136
137 updateWidth(co, li, 5);
138
139 return co;
140 }
141
142 static inline void updateWidth(newtComponent co, struct listbox * li,
143 int maxField) {
144 li->curWidth = maxField;
145 co->width = li->curWidth + li->sbAdjust + 2 * li->bdxAdjust;
146
147 if (li->sb)
148 li->sb->left = co->left + co->width - li->bdxAdjust - 1;
149 }
150
151 void newtListboxSetCurrentByKey(newtComponent co, void * key) {
152 struct listbox * li = co->data;
153 struct items * item;
154 int i;
155
156 item = li->boxItems, i = 0;
157 while (item && item->data != key)
158 item = item->next, i++;
159
160 if (item)
161 newtListboxSetCurrent(co, i);
162 }
163
164 void newtListboxSetCurrent(newtComponent co, int num)
165 {
166 struct listbox * li = co->data;
167
168 if (num >= li->numItems)
169 li->currItem = li->numItems - 1;
170 else if (num < 0)
171 li->currItem = 0;
172 else
173 li->currItem = num;
174
175 if (li->currItem < li->startShowItem)
176 li->startShowItem = li->currItem;
177 else if (li->currItem - li->startShowItem > co->height - 1)
178 li->startShowItem = li->currItem - co->height + 1;
179 if (li->startShowItem + co->height > li->numItems)
180 li->startShowItem = li->numItems - co->height;
181 if(li->startShowItem < 0)
182 li->startShowItem = 0;
183
184 newtListboxRealSetCurrent(co);
185 }
186
187 static void newtListboxRealSetCurrent(newtComponent co)
188 {
189 struct listbox * li = co->data;
190
191 if(li->sb)
192 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
193 listboxDraw(co);
194 if(co->callback) co->callback(co, co->callbackData);
195 }
196
197 void newtListboxSetWidth(newtComponent co, int width) {
198 struct listbox * li = co->data;
199
200 co->width = width;
201 li->curWidth = co->width - li->sbAdjust - 2 * li->bdxAdjust;
202 li->userHasSetWidth = 1;
203 if (li->sb) li->sb->left = co->width + co->left - 1;
204 listboxDraw(co);
205 }
206
207 void * newtListboxGetCurrent(newtComponent co) {
208 struct listbox * li = co->data;
209 int i;
210 struct items *item;
211
212 for(i = 0, item = li->boxItems; item != NULL && i < li->currItem;
213 i++, item = item->next);
214
215 if (item)
216 return (void *)item->data;
217 else
218 return NULL;
219 }
220
221 void newtListboxSelectItem(newtComponent co, const void * key,
222 enum newtFlagsSense sense)
223 {
224 struct listbox * li = co->data;
225 int i;
226 struct items * item;
227
228 item = li->boxItems, i = 0;
229 while (item && item->data != key)
230 item = item->next, i++;
231
232 if (!item) return;
233
234 if (item->isSelected)
235 li->numSelected--;
236
237 switch(sense) {
238 case NEWT_FLAGS_RESET:
239 item->isSelected = 0; break;
240 case NEWT_FLAGS_SET:
241 item->isSelected = 1; break;
242 case NEWT_FLAGS_TOGGLE:
243 item->isSelected = !item->isSelected;
244 }
245
246 if (item->isSelected)
247 li->numSelected++;
248
249 listboxDraw(co);
250 }
251
252 void newtListboxClearSelection(newtComponent co)
253 {
254 struct items *item;
255 struct listbox * li = co->data;
256
257 for(item = li->boxItems; item != NULL;
258 item = item->next)
259 item->isSelected = 0;
260 li->numSelected = 0;
261 listboxDraw(co);
262 }
263
264 /* Free the returned array after use, but NOT the values in the array */
265 void ** newtListboxGetSelection(newtComponent co, int *numitems)
266 {
267 struct listbox * li;
268 int i;
269 void **retval;
270 struct items *item;
271
272 if(!co || !numitems) return NULL;
273
274 li = co->data;
275 if(!li || !li->numSelected) return NULL;
276
277 retval = malloc(li->numSelected * sizeof(void *));
278 for(i = 0, item = li->boxItems; item != NULL;
279 item = item->next)
280 if(item->isSelected)
281 retval[i++] = (void *)item->data;
282 *numitems = li->numSelected;
283 return retval;
284 }
285
286 void newtListboxSetEntry(newtComponent co, int num, const char * text) {
287 struct listbox * li = co->data;
288 int i;
289 struct items *item;
290
291 for(i = 0, item = li->boxItems; item != NULL && i < num;
292 i++, item = item->next);
293
294 if(!item)
295 return;
296 else {
297 free(item->text);
298 item->text = strdup(text);
299 }
300 if (li->userHasSetWidth == 0 && strlen(text) > li->curWidth) {
301 updateWidth(co, li, strlen(text));
302 }
303
304 if (num >= li->startShowItem && num <= li->startShowItem + co->height)
305 listboxDraw(co);
306 }
307
308 void newtListboxSetData(newtComponent co, int num, void * data) {
309 struct listbox * li = co->data;
310 int i;
311 struct items *item;
312
313 for(i = 0, item = li->boxItems; item != NULL && i < num;
314 i++, item = item->next);
315
316 item->data = data;
317 }
318
319 int newtListboxAppendEntry(newtComponent co, const char * text,
320 const void * data) {
321 struct listbox * li = co->data;
322 struct items *item;
323
324 if(li->boxItems) {
325 for (item = li->boxItems; item->next != NULL; item = item->next);
326
327 item = item->next = malloc(sizeof(struct items));
328 } else {
329 item = li->boxItems = malloc(sizeof(struct items));
330 }
331
332 if (!li->userHasSetWidth && text && (strlen(text) > li->curWidth))
333 updateWidth(co, li, strlen(text));
334
335 item->text = strdup(text); item->data = data; item->next = NULL;
336 item->isSelected = 0;
337
338 if (li->grow)
339 co->height++, li->curHeight++;
340 li->numItems++;
341
342 return 0;
343 }
344
345 int newtListboxInsertEntry(newtComponent co, const char * text,
346 const void * data, void * key) {
347 struct listbox * li = co->data;
348 struct items *item, *t;
349
350 if (li->boxItems) {
351 if (key) {
352 item = li->boxItems;
353 while (item && item->data != key) item = item->next;
354
355 if (!item) return 1;
356
357 t = item->next;
358 item = item->next = malloc(sizeof(struct items));
359 item->next = t;
360 } else {
361 t = li->boxItems;
362 item = li->boxItems = malloc(sizeof(struct items));
363 item->next = t;
364 }
365 } else if (key) {
366 return 1;
367 } else {
368 item = li->boxItems = malloc(sizeof(struct items));
369 item->next = NULL;
370 }
371
372 if (!li->userHasSetWidth && text && (strlen(text) > li->curWidth))
373 updateWidth(co, li, strlen(text));
374
375 item->text = strdup(text?text:"(null)"); item->data = data;
376 item->isSelected = 0;
377
378 if (li->sb)
379 li->sb->left = co->left + co->width - li->bdxAdjust - 1;
380 li->numItems++;
381
382 listboxDraw(co);
383
384 return 0;
385 }
386
387 int newtListboxDeleteEntry(newtComponent co, void * key) {
388 struct listbox * li = co->data;
389 int widest = 0, t;
390 struct items *item, *item2 = NULL;
391 int num;
392
393 if (li->boxItems == NULL || li->numItems <= 0)
394 return 0;
395
396 num = 0;
397
398 item2 = NULL, item = li->boxItems;
399 while (item && item->data != key) {
400 item2 = item;
401 item = item->next;
402 num++;
403 }
404
405 if (!item)
406 return -1;
407
408 if (item2)
409 item2->next = item->next;
410 else
411 li->boxItems = item->next;
412
413 free(item->text);
414 free(item);
415 li->numItems--;
416
417 if (!li->userHasSetWidth) {
418 widest = 0;
419 for (item = li->boxItems; item != NULL; item = item->next)
420 if ((t = strlen(item->text)) > widest) widest = t;
421 }
422
423 if (li->currItem >= num)
424 li->currItem--;
425
426 if (!li->userHasSetWidth) {
427 updateWidth(co, li, widest);
428 }
429
430 listboxDraw(co);
431
432 return 0;
433 }
434
435 void newtListboxClear(newtComponent co)
436 {
437 struct listbox * li;
438 struct items *anitem, *nextitem;
439 if(co == NULL || (li = co->data) == NULL)
440 return;
441 for(anitem = li->boxItems; anitem != NULL; anitem = nextitem) {
442 nextitem = anitem->next;
443 free(anitem->text);
444 free(anitem);
445 }
446 li->numItems = li->numSelected = li->currItem = li->startShowItem = 0;
447 li->boxItems = NULL;
448 if (!li->userHasSetWidth)
449 updateWidth(co, li, 5);
450 }
451
452 /* If you don't want to get back the text, pass in NULL for the ptr-ptr. Same
453 goes for the data. */
454 void newtListboxGetEntry(newtComponent co, int num, char **text, void **data) {
455 struct listbox * li = co->data;
456 int i;
457 struct items *item;
458
459 if (!li->boxItems || num >= li->numItems) {
460 if(text)
461 *text = NULL;
462 if(data)
463 *data = NULL;
464 return;
465 }
466
467 i = 0;
468 item = li->boxItems;
469 while (item && i < num) {
470 i++, item = item->next;
471 }
472
473 if (item) {
474 if (text)
475 *text = item->text;
476 if (data)
477 *data = (void *)item->data;
478 }
479 }
480
481 static void listboxDraw(newtComponent co)
482 {
483 struct listbox * li = co->data;
484 struct items *item;
485 int i, j;
486
487 if (!co->isMapped) return ;
488
489 if(li->flags & NEWT_FLAG_BORDER) {
490 if(li->isActive)
491 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
492 else
493 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
494
495 newtDrawBox(co->left, co->top, co->width, co->height, 0);
496 }
497
498 if(li->sb)
499 li->sb->ops->draw(li->sb);
500
501 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
502
503 for(i = 0, item = li->boxItems; item != NULL && i < li->startShowItem;
504 i++, item = item->next);
505
506 j = i;
507
508 for (i = 0; item != NULL && i < li->curHeight; i++, item = item->next) {
509 if (!item->text) continue;
510
511 newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust);
512 if(j + i == li->currItem) {
513 if(item->isSelected)
514 SLsmg_set_color(NEWT_COLORSET_ACTSELLISTBOX);
515 else
516 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
517 } else if(item->isSelected)
518 SLsmg_set_color(NEWT_COLORSET_SELLISTBOX);
519 else
520 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
521
522 SLsmg_write_nstring(item->text, li->curWidth);
523
524 }
525 newtGotorc(co->top + (li->currItem - li->startShowItem), co->left);
526 }
527
528 static struct eventResult listboxEvent(newtComponent co, struct event ev) {
529 struct eventResult er;
530 struct listbox * li = co->data;
531 struct items *item;
532 int i;
533
534 er.result = ER_IGNORED;
535
536 if(ev.when == EV_EARLY || ev.when == EV_LATE) {
537 return er;
538 }
539
540 switch(ev.event) {
541 case EV_KEYPRESS:
542 if (!li->isActive) break;
543
544 switch(ev.u.key) {
545 case ' ':
546 if(!(li->flags & NEWT_FLAG_MULTIPLE)) break;
547 newtListboxSelectItem(co, li->boxItems[li->currItem].data,
548 NEWT_FLAGS_TOGGLE);
549 er.result = ER_SWALLOWED;
550 /* We don't break here, because it is cool to be able to
551 hold space to select a bunch of items in a list at once */
552
553 case NEWT_KEY_DOWN:
554 if(li->numItems <= 0) break;
555 if(li->currItem < li->numItems - 1) {
556 li->currItem++;
557 if(li->currItem > (li->startShowItem + li->curHeight - 1)) {
558 li->startShowItem = li->currItem - li->curHeight + 1;
559 if(li->startShowItem + li->curHeight > li->numItems)
560 li->startShowItem = li->numItems - li->curHeight;
561 }
562 if(li->sb)
563 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
564 listboxDraw(co);
565 }
566 if(co->callback) co->callback(co, co->callbackData);
567 er.result = ER_SWALLOWED;
568 break;
569
570 case NEWT_KEY_ENTER:
571 if(li->numItems <= 0) break;
572 if(li->flags & NEWT_FLAG_RETURNEXIT)
573 er.result = ER_EXITFORM;
574 break;
575
576 case NEWT_KEY_UP:
577 if(li->numItems <= 0) break;
578 if(li->currItem > 0) {
579 li->currItem--;
580 if(li->currItem < li->startShowItem)
581 li->startShowItem = li->currItem;
582 if(li->sb)
583 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
584 listboxDraw(co);
585 }
586 if(co->callback) co->callback(co, co->callbackData);
587 er.result = ER_SWALLOWED;
588 break;
589
590 case NEWT_KEY_PGUP:
591 if(li->numItems <= 0) break;
592 li->startShowItem -= li->curHeight - 1;
593 if(li->startShowItem < 0)
594 li->startShowItem = 0;
595 li->currItem -= li->curHeight - 1;
596 if(li->currItem < 0)
597 li->currItem = 0;
598 newtListboxRealSetCurrent(co);
599 er.result = ER_SWALLOWED;
600 break;
601
602 case NEWT_KEY_PGDN:
603 if(li->numItems <= 0) break;
604 li->startShowItem += li->curHeight;
605 if(li->startShowItem > (li->numItems - li->curHeight)) {
606 li->startShowItem = li->numItems - li->curHeight;
607 }
608 li->currItem += li->curHeight;
609 if(li->currItem >= li->numItems) {
610 li->currItem = li->numItems - 1;
611 }
612 newtListboxRealSetCurrent(co);
613 er.result = ER_SWALLOWED;
614 break;
615
616 case NEWT_KEY_HOME:
617 if(li->numItems <= 0) break;
618 newtListboxSetCurrent(co, 0);
619 er.result = ER_SWALLOWED;
620 break;
621
622 case NEWT_KEY_END:
623 if(li->numItems <= 0) break;
624 li->startShowItem = li->numItems - li->curHeight;
625 if(li->startShowItem < 0)
626 li->startShowItem = 0;
627 li->currItem = li->numItems - 1;
628 newtListboxRealSetCurrent(co);
629 er.result = ER_SWALLOWED;
630 break;
631 default:
632 if (ev.u.key < NEWT_KEY_EXTRA_BASE && isalpha(ev.u.key)) {
633 for(i = 0, item = li->boxItems; item != NULL &&
634 i < li->currItem; i++, item = item->next);
635
636 if (item->text && (toupper(*item->text) == toupper(ev.u.key))) {
637 item = item->next;
638 i++;
639 } else {
640 item = li->boxItems;
641 i = 0;
642 }
643 while (item && item->text &&
644 toupper(*item->text) != toupper(ev.u.key)) {
645 item = item->next;
646 i++;
647 }
648 if (item) {
649 li->currItem = i;
650 if(li->currItem < li->startShowItem ||
651 li->currItem > li->startShowItem)
652 li->startShowItem =
653 li->currItem > li->numItems - li->curHeight ?
654 li->startShowItem = li->numItems - li->curHeight :
655 li->currItem;
656 if(li->sb)
657 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
658 newtListboxRealSetCurrent(co);
659 er.result = ER_SWALLOWED;
660 }
661 }
662 }
663 break;
664
665 case EV_FOCUS:
666 li->isActive = 1;
667 listboxDraw(co);
668 er.result = ER_SWALLOWED;
669 break;
670
671 case EV_UNFOCUS:
672 li->isActive = 0;
673 listboxDraw(co);
674 er.result = ER_SWALLOWED;
675 break;
676
677 case EV_MOUSE:
678 /* if this mouse click was within the listbox, make the current
679 item the item clicked on. */
680 /* Up scroll arrow */
681 if (li->sb &&
682 ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 &&
683 ev.u.mouse.y == co->top + li->bdyAdjust) {
684 if(li->numItems <= 0) break;
685 if(li->currItem > 0) {
686 li->currItem--;
687 if(li->currItem < li->startShowItem)
688 li->startShowItem = li->currItem;
689 if(li->sb)
690 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
691 listboxDraw(co);
692 }
693 if(co->callback) co->callback(co, co->callbackData);
694 er.result = ER_SWALLOWED;
695 break;
696 }
697 /* Down scroll arrow */
698 if (li->sb &&
699 ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 &&
700 ev.u.mouse.y == co->top + co->height - li->bdyAdjust - 1) {
701 if(li->numItems <= 0) break;
702 if(li->currItem < li->numItems - 1) {
703 li->currItem++;
704 if(li->currItem > (li->startShowItem + li->curHeight - 1)) {
705 li->startShowItem = li->currItem - li->curHeight + 1;
706 if(li->startShowItem + li->curHeight > li->numItems)
707 li->startShowItem = li->numItems - li->curHeight;
708 }
709 if(li->sb)
710 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
711 listboxDraw(co);
712 }
713 if(co->callback) co->callback(co, co->callbackData);
714 er.result = ER_SWALLOWED;
715 break;
716 }
717 if ((ev.u.mouse.y >= co->top + li->bdyAdjust) &&
718 (ev.u.mouse.y <= co->top + co->height - (li->bdyAdjust * 2)) &&
719 (ev.u.mouse.x >= co->left + li->bdxAdjust) &&
720 (ev.u.mouse.x <= co->left + co->width + (li->bdxAdjust * 2))) {
721 li->currItem = li->startShowItem +
722 (ev.u.mouse.y - li->bdyAdjust - co->top);
723 newtListboxRealSetCurrent(co);
724 listboxDraw(co);
725 if(co->callback) co->callback(co, co->callbackData);
726 er.result = ER_SWALLOWED;
727 break;
728 }
729 }
730
731 return er;
732 }
733
734 static void listboxDestroy(newtComponent co) {
735 struct listbox * li = co->data;
736 struct items * item, * nextitem;
737
738 nextitem = item = li->boxItems;
739
740 while (item != NULL) {
741 nextitem = item->next;
742 free(item->text);
743 free(item);
744 item = nextitem;
745 }
746
747 if (li->sb) li->sb->ops->destroy(li->sb);
748
749 free(li);
750 free(co);
751 }