s4-tests: Print out what the error is in delete_force()
[Samba.git] / source3 / utils / regedit_list.c
blobc2b370528483604d50274b907f90b72031b10fe7
1 /*
2 * Samba Unix/Linux SMB client library
3 * Registry Editor
4 * Copyright (C) Christopher Davis 2014
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 "regedit_list.h"
21 #include "regedit.h"
23 struct multilist {
24 WINDOW *window;
25 WINDOW *pad;
27 unsigned window_height;
28 unsigned window_width;
29 unsigned start_row;
30 unsigned cursor_row;
32 unsigned ncols;
33 struct multilist_column *columns;
35 const void *data;
36 unsigned nrows;
37 const void *current_row;
38 const struct multilist_accessors *cb;
41 /* data getters */
42 static const void *data_get_first_row(struct multilist *list)
44 SMB_ASSERT(list->cb->get_first_row);
45 return list->cb->get_first_row(list->data);
48 static const void *data_get_next_row(struct multilist *list, const void *row)
50 SMB_ASSERT(list->cb->get_next_row);
51 return list->cb->get_next_row(list->data, row);
54 static const void *data_get_prev_row(struct multilist *list, const void *row)
56 const void *tmp, *next;
58 if (list->cb->get_prev_row) {
59 return list->cb->get_prev_row(list->data, row);
62 tmp = data_get_first_row(list);
63 if (tmp == row) {
64 return NULL;
67 for (; tmp && (next = data_get_next_row(list, tmp)) != row;
68 tmp = next) {
71 SMB_ASSERT(tmp != NULL);
73 return tmp;
76 static unsigned data_get_row_count(struct multilist *list)
78 unsigned i;
79 const void *row;
81 if (list->cb->get_row_count)
82 return list->cb->get_row_count(list->data);
84 for (i = 0, row = data_get_first_row(list);
85 row != NULL;
86 ++i, row = data_get_next_row(list, row)) {
89 return i;
92 static const void *data_get_row_n(struct multilist *list, size_t n)
94 unsigned i;
95 const void *row;
97 if (list->cb->get_row_n)
98 return list->cb->get_row_n(list->data, n);
100 for (i = 0, row = data_get_first_row(list);
101 i < n && row != NULL;
102 ++i, row = data_get_next_row(list, row)) {
105 return row;
108 static const char *data_get_column_header(struct multilist *list, unsigned col)
110 SMB_ASSERT(list->cb->get_column_header);
111 return list->cb->get_column_header(list->data, col);
114 static const char *data_get_item_label(struct multilist *list, const void *row,
115 unsigned col)
117 SMB_ASSERT(list->cb->get_item_label);
118 return list->cb->get_item_label(row, col);
121 static const char *data_get_item_prefix(struct multilist *list, const void *row,
122 unsigned col)
124 if (list->cb->get_item_prefix)
125 return list->cb->get_item_prefix(row, col);
126 return "";
129 static int multilist_free(struct multilist *list)
131 if (list->pad) {
132 delwin(list->pad);
135 return 0;
138 struct multilist *multilist_new(TALLOC_CTX *ctx, WINDOW *window,
139 const struct multilist_accessors *cb,
140 unsigned ncol)
142 struct multilist *list;
144 SMB_ASSERT(ncol > 0);
146 list = talloc_zero(ctx, struct multilist);
147 if (list == NULL) {
148 return NULL;
150 talloc_set_destructor(list, multilist_free);
152 list->cb = cb;
153 list->ncols = ncol;
154 list->columns = talloc_zero_array(list, struct multilist_column, ncol);
155 if (list->columns == NULL) {
156 talloc_free(list);
157 return NULL;
159 multilist_set_window(list, window);
161 return list;
164 struct multilist_column *multilist_column_config(struct multilist *list,
165 unsigned col)
167 SMB_ASSERT(col < list->ncols);
168 return &list->columns[col];
171 static void put_padding(WINDOW *win, size_t col_width, size_t item_len)
173 size_t amt;
175 SMB_ASSERT(item_len <= col_width);
177 amt = col_width - item_len;
178 while (amt--) {
179 waddch(win, ' ');
183 static void put_item(struct multilist *list, WINDOW *win, unsigned col,
184 const char *item, int attr)
186 bool append_sep = true;
187 unsigned i;
188 size_t len;
189 struct multilist_column *col_info;
190 bool trim = false;
192 SMB_ASSERT(col < list->ncols);
193 SMB_ASSERT(item != NULL);
195 if (col == list->ncols - 1) {
196 append_sep = false;
198 col_info = &list->columns[col];
200 len = strlen(item);
201 if (len > col_info->width) {
202 len = col_info->width;
203 trim = true;
206 if (col_info->align_right) {
207 put_padding(win, col_info->width, len);
209 for (i = 0; i < len; ++i) {
210 if (i == len - 1 && trim) {
211 waddch(win, '~' | attr);
212 } else {
213 waddch(win, item[i] | attr);
216 if (!col_info->align_right) {
217 put_padding(win, col_info->width, len);
220 if (append_sep) {
221 waddch(win, ' ');
222 waddch(win, '|');
223 waddch(win, ' ');
227 static void put_header(struct multilist *list)
229 unsigned col;
230 const char *header;
232 if (!list->cb->get_column_header) {
233 return;
236 wmove(list->window, 0, 0);
237 for (col = 0; col < list->ncols; ++col) {
238 header = data_get_column_header(list, col);
239 SMB_ASSERT(header != NULL);
240 put_item(list, list->window, col, header,
241 A_BOLD | COLOR_PAIR(PAIR_YELLOW_BLUE));
245 static WERROR put_data(struct multilist *list)
247 const void *row;
248 int ypos;
249 unsigned col;
250 const char *prefix, *item;
251 char *tmp;
253 for (ypos = 0, row = data_get_first_row(list);
254 row != NULL;
255 row = data_get_next_row(list, row), ++ypos) {
256 wmove(list->pad, ypos, 0);
257 for (col = 0; col < list->ncols; ++col) {
258 prefix = data_get_item_prefix(list, row, col);
259 SMB_ASSERT(prefix != NULL);
260 item = data_get_item_label(list, row, col);
261 SMB_ASSERT(item != NULL);
262 tmp = talloc_asprintf(list, "%s%s", prefix, item);
263 if (tmp == NULL) {
264 return WERR_NOMEM;
266 put_item(list, list->pad, col, tmp, 0);
267 talloc_free(tmp);
271 return WERR_OK;
274 #define MIN_WIDTH 3
275 static struct multilist_column *find_widest_column(struct multilist *list)
277 unsigned col;
278 struct multilist_column *colp;
280 SMB_ASSERT(list->ncols > 0);
281 colp = &list->columns[0];
283 for (col = 1; col < list->ncols; ++col) {
284 if (list->columns[col].width > colp->width) {
285 colp = &list->columns[col];
289 if (colp->width < MIN_WIDTH) {
290 return NULL;
293 return colp;
296 static WERROR calc_column_widths(struct multilist *list)
298 const void *row;
299 unsigned col;
300 size_t len;
301 const char *item;
302 size_t width, total_width, overflow;
303 struct multilist_column *colp;
305 /* calculate the maximum widths for each column */
306 for (col = 0; col < list->ncols; ++col) {
307 len = 0;
308 if (list->cb->get_column_header) {
309 item = data_get_column_header(list, col);
310 len = strlen(item);
312 list->columns[col].width = len;
315 for (row = data_get_first_row(list);
316 row != NULL;
317 row = data_get_next_row(list, row)) {
318 for (col = 0; col < list->ncols; ++col) {
319 item = data_get_item_prefix(list, row, col);
320 SMB_ASSERT(item != NULL);
321 len = strlen(item);
323 item = data_get_item_label(list, row, col);
324 SMB_ASSERT(item != NULL);
325 len += strlen(item);
326 if (len > list->columns[col].width) {
327 list->columns[col].width = len;
332 /* calculate row width */
333 for (width = 0, col = 0; col < list->ncols; ++col) {
334 width += list->columns[col].width;
336 /* width including column spacing and separations */
337 total_width = width + (list->ncols - 1) * 3;
338 /* if everything fits, we're done */
339 if (total_width <= list->window_width) {
340 return WERR_OK;
343 overflow = total_width - list->window_width;
345 /* attempt to trim as much as possible to fit all the columns to
346 the window */
347 while (overflow && (colp = find_widest_column(list))) {
348 colp->width--;
349 overflow--;
352 return WERR_OK;
355 static void highlight_current_row(struct multilist *list)
357 mvwchgat(list->pad, list->cursor_row, 0, -1, A_REVERSE, 0, NULL);
360 static void unhighlight_current_row(struct multilist *list)
362 mvwchgat(list->pad, list->cursor_row, 0, -1, A_NORMAL, 0, NULL);
365 const void *multilist_get_data(struct multilist *list)
367 return list->data;
370 WERROR multilist_set_data(struct multilist *list, const void *data)
372 WERROR rv;
374 SMB_ASSERT(list->window != NULL);
375 list->data = data;
377 calc_column_widths(list);
379 if (list->pad) {
380 delwin(list->pad);
382 /* construct a pad that is exactly the width of the window, and
383 as tall as required to fit all data rows. */
384 list->nrows = data_get_row_count(list);
385 list->pad = newpad(MAX(list->nrows, 1), list->window_width);
386 if (list->pad == NULL) {
387 return WERR_NOMEM;
390 /* add the column headers to the window and render all rows to
391 the pad. */
392 werase(list->window);
393 put_header(list);
394 rv = put_data(list);
395 if (!W_ERROR_IS_OK(rv)) {
396 return rv;
399 /* initialize the cursor */
400 list->start_row = 0;
401 list->cursor_row = 0;
402 list->current_row = data_get_first_row(list);
403 highlight_current_row(list);
405 return WERR_OK;
408 static int get_window_height(struct multilist *list)
410 int height;
412 height = list->window_height;
413 if (list->cb->get_column_header) {
414 height--;
417 return height;
420 static void fix_start_row(struct multilist *list)
422 int height;
424 /* adjust start_row so that the cursor appears on the screen */
426 height = get_window_height(list);
427 if (list->cursor_row < list->start_row) {
428 list->start_row = list->cursor_row;
429 } else if (list->cursor_row >= list->start_row + height) {
430 list->start_row = list->cursor_row - height + 1;
432 if (list->nrows > height && list->nrows - list->start_row < height) {
433 list->start_row = list->nrows - height;
437 WERROR multilist_set_window(struct multilist *list, WINDOW *window)
439 int maxy, maxx;
440 bool rerender = false;
442 getmaxyx(window, maxy, maxx);
444 /* rerender pad if window width is different. */
445 if (list->data && maxx != list->window_width) {
446 rerender = true;
449 list->window = window;
450 list->window_width = maxx;
451 list->window_height = maxy;
452 list->start_row = 0;
453 if (rerender) {
454 const void *row = multilist_get_current_row(list);
455 WERROR rv = multilist_set_data(list, list->data);
456 if (W_ERROR_IS_OK(rv) && row) {
457 multilist_set_current_row(list, row);
459 return rv;
460 } else {
461 put_header(list);
462 fix_start_row(list);
465 return WERR_OK;
468 void multilist_refresh(struct multilist *list)
470 int window_start_row, height;
472 if (list->nrows == 0) {
473 return;
476 /* copy from pad, starting at start_row, to the window, accounting
477 for the column header (if present). */
478 height = MIN(list->window_height, list->nrows);
479 window_start_row = 0;
480 if (list->cb->get_column_header) {
481 window_start_row = 1;
482 if (height < list->window_height) {
483 height++;
486 copywin(list->pad, list->window, list->start_row, 0,
487 window_start_row, 0, height - 1, list->window_width - 1,
488 false);
491 void multilist_driver(struct multilist *list, int c)
493 unsigned page;
494 const void *tmp;
496 if (list->nrows == 0) {
497 return;
500 switch (c) {
501 case ML_CURSOR_UP:
502 if (list->cursor_row == 0) {
503 return;
505 unhighlight_current_row(list);
506 list->cursor_row--;
507 tmp = data_get_prev_row(list, list->current_row);
508 break;
509 case ML_CURSOR_DOWN:
510 if (list->cursor_row == list->nrows - 1) {
511 return;
513 unhighlight_current_row(list);
514 list->cursor_row++;
515 tmp = data_get_next_row(list, list->current_row);
516 break;
517 case ML_CURSOR_PGUP:
518 if (list->cursor_row == 0) {
519 return;
521 unhighlight_current_row(list);
522 page = get_window_height(list);
523 if (page > list->cursor_row) {
524 list->cursor_row = 0;
525 } else {
526 list->cursor_row -= page;
527 list->start_row -= page;
529 tmp = data_get_row_n(list, list->cursor_row);
530 break;
531 case ML_CURSOR_PGDN:
532 if (list->cursor_row == list->nrows - 1) {
533 return;
535 unhighlight_current_row(list);
536 page = get_window_height(list);
537 if (page > list->nrows - list->cursor_row - 1) {
538 list->cursor_row = list->nrows - 1;
539 } else {
540 list->cursor_row += page;
541 list->start_row += page;
543 tmp = data_get_row_n(list, list->cursor_row);
544 break;
545 case ML_CURSOR_HOME:
546 if (list->cursor_row == 0) {
547 return;
549 unhighlight_current_row(list);
550 list->cursor_row = 0;
551 tmp = data_get_row_n(list, list->cursor_row);
552 break;
553 case ML_CURSOR_END:
554 if (list->cursor_row == list->nrows - 1) {
555 return;
557 unhighlight_current_row(list);
558 list->cursor_row = list->nrows - 1;
559 tmp = data_get_row_n(list, list->cursor_row);
560 break;
563 SMB_ASSERT(tmp);
564 list->current_row = tmp;
565 highlight_current_row(list);
566 fix_start_row(list);
569 const void *multilist_get_current_row(struct multilist *list)
571 return list->current_row;
574 void multilist_set_current_row(struct multilist *list, const void *row)
576 unsigned i;
577 const void *tmp;
579 for (i = 0, tmp = data_get_first_row(list);
580 tmp != NULL;
581 ++i, tmp = data_get_next_row(list, tmp)) {
582 if (tmp == row) {
583 unhighlight_current_row(list);
584 list->cursor_row = i;
585 list->current_row = row;
586 highlight_current_row(list);
587 fix_start_row(list);
588 return;