1/* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 * See README.txt for an overview of the Vim source code. 8 */ 9 10/* 11 * (C) 2001,2005 by Marcin Dalecki <martin@dalecki.de> 12 * 13 * Implementation of dialog functions for the Motif GUI variant. 14 * 15 * Note about Lesstif: Apparently lesstif doesn't get the widget layout right, 16 * when using a dynamic scrollbar policy. 17 */ 18 19#include <Xm/Form.h> 20#include <Xm/PushBG.h> 21#include <Xm/Text.h> 22#include <Xm/TextF.h> 23#include <Xm/Label.h> 24#include <Xm/Frame.h> 25#include <Xm/LabelG.h> 26#include <Xm/ToggleBG.h> 27#include <Xm/SeparatoG.h> 28#include <Xm/DialogS.h> 29#include <Xm/List.h> 30#include <Xm/RowColumn.h> 31#include <Xm/AtomMgr.h> 32#include <Xm/Protocols.h> 33 34#include <X11/keysym.h> 35#include <X11/Xatom.h> 36#include <X11/StringDefs.h> 37#include <X11/Intrinsic.h> 38 39#include "vim.h" 40 41extern Widget vimShell; 42 43#ifdef FEAT_MENU 44# define apply_fontlist(w) gui_motif_menu_fontlist(w) 45#else 46# define apply_fontlist(w) 47#endif 48 49/**************************************************************************** 50 * Font selection dialogue implementation. 51 */ 52 53static char wild[3] = "*"; 54 55/* 56 * FIXME: This is a generic function, which should be used throughout the whole 57 * application. 58 * 59 * Add close_callback, which will be called when the user selects close from 60 * the window menu. The close menu item usually activates f.kill which sends a 61 * WM_DELETE_WINDOW protocol request for the window. 62 */ 63 64 static void 65add_cancel_action(Widget shell, XtCallbackProc close_callback, void *arg) 66{ 67 static Atom wmp_atom = 0; 68 static Atom dw_atom = 0; 69 Display *display = XtDisplay(shell); 70 71 /* deactivate the built-in delete response of killing the application */ 72 XtVaSetValues(shell, XmNdeleteResponse, XmDO_NOTHING, NULL); 73 74 /* add a delete window protocol callback instead */ 75 if (!dw_atom) 76 { 77 wmp_atom = XmInternAtom(display, "WM_PROTOCOLS", True); 78 dw_atom = XmInternAtom(display, "WM_DELETE_WINDOW", True); 79 } 80 XmAddProtocolCallback(shell, wmp_atom, dw_atom, close_callback, arg); 81} 82 83#define MAX_FONTS 65535 84#define MAX_FONT_NAME_LEN 256 85#define MAX_ENTRIES_IN_LIST 5000 86#define MAX_DISPLAY_SIZE 150 87#define TEMP_BUF_SIZE 256 88 89enum ListSpecifier 90{ 91 ENCODING, 92 NAME, 93 STYLE, 94 SIZE, 95 NONE 96}; 97 98typedef struct _SharedFontSelData 99{ 100 Widget dialog; 101 Widget ok; 102 Widget cancel; 103 Widget encoding_pulldown; 104 Widget encoding_menu; 105 Widget list[NONE]; 106 Widget name; 107 Widget sample; 108 char **names; /* font name array of arrays */ 109 int num; /* number of font names */ 110 String sel[NONE]; /* selection category */ 111 Boolean in_pixels; /* toggle state - size in pixels */ 112 char *font_name; /* current font name */ 113 XFontStruct *old; /* font data structure for sample display */ 114 XmFontList old_list; /* font data structure for sample display */ 115 Boolean exit; /* used for program exit control */ 116} SharedFontSelData; 117 118/* 119 * Checking access to the font name array for validity. 120 */ 121 static char * 122fn(SharedFontSelData *data, int i) 123{ 124 /* Assertion checks: */ 125 if (data->num < 0) 126 abort(); 127 if (i >= data->num) 128 i = data->num - 1; 129 if (i < 0) 130 i = 0; 131 132 return data->names[i]; 133} 134 135/* 136 * Get a specific substring from a font name. 137 */ 138 static void 139get_part(char *in, int pos, char *out) 140{ 141 int i; 142 int j; 143 144 *out = '\0'; 145 146 for (i = 0; (pos > 0) && (in[i] != '\0'); ++i) 147 if (in[i] == '-') 148 pos--; 149 150 if (in[i] == '\0') 151 return; 152 153 for (j = 0; (in[i] != '-') && (in[i] != '\0'); ++i, ++j) 154 out[j] = in[i]; 155 out[j] = '\0'; 156} 157 158/* 159 * Given a font name this function returns the part used in the first 160 * scroll list. 161 */ 162 static void 163name_part(char *font, char *buf) 164{ 165 char buf2[TEMP_BUF_SIZE]; 166 char buf3[TEMP_BUF_SIZE]; 167 168 get_part(font, 2, buf2); 169 get_part(font, 1, buf3); 170 171 if (*buf3 != NUL) 172 vim_snprintf(buf, TEMP_BUF_SIZE, "%s (%s)", buf2, buf3); 173 else 174 vim_snprintf(buf, TEMP_BUF_SIZE, "%s", buf2); 175} 176 177/* 178 * Given a font name this function returns the part used in the second scroll list. 179 */ 180 static void 181style_part(char *font, char *buf) 182{ 183 char buf2[TEMP_BUF_SIZE]; 184 char buf3[TEMP_BUF_SIZE]; 185 186 get_part(font, 3, buf3); 187 get_part(font, 5, buf2); 188 189 if (!strcmp(buf2, "normal") && !strcmp(buf2, "Normal") 190 && !strcmp(buf2, "NORMAL")) 191 vim_snprintf(buf, TEMP_BUF_SIZE, "%s %s", buf3, buf2); 192 else 193 strcpy(buf, buf3); 194 195 get_part(font, 6, buf2); 196 197 if (buf2[0] != '\0') 198 vim_snprintf(buf3, TEMP_BUF_SIZE, "%s %s", buf, buf2); 199 else 200 strcpy(buf3, buf); 201 202 get_part(font, 4, buf2); 203 204 if (!strcmp(buf2, "o") || !strcmp(buf2, "O")) 205 vim_snprintf(buf, TEMP_BUF_SIZE, "%s oblique", buf3); 206 else if (!strcmp(buf2, "i") || !strcmp(buf2, "I")) 207 vim_snprintf(buf, TEMP_BUF_SIZE, "%s italic", buf3); 208 209 if (!strcmp(buf, " ")) 210 strcpy(buf, "-"); 211} 212 213/* 214 * Given a font name this function returns the part used in the third 215 * scroll list. 216 */ 217 static void 218size_part(char *font, char *buf, int inPixels) 219{ 220 int size; 221 float temp; 222 223 *buf = '\0'; 224 225 if (inPixels) 226 { 227 get_part(font, 7, buf); 228 if (*buf != NUL) 229 { 230 size = atoi(buf); 231 sprintf(buf, "%3d", size); 232 } 233 } 234 else 235 { 236 get_part(font, 8, buf); 237 if (*buf != NUL) 238 { 239 size = atoi(buf); 240 temp = (float)size / 10.0; 241 size = temp; 242 if (buf[strlen(buf) - 1] == '0') 243 sprintf(buf, "%3d", size); 244 else 245 sprintf(buf, "%4.1f", temp); 246 } 247 } 248} 249 250/* 251 * Given a font name this function returns the part used in the choice menu. 252 */ 253 static void 254encoding_part(char *font, char *buf) 255{ 256 char buf1[TEMP_BUF_SIZE]; 257 char buf2[TEMP_BUF_SIZE]; 258 259 *buf = '\0'; 260 261 get_part(font, 13, buf1); 262 get_part(font, 14, buf2); 263 264 if (*buf1 != NUL && *buf2 != NUL) 265 vim_snprintf(buf, TEMP_BUF_SIZE, "%s-%s", buf1, buf2); 266 if (!strcmp(buf, " ")) 267 strcpy(buf, "-"); 268} 269 270/* 271 * Inserts a string into correct sorted position in a list. 272 */ 273 static void 274add_to_list(char **buf, char *item, int *count) 275{ 276 int i; 277 int j; 278 279 if (*count == MAX_ENTRIES_IN_LIST) 280 return; 281 282 /* avoid duplication */ 283 for (i = 0; i < *count; ++i) 284 { 285 if (!strcmp(buf[i], item)) 286 return; 287 } 288 289 /* find order place, but make sure that wild card comes first */ 290 if (!strcmp(item, wild)) 291 i = 0; 292 else 293 for (i = 0; i < *count; ++i) 294 if (strcmp(buf[i], item) > 0 && strcmp(buf[i], wild)) 295 break; 296 297 /* now insert the item */ 298 for (j = *count; j > i; --j) 299 buf[j] = buf[j-1]; 300 buf[i] = XtNewString(item); 301 302 ++(*count); 303} 304 305/* 306 * True if the font matches some field. 307 */ 308 static Boolean 309match(SharedFontSelData *data, enum ListSpecifier l, int i) 310{ 311 char buf[TEMP_BUF_SIZE]; 312 313 /* An empty selection or a wild card matches anything. 314 */ 315 if (!data->sel[l] || !strcmp(data->sel[l], wild)) 316 return True; 317 318 /* chunk out the desired part... */ 319 switch (l) 320 { 321 case ENCODING: 322 encoding_part(fn(data, i), buf); 323 break; 324 325 case NAME: 326 name_part(fn(data, i), buf); 327 break; 328 329 case STYLE: 330 style_part(fn(data, i), buf); 331 break; 332 333 case SIZE: 334 size_part(fn(data, i), buf, data->in_pixels); 335 break; 336 default: 337 ; 338 } 339 340 /* ...and chew it now */ 341 342 return !strcmp(buf, data->sel[l]); 343} 344 345 static Boolean 346proportional(char *font) 347{ 348 char buf[TEMP_BUF_SIZE]; 349 350 get_part(font, 11, buf); 351 352 return !strcmp(buf, "p") || !strcmp(buf, "P"); 353} 354 355 356static void encoding_callback(Widget w, SharedFontSelData *data, 357 XtPointer dummy); 358 359/* 360 * Parse through the fontlist data and set up the three scroll lists. The fix 361 * parameter can be used to exclude a list from any changes. This is used for 362 * updates after selections caused by the users actions. 363 */ 364 static void 365fill_lists(enum ListSpecifier fix, SharedFontSelData *data) 366{ 367 char *list[NONE][MAX_ENTRIES_IN_LIST]; 368 int count[NONE]; 369 char buf[TEMP_BUF_SIZE]; 370 XmString items[MAX_ENTRIES_IN_LIST]; 371 int i; 372 int idx; 373 374 for (idx = (int)ENCODING; idx < (int)NONE; ++idx) 375 count[idx] = 0; 376 377 /* First we insert the wild char into every single list. */ 378 if (fix != ENCODING) 379 add_to_list(list[ENCODING], wild, &count[ENCODING]); 380 if (fix != NAME) 381 add_to_list(list[NAME], wild, &count[NAME]); 382 if (fix != STYLE) 383 add_to_list(list[STYLE], wild, &count[STYLE]); 384 if (fix != SIZE) 385 add_to_list(list[SIZE], wild, &count[SIZE]); 386 387 for (i = 0; i < data->num && i < MAX_ENTRIES_IN_LIST; i++) 388 { 389 if (proportional(fn(data, i))) 390 continue; 391 392 if (fix != ENCODING 393 && match(data, NAME, i) 394 && match(data, STYLE, i) 395 && match(data, SIZE, i)) 396 { 397 encoding_part(fn(data, i), buf); 398 add_to_list(list[ENCODING], buf, &count[ENCODING]); 399 } 400 401 if (fix != NAME 402 && match(data, ENCODING, i) 403 && match(data, STYLE, i) 404 && match(data, SIZE, i)) 405 { 406 name_part(fn(data, i), buf); 407 add_to_list(list[NAME], buf, &count[NAME]); 408 } 409 410 if (fix != STYLE 411 && match(data, ENCODING, i) 412 && match(data, NAME, i) 413 && match(data, SIZE, i)) 414 { 415 style_part(fn(data, i), buf); 416 add_to_list(list[STYLE], buf, &count[STYLE]); 417 } 418 419 if (fix != SIZE 420 && match(data, ENCODING, i) 421 && match(data, NAME, i) 422 && match(data, STYLE, i)) 423 { 424 size_part(fn(data, i), buf, data->in_pixels); 425 add_to_list(list[SIZE], buf, &count[SIZE]); 426 } 427 } 428 429 /* 430 * And now do the preselection in all lists where there was one: 431 */ 432 433 if (fix != ENCODING) 434 { 435 Cardinal n_items; 436 WidgetList children; 437 Widget selected_button = 0; 438 439 /* Get and update the current button list. */ 440 XtVaGetValues(data->encoding_pulldown, 441 XmNchildren, &children, 442 XmNnumChildren, &n_items, 443 NULL); 444 445 for (i = 0; i < count[ENCODING]; ++i) 446 { 447 Widget button; 448 449 items[i] = XmStringCreateLocalized(list[ENCODING][i]); 450 451 if (i < (int)n_items) 452 { 453 /* recycle old button */ 454 XtVaSetValues(children[i], 455 XmNlabelString, items[i], 456 XmNuserData, i, 457 NULL); 458 button = children[i]; 459 } 460 else 461 { 462 /* create a new button */ 463 button = XtVaCreateManagedWidget("button", 464 xmPushButtonGadgetClass, 465 data->encoding_pulldown, 466 XmNlabelString, items[i], 467 XmNuserData, i, 468 NULL); 469 XtAddCallback(button, XmNactivateCallback, 470 (XtCallbackProc) encoding_callback, (XtPointer) data); 471 XtManageChild(button); 472 } 473 474 if (data->sel[ENCODING]) 475 { 476 if (!strcmp(data->sel[ENCODING], list[ENCODING][i])) 477 selected_button = button; 478 } 479 XtFree(list[ENCODING][i]); 480 } 481 482 /* Destroy all the outstanding menu items. 483 */ 484 for (i = count[ENCODING]; i < (int)n_items; ++i) 485 { 486 XtUnmanageChild(children[i]); 487 XtDestroyWidget(children[i]); 488 } 489 490 /* Preserve the current selection visually. 491 */ 492 if (selected_button) 493 { 494 XtVaSetValues(data->encoding_menu, 495 XmNmenuHistory, selected_button, 496 NULL); 497 } 498 499 for (i = 0; i < count[ENCODING]; ++i) 500 XmStringFree(items[i]); 501 } 502 503 /* 504 * Now loop trough the remaining lists and set them up. 505 */ 506 for (idx = (int)NAME; idx < (int)NONE; ++idx) 507 { 508 Widget w; 509 510 if (fix == (enum ListSpecifier)idx) 511 continue; 512 513 switch ((enum ListSpecifier)idx) 514 { 515 case NAME: 516 w = data->list[NAME]; 517 break; 518 case STYLE: 519 w = data->list[STYLE]; 520 break; 521 case SIZE: 522 w = data->list[SIZE]; 523 break; 524 default: 525 w = (Widget)0; /* for lint */ 526 } 527 528 for (i = 0; i < count[idx]; ++i) 529 { 530 items[i] = XmStringCreateLocalized(list[idx][i]); 531 XtFree(list[idx][i]); 532 } 533 XmListDeleteAllItems(w); 534 XmListAddItems(w, items, count[idx], 1); 535 if (data->sel[idx]) 536 { 537 XmStringFree(items[0]); 538 items[0] = XmStringCreateLocalized(data->sel[idx]); 539 XmListSelectItem(w, items[0], False); 540 XmListSetBottomItem(w, items[0]); 541 } 542 for (i = 0; i < count[idx]; ++i) 543 XmStringFree(items[i]); 544 } 545} 546 547 static void 548stoggle_callback(Widget w UNUSED, 549 SharedFontSelData *data, 550 XmToggleButtonCallbackStruct *call_data) 551{ 552 int i, do_sel; 553 char newSize[TEMP_BUF_SIZE]; 554 XmString str; 555 556 if (call_data->reason != (int)XmCR_VALUE_CHANGED) 557 return; 558 559 do_sel = (data->sel[SIZE] != NULL) && strcmp(data->sel[SIZE], wild); 560 561 for (i = 0; do_sel && (i < data->num); i++) 562 if (match(data, ENCODING, i) 563 && match(data, NAME, i) 564 && match(data, STYLE, i) 565 && match(data, SIZE, i)) 566 { 567 size_part(fn(data, i), newSize, !data->in_pixels); 568 break; 569 } 570 571 data->in_pixels = !data->in_pixels; 572 573 if (data->sel[SIZE]) 574 XtFree(data->sel[SIZE]); 575 data->sel[SIZE] = NULL; 576 fill_lists(NONE, data); 577 578 if (do_sel) 579 { 580 str = XmStringCreateLocalized(newSize); 581 XmListSelectItem(data->list[SIZE], str, True); 582 XmListSetBottomItem(data->list[SIZE], str); 583 XmStringFree(str); 584 } 585} 586 587/* 588 * Show the currently selected font in the sample text label. 589 */ 590 static void 591display_sample(SharedFontSelData *data) 592{ 593 Arg args[2]; 594 int n; 595 XFontStruct * font; 596 XmFontList font_list; 597 Display * display; 598 XmString str; 599 600 display = XtDisplay(data->dialog); 601 font = XLoadQueryFont(display, data->font_name); 602 font_list = gui_motif_create_fontlist(font); 603 604 n = 0; 605 str = XmStringCreateLocalized("AaBbZzYy 0123456789"); 606 XtSetArg(args[n], XmNlabelString, str); n++; 607 XtSetArg(args[n], XmNfontList, font_list); n++; 608 609 XtSetValues(data->sample, args, n); 610 XmStringFree(str); 611 612 if (data->old) 613 { 614 XFreeFont(display, data->old); 615 XmFontListFree(data->old_list); 616 } 617 data->old = font; 618 data->old_list = font_list; 619} 620 621 622 static Boolean 623do_choice(Widget w, 624 SharedFontSelData *data, 625 XmListCallbackStruct *call_data, 626 enum ListSpecifier which) 627{ 628 char *sel; 629 630 XmStringGetLtoR(call_data->item, XmSTRING_DEFAULT_CHARSET, &sel); 631 632 if (!data->sel[which]) 633 data->sel[which] = XtNewString(sel); 634 else 635 { 636 if (!strcmp(data->sel[which], sel)) 637 { 638 /* unselecting current selection */ 639 XtFree(data->sel[which]); 640 data->sel[which] = NULL; 641 if (w) 642 XmListDeselectItem(w, call_data->item); 643 } 644 else 645 { 646 XtFree(data->sel[which]); 647 data->sel[which] = XtNewString(sel); 648 } 649 } 650 XtFree(sel); 651 652 fill_lists(which, data); 653 654 /* If there is a font selection, we display it. */ 655 if (data->sel[ENCODING] 656 && data->sel[NAME] 657 && data->sel[STYLE] 658 && data->sel[SIZE] 659 && strcmp(data->sel[ENCODING], wild) 660 && strcmp(data->sel[NAME], wild) 661 && strcmp(data->sel[STYLE], wild) 662 && strcmp(data->sel[SIZE], wild)) 663 { 664 int i; 665 666 if (data->font_name) 667 XtFree(data->font_name); 668 data->font_name = NULL; 669 670 for (i = 0; i < data->num; i++) 671 { 672 if (match(data, ENCODING, i) 673 && match(data, NAME, i) 674 && match(data, STYLE, i) 675 && match(data, SIZE, i)) 676 { 677 data->font_name = XtNewString(fn(data, i)); 678 break; 679 } 680 } 681 682 if (data->font_name) 683 { 684 XmTextSetString(data->name, data->font_name); 685 display_sample(data); 686 } 687 else 688 do_dialog(VIM_ERROR, 689 (char_u *)_("Error"), 690 (char_u *)_("Invalid font specification"), 691 (char_u *)_("&Dismiss"), 1, NULL); 692 693 return True; 694 } 695 else 696 { 697 int n; 698 XmString str; 699 Arg args[4]; 700 char *nomatch_msg = _("no specific match"); 701 702 n = 0; 703 str = XmStringCreateLocalized(nomatch_msg); 704 XtSetArg(args[n], XmNlabelString, str); ++n; 705 XtSetValues(data->sample, args, n); 706 apply_fontlist(data->sample); 707 XmTextSetString(data->name, nomatch_msg); 708 XmStringFree(str); 709 710 return False; 711 } 712} 713 714 static void 715encoding_callback(Widget w, 716 SharedFontSelData *data, 717 XtPointer dummy UNUSED) 718{ 719 XmString str; 720 XmListCallbackStruct fake_data; 721 722 XtVaGetValues(w, XmNlabelString, &str, NULL); 723 724 if (!str) 725 return; 726 727 fake_data.item = str; 728 729 do_choice(0, data, &fake_data, ENCODING); 730} 731 732 static void 733name_callback(Widget w, 734 SharedFontSelData *data, 735 XmListCallbackStruct *call_data) 736{ 737 do_choice(w, data, call_data, NAME); 738} 739 740 static void 741style_callback(Widget w, 742 SharedFontSelData *data, 743 XmListCallbackStruct *call_data) 744{ 745 do_choice(w, data, call_data, STYLE); 746} 747 748 static void 749size_callback(Widget w, 750 SharedFontSelData *data, 751 XmListCallbackStruct *call_data) 752{ 753 do_choice(w, data, call_data, SIZE); 754} 755 756 static void 757cancel_callback(Widget w UNUSED, 758 SharedFontSelData *data, 759 XmListCallbackStruct *call_data UNUSED) 760{ 761 if (data->sel[ENCODING]) 762 { 763 XtFree(data->sel[ENCODING]); 764 data->sel[ENCODING] = NULL; 765 } 766 if (data->sel[NAME]) 767 { 768 XtFree(data->sel[NAME]); 769 data->sel[NAME] = NULL; 770 } 771 if (data->sel[STYLE]) 772 { 773 XtFree(data->sel[STYLE]); 774 data->sel[STYLE] = NULL; 775 } 776 if (data->sel[SIZE]) 777 { 778 XtFree(data->sel[SIZE]); 779 data->sel[SIZE] = NULL; 780 } 781 782 if (data->font_name) 783 XtFree(data->font_name); 784 data->font_name = NULL; 785 786 data->num = 0; 787 XFreeFontNames(data->names); 788 data->names = NULL; 789 data->exit = True; 790} 791 792 static void 793ok_callback(Widget w UNUSED, 794 SharedFontSelData *data, 795 XmPushButtonCallbackStruct *call_data UNUSED) 796{ 797 char *pattern; 798 char **name; 799 int i; 800 801 pattern = XmTextGetString(data->name); 802 name = XListFonts(XtDisplay(data->dialog), pattern, 1, &i); 803 XtFree(pattern); 804 805 if (i != 1) 806 { 807 do_dialog(VIM_ERROR, 808 (char_u *)_("Error"), 809 (char_u *)_("Invalid font specification"), 810 (char_u *)_("&Dismiss"), 1, NULL); 811 XFreeFontNames(name); 812 } 813 else 814 { 815 if (data->font_name) 816 XtFree(data->font_name); 817 data->font_name = XtNewString(name[0]); 818 819 if (data->sel[ENCODING]) 820 { 821 XtFree(data->sel[ENCODING]); 822 data->sel[ENCODING] = NULL; 823 } 824 if (data->sel[NAME]) 825 { 826 XtFree(data->sel[NAME]); 827 data->sel[NAME] = NULL; 828 } 829 if (data->sel[STYLE]) 830 { 831 XtFree(data->sel[STYLE]); 832 data->sel[STYLE] = NULL; 833 } 834 if (data->sel[SIZE]) 835 { 836 XtFree(data->sel[SIZE]); 837 data->sel[SIZE] = NULL; 838 } 839 840 XFreeFontNames(name); 841 842 data->num = 0; 843 XFreeFontNames(data->names); 844 data->names = NULL; 845 data->exit = True; 846 } 847} 848 849/* 850 * Returns pointer to an ASCII character string that contains the name of the 851 * selected font (in X format for naming fonts); it is the users responsibility 852 * to free the space allocated to this string. 853 */ 854 char_u * 855gui_xm_select_font(char_u *current) 856{ 857 static SharedFontSelData _data; 858 859 Widget parent; 860 Widget form; 861 Widget separator; 862 Widget sub_form; 863 Widget size_toggle; 864 Widget name; 865 Widget disp_frame; 866 Widget frame; 867 Arg args[64]; 868 int n; 869 XmString str; 870 char big_font[MAX_FONT_NAME_LEN]; 871 SharedFontSelData *data; 872 873 data = &_data; 874 875 parent = vimShell; 876 data->names = XListFonts(XtDisplay(parent), "-*-*-*-*-*-*-*-*-*-*-*-*-*-*", 877 MAX_FONTS, &data->num); 878 879 /* 880 * Find the name of the biggest font less than the given limit 881 * MAX_DISPLAY_SIZE used to set up the initial height of the display 882 * widget. 883 */ 884 885 { 886 int i; 887 int max; 888 int idx = 0; 889 int size; 890 char buf[128]; 891 892 for (i = 0, max = 0; i < data->num; i++) 893 { 894 get_part(fn(data, i), 7, buf); 895 size = atoi(buf); 896 if ((size > max) && (size < MAX_DISPLAY_SIZE)) 897 { 898 idx = i; 899 max = size; 900 } 901 } 902 strcpy(big_font, fn(data, idx)); 903 } 904 data->old = XLoadQueryFont(XtDisplay(parent), big_font); 905 data->old_list = gui_motif_create_fontlist(data->old); 906 907 /* Set the title of the Dialog window. */ 908 data->dialog = XmCreateDialogShell(parent, "fontSelector", NULL, 0); 909 str = XmStringCreateLocalized(_("Vim - Font Selector")); 910 911 /* Create form popup dialog widget. */ 912 form = XtVaCreateWidget("form", 913 xmFormWidgetClass, data->dialog, 914 XmNdialogTitle, str, 915 XmNautoUnmanage, False, 916 XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL, 917 NULL); 918 XmStringFree(str); 919 920 sub_form = XtVaCreateManagedWidget("subForm", 921 xmFormWidgetClass, form, 922 XmNbottomAttachment, XmATTACH_FORM, 923 XmNbottomOffset, 4, 924 XmNrightAttachment, XmATTACH_FORM, 925 XmNrightOffset, 4, 926 XmNtopAttachment, XmATTACH_FORM, 927 XmNtopOffset, 4, 928 XmNorientation, XmVERTICAL, 929 NULL); 930 931 data->ok = XtVaCreateManagedWidget(_("OK"), 932 xmPushButtonGadgetClass, sub_form, 933 XmNleftAttachment, XmATTACH_FORM, 934 XmNrightAttachment, XmATTACH_FORM, 935 XmNtopAttachment, XmATTACH_FORM, 936 XmNtopOffset, 4, 937 NULL); 938 apply_fontlist(data->ok); 939 940 data->cancel = XtVaCreateManagedWidget(_("Cancel"), 941 xmPushButtonGadgetClass, sub_form, 942 XmNrightAttachment, XmATTACH_FORM, 943 XmNleftAttachment, XmATTACH_FORM, 944 XmNtopAttachment, XmATTACH_WIDGET, 945 XmNtopWidget, data->ok, 946 XmNtopOffset, 4, 947 XmNshowAsDefault, True, 948 NULL); 949 apply_fontlist(data->cancel); 950 951 /* Create the separator for beauty. */ 952 n = 0; 953 XtSetArg(args[n], XmNorientation, XmVERTICAL); n++; 954 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; 955 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; 956 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; 957 XtSetArg(args[n], XmNrightWidget, sub_form); n++; 958 XtSetArg(args[n], XmNrightOffset, 4); n++; 959 separator = XmCreateSeparatorGadget(form, "separator", args, n); 960 XtManageChild(separator); 961 962 /* Create font name text widget and the corresponding label. */ 963 data->name = XtVaCreateManagedWidget("fontName", 964 xmTextWidgetClass, form, 965 XmNbottomAttachment, XmATTACH_FORM, 966 XmNbottomOffset, 4, 967 XmNleftAttachment, XmATTACH_FORM, 968 XmNleftOffset, 4, 969 XmNrightAttachment, XmATTACH_WIDGET, 970 XmNrightWidget, separator, 971 XmNrightOffset, 4, 972 XmNeditable, False, 973 XmNeditMode, XmSINGLE_LINE_EDIT, 974 XmNmaxLength, MAX_FONT_NAME_LEN, 975 XmNcolumns, 60, 976 NULL); 977 978 str = XmStringCreateLocalized(_("Name:")); 979 name = XtVaCreateManagedWidget("fontNameLabel", 980 xmLabelGadgetClass, form, 981 XmNlabelString, str, 982 XmNuserData, data->name, 983 XmNleftAttachment, XmATTACH_OPPOSITE_WIDGET, 984 XmNleftWidget, data->name, 985 XmNbottomAttachment, XmATTACH_WIDGET, 986 XmNbottomWidget, data->name, 987 XmNtopOffset, 1, 988 NULL); 989 XmStringFree(str); 990 apply_fontlist(name); 991 992 /* create sample display label widget */ 993 disp_frame = XtVaCreateManagedWidget("sampleFrame", 994 xmFrameWidgetClass, form, 995 XmNshadowType, XmSHADOW_ETCHED_IN, 996 XmNleftAttachment, XmATTACH_FORM, 997 XmNleftOffset, 4, 998 XmNbottomAttachment, XmATTACH_WIDGET, 999 XmNbottomWidget, name, 1000 XmNrightAttachment, XmATTACH_WIDGET, 1001 XmNrightWidget, separator, 1002 XmNrightOffset, 4, 1003 XmNalignment, XmALIGNMENT_BEGINNING, 1004 NULL); 1005 1006 data->sample = XtVaCreateManagedWidget("sampleLabel", 1007 xmLabelWidgetClass, disp_frame, 1008 XmNleftAttachment, XmATTACH_FORM, 1009 XmNtopAttachment, XmATTACH_FORM, 1010 XmNbottomAttachment, XmATTACH_FORM, 1011 XmNrightAttachment, XmATTACH_FORM, 1012 XmNalignment, XmALIGNMENT_BEGINNING, 1013 XmNrecomputeSize, False, 1014 XmNfontList, data->old_list, 1015 NULL); 1016 1017 /* create toggle button */ 1018 str = XmStringCreateLocalized(_("Show size in Points")); 1019 size_toggle = XtVaCreateManagedWidget("sizeToggle", 1020 xmToggleButtonGadgetClass, form, 1021 XmNlabelString, str, 1022 XmNleftAttachment, XmATTACH_FORM, 1023 XmNleftOffset, 4, 1024 XmNbottomAttachment, XmATTACH_WIDGET, 1025 XmNbottomWidget, disp_frame, 1026 XmNbottomOffset, 4, 1027 NULL); 1028 XmStringFree(str); 1029 apply_fontlist(size_toggle); 1030 XtManageChild(size_toggle); 1031 1032 /* Encoding pulldown menu. 1033 */ 1034 1035 data->encoding_pulldown = XmCreatePulldownMenu(form, 1036 "encodingPulldown", NULL, 0); 1037 str = XmStringCreateLocalized(_("Encoding:")); 1038 n = 0; 1039 XtSetArg(args[n], XmNsubMenuId, data->encoding_pulldown); ++n; 1040 XtSetArg(args[n], XmNlabelString, str); ++n; 1041 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n; 1042 XtSetArg(args[n], XmNleftOffset, 4); ++n; 1043 XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); ++n; 1044 XtSetArg(args[n], XmNbottomWidget, size_toggle); ++n; 1045 XtSetArg(args[n], XmNbottomOffset, 4); ++n; 1046 XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); ++n; 1047 XtSetArg(args[n], XmNrightWidget, separator); ++n; 1048 XtSetArg(args[n], XmNrightOffset, 4); ++n; 1049 data->encoding_menu = XmCreateOptionMenu(form, "encodingMenu", args, n); 1050 XmStringFree(str); 1051 XmAddTabGroup(data->encoding_menu); 1052 1053 /* 1054 * Create scroll list widgets in a separate subform used to manage the 1055 * different sizes of the lists. 1056 */ 1057 1058 sub_form = XtVaCreateManagedWidget("subForm", 1059 xmFormWidgetClass, form, 1060 XmNbottomAttachment, XmATTACH_WIDGET, 1061 XmNbottomWidget, data->encoding_menu, 1062 XmNbottomOffset, 4, 1063 XmNleftAttachment, XmATTACH_FORM, 1064 XmNleftOffset, 4, 1065 XmNrightAttachment, XmATTACH_WIDGET, 1066 XmNrightWidget, separator, 1067 XmNrightOffset, 4, 1068 XmNtopAttachment, XmATTACH_FORM, 1069 XmNtopOffset, 2, 1070 XmNorientation, XmVERTICAL, 1071 NULL); 1072 1073 /* font list */ 1074 frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, sub_form, 1075 XmNshadowThickness, 0, 1076 XmNtopAttachment, XmATTACH_FORM, 1077 XmNbottomAttachment, XmATTACH_FORM, 1078 XmNleftAttachment, XmATTACH_FORM, 1079 XmNrightAttachment, XmATTACH_POSITION, 1080 XmNrightPosition, 50, 1081 NULL); 1082 1083 str = XmStringCreateLocalized(_("Font:")); 1084 name = XtVaCreateManagedWidget("nameListLabel", xmLabelGadgetClass, frame, 1085 XmNchildType, XmFRAME_TITLE_CHILD, 1086 XmNchildVerticalAlignment, XmALIGNMENT_CENTER, 1087 XmNchildHorizontalAlignment, XmALIGNMENT_BEGINNING, 1088 XmNlabelString, str, 1089 NULL); 1090 XmStringFree(str); 1091 apply_fontlist(name); 1092 1093 n = 0; 1094 XtSetArg(args[n], XmNvisibleItemCount, 8); ++n; 1095 XtSetArg(args[n], XmNresizable, True); ++n; 1096 XtSetArg(args[n], XmNlistSizePolicy, XmCONSTANT); ++n; 1097 XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n; 1098#ifdef LESSTIF_VERSION 1099 XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmSTATIC); ++n; 1100#endif 1101 data->list[NAME] = XmCreateScrolledList(frame, "fontList", args, n); 1102 XtVaSetValues(name, XmNuserData, data->list[NAME], NULL); 1103 1104 /* style list */ 1105 frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, sub_form, 1106 XmNshadowThickness, 0, 1107 XmNtopAttachment, XmATTACH_FORM, 1108 XmNbottomAttachment, XmATTACH_FORM, 1109 XmNleftAttachment, XmATTACH_POSITION, 1110 XmNleftPosition, 50, 1111 XmNleftOffset, 4, 1112 XmNrightAttachment, XmATTACH_POSITION, 1113 XmNrightPosition, 80, 1114 NULL); 1115 1116 str = XmStringCreateLocalized(_("Style:")); 1117 name = XtVaCreateManagedWidget("styleListLabel", xmLabelWidgetClass, frame, 1118 XmNchildType, XmFRAME_TITLE_CHILD, 1119 XmNchildVerticalAlignment, XmALIGNMENT_CENTER, 1120 XmNchildHorizontalAlignment, XmALIGNMENT_BEGINNING, 1121 XmNlabelString, str, 1122 NULL); 1123 XmStringFree(str); 1124 apply_fontlist(name); 1125 1126 n = 0; 1127 XtSetArg(args[n], XmNvisibleItemCount, 8); ++n; 1128 XtSetArg(args[n], XmNresizable, True); ++n; 1129 XtSetArg(args[n], XmNlistSizePolicy, XmCONSTANT); ++n; 1130 XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n; 1131#ifdef LESSTIF_VERSION 1132 XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmSTATIC); ++n; 1133#endif 1134 data->list[STYLE] = XmCreateScrolledList(frame, "styleList", args, n); 1135 XtVaSetValues(name, XmNuserData, data->list[STYLE], NULL); 1136 1137 /* size list */ 1138 frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, sub_form, 1139 XmNshadowThickness, 0, 1140 XmNtopAttachment, XmATTACH_FORM, 1141 XmNbottomAttachment, XmATTACH_FORM, 1142 XmNleftAttachment, XmATTACH_POSITION, 1143 XmNleftPosition, 80, 1144 XmNleftOffset, 4, 1145 XmNrightAttachment, XmATTACH_FORM, 1146 NULL); 1147 1148 str = XmStringCreateLocalized(_("Size:")); 1149 name = XtVaCreateManagedWidget("sizeListLabel", xmLabelGadgetClass, frame, 1150 XmNchildType, XmFRAME_TITLE_CHILD, 1151 XmNchildVerticalAlignment, XmALIGNMENT_CENTER, 1152 XmNchildHorizontalAlignment, XmALIGNMENT_BEGINNING, 1153 XmNlabelString, str, 1154 NULL); 1155 XmStringFree(str); 1156 apply_fontlist(name); 1157 1158 n = 0; 1159 XtSetArg(args[n], XmNvisibleItemCount, 8); ++n; 1160 XtSetArg(args[n], XmNresizable, True); ++n; 1161 XtSetArg(args[n], XmNlistSizePolicy, XmCONSTANT); ++n; 1162 XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n; 1163#ifdef LESSTIF_VERSION 1164 XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmSTATIC); ++n; 1165#endif 1166 data->list[SIZE] = XmCreateScrolledList(frame, "sizeList", args, n); 1167 XtVaSetValues(name, XmNuserData, data->list[SIZE], NULL); 1168 1169 /* update form widgets cancel button */ 1170 XtVaSetValues(form, XmNcancelButton, data->cancel, NULL); 1171 1172 XtAddCallback(size_toggle, XmNvalueChangedCallback, 1173 (XtCallbackProc)stoggle_callback, (XtPointer)data); 1174 XtAddCallback(data->list[NAME], XmNbrowseSelectionCallback, 1175 (XtCallbackProc)name_callback, (XtPointer)data); 1176 XtAddCallback(data->list[STYLE], XmNbrowseSelectionCallback, 1177 (XtCallbackProc)style_callback, (XtPointer)data); 1178 XtAddCallback(data->list[SIZE], XmNbrowseSelectionCallback, 1179 (XtCallbackProc)size_callback, (XtPointer)data); 1180 XtAddCallback(data->ok, XmNactivateCallback, 1181 (XtCallbackProc)ok_callback, (XtPointer)data); 1182 XtAddCallback(data->cancel, XmNactivateCallback, 1183 (XtCallbackProc)cancel_callback, (XtPointer)data); 1184 1185 XmProcessTraversal(data->list[NAME], XmTRAVERSE_CURRENT); 1186 1187 /* setup tabgroups */ 1188 1189 XmAddTabGroup(data->list[NAME]); 1190 XmAddTabGroup(data->list[STYLE]); 1191 XmAddTabGroup(data->list[SIZE]); 1192 XmAddTabGroup(size_toggle); 1193 XmAddTabGroup(data->name); 1194 XmAddTabGroup(data->ok); 1195 XmAddTabGroup(data->cancel); 1196 1197 add_cancel_action(data->dialog, (XtCallbackProc)cancel_callback, data); 1198 1199 /* Preset selection data. */ 1200 1201 data->exit = False; 1202 data->in_pixels= True; 1203 data->sel[ENCODING] = NULL; 1204 data->sel[NAME] = NULL; 1205 data->sel[STYLE] = NULL; 1206 data->sel[SIZE] = NULL; 1207 data->font_name = NULL; 1208 1209 /* set up current font parameters */ 1210 if (current && current[0] != '\0') 1211 { 1212 int i; 1213 char **names; 1214 1215 names = XListFonts(XtDisplay(form), (char *) current, 1, &i); 1216 1217 if (i != 0) 1218 { 1219 char namebuf[TEMP_BUF_SIZE]; 1220 char stylebuf[TEMP_BUF_SIZE]; 1221 char sizebuf[TEMP_BUF_SIZE]; 1222 char encodingbuf[TEMP_BUF_SIZE]; 1223 char *found; 1224 1225 found = names[0]; 1226 1227 name_part(found, namebuf); 1228 style_part(found, stylebuf); 1229 size_part(found, sizebuf, data->in_pixels); 1230 encoding_part(found, encodingbuf); 1231 1232 if (*namebuf != NUL 1233 && *stylebuf != NUL 1234 && *sizebuf != NUL 1235 && *encodingbuf != NUL) 1236 { 1237 data->sel[NAME] = XtNewString(namebuf); 1238 data->sel[STYLE] = XtNewString(stylebuf); 1239 data->sel[SIZE] = XtNewString(sizebuf); 1240 data->sel[ENCODING] = XtNewString(encodingbuf); 1241 data->font_name = XtNewString(names[0]); 1242 display_sample(data); 1243 XmTextSetString(data->name, data->font_name); 1244 } 1245 else 1246 { 1247 /* We can't preset a symbolic name, which isn't a full font 1248 * description. Therefore we just behave the same way as if the 1249 * user didn't have selected anything thus far. 1250 * 1251 * Unfortunately there is no known way to expand an abbreviated 1252 * font name. 1253 */ 1254 1255 data->font_name = NULL; 1256 } 1257 } 1258 XFreeFontNames(names); 1259 } 1260 1261 fill_lists(NONE, data); 1262 1263 /* Unfortunately LessTif doesn't align the list widget's properly. I don't 1264 * have currently any idea how to fix this problem. 1265 */ 1266 XtManageChild(data->list[NAME]); 1267 XtManageChild(data->list[STYLE]); 1268 XtManageChild(data->list[SIZE]); 1269 XtManageChild(data->encoding_menu); 1270 manage_centered(form); 1271 1272 /* modal event loop */ 1273 while (!data->exit) 1274 XtAppProcessEvent(XtWidgetToApplicationContext(data->dialog), 1275 (XtInputMask)XtIMAll); 1276 1277 if (data->old) 1278 { 1279 XFreeFont(XtDisplay(data->dialog), data->old); 1280 XmFontListFree(data->old_list); 1281 } 1282 XtDestroyWidget(data->dialog); 1283 1284 gui_motif_synch_fonts(); 1285 1286 return (char_u *) data->font_name; 1287} 1288