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)
__vi_cancel_cb()202 void __vi_cancel_cb()
203 {
204 puts( "cancelled" );
205 }
206 #endif
207
208
destroyed(void)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
window_unmapped(Widget w,XtPointer ptr,XEvent * ev,Boolean * cont)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
__vi_editopt(IPVI * ipvi,const char * str1,u_int32_t len1,const char * str2,u_int32_t len2,u_int32_t val1)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
set_opt(Widget w,XtPointer closure,XtPointer call_data)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__)
add_toggle(Widget parent,optData * option)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
create_toggles(Widget outer,optData * toggles)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__)
add_string_options(Widget parent,optData * options)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__)
create_sheet(Widget parent,optSheet * sheet)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
change_sheet(Widget parent,int current)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__)
create_options_dialog(Widget parent,String title)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
__vi_show_options_dialog(Widget parent,String title)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
__vi_toggle(char * name)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
__vi_create_search_toggles(Widget parent,optData * list)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__)
show_options(Widget w,XtPointer data,XtPointer cbs)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
main(int argc,char * argv[])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