]> git.ipfire.org Git - thirdparty/newt.git/blob - listbox.c
- fix scrollbar positioning in listbox
[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 + li->bdyAdjust);
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)
204 li->sb->left = co->left + co->width - li->bdxAdjust - 1;
205 listboxDraw(co);
206 }
207
208 void * newtListboxGetCurrent(newtComponent co) {
209 struct listbox * li = co->data;
210 int i;
211 struct items *item;
212
213 for(i = 0, item = li->boxItems; item != NULL && i < li->currItem;
214 i++, item = item->next);
215
216 if (item)
217 return (void *)item->data;
218 else
219 return NULL;
220 }
221
222 void newtListboxSelectItem(newtComponent co, const void * key,
223 enum newtFlagsSense sense)
224 {
225 struct listbox * li = co->data;
226 int i;
227 struct items * item;
228
229 item = li->boxItems, i = 0;
230 while (item && item->data != key)
231 item = item->next, i++;
232
233 if (!item) return;
234
235 if (item->isSelected)
236 li->numSelected--;
237
238 switch(sense) {
239 case NEWT_FLAGS_RESET:
240 item->isSelected = 0; break;
241 case NEWT_FLAGS_SET:
242 item->isSelected = 1; break;
243 case NEWT_FLAGS_TOGGLE:
244 item->isSelected = !item->isSelected;
245 }
246
247 if (item->isSelected)
248 li->numSelected++;
249
250 listboxDraw(co);
251 }
252
253 void newtListboxClearSelection(newtComponent co)
254 {
255 struct items *item;
256 struct listbox * li = co->data;
257
258 for(item = li->boxItems; item != NULL;
259 item = item->next)
260 item->isSelected = 0;
261 li->numSelected = 0;
262 listboxDraw(co);
263 }
264
265 /* Free the returned array after use, but NOT the values in the array */
266 void ** newtListboxGetSelection(newtComponent co, int *numitems)
267 {
268 struct listbox * li;
269 int i;
270 void **retval;
271 struct items *item;
272
273 if(!co || !numitems) return NULL;
274
275 li = co->data;
276 if(!li || !li->numSelected) return NULL;
277
278 retval = malloc(li->numSelected * sizeof(void *));
279 for(i = 0, item = li->boxItems; item != NULL;
280 item = item->next)
281 if(item->isSelected)
282 retval[i++] = (void *)item->data;
283 *numitems = li->numSelected;
284 return retval;
285 }
286
287 void newtListboxSetEntry(newtComponent co, int num, const char * text) {
288 struct listbox * li = co->data;
289 int i;
290 struct items *item;
291
292 for(i = 0, item = li->boxItems; item != NULL && i < num;
293 i++, item = item->next);
294
295 if(!item)
296 return;
297 else {
298 free(item->text);
299 item->text = strdup(text);
300 }
301 if (li->userHasSetWidth == 0 && wstrlen(text,-1) > li->curWidth) {
302 updateWidth(co, li, wstrlen(text,-1));
303 }
304
305 if (num >= li->startShowItem && num <= li->startShowItem + co->height)
306 listboxDraw(co);
307 }
308
309 void newtListboxSetData(newtComponent co, int num, void * data) {
310 struct listbox * li = co->data;
311 int i;
312 struct items *item;
313
314 for(i = 0, item = li->boxItems; item != NULL && i < num;
315 i++, item = item->next);
316
317 item->data = data;
318 }
319
320 int newtListboxAppendEntry(newtComponent co, const char * text,
321 const void * data) {
322 struct listbox * li = co->data;
323 struct items *item;
324
325 if(li->boxItems) {
326 for (item = li->boxItems; item->next != NULL; item = item->next);
327
328 item = item->next = malloc(sizeof(struct items));
329 } else {
330 item = li->boxItems = malloc(sizeof(struct items));
331 }
332
333 if (!li->userHasSetWidth && text && (wstrlen(text,-1) > li->curWidth))
334 updateWidth(co, li, wstrlen(text,-1));
335
336 item->text = strdup(text); item->data = data; item->next = NULL;
337 item->isSelected = 0;
338
339 if (li->grow)
340 co->height++, li->curHeight++;
341 li->numItems++;
342
343 return 0;
344 }
345
346 int newtListboxInsertEntry(newtComponent co, const char * text,
347 const void * data, void * key) {
348 struct listbox * li = co->data;
349 struct items *item, *t;
350
351 if (li->boxItems) {
352 if (key) {
353 item = li->boxItems;
354 while (item && item->data != key) item = item->next;
355
356 if (!item) return 1;
357
358 t = item->next;
359 item = item->next = malloc(sizeof(struct items));
360 item->next = t;
361 } else {
362 t = li->boxItems;
363 item = li->boxItems = malloc(sizeof(struct items));
364 item->next = t;
365 }
366 } else if (key) {
367 return 1;
368 } else {
369 item = li->boxItems = malloc(sizeof(struct items));
370 item->next = NULL;
371 }
372
373 if (!li->userHasSetWidth && text && (wstrlen(text,-1) > li->curWidth))
374 updateWidth(co, li, wstrlen(text,-1));
375
376 item->text = strdup(text?text:"(null)"); item->data = data;
377 item->isSelected = 0;
378
379 if (li->sb)
380 li->sb->left = co->left + co->width - li->bdxAdjust - 1;
381 li->numItems++;
382
383 listboxDraw(co);
384
385 return 0;
386 }
387
388 int newtListboxDeleteEntry(newtComponent co, void * key) {
389 struct listbox * li = co->data;
390 int widest = 0, t;
391 struct items *item, *item2 = NULL;
392 int num;
393
394 if (li->boxItems == NULL || li->numItems <= 0)
395 return 0;
396
397 num = 0;
398
399 item2 = NULL, item = li->boxItems;
400 while (item && item->data != key) {
401 item2 = item;
402 item = item->next;
403 num++;
404 }
405
406 if (!item)
407 return -1;
408
409 if (item2)
410 item2->next = item->next;
411 else
412 li->boxItems = item->next;
413
414 free(item->text);
415 free(item);
416 li->numItems--;
417
418 if (!li->userHasSetWidth) {
419 widest = 0;
420 for (item = li->boxItems; item != NULL; item = item->next)
421 if ((t = wstrlen(item->text,-1)) > widest) widest = t;
422 }
423
424 if (li->currItem >= num)
425 li->currItem--;
426
427 if (!li->userHasSetWidth) {
428 updateWidth(co, li, widest);
429 }
430
431 listboxDraw(co);
432
433 return 0;
434 }
435
436 void newtListboxClear(newtComponent co)
437 {
438 struct listbox * li;
439 struct items *anitem, *nextitem;
440 if(co == NULL || (li = co->data) == NULL)
441 return;
442 for(anitem = li->boxItems; anitem != NULL; anitem = nextitem) {
443 nextitem = anitem->next;
444 free(anitem->text);
445 free(anitem);
446 }
447 li->numItems = li->numSelected = li->currItem = li->startShowItem = 0;
448 li->boxItems = NULL;
449 if (!li->userHasSetWidth)
450 updateWidth(co, li, 5);
451 }
452
453 int newtListboxItemCount(newtComponent co)
454 {
455 struct listbox *li = co->data;
456 return li->numItems;
457 }
458
459 /* If you don't want to get back the text, pass in NULL for the ptr-ptr. Same
460 goes for the data. */
461 void newtListboxGetEntry(newtComponent co, int num, char **text, void **data) {
462 struct listbox * li = co->data;
463 int i;
464 struct items *item;
465
466 if (!li->boxItems || num >= li->numItems) {
467 if(text)
468 *text = NULL;
469 if(data)
470 *data = NULL;
471 return;
472 }
473
474 i = 0;
475 item = li->boxItems;
476 while (item && i < num) {
477 i++, item = item->next;
478 }
479
480 if (item) {
481 if (text)
482 *text = item->text;
483 if (data)
484 *data = (void *)item->data;
485 }
486 }
487
488 static void listboxDraw(newtComponent co)
489 {
490 struct listbox * li = co->data;
491 struct items *item;
492 int i, j;
493
494 if (!co->isMapped) return ;
495
496 newtTrashScreen();
497
498 if(li->flags & NEWT_FLAG_BORDER) {
499 if(li->isActive)
500 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
501 else
502 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
503
504 newtDrawBox(co->left, co->top, co->width, co->height, 0);
505 }
506
507 if(li->sb)
508 li->sb->ops->draw(li->sb);
509
510 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
511
512 for(i = 0, item = li->boxItems; item != NULL && i < li->startShowItem;
513 i++, item = item->next);
514
515 j = i;
516
517 for (i = 0; item != NULL && i < li->curHeight; i++, item = item->next) {
518 if (!item->text) continue;
519
520 newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust);
521 if(j + i == li->currItem) {
522 if(li->isActive)
523 SLsmg_set_color(NEWT_COLORSET_ACTSELLISTBOX);
524 else
525 SLsmg_set_color(NEWT_COLORSET_ACTLISTBOX);
526 } else if(item->isSelected)
527 SLsmg_set_color(NEWT_COLORSET_SELLISTBOX);
528 else
529 SLsmg_set_color(NEWT_COLORSET_LISTBOX);
530
531 SLsmg_write_nstring(NULL, li->curWidth);
532 newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust);
533 if (wstrlen(item->text, -1) > li->curWidth) {
534 char *tmp;
535 tmp = strdup(item->text);
536 trim_string(tmp, li->curWidth);
537 SLsmg_write_string(tmp);
538 free(tmp);
539 } else
540 SLsmg_write_string(item->text);
541
542 if (li->flags & NEWT_FLAG_MULTIPLE) {
543 newtGotorc(co->top + i + li->bdyAdjust, co->left + li->bdxAdjust);
544 SLsmg_set_color(item->isSelected ?
545 NEWT_COLORSET_SELLISTBOX : NEWT_COLORSET_LISTBOX);
546 SLsmg_write_nstring(item->text, 1);
547 }
548 }
549 newtGotorc(co->top + (li->currItem - li->startShowItem) + li->bdyAdjust,
550 co->left + li->bdxAdjust);
551 }
552
553 static struct eventResult listboxEvent(newtComponent co, struct event ev) {
554 struct eventResult er;
555 struct listbox * li = co->data;
556 struct items *item;
557 int i;
558
559 er.result = ER_IGNORED;
560
561 if(ev.when == EV_EARLY || ev.when == EV_LATE) {
562 return er;
563 }
564
565 switch(ev.event) {
566 case EV_KEYPRESS:
567 if (!li->isActive) break;
568
569 switch(ev.u.key) {
570 case ' ':
571 if(!(li->flags & NEWT_FLAG_MULTIPLE)) break;
572 newtListboxSelectItem(co, newtListboxGetCurrent(co),
573 NEWT_FLAGS_TOGGLE);
574 er.result = ER_SWALLOWED;
575 /* We don't break here, because it is cool to be able to
576 hold space to select a bunch of items in a list at once */
577
578 case NEWT_KEY_DOWN:
579 if(li->numItems <= 0) break;
580 if(li->currItem < li->numItems - 1) {
581 li->currItem++;
582 if(li->currItem > (li->startShowItem + li->curHeight - 1)) {
583 li->startShowItem = li->currItem - li->curHeight + 1;
584 if(li->startShowItem + li->curHeight > li->numItems)
585 li->startShowItem = li->numItems - li->curHeight;
586 }
587 if(li->sb)
588 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
589 listboxDraw(co);
590 }
591 if(co->callback) co->callback(co, co->callbackData);
592 er.result = ER_SWALLOWED;
593 break;
594
595 case NEWT_KEY_ENTER:
596 if(li->numItems <= 0) break;
597 if(li->flags & NEWT_FLAG_RETURNEXIT)
598 er.result = ER_EXITFORM;
599 break;
600
601 case NEWT_KEY_UP:
602 if(li->numItems <= 0) break;
603 if(li->currItem > 0) {
604 li->currItem--;
605 if(li->currItem < li->startShowItem)
606 li->startShowItem = li->currItem;
607 if(li->sb)
608 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
609 listboxDraw(co);
610 }
611 if(co->callback) co->callback(co, co->callbackData);
612 er.result = ER_SWALLOWED;
613 break;
614
615 case NEWT_KEY_PGUP:
616 if(li->numItems <= 0) break;
617 li->startShowItem -= li->curHeight - 1;
618 if(li->startShowItem < 0)
619 li->startShowItem = 0;
620 li->currItem -= li->curHeight - 1;
621 if(li->currItem < 0)
622 li->currItem = 0;
623 newtListboxRealSetCurrent(co);
624 er.result = ER_SWALLOWED;
625 break;
626
627 case NEWT_KEY_PGDN:
628 if(li->numItems <= 0) break;
629 li->startShowItem += li->curHeight;
630 if(li->startShowItem > (li->numItems - li->curHeight)) {
631 li->startShowItem = li->numItems - li->curHeight;
632 }
633 li->currItem += li->curHeight;
634 if(li->currItem >= li->numItems) {
635 li->currItem = li->numItems - 1;
636 }
637 newtListboxRealSetCurrent(co);
638 er.result = ER_SWALLOWED;
639 break;
640
641 case NEWT_KEY_HOME:
642 if(li->numItems <= 0) break;
643 newtListboxSetCurrent(co, 0);
644 er.result = ER_SWALLOWED;
645 break;
646
647 case NEWT_KEY_END:
648 if(li->numItems <= 0) break;
649 li->startShowItem = li->numItems - li->curHeight;
650 if(li->startShowItem < 0)
651 li->startShowItem = 0;
652 li->currItem = li->numItems - 1;
653 newtListboxRealSetCurrent(co);
654 er.result = ER_SWALLOWED;
655 break;
656 default:
657 if (li->numItems <= 0) break;
658 if (ev.u.key < NEWT_KEY_EXTRA_BASE && isalpha(ev.u.key)) {
659 for(i = 0, item = li->boxItems; item != NULL &&
660 i < li->currItem; i++, item = item->next);
661
662 if (item && item->text && (toupper(*item->text) == toupper(ev.u.key))) {
663 item = item->next;
664 i++;
665 } else {
666 item = li->boxItems;
667 i = 0;
668 }
669 while (item && item->text &&
670 toupper(*item->text) != toupper(ev.u.key)) {
671 item = item->next;
672 i++;
673 }
674 if (item) {
675 li->currItem = i;
676 if(li->currItem < li->startShowItem ||
677 li->currItem > li->startShowItem)
678 li->startShowItem =
679 li->currItem > li->numItems - li->curHeight ?
680 li->startShowItem = li->numItems - li->curHeight :
681 li->currItem;
682 if(li->sb)
683 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
684 newtListboxRealSetCurrent(co);
685 er.result = ER_SWALLOWED;
686 }
687 }
688 }
689 break;
690
691 case EV_FOCUS:
692 li->isActive = 1;
693 listboxDraw(co);
694 if(li->flags & NEWT_FLAG_SHOWCURSOR)
695 newtCursorOn();
696 er.result = ER_SWALLOWED;
697 break;
698
699 case EV_UNFOCUS:
700 li->isActive = 0;
701 listboxDraw(co);
702 if(li->flags & NEWT_FLAG_SHOWCURSOR)
703 newtCursorOff();
704 er.result = ER_SWALLOWED;
705 break;
706
707 case EV_MOUSE:
708 /* if this mouse click was within the listbox, make the current
709 item the item clicked on. */
710 /* Up scroll arrow */
711 if (li->sb &&
712 ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 &&
713 ev.u.mouse.y == co->top + li->bdyAdjust) {
714 if(li->numItems <= 0) break;
715 if(li->currItem > 0) {
716 li->currItem--;
717 if(li->currItem < li->startShowItem)
718 li->startShowItem = li->currItem;
719 if(li->sb)
720 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
721 listboxDraw(co);
722 }
723 if(co->callback) co->callback(co, co->callbackData);
724 er.result = ER_SWALLOWED;
725 break;
726 }
727 /* Down scroll arrow */
728 if (li->sb &&
729 ev.u.mouse.x == co->left + co->width - li->bdxAdjust - 1 &&
730 ev.u.mouse.y == co->top + co->height - li->bdyAdjust - 1) {
731 if(li->numItems <= 0) break;
732 if(li->currItem < li->numItems - 1) {
733 li->currItem++;
734 if(li->currItem > (li->startShowItem + li->curHeight - 1)) {
735 li->startShowItem = li->currItem - li->curHeight + 1;
736 if(li->startShowItem + li->curHeight > li->numItems)
737 li->startShowItem = li->numItems - li->curHeight;
738 }
739 if(li->sb)
740 newtScrollbarSet(li->sb, li->currItem + 1, li->numItems);
741 listboxDraw(co);
742 }
743 if(co->callback) co->callback(co, co->callbackData);
744 er.result = ER_SWALLOWED;
745 break;
746 }
747 if ((ev.u.mouse.y >= co->top + li->bdyAdjust) &&
748 (ev.u.mouse.y <= co->top + co->height - (li->bdyAdjust * 2)) &&
749 (ev.u.mouse.x >= co->left + li->bdxAdjust) &&
750 (ev.u.mouse.x <= co->left + co->width + (li->bdxAdjust * 2))) {
751 li->currItem = li->startShowItem +
752 (ev.u.mouse.y - li->bdyAdjust - co->top);
753 newtListboxRealSetCurrent(co);
754 listboxDraw(co);
755 if(co->callback) co->callback(co, co->callbackData);
756 er.result = ER_SWALLOWED;
757 break;
758 }
759 }
760
761 return er;
762 }
763
764 static void listboxDestroy(newtComponent co) {
765 struct listbox * li = co->data;
766 struct items * item, * nextitem;
767
768 nextitem = item = li->boxItems;
769
770 while (item != NULL) {
771 nextitem = item->next;
772 free(item->text);
773 free(item);
774 item = nextitem;
775 }
776
777 if (li->sb) li->sb->ops->destroy(li->sb);
778
779 free(li);
780 free(co);
781 }