summaryrefslogtreecommitdiffstats
path: root/source3/utils/regedit_list.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/utils/regedit_list.c')
-rw-r--r--source3/utils/regedit_list.c591
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;
+ }
+ }
+}