/*- * Copyright (c) 1995 * Paul Richards. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * verbatim and that no modifications are made prior to this * point in the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Paul Richards. * 4. The name Paul Richards may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL PAUL RICHARDS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include "internal.h" extern hash_table *global_bindings; struct attr_cmd { char *name; int attr; }; static struct attr_cmd attr_cmds[] = { { "standout", 0x00010000}, { "underline", 0x00020000}, { "reverse", 0x00040000}, { "blink", 0x00080000}, { "bold", 0x00200000 } }; int done=0; int init_field(char *key, void *data, void *arg) { struct Tuple *tuple = (struct Tuple *)data; struct Tuple *def_tuple; struct Field *def, *field; int i; int len, lim; int strwidth; field = (struct Field *)tuple->addr; /* Field definitions are global, at least for now */ def_tuple = form_get_tuple(global_bindings, field->defname, FT_FIELD_DEF); if (!def_tuple) { warnx("Field definition not found -- skipping field"); return (-1); } def = (struct Field *)def_tuple->addr; field->height = def->height; field->width = def->width; field->attr = def->attr; field->selattr = def->selattr; field->type = def->type; switch (field->type) { case FF_INPUT: field->field.input = malloc(sizeof (struct InputField)); if (!field->field.input) { warnx("Couldn't allocate memory for input field"); return (-1); } field->field.input->limit = def->field.input->limit; /* Force height to one regardless, at least for now :-) */ field->height = 1; if (!field->width && !field->field.input->limit) { field->width = calc_string_width(def->field.input->label); field->field.input->limit = field->width; } else if (!field->width) field->width = field->field.input->limit; else if (!field->field.input->limit) field->field.input->limit = field->width; if (field->field.input->limit < field->width) field->width = field->field.input->limit; strwidth = strlen(def->field.input->label); field->field.input->input = malloc(strwidth + 1); if (!field->field.input->input) { warnx("Couldn't allocate memory for input field text"); return (-1); } field->field.input->label = malloc(strwidth + 1); if (!field->field.input->label) { warnx("Couldn't allocate memory for input field label"); return (-1); } strncpy(field->field.input->label, def->field.input->label, strwidth + 1); field->field.input->lbl_flag = def->field.input->lbl_flag; /* * If it's a label then clear the input string * otherwise copy the default there. */ if (field->field.input->lbl_flag) field->field.input->input[0] = '\0'; else if (field->field.input->label) { strncpy(field->field.input->input, field->field.input->label, strwidth + 1); field->field.input->input[strwidth] = 0; } break; case FF_TEXT: field->field.text = malloc(sizeof (struct TextField)); if (!field->field.text) { warnx("Couldn't allocate memory for text field"); return (FS_ERROR); } strwidth = strlen(def->field.text->text); if (!field->width) field->width = calc_string_width(def->field.text->text); field->field.text->text = malloc(strwidth + 1); if (!field->field.text->text) { warnx("Couldn't allocate memory for text field text"); return (FS_ERROR); } else strncpy(field->field.text->text, def->field.text->text, strwidth + 1); if (!field->height) calc_field_height(field, field->field.text->text); break; case FF_MENU: field->field.menu = malloc(sizeof (struct MenuField)); if (!field->field.menu) { warnx("Couldn't allocate memory for menu field"); return (FS_ERROR); } field->field.menu->no_options = 0; field->height = 1; lim = 0; for (i=0; i < def->field.menu->no_options; i++) { field->field.menu->no_options = add_menu_option(field->field.menu, def->field.menu->options[i]); if (!field->field.menu->no_options) { warnx("Couldn't add menu option"); return (FS_ERROR); } len = calc_string_width(def->field.menu->options[i]); if (len > lim) lim = len; } if (!field->width) field->width = lim; break; case FF_ACTION: field->field.action = malloc(sizeof (struct ActionField)); if (!field->field.action) { warnx("Couldn't allocate memory for action field"); return (FS_ERROR); } if (!field->width) field->width = calc_string_width(def->field.action->text); strwidth = strlen(def->field.action->text); field->field.action->text = malloc(strwidth + 1); if (!field->field.action->text) { warnx("Couldn't allocate memory for text field text"); return (FS_ERROR); } else strncpy(field->field.action->text, def->field.action->text, strwidth + 1); if (!field->height) calc_field_height(field, field->field.action->text); field->field.action->fn = def->field.action->fn; break; default: break; } return (1); } void display_field(WINDOW *window, struct Field *field) { wattrset(window, field->attr); wmove(window, field->y, field->x); switch (field->type) { case FF_TEXT: display_text(window, field); break; case FF_MENU: display_menu(window, field); break; case FF_INPUT: display_input(window, field); break; case FF_ACTION: display_action(window, field); break; case FF_UNKNOWN: default: break; } wattrset(window, 0); wrefresh(window); } void display_text(WINDOW *window, struct Field *field) { if (print_string(window, field->y, field->x, field->height, field->width, field->field.text->text) == ERR) print_status("Illegal scroll in print_string"); } void display_input(WINDOW *window, struct Field *field) { if (field->field.input->lbl_flag) { if (print_string(window, field->y, field->x, field->height, field->width, field->field.input->label) == ERR) print_status("Illegal scroll in print_string"); } else if (print_string(window, field->y, field->x, field->height, field->width, field->field.input->input) == ERR) print_status("Illegal scroll in print_string"); } void display_menu(WINDOW *window, struct Field *field) { if (print_string(window, field->y, field->x, field->height, field->width, field->field.menu->options[field->field.menu->selected]) == ERR) print_status("Illegal scroll in print_string"); } void display_action(WINDOW *window, struct Field *field) { if (print_string(window, field->y, field->x, field->height, field->width, field->field.action->text) == ERR) print_status("Illegal scroll in print_string"); } int do_action(struct Form *form) { struct Field *field = form->current_field; struct Tuple *tuple; int ch; void (* fn)(); display_action(form->window, field); wmove(form->window, field->y, field->x); for (;;) { ch = wgetch(form->window); if (ch == FK_ACCEPT) { tuple = form_get_tuple(form->bindings, field->field.action->fn, FT_FUNC); if (!tuple) { print_status("No function bound to action"); beep(); continue; } else { fn = tuple->addr; (*fn)(form); return (FS_OK); } } else ch = do_key_bind(form, ch); if (ch == FS_OK) return (FS_OK); else if (ch == FS_ERROR) continue; } } int do_menu(struct Form *form) { struct Field *field = form->current_field; int ch; for (;;) { display_menu(form->window, field); wmove(form->window, field->y, field->x); ch = wgetch(form->window); switch (ch) { case ' ': print_status(""); field->field.menu->selected++; if (field->field.menu->selected >= field->field.menu->no_options) field->field.menu->selected = 0; ch = FS_OK; break; default: ch = do_key_bind(form, ch); break; } if (ch == FS_OK) return (FS_OK); else if (ch == FS_ERROR) { beep(); continue; } else { print_status("Hit the space bar to toggle through options"); beep(); continue; } } } int do_field(struct Form *form) { struct Tuple *tuple; struct Field *field = form->current_field; void (* fn)(); int status; /* Do field entry tasks */ if (field->enter) { tuple = form_get_tuple(form->bindings, field->enter, FT_FUNC); if (tuple) { fn = tuple->addr; (*fn)(form); } } switch (field->type) { case FF_TEXT: status = FS_OK; display_text(form->window, field); break; case FF_INPUT: status = do_input(form); break; case FF_MENU: status = do_menu(form); break; case FF_ACTION: status = do_action(form); break; default: status = FF_UNKNOWN; beep(); print_status("Unknown field type"); form->current_field = form->prev_field; break; } /* Do field leave tasks */ if (field->leave) { tuple = form_get_tuple(form->bindings, field->leave, FT_FUNC); if (tuple) { fn = tuple->addr; (*fn)(form); } } return (status); } int parse_attr(WINDOW *window, char *string) { int inc = 0; struct attr_cmd *attr; if (*(string) == '\\') return (1); while (!isspace(*(string + inc))) { inc++; } for (attr = attr_cmds; attr->name; attr++) { if (strncmp(attr->name, string, inc)) continue; else { wattron(window, attr->attr); break; } } /* Skip trailing space after the attribute string */ while (isspace(*(string + inc))) inc++; return (inc); } int print_string(WINDOW *window, int y, int x, int height, int fwidth, char *string) { int len, skip; int width; if (!string) len = -1; len = strlen(string); if (wmove(window, y, x) == ERR) return (ERR); while (height--) { width = fwidth; while (width--) { if (len-- > 0) { if (*string == '\\') { string++; len--; skip = parse_attr(window, string); len -= skip; string += skip; } if (waddch(window, *string++) == ERR) return (ERR); } else if (waddch(window, ' ') == ERR) return (ERR); } if (wmove(window, ++y, x) == ERR) return (ERR); } return (OK); } void print_status(char *msg) { if (wmove(stdscr, LINES-1, 0) == ERR) { endwin(); exit(1); } wclrtoeol(stdscr); wstandout(stdscr); if (wprintw(stdscr, "%s", msg) == ERR) { endwin(); exit(1); } wstandend(stdscr); wrefresh(stdscr); } int do_input(struct Form *form) { struct Field *field = form->current_field; int len; int disp_off=0, abspos=0, cursor = 0; unsigned int ch; #define DISPOFF ((len < field->width) ? 0 : len - field->width) #define CURSPOS ((len < field->width) ? len : field->width) len = strlen(field->field.input->input); display_input(form->window, field); cursor = CURSPOS; abspos = cursor; for (;;) { wmove(form->window, field->y, field->x+cursor); wrefresh(form->window); ch = wgetch(form->window); ch = do_key_bind(form, ch); /* * If there was a valid motion command then we've * moved to a new field so just return. If the motion * command was invalid then just go around and get another * keystroke. Otherwise, it was not a motion command. */ if (ch == FS_OK) return (FS_OK); else if (ch == FS_ERROR) continue; print_status(""); if (field->field.input->lbl_flag) { field->field.input->lbl_flag = 0; } if ((ch == FK_CHOME) || (ch == '')) { disp_off = 0; cursor = 0; abspos = 0; } else if ((ch == FK_CEND) || (ch == '')) { disp_off = DISPOFF; abspos = len; cursor = CURSPOS; } else if (ch == FK_CDEL) { if (!(len-abspos)) beep(); else { bcopy(field->field.input->input+abspos+1, field->field.input->input+abspos, len - abspos); --len; } } else if ((ch == FK_CLEFT) || (ch == FK_CBS) || (ch == '')) { if (!abspos) beep(); else { if (ch == FK_CBS) { bcopy(field->field.input->input+abspos, field->field.input->input+abspos-1, len-abspos+1); --len; } --abspos; --cursor; if ((disp_off) && (cursor < 0)) { --disp_off; ++cursor; } } } else if ((ch == FK_CRIGHT) || (ch == '')) { if (abspos == len) beep(); else { ++abspos; if (++cursor >= field->width) { ++disp_off; --cursor; } } } else if ((isprint(ch)) && (len < field->field.input->limit)){ bcopy(field->field.input->input+abspos, field->field.input->input+abspos+1, len-abspos+1); field->field.input->input[abspos++] = ch; len++; if (++cursor > field->width) { ++disp_off; --cursor; } } else beep(); print_string(form->window, field->y, field->x, field->height, field->width, field->field.input->input+disp_off); } } /* * Calculate length of printable part of the string, * stripping out the attribute modifiers. */ int calc_string_width(char *string) { int len, width=0; if (!string) return (0); len = strlen(string); while (len) { if (*string != '\\') { width++; len--; string++; continue; } else { string++; len--; if (*string == '\\') { string++; width++; len--; continue; } else { while (!isspace(*string)) { string++; len--; } while (isspace(*string)) { string ++; len--; } } } } return (width); } /* Calculate a default height for a field */ void calc_field_height(struct Field *field, char *string) { int len; len = calc_string_width(string); if (!field->width) { /* * This is a failsafe, this routine shouldn't be called * with a width of 0, the width should be determined * first. */ field->height = 1; return; } if (len < field->width) { field->height = 1; return; } else field->height = len / field->width; if ((field->height*field->width) < len) field->height++; return; } void exit_form(struct Form *form) { form->status = FS_EXIT; } void cancel_form(struct Form *form) { form->status = FS_CANCEL; }