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