1 /*- 2 * Copyright (c) 1996 3 * Rob Zimmermann. All rights reserved. 4 * Copyright (c) 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #ifndef lint 13 static const char sccsid[] = "Id: m_options.c,v 8.22 2003/11/05 17:09:59 skimo Exp (Berkeley) Date: 2003/11/05 17:09:59 "; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/queue.h> 18 19 #include <X11/X.h> 20 #include <X11/Intrinsic.h> 21 #include <Xm/DialogS.h> 22 #include <Xm/Form.h> 23 #include <Xm/Frame.h> 24 #include <Xm/LabelG.h> 25 #include <Xm/PushBG.h> 26 #include <Xm/TextF.h> 27 #include <Xm/ToggleBG.h> 28 #include <Xm/RowColumn.h> 29 30 #include <bitstring.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 #undef LOCK_SUCCESS 36 #include "../common/common.h" 37 #include "../ipc/ip.h" 38 #include "m_motif.h" 39 40 extern int vi_ofd; 41 42 static void set_opt __P((Widget, XtPointer, XtPointer)); 43 44 45 /* constants */ 46 47 #if defined(SelfTest) 48 49 /* in production, get these from the resource list */ 50 51 #define toggleColumns 6 52 53 #endif 54 55 56 /* 57 * global data 58 */ 59 60 static Widget preferences = NULL; 61 62 static optData display[] = { 63 { optToggle, "comment", }, 64 { optToggle, "flash", }, 65 { optToggle, "leftright", }, 66 { optToggle, "list", }, 67 { optToggle, "number", }, 68 { optToggle, "octal", }, 69 { optToggle, "ruler", }, 70 { optToggle, "showmode", }, 71 { optToggle, "slowopen", }, 72 { optToggle, "verbose", }, 73 { optToggle, "windowname", }, 74 { optTerminator, }, 75 }, display_int[] = { 76 { optInteger, "report", }, 77 { optInteger, "scroll", }, 78 { optInteger, "shiftwidth", }, 79 { optInteger, "sidescroll", }, 80 { optInteger, "tabstop", }, 81 { optInteger, "window", }, 82 { optTerminator, }, 83 }, display_str[] = { 84 { optString, "noprint", }, 85 { optString, "print", }, 86 { optTerminator, }, 87 }, files[] = { 88 { optToggle, "autowrite", }, 89 { optToggle, "lock", }, 90 { optToggle, "readonly", }, 91 { optToggle, "writeany", }, 92 { optTerminator, }, 93 }, files_str[] = { 94 { optString, "backup", }, 95 { optString, "path", }, 96 { optTerminator, }, 97 }, general[] = { 98 { optToggle, "exrc", }, 99 { optToggle, "lisp", }, 100 { optToggle, "modeline", }, 101 { optToggle, "sourceany", }, 102 { optToggle, "tildeop", }, 103 { optTerminator, }, 104 }, general_int[] = { 105 { optInteger, "taglength", }, 106 { optTerminator, }, 107 }, general_str[] = { 108 { optString, "cdpath", }, 109 { optString, "directory", }, 110 { optString, "msgcat", }, 111 { optString, "recdir", }, 112 { optString, "shell", }, 113 { optString, "shellmeta", }, 114 { optString, "tags", }, 115 { optTerminator, }, 116 }, input[] = { 117 { optToggle, "altwerase", }, 118 { optToggle, "autoindent", }, 119 { optToggle, "remap", }, 120 { optToggle, "showmatch", }, 121 { optToggle, "ttywerase", }, 122 { optTerminator, }, 123 }, input_int[] = { 124 { optInteger, "escapetime", }, 125 { optInteger, "keytime", }, 126 { optInteger, "matchtime", }, 127 { optInteger, "timeout", }, 128 { optInteger, "wraplen", }, 129 { optInteger, "wrapmargin", }, 130 { optTerminator, }, 131 }, input_str[] = { 132 { optString, "cedit", }, 133 { optString, "filec", }, 134 { optTerminator, }, 135 }, search[] = { 136 { optToggle, "extended", }, 137 { optToggle, "iclower", }, 138 { optToggle, "ignorecase", }, 139 { optToggle, "magic", }, 140 { optToggle, "searchincr", }, 141 { optToggle, "wrapscan", }, 142 { optTerminator, }, 143 }, search_str[] = { 144 { optString, "paragraphs", }, 145 { optString, "sections", }, 146 { optTerminator, }, 147 }; 148 149 /* ********* NOTE *********** 150 * Sheet 0 will always be shown first. It does not matter to the Xt code 151 * which sheet that is, so it ought to be the one users interact with most. 152 * Best guess is that's general editor options, but it might be Search/re. 153 * ********* NOTE *********** 154 */ 155 static optSheet sheets[] = { 156 { "Display", 157 "These options control how text is displayed on the screen", 158 NULL, 159 display, 160 display_int, 161 display_str, 162 }, 163 { "Files", 164 "These options control how the editor handles files", 165 NULL, 166 files, 167 NULL, 168 files_str, 169 }, 170 { "Input", 171 "These options control text input behavior", 172 NULL, 173 input, 174 input_int, 175 input_str, 176 }, 177 { "Search/RE", 178 "These options control searching and Regular Expression behavior", 179 NULL, 180 search, 181 NULL, 182 search_str, 183 }, 184 { "Editor", 185 "These options control general editor configuration", 186 NULL, 187 general, 188 general_int, 189 general_str, 190 }, 191 }; 192 193 194 /* callbacks */ 195 196 #if defined(SelfTest) 197 void __vi_cancel_cb() 198 { 199 puts( "cancelled" ); 200 } 201 #endif 202 203 204 static void destroyed(void) 205 { 206 int i; 207 208 puts( "destroyed" ); 209 210 /* some window managers destroy us upon popdown */ 211 for (i=0; i<XtNumber(sheets); i++) { 212 sheets[i].holder = NULL; 213 } 214 preferences = NULL; 215 } 216 217 218 static void window_unmapped(Widget w, XtPointer ptr, XEvent *ev, Boolean *cont) 219 { 220 if ( ev->type == UnmapNotify ) { 221 #if defined(SelfTest) 222 puts( "unmapped" ); 223 #endif 224 XtPopdown( XtParent( preferences ) ); 225 } 226 } 227 228 /* 229 * __vi_editopt -- 230 * Set an edit option based on a core message. 231 * 232 * PUBLIC: int __vi_editopt __P((IPVI *, const char *, u_int32_t, const char *, u_int32_t, u_int32_t)); 233 */ 234 int 235 __vi_editopt(IPVI *ipvi, const char *str1, u_int32_t len1, const char *str2, u_int32_t len2, u_int32_t val1) 236 { 237 optData *opt; 238 239 #undef NSEARCH 240 #define NSEARCH(list) { \ 241 for (opt = list; opt->kind != optTerminator; ++opt) \ 242 if (!strcmp(opt->name, str1)) \ 243 goto found; \ 244 } 245 246 NSEARCH(display); 247 NSEARCH(display_int); 248 NSEARCH(display_str); 249 NSEARCH(files); 250 NSEARCH(files_str); 251 NSEARCH(general); 252 NSEARCH(general_int); 253 NSEARCH(general_str); 254 NSEARCH(input); 255 NSEARCH(input_int); 256 NSEARCH(input_str); 257 NSEARCH(search); 258 NSEARCH(search_str); 259 260 return (0); 261 262 found: switch (opt->kind) { 263 case optToggle: 264 opt->value = (void *)val1; 265 break; 266 case optInteger: 267 if (opt->value != NULL) 268 free(opt->value); 269 if ((opt->value = malloc(8)) != NULL) 270 (void)snprintf(opt->value, 271 8, "%lu", (u_long)val1); 272 break; 273 case optString: 274 case optFile: 275 if (opt->value != NULL) 276 free(opt->value); 277 if ((opt->value = malloc(len2)) != NULL) 278 memcpy(opt->value, str2, len2); 279 break; 280 case optTerminator: 281 abort(); 282 } 283 return (0); 284 } 285 286 /* 287 * set_opt -- 288 * Send a set-edit-option message to core. 289 */ 290 static void 291 set_opt(Widget w, XtPointer closure, XtPointer call_data) 292 { 293 optData *opt; 294 Boolean set; 295 IP_BUF ipb; 296 String str; 297 extern IPVI ipvi_motif; 298 299 opt = closure; 300 301 ipb.code = VI_EDITOPT; 302 ipb.str1 = opt->name; 303 ipb.len1 = strlen(opt->name); 304 305 switch (opt->kind) { 306 case optToggle: 307 XtVaGetValues(w, XmNset, &set, 0); 308 ipb.val1 = set; 309 ipb.len2 = 0; 310 311 vi_wsend(&ipvi_motif, "ab1", &ipb); 312 if (ipb.val1) { 313 opt->value = (void *)!set; 314 /* 315 * RAZ: 316 * How do we turn off the button? We don't want to 317 * go recursive where we set it and it calls set_opt 318 * to tell the core. Is that possible? 319 */ 320 XtVaSetValues(w, XmNset, &set, 0); 321 break; 322 } 323 324 if (strcmp(opt->name, "ruler") == 0) 325 if (set) 326 __vi_show_text_ruler_dialog( 327 __vi_screen->area, "Ruler"); 328 else 329 __vi_clear_text_ruler_dialog(); 330 break; 331 case optInteger: 332 str = XmTextFieldGetString(w); 333 ipb.val1 = atoi(str); 334 ipb.len2 = 0; 335 vi_send(vi_ofd, "ab1", &ipb); 336 break; 337 case optFile: 338 case optString: 339 ipb.str2 = XmTextFieldGetString(w); 340 ipb.len2 = strlen(ipb.str2); 341 vi_send(vi_ofd, "ab1", &ipb); 342 break; 343 case optTerminator: 344 abort(); 345 } 346 } 347 348 349 /* add toggles to the property sheet */ 350 351 #if defined(__STDC__) 352 static void add_toggle( Widget parent, optData *option ) 353 #else 354 static void add_toggle( parent, option ) 355 Widget parent; 356 optData *option; 357 #endif 358 { 359 Widget w; 360 361 w = XtVaCreateManagedWidget( option->name, 362 xmToggleButtonGadgetClass, 363 parent, 364 XmNset, (Boolean) option->value, 365 0 366 ); 367 XtAddCallback( w, XmNvalueChangedCallback, set_opt, option ); 368 } 369 370 371 static Widget create_toggles(Widget outer, optData *toggles) 372 { 373 Widget inner; 374 int i; 375 376 inner = XtVaCreateWidget( "toggleOptions", 377 xmRowColumnWidgetClass, 378 outer, 379 XmNpacking, XmPACK_COLUMN, 380 XmNnumColumns, 4, 381 XmNtopAttachment, XmATTACH_FORM, 382 XmNrightAttachment, XmATTACH_FORM, 383 XmNleftAttachment, XmATTACH_FORM, 384 0 385 ); 386 387 /* first the booleans */ 388 for (i=0; toggles[i].kind != optTerminator; i++) { 389 add_toggle( inner, &toggles[i] ); 390 } 391 XtManageChild( inner ); 392 393 return inner; 394 } 395 396 397 /* draw text fields and their labels */ 398 399 #if defined(__STDC__) 400 static void add_string_options( Widget parent, 401 optData *options 402 ) 403 #else 404 static void add_string_options( parent, options ) 405 Widget parent; 406 optData *options; 407 #endif 408 { 409 int i; 410 Widget f, w; 411 412 for ( i=0; options[i].kind != optTerminator; i++ ) { 413 414 f = XtVaCreateWidget( "form", 415 xmFormWidgetClass, 416 parent, 417 0 418 ); 419 420 XtVaCreateManagedWidget( options[i].name, 421 xmLabelGadgetClass, 422 f, 423 XmNtopAttachment, XmATTACH_FORM, 424 XmNbottomAttachment, XmATTACH_FORM, 425 XmNleftAttachment, XmATTACH_FORM, 426 XmNrightAttachment, XmATTACH_POSITION, 427 XmNrightPosition, 20, 428 XmNalignment, XmALIGNMENT_END, 429 0 430 ); 431 432 w = XtVaCreateManagedWidget( "text", 433 xmTextFieldWidgetClass, 434 f, 435 XmNtopAttachment, XmATTACH_FORM, 436 XmNbottomAttachment, XmATTACH_FORM, 437 XmNrightAttachment, XmATTACH_FORM, 438 XmNleftAttachment, XmATTACH_POSITION, 439 XmNleftPosition, 20, 440 0 441 ); 442 443 XmTextFieldSetString( w, (char *) options[i].value ); 444 XtAddCallback( w, XmNactivateCallback, set_opt, &options[i] ); 445 XtManageChild( f ); 446 } 447 } 448 449 450 /* draw and display a single page of properties */ 451 452 #if defined(__STDC__) 453 static Widget create_sheet( Widget parent, optSheet *sheet ) 454 #else 455 static Widget create_sheet( parent, sheet ) 456 Widget parent; 457 optSheet *sheet; 458 #endif 459 { 460 Widget outer, inner, frame; 461 Dimension height; 462 XmString str; 463 464 outer = XtVaCreateWidget( sheet->name, 465 xmFormWidgetClass, 466 parent, 467 XmNtopAttachment, XmATTACH_FORM, 468 XmNrightAttachment, XmATTACH_FORM, 469 XmNbottomAttachment, XmATTACH_FORM, 470 XmNleftAttachment, XmATTACH_FORM, 471 XmNshadowType, XmSHADOW_ETCHED_IN, 472 XmNshadowThickness, 2, 473 XmNverticalSpacing, 4, 474 XmNhorizontalSpacing, 4, 475 0 476 ); 477 478 /* add descriptive text */ 479 frame = XtVaCreateManagedWidget( "frame", 480 xmFrameWidgetClass, 481 outer, 482 XmNtopAttachment, XmATTACH_FORM, 483 XmNrightAttachment, XmATTACH_FORM, 484 XmNleftAttachment, XmATTACH_FORM, 485 0 486 ); 487 str = XmStringCreateLtoR( sheet->description, XmSTRING_DEFAULT_CHARSET ); 488 XtVaCreateManagedWidget( "description", 489 xmLabelGadgetClass, 490 frame, 491 XmNlabelString, str, 492 0 493 ); 494 XmStringFree( str ); 495 496 /* Add the toggles. */ 497 inner = create_toggles( outer, sheet->toggles ); 498 XtVaSetValues( inner, 499 XmNtopAttachment, XmATTACH_WIDGET, 500 XmNtopWidget, frame, 501 0 502 ); 503 504 /* the string options go here */ 505 inner = XtVaCreateWidget( "otherOptions", 506 xmRowColumnWidgetClass, 507 outer, 508 XmNpacking, XmPACK_COLUMN, 509 XmNtopAttachment, XmATTACH_WIDGET, 510 XmNtopWidget, inner, 511 XmNrightAttachment, XmATTACH_FORM, 512 XmNbottomAttachment, XmATTACH_FORM, 513 XmNleftAttachment, XmATTACH_FORM, 514 0 515 ); 516 517 /* Optionally, the ints. */ 518 if ( sheet->ints != NULL ) 519 add_string_options( inner, sheet->ints ); 520 521 /* Optionally, the rest. */ 522 if ( sheet->others != NULL ) 523 add_string_options( inner, sheet->others ); 524 525 XtManageChild( inner ); 526 527 /* finally, force resize of the parent */ 528 XtVaGetValues( outer, XmNheight, &height, 0 ); 529 XtVaSetValues( parent, XmNheight, height, 0 ); 530 531 return outer; 532 } 533 534 535 /* change preferences to another sheet */ 536 537 static void change_sheet(Widget parent, int current) 538 { 539 static int current_sheet = -1; 540 541 /* create a new one? */ 542 if ( sheets[current].holder == NULL ) 543 sheets[current].holder = create_sheet( parent, &sheets[current] ); 544 545 /* done with the old one? */ 546 if ( current_sheet != -1 && sheets[current_sheet].holder != NULL ) 547 XtUnmanageChild( sheets[current_sheet].holder ); 548 549 current_sheet = current; 550 XtManageChild( sheets[current].holder ); 551 XtManageChild( parent ); 552 } 553 554 555 /* Draw and display a dialog the describes vi options */ 556 557 #if defined(__STDC__) 558 static Widget create_options_dialog( Widget parent, String title ) 559 #else 560 static Widget create_options_dialog( parent, title ) 561 Widget parent; 562 String title; 563 #endif 564 { 565 Widget box, form, inner; 566 int i; 567 char buffer[1024]; 568 569 /* already built? */ 570 if ( preferences != NULL ) return preferences; 571 572 box = XtVaCreatePopupShell( title, 573 xmDialogShellWidgetClass, 574 parent, 575 XmNtitle, title, 576 XmNallowShellResize, False, 577 0 578 ); 579 XtAddCallback( box, XmNpopdownCallback, __vi_cancel_cb, 0 ); 580 XtAddCallback( box, XmNdestroyCallback, destroyed, 0 ); 581 XtAddEventHandler( box, 582 SubstructureNotifyMask, 583 False, 584 window_unmapped, 585 NULL 586 ); 587 588 form = XtVaCreateWidget( "options", 589 xmFormWidgetClass, 590 box, 591 0 592 ); 593 594 /* copy the pointers to the sheet names */ 595 *buffer = '\0'; 596 for (i=0; i<XtNumber(sheets); i++) { 597 strcat( buffer, "|" ); 598 strcat( buffer, sheets[i].name ); 599 } 600 601 inner = __vi_CreateTabbedFolder( "tabs", 602 form, 603 buffer, 604 XtNumber(sheets), 605 change_sheet 606 ); 607 608 /* build the property sheets early */ 609 for ( i=0; i<XtNumber(sheets); i++ ) 610 change_sheet( inner, i ); 611 612 /* manage all of the sheets right now */ 613 for ( i=0; i<XtNumber(sheets); i++ ) 614 XtManageChild( sheets[i].holder ); 615 XtManageChild( form ); 616 617 /* remove all but the first one */ 618 for ( i=0; i<XtNumber(sheets); i++ ) 619 XtUnmanageChild( sheets[i].holder ); 620 change_sheet( inner, 0 ); /* show first sheet first */ 621 622 /* keep this global, we might destroy it later */ 623 preferences = form; 624 625 /* done */ 626 return form; 627 } 628 629 630 631 /* 632 * module entry point 633 * 634 * __vi_show_options_dialog -- 635 * 636 * 637 * PUBLIC: void __vi_show_options_dialog __P((Widget, String)); 638 */ 639 void 640 __vi_show_options_dialog(Widget parent, String title) 641 { 642 Widget db = create_options_dialog( parent, title ); 643 #if defined(SelfTest) 644 Widget shell = XtParent( db ); 645 #endif 646 647 XtManageChild( db ); 648 649 #if defined(SelfTest) 650 /* wait until it goes away */ 651 XtPopup( shell, XtGrabNone ); 652 #else 653 /* wait until it goes away */ 654 __vi_modal_dialog( db ); 655 #endif 656 } 657 658 659 660 /* module entry point 661 * Utilities for the search dialog 662 * 663 * __vi_toggle -- 664 * Returns the current value of a toggle. 665 * 666 * PUBLIC: int __vi_toggle __P((char *)); 667 */ 668 int 669 __vi_toggle(char *name) 670 { 671 optData *opt; 672 673 #undef NSEARCH 674 #define NSEARCH(list) { \ 675 for (opt = list; opt->kind != optTerminator; ++opt) \ 676 if (!strcmp(opt->name, name)) \ 677 return ((int)opt->value); \ 678 } 679 NSEARCH(display); 680 NSEARCH(files); 681 NSEARCH(general); 682 NSEARCH(input); 683 NSEARCH(search); 684 685 return (0); 686 } 687 688 /* 689 * __vi_create_search_toggles -- 690 * Creates the search toggles. This is so the options and search widgets 691 * share their appearance. 692 * 693 * PUBLIC: Widget __vi_create_search_toggles __P((Widget, optData[])); 694 */ 695 Widget 696 __vi_create_search_toggles(Widget parent, optData *list) 697 { 698 optData *opt; 699 700 /* 701 * Copy current options information into the search table. 702 * 703 * XXX 704 * This is an O(M*N) loop, but I don't think it matters. 705 */ 706 for (opt = list; opt->kind != optTerminator; ++opt) 707 opt->value = (void *)__vi_toggle(opt->name); 708 709 return (create_toggles(parent, list)); 710 } 711 712 713 #if defined(SelfTest) 714 715 #if defined(__STDC__) 716 static void show_options( Widget w, XtPointer data, XtPointer cbs ) 717 #else 718 static void show_options( w, data, cbs ) 719 Widget w; 720 XtPointer data; 721 XtPointer cbs; 722 #endif 723 { 724 __vi_show_options_dialog( data, "Preferences" ); 725 } 726 727 main( int argc, char *argv[] ) 728 { 729 XtAppContext ctx; 730 Widget top_level, rc, button; 731 extern exit(); 732 733 /* create a top-level shell for the window manager */ 734 top_level = XtVaAppInitialize( &ctx, 735 argv[0], 736 NULL, 0, /* options */ 737 (ArgcType) &argc, 738 argv, /* might get modified */ 739 NULL, 740 NULL 741 ); 742 743 rc = XtVaCreateManagedWidget( "rc", 744 xmRowColumnWidgetClass, 745 top_level, 746 0 747 ); 748 749 button = XtVaCreateManagedWidget( "Pop up options dialog", 750 xmPushButtonGadgetClass, 751 rc, 752 0 753 ); 754 XtAddCallback( button, XmNactivateCallback, show_options, rc ); 755 756 button = XtVaCreateManagedWidget( "Quit", 757 xmPushButtonGadgetClass, 758 rc, 759 0 760 ); 761 XtAddCallback( button, XmNactivateCallback, exit, 0 ); 762 763 XtRealizeWidget(top_level); 764 XtAppMainLoop(ctx); 765 } 766 #endif 767