Mercurial > vim
view src/gui_athena.c @ 10801:e7b8be5ecacd v8.0.0290
patch 8.0.0290: cursor positioning wrong if wide character wraps
commit https://github.com/vim/vim/commit/04e87b72c5fa88b7034a5b0ec0be6a7ad763e9d2
Author: Bram Moolenaar <Bram@vim.org>
Date: Wed Feb 1 21:23:10 2017 +0100
patch 8.0.0290: cursor positioning wrong if wide character wraps
Problem: If a wide character doesn't fit at the end of the screen line, and
the line doesn't fit on the screen, then the cursor position may
be wrong. (anliting)
Solution: Don't skip over wide character. (Christian Brabandt, closes #1408)
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Wed, 01 Feb 2017 21:30:04 +0100 |
parents | 4aead6a9b7a9 |
children | 90af0c60d78d |
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 <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 */ #include "vim.h" #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 Pixmap gui_athena_create_pullright_pixmap(Widget); static void gui_athena_menu_timeout(XtPointer, XtIntervalId *); 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 static void gui_athena_scroll_cb_jump(Widget, XtPointer, XtPointer); static void gui_athena_scroll_cb_scroll(Widget, XtPointer, XtPointer); #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); static void get_toolbar_pixmap(vimmenu_T *menu, 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); static Boolean gui_athena_menu_has_submenus(Widget, Widget); 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 it's * 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 it's 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 it's 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) || 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 /* If we have a tooltip, then we need to change it's 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 /* If we have a tooltip, then we need to change it's 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_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) 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 it's 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.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 } #if defined(FEAT_WINDOWS) || defined(PROTO) void gui_mch_destroy_scrollbar(scrollbar_T *sb) { if (sb->id != (Widget)0) XtDestroyWidget(sb->id); } #endif 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; static void keyhit_callback(Widget w, XtPointer client_data, XEvent *event, Boolean *cont); static void butproc(Widget w, XtPointer client_data, XtPointer call_data); static void dialog_wm_handler(Widget w, XtPointer client_data, XEvent *event, Boolean *dum); /* * 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); }