X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/0d7ed720123a8df22d6a690efdb98976d14c82b5..fad95037768a949a677396a6b8340dc1d7e0875d:/lwlib/lwlib-Xm.c diff --git a/lwlib/lwlib-Xm.c b/lwlib/lwlib-Xm.c index 57e4b4a2ca..732e67c0bf 100644 --- a/lwlib/lwlib-Xm.c +++ b/lwlib/lwlib-Xm.c @@ -18,9 +18,11 @@ along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include +#ifdef HAVE_CONFIG_H +#include +#endif + #include -#include #include #include @@ -57,15 +59,13 @@ Boston, MA 02111-1307, USA. */ #include #include -static void xm_pull_down_callback (/* Widget, XtPointer, XtPointer */); -static void xm_internal_update_other_instances (/* Widget, XtPointer, - XtPointer */); -static void xm_generic_callback (/* Widget, XtPointer, XtPointer */); -static void xm_nosel_callback (/* Widget, XtPointer, XtPointer */); -static void xm_pop_down_callback (/* Widget, XtPointer, XtPointer */); +#ifdef __STDC__ +#define P_(X) X +#else +#define P_(X) () +#endif -static void xm_update_menu (/* widget_instance*, Widget, widget_value*, - Boolean) */); +enum do_call_type { pre_activate, selection, no_selection, post_activate }; /* Structures to keep destroyed instances */ @@ -79,8 +79,68 @@ typedef struct _destroyed_instance struct _destroyed_instance* next; } destroyed_instance; -static destroyed_instance* -all_destroyed_instances = NULL; +static destroyed_instance *make_destroyed_instance P_ ((char *, char *, + Widget, Widget, + Boolean)); +static void free_destroyed_instance P_ ((destroyed_instance*)); +Widget first_child P_ ((Widget)); +Boolean lw_motif_widget_p P_ ((Widget)); +static XmString resource_motif_string P_ ((Widget, char *)); +static void destroy_all_children P_ ((Widget, int)); +static void xm_update_label P_ ((widget_instance *, Widget, widget_value *)); +static void xm_update_list P_ ((widget_instance *, Widget, widget_value *)); +static void xm_update_pushbutton P_ ((widget_instance *, Widget, + widget_value *)); +static void xm_update_cascadebutton P_ ((widget_instance *, Widget, + widget_value *)); +static void xm_update_toggle P_ ((widget_instance *, Widget, widget_value *)); +static void xm_update_radiobox P_ ((widget_instance *, Widget, widget_value *)); +static void make_menu_in_widget P_ ((widget_instance *, Widget, + widget_value *, int)); +static void update_one_menu_entry P_ ((widget_instance *, Widget, + widget_value *, Boolean)); +static void xm_update_menu P_ ((widget_instance *, Widget, widget_value *, + Boolean)); +static void xm_update_text P_ ((widget_instance *, Widget, widget_value *)); +static void xm_update_text_field P_ ((widget_instance *, Widget, + widget_value *)); +void xm_update_one_value P_ ((widget_instance *, Widget, widget_value *)); +static void activate_button P_ ((Widget, XtPointer, XtPointer)); +static Widget make_dialog P_ ((char *, Widget, Boolean, char *, char *, + Boolean, Boolean, Boolean, int, int)); +static destroyed_instance* find_matching_instance P_ ((widget_instance*)); +static void mark_dead_instance_destroyed P_ ((Widget, XtPointer, XtPointer)); +static void recenter_widget P_ ((Widget)); +static Widget recycle_instance P_ ((destroyed_instance*)); +Widget xm_create_dialog P_ ((widget_instance*)); +static Widget make_menubar P_ ((widget_instance*)); +static void remove_grabs P_ ((Widget, XtPointer, XtPointer)); +static Widget make_popup_menu P_ ((widget_instance*)); +static Widget make_main P_ ((widget_instance*)); +void xm_destroy_instance P_ ((widget_instance*)); +void xm_popup_menu P_ ((Widget, XEvent *)); +static void set_min_dialog_size P_ ((Widget)); +static void do_call P_ ((Widget, XtPointer, enum do_call_type)); +static void xm_generic_callback P_ ((Widget, XtPointer, XtPointer)); +static void xm_nosel_callback P_ ((Widget, XtPointer, XtPointer)); +static void xm_pull_down_callback P_ ((Widget, XtPointer, XtPointer)); +static void xm_pop_down_callback P_ ((Widget, XtPointer, XtPointer)); +static void xm_unmap_callback P_ ((Widget, XtPointer, XtPointer)); +void xm_set_keyboard_focus P_ ((Widget, Widget)); +void xm_set_main_areas P_ ((Widget, Widget, Widget)); +static void xm_internal_update_other_instances P_ ((Widget, XtPointer, + XtPointer)); +static void xm_arm_callback P_ ((Widget, XtPointer, XtPointer)); + +#if 0 +void xm_update_one_widget P_ ((widget_instance *, Widget, widget_value *, + Boolean)); +void xm_pop_instance P_ ((widget_instance*, Boolean)); +void xm_manage_resizing P_ ((Widget, Boolean)); +#endif + + +static destroyed_instance *all_destroyed_instances = NULL; static destroyed_instance* make_destroyed_instance (name, type, widget, parent, pop_up_p) @@ -188,7 +248,65 @@ destroy_all_children (widget, first_child_to_destroy) } } - /* update the label of anything subclass of a label */ + + +/* Callback XmNarmCallback and XmNdisarmCallback for buttons in a + menu. CLIENT_DATA contains a pointer to the widget_value + corresponding to widget W. CALL_DATA contains a + XmPushButtonCallbackStruct containing the reason why the callback + is called. */ + +static void +xm_arm_callback (w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *) call_data; + widget_value *wv = (widget_value *) client_data; + widget_instance *instance; + + /* Get the id of the menu bar or popup menu this widget is in. */ + while (1) + { + if (XmIsRowColumn (w)) + { + unsigned char type = 0xff; + + XtVaGetValues (w, XmNrowColumnType, &type, NULL); + if (type == XmMENU_BAR || type == XmMENU_POPUP) + break; + } + + w = XtParent (w); + } + + instance = lw_get_widget_instance (w); + if (instance && instance->info->highlight_cb) + { + call_data = cbs->reason == XmCR_DISARM ? NULL : wv; + instance->info->highlight_cb (w, instance->info->id, call_data); + } +} + + + +/* Update the label of widget WIDGET. WIDGET must be a Label widget + or a subclass of Label. WIDGET_INSTANCE is unused. VAL contains + the value to update. + + Menus: + + Emacs fills VAL->name with the text to display in the menu, and + sets VAL->value to null. Function make_menu_in_widget creates + widgets with VAL->name as resource name. This works because the + Label widget uses its resource name for display if no + XmNlabelString is set. + + Dialogs: + + VAL->name is again set to the resource name, but VAL->value is + not null, and contains the label string to display. */ + static void xm_update_label (instance, widget, val) widget_instance* instance; @@ -200,11 +318,13 @@ xm_update_label (instance, widget, val) XmString key_string = 0; Arg al [256]; int ac; - + ac = 0; if (val->value) { + /* A label string is specified, i.e. we are in a dialog. First + see if it is overridden by something from the resource file. */ res_string = resource_motif_string (widget, val->value); if (res_string) @@ -217,6 +337,7 @@ xm_update_label (instance, widget, val) XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET); XtSetArg (al [ac], XmNlabelString, built_string); ac++; } + XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++; } @@ -293,7 +414,7 @@ xm_update_toggle (instance, widget, val) { XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); XtAddCallback (widget, XmNvalueChangedCallback, - xm_internal_update_other_instances, instance); + xm_generic_callback, instance); XtVaSetValues (widget, XmNset, val->selected, XmNalignment, XmALIGNMENT_BEGINNING, 0); } @@ -340,17 +461,8 @@ xm_update_radiobox (instance, widget, val) } } - /* update a popup menu, pulldown menu or a menubar */ -static Boolean -all_dashes_p (s) - char* s; -{ - char* t; - for (t = s; *t; t++) - if (*t != '-') - return False; - return True; -} + +/* update a popup menu, pulldown menu or a menubar */ /* KEEP_FIRST_CHILDREN gives the number of initial children to keep. */ @@ -366,10 +478,12 @@ make_menu_in_widget (instance, widget, val, keep_first_children) int child_index; widget_value* cur; Widget button = 0; + Widget title = 0; Widget menu; Arg al [256]; int ac; Boolean menubar_p; + unsigned char type; Widget* old_children; unsigned int old_num_children; @@ -380,16 +494,30 @@ make_menu_in_widget (instance, widget, val, keep_first_children) for (num_children = 0, cur = val; cur; num_children++, cur = cur->next); children = (Widget*)XtMalloc (num_children * sizeof (Widget)); - /* tricky way to know if this RowColumn is a menubar or a pulldown... */ - menubar_p = False; - XtSetArg (al[0], XmNisHomogeneous, &menubar_p); - XtGetValues (widget, al, 1); + /* WIDGET should be a RowColumn. */ + if (!XmIsRowColumn (widget)) + abort (); - /* add the unmap callback for popups and pulldowns */ - /*** this sounds bogus ***/ + /* Determine whether WIDGET is a menu bar. */ + type = -1; + XtSetArg (al[0], XmNrowColumnType, &type); + XtGetValues (widget, al, 1); + if (type != XmMENU_BAR && type != XmMENU_PULLDOWN && type != XmMENU_POPUP) + abort (); + menubar_p = type == XmMENU_BAR; + +#if 0 /* This can't be used in LessTif as of 2000-01-24 because it's + impossible to decide from this plus the cascading callback if a + popup is still posted or not. When selecting cascade button A, + then B, then clicking on the frame, the sequence of callbacks is + `cascading A', cascading B', `popdown for all cascade buttons in + the menu bar. */ + /* Add a callback to popups and pulldowns that is called when + it is made invisible again. */ if (!menubar_p) XtAddCallback (XtParent (widget), XmNpopdownCallback, xm_pop_down_callback, (XtPointer)instance); +#endif /* Preserve the first KEEP_FIRST_CHILDREN old children. */ for (child_index = 0, cur = val; child_index < keep_first_children; @@ -403,22 +531,26 @@ make_menu_in_widget (instance, widget, val, keep_first_children) /* Create the rest. */ for (child_index = keep_first_children; cur; child_index++, cur = cur->next) - { + { + enum menu_separator separator; + ac = 0; - XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++; - XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; - XtSetArg (al [ac], XmNuserData, cur->call_data); ac++; + XtSetArg (al[ac], XmNsensitive, cur->enabled); ac++; + XtSetArg (al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; + XtSetArg (al[ac], XmNuserData, cur->call_data); ac++; if (instance->pop_up_p && !cur->contents && !cur->call_data - && !all_dashes_p (cur->name)) + && !lw_separator_p (cur->name, &separator, 1)) { ac = 0; XtSetArg (al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++; - button = XmCreateLabel (widget, cur->name, al, ac); + title = button = XmCreateLabel (widget, cur->name, al, ac); } - else if (all_dashes_p (cur->name)) + else if (lw_separator_p (cur->name, &separator, 1)) { - button = XmCreateSeparator (widget, cur->name, NULL, 0); + ac = 0; + XtSetArg (al[ac], XmNseparatorType, separator); ++ac; + button = XmCreateSeparator (widget, cur->name, al, ac); } else if (!cur->contents) { @@ -426,25 +558,51 @@ make_menu_in_widget (instance, widget, val, keep_first_children) button = XmCreateCascadeButton (widget, cur->name, al, ac); else if (!cur->call_data) button = XmCreateLabel (widget, cur->name, al, ac); + else if (cur->button_type == BUTTON_TYPE_TOGGLE + || cur->button_type == BUTTON_TYPE_RADIO) + { + XtSetArg (al[ac], XmNset, cur->selected); ++ac; + XtSetArg (al[ac], XmNvisibleWhenOff, True); ++ac; + XtSetArg (al[ac], XmNindicatorType, + (cur->button_type == BUTTON_TYPE_TOGGLE + ? XmN_OF_MANY : XmONE_OF_MANY)); + ++ac; + button = XmCreateToggleButton (widget, cur->name, al, ac); + XtAddCallback (button, XmNarmCallback, xm_arm_callback, cur); + XtAddCallback (button, XmNdisarmCallback, xm_arm_callback, cur); + } else - button = XmCreatePushButtonGadget (widget, cur->name, al, ac); - + { + button = XmCreatePushButton (widget, cur->name, al, ac); + XtAddCallback (button, XmNarmCallback, xm_arm_callback, cur); + XtAddCallback (button, XmNdisarmCallback, xm_arm_callback, cur); + } + xm_update_label (instance, button, cur); - - /* don't add a callback to a simple label */ - if (cur->call_data) + + /* Add a callback that is called when the button is + selected. Toggle buttons don't support + XmNactivateCallback, we use XmNvalueChangedCallback in + that case. Don't add a callback to a simple label. */ + if (cur->button_type) + xm_update_toggle (instance, button, cur); + else if (cur->call_data) XtAddCallback (button, XmNactivateCallback, xm_generic_callback, (XtPointer)instance); } else { menu = XmCreatePulldownMenu (widget, cur->name, NULL, 0); + + /* XmNpopdownCallback is working strangely under LessTif. + Using XmNunmapCallback is the only way to go there. */ + if (menubar_p) + XtAddCallback (menu, XmNunmapCallback, xm_unmap_callback, + (XtPointer) instance); + make_menu_in_widget (instance, menu, cur->contents, 0); - XtSetArg (al [ac], XmNsubMenuId, menu); ac++; - /* non-zero values don't work reliably in - conjunction with Emacs' event loop */ - XtSetArg (al [ac], XmNmappingDelay, 0); ac++; - button = XmCreateCascadeButtonGadget (widget, cur->name, al, ac); + XtSetArg (al[ac], XmNsubMenuId, menu); ac++; + button = XmCreateCascadeButton (widget, cur->name, al, ac); xm_update_label (instance, button, cur); @@ -452,19 +610,25 @@ make_menu_in_widget (instance, widget, val, keep_first_children) (XtPointer)instance); } - children [child_index] = button; + children[child_index] = button; } - XtManageChildren (children, num_children); - - /* Last entry is the help button. Has to be done after managing - * the buttons otherwise the menubar is only 4 pixels high... */ + /* Last entry is the help button. The original comment read "Has to + be done after managing the buttons otherwise the menubar is only + 4 pixels high." This is no longer true, and to make + XmNmenuHelpWidget work, we need to set it before managing the + children.. --gerd. */ if (button) - { - ac = 0; - XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++; - XtSetValues (widget, al, ac); - } + XtVaSetValues (widget, XmNmenuHelpWidget, button, 0); + + /* LessTif apparently doesn't recompute centered text when more + widgets are added. So, do it after all widgets have been + created. */ + if (title) + XtVaSetValues (title, XmNalignment, XmALIGNMENT_CENTER, 0); + + if (num_children) + XtManageChildren (children, num_children); XtFree ((char *) children); if (old_children) @@ -495,7 +659,11 @@ update_one_menu_entry (instance, widget, val, deep_p) /* update the menu button as a label. */ if (val->this_one_change >= VISIBLE_CHANGE) - xm_update_label (instance, widget, val); + { + xm_update_label (instance, widget, val); + if (val->button_type) + xm_update_toggle (instance, widget, val); + } /* update the pulldown/pullaside as needed */ ac = 0; @@ -509,11 +677,52 @@ update_one_menu_entry (instance, widget, val, deep_p) { if (contents) { - menu = XmCreatePulldownMenu (XtParent (widget), XtName (widget), NULL, 0); - make_menu_in_widget (instance, menu, contents, 0); - ac = 0; - XtSetArg (al [ac], XmNsubMenuId, menu); ac++; - XtSetValues (widget, al, ac); + unsigned int old_num_children, i; + Widget parent; + Widget *widget_list; + + parent = XtParent (widget); + widget_list = XtCompositeChildren (parent, &old_num_children); + + /* Find the widget position within the parent's widget list. */ + for (i = 0; i < old_num_children; i++) + if (strcmp (XtName (widget_list[i]), XtName (widget)) == 0) + break; + if (i == old_num_children) + abort (); + if (XmIsCascadeButton (widget_list[i])) + { + menu = XmCreatePulldownMenu (parent, XtName(widget), NULL, 0); + make_menu_in_widget (instance, menu, contents, 0); + ac = 0; + XtSetArg (al [ac], XmNsubMenuId, menu); ac++; + XtSetValues (widget, al, ac); + } + else + { + Widget button; + + /* The current menuitem is a XmPushButtonGadget, it + needs to be replaced by a CascadeButtonGadget */ + XtDestroyWidget (widget_list[i]); + menu = XmCreatePulldownMenu (parent, val->name, NULL, 0); + make_menu_in_widget (instance, menu, contents, 0); + ac = 0; + XtSetArg (al [ac], XmNsubMenuId, menu); ac++; + /* Non-zero values don't work reliably in + conjunction with Emacs' event loop */ + XtSetArg (al [ac], XmNmappingDelay, 0); ac++; +#ifdef XmNpositionIndex /* This is undefined on SCO ODT 2.0. */ + /* Tell Motif to put it in the right place */ + XtSetArg (al [ac], XmNpositionIndex , i); ac++; +#endif + button = XmCreateCascadeButton (parent, val->name, al, ac); + xm_update_label (instance, button, val); + + XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback, + (XtPointer)instance); + XtManageChild (button); + } } } else if (!contents) @@ -553,7 +762,9 @@ xm_update_menu (instance, widget, val, deep_p) { if (children) { - for (i = 0, cur = val->contents; i < num_children; + for (i = 0, cur = val->contents; + (i < num_children + && cur); /* how else to ditch unwanted children ?? - mgd */ i++, cur = cur->next) { if (cur->this_one_change == STRUCTURAL_CHANGE) @@ -573,7 +784,10 @@ xm_update_menu (instance, widget, val, deep_p) for (i = 0, cur = val->contents; i < num_children_to_keep; i++) { if (!cur) - abort (); + { + num_children_to_keep = i; + break; + } if (children [i]->core.being_destroyed || strcmp (XtName (children [i]), cur->name)) continue; @@ -1265,11 +1479,28 @@ xm_create_dialog (instance) return widget; } +/* Create a menu bar. We turn off the f10 key + because we have not yet managed to make it work right in Motif. */ + static Widget make_menubar (instance) widget_instance* instance; { - return XmCreateMenuBar (instance->parent, instance->info->name, NULL, 0); + Arg al[3]; + int ac; + + ac = 0; + XtSetArg(al[ac], XmNmenuAccelerator, 0); ++ac; + +#if 0 + /* As of 2000-01-17, the LessTif menu bar resizes to height 0 when + all its children are removed, causing an annoying flickering + behavior. Prevent that by not allowing resizing. */ + XtSetArg(al[ac], XmNresizeHeight, False); ++ac; + XtSetArg(al[ac], XmNresizeWidth, False); ++ac; +#endif + + return XmCreateMenuBar (instance->parent, instance->info->name, al, ac); } static void @@ -1298,6 +1529,7 @@ make_popup_menu (instance) parent->core.window = parent_window; return result; } + static Widget make_main (instance) widget_instance* instance; @@ -1554,8 +1786,6 @@ xm_pop_instance (instance, up) /* motif callback */ -enum do_call_type { pre_activate, selection, no_selection, post_activate }; - static void do_call (widget, closure, type) Widget widget; @@ -1660,9 +1890,25 @@ xm_pull_down_callback (widget, closure, call_data) XtPointer closure; XtPointer call_data; { - do_call (widget, closure, pre_activate); + Widget parent = XtParent (widget); + + if (XmIsRowColumn (parent)) + { + unsigned char type = 0xff; + XtVaGetValues (parent, XmNrowColumnType, &type, NULL); + if (type == XmMENU_BAR) + do_call (widget, closure, pre_activate); + } } + +/* XmNpopdownCallback for MenuShell widgets. WIDGET is the MenuShell, + CLOSURE is a pointer to the widget_instance of the shell, CALL_DATA + is always null under LessTif. + + 2000-01-23: This callback is called for each cascade button in + a menu, whether or not its submenu is visible. */ + static void xm_pop_down_callback (widget, closure, call_data) Widget widget; @@ -1676,6 +1922,17 @@ xm_pop_down_callback (widget, closure, call_data) do_call (widget, closure, post_activate); } +static void +xm_unmap_callback (widget, closure, call_data) + Widget widget; + XtPointer closure; + XtPointer call_data; +{ + widget_instance *instance = (widget_instance *) closure; + if (!instance->pop_up_p) + do_call (widget, closure, post_activate); +} + /* set the keyboard focus */ void @@ -1708,20 +1965,5 @@ xm_manage_resizing (w, flag) Widget w; Boolean flag; { - if (flag) - { - /* Enable the edit widget for resizing. */ - Arg al[1]; - - XtSetArg (al[0], XtNallowShellResize, 0); - XtSetValues (w, al, 1); - } - else - { - /* Disable the edit widget from resizing. */ - Arg al[1]; - - XtSetArg (al[0], XtNallowShellResize, 0); - XtSetValues (w, al, 1); - } + XtVaSetValues (w, XtNallowShellResize, flag, NULL); }