Logo Search packages:      
Sourcecode: xcircuit version File versions

parameter.c

/*----------------------------------------------------------------------*/
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#ifdef TCL_WRAPPER 
#include <tk.h>
#else
#include "Xw/Xw.h"
#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;
extern Widget     menuwidgets[];
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[] = {
   POLYGON | SPLINE | LABEL | OBJECT | ARC,     /* P_POSITION */
   LABEL,                           /* P_SUBSTRING */
   POLYGON | SPLINE | LABEL | OBJECT | ARC,     /* P_POSITION_X */
   POLYGON | SPLINE | LABEL | OBJECT | 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 | OBJECT,                        /* P_ROTATION */
   LABEL | OBJECT,                        /* P_SCALE */
   POLYGON | SPLINE | ARC | PATH,         /* P_LINEWIDTH */
   SEL_ANY                          /* P_COLOR (tbd) */
};

#ifdef TCL_WRAPPER
xcWidget *param_buttons[] = {
   /* To be done---map buttons to Tk_Windows! */
};
#else
Widget *param_buttons[] = {
   &ParametersPositionButton,       /* P_POSITION */
   &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 (does not exist yet) P_COLOR */
};
#endif

/*----------------------------------------------*/
/* Draw a circle at all parameter positions     */
/*----------------------------------------------*/

void indicateparams(genericptr thiselem)
{
   int i, j, k, which;
   oparamptr ops;

   if (thiselem != NULL) {
      for (i = 0; i < thiselem->num_params; i++) {
         j = thiselem->passed[i].paramno;
       k = thiselem->passed[i].pointno;
         ops = topobject->params[j];
       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 OBJECT:
                   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;

   /* 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_SetVar(xcinterp, (char *)paramvars[i], "false", TCL_NAMESPACE_ONLY);

   /* For each parameter declared, set the corresponding Tcl variable */
   if (thiselem != NULL) {
      for (i = 0; i < thiselem->num_params; i++) {
         j = thiselem->passed[i].paramno;
         ops = topobject->params[j];
         Tcl_SetVar(xcinterp, (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;

   /* 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 (i = 0; i < thiselem->num_params; i++) {
         j = thiselem->passed[i].paramno;
         ops = topobject->params[j];
         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;
   float fval;
   oparamptr ops, ips;
   XPoint *setpt;
   Boolean needrecalc;  /* for arcs and splines */
   int retval = -1;

   for (eptr = thisobj->plist; eptr < thisobj->plist + thisobj->parts; eptr++) {

      needrecalc = False;
      thiselem = *eptr;
      if (thiselem->num_params == 0) continue;  /* Nothing to substitute */

      for (i = 0; i < thiselem->num_params; i++) {
         j = thiselem->passed[i].paramno;
       k = thiselem->passed[i].pointno;
         if ((pinst != NULL) && (pinst->params != NULL))
            ips = pinst->params[j];
         else
          ips = NULL;
         ops = thisobj->params[j];

       ival = (ips != NULL) ? ips->parameter.ivalue : ops->parameter.ivalue;
         fval = (ips != NULL) ? ips->parameter.fvalue : ops->parameter.fvalue;

         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 OBJECT:
                 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 OBJECT:
                 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 OBJECT:
                 TOOBJINST(&thiselem)->rotation = ival;
                 break;
             }
             break;
          case P_SCALE:
             retval = max(retval, 1);
             switch(thiselem->type) {
                case LABEL:
                 TOLABEL(&thiselem)->scale = fval;
                 break;
                case OBJECT:
                 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;
         }
      }
      /* 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 (celem->type == 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->num_params > 0) 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, *ival;
   float *fval;
   XPoint *setpt;

   pinst = (thisinst == areastruct.topinstance) ? areastruct.topinstance : thisinst;
   thisobj = (pinst == NULL) ? topobject : pinst->thisobject;

   /* make sure that all instance values exist */
   if (pinst != NULL) copyparams(pinst, pinst);

   for (eptr = thisobj->plist; eptr < thisobj->plist + thisobj->parts; eptr++) {

      thiselem = *eptr;
      if (thiselem->num_params == 0) continue;  /* Nothing to write back */

      for (i = 0; i < thiselem->num_params; i++) {

         epp = thiselem->passed + i;
       j = epp->paramno;
       k = epp->pointno;

         if ((pinst != NULL) && (pinst->params != NULL))
            ips = pinst->params[j];
         else
          ips = NULL;
         ops = thisobj->params[j];

       ival = (ips != NULL) ? &ips->parameter.ivalue : &ops->parameter.ivalue;
       fval = (ips != NULL) ? &ips->parameter.fvalue : &ops->parameter.fvalue;

         switch(ops->which) {
          case P_POSITION_X:
             switch(thiselem->type) {
                case OBJECT:
                 *ival = TOOBJINST(&thiselem)->position.x;
                 break;
                case LABEL:
                 *ival = TOLABEL(&thiselem)->position.x;
                 break;
                case POLYGON:
                   setpt = TOPOLY(&thiselem)->points + k;
                 *ival = setpt->x;
                 break;
                case ARC:
                 *ival = TOARC(&thiselem)->position.x;
                 break;
                case SPLINE:
                 *ival = TOSPLINE(&thiselem)->ctrl[k].x;
                 break;
             }
             break;
          case P_POSITION_Y:
             switch(thiselem->type) {
                case OBJECT:
                 *ival = TOOBJINST(&thiselem)->position.y;
                 break;
                case LABEL:
                 *ival = TOLABEL(&thiselem)->position.y;
                 break;
                case POLYGON:
                   setpt = TOPOLY(&thiselem)->points + k;
                 *ival = setpt->y;
                 break;
                case ARC:
                 *ival = TOARC(&thiselem)->position.y;
                 break;
                case SPLINE:
                 *ival = TOSPLINE(&thiselem)->ctrl[k].y;
                 break;
             }
             break;
          case P_STYLE:
             switch(thiselem->type) {
                case POLYGON:
                 *ival = TOPOLY(&thiselem)->style;
                 break;
                case ARC:
                 *ival = TOARC(&thiselem)->style;
                 break;
                case SPLINE:
                 *ival = TOSPLINE(&thiselem)->style;
                 break;
                case PATH:
                 *ival = TOPATH(&thiselem)->style;
                 break;
             }
             break;
          case P_JUSTIFY:
             switch(thiselem->type) {
                case LABEL:
                 *ival = TOLABEL(&thiselem)->justify;
                 break;
             }
             break;
          case P_ANGLE1:
             switch(thiselem->type) {
                case ARC:
                 *fval = TOARC(&thiselem)->angle1;
                 break;
             }
             break;
          case P_ANGLE2:
             switch(thiselem->type) {
                case ARC:
                 *fval = TOARC(&thiselem)->angle1;
                 break;
             }
             break;
          case P_RADIUS:
             switch(thiselem->type) {
                case ARC:
                 *ival = TOARC(&thiselem)->radius;
                 break;
             }
             break;
          case P_MINOR_AXIS:
             switch(thiselem->type) {
                case ARC:
                 *ival = TOARC(&thiselem)->yaxis;
                 break;
             }
             break;
          case P_ROTATION:
             switch(thiselem->type) {
                case OBJECT:
                 *ival = TOOBJINST(&thiselem)->rotation;
                 break;
                case LABEL:
                 *ival = TOLABEL(&thiselem)->rotation;
                 break;
             }
             break;
          case P_SCALE:
             switch(thiselem->type) {
                case OBJECT:
                 *fval = TOOBJINST(&thiselem)->scale;
                 break;
                case LABEL:
                 *fval = TOLABEL(&thiselem)->scale;
                 break;
             }
             break;
          case P_LINEWIDTH:
             switch(thiselem->type) {
                case POLYGON:
                 *fval = TOPOLY(&thiselem)->width;
                 break;
                case ARC:
                 *fval = TOARC(&thiselem)->width;
                 break;
                case SPLINE:
                 *fval = TOSPLINE(&thiselem)->width;
                 break;
                case PATH:
                 *fval = TOPATH(&thiselem)->width;
                 break;
             }
             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);
}

/*------------------------------------------------------*/
/* 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 (i = 0; i < thisobj->num_params; i++) {
      ops = thisobj->params[i];
      ips = thisinst->params[i];
      if (ips == NULL) {
       nullparms++;
       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(ips);
             thisinst->params[i] = NULL;
             nullparms++;
          }
          break;
       case XC_INT: case XC_FLOAT:
          if (ops->parameter.ivalue != ips->parameter.ivalue) {
             ops->parameter.ivalue = ips->parameter.ivalue;
             free(ips);
             thisinst->params[i] = NULL;
             nullparms++;
          }
          break;
      }
   }

   /* Object instance takes all default values---remove the params list */

   if (nullparms == thisobj->num_params) {
      free(thisinst->params);
      thisinst->params = NULL;
   }
}

/*------------------------------------------------------*/
/* 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, nullparms = 0;

   /* 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 or top page:  "
                  "changing default value\n"); */
         replaceparams(thisinst);
         return;
      }
   }

   thisobj = thisinst->thisobject;

   for (i = 0; i < thisobj->num_params; i++) {
      ops = thisobj->params[i];
      ips = thisinst->params[i];
      if (ips == NULL) {
       nullparms++;
       continue;  /* this parameter is already default */
      }

      switch(ops->type) {
       case XC_STRING:
          if (!stringcomp(ops->parameter.string, ips->parameter.string)) {
             freelabel(ips->parameter.string);
             free(ips);
             thisinst->params[i] = NULL;
             nullparms++;
          }
          break;
       case XC_INT: case XC_FLOAT:
          if (ops->parameter.ivalue == ips->parameter.ivalue) {
             free(ips);
             thisinst->params[i] = NULL;
             nullparms++;
          }
          break;
      }
   }

   /* Object instance takes all default values---remove the params list */

   if (nullparms == thisobj->num_params) {
      free(thisinst->params);
      thisinst->params = NULL;
   }
   else {
      /* Object must recompute bounding box if any instance */
      /* uses a non-default parameter.                      */

      calcbboxvalues(thisinst, NULL);
   }
}

/*------------------------------------------------------*/
/* Fill any NULL instance parameters with the values  */
/* from the calling instance, or from the instance    */
/* object's defaults if newinst = callinst.           */ 
/*------------------------------------------------------*/

void copyparams(objinstptr newinst, objinstptr callinst)
{
   objectptr callobj;
   oparamptr *cparams;
   int i;

   if (callinst == NULL) return;

   callobj = callinst->thisobject;
   cparams = callinst->params;

   if (callobj->num_params == 0) return;

   if (newinst->params == NULL) {
      newinst->params = (oparamptr *) malloc(callobj->num_params * sizeof(oparamptr));
      for (i = 0; i < callobj->num_params; i++)
       newinst->params[i] = NULL;
   }

   for (i = 0; i < callobj->num_params; i++) {
      if (newinst->params[i] == NULL) {
         newinst->params[i] = (oparamptr) malloc(sizeof(oparam));
         newinst->params[i]->type = callobj->params[i]->type;
         newinst->params[i]->which = callobj->params[i]->which;
         switch(callobj->params[i]->type) {
          case XC_STRING:
             if (newinst == callinst || callinst->params == NULL ||
                  callinst->params[i] == NULL)
                newinst->params[i]->parameter.string =
                     stringcopy(callobj->params[i]->parameter.string);
             else
                newinst->params[i]->parameter.string =
                     stringcopy(callinst->params[i]->parameter.string);
             break;
          case XC_INT: case XC_FLOAT:
             if (newinst == callinst || callinst->params == NULL ||
                  callinst->params[i] == NULL)
                newinst->params[i]->parameter.ivalue =
                     callobj->params[i]->parameter.ivalue;
             else
                newinst->params[i]->parameter.ivalue =
                     callinst->params[i]->parameter.ivalue;
             break;
          default:
             Fprintf(stderr, "Error:  bad parameter\n");
             break;
       }
      }
   }
}

/*--------------------------------------------------------------*/
/* make a numerical (integer or float) parameter            */
/*--------------------------------------------------------------*/

void makenumericalp(genericptr *gelem, u_int mode)
{
   oparamptr ops;
   eparamptr epp;
   XPoint *pptr;

   /* Parameterized strings are handled by makeparam() */
   if ((*gelem)->type == LABEL && mode == P_SUBSTRING) {
      makeparam(TOLABEL(gelem));
      return;
   }

   /* 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 the parameter doesn't already exist.     */

   if ((*gelem)->num_params > 0) {
      for (epp = (*gelem)->passed; epp < (*gelem)->passed
            + (*gelem)->num_params; epp++) {
       ops = *(topobject->params + epp->paramno);
       if (ops->which == (u_char)mode) {
          Fprintf(stderr, "Cannot duplicate a parameter!\n");
          return;
       }
      }
   }

   if (topobject->params == NULL) {
      topobject->num_params = 1;
      topobject->params = (oparamptr *)malloc(sizeof(oparamptr));
   }
   else {
      topobject->num_params++;
      topobject->params = (oparamptr *)realloc(topobject->params,
            topobject->num_params * sizeof(oparamptr));
   }
   *(topobject->params + topobject->num_params - 1) = (oparamptr)
      malloc(sizeof(oparam));

   initallinstances(topobject, topobject->num_params - 1);

   ops = *(topobject->params + topobject->num_params - 1);
   ops->type = XC_INT;        /* most commonly used value */
   ops->which = (u_char)mode; /* what kind of parameter */

   /* Add the parameter to the element's parameter list */

   if ((*gelem)->num_params == 0) {
      (*gelem)->num_params = 1;
      (*gelem)->passed = (eparamptr)malloc((*gelem)->num_params *
            sizeof(eparam));
   }
   else {
      (*gelem)->num_params++;
      (*gelem)->passed = (eparamptr)realloc((*gelem)->passed,
            (*gelem)->num_params * sizeof(eparam));
   }
   epp = (*gelem)->passed + (*gelem)->num_params - 1;
   epp->paramno = (u_char)(topobject->num_params - 1);
   epp->pointno = 0;

   switch((*gelem)->type) {
      case LABEL:
       switch(mode) {
          case P_POSITION_X:
             ops->parameter.ivalue = (int)TOLABEL(gelem)->position.x;
             break;
          case P_POSITION_Y:
             ops->parameter.ivalue = (int)TOLABEL(gelem)->position.y;
             break;
          case P_JUSTIFY:
             ops->parameter.ivalue = (int)TOLABEL(gelem)->justify;
             break;
          case P_ROTATION:
             ops->parameter.ivalue = (int)TOLABEL(gelem)->rotation;
             break;
          case P_SCALE:
             ops->type = XC_FLOAT;
             ops->parameter.fvalue = TOLABEL(gelem)->scale;
             break;
       }
       break;
      case ARC:
       switch(mode) {
          case P_POSITION_X:
             ops->parameter.ivalue = (int)TOARC(gelem)->position.x;
             break;
          case P_POSITION_Y:
             ops->parameter.ivalue = (int)TOARC(gelem)->position.y;
             break;
          case P_ANGLE1:
             ops->type = XC_FLOAT;
             ops->parameter.fvalue = TOARC(gelem)->angle1;
             break;
          case P_ANGLE2:
             ops->type = XC_FLOAT;
             ops->parameter.fvalue = TOARC(gelem)->angle2;
             break;
          case P_RADIUS:
             ops->parameter.ivalue = (int)TOARC(gelem)->radius;
             break;
          case P_MINOR_AXIS:
             ops->parameter.ivalue = (int)TOARC(gelem)->yaxis;
             break;
          case P_STYLE:
             ops->parameter.ivalue = (int)TOARC(gelem)->style;
             break;
          case P_LINEWIDTH:
             ops->type = XC_FLOAT;
             ops->parameter.fvalue = TOARC(gelem)->width;
             break;
       }
       break;
      case OBJECT:
       switch(mode) {
          case P_POSITION_X:
             ops->parameter.ivalue = (int)TOOBJINST(gelem)->position.x;
             break;
          case P_POSITION_Y:
             ops->parameter.ivalue = (int)TOOBJINST(gelem)->position.y;
             break;
          case P_ROTATION:
             ops->parameter.ivalue = (int)TOOBJINST(gelem)->rotation;
             break;
          case P_SCALE:
             ops->type = XC_FLOAT;
             ops->parameter.fvalue = TOOBJINST(gelem)->scale;
             break;
       }
       break;
      case POLYGON:
       switch(mode) {
          case P_POSITION_X:
             pptr = TOPOLY(gelem)->points + areastruct.editcycle; 
             ops->parameter.ivalue = (int)pptr->x;
             epp->pointno = areastruct.editcycle;
             break;
          case P_POSITION_Y:
             pptr = TOPOLY(gelem)->points + areastruct.editcycle; 
             ops->parameter.ivalue = (int)pptr->y;
             epp->pointno = areastruct.editcycle;
             break;
          case P_STYLE:
             ops->parameter.ivalue = (int)TOPOLY(gelem)->style;
             break;
          case P_LINEWIDTH:
             ops->type = XC_FLOAT;
             ops->parameter.fvalue = TOPOLY(gelem)->width;
             break;
       }
       break;
      case SPLINE:
       switch(mode) {
          case P_POSITION_X:
             pptr = TOSPLINE(gelem)->ctrl + areastruct.editcycle; 
             ops->parameter.ivalue = (int)pptr->x;
             epp->pointno = areastruct.editcycle;
             break;
          case P_POSITION_Y:
             pptr = TOSPLINE(gelem)->ctrl + areastruct.editcycle; 
             ops->parameter.ivalue = (int)pptr->y;
             epp->pointno = areastruct.editcycle;
             break;
          case P_STYLE:
             ops->parameter.ivalue = (int)TOSPLINE(gelem)->style;
             break;
          case P_LINEWIDTH:
             ops->type = XC_FLOAT;
             ops->parameter.fvalue = TOSPLINE(gelem)->width;
             break;
       }
       break;
      case PATH:
       switch(mode) {
          case P_STYLE:
             ops->parameter.ivalue = (int)TOPATH(gelem)->style;
             break;
          case P_LINEWIDTH:
             ops->type = XC_FLOAT;
             ops->parameter.fvalue = TOPATH(gelem)->width;
             break;
       }
       break;
   }
   incr_changes(topobject);
}

/*--------------------------------------------------------------*/
/* remove a numerical (integer or float) parameter          */
/*--------------------------------------------------------------*/

void unmakenumericalp(genericptr *gelem, u_int mode)
{
   /* Parameterized strings are handled by makeparam() */
   if (((*gelem)->type == LABEL) && (mode == P_SUBSTRING)) {
      unparameterize(mode);
      setparammarks(*gelem);
      return;
   }

   /* else. . . (to be done) */
}

/*--------------------------------------------------------------*/
/* Insert an existing parameter into a string.              */
/*--------------------------------------------------------------*/

void insertparam()
{
   labelptr tlab;
   oparamptr ops;
   short i, nparms = 0;
   int result, selparm = -1;

   /* Don't allow nested parameters */

   tlab = TOLABEL(EDITPART);
   if (paramcross(topobject, tlab)) {
      Wprintf("Parameters cannot be nested!");
      return;
   }

   /* 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 (i = 0; i < topobject->num_params; i++) {
      ops = *(topobject->params + i);
      if (ops->type == XC_STRING) {
       nparms++;
       selparm = i;
      }
   }
   if (nparms > 1) {
      char *newstr, *sptr;
      char *sstart = (char *)malloc(250);
      u_char *parammap = (u_char *)malloc(nparms);
      nparms = 0;
      strcpy(sstart, "Choose: ");
      sptr = sstart + 8;
      for (i = 0; i < topobject->num_params; i++) {
       ops = *(topobject->params + i);
       if (ops->type == XC_STRING) {
          parammap[nparms] = i;
          nparms++;
          if (nparms != 1) {
             strcat(sptr, ", ");
             sptr += 2;
          }
          sprintf(sptr, "%d = <", nparms);
          newstr = stringprint(ops->parameter.string, NULL);
          strcat(sptr, newstr);
          free(newstr);
          newstr = NULL;
            sptr += strlen(sptr);
       }
      }
      Wprintf(sstart);
      free(sstart);

#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;
      }
      result = Tcl_GetIntFromObj(xcinterp, Tcl_GetObjResult(xcinterp), &selparm);
      if (result != TCL_OK) {
       Tcl_SetResult(xcinterp, "Bad parameter number.", NULL);
       selparm = -1;
      }
      /* Relate index in list (string parameters only) to index   */
      /* over all parameters.                         */

      if (selparm >= 0) selparm = parammap[selparm];
#else
      /* The non-Tcl version is a bit quirky, but it works. */

      selparm = parammap[getkeynum()];
#endif
      free(parammap);
   }

   if ((selparm >= 0) && (selparm < topobject->num_params))
      labeltext(PARAM_START, (char *)&selparm);
   else
      Wprintf("Parameter number nonexistent.");
}

/*--------------------------------------------------------------*/
/* Parameterize a label string.                             */
/*--------------------------------------------------------------*/

void makeparam(labelptr thislabel)
{
   oparamptr ops;
   stringpart *begpart, *endpart;

   /* 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;
   }

   /* First, place PARAM_START and PARAM_END structures at the    */
   /* intended parameter boundaries                   */

   if (textend > 0 && textend < textpos) {      /* partial string */
      begpart = splitstring(textend, &thislabel->string, areastruct.topinstance);
      endpart = splitstring(textpos, &thislabel->string, areastruct.topinstance);

      /* note that "partial string" may overlap the beginning     */
      /* or the end, so deal with these instances.          */

      begpart = makesegment(&thislabel->string, (begpart->nextpart &&
            textend > 1) ? begpart->nextpart : begpart);
      endpart = makesegment(&thislabel->string, (endpart) ?
            endpart->nextpart : NULL);
   }
   else {                     /* full string */
      makesegment(&thislabel->string, thislabel->string);
      begpart = thislabel->string;
      endpart = makesegment(&thislabel->string, NULL);
   }
   begpart->type = PARAM_START;
   begpart->data.paramno = (topobject->params == NULL) ? 0 :
            topobject->num_params;
   endpart->type = PARAM_END;

   /* Now move the sections of string to the object parameter */

   if (topobject->params == NULL) {
      topobject->num_params = 1;
      topobject->params = (oparamptr *)malloc(sizeof(oparamptr));
   }
   else {
      topobject->num_params++;
      topobject->params = (oparamptr *)realloc(topobject->params,
            topobject->num_params * sizeof(oparamptr));

      /* We have added a parameter.  This requires that all instances   */
      /* of the object which do not have NULL params must initialize    */
      /* and NULL the pointer to the new parameter.               */

      initallinstances(topobject, topobject->num_params - 1);
   }
   *(topobject->params + topobject->num_params - 1) = (oparamptr)
      malloc(sizeof(oparam));


   ops = *(topobject->params + topobject->num_params - 1);
   ops->type = XC_STRING;
   ops->which = P_SUBSTRING;
   ops->parameter.string = begpart->nextpart;
   begpart->nextpart = endpart->nextpart;
   endpart->nextpart = NULL;

   textend = 0;
   incr_changes(topobject);
}

/*------------------------------------------------------*/
/* Search and initialize the selected parameter in all      */
/* instances of the specified object.                 */
/*------------------------------------------------------*/

void initinst(objectptr topobj, objectptr refobj, short pnum)
{
   objinstptr tinst;
   genericptr *pgen;

   if (topobj == NULL) return;
   for (pgen = topobj->plist; pgen < topobj->plist + topobj->parts; pgen++) {
      if ((*pgen)->type == OBJECT) {
       tinst = TOOBJINST(pgen);
       if (tinst->thisobject == refobj) {
          if (tinst->params != NULL) {
             /* Make sure we've allocated enough space for this parameter */
             tinst->params = (oparamptr *)realloc(tinst->params,
                  tinst->thisobject->num_params * sizeof(oparamptr));
             *(tinst->params + pnum) = NULL;
          }
       }
      }
   }
}

/*--------------------------------------------------------------*/
/* Destroy the selected parameter in the indicated instance       */
/*--------------------------------------------------------------*/

void destroyinst(objinstptr tinst, objectptr refobj, short pnum)
{
   oparamptr *ops;
   short k;

   if (tinst->thisobject == refobj) {
      if (tinst->thisobject->num_params > 0) {
       if (tinst->params != NULL) {
          ops = tinst->params + pnum;
          if (*ops != NULL)
             freelabel((*ops)->parameter.string);
          free(*ops);
          *ops = NULL;
          if (pnum == 0) {
             free(tinst->params);
             tinst->params = NULL;
          }
          else {
             /* Re-order the remaining parameters */
             for (k = pnum; k < tinst->thisobject->num_params - 1; k++)
                *(tinst->params + k) = *(tinst->params + k + 1);
          }
       }
      }
   }
}

/*--------------------------------------------------------------*/
/* Search and destroy the selected parameter in all instances     */
/* of the specified object.                           */
/*--------------------------------------------------------------*/

void searchinst(objectptr topobj, objectptr refobj, short pnum)
{
   objinstptr tinst;
   genericptr *pgen;

   if (topobj == NULL) return;

   for (pgen = topobj->plist; pgen < topobj->plist + topobj->parts; pgen++) {
      if ((*pgen)->type == OBJECT) {
       tinst = TOOBJINST(pgen);
       destroyinst(tinst, refobj, pnum);
      }
   }
}

/*--------------------------------------------------------------*/
/* Add a (NULL) parameter pointer to every instance of the  */
/* indicated object                                   */
/*--------------------------------------------------------------*/

void initallinstances(objectptr thisobj, int p)
{
   int j, k;

   for (k = 0; k < xobjs.pages; k++) {
      if (xobjs.pagelist[k]->pageinst != NULL)
         initinst(xobjs.pagelist[k]->pageinst->thisobject, thisobj, p);
   }
   for (j = 0; j < xobjs.numlibs; j++) {
      for (k = 0; k < xobjs.userlibs[j].number; k++) {
         initinst(*(xobjs.userlibs[j].library + k), thisobj, p);
      }
      initinst(xobjs.libtop[LIBRARY + j]->thisobject, thisobj, p);
   }
   for (k = 0; k < xobjs.delbuffer.number; k++) {
      initinst(*(xobjs.delbuffer.library + k), thisobj, p);
   }
}

/*--------------------------------------------------------------*/
/* 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;
   liblistptr spec;
   stringpart *strptr, *lastpart, *nextpart, *endpart, *newstr, *subs;
   int p, j, k, l = -1;
   Boolean is_last = True;
   
   /* make sure there is a parameter here */

   if (thispart->type != PARAM_START) {
      Wprintf("There is no parameter here.");
      return;
   }
   p = thispart->data.paramno;

   /* 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 ((*pgen)->type == LABEL) {
       plab = TOLABEL(pgen);
       for (strptr = plab->string; strptr != NULL; strptr = strptr->nextpart) {
          if (strptr->type == PARAM_START) {
             if ((strptr != thispart) && (strptr->data.paramno == p)) {
              is_last = False;
              break;
             }
          }
       }
       if (!is_last) break;
      }
   }

   ops = *(topobject->params + p);
   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) return;

   /* 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, topobject, p);
   }
   for (j = 0; j < xobjs.numlibs; j++) {
      for (k = 0; k < xobjs.userlibs[j].number; k++) {
       if (*(xobjs.userlibs[j].library + k) == topobject)
          l = j;
       else
            searchinst(*(xobjs.userlibs[j].library + k), topobject, p);
      }
   }
   for (k = 0; k < xobjs.delbuffer.number; k++) {
      searchinst(*(xobjs.delbuffer.library + k), topobject, p);
   }

   /* 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, topobject, p);

   free(ops);
   
   /* Re-order the remaining parameters */

   for (k = p; k < topobject->num_params - 1; k++)
      *(topobject->params + k) = *(topobject->params + k + 1);

   /* Decrement the number of parameters */

   if (--topobject->num_params == 0) {
      free(topobject->params);
      topobject->params = NULL;
   }

   /* decrement the parameter ID number of all parameters in all strings */
   /* of this object whose parameter IDs are greater.              */

   for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts;
            pgen++) {
      if ((*pgen)->type == LABEL) {
       plab = TOLABEL(pgen);
       for (strptr = plab->string; strptr != NULL; strptr = strptr->nextpart) {
          if (strptr->type == PARAM_START)
             if (strptr->data.paramno > p)
              strptr->data.paramno--;
       }
      }
   }
   freelabel(subs);
   incr_changes(topobject);
}

/*------------------------------------------------------*/
/* 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)) objectselect(ptype);
      if (!checkselect(ptype)) return;
   }
   else
      ptype = SEL_ANY;

   if ((areastruct.selects == 1) && (ptype & LABEL) && 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 ((ptype & LABEL) && 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)
{
   short *fselect, ptype;
   labelptr settext;

   if (mode >= 0) {
      ptype = (short)param_select[mode];
      if (!checkselect(ptype)) objectselect(ptype);
      if (!checkselect(ptype)) return;
   }
   else
      ptype = SEL_ANY;

   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);
      }
      else if (mode == P_POSITION) {
       makenumericalp(topobject->plist + (*fselect), P_POSITION_X);
       makenumericalp(topobject->plist + (*fselect), P_POSITION_Y);
      }
      else
       makenumericalp(topobject->plist + (*fselect), mode);
   }
   objectdeselect();
   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 removeinst(objinstptr thisinst)
{
   free(thisinst->params);
   thisinst->params = NULL;
}

/*----------------------------------------------------------------------*/
/* Remove all parameters from an object.                    */
/*----------------------------------------------------------------------*/

void removeparams(objectptr thisobj)
{
   int i;
   oparamptr pptr;

   for (i = 0; i < thisobj->num_params; i++) {
      pptr = thisobj->params[i];
      if (pptr->type == XC_STRING)
       freelabel(pptr->parameter.string);
      free(pptr);
   }
   free(thisobj->params);
   thisobj->params = NULL;
   thisobj->num_params = 0;
}

/*-----------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index