1 /*----------------------------------------------------------------------*/
2 /* elements.c --- xcircuit routines for creating basic elements */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*----------------------------------------------------------------------*/
6 /*----------------------------------------------------------------------*/
7 /* written by Tim Edwards, 8/13/93 */
8 /*----------------------------------------------------------------------*/
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
27 /*----------------------------------------------------------------------*/
29 /*----------------------------------------------------------------------*/
32 #include "colordefs.h"
34 /*----------------------------------------------------------------------*/
35 /* Function prototype declarations */
36 /*----------------------------------------------------------------------*/
37 #include "prototypes.h"
39 /*----------------------------------------------------------------------*/
40 /* Global Variable definitions */
41 /*----------------------------------------------------------------------*/
43 extern Display
*dpy
; /* Works well to make this globally accessible */
44 extern Cursor appcursors
[NUM_CURSORS
];
45 extern XCWindowData
*areawin
;
46 extern Globaldata xobjs
;
48 extern fontinfo
*fonts
;
49 extern short fontcount
;
50 extern char _STR
[150], _STR2
[250];
51 extern int number_colors
;
52 extern colorindex
*colorlist
;
53 #if !defined(HAVE_CAIRO)
57 extern double atan2();
59 /*------------------------------------------------------------------------*/
60 /* Declarations of global variables */
61 /*------------------------------------------------------------------------*/
67 /*--------------------------------------*/
68 /* Element constructor functions */
69 /*--------------------------------------*/
71 /*--------------------------------------------------------------*/
72 /* Label constructor: Create a new label element in the object */
73 /* whose instance is "destinst" and return a pointer to it. */
75 /* "destinst" is the destination instance. If NULL, the */
76 /* top-level instance (areawin->topinstance) is used. */
77 /* "strptr" is a pointer to a stringpart string, and may */
78 /* be NULL. If non-NULL, should NOT be free'd by the */
79 /* calling routine. */
80 /* "pintype" is NORMAL, LOCAL, GLOBAL, or INFO */
81 /* "x" and "y" are the label coordinates. */
82 /* if "dochange" is FALSE, then this label is being drawn */
83 /* as part of a font or library and should not cause */
84 /* changes count to increment. */
86 /* Other properties must be set individually by the calling */
89 /* Return value is a pointer to the newly created label. */
90 /*--------------------------------------------------------------*/
92 labelptr
new_label(objinstptr destinst
, stringpart
*strptr
, int pintype
,
93 int x
, int y
, u_char dochange
)
97 objinstptr locdestinst
;
99 locdestinst
= (destinst
== NULL
) ? areawin
->topinstance
: destinst
;
100 destobject
= locdestinst
->thisobject
;
102 NEW_LABEL(newlab
, destobject
);
103 labeldefaults(*newlab
, pintype
, x
, y
);
105 if (strptr
->type
== FONT_NAME
) {
106 free ((*newlab
)->string
);
107 (*newlab
)->string
= strptr
;
110 (*newlab
)->string
->nextpart
= strptr
;
112 calcbboxvalues(locdestinst
, (genericptr
*)newlab
);
113 updatepagebounds(destobject
);
114 if (dochange
!= (u_char
)0) incr_changes(destobject
);
118 /*--------------------------------------------------------------*/
119 /* Variant of the above; creates a label from a (char *) string */
120 /* instead of a stringpart pointer. Like the stringpart */
121 /* pointer above, "cstr" should NOT be free'd by the calling */
123 /*--------------------------------------------------------------*/
125 labelptr
new_simple_label(objinstptr destinst
, char *cstr
,
126 int pintype
, int x
, int y
)
130 strptr
= (stringpart
*)malloc(sizeof(stringpart
));
131 strptr
->type
= TEXT_STRING
;
132 strptr
->nextpart
= NULL
;
133 strptr
->data
.string
= cstr
;
135 return new_label(destinst
, strptr
, pintype
, x
, y
, (u_char
)0);
138 /*--------------------------------------------------------------*/
139 /* Another variant of the above; creates a "temporary" label */
140 /* from a (char *) string. As above, "cstr" should NOT be */
141 /* free'd by the calling routine. The "temporary" label has no */
142 /* font information, and cannot be displayed nor saved/loaded */
143 /* from the PostScript output file. Used to name networks or */
144 /* to mark port positions. Pin type is always LOCAL. Does not */
145 /* require updating bounding box info since it cannot be */
146 /* displayed. Consequently, only requires passing the object */
147 /* to get the new label, not its instance. */
148 /*--------------------------------------------------------------*/
150 labelptr
new_temporary_label(objectptr destobject
, char *cstr
,
155 NEW_LABEL(newlab
, destobject
);
156 labeldefaults(*newlab
, LOCAL
, x
, y
);
158 (*newlab
)->string
->type
= TEXT_STRING
; /* overwrites FONT record */
159 (*newlab
)->string
->data
.string
= cstr
;
163 /*--------------------------------------------------------------*/
164 /* Polygon constructor: Create a new polygon element in the */
165 /* object whose instance is "destinst" and return a pointer to */
168 /* "destinst" is the destination instance. If NULL, the */
169 /* top-level instance (areawin->topinstance) is used. */
170 /* "points" is a list of XPoint pointers, should not be */
171 /* NULL. It is transferred to the polygon verbatim, */
172 /* and should NOT be free'd by the calling routine. */
173 /* "number" is the number of points in the list, or zero */
174 /* if "points" is NULL. */
176 /* Other properties must be set individually by the calling */
179 /* Return value is a pointer to the newly created polygon. */
180 /*--------------------------------------------------------------*/
182 polyptr
new_polygon(objinstptr destinst
, pointlist
*points
, int number
)
185 objectptr destobject
;
186 objinstptr locdestinst
;
188 locdestinst
= (destinst
== NULL
) ? areawin
->topinstance
: destinst
;
189 destobject
= locdestinst
->thisobject
;
191 NEW_POLY(newpoly
, destobject
);
192 polydefaults(*newpoly
, 0, 0, 0);
193 (*newpoly
)->number
= number
;
194 (*newpoly
)->points
= *points
;
196 calcbboxvalues(locdestinst
, (genericptr
*)newpoly
);
197 updatepagebounds(destobject
);
198 incr_changes(destobject
);
202 /*--------------------------------------------------------------*/
203 /* Spline constructor: Create a new spline element in the */
204 /* object whose instance is "destinst" and return a pointer to */
207 /* "destinst" is the destination instance. If NULL, the */
208 /* top-level instance (areawin->topinstance) is used. */
209 /* "points" is a array of 4 XPoints; should not be NULL. */
211 /* Other properties must be set individually by the calling */
214 /* Return value is a pointer to the newly created spline. */
215 /*--------------------------------------------------------------*/
217 splineptr
new_spline(objinstptr destinst
, pointlist points
)
219 splineptr
*newspline
;
220 objectptr destobject
;
221 objinstptr locdestinst
;
224 locdestinst
= (destinst
== NULL
) ? areawin
->topinstance
: destinst
;
225 destobject
= locdestinst
->thisobject
;
227 NEW_SPLINE(newspline
, destobject
);
228 splinedefaults(*newspline
, 0, 0);
230 for (i
= 0; i
< 4; i
++)
231 (*newspline
)->ctrl
[i
] = points
[i
];
233 calcspline(*newspline
);
234 calcbboxvalues(locdestinst
, (genericptr
*)newspline
);
235 updatepagebounds(destobject
);
236 incr_changes(destobject
);
240 /*--------------------------------------------------------------*/
241 /* Arc constructor: Create a new arc element in the object */
242 /* whose instance is "destinst" and return a pointer to it. */
244 /* "destinst" is the destination instance. If NULL, the */
245 /* top-level instance (areawin->topinstance) is used. */
246 /* "radius" is the radius of the (circular) arc. */
247 /* "x" and "y" represents the arc center position. */
249 /* Other properties must be set individually by the calling */
252 /* Return value is a pointer to the newly created arc. */
253 /*--------------------------------------------------------------*/
255 arcptr
new_arc(objinstptr destinst
, int radius
, int x
, int y
)
258 objectptr destobject
;
259 objinstptr locdestinst
;
261 locdestinst
= (destinst
== NULL
) ? areawin
->topinstance
: destinst
;
262 destobject
= locdestinst
->thisobject
;
264 NEW_ARC(newarc
, destobject
);
265 arcdefaults(*newarc
, x
, y
);
266 (*newarc
)->radius
= (*newarc
)->yaxis
= radius
;
269 calcbboxvalues(locdestinst
, (genericptr
*)newarc
);
270 updatepagebounds(destobject
);
271 incr_changes(destobject
);
275 /*--------------------------------------------------------------*/
276 /* Instance constructor: Create a new object instance element */
277 /* in the object whose instance is "destinst" and return a */
280 /* "destinst" is the destination instance. If NULL, the */
281 /* top-level instance (areawin->topinstance) is used. */
282 /* "srcinst" is the source instance of which this is a */
284 /* "x" and "y" represents the instance position. */
286 /* Other properties must be set individually by the calling */
289 /* Return value is a pointer to the newly created arc. */
290 /*--------------------------------------------------------------*/
292 objinstptr
new_objinst(objinstptr destinst
, objinstptr srcinst
, int x
, int y
)
294 objinstptr
*newobjinst
;
295 objectptr destobject
;
296 objinstptr locdestinst
;
298 locdestinst
= (destinst
== NULL
) ? areawin
->topinstance
: destinst
;
299 destobject
= locdestinst
->thisobject
;
301 NEW_OBJINST(newobjinst
, destobject
);
302 instcopy(*newobjinst
, srcinst
);
303 (*newobjinst
)->position
.x
= x
;
304 (*newobjinst
)->position
.y
= y
;
306 calcbboxvalues(locdestinst
, (genericptr
*)newobjinst
);
307 updatepagebounds(destobject
);
308 incr_changes(destobject
);
312 /*--------------------------------------------------------------*/
313 /* Generic element destructor function */
314 /* (Note---this function is not being used anywhere. . .) */
315 /*--------------------------------------------------------------*/
317 void remove_element(objinstptr destinst
, genericptr genelem
)
319 objectptr destobject
;
320 objinstptr locdestinst
;
322 locdestinst
= (destinst
== NULL
) ? areawin
->topinstance
: destinst
;
323 destobject
= locdestinst
->thisobject
;
325 genelem
->type
&= REMOVE_TAG
;
326 delete_tagged(locdestinst
);
327 calcbboxvalues(locdestinst
, (genericptr
*)NULL
);
328 updatepagebounds(destobject
);
331 /*-------------------------------------*/
332 /* Sane values for a new path instance */
333 /*-------------------------------------*/
335 void pathdefaults(pathptr newpath
, int x
, int y
)
340 newpath
->style
= NORMAL
;
341 newpath
->width
= areawin
->linewidth
;
342 newpath
->style
= areawin
->style
;
343 newpath
->color
= areawin
->color
;
345 newpath
->plist
= (genericptr
*)NULL
;
346 newpath
->passed
= NULL
;
349 /*---------------------------------------*/
350 /* Sane values for a new object instance */
351 /*---------------------------------------*/
353 void instancedefaults(objinstptr newinst
, objectptr thisobj
, int x
, int y
)
355 newinst
->position
.x
= x
;
356 newinst
->position
.y
= y
;
357 newinst
->rotation
= 0.0;
358 newinst
->scale
= 1.0;
359 newinst
->style
= LINE_INVARIANT
;
360 newinst
->thisobject
= thisobj
;
361 newinst
->color
= areawin
->color
;
362 newinst
->params
= NULL
;
363 newinst
->passed
= NULL
;
365 newinst
->bbox
.lowerleft
.x
= thisobj
->bbox
.lowerleft
.x
;
366 newinst
->bbox
.lowerleft
.y
= thisobj
->bbox
.lowerleft
.y
;
367 newinst
->bbox
.width
= thisobj
->bbox
.width
;
368 newinst
->bbox
.height
= thisobj
->bbox
.height
;
370 newinst
->schembbox
= NULL
;
373 /*--------------------------------------*/
374 /* Draw a dot at the current point. */
375 /*--------------------------------------*/
377 void drawdot(int xpos
, int ypos
)
383 /* Find the object "dot" in the builtin library, or else use an arc */
385 if ((dotobj
= finddot()) != (objectptr
)NULL
) {
386 NEW_OBJINST(newdot
, topobject
);
387 instancedefaults(*newdot
, dotobj
, xpos
, ypos
);
388 register_for_undo(XCF_Dot
, UNDO_DONE
, areawin
->topinstance
, *newdot
);
391 NEW_ARC(newarc
, topobject
);
392 arcdefaults(*newarc
, xpos
, ypos
);
393 (*newarc
)->radius
= 6;
394 (*newarc
)->yaxis
= 6;
395 (*newarc
)->width
= 1.0;
396 (*newarc
)->style
= FILLED
| FILLSOLID
| NOBORDER
;
397 (*newarc
)->passed
= NULL
;
398 (*newarc
)->cycle
= NULL
;
400 register_for_undo(XCF_Arc
, UNDO_DONE
, areawin
->topinstance
, *newarc
);
402 incr_changes(topobject
);
405 /*--------------------------------------*/
406 /* Sane default values for a label */
407 /*--------------------------------------*/
409 void labeldefaults(labelptr newlabel
, u_char dopin
, int x
, int y
)
411 newlabel
->rotation
= 0.0;
412 newlabel
->color
= areawin
->color
;
413 newlabel
->scale
= areawin
->textscale
;
414 newlabel
->string
= (stringpart
*)malloc(sizeof(stringpart
));
415 newlabel
->passed
= NULL
;
416 newlabel
->cycle
= NULL
;
418 /* initialize string with font designator */
419 newlabel
->string
->type
= FONT_NAME
;
420 newlabel
->string
->data
.font
= areawin
->psfont
;
421 newlabel
->string
->nextpart
= NULL
;
423 newlabel
->pin
= dopin
;
424 if (dopin
== LOCAL
) newlabel
->color
= LOCALPINCOLOR
;
425 else if (dopin
== GLOBAL
) newlabel
->color
= GLOBALPINCOLOR
;
426 else if (dopin
== INFO
) newlabel
->color
= INFOLABELCOLOR
;
428 newlabel
->anchor
= areawin
->anchor
;
429 newlabel
->position
.x
= x
;
430 newlabel
->position
.y
= y
;
433 /*--------------------------------------*/
434 /* Button handler when creating a label */
435 /*--------------------------------------*/
437 void textbutton(u_char dopin
, int x
, int y
)
441 short tmpheight
, *newselect
;
443 XDefineCursor(dpy
, areawin
->window
, TEXTPTR
);
444 W3printf("Click to end or cancel.");
447 Wprintf("Warning: No fonts available!");
450 NEW_LABEL(newlabel
, topobject
);
451 newselect
= allocselect();
452 *newselect
= topobject
->parts
- 1;
454 labeldefaults(*newlabel
, dopin
, userpt
.x
, userpt
.y
);
456 tmpheight
= (short)(TEXTHEIGHT
* (*newlabel
)->scale
);
457 userpt
.y
-= ((*newlabel
)->anchor
& NOTBOTTOM
) ?
458 (((*newlabel
)->anchor
& TOP
) ? tmpheight
: tmpheight
/ 2) : 0;
459 areawin
->origin
.x
= userpt
.x
;
460 areawin
->origin
.y
= userpt
.y
;
461 areawin
->textpos
= 1; /* Text position is *after* the font declaration */
463 text_mode_draw(xcDRAW_EDIT
, *newlabel
);
466 /*----------------------------------------------------------------------*/
467 /* Report on characters surrounding the current text position */
468 /*----------------------------------------------------------------------*/
472 void charreport(labelptr curlabel
)
474 int i
, locpos
, cleft
= 149;
478 for (i
= areawin
->textpos
- MAXCHARS
; i
<= areawin
->textpos
+ MAXCHARS
- 1; i
++) {
480 strptr
= findstringpart(i
, &locpos
, curlabel
->string
, areawin
->topinstance
);
481 if (i
== areawin
->textpos
) {
482 strncat(_STR2
, "| ", cleft
);
485 if (strptr
== NULL
) break;
486 if (strptr
->type
!= RETURN
|| strptr
->data
.flags
== 0) {
487 charprint(_STR
, strptr
, locpos
);
488 cleft
-= strlen(_STR
);
489 strncat(_STR2
, _STR
, cleft
);
490 strncat(_STR2
, " ", --cleft
);
491 if (cleft
<= 0) break;
494 W3printf("%s", _STR2
);
497 /*----------------------------------------------------------------------*/
498 /* See if a (pin) label has a copy (at least one) in this drawing. */
499 /*----------------------------------------------------------------------*/
501 labelptr
findlabelcopy(labelptr curlabel
, stringpart
*curstring
)
506 for (tgen
= topobject
->plist
; tgen
< topobject
->plist
+ topobject
->parts
; tgen
++) {
507 if (IS_LABEL(*tgen
)) {
508 tlab
= TOLABEL(tgen
);
509 if (tlab
->pin
!= LOCAL
) continue;
510 else if (tlab
== curlabel
) continue; /* Don't count self! */
511 else if (!stringcomp(tlab
->string
, curstring
)) return tlab
;
517 /*--------------------------------------------------------------*/
518 /* Interpret string and add to current label. */
519 /* keypressed is a KeySym */
520 /* clientdata can pass information for label controls */
522 /* Return TRUE if labeltext handled the character, FALSE if the */
523 /* character was not recognized. */
524 /*--------------------------------------------------------------*/
526 Boolean
labeltext(int keypressed
, char *clientdata
)
529 stringpart
*curpos
, *labelbuf
;
531 Boolean r
= True
, do_redraw
= False
;
534 TechPtr oldtech
, newtech
;
536 curlabel
= TOLABEL(EDITPART
);
538 if (curlabel
== NULL
|| curlabel
->type
!= LABEL
) {
539 Wprintf("Error: Bad label string");
540 text_mode_draw(xcDRAW_EMPTY
, curlabel
);
541 eventmode
= NORMAL_MODE
;
545 /* find text segment of the current position */
546 curpos
= findstringpart(areawin
->textpos
, &locpos
, curlabel
->string
,
547 areawin
->topinstance
);
549 if (clientdata
!= NULL
&& keypressed
== TEXT_DELETE
) {
550 if (areawin
->textpos
> 1) {
554 if (areawin
->textend
== 0) areawin
->textend
= areawin
->textpos
- 1;
556 undrawtext(curlabel
);
557 for (strpos
= areawin
->textpos
- 1; strpos
>= areawin
->textend
; strpos
--) {
558 strptr
= findstringpart(strpos
, &curloc
, curlabel
->string
,
559 areawin
->topinstance
);
561 memmove(strptr
->data
.string
+ curloc
,
562 strptr
->data
.string
+ curloc
+ 1,
563 strlen(strptr
->data
.string
+ curloc
+ 1) + 1);
564 if (strlen(strptr
->data
.string
) == 0)
565 deletestring(strptr
, &curlabel
->string
, areawin
->topinstance
);
568 /* Don't delete any parameter boundaries---must use */
569 /* "unparameterize" command for that. */
571 else if (strptr
!= NULL
) {
572 if ((strptr
->type
!= PARAM_START
) && (strptr
->type
!= PARAM_END
))
573 deletestring(strptr
, &curlabel
->string
, areawin
->topinstance
);
578 Fprintf(stdout
, "Error: Unexpected NULL string part\n");
581 areawin
->textend
= 0;
585 else if (clientdata
!= NULL
&& keypressed
== TEXT_DEL_PARAM
) {
586 if (areawin
->textpos
> 1) {
590 strpos
= areawin
->textpos
- 1;
591 strptr
= findstringpart(strpos
, &curloc
, curlabel
->string
,
592 areawin
->topinstance
);
593 if (curloc
> 0) strpos
-= curloc
; /* move to beginning of string */
594 if ((strptr
!= NULL
) && (strptr
->type
!= PARAM_START
) && (strpos
> 0)) {
596 strptr
= findstringpart(strpos
--, &curloc
, curlabel
->string
,
597 areawin
->topinstance
);
599 if ((strptr
!= NULL
) && (strptr
->type
== PARAM_START
)) {
601 undrawtext(curlabel
);
602 unmakeparam(curlabel
, areawin
->topinstance
, strptr
);
608 else if (clientdata
!= NULL
&& keypressed
== TEXT_RETURN
) {
609 Boolean hasstuff
= False
; /* Check for null string */
612 for (tmppos
= curlabel
->string
; tmppos
!= NULL
; tmppos
= tmppos
->nextpart
) {
613 if (tmppos
->type
== PARAM_START
) hasstuff
= True
;
614 else if (tmppos
->type
== TEXT_STRING
) hasstuff
= True
;
616 XDefineCursor(dpy
, areawin
->window
, DEFAULTCURSOR
);
620 if (hasstuff
&& (eventmode
!= ETEXT_MODE
&& eventmode
!= CATTEXT_MODE
)) {
621 register_for_undo(XCF_Text
, UNDO_MORE
, areawin
->topinstance
,
624 incr_changes(topobject
);
625 invalidate_netlist(topobject
);
628 else if (!hasstuff
&& (eventmode
== ETEXT_MODE
)) {
629 if (*(areawin
->selectlist
) < topobject
->parts
) {
630 /* Force the "delete" undo record to be a continuation of */
631 /* the undo series containing the edit. That way, "undo" */
632 /* does not generate a label with null text. */
634 xobjs
.undostack
->idx
= -xobjs
.undostack
->idx
;
635 standard_element_delete(NORMAL
);
638 /* Label had just been created; just delete it w/o undo */
639 freelabel(curlabel
->string
);
646 if ((!hasstuff
) && (eventmode
== CATTEXT_MODE
)) { /* can't have null labels! */
648 redrawtext(curlabel
);
649 areawin
->redraw_needed
= False
; /* ignore previous redraw requests */
650 text_mode_draw(xcDRAW_FINAL
, curlabel
);
651 Wprintf("Object must have a name!");
652 eventmode
= CATALOG_MODE
;
654 else if (!hasstuff
) {
655 areawin
->redraw_needed
= False
; /* ignore previous redraw requests */
656 text_mode_draw(xcDRAW_FINAL
, curlabel
);
657 eventmode
= NORMAL_MODE
;
659 else if (eventmode
== CATTEXT_MODE
) {
664 /* Get the library object whose name matches the original string */
665 oldname
= get_original_string(curlabel
);
666 if ((libobj
= NameToObject(oldname
->nextpart
->data
.string
, NULL
, FALSE
))
669 /* Set name of object to new string. Don't overwrite the */
670 /* object's technology *unless* the new string has a */
671 /* namespace, in which case the object's technology gets */
674 char *techptr
, *libobjname
= libobj
->name
;
675 if ((techptr
= strstr(libobjname
, "::")) != NULL
&&
676 (strstr(curlabel
->string
->nextpart
->data
.string
, "::")
678 libobjname
= techptr
+ 2;
680 /* Save a pointer to the old technology before we overwrite the name */
681 oldtech
= GetObjectTechnology(libobj
);
683 strcpy(libobjname
, curlabel
->string
->nextpart
->data
.string
);
685 /* If checkname() alters the name, it has to be copied back to */
686 /* the catalog label for the object. */
688 if (checkname(libobj
)) {
689 undrawtext(curlabel
);
690 curlabel
->string
->nextpart
->data
.string
= (char *)realloc(
691 curlabel
->string
->nextpart
->data
.string
,
692 (strlen(libobj
->name
) + 1) * sizeof(char));
693 strcpy(curlabel
->string
->nextpart
->data
.string
, libobj
->name
);
694 redrawtext(curlabel
);
696 AddObjectTechnology(libobj
);
698 /* If the technology name has changed, then both the old */
699 /* technology and the new technology need to be marked as */
700 /* having been modified. */
702 newtech
= GetObjectTechnology(libobj
);
704 if (oldtech
!= newtech
) {
705 if (oldtech
) oldtech
->flags
|= (u_char
)TECH_CHANGED
;
706 if (newtech
) newtech
->flags
|= (u_char
)TECH_CHANGED
;
710 /* Check if we altered a page name */
711 else if ((libobj
= NameToPageObject(oldname
->nextpart
->data
.string
,
712 NULL
, &page
)) != NULL
) {
713 strcpy(libobj
->name
, curlabel
->string
->nextpart
->data
.string
);
717 /* Check if we altered a library name */
718 else if ((libnum
= NameToLibrary(oldname
->nextpart
->data
.string
)) != -1) {
719 libobj
= xobjs
.libtop
[libnum
+ LIBRARY
]->thisobject
;
720 strcpy(libobj
->name
, curlabel
->string
->nextpart
->data
.string
);
723 Wprintf("Error: Cannot match name to any object, page, or library!");
724 refresh(NULL
, NULL
, NULL
);
726 areawin
->redraw_needed
= False
; /* ignore previous redraw requests */
727 text_mode_draw(xcDRAW_FINAL
, curlabel
);
729 eventmode
= CATALOG_MODE
;
731 else { /* (hasstuff && eventmode != CATTEXT_MODE) */
732 areawin
->redraw_needed
= False
; /* ignore previous redraw requests */
733 text_mode_draw(xcDRAW_FINAL
, curlabel
);
734 eventmode
= NORMAL_MODE
;
735 incr_changes(topobject
);
736 if (curlabel
->pin
!= False
) invalidate_netlist(topobject
);
739 setdefaultfontmarks();
740 setcolormark(areawin
->color
);
743 /* Delete empty labels */
744 standard_element_delete(NORMAL
);
746 else if ((labelbuf
= get_original_string(curlabel
)) != NULL
) {
748 /* If the original label (before modification) is a pin in a */
749 /* schematic/symbol with a matching symbol/schematic, and the */
750 /* name is unique, change every pin in the matching symbol/ */
751 /* schematic to match the new text. */
753 if ((curlabel
->pin
== LOCAL
) && (topobject
->symschem
!= NULL
) &&
754 (topobject
->symschem
->schemtype
!= PRIMARY
)) {
755 if ((findlabelcopy(curlabel
, labelbuf
) == NULL
)
756 && (findlabelcopy(curlabel
, curlabel
->string
) == NULL
)) {
757 if (changeotherpins(curlabel
, labelbuf
) > 0) {
758 if (topobject
->schemtype
== PRIMARY
||
759 topobject
->schemtype
== SECONDARY
)
760 Wprintf("Changed corresponding pin in associated symbol");
762 Wprintf("Changed corresponding pin in associated schematic");
763 incr_changes(topobject
->symschem
);
764 invalidate_netlist(topobject
->symschem
);
769 resolveparams(areawin
->topinstance
);
770 updateinstparam(topobject
);
771 setobjecttype(topobject
);
774 calcbbox(areawin
->topinstance
);
779 else if (clientdata
!= NULL
&& keypressed
== TEXT_RIGHT
) {
780 if (curpos
!= NULL
) areawin
->textpos
++;
782 else if (clientdata
!= NULL
&& keypressed
== TEXT_LEFT
) {
783 if (areawin
->textpos
> 1) areawin
->textpos
--;
785 else if (clientdata
!= NULL
&& keypressed
== TEXT_DOWN
) {
786 while (curpos
!= NULL
) {
788 curpos
= findstringpart(areawin
->textpos
, &locpos
, curlabel
->string
,
789 areawin
->topinstance
);
791 if (curpos
->type
== RETURN
|| curpos
->type
== MARGINSTOP
)
795 else if (clientdata
!= NULL
&& keypressed
== TEXT_UP
) {
796 while (areawin
->textpos
> 1) {
798 curpos
= findstringpart(areawin
->textpos
, &locpos
, curlabel
->string
,
799 areawin
->topinstance
);
800 if (curpos
->type
== RETURN
|| curpos
->type
== MARGINSTOP
) {
801 if (areawin
->textpos
> 1) areawin
->textpos
--;
806 else if (clientdata
!= NULL
&& keypressed
== TEXT_HOME
)
807 areawin
->textpos
= 1;
808 else if (clientdata
!= NULL
&& keypressed
== TEXT_END
)
809 areawin
->textpos
= stringlength(curlabel
->string
, True
, areawin
->topinstance
);
810 else if (clientdata
!= NULL
&& keypressed
== TEXT_SPLIT
) {
812 XPoint points
[4], points1
[4], points2
[4];
814 /* Everything after the cursor gets dumped into a new label */
816 if ((areawin
->textpos
> 1) && (curpos
!= NULL
)) {
817 labelbbox(curlabel
, points
, areawin
->topinstance
);
818 undrawtext(curlabel
);
819 NEW_LABEL(newlabel
, topobject
);
820 labeldefaults(*newlabel
, curlabel
->pin
, curlabel
->position
.x
,
821 curlabel
->position
.y
);
823 curpos
= splitstring(areawin
->textpos
, &curlabel
->string
,
824 areawin
->topinstance
);
825 /* move back one position to find end of top part of string */
826 curpos
= splitstring(areawin
->textpos
- 1, &curlabel
->string
,
827 areawin
->topinstance
);
828 if (curpos
->nextpart
->type
== FONT_NAME
) {
829 freelabel((*newlabel
)->string
);
830 (*newlabel
)->string
= curpos
->nextpart
;
833 (*newlabel
)->string
->data
.font
= curlabel
->string
->data
.font
;
834 (*newlabel
)->string
->nextpart
= curpos
->nextpart
;
836 curpos
->nextpart
= NULL
;
838 /* Adjust position of both labels to retain their original */
839 /* relative positions. */
841 labelbbox(curlabel
, points1
, areawin
->topinstance
);
842 labelbbox((*newlabel
), points2
, areawin
->topinstance
);
843 curlabel
->position
.x
+= (points
[1].x
- points1
[1].x
);
844 curlabel
->position
.y
+= (points
[1].y
- points1
[1].y
);
845 (*newlabel
)->position
.x
+= (points
[3].x
- points2
[3].x
);
846 (*newlabel
)->position
.y
+= (points
[3].y
- points2
[3].y
);
848 redrawtext(*newlabel
);
853 /* Write a font designator or other control into the string */
855 else if (clientdata
!= NULL
) {
858 Boolean errcond
= False
;
859 TextLinesInfo tlinfo
;
861 /* erase first before redrawing unless the string is empty */
862 undrawtext(curlabel
);
864 /* Get text width first. Don't back up over spaces; this */
865 /* allows the margin width to be padded out with spaces. */
867 if (keypressed
== MARGINSTOP
) {
868 tlinfo
.dostop
= areawin
->textpos
;
869 tlinfo
.tbreak
= NULL
;
870 tlinfo
.padding
= NULL
;
871 tmpext
= ULength(curlabel
, areawin
->topinstance
, &tlinfo
);
872 if (tlinfo
.padding
!= NULL
) free(tlinfo
.padding
);
876 if (keypressed
== MARGINSTOP
) {
877 /* Move forward by any spaces; if we're at the text */
878 /* end, move to the next text part; otherwise, */
879 /* split the string. */
881 while (*(curpos
->data
.string
+ locpos
) == ' ') locpos
++;
882 if (*(curpos
->data
.string
+ locpos
) == '\0') locpos
= 0;
885 curpos
= splitstring(areawin
->textpos
, &curlabel
->string
,
886 areawin
->topinstance
);
887 curpos
= curpos
->nextpart
;
889 newpart
= makesegment(&curlabel
->string
, curpos
);
890 newpart
->type
= keypressed
;
891 switch (keypressed
) {
893 /* Identify this as an explicitly placed line break */
894 newpart
->data
.flags
= 0;
897 newpart
->data
.scale
= *((float *)clientdata
);
900 newpart
->data
.kern
[0] = *((short *)clientdata
);
901 newpart
->data
.kern
[1] = *((short *)clientdata
+ 1);
904 newpart
->data
.color
= *((int *)clientdata
);
905 if (newpart
->data
.color
>= number_colors
) errcond
= True
;
908 newpart
->data
.font
= *((int *)clientdata
);
909 if (newpart
->data
.font
>= fontcount
) errcond
= True
;
912 /* A margin of 1 or 0 is useless, so such a value */
913 /* indicates to take the margin from the current */
916 if (*((int *)clientdata
) <= 1)
917 newpart
->data
.width
= (int)tmpext
.width
;
919 newpart
->data
.width
= *((int *)clientdata
);
920 CheckMarginStop(curlabel
, areawin
->topinstance
, FALSE
);
923 newpart
->data
.string
= (char *)malloc(1 + strlen(clientdata
));
924 strcpy(newpart
->data
.string
, clientdata
);
925 ops
= match_param(topobject
, clientdata
);
926 if (ops
== NULL
) errcond
= True
;
928 /* Forward edit cursor position to the end of the parameter */
931 curpos
= findstringpart(areawin
->textpos
, &locpos
, curlabel
->string
,
932 areawin
->topinstance
);
933 } while (curpos
&& (curpos
->type
!= PARAM_END
));
937 if (errcond
== True
) {
938 Wprintf("Error in insertion. Ignoring.");
939 deletestring(newpart
, &curlabel
->string
, areawin
->topinstance
);
948 /* Append the character to the string. If the current label segment is */
949 /* not text, then create a text segment for it. */
951 else if (keypressed
> 0 && keypressed
< 256) {
955 undrawtext(curlabel
);
957 /* Current position is not in a text string */
960 /* Find part of string which is immediately in front of areawin->textpos */
961 lastpos
= findstringpart(areawin
->textpos
- 1, &locpos
, curlabel
->string
,
962 areawin
->topinstance
);
964 /* No text on either side to attach to: make a new text segment */
966 curpos
= makesegment(&curlabel
->string
, curpos
);
967 curpos
->type
= TEXT_STRING
;
968 curpos
->data
.string
= (u_char
*) malloc(2);
969 curpos
->data
.string
[0] = keypressed
;
970 curpos
->data
.string
[1] = '\0';
972 else { /* append to end of lastpos text string */
973 int slen
= strlen(lastpos
->data
.string
);
974 lastpos
->data
.string
= (u_char
*) realloc(lastpos
->data
.string
,
976 *(lastpos
->data
.string
+ slen
) = keypressed
;
977 *(lastpos
->data
.string
+ slen
+ 1) = '\0';
980 else { /* prepend to end of curpos text string */
981 curpos
->data
.string
= (u_char
*) realloc(curpos
->data
.string
,
982 2 + strlen(curpos
->data
.string
));
983 memmove(curpos
->data
.string
+ locpos
+ 1, curpos
->data
.string
+ locpos
,
984 strlen(curpos
->data
.string
+ locpos
) + 1);
985 *(curpos
->data
.string
+ locpos
) = keypressed
;
987 areawin
->textpos
++; /* move forward to next text position */
992 /* Redraw the label */
995 /* Generate automatic line breaks if there is a MARGINSTOP directive */
996 CheckMarginStop(curlabel
, areawin
->topinstance
, TRUE
);
998 redrawtext(curlabel
);
1001 areawin
->redraw_needed
= False
; /* ignore previous full redraw requests */
1002 text_mode_draw(xcDRAW_EDIT
, curlabel
);
1004 if (r
|| do_redraw
) {
1006 /* Report on characters at the cursor position in the message window */
1008 charreport(curlabel
);
1010 /* find current font and adjust menubuttons as necessary */
1012 cfont
= findcurfont(areawin
->textpos
, curlabel
->string
, areawin
->topinstance
);
1014 Wprintf("Error: Illegal label string");
1018 setfontmarks(cfont
, -1);
1020 areawin
->textend
= 0;
1025 /*-------------------------------------*/
1026 /* Initiate return from text edit mode */
1027 /*-------------------------------------*/
1031 labeltext(TEXT_RETURN
, (char *)1);
1034 /*--------------------------------------*/
1035 /* Change the anchoring of a label */
1036 /*--------------------------------------*/
1038 void reanchor(short mode
)
1040 labelptr curlabel
= NULL
;
1043 Boolean preselected
= False
, changed
= False
;
1044 static short transanchor
[] = {15, 13, 12, 7, 5, 4, 3, 1, 0};
1046 if (eventmode
== TEXT_MODE
|| eventmode
== ETEXT_MODE
) {
1047 curlabel
= TOLABEL(EDITPART
);
1048 UDrawTLine(curlabel
);
1049 undrawtext(curlabel
);
1050 jsave
= curlabel
->anchor
;
1051 curlabel
->anchor
= transanchor
[mode
] |
1052 (curlabel
->anchor
& NONANCHORFIELD
);
1053 if (jsave
!= curlabel
->anchor
) {
1054 register_for_undo(XCF_Anchor
, UNDO_MORE
, areawin
->topinstance
,
1055 (genericptr
)curlabel
, (int)jsave
);
1058 redrawtext(curlabel
);
1059 UDrawTLine(curlabel
);
1061 setfontmarks(-1, curlabel
->anchor
);
1064 if (areawin
->selects
== 0) {
1065 if (!checkselect(LABEL
))
1068 else preselected
= TRUE
;
1070 for (tsel
= areawin
->selectlist
; tsel
< areawin
->selectlist
+
1071 areawin
->selects
; tsel
++) {
1072 if (SELECTTYPE(tsel
) == LABEL
) {
1073 curlabel
= SELTOLABEL(tsel
);
1074 jsave
= curlabel
->anchor
;
1075 undrawtext(curlabel
);
1076 curlabel
->anchor
= transanchor
[mode
] |
1077 (curlabel
->anchor
& NONANCHORFIELD
);
1078 if (jsave
!= curlabel
->anchor
) {
1079 register_for_undo(XCF_Anchor
, UNDO_MORE
, areawin
->topinstance
,
1080 (genericptr
)curlabel
, (int)jsave
);
1085 if (preselected
== FALSE
&& eventmode
!= MOVE_MODE
&& eventmode
!= COPY_MODE
)
1088 draw_all_selected();
1090 if (curlabel
== NULL
)
1091 Wprintf("No labels chosen to reanchor");
1093 pwriteback(areawin
->topinstance
);
1094 calcbbox(areawin
->topinstance
);
1095 incr_changes(topobject
);
1099 /*----------------------------------*/
1100 /* Sane default values for a spline */
1101 /*----------------------------------*/
1103 void splinedefaults(splineptr newspline
, int x
, int y
)
1107 for (j
= 0; j
< 4; j
++) {
1108 newspline
->ctrl
[j
].x
= x
;
1109 newspline
->ctrl
[j
].y
= y
;
1111 newspline
->ctrl
[1].x
+= (int)(xobjs
.pagelist
[areawin
->page
]->gridspace
/ 2);
1112 newspline
->ctrl
[2].x
-= (int)(xobjs
.pagelist
[areawin
->page
]->gridspace
/ 2);
1113 newspline
->width
= areawin
->linewidth
;
1114 newspline
->style
= areawin
->style
;
1115 newspline
->color
= areawin
->color
;
1116 newspline
->passed
= NULL
;
1117 newspline
->cycle
= NULL
;
1118 calcspline(newspline
);
1121 /*-------------------------*/
1122 /* Start drawing a spline. */
1123 /*-------------------------*/
1125 void splinebutton(int x
, int y
)
1127 splineptr
*newspline
;
1132 NEW_SPLINE(newspline
, topobject
);
1133 newselect
= allocselect();
1134 *newselect
= topobject
->parts
- 1;
1136 snap(x
, y
, &userpt
);
1137 splinedefaults(*newspline
, userpt
.x
, userpt
.y
);
1138 addcycle((genericptr
*)newspline
, 3, 0);
1139 makerefcycle((*newspline
)->cycle
, 3);
1141 spline_mode_draw(xcDRAW_EDIT
, *newspline
);
1143 xcAddEventHandler(areawin
->area
, PointerMotionMask
, False
,
1144 (xcEventHandler
)trackelement
, NULL
);
1146 eventmode
= SPLINE_MODE
;
1149 /*----------------------------------------------------------------------*/
1150 /* Generate cycles on a path where endpoints meet, so that the path */
1151 /* remains connected during an edit. If the last point on any part */
1152 /* of the path is a cycle, then the first point on the next part of */
1153 /* the path should also be a cycle, with the same flags. */
1155 /* If the global setting "tangents" is set, then the control points of */
1156 /* connecting splines set the corresponding control point to "ANTIXY" */
1157 /* so that the control points track angle and distance from the */
1159 /*----------------------------------------------------------------------*/
1161 void updatepath(pathptr thepath
)
1163 genericptr
*ggen
, *ngen
;
1164 short locparts
, cycle
;
1167 splineptr thisspline
;
1169 for (ggen
= thepath
->plist
; ggen
< thepath
->plist
+ thepath
->parts
; ggen
++) {
1170 switch (ELEMENTTYPE(*ggen
)) {
1172 findconstrained(TOPOLY(ggen
));
1177 locparts
= (thepath
->style
& UNCLOSED
) ? thepath
->parts
- 1 : thepath
->parts
;
1178 for (ggen
= thepath
->plist
; ggen
< thepath
->plist
+ locparts
; ggen
++) {
1179 ngen
= (ggen
== thepath
->plist
+ thepath
->parts
- 1) ? thepath
->plist
: ggen
+ 1;
1181 switch (ELEMENTTYPE(*ggen
)) {
1183 thispoly
= TOPOLY(ggen
);
1184 if (thispoly
->cycle
== NULL
) continue;
1185 cycle
= thispoly
->number
- 1;
1186 for (cptr
= thispoly
->cycle
;; cptr
++) {
1187 if (cptr
->number
== cycle
) break;
1188 if (cptr
->flags
& LASTENTRY
) break;
1190 if (cptr
->number
!= cycle
) continue;
1193 thisspline
= TOSPLINE(ggen
);
1194 if (thisspline
->cycle
== NULL
) continue;
1196 for (cptr
= thisspline
->cycle
;; cptr
++) {
1197 if (cptr
->number
== cycle
) break;
1198 if (cptr
->flags
& LASTENTRY
) break;
1200 if (cptr
->number
!= cycle
) continue;
1203 addcycle(ngen
, 0, cptr
->flags
& (EDITX
| EDITY
));
1204 switch (ELEMENTTYPE(*ngen
)) {
1206 findconstrained(TOPOLY(ngen
));
1211 /* Do the same thing in the other direction */
1212 locparts
= (thepath
->style
& UNCLOSED
) ? 1 : 0;
1213 for (ggen
= thepath
->plist
+ thepath
->parts
- 1; ggen
>= thepath
->plist
+ locparts
;
1215 ngen
= (ggen
== thepath
->plist
) ? thepath
->plist
+ thepath
->parts
- 1 : ggen
- 1;
1217 switch (ELEMENTTYPE(*ggen
)) {
1219 thispoly
= TOPOLY(ggen
);
1220 if (thispoly
->cycle
== NULL
) continue;
1222 for (cptr
= thispoly
->cycle
;; cptr
++) {
1223 if (cptr
->number
== cycle
) break;
1224 if (cptr
->flags
& LASTENTRY
) break;
1226 if (cptr
->number
!= cycle
) continue;
1229 thisspline
= TOSPLINE(ggen
);
1230 if (thisspline
->cycle
== NULL
) continue;
1232 for (cptr
= thisspline
->cycle
;; cptr
++) {
1233 if (cptr
->number
== cycle
) break;
1234 if (cptr
->flags
& LASTENTRY
) break;
1236 if (cptr
->number
!= cycle
) continue;
1239 switch (ELEMENTTYPE(*ngen
)) {
1241 addcycle(ngen
, TOPOLY(ngen
)->number
- 1, cptr
->flags
& (EDITX
| EDITY
));
1244 addcycle(ngen
, 3, cptr
->flags
& (EDITX
| EDITY
));
1250 /*--------------------------------------*/
1251 /* Set default values for an arc */
1252 /*--------------------------------------*/
1254 void arcdefaults(arcptr newarc
, int x
, int y
)
1256 newarc
->style
= areawin
->style
;
1257 newarc
->color
= areawin
->color
;
1258 newarc
->position
.x
= x
;
1259 newarc
->position
.y
= y
;
1260 newarc
->width
= areawin
->linewidth
;
1264 newarc
->angle2
= 360;
1265 newarc
->passed
= NULL
;
1266 newarc
->cycle
= NULL
;
1270 /*-------------------------------------*/
1271 /* Button handler when creating an arc */
1272 /*-------------------------------------*/
1274 void arcbutton(int x
, int y
)
1281 NEW_ARC(newarc
, topobject
);
1282 newselect
= allocselect();
1283 *newselect
= topobject
->parts
- 1;
1284 snap(x
, y
, &userpt
);
1286 arcdefaults(*newarc
, userpt
.x
, userpt
.y
);
1287 addcycle((genericptr
*)newarc
, 0, 0);
1289 arc_mode_draw(xcDRAW_EDIT
, *newarc
);
1291 xcAddEventHandler(areawin
->area
, PointerMotionMask
, False
,
1292 (xcEventHandler
)trackarc
, NULL
);
1294 eventmode
= ARC_MODE
;
1297 /*----------------------------------*/
1298 /* Track an arc during mouse motion */
1299 /*----------------------------------*/
1301 void trackarc(xcWidget w
, caddr_t clientdata
, caddr_t calldata
)
1307 UNUSED(w
); UNUSED(clientdata
); UNUSED(calldata
);
1309 newarc
= TOARC(EDITPART
);
1311 newpos
= UGetCursorPos();
1313 if (areawin
->save
.x
== newpos
.x
&& areawin
->save
.y
== newpos
.y
) return;
1315 cycle
= (newarc
->cycle
== NULL
) ? -1 : newarc
->cycle
->number
;
1316 if (cycle
== 1 || cycle
== 2) {
1317 float *angleptr
, tmpang
;
1319 adjrat
= (newarc
->yaxis
== 0) ? 1 :
1320 (double)(abs(newarc
->radius
)) / (double)newarc
->yaxis
;
1321 angleptr
= (cycle
== 1) ? &newarc
->angle1
: &newarc
->angle2
;
1322 tmpang
= (float)(atan2((double)(newpos
.y
- newarc
->position
.y
) * adjrat
,
1323 (double)(newpos
.x
- newarc
->position
.x
)) / RADFAC
);
1325 if (tmpang
> newarc
->angle2
) tmpang
-= 360;
1326 else if (newarc
->angle2
- tmpang
> 360) newarc
->angle2
-= 360;
1329 if (tmpang
< newarc
->angle1
) tmpang
+= 360;
1330 else if (tmpang
- newarc
->angle1
> 360) newarc
->angle1
+= 360;
1334 if (newarc
->angle2
<= 0) {
1335 newarc
->angle2
+= 360;
1336 newarc
->angle1
+= 360;
1338 if (newarc
->angle2
<= newarc
->angle1
)
1339 newarc
->angle1
-= 360;
1341 else if (cycle
== 0) {
1342 short direc
= (newarc
->radius
< 0);
1343 newarc
->radius
= wirelength(&newpos
, &(newarc
->position
));
1344 newarc
->yaxis
= (short)((double)newarc
->radius
* saveratio
);
1345 if (direc
) newarc
->radius
= -newarc
->radius
;
1348 newarc
->yaxis
= wirelength(&newpos
, &(newarc
->position
));
1349 saveratio
= (double)newarc
->yaxis
/ (double)newarc
->radius
;
1354 areawin
->save
.x
= newpos
.x
;
1355 areawin
->save
.y
= newpos
.y
;
1357 arc_mode_draw(xcDRAW_EDIT
, newarc
);
1358 printpos(newpos
.x
, newpos
.y
);
1363 /*--------------------------------------*/
1364 /* Sane default values for a polygon */
1365 /*--------------------------------------*/
1367 void polydefaults(polyptr newpoly
, int number
, int x
, int y
)
1371 newpoly
->style
= areawin
->style
& ~UNCLOSED
;
1372 newpoly
->color
= areawin
->color
;
1373 newpoly
->width
= areawin
->linewidth
;
1374 newpoly
->number
= number
;
1375 newpoly
->passed
= NULL
;
1376 newpoly
->cycle
= NULL
;
1378 newpoly
->points
= NULL
;
1380 newpoly
->points
= (pointlist
) malloc(number
* sizeof(XPoint
));
1382 for (pointptr
= newpoly
->points
; pointptr
< newpoly
->points
+ number
;
1390 /*------------------------------------*/
1391 /* Button handler when creating a box */
1392 /*------------------------------------*/
1394 void boxbutton(int x
, int y
)
1401 NEW_POLY(newbox
, topobject
);
1402 newselect
= allocselect();
1403 *newselect
= topobject
->parts
- 1;
1404 snap(x
, y
, &userpt
);
1405 polydefaults(*newbox
, 4, userpt
.x
, userpt
.y
);
1407 poly_mode_draw(xcDRAW_EDIT
, *newbox
);
1409 xcAddEventHandler(areawin
->area
, PointerMotionMask
, False
,
1410 (xcEventHandler
)trackbox
, NULL
);
1412 eventmode
= BOX_MODE
;
1415 /*---------------------------------*/
1416 /* Track a box during mouse motion */
1417 /*---------------------------------*/
1419 void trackbox(xcWidget w
, caddr_t clientdata
, caddr_t calldata
)
1424 UNUSED(w
); UNUSED(clientdata
); UNUSED(calldata
);
1426 newbox
= TOPOLY(EDITPART
);
1427 newpos
= UGetCursorPos();
1430 if (areawin
->save
.x
== newpos
.x
&& areawin
->save
.y
== newpos
.y
) return;
1432 pointptr
= newbox
->points
+ 1; pointptr
->y
= newpos
.y
;
1433 pointptr
++; pointptr
->y
= newpos
.y
; pointptr
->x
= newpos
.x
;
1434 pointptr
++; pointptr
->x
= newpos
.x
;
1436 poly_mode_draw(xcDRAW_EDIT
, newbox
);
1437 printpos(newpos
.x
, newpos
.y
);
1439 areawin
->save
.x
= newpos
.x
;
1440 areawin
->save
.y
= newpos
.y
;
1445 /*----------------------------------------------------------------------*/
1446 /* Track a wire during mouse motion */
1447 /* Note: The manhattanize algorithm will change the effective cursor */
1448 /* position to keep the wire manhattan if the wire is only 1 segment. */
1449 /* It will change the previous point's position if the wire has more */
1450 /* than one segment. They are called at different times to ensure the */
1451 /* wire redraw is correct. */
1452 /*----------------------------------------------------------------------*/
1454 void trackwire(xcWidget w
, caddr_t clientdata
, caddr_t calldata
)
1456 XPoint newpos
, upos
, *tpoint
;
1458 UNUSED(w
); UNUSED(clientdata
); UNUSED(calldata
);
1460 newwire
= TOPOLY(EDITPART
);
1462 if (areawin
->attachto
>= 0) {
1463 upos
= UGetCursorPos();
1464 findattach(&newpos
, NULL
, &upos
);
1467 newpos
= UGetCursorPos();
1469 if (areawin
->manhatn
&& (newwire
->number
== 2))
1470 manhattanize(&newpos
, newwire
, -1, TRUE
);
1473 if (areawin
->save
.x
!= newpos
.x
|| areawin
->save
.y
!= newpos
.y
) {
1474 tpoint
= newwire
->points
+ newwire
->number
- 1;
1475 if (areawin
->manhatn
&& (newwire
->number
> 2))
1476 manhattanize(&newpos
, newwire
, -1, TRUE
);
1477 tpoint
->x
= newpos
.x
;
1478 tpoint
->y
= newpos
.y
;
1479 XcTopSetForeground(newwire
->color
);
1480 poly_mode_draw(xcDRAW_EDIT
, newwire
);
1481 areawin
->save
.x
= newpos
.x
;
1482 areawin
->save
.y
= newpos
.y
;
1483 printpos(newpos
.x
, newpos
.y
);
1489 /*--------------------------*/
1490 /* Start drawing a polygon. */
1491 /*--------------------------*/
1493 void startwire(XPoint
*userpt
)
1500 NEW_POLY(newwire
, topobject
);
1501 newselect
= allocselect();
1502 *newselect
= topobject
->parts
- 1;
1504 /* always start unfilled, unclosed; can fix on next button-push. */
1506 (*newwire
)->style
= UNCLOSED
| (areawin
->style
& (DASHED
| DOTTED
));
1507 (*newwire
)->color
= areawin
->color
;
1508 (*newwire
)->number
= 2;
1509 (*newwire
)->width
= areawin
->linewidth
;
1510 (*newwire
)->points
= (pointlist
) malloc(2 * sizeof(XPoint
));
1511 (*newwire
)->passed
= NULL
;
1512 (*newwire
)->cycle
= NULL
;
1513 pointptr
= (*newwire
)->points
;
1514 pointptr
->x
= (pointptr
+ 1)->x
= areawin
->save
.x
= userpt
->x
;
1515 pointptr
->y
= (pointptr
+ 1)->y
= areawin
->save
.y
= userpt
->y
;
1517 poly_mode_draw(xcDRAW_EDIT
, *newwire
);
1519 xcAddEventHandler(areawin
->area
, PointerMotionMask
, False
,
1520 (xcEventHandler
)trackwire
, NULL
);
1523 /*--------------------------------------------------------------*/
1524 /* Find which points should track along with the edit point in */
1525 /* in polygon RHOMBOID or MANHATTAN edit modes. */
1526 /* (point number is stored in lastpoly->cycle) */
1528 /* NOTE: This routine assumes that either the points have just */
1529 /* been selected, or that advancecycle() has been called to */
1530 /* remove all previously recorded tracking points. */
1531 /*--------------------------------------------------------------*/
1533 void findconstrained(polyptr lastpoly
)
1535 XPoint
*savept
, *npt
, *lpt
;
1537 short lflags
, nflags
;
1539 pointselect
*cptr
, *nptr
;
1541 if (areawin
->boxedit
== NORMAL
) return;
1543 if (lastpoly
->cycle
== NULL
) return;
1545 /* Set "process" flags on all original points */
1546 for (cptr
= lastpoly
->cycle
;; cptr
++) {
1547 cptr
->flags
|= PROCESS
;
1548 if (cptr
->flags
& LASTENTRY
) break;
1551 cptr
= lastpoly
->cycle
;
1553 if (cptr
->flags
& PROCESS
) {
1554 cptr
->flags
&= ~PROCESS
;
1555 cycle
= cptr
->number
;
1556 savept
= lastpoly
->points
+ cycle
;
1558 /* find points before and after the edit point */
1560 lcyc
= (cycle
== 0) ? ((lastpoly
->style
& UNCLOSED
) ?
1561 -1 : lastpoly
->number
- 1) : cycle
- 1;
1562 ncyc
= (cycle
== lastpoly
->number
- 1) ?
1563 ((lastpoly
->style
& UNCLOSED
) ? -1 : 0) : cycle
+ 1;
1565 lpt
= (lcyc
== -1) ? NULL
: lastpoly
->points
+ lcyc
;
1566 npt
= (ncyc
== -1) ? NULL
: lastpoly
->points
+ ncyc
;
1568 lflags
= nflags
= NONE
;
1570 /* two-point polygons (lines) are a degenerate case in RHOMBOID edit mode */
1572 if (areawin
->boxedit
!= MANHATTAN
&& lastpoly
->number
<= 2) return;
1574 /* This is complicated but logical: in MANHATTAN mode, boxes maintain */
1575 /* box shape. In RHOMBOID modes, parallelagrams maintain shape. The */
1576 /* "savedir" variable determines which coordinate(s) of which point(s) */
1577 /* should track along with the edit point. */
1579 if (areawin
->boxedit
!= RHOMBOIDY
) {
1581 if (lpt
->y
== savept
->y
) {
1583 if (areawin
->boxedit
== RHOMBOIDX
&& lpt
->x
!= savept
->x
)
1585 else if (areawin
->boxedit
== RHOMBOIDA
&& npt
!= NULL
) {
1586 if (npt
->y
!= savept
->y
) nflags
|= EDITX
;
1591 if (npt
->y
== savept
->y
) {
1593 if (areawin
->boxedit
== RHOMBOIDX
&& npt
->x
!= savept
->x
)
1595 else if (areawin
->boxedit
== RHOMBOIDA
&& lpt
!= NULL
) {
1596 if (lpt
->y
!= savept
->y
) lflags
|= EDITX
;
1601 if (areawin
->boxedit
!= RHOMBOIDX
) {
1603 if (lpt
->x
== savept
->x
) {
1605 if (areawin
->boxedit
== RHOMBOIDY
&& lpt
->y
!= savept
->y
)
1607 else if (areawin
->boxedit
== RHOMBOIDA
&& npt
!= NULL
) {
1608 if (npt
->x
!= savept
->x
) nflags
|= EDITY
;
1613 if (npt
->x
== savept
->x
) {
1615 if (areawin
->boxedit
== RHOMBOIDY
&& npt
->y
!= savept
->y
)
1617 else if (areawin
->boxedit
== RHOMBOIDA
&& lpt
!= NULL
) {
1618 if (lpt
->x
!= savept
->x
) lflags
|= EDITY
;
1624 if (lpt
!= NULL
&& lflags
!= 0) {
1625 addcycle((genericptr
*)(&lastpoly
), lcyc
, lflags
);
1626 cptr
= nptr
= lastpoly
->cycle
;
1628 if (npt
!= NULL
&& nflags
!= 0) {
1629 addcycle((genericptr
*)(&lastpoly
), ncyc
, nflags
);
1630 cptr
= nptr
= lastpoly
->cycle
;
1635 if (cptr
->flags
& LASTENTRY
) break;
1640 /*------------------------------------------------------*/
1641 /* Track movement of arc, spline, or polygon segments */
1642 /* during edit mode */
1643 /*------------------------------------------------------*/
1645 void trackelement(xcWidget w
, caddr_t clientdata
, caddr_t calldata
)
1647 XPoint newpos
, *curpt
;
1651 UNUSED(w
); UNUSED(clientdata
); UNUSED(calldata
);
1653 newpos
= UGetCursorPos();
1656 /* force attachment if required */
1657 if (areawin
->attachto
>= 0) {
1659 findattach(&apos
, NULL
, &newpos
);
1663 if (areawin
->save
.x
== newpos
.x
&& areawin
->save
.y
== newpos
.y
) return;
1665 /* Find the reference point */
1667 cptr
= getrefpoint(TOGENERIC(EDITPART
), &curpt
);
1668 switch(ELEMENTTYPE(TOGENERIC(EDITPART
))) {
1671 curpt
= TOPOLY(EDITPART
)->points
;
1675 curpt
= &(TOSPLINE(EDITPART
)->ctrl
[0]);
1678 curpt
= &(TOARC(EDITPART
)->position
);
1681 curpt
= &(TOOBJINST(EDITPART
)->position
);
1684 curpt
= &(TOGRAPHIC(EDITPART
)->position
);
1687 deltax
= newpos
.x
- curpt
->x
;
1688 deltay
= newpos
.y
- curpt
->y
;
1690 /* Now adjust all edited elements relative to the reference point */
1691 for (selobj
= areawin
->selectlist
; selobj
< areawin
->selectlist
+
1692 areawin
->selects
; selobj
++) {
1693 if (eventmode
== ARC_MODE
|| eventmode
== EARC_MODE
)
1694 editpoints(SELTOGENERICPTR(selobj
), deltax
, deltay
);
1695 else if (eventmode
== SPLINE_MODE
|| eventmode
== ESPLINE_MODE
)
1696 editpoints(SELTOGENERICPTR(selobj
), deltax
, deltay
);
1697 else if (eventmode
== BOX_MODE
|| eventmode
== EPOLY_MODE
1698 || eventmode
== WIRE_MODE
)
1699 editpoints(SELTOGENERICPTR(selobj
), deltax
, deltay
);
1700 else if (eventmode
== EPATH_MODE
)
1701 editpoints(SELTOGENERICPTR(selobj
), deltax
, deltay
);
1704 if (eventmode
== ARC_MODE
|| eventmode
== EARC_MODE
)
1705 arc_mode_draw(xcDRAW_EDIT
, TOARC(EDITPART
));
1706 else if (eventmode
== SPLINE_MODE
|| eventmode
== ESPLINE_MODE
)
1707 spline_mode_draw(xcDRAW_EDIT
, TOSPLINE(EDITPART
));
1708 else if (eventmode
== BOX_MODE
|| eventmode
== EPOLY_MODE
1709 || eventmode
== WIRE_MODE
)
1710 poly_mode_draw(xcDRAW_EDIT
, TOPOLY(EDITPART
));
1711 else if (eventmode
== EPATH_MODE
)
1712 path_mode_draw(xcDRAW_EDIT
, TOPATH(EDITPART
));
1714 printpos(newpos
.x
, newpos
.y
);
1715 areawin
->save
.x
= newpos
.x
;
1716 areawin
->save
.y
= newpos
.y
;
1721 /*-------------------------------------------------*/
1722 /* Determine values of endpoints of an element */
1723 /*-------------------------------------------------*/
1725 void setendpoint(short *scnt
, short direc
, XPoint
**endpoint
, XPoint
*arcpoint
)
1727 genericptr
*sptr
= topobject
->plist
+ (*scnt
);
1729 switch(ELEMENTTYPE(*sptr
)) {
1732 *endpoint
= TOPOLY(sptr
)->points
+ TOPOLY(sptr
)->number
- 1;
1734 *endpoint
= TOPOLY(sptr
)->points
;
1738 *endpoint
= &(TOSPLINE(sptr
)->ctrl
[3]);
1740 *endpoint
= &(TOSPLINE(sptr
)->ctrl
[0]);
1744 arcpoint
->x
= (short)(TOARC(sptr
)->points
[TOARC(sptr
)->number
- 1].x
1746 arcpoint
->y
= (short)(TOARC(sptr
)->points
[TOARC(sptr
)->number
- 1].y
1750 arcpoint
->x
= (short)(TOARC(sptr
)->points
[0].x
+ 0.5);
1751 arcpoint
->y
= (short)(TOARC(sptr
)->points
[0].y
+ 0.5);
1753 *endpoint
= arcpoint
;
1758 /*------------------------------------------------------------*/
1759 /* Reverse points in a point list */
1760 /*------------------------------------------------------------*/
1762 void reversepoints(XPoint
*plist
, short number
)
1765 XPoint
*pend
= plist
+ number
- 1;
1766 short hnum
= number
>> 1;
1768 for (ppt
= plist
; ppt
< plist
+ hnum
; ppt
++, pend
--) {
1778 /*------------------------------------------------------------*/
1779 /* Same as above for floating-point positions */
1780 /*------------------------------------------------------------*/
1782 void reversefpoints(XfPoint
*plist
, short number
)
1785 XfPoint
*pend
= plist
+ number
- 1;
1786 short hnum
= number
>> 1;
1788 for (ppt
= plist
; ppt
< plist
+ hnum
; ppt
++, pend
--) {
1798 /*--------------------------------------------------------------*/
1799 /* Permanently remove an element from the topobject plist */
1800 /* add = 1 if plist has (parts + 1) elements */
1801 /*--------------------------------------------------------------*/
1803 void freepathparts(short *selectobj
, short add
)
1805 genericptr
*oldelem
= topobject
->plist
+ (*selectobj
);
1806 switch(ELEMENTTYPE(*oldelem
)) {
1808 free((TOPOLY(oldelem
))->points
);
1812 removep(selectobj
, add
);
1815 /*--------------------------------------------------------------*/
1816 /* Remove a part from an object */
1817 /* add = 1 if plist has (parts + 1) elements */
1818 /*--------------------------------------------------------------*/
1820 void removep(short *selectobj
, short add
)
1822 genericptr
*oldelem
= topobject
->plist
+ (*selectobj
);
1824 for (++oldelem
; oldelem
< topobject
->plist
+ topobject
->parts
+ add
; oldelem
++)
1825 *(oldelem
- 1) = *oldelem
;
1830 /*-------------------------------------------------*/
1831 /* Break a path into its constituent components */
1832 /*-------------------------------------------------*/
1837 genericptr
*genp
, *newg
;
1839 polyptr oldpoly
, *newpoly
;
1840 Boolean preselected
;
1843 if (areawin
->selects
== 0) {
1844 select_element(PATH
| POLYGON
);
1845 preselected
= FALSE
;
1847 else preselected
= TRUE
;
1849 if (areawin
->selects
== 0) {
1850 Wprintf("No objects selected.");
1854 /* for each selected path or polygon */
1856 for (selectobj
= areawin
->selectlist
; selectobj
< areawin
->selectlist
1857 + areawin
->selects
; selectobj
++) {
1858 SetForeground(dpy
, areawin
->gc
, BACKGROUND
);
1859 if (SELECTTYPE(selectobj
) == PATH
) {
1860 oldpath
= SELTOPATH(selectobj
);
1862 /* undraw the path */
1864 UDrawPath(oldpath
, xobjs
.pagelist
[areawin
->page
]->wirewidth
);
1866 /* move components to the top level */
1868 topobject
->plist
= (genericptr
*)realloc(topobject
->plist
,
1869 (topobject
->parts
+ oldpath
->parts
) * sizeof(genericptr
));
1870 newg
= topobject
->plist
+ topobject
->parts
;
1871 for (genp
= oldpath
->plist
; genp
< oldpath
->plist
+
1872 oldpath
->parts
; genp
++, newg
++) {
1875 topobject
->parts
+= oldpath
->parts
;
1877 /* remove the path object and revise the selectlist */
1879 freepathparts(selectobj
, 0);
1880 reviseselect(areawin
->selectlist
, areawin
->selects
, selectobj
);
1882 else if (SELECTTYPE(selectobj
) == POLYGON
) {
1883 /* Method to break a polygon, in lieu of the edit-mode */
1884 /* polygon break that was removed. */
1885 oldpoly
= SELTOPOLY(selectobj
);
1886 UDrawPolygon(oldpoly
, xobjs
.pagelist
[areawin
->page
]->wirewidth
);
1888 /* Get the point nearest the cursor, and break at that point */
1889 cycle
= closepoint(oldpoly
, &areawin
->save
);
1890 if (cycle
> 0 && cycle
< (oldpoly
->number
- 1)) {
1891 NEW_POLY(newpoly
, topobject
);
1892 polycopy(*newpoly
, oldpoly
);
1893 for (i
= cycle
; i
< oldpoly
->number
; i
++)
1894 (*newpoly
)->points
[i
- cycle
] = (*newpoly
)->points
[i
];
1895 oldpoly
->number
= cycle
+ 1;
1896 (*newpoly
)->number
= (*newpoly
)->number
- cycle
;
1900 if (!preselected
) clearselects();
1901 drawarea(NULL
, NULL
, NULL
);
1904 /*-------------------------------------------------*/
1905 /* Test if two points are near each other */
1906 /*-------------------------------------------------*/
1908 Boolean
neartest(XPoint
*point1
, XPoint
*point2
)
1912 diff
[0] = point1
->x
- point2
->x
;
1913 diff
[1] = point1
->y
- point2
->y
;
1914 diff
[0] = abs(diff
[0]);
1915 diff
[1] = abs(diff
[1]);
1917 if (diff
[0] <= 2 && diff
[1] <= 2) return True
;
1922 /*-------------------------------------------------*/
1923 /* Join stuff together */
1924 /*-------------------------------------------------*/
1929 polyptr
*newpoly
, nextwire
;
1932 short *scount
, *sptr
, *sptr2
, *direc
, *order
;
1933 short ordered
, startpt
= 0;
1934 short numpolys
, numlabels
, numpoints
, polytype
;
1937 XPoint
*testpoint
, *testpoint2
, *begpoint
, *endpoint
, arcpoint
[4];
1938 XPoint
*begpoint2
, *endpoint2
;
1939 Boolean allpolys
= True
;
1942 numpolys
= numlabels
= 0;
1943 for (selectobj
= areawin
->selectlist
; selectobj
< areawin
->selectlist
1944 + areawin
->selects
; selectobj
++) {
1945 if (SELECTTYPE(selectobj
) == POLYGON
) {
1946 /* arbitrary: keep style of last polygon in selectlist */
1947 polytype
= SELTOPOLY(selectobj
)->style
;
1948 polywidth
= SELTOPOLY(selectobj
)->width
;
1949 polycolor
= SELTOPOLY(selectobj
)->color
;
1952 else if (SELECTTYPE(selectobj
) == SPLINE
) {
1953 polytype
= SELTOSPLINE(selectobj
)->style
;
1954 polywidth
= SELTOSPLINE(selectobj
)->width
;
1955 polycolor
= SELTOSPLINE(selectobj
)->color
;
1959 else if (SELECTTYPE(selectobj
) == ARC
) {
1960 polytype
= SELTOARC(selectobj
)->style
;
1961 polywidth
= SELTOARC(selectobj
)->width
;
1962 polycolor
= SELTOARC(selectobj
)->color
;
1966 else if (SELECTTYPE(selectobj
) == LABEL
)
1969 if ((numpolys
== 0) && (numlabels
== 0)) {
1970 Wprintf("No elements selected for joining.");
1973 else if ((numpolys
== 1) || (numlabels
== 1)) {
1974 Wprintf("Only one element: nothing to join to.");
1977 else if ((numpolys
> 1) && (numlabels
> 1)) {
1978 Wprintf("Selection mixes labels and line segments. Ignoring.");
1981 else if (numlabels
> 0) {
1986 /* scount is a table of element numbers */
1987 /* order is an ordered table of end-to-end elements */
1988 /* direc is an ordered table of path directions (0=same as element, */
1989 /* 1=reverse from element definition) */
1991 scount
= (short *) malloc(numpolys
* sizeof(short));
1992 order
= (short *) malloc(numpolys
* sizeof(short));
1993 direc
= (short *) malloc(numpolys
* sizeof(short));
1997 /* make a record of the element instances involved */
1999 for (selectobj
= areawin
->selectlist
; selectobj
< areawin
->selectlist
2000 + areawin
->selects
; selectobj
++) {
2001 if (SELECTTYPE(selectobj
) == POLYGON
) {
2002 numpoints
+= SELTOPOLY(selectobj
)->number
- 1;
2003 *(sptr
++) = *selectobj
;
2005 else if (SELECTTYPE(selectobj
) == SPLINE
|| SELECTTYPE(selectobj
) == ARC
)
2006 *(sptr
++) = *selectobj
;
2009 /* Sort the elements by sorting the scount array: */
2010 /* Loop through each point as starting point in case of strangely connected */
2011 /* structures. . . for normal structures it should break out on the first */
2012 /* loop (startpt = 0). */
2014 for (startpt
= 0; startpt
< numpolys
; startpt
++) {
2016 /* set first in ordered list */
2019 order
[0] = *(scount
+ startpt
);
2021 for (ordered
= 0; ordered
< numpolys
- 1; ordered
++) {
2023 setendpoint(order
+ ordered
, (1 ^ direc
[ordered
]), &endpoint2
, &arcpoint
[0]);
2024 setendpoint(order
, (0 ^ direc
[0]), &begpoint2
, &arcpoint
[1]);
2026 for (sptr
= scount
; sptr
< scount
+ numpolys
; sptr
++) {
2028 /* don't compare with things already in the list */
2029 for (sptr2
= order
; sptr2
<= order
+ ordered
; sptr2
++)
2030 if (*sptr
== *sptr2
) break;
2031 if (sptr2
!= order
+ ordered
+ 1) continue;
2033 setendpoint(sptr
, 0, &begpoint
, &arcpoint
[2]);
2034 setendpoint(sptr
, 1, &endpoint
, &arcpoint
[3]);
2036 /* four cases of matching endpoint of one element to another */
2038 if (neartest(begpoint
, endpoint2
)) {
2039 order
[ordered
+ 1] = *sptr
;
2040 direc
[ordered
+ 1] = 0;
2043 else if (neartest(endpoint
, endpoint2
)) {
2044 order
[ordered
+ 1] = *sptr
;
2045 direc
[ordered
+ 1] = 1;
2048 else if (neartest(begpoint
, begpoint2
)) {
2049 for (sptr2
= order
+ ordered
+ 1; sptr2
> order
; sptr2
--)
2050 *sptr2
= *(sptr2
- 1);
2051 for (sptr2
= direc
+ ordered
+ 1; sptr2
> direc
; sptr2
--)
2052 *sptr2
= *(sptr2
- 1);
2057 else if (neartest(endpoint
, begpoint2
)) {
2058 for (sptr2
= order
+ ordered
+ 1; sptr2
> order
; sptr2
--)
2059 *sptr2
= *(sptr2
- 1);
2060 for (sptr2
= direc
+ ordered
+ 1; sptr2
> direc
; sptr2
--)
2061 *sptr2
= *(sptr2
- 1);
2067 if (sptr
== scount
+ numpolys
) break;
2069 if (ordered
== numpolys
- 1) break;
2072 if (startpt
== numpolys
) {
2073 Wprintf("Cannot join: Too many free endpoints");
2080 /* create the new polygon or path */
2083 NEW_POLY(newpoly
, topobject
);
2085 (*newpoly
)->number
= numpoints
;
2086 (*newpoly
)->points
= (pointlist
) malloc(numpoints
* sizeof(XPoint
));
2087 (*newpoly
)->width
= polywidth
;
2088 (*newpoly
)->style
= polytype
;
2089 (*newpoly
)->color
= polycolor
;
2090 (*newpoly
)->passed
= NULL
;
2091 (*newpoly
)->cycle
= NULL
;
2093 /* insert the points into the new polygon */
2095 testpoint2
= (*newpoly
)->points
;
2096 for (sptr
= order
; sptr
< order
+ numpolys
; sptr
++) {
2097 nextwire
= SELTOPOLY(sptr
);
2098 if (*(direc
+ (short)(sptr
- order
)) == 0) {
2099 for (testpoint
= nextwire
->points
; testpoint
< nextwire
->points
+
2100 nextwire
->number
- 1; testpoint
++) {
2101 testpoint2
->x
= testpoint
->x
;
2102 testpoint2
->y
= testpoint
->y
;
2107 for (testpoint
= nextwire
->points
+ nextwire
->number
- 1; testpoint
2108 > nextwire
->points
; testpoint
--) {
2109 testpoint2
->x
= testpoint
->x
;
2110 testpoint2
->y
= testpoint
->y
;
2115 /* pick up the last point */
2116 testpoint2
->x
= testpoint
->x
;
2117 testpoint2
->y
= testpoint
->y
;
2119 /* delete the old elements from the list */
2121 register_for_undo(XCF_Wire
, UNDO_MORE
, areawin
->topinstance
, *newpoly
);
2123 delobj
= delete_element(areawin
->topinstance
, areawin
->selectlist
,
2124 areawin
->selects
, NORMAL
);
2125 register_for_undo(XCF_Delete
, UNDO_DONE
, areawin
->topinstance
,
2129 else { /* create a path */
2131 NEW_PATH(newpath
, topobject
);
2132 (*newpath
)->style
= polytype
;
2133 (*newpath
)->color
= polycolor
;
2134 (*newpath
)->width
= polywidth
;
2135 (*newpath
)->parts
= 0;
2136 (*newpath
)->plist
= (genericptr
*) malloc(sizeof(genericptr
));
2137 (*newpath
)->passed
= NULL
;
2139 /* copy the elements from the top level into the path structure */
2141 for (sptr
= order
; sptr
< order
+ numpolys
; sptr
++) {
2142 genericptr
*oldelem
= topobject
->plist
+ *sptr
;
2143 genericptr
*newelem
;
2145 switch (ELEMENTTYPE(*oldelem
)) {
2147 polyptr copypoly
= TOPOLY(oldelem
);
2149 NEW_POLY(newpoly
, (*newpath
));
2150 polycopy(*newpoly
, copypoly
);
2153 arcptr copyarc
= TOARC(oldelem
);
2155 NEW_ARC(newarc
, (*newpath
));
2156 arccopy(*newarc
, copyarc
);
2159 splineptr copyspline
= TOSPLINE(oldelem
);
2160 splineptr
*newspline
;
2161 NEW_SPLINE(newspline
, (*newpath
));
2162 splinecopy(*newspline
, copyspline
);
2165 newelem
= (*newpath
)->plist
+ (*newpath
)->parts
- 1;
2167 /* reverse point order if necessary */
2169 if (*(direc
+ (short)(sptr
- order
)) == 1) {
2170 switch (ELEMENTTYPE(*newelem
)) {
2172 reversepoints(TOPOLY(newelem
)->points
, TOPOLY(newelem
)->number
);
2175 TOARC(newelem
)->radius
= -TOARC(newelem
)->radius
;
2178 reversepoints(TOSPLINE(newelem
)->ctrl
, 4);
2179 calcspline(TOSPLINE(newelem
));
2184 /* decompose arcs into bezier curves */
2185 if (ELEMENTTYPE(*newelem
) == ARC
)
2186 decomposearc(*newpath
);
2189 /* delete the old elements from the list */
2191 register_for_undo(XCF_Join
, UNDO_MORE
, areawin
->topinstance
, *newpath
);
2193 delobj
= delete_element(areawin
->topinstance
, scount
, numpolys
, NORMAL
);
2195 register_for_undo(XCF_Delete
, UNDO_DONE
, areawin
->topinstance
,
2198 /* Remove the path parts from the selection list and add the path */
2200 selectobj
= allocselect();
2201 for (pgen
= topobject
->plist
; pgen
< topobject
->plist
+ topobject
->parts
;
2203 if ((TOPATH(pgen
)) == (*newpath
)) {
2204 *selectobj
= (short)(pgen
- topobject
->plist
);
2212 incr_changes(topobject
);
2213 /* Do not clear the selection, to be consistent with all the */
2214 /* other actions that clear only when something has not been */
2215 /* preselected before the action. Elements must be selected */
2216 /* prior to the "join" action, by necessity. */
2222 /*----------------------------------------------*/
2223 /* Add a new point to a polygon */
2224 /*----------------------------------------------*/
2226 void poly_add_point(polyptr thispoly
, XPoint
*newpoint
) {
2230 thispoly
->points
= (XPoint
*)realloc(thispoly
->points
,
2231 thispoly
->number
* sizeof(XPoint
));
2232 tpoint
= thispoly
->points
+ thispoly
->number
- 1;
2233 tpoint
->x
= newpoint
->x
;
2234 tpoint
->y
= newpoint
->y
;
2237 /*-------------------------------------------------*/
2238 /* ButtonPress handler while a wire is being drawn */
2239 /*-------------------------------------------------*/
2241 void wire_op(int op
, int x
, int y
)
2243 XPoint userpt
, *tpoint
;
2246 snap(x
, y
, &userpt
);
2248 newwire
= TOPOLY(EDITPART
);
2250 if (areawin
->attachto
>= 0) {
2252 findattach(&apos
, NULL
, &userpt
);
2254 areawin
->attachto
= -1;
2257 if (areawin
->manhatn
) manhattanize(&userpt
, newwire
, -1, TRUE
);
2260 tpoint
= newwire
->points
+ newwire
->number
- 1;
2261 tpoint
->x
= userpt
.x
;
2262 tpoint
->y
= userpt
.y
;
2264 /* cancel wire operation completely */
2265 if (op
== XCF_Cancel
) {
2266 free(newwire
->points
);
2269 eventmode
= NORMAL_MODE
;
2273 /* back up one point; prevent length zero wires */
2274 else if ((op
== XCF_Cancel_Last
) || ((tpoint
- 1)->x
== userpt
.x
&&
2275 (tpoint
- 1)->y
== userpt
.y
)) {
2276 if (newwire
->number
<= 2) {
2277 free(newwire
->points
);
2280 eventmode
= NORMAL_MODE
;
2284 if (--newwire
->number
== 2) newwire
->style
= UNCLOSED
|
2285 (areawin
->style
& (DASHED
| DOTTED
));
2289 if (newwire
&& (op
== XCF_Wire
|| op
== XCF_Continue_Element
)) {
2290 if (newwire
->number
== 2)
2291 newwire
->style
= areawin
->style
;
2292 poly_add_point(newwire
, &userpt
);
2294 else if ((newwire
== NULL
) || op
== XCF_Finish_Element
|| op
== XCF_Cancel
) {
2295 xcRemoveEventHandler(areawin
->area
, PointerMotionMask
, False
,
2296 (xcEventHandler
)trackwire
, NULL
);
2300 if (op
== XCF_Finish_Element
) {
2302 /* If the last points are the same, remove all redundant ones. */
2303 /* This avoids the problem of extra points when people do left */
2304 /* click followed by middle click to finish (the redundant way */
2305 /* a lot of drawing programs work). */
2308 while (newwire
->number
> 2) {
2309 tpoint
= newwire
->points
+ newwire
->number
- 1;
2310 t2pt
= newwire
->points
+ newwire
->number
- 2;
2311 if (tpoint
->x
!= t2pt
->x
|| tpoint
->y
!= t2pt
->y
)
2316 incr_changes(topobject
);
2317 if (!nonnetwork(newwire
)) invalidate_netlist(topobject
);
2318 register_for_undo(XCF_Wire
, UNDO_MORE
, areawin
->topinstance
, newwire
);
2319 poly_mode_draw(xcDRAW_FINAL
, newwire
);
2322 poly_mode_draw(xcDRAW_EDIT
, newwire
);
2323 if (op
== XCF_Cancel_Last
)
2324 checkwarp(newwire
->points
+ newwire
->number
- 1);
2327 if (op
== XCF_Finish_Element
) {
2328 eventmode
= NORMAL_MODE
;
2329 singlebbox(EDITPART
);
2333 /*-------------------------------------------------------------------------*/
2334 /* Helper functions for the xxx_mode_draw functions */
2335 /* Functions to be used around the drawing of the edited element */
2336 /* begin_event_mode_drawing starts double buffering and copies the */
2337 /* fixed pixmap. end_event_mode stops the double buffering and displays */
2338 /* everything on screen. */
2339 /*-------------------------------------------------------------------------*/
2342 static Window old_win
;
2343 #endif /* !HAVE_CAIRO */
2345 static void begin_event_mode_drawing(void)
2347 /* Start double buffering */
2349 cairo_identity_matrix(areawin
->cr
);
2350 cairo_translate(areawin
->cr
, areawin
->panx
, areawin
->pany
);
2351 cairo_push_group(areawin
->cr
);
2352 #else /* HAVE_CAIRO */
2353 old_win
= areawin
->window
;
2354 areawin
->window
= (Window
) dbuf
;
2355 #endif /* HAVE_CAIRO */
2357 /* Copy background pixmap with the element(s) not currently being edited */
2359 if (areawin
->panx
|| areawin
->pany
) {
2360 SetForeground(dpy
, areawin
->gc
, BACKGROUND
);
2361 cairo_paint(areawin
->cr
);
2363 cairo_set_source(areawin
->cr
, areawin
->fixed_pixmap
);
2364 cairo_paint(areawin
->cr
);
2365 #else /* HAVE_CAIRO */
2366 if (areawin
->panx
|| areawin
->pany
) {
2367 int x
= max(0, -areawin
->panx
);
2368 int y
= max(0, -areawin
->pany
);
2369 unsigned int w
= areawin
->width
- x
;
2370 unsigned int h
= areawin
->height
- y
;
2371 SetForeground(dpy
, areawin
->gc
, BACKGROUND
);
2372 XSetFillStyle(dpy
, areawin
->gc
, FillSolid
);
2373 XFillRectangle(dpy
, areawin
->window
, areawin
->gc
, 0, 0, areawin
->width
,
2375 XCopyArea(dpy
, areawin
->fixed_pixmap
, areawin
->window
, areawin
->gc
,
2376 x
, y
, w
, h
, max(0, areawin
->panx
), max(0, areawin
->pany
));
2379 XCopyArea(dpy
, areawin
->fixed_pixmap
, areawin
->window
, areawin
->gc
, 0, 0,
2380 areawin
->width
, areawin
->height
, 0, 0);
2381 #endif /* HAVE_CAIRO */
2383 areawin
->redraw_ongoing
= True
;
2387 static void end_event_mode_drawing(void)
2389 /* End double buffering */
2391 cairo_pop_group_to_source(areawin
->cr
);
2392 cairo_paint(areawin
->cr
);
2393 #else /* HAVE_CAIRO */
2394 areawin
->window
= old_win
;
2395 XCopyArea(dpy
, dbuf
, areawin
->window
, areawin
->gc
, 0, 0, areawin
->width
,
2396 areawin
->height
, 0, 0);
2397 #endif /* HAVE_CAIRO */
2399 areawin
->redraw_ongoing
= False
;
2402 /*-------------------------------------------------------------------------*/
2403 /* Helper functions for the xxx_mode_draw functions */
2404 /* Functions to be used when finishing the element. The final state is */
2405 /* drawn into the fixed pixmap, which is show when the */
2406 /* end_event_mode_drawing_final is called */
2407 /* (Sorry about the name :-) ) */
2408 /*-------------------------------------------------------------------------*/
2410 static void begin_event_mode_drawing_final(void)
2413 cairo_identity_matrix(areawin
->cr
);
2414 cairo_push_group(areawin
->cr
);
2415 cairo_set_source(areawin
->cr
, areawin
->fixed_pixmap
);
2416 cairo_paint(areawin
->cr
);
2417 #else /* HAVE_CAIRO */
2418 old_win
= areawin
->window
;
2419 areawin
->window
= (Window
) areawin
->fixed_pixmap
;
2420 #endif /* HAVE_CAIRO */
2422 areawin
->redraw_ongoing
= True
;
2426 static void end_event_mode_drawing_final(void)
2429 cairo_pattern_destroy(areawin
->fixed_pixmap
);
2430 areawin
->fixed_pixmap
= cairo_pop_group(areawin
->cr
);
2431 #else /* HAVE_CAIRO */
2432 areawin
->window
= old_win
;
2433 #endif /* HAVE_CAIRO */
2435 /* Show fixed pixmap */
2437 cairo_identity_matrix(areawin
->cr
);
2438 cairo_set_source(areawin
->cr
, areawin
->fixed_pixmap
);
2439 cairo_paint(areawin
->cr
);
2440 #else /* HAVE_CAIRO */
2441 XCopyArea(dpy
, areawin
->fixed_pixmap
, areawin
->window
, areawin
->gc
, 0, 0,
2442 areawin
->width
, areawin
->height
, 0, 0);
2443 #endif /* HAVE_CAIRO */
2445 areawin
->redraw_ongoing
= False
;
2448 /*-------------------------------------------------------------------------*/
2449 /* Helper function for the xxx_mode_draw functions */
2450 /* Hide all the selected elements and draw all the element not currently */
2451 /* being edited to fixed_pixmap */
2452 /*-------------------------------------------------------------------------*/
2454 static void draw_fixed_without_selection(void)
2457 for (idx
= 0; idx
< areawin
->selects
; idx
++)
2458 SELTOGENERIC(&areawin
->selectlist
[idx
])->type
|= DRAW_HIDE
;
2460 for (idx
= 0; idx
< areawin
->selects
; idx
++)
2461 SELTOGENERIC(&areawin
->selectlist
[idx
])->type
&= ~DRAW_HIDE
;
2464 /*-------------------------------------------------------------------------*/
2465 /* generic xxx_mode_draw function, that handles most of the thing needed */
2466 /* for arcs, splines and paths. */
2467 /*-------------------------------------------------------------------------*/
2469 static void generic_mode_draw(xcDrawType type
, generic
*newgen
,
2470 void (*decorations
)(generic
*newgen
))
2476 case xcREDRAW_FORCED
:
2477 draw_fixed_without_selection();
2481 begin_event_mode_drawing();
2482 for (idx
= 0; idx
< areawin
->selects
; idx
++) {
2483 int scolor
= SELTOCOLOR(&areawin
->selectlist
[idx
]);
2484 XcTopSetForeground(scolor
);
2485 easydraw(areawin
->selectlist
[idx
], scolor
);
2488 (*decorations
)(newgen
);
2489 end_event_mode_drawing();
2493 begin_event_mode_drawing_final();
2494 for (idx
= 0; idx
< areawin
->selects
; idx
++) {
2495 int scolor
= SELTOCOLOR(&areawin
->selectlist
[idx
]);
2496 XcTopSetForeground(scolor
);
2497 easydraw(areawin
->selectlist
[idx
], scolor
);
2499 end_event_mode_drawing_final();
2500 if (areawin
->selects
> 1) /* FIXME: Temp. fix for multiple selects */
2501 areawin
->redraw_needed
= True
; /* Will be removed later on */
2505 /* Do not remove the empty begin/end. For cairo, this renders the */
2506 /* background with the fixed elements */
2507 begin_event_mode_drawing_final();
2508 end_event_mode_drawing_final();
2513 /*-------------------------------------------------------------------------*/
2514 /* Drawing function for ARC_MODE and EARC_MODE */
2515 /*-------------------------------------------------------------------------*/
2517 static void arc_mode_decorations(generic
*newgen
)
2519 UDrawXLine(((arc
*) newgen
)->position
, areawin
->save
);
2522 void arc_mode_draw(xcDrawType type
, arc
*newarc
)
2524 generic_mode_draw(type
, (generic
*) newarc
, arc_mode_decorations
);
2527 /*-------------------------------------------------------------------------*/
2528 /* Drawing function for SPLINE_MODE and ESPLINE_MODE */
2529 /*-------------------------------------------------------------------------*/
2531 static void spline_mode_decorations(generic
*newgen
)
2533 spline
*newspline
= (spline
*) newgen
;
2534 UDrawXLine(newspline
->ctrl
[0], newspline
->ctrl
[1]);
2535 UDrawXLine(newspline
->ctrl
[3], newspline
->ctrl
[2]);
2538 void spline_mode_draw(xcDrawType type
, spline
*newspline
)
2540 generic_mode_draw(type
, (generic
*) newspline
, spline_mode_decorations
);
2543 /*-------------------------------------------------------------------------*/
2544 /* Drawing function for WIRE_MODE, BOX_MODE and EPOLY_MODE */
2545 /*-------------------------------------------------------------------------*/
2547 void poly_mode_draw(xcDrawType type
, polygon
*newpoly
)
2549 generic_mode_draw(type
, (generic
*) newpoly
, NULL
);
2552 /*-------------------------------------------------------------------------*/
2553 /* Drawing function for EPATH_MODE */
2554 /*-------------------------------------------------------------------------*/
2556 static void path_mode_decorations(generic
*newgen
)
2559 path
*newpath
= (path
*) newgen
;
2560 for (ggen
= newpath
->plist
; ggen
< newpath
->plist
+ newpath
->parts
; ggen
++) {
2561 if (ELEMENTTYPE(*ggen
) == SPLINE
) {
2562 spline
*lastspline
= TOSPLINE(ggen
);
2563 UDrawXLine(lastspline
->ctrl
[0], lastspline
->ctrl
[1]);
2564 UDrawXLine(lastspline
->ctrl
[3], lastspline
->ctrl
[2]);
2569 void path_mode_draw(xcDrawType type
, path
*newpath
)
2571 generic_mode_draw(type
, (generic
*) newpath
, path_mode_decorations
);
2574 /*-------------------------------------------------------------------------*/
2575 /* Drawing function for TEXT_MODE, CATTEXT_MODE and ETEXT_MODE */
2576 /*-------------------------------------------------------------------------*/
2578 static void text_mode_decorations(generic
*newgen
)
2580 UDrawTLine((label
*) newgen
);
2583 void text_mode_draw(xcDrawType type
, label
*newlabel
)
2585 generic_mode_draw(type
, (generic
*) newlabel
, text_mode_decorations
);
2588 /*-------------------------------------------------------------------------*/
2589 /* Drawing function for SELAREA_MODE */
2590 /*-------------------------------------------------------------------------*/
2592 void selarea_mode_draw(xcDrawType type
, void *unused
)
2597 case xcREDRAW_FORCED
:
2603 begin_event_mode_drawing();
2604 draw_all_selected();
2605 UDrawBox(areawin
->origin
, areawin
->save
);
2606 end_event_mode_drawing();
2611 /* No need for rendering the background, since it will be */
2612 /* overwritten by the select_area() function anyway */
2617 /*-------------------------------------------------------------------------*/
2618 /* Drawing function for RESCALE_MODE */
2619 /*-------------------------------------------------------------------------*/
2621 void rescale_mode_draw(xcDrawType type
, void *unused
)
2626 case xcREDRAW_FORCED
:
2632 begin_event_mode_drawing();
2633 UDrawRescaleBox(&areawin
->save
);
2634 end_event_mode_drawing();
2639 /* No need for rendering the background, since it will be */
2640 /* overwritten by the select_area() function anyway */
2645 /*-------------------------------------------------------------------------*/
2646 /* Drawing function for CATMOVE_MODE, MOVE_MODE and COPY_MODE */
2647 /*-------------------------------------------------------------------------*/
2649 void move_mode_draw(xcDrawType type
, void *unused
)
2651 float wirewidth
= xobjs
.pagelist
[areawin
->page
]->wirewidth
;
2658 case xcREDRAW_FORCED
:
2660 draw_fixed_without_selection();
2664 begin_event_mode_drawing();
2665 XTopSetForeground(SELECTCOLOR
);
2666 for (idx
= 0; idx
< areawin
->selects
; idx
++)
2667 easydraw(areawin
->selectlist
[idx
], DOFORALL
);
2668 for (selectobj
= areawin
->selectlist
; selectobj
< areawin
->selectlist
2669 + areawin
->selects
; selectobj
++) {
2670 if (SELECTTYPE(selectobj
) == LABEL
) {
2671 label
*labelobj
= SELTOLABEL(selectobj
);
2672 if (labelobj
->pin
== False
)
2676 if (areawin
->pinattach
) {
2677 for (pgen
= topobject
->plist
; pgen
< topobject
->plist
+
2678 topobject
->parts
; pgen
++) {
2679 if (ELEMENTTYPE(*pgen
) == POLYGON
) {
2680 polygon
*cpoly
= TOPOLY(pgen
);
2681 if (cpoly
->cycle
!= NULL
)
2682 UDrawPolygon(cpoly
, wirewidth
);
2686 end_event_mode_drawing();
2690 begin_event_mode_drawing_final();
2691 for (selectobj
= areawin
->selectlist
; selectobj
2692 < areawin
->selectlist
+ areawin
->selects
; selectobj
++) {
2693 XTopSetForeground(SELTOCOLOR(selectobj
));
2694 easydraw(*selectobj
, DOFORALL
);
2696 end_event_mode_drawing_final();
2700 /* Do not remove the empty begin/end. For cairo, this renders the */
2701 /* background with the fixed elements */
2702 begin_event_mode_drawing_final();
2703 end_event_mode_drawing_final();
2708 /*-------------------------------------------------------------------------*/
2709 /* Drawing function for ASSOC_MODE, EINST_MODE, (E)FONTCAT_MODE, PAN_MODE, */
2710 /* NORMAL_MODE, UNDO_MODE and CATALOG_MODE */
2711 /*-------------------------------------------------------------------------*/
2713 void normal_mode_draw(xcDrawType type
, void *unused
)
2719 case xcREDRAW_FORCED
:
2720 draw_fixed_without_selection();
2724 begin_event_mode_drawing();
2726 /* draw the highlighted netlist, if any */
2727 if (checkvalid(topobject
) != -1)
2728 if (topobject
->highlight
.netlist
!= NULL
)
2729 highlightnetlist(topobject
, areawin
->topinstance
, 1);
2731 if ((areawin
->selects
== 1) && SELECTTYPE(areawin
->selectlist
) == LABEL
2732 && areawin
->textend
> 0 && areawin
->textpos
> areawin
->textend
) {
2733 labelptr drawlabel
= SELTOLABEL(areawin
->selectlist
);
2734 UDrawString(drawlabel
, DOSUBSTRING
, areawin
->topinstance
);
2736 else if (eventmode
== NORMAL_MODE
|| eventmode
== CATALOG_MODE
)
2737 draw_all_selected();
2738 end_event_mode_drawing();