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