]> git.ipfire.org Git - thirdparty/newt.git/blob - listbox.c
sync with 0.52.1
[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 | NEWT_FLAG_SHOWCURSOR);
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 > li->curHeight - 1)
178 li->startShowItem = li->currItem - li->curHeight + 1;
179 if (li->startShowItem + li->curHeight > li->numItems)
180 li->startShowItem = li->numItems - li->curHeight;
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 && wstrlen(text,-1) > li->curWidth) {
301 updateWidth(co, li, wstrlen(text,-1));
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 && (wstrlen(text,-1) > li->curWidth))
333 updateWidth(co, li, wstrlen(text,-1));
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 && (wstrlen(text,-1) > li->curWidth))
373 updateWidth(co, li, wstrlen(text,-1));
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 = wstrlen(item->text,-1)) > 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 int newtListboxItemCount(newtComponent co)
453 {
454 struct listbox *li = co->data;
455 return li->numItems;
456 }
457
458 /* If you don't want to get back the text, pass in NULL for the ptr-ptr. Same
459 goes for the data. */
460 void newtListboxGetEntry(newtComponent co, int num, char **text, void **data) {
461 struct listbox * li = co->data;
462 int i;
463 struct items *item;
464
465 if (!li->boxItems || num >= li->numItems) {
466 if(text)
467 *text = NULL;
468 if(data)
469 *data = NULL;
470 return;
471 }
472
473 i = 0;
474 item = li->boxItems;
475 while (item && i < num) {
476 i++, item = item->next;
477 }
478
479 if (item) {
480 if (text)
481 *text = item->text;
482 if (data)
483 *data = (void *)item->data;
484 }
485 }
486
487 static void listboxDraw(newtComponent co)
488 {
489 struct listbox * li = co->data;
490 struct items *item;
491 int i, j;
492
493 if (!co->isMapped) return ;
494
495 newtTrashScreen();
496
497 if(li->flags & NEWT_FLAG_BORDER) {
498 if(li->isActive)
499 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
500 else
501 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
502
503 newtDrawBox(co->left, co->top, co->width, co->height, 0);
504 }
505
506 if(li->sb)
507 li->sb->ops->draw(li->sb);
508
509 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
510
511 for(i = 0, item = li->boxItems; item != NULL && i < li->startShowItem;
512 i++, item = item->next);
513
514 j = i;
515
516 for (i = 0; item != NULL && i < li->curHeight; i++, item = item->next) {
517 if (!item->text) continue;
518
519 newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust);
520 if(j + i == li->currItem) {
521 if(item->isSelected)
522 SLsmg_set_color(NEWT_COLORSET_ACTSELLISTBOX);
523 else
524 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
525 } else if(item->isSelected)
526 SLsmg_set_color(NEWT_COLORSET_SELLISTBOX);
527 else
528 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
529
530 SLsmg_write_nstring(item->text, li->curWidth);
531
532 }
533 newtGotorc(co->top + (li->currItem - li->startShowItem) + li->bdyAdjust,
534 co->left + li->bdxAdjust);
535 }
536
537 static struct eventResult listboxEvent(newtComponent co, struct event ev) {
538 struct eventResult er;
539 struct listbox * li = co->data;
540 struct items *item;
541 int i;
542
543 er.result = ER_IGNORED;
544
545 if(ev.when == EV_EARLY || ev.when == EV_LATE) {
546 return er;
547 }
548
549 switch(ev.event) {
550 case EV_KEYPRESS:
551 if (!li->isActive) break;
552
553 switch(ev.u.key) {
554 case ' ':
555 if(!(li->flags & NEWT_FLAG_MULTIPLE)) break;
556 newtListboxSelectItem(co, newtListboxGetCurrent(co),
557 NEWT_FLAGS_TOGGLE);
558 er.result = ER_SWALLOWED;
559 /* We don't break here, because it is cool to be able to
560 hold space to select a bunch of items in a list at once */
561
562 case NEWT_KEY_DOWN:
563 if(li->numItems <= 0) break;
564 if(li->currItem < li->numItems - 1) {
565 li->currItem++;
566 if(li->currItem > (li->startShowItem + li->curHeight - 1)) {
567 li->startShowItem = li->currItem - li->curHeight + 1;
568 if(li->startShowItem + li->curHeight > li->numItems)
569 li->startShowItem = li->numItems - li->curHeight;
570 }
571 if(li->sb)
572 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
573 listboxDraw(co);
574 }
575 if(co->callback) co->callback(co, co->callbackData);
576 er.result = ER_SWALLOWED;
577 break;
578
579 case NEWT_KEY_ENTER:
580 if(li->numItems <= 0) break;
581 if(li->flags & NEWT_FLAG_RETURNEXIT)
582 er.result = ER_EXITFORM;
583 break;
584
585 case NEWT_KEY_UP:
586 if(li->numItems <= 0) break;
587 if(li->currItem > 0) {
588 li->currItem--;
589 if(li->currItem < li->startShowItem)
590 li->startShowItem = li->currItem;
591 if(li->sb)
592 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
593 listboxDraw(co);
594 }
595 if(co->callback) co->callback(co, co->callbackData);
596 er.result = ER_SWALLOWED;
597 break;
598
599 case NEWT_KEY_PGUP:
600 if(li->numItems <= 0) break;
601 li->startShowItem -= li->curHeight - 1;
602 if(li->startShowItem < 0)
603 li->startShowItem = 0;
604 li->currItem -= li->curHeight - 1;
605 if(li->currItem < 0)
606 li->currItem = 0;
607 newtListboxRealSetCurrent(co);
608 er.result = ER_SWALLOWED;
609 break;
610
611 case NEWT_KEY_PGDN:
612 if(li->numItems <= 0) break;
613 li->startShowItem += li->curHeight;
614 if(li->startShowItem > (li->numItems - li->curHeight)) {
615 li->startShowItem = li->numItems - li->curHeight;
616 }
617 li->currItem += li->curHeight;
618 if(li->currItem >= li->numItems) {
619 li->currItem = li->numItems - 1;
620 }
621 newtListboxRealSetCurrent(co);
622 er.result = ER_SWALLOWED;
623 break;
624
625 case NEWT_KEY_HOME:
626 if(li->numItems <= 0) break;
627 newtListboxSetCurrent(co, 0);
628 er.result = ER_SWALLOWED;
629 break;
630
631 case NEWT_KEY_END:
632 if(li->numItems <= 0) break;
633 li->startShowItem = li->numItems - li->curHeight;
634 if(li->startShowItem < 0)
635 li->startShowItem = 0;
636 li->currItem = li->numItems - 1;
637 newtListboxRealSetCurrent(co);
638 er.result = ER_SWALLOWED;
639 break;
640 default:
641 if (li->numItems <= 0) break;
642 if (ev.u.key < NEWT_KEY_EXTRA_BASE && isalpha(ev.u.key)) {
643 for(i = 0, item = li->boxItems; item != NULL &&
644 i < li->currItem; i++, item = item->next);
645
646 if (item && item->text && (toupper(*item->text) == toupper(ev.u.key))) {
647 item = item->next;
648 i++;
649 } else {
650 item = li->boxItems;
651 i = 0;
652 }
653 while (item && item->text &&
654 toupper(*item->text) != toupper(ev.u.key)) {
655 item = item->next;
656 i++;
657 }
658 if (item) {
659 li->currItem = i;
660 if(li->currItem < li->startShowItem ||
661 li->currItem > li->startShowItem)
662 li->startShowItem =
663 li->currItem > li->numItems - li->curHeight ?
664 li->startShowItem = li->numItems - li->curHeight :
665 li->currItem;
666 if(li->sb)
667 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
668 newtListboxRealSetCurrent(co);
669 er.result = ER_SWALLOWED;
670 }
671 }
672 }
673 break;
674
675 case EV_FOCUS:
676 li->isActive = 1;
677 listboxDraw(co);
678 if(li->flags & NEWT_FLAG_SHOWCURSOR)
679 newtCursorOn();
680 er.result = ER_SWALLOWED;
681 break;
682
683 case EV_UNFOCUS:
684 li->isActive = 0;
685 listboxDraw(co);
686 if(li->flags & NEWT_FLAG_SHOWCURSOR)
687 newtCursorOff();
688 er.result = ER_SWALLOWED;
689 break;
690
691 case EV_MOUSE:
692 /* if this mouse click was within the listbox, make the current
693 item the item clicked on. */
694 /* Up scroll arrow */
695 if (li->sb &&
696 ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 &&
697 ev.u.mouse.y == co->top + li->bdyAdjust) {
698 if(li->numItems <= 0) break;
699 if(li->currItem > 0) {
700 li->currItem--;
701 if(li->currItem < li->startShowItem)
702 li->startShowItem = li->currItem;
703 if(li->sb)
704 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
705 listboxDraw(co);
706 }
707 if(co->callback) co->callback(co, co->callbackData);
708 er.result = ER_SWALLOWED;
709 break;
710 }
711 /* Down scroll arrow */
712 if (li->sb &&
713 ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 &&
714 ev.u.mouse.y == co->top + co->height - li->bdyAdjust - 1) {
715 if(li->numItems <= 0) break;
716 if(li->currItem < li->numItems - 1) {
717 li->currItem++;
718 if(li->currItem > (li->startShowItem + li->curHeight - 1)) {
719 li->startShowItem = li->currItem - li->curHeight + 1;
720 if(li->startShowItem + li->curHeight > li->numItems)
721 li->startShowItem = li->numItems - li->curHeight;
722 }
723 if(li->sb)
724 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
725 listboxDraw(co);
726 }
727 if(co->callback) co->callback(co, co->callbackData);
728 er.result = ER_SWALLOWED;
729 break;
730 }
731 if ((ev.u.mouse.y >= co->top + li->bdyAdjust) &&
732 (ev.u.mouse.y <= co->top + co->height - (li->bdyAdjust * 2)) &&
733 (ev.u.mouse.x >= co->left + li->bdxAdjust) &&
734 (ev.u.mouse.x <= co->left + co->width + (li->bdxAdjust * 2))) {
735 li->currItem = li->startShowItem +
736 (ev.u.mouse.y - li->bdyAdjust - co->top);
737 newtListboxRealSetCurrent(co);
738 listboxDraw(co);
739 if(co->callback) co->callback(co, co->callbackData);
740 er.result = ER_SWALLOWED;
741 break;
742 }
743 }
744
745 return er;
746 }
747
748 static void listboxDestroy(newtComponent co) {
749 struct listbox * li = co->data;
750 struct items * item, * nextitem;
751
752 nextitem = item = li->boxItems;
753
754 while (item != NULL) {
755 nextitem = item->next;
756 free(item->text);
757 free(item);
758 item = nextitem;
759 }
760
761 if (li->sb) li->sb->ops->destroy(li->sb);
762
763 free(li);
764 free(co);
765 }