The Simple Resource Converter

by John Cwikla

Copyright © 1995,1996,1997,1998,1999,2000,2001,2002 John Cwikla, All Rights Reserved.

Have you ever added an X resource to your application and wanted to allow the user to be able to specify strings for values, like XmHORIZONTAL or XmPACK_TIGHT in Motif, but for a some non-standard value? In my column Single-Launching Your Application, I made a quick comment that I allow users to specify the values TRUE, FALSE, or QUERY for my singleLaunch resource. Obviously the standard String-to-Boolean converter does not understand the value QUERY, so it won't turn it into anything useful, and will complain if you try. This month I present a simple resource converter that you can almost cut-and-paste to get this functionality for your own programs.

Quick caveat -- I don't plan to delve into all the nuances of a resource converter, but instead will present a specific type of converter that you may find useful. For a more detailed look into resource converters, see the references section at the end of this column.

This example will center around the XmNsingleLaunch resource. The goal: to allow the user to have

*singleLaunch: TRUE

	or

*singleLanch: FALSE
	
	or

*singleLaunch: QUERY

and have them turn into the values 0, 1, or -1, respectively. This is the underlying feature of this converter, take a string and turn it into a #define'd number, or enumerated value -- just some integer value. In this case, I have a header file containing:

#define SL_FALSE 0
#define SL_TRUE 1
#define SL_QUERY -1

typedef int SingleLaunchType;

SingleLaunchType is defined so that we can guarantee we don't screw up sizes for our offset pointers.Next, in my application's resources, I need to add the singleLaunch resource:

	{XmNsingleLaunch, XmCSingleLaunch, XmRSingleLaunch, sizeof(SingleLaunchType),
		TheOffset(singleLaunch), XtRImmediate, (XtPointer)SL_FALSE},

(TheOffset is a macro that uses XtOffset() to get the resource offset in my application resource struct.)

XmNsingleLaunch, XmCSingleLaunch and XmRSingleLaunch are more #define's in a header file:

#define XmNsingleLaunch "singleLaunch"
#define XmCSingleLaunch "SingleLaunch"
#define XmRSingleLaunch "SingleLaunch"

XmRSingleLaunch is the field that tells the X resource conversion mechanism what type of converter to call when it encounters a resource with the name "singleLaunch" or the class name "SingleLaunch". A resource converter is registered with XtAppSetTypeConverter():

XtAppSetTypeConverter(yourAppContext, XtRString, XmRSingleLaunch, CvtStringToSingleLaunch,
	(XtConvertArgList)NULL, 0,
	XtCacheAll, (XtDestructor)NULL);

So we now have everything we need -- except the converter CvtStringToSingleLaunch().

static void _toLower(char *_str1, char *_str2, int _length)
{
	int i;
	char *ptr;

	for(ptr=_str1,i=0;(ptr!=NULL) && (i<_length);ptr++,i++)
		*(_str2+i) = tolower(*ptr);
}

_toLower() is an auxilliary function. Instead of relying on strncasecmp() begin available, I do the same type of comparison by hand.


Boolean CvtStringToSingleLaunch(Display *_display, XrmValuePtr _args,
	Cardinal *_numArgs, XrmValuePtr _from, XrmValuePtr _to, XtPointer *_data)
{
	char *lower;

/*
** This MUST be static.	The spec for converters says that we may have to provide
** static storage for the return value ourselves.	Plus this variable MUST be
** the same sizeof() as the 4th element in your XtResource array for this
** type of resource. 
*/

	static SingleLaunchType singleLaunch;
	Boolean badConversion = FALSE;

/*
** This resource converter should never receive any extra arguments.
*/

	if (*_numArgs != 0)
	{
		XtAppWarningMsg(XtDisplayToApplicationContext(_display), "CvtStringToSingleLaunch", "wrongParamaters",
		"ResourceError",
		"CvtStringToSingleLaunch needs no arguments.",
		(String *)NULL, (Cardinal *)NULL);
	}

/*
** The first step is to make our resource case insensitive. We get the "in" resource value
** in _from and will place the result in _to.
*/

	lower = XtNewString((char *)_from->addr);
	_toLower(_from->addr, lower, strlen((char *)_from->addr));

/*
** Set up the default value.
*/
	singleLaunch = FALSE;


/*
** Compare the string to our values.	This is the place where you would put
** the strings you would like to compare against.	If you wanted to add "bob"
** as a valid string for this converter, just add another "if" case.
*/

	if (!strncmp(lower, "true", 5))
		singleLaunch = TRUE;
	else
	if (!strncmp(lower, "false", 4))
		singleLaunch = FALSE;
	else
	if (!strncmp(lower, "query", 12))
		singleLaunch = SL_QUERY;
	else
		badConversion = TRUE; /* no match */

	XtFree(lower);

	if (badConversion) /* warn user bad value */
		XtDisplayStringConversionWarning(_display, _from->addr, XmRSingleLaunch);
	else /* 3 cases */
	{
		if (_to->addr == NULL)     /* we must provide static storage ourself */
			_to->addr = (XtPointer)&singleLaunch;
		else
		if (_to->size < sizeof(SingleLaunchType))    /* the size of the storage for thet 4th parameter is too small for our value */
		                                             /* ie, if you had sizeof(char) for the 4th element for singleLaunch */
			badConversion = TRUE;
		else
			*(SingleLaunchType *)_to->addr = singleLaunch;    /* storage is provided, just copy it in */
		_to->size = sizeof(SingleLaunchType); /* our return size */
	}

	return !badConversion;
}

That's it. Basically four steps:

And that's it, your first resource converter.


References

Asente, Paul J., and Swick, Ralph R., X Window System Toolkit, The Complete Programmer's Guide and Specification, Digital Press, 1990.

O'Reilly and Associates, Inc. X Toolkit Intrinsics Reference Manual, Second Edition, 1991.