Change the system name from 'FreeBSD' to 'DragonFly'.
[dragonfly.git] / gnu / lib / libdialog / ui_objects.c
1 /*
2  * Program:     objects.c
3  * Author:      Marc van Kempen
4  * Desc:        Implementation of UI-objects:
5  *              - String input fields
6  *              - List selection
7  *              - Buttons
8  *
9  * Copyright (c) 1995, Marc van Kempen
10  *
11  * All rights reserved.
12  *
13  * This software may be used, modified, copied, distributed, and
14  * sold, in both source and binary form provided that the above
15  * copyright and these terms are retained, verbatim, as the first
16  * lines of this file.  Under no circumstances is the author
17  * responsible for the proper functioning of this software, nor does
18  * the author assume any responsibility for damages incurred with
19  * its use.
20  *
21  */
22
23 #include <stdlib.h>
24 #include <sys/param.h>
25 #include <ncurses.h>
26 #include <dialog.h>
27 #include "dialog.priv.h"
28 #include "ui_objects.h"
29
30 #define ESC 27
31
32 /***********************************************************************
33  *
34  * Obj routines
35  *
36  ***********************************************************************/
37
38 void
39 AddObj(ComposeObj **Obj, int objtype, void *obj)
40 /*
41  * Desc: Add the object <obj> to the list of objects <Obj>
42  */
43 {
44     if (*Obj == NULL) {
45         /* Create the root object */
46         *Obj = (ComposeObj *) malloc( sizeof(ComposeObj) );
47         if (!Obj) {
48             printf("AddObj: Error malloc'ing ComposeObj\n");
49             exit(-1);
50         }
51         (*Obj)->objtype = objtype;
52         (*Obj)->obj = obj;
53         (*Obj)->next = NULL;
54         (*Obj)->prev = NULL;
55     } else {
56         ComposeObj      *o = *Obj;
57
58         /* create the next object */
59         while (o->next) o = (ComposeObj *) o->next;
60         o->next = (struct ComposeObj *) malloc( sizeof(ComposeObj) );
61         if (!o->next) {
62             printf("AddObj: Error malloc'ing o->next\n");
63             exit(-1);
64         }
65         o->next->objtype = objtype;
66         o->next->obj = obj;
67         o->next->next = NULL;
68         o->next->prev = o;
69     }
70
71     return;
72 } /* AddObj() */
73
74 void
75 FreeObj(ComposeObj *Obj)
76 /*
77  * Desc: free the memory occupied by *Obj
78  */
79 {
80     ComposeObj  *o = Obj;
81
82     o = Obj;
83     while (o) {
84         o = Obj->next;
85         free(Obj);
86         Obj = o;
87     }
88
89     return;
90 } /* FreeObj() */
91
92
93 int
94 ReadObj(ComposeObj *Obj)
95 /*
96  * Desc: navigate through the different objects calling their
97  *       respective navigation routines as necessary
98  * Pre:  Obj != NULL
99  */
100 {
101     ComposeObj          *o;
102     ComposeObj          *last;   /* the last object in the list */
103     int                 ret;     /* the return value from the selection routine */
104
105     /* find the last object in the list */
106     last = Obj;
107     while (last->next) last = last->next;
108
109     ret = 0;
110     o = Obj;
111     while ((ret != SEL_BUTTON) && (ret != SEL_ESC)) {
112         switch(o->objtype) {
113         case STRINGOBJ:
114             ret = SelectStringObj((StringObj *) o->obj);
115             break;
116         case LISTOBJ:
117             ret = SelectListObj((ListObj *) o->obj);
118             break;
119         case BUTTONOBJ:
120             ret = SelectButtonObj((ButtonObj *) o->obj);
121             break;
122         }
123         switch(ret) {
124         case KEY_DOWN:
125         case SEL_CR:
126         case SEL_TAB:   /* move to the next object in the list */
127             if (o->next != NULL) {
128                 o = o->next;    /* next object */
129             } else {
130                 o = Obj;        /* beginning of the list */
131             }
132             break;
133
134         case KEY_UP:
135         case SEL_BACKTAB: /* move to the previous object in the list */
136             if (o->prev != NULL) {
137                 o = o->prev;    /* previous object */
138             } else {
139                 o = last;       /* end of the list */
140             }
141             break;
142
143         case KEY_F(1): /* display help_file */
144         case '?':
145             display_helpfile();
146             break;
147         }
148     }
149
150     return(ret);
151
152 } /* ReadObj() */
153
154
155 int
156 PollObj(ComposeObj **Obj)
157 {
158     ComposeObj          *last;   /* the last object in the list */
159     ComposeObj          *first;  /* the first object in the list */
160     int                 ret;     /* the return value from the selection routine */
161
162     /* find the last object in the list */
163     last = *Obj;
164     while (last->next) last = last->next;
165
166     /* find the first object in the list */
167     first = *Obj;
168     while (first->prev) first = first->prev;
169
170     ret = 0;
171     switch((*Obj)->objtype) {
172     case STRINGOBJ:
173         ret = SelectStringObj((StringObj *) (*Obj)->obj);
174         break;
175     case LISTOBJ:
176         ret = SelectListObj((ListObj *) (*Obj)->obj);
177         break;
178     case BUTTONOBJ:
179         ret = SelectButtonObj((ButtonObj *) (*Obj)->obj);
180         break;
181     }
182     switch(ret) {
183     case KEY_DOWN:
184     case SEL_CR:
185     case SEL_TAB:                    /* move to the next object in the list */
186         if ((*Obj)->next != NULL) {
187             *Obj = (*Obj)->next;     /* next object */
188         } else {
189             *Obj = first;            /* beginning of the list */
190         }
191         break;
192
193     case KEY_UP:
194     case SEL_BACKTAB:                /* move to the previous object in the list */
195         if ((*Obj)->prev != NULL) {
196             *Obj = (*Obj)->prev;     /* previous object */
197         } else {
198             *Obj = last;             /* end of the list */
199         }
200         break;
201     }
202
203     return(ret);
204
205 } /* PollObj() */
206
207
208 void
209 DelObj(ComposeObj *Obj)
210 /*
211  * Desc: Free all objects
212  */
213 {
214     ComposeObj  *o;
215
216     o = Obj;
217     while (Obj != NULL) {
218         switch(Obj->objtype) {
219         case STRINGOBJ:
220             DelStringObj((StringObj *) Obj->obj);
221             break;
222         case LISTOBJ:
223             DelListObj((ListObj *) Obj->obj);
224             break;
225         case BUTTONOBJ:
226             DelButtonObj((ButtonObj *) Obj->obj);
227             break;
228         }
229         Obj = Obj->next;
230     }
231
232     FreeObj(o);
233 } /* DelObj() */
234
235 /***********************************************************************
236  *
237  * StringObj routines
238  *
239  ***********************************************************************/
240
241 static void
242 outstr(WINDOW *win, char *str, int attrs)
243 {
244     if (attrs & DITEM_NO_ECHO) {
245         char *cpy;
246         int n = strlen(str);
247
248         cpy = alloca(n + 1);
249         memset(cpy, '*', n);
250         cpy[n] = '\0';
251         waddstr(win, cpy);
252     }
253     else
254         waddstr(win, str);
255 }
256
257 void
258 RefreshStringObj(StringObj *so)
259 /*
260  * Desc: redraw the object
261  */
262 {
263     char tmp[512];
264
265     wmove(so->win, so->y, so->x+1);
266     wattrset(so->win, dialog_attr);
267     waddstr(so->win, so->title);
268
269     draw_box(so->win, so->y+1, so->x, 3, so->w, dialog_attr, border_attr);
270     wattrset(so->win, item_attr);
271     wmove(so->win, so->y+2, so->x+1);
272     if (strlen(so->s) > so->w-2) {
273         strncpy(tmp, (char *) so->s + strlen(so->s) - so->w + 2, so->w - 1);
274         outstr(so->win, tmp, so->attr_mask);
275     } else {
276         outstr(so->win, so->s, so->attr_mask);
277     }
278
279     return;
280 } /* RefreshStringObj() */
281
282 StringObj *
283 NewStringObj(WINDOW *win, char *title, char *s, int y, int x, int w, int len)
284 /*
285  * Desc: Initialize a new stringobj and return a pointer to it.
286  *       Draw the object on the screen at the specified coordinates
287  */
288 {
289     StringObj   *so;
290
291     /* Initialize a new object */
292     so = (StringObj *) malloc( sizeof(StringObj) );
293     if (!so) {
294         printf("NewStringObj: Error malloc'ing StringObj\n");
295         exit(-1);
296     }
297     so->title = (char *) malloc( strlen(title) + 1);
298     if (!so->title) {
299         printf("NewStringObj: Error malloc'ing so->title\n");
300         exit(-1);
301     }
302     strcpy(so->title, title);
303     so->s = s;
304     strcpy(so->s, s);
305     so->x = x;
306     so->y = y;
307     so->w = w;
308     so->len = len;
309     so->win = win;
310     so->attr_mask = DialogInputAttrs;   /* Grossly use a global to avoid changing API */
311
312     /* Draw it on the screen */
313     RefreshStringObj(so);
314
315     return(so);
316 } /* NewStringObj() */
317
318 int
319 SelectStringObj(StringObj *so)
320 /*
321  * Desc: get input using the info in <so>
322  */
323 {
324     int         key;
325     char        tmp[so->len+1];
326
327     strcpy(tmp, so->s);
328     key = line_edit(so->win, so->y+2, so->x+1,
329                     so->len, so->w-2, inputbox_attr, TRUE, tmp, so->attr_mask);
330     if ((key == '\n') || (key == '\r') || (key == '\t') || key == (KEY_BTAB) ) {
331         strcpy(so->s, tmp);
332     }
333     RefreshStringObj(so);
334     if (key == ESC) {
335         return(SEL_ESC);
336     }
337     if (key == '\t') {
338         return(SEL_TAB);
339     }
340     if ( (key == KEY_BTAB) || (key == KEY_F(2)) ) {
341         return(SEL_BACKTAB);
342     }
343     if ((key == '\n') || (key == '\r')) {
344         return(SEL_CR);
345     }
346     return(key);
347 } /* SelectStringObj() */
348
349
350 void
351 DelStringObj(StringObj *so)
352 /*
353  * Desc: Free the space occupied by <so>
354  */
355 {
356    free(so->title);
357    free(so);
358
359    return;
360 }
361
362 /***********************************************************************
363  *
364  * ListObj routines
365  *
366  ***********************************************************************/
367
368 void
369 DrawNames(ListObj *lo)
370 /*
371  * Desc: Just refresh the names, not the surrounding box and title
372  */
373 {
374     int         i, j, h, x, y;
375     char        tmp[MAXPATHLEN];
376
377     x = lo->x + 1;
378     y = lo->y + 2;
379     h = lo->h - 2;
380     for (i=lo->scroll; i<lo->n && i<lo->scroll+h; i++) {
381         wmove(lo->win, y+i-lo->scroll, x);
382         if (lo->seld[i]) {
383             wattrset(lo->win, A_BOLD);
384         } else {
385             wattrset(lo->win, item_attr);
386         }
387         if (strlen(lo->name[i]) > lo->w-2) {
388             strncpy(tmp, lo->name[i], lo->w-2);
389             tmp[lo->w - 2] = 0;
390             waddstr(lo->win, tmp);
391         } else {
392             waddstr(lo->win, lo->name[i]);
393             for (j=strlen(lo->name[i]); j<lo->w-2; j++) waddstr(lo->win, " ");
394         }
395     }
396     wattrset(lo->win, item_attr);
397     while (i<lo->scroll+h) {
398         wmove(lo->win, y+i-lo->scroll, x);
399         for (j=0; j<lo->w-2; j++) waddstr(lo->win, " ");
400         i++;
401     }
402
403     return;
404 } /* DrawNames() */
405
406 void
407 RefreshListObj(ListObj *lo)
408 /*
409  * Desc: redraw the list object
410  */
411 {
412     char        perc[7];
413
414     /* setup the box */
415     wmove(lo->win, lo->y, lo->x+1);
416     wattrset(lo->win, dialog_attr);
417     waddstr(lo->win, lo->title);
418     draw_box(lo->win, lo->y+1, lo->x, lo->h, lo->w, dialog_attr, border_attr);
419
420     /* draw the names */
421     DrawNames(lo);
422
423     /* Draw % indication */
424     sprintf(perc, "(%3d%%)", MIN(100, (int) (100 * (lo->sel+lo->h-2) / MAX(1, lo->n))));
425     wmove(lo->win, lo->y + lo->h, lo->x + lo->w - 8);
426     wattrset(lo->win, dialog_attr);
427     waddstr(lo->win, perc);
428
429
430     return;
431 } /* RefreshListObj() */
432
433 ListObj *
434 NewListObj(WINDOW *win, char *title, char **list, char *listelt, int y, int x,
435            int h, int w, int n)
436 /*
437  * Desc: create a listobj, draw it on the screen and return a pointer to it.
438  */
439 {
440     ListObj     *lo;
441     int         i;
442
443     /* Initialize a new object */
444     lo = (ListObj *) malloc( sizeof(ListObj) );
445     if (!lo) {
446         fprintf(stderr, "NewListObj: Error malloc'ing ListObj\n");
447         exit(-1);
448     }
449     lo->title = (char *) malloc( strlen(title) + 1);
450     if (!lo->title) {
451         fprintf(stderr, "NewListObj: Error malloc'ing lo->title\n");
452         exit(-1);
453     }
454     strcpy(lo->title, title);
455     lo->name = list;
456     if (n>0) {
457         lo->seld = (int *) malloc( n * sizeof(int) );
458         if (!lo->seld) {
459             fprintf(stderr, "NewListObj: Error malloc'ing lo->seld\n");
460             exit(-1);
461         }
462         for (i=0; i<n; i++) {
463             lo->seld[i] = FALSE;
464         }
465     } else {
466         lo->seld = NULL;
467     }
468     lo->y = y;
469     lo->x = x;
470     lo->w = w;
471     lo->h = h;
472     lo->n = n;
473     lo->scroll = 0;
474     lo->sel = 0;
475     lo->elt = listelt;
476     lo->win = win;
477
478     /* Draw the object on the screen */
479     RefreshListObj(lo);
480
481     return(lo);
482 } /* NewListObj() */
483
484 void
485 UpdateListObj(ListObj *lo, char **list, int n)
486 /*
487  * Desc: Update the list in the listobject with the provided list
488  * Pre:  lo->name "has been freed"
489  *       "(A i: 0<=i<lo->n: "lo->name[i] has been freed")"
490  */
491 {
492     int i;
493
494     if (lo->seld) {
495         free(lo->seld);
496     }
497
498     /* Rewrite the list in the object */
499     lo->name = list;
500     if (n>0) {
501         lo->seld = (int *) malloc( n * sizeof(int) );
502         if (!lo->seld) {
503             fprintf(stderr, "UpdateListObj: Error malloc'ing lo->seld\n");
504             exit(-1);
505         }
506         for (i=0; i<n; i++) {
507             lo->seld[i] = FALSE;
508         }
509     } else {
510         lo->seld = NULL;
511     }
512     lo->n = n;
513     lo->scroll = 0;
514     lo->sel = 0;
515
516     /* Draw the object on the screen */
517     RefreshListObj(lo);
518
519     return;
520 } /* UpdateListObj() */
521
522 int
523 SelectListObj(ListObj *lo)
524 /*
525  * Desc: get a listname (or listnames), TAB to move on, or ESC ESC to exit
526  * Pre:  lo->n >= 1
527  */
528 {
529     int         key, sel_x, sel_y, quit;
530     char        tmp[MAXPATHLEN];
531     char        perc[4];
532
533     sel_x = lo->x+1;
534     sel_y = lo->y + 2 + lo->sel - lo->scroll;
535
536     if (lo->n == 0) return(SEL_TAB);
537
538     keypad(lo->win, TRUE);
539
540     /* Draw current selection in inverse video */
541     wmove(lo->win, sel_y, sel_x);
542     wattrset(lo->win, item_selected_attr);
543     waddstr(lo->win, lo->name[lo->sel]);
544
545     key = wgetch(lo->win);
546     quit = FALSE;
547     while ((key != '\t') && (key != '\n') && (key != '\r')
548            && (key != ESC) && (key != KEY_F(1)) && (key != '?') && !quit) {
549         /* first draw current item in normal video */
550         wmove(lo->win, sel_y, sel_x);
551         if (lo->seld[lo->sel]) {
552             wattrset(lo->win, A_BOLD);
553         } else {
554             wattrset(lo->win, item_attr);
555         }
556         if (strlen(lo->name[lo->sel]) > lo->w - 2) {
557             strncpy(tmp, lo->name[lo->sel], lo->w - 2);
558             tmp[lo->w - 2] = 0;
559             waddstr(lo->win, tmp);
560         } else {
561             waddstr(lo->win, lo->name[lo->sel]);
562         }
563
564         switch (key) {
565         case KEY_DOWN:
566         case ctrl('n'):
567             if (sel_y < lo->y + lo->h-1) {
568                 if (lo->sel < lo->n-1) {
569                     sel_y++;
570                     lo->sel++;
571                 }
572             } else {
573                 if (lo->sel < lo->n-1) {
574                     lo->sel++;
575                     lo->scroll++;
576                     DrawNames(lo);
577                     wrefresh(lo->win);
578                 }
579             }
580             break;
581         case KEY_UP:
582         case ctrl('p'):
583             if (sel_y > lo->y+2) {
584                 if (lo->sel > 0) {
585                     sel_y--;
586                     lo->sel--;
587                 }
588             } else {
589                 if (lo->sel > 0) {
590                     lo->sel--;
591                     lo->scroll--;
592                     DrawNames(lo);
593                     wrefresh(lo->win);
594                 }
595             }
596             break;
597         case KEY_HOME:
598         case ctrl('a'):
599             lo->sel = 0;
600             lo->scroll = 0;
601             sel_y = lo->y + 2;
602             DrawNames(lo);
603             wrefresh(lo->win);
604             break;
605         case KEY_END:
606         case ctrl('e'):
607             if (lo->n < lo->h - 3) {
608                 lo->sel = lo->n-1;
609                 lo->scroll = 0;
610                 sel_y = lo->y + 2 + lo->sel - lo->scroll;
611             } else {
612                 /* more than one page of list */
613                 lo->sel = lo->n-1;
614                 lo->scroll = lo->n-1 - (lo->h-3);
615                 sel_y = lo->y + 2 + lo->sel - lo->scroll;
616                 DrawNames(lo);
617                 wrefresh(lo->win);
618             }
619             break;
620         case KEY_NPAGE:
621         case ctrl('f'):
622             lo->sel += lo->h - 2;
623             if (lo->sel >= lo->n) lo->sel = lo->n - 1;
624             lo->scroll += lo->h - 2;
625             if (lo->scroll >= lo->n - 1) lo->scroll = lo->n - 1;
626             if (lo->scroll < 0) lo->scroll = 0;
627             sel_y = lo->y + 2 + lo->sel - lo->scroll;
628             DrawNames(lo);
629             wrefresh(lo->win);
630             break;
631         case KEY_PPAGE:
632         case ctrl('b'):
633             lo->sel -= lo->h - 2;
634             if (lo->sel < 0) lo->sel = 0;
635             lo->scroll -= lo->h - 2;
636             if (lo->scroll < 0) lo->scroll = 0;
637             sel_y = lo->y + 2 + lo->sel - lo->scroll;
638             DrawNames(lo);
639             wrefresh(lo->win);
640             break;
641         default:
642             quit = TRUE;
643             break;
644         }
645         /* Draw % indication */
646         sprintf(perc, "(%3d%%)", MIN(100, (int)
647                                      (100 * (lo->sel+lo->h - 2) / MAX(1, lo->n))));
648         wmove(lo->win, lo->y + lo->h, lo->x + lo->w - 8);
649         wattrset(lo->win, dialog_attr);
650         waddstr(lo->win, perc);
651
652         /* draw current item in inverse */
653         wmove(lo->win, sel_y, sel_x);
654         wattrset(lo->win, item_selected_attr);
655         if (strlen(lo->name[lo->sel]) > lo->w - 2) {
656             /* when printing in inverse video show the last characters in the */
657             /* name that will fit in the window */
658             strncpy(tmp,
659                     lo->name[lo->sel] + strlen(lo->name[lo->sel]) - (lo->w - 2),
660                     lo->w - 2);
661             tmp[lo->w - 2] = 0;
662             waddstr(lo->win, tmp);
663         } else {
664             waddstr(lo->win, lo->name[lo->sel]);
665         }
666         if (!quit) key = wgetch(lo->win);
667     }
668
669     if (key == ESC) {
670         return(SEL_ESC);
671     }
672     if (key == '\t') {
673         return(SEL_TAB);
674     }
675     if ((key == KEY_BTAB) || (key == ctrl('b'))) {
676         return(SEL_BACKTAB);
677     }
678     if ((key == '\n') || (key == '\r')) {
679         strcpy(lo->elt, lo->name[lo->sel]);
680         return(SEL_CR);
681     }
682     return(key);
683 } /* SelectListObj() */
684
685 void
686 DelListObj(ListObj *lo)
687 /*
688  * Desc: Free the space occupied by the listobject
689  */
690 {
691     free(lo->title);
692     if (lo->seld != NULL) free(lo->seld);
693     free(lo);
694
695     return;
696 } /* DelListObj() */
697
698 void
699 MarkCurrentListObj(ListObj *lo)
700 /*
701  * Desc: mark the current item for the selection list
702  */
703 {
704     lo->seld[lo->sel] = !(lo->seld[lo->sel]);
705     DrawNames(lo);
706
707     return;
708 } /* MarkCurrentListObj() */
709
710 void
711 MarkAllListObj(ListObj *lo)
712 /*
713  * Desc: mark all items
714  */
715 {
716     int i;
717
718     for (i=0; i<lo->n; i++) {
719         lo->seld[i] = TRUE;
720     }
721     DrawNames(lo);
722
723     return;
724 } /* MarkAllListObj() */
725
726 void
727 UnMarkAllListObj(ListObj *lo)
728 /*
729  * Desc: unmark all items
730  */
731 {
732     int i;
733
734     for (i=0; i<lo->n; i++) {
735         lo->seld[i] = FALSE;
736     }
737     DrawNames(lo);
738
739     return;
740 } /* UnMarkAllListObj() */
741
742
743 /***********************************************************************
744  *
745  * ButtonObj routines
746  *
747  ***********************************************************************/
748
749
750 void
751 RefreshButtonObj(ButtonObj *bo)
752 /*
753  * Desc: redraw the button
754  */
755 {
756     draw_box(bo->win, bo->y, bo->x, 3, bo->w, dialog_attr, border_attr);
757     print_button(bo->win, bo->title, bo->y+1, bo->x+2, FALSE);
758
759     return;
760 } /* RefreshButtonObj() */
761
762 ButtonObj *
763 NewButtonObj(WINDOW *win, char *title, int *pushed, int y, int x)
764 /*
765  * Desc: Create a new button object
766  */
767 {
768     ButtonObj   *bo;
769
770     bo = (ButtonObj *) malloc( sizeof(ButtonObj) );
771
772     bo->win = win;
773     bo->title = (char *) malloc( strlen(title) + 1);
774     strcpy(bo->title, title);
775     bo->x = x;
776     bo->y = y;
777     bo->w = strlen(title) + 6;
778     bo->h = 3;
779     bo->pushed = pushed;
780
781     RefreshButtonObj(bo);
782
783     return(bo);
784 } /* NewButtonObj() */
785
786 int
787 SelectButtonObj(ButtonObj *bo)
788 /*
789  * Desc: Wait for buttonpresses or TAB's to move on, or ESC ESC
790  */
791 {
792     int key;
793
794     print_button(bo->win, bo->title, bo->y+1, bo->x+2, TRUE);
795     wmove(bo->win, bo->y+1, bo->x+(bo->w/2)-1);
796     key = wgetch(bo->win);
797     print_button(bo->win, bo->title, bo->y+1, bo->x+2, FALSE);
798     switch(key) {
799     case '\t':
800         return(SEL_TAB);
801         break;
802     case KEY_BTAB:
803     case ctrl('b'):
804         return(SEL_BACKTAB);
805     case '\n':
806     case '\r':
807         *(bo->pushed) = TRUE;
808         return(SEL_BUTTON);
809         break;
810     case ESC:
811         return(SEL_ESC);
812         break;
813     default:
814         return(key);
815         break;
816     }
817 } /* SelectButtonObj() */
818
819 void
820 DelButtonObj(ButtonObj *bo)
821 /*
822  * Desc: Free the space occupied by <bo>
823  */
824 {
825     free(bo->title);
826     free(bo);
827
828     return;
829 } /* DelButtonObj() */