Mercurial > vim
view src/gui_motif.c @ 33664:06b59278bfcf v9.0.2070
patch 9.0.2070: [security] disallow setting env in restricted mode
Commit: https://github.com/vim/vim/commit/6b89dd6a7257a1e2e9c7ea070b407bc4674a5118
Author: Christian Brabandt <cb@256bit.org>
Date: Thu Oct 26 22:14:17 2023 +0200
patch 9.0.2070: [security] disallow setting env in restricted mode
Problem: [security] disallow setting env in restricted mode
Solution: Setting environment variables in restricted mode could
potentially be used to execute shell commands. Disallow this.
restricted mode: disable allow setting of environment variables
Setting environment variables in restricted mode, may have some unwanted
consequences. So, for example by setting $GCONV_PATH in restricted mode
and then calling the iconv() function, one may be able to execute some
unwanted payload, because the `iconv_open()` function internally uses
the `$GCONV_PATH` variable to find its conversion data.
So let's disable setting environment variables, even so this is no
complete protection, since we are not clearing the existing environment.
I tried a few ways but wasn't successful :(
One could also argue to disable the iconv() function completely in
restricted mode, but who knows what other API functions can be
influenced by setting some other unrelated environment variables.
So let's leave it as it is currently.
closes: #13394
See: https://huntr.com/bounties/b0a2eda1-459c-4e36-98e6-0cc7d7faccfe/
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 26 Oct 2023 22:30:03 +0200 |
parents | f5675b905d89 |
children | d299b4b3099f |
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 * * 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 <Xm/Form.h> #include <Xm/RowColumn.h> #include <Xm/PushB.h> #include <Xm/Text.h> #include <Xm/TextF.h> #include <Xm/Separator.h> #include <Xm/Label.h> #include <Xm/CascadeB.h> #include <Xm/ScrollBar.h> #include <Xm/MenuShell.h> #include <Xm/DrawingA.h> #if (XmVersion >= 1002) # include <Xm/RepType.h> #endif #include <Xm/Frame.h> #include <Xm/LabelG.h> #include <Xm/ToggleBG.h> #include <Xm/SeparatoG.h> #include <Xm/XmP.h> #include <X11/keysym.h> #include <X11/Xatom.h> #include <X11/StringDefs.h> #include <X11/Intrinsic.h> #ifdef HAVE_X11_XPM_H # if defined(VMS) # include <xpm.h> # else # include <X11/xpm.h> # endif #else # ifdef HAVE_XM_XPMP_H # include <Xm/XpmP.h> # endif #endif #ifdef HAVE_XM_NOTEBOOK_H # include <Xm/Notebook.h> #endif #include "gui_xmebw.h" // for our Enhanced Button Widget #if defined(FEAT_GUI_DIALOG) && defined(HAVE_XPM) # include "../pixmaps/alert.xpm" # include "../pixmaps/error.xpm" # include "../pixmaps/generic.xpm" # include "../pixmaps/info.xpm" # include "../pixmaps/quest.xpm" #endif #define MOTIF_POPUP extern Widget vimShell; static Widget vimForm; static Widget textAreaForm; Widget textArea; #ifdef FEAT_TOOLBAR static Widget toolBarFrame; static Widget toolBar; #endif #ifdef FEAT_GUI_TABLINE static Widget tabLine; static Widget tabLine_menu = 0; static int showing_tabline = 0; #endif #ifdef FEAT_MENU # if (XmVersion >= 1002) // remember the last set value for the tearoff item static int tearoff_val = (int)XmTEAR_OFF_ENABLED; # endif static Widget menuBar; #endif #ifdef FEAT_TOOLBAR static void reset_focus(void); #endif static void gui_motif_menu_colors(Widget id); static void gui_motif_scroll_colors(Widget id); #if (XmVersion >= 1002) # define STRING_TAG XmFONTLIST_DEFAULT_TAG #else # define STRING_TAG XmSTRING_DEFAULT_CHARSET #endif /* * Call-back routines. */ static void scroll_cb(Widget w UNUSED, XtPointer client_data, XtPointer call_data) { scrollbar_T *sb; long value; int dragging; sb = gui_find_scrollbar((long)client_data); value = ((XmScrollBarCallbackStruct *)call_data)->value; dragging = (((XmScrollBarCallbackStruct *)call_data)->reason == (int)XmCR_DRAG); gui_drag_scrollbar(sb, value, dragging); } #ifdef FEAT_GUI_TABLINE static void tabline_cb( Widget w UNUSED, XtPointer client_data UNUSED, XtPointer call_data) { XmNotebookCallbackStruct *nptr; nptr = (XmNotebookCallbackStruct *)call_data; if (nptr->reason != (int)XmCR_NONE) send_tabline_event(nptr->page_number); } static void tabline_button_cb( Widget w, XtPointer client_data UNUSED, XtPointer call_data UNUSED) { XtPointer cmd, tab_idx; XtVaGetValues(w, XmNuserData, &cmd, NULL); XtVaGetValues(tabLine_menu, XmNuserData, &tab_idx, NULL); send_tabline_menu_event((int)(long)tab_idx, (int)(long)cmd); } /* * Tabline single mouse click timeout handler */ static void motif_tabline_timer_cb ( XtPointer timed_out, XtIntervalId *interval_id UNUSED) { *((int *)timed_out) = TRUE; } /* * check if the tabline tab scroller is clicked */ static int tabline_scroller_clicked( char *scroller_name, XButtonPressedEvent *event) { Widget tab_scroll_w; Position pos_x, pos_y; Dimension width, height; tab_scroll_w = XtNameToWidget(tabLine, scroller_name); if (tab_scroll_w != (Widget)0) { XtVaGetValues(tab_scroll_w, XmNx, &pos_x, XmNy, &pos_y, XmNwidth, &width, XmNheight, &height, NULL); if (pos_x >= 0) { // Tab scroller (next) is visible if (event->x >= pos_x && event->x <= pos_x + width && event->y >= pos_y && event->y <= pos_y + height) // Clicked on the scroller return TRUE; } } return FALSE; } static void tabline_menu_cb( Widget w, XtPointer closure UNUSED, XEvent *e, Boolean *continue_dispatch UNUSED) { Widget tab_w; XButtonPressedEvent *event; int tab_idx = 0; WidgetList children; Cardinal numChildren; static XtIntervalId timer = (XtIntervalId)0; static int timed_out = TRUE; event = (XButtonPressedEvent *)e; if (event->button == Button1) { if (tabline_scroller_clicked("MajorTabScrollerNext", event) || tabline_scroller_clicked("MajorTabScrollerPrevious", event)) return; if (!timed_out) { XtRemoveTimeOut(timer); timed_out = TRUE; /* * Double click on the tabline gutter, add a new tab */ send_tabline_menu_event(0, TABLINE_MENU_NEW); } else { /* * Single click on the tabline gutter, start a timer to check * for double clicks */ timer = XtAppAddTimeOut(app_context, (long_u)p_mouset, motif_tabline_timer_cb, &timed_out); timed_out = FALSE; } return; } if (event->button == Button2) { // Middle mouse click on tabpage label closes that tab. XtVaGetValues(tabLine_menu, XmNuserData, &tab_idx, NULL); send_tabline_menu_event(tab_idx, (int)TABLINE_MENU_CLOSE); return; } if (event->button != Button3) return; // When ignoring events don't show the menu. if (hold_gui_events || cmdwin_type != 0) return; if (event->subwindow != None) { tab_w = XtWindowToWidget(XtDisplay(w), event->subwindow); // LINTED: avoid warning: dubious operation on enum if (tab_w != (Widget)0 && XmIsPushButton(tab_w)) XtVaGetValues(tab_w, XmNpageNumber, &tab_idx, NULL); } XtVaSetValues(tabLine_menu, XmNuserData, (XtPointer)(long)tab_idx, NULL); XtVaGetValues(tabLine_menu, XmNchildren, &children, XmNnumChildren, &numChildren, NULL); XtManageChildren(children, numChildren); XmMenuPosition(tabLine_menu, (XButtonPressedEvent *)e) ; XtManageChild(tabLine_menu); } static void tabline_balloon_cb(BalloonEval *beval, int state UNUSED) { int nr; tabpage_T *tp; if (beval->target == (Widget)0) return; XtVaGetValues(beval->target, XmNpageNumber, &nr, NULL); tp = find_tabpage(nr); if (tp == NULL) return; get_tabline_label(tp, TRUE); gui_mch_post_balloon(beval, NameBuff); } #endif /* * End of call-back routines */ /* * Implement three dimensional shading of insensitive labels. * By Marcin Dalecki. */ #include <Xm/XmP.h> #include <Xm/LabelP.h> static XtExposeProc old_label_expose = NULL; static void label_expose(Widget _w, XEvent *_event, Region _region) { GC insensitiveGC; XmLabelWidget lw = (XmLabelWidget)_w; unsigned char label_type = (int)XmSTRING; XtVaGetValues(_w, XmNlabelType, &label_type, (XtPointer)0); if (XtIsSensitive(_w) || label_type != (int)XmSTRING) (*old_label_expose)(_w, _event, _region); else { XGCValues values; XtGCMask mask; XtGCMask dynamic; XFontStruct *fs; _XmFontListGetDefaultFont(lw->label.font, &fs); // FIXME: we should be doing the whole drawing ourself here. insensitiveGC = lw->label.insensitive_GC; mask = GCForeground | GCBackground | GCGraphicsExposures; dynamic = GCClipMask | GCClipXOrigin | GCClipYOrigin; values.graphics_exposures = False; if (fs != 0) { mask |= GCFont; values.font = fs->fid; } if (lw->primitive.top_shadow_pixmap != None && lw->primitive.top_shadow_pixmap != XmUNSPECIFIED_PIXMAP) { mask |= GCFillStyle | GCTile; values.fill_style = FillTiled; values.tile = lw->primitive.top_shadow_pixmap; } lw->label.TextRect.x += 1; lw->label.TextRect.y += 1; if (lw->label._acc_text != 0) { lw->label.acc_TextRect.x += 1; lw->label.acc_TextRect.y += 1; } values.foreground = lw->primitive.top_shadow_color; values.background = lw->core.background_pixel; lw->label.insensitive_GC = XtAllocateGC((Widget)lw, 0, mask, &values, dynamic, (XtGCMask)0); (*old_label_expose)(_w, _event, _region); XtReleaseGC(_w, lw->label.insensitive_GC); lw->label.TextRect.x -= 1; lw->label.TextRect.y -= 1; if (lw->label._acc_text != 0) { lw->label.acc_TextRect.x -= 1; lw->label.acc_TextRect.y -= 1; } values.foreground = lw->primitive.bottom_shadow_color; values.background = lw->core.background_pixel; lw->label.insensitive_GC = XtAllocateGC((Widget) lw, 0, mask, &values, dynamic, (XtGCMask)0); (*old_label_expose)(_w, _event, _region); XtReleaseGC(_w, lw->label.insensitive_GC); lw->label.insensitive_GC = insensitiveGC; } } /* * Create all the motif widgets necessary. */ void gui_x11_create_widgets(void) { #ifdef FEAT_GUI_TABLINE Widget button, scroller; Arg args[10]; int n; XmString xms; #endif /* * Install the 3D shade effect drawing routines. */ if (old_label_expose == NULL) { old_label_expose = xmLabelWidgetClass->core_class.expose; xmLabelWidgetClass->core_class.expose = label_expose; } /* * Start out by adding the configured border width into the border offset */ gui.border_offset = gui.border_width; /* * Install the tearOffModel resource converter. */ #if (XmVersion >= 1002) XmRepTypeInstallTearOffModelConverter(); #endif // Make sure the "Quit" menu entry of the window manager is ignored XtVaSetValues(vimShell, XmNdeleteResponse, XmDO_NOTHING, NULL); vimForm = XtVaCreateManagedWidget("vimForm", xmFormWidgetClass, vimShell, XmNborderWidth, 0, XmNhighlightThickness, 0, XmNshadowThickness, 0, XmNmarginWidth, 0, XmNmarginHeight, 0, XmNresizePolicy, XmRESIZE_ANY, NULL); gui_motif_menu_colors(vimForm); #ifdef FEAT_MENU { Arg al[7]; // Make sure there is enough room for arguments! int ac = 0; # if (XmVersion >= 1002) XtSetArg(al[ac], XmNtearOffModel, tearoff_val); ac++; # endif XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; # ifndef FEAT_TOOLBAR // Always stick to right hand side. XtSetArg(al[ac], XmNrightOffset, 0); ac++; # endif XtSetArg(al[ac], XmNmarginHeight, 0); ac++; menuBar = XmCreateMenuBar(vimForm, "menuBar", al, ac); XtManageChild(menuBar); gui_motif_menu_colors(menuBar); } #endif #ifdef FEAT_TOOLBAR /* * Create an empty ToolBar. We should get buttons defined from menu.vim. */ toolBarFrame = XtVaCreateWidget("toolBarFrame", xmFrameWidgetClass, vimForm, XmNshadowThickness, 0, XmNmarginHeight, 0, XmNmarginWidth, 0, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); gui_motif_menu_colors(toolBarFrame); toolBar = XtVaCreateManagedWidget("toolBar", xmRowColumnWidgetClass, toolBarFrame, XmNchildType, XmFRAME_WORKAREA_CHILD, XmNrowColumnType, XmWORK_AREA, XmNorientation, XmHORIZONTAL, XmNtraversalOn, False, XmNisHomogeneous, False, XmNpacking, XmPACK_TIGHT, XmNspacing, 0, XmNshadowThickness, 0, XmNhighlightThickness, 0, XmNmarginHeight, 0, XmNmarginWidth, 0, XmNadjustLast, True, NULL); gui_motif_menu_colors(toolBar); #endif #ifdef FEAT_GUI_TABLINE // Create the Vim GUI tabline n = 0; XtSetArg(args[n], XmNbindingType, XmNONE); n++; XtSetArg(args[n], XmNorientation, XmVERTICAL); n++; XtSetArg(args[n], XmNbackPageSize, XmNONE); n++; XtSetArg(args[n], XmNbackPageNumber, 0); n++; XtSetArg(args[n], XmNbackPagePlacement, XmTOP_RIGHT); n++; XtSetArg(args[n], XmNmajorTabSpacing, 0); n++; XtSetArg(args[n], XmNshadowThickness, 0); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; tabLine = XmCreateNotebook(vimForm, "Vim tabline", args, n); XtAddCallback(tabLine, XmNpageChangedCallback, (XtCallbackProc)tabline_cb, NULL); XtAddEventHandler(tabLine, ButtonPressMask, False, (XtEventHandler)tabline_menu_cb, NULL); gui.tabline_height = TABLINE_HEIGHT; /* * Set the size of the minor next/prev scrollers to zero, so * that they are not displayed. Due to a bug in OpenMotif 2.3, * even if these children widget are unmanaged, they are again * managed by the Notebook widget and the notebook widget geometry * is adjusted to account for the minor scroller widgets. */ scroller = XtNameToWidget(tabLine, "MinorTabScrollerNext"); XtVaSetValues(scroller, XmNwidth, 0, XmNresizable, False, XmNtraversalOn, False, NULL); scroller = XtNameToWidget(tabLine, "MinorTabScrollerPrevious"); XtVaSetValues(scroller, XmNwidth, 0, XmNresizable, False, XmNtraversalOn, False, NULL); // Create the tabline popup menu tabLine_menu = XmCreatePopupMenu(tabLine, "tabline popup", NULL, 0); // Add the buttons to the tabline popup menu n = 0; XtSetArg(args[n], XmNuserData, (XtPointer)TABLINE_MENU_CLOSE); n++; xms = XmStringCreate((char *)"Close tab", STRING_TAG); XtSetArg(args[n], XmNlabelString, xms); n++; button = XmCreatePushButton(tabLine_menu, "Close", args, n); XtAddCallback(button, XmNactivateCallback, (XtCallbackProc)tabline_button_cb, NULL); XmStringFree(xms); n = 0; XtSetArg(args[n], XmNuserData, (XtPointer)TABLINE_MENU_NEW); n++; xms = XmStringCreate((char *)"New Tab", STRING_TAG); XtSetArg(args[n], XmNlabelString, xms); n++; button = XmCreatePushButton(tabLine_menu, "New Tab", args, n); XtAddCallback(button, XmNactivateCallback, (XtCallbackProc)tabline_button_cb, NULL); XmStringFree(xms); n = 0; XtSetArg(args[n], XmNuserData, (XtPointer)TABLINE_MENU_OPEN); n++; xms = XmStringCreate((char *)"Open tab...", STRING_TAG); XtSetArg(args[n], XmNlabelString, xms); n++; button = XmCreatePushButton(tabLine_menu, "Open tab...", args, n); XtAddCallback(button, XmNactivateCallback, (XtCallbackProc)tabline_button_cb, NULL); XmStringFree(xms); #endif textAreaForm = XtVaCreateManagedWidget("textAreaForm", xmFormWidgetClass, vimForm, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNmarginWidth, 0, XmNmarginHeight, 0, XmNresizePolicy, XmRESIZE_ANY, NULL); gui_motif_scroll_colors(textAreaForm); textArea = XtVaCreateManagedWidget("textArea", xmDrawingAreaWidgetClass, textAreaForm, XmNforeground, gui.norm_pixel, XmNbackground, gui.back_pixel, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, /* * These take some control away from the user, but avoids making them * add resources to get a decent looking setup. */ XmNborderWidth, 0, XmNhighlightThickness, 0, XmNshadowThickness, 0, NULL); /* * Install the callbacks. */ gui_x11_callbacks(textArea, vimForm); // Pretend we don't have input focus, we will get an event if we do. gui.in_focus = FALSE; } /* * 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 } void gui_mch_set_text_area_pos( int x UNUSED, int y UNUSED, int w UNUSED, int h UNUSED) { #ifdef FEAT_TOOLBAR // Give keyboard focus to the textArea instead of the toolbar. reset_focus(); #endif } void gui_x11_set_back_color(void) { if (textArea != NULL) #if (XmVersion >= 1002) XmChangeColor(textArea, gui.back_pixel); #else XtVaSetValues(textArea, XmNbackground, gui.back_pixel, NULL); #endif } /* * Manage dialog centered on pointer. */ void manage_centered(Widget dialog_child) { Widget shell = XtParent(dialog_child); Window root, child; unsigned int mask; unsigned int width, height, border_width, depth; int x, y, win_x, win_y, maxX, maxY; Boolean mappedWhenManaged; // Temporarily set value of XmNmappedWhenManaged // to stop the dialog from popping up right away XtVaGetValues(shell, XmNmappedWhenManaged, &mappedWhenManaged, NULL); XtVaSetValues(shell, XmNmappedWhenManaged, False, NULL); XtManageChild(dialog_child); // Get the pointer position (x, y) XQueryPointer(XtDisplay(shell), XtWindow(shell), &root, &child, &x, &y, &win_x, &win_y, &mask); // Translate the pointer position (x, y) into a position for the new // window that will place the pointer at its center XGetGeometry(XtDisplay(shell), XtWindow(shell), &root, &win_x, &win_y, &width, &height, &border_width, &depth); width += 2 * border_width; height += 2 * border_width; x -= width / 2; y -= height / 2; // Ensure that the dialog remains on screen maxX = XtScreen(shell)->width - width; maxY = XtScreen(shell)->height - height; if (x < 0) x = 0; if (x > maxX) x = maxX; if (y < 0) y = 0; if (y > maxY) y = maxY; // Set desired window position in the DialogShell XtVaSetValues(shell, XmNx, x, XmNy, y, NULL); // Map the widget XtMapWidget(shell); // Restore the value of XmNmappedWhenManaged XtVaSetValues(shell, XmNmappedWhenManaged, mappedWhenManaged, NULL); } #if defined(FEAT_MENU) || defined(FEAT_GUI_DIALOG) || defined(PROTO) /* * Encapsulate the way an XmFontList is created. */ XmFontList gui_motif_create_fontlist(XFontStruct *font) { XmFontList font_list; # if (XmVersion <= 1001) // Motif 1.1 method font_list = XmFontListCreate(font, STRING_TAG); # else // Motif 1.2 method XmFontListEntry font_list_entry; font_list_entry = XmFontListEntryCreate(STRING_TAG, XmFONT_IS_FONT, (XtPointer)font); font_list = XmFontListAppendEntry(NULL, font_list_entry); XmFontListEntryFree(&font_list_entry); # endif return font_list; } # if ((XmVersion > 1001) && defined(FEAT_XFONTSET)) || defined(PROTO) XmFontList gui_motif_fontset2fontlist(XFontSet *fontset) { XmFontList font_list; // Motif 1.2 method XmFontListEntry font_list_entry; font_list_entry = XmFontListEntryCreate(STRING_TAG, XmFONT_IS_FONTSET, (XtPointer)*fontset); font_list = XmFontListAppendEntry(NULL, font_list_entry); XmFontListEntryFree(&font_list_entry); return font_list; } # endif #endif #if defined(FEAT_MENU) || defined(PROTO) /* * Menu stuff. */ static void gui_motif_add_actext(vimmenu_T *menu); #if (XmVersion >= 1002) static void toggle_tearoff(Widget wid); static void gui_mch_recurse_tearoffs(vimmenu_T *menu); #endif static void submenu_change(vimmenu_T *mp, int colors); static void do_set_mnemonics(int enable); static int menu_enabled = TRUE; void gui_mch_enable_menu(int flag) { if (flag) { XtManageChild(menuBar); #ifdef FEAT_TOOLBAR if (XtIsManaged(XtParent(toolBar))) { // toolBar is attached to top form XtVaSetValues(XtParent(toolBar), XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); #ifdef FEAT_GUI_TABLINE if (showing_tabline) { XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, XtParent(toolBar), NULL); XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabLine, NULL); } else #endif XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, XtParent(toolBar), NULL); } else #endif { #ifdef FEAT_GUI_TABLINE if (showing_tabline) { XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabLine, NULL); } else #endif XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); } } else { XtUnmanageChild(menuBar); #ifdef FEAT_TOOLBAR if (XtIsManaged(XtParent(toolBar))) { XtVaSetValues(XtParent(toolBar), XmNtopAttachment, XmATTACH_FORM, NULL); #ifdef FEAT_GUI_TABLINE if (showing_tabline) { XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, XtParent(toolBar), NULL); XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabLine, NULL); } else #endif XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, XtParent(toolBar), NULL); } else #endif { #ifdef FEAT_GUI_TABLINE if (showing_tabline) { XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_FORM, NULL); XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabLine, NULL); } else #endif XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_FORM, NULL); } } } /* * Enable or disable mnemonics for the toplevel menus. */ void gui_motif_set_mnemonics(int enable) { /* * Don't enable menu mnemonics when the menu bar is disabled, LessTif * crashes when using a mnemonic then. */ if (!menu_enabled) enable = FALSE; do_set_mnemonics(enable); } static void do_set_mnemonics(int enable) { vimmenu_T *menu; FOR_ALL_MENUS(menu) if (menu->id != (Widget)0) XtVaSetValues(menu->id, XmNmnemonic, enable ? menu->mnemonic : NUL, NULL); } void gui_mch_add_menu(vimmenu_T *menu, int idx) { XmString label; Widget shell; vimmenu_T *parent = menu->parent; #ifdef MOTIF_POPUP if (menu_is_popup(menu->name)) { Arg arg[2]; int n = 0; // Only create the popup menu when it's actually used, otherwise there // is a delay when using the right mouse button. # if (XmVersion <= 1002) if (mouse_model_popup()) # endif { if (gui.menu_bg_pixel != INVALCOLOR) { XtSetArg(arg[0], XmNbackground, gui.menu_bg_pixel); n++; } if (gui.menu_fg_pixel != INVALCOLOR) { XtSetArg(arg[1], XmNforeground, gui.menu_fg_pixel); n++; } menu->submenu_id = XmCreatePopupMenu(textArea, "contextMenu", arg, n); menu->id = (Widget)0; } return; } #endif if (!menu_is_menubar(menu->name) || (parent != NULL && parent->submenu_id == (Widget)0)) return; label = XmStringCreate((char *)menu->dname, STRING_TAG); if (label == NULL) return; menu->id = XtVaCreateWidget("subMenu", xmCascadeButtonWidgetClass, (parent == NULL) ? menuBar : parent->submenu_id, XmNlabelString, label, XmNmnemonic, p_wak[0] == 'n' ? NUL : menu->mnemonic, #if (XmVersion >= 1002) // submenu: count the tearoff item (needed for LessTif) XmNpositionIndex, idx + (parent != NULL && tearoff_val == (int)XmTEAR_OFF_ENABLED ? 1 : 0), #endif NULL); XmStringFree(label); if (menu->id == (Widget)0) // failed return; // The "Help" menu is a special case, and should be placed at the far // right hand side of the menu-bar. It's recognized by its high priority. if (parent == NULL && menu->priority >= 9999) XtVaSetValues(menuBar, XmNmenuHelpWidget, menu->id, NULL); gui_motif_menu_colors(menu->id); gui_motif_menu_fontlist(menu->id); // add accelerator text gui_motif_add_actext(menu); shell = XtVaCreateWidget("subMenuShell", xmMenuShellWidgetClass, menu->id, XmNwidth, 1, XmNheight, 1, NULL); gui_motif_menu_colors(shell); menu->submenu_id = XtVaCreateWidget("rowColumnMenu", xmRowColumnWidgetClass, shell, XmNrowColumnType, XmMENU_PULLDOWN, NULL); gui_motif_menu_colors(menu->submenu_id); if (menu->submenu_id == (Widget)0) // failed return; #if (XmVersion >= 1002) // Set the colors for the tear off widget toggle_tearoff(menu->submenu_id); #endif XtVaSetValues(menu->id, XmNsubMenuId, menu->submenu_id, NULL); // When we add a top-level item to the menu bar, we can figure out how // high the menu bar should be. if (parent == NULL) gui_mch_compute_menu_height(menu->id); } /* * Add mnemonic and accelerator text to a menu button. */ static void gui_motif_add_actext(vimmenu_T *menu) { XmString label; // Add accelerator text, if there is one if (menu->actext == NULL || menu->id == (Widget)0) return; label = XmStringCreate((char *)menu->actext, STRING_TAG); if (label == NULL) return; XtVaSetValues(menu->id, XmNacceleratorText, label, NULL); XmStringFree(label); } void gui_mch_toggle_tearoffs(int enable) { #if (XmVersion >= 1002) if (enable) tearoff_val = (int)XmTEAR_OFF_ENABLED; else tearoff_val = (int)XmTEAR_OFF_DISABLED; toggle_tearoff(menuBar); gui_mch_recurse_tearoffs(root_menu); #endif } #if (XmVersion >= 1002) /* * Set the tearoff for one menu widget on or off, and set the color of the * tearoff widget. */ static void toggle_tearoff(Widget wid) { Widget w; XtVaSetValues(wid, XmNtearOffModel, tearoff_val, NULL); if (tearoff_val == (int)XmTEAR_OFF_ENABLED && (w = XmGetTearOffControl(wid)) != (Widget)0) gui_motif_menu_colors(w); } static void gui_mch_recurse_tearoffs(vimmenu_T *menu) { while (menu != NULL) { if (!menu_is_popup(menu->name)) { if (menu->submenu_id != (Widget)0) toggle_tearoff(menu->submenu_id); gui_mch_recurse_tearoffs(menu->children); } menu = menu->next; } } #endif int gui_mch_text_area_extra_height(void) { Dimension shadowHeight; XtVaGetValues(textAreaForm, XmNshadowThickness, &shadowHeight, NULL); return shadowHeight; } /* * Compute the height of the menu bar. * We need to check all the items for their position and height, for the case * there are several rows, and/or some characters extend higher or lower. */ void gui_mch_compute_menu_height( Widget id) // can be NULL when deleting menu { Dimension y, maxy; Dimension margin, shadow; vimmenu_T *mp; static Dimension height = 21; // normal height of a menu item /* * Get the height of the new item, before managing it, because it will * still reflect the font size. After managing it depends on the menu * height, which is what we just wanted to get!. */ if (id != (Widget)0) XtVaGetValues(id, XmNheight, &height, NULL); // Find any menu Widget, to be able to call XtManageChild() else FOR_ALL_MENUS(mp) if (mp->id != (Widget)0 && menu_is_menubar(mp->name)) { id = mp->id; break; } /* * Now manage the menu item, to make them all be positioned (makes an * extra row when needed, removes it when not needed). */ if (id != (Widget)0) XtManageChild(id); /* * Now find the menu item that is the furthest down, and get its position. */ maxy = 0; FOR_ALL_MENUS(mp) { if (mp->id != (Widget)0 && menu_is_menubar(mp->name)) { XtVaGetValues(mp->id, XmNy, &y, NULL); if (y > maxy) maxy = y; } } XtVaGetValues(menuBar, XmNmarginHeight, &margin, XmNshadowThickness, &shadow, NULL); /* * This computation is the result of trial-and-error: * maxy = The maximum position of an item; required for when there are * two or more rows * height = height of an item, before managing it; Hopefully this will * change with the font height. Includes shadow-border. * shadow = shadow-border; must be subtracted from the height. * margin = margin around the menu buttons; Must be added. * Add 4 for the underlining of shortcut keys. */ gui.menu_height = maxy + height - 2 * shadow + 2 * margin + 4; // Somehow the menu bar doesn't resize automatically. Set it here, // even though this is a catch 22. Don't do this when starting up, // somehow the menu gets very high then. if (gui.shell_created) XtVaSetValues(menuBar, XmNheight, gui.menu_height, NULL); } #ifdef FEAT_TOOLBAR /* * Icons used by the toolbar code. */ #include "gui_x11_pm.h" static char **get_toolbar_pixmap(vimmenu_T *menu, char **fname); /* * Read an Xpm file. Return OK or FAIL. */ static int check_xpm(char_u *path) { XpmAttributes attrs; int status; Pixmap mask; Pixmap map; attrs.valuemask = 0; // Create the "sensitive" pixmap status = XpmReadFileToPixmap(gui.dpy, RootWindow(gui.dpy, DefaultScreen(gui.dpy)), (char *)path, &map, &mask, &attrs); XpmFreeAttributes(&attrs); if (status == XpmSuccess) return OK; return FAIL; } /* * Allocated a pixmap for toolbar menu "menu". * When it's to be read from a file, "fname" is set to the file name * (in allocated memory). * Return a blank pixmap if it fails. */ static char ** get_toolbar_pixmap(vimmenu_T *menu, char **fname) { char_u buf[MAXPATHL]; // buffer storing expanded pathname char **xpm = NULL; // xpm array int res; *fname = NULL; buf[0] = NUL; // start with NULL path if (menu->iconfile != NULL) { // Use the "icon=" argument. gui_find_iconfile(menu->iconfile, buf, "xpm"); res = check_xpm(buf); // If it failed, try using the menu name. if (res == FAIL && gui_find_bitmap(menu->name, buf, "xpm") == OK) res = check_xpm(buf); if (res == OK) { *fname = (char *)vim_strsave(buf); return tb_blank_xpm; } } 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; } return xpm; } /* * Add arguments for the toolbar pixmap to a menu item. */ static int add_pixmap_args(vimmenu_T *menu, Arg *args, int n) { vim_free(menu->xpm_fname); menu->xpm = get_toolbar_pixmap(menu, &menu->xpm_fname); if (menu->xpm == NULL) { XtSetArg(args[n], XmNlabelType, XmSTRING); n++; } else { if (menu->xpm_fname != NULL) { XtSetArg(args[n], XmNpixmapFile, menu->xpm_fname); n++; } XtSetArg(args[n], XmNpixmapData, menu->xpm); n++; XtSetArg(args[n], XmNlabelLocation, XmBOTTOM); n++; } return n; } #endif // FEAT_TOOLBAR void gui_mch_add_menu_item(vimmenu_T *menu, int idx) { XmString label; vimmenu_T *parent = menu->parent; # if (XmVersion <= 1002) // Don't add Popup menu items when the popup menu isn't used. if (menu_is_child_of_popup(menu) && !mouse_model_popup()) return; # endif # ifdef FEAT_TOOLBAR if (menu_is_toolbar(parent->name)) { WidgetClass type; XmString xms = NULL; // fallback label if pixmap not found int n; Arg args[18]; n = 0; if (menu_is_separator(menu->name)) { char *cp; Dimension wid; /* * A separator has the format "-sep%d[:%d]-". The optional :%d is * a width specifier. If no width is specified then we choose one. */ cp = (char *)vim_strchr(menu->name, ':'); if (cp != NULL) wid = (Dimension)atoi(++cp); else wid = 4; type = xmSeparatorWidgetClass; XtSetArg(args[n], XmNwidth, wid); n++; XtSetArg(args[n], XmNminWidth, wid); n++; XtSetArg(args[n], XmNorientation, XmVERTICAL); n++; XtSetArg(args[n], XmNseparatorType, XmSHADOW_ETCHED_IN); n++; } else { // Without shadows one can't sense whatever the button has been // pressed or not! However we want to save a bit of space... // Need the highlightThickness to see the focus. XtSetArg(args[n], XmNhighlightThickness, 1); n++; XtSetArg(args[n], XmNhighlightOnEnter, True); n++; XtSetArg(args[n], XmNmarginWidth, 0); n++; XtSetArg(args[n], XmNmarginHeight, 0); n++; XtSetArg(args[n], XmNtraversalOn, False); n++; // Set the label here, so that we can switch between icons/text // by changing the XmNlabelType resource. xms = XmStringCreate((char *)menu->dname, STRING_TAG); XtSetArg(args[n], XmNlabelString, xms); n++; n = add_pixmap_args(menu, args, n); type = xmEnhancedButtonWidgetClass; } XtSetArg(args[n], XmNpositionIndex, idx); n++; if (menu->id == NULL) { menu->id = XtCreateManagedWidget((char *)menu->dname, type, toolBar, args, n); if (menu->id != NULL && type == xmEnhancedButtonWidgetClass) { XtAddCallback(menu->id, XmNactivateCallback, gui_x11_menu_cb, menu); } } else XtSetValues(menu->id, args, n); if (xms != NULL) XmStringFree(xms); # ifdef FEAT_BEVAL_GUI gui_mch_menu_set_tip(menu); # endif menu->parent = parent; menu->submenu_id = NULL; // When adding first item to toolbar it might have to be enabled . if (!XtIsManaged(XtParent(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 // No parent, must be a non-menubar menu if (parent->submenu_id == (Widget)0) return; menu->submenu_id = (Widget)0; // Add menu separator if (menu_is_separator(menu->name)) { menu->id = XtVaCreateWidget("subMenu", xmSeparatorGadgetClass, parent->submenu_id, #if (XmVersion >= 1002) // count the tearoff item (needed for LessTif) XmNpositionIndex, idx + (tearoff_val == (int)XmTEAR_OFF_ENABLED ? 1 : 0), #endif NULL); gui_motif_menu_colors(menu->id); return; } label = XmStringCreate((char *)menu->dname, STRING_TAG); if (label == NULL) return; menu->id = XtVaCreateWidget("subMenu", xmPushButtonWidgetClass, parent->submenu_id, XmNlabelString, label, XmNmnemonic, menu->mnemonic, #if (XmVersion >= 1002) // count the tearoff item (needed for LessTif) XmNpositionIndex, idx + (tearoff_val == (int)XmTEAR_OFF_ENABLED ? 1 : 0), #endif NULL); gui_motif_menu_colors(menu->id); gui_motif_menu_fontlist(menu->id); XmStringFree(label); if (menu->id != (Widget)0) { XtAddCallback(menu->id, XmNactivateCallback, gui_x11_menu_cb, (XtPointer)menu); // add accelerator text gui_motif_add_actext(menu); } } #if (XmVersion <= 1002) || defined(PROTO) /* * This function will destroy/create the popup menus dynamically, * according to the value of 'mousemodel'. * This will fix the "right mouse button freeze" that occurs when * there exists a popup menu but it isn't managed. */ void gui_motif_update_mousemodel(vimmenu_T *menu) { int idx = 0; // When GUI hasn't started the menus have not been created. if (!gui.in_use) return; while (menu) { if (menu->children != NULL) { if (menu_is_popup(menu->name)) { if (mouse_model_popup()) { // Popup menu will be used. Create the popup menus. gui_mch_add_menu(menu, idx); gui_motif_update_mousemodel(menu->children); } else { // Popup menu will not be used. Destroy the popup menus. gui_motif_update_mousemodel(menu->children); gui_mch_destroy_menu(menu); } } } else if (menu_is_child_of_popup(menu)) { if (mouse_model_popup()) gui_mch_add_menu_item(menu, idx); else gui_mch_destroy_menu(menu); } menu = menu->next; ++idx; } } #endif void gui_mch_new_menu_colors(void) { if (menuBar == (Widget)0) return; gui_motif_menu_colors(menuBar); #ifdef FEAT_TOOLBAR gui_motif_menu_colors(toolBarFrame); gui_motif_menu_colors(toolBar); #endif submenu_change(root_menu, TRUE); } void gui_mch_new_menu_font(void) { if (menuBar == (Widget)0) return; submenu_change(root_menu, FALSE); { Dimension height; Position w, h; XtVaGetValues(menuBar, XmNheight, &height, NULL); gui.menu_height = height; 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 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) submenu_change(menu, FALSE); # endif } void gui_mch_new_tooltip_colors(void) { # ifdef FEAT_TOOLBAR vimmenu_T *toolbar; if (toolBar == (Widget)0) return; toolbar = gui_find_menu((char_u *)"ToolBar"); if (toolbar != NULL) submenu_change(toolbar, TRUE); # endif } #endif static void 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_motif_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->xpm != NULL) { int n = 0; Arg args[18]; n = add_pixmap_args(mp, args, n); XtSetValues(mp->id, args, n); } # ifdef FEAT_BEVAL_GUI // If we have a tooltip, then we need to change its font if (mp->tip != NULL) { Arg args[2]; args[0].name = XmNbackground; args[0].value = gui.tooltip_bg_pixel; args[1].name = XmNforeground; args[1].value = gui.tooltip_fg_pixel; XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args)); } # endif #endif } else { gui_motif_menu_fontlist(mp->id); #ifdef FEAT_BEVAL_GUI // If we have a tooltip, then we need to change its font if (mp->tip != NULL) { Arg args[1]; args[0].name = XmNfontList; args[0].value = (XtArgVal)gui_motif_fontset2fontlist( &gui.tooltip_fontset); XtSetValues(mp->tip->balloonLabel, &args[0], XtNumber(args)); } #endif } } if (mp->children != NULL) { #if (XmVersion >= 1002) // Set the colors/font for the tear off widget if (mp->submenu_id != (Widget)0) { if (colors) gui_motif_menu_colors(mp->submenu_id); else gui_motif_menu_fontlist(mp->submenu_id); toggle_tearoff(mp->submenu_id); } #endif // Set the colors for the children submenu_change(mp->children, colors); } } } /* * Destroy the machine specific menu widget. */ void gui_mch_destroy_menu(vimmenu_T *menu) { // Please be sure to destroy the parent widget first (i.e. menu->id). // On the other hand, problems have been reported that the submenu must be // deleted first... if (menu->submenu_id != (Widget)0) { XtDestroyWidget(menu->submenu_id); menu->submenu_id = (Widget)0; } if (menu->id == (Widget)0) return; Widget parent; parent = XtParent(menu->id); #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 XtDestroyWidget(menu->id); menu->id = (Widget)0; if (parent == menuBar) gui_mch_compute_menu_height((Widget)0); #ifdef FEAT_TOOLBAR else if (parent == toolBar) { Cardinal num_children; // When removing last toolbar item, don't display the toolbar. XtVaGetValues(toolBar, XmNnumChildren, &num_children, NULL); if (num_children == 0) gui_mch_show_toolbar(FALSE); else gui.toolbar_height = gui_mch_compute_toolbar_height(); } #endif } void gui_mch_show_popupmenu(vimmenu_T *menu UNUSED) { #ifdef MOTIF_POPUP XmMenuPosition(menu->submenu_id, gui_x11_get_last_mouse_event()); XtManageChild(menu->submenu_id); #endif } #endif // FEAT_MENU /* * Set the menu and scrollbar colors to their default values. */ void gui_mch_def_colors(void) { if (!gui.in_use) return; 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) { if (sb->id != (Widget)0) XtVaSetValues(sb->id, XmNvalue, val, XmNsliderSize, size, XmNpageIncrement, (size > 2 ? size - 2 : 1), XmNmaximum, max + 1, // Motif has max one past the end NULL); } void gui_mch_set_scrollbar_pos( scrollbar_T *sb, int x, int y, int w, int h) { if (sb->id == (Widget)0) return; if (sb->type == SBAR_LEFT || sb->type == SBAR_RIGHT) { if (y == 0) h -= gui.border_offset; else y -= gui.border_offset; XtVaSetValues(sb->id, XmNtopOffset, y, XmNbottomOffset, -y - h, XmNwidth, w, NULL); } else XtVaSetValues(sb->id, XmNtopOffset, y, XmNleftOffset, x, XmNrightOffset, gui.which_scrollbars[SBAR_RIGHT] ? gui.scrollbar_width : 0, XmNheight, h, NULL); XtManageChild(sb->id); } int gui_mch_get_scrollbar_xpadding(void) { int xpad; Dimension tw, ww; Position tx; XtVaGetValues(textArea, XtNwidth, &tw, XtNx, &tx, NULL); XtVaGetValues(vimShell, XtNwidth, &ww, NULL); xpad = ww - tw - tx - gui.scrollbar_width; return (xpad < 0) ? 0 : xpad; } int gui_mch_get_scrollbar_ypadding(void) { int ypad; Dimension th, wh; Position ty; XtVaGetValues(textArea, XtNheight, &th, XtNy, &ty, NULL); XtVaGetValues(vimShell, XtNheight, &wh, NULL); ypad = wh - th - ty - gui.scrollbar_height; return (ypad < 0) ? 0 : ypad; } void gui_mch_enable_scrollbar(scrollbar_T *sb, int flag) { Arg args[16]; int n; if (sb->id == (Widget)0) return; n = 0; if (flag) { switch (sb->type) { case SBAR_LEFT: XtSetArg(args[n], XmNleftOffset, gui.scrollbar_width); n++; break; case SBAR_RIGHT: XtSetArg(args[n], XmNrightOffset, gui.scrollbar_width); n++; break; case SBAR_BOTTOM: XtSetArg(args[n], XmNbottomOffset, gui.scrollbar_height);n++; break; } XtSetValues(textArea, args, n); XtManageChild(sb->id); } else { if (!gui.which_scrollbars[sb->type]) { // The scrollbars of this type are all disabled, adjust the // textArea attachment offset. switch (sb->type) { case SBAR_LEFT: XtSetArg(args[n], XmNleftOffset, 0); n++; break; case SBAR_RIGHT: XtSetArg(args[n], XmNrightOffset, 0); n++; break; case SBAR_BOTTOM: XtSetArg(args[n], XmNbottomOffset, 0);n++; break; } XtSetValues(textArea, args, n); } XtUnmanageChild(sb->id); } } void gui_mch_create_scrollbar( scrollbar_T *sb, int orient) // SBAR_VERT or SBAR_HORIZ { Arg args[16]; int n = 0; XtSetArg(args[n], XmNminimum, 0); n++; XtSetArg(args[n], XmNorientation, (orient == SBAR_VERT) ? XmVERTICAL : XmHORIZONTAL); n++; switch (sb->type) { case SBAR_LEFT: XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_FORM); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNwidth, gui.scrollbar_width); n++; break; case SBAR_RIGHT: XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNwidth, gui.scrollbar_width); n++; break; case SBAR_BOTTOM: XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNheight, gui.scrollbar_height); n++; break; } sb->id = XtCreateWidget("scrollBar", xmScrollBarWidgetClass, textAreaForm, args, n); if (sb->id == (Widget)0) return; gui_mch_set_scrollbar_colors(sb); XtAddCallback(sb->id, XmNvalueChangedCallback, scroll_cb, (XtPointer)sb->ident); XtAddCallback(sb->id, XmNdragCallback, scroll_cb, (XtPointer)sb->ident); XtAddEventHandler(sb->id, KeyPressMask, FALSE, gui_x11_key_hit_cb, (XtPointer)0); } 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) { if (gui.scroll_bg_pixel != INVALCOLOR) { #if (XmVersion>=1002) // This should not only set the through color but also adjust // related colors, such as shadows. XmChangeColor(sb->id, gui.scroll_bg_pixel); #endif // Set the through color directly, in case XmChangeColor() decided // to change it. XtVaSetValues(sb->id, XmNtroughColor, gui.scroll_bg_pixel, NULL); } if (gui.scroll_fg_pixel != INVALCOLOR) XtVaSetValues(sb->id, XmNforeground, gui.scroll_fg_pixel, XmNbackground, gui.scroll_fg_pixel, NULL); } // This is needed for the rectangle below the vertical scrollbars. if (sb == &gui.bottom_sbar && textAreaForm != (Widget)0) gui_motif_scroll_colors(textAreaForm); } /* * Miscellaneous stuff: */ Window gui_x11_get_wid(void) { return(XtWindow(textArea)); } /* * Look for a widget in the widget tree w, with a mnemonic matching keycode. * When one is found, simulate a button press on that widget and give it the * keyboard focus. If the mnemonic is on a label, look in the userData field * of the label to see if it points to another widget, and give that the focus. */ static void do_mnemonic(Widget w, unsigned int keycode) { WidgetList children; int numChildren, i; Boolean isMenu; KeySym mnemonic = '\0'; char mneString[2]; Widget userData; unsigned char rowColType; if (XtIsComposite(w)) { if (XtClass(w) == xmRowColumnWidgetClass) { XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL); isMenu = (rowColType != (unsigned char)XmWORK_AREA); } else isMenu = False; if (!isMenu) { XtVaGetValues(w, XmNchildren, &children, XmNnumChildren, &numChildren, NULL); for (i = 0; i < numChildren; i++) do_mnemonic(children[i], keycode); } } else { XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL); if (mnemonic != '\0') { mneString[0] = mnemonic; mneString[1] = '\0'; if (XKeysymToKeycode(XtDisplay(XtParent(w)), XStringToKeysym(mneString)) == keycode) { if (XtClass(w) == xmLabelWidgetClass || XtClass(w) == xmLabelGadgetClass) { XtVaGetValues(w, XmNuserData, &userData, NULL); if (userData != NULL && XtIsWidget(userData)) XmProcessTraversal(userData, XmTRAVERSE_CURRENT); } else { XKeyPressedEvent keyEvent; XmProcessTraversal(w, XmTRAVERSE_CURRENT); CLEAR_FIELD(keyEvent); keyEvent.type = KeyPress; keyEvent.serial = 1; keyEvent.send_event = True; keyEvent.display = XtDisplay(w); keyEvent.window = XtWindow(w); XtCallActionProc(w, "Activate", (XEvent *) & keyEvent, NULL, 0); } } } } } /* * Callback routine for dialog mnemonic processing. */ static void mnemonic_event( Widget w, XtPointer call_data UNUSED, XKeyEvent *event, Boolean *b UNUSED) { do_mnemonic(w, event->keycode); } /* * Search the widget tree under w for widgets with mnemonics. When found, add * a passive grab to the dialog widget for the mnemonic character, thus * directing mnemonic events to the dialog widget. */ static void add_mnemonic_grabs(Widget dialog, Widget w) { char mneString[2]; WidgetList children; int numChildren, i; Boolean isMenu; KeySym mnemonic = '\0'; unsigned char rowColType; if (XtIsComposite(w)) { if (XtClass(w) == xmRowColumnWidgetClass) { XtVaGetValues(w, XmNrowColumnType, &rowColType, NULL); isMenu = (rowColType != (unsigned char)XmWORK_AREA); } else isMenu = False; if (!isMenu) { XtVaGetValues(w, XmNchildren, &children, XmNnumChildren, &numChildren, NULL); for (i = 0; i < numChildren; i++) add_mnemonic_grabs(dialog, children[i]); } } else { XtVaGetValues(w, XmNmnemonic, &mnemonic, NULL); if (mnemonic != '\0') { mneString[0] = mnemonic; mneString[1] = '\0'; XtGrabKey(dialog, XKeysymToKeycode(XtDisplay(dialog), XStringToKeysym(mneString)), Mod1Mask, True, GrabModeAsync, GrabModeAsync); } } } /* * Add a handler for mnemonics in a dialog. Motif itself only handles * mnemonics in menus. Mnemonics added or changed after this call will be * ignored. * * To add a mnemonic to a text field or list, set the XmNmnemonic resource on * the appropriate label and set the XmNuserData resource of the label to the * widget to get the focus when the mnemonic is typed. */ static void activate_dialog_mnemonics(Widget dialog) { if (!dialog) return; XtAddEventHandler(dialog, KeyPressMask, False, (XtEventHandler) mnemonic_event, (XtPointer) NULL); add_mnemonic_grabs(dialog, dialog); } /* * Removes the event handler and key-grabs for dialog mnemonic handling. */ static void suppress_dialog_mnemonics(Widget dialog) { if (!dialog) return; XtUngrabKey(dialog, AnyKey, Mod1Mask); XtRemoveEventHandler(dialog, KeyPressMask, False, (XtEventHandler) mnemonic_event, (XtPointer) NULL); } #if defined(FEAT_BROWSE) || defined(FEAT_GUI_DIALOG) /* * Use the 'guifont' or 'guifontset' as a fontlist for a dialog widget. */ static void set_fontlist(Widget id) { XmFontList fl; #ifdef FONTSET_ALWAYS if (gui.fontset != NOFONTSET) { fl = gui_motif_fontset2fontlist((XFontSet *)&gui.fontset); if (fl != NULL) { if (XtIsManaged(id)) { XtUnmanageChild(id); XtVaSetValues(id, XmNfontList, fl, NULL); // We should force the widget to recalculate its // geometry now. XtManageChild(id); } else XtVaSetValues(id, XmNfontList, fl, NULL); XmFontListFree(fl); } } #else if (gui.norm_font != NOFONT) { fl = gui_motif_create_fontlist((XFontStruct *)gui.norm_font); if (fl != NULL) { if (XtIsManaged(id)) { XtUnmanageChild(id); XtVaSetValues(id, XmNfontList, fl, NULL); // We should force the widget to recalculate its // geometry now. XtManageChild(id); } else XtVaSetValues(id, XmNfontList, fl, NULL); XmFontListFree(fl); } } #endif } #endif #if defined(FEAT_BROWSE) || defined(PROTO) /* * file selector related stuff */ #include <Xm/FileSB.h> #include <Xm/XmStrDefs.h> typedef struct dialog_callback_arg { char * args; // not used right now int id; } dcbarg_T; static Widget dialog_wgt; static char *browse_fname = NULL; static XmStringCharSet charset = (XmStringCharSet) XmSTRING_DEFAULT_CHARSET; // used to set up XmStrings static void DialogCancelCB(Widget, XtPointer, XtPointer); static void DialogAcceptCB(Widget, XtPointer, XtPointer); /* * This function is used to translate the predefined label text of the * precomposed dialogs. We do this explicitly to allow: * * - usage of gettext for translation, as in all the other places. * * - equalize the messages between different GUI implementations as far as * possible. */ static void set_predefined_label(Widget parent, String name, char *new_label) { XmString str; Widget w; char_u *p, *next; KeySym mnemonic = NUL; w = XtNameToWidget(parent, name); if (!w) return; p = vim_strsave((char_u *)new_label); if (p == NULL) return; for (next = p; *next; ++next) { if (*next == DLG_HOTKEY_CHAR) { int len = STRLEN(next); if (len > 0) { mch_memmove(next, next + 1, len); mnemonic = next[0]; } } } str = XmStringCreate((char *)p, STRING_TAG); vim_free(p); if (str != NULL) { XtVaSetValues(w, XmNlabelString, str, XmNmnemonic, mnemonic, NULL); XmStringFree(str); } gui_motif_menu_fontlist(w); } static void set_predefined_fontlist(Widget parent, String name) { Widget w; w = XtNameToWidget(parent, name); if (!w) return; set_fontlist(w); } /* * 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, // not used (extension added) char_u *initdir, // initial directory, NULL for current dir char_u *filter) // file name filter { char_u dirbuf[MAXPATHL]; char_u dfltbuf[MAXPATHL]; char_u *pattern; char_u *tofree = NULL; // There a difference between the resource name and value, Therefore, we // avoid to (ab-)use the (maybe internationalized!) dialog title as a // dialog name. dialog_wgt = XmCreateFileSelectionDialog(vimShell, "browseDialog", NULL, 0); if (initdir == NULL || *initdir == NUL) { mch_dirname(dirbuf, MAXPATHL); initdir = dirbuf; } if (dflt == NULL) dflt = (char_u *)""; else if (STRLEN(initdir) + STRLEN(dflt) + 2 < MAXPATHL) { // The default selection should be the full path, "dflt" is only the // file name. STRCPY(dfltbuf, initdir); add_pathsep(dfltbuf); STRCAT(dfltbuf, dflt); dflt = dfltbuf; } // Can only use one pattern for a file name. Get the first pattern out of // the filter. An empty pattern means everything matches. if (filter == NULL) pattern = (char_u *)""; else { char_u *s, *p; s = filter; for (p = filter; *p != NUL; ++p) { if (*p == '\t') // end of description, start of pattern s = p + 1; if (*p == ';' || *p == '\n') // end of (first) pattern break; } pattern = vim_strnsave(s, p - s); tofree = pattern; if (pattern == NULL) pattern = (char_u *)""; } XtVaSetValues(dialog_wgt, XtVaTypedArg, XmNdirectory, XmRString, (char *)initdir, STRLEN(initdir) + 1, XtVaTypedArg, XmNdirSpec, XmRString, (char *)dflt, STRLEN(dflt) + 1, XtVaTypedArg, XmNpattern, XmRString, (char *)pattern, STRLEN(pattern) + 1, XtVaTypedArg, XmNdialogTitle, XmRString, (char *)title, STRLEN(title) + 1, NULL); set_predefined_label(dialog_wgt, "Apply", _("&Filter")); set_predefined_label(dialog_wgt, "Cancel", _("&Cancel")); set_predefined_label(dialog_wgt, "Dir", _("Directories")); set_predefined_label(dialog_wgt, "FilterLabel", _("Filter")); set_predefined_label(dialog_wgt, "Help", _("&Help")); set_predefined_label(dialog_wgt, "Items", _("Files")); set_predefined_label(dialog_wgt, "OK", _("&OK")); set_predefined_label(dialog_wgt, "Selection", _("Selection")); // This is to save us from silly external settings using not fixed with // fonts for file selection. set_predefined_fontlist(dialog_wgt, "DirListSW.DirList"); set_predefined_fontlist(dialog_wgt, "ItemsListSW.ItemsList"); gui_motif_menu_colors(dialog_wgt); if (gui.scroll_bg_pixel != INVALCOLOR) XtVaSetValues(dialog_wgt, XmNtroughColor, gui.scroll_bg_pixel, NULL); XtAddCallback(dialog_wgt, XmNokCallback, DialogAcceptCB, (XtPointer)0); XtAddCallback(dialog_wgt, XmNcancelCallback, DialogCancelCB, (XtPointer)0); // We have no help in this window, so hide help button XtUnmanageChild(XmFileSelectionBoxGetChild(dialog_wgt, (unsigned char)XmDIALOG_HELP_BUTTON)); manage_centered(dialog_wgt); activate_dialog_mnemonics(dialog_wgt); // sit in a loop until the dialog box has gone away do { XtAppProcessEvent(XtWidgetToApplicationContext(dialog_wgt), (XtInputMask)XtIMAll); } while (XtIsManaged(dialog_wgt)); suppress_dialog_mnemonics(dialog_wgt); XtDestroyWidget(dialog_wgt); vim_free(tofree); if (browse_fname == NULL) return NULL; return vim_strsave((char_u *)browse_fname); } /* * The code below was originally taken from * /usr/examples/motif/xmsamplers/xmeditor.c * on Digital Unix 4.0d, but heavily modified. */ /* * Process callback from Dialog cancel actions. */ static void DialogCancelCB( Widget w UNUSED, // widget id XtPointer client_data UNUSED, // data from application XtPointer call_data UNUSED) // data from widget class { if (browse_fname != NULL) { XtFree(browse_fname); browse_fname = NULL; } XtUnmanageChild(dialog_wgt); } /* * Process callback from Dialog actions. */ static void DialogAcceptCB( Widget w UNUSED, // widget id XtPointer client_data UNUSED, // data from application XtPointer call_data) // data from widget class { XmFileSelectionBoxCallbackStruct *fcb; if (browse_fname != NULL) { XtFree(browse_fname); browse_fname = NULL; } fcb = (XmFileSelectionBoxCallbackStruct *)call_data; // get the filename from the file selection box XmStringGetLtoR(fcb->value, charset, &browse_fname); // popdown the file selection box XtUnmanageChild(dialog_wgt); } #endif // FEAT_BROWSE #if defined(FEAT_GUI_DIALOG) || defined(PROTO) static int dialogStatus; /* * 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, XtPointer client_data UNUSED, XEvent *event, Boolean *cont UNUSED) { char buf[2]; KeySym key_sym; if (XLookupString(&(event->xkey), buf, 2, &key_sym, NULL) == 1) { if (*buf == CAR) dialogStatus = 1; else if (*buf == ESC) dialogStatus = 2; } if ((key_sym == XK_Left || key_sym == XK_Right) && !(event->xkey.state & ShiftMask)) XmTextFieldClearSelection(w, XtLastTimestampProcessed(gui.dpy)); } static void butproc( Widget w UNUSED, XtPointer client_data, XtPointer call_data UNUSED) { dialogStatus = (int)(long)client_data + 1; } #ifdef HAVE_XPM static Widget create_pixmap_label( Widget parent, String name, char **data, ArgList args, Cardinal arg) { Widget label; Display *dsp; Screen *scr; int depth; Pixmap pixmap = 0; XpmAttributes attr; Boolean rs; XpmColorSymbol color[5] = { {"none", NULL, 0}, {"iconColor1", NULL, 0}, {"bottomShadowColor", NULL, 0}, {"topShadowColor", NULL, 0}, {"selectColor", NULL, 0} }; label = XmCreateLabelGadget(parent, name, args, arg); /* * We need to be careful here, since in case of gadgets, there is * no way to get the background color directly from the widget itself. * In such cases we get it from The Core part of his parent instead. */ dsp = XtDisplayOfObject(label); scr = XtScreenOfObject(label); XtVaGetValues(XtIsSubclass(label, coreWidgetClass) ? label : XtParent(label), XmNdepth, &depth, XmNbackground, &color[0].pixel, XmNforeground, &color[1].pixel, XmNbottomShadowColor, &color[2].pixel, XmNtopShadowColor, &color[3].pixel, XmNhighlight, &color[4].pixel, NULL); attr.valuemask = XpmColorSymbols | XpmCloseness | XpmDepth; attr.colorsymbols = color; attr.numsymbols = 5; attr.closeness = 65535; attr.depth = depth; XpmCreatePixmapFromData(dsp, RootWindowOfScreen(scr), data, &pixmap, NULL, &attr); XtVaGetValues(label, XmNrecomputeSize, &rs, NULL); XtVaSetValues(label, XmNrecomputeSize, True, NULL); XtVaSetValues(label, XmNlabelType, XmPIXMAP, XmNlabelPixmap, pixmap, NULL); XtVaSetValues(label, XmNrecomputeSize, rs, NULL); return label; } #endif int gui_mch_dialog( int type UNUSED, char_u *title, char_u *message, char_u *button_names, int dfltbutton, char_u *textfield, // buffer of size IOSIZE int ex_cmd UNUSED) { char_u *buts; char_u *p, *next; XtAppContext app; XmString label; int butcount; Widget w; Widget dialogform = NULL; Widget form = NULL; Widget dialogtextfield = NULL; Widget *buttons; Widget sep_form = NULL; Boolean vertical; Widget separator = NULL; int n; Arg args[6]; #ifdef HAVE_XPM char **icon_data = NULL; Widget dialogpixmap = NULL; #endif if (title == NULL) title = (char_u *)_("Vim dialog"); // if our pointer is currently hidden, then we should show it. gui_mch_mousehide(FALSE); dialogform = XmCreateFormDialog(vimShell, (char *)"dialog", NULL, 0); // Check 'v' flag in 'guioptions': vertical button placement. vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL); // Set the title of the Dialog window label = XmStringCreateSimple((char *)title); if (label == NULL) return -1; XtVaSetValues(dialogform, XmNdialogTitle, label, XmNhorizontalSpacing, 4, XmNverticalSpacing, vertical ? 0 : 4, NULL); XmStringFree(label); // make a copy, so that we can insert NULs buts = vim_strsave(button_names); if (buts == NULL) return -1; // Count the number of buttons and allocate buttons[]. butcount = 1; for (p = buts; *p; ++p) if (*p == DLG_BUTTON_SEP) ++butcount; buttons = ALLOC_MULT(Widget, butcount); if (buttons == NULL) { vim_free(buts); return -1; } /* * Create the buttons. */ sep_form = (Widget) 0; p = buts; for (butcount = 0; *p; ++butcount) { KeySym mnemonic = NUL; for (next = p; *next; ++next) { if (*next == DLG_HOTKEY_CHAR) { int len = STRLEN(next); if (len > 0) { mch_memmove(next, next + 1, len); mnemonic = next[0]; } } if (*next == DLG_BUTTON_SEP) { *next++ = NUL; break; } } label = XmStringCreate(_((char *)p), STRING_TAG); if (label == NULL) break; buttons[butcount] = XtVaCreateManagedWidget("button", xmPushButtonWidgetClass, dialogform, XmNlabelString, label, XmNmnemonic, mnemonic, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 4, XmNshowAsDefault, butcount == dfltbutton - 1, XmNdefaultButtonShadowThickness, 1, NULL); XmStringFree(label); gui_motif_menu_fontlist(buttons[butcount]); // Layout properly. if (butcount > 0) { if (vertical) XtVaSetValues(buttons[butcount], XmNtopWidget, buttons[butcount - 1], NULL); else { if (*next == NUL) { XtVaSetValues(buttons[butcount], XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 4, NULL); // fill in a form as invisible separator sep_form = XtVaCreateWidget("separatorForm", xmFormWidgetClass, dialogform, XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, buttons[butcount - 1], XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, buttons[butcount], XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 4, NULL); XtManageChild(sep_form); } else { XtVaSetValues(buttons[butcount], XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, buttons[butcount - 1], NULL); } } } else if (!vertical) { if (*next == NUL) { XtVaSetValues(buttons[0], XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 4, NULL); // fill in a form as invisible separator sep_form = XtVaCreateWidget("separatorForm", xmFormWidgetClass, dialogform, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 4, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, buttons[0], XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 4, NULL); XtManageChild(sep_form); } else XtVaSetValues(buttons[0], XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 4, NULL); } XtAddCallback(buttons[butcount], XmNactivateCallback, (XtCallbackProc)butproc, (XtPointer)(long)butcount); p = next; } vim_free(buts); separator = (Widget) 0; if (butcount > 0) { // Create the separator for beauty. n = 0; XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNbottomWidget, buttons[0]); n++; XtSetArg(args[n], XmNbottomOffset, 4); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; separator = XmCreateSeparatorGadget(dialogform, "separator", args, n); XtManageChild(separator); } if (textfield != NULL) { dialogtextfield = XtVaCreateWidget("textField", xmTextFieldWidgetClass, dialogform, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); if (butcount > 0) XtVaSetValues(dialogtextfield, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, separator, NULL); else XtVaSetValues(dialogtextfield, XmNbottomAttachment, XmATTACH_FORM, NULL); set_fontlist(dialogtextfield); XmTextFieldSetString(dialogtextfield, (char *)textfield); XtManageChild(dialogtextfield); XtAddEventHandler(dialogtextfield, KeyPressMask, False, (XtEventHandler)keyhit_callback, (XtPointer)NULL); } // Form holding both message and pixmap labels form = XtVaCreateWidget("separatorForm", xmFormWidgetClass, dialogform, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, NULL); XtManageChild(form); #ifdef HAVE_XPM // Add a pixmap, left of the message. switch (type) { case VIM_GENERIC: icon_data = generic_xpm; break; case VIM_ERROR: icon_data = error_xpm; break; case VIM_WARNING: icon_data = alert_xpm; break; case VIM_INFO: icon_data = info_xpm; break; case VIM_QUESTION: icon_data = quest_xpm; break; default: icon_data = generic_xpm; } n = 0; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNtopOffset, 8); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomOffset, 8); n++; XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNleftOffset, 8); n++; dialogpixmap = create_pixmap_label(form, "dialogPixmap", icon_data, args, n); XtManageChild(dialogpixmap); #endif // Create the dialog message. // Since LessTif is apparently having problems with the creation of // properly localized string, we use LtoR here. The symptom is that the // string is not shown properly in multiple lines as it does in native // Motif. label = XmStringCreateLtoR((char *)message, STRING_TAG); if (label == NULL) return -1; w = XtVaCreateManagedWidget("dialogMessage", xmLabelGadgetClass, form, XmNlabelString, label, XmNalignment, XmALIGNMENT_BEGINNING, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 8, #ifdef HAVE_XPM XmNleftAttachment, XmATTACH_WIDGET, XmNleftWidget, dialogpixmap, #else XmNleftAttachment, XmATTACH_FORM, #endif XmNleftOffset, 8, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 8, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 8, NULL); XmStringFree(label); set_fontlist(w); if (textfield != NULL) { XtVaSetValues(form, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, dialogtextfield, NULL); } else { if (butcount > 0) XtVaSetValues(form, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, separator, NULL); else XtVaSetValues(form, XmNbottomAttachment, XmATTACH_FORM, NULL); } if (dfltbutton < 1) dfltbutton = 1; if (dfltbutton > butcount) dfltbutton = butcount; XtVaSetValues(dialogform, XmNdefaultButton, buttons[dfltbutton - 1], NULL); if (textfield != NULL) XtVaSetValues(dialogform, XmNinitialFocus, dialogtextfield, NULL); else XtVaSetValues(dialogform, XmNinitialFocus, buttons[dfltbutton - 1], NULL); manage_centered(dialogform); activate_dialog_mnemonics(dialogform); if (textfield != NULL && *textfield != NUL) { // This only works after the textfield has been realised. XmTextFieldSetSelection(dialogtextfield, (XmTextPosition)0, (XmTextPosition)STRLEN(textfield), XtLastTimestampProcessed(gui.dpy)); XmTextFieldSetCursorPosition(dialogtextfield, (XmTextPosition)STRLEN(textfield)); } app = XtWidgetToApplicationContext(dialogform); // Loop until a button is pressed or the dialog is killed somehow. dialogStatus = -1; for (;;) { XtAppProcessEvent(app, (XtInputMask)XtIMAll); if (dialogStatus >= 0 || !XtIsManaged(dialogform)) break; } vim_free(buttons); if (textfield != NULL) { p = (char_u *)XmTextGetString(dialogtextfield); if (p == NULL || dialogStatus < 0) *textfield = NUL; else vim_strncpy(textfield, p, IOSIZE - 1); XtFree((char *)p); } suppress_dialog_mnemonics(dialogform); XtDestroyWidget(dialogform); return dialogStatus; } #endif // FEAT_GUI_DIALOG #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, XmNnumChildren, &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, XmNchildren, &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_ALL_MENUS(toolbar) 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[1]; int n = 0; // Enable/Disable tooltip (OK to enable while // currently enabled). if (cur->tip != NULL) (*action)(cur->tip); if (!menu_is_separator(cur->name)) { if (text == 1 || cur->xpm == NULL) { XtSetArg(args[n], XmNlabelType, XmSTRING); ++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(XtParent(toolBar)); #ifdef FEAT_GUI_TABLINE if (showing_tabline) { XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, XtParent(toolBar), NULL); XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabLine, NULL); } else #endif XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, XtParent(toolBar), NULL); if (XtIsManaged(menuBar)) XtVaSetValues(XtParent(toolBar), XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); else XtVaSetValues(XtParent(toolBar), XmNtopAttachment, XmATTACH_FORM, NULL); } else { gui.toolbar_height = 0; if (XtIsManaged(menuBar)) { #ifdef FEAT_GUI_TABLINE if (showing_tabline) { XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabLine, NULL); } else #endif XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); } else { #ifdef FEAT_GUI_TABLINE if (showing_tabline) { XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_FORM, NULL); XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabLine, NULL); } else #endif XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_FORM, NULL); } XtUnmanageChild(XtParent(toolBar)); } gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); } /* * 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 reset_focus(void) { if (textArea != NULL) XmProcessTraversal(textArea, XmTRAVERSE_CURRENT); } int gui_mch_compute_toolbar_height(void) { Dimension borders; Dimension height; // total Toolbar height Dimension whgt; // height of each widget WidgetList children; // list of toolBar's children Cardinal numChildren; // how many children toolBar has int i; borders = 0; height = 0; if (toolBar != (Widget)0 && toolBarFrame != (Widget)0) { // get height of XmFrame parent Dimension fst; Dimension fmh; Dimension tst; Dimension tmh; XtVaGetValues(toolBarFrame, XmNshadowThickness, &fst, XmNmarginHeight, &fmh, NULL); borders += fst + fmh; XtVaGetValues(toolBar, XmNshadowThickness, &tst, XmNmarginHeight, &tmh, XmNchildren, &children, XmNnumChildren, &numChildren, NULL); borders += tst + tmh; for (i = 0; i < (int)numChildren; i++) { whgt = 0; XtVaGetValues(children[i], XmNheight, &whgt, NULL); if (height < whgt) height = whgt; } } #ifdef LESSTIF_VERSION // Hack: When starting up we get wrong dimensions. if (height < 10) height = 24; #endif return (int)(height + (borders << 1)); } void motif_get_toolbar_colors( Pixel *bgp, Pixel *fgp, Pixel *bsp, Pixel *tsp, Pixel *hsp) { XtVaGetValues(toolBar, XmNbackground, bgp, XmNforeground, fgp, XmNbottomShadowColor, bsp, XmNtopShadowColor, tsp, XmNhighlightColor, hsp, NULL); } #endif #if defined(FEAT_GUI_TABLINE) || defined(PROTO) /* * Show or hide the tabline. */ void gui_mch_show_tabline(int showit) { if (tabLine == (Widget)0) return; if (!showit != !showing_tabline) { if (showit) { XtManageChild(tabLine); XtUnmanageChild(XtNameToWidget(tabLine, "PageScroller")); XtUnmanageChild(XtNameToWidget(tabLine, "MinorTabScrollerNext")); XtUnmanageChild(XtNameToWidget(tabLine, "MinorTabScrollerPrevious")); #ifdef FEAT_MENU # ifdef FEAT_TOOLBAR if (XtIsManaged(XtParent(toolBar))) XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, XtParent(toolBar), NULL); else # endif if (XtIsManaged(menuBar)) XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); else #endif XtVaSetValues(tabLine, XmNtopAttachment, XmATTACH_FORM, NULL); XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, tabLine, NULL); } else { XtUnmanageChild(tabLine); #ifdef FEAT_MENU # ifdef FEAT_TOOLBAR if (XtIsManaged(XtParent(toolBar))) XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, XtParent(toolBar), NULL); else # endif if (XtIsManaged(menuBar)) XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); else #endif XtVaSetValues(textAreaForm, XmNtopAttachment, XmATTACH_FORM, NULL); } showing_tabline = showit; } } /* * Return TRUE when tabline is displayed. */ int gui_mch_showing_tabline(void) { return tabLine != (Widget)0 && showing_tabline; } /* * Update the labels of the tabline. */ void gui_mch_update_tabline(void) { tabpage_T *tp; int nr = 1, n; Arg args[10]; int curtabidx = 0, currentpage; Widget tab; XmNotebookPageInfo page_info; XmNotebookPageStatus page_status; int last_page, tab_count; XmString label_str; char *label_cstr; BalloonEval *beval; if (tabLine == (Widget)0) return; // Add a label for each tab page. They all contain the same text area. for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) { if (tp == curtab) curtabidx = nr; page_status = XmNotebookGetPageInfo(tabLine, nr, &page_info); if (page_status == XmPAGE_INVALID || page_info.major_tab_widget == (Widget)0) { // Add the tab n = 0; XtSetArg(args[n], XmNnotebookChildType, XmMAJOR_TAB); n++; XtSetArg(args[n], XmNtraversalOn, False); n++; XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++; XtSetArg(args[n], XmNhighlightThickness, 1); n++; XtSetArg(args[n], XmNshadowThickness , 1); n++; tab = XmCreatePushButton(tabLine, "-Empty-", args, n); XtManageChild(tab); beval = gui_mch_create_beval_area(tab, NULL, tabline_balloon_cb, NULL); XtVaSetValues(tab, XmNuserData, beval, NULL); } else tab = page_info.major_tab_widget; XtVaSetValues(tab, XmNpageNumber, nr, NULL); /* * Change the label text only if it is different */ XtVaGetValues(tab, XmNlabelString, &label_str, NULL); if (XmStringGetLtoR(label_str, XmSTRING_DEFAULT_CHARSET, &label_cstr)) { get_tabline_label(tp, FALSE); if (STRCMP(label_cstr, NameBuff) != 0) { XtVaSetValues(tab, XtVaTypedArg, XmNlabelString, XmRString, NameBuff, STRLEN(NameBuff) + 1, NULL); /* * Force a resize of the tab label button */ XtUnmanageChild(tab); XtManageChild(tab); } XtFree(label_cstr); } } tab_count = nr - 1; XtVaGetValues(tabLine, XmNlastPageNumber, &last_page, NULL); // Remove any old labels. while (nr <= last_page) { if (XmNotebookGetPageInfo(tabLine, nr, &page_info) != XmPAGE_INVALID && page_info.page_number == nr && page_info.major_tab_widget != (Widget)0) { XtVaGetValues(page_info.major_tab_widget, XmNuserData, &beval, NULL); if (beval != NULL) gui_mch_destroy_beval_area(beval); XtUnmanageChild(page_info.major_tab_widget); XtDestroyWidget(page_info.major_tab_widget); } nr++; } XtVaSetValues(tabLine, XmNlastPageNumber, tab_count, NULL); XtVaGetValues(tabLine, XmNcurrentPageNumber, ¤tpage, NULL); if (currentpage != curtabidx) XtVaSetValues(tabLine, XmNcurrentPageNumber, curtabidx, NULL); } /* * Set the current tab to "nr". First tab is 1. */ void gui_mch_set_curtab(int nr) { int currentpage; if (tabLine == (Widget)0) return; XtVaGetValues(tabLine, XmNcurrentPageNumber, ¤tpage, NULL); if (currentpage != nr) XtVaSetValues(tabLine, XmNcurrentPageNumber, nr, NULL); } #endif /* * Set the colors of Widget "id" to the menu colors. */ static void gui_motif_menu_colors(Widget id) { if (gui.menu_bg_pixel != INVALCOLOR) #if (XmVersion >= 1002) XmChangeColor(id, gui.menu_bg_pixel); #else XtVaSetValues(id, XmNbackground, gui.menu_bg_pixel, NULL); #endif if (gui.menu_fg_pixel != INVALCOLOR) XtVaSetValues(id, XmNforeground, gui.menu_fg_pixel, NULL); } /* * Set the colors of Widget "id" to the scrollbar colors. */ static void gui_motif_scroll_colors(Widget id) { if (gui.scroll_bg_pixel != INVALCOLOR) #if (XmVersion >= 1002) XmChangeColor(id, gui.scroll_bg_pixel); #else XtVaSetValues(id, XmNbackground, gui.scroll_bg_pixel, NULL); #endif if (gui.scroll_fg_pixel != INVALCOLOR) XtVaSetValues(id, XmNforeground, gui.scroll_fg_pixel, NULL); } /* * Set the fontlist for Widget "id" to use gui.menu_fontset or gui.menu_font. */ void gui_motif_menu_fontlist(Widget id UNUSED) { #ifdef FEAT_MENU #ifdef FONTSET_ALWAYS if (gui.menu_fontset != NOFONTSET) { XmFontList fl; fl = gui_motif_fontset2fontlist((XFontSet *)&gui.menu_fontset); if (fl != NULL) { if (XtIsManaged(id)) { XtUnmanageChild(id); XtVaSetValues(id, XmNfontList, fl, NULL); // We should force the widget to recalculate its // geometry now. XtManageChild(id); } else XtVaSetValues(id, XmNfontList, fl, NULL); XmFontListFree(fl); } } #else if (gui.menu_font != NOFONT) { XmFontList fl; fl = gui_motif_create_fontlist((XFontStruct *)gui.menu_font); if (fl != NULL) { if (XtIsManaged(id)) { XtUnmanageChild(id); XtVaSetValues(id, XmNfontList, fl, NULL); // We should force the widget to recalculate its // geometry now. XtManageChild(id); } else XtVaSetValues(id, XmNfontList, fl, NULL); XmFontListFree(fl); } } #endif #endif } /* * We don't create it twice for the sake of speed. */ typedef struct _SharedFindReplace { Widget dialog; // the main dialog widget Widget wword; // 'Exact match' check button Widget mcase; // 'match case' check button Widget up; // search direction 'Up' radio button Widget down; // search direction 'Down' radio button Widget what; // 'Find what' entry text widget Widget with; // 'Replace with' entry text widget Widget find; // 'Find Next' action button Widget replace; // 'Replace With' action button Widget all; // 'Replace All' action button Widget undo; // 'Undo' action button Widget cancel; } SharedFindReplace; static SharedFindReplace find_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; static SharedFindReplace repl_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; static void find_replace_destroy_callback( Widget w UNUSED, XtPointer client_data, XtPointer call_data UNUSED) { SharedFindReplace *cd = (SharedFindReplace *)client_data; if (cd != NULL) // suppress_dialog_mnemonics(cd->dialog); cd->dialog = (Widget)0; } static void find_replace_dismiss_callback( Widget w UNUSED, XtPointer client_data, XtPointer call_data UNUSED) { SharedFindReplace *cd = (SharedFindReplace *)client_data; if (cd != NULL) XtUnmanageChild(cd->dialog); } static void entry_activate_callback( Widget w UNUSED, XtPointer client_data, XtPointer call_data UNUSED) { XmProcessTraversal((Widget)client_data, XmTRAVERSE_CURRENT); } static void find_replace_callback( Widget w UNUSED, XtPointer client_data, XtPointer call_data UNUSED) { long_u flags = (long_u)client_data; char *find_text, *repl_text; Boolean direction_down = TRUE; Boolean wword; Boolean mcase; SharedFindReplace *sfr; if (flags == FRD_UNDO) { char_u *save_cpo = p_cpo; // No need to be Vi compatible here. p_cpo = empty_option; u_undo(1); p_cpo = save_cpo; gui_update_screen(); return; } // Get the search/replace strings from the dialog if (flags == FRD_FINDNEXT) { repl_text = NULL; sfr = &find_widgets; } else { repl_text = XmTextFieldGetString(repl_widgets.with); sfr = &repl_widgets; } find_text = XmTextFieldGetString(sfr->what); XtVaGetValues(sfr->down, XmNset, &direction_down, NULL); XtVaGetValues(sfr->wword, XmNset, &wword, NULL); XtVaGetValues(sfr->mcase, XmNset, &mcase, NULL); if (wword) flags |= FRD_WHOLE_WORD; if (mcase) flags |= FRD_MATCH_CASE; (void)gui_do_findrepl((int)flags, (char_u *)find_text, (char_u *)repl_text, direction_down); if (find_text != NULL) XtFree(find_text); if (repl_text != NULL) XtFree(repl_text); } static void find_replace_keypress( Widget w UNUSED, SharedFindReplace *frdp, XKeyEvent *event, Boolean *b UNUSED) { KeySym keysym; if (frdp == NULL) return; keysym = XLookupKeysym(event, 0); // the scape key pops the whole dialog down if (keysym == XK_Escape) XtUnmanageChild(frdp->dialog); } static void set_label(Widget w, char *label) { XmString str; char_u *p, *next; KeySym mnemonic = NUL; if (!w) return; p = vim_strsave((char_u *)label); if (p == NULL) return; for (next = p; *next; ++next) { if (*next == DLG_HOTKEY_CHAR) { int len = STRLEN(next); if (len > 0) { mch_memmove(next, next + 1, len); mnemonic = next[0]; } } } str = XmStringCreateSimple((char *)p); vim_free(p); if (str) { XtVaSetValues(w, XmNlabelString, str, XmNmnemonic, mnemonic, NULL); XmStringFree(str); } gui_motif_menu_fontlist(w); } static void find_replace_dialog_create(char_u *arg, int do_replace) { SharedFindReplace *frdp; Widget separator; Widget input_form; Widget button_form; Widget toggle_form; Widget frame; XmString str; int n; Arg args[6]; int wword = FALSE; int mcase = !p_ic; Dimension width; Dimension widest; char_u *entry_text; frdp = do_replace ? &repl_widgets : &find_widgets; // Get the search string to use. entry_text = get_find_dialog_text(arg, &wword, &mcase); // If the dialog already exists, just raise it. if (frdp->dialog) { gui_motif_synch_fonts(); // If the window is already up, just pop it to the top if (XtIsManaged(frdp->dialog)) XMapRaised(XtDisplay(frdp->dialog), XtWindow(XtParent(frdp->dialog))); else XtManageChild(frdp->dialog); XtPopup(XtParent(frdp->dialog), XtGrabNone); XmProcessTraversal(frdp->what, XmTRAVERSE_CURRENT); if (entry_text != NULL) XmTextFieldSetString(frdp->what, (char *)entry_text); vim_free(entry_text); XtVaSetValues(frdp->wword, XmNset, wword, NULL); return; } // Create a fresh new dialog window if (do_replace) str = XmStringCreateSimple(_("VIM - Search and Replace...")); else str = XmStringCreateSimple(_("VIM - Search...")); n = 0; XtSetArg(args[n], XmNautoUnmanage, False); n++; XtSetArg(args[n], XmNnoResize, True); n++; XtSetArg(args[n], XmNdialogTitle, str); n++; frdp->dialog = XmCreateFormDialog(vimShell, "findReplaceDialog", args, n); XmStringFree(str); XtAddCallback(frdp->dialog, XmNdestroyCallback, find_replace_destroy_callback, frdp); button_form = XtVaCreateWidget("buttonForm", xmFormWidgetClass, frdp->dialog, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, 4, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 4, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 4, NULL); frdp->find = XtVaCreateManagedWidget("findButton", xmPushButtonWidgetClass, button_form, XmNsensitive, True, XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); set_label(frdp->find, _("Find &Next")); XtAddCallback(frdp->find, XmNactivateCallback, find_replace_callback, (do_replace ? (XtPointer)FRD_R_FINDNEXT : (XtPointer)FRD_FINDNEXT)); if (do_replace) { frdp->replace = XtVaCreateManagedWidget("replaceButton", xmPushButtonWidgetClass, button_form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, frdp->find, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); set_label(frdp->replace, _("&Replace")); XtAddCallback(frdp->replace, XmNactivateCallback, find_replace_callback, (XtPointer)FRD_REPLACE); frdp->all = XtVaCreateManagedWidget("replaceAllButton", xmPushButtonWidgetClass, button_form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, frdp->replace, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); set_label(frdp->all, _("Replace &All")); XtAddCallback(frdp->all, XmNactivateCallback, find_replace_callback, (XtPointer)FRD_REPLACEALL); frdp->undo = XtVaCreateManagedWidget("undoButton", xmPushButtonWidgetClass, button_form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, frdp->all, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, NULL); set_label(frdp->undo, _("&Undo")); XtAddCallback(frdp->undo, XmNactivateCallback, find_replace_callback, (XtPointer)FRD_UNDO); } frdp->cancel = XtVaCreateManagedWidget("closeButton", xmPushButtonWidgetClass, button_form, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); set_label(frdp->cancel, _("&Cancel")); XtAddCallback(frdp->cancel, XmNactivateCallback, find_replace_dismiss_callback, frdp); gui_motif_menu_fontlist(frdp->cancel); XtManageChild(button_form); n = 0; XtSetArg(args[n], XmNorientation, XmVERTICAL); n++; XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++; XtSetArg(args[n], XmNrightWidget, button_form); n++; XtSetArg(args[n], XmNrightOffset, 4); n++; XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; separator = XmCreateSeparatorGadget(frdp->dialog, "separator", args, n); XtManageChild(separator); input_form = XtVaCreateWidget("inputForm", xmFormWidgetClass, frdp->dialog, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 4, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, separator, XmNrightOffset, 4, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 4, NULL); { Widget label_what; Widget label_with = (Widget)0; str = XmStringCreateSimple(_("Find what:")); label_what = XtVaCreateManagedWidget("whatLabel", xmLabelGadgetClass, input_form, XmNlabelString, str, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 4, NULL); XmStringFree(str); gui_motif_menu_fontlist(label_what); frdp->what = XtVaCreateManagedWidget("whatText", xmTextFieldWidgetClass, input_form, XmNtopAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, NULL); if (do_replace) { frdp->with = XtVaCreateManagedWidget("withText", xmTextFieldWidgetClass, input_form, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, frdp->what, XmNtopOffset, 4, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtAddCallback(frdp->with, XmNactivateCallback, find_replace_callback, (XtPointer) FRD_R_FINDNEXT); str = XmStringCreateSimple(_("Replace with:")); label_with = XtVaCreateManagedWidget("withLabel", xmLabelGadgetClass, input_form, XmNlabelString, str, XmNleftAttachment, XmATTACH_FORM, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, frdp->what, XmNtopOffset, 4, XmNbottomAttachment, XmATTACH_FORM, NULL); XmStringFree(str); gui_motif_menu_fontlist(label_with); /* * Make the entry activation only change the input focus onto the * with item. */ XtAddCallback(frdp->what, XmNactivateCallback, entry_activate_callback, frdp->with); XtAddEventHandler(frdp->with, KeyPressMask, False, (XtEventHandler)find_replace_keypress, (XtPointer) frdp); } else { /* * Make the entry activation do the search. */ XtAddCallback(frdp->what, XmNactivateCallback, find_replace_callback, (XtPointer)FRD_FINDNEXT); } XtAddEventHandler(frdp->what, KeyPressMask, False, (XtEventHandler)find_replace_keypress, (XtPointer)frdp); // Get the maximum width between the label widgets and line them up. n = 0; XtSetArg(args[n], XmNwidth, &width); n++; XtGetValues(label_what, args, n); widest = width; if (do_replace) { XtGetValues(label_with, args, n); if (width > widest) widest = width; } XtVaSetValues(frdp->what, XmNleftOffset, widest, NULL); if (do_replace) XtVaSetValues(frdp->with, XmNleftOffset, widest, NULL); } XtManageChild(input_form); { Widget radio_box; Widget w; frame = XtVaCreateWidget("directionFrame", xmFrameWidgetClass, frdp->dialog, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, input_form, XmNtopOffset, 4, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 4, XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET, XmNrightWidget, input_form, NULL); str = XmStringCreateSimple(_("Direction")); w = XtVaCreateManagedWidget("directionFrameLabel", xmLabelGadgetClass, frame, XmNlabelString, str, XmNchildHorizontalAlignment, XmALIGNMENT_BEGINNING, XmNchildType, XmFRAME_TITLE_CHILD, NULL); XmStringFree(str); gui_motif_menu_fontlist(w); radio_box = XmCreateRadioBox(frame, "radioBox", (ArgList)NULL, 0); str = XmStringCreateSimple( _("Up")); frdp->up = XtVaCreateManagedWidget("upRadioButton", xmToggleButtonGadgetClass, radio_box, XmNlabelString, str, XmNset, False, NULL); XmStringFree(str); gui_motif_menu_fontlist(frdp->up); str = XmStringCreateSimple(_("Down")); frdp->down = XtVaCreateManagedWidget("downRadioButton", xmToggleButtonGadgetClass, radio_box, XmNlabelString, str, XmNset, True, NULL); XmStringFree(str); gui_motif_menu_fontlist(frdp->down); XtManageChild(radio_box); XtManageChild(frame); } toggle_form = XtVaCreateWidget("toggleForm", xmFormWidgetClass, frdp->dialog, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 4, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, frame, XmNrightOffset, 4, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, input_form, XmNtopOffset, 4, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, 4, NULL); str = XmStringCreateSimple(_("Match whole word only")); frdp->wword = XtVaCreateManagedWidget("wordToggle", xmToggleButtonGadgetClass, toggle_form, XmNlabelString, str, XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, 4, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 4, XmNset, wword, NULL); XmStringFree(str); str = XmStringCreateSimple(_("Match case")); frdp->mcase = XtVaCreateManagedWidget("caseToggle", xmToggleButtonGadgetClass, toggle_form, XmNlabelString, str, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, 4, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, frdp->wword, XmNtopOffset, 4, XmNset, mcase, NULL); XmStringFree(str); gui_motif_menu_fontlist(frdp->wword); gui_motif_menu_fontlist(frdp->mcase); XtManageChild(toggle_form); if (entry_text != NULL) XmTextFieldSetString(frdp->what, (char *)entry_text); vim_free(entry_text); gui_motif_synch_fonts(); manage_centered(frdp->dialog); activate_dialog_mnemonics(frdp->dialog); XmProcessTraversal(frdp->what, XmTRAVERSE_CURRENT); } void gui_mch_find_dialog(exarg_T *eap) { if (!gui.in_use) return; find_replace_dialog_create(eap->arg, FALSE); } void gui_mch_replace_dialog(exarg_T *eap) { if (!gui.in_use) return; find_replace_dialog_create(eap->arg, TRUE); } /* * Synchronize all gui elements, which are dependent upon the * main text font used. Those are in esp. the find/replace dialogs. * If you don't understand why this should be needed, please try to * search for "pi\xea\xb6\xe6" in iso8859-2. */ void gui_motif_synch_fonts(void) { SharedFindReplace *frdp; int do_replace; XFontStruct *font; XmFontList font_list; // FIXME: Unless we find out how to create a XmFontList from a XFontSet, // we just give up here on font synchronization. font = (XFontStruct *)gui.norm_font; if (font == NULL) return; font_list = gui_motif_create_fontlist(font); // OK this loop is a bit tricky... for (do_replace = 0; do_replace <= 1; ++do_replace) { frdp = (do_replace) ? (&repl_widgets) : (&find_widgets); if (frdp->dialog) { XtVaSetValues(frdp->what, XmNfontList, font_list, NULL); if (do_replace) XtVaSetValues(frdp->with, XmNfontList, font_list, NULL); } } XmFontListFree(font_list); }