regedit: Enable using tab to cycle thru dialog buttons.
[Samba.git] / source3 / utils / regedit_dialog.c
blob04b88b54888a1dfaab6faa7040ca799334252a04
1 /*
2 * Samba Unix/Linux SMB client library
3 * Registry Editor
4 * Copyright (C) Christopher Davis 2012
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "regedit.h"
22 #include "regedit_dialog.h"
23 #include "regedit_valuelist.h"
24 #include "regedit_hexedit.h"
25 #include "util_reg.h"
26 #include "lib/registry/registry.h"
27 #include <stdarg.h>
28 #include <form.h>
30 static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n)
32 char *str;
34 str = talloc_strndup(ctx, buf, n);
36 if (str) {
37 trim_string(str, " ", " ");
40 return str;
43 static char *string_trim(TALLOC_CTX *ctx, const char *buf)
45 char *str;
47 str = talloc_strdup(ctx, buf);
49 if (str) {
50 trim_string(str, " ", " ");
53 return str;
56 static int dialog_free(struct dialog *dia)
58 if (dia->window) {
59 delwin(dia->window);
61 if (dia->sub_window) {
62 delwin(dia->sub_window);
64 if (dia->panel) {
65 del_panel(dia->panel);
67 if (dia->choices) {
68 unpost_menu(dia->choices);
69 free_menu(dia->choices);
71 if (dia->choice_items) {
72 ITEM **it;
73 for (it = dia->choice_items; *it != NULL; ++it) {
74 free_item(*it);
78 return 0;
81 struct dialog *dialog_new(TALLOC_CTX *ctx, const char *title, int nlines,
82 int ncols, int y, int x)
84 struct dialog *dia;
86 dia = talloc_zero(ctx, struct dialog);
87 if (dia == NULL) {
88 return NULL;
91 talloc_set_destructor(dia, dialog_free);
93 dia->window = newwin(nlines, ncols, y, x);
94 if (dia->window == NULL) {
95 goto fail;
98 box(dia->window, 0, 0);
99 mvwaddstr(dia->window, 0, 1, title);
101 /* body of the dialog within the box outline */
102 dia->sub_window = derwin(dia->window, nlines - 2, ncols - 2, 1, 1);
103 if (dia->sub_window == NULL) {
104 goto fail;
107 dia->panel = new_panel(dia->window);
108 if (dia->panel == NULL) {
109 goto fail;
112 return dia;
114 fail:
115 talloc_free(dia);
117 return NULL;
121 static void center_dialog_above_window(int *nlines, int *ncols,
122 int *y, int *x)
124 int centery, centerx;
126 centery = LINES / 2;
127 centerx = COLS / 2;
128 *y = 0;
129 *x = 0;
131 if (*nlines > LINES) {
132 *nlines = LINES;
134 if (*ncols > COLS) {
135 *ncols = COLS;
138 if (*nlines/2 < centery) {
139 *y = centery - *nlines / 2;
141 if (*ncols/2 < centerx) {
142 *x = centerx - *ncols / 2;
146 static int dialog_getch(struct dialog *dia)
148 int c;
150 c = regedit_getch();
152 if (c == KEY_RESIZE) {
153 int nlines, ncols, y, x;
155 getmaxyx(dia->window, nlines, ncols);
156 getbegyx(dia->window, y, x);
157 if (dia->centered) {
158 center_dialog_above_window(&nlines, &ncols, &y, &x);
159 } else {
160 if (nlines + y > LINES) {
161 if (nlines > LINES) {
162 y = 0;
163 } else {
164 y = LINES - nlines;
167 if (ncols + x > COLS) {
168 if (ncols > COLS) {
169 x = 0;
170 } else {
171 x = COLS - ncols;
175 move_panel(dia->panel, y, x);
176 doupdate();
179 return c;
182 struct dialog *dialog_center_new(TALLOC_CTX *ctx, const char *title, int nlines,
183 int ncols)
185 struct dialog *dia;
186 int y, x;
188 center_dialog_above_window(&nlines, &ncols, &y, &x);
190 dia = dialog_new(ctx, title, nlines, ncols, y, x);
191 if (dia) {
192 dia->centered = true;
195 return dia;
198 struct dialog *dialog_choice_new(TALLOC_CTX *ctx, const char *title,
199 const char **choices, int nlines,
200 int ncols, int y, int x)
202 size_t nchoices, i;
203 struct dialog *dia;
205 dia = dialog_new(ctx, title, nlines, ncols, y, x);
206 if (dia == NULL) {
207 return NULL;
210 dia->menu_window = derwin(dia->sub_window, 1, ncols - 3,
211 nlines - 3, 0);
212 if (dia->menu_window == NULL) {
213 goto fail;
216 for (nchoices = 0; choices[nchoices] != NULL; ++nchoices)
218 dia->choice_items = talloc_zero_array(dia, ITEM *, nchoices + 1);
219 if (dia->choice_items == NULL) {
220 goto fail;
222 for (i = 0; i < nchoices; ++i) {
223 char *desc = talloc_strdup(dia, choices[i]);
224 if (desc == NULL) {
225 goto fail;
227 dia->choice_items[i] = new_item(desc, desc);
228 if (dia->choice_items[i] == NULL) {
229 goto fail;
231 /* store choice index */
232 set_item_userptr(dia->choice_items[i], (void*)(uintptr_t)i);
235 dia->choices = new_menu(dia->choice_items);
236 if (dia->choices == NULL) {
237 goto fail;
240 set_menu_format(dia->choices, 1, ncols);
241 set_menu_win(dia->choices, dia->sub_window);
242 set_menu_sub(dia->choices, dia->menu_window);
243 menu_opts_off(dia->choices, O_SHOWDESC);
244 set_menu_mark(dia->choices, "* ");
245 post_menu(dia->choices);
246 wmove(dia->sub_window, 0, 0);
248 return dia;
250 fail:
251 talloc_free(dia);
253 return NULL;
256 struct dialog *dialog_choice_center_new(TALLOC_CTX *ctx, const char *title,
257 const char **choices, int nlines,
258 int ncols)
260 int y, x;
261 struct dialog *dia;
262 center_dialog_above_window(&nlines, &ncols, &y, &x);
264 dia = dialog_choice_new(ctx, title, choices, nlines, ncols, y, x);
265 if (dia) {
266 dia->centered = true;
269 return dia;
272 static bool current_item_is_first(MENU *menu)
274 const ITEM *it = current_item(menu);
276 return item_index(it) == 0;
279 static bool current_item_is_last(MENU *menu)
281 const ITEM *it = current_item(menu);
283 return item_index(it) == item_count(menu) - 1;
286 static int handle_menu_input(MENU *menu, int c)
288 ITEM *item;
290 switch (c) {
291 case KEY_BTAB:
292 if (current_item_is_first(menu)) {
293 menu_driver(menu, REQ_LAST_ITEM);
294 } else {
295 menu_driver(menu, REQ_LEFT_ITEM);
297 break;
298 case KEY_LEFT:
299 menu_driver(menu, REQ_LEFT_ITEM);
300 break;
301 case '\t':
302 if (current_item_is_last(menu)) {
303 menu_driver(menu, REQ_FIRST_ITEM);
304 break;
305 } else {
306 menu_driver(menu, REQ_RIGHT_ITEM);
308 case KEY_RIGHT:
309 menu_driver(menu, REQ_RIGHT_ITEM);
310 break;
311 case KEY_ENTER:
312 case '\n':
313 item = current_item(menu);
314 return (int)(uintptr_t)item_userptr(item);
317 return -1;
320 static void handle_form_input(FORM *frm, int c)
322 switch (c) {
323 case '\n':
324 form_driver(frm, REQ_NEW_LINE);
325 break;
326 case KEY_UP:
327 form_driver(frm, REQ_UP_CHAR);
328 break;
329 case KEY_DOWN:
330 form_driver(frm, REQ_DOWN_CHAR);
331 break;
332 case '\b':
333 case KEY_BACKSPACE:
334 form_driver(frm, REQ_DEL_PREV);
335 break;
336 case KEY_LEFT:
337 form_driver(frm, REQ_LEFT_CHAR);
338 break;
339 case KEY_RIGHT:
340 form_driver(frm, REQ_RIGHT_CHAR);
341 break;
342 default:
343 form_driver(frm, c);
344 break;
348 static int modal_loop(struct dialog *dia)
350 int c;
351 int selection = -1;
353 update_panels();
354 doupdate();
356 while (selection == -1) {
357 c = dialog_getch(dia);
358 selection = handle_menu_input(dia->choices, c);
359 update_panels();
360 doupdate();
363 talloc_free(dia);
365 return selection;
368 static struct dialog *dialog_msg_new(TALLOC_CTX *ctx, const char *title,
369 const char **choices, int nlines,
370 const char *msg, va_list ap)
372 struct dialog *dia;
373 char *str;
374 int width;
375 #define MIN_WIDTH 20
377 str = talloc_vasprintf(ctx, msg, ap);
378 if (str == NULL) {
379 return NULL;
382 width = strlen(str) + 2;
383 if (width < MIN_WIDTH) {
384 width = MIN_WIDTH;
386 dia = dialog_choice_center_new(ctx, title, choices, nlines, width);
387 if (dia == NULL) {
388 return NULL;
391 waddstr(dia->sub_window, str);
392 talloc_free(str);
394 return dia;
397 int dialog_input(TALLOC_CTX *ctx, char **output, const char *title,
398 const char *msg, ...)
400 va_list ap;
401 struct dialog *dia;
402 const char *choices[] = {
403 "Ok",
404 "Cancel",
405 NULL
407 FIELD *field[2] = {0};
408 FORM *input;
409 WINDOW *input_win;
410 int y, x;
411 int rv = -1;
412 bool input_section = true;
414 va_start(ap, msg);
415 dia = dialog_msg_new(ctx, title, choices, 7, msg, ap);
416 va_end(ap);
417 if (dia == NULL) {
418 return -1;
421 getmaxyx(dia->sub_window, y, x);
422 input_win = derwin(dia->sub_window, 1, x - 2, 2, 1);
423 if (input_win == NULL) {
424 goto finish;
426 field[0] = new_field(1, x - 2, 0, 0, 0, 0);
427 if (field[0] == NULL) {
428 goto finish;
431 field_opts_off(field[0], O_BLANK | O_AUTOSKIP | O_STATIC);
432 set_field_back(field[0], A_REVERSE);
434 input = new_form(field);
435 form_opts_off(input, O_NL_OVERLOAD | O_BS_OVERLOAD);
436 set_form_win(input, dia->sub_window);
437 set_form_sub(input, input_win);
438 set_current_field(input, field[0]);
439 post_form(input);
440 *output = NULL;
442 update_panels();
443 doupdate();
445 while (rv == -1) {
446 int c = dialog_getch(dia);
448 if (c == '\t' || c == KEY_BTAB) {
449 if (input_section) {
450 if (form_driver(input,REQ_VALIDATION) == E_OK) {
451 input_section = false;
452 if (c == '\t') {
453 menu_driver(dia->choices,
454 REQ_FIRST_ITEM);
455 } else {
456 menu_driver(dia->choices,
457 REQ_LAST_ITEM);
459 pos_menu_cursor(dia->choices);
461 } else {
462 if ((c == '\t' &&
463 current_item_is_last(dia->choices)) ||
464 (c == KEY_BTAB &&
465 current_item_is_first(dia->choices))) {
466 input_section = true;
467 set_current_field(input, field[0]);
468 pos_form_cursor(input);
469 } else {
470 handle_menu_input(dia->choices, c);
473 } else if (input_section) {
474 handle_form_input(input, c);
475 } else {
476 rv = handle_menu_input(dia->choices, c);
477 if (rv == DIALOG_OK) {
478 const char *buf = field_buffer(field[0], 0);
479 *output = string_trim(ctx, buf);
482 update_panels();
483 doupdate();
486 finish:
487 if (input) {
488 unpost_form(input);
489 free_form(input);
491 if (field[0]) {
492 free_field(field[0]);
494 if (input_win) {
495 delwin(input_win);
497 talloc_free(dia);
499 return rv;
502 int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type,
503 const char *title, const char *msg, ...)
505 va_list ap;
506 struct dialog *dia;
507 const char *choices[] = {
508 "Ok",
509 "Cancel",
510 NULL
513 if (type == DIA_ALERT) {
514 choices[1] = NULL;
517 va_start(ap, msg);
518 dia = dialog_msg_new(ctx, title, choices, 5, msg, ap);
519 va_end(ap);
520 if (dia == NULL) {
521 return -1;
524 return modal_loop(dia);
527 #define EDIT_WIDTH 50
528 #define EDIT_INTERNAL_WIDTH (EDIT_WIDTH - 2)
529 #define EDIT_INPUT_WIDTH (EDIT_INTERNAL_WIDTH - 2)
531 #define EDIT_NAME_LABEL_Y 0
532 #define EDIT_NAME_LABEL_X 0
533 #define EDIT_NAME_LABEL_WIDTH EDIT_INTERNAL_WIDTH
534 #define EDIT_NAME_LABEL_HEIGHT 1
535 #define EDIT_NAME_INPUT_Y 1
536 #define EDIT_NAME_INPUT_X 1
537 #define EDIT_NAME_INPUT_WIDTH EDIT_INPUT_WIDTH
538 #define EDIT_NAME_INPUT_HEIGHT 1
540 #define EDIT_DATA_LABEL_Y 3
541 #define EDIT_DATA_LABEL_X 0
542 #define EDIT_DATA_LABEL_WIDTH EDIT_INTERNAL_WIDTH
543 #define EDIT_DATA_LABEL_HEIGHT 1
544 #define EDIT_DATA_INPUT_Y 4
545 #define EDIT_DATA_INPUT_X 1
546 #define EDIT_DATA_INPUT_WIDTH EDIT_INPUT_WIDTH
547 #define EDIT_DATA_HEIGHT_ONELINE 1
548 #define EDIT_DATA_HEIGHT_MULTILINE 5
549 #define EDIT_DATA_HEIGHT_BUF 10
551 #define EDIT_FORM_WIN_Y 0
552 #define EDIT_FORM_WIN_X 0
553 #define EDIT_FORM_WIN_HEIGHT_ONELINE \
554 (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
555 EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_ONELINE)
557 #define EDIT_FORM_WIN_HEIGHT_MULTILINE \
558 (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
559 EDIT_DATA_LABEL_HEIGHT + EDIT_DATA_HEIGHT_MULTILINE)
561 #define EDIT_FORM_WIN_HEIGHT_BUF \
562 (EDIT_NAME_LABEL_HEIGHT + EDIT_NAME_INPUT_HEIGHT + 1 + \
563 EDIT_DATA_LABEL_HEIGHT)
565 #define EDIT_FORM_WIN_WIDTH EDIT_INTERNAL_WIDTH
567 #define EDIT_PAD 5
568 #define EDIT_HEIGHT_ONELINE (EDIT_FORM_WIN_HEIGHT_ONELINE + EDIT_PAD)
570 #define EDIT_HEIGHT_MULTILINE (EDIT_FORM_WIN_HEIGHT_MULTILINE + EDIT_PAD)
572 #define EDIT_HEIGHT_BUF \
573 (EDIT_FORM_WIN_HEIGHT_BUF + EDIT_DATA_HEIGHT_BUF + EDIT_PAD)
575 #define MAX_FIELDS 5
576 #define FLD_NAME 1
577 #define FLD_DATA 3
579 enum input_section {
580 IN_NAME,
581 IN_DATA,
582 IN_MENU
585 struct edit_dialog {
586 struct dialog *dia;
587 WINDOW *input_win;
588 FORM *input;
589 FIELD *field[MAX_FIELDS];
590 struct hexedit *buf;
591 enum input_section section;
594 static int edit_dialog_free(struct edit_dialog *edit)
596 FIELD **f;
598 if (edit->input) {
599 unpost_form(edit->input);
600 free_form(edit->input);
602 for (f = edit->field; *f; ++f) {
603 free_field(*f);
605 delwin(edit->input_win);
607 return 0;
610 static WERROR fill_value_buffer(struct edit_dialog *edit,
611 const struct value_item *vitem)
613 char *tmp;
615 switch (vitem->type) {
616 case REG_DWORD: {
617 uint32_t v = 0;
618 if (vitem->data.length >= 4) {
619 v = IVAL(vitem->data.data, 0);
621 tmp = talloc_asprintf(edit, "0x%x", v);
622 if (tmp == NULL) {
623 return WERR_NOMEM;
625 set_field_buffer(edit->field[FLD_DATA], 0, tmp);
626 talloc_free(tmp);
627 break;
629 case REG_SZ:
630 case REG_EXPAND_SZ: {
631 const char *s;
633 if (!pull_reg_sz(edit, &vitem->data, &s)) {
634 return WERR_NOMEM;
636 set_field_buffer(edit->field[FLD_DATA], 0, s);
637 break;
639 case REG_MULTI_SZ: {
640 const char **p, **a;
641 char *buf = NULL;
643 if (!pull_reg_multi_sz(edit, &vitem->data, &a)) {
644 return WERR_NOMEM;
646 for (p = a; *p != NULL; ++p) {
647 if (buf == NULL) {
648 buf = talloc_asprintf(edit, "%s\n", *p);
649 } else {
650 buf = talloc_asprintf_append(buf, "%s\n", *p);
652 if (buf == NULL) {
653 return WERR_NOMEM;
656 set_field_buffer(edit->field[FLD_DATA], 0, buf);
657 talloc_free(buf);
659 case REG_BINARY:
660 /* initialized upon dialog creation */
661 break;
664 return WERR_OK;
667 static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key,
668 const char *name)
670 uint32_t type;
671 DATA_BLOB blob;
672 WERROR rv;
674 rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob);
676 return W_ERROR_IS_OK(rv);
679 static WERROR set_value(struct edit_dialog *edit, struct registry_key *key,
680 uint32_t type, bool new_value)
682 WERROR rv;
683 DATA_BLOB blob;
684 char *name = string_trim(edit, field_buffer(edit->field[FLD_NAME], 0));
686 if (!new_value && !edit->buf && !field_status(edit->field[FLD_DATA])) {
687 return WERR_OK;
689 if (new_value && value_exists(edit, key, name)) {
690 return WERR_FILE_EXISTS;
693 switch (type) {
694 case REG_DWORD: {
695 uint32_t val;
696 int base = 10;
697 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
699 if (buf[0] == '0' && tolower(buf[1]) == 'x') {
700 base = 16;
703 val = strtoul(buf, NULL, base);
704 blob = data_blob_talloc(edit, NULL, sizeof(val));
705 SIVAL(blob.data, 0, val);
706 rv = WERR_OK;
707 break;
709 case REG_SZ:
710 case REG_EXPAND_SZ: {
711 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
712 char *str = string_trim(edit, buf);
714 if (!str || !push_reg_sz(edit, &blob, str)) {
715 rv = WERR_NOMEM;
717 break;
719 case REG_MULTI_SZ: {
720 int rows, cols, max;
721 const char **arr;
722 size_t i;
723 const char *buf = field_buffer(edit->field[FLD_DATA], 0);
725 dynamic_field_info(edit->field[FLD_DATA], &rows, &cols, &max);
727 arr = talloc_zero_array(edit, const char *, rows + 1);
728 if (arr == NULL) {
729 return WERR_NOMEM;
731 for (i = 0; *buf; ++i, buf += cols) {
732 SMB_ASSERT(i < rows);
733 arr[i] = string_trim_n(edit, buf, cols);
735 if (!push_reg_multi_sz(edit, &blob, arr)) {
736 rv = WERR_NOMEM;
738 break;
740 case REG_BINARY:
741 blob = data_blob_talloc(edit, NULL, edit->buf->len);
742 memcpy(blob.data, edit->buf->data, edit->buf->len);
743 break;
746 rv = reg_val_set(key, name, type, blob);
748 return rv;
751 static void section_down(struct edit_dialog *edit)
753 switch (edit->section) {
754 case IN_NAME:
755 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
756 edit->section = IN_DATA;
757 if (edit->buf) {
758 hexedit_set_cursor(edit->buf);
759 } else {
760 set_current_field(edit->input,
761 edit->field[FLD_DATA]);
762 pos_form_cursor(edit->input);
765 break;
766 case IN_DATA:
767 if (edit->buf ||
768 form_driver(edit->input, REQ_VALIDATION) == E_OK) {
769 edit->section = IN_MENU;
770 menu_driver(edit->dia->choices, REQ_FIRST_ITEM);
771 pos_menu_cursor(edit->dia->choices);
773 break;
774 case IN_MENU:
775 if (current_item_is_last(edit->dia->choices)) {
776 edit->section = IN_NAME;
777 set_current_field(edit->input, edit->field[FLD_NAME]);
778 pos_form_cursor(edit->input);
779 } else {
780 menu_driver(edit->dia->choices, REQ_RIGHT_ITEM);
782 break;
784 update_panels();
785 doupdate();
788 static void section_up(struct edit_dialog *edit)
790 switch (edit->section) {
791 case IN_NAME:
792 if (form_driver(edit->input, REQ_VALIDATION) == E_OK) {
793 edit->section = IN_MENU;
794 menu_driver(edit->dia->choices, REQ_LAST_ITEM);
795 pos_menu_cursor(edit->dia->choices);
797 break;
798 case IN_DATA:
799 if (edit->buf ||
800 form_driver(edit->input, REQ_VALIDATION) == E_OK) {
801 edit->section = IN_NAME;
802 set_current_field(edit->input, edit->field[FLD_NAME]);
803 pos_form_cursor(edit->input);
805 break;
806 case IN_MENU:
807 if (current_item_is_first(edit->dia->choices)) {
808 edit->section = IN_DATA;
809 if (edit->buf) {
810 hexedit_set_cursor(edit->buf);
811 } else {
812 set_current_field(edit->input, edit->field[FLD_DATA]);
813 pos_form_cursor(edit->input);
815 } else {
816 menu_driver(edit->dia->choices, REQ_LEFT_ITEM);
818 break;
820 update_panels();
821 doupdate();
824 static void handle_hexedit_input(struct hexedit *buf, int c)
826 switch (c) {
827 case KEY_UP:
828 hexedit_driver(buf, HE_CURSOR_UP);
829 break;
830 case KEY_DOWN:
831 hexedit_driver(buf, HE_CURSOR_DOWN);
832 break;
833 case KEY_LEFT:
834 hexedit_driver(buf, HE_CURSOR_LEFT);
835 break;
836 case KEY_RIGHT:
837 hexedit_driver(buf, HE_CURSOR_RIGHT);
838 break;
839 default:
840 hexedit_driver(buf, c);
841 break;
844 hexedit_set_cursor(buf);
847 static WERROR edit_init_dialog(struct edit_dialog *edit, uint32_t type)
849 char *title;
850 int diaheight = -1;
851 int winheight = -1;
852 const char *choices[] = {
853 "Ok",
854 "Cancel",
855 "Resize",
856 NULL
859 switch (type) {
860 case REG_MULTI_SZ:
861 diaheight = EDIT_HEIGHT_MULTILINE;
862 winheight = EDIT_FORM_WIN_HEIGHT_MULTILINE;
863 choices[2] = NULL;
864 break;
865 case REG_BINARY:
866 diaheight = EDIT_HEIGHT_BUF;
867 winheight = EDIT_FORM_WIN_HEIGHT_BUF;
868 break;
869 default:
870 diaheight = EDIT_HEIGHT_ONELINE;
871 winheight = EDIT_FORM_WIN_HEIGHT_ONELINE;
872 choices[2] = NULL;
873 break;
876 title = talloc_asprintf(edit, "Edit %s value", str_regtype(type));
877 if (title == NULL) {
878 return WERR_NOMEM;
880 edit->dia = dialog_choice_center_new(edit, title, choices, diaheight,
881 EDIT_WIDTH);
882 talloc_free(title);
883 if (edit->dia == NULL) {
884 return WERR_NOMEM;
886 edit->input_win = derwin(edit->dia->sub_window, winheight,
887 EDIT_FORM_WIN_WIDTH,
888 EDIT_FORM_WIN_Y, EDIT_FORM_WIN_X);
889 if (edit->input_win == NULL) {
890 return WERR_NOMEM;
893 return WERR_OK;
896 static WERROR edit_init_form(struct edit_dialog *edit, uint32_t type,
897 const struct value_item *vitem)
900 edit->field[0] = new_field(EDIT_NAME_LABEL_HEIGHT,
901 EDIT_NAME_LABEL_WIDTH,
902 EDIT_NAME_LABEL_Y,
903 EDIT_NAME_LABEL_X, 0, 0);
904 if (edit->field[0] == NULL) {
905 return WERR_NOMEM;
907 set_field_buffer(edit->field[0], 0, "Name");
908 field_opts_off(edit->field[0], O_EDIT);
910 edit->field[FLD_NAME] = new_field(EDIT_NAME_INPUT_HEIGHT,
911 EDIT_NAME_INPUT_WIDTH,
912 EDIT_NAME_INPUT_Y,
913 EDIT_NAME_INPUT_X, 0, 0);
914 if (edit->field[FLD_NAME] == NULL) {
915 return WERR_NOMEM;
918 edit->field[2] = new_field(EDIT_DATA_LABEL_HEIGHT,
919 EDIT_DATA_LABEL_WIDTH,
920 EDIT_DATA_LABEL_Y,
921 EDIT_DATA_LABEL_X, 0, 0);
922 if (edit->field[2] == NULL) {
923 return WERR_NOMEM;
925 set_field_buffer(edit->field[2], 0, "Data");
926 field_opts_off(edit->field[2], O_EDIT);
928 if (type == REG_BINARY) {
929 size_t len = 8;
930 const void *buf = NULL;
932 if (vitem) {
933 len = vitem->data.length;
934 buf = vitem->data.data;
936 edit->buf = hexedit_new(edit, edit->dia->sub_window,
937 EDIT_DATA_HEIGHT_BUF,
938 EDIT_DATA_INPUT_Y,
939 EDIT_DATA_INPUT_X,
940 buf, len);
941 if (edit->buf == NULL) {
942 return WERR_NOMEM;
944 hexedit_refresh(edit->buf);
945 hexedit_set_cursor(edit->buf);
946 } else {
947 int val_rows = EDIT_DATA_HEIGHT_ONELINE;
949 if (type == REG_MULTI_SZ) {
950 val_rows = EDIT_DATA_HEIGHT_MULTILINE;
952 edit->field[FLD_DATA] = new_field(val_rows,
953 EDIT_DATA_INPUT_WIDTH,
954 EDIT_DATA_INPUT_Y,
955 EDIT_DATA_INPUT_X, 0, 0);
956 if (edit->field[FLD_DATA] == NULL) {
957 return WERR_NOMEM;
961 set_field_back(edit->field[FLD_NAME], A_REVERSE);
962 field_opts_off(edit->field[FLD_NAME], O_BLANK | O_AUTOSKIP | O_STATIC);
963 if (edit->field[FLD_DATA]) {
964 set_field_back(edit->field[FLD_DATA], A_REVERSE);
965 field_opts_off(edit->field[FLD_DATA],
966 O_BLANK | O_AUTOSKIP | O_STATIC | O_WRAP);
967 if (type == REG_DWORD) {
968 set_field_type(edit->field[FLD_DATA], TYPE_REGEXP,
969 "^ *([0-9]+|0[xX][0-9a-fA-F]+) *$");
973 if (vitem) {
974 set_field_buffer(edit->field[FLD_NAME], 0, vitem->value_name);
975 field_opts_off(edit->field[FLD_NAME], O_EDIT);
976 fill_value_buffer(edit, vitem);
979 edit->input = new_form(edit->field);
980 if (edit->input == NULL) {
981 return WERR_NOMEM;
983 form_opts_off(edit->input, O_NL_OVERLOAD | O_BS_OVERLOAD);
985 set_form_win(edit->input, edit->dia->sub_window);
986 set_form_sub(edit->input, edit->input_win);
987 set_current_field(edit->input, edit->field[FLD_NAME]);
988 post_form(edit->input);
990 return WERR_OK;
993 WERROR dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, uint32_t type,
994 const struct value_item *vitem)
996 struct edit_dialog *edit;
997 #define DIALOG_RESIZE 2
998 WERROR rv = WERR_NOMEM;
999 int selection;
1001 edit = talloc_zero(ctx, struct edit_dialog);
1002 if (edit == NULL) {
1003 return rv;
1005 talloc_set_destructor(edit, edit_dialog_free);
1007 rv = edit_init_dialog(edit, type);
1008 if (!W_ERROR_IS_OK(rv)) {
1009 goto finish;
1011 rv = edit_init_form(edit, type, vitem);
1012 if (!W_ERROR_IS_OK(rv)) {
1013 goto finish;
1016 update_panels();
1017 doupdate();
1019 edit->section = IN_NAME;
1021 while (1) {
1022 int c = dialog_getch(edit->dia);
1023 if (c == '\t') {
1024 section_down(edit);
1025 continue;
1026 } else if (c == KEY_BTAB) {
1027 section_up(edit);
1028 continue;
1031 if (edit->section == IN_NAME) {
1032 handle_form_input(edit->input, c);
1033 } else if (edit->section == IN_DATA) {
1034 if (edit->buf) {
1035 handle_hexedit_input(edit->buf, c);
1036 } else {
1037 handle_form_input(edit->input, c);
1039 } else {
1040 selection = handle_menu_input(edit->dia->choices, c);
1041 if (selection == DIALOG_OK) {
1042 rv = set_value(edit, key, type, vitem == NULL);
1043 if (W_ERROR_EQUAL(rv, WERR_FILE_EXISTS)) {
1044 dialog_notice(edit, DIA_ALERT,
1045 "Value exists",
1046 "Value name already exists.");
1047 selection = -1;
1048 } else {
1049 goto finish;
1051 } else if (selection == DIALOG_RESIZE) {
1052 char *n;
1053 size_t newlen = 0;
1055 dialog_input(edit, &n, "Resize buffer",
1056 "Enter new size");
1057 if (n) {
1058 newlen = strtoul(n, NULL, 10);
1059 hexedit_resize_buffer(edit->buf, newlen);
1060 hexedit_refresh(edit->buf);
1061 talloc_free(n);
1063 } else if (selection == DIALOG_CANCEL) {
1064 rv = WERR_OK;
1065 goto finish;
1069 update_panels();
1070 doupdate();
1073 finish:
1074 talloc_free(edit);
1076 return rv;
1079 int dialog_select_type(TALLOC_CTX *ctx, int *type)
1081 struct dialog *dia;
1082 const char *choices[] = {
1083 "OK",
1084 "Cancel",
1085 NULL
1087 const char *reg_types[] = {
1088 "REG_DWORD",
1089 "REG_SZ",
1090 "REG_EXPAND_SZ",
1091 "REG_MULTI_SZ",
1092 "REG_BINARY",
1094 #define NTYPES (sizeof(reg_types) / sizeof(const char*))
1095 ITEM **item;
1096 MENU *list;
1097 WINDOW *type_win;
1098 int sel = -1;
1099 size_t i;
1101 dia = dialog_choice_center_new(ctx, "New Value", choices, 10, 20);
1102 if (dia == NULL) {
1103 return -1;
1106 mvwprintw(dia->sub_window, 0, 0, "Choose type:");
1107 type_win = derwin(dia->sub_window, 6, 18, 1, 0);
1108 if (type_win == NULL) {
1109 goto finish;
1112 item = talloc_zero_array(dia, ITEM *, NTYPES + 1);
1113 if (item == NULL) {
1114 goto finish;
1117 for (i = 0; i < NTYPES; ++i) {
1118 int t = regtype_by_string(reg_types[i]);
1120 item[i] = new_item(reg_types[i], reg_types[i]);
1121 if (item[i] == NULL) {
1122 goto finish;
1124 set_item_userptr(item[i], (void*)(uintptr_t)t);
1127 list = new_menu(item);
1128 if (list == NULL) {
1129 goto finish;
1132 set_menu_format(list, 7, 1);
1133 set_menu_win(list, dia->sub_window);
1134 set_menu_sub(list, type_win);
1135 menu_opts_off(list, O_SHOWDESC);
1136 set_menu_mark(list, "* ");
1137 post_menu(list);
1139 update_panels();
1140 doupdate();
1142 while (sel == -1) {
1143 ITEM *it;
1144 int c = dialog_getch(dia);
1146 switch (c) {
1147 case KEY_UP:
1148 menu_driver(list, REQ_UP_ITEM);
1149 break;
1150 case KEY_DOWN:
1151 menu_driver(list, REQ_DOWN_ITEM);
1152 break;
1153 case KEY_LEFT:
1154 menu_driver(dia->choices, REQ_LEFT_ITEM);
1155 break;
1156 case KEY_RIGHT:
1157 menu_driver(dia->choices, REQ_RIGHT_ITEM);
1158 break;
1159 case '\n':
1160 case KEY_ENTER:
1161 it = current_item(list);
1162 *type = (int)(uintptr_t)item_userptr(it);
1163 it = current_item(dia->choices);
1164 sel = (int)(uintptr_t)item_userptr(it);
1165 break;
1168 update_panels();
1169 doupdate();
1172 finish:
1173 if (list) {
1174 unpost_menu(list);
1175 free_menu(list);
1177 if (item) {
1178 ITEM **it;
1179 for (it = item; *it; ++it) {
1180 free_item(*it);
1183 if (type_win) {
1184 delwin(type_win);
1186 talloc_free(dia);
1188 return sel;