1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved		by Bram Moolenaar
4  *				GUI/Motif support by Robert Webb
5  *				Athena port by Bill Foster
6  *
7  * Do ":help uganda"  in Vim to read copying and usage conditions.
8  * Do ":help credits" in Vim to see a list of people who contributed.
9  * See README.txt for an overview of the Vim source code.
10  */
11 
12 #include "vim.h"
13 
14 #include <X11/StringDefs.h>
15 #include <X11/Intrinsic.h>
16 #ifdef FEAT_GUI_NEXTAW
17 # include <X11/neXtaw/Form.h>
18 # include <X11/neXtaw/SimpleMenu.h>
19 # include <X11/neXtaw/MenuButton.h>
20 # include <X11/neXtaw/SmeBSB.h>
21 # include <X11/neXtaw/SmeLine.h>
22 # include <X11/neXtaw/Box.h>
23 # include <X11/neXtaw/Dialog.h>
24 # include <X11/neXtaw/Text.h>
25 # include <X11/neXtaw/AsciiText.h>
26 # include <X11/neXtaw/Scrollbar.h>
27 #else
28 # include <X11/Xaw/Form.h>
29 # include <X11/Xaw/SimpleMenu.h>
30 # include <X11/Xaw/MenuButton.h>
31 # include <X11/Xaw/SmeBSB.h>
32 # include <X11/Xaw/SmeLine.h>
33 # include <X11/Xaw/Box.h>
34 # include <X11/Xaw/Dialog.h>
35 # include <X11/Xaw/Text.h>
36 # include <X11/Xaw/AsciiText.h>
37 #endif // FEAT_GUI_NEXTAW
38 
39 #ifndef FEAT_GUI_NEXTAW
40 # include "gui_at_sb.h"
41 #endif
42 
43 extern Widget vimShell;
44 
45 static Widget vimForm = (Widget)0;
46 Widget textArea = (Widget)0;
47 #ifdef FEAT_MENU
48 static Widget menuBar = (Widget)0;
49 static XtIntervalId timer = 0;	    // 0 = expired, otherwise active
50 
51 // Used to figure out menu ordering
52 static vimmenu_T *a_cur_menu = NULL;
53 static Cardinal	athena_calculate_ins_pos(Widget);
54 
55 static void gui_athena_popup_callback(Widget, XtPointer, XtPointer);
56 static void gui_athena_delayed_arm_action(Widget, XEvent *, String *,
57 						 Cardinal *);
58 static void gui_athena_popdown_submenus_action(Widget, XEvent *,
59 						      String *, Cardinal *);
60 static XtActionsRec	pullAction[2] = {
61     { "menu-delayedpopup", (XtActionProc)gui_athena_delayed_arm_action},
62     { "menu-popdownsubmenus", (XtActionProc)gui_athena_popdown_submenus_action}
63 };
64 #endif
65 
66 #ifdef FEAT_TOOLBAR
67 static void gui_mch_reset_focus(void);
68 static Widget toolBar = (Widget)0;
69 #endif
70 
71 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
72 static void gui_athena_menu_colors(Widget id);
73 #endif
74 static void gui_athena_scroll_colors(Widget id);
75 
76 #ifdef FEAT_MENU
77 static XtTranslations	popupTrans, parentTrans, menuTrans, supermenuTrans;
78 static Pixmap		pullerBitmap = None;
79 static int		puller_width = 0;
80 #endif
81 
82 /*
83  * Scrollbar callback (XtNjumpProc) for when the scrollbar is dragged with the
84  * left or middle mouse button.
85  */
86     static void
gui_athena_scroll_cb_jump(Widget w UNUSED,XtPointer client_data,XtPointer call_data)87 gui_athena_scroll_cb_jump(
88     Widget	w UNUSED,
89     XtPointer	client_data,
90     XtPointer	call_data)
91 {
92     scrollbar_T *sb, *sb_info;
93     long	value;
94 
95     sb = gui_find_scrollbar((long)client_data);
96 
97     if (sb == NULL)
98 	return;
99     else if (sb->wp != NULL)	    // Left or right scrollbar
100     {
101 	/*
102 	 * Careful: need to get scrollbar info out of first (left) scrollbar
103 	 * for window, but keep real scrollbar too because we must pass it to
104 	 * gui_drag_scrollbar().
105 	 */
106 	sb_info = &sb->wp->w_scrollbars[0];
107     }
108     else	    // Bottom scrollbar
109 	sb_info = sb;
110 
111     value = (long)(*((float *)call_data) * (float)(sb_info->max + 1) + 0.001);
112     if (value > sb_info->max)
113 	value = sb_info->max;
114 
115     gui_drag_scrollbar(sb, value, TRUE);
116 }
117 
118 /*
119  * Scrollbar callback (XtNscrollProc) for paging up or down with the left or
120  * right mouse buttons.
121  */
122     static void
gui_athena_scroll_cb_scroll(Widget w UNUSED,XtPointer client_data,XtPointer call_data)123 gui_athena_scroll_cb_scroll(
124     Widget	w UNUSED,
125     XtPointer	client_data,
126     XtPointer	call_data)
127 {
128     scrollbar_T *sb, *sb_info;
129     long	value;
130     int		data = (int)(long)call_data;
131     int		page;
132 
133     sb = gui_find_scrollbar((long)client_data);
134 
135     if (sb == NULL)
136 	return;
137     if (sb->wp != NULL)		// Left or right scrollbar
138     {
139 	/*
140 	 * Careful: need to get scrollbar info out of first (left) scrollbar
141 	 * for window, but keep real scrollbar too because we must pass it to
142 	 * gui_drag_scrollbar().
143 	 */
144 	sb_info = &sb->wp->w_scrollbars[0];
145 
146 	if (sb_info->size > 5)
147 	    page = sb_info->size - 2;	    // use two lines of context
148 	else
149 	    page = sb_info->size;
150 #ifdef FEAT_GUI_NEXTAW
151 	if (data < 0)
152 	{
153 	    data = (data - gui.char_height + 1) / gui.char_height;
154 	    if (data > -sb_info->size)
155 		data = -1;
156 	    else
157 		data = -page;
158 	}
159 	else if (data > 0)
160 	{
161 	    data = (data + gui.char_height - 1) / gui.char_height;
162 	    if (data < sb_info->size)
163 		data = 1;
164 	    else
165 		data = page;
166 	}
167 #else
168 	switch (data)
169 	{
170 	    case  ONE_LINE_DATA: data = 1; break;
171 	    case -ONE_LINE_DATA: data = -1; break;
172 	    case  ONE_PAGE_DATA: data = page; break;
173 	    case -ONE_PAGE_DATA: data = -page; break;
174 	    case  END_PAGE_DATA: data = sb_info->max; break;
175 	    case -END_PAGE_DATA: data = -sb_info->max; break;
176 			default: data = 0; break;
177 	}
178 #endif
179     }
180     else			// Bottom scrollbar
181     {
182 	sb_info = sb;
183 #ifdef FEAT_GUI_NEXTAW
184 	if (data < 0)
185 	{
186 	    data = (data - gui.char_width + 1) / gui.char_width;
187 	    if (data > -sb->size)
188 		data = -1;
189 	}
190 	else if (data > 0)
191 	{
192 	    data = (data + gui.char_width - 1) / gui.char_width;
193 	    if (data < sb->size)
194 		data = 1;
195 	}
196 #endif
197 	if (data < -1)		// page-width left
198 	{
199 	    if (sb->size > 8)
200 		data = -(sb->size - 5);
201 	    else
202 		data = -sb->size;
203 	}
204 	else if (data > 1)	// page-width right
205 	{
206 	    if (sb->size > 8)
207 		data = (sb->size - 5);
208 	    else
209 		data = sb->size;
210 	}
211     }
212 
213     value = sb_info->value + data;
214     if (value > sb_info->max)
215 	value = sb_info->max;
216     else if (value < 0)
217 	value = 0;
218 
219     // Update the bottom scrollbar an extra time (why is this needed??
220     if (sb->wp == NULL)		// Bottom scrollbar
221 	gui_mch_set_scrollbar_thumb(sb, value, sb->size, sb->max);
222 
223     gui_drag_scrollbar(sb, value, FALSE);
224 }
225 
226 /*
227  * Create all the Athena widgets necessary.
228  */
229     void
gui_x11_create_widgets(void)230 gui_x11_create_widgets(void)
231 {
232     /*
233      * We don't have any borders handled internally by the textArea to worry
234      * about so only skip over the configured border width.
235      */
236     gui.border_offset = gui.border_width;
237 
238     // The form containing all the other widgets
239     vimForm = XtVaCreateManagedWidget("vimForm",
240 	formWidgetClass,	vimShell,
241 	XtNborderWidth,		0,
242 	NULL);
243     gui_athena_scroll_colors(vimForm);
244 
245 #ifdef FEAT_MENU
246     // The top menu bar
247     menuBar = XtVaCreateManagedWidget("menuBar",
248 	boxWidgetClass,		vimForm,
249 	XtNresizable,		True,
250 	XtNtop,			XtChainTop,
251 	XtNbottom,		XtChainTop,
252 	XtNleft,		XtChainLeft,
253 	XtNright,		XtChainRight,
254 	XtNinsertPosition,	athena_calculate_ins_pos,
255 	NULL);
256     gui_athena_menu_colors(menuBar);
257     if (gui.menu_fg_pixel != INVALCOLOR)
258 	XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL);
259 #endif
260 
261 #ifdef FEAT_TOOLBAR
262     // Don't create it Managed, it will be managed when creating the first
263     // item.  Otherwise an empty toolbar shows up.
264     toolBar = XtVaCreateWidget("toolBar",
265 	boxWidgetClass,		vimForm,
266 	XtNresizable,		True,
267 	XtNtop,			XtChainTop,
268 	XtNbottom,		XtChainTop,
269 	XtNleft,		XtChainLeft,
270 	XtNright,		XtChainRight,
271 	XtNorientation,		XtorientHorizontal,
272 	XtNhSpace,		1,
273 	XtNvSpace,		3,
274 	XtNinsertPosition,	athena_calculate_ins_pos,
275 	NULL);
276     gui_athena_menu_colors(toolBar);
277 #endif
278 
279     // The text area.
280     textArea = XtVaCreateManagedWidget("textArea",
281 	coreWidgetClass,	vimForm,
282 	XtNresizable,		True,
283 	XtNtop,			XtChainTop,
284 	XtNbottom,		XtChainTop,
285 	XtNleft,		XtChainLeft,
286 	XtNright,		XtChainLeft,
287 	XtNbackground,		gui.back_pixel,
288 	XtNborderWidth,		0,
289 	NULL);
290 
291     /*
292      * Install the callbacks.
293      */
294     gui_x11_callbacks(textArea, vimForm);
295 
296 #ifdef FEAT_MENU
297     popupTrans = XtParseTranslationTable(
298 	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
299 	    "<LeaveWindow>: unhighlight()\n"
300 	    "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n"
301 	    "<Motion>: highlight() menu-delayedpopup()");
302     parentTrans = XtParseTranslationTable("<LeaveWindow>: unhighlight()");
303     menuTrans = XtParseTranslationTable(
304 	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
305 	    "<LeaveWindow>: menu-popdownsubmenus() XtMenuPopdown() unhighlight()\n"
306 	    "<BtnUp>: notify() unhighlight()\n"
307 	    "<BtnMotion>: highlight() menu-delayedpopup()");
308     supermenuTrans = XtParseTranslationTable(
309 	    "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
310 	    "<LeaveWindow>: unhighlight()\n"
311 	    "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n"
312 	    "<BtnMotion>: highlight() menu-delayedpopup()");
313 
314     XtAppAddActions(XtWidgetToApplicationContext(vimForm), pullAction,
315 		    XtNumber(pullAction));
316 #endif
317 
318     // Pretend we don't have input focus, we will get an event if we do.
319     gui.in_focus = FALSE;
320 }
321 
322 #ifdef FEAT_MENU
323 /*
324  * Calculates the Pixmap based on the size of the current menu font.
325  */
326     static Pixmap
gui_athena_create_pullright_pixmap(Widget w)327 gui_athena_create_pullright_pixmap(Widget w)
328 {
329     Pixmap  retval;
330 #ifdef FONTSET_ALWAYS
331     XFontSet	font = None;
332 #else
333     XFontStruct	*font = NULL;
334 #endif
335 
336 #ifdef FONTSET_ALWAYS
337     if (gui.menu_fontset == NOFONTSET)
338 #else
339     if (gui.menu_font == NOFONT)
340 #endif
341     {
342 	XrmValue from, to;
343 	WidgetList  children;
344 	Cardinal    num_children;
345 
346 #ifdef FONTSET_ALWAYS
347 	from.size = strlen(from.addr = XtDefaultFontSet);
348 	to.addr = (XtPointer)&font;
349 	to.size = sizeof(XFontSet);
350 #else
351 	from.size = strlen(from.addr = XtDefaultFont);
352 	to.addr = (XtPointer)&font;
353 	to.size = sizeof(XFontStruct *);
354 #endif
355 	// Assumption: The menuBar children will use the same font as the
356 	//	       pulldown menu items AND they will all be of type
357 	//	       XtNfont.
358 	XtVaGetValues(menuBar, XtNchildren, &children,
359 			       XtNnumChildren, &num_children,
360 			       NULL);
361 	if (XtConvertAndStore(w ? w :
362 				(num_children > 0) ? children[0] : menuBar,
363 			      XtRString, &from,
364 #ifdef FONTSET_ALWAYS
365 			      XtRFontSet, &to
366 #else
367 			      XtRFontStruct, &to
368 #endif
369 		    ) == False)
370 	    return None;
371 	// "font" should now contain data
372     }
373     else
374 #ifdef FONTSET_ALWAYS
375 	font = (XFontSet)gui.menu_fontset;
376 #else
377 	font = (XFontStruct *)gui.menu_font;
378 #endif
379 
380     {
381 	int	    width, height;
382 	GC	    draw_gc, undraw_gc;
383 	XGCValues   gc_values;
384 	XPoint	    points[3];
385 
386 #ifdef FONTSET_ALWAYS
387 	height = fontset_height2(font);
388 #else
389 	height = font->max_bounds.ascent + font->max_bounds.descent;
390 #endif
391 	width = height - 2;
392 	puller_width = width + 4;
393 	retval = XCreatePixmap(gui.dpy,DefaultRootWindow(gui.dpy),width,
394 			       height, 1);
395 	gc_values.foreground = 1;
396 	gc_values.background = 0;
397 	draw_gc = XCreateGC(gui.dpy, retval,
398 		    GCForeground | GCBackground,
399 		    &gc_values);
400 	gc_values.foreground = 0;
401 	gc_values.background = 1;
402 	undraw_gc = XCreateGC(gui.dpy, retval,
403 			      GCForeground | GCBackground,
404 			      &gc_values);
405 	points[0].x = 0;
406 	points[0].y = 0;
407 	points[1].x = width - 1;
408 	points[1].y = (height - 1) / 2;
409 	points[2].x = 0;
410 	points[2].y = height - 1;
411 	XFillRectangle(gui.dpy, retval, undraw_gc, 0, 0, height, height);
412 	XFillPolygon(gui.dpy, retval, draw_gc, points, XtNumber(points),
413 		     Convex, CoordModeOrigin);
414 	XFreeGC(gui.dpy, draw_gc);
415 	XFreeGC(gui.dpy, undraw_gc);
416     }
417     return retval;
418 }
419 #endif
420 
421 /*
422  * Called when the GUI is not going to start after all.
423  */
424     void
gui_x11_destroy_widgets(void)425 gui_x11_destroy_widgets(void)
426 {
427     textArea = NULL;
428 #ifdef FEAT_MENU
429     menuBar = NULL;
430 #endif
431 #ifdef FEAT_TOOLBAR
432     toolBar = NULL;
433 #endif
434 }
435 
436 #if defined(FEAT_TOOLBAR) || defined(PROTO)
437 # include "gui_x11_pm.h"
438 # ifdef HAVE_X11_XPM_H
439 #  include <X11/xpm.h>
440 # endif
441 
442 static void createXpmImages(char_u *path, char **xpm, Pixmap *sen);
443 
444 /*
445  * Allocated a pixmap for toolbar menu "menu".
446  * Return in "sen".
447  */
448     static void
get_toolbar_pixmap(vimmenu_T * menu,Pixmap * sen)449 get_toolbar_pixmap(vimmenu_T *menu, Pixmap *sen)
450 {
451     char_u	buf[MAXPATHL];		// buffer storing expanded pathname
452     char	**xpm = NULL;		// xpm array
453 
454     buf[0] = NUL;			// start with NULL path
455 
456     if (menu->iconfile != NULL)
457     {
458 	// Use the "icon="  argument.
459 	gui_find_iconfile(menu->iconfile, buf, "xpm");
460 	createXpmImages(buf, NULL, sen);
461 
462 	// If it failed, try using the menu name.
463 	if (*sen == (Pixmap)0 && gui_find_bitmap(menu->name, buf, "xpm") == OK)
464 	    createXpmImages(buf, NULL, sen);
465 	if (*sen != (Pixmap)0)
466 	    return;
467     }
468 
469     if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL)
470     {
471 	if (menu->iconidx >= 0 && menu->iconidx
472 	      < (int)ARRAY_LENGTH(built_in_pixmaps))
473 	    xpm = built_in_pixmaps[menu->iconidx];
474 	else
475 	    xpm = tb_blank_xpm;
476     }
477 
478     if (xpm != NULL || buf[0] != NUL)
479 	createXpmImages(buf, xpm, sen);
480 }
481 
482 /*
483  * Read an Xpm file, doing color substitutions for the foreground and
484  * background colors. If there is an error reading a color xpm file,
485  * drop back and read the monochrome file. If successful, create the
486  * insensitive Pixmap too.
487  */
488     static void
createXpmImages(char_u * path,char ** xpm,Pixmap * sen)489 createXpmImages(char_u *path, char **xpm, Pixmap *sen)
490 {
491     Window	rootWindow;
492     XpmAttributes attrs;
493     XpmColorSymbol color[5] =
494     {
495 	{"none", "none", 0},
496 	{"iconColor1", NULL, 0},
497 	{"bottomShadowColor", NULL, 0},
498 	{"topShadowColor", NULL, 0},
499 	{"selectColor", NULL, 0}
500     };
501     int		screenNum;
502     int		status;
503     Pixmap	mask;
504     Pixmap	map;
505 
506     gui_mch_get_toolbar_colors(
507 	    &color[BACKGROUND].pixel,
508 	    &color[FOREGROUND].pixel,
509 	    &color[BOTTOM_SHADOW].pixel,
510 	    &color[TOP_SHADOW].pixel,
511 	    &color[HIGHLIGHT].pixel);
512 
513     // Setup the color substitution table
514     attrs.valuemask = XpmColorSymbols;
515     attrs.colorsymbols = color;
516     attrs.numsymbols = 5;
517 
518     screenNum = DefaultScreen(gui.dpy);
519     rootWindow = RootWindow(gui.dpy, screenNum);
520 
521     // Create the "sensitive" pixmap
522     if (xpm != NULL)
523 	status = XpmCreatePixmapFromData(gui.dpy, rootWindow, xpm,
524 							 &map, &mask, &attrs);
525     else
526 	status = XpmReadFileToPixmap(gui.dpy, rootWindow, (char *)path,
527 							 &map, &mask, &attrs);
528     if (status == XpmSuccess && map != 0)
529     {
530 	XGCValues   gcvalues;
531 	GC	    back_gc;
532 	GC	    mask_gc;
533 
534 	// Need to create new Pixmaps with the mask applied.
535 	gcvalues.foreground = color[BACKGROUND].pixel;
536 	back_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues);
537 	mask_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues);
538 	XSetClipMask(gui.dpy, mask_gc, mask);
539 
540 	// Create the "sensitive" pixmap.
541 	*sen = XCreatePixmap(gui.dpy, rootWindow,
542 		 attrs.width, attrs.height,
543 		 DefaultDepth(gui.dpy, screenNum));
544 	XFillRectangle(gui.dpy, *sen, back_gc, 0, 0,
545 		attrs.width, attrs.height);
546 	XCopyArea(gui.dpy, map, *sen, mask_gc, 0, 0,
547 		attrs.width, attrs.height, 0, 0);
548 
549 	XFreeGC(gui.dpy, back_gc);
550 	XFreeGC(gui.dpy, mask_gc);
551 	XFreePixmap(gui.dpy, map);
552     }
553     else
554 	*sen = 0;
555 
556     XpmFreeAttributes(&attrs);
557 }
558 
559     void
gui_mch_set_toolbar_pos(int x,int y,int w,int h)560 gui_mch_set_toolbar_pos(
561     int	    x,
562     int	    y,
563     int	    w,
564     int	    h)
565 {
566     Dimension	border;
567     int		height;
568 
569     if (!XtIsManaged(toolBar))	// nothing to do
570 	return;
571     XtUnmanageChild(toolBar);
572     XtVaGetValues(toolBar,
573 		XtNborderWidth, &border,
574 		NULL);
575     height = h - 2 * border;
576     if (height < 0)
577 	height = 1;
578     XtVaSetValues(toolBar,
579 		  XtNhorizDistance, x,
580 		  XtNvertDistance, y,
581 		  XtNwidth, w - 2 * border,
582 		  XtNheight,	height,
583 		  NULL);
584     XtManageChild(toolBar);
585 }
586 #endif
587 
588     void
gui_mch_set_text_area_pos(int x,int y,int w,int h)589 gui_mch_set_text_area_pos(
590     int	    x,
591     int	    y,
592     int	    w,
593     int	    h)
594 {
595     XtUnmanageChild(textArea);
596     XtVaSetValues(textArea,
597 		  XtNhorizDistance, x,
598 		  XtNvertDistance, y,
599 		  XtNwidth, w,
600 		  XtNheight, h,
601 		  NULL);
602     XtManageChild(textArea);
603 #ifdef FEAT_TOOLBAR
604     // Give keyboard focus to the textArea instead of the toolbar.
605     gui_mch_reset_focus();
606 #endif
607 }
608 
609 #ifdef FEAT_TOOLBAR
610 /*
611  * A toolbar button has been pushed; now reset the input focus
612  * such that the user can type page up/down etc. and have the
613  * input go to the editor window, not the button
614  */
615     static void
gui_mch_reset_focus(void)616 gui_mch_reset_focus(void)
617 {
618     XtSetKeyboardFocus(vimForm, textArea);
619 }
620 #endif
621 
622 
623     void
gui_x11_set_back_color(void)624 gui_x11_set_back_color(void)
625 {
626     if (textArea != NULL)
627 	XtVaSetValues(textArea,
628 		  XtNbackground, gui.back_pixel,
629 		  NULL);
630 }
631 
632 #if defined(FEAT_MENU) || defined(PROTO)
633 /*
634  * Menu stuff.
635  */
636 
637 static char_u	*make_pull_name(char_u * name);
638 static Widget	get_popup_entry(Widget w);
639 static Widget	submenu_widget(Widget);
640 static Boolean	has_submenu(Widget);
641 static void gui_mch_submenu_change(vimmenu_T *mp, int colors);
642 static void gui_athena_menu_font(Widget id);
643 
644     void
gui_mch_enable_menu(int flag)645 gui_mch_enable_menu(int flag)
646 {
647     if (flag)
648     {
649 	XtManageChild(menuBar);
650 # ifdef FEAT_TOOLBAR
651 	if (XtIsManaged(toolBar))
652 	{
653 	    XtVaSetValues(toolBar,
654 		XtNvertDistance,    gui.menu_height,
655 		NULL);
656 	    XtVaSetValues(textArea,
657 		XtNvertDistance,    gui.menu_height + gui.toolbar_height,
658 		NULL);
659 	}
660 # endif
661     }
662     else
663     {
664 	XtUnmanageChild(menuBar);
665 # ifdef FEAT_TOOLBAR
666 	if (XtIsManaged(toolBar))
667 	{
668 	    XtVaSetValues(toolBar,
669 		XtNvertDistance,    0,
670 		NULL);
671 	}
672 # endif
673     }
674 }
675 
676     void
gui_mch_set_menu_pos(int x,int y,int w,int h)677 gui_mch_set_menu_pos(
678     int	    x,
679     int	    y,
680     int	    w,
681     int	    h)
682 {
683     Dimension	border;
684     int		height;
685 
686     XtUnmanageChild(menuBar);
687     XtVaGetValues(menuBar, XtNborderWidth, &border, NULL);
688     // avoid trouble when there are no menu items, and h is 1
689     height = h - 2 * border;
690     if (height < 0)
691 	height = 1;
692     XtVaSetValues(menuBar,
693 		XtNhorizDistance, x,
694 		XtNvertDistance, y,
695 		XtNwidth, w - 2 * border,
696 		XtNheight, height,
697 		NULL);
698     XtManageChild(menuBar);
699 }
700 
701 /*
702  * Used to calculate the insertion position of a widget with respect to its
703  * neighbors.
704  *
705  * Valid range of return values is: 0 (beginning of children) to
706  *				    numChildren (end of children).
707  */
708     static Cardinal
athena_calculate_ins_pos(Widget widget)709 athena_calculate_ins_pos(Widget widget)
710 {
711     // Assume that if the parent of the vimmenu_T is NULL, then we can get
712     // to this menu by traversing "next", starting at "root_menu".
713     //
714     // This holds true for popup menus, toolbar, and toplevel menu items.
715 
716     // Popup menus:  "id" is NULL. Only submenu_id is valid
717 
718     // Menus that are not toplevel: "parent" will be non-NULL, "id" &
719     // "submenu_id" will be non-NULL.
720 
721     // Toplevel menus: "parent" is NULL, id is the widget of the menu item
722 
723     WidgetList	children;
724     Cardinal	num_children = 0;
725     int		retval;
726     Arg		args[2];
727     int		n = 0;
728     int		i;
729 
730     XtSetArg(args[n], XtNchildren, &children); n++;
731     XtSetArg(args[n], XtNnumChildren, &num_children); n++;
732     XtGetValues(XtParent(widget), args, n);
733 
734     retval = num_children;
735     for (i = 0; i < (int)num_children; ++i)
736     {
737 	Widget	current = children[i];
738 	vimmenu_T	*menu = NULL;
739 
740 	for (menu = (a_cur_menu->parent == NULL)
741 			       ? root_menu : a_cur_menu->parent->children;
742 			       menu != NULL;
743 			       menu = menu->next)
744 	    if (current == menu->id
745 		    && a_cur_menu->priority < menu->priority
746 		    && i < retval)
747 		retval = i;
748     }
749     return retval;
750 }
751 
752     void
gui_mch_add_menu(vimmenu_T * menu,int idx UNUSED)753 gui_mch_add_menu(vimmenu_T *menu, int idx UNUSED)
754 {
755     char_u	*pullright_name;
756     Dimension	height, space, border;
757     vimmenu_T	*parent = menu->parent;
758 
759     a_cur_menu = menu;
760     if (parent == NULL)
761     {
762 	if (menu_is_popup(menu->dname))
763 	{
764 	    menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
765 		simpleMenuWidgetClass,	vimShell,
766 		XtNinsertPosition,	athena_calculate_ins_pos,
767 		XtNtranslations,	popupTrans,
768 		NULL);
769 	    gui_athena_menu_colors(menu->submenu_id);
770 	}
771 	else if (menu_is_menubar(menu->dname))
772 	{
773 	    menu->id = XtVaCreateManagedWidget((char *)menu->dname,
774 		menuButtonWidgetClass, menuBar,
775 		XtNmenuName,	    menu->dname,
776 #ifdef FONTSET_ALWAYS
777 		XtNinternational,   True,
778 #endif
779 		NULL);
780 	    if (menu->id == (Widget)0)
781 		return;
782 	    gui_athena_menu_colors(menu->id);
783 	    gui_athena_menu_font(menu->id);
784 
785 	    menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
786 		simpleMenuWidgetClass, menu->id,
787 		XtNinsertPosition,	athena_calculate_ins_pos,
788 		XtNtranslations,	supermenuTrans,
789 		NULL);
790 	    gui_athena_menu_colors(menu->submenu_id);
791 	    gui_athena_menu_font(menu->submenu_id);
792 
793 	    // Don't update the menu height when it was set at a fixed value
794 	    if (!gui.menu_height_fixed)
795 	    {
796 		/*
797 		 * When we add a top-level item to the menu bar, we can figure
798 		 * out how high the menu bar should be.
799 		 */
800 		XtVaGetValues(menuBar,
801 			XtNvSpace,	&space,
802 			XtNborderWidth, &border,
803 			NULL);
804 		XtVaGetValues(menu->id,
805 			XtNheight,	&height,
806 			NULL);
807 		gui.menu_height = height + 2 * (space + border);
808 	    }
809 	}
810     }
811     else if (parent->submenu_id != (Widget)0)
812     {
813 	menu->id = XtVaCreateManagedWidget((char *)menu->dname,
814 	    smeBSBObjectClass, parent->submenu_id,
815 	    XtNlabel, menu->dname,
816 #ifdef FONTSET_ALWAYS
817 	    XtNinternational,	True,
818 #endif
819 	    NULL);
820 	if (menu->id == (Widget)0)
821 	    return;
822 	if (pullerBitmap == None)
823 	    pullerBitmap = gui_athena_create_pullright_pixmap(menu->id);
824 
825 	XtVaSetValues(menu->id, XtNrightBitmap, pullerBitmap,
826 				NULL);
827 	// If there are other menu items that are not pulldown menus,
828 	// we need to adjust the right margins of those, too.
829 	{
830 	    WidgetList	children;
831 	    Cardinal	num_children;
832 	    int		i;
833 
834 	    XtVaGetValues(parent->submenu_id, XtNchildren, &children,
835 					      XtNnumChildren, &num_children,
836 					      NULL);
837 	    for (i = 0; i < (int)num_children; ++i)
838 	    {
839 		XtVaSetValues(children[i],
840 			      XtNrightMargin, puller_width,
841 			      NULL);
842 	    }
843 	}
844 	gui_athena_menu_colors(menu->id);
845 	gui_athena_menu_font(menu->id);
846 
847 	pullright_name = make_pull_name(menu->dname);
848 	menu->submenu_id = XtVaCreatePopupShell((char *)pullright_name,
849 	    simpleMenuWidgetClass, parent->submenu_id,
850 	    XtNtranslations, menuTrans,
851 	    NULL);
852 	gui_athena_menu_colors(menu->submenu_id);
853 	gui_athena_menu_font(menu->submenu_id);
854 	vim_free(pullright_name);
855 	XtAddCallback(menu->submenu_id, XtNpopupCallback,
856 		      gui_athena_popup_callback, (XtPointer)menu);
857 
858 	if (parent->parent != NULL)
859 	    XtOverrideTranslations(parent->submenu_id, parentTrans);
860     }
861     a_cur_menu = NULL;
862 }
863 
864 // Used to determine whether a SimpleMenu has pulldown entries.
865 //
866 // "id" is the parent of the menu items.
867 // Ignore widget "ignore" in the pane.
868     static Boolean
gui_athena_menu_has_submenus(Widget id,Widget ignore)869 gui_athena_menu_has_submenus(Widget id, Widget ignore)
870 {
871     WidgetList	children;
872     Cardinal	num_children;
873     int		i;
874 
875     XtVaGetValues(id, XtNchildren, &children,
876 		      XtNnumChildren, &num_children,
877 		      NULL);
878     for (i = 0; i < (int)num_children; ++i)
879     {
880 	if (children[i] == ignore)
881 	    continue;
882 	if (has_submenu(children[i]))
883 	    return True;
884     }
885     return False;
886 }
887 
888     static void
gui_athena_menu_font(Widget id)889 gui_athena_menu_font(Widget id)
890 {
891 #ifdef FONTSET_ALWAYS
892     if (gui.menu_fontset != NOFONTSET)
893     {
894 	if (XtIsManaged(id))
895 	{
896 	    XtUnmanageChild(id);
897 	    XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
898 	    // We should force the widget to recalculate its
899 	    // geometry now.
900 	    XtManageChild(id);
901 	}
902 	else
903 	    XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
904 	if (has_submenu(id))
905 	    XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
906     }
907 #else
908     int		managed = FALSE;
909 
910     if (gui.menu_font != NOFONT)
911     {
912 	if (XtIsManaged(id))
913 	{
914 	    XtUnmanageChild(id);
915 	    managed = TRUE;
916 	}
917 
918 # ifdef FEAT_XFONTSET
919 	if (gui.fontset != NOFONTSET)
920 	    XtVaSetValues(id, XtNfontSet, gui.menu_font, NULL);
921 	else
922 # endif
923 	    XtVaSetValues(id, XtNfont, gui.menu_font, NULL);
924 	if (has_submenu(id))
925 	    XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
926 
927 	// Force the widget to recalculate its geometry now.
928 	if (managed)
929 	    XtManageChild(id);
930     }
931 #endif
932 }
933 
934 
935     void
gui_mch_new_menu_font(void)936 gui_mch_new_menu_font(void)
937 {
938     Pixmap oldpuller = None;
939 
940     if (menuBar == (Widget)0)
941 	return;
942 
943     if (pullerBitmap != None)
944     {
945 	oldpuller = pullerBitmap;
946 	pullerBitmap = gui_athena_create_pullright_pixmap(NULL);
947     }
948     gui_mch_submenu_change(root_menu, FALSE);
949 
950     {
951 	// Iterate through the menubar menu items and get the height of
952 	// each one.  The menu bar height is set to the maximum of all
953 	// the heights.
954 	vimmenu_T *mp;
955 	int max_height = 9999;
956 
957 	FOR_ALL_MENUS(mp)
958 	{
959 	    if (menu_is_menubar(mp->dname))
960 	    {
961 		Dimension height;
962 
963 		XtVaGetValues(mp->id,
964 			XtNheight, &height,
965 			NULL);
966 		if (height < max_height)
967 		    max_height = height;
968 	    }
969 	}
970 	if (max_height != 9999)
971 	{
972 	    // Don't update the menu height when it was set at a fixed value
973 	    if (!gui.menu_height_fixed)
974 	    {
975 		Dimension   space, border;
976 
977 		XtVaGetValues(menuBar,
978 			XtNvSpace,	&space,
979 			XtNborderWidth, &border,
980 			NULL);
981 		gui.menu_height = max_height + 2 * (space + border);
982 	    }
983 	}
984     }
985     // Now, to simulate the window being resized.  Only, this
986     // will resize the window to its current state.
987     //
988     // There has to be a better way, but I do not see one at this time.
989     // (David Harrison)
990     {
991 	Position w, h;
992 
993 	XtVaGetValues(vimShell,
994 		XtNwidth, &w,
995 		XtNheight, &h,
996 		NULL);
997 	gui_resize_shell(w, h
998 #ifdef FEAT_XIM
999 						- xim_get_status_area_height()
1000 #endif
1001 		     );
1002     }
1003     gui_set_shellsize(FALSE, TRUE, RESIZE_VERT);
1004     ui_new_shellsize();
1005     if (oldpuller != None)
1006 	XFreePixmap(gui.dpy, oldpuller);
1007 }
1008 
1009 #if defined(FEAT_BEVAL_GUI) || defined(PROTO)
1010     void
gui_mch_new_tooltip_font(void)1011 gui_mch_new_tooltip_font(void)
1012 {
1013 #  ifdef FEAT_TOOLBAR
1014     vimmenu_T   *menu;
1015 
1016     if (toolBar == (Widget)0)
1017 	return;
1018 
1019     menu = gui_find_menu((char_u *)"ToolBar");
1020     if (menu != NULL)
1021 	gui_mch_submenu_change(menu, FALSE);
1022 #  endif
1023 }
1024 
1025     void
gui_mch_new_tooltip_colors(void)1026 gui_mch_new_tooltip_colors(void)
1027 {
1028 # ifdef FEAT_TOOLBAR
1029     vimmenu_T   *menu;
1030 
1031     if (toolBar == (Widget)0)
1032 	return;
1033 
1034     menu = gui_find_menu((char_u *)"ToolBar");
1035     if (menu != NULL)
1036 	gui_mch_submenu_change(menu, TRUE);
1037 # endif
1038 }
1039 #endif
1040 
1041     static void
gui_mch_submenu_change(vimmenu_T * menu,int colors)1042 gui_mch_submenu_change(
1043     vimmenu_T	*menu,
1044     int		colors)		// TRUE for colors, FALSE for font
1045 {
1046     vimmenu_T	*mp;
1047 
1048     for (mp = menu; mp != NULL; mp = mp->next)
1049     {
1050 	if (mp->id != (Widget)0)
1051 	{
1052 	    if (colors)
1053 	    {
1054 		gui_athena_menu_colors(mp->id);
1055 #ifdef FEAT_TOOLBAR
1056 		// For a toolbar item: Free the pixmap and allocate a new one,
1057 		// so that the background color is right.
1058 		if (mp->image != (Pixmap)0)
1059 		{
1060 		    XFreePixmap(gui.dpy, mp->image);
1061 		    get_toolbar_pixmap(mp, &mp->image);
1062 		    if (mp->image != (Pixmap)0)
1063 			XtVaSetValues(mp->id, XtNbitmap, mp->image, NULL);
1064 		}
1065 
1066 # ifdef FEAT_BEVAL_GUI
1067 		// If we have a tooltip, then we need to change its colors
1068 		if (mp->tip != NULL)
1069 		{
1070 		    Arg args[2];
1071 
1072 		    args[0].name = XtNbackground;
1073 		    args[0].value = gui.tooltip_bg_pixel;
1074 		    args[1].name = XtNforeground;
1075 		    args[1].value = gui.tooltip_fg_pixel;
1076 		    XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1077 		}
1078 # endif
1079 #endif
1080 	    }
1081 	    else
1082 	    {
1083 		gui_athena_menu_font(mp->id);
1084 #ifdef FEAT_BEVAL_GUI
1085 		// If we have a tooltip, then we need to change its font
1086 		// Assume XtNinternational == True (in createBalloonEvalWindow)
1087 		if (mp->tip != NULL)
1088 		{
1089 		    Arg args[1];
1090 
1091 		    args[0].name = XtNfontSet;
1092 		    args[0].value = (XtArgVal)gui.tooltip_fontset;
1093 		    XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1094 		}
1095 #endif
1096 	    }
1097 	}
1098 
1099 	if (mp->children != NULL)
1100 	{
1101 	    // Set the colors/font for the tear off widget
1102 	    if (mp->submenu_id != (Widget)0)
1103 	    {
1104 		if (colors)
1105 		    gui_athena_menu_colors(mp->submenu_id);
1106 		else
1107 		    gui_athena_menu_font(mp->submenu_id);
1108 	    }
1109 	    // Set the colors for the children
1110 	    gui_mch_submenu_change(mp->children, colors);
1111 	}
1112     }
1113 }
1114 
1115 /*
1116  * Make a submenu name into a pullright name.
1117  * Replace '.' by '_', can't include '.' in the submenu name.
1118  */
1119     static char_u *
make_pull_name(char_u * name)1120 make_pull_name(char_u * name)
1121 {
1122     char_u  *pname;
1123     char_u  *p;
1124 
1125     pname = vim_strnsave(name, STRLEN(name) + strlen("-pullright"));
1126     if (pname != NULL)
1127     {
1128 	strcat((char *)pname, "-pullright");
1129 	while ((p = vim_strchr(pname, '.')) != NULL)
1130 	    *p = '_';
1131     }
1132     return pname;
1133 }
1134 
1135     void
gui_mch_add_menu_item(vimmenu_T * menu,int idx UNUSED)1136 gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED)
1137 {
1138     vimmenu_T	*parent = menu->parent;
1139 
1140     a_cur_menu = menu;
1141 # ifdef FEAT_TOOLBAR
1142     if (menu_is_toolbar(parent->name))
1143     {
1144 	WidgetClass	type;
1145 	int		n;
1146 	Arg		args[21];
1147 
1148 	n = 0;
1149 	if (menu_is_separator(menu->name))
1150 	{
1151 	    XtSetArg(args[n], XtNlabel, ""); n++;
1152 	    XtSetArg(args[n], XtNborderWidth, 0); n++;
1153 	}
1154 	else
1155 	{
1156 	    get_toolbar_pixmap(menu, &menu->image);
1157 	    XtSetArg(args[n], XtNlabel, menu->dname); n++;
1158 	    XtSetArg(args[n], XtNinternalHeight, 1); n++;
1159 	    XtSetArg(args[n], XtNinternalWidth, 1); n++;
1160 	    XtSetArg(args[n], XtNborderWidth, 1); n++;
1161 	    if (menu->image != 0)
1162 		XtSetArg(args[n], XtNbitmap, menu->image); n++;
1163 	}
1164 	XtSetArg(args[n], XtNhighlightThickness, 0); n++;
1165 	type = commandWidgetClass;
1166 	// TODO: figure out the position in the toolbar?
1167 	//       This currently works fine for the default toolbar, but
1168 	//       what if we add/remove items during later runtime?
1169 
1170 	// NOTE: "idx" isn't used here.  The position is calculated by
1171 	//       athena_calculate_ins_pos().  The position it calculates
1172 	//       should be equal to "idx".
1173 	// TODO: Could we just store "idx" and use that as the child
1174 	// placement?
1175 
1176 	if (menu->id == NULL)
1177 	{
1178 	    menu->id = XtCreateManagedWidget((char *)menu->dname,
1179 			type, toolBar, args, n);
1180 	    XtAddCallback(menu->id,
1181 		    XtNcallback, gui_x11_menu_cb, menu);
1182 	}
1183 	else
1184 	    XtSetValues(menu->id, args, n);
1185 	gui_athena_menu_colors(menu->id);
1186 
1187 #ifdef FEAT_BEVAL_GUI
1188 	gui_mch_menu_set_tip(menu);
1189 #endif
1190 
1191 	menu->parent = parent;
1192 	menu->submenu_id = NULL;
1193 	if (!XtIsManaged(toolBar)
1194 		    && vim_strchr(p_go, GO_TOOLBAR) != NULL)
1195 	    gui_mch_show_toolbar(TRUE);
1196 	gui.toolbar_height = gui_mch_compute_toolbar_height();
1197 	return;
1198     } // toolbar menu item
1199 # endif
1200 
1201     // Add menu separator
1202     if (menu_is_separator(menu->name))
1203     {
1204 	menu->submenu_id = (Widget)0;
1205 	menu->id = XtVaCreateManagedWidget((char *)menu->dname,
1206 		smeLineObjectClass, parent->submenu_id,
1207 		NULL);
1208 	if (menu->id == (Widget)0)
1209 	    return;
1210 	gui_athena_menu_colors(menu->id);
1211     }
1212     else
1213     {
1214 	if (parent != NULL && parent->submenu_id != (Widget)0)
1215 	{
1216 	    menu->submenu_id = (Widget)0;
1217 	    menu->id = XtVaCreateManagedWidget((char *)menu->dname,
1218 		    smeBSBObjectClass, parent->submenu_id,
1219 		    XtNlabel, menu->dname,
1220 #ifdef FONTSET_ALWAYS
1221 		    XtNinternational,	True,
1222 #endif
1223 		    NULL);
1224 	    if (menu->id == (Widget)0)
1225 		return;
1226 
1227 	    // If there are other "pulldown" items in this pane, then adjust
1228 	    // the right margin to accommodate the arrow pixmap, otherwise
1229 	    // the right margin will be the same as the left margin.
1230 	    {
1231 		Dimension   left_margin;
1232 
1233 		XtVaGetValues(menu->id, XtNleftMargin, &left_margin, NULL);
1234 		XtVaSetValues(menu->id, XtNrightMargin,
1235 			gui_athena_menu_has_submenus(parent->submenu_id, NULL) ?
1236 			    puller_width :
1237 			    left_margin,
1238 			NULL);
1239 	    }
1240 
1241 	    gui_athena_menu_colors(menu->id);
1242 	    gui_athena_menu_font(menu->id);
1243 	    XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
1244 		    (XtPointer)menu);
1245 	}
1246     }
1247     a_cur_menu = NULL;
1248 }
1249 
1250 #if defined(FEAT_TOOLBAR) || defined(PROTO)
1251     void
gui_mch_show_toolbar(int showit)1252 gui_mch_show_toolbar(int showit)
1253 {
1254     Cardinal	numChildren;	    // how many children toolBar has
1255 
1256     if (toolBar == (Widget)0)
1257 	return;
1258     XtVaGetValues(toolBar, XtNnumChildren, &numChildren, NULL);
1259     if (showit && numChildren > 0)
1260     {
1261 	// Assume that we want to show the toolbar if p_toolbar contains valid
1262 	// option settings, therefore p_toolbar must not be NULL.
1263 	WidgetList  children;
1264 
1265 	XtVaGetValues(toolBar, XtNchildren, &children, NULL);
1266 	{
1267 	    void    (*action)(BalloonEval *);
1268 	    int	    text = 0;
1269 
1270 	    if (strstr((const char *)p_toolbar, "tooltips"))
1271 		action = &gui_mch_enable_beval_area;
1272 	    else
1273 		action = &gui_mch_disable_beval_area;
1274 	    if (strstr((const char *)p_toolbar, "text"))
1275 		text = 1;
1276 	    else if (strstr((const char *)p_toolbar, "icons"))
1277 		text = -1;
1278 	    if (text != 0)
1279 	    {
1280 		vimmenu_T   *toolbar;
1281 		vimmenu_T   *cur;
1282 
1283 		FOR_ALL_MENUS(toolbar)
1284 		    if (menu_is_toolbar(toolbar->dname))
1285 			break;
1286 		// Assumption: toolbar is NULL if there is no toolbar,
1287 		//	       otherwise it contains the toolbar menu structure.
1288 		//
1289 		// Assumption: "numChildren" == the number of items in the list
1290 		//	       of items beginning with toolbar->children.
1291 		if (toolbar)
1292 		{
1293 		    for (cur = toolbar->children; cur; cur = cur->next)
1294 		    {
1295 			Arg	    args[2];
1296 			int	    n = 0;
1297 
1298 			// Enable/Disable tooltip (OK to enable while currently
1299 			// enabled)
1300 			if (cur->tip != NULL)
1301 			    (*action)(cur->tip);
1302 			if (text == 1)
1303 			{
1304 			    XtSetArg(args[n], XtNbitmap, None);
1305 			    n++;
1306 			    XtSetArg(args[n], XtNlabel,
1307 				    menu_is_separator(cur->name) ? "" :
1308 					(char *)cur->dname);
1309 			    n++;
1310 			}
1311 			else
1312 			{
1313 			    XtSetArg(args[n], XtNbitmap, cur->image);
1314 			    n++;
1315 			    XtSetArg(args[n], XtNlabel, (cur->image == None) ?
1316 				    menu_is_separator(cur->name) ?
1317 					"" :
1318 					(char *)cur->dname
1319 				    :
1320 				    (char *)None);
1321 			    n++;
1322 			}
1323 			if (cur->id != NULL)
1324 			{
1325 			    XtUnmanageChild(cur->id);
1326 			    XtSetValues(cur->id, args, n);
1327 			    XtManageChild(cur->id);
1328 			}
1329 		    }
1330 		}
1331 	    }
1332 	}
1333 	gui.toolbar_height = gui_mch_compute_toolbar_height();
1334 	XtManageChild(toolBar);
1335 	if (XtIsManaged(menuBar))
1336 	{
1337 	    XtVaSetValues(textArea,
1338 		    XtNvertDistance,    gui.toolbar_height + gui.menu_height,
1339 		    NULL);
1340 	    XtVaSetValues(toolBar,
1341 		    XtNvertDistance,    gui.menu_height,
1342 		    NULL);
1343 	}
1344 	else
1345 	{
1346 	    XtVaSetValues(textArea,
1347 		    XtNvertDistance,    gui.toolbar_height,
1348 		    NULL);
1349 	    XtVaSetValues(toolBar,
1350 		    XtNvertDistance,    0,
1351 		    NULL);
1352 	}
1353     }
1354     else
1355     {
1356 	gui.toolbar_height = 0;
1357 	if (XtIsManaged(menuBar))
1358 	    XtVaSetValues(textArea,
1359 		XtNvertDistance,    gui.menu_height,
1360 		NULL);
1361 	else
1362 	    XtVaSetValues(textArea,
1363 		XtNvertDistance,    0,
1364 		NULL);
1365 
1366 	XtUnmanageChild(toolBar);
1367     }
1368     gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
1369 }
1370 
1371 
1372     int
gui_mch_compute_toolbar_height(void)1373 gui_mch_compute_toolbar_height(void)
1374 {
1375     Dimension	height;		    // total Toolbar height
1376     Dimension	whgt;		    // height of each widget
1377     Dimension	marginHeight;	    // XmNmarginHeight of toolBar
1378     Dimension	shadowThickness;    // thickness of Xtparent(toolBar)
1379     WidgetList	children;	    // list of toolBar's children
1380     Cardinal	numChildren;	    // how many children toolBar has
1381     int		i;
1382 
1383     height = 0;
1384     shadowThickness = 0;
1385     marginHeight = 0;
1386     if (toolBar != (Widget)0)
1387     {
1388 	XtVaGetValues(toolBar,
1389 		XtNborderWidth,	    &shadowThickness,
1390 		XtNvSpace,	    &marginHeight,
1391 		XtNchildren,	    &children,
1392 		XtNnumChildren,	    &numChildren,
1393 		NULL);
1394 	for (i = 0; i < (int)numChildren; i++)
1395 	{
1396 	    whgt = 0;
1397 
1398 	    XtVaGetValues(children[i], XtNheight, &whgt, NULL);
1399 	    if (height < whgt)
1400 		height = whgt;
1401 	}
1402     }
1403 
1404     return (int)(height + (marginHeight << 1) + (shadowThickness << 1));
1405 }
1406 
1407     void
gui_mch_get_toolbar_colors(Pixel * bgp,Pixel * fgp,Pixel * bsp,Pixel * tsp,Pixel * hsp)1408 gui_mch_get_toolbar_colors(
1409     Pixel	*bgp,
1410     Pixel	*fgp,
1411     Pixel       *bsp,
1412     Pixel	*tsp,
1413     Pixel	*hsp)
1414 {
1415     XtVaGetValues(toolBar, XtNbackground, bgp, XtNborderColor, fgp, NULL);
1416     *bsp = *bgp;
1417     *tsp = *fgp;
1418     *hsp = *tsp;
1419 }
1420 #endif
1421 
1422 
1423     void
gui_mch_toggle_tearoffs(int enable UNUSED)1424 gui_mch_toggle_tearoffs(int enable UNUSED)
1425 {
1426     // no tearoff menus
1427 }
1428 
1429     void
gui_mch_new_menu_colors(void)1430 gui_mch_new_menu_colors(void)
1431 {
1432     if (menuBar == (Widget)0)
1433 	return;
1434     if (gui.menu_fg_pixel != INVALCOLOR)
1435 	XtVaSetValues(menuBar, XtNborderColor,	gui.menu_fg_pixel, NULL);
1436     gui_athena_menu_colors(menuBar);
1437 #ifdef FEAT_TOOLBAR
1438     gui_athena_menu_colors(toolBar);
1439 #endif
1440 
1441     gui_mch_submenu_change(root_menu, TRUE);
1442 }
1443 
1444 /*
1445  * Destroy the machine specific menu widget.
1446  */
1447     void
gui_mch_destroy_menu(vimmenu_T * menu)1448 gui_mch_destroy_menu(vimmenu_T *menu)
1449 {
1450     Widget	parent;
1451 
1452     // There is no item for the toolbar.
1453     if (menu->id == (Widget)0)
1454 	return;
1455 
1456     parent = XtParent(menu->id);
1457 
1458     // When removing the last "pulldown" menu item from a pane, adjust the
1459     // right margins of the remaining widgets.
1460     if (menu->submenu_id != (Widget)0)
1461     {
1462 	// Go through the menu items in the parent of this item and
1463 	// adjust their margins, if necessary.
1464 	// This takes care of the case when we delete the last menu item in a
1465 	// pane that has a submenu.  In this case, there will be no arrow
1466 	// pixmaps shown anymore.
1467 	{
1468 	    WidgetList  children;
1469 	    Cardinal    num_children;
1470 	    int		i;
1471 	    Dimension	right_margin = 0;
1472 	    Boolean	get_left_margin = False;
1473 
1474 	    XtVaGetValues(parent, XtNchildren, &children,
1475 				  XtNnumChildren, &num_children,
1476 				  NULL);
1477 	    if (gui_athena_menu_has_submenus(parent, menu->id))
1478 		right_margin = puller_width;
1479 	    else
1480 		get_left_margin = True;
1481 
1482 	    for (i = 0; i < (int)num_children; ++i)
1483 	    {
1484 		if (children[i] == menu->id)
1485 		    continue;
1486 		if (get_left_margin == True)
1487 		{
1488 		    Dimension left_margin;
1489 
1490 		    XtVaGetValues(children[i], XtNleftMargin, &left_margin,
1491 				  NULL);
1492 		    XtVaSetValues(children[i], XtNrightMargin, left_margin,
1493 				  NULL);
1494 		}
1495 		else
1496 		    XtVaSetValues(children[i], XtNrightMargin, right_margin,
1497 				  NULL);
1498 	    }
1499 	}
1500     }
1501     // Please be sure to destroy the parent widget first (i.e. menu->id).
1502     //
1503     // This code should be basically identical to that in the file gui_motif.c
1504     // because they are both Xt based.
1505     if (menu->id != (Widget)0)
1506     {
1507 	Cardinal    num_children;
1508 	Dimension   height, space, border;
1509 
1510 	XtVaGetValues(menuBar,
1511 		XtNvSpace,	&space,
1512 		XtNborderWidth, &border,
1513 		NULL);
1514 	XtVaGetValues(menu->id,
1515 		XtNheight,	&height,
1516 		NULL);
1517 #if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL_GUI)
1518 	if (parent == toolBar && menu->tip != NULL)
1519 	{
1520 	    // We try to destroy this before the actual menu, because there are
1521 	    // callbacks, etc. that will be unregistered during the tooltip
1522 	    // destruction.
1523 	    //
1524 	    // If you call "gui_mch_destroy_beval_area()" after destroying
1525 	    // menu->id, then the tooltip's window will have already been
1526 	    // deallocated by Xt, and unknown behaviour will ensue (probably
1527 	    // a core dump).
1528 	    gui_mch_destroy_beval_area(menu->tip);
1529 	    menu->tip = NULL;
1530 	}
1531 #endif
1532 	/*
1533 	 * This is a hack to stop the Athena simpleMenuWidget from getting a
1534 	 * BadValue error when a menu's last child is destroyed. We check to
1535 	 * see if this is the last child and if so, don't delete it. The parent
1536 	 * will be deleted soon anyway, and it will delete its children like
1537 	 * all good widgets do.
1538 	 */
1539 	// NOTE: The cause of the BadValue X Protocol Error is because when the
1540 	// last child is destroyed, it is first unmanaged, thus causing a
1541 	// geometry resize request from the parent Shell widget.
1542 	// Since the Shell widget has no more children, it is resized to have
1543 	// width/height of 0.  XConfigureWindow() is then called with the
1544 	// width/height of 0, which generates the BadValue.
1545 	//
1546 	// This happens in phase two of the widget destruction process.
1547 	{
1548 	    if (parent != menuBar
1549 #ifdef FEAT_TOOLBAR
1550 		    && parent != toolBar
1551 #endif
1552 		    )
1553 	    {
1554 		XtVaGetValues(parent, XtNnumChildren, &num_children, NULL);
1555 		if (num_children > 1)
1556 		    XtDestroyWidget(menu->id);
1557 	    }
1558 	    else
1559 		XtDestroyWidget(menu->id);
1560 	    menu->id = (Widget)0;
1561 	}
1562 
1563 	if (parent == menuBar)
1564 	{
1565 	    if (!gui.menu_height_fixed)
1566 		gui.menu_height = height + 2 * (space + border);
1567 	}
1568 #ifdef FEAT_TOOLBAR
1569 	else if (parent == toolBar)
1570 	{
1571 	    // When removing last toolbar item, don't display the toolbar.
1572 	    XtVaGetValues(toolBar, XtNnumChildren, &num_children, NULL);
1573 	    if (num_children == 0)
1574 		gui_mch_show_toolbar(FALSE);
1575 	    else
1576 		gui.toolbar_height = gui_mch_compute_toolbar_height();
1577 	}
1578 #endif
1579     }
1580     if (menu->submenu_id != (Widget)0)
1581     {
1582 	XtDestroyWidget(menu->submenu_id);
1583 	menu->submenu_id = (Widget)0;
1584     }
1585 }
1586 
1587     static void
gui_athena_menu_timeout(XtPointer client_data,XtIntervalId * id UNUSED)1588 gui_athena_menu_timeout(
1589     XtPointer	    client_data,
1590     XtIntervalId    *id UNUSED)
1591 {
1592     Widget  w = (Widget)client_data;
1593     Widget  popup;
1594 
1595     timer = 0;
1596     if (XtIsSubclass(w,smeBSBObjectClass))
1597     {
1598 	Pixmap p;
1599 
1600 	XtVaGetValues(w, XtNrightBitmap, &p, NULL);
1601 	if ((p != None) && (p != XtUnspecifiedPixmap))
1602 	{
1603 	    // We are dealing with an item that has a submenu
1604 	    popup = get_popup_entry(XtParent(w));
1605 	    if (popup == (Widget)0)
1606 		return;
1607 	    XtPopup(popup, XtGrabNonexclusive);
1608 	}
1609     }
1610 }
1611 
1612 /*
1613  * This routine is used to calculate the position (in screen coordinates)
1614  * where a submenu should appear relative to the menu entry that popped it
1615  * up.  It should appear even with and just slightly to the left of the
1616  * rightmost end of the menu entry that caused the popup.
1617  *
1618  * This is called when XtPopup() is called.
1619  */
1620     static void
gui_athena_popup_callback(Widget w,XtPointer client_data,XtPointer call_data UNUSED)1621 gui_athena_popup_callback(
1622     Widget	w,
1623     XtPointer	client_data,
1624     XtPointer	call_data UNUSED)
1625 {
1626     // Assumption: XtIsSubclass(XtParent(w),simpleMenuWidgetClass)
1627     vimmenu_T	*menu = (vimmenu_T *)client_data;
1628     Dimension	width;
1629     Position	root_x, root_y;
1630 
1631     // First, popdown any siblings that may have menus popped up
1632     {
1633 	vimmenu_T *i;
1634 
1635 	FOR_ALL_CHILD_MENUS(menu->parent, i)
1636 	{
1637 	    if (i->submenu_id != NULL && XtIsManaged(i->submenu_id))
1638 		XtPopdown(i->submenu_id);
1639 	}
1640     }
1641     XtVaGetValues(XtParent(w),
1642 		  XtNwidth,   &width,
1643 		  NULL);
1644     // Assumption: XawSimpleMenuGetActiveEntry(XtParent(w)) == menu->id
1645     // i.e. This IS the active entry
1646     XtTranslateCoords(menu->id,width - 5, 0, &root_x, &root_y);
1647     XtVaSetValues(w, XtNx, root_x,
1648 		     XtNy, root_y,
1649 		     NULL);
1650 }
1651 
1652     static void
gui_athena_popdown_submenus_action(Widget w,XEvent * event,String * args,Cardinal * nargs)1653 gui_athena_popdown_submenus_action(
1654     Widget	w,
1655     XEvent	*event,
1656     String	*args,
1657     Cardinal	*nargs)
1658 {
1659     WidgetList	children;
1660     Cardinal	num_children;
1661 
1662     XtVaGetValues(w, XtNchildren, &children,
1663 		     XtNnumChildren, &num_children,
1664 		     NULL);
1665     for (; num_children > 0; --num_children)
1666     {
1667 	Widget child = children[num_children - 1];
1668 
1669 	if (has_submenu(child))
1670 	{
1671 	    Widget temp_w;
1672 
1673 	    temp_w = submenu_widget(child);
1674 	    gui_athena_popdown_submenus_action(temp_w,event,args,nargs);
1675 	    XtPopdown(temp_w);
1676 	}
1677     }
1678 }
1679 
1680 /*
1681  * Used to determine if the given widget has a submenu that can be popped up.
1682  */
1683     static Boolean
has_submenu(Widget widget)1684 has_submenu(Widget widget)
1685 {
1686     if ((widget != NULL) && XtIsSubclass(widget,smeBSBObjectClass))
1687     {
1688 	Pixmap p;
1689 
1690 	XtVaGetValues(widget, XtNrightBitmap, &p, NULL);
1691 	if ((p != None) && (p != XtUnspecifiedPixmap))
1692 	    return True;
1693     }
1694     return False;
1695 }
1696 
1697     static void
gui_athena_delayed_arm_action(Widget w,XEvent * event,String * args,Cardinal * nargs)1698 gui_athena_delayed_arm_action(
1699     Widget	w,
1700     XEvent	*event,
1701     String	*args,
1702     Cardinal	*nargs)
1703 {
1704     Dimension	width, height;
1705 
1706     if (event->type != MotionNotify)
1707 	return;
1708 
1709     XtVaGetValues(w,
1710 	XtNwidth,   &width,
1711 	XtNheight,  &height,
1712 	NULL);
1713 
1714     if (event->xmotion.x >= (int)width || event->xmotion.y >= (int)height)
1715 	return;
1716 
1717     {
1718 	static Widget	    previous_active_widget = NULL;
1719 	Widget		    current;
1720 
1721 	current = XawSimpleMenuGetActiveEntry(w);
1722 	if (current != previous_active_widget)
1723 	{
1724 	    if (timer)
1725 	    {
1726 		// If the timeout hasn't been triggered, remove it
1727 		XtRemoveTimeOut(timer);
1728 	    }
1729 	    gui_athena_popdown_submenus_action(w,event,args,nargs);
1730 	    if (has_submenu(current))
1731 	    {
1732 		XtAppAddTimeOut(XtWidgetToApplicationContext(w), 600L,
1733 				gui_athena_menu_timeout,
1734 				(XtPointer)current);
1735 	    }
1736 	    previous_active_widget = current;
1737 	}
1738     }
1739 }
1740 
1741     static Widget
get_popup_entry(Widget w)1742 get_popup_entry(Widget w)
1743 {
1744     Widget	menuw;
1745 
1746     // Get the active entry for the current menu
1747     if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)0)
1748 	return NULL;
1749 
1750     return submenu_widget(menuw);
1751 }
1752 
1753 /*
1754  * Given the widget that has been determined to have a submenu, return the
1755  * submenu widget that is to be popped up.
1756  */
1757     static Widget
submenu_widget(Widget widget)1758 submenu_widget(Widget widget)
1759 {
1760     // Precondition: has_submenu(widget) == True
1761     //	    XtIsSubclass(XtParent(widget),simpleMenuWidgetClass) == True
1762 
1763     char_u	*pullright_name;
1764     Widget	popup;
1765 
1766     pullright_name = make_pull_name((char_u *)XtName(widget));
1767     popup = XtNameToWidget(XtParent(widget), (char *)pullright_name);
1768     vim_free(pullright_name);
1769 
1770     return popup;
1771     // Postcondition: (popup != NULL) implies
1772     // (XtIsSubclass(popup,simpleMenuWidgetClass) == True)
1773 }
1774 
1775     void
gui_mch_show_popupmenu(vimmenu_T * menu)1776 gui_mch_show_popupmenu(vimmenu_T *menu)
1777 {
1778     int		rootx, rooty, winx, winy;
1779     Window	root, child;
1780     unsigned int mask;
1781 
1782     if (menu->submenu_id == (Widget)0)
1783 	return;
1784 
1785     // Position the popup menu at the pointer
1786     if (XQueryPointer(gui.dpy, XtWindow(vimShell), &root, &child,
1787 		&rootx, &rooty, &winx, &winy, &mask))
1788     {
1789 	rootx -= 30;
1790 	if (rootx < 0)
1791 	    rootx = 0;
1792 	rooty -= 5;
1793 	if (rooty < 0)
1794 	    rooty = 0;
1795 	XtVaSetValues(menu->submenu_id,
1796 		XtNx, rootx,
1797 		XtNy, rooty,
1798 		NULL);
1799     }
1800 
1801     XtOverrideTranslations(menu->submenu_id, popupTrans);
1802     XtPopupSpringLoaded(menu->submenu_id);
1803 }
1804 
1805 #endif // FEAT_MENU
1806 
1807 /*
1808  * Set the menu and scrollbar colors to their default values.
1809  */
1810     void
gui_mch_def_colors(void)1811 gui_mch_def_colors(void)
1812 {
1813     /*
1814      * Get the colors ourselves.  Using the automatic conversion doesn't
1815      * handle looking for approximate colors.
1816      */
1817     if (gui.in_use)
1818     {
1819 	gui.menu_fg_pixel = gui_get_color((char_u *)gui.rsrc_menu_fg_name);
1820 	gui.menu_bg_pixel = gui_get_color((char_u *)gui.rsrc_menu_bg_name);
1821 	gui.scroll_fg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_fg_name);
1822 	gui.scroll_bg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_bg_name);
1823 #ifdef FEAT_BEVAL_GUI
1824 	gui.tooltip_fg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_fg_name);
1825 	gui.tooltip_bg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_bg_name);
1826 #endif
1827     }
1828 }
1829 
1830 
1831 /*
1832  * Scrollbar stuff.
1833  */
1834 
1835     void
gui_mch_set_scrollbar_thumb(scrollbar_T * sb,long val,long size,long max)1836 gui_mch_set_scrollbar_thumb(
1837     scrollbar_T	*sb,
1838     long	val,
1839     long	size,
1840     long	max)
1841 {
1842     double	v, s;
1843 
1844     if (sb->id == (Widget)0)
1845 	return;
1846 
1847     /*
1848      * Athena scrollbar must go from 0.0 to 1.0.
1849      */
1850     if (max == 0)
1851     {
1852 	// So you can't scroll it at all (normally it scrolls past end)
1853 #ifdef FEAT_GUI_NEXTAW
1854 	XawScrollbarSetThumb(sb->id, 0.0, 1.0);
1855 #else
1856 	vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
1857 #endif
1858     }
1859     else
1860     {
1861 	v = (double)val / (double)(max + 1);
1862 	s = (double)size / (double)(max + 1);
1863 #ifdef FEAT_GUI_NEXTAW
1864 	XawScrollbarSetThumb(sb->id, v, s);
1865 #else
1866 	vim_XawScrollbarSetThumb(sb->id, v, s, 1.0);
1867 #endif
1868     }
1869 }
1870 
1871     void
gui_mch_set_scrollbar_pos(scrollbar_T * sb,int x,int y,int w,int h)1872 gui_mch_set_scrollbar_pos(
1873     scrollbar_T *sb,
1874     int		x,
1875     int		y,
1876     int		w,
1877     int		h)
1878 {
1879     if (sb->id == (Widget)0)
1880 	return;
1881 
1882     XtUnmanageChild(sb->id);
1883     XtVaSetValues(sb->id,
1884 		  XtNhorizDistance, x,
1885 		  XtNvertDistance, y,
1886 		  XtNwidth, w,
1887 		  XtNheight, h,
1888 		  NULL);
1889     XtManageChild(sb->id);
1890 }
1891 
1892     int
gui_mch_get_scrollbar_xpadding(void)1893 gui_mch_get_scrollbar_xpadding(void)
1894 {
1895     // TODO: Calculate the padding for adjust scrollbar position when the
1896     // Window is maximized.
1897     return 0;
1898 }
1899 
1900     int
gui_mch_get_scrollbar_ypadding(void)1901 gui_mch_get_scrollbar_ypadding(void)
1902 {
1903     // TODO: Calculate the padding for adjust scrollbar position when the
1904     // Window is maximized.
1905     return 0;
1906 }
1907 
1908     void
gui_mch_enable_scrollbar(scrollbar_T * sb,int flag)1909 gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
1910 {
1911     if (sb->id != (Widget)0)
1912     {
1913 	if (flag)
1914 	    XtManageChild(sb->id);
1915 	else
1916 	    XtUnmanageChild(sb->id);
1917     }
1918 }
1919 
1920     void
gui_mch_create_scrollbar(scrollbar_T * sb,int orient)1921 gui_mch_create_scrollbar(
1922     scrollbar_T *sb,
1923     int		orient)	// SBAR_VERT or SBAR_HORIZ
1924 {
1925     sb->id = XtVaCreateWidget("scrollBar",
1926 #ifdef FEAT_GUI_NEXTAW
1927 	    scrollbarWidgetClass, vimForm,
1928 #else
1929 	    vim_scrollbarWidgetClass, vimForm,
1930 #endif
1931 	    XtNresizable,   True,
1932 	    XtNtop,	    XtChainTop,
1933 	    XtNbottom,	    XtChainTop,
1934 	    XtNleft,	    XtChainLeft,
1935 	    XtNright,	    XtChainLeft,
1936 	    XtNborderWidth, 0,
1937 	    XtNorientation, (orient == SBAR_VERT) ? XtorientVertical
1938 						  : XtorientHorizontal,
1939 	    XtNforeground, gui.scroll_fg_pixel,
1940 	    XtNbackground, gui.scroll_bg_pixel,
1941 	    NULL);
1942     if (sb->id == (Widget)0)
1943 	return;
1944 
1945     XtAddCallback(sb->id, XtNjumpProc,
1946 		  gui_athena_scroll_cb_jump, (XtPointer)sb->ident);
1947     XtAddCallback(sb->id, XtNscrollProc,
1948 		  gui_athena_scroll_cb_scroll, (XtPointer)sb->ident);
1949 
1950 #ifdef FEAT_GUI_NEXTAW
1951     XawScrollbarSetThumb(sb->id, 0.0, 1.0);
1952 #else
1953     vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
1954 #endif
1955 }
1956 
1957     void
gui_mch_destroy_scrollbar(scrollbar_T * sb)1958 gui_mch_destroy_scrollbar(scrollbar_T *sb)
1959 {
1960     if (sb->id != (Widget)0)
1961 	XtDestroyWidget(sb->id);
1962 }
1963 
1964     void
gui_mch_set_scrollbar_colors(scrollbar_T * sb)1965 gui_mch_set_scrollbar_colors(scrollbar_T *sb)
1966 {
1967     if (sb->id != (Widget)0)
1968 	XtVaSetValues(sb->id,
1969 	    XtNforeground, gui.scroll_fg_pixel,
1970 	    XtNbackground, gui.scroll_bg_pixel,
1971 	    NULL);
1972 
1973     // This is needed for the rectangle below the vertical scrollbars.
1974     if (sb == &gui.bottom_sbar && vimForm != (Widget)0)
1975 	gui_athena_scroll_colors(vimForm);
1976 }
1977 
1978 /*
1979  * Miscellaneous stuff:
1980  */
1981     Window
gui_x11_get_wid(void)1982 gui_x11_get_wid(void)
1983 {
1984     return XtWindow(textArea);
1985 }
1986 
1987 #if defined(FEAT_BROWSE) || defined(PROTO)
1988 /*
1989  * Put up a file requester.
1990  * Returns the selected name in allocated memory, or NULL for Cancel.
1991  */
1992     char_u *
gui_mch_browse(int saving UNUSED,char_u * title,char_u * dflt,char_u * ext UNUSED,char_u * initdir,char_u * filter UNUSED)1993 gui_mch_browse(
1994     int		saving UNUSED,	// select file to write
1995     char_u	*title,		// title for the window
1996     char_u	*dflt,		// default name
1997     char_u	*ext UNUSED,	// extension added
1998     char_u	*initdir,	// initial directory, NULL for current dir
1999     char_u	*filter UNUSED)	// file name filter
2000 {
2001     Position x, y;
2002     char_u	dirbuf[MAXPATHL];
2003 
2004     // Concatenate "initdir" and "dflt".
2005     if (initdir == NULL || *initdir == NUL)
2006 	mch_dirname(dirbuf, MAXPATHL);
2007     else if (STRLEN(initdir) + 2 < MAXPATHL)
2008 	STRCPY(dirbuf, initdir);
2009     else
2010 	dirbuf[0] = NUL;
2011     if (dflt != NULL && *dflt != NUL
2012 			      && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL)
2013     {
2014 	add_pathsep(dirbuf);
2015 	STRCAT(dirbuf, dflt);
2016     }
2017 
2018     // Position the file selector just below the menubar
2019     XtTranslateCoords(vimShell, (Position)0, (Position)
2020 #ifdef FEAT_MENU
2021 	    gui.menu_height
2022 #else
2023 	    0
2024 #endif
2025 	    , &x, &y);
2026     return (char_u *)vim_SelFile(vimShell, (char *)title, (char *)dirbuf,
2027 		  NULL, (int)x, (int)y, gui.menu_fg_pixel, gui.menu_bg_pixel,
2028 		  gui.scroll_fg_pixel, gui.scroll_bg_pixel);
2029 }
2030 #endif
2031 
2032 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2033 
2034 static int	dialogStatus;
2035 static Atom	dialogatom;
2036 
2037 /*
2038  * Callback function for the textfield.  When CR is hit this works like
2039  * hitting the "OK" button, ESC like "Cancel".
2040  */
2041     static void
keyhit_callback(Widget w UNUSED,XtPointer client_data UNUSED,XEvent * event,Boolean * cont UNUSED)2042 keyhit_callback(
2043     Widget		w UNUSED,
2044     XtPointer		client_data UNUSED,
2045     XEvent		*event,
2046     Boolean		*cont UNUSED)
2047 {
2048     char	buf[2];
2049 
2050     if (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1)
2051     {
2052 	if (*buf == CAR)
2053 	    dialogStatus = 1;
2054 	else if (*buf == ESC)
2055 	    dialogStatus = 0;
2056     }
2057 }
2058 
2059     static void
butproc(Widget w UNUSED,XtPointer client_data,XtPointer call_data UNUSED)2060 butproc(
2061     Widget	w UNUSED,
2062     XtPointer	client_data,
2063     XtPointer	call_data UNUSED)
2064 {
2065     dialogStatus = (int)(long)client_data + 1;
2066 }
2067 
2068 /*
2069  * Function called when dialog window closed.
2070  */
2071     static void
dialog_wm_handler(Widget w UNUSED,XtPointer client_data UNUSED,XEvent * event,Boolean * dum UNUSED)2072 dialog_wm_handler(
2073     Widget	w UNUSED,
2074     XtPointer	client_data UNUSED,
2075     XEvent	*event,
2076     Boolean	*dum UNUSED)
2077 {
2078     if (event->type == ClientMessage
2079 	    && (Atom)((XClientMessageEvent *)event)->data.l[0] == dialogatom)
2080 	dialogStatus = 0;
2081 }
2082 
2083     int
gui_mch_dialog(int type UNUSED,char_u * title,char_u * message,char_u * buttons,int dfltbutton UNUSED,char_u * textfield,int ex_cmd UNUSED)2084 gui_mch_dialog(
2085     int		type UNUSED,
2086     char_u	*title,
2087     char_u	*message,
2088     char_u	*buttons,
2089     int		dfltbutton UNUSED,
2090     char_u	*textfield,
2091     int		ex_cmd UNUSED)
2092 {
2093     char_u		*buts;
2094     char_u		*p, *next;
2095     XtAppContext	app;
2096     XEvent		event;
2097     Position		wd, hd;
2098     Position		wv, hv;
2099     Position		x, y;
2100     Widget		dialog;
2101     Widget		dialogshell;
2102     Widget		dialogmessage;
2103     Widget		dialogtextfield = 0;
2104     Widget		dialogButton;
2105     Widget		prev_dialogButton = NULL;
2106     int			butcount;
2107     int			vertical;
2108 
2109     if (title == NULL)
2110 	title = (char_u *)_("Vim dialog");
2111     dialogStatus = -1;
2112 
2113     // if our pointer is currently hidden, then we should show it.
2114     gui_mch_mousehide(FALSE);
2115 
2116     // Check 'v' flag in 'guioptions': vertical button placement.
2117     vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2118 
2119     // The shell is created each time, to make sure it is resized properly
2120     dialogshell = XtVaCreatePopupShell("dialogShell",
2121 	    transientShellWidgetClass, vimShell,
2122 	    XtNtitle, title,
2123 	    NULL);
2124     if (dialogshell == (Widget)0)
2125 	goto error;
2126 
2127     dialog = XtVaCreateManagedWidget("dialog",
2128 	    formWidgetClass, dialogshell,
2129 	    XtNdefaultDistance, 20,
2130 	    NULL);
2131     if (dialog == (Widget)0)
2132 	goto error;
2133     gui_athena_menu_colors(dialog);
2134     dialogmessage = XtVaCreateManagedWidget("dialogMessage",
2135 	    labelWidgetClass, dialog,
2136 	    XtNlabel, message,
2137 	    XtNtop, XtChainTop,
2138 	    XtNbottom, XtChainTop,
2139 	    XtNleft, XtChainLeft,
2140 	    XtNright, XtChainLeft,
2141 	    XtNresizable, True,
2142 	    XtNborderWidth, 0,
2143 	    NULL);
2144     gui_athena_menu_colors(dialogmessage);
2145 
2146     if (textfield != NULL)
2147     {
2148 	dialogtextfield = XtVaCreateManagedWidget("textfield",
2149 		asciiTextWidgetClass, dialog,
2150 		XtNwidth, 400,
2151 		XtNtop, XtChainTop,
2152 		XtNbottom, XtChainTop,
2153 		XtNleft, XtChainLeft,
2154 		XtNright, XtChainRight,
2155 		XtNfromVert, dialogmessage,
2156 		XtNresizable, True,
2157 		XtNstring, textfield,
2158 		XtNlength, IOSIZE,
2159 		XtNuseStringInPlace, True,
2160 		XtNeditType, XawtextEdit,
2161 		XtNwrap, XawtextWrapNever,
2162 		XtNresize, XawtextResizeHeight,
2163 		NULL);
2164 	XtManageChild(dialogtextfield);
2165 	XtAddEventHandler(dialogtextfield, KeyPressMask, False,
2166 			    (XtEventHandler)keyhit_callback, (XtPointer)NULL);
2167 	XawTextSetInsertionPoint(dialogtextfield,
2168 					  (XawTextPosition)STRLEN(textfield));
2169 	XtSetKeyboardFocus(dialog, dialogtextfield);
2170     }
2171 
2172     // make a copy, so that we can insert NULs
2173     buts = vim_strsave(buttons);
2174     if (buts == NULL)
2175 	return -1;
2176 
2177     p = buts;
2178     for (butcount = 0; *p; ++butcount)
2179     {
2180 	for (next = p; *next; ++next)
2181 	{
2182 	    if (*next == DLG_HOTKEY_CHAR)
2183 		STRMOVE(next, next + 1);
2184 	    if (*next == DLG_BUTTON_SEP)
2185 	    {
2186 		*next++ = NUL;
2187 		break;
2188 	    }
2189 	}
2190 	dialogButton = XtVaCreateManagedWidget("button",
2191 		commandWidgetClass, dialog,
2192 		XtNlabel, p,
2193 		XtNtop, XtChainBottom,
2194 		XtNbottom, XtChainBottom,
2195 		XtNleft, XtChainLeft,
2196 		XtNright, XtChainLeft,
2197 		XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield,
2198 		XtNvertDistance, vertical ? 4 : 20,
2199 		XtNresizable, False,
2200 		NULL);
2201 	gui_athena_menu_colors(dialogButton);
2202 	if (butcount > 0)
2203 	    XtVaSetValues(dialogButton,
2204 		    vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton,
2205 		    NULL);
2206 
2207 	XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)(long_u)butcount);
2208 	p = next;
2209 	prev_dialogButton = dialogButton;
2210     }
2211     vim_free(buts);
2212 
2213     XtRealizeWidget(dialogshell);
2214 
2215     // Setup for catching the close-window event, don't let it close Vim!
2216     dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
2217     XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1);
2218     XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL);
2219 
2220     XtVaGetValues(dialogshell,
2221 	    XtNwidth, &wd,
2222 	    XtNheight, &hd,
2223 	    NULL);
2224     XtVaGetValues(vimShell,
2225 	    XtNwidth, &wv,
2226 	    XtNheight, &hv,
2227 	    NULL);
2228     XtTranslateCoords(vimShell,
2229 	    (Position)((wv - wd) / 2),
2230 	    (Position)((hv - hd) / 2),
2231 	    &x, &y);
2232     if (x < 0)
2233 	x = 0;
2234     if (y < 0)
2235 	y = 0;
2236     XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL);
2237 
2238     // Position the mouse pointer in the dialog, required for when focus
2239     // follows mouse.
2240     XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40);
2241 
2242 
2243     app = XtWidgetToApplicationContext(dialogshell);
2244 
2245     XtPopup(dialogshell, XtGrabNonexclusive);
2246 
2247     for (;;)
2248     {
2249 	XtAppNextEvent(app, &event);
2250 	XtDispatchEvent(&event);
2251 	if (dialogStatus >= 0)
2252 	    break;
2253     }
2254 
2255     XtPopdown(dialogshell);
2256 
2257     if (textfield != NULL && dialogStatus < 0)
2258 	*textfield = NUL;
2259 
2260 error:
2261     XtDestroyWidget(dialogshell);
2262 
2263     return dialogStatus;
2264 }
2265 #endif
2266 
2267 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
2268 /*
2269  * Set the colors of Widget "id" to the menu colors.
2270  */
2271     static void
gui_athena_menu_colors(Widget id)2272 gui_athena_menu_colors(Widget id)
2273 {
2274     if (gui.menu_bg_pixel != INVALCOLOR)
2275 	XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL);
2276     if (gui.menu_fg_pixel != INVALCOLOR)
2277 	XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL);
2278 }
2279 #endif
2280 
2281 /*
2282  * Set the colors of Widget "id" to the scroll colors.
2283  */
2284     static void
gui_athena_scroll_colors(Widget id)2285 gui_athena_scroll_colors(Widget id)
2286 {
2287     if (gui.scroll_bg_pixel != INVALCOLOR)
2288 	XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL);
2289     if (gui.scroll_fg_pixel != INVALCOLOR)
2290 	XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL);
2291 }
2292