buttons.c revision 224014
1240116Smarcel/* 2240116Smarcel * $Id: buttons.c,v 1.86 2011/06/28 10:46:46 tom Exp $ 3240116Smarcel * 4240116Smarcel * buttons.c -- draw buttons, e.g., OK/Cancel 5240116Smarcel * 6240116Smarcel * Copyright 2000-2010,2011 Thomas E. Dickey 7240116Smarcel * 8240116Smarcel * This program is free software; you can redistribute it and/or modify 9240116Smarcel * it under the terms of the GNU Lesser General Public License, version 2.1 10240116Smarcel * as published by the Free Software Foundation. 11240116Smarcel * 12240116Smarcel * This program is distributed in the hope that it will be useful, but 13240116Smarcel * WITHOUT ANY WARRANTY; without even the implied warranty of 14240116Smarcel * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15240116Smarcel * Lesser General Public License for more details. 16240116Smarcel * 17240116Smarcel * You should have received a copy of the GNU Lesser General Public 18240116Smarcel * License along with this program; if not, write to 19240116Smarcel * Free Software Foundation, Inc. 20240116Smarcel * 51 Franklin St., Fifth Floor 21240116Smarcel * Boston, MA 02110, USA. 22240116Smarcel */ 23240116Smarcel 24240116Smarcel#include <dialog.h> 25240116Smarcel#include <dlg_keys.h> 26273929Sjmmv 27273929Sjmmv#ifdef NEED_WCHAR_H 28240116Smarcel#include <wchar.h> 29240116Smarcel#endif 30240116Smarcel 31240116Smarcel#define MIN_BUTTON (dialog_state.visit_items ? -1 : 0) 32240116Smarcel 33240116Smarcelstatic void 34240116Smarcelcenter_label(char *buffer, int longest, const char *label) 35240116Smarcel{ 36240116Smarcel int len = dlg_count_columns(label); 37240116Smarcel int left = 0, right = 0; 38240116Smarcel 39240116Smarcel *buffer = 0; 40240116Smarcel if (len < longest) { 41240116Smarcel left = (longest - len) / 2; 42240116Smarcel right = (longest - len - left); 43240116Smarcel if (left > 0) 44273929Sjmmv sprintf(buffer, "%*s", left, " "); 45273929Sjmmv } 46273929Sjmmv strcat(buffer, label); 47273929Sjmmv if (right > 0) 48273929Sjmmv sprintf(buffer + strlen(buffer), "%*s", right, " "); 49240116Smarcel} 50240116Smarcel 51240116Smarcel/* 52240116Smarcel * Parse a multibyte character out of the string, set it past the parsed 53240116Smarcel * character. 54240116Smarcel */ 55240116Smarcelstatic int 56240116Smarcelstring_to_char(const char **stringp) 57240116Smarcel{ 58240116Smarcel int result; 59251108Smarcel#ifdef USE_WIDE_CURSES 60240116Smarcel const char *string = *stringp; 61240116Smarcel size_t have = strlen(string); 62240116Smarcel size_t check; 63240116Smarcel size_t len; 64240116Smarcel wchar_t cmp2[2]; 65240116Smarcel mbstate_t state; 66240116Smarcel 67240116Smarcel memset(&state, 0, sizeof(state)); 68240116Smarcel len = mbrlen(string, have, &state); 69240116Smarcel if ((int) len > 0 && len <= have) { 70240116Smarcel memset(&state, 0, sizeof(state)); 71240116Smarcel memset(cmp2, 0, sizeof(cmp2)); 72251108Smarcel check = mbrtowc(cmp2, string, len, &state); 73240116Smarcel if ((int) check <= 0) 74240116Smarcel cmp2[0] = 0; 75240116Smarcel *stringp += len; 76240116Smarcel } else { 77240116Smarcel cmp2[0] = UCH(*string); 78240116Smarcel *stringp += 1; 79240116Smarcel } 80240116Smarcel result = cmp2[0]; 81240116Smarcel#else 82240116Smarcel const char *string = *stringp; 83240116Smarcel result = UCH(*string); 84240116Smarcel *stringp += 1; 85240116Smarcel#endif 86240116Smarcel return result; 87240116Smarcel} 88240116Smarcel 89240116Smarcel/* 90240116Smarcel * Print a button 91240116Smarcel */ 92240116Smarcelstatic void 93240116Smarcelprint_button(WINDOW *win, char *label, int y, int x, int selected) 94240116Smarcel{ 95240116Smarcel int i; 96240116Smarcel int state = 0; 97240116Smarcel const int *indx = dlg_index_wchars(label); 98240116Smarcel int limit = dlg_count_wchars(label); 99240116Smarcel chtype key_attr = (selected 100240116Smarcel ? button_key_active_attr 101240116Smarcel : button_key_inactive_attr); 102240116Smarcel chtype label_attr = (selected 103240116Smarcel ? button_label_active_attr 104240116Smarcel : button_label_inactive_attr); 105240116Smarcel 106240116Smarcel (void) wmove(win, y, x); 107240116Smarcel wattrset(win, selected 108240116Smarcel ? button_active_attr 109240116Smarcel : button_inactive_attr); 110240116Smarcel (void) waddstr(win, "<"); 111240116Smarcel wattrset(win, label_attr); 112240116Smarcel for (i = 0; i < limit; ++i) { 113240116Smarcel int first = indx[i]; 114240116Smarcel int last = indx[i + 1]; 115240116Smarcel 116240116Smarcel switch (state) { 117240116Smarcel case 0: 118240116Smarcel#ifdef USE_WIDE_CURSES 119240116Smarcel if ((last - first) != 1) { 120240116Smarcel const char *temp = (label + first); 121240116Smarcel int cmp = string_to_char(&temp); 122240116Smarcel if (dlg_isupper(cmp)) { 123240116Smarcel wattrset(win, key_attr); 124240116Smarcel state = 1; 125240116Smarcel } 126240116Smarcel break; 127240116Smarcel } 128240116Smarcel#endif 129240116Smarcel if (dlg_isupper(UCH(label[first]))) { 130240116Smarcel wattrset(win, key_attr); 131240116Smarcel state = 1; 132240116Smarcel } 133240116Smarcel break; 134240116Smarcel case 1: 135240116Smarcel wattrset(win, label_attr); 136240116Smarcel state = 2; 137240116Smarcel break; 138240116Smarcel } 139240116Smarcel waddnstr(win, label + first, last - first); 140240116Smarcel } 141240116Smarcel wattrset(win, selected 142240116Smarcel ? button_active_attr 143240116Smarcel : button_inactive_attr); 144240116Smarcel (void) waddstr(win, ">"); 145240116Smarcel (void) wmove(win, y, x + ((int) strspn(label, " ")) + 1); 146240116Smarcel} 147240116Smarcel 148240116Smarcel/* 149240116Smarcel * Count the buttons in the list. 150240116Smarcel */ 151240116Smarcelint 152240116Smarceldlg_button_count(const char **labels) 153240116Smarcel{ 154240116Smarcel int result = 0; 155240116Smarcel while (*labels++ != 0) 156240116Smarcel ++result; 157240116Smarcel return result; 158240116Smarcel} 159240116Smarcel 160240116Smarcel/* 161240116Smarcel * Compute the size of the button array in columns. Return the total number of 162240116Smarcel * columns in *length, and the longest button's columns in *longest 163240116Smarcel */ 164240116Smarcelvoid 165240116Smarceldlg_button_sizes(const char **labels, 166240116Smarcel int vertical, 167240116Smarcel int *longest, 168240116Smarcel int *length) 169240116Smarcel{ 170240116Smarcel int n; 171240116Smarcel 172240116Smarcel *length = 0; 173240116Smarcel *longest = 0; 174240116Smarcel for (n = 0; labels[n] != 0; n++) { 175240116Smarcel if (vertical) { 176240116Smarcel *length += 1; 177240116Smarcel *longest = 1; 178240116Smarcel } else { 179240116Smarcel int len = dlg_count_columns(labels[n]); 180240116Smarcel if (len > *longest) 181240116Smarcel *longest = len; 182240116Smarcel *length += len; 183240116Smarcel } 184240116Smarcel } 185240116Smarcel /* 186240116Smarcel * If we can, make all of the buttons the same size. This is only optional 187240116Smarcel * for buttons laid out horizontally. 188240116Smarcel */ 189240116Smarcel if (*longest < 6 - (*longest & 1)) 190240116Smarcel *longest = 6 - (*longest & 1); 191258289Sjmmv if (!vertical) 192258289Sjmmv *length = *longest * n; 193240116Smarcel} 194240116Smarcel 195240116Smarcel/* 196258289Sjmmv * Compute the size of the button array. 197258289Sjmmv */ 198258289Sjmmvint 199258289Sjmmvdlg_button_x_step(const char **labels, int limit, int *gap, int *margin, int *step) 200240116Smarcel{ 201240116Smarcel int count = dlg_button_count(labels); 202240116Smarcel int longest; 203240116Smarcel int length; 204240116Smarcel int unused; 205240116Smarcel int used; 206240116Smarcel 207240116Smarcel if (count == 0) 208240116Smarcel return 0; 209240116Smarcel dlg_button_sizes(labels, FALSE, &longest, &length); 210240116Smarcel used = (length + (count * 2)); 211258289Sjmmv unused = limit - used; 212258289Sjmmv 213258289Sjmmv if ((*gap = unused / (count + 3)) <= 0) { 214240116Smarcel if ((*gap = unused / (count + 1)) <= 0) 215240116Smarcel *gap = 1; 216240116Smarcel *margin = *gap; 217258289Sjmmv } else { 218258289Sjmmv *margin = *gap * 2; 219258289Sjmmv } 220258289Sjmmv *step = *gap + (used + count - 1) / count; 221240116Smarcel return (*gap > 0) && (unused >= 0); 222240116Smarcel} 223240116Smarcel 224240116Smarcel/* 225240116Smarcel * Make sure there is enough space for the buttons 226240116Smarcel */ 227240116Smarcelvoid 228240116Smarceldlg_button_layout(const char **labels, int *limit) 229240116Smarcel{ 230240116Smarcel int width = 1; 231240116Smarcel int gap, margin, step; 232258289Sjmmv 233258289Sjmmv if (labels != 0 && dlg_button_count(labels)) { 234240116Smarcel while (!dlg_button_x_step(labels, width, &gap, &margin, &step)) 235240116Smarcel ++width; 236240116Smarcel width += (4 * MARGIN); 237258289Sjmmv if (width > COLS) 238258289Sjmmv width = COLS; 239258289Sjmmv if (width > *limit) 240258289Sjmmv *limit = width; 241240116Smarcel } 242240116Smarcel} 243240116Smarcel 244240116Smarcel/* 245240116Smarcel * Print a list of buttons at the given position. 246240116Smarcel */ 247240116Smarcelvoid 248240116Smarceldlg_draw_buttons(WINDOW *win, 249240116Smarcel int y, int x, 250240116Smarcel const char **labels, 251240116Smarcel int selected, 252240116Smarcel int vertical, 253240116Smarcel int limit) 254240116Smarcel{ 255240116Smarcel chtype save = dlg_get_attrs(win); 256240116Smarcel int n; 257240116Smarcel int step = 0; 258240116Smarcel int length; 259240116Smarcel int longest; 260240116Smarcel int final_x; 261240116Smarcel int final_y; 262240116Smarcel int gap; 263240116Smarcel int margin; 264240116Smarcel size_t need; 265240116Smarcel char *buffer; 266240116Smarcel 267240116Smarcel dlg_mouse_setbase(getbegx(win), getbegy(win)); 268240116Smarcel 269240116Smarcel getyx(win, final_y, final_x); 270240116Smarcel 271240116Smarcel dlg_button_sizes(labels, vertical, &longest, &length); 272240116Smarcel 273240116Smarcel if (vertical) { 274240116Smarcel y += 1; 275240116Smarcel step = 1; 276240116Smarcel } else { 277240116Smarcel dlg_button_x_step(labels, limit, &gap, &margin, &step); 278240116Smarcel x += margin; 279240116Smarcel } 280240116Smarcel 281240116Smarcel /* 282240116Smarcel * Allocate a buffer big enough for any label. 283240116Smarcel */ 284240116Smarcel need = (size_t) longest; 285240116Smarcel for (n = 0; labels[n] != 0; ++n) { 286240116Smarcel need += strlen(labels[n]) + 1; 287240116Smarcel } 288240116Smarcel buffer = dlg_malloc(char, need); 289240116Smarcel assert_ptr(buffer, "dlg_draw_buttons"); 290240116Smarcel 291240116Smarcel /* 292240116Smarcel * Draw the labels. 293240116Smarcel */ 294240116Smarcel for (n = 0; labels[n] != 0; n++) { 295240116Smarcel center_label(buffer, longest, labels[n]); 296240116Smarcel mouse_mkbutton(y, x, dlg_count_columns(buffer), n); 297240116Smarcel print_button(win, buffer, y, x, 298240116Smarcel (selected == n) || (n == 0 && selected < 0)); 299240116Smarcel if (selected == n) 300240116Smarcel getyx(win, final_y, final_x); 301240116Smarcel 302240116Smarcel if (vertical) { 303240116Smarcel if ((y += step) > limit) 304240116Smarcel break; 305240116Smarcel } else { 306240116Smarcel if ((x += step) > limit) 307240116Smarcel break; 308240116Smarcel } 309240116Smarcel } 310240116Smarcel (void) wmove(win, final_y, final_x); 311240116Smarcel wrefresh(win); 312240116Smarcel free(buffer); 313240116Smarcel wattrset(win, save); 314240116Smarcel} 315240116Smarcel 316240116Smarcel/* 317240116Smarcel * Match a given character against the beginning of the string, ignoring case 318240116Smarcel * of the given character. The matching string must begin with an uppercase 319240116Smarcel * character. 320240116Smarcel */ 321240116Smarcelint 322240116Smarceldlg_match_char(int ch, const char *string) 323240116Smarcel{ 324240116Smarcel if (string != 0) { 325240116Smarcel int cmp2 = string_to_char(&string); 326240116Smarcel#ifdef USE_WIDE_CURSES 327240116Smarcel wint_t cmp1 = dlg_toupper(ch); 328240116Smarcel if (cmp2 != 0 && (wchar_t) cmp1 == (wchar_t) dlg_toupper(cmp2)) { 329240116Smarcel return TRUE; 330240116Smarcel } 331240116Smarcel#else 332240116Smarcel if (ch > 0 && ch < 256) { 333240116Smarcel if (dlg_toupper(ch) == dlg_toupper(cmp2)) 334240116Smarcel return TRUE; 335240116Smarcel } 336240116Smarcel#endif 337240116Smarcel } 338240116Smarcel return FALSE; 339240116Smarcel} 340240116Smarcel 341240116Smarcel/* 342240116Smarcel * Find the first uppercase character in the label, which we may use for an 343240116Smarcel * abbreviation. 344240116Smarcel */ 345240116Smarcelint 346240116Smarceldlg_button_to_char(const char *label) 347240116Smarcel{ 348240116Smarcel int cmp = -1; 349240116Smarcel 350240116Smarcel while (*label != 0) { 351240116Smarcel cmp = string_to_char(&label); 352240116Smarcel if (dlg_isupper(cmp)) { 353240116Smarcel break; 354240116Smarcel } 355240116Smarcel } 356240116Smarcel return cmp; 357240116Smarcel} 358240116Smarcel 359240116Smarcel/* 360240116Smarcel * Given a list of button labels, and a character which may be the abbreviation 361240116Smarcel * for one, find it, if it exists. An abbreviation will be the first character 362240116Smarcel * which happens to be capitalized in the label. 363240116Smarcel */ 364240116Smarcelint 365240116Smarceldlg_char_to_button(int ch, const char **labels) 366240116Smarcel{ 367240116Smarcel if (labels != 0) { 368240116Smarcel int j; 369240116Smarcel 370240116Smarcel ch = (int) dlg_toupper(dlg_last_getc()); 371240116Smarcel for (j = 0; labels[j] != 0; ++j) { 372273929Sjmmv int cmp = dlg_button_to_char(labels[j]); 373240116Smarcel if (ch == cmp) { 374240116Smarcel dlg_flush_getc(); 375240116Smarcel return j; 376240116Smarcel } 377240116Smarcel } 378240116Smarcel } 379240116Smarcel return DLG_EXIT_UNKNOWN; 380240116Smarcel} 381240116Smarcel 382240116Smarcelstatic const char * 383240116Smarcelmy_yes_label(void) 384240116Smarcel{ 385240116Smarcel return (dialog_vars.yes_label != NULL) 386240116Smarcel ? dialog_vars.yes_label 387240116Smarcel : _("Yes"); 388240116Smarcel} 389240116Smarcel 390240116Smarcelstatic const char * 391240116Smarcelmy_no_label(void) 392240116Smarcel{ 393240116Smarcel return (dialog_vars.no_label != NULL) 394240116Smarcel ? dialog_vars.no_label 395 : _("No"); 396} 397 398static const char * 399my_ok_label(void) 400{ 401 return (dialog_vars.ok_label != NULL) 402 ? dialog_vars.ok_label 403 : _("OK"); 404} 405 406static const char * 407my_cancel_label(void) 408{ 409 return (dialog_vars.cancel_label != NULL) 410 ? dialog_vars.cancel_label 411 : _("Cancel"); 412} 413 414static const char * 415my_exit_label(void) 416{ 417 return (dialog_vars.exit_label != NULL) 418 ? dialog_vars.exit_label 419 : _("EXIT"); 420} 421 422static const char * 423my_extra_label(void) 424{ 425 return (dialog_vars.extra_label != NULL) 426 ? dialog_vars.extra_label 427 : _("Extra"); 428} 429 430static const char * 431my_help_label(void) 432{ 433 return (dialog_vars.help_label != NULL) 434 ? dialog_vars.help_label 435 : _("Help"); 436} 437 438/* 439 * Return a list of button labels. 440 */ 441const char ** 442dlg_exit_label(void) 443{ 444 const char **result; 445 DIALOG_VARS save; 446 447 if (dialog_vars.extra_button) { 448 dlg_save_vars(&save); 449 dialog_vars.nocancel = TRUE; 450 result = dlg_ok_labels(); 451 dlg_restore_vars(&save); 452 } else { 453 static const char *labels[3]; 454 int n = 0; 455 456 if (!dialog_vars.nook) 457 labels[n++] = my_exit_label(); 458 if (dialog_vars.help_button) 459 labels[n++] = my_help_label(); 460 if (n == 0) 461 labels[n++] = my_exit_label(); 462 labels[n] = 0; 463 464 result = labels; 465 } 466 return result; 467} 468 469/* 470 * Map the given button index for dlg_exit_label() into our exit-code. 471 */ 472int 473dlg_exit_buttoncode(int button) 474{ 475 int result; 476 DIALOG_VARS save; 477 478 dlg_save_vars(&save); 479 dialog_vars.nocancel = TRUE; 480 481 result = dlg_ok_buttoncode(button); 482 483 dlg_restore_vars(&save); 484 485 return result; 486} 487 488const char ** 489dlg_ok_label(void) 490{ 491 static const char *labels[3]; 492 int n = 0; 493 494 labels[n++] = my_ok_label(); 495 if (dialog_vars.help_button) 496 labels[n++] = my_help_label(); 497 labels[n] = 0; 498 return labels; 499} 500 501/* 502 * Return a list of button labels for the OK/Cancel group. 503 */ 504const char ** 505dlg_ok_labels(void) 506{ 507 static const char *labels[5]; 508 int n = 0; 509 510 if (!dialog_vars.nook) 511 labels[n++] = my_ok_label(); 512 if (dialog_vars.extra_button) 513 labels[n++] = my_extra_label(); 514 if (!dialog_vars.nocancel) 515 labels[n++] = my_cancel_label(); 516 if (dialog_vars.help_button) 517 labels[n++] = my_help_label(); 518 labels[n] = 0; 519 return labels; 520} 521 522/* 523 * Map the given button index for dlg_ok_labels() into our exit-code 524 */ 525int 526dlg_ok_buttoncode(int button) 527{ 528 int result = DLG_EXIT_ERROR; 529 int n = !dialog_vars.nook; 530 531 if (!dialog_vars.nook && (button <= 0)) { 532 result = DLG_EXIT_OK; 533 } else if (dialog_vars.extra_button && (button == n++)) { 534 result = DLG_EXIT_EXTRA; 535 } else if (!dialog_vars.nocancel && (button == n++)) { 536 result = DLG_EXIT_CANCEL; 537 } else if (dialog_vars.help_button && (button == n)) { 538 result = DLG_EXIT_HELP; 539 } 540 return result; 541} 542 543/* 544 * Given that we're using dlg_ok_labels() to list buttons, find the next index 545 * in the list of buttons. The 'extra' parameter if negative provides a way to 546 * enumerate extra active areas on the widget. 547 */ 548int 549dlg_next_ok_buttonindex(int current, int extra) 550{ 551 int result = current + 1; 552 553 if (current >= 0 554 && dlg_ok_buttoncode(result) < 0) 555 result = extra; 556 return result; 557} 558 559/* 560 * Similarly, find the previous button index. 561 */ 562int 563dlg_prev_ok_buttonindex(int current, int extra) 564{ 565 int result = current - 1; 566 567 if (result < extra) { 568 for (result = 0; dlg_ok_buttoncode(result + 1) >= 0; ++result) { 569 ; 570 } 571 } 572 return result; 573} 574 575/* 576 * Find the button-index for the "OK" or "Cancel" button, according to 577 * whether --defaultno is given. If --nocancel was given, we always return 578 * the index for "OK". 579 */ 580int 581dlg_defaultno_button(void) 582{ 583 int result = 0; 584 585 if (dialog_vars.defaultno && !dialog_vars.nocancel) { 586 while (dlg_ok_buttoncode(result) != DLG_EXIT_CANCEL) 587 ++result; 588 } 589 return result; 590} 591 592/* 593 * Return a list of buttons for Yes/No labels. 594 */ 595const char ** 596dlg_yes_labels(void) 597{ 598 const char **result; 599 600 if (dialog_vars.extra_button) { 601 result = dlg_ok_labels(); 602 } else { 603 static const char *labels[4]; 604 int n = 0; 605 606 labels[n++] = my_yes_label(); 607 labels[n++] = my_no_label(); 608 if (dialog_vars.help_button) 609 labels[n++] = my_help_label(); 610 labels[n] = 0; 611 612 result = labels; 613 } 614 615 return result; 616} 617 618/* 619 * Map the given button index for dlg_yes_labels() into our exit-code. 620 */ 621int 622dlg_yes_buttoncode(int button) 623{ 624 int result = DLG_EXIT_ERROR; 625 626 if (dialog_vars.extra_button) { 627 result = dlg_ok_buttoncode(button); 628 } else if (button == 0) { 629 result = DLG_EXIT_OK; 630 } else if (button == 1) { 631 result = DLG_EXIT_CANCEL; 632 } else if (button == 2 && dialog_vars.help_button) { 633 result = DLG_EXIT_HELP; 634 } 635 636 return result; 637} 638 639/* 640 * Return the next index in labels[]; 641 */ 642int 643dlg_next_button(const char **labels, int button) 644{ 645 if (labels[button + 1] != 0) 646 ++button; 647 else 648 button = MIN_BUTTON; 649 return button; 650} 651 652/* 653 * Return the previous index in labels[]; 654 */ 655int 656dlg_prev_button(const char **labels, int button) 657{ 658 if (button > MIN_BUTTON) 659 --button; 660 else { 661 while (labels[button + 1] != 0) 662 ++button; 663 } 664 return button; 665} 666