/*----------------------------------------------------------------------*/ /* parameter.c */ /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */ /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /* written by Tim Edwards, 10/26/99 */ /* revised for segmented strings, 3/8/01 */ /*----------------------------------------------------------------------*/ #include #include #include #ifndef XC_WIN32 #include #include #endif #ifdef TCL_WRAPPER #include #else #ifndef XC_WIN32 #include "Xw/Xw.h" #endif #endif /*----------------------------------------------------------------------*/ /* Local includes */ /*----------------------------------------------------------------------*/ #include "xcircuit.h" #include "menudep.h" /*----------------------------------------------------------------------*/ /* Function prototype declarations */ /*----------------------------------------------------------------------*/ #include "prototypes.h" /*----------------------------------------------------------------------*/ /* Externally declared global variables */ /*----------------------------------------------------------------------*/ #ifdef TCL_WRAPPER extern Tcl_Interp *xcinterp; #endif extern Globaldata xobjs; extern Clientdata areastruct; #ifndef TCL_WRAPPER extern Widget menuwidgets[]; #endif extern short textpos, textend; extern char _STR[150]; /*----------------------------------------------------------------------*/ /* The following u_char array matches parameterization types to element */ /* types which are able to accept the given parameterization. */ /*----------------------------------------------------------------------*/ u_char param_select[] = { ALL_TYPES, /* P_NUMERIC */ LABEL, /* P_SUBSTRING */ POLYGON | SPLINE | LABEL | OBJINST | ARC, /* P_POSITION_X */ POLYGON | SPLINE | LABEL | OBJINST | ARC, /* P_POSITION_Y */ POLYGON | SPLINE | ARC | PATH, /* P_STYLE */ LABEL, /* P_JUSTIFY */ ARC, /* P_ANGLE1 */ ARC, /* P_ANGLE2 */ ARC, /* P_RADIUS */ ARC, /* P_MINOR_AXIS */ LABEL | OBJINST, /* P_ROTATION */ LABEL | OBJINST, /* P_SCALE */ POLYGON | SPLINE | ARC | PATH, /* P_LINEWIDTH */ ALL_TYPES, /* P_COLOR */ ALL_TYPES, /* P_EXPRESSION */ POLYGON | SPLINE | LABEL | OBJINST | ARC /* P_POSITION */ }; #ifdef TCL_WRAPPER #ifndef _MSC_VER xcWidget *param_buttons[] = { /* To be done---map buttons to Tk_Windows! */ }; #else xcWidget *param_buttons[]; #endif #else Widget *param_buttons[] = { &ParametersNumericButton, /* P_NUMERIC */ &ParametersSubstringButton, /* P_SUBSTRING */ &ParametersPositionButton, /* P_POSITION_X */ &ParametersPositionButton, /* P_POSITION_Y */ &ParametersStyleButton, /* P_STYLE */ &ParametersJustificationButton, /* P_JUSTIFY */ &ParametersStartAngleButton, /* P_ANGLE1 */ &ParametersEndAngleButton, /* P_ANGLE2 */ &ParametersRadiusButton, /* P_RADIUS */ &ParametersMinorAxisButton, /* P_MINOR_AXIS */ &ParametersRotationButton, /* P_ROTATION */ &ParametersScaleButton, /* P_SCALE */ &ParametersLinewidthButton, /* P_LINEWIDTH */ &ParametersColorButton, /* P_COLOR */ &ParametersPositionButton, /* P_POSITION */ }; #endif /*----------------------------------------------------------------------*/ /* Basic routines for matching parameters by key values. Note that */ /* this really, really ought to be replaced by a hash table search! */ /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /* Check for the existance of a parameter with key "key" in object */ /* "thisobj". Return true if the parameter exists. */ /*----------------------------------------------------------------------*/ Boolean check_param(objectptr thisobj, char *key) { oparamptr tops; for (tops = thisobj->params; tops != NULL; tops = tops->next) if (!strcmp(tops->key, key)) return TRUE; return FALSE; } /*----------------------------------------------------------------------*/ /* Create a new parameter; allocate memory for the parameter and the */ /* key. */ /*----------------------------------------------------------------------*/ oparamptr make_new_parameter(char *key) { oparamptr newops; newops = (oparamptr)malloc(sizeof(oparam)); newops->next = NULL; newops->key = (char *)malloc(1 + strlen(key)); strcpy(newops->key, key); return newops; } /*----------------------------------------------------------------------*/ /* Create a new element (numeric) parameter. Fill in essential values */ /*----------------------------------------------------------------------*/ eparamptr make_new_eparam(char *key) { eparamptr newepp; newepp = (eparamptr)malloc(sizeof(eparam)); newepp->next = NULL; newepp->key = (char *)malloc(1 + strlen(key)); strcpy(newepp->key, key); newepp->pdata.refkey = NULL; /* equivalently, sets pointno=0 */ return newepp; } /*----------------------------------------------------------------------*/ /* Determine if a parameter is indirectly referenced. If so, return */ /* the reference key. If not, return NULL. */ /*----------------------------------------------------------------------*/ char *find_indirect_param(objinstptr thisinst, char *key) { eparamptr epp; for (epp = thisinst->passed; epp != NULL; epp = epp->next) { if (!strcmp(epp->key, key)) return epp->pdata.refkey; } return NULL; } /*----------------------------------------------------------------------*/ /* Find the parameter in the indicated object by key */ /*----------------------------------------------------------------------*/ oparamptr match_param(objectptr thisobj, char *key) { oparamptr fparam; for (fparam = thisobj->params; fparam != NULL; fparam = fparam->next) if (!strcmp(fparam->key, key)) return fparam; return NULL; /* No parameter matched the key---error condition */ } /*----------------------------------------------------------------------*/ /* Find the parameter in the indicated instance by key. If no such */ /* instance value exists, return NULL. */ /*----------------------------------------------------------------------*/ oparamptr match_instance_param(objinstptr thisinst, char *key) { oparamptr fparam; for (fparam = thisinst->params; fparam != NULL; fparam = fparam->next) if (!strcmp(fparam->key, key)) return fparam; return NULL; /* No parameter matched the key---error condition */ } /*----------------------------------------------------------------------*/ /* Find the parameter in the indicated instance by key. If no such */ /* instance value exists, return the object (default) parameter. */ /* */ /* find_param() hides instances of expression parameters, returning the */ /* default parameter value. In cases where the instance value (last */ /* evaluated expression result) is needed, use match_instance_param(). */ /* An exception is made when the instance param has type XC_EXPR, */ /* indicating that the instance redefines the entire expression. */ /*----------------------------------------------------------------------*/ oparamptr find_param(objinstptr thisinst, char *key) { oparamptr fparam, ops; fparam = match_instance_param(thisinst, key); ops = match_param(thisinst->thisobject, key); if ((fparam == NULL) || (ops->type == XC_EXPR) && (fparam->type != XC_EXPR)) fparam = ops; return fparam; } /*----------------------------------------------------------------------*/ /* Find the total number of parameters in an object */ /*----------------------------------------------------------------------*/ int get_num_params(objectptr thisobj) { oparamptr fparam; int nparam = 0; for (fparam = thisobj->params; fparam != NULL; fparam = fparam->next) nparam++; return nparam; } /*----------------------------------------------------------------------*/ /* Remove all element parameters from an element */ /*----------------------------------------------------------------------*/ void free_all_eparams(genericptr thiselem) { while (thiselem->passed != NULL) free_element_param(thiselem, thiselem->passed); } /*----------------------------------------------------------------------*/ /* Remove an element parameter (eparam) and free memory associated with */ /* the parameter key. */ /*----------------------------------------------------------------------*/ void free_element_param(genericptr thiselem, eparamptr thisepp) { eparamptr epp, lastepp = NULL; for (epp = thiselem->passed; epp != NULL; epp = epp->next) { if (epp == thisepp) { if (lastepp != NULL) lastepp->next = epp->next; else thiselem->passed = epp->next; /* If object is an instance and the pdata record is not NULL, */ /* then this is an indirect reference with the reference key */ /* stored as an allocated string in pdata.refkey, which needs */ /* to be free'd. */ if (IS_OBJINST(thiselem) && (epp->pdata.refkey != NULL)) free(epp->pdata.refkey); free(epp->key); free(epp); break; } lastepp = epp; } } /*----------------------------------------------------------------------*/ /* Free an instance parameter. Note that this routine does not free */ /* any strings associated with string parameters! */ /*----------------------------------------------------------------------*/ void free_instance_param(objinstptr thisinst, oparamptr thisparam) { oparamptr ops, lastops = NULL; for (ops = thisinst->params; ops != NULL; ops = ops->next) { if (ops == thisparam) { if (lastops != NULL) lastops->next = ops->next; else thisinst->params = ops->next; free(ops->key); free(ops); break; } lastops = ops; } } /*----------------------------------------------------------------------*/ /* Convenience function used by files.c to set a color parameter. */ /*----------------------------------------------------------------------*/ void std_eparam(genericptr gen, char *key) { eparamptr epp; if (key == NULL) return; epp = make_new_eparam(key); epp->next = gen->passed; gen->passed = epp; } /*----------------------------------------------*/ /* Draw a circle at all parameter positions */ /*----------------------------------------------*/ void indicateparams(genericptr thiselem) { int i, j, k, which; oparamptr ops; eparamptr epp; if (thiselem != NULL) { for (epp = thiselem->passed; epp != NULL; epp = epp->next) { ops = match_param(topobject, epp->key); if (ops == NULL) continue; /* error condition */ k = epp->pdata.pointno; switch(ops->which) { case P_POSITION: case P_POSITION_X: case P_POSITION_Y: switch(thiselem->type) { case ARC: UDrawCircle(&TOARC(&thiselem)->position, ops->which); break; case LABEL: UDrawCircle(&TOLABEL(&thiselem)->position, ops->which); break; case OBJINST: UDrawCircle(&TOOBJINST(&thiselem)->position, ops->which); break; case POLYGON: UDrawCircle(TOPOLY(&thiselem)->points + k, ops->which); break; case SPLINE: UDrawCircle(&TOSPLINE(&thiselem)->ctrl[k], ops->which); break; } break; } } } } /*----------------------------------------------*/ /* Set the menu marks according to properties */ /* which are parameterized. Unmanage the */ /* buttons which do not apply. */ /* */ /* pgen = NULL returns menu to default settings */ /*----------------------------------------------*/ #ifdef TCL_WRAPPER void setparammarks(genericptr thiselem) { /* Set GUI variables associated with the "parameter" menu. */ int i, j, paramno; oparamptr ops; eparamptr epp; /* These match the order of parameter definitions in xcircuit.h */ const char *paramvars[] = {"positionparam", "substringparam", "xparam", "yparam", "styleparam", "justparam", "startparam", "endparam", "radiusparam", "minorparam", "rotationparam", "scaleparam", "linewidthparam", "colorparam"}; /* Reset all variables to false */ for (i = 0; i < 14; i++) Tcl_SetVar2(xcinterp, "XCOps", (char *)paramvars[i], "false", TCL_NAMESPACE_ONLY); /* For each parameter declared, set the corresponding Tcl variable */ if (thiselem != NULL) { for (epp = thiselem->passed; epp != NULL; epp = epp->next) { ops = match_param(topobject, epp->key); if (ops == NULL) continue; /* error condition */ Tcl_SetVar2(xcinterp, "XCOps", (char *)paramvars[ops->which], "true", TCL_NAMESPACE_ONLY); } } } #else void setparammarks(genericptr thiselem) { Widget w; Arg wargs[1]; const int rlength = sizeof(param_buttons) / sizeof(Widget *); int i, j, paramno; oparamptr ops; eparamptr epp; /* Clear all checkmarks */ for (i = 0; i < rlength; i++) { XtSetArg(wargs[0], XtNsetMark, False); XtSetValues(*param_buttons[i], wargs, 1); } /* Check those properties which are parameterized in the element */ if (thiselem != NULL) { for (epp = thiselem->passed; epp != NULL; epp = epp->next) { ops = match_param(topobject, epp->key); w = *param_buttons[ops->which]; XtSetArg(wargs[0], XtNsetMark, True); XtSetValues(w, wargs, 1); } } /* Unmanage widgets which do not apply to the element type */ for (i = 0; i < rlength; i++) { if ((thiselem == NULL) || (param_select[i] & thiselem->type)) XtManageChild(*param_buttons[i]); else XtUnmanageChild(*param_buttons[i]); } } #endif /*------------------------------------------------------*/ /* Make numerical parameter substitutions into an */ /* element. "thisinst" may be NULL, in which case all */ /* default values are used in the substitution. */ /* */ /* Return values: */ /* -1 if the instance declares no parameters */ /* 0 if parameters do not change the instance's bbox */ /* 1 if parameters change instance's bbox */ /* 2 if parameters change instance's netlist */ /*------------------------------------------------------*/ int opsubstitute(objectptr thisobj, objinstptr pinst) { genericptr *eptr, thiselem; int i, j, k, ival; char *key; float fval; oparamptr dps, ops, cps; eparamptr epp; XPoint *setpt; Boolean needrecalc; /* for arcs and splines */ int retval = -1; char *promoted; for (eptr = thisobj->plist; eptr < thisobj->plist + thisobj->parts; eptr++) { needrecalc = False; thiselem = *eptr; if (thiselem->passed == NULL) continue; /* Nothing to substitute */ for (epp = thiselem->passed; epp != NULL; epp = epp->next) { /* Use the parameter from the instance, if available. */ /* Otherwise, revert to the type of the object. */ /* Normally they will be the same. */ ops = match_param(thisobj, epp->key); dps = (pinst != NULL) ? find_param(pinst, epp->key) : ops; if (dps != NULL) { /* Get integer and float values. Promote types if necessary */ switch(dps->type) { case XC_INT: ival = dps->parameter.ivalue; fval = (float)(ival); break; case XC_FLOAT: fval = dps->parameter.fvalue; ival = (int)(fval + 0.5); break; case XC_STRING: promoted = textprint(dps->parameter.string, pinst); if (sscanf(promoted, "%g", &fval) == 1) ival = (int)(fval + 0.5); else ival = 0; free(promoted); break; case XC_EXPR: if ((promoted = evaluate_expr(dps, pinst)) == NULL) continue; if (sscanf(promoted, "%g", &fval) == 1) ival = (int)(fval + 0.5); free(promoted); break; } } else if (ops == NULL) continue; if (IS_OBJINST(thiselem)) { key = epp->pdata.refkey; if (key != NULL) { objinstptr thisinst; oparamptr refop, newop; thisinst = TOOBJINST(eptr); /* Sanity check: refkey must exist in object */ refop = match_param(thisinst->thisobject, key); if (refop == NULL) { Fprintf(stderr, "Error: Reference key %s does not" " exist in object %s\n", key, thisinst->thisobject->name); continue; } /* If an instance value already exists, remove it */ newop = match_instance_param(thisinst, refop->key); if (newop != NULL) free_instance_param(thisinst, newop); /* Create a new instance parameter */ newop = copyparameter(dps); newop->next = thisinst->params; thisinst->params = newop; /* Change the key from the parent to the child */ if (strcmp(ops->key, refop->key)) { free(newop->key); newop->key = strdup(refop->key); } continue; } } k = epp->pdata.pointno; switch(ops->which) { case P_POSITION_X: retval = max(retval, 1); switch(thiselem->type) { case POLYGON: setpt = TOPOLY(&thiselem)->points + k; setpt->x = ival; break; case SPLINE: TOSPLINE(&thiselem)->ctrl[k].x = ival; needrecalc = True; break; case LABEL: TOLABEL(&thiselem)->position.x = ival; break; case OBJINST: TOOBJINST(&thiselem)->position.x = ival; break; case ARC: TOARC(&thiselem)->position.x = ival; break; } break; case P_POSITION_Y: retval = max(retval, 1); switch(thiselem->type) { case POLYGON: setpt = TOPOLY(&thiselem)->points + k; setpt->y = ival; break; case SPLINE: TOSPLINE(&thiselem)->ctrl[k].y = ival; needrecalc = True; break; case LABEL: TOLABEL(&thiselem)->position.y = ival; break; case OBJINST: TOOBJINST(&thiselem)->position.y = ival; break; case ARC: TOARC(&thiselem)->position.y = ival; break; } break; case P_STYLE: retval = max(retval, 0); switch(thiselem->type) { case POLYGON: TOPOLY(&thiselem)->style = ival; break; case SPLINE: TOSPLINE(&thiselem)->style = ival; break; case ARC: TOARC(&thiselem)->style = ival; break; case PATH: TOPATH(&thiselem)->style = ival; break; } break; case P_JUSTIFY: retval = max(retval, 1); switch(thiselem->type) { case LABEL: TOLABEL(&thiselem)->justify = ival; break; } break; case P_ANGLE1: retval = max(retval, 1); switch(thiselem->type) { case ARC: TOARC(&thiselem)->angle1 = fval; needrecalc = True; break; } break; case P_ANGLE2: retval = max(retval, 1); switch(thiselem->type) { case ARC: TOARC(&thiselem)->angle1 = fval; needrecalc = True; break; } break; case P_RADIUS: retval = max(retval, 1); switch(thiselem->type) { case ARC: TOARC(&thiselem)->radius = ival; TOARC(&thiselem)->yaxis = ival; needrecalc = True; break; } break; case P_MINOR_AXIS: retval = max(retval, 1); switch(thiselem->type) { case ARC: TOARC(&thiselem)->yaxis = ival; needrecalc = True; break; } break; case P_ROTATION: retval = max(retval, 1); switch(thiselem->type) { case LABEL: TOLABEL(&thiselem)->rotation = ival; break; case OBJINST: TOOBJINST(&thiselem)->rotation = ival; break; } break; case P_SCALE: retval = max(retval, 1); switch(thiselem->type) { case LABEL: TOLABEL(&thiselem)->scale = fval; break; case OBJINST: TOOBJINST(&thiselem)->scale = fval; break; } break; case P_LINEWIDTH: retval = max(retval, 0); switch(thiselem->type) { case POLYGON: TOPOLY(&thiselem)->width = fval; break; case SPLINE: TOSPLINE(&thiselem)->width = fval; break; case ARC: TOARC(&thiselem)->width = fval; break; case PATH: TOPATH(&thiselem)->width = fval; break; } break; case P_COLOR: retval = max(retval, 0); thiselem->color = ival; break; } } /* substitutions into arcs and splines require that the */ /* line segments be recalculated. */ if (needrecalc) { switch(thiselem->type) { case ARC: calcarc((arcptr)thiselem); break; case SPLINE: calcspline((splineptr)thiselem); break; } } } return retval; } /*------------------------------------------------------*/ /* Same as above, but determines the object from the */ /* current page hierarchy. */ /*------------------------------------------------------*/ int psubstitute(objinstptr thisinst) { objinstptr pinst; objectptr thisobj; pinst = (thisinst == areastruct.topinstance) ? areastruct.topinstance : thisinst; if (pinst == NULL) return -1; /* there is no instance */ else if (pinst->thisobject->params == NULL) return -1; /* object has no parameters */ thisobj = (pinst == NULL) ? topobject : pinst->thisobject; return opsubstitute(thisobj, pinst); } /*----------------------------------------------*/ /* Check if an element contains a parameter. */ /*----------------------------------------------*/ Boolean has_param(genericptr celem) { if (IS_LABEL(celem) == LABEL) { stringpart *cstr; labelptr clab = (labelptr)celem; for (cstr = clab->string; cstr != NULL; cstr = cstr->nextpart) if (cstr->type == PARAM_START) return TRUE; } if (celem->passed != NULL) return TRUE; return FALSE; } /*------------------------------------------------------*/ /* Find "current working values" in the element list of */ /* an object, and write them into the instance's */ /* parameter list. */ /* This is just the opposite of "psubstitute()", except */ /* that instance values are created prior to writeback, */ /* and resolved afterward. */ /*------------------------------------------------------*/ void pwriteback(objinstptr thisinst) { genericptr *eptr, thiselem; objectptr thisobj; objinstptr pinst; eparamptr epp; oparamptr ops, ips; int i, j, k, *destivalptr, found; XPoint *setpt; Boolean changed, need_redraw = FALSE; union { int ival; float fval; } wtemp; pinst = thisinst; thisobj = (pinst == NULL) ? topobject : pinst->thisobject; /* Make sure that all instance values exist */ if (pinst != NULL) copyparams(pinst, pinst); /* Because more than one element can point to the same parameter, we search */ /* through each (numerical) parameter declared in the object. If any */ /* element has a different value, the parameter is changed to match. This */ /* operates on the assumption that no more than one element will change the */ /* value of any one parameter on a single call to pwriteback(). */ for (ops = thisobj->params; ops != NULL; ops = ops->next) { /* handle pre-assigned numeric parameters only */ if ((ops->which == P_SUBSTRING) || (ops->which == P_EXPRESSION) || (ops->which == P_NUMERIC)) continue; found = 0; changed = FALSE; ips = (pinst != NULL) ? match_instance_param(pinst, ops->key) : NULL; for (eptr = thisobj->plist; eptr < thisobj->plist + thisobj->parts; eptr++) { thiselem = *eptr; if (thiselem->passed == NULL) continue; /* Nothing to write back */ for (epp = thiselem->passed; epp != NULL; epp = epp->next) { if (!strcmp(epp->key, ops->key)) { found++; k = epp->pdata.pointno; switch(ops->which) { case P_POSITION_X: switch(thiselem->type) { case OBJINST: wtemp.ival = TOOBJINST(&thiselem)->position.x; break; case LABEL: wtemp.ival = TOLABEL(&thiselem)->position.x; break; case POLYGON: setpt = TOPOLY(&thiselem)->points + k; wtemp.ival = setpt->x; break; case ARC: wtemp.ival = TOARC(&thiselem)->position.x; break; case SPLINE: wtemp.ival = TOSPLINE(&thiselem)->ctrl[k].x; break; } break; case P_POSITION_Y: switch(thiselem->type) { case OBJINST: wtemp.ival = TOOBJINST(&thiselem)->position.y; break; case LABEL: wtemp.ival = TOLABEL(&thiselem)->position.y; break; case POLYGON: setpt = TOPOLY(&thiselem)->points + k; wtemp.ival = setpt->y; break; case ARC: wtemp.ival = TOARC(&thiselem)->position.y; break; case SPLINE: wtemp.ival = TOSPLINE(&thiselem)->ctrl[k].y; break; } break; case P_STYLE: switch(thiselem->type) { case POLYGON: wtemp.ival = TOPOLY(&thiselem)->style; break; case ARC: wtemp.ival = TOARC(&thiselem)->style; break; case SPLINE: wtemp.ival = TOSPLINE(&thiselem)->style; break; case PATH: wtemp.ival = TOPATH(&thiselem)->style; break; } break; case P_JUSTIFY: switch(thiselem->type) { case LABEL: wtemp.ival = TOLABEL(&thiselem)->justify; break; } break; case P_ANGLE1: switch(thiselem->type) { case ARC: wtemp.fval = TOARC(&thiselem)->angle1; break; } break; case P_ANGLE2: switch(thiselem->type) { case ARC: wtemp.fval = TOARC(&thiselem)->angle1; break; } break; case P_RADIUS: switch(thiselem->type) { case ARC: wtemp.ival = TOARC(&thiselem)->radius; break; } break; case P_MINOR_AXIS: switch(thiselem->type) { case ARC: wtemp.ival = TOARC(&thiselem)->yaxis; break; } break; case P_ROTATION: switch(thiselem->type) { case OBJINST: wtemp.ival = TOOBJINST(&thiselem)->rotation; break; case LABEL: wtemp.ival = TOLABEL(&thiselem)->rotation; break; } break; case P_SCALE: switch(thiselem->type) { case OBJINST: wtemp.fval = TOOBJINST(&thiselem)->scale; break; case LABEL: wtemp.fval = TOLABEL(&thiselem)->scale; break; } break; case P_LINEWIDTH: switch(thiselem->type) { case POLYGON: wtemp.fval = TOPOLY(&thiselem)->width; break; case ARC: wtemp.fval = TOARC(&thiselem)->width; break; case SPLINE: wtemp.fval = TOSPLINE(&thiselem)->width; break; case PATH: wtemp.fval = TOPATH(&thiselem)->width; break; } break; case P_COLOR: wtemp.ival = thiselem->color; break; } destivalptr = (ips != NULL) ? &ips->parameter.ivalue : &ops->parameter.ivalue; if ((!changed) && (wtemp.ival != *destivalptr)) { *destivalptr = wtemp.ival; changed = TRUE; } else if (found > 1) need_redraw = TRUE; break; } } } } /* Any instance values which are identical to the default value */ /* get erased (so they won't be written to the output unnecessarily) */ if (pinst != NULL) resolveparams(pinst); /* Because more than one element may use the same parameter, */ /* pwriteback checks for cases in which a change in one element */ /* precipitates a change in another. If so, force a redraw. */ if (need_redraw && (thisinst == areastruct.topinstance)) drawarea(NULL, NULL, NULL); } /*------------------------------------------------------*/ /* If the instance comes from the library, replace the */ /* default value with the instance value. */ /*------------------------------------------------------*/ void replaceparams(objinstptr thisinst) { objectptr thisobj; oparamptr ops, ips; int i, nullparms = 0; thisobj = thisinst->thisobject; for (ops = thisobj->params; ops != NULL; ops = ops->next) { ips = match_instance_param(thisinst, ops->key); if (ips == NULL) continue; /* this parameter is already default */ switch(ops->type) { case XC_STRING: if (stringcomp(ops->parameter.string, ips->parameter.string)) { freelabel(ops->parameter.string); ops->parameter.string = ips->parameter.string; free_instance_param(thisinst, ips); } break; case XC_EXPR: if (strcmp(ops->parameter.expr, ips->parameter.expr)) { free(ops->parameter.expr); ops->parameter.expr = ips->parameter.expr; free_instance_param(thisinst, ips); } case XC_INT: case XC_FLOAT: if (ops->parameter.ivalue != ips->parameter.ivalue) { ops->parameter.ivalue = ips->parameter.ivalue; free_instance_param(thisinst, ips); } break; } } } /*------------------------------------------------------*/ /* Resolve differences between the object instance */ /* parameters and the default parameters. If they */ /* are the same for any parameter, delete that instance */ /* such that the instance reverts to the default value. */ /*------------------------------------------------------*/ void resolveparams(objinstptr thisinst) { objectptr thisobj; liblistptr spec; oparamptr ops, ips; int i; /* If the instance has no parameters itself, ignore it. */ if (thisinst == NULL || thisinst->params == NULL) return; /* If the object was pushed into from a library, we want to change */ /* the default, not the instanced, parameter values. However, this */ /* is not true for "virtual" library objects (in the instlist) */ if ((i = checklibtop()) >= 0) { for (spec = xobjs.userlibs[i].instlist; spec != NULL; spec = spec->next) if (spec->thisinst == thisinst) break; if ((spec == NULL) || (spec->virtual == FALSE)) { /* Fprintf(stdout, "Came from library: changing default value\n"); */ replaceparams(thisinst); return; } } /* Parameters which are changed on a top-level page must also change */ /* the default value; otherwise, the instance value shadows the page */ /* object's value but the page object's value is the one written to */ /* the output file. */ else if (is_page(thisinst->thisobject) >= 0) { replaceparams(thisinst); return; } thisobj = thisinst->thisobject; for (ops = thisobj->params; ops != NULL; ops = ops->next) { ips = match_instance_param(thisinst, ops->key); if (ips == NULL) continue; /* this parameter is already default */ /* If type or which fields do not match, then we don't need to look */ /* any further; object and instance have different parameters. */ if ((ips->type != ops->type) || (ips->which != ops->which)) continue; switch(ops->type) { case XC_STRING: if (!stringcomp(ops->parameter.string, ips->parameter.string)) { freelabel(ips->parameter.string); free_instance_param(thisinst, ips); } break; case XC_EXPR: /* Always retain the last evaluated result, unless */ /* the instance redefines the expression. */ if (ips->type == XC_EXPR) { if (!strcmp(ops->parameter.expr, ips->parameter.expr)) { free(ips->parameter.expr); free_instance_param(thisinst, ips); } } break; case XC_INT: case XC_FLOAT: if (ops->parameter.ivalue == ips->parameter.ivalue) { free_instance_param(thisinst, ips); } break; } } if (thisinst->params != NULL) { /* Object must recompute bounding box if any instance */ /* uses a non-default parameter. */ calcbboxvalues(thisinst, NULL); } } /*--------------------------------------------------------------*/ /* Return a copy of the single eparameter "cepp" */ /*--------------------------------------------------------------*/ eparamptr copyeparam(eparamptr cepp, genericptr thiselem) { eparamptr newepp; newepp = make_new_eparam(cepp->key); if (IS_OBJINST(thiselem) && (cepp->pdata.refkey != NULL)) newepp->pdata.refkey = strdup(cepp->pdata.refkey); else newepp->pdata.pointno = cepp->pdata.pointno; return newepp; } /*------------------------------------------------------*/ /* Copy all element parameters from source to dest */ /*------------------------------------------------------*/ void copyalleparams(genericptr destinst, genericptr sourceinst) { eparamptr cepp, newepp; for (cepp = sourceinst->passed; cepp != NULL; cepp = cepp->next) { newepp = copyeparam(cepp, sourceinst); newepp->next = destinst->passed; destinst->passed = newepp; } } /*--------------------------------------------------------------*/ /* Return a copy of the single parameter "cops" */ /*--------------------------------------------------------------*/ oparamptr copyparameter(oparamptr cops) { oparamptr newops; newops = make_new_parameter(cops->key); newops->type = cops->type; newops->which = cops->which; switch(cops->type) { case XC_STRING: newops->parameter.string = stringcopy(cops->parameter.string); break; case XC_EXPR: newops->parameter.expr = strdup(cops->parameter.expr); break; case XC_INT: case XC_FLOAT: newops->parameter.ivalue = cops->parameter.ivalue; break; default: Fprintf(stderr, "Error: bad parameter\n"); break; } return newops; } /*------------------------------------------------------*/ /* Fill any NULL instance parameters with the values */ /* from the calling instance, or from the instance */ /* object's defaults if destinst = sourceinst. */ /* */ /* Expression parameters get special treatment because */ /* the instance value may be holding the last evaluated */ /* expression, not an instance value of the expression. */ /* If so, its type will be XC_STRING or XC_FLOAT, not */ /* XC_EXPR. */ /*------------------------------------------------------*/ void copyparams(objinstptr destinst, objinstptr sourceinst) { oparamptr psource, cops, newops, ips; if (sourceinst == NULL) return; if (destinst == sourceinst) psource = sourceinst->thisobject->params; else psource = sourceinst->params; for (cops = psource; cops != NULL; cops = cops->next) { if (((ips = match_instance_param(destinst, cops->key)) == NULL) || (cops->type == XC_EXPR && ips->type != XC_EXPR)) { newops = copyparameter(cops); newops->next = destinst->params; destinst->params = newops; } } } /*--------------------------------------------------------------*/ /* Make an unreferenced parameter expression in the object */ /* refobject. */ /* The expression may have either a numeric or string result. */ /* the proper "which" value is passed as an argument. */ /*--------------------------------------------------------------*/ void makeexprparam(objectptr refobject, char *key, char *value, int which) { oparamptr newops; char *newkey; /* Check against postscript reserved names */ newkey = checkpostscriptname(key, NULL, NULL); /* Ensure that no two parameters have the same name! */ if (check_param(refobject, newkey)) { Wprintf("There is already a parameter with that key!"); } newops = make_new_parameter(newkey); newops->next = refobject->params; refobject->params = newops; newops->type = XC_EXPR; /* expression requiring evaluation */ newops->which = which; newops->parameter.expr = strdup(value); incr_changes(refobject); free(newkey); } /*------------------------------------------------------------------*/ /* Make an unreferenced numerical parameter in the object refobject */ /*------------------------------------------------------------------*/ void makefloatparam(objectptr refobject, char *key, float value) { oparamptr newops; char *newkey; /* Check against postscript reserved names */ newkey = checkpostscriptname(key, NULL, NULL); /* Ensure that no two parameters have the same name! */ if (check_param(refobject, newkey)) { Wprintf("There is already a parameter with that key!"); } newops = make_new_parameter(key); newops->next = refobject->params; refobject->params = newops; newops->type = XC_FLOAT; /* general-purpose numeric */ newops->which = P_NUMERIC; newops->parameter.fvalue = value; incr_changes(refobject); free(newkey); } /*----------------------------------------------------------------*/ /* Make an unreferenced string parameter in the object refobject. */ /*----------------------------------------------------------------*/ void makestringparam(objectptr refobject, char *key, stringpart *strptr) { oparamptr newops; char *newkey; /* Check against postscript reserved names */ newkey = checkpostscriptname(key, NULL, NULL); /* Ensure that no two parameters have the same name! */ if (check_param(refobject, newkey)) { Wprintf("There is already a parameter with that key!"); } newops = make_new_parameter(newkey); newops->next = refobject->params; refobject->params = newops; newops->type = XC_STRING; newops->which = P_SUBSTRING; newops->parameter.string = strptr; incr_changes(refobject); free(newkey); } /*--------------------------------------------------------------*/ /* Make a numerical (integer or float) parameter. */ /* If "key" is non-NULL, then the parameter key will be set */ /* from this rather than from the list "param_keys". If the */ /* key is an existing key with the same type as "mode", then */ /* the new parameter will be linked to the existing one. */ /*--------------------------------------------------------------*/ void makenumericalp(genericptr *gelem, u_int mode, char *key) { oparamptr ops, newops; eparamptr epp; XPoint *pptr; char new_key[7], *keyptr; int pidx; /* Numerical parameters have designated keys to avoid the necessity */ /* of having to specify a key, to avoid conflicts with PostScript */ /* predefined keys, and other good reasons. */ /* The order of these type names must match the enumeration in xcircuit.h */ static char *param_keys[] = { "p_gps", "p_str", "p_xps", "p_yps", "p_sty", "p_jst", "p_an1", "p_an2", "p_rad", "p_axs", "p_rot", "p_scl", "p_wid", "p_col" }; /* Parameterized strings are handled by makeparam() */ if (IS_LABEL(*gelem) && mode == P_SUBSTRING) { Fprintf(stderr, "Error: String parameter passed to makenumericalp()\n"); return; } /* Cannot form this type of parameter on a top-level page */ if (is_page(topobject) != -1) { Wprintf("Cannot form a parameter in a top-level page!"); return; } /* Make sure the parameter doesn't already exist. */ for (epp = (*gelem)->passed; epp != NULL; epp = epp->next) { ops = match_param(topobject, epp->key); if (ops->which == (u_char)mode) { Fprintf(stderr, "Cannot duplicate a parameter!\n"); return; } } /* Ensure that no two parameters have the same name! */ if (key) { keyptr = checkpostscriptname(key, NULL, NULL); } else { strcpy(new_key, param_keys[mode]); pidx = 0; while (check_param(topobject, new_key)) { pidx++; sprintf(new_key, "%s%d", param_keys[mode], pidx); } keyptr = new_key; } /* Add the parameter to the element's parameter list */ epp = make_new_eparam(keyptr); epp->next = (*gelem)->passed; (*gelem)->passed = epp; /* If keyptr does not point to an existing parameter, then we need */ /* to create it in the object's parameter list and set the default */ /* value to the existing value of the element. */ ops = match_param(topobject, keyptr); if (ops == NULL) { newops = make_new_parameter(keyptr); newops->next = topobject->params; topobject->params = newops; newops->type = XC_INT; /* most commonly used value */ newops->which = (u_char)mode; /* what kind of parameter */ incr_changes(topobject); } else { if (ops->which != (u_char)mode) { free_element_param(*gelem, epp); Fprintf(stderr, "Error: Attempt to link a parameter to " "a parameter of a different type\n"); goto param_done; } if ((newops = match_instance_param(areastruct.topinstance, keyptr)) == NULL) { newops = make_new_parameter(keyptr); newops->next = areastruct.topinstance->params; areastruct.topinstance->params = newops; newops->type = ops->type; newops->which = ops->which; } else { /* If the parameter exists and the instance has a non-default */ /* value for it, we will not change the instance record. If */ /* the element value is different, then it will change, so we */ /* should redraw. */ drawarea(NULL, NULL, NULL); newops = NULL; goto param_done; } } if (newops) { if (mode == P_COLOR) newops->parameter.ivalue = (int)((*gelem)->color); switch((*gelem)->type) { case LABEL: switch(mode) { case P_POSITION_X: newops->parameter.ivalue = (int)TOLABEL(gelem)->position.x; break; case P_POSITION_Y: newops->parameter.ivalue = (int)TOLABEL(gelem)->position.y; break; case P_JUSTIFY: newops->parameter.ivalue = (int)TOLABEL(gelem)->justify; break; case P_ROTATION: newops->parameter.ivalue = (int)TOLABEL(gelem)->rotation; break; case P_SCALE: newops->type = XC_FLOAT; newops->parameter.fvalue = TOLABEL(gelem)->scale; break; } break; case ARC: switch(mode) { case P_POSITION_X: newops->parameter.ivalue = (int)TOARC(gelem)->position.x; break; case P_POSITION_Y: newops->parameter.ivalue = (int)TOARC(gelem)->position.y; break; case P_ANGLE1: newops->type = XC_FLOAT; newops->parameter.fvalue = TOARC(gelem)->angle1; break; case P_ANGLE2: newops->type = XC_FLOAT; newops->parameter.fvalue = TOARC(gelem)->angle2; break; case P_RADIUS: newops->parameter.ivalue = (int)TOARC(gelem)->radius; break; case P_MINOR_AXIS: newops->parameter.ivalue = (int)TOARC(gelem)->yaxis; break; case P_STYLE: newops->parameter.ivalue = (int)TOARC(gelem)->style; break; case P_LINEWIDTH: newops->type = XC_FLOAT; newops->parameter.fvalue = TOARC(gelem)->width; break; } break; case OBJINST: switch(mode) { case P_POSITION_X: newops->parameter.ivalue = (int)TOOBJINST(gelem)->position.x; break; case P_POSITION_Y: newops->parameter.ivalue = (int)TOOBJINST(gelem)->position.y; break; case P_ROTATION: newops->parameter.ivalue = (int)TOOBJINST(gelem)->rotation; break; case P_SCALE: newops->type = XC_FLOAT; newops->parameter.fvalue = TOOBJINST(gelem)->scale; break; } break; case POLYGON: switch(mode) { case P_POSITION_X: pptr = TOPOLY(gelem)->points + areastruct.editcycle; newops->parameter.ivalue = (int)pptr->x; epp->pdata.pointno = areastruct.editcycle; break; case P_POSITION_Y: pptr = TOPOLY(gelem)->points + areastruct.editcycle; newops->parameter.ivalue = (int)pptr->y; epp->pdata.pointno = areastruct.editcycle; break; case P_STYLE: newops->parameter.ivalue = (int)TOPOLY(gelem)->style; break; case P_LINEWIDTH: newops->type = XC_FLOAT; newops->parameter.fvalue = TOPOLY(gelem)->width; break; } break; case SPLINE: switch(mode) { case P_POSITION_X: pptr = TOSPLINE(gelem)->ctrl + areastruct.editcycle; newops->parameter.ivalue = (int)pptr->x; epp->pdata.pointno = areastruct.editcycle; break; case P_POSITION_Y: pptr = TOSPLINE(gelem)->ctrl + areastruct.editcycle; newops->parameter.ivalue = (int)pptr->y; epp->pdata.pointno = areastruct.editcycle; break; case P_STYLE: newops->parameter.ivalue = (int)TOSPLINE(gelem)->style; break; case P_LINEWIDTH: newops->type = XC_FLOAT; newops->parameter.fvalue = TOSPLINE(gelem)->width; break; } break; case PATH: switch(mode) { case P_STYLE: newops->parameter.ivalue = (int)TOPATH(gelem)->style; break; case P_LINEWIDTH: newops->type = XC_FLOAT; newops->parameter.fvalue = TOPATH(gelem)->width; break; } break; } } param_done: if (keyptr != new_key) free(keyptr); } /*--------------------------------------------------------------*/ /* Remove a numerical (integer or float) parameter. Remove by */ /* type, rather than key. There may be several keys associated */ /* with a particular type, so we want to remove all of them. */ /*--------------------------------------------------------------*/ void unmakenumericalp(genericptr *gelem, u_int mode) { genericptr *pgen; eparamptr epp; oparamptr ops; char *key; Boolean done = False, is_last = True; /* Parameterized strings are handled by makeparam() */ if (mode == P_SUBSTRING) { Fprintf(stderr, "Error: Unmakenumericalp called on a string parameter.\n"); return; } /* Avoid referencing the object by only looking at the element. */ /* But, avoid dereferencing the pointer! */ while (!done) { key = NULL; done = True; for (epp = (*gelem)->passed; epp != NULL; epp = epp->next) { ops = match_param(topobject, epp->key); if (ops == NULL) break; /* Error---no such parameter */ else if (ops->which == (u_char)mode) { key = ops->key; free_element_param(*gelem, epp); /* check for any other references to the parameter. If there */ /* are none, remove all instance records of the eparam, then */ /* remove the parameter itself from the object. */ for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts; pgen++) { if (*pgen == *gelem) continue; for (epp = (*pgen)->passed; epp != NULL; epp = epp->next) { if (!strcmp(epp->key, key)) { is_last = False; break; } } if (!is_last) break; } if (is_last) free_object_param(topobject, ops); done = False; break; } } } } /*--------------------------------------------------------------*/ /* Insert an existing parameter into a string. */ /*--------------------------------------------------------------*/ void insertparam() { labelptr tlab; oparamptr ops; int result, nparms; char *selparm; /* Don't allow nested parameters */ tlab = TOLABEL(EDITPART); if (paramcross(topobject, tlab)) { Wprintf("Parameters cannot be nested!"); return; } #ifdef TCL_WRAPPER /* The Tcl version uses a GUI selection mechanism. */ result = Tcl_Eval(xcinterp, "xcircuit::promptselectparam"); if (result != TCL_OK) { Tcl_SetResult(xcinterp, "Error in executing promptselectparam", NULL); return; } selparm = Tcl_GetString(Tcl_GetObjResult(xcinterp)); ops = match_param(topobject, selparm); if (ops == NULL) { Tcl_SetResult(xcinterp, "No such parameter.", NULL); selparm = NULL; } #else /* First pass is to check how many string parameters there are: */ /* If only one, then automatically use it. Otherwise, prompt */ /* for which parameter to use. */ for (ops = topobject->params; ops != NULL; ops = ops->next) if (ops->type == XC_STRING) nparms++; if (nparms > 1) { char *newstr, *sptr; char *sstart = (char *)malloc(1024); nparms = 0; strcpy(sstart, "Choose: "); sptr = sstart + 8; for (ops = topobject->params; ops != NULL; ops = ops->next) { if (ops->type == XC_STRING) { nparms++; if (nparms != 1) { strcat(sptr, ", "); sptr += 2; } sprintf(sptr, "%s = <", ops->key); newstr = stringprint(ops->parameter.string, NULL); strcat(sptr, newstr); free(newstr); newstr = NULL; sptr += strlen(sptr); } } Wprintf(sstart); free(sstart); /* Need a new non-Tcl version method . . . */ ops = NULL; /* placeholder */ } #endif if (ops != NULL) labeltext(PARAM_START, selparm); else Wprintf("No such parameter."); } /*--------------------------------------------------------------*/ /* Parameterize a label string. */ /*--------------------------------------------------------------*/ void makeparam(labelptr thislabel, char *key) { oparamptr newops; stringpart *begpart, *endpart; char *newkey; /* cannot form a parameter on a top-level page */ if (is_page(topobject) != -1) { Wprintf("Cannot form a parameter in a top-level page!"); return; } /* make sure this does not overlap another parameter */ if (paramcross(topobject, thislabel)) { Wprintf("Parameters cannot be nested!"); textend = 0; return; } /* check parameter against reserved PostScript names */ newkey = checkpostscriptname(key, NULL, NULL); /* First, place PARAM_START and PARAM_END structures at the */ /* intended parameter boundaries */ if (textend > 0 && textend < textpos) { /* partial string */ splitstring(textend, &thislabel->string, areastruct.topinstance); splitstring(textpos, &thislabel->string, areastruct.topinstance); /* Because "splitstring" changes all the pointers, find the */ /* stringpart structures at textend and textpos positions. */ begpart = findstringpart(textend, NULL, thislabel->string, areastruct.topinstance); endpart = findstringpart(textpos, NULL, thislabel->string, areastruct.topinstance); /* Make the new segments for PARAM_START and PARAM_END. */ begpart = makesegment(&thislabel->string, begpart); endpart = makesegment(&thislabel->string, endpart); } else { /* full string */ makesegment(&thislabel->string, thislabel->string); begpart = thislabel->string; endpart = makesegment(&thislabel->string, NULL); } begpart->type = PARAM_START; begpart->data.string = (char *)malloc(1 + strlen(newkey)); strcpy(begpart->data.string, newkey); endpart->type = PARAM_END; endpart->data.string = (u_char *)NULL; /* Now move the sections of string to the object parameter */ newops = make_new_parameter(newkey); newops->next = topobject->params; topobject->params = newops; newops->type = XC_STRING; newops->which = P_SUBSTRING; newops->parameter.string = begpart->nextpart; begpart->nextpart = endpart->nextpart; endpart->nextpart = NULL; textend = 0; incr_changes(topobject); free(newkey); } /*--------------------------------------------------------------*/ /* Destroy the selected parameter in the indicated instance */ /*--------------------------------------------------------------*/ void destroyinst(objinstptr tinst, objectptr refobj, char *key) { oparamptr ops; short k; if (tinst->thisobject == refobj) { ops = match_instance_param(tinst, key); if (ops != NULL) { if (ops->type == XC_STRING) freelabel(ops->parameter.string); else if (ops->type == XC_EXPR) free(ops->parameter.expr); free_instance_param(tinst, ops); } } } /*--------------------------------------------------------------*/ /* Search and destroy the selected parameter in all instances */ /* of the specified object. */ /*--------------------------------------------------------------*/ void searchinst(objectptr topobj, objectptr refobj, char *key) { objinstptr tinst; genericptr *pgen; if (topobj == NULL) return; for (pgen = topobj->plist; pgen < topobj->plist + topobj->parts; pgen++) { if (IS_OBJINST(*pgen)) { tinst = TOOBJINST(pgen); destroyinst(tinst, refobj, key); } } } /*--------------------------------------------------------------*/ /* Destroy the object parameter with key "key" in the object */ /* "thisobj". This requires first tracking down and removing */ /* all instances of the parameter which may exist anywhere in */ /* the database. */ /*--------------------------------------------------------------*/ void free_object_param(objectptr thisobj, oparamptr thisparam) { int k, j, l = -1; liblistptr spec; oparamptr ops, lastops = NULL; char *key = thisparam->key; /* Find all instances of this object and remove any parameter */ /* substitutions which may have been made. */ for (k = 0; k < xobjs.pages; k++) { if (xobjs.pagelist[k]->pageinst != NULL) searchinst(xobjs.pagelist[k]->pageinst->thisobject, thisobj, key); } for (j = 0; j < xobjs.numlibs; j++) { for (k = 0; k < xobjs.userlibs[j].number; k++) { if (*(xobjs.userlibs[j].library + k) == thisobj) l = j; else searchinst(*(xobjs.userlibs[j].library + k), thisobj, key); } } /* Ensure that this parameter is not referred to in the undo records */ /* We could be kinder and gentler to the undo record here. . . */ flush_undo_stack(); /* Also check through all instances on the library page */ if (l >= 0) for (spec = xobjs.userlibs[l].instlist; spec != NULL; spec = spec->next) destroyinst(spec->thisinst, thisobj, key); /* Remove the parameter from the object itself, tidying up */ /* the linked list after it. */ for (ops = thisobj->params; ops != NULL; ops = ops->next) { if (ops == thisparam) { if (lastops != NULL) lastops->next = ops->next; else thisobj->params = ops->next; free(ops->key); free(ops); break; } lastops = ops; } incr_changes(thisobj); } /*--------------------------------------------------------------*/ /* Check if this string contains a parameter */ /*--------------------------------------------------------------*/ stringpart *searchparam(stringpart *tstr) { stringpart *rval = tstr; for (rval = tstr; rval != NULL; rval = rval->nextpart) if (rval->type == PARAM_START) break; return rval; } /*--------------------------------------------------------------*/ /* Remove parameterization from a label string or substring. */ /*--------------------------------------------------------------*/ void unmakeparam(labelptr thislabel, stringpart *thispart) { genericptr *pgen; labelptr plab; oparamptr ops; stringpart *strptr, *lastpart, *nextpart, *endpart, *newstr, *subs; char *key; Boolean is_last = True; /* make sure there is a parameter here */ if (thispart->type != PARAM_START) { Wprintf("There is no parameter here."); return; } key = thispart->data.string; /* Unparameterizing can cause a change in the string */ undrawtext(thislabel); /* Now we need to see if there are other places in the object where */ /* this parameter is used. If so, we delete only this particular */ /* instance of the parameter. Otherwise, we delete the parameter */ /* and also remove it from the parameters list. */ for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts; pgen++) { if (IS_LABEL(*pgen)) { plab = TOLABEL(pgen); for (strptr = plab->string; strptr != NULL; strptr = strptr->nextpart) { if (strptr->type == PARAM_START) { if ((strptr != thispart) && (!strcmp(strptr->data.string, key))) { is_last = False; break; } } } if (!is_last) break; } } ops = match_param(topobject, key); subs = ops->parameter.string; /* Copy the default parameter into the place we are unparameterizing */ newstr = NULL; newstr = stringcopy(subs); /* Delete the "PARAM_END" off of the copied string and link it into */ /* the existing string. */ for (endpart = newstr; endpart->nextpart->type != PARAM_END; endpart = endpart->nextpart); free(endpart->nextpart); endpart->nextpart = thispart->nextpart; /* Find the stringpart before the parameter call (if any) */ lastpart = NULL; for (strptr = thislabel->string; strptr != NULL && strptr != thispart; strptr = strptr->nextpart) { lastpart = strptr; } if (lastpart == NULL) thislabel->string = newstr; else lastpart->nextpart = newstr; free(strptr); /* Merge strings at boundaries, if possible. */ mergestring(endpart); mergestring(lastpart); redrawtext(thislabel); if (is_last) { freelabel(subs); free_object_param(topobject, ops); } } /*------------------------------------------------------*/ /* Wrapper for unmakeparam() */ /*------------------------------------------------------*/ void unparameterize(int mode) { short *fselect, ptype; int locpos; stringpart *strptr, *tmpptr, *lastptr; labelptr settext; if (mode >= 0) { ptype = (short)param_select[mode]; if (!checkselect(ptype)) select_element(ptype); if (!checkselect(ptype)) return; } else ptype = ALL_TYPES; if ((areastruct.selects == 1) && (mode == P_SUBSTRING) && textend > 0 && textend < textpos) { if (SELECTTYPE(areastruct.selectlist) != LABEL) return; /* Not a label */ settext = SELTOLABEL(areastruct.selectlist); strptr = findstringpart(textend, &locpos, settext->string, areastruct.topinstance); while (strptr != NULL && strptr->type != PARAM_END) strptr = strptr->nextpart; if (strptr == NULL) return; /* No parameters */ tmpptr = settext->string; lastptr = NULL; /* Search for parameter boundary, in case selection doesn't include */ /* the whole parameter or the parameter start marker. */ for (tmpptr = settext->string; tmpptr != NULL && tmpptr != strptr; tmpptr = nextstringpart(tmpptr, areastruct.topinstance)) if (tmpptr->type == PARAM_START) lastptr = tmpptr; /* Finish search, unlinking any parameter we might be inside */ for (; tmpptr != NULL; tmpptr = nextstringpart(tmpptr, areastruct.topinstance)); if (lastptr != NULL) unmakeparam(settext, lastptr); } else { for (fselect = areastruct.selectlist; fselect < areastruct.selectlist + areastruct.selects; fselect++) { if ((mode == P_SUBSTRING) && SELECTTYPE(fselect) == LABEL) { settext = SELTOLABEL(fselect); strptr = settext->string; while (strptr != NULL && strptr->type != PARAM_START) strptr = strptr->nextpart; if (strptr != NULL) unmakeparam(settext, strptr); } else if (mode == P_POSITION) { unmakenumericalp(topobject->plist + (*fselect), P_POSITION_X); unmakenumericalp(topobject->plist + (*fselect), P_POSITION_Y); } else unmakenumericalp(topobject->plist + (*fselect), mode); } setparammarks(NULL); } } /*--------------------------------------------------------------*/ /* Wrapper for makeparam() */ /*--------------------------------------------------------------*/ void parameterize(int mode, char *key) { short *fselect, ptype; labelptr settext; if (mode >= 0) { ptype = (short)param_select[mode]; if (!checkselect(ptype)) select_element(ptype); if (!checkselect(ptype)) return; } else ptype = ALL_TYPES; for (fselect = areastruct.selectlist; fselect < areastruct.selectlist + areastruct.selects; fselect++) { if ((mode == P_SUBSTRING) && (areastruct.selects == 1) && (SELECTTYPE(fselect) == LABEL)) { settext = SELTOLABEL(fselect); makeparam(settext, key); } else if (mode == P_POSITION) { makenumericalp(topobject->plist + (*fselect), P_POSITION_X, key); makenumericalp(topobject->plist + (*fselect), P_POSITION_Y, key); } else makenumericalp(topobject->plist + (*fselect), mode, key); } unselect_all(); setparammarks(NULL); } /*----------------------------------------------------------------------*/ /* Looks for a parameter overlapping the textend <--> textpos space. */ /* Returns True if there is a parameter in this space. */ /*----------------------------------------------------------------------*/ Boolean paramcross(objectptr tobj, labelptr tlab) { stringpart *firstptr, *lastptr; int locpos; lastptr = findstringpart(textpos, &locpos, tlab->string, areastruct.topinstance); /* This text position can't be inside another parameter */ for (firstptr = lastptr; firstptr != NULL; firstptr = firstptr->nextpart) if (firstptr->type == PARAM_END) return True; /* The area between textend and textpos cannot contain a parameter */ if (textend > 0) for (firstptr = findstringpart(textend, &locpos, tlab->string, areastruct.topinstance); firstptr != lastptr; firstptr = firstptr->nextpart) if (firstptr->type == PARAM_START || firstptr->type == PARAM_END) return True; return False; } /*----------------------------------------------------------------------*/ /* Check whether this page object was entered via a library page */ /*----------------------------------------------------------------------*/ int checklibtop() { int i; pushlistptr thispush; for (thispush = areastruct.stack; thispush != NULL; thispush = thispush->next) if ((i = is_library(thispush->thisinst->thisobject)) >= 0) return i; return -1; } /*----------------------------------------------------------------------*/ /* Remove all parameters from an object instance */ /* (Reverts all parameters to default value) */ /*----------------------------------------------------------------------*/ void removeinstparams(objinstptr thisinst) { oparamptr ops; while (thisinst->params != NULL) { ops = thisinst->params; thisinst->params = ops->next; free(ops->key); if (ops->type == XC_STRING) freelabel(ops->parameter.string); else if (ops->type == XC_EXPR) free(ops->parameter.expr); free(ops); } } /*----------------------------------------------------------------------*/ /* Remove all parameters from an object. */ /*----------------------------------------------------------------------*/ void removeparams(objectptr thisobj) { oparamptr ops; while (thisobj->params != NULL) { ops = thisobj->params; thisobj->params = ops->next; free(ops->key); if (ops->type == XC_STRING) freelabel(ops->parameter.string); else if (ops->type == XC_EXPR) free(ops->parameter.expr); free(ops); } thisobj->params = NULL; } /*-----------------------------------------------------------------------*/