diff options
Diffstat (limited to 'source3/utils/regedit_list.c')
-rw-r--r-- | source3/utils/regedit_list.c | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/source3/utils/regedit_list.c b/source3/utils/regedit_list.c new file mode 100644 index 0000000..b5405f2 --- /dev/null +++ b/source3/utils/regedit_list.c @@ -0,0 +1,591 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "regedit_list.h" +#include "regedit.h" + +struct multilist { + WINDOW *window; + WINDOW *pad; + + unsigned window_height; + unsigned window_width; + unsigned start_row; + unsigned cursor_row; + + unsigned ncols; + struct multilist_column *columns; + + const void *data; + unsigned nrows; + const void *current_row; + const struct multilist_accessors *cb; +}; + +/* data getters */ +static const void *data_get_first_row(struct multilist *list) +{ + SMB_ASSERT(list->cb->get_first_row); + return list->cb->get_first_row(list->data); +} + +static const void *data_get_next_row(struct multilist *list, const void *row) +{ + SMB_ASSERT(list->cb->get_next_row); + return list->cb->get_next_row(list->data, row); +} + +static const void *data_get_prev_row(struct multilist *list, const void *row) +{ + const void *tmp, *next; + + if (list->cb->get_prev_row) { + return list->cb->get_prev_row(list->data, row); + } + + tmp = data_get_first_row(list); + if (tmp == row) { + return NULL; + } + + for (; tmp && (next = data_get_next_row(list, tmp)) != row; + tmp = next) { + } + + SMB_ASSERT(tmp != NULL); + + return tmp; +} + +static unsigned data_get_row_count(struct multilist *list) +{ + unsigned i; + const void *row; + + if (list->cb->get_row_count) + return list->cb->get_row_count(list->data); + + for (i = 0, row = data_get_first_row(list); + row != NULL; + ++i, row = data_get_next_row(list, row)) { + } + + return i; +} + +static const void *data_get_row_n(struct multilist *list, size_t n) +{ + unsigned i; + const void *row; + + if (list->cb->get_row_n) + return list->cb->get_row_n(list->data, n); + + for (i = 0, row = data_get_first_row(list); + i < n && row != NULL; + ++i, row = data_get_next_row(list, row)) { + } + + return row; +} + +static const char *data_get_column_header(struct multilist *list, unsigned col) +{ + SMB_ASSERT(list->cb->get_column_header); + return list->cb->get_column_header(list->data, col); +} + +static const char *data_get_item_label(struct multilist *list, const void *row, + unsigned col) +{ + SMB_ASSERT(list->cb->get_item_label); + return list->cb->get_item_label(row, col); +} + +static const char *data_get_item_prefix(struct multilist *list, const void *row, + unsigned col) +{ + if (list->cb->get_item_prefix) + return list->cb->get_item_prefix(row, col); + return ""; +} + +static int multilist_free(struct multilist *list) +{ + if (list->pad) { + delwin(list->pad); + } + + return 0; +} + +struct multilist *multilist_new(TALLOC_CTX *ctx, WINDOW *window, + const struct multilist_accessors *cb, + unsigned ncol) +{ + struct multilist *list; + + SMB_ASSERT(ncol > 0); + + list = talloc_zero(ctx, struct multilist); + if (list == NULL) { + return NULL; + } + talloc_set_destructor(list, multilist_free); + + list->cb = cb; + list->ncols = ncol; + list->columns = talloc_zero_array(list, struct multilist_column, ncol); + if (list->columns == NULL) { + talloc_free(list); + return NULL; + } + multilist_set_window(list, window); + + return list; +} + +struct multilist_column *multilist_column_config(struct multilist *list, + unsigned col) +{ + SMB_ASSERT(col < list->ncols); + return &list->columns[col]; +} + +static void put_padding(WINDOW *win, size_t col_width, size_t item_len) +{ + size_t amt; + + SMB_ASSERT(item_len <= col_width); + + amt = col_width - item_len; + while (amt--) { + waddch(win, ' '); + } +} + +static void put_item(struct multilist *list, WINDOW *win, unsigned col, + const char *item, int attr) +{ + bool append_sep = true; + unsigned i; + size_t len; + struct multilist_column *col_info; + bool trim = false; + + SMB_ASSERT(col < list->ncols); + SMB_ASSERT(item != NULL); + + if (col == list->ncols - 1) { + append_sep = false; + } + col_info = &list->columns[col]; + + len = strlen(item); + if (len > col_info->width) { + len = col_info->width; + trim = true; + } + + if (col_info->align_right) { + put_padding(win, col_info->width, len); + } + for (i = 0; i < len; ++i) { + if (i == len - 1 && trim) { + waddch(win, '~' | attr); + } else { + waddch(win, item[i] | attr); + } + } + if (!col_info->align_right) { + put_padding(win, col_info->width, len); + } + + if (append_sep) { + waddch(win, ' '); + waddch(win, '|'); + waddch(win, ' '); + } +} + +static void put_header(struct multilist *list) +{ + unsigned col; + const char *header; + + if (!list->cb->get_column_header) { + return; + } + + wmove(list->window, 0, 0); + for (col = 0; col < list->ncols; ++col) { + header = data_get_column_header(list, col); + SMB_ASSERT(header != NULL); + put_item(list, list->window, col, header, + A_BOLD | COLOR_PAIR(PAIR_YELLOW_BLUE)); + } +} + +static WERROR put_data(struct multilist *list) +{ + const void *row; + int ypos; + unsigned col; + const char *prefix, *item; + char *tmp; + + for (ypos = 0, row = data_get_first_row(list); + row != NULL; + row = data_get_next_row(list, row), ++ypos) { + wmove(list->pad, ypos, 0); + for (col = 0; col < list->ncols; ++col) { + prefix = data_get_item_prefix(list, row, col); + SMB_ASSERT(prefix != NULL); + item = data_get_item_label(list, row, col); + SMB_ASSERT(item != NULL); + tmp = talloc_asprintf(list, "%s%s", prefix, item); + if (tmp == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + put_item(list, list->pad, col, tmp, 0); + talloc_free(tmp); + } + } + + return WERR_OK; +} + +#define MIN_WIDTH 3 +static struct multilist_column *find_widest_column(struct multilist *list) +{ + unsigned col; + struct multilist_column *colp; + + SMB_ASSERT(list->ncols > 0); + colp = &list->columns[0]; + + for (col = 1; col < list->ncols; ++col) { + if (list->columns[col].width > colp->width) { + colp = &list->columns[col]; + } + } + + if (colp->width < MIN_WIDTH) { + return NULL; + } + + return colp; +} + +static WERROR calc_column_widths(struct multilist *list) +{ + const void *row; + unsigned col; + size_t len; + const char *item; + size_t width, total_width, overflow; + struct multilist_column *colp; + + /* calculate the maximum widths for each column */ + for (col = 0; col < list->ncols; ++col) { + len = 0; + if (list->cb->get_column_header) { + item = data_get_column_header(list, col); + len = strlen(item); + } + list->columns[col].width = len; + } + + for (row = data_get_first_row(list); + row != NULL; + row = data_get_next_row(list, row)) { + for (col = 0; col < list->ncols; ++col) { + item = data_get_item_prefix(list, row, col); + SMB_ASSERT(item != NULL); + len = strlen(item); + + item = data_get_item_label(list, row, col); + SMB_ASSERT(item != NULL); + len += strlen(item); + if (len > list->columns[col].width) { + list->columns[col].width = len; + } + } + } + + /* calculate row width */ + for (width = 0, col = 0; col < list->ncols; ++col) { + width += list->columns[col].width; + } + /* width including column spacing and separations */ + total_width = width + (list->ncols - 1) * 3; + /* if everything fits, we're done */ + if (total_width <= list->window_width) { + return WERR_OK; + } + + overflow = total_width - list->window_width; + + /* attempt to trim as much as possible to fit all the columns to + the window */ + while (overflow && (colp = find_widest_column(list))) { + colp->width--; + overflow--; + } + + return WERR_OK; +} + +static void highlight_current_row(struct multilist *list) +{ + mvwchgat(list->pad, list->cursor_row, 0, -1, A_REVERSE, 0, NULL); +} + +static void unhighlight_current_row(struct multilist *list) +{ + mvwchgat(list->pad, list->cursor_row, 0, -1, A_NORMAL, 0, NULL); +} + +const void *multilist_get_data(struct multilist *list) +{ + return list->data; +} + +WERROR multilist_set_data(struct multilist *list, const void *data) +{ + WERROR rv; + + SMB_ASSERT(list->window != NULL); + list->data = data; + + calc_column_widths(list); + + if (list->pad) { + delwin(list->pad); + } + /* construct a pad that is exactly the width of the window, and + as tall as required to fit all data rows. */ + list->nrows = data_get_row_count(list); + list->pad = newpad(MAX(list->nrows, 1), list->window_width); + if (list->pad == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* add the column headers to the window and render all rows to + the pad. */ + werase(list->window); + put_header(list); + rv = put_data(list); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + + /* initialize the cursor */ + list->start_row = 0; + list->cursor_row = 0; + list->current_row = data_get_first_row(list); + highlight_current_row(list); + + return WERR_OK; +} + +static int get_window_height(struct multilist *list) +{ + int height; + + height = list->window_height; + if (list->cb->get_column_header) { + height--; + } + + return height; +} + +static void fix_start_row(struct multilist *list) +{ + int height; + + /* adjust start_row so that the cursor appears on the screen */ + + height = get_window_height(list); + if (list->cursor_row < list->start_row) { + list->start_row = list->cursor_row; + } else if (list->cursor_row >= list->start_row + height) { + list->start_row = list->cursor_row - height + 1; + } + if (list->nrows > height && list->nrows - list->start_row < height) { + list->start_row = list->nrows - height; + } +} + +WERROR multilist_set_window(struct multilist *list, WINDOW *window) +{ + int maxy, maxx; + bool rerender = false; + + getmaxyx(window, maxy, maxx); + + /* rerender pad if window width is different. */ + if (list->data && maxx != list->window_width) { + rerender = true; + } + + list->window = window; + list->window_width = maxx; + list->window_height = maxy; + list->start_row = 0; + if (rerender) { + const void *row = multilist_get_current_row(list); + WERROR rv = multilist_set_data(list, list->data); + if (W_ERROR_IS_OK(rv) && row) { + multilist_set_current_row(list, row); + } + return rv; + } else { + put_header(list); + fix_start_row(list); + } + + return WERR_OK; +} + +void multilist_refresh(struct multilist *list) +{ + int window_start_row, height; + + if (list->nrows == 0) { + return; + } + + /* copy from pad, starting at start_row, to the window, accounting + for the column header (if present). */ + height = MIN(list->window_height, list->nrows); + window_start_row = 0; + if (list->cb->get_column_header) { + window_start_row = 1; + if (height < list->window_height) { + height++; + } + } + copywin(list->pad, list->window, list->start_row, 0, + window_start_row, 0, height - 1, list->window_width - 1, + false); +} + +void multilist_driver(struct multilist *list, int c) +{ + unsigned page; + const void *tmp = NULL; + + if (list->nrows == 0) { + return; + } + + switch (c) { + case ML_CURSOR_UP: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + list->cursor_row--; + tmp = data_get_prev_row(list, list->current_row); + break; + case ML_CURSOR_DOWN: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + list->cursor_row++; + tmp = data_get_next_row(list, list->current_row); + break; + case ML_CURSOR_PGUP: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + page = get_window_height(list); + if (page > list->cursor_row) { + list->cursor_row = 0; + } else { + list->cursor_row -= page; + list->start_row -= page; + } + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_PGDN: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + page = get_window_height(list); + if (page > list->nrows - list->cursor_row - 1) { + list->cursor_row = list->nrows - 1; + } else { + list->cursor_row += page; + list->start_row += page; + } + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_HOME: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + list->cursor_row = 0; + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_END: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + list->cursor_row = list->nrows - 1; + tmp = data_get_row_n(list, list->cursor_row); + break; + } + + SMB_ASSERT(tmp); + list->current_row = tmp; + highlight_current_row(list); + fix_start_row(list); +} + +const void *multilist_get_current_row(struct multilist *list) +{ + return list->current_row; +} + +void multilist_set_current_row(struct multilist *list, const void *row) +{ + unsigned i; + const void *tmp; + + for (i = 0, tmp = data_get_first_row(list); + tmp != NULL; + ++i, tmp = data_get_next_row(list, tmp)) { + if (tmp == row) { + unhighlight_current_row(list); + list->cursor_row = i; + list->current_row = row; + highlight_current_row(list); + fix_start_row(list); + return; + } + } +} |