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