1/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved		by Bram Moolenaar
4 *				GUI/Motif support by Robert Webb
5 *
6 * Do ":help uganda"  in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
9 */
10
11#include <Xm/Form.h>
12#include <Xm/RowColumn.h>
13#include <Xm/PushB.h>
14#include <Xm/Text.h>
15#include <Xm/TextF.h>
16#include <Xm/Separator.h>
17#include <Xm/Label.h>
18#include <Xm/CascadeB.h>
19#include <Xm/ScrollBar.h>
20#include <Xm/MenuShell.h>
21#include <Xm/DrawingA.h>
22#if (XmVersion >= 1002)
23# include <Xm/RepType.h>
24#endif
25#include <Xm/Frame.h>
26#include <Xm/LabelG.h>
27#include <Xm/ToggleBG.h>
28#include <Xm/SeparatoG.h>
29#include <Xm/XmP.h>
30
31#include <X11/keysym.h>
32#include <X11/Xatom.h>
33#include <X11/StringDefs.h>
34#include <X11/Intrinsic.h>
35
36#include "vim.h"
37
38#ifdef HAVE_X11_XPM_H
39# include <X11/xpm.h>
40#else
41# ifdef HAVE_XM_XPMP_H
42#  include <Xm/XpmP.h>
43# endif
44#endif
45#ifdef HAVE_XM_NOTEBOOK_H
46# include <Xm/Notebook.h>
47#endif
48
49#include "gui_xmebw.h"	/* for our Enhanced Button Widget */
50
51#if defined(FEAT_GUI_DIALOG) && defined(HAVE_XPM)
52# include "../pixmaps/alert.xpm"
53# include "../pixmaps/error.xpm"
54# include "../pixmaps/generic.xpm"
55# include "../pixmaps/info.xpm"
56# include "../pixmaps/quest.xpm"
57#endif
58
59#define MOTIF_POPUP
60
61extern Widget vimShell;
62
63static Widget vimForm;
64static Widget textAreaForm;
65Widget textArea;
66#ifdef FEAT_TOOLBAR
67static Widget toolBarFrame;
68static Widget toolBar;
69#endif
70#ifdef FEAT_GUI_TABLINE
71static Widget	tabLine;
72static Widget	tabLine_menu = 0;
73static int	showing_tabline = 0;
74#endif
75#ifdef FEAT_FOOTER
76static Widget footer;
77#endif
78#ifdef FEAT_MENU
79# if (XmVersion >= 1002)
80/* remember the last set value for the tearoff item */
81static int tearoff_val = (int)XmTEAR_OFF_ENABLED;
82# endif
83static Widget menuBar;
84#endif
85
86static void scroll_cb __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
87#ifdef FEAT_GUI_TABLINE
88static void tabline_cb __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
89static void tabline_button_cb __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
90static void tabline_menu_cb __ARGS((Widget w, XtPointer	closure, XEvent	*e, Boolean *continue_dispatch));
91static void tabline_balloon_cb __ARGS((BalloonEval *beval, int state));
92#endif
93#ifdef FEAT_TOOLBAR
94# ifdef FEAT_FOOTER
95static void toolbarbutton_enter_cb __ARGS((Widget, XtPointer, XEvent *, Boolean *));
96static void toolbarbutton_leave_cb __ARGS((Widget, XtPointer, XEvent *, Boolean *));
97# endif
98static void reset_focus __ARGS((void));
99#endif
100#ifdef FEAT_FOOTER
101static int gui_mch_compute_footer_height __ARGS((void));
102#endif
103#ifdef WSDEBUG
104static void attachDump(Widget, char *);
105#endif
106
107static void gui_motif_menu_colors __ARGS((Widget id));
108static void gui_motif_scroll_colors __ARGS((Widget id));
109
110#if (XmVersion >= 1002)
111# define STRING_TAG  XmFONTLIST_DEFAULT_TAG
112#else
113# define STRING_TAG  XmSTRING_DEFAULT_CHARSET
114#endif
115
116/*
117 * Call-back routines.
118 */
119
120    static void
121scroll_cb(w, client_data, call_data)
122    Widget	w UNUSED;
123    XtPointer	client_data, call_data;
124{
125    scrollbar_T *sb;
126    long	value;
127    int		dragging;
128
129    sb = gui_find_scrollbar((long)client_data);
130
131    value = ((XmScrollBarCallbackStruct *)call_data)->value;
132    dragging = (((XmScrollBarCallbackStruct *)call_data)->reason ==
133							      (int)XmCR_DRAG);
134    gui_drag_scrollbar(sb, value, dragging);
135}
136
137#ifdef FEAT_GUI_TABLINE
138    static void
139tabline_cb(w, client_data, call_data)
140    Widget	w UNUSED;
141    XtPointer	client_data UNUSED;
142    XtPointer	call_data;
143{
144    XmNotebookCallbackStruct *nptr;
145
146    nptr = (XmNotebookCallbackStruct *)call_data;
147    if (nptr->reason != (int)XmCR_NONE)
148	send_tabline_event(nptr->page_number);
149}
150
151    static void
152tabline_button_cb(w, client_data, call_data)
153    Widget	w;
154    XtPointer	client_data UNUSED;
155    XtPointer	call_data UNUSED;
156{
157    int		cmd, tab_idx;
158
159    XtVaGetValues(w, XmNuserData, &cmd, NULL);
160    XtVaGetValues(tabLine_menu, XmNuserData, &tab_idx, NULL);
161
162    send_tabline_menu_event(tab_idx, cmd);
163}
164
165/*
166 * Tabline single mouse click timeout handler
167 */
168    static void
169motif_tabline_timer_cb (timed_out, interval_id)
170    XtPointer		timed_out;
171    XtIntervalId	*interval_id UNUSED;
172{
173    *((int *)timed_out) = TRUE;
174}
175
176/*
177 * check if the tabline tab scroller is clicked
178 */
179    static int
180tabline_scroller_clicked(scroller_name, event)
181    char		*scroller_name;
182    XButtonPressedEvent *event;
183{
184    Widget	tab_scroll_w;
185    Position	pos_x, pos_y;
186    Dimension	width, height;
187
188    tab_scroll_w = XtNameToWidget(tabLine, scroller_name);
189    if (tab_scroll_w != (Widget)0) {
190	XtVaGetValues(tab_scroll_w, XmNx, &pos_x, XmNy, &pos_y, XmNwidth,
191		      &width, XmNheight, &height, NULL);
192	if (pos_x >= 0) {
193	    /* Tab scroller (next) is visible */
194	    if ((event->x >= pos_x) && (event->x <= pos_x + width) &&
195		(event->y >= pos_y) && (event->y <= pos_y + height)) {
196		/* Clicked on the scroller */
197		return TRUE;
198	    }
199	}
200    }
201    return FALSE;
202}
203
204    static void
205tabline_menu_cb(w, closure, e, continue_dispatch)
206    Widget	w;
207    XtPointer	closure UNUSED;
208    XEvent	*e;
209    Boolean	*continue_dispatch UNUSED;
210{
211    Widget			tab_w;
212    XButtonPressedEvent		*event;
213    int				tab_idx = 0;
214    WidgetList			children;
215    Cardinal			numChildren;
216    static XtIntervalId		timer = (XtIntervalId)0;
217    static int			timed_out = TRUE;
218
219    event = (XButtonPressedEvent *)e;
220
221    if (event->button == Button1)
222    {
223	if (tabline_scroller_clicked("MajorTabScrollerNext", event)
224	    || tabline_scroller_clicked("MajorTabScrollerPrevious", event))
225	    return;
226
227	if (!timed_out)
228	{
229	    XtRemoveTimeOut(timer);
230	    timed_out = TRUE;
231
232	    /*
233	     * Double click on the tabline gutter, add a new tab
234	     */
235	    send_tabline_menu_event(0, TABLINE_MENU_NEW);
236	}
237	else
238	{
239	    /*
240	     * Single click on the tabline gutter, start a timer to check
241	     * for double clicks
242	     */
243	    timer = XtAppAddTimeOut(app_context, (long_u)p_mouset,
244				    motif_tabline_timer_cb, &timed_out);
245	    timed_out = FALSE;
246	}
247	return;
248    }
249
250    if (event->button != Button3)
251	return;
252
253    /* When ignoring events don't show the menu. */
254    if (hold_gui_events
255# ifdef FEAT_CMDWIN
256	    || cmdwin_type != 0
257# endif
258       )
259	return;
260
261    if (event->subwindow != None)
262    {
263	tab_w = XtWindowToWidget(XtDisplay(w), event->subwindow);
264	/* LINTED: avoid warning: dubious operation on enum */
265	if (tab_w != (Widget)0 && XmIsPushButton(tab_w))
266	    XtVaGetValues(tab_w, XmNpageNumber, &tab_idx, NULL);
267    }
268
269    XtVaSetValues(tabLine_menu, XmNuserData, tab_idx, NULL);
270    XtVaGetValues(tabLine_menu, XmNchildren, &children, XmNnumChildren,
271		  &numChildren, NULL);
272    XtManageChildren(children, numChildren);
273    XmMenuPosition(tabLine_menu, (XButtonPressedEvent *)e) ;
274    XtManageChild(tabLine_menu);
275}
276
277    static void
278tabline_balloon_cb(beval, state)
279    BalloonEval	*beval;
280    int		state UNUSED;
281{
282    int		nr;
283    tabpage_T	*tp;
284
285    if (beval->target == (Widget)0)
286	return;
287
288    XtVaGetValues(beval->target, XmNpageNumber, &nr, NULL);
289    tp = find_tabpage(nr);
290    if (tp == NULL)
291	return;
292
293    get_tabline_label(tp, TRUE);
294    gui_mch_post_balloon(beval, NameBuff);
295}
296
297#endif
298
299/*
300 * End of call-back routines
301 */
302
303/*
304 * Implement three dimensional shading of insensitive labels.
305 * By Marcin Dalecki.
306 */
307
308#include <Xm/XmP.h>
309#include <Xm/LabelP.h>
310
311static XtExposeProc old_label_expose = NULL;
312
313static void label_expose __ARGS((Widget _w, XEvent *_event, Region _region));
314
315    static void
316label_expose(_w, _event, _region)
317    Widget	_w;
318    XEvent	*_event;
319    Region	_region;
320{
321    GC		    insensitiveGC;
322    XmLabelWidget   lw = (XmLabelWidget)_w;
323    unsigned char   label_type = (int)XmSTRING;
324
325    XtVaGetValues(_w, XmNlabelType, &label_type, (XtPointer)0);
326
327    if (XtIsSensitive(_w) || label_type != (int)XmSTRING)
328	(*old_label_expose)(_w, _event, _region);
329    else
330    {
331	XGCValues   values;
332	XtGCMask    mask;
333	XtGCMask    dynamic;
334	XFontStruct *fs;
335
336	_XmFontListGetDefaultFont(lw->label.font, &fs);
337
338	/* FIXME: we should be doing the whole drawing ourself here. */
339	insensitiveGC = lw->label.insensitive_GC;
340
341	mask = GCForeground | GCBackground | GCGraphicsExposures;
342	dynamic = GCClipMask | GCClipXOrigin | GCClipYOrigin;
343	values.graphics_exposures = False;
344
345	if (fs != 0)
346	{
347	    mask |= GCFont;
348	    values.font = fs->fid;
349	}
350
351	if (lw->primitive.top_shadow_pixmap != None
352		&& lw->primitive.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
353	{
354	    mask |= GCFillStyle | GCTile;
355	    values.fill_style = FillTiled;
356	    values.tile = lw->primitive.top_shadow_pixmap;
357	}
358
359	lw->label.TextRect.x += 1;
360	lw->label.TextRect.y += 1;
361	if (lw->label._acc_text != 0)
362	{
363	    lw->label.acc_TextRect.x += 1;
364	    lw->label.acc_TextRect.y += 1;
365	}
366
367	values.foreground = lw->primitive.top_shadow_color;
368	values.background = lw->core.background_pixel;
369
370	lw->label.insensitive_GC = XtAllocateGC((Widget)lw, 0, mask,
371					       &values, dynamic, (XtGCMask)0);
372	(*old_label_expose)(_w, _event, _region);
373	XtReleaseGC(_w, lw->label.insensitive_GC);
374
375	lw->label.TextRect.x -= 1;
376	lw->label.TextRect.y -= 1;
377	if (lw->label._acc_text != 0)
378	{
379	    lw->label.acc_TextRect.x -= 1;
380	    lw->label.acc_TextRect.y -= 1;
381	}
382
383	values.foreground = lw->primitive.bottom_shadow_color;
384	values.background = lw->core.background_pixel;
385
386	lw->label.insensitive_GC = XtAllocateGC((Widget) lw, 0, mask,
387					       &values, dynamic, (XtGCMask)0);
388	(*old_label_expose)(_w, _event, _region);
389	XtReleaseGC(_w, lw->label.insensitive_GC);
390
391	lw->label.insensitive_GC = insensitiveGC;
392    }
393}
394
395/*
396 * Create all the motif widgets necessary.
397 */
398    void
399gui_x11_create_widgets()
400{
401#ifdef FEAT_GUI_TABLINE
402    Widget	button, scroller;
403    Arg		args[10];
404    int		n;
405    XmString	xms;
406#endif
407
408    /*
409     * Install the 3D shade effect drawing routines.
410     */
411    if (old_label_expose == NULL)
412    {
413	old_label_expose = xmLabelWidgetClass->core_class.expose;
414	xmLabelWidgetClass->core_class.expose = label_expose;
415    }
416
417    /*
418     * Start out by adding the configured border width into the border offset
419     */
420    gui.border_offset = gui.border_width;
421
422    /*
423     * Install the tearOffModel resource converter.
424     */
425#if (XmVersion >= 1002)
426    XmRepTypeInstallTearOffModelConverter();
427#endif
428
429    /* Make sure the "Quit" menu entry of the window manager is ignored */
430    XtVaSetValues(vimShell, XmNdeleteResponse, XmDO_NOTHING, NULL);
431
432    vimForm = XtVaCreateManagedWidget("vimForm",
433	xmFormWidgetClass, vimShell,
434	XmNborderWidth, 0,
435	XmNhighlightThickness, 0,
436	XmNshadowThickness, 0,
437	XmNmarginWidth, 0,
438	XmNmarginHeight, 0,
439	XmNresizePolicy, XmRESIZE_ANY,
440	NULL);
441    gui_motif_menu_colors(vimForm);
442
443#ifdef FEAT_MENU
444    {
445	Arg al[7]; /* Make sure there is enough room for arguments! */
446	int ac = 0;
447
448# if (XmVersion >= 1002)
449	XtSetArg(al[ac], XmNtearOffModel, tearoff_val); ac++;
450# endif
451	XtSetArg(al[ac], XmNleftAttachment,  XmATTACH_FORM); ac++;
452	XtSetArg(al[ac], XmNtopAttachment,   XmATTACH_FORM); ac++;
453	XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
454# ifndef FEAT_TOOLBAR
455	/* Always stick to right hand side. */
456	XtSetArg(al[ac], XmNrightOffset, 0); ac++;
457# endif
458	XtSetArg(al[ac], XmNmarginHeight, 0); ac++;
459	menuBar = XmCreateMenuBar(vimForm, "menuBar", al, ac);
460	XtManageChild(menuBar);
461
462	/* Remember the default colors, needed for ":hi clear". */
463	XtVaGetValues(menuBar,
464	    XmNbackground, &gui.menu_def_bg_pixel,
465	    XmNforeground, &gui.menu_def_fg_pixel,
466	    NULL);
467	gui_motif_menu_colors(menuBar);
468    }
469#endif
470
471#ifdef FEAT_TOOLBAR
472    /*
473     * Create an empty ToolBar. We should get buttons defined from menu.vim.
474     */
475    toolBarFrame = XtVaCreateWidget("toolBarFrame",
476	xmFrameWidgetClass, vimForm,
477	XmNshadowThickness, 0,
478	XmNmarginHeight, 0,
479	XmNmarginWidth, 0,
480	XmNleftAttachment, XmATTACH_FORM,
481	XmNrightAttachment, XmATTACH_FORM,
482	NULL);
483    gui_motif_menu_colors(toolBarFrame);
484
485    toolBar = XtVaCreateManagedWidget("toolBar",
486	xmRowColumnWidgetClass, toolBarFrame,
487	XmNchildType, XmFRAME_WORKAREA_CHILD,
488	XmNrowColumnType, XmWORK_AREA,
489	XmNorientation, XmHORIZONTAL,
490	XmNtraversalOn, False,
491	XmNisHomogeneous, False,
492	XmNpacking, XmPACK_TIGHT,
493	XmNspacing, 0,
494	XmNshadowThickness, 0,
495	XmNhighlightThickness, 0,
496	XmNmarginHeight, 0,
497	XmNmarginWidth, 0,
498	XmNadjustLast, True,
499	NULL);
500    gui_motif_menu_colors(toolBar);
501
502#endif
503
504#ifdef FEAT_GUI_TABLINE
505    /* Create the Vim GUI tabline */
506    n = 0;
507    XtSetArg(args[n], XmNbindingType, XmNONE); n++;
508    XtSetArg(args[n], XmNorientation, XmVERTICAL); n++;
509    XtSetArg(args[n], XmNbackPageSize, XmNONE); n++;
510    XtSetArg(args[n], XmNbackPageNumber, 0); n++;
511    XtSetArg(args[n], XmNbackPagePlacement, XmTOP_RIGHT); n++;
512    XtSetArg(args[n], XmNmajorTabSpacing, 0); n++;
513    XtSetArg(args[n], XmNshadowThickness, 0); n++;
514    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
515    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
516    tabLine = XmCreateNotebook(vimForm, "Vim tabline", args, n);
517
518    XtAddCallback(tabLine, XmNpageChangedCallback, (XtCallbackProc)tabline_cb,
519			NULL);
520    XtAddEventHandler(tabLine, ButtonPressMask, False,
521			(XtEventHandler)tabline_menu_cb, NULL);
522
523    gui.tabline_height = TABLINE_HEIGHT;
524
525    /*
526     * Set the size of the minor next/prev scrollers to zero, so
527     * that they are not displayed. Due to a bug in OpenMotif 2.3,
528     * even if these children widget are unmanaged, they are again
529     * managed by the Notebook widget and the notebook widget geometry
530     * is adjusted to account for the minor scroller widgets.
531     */
532    scroller = XtNameToWidget(tabLine, "MinorTabScrollerNext");
533    XtVaSetValues(scroller, XmNwidth, 0, XmNresizable, False,
534		  XmNtraversalOn, False, NULL);
535    scroller = XtNameToWidget(tabLine, "MinorTabScrollerPrevious");
536    XtVaSetValues(scroller, XmNwidth, 0, XmNresizable, False,
537		  XmNtraversalOn, False, NULL);
538
539    /* Create the tabline popup menu */
540    tabLine_menu = XmCreatePopupMenu(tabLine, "tabline popup", NULL, 0);
541
542    /* Add the buttons to the menu */
543    n = 0;
544    XtSetArg(args[n], XmNuserData, TABLINE_MENU_CLOSE); n++;
545    xms = XmStringCreate((char *)"Close tab", STRING_TAG);
546    XtSetArg(args[n], XmNlabelString, xms); n++;
547    button = XmCreatePushButton(tabLine_menu, "Close", args, n);
548    XtAddCallback(button, XmNactivateCallback,
549		  (XtCallbackProc)tabline_button_cb, NULL);
550    XmStringFree(xms);
551
552    n = 0;
553    XtSetArg(args[n], XmNuserData, TABLINE_MENU_NEW); n++;
554    xms = XmStringCreate((char *)"New Tab", STRING_TAG);
555    XtSetArg(args[n], XmNlabelString, xms); n++;
556    button = XmCreatePushButton(tabLine_menu, "New Tab", args, n);
557    XtAddCallback(button, XmNactivateCallback,
558		  (XtCallbackProc)tabline_button_cb, NULL);
559    XmStringFree(xms);
560
561    n = 0;
562    XtSetArg(args[n], XmNuserData, TABLINE_MENU_OPEN); n++;
563    xms = XmStringCreate((char *)"Open tab...", STRING_TAG);
564    XtSetArg(args[n], XmNlabelString, xms); n++;
565    button = XmCreatePushButton(tabLine_menu, "Open tab...", args, n);
566    XtAddCallback(button, XmNactivateCallback,
567		  (XtCallbackProc)tabline_button_cb, NULL);
568    XmStringFree(xms);
569#endif
570
571    textAreaForm = XtVaCreateManagedWidget("textAreaForm",
572	xmFormWidgetClass, vimForm,
573	XmNleftAttachment, XmATTACH_FORM,
574	XmNrightAttachment, XmATTACH_FORM,
575	XmNbottomAttachment, XmATTACH_FORM,
576	XmNtopAttachment, XmATTACH_FORM,
577	XmNmarginWidth, 0,
578	XmNmarginHeight, 0,
579	XmNresizePolicy, XmRESIZE_ANY,
580	NULL);
581    gui_motif_scroll_colors(textAreaForm);
582
583    textArea = XtVaCreateManagedWidget("textArea",
584	xmDrawingAreaWidgetClass, textAreaForm,
585	XmNforeground, gui.norm_pixel,
586	XmNbackground, gui.back_pixel,
587	XmNleftAttachment, XmATTACH_FORM,
588	XmNtopAttachment, XmATTACH_FORM,
589	XmNrightAttachment, XmATTACH_FORM,
590	XmNbottomAttachment, XmATTACH_FORM,
591
592	/*
593	 * These take some control away from the user, but avoids making them
594	 * add resources to get a decent looking setup.
595	 */
596	XmNborderWidth, 0,
597	XmNhighlightThickness, 0,
598	XmNshadowThickness, 0,
599	NULL);
600
601#ifdef FEAT_FOOTER
602    /*
603     * Create the Footer.
604     */
605    footer = XtVaCreateWidget("footer",
606	xmLabelGadgetClass, vimForm,
607	XmNalignment, XmALIGNMENT_BEGINNING,
608	XmNmarginHeight, 0,
609	XmNmarginWidth, 0,
610	XmNtraversalOn, False,
611	XmNrecomputeSize, False,
612	XmNleftAttachment, XmATTACH_FORM,
613	XmNleftOffset, 5,
614	XmNrightAttachment, XmATTACH_FORM,
615	XmNbottomAttachment, XmATTACH_FORM,
616	NULL);
617    gui_mch_set_footer((char_u *) "");
618#endif
619
620    /*
621     * Install the callbacks.
622     */
623    gui_x11_callbacks(textArea, vimForm);
624
625    /* Pretend we don't have input focus, we will get an event if we do. */
626    gui.in_focus = FALSE;
627}
628
629/*
630 * Called when the GUI is not going to start after all.
631 */
632    void
633gui_x11_destroy_widgets()
634{
635    textArea = NULL;
636#ifdef FEAT_MENU
637    menuBar = NULL;
638#endif
639}
640
641    void
642gui_mch_set_text_area_pos(x, y, w, h)
643    int	    x UNUSED;
644    int	    y UNUSED;
645    int	    w UNUSED;
646    int	    h UNUSED;
647{
648#ifdef FEAT_TOOLBAR
649    /* Give keyboard focus to the textArea instead of the toolbar. */
650    reset_focus();
651#endif
652}
653
654    void
655gui_x11_set_back_color()
656{
657    if (textArea != NULL)
658#if (XmVersion >= 1002)
659	XmChangeColor(textArea, gui.back_pixel);
660#else
661	XtVaSetValues(textArea,
662		  XmNbackground, gui.back_pixel,
663		  NULL);
664#endif
665}
666
667/*
668 * Manage dialog centered on pointer. This could be used by the Athena code as
669 * well.
670 */
671    void
672manage_centered(dialog_child)
673    Widget dialog_child;
674{
675    Widget shell = XtParent(dialog_child);
676    Window root, child;
677    unsigned int mask;
678    unsigned int width, height, border_width, depth;
679    int x, y, win_x, win_y, maxX, maxY;
680    Boolean mappedWhenManaged;
681
682    /* Temporarily set value of XmNmappedWhenManaged
683       to stop the dialog from popping up right away */
684    XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL);
685    XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL);
686
687    XtManageChild(dialog_child);
688
689    /* Get the pointer position (x, y) */
690    XQueryPointer(XtDisplay(shell), XtWindow(shell), &root, &child,
691		  &x, &y, &win_x, &win_y, &mask);
692
693    /* Translate the pointer position (x, y) into a position for the new
694       window that will place the pointer at its center */
695    XGetGeometry(XtDisplay(shell), XtWindow(shell), &root, &win_x, &win_y,
696		 &width, &height, &border_width, &depth);
697    width += 2 * border_width;
698    height += 2 * border_width;
699    x -= width / 2;
700    y -= height / 2;
701
702    /* Ensure that the dialog remains on screen */
703    maxX = XtScreen(shell)->width - width;
704    maxY = XtScreen(shell)->height - height;
705    if (x < 0)
706	x = 0;
707    if (x > maxX)
708	x = maxX;
709    if (y < 0)
710	y = 0;
711    if (y > maxY)
712	y = maxY;
713
714    /* Set desired window position in the DialogShell */
715    XtVaSetValues(shell, XmNx, x, XmNy, y, NULL);
716
717    /* Map the widget */
718    XtMapWidget(shell);
719
720    /* Restore the value of XmNmappedWhenManaged */
721    XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL);
722}
723
724#if defined(FEAT_MENU) || defined(FEAT_SUN_WORKSHOP) \
725	|| defined(FEAT_GUI_DIALOG) || defined(PROTO)
726
727/*
728 * Encapsulate the way an XmFontList is created.
729 */
730    XmFontList
731gui_motif_create_fontlist(font)
732    XFontStruct    *font;
733{
734    XmFontList font_list;
735
736# if (XmVersion <= 1001)
737    /* Motif 1.1 method */
738    font_list = XmFontListCreate(font, STRING_TAG);
739# else
740    /* Motif 1.2 method */
741    XmFontListEntry font_list_entry;
742
743    font_list_entry = XmFontListEntryCreate(STRING_TAG, XmFONT_IS_FONT,
744					    (XtPointer)font);
745    font_list = XmFontListAppendEntry(NULL, font_list_entry);
746    XmFontListEntryFree(&font_list_entry);
747# endif
748    return font_list;
749}
750
751# if ((XmVersion > 1001) && defined(FEAT_XFONTSET)) || defined(PROTO)
752    XmFontList
753gui_motif_fontset2fontlist(fontset)
754    XFontSet	*fontset;
755{
756    XmFontList font_list;
757
758    /* Motif 1.2 method */
759    XmFontListEntry font_list_entry;
760
761    font_list_entry = XmFontListEntryCreate(STRING_TAG,
762					    XmFONT_IS_FONTSET,
763					    (XtPointer)*fontset);
764    font_list = XmFontListAppendEntry(NULL, font_list_entry);
765    XmFontListEntryFree(&font_list_entry);
766    return font_list;
767}
768# endif
769
770#endif
771
772#if defined(FEAT_MENU) || defined(PROTO)
773/*
774 * Menu stuff.
775 */
776
777static void gui_motif_add_actext __ARGS((vimmenu_T *menu));
778#if (XmVersion >= 1002)
779static void toggle_tearoff __ARGS((Widget wid));
780static void gui_mch_recurse_tearoffs __ARGS((vimmenu_T *menu));
781#endif
782static void submenu_change __ARGS((vimmenu_T *mp, int colors));
783
784static void do_set_mnemonics __ARGS((int enable));
785static int menu_enabled = TRUE;
786
787    void
788gui_mch_enable_menu(flag)
789    int	    flag;
790{
791    if (flag)
792    {
793	XtManageChild(menuBar);
794#ifdef FEAT_TOOLBAR
795	if (XtIsManaged(XtParent(toolBar)))
796	{
797	    /* toolBar is attached to top form */
798	    XtVaSetValues(XtParent(toolBar),
799		XmNtopAttachment, XmATTACH_WIDGET,
800		XmNtopWidget, menuBar,
801		NULL);
802#ifdef FEAT_GUI_TABLINE
803	    if (showing_tabline)
804	    {
805		XtVaSetValues(tabLine,
806			      XmNtopAttachment, XmATTACH_WIDGET,
807			      XmNtopWidget, XtParent(toolBar),
808			      NULL);
809		XtVaSetValues(textAreaForm,
810			      XmNtopAttachment, XmATTACH_WIDGET,
811			      XmNtopWidget, tabLine,
812			      NULL);
813	    }
814	    else
815#endif
816		XtVaSetValues(textAreaForm,
817			      XmNtopAttachment, XmATTACH_WIDGET,
818			      XmNtopWidget, XtParent(toolBar),
819			      NULL);
820	}
821	else
822#endif
823	{
824#ifdef FEAT_GUI_TABLINE
825	    if (showing_tabline)
826	    {
827		XtVaSetValues(tabLine,
828			      XmNtopAttachment, XmATTACH_WIDGET,
829			      XmNtopWidget, menuBar,
830			      NULL);
831		XtVaSetValues(textAreaForm,
832			      XmNtopAttachment, XmATTACH_WIDGET,
833			      XmNtopWidget, tabLine,
834			      NULL);
835	    }
836	    else
837#endif
838		XtVaSetValues(textAreaForm,
839			      XmNtopAttachment, XmATTACH_WIDGET,
840			      XmNtopWidget, menuBar,
841			      NULL);
842	}
843    }
844    else
845    {
846	XtUnmanageChild(menuBar);
847#ifdef FEAT_TOOLBAR
848	if (XtIsManaged(XtParent(toolBar)))
849	{
850	    XtVaSetValues(XtParent(toolBar),
851		XmNtopAttachment, XmATTACH_FORM,
852		NULL);
853#ifdef FEAT_GUI_TABLINE
854	    if (showing_tabline)
855	    {
856		XtVaSetValues(tabLine,
857			      XmNtopAttachment, XmATTACH_WIDGET,
858			      XmNtopWidget, XtParent(toolBar),
859			      NULL);
860		XtVaSetValues(textAreaForm,
861			      XmNtopAttachment, XmATTACH_WIDGET,
862			      XmNtopWidget, tabLine,
863			      NULL);
864	    }
865	    else
866#endif
867		XtVaSetValues(textAreaForm,
868			      XmNtopAttachment, XmATTACH_WIDGET,
869			      XmNtopWidget, XtParent(toolBar),
870			      NULL);
871	}
872	else
873#endif
874	{
875#ifdef FEAT_GUI_TABLINE
876	    if (showing_tabline)
877	    {
878		XtVaSetValues(tabLine,
879			      XmNtopAttachment, XmATTACH_FORM,
880			      NULL);
881		XtVaSetValues(textAreaForm,
882			      XmNtopAttachment, XmATTACH_WIDGET,
883			      XmNtopWidget, tabLine,
884			      NULL);
885	    }
886	    else
887#endif
888		XtVaSetValues(textAreaForm,
889			      XmNtopAttachment, XmATTACH_FORM,
890			      NULL);
891	}
892    }
893
894}
895
896/*
897 * Enable or disable mnemonics for the toplevel menus.
898 */
899    void
900gui_motif_set_mnemonics(enable)
901    int		enable;
902{
903    /*
904     * Don't enable menu mnemonics when the menu bar is disabled, LessTif
905     * crashes when using a mnemonic then.
906     */
907    if (!menu_enabled)
908	enable = FALSE;
909    do_set_mnemonics(enable);
910}
911
912    static void
913do_set_mnemonics(enable)
914    int		enable;
915{
916    vimmenu_T	*menu;
917
918    for (menu = root_menu; menu != NULL; menu = menu->next)
919	if (menu->id != (Widget)0)
920	    XtVaSetValues(menu->id,
921		    XmNmnemonic, enable ? menu->mnemonic : NUL,
922		    NULL);
923}
924
925    void
926gui_mch_add_menu(menu, idx)
927    vimmenu_T	*menu;
928    int		idx;
929{
930    XmString	label;
931    Widget	shell;
932    vimmenu_T	*parent = menu->parent;
933
934#ifdef MOTIF_POPUP
935    if (menu_is_popup(menu->name))
936    {
937	Arg arg[2];
938	int n = 0;
939
940	/* Only create the popup menu when it's actually used, otherwise there
941	 * is a delay when using the right mouse button. */
942# if (XmVersion <= 1002)
943	if (mouse_model_popup())
944# endif
945	{
946	    if (gui.menu_bg_pixel != INVALCOLOR)
947	    {
948		XtSetArg(arg[0], XmNbackground, gui.menu_bg_pixel); n++;
949	    }
950	    if (gui.menu_fg_pixel != INVALCOLOR)
951	    {
952		XtSetArg(arg[1], XmNforeground, gui.menu_fg_pixel); n++;
953	    }
954	    menu->submenu_id = XmCreatePopupMenu(textArea, "contextMenu",
955								      arg, n);
956	    menu->id = (Widget)0;
957	}
958	return;
959    }
960#endif
961
962    if (!menu_is_menubar(menu->name)
963	    || (parent != NULL && parent->submenu_id == (Widget)0))
964	return;
965
966    label = XmStringCreate((char *)menu->dname, STRING_TAG);
967    if (label == NULL)
968	return;
969    menu->id = XtVaCreateWidget("subMenu",
970	    xmCascadeButtonWidgetClass,
971	    (parent == NULL) ? menuBar : parent->submenu_id,
972	    XmNlabelString, label,
973	    XmNmnemonic, p_wak[0] == 'n' ? NUL : menu->mnemonic,
974#if (XmVersion >= 1002)
975	    /* submenu: count the tearoff item (needed for LessTif) */
976	    XmNpositionIndex, idx + (parent != NULL
977			   && tearoff_val == (int)XmTEAR_OFF_ENABLED ? 1 : 0),
978#endif
979	    NULL);
980    gui_motif_menu_colors(menu->id);
981    gui_motif_menu_fontlist(menu->id);
982    XmStringFree(label);
983
984    if (menu->id == (Widget)0)		/* failed */
985	return;
986
987    /* add accelerator text */
988    gui_motif_add_actext(menu);
989
990    shell = XtVaCreateWidget("subMenuShell",
991	xmMenuShellWidgetClass, menu->id,
992	XmNwidth, 1,
993	XmNheight, 1,
994	NULL);
995    gui_motif_menu_colors(shell);
996    menu->submenu_id = XtVaCreateWidget("rowColumnMenu",
997	xmRowColumnWidgetClass, shell,
998	XmNrowColumnType, XmMENU_PULLDOWN,
999	NULL);
1000    gui_motif_menu_colors(menu->submenu_id);
1001
1002    if (menu->submenu_id == (Widget)0)		/* failed */
1003	return;
1004
1005#if (XmVersion >= 1002)
1006    /* Set the colors for the tear off widget */
1007    toggle_tearoff(menu->submenu_id);
1008#endif
1009
1010    XtVaSetValues(menu->id,
1011	XmNsubMenuId, menu->submenu_id,
1012	NULL);
1013
1014    /*
1015     * The "Help" menu is a special case, and should be placed at the far
1016     * right hand side of the menu-bar.  It's recognized by its high priority.
1017     */
1018    if (parent == NULL && menu->priority >= 9999)
1019	XtVaSetValues(menuBar,
1020		XmNmenuHelpWidget, menu->id,
1021		NULL);
1022
1023    /*
1024     * When we add a top-level item to the menu bar, we can figure out how
1025     * high the menu bar should be.
1026     */
1027    if (parent == NULL)
1028	gui_mch_compute_menu_height(menu->id);
1029}
1030
1031
1032/*
1033 * Add mnemonic and accelerator text to a menu button.
1034 */
1035    static void
1036gui_motif_add_actext(menu)
1037    vimmenu_T	*menu;
1038{
1039    XmString	label;
1040
1041    /* Add accelerator text, if there is one */
1042    if (menu->actext != NULL && menu->id != (Widget)0)
1043    {
1044	label = XmStringCreate((char *)menu->actext, STRING_TAG);
1045	if (label == NULL)
1046	    return;
1047	XtVaSetValues(menu->id, XmNacceleratorText, label, NULL);
1048	XmStringFree(label);
1049    }
1050}
1051
1052    void
1053gui_mch_toggle_tearoffs(enable)
1054    int		enable;
1055{
1056#if (XmVersion >= 1002)
1057    if (enable)
1058	tearoff_val = (int)XmTEAR_OFF_ENABLED;
1059    else
1060	tearoff_val = (int)XmTEAR_OFF_DISABLED;
1061    toggle_tearoff(menuBar);
1062    gui_mch_recurse_tearoffs(root_menu);
1063#endif
1064}
1065
1066#if (XmVersion >= 1002)
1067/*
1068 * Set the tearoff for one menu widget on or off, and set the color of the
1069 * tearoff widget.
1070 */
1071    static void
1072toggle_tearoff(wid)
1073    Widget	wid;
1074{
1075    Widget	w;
1076
1077    XtVaSetValues(wid, XmNtearOffModel, tearoff_val, NULL);
1078    if (tearoff_val == (int)XmTEAR_OFF_ENABLED
1079	    && (w = XmGetTearOffControl(wid)) != (Widget)0)
1080	gui_motif_menu_colors(w);
1081}
1082
1083    static void
1084gui_mch_recurse_tearoffs(menu)
1085    vimmenu_T	*menu;
1086{
1087    while (menu != NULL)
1088    {
1089	if (!menu_is_popup(menu->name))
1090	{
1091	    if (menu->submenu_id != (Widget)0)
1092		toggle_tearoff(menu->submenu_id);
1093	    gui_mch_recurse_tearoffs(menu->children);
1094	}
1095	menu = menu->next;
1096    }
1097}
1098#endif
1099
1100    int
1101gui_mch_text_area_extra_height()
1102{
1103    Dimension	shadowHeight;
1104
1105    XtVaGetValues(textAreaForm, XmNshadowThickness, &shadowHeight, NULL);
1106    return shadowHeight;
1107}
1108
1109/*
1110 * Compute the height of the menu bar.
1111 * We need to check all the items for their position and height, for the case
1112 * there are several rows, and/or some characters extend higher or lower.
1113 */
1114    void
1115gui_mch_compute_menu_height(id)
1116    Widget	id;		    /* can be NULL when deleting menu */
1117{
1118    Dimension	y, maxy;
1119    Dimension	margin, shadow;
1120    vimmenu_T	*mp;
1121    static Dimension	height = 21;	/* normal height of a menu item */
1122
1123    /*
1124     * Get the height of the new item, before managing it, because it will
1125     * still reflect the font size.  After managing it depends on the menu
1126     * height, which is what we just wanted to get!.
1127     */
1128    if (id != (Widget)0)
1129	XtVaGetValues(id, XmNheight, &height, NULL);
1130
1131    /* Find any menu Widget, to be able to call XtManageChild() */
1132    else
1133	for (mp = root_menu; mp != NULL; mp = mp->next)
1134	    if (mp->id != (Widget)0 && menu_is_menubar(mp->name))
1135	    {
1136		id = mp->id;
1137		break;
1138	    }
1139
1140    /*
1141     * Now manage the menu item, to make them all be positioned (makes an
1142     * extra row when needed, removes it when not needed).
1143     */
1144    if (id != (Widget)0)
1145	XtManageChild(id);
1146
1147    /*
1148     * Now find the menu item that is the furthest down, and get it's position.
1149     */
1150    maxy = 0;
1151    for (mp = root_menu; mp != NULL; mp = mp->next)
1152    {
1153	if (mp->id != (Widget)0 && menu_is_menubar(mp->name))
1154	{
1155	    XtVaGetValues(mp->id, XmNy, &y, NULL);
1156	    if (y > maxy)
1157		maxy = y;
1158	}
1159    }
1160
1161    XtVaGetValues(menuBar,
1162	XmNmarginHeight, &margin,
1163	XmNshadowThickness, &shadow,
1164	NULL);
1165
1166    /*
1167     * This computation is the result of trial-and-error:
1168     * maxy =	The maximum position of an item; required for when there are
1169     *		two or more rows
1170     * height = height of an item, before managing it;	Hopefully this will
1171     *		change with the font height.  Includes shadow-border.
1172     * shadow =	shadow-border; must be subtracted from the height.
1173     * margin = margin around the menu buttons;  Must be added.
1174     * Add 4 for the underlining of shortcut keys.
1175     */
1176    gui.menu_height = maxy + height - 2 * shadow + 2 * margin + 4;
1177
1178    /* Somehow the menu bar doesn't resize automatically.  Set it here,
1179     * even though this is a catch 22.  Don't do this when starting up,
1180     * somehow the menu gets very high then. */
1181    if (gui.shell_created)
1182	XtVaSetValues(menuBar, XmNheight, gui.menu_height, NULL);
1183}
1184
1185#ifdef FEAT_TOOLBAR
1186
1187/*
1188 * Icons used by the toolbar code.
1189 */
1190#include "gui_x11_pm.h"
1191
1192static int check_xpm __ARGS((char_u *path));
1193static char **get_toolbar_pixmap __ARGS((vimmenu_T *menu, char **fname));
1194static int add_pixmap_args __ARGS((vimmenu_T *menu, Arg *args, int n));
1195
1196/*
1197 * Read an Xpm file.  Return OK or FAIL.
1198 */
1199    static int
1200check_xpm(path)
1201    char_u	*path;
1202{
1203    XpmAttributes attrs;
1204    int		status;
1205    Pixmap	mask;
1206    Pixmap	map;
1207
1208    attrs.valuemask = 0;
1209
1210    /* Create the "sensitive" pixmap */
1211    status = XpmReadFileToPixmap(gui.dpy,
1212	    RootWindow(gui.dpy, DefaultScreen(gui.dpy)),
1213	    (char *)path, &map, &mask, &attrs);
1214    XpmFreeAttributes(&attrs);
1215
1216    if (status == XpmSuccess)
1217	return OK;
1218    return FAIL;
1219}
1220
1221
1222/*
1223 * Allocated a pixmap for toolbar menu "menu".
1224 * When it's to be read from a file, "fname" is set to the file name
1225 * (in allocated memory).
1226 * Return a blank pixmap if it fails.
1227 */
1228    static char **
1229get_toolbar_pixmap(menu, fname)
1230    vimmenu_T	*menu;
1231    char	**fname;
1232{
1233    char_u	buf[MAXPATHL];		/* buffer storing expanded pathname */
1234    char	**xpm = NULL;		/* xpm array */
1235    int		res;
1236
1237    *fname = NULL;
1238    buf[0] = NUL;			/* start with NULL path */
1239
1240    if (menu->iconfile != NULL)
1241    {
1242	/* Use the "icon="  argument. */
1243	gui_find_iconfile(menu->iconfile, buf, "xpm");
1244	res = check_xpm(buf);
1245
1246	/* If it failed, try using the menu name. */
1247	if (res == FAIL && gui_find_bitmap(menu->name, buf, "xpm") == OK)
1248	    res = check_xpm(buf);
1249	if (res == OK)
1250	{
1251	    *fname = (char *)vim_strsave(buf);
1252	    return tb_blank_xpm;
1253	}
1254    }
1255
1256    if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL)
1257    {
1258	if (menu->iconidx >= 0 && menu->iconidx
1259	       < (int)(sizeof(built_in_pixmaps) / sizeof(built_in_pixmaps[0])))
1260	    xpm = built_in_pixmaps[menu->iconidx];
1261	else
1262	    xpm = tb_blank_xpm;
1263    }
1264
1265    return xpm;
1266}
1267
1268/*
1269 * Add arguments for the toolbar pixmap to a menu item.
1270 */
1271    static int
1272add_pixmap_args(menu, args, n)
1273    vimmenu_T	*menu;
1274    Arg		*args;
1275    int		n;
1276{
1277    vim_free(menu->xpm_fname);
1278    menu->xpm = get_toolbar_pixmap(menu, &menu->xpm_fname);
1279    if (menu->xpm == NULL)
1280    {
1281	XtSetArg(args[n], XmNlabelType, XmSTRING); n++;
1282    }
1283    else
1284    {
1285	if (menu->xpm_fname != NULL)
1286	{
1287	    XtSetArg(args[n], XmNpixmapFile, menu->xpm_fname); n++;
1288	}
1289	XtSetArg(args[n], XmNpixmapData, menu->xpm); n++;
1290	XtSetArg(args[n], XmNlabelLocation, XmBOTTOM); n++;
1291    }
1292    return n;
1293}
1294#endif /* FEAT_TOOLBAR */
1295
1296    void
1297gui_mch_add_menu_item(menu, idx)
1298    vimmenu_T	*menu;
1299    int		idx;
1300{
1301    XmString	label;
1302    vimmenu_T	*parent = menu->parent;
1303
1304# ifdef EBCDIC
1305    menu->mnemonic = 0;
1306# endif
1307
1308# if (XmVersion <= 1002)
1309    /* Don't add Popup menu items when the popup menu isn't used. */
1310    if (menu_is_child_of_popup(menu) && !mouse_model_popup())
1311	return;
1312# endif
1313
1314# ifdef FEAT_TOOLBAR
1315    if (menu_is_toolbar(parent->name))
1316    {
1317	WidgetClass	type;
1318	XmString	xms = NULL;    /* fallback label if pixmap not found */
1319	int		n;
1320	Arg		args[18];
1321
1322	n = 0;
1323	if (menu_is_separator(menu->name))
1324	{
1325	    char	*cp;
1326	    Dimension	wid;
1327
1328	    /*
1329	     * A separator has the format "-sep%d[:%d]-". The optional :%d is
1330	     * a width specifier. If no width is specified then we choose one.
1331	     */
1332	    cp = (char *)vim_strchr(menu->name, ':');
1333	    if (cp != NULL)
1334		wid = (Dimension)atoi(++cp);
1335	    else
1336		wid = 4;
1337
1338	    type = xmSeparatorWidgetClass;
1339	    XtSetArg(args[n], XmNwidth, wid); n++;
1340	    XtSetArg(args[n], XmNminWidth, wid); n++;
1341	    XtSetArg(args[n], XmNorientation, XmVERTICAL); n++;
1342	    XtSetArg(args[n], XmNseparatorType, XmSHADOW_ETCHED_IN); n++;
1343	}
1344	else
1345	{
1346	    /* Without shadows one can't sense whatever the button has been
1347	     * pressed or not! However we wan't to save a bit of space...
1348	     * Need the highlightThickness to see the focus.
1349	     */
1350	    XtSetArg(args[n], XmNhighlightThickness, 1); n++;
1351	    XtSetArg(args[n], XmNhighlightOnEnter, True); n++;
1352	    XtSetArg(args[n], XmNmarginWidth, 0); n++;
1353	    XtSetArg(args[n], XmNmarginHeight, 0); n++;
1354	    XtSetArg(args[n], XmNtraversalOn, False); n++;
1355	    /* Set the label here, so that we can switch between icons/text
1356	     * by changing the XmNlabelType resource. */
1357	    xms = XmStringCreate((char *)menu->dname, STRING_TAG);
1358	    XtSetArg(args[n], XmNlabelString, xms); n++;
1359
1360	    n = add_pixmap_args(menu, args, n);
1361
1362	    type = xmEnhancedButtonWidgetClass;
1363	}
1364
1365	XtSetArg(args[n], XmNpositionIndex, idx); n++;
1366	if (menu->id == NULL)
1367	{
1368	    menu->id = XtCreateManagedWidget((char *)menu->dname,
1369			type, toolBar, args, n);
1370	    if (menu->id != NULL && type == xmEnhancedButtonWidgetClass)
1371	    {
1372		XtAddCallback(menu->id,
1373			XmNactivateCallback, gui_x11_menu_cb, menu);
1374# ifdef FEAT_FOOTER
1375		XtAddEventHandler(menu->id, EnterWindowMask, False,
1376			toolbarbutton_enter_cb, menu);
1377		XtAddEventHandler(menu->id, LeaveWindowMask, False,
1378			toolbarbutton_leave_cb, menu);
1379# endif
1380	    }
1381	}
1382	else
1383	    XtSetValues(menu->id, args, n);
1384	if (xms != NULL)
1385	    XmStringFree(xms);
1386
1387# ifdef FEAT_BEVAL
1388	gui_mch_menu_set_tip(menu);
1389# endif
1390
1391	menu->parent = parent;
1392	menu->submenu_id = NULL;
1393	/* When adding first item to toolbar it might have to be enabled .*/
1394	if (!XtIsManaged(XtParent(toolBar))
1395		    && vim_strchr(p_go, GO_TOOLBAR) != NULL)
1396	    gui_mch_show_toolbar(TRUE);
1397	gui.toolbar_height = gui_mch_compute_toolbar_height();
1398	return;
1399    } /* toolbar menu item */
1400# endif
1401
1402    /* No parent, must be a non-menubar menu */
1403    if (parent->submenu_id == (Widget)0)
1404	return;
1405
1406    menu->submenu_id = (Widget)0;
1407
1408    /* Add menu separator */
1409    if (menu_is_separator(menu->name))
1410    {
1411	menu->id = XtVaCreateWidget("subMenu",
1412		xmSeparatorGadgetClass, parent->submenu_id,
1413#if (XmVersion >= 1002)
1414		/* count the tearoff item (needed for LessTif) */
1415		XmNpositionIndex, idx + (tearoff_val == (int)XmTEAR_OFF_ENABLED
1416								     ? 1 : 0),
1417#endif
1418		NULL);
1419	gui_motif_menu_colors(menu->id);
1420	return;
1421    }
1422
1423    label = XmStringCreate((char *)menu->dname, STRING_TAG);
1424    if (label == NULL)
1425	return;
1426    menu->id = XtVaCreateWidget("subMenu",
1427	xmPushButtonWidgetClass, parent->submenu_id,
1428	XmNlabelString, label,
1429	XmNmnemonic, menu->mnemonic,
1430#if (XmVersion >= 1002)
1431	/* count the tearoff item (needed for LessTif) */
1432	XmNpositionIndex, idx + (tearoff_val == (int)XmTEAR_OFF_ENABLED
1433								     ? 1 : 0),
1434#endif
1435	NULL);
1436    gui_motif_menu_colors(menu->id);
1437    gui_motif_menu_fontlist(menu->id);
1438    XmStringFree(label);
1439
1440    if (menu->id != (Widget)0)
1441    {
1442	XtAddCallback(menu->id, XmNactivateCallback, gui_x11_menu_cb,
1443		(XtPointer)menu);
1444	/* add accelerator text */
1445	gui_motif_add_actext(menu);
1446    }
1447}
1448
1449#if (XmVersion <= 1002) || defined(PROTO)
1450/*
1451 * This function will destroy/create the popup menus dynamically,
1452 * according to the value of 'mousemodel'.
1453 * This will fix the "right mouse button freeze" that occurs when
1454 * there exists a popup menu but it isn't managed.
1455 */
1456    void
1457gui_motif_update_mousemodel(menu)
1458    vimmenu_T	*menu;
1459{
1460    int		idx = 0;
1461
1462    /* When GUI hasn't started the menus have not been created. */
1463    if (!gui.in_use)
1464      return;
1465
1466    while (menu)
1467    {
1468      if (menu->children != NULL)
1469      {
1470	  if (menu_is_popup(menu->name))
1471	  {
1472	      if (mouse_model_popup())
1473	      {
1474		  /* Popup menu will be used.  Create the popup menus. */
1475		  gui_mch_add_menu(menu, idx);
1476		  gui_motif_update_mousemodel(menu->children);
1477	      }
1478	      else
1479	      {
1480		  /* Popup menu will not be used.  Destroy the popup menus. */
1481		  gui_motif_update_mousemodel(menu->children);
1482		  gui_mch_destroy_menu(menu);
1483	      }
1484	  }
1485      }
1486      else if (menu_is_child_of_popup(menu))
1487      {
1488	  if (mouse_model_popup())
1489	      gui_mch_add_menu_item(menu, idx);
1490	  else
1491	      gui_mch_destroy_menu(menu);
1492      }
1493      menu = menu->next;
1494      ++idx;
1495    }
1496}
1497#endif
1498
1499    void
1500gui_mch_new_menu_colors()
1501{
1502    if (menuBar == (Widget)0)
1503	return;
1504    gui_motif_menu_colors(menuBar);
1505#ifdef FEAT_TOOLBAR
1506    gui_motif_menu_colors(toolBarFrame);
1507    gui_motif_menu_colors(toolBar);
1508#endif
1509
1510    submenu_change(root_menu, TRUE);
1511}
1512
1513    void
1514gui_mch_new_menu_font()
1515{
1516    if (menuBar == (Widget)0)
1517	return;
1518    submenu_change(root_menu, FALSE);
1519    {
1520	Dimension   height;
1521	Position w, h;
1522
1523	XtVaGetValues(menuBar, XmNheight, &height, NULL);
1524	gui.menu_height = height;
1525
1526	XtVaGetValues(vimShell, XtNwidth, &w, XtNheight, &h, NULL);
1527	gui_resize_shell(w, h
1528#ifdef FEAT_XIM
1529		- xim_get_status_area_height()
1530#endif
1531		     );
1532    }
1533    gui_set_shellsize(FALSE, TRUE, RESIZE_VERT);
1534    ui_new_shellsize();
1535}
1536
1537#if defined(FEAT_BEVAL) || defined(PROTO)
1538    void
1539gui_mch_new_tooltip_font()
1540{
1541# ifdef FEAT_TOOLBAR
1542    vimmenu_T   *menu;
1543
1544    if (toolBar == (Widget)0)
1545	return;
1546
1547    menu = gui_find_menu((char_u *)"ToolBar");
1548    if (menu != NULL)
1549	submenu_change(menu, FALSE);
1550# endif
1551}
1552
1553    void
1554gui_mch_new_tooltip_colors()
1555{
1556# ifdef FEAT_TOOLBAR
1557    vimmenu_T   *toolbar;
1558
1559    if (toolBar == (Widget)0)
1560	return;
1561
1562    toolbar = gui_find_menu((char_u *)"ToolBar");
1563    if (toolbar != NULL)
1564	submenu_change(toolbar, TRUE);
1565# endif
1566}
1567#endif
1568
1569    static void
1570submenu_change(menu, colors)
1571    vimmenu_T	*menu;
1572    int		colors;		/* TRUE for colors, FALSE for font */
1573{
1574    vimmenu_T	*mp;
1575
1576    for (mp = menu; mp != NULL; mp = mp->next)
1577    {
1578	if (mp->id != (Widget)0)
1579	{
1580	    if (colors)
1581	    {
1582		gui_motif_menu_colors(mp->id);
1583#ifdef FEAT_TOOLBAR
1584		/* For a toolbar item: Free the pixmap and allocate a new one,
1585		 * so that the background color is right. */
1586		if (mp->xpm != NULL)
1587		{
1588		    int		n = 0;
1589		    Arg		args[18];
1590
1591		    n = add_pixmap_args(mp, args, n);
1592		    XtSetValues(mp->id, args, n);
1593		}
1594# ifdef FEAT_BEVAL
1595		/* If we have a tooltip, then we need to change it's font */
1596		if (mp->tip != NULL)
1597		{
1598		    Arg args[2];
1599
1600		    args[0].name = XmNbackground;
1601		    args[0].value = gui.tooltip_bg_pixel;
1602		    args[1].name = XmNforeground;
1603		    args[1].value = gui.tooltip_fg_pixel;
1604		    XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1605		}
1606# endif
1607#endif
1608	    }
1609	    else
1610	    {
1611		gui_motif_menu_fontlist(mp->id);
1612#ifdef FEAT_BEVAL
1613		/* If we have a tooltip, then we need to change it's font */
1614		if (mp->tip != NULL)
1615		{
1616		    Arg args[1];
1617
1618		    args[0].name = XmNfontList;
1619		    args[0].value = (XtArgVal)gui_motif_fontset2fontlist(
1620						    &gui.tooltip_fontset);
1621		    XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1622		}
1623#endif
1624	    }
1625	}
1626
1627	if (mp->children != NULL)
1628	{
1629#if (XmVersion >= 1002)
1630	    /* Set the colors/font for the tear off widget */
1631	    if (mp->submenu_id != (Widget)0)
1632	    {
1633		if (colors)
1634		    gui_motif_menu_colors(mp->submenu_id);
1635		else
1636		    gui_motif_menu_fontlist(mp->submenu_id);
1637		toggle_tearoff(mp->submenu_id);
1638	    }
1639#endif
1640	    /* Set the colors for the children */
1641	    submenu_change(mp->children, colors);
1642	}
1643    }
1644}
1645
1646/*
1647 * Destroy the machine specific menu widget.
1648 */
1649    void
1650gui_mch_destroy_menu(menu)
1651    vimmenu_T	*menu;
1652{
1653    /* Please be sure to destroy the parent widget first (i.e. menu->id).
1654     * On the other hand, problems have been reported that the submenu must be
1655     * deleted first...
1656     *
1657     * This code should be basically identical to that in the file gui_athena.c
1658     * because they are both Xt based.
1659     */
1660    if (menu->submenu_id != (Widget)0)
1661    {
1662	XtDestroyWidget(menu->submenu_id);
1663	menu->submenu_id = (Widget)0;
1664    }
1665
1666    if (menu->id != (Widget)0)
1667    {
1668	Widget	    parent;
1669
1670	parent = XtParent(menu->id);
1671#if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL)
1672	if (parent == toolBar && menu->tip != NULL)
1673	{
1674	    /* We try to destroy this before the actual menu, because there are
1675	     * callbacks, etc. that will be unregistered during the tooltip
1676	     * destruction.
1677	     *
1678	     * If you call "gui_mch_destroy_beval_area()" after destroying
1679	     * menu->id, then the tooltip's window will have already been
1680	     * deallocated by Xt, and unknown behaviour will ensue (probably
1681	     * a core dump).
1682	     */
1683	    gui_mch_destroy_beval_area(menu->tip);
1684	    menu->tip = NULL;
1685	}
1686#endif
1687	XtDestroyWidget(menu->id);
1688	menu->id = (Widget)0;
1689	if (parent == menuBar)
1690	    gui_mch_compute_menu_height((Widget)0);
1691#ifdef FEAT_TOOLBAR
1692	else if (parent == toolBar)
1693	{
1694	    Cardinal    num_children;
1695
1696	    /* When removing last toolbar item, don't display the toolbar. */
1697	    XtVaGetValues(toolBar, XmNnumChildren, &num_children, NULL);
1698	    if (num_children == 0)
1699		gui_mch_show_toolbar(FALSE);
1700	    else
1701		gui.toolbar_height = gui_mch_compute_toolbar_height();
1702	}
1703#endif
1704    }
1705}
1706
1707    void
1708gui_mch_show_popupmenu(menu)
1709    vimmenu_T *menu UNUSED;
1710{
1711#ifdef MOTIF_POPUP
1712    XmMenuPosition(menu->submenu_id, gui_x11_get_last_mouse_event());
1713    XtManageChild(menu->submenu_id);
1714#endif
1715}
1716
1717#endif /* FEAT_MENU */
1718
1719/*
1720 * Set the menu and scrollbar colors to their default values.
1721 */
1722    void
1723gui_mch_def_colors()
1724{
1725    if (gui.in_use)
1726    {
1727	/* Use the values saved when starting up.  These should come from the
1728	 * window manager or a resources file. */
1729	gui.menu_fg_pixel = gui.menu_def_fg_pixel;
1730	gui.menu_bg_pixel = gui.menu_def_bg_pixel;
1731	gui.scroll_fg_pixel = gui.scroll_def_fg_pixel;
1732	gui.scroll_bg_pixel = gui.scroll_def_bg_pixel;
1733#ifdef FEAT_BEVAL
1734	gui.tooltip_fg_pixel =
1735			gui_get_color((char_u *)gui.rsrc_tooltip_fg_name);
1736	gui.tooltip_bg_pixel =
1737			gui_get_color((char_u *)gui.rsrc_tooltip_bg_name);
1738#endif
1739    }
1740}
1741
1742
1743/*
1744 * Scrollbar stuff.
1745 */
1746
1747    void
1748gui_mch_set_scrollbar_thumb(sb, val, size, max)
1749    scrollbar_T *sb;
1750    long	val;
1751    long	size;
1752    long	max;
1753{
1754    if (sb->id != (Widget)0)
1755	XtVaSetValues(sb->id,
1756		  XmNvalue, val,
1757		  XmNsliderSize, size,
1758		  XmNpageIncrement, (size > 2 ? size - 2 : 1),
1759		  XmNmaximum, max + 1,	    /* Motif has max one past the end */
1760		  NULL);
1761}
1762
1763    void
1764gui_mch_set_scrollbar_pos(sb, x, y, w, h)
1765    scrollbar_T *sb;
1766    int		x;
1767    int		y;
1768    int		w;
1769    int		h;
1770{
1771    if (sb->id != (Widget)0)
1772    {
1773	if (sb->type == SBAR_LEFT || sb->type == SBAR_RIGHT)
1774	{
1775	    if (y == 0)
1776		h -= gui.border_offset;
1777	    else
1778		y -= gui.border_offset;
1779	    XtVaSetValues(sb->id,
1780			  XmNtopOffset, y,
1781			  XmNbottomOffset, -y - h,
1782			  XmNwidth, w,
1783			  NULL);
1784	}
1785	else
1786	    XtVaSetValues(sb->id,
1787			  XmNtopOffset, y,
1788			  XmNleftOffset, x,
1789			  XmNrightOffset, gui.which_scrollbars[SBAR_RIGHT]
1790						    ? gui.scrollbar_width : 0,
1791			  XmNheight, h,
1792			  NULL);
1793	XtManageChild(sb->id);
1794    }
1795}
1796
1797    void
1798gui_mch_enable_scrollbar(sb, flag)
1799    scrollbar_T *sb;
1800    int		flag;
1801{
1802    Arg		args[16];
1803    int		n;
1804
1805    if (sb->id != (Widget)0)
1806    {
1807	n = 0;
1808	if (flag)
1809	{
1810	    switch (sb->type)
1811	    {
1812		case SBAR_LEFT:
1813		    XtSetArg(args[n], XmNleftOffset, gui.scrollbar_width); n++;
1814		    break;
1815
1816		case SBAR_RIGHT:
1817		    XtSetArg(args[n], XmNrightOffset, gui.scrollbar_width); n++;
1818		    break;
1819
1820		case SBAR_BOTTOM:
1821		    XtSetArg(args[n], XmNbottomOffset, gui.scrollbar_height);n++;
1822		    break;
1823	    }
1824	    XtSetValues(textArea, args, n);
1825	    XtManageChild(sb->id);
1826	}
1827	else
1828	{
1829	    if (!gui.which_scrollbars[sb->type])
1830	    {
1831		/* The scrollbars of this type are all disabled, adjust the
1832		 * textArea attachment offset. */
1833		switch (sb->type)
1834		{
1835		    case SBAR_LEFT:
1836			XtSetArg(args[n], XmNleftOffset, 0); n++;
1837			break;
1838
1839		    case SBAR_RIGHT:
1840			XtSetArg(args[n], XmNrightOffset, 0); n++;
1841			break;
1842
1843		    case SBAR_BOTTOM:
1844			XtSetArg(args[n], XmNbottomOffset, 0);n++;
1845			break;
1846		}
1847		XtSetValues(textArea, args, n);
1848	    }
1849	    XtUnmanageChild(sb->id);
1850	}
1851    }
1852}
1853
1854    void
1855gui_mch_create_scrollbar(sb, orient)
1856    scrollbar_T *sb;
1857    int		orient;	/* SBAR_VERT or SBAR_HORIZ */
1858{
1859    Arg		args[16];
1860    int		n;
1861
1862    n = 0;
1863    XtSetArg(args[n], XmNminimum, 0); n++;
1864    XtSetArg(args[n], XmNorientation,
1865	    (orient == SBAR_VERT) ? XmVERTICAL : XmHORIZONTAL); n++;
1866
1867    switch (sb->type)
1868    {
1869	case SBAR_LEFT:
1870	    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1871	    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_FORM); n++;
1872	    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1873	    break;
1874
1875	case SBAR_RIGHT:
1876	    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
1877	    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_FORM); n++;
1878	    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1879	    break;
1880
1881	case SBAR_BOTTOM:
1882	    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
1883	    XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
1884	    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
1885	    break;
1886    }
1887
1888    sb->id = XtCreateWidget("scrollBar",
1889	    xmScrollBarWidgetClass, textAreaForm, args, n);
1890
1891    /* Remember the default colors, needed for ":hi clear". */
1892    if (gui.scroll_def_bg_pixel == (guicolor_T)0
1893	    && gui.scroll_def_fg_pixel == (guicolor_T)0)
1894	XtVaGetValues(sb->id,
1895		XmNbackground, &gui.scroll_def_bg_pixel,
1896		XmNforeground, &gui.scroll_def_fg_pixel,
1897		NULL);
1898
1899    if (sb->id != (Widget)0)
1900    {
1901	gui_mch_set_scrollbar_colors(sb);
1902	XtAddCallback(sb->id, XmNvalueChangedCallback,
1903		      scroll_cb, (XtPointer)sb->ident);
1904	XtAddCallback(sb->id, XmNdragCallback,
1905		      scroll_cb, (XtPointer)sb->ident);
1906	XtAddEventHandler(sb->id, KeyPressMask, FALSE, gui_x11_key_hit_cb,
1907	    (XtPointer)0);
1908    }
1909}
1910
1911#if defined(FEAT_WINDOWS) || defined(PROTO)
1912    void
1913gui_mch_destroy_scrollbar(sb)
1914    scrollbar_T *sb;
1915{
1916    if (sb->id != (Widget)0)
1917	XtDestroyWidget(sb->id);
1918}
1919#endif
1920
1921    void
1922gui_mch_set_scrollbar_colors(sb)
1923    scrollbar_T *sb;
1924{
1925    if (sb->id != (Widget)0)
1926    {
1927	if (gui.scroll_bg_pixel != INVALCOLOR)
1928	{
1929#if (XmVersion>=1002)
1930	    XmChangeColor(sb->id, gui.scroll_bg_pixel);
1931#else
1932	    XtVaSetValues(sb->id,
1933		    XmNtroughColor, gui.scroll_bg_pixel,
1934		    NULL);
1935#endif
1936	}
1937
1938	if (gui.scroll_fg_pixel != INVALCOLOR)
1939	    XtVaSetValues(sb->id,
1940		    XmNforeground, gui.scroll_fg_pixel,
1941#if (XmVersion<1002)
1942		    XmNbackground, gui.scroll_fg_pixel,
1943#endif
1944		    NULL);
1945    }
1946
1947    /* This is needed for the rectangle below the vertical scrollbars. */
1948    if (sb == &gui.bottom_sbar && textAreaForm != (Widget)0)
1949	gui_motif_scroll_colors(textAreaForm);
1950}
1951
1952/*
1953 * Miscellaneous stuff:
1954 */
1955
1956    Window
1957gui_x11_get_wid()
1958{
1959    return(XtWindow(textArea));
1960}
1961
1962/*
1963 * Look for a widget in the widget tree w, with a mnemonic matching keycode.
1964 * When one is found, simulate a button press on that widget and give it the
1965 * keyboard focus.  If the mnemonic is on a label, look in the userData field
1966 * of the label to see if it points to another widget, and give that the focus.
1967 */
1968    static void
1969do_mnemonic(Widget w, unsigned int keycode)
1970{
1971    WidgetList	    children;
1972    int		    numChildren, i;
1973    Boolean	    isMenu;
1974    KeySym	    mnemonic = '\0';
1975    char	    mneString[2];
1976    Widget	    userData;
1977    unsigned char   rowColType;
1978
1979    if (XtIsComposite(w))
1980    {
1981	if (XtClass(w) == xmRowColumnWidgetClass)
1982	{
1983	    XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
1984	    isMenu = (rowColType != (unsigned char)XmWORK_AREA);
1985	}
1986	else
1987	    isMenu = False;
1988	if (!isMenu)
1989	{
1990	    XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
1991			  &numChildren, NULL);
1992	    for (i = 0; i < numChildren; i++)
1993		do_mnemonic(children[i], keycode);
1994	}
1995    }
1996    else
1997    {
1998	XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
1999	if (mnemonic != '\0')
2000	{
2001	    mneString[0] = mnemonic;
2002	    mneString[1] = '\0';
2003	    if (XKeysymToKeycode(XtDisplay(XtParent(w)),
2004				       XStringToKeysym(mneString)) == keycode)
2005	    {
2006		if (XtClass(w) == xmLabelWidgetClass
2007			|| XtClass(w) == xmLabelGadgetClass)
2008		{
2009		    XtVaGetValues(w, XmNuserData, &userData, NULL);
2010		    if (userData != NULL && XtIsWidget(userData))
2011			XmProcessTraversal(userData, XmTRAVERSE_CURRENT);
2012		}
2013		else
2014		{
2015		    XKeyPressedEvent keyEvent;
2016
2017		    XmProcessTraversal(w, XmTRAVERSE_CURRENT);
2018
2019		    vim_memset((char *) &keyEvent, 0, sizeof(XKeyPressedEvent));
2020		    keyEvent.type = KeyPress;
2021		    keyEvent.serial = 1;
2022		    keyEvent.send_event = True;
2023		    keyEvent.display = XtDisplay(w);
2024		    keyEvent.window = XtWindow(w);
2025		    XtCallActionProc(w, "Activate", (XEvent *) & keyEvent,
2026								     NULL, 0);
2027		}
2028	    }
2029	}
2030    }
2031}
2032
2033/*
2034 * Callback routine for dialog mnemonic processing.
2035 */
2036    static void
2037mnemonic_event(Widget w, XtPointer call_data UNUSED, XKeyEvent *event)
2038{
2039    do_mnemonic(w, event->keycode);
2040}
2041
2042
2043/*
2044 * Search the widget tree under w for widgets with mnemonics.  When found, add
2045 * a passive grab to the dialog widget for the mnemonic character, thus
2046 * directing mnemonic events to the dialog widget.
2047 */
2048    static void
2049add_mnemonic_grabs(Widget dialog, Widget w)
2050{
2051    char	    mneString[2];
2052    WidgetList	    children;
2053    int		    numChildren, i;
2054    Boolean	    isMenu;
2055    KeySym	    mnemonic = '\0';
2056    unsigned char   rowColType;
2057
2058    if (XtIsComposite(w))
2059    {
2060	if (XtClass(w) == xmRowColumnWidgetClass)
2061	{
2062	    XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL);
2063	    isMenu = (rowColType != (unsigned char)XmWORK_AREA);
2064	}
2065	else
2066	    isMenu = False;
2067	if (!isMenu)
2068	{
2069	    XtVaGetValues(w, XmNchildren, &children, XmNnumChildren,
2070							  &numChildren, NULL);
2071	    for (i = 0; i < numChildren; i++)
2072		add_mnemonic_grabs(dialog, children[i]);
2073	}
2074    }
2075    else
2076    {
2077	XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL);
2078	if (mnemonic != '\0')
2079	{
2080	    mneString[0] = mnemonic;
2081	    mneString[1] = '\0';
2082	    XtGrabKey(dialog, XKeysymToKeycode(XtDisplay(dialog),
2083						  XStringToKeysym(mneString)),
2084		    Mod1Mask, True, GrabModeAsync, GrabModeAsync);
2085	}
2086    }
2087}
2088
2089/*
2090 * Add a handler for mnemonics in a dialog.  Motif itself only handles
2091 * mnemonics in menus. Mnemonics added or changed after this call will be
2092 * ignored.
2093 *
2094 * To add a mnemonic to a text field or list, set the XmNmnemonic resource on
2095 * the appropriate label and set the XmNuserData resource of the label to the
2096 * widget to get the focus when the mnemonic is typed.
2097 */
2098    static void
2099activate_dialog_mnemonics(Widget dialog)
2100{
2101    if (!dialog)
2102	return;
2103
2104    XtAddEventHandler(dialog, KeyPressMask, False,
2105			   (XtEventHandler) mnemonic_event, (XtPointer) NULL);
2106    add_mnemonic_grabs(dialog, dialog);
2107}
2108
2109/*
2110 * Removes the event handler and key-grabs for dialog mnemonic handling.
2111 */
2112    static void
2113suppress_dialog_mnemonics(Widget dialog)
2114{
2115    if (!dialog)
2116	return;
2117
2118    XtUngrabKey(dialog, AnyKey, Mod1Mask);
2119    XtRemoveEventHandler(dialog, KeyPressMask, False,
2120			   (XtEventHandler) mnemonic_event, (XtPointer) NULL);
2121}
2122
2123#if defined(FEAT_BROWSE) || defined(FEAT_GUI_DIALOG)
2124static void set_fontlist __ARGS((Widget wg));
2125
2126/*
2127 * Use the 'guifont' or 'guifontset' as a fontlist for a dialog widget.
2128 */
2129    static void
2130set_fontlist(id)
2131    Widget id;
2132{
2133    XmFontList fl;
2134
2135#ifdef FONTSET_ALWAYS
2136    if (gui.fontset != NOFONTSET)
2137    {
2138	fl = gui_motif_fontset2fontlist((XFontSet *)&gui.fontset);
2139	if (fl != NULL)
2140	{
2141	    if (XtIsManaged(id))
2142	    {
2143		XtUnmanageChild(id);
2144		XtVaSetValues(id, XmNfontList, fl, NULL);
2145		/* We should force the widget to recalculate it's
2146		 * geometry now. */
2147		XtManageChild(id);
2148	    }
2149	    else
2150		XtVaSetValues(id, XmNfontList, fl, NULL);
2151	    XmFontListFree(fl);
2152	}
2153    }
2154#else
2155    if (gui.norm_font != NOFONT)
2156    {
2157	fl = gui_motif_create_fontlist((XFontStruct *)gui.norm_font);
2158	if (fl != NULL)
2159	{
2160	    if (XtIsManaged(id))
2161	    {
2162		XtUnmanageChild(id);
2163		XtVaSetValues(id, XmNfontList, fl, NULL);
2164		/* We should force the widget to recalculate it's
2165		 * geometry now. */
2166		XtManageChild(id);
2167	    }
2168	    else
2169		XtVaSetValues(id, XmNfontList, fl, NULL);
2170	    XmFontListFree(fl);
2171	}
2172    }
2173#endif
2174}
2175#endif
2176
2177#if defined(FEAT_BROWSE) || defined(PROTO)
2178
2179/*
2180 * file selector related stuff
2181 */
2182
2183#include <Xm/FileSB.h>
2184#include <Xm/XmStrDefs.h>
2185
2186typedef struct dialog_callback_arg
2187{
2188    char *  args;   /* not used right now */
2189    int	    id;
2190} dcbarg_T;
2191
2192static Widget dialog_wgt;
2193static char *browse_fname = NULL;
2194static XmStringCharSet charset = (XmStringCharSet) XmSTRING_DEFAULT_CHARSET;
2195				/* used to set up XmStrings */
2196
2197static void DialogCancelCB __ARGS((Widget, XtPointer, XtPointer));
2198static void DialogAcceptCB __ARGS((Widget, XtPointer, XtPointer));
2199
2200/*
2201 * This function is used to translate the predefined label text of the
2202 * precomposed dialogs. We do this explicitly to allow:
2203 *
2204 * - usage of gettext for translation, as in all the other places.
2205 *
2206 * - equalize the messages between different GUI implementations as far as
2207 * possible.
2208 */
2209static void set_predefined_label __ARGS((Widget parent, String name, char *new_label));
2210
2211static void
2212set_predefined_label(parent, name, new_label)
2213    Widget  parent;
2214    String  name;
2215    char    *new_label;
2216{
2217    XmString	str;
2218    Widget	w;
2219    char_u	*p, *next;
2220    KeySym	mnemonic = NUL;
2221
2222    w = XtNameToWidget(parent, name);
2223
2224    if (!w)
2225	return;
2226
2227    p = vim_strsave((char_u *)new_label);
2228    if (p == NULL)
2229	return;
2230    for (next = p; *next; ++next)
2231    {
2232	if (*next == DLG_HOTKEY_CHAR)
2233	{
2234	    int len = STRLEN(next);
2235
2236	    if (len > 0)
2237	    {
2238		mch_memmove(next, next + 1, len);
2239		mnemonic = next[0];
2240	    }
2241	}
2242    }
2243
2244    str = XmStringCreate((char *)p, STRING_TAG);
2245    vim_free(p);
2246
2247    if (str != NULL)
2248    {
2249	XtVaSetValues(w,
2250		XmNlabelString, str,
2251		XmNmnemonic, mnemonic,
2252		NULL);
2253	XmStringFree(str);
2254    }
2255    gui_motif_menu_fontlist(w);
2256}
2257
2258static void
2259set_predefined_fontlist(parent, name)
2260    Widget parent;
2261    String name;
2262{
2263    Widget w;
2264    w = XtNameToWidget(parent, name);
2265
2266    if (!w)
2267	return;
2268
2269    set_fontlist(w);
2270}
2271
2272/*
2273 * Put up a file requester.
2274 * Returns the selected name in allocated memory, or NULL for Cancel.
2275 */
2276    char_u *
2277gui_mch_browse(saving, title, dflt, ext, initdir, filter)
2278    int		saving UNUSED;	/* select file to write */
2279    char_u	*title;		/* title for the window */
2280    char_u	*dflt;		/* default name */
2281    char_u	*ext UNUSED;	/* not used (extension added) */
2282    char_u	*initdir;	/* initial directory, NULL for current dir */
2283    char_u	*filter;	/* file name filter */
2284{
2285    char_u	dirbuf[MAXPATHL];
2286    char_u	dfltbuf[MAXPATHL];
2287    char_u	*pattern;
2288    char_u	*tofree = NULL;
2289
2290    /* There a difference between the resource name and value, Therefore, we
2291     * avoid to (ab-)use the (maybe internationalized!) dialog title as a
2292     * dialog name.
2293     */
2294
2295    dialog_wgt = XmCreateFileSelectionDialog(vimShell, "browseDialog", NULL, 0);
2296
2297    if (initdir == NULL || *initdir == NUL)
2298    {
2299	mch_dirname(dirbuf, MAXPATHL);
2300	initdir = dirbuf;
2301    }
2302
2303    if (dflt == NULL)
2304	dflt = (char_u *)"";
2305    else if (STRLEN(initdir) + STRLEN(dflt) + 2 < MAXPATHL)
2306    {
2307	/* The default selection should be the full path, "dflt" is only the
2308	 * file name. */
2309	STRCPY(dfltbuf, initdir);
2310	add_pathsep(dfltbuf);
2311	STRCAT(dfltbuf, dflt);
2312	dflt = dfltbuf;
2313    }
2314
2315    /* Can only use one pattern for a file name.  Get the first pattern out of
2316     * the filter.  An empty pattern means everything matches. */
2317    if (filter == NULL)
2318	pattern = (char_u *)"";
2319    else
2320    {
2321	char_u	*s, *p;
2322
2323	s = filter;
2324	for (p = filter; *p != NUL; ++p)
2325	{
2326	    if (*p == '\t')	/* end of description, start of pattern */
2327		s = p + 1;
2328	    if (*p == ';' || *p == '\n')	/* end of (first) pattern */
2329		break;
2330	}
2331	pattern = vim_strnsave(s, p - s);
2332	tofree = pattern;
2333	if (pattern == NULL)
2334	    pattern = (char_u *)"";
2335    }
2336
2337    XtVaSetValues(dialog_wgt,
2338	XtVaTypedArg,
2339	    XmNdirectory, XmRString, (char *)initdir, STRLEN(initdir) + 1,
2340	XtVaTypedArg,
2341	    XmNdirSpec,	XmRString, (char *)dflt, STRLEN(dflt) + 1,
2342	XtVaTypedArg,
2343	    XmNpattern,	XmRString, (char *)pattern, STRLEN(pattern) + 1,
2344	XtVaTypedArg,
2345	    XmNdialogTitle, XmRString, (char *)title, STRLEN(title) + 1,
2346	NULL);
2347
2348    set_predefined_label(dialog_wgt, "Apply", _("&Filter"));
2349    set_predefined_label(dialog_wgt, "Cancel", _("&Cancel"));
2350    set_predefined_label(dialog_wgt, "Dir", _("Directories"));
2351    set_predefined_label(dialog_wgt, "FilterLabel", _("Filter"));
2352    set_predefined_label(dialog_wgt, "Help", _("&Help"));
2353    set_predefined_label(dialog_wgt, "Items", _("Files"));
2354    set_predefined_label(dialog_wgt, "OK", _("&OK"));
2355    set_predefined_label(dialog_wgt, "Selection", _("Selection"));
2356
2357    /* This is to save us from silly external settings using not fixed with
2358     * fonts for file selection.
2359     */
2360    set_predefined_fontlist(dialog_wgt, "DirListSW.DirList");
2361    set_predefined_fontlist(dialog_wgt, "ItemsListSW.ItemsList");
2362
2363    gui_motif_menu_colors(dialog_wgt);
2364    if (gui.scroll_bg_pixel != INVALCOLOR)
2365	XtVaSetValues(dialog_wgt, XmNtroughColor, gui.scroll_bg_pixel, NULL);
2366
2367    XtAddCallback(dialog_wgt, XmNokCallback, DialogAcceptCB, (XtPointer)0);
2368    XtAddCallback(dialog_wgt, XmNcancelCallback, DialogCancelCB, (XtPointer)0);
2369    /* We have no help in this window, so hide help button */
2370    XtUnmanageChild(XmFileSelectionBoxGetChild(dialog_wgt,
2371					(unsigned char)XmDIALOG_HELP_BUTTON));
2372
2373    manage_centered(dialog_wgt);
2374    activate_dialog_mnemonics(dialog_wgt);
2375
2376    /* sit in a loop until the dialog box has gone away */
2377    do
2378    {
2379	XtAppProcessEvent(XtWidgetToApplicationContext(dialog_wgt),
2380	    (XtInputMask)XtIMAll);
2381    } while (XtIsManaged(dialog_wgt));
2382
2383    suppress_dialog_mnemonics(dialog_wgt);
2384    XtDestroyWidget(dialog_wgt);
2385    vim_free(tofree);
2386
2387    if (browse_fname == NULL)
2388	return NULL;
2389    return vim_strsave((char_u *)browse_fname);
2390}
2391
2392/*
2393 * The code below was originally taken from
2394 *	/usr/examples/motif/xmsamplers/xmeditor.c
2395 * on Digital Unix 4.0d, but heavily modified.
2396 */
2397
2398/*
2399 * Process callback from Dialog cancel actions.
2400 */
2401    static void
2402DialogCancelCB(w, client_data, call_data)
2403    Widget	w UNUSED;		/*  widget id		*/
2404    XtPointer	client_data UNUSED;	/*  data from application   */
2405    XtPointer	call_data UNUSED;	/*  data from widget class  */
2406{
2407    if (browse_fname != NULL)
2408    {
2409	XtFree(browse_fname);
2410	browse_fname = NULL;
2411    }
2412    XtUnmanageChild(dialog_wgt);
2413}
2414
2415/*
2416 * Process callback from Dialog actions.
2417 */
2418    static void
2419DialogAcceptCB(w, client_data, call_data)
2420    Widget	w UNUSED;		/*  widget id		*/
2421    XtPointer	client_data UNUSED;	/*  data from application   */
2422    XtPointer	call_data;		/*  data from widget class  */
2423{
2424    XmFileSelectionBoxCallbackStruct *fcb;
2425
2426    if (browse_fname != NULL)
2427    {
2428	XtFree(browse_fname);
2429	browse_fname = NULL;
2430    }
2431    fcb = (XmFileSelectionBoxCallbackStruct *)call_data;
2432
2433    /* get the filename from the file selection box */
2434    XmStringGetLtoR(fcb->value, charset, &browse_fname);
2435
2436    /* popdown the file selection box */
2437    XtUnmanageChild(dialog_wgt);
2438}
2439
2440#endif /* FEAT_BROWSE */
2441
2442#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2443
2444static int	dialogStatus;
2445
2446static void keyhit_callback __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont));
2447static void butproc __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
2448
2449/*
2450 * Callback function for the textfield.  When CR is hit this works like
2451 * hitting the "OK" button, ESC like "Cancel".
2452 */
2453    static void
2454keyhit_callback(w, client_data, event, cont)
2455    Widget		w;
2456    XtPointer		client_data UNUSED;
2457    XEvent		*event;
2458    Boolean		*cont UNUSED;
2459{
2460    char	buf[2];
2461    KeySym	key_sym;
2462
2463    if (XLookupString(&(event->xkey), buf, 2, &key_sym, NULL) == 1)
2464    {
2465	if (*buf == CAR)
2466	    dialogStatus = 1;
2467	else if (*buf == ESC)
2468	    dialogStatus = 2;
2469    }
2470    if ((key_sym == XK_Left || key_sym == XK_Right)
2471	    && !(event->xkey.state & ShiftMask))
2472	XmTextFieldClearSelection(w, XtLastTimestampProcessed(gui.dpy));
2473}
2474
2475    static void
2476butproc(w, client_data, call_data)
2477    Widget	w UNUSED;
2478    XtPointer	client_data;
2479    XtPointer	call_data UNUSED;
2480{
2481    dialogStatus = (int)(long)client_data + 1;
2482}
2483
2484#ifdef HAVE_XPM
2485
2486static Widget create_pixmap_label(Widget parent, String name, char **data, ArgList args, Cardinal arg);
2487
2488    static Widget
2489create_pixmap_label(parent, name, data, args, arg)
2490    Widget	parent;
2491    String	name;
2492    char	**data;
2493    ArgList	args;
2494    Cardinal	arg;
2495{
2496    Widget		label;
2497    Display		*dsp;
2498    Screen		*scr;
2499    int			depth;
2500    Pixmap		pixmap = 0;
2501    XpmAttributes	attr;
2502    Boolean		rs;
2503    XpmColorSymbol	color[5] =
2504    {
2505	{"none", NULL, 0},
2506	{"iconColor1", NULL, 0},
2507	{"bottomShadowColor", NULL, 0},
2508	{"topShadowColor", NULL, 0},
2509	{"selectColor", NULL, 0}
2510    };
2511
2512    label = XmCreateLabelGadget(parent, name, args, arg);
2513
2514    /*
2515     * We need to be careful here, since in case of gadgets, there is
2516     * no way to get the background color directly from the widget itself.
2517     * In such cases we get it from The Core part of his parent instead.
2518     */
2519    dsp = XtDisplayOfObject(label);
2520    scr = XtScreenOfObject(label);
2521    XtVaGetValues(XtIsSubclass(label, coreWidgetClass)
2522	    ?  label : XtParent(label),
2523		  XmNdepth, &depth,
2524		  XmNbackground, &color[0].pixel,
2525		  XmNforeground, &color[1].pixel,
2526		  XmNbottomShadowColor, &color[2].pixel,
2527		  XmNtopShadowColor, &color[3].pixel,
2528		  XmNhighlight, &color[4].pixel,
2529		  NULL);
2530
2531    attr.valuemask = XpmColorSymbols | XpmCloseness | XpmDepth;
2532    attr.colorsymbols = color;
2533    attr.numsymbols = 5;
2534    attr.closeness = 65535;
2535    attr.depth = depth;
2536    XpmCreatePixmapFromData(dsp, RootWindowOfScreen(scr),
2537		    data, &pixmap, NULL, &attr);
2538
2539    XtVaGetValues(label, XmNrecomputeSize, &rs, NULL);
2540    XtVaSetValues(label, XmNrecomputeSize, True, NULL);
2541    XtVaSetValues(label,
2542	    XmNlabelType, XmPIXMAP,
2543	    XmNlabelPixmap, pixmap,
2544	    NULL);
2545    XtVaSetValues(label, XmNrecomputeSize, rs, NULL);
2546
2547    return label;
2548}
2549#endif
2550
2551    int
2552gui_mch_dialog(type, title, message, button_names, dfltbutton, textfield)
2553    int		type UNUSED;
2554    char_u	*title;
2555    char_u	*message;
2556    char_u	*button_names;
2557    int		dfltbutton;
2558    char_u	*textfield;		/* buffer of size IOSIZE */
2559{
2560    char_u		*buts;
2561    char_u		*p, *next;
2562    XtAppContext	app;
2563    XmString		label;
2564    int			butcount;
2565    Widget		w;
2566    Widget		dialogform = NULL;
2567    Widget		form = NULL;
2568    Widget		dialogtextfield = NULL;
2569    Widget		*buttons;
2570    Widget		sep_form = NULL;
2571    Boolean		vertical;
2572    Widget		separator = NULL;
2573    int			n;
2574    Arg			args[6];
2575#ifdef HAVE_XPM
2576    char		**icon_data = NULL;
2577    Widget		dialogpixmap = NULL;
2578#endif
2579
2580    if (title == NULL)
2581	title = (char_u *)_("Vim dialog");
2582
2583    /* if our pointer is currently hidden, then we should show it. */
2584    gui_mch_mousehide(FALSE);
2585
2586    dialogform = XmCreateFormDialog(vimShell, (char *)"dialog", NULL, 0);
2587
2588    /* Check 'v' flag in 'guioptions': vertical button placement. */
2589    vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2590
2591    /* Set the title of the Dialog window */
2592    label = XmStringCreateSimple((char *)title);
2593    if (label == NULL)
2594	return -1;
2595    XtVaSetValues(dialogform,
2596	    XmNdialogTitle, label,
2597	    XmNhorizontalSpacing, 4,
2598	    XmNverticalSpacing, vertical ? 0 : 4,
2599	    NULL);
2600    XmStringFree(label);
2601
2602    /* make a copy, so that we can insert NULs */
2603    buts = vim_strsave(button_names);
2604    if (buts == NULL)
2605	return -1;
2606
2607    /* Count the number of buttons and allocate buttons[]. */
2608    butcount = 1;
2609    for (p = buts; *p; ++p)
2610	if (*p == DLG_BUTTON_SEP)
2611	    ++butcount;
2612    buttons = (Widget *)alloc((unsigned)(butcount * sizeof(Widget)));
2613    if (buttons == NULL)
2614    {
2615	vim_free(buts);
2616	return -1;
2617    }
2618
2619    /*
2620     * Create the buttons.
2621     */
2622    sep_form = (Widget) 0;
2623    p = buts;
2624    for (butcount = 0; *p; ++butcount)
2625    {
2626	KeySym mnemonic = NUL;
2627
2628	for (next = p; *next; ++next)
2629	{
2630	    if (*next == DLG_HOTKEY_CHAR)
2631	    {
2632		int len = STRLEN(next);
2633
2634		if (len > 0)
2635		{
2636		    mch_memmove(next, next + 1, len);
2637		    mnemonic = next[0];
2638		}
2639	    }
2640	    if (*next == DLG_BUTTON_SEP)
2641	    {
2642		*next++ = NUL;
2643		break;
2644	    }
2645	}
2646	label = XmStringCreate(_((char *)p), STRING_TAG);
2647	if (label == NULL)
2648	    break;
2649
2650	buttons[butcount] = XtVaCreateManagedWidget("button",
2651		xmPushButtonWidgetClass, dialogform,
2652		XmNlabelString, label,
2653		XmNmnemonic, mnemonic,
2654		XmNbottomAttachment, XmATTACH_FORM,
2655		XmNbottomOffset, 4,
2656		XmNshowAsDefault, butcount == dfltbutton - 1,
2657		XmNdefaultButtonShadowThickness, 1,
2658		NULL);
2659	XmStringFree(label);
2660	gui_motif_menu_fontlist(buttons[butcount]);
2661
2662	/* Layout properly. */
2663
2664	if (butcount > 0)
2665	{
2666	    if (vertical)
2667		XtVaSetValues(buttons[butcount],
2668			XmNtopWidget, buttons[butcount - 1],
2669			NULL);
2670	    else
2671	    {
2672		if (*next == NUL)
2673		{
2674		    XtVaSetValues(buttons[butcount],
2675			    XmNrightAttachment, XmATTACH_FORM,
2676			    XmNrightOffset, 4,
2677			    NULL);
2678
2679		    /* fill in a form as invisible separator */
2680		    sep_form = XtVaCreateWidget("separatorForm",
2681			    xmFormWidgetClass,	dialogform,
2682			    XmNleftAttachment, XmATTACH_WIDGET,
2683			    XmNleftWidget, buttons[butcount - 1],
2684			    XmNrightAttachment, XmATTACH_WIDGET,
2685			    XmNrightWidget, buttons[butcount],
2686			    XmNbottomAttachment, XmATTACH_FORM,
2687			    XmNbottomOffset, 4,
2688			    NULL);
2689		    XtManageChild(sep_form);
2690		}
2691		else
2692		{
2693		    XtVaSetValues(buttons[butcount],
2694			    XmNleftAttachment, XmATTACH_WIDGET,
2695			    XmNleftWidget, buttons[butcount - 1],
2696			    NULL);
2697		}
2698	    }
2699	}
2700	else if (!vertical)
2701	{
2702	    if (*next == NUL)
2703	    {
2704		XtVaSetValues(buttons[0],
2705			XmNrightAttachment, XmATTACH_FORM,
2706			XmNrightOffset, 4,
2707			NULL);
2708
2709		/* fill in a form as invisible separator */
2710		sep_form = XtVaCreateWidget("separatorForm",
2711			xmFormWidgetClass, dialogform,
2712			XmNleftAttachment, XmATTACH_FORM,
2713			XmNleftOffset, 4,
2714			XmNrightAttachment, XmATTACH_WIDGET,
2715			XmNrightWidget, buttons[0],
2716			XmNbottomAttachment, XmATTACH_FORM,
2717			XmNbottomOffset, 4,
2718			NULL);
2719		XtManageChild(sep_form);
2720	    }
2721	    else
2722		XtVaSetValues(buttons[0],
2723			XmNleftAttachment, XmATTACH_FORM,
2724			XmNleftOffset, 4,
2725			NULL);
2726	}
2727
2728	XtAddCallback(buttons[butcount], XmNactivateCallback,
2729			  (XtCallbackProc)butproc, (XtPointer)(long)butcount);
2730	p = next;
2731    }
2732    vim_free(buts);
2733
2734    separator = (Widget) 0;
2735    if (butcount > 0)
2736    {
2737	/* Create the separator for beauty. */
2738	n = 0;
2739	XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
2740	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
2741	XtSetArg(args[n], XmNbottomWidget, buttons[0]); n++;
2742	XtSetArg(args[n], XmNbottomOffset, 4); n++;
2743	XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
2744	XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
2745	separator = XmCreateSeparatorGadget(dialogform, "separator", args, n);
2746	XtManageChild(separator);
2747    }
2748
2749    if (textfield != NULL)
2750    {
2751	dialogtextfield = XtVaCreateWidget("textField",
2752		xmTextFieldWidgetClass, dialogform,
2753		XmNleftAttachment, XmATTACH_FORM,
2754		XmNrightAttachment, XmATTACH_FORM,
2755		NULL);
2756	if (butcount > 0)
2757	    XtVaSetValues(dialogtextfield,
2758		    XmNbottomAttachment, XmATTACH_WIDGET,
2759		    XmNbottomWidget, separator,
2760		    NULL);
2761	else
2762	    XtVaSetValues(dialogtextfield,
2763		    XmNbottomAttachment, XmATTACH_FORM,
2764		    NULL);
2765
2766	set_fontlist(dialogtextfield);
2767	XmTextFieldSetString(dialogtextfield, (char *)textfield);
2768	XtManageChild(dialogtextfield);
2769	XtAddEventHandler(dialogtextfield, KeyPressMask, False,
2770			    (XtEventHandler)keyhit_callback, (XtPointer)NULL);
2771    }
2772
2773    /* Form holding both message and pixmap labels */
2774    form = XtVaCreateWidget("separatorForm",
2775	    xmFormWidgetClass, dialogform,
2776	    XmNleftAttachment, XmATTACH_FORM,
2777	    XmNrightAttachment, XmATTACH_FORM,
2778	    XmNtopAttachment, XmATTACH_FORM,
2779	    NULL);
2780    XtManageChild(form);
2781
2782#ifdef HAVE_XPM
2783    /* Add a pixmap, left of the message. */
2784    switch (type)
2785    {
2786	case VIM_GENERIC:
2787	    icon_data = generic_xpm;
2788	    break;
2789	case VIM_ERROR:
2790	    icon_data = error_xpm;
2791	    break;
2792	case VIM_WARNING:
2793	    icon_data = alert_xpm;
2794	    break;
2795	case VIM_INFO:
2796	    icon_data = info_xpm;
2797	    break;
2798	case VIM_QUESTION:
2799	    icon_data = quest_xpm;
2800	    break;
2801	default:
2802	    icon_data = generic_xpm;
2803    }
2804
2805    n = 0;
2806    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
2807    XtSetArg(args[n], XmNtopOffset, 8); n++;
2808    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
2809    XtSetArg(args[n], XmNbottomOffset, 8); n++;
2810    XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
2811    XtSetArg(args[n], XmNleftOffset, 8); n++;
2812
2813    dialogpixmap = create_pixmap_label(form, "dialogPixmap",
2814	    icon_data, args, n);
2815    XtManageChild(dialogpixmap);
2816#endif
2817
2818    /* Create the dialog message.
2819     * Since LessTif is apparently having problems with the creation of
2820     * properly localized string, we use LtoR here. The symptom is that the
2821     * string sill not show properly in multiple lines as it does in native
2822     * Motif.
2823     */
2824    label = XmStringCreateLtoR((char *)message, STRING_TAG);
2825    if (label == NULL)
2826	return -1;
2827    w = XtVaCreateManagedWidget("dialogMessage",
2828				xmLabelGadgetClass, form,
2829				XmNlabelString, label,
2830				XmNalignment, XmALIGNMENT_BEGINNING,
2831				XmNtopAttachment, XmATTACH_FORM,
2832				XmNtopOffset, 8,
2833#ifdef HAVE_XPM
2834				XmNleftAttachment, XmATTACH_WIDGET,
2835				XmNleftWidget, dialogpixmap,
2836#else
2837				XmNleftAttachment, XmATTACH_FORM,
2838#endif
2839				XmNleftOffset, 8,
2840				XmNrightAttachment, XmATTACH_FORM,
2841				XmNrightOffset, 8,
2842				XmNbottomAttachment, XmATTACH_FORM,
2843				XmNbottomOffset, 8,
2844				NULL);
2845    XmStringFree(label);
2846    set_fontlist(w);
2847
2848    if (textfield != NULL)
2849    {
2850	XtVaSetValues(form,
2851		XmNbottomAttachment, XmATTACH_WIDGET,
2852		XmNbottomWidget, dialogtextfield,
2853		NULL);
2854    }
2855    else
2856    {
2857	if (butcount > 0)
2858	    XtVaSetValues(form,
2859		    XmNbottomAttachment, XmATTACH_WIDGET,
2860		    XmNbottomWidget, separator,
2861		    NULL);
2862	else
2863	    XtVaSetValues(form,
2864		    XmNbottomAttachment, XmATTACH_FORM,
2865		    NULL);
2866    }
2867
2868    if (dfltbutton < 1)
2869	dfltbutton = 1;
2870    if (dfltbutton > butcount)
2871	dfltbutton = butcount;
2872    XtVaSetValues(dialogform,
2873	    XmNdefaultButton, buttons[dfltbutton - 1], NULL);
2874    if (textfield != NULL)
2875	XtVaSetValues(dialogform, XmNinitialFocus, dialogtextfield, NULL);
2876    else
2877	XtVaSetValues(dialogform, XmNinitialFocus, buttons[dfltbutton - 1],
2878									NULL);
2879
2880    manage_centered(dialogform);
2881    activate_dialog_mnemonics(dialogform);
2882
2883    if (textfield != NULL && *textfield != NUL)
2884    {
2885	/* This only works after the textfield has been realised. */
2886	XmTextFieldSetSelection(dialogtextfield,
2887			 (XmTextPosition)0, (XmTextPosition)STRLEN(textfield),
2888					   XtLastTimestampProcessed(gui.dpy));
2889	XmTextFieldSetCursorPosition(dialogtextfield,
2890					   (XmTextPosition)STRLEN(textfield));
2891    }
2892
2893    app = XtWidgetToApplicationContext(dialogform);
2894
2895    /* Loop until a button is pressed or the dialog is killed somehow. */
2896    dialogStatus = -1;
2897    for (;;)
2898    {
2899	XtAppProcessEvent(app, (XtInputMask)XtIMAll);
2900	if (dialogStatus >= 0 || !XtIsManaged(dialogform))
2901	    break;
2902    }
2903
2904    vim_free(buttons);
2905
2906    if (textfield != NULL)
2907    {
2908	p = (char_u *)XmTextGetString(dialogtextfield);
2909	if (p == NULL || dialogStatus < 0)
2910	    *textfield = NUL;
2911	else
2912	    vim_strncpy(textfield, p, IOSIZE - 1);
2913	XtFree((char *)p);
2914    }
2915
2916    suppress_dialog_mnemonics(dialogform);
2917    XtDestroyWidget(dialogform);
2918
2919    return dialogStatus;
2920}
2921#endif /* FEAT_GUI_DIALOG */
2922
2923#if defined(FEAT_FOOTER) || defined(PROTO)
2924
2925    static int
2926gui_mch_compute_footer_height()
2927{
2928    Dimension	height;		    /* total Toolbar height */
2929    Dimension	top;		    /* XmNmarginTop */
2930    Dimension	bottom;		    /* XmNmarginBottom */
2931    Dimension	shadow;		    /* XmNshadowThickness */
2932
2933    XtVaGetValues(footer,
2934	    XmNheight, &height,
2935	    XmNmarginTop, &top,
2936	    XmNmarginBottom, &bottom,
2937	    XmNshadowThickness, &shadow,
2938	    NULL);
2939
2940    return (int) height + top + bottom + (shadow << 1);
2941}
2942
2943    void
2944gui_mch_enable_footer(showit)
2945    int		showit;
2946{
2947    if (showit)
2948    {
2949	gui.footer_height = gui_mch_compute_footer_height();
2950	XtManageChild(footer);
2951    }
2952    else
2953    {
2954	gui.footer_height = 0;
2955	XtUnmanageChild(footer);
2956    }
2957    XtVaSetValues(textAreaForm, XmNbottomOffset, gui.footer_height, NULL);
2958}
2959
2960    void
2961gui_mch_set_footer(s)
2962    char_u	*s;
2963{
2964    XmString	xms;
2965
2966    xms = XmStringCreate((char *)s, STRING_TAG);
2967    if (xms != NULL)
2968    {
2969	XtVaSetValues(footer, XmNlabelString, xms, NULL);
2970	XmStringFree(xms);
2971    }
2972}
2973
2974#endif
2975
2976
2977#if defined(FEAT_TOOLBAR) || defined(PROTO)
2978    void
2979gui_mch_show_toolbar(int showit)
2980{
2981    Cardinal	numChildren;	    /* how many children toolBar has */
2982
2983    if (toolBar == (Widget)0)
2984	return;
2985    XtVaGetValues(toolBar, XmNnumChildren, &numChildren, NULL);
2986    if (showit && numChildren > 0)
2987    {
2988	/* Assume that we want to show the toolbar if p_toolbar contains
2989	 * valid option settings, therefore p_toolbar must not be NULL.
2990	 */
2991	WidgetList  children;
2992
2993	XtVaGetValues(toolBar, XmNchildren, &children, NULL);
2994	{
2995	    void    (*action)(BalloonEval *);
2996	    int	    text = 0;
2997
2998	    if (strstr((const char *)p_toolbar, "tooltips"))
2999		action = &gui_mch_enable_beval_area;
3000	    else
3001		action = &gui_mch_disable_beval_area;
3002	    if (strstr((const char *)p_toolbar, "text"))
3003		text = 1;
3004	    else if (strstr((const char *)p_toolbar, "icons"))
3005		text = -1;
3006	    if (text != 0)
3007	    {
3008		vimmenu_T   *toolbar;
3009		vimmenu_T   *cur;
3010
3011		for (toolbar = root_menu; toolbar; toolbar = toolbar->next)
3012		    if (menu_is_toolbar(toolbar->dname))
3013			break;
3014		/* Assumption: toolbar is NULL if there is no toolbar,
3015		 *	       otherwise it contains the toolbar menu structure.
3016		 *
3017		 * Assumption: "numChildren" == the number of items in the list
3018		 *	       of items beginning with toolbar->children.
3019		 */
3020		if (toolbar)
3021		{
3022		    for (cur = toolbar->children; cur; cur = cur->next)
3023		    {
3024			Arg	    args[1];
3025			int	    n = 0;
3026
3027			/* Enable/Disable tooltip (OK to enable while
3028			 * currently enabled). */
3029			if (cur->tip != NULL)
3030			    (*action)(cur->tip);
3031			if (!menu_is_separator(cur->name))
3032			{
3033			    if (text == 1 || cur->xpm == NULL)
3034			    {
3035				XtSetArg(args[n], XmNlabelType, XmSTRING);
3036				++n;
3037			    }
3038			    if (cur->id != NULL)
3039			    {
3040				XtUnmanageChild(cur->id);
3041				XtSetValues(cur->id, args, n);
3042				XtManageChild(cur->id);
3043			    }
3044			}
3045		    }
3046		}
3047	    }
3048	}
3049	gui.toolbar_height = gui_mch_compute_toolbar_height();
3050	XtManageChild(XtParent(toolBar));
3051#ifdef FEAT_GUI_TABLINE
3052	if (showing_tabline)
3053	{
3054	    XtVaSetValues(tabLine,
3055			  XmNtopAttachment, XmATTACH_WIDGET,
3056			  XmNtopWidget, XtParent(toolBar),
3057			  NULL);
3058	    XtVaSetValues(textAreaForm,
3059			  XmNtopAttachment, XmATTACH_WIDGET,
3060			  XmNtopWidget, tabLine,
3061			  NULL);
3062	}
3063	else
3064#endif
3065	    XtVaSetValues(textAreaForm,
3066			  XmNtopAttachment, XmATTACH_WIDGET,
3067			  XmNtopWidget, XtParent(toolBar),
3068			  NULL);
3069	if (XtIsManaged(menuBar))
3070	    XtVaSetValues(XtParent(toolBar),
3071		    XmNtopAttachment, XmATTACH_WIDGET,
3072		    XmNtopWidget, menuBar,
3073		    NULL);
3074	else
3075	    XtVaSetValues(XtParent(toolBar),
3076		    XmNtopAttachment, XmATTACH_FORM,
3077		    NULL);
3078    }
3079    else
3080    {
3081	gui.toolbar_height = 0;
3082	if (XtIsManaged(menuBar))
3083	{
3084#ifdef FEAT_GUI_TABLINE
3085	    if (showing_tabline)
3086	    {
3087		XtVaSetValues(tabLine,
3088			      XmNtopAttachment, XmATTACH_WIDGET,
3089			      XmNtopWidget, menuBar,
3090			      NULL);
3091		XtVaSetValues(textAreaForm,
3092			      XmNtopAttachment, XmATTACH_WIDGET,
3093			      XmNtopWidget, tabLine,
3094			      NULL);
3095	    }
3096	    else
3097#endif
3098		XtVaSetValues(textAreaForm,
3099			      XmNtopAttachment, XmATTACH_WIDGET,
3100			      XmNtopWidget, menuBar,
3101			      NULL);
3102	}
3103	else
3104	{
3105#ifdef FEAT_GUI_TABLINE
3106	    if (showing_tabline)
3107	    {
3108		XtVaSetValues(tabLine,
3109			      XmNtopAttachment, XmATTACH_FORM,
3110			      NULL);
3111		XtVaSetValues(textAreaForm,
3112			      XmNtopAttachment, XmATTACH_WIDGET,
3113			      XmNtopWidget, tabLine,
3114			      NULL);
3115	    }
3116	    else
3117#endif
3118		XtVaSetValues(textAreaForm,
3119			      XmNtopAttachment, XmATTACH_FORM,
3120			      NULL);
3121	}
3122
3123	XtUnmanageChild(XtParent(toolBar));
3124    }
3125    gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
3126}
3127
3128/*
3129 * A toolbar button has been pushed; now reset the input focus
3130 * such that the user can type page up/down etc. and have the
3131 * input go to the editor window, not the button
3132 */
3133    static void
3134reset_focus()
3135{
3136    if (textArea != NULL)
3137	XmProcessTraversal(textArea, XmTRAVERSE_CURRENT);
3138}
3139
3140    int
3141gui_mch_compute_toolbar_height()
3142{
3143    Dimension	borders;
3144    Dimension	height;		    /* total Toolbar height */
3145    Dimension	whgt;		    /* height of each widget */
3146    WidgetList	children;	    /* list of toolBar's children */
3147    Cardinal	numChildren;	    /* how many children toolBar has */
3148    int		i;
3149
3150    borders = 0;
3151    height = 0;
3152    if (toolBar != (Widget)0 && toolBarFrame != (Widget)0)
3153    {				    /* get height of XmFrame parent */
3154	Dimension	fst;
3155	Dimension	fmh;
3156	Dimension	tst;
3157	Dimension	tmh;
3158
3159	XtVaGetValues(toolBarFrame,
3160		XmNshadowThickness, &fst,
3161		XmNmarginHeight, &fmh,
3162		NULL);
3163	borders += fst + fmh;
3164	XtVaGetValues(toolBar,
3165		XmNshadowThickness, &tst,
3166		XmNmarginHeight, &tmh,
3167		XmNchildren, &children,
3168		XmNnumChildren, &numChildren, NULL);
3169	borders += tst + tmh;
3170	for (i = 0; i < (int)numChildren; i++)
3171	{
3172	    whgt = 0;
3173	    XtVaGetValues(children[i], XmNheight, &whgt, NULL);
3174	    if (height < whgt)
3175		height = whgt;
3176	}
3177    }
3178#ifdef LESSTIF_VERSION
3179    /* Hack: When starting up we get wrong dimensions. */
3180    if (height < 10)
3181	height = 24;
3182#endif
3183
3184    return (int)(height + (borders << 1));
3185}
3186
3187    void
3188motif_get_toolbar_colors(bgp, fgp, bsp, tsp, hsp)
3189    Pixel       *bgp;
3190    Pixel       *fgp;
3191    Pixel       *bsp;
3192    Pixel       *tsp;
3193    Pixel       *hsp;
3194{
3195    XtVaGetValues(toolBar,
3196	    XmNbackground, bgp,
3197	    XmNforeground, fgp,
3198	    XmNbottomShadowColor, bsp,
3199	    XmNtopShadowColor, tsp,
3200	    XmNhighlightColor, hsp,
3201	    NULL);
3202}
3203
3204# ifdef FEAT_FOOTER
3205/*
3206 * The next toolbar enter/leave callbacks should really do balloon help.  But
3207 * I have to use footer help for backwards compatability.  Hopefully both will
3208 * get implemented and the user will have a choice.
3209 */
3210    static void
3211toolbarbutton_enter_cb(w, client_data, event, cont)
3212    Widget	w UNUSED;
3213    XtPointer	client_data;
3214    XEvent	*event UNUSED;
3215    Boolean	*cont UNUSED;
3216{
3217    vimmenu_T	*menu = (vimmenu_T *) client_data;
3218
3219    if (menu->strings[MENU_INDEX_TIP] != NULL)
3220    {
3221	if (vim_strchr(p_go, GO_FOOTER) != NULL)
3222	    gui_mch_set_footer(menu->strings[MENU_INDEX_TIP]);
3223    }
3224}
3225
3226    static void
3227toolbarbutton_leave_cb(w, client_data, event, cont)
3228    Widget	w UNUSED;
3229    XtPointer	client_data UNUSED;
3230    XEvent	*event UNUSED;
3231    Boolean	*cont UNUSED;
3232{
3233    gui_mch_set_footer((char_u *) "");
3234}
3235# endif
3236#endif
3237
3238#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
3239/*
3240 * Show or hide the tabline.
3241 */
3242    void
3243gui_mch_show_tabline(int showit)
3244{
3245    if (tabLine == (Widget)0)
3246	return;
3247
3248    if (!showit != !showing_tabline)
3249    {
3250	if (showit)
3251	{
3252	    XtManageChild(tabLine);
3253	    XtUnmanageChild(XtNameToWidget(tabLine, "PageScroller"));
3254	    XtUnmanageChild(XtNameToWidget(tabLine, "MinorTabScrollerNext"));
3255	    XtUnmanageChild(XtNameToWidget(tabLine,
3256					   "MinorTabScrollerPrevious"));
3257#ifdef FEAT_MENU
3258# ifdef FEAT_TOOLBAR
3259	    if (XtIsManaged(XtParent(toolBar)))
3260		XtVaSetValues(tabLine,
3261			      XmNtopAttachment, XmATTACH_WIDGET,
3262			      XmNtopWidget, XtParent(toolBar), NULL);
3263	    else
3264# endif
3265		if (XtIsManaged(menuBar))
3266		XtVaSetValues(tabLine,
3267			      XmNtopAttachment, XmATTACH_WIDGET,
3268			      XmNtopWidget, menuBar, NULL);
3269	    else
3270#endif
3271		XtVaSetValues(tabLine,
3272			      XmNtopAttachment, XmATTACH_FORM, NULL);
3273	    XtVaSetValues(textAreaForm,
3274			  XmNtopAttachment, XmATTACH_WIDGET,
3275			  XmNtopWidget, tabLine,
3276			  NULL);
3277	}
3278	else
3279	{
3280	    XtUnmanageChild(tabLine);
3281#ifdef FEAT_MENU
3282# ifdef FEAT_TOOLBAR
3283	    if (XtIsManaged(XtParent(toolBar)))
3284		XtVaSetValues(textAreaForm,
3285			      XmNtopAttachment, XmATTACH_WIDGET,
3286			      XmNtopWidget, XtParent(toolBar), NULL);
3287	    else
3288# endif
3289		if (XtIsManaged(menuBar))
3290		XtVaSetValues(textAreaForm,
3291			      XmNtopAttachment, XmATTACH_WIDGET,
3292			      XmNtopWidget, menuBar, NULL);
3293	    else
3294#endif
3295		XtVaSetValues(textAreaForm,
3296			      XmNtopAttachment, XmATTACH_FORM, NULL);
3297	}
3298	showing_tabline = showit;
3299    }
3300}
3301
3302/*
3303 * Return TRUE when tabline is displayed.
3304 */
3305    int
3306gui_mch_showing_tabline(void)
3307{
3308    return tabLine != (Widget)0 && showing_tabline;
3309}
3310
3311/*
3312 * Update the labels of the tabline.
3313 */
3314    void
3315gui_mch_update_tabline(void)
3316{
3317    tabpage_T		*tp;
3318    int			nr = 1, n;
3319    Arg			args[10];
3320    int			curtabidx = 0, currentpage;
3321    Widget		tab;
3322    XmNotebookPageInfo	page_info;
3323    XmNotebookPageStatus page_status;
3324    int			last_page, tab_count;
3325    XmString		label_str;
3326    char		*label_cstr;
3327    BalloonEval		*beval;
3328
3329    if (tabLine == (Widget)0)
3330	return;
3331
3332    /* Add a label for each tab page.  They all contain the same text area. */
3333    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
3334    {
3335	if (tp == curtab)
3336	    curtabidx = nr;
3337
3338	page_status = XmNotebookGetPageInfo(tabLine, nr, &page_info);
3339	if (page_status == XmPAGE_INVALID
3340		|| page_info.major_tab_widget == (Widget)0)
3341	{
3342	    /* Add the tab */
3343	    n = 0;
3344	    XtSetArg(args[n], XmNnotebookChildType, XmMAJOR_TAB); n++;
3345	    XtSetArg(args[n], XmNtraversalOn, False); n++;
3346	    XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
3347	    XtSetArg(args[n], XmNhighlightThickness, 1); n++;
3348	    XtSetArg(args[n], XmNshadowThickness , 1); n++;
3349	    tab = XmCreatePushButton(tabLine, "-Empty-", args, n);
3350	    XtManageChild(tab);
3351	    beval = gui_mch_create_beval_area(tab, NULL, tabline_balloon_cb,
3352									NULL);
3353	    XtVaSetValues(tab, XmNuserData, beval, NULL);
3354	}
3355	else
3356	    tab = page_info.major_tab_widget;
3357
3358	XtVaSetValues(tab, XmNpageNumber, nr, NULL);
3359
3360	/*
3361	 * Change the label text only if it is different
3362	 */
3363	XtVaGetValues(tab, XmNlabelString, &label_str, NULL);
3364	if (XmStringGetLtoR(label_str, XmSTRING_DEFAULT_CHARSET, &label_cstr))
3365	{
3366	    get_tabline_label(tp, FALSE);
3367	    if (STRCMP(label_cstr, NameBuff) != 0)
3368	    {
3369		XtVaSetValues(tab, XtVaTypedArg, XmNlabelString, XmRString,
3370			      NameBuff, STRLEN(NameBuff) + 1, NULL);
3371		/*
3372		 * Force a resize of the tab label button
3373		 */
3374		XtUnmanageChild(tab);
3375		XtManageChild(tab);
3376	    }
3377	    XtFree(label_cstr);
3378	}
3379    }
3380
3381    tab_count = nr - 1;
3382
3383    XtVaGetValues(tabLine, XmNlastPageNumber, &last_page, NULL);
3384
3385    /* Remove any old labels. */
3386    while (nr <= last_page)
3387    {
3388	if (XmNotebookGetPageInfo(tabLine, nr, &page_info) != XmPAGE_INVALID
3389	    && page_info.page_number == nr
3390	    && page_info.major_tab_widget != (Widget)0)
3391	{
3392	    XtVaGetValues(page_info.major_tab_widget, XmNuserData, &beval, NULL);
3393	    if (beval != NULL)
3394		gui_mch_destroy_beval_area(beval);
3395	    XtUnmanageChild(page_info.major_tab_widget);
3396	    XtDestroyWidget(page_info.major_tab_widget);
3397	}
3398	nr++;
3399    }
3400
3401    XtVaSetValues(tabLine, XmNlastPageNumber, tab_count, NULL);
3402
3403    XtVaGetValues(tabLine, XmNcurrentPageNumber, &currentpage, NULL);
3404    if (currentpage != curtabidx)
3405	XtVaSetValues(tabLine, XmNcurrentPageNumber, curtabidx, NULL);
3406}
3407
3408/*
3409 * Set the current tab to "nr".  First tab is 1.
3410 */
3411    void
3412gui_mch_set_curtab(nr)
3413    int		nr;
3414{
3415    int		currentpage;
3416
3417    if (tabLine == (Widget)0)
3418	return;
3419
3420    XtVaGetValues(tabLine, XmNcurrentPageNumber, &currentpage, NULL);
3421    if (currentpage != nr)
3422	XtVaSetValues(tabLine, XmNcurrentPageNumber, nr, NULL);
3423}
3424#endif
3425
3426/*
3427 * Set the colors of Widget "id" to the menu colors.
3428 */
3429    static void
3430gui_motif_menu_colors(id)
3431    Widget  id;
3432{
3433    if (gui.menu_bg_pixel != INVALCOLOR)
3434#if (XmVersion >= 1002)
3435	XmChangeColor(id, gui.menu_bg_pixel);
3436#else
3437	XtVaSetValues(id, XmNbackground, gui.menu_bg_pixel, NULL);
3438#endif
3439    if (gui.menu_fg_pixel != INVALCOLOR)
3440	XtVaSetValues(id, XmNforeground, gui.menu_fg_pixel, NULL);
3441}
3442
3443/*
3444 * Set the colors of Widget "id" to the scrollbar colors.
3445 */
3446    static void
3447gui_motif_scroll_colors(id)
3448    Widget  id;
3449{
3450    if (gui.scroll_bg_pixel != INVALCOLOR)
3451#if (XmVersion >= 1002)
3452	XmChangeColor(id, gui.scroll_bg_pixel);
3453#else
3454	XtVaSetValues(id, XmNbackground, gui.scroll_bg_pixel, NULL);
3455#endif
3456    if (gui.scroll_fg_pixel != INVALCOLOR)
3457	XtVaSetValues(id, XmNforeground, gui.scroll_fg_pixel, NULL);
3458}
3459
3460/*
3461 * Set the fontlist for Widget "id" to use gui.menu_fontset or gui.menu_font.
3462 */
3463    void
3464gui_motif_menu_fontlist(id)
3465    Widget  id UNUSED;
3466{
3467#ifdef FEAT_MENU
3468#ifdef FONTSET_ALWAYS
3469    if (gui.menu_fontset != NOFONTSET)
3470    {
3471	XmFontList fl;
3472
3473	fl = gui_motif_fontset2fontlist((XFontSet *)&gui.menu_fontset);
3474	if (fl != NULL)
3475	{
3476	    if (XtIsManaged(id))
3477	    {
3478		XtUnmanageChild(id);
3479		XtVaSetValues(id, XmNfontList, fl, NULL);
3480		/* We should force the widget to recalculate it's
3481		 * geometry now. */
3482		XtManageChild(id);
3483	    }
3484	    else
3485		XtVaSetValues(id, XmNfontList, fl, NULL);
3486	    XmFontListFree(fl);
3487	}
3488    }
3489#else
3490    if (gui.menu_font != NOFONT)
3491    {
3492	XmFontList fl;
3493
3494	fl = gui_motif_create_fontlist((XFontStruct *)gui.menu_font);
3495	if (fl != NULL)
3496	{
3497	    if (XtIsManaged(id))
3498	    {
3499		XtUnmanageChild(id);
3500		XtVaSetValues(id, XmNfontList, fl, NULL);
3501		/* We should force the widget to recalculate it's
3502		 * geometry now. */
3503		XtManageChild(id);
3504	    }
3505	    else
3506		XtVaSetValues(id, XmNfontList, fl, NULL);
3507	    XmFontListFree(fl);
3508	}
3509    }
3510#endif
3511#endif
3512}
3513
3514
3515/*
3516 * We don't create it twice for the sake of speed.
3517 */
3518
3519typedef struct _SharedFindReplace
3520{
3521    Widget dialog;	/* the main dialog widget */
3522    Widget wword;	/* 'Exact match' check button */
3523    Widget mcase;	/* 'match case' check button */
3524    Widget up;		/* search direction 'Up' radio button */
3525    Widget down;	/* search direction 'Down' radio button */
3526    Widget what;	/* 'Find what' entry text widget */
3527    Widget with;	/* 'Replace with' entry text widget */
3528    Widget find;	/* 'Find Next' action button */
3529    Widget replace;	/* 'Replace With' action button */
3530    Widget all;		/* 'Replace All' action button */
3531    Widget undo;	/* 'Undo' action button */
3532
3533    Widget cancel;
3534} SharedFindReplace;
3535
3536static SharedFindReplace find_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
3537static SharedFindReplace repl_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
3538
3539static void find_replace_destroy_callback __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
3540static void find_replace_dismiss_callback __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
3541static void entry_activate_callback __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
3542static void find_replace_callback __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
3543static void find_replace_keypress __ARGS((Widget w, SharedFindReplace * frdp, XKeyEvent * event));
3544static void find_replace_dialog_create __ARGS((char_u *entry_text, int do_replace));
3545
3546    static void
3547find_replace_destroy_callback(w, client_data, call_data)
3548    Widget	w UNUSED;
3549    XtPointer	client_data;
3550    XtPointer	call_data UNUSED;
3551{
3552    SharedFindReplace *cd = (SharedFindReplace *)client_data;
3553
3554    if (cd != NULL)
3555       /* suppress_dialog_mnemonics(cd->dialog); */
3556	cd->dialog = (Widget)0;
3557}
3558
3559    static void
3560find_replace_dismiss_callback(w, client_data, call_data)
3561    Widget	w UNUSED;
3562    XtPointer	client_data;
3563    XtPointer	call_data UNUSED;
3564{
3565    SharedFindReplace *cd = (SharedFindReplace *)client_data;
3566
3567    if (cd != NULL)
3568	XtUnmanageChild(cd->dialog);
3569}
3570
3571    static void
3572entry_activate_callback(w, client_data, call_data)
3573    Widget	w UNUSED;
3574    XtPointer	client_data;
3575    XtPointer	call_data UNUSED;
3576{
3577    XmProcessTraversal((Widget)client_data, XmTRAVERSE_CURRENT);
3578}
3579
3580    static void
3581find_replace_callback(w, client_data, call_data)
3582    Widget	w UNUSED;
3583    XtPointer	client_data;
3584    XtPointer	call_data UNUSED;
3585{
3586    long_u	flags = (long_u)client_data;
3587    char	*find_text, *repl_text;
3588    Boolean	direction_down = TRUE;
3589    Boolean	wword;
3590    Boolean	mcase;
3591    SharedFindReplace *sfr;
3592
3593    if (flags == FRD_UNDO)
3594    {
3595	char_u	*save_cpo = p_cpo;
3596
3597	/* No need to be Vi compatible here. */
3598	p_cpo = (char_u *)"";
3599	u_undo(1);
3600	p_cpo = save_cpo;
3601	gui_update_screen();
3602	return;
3603    }
3604
3605    /* Get the search/replace strings from the dialog */
3606    if (flags == FRD_FINDNEXT)
3607    {
3608	repl_text = NULL;
3609	sfr = &find_widgets;
3610    }
3611    else
3612    {
3613	repl_text = XmTextFieldGetString(repl_widgets.with);
3614	sfr = &repl_widgets;
3615    }
3616    find_text = XmTextFieldGetString(sfr->what);
3617    XtVaGetValues(sfr->down, XmNset, &direction_down, NULL);
3618    XtVaGetValues(sfr->wword, XmNset, &wword, NULL);
3619    XtVaGetValues(sfr->mcase, XmNset, &mcase, NULL);
3620    if (wword)
3621	flags |= FRD_WHOLE_WORD;
3622    if (mcase)
3623	flags |= FRD_MATCH_CASE;
3624
3625    (void)gui_do_findrepl((int)flags, (char_u *)find_text, (char_u *)repl_text,
3626							      direction_down);
3627
3628    if (find_text != NULL)
3629	XtFree(find_text);
3630    if (repl_text != NULL)
3631	XtFree(repl_text);
3632}
3633
3634    static void
3635find_replace_keypress(w, frdp, event)
3636    Widget		w UNUSED;
3637    SharedFindReplace	*frdp;
3638    XKeyEvent		*event;
3639{
3640    KeySym keysym;
3641
3642    if (frdp == NULL)
3643	return;
3644
3645    keysym = XLookupKeysym(event, 0);
3646
3647    /* the scape key pops the whole dialog down */
3648    if (keysym == XK_Escape)
3649	XtUnmanageChild(frdp->dialog);
3650}
3651
3652    static void
3653set_label(w, label)
3654    Widget w;
3655    char_u *label;
3656{
3657    XmString	str;
3658    char_u	*p, *next;
3659    KeySym	mnemonic = NUL;
3660
3661    if (!w)
3662	return;
3663
3664    p = vim_strsave(label);
3665    if (p == NULL)
3666	return;
3667    for (next = p; *next; ++next)
3668    {
3669	if (*next == DLG_HOTKEY_CHAR)
3670	{
3671	    int len = STRLEN(next);
3672
3673	    if (len > 0)
3674	    {
3675		mch_memmove(next, next + 1, len);
3676		mnemonic = next[0];
3677	    }
3678	}
3679    }
3680
3681    str = XmStringCreateSimple((char *)p);
3682    vim_free(p);
3683    if (str)
3684    {
3685	XtVaSetValues(w,
3686		XmNlabelString, str,
3687		XmNmnemonic, mnemonic,
3688		NULL);
3689	XmStringFree(str);
3690    }
3691    gui_motif_menu_fontlist(w);
3692}
3693
3694    static void
3695find_replace_dialog_create(arg, do_replace)
3696    char_u	*arg;
3697    int		do_replace;
3698{
3699    SharedFindReplace	*frdp;
3700    Widget		separator;
3701    Widget		input_form;
3702    Widget		button_form;
3703    Widget		toggle_form;
3704    Widget		frame;
3705    XmString		str;
3706    int			n;
3707    Arg			args[6];
3708    int			wword = FALSE;
3709    int			mcase = !p_ic;
3710    Dimension		width;
3711    Dimension		widest;
3712    char_u		*entry_text;
3713
3714    frdp = do_replace ? &repl_widgets : &find_widgets;
3715
3716    /* Get the search string to use. */
3717    entry_text = get_find_dialog_text(arg, &wword, &mcase);
3718
3719    /* If the dialog already exists, just raise it. */
3720    if (frdp->dialog)
3721    {
3722	gui_motif_synch_fonts();
3723
3724	/* If the window is already up, just pop it to the top */
3725	if (XtIsManaged(frdp->dialog))
3726	    XMapRaised(XtDisplay(frdp->dialog),
3727					    XtWindow(XtParent(frdp->dialog)));
3728	else
3729	    XtManageChild(frdp->dialog);
3730	XtPopup(XtParent(frdp->dialog), XtGrabNone);
3731	XmProcessTraversal(frdp->what, XmTRAVERSE_CURRENT);
3732
3733	if (entry_text != NULL)
3734	    XmTextFieldSetString(frdp->what, (char *)entry_text);
3735	vim_free(entry_text);
3736
3737	XtVaSetValues(frdp->wword, XmNset, wword, NULL);
3738	return;
3739    }
3740
3741    /* Create a fresh new dialog window */
3742    if (do_replace)
3743	 str = XmStringCreateSimple(_("VIM - Search and Replace..."));
3744    else
3745	 str = XmStringCreateSimple(_("VIM - Search..."));
3746
3747    n = 0;
3748    XtSetArg(args[n], XmNautoUnmanage, False); n++;
3749    XtSetArg(args[n], XmNnoResize, True); n++;
3750    XtSetArg(args[n], XmNdialogTitle, str); n++;
3751
3752    frdp->dialog = XmCreateFormDialog(vimShell, "findReplaceDialog", args, n);
3753    XmStringFree(str);
3754    XtAddCallback(frdp->dialog, XmNdestroyCallback,
3755	    find_replace_destroy_callback, frdp);
3756
3757    button_form = XtVaCreateWidget("buttonForm",
3758	    xmFormWidgetClass,	frdp->dialog,
3759	    XmNrightAttachment, XmATTACH_FORM,
3760	    XmNrightOffset, 4,
3761	    XmNtopAttachment, XmATTACH_FORM,
3762	    XmNtopOffset, 4,
3763	    XmNbottomAttachment, XmATTACH_FORM,
3764	    XmNbottomOffset, 4,
3765	    NULL);
3766
3767    frdp->find = XtVaCreateManagedWidget("findButton",
3768	    xmPushButtonWidgetClass, button_form,
3769	    XmNsensitive, True,
3770	    XmNtopAttachment, XmATTACH_FORM,
3771	    XmNleftAttachment, XmATTACH_FORM,
3772	    XmNrightAttachment, XmATTACH_FORM,
3773	    NULL);
3774    set_label(frdp->find, _("Find &Next"));
3775
3776    XtAddCallback(frdp->find, XmNactivateCallback,
3777	    find_replace_callback,
3778	    (do_replace ? (XtPointer)FRD_R_FINDNEXT : (XtPointer)FRD_FINDNEXT));
3779
3780    if (do_replace)
3781    {
3782	frdp->replace = XtVaCreateManagedWidget("replaceButton",
3783		xmPushButtonWidgetClass, button_form,
3784		XmNtopAttachment, XmATTACH_WIDGET,
3785		XmNtopWidget, frdp->find,
3786		XmNleftAttachment, XmATTACH_FORM,
3787		XmNrightAttachment, XmATTACH_FORM,
3788		NULL);
3789	set_label(frdp->replace, _("&Replace"));
3790	XtAddCallback(frdp->replace, XmNactivateCallback,
3791		find_replace_callback, (XtPointer)FRD_REPLACE);
3792
3793	frdp->all = XtVaCreateManagedWidget("replaceAllButton",
3794		xmPushButtonWidgetClass, button_form,
3795		XmNtopAttachment, XmATTACH_WIDGET,
3796		XmNtopWidget, frdp->replace,
3797		XmNleftAttachment, XmATTACH_FORM,
3798		XmNrightAttachment, XmATTACH_FORM,
3799		NULL);
3800	set_label(frdp->all, _("Replace &All"));
3801	XtAddCallback(frdp->all, XmNactivateCallback,
3802		find_replace_callback, (XtPointer)FRD_REPLACEALL);
3803
3804	frdp->undo = XtVaCreateManagedWidget("undoButton",
3805		xmPushButtonWidgetClass, button_form,
3806		XmNtopAttachment, XmATTACH_WIDGET,
3807		XmNtopWidget, frdp->all,
3808		XmNleftAttachment, XmATTACH_FORM,
3809		XmNrightAttachment, XmATTACH_FORM,
3810		NULL);
3811	set_label(frdp->undo, _("&Undo"));
3812	XtAddCallback(frdp->undo, XmNactivateCallback,
3813		find_replace_callback, (XtPointer)FRD_UNDO);
3814    }
3815
3816    frdp->cancel = XtVaCreateManagedWidget("closeButton",
3817	    xmPushButtonWidgetClass, button_form,
3818	    XmNleftAttachment, XmATTACH_FORM,
3819	    XmNrightAttachment, XmATTACH_FORM,
3820	    XmNbottomAttachment, XmATTACH_FORM,
3821	    NULL);
3822    set_label(frdp->cancel, _("&Cancel"));
3823    XtAddCallback(frdp->cancel, XmNactivateCallback,
3824	    find_replace_dismiss_callback, frdp);
3825    gui_motif_menu_fontlist(frdp->cancel);
3826
3827    XtManageChild(button_form);
3828
3829    n = 0;
3830    XtSetArg(args[n], XmNorientation, XmVERTICAL); n++;
3831    XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
3832    XtSetArg(args[n], XmNrightWidget, button_form); n++;
3833    XtSetArg(args[n], XmNrightOffset, 4); n++;
3834    XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
3835    XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
3836    separator = XmCreateSeparatorGadget(frdp->dialog, "separator", args, n);
3837    XtManageChild(separator);
3838
3839    input_form = XtVaCreateWidget("inputForm",
3840	    xmFormWidgetClass,	frdp->dialog,
3841	    XmNleftAttachment, XmATTACH_FORM,
3842	    XmNleftOffset, 4,
3843	    XmNrightAttachment, XmATTACH_WIDGET,
3844	    XmNrightWidget, separator,
3845	    XmNrightOffset, 4,
3846	    XmNtopAttachment, XmATTACH_FORM,
3847	    XmNtopOffset, 4,
3848	    NULL);
3849
3850    {
3851	Widget label_what;
3852	Widget label_with = (Widget)0;
3853
3854	str = XmStringCreateSimple(_("Find what:"));
3855	label_what = XtVaCreateManagedWidget("whatLabel",
3856		xmLabelGadgetClass, input_form,
3857		XmNlabelString, str,
3858		XmNleftAttachment,	XmATTACH_FORM,
3859		XmNtopAttachment, XmATTACH_FORM,
3860		XmNtopOffset, 4,
3861		NULL);
3862	XmStringFree(str);
3863	gui_motif_menu_fontlist(label_what);
3864
3865	frdp->what = XtVaCreateManagedWidget("whatText",
3866		xmTextFieldWidgetClass, input_form,
3867		XmNtopAttachment, XmATTACH_FORM,
3868		XmNrightAttachment, XmATTACH_FORM,
3869		XmNleftAttachment, XmATTACH_FORM,
3870		NULL);
3871
3872	if (do_replace)
3873	{
3874	    frdp->with = XtVaCreateManagedWidget("withText",
3875		    xmTextFieldWidgetClass,	input_form,
3876		    XmNtopAttachment, XmATTACH_WIDGET,
3877		    XmNtopWidget, frdp->what,
3878		    XmNtopOffset, 4,
3879		    XmNleftAttachment, XmATTACH_FORM,
3880		    XmNrightAttachment, XmATTACH_FORM,
3881		    XmNbottomAttachment, XmATTACH_FORM,
3882		    NULL);
3883
3884	    XtAddCallback(frdp->with, XmNactivateCallback,
3885		    find_replace_callback, (XtPointer) FRD_R_FINDNEXT);
3886
3887	    str = XmStringCreateSimple(_("Replace with:"));
3888	    label_with = XtVaCreateManagedWidget("withLabel",
3889		    xmLabelGadgetClass, input_form,
3890		    XmNlabelString, str,
3891		    XmNleftAttachment, XmATTACH_FORM,
3892		    XmNtopAttachment, XmATTACH_WIDGET,
3893		    XmNtopWidget, frdp->what,
3894		    XmNtopOffset, 4,
3895		    XmNbottomAttachment, XmATTACH_FORM,
3896		    NULL);
3897	    XmStringFree(str);
3898	    gui_motif_menu_fontlist(label_with);
3899
3900	    /*
3901	     * Make the entry activation only change the input focus onto the
3902	     * with item.
3903	     */
3904	    XtAddCallback(frdp->what, XmNactivateCallback,
3905		    entry_activate_callback, frdp->with);
3906	    XtAddEventHandler(frdp->with, KeyPressMask, False,
3907			    (XtEventHandler)find_replace_keypress,
3908			    (XtPointer) frdp);
3909
3910	}
3911	else
3912	{
3913	    /*
3914	     * Make the entry activation do the search.
3915	     */
3916	    XtAddCallback(frdp->what, XmNactivateCallback,
3917		    find_replace_callback, (XtPointer)FRD_FINDNEXT);
3918	}
3919	XtAddEventHandler(frdp->what, KeyPressMask, False,
3920			    (XtEventHandler)find_replace_keypress,
3921			    (XtPointer)frdp);
3922
3923	/* Get the maximum width between the label widgets and line them up.
3924	 */
3925	n = 0;
3926	XtSetArg(args[n], XmNwidth, &width); n++;
3927	XtGetValues(label_what, args, n);
3928	widest = width;
3929	if (do_replace)
3930	{
3931	    XtGetValues(label_with, args, n);
3932	    if (width > widest)
3933		widest = width;
3934	}
3935
3936	XtVaSetValues(frdp->what, XmNleftOffset, widest, NULL);
3937	if (do_replace)
3938	    XtVaSetValues(frdp->with, XmNleftOffset, widest, NULL);
3939
3940    }
3941
3942    XtManageChild(input_form);
3943
3944    {
3945	Widget radio_box;
3946	Widget w;
3947
3948	frame = XtVaCreateWidget("directionFrame",
3949		xmFrameWidgetClass, frdp->dialog,
3950		XmNtopAttachment, XmATTACH_WIDGET,
3951		XmNtopWidget, input_form,
3952		XmNtopOffset, 4,
3953		XmNbottomAttachment, XmATTACH_FORM,
3954		XmNbottomOffset, 4,
3955		XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET,
3956		XmNrightWidget, input_form,
3957		NULL);
3958
3959	str = XmStringCreateSimple(_("Direction"));
3960	w = XtVaCreateManagedWidget("directionFrameLabel",
3961		xmLabelGadgetClass, frame,
3962		XmNlabelString, str,
3963		XmNchildHorizontalAlignment, XmALIGNMENT_BEGINNING,
3964		XmNchildType, XmFRAME_TITLE_CHILD,
3965		NULL);
3966	XmStringFree(str);
3967	gui_motif_menu_fontlist(w);
3968
3969	radio_box = XmCreateRadioBox(frame, "radioBox",
3970		(ArgList)NULL, 0);
3971
3972	str = XmStringCreateSimple( _("Up"));
3973	frdp->up = XtVaCreateManagedWidget("upRadioButton",
3974		xmToggleButtonGadgetClass, radio_box,
3975		XmNlabelString, str,
3976		XmNset, False,
3977		NULL);
3978	XmStringFree(str);
3979	gui_motif_menu_fontlist(frdp->up);
3980
3981	str = XmStringCreateSimple(_("Down"));
3982	frdp->down = XtVaCreateManagedWidget("downRadioButton",
3983		xmToggleButtonGadgetClass, radio_box,
3984		XmNlabelString, str,
3985		XmNset, True,
3986		NULL);
3987	XmStringFree(str);
3988	gui_motif_menu_fontlist(frdp->down);
3989
3990	XtManageChild(radio_box);
3991	XtManageChild(frame);
3992    }
3993
3994    toggle_form = XtVaCreateWidget("toggleForm",
3995	    xmFormWidgetClass,	frdp->dialog,
3996	    XmNleftAttachment, XmATTACH_FORM,
3997	    XmNleftOffset, 4,
3998	    XmNrightAttachment, XmATTACH_WIDGET,
3999	    XmNrightWidget, frame,
4000	    XmNrightOffset, 4,
4001	    XmNtopAttachment, XmATTACH_WIDGET,
4002	    XmNtopWidget, input_form,
4003	    XmNtopOffset, 4,
4004	    XmNbottomAttachment, XmATTACH_FORM,
4005	    XmNbottomOffset, 4,
4006	    NULL);
4007
4008    str = XmStringCreateSimple(_("Match whole word only"));
4009    frdp->wword = XtVaCreateManagedWidget("wordToggle",
4010	    xmToggleButtonGadgetClass, toggle_form,
4011	    XmNlabelString, str,
4012	    XmNtopAttachment, XmATTACH_FORM,
4013	    XmNtopOffset, 4,
4014	    XmNleftAttachment, XmATTACH_FORM,
4015	    XmNleftOffset, 4,
4016	    XmNset, wword,
4017	    NULL);
4018    XmStringFree(str);
4019
4020    str = XmStringCreateSimple(_("Match case"));
4021    frdp->mcase = XtVaCreateManagedWidget("caseToggle",
4022	    xmToggleButtonGadgetClass, toggle_form,
4023	    XmNlabelString, str,
4024	    XmNleftAttachment, XmATTACH_FORM,
4025	    XmNleftOffset, 4,
4026	    XmNtopAttachment, XmATTACH_WIDGET,
4027	    XmNtopWidget, frdp->wword,
4028	    XmNtopOffset, 4,
4029	    XmNset, mcase,
4030	    NULL);
4031    XmStringFree(str);
4032    gui_motif_menu_fontlist(frdp->wword);
4033    gui_motif_menu_fontlist(frdp->mcase);
4034
4035    XtManageChild(toggle_form);
4036
4037    if (entry_text != NULL)
4038	XmTextFieldSetString(frdp->what, (char *)entry_text);
4039    vim_free(entry_text);
4040
4041    gui_motif_synch_fonts();
4042
4043    manage_centered(frdp->dialog);
4044    activate_dialog_mnemonics(frdp->dialog);
4045    XmProcessTraversal(frdp->what, XmTRAVERSE_CURRENT);
4046}
4047
4048   void
4049gui_mch_find_dialog(eap)
4050    exarg_T	*eap;
4051{
4052    if (!gui.in_use)
4053	return;
4054
4055    find_replace_dialog_create(eap->arg, FALSE);
4056}
4057
4058
4059    void
4060gui_mch_replace_dialog(eap)
4061    exarg_T	*eap;
4062{
4063    if (!gui.in_use)
4064	return;
4065
4066    find_replace_dialog_create(eap->arg, TRUE);
4067}
4068
4069/*
4070 * Synchronize all gui elements, which are dependant upon the
4071 * main text font used. Those are in esp. the find/replace dialogs.
4072 * If you don't understand why this should be needed, please try to
4073 * search for "pi��" in iso8859-2.
4074 */
4075    void
4076gui_motif_synch_fonts(void)
4077{
4078    SharedFindReplace *frdp;
4079    int		    do_replace;
4080    XFontStruct	    *font;
4081    XmFontList	    font_list;
4082
4083    /* FIXME: Unless we find out how to create a XmFontList from a XFontSet,
4084     * we just give up here on font synchronization. */
4085    font = (XFontStruct *)gui.norm_font;
4086    if (font == NULL)
4087	return;
4088
4089    font_list = gui_motif_create_fontlist(font);
4090
4091    /* OK this loop is a bit tricky... */
4092    for (do_replace = 0; do_replace <= 1; ++do_replace)
4093    {
4094	frdp = (do_replace) ? (&repl_widgets) : (&find_widgets);
4095	if (frdp->dialog)
4096	{
4097	    XtVaSetValues(frdp->what, XmNfontList, font_list, NULL);
4098	    if (do_replace)
4099		XtVaSetValues(frdp->with, XmNfontList, font_list, NULL);
4100	}
4101    }
4102
4103    XmFontListFree(font_list);
4104}
4105