Corrected a long-standing error in which ending text with a literal
[xcircuit.git] / elements.c
blobc7705f4bee76a756955ff870ea28952694cee11d
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 /*----------------------------------------------------------------------*/
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
14 #ifndef XC_WIN32
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #endif
19 #ifdef TCL_WRAPPER
20 #include <tk.h>
21 #else
22 #ifndef XC_WIN32
23 #include "Xw/Xw.h"
24 #endif
25 #endif
27 /*----------------------------------------------------------------------*/
28 /* Local includes */
29 /*----------------------------------------------------------------------*/
31 #include "xcircuit.h"
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;
47 extern xcWidget top;
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)
54 extern Pixmap dbuf;
55 #endif
57 extern double atan2();
59 /*------------------------------------------------------------------------*/
60 /* Declarations of global variables */
61 /*------------------------------------------------------------------------*/
63 char extchar[20];
64 double saveratio;
65 u_char texttype;
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. */
74 /* */
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. */
85 /* */
86 /* Other properties must be set individually by the calling */
87 /* routine. */
88 /* */
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)
95 labelptr *newlab;
96 objectptr destobject;
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;
109 else
110 (*newlab)->string->nextpart = strptr;
112 calcbboxvalues(locdestinst, (genericptr *)newlab);
113 updatepagebounds(destobject);
114 if (dochange != (u_char)0) incr_changes(destobject);
115 return *newlab;
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 */
122 /* routine. */
123 /*--------------------------------------------------------------*/
125 labelptr new_simple_label(objinstptr destinst, char *cstr,
126 int pintype, int x, int y)
128 stringpart *strptr;
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,
151 int x, int y)
153 labelptr *newlab;
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;
160 return *newlab;
163 /*--------------------------------------------------------------*/
164 /* Polygon constructor: Create a new polygon element in the */
165 /* object whose instance is "destinst" and return a pointer to */
166 /* it. */
167 /* */
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. */
175 /* */
176 /* Other properties must be set individually by the calling */
177 /* routine. */
178 /* */
179 /* Return value is a pointer to the newly created polygon. */
180 /*--------------------------------------------------------------*/
182 polyptr new_polygon(objinstptr destinst, pointlist *points, int number)
184 polyptr *newpoly;
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);
199 return *newpoly;
202 /*--------------------------------------------------------------*/
203 /* Spline constructor: Create a new spline element in the */
204 /* object whose instance is "destinst" and return a pointer to */
205 /* it. */
206 /* */
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. */
210 /* */
211 /* Other properties must be set individually by the calling */
212 /* routine. */
213 /* */
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;
222 int i;
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);
237 return *newspline;
240 /*--------------------------------------------------------------*/
241 /* Arc constructor: Create a new arc element in the object */
242 /* whose instance is "destinst" and return a pointer to it. */
243 /* */
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. */
248 /* */
249 /* Other properties must be set individually by the calling */
250 /* routine. */
251 /* */
252 /* Return value is a pointer to the newly created arc. */
253 /*--------------------------------------------------------------*/
255 arcptr new_arc(objinstptr destinst, int radius, int x, int y)
257 arcptr *newarc;
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;
268 calcarc(*newarc);
269 calcbboxvalues(locdestinst, (genericptr *)newarc);
270 updatepagebounds(destobject);
271 incr_changes(destobject);
272 return *newarc;
275 /*--------------------------------------------------------------*/
276 /* Instance constructor: Create a new object instance element */
277 /* in the object whose instance is "destinst" and return a */
278 /* pointer to it. */
279 /* */
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 */
283 /* copy. */
284 /* "x" and "y" represents the instance position. */
285 /* */
286 /* Other properties must be set individually by the calling */
287 /* routine. */
288 /* */
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);
309 return *newobjinst;
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)
337 UNUSED(x);
338 UNUSED(y);
340 newpath->style = NORMAL;
341 newpath->width = areawin->linewidth;
342 newpath->style = areawin->style;
343 newpath->color = areawin->color;
344 newpath->parts = 0;
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)
379 arcptr *newarc;
380 objinstptr *newdot;
381 objectptr dotobj;
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);
390 else {
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;
399 calcarc(*newarc);
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)
439 labelptr *newlabel;
440 XPoint userpt;
441 short tmpheight, *newselect;
443 XDefineCursor(dpy, areawin->window, TEXTPTR);
444 W3printf("Click to end or cancel.");
446 if (fontcount == 0)
447 Wprintf("Warning: No fonts available!");
449 unselect_all();
450 NEW_LABEL(newlabel, topobject);
451 newselect = allocselect();
452 *newselect = topobject->parts - 1;
453 snap(x, y, &userpt);
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 /*----------------------------------------------------------------------*/
470 #define MAXCHARS 10
472 void charreport(labelptr curlabel)
474 int i, locpos, cleft = 149;
475 stringpart *strptr;
477 _STR2[0] = '\0';
478 for (i = areawin->textpos - MAXCHARS; i <= areawin->textpos + MAXCHARS - 1; i++) {
479 if (i < 0) continue;
480 strptr = findstringpart(i, &locpos, curlabel->string, areawin->topinstance);
481 if (i == areawin->textpos) {
482 strncat(_STR2, "| ", cleft);
483 cleft -= 2;
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)
503 genericptr *tgen;
504 labelptr tlab;
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;
514 return NULL;
517 /*--------------------------------------------------------------*/
518 /* Interpret string and add to current label. */
519 /* keypressed is a KeySym */
520 /* clientdata can pass information for label controls */
521 /* */
522 /* Return TRUE if labeltext handled the character, FALSE if the */
523 /* character was not recognized. */
524 /*--------------------------------------------------------------*/
526 Boolean labeltext(int keypressed, char *clientdata)
528 labelptr curlabel;
529 stringpart *curpos, *labelbuf;
530 int locpos;
531 Boolean r = True, do_redraw = False;
532 short cfont;
533 TextExtents tmpext;
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;
542 return FALSE;
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) {
551 int curloc, strpos;
552 stringpart *strptr;
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);
560 if (curloc >= 0) {
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);
574 else
575 areawin->textpos++;
577 else
578 Fprintf(stdout, "Error: Unexpected NULL string part\n");
579 areawin->textpos--;
581 areawin->textend = 0;
582 do_redraw = True;
585 else if (clientdata != NULL && keypressed == TEXT_DEL_PARAM) {
586 if (areawin->textpos > 1) {
587 int curloc, strpos;
588 stringpart *strptr;
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)) {
595 strpos--;
596 strptr = findstringpart(strpos--, &curloc, curlabel->string,
597 areawin->topinstance);
599 if ((strptr != NULL) && (strptr->type == PARAM_START)) {
600 if (strpos >= 0) {
601 undrawtext(curlabel);
602 unmakeparam(curlabel, areawin->topinstance, strptr);
603 do_redraw = True;
608 else if (clientdata != NULL && keypressed == TEXT_RETURN) {
609 Boolean hasstuff = False; /* Check for null string */
610 stringpart *tmppos;
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);
618 W3printf("");
620 if (hasstuff && (eventmode != ETEXT_MODE && eventmode != CATTEXT_MODE)) {
621 register_for_undo(XCF_Text, UNDO_MORE, areawin->topinstance,
622 curlabel);
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);
637 else {
638 /* Label had just been created; just delete it w/o undo */
639 freelabel(curlabel->string);
640 free(curlabel);
641 topobject->parts--;
642 unselect_all();
646 if ((!hasstuff) && (eventmode == CATTEXT_MODE)) { /* can't have null labels! */
647 undo_action();
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) {
660 objectptr libobj;
661 stringpart *oldname;
662 int page, libnum;
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))
667 != NULL) {
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 */
672 /* changed. */
674 char *techptr, *libobjname = libobj->name;
675 if ((techptr = strstr(libobjname, "::")) != NULL &&
676 (strstr(curlabel->string->nextpart->data.string, "::")
677 == NULL))
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);
714 renamepage(page);
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);
722 else {
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);
742 if (!hasstuff) {
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");
761 else
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);
773 else
774 calcbbox(areawin->topinstance);
776 unselect_all();
777 return r;
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) {
787 areawin->textpos++;
788 curpos = findstringpart(areawin->textpos, &locpos, curlabel->string,
789 areawin->topinstance);
790 if (curpos != NULL)
791 if (curpos->type == RETURN || curpos->type == MARGINSTOP)
792 break;
795 else if (clientdata != NULL && keypressed == TEXT_UP) {
796 while (areawin->textpos > 1) {
797 areawin->textpos--;
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--;
802 break;
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) {
811 labelptr *newlabel;
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);
822 if (locpos > 0)
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;
832 else {
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);
849 do_redraw = True;
853 /* Write a font designator or other control into the string */
855 else if (clientdata != NULL) {
856 oparamptr ops;
857 stringpart *newpart;
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);
875 if (locpos > 0) {
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;
884 if (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) {
892 case RETURN:
893 /* Identify this as an explicitly placed line break */
894 newpart->data.flags = 0;
895 break;
896 case FONT_SCALE:
897 newpart->data.scale = *((float *)clientdata);
898 break;
899 case KERN:
900 newpart->data.kern[0] = *((short *)clientdata);
901 newpart->data.kern[1] = *((short *)clientdata + 1);
902 break;
903 case FONT_COLOR:
904 newpart->data.color = *((int *)clientdata);
905 if (newpart->data.color >= number_colors) errcond = True;
906 break;
907 case FONT_NAME:
908 newpart->data.font = *((int *)clientdata);
909 if (newpart->data.font >= fontcount) errcond = True;
910 break;
911 case MARGINSTOP:
912 /* A margin of 1 or 0 is useless, so such a value */
913 /* indicates to take the margin from the current */
914 /* position. */
916 if (*((int *)clientdata) <= 1)
917 newpart->data.width = (int)tmpext.width;
918 else
919 newpart->data.width = *((int *)clientdata);
920 CheckMarginStop(curlabel, areawin->topinstance, FALSE);
921 break;
922 case PARAM_START:
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;
927 else {
928 /* Forward edit cursor position to the end of the parameter */
929 do {
930 areawin->textpos++;
931 curpos = findstringpart(areawin->textpos, &locpos, curlabel->string,
932 areawin->topinstance);
933 } while (curpos && (curpos->type != PARAM_END));
935 break;
937 if (errcond == True) {
938 Wprintf("Error in insertion. Ignoring.");
939 deletestring(newpart, &curlabel->string, areawin->topinstance);
940 r = FALSE;
942 else {
943 areawin->textpos++;
945 do_redraw = True;
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) {
952 stringpart *lastpos;
954 /* erase first. */
955 undrawtext(curlabel);
957 /* Current position is not in a text string */
958 if (locpos < 0) {
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 */
965 if (locpos < 0) {
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,
975 2 + slen);
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 */
988 do_redraw = True;
989 r = TRUE;
992 /* Redraw the label */
994 if (do_redraw) {
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);
1013 if (cfont < 0) {
1014 Wprintf("Error: Illegal label string");
1015 return r;
1017 else
1018 setfontmarks(cfont, -1);
1020 areawin->textend = 0;
1022 return r;
1025 /*-------------------------------------*/
1026 /* Initiate return from text edit mode */
1027 /*-------------------------------------*/
1029 void textreturn()
1031 labeltext(TEXT_RETURN, (char *)1);
1034 /*--------------------------------------*/
1035 /* Change the anchoring of a label */
1036 /*--------------------------------------*/
1038 void reanchor(short mode)
1040 labelptr curlabel = NULL;
1041 short *tsel;
1042 short jsave;
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);
1056 changed = True;
1058 redrawtext(curlabel);
1059 UDrawTLine(curlabel);
1061 setfontmarks(-1, curlabel->anchor);
1063 else {
1064 if (areawin->selects == 0) {
1065 if (!checkselect(LABEL))
1066 return;
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);
1081 changed = True;
1085 if (preselected == FALSE && eventmode != MOVE_MODE && eventmode != COPY_MODE)
1086 unselect_all();
1087 else
1088 draw_all_selected();
1090 if (curlabel == NULL)
1091 Wprintf("No labels chosen to reanchor");
1092 else if (changed) {
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)
1105 short j;
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;
1128 XPoint userpt;
1129 short *newselect;
1131 unselect_all();
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. */
1154 /* */
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 */
1158 /* endpoint. */
1159 /*----------------------------------------------------------------------*/
1161 void updatepath(pathptr thepath)
1163 genericptr *ggen, *ngen;
1164 short locparts, cycle;
1165 pointselect *cptr;
1166 polyptr thispoly;
1167 splineptr thisspline;
1169 for (ggen = thepath->plist; ggen < thepath->plist + thepath->parts; ggen++) {
1170 switch (ELEMENTTYPE(*ggen)) {
1171 case POLYGON:
1172 findconstrained(TOPOLY(ggen));
1173 break;
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)) {
1182 case POLYGON:
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;
1191 break;
1192 case SPLINE:
1193 thisspline = TOSPLINE(ggen);
1194 if (thisspline->cycle == NULL) continue;
1195 cycle = 3;
1196 for (cptr = thisspline->cycle;; cptr++) {
1197 if (cptr->number == cycle) break;
1198 if (cptr->flags & LASTENTRY) break;
1200 if (cptr->number != cycle) continue;
1201 break;
1203 addcycle(ngen, 0, cptr->flags & (EDITX | EDITY));
1204 switch (ELEMENTTYPE(*ngen)) {
1205 case POLYGON:
1206 findconstrained(TOPOLY(ngen));
1207 break;
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;
1214 ggen--) {
1215 ngen = (ggen == thepath->plist) ? thepath->plist + thepath->parts - 1 : ggen - 1;
1217 switch (ELEMENTTYPE(*ggen)) {
1218 case POLYGON:
1219 thispoly = TOPOLY(ggen);
1220 if (thispoly->cycle == NULL) continue;
1221 cycle = 0;
1222 for (cptr = thispoly->cycle;; cptr++) {
1223 if (cptr->number == cycle) break;
1224 if (cptr->flags & LASTENTRY) break;
1226 if (cptr->number != cycle) continue;
1227 break;
1228 case SPLINE:
1229 thisspline = TOSPLINE(ggen);
1230 if (thisspline->cycle == NULL) continue;
1231 cycle = 0;
1232 for (cptr = thisspline->cycle;; cptr++) {
1233 if (cptr->number == cycle) break;
1234 if (cptr->flags & LASTENTRY) break;
1236 if (cptr->number != cycle) continue;
1237 break;
1239 switch (ELEMENTTYPE(*ngen)) {
1240 case POLYGON:
1241 addcycle(ngen, TOPOLY(ngen)->number - 1, cptr->flags & (EDITX | EDITY));
1242 break;
1243 case SPLINE:
1244 addcycle(ngen, 3, cptr->flags & (EDITX | EDITY));
1245 break;
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;
1261 newarc->radius = 0;
1262 newarc->yaxis = 0;
1263 newarc->angle1 = 0;
1264 newarc->angle2 = 360;
1265 newarc->passed = NULL;
1266 newarc->cycle = NULL;
1267 calcarc(newarc);
1270 /*-------------------------------------*/
1271 /* Button handler when creating an arc */
1272 /*-------------------------------------*/
1274 void arcbutton(int x, int y)
1276 arcptr *newarc;
1277 XPoint userpt;
1278 short *newselect;
1280 unselect_all();
1281 NEW_ARC(newarc, topobject);
1282 newselect = allocselect();
1283 *newselect = topobject->parts - 1;
1284 snap(x, y, &userpt);
1285 saveratio = 1.0;
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)
1303 XPoint newpos;
1304 arcptr newarc;
1305 double adjrat;
1306 short cycle;
1307 UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
1309 newarc = TOARC(EDITPART);
1311 newpos = UGetCursorPos();
1312 u2u_snap(&newpos);
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);
1324 if (cycle == 1) {
1325 if (tmpang > newarc->angle2) tmpang -= 360;
1326 else if (newarc->angle2 - tmpang > 360) newarc->angle2 -= 360;
1328 else {
1329 if (tmpang < newarc->angle1) tmpang += 360;
1330 else if (tmpang - newarc->angle1 > 360) newarc->angle1 += 360;
1332 *angleptr = tmpang;
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;
1347 else {
1348 newarc->yaxis = wirelength(&newpos, &(newarc->position));
1349 saveratio = (double)newarc->yaxis / (double)newarc->radius;
1352 calcarc(newarc);
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);
1360 flusharea();
1363 /*--------------------------------------*/
1364 /* Sane default values for a polygon */
1365 /*--------------------------------------*/
1367 void polydefaults(polyptr newpoly, int number, int x, int y)
1369 pointlist pointptr;
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;
1377 if (number == 0)
1378 newpoly->points = NULL;
1379 else {
1380 newpoly->points = (pointlist) malloc(number * sizeof(XPoint));
1382 for (pointptr = newpoly->points; pointptr < newpoly->points + number;
1383 pointptr++) {
1384 pointptr->x = x;
1385 pointptr->y = y;
1390 /*------------------------------------*/
1391 /* Button handler when creating a box */
1392 /*------------------------------------*/
1394 void boxbutton(int x, int y)
1396 polyptr *newbox;
1397 XPoint userpt;
1398 short *newselect;
1400 unselect_all();
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)
1421 XPoint newpos;
1422 polyptr newbox;
1423 pointlist pointptr;
1424 UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
1426 newbox = TOPOLY(EDITPART);
1427 newpos = UGetCursorPos();
1428 u2u_snap(&newpos);
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;
1442 flusharea();
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;
1457 polyptr newwire;
1458 UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
1460 newwire = TOPOLY(EDITPART);
1462 if (areawin->attachto >= 0) {
1463 upos = UGetCursorPos();
1464 findattach(&newpos, NULL, &upos);
1466 else {
1467 newpos = UGetCursorPos();
1468 u2u_snap(&newpos);
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);
1486 flusharea();
1489 /*--------------------------*/
1490 /* Start drawing a polygon. */
1491 /*--------------------------*/
1493 void startwire(XPoint *userpt)
1495 polyptr *newwire;
1496 pointlist pointptr;
1497 short *newselect;
1499 unselect_all();
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) */
1527 /* */
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;
1536 short cycle;
1537 short lflags, nflags;
1538 short lcyc, ncyc;
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;
1552 while (1) {
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 /* Avoid attempting to manipulate degenerate points. */
1571 if (lpt != NULL && lpt->x == savept->x && lpt->y == savept->y) return;
1572 if (npt != NULL && npt->x == savept->x && npt->y == savept->y) return;
1574 /* two-point polygons (lines) are a degenerate case in RHOMBOID edit mode */
1576 if (areawin->boxedit != MANHATTAN && lastpoly->number <= 2) return;
1578 /* This is complicated but logical: in MANHATTAN mode, boxes maintain */
1579 /* box shape. In RHOMBOID modes, parallelagrams maintain shape. The */
1580 /* "savedir" variable determines which coordinate(s) of which point(s) */
1581 /* should track along with the edit point. */
1583 if (areawin->boxedit != RHOMBOIDY) {
1584 if (lpt != NULL) {
1585 if (lpt->y == savept->y) {
1586 lflags |= EDITY;
1587 if (areawin->boxedit == RHOMBOIDX && lpt->x != savept->x)
1588 lflags |= EDITX;
1589 else if (areawin->boxedit == RHOMBOIDA && npt != NULL) {
1590 if (npt->y != savept->y) nflags |= EDITX;
1594 if (npt != NULL) {
1595 if (npt->y == savept->y) {
1596 nflags |= EDITY;
1597 if (areawin->boxedit == RHOMBOIDX && npt->x != savept->x)
1598 nflags |= EDITX;
1599 else if (areawin->boxedit == RHOMBOIDA && lpt != NULL) {
1600 if (lpt->y != savept->y) lflags |= EDITX;
1605 if (areawin->boxedit != RHOMBOIDX) {
1606 if (lpt != NULL) {
1607 if (lpt->x == savept->x) {
1608 lflags |= EDITX;
1609 if (areawin->boxedit == RHOMBOIDY && lpt->y != savept->y)
1610 lflags |= EDITY;
1611 else if (areawin->boxedit == RHOMBOIDA && npt != NULL) {
1612 if (npt->x != savept->x) nflags |= EDITY;
1616 if (npt != NULL) {
1617 if (npt->x == savept->x) {
1618 nflags |= EDITX;
1619 if (areawin->boxedit == RHOMBOIDY && npt->y != savept->y)
1620 nflags |= EDITY;
1621 else if (areawin->boxedit == RHOMBOIDA && lpt != NULL) {
1622 if (lpt->x != savept->x) lflags |= EDITY;
1627 nptr = cptr + 1;
1628 if (lpt != NULL && lflags != 0) {
1629 addcycle((genericptr *)(&lastpoly), lcyc, lflags);
1630 cptr = nptr = lastpoly->cycle;
1632 if (npt != NULL && nflags != 0) {
1633 addcycle((genericptr *)(&lastpoly), ncyc, nflags);
1634 cptr = nptr = lastpoly->cycle;
1637 else
1638 nptr = cptr + 1;
1639 if (cptr->flags & LASTENTRY) break;
1640 cptr = nptr;
1644 /*------------------------------------------------------*/
1645 /* Track movement of arc, spline, or polygon segments */
1646 /* during edit mode */
1647 /*------------------------------------------------------*/
1649 void trackelement(xcWidget w, caddr_t clientdata, caddr_t calldata)
1651 XPoint newpos, *curpt;
1652 short *selobj;
1653 pointselect *cptr;
1654 int deltax, deltay;
1655 UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
1657 newpos = UGetCursorPos();
1658 u2u_snap(&newpos);
1660 /* force attachment if required */
1661 if (areawin->attachto >= 0) {
1662 XPoint apos;
1663 findattach(&apos, NULL, &newpos);
1664 newpos = apos;
1667 if (areawin->save.x == newpos.x && areawin->save.y == newpos.y) return;
1669 /* Find the reference point */
1671 cptr = getrefpoint(TOGENERIC(EDITPART), &curpt);
1672 switch(ELEMENTTYPE(TOGENERIC(EDITPART))) {
1673 case POLYGON:
1674 if (cptr == NULL)
1675 curpt = TOPOLY(EDITPART)->points;
1676 break;
1677 case SPLINE:
1678 if (cptr == NULL)
1679 curpt = &(TOSPLINE(EDITPART)->ctrl[0]);
1680 break;
1681 case ARC:
1682 curpt = &(TOARC(EDITPART)->position);
1683 break;
1684 case OBJINST:
1685 curpt = &(TOOBJINST(EDITPART)->position);
1686 break;
1687 case GRAPHIC:
1688 curpt = &(TOGRAPHIC(EDITPART)->position);
1689 break;
1691 deltax = newpos.x - curpt->x;
1692 deltay = newpos.y - curpt->y;
1694 /* Now adjust all edited elements relative to the reference point */
1695 for (selobj = areawin->selectlist; selobj < areawin->selectlist +
1696 areawin->selects; selobj++) {
1697 if (eventmode == ARC_MODE || eventmode == EARC_MODE)
1698 editpoints(SELTOGENERICPTR(selobj), deltax, deltay);
1699 else if (eventmode == SPLINE_MODE || eventmode == ESPLINE_MODE)
1700 editpoints(SELTOGENERICPTR(selobj), deltax, deltay);
1701 else if (eventmode == BOX_MODE || eventmode == EPOLY_MODE
1702 || eventmode == WIRE_MODE)
1703 editpoints(SELTOGENERICPTR(selobj), deltax, deltay);
1704 else if (eventmode == EPATH_MODE)
1705 editpoints(SELTOGENERICPTR(selobj), deltax, deltay);
1708 if (eventmode == ARC_MODE || eventmode == EARC_MODE)
1709 arc_mode_draw(xcDRAW_EDIT, TOARC(EDITPART));
1710 else if (eventmode == SPLINE_MODE || eventmode == ESPLINE_MODE)
1711 spline_mode_draw(xcDRAW_EDIT, TOSPLINE(EDITPART));
1712 else if (eventmode == BOX_MODE || eventmode == EPOLY_MODE
1713 || eventmode == WIRE_MODE)
1714 poly_mode_draw(xcDRAW_EDIT, TOPOLY(EDITPART));
1715 else if (eventmode == EPATH_MODE)
1716 path_mode_draw(xcDRAW_EDIT, TOPATH(EDITPART));
1718 printpos(newpos.x, newpos.y);
1719 areawin->save.x = newpos.x;
1720 areawin->save.y = newpos.y;
1722 flusharea();
1725 /*-------------------------------------------------*/
1726 /* Determine values of endpoints of an element */
1727 /*-------------------------------------------------*/
1729 void setendpoint(short *scnt, short direc, XPoint **endpoint, XPoint *arcpoint)
1731 genericptr *sptr = topobject->plist + (*scnt);
1733 switch(ELEMENTTYPE(*sptr)) {
1734 case POLYGON:
1735 if (direc)
1736 *endpoint = TOPOLY(sptr)->points + TOPOLY(sptr)->number - 1;
1737 else
1738 *endpoint = TOPOLY(sptr)->points;
1739 break;
1740 case SPLINE:
1741 if (direc)
1742 *endpoint = &(TOSPLINE(sptr)->ctrl[3]);
1743 else
1744 *endpoint = &(TOSPLINE(sptr)->ctrl[0]);
1745 break;
1746 case ARC:
1747 if (direc) {
1748 arcpoint->x = (short)(TOARC(sptr)->points[TOARC(sptr)->number - 1].x
1749 + 0.5);
1750 arcpoint->y = (short)(TOARC(sptr)->points[TOARC(sptr)->number - 1].y
1751 + 0.5);
1753 else {
1754 arcpoint->x = (short)(TOARC(sptr)->points[0].x + 0.5);
1755 arcpoint->y = (short)(TOARC(sptr)->points[0].y + 0.5);
1757 *endpoint = arcpoint;
1758 break;
1762 /*------------------------------------------------------------*/
1763 /* Reverse points in a point list */
1764 /*------------------------------------------------------------*/
1766 void reversepoints(XPoint *plist, short number)
1768 XPoint hold, *ppt;
1769 XPoint *pend = plist + number - 1;
1770 short hnum = number >> 1;
1772 for (ppt = plist; ppt < plist + hnum; ppt++, pend--) {
1773 hold.x = ppt->x;
1774 hold.y = ppt->y;
1775 ppt->x = pend->x;
1776 ppt->y = pend->y;
1777 pend->x = hold.x;
1778 pend->y = hold.y;
1782 /*------------------------------------------------------------*/
1783 /* Same as above for floating-point positions */
1784 /*------------------------------------------------------------*/
1786 void reversefpoints(XfPoint *plist, short number)
1788 XfPoint hold, *ppt;
1789 XfPoint *pend = plist + number - 1;
1790 short hnum = number >> 1;
1792 for (ppt = plist; ppt < plist + hnum; ppt++, pend--) {
1793 hold.x = ppt->x;
1794 hold.y = ppt->y;
1795 ppt->x = pend->x;
1796 ppt->y = pend->y;
1797 pend->x = hold.x;
1798 pend->y = hold.y;
1802 /*--------------------------------------------------------------*/
1803 /* Permanently remove an element from the topobject plist */
1804 /* add = 1 if plist has (parts + 1) elements */
1805 /*--------------------------------------------------------------*/
1807 void freepathparts(short *selectobj, short add)
1809 genericptr *oldelem = topobject->plist + (*selectobj);
1810 switch(ELEMENTTYPE(*oldelem)) {
1811 case POLYGON:
1812 free((TOPOLY(oldelem))->points);
1813 break;
1815 free(*oldelem);
1816 removep(selectobj, add);
1819 /*--------------------------------------------------------------*/
1820 /* Remove a part from an object */
1821 /* add = 1 if plist has (parts + 1) elements */
1822 /*--------------------------------------------------------------*/
1824 void removep(short *selectobj, short add)
1826 genericptr *oldelem = topobject->plist + (*selectobj);
1828 for (++oldelem; oldelem < topobject->plist + topobject->parts + add; oldelem++)
1829 *(oldelem - 1) = *oldelem;
1831 topobject->parts--;
1834 /*-------------------------------------------------*/
1835 /* Break a path into its constituent components */
1836 /*-------------------------------------------------*/
1838 void unjoin()
1840 short *selectobj;
1841 genericptr *genp, *newg;
1842 pathptr oldpath;
1843 polyptr oldpoly, *newpoly;
1844 Boolean preselected;
1845 short i, cycle;
1847 if (areawin->selects == 0) {
1848 select_element(PATH | POLYGON);
1849 preselected = FALSE;
1851 else preselected = TRUE;
1853 if (areawin->selects == 0) {
1854 Wprintf("No objects selected.");
1855 return;
1858 /* for each selected path or polygon */
1860 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
1861 + areawin->selects; selectobj++) {
1862 SetForeground(dpy, areawin->gc, BACKGROUND);
1863 if (SELECTTYPE(selectobj) == PATH) {
1864 oldpath = SELTOPATH(selectobj);
1866 /* undraw the path */
1868 UDrawPath(oldpath, xobjs.pagelist[areawin->page]->wirewidth);
1870 /* move components to the top level */
1872 topobject->plist = (genericptr *)realloc(topobject->plist,
1873 (topobject->parts + oldpath->parts) * sizeof(genericptr));
1874 newg = topobject->plist + topobject->parts;
1875 for (genp = oldpath->plist; genp < oldpath->plist +
1876 oldpath->parts; genp++, newg++) {
1877 *newg = *genp;
1879 topobject->parts += oldpath->parts;
1881 /* remove the path object and revise the selectlist */
1883 freepathparts(selectobj, 0);
1884 reviseselect(areawin->selectlist, areawin->selects, selectobj);
1886 else if (SELECTTYPE(selectobj) == POLYGON) {
1887 /* Method to break a polygon, in lieu of the edit-mode */
1888 /* polygon break that was removed. */
1889 oldpoly = SELTOPOLY(selectobj);
1890 UDrawPolygon(oldpoly, xobjs.pagelist[areawin->page]->wirewidth);
1892 /* Get the point nearest the cursor, and break at that point */
1893 cycle = closepoint(oldpoly, &areawin->save);
1894 if (cycle > 0 && cycle < (oldpoly->number - 1)) {
1895 NEW_POLY(newpoly, topobject);
1896 polycopy(*newpoly, oldpoly);
1897 for (i = cycle; i < oldpoly->number; i++)
1898 (*newpoly)->points[i - cycle] = (*newpoly)->points[i];
1899 oldpoly->number = cycle + 1;
1900 (*newpoly)->number = (*newpoly)->number - cycle;
1904 if (!preselected) clearselects();
1905 drawarea(NULL, NULL, NULL);
1908 /*-------------------------------------------------*/
1909 /* Test if two points are near each other */
1910 /*-------------------------------------------------*/
1912 Boolean neartest(XPoint *point1, XPoint *point2)
1914 short diff[2];
1916 diff[0] = point1->x - point2->x;
1917 diff[1] = point1->y - point2->y;
1918 diff[0] = abs(diff[0]);
1919 diff[1] = abs(diff[1]);
1921 if (diff[0] <= 2 && diff[1] <= 2) return True;
1922 else return False;
1926 /*-------------------------------------------------*/
1927 /* Join stuff together */
1928 /*-------------------------------------------------*/
1930 void join()
1932 short *selectobj;
1933 polyptr *newpoly, nextwire;
1934 pathptr *newpath;
1935 genericptr *pgen;
1936 short *scount, *sptr, *sptr2, *direc, *order;
1937 short ordered, startpt = 0;
1938 short numpolys, numlabels, numpoints, polytype;
1939 int polycolor;
1940 float polywidth;
1941 XPoint *testpoint, *testpoint2, *begpoint, *endpoint, arcpoint[4];
1942 XPoint *begpoint2, *endpoint2;
1943 Boolean allpolys = True;
1944 objectptr delobj;
1946 numpolys = numlabels = 0;
1947 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
1948 + areawin->selects; selectobj++) {
1949 if (SELECTTYPE(selectobj) == POLYGON) {
1950 /* arbitrary: keep style of last polygon in selectlist */
1951 polytype = SELTOPOLY(selectobj)->style;
1952 polywidth = SELTOPOLY(selectobj)->width;
1953 polycolor = SELTOPOLY(selectobj)->color;
1954 numpolys++;
1956 else if (SELECTTYPE(selectobj) == SPLINE) {
1957 polytype = SELTOSPLINE(selectobj)->style;
1958 polywidth = SELTOSPLINE(selectobj)->width;
1959 polycolor = SELTOSPLINE(selectobj)->color;
1960 numpolys++;
1961 allpolys = False;
1963 else if (SELECTTYPE(selectobj) == ARC) {
1964 polytype = SELTOARC(selectobj)->style;
1965 polywidth = SELTOARC(selectobj)->width;
1966 polycolor = SELTOARC(selectobj)->color;
1967 numpolys++;
1968 allpolys = False;
1970 else if (SELECTTYPE(selectobj) == LABEL)
1971 numlabels++;
1973 if ((numpolys == 0) && (numlabels == 0)) {
1974 Wprintf("No elements selected for joining.");
1975 return;
1977 else if ((numpolys == 1) || (numlabels == 1)) {
1978 Wprintf("Only one element: nothing to join to.");
1979 return;
1981 else if ((numpolys > 1) && (numlabels > 1)) {
1982 Wprintf("Selection mixes labels and line segments. Ignoring.");
1983 return;
1985 else if (numlabels > 0) {
1986 joinlabels();
1987 return;
1990 /* scount is a table of element numbers */
1991 /* order is an ordered table of end-to-end elements */
1992 /* direc is an ordered table of path directions (0=same as element, */
1993 /* 1=reverse from element definition) */
1995 scount = (short *) malloc(numpolys * sizeof(short));
1996 order = (short *) malloc(numpolys * sizeof(short));
1997 direc = (short *) malloc(numpolys * sizeof(short));
1998 sptr = scount;
1999 numpoints = 1;
2001 /* make a record of the element instances involved */
2003 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
2004 + areawin->selects; selectobj++) {
2005 if (SELECTTYPE(selectobj) == POLYGON) {
2006 numpoints += SELTOPOLY(selectobj)->number - 1;
2007 *(sptr++) = *selectobj;
2009 else if (SELECTTYPE(selectobj) == SPLINE || SELECTTYPE(selectobj) == ARC)
2010 *(sptr++) = *selectobj;
2013 /* Sort the elements by sorting the scount array: */
2014 /* Loop through each point as starting point in case of strangely connected */
2015 /* structures. . . for normal structures it should break out on the first */
2016 /* loop (startpt = 0). */
2018 for (startpt = 0; startpt < numpolys; startpt++) {
2020 /* set first in ordered list */
2022 direc[0] = 0;
2023 order[0] = *(scount + startpt);
2025 for (ordered = 0; ordered < numpolys - 1; ordered++) {
2027 setendpoint(order + ordered, (1 ^ direc[ordered]), &endpoint2, &arcpoint[0]);
2028 setendpoint(order, (0 ^ direc[0]), &begpoint2, &arcpoint[1]);
2030 for (sptr = scount; sptr < scount + numpolys; sptr++) {
2032 /* don't compare with things already in the list */
2033 for (sptr2 = order; sptr2 <= order + ordered; sptr2++)
2034 if (*sptr == *sptr2) break;
2035 if (sptr2 != order + ordered + 1) continue;
2037 setendpoint(sptr, 0, &begpoint, &arcpoint[2]);
2038 setendpoint(sptr, 1, &endpoint, &arcpoint[3]);
2040 /* four cases of matching endpoint of one element to another */
2042 if (neartest(begpoint, endpoint2)) {
2043 order[ordered + 1] = *sptr;
2044 direc[ordered + 1] = 0;
2045 break;
2047 else if (neartest(endpoint, endpoint2)) {
2048 order[ordered + 1] = *sptr;
2049 direc[ordered + 1] = 1;
2050 break;
2052 else if (neartest(begpoint, begpoint2)) {
2053 for (sptr2 = order + ordered + 1; sptr2 > order; sptr2--)
2054 *sptr2 = *(sptr2 - 1);
2055 for (sptr2 = direc + ordered + 1; sptr2 > direc; sptr2--)
2056 *sptr2 = *(sptr2 - 1);
2057 order[0] = *sptr;
2058 direc[0] = 1;
2059 break;
2061 else if (neartest(endpoint, begpoint2)) {
2062 for (sptr2 = order + ordered + 1; sptr2 > order; sptr2--)
2063 *sptr2 = *(sptr2 - 1);
2064 for (sptr2 = direc + ordered + 1; sptr2 > direc; sptr2--)
2065 *sptr2 = *(sptr2 - 1);
2066 order[0] = *sptr;
2067 direc[0] = 0;
2068 break;
2071 if (sptr == scount + numpolys) break;
2073 if (ordered == numpolys - 1) break;
2076 if (startpt == numpolys) {
2077 Wprintf("Cannot join: Too many free endpoints");
2078 free(order);
2079 free(direc);
2080 free(scount);
2081 return;
2084 /* create the new polygon or path */
2086 if (allpolys) {
2087 NEW_POLY(newpoly, topobject);
2089 (*newpoly)->number = numpoints;
2090 (*newpoly)->points = (pointlist) malloc(numpoints * sizeof(XPoint));
2091 (*newpoly)->width = polywidth;
2092 (*newpoly)->style = polytype;
2093 (*newpoly)->color = polycolor;
2094 (*newpoly)->passed = NULL;
2095 (*newpoly)->cycle = NULL;
2097 /* insert the points into the new polygon */
2099 testpoint2 = (*newpoly)->points;
2100 for (sptr = order; sptr < order + numpolys; sptr++) {
2101 nextwire = SELTOPOLY(sptr);
2102 if (*(direc + (short)(sptr - order)) == 0) {
2103 for (testpoint = nextwire->points; testpoint < nextwire->points +
2104 nextwire->number - 1; testpoint++) {
2105 testpoint2->x = testpoint->x;
2106 testpoint2->y = testpoint->y;
2107 testpoint2++;
2110 else {
2111 for (testpoint = nextwire->points + nextwire->number - 1; testpoint
2112 > nextwire->points; testpoint--) {
2113 testpoint2->x = testpoint->x;
2114 testpoint2->y = testpoint->y;
2115 testpoint2++;
2119 /* pick up the last point */
2120 testpoint2->x = testpoint->x;
2121 testpoint2->y = testpoint->y;
2123 /* delete the old elements from the list */
2125 register_for_undo(XCF_Wire, UNDO_MORE, areawin->topinstance, *newpoly);
2127 delobj = delete_element(areawin->topinstance, areawin->selectlist,
2128 areawin->selects, NORMAL);
2129 register_for_undo(XCF_Delete, UNDO_DONE, areawin->topinstance,
2130 delobj, NORMAL);
2133 else { /* create a path */
2135 NEW_PATH(newpath, topobject);
2136 (*newpath)->style = polytype;
2137 (*newpath)->color = polycolor;
2138 (*newpath)->width = polywidth;
2139 (*newpath)->parts = 0;
2140 (*newpath)->plist = (genericptr *) malloc(sizeof(genericptr));
2141 (*newpath)->passed = NULL;
2143 /* copy the elements from the top level into the path structure */
2145 for (sptr = order; sptr < order + numpolys; sptr++) {
2146 genericptr *oldelem = topobject->plist + *sptr;
2147 genericptr *newelem;
2149 switch (ELEMENTTYPE(*oldelem)) {
2150 case POLYGON: {
2151 polyptr copypoly = TOPOLY(oldelem);
2152 polyptr *newpoly;
2153 NEW_POLY(newpoly, (*newpath));
2154 polycopy(*newpoly, copypoly);
2155 } break;
2156 case ARC: {
2157 arcptr copyarc = TOARC(oldelem);
2158 arcptr *newarc;
2159 NEW_ARC(newarc, (*newpath));
2160 arccopy(*newarc, copyarc);
2161 } break;
2162 case SPLINE: {
2163 splineptr copyspline = TOSPLINE(oldelem);
2164 splineptr *newspline;
2165 NEW_SPLINE(newspline, (*newpath));
2166 splinecopy(*newspline, copyspline);
2167 } break;
2169 newelem = (*newpath)->plist + (*newpath)->parts - 1;
2171 /* reverse point order if necessary */
2173 if (*(direc + (short)(sptr - order)) == 1) {
2174 switch (ELEMENTTYPE(*newelem)) {
2175 case POLYGON:
2176 reversepoints(TOPOLY(newelem)->points, TOPOLY(newelem)->number);
2177 break;
2178 case ARC:
2179 TOARC(newelem)->radius = -TOARC(newelem)->radius;
2180 break;
2181 case SPLINE:
2182 reversepoints(TOSPLINE(newelem)->ctrl, 4);
2183 calcspline(TOSPLINE(newelem));
2184 break;
2188 /* decompose arcs into bezier curves */
2189 if (ELEMENTTYPE(*newelem) == ARC)
2190 decomposearc(*newpath, NULL);
2193 /* delete the old elements from the list */
2195 register_for_undo(XCF_Join, UNDO_MORE, areawin->topinstance, *newpath);
2197 delobj = delete_element(areawin->topinstance, scount, numpolys, NORMAL);
2199 register_for_undo(XCF_Delete, UNDO_DONE, areawin->topinstance,
2200 delobj, NORMAL);
2202 /* Remove the path parts from the selection list and add the path */
2203 clearselects();
2204 selectobj = allocselect();
2205 for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts;
2206 pgen++) {
2207 if ((TOPATH(pgen)) == (*newpath)) {
2208 *selectobj = (short)(pgen - topobject->plist);
2209 break;
2214 /* clean up */
2216 incr_changes(topobject);
2217 /* Do not clear the selection, to be consistent with all the */
2218 /* other actions that clear only when something has not been */
2219 /* preselected before the action. Elements must be selected */
2220 /* prior to the "join" action, by necessity. */
2221 free(scount);
2222 free(order);
2223 free(direc);
2226 /*----------------------------------------------*/
2227 /* Add a new point to a polygon */
2228 /*----------------------------------------------*/
2230 void poly_add_point(polyptr thispoly, XPoint *newpoint) {
2231 XPoint *tpoint;
2233 thispoly->number++;
2234 thispoly->points = (XPoint *)realloc(thispoly->points,
2235 thispoly->number * sizeof(XPoint));
2236 tpoint = thispoly->points + thispoly->number - 1;
2237 tpoint->x = newpoint->x;
2238 tpoint->y = newpoint->y;
2241 /*-------------------------------------------------*/
2242 /* ButtonPress handler while a wire is being drawn */
2243 /*-------------------------------------------------*/
2245 void wire_op(int op, int x, int y)
2247 XPoint userpt, *tpoint;
2248 polyptr newwire;
2250 snap(x, y, &userpt);
2252 newwire = TOPOLY(EDITPART);
2254 if (areawin->attachto >= 0) {
2255 XPoint apos;
2256 findattach(&apos, NULL, &userpt);
2257 userpt = apos;
2258 areawin->attachto = -1;
2260 else {
2261 if (areawin->manhatn) manhattanize(&userpt, newwire, -1, TRUE);
2264 tpoint = newwire->points + newwire->number - 1;
2265 tpoint->x = userpt.x;
2266 tpoint->y = userpt.y;
2268 /* cancel wire operation completely */
2269 if (op == XCF_Cancel) {
2270 free(newwire->points);
2271 free(newwire);
2272 newwire = NULL;
2273 eventmode = NORMAL_MODE;
2274 topobject->parts--;
2277 /* back up one point; prevent length zero wires */
2278 else if ((op == XCF_Cancel_Last) || ((tpoint - 1)->x == userpt.x &&
2279 (tpoint - 1)->y == userpt.y)) {
2280 if (newwire->number <= 2) {
2281 free(newwire->points);
2282 free(newwire);
2283 newwire = NULL;
2284 eventmode = NORMAL_MODE;
2285 topobject->parts--;
2287 else {
2288 if (--newwire->number == 2) newwire->style = UNCLOSED |
2289 (areawin->style & (DASHED | DOTTED));
2293 if (newwire && (op == XCF_Wire || op == XCF_Continue_Element)) {
2294 if (newwire->number == 2)
2295 newwire->style = areawin->style;
2296 poly_add_point(newwire, &userpt);
2298 else if ((newwire == NULL) || op == XCF_Finish_Element || op == XCF_Cancel) {
2299 xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
2300 (xcEventHandler)trackwire, NULL);
2303 if (newwire) {
2304 if (op == XCF_Finish_Element) {
2306 /* If the last points are the same, remove all redundant ones. */
2307 /* This avoids the problem of extra points when people do left */
2308 /* click followed by middle click to finish (the redundant way */
2309 /* a lot of drawing programs work). */
2311 XPoint *t2pt;
2312 while (newwire->number > 2) {
2313 tpoint = newwire->points + newwire->number - 1;
2314 t2pt = newwire->points + newwire->number - 2;
2315 if (tpoint->x != t2pt->x || tpoint->y != t2pt->y)
2316 break;
2317 newwire->number--;
2320 incr_changes(topobject);
2321 if (!nonnetwork(newwire)) invalidate_netlist(topobject);
2322 register_for_undo(XCF_Wire, UNDO_MORE, areawin->topinstance, newwire);
2323 poly_mode_draw(xcDRAW_FINAL, newwire);
2325 else
2326 poly_mode_draw(xcDRAW_EDIT, newwire);
2327 if (op == XCF_Cancel_Last)
2328 checkwarp(newwire->points + newwire->number - 1);
2331 if (op == XCF_Finish_Element) {
2332 eventmode = NORMAL_MODE;
2333 singlebbox(EDITPART);
2337 /*-------------------------------------------------------------------------*/
2338 /* Helper functions for the xxx_mode_draw functions */
2339 /* Functions to be used around the drawing of the edited element */
2340 /* begin_event_mode_drawing starts double buffering and copies the */
2341 /* fixed pixmap. end_event_mode stops the double buffering and displays */
2342 /* everything on screen. */
2343 /*-------------------------------------------------------------------------*/
2345 #ifndef HAVE_CAIRO
2346 static Window old_win;
2347 #endif /* !HAVE_CAIRO */
2349 static void begin_event_mode_drawing(void)
2351 /* Start double buffering */
2352 #ifdef HAVE_CAIRO
2353 cairo_identity_matrix(areawin->cr);
2354 cairo_translate(areawin->cr, areawin->panx, areawin->pany);
2355 cairo_push_group(areawin->cr);
2356 #else /* HAVE_CAIRO */
2357 old_win = areawin->window;
2358 areawin->window = (Window) dbuf;
2359 #endif /* HAVE_CAIRO */
2361 /* Copy background pixmap with the element(s) not currently being edited */
2362 #ifdef HAVE_CAIRO
2363 if (areawin->panx || areawin->pany) {
2364 SetForeground(dpy, areawin->gc, BACKGROUND);
2365 cairo_paint(areawin->cr);
2367 cairo_set_source(areawin->cr, areawin->fixed_pixmap);
2368 cairo_paint(areawin->cr);
2369 #else /* HAVE_CAIRO */
2370 if (areawin->panx || areawin->pany) {
2371 int x = max(0, -areawin->panx);
2372 int y = max(0, -areawin->pany);
2373 unsigned int w = areawin->width - x;
2374 unsigned int h = areawin->height - y;
2375 SetForeground(dpy, areawin->gc, BACKGROUND);
2376 XSetFillStyle(dpy, areawin->gc, FillSolid);
2377 XFillRectangle(dpy, areawin->window, areawin->gc, 0, 0, areawin->width,
2378 areawin->height);
2379 XCopyArea(dpy, areawin->fixed_pixmap, areawin->window, areawin->gc,
2380 x, y, w, h, max(0, areawin->panx), max(0, areawin->pany));
2382 else
2383 XCopyArea(dpy, areawin->fixed_pixmap, areawin->window, areawin->gc, 0, 0,
2384 areawin->width, areawin->height, 0, 0);
2385 #endif /* HAVE_CAIRO */
2387 areawin->redraw_ongoing = True;
2388 newmatrix();
2391 static void end_event_mode_drawing(void)
2393 /* End double buffering */
2394 #ifdef HAVE_CAIRO
2395 cairo_pop_group_to_source(areawin->cr);
2396 cairo_paint(areawin->cr);
2397 #else /* HAVE_CAIRO */
2398 areawin->window = old_win;
2399 XCopyArea(dpy, dbuf, areawin->window, areawin->gc, 0, 0, areawin->width,
2400 areawin->height, 0, 0);
2401 #endif /* HAVE_CAIRO */
2403 areawin->redraw_ongoing = False;
2406 /*-------------------------------------------------------------------------*/
2407 /* Helper functions for the xxx_mode_draw functions */
2408 /* Functions to be used when finishing the element. The final state is */
2409 /* drawn into the fixed pixmap, which is show when the */
2410 /* end_event_mode_drawing_final is called */
2411 /* (Sorry about the name :-) ) */
2412 /*-------------------------------------------------------------------------*/
2414 static void begin_event_mode_drawing_final(void)
2416 #ifdef HAVE_CAIRO
2417 cairo_identity_matrix(areawin->cr);
2418 cairo_push_group(areawin->cr);
2419 cairo_set_source(areawin->cr, areawin->fixed_pixmap);
2420 cairo_paint(areawin->cr);
2421 #else /* HAVE_CAIRO */
2422 old_win = areawin->window;
2423 areawin->window = (Window) areawin->fixed_pixmap;
2424 #endif /* HAVE_CAIRO */
2426 areawin->redraw_ongoing = True;
2427 newmatrix();
2430 static void end_event_mode_drawing_final(void)
2432 #ifdef HAVE_CAIRO
2433 cairo_pattern_destroy(areawin->fixed_pixmap);
2434 areawin->fixed_pixmap = cairo_pop_group(areawin->cr);
2435 #else /* HAVE_CAIRO */
2436 areawin->window = old_win;
2437 #endif /* HAVE_CAIRO */
2439 /* Show fixed pixmap */
2440 #ifdef HAVE_CAIRO
2441 cairo_identity_matrix(areawin->cr);
2442 cairo_set_source(areawin->cr, areawin->fixed_pixmap);
2443 cairo_paint(areawin->cr);
2444 #else /* HAVE_CAIRO */
2445 XCopyArea(dpy, areawin->fixed_pixmap, areawin->window, areawin->gc, 0, 0,
2446 areawin->width, areawin->height, 0, 0);
2447 #endif /* HAVE_CAIRO */
2449 areawin->redraw_ongoing = False;
2452 /*-------------------------------------------------------------------------*/
2453 /* Helper function for the xxx_mode_draw functions */
2454 /* Hide all the selected elements and draw all the element not currently */
2455 /* being edited to fixed_pixmap */
2456 /*-------------------------------------------------------------------------*/
2458 static void draw_fixed_without_selection(void)
2460 int idx;
2461 for (idx = 0; idx < areawin->selects; idx++)
2462 SELTOGENERIC(&areawin->selectlist[idx])->type |= DRAW_HIDE;
2463 draw_fixed();
2464 for (idx = 0; idx < areawin->selects; idx++)
2465 SELTOGENERIC(&areawin->selectlist[idx])->type &= ~DRAW_HIDE;
2468 /*-------------------------------------------------------------------------*/
2469 /* generic xxx_mode_draw function, that handles most of the thing needed */
2470 /* for arcs, splines and paths. */
2471 /*-------------------------------------------------------------------------*/
2473 static void generic_mode_draw(xcDrawType type, generic *newgen,
2474 void (*decorations)(generic *newgen))
2476 int idx;
2478 switch (type) {
2479 case xcDRAW_INIT:
2480 case xcREDRAW_FORCED:
2481 draw_fixed_without_selection();
2482 /* fallthrough */
2484 case xcDRAW_EDIT:
2485 begin_event_mode_drawing();
2486 for (idx = 0; idx < areawin->selects; idx++) {
2487 int scolor = SELTOCOLOR(&areawin->selectlist[idx]);
2488 XcTopSetForeground(scolor);
2489 easydraw(areawin->selectlist[idx], scolor);
2491 if (decorations)
2492 (*decorations)(newgen);
2493 end_event_mode_drawing();
2494 break;
2496 case xcDRAW_FINAL:
2497 begin_event_mode_drawing_final();
2498 for (idx = 0; idx < areawin->selects; idx++) {
2499 int scolor = SELTOCOLOR(&areawin->selectlist[idx]);
2500 XcTopSetForeground(scolor);
2501 easydraw(areawin->selectlist[idx], scolor);
2503 end_event_mode_drawing_final();
2504 if (areawin->selects > 1) /* FIXME: Temp. fix for multiple selects */
2505 areawin->redraw_needed = True; /* Will be removed later on */
2506 break;
2508 case xcDRAW_EMPTY:
2509 /* Do not remove the empty begin/end. For cairo, this renders the */
2510 /* background with the fixed elements */
2511 begin_event_mode_drawing_final();
2512 end_event_mode_drawing_final();
2513 break;
2517 /*-------------------------------------------------------------------------*/
2518 /* Drawing function for ARC_MODE and EARC_MODE */
2519 /*-------------------------------------------------------------------------*/
2521 static void arc_mode_decorations(generic *newgen)
2523 UDrawXLine(((arc*) newgen)->position, areawin->save);
2526 void arc_mode_draw(xcDrawType type, arc *newarc)
2528 generic_mode_draw(type, (generic*) newarc, arc_mode_decorations);
2531 /*-------------------------------------------------------------------------*/
2532 /* Drawing function for SPLINE_MODE and ESPLINE_MODE */
2533 /*-------------------------------------------------------------------------*/
2535 static void spline_mode_decorations(generic *newgen)
2537 spline *newspline = (spline*) newgen;
2538 UDrawXLine(newspline->ctrl[0], newspline->ctrl[1]);
2539 UDrawXLine(newspline->ctrl[3], newspline->ctrl[2]);
2542 void spline_mode_draw(xcDrawType type, spline *newspline)
2544 generic_mode_draw(type, (generic*) newspline, spline_mode_decorations);
2547 /*-------------------------------------------------------------------------*/
2548 /* Drawing function for WIRE_MODE, BOX_MODE and EPOLY_MODE */
2549 /*-------------------------------------------------------------------------*/
2551 void poly_mode_draw(xcDrawType type, polygon *newpoly)
2553 generic_mode_draw(type, (generic*) newpoly, NULL);
2556 /*-------------------------------------------------------------------------*/
2557 /* Drawing function for EPATH_MODE */
2558 /*-------------------------------------------------------------------------*/
2560 static void path_mode_decorations(generic *newgen)
2562 genericptr *ggen;
2563 path *newpath = (path*) newgen;
2564 for (ggen = newpath->plist; ggen < newpath->plist + newpath->parts; ggen++) {
2565 if (ELEMENTTYPE(*ggen) == SPLINE) {
2566 spline *lastspline = TOSPLINE(ggen);
2567 UDrawXLine(lastspline->ctrl[0], lastspline->ctrl[1]);
2568 UDrawXLine(lastspline->ctrl[3], lastspline->ctrl[2]);
2573 void path_mode_draw(xcDrawType type, path *newpath)
2575 generic_mode_draw(type, (generic*) newpath, path_mode_decorations);
2578 /*-------------------------------------------------------------------------*/
2579 /* Drawing function for TEXT_MODE, CATTEXT_MODE and ETEXT_MODE */
2580 /*-------------------------------------------------------------------------*/
2582 static void text_mode_decorations(generic *newgen)
2584 UDrawTLine((label*) newgen);
2587 void text_mode_draw(xcDrawType type, label *newlabel)
2589 generic_mode_draw(type, (generic*) newlabel, text_mode_decorations);
2592 /*-------------------------------------------------------------------------*/
2593 /* Drawing function for SELAREA_MODE */
2594 /*-------------------------------------------------------------------------*/
2596 void selarea_mode_draw(xcDrawType type, void *unused)
2598 UNUSED(unused);
2600 switch (type) {
2601 case xcREDRAW_FORCED:
2602 draw_fixed();
2603 /* fallthrough */
2605 case xcDRAW_INIT:
2606 case xcDRAW_EDIT:
2607 begin_event_mode_drawing();
2608 draw_all_selected();
2609 UDrawBox(areawin->origin, areawin->save);
2610 end_event_mode_drawing();
2611 break;
2613 case xcDRAW_FINAL:
2614 case xcDRAW_EMPTY:
2615 /* No need for rendering the background, since it will be */
2616 /* overwritten by the select_area() function anyway */
2617 break;
2621 /*-------------------------------------------------------------------------*/
2622 /* Drawing function for RESCALE_MODE */
2623 /*-------------------------------------------------------------------------*/
2625 void rescale_mode_draw(xcDrawType type, void *unused)
2627 UNUSED(unused);
2629 switch (type) {
2630 case xcREDRAW_FORCED:
2631 draw_fixed();
2632 /* fallthrough */
2634 case xcDRAW_INIT:
2635 case xcDRAW_EDIT:
2636 begin_event_mode_drawing();
2637 UDrawRescaleBox(&areawin->save);
2638 end_event_mode_drawing();
2639 break;
2641 case xcDRAW_FINAL:
2642 case xcDRAW_EMPTY:
2643 /* No need for rendering the background, since it will be */
2644 /* overwritten by the select_area() function anyway */
2645 break;
2649 /*-------------------------------------------------------------------------*/
2650 /* Drawing function for CATMOVE_MODE, MOVE_MODE and COPY_MODE */
2651 /*-------------------------------------------------------------------------*/
2653 void move_mode_draw(xcDrawType type, void *unused)
2655 float wirewidth = xobjs.pagelist[areawin->page]->wirewidth;
2656 short *selectobj;
2657 genericptr *pgen;
2658 int idx;
2659 UNUSED(unused);
2661 switch (type) {
2662 case xcREDRAW_FORCED:
2663 case xcDRAW_INIT:
2664 draw_fixed_without_selection();
2665 /* fallthrough */
2667 case xcDRAW_EDIT:
2668 begin_event_mode_drawing();
2669 XTopSetForeground(SELECTCOLOR);
2670 for (idx = 0; idx < areawin->selects; idx++)
2671 easydraw(areawin->selectlist[idx], DOFORALL);
2672 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
2673 + areawin->selects; selectobj++) {
2674 if (SELECTTYPE(selectobj) == LABEL) {
2675 label *labelobj = SELTOLABEL(selectobj);
2676 if (labelobj->pin == False)
2677 UDrawX(labelobj);
2680 if (areawin->pinattach) {
2681 for (pgen = topobject->plist; pgen < topobject->plist +
2682 topobject->parts; pgen++) {
2683 if (ELEMENTTYPE(*pgen) == POLYGON) {
2684 polygon *cpoly = TOPOLY(pgen);
2685 if (cpoly->cycle != NULL)
2686 UDrawPolygon(cpoly, wirewidth);
2690 end_event_mode_drawing();
2691 break;
2693 case xcDRAW_FINAL:
2694 begin_event_mode_drawing_final();
2695 for (selectobj = areawin->selectlist; selectobj
2696 < areawin->selectlist + areawin->selects; selectobj++) {
2697 XTopSetForeground(SELTOCOLOR(selectobj));
2698 easydraw(*selectobj, DOFORALL);
2700 end_event_mode_drawing_final();
2701 break;
2703 case xcDRAW_EMPTY:
2704 /* Do not remove the empty begin/end. For cairo, this renders the */
2705 /* background with the fixed elements */
2706 begin_event_mode_drawing_final();
2707 end_event_mode_drawing_final();
2708 break;
2712 /*-------------------------------------------------------------------------*/
2713 /* Drawing function for ASSOC_MODE, EINST_MODE, (E)FONTCAT_MODE, PAN_MODE, */
2714 /* NORMAL_MODE, UNDO_MODE and CATALOG_MODE */
2715 /*-------------------------------------------------------------------------*/
2717 void normal_mode_draw(xcDrawType type, void *unused)
2719 UNUSED(unused);
2721 switch (type) {
2722 case xcDRAW_INIT:
2723 case xcREDRAW_FORCED:
2724 draw_fixed_without_selection();
2725 /* fallthrough */
2727 case xcDRAW_EDIT:
2728 begin_event_mode_drawing();
2730 /* draw the highlighted netlist, if any */
2731 if (checkvalid(topobject) != -1)
2732 if (topobject->highlight.netlist != NULL)
2733 highlightnetlist(topobject, areawin->topinstance, 1);
2735 if ((areawin->selects == 1) && SELECTTYPE(areawin->selectlist) == LABEL
2736 && areawin->textend > 0 && areawin->textpos > areawin->textend) {
2737 labelptr drawlabel = SELTOLABEL(areawin->selectlist);
2738 UDrawString(drawlabel, DOSUBSTRING, areawin->topinstance);
2740 else if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE)
2741 draw_all_selected();
2742 end_event_mode_drawing();
2743 break;
2745 case xcDRAW_FINAL:
2746 break;
2748 case xcDRAW_EMPTY:
2749 break;