blob: 28959786cd33550c7dc877039b87799f70958896 [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;
Bram Moolenaard25c16e2016-01-29 22:13:30 +010052static Cardinal athena_calculate_ins_pos(Widget);
Bram Moolenaar071d4272004-06-13 20:20:40 +000053
Bram Moolenaard25c16e2016-01-29 22:13:30 +010054static Pixmap gui_athena_create_pullright_pixmap(Widget);
55static void gui_athena_menu_timeout(XtPointer, XtIntervalId *);
56static void gui_athena_popup_callback(Widget, XtPointer, XtPointer);
57static void gui_athena_delayed_arm_action(Widget, XEvent *, String *,
58 Cardinal *);
59static void gui_athena_popdown_submenus_action(Widget, XEvent *,
60 String *, Cardinal *);
Bram Moolenaar071d4272004-06-13 20:20:40 +000061static 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
Bram Moolenaard25c16e2016-01-29 22:13:30 +010068static void gui_mch_reset_focus(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +000069static Widget toolBar = (Widget)0;
70#endif
71
Bram Moolenaard25c16e2016-01-29 22:13:30 +010072static void gui_athena_scroll_cb_jump(Widget, XtPointer, XtPointer);
73static void gui_athena_scroll_cb_scroll(Widget, XtPointer, XtPointer);
Bram Moolenaar071d4272004-06-13 20:20:40 +000074#if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
Bram Moolenaard25c16e2016-01-29 22:13:30 +010075static void gui_athena_menu_colors(Widget id);
Bram Moolenaar071d4272004-06-13 20:20:40 +000076#endif
Bram Moolenaard25c16e2016-01-29 22:13:30 +010077static void gui_athena_scroll_colors(Widget id);
Bram Moolenaar071d4272004-06-13 20:20:40 +000078
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
Bram Moolenaar66f948e2016-01-30 16:39:25 +010090gui_athena_scroll_cb_jump(
91 Widget w UNUSED,
92 XtPointer client_data, call_data)
Bram Moolenaar071d4272004-06-13 20:20:40 +000093{
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
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100125gui_athena_scroll_cb_scroll(
126 Widget w UNUSED,
127 XtPointer client_data, call_data)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000128{
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
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100231gui_x11_create_widgets(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232{
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
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100328gui_athena_create_pullright_pixmap(Widget w)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000329{
330 Pixmap retval;
331#ifdef FONTSET_ALWAYS
332 XFontSet font = None;
333#else
334 XFontStruct *font = NULL;
335#endif
336
337#ifdef FONTSET_ALWAYS
338 if (gui.menu_fontset == NOFONTSET)
339#else
340 if (gui.menu_font == NOFONT)
341#endif
342 {
343 XrmValue from, to;
344 WidgetList children;
345 Cardinal num_children;
346
347#ifdef FONTSET_ALWAYS
348 from.size = strlen(from.addr = XtDefaultFontSet);
349 to.addr = (XtPointer)&font;
350 to.size = sizeof(XFontSet);
351#else
352 from.size = strlen(from.addr = XtDefaultFont);
353 to.addr = (XtPointer)&font;
354 to.size = sizeof(XFontStruct *);
355#endif
356 /* Assumption: The menuBar children will use the same font as the
357 * pulldown menu items AND they will all be of type
358 * XtNfont.
359 */
360 XtVaGetValues(menuBar, XtNchildren, &children,
361 XtNnumChildren, &num_children,
362 NULL);
363 if (XtConvertAndStore(w ? w :
364 (num_children > 0) ? children[0] : menuBar,
365 XtRString, &from,
366#ifdef FONTSET_ALWAYS
367 XtRFontSet, &to
368#else
369 XtRFontStruct, &to
370#endif
371 ) == False)
372 return None;
373 /* "font" should now contain data */
374 }
375 else
376#ifdef FONTSET_ALWAYS
377 font = (XFontSet)gui.menu_fontset;
378#else
379 font = (XFontStruct *)gui.menu_font;
380#endif
381
382 {
383 int width, height;
384 GC draw_gc, undraw_gc;
385 XGCValues gc_values;
386 XPoint points[3];
387
388#ifdef FONTSET_ALWAYS
389 height = fontset_height2(font);
390#else
391 height = font->max_bounds.ascent + font->max_bounds.descent;
392#endif
393 width = height - 2;
394 puller_width = width + 4;
395 retval = XCreatePixmap(gui.dpy,DefaultRootWindow(gui.dpy),width,
396 height, 1);
397 gc_values.foreground = 1;
398 gc_values.background = 0;
399 draw_gc = XCreateGC(gui.dpy, retval,
400 GCForeground | GCBackground,
401 &gc_values);
402 gc_values.foreground = 0;
403 gc_values.background = 1;
404 undraw_gc = XCreateGC(gui.dpy, retval,
405 GCForeground | GCBackground,
406 &gc_values);
407 points[0].x = 0;
408 points[0].y = 0;
409 points[1].x = width - 1;
410 points[1].y = (height - 1) / 2;
411 points[2].x = 0;
412 points[2].y = height - 1;
413 XFillRectangle(gui.dpy, retval, undraw_gc, 0, 0, height, height);
414 XFillPolygon(gui.dpy, retval, draw_gc, points, XtNumber(points),
415 Convex, CoordModeOrigin);
416 XFreeGC(gui.dpy, draw_gc);
417 XFreeGC(gui.dpy, undraw_gc);
418 }
419 return retval;
420}
421#endif
422
423/*
424 * Called when the GUI is not going to start after all.
425 */
426 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100427gui_x11_destroy_widgets(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428{
429 textArea = NULL;
430#ifdef FEAT_MENU
431 menuBar = NULL;
432#endif
433#ifdef FEAT_TOOLBAR
434 toolBar = NULL;
435#endif
436}
437
438#if defined(FEAT_TOOLBAR) || defined(PROTO)
Bram Moolenaar34114692005-01-02 11:28:13 +0000439# include "gui_x11_pm.h"
440# ifdef HAVE_X11_XPM_H
441# include <X11/xpm.h>
442# endif
443
Bram Moolenaard25c16e2016-01-29 22:13:30 +0100444static void createXpmImages(char_u *path, char **xpm, Pixmap *sen);
445static void get_toolbar_pixmap(vimmenu_T *menu, Pixmap *sen);
Bram Moolenaar34114692005-01-02 11:28:13 +0000446
447/*
448 * Allocated a pixmap for toolbar menu "menu".
449 * Return in "sen".
450 */
451 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100452get_toolbar_pixmap(vimmenu_T *menu, Pixmap *sen)
Bram Moolenaar34114692005-01-02 11:28:13 +0000453{
454 char_u buf[MAXPATHL]; /* buffer storing expanded pathname */
455 char **xpm = NULL; /* xpm array */
456
457 buf[0] = NUL; /* start with NULL path */
458
459 if (menu->iconfile != NULL)
460 {
461 /* Use the "icon=" argument. */
462 gui_find_iconfile(menu->iconfile, buf, "xpm");
463 createXpmImages(buf, NULL, sen);
464
465 /* If it failed, try using the menu name. */
466 if (*sen == (Pixmap)0 && gui_find_bitmap(menu->name, buf, "xpm") == OK)
467 createXpmImages(buf, NULL, sen);
468 if (*sen != (Pixmap)0)
469 return;
470 }
471
472 if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL)
473 {
474 if (menu->iconidx >= 0 && menu->iconidx
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +0000475 < (int)(sizeof(built_in_pixmaps) / sizeof(built_in_pixmaps[0])))
Bram Moolenaar34114692005-01-02 11:28:13 +0000476 xpm = built_in_pixmaps[menu->iconidx];
477 else
478 xpm = tb_blank_xpm;
479 }
480
481 if (xpm != NULL || buf[0] != NUL)
482 createXpmImages(buf, xpm, sen);
483}
484
485/*
486 * Read an Xpm file, doing color substitutions for the foreground and
487 * background colors. If there is an error reading a color xpm file,
488 * drop back and read the monochrome file. If successful, create the
489 * insensitive Pixmap too.
490 */
491 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100492createXpmImages(char_u *path, char **xpm, Pixmap *sen)
Bram Moolenaar34114692005-01-02 11:28:13 +0000493{
494 Window rootWindow;
495 XpmAttributes attrs;
496 XpmColorSymbol color[5] =
497 {
498 {"none", "none", 0},
499 {"iconColor1", NULL, 0},
500 {"bottomShadowColor", NULL, 0},
501 {"topShadowColor", NULL, 0},
502 {"selectColor", NULL, 0}
503 };
504 int screenNum;
505 int status;
506 Pixmap mask;
507 Pixmap map;
508
509 gui_mch_get_toolbar_colors(
510 &color[BACKGROUND].pixel,
511 &color[FOREGROUND].pixel,
512 &color[BOTTOM_SHADOW].pixel,
513 &color[TOP_SHADOW].pixel,
514 &color[HIGHLIGHT].pixel);
515
Bram Moolenaar84a05ac2013-05-06 04:24:17 +0200516 /* Setup the color substitution table */
Bram Moolenaar34114692005-01-02 11:28:13 +0000517 attrs.valuemask = XpmColorSymbols;
518 attrs.colorsymbols = color;
519 attrs.numsymbols = 5;
520
521 screenNum = DefaultScreen(gui.dpy);
522 rootWindow = RootWindow(gui.dpy, screenNum);
523
524 /* Create the "sensitive" pixmap */
525 if (xpm != NULL)
526 status = XpmCreatePixmapFromData(gui.dpy, rootWindow, xpm,
527 &map, &mask, &attrs);
528 else
529 status = XpmReadFileToPixmap(gui.dpy, rootWindow, (char *)path,
530 &map, &mask, &attrs);
531 if (status == XpmSuccess && map != 0)
532 {
533 XGCValues gcvalues;
534 GC back_gc;
535 GC mask_gc;
536
537 /* Need to create new Pixmaps with the mask applied. */
538 gcvalues.foreground = color[BACKGROUND].pixel;
539 back_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues);
540 mask_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues);
541 XSetClipMask(gui.dpy, mask_gc, mask);
542
543 /* Create the "sensitive" pixmap. */
544 *sen = XCreatePixmap(gui.dpy, rootWindow,
545 attrs.width, attrs.height,
546 DefaultDepth(gui.dpy, screenNum));
547 XFillRectangle(gui.dpy, *sen, back_gc, 0, 0,
548 attrs.width, attrs.height);
549 XCopyArea(gui.dpy, map, *sen, mask_gc, 0, 0,
550 attrs.width, attrs.height, 0, 0);
551
552 XFreeGC(gui.dpy, back_gc);
553 XFreeGC(gui.dpy, mask_gc);
554 XFreePixmap(gui.dpy, map);
555 }
556 else
557 *sen = 0;
558
559 XpmFreeAttributes(&attrs);
560}
561
Bram Moolenaar071d4272004-06-13 20:20:40 +0000562 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100563gui_mch_set_toolbar_pos(
564 int x,
565 int y,
566 int w,
567 int h)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568{
569 Dimension border;
570 int height;
571
572 if (!XtIsManaged(toolBar)) /* nothing to do */
573 return;
574 XtUnmanageChild(toolBar);
575 XtVaGetValues(toolBar,
576 XtNborderWidth, &border,
577 NULL);
578 height = h - 2 * border;
579 if (height < 0)
580 height = 1;
581 XtVaSetValues(toolBar,
582 XtNhorizDistance, x,
583 XtNvertDistance, y,
584 XtNwidth, w - 2 * border,
585 XtNheight, height,
586 NULL);
587 XtManageChild(toolBar);
588}
589#endif
590
591 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100592gui_mch_set_text_area_pos(
593 int x,
594 int y,
595 int w,
596 int h)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000597{
598 XtUnmanageChild(textArea);
599 XtVaSetValues(textArea,
600 XtNhorizDistance, x,
601 XtNvertDistance, y,
602 XtNwidth, w,
603 XtNheight, h,
604 NULL);
605 XtManageChild(textArea);
606#ifdef FEAT_TOOLBAR
607 /* Give keyboard focus to the textArea instead of the toolbar. */
608 gui_mch_reset_focus();
609#endif
610}
611
612#ifdef FEAT_TOOLBAR
613/*
614 * A toolbar button has been pushed; now reset the input focus
615 * such that the user can type page up/down etc. and have the
616 * input go to the editor window, not the button
617 */
618 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100619gui_mch_reset_focus(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620{
621 XtSetKeyboardFocus(vimForm, textArea);
622}
623#endif
624
625
626 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100627gui_x11_set_back_color(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628{
629 if (textArea != NULL)
630 XtVaSetValues(textArea,
631 XtNbackground, gui.back_pixel,
632 NULL);
633}
634
635#if defined(FEAT_MENU) || defined(PROTO)
636/*
637 * Menu stuff.
638 */
639
Bram Moolenaard25c16e2016-01-29 22:13:30 +0100640static char_u *make_pull_name(char_u * name);
641static Widget get_popup_entry(Widget w);
642static Widget submenu_widget(Widget);
643static Boolean has_submenu(Widget);
644static void gui_mch_submenu_change(vimmenu_T *mp, int colors);
645static void gui_athena_menu_font(Widget id);
646static Boolean gui_athena_menu_has_submenus(Widget, Widget);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647
648 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100649gui_mch_enable_menu(int flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000650{
651 if (flag)
652 {
653 XtManageChild(menuBar);
654# ifdef FEAT_TOOLBAR
655 if (XtIsManaged(toolBar))
656 {
657 XtVaSetValues(toolBar,
658 XtNvertDistance, gui.menu_height,
659 NULL);
660 XtVaSetValues(textArea,
661 XtNvertDistance, gui.menu_height + gui.toolbar_height,
662 NULL);
663 }
664# endif
665 }
666 else
667 {
668 XtUnmanageChild(menuBar);
669# ifdef FEAT_TOOLBAR
670 if (XtIsManaged(toolBar))
671 {
672 XtVaSetValues(toolBar,
673 XtNvertDistance, 0,
674 NULL);
675 }
676# endif
677 }
678}
679
680 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100681gui_mch_set_menu_pos(
682 int x,
683 int y,
684 int w,
685 int h)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686{
687 Dimension border;
688 int height;
689
690 XtUnmanageChild(menuBar);
691 XtVaGetValues(menuBar, XtNborderWidth, &border, NULL);
692 /* avoid trouble when there are no menu items, and h is 1 */
693 height = h - 2 * border;
694 if (height < 0)
695 height = 1;
696 XtVaSetValues(menuBar,
697 XtNhorizDistance, x,
698 XtNvertDistance, y,
699 XtNwidth, w - 2 * border,
700 XtNheight, height,
701 NULL);
702 XtManageChild(menuBar);
703}
704
705/*
706 * Used to calculate the insertion position of a widget with respect to its
707 * neighbors.
708 *
709 * Valid range of return values is: 0 (beginning of children) to
710 * numChildren (end of children).
711 */
712 static Cardinal
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100713athena_calculate_ins_pos(Widget widget)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000714{
715 /* Assume that if the parent of the vimmenu_T is NULL, then we can get
716 * to this menu by traversing "next", starting at "root_menu".
717 *
718 * This holds true for popup menus, toolbar, and toplevel menu items.
719 */
720
721 /* Popup menus: "id" is NULL. Only submenu_id is valid */
722
723 /* Menus that are not toplevel: "parent" will be non-NULL, "id" &
724 * "submenu_id" will be non-NULL.
725 */
726
727 /* Toplevel menus: "parent" is NULL, id is the widget of the menu item */
728
729 WidgetList children;
730 Cardinal num_children = 0;
731 int retval;
732 Arg args[2];
733 int n = 0;
734 int i;
735
736 XtSetArg(args[n], XtNchildren, &children); n++;
737 XtSetArg(args[n], XtNnumChildren, &num_children); n++;
738 XtGetValues(XtParent(widget), args, n);
739
740 retval = num_children;
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +0000741 for (i = 0; i < (int)num_children; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 {
743 Widget current = children[i];
744 vimmenu_T *menu = NULL;
745
746 for (menu = (a_cur_menu->parent == NULL)
747 ? root_menu : a_cur_menu->parent->children;
748 menu != NULL;
749 menu = menu->next)
750 if (current == menu->id
751 && a_cur_menu->priority < menu->priority
752 && i < retval)
753 retval = i;
754 }
755 return retval;
756}
757
Bram Moolenaar071d4272004-06-13 20:20:40 +0000758 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100759gui_mch_add_menu(vimmenu_T *menu, int idx UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760{
761 char_u *pullright_name;
762 Dimension height, space, border;
763 vimmenu_T *parent = menu->parent;
764
765 a_cur_menu = menu;
766 if (parent == NULL)
767 {
768 if (menu_is_popup(menu->dname))
769 {
770 menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
771 simpleMenuWidgetClass, vimShell,
772 XtNinsertPosition, athena_calculate_ins_pos,
773 XtNtranslations, popupTrans,
774 NULL);
775 gui_athena_menu_colors(menu->submenu_id);
776 }
777 else if (menu_is_menubar(menu->dname))
778 {
779 menu->id = XtVaCreateManagedWidget((char *)menu->dname,
780 menuButtonWidgetClass, menuBar,
781 XtNmenuName, menu->dname,
782#ifdef FONTSET_ALWAYS
783 XtNinternational, True,
784#endif
785 NULL);
786 if (menu->id == (Widget)0)
787 return;
788 gui_athena_menu_colors(menu->id);
789 gui_athena_menu_font(menu->id);
790
791 menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname,
792 simpleMenuWidgetClass, menu->id,
793 XtNinsertPosition, athena_calculate_ins_pos,
794 XtNtranslations, supermenuTrans,
795 NULL);
796 gui_athena_menu_colors(menu->submenu_id);
797 gui_athena_menu_font(menu->submenu_id);
798
799 /* Don't update the menu height when it was set at a fixed value */
800 if (!gui.menu_height_fixed)
801 {
802 /*
803 * When we add a top-level item to the menu bar, we can figure
804 * out how high the menu bar should be.
805 */
806 XtVaGetValues(menuBar,
807 XtNvSpace, &space,
808 XtNborderWidth, &border,
809 NULL);
810 XtVaGetValues(menu->id,
811 XtNheight, &height,
812 NULL);
813 gui.menu_height = height + 2 * (space + border);
814 }
815 }
816 }
817 else if (parent->submenu_id != (Widget)0)
818 {
819 menu->id = XtVaCreateManagedWidget((char *)menu->dname,
820 smeBSBObjectClass, parent->submenu_id,
821 XtNlabel, menu->dname,
822#ifdef FONTSET_ALWAYS
823 XtNinternational, True,
824#endif
825 NULL);
826 if (menu->id == (Widget)0)
827 return;
828 if (pullerBitmap == None)
829 pullerBitmap = gui_athena_create_pullright_pixmap(menu->id);
830
831 XtVaSetValues(menu->id, XtNrightBitmap, pullerBitmap,
832 NULL);
833 /* If there are other menu items that are not pulldown menus,
834 * we need to adjust the right margins of those, too.
835 */
836 {
837 WidgetList children;
838 Cardinal num_children;
839 int i;
840
841 XtVaGetValues(parent->submenu_id, XtNchildren, &children,
842 XtNnumChildren, &num_children,
843 NULL);
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +0000844 for (i = 0; i < (int)num_children; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000845 {
846 XtVaSetValues(children[i],
847 XtNrightMargin, puller_width,
848 NULL);
849 }
850 }
851 gui_athena_menu_colors(menu->id);
852 gui_athena_menu_font(menu->id);
853
854 pullright_name = make_pull_name(menu->dname);
855 menu->submenu_id = XtVaCreatePopupShell((char *)pullright_name,
856 simpleMenuWidgetClass, parent->submenu_id,
857 XtNtranslations, menuTrans,
858 NULL);
859 gui_athena_menu_colors(menu->submenu_id);
860 gui_athena_menu_font(menu->submenu_id);
861 vim_free(pullright_name);
862 XtAddCallback(menu->submenu_id, XtNpopupCallback,
863 gui_athena_popup_callback, (XtPointer)menu);
864
865 if (parent->parent != NULL)
866 XtOverrideTranslations(parent->submenu_id, parentTrans);
867 }
868 a_cur_menu = NULL;
869}
870
871/* Used to determine whether a SimpleMenu has pulldown entries.
872 *
873 * "id" is the parent of the menu items.
874 * Ignore widget "ignore" in the pane.
875 */
876 static Boolean
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100877gui_athena_menu_has_submenus(Widget id, Widget ignore)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878{
879 WidgetList children;
880 Cardinal num_children;
881 int i;
882
883 XtVaGetValues(id, XtNchildren, &children,
884 XtNnumChildren, &num_children,
885 NULL);
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +0000886 for (i = 0; i < (int)num_children; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000887 {
888 if (children[i] == ignore)
889 continue;
890 if (has_submenu(children[i]))
891 return True;
892 }
893 return False;
894}
895
896 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100897gui_athena_menu_font(Widget id)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000898{
899#ifdef FONTSET_ALWAYS
900 if (gui.menu_fontset != NOFONTSET)
901 {
902 if (XtIsManaged(id))
903 {
904 XtUnmanageChild(id);
905 XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
906 /* We should force the widget to recalculate it's
907 * geometry now. */
908 XtManageChild(id);
909 }
910 else
911 XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL);
912 if (has_submenu(id))
913 XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
914 }
915#else
916 int managed = FALSE;
917
918 if (gui.menu_font != NOFONT)
919 {
920 if (XtIsManaged(id))
921 {
922 XtUnmanageChild(id);
923 managed = TRUE;
924 }
925
926# ifdef FEAT_XFONTSET
927 if (gui.fontset != NOFONTSET)
928 XtVaSetValues(id, XtNfontSet, gui.menu_font, NULL);
929 else
930# endif
931 XtVaSetValues(id, XtNfont, gui.menu_font, NULL);
932 if (has_submenu(id))
933 XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL);
934
935 /* Force the widget to recalculate it's geometry now. */
936 if (managed)
937 XtManageChild(id);
938 }
939#endif
940}
941
942
943 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100944gui_mch_new_menu_font(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000945{
946 Pixmap oldpuller = None;
947
948 if (menuBar == (Widget)0)
949 return;
950
951 if (pullerBitmap != None)
952 {
953 oldpuller = pullerBitmap;
954 pullerBitmap = gui_athena_create_pullright_pixmap(NULL);
955 }
956 gui_mch_submenu_change(root_menu, FALSE);
957
958 {
959 /* Iterate through the menubar menu items and get the height of
960 * each one. The menu bar height is set to the maximum of all
961 * the heights.
962 */
963 vimmenu_T *mp;
964 int max_height = 9999;
965
966 for (mp = root_menu; mp != NULL; mp = mp->next)
967 {
968 if (menu_is_menubar(mp->dname))
969 {
970 Dimension height;
971
972 XtVaGetValues(mp->id,
Bram Moolenaar623fd5e2005-01-25 21:42:15 +0000973 XtNheight, &height,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000974 NULL);
975 if (height < max_height)
976 max_height = height;
977 }
978 }
979 if (max_height != 9999)
980 {
981 /* Don't update the menu height when it was set at a fixed value */
982 if (!gui.menu_height_fixed)
983 {
984 Dimension space, border;
985
986 XtVaGetValues(menuBar,
987 XtNvSpace, &space,
988 XtNborderWidth, &border,
989 NULL);
990 gui.menu_height = max_height + 2 * (space + border);
991 }
992 }
993 }
994 /* Now, to simulate the window being resized. Only, this
995 * will resize the window to it's current state.
996 *
997 * There has to be a better way, but I do not see one at this time.
998 * (David Harrison)
999 */
1000 {
1001 Position w, h;
1002
1003 XtVaGetValues(vimShell,
1004 XtNwidth, &w,
1005 XtNheight, &h,
1006 NULL);
1007 gui_resize_shell(w, h
1008#ifdef FEAT_XIM
1009 - xim_get_status_area_height()
1010#endif
1011 );
1012 }
Bram Moolenaar2e2a2812006-03-27 20:55:21 +00001013 gui_set_shellsize(FALSE, TRUE, RESIZE_VERT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001014 ui_new_shellsize();
1015 if (oldpuller != None)
1016 XFreePixmap(gui.dpy, oldpuller);
1017}
1018
1019#if defined(FEAT_BEVAL) || defined(PROTO)
1020 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001021gui_mch_new_tooltip_font(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022{
1023# ifdef FEAT_TOOLBAR
1024 vimmenu_T *menu;
1025
1026 if (toolBar == (Widget)0)
1027 return;
1028
1029 menu = gui_find_menu((char_u *)"ToolBar");
1030 if (menu != NULL)
1031 gui_mch_submenu_change(menu, FALSE);
1032# endif
1033}
1034
1035 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001036gui_mch_new_tooltip_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037{
1038# ifdef FEAT_TOOLBAR
1039 vimmenu_T *menu;
1040
1041 if (toolBar == (Widget)0)
1042 return;
1043
1044 menu = gui_find_menu((char_u *)"ToolBar");
1045 if (menu != NULL)
1046 gui_mch_submenu_change(menu, TRUE);
1047# endif
1048}
1049#endif
1050
1051 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001052gui_mch_submenu_change(
1053 vimmenu_T *menu,
1054 int colors) /* TRUE for colors, FALSE for font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055{
1056 vimmenu_T *mp;
1057
1058 for (mp = menu; mp != NULL; mp = mp->next)
1059 {
1060 if (mp->id != (Widget)0)
1061 {
1062 if (colors)
1063 {
1064 gui_athena_menu_colors(mp->id);
1065#ifdef FEAT_TOOLBAR
1066 /* For a toolbar item: Free the pixmap and allocate a new one,
1067 * so that the background color is right. */
1068 if (mp->image != (Pixmap)0)
1069 {
1070 XFreePixmap(gui.dpy, mp->image);
Bram Moolenaar34114692005-01-02 11:28:13 +00001071 get_toolbar_pixmap(mp, &mp->image);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001072 if (mp->image != (Pixmap)0)
1073 XtVaSetValues(mp->id, XtNbitmap, mp->image, NULL);
1074 }
1075
1076# ifdef FEAT_BEVAL
1077 /* If we have a tooltip, then we need to change it's colors */
1078 if (mp->tip != NULL)
1079 {
1080 Arg args[2];
1081
1082 args[0].name = XtNbackground;
1083 args[0].value = gui.tooltip_bg_pixel;
1084 args[1].name = XtNforeground;
1085 args[1].value = gui.tooltip_fg_pixel;
1086 XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1087 }
1088# endif
1089#endif
1090 }
1091 else
1092 {
1093 gui_athena_menu_font(mp->id);
1094#ifdef FEAT_BEVAL
1095 /* If we have a tooltip, then we need to change it's font */
1096 /* Assume XtNinternational == True (in createBalloonEvalWindow)
1097 */
1098 if (mp->tip != NULL)
1099 {
1100 Arg args[1];
1101
1102 args[0].name = XtNfontSet;
1103 args[0].value = (XtArgVal)gui.tooltip_fontset;
1104 XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args));
1105 }
1106#endif
1107 }
1108 }
1109
1110 if (mp->children != NULL)
1111 {
1112 /* Set the colors/font for the tear off widget */
1113 if (mp->submenu_id != (Widget)0)
1114 {
1115 if (colors)
1116 gui_athena_menu_colors(mp->submenu_id);
1117 else
1118 gui_athena_menu_font(mp->submenu_id);
1119 }
1120 /* Set the colors for the children */
1121 gui_mch_submenu_change(mp->children, colors);
1122 }
1123 }
1124}
1125
1126/*
1127 * Make a submenu name into a pullright name.
1128 * Replace '.' by '_', can't include '.' in the submenu name.
1129 */
1130 static char_u *
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001131make_pull_name(char_u * name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001132{
1133 char_u *pname;
1134 char_u *p;
1135
1136 pname = vim_strnsave(name, STRLEN(name) + strlen("-pullright"));
1137 if (pname != NULL)
1138 {
1139 strcat((char *)pname, "-pullright");
1140 while ((p = vim_strchr(pname, '.')) != NULL)
1141 *p = '_';
1142 }
1143 return pname;
1144}
1145
Bram Moolenaar071d4272004-06-13 20:20:40 +00001146 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001147gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001148{
1149 vimmenu_T *parent = menu->parent;
1150
1151 a_cur_menu = menu;
1152# ifdef FEAT_TOOLBAR
1153 if (menu_is_toolbar(parent->name))
1154 {
1155 WidgetClass type;
1156 int n;
1157 Arg args[21];
1158
1159 n = 0;
1160 if (menu_is_separator(menu->name))
1161 {
1162 XtSetArg(args[n], XtNlabel, ""); n++;
1163 XtSetArg(args[n], XtNborderWidth, 0); n++;
1164 }
1165 else
1166 {
Bram Moolenaar34114692005-01-02 11:28:13 +00001167 get_toolbar_pixmap(menu, &menu->image);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 XtSetArg(args[n], XtNlabel, menu->dname); n++;
1169 XtSetArg(args[n], XtNinternalHeight, 1); n++;
1170 XtSetArg(args[n], XtNinternalWidth, 1); n++;
1171 XtSetArg(args[n], XtNborderWidth, 1); n++;
1172 if (menu->image != 0)
1173 XtSetArg(args[n], XtNbitmap, menu->image); n++;
1174 }
1175 XtSetArg(args[n], XtNhighlightThickness, 0); n++;
1176 type = commandWidgetClass;
1177 /* TODO: figure out the position in the toolbar?
1178 * This currently works fine for the default toolbar, but
1179 * what if we add/remove items during later runtime?
1180 */
1181
1182 /* NOTE: "idx" isn't used here. The position is calculated by
1183 * athena_calculate_ins_pos(). The position it calculates
1184 * should be equal to "idx".
1185 */
1186 /* TODO: Could we just store "idx" and use that as the child
1187 * placement?
1188 */
1189
1190 if (menu->id == NULL)
1191 {
1192 menu->id = XtCreateManagedWidget((char *)menu->dname,
1193 type, toolBar, args, n);
1194 XtAddCallback(menu->id,
1195 XtNcallback, gui_x11_menu_cb, menu);
1196 }
1197 else
1198 XtSetValues(menu->id, args, n);
1199 gui_athena_menu_colors(menu->id);
1200
1201#ifdef FEAT_BEVAL
1202 gui_mch_menu_set_tip(menu);
1203#endif
1204
1205 menu->parent = parent;
1206 menu->submenu_id = NULL;
1207 if (!XtIsManaged(toolBar)
1208 && vim_strchr(p_go, GO_TOOLBAR) != NULL)
1209 gui_mch_show_toolbar(TRUE);
1210 gui.toolbar_height = gui_mch_compute_toolbar_height();
1211 return;
1212 } /* toolbar menu item */
1213# endif
1214
1215 /* Add menu separator */
1216 if (menu_is_separator(menu->name))
1217 {
1218 menu->submenu_id = (Widget)0;
1219 menu->id = XtVaCreateManagedWidget((char *)menu->dname,
1220 smeLineObjectClass, parent->submenu_id,
1221 NULL);
1222 if (menu->id == (Widget)0)
1223 return;
1224 gui_athena_menu_colors(menu->id);
1225 }
1226 else
1227 {
1228 if (parent != NULL && parent->submenu_id != (Widget)0)
1229 {
1230 menu->submenu_id = (Widget)0;
1231 menu->id = XtVaCreateManagedWidget((char *)menu->dname,
1232 smeBSBObjectClass, parent->submenu_id,
1233 XtNlabel, menu->dname,
1234#ifdef FONTSET_ALWAYS
1235 XtNinternational, True,
1236#endif
1237 NULL);
1238 if (menu->id == (Widget)0)
1239 return;
1240
1241 /* If there are other "pulldown" items in this pane, then adjust
Bram Moolenaar9d6650f2010-06-06 23:04:47 +02001242 * the right margin to accommodate the arrow pixmap, otherwise
Bram Moolenaar071d4272004-06-13 20:20:40 +00001243 * the right margin will be the same as the left margin.
1244 */
1245 {
1246 Dimension left_margin;
1247
1248 XtVaGetValues(menu->id, XtNleftMargin, &left_margin, NULL);
1249 XtVaSetValues(menu->id, XtNrightMargin,
1250 gui_athena_menu_has_submenus(parent->submenu_id, NULL) ?
1251 puller_width :
1252 left_margin,
1253 NULL);
1254 }
1255
1256 gui_athena_menu_colors(menu->id);
1257 gui_athena_menu_font(menu->id);
1258 XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb,
1259 (XtPointer)menu);
1260 }
1261 }
1262 a_cur_menu = NULL;
1263}
1264
1265#if defined(FEAT_TOOLBAR) || defined(PROTO)
1266 void
1267gui_mch_show_toolbar(int showit)
1268{
1269 Cardinal numChildren; /* how many children toolBar has */
1270
1271 if (toolBar == (Widget)0)
1272 return;
1273 XtVaGetValues(toolBar, XtNnumChildren, &numChildren, NULL);
1274 if (showit && numChildren > 0)
1275 {
1276 /* Assume that we want to show the toolbar if p_toolbar contains valid
1277 * option settings, therefore p_toolbar must not be NULL.
1278 */
1279 WidgetList children;
1280
1281 XtVaGetValues(toolBar, XtNchildren, &children, NULL);
1282 {
1283 void (*action)(BalloonEval *);
1284 int text = 0;
1285
1286 if (strstr((const char *)p_toolbar, "tooltips"))
1287 action = &gui_mch_enable_beval_area;
1288 else
1289 action = &gui_mch_disable_beval_area;
1290 if (strstr((const char *)p_toolbar, "text"))
1291 text = 1;
1292 else if (strstr((const char *)p_toolbar, "icons"))
1293 text = -1;
1294 if (text != 0)
1295 {
1296 vimmenu_T *toolbar;
1297 vimmenu_T *cur;
1298
1299 for (toolbar = root_menu; toolbar; toolbar = toolbar->next)
1300 if (menu_is_toolbar(toolbar->dname))
1301 break;
1302 /* Assumption: toolbar is NULL if there is no toolbar,
1303 * otherwise it contains the toolbar menu structure.
1304 *
1305 * Assumption: "numChildren" == the number of items in the list
1306 * of items beginning with toolbar->children.
1307 */
1308 if (toolbar)
1309 {
1310 for (cur = toolbar->children; cur; cur = cur->next)
1311 {
1312 Arg args[2];
1313 int n = 0;
1314
1315 /* Enable/Disable tooltip (OK to enable while currently
1316 * enabled)
1317 */
1318 if (cur->tip != NULL)
1319 (*action)(cur->tip);
1320 if (text == 1)
1321 {
1322 XtSetArg(args[n], XtNbitmap, None);
1323 n++;
1324 XtSetArg(args[n], XtNlabel,
1325 menu_is_separator(cur->name) ? "" :
1326 (char *)cur->dname);
1327 n++;
1328 }
1329 else
1330 {
1331 XtSetArg(args[n], XtNbitmap, cur->image);
1332 n++;
1333 XtSetArg(args[n], XtNlabel, (cur->image == None) ?
1334 menu_is_separator(cur->name) ?
1335 "" :
1336 (char *)cur->dname
1337 :
1338 (char *)None);
1339 n++;
1340 }
1341 if (cur->id != NULL)
1342 {
1343 XtUnmanageChild(cur->id);
1344 XtSetValues(cur->id, args, n);
1345 XtManageChild(cur->id);
1346 }
1347 }
1348 }
1349 }
1350 }
1351 gui.toolbar_height = gui_mch_compute_toolbar_height();
1352 XtManageChild(toolBar);
1353 if (XtIsManaged(menuBar))
1354 {
1355 XtVaSetValues(textArea,
1356 XtNvertDistance, gui.toolbar_height + gui.menu_height,
1357 NULL);
1358 XtVaSetValues(toolBar,
1359 XtNvertDistance, gui.menu_height,
1360 NULL);
1361 }
1362 else
1363 {
1364 XtVaSetValues(textArea,
1365 XtNvertDistance, gui.toolbar_height,
1366 NULL);
1367 XtVaSetValues(toolBar,
1368 XtNvertDistance, 0,
1369 NULL);
1370 }
1371 }
1372 else
1373 {
1374 gui.toolbar_height = 0;
1375 if (XtIsManaged(menuBar))
1376 XtVaSetValues(textArea,
1377 XtNvertDistance, gui.menu_height,
1378 NULL);
1379 else
1380 XtVaSetValues(textArea,
1381 XtNvertDistance, 0,
1382 NULL);
1383
1384 XtUnmanageChild(toolBar);
1385 }
Bram Moolenaar2e2a2812006-03-27 20:55:21 +00001386 gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387}
1388
1389
1390 int
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001391gui_mch_compute_toolbar_height(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001392{
1393 Dimension height; /* total Toolbar height */
1394 Dimension whgt; /* height of each widget */
1395 Dimension marginHeight; /* XmNmarginHeight of toolBar */
1396 Dimension shadowThickness; /* thickness of Xtparent(toolBar) */
1397 WidgetList children; /* list of toolBar's children */
1398 Cardinal numChildren; /* how many children toolBar has */
1399 int i;
1400
1401 height = 0;
1402 shadowThickness = 0;
1403 marginHeight = 0;
1404 if (toolBar != (Widget)0)
1405 {
1406 XtVaGetValues(toolBar,
1407 XtNborderWidth, &shadowThickness,
1408 XtNvSpace, &marginHeight,
1409 XtNchildren, &children,
1410 XtNnumChildren, &numChildren,
1411 NULL);
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00001412 for (i = 0; i < (int)numChildren; i++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413 {
1414 whgt = 0;
1415
1416 XtVaGetValues(children[i], XtNheight, &whgt, NULL);
1417 if (height < whgt)
1418 height = whgt;
1419 }
1420 }
1421
1422 return (int)(height + (marginHeight << 1) + (shadowThickness << 1));
1423}
1424
1425 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001426gui_mch_get_toolbar_colors(
1427 Pixel *bgp,
1428 Pixel *fgp,
1429 Pixel *bsp,
1430 Pixel *tsp,
1431 Pixel *hsp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432{
1433 XtVaGetValues(toolBar, XtNbackground, bgp, XtNborderColor, fgp, NULL);
1434 *bsp = *bgp;
1435 *tsp = *fgp;
1436 *hsp = *tsp;
1437}
1438#endif
1439
1440
Bram Moolenaar071d4272004-06-13 20:20:40 +00001441 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001442gui_mch_toggle_tearoffs(int enable UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001443{
1444 /* no tearoff menus */
1445}
1446
1447 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001448gui_mch_new_menu_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449{
1450 if (menuBar == (Widget)0)
1451 return;
1452 if (gui.menu_fg_pixel != INVALCOLOR)
1453 XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL);
1454 gui_athena_menu_colors(menuBar);
1455#ifdef FEAT_TOOLBAR
1456 gui_athena_menu_colors(toolBar);
1457#endif
1458
1459 gui_mch_submenu_change(root_menu, TRUE);
1460}
1461
1462/*
1463 * Destroy the machine specific menu widget.
1464 */
1465 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001466gui_mch_destroy_menu(vimmenu_T *menu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001467{
1468 Widget parent;
1469
1470 /* There is no item for the toolbar. */
1471 if (menu->id == (Widget)0)
1472 return;
1473
1474 parent = XtParent(menu->id);
1475
1476 /* When removing the last "pulldown" menu item from a pane, adjust the
1477 * right margins of the remaining widgets.
1478 */
1479 if (menu->submenu_id != (Widget)0)
1480 {
1481 /* Go through the menu items in the parent of this item and
1482 * adjust their margins, if necessary.
1483 * This takes care of the case when we delete the last menu item in a
1484 * pane that has a submenu. In this case, there will be no arrow
1485 * pixmaps shown anymore.
1486 */
1487 {
1488 WidgetList children;
1489 Cardinal num_children;
1490 int i;
1491 Dimension right_margin = 0;
1492 Boolean get_left_margin = False;
1493
1494 XtVaGetValues(parent, XtNchildren, &children,
1495 XtNnumChildren, &num_children,
1496 NULL);
1497 if (gui_athena_menu_has_submenus(parent, menu->id))
1498 right_margin = puller_width;
1499 else
1500 get_left_margin = True;
1501
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00001502 for (i = 0; i < (int)num_children; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001503 {
1504 if (children[i] == menu->id)
1505 continue;
1506 if (get_left_margin == True)
1507 {
1508 Dimension left_margin;
1509
1510 XtVaGetValues(children[i], XtNleftMargin, &left_margin,
1511 NULL);
1512 XtVaSetValues(children[i], XtNrightMargin, left_margin,
1513 NULL);
1514 }
1515 else
1516 XtVaSetValues(children[i], XtNrightMargin, right_margin,
1517 NULL);
1518 }
1519 }
1520 }
1521 /* Please be sure to destroy the parent widget first (i.e. menu->id).
1522 *
1523 * This code should be basically identical to that in the file gui_motif.c
1524 * because they are both Xt based.
1525 */
1526 if (menu->id != (Widget)0)
1527 {
1528 Cardinal num_children;
1529 Dimension height, space, border;
1530
1531 XtVaGetValues(menuBar,
1532 XtNvSpace, &space,
1533 XtNborderWidth, &border,
1534 NULL);
1535 XtVaGetValues(menu->id,
1536 XtNheight, &height,
1537 NULL);
1538#if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL)
1539 if (parent == toolBar && menu->tip != NULL)
1540 {
1541 /* We try to destroy this before the actual menu, because there are
1542 * callbacks, etc. that will be unregistered during the tooltip
1543 * destruction.
1544 *
1545 * If you call "gui_mch_destroy_beval_area()" after destroying
1546 * menu->id, then the tooltip's window will have already been
1547 * deallocated by Xt, and unknown behaviour will ensue (probably
1548 * a core dump).
1549 */
1550 gui_mch_destroy_beval_area(menu->tip);
1551 menu->tip = NULL;
1552 }
1553#endif
1554 /*
1555 * This is a hack to stop the Athena simpleMenuWidget from getting a
1556 * BadValue error when a menu's last child is destroyed. We check to
1557 * see if this is the last child and if so, don't delete it. The parent
1558 * will be deleted soon anyway, and it will delete it's children like
1559 * all good widgets do.
1560 */
1561 /* NOTE: The cause of the BadValue X Protocol Error is because when the
1562 * last child is destroyed, it is first unmanaged, thus causing a
1563 * geometry resize request from the parent Shell widget.
1564 * Since the Shell widget has no more children, it is resized to have
1565 * width/height of 0. XConfigureWindow() is then called with the
1566 * width/height of 0, which generates the BadValue.
1567 *
1568 * This happens in phase two of the widget destruction process.
1569 */
1570 {
1571 if (parent != menuBar
1572#ifdef FEAT_TOOLBAR
1573 && parent != toolBar
1574#endif
1575 )
1576 {
1577 XtVaGetValues(parent, XtNnumChildren, &num_children, NULL);
1578 if (num_children > 1)
1579 XtDestroyWidget(menu->id);
1580 }
1581 else
1582 XtDestroyWidget(menu->id);
1583 menu->id = (Widget)0;
1584 }
1585
1586 if (parent == menuBar)
1587 {
1588 if (!gui.menu_height_fixed)
1589 gui.menu_height = height + 2 * (space + border);
1590 }
1591#ifdef FEAT_TOOLBAR
1592 else if (parent == toolBar)
1593 {
1594 /* When removing last toolbar item, don't display the toolbar. */
1595 XtVaGetValues(toolBar, XtNnumChildren, &num_children, NULL);
1596 if (num_children == 0)
1597 gui_mch_show_toolbar(FALSE);
1598 else
1599 gui.toolbar_height = gui_mch_compute_toolbar_height();
1600 }
1601#endif
1602 }
1603 if (menu->submenu_id != (Widget)0)
1604 {
1605 XtDestroyWidget(menu->submenu_id);
1606 menu->submenu_id = (Widget)0;
1607 }
1608}
1609
Bram Moolenaar071d4272004-06-13 20:20:40 +00001610 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001611gui_athena_menu_timeout(
1612 XtPointer client_data,
1613 XtIntervalId *id UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001614{
1615 Widget w = (Widget)client_data;
1616 Widget popup;
1617
1618 timer = 0;
1619 if (XtIsSubclass(w,smeBSBObjectClass))
1620 {
1621 Pixmap p;
1622
1623 XtVaGetValues(w, XtNrightBitmap, &p, NULL);
1624 if ((p != None) && (p != XtUnspecifiedPixmap))
1625 {
1626 /* We are dealing with an item that has a submenu */
1627 popup = get_popup_entry(XtParent(w));
1628 if (popup == (Widget)0)
1629 return;
1630 XtPopup(popup, XtGrabNonexclusive);
1631 }
1632 }
1633}
1634
1635/* This routine is used to calculate the position (in screen coordinates)
1636 * where a submenu should appear relative to the menu entry that popped it
1637 * up. It should appear even with and just slightly to the left of the
1638 * rightmost end of the menu entry that caused the popup.
1639 *
1640 * This is called when XtPopup() is called.
1641 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001642 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001643gui_athena_popup_callback(
1644 Widget w,
1645 XtPointer client_data,
1646 XtPointer call_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001647{
1648 /* Assumption: XtIsSubclass(XtParent(w),simpleMenuWidgetClass) */
1649 vimmenu_T *menu = (vimmenu_T *)client_data;
1650 Dimension width;
1651 Position root_x, root_y;
1652
1653 /* First, popdown any siblings that may have menus popped up */
1654 {
1655 vimmenu_T *i;
1656
1657 for (i = menu->parent->children; i != NULL; i = i->next)
1658 {
1659 if (i->submenu_id != NULL && XtIsManaged(i->submenu_id))
1660 XtPopdown(i->submenu_id);
1661 }
1662 }
1663 XtVaGetValues(XtParent(w),
1664 XtNwidth, &width,
1665 NULL);
1666 /* Assumption: XawSimpleMenuGetActiveEntry(XtParent(w)) == menu->id */
1667 /* i.e. This IS the active entry */
1668 XtTranslateCoords(menu->id,width - 5, 0, &root_x, &root_y);
1669 XtVaSetValues(w, XtNx, root_x,
1670 XtNy, root_y,
1671 NULL);
1672}
1673
Bram Moolenaar071d4272004-06-13 20:20:40 +00001674 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001675gui_athena_popdown_submenus_action(
1676 Widget w,
1677 XEvent *event,
1678 String *args,
1679 Cardinal *nargs)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680{
1681 WidgetList children;
1682 Cardinal num_children;
1683
1684 XtVaGetValues(w, XtNchildren, &children,
1685 XtNnumChildren, &num_children,
1686 NULL);
1687 for (; num_children > 0; --num_children)
1688 {
1689 Widget child = children[num_children - 1];
1690
1691 if (has_submenu(child))
1692 {
1693 Widget temp_w;
1694
1695 temp_w = submenu_widget(child);
1696 gui_athena_popdown_submenus_action(temp_w,event,args,nargs);
1697 XtPopdown(temp_w);
1698 }
1699 }
1700}
1701
1702/* Used to determine if the given widget has a submenu that can be popped up. */
1703 static Boolean
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001704has_submenu(Widget widget)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705{
1706 if ((widget != NULL) && XtIsSubclass(widget,smeBSBObjectClass))
1707 {
1708 Pixmap p;
1709
1710 XtVaGetValues(widget, XtNrightBitmap, &p, NULL);
1711 if ((p != None) && (p != XtUnspecifiedPixmap))
1712 return True;
1713 }
1714 return False;
1715}
1716
Bram Moolenaar071d4272004-06-13 20:20:40 +00001717 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001718gui_athena_delayed_arm_action(
1719 Widget w,
1720 XEvent *event,
1721 String *args,
1722 Cardinal *nargs)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723{
1724 Dimension width, height;
1725
1726 if (event->type != MotionNotify)
1727 return;
1728
1729 XtVaGetValues(w,
1730 XtNwidth, &width,
1731 XtNheight, &height,
1732 NULL);
1733
1734 if (event->xmotion.x >= (int)width || event->xmotion.y >= (int)height)
1735 return;
1736
1737 {
1738 static Widget previous_active_widget = NULL;
1739 Widget current;
1740
1741 current = XawSimpleMenuGetActiveEntry(w);
1742 if (current != previous_active_widget)
1743 {
1744 if (timer)
1745 {
1746 /* If the timeout hasn't been triggered, remove it */
1747 XtRemoveTimeOut(timer);
1748 }
1749 gui_athena_popdown_submenus_action(w,event,args,nargs);
1750 if (has_submenu(current))
1751 {
1752 XtAppAddTimeOut(XtWidgetToApplicationContext(w), 600L,
1753 gui_athena_menu_timeout,
1754 (XtPointer)current);
1755 }
1756 previous_active_widget = current;
1757 }
1758 }
1759}
1760
1761 static Widget
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001762get_popup_entry(Widget w)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001763{
1764 Widget menuw;
1765
1766 /* Get the active entry for the current menu */
1767 if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)0)
1768 return NULL;
1769
1770 return submenu_widget(menuw);
1771}
1772
1773/* Given the widget that has been determined to have a submenu, return the submenu widget
1774 * that is to be popped up.
1775 */
1776 static Widget
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001777submenu_widget(Widget widget)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778{
1779 /* Precondition: has_submenu(widget) == True
1780 * XtIsSubclass(XtParent(widget),simpleMenuWidgetClass) == True
1781 */
1782
1783 char_u *pullright_name;
1784 Widget popup;
1785
1786 pullright_name = make_pull_name((char_u *)XtName(widget));
1787 popup = XtNameToWidget(XtParent(widget), (char *)pullright_name);
1788 vim_free(pullright_name);
1789
1790 return popup;
1791 /* Postcondition: (popup != NULL) implies
1792 * (XtIsSubclass(popup,simpleMenuWidgetClass) == True) */
1793}
1794
Bram Moolenaar071d4272004-06-13 20:20:40 +00001795 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001796gui_mch_show_popupmenu(vimmenu_T *menu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797{
1798 int rootx, rooty, winx, winy;
1799 Window root, child;
1800 unsigned int mask;
1801
1802 if (menu->submenu_id == (Widget)0)
1803 return;
1804
1805 /* Position the popup menu at the pointer */
1806 if (XQueryPointer(gui.dpy, XtWindow(vimShell), &root, &child,
1807 &rootx, &rooty, &winx, &winy, &mask))
1808 {
1809 rootx -= 30;
1810 if (rootx < 0)
1811 rootx = 0;
1812 rooty -= 5;
1813 if (rooty < 0)
1814 rooty = 0;
1815 XtVaSetValues(menu->submenu_id,
1816 XtNx, rootx,
1817 XtNy, rooty,
1818 NULL);
1819 }
1820
1821 XtOverrideTranslations(menu->submenu_id, popupTrans);
1822 XtPopupSpringLoaded(menu->submenu_id);
1823}
1824
1825#endif /* FEAT_MENU */
1826
1827/*
1828 * Set the menu and scrollbar colors to their default values.
1829 */
1830 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001831gui_mch_def_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001832{
1833 /*
1834 * Get the colors ourselves. Using the automatic conversion doesn't
1835 * handle looking for approximate colors.
1836 */
1837 if (gui.in_use)
1838 {
1839 gui.menu_fg_pixel = gui_get_color((char_u *)gui.rsrc_menu_fg_name);
1840 gui.menu_bg_pixel = gui_get_color((char_u *)gui.rsrc_menu_bg_name);
1841 gui.scroll_fg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_fg_name);
1842 gui.scroll_bg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_bg_name);
1843#ifdef FEAT_BEVAL
1844 gui.tooltip_fg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_fg_name);
1845 gui.tooltip_bg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_bg_name);
1846#endif
1847 }
1848}
1849
1850
1851/*
1852 * Scrollbar stuff.
1853 */
1854
1855 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001856gui_mch_set_scrollbar_thumb(
1857 scrollbar_T *sb,
1858 long val,
1859 long size,
1860 long max)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001861{
1862 double v, s;
1863
1864 if (sb->id == (Widget)0)
1865 return;
1866
1867 /*
1868 * Athena scrollbar must go from 0.0 to 1.0.
1869 */
1870 if (max == 0)
1871 {
1872 /* So you can't scroll it at all (normally it scrolls past end) */
1873#ifdef FEAT_GUI_NEXTAW
1874 XawScrollbarSetThumb(sb->id, 0.0, 1.0);
1875#else
1876 vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
1877#endif
1878 }
1879 else
1880 {
1881 v = (double)val / (double)(max + 1);
1882 s = (double)size / (double)(max + 1);
1883#ifdef FEAT_GUI_NEXTAW
1884 XawScrollbarSetThumb(sb->id, v, s);
1885#else
1886 vim_XawScrollbarSetThumb(sb->id, v, s, 1.0);
1887#endif
1888 }
1889}
1890
1891 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001892gui_mch_set_scrollbar_pos(
1893 scrollbar_T *sb,
1894 int x,
1895 int y,
1896 int w,
1897 int h)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001898{
1899 if (sb->id == (Widget)0)
1900 return;
1901
1902 XtUnmanageChild(sb->id);
1903 XtVaSetValues(sb->id,
1904 XtNhorizDistance, x,
1905 XtNvertDistance, y,
1906 XtNwidth, w,
1907 XtNheight, h,
1908 NULL);
1909 XtManageChild(sb->id);
1910}
1911
1912 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001913gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914{
1915 if (sb->id != (Widget)0)
1916 {
1917 if (flag)
1918 XtManageChild(sb->id);
1919 else
1920 XtUnmanageChild(sb->id);
1921 }
1922}
1923
1924 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001925gui_mch_create_scrollbar(
1926 scrollbar_T *sb,
1927 int orient) /* SBAR_VERT or SBAR_HORIZ */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928{
1929 sb->id = XtVaCreateWidget("scrollBar",
1930#ifdef FEAT_GUI_NEXTAW
1931 scrollbarWidgetClass, vimForm,
1932#else
1933 vim_scrollbarWidgetClass, vimForm,
1934#endif
1935 XtNresizable, True,
1936 XtNtop, XtChainTop,
1937 XtNbottom, XtChainTop,
1938 XtNleft, XtChainLeft,
1939 XtNright, XtChainLeft,
1940 XtNborderWidth, 0,
1941 XtNorientation, (orient == SBAR_VERT) ? XtorientVertical
1942 : XtorientHorizontal,
1943 XtNforeground, gui.scroll_fg_pixel,
1944 XtNbackground, gui.scroll_bg_pixel,
1945 NULL);
1946 if (sb->id == (Widget)0)
1947 return;
1948
1949 XtAddCallback(sb->id, XtNjumpProc,
1950 gui_athena_scroll_cb_jump, (XtPointer)sb->ident);
1951 XtAddCallback(sb->id, XtNscrollProc,
1952 gui_athena_scroll_cb_scroll, (XtPointer)sb->ident);
1953
1954#ifdef FEAT_GUI_NEXTAW
1955 XawScrollbarSetThumb(sb->id, 0.0, 1.0);
1956#else
1957 vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0);
1958#endif
1959}
1960
1961#if defined(FEAT_WINDOWS) || defined(PROTO)
1962 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001963gui_mch_destroy_scrollbar(scrollbar_T *sb)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001964{
1965 if (sb->id != (Widget)0)
1966 XtDestroyWidget(sb->id);
1967}
1968#endif
1969
1970 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001971gui_mch_set_scrollbar_colors(scrollbar_T *sb)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972{
1973 if (sb->id != (Widget)0)
1974 XtVaSetValues(sb->id,
1975 XtNforeground, gui.scroll_fg_pixel,
1976 XtNbackground, gui.scroll_bg_pixel,
1977 NULL);
1978
1979 /* This is needed for the rectangle below the vertical scrollbars. */
1980 if (sb == &gui.bottom_sbar && vimForm != (Widget)0)
1981 gui_athena_scroll_colors(vimForm);
1982}
1983
1984/*
1985 * Miscellaneous stuff:
1986 */
1987 Window
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001988gui_x11_get_wid(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989{
1990 return XtWindow(textArea);
1991}
1992
1993#if defined(FEAT_BROWSE) || defined(PROTO)
1994/*
1995 * Put up a file requester.
1996 * Returns the selected name in allocated memory, or NULL for Cancel.
1997 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001998 char_u *
Bram Moolenaar66f948e2016-01-30 16:39:25 +01001999gui_mch_browse(
2000 int saving UNUSED, /* select file to write */
2001 char_u *title, /* title for the window */
2002 char_u *dflt, /* default name */
2003 char_u *ext UNUSED, /* extension added */
2004 char_u *initdir, /* initial directory, NULL for current dir */
2005 char_u *filter UNUSED) /* file name filter */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002006{
2007 Position x, y;
2008 char_u dirbuf[MAXPATHL];
2009
2010 /* Concatenate "initdir" and "dflt". */
2011 if (initdir == NULL || *initdir == NUL)
2012 mch_dirname(dirbuf, MAXPATHL);
2013 else if (STRLEN(initdir) + 2 < MAXPATHL)
2014 STRCPY(dirbuf, initdir);
2015 else
2016 dirbuf[0] = NUL;
2017 if (dflt != NULL && *dflt != NUL
2018 && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL)
2019 {
2020 add_pathsep(dirbuf);
2021 STRCAT(dirbuf, dflt);
2022 }
2023
2024 /* Position the file selector just below the menubar */
2025 XtTranslateCoords(vimShell, (Position)0, (Position)
2026#ifdef FEAT_MENU
2027 gui.menu_height
2028#else
2029 0
2030#endif
2031 , &x, &y);
2032 return (char_u *)vim_SelFile(vimShell, (char *)title, (char *)dirbuf,
2033 NULL, (int)x, (int)y, gui.menu_fg_pixel, gui.menu_bg_pixel,
2034 gui.scroll_fg_pixel, gui.scroll_bg_pixel);
2035}
2036#endif
2037
2038#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
2039
2040static int dialogStatus;
2041static Atom dialogatom;
2042
Bram Moolenaard25c16e2016-01-29 22:13:30 +01002043static void keyhit_callback(Widget w, XtPointer client_data, XEvent *event, Boolean *cont);
2044static void butproc(Widget w, XtPointer client_data, XtPointer call_data);
2045static void dialog_wm_handler(Widget w, XtPointer client_data, XEvent *event, Boolean *dum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046
2047/*
2048 * Callback function for the textfield. When CR is hit this works like
2049 * hitting the "OK" button, ESC like "Cancel".
2050 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002051 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01002052keyhit_callback(
2053 Widget w UNUSED,
2054 XtPointer client_data UNUSED,
2055 XEvent *event,
2056 Boolean *cont UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002057{
2058 char buf[2];
2059
2060 if (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1)
2061 {
2062 if (*buf == CAR)
2063 dialogStatus = 1;
2064 else if (*buf == ESC)
2065 dialogStatus = 0;
2066 }
2067}
2068
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01002070butproc(
2071 Widget w UNUSED,
2072 XtPointer client_data,
2073 XtPointer call_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002074{
2075 dialogStatus = (int)(long)client_data + 1;
2076}
2077
2078/*
2079 * Function called when dialog window closed.
2080 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002081 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01002082dialog_wm_handler(
2083 Widget w UNUSED,
2084 XtPointer client_data UNUSED,
2085 XEvent *event,
2086 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087{
2088 if (event->type == ClientMessage
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00002089 && (Atom)((XClientMessageEvent *)event)->data.l[0] == dialogatom)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002090 dialogStatus = 0;
2091}
2092
Bram Moolenaar071d4272004-06-13 20:20:40 +00002093 int
Bram Moolenaar66f948e2016-01-30 16:39:25 +01002094gui_mch_dialog(
2095 int type UNUSED,
2096 char_u *title,
2097 char_u *message,
2098 char_u *buttons,
2099 int dfltbutton UNUSED,
2100 char_u *textfield,
2101 int ex_cmd UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002102{
2103 char_u *buts;
2104 char_u *p, *next;
2105 XtAppContext app;
2106 XEvent event;
2107 Position wd, hd;
2108 Position wv, hv;
2109 Position x, y;
2110 Widget dialog;
2111 Widget dialogshell;
2112 Widget dialogmessage;
2113 Widget dialogtextfield = 0;
2114 Widget dialogButton;
2115 Widget prev_dialogButton = NULL;
2116 int butcount;
2117 int vertical;
2118
2119 if (title == NULL)
2120 title = (char_u *)_("Vim dialog");
2121 dialogStatus = -1;
2122
2123 /* if our pointer is currently hidden, then we should show it. */
2124 gui_mch_mousehide(FALSE);
2125
2126 /* Check 'v' flag in 'guioptions': vertical button placement. */
2127 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
2128
2129 /* The shell is created each time, to make sure it is resized properly */
2130 dialogshell = XtVaCreatePopupShell("dialogShell",
2131 transientShellWidgetClass, vimShell,
2132 XtNtitle, title,
2133 NULL);
2134 if (dialogshell == (Widget)0)
2135 goto error;
2136
2137 dialog = XtVaCreateManagedWidget("dialog",
2138 formWidgetClass, dialogshell,
2139 XtNdefaultDistance, 20,
2140 NULL);
2141 if (dialog == (Widget)0)
2142 goto error;
2143 gui_athena_menu_colors(dialog);
2144 dialogmessage = XtVaCreateManagedWidget("dialogMessage",
2145 labelWidgetClass, dialog,
2146 XtNlabel, message,
2147 XtNtop, XtChainTop,
2148 XtNbottom, XtChainTop,
2149 XtNleft, XtChainLeft,
2150 XtNright, XtChainLeft,
2151 XtNresizable, True,
2152 XtNborderWidth, 0,
2153 NULL);
2154 gui_athena_menu_colors(dialogmessage);
2155
2156 if (textfield != NULL)
2157 {
2158 dialogtextfield = XtVaCreateManagedWidget("textfield",
2159 asciiTextWidgetClass, dialog,
2160 XtNwidth, 400,
2161 XtNtop, XtChainTop,
2162 XtNbottom, XtChainTop,
2163 XtNleft, XtChainLeft,
2164 XtNright, XtChainRight,
2165 XtNfromVert, dialogmessage,
2166 XtNresizable, True,
2167 XtNstring, textfield,
2168 XtNlength, IOSIZE,
2169 XtNuseStringInPlace, True,
2170 XtNeditType, XawtextEdit,
2171 XtNwrap, XawtextWrapNever,
2172 XtNresize, XawtextResizeHeight,
2173 NULL);
2174 XtManageChild(dialogtextfield);
2175 XtAddEventHandler(dialogtextfield, KeyPressMask, False,
2176 (XtEventHandler)keyhit_callback, (XtPointer)NULL);
2177 XawTextSetInsertionPoint(dialogtextfield,
2178 (XawTextPosition)STRLEN(textfield));
2179 XtSetKeyboardFocus(dialog, dialogtextfield);
2180 }
2181
2182 /* make a copy, so that we can insert NULs */
2183 buts = vim_strsave(buttons);
2184 if (buts == NULL)
2185 return -1;
2186
2187 p = buts;
2188 for (butcount = 0; *p; ++butcount)
2189 {
2190 for (next = p; *next; ++next)
2191 {
2192 if (*next == DLG_HOTKEY_CHAR)
Bram Moolenaar3577c6f2008-06-24 21:16:56 +00002193 STRMOVE(next, next + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002194 if (*next == DLG_BUTTON_SEP)
2195 {
2196 *next++ = NUL;
2197 break;
2198 }
2199 }
2200 dialogButton = XtVaCreateManagedWidget("button",
2201 commandWidgetClass, dialog,
2202 XtNlabel, p,
2203 XtNtop, XtChainBottom,
2204 XtNbottom, XtChainBottom,
2205 XtNleft, XtChainLeft,
2206 XtNright, XtChainLeft,
2207 XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield,
2208 XtNvertDistance, vertical ? 4 : 20,
2209 XtNresizable, False,
2210 NULL);
2211 gui_athena_menu_colors(dialogButton);
2212 if (butcount > 0)
2213 XtVaSetValues(dialogButton,
2214 vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton,
2215 NULL);
2216
Bram Moolenaar9d6650f2010-06-06 23:04:47 +02002217 XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)(long_u)butcount);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002218 p = next;
2219 prev_dialogButton = dialogButton;
2220 }
2221 vim_free(buts);
2222
2223 XtRealizeWidget(dialogshell);
2224
2225 /* Setup for catching the close-window event, don't let it close Vim! */
2226 dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
2227 XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1);
2228 XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL);
2229
2230 XtVaGetValues(dialogshell,
2231 XtNwidth, &wd,
2232 XtNheight, &hd,
2233 NULL);
2234 XtVaGetValues(vimShell,
2235 XtNwidth, &wv,
2236 XtNheight, &hv,
2237 NULL);
2238 XtTranslateCoords(vimShell,
2239 (Position)((wv - wd) / 2),
2240 (Position)((hv - hd) / 2),
2241 &x, &y);
2242 if (x < 0)
2243 x = 0;
2244 if (y < 0)
2245 y = 0;
2246 XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL);
2247
2248 /* Position the mouse pointer in the dialog, required for when focus
2249 * follows mouse. */
2250 XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40);
2251
2252
2253 app = XtWidgetToApplicationContext(dialogshell);
2254
2255 XtPopup(dialogshell, XtGrabNonexclusive);
2256
Bram Moolenaarac76e4d2005-07-09 20:58:57 +00002257 for (;;)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002258 {
2259 XtAppNextEvent(app, &event);
2260 XtDispatchEvent(&event);
2261 if (dialogStatus >= 0)
2262 break;
2263 }
2264
2265 XtPopdown(dialogshell);
2266
2267 if (textfield != NULL && dialogStatus < 0)
2268 *textfield = NUL;
2269
2270error:
2271 XtDestroyWidget(dialogshell);
2272
2273 return dialogStatus;
2274}
2275#endif
2276
2277#if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU)
2278/*
2279 * Set the colors of Widget "id" to the menu colors.
2280 */
2281 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01002282gui_athena_menu_colors(Widget id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002283{
2284 if (gui.menu_bg_pixel != INVALCOLOR)
2285 XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL);
2286 if (gui.menu_fg_pixel != INVALCOLOR)
2287 XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL);
2288}
2289#endif
2290
2291/*
2292 * Set the colors of Widget "id" to the scroll colors.
2293 */
2294 static void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01002295gui_athena_scroll_colors(Widget id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002296{
2297 if (gui.scroll_bg_pixel != INVALCOLOR)
2298 XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL);
2299 if (gui.scroll_fg_pixel != INVALCOLOR)
2300 XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL);
2301}