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