Merge branch 'master' into xcircuit-3.10
[xcircuit.git] / parameter.c
blobe26bd956406990a78c9d6e5bde3944434c7e0d1e
1 /*----------------------------------------------------------------------*/
2 /* parameter.c */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*----------------------------------------------------------------------*/
6 /*----------------------------------------------------------------------*/
7 /* written by Tim Edwards, 10/26/99 */
8 /* revised for segmented strings, 3/8/01 */
9 /*----------------------------------------------------------------------*/
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
15 #ifndef XC_WIN32
16 #include <X11/Intrinsic.h>
17 #include <X11/StringDefs.h>
18 #endif
20 #ifdef TCL_WRAPPER
21 #include <tk.h>
22 #else
23 #ifndef XC_WIN32
24 #include "Xw/Xw.h"
25 #endif
26 #endif
28 /*----------------------------------------------------------------------*/
29 /* Local includes */
30 /*----------------------------------------------------------------------*/
32 #include "xcircuit.h"
33 #include "menudep.h"
35 /*----------------------------------------------------------------------*/
36 /* Function prototype declarations */
37 /*----------------------------------------------------------------------*/
38 #include "prototypes.h"
40 /*----------------------------------------------------------------------*/
41 /* Externally declared global variables */
42 /*----------------------------------------------------------------------*/
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];
54 /*----------------------------------------------------------------------*/
55 /* The following u_char array matches parameterization types to element */
56 /* types which are able to accept the given parameterization. */
57 /*----------------------------------------------------------------------*/
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 */
78 #ifdef TCL_WRAPPER
79 #ifndef _MSC_VER
80 xcWidget *param_buttons[] = { NULL /* (jdk) */
81 /* To be done---map buttons to Tk_Windows! */
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 */
104 #endif
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 /*----------------------------------------------------------------------*/
111 /*----------------------------------------------------------------------*/
112 /* Check for the existance of a parameter with key "key" in object */
113 /* "thisobj". Return true if the parameter exists. */
114 /*----------------------------------------------------------------------*/
116 Boolean check_param(objectptr thisobj, char *key)
118 oparamptr tops;
120 for (tops = thisobj->params; tops != NULL; tops = tops->next)
121 if (!strcmp(tops->key, key))
122 return TRUE;
124 return FALSE;
127 /*----------------------------------------------------------------------*/
128 /* Create a new parameter; allocate memory for the parameter and the */
129 /* key. */
130 /*----------------------------------------------------------------------*/
132 oparamptr make_new_parameter(char *key)
134 oparamptr newops;
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;
143 /*----------------------------------------------------------------------*/
144 /* Create a new element (numeric) parameter. Fill in essential values */
145 /*----------------------------------------------------------------------*/
147 eparamptr make_new_eparam(char *key)
149 eparamptr newepp;
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;
158 return newepp;
161 /*----------------------------------------------------------------------*/
162 /* Determine if a parameter is indirectly referenced. If so, return */
163 /* the parameter name. If not, return NULL. */
164 /*----------------------------------------------------------------------*/
166 char *find_indirect_param(objinstptr thisinst, char *refkey)
168 eparamptr epp;
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;
174 return NULL;
177 /*----------------------------------------------------------------------*/
178 /* Find the parameter in the indicated object by key */
179 /*----------------------------------------------------------------------*/
181 oparamptr match_param(objectptr thisobj, char *key)
183 oparamptr fparam;
185 for (fparam = thisobj->params; fparam != NULL; fparam = fparam->next)
186 if (!strcmp(fparam->key, key))
187 return fparam;
189 return NULL; /* No parameter matched the key---error condition */
192 /*----------------------------------------------------------------------*/
193 /* Find the parameter in the indicated instance by key. If no such */
194 /* instance value exists, return NULL. */
195 /*----------------------------------------------------------------------*/
197 oparamptr match_instance_param(objinstptr thisinst, char *key)
199 oparamptr fparam;
201 for (fparam = thisinst->params; fparam != NULL; fparam = fparam->next)
202 if (!strcmp(fparam->key, key))
203 return fparam;
205 return NULL; /* No parameter matched the key---error condition */
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 /*----------------------------------------------------------------------*/
219 oparamptr find_param(objinstptr thisinst, char *key)
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;
227 return fparam;
230 /*----------------------------------------------------------------------*/
231 /* Find the total number of parameters in an object */
232 /*----------------------------------------------------------------------*/
234 int get_num_params(objectptr thisobj)
236 oparamptr fparam;
237 int nparam = 0;
239 for (fparam = thisobj->params; fparam != NULL; fparam = fparam->next)
240 nparam++;
241 return nparam;
244 /*----------------------------------------------------------------------*/
245 /* Remove all element parameters from an element */
246 /*----------------------------------------------------------------------*/
248 void free_all_eparams(genericptr thiselem)
250 while (thiselem->passed != NULL)
251 free_element_param(thiselem, thiselem->passed);
254 /*----------------------------------------------------------------------*/
255 /* Remove an element parameter (eparam) and free memory associated with */
256 /* the parameter key. */
257 /*----------------------------------------------------------------------*/
259 void free_element_param(genericptr thiselem, eparamptr thisepp)
261 eparamptr epp, lastepp = NULL;
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;
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. */
275 if ((epp->flags & P_INDIRECT) && (epp->pdata.refkey != NULL))
276 free(epp->pdata.refkey);
278 free(epp->key);
279 free(epp);
280 break;
282 lastepp = epp;
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 /*----------------------------------------------------------------------*/
295 oparamptr free_instance_param(objinstptr thisinst, oparamptr thisparam)
297 oparamptr ops, lastops = NULL;
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;
309 lastops = ops;
311 return lastops;
314 /*----------------------------------------------------------------------*/
315 /* Convenience function used by files.c to set a color parameter. */
316 /*----------------------------------------------------------------------*/
318 void std_eparam(genericptr gen, char *key)
320 eparamptr epp;
322 if (key == NULL) return;
324 epp = make_new_eparam(key);
325 epp->next = gen->passed;
326 gen->passed = epp;
329 /*----------------------------------------------*/
330 /* Draw a circle at all parameter positions */
331 /*----------------------------------------------*/
333 void indicateparams(genericptr thiselem)
335 int k;
336 oparamptr ops;
337 eparamptr epp;
338 genericptr *pgen;
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;
378 break;
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 /*----------------------------------------------*/
392 #ifdef TCL_WRAPPER
393 void setparammarks(genericptr thiselem)
395 /* Set GUI variables associated with the "parameter" menu. */
397 int i;
398 oparamptr ops;
399 eparamptr epp;
400 Boolean ptest[NUM_PARAM_TYPES];
402 for (i = 0; i < NUM_PARAM_TYPES; i++)
403 ptest[i] = FALSE;
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;
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". */
420 for (i = P_POSITION_X; i <= P_COLOR; i++)
421 if (ptest[i] != TRUE)
422 XcInternalTagCall(xcinterp, 3, "parameter", "replace", translateparamtype(i));
424 #else
426 void setparammarks(genericptr thiselem)
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;
435 /* Clear all checkmarks */
437 for (i = 0; i < rlength; i++) {
438 XtSetArg(wargs[0], XtNsetMark, False);
439 XtSetValues(*param_buttons[i], wargs, 1);
442 /* Check those properties which are parameterized in the element */
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);
453 /* Unmanage widgets which do not apply to the element type */
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]);
463 #endif
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 /*------------------------------------------------------*/
472 void exprsub(genericptr thiselem)
474 genericptr *pgen;
475 eparamptr epp;
476 int k, ival;
477 oparamptr dps, ops;
478 float fval;
479 XPoint *setpt;
480 char *promoted;
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;
505 else { /* spline */
506 TOSPLINE(pgen)->ctrl[k].x = ival;
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;
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;
526 else { /* spline */
527 TOSPLINE(pgen)->ctrl[k].y = ival;
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;
544 /*------------------------------------------------------*/
545 /* Make numerical parameter substitutions into an */
546 /* element. */
547 /*------------------------------------------------------*/
549 int epsubstitute(genericptr thiselem, objectptr thisobj, objinstptr pinst,
550 Boolean *needrecalc)
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;
562 for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
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. */
568 ops = match_param(thisobj, epp->key);
569 dps = (pinst != NULL) ? find_param(pinst, epp->key) : ops;
571 if (dps != NULL) {
573 /* Get integer and float values. Promote types if necessary */
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;
600 else if (ops == NULL)
601 continue;
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;
609 thisinst = (objinstptr)thiselem;
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;
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);
625 /* Create a new instance parameter */
626 newop = copyparameter(dps);
627 newop->next = thisinst->params;
628 thisinst->params = newop;
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);
635 continue;
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;
654 else { /* spline */
655 diff = ival - TOSPLINE(pgen)->ctrl[0].x;
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;
666 else { /* spline */
667 for (i = 0; i < 4; i++) {
668 TOSPLINE(pgen)->ctrl[i].x += diff;
670 if (needrecalc) *needrecalc = True;
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;
680 else { /* spline */
681 TOSPLINE(pgen)->ctrl[k].x = ival;
682 if (needrecalc) *needrecalc = True;
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;
695 else {
696 setpt = TOPOLY(&thiselem)->points + k;
697 setpt->x = ival;
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;
709 else {
710 TOSPLINE(&thiselem)->ctrl[k].x = ival;
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;
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;
735 else { /* spline */
736 diff = ival - TOSPLINE(pgen)->ctrl[0].y;
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;
747 else { /* spline */
748 for (i = 0; i < 4; i++) {
749 TOSPLINE(pgen)->ctrl[i].y += diff;
751 if (needrecalc) *needrecalc = True;
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;
761 else { /* spline */
762 TOSPLINE(pgen)->ctrl[k].y = ival;
763 if (needrecalc) *needrecalc = True;
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;
776 else {
777 setpt = TOPOLY(&thiselem)->points + k;
778 setpt->y = ival;
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;
790 else {
791 TOSPLINE(&thiselem)->ctrl[k].y = ival;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
906 break;
907 case P_COLOR:
908 retval = max(retval, 0);
909 thiselem->color = ival;
910 break;
914 return retval;
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 /*------------------------------------------------------*/
930 int opsubstitute(objectptr thisobj, objinstptr pinst)
932 genericptr *eptr, *pgen, thiselem;
933 stringpart *strptr;
934 int retval = -1;
935 Boolean needrecalc; /* for arcs and splines */
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. */
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));
951 if (thisobj->params == NULL)
952 return -1; /* object has no parameters */
954 for (eptr = thisobj->plist; eptr < thisobj->plist + thisobj->parts; eptr++) {
956 needrecalc = False;
957 thiselem = *eptr;
958 if (thiselem->passed == NULL) continue; /* Nothing to substitute */
959 retval = epsubstitute(thiselem, thisobj, pinst, &needrecalc);
961 /* substitutions into arcs and splines require that the */
962 /* line segments be recalculated. */
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;
981 return retval;
984 /*------------------------------------------------------*/
985 /* Same as above, but determines the object from the */
986 /* current page hierarchy. */
987 /*------------------------------------------------------*/
989 int psubstitute(objinstptr thisinst)
991 objinstptr pinst;
992 objectptr thisobj;
994 pinst = (thisinst == areawin->topinstance) ? areawin->topinstance : thisinst;
995 if (pinst == NULL) return -1; /* there is no instance */
996 thisobj = pinst->thisobject;
998 return opsubstitute(thisobj, pinst);
1001 /*----------------------------------------------*/
1002 /* Check if an element contains a parameter. */
1003 /*----------------------------------------------*/
1005 Boolean has_param(genericptr celem)
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;
1014 if (celem->passed != NULL) return TRUE;
1015 return FALSE;
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 /*------------------------------------------------------*/
1027 void pwriteback(objinstptr thisinst)
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;
1042 pinst = thisinst;
1043 thisobj = (pinst == NULL) ? topobject : pinst->thisobject;
1045 /* Make sure that all instance values exist */
1046 if (pinst != NULL) copyparams(pinst, pinst);
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(). */
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;
1101 else
1102 wtemp.ival = TOSPLINE(pgen)->ctrl[k].x;
1103 break;
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;
1133 else
1134 wtemp.ival = TOSPLINE(pgen)->ctrl[k].y;
1135 break;
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;
1153 break;
1154 case P_ANCHOR:
1155 switch(thiselem->type) {
1156 case LABEL:
1157 wtemp.ival = TOLABEL(eptr)->anchor;
1158 break;
1160 break;
1161 case P_ANGLE1:
1162 switch(thiselem->type) {
1163 case ARC:
1164 wtemp.fval = TOARC(eptr)->angle1;
1165 break;
1167 break;
1168 case P_ANGLE2:
1169 switch(thiselem->type) {
1170 case ARC:
1171 wtemp.fval = TOARC(eptr)->angle1;
1172 break;
1174 break;
1175 case P_RADIUS:
1176 switch(thiselem->type) {
1177 case ARC:
1178 wtemp.ival = TOARC(eptr)->radius;
1179 break;
1181 break;
1182 case P_MINOR_AXIS:
1183 switch(thiselem->type) {
1184 case ARC:
1185 wtemp.ival = TOARC(eptr)->yaxis;
1186 break;
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;
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;
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;
1224 break;
1225 case P_COLOR:
1226 wtemp.ival = thiselem->color;
1227 break;
1229 type = (ips != NULL) ? ips->type : ops->type;
1230 if (type != XC_FLOAT && type != XC_INT) break;
1232 destivalptr = (ips != NULL) ? &ips->parameter.ivalue
1233 : &ops->parameter.ivalue;
1234 if ((!changed) && (wtemp.ival != *destivalptr)) {
1235 *destivalptr = wtemp.ival;
1236 changed = TRUE;
1238 else if (found > 1) need_redraw = TRUE;
1239 break;
1245 /* Any instance values which are identical to the default value */
1246 /* get erased (so they won't be written to the output unnecessarily) */
1248 if (pinst != NULL) resolveparams(pinst);
1250 if (need_redraw) {
1251 incr_changes(thisobj);
1252 invalidate_netlist(thisobj);
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. */
1259 if (need_redraw && (thisinst == areawin->topinstance))
1260 drawarea(NULL, NULL, NULL);
1263 /*------------------------------------------------------*/
1264 /* If the instance comes from the library, replace the */
1265 /* default value with the instance value. */
1266 /*------------------------------------------------------*/
1268 void replaceparams(objinstptr thisinst)
1270 objectptr thisobj;
1271 oparamptr ops, ips;
1272 /* int i, nullparms = 0; (jdk) */
1274 thisobj = thisinst->thisobject;
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 */
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);
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.
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);
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);
1305 break;
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 /*------------------------------------------------------*/
1317 void resolveparams(objinstptr thisinst)
1319 objectptr thisobj;
1320 liblistptr spec;
1321 oparamptr ops, ips;
1322 int i;
1324 /* If the instance has no parameters itself, ignore it. */
1325 if (thisinst == NULL || thisinst->params == NULL) return;
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) */
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;
1337 if ((spec == NULL) || (spec->virtual == FALSE)) {
1338 /* Fprintf(stdout, "Came from library: changing default value\n"); */
1339 replaceparams(thisinst);
1340 return;
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. */
1349 else if (is_page(thisinst->thisobject) >= 0) {
1350 replaceparams(thisinst);
1351 return;
1354 thisobj = thisinst->thisobject;
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 */
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;
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);
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);
1376 break;
1377 case XC_INT: case XC_FLOAT:
1378 if (ops->parameter.ivalue == ips->parameter.ivalue) {
1379 free_instance_param(thisinst, ips);
1381 break;
1385 if (thisinst->params != NULL) {
1386 /* Object must recompute bounding box if any instance */
1387 /* uses a non-default parameter. */
1389 calcbboxvalues(thisinst, NULL);
1393 /*--------------------------------------------------------------*/
1394 /* Return a copy of the single eparameter "cepp" */
1395 /*--------------------------------------------------------------*/
1397 eparamptr copyeparam(eparamptr cepp, genericptr thiselem)
1399 eparamptr newepp;
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;
1410 /*------------------------------------------------------*/
1411 /* Copy all element parameters from source to dest */
1412 /*------------------------------------------------------*/
1414 void copyalleparams(genericptr destinst, genericptr sourceinst)
1416 eparamptr cepp, newepp;
1418 for (cepp = sourceinst->passed; cepp != NULL; cepp = cepp->next) {
1419 newepp = copyeparam(cepp, sourceinst);
1420 newepp->next = destinst->passed;
1421 destinst->passed = newepp;
1425 /*--------------------------------------------------------------*/
1426 /* Return a copy of the single parameter "cops" */
1427 /*--------------------------------------------------------------*/
1429 oparamptr copyparameter(oparamptr cops)
1431 oparamptr newops;
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;
1450 return newops;
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 /*------------------------------------------------------*/
1465 void copyparams(objinstptr destinst, objinstptr sourceinst)
1467 oparamptr psource, cops, newops, ips;
1469 if (sourceinst == NULL) return;
1470 if (destinst == sourceinst)
1471 psource = sourceinst->thisobject->params;
1472 else
1473 psource = sourceinst->params;
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;
1481 else if ((cops->type == XC_EXPR) && (ips->type != XC_EXPR))
1482 free_instance_param(destinst, ips);
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 /*--------------------------------------------------------------*/
1496 char *makeexprparam(objectptr refobject, char *key, char *value, int which)
1498 oparamptr newops;
1499 char *newkey, stkey[20];
1500 int pidx;
1502 /* Check against object names, which are reserved words */
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);
1511 newkey = stkey;
1513 else {
1514 /* Check parameter key for valid name syntax */
1516 newkey = checkvalidname(key, NULL);
1517 if (newkey == NULL) newkey = key;
1519 /* Ensure that no two parameters have the same name! */
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;
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);
1537 return newops->key;
1540 /*------------------------------------------------------------------*/
1541 /* Make an unreferenced numerical parameter in the object refobject */
1542 /* */
1543 /* Return FALSE if unsuccessful, TRUE otherwise. */
1544 /*------------------------------------------------------------------*/
1546 Boolean makefloatparam(objectptr refobject, char *key, float value)
1548 oparamptr newops;
1549 char *newkey;
1551 /* Check against object names, which are reserved words */
1553 newkey = checkvalidname(key, NULL);
1554 if (newkey == NULL) newkey = key;
1556 /* Ensure that no two parameters have the same name! */
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;
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);
1573 return TRUE;
1576 /*----------------------------------------------------------------*/
1577 /* Make an unreferenced string parameter in the object refobject. */
1578 /* Return FALSE if unsuccessful, TRUE otherwise. */
1579 /*----------------------------------------------------------------*/
1581 Boolean makestringparam(objectptr refobject, char *key, stringpart *strptr)
1583 oparamptr newops;
1584 char *newkey;
1586 /* Check against object names, which are reserved words */
1588 newkey = checkvalidname(key, NULL);
1589 if (newkey == NULL) newkey = key;
1591 /* Ensure that no two parameters have the same name! */
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;
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);
1608 return TRUE;
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 /*--------------------------------------------------------------*/
1620 char *getnumericalpkey(u_int mode)
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"
1628 if (mode < 0 || mode > 13) return param_keys[14];
1629 return param_keys[mode];
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 /*--------------------------------------------------------------*/
1640 void makenumericalp(genericptr *gelem, u_int mode, char *key, short cycle)
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;
1650 /* Parameterized strings are handled by makeparam() */
1652 if (IS_LABEL(*gelem) && mode == P_SUBSTRING) {
1653 Fprintf(stderr, "Error: String parameter passed to makenumericalp()\n");
1654 return;
1657 /* Make sure the parameter doesn't already exist. */
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))
1666 if ((cycle < 0) || (TOPOLY(gelem)->cycle->number != cycle)) {
1667 Fprintf(stderr, "Cannot duplicate a point parameter!\n");
1668 return;
1671 else {
1672 Fprintf(stderr, "Cannot duplicate a parameter!\n");
1673 return;
1678 /* Ensure that no two parameters have the same name! */
1680 if (key) {
1681 keyptr = checkvalidname(key, NULL);
1682 if (keyptr == NULL) keyptr = key;
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);
1691 keyptr = new_key;
1694 /* Add the parameter to the element's parameter list */
1696 epp = make_new_eparam(keyptr);
1697 epp->next = (*gelem)->passed;
1698 (*gelem)->passed = epp;
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. */
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);
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;
1720 else if (ops->type == XC_EXPR)
1721 goto param_done;
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;
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;
1740 if (newops) {
1741 if (mode == P_COLOR)
1742 newops->parameter.ivalue = (int)((*gelem)->color);
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;
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;
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;
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;
1828 } else {
1829 pptr = TOPOLY(gelem)->points + loccycle;
1830 newops->parameter.ivalue = (int)pptr->x;
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;
1842 } else {
1843 pptr = TOPOLY(gelem)->points + loccycle;
1844 newops->parameter.ivalue = (int)pptr->y;
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;
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;
1870 } else {
1871 pptr = TOSPLINE(gelem)->ctrl + loccycle;
1872 newops->parameter.ivalue = (int)pptr->x;
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;
1884 } else {
1885 pptr = TOSPLINE(gelem)->ctrl + loccycle;
1886 newops->parameter.ivalue = (int)pptr->y;
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;
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;
1909 else {
1910 Fprintf(stderr, "Can't parameterize a path point from "
1911 "the command line.\n");
1912 break;
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;
1930 else {
1931 pptr = &(TOSPLINE(pathpgen)->ctrl[0]);
1932 newops->parameter.ivalue = (int)pptr->x;
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;
1943 else {
1944 for (i = 0; i < 4; i++) {
1945 pptr = &(TOSPLINE(pathpgen)->ctrl[i]);
1946 pptr->x -= newops->parameter.ivalue;
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;
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;
1968 else {
1969 pptr = &(TOSPLINE(pathpgen)->ctrl[0]);
1970 newops->parameter.ivalue = (int)pptr->y;
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;
1981 else {
1982 for (i = 0; i < 4; i++) {
1983 pptr = &(TOSPLINE(pathpgen)->ctrl[i]);
1984 pptr->y -= newops->parameter.ivalue;
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;
1997 break;
1999 break;
2003 param_done:
2004 if ((keyptr != new_key) && (keyptr != key)) free(keyptr);
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 /*--------------------------------------------------------------*/
2013 void removenumericalp(genericptr *gelem, u_int mode)
2015 genericptr *pgen;
2016 eparamptr epp;
2017 oparamptr ops;
2018 char *key;
2019 Boolean done = False, is_last = True;
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;
2027 /* Avoid referencing the object by only looking at the element. */
2028 /* But, avoid dereferencing the pointer! */
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);
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. */
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;
2053 if (!is_last) break;
2055 if (is_last)
2056 free_object_param(topobject, ops);
2058 done = False;
2059 break;
2065 #ifndef TCL_WRAPPER
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 /*--------------------------------------------------------------*/
2077 void insertparam()
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;
2087 /* Don't allow nested parameters */
2089 tlab = TOLABEL(EDITPART);
2090 if (paramcross(topobject, tlab)) {
2091 Wprintf("Parameters cannot be nested!");
2092 return;
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;
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);
2114 /* If only one parameter, then automatically use it. Otherwise, */
2115 /* prompt for which parameter to use. */
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++;
2130 free(sstart);
2131 ops = chosen_ops;
2132 if (ops != NULL) selparm = ops->key;
2134 if (ops != NULL)
2135 labeltext(PARAM_START, selparm);
2136 else
2137 Wprintf("No such parameter.");
2140 #endif
2142 /*--------------------------------------------------------------*/
2143 /* Parameterize a label string. */
2144 /*--------------------------------------------------------------*/
2146 void makeparam(labelptr thislabel, char *key)
2148 oparamptr newops;
2149 stringpart *begpart, *endpart;
2150 char *newkey;
2152 /* Ensure that no two parameters have the same name! */
2154 if (check_param(topobject, key)) {
2155 Wprintf("There is already a parameter named %s!", key);
2156 areawin->textend = 0;
2157 return;
2160 /* make sure this does not overlap another parameter */
2162 if (paramcross(topobject, thislabel)) {
2163 Wprintf("Parameters cannot be nested!");
2164 areawin->textend = 0;
2165 return;
2168 /* Check parameter for valid name syntax */
2170 newkey = checkvalidname(key, NULL);
2171 if (newkey == NULL) newkey = key;
2173 /* First, place PARAM_START and PARAM_END structures at the */
2174 /* intended parameter boundaries */
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);
2181 /* Because "splitstring" changes all the pointers, find the */
2182 /* stringpart structures at textend and textpos positions. */
2184 begpart = findstringpart(areawin->textend, NULL, thislabel->string,
2185 areawin->topinstance);
2186 endpart = findstringpart(areawin->textpos, NULL, thislabel->string,
2187 areawin->topinstance);
2189 /* Make the new segments for PARAM_START and PARAM_END. */
2191 begpart = makesegment(&thislabel->string, begpart);
2192 endpart = makesegment(&thislabel->string, endpart);
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;
2202 else {
2203 makesegment(&thislabel->string, thislabel->string);
2204 begpart = thislabel->string;
2206 endpart = makesegment(&thislabel->string, NULL);
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;
2214 /* Now move the sections of string to the object parameter */
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;
2225 areawin->textend = 0;
2226 incr_changes(topobject);
2227 if (newkey != key) free(newkey);
2230 /*--------------------------------------------------------------*/
2231 /* Destroy the selected parameter in the indicated instance */
2232 /*--------------------------------------------------------------*/
2234 void destroyinst(objinstptr tinst, objectptr refobj, char *key)
2236 oparamptr ops;
2237 /* short k; (jdk) */
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);
2251 /*--------------------------------------------------------------*/
2252 /* Search and destroy the selected parameter in all instances */
2253 /* of the specified object. */
2254 /*--------------------------------------------------------------*/
2256 void searchinst(objectptr topobj, objectptr refobj, char *key)
2258 objinstptr tinst;
2259 genericptr *pgen;
2261 if (topobj == NULL) return;
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);
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 /*--------------------------------------------------------------*/
2278 void free_object_param(objectptr thisobj, oparamptr thisparam)
2280 int k, j, l = -1;
2281 liblistptr spec;
2282 oparamptr ops, lastops = NULL;
2283 genericptr *pgen;
2284 char *key = thisparam->key;
2286 /* Find all instances of this object and remove any parameter */
2287 /* substitutions which may have been made. */
2289 for (k = 0; k < xobjs.pages; k++) {
2290 if (xobjs.pagelist[k]->pageinst != NULL)
2291 searchinst(xobjs.pagelist[k]->pageinst->thisobject, thisobj, key);
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);
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();
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);
2311 /* Remove the parameter from any labels that it might occur in */
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);
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;
2334 /* Remove the parameter from the object itself, tidying up */
2335 /* the linked list after it. */
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;
2347 lastops = ops;
2350 incr_changes(thisobj);
2353 /*--------------------------------------------------------------*/
2354 /* Check if this string contains a parameter */
2355 /*--------------------------------------------------------------*/
2357 stringpart *searchparam(stringpart *tstr)
2359 stringpart *rval = tstr;
2360 for (rval = tstr; rval != NULL; rval = rval->nextpart)
2361 if (rval->type == PARAM_START)
2362 break;
2363 return rval;
2366 /*--------------------------------------------------------------*/
2367 /* Remove parameterization from a label string or substring. */
2368 /*--------------------------------------------------------------*/
2370 void unmakeparam(labelptr thislabel, objinstptr thisinst, stringpart *thispart)
2372 oparamptr ops;
2373 oparamptr testop;
2374 stringpart *strptr, *lastpart, *endpart, *newstr, *subs;
2375 char *key;
2377 /* make sure there is a parameter here */
2379 if (thispart->type != PARAM_START) {
2380 Wprintf("There is no parameter here.");
2381 return;
2383 key = thispart->data.string;
2385 /* Unparameterizing can cause a change in the string */
2386 undrawtext(thislabel);
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(). */
2392 ops = (thisinst != NULL) ? match_instance_param(thisinst, key) :
2393 match_param(topobject, key);
2395 if (ops == NULL) ops = match_param(topobject, key);
2397 if (ops == NULL) return; /* Report error? */
2399 /* Copy the default parameter into the place we are unparameterizing */
2400 /* Promote first to a string type if necessary */
2402 if (ops->type == XC_STRING) {
2403 subs = ops->parameter.string;
2404 newstr = NULL;
2405 newstr = stringcopy(subs);
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. . .) */
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;
2418 else {
2419 endpart = newstr;
2420 newstr = newstr->nextpart;
2421 free(endpart);
2422 endpart = NULL;
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;
2431 else {
2432 /* This should not happen */
2433 Fprintf(stderr, "Error: String contains non-string parameter!\n");
2434 redrawtext(thislabel);
2435 return;
2438 lastpart = NULL;
2439 for (strptr = thislabel->string; strptr != NULL && strptr != thispart;
2440 strptr = strptr->nextpart) {
2441 lastpart = strptr;
2443 if (lastpart == NULL)
2444 thislabel->string = newstr;
2445 else
2446 lastpart->nextpart = newstr;
2447 free(strptr);
2449 /* Merge strings at boundaries, if possible. */
2450 if (endpart) mergestring(endpart);
2451 mergestring(lastpart);
2453 redrawtext(thislabel);
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 /*----------------------------------------------------------------------*/
2465 void unparameterize(int mode)
2467 short *fselect, ptype;
2468 int locpos;
2469 stringpart *strptr, *tmpptr, *lastptr;
2470 labelptr settext;
2472 if (mode >= 0) {
2473 ptype = (short)param_select[mode];
2474 if (!checkselect(ptype)) select_element(ptype);
2475 if (!checkselect(ptype)) return;
2477 else
2478 ptype = ALL_TYPES;
2480 // NOTE: Need a different method for interactive edit; remove only the
2481 // parameter under the cursor.
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);
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;
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;
2504 tmpptr = tmpptr->nextpart;
2507 lastptr = NULL;
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;
2521 /* Search for parameter boundary, in case selection doesn't include */
2522 /* the whole parameter or the parameter start marker. */
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));
2530 if (lastptr != NULL) unmakeparam(settext, areawin->topinstance, lastptr);
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;
2538 settext = SELTOLABEL(fselect);
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;
2551 strptr = strptr->nextpart;
2555 else if (mode == P_POSITION) {
2556 removenumericalp(topobject->plist + (*fselect), P_POSITION_X);
2557 removenumericalp(topobject->plist + (*fselect), P_POSITION_Y);
2559 else
2560 removenumericalp(topobject->plist + (*fselect), mode);
2562 setparammarks(NULL);
2566 /*--------------------------------------------------------------*/
2567 /* Wrapper for makeparam() */
2568 /*--------------------------------------------------------------*/
2570 void parameterize(int mode, char *key, short cycle)
2572 short *fselect, ptype;
2573 labelptr settext;
2574 Boolean preselected;
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;
2582 else
2583 ptype = ALL_TYPES;
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);
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);
2596 else
2597 makenumericalp(topobject->plist + (*fselect), mode, key, cycle);
2599 if (!preselected) unselect_all();
2600 setparammarks(NULL);
2603 /*----------------------------------------------------------------------*/
2604 /* Looks for a parameter overlapping the textend <--> textpos space. */
2605 /* Returns True if there is a parameter in this space. */
2606 /*----------------------------------------------------------------------*/
2608 Boolean paramcross(objectptr tobj, labelptr tlab)
2610 stringpart *firstptr, *lastptr;
2611 int locpos;
2613 lastptr = findstringpart(areawin->textpos, &locpos, tlab->string,
2614 areawin->topinstance);
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;
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;
2628 return False;
2631 /*----------------------------------------------------------------------*/
2632 /* Check whether this page object was entered via a library page */
2633 /*----------------------------------------------------------------------*/
2635 int checklibtop()
2637 int i;
2638 pushlistptr thispush;
2640 for (thispush = areawin->stack; thispush != NULL; thispush = thispush->next)
2641 if ((i = is_library(thispush->thisinst->thisobject)) >= 0)
2642 return i;
2644 return -1;
2647 /*----------------------------------------------------------------------*/
2648 /* Remove all parameters from an object instance */
2649 /* (Reverts all parameters to default value) */
2650 /*----------------------------------------------------------------------*/
2652 void removeinstparams(objinstptr thisinst)
2654 oparamptr ops;
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);
2668 /*----------------------------------------------------------------------*/
2669 /* Remove all parameters from an object. */
2670 /*----------------------------------------------------------------------*/
2672 void removeparams(objectptr thisobj)
2674 oparamptr ops;
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);
2686 thisobj->params = NULL;
2689 /*-----------------------------------------------------------------------*/