Merged from the latest developing branch.
[MacVim.git] / src / gui_athena.c
blobf5b8f70f9b407fcf8ea8310d578940635a0f7375
1 /* vi:set ts=8 sts=4 sw=4:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * GUI/Motif support by Robert Webb
5 * Athena port by Bill Foster
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.
12 #include <X11/StringDefs.h>
13 #include <X11/Intrinsic.h>
14 #ifdef FEAT_GUI_NEXTAW
15 # include <X11/neXtaw/Form.h>
16 # include <X11/neXtaw/SimpleMenu.h>
17 # include <X11/neXtaw/MenuButton.h>
18 # include <X11/neXtaw/SmeBSB.h>
19 # include <X11/neXtaw/SmeLine.h>
20 # include <X11/neXtaw/Box.h>
21 # include <X11/neXtaw/Dialog.h>
22 # include <X11/neXtaw/Text.h>
23 # include <X11/neXtaw/AsciiText.h>
24 # include <X11/neXtaw/Scrollbar.h>
25 #else
26 # include <X11/Xaw/Form.h>
27 # include <X11/Xaw/SimpleMenu.h>
28 # include <X11/Xaw/MenuButton.h>
29 # include <X11/Xaw/SmeBSB.h>
30 # include <X11/Xaw/SmeLine.h>
31 # include <X11/Xaw/Box.h>
32 # include <X11/Xaw/Dialog.h>
33 # include <X11/Xaw/Text.h>
34 # include <X11/Xaw/AsciiText.h>
35 #endif /* FEAT_GUI_NEXTAW */
37 #include "vim.h"
38 #ifndef FEAT_GUI_NEXTAW
39 # include "gui_at_sb.h"
40 #endif
42 extern Widget vimShell;
44 static Widget vimForm = (Widget)0;
45 Widget textArea = (Widget)0;
46 #ifdef FEAT_MENU
47 static Widget menuBar = (Widget)0;
48 static XtIntervalId timer = 0; /* 0 = expired, otherwise active */
50 /* Used to figure out menu ordering */
51 static vimmenu_T *a_cur_menu = NULL;
52 static Cardinal athena_calculate_ins_pos __ARGS((Widget));
54 static Pixmap gui_athena_create_pullright_pixmap __ARGS((Widget));
55 static void gui_athena_menu_timeout __ARGS((XtPointer, XtIntervalId *));
56 static void gui_athena_popup_callback __ARGS((Widget, XtPointer, XtPointer));
57 static void gui_athena_delayed_arm_action __ARGS((Widget, XEvent *, String *,
58 Cardinal *));
59 static void gui_athena_popdown_submenus_action __ARGS((Widget, XEvent *,
60 String *, Cardinal *));
61 static XtActionsRec pullAction[2] = {
62 { "menu-delayedpopup", (XtActionProc)gui_athena_delayed_arm_action},
63 { "menu-popdownsubmenus", (XtActionProc)gui_athena_popdown_submenus_action}
65 #endif
67 #ifdef FEAT_TOOLBAR
68 static void gui_mch_reset_focus __ARGS((void));
69 static Widget toolBar = (Widget)0;
70 #endif
72 static void gui_athena_scroll_cb_jump __ARGS((Widget, XtPointer, XtPointer));
73 static void gui_athena_scroll_cb_scroll __ARGS((Widget, XtPointer, XtPointer));
74 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
75 static void gui_athena_menu_colors __ARGS((Widget id));
76 #endif
77 static void gui_athena_scroll_colors __ARGS((Widget id));
79 #ifdef FEAT_MENU
80 static XtTranslations popupTrans, parentTrans, menuTrans, supermenuTrans;
81 static Pixmap pullerBitmap = None;
82 static int puller_width = 0;
83 #endif
86 * Scrollbar callback (XtNjumpProc) for when the scrollbar is dragged with the
87 * left or middle mouse button.
89 static void
90 gui_athena_scroll_cb_jump(w, client_data, call_data)
91 Widget w UNUSED;
92 XtPointer client_data, call_data;
94 scrollbar_T *sb, *sb_info;
95 long value;
97 sb = gui_find_scrollbar((long)client_data);
99 if (sb == NULL)
100 return;
101 else if (sb->wp != NULL) /* Left or right scrollbar */
104 * Careful: need to get scrollbar info out of first (left) scrollbar
105 * for window, but keep real scrollbar too because we must pass it to
106 * gui_drag_scrollbar().
108 sb_info = &sb->wp->w_scrollbars[0];
110 else /* Bottom scrollbar */
111 sb_info = sb;
113 value = (long)(*((float *)call_data) * (float)(sb_info->max + 1) + 0.001);
114 if (value > sb_info->max)
115 value = sb_info->max;
117 gui_drag_scrollbar(sb, value, TRUE);
121 * Scrollbar callback (XtNscrollProc) for paging up or down with the left or
122 * right mouse buttons.
124 static void
125 gui_athena_scroll_cb_scroll(w, client_data, call_data)
126 Widget w UNUSED;
127 XtPointer client_data, call_data;
129 scrollbar_T *sb, *sb_info;
130 long value;
131 int data = (int)(long)call_data;
132 int page;
134 sb = gui_find_scrollbar((long)client_data);
136 if (sb == NULL)
137 return;
138 if (sb->wp != NULL) /* Left or right scrollbar */
141 * Careful: need to get scrollbar info out of first (left) scrollbar
142 * for window, but keep real scrollbar too because we must pass it to
143 * gui_drag_scrollbar().
145 sb_info = &sb->wp->w_scrollbars[0];
147 if (sb_info->size > 5)
148 page = sb_info->size - 2; /* use two lines of context */
149 else
150 page = sb_info->size;
151 #ifdef FEAT_GUI_NEXTAW
152 if (data < 0)
154 data = (data - gui.char_height + 1) / gui.char_height;
155 if (data > -sb_info->size)
156 data = -1;
157 else
158 data = -page;
160 else if (data > 0)
162 data = (data + gui.char_height - 1) / gui.char_height;
163 if (data < sb_info->size)
164 data = 1;
165 else
166 data = page;
168 #else
169 switch (data)
171 case ONE_LINE_DATA: data = 1; break;
172 case -ONE_LINE_DATA: data = -1; break;
173 case ONE_PAGE_DATA: data = page; break;
174 case -ONE_PAGE_DATA: data = -page; break;
175 case END_PAGE_DATA: data = sb_info->max; break;
176 case -END_PAGE_DATA: data = -sb_info->max; break;
177 default: data = 0; break;
179 #endif
181 else /* Bottom scrollbar */
183 sb_info = sb;
184 #ifdef FEAT_GUI_NEXTAW
185 if (data < 0)
187 data = (data - gui.char_width + 1) / gui.char_width;
188 if (data > -sb->size)
189 data = -1;
191 else if (data > 0)
193 data = (data + gui.char_width - 1) / gui.char_width;
194 if (data < sb->size)
195 data = 1;
197 #endif
198 if (data < -1) /* page-width left */
200 if (sb->size > 8)
201 data = -(sb->size - 5);
202 else
203 data = -sb->size;
205 else if (data > 1) /* page-width right */
207 if (sb->size > 8)
208 data = (sb->size - 5);
209 else
210 data = sb->size;
214 value = sb_info->value + data;
215 if (value > sb_info->max)
216 value = sb_info->max;
217 else if (value < 0)
218 value = 0;
220 /* Update the bottom scrollbar an extra time (why is this needed?? */
221 if (sb->wp == NULL) /* Bottom scrollbar */
222 gui_mch_set_scrollbar_thumb(sb, value, sb->size, sb->max);
224 gui_drag_scrollbar(sb, value, FALSE);
228 * Create all the Athena widgets necessary.
230 void
231 gui_x11_create_widgets()
234 * We don't have any borders handled internally by the textArea to worry
235 * about so only skip over the configured border width.
237 gui.border_offset = gui.border_width;
239 #if 0 /* not needed? */
240 XtInitializeWidgetClass(formWidgetClass);
241 XtInitializeWidgetClass(boxWidgetClass);
242 XtInitializeWidgetClass(coreWidgetClass);
243 #ifdef FEAT_MENU
244 XtInitializeWidgetClass(menuButtonWidgetClass);
245 #endif
246 XtInitializeWidgetClass(simpleMenuWidgetClass);
247 #ifdef FEAT_GUI_NEXTAW
248 XtInitializeWidgetClass(scrollbarWidgetClass);
249 #else
250 XtInitializeWidgetClass(vim_scrollbarWidgetClass);
251 #endif
252 #endif
254 /* The form containing all the other widgets */
255 vimForm = XtVaCreateManagedWidget("vimForm",
256 formWidgetClass, vimShell,
257 XtNborderWidth, 0,
258 NULL);
259 gui_athena_scroll_colors(vimForm);
261 #ifdef FEAT_MENU
262 /* The top menu bar */
263 menuBar = XtVaCreateManagedWidget("menuBar",
264 boxWidgetClass, vimForm,
265 XtNresizable, True,
266 XtNtop, XtChainTop,
267 XtNbottom, XtChainTop,
268 XtNleft, XtChainLeft,
269 XtNright, XtChainRight,
270 XtNinsertPosition, athena_calculate_ins_pos,
271 NULL);
272 gui_athena_menu_colors(menuBar);
273 if (gui.menu_fg_pixel != INVALCOLOR)
274 XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL);
275 #endif
277 #ifdef FEAT_TOOLBAR
278 /* Don't create it Managed, it will be managed when creating the first
279 * item. Otherwise an empty toolbar shows up. */
280 toolBar = XtVaCreateWidget("toolBar",
281 boxWidgetClass, vimForm,
282 XtNresizable, True,
283 XtNtop, XtChainTop,
284 XtNbottom, XtChainTop,
285 XtNleft, XtChainLeft,
286 XtNright, XtChainRight,
287 XtNorientation, XtorientHorizontal,
288 XtNhSpace, 1,
289 XtNvSpace, 3,
290 XtNinsertPosition, athena_calculate_ins_pos,
291 NULL);
292 gui_athena_menu_colors(toolBar);
293 #endif
295 /* The text area. */
296 textArea = XtVaCreateManagedWidget("textArea",
297 coreWidgetClass, vimForm,
298 XtNresizable, True,
299 XtNtop, XtChainTop,
300 XtNbottom, XtChainTop,
301 XtNleft, XtChainLeft,
302 XtNright, XtChainLeft,
303 XtNbackground, gui.back_pixel,
304 XtNborderWidth, 0,
305 NULL);
308 * Install the callbacks.
310 gui_x11_callbacks(textArea, vimForm);
312 #ifdef FEAT_MENU
313 popupTrans = XtParseTranslationTable(
314 "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
315 "<LeaveWindow>: unhighlight()\n"
316 "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n"
317 "<Motion>: highlight() menu-delayedpopup()");
318 parentTrans = XtParseTranslationTable("<LeaveWindow>: unhighlight()");
319 menuTrans = XtParseTranslationTable(
320 "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
321 "<LeaveWindow>: menu-popdownsubmenus() XtMenuPopdown() unhighlight()\n"
322 "<BtnUp>: notify() unhighlight()\n"
323 "<BtnMotion>: highlight() menu-delayedpopup()");
324 supermenuTrans = XtParseTranslationTable(
325 "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n"
326 "<LeaveWindow>: unhighlight()\n"
327 "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n"
328 "<BtnMotion>: highlight() menu-delayedpopup()");
330 XtAppAddActions(XtWidgetToApplicationContext(vimForm), pullAction,
331 XtNumber(pullAction));
332 #endif
334 /* Pretend we don't have input focus, we will get an event if we do. */
335 gui.in_focus = FALSE;
338 #ifdef FEAT_MENU
340 * Calculates the Pixmap based on the size of the current menu font.
342 static Pixmap
343 gui_athena_create_pullright_pixmap(w)
344 Widget w;
346 Pixmap retval;
347 #ifdef FONTSET_ALWAYS
348 XFontSet font = None;
349 #else
350 XFontStruct *font = NULL;
351 #endif
353 #ifdef FONTSET_ALWAYS
354 if (gui.menu_fontset == NOFONTSET)
355 #else
356 if (gui.menu_font == NOFONT)
357 #endif
359 XrmValue from, to;
360 WidgetList children;
361 Cardinal num_children;
363 #ifdef FONTSET_ALWAYS
364 from.size = strlen(from.addr = XtDefaultFontSet);
365 to.addr = (XtPointer)&font;
366 to.size = sizeof(XFontSet);
367 #else
368 from.size = strlen(from.addr = XtDefaultFont);
369 to.addr = (XtPointer)&font;
370 to.size = sizeof(XFontStruct *);
371 #endif
372 /* Assumption: The menuBar children will use the same font as the
373 * pulldown menu items AND they will all be of type
374 * XtNfont.
376 XtVaGetValues(menuBar, XtNchildren, &children,
377 XtNnumChildren, &num_children,
378 NULL);
379 if (XtConvertAndStore(w ? w :
380 (num_children > 0) ? children[0] : menuBar,
381 XtRString, &from,
382 #ifdef FONTSET_ALWAYS
383 XtRFontSet, &to
384 #else
385 XtRFontStruct, &to
386 #endif
387 ) == False)
388 return None;
389 /* "font" should now contain data */
391 else
392 #ifdef FONTSET_ALWAYS
393 font = (XFontSet)gui.menu_fontset;
394 #else
395 font = (XFontStruct *)gui.menu_font;
396 #endif
399 int width, height;
400 GC draw_gc, undraw_gc;
401 XGCValues gc_values;
402 XPoint points[3];
404 #ifdef FONTSET_ALWAYS
405 height = fontset_height2(font);
406 #else
407 height = font->max_bounds.ascent + font->max_bounds.descent;
408 #endif
409 width = height - 2;
410 puller_width = width + 4;
411 retval = XCreatePixmap(gui.dpy,DefaultRootWindow(gui.dpy),width,
412 height, 1);
413 gc_values.foreground = 1;
414 gc_values.background = 0;
415 draw_gc = XCreateGC(gui.dpy, retval,
416 GCForeground | GCBackground,
417 &gc_values);
418 gc_values.foreground = 0;
419 gc_values.background = 1;
420 undraw_gc = XCreateGC(gui.dpy, retval,
421 GCForeground | GCBackground,
422 &gc_values);
423 points[0].x = 0;
424 points[0].y = 0;
425 points[1].x = width - 1;
426 points[1].y = (height - 1) / 2;
427 points[2].x = 0;
428 points[2].y = height - 1;
429 XFillRectangle(gui.dpy, retval, undraw_gc, 0, 0, height, height);
430 XFillPolygon(gui.dpy, retval, draw_gc, points, XtNumber(points),
431 Convex, CoordModeOrigin);
432 XFreeGC(gui.dpy, draw_gc);
433 XFreeGC(gui.dpy, undraw_gc);
435 return retval;
437 #endif
440 * Called when the GUI is not going to start after all.
442 void
443 gui_x11_destroy_widgets()
445 textArea = NULL;
446 #ifdef FEAT_MENU
447 menuBar = NULL;
448 #endif
449 #ifdef FEAT_TOOLBAR
450 toolBar = NULL;
451 #endif
454 #if defined(FEAT_TOOLBAR) || defined(PROTO)
455 # include "gui_x11_pm.h"
456 # ifdef HAVE_X11_XPM_H
457 # include <X11/xpm.h>
458 # endif
460 static void createXpmImages __ARGS((char_u *path, char **xpm, Pixmap *sen));
461 static void get_toolbar_pixmap __ARGS((vimmenu_T *menu, Pixmap *sen));
464 * Allocated a pixmap for toolbar menu "menu".
465 * Return in "sen".
467 static void
468 get_toolbar_pixmap(menu, sen)
469 vimmenu_T *menu;
470 Pixmap *sen;
472 char_u buf[MAXPATHL]; /* buffer storing expanded pathname */
473 char **xpm = NULL; /* xpm array */
475 buf[0] = NUL; /* start with NULL path */
477 if (menu->iconfile != NULL)
479 /* Use the "icon=" argument. */
480 gui_find_iconfile(menu->iconfile, buf, "xpm");
481 createXpmImages(buf, NULL, sen);
483 /* If it failed, try using the menu name. */
484 if (*sen == (Pixmap)0 && gui_find_bitmap(menu->name, buf, "xpm") == OK)
485 createXpmImages(buf, NULL, sen);
486 if (*sen != (Pixmap)0)
487 return;
490 if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL)
492 if (menu->iconidx >= 0 && menu->iconidx
493 < (int)(sizeof(built_in_pixmaps) / sizeof(built_in_pixmaps[0])))
494 xpm = built_in_pixmaps[menu->iconidx];
495 else
496 xpm = tb_blank_xpm;
499 if (xpm != NULL || buf[0] != NUL)
500 createXpmImages(buf, xpm, sen);
504 * Read an Xpm file, doing color substitutions for the foreground and
505 * background colors. If there is an error reading a color xpm file,
506 * drop back and read the monochrome file. If successful, create the
507 * insensitive Pixmap too.
509 static void
510 createXpmImages(path, xpm, sen)
511 char_u *path;
512 char **xpm;
513 Pixmap *sen;
515 Window rootWindow;
516 XpmAttributes attrs;
517 XpmColorSymbol color[5] =
519 {"none", "none", 0},
520 {"iconColor1", NULL, 0},
521 {"bottomShadowColor", NULL, 0},
522 {"topShadowColor", NULL, 0},
523 {"selectColor", NULL, 0}
525 int screenNum;
526 int status;
527 Pixmap mask;
528 Pixmap map;
530 gui_mch_get_toolbar_colors(
531 &color[BACKGROUND].pixel,
532 &color[FOREGROUND].pixel,
533 &color[BOTTOM_SHADOW].pixel,
534 &color[TOP_SHADOW].pixel,
535 &color[HIGHLIGHT].pixel);
537 /* Setup the color subsititution table */
538 attrs.valuemask = XpmColorSymbols;
539 attrs.colorsymbols = color;
540 attrs.numsymbols = 5;
542 screenNum = DefaultScreen(gui.dpy);
543 rootWindow = RootWindow(gui.dpy, screenNum);
545 /* Create the "sensitive" pixmap */
546 if (xpm != NULL)
547 status = XpmCreatePixmapFromData(gui.dpy, rootWindow, xpm,
548 &map, &mask, &attrs);
549 else
550 status = XpmReadFileToPixmap(gui.dpy, rootWindow, (char *)path,
551 &map, &mask, &attrs);
552 if (status == XpmSuccess && map != 0)
554 XGCValues gcvalues;
555 GC back_gc;
556 GC mask_gc;
558 /* Need to create new Pixmaps with the mask applied. */
559 gcvalues.foreground = color[BACKGROUND].pixel;
560 back_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues);
561 mask_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues);
562 XSetClipMask(gui.dpy, mask_gc, mask);
564 /* Create the "sensitive" pixmap. */
565 *sen = XCreatePixmap(gui.dpy, rootWindow,
566 attrs.width, attrs.height,
567 DefaultDepth(gui.dpy, screenNum));
568 XFillRectangle(gui.dpy, *sen, back_gc, 0, 0,
569 attrs.width, attrs.height);
570 XCopyArea(gui.dpy, map, *sen, mask_gc, 0, 0,
571 attrs.width, attrs.height, 0, 0);
573 XFreeGC(gui.dpy, back_gc);
574 XFreeGC(gui.dpy, mask_gc);
575 XFreePixmap(gui.dpy, map);
577 else
578 *sen = 0;
580 XpmFreeAttributes(&attrs);
583 void
584 gui_mch_set_toolbar_pos(x, y, w, h)
585 int x;
586 int y;
587 int w;
588 int h;
590 Dimension border;
591 int height;
593 if (!XtIsManaged(toolBar)) /* nothing to do */
594 return;
595 XtUnmanageChild(toolBar);
596 XtVaGetValues(toolBar,
597 XtNborderWidth, &border,
598 NULL);
599 height = h - 2 * border;
600 if (height < 0)
601 height = 1;
602 XtVaSetValues(toolBar,
603 XtNhorizDistance, x,
604 XtNvertDistance, y,
605 XtNwidth, w - 2 * border,
606 XtNheight, height,
607 NULL);
608 XtManageChild(toolBar);
610 #endif
612 void
613 gui_mch_set_text_area_pos(x, y, w, h)
614 int x;
615 int y;
616 int w;
617 int h;
619 XtUnmanageChild(textArea);
620 XtVaSetValues(textArea,
621 XtNhorizDistance, x,
622 XtNvertDistance, y,
623 XtNwidth, w,
624 XtNheight, h,
625 NULL);
626 XtManageChild(textArea);
627 #ifdef FEAT_TOOLBAR
628 /* Give keyboard focus to the textArea instead of the toolbar. */
629 gui_mch_reset_focus();
630 #endif
633 #ifdef FEAT_TOOLBAR
635 * A toolbar button has been pushed; now reset the input focus
636 * such that the user can type page up/down etc. and have the
637 * input go to the editor window, not the button
639 static void
640 gui_mch_reset_focus()
642 XtSetKeyboardFocus(vimForm, textArea);
644 #endif
647 void
648 gui_x11_set_back_color()
650 if (textArea != NULL)
651 XtVaSetValues(textArea,
652 XtNbackground, gui.back_pixel,
653 NULL);
656 #if defined(FEAT_MENU) || defined(PROTO)
658 * Menu stuff.
661 static char_u *make_pull_name __ARGS((char_u * name));
662 static Widget get_popup_entry __ARGS((Widget w));
663 static Widget submenu_widget __ARGS((Widget));
664 static Boolean has_submenu __ARGS((Widget));
665 static void gui_mch_submenu_change __ARGS((vimmenu_T *mp, int colors));
666 static void gui_athena_menu_font __ARGS((Widget id));
667 static Boolean gui_athena_menu_has_submenus __ARGS((Widget, Widget));
669 void
670 gui_mch_enable_menu(flag)
671 int flag;
673 if (flag)
675 XtManageChild(menuBar);
676 # ifdef FEAT_TOOLBAR
677 if (XtIsManaged(toolBar))
679 XtVaSetValues(toolBar,
680 XtNvertDistance, gui.menu_height,
681 NULL);
682 XtVaSetValues(textArea,
683 XtNvertDistance, gui.menu_height + gui.toolbar_height,
684 NULL);
686 # endif
688 else
690 XtUnmanageChild(menuBar);
691 # ifdef FEAT_TOOLBAR
692 if (XtIsManaged(toolBar))
694 XtVaSetValues(toolBar,
695 XtNvertDistance, 0,
696 NULL);
698 # endif
702 void
703 gui_mch_set_menu_pos(x, y, w, h)
704 int x;
705 int y;
706 int w;
707 int h;
709 Dimension border;
710 int height;
712 XtUnmanageChild(menuBar);
713 XtVaGetValues(menuBar, XtNborderWidth, &border, NULL);
714 /* avoid trouble when there are no menu items, and h is 1 */
715 height = h - 2 * border;
716 if (height < 0)
717 height = 1;
718 XtVaSetValues(menuBar,
719 XtNhorizDistance, x,
720 XtNvertDistance, y,
721 XtNwidth, w - 2 * border,
722 XtNheight, height,
723 NULL);
724 XtManageChild(menuBar);
728 * Used to calculate the insertion position of a widget with respect to its
729 * neighbors.
731 * Valid range of return values is: 0 (beginning of children) to
732 * numChildren (end of children).
734 static Cardinal
735 athena_calculate_ins_pos(widget)
736 Widget widget;
738 /* Assume that if the parent of the vimmenu_T is NULL, then we can get
739 * to this menu by traversing "next", starting at "root_menu".
741 * This holds true for popup menus, toolbar, and toplevel menu items.
744 /* Popup menus: "id" is NULL. Only submenu_id is valid */
746 /* Menus that are not toplevel: "parent" will be non-NULL, "id" &
747 * "submenu_id" will be non-NULL.
750 /* Toplevel menus: "parent" is NULL, id is the widget of the menu item */
752 WidgetList children;
753 Cardinal num_children = 0;
754 int retval;
755 Arg args[2];
756 int n = 0;
757 int i;
759 XtSetArg(args[n], XtNchildren, &children); n++;
760 XtSetArg(args[n], XtNnumChildren, &num_children); n++;
761 XtGetValues(XtParent(widget), args, n);
763 retval = num_children;
764 for (i = 0; i < (int)num_children; ++i)
766 Widget current = children[i];
767 vimmenu_T *menu = NULL;
769 for (menu = (a_cur_menu->parent == NULL)
770 ? root_menu : a_cur_menu->parent->children;
771 menu != NULL;
772 menu = menu->next)
773 if (current == menu->id
774 && a_cur_menu->priority < menu->priority
775 && i < retval)
776 retval = i;
778 return retval;
781 void
782 gui_mch_add_menu(menu, idx)
783 vimmenu_T *menu;
784 int idx UNUSED;
786 char_u *pullright_name;
787 Dimension height, space, border;
788 vimmenu_T *parent = menu->parent;
790 a_cur_menu = menu;
791 if (parent == NULL)
793 if (menu_is_popup(menu->dname))
795 menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
796 simpleMenuWidgetClass, vimShell,
797 XtNinsertPosition, athena_calculate_ins_pos,
798 XtNtranslations, popupTrans,
799 NULL);
800 gui_athena_menu_colors(menu->submenu_id);
802 else if (menu_is_menubar(menu->dname))
804 menu->id = XtVaCreateManagedWidget((char *)menu->dname,
805 menuButtonWidgetClass, menuBar,
806 XtNmenuName, menu->dname,
807 #ifdef FONTSET_ALWAYS
808 XtNinternational, True,
809 #endif
810 NULL);
811 if (menu->id == (Widget)0)
812 return;
813 gui_athena_menu_colors(menu->id);
814 gui_athena_menu_font(menu->id);
816 menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
817 simpleMenuWidgetClass, menu->id,
818 XtNinsertPosition, athena_calculate_ins_pos,
819 XtNtranslations, supermenuTrans,
820 NULL);
821 gui_athena_menu_colors(menu->submenu_id);
822 gui_athena_menu_font(menu->submenu_id);
824 /* Don't update the menu height when it was set at a fixed value */
825 if (!gui.menu_height_fixed)
828 * When we add a top-level item to the menu bar, we can figure
829 * out how high the menu bar should be.
831 XtVaGetValues(menuBar,
832 XtNvSpace, &space,
833 XtNborderWidth, &border,
834 NULL);
835 XtVaGetValues(menu->id,
836 XtNheight, &height,
837 NULL);
838 gui.menu_height = height + 2 * (space + border);
842 else if (parent->submenu_id != (Widget)0)
844 menu->id = XtVaCreateManagedWidget((char *)menu->dname,
845 smeBSBObjectClass, parent->submenu_id,
846 XtNlabel, menu->dname,
847 #ifdef FONTSET_ALWAYS
848 XtNinternational, True,
849 #endif
850 NULL);
851 if (menu->id == (Widget)0)
852 return;
853 if (pullerBitmap == None)
854 pullerBitmap = gui_athena_create_pullright_pixmap(menu->id);
856 XtVaSetValues(menu->id, XtNrightBitmap, pullerBitmap,
857 NULL);
858 /* If there are other menu items that are not pulldown menus,
859 * we need to adjust the right margins of those, too.
862 WidgetList children;
863 Cardinal num_children;
864 int i;
866 XtVaGetValues(parent->submenu_id, XtNchildren, &children,
867 XtNnumChildren, &num_children,
868 NULL);
869 for (i = 0; i < (int)num_children; ++i)
871 XtVaSetValues(children[i],
872 XtNrightMargin, puller_width,
873 NULL);
876 gui_athena_menu_colors(menu->id);
877 gui_athena_menu_font(menu->id);
879 pullright_name = make_pull_name(menu->dname);
880 menu->submenu_id = XtVaCreatePopupShell((char *)pullright_name,
881 simpleMenuWidgetClass, parent->submenu_id,
882 XtNtranslations, menuTrans,
883 NULL);
884 gui_athena_menu_colors(menu->submenu_id);
885 gui_athena_menu_font(menu->submenu_id);
886 vim_free(pullright_name);
887 XtAddCallback(menu->submenu_id, XtNpopupCallback,
888 gui_athena_popup_callback, (XtPointer)menu);
890 if (parent->parent != NULL)
891 XtOverrideTranslations(parent->submenu_id, parentTrans);
893 a_cur_menu = NULL;
896 /* Used to determine whether a SimpleMenu has pulldown entries.
898 * "id" is the parent of the menu items.
899 * Ignore widget "ignore" in the pane.
901 static Boolean
902 gui_athena_menu_has_submenus(id, ignore)
903 Widget id;
904 Widget ignore;
906 WidgetList children;
907 Cardinal num_children;
908 int i;
910 XtVaGetValues(id, XtNchildren, &children,
911 XtNnumChildren, &num_children,
912 NULL);
913 for (i = 0; i < (int)num_children; ++i)
915 if (children[i] == ignore)
916 continue;
917 if (has_submenu(children[i]))
918 return True;
920 return False;
923 static void
924 gui_athena_menu_font(id)
925 Widget id;
927 #ifdef FONTSET_ALWAYS
928 if (gui.menu_fontset != NOFONTSET)
930 if (XtIsManaged(id))
932 XtUnmanageChild(id);
933 XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
934 /* We should force the widget to recalculate it's
935 * geometry now. */
936 XtManageChild(id);
938 else
939 XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
940 if (has_submenu(id))
941 XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
943 #else
944 int managed = FALSE;
946 if (gui.menu_font != NOFONT)
948 if (XtIsManaged(id))
950 XtUnmanageChild(id);
951 managed = TRUE;
954 # ifdef FEAT_XFONTSET
955 if (gui.fontset != NOFONTSET)
956 XtVaSetValues(id, XtNfontSet, gui.menu_font, NULL);
957 else
958 # endif
959 XtVaSetValues(id, XtNfont, gui.menu_font, NULL);
960 if (has_submenu(id))
961 XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
963 /* Force the widget to recalculate it's geometry now. */
964 if (managed)
965 XtManageChild(id);
967 #endif
971 void
972 gui_mch_new_menu_font()
974 Pixmap oldpuller = None;
976 if (menuBar == (Widget)0)
977 return;
979 if (pullerBitmap != None)
981 oldpuller = pullerBitmap;
982 pullerBitmap = gui_athena_create_pullright_pixmap(NULL);
984 gui_mch_submenu_change(root_menu, FALSE);
987 /* Iterate through the menubar menu items and get the height of
988 * each one. The menu bar height is set to the maximum of all
989 * the heights.
991 vimmenu_T *mp;
992 int max_height = 9999;
994 for (mp = root_menu; mp != NULL; mp = mp->next)
996 if (menu_is_menubar(mp->dname))
998 Dimension height;
1000 XtVaGetValues(mp->id,
1001 XtNheight, &height,
1002 NULL);
1003 if (height < max_height)
1004 max_height = height;
1007 if (max_height != 9999)
1009 /* Don't update the menu height when it was set at a fixed value */
1010 if (!gui.menu_height_fixed)
1012 Dimension space, border;
1014 XtVaGetValues(menuBar,
1015 XtNvSpace, &space,
1016 XtNborderWidth, &border,
1017 NULL);
1018 gui.menu_height = max_height + 2 * (space + border);
1022 /* Now, to simulate the window being resized. Only, this
1023 * will resize the window to it's current state.
1025 * There has to be a better way, but I do not see one at this time.
1026 * (David Harrison)
1029 Position w, h;
1031 XtVaGetValues(vimShell,
1032 XtNwidth, &w,
1033 XtNheight, &h,
1034 NULL);
1035 gui_resize_shell(w, h
1036 #ifdef FEAT_XIM
1037 - xim_get_status_area_height()
1038 #endif
1041 gui_set_shellsize(FALSE, TRUE, RESIZE_VERT);
1042 ui_new_shellsize();
1043 if (oldpuller != None)
1044 XFreePixmap(gui.dpy, oldpuller);
1047 #if defined(FEAT_BEVAL) || defined(PROTO)
1048 void
1049 gui_mch_new_tooltip_font()
1051 # ifdef FEAT_TOOLBAR
1052 vimmenu_T *menu;
1054 if (toolBar == (Widget)0)
1055 return;
1057 menu = gui_find_menu((char_u *)"ToolBar");
1058 if (menu != NULL)
1059 gui_mch_submenu_change(menu, FALSE);
1060 # endif
1063 void
1064 gui_mch_new_tooltip_colors()
1066 # ifdef FEAT_TOOLBAR
1067 vimmenu_T *menu;
1069 if (toolBar == (Widget)0)
1070 return;
1072 menu = gui_find_menu((char_u *)"ToolBar");
1073 if (menu != NULL)
1074 gui_mch_submenu_change(menu, TRUE);
1075 # endif
1077 #endif
1079 static void
1080 gui_mch_submenu_change(menu, colors)
1081 vimmenu_T *menu;
1082 int colors; /* TRUE for colors, FALSE for font */
1084 vimmenu_T *mp;
1086 for (mp = menu; mp != NULL; mp = mp->next)
1088 if (mp->id != (Widget)0)
1090 if (colors)
1092 gui_athena_menu_colors(mp->id);
1093 #ifdef FEAT_TOOLBAR
1094 /* For a toolbar item: Free the pixmap and allocate a new one,
1095 * so that the background color is right. */
1096 if (mp->image != (Pixmap)0)
1098 XFreePixmap(gui.dpy, mp->image);
1099 get_toolbar_pixmap(mp, &mp->image);
1100 if (mp->image != (Pixmap)0)
1101 XtVaSetValues(mp->id, XtNbitmap, mp->image, NULL);
1104 # ifdef FEAT_BEVAL
1105 /* If we have a tooltip, then we need to change it's colors */
1106 if (mp->tip != NULL)
1108 Arg args[2];
1110 args[0].name = XtNbackground;
1111 args[0].value = gui.tooltip_bg_pixel;
1112 args[1].name = XtNforeground;
1113 args[1].value = gui.tooltip_fg_pixel;
1114 XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1116 # endif
1117 #endif
1119 else
1121 gui_athena_menu_font(mp->id);
1122 #ifdef FEAT_BEVAL
1123 /* If we have a tooltip, then we need to change it's font */
1124 /* Assume XtNinternational == True (in createBalloonEvalWindow)
1126 if (mp->tip != NULL)
1128 Arg args[1];
1130 args[0].name = XtNfontSet;
1131 args[0].value = (XtArgVal)gui.tooltip_fontset;
1132 XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1134 #endif
1138 if (mp->children != NULL)
1140 /* Set the colors/font for the tear off widget */
1141 if (mp->submenu_id != (Widget)0)
1143 if (colors)
1144 gui_athena_menu_colors(mp->submenu_id);
1145 else
1146 gui_athena_menu_font(mp->submenu_id);
1148 /* Set the colors for the children */
1149 gui_mch_submenu_change(mp->children, colors);
1155 * Make a submenu name into a pullright name.
1156 * Replace '.' by '_', can't include '.' in the submenu name.
1158 static char_u *
1159 make_pull_name(name)
1160 char_u * name;
1162 char_u *pname;
1163 char_u *p;
1165 pname = vim_strnsave(name, STRLEN(name) + strlen("-pullright"));
1166 if (pname != NULL)
1168 strcat((char *)pname, "-pullright");
1169 while ((p = vim_strchr(pname, '.')) != NULL)
1170 *p = '_';
1172 return pname;
1175 void
1176 gui_mch_add_menu_item(menu, idx)
1177 vimmenu_T *menu;
1178 int idx UNUSED;
1180 vimmenu_T *parent = menu->parent;
1182 a_cur_menu = menu;
1183 # ifdef FEAT_TOOLBAR
1184 if (menu_is_toolbar(parent->name))
1186 WidgetClass type;
1187 int n;
1188 Arg args[21];
1190 n = 0;
1191 if (menu_is_separator(menu->name))
1193 XtSetArg(args[n], XtNlabel, ""); n++;
1194 XtSetArg(args[n], XtNborderWidth, 0); n++;
1196 else
1198 get_toolbar_pixmap(menu, &menu->image);
1199 XtSetArg(args[n], XtNlabel, menu->dname); n++;
1200 XtSetArg(args[n], XtNinternalHeight, 1); n++;
1201 XtSetArg(args[n], XtNinternalWidth, 1); n++;
1202 XtSetArg(args[n], XtNborderWidth, 1); n++;
1203 if (menu->image != 0)
1204 XtSetArg(args[n], XtNbitmap, menu->image); n++;
1206 XtSetArg(args[n], XtNhighlightThickness, 0); n++;
1207 type = commandWidgetClass;
1208 /* TODO: figure out the position in the toolbar?
1209 * This currently works fine for the default toolbar, but
1210 * what if we add/remove items during later runtime?
1213 /* NOTE: "idx" isn't used here. The position is calculated by
1214 * athena_calculate_ins_pos(). The position it calculates
1215 * should be equal to "idx".
1217 /* TODO: Could we just store "idx" and use that as the child
1218 * placement?
1221 if (menu->id == NULL)
1223 menu->id = XtCreateManagedWidget((char *)menu->dname,
1224 type, toolBar, args, n);
1225 XtAddCallback(menu->id,
1226 XtNcallback, gui_x11_menu_cb, menu);
1228 else
1229 XtSetValues(menu->id, args, n);
1230 gui_athena_menu_colors(menu->id);
1232 #ifdef FEAT_BEVAL
1233 gui_mch_menu_set_tip(menu);
1234 #endif
1236 menu->parent = parent;
1237 menu->submenu_id = NULL;
1238 if (!XtIsManaged(toolBar)
1239 && vim_strchr(p_go, GO_TOOLBAR) != NULL)
1240 gui_mch_show_toolbar(TRUE);
1241 gui.toolbar_height = gui_mch_compute_toolbar_height();
1242 return;
1243 } /* toolbar menu item */
1244 # endif
1246 /* Add menu separator */
1247 if (menu_is_separator(menu->name))
1249 menu->submenu_id = (Widget)0;
1250 menu->id = XtVaCreateManagedWidget((char *)menu->dname,
1251 smeLineObjectClass, parent->submenu_id,
1252 NULL);
1253 if (menu->id == (Widget)0)
1254 return;
1255 gui_athena_menu_colors(menu->id);
1257 else
1259 if (parent != NULL && parent->submenu_id != (Widget)0)
1261 menu->submenu_id = (Widget)0;
1262 menu->id = XtVaCreateManagedWidget((char *)menu->dname,
1263 smeBSBObjectClass, parent->submenu_id,
1264 XtNlabel, menu->dname,
1265 #ifdef FONTSET_ALWAYS
1266 XtNinternational, True,
1267 #endif
1268 NULL);
1269 if (menu->id == (Widget)0)
1270 return;
1272 /* If there are other "pulldown" items in this pane, then adjust
1273 * the right margin to accomodate the arrow pixmap, otherwise
1274 * the right margin will be the same as the left margin.
1277 Dimension left_margin;
1279 XtVaGetValues(menu->id, XtNleftMargin, &left_margin, NULL);
1280 XtVaSetValues(menu->id, XtNrightMargin,
1281 gui_athena_menu_has_submenus(parent->submenu_id, NULL) ?
1282 puller_width :
1283 left_margin,
1284 NULL);
1287 gui_athena_menu_colors(menu->id);
1288 gui_athena_menu_font(menu->id);
1289 XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
1290 (XtPointer)menu);
1293 a_cur_menu = NULL;
1296 #if defined(FEAT_TOOLBAR) || defined(PROTO)
1297 void
1298 gui_mch_show_toolbar(int showit)
1300 Cardinal numChildren; /* how many children toolBar has */
1302 if (toolBar == (Widget)0)
1303 return;
1304 XtVaGetValues(toolBar, XtNnumChildren, &numChildren, NULL);
1305 if (showit && numChildren > 0)
1307 /* Assume that we want to show the toolbar if p_toolbar contains valid
1308 * option settings, therefore p_toolbar must not be NULL.
1310 WidgetList children;
1312 XtVaGetValues(toolBar, XtNchildren, &children, NULL);
1314 void (*action)(BalloonEval *);
1315 int text = 0;
1317 if (strstr((const char *)p_toolbar, "tooltips"))
1318 action = &gui_mch_enable_beval_area;
1319 else
1320 action = &gui_mch_disable_beval_area;
1321 if (strstr((const char *)p_toolbar, "text"))
1322 text = 1;
1323 else if (strstr((const char *)p_toolbar, "icons"))
1324 text = -1;
1325 if (text != 0)
1327 vimmenu_T *toolbar;
1328 vimmenu_T *cur;
1330 for (toolbar = root_menu; toolbar; toolbar = toolbar->next)
1331 if (menu_is_toolbar(toolbar->dname))
1332 break;
1333 /* Assumption: toolbar is NULL if there is no toolbar,
1334 * otherwise it contains the toolbar menu structure.
1336 * Assumption: "numChildren" == the number of items in the list
1337 * of items beginning with toolbar->children.
1339 if (toolbar)
1341 for (cur = toolbar->children; cur; cur = cur->next)
1343 Arg args[2];
1344 int n = 0;
1346 /* Enable/Disable tooltip (OK to enable while currently
1347 * enabled)
1349 if (cur->tip != NULL)
1350 (*action)(cur->tip);
1351 if (text == 1)
1353 XtSetArg(args[n], XtNbitmap, None);
1354 n++;
1355 XtSetArg(args[n], XtNlabel,
1356 menu_is_separator(cur->name) ? "" :
1357 (char *)cur->dname);
1358 n++;
1360 else
1362 XtSetArg(args[n], XtNbitmap, cur->image);
1363 n++;
1364 XtSetArg(args[n], XtNlabel, (cur->image == None) ?
1365 menu_is_separator(cur->name) ?
1366 "" :
1367 (char *)cur->dname
1369 (char *)None);
1370 n++;
1372 if (cur->id != NULL)
1374 XtUnmanageChild(cur->id);
1375 XtSetValues(cur->id, args, n);
1376 XtManageChild(cur->id);
1382 gui.toolbar_height = gui_mch_compute_toolbar_height();
1383 XtManageChild(toolBar);
1384 if (XtIsManaged(menuBar))
1386 XtVaSetValues(textArea,
1387 XtNvertDistance, gui.toolbar_height + gui.menu_height,
1388 NULL);
1389 XtVaSetValues(toolBar,
1390 XtNvertDistance, gui.menu_height,
1391 NULL);
1393 else
1395 XtVaSetValues(textArea,
1396 XtNvertDistance, gui.toolbar_height,
1397 NULL);
1398 XtVaSetValues(toolBar,
1399 XtNvertDistance, 0,
1400 NULL);
1403 else
1405 gui.toolbar_height = 0;
1406 if (XtIsManaged(menuBar))
1407 XtVaSetValues(textArea,
1408 XtNvertDistance, gui.menu_height,
1409 NULL);
1410 else
1411 XtVaSetValues(textArea,
1412 XtNvertDistance, 0,
1413 NULL);
1415 XtUnmanageChild(toolBar);
1417 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
1422 gui_mch_compute_toolbar_height()
1424 Dimension height; /* total Toolbar height */
1425 Dimension whgt; /* height of each widget */
1426 Dimension marginHeight; /* XmNmarginHeight of toolBar */
1427 Dimension shadowThickness; /* thickness of Xtparent(toolBar) */
1428 WidgetList children; /* list of toolBar's children */
1429 Cardinal numChildren; /* how many children toolBar has */
1430 int i;
1432 height = 0;
1433 shadowThickness = 0;
1434 marginHeight = 0;
1435 if (toolBar != (Widget)0)
1437 XtVaGetValues(toolBar,
1438 XtNborderWidth, &shadowThickness,
1439 XtNvSpace, &marginHeight,
1440 XtNchildren, &children,
1441 XtNnumChildren, &numChildren,
1442 NULL);
1443 for (i = 0; i < (int)numChildren; i++)
1445 whgt = 0;
1447 XtVaGetValues(children[i], XtNheight, &whgt, NULL);
1448 if (height < whgt)
1449 height = whgt;
1453 return (int)(height + (marginHeight << 1) + (shadowThickness << 1));
1456 void
1457 gui_mch_get_toolbar_colors(bgp, fgp, bsp, tsp, hsp)
1458 Pixel *bgp;
1459 Pixel *fgp;
1460 Pixel *bsp;
1461 Pixel *tsp;
1462 Pixel *hsp;
1464 XtVaGetValues(toolBar, XtNbackground, bgp, XtNborderColor, fgp, NULL);
1465 *bsp = *bgp;
1466 *tsp = *fgp;
1467 *hsp = *tsp;
1469 #endif
1472 void
1473 gui_mch_toggle_tearoffs(enable)
1474 int enable UNUSED;
1476 /* no tearoff menus */
1479 void
1480 gui_mch_new_menu_colors()
1482 if (menuBar == (Widget)0)
1483 return;
1484 if (gui.menu_fg_pixel != INVALCOLOR)
1485 XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL);
1486 gui_athena_menu_colors(menuBar);
1487 #ifdef FEAT_TOOLBAR
1488 gui_athena_menu_colors(toolBar);
1489 #endif
1491 gui_mch_submenu_change(root_menu, TRUE);
1495 * Destroy the machine specific menu widget.
1497 void
1498 gui_mch_destroy_menu(menu)
1499 vimmenu_T *menu;
1501 Widget parent;
1503 /* There is no item for the toolbar. */
1504 if (menu->id == (Widget)0)
1505 return;
1507 parent = XtParent(menu->id);
1509 /* When removing the last "pulldown" menu item from a pane, adjust the
1510 * right margins of the remaining widgets.
1512 if (menu->submenu_id != (Widget)0)
1514 /* Go through the menu items in the parent of this item and
1515 * adjust their margins, if necessary.
1516 * This takes care of the case when we delete the last menu item in a
1517 * pane that has a submenu. In this case, there will be no arrow
1518 * pixmaps shown anymore.
1521 WidgetList children;
1522 Cardinal num_children;
1523 int i;
1524 Dimension right_margin = 0;
1525 Boolean get_left_margin = False;
1527 XtVaGetValues(parent, XtNchildren, &children,
1528 XtNnumChildren, &num_children,
1529 NULL);
1530 if (gui_athena_menu_has_submenus(parent, menu->id))
1531 right_margin = puller_width;
1532 else
1533 get_left_margin = True;
1535 for (i = 0; i < (int)num_children; ++i)
1537 if (children[i] == menu->id)
1538 continue;
1539 if (get_left_margin == True)
1541 Dimension left_margin;
1543 XtVaGetValues(children[i], XtNleftMargin, &left_margin,
1544 NULL);
1545 XtVaSetValues(children[i], XtNrightMargin, left_margin,
1546 NULL);
1548 else
1549 XtVaSetValues(children[i], XtNrightMargin, right_margin,
1550 NULL);
1554 /* Please be sure to destroy the parent widget first (i.e. menu->id).
1556 * This code should be basically identical to that in the file gui_motif.c
1557 * because they are both Xt based.
1559 if (menu->id != (Widget)0)
1561 Cardinal num_children;
1562 Dimension height, space, border;
1564 XtVaGetValues(menuBar,
1565 XtNvSpace, &space,
1566 XtNborderWidth, &border,
1567 NULL);
1568 XtVaGetValues(menu->id,
1569 XtNheight, &height,
1570 NULL);
1571 #if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL)
1572 if (parent == toolBar && menu->tip != NULL)
1574 /* We try to destroy this before the actual menu, because there are
1575 * callbacks, etc. that will be unregistered during the tooltip
1576 * destruction.
1578 * If you call "gui_mch_destroy_beval_area()" after destroying
1579 * menu->id, then the tooltip's window will have already been
1580 * deallocated by Xt, and unknown behaviour will ensue (probably
1581 * a core dump).
1583 gui_mch_destroy_beval_area(menu->tip);
1584 menu->tip = NULL;
1586 #endif
1588 * This is a hack to stop the Athena simpleMenuWidget from getting a
1589 * BadValue error when a menu's last child is destroyed. We check to
1590 * see if this is the last child and if so, don't delete it. The parent
1591 * will be deleted soon anyway, and it will delete it's children like
1592 * all good widgets do.
1594 /* NOTE: The cause of the BadValue X Protocol Error is because when the
1595 * last child is destroyed, it is first unmanaged, thus causing a
1596 * geometry resize request from the parent Shell widget.
1597 * Since the Shell widget has no more children, it is resized to have
1598 * width/height of 0. XConfigureWindow() is then called with the
1599 * width/height of 0, which generates the BadValue.
1601 * This happens in phase two of the widget destruction process.
1604 if (parent != menuBar
1605 #ifdef FEAT_TOOLBAR
1606 && parent != toolBar
1607 #endif
1610 XtVaGetValues(parent, XtNnumChildren, &num_children, NULL);
1611 if (num_children > 1)
1612 XtDestroyWidget(menu->id);
1614 else
1615 XtDestroyWidget(menu->id);
1616 menu->id = (Widget)0;
1619 if (parent == menuBar)
1621 if (!gui.menu_height_fixed)
1622 gui.menu_height = height + 2 * (space + border);
1624 #ifdef FEAT_TOOLBAR
1625 else if (parent == toolBar)
1627 /* When removing last toolbar item, don't display the toolbar. */
1628 XtVaGetValues(toolBar, XtNnumChildren, &num_children, NULL);
1629 if (num_children == 0)
1630 gui_mch_show_toolbar(FALSE);
1631 else
1632 gui.toolbar_height = gui_mch_compute_toolbar_height();
1634 #endif
1636 if (menu->submenu_id != (Widget)0)
1638 XtDestroyWidget(menu->submenu_id);
1639 menu->submenu_id = (Widget)0;
1643 static void
1644 gui_athena_menu_timeout(client_data, id)
1645 XtPointer client_data;
1646 XtIntervalId *id UNUSED;
1648 Widget w = (Widget)client_data;
1649 Widget popup;
1651 timer = 0;
1652 if (XtIsSubclass(w,smeBSBObjectClass))
1654 Pixmap p;
1656 XtVaGetValues(w, XtNrightBitmap, &p, NULL);
1657 if ((p != None) && (p != XtUnspecifiedPixmap))
1659 /* We are dealing with an item that has a submenu */
1660 popup = get_popup_entry(XtParent(w));
1661 if (popup == (Widget)0)
1662 return;
1663 XtPopup(popup, XtGrabNonexclusive);
1668 /* This routine is used to calculate the position (in screen coordinates)
1669 * where a submenu should appear relative to the menu entry that popped it
1670 * up. It should appear even with and just slightly to the left of the
1671 * rightmost end of the menu entry that caused the popup.
1673 * This is called when XtPopup() is called.
1675 static void
1676 gui_athena_popup_callback(w, client_data, call_data)
1677 Widget w;
1678 XtPointer client_data;
1679 XtPointer call_data UNUSED;
1681 /* Assumption: XtIsSubclass(XtParent(w),simpleMenuWidgetClass) */
1682 vimmenu_T *menu = (vimmenu_T *)client_data;
1683 Dimension width;
1684 Position root_x, root_y;
1686 /* First, popdown any siblings that may have menus popped up */
1688 vimmenu_T *i;
1690 for (i = menu->parent->children; i != NULL; i = i->next)
1692 if (i->submenu_id != NULL && XtIsManaged(i->submenu_id))
1693 XtPopdown(i->submenu_id);
1696 XtVaGetValues(XtParent(w),
1697 XtNwidth, &width,
1698 NULL);
1699 /* Assumption: XawSimpleMenuGetActiveEntry(XtParent(w)) == menu->id */
1700 /* i.e. This IS the active entry */
1701 XtTranslateCoords(menu->id,width - 5, 0, &root_x, &root_y);
1702 XtVaSetValues(w, XtNx, root_x,
1703 XtNy, root_y,
1704 NULL);
1707 static void
1708 gui_athena_popdown_submenus_action(w, event, args, nargs)
1709 Widget w;
1710 XEvent *event;
1711 String *args;
1712 Cardinal *nargs;
1714 WidgetList children;
1715 Cardinal num_children;
1717 XtVaGetValues(w, XtNchildren, &children,
1718 XtNnumChildren, &num_children,
1719 NULL);
1720 for (; num_children > 0; --num_children)
1722 Widget child = children[num_children - 1];
1724 if (has_submenu(child))
1726 Widget temp_w;
1728 temp_w = submenu_widget(child);
1729 gui_athena_popdown_submenus_action(temp_w,event,args,nargs);
1730 XtPopdown(temp_w);
1735 /* Used to determine if the given widget has a submenu that can be popped up. */
1736 static Boolean
1737 has_submenu(widget)
1738 Widget widget;
1740 if ((widget != NULL) && XtIsSubclass(widget,smeBSBObjectClass))
1742 Pixmap p;
1744 XtVaGetValues(widget, XtNrightBitmap, &p, NULL);
1745 if ((p != None) && (p != XtUnspecifiedPixmap))
1746 return True;
1748 return False;
1751 static void
1752 gui_athena_delayed_arm_action(w, event, args, nargs)
1753 Widget w;
1754 XEvent *event;
1755 String *args;
1756 Cardinal *nargs;
1758 Dimension width, height;
1760 if (event->type != MotionNotify)
1761 return;
1763 XtVaGetValues(w,
1764 XtNwidth, &width,
1765 XtNheight, &height,
1766 NULL);
1768 if (event->xmotion.x >= (int)width || event->xmotion.y >= (int)height)
1769 return;
1772 static Widget previous_active_widget = NULL;
1773 Widget current;
1775 current = XawSimpleMenuGetActiveEntry(w);
1776 if (current != previous_active_widget)
1778 if (timer)
1780 /* If the timeout hasn't been triggered, remove it */
1781 XtRemoveTimeOut(timer);
1783 gui_athena_popdown_submenus_action(w,event,args,nargs);
1784 if (has_submenu(current))
1786 XtAppAddTimeOut(XtWidgetToApplicationContext(w), 600L,
1787 gui_athena_menu_timeout,
1788 (XtPointer)current);
1790 previous_active_widget = current;
1795 static Widget
1796 get_popup_entry(w)
1797 Widget w;
1799 Widget menuw;
1801 /* Get the active entry for the current menu */
1802 if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)0)
1803 return NULL;
1805 return submenu_widget(menuw);
1808 /* Given the widget that has been determined to have a submenu, return the submenu widget
1809 * that is to be popped up.
1811 static Widget
1812 submenu_widget(widget)
1813 Widget widget;
1815 /* Precondition: has_submenu(widget) == True
1816 * XtIsSubclass(XtParent(widget),simpleMenuWidgetClass) == True
1819 char_u *pullright_name;
1820 Widget popup;
1822 pullright_name = make_pull_name((char_u *)XtName(widget));
1823 popup = XtNameToWidget(XtParent(widget), (char *)pullright_name);
1824 vim_free(pullright_name);
1826 return popup;
1827 /* Postcondition: (popup != NULL) implies
1828 * (XtIsSubclass(popup,simpleMenuWidgetClass) == True) */
1831 void
1832 gui_mch_show_popupmenu(menu)
1833 vimmenu_T *menu;
1835 int rootx, rooty, winx, winy;
1836 Window root, child;
1837 unsigned int mask;
1839 if (menu->submenu_id == (Widget)0)
1840 return;
1842 /* Position the popup menu at the pointer */
1843 if (XQueryPointer(gui.dpy, XtWindow(vimShell), &root, &child,
1844 &rootx, &rooty, &winx, &winy, &mask))
1846 rootx -= 30;
1847 if (rootx < 0)
1848 rootx = 0;
1849 rooty -= 5;
1850 if (rooty < 0)
1851 rooty = 0;
1852 XtVaSetValues(menu->submenu_id,
1853 XtNx, rootx,
1854 XtNy, rooty,
1855 NULL);
1858 XtOverrideTranslations(menu->submenu_id, popupTrans);
1859 XtPopupSpringLoaded(menu->submenu_id);
1862 #endif /* FEAT_MENU */
1865 * Set the menu and scrollbar colors to their default values.
1867 void
1868 gui_mch_def_colors()
1871 * Get the colors ourselves. Using the automatic conversion doesn't
1872 * handle looking for approximate colors.
1874 if (gui.in_use)
1876 gui.menu_fg_pixel = gui_get_color((char_u *)gui.rsrc_menu_fg_name);
1877 gui.menu_bg_pixel = gui_get_color((char_u *)gui.rsrc_menu_bg_name);
1878 gui.scroll_fg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_fg_name);
1879 gui.scroll_bg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_bg_name);
1880 #ifdef FEAT_BEVAL
1881 gui.tooltip_fg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_fg_name);
1882 gui.tooltip_bg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_bg_name);
1883 #endif
1889 * Scrollbar stuff.
1892 void
1893 gui_mch_set_scrollbar_thumb(sb, val, size, max)
1894 scrollbar_T *sb;
1895 long val;
1896 long size;
1897 long max;
1899 double v, s;
1901 if (sb->id == (Widget)0)
1902 return;
1905 * Athena scrollbar must go from 0.0 to 1.0.
1907 if (max == 0)
1909 /* So you can't scroll it at all (normally it scrolls past end) */
1910 #ifdef FEAT_GUI_NEXTAW
1911 XawScrollbarSetThumb(sb->id, 0.0, 1.0);
1912 #else
1913 vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
1914 #endif
1916 else
1918 v = (double)val / (double)(max + 1);
1919 s = (double)size / (double)(max + 1);
1920 #ifdef FEAT_GUI_NEXTAW
1921 XawScrollbarSetThumb(sb->id, v, s);
1922 #else
1923 vim_XawScrollbarSetThumb(sb->id, v, s, 1.0);
1924 #endif
1928 void
1929 gui_mch_set_scrollbar_pos(sb, x, y, w, h)
1930 scrollbar_T *sb;
1931 int x;
1932 int y;
1933 int w;
1934 int h;
1936 if (sb->id == (Widget)0)
1937 return;
1939 XtUnmanageChild(sb->id);
1940 XtVaSetValues(sb->id,
1941 XtNhorizDistance, x,
1942 XtNvertDistance, y,
1943 XtNwidth, w,
1944 XtNheight, h,
1945 NULL);
1946 XtManageChild(sb->id);
1949 void
1950 gui_mch_enable_scrollbar(sb, flag)
1951 scrollbar_T *sb;
1952 int flag;
1954 if (sb->id != (Widget)0)
1956 if (flag)
1957 XtManageChild(sb->id);
1958 else
1959 XtUnmanageChild(sb->id);
1963 void
1964 gui_mch_create_scrollbar(sb, orient)
1965 scrollbar_T *sb;
1966 int orient; /* SBAR_VERT or SBAR_HORIZ */
1968 sb->id = XtVaCreateWidget("scrollBar",
1969 #ifdef FEAT_GUI_NEXTAW
1970 scrollbarWidgetClass, vimForm,
1971 #else
1972 vim_scrollbarWidgetClass, vimForm,
1973 #endif
1974 XtNresizable, True,
1975 XtNtop, XtChainTop,
1976 XtNbottom, XtChainTop,
1977 XtNleft, XtChainLeft,
1978 XtNright, XtChainLeft,
1979 XtNborderWidth, 0,
1980 XtNorientation, (orient == SBAR_VERT) ? XtorientVertical
1981 : XtorientHorizontal,
1982 XtNforeground, gui.scroll_fg_pixel,
1983 XtNbackground, gui.scroll_bg_pixel,
1984 NULL);
1985 if (sb->id == (Widget)0)
1986 return;
1988 XtAddCallback(sb->id, XtNjumpProc,
1989 gui_athena_scroll_cb_jump, (XtPointer)sb->ident);
1990 XtAddCallback(sb->id, XtNscrollProc,
1991 gui_athena_scroll_cb_scroll, (XtPointer)sb->ident);
1993 #ifdef FEAT_GUI_NEXTAW
1994 XawScrollbarSetThumb(sb->id, 0.0, 1.0);
1995 #else
1996 vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
1997 #endif
2000 #if defined(FEAT_WINDOWS) || defined(PROTO)
2001 void
2002 gui_mch_destroy_scrollbar(sb)
2003 scrollbar_T *sb;
2005 if (sb->id != (Widget)0)
2006 XtDestroyWidget(sb->id);
2008 #endif
2010 void
2011 gui_mch_set_scrollbar_colors(sb)
2012 scrollbar_T *sb;
2014 if (sb->id != (Widget)0)
2015 XtVaSetValues(sb->id,
2016 XtNforeground, gui.scroll_fg_pixel,
2017 XtNbackground, gui.scroll_bg_pixel,
2018 NULL);
2020 /* This is needed for the rectangle below the vertical scrollbars. */
2021 if (sb == &gui.bottom_sbar && vimForm != (Widget)0)
2022 gui_athena_scroll_colors(vimForm);
2026 * Miscellaneous stuff:
2028 Window
2029 gui_x11_get_wid()
2031 return XtWindow(textArea);
2034 #if defined(FEAT_BROWSE) || defined(PROTO)
2036 * Put up a file requester.
2037 * Returns the selected name in allocated memory, or NULL for Cancel.
2039 char_u *
2040 gui_mch_browse(saving, title, dflt, ext, initdir, filter)
2041 int saving UNUSED; /* select file to write */
2042 char_u *title; /* title for the window */
2043 char_u *dflt; /* default name */
2044 char_u *ext UNUSED; /* extension added */
2045 char_u *initdir; /* initial directory, NULL for current dir */
2046 char_u *filter UNUSED; /* file name filter */
2048 Position x, y;
2049 char_u dirbuf[MAXPATHL];
2051 /* Concatenate "initdir" and "dflt". */
2052 if (initdir == NULL || *initdir == NUL)
2053 mch_dirname(dirbuf, MAXPATHL);
2054 else if (STRLEN(initdir) + 2 < MAXPATHL)
2055 STRCPY(dirbuf, initdir);
2056 else
2057 dirbuf[0] = NUL;
2058 if (dflt != NULL && *dflt != NUL
2059 && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL)
2061 add_pathsep(dirbuf);
2062 STRCAT(dirbuf, dflt);
2065 /* Position the file selector just below the menubar */
2066 XtTranslateCoords(vimShell, (Position)0, (Position)
2067 #ifdef FEAT_MENU
2068 gui.menu_height
2069 #else
2071 #endif
2072 , &x, &y);
2073 return (char_u *)vim_SelFile(vimShell, (char *)title, (char *)dirbuf,
2074 NULL, (int)x, (int)y, gui.menu_fg_pixel, gui.menu_bg_pixel,
2075 gui.scroll_fg_pixel, gui.scroll_bg_pixel);
2077 #endif
2079 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2081 static int dialogStatus;
2082 static Atom dialogatom;
2084 static void keyhit_callback __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *cont));
2085 static void butproc __ARGS((Widget w, XtPointer client_data, XtPointer call_data));
2086 static void dialog_wm_handler __ARGS((Widget w, XtPointer client_data, XEvent *event, Boolean *dum));
2089 * Callback function for the textfield. When CR is hit this works like
2090 * hitting the "OK" button, ESC like "Cancel".
2092 static void
2093 keyhit_callback(w, client_data, event, cont)
2094 Widget w UNUSED;
2095 XtPointer client_data UNUSED;
2096 XEvent *event;
2097 Boolean *cont UNUSED;
2099 char buf[2];
2101 if (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1)
2103 if (*buf == CAR)
2104 dialogStatus = 1;
2105 else if (*buf == ESC)
2106 dialogStatus = 0;
2110 static void
2111 butproc(w, client_data, call_data)
2112 Widget w UNUSED;
2113 XtPointer client_data;
2114 XtPointer call_data UNUSED;
2116 dialogStatus = (int)(long)client_data + 1;
2120 * Function called when dialog window closed.
2122 static void
2123 dialog_wm_handler(w, client_data, event, dum)
2124 Widget w UNUSED;
2125 XtPointer client_data UNUSED;
2126 XEvent *event;
2127 Boolean *dum UNUSED;
2129 if (event->type == ClientMessage
2130 && (Atom)((XClientMessageEvent *)event)->data.l[0] == dialogatom)
2131 dialogStatus = 0;
2135 gui_mch_dialog(type, title, message, buttons, dfltbutton, textfield)
2136 int type UNUSED;
2137 char_u *title;
2138 char_u *message;
2139 char_u *buttons;
2140 int dfltbutton UNUSED;
2141 char_u *textfield;
2143 char_u *buts;
2144 char_u *p, *next;
2145 XtAppContext app;
2146 XEvent event;
2147 Position wd, hd;
2148 Position wv, hv;
2149 Position x, y;
2150 Widget dialog;
2151 Widget dialogshell;
2152 Widget dialogmessage;
2153 Widget dialogtextfield = 0;
2154 Widget dialogButton;
2155 Widget prev_dialogButton = NULL;
2156 int butcount;
2157 int vertical;
2159 if (title == NULL)
2160 title = (char_u *)_("Vim dialog");
2161 dialogStatus = -1;
2163 /* if our pointer is currently hidden, then we should show it. */
2164 gui_mch_mousehide(FALSE);
2166 /* Check 'v' flag in 'guioptions': vertical button placement. */
2167 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2169 /* The shell is created each time, to make sure it is resized properly */
2170 dialogshell = XtVaCreatePopupShell("dialogShell",
2171 transientShellWidgetClass, vimShell,
2172 XtNtitle, title,
2173 NULL);
2174 if (dialogshell == (Widget)0)
2175 goto error;
2177 dialog = XtVaCreateManagedWidget("dialog",
2178 formWidgetClass, dialogshell,
2179 XtNdefaultDistance, 20,
2180 NULL);
2181 if (dialog == (Widget)0)
2182 goto error;
2183 gui_athena_menu_colors(dialog);
2184 dialogmessage = XtVaCreateManagedWidget("dialogMessage",
2185 labelWidgetClass, dialog,
2186 XtNlabel, message,
2187 XtNtop, XtChainTop,
2188 XtNbottom, XtChainTop,
2189 XtNleft, XtChainLeft,
2190 XtNright, XtChainLeft,
2191 XtNresizable, True,
2192 XtNborderWidth, 0,
2193 NULL);
2194 gui_athena_menu_colors(dialogmessage);
2196 if (textfield != NULL)
2198 dialogtextfield = XtVaCreateManagedWidget("textfield",
2199 asciiTextWidgetClass, dialog,
2200 XtNwidth, 400,
2201 XtNtop, XtChainTop,
2202 XtNbottom, XtChainTop,
2203 XtNleft, XtChainLeft,
2204 XtNright, XtChainRight,
2205 XtNfromVert, dialogmessage,
2206 XtNresizable, True,
2207 XtNstring, textfield,
2208 XtNlength, IOSIZE,
2209 XtNuseStringInPlace, True,
2210 XtNeditType, XawtextEdit,
2211 XtNwrap, XawtextWrapNever,
2212 XtNresize, XawtextResizeHeight,
2213 NULL);
2214 XtManageChild(dialogtextfield);
2215 XtAddEventHandler(dialogtextfield, KeyPressMask, False,
2216 (XtEventHandler)keyhit_callback, (XtPointer)NULL);
2217 XawTextSetInsertionPoint(dialogtextfield,
2218 (XawTextPosition)STRLEN(textfield));
2219 XtSetKeyboardFocus(dialog, dialogtextfield);
2222 /* make a copy, so that we can insert NULs */
2223 buts = vim_strsave(buttons);
2224 if (buts == NULL)
2225 return -1;
2227 p = buts;
2228 for (butcount = 0; *p; ++butcount)
2230 for (next = p; *next; ++next)
2232 if (*next == DLG_HOTKEY_CHAR)
2233 STRMOVE(next, next + 1);
2234 if (*next == DLG_BUTTON_SEP)
2236 *next++ = NUL;
2237 break;
2240 dialogButton = XtVaCreateManagedWidget("button",
2241 commandWidgetClass, dialog,
2242 XtNlabel, p,
2243 XtNtop, XtChainBottom,
2244 XtNbottom, XtChainBottom,
2245 XtNleft, XtChainLeft,
2246 XtNright, XtChainLeft,
2247 XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield,
2248 XtNvertDistance, vertical ? 4 : 20,
2249 XtNresizable, False,
2250 NULL);
2251 gui_athena_menu_colors(dialogButton);
2252 if (butcount > 0)
2253 XtVaSetValues(dialogButton,
2254 vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton,
2255 NULL);
2257 XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)butcount);
2258 p = next;
2259 prev_dialogButton = dialogButton;
2261 vim_free(buts);
2263 XtRealizeWidget(dialogshell);
2265 /* Setup for catching the close-window event, don't let it close Vim! */
2266 dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
2267 XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1);
2268 XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL);
2270 XtVaGetValues(dialogshell,
2271 XtNwidth, &wd,
2272 XtNheight, &hd,
2273 NULL);
2274 XtVaGetValues(vimShell,
2275 XtNwidth, &wv,
2276 XtNheight, &hv,
2277 NULL);
2278 XtTranslateCoords(vimShell,
2279 (Position)((wv - wd) / 2),
2280 (Position)((hv - hd) / 2),
2281 &x, &y);
2282 if (x < 0)
2283 x = 0;
2284 if (y < 0)
2285 y = 0;
2286 XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL);
2288 /* Position the mouse pointer in the dialog, required for when focus
2289 * follows mouse. */
2290 XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40);
2293 app = XtWidgetToApplicationContext(dialogshell);
2295 XtPopup(dialogshell, XtGrabNonexclusive);
2297 for (;;)
2299 XtAppNextEvent(app, &event);
2300 XtDispatchEvent(&event);
2301 if (dialogStatus >= 0)
2302 break;
2305 XtPopdown(dialogshell);
2307 if (textfield != NULL && dialogStatus < 0)
2308 *textfield = NUL;
2310 error:
2311 XtDestroyWidget(dialogshell);
2313 return dialogStatus;
2315 #endif
2317 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
2319 * Set the colors of Widget "id" to the menu colors.
2321 static void
2322 gui_athena_menu_colors(id)
2323 Widget id;
2325 if (gui.menu_bg_pixel != INVALCOLOR)
2326 XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL);
2327 if (gui.menu_fg_pixel != INVALCOLOR)
2328 XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL);
2330 #endif
2333 * Set the colors of Widget "id" to the scroll colors.
2335 static void
2336 gui_athena_scroll_colors(id)
2337 Widget id;
2339 if (gui.scroll_bg_pixel != INVALCOLOR)
2340 XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL);
2341 if (gui.scroll_fg_pixel != INVALCOLOR)
2342 XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL);