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.