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