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 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 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 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 * 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 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 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 * 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 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 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 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 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