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