1 /*----------------------------------------------------------------------*/
2 /* parameter.c								*/
3 /* Copyright (c) 2002  Tim Edwards, Johns Hopkins University        	*/
4 /*----------------------------------------------------------------------*/
5 
6 /*----------------------------------------------------------------------*/
7 /*      written by Tim Edwards, 10/26/99    				*/
8 /*	revised for segmented strings, 3/8/01				*/
9 /*----------------------------------------------------------------------*/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #ifndef XC_WIN32
16 #include <X11/Intrinsic.h>
17 #include <X11/StringDefs.h>
18 #endif
19 
20 #ifdef TCL_WRAPPER
21 #include <tk.h>
22 #else
23 #ifndef XC_WIN32
24 #include "Xw/Xw.h"
25 #endif
26 #endif
27 
28 /*----------------------------------------------------------------------*/
29 /* Local includes							*/
30 /*----------------------------------------------------------------------*/
31 
32 #include "xcircuit.h"
33 #include "menudep.h"
34 
35 /*----------------------------------------------------------------------*/
36 /* Function prototype declarations                                      */
37 /*----------------------------------------------------------------------*/
38 #include "prototypes.h"
39 
40 /*----------------------------------------------------------------------*/
41 /* Externally declared global variables					*/
42 /*----------------------------------------------------------------------*/
43 
44 #ifdef TCL_WRAPPER
45 extern Tcl_Interp *xcinterp;
46 #endif
47 extern Globaldata xobjs;
48 extern XCWindowData *areawin;
49 #ifndef TCL_WRAPPER
50 extern Widget     menuwidgets[];
51 #endif
52 extern char _STR[150];
53 
54 /*----------------------------------------------------------------------*/
55 /* The following u_char array matches parameterization types to element	*/
56 /* types which are able to accept the given parameterization.		*/
57 /*----------------------------------------------------------------------*/
58 
59 u_char param_select[] = {
60    ALL_TYPES,					/* P_NUMERIC */
61    LABEL,					/* P_SUBSTRING */
62    POLYGON | SPLINE | LABEL | OBJINST | ARC,	/* P_POSITION_X */
63    POLYGON | SPLINE | LABEL | OBJINST | ARC,	/* P_POSITION_Y */
64    POLYGON | SPLINE | ARC | PATH,		/* P_STYLE */
65    LABEL,					/* P_ANCHOR */
66    ARC,						/* P_ANGLE1 */
67    ARC,						/* P_ANGLE2 */
68    ARC,						/* P_RADIUS */
69    ARC,						/* P_MINOR_AXIS */
70    LABEL | OBJINST,				/* P_ROTATION */
71    LABEL | OBJINST,				/* P_SCALE */
72    POLYGON | SPLINE | ARC | PATH,		/* P_LINEWIDTH */
73    ALL_TYPES, 					/* P_COLOR */
74    ALL_TYPES, 					/* P_EXPRESSION */
75    POLYGON | SPLINE | LABEL | OBJINST | ARC 	/* P_POSITION */
76 };
77 
78 #ifdef TCL_WRAPPER
79 #ifndef _MSC_VER
80 xcWidget *param_buttons[] = { NULL              /* (jdk) */
81    /* To be done---map buttons to Tk_Windows! */
82 };
83 #else
84 xcWidget *param_buttons[];
85 #endif
86 #else
87 Widget *param_buttons[] = {
88    &ParametersNumericButton,		/* P_NUMERIC */
89    &ParametersSubstringButton,		/* P_SUBSTRING */
90    &ParametersPositionButton,		/* P_POSITION_X */
91    &ParametersPositionButton,		/* P_POSITION_Y */
92    &ParametersStyleButton,		/* P_STYLE */
93    &ParametersAnchoringButton,		/* P_ANCHOR */
94    &ParametersStartAngleButton,		/* P_ANGLE1 */
95    &ParametersEndAngleButton,		/* P_ANGLE2 */
96    &ParametersRadiusButton,		/* P_RADIUS */
97    &ParametersMinorAxisButton,		/* P_MINOR_AXIS */
98    &ParametersRotationButton,		/* P_ROTATION */
99    &ParametersScaleButton,		/* P_SCALE */
100    &ParametersLinewidthButton,		/* P_LINEWIDTH */
101    &ParametersColorButton,		/* P_COLOR */
102    &ParametersPositionButton,		/* P_POSITION */
103 };
104 #endif
105 
106 /*----------------------------------------------------------------------*/
107 /* Basic routines for matching parameters by key values.  Note that	*/
108 /* this really, really ought to be replaced by a hash table search!	*/
109 /*----------------------------------------------------------------------*/
110 
111 /*----------------------------------------------------------------------*/
112 /* Check for the existance of a parameter with key "key" in object	*/
113 /* "thisobj".   Return true if the parameter exists.			*/
114 /*----------------------------------------------------------------------*/
115 
check_param(objectptr thisobj,char * key)116 Boolean check_param(objectptr thisobj, char *key)
117 {
118    oparamptr tops;
119 
120    for (tops = thisobj->params; tops != NULL; tops = tops->next)
121       if (!strcmp(tops->key, key))
122 	 return TRUE;
123 
124    return FALSE;
125 }
126 
127 /*----------------------------------------------------------------------*/
128 /* Create a new parameter;  allocate memory for the parameter and the	*/
129 /* key.									*/
130 /*----------------------------------------------------------------------*/
131 
make_new_parameter(char * key)132 oparamptr make_new_parameter(char *key)
133 {
134    oparamptr newops;
135 
136    newops = (oparamptr)malloc(sizeof(oparam));
137    newops->next = NULL;
138    newops->key = (char *)malloc(1 + strlen(key));
139    strcpy(newops->key, key);
140    return newops;
141 }
142 
143 /*----------------------------------------------------------------------*/
144 /* Create a new element (numeric) parameter.  Fill in essential values	*/
145 /*----------------------------------------------------------------------*/
146 
make_new_eparam(char * key)147 eparamptr make_new_eparam(char *key)
148 {
149    eparamptr newepp;
150 
151    newepp = (eparamptr)malloc(sizeof(eparam));
152    newepp->next = NULL;
153    newepp->key = (char *)malloc(1 + strlen(key));
154    strcpy(newepp->key, key);
155    newepp->pdata.refkey = NULL;		/* equivalently, sets pointno=0 */
156    newepp->flags = 0;
157 
158    return newepp;
159 }
160 
161 /*----------------------------------------------------------------------*/
162 /* Determine if a parameter is indirectly referenced.  If so, return	*/
163 /* the parameter name.  If not, return NULL.				*/
164 /*----------------------------------------------------------------------*/
165 
find_indirect_param(objinstptr thisinst,char * refkey)166 char *find_indirect_param(objinstptr thisinst, char *refkey)
167 {
168    eparamptr epp;
169 
170    for (epp = thisinst->passed; epp != NULL; epp = epp->next) {
171       if ((epp->flags & P_INDIRECT) && !strcmp(epp->pdata.refkey, refkey))
172 	 return epp->key;
173    }
174    return NULL;
175 }
176 
177 /*----------------------------------------------------------------------*/
178 /* Find the parameter in the indicated object by key			*/
179 /*----------------------------------------------------------------------*/
180 
match_param(objectptr thisobj,char * key)181 oparamptr match_param(objectptr thisobj, char *key)
182 {
183    oparamptr fparam;
184 
185    for (fparam = thisobj->params; fparam != NULL; fparam = fparam->next)
186       if (!strcmp(fparam->key, key))
187 	 return fparam;
188 
189    return NULL;		/* No parameter matched the key---error condition */
190 }
191 
192 /*----------------------------------------------------------------------*/
193 /* Find the parameter in the indicated instance by key.  If no such 	*/
194 /* instance value exists, return NULL.					*/
195 /*----------------------------------------------------------------------*/
196 
match_instance_param(objinstptr thisinst,char * key)197 oparamptr match_instance_param(objinstptr thisinst, char *key)
198 {
199    oparamptr fparam;
200 
201    for (fparam = thisinst->params; fparam != NULL; fparam = fparam->next)
202       if (!strcmp(fparam->key, key))
203 	 return fparam;
204 
205    return NULL;		/* No parameter matched the key---error condition */
206 }
207 
208 /*----------------------------------------------------------------------*/
209 /* Find the parameter in the indicated instance by key.  If no such 	*/
210 /* instance value exists, return the object (default) parameter.	*/
211 /*									*/
212 /* find_param() hides instances of expression parameters, returning the	*/
213 /* default parameter value.  In cases where the instance value (last	*/
214 /* evaluated expression result) is needed, use match_instance_param().	*/
215 /* An exception is made when the instance param has type XC_EXPR,	*/
216 /* indicating that the instance redefines the entire expression.	*/
217 /*----------------------------------------------------------------------*/
218 
find_param(objinstptr thisinst,char * key)219 oparamptr find_param(objinstptr thisinst, char *key)
220 {
221    oparamptr fparam, ops;
222    fparam = match_instance_param(thisinst, key);
223    ops = match_param(thisinst->thisobject, key);
224    if ((fparam == NULL) || ((ops->type == XC_EXPR) && (fparam->type != XC_EXPR)))
225       fparam = ops;
226 
227    return fparam;
228 }
229 
230 /*----------------------------------------------------------------------*/
231 /* Find the total number of parameters in an object			*/
232 /*----------------------------------------------------------------------*/
233 
get_num_params(objectptr thisobj)234 int get_num_params(objectptr thisobj)
235 {
236    oparamptr fparam;
237    int nparam = 0;
238 
239    for (fparam = thisobj->params; fparam != NULL; fparam = fparam->next)
240       nparam++;
241    return nparam;
242 }
243 
244 /*----------------------------------------------------------------------*/
245 /* Remove all element parameters from an element			*/
246 /*----------------------------------------------------------------------*/
247 
free_all_eparams(genericptr thiselem)248 void free_all_eparams(genericptr thiselem)
249 {
250    while (thiselem->passed != NULL)
251       free_element_param(thiselem, thiselem->passed);
252 }
253 
254 /*----------------------------------------------------------------------*/
255 /* Remove an element parameter (eparam) and free memory associated with	*/
256 /* the parameter key.							*/
257 /*----------------------------------------------------------------------*/
258 
free_element_param(genericptr thiselem,eparamptr thisepp)259 void free_element_param(genericptr thiselem, eparamptr thisepp)
260 {
261    eparamptr epp, lastepp = NULL;
262 
263    for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
264       if (epp == thisepp) {
265 	 if (lastepp != NULL)
266 	    lastepp->next = epp->next;
267 	 else
268 	    thiselem->passed = epp->next;
269 
270 	 /* If object is an instance and the pdata record is not NULL,	*/
271 	 /* then this is an indirect reference with the reference key	*/
272 	 /* stored as an allocated string in pdata.refkey, which needs	*/
273 	 /* to be free'd.						*/
274 
275 	 if ((epp->flags & P_INDIRECT) && (epp->pdata.refkey != NULL))
276 	    free(epp->pdata.refkey);
277 
278 	 free(epp->key);
279 	 free(epp);
280 	 break;
281       }
282       lastepp = epp;
283    }
284 }
285 
286 /*----------------------------------------------------------------------*/
287 /* Free an instance parameter.  Note that this routine does not free	*/
288 /* any strings associated with string parameters!			*/
289 /*									*/
290 /* Return a pointer to the entry before the one deleted, so we can use	*/
291 /* free_instance_param() inside a loop over an instance's parameters	*/
292 /* without having to keep track of the previous pointer position.	*/
293 /*----------------------------------------------------------------------*/
294 
free_instance_param(objinstptr thisinst,oparamptr thisparam)295 oparamptr free_instance_param(objinstptr thisinst, oparamptr thisparam)
296 {
297    oparamptr ops, lastops = NULL;
298 
299    for (ops = thisinst->params; ops != NULL; ops = ops->next) {
300       if (ops == thisparam) {
301 	 if (lastops != NULL)
302 	    lastops->next = ops->next;
303 	 else
304 	    thisinst->params = ops->next;
305 	 free(ops->key);
306 	 free(ops);
307 	 break;
308       }
309       lastops = ops;
310    }
311    return lastops;
312 }
313 
314 /*----------------------------------------------------------------------*/
315 /* Convenience function used by files.c to set a color parameter.	*/
316 /*----------------------------------------------------------------------*/
317 
std_eparam(genericptr gen,char * key)318 void std_eparam(genericptr gen, char *key)
319 {
320    eparamptr epp;
321 
322    if (key == NULL) return;
323 
324    epp = make_new_eparam(key);
325    epp->next = gen->passed;
326    gen->passed = epp;
327 }
328 
329 /*----------------------------------------------*/
330 /* Draw a circle at all parameter positions	*/
331 /*----------------------------------------------*/
332 
indicateparams(genericptr thiselem)333 void indicateparams(genericptr thiselem)
334 {
335    int k;
336    oparamptr ops;
337    eparamptr epp;
338    genericptr *pgen;
339 
340    if (thiselem != NULL) {
341       for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
342 	 ops = match_param(topobject, epp->key);
343 	 if (ops == NULL) continue;	/* error condition */
344 	 if (ELEMENTTYPE(thiselem) == PATH)
345 	    k = epp->pdata.pathpt[1];
346 	 else
347 	    k = epp->pdata.pointno;
348 	 if (k < 0) k = 0;
349 	 switch(ops->which) {
350 	    case P_POSITION: case P_POSITION_X: case P_POSITION_Y:
351 	       switch(thiselem->type) {
352 		  case ARC:
353 	             UDrawCircle(&TOARC(&thiselem)->position, ops->which);
354 		     break;
355 		  case LABEL:
356 	             UDrawCircle(&TOLABEL(&thiselem)->position, ops->which);
357 		     break;
358 		  case OBJINST:
359 	             UDrawCircle(&TOOBJINST(&thiselem)->position, ops->which);
360 		     break;
361 		  case POLYGON:
362 	             UDrawCircle(TOPOLY(&thiselem)->points + k, ops->which);
363 		     break;
364 		  case SPLINE:
365 	             UDrawCircle(&TOSPLINE(&thiselem)->ctrl[k], ops->which);
366 		     break;
367 		  case PATH:
368 		     if (epp->pdata.pathpt[0] < 0)
369 		        pgen = ((pathptr)thiselem)->plist;
370 		     else
371 		        pgen = ((pathptr)thiselem)->plist + epp->pdata.pathpt[0];
372 		     if (ELEMENTTYPE(*pgen) == POLYGON)
373 	                 UDrawCircle(TOPOLY(pgen)->points + k, ops->which);
374 		     else	/* spline */
375 	                 UDrawCircle(&TOSPLINE(pgen)->ctrl[k], ops->which);
376 		     break;
377 	       }
378 	       break;
379 	 }
380       }
381    }
382 }
383 
384 /*----------------------------------------------*/
385 /* Set the menu marks according to properties	*/
386 /* which are parameterized.  Unmanage the	*/
387 /* buttons which do not apply.			*/
388 /*						*/
389 /* pgen = NULL returns menu to default settings */
390 /*----------------------------------------------*/
391 
392 #ifdef TCL_WRAPPER
setparammarks(genericptr thiselem)393 void setparammarks(genericptr thiselem)
394 {
395    /* Set GUI variables associated with the "parameter" menu.	*/
396 
397    int i;
398    oparamptr ops;
399    eparamptr epp;
400    Boolean ptest[NUM_PARAM_TYPES];
401 
402    for (i = 0; i < NUM_PARAM_TYPES; i++)
403       ptest[i] = FALSE;
404 
405    /* For each parameter declared, set the corresponding Tcl variable */
406    if (thiselem != NULL) {
407       for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
408 	 ops = match_param(topobject, epp->key);
409 	 if (ops == NULL) continue;	/* error condition */
410 	 XcInternalTagCall(xcinterp, 3, "parameter", "make",
411 			translateparamtype(ops->which));
412 	 ptest[ops->which] = TRUE;
413       }
414    }
415 
416    /* Now reset all of those parameters that were not set above.		*/
417    /* Note that the parameters that we want to mark ignore the following types:	*/
418    /* "numeric", "substring", "expression", and "position".			*/
419 
420    for (i = P_POSITION_X; i <= P_COLOR; i++)
421       if (ptest[i] != TRUE)
422 	 XcInternalTagCall(xcinterp, 3, "parameter", "replace", translateparamtype(i));
423 }
424 #else
425 
setparammarks(genericptr thiselem)426 void setparammarks(genericptr thiselem)
427 {
428    Widget w;
429    Arg	wargs[1];
430    const int rlength = sizeof(param_buttons) / sizeof(Widget *);
431    int i, j, paramno;
432    oparamptr ops;
433    eparamptr epp;
434 
435    /* Clear all checkmarks */
436 
437    for (i = 0; i < rlength; i++) {
438       XtSetArg(wargs[0], XtNsetMark, False);
439       XtSetValues(*param_buttons[i], wargs, 1);
440    }
441 
442    /* Check those properties which are parameterized in the element */
443 
444    if (thiselem != NULL) {
445       for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
446          ops = match_param(topobject, epp->key);
447          w = *param_buttons[ops->which];
448          XtSetArg(wargs[0], XtNsetMark, True);
449          XtSetValues(w, wargs, 1);
450       }
451    }
452 
453    /* Unmanage widgets which do not apply to the element type */
454 
455    for (i = 0; i < rlength; i++) {
456       if ((thiselem == NULL) || (param_select[i] & thiselem->type))
457 	 XtManageChild(*param_buttons[i]);
458       else
459 	 XtUnmanageChild(*param_buttons[i]);
460    }
461 }
462 
463 #endif
464 
465 /*------------------------------------------------------*/
466 /* This function is like epsubstitute() below it, but	*/
467 /* only substitutes those values that are expression	*/
468 /* types.  This allows constraints to be applied when	*/
469 /* editing elements.					*/
470 /*------------------------------------------------------*/
471 
exprsub(genericptr thiselem)472 void exprsub(genericptr thiselem)
473 {
474    genericptr *pgen;
475    eparamptr epp;
476    int k, ival;
477    oparamptr dps, ops;
478    float fval;
479    XPoint *setpt;
480    char *promoted;
481 
482    for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
483       ops = match_param(topobject, epp->key);
484       dps = find_param(areawin->topinstance, epp->key);
485       if (dps != NULL) {
486 	 switch(dps->type) {
487 	    case XC_EXPR:
488 	       if ((promoted = evaluate_expr(topobject, dps, areawin->topinstance))
489 			== NULL) continue;
490 	       if (sscanf(promoted, "%g", &fval) == 1)
491 		  ival = (int)(fval + 0.5);
492 	       free(promoted);
493 	       if (ELEMENTTYPE(thiselem) == PATH)
494 		  k = epp->pdata.pathpt[1];
495 	       else
496 		  k = epp->pdata.pointno;
497 	       if (ops->which == P_POSITION_X) {
498 		  switch(thiselem->type) {
499 		     case PATH:
500 			pgen = TOPATH(&thiselem)->plist + epp->pdata.pathpt[0];
501 			if (ELEMENTTYPE(*pgen) == POLYGON) {
502 			   setpt = TOPOLY(pgen)->points + k;
503 			   setpt->x = ival;
504 			}
505 			else { /* spline */
506 			   TOSPLINE(pgen)->ctrl[k].x = ival;
507 			}
508 			break;
509 		     case POLYGON:
510 			setpt = TOPOLY(&thiselem)->points + k;
511 			setpt->x = ival;
512 			break;
513 		     case SPLINE:
514 			TOSPLINE(&thiselem)->ctrl[k].x = ival;
515 			break;
516 		  }
517 	       }
518 	       else if (ops->which == P_POSITION_Y) {
519 		  switch(thiselem->type) {
520 		     case PATH:
521 			pgen = TOPATH(&thiselem)->plist + epp->pdata.pathpt[0];
522 			if (ELEMENTTYPE(*pgen) == POLYGON) {
523 			   setpt = TOPOLY(pgen)->points + k;
524 			   setpt->y = ival;
525 			}
526 			else { /* spline */
527 			   TOSPLINE(pgen)->ctrl[k].y = ival;
528 			}
529 			break;
530 		     case POLYGON:
531 			setpt = TOPOLY(&thiselem)->points + k;
532 			setpt->y = ival;
533 			break;
534 		     case SPLINE:
535 			TOSPLINE(&thiselem)->ctrl[k].y = ival;
536 			break;
537 		  }
538 	       }
539 	 }
540       }
541    }
542 }
543 
544 /*------------------------------------------------------*/
545 /* Make numerical parameter substitutions into an	*/
546 /* element.						*/
547 /*------------------------------------------------------*/
548 
epsubstitute(genericptr thiselem,objectptr thisobj,objinstptr pinst,Boolean * needrecalc)549 int epsubstitute(genericptr thiselem, objectptr thisobj, objinstptr pinst,
550    Boolean *needrecalc)
551 {
552    genericptr *pgen;
553    eparamptr epp;
554    oparamptr dps, ops;
555    int retval = -1;
556    int i, k, ival, diff;
557    char *key;
558    float fval;
559    XPoint *setpt;
560    char *promoted;
561 
562    for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
563 
564       /* Use the parameter from the instance, if available.	*/
565       /* Otherwise, revert to the type of the object.	*/
566       /* Normally they will be the same.			*/
567 
568       ops = match_param(thisobj, epp->key);
569       dps = (pinst != NULL) ?  find_param(pinst, epp->key) : ops;
570 
571       if (dps != NULL) {
572 
573 	 /* Get integer and float values.  Promote types if necessary */
574 
575 	 switch(dps->type) {
576 	    case XC_INT:
577 	       ival = dps->parameter.ivalue;
578 	       fval = (float)(ival);
579 	       break;
580 	    case XC_FLOAT:
581 	       fval = dps->parameter.fvalue;
582 	       ival = (int)(fval + ((fval < 0) ? -0.5 : 0.5));
583 	       break;
584 	    case XC_STRING:
585 	       promoted = textprint(dps->parameter.string, pinst);
586 	       if (sscanf(promoted, "%g", &fval) == 1)
587 	          ival = (int)(fval + ((fval < 0) ? -0.5 : 0.5));
588 	       else
589 		  ival = 0;
590 	       free(promoted);
591 	       break;
592 	    case XC_EXPR:
593 	       if ((promoted = evaluate_expr(thisobj, dps, pinst)) == NULL) continue;
594 	       if (sscanf(promoted, "%g", &fval) == 1)
595 	          ival = (int)(fval + ((fval < 0) ? -0.5 : 0.5));
596 	       free(promoted);
597 	       break;
598 	 }
599       }
600       else if (ops == NULL)
601 	 continue;
602 
603       if ((epp->flags & P_INDIRECT) && (epp->pdata.refkey != NULL)) {
604 	 key = epp->pdata.refkey;
605 	 if (key != NULL) {
606 	    objinstptr thisinst;
607 	    oparamptr refop, newop;
608 
609 	    thisinst = (objinstptr)thiselem;
610 
611 	    /* Sanity check: refkey must exist in object */
612 	    refop = match_param(thisinst->thisobject, key);
613 	    if (refop == NULL) {
614 	       Fprintf(stderr, "Error:  Reference key %s does not"
615 			" exist in object %s\n",
616 			key, thisinst->thisobject->name);
617 	       continue;
618 	    }
619 
620 	    /* If an instance value already exists, remove it */
621 	    newop = match_instance_param(thisinst, refop->key);
622 	    if (newop != NULL)
623 	       free_instance_param(thisinst, newop);
624 
625 	    /* Create a new instance parameter */
626 	    newop = copyparameter(dps);
627 	    newop->next = thisinst->params;
628 	    thisinst->params = newop;
629 
630 	    /* Change the key from the parent to the child */
631 	    if (strcmp(ops->key, refop->key)) {
632 	       free(newop->key);
633 	       newop->key = strdup(refop->key);
634 	    }
635 	    continue;
636 	 }
637       }
638 
639       if (ELEMENTTYPE(thiselem) == PATH)
640 	 k = epp->pdata.pathpt[1];
641       else
642 	 k = epp->pdata.pointno;
643       switch(ops->which) {
644 	 case P_POSITION_X:
645 	    retval = max(retval, 1);
646 	    switch(thiselem->type) {
647 	       case PATH:
648 		  if (k < 0) {
649 		     pgen = TOPATH(&thiselem)->plist;
650 		     if (ELEMENTTYPE(*pgen) == POLYGON) {
651 		        setpt = TOPOLY(pgen)->points;
652 		        diff = ival - setpt->x;
653 		     }
654 		     else { /* spline */
655 		        diff = ival - TOSPLINE(pgen)->ctrl[0].x;
656 		     }
657 		     for (pgen = TOPATH(&thiselem)->plist; pgen <
658 				TOPATH(&thiselem)->plist +
659 				TOPATH(&thiselem)->parts; pgen++) {
660 		        if (ELEMENTTYPE(*pgen) == POLYGON) {
661 			   for (i = 0; i < TOPOLY(pgen)->number; i++) {
662 		              setpt = TOPOLY(pgen)->points + i;
663 		              setpt->x += diff;
664 			   }
665 		        }
666 		        else { /* spline */
667 			   for (i = 0; i < 4; i++) {
668 		              TOSPLINE(pgen)->ctrl[i].x += diff;
669 			   }
670 		           if (needrecalc) *needrecalc = True;
671 		        }
672 		     }
673 		  }
674 		  else {
675 		     pgen = TOPATH(&thiselem)->plist + epp->pdata.pathpt[0];
676 		     if (ELEMENTTYPE(*pgen) == POLYGON) {
677 		        setpt = TOPOLY(pgen)->points + k;
678 		        setpt->x = ival;
679 		     }
680 		     else { /* spline */
681 		        TOSPLINE(pgen)->ctrl[k].x = ival;
682 		        if (needrecalc) *needrecalc = True;
683 		     }
684 		  }
685 		  break;
686 	       case POLYGON:
687 		  if (k < 0) {
688 		     setpt = TOPOLY(&thiselem)->points;
689 		     diff = ival - setpt->x;
690 		     for (i = 0; i < TOPOLY(&thiselem)->number; i++) {
691 			setpt = TOPOLY(&thiselem)->points + i;
692 			setpt->x += diff;
693 		     }
694 		  }
695 		  else {
696 	             setpt = TOPOLY(&thiselem)->points + k;
697 		     setpt->x = ival;
698 		  }
699 		  break;
700 	       case SPLINE:
701 		  if (k < 0) {
702 		     setpt = &(TOSPLINE(&thiselem)->ctrl[0]);
703 		     diff = ival - setpt->x;
704 		     for (i = 0; i < 4; i++) {
705 			setpt = &(TOSPLINE(&thiselem)->ctrl[i]);
706 			setpt->x += diff;
707 		     }
708 		  }
709 		  else {
710 		     TOSPLINE(&thiselem)->ctrl[k].x = ival;
711 		  }
712 		  if (needrecalc) *needrecalc = True;
713 		  break;
714 	       case LABEL:
715 		  TOLABEL(&thiselem)->position.x = ival;
716 		  break;
717 	       case OBJINST:
718 		  TOOBJINST(&thiselem)->position.x = ival;
719 		  break;
720 	       case ARC:
721 		  TOARC(&thiselem)->position.x = ival;
722 		  break;
723 	    }
724 	    break;
725 	 case P_POSITION_Y:
726 	    retval = max(retval, 1);
727 	    switch(thiselem->type) {
728 	       case PATH:
729 		  if (k < 0) {
730 		     pgen = TOPATH(&thiselem)->plist;
731 		     if (ELEMENTTYPE(*pgen) == POLYGON) {
732 		        setpt = TOPOLY(pgen)->points;
733 		        diff = ival - setpt->y;
734 		     }
735 		     else { /* spline */
736 		        diff = ival - TOSPLINE(pgen)->ctrl[0].y;
737 		     }
738 		     for (pgen = TOPATH(&thiselem)->plist; pgen <
739 				TOPATH(&thiselem)->plist +
740 				TOPATH(&thiselem)->parts; pgen++) {
741 		        if (ELEMENTTYPE(*pgen) == POLYGON) {
742 			   for (i = 0; i < TOPOLY(pgen)->number; i++) {
743 		              setpt = TOPOLY(pgen)->points + i;
744 		              setpt->y += diff;
745 			   }
746 		        }
747 		        else { /* spline */
748 			   for (i = 0; i < 4; i++) {
749 		              TOSPLINE(pgen)->ctrl[i].y += diff;
750 			   }
751 		           if (needrecalc) *needrecalc = True;
752 		        }
753 		     }
754 		  }
755 		  else {
756 		     pgen = TOPATH(&thiselem)->plist + epp->pdata.pathpt[0];
757 		     if (ELEMENTTYPE(*pgen) == POLYGON) {
758 		        setpt = TOPOLY(pgen)->points + k;
759 		        setpt->y = ival;
760 		     }
761 		     else { /* spline */
762 		        TOSPLINE(pgen)->ctrl[k].y = ival;
763 		        if (needrecalc) *needrecalc = True;
764 		     }
765 		  }
766 		  break;
767 	       case POLYGON:
768 		  if (k < 0) {
769 		     setpt = TOPOLY(&thiselem)->points;
770 		     diff = ival - setpt->y;
771 		     for (i = 0; i < TOPOLY(&thiselem)->number; i++) {
772 			setpt = TOPOLY(&thiselem)->points + i;
773 			setpt->y += diff;
774 		     }
775 		  }
776 		  else {
777 	             setpt = TOPOLY(&thiselem)->points + k;
778 		     setpt->y = ival;
779 		  }
780 		  break;
781 	       case SPLINE:
782 		  if (k < 0) {
783 		     setpt = &(TOSPLINE(&thiselem)->ctrl[0]);
784 		     diff = ival - setpt->y;
785 		     for (i = 0; i < 4; i++) {
786 			setpt = &(TOSPLINE(&thiselem)->ctrl[i]);
787 			setpt->y += diff;
788 		     }
789 		  }
790 		  else {
791 		     TOSPLINE(&thiselem)->ctrl[k].y = ival;
792 		  }
793 		  if (needrecalc) *needrecalc = True;
794 		  break;
795 	       case LABEL:
796 		  TOLABEL(&thiselem)->position.y = ival;
797 		  break;
798 	       case OBJINST:
799 		  TOOBJINST(&thiselem)->position.y = ival;
800 		  break;
801 	       case ARC:
802 		  TOARC(&thiselem)->position.y = ival;
803 		  break;
804 	    }
805 	    break;
806 	 case P_STYLE:
807 	    retval = max(retval, 0);
808 	    switch(thiselem->type) {
809 	       case POLYGON:
810 		  TOPOLY(&thiselem)->style = ival;
811 		  break;
812 	       case SPLINE:
813 		  TOSPLINE(&thiselem)->style = ival;
814 		  break;
815 	       case ARC:
816 		  TOARC(&thiselem)->style = ival;
817 		  break;
818 	       case PATH:
819 		  TOPATH(&thiselem)->style = ival;
820 		  break;
821 	    }
822 	    break;
823 	 case P_ANCHOR:
824 	    retval = max(retval, 1);
825 	    switch(thiselem->type) {
826 	       case LABEL:
827 		  TOLABEL(&thiselem)->anchor = ival;
828 		  break;
829 	    }
830 	    break;
831 	 case P_ANGLE1:
832 	    retval = max(retval, 1);
833 	    switch(thiselem->type) {
834 	       case ARC:
835 		  TOARC(&thiselem)->angle1 = fval;
836 		  if (needrecalc) *needrecalc = True;
837 		  break;
838 	    }
839 	    break;
840 	 case P_ANGLE2:
841 	    retval = max(retval, 1);
842 	    switch(thiselem->type) {
843 	       case ARC:
844 		  TOARC(&thiselem)->angle1 = fval;
845 		  if (needrecalc) *needrecalc = True;
846 		  break;
847 	    }
848 	    break;
849 	 case P_RADIUS:
850 	    retval = max(retval, 1);
851 	    switch(thiselem->type) {
852 	       case ARC:
853 		  TOARC(&thiselem)->radius = ival;
854 		  TOARC(&thiselem)->yaxis = ival;
855 		  if (needrecalc) *needrecalc = True;
856 		  break;
857 	    }
858 	    break;
859 	 case P_MINOR_AXIS:
860 	    retval = max(retval, 1);
861 	    switch(thiselem->type) {
862 	       case ARC:
863 		  TOARC(&thiselem)->yaxis = ival;
864 		  if (needrecalc) *needrecalc = True;
865 		  break;
866 	    }
867 	    break;
868 	 case P_ROTATION:
869 	    retval = max(retval, 1);
870 	    switch(thiselem->type) {
871 	       case LABEL:
872 		  TOLABEL(&thiselem)->rotation = fval;
873 		  break;
874 	       case OBJINST:
875 		  TOOBJINST(&thiselem)->rotation = fval;
876 		  break;
877 	    }
878 	    break;
879 	 case P_SCALE:
880 	    retval = max(retval, 1);
881 	    switch(thiselem->type) {
882 	       case LABEL:
883 		  TOLABEL(&thiselem)->scale = fval;
884 		  break;
885 	       case OBJINST:
886 		  TOOBJINST(&thiselem)->scale = fval;
887 		  break;
888 	    }
889 	    break;
890 	 case P_LINEWIDTH:
891 	    retval = max(retval, 0);
892 	    switch(thiselem->type) {
893 	       case POLYGON:
894 		  TOPOLY(&thiselem)->width = fval;
895 		  break;
896 	       case SPLINE:
897 		  TOSPLINE(&thiselem)->width = fval;
898 		  break;
899 	       case ARC:
900 		  TOARC(&thiselem)->width = fval;
901 		  break;
902 	       case PATH:
903 		  TOPATH(&thiselem)->width = fval;
904 		  break;
905 	    }
906 	    break;
907 	 case P_COLOR:
908 	    retval = max(retval, 0);
909 	    thiselem->color = ival;
910 	    break;
911       }
912    }
913 
914    return retval;
915 }
916 
917 /*------------------------------------------------------*/
918 /* Make numerical parameter substitutions into all	*/
919 /* elements of an object.  "thisinst" may be NULL, in	*/
920 /* which case all default values are used in the	*/
921 /* substitution.					*/
922 /*							*/
923 /* Return values:					*/
924 /*   -1 if the instance declares no parameters		*/
925 /*    0 if parameters do not change the instance's bbox */
926 /*    1 if parameters change instance's bbox		*/
927 /*    2 if parameters change instance's netlist		*/
928 /*------------------------------------------------------*/
929 
opsubstitute(objectptr thisobj,objinstptr pinst)930 int opsubstitute(objectptr thisobj, objinstptr pinst)
931 {
932    genericptr *eptr, *pgen, thiselem;
933    stringpart *strptr;
934    int retval = -1;
935    Boolean needrecalc;	/* for arcs and splines */
936 
937    /* Perform expression parameter substitutions on all labels.	*/
938    /* Note that this used to be done on an immediate basis as	*/
939    /* labels were parsed.  The main difference is that only one	*/
940    /* expression parameter can be used per label if it is to	*/
941    /* compute the result of some aspect of the label, such as	*/
942    /* position; this is a tradeoff for much simplified handling	*/
943    /* of expression results, like having to avoid infinite	*/
944    /* recursion in an expression result.			*/
945 
946    for (eptr = thisobj->plist; eptr < thisobj->plist + thisobj->parts; eptr++)
947       if ((*eptr)->type == LABEL)
948 	 for (strptr = (TOLABEL(eptr))->string; strptr != NULL; strptr =
949 			nextstringpartrecompute(strptr, pinst));
950 
951    if (thisobj->params == NULL)
952       return -1;			    /* object has no parameters */
953 
954    for (eptr = thisobj->plist; eptr < thisobj->plist + thisobj->parts; eptr++) {
955 
956       needrecalc = False;
957       thiselem = *eptr;
958       if (thiselem->passed == NULL) continue;	/* Nothing to substitute */
959       retval = epsubstitute(thiselem, thisobj, pinst, &needrecalc);
960 
961       /* substitutions into arcs and splines require that the	*/
962       /* line segments be recalculated.				*/
963 
964       if (needrecalc) {
965 	 switch(thiselem->type) {
966 	    case ARC:
967 	       calcarc((arcptr)thiselem);
968 	       break;
969 	    case SPLINE:
970 	       calcspline((splineptr)thiselem);
971 	       break;
972 	    case PATH:
973 	       for (pgen = ((pathptr)thiselem)->plist; pgen < ((pathptr)thiselem)->plist
974 				+ ((pathptr)thiselem)->parts; pgen++)
975 		  if (ELEMENTTYPE(*pgen) == SPLINE)
976 		     calcspline((splineptr)*pgen);
977 	       break;
978 	 }
979       }
980    }
981    return retval;
982 }
983 
984 /*------------------------------------------------------*/
985 /* Same as above, but determines the object from the	*/
986 /* current page hierarchy.				*/
987 /*------------------------------------------------------*/
988 
psubstitute(objinstptr thisinst)989 int psubstitute(objinstptr thisinst)
990 {
991    objinstptr pinst;
992    objectptr thisobj;
993 
994    pinst = (thisinst == areawin->topinstance) ? areawin->topinstance : thisinst;
995    if (pinst == NULL) return -1;		/* there is no instance */
996    thisobj = pinst->thisobject;
997 
998    return opsubstitute(thisobj, pinst);
999 }
1000 
1001 /*----------------------------------------------*/
1002 /* Check if an element contains a parameter.	*/
1003 /*----------------------------------------------*/
1004 
has_param(genericptr celem)1005 Boolean has_param(genericptr celem)
1006 {
1007    if (IS_LABEL(celem)) {
1008       stringpart *cstr;
1009       labelptr clab = (labelptr)celem;
1010       for (cstr = clab->string; cstr != NULL; cstr = cstr->nextpart)
1011 	 if (cstr->type == PARAM_START)
1012 	    return TRUE;
1013    }
1014    if (celem->passed != NULL) return TRUE;
1015    return FALSE;
1016 }
1017 
1018 /*------------------------------------------------------*/
1019 /* Find "current working values" in the element list of	*/
1020 /* an object, and write them into the instance's	*/
1021 /* parameter list.					*/
1022 /* This is just the opposite of "psubstitute()", except	*/
1023 /* that instance values are created prior to writeback, */
1024 /* and resolved afterward.				*/
1025 /*------------------------------------------------------*/
1026 
pwriteback(objinstptr thisinst)1027 void pwriteback(objinstptr thisinst)
1028 {
1029    genericptr *eptr, *pgen, thiselem;
1030    objectptr thisobj;
1031    objinstptr pinst;
1032    eparamptr epp;
1033    oparamptr ops, ips;
1034    int k, type, *destivalptr, found;
1035    XPoint *setpt;
1036    Boolean changed, need_redraw = FALSE;
1037    union {
1038       int ival;
1039       float fval;
1040    } wtemp;
1041 
1042    pinst = thisinst;
1043    thisobj = (pinst == NULL) ? topobject : pinst->thisobject;
1044 
1045    /* Make sure that all instance values exist */
1046    if (pinst != NULL) copyparams(pinst, pinst);
1047 
1048    /* Because more than one element can point to the same parameter, we search	*/
1049    /* through each (numerical) parameter declared in the object.  If any	*/
1050    /* element has a different value, the parameter is changed to match.   This	*/
1051    /* operates on the assumption that no more than one element will change the	*/
1052    /* value of any one parameter on a single call to pwriteback().		*/
1053 
1054    for (ops = thisobj->params; ops != NULL; ops = ops->next) {
1055       /* handle pre-assigned numeric parameters only */
1056       if ((ops->which == P_SUBSTRING) || (ops->which == P_EXPRESSION) ||
1057 		(ops->which == P_NUMERIC))
1058 	 continue;
1059       found = 0;
1060       changed = FALSE;
1061       ips = (pinst != NULL) ? match_instance_param(pinst, ops->key) : NULL;
1062       for (eptr = thisobj->plist; eptr < thisobj->plist + thisobj->parts; eptr++) {
1063          thiselem = *eptr;
1064          if (thiselem->passed == NULL) continue;	/* Nothing to write back */
1065          for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
1066 	    if (!strcmp(epp->key, ops->key)) {
1067 	       found++;
1068 	       if (ELEMENTTYPE(thiselem) == PATH)
1069 		  k = epp->pdata.pathpt[1];
1070 	       else
1071 	          k = epp->pdata.pointno;
1072 	       if (k < 0) k = 0;
1073                switch(ops->which) {
1074 	          case P_POSITION_X:
1075 	             switch(thiselem->type) {
1076 	                case OBJINST:
1077 		           wtemp.ival = TOOBJINST(eptr)->position.x;
1078 		           break;
1079 	                case LABEL:
1080 		           wtemp.ival = TOLABEL(eptr)->position.x;
1081 		           break;
1082 	                case POLYGON:
1083 	                   setpt = TOPOLY(eptr)->points + k;
1084 		           wtemp.ival = setpt->x;
1085 		           break;
1086 	                case ARC:
1087 		           wtemp.ival = TOARC(eptr)->position.x;
1088 		           break;
1089 	                case SPLINE:
1090 		           wtemp.ival = TOSPLINE(eptr)->ctrl[k].x;
1091 		           break;
1092 			case PATH:
1093 			   if (epp->pdata.pathpt[0] < 0)
1094 			      pgen = TOPATH(eptr)->plist;
1095 			   else
1096 			      pgen = TOPATH(eptr)->plist + epp->pdata.pathpt[0];
1097 			   if (ELEMENTTYPE(*pgen) == POLYGON) {
1098 	                      setpt = TOPOLY(pgen)->points + k;
1099 		              wtemp.ival = setpt->x;
1100 			   }
1101 			   else
1102 		              wtemp.ival = TOSPLINE(pgen)->ctrl[k].x;
1103 			   break;
1104 	             }
1105 	             break;
1106 	          case P_POSITION_Y:
1107 	             switch(thiselem->type) {
1108 	                case OBJINST:
1109 		           wtemp.ival = TOOBJINST(eptr)->position.y;
1110 		           break;
1111 	                case LABEL:
1112 		           wtemp.ival = TOLABEL(eptr)->position.y;
1113 		           break;
1114 	                case POLYGON:
1115 	                   setpt = TOPOLY(eptr)->points + k;
1116 		           wtemp.ival = setpt->y;
1117 		           break;
1118 	                case ARC:
1119 		           wtemp.ival = TOARC(eptr)->position.y;
1120 		           break;
1121 	                case SPLINE:
1122 		           wtemp.ival = TOSPLINE(eptr)->ctrl[k].y;
1123 		           break;
1124 			case PATH:
1125 			   if (epp->pdata.pathpt[0] < 0)
1126 			      pgen = TOPATH(eptr)->plist;
1127 			   else
1128 			      pgen = TOPATH(eptr)->plist + epp->pdata.pathpt[0];
1129 			   if (ELEMENTTYPE(*pgen) == POLYGON) {
1130 	                      setpt = TOPOLY(pgen)->points + k;
1131 		              wtemp.ival = setpt->y;
1132 			   }
1133 			   else
1134 		              wtemp.ival = TOSPLINE(pgen)->ctrl[k].y;
1135 			   break;
1136 	             }
1137 	             break;
1138 	          case P_STYLE:
1139 	             switch(thiselem->type) {
1140 	                case POLYGON:
1141 		           wtemp.ival = TOPOLY(eptr)->style;
1142 		           break;
1143 	                case ARC:
1144 		           wtemp.ival = TOARC(eptr)->style;
1145 		           break;
1146 	                case SPLINE:
1147 		           wtemp.ival = TOSPLINE(eptr)->style;
1148 		           break;
1149 	                case PATH:
1150 		           wtemp.ival = TOPATH(eptr)->style;
1151 		           break;
1152 	             }
1153 	             break;
1154 	          case P_ANCHOR:
1155 	             switch(thiselem->type) {
1156 	                case LABEL:
1157 		           wtemp.ival = TOLABEL(eptr)->anchor;
1158 		           break;
1159 	             }
1160 	             break;
1161 	          case P_ANGLE1:
1162 	             switch(thiselem->type) {
1163 	                case ARC:
1164 		           wtemp.fval = TOARC(eptr)->angle1;
1165 		           break;
1166 	             }
1167 	             break;
1168 	          case P_ANGLE2:
1169 	             switch(thiselem->type) {
1170 	                case ARC:
1171 		           wtemp.fval = TOARC(eptr)->angle1;
1172 		           break;
1173 	             }
1174 	             break;
1175 	          case P_RADIUS:
1176 	             switch(thiselem->type) {
1177 	                case ARC:
1178 		           wtemp.ival = TOARC(eptr)->radius;
1179 		           break;
1180 	             }
1181 	             break;
1182 	          case P_MINOR_AXIS:
1183 	             switch(thiselem->type) {
1184 	                case ARC:
1185 		           wtemp.ival = TOARC(eptr)->yaxis;
1186 		           break;
1187 	             }
1188 	             break;
1189 	          case P_ROTATION:
1190 	             switch(thiselem->type) {
1191 	                case OBJINST:
1192 		           wtemp.fval = TOOBJINST(eptr)->rotation;
1193 		           break;
1194 	                case LABEL:
1195 		           wtemp.fval = TOLABEL(eptr)->rotation;
1196 		           break;
1197 	             }
1198 	             break;
1199 	          case P_SCALE:
1200 	             switch(thiselem->type) {
1201 	                case OBJINST:
1202 		           wtemp.fval = TOOBJINST(eptr)->scale;
1203 		           break;
1204 	                case LABEL:
1205 		           wtemp.fval = TOLABEL(eptr)->scale;
1206 		           break;
1207 	             }
1208 	             break;
1209 	          case P_LINEWIDTH:
1210 	             switch(thiselem->type) {
1211 	                case POLYGON:
1212 		           wtemp.fval = TOPOLY(eptr)->width;
1213 		           break;
1214 	                case ARC:
1215 		           wtemp.fval = TOARC(eptr)->width;
1216 		           break;
1217 	                case SPLINE:
1218 		           wtemp.fval = TOSPLINE(eptr)->width;
1219 		           break;
1220 	                case PATH:
1221 		           wtemp.fval = TOPATH(eptr)->width;
1222 		           break;
1223 	             }
1224 	             break;
1225 		  case P_COLOR:
1226 		     wtemp.ival = thiselem->color;
1227 		     break;
1228                }
1229 	       type = (ips != NULL) ? ips->type : ops->type;
1230 	       if (type != XC_FLOAT && type != XC_INT) break;
1231 
1232 	       destivalptr = (ips != NULL) ? &ips->parameter.ivalue
1233 			: &ops->parameter.ivalue;
1234 	       if ((!changed) && (wtemp.ival != *destivalptr)) {
1235 		  *destivalptr = wtemp.ival;
1236 		  changed = TRUE;
1237 	       }
1238 	       else if (found > 1) need_redraw = TRUE;
1239 	       break;
1240 	    }
1241 	 }
1242       }
1243    }
1244 
1245    /* Any instance values which are identical to the default value	*/
1246    /* get erased (so they won't be written to the output unnecessarily) */
1247 
1248    if (pinst != NULL) resolveparams(pinst);
1249 
1250    if (need_redraw) {
1251       incr_changes(thisobj);
1252       invalidate_netlist(thisobj);
1253    }
1254 
1255    /* Because more than one element may use the same parameter,		*/
1256    /* pwriteback checks for cases in which a change in one element	*/
1257    /* precipitates a change in another.  If so, force a redraw.		*/
1258 
1259    if (need_redraw && (thisinst == areawin->topinstance))
1260       drawarea(NULL, NULL, NULL);
1261 }
1262 
1263 /*------------------------------------------------------*/
1264 /* If the instance comes from the library, replace the	*/
1265 /* default value with the instance value.		*/
1266 /*------------------------------------------------------*/
1267 
replaceparams(objinstptr thisinst)1268 void replaceparams(objinstptr thisinst)
1269 {
1270    objectptr thisobj;
1271    oparamptr ops, ips;
1272    /* int i, nullparms = 0; (jdk) */
1273 
1274    thisobj = thisinst->thisobject;
1275 
1276    for (ops = thisobj->params; ops != NULL; ops = ops->next) {
1277       ips = match_instance_param(thisinst, ops->key);
1278       if (ips == NULL) continue;  /* this parameter is already default */
1279 
1280       switch(ops->type) {
1281 	 case XC_STRING:
1282 	    if (stringcomp(ops->parameter.string, ips->parameter.string)) {
1283 	       freelabel(ops->parameter.string);
1284 	       ops->parameter.string = ips->parameter.string;
1285 	       free_instance_param(thisinst, ips);
1286 	    }
1287 	    break;
1288 	 case XC_EXPR:
1289 	    /* Expression parameters should be replaced *only* if the
1290 	     * instance value is also an expression, and not an evaluated
1291 	     * result.
1292 	     */
1293 	    if ((ips->type == XC_EXPR) &&
1294 			strcmp(ops->parameter.expr, ips->parameter.expr)) {
1295 	       free(ops->parameter.expr);
1296 	       ops->parameter.expr = ips->parameter.expr;
1297 	       free_instance_param(thisinst, ips);
1298 	    }
1299 	    break;
1300 	 case XC_INT: case XC_FLOAT:
1301 	    if (ops->parameter.ivalue != ips->parameter.ivalue) {
1302 	       ops->parameter.ivalue = ips->parameter.ivalue;
1303 	       free_instance_param(thisinst, ips);
1304 	    }
1305 	    break;
1306       }
1307    }
1308 }
1309 
1310 /*------------------------------------------------------*/
1311 /* Resolve differences between the object instance	*/
1312 /* parameters and the default parameters.  If they	*/
1313 /* are the same for any parameter, delete that instance	*/
1314 /* such that the instance reverts to the default value.	*/
1315 /*------------------------------------------------------*/
1316 
resolveparams(objinstptr thisinst)1317 void resolveparams(objinstptr thisinst)
1318 {
1319    objectptr thisobj;
1320    liblistptr spec;
1321    oparamptr ops, ips;
1322    int i;
1323 
1324    /* If the instance has no parameters itself, ignore it. */
1325    if (thisinst == NULL || thisinst->params == NULL) return;
1326 
1327    /* If the object was pushed into from a library, we want to change	*/
1328    /* the default, not the instanced, parameter values.  However, this	*/
1329    /* is not true for "virtual" library objects (in the instlist)	*/
1330 
1331    if ((i = checklibtop()) >= 0) {
1332       for (spec = xobjs.userlibs[i].instlist; spec != NULL;
1333                 spec = spec->next)
1334          if (spec->thisinst == thisinst)
1335 	    break;
1336 
1337       if ((spec == NULL) || (spec->virtual == FALSE)) {
1338          /* Fprintf(stdout, "Came from library:  changing default value\n"); */
1339          replaceparams(thisinst);
1340          return;
1341       }
1342    }
1343 
1344    /* Parameters which are changed on a top-level page must also change	*/
1345    /* the default value; otherwise, the instance value shadows the page	*/
1346    /* object's value but the page object's value is the one written to	*/
1347    /* the output file.							*/
1348 
1349    else if (is_page(thisinst->thisobject) >= 0) {
1350       replaceparams(thisinst);
1351       return;
1352    }
1353 
1354    thisobj = thisinst->thisobject;
1355 
1356    for (ops = thisobj->params; ops != NULL; ops = ops->next) {
1357       ips = match_instance_param(thisinst, ops->key);
1358       if (ips == NULL) continue;  /* this parameter is already default */
1359 
1360       /* If type or which fields do not match, then we don't need to look */
1361       /* any further;  object and instance have different parameters.	  */
1362       if ((ips->type != ops->type) || (ips->which != ops->which)) continue;
1363 
1364       switch(ops->type) {
1365 	 case XC_STRING:
1366 	    if (!stringcomp(ops->parameter.string, ips->parameter.string)) {
1367 	       freelabel(ips->parameter.string);
1368 	       free_instance_param(thisinst, ips);
1369 	    }
1370 	    break;
1371 	 case XC_EXPR:
1372 	    if (!strcmp(ops->parameter.expr, ips->parameter.expr)) {
1373 	       free(ips->parameter.expr);
1374 	       free_instance_param(thisinst, ips);
1375 	    }
1376 	    break;
1377 	 case XC_INT: case XC_FLOAT:
1378 	    if (ops->parameter.ivalue == ips->parameter.ivalue) {
1379 	       free_instance_param(thisinst, ips);
1380 	    }
1381 	    break;
1382       }
1383    }
1384 
1385    if (thisinst->params != NULL) {
1386       /* Object must recompute bounding box if any instance	*/
1387       /* uses a non-default parameter.				*/
1388 
1389       calcbboxvalues(thisinst, NULL);
1390    }
1391 }
1392 
1393 /*--------------------------------------------------------------*/
1394 /* Return a copy of the single eparameter "cepp"		*/
1395 /*--------------------------------------------------------------*/
1396 
copyeparam(eparamptr cepp,genericptr thiselem)1397 eparamptr copyeparam(eparamptr cepp, genericptr thiselem)
1398 {
1399    eparamptr newepp;
1400 
1401    newepp = make_new_eparam(cepp->key);
1402    if ((cepp->flags & P_INDIRECT) && (cepp->pdata.refkey != NULL))
1403       newepp->pdata.refkey = strdup(cepp->pdata.refkey);
1404    else
1405       newepp->pdata.pointno = cepp->pdata.pointno;  /* also covers pathpt[] */
1406    newepp->flags = cepp->flags;
1407    return newepp;
1408 }
1409 
1410 /*------------------------------------------------------*/
1411 /* Copy all element parameters from source to dest	*/
1412 /*------------------------------------------------------*/
1413 
copyalleparams(genericptr destinst,genericptr sourceinst)1414 void copyalleparams(genericptr destinst, genericptr sourceinst)
1415 {
1416    eparamptr cepp, newepp;
1417 
1418    for (cepp = sourceinst->passed; cepp != NULL; cepp = cepp->next) {
1419       newepp = copyeparam(cepp, sourceinst);
1420       newepp->next = destinst->passed;
1421       destinst->passed = newepp;
1422    }
1423 }
1424 
1425 /*--------------------------------------------------------------*/
1426 /* Return a copy of the single parameter "cops"			*/
1427 /*--------------------------------------------------------------*/
1428 
copyparameter(oparamptr cops)1429 oparamptr copyparameter(oparamptr cops)
1430 {
1431    oparamptr newops;
1432 
1433    newops = make_new_parameter(cops->key);
1434    newops->type = cops->type;
1435    newops->which = cops->which;
1436    switch(cops->type) {
1437       case XC_STRING:
1438 	 newops->parameter.string = stringcopy(cops->parameter.string);
1439 	 break;
1440       case XC_EXPR:
1441 	 newops->parameter.expr = strdup(cops->parameter.expr);
1442 	 break;
1443       case XC_INT: case XC_FLOAT:
1444 	 newops->parameter.ivalue = cops->parameter.ivalue;
1445 	 break;
1446       default:
1447 	 Fprintf(stderr, "Error:  bad parameter\n");
1448 	 break;
1449    }
1450    return newops;
1451 }
1452 
1453 /*------------------------------------------------------*/
1454 /* Fill any NULL instance parameters with the values	*/
1455 /* from the calling instance, or from the instance	*/
1456 /* object's defaults if destinst = sourceinst.		*/
1457 /*							*/
1458 /* Expression parameters get special treatment because	*/
1459 /* the instance value may be holding the last evaluated	*/
1460 /* expression, not an instance value of the expression.	*/
1461 /* If so, its type will be XC_STRING or XC_FLOAT, not	*/
1462 /* XC_EXPR.						*/
1463 /*------------------------------------------------------*/
1464 
copyparams(objinstptr destinst,objinstptr sourceinst)1465 void copyparams(objinstptr destinst, objinstptr sourceinst)
1466 {
1467    oparamptr psource, cops, newops, ips;
1468 
1469    if (sourceinst == NULL) return;
1470    if (destinst == sourceinst)
1471       psource = sourceinst->thisobject->params;
1472    else
1473       psource = sourceinst->params;
1474 
1475    for (cops = psource; cops != NULL; cops = cops->next) {
1476       if ((ips = match_instance_param(destinst, cops->key)) == NULL) {
1477          newops = copyparameter(cops);
1478 	 newops->next = destinst->params;
1479 	 destinst->params = newops;
1480       }
1481       else if ((cops->type == XC_EXPR) && (ips->type != XC_EXPR))
1482 	 free_instance_param(destinst, ips);
1483    }
1484 }
1485 
1486 /*--------------------------------------------------------------*/
1487 /* Make an unreferenced parameter expression in the object	*/
1488 /* refobject.							*/
1489 /* The expression may have either a numeric or string result.	*/
1490 /* the proper "which" value is passed as an argument.		*/
1491 /*								*/
1492 /* Return NULL if unsuccessful, the parameter key (which may	*/
1493 /* have been modified by checkvalidname()) otherwise.		*/
1494 /*--------------------------------------------------------------*/
1495 
makeexprparam(objectptr refobject,char * key,char * value,int which)1496 char *makeexprparam(objectptr refobject, char *key, char *value, int which)
1497 {
1498    oparamptr newops;
1499    char *newkey, stkey[20];
1500    int pidx;
1501 
1502    /* Check against object names, which are reserved words */
1503 
1504    if (key == NULL) {
1505       strcpy(stkey, getnumericalpkey(which));
1506       pidx = 0;
1507       while (check_param(refobject, stkey)) {
1508          pidx++;
1509          sprintf(stkey, "%s%d", getnumericalpkey(which), pidx);
1510       }
1511       newkey = stkey;
1512    }
1513    else {
1514       /* Check parameter key for valid name syntax */
1515 
1516       newkey = checkvalidname(key, NULL);
1517       if (newkey == NULL) newkey = key;
1518 
1519       /* Ensure that no two parameters have the same name! */
1520 
1521       if (check_param(refobject, newkey)) {
1522          Wprintf("There is already a parameter named %s!", newkey);
1523          if (newkey != key) free(newkey);
1524          return NULL;
1525       }
1526    }
1527 
1528    newops = make_new_parameter(newkey);
1529    newops->next = refobject->params;
1530    refobject->params = newops;
1531    newops->type = XC_EXPR;		/* expression requiring evaluation */
1532    newops->which = which;
1533    newops->parameter.expr = strdup(value);
1534    incr_changes(refobject);
1535    if ((newkey != key) && (newkey != stkey)) free(newkey);
1536 
1537    return newops->key;
1538 }
1539 
1540 /*------------------------------------------------------------------*/
1541 /* Make an unreferenced numerical parameter in the object refobject */
1542 /*								    */
1543 /* Return FALSE if unsuccessful, TRUE otherwise.		    */
1544 /*------------------------------------------------------------------*/
1545 
makefloatparam(objectptr refobject,char * key,float value)1546 Boolean makefloatparam(objectptr refobject, char *key, float value)
1547 {
1548    oparamptr newops;
1549    char *newkey;
1550 
1551    /* Check against object names, which are reserved words */
1552 
1553    newkey = checkvalidname(key, NULL);
1554    if (newkey == NULL) newkey = key;
1555 
1556    /* Ensure that no two parameters have the same name! */
1557 
1558    if (check_param(refobject, newkey)) {
1559       Wprintf("There is already a parameter named %s!", newkey);
1560       if (newkey != key) free(newkey);
1561       return FALSE;
1562    }
1563 
1564    newops = make_new_parameter(key);
1565    newops->next = refobject->params;
1566    refobject->params = newops;
1567    newops->type = XC_FLOAT;		/* general-purpose numeric */
1568    newops->which = P_NUMERIC;
1569    newops->parameter.fvalue = value;
1570    incr_changes(refobject);
1571    if (newkey != key) free(newkey);
1572 
1573    return TRUE;
1574 }
1575 
1576 /*----------------------------------------------------------------*/
1577 /* Make an unreferenced string parameter in the object refobject. */
1578 /* Return FALSE if unsuccessful, TRUE otherwise.		  */
1579 /*----------------------------------------------------------------*/
1580 
makestringparam(objectptr refobject,char * key,stringpart * strptr)1581 Boolean makestringparam(objectptr refobject, char *key, stringpart *strptr)
1582 {
1583    oparamptr newops;
1584    char *newkey;
1585 
1586    /* Check against object names, which are reserved words */
1587 
1588    newkey = checkvalidname(key, NULL);
1589    if (newkey == NULL) newkey = key;
1590 
1591    /* Ensure that no two parameters have the same name! */
1592 
1593    if (check_param(refobject, newkey)) {
1594       Wprintf("There is already a parameter named %s!", newkey);
1595       if (newkey != key) free(newkey);
1596       return FALSE;
1597    }
1598 
1599    newops = make_new_parameter(newkey);
1600    newops->next = refobject->params;
1601    refobject->params = newops;
1602    newops->type = XC_STRING;
1603    newops->which = P_SUBSTRING;
1604    newops->parameter.string = strptr;
1605    incr_changes(refobject);
1606    if (newkey != key) free(newkey);
1607 
1608    return TRUE;
1609 }
1610 
1611 /*--------------------------------------------------------------*/
1612 /* Return the built-in parameter key corresponding to a 	*/
1613 /* parameter type (as defined in xcircuit.h).			*/
1614 /*								*/
1615 /* Numerical parameters have designated keys to avoid the	*/
1616 /* necessity of having to specify a key, to avoid conflicts	*/
1617 /* with PostScript predefined keys, and other good reasons.	*/
1618 /*--------------------------------------------------------------*/
1619 
getnumericalpkey(u_int mode)1620 char *getnumericalpkey(u_int mode)
1621 {
1622    static char *param_keys[] = {
1623 	"p_gps", "p_str", "p_xps", "p_yps", "p_sty", "p_jst", "p_an1",
1624 	"p_an2", "p_rad", "p_axs", "p_rot", "p_scl", "p_wid", "p_col",
1625 	"p_bad"
1626    };
1627 
1628    if (mode < 0 || mode > 13) return param_keys[14];
1629    return param_keys[mode];
1630 }
1631 
1632 /*--------------------------------------------------------------*/
1633 /* Make a numerical (integer or float) parameter.		*/
1634 /* If "key" is non-NULL, then the parameter key will be set	*/
1635 /* from this rather than from the list "param_keys".  If the	*/
1636 /* key is an existing key with the same type as "mode", then	*/
1637 /* the new parameter will be linked to the existing one.	*/
1638 /*--------------------------------------------------------------*/
1639 
makenumericalp(genericptr * gelem,u_int mode,char * key,short cycle)1640 void makenumericalp(genericptr *gelem, u_int mode, char *key, short cycle)
1641 {
1642    genericptr pgen, *pathpgen;
1643    oparamptr ops, newops;
1644    eparamptr epp;
1645    XPoint *pptr;
1646    char new_key[7], *keyptr;
1647    int pidx, i;
1648    short loccycle = cycle;
1649 
1650    /* Parameterized strings are handled by makeparam() */
1651 
1652    if (IS_LABEL(*gelem) && mode == P_SUBSTRING) {
1653       Fprintf(stderr, "Error: String parameter passed to makenumericalp()\n");
1654       return;
1655    }
1656 
1657    /* Make sure the parameter doesn't already exist.	   */
1658 
1659    for (epp = (*gelem)->passed; epp != NULL; epp = epp->next) {
1660       ops = match_param(topobject, epp->key);
1661       if (ops->which == (u_char)mode) {
1662 	 if ((mode == P_POSITION_X || mode == P_POSITION_Y) &&
1663 		((*gelem)->type == POLYGON || (*gelem)->type == SPLINE) &&
1664 		(TOPOLY(gelem)->cycle != NULL))
1665 	 {
1666 	    if ((cycle < 0) || (TOPOLY(gelem)->cycle->number != cycle)) {
1667 	       Fprintf(stderr, "Cannot duplicate a point parameter!\n");
1668 	       return;
1669 	    }
1670 	 }
1671 	 else {
1672 	    Fprintf(stderr, "Cannot duplicate a parameter!\n");
1673 	    return;
1674 	 }
1675       }
1676    }
1677 
1678    /* Ensure that no two parameters have the same name! */
1679 
1680    if (key) {
1681       keyptr = checkvalidname(key, NULL);
1682       if (keyptr == NULL) keyptr = key;
1683    }
1684    else {
1685       strcpy(new_key, getnumericalpkey(mode));
1686       pidx = 0;
1687       while (check_param(topobject, new_key)) {
1688          pidx++;
1689          sprintf(new_key, "%s%d", getnumericalpkey(mode), pidx);
1690       }
1691       keyptr = new_key;
1692    }
1693 
1694    /* Add the parameter to the element's parameter list */
1695 
1696    epp = make_new_eparam(keyptr);
1697    epp->next = (*gelem)->passed;
1698    (*gelem)->passed = epp;
1699 
1700    /* If keyptr does not point to an existing parameter, then we need	*/
1701    /* to create it in the object's parameter list and set the default	*/
1702    /* value to the existing value of the element.			*/
1703 
1704    ops = match_param(topobject, keyptr);
1705    if (ops == NULL) {
1706       newops = make_new_parameter(keyptr);
1707       newops->next = topobject->params;
1708       topobject->params = newops;
1709       newops->type = XC_INT;		/* most commonly used value */
1710       newops->which = (u_char)mode;	/* what kind of parameter */
1711       incr_changes(topobject);
1712    }
1713    else {
1714       if (ops->which != (u_char)mode) {
1715 	 free_element_param(*gelem, epp);
1716 	 Fprintf(stderr, "Error: Attempt to link a parameter to "
1717 			"a parameter of a different type\n");
1718 	 goto param_done;
1719       }
1720       else if (ops->type == XC_EXPR)
1721 	 goto param_done;
1722 
1723       if ((newops = match_instance_param(areawin->topinstance, keyptr)) == NULL) {
1724          newops = make_new_parameter(keyptr);
1725          newops->next = areawin->topinstance->params;
1726          areawin->topinstance->params = newops;
1727          newops->type = ops->type;
1728          newops->which = ops->which;
1729       }
1730       else {
1731          /* If the parameter exists and the instance has a non-default	*/
1732          /* value for it, we will not change the instance record.  If	*/
1733          /* the element value is different, then it will change, so we	*/
1734 	 /* should redraw.						*/
1735          drawarea(NULL, NULL, NULL);
1736          newops = NULL;
1737       }
1738    }
1739 
1740    if (newops) {
1741       if (mode == P_COLOR)
1742 	 newops->parameter.ivalue = (int)((*gelem)->color);
1743 
1744       switch((*gelem)->type) {
1745          case LABEL:
1746 	    switch(mode) {
1747 	       case P_POSITION_X:
1748 	          newops->parameter.ivalue = (int)TOLABEL(gelem)->position.x;
1749 	          break;
1750 	       case P_POSITION_Y:
1751 	          newops->parameter.ivalue = (int)TOLABEL(gelem)->position.y;
1752 	          break;
1753 	       case P_ANCHOR:
1754 	          newops->parameter.ivalue = (int)TOLABEL(gelem)->anchor;
1755 	          break;
1756 	       case P_ROTATION:
1757 	          newops->type = XC_FLOAT;
1758 	          newops->parameter.fvalue = TOLABEL(gelem)->rotation;
1759 	          break;
1760 	       case P_SCALE:
1761 	          newops->type = XC_FLOAT;
1762 	          newops->parameter.fvalue = TOLABEL(gelem)->scale;
1763 	          break;
1764 	    }
1765 	    break;
1766          case ARC:
1767 	    switch(mode) {
1768 	       case P_POSITION_X:
1769 	          newops->parameter.ivalue = (int)TOARC(gelem)->position.x;
1770 	          break;
1771 	       case P_POSITION_Y:
1772 	          newops->parameter.ivalue = (int)TOARC(gelem)->position.y;
1773 	          break;
1774 	       case P_ANGLE1:
1775 	          newops->type = XC_FLOAT;
1776 	          newops->parameter.fvalue = TOARC(gelem)->angle1;
1777 	          break;
1778 	       case P_ANGLE2:
1779 	          newops->type = XC_FLOAT;
1780 	          newops->parameter.fvalue = TOARC(gelem)->angle2;
1781 	          break;
1782 	       case P_RADIUS:
1783 	          newops->parameter.ivalue = (int)TOARC(gelem)->radius;
1784 	          break;
1785 	       case P_MINOR_AXIS:
1786 	          newops->parameter.ivalue = (int)TOARC(gelem)->yaxis;
1787 	          break;
1788 	       case P_STYLE:
1789 	          newops->parameter.ivalue = (int)TOARC(gelem)->style;
1790 	          break;
1791 	       case P_LINEWIDTH:
1792 	          newops->type = XC_FLOAT;
1793 	          newops->parameter.fvalue = TOARC(gelem)->width;
1794 	          break;
1795 	    }
1796 	    break;
1797          case OBJINST:
1798 	    switch(mode) {
1799 	       case P_POSITION_X:
1800 	          newops->parameter.ivalue = (int)TOOBJINST(gelem)->position.x;
1801 	          break;
1802 	       case P_POSITION_Y:
1803 	          newops->parameter.ivalue = (int)TOOBJINST(gelem)->position.y;
1804 	          break;
1805 	       case P_ROTATION:
1806 	          newops->type = XC_FLOAT;
1807 	          newops->parameter.fvalue = TOOBJINST(gelem)->rotation;
1808 	          break;
1809 	       case P_SCALE:
1810 	          newops->type = XC_FLOAT;
1811 	          newops->parameter.fvalue = TOOBJINST(gelem)->scale;
1812 	          break;
1813 	    }
1814 	    break;
1815          case POLYGON:
1816 	    if (loccycle == -1)
1817 	       loccycle = (TOPOLY(gelem)->cycle != NULL) ?
1818 			TOPOLY(gelem)->cycle->number : -1;
1819 	    switch(mode) {
1820 	       case P_POSITION_X:
1821 		  if (loccycle == -1) {
1822 	             pptr = TOPOLY(gelem)->points;
1823 	             newops->parameter.ivalue = (int)pptr->x;
1824 		     for (i = 0; i < TOPOLY(gelem)->number; i++) {
1825 	                pptr = TOPOLY(gelem)->points + i;
1826 			pptr->x -= newops->parameter.ivalue;
1827 		     }
1828 		  } else {
1829 	             pptr = TOPOLY(gelem)->points + loccycle;
1830 	             newops->parameter.ivalue = (int)pptr->x;
1831 		  }
1832 	          epp->pdata.pointno = loccycle;
1833 	          break;
1834 	       case P_POSITION_Y:
1835 		  if (loccycle == -1) {
1836 	             pptr = TOPOLY(gelem)->points;
1837 	             newops->parameter.ivalue = (int)pptr->y;
1838 		     for (i = 0; i < TOPOLY(gelem)->number; i++) {
1839 	                pptr = TOPOLY(gelem)->points + i;
1840 			pptr->y -= newops->parameter.ivalue;
1841 		     }
1842 		  } else {
1843 	             pptr = TOPOLY(gelem)->points + loccycle;
1844 	             newops->parameter.ivalue = (int)pptr->y;
1845 		  }
1846 	          epp->pdata.pointno = loccycle;
1847 	          break;
1848 	       case P_STYLE:
1849 	          newops->parameter.ivalue = (int)TOPOLY(gelem)->style;
1850 	          break;
1851 	       case P_LINEWIDTH:
1852 	          newops->type = XC_FLOAT;
1853 	          newops->parameter.fvalue = TOPOLY(gelem)->width;
1854 	          break;
1855 	    }
1856 	    break;
1857          case SPLINE:
1858 	    if (loccycle == -1)
1859 	       loccycle = (TOSPLINE(gelem)->cycle != NULL) ?
1860 			TOSPLINE(gelem)->cycle->number : -1;
1861 	    switch(mode) {
1862 	       case P_POSITION_X:
1863 		  if (loccycle == -1) {
1864 	             pptr = &(TOSPLINE(gelem)->ctrl[0]);
1865 	             newops->parameter.ivalue = (int)pptr->x;
1866 		     for (i = 0; i < 4; i++) {
1867 	                pptr = &(TOSPLINE(gelem)->ctrl[i]);
1868 			pptr->x -= newops->parameter.ivalue;
1869 		     }
1870 		  } else {
1871 	             pptr = TOSPLINE(gelem)->ctrl + loccycle;
1872 	             newops->parameter.ivalue = (int)pptr->x;
1873 		  }
1874 	          epp->pdata.pointno = loccycle;
1875 	          break;
1876 	       case P_POSITION_Y:
1877 		  if (loccycle == -1) {
1878 	             pptr = &(TOSPLINE(gelem)->ctrl[0]);
1879 	             newops->parameter.ivalue = (int)pptr->y;
1880 		     for (i = 0; i < 4; i++) {
1881 	                pptr = &(TOSPLINE(gelem)->ctrl[i]);
1882 			pptr->y -= newops->parameter.ivalue;
1883 		     }
1884 		  } else {
1885 	             pptr = TOSPLINE(gelem)->ctrl + loccycle;
1886 	             newops->parameter.ivalue = (int)pptr->y;
1887 		  }
1888 	          epp->pdata.pointno = loccycle;
1889 	          break;
1890 	       case P_STYLE:
1891 	          newops->parameter.ivalue = (int)TOSPLINE(gelem)->style;
1892 	          break;
1893 	       case P_LINEWIDTH:
1894 	          newops->type = XC_FLOAT;
1895 	          newops->parameter.fvalue = TOSPLINE(gelem)->width;
1896 	          break;
1897 	    }
1898 	    break;
1899          case PATH:
1900 	    if (loccycle == -1 && (mode == P_POSITION_X || mode == P_POSITION_Y)) {
1901 	       pgen = getsubpart(TOPATH(gelem), &pidx);
1902 	       if (ELEMENTTYPE(pgen) == POLYGON)
1903 	          loccycle = (((polyptr)pgen)->cycle != NULL) ?
1904 			((polyptr)pgen)->cycle->number : -1;
1905 	       else
1906 	          loccycle = (((splineptr)pgen)->cycle != NULL) ?
1907 			((splineptr)pgen)->cycle->number : -1;
1908 	    }
1909 	    else {
1910 	       Fprintf(stderr, "Can't parameterize a path point from "
1911 			"the command line.\n");
1912 	       break;
1913 	    }
1914 	    switch(mode) {
1915 	       case P_STYLE:
1916 	          newops->parameter.ivalue = (int)TOPATH(gelem)->style;
1917 	          break;
1918 	       case P_LINEWIDTH:
1919 	          newops->type = XC_FLOAT;
1920 	          newops->parameter.fvalue = TOPATH(gelem)->width;
1921 	          break;
1922 	       case P_POSITION_X:
1923 		  newops->type = XC_INT;
1924 		  if (loccycle == -1) {
1925 		     pathpgen = TOPATH(gelem)->plist;
1926 		     if (ELEMENTTYPE(*pathpgen) == POLYGON) {
1927 	                pptr = TOPOLY(pathpgen)->points;
1928 	                newops->parameter.ivalue = (int)pptr->x;
1929 		     }
1930 		     else {
1931 	                pptr = &(TOSPLINE(pathpgen)->ctrl[0]);
1932 	                newops->parameter.ivalue = (int)pptr->x;
1933 		     }
1934 		     for (pathpgen = TOPATH(gelem)->plist; pathpgen <
1935 				TOPATH(gelem)->plist + TOPATH(gelem)->parts;
1936 				pathpgen++) {
1937 			if (ELEMENTTYPE(*pathpgen) == POLYGON) {
1938 			   for (i = 0; i < TOPOLY(pathpgen)->number; i++) {
1939 	                      pptr = TOPOLY(pathpgen)->points + i;
1940 			      pptr->x -= newops->parameter.ivalue;
1941 			   }
1942 		 	}
1943 			else {
1944 			   for (i = 0; i < 4; i++) {
1945 	                      pptr = &(TOSPLINE(pathpgen)->ctrl[i]);
1946 			      pptr->x -= newops->parameter.ivalue;
1947 			   }
1948 			}
1949 		     }
1950 		  }
1951 		  else {
1952 		     if (ELEMENTTYPE(pgen) == POLYGON)
1953 		        newops->parameter.ivalue = ((polyptr)pgen)->points[loccycle].x;
1954 		     else
1955 		        newops->parameter.ivalue = ((splineptr)pgen)->ctrl[loccycle].x;
1956 	             epp->pdata.pathpt[1] = loccycle;
1957 	             epp->pdata.pathpt[0] = pidx;
1958 		  }
1959 		  break;
1960 	       case P_POSITION_Y:
1961 		  newops->type = XC_INT;
1962 		  if (loccycle == -1) {
1963 		     pathpgen = TOPATH(gelem)->plist;
1964 		     if (ELEMENTTYPE(*pathpgen) == POLYGON) {
1965 	                pptr = TOPOLY(pathpgen)->points;
1966 	                newops->parameter.ivalue = (int)pptr->y;
1967 		     }
1968 		     else {
1969 	                pptr = &(TOSPLINE(pathpgen)->ctrl[0]);
1970 	                newops->parameter.ivalue = (int)pptr->y;
1971 		     }
1972 		     for (pathpgen = TOPATH(gelem)->plist; pathpgen <
1973 				TOPATH(gelem)->plist + TOPATH(gelem)->parts;
1974 				pathpgen++) {
1975 			if (ELEMENTTYPE(*pathpgen) == POLYGON) {
1976 			   for (i = 0; i < TOPOLY(pathpgen)->number; i++) {
1977 	                      pptr = TOPOLY(pathpgen)->points + i;
1978 			      pptr->y -= newops->parameter.ivalue;
1979 			   }
1980 		 	}
1981 			else {
1982 			   for (i = 0; i < 4; i++) {
1983 	                      pptr = &(TOSPLINE(pathpgen)->ctrl[i]);
1984 			      pptr->y -= newops->parameter.ivalue;
1985 			   }
1986 			}
1987 		     }
1988 		  }
1989 		  else {
1990 		     if (ELEMENTTYPE(pgen) == POLYGON)
1991 		        newops->parameter.ivalue = ((polyptr)pgen)->points[loccycle].y;
1992 		     else
1993 		        newops->parameter.ivalue = ((splineptr)pgen)->ctrl[loccycle].y;
1994 	             epp->pdata.pathpt[1] = loccycle;
1995 	             epp->pdata.pathpt[0] = pidx;
1996 		  }
1997 		  break;
1998 	    }
1999 	    break;
2000       }
2001    }
2002 
2003 param_done:
2004    if ((keyptr != new_key) && (keyptr != key)) free(keyptr);
2005 }
2006 
2007 /*--------------------------------------------------------------*/
2008 /* Remove a numerical (integer or float) parameter.  Remove by	*/
2009 /* type, rather than key.  There may be several keys associated	*/
2010 /* with a particular type, so we want to remove all of them.	*/
2011 /*--------------------------------------------------------------*/
2012 
removenumericalp(genericptr * gelem,u_int mode)2013 void removenumericalp(genericptr *gelem, u_int mode)
2014 {
2015    genericptr *pgen;
2016    eparamptr epp;
2017    oparamptr ops;
2018    char *key;
2019    Boolean done = False, is_last = True;
2020 
2021    /* Parameterized strings are handled by makeparam() */
2022    if (mode == P_SUBSTRING) {
2023       Fprintf(stderr, "Error: Unmakenumericalp called on a string parameter.\n");
2024       return;
2025    }
2026 
2027    /* Avoid referencing the object by only looking at the element.	*/
2028    /* But, avoid dereferencing the pointer!				*/
2029 
2030    while (!done) {
2031       key = NULL;
2032       done = True;
2033       for (epp = (*gelem)->passed; epp != NULL; epp = epp->next) {
2034 	 ops = match_param(topobject, epp->key);
2035 	 if (ops == NULL) break;	/* Error---no such parameter */
2036          else if (ops->which == (u_char)mode) {
2037 	    key = ops->key;
2038 	    free_element_param(*gelem, epp);
2039 
2040 	    /* check for any other references to the parameter.  If there */
2041 	    /* are none, remove all instance records of the eparam, then  */
2042 	    /* remove the parameter itself from the object.		  */
2043 
2044 	    for (pgen = topobject->plist; pgen < topobject->plist
2045 			+ topobject->parts; pgen++) {
2046 	       if (*pgen == *gelem) continue;
2047 	       for (epp = (*pgen)->passed; epp != NULL; epp = epp->next) {
2048 		  if (!strcmp(epp->key, key)) {
2049 		     is_last = False;
2050 		     break;
2051 		  }
2052 	       }
2053 	       if (!is_last) break;
2054 	    }
2055 	    if (is_last)
2056 	       free_object_param(topobject, ops);
2057 
2058 	    done = False;
2059 	    break;
2060 	 }
2061       }
2062    }
2063 }
2064 
2065 #ifndef TCL_WRAPPER
2066 
2067 /*--------------------------------------------------------------*/
2068 /* Insert an existing parameter into a string.			*/
2069 /* This code needs to be replaced in the non-Tcl version with	*/
2070 /* a new pop-up window using a callback to labeltext().		*/
2071 /*								*/
2072 /* This routine has been replaced in the Tcl version with a	*/
2073 /* callback to command "label insert parameter" from the 	*/
2074 /* parameter-select pop-up window.				*/
2075 /*--------------------------------------------------------------*/
2076 
insertparam()2077 void insertparam()
2078 {
2079    labelptr tlab;
2080    oparamptr ops;
2081    int result, nparms;
2082    char *selparm;
2083    char *newstr, *sptr;
2084    char *sstart = (char *)malloc(1024);
2085    oparamptr chosen_ops=NULL;
2086 
2087    /* Don't allow nested parameters */
2088 
2089    tlab = TOLABEL(EDITPART);
2090    if (paramcross(topobject, tlab)) {
2091       Wprintf("Parameters cannot be nested!");
2092       return;
2093    }
2094 
2095    nparms = 0;
2096    strcpy(sstart, "Choose: ");
2097    sptr = sstart + 8;
2098    for (ops = topobject->params; ops != NULL; ops = ops->next) {
2099       if (ops->type == XC_STRING) {
2100 	 chosen_ops = ops;
2101 	 nparms++;
2102 	 if (nparms != 1) {
2103 	    strcat(sptr, ", ");
2104 	    sptr += 2;
2105 	 }
2106 	 newstr = stringprint(ops->parameter.string, NULL);
2107 	 sprintf(sptr, "%d = %s = <%s", nparms, ops->key, newstr);
2108 	 free(newstr);
2109 	 newstr = NULL;
2110          sptr += strlen(sptr);
2111       }
2112    }
2113 
2114    /* If only one parameter, then automatically use it.  Otherwise,  */
2115    /* prompt for which parameter to use.                             */
2116 
2117    if (nparms > 1) {
2118       int i=0, select_int;
2119       chosen_ops = NULL;
2120       Wprintf("%s", sstart);
2121       select_int = getkeynum();
2122       for (ops = topobject->params; ops != NULL; ops = ops->next) {
2123 	 if (ops->type == XC_STRING) {
2124 	    if (i==select_int) chosen_ops = ops;
2125 	    i++;
2126 	 }
2127       }
2128    }
2129 
2130    free(sstart);
2131    ops = chosen_ops;
2132    if (ops != NULL) selparm = ops->key;
2133 
2134    if (ops != NULL)
2135       labeltext(PARAM_START, selparm);
2136    else
2137       Wprintf("No such parameter.");
2138 }
2139 
2140 #endif
2141 
2142 /*--------------------------------------------------------------*/
2143 /* Parameterize a label string.					*/
2144 /*--------------------------------------------------------------*/
2145 
makeparam(labelptr thislabel,char * key)2146 void makeparam(labelptr thislabel, char *key)
2147 {
2148    oparamptr newops;
2149    stringpart *begpart, *endpart;
2150    char *newkey;
2151 
2152    /* Ensure that no two parameters have the same name! */
2153 
2154    if (check_param(topobject, key)) {
2155       Wprintf("There is already a parameter named %s!", key);
2156       areawin->textend = 0;
2157       return;
2158    }
2159 
2160    /* make sure this does not overlap another parameter */
2161 
2162    if (paramcross(topobject, thislabel)) {
2163       Wprintf("Parameters cannot be nested!");
2164       areawin->textend = 0;
2165       return;
2166    }
2167 
2168    /* Check parameter for valid name syntax */
2169 
2170    newkey = checkvalidname(key, NULL);
2171    if (newkey == NULL) newkey = key;
2172 
2173    /* First, place PARAM_START and PARAM_END structures at the	*/
2174    /* intended parameter boundaries				*/
2175 
2176    if (areawin->textend > 0 && areawin->textend < areawin->textpos) {
2177       /* partial string */
2178       splitstring(areawin->textend, &thislabel->string, areawin->topinstance);
2179       splitstring(areawin->textpos, &thislabel->string, areawin->topinstance);
2180 
2181       /* Because "splitstring" changes all the pointers, find the    */
2182       /* stringpart structures at textend and textpos positions.     */
2183 
2184       begpart = findstringpart(areawin->textend, NULL, thislabel->string,
2185 		areawin->topinstance);
2186       endpart = findstringpart(areawin->textpos, NULL, thislabel->string,
2187 		areawin->topinstance);
2188 
2189       /* Make the new segments for PARAM_START and PARAM_END.	*/
2190 
2191       begpart = makesegment(&thislabel->string, begpart);
2192       endpart = makesegment(&thislabel->string, endpart);
2193    }
2194    else {				/* full string */
2195       /* Don't include the first font designator as part of the		*/
2196       /* parameter or else havoc ensues.				*/
2197       if (thislabel->string->type == FONT_NAME && thislabel->string->nextpart
2198 		!= NULL) {
2199          makesegment(&thislabel->string, thislabel->string->nextpart);
2200          begpart = thislabel->string->nextpart;
2201       }
2202       else {
2203          makesegment(&thislabel->string, thislabel->string);
2204          begpart = thislabel->string;
2205       }
2206       endpart = makesegment(&thislabel->string, NULL);
2207    }
2208    begpart->type = PARAM_START;
2209    begpart->data.string = (char *)malloc(1 + strlen(newkey));
2210    strcpy(begpart->data.string, newkey);
2211    endpart->type = PARAM_END;
2212    endpart->data.string = (u_char *)NULL;
2213 
2214    /* Now move the sections of string to the object parameter */
2215 
2216    newops = make_new_parameter(newkey);
2217    newops->next = topobject->params;
2218    topobject->params = newops;
2219    newops->type = XC_STRING;
2220    newops->which = P_SUBSTRING;
2221    newops->parameter.string = begpart->nextpart;
2222    begpart->nextpart = endpart->nextpart;
2223    endpart->nextpart = NULL;
2224 
2225    areawin->textend = 0;
2226    incr_changes(topobject);
2227    if (newkey != key) free(newkey);
2228 }
2229 
2230 /*--------------------------------------------------------------*/
2231 /* Destroy the selected parameter in the indicated instance 	*/
2232 /*--------------------------------------------------------------*/
2233 
destroyinst(objinstptr tinst,objectptr refobj,char * key)2234 void destroyinst(objinstptr tinst, objectptr refobj, char *key)
2235 {
2236    oparamptr ops;
2237    /* short k; (jdk) */
2238 
2239    if (tinst->thisobject == refobj) {
2240       ops = match_instance_param(tinst, key);
2241       if (ops != NULL) {
2242 	 if (ops->type == XC_STRING)
2243 	    freelabel(ops->parameter.string);
2244 	 else if (ops->type == XC_EXPR)
2245 	    free(ops->parameter.expr);
2246 	 free_instance_param(tinst, ops);
2247       }
2248    }
2249 }
2250 
2251 /*--------------------------------------------------------------*/
2252 /* Search and destroy the selected parameter in all instances 	*/
2253 /* of the specified object.					*/
2254 /*--------------------------------------------------------------*/
2255 
searchinst(objectptr topobj,objectptr refobj,char * key)2256 void searchinst(objectptr topobj, objectptr refobj, char *key)
2257 {
2258    objinstptr tinst;
2259    genericptr *pgen;
2260 
2261    if (topobj == NULL) return;
2262 
2263    for (pgen = topobj->plist; pgen < topobj->plist + topobj->parts; pgen++) {
2264       if (IS_OBJINST(*pgen)) {
2265 	 tinst = TOOBJINST(pgen);
2266 	 destroyinst(tinst, refobj, key);
2267       }
2268    }
2269 }
2270 
2271 /*--------------------------------------------------------------*/
2272 /* Destroy the object parameter with key "key" in the object	*/
2273 /* "thisobj".  This requires first tracking down and removing	*/
2274 /* all instances of the parameter which may exist anywhere in	*/
2275 /* the database.						*/
2276 /*--------------------------------------------------------------*/
2277 
free_object_param(objectptr thisobj,oparamptr thisparam)2278 void free_object_param(objectptr thisobj, oparamptr thisparam)
2279 {
2280    int k, j, l = -1;
2281    liblistptr spec;
2282    oparamptr ops, lastops = NULL;
2283    genericptr *pgen;
2284    char *key = thisparam->key;
2285 
2286    /* Find all instances of this object and remove any parameter */
2287    /* substitutions which may have been made.		         */
2288 
2289    for (k = 0; k < xobjs.pages; k++) {
2290       if (xobjs.pagelist[k]->pageinst != NULL)
2291          searchinst(xobjs.pagelist[k]->pageinst->thisobject, thisobj, key);
2292    }
2293    for (j = 0; j < xobjs.numlibs; j++) {
2294       for (k = 0; k < xobjs.userlibs[j].number; k++) {
2295 	 if (*(xobjs.userlibs[j].library + k) == thisobj)
2296 	    l = j;
2297 	 else
2298             searchinst(*(xobjs.userlibs[j].library + k), thisobj, key);
2299       }
2300    }
2301 
2302    /* Ensure that this parameter is not referred to in the undo records */
2303    /* We could be kinder and gentler to the undo record here. . . */
2304    flush_undo_stack();
2305 
2306    /* Also check through all instances on the library page */
2307    if (l >= 0)
2308       for (spec = xobjs.userlibs[l].instlist; spec != NULL; spec = spec->next)
2309          destroyinst(spec->thisinst, thisobj, key);
2310 
2311    /* Remove the parameter from any labels that it might occur in */
2312 
2313    for (pgen = thisobj->plist; pgen < thisobj->plist + thisobj->parts; pgen++) {
2314       if (IS_LABEL(*pgen)) {
2315 	 Boolean pending = TRUE;
2316 	 stringpart *strptr;
2317 	 labelptr plab = TOLABEL(pgen);
2318 
2319 	 while (pending) {
2320 	    pending = FALSE;
2321 	    for (strptr = plab->string; strptr != NULL; strptr = strptr->nextpart) {
2322 	       if (strptr->type == PARAM_START) {
2323 	          if (!strcmp(strptr->data.string, key)) {
2324 		     unmakeparam(plab, NULL, strptr);
2325 		     pending = TRUE;
2326 		     break;
2327 	          }
2328 	       }
2329 	    }
2330 	 }
2331       }
2332    }
2333 
2334    /* Remove the parameter from the object itself, tidying up	*/
2335    /* the linked list after it.					*/
2336 
2337    for (ops = thisobj->params; ops != NULL; ops = ops->next) {
2338       if (ops == thisparam) {
2339 	 if (lastops != NULL)
2340 	    lastops->next = ops->next;
2341 	 else
2342 	    thisobj->params = ops->next;
2343 	 free(ops->key);
2344 	 free(ops);
2345 	 break;
2346       }
2347       lastops = ops;
2348    }
2349 
2350    incr_changes(thisobj);
2351 }
2352 
2353 /*--------------------------------------------------------------*/
2354 /* Check if this string contains a parameter			*/
2355 /*--------------------------------------------------------------*/
2356 
searchparam(stringpart * tstr)2357 stringpart *searchparam(stringpart *tstr)
2358 {
2359    stringpart *rval = tstr;
2360    for (rval = tstr; rval != NULL; rval = rval->nextpart)
2361       if (rval->type == PARAM_START)
2362 	 break;
2363    return rval;
2364 }
2365 
2366 /*--------------------------------------------------------------*/
2367 /* Remove parameterization from a label string or substring.	*/
2368 /*--------------------------------------------------------------*/
2369 
unmakeparam(labelptr thislabel,objinstptr thisinst,stringpart * thispart)2370 void unmakeparam(labelptr thislabel, objinstptr thisinst, stringpart *thispart)
2371 {
2372    oparamptr ops;
2373    oparamptr testop;
2374    stringpart *strptr, *lastpart, *endpart, *newstr, *subs;
2375    char *key;
2376 
2377    /* make sure there is a parameter here */
2378 
2379    if (thispart->type != PARAM_START) {
2380       Wprintf("There is no parameter here.");
2381       return;
2382    }
2383    key = thispart->data.string;
2384 
2385    /* Unparameterizing can cause a change in the string */
2386    undrawtext(thislabel);
2387 
2388    /* Methodology change 7/20/06:  Remove only the instance of the	*/
2389    /* parameter.  The parameter itself will be deleted by a different	*/
2390    /* method, using free_object_param().				*/
2391 
2392    ops = (thisinst != NULL) ? match_instance_param(thisinst, key) :
2393 		match_param(topobject, key);
2394 
2395    if (ops == NULL) ops = match_param(topobject, key);
2396 
2397    if (ops == NULL) return;	/* Report error? */
2398 
2399    /* Copy the default parameter into the place we are unparameterizing */
2400    /* Promote first to a string type if necessary */
2401 
2402    if (ops->type == XC_STRING) {
2403       subs = ops->parameter.string;
2404       newstr = NULL;
2405       newstr = stringcopy(subs);
2406 
2407       /* Delete the "PARAM_END" off of the copied string and link it 	*/
2408       /* into the existing string.					*/
2409       /* (NOTE:  If parameter is an empty string, there may be nothing	*/
2410       /* before PARAM_END. . .)						*/
2411 
2412       if (newstr->type != PARAM_END) {
2413          for (endpart = newstr; endpart->nextpart->type != PARAM_END;
2414 		endpart = endpart->nextpart);
2415          free(endpart->nextpart);
2416          endpart->nextpart = thispart->nextpart;
2417       }
2418       else {
2419          endpart = newstr;
2420          newstr = newstr->nextpart;
2421          free(endpart);
2422          endpart = NULL;
2423       }
2424 
2425       /* Remove dangling link from instance parameter */
2426       /* (If this was a global parameter, it will have no effect) */
2427       for (strptr = ops->parameter.string; strptr->type != PARAM_END;
2428 		strptr = strptr->nextpart);
2429       strptr->nextpart = NULL;
2430    }
2431    else {
2432       /* This should not happen */
2433       Fprintf(stderr, "Error:  String contains non-string parameter!\n");
2434       redrawtext(thislabel);
2435       return;
2436    }
2437 
2438    lastpart = NULL;
2439    for (strptr = thislabel->string; strptr != NULL && strptr != thispart;
2440 		strptr = strptr->nextpart) {
2441       lastpart = strptr;
2442    }
2443    if (lastpart == NULL)
2444       thislabel->string = newstr;
2445    else
2446       lastpart->nextpart = newstr;
2447    free(strptr);
2448 
2449    /* Merge strings at boundaries, if possible. */
2450    if (endpart) mergestring(endpart);
2451    mergestring(lastpart);
2452 
2453    redrawtext(thislabel);
2454 }
2455 
2456 /*----------------------------------------------------------------------*/
2457 /* Wrapper for unmakeparam().  Remove a parameterized substring from a	*/
2458 /* label, or remove a numeric parameter from an element.		*/
2459 /*									*/
2460 /* NOTE:  This routine should not combine the instance-only string	*/
2461 /* parameter removal and the numeric parameter deletion, which is	*/
2462 /* fundamentally different in nature.					*/
2463 /*----------------------------------------------------------------------*/
2464 
unparameterize(int mode)2465 void unparameterize(int mode)
2466 {
2467    short *fselect, ptype;
2468    int locpos;
2469    stringpart *strptr, *tmpptr, *lastptr;
2470    labelptr settext;
2471 
2472    if (mode >= 0) {
2473       ptype = (short)param_select[mode];
2474       if (!checkselect(ptype)) select_element(ptype);
2475       if (!checkselect(ptype)) return;
2476    }
2477    else
2478       ptype = ALL_TYPES;
2479 
2480    // NOTE:  Need a different method for interactive edit;  remove only the
2481    // parameter under the cursor.
2482 
2483    if (eventmode == ETEXT_MODE) {
2484       /* ETEXT_MODE implies there is only one selected label */
2485       settext = SELTOLABEL(areawin->selectlist);
2486       strptr = findstringpart(areawin->textpos, &locpos, settext->string,
2487 		areawin->topinstance);
2488 
2489       /* Assume the cursor is inside a parameter and find the end */
2490       while (strptr != NULL && strptr->type != PARAM_START && strptr->type != PARAM_END)
2491 	 strptr = strptr->nextpart;
2492 
2493       if (strptr && (strptr->type == PARAM_END)) {
2494 	 strptr = strptr->nextpart;
2495          tmpptr = settext->string;
2496          while (tmpptr != NULL) {
2497 	    if (tmpptr->type == PARAM_START) {
2498 	       if (tmpptr->nextpart == strptr) {
2499 		  /* tmpptr now points to the parameter to be removed */
2500 		  unmakeparam(settext, areawin->topinstance, tmpptr);
2501 		  break;
2502 	       }
2503 	    }
2504 	    tmpptr = tmpptr->nextpart;
2505 	 }
2506       }
2507       lastptr = NULL;
2508    }
2509    else if ((areawin->selects == 1) && (mode == P_SUBSTRING) && areawin->textend > 0
2510 		&& areawin->textend < areawin->textpos) {
2511       if (SELECTTYPE(areawin->selectlist) != LABEL) return;	 /* Not a label */
2512       settext = SELTOLABEL(areawin->selectlist);
2513       strptr = findstringpart(areawin->textend, &locpos, settext->string,
2514 		areawin->topinstance);
2515       while (strptr != NULL && strptr->type != PARAM_END)
2516 	 strptr = strptr->nextpart;
2517       if (strptr == NULL) return;	/* No parameters */
2518       tmpptr = settext->string;
2519       lastptr = NULL;
2520 
2521       /* Search for parameter boundary, in case selection doesn't include */
2522       /* the whole parameter or the parameter start marker.		  */
2523 
2524       for (tmpptr = settext->string; tmpptr != NULL && tmpptr != strptr;
2525 		tmpptr = nextstringpart(tmpptr, areawin->topinstance))
2526 	 if (tmpptr->type == PARAM_START) lastptr = tmpptr;
2527       /* Finish search, unlinking any parameter we might be inside */
2528       for (; tmpptr != NULL; tmpptr = nextstringpart(tmpptr, areawin->topinstance));
2529 
2530       if (lastptr != NULL) unmakeparam(settext, areawin->topinstance, lastptr);
2531    }
2532    else {
2533       for (fselect = areawin->selectlist; fselect < areawin->selectlist +
2534             areawin->selects; fselect++) {
2535          if ((mode == P_SUBSTRING) && SELECTTYPE(fselect) == LABEL) {
2536 	    u_char found;
2537 
2538             settext = SELTOLABEL(fselect);
2539 
2540 	    // Remove all parameters from the string.
2541 	    found = 1;
2542 	    while (found == (u_char)1) {
2543 	       found = (u_char)0;
2544                strptr = settext->string;
2545 	       while (strptr != NULL) {
2546 	          if (strptr->type == PARAM_START) {
2547 		     unmakeparam(settext, areawin->topinstance, strptr);
2548 		     found = (u_char)1;
2549 		     break;
2550 		  }
2551 	          strptr = strptr->nextpart;
2552 	       }
2553 	    }
2554 	 }
2555 	 else if (mode == P_POSITION) {
2556 	    removenumericalp(topobject->plist + (*fselect), P_POSITION_X);
2557 	    removenumericalp(topobject->plist + (*fselect), P_POSITION_Y);
2558 	 }
2559 	 else
2560 	    removenumericalp(topobject->plist + (*fselect), mode);
2561       }
2562       setparammarks(NULL);
2563    }
2564 }
2565 
2566 /*--------------------------------------------------------------*/
2567 /* Wrapper for makeparam()					*/
2568 /*--------------------------------------------------------------*/
2569 
parameterize(int mode,char * key,short cycle)2570 void parameterize(int mode, char *key, short cycle)
2571 {
2572    short *fselect, ptype;
2573    labelptr settext;
2574    Boolean preselected;
2575 
2576    preselected = (areawin->selects > 0) ? TRUE : FALSE;
2577    if (mode >= 0) {
2578       ptype = (short)param_select[mode];
2579       if (!checkselect(ptype)) select_element(ptype);
2580       if (!checkselect(ptype)) return;
2581    }
2582    else
2583       ptype = ALL_TYPES;
2584 
2585    for (fselect = areawin->selectlist; fselect < areawin->selectlist +
2586             areawin->selects; fselect++) {
2587       if ((mode == P_SUBSTRING) && (areawin->selects == 1) &&
2588 		(SELECTTYPE(fselect) == LABEL)) {
2589          settext = SELTOLABEL(fselect);
2590          makeparam(settext, key);
2591       }
2592       else if (mode == P_POSITION) {
2593 	 makenumericalp(topobject->plist + (*fselect), P_POSITION_X, key, cycle);
2594 	 makenumericalp(topobject->plist + (*fselect), P_POSITION_Y, key, cycle);
2595       }
2596       else
2597 	 makenumericalp(topobject->plist + (*fselect), mode, key, cycle);
2598    }
2599    if (!preselected) unselect_all();
2600    setparammarks(NULL);
2601 }
2602 
2603 /*----------------------------------------------------------------------*/
2604 /* Looks for a parameter overlapping the textend <--> textpos space.	*/
2605 /* Returns True if there is a parameter in this space.			*/
2606 /*----------------------------------------------------------------------*/
2607 
paramcross(objectptr tobj,labelptr tlab)2608 Boolean paramcross(objectptr tobj, labelptr tlab)
2609 {
2610    stringpart *firstptr, *lastptr;
2611    int locpos;
2612 
2613    lastptr = findstringpart(areawin->textpos, &locpos, tlab->string,
2614 			areawin->topinstance);
2615 
2616    /* This text position can't be inside another parameter */
2617    for (firstptr = lastptr; firstptr != NULL; firstptr = firstptr->nextpart)
2618       if (firstptr->type == PARAM_END) return True;
2619 
2620    /* The area between textend and textpos cannot contain a parameter */
2621    if (areawin->textend > 0)
2622       for (firstptr = findstringpart(areawin->textend, &locpos, tlab->string,
2623 		areawin->topinstance); firstptr != lastptr;
2624 		firstptr = firstptr->nextpart)
2625          if (firstptr->type == PARAM_START || firstptr->type == PARAM_END)
2626 	    return True;
2627 
2628    return False;
2629 }
2630 
2631 /*----------------------------------------------------------------------*/
2632 /* Check whether this page object was entered via a library page	*/
2633 /*----------------------------------------------------------------------*/
2634 
checklibtop()2635 int checklibtop()
2636 {
2637    int i;
2638    pushlistptr thispush;
2639 
2640    for (thispush = areawin->stack; thispush != NULL; thispush = thispush->next)
2641       if ((i = is_library(thispush->thisinst->thisobject)) >= 0)
2642 	 return i;
2643 
2644    return -1;
2645 }
2646 
2647 /*----------------------------------------------------------------------*/
2648 /* Remove all parameters from an object	instance			*/
2649 /* (Reverts all parameters to default value)				*/
2650 /*----------------------------------------------------------------------*/
2651 
removeinstparams(objinstptr thisinst)2652 void removeinstparams(objinstptr thisinst)
2653 {
2654    oparamptr ops;
2655 
2656    while (thisinst->params != NULL) {
2657       ops = thisinst->params;
2658       thisinst->params = ops->next;
2659       free(ops->key);
2660       if (ops->type == XC_STRING)
2661 	 freelabel(ops->parameter.string);
2662       else if (ops->type == XC_EXPR)
2663 	 free(ops->parameter.expr);
2664       free(ops);
2665    }
2666 }
2667 
2668 /*----------------------------------------------------------------------*/
2669 /* Remove all parameters from an object.				*/
2670 /*----------------------------------------------------------------------*/
2671 
removeparams(objectptr thisobj)2672 void removeparams(objectptr thisobj)
2673 {
2674    oparamptr ops;
2675 
2676    while (thisobj->params != NULL) {
2677       ops = thisobj->params;
2678       thisobj->params = ops->next;
2679       free(ops->key);
2680       if (ops->type == XC_STRING)
2681 	 freelabel(ops->parameter.string);
2682       else if (ops->type == XC_EXPR)
2683 	 free(ops->parameter.expr);
2684       free(ops);
2685    }
2686    thisobj->params = NULL;
2687 }
2688 
2689 /*-----------------------------------------------------------------------*/
2690