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