diff options
-rw-r--r-- | source3/utils/regedit_dialog.c | 2468 | ||||
-rw-r--r-- | source3/utils/regedit_dialog.h | 186 |
2 files changed, 1800 insertions, 854 deletions
diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c index ce67065d8b..119219d4a3 100644 --- a/source3/utils/regedit_dialog.c +++ b/source3/utils/regedit_dialog.c @@ -55,31 +55,19 @@ static char *string_trim(TALLOC_CTX *ctx, const char *buf) static int dialog_free(struct dialog *dia) { - if (dia->window) { - delwin(dia->window); - } - if (dia->sub_window) { - delwin(dia->sub_window); - } - if (dia->panel) { - del_panel(dia->panel); - } - if (dia->choices) { - unpost_menu(dia->choices); - free_menu(dia->choices); - } - if (dia->choice_items) { - ITEM **it; - for (it = dia->choice_items; *it != NULL; ++it) { - free_item(*it); - } - } + dialog_destroy(dia); return 0; } -struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines, - int ncols, int y, int x) +static bool default_validator(struct dialog *dia, struct dialog_section *sect, + void *arg) +{ + return true; +} + +struct dialog *dialog_new(TALLOC_CTX *ctx, short color, const char *title, + int y, int x) { struct dialog *dia; @@ -90,24 +78,14 @@ struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines, talloc_set_destructor(dia, dialog_free); - dia->window = newwin(nlines, ncols, y, x); - if (dia->window == NULL) { - goto fail; - } - - box(dia->window, 0, 0); - mvwaddstr(dia->window, 0, 1, title); - - /* body of the dialog within the box outline */ - dia->sub_window = derwin(dia->window, nlines - 2, ncols - 2, 1, 1); - if (dia->sub_window == NULL) { - goto fail; - } - - dia->panel = new_panel(dia->window); - if (dia->panel == NULL) { + dia->title = talloc_strdup(dia, title); + if (dia->title == NULL) { goto fail; } + dia->x = x; + dia->y = y; + dia->color = color; + dia->submit = default_validator; return dia; @@ -118,8 +96,13 @@ fail: } -static void center_dialog_above_window(int *nlines, int *ncols, - int *y, int *x) +void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg) +{ + dia->submit = cb; + dia->submit_arg = arg; +} + +static void center_above_window(int *nlines, int *ncols, int *y, int *x) { int centery, centerx; @@ -143,19 +126,333 @@ static void center_dialog_above_window(int *nlines, int *ncols, } } +void dialog_section_destroy(struct dialog_section *section) +{ + if (section->ops->destroy) { + section->ops->destroy(section); + } + if (section->window) { + delwin(section->window); + section->window = NULL; + } +} + +void dialog_section_init(struct dialog_section *section, + const struct dialog_section_ops *ops, + int nlines, int ncols) +{ + section->ops = ops; + section->nlines = nlines; + section->ncols = ncols; +} + +const char *dialog_section_get_name(struct dialog_section *section) +{ + return section->name; +} + +void dialog_section_set_name(struct dialog_section *section, const char *name) +{ + TALLOC_FREE(section->name); + section->name = talloc_strdup(section, name); +} + +void dialog_section_set_justify(struct dialog_section *section, + enum section_justify justify) +{ + section->justify = justify; +} + +/* append a section to the dialog's circular list */ +void dialog_append_section(struct dialog *dia, + struct dialog_section *section) +{ + SMB_ASSERT(section != NULL); + + if (!dia->head_section) { + dia->head_section = section; + } + if (dia->tail_section) { + dia->tail_section->next = section; + } + section->prev = dia->tail_section; + section->next = dia->head_section; + dia->head_section->prev = section; + dia->tail_section = section; +} + +struct dialog_section *dialog_find_section(struct dialog *dia, const char *name) +{ + struct dialog_section *section = dia->head_section; + + do { + if (section->name && strequal(section->name, name)) { + return section; + } + section = section->next; + } while (section != dia->head_section); + + return NULL; +} + +static void section_on_input(struct dialog *dia, int c) +{ + struct dialog_section *section = dia->current_section; + + if (!section->ops->on_input) { + return; + } + section->ops->on_input(dia, section, c); +} + +static bool section_on_tab(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_tab) { + return false; + } + return section->ops->on_tab(dia, section); +} + +static bool section_on_btab(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_btab) { + return false; + } + return section->ops->on_btab(dia, section); +} + +static bool section_on_up(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_up) { + return false; + } + return section->ops->on_up(dia, section); +} + +static bool section_on_down(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_down) { + return false; + } + return section->ops->on_down(dia, section); +} + +static bool section_on_left(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_left) { + return false; + } + return section->ops->on_left(dia, section); +} + +static bool section_on_right(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_right) { + return false; + } + return section->ops->on_right(dia, section); +} + +static enum dialog_action section_on_enter(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_enter) { + return DIALOG_OK; + } + return section->ops->on_enter(dia, section); +} + +static bool section_on_focus(struct dialog *dia, bool forward) +{ + struct dialog_section *section = dia->current_section; + + if (!section->ops->on_focus) { + return false; + } + return section->ops->on_focus(dia, section, forward); +} + +static void section_on_leave_focus(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (section->ops->on_leave_focus) { + section->ops->on_leave_focus(dia, section); + } +} + +static void section_set_next_focus(struct dialog *dia) +{ + section_on_leave_focus(dia); + + do { + dia->current_section = dia->current_section->next; + } while (!section_on_focus(dia, true)); +} + +static void section_set_previous_focus(struct dialog *dia) +{ + section_on_leave_focus(dia); + + do { + dia->current_section = dia->current_section->prev; + } while (!section_on_focus(dia, false)); +} + +WERROR dialog_create(struct dialog *dia) +{ + WERROR rv = WERR_OK; + int row, col; + int nlines, ncols; + struct dialog_section *section; + + nlines = 0; + ncols = 0; + SMB_ASSERT(dia->head_section != NULL); + + /* calculate total size based on sections */ + section = dia->head_section; + do { + nlines += section->nlines; + ncols = MAX(ncols, section->ncols); + section = section->next; + } while (section != dia->head_section); + + /* fill in widths for sections that expand */ + section = dia->head_section; + do { + if (section->ncols < 0) { + section->ncols = ncols; + } + section = section->next; + } while (section != dia->head_section); + + /* create window for dialog */ + nlines += 4; + ncols += 6; + dia->centered = false; + if (dia->y < 0 || dia->x < 0) { + dia->centered = true; + center_above_window(&nlines, &ncols, &dia->y, &dia->x); + } + dia->window = newwin(nlines, ncols, dia->y, dia->x); + if (dia->window == NULL) { + rv = WERR_NOMEM; + goto fail; + } + dia->panel = new_panel(dia->window); + if (dia->panel == NULL) { + rv = WERR_NOMEM; + goto fail; + } + + /* setup color and border */ + wbkgdset(dia->window, ' ' | COLOR_PAIR(dia->color)); + wclear(dia->window); + mvwhline(dia->window, 1, 2, 0, ncols - 4); + mvwhline(dia->window, nlines - 2, 2, 0, ncols - 4); + mvwvline(dia->window, 2, 1, 0, nlines - 4); + mvwvline(dia->window, 2, ncols - 2, 0, nlines - 4); + mvwaddch(dia->window, 1, 1, ACS_ULCORNER); + mvwaddch(dia->window, 1, ncols - 2, ACS_URCORNER); + mvwaddch(dia->window, nlines - 2, 1, ACS_LLCORNER); + mvwaddch(dia->window, nlines - 2, ncols - 2, ACS_LRCORNER); + col = ncols / 2 - MIN(strlen(dia->title) + 2, ncols) / 2; + mvwprintw(dia->window, 1, col, " %s ", dia->title); + + /* create subwindows for each section */ + row = 2; + section = dia->head_section; + do { + col = 3; + + switch (section->justify) { + case SECTION_JUSTIFY_LEFT: + break; + case SECTION_JUSTIFY_CENTER: + col += (ncols - 6)/ 2 - section->ncols / 2; + break; + case SECTION_JUSTIFY_RIGHT: + break; + } + + section->window = derwin(dia->window, section->nlines, + section->ncols, row, col); + if (section->window == NULL) { + rv = WERR_NOMEM; + goto fail; + } + SMB_ASSERT(section->ops->create != NULL); + rv = section->ops->create(dia, section); + row += section->nlines; + section = section->next; + } while (section != dia->head_section && W_ERROR_IS_OK(rv)); + + dia->current_section = dia->head_section; + section_set_next_focus(dia); + +fail: + return rv; +} + +void dialog_show(struct dialog *dia) +{ + struct dialog_section *section; + + touchwin(dia->window); + wnoutrefresh(dia->window); + section = dia->head_section; + do { + wnoutrefresh(section->window); + section = section->next; + } while (section != dia->head_section); +} + +void dialog_destroy(struct dialog *dia) +{ + struct dialog_section *section; + + section = dia->head_section; + do { + dialog_section_destroy(section); + section = section->next; + } while (section != dia->head_section); + + if (dia->panel) { + del_panel(dia->panel); + dia->panel = NULL; + } + if (dia->window) { + delwin(dia->window); + dia->window = NULL; + } +} + static int dialog_getch(struct dialog *dia) { int c; c = regedit_getch(); - if (c == KEY_RESIZE) { int nlines, ncols, y, x; getmaxyx(dia->window, nlines, ncols); getbegyx(dia->window, y, x); if (dia->centered) { - center_dialog_above_window(&nlines, &ncols, &y, &x); + center_above_window(&nlines, &ncols, &y, &x); } else { if (nlines + y > LINES) { if (nlines > LINES) { @@ -173,1052 +470,1555 @@ static int dialog_getch(struct dialog *dia) } } move_panel(dia->panel, y, x); - doupdate(); } return c; } -struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title, - int nlines, int ncols) +bool dialog_handle_input(struct dialog *dia, WERROR *err, + enum dialog_action *action) { - struct dialog *dia; - int y, x; + int c; - center_dialog_above_window(&nlines, &ncols, &y, &x); + *err = WERR_OK; - dia = dialog_new(ctx, title, nlines, ncols, y, x); - if (dia) { - dia->centered = true; + c = dialog_getch(dia); + + switch (c) { + case '\t': + if (!section_on_tab(dia)) { + section_set_next_focus(dia); + } + break; + case KEY_BTAB: + if (!section_on_btab(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_UP: + if (!section_on_up(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_DOWN: + if (!section_on_down(dia)) { + section_set_next_focus(dia); + } + break; + case KEY_LEFT: + if (!section_on_left(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_RIGHT: + if (!section_on_right(dia)) { + section_set_next_focus(dia); + } + break; + case '\n': + case KEY_ENTER: + *action = section_on_enter(dia); + switch (*action) { + case DIALOG_IGNORE: + break; + case DIALOG_CANCEL: + return false; + case DIALOG_OK: + return !dia->submit(dia, dia->current_section, + dia->submit_arg); + } + break; + case 27: /* ESC */ + return false; + default: + section_on_input(dia, c); + break; } - return dia; + return true; } -struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, - int ncols, int y, int x) +void dialog_modal_loop(struct dialog *dia, WERROR *err, + enum dialog_action *action) { - size_t nchoices, i; - struct dialog *dia; + do { + update_panels(); + doupdate(); + } while (dialog_handle_input(dia, err, action)); +} - dia = dialog_new(ctx, title, nlines, ncols, y, x); - if (dia == NULL) { - return NULL; - } +/* text label */ +struct dialog_section_label { + struct dialog_section section; + char **text; +}; - dia->menu_window = derwin(dia->sub_window, 1, ncols - 3, - nlines - 3, 0); - if (dia->menu_window == NULL) { - goto fail; +static WERROR label_create(struct dialog *dia, struct dialog_section *section) +{ + int row; + struct dialog_section_label *label = + talloc_get_type_abort(section, struct dialog_section_label); + + for (row = 0; row < section->nlines; ++row) { + mvwaddstr(section->window, row, 0, label->text[row]); } - for (nchoices = 0; choices[nchoices] != NULL; ++nchoices) - ; - dia->choice_items = talloc_zero_array(dia, ITEM *, nchoices + 1); - if (dia->choice_items == NULL) { + return WERR_OK; +} + +struct dialog_section_ops label_ops = { + .create = label_create, +}; + +static int label_free(struct dialog_section_label *label) +{ + dialog_section_destroy(&label->section); + return 0; +} + +struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx, + const char *msg, va_list ap) +{ + struct dialog_section_label *label; + char *tmp, *ptmp, *line, *saveptr; + int nlines, ncols; + + label = talloc_zero(ctx, struct dialog_section_label); + if (label == NULL) { + return NULL; + } + talloc_set_destructor(label, label_free); + tmp = talloc_vasprintf(label, msg, ap); + if (tmp == NULL) { goto fail; } - for (i = 0; i < nchoices; ++i) { - char *desc = talloc_strdup(dia, choices[i]); - if (desc == NULL) { + + for (nlines = 0, ncols = 0, ptmp = tmp; + (line = strtok_r(ptmp, "\n", &saveptr)) != NULL; + ++nlines) { + ptmp = NULL; + label->text = talloc_realloc(label, label->text, + char *, nlines + 1); + if (label->text == NULL) { goto fail; } - dia->choice_items[i] = new_item(desc, desc); - if (dia->choice_items[i] == NULL) { + ncols = MAX(ncols, strlen(line)); + label->text[nlines] = talloc_strdup(label->text, line); + if (label->text[nlines] == NULL) { goto fail; } - /* store choice index */ - set_item_userptr(dia->choice_items[i], (void*)(uintptr_t)i); } + talloc_free(tmp); + dialog_section_init(&label->section, &label_ops, nlines, ncols); - dia->choices = new_menu(dia->choice_items); - if (dia->choices == NULL) { - goto fail; - } + return &label->section; - set_menu_format(dia->choices, 1, ncols); - set_menu_win(dia->choices, dia->sub_window); - set_menu_sub(dia->choices, dia->menu_window); - menu_opts_off(dia->choices, O_SHOWDESC); - set_menu_mark(dia->choices, "* "); - post_menu(dia->choices); - wmove(dia->sub_window, 0, 0); +fail: + talloc_free(label); + return NULL; +} - return dia; +struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx, + const char *msg, ...) +{ + va_list ap; + struct dialog_section *rv; -fail: - talloc_free(dia); + va_start(ap, msg); + rv = dialog_section_label_new_va(ctx, msg, ap); + va_end(ap); - return NULL; + return rv; } -struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, - int ncols) +/* horizontal separator */ +struct dialog_section_hsep { + struct dialog_section section; + int sep; +}; + +static WERROR hsep_create(struct dialog *dia, struct dialog_section *section) { int y, x; - struct dialog *dia; - center_dialog_above_window(&nlines, &ncols, &y, &x); + struct dialog_section_hsep *hsep = + talloc_get_type_abort(section, struct dialog_section_hsep); - dia = dialog_choice_new(ctx, title, choices, nlines, ncols, y, x); - if (dia) { - dia->centered = true; + whline(section->window, hsep->sep, section->ncols); + + if (hsep->sep == 0 || hsep->sep == ACS_HLINE) { + /* change the border characters around this section to + tee chars */ + getparyx(section->window, y, x); + mvwaddch(dia->window, y, x - 1, ACS_HLINE); + mvwaddch(dia->window, y, x - 2, ACS_LTEE); + mvwaddch(dia->window, y, x + section->ncols, ACS_HLINE); + mvwaddch(dia->window, y, x + section->ncols + 1, ACS_RTEE); } - return dia; + return WERR_OK; } -static bool current_item_is_first(MENU *menu) -{ - const ITEM *it = current_item(menu); +struct dialog_section_ops hsep_ops = { + .create = hsep_create +}; - return item_index(it) == 0; +static int hsep_free(struct dialog_section_hsep *hsep) +{ + dialog_section_destroy(&hsep->section); + return 0; } -static bool current_item_is_last(MENU *menu) +struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep) { - const ITEM *it = current_item(menu); + struct dialog_section_hsep *hsep; + + hsep = talloc_zero(ctx, struct dialog_section_hsep); + if (hsep) { + talloc_set_destructor(hsep, hsep_free); + dialog_section_init(&hsep->section, &hsep_ops, 1, -1); + hsep->sep = sep; + } - return item_index(it) == item_count(menu) - 1; + return &hsep->section; } -static int handle_menu_input(MENU *menu, int c) +/* text input field */ +struct dialog_section_text_field { + struct dialog_section section; + unsigned opts; + FIELD *field[2]; + FORM *form; +}; + +static WERROR text_field_create(struct dialog *dia, + struct dialog_section *section) { - ITEM *item; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); - switch (c) { - case KEY_BTAB: - if (current_item_is_first(menu)) { - menu_driver(menu, REQ_LAST_ITEM); - } else { - menu_driver(menu, REQ_LEFT_ITEM); - } - break; - case KEY_LEFT: - menu_driver(menu, REQ_LEFT_ITEM); - break; - case '\t': - if (current_item_is_last(menu)) { - menu_driver(menu, REQ_FIRST_ITEM); - break; - } else { - menu_driver(menu, REQ_RIGHT_ITEM); - } - case KEY_RIGHT: - menu_driver(menu, REQ_RIGHT_ITEM); - break; - case KEY_ENTER: - case '\n': - item = current_item(menu); - return (int)(uintptr_t)item_userptr(item); + text_field->field[0] = new_field(section->nlines, section->ncols, + 0, 0, 0, 0); + if (text_field->field[0] == NULL) { + return WERR_NOMEM; } + set_field_back(text_field->field[0], A_REVERSE); + set_field_opts(text_field->field[0], text_field->opts); - return -1; + text_field->form = new_form(text_field->field); + if (text_field->form == NULL) { + return WERR_NOMEM; + } + + set_form_win(text_field->form, dia->window); + set_form_sub(text_field->form, section->window); + set_current_field(text_field->form, text_field->field[0]); + post_form(text_field->form); + + return WERR_OK; } -static void handle_form_input(FORM *frm, int c) +static void text_field_destroy(struct dialog_section *section) { + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (text_field->form) { + unpost_form(text_field->form); + free_form(text_field->form); + text_field->form = NULL; + } + if (text_field->field[0]) { + free_field(text_field->field[0]); + text_field->field[0] = NULL; + } +} + +static void text_field_on_input(struct dialog *dia, + struct dialog_section *section, + int c) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + switch (c) { - case '\n': - form_driver(frm, REQ_NEW_LINE); - break; - case KEY_UP: - form_driver(frm, REQ_UP_CHAR); - break; - case KEY_DOWN: - form_driver(frm, REQ_DOWN_CHAR); - break; - case '\b': case KEY_BACKSPACE: - form_driver(frm, REQ_DEL_PREV); - break; - case KEY_LEFT: - form_driver(frm, REQ_LEFT_CHAR); - break; - case KEY_RIGHT: - form_driver(frm, REQ_RIGHT_CHAR); + form_driver(text_field->form, REQ_DEL_PREV); break; default: - form_driver(frm, c); + form_driver(text_field->form, c); break; } } -static int modal_loop(struct dialog *dia) +static bool text_field_on_up(struct dialog *dia, + struct dialog_section *section) { - int c; - int selection = -1; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); - update_panels(); - doupdate(); + if (section->nlines > 1) { + form_driver(text_field->form, REQ_UP_CHAR); + return true; + } + return false; +} - while (selection == -1) { - c = dialog_getch(dia); - selection = handle_menu_input(dia->choices, c); - update_panels(); - doupdate(); +static bool text_field_on_down(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_DOWN_CHAR); + return true; } + return false; +} - talloc_free(dia); +static bool text_field_on_left(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); - return selection; + form_driver(text_field->form, REQ_LEFT_CHAR); + + return true; } -static struct dialog *dialog_msg_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, - const char *msg, va_list ap) +static bool text_field_on_right(struct dialog *dia, + struct dialog_section *section) { - struct dialog *dia; - char *str; - int width; -#define MIN_WIDTH 20 + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); - str = talloc_vasprintf(ctx, msg, ap); - if (str == NULL) { - return NULL; - } + form_driver(text_field->form, REQ_RIGHT_CHAR); - width = strlen(str) + 2; - if (width < MIN_WIDTH) { - width = MIN_WIDTH; + return true; +} + +static enum dialog_action text_field_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_NEW_LINE); + return DIALOG_IGNORE; } - dia = dialog_choice_center_new(ctx, title, choices, nlines, width); - if (dia == NULL) { + + return DIALOG_OK; +} + +static bool text_field_on_focus(struct dialog *dia, + struct dialog_section *section, bool forward) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + pos_form_cursor(text_field->form); + + return true; +} + +struct dialog_section_ops text_field_ops = { + .create = text_field_create, + .destroy = text_field_destroy, + .on_input = text_field_on_input, + .on_up = text_field_on_up, + .on_down = text_field_on_down, + .on_left = text_field_on_left, + .on_right = text_field_on_right, + .on_enter = text_field_on_enter, + .on_focus = text_field_on_focus +}; + +static int text_field_free(struct dialog_section_text_field *text_field) +{ + dialog_section_destroy(&text_field->section); + return 0; +} + +struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx, + int height, int width) +{ + struct dialog_section_text_field *text_field; + + text_field = talloc_zero(ctx, struct dialog_section_text_field); + if (text_field == NULL) { return NULL; } + talloc_set_destructor(text_field, text_field_free); + dialog_section_init(&text_field->section, &text_field_ops, + height, width); + text_field->opts = O_ACTIVE | O_PUBLIC | O_EDIT | O_VISIBLE | O_NULLOK; - waddstr(dia->sub_window, str); - talloc_free(str); + return &text_field->section; +} - return dia; +const char *dialog_section_text_field_get(TALLOC_CTX *ctx, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + return string_trim(ctx, field_buffer(text_field->field[0], 0)); } -int dialog_input(TALLOC_CTX *ctx, char **output, const char *title, - const char *msg, ...) +void dialog_section_text_field_set(struct dialog_section *section, + const char *s) { - va_list ap; - struct dialog *dia; - const char *choices[] = { - "Ok", - "Cancel", - NULL - }; - FIELD *field[2] = {0}; - FORM *input; - WINDOW *input_win; - int y, x; - int rv = -1; - bool input_section = true; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); - va_start(ap, msg); - dia = dialog_msg_new(ctx, title, choices, 7, msg, ap); - va_end(ap); - if (dia == NULL) { - return -1; - } + set_field_buffer(text_field->field[0], 0, s); +} + +const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx, + struct dialog_section *section) +{ + int rows, cols, max; + const char **arr; + size_t i; + const char *buf; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); - getmaxyx(dia->sub_window, y, x); - input_win = derwin(dia->sub_window, 1, x - 2, 2, 1); - if (input_win == NULL) { - goto finish; + form_driver(text_field->form, REQ_VALIDATION); + buf = field_buffer(text_field->field[0], 0); + + dynamic_field_info(text_field->field[0], &rows, &cols, &max); + + arr = talloc_zero_array(ctx, const char *, rows + 1); + if (arr == NULL) { + return NULL; } - field[0] = new_field(1, x - 2, 0, 0, 0, 0); - if (field[0] == NULL) { - goto finish; + for (i = 0; *buf; ++i, buf += cols) { + SMB_ASSERT(i < rows); + arr[i] = string_trim_n(arr, buf, cols); } - field_opts_off(field[0], O_BLANK | O_AUTOSKIP | O_STATIC); - set_field_back(field[0], A_REVERSE); - - input = new_form(field); - form_opts_off(input, O_NL_OVERLOAD | O_BS_OVERLOAD); - set_form_win(input, dia->sub_window); - set_form_sub(input, input_win); - set_current_field(input, field[0]); - post_form(input); - *output = NULL; + return arr; +} - update_panels(); - doupdate(); - - while (rv == -1) { - int c = dialog_getch(dia); - - if (c == '\t' || c == KEY_BTAB) { - if (input_section) { - int valid = form_driver(input, REQ_VALIDATION); - if (valid == E_OK) { - input_section = false; - if (c == '\t') { - menu_driver(dia->choices, - REQ_FIRST_ITEM); - } else { - menu_driver(dia->choices, - REQ_LAST_ITEM); - } - pos_menu_cursor(dia->choices); - } - } else { - if ((c == '\t' && - current_item_is_last(dia->choices)) || - (c == KEY_BTAB && - current_item_is_first(dia->choices))) { - input_section = true; - set_current_field(input, field[0]); - pos_form_cursor(input); - } else { - handle_menu_input(dia->choices, c); - } - } - } else if (input_section) { - handle_form_input(input, c); - } else { - rv = handle_menu_input(dia->choices, c); - if (rv == DIALOG_OK) { - const char *buf = field_buffer(field[0], 0); - *output = string_trim(ctx, buf); - } +WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx, + struct dialog_section *section, + const char **array) +{ + int rows, cols, max; + size_t padding, length, index; + const char **arrayp; + char *buf = NULL; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + dynamic_field_info(text_field->field[0], &rows, &cols, &max); + /* try to fit each string on it's own line. each line + needs to be padded with whitespace manually, since + ncurses fields do not have newlines. */ + for (index = 0, arrayp = array; *arrayp != NULL; ++arrayp) { + length = MIN(strlen(*arrayp), cols); + padding = cols - length; + buf = talloc_realloc(ctx, buf, char, + talloc_array_length(buf) + + length + padding + 1); + if (buf == NULL) { + return WERR_NOMEM; } - update_panels(); - doupdate(); + memcpy(&buf[index], *arrayp, length); + index += length; + memset(&buf[index], ' ', padding); + index += padding; + buf[index] = '\0'; } -finish: - if (input) { - unpost_form(input); - free_form(input); + set_field_buffer(text_field->field[0], 0, buf); + talloc_free(buf); + + return WERR_OK; +} + +bool dialog_section_text_field_get_int(struct dialog_section *section, + long long *out) +{ + bool rv; + const char *buf; + char *endp; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + buf = string_trim(section, field_buffer(text_field->field[0], 0)); + if (buf == NULL) { + return false; + } + *out = strtoll(buf, &endp, 0); + rv = true; + if (endp == buf || endp == NULL || endp[0] != '\0') { + rv = false; } - if (field[0]) { - free_field(field[0]); + + return rv; +} + + +bool dialog_section_text_field_get_uint(struct dialog_section *section, + unsigned long long *out) +{ + bool rv; + const char *buf; + char *endp; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + buf = string_trim(section, field_buffer(text_field->field[0], 0)); + if (buf == NULL) { + return false; } - if (input_win) { - delwin(input_win); + *out = strtoull(buf, &endp, 0); + rv = true; + if (endp == buf || endp == NULL || endp[0] != '\0') { + rv = false; } - talloc_free(dia); return rv; } -int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, - const char *title, const char *msg, ...) +/* hex editor field */ +struct dialog_section_hexedit { + struct dialog_section section; + struct hexedit *buf; +}; + +#define HEXEDIT_MIN_SIZE 16 +static WERROR hexedit_create(struct dialog *dia, + struct dialog_section *section) { - va_list ap; - struct dialog *dia; - const char *choices[] = { - "Ok", - "Cancel", - NULL - }; + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); - if (type == DIA_ALERT) { - choices[1] = NULL; + hexedit->buf = hexedit_new(dia, section->window, section->nlines, + 0, 0, NULL, HEXEDIT_MIN_SIZE); + if (hexedit->buf == NULL) { + return WERR_NOMEM; } - va_start(ap, msg); - dia = dialog_msg_new(ctx, title, choices, 5, msg, ap); - va_end(ap); - if (dia == NULL) { - return -1; + hexedit_refresh(hexedit->buf); + + return WERR_OK; +} + +static void hexedit_destroy(struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + if (hexedit->buf) { + TALLOC_FREE(hexedit->buf); + } +} + +static void hexedit_on_input(struct dialog *dia, + struct dialog_section *section, + int c) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + switch (c) { + case KEY_BACKSPACE: + // FIXME hexedit_driver(hexedit->buf, c); + break; + default: + hexedit_driver(hexedit->buf, c); + break; } +} - return modal_loop(dia); +static bool hexedit_on_up(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_UP); + + return true; } -#define EDIT_WIDTH 50 -#define EDIT_INTERNAL_WIDTH (EDIT_WIDTH - 2) -#define EDIT_INPUT_WIDTH (EDIT_INTERNAL_WIDTH - 2) +static bool hexedit_on_down(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); -#define EDIT_NAME_LABEL_Y 0 -#define EDIT_NAME_LABEL_X 0 -#define EDIT_NAME_LABEL_WIDTH EDIT_INTERNAL_WIDTH -#define EDIT_NAME_LABEL_HEIGHT 1 -#define EDIT_NAME_INPUT_Y 1 -#define EDIT_NAME_INPUT_X 1 -#define EDIT_NAME_INPUT_WIDTH EDIT_INPUT_WIDTH -#define EDIT_NAME_INPUT_HEIGHT 1 + hexedit_driver(hexedit->buf, HE_CURSOR_DOWN); -#define EDIT_DATA_LABEL_Y 3 -#define EDIT_DATA_LABEL_X 0 -#define EDIT_DATA_LABEL_WIDTH EDIT_INTERNAL_WIDTH -#define EDIT_DATA_LABEL_HEIGHT 1 -#define EDIT_DATA_INPUT_Y 4 -#define EDIT_DATA_INPUT_X 1 -#define EDIT_DATA_INPUT_WIDTH EDIT_INPUT_WIDTH -#define EDIT_DATA_HEIGHT_ONELINE 1 -#define EDIT_DATA_HEIGHT_MULTILINE 5 -#define EDIT_DATA_HEIGHT_BUF 10 + return true; +} -#define EDIT_FORM_WIN_Y 0 -#define EDIT_FORM_WIN_X 0 -#define EDIT_FORM_WIN_HEIGHT_ONELINE \ - (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \ - EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_ONELINE) +static bool hexedit_on_left(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); -#define EDIT_FORM_WIN_HEIGHT_MULTILINE \ - (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \ - EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_MULTILINE) + hexedit_driver(hexedit->buf, HE_CURSOR_LEFT); -#define EDIT_FORM_WIN_HEIGHT_BUF \ - (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \ - EDIT_DATA_LABEL_HEIGHT) + return true; +} -#define EDIT_FORM_WIN_WIDTH EDIT_INTERNAL_WIDTH +static bool hexedit_on_right(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); -#define EDIT_PAD 5 -#define EDIT_HEIGHT_ONELINE (EDIT_FORM_WIN_HEIGHT_ONELINE + EDIT_PAD) + hexedit_driver(hexedit->buf, HE_CURSOR_RIGHT); -#define EDIT_HEIGHT_MULTILINE (EDIT_FORM_WIN_HEIGHT_MULTILINE + EDIT_PAD) + return true; +} -#define EDIT_HEIGHT_BUF \ - (EDIT_FORM_WIN_HEIGHT_BUF + EDIT_DATA_HEIGHT_BUF + EDIT_PAD) +static enum dialog_action hexedit_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + return DIALOG_IGNORE; +} -#define MAX_FIELDS 5 -#define FLD_NAME 1 -#define FLD_DATA 3 +static bool hexedit_on_focus(struct dialog *dia, + struct dialog_section *section, bool forward) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); -#define DIALOG_RESIZE 2 + hexedit_set_cursor(hexedit->buf); -enum input_section { - IN_NAME, - IN_DATA, - IN_MENU -}; + return true; +} -struct edit_dialog { - struct dialog *dia; - WINDOW *input_win; - FORM *input; - FIELD *field[MAX_FIELDS]; - struct hexedit *buf; - enum input_section section; - bool closing; - uint32_t mode; +struct dialog_section_ops hexedit_ops = { + .create = hexedit_create, + .destroy = hexedit_destroy, + .on_input = hexedit_on_input, + .on_up = hexedit_on_up, + .on_down = hexedit_on_down, + .on_left = hexedit_on_left, + .on_right = hexedit_on_right, + .on_enter = hexedit_on_enter, + .on_focus = hexedit_on_focus }; -static int edit_dialog_free(struct edit_dialog *edit) +static int hexedit_free(struct dialog_section_hexedit *hexedit) { - FIELD **f; + dialog_section_destroy(&hexedit->section); + return 0; +} - if (edit->input) { - unpost_form(edit->input); - free_form(edit->input); - } - for (f = edit->field; *f; ++f) { - free_field(*f); +struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height) +{ + struct dialog_section_hexedit *hexedit; + + hexedit = talloc_zero(ctx, struct dialog_section_hexedit); + if (hexedit == NULL) { + return NULL; } - delwin(edit->input_win); + talloc_set_destructor(hexedit, hexedit_free); + dialog_section_init(&hexedit->section, &hexedit_ops, + height, LINE_WIDTH); - return 0; + return &hexedit->section; } -static WERROR fill_value_buffer(struct edit_dialog *edit, - const struct value_item *vitem) +WERROR dialog_section_hexedit_set_buf(struct dialog_section *section, + const void *data, size_t size) { - char *tmp; + WERROR rv; + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); - switch (vitem->type) { - case REG_DWORD: { - uint32_t v = 0; - if (vitem->data.length >= 4) { - v = IVAL(vitem->data.data, 0); - } - tmp = talloc_asprintf(edit, "0x%x", v); - if (tmp == NULL) { - return WERR_NOMEM; - } - set_field_buffer(edit->field[FLD_DATA], 0, tmp); - talloc_free(tmp); - break; - } - case REG_SZ: - case REG_EXPAND_SZ: { - const char *s; + SMB_ASSERT(hexedit->buf != NULL); - if (!pull_reg_sz(edit, &vitem->data, &s)) { - return WERR_NOMEM; - } - set_field_buffer(edit->field[FLD_DATA], 0, s); - break; + rv = hexedit_set_buf(hexedit->buf, data, size); + if (W_ERROR_IS_OK(rv)) { + hexedit_refresh(hexedit->buf); + hexedit_set_cursor(hexedit->buf); } - case REG_MULTI_SZ: { - int rows, cols, max; - size_t padding, length, index; - const char **array, **arrayp; - char *buf = NULL; - dynamic_field_info(edit->field[FLD_DATA], &rows, &cols, &max); - if (!pull_reg_multi_sz(edit, &vitem->data, &array)) { - return WERR_NOMEM; - } + return rv; +} - /* try to fit each string on it's own line. each line - needs to be padded with whitespace manually, since - ncurses fields do not have newlines. */ - for (index = 0, arrayp = array; *arrayp != NULL; ++arrayp) { - length = MIN(strlen(*arrayp), cols); - padding = cols - length; - buf = talloc_realloc(edit, buf, char, - talloc_array_length(buf) + - length + padding + 1); - if (buf == NULL) { - return WERR_NOMEM; - } - memcpy(&buf[index], *arrayp, length); - index += length; - memset(&buf[index], ' ', padding); - index += padding; - buf[index] = '\0'; - } +void dialog_section_hexedit_get_buf(struct dialog_section *section, + const void **data, size_t *size) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + *data = hexedit_get_buf(hexedit->buf); + *size = hexedit_get_buf_len(hexedit->buf); +} + +/* button box */ +struct dialog_section_buttons { + struct dialog_section section; + struct button_spec *spec; + int current_button; +}; + +static void buttons_unhighlight(struct dialog_section_buttons *buttons) +{ + short pair; + attr_t attr; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(buttons->section.window, &attr, &pair, NULL); + mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); + wnoutrefresh(buttons->section.window); +} + +static void buttons_highlight(struct dialog_section_buttons *buttons) +{ + struct button_spec *button = &buttons->spec[buttons->current_button]; + short pair; + attr_t attr; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(buttons->section.window, &attr, &pair, NULL); + mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); + mvwchgat(buttons->section.window, 0, button->col, + strlen(button->label), A_REVERSE, pair, NULL); + wmove(buttons->section.window, 0, button->col + 2); + wcursyncup(buttons->section.window); + wnoutrefresh(buttons->section.window); +} - set_field_buffer(edit->field[FLD_DATA], 0, buf); - talloc_free(buf); +static bool buttons_highlight_next(struct dialog_section_buttons *buttons) +{ + if (buttons->current_button < talloc_array_length(buttons->spec) - 1) { + buttons->current_button++; + buttons_highlight(buttons); + return true; } - case REG_BINARY: - /* initialized upon dialog creation */ - break; + return false; +} + +static bool buttons_highlight_previous(struct dialog_section_buttons *buttons) +{ + if (buttons->current_button > 0) { + buttons->current_button--; + buttons_highlight(buttons); + return true; } + return false; +} + +static WERROR buttons_create(struct dialog *dia, + struct dialog_section *section) +{ + size_t i, nbuttons; + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + nbuttons = talloc_array_length(buttons->spec); + for (i = 0; i < nbuttons; ++i) { + struct button_spec *button = &buttons->spec[i]; + mvwaddstr(section->window, 0, button->col, button->label); + } + + buttons->current_button = 0; return WERR_OK; } -static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key, - const char *name) +static bool buttons_on_btab(struct dialog *dia, struct dialog_section *section) { - uint32_t type; - DATA_BLOB blob; - WERROR rv; + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); - rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob); + return buttons_highlight_previous(buttons); +} - return W_ERROR_IS_OK(rv); +static bool buttons_on_tab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + return buttons_highlight_next(buttons); } -static WERROR set_value(struct edit_dialog *edit, struct registry_key *key, - uint32_t type, bool new_value) +static enum dialog_action buttons_on_enter(struct dialog *dia, + struct dialog_section *section) { - WERROR rv; - DATA_BLOB blob; - char *name = string_trim(edit, field_buffer(edit->field[FLD_NAME], 0)); + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + struct button_spec *spec = &buttons->spec[buttons->current_button]; - if (!new_value && !edit->buf && !field_status(edit->field[FLD_DATA])) { - return WERR_OK; - } - if (new_value && value_exists(edit, key, name)) { - return WERR_FILE_EXISTS; + if (spec->on_enter) { + return spec->on_enter(dia, section); } - switch (edit->mode) { - case REG_DWORD: { - uint32_t val; - int base = 10; - const char *buf = field_buffer(edit->field[FLD_DATA], 0); + return spec->action; +} - if (buf[0] == '0' && tolower(buf[1]) == 'x') { - base = 16; - } +static bool buttons_on_focus(struct dialog *dia, + struct dialog_section *section, + bool forward) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); - val = strtoul(buf, NULL, base); - blob = data_blob_talloc(edit, NULL, sizeof(val)); - SIVAL(blob.data, 0, val); - rv = WERR_OK; - break; + if (forward) { + buttons->current_button = 0; + } else { + buttons->current_button = talloc_array_length(buttons->spec) - 1; } - case REG_SZ: - case REG_EXPAND_SZ: { - const char *buf = field_buffer(edit->field[FLD_DATA], 0); - char *str = string_trim(edit, buf); + buttons_highlight(buttons); - if (!str || !push_reg_sz(edit, &blob, str)) { - rv = WERR_NOMEM; - } - break; + return true; +} + +static void buttons_on_leave_focus(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + buttons_unhighlight(buttons); +} + +struct dialog_section_ops buttons_ops = { + .create = buttons_create, + .on_tab = buttons_on_tab, + .on_btab = buttons_on_btab, + .on_up = buttons_on_btab, + .on_down = buttons_on_tab, + .on_left = buttons_on_btab, + .on_right = buttons_on_tab, + .on_enter = buttons_on_enter, + .on_focus = buttons_on_focus, + .on_leave_focus = buttons_on_leave_focus +}; + +static int buttons_free(struct dialog_section_buttons *buttons) +{ + dialog_section_destroy(&buttons->section); + return 0; +} + +struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx, + const struct button_spec *spec) +{ + struct dialog_section_buttons *buttons; + size_t i, nbuttons; + int width; + + buttons = talloc_zero(ctx, struct dialog_section_buttons); + if (buttons == NULL) { + return NULL; } - case REG_MULTI_SZ: { - int rows, cols, max; - const char **arr; - size_t i; - const char *buf = field_buffer(edit->field[FLD_DATA], 0); + talloc_set_destructor(buttons, buttons_free); - dynamic_field_info(edit->field[FLD_DATA], &rows, &cols, &max); + for (nbuttons = 0; spec[nbuttons].label; ++nbuttons) { + } + buttons->spec = talloc_zero_array(buttons, struct button_spec, nbuttons); + if (buttons->spec == NULL) { + goto fail; + } - arr = talloc_zero_array(edit, const char *, rows + 1); - if (arr == NULL) { - return WERR_NOMEM; - } - for (i = 0; *buf; ++i, buf += cols) { - SMB_ASSERT(i < rows); - arr[i] = string_trim_n(edit, buf, cols); + for (width = 0, i = 0; i < nbuttons; ++i) { + buttons->spec[i] = spec[i]; + buttons->spec[i].label = talloc_asprintf(buttons->spec, + "[ %s ]", + spec[i].label); + if (!buttons->spec[i].label) { + goto fail; } - if (!push_reg_multi_sz(edit, &blob, arr)) { - rv = WERR_NOMEM; + + buttons->spec[i].col = width; + width += strlen(buttons->spec[i].label); + if (i != nbuttons - 1) { + ++width; } - break; } - case REG_BINARY: - blob = data_blob_talloc(edit, NULL, edit->buf->len); - memcpy(blob.data, edit->buf->data, edit->buf->len); - break; + + dialog_section_init(&buttons->section, &buttons_ops, 1, width); + + return &buttons->section; + +fail: + talloc_free(buttons); + return NULL; +} + +/* options */ +struct dialog_section_options { + struct dialog_section section; + struct option_spec *spec; + int current_option; + bool single_select; +}; + +static void options_unhighlight(struct dialog_section_options *options) +{ + short pair; + attr_t attr; + size_t row; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(options->section.window, &attr, &pair, NULL); + for (row = 0; row < options->section.nlines; ++row) { + mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); + } + wnoutrefresh(options->section.window); +} + +static void options_highlight(struct dialog_section_options *options) +{ + struct option_spec *option = &options->spec[options->current_option]; + short pair; + attr_t attr; + size_t row; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(options->section.window, &attr, &pair, NULL); + for (row = 0; row < options->section.nlines; ++row) { + mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); + } + mvwchgat(options->section.window, option->row, option->col, + strlen(option->label), A_REVERSE, pair, NULL); + wmove(options->section.window, option->row, option->col + 4); + wcursyncup(options->section.window); + wnoutrefresh(options->section.window); +} + +static void options_render_state(struct dialog_section_options *options) +{ + size_t i, noptions; + + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + struct option_spec *option = &options->spec[i]; + char c = ' '; + if (*option->state) + c = 'x'; + mvwaddch(options->section.window, + option->row, option->col + 1, c); + wnoutrefresh(options->section.window); } +} - rv = reg_val_set(key, name, type, blob); +static bool options_highlight_next(struct dialog_section_options *options) +{ + if (options->current_option < talloc_array_length(options->spec) - 1) { + options->current_option++; + options_highlight(options); + return true; + } + return false; +} - return rv; +static bool options_highlight_previous(struct dialog_section_options *options) +{ + if (options->current_option > 0) { + options->current_option--; + options_highlight(options); + return true; + } + return false; } -static void section_down(struct edit_dialog *edit) +static WERROR options_create(struct dialog *dia, + struct dialog_section *section) { - switch (edit->section) { - case IN_NAME: - if (form_driver(edit->input, REQ_VALIDATION) == E_OK) { - edit->section = IN_DATA; - if (edit->buf) { - hexedit_set_cursor(edit->buf); - } else { - set_current_field(edit->input, - edit->field[FLD_DATA]); - pos_form_cursor(edit->input); - } - } - break; - case IN_DATA: - if (edit->buf || - form_driver(edit->input, REQ_VALIDATION) == E_OK) { - edit->section = IN_MENU; - menu_driver(edit->dia->choices, REQ_FIRST_ITEM); - pos_menu_cursor(edit->dia->choices); - } - break; - case IN_MENU: - if (current_item_is_last(edit->dia->choices)) { - edit->section = IN_NAME; - set_current_field(edit->input, edit->field[FLD_NAME]); - pos_form_cursor(edit->input); - } else { - menu_driver(edit->dia->choices, REQ_RIGHT_ITEM); - } - break; + size_t i, noptions; + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + struct option_spec *option = &options->spec[i]; + mvwaddstr(section->window, option->row, option->col, + option->label); } + + options->current_option = 0; + options_render_state(options); + + return WERR_OK; } -static void section_up(struct edit_dialog *edit) +static bool options_on_btab(struct dialog *dia, struct dialog_section *section) { - switch (edit->section) { - case IN_NAME: - if (form_driver(edit->input, REQ_VALIDATION) == E_OK) { - edit->section = IN_MENU; - menu_driver(edit->dia->choices, REQ_LAST_ITEM); - pos_menu_cursor(edit->dia->choices); - } - break; - case IN_DATA: - if (edit->buf || - form_driver(edit->input, REQ_VALIDATION) == E_OK) { - edit->section = IN_NAME; - set_current_field(edit->input, edit->field[FLD_NAME]); - pos_form_cursor(edit->input); - } - break; - case IN_MENU: - if (current_item_is_first(edit->dia->choices)) { - edit->section = IN_DATA; - if (edit->buf) { - hexedit_set_cursor(edit->buf); - } else { - set_current_field(edit->input, - edit->field[FLD_DATA]); - pos_form_cursor(edit->input); + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + return options_highlight_previous(options); +} + +static bool options_on_tab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + return options_highlight_next(options); +} + +static void options_on_input(struct dialog *dia, struct dialog_section *section, int c) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + if (c == ' ') { + struct option_spec *option = &options->spec[options->current_option]; + if (options->single_select) { + size_t i, noptions; + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + *(options->spec[i].state) = false; } - } else { - menu_driver(edit->dia->choices, REQ_LEFT_ITEM); } - break; + *option->state = !*option->state; + options_unhighlight(options); + options_render_state(options); + options_highlight(options); } } -static void handle_hexedit_input(struct hexedit *buf, int c) +static enum dialog_action options_on_enter(struct dialog *dia, struct dialog_section *section) { - switch (c) { - case KEY_UP: - hexedit_driver(buf, HE_CURSOR_UP); - break; - case KEY_DOWN: - hexedit_driver(buf, HE_CURSOR_DOWN); - break; - case KEY_LEFT: - hexedit_driver(buf, HE_CURSOR_LEFT); - break; - case KEY_RIGHT: - hexedit_driver(buf, HE_CURSOR_RIGHT); - break; - default: - hexedit_driver(buf, c); - break; + options_on_input(dia, section, ' '); + return DIALOG_OK; +} + +static bool options_on_focus(struct dialog *dia, + struct dialog_section *section, + bool forward) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + if (forward) { + options->current_option = 0; + } else { + options->current_option = talloc_array_length(options->spec) - 1; } + options_highlight(options); - hexedit_set_cursor(buf); + return true; } -static WERROR edit_init_dialog(struct edit_dialog *edit, uint32_t type) +static void options_on_leave_focus(struct dialog *dia, + struct dialog_section *section) { - char *title; - int diaheight = -1; - int winheight = -1; - const char *choices[] = { - "Ok", - "Cancel", - "Resize", - NULL - }; + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + options_unhighlight(options); +} - switch (edit->mode) { - case REG_MULTI_SZ: - diaheight = EDIT_HEIGHT_MULTILINE; - winheight = EDIT_FORM_WIN_HEIGHT_MULTILINE; - choices[2] = NULL; - break; - case REG_BINARY: - diaheight = EDIT_HEIGHT_BUF; - winheight = EDIT_FORM_WIN_HEIGHT_BUF; - break; - default: - diaheight = EDIT_HEIGHT_ONELINE; - winheight = EDIT_FORM_WIN_HEIGHT_ONELINE; - choices[2] = NULL; - break; +struct dialog_section_ops options_ops = { + .create = options_create, + .on_tab = options_on_tab, + .on_btab = options_on_btab, + .on_up = options_on_btab, + .on_down = options_on_tab, + .on_left = options_on_btab, + .on_right = options_on_tab, + .on_input = options_on_input, + .on_enter = options_on_enter, + .on_focus = options_on_focus, + .on_leave_focus = options_on_leave_focus +}; + +static int options_free(struct dialog_section_options *options) +{ + dialog_section_destroy(&options->section); + return 0; +} + +struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx, + const struct option_spec *spec, + int maxcol, bool single_select) +{ + struct dialog_section_options *options; + size_t i, noptions; + int width, maxwidth, maxrows; + + options = talloc_zero(ctx, struct dialog_section_options); + if (options == NULL) { + return NULL; } + talloc_set_destructor(options, options_free); - title = talloc_asprintf(edit, "Edit %s value", str_regtype(type)); - if (title == NULL) { - return WERR_NOMEM; + for (noptions = 0; spec[noptions].label; ++noptions) { } - edit->dia = dialog_choice_center_new(edit, title, choices, diaheight, - EDIT_WIDTH); - talloc_free(title); - if (edit->dia == NULL) { - return WERR_NOMEM; + options->spec = talloc_zero_array(options, struct option_spec, noptions); + if (options->spec == NULL) { + goto fail; } - edit->input_win = derwin(edit->dia->sub_window, winheight, - EDIT_FORM_WIN_WIDTH, - EDIT_FORM_WIN_Y, EDIT_FORM_WIN_X); - if (edit->input_win == NULL) { - return WERR_NOMEM; + + maxrows = noptions / maxcol; + if (noptions % maxcol) { + ++maxrows; } - return WERR_OK; + for (width = 0, maxwidth = 0, i = 0; i < noptions; ++i) { + options->spec[i] = spec[i]; + options->spec[i].label = talloc_asprintf(options->spec, + "[ ] %s", + spec[i].label); + if (!options->spec[i].label) { + goto fail; + } + + options->spec[i].col = maxwidth; + options->spec[i].row = i % maxrows; + width = MAX(strlen(options->spec[i].label), width); + if (options->spec[i].row == maxrows - 1 || i == noptions - 1) { + maxwidth += width + 1; + width = 0; + } + } + + dialog_section_init(&options->section, &options_ops, maxrows, maxwidth - 1); + options->single_select = single_select; + + return &options->section; + +fail: + talloc_free(options); + return NULL; } -static WERROR edit_init_form(struct edit_dialog *edit, - const struct value_item *vitem) + +int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, + const char *msg, ...) { + va_list ap; + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; - edit->field[0] = new_field(EDIT_NAME_LABEL_HEIGHT, - EDIT_NAME_LABEL_WIDTH, - EDIT_NAME_LABEL_Y, - EDIT_NAME_LABEL_X, 0, 0); - if (edit->field[0] == NULL) { - return WERR_NOMEM; + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); + va_start(ap, msg); + section = dialog_section_label_new_va(dia, msg, ap); + va_end(ap); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_text_field_new(dia, 1, -1); + dialog_section_set_name(section, "input"); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); + + *output = NULL; + if (action == DIALOG_OK) { + section = dialog_find_section(dia, "input"); + *output = dialog_section_text_field_get(ctx, section); } - set_field_buffer(edit->field[0], 0, "Name"); - field_opts_off(edit->field[0], O_EDIT); - edit->field[FLD_NAME] = new_field(EDIT_NAME_INPUT_HEIGHT, - EDIT_NAME_INPUT_WIDTH, - EDIT_NAME_INPUT_Y, - EDIT_NAME_INPUT_X, 0, 0); - if (edit->field[FLD_NAME] == NULL) { - return WERR_NOMEM; + talloc_free(dia); + + return action; +} + +int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, + const char *title, const char *msg, ...) +{ + va_list ap; + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct button_spec spec[3]; + + memset(&spec, '\0', sizeof(spec)); + spec[0].label = "OK"; + spec[0].action = DIALOG_OK; + if (type == DIA_CONFIRM) { + spec[1].label = "Cancel"; + spec[1].action = DIALOG_CANCEL; } - edit->field[2] = new_field(EDIT_DATA_LABEL_HEIGHT, - EDIT_DATA_LABEL_WIDTH, - EDIT_DATA_LABEL_Y, - EDIT_DATA_LABEL_X, 0, 0); - if (edit->field[2] == NULL) { - return WERR_NOMEM; + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); + va_start(ap, msg); + section = dialog_section_label_new_va(dia, msg, ap); + va_end(ap); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); + + return action; +} + + +struct edit_req { + uint32_t type; + uint32_t mode; + struct registry_key *key; + const struct value_item *vitem; +}; + +static WERROR fill_value_buffer(struct dialog *dia, struct edit_req *edit) +{ + char *tmp; + struct dialog_section *data; + + if (edit->vitem == NULL) { + return WERR_OK; } - set_field_buffer(edit->field[2], 0, "Data"); - field_opts_off(edit->field[2], O_EDIT); - if (edit->mode == REG_BINARY) { - size_t len = 8; - const void *buf = NULL; + data = dialog_find_section(dia, "data"); + SMB_ASSERT(data != NULL); - if (vitem) { - len = vitem->data.length; - buf = vitem->data.data; + switch (edit->mode) { + case REG_DWORD: { + uint32_t v = 0; + if (edit->vitem->data.length >= 4) { + v = IVAL(edit->vitem->data.data, 0); } - edit->buf = hexedit_new(edit, edit->dia->sub_window, - EDIT_DATA_HEIGHT_BUF, - EDIT_DATA_INPUT_Y, - EDIT_DATA_INPUT_X, - buf, len); - if (edit->buf == NULL) { + tmp = talloc_asprintf(dia, "%u", (unsigned)v); + if (tmp == NULL) { return WERR_NOMEM; } - hexedit_refresh(edit->buf); - hexedit_set_cursor(edit->buf); - } else { - int val_rows = EDIT_DATA_HEIGHT_ONELINE; + dialog_section_text_field_set(data, tmp); + talloc_free(tmp); + break; + } + case REG_SZ: + case REG_EXPAND_SZ: { + const char *s; - if (edit->mode == REG_MULTI_SZ) { - val_rows = EDIT_DATA_HEIGHT_MULTILINE; - } - edit->field[FLD_DATA] = new_field(val_rows, - EDIT_DATA_INPUT_WIDTH, - EDIT_DATA_INPUT_Y, - EDIT_DATA_INPUT_X, 0, 0); - if (edit->field[FLD_DATA] == NULL) { + if (!pull_reg_sz(dia, &edit->vitem->data, &s)) { return WERR_NOMEM; } + dialog_section_text_field_set(data, s); + break; } + case REG_MULTI_SZ: { + const char **array; - set_field_back(edit->field[FLD_NAME], A_REVERSE); - field_opts_off(edit->field[FLD_NAME], O_BLANK | O_AUTOSKIP | O_STATIC); - if (edit->field[FLD_DATA]) { - set_field_back(edit->field[FLD_DATA], A_REVERSE); - field_opts_off(edit->field[FLD_DATA], - O_BLANK | O_AUTOSKIP | O_STATIC | O_WRAP); - if (edit->mode == REG_DWORD) { - set_field_type(edit->field[FLD_DATA], TYPE_REGEXP, - "^ *([0-9]+|0[xX][0-9a-fA-F]+) *$"); + if (!pull_reg_multi_sz(dia, &edit->vitem->data, &array)) { + return WERR_NOMEM; } + return dialog_section_text_field_set_lines(dia, data, array); } - - if (vitem) { - set_field_buffer(edit->field[FLD_NAME], 0, vitem->value_name); - field_opts_off(edit->field[FLD_NAME], O_EDIT); - fill_value_buffer(edit, vitem); - } - - edit->input = new_form(edit->field); - if (edit->input == NULL) { - return WERR_NOMEM; + case REG_BINARY: + default: + return dialog_section_hexedit_set_buf(data, + edit->vitem->data.data, + edit->vitem->data.length); } - form_opts_off(edit->input, O_NL_OVERLOAD | O_BS_OVERLOAD); - - set_form_win(edit->input, edit->dia->sub_window); - set_form_sub(edit->input, edit->input_win); - set_current_field(edit->input, edit->field[FLD_NAME]); - post_form(edit->input); return WERR_OK; } -static WERROR handle_editor_input(struct edit_dialog *edit, - struct registry_key *key, - uint32_t type, - const struct value_item *vitem, int c) +static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key, + const char *name) { - WERROR rv = WERR_OK; - int selection; + uint32_t type; + DATA_BLOB blob; + WERROR rv; - if (edit->section == IN_NAME) { - handle_form_input(edit->input, c); - } else if (edit->section == IN_DATA) { - if (edit->buf) { - handle_hexedit_input(edit->buf, c); - } else { - handle_form_input(edit->input, c); + rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob); + + return W_ERROR_IS_OK(rv); +} + +static bool edit_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct edit_req *edit = arg; + WERROR rv; + DATA_BLOB blob; + const char *name; + struct dialog_section *name_section, *data; + + name_section = dialog_find_section(dia, "name"); + if (name_section) { + name = dialog_section_text_field_get(dia, name_section); + if (*name == '\0') { + dialog_notice(dia, DIA_ALERT, "Error", + "Value name must not be blank."); + return false; } - } else { - selection = handle_menu_input(edit->dia->choices, c); - if (selection == DIALOG_OK) { - rv = set_value(edit, key, type, vitem == NULL); - if (W_ERROR_EQUAL(rv, WERR_FILE_EXISTS)) { - dialog_notice(edit, DIA_ALERT, - "Value exists", - "Value name already exists."); - selection = -1; - } else { - edit->closing = true; - } - } else if (selection == DIALOG_RESIZE) { - char *n; - size_t newlen = 0; - - dialog_input(edit, &n, "Resize buffer", - "Enter new size"); - if (n) { - newlen = strtoul(n, NULL, 10); - edit->section = IN_DATA; - hexedit_resize_buffer(edit->buf, newlen); - hexedit_refresh(edit->buf); - hexedit_set_cursor(edit->buf); - talloc_free(n); - } - } else if (selection == DIALOG_CANCEL) { - edit->closing = true; + if (value_exists(dia, edit->key, name)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Value named \"%s\" already exists.", + name); + return false; } + } else { + SMB_ASSERT(edit->vitem); + name = edit->vitem->value_name; } + SMB_ASSERT(name); - return rv; -} + data = dialog_find_section(dia, "data"); + SMB_ASSERT(data != NULL); -WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, - uint32_t type, const struct value_item *vitem, - bool force_binary) -{ - struct edit_dialog *edit; - WERROR rv = WERR_NOMEM; + rv = WERR_OK; + switch (edit->mode) { + case REG_DWORD: { + unsigned long long v; + uint32_t val; - edit = talloc_zero(ctx, struct edit_dialog); - if (edit == NULL) { - return rv; + if (!dialog_section_text_field_get_uint(data, &v)) { + dialog_notice(dia, DIA_ALERT, "Error", + "REG_DWORD value must be an integer."); + return false; + } + if (v > UINT32_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "REG_DWORD value must less than %lu.", + (unsigned long)UINT32_MAX); + return false; + } + val = (uint32_t)v; + blob = data_blob_talloc(dia, NULL, sizeof(val)); + SIVAL(blob.data, 0, val); + break; } - talloc_set_destructor(edit, edit_dialog_free); + case REG_SZ: + case REG_EXPAND_SZ: { + const char *buf; - edit->mode = type; - if (force_binary || (vitem && vitem->unprintable)) { - edit->mode = REG_BINARY; + buf = dialog_section_text_field_get(dia, data); + if (!buf || !push_reg_sz(dia, &blob, buf)) { + rv = WERR_NOMEM; + } + break; } + case REG_MULTI_SZ: { + const char **lines; - rv = edit_init_dialog(edit, type); - if (!W_ERROR_IS_OK(rv)) { - goto finish; - } - rv = edit_init_form(edit, vitem); - if (!W_ERROR_IS_OK(rv)) { - goto finish; + lines = dialog_section_text_field_get_lines(dia, data); + if (!lines || !push_reg_multi_sz(dia, &blob, lines)) { + rv = WERR_NOMEM; + } + break; } + case REG_BINARY: { + const void *buf; + size_t len; - update_panels(); - doupdate(); - - edit->section = IN_NAME; - edit->closing = false; + dialog_section_hexedit_get_buf(data, &buf, &len); + blob = data_blob_talloc(dia, buf, len); + break; + } + } - do { - int c = dialog_getch(edit->dia); + if (W_ERROR_IS_OK(rv)) { + rv = reg_val_set(edit->key, name, edit->type, blob); + } - if (c == '\t') { - section_down(edit); - } else if (c == KEY_BTAB) { - section_up(edit); - } else { - rv = handle_editor_input(edit, key, type, vitem, c); - } + if (!W_ERROR_IS_OK(rv)) { + const char *msg = get_friendly_werror_msg(rv); + dialog_notice(dia, DIA_ALERT, "Error", + "Error saving value:\n%s", msg); - update_panels(); - doupdate(); - } while (!edit->closing); + return false; + } -finish: - talloc_free(edit); + return true; - return rv; } -int dialog_select_type(TALLOC_CTX *ctx, int *type) +WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, + uint32_t type, const struct value_item *vitem, + bool force_binary) { + WERROR err; + enum dialog_action action; struct dialog *dia; - const char *choices[] = { - "OK", - "Cancel", - NULL + struct dialog_section *section; + struct edit_req edit; + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } }; - const char *reg_types[] = { - "REG_DWORD", - "REG_SZ", - "REG_EXPAND_SZ", - "REG_MULTI_SZ", - "REG_BINARY", - }; -#define NTYPES (sizeof(reg_types) / sizeof(const char*)) - ITEM **item; - MENU *list; - WINDOW *type_win; - int sel = -1; - size_t i; - dia = dialog_choice_center_new(ctx, "New Value", choices, 10, 20); - if (dia == NULL) { - return -1; + edit.key = key; + edit.vitem = vitem; + edit.type = type; + edit.mode = type; + if (force_binary || (vitem && vitem->unprintable)) { + edit.mode = REG_BINARY; } - mvwprintw(dia->sub_window, 0, 0, "Choose type:"); - type_win = derwin(dia->sub_window, 6, 18, 1, 0); - if (type_win == NULL) { - goto finish; - } + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Edit Value", -1, -1); + dialog_set_submit_cb(dia, edit_on_submit, &edit); - item = talloc_zero_array(dia, ITEM *, NTYPES + 1); - if (item == NULL) { - goto finish; + section = dialog_section_label_new(dia, "Type"); + dialog_append_section(dia, section); + section = dialog_section_label_new(dia, "%s", + str_regtype(type)); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + + section = dialog_section_label_new(dia, "Name"); + dialog_append_section(dia, section); + if (vitem) { + section = dialog_section_label_new(dia, "%s", + vitem->value_name); + } else { + section = dialog_section_text_field_new(dia, 1, 50); + dialog_section_set_name(section, "name"); } + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); - for (i = 0; i < NTYPES; ++i) { - int t = regtype_by_string(reg_types[i]); + section = dialog_section_label_new(dia, "Data"); + dialog_append_section(dia, section); - item[i] = new_item(reg_types[i], reg_types[i]); - if (item[i] == NULL) { - goto finish; - } - set_item_userptr(item[i], (void*)(uintptr_t)t); + switch (edit.mode) { + case REG_DWORD: + case REG_SZ: + case REG_EXPAND_SZ: + section = dialog_section_text_field_new(dia, 1, 50); + break; + case REG_MULTI_SZ: + section = dialog_section_text_field_new(dia, 10, 50); + break; + case REG_BINARY: + default: + section = dialog_section_hexedit_new(dia, 10); + break; } - list = new_menu(item); - if (list == NULL) { - goto finish; - } + dialog_section_set_name(section, "data"); + dialog_append_section(dia, section); - set_menu_format(list, 7, 1); - set_menu_win(list, dia->sub_window); - set_menu_sub(list, type_win); - menu_opts_off(list, O_SHOWDESC); - set_menu_mark(list, "* "); - post_menu(list); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); - update_panels(); - doupdate(); + dialog_create(dia); - while (sel == -1) { - ITEM *it; - int c = dialog_getch(dia); + err = fill_value_buffer(dia, &edit); + if (!W_ERROR_IS_OK(err)) { + return err; + } - switch (c) { - case KEY_UP: - menu_driver(list, REQ_UP_ITEM); - break; - case KEY_DOWN: - menu_driver(list, REQ_DOWN_ITEM); - break; - case KEY_LEFT: - menu_driver(dia->choices, REQ_LEFT_ITEM); - break; - case KEY_RIGHT: - menu_driver(dia->choices, REQ_RIGHT_ITEM); - break; - case '\n': - case KEY_ENTER: - it = current_item(list); - *type = (int)(uintptr_t)item_userptr(it); - it = current_item(dia->choices); - sel = (int)(uintptr_t)item_userptr(it); - break; - } + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); - update_panels(); - doupdate(); - } + talloc_free(dia); -finish: - if (list) { - unpost_menu(list); - free_menu(list); - } - if (item) { - ITEM **it; - for (it = item; *it; ++it) { - free_item(*it); + return WERR_OK; +} + +int dialog_select_type(TALLOC_CTX *ctx, int *type) +{ + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + const char *reg_types[] = { + "REG_BINARY", + "REG_DWORD", + "REG_EXPAND_SZ", + "REG_MULTI_SZ", + "REG_SZ" + }; + #define NTYPES ARRAY_SIZE(reg_types) + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + bool flags[NTYPES] = { true }; + struct option_spec opsec[NTYPES + 1]; + unsigned i; + + memset(&opsec, '\0', sizeof(opsec)); + for (i = 0; i < NTYPES; ++i) { + opsec[i].label = reg_types[i]; + opsec[i].state = &flags[i]; + } + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "New Value", -1, -1); + + section = dialog_section_label_new(dia, "Select type for new value:"); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_options_new(dia, opsec, 2, true); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + + dialog_modal_loop(dia, &err, &action); + if (action == DIALOG_OK) { + for (i = 0; i < NTYPES; ++i) { + if (flags[i]) { + *type = regtype_by_string(reg_types[i]); + break; + } } } - if (type_win) { - delwin(type_win); - } + talloc_free(dia); - return sel; + return action; } int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts) diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h index f57bcd1a6f..0018f9fc06 100644 --- a/source3/utils/regedit_dialog.h +++ b/source3/utils/regedit_dialog.h @@ -24,44 +24,190 @@ #include <panel.h> #include <menu.h> +struct dialog; +struct dialog_section; + +/* dialog submit cb. return true to close dialog, false to keep + it open */ +typedef bool (*dialog_submit_cb)(struct dialog *, struct dialog_section *, + void *); + struct dialog { + char *title; WINDOW *window; - WINDOW *sub_window; - WINDOW *menu_window; PANEL *panel; - MENU *choices; - ITEM **choice_items; + int x; + int y; + short color; bool centered; + dialog_submit_cb submit; + void *submit_arg; + struct dialog_section *head_section; + struct dialog_section *tail_section; + struct dialog_section *current_section; +}; + +enum dialog_action { + DIALOG_IGNORE, + DIALOG_OK, + DIALOG_CANCEL }; -struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines, - int ncols, int y, int x); +struct dialog_section_ops { + /* create section */ + WERROR (*create)(struct dialog *, struct dialog_section *); + + /* (optional) cleanup the section */ + void (*destroy)(struct dialog_section *); + + /* (optional) handle character input */ + void (*on_input)(struct dialog *, struct dialog_section *, int c); + + /* (optional) handle a tab character. return true if section dealt + with the tab internally, or false to advance focus to + the next dialog section. */ + bool (*on_tab)(struct dialog *, struct dialog_section *); -struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title, - int nlines, int ncols); + /* (optional) handle a btab character. return true if section dealt + with the tab internally, or false to move focus to + the previous dialog section. */ + bool (*on_btab)(struct dialog *, struct dialog_section *); -struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, int ncols, - int y, int x); + /* */ + bool (*on_up)(struct dialog *, struct dialog_section *); -struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title, - const char **choices, int nlines, - int ncols); + /* */ + bool (*on_down)(struct dialog *, struct dialog_section *); + + /* */ + bool (*on_left)(struct dialog *, struct dialog_section *); + + /* */ + bool (*on_right)(struct dialog *, struct dialog_section *); + + /* (optional) handle enter key. return DIALOG_OK to submit + dialog, DIALOG_CANCEL to close dialog, or DIALOG_IGNORE to + handle the enter internally. */ + enum dialog_action (*on_enter)(struct dialog *, + struct dialog_section *); + + /* (optional) called when this section is about to take focus. forward + is set to true when focus has landed here from forward traversal, + such as from a tab. return true to accept focus, false to pass to an + adjacent section. */ + bool (*on_focus)(struct dialog *, struct dialog_section *, bool forward); + + /* (optional) called when focus is leaving this section */ + void (*on_leave_focus)(struct dialog *, struct dialog_section *); +}; + +enum section_justify { + SECTION_JUSTIFY_LEFT, + SECTION_JUSTIFY_CENTER, + SECTION_JUSTIFY_RIGHT, +}; + +struct dialog_section { + char *name; + int nlines; + int ncols; + WINDOW *window; + enum section_justify justify; + const struct dialog_section_ops *ops; + struct dialog_section *next; + struct dialog_section *prev; +}; + +struct dialog *dialog_new(TALLOC_CTX *ctx, short color, + const char *title, int y, int x); + +void dialog_section_destroy(struct dialog_section *section); +void dialog_section_init(struct dialog_section *section, + const struct dialog_section_ops *ops, + int nlines, int ncols); + +void dialog_section_set_name(struct dialog_section *section, const char *name); +const char *dialog_section_get_name(struct dialog_section *section); +void dialog_section_set_justify(struct dialog_section *section, + enum section_justify justify); + +void dialog_append_section(struct dialog *dia, + struct dialog_section *section); +struct dialog_section *dialog_find_section(struct dialog *dia, + const char *name); + +WERROR dialog_create(struct dialog *dia); +void dialog_show(struct dialog *dia); +void dialog_destroy(struct dialog *dia); +void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg); +bool dialog_handle_input(struct dialog *dia, WERROR *err, + enum dialog_action *action); +void dialog_modal_loop(struct dialog *dia, WERROR *err, + enum dialog_action *action); + +struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx, + const char *msg, va_list ap); +struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx, + const char *msg, ...); + +struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep); + + +struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx, + int height, int width); +const char *dialog_section_text_field_get(TALLOC_CTX *ctx, + struct dialog_section *section); +const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx, + struct dialog_section *section); +bool dialog_section_text_field_get_int(struct dialog_section *section, + long long *out); +bool dialog_section_text_field_get_uint(struct dialog_section *section, + unsigned long long *out); +void dialog_section_text_field_set(struct dialog_section *section, + const char *s); +WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx, + struct dialog_section *section, + const char **array); + +struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height); +WERROR dialog_section_hexedit_set_buf(struct dialog_section *section, + const void *data, size_t size); +void dialog_section_hexedit_get_buf(struct dialog_section *section, + const void **data, size_t *size); + +struct button_spec { + const char *label; + enum dialog_action (*on_enter)(struct dialog *, + struct dialog_section *); + enum dialog_action action; + + /* internal */ + int col; +}; +struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx, + const struct button_spec *spec); + +struct option_spec { + const char *label; + bool *state; + + /* internal */ + int col; + int row; +}; +struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx, + const struct option_spec *spec, + int maxcol, bool single_select); enum dialog_type { DIA_ALERT, DIA_CONFIRM }; -enum dialog_selection { - DIALOG_OK = 0, - DIALOG_CANCEL = 1 -}; - int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, const char *title, const char *msg, ...); -int dialog_input(TALLOC_CTX *ctx, char **output, const char *title, +int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, const char *msg, ...); struct registry_key; |