marbru@Central (Martin Brunecky) (03/21/90)
Submitted-by: sunpeaks!auto-trol!marbru@Central (Martin Brunecky) Posting-number: Volume 6, Issue 39 Archive-name: wsxc/part01 WsXc - POOR MAN'S UIL This posting contains WsXc, an Xt Intrinsic extension alowing to specify the entire user interface within X resource database. A HelloWorld example using Motif widgets is included. It is provided on as-is basis, without any support or waranties, to gain feedback about this approach of user interface definition. CONTENTS: o README Basic description of WsXc facility This is neither a tutorial, nor a reference manual.For more details about individual functions, plese refer to the source - it contains detailed description of each function. I am sorry I can't provide man pages nor Makefile, I just don't know how. The following files contain the code, based on R3 Xt Intrinsics, tested on SPARC, Ultrix/RISC and VAX/VMS. I assume compatibility with R4, even though it has not been tested. o WsCreateXrm.h defines available public functions o WsCreateXrmFunc.c widget tree creation and control o WsCvtStrToCallback.c string to callback resource convertor The following is a demo application, using the code listed above, and Motif (1.0). You can modify the code to use your favorite widget set, and play with application resource files. o HelloWorld.c sample application o HelloWorldBasic basic X resource file, move to ~/HelloWorld o HelloWorldMore an expanded X resource file #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: README WsCreateXrm.h WsCreateXrmFunc.c # WsCvtStrToCallback.c HelloWorld.c HelloWorld_Basic HelloWorld_More # Wrapped by marbru@auto-trol on Fri Mar 16 19:31:21 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(9584 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' X X UIL or NOT to UIL ? X X A few weeks ago, comp.windows.x carried a discussion of the X advantages and disadvantages of UIL. Some people suggested that UIL X is essentially useless, that the X resource database is enough, X while others have pointed out that there are things you cannot do X with the database alone, such as definition of a widget tree, X callbacks, compound strings etc. X X Prompted by this discussion, I have put together some code which, in X my belief, extends the the X Resource Database so that an ENTIRE X USER INTERFACE can be defined (and customized) within the database. X This avoidins the need for multiple sources of user interface X definition by replacing UIL. I am posting an overview of this "POOR X MAN'S UIL" here to get some feedback. X X To start, let me present a simple example of an application-class X X resource file for a Motif based HelloWorld: X X # X # shell - an application shell, containing RowColumn organizer X # X HelloWorld.managed.child_0: box,xmRowColumn X # X # box - the main container, contains label and button X # X Helloworld.box.spacing: 8 X HelloWorld.box.managed.child_0: label,xmLabel X HelloWorld.box.managed.child_1: button,xmPushButton X # X # label X # X HelloWorld.box.label.labelString: Hello, WORLD ! X # X # button X # X HelloWorld.box.button.labelString: Push ME X HelloWorld.box.button.activateCallback: push(Push again to EXIT) X # X X Except for the top-level shell creation, there is NO widget creation X code in my HelloWorld. The entire widget tree definition is shown X above. For any widget, I can specify any number of children with X all their resources, and all recursively. Callbacks are specified X in a similar way, by passing a string argument as call data. Note X the example is a starting point: It does NOT show all the X functionality such as deffered subtree creation, manage/unmanage X callbacks, etc... X X X APPLICATION CODE IMPACT X X A runtime interpreter must be able to translate X resource database X strings into widget classes (or widget creation routines), and X callbacks. Even though some systems (VMS for example) allow dynamic X binding, the current implementation uses registration routines: X X WsRegisterObjectClass ( app, "xmlabel", xmLabel ); X WsRegisterObjectClass ( app, "xmpushbutton", xmPushButton ); X WsRegisterConstructor ( app, "xmRowColumn", XmCreateRowColumn ); X X WsRegisterCallback ( app, "push", pushCB, NULL ); X X In environments supporting shareable images, all toolkit classes and X constructors may be registered during toolkit initialization. X X To initiate creation of the widget tree from the definitions stored X in the X resource database, an application must (either directly, or X by means of a callback) invoke the routine: X X WsCreateXrmChildren ( widget ); X X X WIDGET TREE CREATION MECHANISM X X The WsCreateXrmChildren routine scans the X resource database for X widget subresources in the following format: X X path...widget.[un]managed.child_n: name,class[,nonrecursive] X X For each such subresource, the routine creates a child as specified X by the name and object class (or the creation routine - X constructor). Creation recursively descends the widget tree, unless X stopped by a non-composite widget/object or the "nonrecursive" X option. The latter may be used to defer sub-tree creation, using a X callback which invokes WsCreateXrmChildren (for example, X WsCreateXrmChildrenCB). X X When a child is created, WsCreateXrmChildren checks the resource X database for xrmCreateCallback resource for the child, and executes X such callbacks if present. This mechanism allows the delivery of X child's widget ID to other, already existing widgets (such as a X Motif defaultButton resource in dialog boxes). X X X CALLBACK STRING CONVERSION X X A string to callback converter is provided with the package. The X converter builds an XtCallbackList using registered callback names. X Any callback on the list may have an optional string argument. A X pointer to the string is used as callback client data. If no X argument (string) is provided, the default client data value X provided at registration time is used. The callback resource X specification in the X resource database has the following format: X X path...widget.callbackName: name[(args)][,name[(args)]]... X X where name is the callback name assigned by WsRegisterCallback, and X args presents an arbitrary string. X X X WIDGET TREE CONTROL CALLBACKS X X The package provides basic callbacks for widget tree control. The X callbacks take a (list of) widget names as "client data". The X widget name is qualified according to Xrm rules: box.label. The X following is list of the provided callbacks: X X o CreateXrmChildrenCB ( widgetName, widgetname, ... ) X creates children of the named widgets as specified in the X X resource database X X o ManageNamedChildrenCB ( widgetName, widgetName, ... ) X manages a (list of) named widget(s) X X o UnmanageNamedChildrenCB ( widgetName, widgetName, ... ) X unmanages a (list of) named widget(s). X X o SetWidgetResourceCB ( resourceName, widgetname, ... ) X sets the specified resource in a (list of) named widget(s) to X the widget id of the widget invoking the callback. X X Consider the callbacks above as a starting point. More callbacks X can be provided to control popup/popdown, to load additional X resource files and much more. X X X COMPARISON WITH UIL X X The X resource database user interface definition, here (for lack of X better names) referred to as WsXc, performs essentialy the same X function as UIL. A complete comparison with UIL can not be done X without additional input. Here I try only to mention several X important differences between UIL and WsXc. X X Implementation: X A UIL application uses multiple user interface definition sources X (application code, UIL file, compiled UID file and an X resource X file). WsXc requires only application code and an X resource file, X and the application code would be limited to callback functions. X The UIL approach is based on a compiler generating intermediate code X which is interpreted by Mrm at runtime. WsXc is purely a runtime X interpreter. X X Performance: X Since UIL uses pre-compiled, machine specific data, the widget tree X creation could be faster than that for WsXc. However, even UIL X widget creation accesses the X resource database for resources NOT X explicitly specified by the UIL file. Since MOST resources are X usually NOT explictly specified, the overhead depends more on the X Xrm database volume, than on the widget creation method used. X Preliminary experience with WsXc is favorable. However, final X X Page 4 X X X judgement requires much more experience than is currently available. X X Extensibility. X Adding new widgets to UIL, even with the new Motif WML facility, is X not an easy process. Adding new data types (resource representation X types) to UIL is sometimes impossible. On the contarry, there is X nothing special about adding additional widgets to WsXc. The same X method also applies to adding new data types. The only requirement X is the addition of a convertor from string to a particular data X type. X X Syntax Checking: X The UIL compiler can perform rigorous syntax checking for widget X resources, thus assisting in user interface development. WsXc can X not catch any syntax errors in resource pathname specification, such X resources are simply ignored. However, errors in resource value X specification can be caught by the resource converter. In addition, X a simple tool that acquires a widget's resource list and performs X X resource file syntax checking can be provided. X X Value Computations: X The UIL compiler can compute the geometry of individual widgets X using arbitrary arithmetic expressions. Geometry values in the X X resource database can not, currently, contain expressions. But, X since Xrm uses cpp, a string substitution could be applied. This X limitation is a resource converter issue. A more intelligent string X to integer converter could evaluate arithmetic expressions, X including X resource database value substitution. Besides, geometry X configuration should be left to the geometry manager widgets and not X hardcoded. X X Resource Conversions: X UIL supports resource conversions such as colors, pixelmaps and X compound strings. Many of the conversions are performed at runtime, X using resource converters, the same as WsXc. For some resources, X such as Compound Strings, UIL compile time conversion provides some X runtime savings. In addition, the current string to compound string X resource converters are not intelligent enough to allow an unlimited X compound string specification in an X resource file. X END_OF_FILE if test 9584 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'WsCreateXrm.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'WsCreateXrm.h'\" else echo shar: Extracting \"'WsCreateXrm.h'\" \(1153 characters\) sed "s/^X//" >'WsCreateXrm.h' <<'END_OF_FILE' X/* X******************************************************************************* X* X* SCCS_data: %Z%%M% %I%(%G%) X* X* Include_name: X* X* WsCreateXrm.h X* X* Subsystem_group: X* X* Window System X* X* Related_keywords: X* X* Public Defines X* X* Include_description: X* X* Public defines for the Window System widget tree creation from X* the Xrm database. X* X* Include_history: X* X* mm/dd/yy initials action X* -------- -------- ------------------------------------------------------- X* 03/02/90 marbru created X* X******************************************************************************* X*/ X#ifndef _WsCreateXrm_h X#define _WsCreateXrm_h X X X/* -- Widget constructor registration routine */ X Xextern void WsRegisterObjectClass (); Xextern void WsRegisterConstructor (); Xextern void WsRegisterCallback (); Xextern void WsRegisterXrmCallbacks(); X X/* -- Widget creation routine */ X Xextern void WsCreateXrmChildren (); X X/* -- Convenience callbacks */ X Xextern void WsCreateXrmChildrenCB(); Xextern void WsManageNamedChildrenCB(); Xextern void WsUnmanageNamedChildrenCB(); Xextern void WsSetWidgetResourceCB(); X X#endif /* _WsCreateXrm_h */ END_OF_FILE if test 1153 -ne `wc -c <'WsCreateXrm.h'`; then echo shar: \"'WsCreateXrm.h'\" unpacked with wrong size! fi # end of 'WsCreateXrm.h' fi if test -f 'WsCreateXrmFunc.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'WsCreateXrmFunc.c'\" else echo shar: Extracting \"'WsCreateXrmFunc.c'\" \(22340 characters\) sed "s/^X//" >'WsCreateXrmFunc.c' <<'END_OF_FILE' X/* X******************************************************************************* X* X* SCCS_data: %Z%%M% %I%(%G%) X* X* Module_name: X* X* WsCreateXrmFunc.c X* X* Subsystem_group: X* X* Window System, Widget tree creation from Xrm database X* X* Related_keywords: X* X* Widget, Creation X* X* Module_description: X* X* This module contains the functions and convenience callbacks X* used to create and manage a widget tree using the Xrm databse. X* The Xrm database format used to define widget's children is X* as follows: X* X* toplevel...widget.managed.child_n: name,constructor[,n[onrecursive]] X* toplevel...widget.unmanged.child_n: name,constructor[,n[onrecursive]] X* X* Example: X* helloWorld.managed.child_0: box,XmCreateRowColumn X* helloWorld.box.managed.child_0: label,XmCreateLabel X* helloWorld.box.managed.child_1: button,XmCreatePushButton X* helloWorld.box.label.labelString: Hello, World ! X* helloWorld.box.button.labelString: Bye Bye, World ! X* helloworld.box.button.activateCallback: push(EXIT) X* X* Since (for portability reasons) we can not assume runtime binding, X* all widget classes or creation routines (constructors) must be X* "registered" by the application BEFORE widget tree creation. X* X* The widget tree creation is performed by the WsCreateXrmChildren() X* function, which descends the widget tree recursively until no more X* children are found, or widget creation is flagged as "nonrecursive", X* or a non-composite widget/object is found. X* X* Several convenience callbacks are provided here, more will probbably X* follow. X* X* Module_interface_summary: X* X* X* Xt Widget/Object Class Registration Routine: X* X* WsRegisterObjectClass( X* WsAppContext app, - application context X* String name, - class name, case insensitive X* WidgetClass class ) - class record pointer X* X* X* Xt Widget/Object Contructor Registration Routine: X* X* WsRegisterConstructor( X* WsAppContext app, - application context X* String name, - constructor name, case insens. X* (*Widget)() const ) - constructor function pointer X* X* X* Standard callback registration routine (all the following callbacks) X* X* WsRegisterXrmCallbacks ( X* WsAppContext app ) - application context X* X* Convenience Callbacks: X* X* WsCreateXrmChildrenCB () - creates children for named widgets X* WsManageNamedChildrenCB () - manages named widgets X* WsUnmanageNamedChidrenCB() - unmanages named widgets X* WsSetWidgetResourceCB () - sets widget resource in named widgets X* WsLoadResourceFileCB () - loads a new database file X* X* X* Module_history: X* X* mm/dd/yy initials function action X* -------- -------- -------- --------------------------------------------- X* 02/26/90 MarBru All Created X* 02/16/90 MarBru Create.. Limited creation to composite widgets/objects X* X* Design_notes: X X* For VMS, we could have used LIB$FIND_IMAGE_SYMBOL and use dynamic X* (runtime) binding. But since most UNIX systems lack such capability, X* we stick to the concept of "registration" routines. X* X******************************************************************************* X*/ X/* X******************************************************************************* X* Include_files. X******************************************************************************* X*/ X X/* -- Operating system includes */ X#include <strings.h> X#include <ctype.h> X X/* -- X Window System includes */ X#include <X11/IntrinsicP.h> X#include <X11/StringDefs.h> X X/* -- Auto-trol Window System includes */ X X/* X******************************************************************************* X* Private_constant_declarations. X******************************************************************************* X*/ X#undef NUL X#define NUL '\0' X#define MAX_XRMSTRING 1024 /* max length of the Xrm DB string */ X#define MAX_ERRMSG 1024 /* max length of error message */ X#define MAX_CHILDREN 1024 /* max number of widget's children */ X#define ADD_CLASSES 16 /* increment of class cache */ X X#define WsNxrmCreateCallback "xrmCreateCallback" X#define WsCXrmCreateCallback "XrmCreateCallback" X X/* X******************************************************************************* X* Private_type_declarations. X******************************************************************************* X Class/constructor cache record contains both class and constructor, X one of which must be NULL. X*/ Xtypedef struct /* Class cache record */ X{ X XrmQuark quark; /* quarkified callback name */ X Widget (*constructor)(); /* constructor function ptr */ X WidgetClass class; /* widget class pointer */ X} ClCacheRec; X X/* X******************************************************************************* X* Private_macro_definitions. X******************************************************************************* X*/ X X/* X******************************************************************************* X* Private_data_definitions. X******************************************************************************* X The following cache/registry of known widget classes and contructors, X initially empty, are loaded by the application using "registration" X routines. X Assuming small numbers of constructors, the sequential search X of such cache is (initially) considered acceptable. X*/ X X/* -- Named object classes cache, intially empty */ X Xstatic int classes_num = 0; Xstatic int classes_max = 0; Xstatic ClCacheRec *classes_ptr = NULL; X X/* X******************************************************************************* X* Private_function_declarations. X******************************************************************************* X*/ X X X/* X -- Names to Widget List X****************************************************************************** X This routine converts a string of comma separated widget names X (or widget pathes) into a list of widget id's. Blank space ignored X If a NULL string is provided, the widget ID of reference widget X is put on the list X The widget search starts at the TOP of widget hierarchy (using the X top level shell as a reference widget), thus the widget pathname X must include all the widget parents (excluding the shell). X*/ Xstatic void NamesToWidgetList ( w, client, widget_list, widget_count ) XWidget w; /* reference widget */ Xcaddr_t client; /* callback client data - string of names */ XWidget *widget_list; /* returned widget list */ XCardinal *widget_count; /* returned widget count */ X{ X char *string = (String)client; X char name[MAX_XRMSTRING]; X register char *s; X register char *d; X X/* -- default case, no string provided, return the calling widget */ X if (!string) X { X widget_list[0] = w; X *widget_count = 1; X return; X } X X/* -- find the reference widget as the toplevel shell */ X while ( XtParent(w) ) w = XtParent(w); X X/* -- parse the input string "name.name,name.name,name.name.name" */ X *widget_count = 0; X for ( d = name, s = string; ; s++ ) X { X if ( *s == ',' || *s == NUL ) X { X Widget widget; X if ( *s == NUL && d == name ) return; X *d = NUL; X widget = XtNameToWidget ( w, name ); X if ( widget ) X { X widget_list[*widget_count] = widget; X (*widget_count)++; X } X else X XtStringConversionWarning (name, "Widget"); X d = name; X } X else if ( *s > ' ' ) X { X *d++ = *s; X } X else X ; X } X} X/* X -- Call Create Callback X******************************************************************************* X This function calls the CreateCallback defined for the widget in Xrm X resource database. X Note the xrmCreateCallback "resource" is NOT a true widget resource, X there are no instance data associated with it, and only exists in the X Xrm resource database, used ONLY at widget creation time. X*/ Xstatic Widget CallCreateCallback ( w ) XWidget w; /* child's parent */ X{ X static XtResource res[] = X { X { WsNxrmCreateCallback, WsCXrmCreateCallback, XtRCallback, X sizeof(XtCallbackList), 0, XtRImmediate, (caddr_t)NULL X }, X }; X XtCallbackList callback = NULL; X X XtGetApplicationResources ( w, &callback, res, XtNumber(res), NULL, 0 ); X X /* If there was any callback list defined, invoke all callbacks on list */ X if ( callback ) X { X XtCallbackRec *cb = callback; X for ( cb = callback; cb->callback; cb++ ) X (*cb->callback) ( w, cb->closure, NULL ); X X } X} X X/* X -- Create Database Child X******************************************************************************* X This function checks the resource database for a presence of widget's X subresource in a form: X X ...widget.type.child_nn: name.constr_name[,n[onrecursive]] X X where type is either "managed" or "unmanaged". X X If such a resource is present, the child is created and, if nonrecursive X option is NOT present, the CreateDatabseChildren is called for this X child causing recursive tree creation. Creation stops if child_0 or X two subsequent children are not defined. Creation also stops at any X non-composite widget/object. Thus, popup-shells etc. must be created X as manager children. X X*/ Xstatic Widget CreateDatabaseChild ( w, nn, type ) XWidget w; /* child's parent */ Xint nn; /* child # to look for */ XString type; /* child type: managed or unmanaged */ X{ X static XtResource c_resource[] = X { X { NULL, NULL, XtRString, sizeof(String), 0, XtRImmediate, (caddr_t)NULL }, X }; X Boolean recursive; X char res_name[20]; X String string; X X /* update our resource list to look for type.child_n subresource */ X sprintf ( res_name, "child_%d", nn ); X /* toolkit quarkifies resource lists, (flagged by negative offset) */ X if ( ((int)c_resource[0].resource_offset) < 0 ) X { X XrmQuark qname = XrmStringToQuark(res_name); X c_resource[0].resource_name = (String)qname; X c_resource[0].resource_class = (String)qname; /* no class used */ X } X else X { X c_resource[0].resource_name = res_name; X c_resource[0].resource_class = res_name; /* no class allowed / used */ X } X XtGetSubresources ( w, &string, type, type, c_resource, 1, NULL, 0 ); X X /* Xrm query returned a string for [un]managed.child_n resource, process */ X if ( string ) X { X void WsCreateXrmChildren(); X XrmQuark quark; X Widget (*found_const)() = NULL; X WidgetClass found_class = NULL; X char name [MAX_XRMSTRING]; X char constr[MAX_XRMSTRING]; X register char *s; X register char *d; X register int i; X X /* extract widget name */ X for ( d=name, s=string; (*s && *s!=','); ) X *d++ = *s++; X *d = NUL; X X /* check for missing class/constructor name */ X if ( *s != ',' ) X { X char msg [MAX_ERRMSG]; X sprintf ( msg, X "Resource db error, missing constructor specifier for %s", name ); X XtWarning( msg ); X return (Widget)NULL; X } X s++; X X /* extract class/constructor name and force lowercase, no white space */ X for ( d=constr; (*s && *s!=','); ) X if (*s > ' ') X *d++ = (isupper(*s)) ? tolower(*s++) : *s++; X else X s++; X *d = NUL; X X /* check for non-recursive option */ X recursive = ( *s==',' && (s[1]=='n' || s[1]=='N') ) ? FALSE : TRUE; X X X /* try to locate class/constructor in our caches */ X quark = XrmStringToQuark ( constr ); X for (i=0; i<classes_num; i++) X if ( classes_ptr[i].quark == quark ) X { X found_class = classes_ptr[i].class; X found_const = classes_ptr[i].constructor; X break; X } X X /* if we'w found a class or constructor, create child, call callback */ X if (found_class || found_const ) X { X Widget child; X if ( found_class ) X child = XtCreateWidget ( name, found_class, w, NULL, 0 ); X else X child = (*found_const) ( w, name, NULL, 0 ); X X CallCreateCallback ( child ); X if ( recursive ) WsCreateXrmChildren ( child ); X return (child); X } X else X { X char msg[MAX_ERRMSG]; X sprintf ( msg,"Cannot create child %s using %s, unknown class/constructor", X name, constr ); X XtWarning( msg ); X return (Widget)NULL; X } X } X else X { X return (Widget)NULL; X } X} X X/* X******************************************************************************* X* Public_function_declarations. X******************************************************************************* X*/ X/* X -- Register Object Class X******************************************************************************* X This procedure adds object class name to our list of registered X classes/constructors. So far very simplistic ... without checking for X duplicate entries, no cache hashing scheme....no ties to app_context. X*/ Xvoid WsRegisterObjectClass ( app, name, class ) XXtAppContext app; /* not used (yet), must be present */ XString name; /* constructor name, case insensitive */ XWidgetClass class; /* Xt object class pointer */ X{ X char cr_name[MAX_XRMSTRING]; X register char *s; X register char *d; X X for ( d=cr_name, s=name; *s; s++) X *d++ = (isupper(*s)) ? tolower (*s) : *s; X *d = '\0'; X X if (classes_num >= classes_max ) X { X classes_max += ADD_CLASSES; X classes_ptr = (ClCacheRec*) XtRealloc((char*)classes_ptr, X sizeof(ClCacheRec) * classes_max); X } X classes_ptr[classes_num].quark = XrmStringToQuark ( cr_name ); X classes_ptr[classes_num].constructor = NULL; X classes_ptr[classes_num].class = class; X classes_num++; X} X X/* X -- Register constructor X******************************************************************************* X This procedure adds constructor procedure/name to our list of registered X classes/constructors. So far very simplistic ... without checking for X duplicate entries, no cache hashing scheme....no ties to app_context. X X Note the constructor is a "standard" widget creation routine X Widget WsCreateXyyyZyyy ( parent, name, args, nargs ) X*/ Xvoid WsRegisterConstructor ( app, name, constructor ) XXtAppContext app; /* not used (yet), must be present */ XString name; /* constructor name, case insensitive */ XWidget (*constructor) (); /* pointer to a widget creation routine */ X{ X char cr_name[MAX_XRMSTRING]; X register char *s; X register char *d; X X for ( d=cr_name, s=name; *s; s++) X *d++ = (isupper(*s)) ? tolower (*s) : *s; X *d = '\0'; X X if (classes_num >= classes_max ) X { X classes_max += ADD_CLASSES; X classes_ptr = (ClCacheRec*) XtRealloc((char*)classes_ptr, X sizeof(ClCacheRec) * classes_max); X } X classes_ptr[classes_num].quark = XrmStringToQuark ( cr_name ); X classes_ptr[classes_num].constructor = constructor; X classes_ptr[classes_num].class = NULL; X classes_num++; X} X X/* X -- Create Xrm Database Children X******************************************************************************* X This routine creates widget children as defined in X reosurce databse. X We look for children defintion starting at child 0 (first looking X for managed, then unmanaged child). The child lookup terminates if X child_0 is not present, or two two subsequent child lookups failed X (allowing to continue if one child was defined incorrectly). X X To reduce the databse search overhead, we only attempt to create X children for composite widgets and objects. X*/ Xvoid WsCreateXrmChildren ( w ) XWidget w; X{ X Widget child; X Widget prev; X Widget managed[MAX_CHILDREN]; X register int i,num; X X/* -- check if the requested widget is a manager ( composite ) */ X if (! ( XtIsSubclass( w, compositeWidgetClass ) X || XtIsSubclass( w, compositeObjectClass ) /* not in R3 intrinsics */ X ) ) return; X X X/* -- look for children definition until we have 2 subsequent misses */ X child = (Widget) 1; X prev = (Widget) 0; /* this makes the child_0 mandatory */ X X for ( i=0, num=0; (prev || child) ;i++) X { X child = CreateDatabaseChild ( w, i, "managed" ); X if ( child ) X managed[num++] = child; X else X child = CreateDatabaseChild ( w, i, "unmanaged" ); X prev = child; X } X XtManageChildren(managed, num ); X} X X X/* X -- Create Xrm Children callback X******************************************************************************* X X For each widget specified by the list of widget names in client data, X (or the widget invoking this callback if client data is NULL), this X callback creates any children defined in the Xrm database: X X ....widget.managed.child_0: name,constr_name[,n[onrecursive]] X ....widget.unmanaged.child_0: name,constr_name[,n[onrecursive]] X X If resource above (child_0 - child_nn) exists, the callback creates X (and optionally manages) a child using a constructor routine registered X under "constr_name". X The creation process recursivly follows the widget tree, unless X the constructor is specified with the "nonrecursive" option, or X a non-composite widget/object is found. X X The search for "child_n" resource stops if two subsequent children X or child_0 are not specified, or their creation fails (error). X X After all children have been created, the string of widget names X is changed to an empty one ("\0"), to prevent duplicate widget X creation. X X*/ Xvoid WsCreateXrmChildrenCB ( w, client, call ) XWidget w; Xcaddr_t client; /* client data, list of named children */ Xcaddr_t call; /* call data, not used */ X{ X Widget* widget_list[MAX_CHILDREN]; X Cardinal widget_count; X int i; X X X /* for NULL client, we get back "w", for "\0" we get zero widget count */ X NamesToWidgetList ( w, client, widget_list, &widget_count ); X X for ( i=0; i<widget_count; i++) X WsCreateXrmChildren(widget_list[i]); X X /* prevent repeated invokation by changing string to empty "\0" one */ X if ( client ) X *client = NUL; X} X X/* X -- Manage named children callback X******************************************************************************* X This callback translates string passed in as client data into a widget id X and manages it. A comma separated list of children can be specified. X NULL string pointer defaults widget invoking the callback X*/ Xvoid WsManageNamedChildrenCB ( w, client, call ) XWidget w; Xcaddr_t client; /* client data, list of named children */ Xcaddr_t call; /* call data, not used */ X{ X Widget* widget_list[MAX_CHILDREN]; X Cardinal widget_count; X X NamesToWidgetList ( w, client, widget_list, &widget_count ); X XtManageChildren ( widget_list, widget_count ); X} X X X/* X -- Unmanage named children callback X******************************************************************************* X This callback translates string passed in as client data into a widget id X and manages it. A comma separated list of children can be specified. X NULL string pointer defaults widget invoking the callback X*/ Xvoid WsUnmanageNamedChildrenCB ( w, client, call ) XWidget w; Xcaddr_t client; /* client data, list of named children */ Xcaddr_t call; /* call data, not used */ X{ X Widget* widget_list[MAX_CHILDREN]; X Cardinal widget_count; X X NamesToWidgetList ( w, client, widget_list, &widget_count ); X XtUnmanageChildren ( widget_list, widget_count ); X} X X X/* X -- Set Widget Resource Callback X******************************************************************************* X This callback loads invoking widget into the prescribed resource of the X named widget(s). X Typically, this callback is used to set the "XmNdefaultButton" resource, X and is invoked from button creation callback. X X The client data argument has a format: X X resource_name,target_widget_name[,target_widget_name...] X*/ Xvoid WsSetWidgetResourceCB ( w, client, call ) XWidget w; Xcaddr_t client; /* client data, resource name, list of named children */ Xcaddr_t call; /* call data, not used */ X{ X Widget* widget_list[MAX_CHILDREN]; X Cardinal widget_count; X char resource[MAX_XRMSTRING]; X register char *d,*s; X register int i; X Arg args[1]; X X for ( d=resource, s=client; (*s && *s!=','); s++ ) X if (*s > ' ') *d++ = *s; X *d = NUL; X X if (*s == ',' ) s++; X if (*s == NUL ) X { X XtWarning("No widget names for WsSetWidgetresourceCB"); X return; X } X NamesToWidgetList ( w, s, widget_list, &widget_count ); X X /* set the resource to set */ X args[0].name = resource; X args[0].value = (XtArgVal)w; X X for (i=0; i<widget_count; i++) X XtSetValues ( widget_list[i], args, 1 ); X} X X/* X -- Load Resource File X******************************************************************************* X This callbacks loads the specified resource file into application X resource database. It allows to load the resources on as-needed X basis, reducing the intitial resource load overhead. X X Two locations are searched for a specified file: X X XAPP_DEFAULT_PATH X XUSER_DEFAULT_PATH (or env.variable "XAPPLRESLANGPATH") X X Not implemented -- too toolkit dependent ( R3/Motif/R4 ). X*/ Xvoid WsLoadResourceFileCB ( w, client, call ) XWidget w; Xcaddr_t client; /* client data, X resources file name */ Xcaddr_t call; /* call data, not used */ X{ X printf("Sorry, deffered resource load not implemented, file %s\n",call); X printf("Merge your file into application class file\n"); X} X X/* X -- WsRegisterXrmCallbacks X******************************************************************************* X*/ Xvoid WsRegisterXrmCallbacks ( app ) XXtAppContext app; X{ X#define REG( name, cb, cl ) WsRegisterCallback ( app, name, cb, cl ) X X REG("CreateXrmChildrenCB", WsCreateXrmChildrenCB, NULL ); X REG("ManageNamedChildrenCB", WsManageNamedChildrenCB, NULL ); X REG("UnmanageNamedChildrenCB", WsUnmanageNamedChildrenCB, NULL ); X REG("SetWidgetResourceCB", WsSetWidgetResourceCB, NULL ); X X#undef REG X} END_OF_FILE if test 22340 -ne `wc -c <'WsCreateXrmFunc.c'`; then echo shar: \"'WsCreateXrmFunc.c'\" unpacked with wrong size! fi # end of 'WsCreateXrmFunc.c' fi if test -f 'WsCvtStrToCallback.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'WsCvtStrToCallback.c'\" else echo shar: Extracting \"'WsCvtStrToCallback.c'\" \(10320 characters\) sed "s/^X//" >'WsCvtStrToCallback.c' <<'END_OF_FILE' X/* X******************************************************************************* X* X* SCCS_data: %Z%%M% %I%(%G%) X* X* Module_name: X* X* WsCvtStrToCallback X* X* Subsystem_group: X* X* Window System, Widget Set, Converters X* X* Related_keywords: X* X* Converter X* X* Module_description: X* X* This module contains the String To Callback X resource converter. X* X* The converter parses the resource string in the format: X* X* ...path: name[(args)][,name[(args)]]... X* X* where: name: specifies the registered callback function name X* args: specifies the string passed to a callback as X* "client data". X* X* Multiple callbacks can be specified for a single callback list X* resource. Any callbacks must be "registered" by the application X* prior converter invocation (.i.e.prior widget creation). X* If no "args" string is provided, the default "client data" X* specified at callback registration are used. X* X* Module_interface_summary: X* X* X* Resource converter is invoked indirectly by the toolkit. The X* converter is added to the toolkit by widgets calling X* WsAddStrToCallbackP() in the widget intialization code. X* X* To register application callbacks, use: X* X* WsRegisterCallback ( X* WsAppContext app, - application context X* String name, - register name, case insensitive X* Callback callback, - callback function pointer X* ClientData closure ) - default client data X* X* Module_history: X* X* mm/dd/yy initials function action X* -------- -------- -------- --------------------------------------------- X* 02/26/90 MarBru All Created X* X* Design_notes: X* X* For VMS, we could have used LIB$FIND_IMAGE_SYMBOL and use dynamic X* (runtime) binding. But since most UNIX systems lack such capability, X* we stick to the concept of "registration" routines. X* X******************************************************************************* X*/ X/* X******************************************************************************* X* Include_files. X******************************************************************************* X*/ X X/* -- Operating system includes */ X#include <strings.h> X#include <ctype.h> X X/* -- X Window System includes */ X#include <X11/IntrinsicP.h> X#include <X11/StringDefs.h> X X/* -- Auto-trol Window System includes */ X X/* X******************************************************************************* X* Private_constant_declarations. X******************************************************************************* X*/ X#undef NUL X#define NUL '\0' X X/* X******************************************************************************* X* Private_type_declarations. X******************************************************************************* X*/ X#define MAX_XRMSTRING 1024 /* max length of the DB string */ X#define MAX_CALLBACKS 64 /* max number of callbacks per list */ X#define ADD_CALLBACKS 16 /* increment of callback cache size */ X X/* X******************************************************************************* X* Private_macro_definitions. X******************************************************************************* X*/ X X/* X******************************************************************************* X* Private_data_definitions. X******************************************************************************* X The following cache/registry of known callbacks, initially empty, X is loaded by the application using "registration" routines. X Assuming small numbers of callbacks, the sequential search X of such cache is (initially) considered acceptable. X*/ X X/* -- Named callback procedures cache, intially empty */ X Xtypedef struct X{ X XrmQuark quark; /* quarkified callback name */ X XtCallbackProc callback; /* callback procedure pointer */ X caddr_t closure; /* default client data */ X} CBCacheRec; X X X Xstatic int callbacks_num = 0; Xstatic int callbacks_max = 0; Xstatic CBCacheRec *callbacks_ptr = NULL; X X X/* X******************************************************************************* X* Private_function_declarations. X******************************************************************************* X*/ X X/* X -- Convert String To Callback X******************************************************************************* X This conversion creates a callback list structure from the X resource X database string in format: X X name(arg),name(arg)..... X X Note "name" is not case sensitive, while "arg" may be - it is passed to X a callback as client data as a null terminated string (first level X parenthesis stripped off). X*/ Xvoid CvtStringToCallback (args, num_args, fromVal, toVal) X XXrmValue *args; XCardinal *num_args; XXrmValue *fromVal; XXrmValue *toVal; X{ X typedef struct X { X char *nsta,*nend; /* callback name start, end */ X char *asta,*aend; /* argument string start, end */ X } Segment; X X static XtCallbackRec *cb; X XtCallbackRec callback_list[MAX_CALLBACKS]; X int callback_num = 0; X String string = (char *) fromVal->addr; X Segment segs[MAX_CALLBACKS]; X Segment *seg; X register char *s; X register int i,ipar; X X/* -- assume error or undefined input argument */ X toVal->size = 0; X toVal->addr = (caddr_t) NULL; X if (s == NULL) return; X X/* -- parse input string finding segments "name(arg)" comma separated */ X ipar = 0; X seg = segs; X seg->nsta = string; X seg->nend = seg->asta = seg->aend = (char*)NULL; X X for ( s=string; *s; s++ ) X { X switch (*s) X { X case ',': if ( ipar > 0 ) break; /* commas in arguments ignored */ X if ( seg->nend == NULL ) seg->nend = s-1; /* no argument */ X seg++; /* start the next segment */ X seg->nsta = (s[1]) ? s+1 : (char*)NULL; X seg->nend = seg->asta = seg->aend = (char*)NULL; X break; X X case '(': if ( ipar++ == 0 ) { seg->nend = s-1; seg->asta = s+1; }; X break; X X case ')': if ( --ipar == 0 ) { seg->aend = s-1; }; X break; X deafult: ; X } X } X seg++; /* start the terminating segment */ X seg->nsta = (char*)NULL; X X if (ipar) X { X XtStringConversionWarning (string, "Callback, unbalanced parenthesis"); X return; X } X X X/* -- process individual callback string segments "name(arg)" */ X for( seg = segs; seg->nsta; seg++) X { X char cb_name[MAX_XRMSTRING]; X XtCallbackProc found = (XtCallbackProc)NULL; X XrmQuark quark; X register char *d; X register char *end; X X /* our callback cache names are case insensitive, no white space */ X for ( s=seg->nsta, d=cb_name; s<=seg->nend; ) X if ( *s > ' ') X *d++ = (isupper(*s) ) ? tolower (*s++) : *s++; X else X s++; X *d = NUL; X X /* try to locate callback in our cache of callbacks */ X quark = XrmStringToQuark (cb_name); X for (i=0; i<callbacks_num; i++) X if ( callbacks_ptr[i].quark == quark ) X { X register XtCallbackRec *rec = &callback_list[callback_num]; X rec->callback = found = callbacks_ptr[i].callback; X rec->closure = callbacks_ptr[i].closure; X break; X } X X /* we have found a registered callback, process arguments */ X if (found) X { X register char *arg; X register int alen; X register XtCallbackRec *rec = &callback_list[callback_num]; X X if ( seg->asta ) X { X alen = (int)seg->aend - (int)seg->asta +1; X arg = XtMalloc(alen+1); X strncpy ( arg, seg->asta, alen ); X arg[alen+1] = NUL; X rec->closure = (caddr_t)arg; X } X callback_num++; X } X else X { X XtStringConversionWarning (cb_name, "Callback, unknown callback name"); X } X } /* end for seg loop */ X X/* -- terminate the callback list */ X { X register XtCallbackRec *rec = &callback_list[callback_num]; X rec->callback = NULL; X rec->closure = NULL; X callback_num++; X } X X/* -- make a permanent copy of the new callback list, and return a pointer */ X cb = (XtCallbackRec*)XtMalloc( callback_num * sizeof (XtCallbackRec) ); X memcpy ( (char*)cb, (char*)callback_list, X callback_num * sizeof (XtCallbackRec)); X toVal->size = sizeof (XtCallbackRec*); X toVal->addr = (caddr_t)&cb; X} X X/* X******************************************************************************* X* Public_function_declarations. X******************************************************************************* X*/ X X/* X -- Add String To Callback Convertor X******************************************************************************* X*/ X Xvoid WsAddStringToCallbackP () X{ X static Boolean added = FALSE; X if ( !added ) X { X XtAddConverter (XtRString, X XtRCallback, X CvtStringToCallback, X (XtConvertArgList)NULL, X (Cardinal)0); X added = TRUE; X } X} X X X/* X -- Register callback X******************************************************************************* X This procedure adds callback procedure/name to our list of registered X callbacks. So far very simplistic ... without checking for duplicate X entries, no cache hashing scheme, no ties to app_context. X*/ Xvoid WsRegisterCallback ( app, name, callback, closure ) XXtAppContext app; /* not used (yet), must be present */ XString name; /* callback name, case insensitive */ XXtCallbackProc callback; /* callback function pointer */ Xcaddr_t closure; /* default closure (client data) */ X{ X char cb_name[MAX_XRMSTRING]; X register char *s; X register char *d; X X for ( d=cb_name, s=name; *s; s++) X *d++ = (isupper(*s)) ? tolower (*s) : *s; X *d = '\0'; X X if (callbacks_num >= callbacks_max ) X { X callbacks_max += ADD_CALLBACKS; X callbacks_ptr = (CBCacheRec*) XtRealloc((char*)callbacks_ptr, X sizeof(CBCacheRec) * callbacks_max); X } X callbacks_ptr[callbacks_num].quark = XrmStringToQuark ( cb_name ); X callbacks_ptr[callbacks_num].callback = callback; X callbacks_ptr[callbacks_num].closure = closure; X callbacks_num++; X} END_OF_FILE if test 10320 -ne `wc -c <'WsCvtStrToCallback.c'`; then echo shar: \"'WsCvtStrToCallback.c'\" unpacked with wrong size! fi # end of 'WsCvtStrToCallback.c' fi if test -f 'HelloWorld.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'HelloWorld.c'\" else echo shar: Extracting \"'HelloWorld.c'\" \(3743 characters\) sed "s/^X//" >'HelloWorld.c' <<'END_OF_FILE' X/* X******************************************************************************* X* HelloWorld.c X******************************************************************************* X This program demonstrates usage of the Xrm (X resource management) databse X for a widget tree definition and management. X There is very little code in this example, since the entire user interface X definition is stored in the Xrm database, preferably in the application X class resource file: ~/HelloWorld X X ATTC NOTE: This example does NOT use Ws, since it's intended for use X outside Auto-trol. X/* X******************************************************************************* X* Include_files. X******************************************************************************* X*/ X X#include <Xm/Xm.h> /* Motif public header file */ X#include <Xm/Label.h> /* Motif label widget */ X#include <Xm/PushB.h> /* Motif pushbutton widget */ X#include <Xm/BulletinB.h> /* Motif bulletin board widget */ X#include <Xm/RowColumn.h> /* Motif row column widget */ X#include "WsCreateXrm.h" /* Window System Xrm Creation routines */ X X/* X******************************************************************************* X* Application callback declaration (callbacks should be in a separate file) X******************************************************************************* X*/ Xvoid pushCB(); X X/* X******************************************************************************* X* MAIN function X******************************************************************************* X*/ X Xmain ( argc, argv ) Xint argc; Xchar **argv; X{ X Widget app_shellW; /* application shell widget */ X XtAppContext app; X X/* -- Intialize AWS creating the application shell */ X app_shellW = XtInitialize ( "helloWorld","HelloWorld", X NULL, 0, &argc, argv ); X app = XtWidgetToApplicationContext(app_shellW); X X/* -- Register the string to callback converter, used for a pushbutton */ X WsAddStringToCallbackP(); X X/* -- Register available Widget constructors */ X WsRegisterConstructor ( app, "xmLabel", XmCreateLabel ); X WsRegisterConstructor ( app, "xmPushButton", XmCreatePushButton); X WsRegisterConstructor ( app, "xmRowColumn", XmCreateRowColumn ); X WsRegisterObjectClass ( app, "xmBulletinBoard", xmBulletinBoardWidgetClass); X X/* -- Register available callbacks */ X WsRegisterXrmCallbacks( app ); X WsRegisterCallback ( app, "push", pushCB, NULL ); X X X/* -- Create children of the toplevel shell defined by the Xrm database */ X WsCreateXrmChildren ( app_shellW ); X X/* -- Realize the widget tree and enter the main application loop */ X XtRealizeWidget ( app_shellW ); X XtMainLoop ( ); X} X/* X******************************************************************************* X Application callbacks (should be in a separate file) X******************************************************************************* X*/ X/* X -- Push callback X******************************************************************************* X This callback is a state machine; the first invocation loads the text X specified as client data into XmNlabelString resource of invoking widget; X the next invocation exits the application. X*/ Xvoid pushCB ( w, client, call ) XWidget w; Xcaddr_t client; Xcaddr_t call; X{ X static Boolean first = TRUE; X X if ( first ) X { X static Arg setargs[] = { XmNlabelString, NULL }; X String text = ( client ) ? (String) client : "No text in Xrdb" ; X X setargs[0].value = (XtArgVal)XmStringCreateLtoR(text,XmSTRING_DEFAULT_CHARSET); X WsSetResources ( w, setargs, 1); X first = FALSE; X } X else X { X exit(); X } X} END_OF_FILE if test 3743 -ne `wc -c <'HelloWorld.c'`; then echo shar: \"'HelloWorld.c'\" unpacked with wrong size! fi # end of 'HelloWorld.c' fi if test -f 'HelloWorld_Basic' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'HelloWorld_Basic'\" else echo shar: Extracting \"'HelloWorld_Basic'\" \(851 characters\) sed "s/^X//" >'HelloWorld_Basic' <<'END_OF_FILE' X# HelloWorld - global resources X# X*fontList: -*-Helvetica-Medium-R-Normal--14* X# X# shell X# XHelloWorld.managed.child_0: box,xmRowColumn X# X# box - the main container X# XHelloWorld.box.spacing: 2 XHelloWorld.box.background: red XHelloWorld.box.marginWidth: 4 XHelloWorld.box.marginHeight: 4 XHelloWorld.box.entryAlignment: alignment_center XHelloWorld.box.managed.child_0: label,xmlabel XHelloWorld.box.managed.child_1: button,xmpushbutton X# X# label X# XHelloWorld.box.label.background: white XHelloWorld.box.label.foreground: blue XHelloWorld.box.label.labelString: Hello, WORLD ! X# X# button X# XHelloWorld.box.button.background: lightBlue XHelloWorld.box.button.foreground: white XHelloWorld.box.button.location: center XHelloWorld.box.button.labelString: Push ME XHelloWorld.box.button.activateCallback: push(Push to EXIT) X# END_OF_FILE if test 851 -ne `wc -c <'HelloWorld_Basic'`; then echo shar: \"'HelloWorld_Basic'\" unpacked with wrong size! fi # end of 'HelloWorld_Basic' fi if test -f 'HelloWorld_More' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'HelloWorld_More'\" else echo shar: Extracting \"'HelloWorld_More'\" \(2171 characters\) sed "s/^X//" >'HelloWorld_More' <<'END_OF_FILE' X# X# HelloWorld X# box main container X# lbl1 headline label - hello world X# box1 bulletin board for default button testing X# btn1 default button X# btn2 button controlling lbl2 X# btn3 button controlling lbl2 X# lbl2 footnote X# X# global resources X# X*fontList: -*-Helvetica-Medium-R-Normal--14* X# X# shell X# XHelloworld.input: true XHelloWorld.managed.child_0: box,xmRowColumn X# X# box - the main container X# XHelloWorld.box.layout: column XHelloWorld.box.spacing: 8 XHelloWorld.box.background: red XHelloWorld.box.marginWidth: 0 XHelloWorld.box.marginHeight: 0 XHelloWorld.box.entryAlignment: alignment_center XHelloWorld.box.managed.child_0: lbl1,xmLabel XHelloWorld.box.managed.child_1: box1,xmBulletinBoard XHelloWorld.box.managed.child_2: btn2,xmPushButton XHelloWorld.box.managed.child_3: btn3,xmPushButton XHelloWorld.box.managed.child_4: lbl2,xmLabel X# X# lbl1 X# XHelloWorld.box.lbl1.background: white XHelloWorld.box.lbl1.foreground: blue XHelloWorld.box.lbl1.labelString: Hello, WORLD ! X# X# box1 X# XHelloWorld.box.box1.marginWidth: 8 XHelloWorld.box.box1.marginHeight: 8 XHelloWorld.box.box1.managed.child_0: btn1,xmPushButton X# X# btn1 X# XHelloWorld.box.box1.btn1.labelString: Push ME XHelloWorld.box.box1.btn1.background: lightBlue XHelloWorld.box.box1.btn1.foreground: red XHelloWorld.box.box1.btn1.showAsDefault: 2 XHelloWorld.box.box1.btn1.xrmCreateCallback: SetWidgetResourceCB(defaultButton,box.box1) XHelloWorld.box.box1.btn1.activateCallback: push(Push to EXIT !) X# X# btn2 X# XHelloWorld.box.btn2.background: lightBlue XHelloWorld.box.btn2.foreground: blue XHelloWorld.box.btn2.labelString: Remove Footnote XHelloWorld.box.btn2.activateCallback: UnmanageNamedChildrenCB(box.lbl2) X# X# btn3 X# XHelloWorld.box.btn3.background: lightBlue XHelloWorld.box.btn3.foreground: blue XHelloWorld.box.btn3.labelString: Add Footnote XHelloWorld.box.btn3.activateCallback: ManageNamedChildrenCB(box.lbl2) X# X# lbl2 X# XHelloWorld.box.lbl2.background: white XHelloWorld.box.lbl2.foreground: blue XHelloWorld.box.lbl2.labelString: Ain't it WONDERFULL ? X# END_OF_FILE if test 2171 -ne `wc -c <'HelloWorld_More'`; then echo shar: \"'HelloWorld_More'\" unpacked with wrong size! fi # end of 'HelloWorld_More' fi echo shar: End of shell archive. exit 0 dan ----------------------------------------------------------- O'Reilly && Associates argv@sun.com / argv@ora.com 632 Petaluma Ave, Sebastopol, CA 95472 800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104 Opinions expressed reflect those of the author only.