/* $OpenBSD: gui_motif.c,v 1.2 1996/09/21 06:23:03 downsj Exp $ */ /* vi:set ts=4 sw=4: * * 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. */ #include #include #include #include #include #include #include #include #if (XmVersion >= 1002) # include #endif #include #include #include #include "vim.h" #include "globals.h" #include "proto.h" #include "option.h" #include "ops.h" extern Widget vimShell; static Widget vimForm; static Widget textArea; static Widget scrollbarBox[3]; /* Left, right & bottom scrollbar boxes */ static Widget menuBar; /* * Call-back routines. */ static void scroll_cb(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { char_u bytes[4 + sizeof(long_u)]; WIN *wp; GuiScrollbar *sb; int sb_num; if (((XmScrollBarCallbackStruct *)call_data)->reason == XmCR_DRAG) gui.dragged_sb = (XtParent(w) == scrollbarBox[SB_LEFT]) ? SB_LEFT : SB_RIGHT; else gui.dragged_sb = SB_NONE; gui.dragged_wp = (WIN *)client_data; sb_num = 0; for (wp = firstwin; wp != gui.dragged_wp && wp != NULL; wp = wp->w_next) sb_num++; bytes[0] = CSI; bytes[1] = KS_SCROLLBAR; bytes[2] = K_FILLER; bytes[3] = (char_u)sb_num; if (gui.dragged_wp == NULL) sb = &gui.cmdline_sb; else sb = &wp->w_scrollbar; sb->value = ((XmScrollBarCallbackStruct *)call_data)->value; add_long_to_buf((long_u)sb->value, bytes + 4); add_to_input_buf(bytes, 4 + sizeof(long_u)); } static void horiz_scroll_cb(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { char_u bytes[3 + sizeof(long_u)]; if (((XmScrollBarCallbackStruct *)call_data)->reason == XmCR_DRAG) gui.dragged_sb = SB_BOTTOM; else gui.dragged_sb = SB_NONE; bytes[0] = CSI; bytes[1] = KS_HORIZ_SCROLLBAR; bytes[2] = K_FILLER; add_long_to_buf((long_u)((XmScrollBarCallbackStruct *)call_data)->value, bytes + 3); add_to_input_buf(bytes, 3 + sizeof(long_u)); } /* * End of call-back routines */ /* * Create all the motif widgets necessary. */ void gui_mch_create_widgets() { int i; Dimension n; /* * 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 XtInitializeWidgetClass(xmFormWidgetClass); XtInitializeWidgetClass(xmRowColumnWidgetClass); XtInitializeWidgetClass(xmRowColumnWidgetClass); XtInitializeWidgetClass(xmPrimitiveWidgetClass); vimForm = XtVaCreateManagedWidget("vimForm", xmFormWidgetClass, vimShell, XmNresizePolicy, XmRESIZE_GROW, XmNforeground, gui.menu_fg_pixel, XmNbackground, gui.menu_bg_pixel, NULL); menuBar = XtVaCreateManagedWidget("menuBar", xmRowColumnWidgetClass, vimForm, XmNresizeHeight, False, #if (XmVersion >= 1002) XmNtearOffModel, XmTEAR_OFF_ENABLED, #endif XmNtopAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNrowColumnType, XmMENU_BAR, XmNheight, gui.menu_height, XmNforeground, gui.menu_fg_pixel, XmNbackground, gui.menu_bg_pixel, NULL); scrollbarBox[SB_LEFT] = XtVaCreateWidget("leftScrollBarBox", xmRowColumnWidgetClass, vimForm, XmNresizeWidth, False, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, XmNbottomAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNmarginWidth, 0, XmNmarginHeight, 0, XmNspacing, 0, XmNwidth, gui.scrollbar_width, XmNforeground, gui.scroll_fg_pixel, XmNbackground, gui.scroll_fg_pixel, NULL); scrollbarBox[SB_RIGHT] = XtVaCreateWidget("rightScrollBarBox", xmRowColumnWidgetClass, vimForm, XmNresizeWidth, False, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, XmNbottomAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNmarginWidth, 0, XmNmarginHeight, 0, XmNspacing, 0, XmNwidth, gui.scrollbar_width, XmNforeground, gui.scroll_fg_pixel, XmNbackground, gui.scroll_fg_pixel, NULL); scrollbarBox[SB_BOTTOM] = XtVaCreateWidget("bottomScrollBarBox", xmScrollBarWidgetClass, vimForm, XmNorientation, XmHORIZONTAL, XmNminimum, 0, XmNvalue, 0, XmNsliderSize, Columns, XmNmaximum, Columns, /* Motif want one more than actual max */ XmNresizeHeight, False, XmNbottomAttachment, XmATTACH_FORM, XmNheight, gui.scrollbar_width, XmNshadowThickness, 1, XmNbackground, gui.scroll_fg_pixel, XmNtroughColor, gui.scroll_bg_pixel, NULL); XtAddCallback(scrollbarBox[SB_BOTTOM], XmNvalueChangedCallback, horiz_scroll_cb, (XtPointer)NULL); XtAddCallback(scrollbarBox[SB_BOTTOM], XmNdragCallback, horiz_scroll_cb, (XtPointer)NULL); textArea = XtVaCreateManagedWidget("textArea", xmPrimitiveWidgetClass, vimForm, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, XmNbackground, gui.back_pixel, /* 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); /* * If there are highlight or shadow borders, add their widths to our * border offset so we don't draw over them. */ XtVaGetValues(textArea, XmNhighlightThickness, &n, NULL); gui.border_offset += n; XtVaGetValues(textArea, XmNshadowThickness, &n, NULL); gui.border_offset += n; /* Create the command line scroll bars */ for (i = 0; i < 2; i++) { gui.cmdline_sb.id[i] = XtVaCreateManagedWidget("cmdlineScrollBar", xmScrollBarWidgetClass, scrollbarBox[i], XmNshadowThickness, 1, XmNshowArrows, False, XmNbackground, gui.scroll_fg_pixel, XmNtroughColor, gui.scroll_fg_pixel, NULL); XtAddCallback(gui.cmdline_sb.id[i], XmNvalueChangedCallback, scroll_cb, (XtPointer)NULL); XtAddCallback(gui.cmdline_sb.id[i], XmNdragCallback, scroll_cb, (XtPointer)NULL); } gui.num_scrollbars = 1; /* * Text area callbacks */ XtAddEventHandler(textArea, VisibilityChangeMask, FALSE, gui_x11_visibility_cb, (XtPointer)0); XtAddEventHandler(textArea, ExposureMask, FALSE, gui_x11_expose_cb, (XtPointer)0); XtAddEventHandler(textArea, StructureNotifyMask, FALSE, gui_x11_resize_window_cb, (XtPointer)0); XtAddEventHandler(textArea, FocusChangeMask, FALSE, gui_x11_focus_change_cb, (XtPointer)0); XtAddEventHandler(textArea, KeyPressMask, FALSE, gui_x11_key_hit_cb, (XtPointer)0); XtAddEventHandler(textArea, ButtonPressMask | ButtonReleaseMask | ButtonMotionMask, FALSE, gui_x11_mouse_cb, (XtPointer)0); } int gui_mch_get_winsize() { Dimension n; Dimension base_width = 0, base_height = 0; if (gui.which_scrollbars[SB_LEFT]) { XtVaGetValues(scrollbarBox[SB_LEFT], XmNwidth, &n, NULL); base_width += n; } if (gui.which_scrollbars[SB_RIGHT]) { XtVaGetValues(scrollbarBox[SB_RIGHT], XmNwidth, &n, NULL); base_width += n; } if (gui.which_scrollbars[SB_BOTTOM]) { XtVaGetValues(scrollbarBox[SB_BOTTOM], XmNheight, &n, NULL); base_height += n; } base_height += 2 * gui.border_offset; base_width += 2 * gui.border_offset; if (gui.menu_is_active) { XtVaGetValues(menuBar, XmNheight, &n, NULL); base_height += n; } XtVaGetValues(vimShell, XmNheight, &n, NULL); gui.num_rows = (int)(n - base_height) / (int)gui.char_height; XtVaGetValues(vimShell, XmNwidth, &n, NULL); gui.num_cols = (int)(n - base_width) / (int)gui.char_width; Rows = gui.num_rows; Columns = gui.num_cols; gui_reset_scroll_region(); return OK; } void gui_mch_set_winsize() { Dimension left_width, right_width, bottom_height, menu_height; Dimension base_width = 0, base_height = 0; base_width += 2 * gui.border_offset; base_height += 2 * gui.border_offset; if (gui.which_scrollbars[SB_LEFT]) { XtVaGetValues(scrollbarBox[SB_LEFT], XmNwidth, &left_width, NULL); base_width += left_width; } if (gui.which_scrollbars[SB_RIGHT]) { XtVaGetValues(scrollbarBox[SB_RIGHT], XmNwidth, &right_width, NULL); base_width += right_width; } if (gui.which_scrollbars[SB_BOTTOM]) { XtVaGetValues(scrollbarBox[SB_BOTTOM], XmNheight, &bottom_height, NULL); base_height += bottom_height; } if (gui.menu_is_active) { XtVaGetValues(menuBar, XmNheight, &menu_height, NULL); base_height += menu_height; } /* * Need to unmanage vimForm here for a moment, to avoid an error message * when .gvimrc contains ":set guioptions=r". */ XtUnmanageChild(vimForm); XtVaSetValues(vimShell, #ifdef XmNbaseWidth XmNbaseWidth, base_width, XmNbaseHeight, base_height, #endif XmNwidthInc, gui.char_width, XmNheightInc, gui.char_height, XmNminWidth, base_width + MIN_COLUMNS * gui.char_width, XmNminHeight, base_height + MIN_ROWS * gui.char_height, XmNwidth, base_width + Columns * gui.char_width, XmNheight, base_height + Rows * gui.char_height, NULL); XtManageChild(vimForm); } /* * Menu stuff. */ void gui_mch_add_menu(menu, parent) GuiMenu *menu; GuiMenu *parent; { #if (XmVersion >= 1002) Widget widget; XmString label = XmStringCreate((char *)menu->name, XmFONTLIST_DEFAULT_TAG); #else XmString label = XmStringCreate((char *)menu->name, XmSTRING_DEFAULT_CHARSET); #endif Widget shell; menu->id = XtVaCreateWidget("subMenu", xmCascadeButtonWidgetClass, (parent == NULL) ? menuBar : parent->submenu_id, XmNlabelString, label, XmNforeground, gui.menu_fg_pixel, XmNbackground, gui.menu_bg_pixel, NULL); /* XtFree((char *)label); makes Lesstif crash */ /* if 'guic' contains 'g', make menu's contain grey items */ if (vim_strchr(p_guioptions, GO_GREY) != NULL) XtManageChild(menu->id); shell = XtVaCreateWidget("subMenuShell", xmMenuShellWidgetClass, menu->id, XmNwidth, 1, XmNheight, 1, XmNforeground, gui.menu_fg_pixel, XmNbackground, gui.menu_bg_pixel, NULL); menu->submenu_id = XtVaCreateWidget("rowColumnMenu", xmRowColumnWidgetClass, shell, XmNrowColumnType, XmMENU_PULLDOWN, #if (XmVersion >= 1002) XmNtearOffModel, XmTEAR_OFF_ENABLED, #endif NULL); #if (XmVersion >= 1002) /* Set the colors for the tear off widget */ if ((widget = XmGetTearOffControl(menu->submenu_id)) != (Widget)NULL) XtVaSetValues(widget, XmNforeground, gui.menu_fg_pixel, XmNbackground, gui.menu_bg_pixel, NULL); #endif XtVaSetValues(menu->id, XmNsubMenuId, menu->submenu_id, NULL); /* * The "Help" menu is a special case, and should be placed at the far right * hand side of the menu-bar. */ if (parent == NULL && STRCMP((char *)menu->name, "Help") == 0) XtVaSetValues(menuBar, XmNmenuHelpWidget, menu->id, NULL); if (parent == NULL) XtVaSetValues(XtParent(menu->id), XmNforeground, gui.menu_fg_pixel, XmNbackground, gui.menu_bg_pixel, NULL); } void gui_mch_add_menu_item(menu, parent) GuiMenu *menu; GuiMenu *parent; { #if (XmVersion >= 1002) XmString label = XmStringCreate((char *)menu->name, XmFONTLIST_DEFAULT_TAG); #else XmString label = XmStringCreate((char *)menu->name, XmSTRING_DEFAULT_CHARSET); #endif menu->submenu_id = (Widget)0; menu->id = XtVaCreateWidget("subMenu", xmPushButtonWidgetClass, parent->submenu_id, XmNlabelString, label, XmNforeground, gui.menu_fg_pixel, XmNbackground, gui.menu_bg_pixel, NULL); /* XtFree((char *)label); makes Lesstif crash */ if (vim_strchr(p_guioptions, GO_GREY) != NULL) XtManageChild(menu->id); XtAddCallback(menu->id, XmNactivateCallback, gui_x11_menu_cb, (XtPointer)menu); } /* * Destroy the machine specific menu widget. */ void gui_mch_destroy_menu(menu) GuiMenu *menu; { if (menu->id != (Widget)0) XtDestroyWidget(menu->id); if (menu->submenu_id != (Widget)0) XtDestroyWidget(menu->submenu_id); } /* * Scrollbar stuff: */ void gui_mch_create_which_components() { static int prev_which_scrollbars[3] = {FALSE, FALSE, FALSE}; int i; char *attach = NULL, *widget = NULL; /* NOT char_u */ WIN *wp; gui_x11_use_resize_callback(textArea, FALSE); for (i = 0; i < 3; i++) { switch (i) { case SB_LEFT: attach = XmNleftAttachment; widget = XmNleftWidget; break; case SB_RIGHT: attach = XmNrightAttachment; widget = XmNrightWidget; break; case SB_BOTTOM: attach = XmNbottomAttachment; widget = XmNbottomWidget; break; } if (gui.which_scrollbars[i]) { XtManageChild(scrollbarBox[i]); XtVaSetValues(textArea, attach, XmATTACH_WIDGET, widget, scrollbarBox[i], NULL); if (i != SB_BOTTOM && gui.which_scrollbars[SB_BOTTOM]) XtVaSetValues(scrollbarBox[SB_BOTTOM], attach, XmATTACH_WIDGET, widget, scrollbarBox[i], NULL); } else { XtUnmanageChild(scrollbarBox[i]); XtVaSetValues(textArea, attach, XmATTACH_FORM, NULL); if (i != SB_BOTTOM && gui.which_scrollbars[SB_BOTTOM]) XtVaSetValues(scrollbarBox[SB_BOTTOM], attach, XmATTACH_FORM, NULL); } if (gui.which_scrollbars[i] != prev_which_scrollbars[i]) { if (i == SB_LEFT || i == SB_RIGHT) { if (gui.which_scrollbars[i]) { /* Scrollbar box has just appeared */ gui.new_sb[i] = TRUE; } else { /* Scrollbar box has just been deleted */ for (wp = firstwin; wp != NULL; wp = wp->w_next) XtDestroyWidget(wp->w_scrollbar.id[i]); } } prev_which_scrollbars[i] = gui.which_scrollbars[i]; } } if (gui.menu_is_active) { XtManageChild(menuBar); XtVaSetValues(textArea, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); if (gui.which_scrollbars[SB_LEFT]) { XtVaSetValues(scrollbarBox[SB_LEFT], XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); } if (gui.which_scrollbars[SB_RIGHT]) { XtVaSetValues(scrollbarBox[SB_RIGHT], XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menuBar, NULL); } } else { XtUnmanageChild(menuBar); XtVaSetValues(textArea, XmNtopAttachment, XmATTACH_FORM, NULL); if (gui.which_scrollbars[SB_LEFT]) { XtVaSetValues(scrollbarBox[SB_LEFT], XmNtopAttachment, XmATTACH_FORM, NULL); } if (gui.which_scrollbars[SB_RIGHT]) { XtVaSetValues(scrollbarBox[SB_RIGHT], XmNtopAttachment, XmATTACH_FORM, NULL); } } gui_x11_use_resize_callback(textArea, TRUE); if (vimForm != (Widget)NULL) gui_mch_set_winsize(); } /* * Vertical scrollbar stuff: */ void gui_mch_update_scrollbars(worst_update, which_sb) int worst_update; int which_sb; /* SB_LEFT or SB_RIGHT */ { WIN *wp; GuiScrollbar *sb; int idx; Dimension h; /* Height of scrollbar (in pixels) */ Dimension y; /* Coord of top of scrollbar (in pixels) */ int tmp; int val = 0, size = 0, max = 0; if (worst_update >= SB_UPDATE_HEIGHT) { gui_x11_use_resize_callback(textArea, FALSE); XtUnmanageChild(scrollbarBox[which_sb]); } for (wp = firstwin, idx = 0; wp; wp = wp->w_next, idx++) { sb = &wp->w_scrollbar; if (sb->update[which_sb] >= SB_UPDATE_VALUE) { val = sb->value; size = sb->size; max = sb->max + 1; /* Motif has max one past the end */ } if (sb->update[which_sb] == SB_UPDATE_CREATE) { sb->id[which_sb] = XtVaCreateManagedWidget("scrollBar", xmScrollBarWidgetClass, scrollbarBox[which_sb], XmNshadowThickness, 1, #if (XmVersion >= 1002) /* What do we do otherwise? */ XmNpositionIndex, idx, #endif XmNminimum, 1, XmNmaximum, max, XmNbackground, gui.scroll_fg_pixel, XmNtroughColor, gui.scroll_bg_pixel, NULL); XtAddCallback(sb->id[which_sb], XmNvalueChangedCallback, scroll_cb, (XtPointer)wp); XtAddCallback(sb->id[which_sb], XmNdragCallback, scroll_cb, (XtPointer)wp); } if (sb->update[which_sb] >= SB_UPDATE_HEIGHT) { h = sb->height * gui.char_height + sb->status_height * gui.char_height / 2; y = wp->w_winpos * gui.char_height; if (wp == firstwin) { /* Height of top scrollbar includes width of top border */ h += gui.border_offset; } else { /* * Height of other scrollbars includes half of status bar above */ tmp = wp->w_prev->w_status_height * (gui.char_height + 1) / 2; h += tmp; y += gui.border_offset - tmp; } XtVaSetValues(sb->id[which_sb], XmNvalue, val, XmNsliderSize, size, XmNpageIncrement, (size > 2 ? size - 2 : 1), XmNmaximum, max, XmNheight, h, XmNy, y, NULL); } else if (sb->update[which_sb] == SB_UPDATE_VALUE) { XtVaSetValues(sb->id[which_sb], XmNvalue, val, XmNsliderSize, size, XmNpageIncrement, (size > 2 ? size - 2 : 1), XmNmaximum, max, NULL); } sb->update[which_sb] = SB_UPDATE_NOTHING; } /* Command line scrollbar */ sb = &gui.cmdline_sb; max = sb->max + 1; /* Motif has max one past the end */ if (sb->update[which_sb] == SB_UPDATE_HEIGHT) { h = lastwin->w_status_height * (gui.char_height + 1) / 2; y = (Rows - sb->height) * gui.char_height - h; h += sb->height * gui.char_height; /* Height of cmdline scrollbar includes width of bottom border */ h += gui.border_offset; XtVaSetValues(sb->id[which_sb], XmNvalue, sb->value, XmNsliderSize, sb->size, XmNmaximum, max, XmNheight, h, XmNy, y, NULL); } else if (sb->update[which_sb] == SB_UPDATE_VALUE) { XtVaSetValues(sb->id[which_sb], XmNvalue, sb->value, XmNsliderSize, sb->size, XmNmaximum, max, NULL); } sb->update[which_sb] = SB_UPDATE_NOTHING; if (worst_update >= SB_UPDATE_HEIGHT) { if (worst_update >= SB_UPDATE_CREATE) gui_mch_reorder_scrollbars(which_sb); XtManageChild(scrollbarBox[which_sb]); gui_x11_use_resize_callback(textArea, TRUE); } } void gui_mch_reorder_scrollbars(which_sb) int which_sb; { Widget *children; int num_children; Widget tmp; WIN *wp, *wp2; int i, j; XtVaGetValues(scrollbarBox[which_sb], XmNchildren, &children, XmNnumChildren, &num_children, NULL); /* Should be in same order as in the window list */ wp = firstwin; for (i = 0; i < num_children; i++, wp = wp->w_next) { if (wp == NULL) break; /* Shouldn't happen */ if (wp->w_scrollbar.id[which_sb] != children[i]) { /* It's in the wrong place, find what should go here */ wp2 = wp->w_next; for (j = i + 1; j < num_children; j++, wp2 = wp2->w_next) { if (wp2 == NULL) break; /* Shouldn't happen */ if (wp->w_scrollbar.id[which_sb] == children[j]) break; /* Found it */ } if (j >= num_children || wp2 == NULL) break; /* Shouldn't happen */ tmp = children[i]; children[i] = children[j]; children[j] = tmp; } } XtVaSetValues(scrollbarBox[which_sb], XmNchildren, children, NULL); } void gui_mch_destroy_scrollbar(wp) WIN *wp; { if (gui.which_scrollbars[SB_LEFT]) XtDestroyWidget(wp->w_scrollbar.id[SB_LEFT]); if (gui.which_scrollbars[SB_RIGHT]) XtDestroyWidget(wp->w_scrollbar.id[SB_RIGHT]); gui.num_scrollbars--; } /* * Horizontal scrollbar stuff: */ void gui_mch_update_horiz_scrollbar(value, size, max) int value; int size; int max; { static int prev_value = -1, prev_size = -1, prev_max = -1; if (value == prev_value && size == prev_size && max == prev_max) return; prev_value = value; prev_size = size; prev_max = max; XtVaSetValues(scrollbarBox[SB_BOTTOM], XmNvalue, value, XmNsliderSize, size, XmNpageIncrement, (size > 2 ? size - 2 : 1), XmNmaximum, max, NULL); } Window gui_mch_get_wid() { return( XtWindow(textArea) ); }