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