1 /*-----------------------------------------------------------------------*/
2 /* text.c --- text processing routines for xcircuit */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-----------------------------------------------------------------------*/
8 #include <ctype.h> /* for isprint() and isdigit() */
11 #include <X11/Intrinsic.h>
12 #include <X11/StringDefs.h>
15 /*------------------------------------------------------------------------*/
17 /*------------------------------------------------------------------------*/
23 #include "colordefs.h"
26 /*----------------------------------------------------------------------*/
27 /* Function prototype declarations */
28 /*----------------------------------------------------------------------*/
29 #include "prototypes.h"
31 /*------------------------------------------------------------------------*/
32 /* External Variable definitions */
33 /*------------------------------------------------------------------------*/
36 extern XCWindowData
*areawin
;
37 extern Globaldata xobjs
;
38 extern short fontcount
;
39 extern fontinfo
*fonts
;
40 extern colorindex
*colorlist
;
41 extern char _STR
[150];
44 extern const char *utf8encodings
[][256];
46 /* Global value of distance between characters in the font catalog */
51 /*----------------------------------------------------------------------*/
52 /* Evaluation of expression types in strings (non-Tcl version---these */
53 /* are PostScript expressions). */
55 /* For now, expressions are just copied as-is, without evaluation. */
56 /* An allocated string is returned, and it is the responsibility of the */
57 /* calling routine to free it. */
58 /*----------------------------------------------------------------------*/
60 char *evaluate_expr(objectptr thisobj
, oparamptr ops
, objinstptr pinst
)
65 if (ops
->type
!= XC_EXPR
) return NULL
;
66 return strdup(ops
->parameter
.expr
);
71 /*----------------------------------------------------------------------*/
72 /* Determine if a label contains a parameter. */
73 /*----------------------------------------------------------------------*/
75 Boolean
hasparameter(labelptr curlabel
)
79 for (chrptr
= curlabel
->string
; chrptr
!= NULL
; chrptr
= chrptr
->nextpart
)
80 if (chrptr
->type
== PARAM_START
)
86 /*----------------------------------------------------------------------*/
87 /* Join selected labels together. */
88 /*----------------------------------------------------------------------*/
92 /* genericptr *genobj; (jdk) */
96 labelptr dest
, source
;
98 if (areawin
->selects
< 2) {
99 Wprintf("Not enough labels selected for joining");
103 SetForeground(dpy
, areawin
->gc
, BACKGROUND
);
105 for (jl
= areawin
->selectlist
; jl
< areawin
->selectlist
+
106 areawin
->selects
; jl
++) {
107 if (SELECTTYPE(jl
) == LABEL
) {
108 dest
= SELTOLABEL(jl
);
109 UDrawString(dest
, DOFORALL
, areawin
->topinstance
);
110 for (endpart
= dest
->string
; endpart
->nextpart
!= NULL
; endpart
=
116 for (++jl
; jl
< areawin
->selectlist
+ areawin
->selects
; jl
++) {
117 if (SELECTTYPE(jl
) == LABEL
) {
118 source
= SELTOLABEL(jl
);
119 UDrawString(source
, DOFORALL
, areawin
->topinstance
);
120 endpart
->nextpart
= source
->string
;
121 for (; endpart
->nextpart
!= NULL
; endpart
= endpart
->nextpart
);
124 reviseselect(areawin
->selectlist
, areawin
->selects
, jl
);
128 SetForeground(dpy
, areawin
->gc
, dest
->color
);
129 UDrawString(dest
, dest
->color
, areawin
->topinstance
);
131 incr_changes(topobject
);
135 /*----------------------------------------------------------------------*/
136 /* Insert a new segment into a string */
137 /*----------------------------------------------------------------------*/
139 stringpart
*makesegment(stringpart
**strhead
, stringpart
*before
)
141 stringpart
*newptr
, *lastptr
, *nextptr
;
143 newptr
= (stringpart
*)malloc(sizeof(stringpart
));
144 newptr
->data
.string
= NULL
;
146 if (before
== *strhead
) { /* insert at beginning */
147 newptr
->nextpart
= *strhead
;
150 else { /* otherwise */
151 for(lastptr
= *strhead
; lastptr
!= NULL
;) {
152 nextptr
= nextstringpart(lastptr
, areawin
->topinstance
);
153 if (nextptr
== before
) {
154 if (lastptr
->type
== PARAM_START
) {
155 oparamptr obs
= NULL
;
156 char *key
= lastptr
->data
.string
;
157 obs
= find_param(areawin
->topinstance
, key
);
159 Wprintf("Error: Bad parameter \"%s\"!", key
);
161 obs
->parameter
.string
= newptr
; /* ?? */
165 lastptr
->nextpart
= newptr
;
167 newptr
->nextpart
= nextptr
;
170 else if (lastptr
->nextpart
== before
&& lastptr
->type
== PARAM_START
) {
171 lastptr
->nextpart
= newptr
;
172 newptr
->nextpart
= before
;
181 /*----------------------------------------------------------------------*/
182 /* Split a string across text segments */
183 /*----------------------------------------------------------------------*/
185 stringpart
*splitstring(int tpos
, stringpart
**strtop
, objinstptr localinst
)
188 stringpart
*newpart
, *ipart
;
190 ipart
= findstringpart(tpos
, &locpos
, *strtop
, localinst
);
191 if (locpos
> 0) { /* split the string */
192 newpart
= makesegment(strtop
, ipart
);
193 newpart
->type
= TEXT_STRING
;
194 newpart
->data
.string
= ipart
->data
.string
;
195 slen
= strlen(newpart
->data
.string
) - locpos
;
196 ipart
->data
.string
= (u_char
*)malloc(slen
+ 1);
197 strncpy(ipart
->data
.string
, newpart
->data
.string
+ locpos
, slen
+ 1);
198 *(newpart
->data
.string
+ locpos
) = '\0';
200 else newpart
= ipart
;
205 /*----------------------------------------------------------------------*/
206 /* Get the next string part, linking to a parameter if necessary */
207 /*----------------------------------------------------------------------*/
209 stringpart
*nextstringpartrecompute(stringpart
*strptr
, objinstptr thisinst
)
211 stringpart
*nextptr
= strptr
->nextpart
;
213 if (strptr
->type
== PARAM_START
)
214 nextptr
= linkstring(thisinst
, strptr
, TRUE
);
215 else if (strptr
->type
== PARAM_END
) {
216 strptr
->nextpart
= NULL
;
218 /* Parameters that have a non-NULL entry in data have */
219 /* been promoted from an expression or numerical value */
220 /* to a string. The memory allocated for this string */
221 /* should be free'd. */
223 if (strptr
->data
.string
!= (u_char
*)NULL
) {
224 fprintf(stderr
, "Non-NULL data in PARAM_END segment\n");
225 free(strptr
->data
.string
);
226 strptr
->data
.string
= (u_char
*)NULL
;
232 /*----------------------------------------------------------------------*/
233 /* Same as the above routine, but don't recompute expression parameters */
234 /* when encountered. Use the previously generated result. */
235 /*----------------------------------------------------------------------*/
237 stringpart
*nextstringpart(stringpart
*strptr
, objinstptr thisinst
)
239 stringpart
*nextptr
= strptr
->nextpart
;
241 if (strptr
->type
== PARAM_START
)
242 nextptr
= linkstring(thisinst
, strptr
, FALSE
);
243 else if (strptr
->type
== PARAM_END
) {
244 strptr
->nextpart
= NULL
;
245 if (strptr
->data
.string
!= (u_char
*)NULL
) {
246 fprintf(stderr
, "Non-NULL data in PARAM_END segment\n");
247 free(strptr
->data
.string
);
248 strptr
->data
.string
= (u_char
*)NULL
;
254 /*----------------------------------------------------------------------*/
255 /* Remove a string part from the string */
256 /*----------------------------------------------------------------------*/
258 stringpart
*deletestring0(stringpart
*dstr
, stringpart
**strtop
, objinstptr thisinst
,
261 stringpart
*strptr
= NULL
, *nextptr
;
266 *strtop
= dstr
->nextpart
;
269 while (strptr
!= NULL
) {
270 nextptr
= nextstringpart(strptr
, thisinst
);
271 if (nextptr
== dstr
) break;
277 /* If this is the begining of a parameter, then we have to figure */
278 /* out if it's an instance or a default, and change the pointer */
279 /* to the parameter in the parameter list, accordingly. */
281 else if ((strptr
->type
== PARAM_START
) && (thisinst
!= NULL
)) {
282 key
= strptr
->data
.string
;
283 ops
= find_param(thisinst
, key
);
285 Fprintf(stderr
, "Error in deletestring: Bad parameter %s found\n", key
);
290 ops
->parameter
.string
= dstr
->nextpart
;
293 /* Deleting an expression result can only result */
294 /* in bad things happening. */
297 /* What to be done here? */
302 /* If this is the end of a parameter, we have to link the */
303 /* PARAM_START, not the PARAM_END, which has already been nulled. */
304 else if (strptr
->type
== PARAM_END
) {
305 for (strptr
= *strtop
; strptr
!= NULL
; strptr
= strptr
->nextpart
) {
306 if (strptr
->nextpart
== dstr
) {
307 strptr
->nextpart
= dstr
->nextpart
;
313 strptr
->nextpart
= dstr
->nextpart
;
315 if (dstr
->type
== TEXT_STRING
)
316 free(dstr
->data
.string
);
319 /* attempt to merge, if legal, and requested */
320 if (strptr
&& domerge
)
326 /*----------------------------------------------------------------------*/
327 /* deletestring() is a wrapper for deletestring0() */
328 /*----------------------------------------------------------------------*/
330 stringpart
*deletestring(stringpart
*dstr
, stringpart
**strtop
, objinstptr thisinst
)
332 return deletestring0(dstr
, strtop
, thisinst
, TRUE
);
335 /*----------------------------------------------------------------------*/
336 /* Merge string parts at boundary, if parts can be legally merged */
337 /* If the indicated string part is text and the part following the */
338 /* indicated string part is also text, merge the two. The indicated */
339 /* string part is returned, and the following part is freed. */
341 /* (Fixes thanks to Petter Larsson 11/17/03) */
342 /*----------------------------------------------------------------------*/
344 stringpart
*mergestring(stringpart
*firststr
)
346 stringpart
*nextstr
= NULL
;
348 if (firststr
) nextstr
= firststr
->nextpart
;
349 if (nextstr
!= NULL
) {
350 if (firststr
->type
== TEXT_STRING
&& nextstr
->type
== TEXT_STRING
) {
351 firststr
->nextpart
= nextstr
->nextpart
;
352 firststr
->data
.string
= (char *)realloc(firststr
->data
.string
,
353 1 + strlen(firststr
->data
.string
) + strlen(nextstr
->data
.string
));
354 strcat(firststr
->data
.string
, nextstr
->data
.string
);
355 free(nextstr
->data
.string
);
362 /*----------------------------------------------------------------------*/
363 /* Link a parameter to a string */
364 /* If compute_exprs is TRUE, then we should recompute any expression */
365 /* parameters encountered. If FALSE, then we assume that all */
366 /* expressions have been computed previously, and may use the recorded */
367 /* instance value. */
369 /* 11/20/06---changed to allow two different static strings to save */
370 /* promoted results. This is necessary because we may be comparing */
371 /* two promoted results in, e.g., stringcomprelaxed(), and we don't */
372 /* want to overwrite the first result with the second. */
373 /*----------------------------------------------------------------------*/
375 stringpart
*linkstring(objinstptr localinst
, stringpart
*strstart
,
376 Boolean compute_exprs
)
379 stringpart
*tmpptr
, *nextptr
= NULL
;
380 static stringpart
*promote
[2] = {NULL
, NULL
};
381 static unsigned char pidx
= 0;
384 if (strstart
->type
!= PARAM_START
) return NULL
;
386 key
= strstart
->data
.string
;
388 /* In case of no calling instance, always get the default from the */
389 /* current page object. */
391 if (localinst
== NULL
) {
392 ops
= match_param(topobject
, key
);
397 ops
= find_param(localinst
, key
);
399 /* We get here in cases where the object definition is being read, */
400 /* and there is no instance of the object to link to. In that */
401 /* case, we ignore parameters and move on to the next part. */
402 return strstart
->nextpart
;
406 if (ops
->type
!= XC_STRING
) {
408 if (promote
[pidx
] == NULL
) {
409 /* Generate static string for promoting numerical parameters */
410 tmpptr
= makesegment(&promote
[pidx
], NULL
);
411 tmpptr
->type
= TEXT_STRING
;
412 tmpptr
= makesegment(&promote
[pidx
], NULL
);
413 tmpptr
->type
= PARAM_END
;
416 if (promote
[pidx
]->data
.string
!= NULL
) {
417 free(promote
[pidx
]->data
.string
);
418 promote
[pidx
]->data
.string
= NULL
;
422 /* Promote numerical type to string */
423 if (ops
->type
== XC_INT
) {
424 promote
[pidx
]->data
.string
= (char *)malloc(13);
425 sprintf(promote
[pidx
]->data
.string
, "%12d", ops
->parameter
.ivalue
);
426 nextptr
= promote
[pidx
++];
428 else if (ops
->type
== XC_FLOAT
) {
429 promote
[pidx
]->data
.string
= (char *)malloc(13);
430 sprintf(promote
[pidx
]->data
.string
, "%g", (double)(ops
->parameter
.fvalue
));
431 nextptr
= promote
[pidx
++];
433 else { /* ops->type == XC_EXPR */
435 if (!compute_exprs
&& (ips
= match_instance_param(localinst
, key
))
436 != NULL
&& (ips
->type
== XC_STRING
)) {
437 nextptr
= ips
->parameter
.string
;
438 promote
[pidx
]->data
.string
= NULL
;
441 promote
[pidx
]->data
.string
= evaluate_expr(((localinst
== NULL
) ?
442 topobject
: localinst
->thisobject
), ops
, localinst
);
443 if (promote
[pidx
]->data
.string
!= NULL
)
444 nextptr
= promote
[pidx
++];
449 pidx
&= 0x1; /* pidx toggles between 0 and 1 */
452 nextptr
= ops
->parameter
.string
;
454 /* If the parameter exists, link the end of the parameter back to */
455 /* the calling string. */
457 if (nextptr
!= NULL
) {
459 while (tmpptr
->type
!= PARAM_END
)
460 if ((tmpptr
= tmpptr
->nextpart
) == NULL
)
462 tmpptr
->nextpart
= strstart
->nextpart
;
468 /*----------------------------------------------------------------------*/
469 /* Find the last font used prior to the indicated text position */
470 /*----------------------------------------------------------------------*/
472 int findcurfont(int tpos
, stringpart
*strtop
, objinstptr thisinst
)
478 curpos
= findstringpart(tpos
, NULL
, strtop
, thisinst
);
479 for (strptr
= strtop
; (strptr
!= NULL
) && (strptr
!= curpos
);
480 strptr
= nextstringpart(strptr
, thisinst
))
481 if (strptr
->type
== FONT_NAME
)
482 cfont
= strptr
->data
.font
;
487 /*----------------------------------------------------------------------*/
488 /* Return a local position and stringpart for the first occurrence of */
489 /* "substring" in the the indicated xcircuit string. If non-NULL, */
490 /* "locpos" is set to the position of the substring in the stringpart, */
491 /* or -1 if the text was not found. Text cannot cross stringpart */
492 /* boundaries (although this should be allowed). */
493 /*----------------------------------------------------------------------*/
495 stringpart
*findtextinstring(char *search
, int *locpos
, stringpart
*strtop
,
496 objinstptr localinst
)
498 stringpart
*strptr
= strtop
;
501 for (strptr
= strtop
; strptr
!= NULL
; strptr
= nextstringpart(strptr
, localinst
)) {
502 if ((strptr
->type
== TEXT_STRING
) && strptr
->data
.string
) {
503 strstart
= strstr(strptr
->data
.string
, search
);
504 if (strstart
!= NULL
) {
506 *locpos
= (int)(strstart
- (char *)strptr
->data
.string
);
511 if (locpos
!= NULL
) *locpos
= -1;
515 /*----------------------------------------------------------------------*/
516 /* Return a local position and stringpart for "tpos" positions into */
517 /* the indicated string. Position and stringpart are for the character */
518 /* or command immediately preceding "tpos" */
519 /*----------------------------------------------------------------------*/
521 stringpart
*findstringpart(int tpos
, int *locpos
, stringpart
*strtop
,
522 objinstptr localinst
)
524 stringpart
*strptr
= strtop
;
525 int testpos
= 0, tmplen
;
527 for (strptr
= strtop
; strptr
!= NULL
; strptr
= nextstringpart(strptr
, localinst
)) {
528 if ((strptr
->type
== TEXT_STRING
) && strptr
->data
.string
) {
529 tmplen
= strlen(strptr
->data
.string
);
530 if (testpos
+ tmplen
> tpos
) {
531 if (locpos
!= NULL
) *locpos
= (tpos
- testpos
);
534 else testpos
+= tmplen
- 1;
536 if (locpos
!= NULL
) *locpos
= -1;
537 if (testpos
>= tpos
) return strptr
;
544 /*----------------------------------------------------------------------*/
545 /* The following must be in an order matching the "Text string part */
546 /* types" defined in xcircuit.h. */
547 /*----------------------------------------------------------------------*/
549 static char *nonprint
[] = {
550 "Text", "Subscript", "Superscript", "Normalscript",
551 "Underline", "Overline", "Noline",
552 "Tab_Stop", "Tab_Forward", "Tab_Backward",
553 "Halfspace", "Quarterspace", "<Return>",
554 "Font", "Scale", "Color", "Margin_Stop", "Kern",
555 "Parameter", ">", "Net_Name", "Error", NULL
}; /* (jdk) */
557 /* Handling of certain text escapes (subscript, superscript, underline, */
558 /* and overline) in TeX (added by Fabian Inostroza) */
560 static char *nonprinttex
[] = {
562 "\\underline{", "\\overline{", "}",
563 "Tab_Stop", "Tab_Forward", "Tab_Backward",
564 "Halfspace", "Quarterspace", "<Return>",
565 "Font", "Scale", "Color", "Margin_Stop", "Kern",
566 "Parameter", ">", "Net_Name", "Error", NULL
};
568 /*----------------------------------------------------------------------*/
570 /* Write a printable version of the character or command at the */
571 /* indicated string part and position. */
572 /*----------------------------------------------------------------------*/
574 void charprint(char *sout
, stringpart
*strptr
, int locpos
)
578 switch (strptr
->type
) {
580 if (strptr
->data
.string
) {
581 if (locpos
> (int) strlen(strptr
->data
.string
)) {
582 strcpy(sout
, "<ERROR>");
584 else sc
= *(strptr
->data
.string
+ locpos
);
586 sprintf(sout
, "%c", sc
);
588 sprintf(sout
, "/%03o", (u_char
)sc
);
594 sprintf(sout
, "Font=%s", (strptr
->data
.font
>= fontcount
) ?
595 "(unknown)" : fonts
[strptr
->data
.font
].psname
);
598 sprintf(sout
, "Scale=%3.2f", strptr
->data
.scale
);
601 sprintf(sout
, "Kern=(%d,%d)", strptr
->data
.kern
[0], strptr
->data
.kern
[1]);
604 sprintf(sout
, "Parameter(%s)<", strptr
->data
.string
);
607 strcpy(sout
, nonprint
[strptr
->type
]);
612 /*----------------------------------------------------------------------*/
613 /* Version of the above, for printing LaTeX strings */
614 /* added by Fabian Inostroza 7/14/2013 */
615 /*----------------------------------------------------------------------*/
617 void charprinttex(char *sout
, stringpart
*strptr
, int locpos
)
621 switch (strptr
->type
) {
623 if (strptr
->data
.string
) {
624 if (locpos
> (int) strlen(strptr
->data
.string
)) {
625 strcpy(sout
, "<ERROR>");
627 else sc
= *(strptr
->data
.string
+ locpos
);
629 sprintf(sout
, "%c", sc
);
631 sprintf(sout
, "/%03o", (u_char
)sc
);
643 /*----------------------------------------------------------------------*/
644 /* Print a string (allocates memory for the string; must be freed by */
645 /* the calling routine). */
646 /*----------------------------------------------------------------------*/
648 char *xcstringtostring(stringpart
*strtop
, objinstptr localinst
, Boolean textonly
)
654 sout
= (char *)malloc(1);
657 while ((strptr
= findstringpart(pos
++, &locpos
, strtop
, localinst
)) != NULL
) {
658 if (!textonly
|| strptr
->type
== TEXT_STRING
) {
659 charprint(_STR
, strptr
, locpos
);
660 sout
= (char *)realloc(sout
, strlen(sout
) + strlen(_STR
) + 1);
663 /* Overbar on schematic names is translated to logical-NOT ("!") */
664 else if (textonly
&& strptr
->type
== OVERLINE
) {
665 sout
= (char *)realloc(sout
, strlen(sout
) + 2);
672 /*----------------------------------------------------------------------*/
673 /* Version of the above, for printing LaTeX strings */
674 /* added by Fabian Inostroza 7/14/2013 */
675 /*----------------------------------------------------------------------*/
677 char *textprinttex(stringpart
*strtop
, objinstptr localinst
)
683 sout
= (char *)malloc(1);
686 while ((strptr
= findstringpart(pos
++, &locpos
, strtop
, localinst
)) != NULL
) {
687 charprinttex(_STR
, strptr
, locpos
);
688 sout
= (char *)realloc(sout
, strlen(sout
) + strlen(_STR
) + 1);
694 /*----------------------------------------------------------------------*/
695 /* Wrappers for xcstringtostring(): */
696 /* stringprint() includes information on text controls appropriate */
697 /* for printing in the message window (charreport()) */
698 /*----------------------------------------------------------------------*/
700 char *stringprint(stringpart
*strtop
, objinstptr localinst
)
702 return xcstringtostring(strtop
, localinst
, False
);
705 /*----------------------------------------------------------------------*/
706 /* textprint() excludes text controls, resulting in a string */
707 /* appropriate for netlist information, or for promoting a string */
708 /* parameter to a numeric type. */
709 /*----------------------------------------------------------------------*/
711 char *textprint(stringpart
*strtop
, objinstptr localinst
)
713 return xcstringtostring(strtop
, localinst
, True
);
716 /*----------------------------------------------------------------------*/
717 /* textprintsubnet() is like textprint(), except that strings in bus */
718 /* notation are reduced to just the single subnet. */
719 /*----------------------------------------------------------------------*/
721 char *textprintsubnet(stringpart
*strtop
, objinstptr localinst
, int subnet
)
723 char *newstr
, *busptr
, *endptr
, *substr
;
725 newstr
= xcstringtostring(strtop
, localinst
, True
);
727 busptr
= strchr(newstr
, areawin
->buschar
);
728 if (busptr
!= NULL
) {
729 endptr
= find_delimiter(busptr
);
730 if (endptr
!= NULL
) {
731 if (busptr
== newstr
)
732 sprintf(newstr
, "%d", subnet
);
734 substr
= strdup(newstr
);
736 sprintf(substr
+ (int)(busptr
- newstr
), "%d%s", subnet
, endptr
);
743 /* Promote a non-bus label to a bus label */
744 substr
= malloc(10 + strlen(newstr
));
745 strcpy(substr
, newstr
);
747 while ((*endptr
) != '\0') endptr
++;
748 sprintf(endptr
, "%c%d%c", areawin
->buschar
, subnet
,
749 standard_delimiter_end(areawin
->buschar
));
757 /*----------------------------------------------------------------------*/
758 /* Another variant: Print a subnet list according to the entries in */
759 /* a netlist (Genericlist *) pointer. This includes the condition in */
760 /* which the list is a single wire, not a bus. If "pinstring" is non- */
761 /* NULL, it will be used as the name of the subnet. Otherwise, */
762 /* "prefix" will be used, and will be appended with the net ID of the */
763 /* first net in the sublist to make the identifier unique. */
764 /*----------------------------------------------------------------------*/
766 char *textprintnet(char *prefix
, char *pinstring
, Genericlist
*sublist
)
773 if (sublist
->subnets
== 0) {
774 newstr
= (char *)malloc(strlen(prefix
) + 10);
775 sprintf(newstr
, "%s%d", prefix
, sublist
->net
.id
);
777 else { /* just make a comma-separated list */
778 newstr
= (char *)malloc(strlen(prefix
) + 20 + 3 * sublist
->subnets
);
779 sbus
= sublist
->net
.list
;
780 sprintf(newstr
, "%s%d%c", prefix
, sbus
->netid
, areawin
->buschar
);
781 for (i
= 0; i
< sublist
->subnets
; i
++) {
782 sbus
= sublist
->net
.list
+ i
;
783 sptr
= newstr
+ strlen(newstr
);
786 sprintf(sptr
, "%d", sbus
->subnetid
);
788 sptr
= newstr
+ strlen(newstr
);
789 sprintf(sptr
, "%c", standard_delimiter_end(areawin
->buschar
));
794 /*----------------------------------------------------------------------*/
795 /* Test equivalence of the text string parts of a label with the */
796 /* indicated char * string. */
798 /* Return 0 if the strings match. Return 1 or the result of strcmp() */
799 /* on the first non-matching substring text segment if the strings */
802 /* If "exact" is True, requires an exact match, otherwise requires */
803 /* that the label only match text to the length of text. */
804 /*----------------------------------------------------------------------*/
806 int textcompx(stringpart
*string
, char *text
, Boolean exact
, objinstptr localinst
)
812 size_t llen
= strlen(text
), slen
;
813 Boolean has_text
= FALSE
;
815 for (strptr
= string
; strptr
!= NULL
; strptr
= nextstringpart(strptr
, localinst
)) {
816 if (strptr
->type
== TEXT_STRING
) {
818 sptr
= strptr
->data
.string
;
819 slen
= min(strlen(sptr
), llen
);
821 if (!exact
&& (rval
= strncmp(sptr
, tptr
, slen
)))
823 else if (exact
&& (rval
= strcmp(sptr
, tptr
)))
825 else if (!exact
&& (llen
== 0))
832 /* Check condition that no text was encountered in the xcircuit string */
833 return ((llen
> 0) && !has_text
) ? 1 : 0;
836 /*----------------------------------------------------------------------*/
837 /* Wrappers for textcompx(), equivalent to strcmp() and strncmp(). */
838 /*----------------------------------------------------------------------*/
840 int textcomp(stringpart
*string
, char *text
, objinstptr localinst
)
842 return textcompx(string
, text
, True
, localinst
);
845 /*----------------------------------------------------------------------*/
847 int textncomp(stringpart
*string
, char *text
, objinstptr localinst
)
849 return textcompx(string
, text
, False
, localinst
);
852 /*----------------------------------------------------------------------*/
853 /* Test equivalence of two label strings */
854 /*----------------------------------------------------------------------*/
856 int stringcomp(stringpart
*string1
, stringpart
*string2
)
858 stringpart
*strptr1
, *strptr2
;
860 for (strptr1
= string1
, strptr2
= string2
; strptr1
!= NULL
&& strptr2
!= NULL
;
861 strptr1
= strptr1
->nextpart
, strptr2
= strptr2
->nextpart
) {
862 if (strptr1
->type
!= strptr2
->type
)
865 switch (strptr1
->type
) {
867 if (strptr1
->data
.string
&& strptr2
->data
.string
) {
868 if (strcmp(strptr1
->data
.string
, strptr2
->data
.string
))
871 else if (strptr1
->data
.string
|| strptr2
->data
.string
)
875 if (strptr1
->data
.scale
!= strptr2
->data
.scale
) return 1;
878 if (strptr1
->data
.color
!= strptr2
->data
.color
) return 1;
881 if (strptr1
->data
.font
!= strptr2
->data
.font
) return 1;
884 if (strptr1
->data
.kern
[0] != strptr2
->data
.kern
[0] ||
885 strptr1
->data
.kern
[1] != strptr2
->data
.kern
[1]) return 1;
891 /* One string continues after the other ends. . . */
892 if (strptr1
!= NULL
|| strptr2
!= NULL
) return 1;
896 /*----------------------------------------------------------------------*/
897 /* Test if the specified font is in the "Symbol" font family. */
898 /*----------------------------------------------------------------------*/
900 Boolean
issymbolfont(int fontnumber
)
902 if (!strcmp(fonts
[fontnumber
].family
, "Symbol")) return True
;
906 /*----------------------------------------------------------------------*/
907 /* Test if the specified font is in the "Helvetica" font family. */
908 /* SVG uses this to make sure it scales down the font by 7/8 to match */
909 /* our internal vectors, and to use "oblique" for the style instead of */
911 /*----------------------------------------------------------------------*/
913 Boolean
issansfont(int fontnumber
)
915 if (!strcmp(fonts
[fontnumber
].family
, "Helvetica")) return True
;
919 /*----------------------------------------------------------------------*/
920 /* Test if the specified font is ISO-Latin1 encoding */
921 /*----------------------------------------------------------------------*/
923 Boolean
isisolatin1(int fontnumber
)
925 if ((fonts
[fontnumber
].flags
& 0xf80) == 0x100) return True
;
929 /*----------------------------------------------------------------------*/
930 /* For a label representing a single bus subnet, return the index of */
932 /*----------------------------------------------------------------------*/
934 int sub_bus_idx(labelptr thislab
, objinstptr thisinst
)
940 for (strptr
= thislab
->string
; strptr
!= NULL
; strptr
=
941 nextstringpart(strptr
, thisinst
)) {
942 if (strptr
->type
== TEXT_STRING
) {
943 if ((busptr
= strchr(strptr
->data
.string
, areawin
->buschar
)) != NULL
) {
944 if (sscanf(++busptr
, "%d", &busidx
) == 1)
947 if (sscanf(strptr
->data
.string
, "%d", &busidx
) == 1)
954 /*----------------------------------------------------------------------*/
955 /* The following routine is like sub_bus_idx but returns TRUE or FALSE */
956 /* depending on whether the label was determined to be in bus notation */
957 /* or not. Note that sub_bux_idx may be run on sub-bus names (those */
958 /* that are internally generated from labels in bus notation), but */
959 /* pin_is_bus should not, because pin numbers in bus notation get the */
960 /* bus delimiters stripped from them. */
961 /*----------------------------------------------------------------------*/
963 Boolean
pin_is_bus(labelptr thislab
, objinstptr thisinst
)
967 /* int busidx; (jdk) */
968 Boolean found_delimiter
= FALSE
;
970 for (strptr
= thislab
->string
; strptr
!= NULL
; strptr
=
971 nextstringpart(strptr
, thisinst
)) {
972 if (strptr
->type
== TEXT_STRING
) {
973 if ((busptr
= strchr(strptr
->data
.string
, areawin
->buschar
)) != NULL
) {
974 if (isdigit(*(++busptr
)))
977 found_delimiter
= TRUE
;
979 else if (found_delimiter
== TRUE
) {
980 return (isdigit(*(strptr
->data
.string
))) ? TRUE
: FALSE
;
987 /*----------------------------------------------------------------------*/
988 /* When encountering a label with bus notation, create a list of */
989 /* subnets belonging to the bus. Return the list as a pointer to a */
990 /* Genericlist structure. This structure is statically allocated and */
991 /* is expected to have its contents copied into the target netlist */
994 /* Unlike the above routine, this routine prints the original string */
995 /* into a char* array using textprint() so that escape sequences are */
996 /* removed and will not affect the result. */
998 /* To speed things up, the calling routine should have already called */
999 /* pin_is_bus() to determine if the label does indeed represent a bus. */
1000 /* break_up_bus() is much slower in determining this. If break_up_bus */
1001 /* is passed a string that cannot be identified as a bus, then it */
1002 /* returns a NULL pointer. */
1004 /* If netlist points to a structure with no subnets, then its net ID */
1005 /* is the starting point for the nets returned by break_up_bus. */
1006 /* Otherwise, netlist is assumed to be a valid netlist for a bus that */
1007 /* matches "blab", and we will use net IDs from this list. */
1008 /*----------------------------------------------------------------------*/
1010 Genericlist
*break_up_bus(labelptr blab
, objinstptr thisinst
, Genericlist
*netlist
)
1012 static Genericlist
*subnets
= NULL
;
1014 buslist
*sbus
, *jbus
;
1015 int istart
, iend
, i
, j
, netstart
, matched
;
1016 char *buspos
, *busend
, *busptr
;
1018 if (pin_is_bus(blab
, thisinst
) == FALSE
) return NULL
;
1019 if (subnets
== NULL
) {
1020 /* This happens on the first pass only */
1021 subnets
= (Genericlist
*)malloc(sizeof(Genericlist
));
1022 subnets
->net
.list
= (buslist
*)malloc(sizeof(buslist
));
1024 subnets
->subnets
= 0;
1026 tptr
= textprint(blab
->string
, thisinst
);
1027 buspos
= strchr(tptr
, areawin
->buschar
);
1029 /* The notation "(...)" with NO TEXT preceding the opening bus */
1030 /* delimiter, is assumed to represent a numerical pin range. So */
1031 /* instead of generating labels, e.g., "(1)", "(2)", etc., we */
1032 /* generate labels "1", "2", etc. */
1034 if (buspos
== NULL
) {
1035 Fprintf(stderr
, "Error: Bus specification has no start delimiter!\n");
1039 netstart
= (netlist
->subnets
== 0) ? netlist
->net
.id
: 0;
1041 busend
= find_delimiter(buspos
);
1043 if (busend
== NULL
) {
1044 Fprintf(stderr
, "Error: Bus specification has no end delimiter!\n");
1048 /* Find the range of each bus */
1052 for (busptr
= buspos
+ 1; busptr
< busend
; busptr
++) {
1053 if (sscanf(busptr
, "%d", &iend
) == 0) break;
1054 while ((*busptr
!= ':') && (*busptr
!= '-') && (*busptr
!= ',')
1055 && (*busptr
!= *busend
))
1057 if ((*busptr
== ':') || (*busptr
== '-')) /* numerical range */
1060 if (istart
< 0) istart
= iend
;
1064 /* Create a new list entry for this subnet number */
1067 subnets
->net
.list
= (buslist
*)realloc(subnets
->net
.list
,
1068 subnets
->subnets
* sizeof(buslist
));
1070 sbus
= subnets
->net
.list
+ subnets
->subnets
- 1;
1073 sbus
->netid
= netstart
++;
1077 /* Net ID is the net ID for the matching subnet of netlist */
1078 for (j
= 0; j
< netlist
->subnets
; j
++) {
1079 jbus
= netlist
->net
.list
+ j
;
1080 if (jbus
->subnetid
== i
) {
1082 sbus
->netid
= jbus
->netid
;
1086 /* Insert a net ID of zero if it can't be found */
1087 if (j
== netlist
->subnets
) {
1092 if (i
== iend
) break;
1093 else if (istart
> iend
) i
--;
1102 return (matched
== 0) ? NULL
: subnets
;
1105 /*----------------------------------------------------------------------*/
1106 /* Test equivalence of two label strings (relaxed constraints) */
1107 /* Like stringcomp(), but ignores "superficial" differences such as */
1108 /* color and font (unless one of the fonts is Symbol), scale, */
1109 /* underlining, tabbing, and kerning. */
1111 /* Return 0 if matching. Return 1 if not matching. */
1113 /* For bus notation, ignore everything inside the bus delimiters. */
1114 /* The calling routine must determine if the labels being compared */
1115 /* have matching subnet ranges. Note that as written, this does not */
1116 /* check any text occurring after the opening bus delimiter! Thus, */
1117 /* "mynet(1:2)" and "mynet(3:4)" and "mynet(1)_alt" are all considered */
1118 /* exact matches in bus notation. */
1119 /*----------------------------------------------------------------------*/
1121 int stringcomprelaxed(stringpart
*string1
, stringpart
*string2
,
1122 objinstptr thisinst
)
1124 stringpart
*strptr1
= string1
, *strptr2
= string2
;
1125 Boolean font1
= False
, font2
= False
, inbus_match
= TRUE
;
1130 if (strptr1
->type
== FONT_NAME
)
1131 font1
= issymbolfont(strptr1
->data
.font
);
1132 if (strptr2
->type
== FONT_NAME
)
1133 font2
= issymbolfont(strptr2
->data
.font
);
1135 while ((strptr1
!= NULL
) || (strptr2
!= NULL
)) {
1136 while (strptr1
!= NULL
&& strptr1
->type
!= TEXT_STRING
&&
1137 strptr1
->type
!= OVERLINE
) {
1138 if (strptr1
->type
== FONT_NAME
)
1139 font1
= issymbolfont(strptr1
->data
.font
);
1140 strptr1
= nextstringpart(strptr1
, thisinst
);
1142 while (strptr2
!= NULL
&& strptr2
->type
!= TEXT_STRING
&&
1143 strptr2
->type
!= OVERLINE
) {
1144 if (strptr2
->type
== FONT_NAME
)
1145 font2
= issymbolfont(strptr2
->data
.font
);
1146 strptr2
= nextstringpart(strptr2
, thisinst
);
1148 if (strptr1
== NULL
|| strptr2
== NULL
) break;
1149 if (font1
!= font2
) return 1;
1150 if (strptr1
->type
!= strptr2
->type
) return 1;
1152 switch (strptr1
->type
) {
1155 char matchchar
= areawin
->buschar
;
1156 switch (areawin
->buschar
) {
1157 case '(': matchchar
= ')'; break;
1158 case '[': matchchar
= ']'; break;
1159 case '{': matchchar
= '}'; break;
1160 case '<': matchchar
= '>'; break;
1162 buspos
= strchr(strptr1
->data
.string
, matchchar
);
1163 if (buspos
!= NULL
) {
1164 bpos
= (int)(buspos
- (char *)(strptr1
->data
.string
));
1165 if ((int) strlen(strptr2
->data
.string
) > bpos
) {
1166 if (!strcmp(strptr1
->data
.string
+ bpos
,
1167 strptr2
->data
.string
+ bpos
)) {
1174 if (inbus_match
== TRUE
)
1175 if (strcmp(strptr1
->data
.string
, strptr2
->data
.string
))
1176 inbus_match
= FALSE
;
1178 else if (!strcmp(strptr1
->data
.string
, strptr2
->data
.string
))
1181 /* To be a matching bus, the strings match everywhere */
1182 /* except between the bus notation delimiters (default */
1185 buspos
= strchr(strptr1
->data
.string
, areawin
->buschar
);
1186 if (buspos
!= NULL
) {
1188 bpos
= (int)(buspos
- (char *)(strptr1
->data
.string
)) + 1;
1189 if (!strncmp(strptr1
->data
.string
, strptr2
->data
.string
, bpos
)) {
1194 return 1; /* strings did not match, exactly or as bus notation */
1197 if (strptr1
->type
!= strptr2
->type
) return 1;
1200 strptr1
= nextstringpart(strptr1
, thisinst
);
1201 strptr2
= nextstringpart(strptr2
, thisinst
);
1205 /* One string continues after the other ends. . . */
1206 if (strptr1
!= NULL
|| strptr2
!= NULL
) return 1;
1208 /* Treat no closing bus delimiter as a non-bus string */
1209 else if ((in_bus
== 1) && (inbus_match
== FALSE
)) return 1;
1213 /*----------------------------------------------------------------------*/
1214 /* Find the number of parts in a string (excluding parameter contents) */
1215 /*----------------------------------------------------------------------*/
1217 int stringparts(stringpart
*string
)
1222 for (strptr
= string
; strptr
!= NULL
; strptr
= strptr
->nextpart
)
1228 /*----------------------------------------------------------------------*/
1229 /* Compute the total character length of a string */
1230 /* If "doparam" is True, include parameter contents. */
1231 /*----------------------------------------------------------------------*/
1233 int stringlength(stringpart
*string
, Boolean doparam
, objinstptr thisinst
)
1238 for (strptr
= string
; strptr
!= NULL
; strptr
= (doparam
) ?
1239 nextstringpart(strptr
, thisinst
) : strptr
->nextpart
) {
1240 if (strptr
->type
== TEXT_STRING
) {
1241 if (strptr
->data
.string
)
1242 ctotal
+= strlen(strptr
->data
.string
);
1251 /*----------------------------------------------------------------------*/
1252 /* Copy the contents of a string (excluding parameter contents) */
1253 /*----------------------------------------------------------------------*/
1255 stringpart
*stringcopy(stringpart
*string
)
1257 stringpart
*strptr
, *newpart
, *newtop
= NULL
, *topptr
;
1259 for (strptr
= string
; strptr
!= NULL
; strptr
= strptr
->nextpart
) {
1261 /* Don't use makesegment(), which looks at parameter contents */
1262 newpart
= (stringpart
*)malloc(sizeof(stringpart
));
1263 newpart
->nextpart
= NULL
;
1267 topptr
->nextpart
= newpart
;
1270 newpart
->type
= strptr
->type
;
1271 if ((strptr
->type
== TEXT_STRING
) || (strptr
->type
== PARAM_START
)) {
1272 newpart
->data
.string
= (char *)malloc(1 + strlen(strptr
->data
.string
));
1273 strcpy(newpart
->data
.string
, strptr
->data
.string
);
1276 newpart
->data
= strptr
->data
;
1281 /*----------------------------------------------------------------------*/
1282 /* Copy the contents of a string, embedding parameter contents */
1283 /*----------------------------------------------------------------------*/
1285 stringpart
*stringcopyall(stringpart
*string
, objinstptr thisinst
)
1287 stringpart
*strptr
, *newpart
, *newtop
, *topend
;
1289 for (strptr
= string
; strptr
!= NULL
;
1290 strptr
= nextstringpart(strptr
, thisinst
)) {
1291 newpart
= (stringpart
*)malloc(sizeof(stringpart
));
1292 newpart
->type
= strptr
->type
;
1293 newpart
->nextpart
= NULL
;
1294 if (strptr
== string
) newtop
= newpart
;
1295 else topend
->nextpart
= newpart
;
1297 if ((strptr
->type
== TEXT_STRING
|| strptr
->type
== PARAM_START
)
1298 && strptr
->data
.string
) {
1299 newpart
->data
.string
= (char *)malloc(1 + strlen(strptr
->data
.string
));
1300 strcpy(newpart
->data
.string
, strptr
->data
.string
);
1303 newpart
->data
= strptr
->data
;
1308 /*----------------------------------------------------------------------*/
1309 /* Copy the contents of a saved string with embedded parameter contents */
1310 /* back to the string and the instance parameters. */
1311 /*----------------------------------------------------------------------*/
1313 stringpart
*stringcopyback(stringpart
*string
, objinstptr thisinst
)
1315 stringpart
*strptr
, *newpart
, *curend
= NULL
;
1316 stringpart
*rettop
, *curtop
, *savend
= NULL
;
1321 for (strptr
= string
; strptr
!= NULL
; strptr
= strptr
->nextpart
) {
1323 newpart
= (stringpart
*)malloc(sizeof(stringpart
));
1324 newpart
->type
= strptr
->type
;
1325 newpart
->nextpart
= NULL
;
1326 newpart
->data
.string
= NULL
;
1328 if (strptr
== string
) rettop
= newpart
; /* initial segment */
1329 else curend
->nextpart
= newpart
; /* append segment to label */
1332 if (curend
->type
== PARAM_START
) {
1333 key
= curend
->data
.string
;
1338 else if (curend
->type
== PARAM_END
) {
1339 curend
->nextpart
= NULL
;
1340 savend
->nextpart
= newpart
;
1341 if (need_free
) freelabel(curtop
);
1347 if (strptr
->type
== TEXT_STRING
|| strptr
->type
== PARAM_START
) {
1348 if (strptr
->data
.string
) {
1349 newpart
->data
.string
= (char *)malloc(1 + strlen(strptr
->data
.string
));
1350 strcpy(newpart
->data
.string
, strptr
->data
.string
);
1353 newpart
->data
.string
= NULL
;
1355 else if (strptr
->type
== PARAM_END
) {
1357 pparam
= find_param(thisinst
, key
);
1358 if (pparam
== NULL
) {
1359 Fprintf(stderr
, "Error: Bad parameter %s encountered!\n", key
);
1361 else if (pparam
->type
== XC_STRING
) {
1362 freelabel(pparam
->parameter
.string
);
1363 pparam
->parameter
.string
= curtop
;
1369 char *tmpstr
= textprint(curtop
, thisinst
);
1371 /* Promote string types into the type of the parameter */
1372 switch (pparam
->type
) {
1374 if (sscanf(tmpstr
, "%d", &ival
) == 1)
1375 pparam
->parameter
.ivalue
= ival
;
1379 if (sscanf(tmpstr
, "%g", &fval
) == 1)
1380 pparam
->parameter
.fvalue
= fval
;
1383 /* Expression results are derived and cannot be */
1384 /* changed except by changing the expression */
1394 Fprintf(stderr
, "Error: Bad parameter in stringcopyback()\n");
1398 newpart
->data
= strptr
->data
;
1401 /* tie up loose ends, if necessary */
1402 if ((curend
!= NULL
) && (curend
->type
== PARAM_END
)) {
1403 savend
->nextpart
= NULL
;
1404 if (need_free
) freelabel(curtop
);
1410 /*---------------------------------------------------------------------*/
1411 /* draw a single character at 0, 0 using current transformation matrix */
1412 /*---------------------------------------------------------------------*/
1415 static float UDrawChar(u_char code
, short styles
, short ffont
, int groupheight
,
1416 int passcolor
, float passwidth
)
1423 if ((ffont
>= fontcount
) || (fonts
[ffont
].encoding
== NULL
))
1428 charinst
.type
= OBJINST
;
1429 charinst
.color
= DEFAULTCOLOR
;
1430 charinst
.rotation
= 0.0;
1431 charinst
.scale
= fonts
[ffont
].scale
;
1432 charinst
.position
= alphapts
[0];
1433 charinst
.params
= NULL
;
1435 /* get proper font and character */
1437 drawchar
= fonts
[ffont
].encoding
[(u_char
)code
];
1438 charinst
.thisobject
= drawchar
;
1440 localwidth
= (drawchar
->bbox
.lowerleft
.x
+ drawchar
->bbox
.width
)
1441 * fonts
[ffont
].scale
;
1443 if ((fonts
[ffont
].flags
& 0x22) == 0x22) { /* font is derived and italic */
1444 USlantCTM(DCTM
, 0.25); /* premultiply by slanting function */
1447 if (!(styles
& 64)) {
1449 UDrawObject(&charinst
, SINGLE
, passcolor
, passwidth
, NULL
);
1451 /* under- and overlines */
1453 alphapts
[0].y
= alphapts
[1].y
= -6;
1454 else if (styles
& 16)
1455 alphapts
[0].y
= alphapts
[1].y
= groupheight
+ 4;
1457 alphapts
[0].x
= 0; alphapts
[1].x
= localwidth
;
1458 UDrawSimpleLine(&alphapts
[0], &alphapts
[1]);
1463 #endif /* !HAVE_CAIRO */
1465 /*---------------------------------------------------------------------*/
1466 /* Draw a string at position offset, starting at the start index and */
1467 /* ending before the end index. The offset is updated on return. */
1468 /*---------------------------------------------------------------------*/
1471 void UDrawCharString(u_char
*text
, int start
, int end
, XfPoint
*offset
,
1472 short styles
, short ffont
, int groupheight
, int passcolor
, float tmpscale
)
1476 float tmpthick
= ((fonts
[ffont
].flags
& 0x21) == 0x21) ? 4.0 : 2.0;
1477 for (idx
= start
; idx
< end
; idx
++)
1483 UPreMultCTM(DCTM
, p
, tmpscale
, 0);
1484 offset
->x
+= UDrawChar(text
[idx
], styles
, ffont
, groupheight
, passcolor
,
1485 tmpthick
) * tmpscale
;
1489 #endif /* !HAVE_CAIRO */
1491 /*----------------------------------------------------------------------*/
1492 /* Draw an entire string, including parameter substitutions */
1493 /* (Normally called as UDrawString(); see below) */
1494 /*----------------------------------------------------------------------*/
1496 void UDrawString0(labelptr drawlabel
, int passcolor
, objinstptr localinst
,
1501 short fstyle
, ffont
, tmpanchor
;
1504 int defaultcolor
, curcolor
, scolor
;
1506 short oldfont
, oldstyle
;
1507 float tmpscale
= 1.0, natscale
= 1.0;
1509 XPoint bboxin
[2], bboxout
[2];
1512 TextLinesInfo tlinfo
;
1513 short *tabstops
= NULL
;
1514 short tabno
, numtabs
= 0;
1517 cairo_matrix_t fm
= {40., 0., 0., -40., 0., 0.}; /* TODO: Why 40? */
1519 if (!areawin
->redraw_ongoing
) {
1520 areawin
->redraw_needed
= True
;
1523 #endif /* HAVE_CAIRO */
1525 if (fontcount
== 0) return;
1527 /* Don't draw temporary labels from schematic capture system */
1528 if (drawlabel
->string
->type
!= FONT_NAME
) return;
1530 if (passcolor
== DOSUBSTRING
)
1531 defaultcolor
= curcolor
= drawlabel
->color
;
1533 defaultcolor
= curcolor
= passcolor
;
1535 if (defaultcolor
!= DOFORALL
) {
1536 if (drawlabel
->color
!= DEFAULTCOLOR
)
1537 curcolor
= drawlabel
->color
;
1539 curcolor
= defaultcolor
;
1540 XTopSetForeground(curcolor
);
1543 /* calculate the transformation matrix for this object */
1544 /* in natural units of the alphabet vectors */
1545 /* (conversion to window units) */
1548 UPreMultCTM(DCTM
, drawlabel
->position
, drawlabel
->scale
, drawlabel
->rotation
);
1550 /* check for flip invariance; recompute CTM and anchoring if necessary */
1552 tmpanchor
= flipadjust(drawlabel
->anchor
);
1555 tlinfo
.tbreak
= NULL
;
1556 tlinfo
.padding
= NULL
;
1558 /* "natural" (unscaled) length */
1559 tmpext
= ULength(drawlabel
, localinst
, &tlinfo
);
1561 newpoint
.x
= (tmpanchor
& NOTLEFT
?
1562 (tmpanchor
& RIGHT
? -tmpext
.maxwidth
: -tmpext
.maxwidth
>> 1) : 0);
1563 newpoint
.y
= (tmpanchor
& NOTBOTTOM
?
1564 (tmpanchor
& TOP
? -tmpext
.ascent
: -(tmpext
.ascent
+ tmpext
.base
) >> 1)
1567 /* Pinlabels have an additional offset spacing to pad */
1568 /* them from the circuit point to which they attach. */
1570 if (drawlabel
->pin
) {
1574 pinadjust(tmpanchor
, &p
.x
, &p
.y
, 1);
1580 baseline
= newpoint
.y
;
1582 /* do quick calculation on bounding box; don't draw if off-screen */
1584 bboxin
[0].x
= newpoint
.x
;
1585 bboxin
[0].y
= newpoint
.y
+ tmpext
.descent
;
1586 bboxin
[1].x
= newpoint
.x
+ tmpext
.maxwidth
;
1587 bboxin
[1].y
= newpoint
.y
+ tmpext
.ascent
;
1588 UTransformbyCTM(DCTM
, bboxin
, bboxout
, 2);
1589 xm
= (bboxout
[0].x
< bboxout
[1].x
) ? 0 : 1;
1590 ym
= (bboxout
[0].y
< bboxout
[1].y
) ? 0 : 1;
1592 if (bboxout
[xm
].x
< areawin
->width
&& bboxout
[ym
].y
< areawin
->height
&&
1593 bboxout
[1 - xm
].x
> 0 && bboxout
[1 - ym
].y
> 0) {
1597 /* At the beginning of the line, add offsets for right or center */
1598 /* justification, if required. */
1600 if (tlinfo
.padding
!= NULL
) {
1601 if (tmpanchor
& JUSTIFYRIGHT
)
1602 newpoint
.x
+= tlinfo
.padding
[linenum
];
1603 else if (tmpanchor
& TEXTCENTERED
)
1604 newpoint
.x
+= 0.5 * tlinfo
.padding
[linenum
];
1608 for (strptr
= drawlabel
->string
; strptr
!= NULL
;
1609 strptr
= nextstringpart(strptr
, localinst
)) {
1611 /* All segments other than text cancel any */
1612 /* existing overline/underline in effect. */
1614 if (strptr
->type
!= TEXT_STRING
)
1617 /* deal with each text segment type */
1619 switch(strptr
->type
) {
1621 if (strptr
->data
.font
< fontcount
) {
1622 ffont
= strptr
->data
.font
;
1623 fstyle
= 0; /* style reset by font change */
1624 if (baseline
== newpoint
.y
) { /* set top-level font and style */
1630 cairo_set_font_face(areawin
->cr
, fonts
[ffont
].font_face
);
1631 cairo_set_font_matrix(areawin
->cr
, &fm
);
1632 #endif /* HAVE_CAIRO */
1636 tmpscale
= natscale
* strptr
->data
.scale
;
1637 /* if (baseline == newpoint.y) */ /* reset top-level scale */
1638 /* natscale = tmpscale; */
1642 newpoint
.x
+= strptr
->data
.kern
[0];
1643 newpoint
.y
+= strptr
->data
.kern
[1];
1647 if (defaultcolor
!= DOFORALL
) {
1648 if (strptr
->data
.color
!= DEFAULTCOLOR
) {
1649 curcolor
= strptr
->data
.color
;
1651 XTopSetForeground(curcolor
);
1655 if (curcolor
!= DEFAULTCOLOR
) {
1656 XTopSetForeground(defaultcolor
);
1658 curcolor
= DEFAULTCOLOR
;
1663 case TABBACKWARD
: /* find first tab value with x < xtotal */
1664 for (tabno
= numtabs
- 1; tabno
>= 0; tabno
--) {
1665 if (tabstops
[tabno
] < newpoint
.x
) {
1666 newpoint
.x
= tabstops
[tabno
];
1672 case TABFORWARD
: /* find first tab value with x > xtotal */
1673 for (tabno
= 0; tabno
< numtabs
; tabno
++) {
1674 if (tabstops
[tabno
] > newpoint
.x
) {
1675 newpoint
.x
= tabstops
[tabno
];
1683 if (tabstops
== NULL
) tabstops
= (short *)malloc(sizeof(short));
1684 else tabstops
= (short *)realloc(tabstops
, numtabs
* sizeof(short));
1685 tabstops
[numtabs
- 1] = newpoint
.x
;
1689 tmpscale
= natscale
= 1.0;
1690 baseline
-= BASELINE
;
1691 newpoint
.y
= baseline
;
1694 if (tlinfo
.padding
!= NULL
) {
1695 if (tmpanchor
& JUSTIFYRIGHT
)
1696 newpoint
.x
+= tlinfo
.padding
[linenum
];
1697 else if (tmpanchor
& TEXTCENTERED
)
1698 newpoint
.x
+= 0.5 * tlinfo
.padding
[linenum
];
1704 natscale
*= SUBSCALE
;
1705 tmpscale
= natscale
;
1706 newpoint
.y
-= (short)((TEXTHEIGHT
>> 1) * natscale
);
1710 natscale
*= SUBSCALE
;
1711 tmpscale
= natscale
;
1712 newpoint
.y
+= (short)(TEXTHEIGHT
* natscale
);
1716 tmpscale
= natscale
= 1.0;
1717 ffont
= oldfont
; /* revert to top-level font and style */
1719 newpoint
.y
= baseline
;
1721 cairo_set_font_face(areawin
->cr
, fonts
[oldfont
].font_face
);
1722 cairo_set_font_matrix(areawin
->cr
, &fm
);
1723 #endif /* HAVE_CAIRO */
1731 if (strptr
->nextpart
!= NULL
&& strptr
->nextpart
->type
== TEXT_STRING
) {
1735 for (textptr
= strptr
->nextpart
->data
.string
;
1736 textptr
&& *textptr
!= '\0'; textptr
++) {
1738 tmpheight
= fonts
[ffont
].glyph_top
[*textptr
];
1739 #else /* HAVE_CAIRO */
1741 charptr
= fonts
[ffont
].encoding
[*(u_char
*)textptr
];
1742 tmpheight
= (int)((float)charptr
->bbox
.height
1743 * fonts
[ffont
].scale
);
1744 #endif /* HAVE_CAIRO */
1745 if (group
< tmpheight
) group
= tmpheight
;
1756 newpoint
.x
+= fonts
[ffont
].glyph_advance
[' '] * tmpscale
/ 2.;
1760 newpoint
.x
+= fonts
[ffont
].glyph_advance
[' '] * tmpscale
/ 4.;
1762 #else /* HAVE_CAIRO */
1763 case HALFSPACE
: case QTRSPACE
: {
1769 UPreMultCTM(DCTM
, p
, tmpscale
, 0);
1770 addx
= UDrawChar((u_char
)32, fstyle
, ffont
, group
,
1772 newpoint
.x
+= addx
>> ((strptr
->type
== HALFSPACE
) ? 1 : 2);
1775 #endif /* HAVE_CAIRO */
1778 textptr
= strptr
->data
.string
;
1780 /* Don't write technology names in catalog mode if this */
1781 /* option is enabled (but *not* CATTEXT_MODE!) */
1783 if (((eventmode
== CATALOG_MODE
) && !xobjs
.showtech
)
1784 || ((eventmode
== CATTEXT_MODE
)
1785 && (drawlabel
!= TOLABEL(EDITPART
)))) {
1786 char *nsptr
= strstr(textptr
, "::");
1787 if (nsptr
!= NULL
) {
1788 textptr
= nsptr
+ 2;
1789 pos
+= (pointertype
)nsptr
- (pointertype
)strptr
->data
.string
+ 2;
1794 if (passcolor
== DOSUBSTRING
) {
1795 int len
= strlen(textptr
);
1796 int begin_sel
= min(max(0, areawin
->textend
- pos
), len
);
1797 int end_sel
= min(max(0, areawin
->textpos
- pos
), len
);
1798 if (begin_sel
> 0) {
1799 UDrawCharString(textptr
, 0, begin_sel
, &newpoint
, fstyle
,
1800 ffont
, group
, scolor
, tmpscale
);
1802 if (begin_sel
< end_sel
) {
1803 scolor
= SELECTCOLOR
;
1805 XTopSetForeground(scolor
);
1806 #endif /* HAVE_CAIRO */
1807 UDrawCharString(textptr
, begin_sel
, end_sel
, &newpoint
,
1808 fstyle
, ffont
, group
, scolor
, tmpscale
);
1810 if (end_sel
< len
) {
1812 XTopSetForeground(curcolor
);
1813 UDrawCharString(textptr
, end_sel
, len
, &newpoint
, fstyle
,
1814 ffont
, group
, scolor
, tmpscale
);
1818 UDrawCharString(textptr
, 0, strlen(textptr
),
1819 &newpoint
, fstyle
, ffont
, group
, scolor
, tmpscale
);
1821 pos
+= strlen(textptr
) - 1;
1828 /* enddraw: (jdk) */
1830 if (tabstops
!= NULL
) free(tabstops
);
1831 if (tlinfo
.padding
!= NULL
) free(tlinfo
.padding
);
1833 /* Pop the string transformation matrix */
1837 if (drawX
&& drawlabel
->pin
) UDrawXDown(drawlabel
);
1839 if ((defaultcolor
!= DOFORALL
) && (passcolor
!= curcolor
) &&
1840 (passcolor
!= DOSUBSTRING
)) {
1841 XTopSetForeground(passcolor
);
1845 /*----------------------------------------------------------------------*/
1846 /* Draw String, with an "x" mark at the origin */
1847 /*----------------------------------------------------------------------*/
1849 void UDrawString(labelptr drawlabel
, int passcolor
, objinstptr localinst
)
1851 UDrawString0(drawlabel
, passcolor
, localinst
, TRUE
);
1854 /*----------------------------------------------------------------------*/
1855 /* Draw String, without the "x" mark */
1856 /*----------------------------------------------------------------------*/
1858 void UDrawStringNoX(labelptr drawlabel
, int passcolor
, objinstptr localinst
)
1860 UDrawString0(drawlabel
, passcolor
, localinst
, FALSE
);
1863 /*----------------------------------------------------------------------*/
1864 /* Compute the actual length of a string or portion thereof. */
1866 /* The third argument is a pointer to a structure TextLinesInfo, having */
1867 /* fields "dostop", "tbreak", and "padding". The third argument may */
1868 /* be NULL, in which case dostop defaults to 0 and tbreak and */
1869 /* padding default to NULL. */
1871 /* If "dostop" is non-zero, ULength computes the length to a specific */
1872 /* location in the string. */
1873 /* If "padding" is non-NULL, it is a pointer to an array to be */
1874 /* allocated and filled in with the individual padding lengths. If */
1875 /* the array is already allocated, then it is assumed to contain valid */
1876 /* linewidth information from a previous call to ULength(). */
1877 /* If "tbreak" is non-NULL, it is a pointer to an XPoint, and the */
1878 /* return TextExtents record "width" will be filled in with the index */
1879 /* position in the string that is closest to that point. */
1880 /* When using either "dostop" or "tbreak", the line number stopped on */
1881 /* is returned in TextLinesInfo field "line". When using "tbreak", the */
1882 /* position index is returned in "dostop". */
1883 /*----------------------------------------------------------------------*/
1885 TextExtents
ULength(labelptr drawlabel
, objinstptr localinst
,
1886 TextLinesInfo
*tlinfo
)
1891 float oldscale
, strscale
, natscale
, locscale
= 1.0, xtotal
= 0.5;
1892 float lasttotal
= xtotal
;
1895 objectptr
*somebet
= NULL
;
1896 short locpos
= 0, lastpos
= 0;
1899 short *tabstops
= NULL
;
1900 short tabno
, numtabs
= 0;
1902 Boolean dobreak
= FALSE
;
1904 fontinfo
*font
= NULL
;
1905 #endif /* HAVE_CAIRO */
1907 dostop
= (tlinfo
== NULL
) ? 0 : tlinfo
->dostop
;
1909 retext
.ascent
= retext
.descent
= retext
.base
= 0;
1910 retext
.width
= retext
.maxwidth
= 0;
1912 if (fontcount
== 0) return retext
;
1914 /* Don't draw temporary labels from schematic capture system */
1915 else if (drawlabel
->string
->type
!= FONT_NAME
) return retext
;
1917 /* Copy tbreak point locally */
1918 if ((tlinfo
!= NULL
) && (tlinfo
->tbreak
!= NULL
)) tbreak
= *tlinfo
->tbreak
;
1920 /* Adjust tbreak x value for justification */
1921 if ((tlinfo
!= NULL
) && (tlinfo
->padding
!= NULL
)) {
1922 if (drawlabel
->anchor
& JUSTIFYRIGHT
)
1923 tbreak
.x
-= tlinfo
->padding
[linenum
];
1924 else if (drawlabel
->anchor
& TEXTCENTERED
)
1925 tbreak
.x
-= 0.5 * tlinfo
->padding
[linenum
];
1929 oldscale
= strscale
= natscale
;
1931 for (strptr
= drawlabel
->string
; strptr
!= NULL
;
1932 strptr
= nextstringpart(strptr
, localinst
)) {
1933 switch (strptr
->type
) {
1935 natscale
*= SUBSCALE
;
1936 strscale
= natscale
;
1937 ykern
+= TEXTHEIGHT
* natscale
;
1940 natscale
*= SUBSCALE
;
1941 strscale
= natscale
;
1942 ykern
-= TEXTHEIGHT
* natscale
/ 2.0;
1945 natscale
= strscale
= oldscale
;
1949 if (tlinfo
!= NULL
) {
1950 if (tlinfo
->padding
== NULL
) {
1951 stringpart
*nextret
= strptr
;
1953 while ((nextret
= nextstringpart(nextret
, localinst
)) != NULL
)
1954 if (nextret
->type
== RETURN
)
1956 tlinfo
->padding
= (float *)malloc(numlines
* sizeof(float));
1958 if (tlinfo
->padding
!= NULL
)
1959 tlinfo
->padding
[linenum
++] = xtotal
;
1961 natscale
= strscale
= oldscale
;
1963 retext
.base
-= BASELINE
;
1964 retext
.maxwidth
= max(retext
.width
, retext
.maxwidth
);
1965 retext
.maxwidth
= max(retext
.maxwidth
, xtotal
);
1968 /* Re-copy tbreak point locally */
1969 if ((tlinfo
!= NULL
) && (tlinfo
->tbreak
!= NULL
))
1970 tbreak
= *tlinfo
->tbreak
;
1972 /* Adjust tbreak x value for justification on next line */
1973 if ((tlinfo
!= NULL
) && (tlinfo
->padding
!= NULL
)) {
1974 if (drawlabel
->anchor
& JUSTIFYRIGHT
)
1975 tbreak
.x
-= tlinfo
->padding
[linenum
];
1976 else if (drawlabel
->anchor
& TEXTCENTERED
)
1977 tbreak
.x
-= 0.5 * tlinfo
->padding
[linenum
];
1983 xtotal
+= font
->glyph_advance
[' '] * locscale
* strscale
/ 2.;
1984 #else /* HAVE_CAIRO */
1985 objectptr chptr
= (*(somebet
+ 32));
1986 xtotal
+= (float)(chptr
->bbox
.width
+ chptr
->bbox
.lowerleft
.x
)
1987 * locscale
* natscale
/ 2.;
1988 #endif /* HAVE_CAIRO */
1994 xtotal
+= font
->glyph_advance
[' '] * locscale
* strscale
/ 4.;
1995 #else /* HAVE_CAIRO */
1996 objectptr chptr
= (*(somebet
+ 32));
1997 xtotal
+= (float)(chptr
->bbox
.width
+ chptr
->bbox
.lowerleft
.x
)
1998 * locscale
* natscale
/ 4.;
1999 #endif /* HAVE_CAIRO */
2002 case TABBACKWARD
: /* find first tab value with x < xtotal */
2003 for (tabno
= numtabs
- 1; tabno
>= 0; tabno
--) {
2004 if (tabstops
[tabno
] < xtotal
) {
2005 xtotal
= tabstops
[tabno
];
2010 case TABFORWARD
: /* find first tab value with x > xtotal */
2011 for (tabno
= 0; tabno
< numtabs
; tabno
++) {
2012 if (tabstops
[tabno
] > xtotal
) {
2013 xtotal
= tabstops
[tabno
];
2020 if (tabstops
== NULL
) tabstops
= (short *)malloc(sizeof(short));
2021 else tabstops
= (short *)realloc(tabstops
, numtabs
* sizeof(short));
2022 tabstops
[numtabs
- 1] = xtotal
;
2025 strscale
= natscale
* strptr
->data
.scale
;
2026 /* if (ykern == 0.0) */
2027 /* natscale = strscale; */
2030 xtotal
+= strptr
->data
.kern
[0];
2031 ykern
+= strptr
->data
.kern
[1];
2034 if (strptr
->data
.font
< fontcount
) {
2035 somebet
= fonts
[strptr
->data
.font
].encoding
;
2036 locscale
= fonts
[strptr
->data
.font
].scale
;
2038 natscale
= locscale
;
2040 font
= fonts
+ strptr
->data
.font
;
2041 #endif /* HAVE_CAIRO */
2045 textptr
= strptr
->data
.string
;
2047 /* Don't write technology names in catalog mode if */
2048 /* the option is enabled, so ignore when measuring */
2050 if (((eventmode
== CATALOG_MODE
) && !xobjs
.showtech
)
2051 || ((eventmode
== CATTEXT_MODE
)
2052 && (drawlabel
!= TOLABEL(EDITPART
)))) {
2053 char *nsptr
= strstr(textptr
, "::");
2054 if (nsptr
!= NULL
) {
2055 textptr
= nsptr
+ 2;
2056 locpos
+= (pointertype
)nsptr
- (pointertype
)strptr
->data
.string
+ 2;
2060 if (somebet
== NULL
) break;
2062 for (; textptr
&& *textptr
!= '\0'; textptr
++) {
2063 float advance
, top
, bottom
;
2064 if (dostop
&& (locpos
>= dostop
)) break;
2068 advance
= font
->glyph_advance
[*textptr
];
2069 top
= font
->glyph_top
[*textptr
];
2070 bottom
= font
->glyph_bottom
[*textptr
];
2071 #else /* HAVE_CAIRO */
2073 objectptr chptr
= *(somebet
+ *textptr
);
2074 advance
= chptr
->bbox
.width
+ chptr
->bbox
.lowerleft
.x
;
2075 top
= chptr
->bbox
.height
+ chptr
->bbox
.lowerleft
.y
;
2076 bottom
= chptr
->bbox
.lowerleft
.y
;
2078 #endif /* HAVE_CAIRO */
2079 xtotal
+= advance
* locscale
* strscale
;
2080 retext
.ascent
= max(retext
.ascent
, (short)(retext
.base
+ ykern
2081 + top
* locscale
* strscale
));
2082 retext
.descent
= min(retext
.descent
, (short)(retext
.base
+ ykern
2083 + bottom
* locscale
* strscale
));
2085 if ((tlinfo
!= NULL
) && (tlinfo
->tbreak
!= NULL
)) {
2086 if ((xtotal
> tbreak
.x
) && (retext
.base
<= tbreak
.y
)) {
2097 if (strptr
->type
!= TEXT_STRING
) locpos
++;
2098 if (dostop
&& (locpos
>= dostop
)) break;
2101 if (tabstops
!= NULL
) free(tabstops
);
2103 /* Update width and maxwidth entries */
2104 retext
.width
= max(retext
.width
, xtotal
);
2105 retext
.maxwidth
= max(retext
.maxwidth
, xtotal
);
2107 /* If not doing "dostop", put the last position into the */
2108 /* list of padding. Since padding values are currently */
2109 /* linewidths, subtract them from maxwidth. */
2111 if (tlinfo
!= NULL
) {
2113 if ((tlinfo
->dostop
== 0) && (tlinfo
->padding
!= NULL
)) {
2114 tlinfo
->padding
[linenum
] = xtotal
;
2115 for (i
= 0; i
<= linenum
; i
++)
2116 tlinfo
->padding
[i
] = retext
.maxwidth
- tlinfo
->padding
[i
];
2118 tlinfo
->line
= linenum
;
2121 /* Return character position in tlinfo->dostop (which should */
2122 /* be unused if using "tbreak" */
2124 if ((tlinfo
!= NULL
) && (tlinfo
->tbreak
!= NULL
)) {
2125 int slen
= stringlength(drawlabel
->string
, True
, localinst
);
2126 if ((tbreak
.x
- lasttotal
) < (xtotal
- tbreak
.x
))
2127 locpos
= lastpos
+ 1;
2128 if (locpos
< 1) locpos
= 1;
2129 else if (locpos
> slen
) locpos
= slen
;
2130 if (tlinfo
!= NULL
) tlinfo
->dostop
= locpos
;
2135 /*----------------------------------------------------------------------*/
2136 /* Remove all RETURN directives following a MARGINSTOP. Automatically */
2137 /* generated line breaks are identified by an nonzero "flags" field. */
2138 /*----------------------------------------------------------------------*/
2140 void RemoveMarginNewlines(labelptr settext
, objinstptr localinst
)
2145 for (strptr
= settext
->string
; strptr
!= NULL
;
2146 strptr
= nextstringpart(strptr
, localinst
)) {
2147 switch (strptr
->type
) {
2149 if (strptr
->data
.flags
!= 0) {
2150 /* Remove (without merge) */
2151 strptr
= deletestring0(strptr
, &settext
->string
, localinst
, FALSE
);
2152 if (strpos
<= areawin
->textpos
) areawin
->textpos
--;
2158 if (strptr
->data
.string
)
2159 strpos
+= strlen(strptr
->data
.string
);
2169 /*----------------------------------------------------------------------*/
2170 /* Analyze a text label and insert RETURN directives as necessary to */
2171 /* keep the text within the width limit set by MARGIN. Break up text */
2172 /* at word boundaries where necessary. This routine is run only when */
2173 /* (1) editing a text label or loading one from a file, and (2) the */
2174 /* text label both contains a MARGIN directive and exceeds the margin */
2175 /* width, as determined by CheckMarginStop(). */
2176 /*----------------------------------------------------------------------*/
2178 void InsertMarginNewlines(labelptr settext
, objinstptr localinst
)
2180 stringpart
*strptr
, *lastseg
= NULL
;
2182 int strpos
= 0, locpos
, slen
, savelen
;
2184 TextLinesInfo tlinfo
;
2186 /* 1) Find the position of the margin stop. Track position */
2187 /* in string a la findstringpart(), as we need to pass this */
2190 for (strptr
= settext
->string
; strptr
!= NULL
;
2191 strptr
= nextstringpart(strptr
, localinst
)) {
2192 switch (strptr
->type
) {
2194 margin
= strptr
->data
.width
;
2199 if (strptr
->data
.string
)
2200 strpos
+= strlen(strptr
->data
.string
);
2207 if (margin
> 0) break;
2209 if (margin
== 0) return; /* Should not happen. . . */
2212 /* 2) Compute the drawn string length at each word break. When a */
2213 /* word overruns the margin, place a line break in front of it. */
2215 tlinfo
.tbreak
= NULL
;
2216 tlinfo
.padding
= NULL
;
2219 strptr
= findstringpart(strpos
, &locpos
, settext
->string
, localinst
);
2220 if (strptr
== NULL
) break;
2221 else if (strptr
->type
== TEXT_STRING
) {
2222 slen
= strlen(strptr
->data
.string
);
2223 /* Ignore trailing spaces */
2224 while ((slen
> 0) && (*(strptr
->data
.string
+ slen
- 1) == ' ')) slen
--;
2226 tlinfo
.dostop
= strpos
+ slen
;
2228 tmpext
= ULength(settext
, localinst
, &tlinfo
);
2229 if (tmpext
.width
> margin
) {
2231 while ((slen
> 0) && (tmpext
.width
> margin
)) {
2232 while ((slen
> 0) && (*(strptr
->data
.string
+ slen
- 1) != ' ')) slen
--;
2233 while ((slen
> 0) && (*(strptr
->data
.string
+ slen
- 1) == ' ')) {
2237 tlinfo
.dostop
= strpos
+ slen
- 1;
2238 tmpext
= ULength(settext
, localinst
, &tlinfo
);
2240 /* Take the first space, in case we have a single word so long that */
2241 /* it exceeds the margin by itself. */
2242 if (savelen
> slen
) slen
= savelen
;
2244 /* Split string at word separation before the margin. */
2246 while ((slen
> 0) && (*(strptr
->data
.string
+ slen
) == ' ')) slen
++;
2247 strptr
= splitstring(strpos
+ slen
, &settext
->string
, localinst
);
2248 strptr
= nextstringpart(strptr
, localinst
);
2251 /* Insert a carriage return, if the previous segment was not */
2253 if (slen
> 0 || (lastseg
->type
!= RETURN
)) {
2254 strptr
= makesegment(&settext
->string
, strptr
);
2255 strptr
->type
= RETURN
;
2256 strptr
->data
.flags
= 1; /* Mark as auto-generated line wrap */
2258 if (areawin
->textpos
> strpos
) areawin
->textpos
++;
2261 strpos
+= strlen(strptr
->data
.string
);
2264 strpos
+= strlen(strptr
->data
.string
);
2266 else if (strptr
->type
== MARGINSTOP
) {
2267 /* Allows multiple margin stops in the same text block */
2268 margin
= strptr
->data
.width
;
2278 /*----------------------------------------------------------------------*/
2279 /* Check a string for presence of a MARGINSTOP directive. If it has */
2280 /* one, check if ULength exceeds the margin. If so, remove all Return */
2281 /* directives after the MARGINSTOP, and re-insert them such that the */
2282 /* text stays within the margin. */
2283 /*----------------------------------------------------------------------*/
2285 void CheckMarginStop(labelptr settext
, objinstptr localinst
, Boolean force
)
2290 TextLinesInfo tlinfo
;
2292 for (strptr
= settext
->string
; strptr
!= NULL
;
2293 strptr
= nextstringpart(strptr
, localinst
)) {
2294 switch (strptr
->type
) {
2296 margin
= strptr
->data
.width
;
2299 if (margin
> 0) break;
2303 tlinfo
.tbreak
= NULL
;
2304 tlinfo
.padding
= NULL
;
2305 tmpext
= ULength(settext
, localinst
, &tlinfo
);
2306 if ((force
== TRUE
) || (tmpext
.maxwidth
> margin
)) {
2307 RemoveMarginNewlines(settext
, localinst
);
2308 InsertMarginNewlines(settext
, localinst
);
2312 /* In case the Margin Stop directive just got deleted. . . */
2313 RemoveMarginNewlines(settext
, localinst
);
2317 /*----------------------------------------------------------------------*/
2318 /* low-level routines for drawing and erasing labels */
2319 /*----------------------------------------------------------------------*/
2321 void undrawtextsimple(labelptr settext
)
2323 XTopSetForeground(BACKGROUND
);
2324 UDrawString(settext
, DOFORALL
, areawin
->topinstance
);
2327 /*----------------------------------------------------------------------*/
2329 void redrawtextsimple(labelptr settext
)
2331 UDrawString(settext
, settext
->color
, areawin
->topinstance
);
2334 /*----------------------------------------------------------------------*/
2335 /* Redraw all labels in the current object which contain the same */
2336 /* parameter(s) as the indicated label. */
2337 /* (It's easier not to bother to check for the same parameter, as there */
2338 /* are typically so few parameters in an object that the extra compute */
2339 /* and draw time is negligible.) */
2341 /* Function pointer (undrawtextsimple or redrawtextsimple) indicates */
2342 /* which function to call on this text label. */
2343 /*----------------------------------------------------------------------*/
2345 void drawtextandupdate(labelptr curlabel
, void (*function
)(labelptr
))
2350 for (pgen
= topobject
->plist
; pgen
< topobject
->plist
+ topobject
->parts
;
2352 if (IS_LABEL(*pgen
)) {
2353 slab
= TOLABEL(pgen
);
2354 if (slab
== curlabel
) continue;
2355 if (hasparameter(slab
))
2361 /*----------------------------------------------------------------------*/
2362 /* Wrapper functions for drawtextandupdate() */
2363 /*----------------------------------------------------------------------*/
2365 void undrawtext(labelptr curlabel
)
2367 undrawtextsimple(curlabel
);
2369 if (hasparameter(curlabel
))
2370 drawtextandupdate(curlabel
, undrawtextsimple
);
2373 /*----------------------------------------------------------------------*/
2375 void redrawtext(labelptr curlabel
)
2377 redrawtextsimple(curlabel
);
2379 if (hasparameter(curlabel
))
2380 drawtextandupdate(curlabel
, redrawtextsimple
);
2383 /*----------------------------------------------------------------------*/
2384 /* Draw the catalog of font characters */
2385 /*----------------------------------------------------------------------*/
2387 void composefontlib(short cfont
)
2389 /* genericptr *pgen; (jdk) */
2391 objectptr directory
= xobjs
.libtop
[FONTLIB
]->thisobject
;
2392 short visobjects
, i
;
2396 reset(directory
, NORMAL
);
2398 /* Find the number of non-null characters. Do this by assuming */
2399 /* that all fonts encode nullchar at position zero. */
2402 nullobj
= fonts
[cfont
].encoding
[0];
2403 for(i
= 1; i
< 256; i
++)
2404 if (fonts
[cfont
].encoding
[i
] != nullobj
) visobjects
++;
2406 /* add the box and gridlines */
2410 /* generate the list of object instances */
2412 directory
->plist
= (genericptr
*) realloc(directory
->plist
, visobjects
2413 * sizeof(genericptr
));
2414 directory
->parts
= 0;
2416 for (i
= 0; i
< 256; i
++) {
2417 stringpart
*sfont
, *schar
;
2419 u_char character
[2] = {0, 0};
2422 if (fonts
[cfont
].encoding
[i
] == nullobj
)
2425 sfont
= (stringpart
*) malloc(sizeof(stringpart
));
2426 schar
= (stringpart
*) malloc(sizeof(stringpart
));
2427 sfont
->type
= FONT_NAME
;
2428 sfont
->data
.font
= cfont
;
2429 sfont
->nextpart
= schar
;
2430 schar
->type
= TEXT_STRING
;
2431 schar
->data
.string
= strdup(character
);
2432 schar
->nextpart
= NULL
;
2433 label
= new_label(xobjs
.libtop
[FONTLIB
], sfont
, NORMAL
,
2434 (i
% 16) * del
+ del
/ 2, -(i
/ 16) * del
- del
/ 2 - 32 /2,
2436 label
->anchor
= NOTLEFT
;
2437 label
->color
= DEFAULTCOLOR
;
2440 /* separate characters with gridlines (17 vert., 17 horiz.) */
2442 for (i
= 0; i
< 34; i
++) {
2443 NEW_POLY(drawbox
, directory
);
2444 polydefaults(*drawbox
, 2, 0, 0);
2446 (*drawbox
)->color
= SNAPCOLOR
; /* default red */
2447 (*drawbox
)->style
= UNCLOSED
;
2448 (*drawbox
)->width
= 1.0;
2451 pointptr
= (*drawbox
)->points
;
2452 pointptr
->x
= i
* del
;
2454 pointptr
= (*drawbox
)->points
+ 1;
2455 pointptr
->x
= i
* del
;
2456 pointptr
->y
= -16 * del
;
2459 pointptr
= (*drawbox
)->points
;
2461 pointptr
->y
= (17 - i
) * del
;
2462 pointptr
= (*drawbox
)->points
+ 1;
2463 pointptr
->x
= 16 * del
;
2464 pointptr
->y
= (17 - i
) * del
;
2468 /* Set the bounding box for this display. */
2469 /* This is just the bounds of the grid built above, so there's no */
2470 /* need to call any of the bounding box calculation routines. */
2472 directory
->bbox
.lowerleft
.x
= 0;
2473 directory
->bbox
.lowerleft
.y
= pointptr
->y
;
2474 directory
->bbox
.width
= pointptr
->x
;
2475 directory
->bbox
.height
= pointptr
->x
;
2477 xobjs
.libtop
[FONTLIB
]->bbox
.lowerleft
.x
= 0;
2478 xobjs
.libtop
[FONTLIB
]->bbox
.lowerleft
.y
= pointptr
->y
;
2479 xobjs
.libtop
[FONTLIB
]->bbox
.width
= pointptr
->x
;
2480 xobjs
.libtop
[FONTLIB
]->bbox
.height
= pointptr
->x
;
2482 centerview(xobjs
.libtop
[FONTLIB
]);
2485 /*------------------------------------------------------*/
2486 /* ButtonPress handler during font catalog viewing mode */
2487 /*------------------------------------------------------*/
2489 void fontcat_op(int op
, int x
, int y
)
2494 if (op
!= XCF_Cancel
) {
2496 window_to_user(x
, y
, &areawin
->save
);
2498 chy
= -areawin
->save
.y
/ del
;
2499 chx
= areawin
->save
.x
/ del
;
2504 rch
= (u_long
)(chy
* 16 + chx
);
2510 labeltext(rch
, NULL
);
2513 /*-------------------------------------------------------------------------*/