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