Mercurial > vim
view src/gui_athena.c @ 16150:b23048205589 v8.1.1080
patch 8.1.1080: when a screendump test fails, moving the file is a hassle
commit https://github.com/vim/vim/commit/ef7f0e367eeaf6fb31b1caa0e3de1a4b07e86af3
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Mar 30 15:59:51 2019 +0100
patch 8.1.1080: when a screendump test fails, moving the file is a hassle
Problem: When a screendump test fails, moving the file is a hassle.
Solution: Instead of appending ".failed" to the file name, keep the same
file name but put the screendump in the "failed" directory.
Then the file name only needs to be typed once when moving a
screendump.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 30 Mar 2019 16:00:06 +0100 |
parents | 6e4e0d43b20b |
children | 79e10adc821d |
line wrap: on
line source
/* vi:set ts=8 sts=4 sw=4 noet: * * VIM - Vi IMproved by Bram Moolenaar * GUI/Motif support by Robert Webb * Athena port by Bill Foster * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ #include "vim.h" #include <X11/StringDefs.h> #include <X11/Intrinsic.h> #ifdef FEAT_GUI_NEXTAW # include <X11/neXtaw/Form.h> # include <X11/neXtaw/SimpleMenu.h> # include <X11/neXtaw/MenuButton.h> # include <X11/neXtaw/SmeBSB.h> # include <X11/neXtaw/SmeLine.h> # include <X11/neXtaw/Box.h> # include <X11/neXtaw/Dialog.h> # include <X11/neXtaw/Text.h> # include <X11/neXtaw/AsciiText.h> # include <X11/neXtaw/Scrollbar.h> #else # include <X11/Xaw/Form.h> # include <X11/Xaw/SimpleMenu.h> # include <X11/Xaw/MenuButton.h> # include <X11/Xaw/SmeBSB.h> # include <X11/Xaw/SmeLine.h> # include <X11/Xaw/Box.h> # include <X11/Xaw/Dialog.h> # include <X11/Xaw/Text.h> # include <X11/Xaw/AsciiText.h> #endif /* FEAT_GUI_NEXTAW */ #ifndef FEAT_GUI_NEXTAW # include "gui_at_sb.h" #endif extern Widget vimShell; static Widget vimForm = (Widget)0; Widget textArea = (Widget)0; #ifdef FEAT_MENU static Widget menuBar = (Widget)0; static XtIntervalId timer = 0; /* 0 = expired, otherwise active */ /* Used to figure out menu ordering */ static vimmenu_T *a_cur_menu = NULL; static Cardinal athena_calculate_ins_pos(Widget); static void gui_athena_popup_callback(Widget, XtPointer, XtPointer); static void gui_athena_delayed_arm_action(Widget, XEvent *, String *, Cardinal *); static void gui_athena_popdown_submenus_action(Widget, XEvent *, String *, Cardinal *); static XtActionsRec pullAction[2] = { { "menu-delayedpopup", (XtActionProc)gui_athena_delayed_arm_action}, { "menu-popdownsubmenus", (XtActionProc)gui_athena_popdown_submenus_action} }; #endif #ifdef FEAT_TOOLBAR static void gui_mch_reset_focus(void); static Widget toolBar = (Widget)0; #endif #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU) static void gui_athena_menu_colors(Widget id); #endif static void gui_athena_scroll_colors(Widget id); #ifdef FEAT_MENU static XtTranslations popupTrans, parentTrans, menuTrans, supermenuTrans; static Pixmap pullerBitmap = None; static int puller_width = 0; #endif /* * Scrollbar callback (XtNjumpProc) for when the scrollbar is dragged with the * left or middle mouse button. */ static void gui_athena_scroll_cb_jump( Widget w UNUSED, XtPointer client_data, XtPointer call_data) { scrollbar_T *sb, *sb_info; long value; sb = gui_find_scrollbar((long)client_data); if (sb == NULL) return; else if (sb->wp != NULL) /* Left or right scrollbar */ { /* * Careful: need to get scrollbar info out of first (left) scrollbar * for window, but keep real scrollbar too because we must pass it to * gui_drag_scrollbar(). */ sb_info = &sb->wp->w_scrollbars[0]; } else /* Bottom scrollbar */ sb_info = sb; value = (long)(*((float *)call_data) * (float)(sb_info->max + 1) + 0.001); if (value > sb_info->max) value = sb_info->max; gui_drag_scrollbar(sb, value, TRUE); } /* * Scrollbar callback (XtNscrollProc) for paging up or down with the left or * right mouse buttons. */ static void gui_athena_scroll_cb_scroll( Widget w UNUSED, XtPointer client_data, XtPointer call_data) { scrollbar_T *sb, *sb_info; long value; int data = (int)(long)call_data; int page; sb = gui_find_scrollbar((long)client_data); if (sb == NULL) return; if (sb->wp != NULL) /* Left or right scrollbar */ { /* * Careful: need to get scrollbar info out of first (left) scrollbar * for window, but keep real scrollbar too because we must pass it to * gui_drag_scrollbar(). */ sb_info = &sb->wp->w_scrollbars[0]; if (sb_info->size > 5) page = sb_info->size - 2; /* use two lines of context */ else page = sb_info->size; #ifdef FEAT_GUI_NEXTAW if (data < 0) { data = (data - gui.char_height + 1) / gui.char_height; if (data > -sb_info->size) data = -1; else data = -page; } else if (data > 0) { data = (data + gui.char_height - 1) / gui.char_height; if (data < sb_info->size) data = 1; else data = page; } #else switch (data) { case ONE_LINE_DATA: data = 1; break; case -ONE_LINE_DATA: data = -1; break; case ONE_PAGE_DATA: data = page; break; case -ONE_PAGE_DATA: data = -page; break; case END_PAGE_DATA: data = sb_info->max; break; case -END_PAGE_DATA: data = -sb_info->max; break; default: data = 0; break; } #endif } else /* Bottom scrollbar */ { sb_info = sb; #ifdef FEAT_GUI_NEXTAW if (data < 0) { data = (data - gui.char_width + 1) / gui.char_width; if (data > -sb->size) data = -1; } else if (data > 0) { data = (data + gui.char_width - 1) / gui.char_width; if (data < sb->size) data = 1; } #endif if (data < -1) /* page-width left */ { if (sb->size > 8) data = -(sb->size - 5); else data = -sb->size; } else if (data > 1) /* page-width right */ { if (sb->size > 8) data = (sb->size - 5); else data = sb->size; } } value = sb_info->value + data; if (value > sb_info->max) value = sb_info->max; else if (value < 0) value = 0; /* Update the bottom scrollbar an extra time (why is this needed?? */ if (sb->wp == NULL) /* Bottom scrollbar */ gui_mch_set_scrollbar_thumb(sb, value, sb->size, sb->max); gui_drag_scrollbar(sb, value, FALSE); } /* * Create all the Athena widgets necessary. */ void gui_x11_create_widgets(void) { /* * We don't have any borders handled internally by the textArea to worry * about so only skip over the configured border width. */ gui.border_offset = gui.border_width; /* The form containing all the other widgets */ vimForm = XtVaCreateManagedWidget("vimForm", formWidgetClass, vimShell, XtNborderWidth, 0, NULL); gui_athena_scroll_colors(vimForm); #ifdef FEAT_MENU /* The top menu bar */ menuBar = XtVaCreateManagedWidget("menuBar", boxWidgetClass, vimForm, XtNresizable, True, XtNtop, XtChainTop, XtNbottom, XtChainTop, XtNleft, XtChainLeft, XtNright, XtChainRight, XtNinsertPosition, athena_calculate_ins_pos, NULL); gui_athena_menu_colors(menuBar); if (gui.menu_fg_pixel != INVALCOLOR) XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL); #endif #ifdef FEAT_TOOLBAR /* Don't create it Managed, it will be managed when creating the first * item. Otherwise an empty toolbar shows up. */ toolBar = XtVaCreateWidget("toolBar", boxWidgetClass, vimForm, XtNresizable, True, XtNtop, XtChainTop, XtNbottom, XtChainTop, XtNleft, XtChainLeft, XtNright, XtChainRight, XtNorientation, XtorientHorizontal, XtNhSpace, 1, XtNvSpace, 3, XtNinsertPosition, athena_calculate_ins_pos, NULL); gui_athena_menu_colors(toolBar); #endif /* The text area. */ textArea = XtVaCreateManagedWidget("textArea", coreWidgetClass, vimForm, XtNresizable, True, XtNtop, XtChainTop, XtNbottom, XtChainTop, XtNleft, XtChainLeft, XtNright, XtChainLeft, XtNbackground, gui.back_pixel, XtNborderWidth, 0, NULL); /* * Install the callbacks. */ gui_x11_callbacks(textArea, vimForm); #ifdef FEAT_MENU popupTrans = XtParseTranslationTable( "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n" "<LeaveWindow>: unhighlight()\n" "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n" "<Motion>: highlight() menu-delayedpopup()"); parentTrans = XtParseTranslationTable("<LeaveWindow>: unhighlight()"); menuTrans = XtParseTranslationTable( "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n" "<LeaveWindow>: menu-popdownsubmenus() XtMenuPopdown() unhighlight()\n" "<BtnUp>: notify() unhighlight()\n" "<BtnMotion>: highlight() menu-delayedpopup()"); supermenuTrans = XtParseTranslationTable( "<EnterWindow>: menu-popdownsubmenus() highlight() menu-delayedpopup()\n" "<LeaveWindow>: unhighlight()\n" "<BtnUp>: menu-popdownsubmenus() XtMenuPopdown() notify() unhighlight()\n" "<BtnMotion>: highlight() menu-delayedpopup()"); XtAppAddActions(XtWidgetToApplicationContext(vimForm), pullAction, XtNumber(pullAction)); #endif /* Pretend we don't have input focus, we will get an event if we do. */ gui.in_focus = FALSE; } #ifdef FEAT_MENU /* * Calculates the Pixmap based on the size of the current menu font. */ static Pixmap gui_athena_create_pullright_pixmap(Widget w) { Pixmap retval; #ifdef FONTSET_ALWAYS XFontSet font = None; #else XFontStruct *font = NULL; #endif #ifdef FONTSET_ALWAYS if (gui.menu_fontset == NOFONTSET) #else if (gui.menu_font == NOFONT) #endif { XrmValue from, to; WidgetList children; Cardinal num_children; #ifdef FONTSET_ALWAYS from.size = strlen(from.addr = XtDefaultFontSet); to.addr = (XtPointer)&font; to.size = sizeof(XFontSet); #else from.size = strlen(from.addr = XtDefaultFont); to.addr = (XtPointer)&font; to.size = sizeof(XFontStruct *); #endif /* Assumption: The menuBar children will use the same font as the * pulldown menu items AND they will all be of type * XtNfont. */ XtVaGetValues(menuBar, XtNchildren, &children, XtNnumChildren, &num_children, NULL); if (XtConvertAndStore(w ? w : (num_children > 0) ? children[0] : menuBar, XtRString, &from, #ifdef FONTSET_ALWAYS XtRFontSet, &to #else XtRFontStruct, &to #endif ) == False) return None; /* "font" should now contain data */ } else #ifdef FONTSET_ALWAYS font = (XFontSet)gui.menu_fontset; #else font = (XFontStruct *)gui.menu_font; #endif { int width, height; GC draw_gc, undraw_gc; XGCValues gc_values; XPoint points[3]; #ifdef FONTSET_ALWAYS height = fontset_height2(font); #else height = font->max_bounds.ascent + font->max_bounds.descent; #endif width = height - 2; puller_width = width + 4; retval = XCreatePixmap(gui.dpy,DefaultRootWindow(gui.dpy),width, height, 1); gc_values.foreground = 1; gc_values.background = 0; draw_gc = XCreateGC(gui.dpy, retval, GCForeground | GCBackground, &gc_values); gc_values.foreground = 0; gc_values.background = 1; undraw_gc = XCreateGC(gui.dpy, retval, GCForeground | GCBackground, &gc_values); points[0].x = 0; points[0].y = 0; points[1].x = width - 1; points[1].y = (height - 1) / 2; points[2].x = 0; points[2].y = height - 1; XFillRectangle(gui.dpy, retval, undraw_gc, 0, 0, height, height); XFillPolygon(gui.dpy, retval, draw_gc, points, XtNumber(points), Convex, CoordModeOrigin); XFreeGC(gui.dpy, draw_gc); XFreeGC(gui.dpy, undraw_gc); } return retval; } #endif /* * Called when the GUI is not going to start after all. */ void gui_x11_destroy_widgets(void) { textArea = NULL; #ifdef FEAT_MENU menuBar = NULL; #endif #ifdef FEAT_TOOLBAR toolBar = NULL; #endif } #if defined(FEAT_TOOLBAR) || defined(PROTO) # include "gui_x11_pm.h" # ifdef HAVE_X11_XPM_H # include <X11/xpm.h> # endif static void createXpmImages(char_u *path, char **xpm, Pixmap *sen); /* * Allocated a pixmap for toolbar menu "menu". * Return in "sen". */ static void get_toolbar_pixmap(vimmenu_T *menu, Pixmap *sen) { char_u buf[MAXPATHL]; /* buffer storing expanded pathname */ char **xpm = NULL; /* xpm array */ buf[0] = NUL; /* start with NULL path */ if (menu->iconfile != NULL) { /* Use the "icon=" argument. */ gui_find_iconfile(menu->iconfile, buf, "xpm"); createXpmImages(buf, NULL, sen); /* If it failed, try using the menu name. */ if (*sen == (Pixmap)0 && gui_find_bitmap(menu->name, buf, "xpm") == OK) createXpmImages(buf, NULL, sen); if (*sen != (Pixmap)0) return; } if (menu->icon_builtin || gui_find_bitmap(menu->name, buf, "xpm") == FAIL) { if (menu->iconidx >= 0 && menu->iconidx < (int)(sizeof(built_in_pixmaps) / sizeof(built_in_pixmaps[0]))) xpm = built_in_pixmaps[menu->iconidx]; else xpm = tb_blank_xpm; } if (xpm != NULL || buf[0] != NUL) createXpmImages(buf, xpm, sen); } /* * Read an Xpm file, doing color substitutions for the foreground and * background colors. If there is an error reading a color xpm file, * drop back and read the monochrome file. If successful, create the * insensitive Pixmap too. */ static void createXpmImages(char_u *path, char **xpm, Pixmap *sen) { Window rootWindow; XpmAttributes attrs; XpmColorSymbol color[5] = { {"none", "none", 0}, {"iconColor1", NULL, 0}, {"bottomShadowColor", NULL, 0}, {"topShadowColor", NULL, 0}, {"selectColor", NULL, 0} }; int screenNum; int status; Pixmap mask; Pixmap map; gui_mch_get_toolbar_colors( &color[BACKGROUND].pixel, &color[FOREGROUND].pixel, &color[BOTTOM_SHADOW].pixel, &color[TOP_SHADOW].pixel, &color[HIGHLIGHT].pixel); /* Setup the color substitution table */ attrs.valuemask = XpmColorSymbols; attrs.colorsymbols = color; attrs.numsymbols = 5; screenNum = DefaultScreen(gui.dpy); rootWindow = RootWindow(gui.dpy, screenNum); /* Create the "sensitive" pixmap */ if (xpm != NULL) status = XpmCreatePixmapFromData(gui.dpy, rootWindow, xpm, &map, &mask, &attrs); else status = XpmReadFileToPixmap(gui.dpy, rootWindow, (char *)path, &map, &mask, &attrs); if (status == XpmSuccess && map != 0) { XGCValues gcvalues; GC back_gc; GC mask_gc; /* Need to create new Pixmaps with the mask applied. */ gcvalues.foreground = color[BACKGROUND].pixel; back_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues); mask_gc = XCreateGC(gui.dpy, map, GCForeground, &gcvalues); XSetClipMask(gui.dpy, mask_gc, mask); /* Create the "sensitive" pixmap. */ *sen = XCreatePixmap(gui.dpy, rootWindow, attrs.width, attrs.height, DefaultDepth(gui.dpy, screenNum)); XFillRectangle(gui.dpy, *sen, back_gc, 0, 0, attrs.width, attrs.height); XCopyArea(gui.dpy, map, *sen, mask_gc, 0, 0, attrs.width, attrs.height, 0, 0); XFreeGC(gui.dpy, back_gc); XFreeGC(gui.dpy, mask_gc); XFreePixmap(gui.dpy, map); } else *sen = 0; XpmFreeAttributes(&attrs); } void gui_mch_set_toolbar_pos( int x, int y, int w, int h) { Dimension border; int height; if (!XtIsManaged(toolBar)) /* nothing to do */ return; XtUnmanageChild(toolBar); XtVaGetValues(toolBar, XtNborderWidth, &border, NULL); height = h - 2 * border; if (height < 0) height = 1; XtVaSetValues(toolBar, XtNhorizDistance, x, XtNvertDistance, y, XtNwidth, w - 2 * border, XtNheight, height, NULL); XtManageChild(toolBar); } #endif void gui_mch_set_text_area_pos( int x, int y, int w, int h) { XtUnmanageChild(textArea); XtVaSetValues(textArea, XtNhorizDistance, x, XtNvertDistance, y, XtNwidth, w, XtNheight, h, NULL); XtManageChild(textArea); #ifdef FEAT_TOOLBAR /* Give keyboard focus to the textArea instead of the toolbar. */ gui_mch_reset_focus(); #endif } #ifdef FEAT_TOOLBAR /* * A toolbar button has been pushed; now reset the input focus * such that the user can type page up/down etc. and have the * input go to the editor window, not the button */ static void gui_mch_reset_focus(void) { XtSetKeyboardFocus(vimForm, textArea); } #endif void gui_x11_set_back_color(void) { if (textArea != NULL) XtVaSetValues(textArea, XtNbackground, gui.back_pixel, NULL); } #if defined(FEAT_MENU) || defined(PROTO) /* * Menu stuff. */ static char_u *make_pull_name(char_u * name); static Widget get_popup_entry(Widget w); static Widget submenu_widget(Widget); static Boolean has_submenu(Widget); static void gui_mch_submenu_change(vimmenu_T *mp, int colors); static void gui_athena_menu_font(Widget id); void gui_mch_enable_menu(int flag) { if (flag) { XtManageChild(menuBar); # ifdef FEAT_TOOLBAR if (XtIsManaged(toolBar)) { XtVaSetValues(toolBar, XtNvertDistance, gui.menu_height, NULL); XtVaSetValues(textArea, XtNvertDistance, gui.menu_height + gui.toolbar_height, NULL); } # endif } else { XtUnmanageChild(menuBar); # ifdef FEAT_TOOLBAR if (XtIsManaged(toolBar)) { XtVaSetValues(toolBar, XtNvertDistance, 0, NULL); } # endif } } void gui_mch_set_menu_pos( int x, int y, int w, int h) { Dimension border; int height; XtUnmanageChild(menuBar); XtVaGetValues(menuBar, XtNborderWidth, &border, NULL); /* avoid trouble when there are no menu items, and h is 1 */ height = h - 2 * border; if (height < 0) height = 1; XtVaSetValues(menuBar, XtNhorizDistance, x, XtNvertDistance, y, XtNwidth, w - 2 * border, XtNheight, height, NULL); XtManageChild(menuBar); } /* * Used to calculate the insertion position of a widget with respect to its * neighbors. * * Valid range of return values is: 0 (beginning of children) to * numChildren (end of children). */ static Cardinal athena_calculate_ins_pos(Widget widget) { /* Assume that if the parent of the vimmenu_T is NULL, then we can get * to this menu by traversing "next", starting at "root_menu". * * This holds true for popup menus, toolbar, and toplevel menu items. */ /* Popup menus: "id" is NULL. Only submenu_id is valid */ /* Menus that are not toplevel: "parent" will be non-NULL, "id" & * "submenu_id" will be non-NULL. */ /* Toplevel menus: "parent" is NULL, id is the widget of the menu item */ WidgetList children; Cardinal num_children = 0; int retval; Arg args[2]; int n = 0; int i; XtSetArg(args[n], XtNchildren, &children); n++; XtSetArg(args[n], XtNnumChildren, &num_children); n++; XtGetValues(XtParent(widget), args, n); retval = num_children; for (i = 0; i < (int)num_children; ++i) { Widget current = children[i]; vimmenu_T *menu = NULL; for (menu = (a_cur_menu->parent == NULL) ? root_menu : a_cur_menu->parent->children; menu != NULL; menu = menu->next) if (current == menu->id && a_cur_menu->priority < menu->priority && i < retval) retval = i; } return retval; } void gui_mch_add_menu(vimmenu_T *menu, int idx UNUSED) { char_u *pullright_name; Dimension height, space, border; vimmenu_T *parent = menu->parent; a_cur_menu = menu; if (parent == NULL) { if (menu_is_popup(menu->dname)) { menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname, simpleMenuWidgetClass, vimShell, XtNinsertPosition, athena_calculate_ins_pos, XtNtranslations, popupTrans, NULL); gui_athena_menu_colors(menu->submenu_id); } else if (menu_is_menubar(menu->dname)) { menu->id = XtVaCreateManagedWidget((char *)menu->dname, menuButtonWidgetClass, menuBar, XtNmenuName, menu->dname, #ifdef FONTSET_ALWAYS XtNinternational, True, #endif NULL); if (menu->id == (Widget)0) return; gui_athena_menu_colors(menu->id); gui_athena_menu_font(menu->id); menu->submenu_id = XtVaCreatePopupShell((char *)menu->dname, simpleMenuWidgetClass, menu->id, XtNinsertPosition, athena_calculate_ins_pos, XtNtranslations, supermenuTrans, NULL); gui_athena_menu_colors(menu->submenu_id); gui_athena_menu_font(menu->submenu_id); /* Don't update the menu height when it was set at a fixed value */ if (!gui.menu_height_fixed) { /* * When we add a top-level item to the menu bar, we can figure * out how high the menu bar should be. */ XtVaGetValues(menuBar, XtNvSpace, &space, XtNborderWidth, &border, NULL); XtVaGetValues(menu->id, XtNheight, &height, NULL); gui.menu_height = height + 2 * (space + border); } } } else if (parent->submenu_id != (Widget)0) { menu->id = XtVaCreateManagedWidget((char *)menu->dname, smeBSBObjectClass, parent->submenu_id, XtNlabel, menu->dname, #ifdef FONTSET_ALWAYS XtNinternational, True, #endif NULL); if (menu->id == (Widget)0) return; if (pullerBitmap == None) pullerBitmap = gui_athena_create_pullright_pixmap(menu->id); XtVaSetValues(menu->id, XtNrightBitmap, pullerBitmap, NULL); /* If there are other menu items that are not pulldown menus, * we need to adjust the right margins of those, too. */ { WidgetList children; Cardinal num_children; int i; XtVaGetValues(parent->submenu_id, XtNchildren, &children, XtNnumChildren, &num_children, NULL); for (i = 0; i < (int)num_children; ++i) { XtVaSetValues(children[i], XtNrightMargin, puller_width, NULL); } } gui_athena_menu_colors(menu->id); gui_athena_menu_font(menu->id); pullright_name = make_pull_name(menu->dname); menu->submenu_id = XtVaCreatePopupShell((char *)pullright_name, simpleMenuWidgetClass, parent->submenu_id, XtNtranslations, menuTrans, NULL); gui_athena_menu_colors(menu->submenu_id); gui_athena_menu_font(menu->submenu_id); vim_free(pullright_name); XtAddCallback(menu->submenu_id, XtNpopupCallback, gui_athena_popup_callback, (XtPointer)menu); if (parent->parent != NULL) XtOverrideTranslations(parent->submenu_id, parentTrans); } a_cur_menu = NULL; } /* Used to determine whether a SimpleMenu has pulldown entries. * * "id" is the parent of the menu items. * Ignore widget "ignore" in the pane. */ static Boolean gui_athena_menu_has_submenus(Widget id, Widget ignore) { WidgetList children; Cardinal num_children; int i; XtVaGetValues(id, XtNchildren, &children, XtNnumChildren, &num_children, NULL); for (i = 0; i < (int)num_children; ++i) { if (children[i] == ignore) continue; if (has_submenu(children[i])) return True; } return False; } static void gui_athena_menu_font(Widget id) { #ifdef FONTSET_ALWAYS if (gui.menu_fontset != NOFONTSET) { if (XtIsManaged(id)) { XtUnmanageChild(id); XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL); /* We should force the widget to recalculate its * geometry now. */ XtManageChild(id); } else XtVaSetValues(id, XtNfontSet, gui.menu_fontset, NULL); if (has_submenu(id)) XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL); } #else int managed = FALSE; if (gui.menu_font != NOFONT) { if (XtIsManaged(id)) { XtUnmanageChild(id); managed = TRUE; } # ifdef FEAT_XFONTSET if (gui.fontset != NOFONTSET) XtVaSetValues(id, XtNfontSet, gui.menu_font, NULL); else # endif XtVaSetValues(id, XtNfont, gui.menu_font, NULL); if (has_submenu(id)) XtVaSetValues(id, XtNrightBitmap, pullerBitmap, NULL); /* Force the widget to recalculate its geometry now. */ if (managed) XtManageChild(id); } #endif } void gui_mch_new_menu_font(void) { Pixmap oldpuller = None; if (menuBar == (Widget)0) return; if (pullerBitmap != None) { oldpuller = pullerBitmap; pullerBitmap = gui_athena_create_pullright_pixmap(NULL); } gui_mch_submenu_change(root_menu, FALSE); { /* Iterate through the menubar menu items and get the height of * each one. The menu bar height is set to the maximum of all * the heights. */ vimmenu_T *mp; int max_height = 9999; for (mp = root_menu; mp != NULL; mp = mp->next) { if (menu_is_menubar(mp->dname)) { Dimension height; XtVaGetValues(mp->id, XtNheight, &height, NULL); if (height < max_height) max_height = height; } } if (max_height != 9999) { /* Don't update the menu height when it was set at a fixed value */ if (!gui.menu_height_fixed) { Dimension space, border; XtVaGetValues(menuBar, XtNvSpace, &space, XtNborderWidth, &border, NULL); gui.menu_height = max_height + 2 * (space + border); } } } /* Now, to simulate the window being resized. Only, this * will resize the window to its current state. * * There has to be a better way, but I do not see one at this time. * (David Harrison) */ { Position w, h; XtVaGetValues(vimShell, XtNwidth, &w, XtNheight, &h, NULL); gui_resize_shell(w, h #ifdef FEAT_XIM - xim_get_status_area_height() #endif ); } gui_set_shellsize(FALSE, TRUE, RESIZE_VERT); ui_new_shellsize(); if (oldpuller != None) XFreePixmap(gui.dpy, oldpuller); } #if defined(FEAT_BEVAL_GUI) || defined(PROTO) void gui_mch_new_tooltip_font(void) { # ifdef FEAT_TOOLBAR vimmenu_T *menu; if (toolBar == (Widget)0) return; menu = gui_find_menu((char_u *)"ToolBar"); if (menu != NULL) gui_mch_submenu_change(menu, FALSE); # endif } void gui_mch_new_tooltip_colors(void) { # ifdef FEAT_TOOLBAR vimmenu_T *menu; if (toolBar == (Widget)0) return; menu = gui_find_menu((char_u *)"ToolBar"); if (menu != NULL) gui_mch_submenu_change(menu, TRUE); # endif } #endif static void gui_mch_submenu_change( vimmenu_T *menu, int colors) /* TRUE for colors, FALSE for font */ { vimmenu_T *mp; for (mp = menu; mp != NULL; mp = mp->next) { if (mp->id != (Widget)0) { if (colors) { gui_athena_menu_colors(mp->id); #ifdef FEAT_TOOLBAR /* For a toolbar item: Free the pixmap and allocate a new one, * so that the background color is right. */ if (mp->image != (Pixmap)0) { XFreePixmap(gui.dpy, mp->image); get_toolbar_pixmap(mp, &mp->image); if (mp->image != (Pixmap)0) XtVaSetValues(mp->id, XtNbitmap, mp->image, NULL); } # ifdef FEAT_BEVAL_GUI /* If we have a tooltip, then we need to change its colors */ if (mp->tip != NULL) { Arg args[2]; args[0].name = XtNbackground; args[0].value = gui.tooltip_bg_pixel; args[1].name = XtNforeground; args[1].value = gui.tooltip_fg_pixel; XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args)); } # endif #endif } else { gui_athena_menu_font(mp->id); #ifdef FEAT_BEVAL_GUI /* If we have a tooltip, then we need to change its font */ /* Assume XtNinternational == True (in createBalloonEvalWindow) */ if (mp->tip != NULL) { Arg args[1]; args[0].name = XtNfontSet; args[0].value = (XtArgVal)gui.tooltip_fontset; XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args)); } #endif } } if (mp->children != NULL) { /* Set the colors/font for the tear off widget */ if (mp->submenu_id != (Widget)0) { if (colors) gui_athena_menu_colors(mp->submenu_id); else gui_athena_menu_font(mp->submenu_id); } /* Set the colors for the children */ gui_mch_submenu_change(mp->children, colors); } } } /* * Make a submenu name into a pullright name. * Replace '.' by '_', can't include '.' in the submenu name. */ static char_u * make_pull_name(char_u * name) { char_u *pname; char_u *p; pname = vim_strnsave(name, STRLEN(name) + strlen("-pullright")); if (pname != NULL) { strcat((char *)pname, "-pullright"); while ((p = vim_strchr(pname, '.')) != NULL) *p = '_'; } return pname; } void gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED) { vimmenu_T *parent = menu->parent; a_cur_menu = menu; # ifdef FEAT_TOOLBAR if (menu_is_toolbar(parent->name)) { WidgetClass type; int n; Arg args[21]; n = 0; if (menu_is_separator(menu->name)) { XtSetArg(args[n], XtNlabel, ""); n++; XtSetArg(args[n], XtNborderWidth, 0); n++; } else { get_toolbar_pixmap(menu, &menu->image); XtSetArg(args[n], XtNlabel, menu->dname); n++; XtSetArg(args[n], XtNinternalHeight, 1); n++; XtSetArg(args[n], XtNinternalWidth, 1); n++; XtSetArg(args[n], XtNborderWidth, 1); n++; if (menu->image != 0) XtSetArg(args[n], XtNbitmap, menu->image); n++; } XtSetArg(args[n], XtNhighlightThickness, 0); n++; type = commandWidgetClass; /* TODO: figure out the position in the toolbar? * This currently works fine for the default toolbar, but * what if we add/remove items during later runtime? */ /* NOTE: "idx" isn't used here. The position is calculated by * athena_calculate_ins_pos(). The position it calculates * should be equal to "idx". */ /* TODO: Could we just store "idx" and use that as the child * placement? */ if (menu->id == NULL) { menu->id = XtCreateManagedWidget((char *)menu->dname, type, toolBar, args, n); XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb, menu); } else XtSetValues(menu->id, args, n); gui_athena_menu_colors(menu->id); #ifdef FEAT_BEVAL_GUI gui_mch_menu_set_tip(menu); #endif menu->parent = parent; menu->submenu_id = NULL; if (!XtIsManaged(toolBar) && vim_strchr(p_go, GO_TOOLBAR) != NULL) gui_mch_show_toolbar(TRUE); gui.toolbar_height = gui_mch_compute_toolbar_height(); return; } /* toolbar menu item */ # endif /* Add menu separator */ if (menu_is_separator(menu->name)) { menu->submenu_id = (Widget)0; menu->id = XtVaCreateManagedWidget((char *)menu->dname, smeLineObjectClass, parent->submenu_id, NULL); if (menu->id == (Widget)0) return; gui_athena_menu_colors(menu->id); } else { if (parent != NULL && parent->submenu_id != (Widget)0) { menu->submenu_id = (Widget)0; menu->id = XtVaCreateManagedWidget((char *)menu->dname, smeBSBObjectClass, parent->submenu_id, XtNlabel, menu->dname, #ifdef FONTSET_ALWAYS XtNinternational, True, #endif NULL); if (menu->id == (Widget)0) return; /* If there are other "pulldown" items in this pane, then adjust * the right margin to accommodate the arrow pixmap, otherwise * the right margin will be the same as the left margin. */ { Dimension left_margin; XtVaGetValues(menu->id, XtNleftMargin, &left_margin, NULL); XtVaSetValues(menu->id, XtNrightMargin, gui_athena_menu_has_submenus(parent->submenu_id, NULL) ? puller_width : left_margin, NULL); } gui_athena_menu_colors(menu->id); gui_athena_menu_font(menu->id); XtAddCallback(menu->id, XtNcallback, gui_x11_menu_cb, (XtPointer)menu); } } a_cur_menu = NULL; } #if defined(FEAT_TOOLBAR) || defined(PROTO) void gui_mch_show_toolbar(int showit) { Cardinal numChildren; /* how many children toolBar has */ if (toolBar == (Widget)0) return; XtVaGetValues(toolBar, XtNnumChildren, &numChildren, NULL); if (showit && numChildren > 0) { /* Assume that we want to show the toolbar if p_toolbar contains valid * option settings, therefore p_toolbar must not be NULL. */ WidgetList children; XtVaGetValues(toolBar, XtNchildren, &children, NULL); { void (*action)(BalloonEval *); int text = 0; if (strstr((const char *)p_toolbar, "tooltips")) action = &gui_mch_enable_beval_area; else action = &gui_mch_disable_beval_area; if (strstr((const char *)p_toolbar, "text")) text = 1; else if (strstr((const char *)p_toolbar, "icons")) text = -1; if (text != 0) { vimmenu_T *toolbar; vimmenu_T *cur; for (toolbar = root_menu; toolbar; toolbar = toolbar->next) if (menu_is_toolbar(toolbar->dname)) break; /* Assumption: toolbar is NULL if there is no toolbar, * otherwise it contains the toolbar menu structure. * * Assumption: "numChildren" == the number of items in the list * of items beginning with toolbar->children. */ if (toolbar) { for (cur = toolbar->children; cur; cur = cur->next) { Arg args[2]; int n = 0; /* Enable/Disable tooltip (OK to enable while currently * enabled) */ if (cur->tip != NULL) (*action)(cur->tip); if (text == 1) { XtSetArg(args[n], XtNbitmap, None); n++; XtSetArg(args[n], XtNlabel, menu_is_separator(cur->name) ? "" : (char *)cur->dname); n++; } else { XtSetArg(args[n], XtNbitmap, cur->image); n++; XtSetArg(args[n], XtNlabel, (cur->image == None) ? menu_is_separator(cur->name) ? "" : (char *)cur->dname : (char *)None); n++; } if (cur->id != NULL) { XtUnmanageChild(cur->id); XtSetValues(cur->id, args, n); XtManageChild(cur->id); } } } } } gui.toolbar_height = gui_mch_compute_toolbar_height(); XtManageChild(toolBar); if (XtIsManaged(menuBar)) { XtVaSetValues(textArea, XtNvertDistance, gui.toolbar_height + gui.menu_height, NULL); XtVaSetValues(toolBar, XtNvertDistance, gui.menu_height, NULL); } else { XtVaSetValues(textArea, XtNvertDistance, gui.toolbar_height, NULL); XtVaSetValues(toolBar, XtNvertDistance, 0, NULL); } } else { gui.toolbar_height = 0; if (XtIsManaged(menuBar)) XtVaSetValues(textArea, XtNvertDistance, gui.menu_height, NULL); else XtVaSetValues(textArea, XtNvertDistance, 0, NULL); XtUnmanageChild(toolBar); } gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); } int gui_mch_compute_toolbar_height(void) { Dimension height; /* total Toolbar height */ Dimension whgt; /* height of each widget */ Dimension marginHeight; /* XmNmarginHeight of toolBar */ Dimension shadowThickness; /* thickness of Xtparent(toolBar) */ WidgetList children; /* list of toolBar's children */ Cardinal numChildren; /* how many children toolBar has */ int i; height = 0; shadowThickness = 0; marginHeight = 0; if (toolBar != (Widget)0) { XtVaGetValues(toolBar, XtNborderWidth, &shadowThickness, XtNvSpace, &marginHeight, XtNchildren, &children, XtNnumChildren, &numChildren, NULL); for (i = 0; i < (int)numChildren; i++) { whgt = 0; XtVaGetValues(children[i], XtNheight, &whgt, NULL); if (height < whgt) height = whgt; } } return (int)(height + (marginHeight << 1) + (shadowThickness << 1)); } void gui_mch_get_toolbar_colors( Pixel *bgp, Pixel *fgp, Pixel *bsp, Pixel *tsp, Pixel *hsp) { XtVaGetValues(toolBar, XtNbackground, bgp, XtNborderColor, fgp, NULL); *bsp = *bgp; *tsp = *fgp; *hsp = *tsp; } #endif void gui_mch_toggle_tearoffs(int enable UNUSED) { /* no tearoff menus */ } void gui_mch_new_menu_colors(void) { if (menuBar == (Widget)0) return; if (gui.menu_fg_pixel != INVALCOLOR) XtVaSetValues(menuBar, XtNborderColor, gui.menu_fg_pixel, NULL); gui_athena_menu_colors(menuBar); #ifdef FEAT_TOOLBAR gui_athena_menu_colors(toolBar); #endif gui_mch_submenu_change(root_menu, TRUE); } /* * Destroy the machine specific menu widget. */ void gui_mch_destroy_menu(vimmenu_T *menu) { Widget parent; /* There is no item for the toolbar. */ if (menu->id == (Widget)0) return; parent = XtParent(menu->id); /* When removing the last "pulldown" menu item from a pane, adjust the * right margins of the remaining widgets. */ if (menu->submenu_id != (Widget)0) { /* Go through the menu items in the parent of this item and * adjust their margins, if necessary. * This takes care of the case when we delete the last menu item in a * pane that has a submenu. In this case, there will be no arrow * pixmaps shown anymore. */ { WidgetList children; Cardinal num_children; int i; Dimension right_margin = 0; Boolean get_left_margin = False; XtVaGetValues(parent, XtNchildren, &children, XtNnumChildren, &num_children, NULL); if (gui_athena_menu_has_submenus(parent, menu->id)) right_margin = puller_width; else get_left_margin = True; for (i = 0; i < (int)num_children; ++i) { if (children[i] == menu->id) continue; if (get_left_margin == True) { Dimension left_margin; XtVaGetValues(children[i], XtNleftMargin, &left_margin, NULL); XtVaSetValues(children[i], XtNrightMargin, left_margin, NULL); } else XtVaSetValues(children[i], XtNrightMargin, right_margin, NULL); } } } /* Please be sure to destroy the parent widget first (i.e. menu->id). * * This code should be basically identical to that in the file gui_motif.c * because they are both Xt based. */ if (menu->id != (Widget)0) { Cardinal num_children; Dimension height, space, border; XtVaGetValues(menuBar, XtNvSpace, &space, XtNborderWidth, &border, NULL); XtVaGetValues(menu->id, XtNheight, &height, NULL); #if defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL_GUI) if (parent == toolBar && menu->tip != NULL) { /* We try to destroy this before the actual menu, because there are * callbacks, etc. that will be unregistered during the tooltip * destruction. * * If you call "gui_mch_destroy_beval_area()" after destroying * menu->id, then the tooltip's window will have already been * deallocated by Xt, and unknown behaviour will ensue (probably * a core dump). */ gui_mch_destroy_beval_area(menu->tip); menu->tip = NULL; } #endif /* * This is a hack to stop the Athena simpleMenuWidget from getting a * BadValue error when a menu's last child is destroyed. We check to * see if this is the last child and if so, don't delete it. The parent * will be deleted soon anyway, and it will delete its children like * all good widgets do. */ /* NOTE: The cause of the BadValue X Protocol Error is because when the * last child is destroyed, it is first unmanaged, thus causing a * geometry resize request from the parent Shell widget. * Since the Shell widget has no more children, it is resized to have * width/height of 0. XConfigureWindow() is then called with the * width/height of 0, which generates the BadValue. * * This happens in phase two of the widget destruction process. */ { if (parent != menuBar #ifdef FEAT_TOOLBAR && parent != toolBar #endif ) { XtVaGetValues(parent, XtNnumChildren, &num_children, NULL); if (num_children > 1) XtDestroyWidget(menu->id); } else XtDestroyWidget(menu->id); menu->id = (Widget)0; } if (parent == menuBar) { if (!gui.menu_height_fixed) gui.menu_height = height + 2 * (space + border); } #ifdef FEAT_TOOLBAR else if (parent == toolBar) { /* When removing last toolbar item, don't display the toolbar. */ XtVaGetValues(toolBar, XtNnumChildren, &num_children, NULL); if (num_children == 0) gui_mch_show_toolbar(FALSE); else gui.toolbar_height = gui_mch_compute_toolbar_height(); } #endif } if (menu->submenu_id != (Widget)0) { XtDestroyWidget(menu->submenu_id); menu->submenu_id = (Widget)0; } } static void gui_athena_menu_timeout( XtPointer client_data, XtIntervalId *id UNUSED) { Widget w = (Widget)client_data; Widget popup; timer = 0; if (XtIsSubclass(w,smeBSBObjectClass)) { Pixmap p; XtVaGetValues(w, XtNrightBitmap, &p, NULL); if ((p != None) && (p != XtUnspecifiedPixmap)) { /* We are dealing with an item that has a submenu */ popup = get_popup_entry(XtParent(w)); if (popup == (Widget)0) return; XtPopup(popup, XtGrabNonexclusive); } } } /* This routine is used to calculate the position (in screen coordinates) * where a submenu should appear relative to the menu entry that popped it * up. It should appear even with and just slightly to the left of the * rightmost end of the menu entry that caused the popup. * * This is called when XtPopup() is called. */ static void gui_athena_popup_callback( Widget w, XtPointer client_data, XtPointer call_data UNUSED) { /* Assumption: XtIsSubclass(XtParent(w),simpleMenuWidgetClass) */ vimmenu_T *menu = (vimmenu_T *)client_data; Dimension width; Position root_x, root_y; /* First, popdown any siblings that may have menus popped up */ { vimmenu_T *i; for (i = menu->parent->children; i != NULL; i = i->next) { if (i->submenu_id != NULL && XtIsManaged(i->submenu_id)) XtPopdown(i->submenu_id); } } XtVaGetValues(XtParent(w), XtNwidth, &width, NULL); /* Assumption: XawSimpleMenuGetActiveEntry(XtParent(w)) == menu->id */ /* i.e. This IS the active entry */ XtTranslateCoords(menu->id,width - 5, 0, &root_x, &root_y); XtVaSetValues(w, XtNx, root_x, XtNy, root_y, NULL); } static void gui_athena_popdown_submenus_action( Widget w, XEvent *event, String *args, Cardinal *nargs) { WidgetList children; Cardinal num_children; XtVaGetValues(w, XtNchildren, &children, XtNnumChildren, &num_children, NULL); for (; num_children > 0; --num_children) { Widget child = children[num_children - 1]; if (has_submenu(child)) { Widget temp_w; temp_w = submenu_widget(child); gui_athena_popdown_submenus_action(temp_w,event,args,nargs); XtPopdown(temp_w); } } } /* Used to determine if the given widget has a submenu that can be popped up. */ static Boolean has_submenu(Widget widget) { if ((widget != NULL) && XtIsSubclass(widget,smeBSBObjectClass)) { Pixmap p; XtVaGetValues(widget, XtNrightBitmap, &p, NULL); if ((p != None) && (p != XtUnspecifiedPixmap)) return True; } return False; } static void gui_athena_delayed_arm_action( Widget w, XEvent *event, String *args, Cardinal *nargs) { Dimension width, height; if (event->type != MotionNotify) return; XtVaGetValues(w, XtNwidth, &width, XtNheight, &height, NULL); if (event->xmotion.x >= (int)width || event->xmotion.y >= (int)height) return; { static Widget previous_active_widget = NULL; Widget current; current = XawSimpleMenuGetActiveEntry(w); if (current != previous_active_widget) { if (timer) { /* If the timeout hasn't been triggered, remove it */ XtRemoveTimeOut(timer); } gui_athena_popdown_submenus_action(w,event,args,nargs); if (has_submenu(current)) { XtAppAddTimeOut(XtWidgetToApplicationContext(w), 600L, gui_athena_menu_timeout, (XtPointer)current); } previous_active_widget = current; } } } static Widget get_popup_entry(Widget w) { Widget menuw; /* Get the active entry for the current menu */ if ((menuw = XawSimpleMenuGetActiveEntry(w)) == (Widget)0) return NULL; return submenu_widget(menuw); } /* Given the widget that has been determined to have a submenu, return the submenu widget * that is to be popped up. */ static Widget submenu_widget(Widget widget) { /* Precondition: has_submenu(widget) == True * XtIsSubclass(XtParent(widget),simpleMenuWidgetClass) == True */ char_u *pullright_name; Widget popup; pullright_name = make_pull_name((char_u *)XtName(widget)); popup = XtNameToWidget(XtParent(widget), (char *)pullright_name); vim_free(pullright_name); return popup; /* Postcondition: (popup != NULL) implies * (XtIsSubclass(popup,simpleMenuWidgetClass) == True) */ } void gui_mch_show_popupmenu(vimmenu_T *menu) { int rootx, rooty, winx, winy; Window root, child; unsigned int mask; if (menu->submenu_id == (Widget)0) return; /* Position the popup menu at the pointer */ if (XQueryPointer(gui.dpy, XtWindow(vimShell), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) { rootx -= 30; if (rootx < 0) rootx = 0; rooty -= 5; if (rooty < 0) rooty = 0; XtVaSetValues(menu->submenu_id, XtNx, rootx, XtNy, rooty, NULL); } XtOverrideTranslations(menu->submenu_id, popupTrans); XtPopupSpringLoaded(menu->submenu_id); } #endif /* FEAT_MENU */ /* * Set the menu and scrollbar colors to their default values. */ void gui_mch_def_colors(void) { /* * Get the colors ourselves. Using the automatic conversion doesn't * handle looking for approximate colors. */ if (gui.in_use) { gui.menu_fg_pixel = gui_get_color((char_u *)gui.rsrc_menu_fg_name); gui.menu_bg_pixel = gui_get_color((char_u *)gui.rsrc_menu_bg_name); gui.scroll_fg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_fg_name); gui.scroll_bg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_bg_name); #ifdef FEAT_BEVAL_GUI gui.tooltip_fg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_fg_name); gui.tooltip_bg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_bg_name); #endif } } /* * Scrollbar stuff. */ void gui_mch_set_scrollbar_thumb( scrollbar_T *sb, long val, long size, long max) { double v, s; if (sb->id == (Widget)0) return; /* * Athena scrollbar must go from 0.0 to 1.0. */ if (max == 0) { /* So you can't scroll it at all (normally it scrolls past end) */ #ifdef FEAT_GUI_NEXTAW XawScrollbarSetThumb(sb->id, 0.0, 1.0); #else vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0); #endif } else { v = (double)val / (double)(max + 1); s = (double)size / (double)(max + 1); #ifdef FEAT_GUI_NEXTAW XawScrollbarSetThumb(sb->id, v, s); #else vim_XawScrollbarSetThumb(sb->id, v, s, 1.0); #endif } } void gui_mch_set_scrollbar_pos( scrollbar_T *sb, int x, int y, int w, int h) { if (sb->id == (Widget)0) return; XtUnmanageChild(sb->id); XtVaSetValues(sb->id, XtNhorizDistance, x, XtNvertDistance, y, XtNwidth, w, XtNheight, h, NULL); XtManageChild(sb->id); } void gui_mch_enable_scrollbar(scrollbar_T *sb, int flag) { if (sb->id != (Widget)0) { if (flag) XtManageChild(sb->id); else XtUnmanageChild(sb->id); } } void gui_mch_create_scrollbar( scrollbar_T *sb, int orient) /* SBAR_VERT or SBAR_HORIZ */ { sb->id = XtVaCreateWidget("scrollBar", #ifdef FEAT_GUI_NEXTAW scrollbarWidgetClass, vimForm, #else vim_scrollbarWidgetClass, vimForm, #endif XtNresizable, True, XtNtop, XtChainTop, XtNbottom, XtChainTop, XtNleft, XtChainLeft, XtNright, XtChainLeft, XtNborderWidth, 0, XtNorientation, (orient == SBAR_VERT) ? XtorientVertical : XtorientHorizontal, XtNforeground, gui.scroll_fg_pixel, XtNbackground, gui.scroll_bg_pixel, NULL); if (sb->id == (Widget)0) return; XtAddCallback(sb->id, XtNjumpProc, gui_athena_scroll_cb_jump, (XtPointer)sb->ident); XtAddCallback(sb->id, XtNscrollProc, gui_athena_scroll_cb_scroll, (XtPointer)sb->ident); #ifdef FEAT_GUI_NEXTAW XawScrollbarSetThumb(sb->id, 0.0, 1.0); #else vim_XawScrollbarSetThumb(sb->id, 0.0, 1.0, 0.0); #endif } void gui_mch_destroy_scrollbar(scrollbar_T *sb) { if (sb->id != (Widget)0) XtDestroyWidget(sb->id); } void gui_mch_set_scrollbar_colors(scrollbar_T *sb) { if (sb->id != (Widget)0) XtVaSetValues(sb->id, XtNforeground, gui.scroll_fg_pixel, XtNbackground, gui.scroll_bg_pixel, NULL); /* This is needed for the rectangle below the vertical scrollbars. */ if (sb == &gui.bottom_sbar && vimForm != (Widget)0) gui_athena_scroll_colors(vimForm); } /* * Miscellaneous stuff: */ Window gui_x11_get_wid(void) { return XtWindow(textArea); } #if defined(FEAT_BROWSE) || defined(PROTO) /* * Put up a file requester. * Returns the selected name in allocated memory, or NULL for Cancel. */ char_u * gui_mch_browse( int saving UNUSED, /* select file to write */ char_u *title, /* title for the window */ char_u *dflt, /* default name */ char_u *ext UNUSED, /* extension added */ char_u *initdir, /* initial directory, NULL for current dir */ char_u *filter UNUSED) /* file name filter */ { Position x, y; char_u dirbuf[MAXPATHL]; /* Concatenate "initdir" and "dflt". */ if (initdir == NULL || *initdir == NUL) mch_dirname(dirbuf, MAXPATHL); else if (STRLEN(initdir) + 2 < MAXPATHL) STRCPY(dirbuf, initdir); else dirbuf[0] = NUL; if (dflt != NULL && *dflt != NUL && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL) { add_pathsep(dirbuf); STRCAT(dirbuf, dflt); } /* Position the file selector just below the menubar */ XtTranslateCoords(vimShell, (Position)0, (Position) #ifdef FEAT_MENU gui.menu_height #else 0 #endif , &x, &y); return (char_u *)vim_SelFile(vimShell, (char *)title, (char *)dirbuf, NULL, (int)x, (int)y, gui.menu_fg_pixel, gui.menu_bg_pixel, gui.scroll_fg_pixel, gui.scroll_bg_pixel); } #endif #if defined(FEAT_GUI_DIALOG) || defined(PROTO) static int dialogStatus; static Atom dialogatom; /* * Callback function for the textfield. When CR is hit this works like * hitting the "OK" button, ESC like "Cancel". */ static void keyhit_callback( Widget w UNUSED, XtPointer client_data UNUSED, XEvent *event, Boolean *cont UNUSED) { char buf[2]; if (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) { if (*buf == CAR) dialogStatus = 1; else if (*buf == ESC) dialogStatus = 0; } } static void butproc( Widget w UNUSED, XtPointer client_data, XtPointer call_data UNUSED) { dialogStatus = (int)(long)client_data + 1; } /* * Function called when dialog window closed. */ static void dialog_wm_handler( Widget w UNUSED, XtPointer client_data UNUSED, XEvent *event, Boolean *dum UNUSED) { if (event->type == ClientMessage && (Atom)((XClientMessageEvent *)event)->data.l[0] == dialogatom) dialogStatus = 0; } 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) { char_u *buts; char_u *p, *next; XtAppContext app; XEvent event; Position wd, hd; Position wv, hv; Position x, y; Widget dialog; Widget dialogshell; Widget dialogmessage; Widget dialogtextfield = 0; Widget dialogButton; Widget prev_dialogButton = NULL; int butcount; int vertical; if (title == NULL) title = (char_u *)_("Vim dialog"); dialogStatus = -1; /* if our pointer is currently hidden, then we should show it. */ gui_mch_mousehide(FALSE); /* Check 'v' flag in 'guioptions': vertical button placement. */ vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL); /* The shell is created each time, to make sure it is resized properly */ dialogshell = XtVaCreatePopupShell("dialogShell", transientShellWidgetClass, vimShell, XtNtitle, title, NULL); if (dialogshell == (Widget)0) goto error; dialog = XtVaCreateManagedWidget("dialog", formWidgetClass, dialogshell, XtNdefaultDistance, 20, NULL); if (dialog == (Widget)0) goto error; gui_athena_menu_colors(dialog); dialogmessage = XtVaCreateManagedWidget("dialogMessage", labelWidgetClass, dialog, XtNlabel, message, XtNtop, XtChainTop, XtNbottom, XtChainTop, XtNleft, XtChainLeft, XtNright, XtChainLeft, XtNresizable, True, XtNborderWidth, 0, NULL); gui_athena_menu_colors(dialogmessage); if (textfield != NULL) { dialogtextfield = XtVaCreateManagedWidget("textfield", asciiTextWidgetClass, dialog, XtNwidth, 400, XtNtop, XtChainTop, XtNbottom, XtChainTop, XtNleft, XtChainLeft, XtNright, XtChainRight, XtNfromVert, dialogmessage, XtNresizable, True, XtNstring, textfield, XtNlength, IOSIZE, XtNuseStringInPlace, True, XtNeditType, XawtextEdit, XtNwrap, XawtextWrapNever, XtNresize, XawtextResizeHeight, NULL); XtManageChild(dialogtextfield); XtAddEventHandler(dialogtextfield, KeyPressMask, False, (XtEventHandler)keyhit_callback, (XtPointer)NULL); XawTextSetInsertionPoint(dialogtextfield, (XawTextPosition)STRLEN(textfield)); XtSetKeyboardFocus(dialog, dialogtextfield); } /* make a copy, so that we can insert NULs */ buts = vim_strsave(buttons); if (buts == NULL) return -1; p = buts; for (butcount = 0; *p; ++butcount) { for (next = p; *next; ++next) { if (*next == DLG_HOTKEY_CHAR) STRMOVE(next, next + 1); if (*next == DLG_BUTTON_SEP) { *next++ = NUL; break; } } dialogButton = XtVaCreateManagedWidget("button", commandWidgetClass, dialog, XtNlabel, p, XtNtop, XtChainBottom, XtNbottom, XtChainBottom, XtNleft, XtChainLeft, XtNright, XtChainLeft, XtNfromVert, textfield == NULL ? dialogmessage : dialogtextfield, XtNvertDistance, vertical ? 4 : 20, XtNresizable, False, NULL); gui_athena_menu_colors(dialogButton); if (butcount > 0) XtVaSetValues(dialogButton, vertical ? XtNfromVert : XtNfromHoriz, prev_dialogButton, NULL); XtAddCallback(dialogButton, XtNcallback, butproc, (XtPointer)(long_u)butcount); p = next; prev_dialogButton = dialogButton; } vim_free(buts); XtRealizeWidget(dialogshell); /* Setup for catching the close-window event, don't let it close Vim! */ dialogatom = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(gui.dpy, XtWindow(dialogshell), &dialogatom, 1); XtAddEventHandler(dialogshell, NoEventMask, True, dialog_wm_handler, NULL); XtVaGetValues(dialogshell, XtNwidth, &wd, XtNheight, &hd, NULL); XtVaGetValues(vimShell, XtNwidth, &wv, XtNheight, &hv, NULL); XtTranslateCoords(vimShell, (Position)((wv - wd) / 2), (Position)((hv - hd) / 2), &x, &y); if (x < 0) x = 0; if (y < 0) y = 0; XtVaSetValues(dialogshell, XtNx, x, XtNy, y, NULL); /* Position the mouse pointer in the dialog, required for when focus * follows mouse. */ XWarpPointer(gui.dpy, (Window)0, XtWindow(dialogshell), 0, 0, 0, 0, 20, 40); app = XtWidgetToApplicationContext(dialogshell); XtPopup(dialogshell, XtGrabNonexclusive); for (;;) { XtAppNextEvent(app, &event); XtDispatchEvent(&event); if (dialogStatus >= 0) break; } XtPopdown(dialogshell); if (textfield != NULL && dialogStatus < 0) *textfield = NUL; error: XtDestroyWidget(dialogshell); return dialogStatus; } #endif #if defined(FEAT_GUI_DIALOG) || defined(FEAT_MENU) /* * Set the colors of Widget "id" to the menu colors. */ static void gui_athena_menu_colors(Widget id) { if (gui.menu_bg_pixel != INVALCOLOR) XtVaSetValues(id, XtNbackground, gui.menu_bg_pixel, NULL); if (gui.menu_fg_pixel != INVALCOLOR) XtVaSetValues(id, XtNforeground, gui.menu_fg_pixel, NULL); } #endif /* * Set the colors of Widget "id" to the scroll colors. */ static void gui_athena_scroll_colors(Widget id) { if (gui.scroll_bg_pixel != INVALCOLOR) XtVaSetValues(id, XtNbackground, gui.scroll_bg_pixel, NULL); if (gui.scroll_fg_pixel != INVALCOLOR) XtVaSetValues(id, XtNforeground, gui.scroll_fg_pixel, NULL); }