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
AddObj(ComposeObj ** Obj,int objtype,void * obj)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
FreeObj(ComposeObj * Obj)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
ReadObj(ComposeObj * Obj)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
PollObj(ComposeObj ** Obj)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
DelObj(ComposeObj * Obj)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
outstr(WINDOW * win,char * str,int attrs)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
RefreshStringObj(StringObj * so)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 *
NewStringObj(WINDOW * win,char * title,char * s,int y,int x,int w,int len)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
SelectStringObj(StringObj * so)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
DelStringObj(StringObj * so)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
DrawNames(ListObj * lo)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
RefreshListObj(ListObj * lo)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 *
NewListObj(WINDOW * win,char * title,char ** list,char * listelt,int y,int x,int h,int w,int n)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
UpdateListObj(ListObj * lo,char ** list,int n)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
SelectListObj(ListObj * lo)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
DelListObj(ListObj * lo)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
MarkCurrentListObj(ListObj * lo)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
MarkAllListObj(ListObj * lo)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
UnMarkAllListObj(ListObj * lo)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
RefreshButtonObj(ButtonObj * bo)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 *
NewButtonObj(WINDOW * win,char * title,int * pushed,int y,int x)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
SelectButtonObj(ButtonObj * bo)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
DelButtonObj(ButtonObj * bo)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() */
830