xref: /minix/external/bsd/nvi/dist/motif_l/m_options.c (revision 84d9c625)
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