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