2 * $Id: guage.c,v 1.85 2022/04/03 22:38:16 tom Exp $
4 * guage.c -- implements the gauge dialog
6 * Copyright 2000-2021,2022 Thomas E. Dickey
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
23 * An earlier version of this program lists as authors
24 * Marc Ewing, Red Hat Software
27 #include <dlg_internals.h>
29 #define MY_LEN (MAX_LEN)/2
32 #define MIN_WIDE (10 + 2 * (2 + MARGIN))
34 #define isMarker(buf) !strncmp(buf, "XXX", (size_t) 3)
36 typedef struct _my_obj
{
37 DIALOG_CALLBACK obj
; /* has to be first in struct */
42 char prompt_buf
[MY_LEN
];
46 char line
[MAX_LEN
+ 1];
49 static MY_OBJ
*all_objects
;
54 MY_OBJ
*list
= all_objects
;
70 MY_OBJ
*p
= all_objects
;
77 all_objects
= p
->next
;
87 read_data(char *buffer
, FILE *fp
)
93 } else if (fgets(buffer
, MY_LEN
, fp
) != 0) {
94 DLG_TRACE(("read_data:%s", buffer
));
95 buffer
[MY_LEN
] = '\0';
96 dlg_trim_string(buffer
);
105 decode_percent(char *buffer
)
108 long value
= strtol(buffer
, &tmp
, 10);
110 if (tmp
!= 0 && (*tmp
== 0 || isspace(UCH(*tmp
))) && value
>= 0) {
117 repaint_text(MY_OBJ
* obj
)
119 WINDOW
*dialog
= obj
->obj
.win
;
124 (void) werase(dialog
);
125 dlg_draw_box2(dialog
, 0, 0, obj
->height
, obj
->width
, dialog_attr
,
126 border_attr
, border2_attr
);
128 dlg_draw_title(dialog
, obj
->title
);
130 dlg_attrset(dialog
, dialog_attr
);
131 dlg_draw_helpline(dialog
, FALSE
);
132 dlg_print_autowrap(dialog
, obj
->prompt
, obj
->height
, obj
->width
);
134 dlg_draw_box2(dialog
,
135 obj
->height
- 4, 2 + MARGIN
,
136 2 + MARGIN
, obj
->width
- 2 * (2 + MARGIN
),
142 * Clear the area for the progress bar by filling it with spaces
143 * in the gauge-attribute, and write the percentage with that
146 (void) wmove(dialog
, obj
->height
- 3, 4);
147 dlg_attrset(dialog
, gauge_attr
);
149 for (i
= 0; i
< (obj
->width
- 2 * (3 + MARGIN
)); i
++)
150 (void) waddch(dialog
, ' ');
152 (void) wmove(dialog
, obj
->height
- 3, (obj
->width
/ 2) - 2);
153 (void) wprintw(dialog
, "%3d%%", obj
->percent
);
156 * Now draw a bar in reverse, relative to the background.
157 * The window attribute was useful for painting the background,
158 * but requires some tweaks to reverse it.
160 x
= (obj
->percent
* (obj
->width
- 2 * (3 + MARGIN
))) / 100;
161 if ((gauge_attr
& A_REVERSE
) != 0) {
162 dlg_attroff(dialog
, A_REVERSE
);
164 dlg_attrset(dialog
, A_REVERSE
);
166 (void) wmove(dialog
, obj
->height
- 3, 4);
167 for (i
= 0; i
< x
; i
++) {
168 chtype ch2
= winch(dialog
);
169 if (gauge_attr
& A_REVERSE
) {
172 (void) waddch(dialog
, ch2
);
175 (void) wrefresh(dialog
);
180 handle_input(DIALOG_CALLBACK
* cb
)
182 MY_OBJ
*obj
= (MY_OBJ
*) cb
;
183 FILE *my_input
= ((obj
!= NULL
&& obj
->obj
.input
!= NULL
)
185 : dialog_state
.pipe_input
);
187 bool cleanup
= FALSE
;
189 char buf
[MY_LEN
+ 1];
191 if (my_input
== NULL
) {
194 } else if ((status
= read_data(buf
, my_input
)) > 0) {
198 * Historically, next line should be percentage, but one of the
199 * worse-written clones of 'dialog' assumes the number is missing.
200 * (Gresham's Law applied to software).
202 if ((status
= read_data(buf
, my_input
)) > 0) {
204 obj
->prompt_buf
[0] = '\0';
205 if (decode_percent(buf
))
206 obj
->percent
= atoi(buf
);
208 strcpy(obj
->prompt_buf
, buf
);
210 /* Rest is message text */
211 while ((status
= read_data(buf
, my_input
)) > 0
213 if (strlen(obj
->prompt_buf
) + strlen(buf
) <
214 sizeof(obj
->prompt_buf
) - 1) {
215 strcat(obj
->prompt_buf
, buf
);
219 if (obj
->prompt
!= obj
->prompt_buf
)
221 obj
->prompt
= obj
->prompt_buf
;
223 } else if (decode_percent(buf
)) {
224 obj
->percent
= atoi(buf
);
227 if (feof(my_input
) ||
228 (ferror(my_input
) && errno
!= EINTR
)) {
239 dlg_remove_callback(cb
);
248 handle_my_getc(DIALOG_CALLBACK
* cb
, int ch
, int fkey
, int *result
)
252 *result
= DLG_EXIT_OK
;
254 if (!fkey
&& (ch
== ERR
)) {
255 (void) handle_input(cb
);
256 /* cb might be freed in handle_input */
257 status
= (valid((MY_OBJ
*) cb
) && (cb
->input
!= 0));
266 my_cleanup(DIALOG_CALLBACK
* cb
)
268 MY_OBJ
*obj
= (MY_OBJ
*) cb
;
271 if (obj
->prompt
!= obj
->prompt_buf
) {
273 obj
->prompt
= obj
->prompt_buf
;
276 dlg_del_window(obj
->obj
.win
);
282 dlg_update_gauge(void *objptr
, int percent
)
284 MY_OBJ
*obj
= (MY_OBJ
*) objptr
;
285 bool save_finish_string
= dialog_state
.finish_string
;
287 dialog_state
.finish_string
= TRUE
;
289 obj
->percent
= percent
;
291 dialog_state
.finish_string
= save_finish_string
;
295 * (Re)Allocates an object and fills it as per the arguments
298 dlg_reallocate_gauge(void *objptr
,
305 char *prompt
= dlg_strclone(cprompt
);
307 bool save_finish_string
= dialog_state
.finish_string
;
309 dialog_state
.finish_string
= TRUE
;
310 dlg_tab_correct_str(prompt
);
313 /* create a new object */
314 obj
= dlg_calloc(MY_OBJ
, 1);
315 assert_ptr(obj
, "dialog_gauge");
317 dlg_auto_size(title
, prompt
, &height
, &width
, MIN_HIGH
, MIN_WIDE
);
318 dlg_print_size(height
, width
);
319 dlg_ctl_size(height
, width
);
322 /* reuse an existing object */
324 height
= obj
->height
;
328 if (obj
->obj
.win
== 0) {
329 /* center dialog box on screen */
330 int x
= dlg_box_x_ordinate(width
);
331 int y
= dlg_box_y_ordinate(height
);
332 WINDOW
*dialog
= dlg_new_window(height
, width
, y
, x
);
333 obj
->obj
.win
= dialog
;
336 obj
->obj
.input
= dialog_state
.pipe_input
;
337 obj
->obj
.keep_win
= TRUE
;
338 obj
->obj
.bg_task
= TRUE
;
339 obj
->obj
.handle_getc
= handle_my_getc
;
340 obj
->obj
.handle_input
= handle_input
;
342 if (obj
->title
== 0 || strcmp(obj
->title
, title
)) {
343 dlg_finish_string(obj
->title
);
345 obj
->title
= dlg_strclone(title
);
348 dlg_finish_string(obj
->prompt
);
351 obj
->prompt
= prompt
;
352 obj
->percent
= percent
;
353 obj
->height
= height
;
356 /* if this was a new object, link it into the list */
358 obj
->next
= all_objects
;
362 dialog_state
.finish_string
= save_finish_string
;
367 dlg_allocate_gauge(const char *title
,
373 return dlg_reallocate_gauge(NULL
, title
, cprompt
, height
, width
, percent
);
377 dlg_free_gauge(void *objptr
)
379 MY_OBJ
*obj
= (MY_OBJ
*) objptr
;
386 obj
->obj
.keep_win
= FALSE
;
387 dlg_remove_callback(&(obj
->obj
));
394 * Display a gauge, or progress meter. Starts at percent% and reads stdin. If
395 * stdin is not XXX, then it is interpreted as a percentage, and the display is
396 * updated accordingly. Otherwise the next line is the percentage, and
397 * subsequent lines up to another XXX are used for the new prompt. Note that
398 * the size of the window never changes, so the prompt can not get any larger
399 * than the height and width specified.
402 dialog_gauge(const char *title
,
410 void *objptr
= dlg_allocate_gauge(title
, cprompt
, height
, width
, percent
);
411 MY_OBJ
*obj
= (MY_OBJ
*) objptr
;
413 DLG_TRACE(("# gauge args:\n"));
414 DLG_TRACE2S("title", title
);
415 DLG_TRACE2S("message", cprompt
);
416 DLG_TRACE2N("height", height
);
417 DLG_TRACE2N("width", width
);
418 DLG_TRACE2N("percent", percent
);
420 dlg_add_callback_ref((DIALOG_CALLBACK
**) & obj
, my_cleanup
);
421 dlg_update_gauge(obj
, percent
);
423 dlg_trace_win(obj
->obj
.win
);
425 ch
= dlg_getc(obj
->obj
.win
, &fkey
);
427 if (fkey
&& ch
== KEY_RESIZE
) {
428 MY_OBJ
*oldobj
= obj
;
430 dlg_will_resize(obj
->obj
.win
);
432 obj
= dlg_allocate_gauge(title
,
438 /* avoid breaking new window in dlg_remove_callback */
439 oldobj
->obj
.caller
= 0;
440 oldobj
->obj
.input
= 0;
441 oldobj
->obj
.keep_win
= FALSE
;
443 /* remove the old version of the gauge */
444 _dlg_resize_cleanup(oldobj
->obj
.win
);
445 dlg_remove_callback(&(oldobj
->obj
));
447 dlg_add_callback_ref((DIALOG_CALLBACK
**) & obj
, my_cleanup
);
448 dlg_update_gauge(obj
, obj
->percent
);
452 while (valid(obj
) && handle_my_getc(&(obj
->obj
), ch
, fkey
, &result
));
456 return (DLG_EXIT_OK
);