/* The lwlib interface to Motif widgets. Copyright (C) 1992, 1993, 1994 Lucid, Inc. Copyright (C) 1995 Tinker Systems and INS Engineering Corp. This file is part of the Lucid Widget Library. The Lucid Widget Library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The Lucid Widget Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the Lucid Widget Library. If not, see . */ #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "lwlib-Xm.h" #include "lwlib-utils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LWLIB_WIDGETS_MOTIF #include #if XmVERSION > 1 #include #endif #endif #ifdef LWLIB_MENUBARS_MOTIF static void xm_pull_down_callback (Widget, XtPointer, XtPointer); #endif static void xm_internal_update_other_instances (Widget, XtPointer, XtPointer); /* static void xm_pop_down_callback (Widget, XtPointer, XtPointer); */ static void xm_generic_callback (Widget, XtPointer, XtPointer); #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) static void mark_dead_instance_destroyed (Widget widget, XtPointer closure, XtPointer call_data); static void xm_nosel_callback (Widget, XtPointer, XtPointer); #endif #ifdef LWLIB_SCROLLBARS_MOTIF static void xm_scrollbar_callback (Widget, XtPointer, XtPointer); #endif #ifdef LWLIB_MENUBARS_MOTIF static void xm_update_menu (widget_instance* instance, Widget widget, widget_value* val, Boolean deep_p); #endif /* Structures to keep destroyed instances */ typedef struct _destroyed_instance { char* name; char* type; Widget widget; Widget parent; Boolean pop_up_p; struct _destroyed_instance* next; } destroyed_instance; static destroyed_instance* all_destroyed_instances = NULL; /* Utility function. */ static char * safe_strdup (char* s) { char *result; if (! s) return 0; result = (char *) malloc (strlen (s) + 1); if (! result) return 0; strcpy (result, s); return result; } static destroyed_instance* make_destroyed_instance (char* name, char* type, Widget widget, Widget parent, Boolean pop_up_p) { destroyed_instance* instance = (destroyed_instance*) malloc (sizeof (destroyed_instance)); instance->name = safe_strdup (name); instance->type = safe_strdup (type); instance->widget = widget; instance->parent = parent; instance->pop_up_p = pop_up_p; instance->next = NULL; return instance; } #ifdef LWLIB_DIALOGS_MOTIF static void free_destroyed_instance (destroyed_instance* instance) { free (instance->name); free (instance->type); free (instance); } #endif /* LWLIB_DIALOGS_MOTIF */ /* motif utility functions */ Widget first_child (Widget widget) { return ((CompositeWidget)widget)->composite.children [0]; } Boolean lw_motif_widget_p (Widget widget) { return #ifdef LWLIB_DIALOGS_MOTIF XtClass (widget) == xmDialogShellWidgetClass || #endif XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget); } static char * resource_string (Widget widget, char *name) { XtResource resource; char *result = NULL; resource.resource_name = "labelString"; resource.resource_class = "LabelString"; /* #### should be Xmsomething... */ resource.resource_type = XtRString; resource.resource_size = sizeof (String); resource.resource_offset = 0; resource.default_type = XtRImmediate; resource.default_addr = 0; XtGetSubresources (widget, (XtPointer)&result, name, name, &resource, 1, NULL, 0); return result; } #ifdef LWLIB_DIALOGS_MOTIF static Boolean is_in_dialog_box (Widget w) { Widget wmshell; wmshell = XtParent (w); while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass)) wmshell = XtParent (wmshell); if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass) return True; else return False; } #endif /* LWLIB_DIALOGS_MOTIF */ #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) /* update the label of anything subclass of a label */ static void xm_update_label (widget_instance* UNUSED (instance), Widget widget, widget_value* val) { XmString built_string = NULL; XmString key_string = NULL; XmString val_string = NULL; XmString name_string = NULL; Arg al [20]; int ac = 0; int type; /* Don't clobber pixmap types. */ Xt_GET_VALUE (widget, XmNlabelType, &type); if (type == XmPIXMAP) return; if (val->value) { /* #### Temporary fix. I though Motif was supposed to grok %_ type things. */ lw_remove_accelerator_spec (val->value); #ifdef LWLIB_DIALOGS_MOTIF /* * Sigh. The main text of a label is the name field for menubar * entries. The value field is a possible additional field to be * concatenated on to the name field. HOWEVER, with dialog boxes * the value field is the complete text which is supposed to be * displayed as the label. Yuck. */ if (is_in_dialog_box (widget)) { char *value_name = NULL; value_name = resource_string (widget, val->value); if (!value_name) value_name = val->value; built_string = XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET); } else #endif /* LWLIB_DIALOGS_MOTIF */ { char *value_name = NULL; char *res_name = NULL; res_name = resource_string (widget, val->name); /* Concatenating the value with itself seems just plain daft. */ if (!res_name) { built_string = XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET); } else { name_string = XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET); value_name = XtMalloc (strlen (val->value) + 2); *value_name = 0; strcat (value_name, " "); strcat (value_name, val->value); val_string = XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET); built_string = XmStringConcat (name_string, val_string); XtFree (value_name); } } Xt_SET_ARG (al [ac], XmNlabelString, built_string); ac++; Xt_SET_ARG (al [ac], XmNlabelType, XmSTRING); ac++; } if (val->key) { key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET); Xt_SET_ARG (al [ac], XmNacceleratorText, key_string); ac++; } if (ac) XtSetValues (widget, al, ac); if (built_string) XmStringFree (built_string); if (key_string) XmStringFree (key_string); if (name_string) XmStringFree (name_string); if (val_string) XmStringFree (val_string); } static void xm_safe_update_label (widget_instance* instance, Widget widget, widget_value* val) { /* Don't clobber non-labels. */ if (XtIsSubclass (widget, xmLabelWidgetClass)) xm_update_label (instance, widget, val); } #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */ /* update of list */ static void xm_update_list (widget_instance* instance, Widget widget, widget_value* val) { widget_value* cur; int i; XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback); XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback, instance); for (cur = val->contents, i = 0; cur; cur = cur->next) if (cur->value) { XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET); i += 1; XmListAddItem (widget, xmstr, 0); if (cur->selected) XmListSelectPos (widget, i, False); XmStringFree (xmstr); } } /* update of buttons */ static void xm_update_pushbutton (widget_instance* instance, Widget widget, widget_value* UNUSED (val)) { Xt_SET_VALUE (widget, XmNalignment, XmALIGNMENT_CENTER); XtRemoveAllCallbacks (widget, XmNactivateCallback); XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance); } #ifdef LWLIB_WIDGETS_MOTIF static void xm_update_progress (widget_instance* UNUSED (instance), Widget scale, widget_value* val) { Arg al[20]; int ac = 0; Dimension height = 0; Dimension width = 0; if (!val->call_data) { Xt_SET_ARG (al [ac], XmNeditable, False); ac++; } else { Xt_SET_ARG (al [ac], XmNeditable, val->enabled); ac++; } height = (Dimension) lw_get_value_arg (val, XtNheight); width = (Dimension) lw_get_value_arg (val, XtNwidth); if (height > 0) { Xt_SET_ARG (al [ac], XmNscaleHeight, height); ac++; } if (width > 0) { Xt_SET_ARG (al [ac], XmNscaleWidth, width); ac++; } XtSetValues (scale, al, ac); } #endif /* LWLIB_WIDGETS_MOTIF */ #ifdef LWLIB_MENUBARS_MOTIF static void xm_update_cascadebutton (widget_instance* instance, Widget widget, widget_value* val) { /* Should also rebuild the menu by calling ...update_menu... */ if (val && val->type == CASCADE_TYPE && val->contents && val->contents->type == INCREMENTAL_TYPE) { /* okay, we're now doing a lisp callback to incrementally generate more of the menu. */ XtRemoveAllCallbacks (widget, XmNcascadingCallback); XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback, instance); XtCallCallbacks ((Widget)widget, XmNcascadingCallback, (XtPointer)val->contents); } else { XtRemoveAllCallbacks (widget, XmNcascadingCallback); XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback, instance); } } #endif /* LWLIB_MENUBARS_MOTIF */ /* update toggle and radiobox */ static void xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val) { Arg al [2]; XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback, instance); Xt_SET_ARG (al [0], XmNset, val->selected); Xt_SET_ARG (al [1], XmNalignment, XmALIGNMENT_BEGINNING); XtSetValues (widget, al, 1); } static void xm_update_radiobox (widget_instance* instance, Widget widget, widget_value* val) { Widget toggle; widget_value* cur; /* update the callback */ XtRemoveAllCallbacks (widget, XmNentryCallback); XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance); /* first update all the toggles */ /* Energize kernel interface is currently bad. It sets the selected widget with the selected flag but returns it by its name. So we currently have to support both setting the selection with the selected slot of val contents and setting it with the "value" slot of val. The latter has a higher priority. This to be removed when the kernel is fixed. */ for (cur = val->contents; cur; cur = cur->next) { toggle = XtNameToWidget (widget, cur->value); if (toggle) { Arg al [2]; Xt_SET_ARG (al [0], XmNsensitive, cur->enabled); Xt_SET_ARG (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False)); XtSetValues (toggle, al, 2); } } /* The selected was specified by the value slot */ if (val->value) { toggle = XtNameToWidget (widget, val->value); if (toggle) Xt_SET_VALUE (toggle, XmNset, True); } } #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1 /* update of combo box */ static void xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val) { widget_value* cur; int i; XtRemoveAllCallbacks (widget, XmNselectionCallback); XtAddCallback (widget, XmNselectionCallback, xm_generic_callback, instance); for (cur = val->contents, i = 0; cur; cur = cur->next) if (cur->value) { XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET); i += 1; XmListAddItem (CB_List (widget), xmstr, 0); if (cur->selected) XmListSelectPos (CB_List (widget), i, False); XmStringFree (xmstr); } } #endif #ifdef LWLIB_MENUBARS_MOTIF /* update a popup menu, pulldown menu or a menubar */ static void make_menu_in_widget (widget_instance* instance, Widget widget, widget_value* val) { Widget* children = 0; int num_children; int child_index; widget_value* cur; Widget button = 0; Widget menu; Arg al [256]; int ac; Boolean menubar_p = False; /* Allocate the children array */ 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... */ Xt_GET_VALUE (widget, XmNisHomogeneous, &menubar_p); /* add the unmap callback for popups and pulldowns */ /*** this sounds bogus ***/ /* probably because it is -- cet */ /* if (!menubar_p) XtAddCallback (XtParent (widget), XmNpopdownCallback, xm_pop_down_callback, (XtPointer)instance); */ num_children = 0; for (child_index = 0, cur = val; cur; child_index++, cur = cur->next) { ac = 0; button = 0; Xt_SET_ARG (al [ac], XmNsensitive, cur->enabled); ac++; Xt_SET_ARG (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; Xt_SET_ARG (al [ac], XmNuserData, cur->call_data); ac++; switch (cur->type) { case PUSHRIGHT_TYPE: /* A pushright marker which is not needed for the real Motif menubar. */ break; case SEPARATOR_TYPE: ac = 0; if (cur->value) { /* #### - xlwmenu.h supports several types that motif does not. Also, motif supports pixmaps w/ type NO_LINE and lwlib provides no way to access that functionality. --Stig */ Xt_SET_ARG (al [ac], XmNseparatorType, cur->value); ac++; } button = XmCreateSeparator (widget, "separator", al, ac); break; case CASCADE_TYPE: menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0); make_menu_in_widget (instance, menu, cur->contents); Xt_SET_ARG (al [ac], XmNsubMenuId, menu); ac++; button = XmCreateCascadeButton (widget, cur->name, al, ac); xm_safe_update_label (instance, button, cur); XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback, (XtPointer)instance); break; default: if (menubar_p) button = XmCreateCascadeButton (widget, cur->name, al, ac); else if (!cur->call_data) button = XmCreateLabel (widget, cur->name, al, ac); else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE) { Xt_SET_ARG (al [ac], XmNindicatorType, (cur->type == TOGGLE_TYPE ? XmN_OF_MANY : XmONE_OF_MANY)); ac++; Xt_SET_ARG (al [ac], XmNvisibleWhenOff, True); ac++; button = XmCreateToggleButtonGadget (widget, cur->name, al, ac); } else button = XmCreatePushButtonGadget (widget, cur->name, al, ac); xm_safe_update_label (instance, button, cur); /* don't add a callback to a simple label */ if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE) xm_update_toggle (instance, button, cur); else if (cur->call_data) XtAddCallback (button, XmNactivateCallback, xm_generic_callback, (XtPointer)instance); } /* switch (cur->type) */ if (button) children [num_children++] = button; } /* Last entry is the help button. This used be done after managing the buttons. The comment claimed that it had to be done this way otherwise the menubar ended up only 4 pixels high. That must have been in the Old World. In the New World it stays the proper height if you don't manage them until after you set this and as a bonus the Help menu ends up where it is supposed to. */ if (button) Xt_SET_VALUE (widget, XmNmenuHelpWidget, button); if (num_children) XtManageChildren (children, num_children); XtFree ((char *) children); } static void update_one_menu_entry (widget_instance* instance, Widget widget, widget_value* val, Boolean deep_p) { Arg al [2]; int ac; Widget menu; widget_value* contents; if (val->change == NO_CHANGE) return; /* update the sensitivity and userdata */ /* Common to all widget types */ Xt_SET_ARG (al [0], XmNsensitive, val->enabled); Xt_SET_ARG (al [1], XmNuserData, val->call_data); XtSetValues (widget, al, 2); /* update the menu button as a label. */ if (val->change >= VISIBLE_CHANGE) { xm_safe_update_label (instance, widget, val); if (XtClass (widget) == xmToggleButtonWidgetClass || XtClass (widget) == xmToggleButtonGadgetClass) { xm_update_toggle (instance, widget, val); } } /* update the pulldown/pullaside as needed */ menu = NULL; Xt_GET_VALUE (widget, XmNsubMenuId, &menu); contents = val->contents; if (!menu) { if (contents) { menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0); make_menu_in_widget (instance, menu, contents); Xt_SET_VALUE (widget, XmNsubMenuId, menu); } } else if (!contents) { Xt_SET_VALUE (widget, XmNsubMenuId, NULL); XtDestroyWidget (menu); } else if (deep_p && contents->change != NO_CHANGE) xm_update_menu (instance, menu, val, 1); } static void xm_update_menu (widget_instance* instance, Widget widget, widget_value* val, Boolean deep_p) { /* Widget is a RowColumn widget whose contents have to be updated * to reflect the list of items in val->contents */ if (val->contents->change == STRUCTURAL_CHANGE) { destroy_all_children (widget); make_menu_in_widget (instance, widget, val->contents); } else { /* Update all the buttons of the RowColumn in order. */ Widget* children; unsigned int num_children; int i; widget_value *cur = 0; children = XtCompositeChildren (widget, &num_children); if (children) { for (i = 0, cur = val->contents; i < num_children; i++) { if (!cur) abort (); /* skip if this is a pushright marker or a separator */ if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE) { cur = cur->next; #if 0 /* #### - this could puke if you have a separator as the last item on a pullright menu. */ if (!cur) abort (); #else if (!cur) continue; #endif } if (children [i]->core.being_destroyed || strcmp (XtName (children [i]), cur->name)) continue; update_one_menu_entry (instance, children [i], cur, deep_p); cur = cur->next; } XtFree ((char *) children); } if (cur) abort (); } } #endif /* LWLIB_MENUBARS_MOTIF */ /* update text widgets */ static void xm_update_text (widget_instance* instance, Widget widget, widget_value* val) { XmTextSetString (widget, val->value ? val->value : (char *) ""); XtRemoveAllCallbacks (widget, XmNactivateCallback); XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance); XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); XtAddCallback (widget, XmNvalueChangedCallback, xm_internal_update_other_instances, instance); } static void xm_update_text_field (widget_instance* instance, Widget widget, widget_value* val) { XmTextFieldSetString (widget, val->value ? val->value : (char *) ""); XtRemoveAllCallbacks (widget, XmNactivateCallback); XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance); XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); XtAddCallback (widget, XmNvalueChangedCallback, xm_internal_update_other_instances, instance); } #ifdef LWLIB_SCROLLBARS_MOTIF /* * If this function looks like it does a lot more work than it needs to, * you're right. Blame the Motif scrollbar for not being smart about * updating its appearance. */ static void xm_update_scrollbar (widget_instance *instance, Widget widget, widget_value *val) { if (val->scrollbar_data) { scrollbar_values *data = val->scrollbar_data; int widget_sliderSize, widget_val; int new_sliderSize, new_value; double percent; double h_water, l_water; Arg al [4]; /* First size and position the scrollbar widget. */ Xt_SET_ARG (al [0], XtNx, data->scrollbar_x); Xt_SET_ARG (al [1], XtNy, data->scrollbar_y); Xt_SET_ARG (al [2], XtNwidth, data->scrollbar_width); Xt_SET_ARG (al [3], XtNheight, data->scrollbar_height); XtSetValues (widget, al, 4); /* Now size the scrollbar's slider. */ Xt_SET_ARG (al [0], XmNsliderSize, &widget_sliderSize); Xt_SET_ARG (al [1], XmNvalue, &widget_val); XtGetValues (widget, al, 2); percent = (double) data->slider_size / (double) (data->maximum - data->minimum); new_sliderSize = (int) ((double) (INT_MAX - 1) * percent); percent = (double) (data->slider_position - data->minimum) / (double) (data->maximum - data->minimum); new_value = (int) ((double) (INT_MAX - 1) * percent); if (new_sliderSize > (INT_MAX - 1)) new_sliderSize = INT_MAX - 1; else if (new_sliderSize < 1) new_sliderSize = 1; if (new_value > (INT_MAX - new_sliderSize)) new_value = INT_MAX - new_sliderSize; else if (new_value < 1) new_value = 1; h_water = 1.05; l_water = 0.95; if (new_sliderSize != widget_sliderSize || new_value != widget_val) { int force = ((INT_MAX - widget_sliderSize - widget_val) ? 0 : (INT_MAX - new_sliderSize - new_value)); if (force || (double)new_sliderSize < (l_water * (double)widget_sliderSize) || (double)new_sliderSize > (h_water * (double)widget_sliderSize) || (double)new_value < (l_water * (double)widget_val) || (double)new_value > (h_water * (double)widget_val)) { XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1, False); } } } } #endif /* LWLIB_SCROLLBARS_MOTIF */ /* update a motif widget */ void xm_update_one_widget (widget_instance* instance, Widget widget, widget_value* val, #ifdef LWLIB_MENUBARS_MOTIF Boolean deep_p #else Boolean UNUSED (deep_p) #endif ) { WidgetClass class_; Arg al [20]; int ac = 0; /* Mark as not edited */ val->edited = False; /* Common to all widget types */ Xt_SET_ARG (al [ac], XmNsensitive, val->enabled); ac++; Xt_SET_ARG (al [ac], XmNuserData, val->call_data); ac++; XtSetValues (widget, al, ac); #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) /* Common to all label like widgets */ xm_safe_update_label (instance, widget, val); #endif class_ = XtClass (widget); /* Class specific things */ if (class_ == xmPushButtonWidgetClass || class_ == xmArrowButtonWidgetClass) { xm_update_pushbutton (instance, widget, val); } #ifdef LWLIB_MENUBARS_MOTIF else if (class_ == xmCascadeButtonWidgetClass) { xm_update_cascadebutton (instance, widget, val); } #endif else if (class_ == xmToggleButtonWidgetClass || class_ == xmToggleButtonGadgetClass) { xm_update_toggle (instance, widget, val); } else if (class_ == xmRowColumnWidgetClass) { Boolean radiobox = 0; Xt_GET_VALUE (widget, XmNradioBehavior, &radiobox); if (radiobox) xm_update_radiobox (instance, widget, val); #ifdef LWLIB_MENUBARS_MOTIF else xm_update_menu (instance, widget, val, deep_p); #endif } else if (class_ == xmTextWidgetClass) { xm_update_text (instance, widget, val); } else if (class_ == xmTextFieldWidgetClass) { xm_update_text_field (instance, widget, val); } else if (class_ == xmListWidgetClass) { xm_update_list (instance, widget, val); } #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1 else if (class_ == xmComboBoxWidgetClass) { xm_update_combo_box (instance, widget, val); } #endif #ifdef LWLIB_SCROLLBARS_MOTIF else if (class_ == xmScrollBarWidgetClass) { xm_update_scrollbar (instance, widget, val); } #endif #ifdef LWLIB_WIDGETS_MOTIF else if (class_ == xmScaleWidgetClass) { xm_update_progress (instance, widget, val); } #endif /* Lastly update our global arg values. */ if (val->args && val->args->nargs) XtSetValues (widget, val->args->args, val->args->nargs); } /* getting the value back */ void xm_update_one_value (widget_instance* instance, Widget widget, widget_value* val) { WidgetClass class_ = XtClass (widget); widget_value *old_wv; /* copy the call_data slot into the "return" widget_value */ for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next) if (!strcmp (val->name, old_wv->name)) { val->call_data = old_wv->call_data; break; } if (class_ == xmToggleButtonWidgetClass || class_ == xmToggleButtonGadgetClass) { Xt_GET_VALUE (widget, XmNset, &val->selected); val->edited = True; } else if (class_ == xmTextWidgetClass) { if (val->value) XtFree (val->value); val->value = XmTextGetString (widget); val->edited = True; } else if (class_ == xmTextFieldWidgetClass) { if (val->value) XtFree (val->value); val->value = XmTextFieldGetString (widget); val->edited = True; } else if (class_ == xmRowColumnWidgetClass) { Boolean radiobox = 0; Xt_GET_VALUE (widget, XmNradioBehavior, &radiobox); if (radiobox) { CompositeWidget radio = (CompositeWidget)widget; unsigned int i; for (i = 0; i < radio->composite.num_children; i++) { int set = False; Widget toggle = radio->composite.children [i]; Xt_GET_VALUE (toggle, XmNset, &set); if (set) { if (val->value) free (val->value); val->value = safe_strdup (XtName (toggle)); } } val->edited = True; } } else if (class_ == xmListWidgetClass #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1 || class_ == xmComboBoxWidgetClass #endif ) { int pos_cnt; int* pos_list; Widget list = widget; #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1 if (class_ == xmComboBoxWidgetClass) list = CB_List (widget); #endif if (XmListGetSelectedPos (list, &pos_list, &pos_cnt)) { int i; widget_value* cur; for (cur = val->contents, i = 0; cur; cur = cur->next) if (cur->value) { int j; cur->selected = False; i += 1; for (j = 0; j < pos_cnt; j++) if (pos_list [j] == i) { cur->selected = True; val->value = safe_strdup (cur->name); } } val->edited = 1; XtFree ((char *) pos_list); } } #ifdef LWLIB_SCROLLBARS_MOTIF else if (class_ == xmScrollBarWidgetClass) { /* This function is not used by the scrollbar. */ return; } #endif } #ifdef LWLIB_DIALOGS_MOTIF /* This function is for activating a button from a program. It's wrong because we pass a NULL argument in the call_data which is not Motif compatible. This is used from the XmNdefaultAction callback of the List widgets to have a double-click put down a dialog box like the button would do. I could not find a way to do that with accelerators. */ static void activate_button (Widget UNUSED (widget), XtPointer closure, XtPointer UNUSED (call_data)) { Widget button = (Widget)closure; XtCallCallbacks (button, XmNactivateCallback, NULL); } /* creation functions */ /* dialogs */ #if (XmVersion >= 1002) # define ARMANDACTIVATE_KLUDGE # define DND_KLUDGE #endif #ifdef ARMANDACTIVATE_KLUDGE /* We want typing Return at a dialog box to select the default button; but we're satisfied with having it select the leftmost button instead. In Motif 1.1.5 we could do this by putting this resource in the app-defaults file: *dialog*button1.accelerators:#override\ Return: ArmAndActivate()\n\ KP_Enter: ArmAndActivate()\n\ Ctrlm: ArmAndActivate()\n but that doesn't work with 1.2.1 and I don't understand why. However, doing the equivalent C code does work, with the notable disadvantage that the user can't override it. So that's what we do until we figure out something better.... */ static char button_trans[] = "\ Return: ArmAndActivate()\n\ KP_Enter: ArmAndActivate()\n\ Ctrlm: ArmAndActivate()\n"; #endif /* ARMANDACTIVATE_KLUDGE */ #ifdef DND_KLUDGE /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom was a segv down in libXm somewhere if you used the middle button on a dialog box to begin a drag; when you released the button to make a drop things would lose if you were not over the button where you started the drag (canceling the operation). This was probably due to the fact that the dialog boxes were not set up to handle a drag but were trying to do so anyway for some reason. So we disable drag-and-drop in dialog boxes by turning off the binding for Btn2Down which, by default, initiates a drag. Clearly this is a shitty solution as it only works in default configurations, but... */ static char disable_dnd_trans[] = ": "; #endif /* DND_KLUDGE */ static Widget make_dialog (char* UNUSED (name), Widget parent, Boolean pop_up_p, const char* shell_title, const char* icon_name, Boolean text_input_slot, Boolean radio_box, Boolean list, int left_buttons, int right_buttons) { Widget result; Widget form; Widget row; Widget icon; Widget icon_separator; Widget message; Widget value = 0; Widget separator; Widget button = 0; Widget children [16]; /* for the final XtManageChildren */ int n_children; Arg al[64]; /* Arg List */ int ac; /* Arg Count */ int i; #ifdef DND_KLUDGE XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans); # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override) #else /* ! DND_KLUDGE */ # define DO_DND_KLUDGE(widget) #endif /* ! DND_KLUDGE */ if (pop_up_p) { ac = 0; Xt_SET_ARG(al[ac], XmNtitle, shell_title); ac++; Xt_SET_ARG(al[ac], XtNallowShellResize, True); ac++; Xt_SET_ARG(al[ac], XmNdeleteResponse, XmUNMAP); ac++; result = XmCreateDialogShell (parent, "dialog", al, ac); Xt_SET_ARG(al[ac], XmNautoUnmanage, FALSE); ac++; /* Xt_SET_ARG(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */ Xt_SET_ARG(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; form = XmCreateForm (result, (char *) shell_title, al, ac); } else { ac = 0; Xt_SET_ARG(al[ac], XmNautoUnmanage, FALSE); ac++; Xt_SET_ARG(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; form = XmCreateForm (parent, (char *) shell_title, al, ac); result = form; } ac = 0; Xt_SET_ARG(al[ac], XmNpacking, XmPACK_COLUMN); ac++; Xt_SET_ARG(al[ac], XmNorientation, XmVERTICAL); ac++; Xt_SET_ARG(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++; Xt_SET_ARG(al[ac], XmNmarginWidth, 0); ac++; Xt_SET_ARG(al[ac], XmNmarginHeight, 0); ac++; Xt_SET_ARG(al[ac], XmNspacing, 13); ac++; Xt_SET_ARG(al[ac], XmNadjustLast, False); ac++; Xt_SET_ARG(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++; Xt_SET_ARG(al[ac], XmNisAligned, True); ac++; Xt_SET_ARG(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; Xt_SET_ARG(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNbottomOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNleftOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNrightOffset, 13); ac++; row = XmCreateRowColumn (form, "row", al, ac); n_children = 0; for (i = 0; i < left_buttons; i++) { char button_name [16]; sprintf (button_name, "button%d", i + 1); ac = 0; if (i == 0) { Xt_SET_ARG(al[ac], XmNhighlightThickness, 1); ac++; Xt_SET_ARG(al[ac], XmNshowAsDefault, TRUE); ac++; } Xt_SET_ARG(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; children [n_children] = XmCreatePushButton (row, button_name, al, ac); DO_DND_KLUDGE (children [n_children]); if (i == 0) { button = children [n_children]; ac = 0; Xt_SET_ARG(al[ac], XmNdefaultButton, button); ac++; XtSetValues (row, al, ac); #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */ { XtTranslations losers = XtParseTranslationTable (button_trans); XtOverrideTranslations (button, losers); XtFree ((char *) losers); } #endif /* ARMANDACTIVATE_KLUDGE */ } n_children++; } /* invisible separator button */ ac = 0; Xt_SET_ARG (al[ac], XmNmappedWhenManaged, FALSE); ac++; children [n_children] = XmCreateLabel (row, "separator_button", al, ac); DO_DND_KLUDGE (children [n_children]); n_children++; for (i = 0; i < right_buttons; i++) { char button_name [16]; sprintf (button_name, "button%d", left_buttons + i + 1); ac = 0; Xt_SET_ARG(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; children [n_children] = XmCreatePushButton (row, button_name, al, ac); DO_DND_KLUDGE (children [n_children]); if (! button) button = children [n_children]; n_children++; } XtManageChildren (children, n_children); ac = 0; Xt_SET_ARG(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; Xt_SET_ARG(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNbottomOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNbottomWidget, row); ac++; Xt_SET_ARG(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNleftOffset, 0); ac++; Xt_SET_ARG(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNrightOffset, 0); ac++; separator = XmCreateSeparator (form, "", al, ac); ac = 0; Xt_SET_ARG(al[ac], XmNlabelType, XmPIXMAP); ac++; Xt_SET_ARG(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNtopOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; Xt_SET_ARG(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNleftOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++; icon = XmCreateLabel (form, (char *) icon_name, al, ac); DO_DND_KLUDGE (icon); ac = 0; Xt_SET_ARG(al[ac], XmNmappedWhenManaged, FALSE); ac++; Xt_SET_ARG(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNtopOffset, 6); ac++; Xt_SET_ARG(al[ac], XmNtopWidget, icon); ac++; Xt_SET_ARG(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNbottomOffset, 6); ac++; Xt_SET_ARG(al[ac], XmNbottomWidget, separator); ac++; Xt_SET_ARG(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++; Xt_SET_ARG(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++; icon_separator = XmCreateLabel (form, "", al, ac); DO_DND_KLUDGE (icon_separator); if (text_input_slot) { ac = 0; Xt_SET_ARG(al[ac], XmNcolumns, 50); ac++; Xt_SET_ARG(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; Xt_SET_ARG(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNbottomOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNbottomWidget, separator); ac++; Xt_SET_ARG(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNleftOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNleftWidget, icon); ac++; Xt_SET_ARG(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNrightOffset, 13); ac++; value = XmCreateTextField (form, "value", al, ac); DO_DND_KLUDGE (value); } else if (radio_box) { Widget radio_butt; ac = 0; Xt_SET_ARG(al[ac], XmNmarginWidth, 0); ac++; Xt_SET_ARG(al[ac], XmNmarginHeight, 0); ac++; Xt_SET_ARG(al[ac], XmNspacing, 13); ac++; Xt_SET_ARG(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++; Xt_SET_ARG(al[ac], XmNorientation, XmHORIZONTAL); ac++; Xt_SET_ARG(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNbottomOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNbottomWidget, separator); ac++; Xt_SET_ARG(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNleftOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNleftWidget, icon); ac++; Xt_SET_ARG(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNrightOffset, 13); ac++; value = XmCreateRadioBox (form, "radiobutton1", al, ac); ac = 0; i = 0; radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac); children [i++] = radio_butt; radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac); children [i++] = radio_butt; radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac); children [i++] = radio_butt; XtManageChildren (children, i); } else if (list) { ac = 0; Xt_SET_ARG(al[ac], XmNvisibleItemCount, 5); ac++; Xt_SET_ARG(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; Xt_SET_ARG(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNbottomOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNbottomWidget, separator); ac++; Xt_SET_ARG(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNleftOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNleftWidget, icon); ac++; Xt_SET_ARG(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNrightOffset, 13); ac++; value = XmCreateScrolledList (form, "list", al, ac); /* this is the easiest way I found to have the double click in the list activate the default button */ XtAddCallback (value, XmNdefaultActionCallback, activate_button, button); } /* else add nothing; it's a separator */ ac = 0; Xt_SET_ARG(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; Xt_SET_ARG(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNtopOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNbottomOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNbottomWidget, text_input_slot || radio_box || list ? value : separator); ac++; Xt_SET_ARG(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; Xt_SET_ARG(al[ac], XmNleftOffset, 13); ac++; Xt_SET_ARG(al[ac], XmNleftWidget, icon); ac++; Xt_SET_ARG(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; Xt_SET_ARG(al[ac], XmNrightOffset, 13); ac++; message = XmCreateLabel (form, "message", al, ac); DO_DND_KLUDGE (message); if (list) XtManageChild (value); i = 0; children [i] = row; i++; children [i] = separator; i++; if (text_input_slot || radio_box) { children [i] = value; i++; } children [i] = message; i++; children [i] = icon; i++; children [i] = icon_separator; i++; XtManageChildren (children, i); if (text_input_slot || list) { XtInstallAccelerators (value, button); XmProcessTraversal(value, XmTRAVERSE_CURRENT); } else if (radio_box) { XtInstallAccelerators (form, button); XmProcessTraversal(value, XmTRAVERSE_CURRENT); } /* else we don' need no STEENKIN' assellerators. */ #ifdef DND_KLUDGE XtFree ((char *) dnd_override); #endif #undef DO_DND_KLUDGE return result; } static destroyed_instance* find_matching_instance (widget_instance* instance) { destroyed_instance* cur; destroyed_instance* prev; char* type = instance->info->type; char* name = instance->info->name; for (prev = NULL, cur = all_destroyed_instances; cur; prev = cur, cur = cur->next) { if (!strcmp (cur->name, name) && !strcmp (cur->type, type) && cur->parent == instance->parent && cur->pop_up_p == instance->pop_up_p) { if (prev) prev->next = cur->next; else all_destroyed_instances = cur->next; return cur; } /* do some cleanup */ else if (!cur->widget) { if (prev) prev->next = cur->next; else all_destroyed_instances = cur->next; free_destroyed_instance (cur); cur = prev ? prev : all_destroyed_instances; } } return NULL; } static void recenter_widget (Widget widget) { Widget parent = XtParent (widget); Screen* screen = XtScreen (widget); Dimension screen_width = WidthOfScreen (screen); Dimension screen_height = HeightOfScreen (screen); Dimension parent_width = 0; Dimension parent_height = 0; Dimension child_width = 0; Dimension child_height = 0; Position x; Position y; Arg al [2]; Xt_SET_ARG (al [0], XtNwidth, &child_width); Xt_SET_ARG (al [1], XtNheight, &child_height); XtGetValues (widget, al, 2); Xt_SET_ARG (al [0], XtNwidth, &parent_width); Xt_SET_ARG (al [1], XtNheight, &parent_height); XtGetValues (parent, al, 2); x = (Position) ((parent_width - child_width) / 2); y = (Position) ((parent_height - child_height) / 2); XtTranslateCoords (parent, x, y, &x, &y); if ((Dimension) (x + child_width) > screen_width) x = screen_width - child_width; if (x < 0) x = 0; if ((Dimension) (y + child_height) > screen_height) y = screen_height - child_height; if (y < 0) y = 0; Xt_SET_ARG (al [0], XtNx, x); Xt_SET_ARG (al [1], XtNy, y); XtSetValues (widget, al, 2); } static Widget recycle_instance (destroyed_instance* instance) { Widget widget = instance->widget; /* widget is NULL if the parent was destroyed. */ if (widget) { Widget focus; Widget separator; /* Remove the destroy callback as the instance is not in the list anymore */ XtRemoveCallback (instance->parent, XtNdestroyCallback, mark_dead_instance_destroyed, (XtPointer)instance); /* Give the focus to the initial item */ focus = XtNameToWidget (widget, "*value"); if (!focus) focus = XtNameToWidget (widget, "*button1"); if (focus) XmProcessTraversal(focus, XmTRAVERSE_CURRENT); /* shrink the separator label back to their original size */ separator = XtNameToWidget (widget, "*separator_button"); if (separator) { Arg al [2]; Xt_SET_ARG (al [0], XtNwidth, 5); Xt_SET_ARG (al [1], XtNheight, 5); XtSetValues (separator, al, 2); } /* Center the dialog in its parent */ recenter_widget (widget); } free_destroyed_instance (instance); return widget; } Widget xm_create_dialog (widget_instance* instance) { char* name = instance->info->type; Widget parent = instance->parent; Widget widget; Boolean pop_up_p = instance->pop_up_p; const char* shell_name = 0; const char* icon_name = 0; Boolean text_input_slot = False; Boolean radio_box = False; Boolean list = False; int total_buttons; int left_buttons = 0; int right_buttons = 1; destroyed_instance* dead_one; /* try to find a widget to recycle */ dead_one = find_matching_instance (instance); if (dead_one) { Widget recycled_widget = recycle_instance (dead_one); if (recycled_widget) return recycled_widget; } switch (name [0]){ case 'E': case 'e': icon_name = "dbox-error"; shell_name = "Error"; break; case 'I': case 'i': icon_name = "dbox-info"; shell_name = "Information"; break; case 'L': case 'l': list = True; icon_name = "dbox-question"; shell_name = "Prompt"; break; case 'P': case 'p': text_input_slot = True; icon_name = "dbox-question"; shell_name = "Prompt"; break; case 'Q': case 'q': icon_name = "dbox-question"; shell_name = "Question"; break; } total_buttons = name [1] - '0'; if (name [3] == 'T' || name [3] == 't') { text_input_slot = False; radio_box = True; } else if (name [3]) right_buttons = name [4] - '0'; left_buttons = total_buttons - right_buttons; widget = make_dialog (name, parent, pop_up_p, shell_name, icon_name, text_input_slot, radio_box, list, left_buttons, right_buttons); XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback, (XtPointer) instance); return widget; } #endif /* LWLIB_DIALOGS_MOTIF */ #ifdef LWLIB_MENUBARS_MOTIF static Widget make_menubar (widget_instance* instance) { Arg al[10]; int ac = 0; Xt_SET_ARG(al[ac], XmNmarginHeight, 0); ac++; Xt_SET_ARG(al[ac], XmNshadowThickness, 3); ac++; return XmCreateMenuBar (instance->parent, instance->info->name, al, ac); } static void remove_grabs (Widget shell, XtPointer closure, XtPointer call_data) { Widget menu = (Widget) closure; XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu))); } static Widget make_popup_menu (widget_instance* instance) { Widget parent = instance->parent; Window parent_window = parent->core.window; Widget result; /* sets the parent window to 0 to fool Motif into not generating a grab */ parent->core.window = 0; result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0); XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs, (XtPointer)result); parent->core.window = parent_window; return result; } #endif /* LWLIB_MENUBARS_MOTIF */ #ifdef LWLIB_SCROLLBARS_MOTIF static Widget make_scrollbar (widget_instance *instance, int vertical) { Arg al[20]; int ac = 0; static XtCallbackRec callbacks[2] = { {xm_scrollbar_callback, NULL}, {NULL, NULL} }; callbacks[0].closure = (XtPointer) instance; Xt_SET_ARG (al[ac], XmNminimum, 1); ac++; Xt_SET_ARG (al[ac], XmNmaximum, INT_MAX); ac++; Xt_SET_ARG (al[ac], XmNincrement, 1); ac++; Xt_SET_ARG (al[ac], XmNpageIncrement, 1); ac++; Xt_SET_ARG (al[ac], XmNborderWidth, 0); ac++; Xt_SET_ARG (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++; Xt_SET_ARG (al[ac], XmNdecrementCallback, callbacks); ac++; Xt_SET_ARG (al[ac], XmNdragCallback, callbacks); ac++; Xt_SET_ARG (al[ac], XmNincrementCallback, callbacks); ac++; Xt_SET_ARG (al[ac], XmNpageDecrementCallback, callbacks); ac++; Xt_SET_ARG (al[ac], XmNpageIncrementCallback, callbacks); ac++; Xt_SET_ARG (al[ac], XmNtoBottomCallback, callbacks); ac++; Xt_SET_ARG (al[ac], XmNtoTopCallback, callbacks); ac++; Xt_SET_ARG (al[ac], XmNvalueChangedCallback, callbacks); ac++; return XmCreateScrollBar (instance->parent, instance->info->name, al, ac); } static Widget make_vertical_scrollbar (widget_instance *instance) { return make_scrollbar (instance, 1); } static Widget make_horizontal_scrollbar (widget_instance *instance) { return make_scrollbar (instance, 0); } #endif /* LWLIB_SCROLLBARS_MOTIF */ #ifdef LWLIB_WIDGETS_MOTIF /* glyph widgets */ static Widget xm_create_button (widget_instance *instance) { Arg al[20]; int ac = 0; Widget button = 0; widget_value* val = instance->info->val; Xt_SET_ARG (al [ac], XmNsensitive, val->enabled); ac++; Xt_SET_ARG (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; Xt_SET_ARG (al [ac], XmNuserData, val->call_data); ac++; Xt_SET_ARG (al [ac], XmNmappedWhenManaged, FALSE); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ Xt_SET_ARG (al [ac], XmNhighlightThickness, (Dimension)0); ac++; /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); if (!val->call_data) button = XmCreateLabel (instance->parent, val->name, al, ac); else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE) { Xt_SET_ARG (al [ac], XmNset, val->selected); ac++; Xt_SET_ARG (al [ac], XmNindicatorType, (val->type == TOGGLE_TYPE ? XmN_OF_MANY : XmONE_OF_MANY)); ac++; Xt_SET_ARG (al [ac], XmNvisibleWhenOff, True); ac++; button = XmCreateToggleButton (instance->parent, val->name, al, ac); XtRemoveAllCallbacks (button, XmNvalueChangedCallback); XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback, (XtPointer)instance); } else { button = XmCreatePushButton (instance->parent, val->name, al, ac); XtAddCallback (button, XmNactivateCallback, xm_generic_callback, (XtPointer)instance); } XtManageChild (button); return button; } static Widget xm_create_progress (widget_instance *instance) { Arg al[20]; int ac = 0; Dimension height = 0; Dimension width = 0; Widget scale = 0; widget_value* val = instance->info->val; if (!val->call_data) { Xt_SET_ARG (al [ac], XmNeditable, False); ac++; } else { Xt_SET_ARG (al [ac], XmNeditable, val->enabled); ac++; } Xt_SET_ARG (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; Xt_SET_ARG (al [ac], XmNuserData, val->call_data); ac++; Xt_SET_ARG (al [ac], XmNmappedWhenManaged, FALSE); ac++; Xt_SET_ARG (al [ac], XmNorientation, XmHORIZONTAL); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ Xt_SET_ARG (al [ac], XmNhighlightThickness, (Dimension)0); ac++; height = (Dimension)lw_get_value_arg (val, XtNheight); width = (Dimension)lw_get_value_arg (val, XtNwidth); if (height > 0) { Xt_SET_ARG (al [ac], XmNscaleHeight, height); ac++; } if (width > 0) { Xt_SET_ARG (al [ac], XmNscaleWidth, width); ac++; } /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); scale = XmCreateScale (instance->parent, val->name, al, ac); if (val->call_data) XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback, (XtPointer)instance); XtManageChild (scale); return scale; } static Widget xm_create_text_field (widget_instance *instance) { Arg al[20]; int ac = 0; Widget text = 0; widget_value* val = instance->info->val; Xt_SET_ARG (al [ac], XmNsensitive, val->enabled); ac++; Xt_SET_ARG (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; Xt_SET_ARG (al [ac], XmNuserData, val->call_data); ac++; Xt_SET_ARG (al [ac], XmNmappedWhenManaged, FALSE); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ Xt_SET_ARG (al [ac], XmNhighlightThickness, (Dimension)0); ac++; /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); text = XmCreateTextField (instance->parent, val->name, al, ac); if (val->call_data) XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback, (XtPointer)instance); XtManageChild (text); return text; } static Widget xm_create_label_field (widget_instance *instance) { return xm_create_label (instance->parent, instance->info->val); } Widget xm_create_label (Widget parent, widget_value* val) { Arg al[20]; int ac = 0; Widget label = 0; Xt_SET_ARG (al [ac], XmNsensitive, val->enabled); ac++; Xt_SET_ARG (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; Xt_SET_ARG (al [ac], XmNmappedWhenManaged, FALSE); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ Xt_SET_ARG (al [ac], XmNhighlightThickness, (Dimension)0); ac++; /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); label = XmCreateLabel (parent, val->name, al, ac); XtManageChild (label); /* Do it again for arguments that have no effect until the widget is realized. */ ac = 0; lw_add_value_args_to_args (val, al, &ac); XtSetValues (label, al, ac); return label; } #if XmVERSION > 1 static Widget xm_create_combo_box (widget_instance *instance) { Arg al[20]; int ac = 0; Widget combo = 0; widget_value* val = instance->info->val; Xt_SET_ARG (al [ac], XmNsensitive, val->enabled); ac++; Xt_SET_ARG (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; Xt_SET_ARG (al [ac], XmNuserData, val->call_data); ac++; Xt_SET_ARG (al [ac], XmNmappedWhenManaged, FALSE); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ Xt_SET_ARG (al [ac], XmNhighlightThickness, (Dimension)0); ac++; /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac); if (val->call_data) XtAddCallback (combo, XmNselectionCallback, xm_generic_callback, (XtPointer)instance); XtManageChild (combo); return combo; } #endif #endif /* LWLIB_WIDGETS_MOTIF */ /* Table of functions to create widgets */ const widget_creation_entry xm_creation_table [] = { #ifdef LWLIB_MENUBARS_MOTIF {"menubar", make_menubar}, {"popup", make_popup_menu}, #endif #ifdef LWLIB_SCROLLBARS_MOTIF {"vertical-scrollbar", make_vertical_scrollbar}, {"horizontal-scrollbar", make_horizontal_scrollbar}, #endif #ifdef LWLIB_WIDGETS_MOTIF {"button", xm_create_button}, {"progress", xm_create_progress}, {"text-field", xm_create_text_field}, {"label", xm_create_label_field}, #if XmVERSION > 1 {"combo-box", xm_create_combo_box}, #endif #endif {NULL, NULL} }; /* Destruction of instances */ void xm_destroy_instance ( #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) widget_instance* instance #else widget_instance* UNUSED (instance) #endif ) { #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) /* It appears that this is used only for dialog boxes. */ Widget widget = instance->widget; /* recycle the dialog boxes */ /* Disable the recycling until we can find a way to have the dialog box get reasonable layout after we modify its contents. */ if (0 && XtClass (widget) == xmDialogShellWidgetClass) { destroyed_instance* dead_instance = make_destroyed_instance (instance->info->name, instance->info->type, instance->widget, instance->parent, instance->pop_up_p); dead_instance->next = all_destroyed_instances; all_destroyed_instances = dead_instance; XtUnmanageChild (first_child (instance->widget)); XFlush (XtDisplay (instance->widget)); XtAddCallback (instance->parent, XtNdestroyCallback, mark_dead_instance_destroyed, (XtPointer)dead_instance); } else { /* This might not be necessary now that the nosel is attached to popdown instead of destroy, but it can't hurt. */ XtRemoveCallback (instance->widget, XtNdestroyCallback, xm_nosel_callback, (XtPointer)instance); XtDestroyWidget (instance->widget); } #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */ } /* popup utility */ #ifdef LWLIB_MENUBARS_MOTIF void xm_popup_menu (Widget widget, XEvent *event) { if (event->type == ButtonPress || event->type == ButtonRelease) { /* This is so totally ridiculous: there's NO WAY to tell Motif that *any* button can select a menu item. Only one button can have that honor. */ char *trans = 0; if (event->xbutton.state & Button5Mask) trans = ""; else if (event->xbutton.state & Button4Mask) trans = ""; else if (event->xbutton.state & Button3Mask) trans = ""; else if (event->xbutton.state & Button2Mask) trans = ""; else if (event->xbutton.state & Button1Mask) trans = ""; if (trans) { Arg al [1]; Xt_SET_ARG (al [0], XmNmenuPost, trans); XtSetValues (widget, al, 1); } XmMenuPosition (widget, (XButtonPressedEvent *) event); } XtManageChild (widget); } #endif #ifdef LWLIB_DIALOGS_MOTIF static void set_min_dialog_size (Widget w) { short width; short height; Arg al [2]; Xt_SET_ARG (al [0], XmNwidth, &width); Xt_SET_ARG (al [1], XmNheight, &height); XtGetValues (w, al, 2); Xt_SET_ARG (al [0], XmNminWidth, width); Xt_SET_ARG (al [1], XmNminHeight, height); XtSetValues (w, al, 2); } #endif void xm_pop_instance (widget_instance* instance, Boolean up) { Widget widget = instance->widget; #ifdef LWLIB_DIALOGS_MOTIF if (XtClass (widget) == xmDialogShellWidgetClass) { Widget widget_to_manage = first_child (widget); if (up) { XtManageChild (widget_to_manage); set_min_dialog_size (widget); XmProcessTraversal(widget, XmTRAVERSE_CURRENT); } else XtUnmanageChild (widget_to_manage); } else #endif { if (up) XtManageChild (widget); else XtUnmanageChild (widget); } } /* motif callback */ enum do_call_type { pre_activate, selection, no_selection, post_activate }; static void do_call (Widget widget, XtPointer closure, enum do_call_type type) { XtPointer user_data; widget_instance* instance = (widget_instance*)closure; Widget instance_widget; LWLIB_ID id; if (!instance) return; if (widget->core.being_destroyed) return; instance_widget = instance->widget; if (!instance_widget) return; id = instance->info->id; user_data = NULL; Xt_GET_VALUE (widget, XmNuserData, &user_data); switch (type) { case pre_activate: if (instance->info->pre_activate_cb) instance->info->pre_activate_cb (widget, id, user_data); break; case selection: if (instance->info->selection_cb) instance->info->selection_cb (widget, id, user_data); break; case no_selection: if (instance->info->selection_cb) instance->info->selection_cb (widget, id, (XtPointer) -1); break; case post_activate: if (instance->info->post_activate_cb) instance->info->post_activate_cb (widget, id, user_data); break; default: abort (); } } /* Like lw_internal_update_other_instances except that it does not do anything if its shell parent is not managed. This is to protect lw_internal_update_other_instances to dereference freed memory if the widget was ``destroyed'' by caching it in the all_destroyed_instances list */ static void xm_internal_update_other_instances (Widget widget, XtPointer closure, XtPointer call_data) { Widget parent; for (parent = widget; parent; parent = XtParent (parent)) if (XtIsShell (parent)) break; else if (!XtIsManaged (parent)) return; lw_internal_update_other_instances (widget, closure, call_data); } static void xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data) { #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)) /* We want the selected status to change only when we decide it should change. Yuck but correct. */ if (XtClass (widget) == xmToggleButtonWidgetClass || XtClass (widget) == xmToggleButtonGadgetClass) { Boolean check; Xt_GET_VALUE (widget, XmNset, &check); Xt_SET_VALUE (widget, XmNset, !check); } #endif lw_internal_update_other_instances (widget, closure, call_data); do_call (widget, closure, selection); } #if 0 /* Caller above is commented out */ static void xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data) { do_call (widget, closure, post_activate); } #endif /* 0 */ #ifdef LWLIB_MENUBARS_MOTIF static void xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data) { #if 0 if (call_data) { /* new behavior for incremental menu construction */ } else #endif do_call (widget, closure, pre_activate); } #endif /* LWLIB_MENUBARS_MOTIF */ #ifdef LWLIB_SCROLLBARS_MOTIF static void xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data) { widget_instance *instance = (widget_instance *) closure; LWLIB_ID id; XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *) call_data; scroll_event event_data; scrollbar_values *val = (scrollbar_values *) instance->info->val->scrollbar_data; double percent; if (!instance || widget->core.being_destroyed) return; id = instance->info->id; percent = (double) (data->value - 1) / (double) (INT_MAX - 1); event_data.slider_value = (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum; if (event_data.slider_value > (val->maximum - val->slider_size)) event_data.slider_value = val->maximum - val->slider_size; else if (event_data.slider_value < 1) event_data.slider_value = 1; if (data->event) { switch (data->event->xany.type) { case KeyPress: case KeyRelease: event_data.time = data->event->xkey.time; break; case ButtonPress: case ButtonRelease: event_data.time = data->event->xbutton.time; break; case MotionNotify: event_data.time = data->event->xmotion.time; break; case EnterNotify: case LeaveNotify: event_data.time = data->event->xcrossing.time; break; default: event_data.time = 0; break; } } else event_data.time = 0; switch (data->reason) { case XmCR_DECREMENT: event_data.action = SCROLLBAR_LINE_UP; break; case XmCR_INCREMENT: event_data.action = SCROLLBAR_LINE_DOWN; break; case XmCR_PAGE_DECREMENT: event_data.action = SCROLLBAR_PAGE_UP; break; case XmCR_PAGE_INCREMENT: event_data.action = SCROLLBAR_PAGE_DOWN; break; case XmCR_TO_TOP: event_data.action = SCROLLBAR_TOP; break; case XmCR_TO_BOTTOM: event_data.action = SCROLLBAR_BOTTOM; break; case XmCR_DRAG: event_data.action = SCROLLBAR_DRAG; break; case XmCR_VALUE_CHANGED: event_data.action = SCROLLBAR_CHANGE; break; default: event_data.action = SCROLLBAR_CHANGE; break; } if (instance->info->pre_activate_cb) instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data); } #endif /* LWLIB_SCROLLBARS_MOTIF */ #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) static void mark_dead_instance_destroyed (Widget UNUSED (widget), XtPointer closure, XtPointer UNUSED (call_data)) { destroyed_instance* instance = (destroyed_instance*)closure; instance->widget = NULL; } static void xm_nosel_callback (Widget widget, XtPointer closure, XtPointer UNUSED (call_data)) { /* This callback is only called when a dialog box is dismissed with the wm's destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed in that case, not just unmapped, so that it releases its keyboard grabs. But there are problems with running our callbacks while the widget is in the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP instead of XmDESTROY and then destroy it ourself after having run the callback. */ do_call (widget, closure, no_selection); XtDestroyWidget (widget); } #endif /* set the keyboard focus */ void xm_set_keyboard_focus (Widget UNUSED (parent), Widget w) { XmProcessTraversal (w, XmTRAVERSE_CURRENT); /* At some point we believed that it was necessary to use XtSetKeyboardFocus instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus. Presumably the problem was elsewhere, and is now gone... */ }