1 /*
2 * $Id: progressbox.c,v 1.55 2022/04/03 22:38:16 tom Exp $
3 *
4 * progressbox.c -- implements the progress box
5 *
6 * Copyright 2006-2020,2022 Thomas E. Dickey
7 * Copyright 2005 Valery Reznic
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License, version 2.1
11 * as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program; if not, write to
20 * Free Software Foundation, Inc.
21 * 51 Franklin St., Fifth Floor
22 * Boston, MA 02110, USA.
23 */
24
25 #include <dlg_internals.h>
26 #include <dlg_keys.h>
27
28 #define MIN_HIGH (4)
29 #define MIN_WIDE (10 + 2 * (2 + MARGIN))
30
31 #ifdef KEY_RESIZE
32 typedef struct _wrote {
33 struct _wrote *link;
34 char *text;
35 } WROTE;
36 #endif
37
38 typedef struct {
39 DIALOG_CALLBACK obj;
40 WINDOW *text;
41 char *prompt;
42 int high, wide;
43 int old_high, old_wide;
44 char line[MAX_LEN + 1];
45 int is_eof;
46 #ifdef KEY_RESIZE
47 WROTE *wrote;
48 #endif
49 } MY_OBJ;
50
51 static void
free_obj(MY_OBJ * obj)52 free_obj(MY_OBJ * obj)
53 {
54 dlg_del_window(obj->obj.win);
55 free(obj->prompt);
56 #ifdef KEY_RESIZE
57 while (obj->wrote) {
58 WROTE *wrote = obj->wrote;
59 obj->wrote = wrote->link;
60 free(wrote->text);
61 free(wrote);
62 }
63 #endif
64 free(obj);
65 }
66
67 #ifdef KEY_RESIZE
68 static void
restart_obj(MY_OBJ * obj)69 restart_obj(MY_OBJ * obj)
70 {
71 free(obj->prompt);
72 obj->high = obj->old_high;
73 obj->wide = obj->old_wide;
74 dlg_clear();
75 dlg_del_window(obj->obj.win);
76 }
77 #endif
78
79 static void
start_obj(MY_OBJ * obj,const char * title,const char * cprompt)80 start_obj(MY_OBJ * obj, const char *title, const char *cprompt)
81 {
82 int y, x, thigh;
83
84 obj->prompt = dlg_strclone(cprompt);
85 dlg_tab_correct_str(obj->prompt);
86 dlg_auto_size(title, obj->prompt, &obj->high, &obj->wide, MIN_HIGH, MIN_WIDE);
87
88 dlg_print_size(obj->high, obj->wide);
89 dlg_ctl_size(obj->high, obj->wide);
90
91 x = dlg_box_x_ordinate(obj->wide);
92 y = dlg_box_y_ordinate(obj->high);
93 thigh = obj->high - (2 * MARGIN);
94
95 obj->obj.win = dlg_new_window(obj->high, obj->wide, y, x);
96
97 dlg_draw_box2(obj->obj.win,
98 0, 0,
99 obj->high, obj->wide,
100 dialog_attr,
101 border_attr,
102 border2_attr);
103 dlg_draw_title(obj->obj.win, title);
104 dlg_draw_helpline(obj->obj.win, FALSE);
105
106 if (obj->prompt[0] != '\0') {
107 int i;
108 int y2, x2;
109
110 dlg_attrset(obj->obj.win, dialog_attr);
111 dlg_print_autowrap(obj->obj.win, obj->prompt, obj->high, obj->wide);
112 getyx(obj->obj.win, y2, x2);
113 (void) x2;
114 ++y2;
115 wmove(obj->obj.win, y2, MARGIN);
116 for (i = 0; i < getmaxx(obj->obj.win) - 2 * MARGIN; i++)
117 (void) waddch(obj->obj.win, dlg_boxchar(ACS_HLINE));
118 y += y2;
119 thigh -= y2;
120 }
121
122 /* Create window for text region, used for scrolling text */
123 obj->text = dlg_sub_window(obj->obj.win,
124 thigh,
125 obj->wide - (2 * MARGIN),
126 y + MARGIN,
127 x + MARGIN);
128
129 (void) wrefresh(obj->obj.win);
130
131 (void) wmove(obj->obj.win, getmaxy(obj->text), (MARGIN + 1));
132 (void) wnoutrefresh(obj->obj.win);
133
134 dlg_attr_clear(obj->text, getmaxy(obj->text), getmaxx(obj->text), dialog_attr);
135 }
136
137 /*
138 * Return current line of text.
139 */
140 static char *
get_line(MY_OBJ * obj,int * restart)141 get_line(MY_OBJ * obj, int *restart)
142 {
143 FILE *fp = obj->obj.input;
144 int col = 0;
145 int j, tmpint;
146 char *result = obj->line;
147
148 *restart = 0;
149 for (;;) {
150 int ch = getc(fp);
151 #ifdef KEY_RESIZE
152 /* SIGWINCH may have interrupted this - try to ignore if resizable */
153 if (ferror(fp)) {
154 switch (errno) {
155 case EINTR:
156 clearerr(fp);
157 continue;
158 default:
159 break;
160 }
161 }
162 #endif
163 if (feof(fp) || ferror(fp)) {
164 obj->is_eof = 1;
165 if (!col) {
166 result = NULL;
167 }
168 break;
169 }
170 if (ch == '\n')
171 break;
172 if (ch == '\r')
173 break;
174 if (col >= MAX_LEN)
175 continue;
176 if ((ch == TAB) && (dialog_vars.tab_correct)) {
177 tmpint = dialog_state.tab_len
178 - (col % dialog_state.tab_len);
179 for (j = 0; j < tmpint; j++) {
180 if (col < MAX_LEN) {
181 obj->line[col] = ' ';
182 ++col;
183 } else {
184 break;
185 }
186 }
187 } else {
188 obj->line[col] = (char) ch;
189 ++col;
190 }
191 }
192
193 obj->line[col] = '\0';
194
195 #ifdef KEY_RESIZE
196 if (result != NULL) {
197 WINDOW *win = obj->text;
198 WROTE *wrote = dlg_calloc(WROTE, 1);
199
200 if (wrote != 0) {
201 wrote->text = dlg_strclone(obj->line);
202 wrote->link = obj->wrote;
203 obj->wrote = wrote;
204 }
205
206 nodelay(win, TRUE);
207 if (wgetch(win) == KEY_RESIZE) {
208 *restart = 1;
209 }
210 nodelay(win, FALSE);
211 }
212 #endif
213 return result;
214 }
215
216 /*
217 * Print a new line of text.
218 */
219 static void
print_line(MY_OBJ * obj,const char * line,int row)220 print_line(MY_OBJ * obj, const char *line, int row)
221 {
222 int width = obj->wide - (2 * MARGIN);
223 int limit = MIN((int) strlen(line), width - 2);
224
225 (void) wmove(obj->text, row, 0); /* move cursor to correct line */
226 wprintw(obj->text, " %.*s", limit, line);
227 while (++limit < width) {
228 waddch(obj->text, ' ');
229 }
230 }
231
232 #ifdef KEY_RESIZE
233 static int
wrote_size(MY_OBJ * obj,int want)234 wrote_size(MY_OBJ * obj, int want)
235 {
236 int result = 0;
237 WROTE *wrote = obj->wrote;
238 while (wrote != NULL && want > 0) {
239 wrote = wrote->link;
240 want--;
241 result++;
242 }
243 return result;
244 }
245
246 static const char *
wrote_data(MY_OBJ * obj,int want)247 wrote_data(MY_OBJ * obj, int want)
248 {
249 const char *result = NULL;
250 WROTE *wrote = obj->wrote;
251 while (wrote != NULL && want > 0) {
252 result = wrote->text;
253 wrote = wrote->link;
254 want--;
255 }
256 return result;
257 }
258
259 static int
reprint_lines(MY_OBJ * obj,int buttons)260 reprint_lines(MY_OBJ * obj, int buttons)
261 {
262 int want = getmaxy(obj->text) - (buttons ? 2 : 0);
263 int have = wrote_size(obj, want);
264 int n;
265 for (n = 0; n < have; ++n) {
266 print_line(obj, wrote_data(obj, have - n), n);
267 }
268 (void) wrefresh(obj->text);
269 return have;
270 }
271 #endif
272
273 static int
pause_for_ok(MY_OBJ * obj,const char * title,const char * cprompt)274 pause_for_ok(MY_OBJ * obj, const char *title, const char *cprompt)
275 {
276 /* *INDENT-OFF* */
277 static DLG_KEYS_BINDING binding[] = {
278 HELPKEY_BINDINGS,
279 ENTERKEY_BINDINGS,
280 TRAVERSE_BINDINGS,
281 END_KEYS_BINDING
282 };
283 /* *INDENT-ON* */
284
285 int button;
286 int key, fkey;
287 int result = DLG_EXIT_UNKNOWN;
288 const char **buttons = dlg_ok_label();
289 bool save_nocancel = dialog_vars.nocancel;
290 bool redraw = TRUE;
291
292 (void) title;
293 (void) cprompt;
294
295 dialog_vars.nocancel = TRUE;
296 button = dlg_default_button();
297
298 #ifdef KEY_RESIZE
299 restart:
300 #endif
301
302 dlg_register_window(obj->obj.win, "progressbox", binding);
303 dlg_register_buttons(obj->obj.win, "progressbox", buttons);
304
305 dlg_draw_bottom_box2(obj->obj.win, border_attr, border2_attr, dialog_attr);
306
307 while (result == DLG_EXIT_UNKNOWN) {
308 int check;
309
310 if (redraw) {
311 redraw = FALSE;
312 if (button < 0)
313 button = 0;
314 dlg_draw_buttons(obj->obj.win,
315 obj->high - 2, 0,
316 buttons, button,
317 FALSE, obj->wide);
318 }
319
320 key = dlg_mouse_wgetch(obj->obj.win, &fkey);
321 if (dlg_result_key(key, fkey, &result)) {
322 if (!dlg_button_key(result, &button, &key, &fkey))
323 break;
324 }
325
326 if (!fkey && (check = dlg_char_to_button(key, buttons)) >= 0) {
327 result = dlg_ok_buttoncode(check);
328 break;
329 }
330
331 if (fkey) {
332 switch (key) {
333 case DLGK_FIELD_NEXT:
334 button = dlg_next_button(buttons, button);
335 redraw = TRUE;
336 break;
337 case DLGK_FIELD_PREV:
338 button = dlg_prev_button(buttons, button);
339 redraw = TRUE;
340 break;
341 case DLGK_ENTER:
342 result = dlg_ok_buttoncode(button);
343 break;
344 #ifdef KEY_RESIZE
345 case KEY_RESIZE:
346 dlg_will_resize(obj->obj.win);
347 restart_obj(obj);
348 start_obj(obj, title, cprompt);
349 reprint_lines(obj, TRUE);
350 redraw = TRUE;
351 goto restart;
352 #endif
353 default:
354 if (is_DLGK_MOUSE(key)) {
355 result = dlg_ok_buttoncode(key - M_EVENT);
356 if (result < 0)
357 result = DLG_EXIT_OK;
358 } else {
359 beep();
360 }
361 break;
362 }
363
364 } else if (key > 0) {
365 beep();
366 }
367 }
368 dlg_add_last_key(-1);
369
370 dlg_mouse_free_regions();
371 dlg_unregister_window(obj->obj.win);
372
373 dialog_vars.nocancel = save_nocancel;
374 return result;
375 }
376
377 int
dlg_progressbox(const char * title,const char * cprompt,int height,int width,int pauseopt,FILE * fp)378 dlg_progressbox(const char *title,
379 const char *cprompt,
380 int height,
381 int width,
382 int pauseopt,
383 FILE *fp)
384 {
385 int i;
386 MY_OBJ *obj;
387 int again = 0;
388 int toprow = 0;
389 int result;
390
391 DLG_TRACE(("# progressbox args:\n"));
392 DLG_TRACE2S("title", title);
393 DLG_TRACE2S("message", cprompt);
394 DLG_TRACE2N("height", height);
395 DLG_TRACE2N("width", width);
396 DLG_TRACE2N("pause", pauseopt);
397 DLG_TRACE2N("fp", fp ? fileno(fp) : -1);
398
399 obj = dlg_calloc(MY_OBJ, 1);
400 assert_ptr(obj, "dlg_progressbox");
401 obj->obj.input = fp;
402
403 obj->high = height;
404 obj->wide = width;
405
406 #ifdef KEY_RESIZE
407 obj->old_high = height;
408 obj->old_wide = width;
409
410 curs_set(0);
411 restart:
412 #endif
413
414 start_obj(obj, title, cprompt);
415 #ifdef KEY_RESIZE
416 if (again) {
417 toprow = reprint_lines(obj, FALSE);
418 }
419 #endif
420
421 for (i = toprow; get_line(obj, &again); i++) {
422 #ifdef KEY_RESIZE
423 if (again) {
424 dlg_will_resize(obj->obj.win);
425 restart_obj(obj);
426 goto restart;
427 }
428 #endif
429 if (i < getmaxy(obj->text)) {
430 print_line(obj, obj->line, i);
431 } else {
432 scrollok(obj->text, TRUE);
433 scroll(obj->text);
434 scrollok(obj->text, FALSE);
435 print_line(obj, obj->line, getmaxy(obj->text) - 1);
436 }
437 (void) wrefresh(obj->text);
438 if (obj->is_eof)
439 break;
440 }
441
442 dlg_trace_win(obj->obj.win);
443 curs_set(1);
444
445 if (pauseopt) {
446 int need = 1 + MARGIN;
447 int base = getmaxy(obj->text) - need;
448 if (i >= base) {
449 i -= base;
450 if (i > need)
451 i = need;
452 if (i > 0) {
453 scrollok(obj->text, TRUE);
454 }
455 wscrl(obj->text, i);
456 }
457 (void) wrefresh(obj->text);
458 result = pause_for_ok(obj, title, cprompt);
459 } else {
460 wrefresh(obj->obj.win);
461 result = DLG_EXIT_OK;
462 }
463
464 free_obj(obj);
465
466 return result;
467 }
468
469 /*
470 * Display text from a stdin in a scrolling window.
471 */
472 int
dialog_progressbox(const char * title,const char * cprompt,int height,int width)473 dialog_progressbox(const char *title, const char *cprompt, int height, int width)
474 {
475 int result;
476 result = dlg_progressbox(title,
477 cprompt,
478 height,
479 width,
480 FALSE,
481 dialog_state.pipe_input);
482 dialog_state.pipe_input = 0;
483 return result;
484 }
485