diff options
Diffstat (limited to 'Build/source/utils/dialog/dialog-1.1-20110707/editbox.c')
-rw-r--r-- | Build/source/utils/dialog/dialog-1.1-20110707/editbox.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/Build/source/utils/dialog/dialog-1.1-20110707/editbox.c b/Build/source/utils/dialog/dialog-1.1-20110707/editbox.c new file mode 100644 index 00000000000..c7da10c7410 --- /dev/null +++ b/Build/source/utils/dialog/dialog-1.1-20110707/editbox.c @@ -0,0 +1,721 @@ +/* + * $Id: editbox.c,v 1.55 2011/06/21 00:10:46 tom Exp $ + * + * editbox.c -- implements the edit box + * + * Copyright 2007-2010,2011 Thomas E. Dickey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1 + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to + * Free Software Foundation, Inc. + * 51 Franklin St., Fifth Floor + * Boston, MA 02110, USA. + */ + +#include <dialog.h> +#include <dlg_keys.h> + +#include <sys/stat.h> + +#define sTEXT -1 + +static void +fail_list(void) +{ + dlg_exiterr("File too large"); +} + +static void +grow_list(char ***list, int *have, int want) +{ + if (want > *have) { + size_t last = (size_t) *have; + size_t need = (size_t) (want | 31) + 3; + *have = (int) need; + (*list) = dlg_realloc(char *, need, *list); + if ((*list) == 0) { + fail_list(); + } + while (++last < need) { + (*list)[last] = 0; + } + } +} + +static void +load_list(const char *file, char ***list, int *rows) +{ + FILE *fp; + char *blob = 0; + struct stat sb; + unsigned n, pass; + unsigned need; + size_t size; + + *list = 0; + *rows = 0; + + if (stat(file, &sb) < 0 || + (sb.st_mode & S_IFMT) != S_IFREG) + dlg_exiterr("Not a file: %s", file); + + size = (size_t) sb.st_size; + if ((blob = dlg_malloc(char, size + 1)) == 0) + fail_list(); + blob[size] = '\0'; + + if ((fp = fopen(file, "r")) == 0) + dlg_exiterr("Cannot open: %s", file); + size = fread(blob, sizeof(char), size, fp); + fclose(fp); + + for (pass = 0; pass < 2; ++pass) { + int first = TRUE; + need = 0; + for (n = 0; n < size; ++n) { + if (first && pass) { + (*list)[need] = blob + n; + first = FALSE; + } + if (blob[n] == '\n') { + first = TRUE; + ++need; + if (pass) + blob[n] = '\0'; + } + } + if (pass) { + if (need == 0) { + (*list)[0] = dlg_strclone(""); + (*list)[1] = 0; + } else { + for (n = 0; n < need; ++n) { + (*list)[n] = dlg_strclone((*list)[n]); + } + (*list)[need] = 0; + } + } else { + grow_list(list, rows, (int) need + 1); + } + } + free(blob); +} + +static void +free_list(char ***list, int *rows) +{ + if (*list != 0) { + int n; + for (n = 0; n < (*rows); ++n) { + if ((*list)[n] != 0) + free((*list)[n]); + } + free(*list); + *list = 0; + } + *rows = 0; +} + +/* + * Display a single row in the editing window: + * thisrow is the actual row number that's being displayed. + * show_row is the row number that's highlighted for edit. + * base_row is the first row number in the window + */ +static bool +display_one(WINDOW *win, + char *text, + int thisrow, + int show_row, + int base_row, + int chr_offset) +{ + bool result; + + if (text != 0) { + dlg_show_string(win, + text, + chr_offset, + ((thisrow == show_row) + ? form_active_text_attr + : form_text_attr), + thisrow - base_row, + 0, + getmaxx(win), + FALSE, + FALSE); + result = TRUE; + } else { + result = FALSE; + } + return result; +} + +static void +display_all(WINDOW *win, + char **list, + int show_row, + int firstrow, + int lastrow, + int chr_offset) +{ + int limit = getmaxy(win); + int row; + + dlg_attr_clear(win, getmaxy(win), getmaxx(win), dialog_attr); + if (lastrow - firstrow >= limit) + lastrow = firstrow + limit; + for (row = firstrow; row < lastrow; ++row) { + if (!display_one(win, list[row], + row, show_row, firstrow, + (row == show_row) ? chr_offset : 0)) + break; + } +} + +static int +size_list(char **list) +{ + int result = 0; + + if (list != 0) { + while (*list++ != 0) { + ++result; + } + } + return result; +} + +static bool +scroll_to(int pagesize, int rows, int *base_row, int *this_row, int target) +{ + bool result = FALSE; + + if (target < *base_row) { + if (target < 0) { + if (*base_row == 0 && *this_row == 0) { + beep(); + } else { + *this_row = 0; + *base_row = 0; + result = TRUE; + } + } else { + *this_row = target; + *base_row = target; + result = TRUE; + } + } else if (target >= rows) { + if (*this_row < rows - 1) { + *this_row = rows - 1; + *base_row = rows - 1; + result = TRUE; + } else { + beep(); + } + } else if (target >= *base_row + pagesize) { + *this_row = target; + *base_row = target; + result = TRUE; + } else { + *this_row = target; + result = FALSE; + } + if (pagesize < rows) { + if (*base_row + pagesize >= rows) { + *base_row = rows - pagesize; + } + } else { + *base_row = 0; + } + return result; +} + +static int +col_to_chr_offset(const char *text, int col) +{ + const int *cols = dlg_index_columns(text); + const int *indx = dlg_index_wchars(text); + bool found = FALSE; + int result = 0; + unsigned n; + unsigned len = (unsigned) dlg_count_wchars(text); + + for (n = 0; n < len; ++n) { + if (cols[n] <= col && cols[n + 1] > col) { + result = indx[n]; + found = TRUE; + break; + } + } + if (!found && len && cols[len] == col) { + result = indx[len]; + } + return result; +} + +#define SCROLL_TO(target) show_all = scroll_to(pagesize, listsize, &base_row, &thisrow, target) + +#define PREV_ROW (*list)[thisrow - 1] +#define THIS_ROW (*list)[thisrow] +#define NEXT_ROW (*list)[thisrow + 1] + +#define UPDATE_COL(input) col_offset = dlg_edit_offset(input, chr_offset, box_width) + +static int +widest_line(char **list) +{ + int result = MAX_LEN; + char *value; + + if (list != 0) { + while ((value = *list++) != 0) { + int check = (int) strlen(value); + if (check > result) + result = check; + } + } + return result; +} + +#define NAVIGATE_BINDINGS \ + DLG_KEYS_DATA( DLGK_GRID_DOWN, KEY_DOWN ), \ + DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ), \ + DLG_KEYS_DATA( DLGK_GRID_LEFT, KEY_LEFT ), \ + DLG_KEYS_DATA( DLGK_GRID_UP, KEY_UP ), \ + DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), \ + DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), \ + DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ), \ + DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_END ), \ + DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_LL ), \ + DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ), \ + DLG_KEYS_DATA( DLGK_PAGE_NEXT, DLGK_MOUSE(KEY_NPAGE) ), \ + DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE ), \ + DLG_KEYS_DATA( DLGK_PAGE_PREV, DLGK_MOUSE(KEY_PPAGE) ) +/* + * Display a dialog box for editing a copy of a file + */ +int +dlg_editbox(const char *title, + char ***list, + int *rows, + int height, + int width) +{ + /* *INDENT-OFF* */ + static DLG_KEYS_BINDING binding[] = { + HELPKEY_BINDINGS, + ENTERKEY_BINDINGS, + NAVIGATE_BINDINGS, + END_KEYS_BINDING + }; + static DLG_KEYS_BINDING binding2[] = { + INPUTSTR_BINDINGS, + HELPKEY_BINDINGS, + ENTERKEY_BINDINGS, + NAVIGATE_BINDINGS, + END_KEYS_BINDING + }; + /* *INDENT-ON* */ + +#ifdef KEY_RESIZE + int old_height = height; + int old_width = width; +#endif + int x, y, box_y, box_x, box_height, box_width; + int show_buttons; + int thisrow, base_row, lastrow; + int goal_col = -1; + int col_offset = 0; + int chr_offset = 0; + int key, fkey, code; + int pagesize; + int listsize = size_list(*list); + int result = DLG_EXIT_UNKNOWN; + int state; + size_t max_len = (size_t) dlg_max_input(widest_line(*list)); + char *input, *buffer; + bool show_all, show_one, was_mouse; + WINDOW *dialog; + WINDOW *editing; + DIALOG_VARS save_vars; + const char **buttons = dlg_ok_labels(); + int mincols = (3 * COLS / 4); + + dlg_save_vars(&save_vars); + dialog_vars.separate_output = TRUE; + + dlg_does_output(); + + buffer = dlg_malloc(char, max_len + 1); + assert_ptr(buffer, "dlg_editbox"); + + thisrow = base_row = lastrow = 0; + +#ifdef KEY_RESIZE + retry: +#endif + show_buttons = TRUE; + state = dialog_vars.defaultno ? dlg_defaultno_button() : sTEXT; + key = fkey = 0; + + dlg_button_layout(buttons, &mincols); + dlg_auto_size(title, "", &height, &width, 3 * LINES / 4, mincols); + dlg_print_size(height, width); + dlg_ctl_size(height, width); + + x = dlg_box_x_ordinate(width); + y = dlg_box_y_ordinate(height); + + dialog = dlg_new_window(height, width, y, x); + dlg_register_window(dialog, "editbox", binding); + dlg_register_buttons(dialog, "editbox", buttons); + + dlg_mouse_setbase(x, y); + + dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + dlg_draw_bottom_box(dialog); + dlg_draw_title(dialog, title); + + wattrset(dialog, dialog_attr); + + /* Draw the editing field in a box */ + box_y = MARGIN + 0; + box_x = MARGIN + 1; + box_width = width - 2 - (2 * MARGIN); + box_height = height - (4 * MARGIN); + + dlg_draw_box(dialog, + box_y, + box_x, + box_height, + box_width, + border_attr, dialog_attr); + dlg_mouse_mkbigregion(box_y + MARGIN, + box_x + MARGIN, + box_height - (2 * MARGIN), + box_width - (2 * MARGIN), + KEY_MAX, 1, 1, 3); + editing = dlg_sub_window(dialog, + box_height - (2 * MARGIN), + box_width - (2 * MARGIN), + getbegy(dialog) + box_y + 1, + getbegx(dialog) + box_x + 1); + dlg_register_window(editing, "editbox", binding2); + + show_all = TRUE; + show_one = FALSE; + pagesize = getmaxy(editing); + + while (result == DLG_EXIT_UNKNOWN) { + int edit = 0; + + if (show_all) { + display_all(editing, *list, thisrow, base_row, listsize, chr_offset); + display_one(editing, THIS_ROW, + thisrow, thisrow, base_row, chr_offset); + show_all = FALSE; + show_one = TRUE; + } else { + if (thisrow != lastrow) { + display_one(editing, (*list)[lastrow], + lastrow, thisrow, base_row, 0); + show_one = TRUE; + } + } + if (show_one) { + display_one(editing, THIS_ROW, + thisrow, thisrow, base_row, chr_offset); + getyx(editing, y, x); + dlg_draw_scrollbar(dialog, + base_row, + base_row, + base_row + pagesize, + listsize, + box_x, + box_x + getmaxx(editing), + box_y + 0, + box_y + getmaxy(editing) + 1, + dialog_attr, + border_attr); + wmove(editing, y, x); + show_one = FALSE; + } + lastrow = thisrow; + input = THIS_ROW; + + /* + * The last field drawn determines where the cursor is shown: + */ + if (show_buttons) { + show_buttons = FALSE; + UPDATE_COL(input); + if (state != sTEXT) { + display_one(editing, input, thisrow, + -1, base_row, 0); + wrefresh(editing); + } + dlg_draw_buttons(dialog, + height - 2, + 0, + buttons, + (state != sTEXT) ? state : 99, + FALSE, + width); + if (state == sTEXT) { + display_one(editing, input, thisrow, + thisrow, base_row, chr_offset); + } + } + + key = dlg_mouse_wgetch((state == sTEXT) ? editing : dialog, &fkey); + if (key == ERR) { + result = DLG_EXIT_ERROR; + break; + } else if (key == ESC) { + result = DLG_EXIT_ESC; + break; + } + if (state != sTEXT) { + if (dlg_result_key(key, fkey, &result)) + break; + } + + was_mouse = (fkey && is_DLGK_MOUSE(key)); + if (was_mouse) + key -= M_EVENT; + + /* + * Handle mouse clicks first, since we want to know if this is a + * button, or something that dlg_edit_string() should handle. + */ + if (fkey + && was_mouse + && (code = dlg_ok_buttoncode(key)) >= 0) { + result = code; + continue; + } + + if (was_mouse + && (key >= KEY_MAX)) { + int wide = getmaxx(editing); + int cell = key - KEY_MAX; + thisrow = (cell / wide) + base_row; + col_offset = (cell % wide); + chr_offset = col_to_chr_offset(THIS_ROW, col_offset); + show_one = TRUE; + if (state != sTEXT) { + state = sTEXT; + show_buttons = TRUE; + } + continue; + } else if (was_mouse && key >= KEY_MIN) { + key = dlg_lookup_key(dialog, key, &fkey); + } + + if (state == sTEXT) { /* editing box selected */ + /* + * Intercept scrolling keys that dlg_edit_string() does not + * understand. + */ + if (fkey) { + bool moved = TRUE; + + switch (key) { + case DLGK_GRID_UP: + SCROLL_TO(thisrow - 1); + break; + case DLGK_GRID_DOWN: + SCROLL_TO(thisrow + 1); + break; + case DLGK_PAGE_FIRST: + SCROLL_TO(0); + break; + case DLGK_PAGE_LAST: + SCROLL_TO(listsize); + break; + case DLGK_PAGE_NEXT: + SCROLL_TO(base_row + pagesize); + break; + case DLGK_PAGE_PREV: + if (thisrow > base_row) { + SCROLL_TO(base_row); + } else { + SCROLL_TO(base_row - pagesize); + } + break; + case DLGK_DELETE_LEFT: + if (chr_offset == 0) { + if (thisrow == 0) { + beep(); + } else { + size_t len = (strlen(THIS_ROW) + + strlen(PREV_ROW) + 1); + char *tmp = dlg_malloc(char, len); + + assert_ptr(tmp, "dlg_editbox"); + + chr_offset = dlg_count_wchars(PREV_ROW); + UPDATE_COL(PREV_ROW); + goal_col = col_offset; + + sprintf(tmp, "%s%s", PREV_ROW, THIS_ROW); + if (len > max_len) + tmp[max_len] = '\0'; + + free(PREV_ROW); + PREV_ROW = tmp; + for (y = thisrow; y < listsize; ++y) { + (*list)[y] = (*list)[y + 1]; + } + --listsize; + --thisrow; + SCROLL_TO(thisrow); + + show_all = TRUE; + } + } else { + /* dlg_edit_string() can handle this case */ + moved = FALSE; + } + break; + default: + moved = FALSE; + break; + } + if (moved) { + if (thisrow != lastrow) { + if (goal_col < 0) + goal_col = col_offset; + chr_offset = col_to_chr_offset(THIS_ROW, goal_col); + } else { + UPDATE_COL(THIS_ROW); + } + continue; + } + } + strncpy(buffer, input, max_len - 1)[max_len - 1] = '\0'; + edit = dlg_edit_string(buffer, &chr_offset, key, fkey, FALSE); + + if (edit) { + goal_col = UPDATE_COL(input); + if (strcmp(input, buffer)) { + free(input); + THIS_ROW = dlg_strclone(buffer); + input = THIS_ROW; + } + display_one(editing, input, thisrow, + thisrow, base_row, chr_offset); + continue; + } + } + + /* handle non-functionkeys */ + if (!fkey && (code = dlg_char_to_button(key, buttons)) >= 0) { + dlg_del_window(dialog); + result = dlg_ok_buttoncode(code); + continue; + } + + /* handle functionkeys */ + if (fkey) { + switch (key) { + case DLGK_FIELD_PREV: + show_buttons = TRUE; + state = dlg_prev_ok_buttonindex(state, sTEXT); + break; + case DLGK_FIELD_NEXT: + show_buttons = TRUE; + state = dlg_next_ok_buttonindex(state, sTEXT); + break; + case DLGK_ENTER: + if (state == sTEXT) { + const int *indx = dlg_index_wchars(THIS_ROW); + int split = indx[chr_offset]; + char *tmp = dlg_strclone(THIS_ROW + split); + + assert_ptr(tmp, "dlg_editbox"); + grow_list(list, rows, listsize + 1); + ++listsize; + for (y = listsize; y > thisrow; --y) { + (*list)[y] = (*list)[y - 1]; + } + THIS_ROW[split] = '\0'; + ++thisrow; + chr_offset = 0; + col_offset = 0; + THIS_ROW = tmp; + SCROLL_TO(thisrow); + show_all = TRUE; + } else { + result = dlg_ok_buttoncode(state); + } + break; +#ifdef KEY_RESIZE + case KEY_RESIZE: + /* reset data */ + height = old_height; + width = old_width; + /* repaint */ + dlg_clear(); + dlg_del_window(editing); + dlg_del_window(dialog); + refresh(); + dlg_mouse_free_regions(); + goto retry; +#endif + default: + beep(); + break; + } + } else { + if ((key == ' ') && (state != sTEXT)) { + result = dlg_ok_buttoncode(state); + } else { + beep(); + } + } + } + + dlg_unregister_window(editing); + dlg_del_window(editing); + dlg_del_window(dialog); + dlg_mouse_free_regions(); + + /* + * The caller's copy of the (*list)[] array has been updated, but for + * consistency with the other widgets, we put the "real" result in + * the output buffer. + */ + if (result == DLG_EXIT_OK) { + int n; + for (n = 0; n < listsize; ++n) { + dlg_add_result((*list)[n]); + dlg_add_separator(); + } + } + free(buffer); + dlg_restore_vars(&save_vars); + return result; +} + +int +dialog_editbox(const char *title, const char *file, int height, int width) +{ + int result; + char **list; + int rows; + + load_list(file, &list, &rows); + result = dlg_editbox(title, &list, &rows, height, width); + free_list(&list, &rows); + return result; +} |