MOTIF ANGST PAGE

If you have any ANGSTFUL Motif code, comments, documentation, or resources, please share them with me! Here is some of the stronger stuff I've found. Note: this is only for official Open Software Foundation Motif inspired Angst. If you're experiencing TCL/Tk That Only Looks Like Motif But Doesn't Suck Angst, then you should stop whining and fix the problem yourself, if somebody else hasn't already.


/* Note that the text callbacks are "weird" in that they expect values in the callback structure to be set inside the callback proc to determine what actions need to be taken after the callbackproc returns. In particular, the XmTextVerifyCallbackStruct's 'doit' slot is always set to True, and must be set to False if the callbackproc doesn't want the action to be taken. To do this, Set_Call_Data_For_XmTextVerifyCallbackStruct() is called by Wcb_Meta_Callbackproc() after the callback lisp code is evaluated, and the values bound to these settable variables are set inside call_data....

Another inconsistency with the Text widget is that some callbacks on this widget return XmAnyCallbackStruct's (XmNactivateCallback, XmNfocusCallback, XmNvalueChangedCallback), whereas XmNlosingFocusCallback, XmNmodifyVerifyCallback, and XmNmotionVerifyCallback return XmTextVerifyCallbackStruct. In the code below, we look at the 'reason' slot of the call data, (which is present in both XmAnyCallbackStruct and in XmTextVerifyCallbackStruct) to determine the kind of callback that occured and we only bind the values that are appropriate for that kind of callback. Information about which slots are valid for particular callback was taken from the documentation on the XmText(3X) widget, and verified against the Motif 1.1 source -- this is valid for both XmText and XmTextField widgets... */

static LVAL s_CALLBACK_CUR_INSERT, s_CALLBACK_NEW_INSERT, s_CALLBACK_START_POS, s_CALLBACK_END_POS, s_CALLBACK_TEXT;
static void Lexical_Bindings_For_XmTextVerifyCallbackStruct(bindings_list,
							    lexical_env,
							    call_data,
							    client_data)
     LVAL bindings_list;	/* a list of symbols to which values from XmTextVerifyCallbackStruct are bound */
     LVAL lexical_env;		
     XtPointer call_data;
     LVAL client_data;		/* XLTYPE_CALLBACKOBJ */
{
  extern LVAL true;
  register LVAL s_bindname;
  XmTextVerifyCallbackStruct* cd;

  /* How long can this go on???? */
}

/******************************************************************************
 * As far as I can tell, the only settable field in the Text widget's call_data
 * is the doit field. Of course, the documentation and the source are quite
 * cryptic about this!
 ******************************************************************************/
static void Set_Call_Data_For_XmTextVerifyCallbackStruct(lexical_env, call_data)
     LVAL lexical_env;		
     XtPointer call_data;
{
  register LVAL ep;

  /* Note: Wcb_Meta_Callbackproc() won't call this proc if call_data==NULL */

  switch (((XmAnyCallbackStruct*) call_data)->reason) {
  case XmCR_LOSING_FOCUS:	/* valid XmTextVerifyCallbackStruct fields: reason, event, doit, currInsert, newInsert, startPos, endPos */
  case XmCR_MODIFYING_TEXT_VALUE: /* valid XmTextVerifyCallbackStruct fields: reason, event, doit, currInsert, newInsert, text, startPos, endPos */
  case XmCR_MOVING_INSERT_CURSOR: /* valid XmTextVerifyCallbackStruct fields: reason, event, currInsert, newInsert, doit */
    for (ep = car(lexical_env); /* get current environment stack frame which was created in Wcb_Meta_Callbackproc() by Lexical_Bindings_For_XmTextVerifyCallbackStruct(). */
	 (ep != NIL); ep = cdr(ep)) /* while there are more bindings in current environment */
      if (s_CALLBACK_DOIT == car(car(ep))) { /* check to see if this symbol was bound in the envt */
	((XmTextVerifyCallbackStruct*) call_data)->doit = ((cdr(car(ep)) != NIL) ? TRUE : FALSE); /* set doit field if value is non-NIL */
	return;
      }
    break;
  default:			/* do nothing for most cases... */
    break;
  }
}

/********************************************************************************/
static LVAL Cvt_XmRXmString_to_LVAL(res_val, resource)
     GetValues_Union    res_val;
     Resource_Instance* resource;
{
#ifdef WINTERP_MOTIF_11
#ifdef WINTERP_MOTIF_111	/* MOTIF 1.1.1 version */
  /*
   * Motif 1.1.1 documentation claims that XmStrings retrieved via XtGetValues()
   * are copies of the internal resource value, and that XmStringFree() must be
   * called by the application to free the copyied value. In WINTERP, by passing
   * the XmString pointer to cv_xmstring, we can be assured that the lisp-XmString
   * will get freed upon garbage collection when the node is no longer referenced.
   */
  return (res_val.XtPointer_value ? cv_xmstring((XmString) res_val.XtPointer_value) : NIL);
#else				/* MOTIF 1.1 version */
  /*
   * Motif 1.1's README states:
   * |        Compound String Resources Inconsistently Copied
   * | 
   * |        In this release, XtGetValues for a resource whose value is a
   * |        compound string sometimes does and sometimes does not copy
   * |        the returned string.  Following is a list of some known
   * |        resources whose XmString values are not copied (this list
   * |        may not be exhaustive):
   * | 
   * |             XmBulletinBoard     XmNdialogTitle
   * |             XmFileSelectionBox  XmNdirectory
   * |                                 XmNnoMatchString
   * |             XmRowColumn         XmNlabelString
   * |             XmScale             XmNtitleString
   *
   * Handling the above would require special casing for all XmRString resources,
   * which I don't have time to do. They fixed this stupidity in 1.1.1 (see above),
   * but certainly in a suboptimal fashion -- yet more wasteful copying occurs because
   * all widgets copy their internal XmStrings upon XtGetValues().
   *
   * The upshot of all this is that for Motif 1.1 (but not 1.1.1), there will be a memory
   * leak in XtGetValues() on XmString resources that copy the internal resource value.
   * We must make a copy here because it would be worse to XmStringFree() resources that
   * returned a pointer whose memory was managed by Motif.
   */
  return (res_val.XtPointer_value ? cv_xmstring(XmStringCopy((XmString) res_val.XtPointer_value)) : NIL);
#endif /* WINTERP_MOTIF_111 */
#else				/* MOTIF 1.0 version */
  /*
   * In Motif 1.0, XmStrings() returned via XtGetValues() were temporary pointers
   * that has to be copied via XmStringCopy()...
   */
  return (res_val.XtPointer_value ? cv_xmstring(XmStringCopy((XmString) res_val.XtPointer_value)) : NIL);
#endif /* WINTERP_MOTIF_11 */
}
/* 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. */

/* 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;
      Dimension height, width, pane_minimum, pane_maximum;
      Dimension pos_x, pos_y;
      int widget_sliderSize, widget_val;
      int new_sliderSize, new_value;
      double percent;
      double h_water, l_water;

      XtVaGetValues (widget,
		     XmNheight, &height,
		     XmNwidth, &width,
		     XmNx, &pos_x,
		     XmNy, &pos_y,
		     XmNpaneMinimum, &pane_minimum,
		     XmNpaneMaximum, &pane_maximum,
		     XmNsliderSize, &widget_sliderSize,
		     XmNvalue, &widget_val,
		     0);

      /*
       * First size and position the scrollbar widget.
       */
      if (height != data->scrollbar_height || pos_y != data->scrollbar_pos)
	{
	  XtConfigureWidget (widget, pos_x, data->scrollbar_pos,
			     width, data->scrollbar_height, 0);

	  /* Force the manager to recompute the positioning. */
	  XtVaSetValues (widget, XmNrefigureMode, False, 0);
	  XtVaSetValues (widget, XmNrefigureMode, True, 0);
	}

      /* pane_min should always equal pane_max */
      if (pane_minimum != data->scrollbar_height)
	XtVaSetValues (widget,
		       XmNpaneMinimum, data->scrollbar_height,
		       XmNpaneMaximum, data->scrollbar_height, 0);

      /*
       * Now the size the scrollbar's slider.
       */
      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;
      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);
	    }
	}
    }
}


#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\
	<KeyPress>Return: ArmAndActivate()\n\
	<KeyPress>KP_Enter: ArmAndActivate()\n\
	Ctrl<KeyPress>m: 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[] = "\
<KeyPress>Return: ArmAndActivate()\n\
<KeyPress>KP_Enter: ArmAndActivate()\n\
Ctrl<KeyPress>m: 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[] = "<Btn2Down>: ";
#endif /* DND_KLUDGE */
/* sets the parent window to 0 to fool Motif into not generating a grab */
#ifdef USE_MOTIF
  /* The menu.font_list slot came from the *fontList resource (Motif standard.)
     The menu.font_list_2 slot came from the *font resource, for backward
     compatibility with older versions of this code, and consistency with the
     rest of emacs.  If both font and fontList are specified, we use font.
     If only one is specified, we use that.  If neither are specified, we
     use the "fallback" value.  What a kludge!!!

     Note that this has the bug that a more general wildcard like "*fontList:"
     will override a more specific resoure like "Emacs*menubar.font:".  But
     I can't think of a way around that.
   */
  if (mw->menu.font_list)	  /* if *fontList is specified, use that */
    ;
  else if (mw->menu.font_list_2)  /* else if *font is specified, use that */
    mw->menu.font_list = mw->menu.font_list_2;
  else				  /* otherwise use default */
    mw->menu.font_list = mw->menu.fallback_font_list;
#endif


/*
 *    This is a horrible function which should not be needed.
 *    use it to put the resize method back the way the XlwMenu
 *    class initializer put it. Motif screws with this when
 *    the XlwMenu class gets instantiated.
 */
void
xlw_unmunge_class_resize(Widget w) {
  if (w->core.widget_class->core_class.resize != XlwMenuResize)
    w->core.widget_class->core_class.resize = XlwMenuResize;
}