ui_objects.c revision 6458
1/* 2 * Program: objects.c 3 * Author: Marc van Kempen 4 * Desc: Implementation of UI-objects: 5 * - String input fields 6 * - List selection 7 * - Buttons 8 * 9 * Copyright (c) 1995, Marc van Kempen 10 * 11 * All rights reserved. 12 * 13 * This software may be used, modified, copied, distributed, and 14 * sold, in both source and binary form provided that the above 15 * copyright and these terms are retained, verbatim, as the first 16 * lines of this file. Under no circumstances is the author 17 * responsible for the proper functioning of this software, nor does 18 * the author assume any responsibility for damages incurred with 19 * its use. 20 * 21 */ 22 23#include <stdlib.h> 24#include <sys/param.h> 25#include <ncurses.h> 26#include <dialog.h> 27#include "dialog.priv.h" 28#include "ui_objects.h" 29 30#define ESC 27 31 32 33/*********************************************************************** 34 * 35 * Obj routines 36 * 37 ***********************************************************************/ 38 39void 40AddObj(ComposeObj **Obj, int objtype, void *obj) 41/* 42 * Desc: Add the object <obj> to the list of objects <Obj> 43 */ 44{ 45 if (*Obj == NULL) { 46 /* Create the root object */ 47 *Obj = (ComposeObj *) malloc( sizeof(ComposeObj) ); 48 if (!Obj) { 49 printf("AddObj: Error malloc'ing ComposeObj\n"); 50 exit(-1); 51 } 52 (*Obj)->objtype = objtype; 53 (*Obj)->obj = obj; 54 (*Obj)->next = NULL; 55 (*Obj)->prev = NULL; 56 } else { 57 ComposeObj *o = *Obj; 58 59 /* create the next object */ 60 while (o->next) o = (ComposeObj *) o->next; 61 o->next = (struct ComposeObj *) malloc( sizeof(ComposeObj) ); 62 if (!o->next) { 63 printf("AddObj: Error malloc'ing o->next\n"); 64 exit(-1); 65 } 66 o->next->objtype = objtype; 67 o->next->obj = obj; 68 o->next->next = NULL; 69 o->next->prev = o; 70 } 71 72 return; 73} /* AddObj() */ 74 75void 76FreeObj(ComposeObj *Obj) 77/* 78 * Desc: free the memory occupied by *Obj 79 */ 80{ 81 ComposeObj *o = Obj; 82 83 o = Obj; 84 while (o) { 85 o = Obj->next; 86 free(Obj); 87 Obj = o; 88 } 89 90 return; 91} /* FreeObj() */ 92 93 94int 95ReadObj(ComposeObj *Obj) 96/* 97 * Desc: navigate through the different objects calling their 98 * respective navigation routines as necessary 99 * Pre: Obj != NULL 100 */ 101{ 102 ComposeObj *o; 103 ComposeObj *last; /* the last object in the list */ 104 int ret; /* the return value from the selection routine */ 105 106 /* find the last object in the list */ 107 last = Obj; 108 while (last->next) last = last->next; 109 110 ret = 0; 111 o = Obj; 112 while ((ret != SEL_BUTTON) && (ret != SEL_ESC)) { 113 switch(o->objtype) { 114 case STRINGOBJ: 115 ret = SelectStringObj((StringObj *) o->obj); 116 break; 117 case LISTOBJ: 118 ret = SelectListObj((ListObj *) o->obj); 119 break; 120 case BUTTONOBJ: 121 ret = SelectButtonObj((ButtonObj *) o->obj); 122 break; 123 } 124 switch(ret) { 125 case SEL_CR: 126 case SEL_TAB: /* move to the next object in the list */ 127 if (o->next != NULL) { 128 o = o->next; /* next object */ 129 } else { 130 o = Obj; /* beginning of the list */ 131 } 132 break; 133 case SEL_BACKTAB: /* move to the previous object in the list */ 134 if (o->prev != NULL) { 135 o = o->prev; /* previous object */ 136 } else { 137 o = last; /* end of the list */ 138 } 139 break; 140 case KEY_F(1): /* display help_file */ 141 case '?': 142 display_helpfile(); 143 break; 144 } 145 } 146 147 return(ret); 148 149} /* ReadObj() */ 150 151 152int 153PollObj(ComposeObj **Obj) 154{ 155 ComposeObj *last; /* the last object in the list */ 156 ComposeObj *first; /* the first object in the list */ 157 int ret; /* the return value from the selection routine */ 158 159 /* find the last object in the list */ 160 last = *Obj; 161 while (last->next) last = last->next; 162 163 /* find the first object in the list */ 164 first = *Obj; 165 while (first->prev) first = first->prev; 166 167 ret = 0; 168 switch((*Obj)->objtype) { 169 case STRINGOBJ: 170 ret = SelectStringObj((StringObj *) (*Obj)->obj); 171 break; 172 case LISTOBJ: 173 ret = SelectListObj((ListObj *) (*Obj)->obj); 174 break; 175 case BUTTONOBJ: 176 ret = SelectButtonObj((ButtonObj *) (*Obj)->obj); 177 break; 178 } 179 switch(ret) { 180 case SEL_CR: 181 case SEL_TAB: /* move to the next object in the list */ 182 if ((*Obj)->next != NULL) { 183 *Obj = (*Obj)->next; /* next object */ 184 } else { 185 *Obj = first; /* beginning of the list */ 186 } 187 break; 188 case SEL_BACKTAB: /* move to the previous object in the list */ 189 if ((*Obj)->prev != NULL) { 190 *Obj = (*Obj)->prev; /* previous object */ 191 } else { 192 *Obj = last; /* end of the list */ 193 } 194 break; 195 } 196 197 return(ret); 198 199} /* PollObj() */ 200 201 202void 203DelObj(ComposeObj *Obj) 204/* 205 * Desc: Free all objects 206 */ 207{ 208 ComposeObj *o; 209 210 o = Obj; 211 while (Obj != NULL) { 212 switch(Obj->objtype) { 213 case STRINGOBJ: 214 DelStringObj((StringObj *) Obj->obj); 215 break; 216 case LISTOBJ: 217 DelListObj((ListObj *) Obj->obj); 218 break; 219 case BUTTONOBJ: 220 DelButtonObj((ButtonObj *) Obj->obj); 221 break; 222 } 223 Obj = Obj->next; 224 } 225 226 FreeObj(o); 227} /* DelObj() */ 228 229/*********************************************************************** 230 * 231 * StringObj routines 232 * 233 ***********************************************************************/ 234 235void 236RefreshStringObj(StringObj *so) 237/* 238 * Desc: redraw the object 239 */ 240{ 241 char tmp[512]; 242 243 wmove(so->win, so->y, so->x+1); 244 wattrset(so->win, dialog_attr); 245 waddstr(so->win, so->title); 246 247 draw_box(so->win, so->y+1, so->x, 3, so->w, dialog_attr, border_attr); 248 wattrset(so->win, item_attr); 249 wmove(so->win, so->y+2, so->x+1); 250 if (strlen(so->s) > so->w-2) { 251 strncpy(tmp, (char *) so->s + strlen(so->s) - so->w + 2, so->w - 1); 252 waddstr(so->win, tmp); 253 } else { 254 waddstr(so->win, so->s); 255 } 256 257 return; 258} /* RefreshStringObj() */ 259 260StringObj * 261NewStringObj(WINDOW *win, char *title, char *s, int y, int x, int w, int len) 262/* 263 * Desc: Initialize a new stringobj and return a pointer to it. 264 * Draw the object on the screen at the specified coordinates 265 */ 266{ 267 StringObj *so; 268 269 /* Initialize a new object */ 270 so = (StringObj *) malloc( sizeof(StringObj) ); 271 if (!so) { 272 printf("NewStringObj: Error malloc'ing StringObj\n"); 273 exit(-1); 274 } 275 so->title = (char *) malloc( strlen(title) + 1); 276 if (!so->title) { 277 printf("NewStringObj: Error malloc'ing so->title\n"); 278 exit(-1); 279 } 280 strcpy(so->title, title); 281 so->s = s; 282 strcpy(so->s, s); 283 so->x = x; 284 so->y = y; 285 so->w = w; 286 so->len = len; 287 so->win = win; 288 289 /* Draw it on the screen */ 290 RefreshStringObj(so); 291 292 return(so); 293} /* NewStringObj() */ 294 295int 296SelectStringObj(StringObj *so) 297/* 298 * Desc: get input using the info in <so> 299 */ 300{ 301 int key; 302 char tmp[so->len+1]; 303 304 strcpy(tmp, so->s); 305 key = line_edit(so->win, so->y+2, so->x+1, 306 so->len, so->w-2, inputbox_attr, TRUE, tmp); 307 if ((key == '\n') || (key == '\r')) { 308 strcpy(so->s, tmp); 309 } 310 RefreshStringObj(so); 311 if (key == ESC) { 312 return(SEL_ESC); 313 } 314 if (key == '\t') { 315 return(SEL_TAB); 316 } 317 if ( (key == KEY_BTAB) || (key == KEY_F(2)) ) { 318 return(SEL_BACKTAB); 319 } 320 if ((key == '\n') || (key == '\r')) { 321 strcpy(so->s, tmp); 322 return(SEL_CR); 323 } 324 return(key); 325} /* SelectStringObj() */ 326 327 328void 329DelStringObj(StringObj *so) 330/* 331 * Desc: Free the space occupied by <so> 332 */ 333{ 334 free(so->title); 335 free(so); 336 337 return; 338} 339 340/*********************************************************************** 341 * 342 * ListObj routines 343 * 344 ***********************************************************************/ 345 346void 347DrawNames(ListObj *lo) 348/* 349 * Desc: Just refresh the names, not the surrounding box and title 350 */ 351{ 352 int i, j, h, x, y; 353 char tmp[MAXPATHLEN]; 354 355 x = lo->x + 1; 356 y = lo->y + 2; 357 h = lo->h - 2; 358 wattrset(lo->win, item_attr); 359 for (i=lo->scroll; i<lo->n && i<lo->scroll+h; i++) { 360 wmove(lo->win, y+i-lo->scroll, x); 361 if (strlen(lo->name[i]) > lo->w-2) { 362 strncpy(tmp, lo->name[i], lo->w-2); 363 tmp[lo->w - 2] = 0; 364 waddstr(lo->win, tmp); 365 } else { 366 waddstr(lo->win, lo->name[i]); 367 for (j=strlen(lo->name[i]); j<lo->w-2; j++) waddstr(lo->win, " "); 368 } 369 } 370 while (i<lo->scroll+h) { 371 wmove(lo->win, y+i-lo->scroll, x); 372 for (j=0; j<lo->w-2; j++) waddstr(lo->win, " "); 373 i++; 374 } 375 376 return; 377} /* DrawNames() */ 378 379void 380RefreshListObj(ListObj *lo) 381/* 382 * Desc: redraw the list object 383 */ 384{ 385 char perc[7]; 386 387 /* setup the box */ 388 wmove(lo->win, lo->y, lo->x+1); 389 wattrset(lo->win, dialog_attr); 390 waddstr(lo->win, lo->title); 391 draw_box(lo->win, lo->y+1, lo->x, lo->h, lo->w, dialog_attr, border_attr); 392 393 /* draw the names */ 394 DrawNames(lo); 395 396 /* Draw % indication */ 397 sprintf(perc, "(%3d%%)", MIN(100, (int) (100 * (lo->sel+lo->h-2) / MAX(1, lo->n)))); 398 wmove(lo->win, lo->y + lo->h, lo->x + lo->w - 8); 399 wattrset(lo->win, dialog_attr); 400 waddstr(lo->win, perc); 401 402 403 return; 404} /* RefreshListObj() */ 405 406ListObj * 407NewListObj(WINDOW *win, char *title, char **list, char *listelt, int y, int x, 408 int h, int w, int n) 409/* 410 * Desc: create a listobj, draw it on the screen and return a pointer to it. 411 */ 412{ 413 ListObj *lo; 414 415 /* Initialize a new object */ 416 lo = (ListObj *) malloc( sizeof(ListObj) ); 417 if (!lo) { 418 printf("NewListObj: Error malloc'ing ListObj\n"); 419 exit(-1); 420 } 421 lo->title = (char *) malloc( strlen(title) + 1); 422 if (!lo->title) { 423 printf("NewListObj: Error malloc'ing lo->title\n"); 424 exit(-1); 425 } 426 strcpy(lo->title, title); 427 lo->name = list; 428 lo->y = y; 429 lo->x = x; 430 lo->w = w; 431 lo->h = h; 432 lo->n = n; 433 lo->scroll = 0; 434 lo->sel = 0; 435 lo->elt = listelt; 436 lo->win = win; 437 438 /* Draw the object on the screen */ 439 RefreshListObj(lo); 440 441 return(lo); 442} /* NewListObj() */ 443 444void 445UpdateListObj(ListObj *lo, char **list, int n) 446/* 447 * Desc: Update the list in the listobject with the provided list 448 * Pre: lo->name "has been allocated" 449 * (A i: 0<=i<lo->n: "lo->name[i] has been allocated") 450 */ 451{ 452 int i; 453 454 /* Free the current names */ 455 if (lo->name != NULL) { 456 for (i=0; i<lo->n; i++) { 457 free(lo->name[i]); 458 } 459 free(lo->name); 460 } 461 462 /* Rewrite the list in the object */ 463 lo->name = list; 464 lo->n = n; 465 lo->scroll = 0; 466 lo->sel = 0; 467 468 /* Draw the object on the screen */ 469 RefreshListObj(lo); 470 471 return; 472} /* UpdateListObj() */ 473 474int 475SelectListObj(ListObj *lo) 476/* 477 * Desc: get a listname (or listnames), TAB to move on, or ESC ESC to exit 478 * Pre: lo->n >= 1 479 */ 480{ 481 int key, sel_x, sel_y; 482 char tmp[MAXPATHLEN]; 483 char perc[4]; 484 485 sel_x = lo->x+1; 486 sel_y = lo->y + 2 + lo->sel - lo->scroll; 487 488 if (lo->n == 0) return(SEL_TAB); 489 490 keypad(lo->win, TRUE); 491 492 /* Draw current selection in inverse video */ 493 wmove(lo->win, sel_y, sel_x); 494 wattrset(lo->win, item_selected_attr); 495 waddstr(lo->win, lo->name[lo->sel]); 496 497 key = wgetch(lo->win); 498 while ((key != '\t') && (key != '\n') && (key != '\r') 499 && (key != ESC) && (key != KEY_F(1)) && (key != '?')) { 500 /* first draw current item in normal video */ 501 wmove(lo->win, sel_y, sel_x); 502 wattrset(lo->win, item_attr); 503 if (strlen(lo->name[lo->sel]) > lo->w - 2) { 504 strncpy(tmp, lo->name[lo->sel], lo->w - 2); 505 tmp[lo->w - 2] = 0; 506 waddstr(lo->win, tmp); 507 } else { 508 waddstr(lo->win, lo->name[lo->sel]); 509 } 510 511 switch (key) { 512 case KEY_DOWN: 513 case ctrl('n'): 514 if (sel_y < lo->y + lo->h-1) { 515 if (lo->sel < lo->n-1) { 516 sel_y++; 517 lo->sel++; 518 } 519 } else { 520 if (lo->sel < lo->n-1) { 521 lo->sel++; 522 lo->scroll++; 523 DrawNames(lo); 524 wrefresh(lo->win); 525 } 526 } 527 break; 528 case KEY_UP: 529 case ctrl('p'): 530 if (sel_y > lo->y+2) { 531 if (lo->sel > 0) { 532 sel_y--; 533 lo->sel--; 534 } 535 } else { 536 if (lo->sel > 0) { 537 lo->sel--; 538 lo->scroll--; 539 DrawNames(lo); 540 wrefresh(lo->win); 541 } 542 } 543 break; 544 case KEY_HOME: 545 case ctrl('a'): 546 lo->sel = 0; 547 lo->scroll = 0; 548 sel_y = lo->y + 2; 549 DrawNames(lo); 550 wrefresh(lo->win); 551 break; 552 case KEY_END: 553 case ctrl('e'): 554 if (lo->n < lo->h - 3) { 555 lo->sel = lo->n-1; 556 lo->scroll = 0; 557 sel_y = lo->y + 2 + lo->sel - lo->scroll; 558 } else { 559 /* more than one page of list */ 560 lo->sel = lo->n-1; 561 lo->scroll = lo->n-1 - (lo->h-3); 562 sel_y = lo->y + 2 + lo->sel - lo->scroll; 563 DrawNames(lo); 564 wrefresh(lo->win); 565 } 566 break; 567 case KEY_NPAGE: 568 case ctrl('f'): 569 lo->sel += lo->h - 2; 570 if (lo->sel >= lo->n) lo->sel = lo->n - 1; 571 lo->scroll += lo->h - 2; 572 if (lo->scroll >= lo->n - 1) lo->scroll = lo->n - 1; 573 if (lo->scroll < 0) lo->scroll = 0; 574 sel_y = lo->y + 2 + lo->sel - lo->scroll; 575 DrawNames(lo); 576 wrefresh(lo->win); 577 break; 578 case KEY_PPAGE: 579 case ctrl('b'): 580 lo->sel -= lo->h - 2; 581 if (lo->sel < 0) lo->sel = 0; 582 lo->scroll -= lo->h - 2; 583 if (lo->scroll < 0) lo->scroll = 0; 584 sel_y = lo->y + 2 + lo->sel - lo->scroll; 585 DrawNames(lo); 586 wrefresh(lo->win); 587 break; 588 } 589 /* Draw % indication */ 590 sprintf(perc, "(%3d%%)", MIN(100, (int) 591 (100 * (lo->sel+lo->h - 2) / MAX(1, lo->n)))); 592 wmove(lo->win, lo->y + lo->h, lo->x + lo->w - 8); 593 wattrset(lo->win, dialog_attr); 594 waddstr(lo->win, perc); 595 596 /* draw current item in inverse */ 597 wmove(lo->win, sel_y, sel_x); 598 wattrset(lo->win, item_selected_attr); 599 if (strlen(lo->name[lo->sel]) > lo->w - 2) { 600 /* when printing in inverse video show the last characters in the */ 601 /* name that will fit in the window */ 602 strncpy(tmp, 603 lo->name[lo->sel] + strlen(lo->name[lo->sel]) - (lo->w - 2), 604 lo->w - 2); 605 tmp[lo->w - 2] = 0; 606 waddstr(lo->win, tmp); 607 } else { 608 waddstr(lo->win, lo->name[lo->sel]); 609 } 610 key = wgetch(lo->win); 611 } 612 613 if (key == ESC) { 614 return(SEL_ESC); 615 } 616 if (key == '\t') { 617 return(SEL_TAB); 618 } 619 if ((key == KEY_BTAB) || (key == ctrl('b'))) { 620 return(SEL_BACKTAB); 621 } 622 if ((key == '\n') || (key == '\r')) { 623 strcpy(lo->elt, lo->name[lo->sel]); 624 return(SEL_CR); 625 } 626 return(key); 627} /* SelectListObj() */ 628 629void 630DelListObj(ListObj *lo) 631/* 632 * Desc: Free the space occupied by the listobject 633 */ 634{ 635 int i; 636 637 free(lo->title); 638 if (lo->name != NULL) { 639 for (i=0; i<lo->n; i++) { 640 free(lo->name[i]); 641 } 642 free(lo->name); 643 } 644 645 free(lo); 646 647} /* DelListObj() */ 648 649 650/*********************************************************************** 651 * 652 * ButtonObj routines 653 * 654 ***********************************************************************/ 655 656 657void 658RefreshButtonObj(ButtonObj *bo) 659/* 660 * Desc: redraw the button 661 */ 662{ 663 draw_box(bo->win, bo->y, bo->x, 3, bo->w, dialog_attr, border_attr); 664 print_button(bo->win, bo->title, bo->y+1, bo->x+2, FALSE); 665 666 return; 667} /* RefreshButtonObj() */ 668 669ButtonObj * 670NewButtonObj(WINDOW *win, char *title, int *pushed, int y, int x) 671/* 672 * Desc: Create a new button object 673 */ 674{ 675 ButtonObj *bo; 676 677 bo = (ButtonObj *) malloc( sizeof(ButtonObj) ); 678 679 bo->win = win; 680 bo->title = (char *) malloc( strlen(title) + 1); 681 strcpy(bo->title, title); 682 bo->x = x; 683 bo->y = y; 684 bo->w = strlen(title) + 6; 685 bo->h = 3; 686 bo->pushed = pushed; 687 688 RefreshButtonObj(bo); 689 690 return(bo); 691} /* NewButtonObj() */ 692 693int 694SelectButtonObj(ButtonObj *bo) 695/* 696 * Desc: Wait for buttonpresses or TAB's to move on, or ESC ESC 697 */ 698{ 699 int key; 700 701 print_button(bo->win, bo->title, bo->y+1, bo->x+2, TRUE); 702 wmove(bo->win, bo->y+1, bo->x+(bo->w/2)-1); 703 key = wgetch(bo->win); 704 print_button(bo->win, bo->title, bo->y+1, bo->x+2, FALSE); 705 switch(key) { 706 case '\t': 707 return(SEL_TAB); 708 break; 709 case KEY_BTAB: 710 case ctrl('b'): 711 return(SEL_BACKTAB); 712 case '\n': 713 *(bo->pushed) = TRUE; 714 return(SEL_BUTTON); 715 break; 716 case ESC: 717 return(SEL_ESC); 718 break; 719 default: 720 return(key); 721 break; 722 } 723} /* SelectButtonObj() */ 724 725void 726DelButtonObj(ButtonObj *bo) 727/* 728 * Desc: Free the space occupied by <bo> 729 */ 730{ 731 free(bo->title); 732 free(bo); 733 734 return; 735} /* DelButtonObj() */ 736