2 * Samba Unix/Linux SMB client library
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"
27 unsigned window_height
;
28 unsigned window_width
;
33 struct multilist_column
*columns
;
37 const void *current_row
;
38 const struct multilist_accessors
*cb
;
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
);
67 for (; tmp
&& (next
= data_get_next_row(list
, tmp
)) != row
;
71 SMB_ASSERT(tmp
!= NULL
);
76 static unsigned data_get_row_count(struct multilist
*list
)
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
);
86 ++i
, row
= data_get_next_row(list
, row
)) {
92 static const void *data_get_row_n(struct multilist
*list
, size_t n
)
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
)) {
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
,
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
,
124 if (list
->cb
->get_item_prefix
)
125 return list
->cb
->get_item_prefix(row
, col
);
129 static int multilist_free(struct multilist
*list
)
138 struct multilist
*multilist_new(TALLOC_CTX
*ctx
, WINDOW
*window
,
139 const struct multilist_accessors
*cb
,
142 struct multilist
*list
;
144 SMB_ASSERT(ncol
> 0);
146 list
= talloc_zero(ctx
, struct multilist
);
150 talloc_set_destructor(list
, multilist_free
);
154 list
->columns
= talloc_zero_array(list
, struct multilist_column
, ncol
);
155 if (list
->columns
== NULL
) {
159 multilist_set_window(list
, window
);
164 struct multilist_column
*multilist_column_config(struct multilist
*list
,
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
)
175 SMB_ASSERT(item_len
<= col_width
);
177 amt
= col_width
- item_len
;
183 static void put_item(struct multilist
*list
, WINDOW
*win
, unsigned col
,
184 const char *item
, int attr
)
186 bool append_sep
= true;
189 struct multilist_column
*col_info
;
192 SMB_ASSERT(col
< list
->ncols
);
193 SMB_ASSERT(item
!= NULL
);
195 if (col
== list
->ncols
- 1) {
198 col_info
= &list
->columns
[col
];
201 if (len
> col_info
->width
) {
202 len
= col_info
->width
;
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
);
213 waddch(win
, item
[i
] | attr
);
216 if (!col_info
->align_right
) {
217 put_padding(win
, col_info
->width
, len
);
227 static void put_header(struct multilist
*list
)
232 if (!list
->cb
->get_column_header
) {
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
)
250 const char *prefix
, *item
;
253 for (ypos
= 0, row
= data_get_first_row(list
);
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
);
266 put_item(list
, list
->pad
, col
, tmp
, 0);
275 static struct multilist_column
*find_widest_column(struct multilist
*list
)
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
) {
296 static WERROR
calc_column_widths(struct multilist
*list
)
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
) {
308 if (list
->cb
->get_column_header
) {
309 item
= data_get_column_header(list
, col
);
312 list
->columns
[col
].width
= len
;
315 for (row
= data_get_first_row(list
);
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
);
323 item
= data_get_item_label(list
, row
, col
);
324 SMB_ASSERT(item
!= NULL
);
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
) {
343 overflow
= total_width
- list
->window_width
;
345 /* attempt to trim as much as possible to fit all the columns to
347 while (overflow
&& (colp
= find_widest_column(list
))) {
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
)
370 WERROR
multilist_set_data(struct multilist
*list
, const void *data
)
374 SMB_ASSERT(list
->window
!= NULL
);
377 calc_column_widths(list
);
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
) {
390 /* add the column headers to the window and render all rows to
392 werase(list
->window
);
395 if (!W_ERROR_IS_OK(rv
)) {
399 /* initialize the cursor */
401 list
->cursor_row
= 0;
402 list
->current_row
= data_get_first_row(list
);
403 highlight_current_row(list
);
408 static int get_window_height(struct multilist
*list
)
412 height
= list
->window_height
;
413 if (list
->cb
->get_column_header
) {
420 static void fix_start_row(struct multilist
*list
)
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
)
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
) {
449 list
->window
= window
;
450 list
->window_width
= maxx
;
451 list
->window_height
= maxy
;
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
);
468 void multilist_refresh(struct multilist
*list
)
470 int window_start_row
, height
;
472 if (list
->nrows
== 0) {
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
) {
486 copywin(list
->pad
, list
->window
, list
->start_row
, 0,
487 window_start_row
, 0, height
- 1, list
->window_width
- 1,
491 void multilist_driver(struct multilist
*list
, int c
)
494 const void *tmp
= NULL
;
496 if (list
->nrows
== 0) {
502 if (list
->cursor_row
== 0) {
505 unhighlight_current_row(list
);
507 tmp
= data_get_prev_row(list
, list
->current_row
);
510 if (list
->cursor_row
== list
->nrows
- 1) {
513 unhighlight_current_row(list
);
515 tmp
= data_get_next_row(list
, list
->current_row
);
518 if (list
->cursor_row
== 0) {
521 unhighlight_current_row(list
);
522 page
= get_window_height(list
);
523 if (page
> list
->cursor_row
) {
524 list
->cursor_row
= 0;
526 list
->cursor_row
-= page
;
527 list
->start_row
-= page
;
529 tmp
= data_get_row_n(list
, list
->cursor_row
);
532 if (list
->cursor_row
== list
->nrows
- 1) {
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;
540 list
->cursor_row
+= page
;
541 list
->start_row
+= page
;
543 tmp
= data_get_row_n(list
, list
->cursor_row
);
546 if (list
->cursor_row
== 0) {
549 unhighlight_current_row(list
);
550 list
->cursor_row
= 0;
551 tmp
= data_get_row_n(list
, list
->cursor_row
);
554 if (list
->cursor_row
== list
->nrows
- 1) {
557 unhighlight_current_row(list
);
558 list
->cursor_row
= list
->nrows
- 1;
559 tmp
= data_get_row_n(list
, list
->cursor_row
);
564 list
->current_row
= tmp
;
565 highlight_current_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
)
579 for (i
= 0, tmp
= data_get_first_row(list
);
581 ++i
, tmp
= data_get_next_row(list
, tmp
)) {
583 unhighlight_current_row(list
);
584 list
->cursor_row
= i
;
585 list
->current_row
= row
;
586 highlight_current_row(list
);