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