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