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.
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.