/* * Program: objects.c * Author: Marc van Kempen * Desc: Implementation of UI-objects: * - String input fields * - List selection * - Buttons * * Copyright (c) 1995, Marc van Kempen * * All rights reserved. * * This software may be used, modified, copied, distributed, and * sold, in both source and binary form provided that the above * copyright and these terms are retained, verbatim, as the first * lines of this file. Under no circumstances is the author * responsible for the proper functioning of this software, nor does * the author assume any responsibility for damages incurred with * its use. * */ #include #include #include #include #include "dialog.priv.h" #include "ui_objects.h" #define ESC 27 /*********************************************************************** * * Obj routines * ***********************************************************************/ void AddObj(ComposeObj **Obj, int objtype, void *obj) /* * Desc: Add the object to the list of objects */ { if (*Obj == NULL) { /* Create the root object */ *Obj = (ComposeObj *) malloc( sizeof(ComposeObj) ); if (!Obj) { printf("AddObj: Error malloc'ing ComposeObj\n"); exit(-1); } (*Obj)->objtype = objtype; (*Obj)->obj = obj; (*Obj)->next = NULL; (*Obj)->prev = NULL; } else { ComposeObj *o = *Obj; /* create the next object */ while (o->next) o = (ComposeObj *) o->next; o->next = (struct ComposeObj *) malloc( sizeof(ComposeObj) ); if (!o->next) { printf("AddObj: Error malloc'ing o->next\n"); exit(-1); } o->next->objtype = objtype; o->next->obj = obj; o->next->next = NULL; o->next->prev = o; } return; } /* AddObj() */ void FreeObj(ComposeObj *Obj) /* * Desc: free the memory occupied by *Obj */ { ComposeObj *o = Obj; o = Obj; while (o) { o = Obj->next; free(Obj); Obj = o; } return; } /* FreeObj() */ int ReadObj(ComposeObj *Obj) /* * Desc: navigate through the different objects calling their * respective navigation routines as necessary * Pre: Obj != NULL */ { ComposeObj *o; ComposeObj *last; /* the last object in the list */ int ret; /* the return value from the selection routine */ /* find the last object in the list */ last = Obj; while (last->next) last = last->next; ret = 0; o = Obj; while ((ret != SEL_BUTTON) && (ret != SEL_ESC)) { switch(o->objtype) { case STRINGOBJ: ret = SelectStringObj((StringObj *) o->obj); break; case LISTOBJ: ret = SelectListObj((ListObj *) o->obj); break; case BUTTONOBJ: ret = SelectButtonObj((ButtonObj *) o->obj); break; } switch(ret) { case KEY_DOWN: case SEL_CR: case SEL_TAB: /* move to the next object in the list */ if (o->next != NULL) { o = o->next; /* next object */ } else { o = Obj; /* beginning of the list */ } break; case KEY_UP: case SEL_BACKTAB: /* move to the previous object in the list */ if (o->prev != NULL) { o = o->prev; /* previous object */ } else { o = last; /* end of the list */ } break; case KEY_F(1): /* display help_file */ case '?': display_helpfile(); break; } } return(ret); } /* ReadObj() */ int PollObj(ComposeObj **Obj) { ComposeObj *last; /* the last object in the list */ ComposeObj *first; /* the first object in the list */ int ret; /* the return value from the selection routine */ /* find the last object in the list */ last = *Obj; while (last->next) last = last->next; /* find the first object in the list */ first = *Obj; while (first->prev) first = first->prev; ret = 0; switch((*Obj)->objtype) { case STRINGOBJ: ret = SelectStringObj((StringObj *) (*Obj)->obj); break; case LISTOBJ: ret = SelectListObj((ListObj *) (*Obj)->obj); break; case BUTTONOBJ: ret = SelectButtonObj((ButtonObj *) (*Obj)->obj); break; } switch(ret) { case KEY_DOWN: case SEL_CR: case SEL_TAB: /* move to the next object in the list */ if ((*Obj)->next != NULL) { *Obj = (*Obj)->next; /* next object */ } else { *Obj = first; /* beginning of the list */ } break; case KEY_UP: case SEL_BACKTAB: /* move to the previous object in the list */ if ((*Obj)->prev != NULL) { *Obj = (*Obj)->prev; /* previous object */ } else { *Obj = last; /* end of the list */ } break; } return(ret); } /* PollObj() */ void DelObj(ComposeObj *Obj) /* * Desc: Free all objects */ { ComposeObj *o; o = Obj; while (Obj != NULL) { switch(Obj->objtype) { case STRINGOBJ: DelStringObj((StringObj *) Obj->obj); break; case LISTOBJ: DelListObj((ListObj *) Obj->obj); break; case BUTTONOBJ: DelButtonObj((ButtonObj *) Obj->obj); break; } Obj = Obj->next; } FreeObj(o); } /* DelObj() */ /*********************************************************************** * * StringObj routines * ***********************************************************************/ static void outstr(WINDOW *win, char *str, int attrs) { if (attrs & DITEM_NO_ECHO) { char *cpy; int n = strlen(str); cpy = alloca(n + 1); memset(cpy, '*', n); cpy[n] = '\0'; waddstr(win, cpy); } else waddstr(win, str); } void RefreshStringObj(StringObj *so) /* * Desc: redraw the object */ { char tmp[512]; wmove(so->win, so->y, so->x+1); wattrset(so->win, dialog_attr); waddstr(so->win, so->title); draw_box(so->win, so->y+1, so->x, 3, so->w, dialog_attr, border_attr); wattrset(so->win, item_attr); wmove(so->win, so->y+2, so->x+1); if (strlen(so->s) > so->w-2) { strncpy(tmp, (char *) so->s + strlen(so->s) - so->w + 2, so->w - 1); outstr(so->win, tmp, so->attr_mask); } else { outstr(so->win, so->s, so->attr_mask); } return; } /* RefreshStringObj() */ StringObj * NewStringObj(WINDOW *win, char *title, char *s, int y, int x, int w, int len) /* * Desc: Initialize a new stringobj and return a pointer to it. * Draw the object on the screen at the specified coordinates */ { StringObj *so; /* Initialize a new object */ so = (StringObj *) malloc( sizeof(StringObj) ); if (!so) { printf("NewStringObj: Error malloc'ing StringObj\n"); exit(-1); } so->title = (char *) malloc( strlen(title) + 1); if (!so->title) { printf("NewStringObj: Error malloc'ing so->title\n"); exit(-1); } strcpy(so->title, title); so->s = s; strcpy(so->s, s); so->x = x; so->y = y; so->w = w; so->len = len; so->win = win; so->attr_mask = DialogInputAttrs; /* Grossly use a global to avoid changing API */ /* Draw it on the screen */ RefreshStringObj(so); return(so); } /* NewStringObj() */ int SelectStringObj(StringObj *so) /* * Desc: get input using the info in */ { int key; char tmp[so->len+1]; strcpy(tmp, so->s); key = line_edit(so->win, so->y+2, so->x+1, so->len, so->w-2, inputbox_attr, TRUE, tmp, so->attr_mask); if ((key == '\n') || (key == '\r') || (key == '\t') || key == (KEY_BTAB) ) { strcpy(so->s, tmp); } RefreshStringObj(so); if (key == ESC) { return(SEL_ESC); } if (key == '\t') { return(SEL_TAB); } if ( (key == KEY_BTAB) || (key == KEY_F(2)) ) { return(SEL_BACKTAB); } if ((key == '\n') || (key == '\r')) { return(SEL_CR); } return(key); } /* SelectStringObj() */ void DelStringObj(StringObj *so) /* * Desc: Free the space occupied by */ { free(so->title); free(so); return; } /*********************************************************************** * * ListObj routines * ***********************************************************************/ void DrawNames(ListObj *lo) /* * Desc: Just refresh the names, not the surrounding box and title */ { int i, j, h, x, y; char tmp[MAXPATHLEN]; x = lo->x + 1; y = lo->y + 2; h = lo->h - 2; for (i=lo->scroll; in && iscroll+h; i++) { wmove(lo->win, y+i-lo->scroll, x); if (lo->seld[i]) { wattrset(lo->win, A_BOLD); } else { wattrset(lo->win, item_attr); } if (strlen(lo->name[i]) > lo->w-2) { strncpy(tmp, lo->name[i], lo->w-2); tmp[lo->w - 2] = 0; waddstr(lo->win, tmp); } else { waddstr(lo->win, lo->name[i]); for (j=strlen(lo->name[i]); jw-2; j++) waddstr(lo->win, " "); } } wattrset(lo->win, item_attr); while (iscroll+h) { wmove(lo->win, y+i-lo->scroll, x); for (j=0; jw-2; j++) waddstr(lo->win, " "); i++; } return; } /* DrawNames() */ void RefreshListObj(ListObj *lo) /* * Desc: redraw the list object */ { char perc[7]; /* setup the box */ wmove(lo->win, lo->y, lo->x+1); wattrset(lo->win, dialog_attr); waddstr(lo->win, lo->title); draw_box(lo->win, lo->y+1, lo->x, lo->h, lo->w, dialog_attr, border_attr); /* draw the names */ DrawNames(lo); /* Draw % indication */ sprintf(perc, "(%3d%%)", MIN(100, (int) (100 * (lo->sel+lo->h-2) / MAX(1, lo->n)))); wmove(lo->win, lo->y + lo->h, lo->x + lo->w - 8); wattrset(lo->win, dialog_attr); waddstr(lo->win, perc); return; } /* RefreshListObj() */ ListObj * NewListObj(WINDOW *win, char *title, char **list, char *listelt, int y, int x, int h, int w, int n) /* * Desc: create a listobj, draw it on the screen and return a pointer to it. */ { ListObj *lo; int i; /* Initialize a new object */ lo = (ListObj *) malloc( sizeof(ListObj) ); if (!lo) { fprintf(stderr, "NewListObj: Error malloc'ing ListObj\n"); exit(-1); } lo->title = (char *) malloc( strlen(title) + 1); if (!lo->title) { fprintf(stderr, "NewListObj: Error malloc'ing lo->title\n"); exit(-1); } strcpy(lo->title, title); lo->name = list; if (n>0) { lo->seld = (int *) malloc( n * sizeof(int) ); if (!lo->seld) { fprintf(stderr, "NewListObj: Error malloc'ing lo->seld\n"); exit(-1); } for (i=0; iseld[i] = FALSE; } } else { lo->seld = NULL; } lo->y = y; lo->x = x; lo->w = w; lo->h = h; lo->n = n; lo->scroll = 0; lo->sel = 0; lo->elt = listelt; lo->win = win; /* Draw the object on the screen */ RefreshListObj(lo); return(lo); } /* NewListObj() */ void UpdateListObj(ListObj *lo, char **list, int n) /* * Desc: Update the list in the listobject with the provided list * Pre: lo->name "has been freed" * "(A i: 0<=in: "lo->name[i] has been freed")" */ { int i; if (lo->seld) { free(lo->seld); } /* Rewrite the list in the object */ lo->name = list; if (n>0) { lo->seld = (int *) malloc( n * sizeof(int) ); if (!lo->seld) { fprintf(stderr, "UpdateListObj: Error malloc'ing lo->seld\n"); exit(-1); } for (i=0; iseld[i] = FALSE; } } else { lo->seld = NULL; } lo->n = n; lo->scroll = 0; lo->sel = 0; /* Draw the object on the screen */ RefreshListObj(lo); return; } /* UpdateListObj() */ int SelectListObj(ListObj *lo) /* * Desc: get a listname (or listnames), TAB to move on, or ESC ESC to exit * Pre: lo->n >= 1 */ { int key, sel_x, sel_y, quit; char tmp[MAXPATHLEN]; char perc[4]; sel_x = lo->x+1; sel_y = lo->y + 2 + lo->sel - lo->scroll; if (lo->n == 0) return(SEL_TAB); keypad(lo->win, TRUE); /* Draw current selection in inverse video */ wmove(lo->win, sel_y, sel_x); wattrset(lo->win, item_selected_attr); waddstr(lo->win, lo->name[lo->sel]); key = wgetch(lo->win); quit = FALSE; while ((key != '\t') && (key != '\n') && (key != '\r') && (key != ESC) && (key != KEY_F(1)) && (key != '?') && !quit) { /* first draw current item in normal video */ wmove(lo->win, sel_y, sel_x); if (lo->seld[lo->sel]) { wattrset(lo->win, A_BOLD); } else { wattrset(lo->win, item_attr); } if (strlen(lo->name[lo->sel]) > lo->w - 2) { strncpy(tmp, lo->name[lo->sel], lo->w - 2); tmp[lo->w - 2] = 0; waddstr(lo->win, tmp); } else { waddstr(lo->win, lo->name[lo->sel]); } switch (key) { case KEY_DOWN: case ctrl('n'): if (sel_y < lo->y + lo->h-1) { if (lo->sel < lo->n-1) { sel_y++; lo->sel++; } } else { if (lo->sel < lo->n-1) { lo->sel++; lo->scroll++; DrawNames(lo); wrefresh(lo->win); } } break; case KEY_UP: case ctrl('p'): if (sel_y > lo->y+2) { if (lo->sel > 0) { sel_y--; lo->sel--; } } else { if (lo->sel > 0) { lo->sel--; lo->scroll--; DrawNames(lo); wrefresh(lo->win); } } break; case KEY_HOME: case ctrl('a'): lo->sel = 0; lo->scroll = 0; sel_y = lo->y + 2; DrawNames(lo); wrefresh(lo->win); break; case KEY_END: case ctrl('e'): if (lo->n < lo->h - 3) { lo->sel = lo->n-1; lo->scroll = 0; sel_y = lo->y + 2 + lo->sel - lo->scroll; } else { /* more than one page of list */ lo->sel = lo->n-1; lo->scroll = lo->n-1 - (lo->h-3); sel_y = lo->y + 2 + lo->sel - lo->scroll; DrawNames(lo); wrefresh(lo->win); } break; case KEY_NPAGE: case ctrl('f'): lo->sel += lo->h - 2; if (lo->sel >= lo->n) lo->sel = lo->n - 1; lo->scroll += lo->h - 2; if (lo->scroll >= lo->n - 1) lo->scroll = lo->n - 1; if (lo->scroll < 0) lo->scroll = 0; sel_y = lo->y + 2 + lo->sel - lo->scroll; DrawNames(lo); wrefresh(lo->win); break; case KEY_PPAGE: case ctrl('b'): lo->sel -= lo->h - 2; if (lo->sel < 0) lo->sel = 0; lo->scroll -= lo->h - 2; if (lo->scroll < 0) lo->scroll = 0; sel_y = lo->y + 2 + lo->sel - lo->scroll; DrawNames(lo); wrefresh(lo->win); break; default: quit = TRUE; break; } /* Draw % indication */ sprintf(perc, "(%3d%%)", MIN(100, (int) (100 * (lo->sel+lo->h - 2) / MAX(1, lo->n)))); wmove(lo->win, lo->y + lo->h, lo->x + lo->w - 8); wattrset(lo->win, dialog_attr); waddstr(lo->win, perc); /* draw current item in inverse */ wmove(lo->win, sel_y, sel_x); wattrset(lo->win, item_selected_attr); if (strlen(lo->name[lo->sel]) > lo->w - 2) { /* when printing in inverse video show the last characters in the */ /* name that will fit in the window */ strncpy(tmp, lo->name[lo->sel] + strlen(lo->name[lo->sel]) - (lo->w - 2), lo->w - 2); tmp[lo->w - 2] = 0; waddstr(lo->win, tmp); } else { waddstr(lo->win, lo->name[lo->sel]); } if (!quit) key = wgetch(lo->win); } if (key == ESC) { return(SEL_ESC); } if (key == '\t') { return(SEL_TAB); } if ((key == KEY_BTAB) || (key == ctrl('b'))) { return(SEL_BACKTAB); } if ((key == '\n') || (key == '\r')) { strcpy(lo->elt, lo->name[lo->sel]); return(SEL_CR); } return(key); } /* SelectListObj() */ void DelListObj(ListObj *lo) /* * Desc: Free the space occupied by the listobject */ { free(lo->title); if (lo->seld != NULL) free(lo->seld); free(lo); return; } /* DelListObj() */ void MarkCurrentListObj(ListObj *lo) /* * Desc: mark the current item for the selection list */ { lo->seld[lo->sel] = !(lo->seld[lo->sel]); DrawNames(lo); return; } /* MarkCurrentListObj() */ void MarkAllListObj(ListObj *lo) /* * Desc: mark all items */ { int i; for (i=0; in; i++) { lo->seld[i] = TRUE; } DrawNames(lo); return; } /* MarkAllListObj() */ void UnMarkAllListObj(ListObj *lo) /* * Desc: unmark all items */ { int i; for (i=0; in; i++) { lo->seld[i] = FALSE; } DrawNames(lo); return; } /* UnMarkAllListObj() */ /*********************************************************************** * * ButtonObj routines * ***********************************************************************/ void RefreshButtonObj(ButtonObj *bo) /* * Desc: redraw the button */ { draw_box(bo->win, bo->y, bo->x, 3, bo->w, dialog_attr, border_attr); print_button(bo->win, bo->title, bo->y+1, bo->x+2, FALSE); return; } /* RefreshButtonObj() */ ButtonObj * NewButtonObj(WINDOW *win, char *title, int *pushed, int y, int x) /* * Desc: Create a new button object */ { ButtonObj *bo; bo = (ButtonObj *) malloc( sizeof(ButtonObj) ); bo->win = win; bo->title = (char *) malloc( strlen(title) + 1); strcpy(bo->title, title); bo->x = x; bo->y = y; bo->w = strlen(title) + 6; bo->h = 3; bo->pushed = pushed; RefreshButtonObj(bo); return(bo); } /* NewButtonObj() */ int SelectButtonObj(ButtonObj *bo) /* * Desc: Wait for buttonpresses or TAB's to move on, or ESC ESC */ { int key; print_button(bo->win, bo->title, bo->y+1, bo->x+2, TRUE); wmove(bo->win, bo->y+1, bo->x+(bo->w/2)-1); key = wgetch(bo->win); print_button(bo->win, bo->title, bo->y+1, bo->x+2, FALSE); switch(key) { case '\t': return(SEL_TAB); break; case KEY_BTAB: case ctrl('b'): return(SEL_BACKTAB); case '\n': case '\r': *(bo->pushed) = TRUE; return(SEL_BUTTON); break; case ESC: return(SEL_ESC); break; default: return(key); break; } } /* SelectButtonObj() */ void DelButtonObj(ButtonObj *bo) /* * Desc: Free the space occupied by */ { free(bo->title); free(bo); return; } /* DelButtonObj() */