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