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