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