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_menu.c,v 8.26 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_menu.c,v 1.2 2014/01/26 21:43:45 christos Exp $");
19 #endif
20 
21 #include <sys/queue.h>
22 
23 #include <X11/Intrinsic.h>
24 #include <X11/StringDefs.h>
25 #include <Xm/PushB.h>
26 #include <Xm/CascadeB.h>
27 #include <Xm/RowColumn.h>
28 #include <Xm/Separator.h>
29 #include <Xm/FileSB.h>
30 #include <Xm/SelectioB.h>
31 
32 #include <bitstring.h>
33 #include <stdio.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 /* save this for creation of children */
43 static    Widget    main_widget = NULL;
44 
45 /* This module defines the menu structure for vi.  Each menu
46  * item has an action routine associated with it.  For the most
47  * part, those actions will simply call vi_send with vi commands.
48  * others will pop up file selection dialogs and use them for
49  * vi commands, and other will have to have special actions.
50  *
51  * Future:
52  *        vi core will have to let us know when to be sensitive
53  *        change VI_STRING to VI_COMMAND so that menu actions cannot
54  *                  be confusing when in insert mode
55  *        need VI_CUT, VI_COPY, and VI_PASTE to perform the appropriate
56  *                  operations on the visible text of yank buffer.  VI_COPY
57  *                  is likely a NOP, but it will make users happy
58  *        add mnemonics
59  *        add accelerators
60  *        implement file selection dialog boxes
61  *        implement string prompt dialog boxes (e.g. for 'find')
62  *
63  * Interface:
64  *        Widget    create_menubar( Widget parent ) creates and returns the
65  *                  X menu structure.  The caller can place this
66  *                  anywhere in the widget heirarchy.
67  */
68 
69 #define   BufferSize          1024
70 
71 /*
72  * __vi_send_command_string --
73  *        Utility:  Send a menu command to vi
74  *
75  * Future:
76  * Change VI_STRING to VI_COMMAND so that menu actions cannot be confusing
77  * when in insert mode.
78  *
79  * XXX
80  * THIS SHOULD GO AWAY -- WE SHOULDN'T SEND UNINTERPRETED STRINGS TO THE
81  * CORE.
82  *
83  * PUBLIC: void __vi_send_command_string __P((String));
84  */
85 void
__vi_send_command_string(String str)86 __vi_send_command_string(String str)
87 {
88     IP_BUF          ipb;
89     char  buffer[BufferSize];
90 
91     /* Future:  Need VI_COMMAND so vi knows this is not text to insert
92      * At that point, appending a cr/lf will not be necessary.  For now,
93      * append iff we are a colon or slash command.  Of course, if we are in
94      * insert mode, all bets are off.
95      */
96     strcpy( buffer, str );
97     switch ( *str ) {
98           case ':':
99           case '/':
100               strcat( buffer, "\n" );
101               break;
102     }
103 
104     ipb.code = VI_STRING;
105     ipb.str1 = buffer;
106     ipb.len1 = strlen(buffer);
107     vi_send(vi_ofd, "a", &ipb);
108 }
109 
110 
111 /* Utility:  beep for unimplemented command */
112 
113 #if defined(__STDC__)
send_beep(Widget w)114 static    void      send_beep( Widget w )
115 #else
116 static    void      send_beep( w )
117           Widget    w;
118 #endif
119 {
120     XBell( XtDisplay(w), 0 );
121 }
122 
123 
124 /*
125  * __vi_cancel_cb --
126  *        Utility:  make a dialog box go Modal
127  *
128  * PUBLIC: void __vi_cancel_cb __P((Widget, XtPointer, XtPointer));
129  */
130 static    Bool      have_answer;
131 void
__vi_cancel_cb(Widget w,XtPointer client_data,XtPointer call_data)132 __vi_cancel_cb(Widget w, XtPointer client_data, XtPointer call_data)
133 {
134           have_answer = True;
135 }
136 
137 /*
138  * PUBLIC: void __vi_modal_dialog __P((Widget));
139  */
140 void
__vi_modal_dialog(Widget db)141 __vi_modal_dialog(Widget db)
142 {
143     XtAppContext    ctx;
144 
145     /* post the dialog */
146     XtManageChild( db );
147     XtPopup( XtParent(db), XtGrabExclusive );
148 
149     /* wait for a response */
150     ctx = XtWidgetToApplicationContext(db);
151     XtAddGrab( XtParent(db), TRUE, FALSE );
152     for ( have_answer = False; ! have_answer; )
153           XtAppProcessEvent( ctx, XtIMAll );
154 
155     /* done with db */
156     XtPopdown( XtParent(db) );
157     XtRemoveGrab( XtParent(db) );
158 }
159 
160 
161 /* Utility:  Get a file (using standard File Selection Dialog Box) */
162 
163 static    String    file_name;
164 
165 
166 #if defined(__STDC__)
ok_file_name(Widget w,XtPointer client_data,XtPointer call_data)167 static    void                ok_file_name( Widget w,
168                                               XtPointer client_data,
169                                               XtPointer call_data
170                                               )
171 #else
172 static    void                ok_file_name( w, client_data, call_data )
173           Widget              w;
174           XtPointer client_data;
175           XtPointer call_data;
176 #endif
177 {
178     XmFileSelectionBoxCallbackStruct    *cbs;
179 
180     cbs = (XmFileSelectionBoxCallbackStruct *) call_data;
181     XmStringGetLtoR( cbs->value, XmSTRING_DEFAULT_CHARSET, &file_name );
182 
183     have_answer = True;
184 }
185 
186 
187 #if defined(__STDC__)
get_file(Widget w,String prompt)188 static    String    get_file( Widget w, String prompt )
189 #else
190 static    String    get_file( w, prompt )
191           Widget    w;
192           String    prompt;
193 #endif
194 {
195     /* make it static so we can reuse it */
196     static          Widget    db;
197 
198     /* our return parameter */
199     if ( file_name != NULL ) {
200           XtFree( file_name );
201           file_name = NULL;
202     }
203 
204     /* create one? */
205     if ( db == NULL ){
206           db = XmCreateFileSelectionDialog( main_widget, "file", NULL, 0 );
207           XtAddCallback( db, XmNokCallback, ok_file_name, NULL );
208           XtAddCallback( db, XmNcancelCallback, __vi_cancel_cb, NULL );
209     }
210 
211     /* use the title as a prompt */
212     XtVaSetValues( XtParent(db), XmNtitle, prompt, 0 );
213 
214     /* wait for a response */
215     __vi_modal_dialog( db );
216 
217     /* done */
218     return file_name;
219 }
220 
221 
222 /*
223  * file_command --
224  *        Get a file name and send it with the command to the core.
225  */
226 static void
file_command(Widget w,int code,String prompt)227 file_command(Widget w, int code, String prompt)
228 {
229           IP_BUF ipb;
230           char *file;
231 
232           if ((file = get_file(w, prompt)) != NULL) {
233                     ipb.code = code;
234                     ipb.str1 = file;
235                     ipb.len1 = strlen(file);
236                     vi_send(vi_ofd, "a", &ipb);
237           }
238 }
239 
240 
241 /*
242  * Menu action routines (one per menu entry)
243  *
244  * These are in the order in which they appear in the menu structure.
245  */
246 static void
ma_edit_file(Widget w,XtPointer call_data,XtPointer client_data)247 ma_edit_file(Widget w, XtPointer call_data, XtPointer client_data)
248 {
249           file_command(w, VI_EDIT, "Edit");
250 }
251 
252 static void
ma_split(Widget w,XtPointer call_data,XtPointer client_data)253 ma_split(Widget w, XtPointer call_data, XtPointer client_data)
254 {
255           file_command(w, VI_EDITSPLIT, "Edit");
256 }
257 
258 static void
ma_save(Widget w,XtPointer call_data,XtPointer client_data)259 ma_save(Widget w, XtPointer call_data, XtPointer client_data)
260 {
261           IP_BUF ipb;
262 
263           ipb.code = VI_WRITE;
264           (void)vi_send(vi_ofd, NULL, &ipb);
265 }
266 
267 static void
ma_save_as(Widget w,XtPointer call_data,XtPointer client_data)268 ma_save_as(Widget w, XtPointer call_data, XtPointer client_data)
269 {
270           file_command(w, VI_WRITEAS, "Save As");
271 }
272 
273 static void
ma_wq(Widget w,XtPointer call_data,XtPointer client_data)274 ma_wq(Widget w, XtPointer call_data, XtPointer client_data)
275 {
276           IP_BUF ipb;
277 
278           ipb.code = VI_WQ;
279           (void)vi_send(vi_ofd, NULL, &ipb);
280 }
281 
282 static void
ma_quit(Widget w,XtPointer call_data,XtPointer client_data)283 ma_quit(Widget w, XtPointer call_data, XtPointer client_data)
284 {
285           IP_BUF ipb;
286 
287           ipb.code = VI_QUIT;
288           (void)vi_send(vi_ofd, NULL, &ipb);
289 }
290 
291 static void
ma_undo(Widget w,XtPointer call_data,XtPointer client_data)292 ma_undo(Widget w, XtPointer call_data, XtPointer client_data)
293 {
294           IP_BUF ipb;
295 
296           ipb.code = VI_UNDO;
297           (void)vi_send(vi_ofd, NULL, &ipb);
298 }
299 
300 #if defined(__STDC__)
ma_cut(Widget w,XtPointer call_data,XtPointer client_data)301 static void         ma_cut(   Widget w,
302                               XtPointer call_data,
303                               XtPointer client_data
304                               )
305 #else
306 static    void                ma_cut( w, call_data, client_data )
307           Widget              w;
308           XtPointer call_data;
309           XtPointer client_data;
310 #endif
311 {
312     /* future */
313     send_beep( w );
314 }
315 
316 
317 #if defined(__STDC__)
ma_copy(Widget w,XtPointer call_data,XtPointer client_data)318 static    void      ma_copy(  Widget w,
319                                         XtPointer call_data,
320                                         XtPointer client_data
321                                         )
322 #else
323 static    void                ma_copy( w, call_data, client_data )
324           Widget              w;
325           XtPointer call_data;
326           XtPointer client_data;
327 #endif
328 {
329     /* future */
330     send_beep( w );
331 }
332 
333 
334 #if defined(__STDC__)
ma_paste(Widget w,XtPointer call_data,XtPointer client_data)335 static    void      ma_paste( Widget w,
336                                         XtPointer call_data,
337                                         XtPointer client_data
338                                         )
339 #else
340 static    void                ma_paste( w, call_data, client_data )
341           Widget              w;
342           XtPointer call_data;
343           XtPointer client_data;
344 #endif
345 {
346     /* future */
347     send_beep( w );
348 }
349 
350 static void
ma_find(Widget w,XtPointer call_data,XtPointer client_data)351 ma_find(Widget w, XtPointer call_data, XtPointer client_data)
352 {
353           __vi_show_search_dialog( main_widget, "Find" );
354 }
355 
356 static void
ma_find_next(Widget w,XtPointer call_data,XtPointer client_data)357 ma_find_next(Widget w, XtPointer call_data, XtPointer client_data)
358 {
359           __vi_search( w );
360 }
361 
362 static void
ma_tags(Widget w,XtPointer call_data,XtPointer client_data)363 ma_tags(Widget w, XtPointer call_data, XtPointer client_data)
364 {
365           __vi_show_tags_dialog( main_widget, "Tag Stack" );
366 }
367 
368 static void
ma_tagpop(Widget w,XtPointer call_data,XtPointer client_data)369 ma_tagpop(Widget w, XtPointer call_data, XtPointer client_data)
370 {
371           __vi_send_command_string( "\024" );
372 }
373 
374 static void
ma_tagtop(Widget w,XtPointer call_data,XtPointer client_data)375 ma_tagtop(Widget w, XtPointer call_data, XtPointer client_data)
376 {
377           __vi_send_command_string( ":tagtop" );
378 }
379 
380 #if defined(__STDC__)
ma_preferences(Widget w,XtPointer call_data,XtPointer client_data)381 static void         ma_preferences(     Widget w,
382                                         XtPointer call_data,
383                                         XtPointer client_data
384                                         )
385 #else
386 static    void                ma_preferences( w, call_data, client_data )
387           Widget              w;
388           XtPointer call_data;
389           XtPointer client_data;
390 #endif
391 {
392           __vi_show_options_dialog( main_widget, "Preferences" );
393 }
394 
395 
396 /* Menu construction routines */
397 
398 typedef   struct {
399     String          title;
400     void  (*action)();
401     String          accel;              /* for Motif */
402     String          accel_text;         /* for the user */
403 } pull_down;
404 
405 typedef   struct {
406     char  mnemonic;
407     String          title;
408     pull_down       *actions;
409 } menu_bar;
410 
411 static    pull_down file_menu[] = {
412     { "Edit File...",                   ma_edit_file,       "Alt<Key>e",        "Alt+E"   },
413     { "",                     NULL,               NULL,     NULL      },
414     { "Split Window...",      ma_split, NULL,     NULL      },
415     { "",                     NULL,               NULL,     NULL      },
416     { "Save ",                          ma_save,  "Alt<Key>s",        "Alt+S"   },
417     { "Save As...",           ma_save_as,         "Shift Alt<Key>s",  "Shift+Alt+S"       },
418     { "",                     NULL,               NULL,     NULL      },
419     { "Write and Quit",                 ma_wq,              "Shift Alt<Key>q",  "Shift+Alt+Q"       },
420     { "Quit",                           ma_quit,  "Alt<Key>q",        "Alt+Q"   },
421     { NULL,                             NULL,               NULL,     NULL      },
422 };
423 
424 static    pull_down edit_menu[] = {
425     { "Undo",                           ma_undo,  NULL,     NULL      },
426     { "",                     NULL,               NULL,     NULL      },
427     { "Cut",                            ma_cut,             "Alt<Key>x",        "Alt+X"   },
428     { "Copy",                           ma_copy,  "Alt<Key>c",        "Alt+C"   },
429     { "Paste",                          ma_paste, "Alt<Key>v",        "Alt+V"   },
430     { "",                     NULL,               NULL,     NULL      },
431     { "Find",                           ma_find,  "Alt<Key>f",        "Alt+F"   },
432     { "Find Next",            ma_find_next,       "Alt<Key>g",        "Alt+G"   },
433     { NULL,                             NULL,               NULL,     NULL      },
434 };
435 
436 static    pull_down options_menu[] = {
437     { "Preferences",                    ma_preferences,     NULL,     NULL      },
438     { "Command Mode Maps",    NULL,               NULL,     NULL      },
439     { "Insert Mode Maps",     NULL,               NULL,     NULL      },
440     { NULL,                             NULL,               NULL,     NULL      },
441 };
442 
443 static    pull_down tag_menu[] = {
444     { "Show Tag Stack",                 ma_tags,  "Alt<Key>t",        "Alt+T"   },
445     { "",                     NULL,               NULL,     NULL      },
446     { "Pop Tag",              ma_tagpop,          NULL,     NULL      },
447     { "Clear Stack",                    ma_tagtop,          NULL,     NULL      },
448     { NULL,                             NULL,               NULL,     NULL      },
449 };
450 
451 static    pull_down help_menu[] = {
452     { NULL,                             NULL,               NULL,     NULL      },
453 };
454 
455 static    menu_bar  main_menu[] = {
456     { 'F',          "File",             file_menu },
457     { 'E',          "Edit",   edit_menu },
458     { 'O',          "Options",          options_menu        },
459     { 'T',          "Tag",              tag_menu  },
460     { 'H',          "Help",             help_menu },
461     { 0,  NULL,               NULL                },
462 };
463 
464 
465 #if defined(__STDC__)
add_entries(Widget parent,pull_down * actions)466 static    void      add_entries( Widget parent, pull_down *actions )
467 #else
468 static    void                add_entries( parent, actions )
469           Widget              parent;
470           pull_down *actions;
471 #endif
472 {
473     Widget          w;
474     XmString        str;
475 
476     for ( ; actions->title != NULL; actions++ ) {
477 
478           /* a separator? */
479           if ( *actions->title != '\0' ) {
480               w = XmCreatePushButton( parent, actions->title, NULL, 0 );
481               if ( actions->action == NULL )
482                     XtSetSensitive( w, False );
483               else
484                     XtAddCallback(  w,
485                                         XmNactivateCallback,
486                                         (XtCallbackProc) actions->action,
487                                         actions
488                                         );
489               if ( actions->accel != NULL ) {
490                     str = XmStringCreateSimple( actions->accel_text );
491                     XtVaSetValues(      w,
492                                         XmNaccelerator,               actions->accel,
493                                         XmNacceleratorText, str,
494                                         0
495                                         );
496                     XmStringFree( str );
497               }
498           }
499           else {
500               w = XmCreateSeparator( parent, "separator", NULL, 0 );
501           }
502 
503           XtManageChild( w );
504 
505     }
506 }
507 
508 /*
509  * vi_create_menubar --
510  *
511  * PUBLIC: Widget vi_create_menubar __P((Widget));
512  */
513 Widget
vi_create_menubar(Widget parent)514 vi_create_menubar(Widget parent)
515 {
516     Widget          menu, pull, button;
517     menu_bar        *ptr;
518 
519     /* save this for creation of children */
520     main_widget = parent;
521 
522     menu = XmCreateMenuBar( parent, "Menu", NULL, 0 );
523 
524     for ( ptr=main_menu; ptr->title != NULL; ptr++ ) {
525 
526           pull = XmCreatePulldownMenu( menu, "pull", NULL, 0 );
527           add_entries( pull, ptr->actions );
528           button = XmCreateCascadeButton( menu, ptr->title, NULL, 0 );
529           XtVaSetValues( button, XmNsubMenuId, pull, 0 );
530 
531           if ( strcmp( ptr->title, "Help" ) == 0 )
532               XtVaSetValues( menu, XmNmenuHelpWidget, button, 0 );
533 
534 #if 0
535           /* These screw up accelerator processing.  Punt for now */
536           if ( ptr->mnemonic )
537               XtVaSetValues( button, XmNmnemonic, ptr->mnemonic, 0 );
538 #endif
539 
540           XtManageChild( button );
541     }
542 
543     return menu;
544 }
545