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