/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include "DynamicResource.h" #include #include #include #include // // What's it do? // // Each object is a resource name,value pair and a list of widget names into // which to make that setting. Use installed XtTypeConverters in order to // accept string representations of resource values. Use Xt functions to // ensure that the resource name exists in the widget. Supply print and parse // functions in order to transfer things to and from files. // // // Supply the root widget and/or the resourceName or neither. // DynamicResource::DynamicResource(const char *resourceName, Widget root) { this->widgetNames = NUL(String *); this->nameCnt = 0; if ((resourceName)&&(resourceName[0])) { this->resourceName = new char [1+strlen(resourceName)]; strcpy (this->resourceName, resourceName); } else this->resourceName = NUL(char*); this->valid_resource = FALSE; this->stringRepresentation = NUL(char *); this->storedData = FALSE; this->setting.default_addr = 0; this->root = NUL(Widget); if (root) this->setRootWidget(root); } DynamicResource::~DynamicResource() { int i; if (this->valid_resource) { delete this->setting.resource_name; delete this->setting.resource_class; delete this->setting.resource_type; delete this->setting.default_type; // FIXME: The value in default_addr may point to something which // should be free-ed or deallocated in a special way i.e XmString. // delete this->setting->default_addr; } if (this->nameCnt) { for (i=0; inameCnt; i++) delete this->widgetNames[i]; delete this->widgetNames; } if (this->resourceName) delete this->resourceName; if (this->stringRepresentation) delete this->stringRepresentation; } //void DynamicResource::initialize() //{ // if (!DynamicResource::DynamicResourceClassInitialized) { // DynamicResource::DynamicResourceClassInitialized = TRUE; // } //} // // The root widget is changeable. Whenever it is set, run thru the list of // names, verifying that a widget has a resource and then setting the resource. // boolean DynamicResource::setRootWidget(Widget root) { int i; if (!root) { this->root = NUL(Widget); return TRUE; } if (!this->valid_resource) { Widget dest; boolean no_mistakes = TRUE; boolean any_found = FALSE; for (i=0; ((inameCnt) && (no_mistakes)); i++) { if (!(dest = XtNameToWidget (root, this->widgetNames[i]))) no_mistakes = FALSE; else no_mistakes = DynamicResource::VerifyPresence (this->resourceName, dest, &this->setting); if (no_mistakes) any_found = TRUE; } if ((any_found) && (no_mistakes)) this->valid_resource = TRUE; } if (this->root == root) return TRUE; this->root = root; // It's not bad for valid_resource to be FALSE here. It just means that we // haven't built up the widgetName list enough to have found the resource. if (!this->valid_resource) { return FALSE; } boolean applyOK=TRUE; for (i=0; ((inameCnt)&&(applyOK)); i++) applyOK = DynamicResource::applySetting (this->widgetNames[i]); if (!applyOK) { return FALSE; } return TRUE; } // // Produce a call to an installed resource converter. // Assumes that the data can be held in an XtPointer (an int I suspect). That will // include most things. For settings consisting of multiple bytes use // ::setDataDirectly. It doesn't use a converter. // boolean DynamicResource::setData (const char *value) { int i; if (!this->valid_resource) return FALSE; this->storedData = FALSE; if (this->stringRepresentation) delete this->stringRepresentation; this->stringRepresentation = new char[1+strlen(value)]; strcpy (this->stringRepresentation, value); // FIXME: The value in default_addr may point to something which // should be free-ed or deallocated in a special way i.e XmString. // delete this->setting->default_addr; this->setting.default_addr = (XtPointer)this->stringRepresentation; if (!this->root) return TRUE; boolean applyOK = TRUE; for (i=0; ((inameCnt)&&(applyOK)); i++) applyOK = DynamicResource::applySetting (this->widgetNames[i]); return applyOK; } boolean DynamicResource::setData () { int i; if (!this->valid_resource) return FALSE; if (!this->storedData) return FALSE; boolean applyOK=TRUE; for (i=0; ((inameCnt)&&(applyOK)); i++) applyOK = DynamicResource::applySetting (this->widgetNames[i]); return applyOK; } // // Don't attempt to use a resource converter. This would be for setting a pixmap // resource if you had already created the pixmap yourself or for setting the // text inside a text widget. // boolean DynamicResource::setDataDirectly (void *value) { this->storedData = TRUE; this->stringRepresentation = NUL(char*); this->setting.default_addr = (XtPointer)value; return this->setData(); } // // When adding a widget to the list, is it good to verify that // this->setting.resource_name is in the set of resources held by the // widget of interest? The name used will be ...XtName(XtParent(dest)).XtName(dest) // // If allIntermediates is set to TRUE, then automatically include all widgets // which are both children of root and ancestors of dest. This facilitates // setting colors resources in objects made of several widgets. // boolean DynamicResource::addWidgetToNameList (Widget dest, boolean allAncestors) { boolean applyOK=TRUE; if (this->inWidgetList (dest)) return TRUE; ASSERT(this->resourceName && this->resourceName[0]); if (!this->valid_resource) { this->valid_resource = DynamicResource::VerifyPresence (this->resourceName, dest, &this->setting); if (!this->valid_resource) return FALSE; } ASSERT(this->root); char *cp = DynamicResource::QualifiedWidgetName(this->root, dest); this->widgetNames = DynamicResource::GrowNameList (this->widgetNames, &this->nameCnt, cp); if ((this->storedData) || ((this->root)&&(this->stringRepresentation))) applyOK = this->applySetting (cp); if ((allAncestors)&&(applyOK)) { if (dest != this->root) { applyOK = this->addWidgetToNameList (XtParent(dest), allAncestors); } } delete cp; return applyOK; } // // Add dest and all decendants thereof. // ...used for making a setting once and have it trickle down automatically. // boolean DynamicResource::addWidgetsToNameList (Widget dest, boolean ) { Widget menu, *kids; int i, nkids; boolean applyOK=TRUE; if (!this->addWidgetToNameList (dest, FALSE)) return FALSE; if ((XtIsSubclass (dest, xmCascadeButtonWidgetClass))|| (XtIsSubclass (dest, xmCascadeButtonGadgetClass))) { menu = 0; XtVaGetValues (dest, XmNsubMenuId, &menu, NULL); if (!menu) return TRUE; applyOK = this->addWidgetsToNameList (menu, FALSE); } if (!XtIsComposite (dest)) return applyOK; XtVaGetValues (dest, XmNchildren, &kids, XmNnumChildren, &nkids, NULL); for (i=0; ((iaddWidgetsToNameList (kids[i], FALSE); } return applyOK; } // // Make the results of our investigations available. Memory is owned locally. // void * DynamicResource::getData() { if (!this->valid_resource) return 0 ; if (!this->storedData) return 0; return (void *)this->setting.default_addr; } // // Form the name w would get if it were added, then check the list of names // to see if that string is present. // boolean DynamicResource::inWidgetList (Widget w) { int i; if (!this->valid_resource) return FALSE; char *wname = DynamicResource::QualifiedWidgetName (this->root, w); for (i=0; inameCnt; i++) { if (!strcmp(this->widgetNames[i], wname)) return TRUE; } return FALSE; } // P R I N T A N D P A R S E P R I N T A N D P A R S E // P R I N T A N D P A R S E P R I N T A N D P A R S E // P R I N T A N D P A R S E P R I N T A N D P A R S E // P R I N T A N D P A R S E P R I N T A N D P A R S E // // %s.%s:%s\n, with widget name, resource name, and resource value // There is 1 resource spec per line. // boolean DynamicResource::printComment (FILE *f) { int i; if ((!this->valid_resource) && (this->root != NUL(Widget))) return TRUE; if (!this->stringRepresentation) return TRUE; for (i=0; inameCnt; i++) { if (fprintf (f, " // resource %s.%s:%s\n", this->widgetNames[i], this->resourceName, this->stringRepresentation) < 0) return FALSE; } return TRUE; } boolean DynamicResource::parseComment (const char *comment, const char * /* file */, int /* line */) { char *full_resource, *resource_value, *rname; int i; full_resource = new char[strlen(comment)]; resource_value = new char[strlen(comment)]; if (sscanf (comment, " resource %[^:]:%[^\n]", full_resource, resource_value) != 2) { delete full_resource; delete resource_value; return FALSE; } // break full_resource into widget name and resource name by pulling the // resource name off the end. int len = strlen(full_resource); rname = new char[1+len]; int j = 0; for (i=len-1; i>=0; i--) { if (full_resource[i] == '.') { full_resource[i] = '\0'; break; } rname[j++] = full_resource[i]; } rname[j] = '\0'; char *widgetName = new char[1+strlen(full_resource)]; strcpy (widgetName, full_resource); resourceName = new char[1+j]; int k = 0; for (i=j-1; i>=0; i--) resourceName[k++] = rname[i]; resourceName[k] = '\0'; // Now we have the XmN... into resourceName and the qualified widget name // in widgetName. Add them to this->widgetNames. this->resourceName = resourceName; this->widgetNames = DynamicResource::GrowNameList (this->widgetNames, &this->nameCnt, widgetName); this->setting.default_addr = this->stringRepresentation = resource_value; this->storedData = FALSE; this->valid_resource = FALSE; delete widgetName; delete rname; delete full_resource; return TRUE; } // S T A T I C U T I L S S T A T I C U T I L S S T A T I C U T I L S // S T A T I C U T I L S S T A T I C U T I L S S T A T I C U T I L S // S T A T I C U T I L S S T A T I C U T I L S S T A T I C U T I L S // S T A T I C U T I L S S T A T I C U T I L S S T A T I C U T I L S // // Produce the string which represents the name of the widget and parents // from root on down. The string will be passed to XtNameToWidget(). // The string will contain at most MAXLEVELS names. // #define MAXLEVELS 3 char * DynamicResource::QualifiedWidgetName (Widget root, Widget dest) { char *names_of_dest[MAXLEVELS+1]; int i; if (dest == root) { char *cp = new char[2]; cp[0] = '.'; cp[1] = '\0'; return cp; } // compute the length of the new widget name. Widget w = dest; ASSERT(w != root); int howmany = 0; int totlen = 0; char *cp; do { cp = XtName(w); totlen+= 1+strlen(cp); names_of_dest[howmany++] = cp; w = XtParent(w); ASSERT(w); // would fail if dest were not a descendant of this->root } while ((w != root)&&(howmany=0; i--) { strcpy (&newname[os], names_of_dest[i]); os+= strlen(names_of_dest[i]); // don't tack on the separator the last time around if (i != 0) { strcpy (&newname[os], "."); os++; } } return newname; } // // Convert a name to a Widget, then insert the resource spec. // boolean DynamicResource::applySetting (const char *name) { XrmValue toinout, from; unsigned char uchar_space; int int_space; short short_space; long long_space; Widget w; if (!strcmp(name, ".")) w = this->root; else w = XtNameToWidget (this->root, name); ASSERT(w); if (!this->storedData) { ASSERT (from.addr = (XPointer)this->setting.default_addr); from.size = 1+strlen((char *)from.addr); // // To get the data for use in XtSetValues, you have to // dereference a pointer. That requires the appropriate cast. Noteworthy // in its absence are the types float and double. I'm not sure how to // move them around in an ARCH-independent way. I suspect it's not necessary // to use various chunks of memory for this. One chunk would suffice if it's // guarranteed to be large enough and aligned properly. // toinout.size = this->setting.resource_size; if (this->setting.resource_size == sizeof(unsigned char)) { toinout.addr = (XPointer)&uchar_space; } else if (this->setting.resource_size == sizeof(short)) { toinout.addr = (XPointer)&short_space; } else if (this->setting.resource_size == sizeof(int)) { toinout.addr = (XPointer)&int_space; } else if (this->setting.resource_size == sizeof(long)) { toinout.addr = (XPointer)&long_space; } else ASSERT(0); // This would fail if there were no installed type converter. // It also fails for unrecognized data for some resources. Boolean resOK = XtConvertAndStore(w, XmRString, &from, this->setting.resource_type, &toinout); if (resOK) { if (this->setting.resource_size == sizeof(unsigned char)) { this->setting.default_addr = (XtPointer)*((unsigned char *)toinout.addr); } else if (this->setting.resource_size == sizeof(short)) { this->setting.default_addr = (XtPointer)*((short *) toinout.addr); } else if (this->setting.resource_size == sizeof(int)) { this->setting.default_addr = (XtPointer)*((int *) toinout.addr); } else if (this->setting.resource_size == sizeof(long)) { this->setting.default_addr = (XtPointer)*((long *) toinout.addr); } this->storedData = TRUE; } else { if (this->stringRepresentation) delete this->stringRepresentation; this->setting.default_addr = 0; this->stringRepresentation = NULL; return FALSE; } } XtVaSetValues (w, this->setting.resource_name, this->setting.default_addr, NULL); return TRUE; } // // TRUE if resourceName names a resource held by Widget w. The XtResource from that // widget is copied into *setting. (Assumes XmNlabelString means the same thing // for anyone who has one. I believe that's safe.) // // It would be good to check for the types we can handle, but // when the XtConvertAndStore happens we'll check the return value. If this stuff // were exposed to users, then you would have to handle errors, but I suspect that // will never happen. // This code should be able to handle any type which editres can. // boolean DynamicResource::VerifyPresence (const char *resourceName, Widget w, XtResource *setting) { XtResourceList reslist; Cardinal rescnt; int i; // Not an error because we want to go thru the constructor OK even // if the resource name is not yet know. This is because of printing, parsing. if ((!resourceName) || (!resourceName[0])) return FALSE; XtGetResourceList (XtClass(w), &reslist, &rescnt); for (i=0; iresource_name = new char[1+strlen(src->resource_name)]; strcpy (dest->resource_name, src->resource_name); dest->resource_class = new char[1+strlen(src->resource_class)]; strcpy (dest->resource_class, src->resource_class); dest->resource_type = new char[1+strlen(src->resource_type)]; strcpy (dest->resource_type, src->resource_type); dest->default_type = new char[1+strlen(src->default_type)]; strcpy (dest->default_type, src->default_type); dest->resource_size = src->resource_size; dest->resource_offset = src->resource_offset; } String * DynamicResource::GrowNameList (String *widgetNames, int *n, const char *newname) { int nameCnt = *n; String *newlist; int i; // increase the size of this->widgetNames newlist = new String[nameCnt + 1]; for (i=0; i