1 /*-------------------------------------------------------------------------*/
2 /* events.c --- xcircuit routines handling Xevents and Callbacks */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-------------------------------------------------------------------------*/
5
6 /*-------------------------------------------------------------------------*/
7 /* written by Tim Edwards, 8/13/93 */
8 /*-------------------------------------------------------------------------*/
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 #include <ctype.h>
15
16 #ifndef XC_WIN32
17 #include <X11/Intrinsic.h>
18 #include <X11/StringDefs.h>
19 #define XK_MISCELLANY
20 #define XK_LATIN1
21 #include <X11/keysymdef.h>
22 #else
23 #ifdef TCL_WRAPPER
24 #define XK_MISCELLANY
25 #define XK_LATIN1
26 #include <X11/keysymdef.h>
27 #endif
28 #endif
29
30 #ifdef HAVE_CAIRO
31 #include <cairo/cairo-xlib.h>
32 #endif
33
34 /*-------------------------------------------------------------------------*/
35 /* Local includes */
36 /*-------------------------------------------------------------------------*/
37
38 #ifdef TCL_WRAPPER
39 #include <tk.h>
40 #endif
41
42 #include "xcircuit.h"
43 #include "colordefs.h"
44
45 #define HOLD_MASK (Mod4Mask << 16)
46
47 /*----------------------------------------------------------------------*/
48 /* Function prototype declarations */
49 /*----------------------------------------------------------------------*/
50 #include "prototypes.h"
51
52 /*-------------------------------------------------------------------------*/
53 /* Global Variable definitions */
54 /*-------------------------------------------------------------------------*/
55
56 extern XtAppContext app;
57 extern Display *dpy;
58 extern Cursor appcursors[NUM_CURSORS];
59 extern Globaldata xobjs;
60 extern XCWindowData *areawin;
61 extern ApplicationData appdata;
62 extern colorindex *colorlist;
63 extern short popups;
64 extern int pressmode;
65 extern xcWidget message2, top;
66 extern char _STR[150], _STR2[250];
67 extern short beeper;
68 extern double saveratio;
69 extern u_char texttype;
70 extern aliasptr aliastop;
71
72 #ifdef TCL_WRAPPER
73 extern Tcl_Interp *xcinterp;
74 #else
75 extern short help_up;
76 #endif
77
78 /* double buffer */
79 #if !defined(HAVE_CAIRO)
80 Pixmap dbuf = (Pixmap)NULL;
81 #endif
82
83 Boolean was_preselected;
84
85 /*----------------------------------------------------------------------------*/
86 /* Edit Object pushing and popping. */
87 /*----------------------------------------------------------------------------*/
88
recursefind(objectptr parent,objectptr suspect)89 Boolean recursefind(objectptr parent, objectptr suspect)
90 {
91 genericptr *shell;
92
93 if (parent == suspect) return True;
94
95 for (shell = parent->plist; shell < parent->plist + parent->parts; shell++)
96 if (IS_OBJINST(*shell))
97 if (recursefind(TOOBJINST(shell)->thisobject, suspect)) return True;
98
99 return False;
100 }
101
102 /*--------------------------------------------------------------*/
103 /* Transfer objects in the select list to the current object */
104 /* (but disallow infinitely recursive loops!) */
105 /*--------------------------------------------------------------*/
106 /* IMPORTANT: delete_for_xfer() MUST be executed prior to */
107 /* calling transferselects(), so that the deleted elements are */
108 /* in an object saved in areawin->editstack. */
109 /*--------------------------------------------------------------*/
110
transferselects()111 void transferselects()
112 {
113 short locselects;
114 objinstptr tobj;
115 XPoint newpos;
116
117 if (areawin->editstack->parts == 0) return;
118
119 if (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
120 eventmode == UNDO_MODE || eventmode == CATMOVE_MODE) {
121 short ps = topobject->parts;
122
123 freeselects();
124
125 locselects = areawin->editstack->parts;
126 areawin->selectlist = xc_undelete(areawin->topinstance,
127 areawin->editstack, (short)NORMAL, (short *)NULL);
128 areawin->selects = locselects;
129
130 /* Move all selected items to the cursor position */
131 newpos = UGetCursor();
132 drag((int)newpos.x, (int)newpos.y);
133
134 /* check to make sure this object is not the current object */
135 /* or one of its direct ancestors, else an infinite loop results. */
136
137 for (ps = 0; ps < topobject->parts; ps++) {
138 if (IS_OBJINST(*(topobject->plist + ps))) {
139 tobj = TOOBJINST(topobject->plist + ps);
140 if (recursefind(tobj->thisobject, topobject)) {
141 Wprintf("Attempt to place object inside of itself");
142 delete_noundo(NORMAL);
143 break;
144 }
145 }
146 }
147 }
148 }
149
150 /*-------------------------------------------------------------------*/
151 /* Make a new matrix corresponding to the current position and scale */
152 /*-------------------------------------------------------------------*/
153
newmatrix()154 void newmatrix()
155 {
156 if (DCTM == NULL) {
157 DCTM = (Matrixptr)malloc(sizeof(Matrix));
158 DCTM->nextmatrix = NULL;
159 }
160 UResetCTM(DCTM);
161 UMakeWCTM(DCTM);
162 }
163
164 /*-------------------------------------------------------*/
165 /* set the viewscale variable to the proper address */
166 /*-------------------------------------------------------*/
167
setpage(Boolean killselects)168 void setpage(Boolean killselects)
169 {
170 areawin->vscale = topobject->viewscale;
171 areawin->pcorner = topobject->pcorner;
172 newmatrix();
173
174 if (killselects) clearselects();
175
176 #ifdef TCL_WRAPPER
177 if (xobjs.suspend < 0)
178 XcInternalTagCall(xcinterp, 2, "page", "goto");
179 #endif
180 }
181
182 /*-------------------------------------------------------*/
183 /* switch to a new page */
184 /*-------------------------------------------------------*/
185
changepage(short pagenumber)186 int changepage(short pagenumber)
187 {
188 short npage;
189 objectptr pageobj;
190 u_char undo_type;
191
192 /* to add to existing number of top level pages. . . */
193
194 if (pagenumber == 255) {
195 if (xobjs.pages == 255) {
196 Wprintf("Out of available pages!");
197 return -1;
198 }
199 else pagenumber = xobjs.pages;
200 }
201
202 if (pagenumber >= xobjs.pages) {
203
204 xobjs.pagelist = (Pagedata **)realloc(xobjs.pagelist, (pagenumber + 1)
205 * sizeof(Pagedata *));
206 xobjs.pagelist[pagenumber] = (Pagedata *)malloc(sizeof(Pagedata));
207 xobjs.pagelist[pagenumber]->filename = NULL;
208 xobjs.pagelist[pagenumber]->background.name = NULL;
209 xobjs.pagelist[pagenumber]->pageinst = NULL;
210
211 /* If we skipped ahead to pagenumber, fill in the pages in between */
212 for (npage = xobjs.pages; npage < pagenumber; npage++) {
213 xobjs.pagelist[npage] = (Pagedata *)malloc(sizeof(Pagedata));
214 xobjs.pagelist[npage]->pageinst = NULL;
215 }
216
217 xobjs.pages = pagenumber + 1;
218 makepagebutton();
219 }
220
221 if (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) {
222 delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
223 undo_type = UNDO_MORE;
224 }
225 else {
226 clearselects();
227 undo_type = UNDO_DONE;
228 }
229 if (areawin->page != pagenumber)
230 register_for_undo(XCF_Page, undo_type, areawin->topinstance,
231 areawin->page, pagenumber);
232
233 if (eventmode != ASSOC_MODE) {
234 areawin->page = pagenumber;
235 free_stack(&areawin->stack);
236 }
237 if (xobjs.pagelist[pagenumber]->pageinst == NULL) {
238
239 /* initialize a new page */
240
241 pageobj = (objectptr) malloc (sizeof(object));
242 initmem(pageobj);
243 sprintf(pageobj->name, "Page %d", pagenumber + 1);
244
245 xobjs.pagelist[pagenumber]->pageinst = newpageinst(pageobj);
246 xobjs.pagelist[pagenumber]->filename = NULL;
247 xobjs.pagelist[pagenumber]->background.name = NULL;
248
249 pagereset(pagenumber);
250 }
251
252 /* Write back the current view parameters */
253 if (areawin->topinstance != NULL) {
254 topobject->viewscale = areawin->vscale;
255 topobject->pcorner = areawin->pcorner;
256 }
257
258 areawin->topinstance = xobjs.pagelist[pagenumber]->pageinst;
259
260 setpage(TRUE);
261
262 return 0;
263 }
264
265 /*-------------------------------------------------------*/
266 /* switch to a new page and redisplay */
267 /*-------------------------------------------------------*/
268
newpage(short pagenumber)269 void newpage(short pagenumber)
270 {
271 switch (eventmode) {
272 case CATALOG_MODE:
273 eventmode = NORMAL_MODE;
274 catreturn();
275 break;
276
277 case NORMAL_MODE: case COPY_MODE: case MOVE_MODE: case UNDO_MODE:
278 if (changepage(pagenumber) >= 0) {
279 transferselects();
280 renderbackground();
281 refresh(NULL, NULL, NULL);
282
283 togglegrid((u_short)xobjs.pagelist[areawin->page]->coordstyle);
284 setsymschem();
285 }
286 break;
287
288 default:
289 Wprintf("Cannot switch pages from this mode");
290 break;
291 }
292 }
293
294 /*---------------------------------------*/
295 /* Stack structure push and pop routines */
296 /*---------------------------------------*/
297
push_stack(pushlistptr * stackroot,objinstptr thisinst,char * clientdata)298 void push_stack(pushlistptr *stackroot, objinstptr thisinst, char *clientdata)
299 {
300 pushlistptr newpush;
301
302 newpush = (pushlistptr)malloc(sizeof(pushlist));
303 newpush->next = *stackroot;
304 newpush->clientdata = clientdata;
305 newpush->thisinst = thisinst;
306 *stackroot = newpush;
307 }
308
309 /*----------------------------------------------------------*/
310
pop_stack(pushlistptr * stackroot)311 void pop_stack(pushlistptr *stackroot)
312 {
313 pushlistptr lastpush;
314
315 if (!(*stackroot)) {
316 Fprintf(stderr, "pop_genstack() Error: NULL instance stack!\n");
317 return;
318 }
319
320 lastpush = (*stackroot)->next;
321 free(*stackroot);
322 *stackroot = lastpush;
323 }
324
325 /*----------------------------------------------------------*/
326
free_stack(pushlistptr * stackroot)327 void free_stack(pushlistptr *stackroot)
328 {
329 while ((*stackroot) != NULL)
330 pop_stack(stackroot);
331 }
332
333 /*------------------------------------------*/
334 /* Push object onto hierarchy stack to edit */
335 /*------------------------------------------*/
336
pushobject(objinstptr thisinst)337 void pushobject(objinstptr thisinst)
338 {
339 short *selectobj, *savelist;
340 int saves;
341 u_char undo_type = UNDO_DONE;
342 objinstptr pushinst = thisinst;
343
344 savelist = NULL;
345 saves = 0;
346 if (eventmode == MOVE_MODE || eventmode == COPY_MODE) {
347 savelist = areawin->selectlist;
348 saves = areawin->selects;
349 areawin->selectlist = NULL;
350 areawin->selects = 0;
351 undo_type = UNDO_MORE;
352 }
353
354 if (pushinst == NULL) {
355 selectobj = areawin->selectlist;
356 if (areawin->selects == 0) {
357 disable_selects(topobject, savelist, saves);
358 selectobj = select_element(OBJINST);
359 enable_selects(topobject, savelist, saves);
360 }
361 if (areawin->selects == 0) {
362 Wprintf("No objects selected.");
363 return;
364 }
365 else if (areawin->selects > 1) {
366 Wprintf("Choose only one object.");
367 return;
368 }
369 else if (SELECTTYPE(selectobj) != OBJINST) {
370 Wprintf("Element to push must be an object.");
371 return;
372 }
373 else pushinst = SELTOOBJINST(selectobj);
374 }
375
376 if (savelist != NULL) {
377 delete_for_xfer(NORMAL, savelist, saves);
378 free(savelist);
379 }
380
381 register_for_undo(XCF_Push, undo_type, areawin->topinstance, pushinst);
382
383 /* save the address of the current object to the push stack */
384
385 push_stack(&areawin->stack, areawin->topinstance, NULL);
386
387 topobject->viewscale = areawin->vscale;
388 topobject->pcorner = areawin->pcorner;
389 areawin->topinstance = pushinst;
390
391 /* move selected items to the new object */
392
393 setpage(TRUE);
394 transferselects();
395 refresh(NULL, NULL, NULL);
396 setsymschem();
397 }
398
399 /*--------------------------*/
400 /* Pop edit hierarchy stack */
401 /*--------------------------*/
402
popobject(xcWidget w,pointertype no_undo,caddr_t calldata)403 void popobject(xcWidget w, pointertype no_undo, caddr_t calldata)
404 {
405 u_char undo_type = UNDO_DONE;
406 UNUSED(w); UNUSED(calldata);
407
408 if (areawin->stack == NULL || (eventmode != NORMAL_MODE && eventmode != MOVE_MODE
409 && eventmode != COPY_MODE && eventmode != FONTCAT_MODE &&
410 eventmode != ASSOC_MODE && eventmode != UNDO_MODE &&
411 eventmode != EFONTCAT_MODE)) return;
412
413 if ((eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE)
414 && ((areawin->stack->thisinst == xobjs.libtop[LIBRARY]) ||
415 (areawin->stack->thisinst == xobjs.libtop[USERLIB]))) return;
416
417 /* remove any selected items from the current object */
418
419 if (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) {
420 undo_type = UNDO_MORE;
421 delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
422 }
423 else if (eventmode != FONTCAT_MODE && eventmode != EFONTCAT_MODE)
424 unselect_all();
425
426 /* If coming from the library, don't register an undo action, because */
427 /* it has already been registered as type XCF_Library_Pop. */
428
429 if (no_undo == (pointertype)0)
430 register_for_undo(XCF_Pop, undo_type, areawin->topinstance);
431
432 topobject->viewscale = areawin->vscale;
433 topobject->pcorner = areawin->pcorner;
434 areawin->topinstance = areawin->stack->thisinst;
435 pop_stack(&areawin->stack);
436
437 /* if new object is a library or PAGELIB, put back into CATALOG_MODE */
438
439 if (is_library(topobject) >= 0) eventmode = CATALOG_MODE;
440
441 /* move selected items to the new object */
442
443 if (eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE)
444 setpage(False);
445 else {
446 setpage(True);
447 setsymschem();
448 if (eventmode != ASSOC_MODE)
449 transferselects();
450 }
451 refresh(NULL, NULL, NULL);
452 }
453
454 /*-------------------------------------------------------------------------*/
455 /* Destructive reset of entire object */
456 /*-------------------------------------------------------------------------*/
457
resetbutton(xcWidget button,pointertype pageno,caddr_t calldata)458 void resetbutton(xcWidget button, pointertype pageno, caddr_t calldata)
459 {
460 short page;
461 objectptr pageobj;
462 objinstptr pageinst;
463 UNUSED(button); UNUSED(calldata);
464
465 if (eventmode != NORMAL_MODE) return;
466
467 page = (pageno == (pointertype)0) ? areawin->page : (short)(pageno - 1);
468
469 pageinst = xobjs.pagelist[page]->pageinst;
470
471 if (pageinst == NULL) return; /* page already cleared */
472
473 pageobj = pageinst->thisobject;
474
475 /* Make sure this is a real top-level page */
476
477 if (is_page(topobject) < 0) {
478 if (pageno == (pointertype)0) {
479 Wprintf("Can only clear top-level pages!");
480 return;
481 }
482 else {
483 /* Make sure that we're not in the hierarchy of the page being deleted */
484 pushlistptr slist;
485 for (slist = areawin->stack; slist != NULL; slist = slist->next)
486 if (slist->thisinst->thisobject == pageobj) {
487 Wprintf("Can't delete the page while you're in its hierarchy!");
488 return;
489 }
490 }
491 }
492
493 /* Watch for pages which are linked by schematic/symbol. */
494
495 if (pageobj->symschem != NULL) {
496 Wprintf("Schematic association to object %s", pageobj->symschem->name);
497 return;
498 }
499
500 sprintf(pageobj->name, "Page %d", page + 1);
501 xobjs.pagelist[page]->filename = (char *)realloc(xobjs.pagelist[page]->filename,
502 (strlen(pageobj->name) + 1) * sizeof(char));
503 strcpy(xobjs.pagelist[page]->filename, pageobj->name);
504 reset(pageobj, NORMAL);
505 flush_undo_stack();
506
507 if (page == areawin->page) {
508 areawin->redraw_needed = True;
509 drawarea(areawin->area, NULL, NULL);
510 printname(pageobj);
511 renamepage(page);
512 Wprintf("Page cleared.");
513 }
514 }
515
516 /*------------------------------------------------------*/
517 /* Redraw the horizontal scrollbar */
518 /*------------------------------------------------------*/
519
drawhbar(xcWidget bar,caddr_t clientdata,caddr_t calldata)520 void drawhbar(xcWidget bar, caddr_t clientdata, caddr_t calldata)
521 {
522 Window bwin;
523 float frac;
524 long rleft, rright, rmid;
525 UNUSED(clientdata); UNUSED(calldata);
526
527 if (!xcIsRealized(bar)) return;
528 if (xobjs.suspend >= 0) return;
529
530 bwin = xcWindow(bar);
531
532 if (topobject->bbox.width > 0) {
533 frac = (float) areawin->width / (float) topobject->bbox.width;
534 rleft = (long)(frac * (float)(areawin->pcorner.x
535 - topobject->bbox.lowerleft.x));
536 rright = rleft + (long)(frac * (float)areawin->width / areawin->vscale);
537 }
538 else {
539 rleft = 0L;
540 rright = (long)areawin->width;
541 }
542 rmid = (rright + rleft) >> 1;
543
544 if (rleft < 0) rleft = 0;
545 if (rright > areawin->width) rright = areawin->width;
546
547 XSetFunction(dpy, areawin->gc, GXcopy);
548 XSetForeground(dpy, areawin->gc, colorlist[BARCOLOR].color.pixel);
549 if (rmid > 0 && rleft > 0)
550 XClearArea(dpy, bwin, 0, 0, (int)rleft, SBARSIZE, FALSE);
551 XFillRectangle(dpy, bwin, areawin->gc, (int)rleft + 1, 1,
552 (int)(rright - rleft), SBARSIZE - 1);
553 if (rright > rmid)
554 XClearArea(dpy, bwin, (int)rright + 1, 0, areawin->width
555 - (int)rright, SBARSIZE, FALSE);
556 XClearArea(dpy, bwin, (int)rmid - 1, 1, 3, SBARSIZE, FALSE);
557
558 XSetForeground(dpy, areawin->gc, colorlist[areawin->gccolor].color.pixel);
559 }
560
561 /*------------------------------------------------------*/
562 /* Redraw the vertical scrollbar */
563 /*------------------------------------------------------*/
564
drawvbar(xcWidget bar,caddr_t clientdata,caddr_t calldata)565 void drawvbar(xcWidget bar, caddr_t clientdata, caddr_t calldata)
566 {
567 Window bwin = xcWindow(bar);
568 float frac;
569 long rtop, rbot, rmid;
570 UNUSED(clientdata); UNUSED(calldata);
571
572 if (!xcIsRealized(bar)) return;
573 if (xobjs.suspend >= 0) return;
574
575 if (topobject->bbox.height > 0) {
576 frac = (float)areawin->height / (float)topobject->bbox.height;
577 rbot = (long)(frac * (float)(topobject->bbox.lowerleft.y
578 - areawin->pcorner.y + topobject->bbox.height));
579 rtop = rbot - (long)(frac * (float)areawin->height / areawin->vscale);
580 }
581 else {
582 rbot = areawin->height;
583 rtop = 0;
584 }
585 rmid = (rtop + rbot) >> 1;
586
587 if (rtop < 0) rtop = 0;
588 if (rbot > areawin->height) rbot = areawin->height;
589
590 XSetFunction(dpy, areawin->gc, GXcopy);
591 XSetForeground(dpy, areawin->gc, colorlist[BARCOLOR].color.pixel);
592 if (rmid > 0 && rtop > 0)
593 XClearArea(dpy, bwin, 0, 0, SBARSIZE, (int)rtop, FALSE);
594 XFillRectangle(dpy, bwin, areawin->gc, 0, (int)rtop + 2, SBARSIZE,
595 (int)(rbot - rtop));
596 if (rbot > rmid)
597 XClearArea(dpy, bwin, 0, (int)rbot + 1, SBARSIZE, areawin->height
598 - (int)rbot, FALSE);
599 XClearArea(dpy, bwin, 0, (int)rmid - 1, SBARSIZE, 3, FALSE);
600
601 XSetForeground(dpy, areawin->gc, colorlist[areawin->gccolor].color.pixel);
602 }
603
604 /*------------------------------------------------------*/
605 /* Simultaneously scroll the screen and horizontal */
606 /* bar when dragging the mouse in the scrollbar area */
607 /*------------------------------------------------------*/
608
panhbar(xcWidget bar,caddr_t clientdata,XButtonEvent * event)609 void panhbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event)
610 {
611 long newx, newpx;
612 short savex = areawin->pcorner.x;
613 UNUSED(clientdata);
614
615 if (eventmode == SELAREA_MODE) return;
616
617 newx = (long)(event->x * ((float)topobject->bbox.width /
618 areawin->width) + topobject->bbox.lowerleft.x - 0.5 *
619 ((float)areawin->width / areawin->vscale));
620 areawin->pcorner.x = (short)newx;
621 drawhbar(bar, NULL, NULL);
622 areawin->pcorner.x = savex;
623
624 if ((newpx = (long)(newx - savex) * areawin->vscale) == 0)
625 return;
626
627 areawin->panx = -newpx;
628 drawarea(NULL, NULL, NULL);
629 }
630
631 /*------------------------------------------------------*/
632 /* End the horizontal scroll and refresh entire screen */
633 /*------------------------------------------------------*/
634
endhbar(xcWidget bar,caddr_t clientdata,XButtonEvent * event)635 void endhbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event)
636 {
637 long newx;
638 short savex = areawin->pcorner.x;
639 UNUSED(clientdata);
640
641 areawin->panx = 0;
642
643 newx = (long)(event->x * ((float)topobject->bbox.width /
644 areawin->width) + topobject->bbox.lowerleft.x - 0.5 *
645 ((float)areawin->width / areawin->vscale));
646
647 areawin->pcorner.x = (short)newx;
648
649 if ((newx << 1) != (long)((short)(newx << 1)) || checkbounds() == -1) {
650 areawin->pcorner.x = savex;
651 Wprintf("Reached boundary: cannot pan further");
652 }
653 else
654 W3printf(" ");
655
656 areawin->redraw_needed = True;
657 areawin->lastbackground = NULL;
658 renderbackground();
659 drawhbar(bar, NULL, NULL);
660 drawarea(bar, NULL, NULL);
661 }
662
663 /*------------------------------------------------------*/
664 /* Simultaneously scroll the screen and vertical */
665 /* bar when dragging the mouse in the scrollbar area */
666 /*------------------------------------------------------*/
667
panvbar(xcWidget bar,caddr_t clientdata,XButtonEvent * event)668 void panvbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event)
669 {
670 long newy, newpy;
671 short savey = areawin->pcorner.y;
672 UNUSED(clientdata);
673
674 if (eventmode == SELAREA_MODE) return;
675
676 newy = (int)((areawin->height - event->y) *
677 ((float)topobject->bbox.height / areawin->height) +
678 topobject->bbox.lowerleft.y - 0.5 * ((float)areawin->height /
679 areawin->vscale));
680 areawin->pcorner.y = (short)newy;
681 drawvbar(bar, NULL, NULL);
682 areawin->pcorner.y = savey;
683
684 if ((newpy = (long)(newy - savey) * areawin->vscale) == 0)
685 return;
686
687 areawin->pany = newpy;
688 drawarea(NULL, NULL, NULL);
689 }
690
691 /*------------------------------------------------------*/
692 /* Pan the screen to follow the cursor position */
693 /*------------------------------------------------------*/
694
trackpan(int x,int y)695 void trackpan(int x, int y)
696 {
697 XPoint newpos;
698 short savey = areawin->pcorner.y;
699 short savex = areawin->pcorner.x;
700
701 newpos.x = areawin->origin.x - x;
702 newpos.y = y - areawin->origin.y;
703
704 areawin->pcorner.x += newpos.x / areawin->vscale;
705 areawin->pcorner.y += newpos.y / areawin->vscale;
706
707 drawhbar(areawin->scrollbarh, NULL, NULL);
708 drawvbar(areawin->scrollbarv, NULL, NULL);
709
710 areawin->panx = -newpos.x;
711 areawin->pany = newpos.y;
712
713 drawarea(NULL, NULL, NULL);
714
715 areawin->pcorner.x = savex;
716 areawin->pcorner.y = savey;
717
718 }
719
720 /*------------------------------------------------------*/
721 /* End the vertical scroll and refresh entire screen */
722 /*------------------------------------------------------*/
723
endvbar(xcWidget bar,caddr_t clientdata,XButtonEvent * event)724 void endvbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event)
725 {
726 long newy;
727 short savey = areawin->pcorner.y;
728 UNUSED(clientdata);
729
730 areawin->pany = 0;
731
732 newy = (int)((areawin->height - event->y) *
733 ((float)topobject->bbox.height / areawin->height) +
734 topobject->bbox.lowerleft.y - 0.5 * ((float)areawin->height /
735 areawin->vscale));
736
737 areawin->pcorner.y = (short)newy;
738
739 if ((newy << 1) != (long)((short)(newy << 1)) || checkbounds() == -1) {
740 areawin->pcorner.y = savey;
741 Wprintf("Reached boundary: cannot pan further");
742 }
743 else
744 W3printf(" ");
745
746 areawin->redraw_needed = True;
747 areawin->lastbackground = NULL;
748 renderbackground();
749 drawvbar(bar, NULL, NULL);
750 drawarea(bar, NULL, NULL);
751 }
752
753 /*--------------------------------------------------------------------*/
754 /* Zoom functions-- zoom box, zoom in, zoom out, and pan. */
755 /*--------------------------------------------------------------------*/
756
postzoom()757 void postzoom()
758 {
759 W3printf(" ");
760 areawin->lastbackground = NULL;
761 renderbackground();
762 newmatrix();
763 }
764
765 /*--------------------------------------------------------------------*/
766
zoominbox(int x,int y)767 void zoominbox(int x, int y)
768 {
769 float savescale;
770 float delxscale, delyscale;
771 XPoint savell; /* ucenter, ncenter, (jdk)*/
772 UNUSED(x); UNUSED(y);
773
774 savescale = areawin->vscale;
775 savell.x = areawin->pcorner.x;
776 savell.y = areawin->pcorner.y;
777
778 /* zoom-box function: corners are in areawin->save and areawin->origin */
779 /* select box has lower-left corner in .origin, upper-right in .save */
780 /* ignore if zoom box is size zero */
781
782 if (areawin->save.x == areawin->origin.x || areawin->save.y == areawin->origin.y) {
783 Wprintf("Zoom box of size zero: Ignoring.");
784 eventmode = NORMAL_MODE;
785 return;
786 }
787
788 /* determine whether x or y is limiting factor in zoom */
789 delxscale = (areawin->width / areawin->vscale) /
790 abs(areawin->save.x - areawin->origin.x);
791 delyscale = (areawin->height / areawin->vscale) /
792 abs(areawin->save.y - areawin->origin.y);
793 areawin->vscale *= min(delxscale, delyscale);
794
795 areawin->pcorner.x = min(areawin->origin.x, areawin->save.x) -
796 (areawin->width / areawin->vscale -
797 abs(areawin->save.x - areawin->origin.x)) / 2;
798 areawin->pcorner.y = min(areawin->origin.y, areawin->save.y) -
799 (areawin->height / areawin->vscale -
800 abs(areawin->save.y - areawin->origin.y)) / 2;
801 eventmode = NORMAL_MODE;
802
803 /* check for minimum scale */
804
805 if (checkbounds() == -1) {
806 areawin->pcorner.x = savell.x;
807 areawin->pcorner.y = savell.y;
808 areawin->vscale = savescale;
809 Wprintf("At minimum scale: cannot scale further");
810
811 /* this is a rare case where an object gets out-of-bounds */
812
813 if (checkbounds() == -1) {
814 if (beeper) XBell(dpy, 100);
815 Wprintf("Unable to scale: Delete out-of-bounds object!");
816 }
817 return;
818 }
819 postzoom();
820 }
821
822 /*--------------------------------------------------------------------*/
823
zoomin(int x,int y)824 void zoomin(int x, int y)
825 {
826 float savescale;
827 XPoint ucenter, ncenter, savell;
828
829 savescale = areawin->vscale;
830 savell.x = areawin->pcorner.x;
831 savell.y = areawin->pcorner.y;
832
833 window_to_user(areawin->width / 2, areawin->height / 2, &ucenter);
834 areawin->vscale *= areawin->zoomfactor;
835 window_to_user(areawin->width / 2, areawin->height / 2, &ncenter);
836 areawin->pcorner.x += (ucenter.x - ncenter.x);
837 areawin->pcorner.y += (ucenter.y - ncenter.y);
838
839 /* check for minimum scale */
840
841 if (checkbounds() == -1) {
842 areawin->pcorner.x = savell.x;
843 areawin->pcorner.y = savell.y;
844 areawin->vscale = savescale;
845 Wprintf("At minimum scale: cannot scale further");
846
847 /* this is a rare case where an object gets out-of-bounds */
848
849 if (checkbounds() == -1) {
850 if (beeper) XBell(dpy, 100);
851 Wprintf("Unable to scale: Delete out-of-bounds object!");
852 }
853 return;
854 }
855 else if (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
856 eventmode == CATMOVE_MODE)
857 drag(x, y);
858
859 postzoom();
860 }
861
862 /*--------------------------------------------------------------------*/
863
zoominrefresh(int x,int y)864 void zoominrefresh(int x, int y)
865 {
866 if (eventmode == SELAREA_MODE)
867 zoominbox(x, y);
868 else
869 zoomin(x, y);
870 refresh(NULL, NULL, NULL);
871 }
872
873 /*--------------------------------------------------------------------*/
874
zoomoutbox(int x,int y)875 void zoomoutbox(int x, int y)
876 {
877 float savescale;
878 float delxscale, delyscale, scalefac;
879 XPoint savell; /* ucenter, ncenter, (jdk)*/
880 XlPoint newll;
881 UNUSED(x); UNUSED(y);
882
883 savescale = areawin->vscale;
884 savell.x = areawin->pcorner.x;
885 savell.y = areawin->pcorner.y;
886
887 /* zoom-box function, analogous to that for zoom-in */
888 /* ignore if zoom box is size zero */
889
890 if (areawin->save.x == areawin->origin.x || areawin->save.y == areawin->origin.y) {
891 Wprintf("Zoom box of size zero: Ignoring.");
892 eventmode = NORMAL_MODE;
893 return;
894 }
895
896 /* determine whether x or y is limiting factor in zoom */
897 delxscale = abs(areawin->save.x - areawin->origin.x) /
898 (areawin->width / areawin->vscale);
899 delyscale = abs(areawin->save.y - areawin->origin.y) /
900 (areawin->height / areawin->vscale);
901 scalefac = min(delxscale, delyscale);
902 areawin->vscale *= scalefac;
903
904 /* compute lower-left corner of (reshaped) select box */
905 if (delxscale < delyscale) {
906 newll.y = min(areawin->save.y, areawin->origin.y);
907 newll.x = (areawin->save.x + areawin->origin.x
908 - (abs(areawin->save.y - areawin->origin.y) *
909 areawin->width / areawin->height)) / 2;
910 }
911 else {
912 newll.x = min(areawin->save.x, areawin->origin.x);
913 newll.y = (areawin->save.y + areawin->origin.y
914 - (abs(areawin->save.x - areawin->origin.x) *
915 areawin->height / areawin->width)) / 2;
916 }
917
918 /* extrapolate to find new lower-left corner of screen */
919 newll.x = areawin->pcorner.x - (int)((float)(newll.x -
920 areawin->pcorner.x) / scalefac);
921 newll.y = areawin->pcorner.y - (int)((float)(newll.y -
922 areawin->pcorner.y) / scalefac);
923
924 eventmode = NORMAL_MODE;
925 areawin->pcorner.x = (short)newll.x;
926 areawin->pcorner.y = (short)newll.y;
927
928 if ((newll.x << 1) != (long)(areawin->pcorner.x << 1) || (newll.y << 1)
929 != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) {
930 areawin->vscale = savescale;
931 areawin->pcorner.x = savell.x;
932 areawin->pcorner.y = savell.y;
933 Wprintf("At maximum scale: cannot scale further.");
934 return;
935 }
936 postzoom();
937 }
938
939 /*--------------------------------------------------------------------*/
940
zoomout(int x,int y)941 void zoomout(int x, int y)
942 {
943 float savescale;
944 XPoint ucenter, ncenter, savell;
945 XlPoint newll;
946
947 savescale = areawin->vscale;
948 savell.x = areawin->pcorner.x;
949 savell.y = areawin->pcorner.y;
950
951 window_to_user(areawin->width / 2, areawin->height / 2, &ucenter);
952 areawin->vscale /= areawin->zoomfactor;
953 window_to_user(areawin->width / 2, areawin->height / 2, &ncenter);
954 newll.x = (long)areawin->pcorner.x + (long)(ucenter.x - ncenter.x);
955 newll.y = (long)areawin->pcorner.y + (long)(ucenter.y - ncenter.y);
956 areawin->pcorner.x = (short)newll.x;
957 areawin->pcorner.y = (short)newll.y;
958
959 if ((newll.x << 1) != (long)(areawin->pcorner.x << 1) || (newll.y << 1)
960 != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) {
961 areawin->vscale = savescale;
962 areawin->pcorner.x = savell.x;
963 areawin->pcorner.y = savell.y;
964 Wprintf("At maximum scale: cannot scale further.");
965 return;
966 }
967 else if (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
968 eventmode == CATMOVE_MODE)
969 drag(x, y);
970
971 postzoom();
972 }
973
974 /*--------------------------------------------------------------------*/
975
zoomoutrefresh(int x,int y)976 void zoomoutrefresh(int x, int y)
977 {
978 if (eventmode == SELAREA_MODE)
979 zoomoutbox(x, y);
980 else
981 zoomout(x, y);
982 refresh(NULL, NULL, NULL);
983 }
984
985 /*--------------------------------------*/
986 /* Call to XWarpPointer */
987 /*--------------------------------------*/
988
warppointer(int x,int y)989 void warppointer(int x, int y)
990 {
991 XWarpPointer(dpy, None, areawin->window, 0, 0, 0, 0, x, y);
992 }
993
994 /*--------------------------------------------------------------*/
995 /* ButtonPress handler during center pan */
996 /* x and y are cursor coordinates. */
997 /* If ptype is 1-4 (directional), then "value" is a fraction of */
998 /* the screen to scroll. */
999 /*--------------------------------------------------------------*/
1000
panbutton(u_int ptype,int x,int y,float value)1001 void panbutton(u_int ptype, int x, int y, float value)
1002 {
1003 /* Window pwin; (jdk) */
1004 int xpos, ypos, newllx, newlly;
1005 XPoint savell; /* , newpos; (jdk)*/
1006 Dimension hwidth = areawin->width >> 1, hheight = areawin->height >> 1;
1007
1008 savell.x = areawin->pcorner.x;
1009 savell.y = areawin->pcorner.y;
1010
1011 switch(ptype) {
1012 case 1:
1013 xpos = hwidth - (hwidth * 2 * value);
1014 ypos = hheight;
1015 break;
1016 case 2:
1017 xpos = hwidth + (hwidth * 2 * value);
1018 ypos = hheight;
1019 break;
1020 case 3:
1021 xpos = hwidth;
1022 ypos = hheight - (hheight * 2 * value);
1023 break;
1024 case 4:
1025 xpos = hwidth;
1026 ypos = hheight + (hheight * 2 * value);
1027 break;
1028 case 5:
1029 xpos = x;
1030 ypos = y;
1031 break;
1032 case 6: /* "pan follow" */
1033 if (eventmode == PAN_MODE)
1034 finish_op(XCF_Finish, x, y);
1035 else if (eventmode == NORMAL_MODE) {
1036 eventmode = PAN_MODE;
1037 areawin->save.x = x;
1038 areawin->save.y = y;
1039 u2u_snap(&areawin->save);
1040 areawin->origin = areawin->save;
1041 #ifdef TCL_WRAPPER
1042 Tk_CreateEventHandler(areawin->area, PointerMotionMask |
1043 ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
1044 #else
1045 xcAddEventHandler(areawin->area, PointerMotionMask |
1046 ButtonMotionMask , False, (xcEventHandler)xlib_drag,
1047 NULL);
1048 #endif
1049 }
1050 return;
1051 break;
1052 default: /* "pan here" */
1053 xpos = x;
1054 ypos = y;
1055 warppointer(hwidth, hheight);
1056 break;
1057 }
1058
1059 xpos -= hwidth;
1060 ypos = hheight - ypos;
1061
1062 newllx = (int)areawin->pcorner.x + (int)((float)xpos / areawin->vscale);
1063 newlly = (int)areawin->pcorner.y + (int)((float)ypos / areawin->vscale);
1064
1065 areawin->pcorner.x = (short) newllx;
1066 areawin->pcorner.y = (short) newlly;
1067
1068 if ((newllx << 1) != (long)(areawin->pcorner.x << 1) || (newlly << 1)
1069 != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) {
1070 areawin->pcorner.x = savell.x;
1071 areawin->pcorner.x = savell.y;
1072 Wprintf("Reached bounds: cannot pan further.");
1073 return;
1074 }
1075 else if (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1076 eventmode == CATMOVE_MODE)
1077 drag(x, y);
1078
1079 postzoom();
1080 }
1081
1082 /*--------------------------------------------------------------*/
1083
panrefresh(u_int ptype,int x,int y,float value)1084 void panrefresh(u_int ptype, int x, int y, float value)
1085 {
1086 panbutton(ptype, x, y, value);
1087 refresh(NULL, NULL, NULL);
1088 }
1089
1090 /*----------------------------------------------------------------*/
1091 /* Check for out-of-bounds before warping pointer, and pan window */
1092 /* if necessary. */
1093 /*----------------------------------------------------------------*/
1094
checkwarp(XPoint * userpt)1095 void checkwarp(XPoint *userpt)
1096 {
1097 XPoint wpoint;
1098
1099 user_to_window(*userpt, &wpoint);
1100
1101 if (wpoint.x < 0 || wpoint.y < 0 || wpoint.x > areawin->width ||
1102 wpoint.y > areawin->height) {
1103 panrefresh(5, wpoint.x, wpoint.y, 0);
1104 wpoint.x = areawin->width >> 1;
1105 wpoint.y = areawin->height >> 1;
1106 /* snap(wpoint.x, wpoint.y, userpt); */
1107 }
1108 warppointer(wpoint.x, wpoint.y);
1109 }
1110
1111 /*--------------------------------------------------------------*/
1112 /* Return a pointer to the element containing a reference point */
1113 /*--------------------------------------------------------------*/
1114
getsubpart(pathptr editpath,int * idx)1115 genericptr getsubpart(pathptr editpath, int *idx)
1116 {
1117 pointselect *tmpptr = NULL;
1118 genericptr *pgen;
1119
1120 if (idx) *idx = 0;
1121
1122 for (pgen = editpath->plist; pgen < editpath->plist + editpath->parts; pgen++) {
1123 switch (ELEMENTTYPE(*pgen)) {
1124 case POLYGON:
1125 if (TOPOLY(pgen)->cycle != NULL) {
1126 for (tmpptr = TOPOLY(pgen)->cycle;; tmpptr++) {
1127 if (tmpptr->flags & REFERENCE) break;
1128 if (tmpptr->flags & LASTENTRY) break;
1129 }
1130 if (tmpptr->flags & REFERENCE) return *pgen;
1131 }
1132 break;
1133 case SPLINE:
1134 if (TOSPLINE(pgen)->cycle != NULL) {
1135 for (tmpptr = TOSPLINE(pgen)->cycle;; tmpptr++) {
1136 if (tmpptr->flags & REFERENCE) break;
1137 if (tmpptr->flags & LASTENTRY) break;
1138 }
1139 if (tmpptr->flags & REFERENCE) return *pgen;
1140 }
1141 break;
1142 }
1143 if (idx) (*idx)++;
1144 }
1145 return NULL;
1146 }
1147
1148 /*--------------------------------------------------------------*/
1149 /* Return a pointer to the current reference point of an */
1150 /* edited element (polygon, spline, or path) */
1151 /*--------------------------------------------------------------*/
1152
getrefpoint(genericptr genptr,XPoint ** refpt)1153 pointselect *getrefpoint(genericptr genptr, XPoint **refpt)
1154 {
1155 pointselect *tmpptr = NULL;
1156 genericptr *pgen;
1157
1158 if (refpt) *refpt = NULL;
1159 switch (genptr->type) {
1160 case POLYGON:
1161 if (((polyptr)genptr)->cycle != NULL) {
1162 for (tmpptr = ((polyptr)genptr)->cycle;; tmpptr++) {
1163 if (tmpptr->flags & REFERENCE) break;
1164 if (tmpptr->flags & LASTENTRY) break;
1165 }
1166 if (!(tmpptr->flags & REFERENCE)) tmpptr = NULL;
1167 else if (refpt) *refpt = ((polyptr)genptr)->points + tmpptr->number;
1168 }
1169 break;
1170 case SPLINE:
1171 if (((splineptr)genptr)->cycle != NULL) {
1172 for (tmpptr = ((splineptr)genptr)->cycle;; tmpptr++) {
1173 if (tmpptr->flags & REFERENCE) break;
1174 if (tmpptr->flags & LASTENTRY) break;
1175 }
1176 if (!(tmpptr->flags & REFERENCE)) tmpptr = NULL;
1177 else if (refpt) *refpt = &((splineptr)genptr)->ctrl[tmpptr->number];
1178 }
1179 break;
1180 case PATH:
1181 for (pgen = ((pathptr)genptr)->plist; pgen < ((pathptr)genptr)->plist +
1182 ((pathptr)genptr)->parts; pgen++) {
1183 if ((tmpptr = getrefpoint(*pgen, refpt)) != NULL)
1184 return tmpptr;
1185 }
1186 break;
1187 default:
1188 tmpptr = NULL;
1189 break;
1190 }
1191 return tmpptr;
1192 }
1193
1194 /*--------------------------------------------------------------*/
1195 /* Return next edit point on a polygon, arc, or spline. Do not */
1196 /* update the cycle of the element. */
1197 /*--------------------------------------------------------------*/
1198
checkcycle(genericptr genptr,short dir)1199 int checkcycle(genericptr genptr, short dir)
1200 {
1201 pointselect *tmpptr;
1202 short tmppt, points;
1203 genericptr *pgen;
1204
1205 switch (genptr->type) {
1206 case POLYGON:
1207 if (((polyptr)genptr)->cycle == NULL)
1208 tmpptr = NULL;
1209 else {
1210 for (tmpptr = ((polyptr)genptr)->cycle;; tmpptr++) {
1211 if (tmpptr->flags & REFERENCE) break;
1212 if (tmpptr->flags & LASTENTRY) break;
1213 }
1214 if (!(tmpptr->flags & REFERENCE)) tmpptr = ((polyptr)genptr)->cycle;
1215 }
1216 tmppt = (tmpptr == NULL) ? -1 : tmpptr->number;
1217 points = ((polyptr)genptr)->number;
1218 break;
1219 case SPLINE:
1220 if (((splineptr)genptr)->cycle == NULL)
1221 tmpptr = NULL;
1222 else {
1223 for (tmpptr = ((splineptr)genptr)->cycle;; tmpptr++) {
1224 if (tmpptr->flags & REFERENCE) break;
1225 if (tmpptr->flags & LASTENTRY) break;
1226 }
1227 if (!(tmpptr->flags & REFERENCE)) tmpptr = ((splineptr)genptr)->cycle;
1228 }
1229 tmppt = (tmpptr == NULL) ? -1 : tmpptr->number;
1230 points = 4;
1231 break;
1232 case ARC:
1233 tmpptr = ((arcptr)genptr)->cycle;
1234 tmppt = (tmpptr == NULL) ? -1 : tmpptr->number;
1235 points = 4;
1236 break;
1237 case PATH:
1238 for (pgen = ((pathptr)genptr)->plist; pgen < ((pathptr)genptr)->plist +
1239 ((pathptr)genptr)->parts; pgen++) {
1240 if ((tmppt = checkcycle(*pgen, dir)) >= 0)
1241 return tmppt;
1242 }
1243 break;
1244 default:
1245 tmppt = -1;
1246 break;
1247 }
1248 if (tmppt >= 0) { /* Ignore nonexistent cycles */
1249 tmppt += dir;
1250 if (tmppt < 0) tmppt += points;
1251 tmppt %= points;
1252 }
1253 return tmppt;
1254 }
1255
1256 /*--------------------------------------------------------------*/
1257 /* Change to the next part of a path for editing */
1258 /* For now, |dir| is treated as 1 regardless of its value. */
1259 /*--------------------------------------------------------------*/
1260
nextpathcycle(pathptr nextpath,short dir)1261 void nextpathcycle(pathptr nextpath, short dir)
1262 {
1263 genericptr ppart = getsubpart(nextpath, NULL);
1264 genericptr *ggen;
1265 XPoint *curpt;
1266 polyptr thispoly;
1267 splineptr thisspline;
1268 pointselect *cptr;
1269 short cycle, newcycle;
1270
1271 /* Simple cases---don't need to switch elements */
1272
1273 switch (ELEMENTTYPE(ppart)) {
1274 case POLYGON:
1275 thispoly = (polyptr)ppart;
1276 cptr = thispoly->cycle;
1277 if (cptr == NULL) return;
1278 curpt = thispoly->points + cptr->number;
1279 newcycle = checkcycle(ppart, dir);
1280 advancecycle(&ppart, newcycle);
1281 if (cptr->number < thispoly->number && cptr->number > 0) {
1282 checkwarp(thispoly->points + cptr->number);
1283 removeothercycles(nextpath, ppart);
1284 updatepath(nextpath);
1285 return;
1286 }
1287 break;
1288 case SPLINE:
1289 thisspline = (splineptr)ppart;
1290 cptr = ((splineptr)ppart)->cycle;
1291 if (cptr == NULL) return;
1292 curpt = &thisspline->ctrl[cptr->number];
1293 newcycle = checkcycle(ppart, dir);
1294 advancecycle(&ppart, newcycle);
1295 if (cptr->number < 4 && cptr->number > 0) {
1296 checkwarp(&thisspline->ctrl[cptr->number]);
1297 removeothercycles(nextpath, ppart);
1298 updatepath(nextpath);
1299 if (newcycle == 1 || newcycle == 2)
1300 addanticycle(nextpath, thisspline, newcycle);
1301 return;
1302 }
1303 break;
1304 }
1305
1306 /* Moving on to the next element. . . */
1307
1308 /* If dir < 0, go to the penultimate cycle of the last part */
1309 /* If dir > 0, go to the second cycle of the next part */
1310
1311 for (ggen = nextpath->plist; (*ggen != ppart) &&
1312 (ggen < nextpath->plist + nextpath->parts); ggen++);
1313
1314 if (ggen == nextpath->plist + nextpath->parts) return; /* shouldn't happen! */
1315
1316 if (dir > 0)
1317 ggen++;
1318 else
1319 ggen--;
1320
1321 if (ggen < nextpath->plist)
1322 ggen = nextpath->plist + nextpath->parts - 1;
1323 else if (ggen == nextpath->plist + nextpath->parts)
1324 ggen = nextpath->plist;
1325
1326 removecycle((genericptr *)(&nextpath));
1327
1328 /* The next point to edit is the first point in the next segment */
1329 /* that is not at the same position as the one we were last editing. */
1330
1331 switch (ELEMENTTYPE(*ggen)) {
1332 case POLYGON:
1333 thispoly = TOPOLY(ggen);
1334 cycle = (dir > 0) ? 0 : thispoly->number - 1;
1335 addcycle(ggen, cycle, 0);
1336 makerefcycle(thispoly->cycle, cycle);
1337 if ((thispoly->points + cycle)->x == curpt->x &&
1338 (thispoly->points + cycle)->y == curpt->y) {
1339 newcycle = checkcycle((genericptr)thispoly, 1);
1340 advancecycle(ggen, newcycle);
1341 cycle = newcycle;
1342 }
1343 checkwarp(thispoly->points + cycle);
1344 break;
1345 case SPLINE:
1346 thisspline = TOSPLINE(ggen);
1347 cycle = (dir > 0) ? 0 : 3;
1348 addcycle(ggen, cycle, 0);
1349 makerefcycle(thisspline->cycle, cycle);
1350 if (thisspline->ctrl[cycle].x == curpt->x &&
1351 thisspline->ctrl[cycle].y == curpt->y) {
1352 newcycle = checkcycle((genericptr)thisspline, 1);
1353 advancecycle(ggen, newcycle);
1354 cycle = newcycle;
1355 if (cycle == 1 || cycle == 2)
1356 addanticycle(nextpath, thisspline, cycle);
1357 }
1358 checkwarp(&(thisspline->ctrl[cycle]));
1359 break;
1360 }
1361 updatepath(nextpath);
1362 }
1363
1364 /*--------------------------------------------------------------*/
1365 /* Change to next edit point on a polygon */
1366 /*--------------------------------------------------------------*/
1367
nextpolycycle(polyptr * nextpoly,short dir)1368 void nextpolycycle(polyptr *nextpoly, short dir)
1369 {
1370 short newcycle;
1371
1372 newcycle = checkcycle((genericptr)(*nextpoly), dir);
1373 advancecycle((genericptr *)nextpoly, newcycle);
1374 findconstrained(*nextpoly);
1375 printeditbindings();
1376
1377 newcycle = (*nextpoly)->cycle->number;
1378 checkwarp((*nextpoly)->points + newcycle);
1379 }
1380
1381 /*--------------------------------------------------------------*/
1382 /* Change to next edit cycle on a spline */
1383 /*--------------------------------------------------------------*/
1384
nextsplinecycle(splineptr * nextspline,short dir)1385 void nextsplinecycle(splineptr *nextspline, short dir)
1386 {
1387 short newcycle;
1388 newcycle = checkcycle((genericptr)(*nextspline), dir);
1389 advancecycle((genericptr *)nextspline, newcycle);
1390
1391 if (newcycle == 1 || newcycle == 2)
1392 Wprintf("Adjust control point");
1393 else
1394 Wprintf("Adjust endpoint position");
1395
1396 checkwarp(&(*nextspline)->ctrl[newcycle]);
1397 }
1398
1399 /*--------------------------------------------------------------*/
1400 /* Warp pointer to the edit point on an arc. */
1401 /*--------------------------------------------------------------*/
1402
warparccycle(arcptr nextarc,short cycle)1403 void warparccycle(arcptr nextarc, short cycle)
1404 {
1405 XPoint curang;
1406 double rad;
1407
1408 switch(cycle) {
1409 case 0:
1410 curang.x = nextarc->position.x + abs(nextarc->radius);
1411 curang.y = nextarc->position.y;
1412 if (abs(nextarc->radius) != nextarc->yaxis)
1413 Wprintf("Adjust ellipse size");
1414 else
1415 Wprintf("Adjust arc radius");
1416 break;
1417 case 1:
1418 rad = (double)(nextarc->angle1 * RADFAC);
1419 curang.x = nextarc->position.x + abs(nextarc->radius) * cos(rad);
1420 curang.y = nextarc->position.y + nextarc->yaxis * sin(rad);
1421 Wprintf("Adjust arc endpoint");
1422 break;
1423 case 2:
1424 rad = (double)(nextarc->angle2 * RADFAC);
1425 curang.x = nextarc->position.x + abs(nextarc->radius) * cos(rad);
1426 curang.y = nextarc->position.y + nextarc->yaxis * sin(rad);
1427 Wprintf("Adjust arc endpoint");
1428 break;
1429 case 3:
1430 curang.x = nextarc->position.x;
1431 curang.y = nextarc->position.y + nextarc->yaxis;
1432 Wprintf("Adjust ellipse minor axis");
1433 break;
1434 }
1435 checkwarp(&curang);
1436 }
1437
1438 /*--------------------------------------------------------------*/
1439 /* Change to next edit cycle on an arc */
1440 /*--------------------------------------------------------------*/
1441
nextarccycle(arcptr * nextarc,short dir)1442 void nextarccycle(arcptr *nextarc, short dir)
1443 {
1444 short newcycle;
1445
1446 newcycle = checkcycle((genericptr)(*nextarc), dir);
1447 advancecycle((genericptr *)nextarc, newcycle);
1448 warparccycle(*nextarc, newcycle);
1449 }
1450
1451 /*------------------------------------------------------*/
1452 /* Get a numerical response from the keyboard (0-9) */
1453 /*------------------------------------------------------*/
1454
1455 #ifndef TCL_WRAPPER
1456
getkeynum()1457 short getkeynum()
1458 {
1459 XEvent event;
1460 XKeyEvent *keyevent = (XKeyEvent *)(&event);
1461 KeySym keypressed;
1462
1463 for (;;) {
1464 XNextEvent(dpy, &event);
1465 if (event.type == KeyPress) break;
1466 else xcDispatchEvent(&event);
1467 }
1468 XLookupString(keyevent, _STR, 150, &keypressed, NULL);
1469 if (keypressed > XK_0 && keypressed <= XK_9)
1470 return (short)(keypressed - XK_1);
1471 else
1472 return -1;
1473 }
1474
1475 #endif
1476
1477 /*--------------------------*/
1478 /* Register a "press" event */
1479 /*--------------------------*/
1480
1481 #ifdef TCL_WRAPPER
makepress(ClientData clientdata)1482 void makepress(ClientData clientdata)
1483 #else
1484 void makepress(XtPointer clientdata, xcIntervalId *id)
1485 #endif
1486 {
1487 int keywstate = (int)((pointertype)clientdata);
1488 #ifndef TCL_WRAPPER
1489 UNUSED(id);
1490 #endif
1491
1492 /* Button/Key was pressed long enough to make a "press", not a "tap" */
1493
1494 areawin->time_id = 0;
1495 pressmode = keywstate;
1496 eventdispatch(keywstate | HOLD_MASK, areawin->save.x, areawin->save.y);
1497 }
1498
1499 /*------------------------------------------------------*/
1500 /* Handle button events as if they were keyboard events */
1501 /*------------------------------------------------------*/
1502
buttonhandler(xcWidget w,caddr_t clientdata,XButtonEvent * event)1503 void buttonhandler(xcWidget w, caddr_t clientdata, XButtonEvent *event)
1504 {
1505 XKeyEvent *kevent = (XKeyEvent *)event;
1506
1507 if (event->type == ButtonPress)
1508 kevent->type = KeyPress;
1509 else
1510 kevent->type = KeyRelease;
1511
1512 switch (event->button) {
1513 case Button1:
1514 kevent->state |= Button1Mask;
1515 break;
1516 case Button2:
1517 kevent->state |= Button2Mask;
1518 break;
1519 case Button3:
1520 kevent->state |= Button3Mask;
1521 break;
1522 case Button4:
1523 kevent->state |= Button4Mask;
1524 break;
1525 case Button5:
1526 kevent->state |= Button5Mask;
1527 break;
1528 }
1529 keyhandler(w, clientdata, kevent);
1530 }
1531
1532 /*--------------------------------------------------------------*/
1533 /* Edit operations specific to polygons (point manipulation) */
1534 /*--------------------------------------------------------------*/
1535
poly_edit_op(int op)1536 void poly_edit_op(int op)
1537 {
1538 genericptr keygen = *(EDITPART);
1539 polyptr lwire;
1540 XPoint *lpoint;
1541 short cycle;
1542
1543 if (IS_PATH(keygen))
1544 keygen = getsubpart((pathptr)keygen, NULL);
1545
1546 switch(ELEMENTTYPE(keygen)) {
1547 case POLYGON: {
1548 lwire = (polyptr)keygen;
1549
1550 /* Remove a point from the polygon */
1551 if (op == XCF_Edit_Delete) {
1552 if (lwire->number < 3) return;
1553 if (lwire->number == 3 && !(lwire->style & UNCLOSED))
1554 lwire->style |= UNCLOSED;
1555 cycle = checkcycle((genericptr)lwire, 0);
1556 lwire->number--;
1557 for (lpoint = lwire->points + cycle; lpoint <
1558 lwire->points + lwire->number; lpoint++)
1559 *lpoint = *(lpoint + 1);
1560 if (eventmode == EPOLY_MODE)
1561 poly_mode_draw(xcDRAW_EDIT, TOPOLY(EDITPART));
1562 else
1563 path_mode_draw(xcDRAW_EDIT, TOPATH(EDITPART));
1564 nextpolycycle(&lwire, -1);
1565 }
1566
1567 /* Add a point to the polygon */
1568 else if (op == XCF_Edit_Insert || op == XCF_Edit_Append) {
1569 lwire->number++;
1570 lwire->points = (XPoint *)realloc(lwire->points, lwire->number
1571 * sizeof(XPoint));
1572 cycle = checkcycle((genericptr)lwire, 0);
1573 for (lpoint = lwire->points + lwire->number - 1; lpoint > lwire->
1574 points + cycle; lpoint--)
1575 *lpoint = *(lpoint - 1);
1576 if (eventmode == EPOLY_MODE)
1577 poly_mode_draw(xcDRAW_EDIT, TOPOLY(EDITPART));
1578 else
1579 path_mode_draw(xcDRAW_EDIT, TOPATH(EDITPART));
1580 if (op == XCF_Edit_Append)
1581 nextpolycycle(&lwire, 1);
1582 }
1583
1584 /* Parameterize the position of a polygon point */
1585 else if (op == XCF_Edit_Param) {
1586 cycle = checkcycle((genericptr)lwire, 0);
1587 makenumericalp(&keygen, P_POSITION_X, NULL, cycle);
1588 makenumericalp(&keygen, P_POSITION_Y, NULL, cycle);
1589 }
1590 }
1591 }
1592 }
1593
1594 /*----------------------------------------------------------------------*/
1595 /* Handle attachment of edited elements to nearby elements */
1596 /*----------------------------------------------------------------------*/
1597
attach_to()1598 void attach_to()
1599 {
1600 /* Conditions: One element is selected, key "A" is pressed. */
1601 /* Then there must exist a spline, polygon, arc, or label */
1602 /* to attach to. */
1603
1604 if (areawin->selects <= 1) {
1605 short *refsel;
1606
1607 if (areawin->attachto >= 0) {
1608 areawin->attachto = -1; /* default value---no attachments */
1609 Wprintf("Unconstrained moving");
1610 }
1611 else {
1612 int select_prev;
1613
1614 select_prev = areawin->selects;
1615 refsel = select_add_element(SPLINE|ARC|POLYGON|LABEL|OBJINST);
1616 if ((refsel != NULL) && (areawin->selects > select_prev)) {
1617
1618 /* transfer refsel over to attachto */
1619
1620 areawin->attachto = *(refsel + areawin->selects - 1);
1621 areawin->selects--;
1622 if (areawin->selects == 0) freeselects();
1623 XTopSetForeground(SELTOCOLOR(refsel));
1624 easydraw(areawin->attachto, DEFAULTCOLOR);
1625
1626 /* restore graphics state */
1627 SetForeground(dpy, areawin->gc, areawin->gccolor);
1628
1629 Wprintf("Constrained attach");
1630
1631 /* Starting a new wire? */
1632 if (eventmode == NORMAL_MODE) {
1633 XPoint newpos, userpt;
1634 userpt = UGetCursorPos();
1635 findattach(&newpos, NULL, &userpt);
1636 startwire(&newpos);
1637 eventmode = WIRE_MODE;
1638 areawin->attachto = -1;
1639 }
1640 }
1641 else {
1642 Wprintf("Nothing found to attach to");
1643 }
1644 }
1645 }
1646 }
1647
1648 /*--------------------------------------------------------------*/
1649 /* This function returns TRUE if the indicated function is */
1650 /* compatible with the current eventmode; that is, whether */
1651 /* the function could ever be called from eventdispatch() */
1652 /* given the existing eventmode. */
1653 /* */
1654 /* Note that this function has to be carefully written or the */
1655 /* function dispatch mechanism can put xcircuit into a bad */
1656 /* state. */
1657 /*--------------------------------------------------------------*/
1658
compatible_function(int function)1659 Boolean compatible_function(int function)
1660 {
1661 int r = FALSE;
1662 char *funcname;
1663
1664 switch(function) {
1665 case XCF_Text_Left: case XCF_Text_Right:
1666 case XCF_Text_Home: case XCF_Text_End:
1667 case XCF_Text_Return: case XCF_Text_Delete:
1668 case XCF_Text_Delete_Param:
1669 r = (eventmode == CATTEXT_MODE || eventmode == TEXT_MODE ||
1670 eventmode == ETEXT_MODE) ?
1671 TRUE : FALSE;
1672 break;
1673
1674 case XCF_Linebreak: case XCF_Halfspace:
1675 case XCF_Quarterspace: case XCF_TabStop:
1676 case XCF_TabForward: case XCF_TabBackward:
1677 case XCF_Superscript: case XCF_Subscript:
1678 case XCF_Normalscript: case XCF_Underline:
1679 case XCF_Overline: case XCF_Font:
1680 case XCF_Boldfont: case XCF_Italicfont:
1681 case XCF_Normalfont: case XCF_ISO_Encoding:
1682 case XCF_Special: case XCF_Text_Split:
1683 case XCF_Text_Up: case XCF_Text_Down:
1684 case XCF_Parameter:
1685 r = (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) ?
1686 TRUE : FALSE;
1687 break;
1688
1689 case XCF_Anchor:
1690 r = (eventmode == TEXT_MODE || eventmode == ETEXT_MODE ||
1691 eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1692 eventmode == NORMAL_MODE) ?
1693 TRUE : FALSE;
1694 break;
1695
1696 case XCF_Edit_Delete: case XCF_Edit_Insert: case XCF_Edit_Append:
1697 case XCF_Edit_Param:
1698 r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE) ?
1699 TRUE : FALSE;
1700 break;
1701
1702 case XCF_Edit_Next:
1703 r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE ||
1704 eventmode == EINST_MODE || eventmode == EARC_MODE ||
1705 eventmode == ESPLINE_MODE) ?
1706 TRUE : FALSE;
1707 break;
1708
1709 case XCF_Attach:
1710 r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE ||
1711 eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1712 eventmode == WIRE_MODE || eventmode == NORMAL_MODE) ?
1713 TRUE : FALSE;
1714 break;
1715
1716 case XCF_Rotate: case XCF_Flip_X:
1717 case XCF_Flip_Y:
1718 r = (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1719 eventmode == NORMAL_MODE || eventmode == CATALOG_MODE) ?
1720 TRUE : FALSE;
1721 break;
1722
1723 case XCF_Snap: case XCF_Swap:
1724 r = (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1725 eventmode == NORMAL_MODE) ?
1726 TRUE : FALSE;
1727 break;
1728
1729 case XCF_Double_Snap: case XCF_Halve_Snap:
1730 case XCF_SnapTo:
1731 r = (eventmode == CATALOG_MODE || eventmode == CATTEXT_MODE ||
1732 eventmode == ASSOC_MODE || eventmode == CATMOVE_MODE) ?
1733 FALSE : TRUE;
1734 break;
1735
1736 case XCF_Library_Pop:
1737 r = (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE) ?
1738 TRUE : FALSE;
1739 break;
1740
1741 case XCF_Library_Edit: case XCF_Library_Delete:
1742 case XCF_Library_Duplicate: case XCF_Library_Hide:
1743 case XCF_Library_Virtual: case XCF_Library_Move:
1744 case XCF_Library_Copy:
1745 r = (eventmode == CATALOG_MODE) ?
1746 TRUE : FALSE;
1747 break;
1748
1749 case XCF_Library_Directory:
1750 r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE ||
1751 eventmode == ASSOC_MODE) ?
1752 TRUE : FALSE;
1753 break;
1754
1755 case XCF_Next_Library:
1756 r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE ||
1757 eventmode == ASSOC_MODE || eventmode == CATMOVE_MODE) ?
1758 TRUE : FALSE;
1759 break;
1760
1761 case XCF_Select: case XCF_Exit:
1762 r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE) ?
1763 TRUE : FALSE;
1764 break;
1765
1766 case XCF_Pop:
1767 r = (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1768 eventmode == CATALOG_MODE || eventmode == NORMAL_MODE ||
1769 eventmode == ASSOC_MODE) ?
1770 TRUE : FALSE;
1771 break;
1772
1773 case XCF_Push:
1774 r = (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1775 eventmode == CATALOG_MODE || eventmode == NORMAL_MODE) ?
1776 TRUE : FALSE;
1777 break;
1778
1779 case XCF_SelectBox: case XCF_Wire:
1780 case XCF_Delete: case XCF_Rescale:
1781 case XCF_Pin_Label: case XCF_Pin_Global:
1782 case XCF_Info_Label: case XCF_Connectivity:
1783 case XCF_Box: case XCF_Arc:
1784 case XCF_Text: case XCF_Exchange:
1785 case XCF_Copy: case XCF_Virtual:
1786 case XCF_Page_Directory: case XCF_Join:
1787 case XCF_Unjoin: case XCF_Spline:
1788 case XCF_Edit: case XCF_Undo:
1789 case XCF_Redo: case XCF_Select_Save:
1790 case XCF_Unselect: case XCF_Dashed:
1791 case XCF_Dotted: case XCF_Solid:
1792 case XCF_Dot: case XCF_Write:
1793 case XCF_Netlist: case XCF_Sim:
1794 case XCF_SPICE: case XCF_SPICEflat:
1795 case XCF_PCB: case XCF_Move:
1796 r = (eventmode == NORMAL_MODE) ?
1797 TRUE : FALSE;
1798 break;
1799
1800 case XCF_Nothing: case XCF_View:
1801 case XCF_Redraw: case XCF_Zoom_In:
1802 case XCF_Zoom_Out: case XCF_Pan:
1803 case XCF_Page: case XCF_Help:
1804 case XCF_Cancel: case XCF_Prompt:
1805 r = TRUE;
1806 break;
1807
1808 case XCF_Continue_Copy:
1809 case XCF_Finish_Copy:
1810 r = (eventmode == COPY_MODE) ?
1811 TRUE : FALSE;
1812 break;
1813
1814 case XCF_Continue_Element:
1815 case XCF_Finish_Element:
1816 r = (eventmode == WIRE_MODE || eventmode == BOX_MODE ||
1817 eventmode == ARC_MODE || eventmode == SPLINE_MODE ||
1818 eventmode == EPATH_MODE || eventmode == EPOLY_MODE ||
1819 eventmode == EARC_MODE || eventmode == ESPLINE_MODE ||
1820 eventmode == MOVE_MODE || eventmode == CATMOVE_MODE ||
1821 eventmode == EINST_MODE || eventmode == RESCALE_MODE) ?
1822 TRUE : FALSE;
1823 break;
1824
1825 case XCF_Cancel_Last:
1826 r = (eventmode == WIRE_MODE || eventmode == ARC_MODE ||
1827 eventmode == SPLINE_MODE || eventmode == EPATH_MODE ||
1828 eventmode == EPOLY_MODE || eventmode == EARC_MODE ||
1829 eventmode == EINST_MODE || eventmode == ESPLINE_MODE) ?
1830 TRUE : FALSE;
1831 break;
1832
1833 case XCF_Finish:
1834 r = (eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE ||
1835 eventmode == ASSOC_MODE || eventmode == CATALOG_MODE ||
1836 eventmode == CATTEXT_MODE || eventmode == MOVE_MODE ||
1837 eventmode == RESCALE_MODE || eventmode == SELAREA_MODE ||
1838 eventmode == PAN_MODE || eventmode == NORMAL_MODE ||
1839 eventmode == CATMOVE_MODE) ?
1840 TRUE : FALSE;
1841 break;
1842
1843 default: /* Function type was not handled. */
1844 funcname = func_to_string(function);
1845 if (funcname == NULL)
1846 Wprintf("Error: \"%s\" is not a known function!");
1847 else
1848 Wprintf("Error: Function type \"%s\" (%d) not handled by "
1849 "compatible_function()", func_to_string(function),
1850 function);
1851 break;
1852 }
1853 return r;
1854 }
1855
1856 /*----------------------------------------------------------------------*/
1857 /* Main event dispatch routine. Call one of the known routines based */
1858 /* on the key binding. Some handling is done by secondary dispatch */
1859 /* routines in other files; when this is done, the key binding is */
1860 /* determined here and the bound operation type passed to the secondary */
1861 /* dispatch routine. */
1862 /* */
1863 /* Return value: 0 if event was handled, -1 if not. */
1864 /*----------------------------------------------------------------------*/
1865
eventdispatch(int keywstate,int x,int y)1866 int eventdispatch(int keywstate, int x, int y)
1867 {
1868 short value; /* For return values from boundfunction() */
1869 int function; /* What function should be invoked */
1870 int handled = -1; /* event was handled */
1871
1872 /* Invalid key state returned from getkeysignature(); usually this */
1873 /* means a modifier key pressed by itself. */
1874
1875 if (keywstate == -1) return -1;
1876 function = boundfunction(areawin->area, keywstate, &value);
1877
1878 /* Check for ASCII or ISO-Latin1-9 characters in keywstate while in */
1879 /* a text-entry state. Only the function XCF_Special is allowed in */
1880 /* text-entry mode, because XCF_Special can be used to enter the */
1881 /* character bound to the XCF_Special function. */
1882
1883 if (keywstate >= 32 && keywstate < 256) {
1884 if (eventmode == CATTEXT_MODE || eventmode == TEXT_MODE ||
1885 eventmode == ETEXT_MODE) {
1886 if (function != XCF_Special)
1887 handled = labeltext(keywstate, NULL);
1888 else if (eventmode != CATTEXT_MODE) {
1889 labelptr elabel = TOLABEL(EDITPART);
1890 if (elabel->anchor & LATEXLABEL)
1891 handled = labeltext(keywstate, NULL);
1892 }
1893 }
1894 }
1895
1896 if (handled == -1) {
1897 if (function > -1)
1898 handled = functiondispatch(function, value, x, y);
1899 else {
1900 char *keystring = key_to_string(keywstate);
1901 #ifdef HAVE_PYTHON
1902 if (python_key_command(keywstate) < 0)
1903 #endif
1904 Wprintf("Key \'%s\' is not bound to a macro", keystring);
1905 free(keystring);
1906 }
1907 }
1908
1909 if (areawin->redraw_needed)
1910 drawarea(NULL, NULL, NULL);
1911
1912 return handled;
1913 }
1914
1915 /*----------------------------------------------------------------------*/
1916 /* Dispatch actions by function number. Note that the structure of */
1917 /* this function is closely tied to the routine compatible_function(). */
1918 /*----------------------------------------------------------------------*/
1919
functiondispatch(int function,short value,int x,int y)1920 int functiondispatch(int function, short value, int x, int y)
1921 {
1922 int result = 0;
1923
1924 switch (eventmode) {
1925 case MOVE_MODE:
1926 case COPY_MODE:
1927 snap(x, y, &areawin->save);
1928 break;
1929 case NORMAL_MODE:
1930 window_to_user(x, y, &areawin->save);
1931 break;
1932 default:
1933 break;
1934 }
1935
1936 switch(function) {
1937 case XCF_Page:
1938 if (value < 0 || value > xobjs.pages)
1939 Wprintf("Page %d out of range.", (int)value);
1940 else
1941 newpage(value - 1);
1942 break;
1943 case XCF_Anchor:
1944 reanchor(value);
1945 break;
1946 case XCF_Superscript:
1947 labeltext(SUPERSCRIPT, (char *)1);
1948 break;
1949 case XCF_Subscript:
1950 labeltext(SUBSCRIPT, (char *)1);
1951 break;
1952 case XCF_Normalscript:
1953 labeltext(NORMALSCRIPT, (char *)1);
1954 break;
1955 case XCF_Font:
1956 setfont(NULL, 1000, NULL);
1957 break;
1958 case XCF_Boldfont:
1959 fontstyle(NULL, 1, NULL);
1960 break;
1961 case XCF_Italicfont:
1962 fontstyle(NULL, 2, NULL);
1963 break;
1964 case XCF_Normalfont:
1965 fontstyle(NULL, 0, NULL);
1966 break;
1967 case XCF_Underline:
1968 labeltext(UNDERLINE, (char *)1);
1969 break;
1970 case XCF_Overline:
1971 labeltext(OVERLINE, (char *)1);
1972 break;
1973 case XCF_ISO_Encoding:
1974 fontencoding(NULL, 2, NULL);
1975 break;
1976 case XCF_Halfspace:
1977 labeltext(HALFSPACE, (char *)1);
1978 break;
1979 case XCF_Quarterspace:
1980 labeltext(QTRSPACE, (char *)1);
1981 break;
1982 case XCF_Special:
1983 result = dospecial();
1984 break;
1985 #ifndef TCL_WRAPPER
1986 case XCF_Parameter:
1987 insertparam();
1988 break;
1989 #endif
1990 case XCF_TabStop:
1991 labeltext(TABSTOP, (char *)1);
1992 break;
1993 case XCF_TabForward:
1994 labeltext(TABFORWARD, (char *)1);
1995 break;
1996 case XCF_TabBackward:
1997 labeltext(TABBACKWARD, (char *)1);
1998 break;
1999 case XCF_Text_Return:
2000 labeltext(TEXT_RETURN, (char *)1);
2001 break;
2002 case XCF_Text_Delete:
2003 labeltext(TEXT_DELETE, (char *)1);
2004 break;
2005 case XCF_Text_Delete_Param:
2006 labeltext(TEXT_DEL_PARAM, (char *)1);
2007 break;
2008 case XCF_Text_Right:
2009 labeltext(TEXT_RIGHT, (char *)1);
2010 break;
2011 case XCF_Text_Left:
2012 labeltext(TEXT_LEFT, (char *)1);
2013 break;
2014 case XCF_Text_Up:
2015 labeltext(TEXT_UP, (char *)1);
2016 break;
2017 case XCF_Text_Down:
2018 labeltext(TEXT_DOWN, (char *)1);
2019 break;
2020 case XCF_Text_Split:
2021 labeltext(TEXT_SPLIT, (char *)1);
2022 break;
2023 case XCF_Text_Home:
2024 labeltext(TEXT_HOME, (char *)1);
2025 break;
2026 case XCF_Text_End:
2027 labeltext(TEXT_END, (char *)1);
2028 break;
2029 case XCF_Linebreak:
2030 labeltext(RETURN, (char *)1);
2031 break;
2032 case XCF_Edit_Param:
2033 case XCF_Edit_Delete:
2034 case XCF_Edit_Insert:
2035 case XCF_Edit_Append:
2036 poly_edit_op(function);
2037 break;
2038 case XCF_Edit_Next:
2039 path_op(*(EDITPART), XCF_Continue_Element, x, y);
2040 break;
2041 case XCF_Attach:
2042 attach_to();
2043 break;
2044 case XCF_Next_Library:
2045 changecat();
2046 break;
2047 case XCF_Library_Directory:
2048 startcatalog(NULL, LIBLIB, NULL);
2049 break;
2050 case XCF_Library_Edit:
2051 window_to_user(x, y, &areawin->save);
2052 unselect_all();
2053 select_element(LABEL);
2054 if (areawin->selects == 1)
2055 edit(x, y);
2056 break;
2057 case XCF_Library_Delete:
2058 catalog_op(XCF_Select, x, y);
2059 catdelete();
2060 break;
2061 case XCF_Library_Duplicate:
2062 catalog_op(XCF_Select, x, y);
2063 copycat();
2064 break;
2065 case XCF_Library_Hide:
2066 catalog_op(XCF_Select, x, y);
2067 cathide();
2068 break;
2069 case XCF_Library_Virtual:
2070 catalog_op(XCF_Select, x, y);
2071 catvirtualcopy();
2072 break;
2073 case XCF_Page_Directory:
2074 startcatalog(NULL, PAGELIB, NULL);
2075 break;
2076 case XCF_Library_Copy:
2077 case XCF_Library_Pop:
2078 catalog_op(function, x, y);
2079 break;
2080 case XCF_Virtual:
2081 copyvirtual();
2082 break;
2083 case XCF_Help:
2084 starthelp(NULL, NULL, NULL);
2085 break;
2086 case XCF_Redraw:
2087 areawin->redraw_needed = True;
2088 break;
2089 case XCF_View:
2090 zoomview(NULL, NULL, NULL);
2091 break;
2092 case XCF_Zoom_In:
2093 zoominrefresh(x, y);
2094 break;
2095 case XCF_Zoom_Out:
2096 zoomoutrefresh(x, y);
2097 break;
2098 case XCF_Pan:
2099 panrefresh(value, x, y, 0.3);
2100 break;
2101 case XCF_Double_Snap:
2102 setsnap(1);
2103 break;
2104 case XCF_Halve_Snap:
2105 setsnap(-1);
2106 break;
2107 case XCF_Write:
2108 #ifdef TCL_WRAPPER
2109 Tcl_Eval(xcinterp, "xcircuit::promptsavepage");
2110 #else
2111 outputpopup(NULL, NULL, NULL);
2112 #endif
2113 break;
2114 case XCF_Rotate:
2115 elementrotate(value, &areawin->save);
2116 break;
2117 case XCF_Flip_X:
2118 elementflip(&areawin->save);
2119 break;
2120 case XCF_Flip_Y:
2121 elementvflip(&areawin->save);
2122 break;
2123 case XCF_Snap:
2124 snapelement();
2125 break;
2126 case XCF_SnapTo:
2127 if (areawin->snapto) {
2128 areawin->snapto = False;
2129 Wprintf("Snap-to off");
2130 }
2131 else {
2132 areawin->snapto = True;
2133 Wprintf("Snap-to on");
2134 }
2135 break;
2136 case XCF_Pop:
2137 if (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE) {
2138 eventmode = NORMAL_MODE;
2139 catreturn();
2140 }
2141 else
2142 popobject(NULL, 0, NULL);
2143 break;
2144 case XCF_Push:
2145 if (eventmode == CATALOG_MODE) {
2146 /* Don't allow push from library directory */
2147 if ((areawin->topinstance != xobjs.libtop[LIBLIB])
2148 && (areawin->topinstance != xobjs.libtop[PAGELIB])) {
2149 window_to_user(x, y, &areawin->save);
2150 eventmode = NORMAL_MODE;
2151 pushobject(NULL);
2152 }
2153 }
2154 else
2155 pushobject(NULL);
2156 break;
2157 case XCF_Delete:
2158 deletebutton(x, y);
2159 break;
2160 case XCF_Select:
2161 if (eventmode == CATALOG_MODE)
2162 catalog_op(function, x, y);
2163 else
2164 select_add_element(ALL_TYPES);
2165 break;
2166 case XCF_Box:
2167 boxbutton(x, y);
2168 break;
2169 case XCF_Arc:
2170 arcbutton(x, y);
2171 break;
2172 case XCF_Text:
2173 eventmode = TEXT_MODE;
2174 textbutton(NORMAL, x, y);
2175 break;
2176 case XCF_Exchange:
2177 exchange();
2178 break;
2179 case XCF_Library_Move:
2180 /* Don't allow from library directory. Then fall through to XCF_Move */
2181 if (areawin->topinstance == xobjs.libtop[LIBLIB]) break;
2182 case XCF_Move:
2183 if (areawin->selects == 0) {
2184 was_preselected = FALSE;
2185 if (eventmode == CATALOG_MODE)
2186 catalog_op(XCF_Select, x, y);
2187 else
2188 select_element(ALL_TYPES);
2189 }
2190 else was_preselected = TRUE;
2191
2192 if (areawin->selects > 0) {
2193 eventmode = (eventmode == CATALOG_MODE) ? CATMOVE_MODE : MOVE_MODE;
2194 u2u_snap(&areawin->save);
2195 areawin->origin = areawin->save;
2196 reset_cycles();
2197 select_connected_pins();
2198 XDefineCursor(dpy, areawin->window, ARROW);
2199 move_mode_draw(xcDRAW_INIT, NULL);
2200 #ifdef TCL_WRAPPER
2201 Tk_CreateEventHandler(areawin->area, ButtonMotionMask |
2202 PointerMotionMask, (Tk_EventProc *)xctk_drag,
2203 NULL);
2204 #endif
2205 }
2206 break;
2207 case XCF_Join:
2208 join();
2209 break;
2210 case XCF_Unjoin:
2211 unjoin();
2212 break;
2213 case XCF_Spline:
2214 splinebutton(x, y);
2215 break;
2216 case XCF_Edit:
2217 edit(x, y);
2218 break;
2219 case XCF_Undo:
2220 undo_action();
2221 break;
2222 case XCF_Redo:
2223 redo_action();
2224 break;
2225 case XCF_Select_Save:
2226 #ifdef TCL_WRAPPER
2227 Tcl_Eval(xcinterp, "xcircuit::promptmakeobject");
2228 #else
2229 selectsave(NULL, NULL, NULL);
2230 #endif
2231 break;
2232 case XCF_Unselect:
2233 select_add_element(-ALL_TYPES);
2234 break;
2235 case XCF_Dashed:
2236 setelementstyle(NULL, DASHED, NOBORDER | DOTTED | DASHED);
2237 break;
2238 case XCF_Dotted:
2239 setelementstyle(NULL, DOTTED, NOBORDER | DOTTED | DASHED);
2240 break;
2241 case XCF_Solid:
2242 setelementstyle(NULL, NORMAL, NOBORDER | DOTTED | DASHED);
2243 break;
2244 case XCF_Prompt:
2245 docommand();
2246 break;
2247 case XCF_Dot:
2248 snap(x, y, &areawin->save);
2249 drawdot(areawin->save.x, areawin->save.y);
2250 areawin->redraw_needed = True;
2251 drawarea(NULL, NULL, NULL);
2252 break;
2253 case XCF_Wire:
2254 u2u_snap(&areawin->save);
2255 startwire(&areawin->save);
2256 eventmode = WIRE_MODE;
2257 break;
2258 case XCF_Nothing:
2259 DoNothing(NULL, NULL, NULL);
2260 break;
2261 case XCF_Exit:
2262 quitcheck(areawin->area, NULL, NULL);
2263 break;
2264 case XCF_Netlist:
2265 callwritenet(NULL, 0, NULL);
2266 break;
2267 case XCF_Swap:
2268 swapschem(0, -1, NULL);
2269 break;
2270 case XCF_Pin_Label:
2271 eventmode = TEXT_MODE;
2272 textbutton(LOCAL, x, y);
2273 break;
2274 case XCF_Pin_Global:
2275 eventmode = TEXT_MODE;
2276 textbutton(GLOBAL, x, y);
2277 break;
2278 case XCF_Info_Label:
2279 eventmode = TEXT_MODE;
2280 textbutton(INFO, x, y);
2281 break;
2282 case XCF_Rescale:
2283 if (checkselect(LABEL | OBJINST | GRAPHIC) == TRUE) {
2284 eventmode = RESCALE_MODE;
2285 rescale_mode_draw(xcDRAW_INIT, NULL);
2286 #ifdef TCL_WRAPPER
2287 Tk_CreateEventHandler(areawin->area, PointerMotionMask |
2288 ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
2289 #else
2290 xcAddEventHandler(areawin->area, PointerMotionMask |
2291 ButtonMotionMask, False, (xcEventHandler)xlib_drag,
2292 NULL);
2293 #endif
2294 }
2295 break;
2296 case XCF_SelectBox:
2297 startselect();
2298 break;
2299 case XCF_Connectivity:
2300 connectivity(NULL, NULL, NULL);
2301 break;
2302 case XCF_Copy:
2303 case XCF_Continue_Copy:
2304 case XCF_Finish_Copy:
2305 copy_op(function, x, y);
2306 break;
2307 case XCF_Continue_Element:
2308 if (eventmode == CATMOVE_MODE || eventmode == MOVE_MODE ||
2309 eventmode == RESCALE_MODE)
2310 finish_op(XCF_Finish, x, y);
2311 else
2312 continue_op(function, x, y);
2313 break;
2314 case XCF_Finish_Element:
2315 case XCF_Cancel_Last:
2316 case XCF_Cancel:
2317 finish_op(function, x, y);
2318 break;
2319 case XCF_Finish:
2320 if (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE)
2321 catalog_op(XCF_Library_Pop, x, y);
2322 else
2323 finish_op(function, x, y);
2324 break;
2325 case XCF_Sim:
2326 writenet(topobject, "flatsim", "sim");
2327 break;
2328 case XCF_SPICE:
2329 writenet(topobject, "spice", "spc");
2330 break;
2331 case XCF_PCB:
2332 writenet(topobject, "pcb", "pcbnet");
2333 break;
2334 case XCF_SPICEflat:
2335 writenet(topobject, "flatspice", "fspc");
2336 break;
2337 case XCF_Graphic:
2338 Wprintf("Action not handled");
2339 result = -1;
2340 break;
2341 case XCF_ChangeStyle:
2342 Wprintf("Action not handled");
2343 result = -1;
2344 break;
2345 }
2346
2347 /* Ensure that we do not get stuck in suspend mode */
2348 /* by removing the suspend state whenever a key is */
2349 /* pressed. */
2350
2351 if (xobjs.suspend == 1) {
2352 xobjs.suspend = -1;
2353 refresh(NULL, NULL, NULL);
2354 }
2355 else if (xobjs.suspend != 2)
2356 xobjs.suspend = -1;
2357
2358 return result;
2359 }
2360
2361 /* forward declaration */
2362 extern int utf8_reverse_lookup(char *);
2363
2364 /*------------------------------------------------------*/
2365 /* Get a canonical signature for a button/key event */
2366 /*------------------------------------------------------*/
2367
getkeysignature(XKeyEvent * event)2368 int getkeysignature(XKeyEvent *event)
2369 {
2370 KeySym keypressed;
2371 int keywstate; /* KeySym with prepended state information */
2372
2373 int utf8enc; /* 8-bit value bound to UTF-8 encoding */
2374 char buffer[16];
2375 KeySym keysym;
2376 Status status;
2377 static XIM xim = NULL;
2378 static XIC xic = NULL;
2379
2380 #ifdef _MSC_VER
2381 if (event->keycode == 0 && event->state == 0)
2382 return -1;
2383 #endif
2384 XLookupString(event, _STR, 150, &keypressed, NULL);
2385
2386 /* Ignore Shift, Control, Caps Lock, and Meta (Alt) keys */
2387 /* when pressed alone. */
2388
2389 if (keypressed == XK_Control_L || keypressed == XK_Control_R ||
2390 keypressed == XK_Alt_L || keypressed == XK_Alt_R ||
2391 keypressed == XK_Caps_Lock || keypressed == XK_Shift_L ||
2392 keypressed == XK_Shift_R)
2393 return -1;
2394
2395 /* Only keep key state information pertaining to Shift, Caps Lock, */
2396 /* Control, and Alt (Meta) */
2397
2398 keywstate = (keypressed & 0xffff);
2399
2400 /* Convert codes outside the character (0 - 255) range but within */
2401 /* the ISO-Latin1,...,9 encoding scheme (256-5120). X11 has unique */
2402 /* keysyms for each character, but the ISO-Latin encodings define */
2403 /* mappings to the 8-bit (256) character set. */
2404
2405 if (keywstate >= 256 && keywstate < 5120)
2406 keywstate = XKeysymToKeycode(dpy, (KeySym)keywstate);
2407
2408 if (event->keycode != 0) { /* Only for actual key events */
2409
2410 /* Get keyboard input method */
2411 if (xim == NULL) {
2412 xim = XOpenIM(dpy, 0, 0, 0);
2413 xic = XCreateIC(xim,
2414 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
2415 XNClientWindow, areawin->window,
2416 XNFocusWindow, areawin->window,
2417 NULL);
2418 XSetICFocus(xic);
2419 }
2420 if (xic) {
2421 /* Do a UTF-8 code lookup */
2422 Xutf8LookupString(xic, event, buffer, 15, &keysym, &status);
2423 /* Convert a UTF-8 code to a known encoding */
2424 utf8enc = utf8_reverse_lookup(buffer);
2425 if ((utf8enc != -1) && (utf8enc != (keywstate & 0xff))) keywstate = utf8enc;
2426 }
2427 }
2428
2429 /* ASCII values already come upper/lowercase; we only want to register */
2430 /* a Shift key if it's a non-ASCII key or another modifier is in effect */
2431
2432 keywstate |= (((LockMask | ControlMask | Mod1Mask) & event->state) << 16);
2433 if (keywstate > 255) keywstate |= ((ShiftMask & event->state) << 16);
2434
2435 /* Treat button events and key events in the same way by setting */
2436 /* a key state for buttons */
2437
2438 if (keypressed == 0)
2439 keywstate |= (((Button1Mask | Button2Mask | Button3Mask | Button4Mask |
2440 Button5Mask | ShiftMask)
2441 & event->state) << 16);
2442
2443 return keywstate;
2444 }
2445
2446 /*------------------------*/
2447 /* Handle keyboard inputs */
2448 /*------------------------*/
2449
keyhandler(xcWidget w,caddr_t clientdata,XKeyEvent * event)2450 void keyhandler(xcWidget w, caddr_t clientdata, XKeyEvent *event)
2451 {
2452 int keywstate; /* KeySym with prepended state information */
2453 int func;
2454 UNUSED(w); UNUSED(clientdata);
2455
2456 #ifdef TCL_WRAPPER
2457 if (popups > 0) return;
2458 #else
2459 if (popups > 0 && help_up == 0) return;
2460 #endif
2461
2462 if ((event->type == KeyRelease) || (event->type == ButtonRelease)) {
2463
2464 /* Register a "tap" event if a key or button was released */
2465 /* while a timeout event is pending. */
2466
2467 if (areawin->time_id != 0) {
2468 xcRemoveTimeOut(areawin->time_id);
2469 areawin->time_id = 0;
2470 keywstate = getkeysignature(event);
2471 eventdispatch(keywstate, areawin->save.x, areawin->save.y);
2472 }
2473 else {
2474 keywstate = getkeysignature(event);
2475 if ((pressmode != 0) && (keywstate == pressmode)) {
2476 /* Events that require hold & drag (namely, MOVE_MODE) */
2477 /* must be resolved here. Call finish_op() to ensure */
2478 /* that we restore xcircuit to a state of sanity. */
2479
2480 finish_op(XCF_Finish, event->x, event->y);
2481 pressmode = 0;
2482 if (areawin->redraw_needed)
2483 drawarea(NULL, NULL, NULL);
2484 }
2485 return; /* Ignore all other release events */
2486 }
2487 }
2488
2489 /* Check if any bindings match key/button "hold". If so, then start */
2490 /* the timer and wait for key release or timeout. */
2491
2492 else {
2493 keywstate = getkeysignature(event);
2494 if ((keywstate != -1) && (xobjs.hold == TRUE)) {
2495
2496 /* Establish whether a HOLD modifier binding would apply in */
2497 /* the current eventmode. If so, set the HOLD timer. */
2498
2499 func = boundfunction(areawin->area, keywstate | HOLD_MASK, NULL);
2500 if (func != -1) {
2501 areawin->save.x = event->x;
2502 areawin->save.y = event->y;
2503 areawin->time_id = xcAddTimeOut(app, PRESSTIME,
2504 makepress, (ClientData)((pointertype)keywstate));
2505 return;
2506 }
2507
2508 }
2509 eventdispatch(keywstate, event->x, event->y);
2510 }
2511 }
2512
2513 /*--------------------------------*/
2514 /* Set snap spacing from keyboard */
2515 /*--------------------------------*/
2516
setsnap(short direction)2517 void setsnap(short direction)
2518 {
2519 float oldsnap = xobjs.pagelist[areawin->page]->snapspace;
2520 char buffer[50];
2521
2522 if (direction > 0) xobjs.pagelist[areawin->page]->snapspace *= 2;
2523 else {
2524 if (oldsnap >= 2.0)
2525 xobjs.pagelist[areawin->page]->snapspace /= 2;
2526 else {
2527 measurestr(xobjs.pagelist[areawin->page]->snapspace, buffer);
2528 Wprintf("Snap space at minimum value of %s", buffer);
2529 }
2530 }
2531 if (xobjs.pagelist[areawin->page]->snapspace != oldsnap) {
2532 measurestr(xobjs.pagelist[areawin->page]->snapspace, buffer);
2533 Wprintf("Snap spacing set to %s", buffer);
2534 areawin->redraw_needed = True;
2535 drawarea(NULL, NULL, NULL);
2536 }
2537 }
2538
2539 /*-----------------------------------------*/
2540 /* Reposition an object onto the snap grid */
2541 /*-----------------------------------------*/
2542
snapelement()2543 void snapelement()
2544 {
2545 short *selectobj;
2546 Boolean preselected;
2547
2548 preselected = (areawin->selects > 0) ? TRUE : FALSE;
2549 if (!checkselect(ALL_TYPES)) return;
2550 SetForeground(dpy, areawin->gc, BACKGROUND);
2551 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
2552 + areawin->selects; selectobj++) {
2553 easydraw(*selectobj, DOFORALL);
2554 switch(SELECTTYPE(selectobj)) {
2555 case OBJINST: {
2556 objinstptr snapobj = SELTOOBJINST(selectobj);
2557
2558 u2u_snap(&snapobj->position);
2559 } break;
2560 case GRAPHIC: {
2561 graphicptr snapg = SELTOGRAPHIC(selectobj);
2562
2563 u2u_snap(&snapg->position);
2564 } break;
2565 case LABEL: {
2566 labelptr snaplabel = SELTOLABEL(selectobj);
2567
2568 u2u_snap(&snaplabel->position);
2569 } break;
2570 case POLYGON: {
2571 polyptr snappoly = SELTOPOLY(selectobj);
2572 pointlist snappoint;
2573
2574 for (snappoint = snappoly->points; snappoint < snappoly->points +
2575 snappoly->number; snappoint++)
2576 u2u_snap(snappoint);
2577 } break;
2578 case ARC: {
2579 arcptr snaparc = SELTOARC(selectobj);
2580
2581 u2u_snap(&snaparc->position);
2582 if (areawin->snapto) {
2583 snaparc->radius = (snaparc->radius /
2584 xobjs.pagelist[areawin->page]->snapspace) *
2585 xobjs.pagelist[areawin->page]->snapspace;
2586 snaparc->yaxis = (snaparc->yaxis /
2587 xobjs.pagelist[areawin->page]->snapspace) *
2588 xobjs.pagelist[areawin->page]->snapspace;
2589 }
2590 calcarc(snaparc);
2591 } break;
2592 case SPLINE: {
2593 splineptr snapspline = SELTOSPLINE(selectobj);
2594 short i;
2595
2596 for (i = 0; i < 4; i++)
2597 u2u_snap(&snapspline->ctrl[i]);
2598 calcspline(snapspline);
2599 } break;
2600 }
2601 if (preselected || (eventmode != NORMAL_MODE)) {
2602 SetForeground(dpy, areawin->gc, SELECTCOLOR);
2603 easydraw(*selectobj, DOFORALL);
2604 }
2605 }
2606 select_invalidate_netlist();
2607 if (eventmode == NORMAL_MODE)
2608 if (!preselected)
2609 unselect_all();
2610 }
2611
2612 /*----------------------------------------------*/
2613 /* Routines to print the cursor position */
2614 /*----------------------------------------------*/
2615
2616 /*----------------------------------------------*/
2617 /* fast integer power-of-10 routine */
2618 /*----------------------------------------------*/
2619
ipow10(int a)2620 int ipow10(int a)
2621 {
2622 int i;
2623 char istr[12];
2624
2625 switch (a) {
2626 case 0: return 1; break;
2627 case 1: return 10; break;
2628 case 2: return 100; break;
2629 case 3: return 1000; break;
2630 default:
2631 istr[0] = '1';
2632 for (i = 1; i < a + 1; i++) istr[i] = '0';
2633 istr[i] = '\0';
2634 return atoi(istr);
2635 break;
2636 }
2637 }
2638
2639 /*--------------------------------------------------*/
2640 /* find greatest common factor between two integers */
2641 /*--------------------------------------------------*/
2642
calcgcf(int a,int b)2643 int calcgcf(int a, int b)
2644 {
2645 register int mod;
2646
2647 if ((mod = a % b) == 0) return (b);
2648 else return (calcgcf(b, mod));
2649 }
2650
2651 /*--------------------------------------------------------------*/
2652 /* generate a fraction from a float, if possible */
2653 /* fraction returned as a string (must be allocated beforehand) */
2654 /*--------------------------------------------------------------*/
2655
fraccalc(float xyval,char * fstr)2656 void fraccalc(float xyval, char *fstr)
2657 {
2658 short i, t, rept;
2659 int ip, mant, divisor, denom, numer, rpart;
2660 double fp;
2661 char num[10], *nptr = &num[2], *sptr;
2662
2663 ip = (int)xyval;
2664 fp = fabs(xyval - ip);
2665
2666 /* write fractional part and grab mantissa as integer */
2667
2668 sprintf(num, "%1.7f", fp);
2669 num[8] = '\0'; /* no rounding up! */
2670 sscanf(nptr, "%d", &mant);
2671
2672 if (mant != 0) { /* search for repeating substrings */
2673 for (i = 1; i <= 3; i++) {
2674 rept = 1;
2675 nptr = &num[8] - i;
2676 while ((sptr = nptr - rept * i) >= &num[2]) {
2677 for (t = 0; t < i; t++)
2678 if (*(sptr + t) != *(nptr + t)) break;
2679 if (t != i) break;
2680 else rept++;
2681 }
2682 if (rept > 1) break;
2683 }
2684 nptr = &num[8] - i;
2685 sscanf(nptr, "%d", &rpart); /* rpart is repeating part of mantissa */
2686 if (i > 3 || rpart == 0) { /* no repeat */
2687 divisor = calcgcf(1000000, mant);
2688 denom = 1000000 / divisor;
2689 }
2690 else { /* repeat */
2691 int z, p, fd;
2692
2693 *nptr = '\0';
2694 sscanf(&num[2], "%d", &z);
2695 p = ipow10(i) - 1;
2696 mant = z * p + rpart;
2697 fd = ipow10(nptr - &num[2]) * p;
2698
2699 divisor = calcgcf(fd, mant);
2700 denom = fd / divisor;
2701 }
2702 numer = mant / divisor;
2703 if (denom > 1024)
2704 sprintf(fstr, "%5.3f", xyval);
2705 else if (ip == 0)
2706 sprintf(fstr, "%hd/%hd", (xyval > 0) ? numer : -numer, denom);
2707 else
2708 sprintf(fstr, "%hd %hd/%hd", ip, numer, denom);
2709 }
2710 else sprintf(fstr, "%hd", ip);
2711 }
2712
2713 /*------------------------------------------------------------------------------*/
2714 /* Print the position of the cursor in the upper right-hand message window */
2715 /*------------------------------------------------------------------------------*/
2716
printpos(short xval,short yval)2717 void printpos(short xval, short yval)
2718 {
2719 float f1, f2;
2720 float oscale, iscale = (float)xobjs.pagelist[areawin->page]->drawingscale.y /
2721 (float)xobjs.pagelist[areawin->page]->drawingscale.x;
2722 int llen, lwid;
2723 u_char wlflag = 0;
2724 XPoint *tpoint, *npoint;
2725 char *sptr;
2726 short cycle;
2727
2728 /* For polygons, print the length (last line of a wire or polygon) or */
2729 /* length and width (box only) */
2730
2731 if (eventmode == BOX_MODE || eventmode == EPOLY_MODE || eventmode == WIRE_MODE) {
2732 polyptr lwire = (eventmode == BOX_MODE) ? TOPOLY(ENDPART) : TOPOLY(EDITPART);
2733 if ((eventmode == EPOLY_MODE) && (lwire->number > 2)) {
2734 /* sanity check on edit cycle */
2735 cycle = (lwire->cycle) ? lwire->cycle->number : -1;
2736 if (cycle < 0 || cycle >= lwire->number) {
2737 advancecycle((genericptr *)(&lwire), 0);
2738 cycle = 0;
2739 }
2740 tpoint = lwire->points + cycle;
2741 npoint = lwire->points + checkcycle((genericptr)lwire, 1);
2742 llen = wirelength(tpoint, npoint);
2743 npoint = lwire->points + checkcycle((genericptr)lwire, -1);
2744 lwid = wirelength(tpoint, npoint);
2745 wlflag = 3;
2746 if (lwire->style & UNCLOSED) { /* unclosed polys */
2747 if (cycle == 0)
2748 wlflag = 1;
2749 else if (cycle == lwire->number - 1) {
2750 wlflag = 1;
2751 llen = lwid;
2752 }
2753 }
2754 if ((npoint->y - tpoint->y) == 0) { /* swap width and length */
2755 int tmp = lwid;
2756 lwid = llen;
2757 llen = tmp;
2758 }
2759 }
2760 else if (eventmode == BOX_MODE) {
2761 tpoint = lwire->points;
2762 npoint = lwire->points + 1;
2763 llen = wirelength(tpoint, npoint);
2764 npoint = lwire->points + 3;
2765 lwid = wirelength(tpoint, npoint);
2766 if ((npoint->y - tpoint->y) == 0) { /* swap width and length */
2767 int tmp = lwid;
2768 lwid = llen;
2769 llen = tmp;
2770 }
2771 wlflag = 3;
2772 }
2773 else {
2774 tpoint = lwire->points + lwire->number - 1;
2775 llen = wirelength(tpoint - 1, tpoint);
2776 wlflag = 1;
2777 }
2778 }
2779 else if (eventmode == ARC_MODE || eventmode == EARC_MODE) {
2780 arcptr larc = (eventmode == ARC_MODE) ? TOARC(ENDPART) : TOARC(EDITPART);
2781 llen = larc->radius;
2782 if (abs(larc->radius) != larc->yaxis) {
2783 lwid = larc->yaxis;
2784 wlflag = 3;
2785 }
2786 else
2787 wlflag = 1;
2788 }
2789
2790 switch (xobjs.pagelist[areawin->page]->coordstyle) {
2791 case INTERNAL:
2792 sprintf(_STR, "%g, %g", xval * iscale, yval * iscale);
2793 sptr = _STR + strlen(_STR);
2794 if (wlflag) {
2795 if (wlflag & 2)
2796 sprintf(sptr, " (%g x %g)", llen * iscale, lwid * iscale);
2797 else
2798 sprintf(sptr, " (length %g)", llen * iscale);
2799 }
2800 break;
2801 case DEC_INCH:
2802 oscale = xobjs.pagelist[areawin->page]->outscale * INCHSCALE;
2803 f1 = ((float)(xval) * iscale * oscale) / 72.0;
2804 f2 = ((float)(yval) * iscale * oscale) / 72.0;
2805 sprintf(_STR, "%5.3f, %5.3f in", f1, f2);
2806 sptr = _STR + strlen(_STR);
2807 if (wlflag) {
2808 f1 = ((float)(llen) * iscale * oscale) / 72.0;
2809 if (wlflag & 2) {
2810 f2 = ((float)(lwid) * iscale * oscale) / 72.0;
2811 sprintf(sptr, " (%5.3f x %5.3f in)", f1, f2);
2812 }
2813 else
2814 sprintf(sptr, " (length %5.3f in)", f1);
2815 }
2816 break;
2817 case FRAC_INCH: {
2818 char fstr1[30], fstr2[30];
2819
2820 oscale = xobjs.pagelist[areawin->page]->outscale * INCHSCALE;
2821 fraccalc((((float)(xval) * iscale * oscale) / 72.0), fstr1);
2822 fraccalc((((float)(yval) * iscale * oscale) / 72.0), fstr2);
2823 sprintf(_STR, "%s, %s in", fstr1, fstr2);
2824 sptr = _STR + strlen(_STR);
2825 if (wlflag) {
2826 fraccalc((((float)(llen) * iscale * oscale) / 72.0), fstr1);
2827 if (wlflag & 2) {
2828 fraccalc((((float)(lwid) * iscale * oscale) / 72.0), fstr2);
2829 sprintf(sptr, " (%s x %s in)", fstr1, fstr2);
2830 }
2831 else
2832 sprintf(sptr, " (length %s in)", fstr1);
2833 }
2834 } break;
2835 case CM:
2836 oscale = xobjs.pagelist[areawin->page]->outscale * CMSCALE;
2837 f1 = ((float)(xval) * iscale * oscale) / IN_CM_CONVERT;
2838 f2 = ((float)(yval) * iscale * oscale) / IN_CM_CONVERT;
2839 sprintf(_STR, "%5.3f, %5.3f cm", f1, f2);
2840 sptr = _STR + strlen(_STR);
2841 if (wlflag) {
2842 f1 = ((float)(llen) * iscale * oscale) / IN_CM_CONVERT;
2843 if (wlflag & 2) {
2844 f2 = ((float)(lwid) * iscale * oscale) / IN_CM_CONVERT;
2845 sprintf(sptr, " (%5.3f x %5.3f cm)", f1, f2);
2846 }
2847 else
2848 sprintf(sptr, " (length %5.3f cm)", f1);
2849 }
2850 break;
2851 }
2852 W1printf(_STR);
2853 }
2854
2855 /*---------------------------------------------------*/
2856 /* Find nearest point of intersection of the cursor */
2857 /* position to a wire and move there. */
2858 /*---------------------------------------------------*/
2859
findwirex(XPoint * endpt1,XPoint * endpt2,XPoint * userpt,XPoint * newpos,float * rot)2860 void findwirex(XPoint *endpt1, XPoint *endpt2, XPoint *userpt,
2861 XPoint *newpos, float *rot)
2862 {
2863 long xsq, ysq, zsq;
2864 float frac;
2865
2866 xsq = sqwirelen(endpt1, endpt2);
2867 ysq = sqwirelen(endpt1, userpt);
2868 zsq = sqwirelen(endpt2, userpt);
2869 frac = 0.5 + (float)(ysq - zsq) / (float)(xsq << 1);
2870 if (frac > 1) frac = 1;
2871 else if (frac < 0) frac = 0;
2872 newpos->x = endpt1->x + (int)((endpt2->x - endpt1->x) * frac);
2873 newpos->y = endpt1->y + (int)((endpt2->y - endpt1->y) * frac);
2874
2875 *rot = 180.0 + INVRFAC * atan2((double)(endpt1->x -
2876 endpt2->x), (double)(endpt1->y - endpt2->y));
2877 }
2878
2879 /*----------------------------------------------------------------*/
2880 /* Find the closest point of attachment from the pointer position */
2881 /* to the "attachto" element. */
2882 /*----------------------------------------------------------------*/
2883
findattach(XPoint * newpos,float * rot,XPoint * userpt)2884 void findattach(XPoint *newpos, float *rot, XPoint *userpt)
2885 {
2886 XPoint *endpt1, *endpt2;
2887 float frac;
2888 double tmpang;
2889 float locrot = 0.0;
2890
2891 if (rot) locrot = *rot;
2892
2893 /* find point of intersection and slope */
2894
2895 if (SELECTTYPE(&areawin->attachto) == ARC) {
2896 arcptr aarc = SELTOARC(&areawin->attachto);
2897 float tmpdeg;
2898 tmpang = atan2((double)(userpt->y - aarc->position.y) * (double)
2899 (abs(aarc->radius)), (double)(userpt->x - aarc->position.x) *
2900 (double)aarc->yaxis);
2901
2902 /* don't follow the arc beyond its endpoints */
2903
2904 tmpdeg = (float)(tmpang * INVRFAC);
2905 if (tmpdeg < 0) tmpdeg += 360;
2906 if (((aarc->angle2 > 360) && (tmpdeg > aarc->angle2 - 360) &&
2907 (tmpdeg < aarc->angle1)) ||
2908 ((aarc->angle1 < 0) && (tmpdeg > aarc->angle2) &&
2909 (tmpdeg < aarc->angle1 + 360)) ||
2910 ((aarc->angle1 >= 0) && (aarc->angle2 <= 360) && ((tmpdeg
2911 > aarc->angle2) || (tmpdeg < aarc->angle1)))) {
2912 float testd1 = aarc->angle1 - tmpdeg;
2913 float testd2 = tmpdeg - aarc->angle2;
2914 if (testd1 < 0) testd1 += 360;
2915 if (testd2 < 0) testd2 += 360;
2916
2917 /* if arc is closed, attach to the line between the endpoints */
2918
2919 if (!(aarc->style & UNCLOSED)) {
2920 XPoint end1, end2;
2921 tmpang = (double) aarc->angle1 / INVRFAC;
2922 end1.x = aarc->position.x + abs(aarc->radius) * cos(tmpang);
2923 end1.y = aarc->position.y + aarc->yaxis * sin(tmpang);
2924 tmpang = (double) aarc->angle2 / INVRFAC;
2925 end2.x = aarc->position.x + abs(aarc->radius) * cos(tmpang);
2926 end2.y = aarc->position.y + aarc->yaxis * sin(tmpang);
2927 findwirex(&end1, &end2, userpt, newpos, &locrot);
2928 if (rot) *rot = locrot;
2929 return;
2930 }
2931 else
2932 tmpang = (double)((testd1 < testd2) ? aarc->angle1 : aarc->angle2)
2933 / INVRFAC;
2934 }
2935
2936 /* get position in user coordinates nearest to the intersect pt */
2937
2938 newpos->x = aarc->position.x + abs(aarc->radius) * cos(tmpang);
2939 newpos->y = aarc->position.y + aarc->yaxis * sin(tmpang);
2940
2941 /* rotation of object is normal to the curve of the ellipse */
2942
2943 if (rot) {
2944 *rot = 90.0 - INVRFAC * tmpang;
2945 if (*rot < 0.0) *rot += 360.0;
2946 }
2947 }
2948 else if (SELECTTYPE(&areawin->attachto) == SPLINE) {
2949 splineptr aspline = SELTOSPLINE(&areawin->attachto);
2950 frac = findsplinemin(aspline, userpt);
2951 findsplinepos(aspline, frac, newpos, &locrot);
2952 if (rot) *rot = locrot;
2953 }
2954 else if (SELECTTYPE(&areawin->attachto) == OBJINST) {
2955
2956 objinstptr ainst = SELTOOBJINST(&areawin->attachto);
2957 objectptr aobj = ainst->thisobject;
2958 genericptr *ggen;
2959 long testdist, mindist = 1e8;
2960 XPoint mdpoint;
2961
2962 /* In case instance has no pin labels, we will attach to */
2963 /* the instance's own origin. */
2964
2965 mdpoint.x = mdpoint.y = 0;
2966 ReferencePosition(ainst, &mdpoint, newpos);
2967
2968 /* Find the nearest pin label in the object instance and attach to it */
2969 for (ggen = aobj->plist; ggen < aobj->plist + aobj->parts; ggen++) {
2970 if (ELEMENTTYPE(*ggen) == LABEL) {
2971 labelptr alab = TOLABEL(ggen);
2972 if (alab->pin == LOCAL || alab->pin == GLOBAL) {
2973 ReferencePosition(ainst, &alab->position, &mdpoint);
2974 testdist = sqwirelen(&mdpoint, userpt);
2975 if (testdist < mindist) {
2976 mindist = testdist;
2977 *newpos = mdpoint;
2978 }
2979 }
2980 }
2981 }
2982 }
2983 else if (SELECTTYPE(&areawin->attachto) == LABEL) {
2984 /* Only one choice: Attach to the label position */
2985 labelptr alabel = SELTOLABEL(&areawin->attachto);
2986 newpos->x = alabel->position.x;
2987 newpos->y = alabel->position.y;
2988 }
2989 else if (SELECTTYPE(&areawin->attachto) == POLYGON) {
2990 polyptr apoly = SELTOPOLY(&areawin->attachto);
2991 XPoint *testpt, *minpt, *nxtpt;
2992 long mindist = 1e8, testdist;
2993 minpt = nxtpt = apoly->points; /* so variables aren't uninitialized */
2994 for (testpt = apoly->points; testpt < apoly->points +
2995 apoly->number - 1; testpt++) {
2996 testdist = finddist(testpt, testpt + 1, userpt);
2997 if (testdist < mindist) {
2998 mindist = testdist;
2999 minpt = testpt;
3000 nxtpt = testpt + 1;
3001 }
3002 }
3003 if (!(apoly->style & UNCLOSED)) {
3004 testdist = finddist(testpt, apoly->points, userpt);
3005 if (testdist < mindist) {
3006 mindist = testdist;
3007 minpt = testpt;
3008 nxtpt = apoly->points;
3009 }
3010 }
3011 endpt1 = minpt;
3012 endpt2 = nxtpt;
3013 findwirex(endpt1, endpt2, userpt, newpos, &locrot);
3014 if (rot) *rot = locrot;
3015 }
3016 }
3017
3018 /*--------------------------------------------*/
3019 /* Find closest point in a path to the cursor */
3020 /*--------------------------------------------*/
3021
pathclosepoint(pathptr dragpath,XPoint * newpos)3022 XPoint *pathclosepoint(pathptr dragpath, XPoint *newpos)
3023 {
3024 XPoint *rpoint;
3025 genericptr *cpoint;
3026 short mpoint;
3027 int mdist = 1000000, tdist;
3028
3029 for (cpoint = dragpath->plist; cpoint < dragpath->plist + dragpath->parts;
3030 cpoint++) {
3031 switch(ELEMENTTYPE(*cpoint)) {
3032 case ARC:
3033 tdist = wirelength(&(TOARC(cpoint)->position), newpos);
3034 if (tdist < mdist) {
3035 mdist = tdist;
3036 rpoint = &(TOARC(cpoint)->position);
3037 }
3038 break;
3039 case POLYGON:
3040 mpoint = closepoint(TOPOLY(cpoint), newpos);
3041 tdist = wirelength(TOPOLY(cpoint)->points + mpoint, newpos);
3042 if (tdist < mdist) {
3043 mdist = tdist;
3044 rpoint = TOPOLY(cpoint)->points + mpoint;
3045 }
3046 break;
3047 case SPLINE:
3048 tdist = wirelength(&(TOSPLINE(cpoint)->ctrl[0]), newpos);
3049 if (tdist < mdist) {
3050 mdist = tdist;
3051 rpoint = &(TOSPLINE(cpoint)->ctrl[0]);
3052 }
3053 tdist = wirelength(&(TOSPLINE(cpoint)->ctrl[3]), newpos);
3054 if (tdist < mdist) {
3055 mdist = tdist;
3056 rpoint = &(TOSPLINE(cpoint)->ctrl[3]);
3057 }
3058 break;
3059 }
3060 }
3061 return rpoint;
3062 }
3063
3064 /*-------------------------------------------*/
3065 /* Drag a selected element around the screen */
3066 /*-------------------------------------------*/
3067
drag(int x,int y)3068 void drag(int x, int y)
3069 {
3070 XEvent again;
3071 Boolean eventcheck = False;
3072 XPoint userpt;
3073 short deltax, deltay;
3074 int locx, locy;
3075
3076 locx = x;
3077 locy = y;
3078
3079 /* flush out multiple pointermotion events from the event queue */
3080 /* use only the last motion event */
3081 while (XCheckWindowEvent(dpy, areawin->window, PointerMotionMask |
3082 Button1MotionMask, &again) == True) eventcheck = True;
3083 if (eventcheck) {
3084 XButtonEvent *event = (XButtonEvent *)(&again);
3085 locx = (int)event->x;
3086 locy = (int)event->y;
3087 }
3088
3089 /* Determine if this event is supposed to be handled by */
3090 /* trackselarea(), or whether we should not be here at all */
3091 /* (button press and mouse movement in an unsupported mode) */
3092
3093 if (eventmode == SELAREA_MODE) {
3094 trackselarea();
3095 return;
3096 }
3097 else if (eventmode == RESCALE_MODE) {
3098 trackrescale();
3099 return;
3100 }
3101 else if (eventmode == PAN_MODE) {
3102 trackpan(locx, locy);
3103 return;
3104 }
3105 else if (eventmode != CATMOVE_MODE && eventmode != MOVE_MODE
3106 && eventmode != COPY_MODE)
3107 return;
3108
3109 snap(locx, locy, &userpt);
3110 deltax = userpt.x - areawin->save.x;
3111 deltay = userpt.y - areawin->save.y;
3112 if (deltax == 0 && deltay == 0) return;
3113
3114 areawin->save.x = userpt.x;
3115 areawin->save.y = userpt.y;
3116
3117 /* set up the graphics state for moving a selected object */
3118
3119 XTopSetForeground(SELECTCOLOR);
3120
3121 placeselects(deltax, deltay, &userpt);
3122
3123 /* restore graphics state */
3124
3125 SetForeground(dpy, areawin->gc, areawin->gccolor);
3126
3127 /* print the position and other useful measurements */
3128
3129 printpos(userpt.x, userpt.y);
3130 }
3131
3132 /*------------------------------------------------------*/
3133 /* Wrapper for drag() for xlib callback compatibility. */
3134 /*------------------------------------------------------*/
3135
xlib_drag(xcWidget w,caddr_t clientdata,XEvent * event)3136 void xlib_drag(xcWidget w, caddr_t clientdata, XEvent *event)
3137 {
3138 XButtonEvent *bevent = (XButtonEvent *)event;
3139 UNUSED(w); UNUSED(clientdata);
3140
3141 drag(bevent->x, bevent->y);
3142 if (areawin->redraw_needed)
3143 drawarea(NULL, NULL, NULL);
3144 }
3145
3146 /*----------------------------------------------*/
3147 /* Rotate an element of a path */
3148 /*----------------------------------------------*/
3149
elemrotate(genericptr * genobj,float direction,XPoint * position)3150 void elemrotate(genericptr *genobj, float direction, XPoint *position)
3151 {
3152 XPoint negpt, *newpts = (XPoint *)NULL;
3153
3154 negpt.x = -position->x;
3155 negpt.y = -position->y;
3156
3157 switch(ELEMENTTYPE(*genobj)) {
3158 case(ARC):{
3159 arcptr rotatearc = TOARC(genobj);
3160 rotatearc->angle1 -= direction;
3161 rotatearc->angle2 -= direction;
3162 if (rotatearc->angle1 >= 360) {
3163 rotatearc->angle1 -= 360;
3164 rotatearc->angle2 -= 360;
3165 }
3166 else if (rotatearc->angle2 <= 0) {
3167 rotatearc->angle1 += 360;
3168 rotatearc->angle2 += 360;
3169 }
3170 newpts = (XPoint *)malloc(sizeof(XPoint));
3171 UTransformPoints(&rotatearc->position, newpts, 1, negpt, 1.0, 0);
3172 UTransformPoints(newpts, &rotatearc->position, 1, *position,
3173 1.0, direction);
3174 calcarc(rotatearc);
3175 }break;
3176
3177 case(SPLINE):{
3178 splineptr rotatespline = TOSPLINE(genobj);
3179 newpts = (XPoint *)malloc(4 * sizeof(XPoint));
3180 UTransformPoints(rotatespline->ctrl, newpts, 4, negpt, 1.0, 0);
3181 UTransformPoints(newpts, rotatespline->ctrl, 4, *position,
3182 1.0, direction);
3183 calcspline(rotatespline);
3184 }break;
3185
3186 case(POLYGON):{
3187 polyptr rotatepoly = TOPOLY(genobj);
3188 newpts = (XPoint *)malloc(rotatepoly->number * sizeof(XPoint));
3189 UTransformPoints(rotatepoly->points, newpts, rotatepoly->number,
3190 negpt, 1.0, 0);
3191 UTransformPoints(newpts, rotatepoly->points, rotatepoly->number,
3192 *position, 1.0, direction);
3193 }break;
3194 }
3195 if (newpts) free(newpts);
3196 }
3197
3198 /*------------------------------------------------------*/
3199 /* Rotate an element or group of elements */
3200 /* Objects and labels, if selected singly, rotate */
3201 /* about their position point. All other elements, */
3202 /* and groups, rotate about the cursor point. */
3203 /*------------------------------------------------------*/
3204
elementrotate(float direction,XPoint * position)3205 void elementrotate(float direction, XPoint *position)
3206 {
3207 short *selectobj; /* , ld; (jdk) */
3208 Boolean single = False;
3209 Boolean need_refresh = False;
3210 Boolean preselected;
3211 XPoint newpt, negpt;
3212
3213 preselected = (areawin->selects > 0) ? TRUE : FALSE;
3214 if (!checkselect(ALL_TYPES)) return;
3215 if (areawin->selects == 1) single = True;
3216
3217 negpt.x = -position->x;
3218 negpt.y = -position->y;
3219
3220 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
3221 + areawin->selects; selectobj++) {
3222
3223 /* erase the element */
3224 if (!need_refresh) {
3225 SetForeground(dpy, areawin->gc, BACKGROUND);
3226 easydraw(*selectobj, DOFORALL);
3227 }
3228
3229 switch(SELECTTYPE(selectobj)) {
3230
3231 case(OBJINST):{
3232 objinstptr rotateobj = SELTOOBJINST(selectobj);
3233
3234 if (is_library(topobject) >= 0 && !is_virtual(rotateobj)) break;
3235 rotateobj->rotation += direction;
3236 while (rotateobj->rotation >= 360) rotateobj->rotation -= 360;
3237 while (rotateobj->rotation <= 0) rotateobj->rotation += 360;
3238 if (!single) {
3239 UTransformPoints(&rotateobj->position, &newpt, 1, negpt, 1.0, 0);
3240 UTransformPoints(&newpt, &rotateobj->position, 1, *position,
3241 1.0, direction);
3242 }
3243 }break;
3244
3245 case(LABEL):{
3246 labelptr rotatetext = SELTOLABEL(selectobj);
3247
3248 rotatetext->rotation += direction;
3249 while (rotatetext->rotation >= 360) rotatetext->rotation -= 360;
3250 while (rotatetext->rotation <= 0) rotatetext->rotation += 360;
3251 if (!single) {
3252 UTransformPoints(&rotatetext->position, &newpt, 1, negpt, 1.0, 0);
3253 UTransformPoints(&newpt, &rotatetext->position, 1, *position,
3254 1.0, direction);
3255 }
3256 }break;
3257
3258 case(GRAPHIC):{
3259 graphicptr rotateg = SELTOGRAPHIC(selectobj);
3260
3261 rotateg->rotation += direction;
3262 while (rotateg->rotation >= 360) rotateg->rotation -= 360;
3263 while (rotateg->rotation <= 0) rotateg->rotation += 360;
3264 #ifndef HAVE_CAIRO
3265 rotateg->valid = FALSE;
3266 #endif /* !HAVE_CAIRO */
3267 if (!single) {
3268 UTransformPoints(&rotateg->position, &newpt, 1, negpt, 1.0, 0);
3269 UTransformPoints(&newpt, &rotateg->position, 1, *position,
3270 1.0, direction);
3271 }
3272 need_refresh = True;
3273 }break;
3274
3275 case POLYGON: case ARC: case SPLINE:{
3276 genericptr *genpart = topobject->plist + *selectobj;
3277 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3278 *genpart);
3279 elemrotate(genpart, direction, position);
3280 }break;
3281
3282 case PATH:{
3283 genericptr *genpart;
3284 pathptr rotatepath = SELTOPATH(selectobj);
3285
3286 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3287 rotatepath);
3288 for (genpart = rotatepath->plist; genpart < rotatepath->plist
3289 + rotatepath->parts; genpart++)
3290 elemrotate(genpart, direction, position);
3291 }break;
3292 }
3293
3294 /* redisplay the element */
3295 if (preselected || ((eventmode != NORMAL_MODE) && !need_refresh)) {
3296 SetForeground(dpy, areawin->gc, SELECTCOLOR);
3297 easydraw(*selectobj, DOFORALL);
3298 }
3299 }
3300
3301 /* This takes care of all selected instances and labels in one go, */
3302 /* because we only need to know the origin and amount of rotation. */
3303
3304 if (eventmode != COPY_MODE)
3305 register_for_undo(XCF_Rotate, UNDO_MORE, areawin->topinstance,
3306 (eventmode == MOVE_MODE) ? &areawin->origin : position,
3307 (double)direction);
3308
3309 /* New rule (6/15/07) to be applied generally: If objects were */
3310 /* selected prior to calling elementrotate() and similar functions, */
3311 /* leave them selected upon exit. Otherwise, deselect them. */
3312
3313 if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE)
3314 if (!preselected)
3315 unselect_all();
3316
3317 if (eventmode == CATALOG_MODE) {
3318 int libnum;
3319 if ((libnum = is_library(topobject)) >= 0) {
3320 composelib(libnum + LIBRARY);
3321 need_refresh = TRUE;
3322 }
3323 }
3324 else {
3325 pwriteback(areawin->topinstance);
3326 calcbbox(areawin->topinstance);
3327 }
3328
3329 if (need_refresh) drawarea(NULL, NULL, NULL);
3330 }
3331
3332 /*----------------------------------------------*/
3333 /* Rescale the current edit element to the */
3334 /* dimensions of the rescale box. */
3335 /*----------------------------------------------*/
3336
elementrescale(float newscale)3337 void elementrescale(float newscale)
3338 {
3339 short *selectobj;
3340 labelptr sclab;
3341 objinstptr scinst;
3342 graphicptr scgraph;
3343 float oldsize;
3344
3345 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
3346 + areawin->selects; selectobj++) {
3347 switch (SELECTTYPE(selectobj)) {
3348 case LABEL:
3349 sclab = SELTOLABEL(selectobj);
3350 oldsize = sclab->scale;
3351 sclab->scale = newscale;
3352 break;
3353 case OBJINST:
3354 scinst = SELTOOBJINST(selectobj);
3355 oldsize = scinst->scale;
3356 scinst->scale = newscale;
3357 break;
3358 case GRAPHIC:
3359 scgraph = SELTOGRAPHIC(selectobj);
3360 oldsize = scgraph->scale;
3361 scgraph->scale = newscale;
3362 break;
3363 }
3364 register_for_undo(XCF_Rescale, UNDO_MORE, areawin->topinstance,
3365 SELTOGENERIC(selectobj), (double)oldsize);
3366 }
3367 calcbbox(areawin->topinstance);
3368 }
3369
3370 /*-------------------------------------------------*/
3371 /* Edit an element in an element-dependent fashion */
3372 /*-------------------------------------------------*/
3373
edit(int x,int y)3374 void edit(int x, int y)
3375 {
3376 short *selectobj;
3377
3378 if (areawin->selects == 0) {
3379 Boolean saveredraw = areawin->redraw_needed;
3380 selectobj = select_element(ALL_TYPES);
3381 areawin->redraw_needed = saveredraw;
3382 }
3383 else
3384 selectobj = areawin->selectlist;
3385 if (areawin->selects == 0)
3386 return;
3387 else if (areawin->selects != 1) { /* Multiple object edit */
3388 int selnum;
3389 short *selectlist, selrefno;
3390 Boolean save_redraw = areawin->redraw_needed;
3391
3392 /* Find the closest part to use as a reference */
3393 selnum = areawin->selects;
3394 selectlist = areawin->selectlist;
3395 areawin->selects = 0;
3396 areawin->selectlist = NULL;
3397 selectobj = select_element(ALL_TYPES);
3398 if (selectobj != NULL)
3399 selrefno = *selectobj;
3400 else
3401 selrefno = -1;
3402 freeselects();
3403 areawin->selects = selnum;
3404 areawin->selectlist = selectlist;
3405 areawin->redraw_needed = save_redraw;
3406 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
3407 + areawin->selects; selectobj++) {
3408 if (*selectobj == selrefno) break;
3409 }
3410 if (selectobj == areawin->selectlist + areawin->selects) {
3411 Wprintf("Put cursor close to the reference element.");
3412 return;
3413 }
3414
3415 /* Shuffle the reference element to the beginning of the select list */
3416 *selectobj = *(areawin->selectlist);
3417 *(areawin->selectlist) = selrefno;
3418 selectobj = areawin->selectlist;
3419 }
3420
3421 switch(SELECTTYPE(selectobj)) {
3422 case LABEL: {
3423 labelptr *lastlabel = (labelptr *)EDITPART;
3424 short curfont;
3425 XPoint tmppt;
3426 TextExtents tmpext;
3427
3428 /* save the old string, including parameters */
3429 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3430 *lastlabel);
3431
3432 /* fill any NULL instance parameters with the default value */
3433 copyparams(areawin->topinstance, areawin->topinstance);
3434
3435 /* place text cursor line at point nearest the cursor */
3436 /* unless textend is set. . . */
3437
3438 if (areawin->textend == 0) {
3439 TextLinesInfo tlinfo;
3440 tlinfo.dostop = 0;
3441 tlinfo.tbreak = NULL;
3442 tlinfo.padding = NULL;
3443
3444 window_to_user(x, y, &areawin->save);
3445 InvTransformPoints(&areawin->save, &tmppt, 1, (*lastlabel)->position,
3446 (*lastlabel)->scale, (*lastlabel)->rotation);
3447 tmpext = ULength(*lastlabel, areawin->topinstance, &tlinfo);
3448 tmppt.x += ((*lastlabel)->anchor & NOTLEFT ?
3449 ((*lastlabel)->anchor & RIGHT ? tmpext.maxwidth
3450 : tmpext.maxwidth >> 1) : 0);
3451 tmppt.y += ((*lastlabel)->anchor & NOTBOTTOM ?
3452 ((*lastlabel)->anchor & TOP ? tmpext.ascent :
3453 (tmpext.ascent + tmpext.base) >> 1) : tmpext.base);
3454 if ((*lastlabel)->pin)
3455 pinadjust((*lastlabel)->anchor, &tmppt.x, NULL, -1);
3456
3457 /* Where tbreak is passed to ULength, the character position */
3458 /* is returned in the TextLinesInfo dostop field. */
3459 tlinfo.tbreak = &tmppt;
3460 tmpext = ULength(*lastlabel, areawin->topinstance, &tlinfo);
3461 areawin->textpos = tlinfo.dostop;
3462
3463 if (tlinfo.padding != NULL) free(tlinfo.padding);
3464 }
3465
3466 /* find current font */
3467
3468 curfont = findcurfont(areawin->textpos, (*lastlabel)->string,
3469 areawin->topinstance);
3470
3471 /* change menu buttons accordingly */
3472
3473 setfontmarks(curfont, (*lastlabel)->anchor);
3474
3475 if (eventmode == CATALOG_MODE) {
3476 /* CATTEXT_MODE may show an otherwise hidden library namespace */
3477 undrawtext(*lastlabel);
3478 eventmode = CATTEXT_MODE;
3479 redrawtext(*lastlabel);
3480 areawin->redraw_needed = False; /* ignore prev. redraw requests */
3481 text_mode_draw(xcDRAW_INIT, *lastlabel);
3482 }
3483 else {
3484 eventmode = ETEXT_MODE;
3485 text_mode_draw(xcDRAW_INIT, *lastlabel);
3486 }
3487
3488 XDefineCursor(dpy, areawin->window, TEXTPTR);
3489
3490 /* write the text at the bottom */
3491
3492 charreport(*lastlabel);
3493
3494 } break;
3495
3496 case POLYGON: case ARC: case SPLINE: case PATH:
3497 window_to_user(x, y, &areawin->save);
3498 pathedit(*(EDITPART));
3499 break;
3500
3501 case OBJINST: case GRAPHIC:
3502 if (areawin->selects == 1)
3503 unselect_all();
3504 return;
3505 }
3506 XDefineCursor (dpy, areawin->window, EDCURSOR);
3507 }
3508
3509 /*----------------------------------------------------------------------*/
3510 /* edit() routine for path-type elements (polygons, splines, arcs, and */
3511 /* paths) */
3512 /*----------------------------------------------------------------------*/
3513
pathedit(genericptr editpart)3514 void pathedit(genericptr editpart)
3515 {
3516 splineptr lastspline = NULL;
3517 pathptr lastpath;
3518 polyptr lastpoly = NULL;
3519 arcptr lastarc;
3520 XPoint *savept;
3521 short *eselect;
3522 int cycle;
3523 Boolean havecycle;
3524
3525 /* Find and set constrained edit points on all elements. Register */
3526 /* each element with the undo mechanism. */
3527
3528 for (eselect = areawin->selectlist; eselect < areawin->selectlist +
3529 areawin->selects; eselect++) {
3530 switch (SELECTTYPE(eselect)) {
3531 case POLYGON:
3532 findconstrained(SELTOPOLY(eselect));
3533 /* fall through */
3534 default:
3535 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3536 SELTOGENERIC(eselect));
3537 break;
3538 }
3539 }
3540
3541 switch(ELEMENTTYPE(editpart)) {
3542 case PATH: {
3543 genericptr *ggen, *savegen = NULL;
3544 int mincycle, dist, mindist = 1e6;
3545
3546 lastpath = (pathptr)editpart;
3547 havecycle = (checkcycle(editpart, 0) >= 0) ? True : False;
3548
3549 /* determine which point of the path is closest to the cursor */
3550 for (ggen = lastpath->plist; ggen < lastpath->plist + lastpath->parts;
3551 ggen++) {
3552 switch (ELEMENTTYPE(*ggen)) {
3553 case POLYGON:
3554 lastpoly = TOPOLY(ggen);
3555 cycle = closepoint(lastpoly, &areawin->save);
3556 dist = wirelength(lastpoly->points + cycle, &areawin->save);
3557 break;
3558 case SPLINE:
3559 lastspline = TOSPLINE(ggen);
3560 cycle = (wirelength(&lastspline->ctrl[0],
3561 &areawin->save) < wirelength(&lastspline->ctrl[3],
3562 &areawin->save)) ? 0 : 3;
3563 dist = wirelength(&lastspline->ctrl[cycle], &areawin->save);
3564 break;
3565 }
3566 if (dist < mindist) {
3567 mindist = dist;
3568 mincycle = cycle;
3569 savegen = ggen;
3570 }
3571 }
3572 if (savegen == NULL) return; /* something went terribly wrong */
3573 switch (ELEMENTTYPE(*savegen)) {
3574 case POLYGON:
3575 lastpoly = TOPOLY(savegen);
3576 addcycle(savegen, mincycle, 0);
3577 savept = lastpoly->points + mincycle;
3578 makerefcycle(lastpoly->cycle, mincycle);
3579 findconstrained(lastpoly);
3580 break;
3581 case SPLINE:
3582 lastspline = TOSPLINE(savegen);
3583 addcycle(savegen, mincycle, 0);
3584 savept = &lastspline->ctrl[mincycle];
3585 makerefcycle(lastspline->cycle, mincycle);
3586 break;
3587 }
3588 updatepath(lastpath);
3589 if (!havecycle)
3590 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3591 (genericptr)lastpath);
3592 patheditpush(lastpath);
3593 areawin->origin = areawin->save;
3594 checkwarp(savept);
3595
3596 path_mode_draw(xcDRAW_INIT, lastpath);
3597
3598 xcAddEventHandler(areawin->area, PointerMotionMask, False,
3599 (xcEventHandler)trackelement, NULL);
3600 eventmode = EPATH_MODE;
3601 printpos(savept->x, savept->y);
3602
3603 } break;
3604 case POLYGON: {
3605
3606 lastpoly = (polyptr)editpart;
3607
3608 /* Determine which point of polygon is closest to cursor */
3609 cycle = closepoint(lastpoly, &areawin->save);
3610 havecycle = (lastpoly->cycle == NULL) ? False : True;
3611 addcycle(&editpart, cycle, 0);
3612 savept = lastpoly->points + cycle;
3613 makerefcycle(lastpoly->cycle, cycle);
3614 if (!havecycle) {
3615 findconstrained(lastpoly);
3616 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3617 (genericptr)lastpoly);
3618 }
3619
3620 /* Push onto the editstack */
3621 polyeditpush(lastpoly);
3622
3623 /* remember our postion for pointer restore */
3624 areawin->origin = areawin->save;
3625
3626 checkwarp(savept);
3627
3628 poly_mode_draw(xcDRAW_INIT, lastpoly);
3629
3630 xcAddEventHandler(areawin->area, PointerMotionMask, False,
3631 (xcEventHandler)trackelement, NULL);
3632 eventmode = EPOLY_MODE;
3633 printeditbindings();
3634 printpos(savept->x, savept->y);
3635 } break;
3636 case SPLINE: {
3637 XPoint *curpt;
3638
3639 lastspline = (splineptr)editpart;
3640
3641 /* find which point is closest to the cursor */
3642
3643 cycle = (wirelength(&lastspline->ctrl[0],
3644 &areawin->save) < wirelength(&lastspline->ctrl[3],
3645 &areawin->save)) ? 0 : 3;
3646 havecycle = (lastspline->cycle == NULL) ? False : True;
3647 addcycle(&editpart, cycle, 0);
3648 makerefcycle(lastspline->cycle, cycle);
3649 curpt = &lastspline->ctrl[cycle];
3650 if (!havecycle)
3651 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3652 (genericptr)lastspline);
3653
3654 /* Push onto the editstack */
3655 splineeditpush(lastspline);
3656
3657 /* remember our postion for pointer restore */
3658 areawin->origin = areawin->save;
3659
3660 checkwarp(curpt);
3661
3662 spline_mode_draw(xcDRAW_INIT, lastspline);
3663 xcAddEventHandler(areawin->area, PointerMotionMask, False,
3664 (xcEventHandler)trackelement, NULL);
3665 eventmode = ESPLINE_MODE;
3666 } break;
3667 case ARC: {
3668 XPoint curpt;
3669 float tmpratio, tlen;
3670
3671 lastarc = (arcptr)editpart;
3672
3673 /* find a part of the arc close to the pointer */
3674
3675 tlen = (float)wirelength(&areawin->save, &(lastarc->position));
3676 tmpratio = (float)(abs(lastarc->radius)) / tlen;
3677 curpt.x = lastarc->position.x + tmpratio * (areawin->save.x
3678 - lastarc->position.x);
3679 tmpratio = (float)lastarc->yaxis / tlen;
3680 curpt.y = lastarc->position.y + tmpratio * (areawin->save.y
3681 - lastarc->position.y);
3682 addcycle(&editpart, 0, 0);
3683 saveratio = (double)(lastarc->yaxis) / (double)(abs(lastarc->radius));
3684
3685 /* Push onto the editstack */
3686 arceditpush(lastarc);
3687 areawin->origin = areawin->save;
3688
3689 checkwarp(&curpt);
3690
3691 areawin->save.x = curpt.x; /* for redrawing dotted edit line */
3692 areawin->save.y = curpt.y;
3693 arc_mode_draw(xcDRAW_INIT, lastarc);
3694 xcAddEventHandler(areawin->area, PointerMotionMask, False,
3695 (xcEventHandler)trackarc, NULL);
3696 eventmode = EARC_MODE;
3697 printpos(curpt.x, curpt.y);
3698 } break;
3699 }
3700 }
3701
3702 /*------------------------------------------------------*/
3703 /* Raise an element to the top of the list */
3704 /*------------------------------------------------------*/
3705
xc_top(short * selectno,short * orderlist)3706 void xc_top(short *selectno, short *orderlist)
3707 {
3708 short i;
3709 genericptr *raiseobj, *genobj, temp;
3710
3711 raiseobj = topobject->plist + *selectno;
3712 temp = *raiseobj;
3713 i = *selectno;
3714 for (genobj = topobject->plist + *selectno; genobj <
3715 topobject->plist + topobject->parts - 1; genobj++) {
3716 *genobj = *(genobj + 1);
3717 *(orderlist + i) = *(orderlist + i + 1);
3718 i++;
3719 }
3720 *(topobject->plist + topobject->parts - 1) = temp;
3721 *(orderlist + topobject->parts - 1) = *selectno;
3722 *selectno = topobject->parts - 1;
3723 }
3724
3725 /*------------------------------------------------------*/
3726 /* Lower an element to the bottom of the list */
3727 /*------------------------------------------------------*/
3728
xc_bottom(short * selectno,short * orderlist)3729 void xc_bottom(short *selectno, short *orderlist)
3730 {
3731 short i;
3732 genericptr *lowerobj, *genobj, temp;
3733
3734 lowerobj = topobject->plist + *selectno;
3735 temp = *lowerobj;
3736 i = *selectno;
3737 for (genobj = topobject->plist + *selectno;
3738 genobj > topobject->plist; genobj--) {
3739 *genobj = *(genobj - 1);
3740 *(orderlist + i) = *(orderlist + i - 1);
3741 i--;
3742 }
3743 *genobj = temp;
3744 *orderlist = *selectno;
3745 *selectno = 0;
3746 }
3747
3748 /*--------------------------------------------------------------*/
3749 /* Raise all selected elements by one position in the list */
3750 /*--------------------------------------------------------------*/
3751
xc_raise()3752 void xc_raise()
3753 {
3754 short *sel, topsel, maxsel, *topidx, limit, *orderlist, i;
3755 genericptr *raiseobj, temp;
3756
3757 orderlist = (short *)malloc(topobject->parts * sizeof(short));
3758 for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i;
3759
3760 /* Find topmost element in the select list */
3761 maxsel = -1;
3762 for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects;
3763 sel++) {
3764 if (*sel > maxsel) {
3765 maxsel = *sel;
3766 topidx = sel;
3767 }
3768 }
3769 if (maxsel == -1) return; /* Error condition */
3770
3771 topsel = maxsel;
3772 limit = topobject->parts - 1;
3773 while (1) {
3774
3775 /* Exchange the topmost element with the one above it */
3776 if (topsel < limit) {
3777 raiseobj = topobject->plist + topsel;
3778 temp = *raiseobj;
3779 *raiseobj = *(raiseobj + 1);
3780 *(raiseobj + 1) = temp;
3781 (*topidx)++;
3782 i = *(orderlist + topsel);
3783 *(orderlist + topsel) = *(orderlist + topsel + 1);
3784 *(orderlist + topsel + 1) = i;
3785 }
3786 else
3787 limit = topsel - 1;
3788
3789 /* Find next topmost element */
3790 topsel = -1;
3791 for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects;
3792 sel++) {
3793 if (*sel < maxsel) {
3794 if (*sel > topsel) {
3795 topsel = *sel;
3796 topidx = sel;
3797 }
3798 }
3799 }
3800 if (topsel == -1) break; /* No more elements to raise */
3801 maxsel = topsel;
3802 }
3803 register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance, orderlist,
3804 topobject->parts);
3805 }
3806
3807 /*--------------------------------------------------------------*/
3808 /* Lower all selected elements by one position in the list */
3809 /*--------------------------------------------------------------*/
3810
xc_lower()3811 void xc_lower()
3812 {
3813 short *sel, botsel, minsel, *botidx, limit, *orderlist, i;
3814 genericptr *lowerobj, temp;
3815
3816 orderlist = (short *)malloc(topobject->parts * sizeof(short));
3817 for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i;
3818
3819 /* Find bottommost element in the select list */
3820 minsel = topobject->parts;
3821 for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects;
3822 sel++) {
3823 if (*sel < minsel) {
3824 minsel = *sel;
3825 botidx = sel;
3826 }
3827 }
3828 if (minsel == topobject->parts) return; /* Error condition */
3829
3830 botsel = minsel;
3831 limit = 0;
3832 while (1) {
3833
3834 /* Exchange the topmost element with the one below it */
3835 if (botsel > limit) {
3836 lowerobj = topobject->plist + botsel;
3837 temp = *lowerobj;
3838 *lowerobj = *(lowerobj - 1);
3839 *(lowerobj - 1) = temp;
3840 (*botidx)--;
3841 i = *(orderlist + botsel);
3842 *(orderlist + botsel) = *(orderlist + botsel - 1);
3843 *(orderlist + botsel - 1) = i;
3844 }
3845 else
3846 limit = botsel + 1;
3847
3848 /* Find next topmost element */
3849 botsel = topobject->parts;
3850 for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects;
3851 sel++) {
3852 if (*sel > minsel) {
3853 if (*sel < botsel) {
3854 botsel = *sel;
3855 botidx = sel;
3856 }
3857 }
3858 }
3859 if (botsel == topobject->parts) break; /* No more elements to raise */
3860 minsel = botsel;
3861 }
3862 register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance, orderlist,
3863 topobject->parts);
3864 }
3865
3866 /*------------------------------------------------------*/
3867 /* Generate a virtual copy of an object instance in the */
3868 /* user library. This is like the library virtual copy */
3869 /* except that it allows the user to generate a library */
3870 /* copy of an existing instance, without having to make */
3871 /* a copy of the master library instance and edit it. */
3872 /* copyvirtual() also allows the library virtual */
3873 /* instance to take on a specific rotation or flip */
3874 /* value, which cannot be done with the library virtual */
3875 /* copy function. */
3876 /*------------------------------------------------------*/
3877
copyvirtual()3878 void copyvirtual()
3879 {
3880 short *selectno, created = 0;
3881 objinstptr vcpobj, libinst;
3882
3883 for (selectno = areawin->selectlist; selectno < areawin->selectlist +
3884 areawin->selects; selectno++) {
3885 if (SELECTTYPE(selectno) == OBJINST) {
3886 vcpobj = SELTOOBJINST(selectno);
3887 libinst = addtoinstlist(USERLIB - LIBRARY, vcpobj->thisobject, TRUE);
3888 instcopy(libinst, vcpobj);
3889 created++;
3890 }
3891 }
3892 if (created == 0) {
3893 Wprintf("No object instances selected for virtual copy!");
3894 }
3895 else {
3896 unselect_all();
3897 composelib(USERLIB);
3898 }
3899 }
3900
3901 /*------------------------------------------------------*/
3902 /* Exchange the list position (drawing order) of two */
3903 /* elements, or move the position of one element to the */
3904 /* top or bottom. */
3905 /*------------------------------------------------------*/
3906
exchange()3907 void exchange()
3908 {
3909 short *selectno, *orderlist, i;
3910 genericptr *exchobj, *exchobj2, temp;
3911 Boolean preselected;
3912
3913 preselected = (areawin->selects > 0) ? TRUE : FALSE;
3914 if (!checkselect(ALL_TYPES)) {
3915 Wprintf("Select 1 or 2 objects");
3916 return;
3917 }
3918
3919 selectno = areawin->selectlist;
3920 orderlist = (short *)malloc(topobject->parts * sizeof(short));
3921 for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i;
3922
3923 if (areawin->selects == 1) { /* lower if on top; raise otherwise */
3924 if (*selectno == topobject->parts - 1)
3925 xc_bottom(selectno, orderlist);
3926 else
3927 xc_top(selectno, orderlist);
3928 }
3929 else { /* exchange the two objects */
3930 exchobj = topobject->plist + *selectno;
3931 exchobj2 = topobject->plist + *(selectno + 1);
3932
3933 temp = *exchobj;
3934 *exchobj = *exchobj2;
3935 *exchobj2 = temp;
3936
3937 i = *(orderlist + *selectno);
3938 *(orderlist + *selectno) = *(orderlist + *(selectno + 1));
3939 *(orderlist + *(selectno + 1)) = i;
3940 }
3941 register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance,
3942 orderlist, topobject->parts);
3943
3944 incr_changes(topobject);
3945 if (!preselected)
3946 clearselects();
3947 drawarea(NULL, NULL, NULL);
3948 }
3949
3950 /*--------------------------------------------------------*/
3951 /* Flip an element horizontally (POLYGON, ARC, or SPLINE) */
3952 /*--------------------------------------------------------*/
3953
elhflip(genericptr * genobj,short x)3954 void elhflip(genericptr *genobj, short x)
3955 {
3956 switch(ELEMENTTYPE(*genobj)) {
3957 case POLYGON:{
3958 polyptr flippoly = TOPOLY(genobj);
3959 pointlist ppoint;
3960 for (ppoint = flippoly->points; ppoint < flippoly->points +
3961 flippoly->number; ppoint++)
3962 ppoint->x = (x << 1) - ppoint->x;
3963 }break;
3964
3965 case ARC:{
3966 arcptr fliparc = TOARC(genobj);
3967 float tmpang = 180 - fliparc->angle1;
3968 fliparc->angle1 = 180 - fliparc->angle2;
3969 fliparc->angle2 = tmpang;
3970 if (fliparc->angle2 < 0) {
3971 fliparc->angle1 += 360;
3972 fliparc->angle2 += 360;
3973 }
3974 fliparc->radius = -fliparc->radius;
3975 fliparc->position.x = (x << 1) - fliparc->position.x;
3976 calcarc(fliparc);
3977 }break;
3978
3979 case SPLINE:{
3980 splineptr flipspline = TOSPLINE(genobj);
3981 int i;
3982 for (i = 0; i < 4; i++)
3983 flipspline->ctrl[i].x = (x << 1) - flipspline->ctrl[i].x;
3984 calcspline(flipspline);
3985 }break;
3986 }
3987 }
3988
3989 /*--------------------------------------------------------*/
3990 /* Flip an element vertically (POLYGON, ARC, or SPLINE) */
3991 /*--------------------------------------------------------*/
3992
elvflip(genericptr * genobj,short y)3993 void elvflip(genericptr *genobj, short y)
3994 {
3995 switch(ELEMENTTYPE(*genobj)) {
3996
3997 case POLYGON:{
3998 polyptr flippoly = TOPOLY(genobj);
3999 pointlist ppoint;
4000
4001 for (ppoint = flippoly->points; ppoint < flippoly->points +
4002 flippoly->number; ppoint++)
4003 ppoint->y = (y << 1) - ppoint->y;
4004 }break;
4005
4006 case ARC:{
4007 arcptr fliparc = TOARC(genobj);
4008 float tmpang = 360 - fliparc->angle1;
4009 fliparc->angle1 = 360 - fliparc->angle2;
4010 fliparc->angle2 = tmpang;
4011 if (fliparc->angle1 >= 360) {
4012 fliparc->angle1 -= 360;
4013 fliparc->angle2 -= 360;
4014 }
4015 fliparc->radius = -fliparc->radius;
4016 fliparc->position.y = (y << 1) - fliparc->position.y;
4017 calcarc(fliparc);
4018 }break;
4019
4020 case SPLINE:{
4021 splineptr flipspline = TOSPLINE(genobj);
4022 int i;
4023 for (i = 0; i < 4; i++)
4024 flipspline->ctrl[i].y = (y << 1) - flipspline->ctrl[i].y;
4025 calcspline(flipspline);
4026 }break;
4027 }
4028 }
4029
4030 /*------------------------------------------------------*/
4031 /* Horizontally flip an element */
4032 /*------------------------------------------------------*/
4033
elementflip(XPoint * position)4034 void elementflip(XPoint *position)
4035 {
4036 short *selectobj;
4037 Boolean single = False;
4038 Boolean preselected;
4039
4040 preselected = (areawin->selects > 0) ? TRUE : FALSE;
4041 if (!checkselect(ALL_TYPES)) return;
4042 if (areawin->selects == 1) single = True;
4043
4044 if (eventmode != COPY_MODE)
4045 register_for_undo(XCF_Flip_X, UNDO_MORE, areawin->topinstance,
4046 (eventmode == MOVE_MODE) ? &areawin->origin : position);
4047
4048 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
4049 + areawin->selects; selectobj++) {
4050
4051 /* erase the object */
4052 SetForeground(dpy, areawin->gc, BACKGROUND);
4053 easydraw(*selectobj, DOFORALL);
4054
4055 switch(SELECTTYPE(selectobj)) {
4056 case LABEL:{
4057 labelptr fliplab = SELTOLABEL(selectobj);
4058 if ((fliplab->anchor & (RIGHT | NOTLEFT)) != NOTLEFT)
4059 fliplab->anchor ^= (RIGHT | NOTLEFT);
4060 if (!single)
4061 fliplab->position.x = (position->x << 1) - fliplab->position.x;
4062 }break;
4063 case GRAPHIC:{
4064 graphicptr flipg = SELTOGRAPHIC(selectobj);
4065 flipg->scale = -flipg->scale;
4066 #ifndef HAVE_CAIRO
4067 flipg->valid = FALSE;
4068 #endif /* !HAVE_CAIRO */
4069 if (!single)
4070 flipg->position.x = (position->x << 1) - flipg->position.x;
4071 }break;
4072 case OBJINST:{
4073 objinstptr flipobj = SELTOOBJINST(selectobj);
4074 if (is_library(topobject) >= 0 && !is_virtual(flipobj)) break;
4075 flipobj->scale = -flipobj->scale;
4076 if (!single)
4077 flipobj->position.x = (position->x << 1) - flipobj->position.x;
4078 }break;
4079 case POLYGON: case ARC: case SPLINE:
4080 elhflip(topobject->plist + *selectobj, position->x);
4081 break;
4082 case PATH:{
4083 genericptr *genpart;
4084 pathptr flippath = SELTOPATH(selectobj);
4085
4086 for (genpart = flippath->plist; genpart < flippath->plist
4087 + flippath->parts; genpart++)
4088 elhflip(genpart, position->x);
4089 }break;
4090 }
4091
4092 if (preselected || (eventmode != NORMAL_MODE)) {
4093 SetForeground(dpy, areawin->gc, SELECTCOLOR);
4094 easydraw(*selectobj, DOFORALL);
4095 }
4096 }
4097 select_invalidate_netlist();
4098 if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE)
4099 if (!preselected)
4100 unselect_all();
4101
4102 if (eventmode == NORMAL_MODE)
4103 incr_changes(topobject);
4104 if (eventmode == CATALOG_MODE) {
4105 int libnum;
4106 if ((libnum = is_library(topobject)) >= 0) {
4107 composelib(libnum + LIBRARY);
4108 drawarea(NULL, NULL, NULL);
4109 }
4110 }
4111 else {
4112 pwriteback(areawin->topinstance);
4113 calcbbox(areawin->topinstance);
4114 }
4115 }
4116
4117 /*----------------------------------------------*/
4118 /* Vertically flip an element */
4119 /*----------------------------------------------*/
4120
elementvflip(XPoint * position)4121 void elementvflip(XPoint *position)
4122 {
4123 short *selectobj;
4124 /*short fsign; (jdk) */
4125 Boolean preselected;
4126 Boolean single = False;
4127
4128 preselected = (areawin->selects > 0) ? TRUE : FALSE;
4129 if (!checkselect(ALL_TYPES)) return;
4130 if (areawin->selects == 1) single = True;
4131
4132 if (eventmode != COPY_MODE)
4133 register_for_undo(XCF_Flip_Y, UNDO_MORE, areawin->topinstance,
4134 (eventmode == MOVE_MODE) ? &areawin->origin : position);
4135
4136 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
4137 + areawin->selects; selectobj++) {
4138
4139 /* erase the object */
4140 SetForeground(dpy, areawin->gc, BACKGROUND);
4141 easydraw(*selectobj, DOFORALL);
4142
4143 switch(SELECTTYPE(selectobj)) {
4144 case(LABEL):{
4145 labelptr fliplab = SELTOLABEL(selectobj);
4146 if ((fliplab->anchor & (TOP | NOTBOTTOM)) != NOTBOTTOM)
4147 fliplab->anchor ^= (TOP | NOTBOTTOM);
4148 if (!single)
4149 fliplab->position.y = (position->y << 1) - fliplab->position.y;
4150 } break;
4151 case(OBJINST):{
4152 objinstptr flipobj = SELTOOBJINST(selectobj);
4153
4154 if (is_library(topobject) >= 0 && !is_virtual(flipobj)) break;
4155 flipobj->scale = -(flipobj->scale);
4156 flipobj->rotation += 180;
4157 while (flipobj->rotation >= 360) flipobj->rotation -= 360;
4158 if (!single)
4159 flipobj->position.y = (position->y << 1) - flipobj->position.y;
4160 }break;
4161 case(GRAPHIC):{
4162 graphicptr flipg = SELTOGRAPHIC(selectobj);
4163
4164 flipg->scale = -(flipg->scale);
4165 flipg->rotation += 180;
4166 while (flipg->rotation >= 360) flipg->rotation -= 360;
4167 if (!single)
4168 flipg->position.y = (position->y << 1) - flipg->position.y;
4169 }break;
4170 case POLYGON: case ARC: case SPLINE:
4171 elvflip(topobject->plist + *selectobj, position->y);
4172 break;
4173 case PATH:{
4174 genericptr *genpart;
4175 pathptr flippath = SELTOPATH(selectobj);
4176
4177 for (genpart = flippath->plist; genpart < flippath->plist
4178 + flippath->parts; genpart++)
4179 elvflip(genpart, position->y);
4180 }break;
4181 }
4182 if (preselected || (eventmode != NORMAL_MODE)) {
4183 SetForeground(dpy, areawin->gc, SELECTCOLOR);
4184 easydraw(*selectobj, DOFORALL);
4185 }
4186 }
4187 select_invalidate_netlist();
4188 if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE)
4189 if (!preselected)
4190 unselect_all();
4191 if (eventmode == NORMAL_MODE) {
4192 incr_changes(topobject);
4193 }
4194 if (eventmode == CATALOG_MODE) {
4195 int libnum;
4196 if ((libnum = is_library(topobject)) >= 0) {
4197 composelib(libnum + LIBRARY);
4198 drawarea(NULL, NULL, NULL);
4199 }
4200 }
4201 else {
4202 pwriteback(areawin->topinstance);
4203 calcbbox(areawin->topinstance);
4204 }
4205 }
4206
4207 /*----------------------------------------*/
4208 /* ButtonPress handler during delete mode */
4209 /*----------------------------------------*/
4210
deletebutton(int x,int y)4211 void deletebutton(int x, int y)
4212 {
4213 UNUSED(x); UNUSED(y);
4214
4215 if (checkselect(ALL_TYPES)) {
4216 standard_element_delete(ERASE);
4217 calcbbox(areawin->topinstance);
4218 }
4219 setoptionmenu(); /* Return GUI check/radio boxes to default */
4220 }
4221
4222 /*----------------------------------------------------------------------*/
4223 /* Process of element deletion. Remove one element from the indicated */
4224 /* object. */
4225 /*----------------------------------------------------------------------*/
4226
delete_one_element(objinstptr thisinstance,genericptr thiselement)4227 void delete_one_element(objinstptr thisinstance, genericptr thiselement)
4228 {
4229 objectptr thisobject;
4230 genericptr *genobj;
4231 Boolean pinchange = False;
4232
4233 thisobject = thisinstance->thisobject;
4234
4235 /* The netlist contains pointers to elements which no longer */
4236 /* exist on the page, so we should remove them from the netlist. */
4237
4238 if (RemoveFromNetlist(thisobject, thiselement)) pinchange = True;
4239 for (genobj = thisobject->plist; genobj < thisobject->plist
4240 + thisobject->parts; genobj++)
4241 if (*genobj == thiselement)
4242 break;
4243
4244 if (genobj == thisobject->plist + thisobject->parts) return;
4245
4246 for (++genobj; genobj < thisobject->plist + thisobject->parts; genobj++)
4247 *(genobj - 1) = *genobj;
4248 thisobject->parts--;
4249
4250 if (pinchange) setobjecttype(thisobject);
4251 incr_changes(thisobject);
4252 calcbbox(thisinstance);
4253 invalidate_netlist(thisobject);
4254 /* freenetlist(thisobject); */
4255 }
4256
4257 /*----------------------------------------------------------------------*/
4258 /* Process of element deletion. Remove everything in the selection */
4259 /* list from the indicated object, and return a new object containing */
4260 /* only the deleted elements. */
4261 /* */
4262 /* if drawmode is DRAW, we erase the objects as we remove them. */
4263 /* */
4264 /* Note that if "slist" is areawin->selectlist, it is freed by this */
4265 /* routine (calls freeselects()), but not otherwise. */
4266 /*----------------------------------------------------------------------*/
4267
delete_element(objinstptr thisinstance,short * slist,int selects,short drawmode)4268 objectptr delete_element(objinstptr thisinstance, short *slist, int selects,
4269 short drawmode)
4270 {
4271 short *selectobj;
4272 objectptr delobj, thisobject;
4273 genericptr *genobj;
4274 Boolean pinchange = False;
4275
4276 if (slist == NULL || selects == 0) return NULL;
4277
4278 thisobject = thisinstance->thisobject;
4279
4280 delobj = (objectptr) malloc(sizeof(object));
4281 initmem(delobj);
4282
4283 if (drawmode) {
4284 SetForeground(dpy, areawin->gc, BACKGROUND);
4285 }
4286
4287 for (selectobj = slist; selectobj < slist + selects; selectobj++) {
4288 genobj = thisobject->plist + *selectobj;
4289 if (drawmode) easydraw(*selectobj, DOFORALL);
4290 PLIST_INCR(delobj);
4291 *(delobj->plist + delobj->parts) = *genobj;
4292 delobj->parts++;
4293
4294 /* The netlist contains pointers to elements which no longer */
4295 /* exist on the page, so we should remove them from the netlist. */
4296
4297 if (RemoveFromNetlist(thisobject, *genobj)) pinchange = True;
4298 for (++genobj; genobj < thisobject->plist + thisobject->parts; genobj++)
4299 *(genobj - 1) = *genobj;
4300 thisobject->parts--;
4301 reviseselect(slist, selects, selectobj);
4302 }
4303 if (pinchange) setobjecttype(thisobject);
4304
4305 if (slist == areawin->selectlist)
4306 freeselects();
4307
4308 calcbbox(thisinstance);
4309 /* freenetlist(thisobject); */
4310
4311 if (drawmode) {
4312 SetForeground(dpy, areawin->gc, FOREGROUND);
4313 drawarea(NULL, NULL, NULL);
4314 }
4315 return delobj;
4316 }
4317
4318 /*----------------------------------------------------------------------*/
4319 /* Wrapper for delete_element(). Remember this deletion for the undo */
4320 /* function. */
4321 /*----------------------------------------------------------------------*/
4322
standard_element_delete(short drawmode)4323 void standard_element_delete(short drawmode)
4324 {
4325 objectptr delobj;
4326
4327 /* register_for_undo(XCF_Select, UNDO_MORE, areawin->topinstance, */
4328 /* areawin->selectlist, areawin->selects); */
4329 select_invalidate_netlist();
4330 delobj = delete_element(areawin->topinstance, areawin->selectlist,
4331 areawin->selects, drawmode);
4332 register_for_undo(XCF_Delete, UNDO_DONE, areawin->topinstance,
4333 delobj, (int)drawmode);
4334 incr_changes(topobject); /* Treat as one change */
4335 }
4336
4337 /*----------------------------------------------------------------------*/
4338 /* Another wrapper for delete_element(), in which we do not save the */
4339 /* deletion as an undo event. However, the returned object is saved */
4340 /* on areawin->editstack, so that the objects can be grabbed. This */
4341 /* allows objects to be carried across pages and through the hierarchy. */
4342 /*----------------------------------------------------------------------*/
4343
delete_for_xfer(short drawmode,short * slist,int selects)4344 void delete_for_xfer(short drawmode, short *slist, int selects)
4345 {
4346 if (selects > 0) {
4347 reset(areawin->editstack, DESTROY);
4348 areawin->editstack = delete_element(areawin->topinstance,
4349 slist, selects, drawmode);
4350 }
4351 }
4352
4353 /*----------------------------------------------------------------------*/
4354 /* Yet another wrapper for delete_element(), in which we destroy the */
4355 /* object returned and free all associated memory. */
4356 /*----------------------------------------------------------------------*/
4357
delete_noundo(short drawmode)4358 void delete_noundo(short drawmode)
4359 {
4360 objectptr delobj;
4361
4362 select_invalidate_netlist();
4363 delobj = delete_element(areawin->topinstance, areawin->selectlist,
4364 areawin->selects, drawmode);
4365
4366 if (delobj != NULL) reset(delobj, DESTROY);
4367 }
4368
4369 /*----------------------------------------------------------------------*/
4370 /* Undelete last deleted elements and return a selectlist of the */
4371 /* elements. If "olist" is non-NULL, then the undeleted elements are */
4372 /* placed into the object of thisinstance in the order given by olist, */
4373 /* and a copy of olist is returned. If "olist" is NULL, then the */
4374 /* undeleted elements are placed at the end of thisinstance->thisobject */
4375 /* ->plist, and a new selection list is returned. If "olist" is non- */
4376 /* NULL, then the size of olist had better match the number of objects */
4377 /* in delobj! It is up to the calling routine to check this. */
4378 /*----------------------------------------------------------------------*/
4379
xc_undelete(objinstptr thisinstance,objectptr delobj,short mode,short * olist)4380 short *xc_undelete(objinstptr thisinstance, objectptr delobj, short mode,
4381 short *olist)
4382 {
4383 objectptr thisobject;
4384 genericptr *regen;
4385 short *slist, count, i; /* position; (jdk) */
4386
4387 thisobject = thisinstance->thisobject;
4388 slist = (short *)malloc(delobj->parts * sizeof(short));
4389 count = 0;
4390
4391 for (regen = delobj->plist; regen < delobj->plist + delobj->parts; regen++) {
4392 PLIST_INCR(thisobject);
4393 if (olist == NULL) {
4394 *(slist + count) = thisobject->parts;
4395 *(topobject->plist + topobject->parts) = *regen;
4396 }
4397 else {
4398 *(slist + count) = *(olist + count);
4399 for (i = thisobject->parts; i > *(olist + count); i--)
4400 *(thisobject->plist + i) = *(thisobject->plist + i - 1);
4401 *(thisobject->plist + i) = *regen;
4402 }
4403 thisobject->parts++;
4404 if (mode) {
4405 XTopSetForeground((*regen)->color);
4406 easydraw(*(slist + count), DEFAULTCOLOR);
4407 }
4408 count++;
4409
4410 /* If the element has passed parameters (eparam), then we have to */
4411 /* check if the key exists in the new parent object. If not, */
4412 /* delete the parameter. */
4413
4414 if ((*regen)->passed) {
4415 eparamptr nextepp, epp = (*regen)->passed;
4416 while (epp != NULL) {
4417 nextepp = epp->next;
4418 if (!match_param(thisobject, epp->key)) {
4419 if (epp == (*regen)->passed) (*regen)->passed = nextepp;
4420 free_element_param(*regen, epp);
4421 }
4422 epp = nextepp;
4423 }
4424 }
4425
4426 /* Likewise, string parameters must be checked in labels because */
4427 /* they act like element parameters. */
4428
4429 if (IS_LABEL(*regen)) {
4430 labelptr glab = TOLABEL(regen);
4431 stringpart *gstr, *lastpart = NULL;
4432 for (gstr = glab->string; gstr != NULL; gstr = lastpart->nextpart) {
4433 if (gstr->type == PARAM_START) {
4434 if (!match_param(thisobject, gstr->data.string)) {
4435 free(gstr->data.string);
4436 if (lastpart)
4437 lastpart->nextpart = gstr->nextpart;
4438 else
4439 glab->string = gstr->nextpart;
4440 free(gstr);
4441 gstr = (lastpart) ? lastpart : glab->string;
4442 }
4443 }
4444 lastpart = gstr;
4445 }
4446 }
4447 }
4448 incr_changes(thisobject); /* treat as one change */
4449 calcbbox(thisinstance);
4450
4451 /* flush the delete buffer but don't delete the elements */
4452 reset(delobj, SAVE);
4453
4454 if (delobj != areawin->editstack) free(delobj);
4455
4456 return slist;
4457 }
4458
4459 /*----------------------------*/
4460 /* select save object handler */
4461 /*----------------------------*/
4462
printname(objectptr curobject)4463 void printname(objectptr curobject)
4464 {
4465 char editstr[10], pagestr[10];
4466 short ispage;
4467
4468 #ifndef TCL_WRAPPER
4469 Arg wargs[1];
4470 Dimension swidth, swidth2, sarea;
4471 char tmpname[256];
4472 char *sptr = tmpname;
4473 #endif
4474
4475 /* print full string to make message widget proper size */
4476
4477 strcpy(editstr, ((ispage = is_page(curobject)) >= 0) ? "Editing: " : "");
4478 strcpy(editstr, (is_library(curobject) >= 0) ? "Library: " : "");
4479 if (strstr(curobject->name, "Page") == NULL && (ispage >= 0))
4480 sprintf(pagestr, " (p. %d)", areawin->page + 1);
4481 else
4482 pagestr[0] = '\0';
4483 W2printf("%s%s%s", editstr, curobject->name, pagestr);
4484
4485 /* Tcl doesn't update width changes immediately. . . what to do? */
4486 /* (i.e., Tk_Width(message2) gives the original width) */
4487 #ifndef TCL_WRAPPER
4488
4489 XtSetArg(wargs[0], XtNwidth, &sarea);
4490 XtGetValues(message2, wargs, 1);
4491
4492 /* in the remote case that the string is longer than message widget, */
4493 /* truncate the string and denote the truncation with an ellipsis (...) */
4494
4495 strcpy(tmpname, curobject->name);
4496 swidth2 = XTextWidth(appdata.xcfont, editstr, strlen(editstr));
4497 swidth = XTextWidth(appdata.xcfont, tmpname, strlen(tmpname));
4498
4499 if ((swidth + swidth2) > sarea) {
4500 char *ip;
4501 while ((swidth + swidth2) > sarea) {
4502 sptr++;
4503 swidth = XTextWidth(appdata.xcfont, sptr, strlen(sptr));
4504 }
4505 for(ip = sptr; ip < sptr + 3 && *ip != '\0'; ip++) *ip = '.';
4506
4507 W2printf("Editing: %s", sptr);
4508 }
4509 #endif
4510 }
4511
4512 /*--------------------------------------------------------------*/
4513 /* Make sure that a string does not conflict with postscript */
4514 /* names (commands and definitions found in xcircps2.pro). */
4515 /* */
4516 /* Return value is NULL if no change was made. Otherwise, the */
4517 /* return value is an allocated string. */
4518 /*--------------------------------------------------------------*/
4519
checkvalidname(char * teststring,objectptr newobj)4520 char *checkvalidname(char *teststring, objectptr newobj)
4521 {
4522 int i, j;
4523 short dupl; /* flag a duplicate string */
4524 objectptr *libobj;
4525 char *sptr, *pptr, *cptr;
4526 aliasptr aref;
4527 slistptr sref;
4528
4529 /* Try not to allocate memory unless necessary */
4530
4531 sptr = teststring;
4532 pptr = sptr;
4533
4534 do {
4535 dupl = 0;
4536 if (newobj != NULL) {
4537 for (i = 0; i < xobjs.numlibs; i++) {
4538 for (j = 0; j < xobjs.userlibs[i].number; j++) {
4539 libobj = xobjs.userlibs[i].library + j;
4540
4541 if (*libobj == newobj) continue;
4542 if (!strcmp(pptr, (*libobj)->name)) {
4543
4544 /* Prepend an underscore to the object name. If the */
4545 /* object has no technology, create a null technology */
4546 /* name. Otherwise, the technology remains the same */
4547 /* but the object name gets the prepended underscore. */
4548
4549 if ((cptr = strstr(pptr, "::")) == NULL) {
4550 pptr = (char *)malloc(strlen((*libobj)->name) + 4);
4551 sprintf(pptr, "::_%s", (*libobj)->name);
4552 }
4553 else {
4554 int offset = cptr - pptr + 2;
4555 if (pptr == sptr)
4556 pptr = (char *)malloc(strlen((*libobj)->name) + 2);
4557 else
4558 pptr = (char *)realloc(pptr, strlen((*libobj)->name) + 2);
4559 sprintf(pptr, "%s", (*libobj)->name);
4560 sprintf(pptr + offset, "_%s", (*libobj)->name + offset);
4561 }
4562 dupl = 1;
4563 }
4564 }
4565 }
4566
4567 /* If we're in the middle of a file load, the name cannot be */
4568 /* the same as an alias, either. */
4569
4570 if (aliastop != NULL) {
4571 for (aref = aliastop; aref != NULL; aref = aref->next) {
4572 for (sref = aref->aliases; sref != NULL; sref = sref->next) {
4573 if (!strcmp(pptr, sref->alias)) {
4574 if (pptr == sptr)
4575 pptr = (char *)malloc(strlen(sref->alias) + 2);
4576 else
4577 pptr = (char *)realloc(pptr, strlen(sref->alias) + 2);
4578 sprintf(pptr, "_%s", sref->alias);
4579 dupl = 1;
4580 }
4581 }
4582 }
4583 }
4584 }
4585
4586 } while (dupl == 1);
4587
4588 return (pptr == sptr) ? NULL : pptr;
4589 }
4590
4591 /*--------------------------------------------------------------*/
4592 /* Make sure that name for new object does not conflict with */
4593 /* existing object definitions */
4594 /* */
4595 /* Return: True if name required change, False otherwise */
4596 /*--------------------------------------------------------------*/
4597
checkname(objectptr newobj)4598 Boolean checkname(objectptr newobj)
4599 {
4600 char *pptr;
4601
4602 /* Check for empty string */
4603 if (strlen(newobj->name) == 0) {
4604 Wprintf("Blank object name changed to default");
4605 sprintf(newobj->name, "user_object");
4606 }
4607
4608 pptr = checkvalidname(newobj->name, newobj);
4609
4610 /* Change name if necessary to avoid naming conflicts */
4611 if (pptr == NULL) {
4612 Wprintf("Created new object %s", newobj->name);
4613 return False;
4614 }
4615 else {
4616 Wprintf("Changed name from %s to %s to avoid conflict with "
4617 "existing object", newobj->name, pptr);
4618
4619 strncpy(newobj->name, pptr, 79);
4620 free(pptr);
4621 }
4622 return True;
4623 }
4624
4625 /*------------------------------------------------------------*/
4626 /* Find the object "dot" in the builtin library, if it exists */
4627 /*------------------------------------------------------------*/
4628
finddot()4629 objectptr finddot()
4630 {
4631 objectptr dotobj;
4632 short i, j;
4633 char *name, *pptr;
4634
4635 for (i = 0; i < xobjs.numlibs; i++) {
4636 for (j = 0; j < xobjs.userlibs[i].number; j++) {
4637 dotobj = *(xobjs.userlibs[i].library + j);
4638 name = dotobj->name;
4639 if ((pptr = strstr(name, "::")) != NULL) name = pptr + 2;
4640 if (!strcmp(name, "dot")) {
4641 return dotobj;
4642 }
4643 }
4644 }
4645 return (objectptr)NULL;
4646 }
4647
4648 /*--------------------------------------*/
4649 /* Add value origin to all points */
4650 /*--------------------------------------*/
4651
movepoints(genericptr * ssgen,short deltax,short deltay)4652 void movepoints(genericptr *ssgen, short deltax, short deltay)
4653 {
4654 switch(ELEMENTTYPE(*ssgen)) {
4655 case ARC:{
4656 fpointlist sspoints;
4657 TOARC(ssgen)->position.x += deltax;
4658 TOARC(ssgen)->position.y += deltay;
4659 for (sspoints = TOARC(ssgen)->points; sspoints < TOARC(ssgen)->points +
4660 TOARC(ssgen)->number; sspoints++) {
4661 sspoints->x += deltax;
4662 sspoints->y += deltay;
4663 }
4664 }break;
4665
4666 case POLYGON:{
4667 pointlist sspoints;
4668 for (sspoints = TOPOLY(ssgen)->points; sspoints < TOPOLY(ssgen)->points +
4669 TOPOLY(ssgen)->number; sspoints++) {
4670 sspoints->x += deltax;
4671 sspoints->y += deltay;
4672 }
4673 }break;
4674
4675 case SPLINE:{
4676 fpointlist sspoints;
4677 short j;
4678 for (sspoints = TOSPLINE(ssgen)->points; sspoints <
4679 TOSPLINE(ssgen)->points + INTSEGS; sspoints++) {
4680 sspoints->x += deltax;
4681 sspoints->y += deltay;
4682 }
4683 for (j = 0; j < 4; j++) {
4684 TOSPLINE(ssgen)->ctrl[j].x += deltax;
4685 TOSPLINE(ssgen)->ctrl[j].y += deltay;
4686 }
4687 }break;
4688 case OBJINST:
4689 TOOBJINST(ssgen)->position.x += deltax;
4690 TOOBJINST(ssgen)->position.y += deltay;
4691 break;
4692 case GRAPHIC:
4693 TOGRAPHIC(ssgen)->position.x += deltax;
4694 TOGRAPHIC(ssgen)->position.y += deltay;
4695 break;
4696 case LABEL:
4697 TOLABEL(ssgen)->position.x += deltax;
4698 TOLABEL(ssgen)->position.y += deltay;
4699 break;
4700 }
4701 }
4702
4703 /*----------------------------------------------------------------------*/
4704 /* Add value origin to all edited points, according to edit flags */
4705 /*----------------------------------------------------------------------*/
4706
editpoints(genericptr * ssgen,short deltax,short deltay)4707 void editpoints(genericptr *ssgen, short deltax, short deltay)
4708 {
4709 pathptr editpath;
4710 polyptr editpoly;
4711 splineptr editspline;
4712 short cycle, cpoint;
4713 pointselect *cptr;
4714 XPoint *curpt;
4715 genericptr *ggen;
4716
4717 switch(ELEMENTTYPE(*ssgen)) {
4718 case POLYGON:
4719 editpoly = TOPOLY(ssgen);
4720 if (editpoly->cycle == NULL)
4721 movepoints(ssgen, deltax, deltay);
4722 else {
4723 for (cptr = editpoly->cycle;; cptr++) {
4724 cycle = cptr->number;
4725 curpt = editpoly->points + cycle;
4726 if (cptr->flags & EDITX) curpt->x += deltax;
4727 if (cptr->flags & EDITY) curpt->y += deltay;
4728 if (cptr->flags & LASTENTRY) break;
4729 }
4730 }
4731 exprsub(*ssgen);
4732 break;
4733
4734 case SPLINE:
4735 editspline = TOSPLINE(ssgen);
4736 if (editspline->cycle == NULL)
4737 movepoints(ssgen, deltax, deltay);
4738 else {
4739 for (cptr = editspline->cycle;; cptr++) {
4740 cycle = cptr->number;
4741 if (cycle == 0 || cycle == 3) {
4742 cpoint = (cycle == 0) ? 1 : 2;
4743 if (cptr->flags & EDITX) editspline->ctrl[cpoint].x += deltax;
4744 if (cptr->flags & EDITY) editspline->ctrl[cpoint].y += deltay;
4745 }
4746 if (cptr->flags & EDITX) editspline->ctrl[cycle].x += deltax;
4747 if (cptr->flags & EDITY) editspline->ctrl[cycle].y += deltay;
4748 if (cptr->flags & ANTIXY) {
4749 editspline->ctrl[cycle].x -= deltax;
4750 editspline->ctrl[cycle].y -= deltay;
4751 }
4752 if (cptr->flags & LASTENTRY) break;
4753 }
4754 }
4755 exprsub(*ssgen);
4756 calcspline(editspline);
4757 break;
4758
4759 case PATH:
4760 editpath = TOPATH(ssgen);
4761 if (checkcycle(*ssgen, 0) < 0) {
4762 for (ggen = editpath->plist; ggen < editpath->plist + editpath->parts;
4763 ggen++)
4764 movepoints(ggen, deltax, deltay);
4765 }
4766 else {
4767 for (ggen = editpath->plist; ggen < editpath->plist + editpath->parts;
4768 ggen++) {
4769 if (checkcycle(*ggen, 0) >= 0)
4770 editpoints(ggen, deltax, deltay);
4771 }
4772 }
4773 break;
4774
4775 default:
4776 movepoints(ssgen, deltax, deltay);
4777 exprsub(*ssgen);
4778 break;
4779 }
4780 }
4781
4782 #ifndef TCL_WRAPPER
4783
xlib_makeobject(xcWidget w,caddr_t nulldata)4784 void xlib_makeobject(xcWidget w, caddr_t nulldata)
4785 {
4786 UNUSED(w); UNUSED(nulldata);
4787
4788 domakeobject(-1, (char *)_STR2, FALSE);
4789 }
4790
4791 #endif /* !TCL_WRAPPER */
4792
4793 /*--------------------------------------------------------------*/
4794 /* Set the name for a new user-defined object and make the */
4795 /* object. If "forceempty" is true, we allow creation of a new */
4796 /* object with no elements (normally would be used only from a */
4797 /* script, where an object is being constructed automatically). */
4798 /*--------------------------------------------------------------*/
4799
domakeobject(int libnum,char * name,Boolean forceempty)4800 objinstptr domakeobject(int libnum, char *name, Boolean forceempty)
4801 {
4802 objectptr *newobj;
4803 objinstptr *newinst;
4804 genericptr *ssgen;
4805 oparamptr ops, newop;
4806 eparamptr epp, newepp;
4807 stringpart *sptr;
4808 XPoint origin;
4809 short loclibnum = libnum;
4810
4811 if (libnum == -1) loclibnum = USERLIB - LIBRARY;
4812
4813 /* make room for new entry in library list */
4814
4815 xobjs.userlibs[loclibnum].library = (objectptr *)
4816 realloc(xobjs.userlibs[loclibnum].library,
4817 (xobjs.userlibs[loclibnum].number + 1) * sizeof(objectptr));
4818
4819 newobj = xobjs.userlibs[loclibnum].library + xobjs.userlibs[loclibnum].number;
4820
4821 *newobj = delete_element(areawin->topinstance, areawin->selectlist,
4822 areawin->selects, NORMAL);
4823
4824 if (*newobj == NULL) {
4825 objectptr initobj;
4826
4827 if (!forceempty) return NULL;
4828
4829 /* Create a new (empty) object */
4830
4831 initobj = (objectptr) malloc(sizeof(object));
4832 initmem(initobj);
4833 *newobj = initobj;
4834 }
4835
4836 invalidate_netlist(topobject);
4837 xobjs.userlibs[loclibnum].number++;
4838
4839 /* Create the instance of this object so we can compute a bounding box */
4840
4841 NEW_OBJINST(newinst, topobject);
4842 instancedefaults(*newinst, *newobj, 0, 0);
4843 calcbbox(*newinst);
4844
4845 /* find closest snap point to bbox center and make this the obj. center */
4846
4847 if (areawin->center) {
4848 origin.x = (*newobj)->bbox.lowerleft.x + (*newobj)->bbox.width / 2;
4849 origin.y = (*newobj)->bbox.lowerleft.y + (*newobj)->bbox.height / 2;
4850 }
4851 else {
4852 origin.x = origin.y = 0;
4853 }
4854 u2u_snap(&origin);
4855 instancedefaults(*newinst, *newobj, origin.x, origin.y);
4856
4857 for (ssgen = (*newobj)->plist; ssgen < (*newobj)->plist + (*newobj)->parts;
4858 ssgen++) {
4859 switch(ELEMENTTYPE(*ssgen)) {
4860
4861 case(OBJINST):
4862 TOOBJINST(ssgen)->position.x -= origin.x;
4863 TOOBJINST(ssgen)->position.y -= origin.y;
4864 break;
4865
4866 case(GRAPHIC):
4867 TOGRAPHIC(ssgen)->position.x -= origin.x;
4868 TOGRAPHIC(ssgen)->position.y -= origin.y;
4869 break;
4870
4871 case(LABEL):
4872 TOLABEL(ssgen)->position.x -= origin.x;
4873 TOLABEL(ssgen)->position.y -= origin.y;
4874 break;
4875
4876 case(PATH):{
4877 genericptr *pathlist;
4878 for (pathlist = TOPATH(ssgen)->plist; pathlist < TOPATH(ssgen)->plist
4879 + TOPATH(ssgen)->parts; pathlist++) {
4880 movepoints(pathlist, -origin.x, -origin.y);
4881 }
4882 }break;
4883
4884 default:
4885 movepoints(ssgen, -origin.x, -origin.y);
4886 break;
4887 }
4888 }
4889
4890 for (ssgen = (*newobj)->plist; ssgen < (*newobj)->plist + (*newobj)->parts;
4891 ssgen++) {
4892 for (epp = (*ssgen)->passed; epp != NULL; epp = epp->next) {
4893 ops = match_param(topobject, epp->key);
4894 newop = copyparameter(ops);
4895 newop->next = (*newobj)->params;
4896 (*newobj)->params = newop;
4897
4898 /* Generate an indirect parameter reference from child to parent */
4899 newepp = make_new_eparam(epp->key);
4900 newepp->flags |= P_INDIRECT;
4901 newepp->pdata.refkey = strdup(epp->key);
4902 newepp->next = (*newinst)->passed;
4903 (*newinst)->passed = newepp;
4904 }
4905 if (IS_LABEL(*ssgen)) {
4906 /* Also need to check for substring parameters in labels */
4907 for (sptr = TOLABEL(ssgen)->string; sptr != NULL; sptr = sptr->nextpart) {
4908 if (sptr->type == PARAM_START) {
4909 ops = match_param(topobject, sptr->data.string);
4910 if (ops) {
4911 newop = copyparameter(ops);
4912 newop->next = (*newobj)->params;
4913 (*newobj)->params = newop;
4914 }
4915
4916 /* Generate an indirect parameter reference from child to parent */
4917 newepp = make_new_eparam(sptr->data.string);
4918 newepp->flags |= P_INDIRECT;
4919 newepp->pdata.refkey = strdup(sptr->data.string);
4920 newepp->next = (*newinst)->passed;
4921 (*newinst)->passed = newepp;
4922 }
4923 }
4924 }
4925 }
4926
4927 /* any parameters in the top-level object that used by the selected */
4928 /* elements must be copied into the new object. */
4929
4930 /* put new object back into place */
4931
4932 (*newobj)->hidden = False;
4933 (*newobj)->schemtype = SYMBOL;
4934
4935 calcbbox(*newinst);
4936 incr_changes(*newobj);
4937
4938 /* (netlist invalidation was taken care of by delete_element() */
4939 /* Also, incr_changes(topobject) was done by delete_element()) */
4940
4941 XTopSetForeground((*newinst)->color);
4942 UDrawObject(*newinst, SINGLE, (*newinst)->color,
4943 xobjs.pagelist[areawin->page]->wirewidth, NULL);
4944
4945
4946 /* Copy name into object and check for conflicts */
4947
4948 strcpy((*newobj)->name, name);
4949 checkname(*newobj);
4950
4951 /* register the technology and mark the technology as not saved */
4952 AddObjectTechnology(*newobj);
4953
4954 /* generate library instance for this object (bounding box */
4955 /* should be default, so don't do calcbbox() on it) */
4956
4957 addtoinstlist(loclibnum, *newobj, FALSE);
4958
4959 /* recompile the user catalog and reset view bounds */
4960
4961 composelib(loclibnum + LIBRARY);
4962 centerview(xobjs.libtop[loclibnum + LIBRARY]);
4963
4964 return *newinst;
4965 }
4966
4967 #ifndef TCL_WRAPPER
4968
4969 /*-------------------------------------------*/
4970 /* Make a user object from selected elements */
4971 /*-------------------------------------------*/
4972
selectsave(xcWidget w,caddr_t clientdata,caddr_t calldata)4973 void selectsave(xcWidget w, caddr_t clientdata, caddr_t calldata)
4974 {
4975 buttonsave *popdata = (buttonsave *)malloc(sizeof(buttonsave));
4976 UNUSED(clientdata); UNUSED(calldata);
4977
4978 if (areawin->selects == 0) return; /* nothing was selected */
4979
4980 /* Get a name for the new object */
4981
4982 eventmode = NORMAL_MODE;
4983 popdata->dataptr = NULL;
4984 popdata->button = NULL; /* indicates that no button is assc'd w/ the popup */
4985 popupprompt(w, "Enter name for new object:", "\0", xlib_makeobject, popdata, NULL);
4986 }
4987
4988 #endif
4989
4990 /*-----------------------------*/
4991 /* Edit-stack support routines */
4992 /*-----------------------------*/
4993
arceditpush(arcptr lastarc)4994 void arceditpush(arcptr lastarc)
4995 {
4996 arcptr *newarc;
4997
4998 NEW_ARC(newarc, areawin->editstack);
4999 arccopy(*newarc, lastarc);
5000 copycycles(&((*newarc)->cycle), &(lastarc->cycle));
5001 }
5002
5003 /*--------------------------------------*/
5004
splineeditpush(splineptr lastspline)5005 void splineeditpush(splineptr lastspline)
5006 {
5007 splineptr *newspline;
5008
5009 NEW_SPLINE(newspline, areawin->editstack);
5010 splinecopy(*newspline, lastspline);
5011 }
5012
5013 /*--------------------------------------*/
5014
polyeditpush(polyptr lastpoly)5015 void polyeditpush(polyptr lastpoly)
5016 {
5017 polyptr *newpoly;
5018
5019 NEW_POLY(newpoly, areawin->editstack);
5020 polycopy(*newpoly, lastpoly);
5021 }
5022
5023 /*--------------------------------------*/
5024
patheditpush(pathptr lastpath)5025 void patheditpush(pathptr lastpath)
5026 {
5027 pathptr *newpath;
5028
5029 NEW_PATH(newpath, areawin->editstack);
5030 pathcopy(*newpath, lastpath);
5031 }
5032
5033 /*-----------------------------*/
5034 /* Copying support routines */
5035 /*-----------------------------*/
5036
copypoints(pointlist points,int number)5037 pointlist copypoints(pointlist points, int number)
5038 {
5039 pointlist rpoints, cpoints, newpoints;
5040
5041 rpoints = (pointlist) malloc(number * sizeof(XPoint));
5042 for (newpoints = rpoints, cpoints = points;
5043 newpoints < rpoints + number;
5044 newpoints++, cpoints++) {
5045 newpoints->x = cpoints->x;
5046 newpoints->y = cpoints->y;
5047 }
5048 return rpoints;
5049 }
5050
5051 /*------------------------------------------*/
5052
graphiccopy(graphicptr newg,graphicptr copyg)5053 void graphiccopy(graphicptr newg, graphicptr copyg)
5054 {
5055 Imagedata *iptr;
5056 int i;
5057
5058 newg->source = copyg->source;
5059 newg->position.x = copyg->position.x;
5060 newg->position.y = copyg->position.y;
5061 newg->rotation = copyg->rotation;
5062 newg->scale = copyg->scale;
5063 newg->color = copyg->color;
5064 newg->passed = NULL;
5065 copyalleparams((genericptr)newg, (genericptr)copyg);
5066 #ifndef HAVE_CAIRO
5067 newg->valid = FALSE;
5068 newg->target = NULL;
5069 newg->clipmask = (Pixmap)NULL;
5070 #endif /* HAVE_CAIRO */
5071
5072 /* Update the refcount of the source image */
5073 for (i = 0; i < xobjs.images; i++) {
5074 iptr = xobjs.imagelist + i;
5075 if (iptr->image == newg->source) {
5076 iptr->refcount++;
5077 break;
5078 }
5079 }
5080 }
5081
5082 /*------------------------------------------*/
5083
labelcopy(labelptr newtext,labelptr copytext)5084 void labelcopy(labelptr newtext, labelptr copytext)
5085 {
5086 newtext->string = stringcopy(copytext->string);
5087 newtext->position.x = copytext->position.x;
5088 newtext->position.y = copytext->position.y;
5089 newtext->rotation = copytext->rotation;
5090 newtext->scale = copytext->scale;
5091 newtext->anchor = copytext->anchor;
5092 newtext->color = copytext->color;
5093 newtext->passed = NULL;
5094 newtext->cycle = NULL;
5095 copyalleparams((genericptr)newtext, (genericptr)copytext);
5096 newtext->pin = copytext->pin;
5097 }
5098
5099 /*------------------------------------------*/
5100
arccopy(arcptr newarc,arcptr copyarc)5101 void arccopy(arcptr newarc, arcptr copyarc)
5102 {
5103 newarc->style = copyarc->style;
5104 newarc->color = copyarc->color;
5105 newarc->position.x = copyarc->position.x;
5106 newarc->position.y = copyarc->position.y;
5107 newarc->radius = copyarc->radius;
5108 newarc->yaxis = copyarc->yaxis;
5109 newarc->angle1 = copyarc->angle1;
5110 newarc->angle2 = copyarc->angle2;
5111 newarc->width = copyarc->width;
5112 newarc->passed = NULL;
5113 newarc->cycle = NULL;
5114 copyalleparams((genericptr)newarc, (genericptr)copyarc);
5115 calcarc(newarc);
5116 }
5117
5118 /*------------------------------------------*/
5119
polycopy(polyptr newpoly,polyptr copypoly)5120 void polycopy(polyptr newpoly, polyptr copypoly)
5121 {
5122 newpoly->style = copypoly->style;
5123 newpoly->color = copypoly->color;
5124 newpoly->width = copypoly->width;
5125 newpoly->number = copypoly->number;
5126 copycycles(&(newpoly->cycle), &(copypoly->cycle));
5127 newpoly->points = copypoints(copypoly->points, copypoly->number);
5128
5129 newpoly->passed = NULL;
5130 copyalleparams((genericptr)newpoly, (genericptr)copypoly);
5131 }
5132
5133 /*------------------------------------------*/
5134
splinecopy(splineptr newspline,splineptr copyspline)5135 void splinecopy(splineptr newspline, splineptr copyspline)
5136 {
5137 short i;
5138
5139 newspline->style = copyspline->style;
5140 newspline->color = copyspline->color;
5141 newspline->width = copyspline->width;
5142 copycycles(&(newspline->cycle), &(copyspline->cycle));
5143 for (i = 0; i < 4; i++) {
5144 newspline->ctrl[i].x = copyspline->ctrl[i].x;
5145 newspline->ctrl[i].y = copyspline->ctrl[i].y;
5146 }
5147 for (i = 0; i < INTSEGS; i++) {
5148 newspline->points[i].x = copyspline->points[i].x;
5149 newspline->points[i].y = copyspline->points[i].y;
5150 }
5151 newspline->passed = NULL;
5152 copyalleparams((genericptr)newspline, (genericptr)copyspline);
5153 }
5154
5155 /*----------------------------------------------*/
5156 /* Copy a path */
5157 /*----------------------------------------------*/
5158
pathcopy(pathptr newpath,pathptr copypath)5159 void pathcopy(pathptr newpath, pathptr copypath)
5160 {
5161 genericptr *ggen;
5162 splineptr *newspline, copyspline;
5163 polyptr *newpoly, copypoly;
5164
5165 newpath->style = copypath->style;
5166 newpath->color = copypath->color;
5167 newpath->width = copypath->width;
5168 newpath->parts = 0;
5169 newpath->passed = NULL;
5170 copyalleparams((genericptr)newpath, (genericptr)copypath);
5171 newpath->plist = (genericptr *)malloc(copypath->parts * sizeof(genericptr));
5172
5173 for (ggen = copypath->plist; ggen < copypath->plist + copypath->parts; ggen++) {
5174 switch (ELEMENTTYPE(*ggen)) {
5175 case POLYGON:
5176 copypoly = TOPOLY(ggen);
5177 NEW_POLY(newpoly, newpath);
5178 polycopy(*newpoly, copypoly);
5179 break;
5180 case SPLINE:
5181 copyspline = TOSPLINE(ggen);
5182 NEW_SPLINE(newspline, newpath);
5183 splinecopy(*newspline, copyspline);
5184 break;
5185 }
5186 }
5187 }
5188
5189 /*--------------------------------------------------------------*/
5190 /* Copy an object instance */
5191 /*--------------------------------------------------------------*/
5192
instcopy(objinstptr newobj,objinstptr copyobj)5193 void instcopy(objinstptr newobj, objinstptr copyobj)
5194 {
5195 newobj->position.x = copyobj->position.x;
5196 newobj->position.y = copyobj->position.y;
5197 newobj->rotation = copyobj->rotation;
5198 newobj->scale = copyobj->scale;
5199 newobj->style = copyobj->style;
5200 newobj->thisobject = copyobj->thisobject;
5201 newobj->color = copyobj->color;
5202 newobj->bbox.lowerleft.x = copyobj->bbox.lowerleft.x;
5203 newobj->bbox.lowerleft.y = copyobj->bbox.lowerleft.y;
5204 newobj->bbox.width = copyobj->bbox.width;
5205 newobj->bbox.height = copyobj->bbox.height;
5206
5207 newobj->passed = NULL;
5208 copyalleparams((genericptr)newobj, (genericptr)copyobj);
5209
5210 newobj->params = NULL;
5211 copyparams(newobj, copyobj);
5212
5213 /* If the parameters are the same, the bounding box should be, too. */
5214 if (copyobj->schembbox != NULL) {
5215 newobj->schembbox = (BBox *)malloc(sizeof(BBox));
5216 newobj->schembbox->lowerleft.x = copyobj->schembbox->lowerleft.x;
5217 newobj->schembbox->lowerleft.y = copyobj->schembbox->lowerleft.y;
5218 newobj->schembbox->width = copyobj->schembbox->width;
5219 newobj->schembbox->height = copyobj->schembbox->height;
5220 }
5221 else
5222 newobj->schembbox = NULL;
5223 }
5224
5225 /*--------------------------------------------------------------*/
5226 /* The method for removing objects from a list is to add the */
5227 /* value REMOVE_TAG to the type of each object needing to be */
5228 /* removed, and then calling this routine. */
5229 /*--------------------------------------------------------------*/
5230
delete_tagged(objinstptr thisinst)5231 void delete_tagged(objinstptr thisinst) {
5232 Boolean tagged = True;
5233 objectptr thisobject, delobj;
5234 genericptr *pgen;
5235 short *sobj, stmp;
5236
5237 thisobject = thisinst->thisobject;
5238
5239 while (tagged) {
5240 tagged = False;
5241 for (stmp = 0; stmp < thisobject->parts; stmp++) {
5242 pgen = thisobject->plist + stmp;
5243 if ((*pgen)->type & REMOVE_TAG) {
5244 (*pgen)->type &= (~REMOVE_TAG);
5245 tagged = True;
5246
5247 delobj = delete_element(thisinst, &stmp, 1, 0);
5248 register_for_undo(XCF_Delete, UNDO_MORE, thisinst, delobj, 0);
5249
5250 /* If we destroy elements in the current window, we need to */
5251 /* make sure that the selection list is updated appropriately. */
5252
5253 if ((thisobject == topobject) && (areawin->selects > 0)) {
5254 for (sobj = areawin->selectlist; sobj < areawin->selectlist +
5255 areawin->selects; sobj++)
5256 if (*sobj > stmp) (*sobj)--;
5257 }
5258
5259 /* Also ensure that this element is not referenced in any */
5260 /* netlist. If it is, remove it and mark the netlist as */
5261 /* invalid. */
5262
5263 remove_netlist_element(thisobject, *pgen);
5264 }
5265 }
5266 }
5267 undo_finish_series();
5268 }
5269
5270 /*-----------------------------------------------------------------*/
5271 /* For copying: Check if an object is about to be placed directly */
5272 /* on top of the same object. If so, delete the one underneath. */
5273 /*-----------------------------------------------------------------*/
5274
checkoverlap()5275 void checkoverlap()
5276 {
5277 short *sobj, *cobj;
5278 genericptr *sgen, *pgen;
5279 Boolean tagged = False;
5280
5281 /* Work through the select list */
5282
5283 for (sobj = areawin->selectlist; sobj < areawin->selectlist +
5284 areawin->selects; sobj++) {
5285 sgen = topobject->plist + (*sobj);
5286
5287 /* For each object being copied, compare it against every object */
5288 /* on the current page (except self). Flag if it's the same. */
5289
5290 for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts;
5291 pgen++) {
5292 if (pgen == sgen) continue;
5293 if (compare_single(sgen, pgen)) {
5294 /* Make sure that this object is not part of the selection, */
5295 /* else chaos will reign. */
5296 for (cobj = areawin->selectlist; cobj < areawin->selectlist +
5297 areawin->selects; cobj++) {
5298 if (pgen == topobject->plist + (*cobj)) break;
5299 }
5300 /* Tag it for future deletion and prevent further compares */
5301 if (cobj == areawin->selectlist + areawin->selects) {
5302 tagged = True;
5303 (*pgen)->type |= REMOVE_TAG;
5304 }
5305 }
5306 }
5307 }
5308 if (tagged) {
5309 /* Delete the tagged elements */
5310 Wprintf("Duplicate object deleted");
5311 delete_tagged(areawin->topinstance);
5312 incr_changes(topobject);
5313 }
5314 }
5315
5316 /*--------------------------------------------------------------*/
5317 /* Direct placement of elements. Assumes that the selectlist */
5318 /* contains all the elements to be positioned. "deltax" and */
5319 /* "deltay" are relative x and y positions to move the */
5320 /* elements. */
5321 /*--------------------------------------------------------------*/
5322
placeselects(short deltax,short deltay,XPoint * userpt)5323 void placeselects(short deltax, short deltay, XPoint *userpt)
5324 {
5325 short *dragselect;
5326 XPoint newpos, *ppt;
5327 float rot;
5328 short closest;
5329 Boolean doattach;
5330 genericptr *pgen;
5331 polyptr cpoly;
5332
5333 doattach = ((userpt == NULL) || (areawin->attachto < 0)) ? FALSE : TRUE;
5334
5335 /* under attachto condition, keep element attached to */
5336 /* the attachto element. */
5337
5338 if (doattach) findattach(&newpos, &rot, userpt);
5339
5340 #ifdef HAVE_CAIRO
5341 #else
5342 areawin->clipped = -1; /* Prevent clipping */
5343 #endif /* HAVE_CAIRO */
5344
5345 for (dragselect = areawin->selectlist; dragselect < areawin->selectlist
5346 + areawin->selects; dragselect++) {
5347
5348 switch(SELECTTYPE(dragselect)) {
5349 case OBJINST: {
5350 objinstptr draginst = SELTOOBJINST(dragselect);
5351
5352 if (doattach) {
5353 draginst->position.x = newpos.x;
5354 draginst->position.y = newpos.y;
5355 while (rot >= 360.0) rot -= 360.0;
5356 while (rot < 0.0) rot += 360.0;
5357 draginst->rotation = rot;
5358 }
5359 else {
5360 draginst->position.x += deltax;
5361 draginst->position.y += deltay;
5362 }
5363
5364 } break;
5365 case GRAPHIC: {
5366 graphicptr dragg = SELTOGRAPHIC(dragselect);
5367 dragg->position.x += deltax;
5368 dragg->position.y += deltay;
5369 } break;
5370 case LABEL: {
5371 labelptr draglabel = SELTOLABEL(dragselect);
5372 if (doattach) {
5373 draglabel->position.x = newpos.x;
5374 draglabel->position.y = newpos.y;
5375 draglabel->rotation = rot;
5376 }
5377 else {
5378 draglabel->position.x += deltax;
5379 draglabel->position.y += deltay;
5380 }
5381 } break;
5382 case PATH: {
5383 pathptr dragpath = SELTOPATH(dragselect);
5384 genericptr *pathlist;
5385
5386 if (doattach) {
5387 XPoint *pdelta = pathclosepoint(dragpath, &newpos);
5388 deltax = newpos.x - pdelta->x;
5389 deltay = newpos.y - pdelta->y;
5390 }
5391 for (pathlist = dragpath->plist; pathlist < dragpath->plist
5392 + dragpath->parts; pathlist++) {
5393 movepoints(pathlist, deltax, deltay);
5394 }
5395 } break;
5396 case POLYGON: {
5397 polyptr dragpoly = SELTOPOLY(dragselect);
5398 pointlist dragpoints;
5399
5400 /* if (dragpoly->cycle != NULL) continue; */
5401 if (doattach) {
5402 closest = closepoint(dragpoly, &newpos);
5403 deltax = newpos.x - dragpoly->points[closest].x;
5404 deltay = newpos.y - dragpoly->points[closest].y;
5405 }
5406 for (dragpoints = dragpoly->points; dragpoints < dragpoly->points
5407 + dragpoly->number; dragpoints++) {
5408 dragpoints->x += deltax;
5409 dragpoints->y += deltay;
5410 }
5411 } break;
5412 case SPLINE: {
5413 splineptr dragspline = SELTOSPLINE(dragselect);
5414 short j;
5415 fpointlist dragpoints;
5416
5417 /* if (dragspline->cycle != NULL) continue; */
5418 if (doattach) {
5419 closest = (wirelength(&dragspline->ctrl[0], &newpos)
5420 > wirelength(&dragspline->ctrl[3], &newpos)) ? 3 : 0;
5421 deltax = newpos.x - dragspline->ctrl[closest].x;
5422 deltay = newpos.y - dragspline->ctrl[closest].y;
5423 }
5424 for (dragpoints = dragspline->points; dragpoints < dragspline->
5425 points + INTSEGS; dragpoints++) {
5426 dragpoints->x += deltax;
5427 dragpoints->y += deltay;
5428 }
5429 for (j = 0; j < 4; j++) {
5430 dragspline->ctrl[j].x += deltax;
5431 dragspline->ctrl[j].y += deltay;
5432 }
5433 } break;
5434 case ARC: {
5435 arcptr dragarc = SELTOARC(dragselect);
5436 fpointlist dragpoints;
5437
5438 if (doattach) {
5439 deltax = newpos.x - dragarc->position.x;
5440 deltay = newpos.y - dragarc->position.y;
5441 }
5442 dragarc->position.x += deltax;
5443 dragarc->position.y += deltay;
5444 for (dragpoints = dragarc->points; dragpoints < dragarc->
5445 points + dragarc->number; dragpoints++) {
5446 dragpoints->x += deltax;
5447 dragpoints->y += deltay;
5448 }
5449 } break;
5450 }
5451 }
5452
5453 if (areawin->pinattach) {
5454 for (pgen = topobject->plist; pgen < topobject->plist +
5455 topobject->parts; pgen++) {
5456 if (ELEMENTTYPE(*pgen) == POLYGON) {
5457 cpoly = TOPOLY(pgen);
5458 if (cpoly->cycle != NULL) {
5459 ppt = cpoly->points + cpoly->cycle->number;
5460 newpos.x = ppt->x + deltax;
5461 newpos.y = ppt->y + deltay;
5462 if (areawin->manhatn)
5463 manhattanize(&newpos, cpoly, cpoly->cycle->number, FALSE);
5464 ppt->x = newpos.x;
5465 ppt->y = newpos.y;
5466 }
5467 }
5468 }
5469 }
5470
5471 move_mode_draw(xcDRAW_EDIT, NULL);
5472 #ifdef HAVE_CAIRO
5473 #else
5474 areawin->clipped = 0;
5475 #endif /* HAVE_CAIRO */
5476 }
5477
5478 /*----------------------------------------------------------------------*/
5479 /* Copy handler. Assumes that the selectlist contains the elements */
5480 /* to be copied, and that the initial position of the copy is held */
5481 /* in areawin->save. */
5482 /*----------------------------------------------------------------------*/
5483
createcopies()5484 void createcopies()
5485 {
5486 short *selectobj;
5487
5488 if (!checkselect_draw(ALL_TYPES, True)) return;
5489 u2u_snap(&areawin->save);
5490 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
5491 + areawin->selects; selectobj++) {
5492
5493 /* Cycles will not be used for copy mode: remove them */
5494 removecycle(topobject->plist + (*selectobj));
5495
5496 switch(SELECTTYPE(selectobj)) {
5497 case LABEL: { /* copy label */
5498 labelptr copytext = SELTOLABEL(selectobj);
5499 labelptr *newtext;
5500
5501 NEW_LABEL(newtext, topobject);
5502 labelcopy(*newtext, copytext);
5503 } break;
5504 case OBJINST: { /* copy object instance */
5505 objinstptr copyobj = SELTOOBJINST(selectobj);
5506 objinstptr *newobj;
5507 NEW_OBJINST(newobj, topobject);
5508 instcopy(*newobj, copyobj);
5509 } break;
5510 case GRAPHIC: { /* copy graphic instance */
5511 graphicptr copyg = SELTOGRAPHIC(selectobj);
5512 graphicptr *newg;
5513 NEW_GRAPHIC(newg, topobject);
5514 graphiccopy(*newg, copyg);
5515 } break;
5516 case PATH: { /* copy path */
5517 pathptr copypath = SELTOPATH(selectobj);
5518 pathptr *newpath;
5519 NEW_PATH(newpath, topobject);
5520 pathcopy(*newpath, copypath);
5521 } break;
5522 case ARC: { /* copy arc */
5523 arcptr copyarc = SELTOARC(selectobj);
5524 arcptr *newarc;
5525 NEW_ARC(newarc, topobject);
5526 arccopy(*newarc, copyarc);
5527 } break;
5528 case POLYGON: { /* copy polygons */
5529 polyptr copypoly = SELTOPOLY(selectobj);
5530 polyptr *newpoly;
5531 NEW_POLY(newpoly, topobject);
5532 polycopy(*newpoly, copypoly);
5533 } break;
5534 case SPLINE: { /* copy spline */
5535 splineptr copyspline = SELTOSPLINE(selectobj);
5536 splineptr *newspline;
5537 NEW_SPLINE(newspline, topobject);
5538 splinecopy(*newspline, copyspline);
5539 } break;
5540 }
5541
5542 /* change selection from the old to the new object */
5543
5544 *selectobj = topobject->parts - 1;
5545 }
5546 }
5547
5548 /*--------------------------------------------------------------*/
5549 /* Function which initiates interactive placement of copied */
5550 /* elements. */
5551 /*--------------------------------------------------------------*/
5552
copydrag()5553 void copydrag()
5554 {
5555 if (areawin->selects > 0) {
5556 move_mode_draw(xcDRAW_INIT, NULL);
5557 if (eventmode == NORMAL_MODE) {
5558 XDefineCursor(dpy, areawin->window, COPYCURSOR);
5559 eventmode = COPY_MODE;
5560 #ifdef TCL_WRAPPER
5561 Tk_CreateEventHandler(areawin->area, PointerMotionMask |
5562 ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5563 #else
5564 XtAddEventHandler(areawin->area, PointerMotionMask |
5565 ButtonMotionMask, False, (XtEventHandler)xlib_drag,
5566 NULL);
5567 #endif
5568 }
5569 select_invalidate_netlist();
5570 }
5571 }
5572
5573 /*-----------------------------------------------------------*/
5574 /* Copy handler for copying from a button push or key event. */
5575 /*-----------------------------------------------------------*/
5576
copy_op(int op,int x,int y)5577 void copy_op(int op, int x, int y)
5578 {
5579 if (op == XCF_Copy) {
5580 window_to_user(x, y, &areawin->save);
5581 createcopies(); /* This function does all the hard work */
5582 copydrag(); /* Start interactive placement */
5583 }
5584 else {
5585 eventmode = NORMAL_MODE;
5586 areawin->attachto = -1;
5587 W3printf("");
5588 #ifdef TCL_WRAPPER
5589 xcRemoveEventHandler(areawin->area, PointerMotionMask |
5590 ButtonMotionMask, False, (xcEventHandler)xctk_drag,
5591 NULL);
5592 #else
5593 xcRemoveEventHandler(areawin->area, PointerMotionMask |
5594 ButtonMotionMask, False, (xcEventHandler)xlib_drag,
5595 NULL);
5596 #endif
5597 XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5598 u2u_snap(&areawin->save);
5599 if (op == XCF_Cancel) {
5600 move_mode_draw(xcDRAW_EMPTY, NULL);
5601 delete_noundo(NORMAL);
5602 }
5603 else if (op == XCF_Finish_Copy) {
5604 move_mode_draw(xcDRAW_FINAL, NULL);
5605 /* If selected objects are the only ones on the page, */
5606 /* then do a full bbox calculation. */
5607 if (topobject->parts == areawin->selects)
5608 calcbbox(areawin->topinstance);
5609 else
5610 calcbboxselect();
5611 checkoverlap();
5612 register_for_undo(XCF_Copy, UNDO_MORE, areawin->topinstance,
5613 areawin->selectlist, areawin->selects);
5614 unselect_all();
5615 incr_changes(topobject);
5616 }
5617 else { /* XCF_Continue_Copy */
5618 move_mode_draw(xcDRAW_FINAL, NULL);
5619 if (topobject->parts == areawin->selects)
5620 calcbbox(areawin->topinstance);
5621 else
5622 calcbboxselect();
5623 checkoverlap();
5624 register_for_undo(XCF_Copy, UNDO_DONE, areawin->topinstance,
5625 areawin->selectlist, areawin->selects);
5626 createcopies();
5627 copydrag(); /* Start interactive placement again */
5628 incr_changes(topobject);
5629 }
5630 }
5631 }
5632
5633 /*----------------------------------------------*/
5634 /* Check for more than one button being pressed */
5635 /*----------------------------------------------*/
5636
checkmultiple(XButtonEvent * event)5637 Boolean checkmultiple(XButtonEvent *event)
5638 {
5639 int state = Button1Mask | Button2Mask | Button3Mask |
5640 Button4Mask | Button5Mask;
5641 state &= event->state;
5642 /* ((x - 1) & x) is always non-zero if x has more than one bit set */
5643 return (((state - 1) & state) == 0) ? False : True;
5644 }
5645
5646 /*----------------------------------------------------------------------*/
5647 /* Operation continuation---dependent upon the ongoing operation. */
5648 /* This operation only pertains to a few event modes for which */
5649 /* continuation of action makes sense---drawing wires (polygons), and */
5650 /* editing polygons, arcs, splines, and paths. */
5651 /*----------------------------------------------------------------------*/
5652
continue_op(int op,int x,int y)5653 void continue_op(int op, int x, int y)
5654 {
5655 XPoint ppos;
5656
5657 if (eventmode != EARC_MODE && eventmode != ARC_MODE) {
5658 window_to_user(x, y, &areawin->save);
5659 }
5660 snap(x, y, &ppos);
5661 printpos(ppos.x, ppos.y);
5662
5663 switch(eventmode) {
5664 case(COPY_MODE):
5665 copy_op(op, x, y);
5666 break;
5667 case(WIRE_MODE):
5668 wire_op(op, x, y);
5669 break;
5670 case(EPATH_MODE): case(EPOLY_MODE): case (ARC_MODE):
5671 case(EARC_MODE): case(SPLINE_MODE): case(ESPLINE_MODE):
5672 path_op(*(EDITPART), op, x, y);
5673 break;
5674 case(EINST_MODE):
5675 inst_op(*(EDITPART), op, x, y);
5676 break;
5677 case(BOX_MODE):
5678 finish_op(XCF_Finish_Element, x, y);
5679 break;
5680 default:
5681 break;
5682 }
5683 }
5684
5685 /*--------------------------------------------------------------*/
5686 /* Finish or cancel an operation. This forces a return to */
5687 /* "normal" mode, with whatever other side effects are caused */
5688 /* by the operation. */
5689 /*--------------------------------------------------------------*/
5690
finish_op(int op,int x,int y)5691 void finish_op(int op, int x, int y)
5692 {
5693 labelptr curlabel;
5694 int libnum;
5695 XPoint snappt, boxpts[4];
5696 float fscale;
5697
5698 if (eventmode != EARC_MODE && eventmode != ARC_MODE) {
5699 window_to_user(x, y, &areawin->save);
5700 }
5701 switch(eventmode) {
5702 case(EPATH_MODE): case(BOX_MODE): case(EPOLY_MODE): case (ARC_MODE):
5703 case(EARC_MODE): case(SPLINE_MODE): case(ESPLINE_MODE):
5704 path_op(*(EDITPART), op, x, y);
5705 break;
5706
5707 case(EINST_MODE):
5708 inst_op(*(EDITPART), op, x, y);
5709 break;
5710
5711 case (FONTCAT_MODE):
5712 case (EFONTCAT_MODE):
5713 fontcat_op(op, x, y);
5714 eventmode = (eventmode == FONTCAT_MODE) ? TEXT_MODE : ETEXT_MODE;
5715 text_mode_draw(xcDRAW_INIT, TOLABEL(EDITPART));
5716 XDefineCursor (dpy, areawin->window, TEXTPTR);
5717 break;
5718
5719 case(ASSOC_MODE):
5720 case(CATALOG_MODE):
5721 catalog_op(op, x, y);
5722 break;
5723
5724 case(WIRE_MODE):
5725 wire_op(op, x, y);
5726 break;
5727
5728 case(COPY_MODE):
5729 copy_op(op, x, y);
5730 break;
5731
5732 case(TEXT_MODE):
5733 curlabel = TOLABEL(EDITPART);
5734 if (op == XCF_Cancel) {
5735 redrawtext(curlabel);
5736 areawin->redraw_needed = False; /* ignore previous requests */
5737 text_mode_draw(xcDRAW_EMPTY, curlabel);
5738 freelabel(curlabel->string);
5739 free(curlabel);
5740 topobject->parts--;
5741 curlabel = NULL;
5742 }
5743 else {
5744 singlebbox(EDITPART);
5745 incr_changes(topobject);
5746 select_invalidate_netlist();
5747 text_mode_draw(xcDRAW_FINAL, curlabel);
5748 }
5749 setdefaultfontmarks();
5750 eventmode = NORMAL_MODE;
5751 break;
5752
5753 case(ETEXT_MODE): case(CATTEXT_MODE):
5754 curlabel = TOLABEL(EDITPART);
5755 if (op == XCF_Cancel) {
5756 /* restore the original text */
5757 undrawtext(curlabel);
5758 undo_finish_series();
5759 undo_action();
5760 redrawtext(curlabel);
5761 areawin->redraw_needed = False; /* ignore previous requests */
5762 text_mode_draw(xcDRAW_EMPTY, curlabel);
5763 if (eventmode == CATTEXT_MODE) eventmode = CATALOG_MODE;
5764 W3printf("");
5765 setdefaultfontmarks();
5766 }
5767 else textreturn(); /* Generate "return" key character */
5768 areawin->textend = 0;
5769 break;
5770
5771 case(CATMOVE_MODE):
5772 u2u_snap(&areawin->save);
5773 #ifdef TCL_WRAPPER
5774 Tk_DeleteEventHandler(areawin->area, ButtonMotionMask |
5775 PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5776 #else
5777 xcRemoveEventHandler(areawin->area, ButtonMotionMask |
5778 PointerMotionMask, FALSE, (xcEventHandler)xlib_drag,
5779 NULL);
5780 #endif
5781 if (op == XCF_Cancel) {
5782 /* Just regenerate the library where we started */
5783 if (areawin->selects >= 1) {
5784 objinstptr selinst = SELTOOBJINST(areawin->selectlist);
5785 libnum = libfindobject(selinst->thisobject, NULL);
5786 if (libnum >= 0)
5787 composelib(libnum + LIBRARY);
5788 }
5789 }
5790 else {
5791 catmove(x, y);
5792 }
5793 clearselects();
5794 eventmode = CATALOG_MODE;
5795 XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5796 break;
5797
5798 case(MOVE_MODE):
5799 u2u_snap(&areawin->save);
5800 #ifdef TCL_WRAPPER
5801 Tk_DeleteEventHandler(areawin->area, ButtonMotionMask |
5802 PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5803 #else
5804 xcRemoveEventHandler(areawin->area, ButtonMotionMask |
5805 PointerMotionMask, FALSE, (xcEventHandler)xlib_drag,
5806 NULL);
5807 #endif
5808 if (op == XCF_Cancel) {
5809 /* If we came from the library with an object instance, in */
5810 /* MOVE_MODE, then "cancel" should delete the element. */
5811 /* Otherwise, put the position of the element back to what */
5812 /* it was before we started the move. The difference is */
5813 /* indicated by the value of areawin->editpart. */
5814
5815 if ((areawin->selects > 0) && (*areawin->selectlist == topobject->parts))
5816 delete_noundo(NORMAL);
5817 else
5818 placeselects(areawin->origin.x - areawin->save.x,
5819 areawin->origin.y - areawin->save.y, NULL);
5820 clearselects();
5821 drawarea(NULL, NULL, NULL);
5822 }
5823 else {
5824 if (areawin->selects > 0) {
5825 register_for_undo(XCF_Move,
5826 /* (was_preselected) ? UNDO_DONE : UNDO_MORE, */
5827 UNDO_MORE,
5828 areawin->topinstance,
5829 (int)(areawin->save.x - areawin->origin.x),
5830 (int)(areawin->save.y - areawin->origin.y));
5831 pwriteback(areawin->topinstance);
5832 incr_changes(topobject);
5833 select_invalidate_netlist();
5834 unselect_all(); /* The way it used to be. . . */
5835 }
5836 W3printf("");
5837 /* full calc needed: move may shrink bbox */
5838 calcbbox(areawin->topinstance);
5839 checkoverlap();
5840 /* if (!was_preselected) clearselects(); */
5841 }
5842 areawin->attachto = -1;
5843 break;
5844
5845 case(RESCALE_MODE):
5846
5847 #ifdef TCL_WRAPPER
5848 Tk_DeleteEventHandler(areawin->area, PointerMotionMask |
5849 ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5850 #else
5851 xcRemoveEventHandler(areawin->area, PointerMotionMask |
5852 ButtonMotionMask, FALSE, (xcEventHandler)xlib_drag,
5853 NULL);
5854 #endif
5855 rescale_mode_draw(xcDRAW_FINAL, NULL);
5856 if (op != XCF_Cancel) {
5857 XPoint newpoints[5];
5858 fscale = UGetRescaleBox(&areawin->save, newpoints);
5859 if (fscale != 0.) {
5860 elementrescale(fscale);
5861 areawin->redraw_needed = True;
5862 }
5863 }
5864 eventmode = NORMAL_MODE;
5865 break;
5866
5867 case(SELAREA_MODE):
5868 selarea_mode_draw(xcDRAW_FINAL, NULL);
5869
5870 #ifdef TCL_WRAPPER
5871 Tk_DeleteEventHandler(areawin->area, ButtonMotionMask |
5872 PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5873 #else
5874 xcRemoveEventHandler(areawin->area, ButtonMotionMask |
5875 PointerMotionMask, FALSE, (xcEventHandler)xlib_drag,
5876 NULL);
5877 #endif
5878 /* Zero-width boxes act like a normal selection. Otherwise, */
5879 /* proceed with the area select. */
5880
5881 if ((areawin->origin.x == areawin->save.x) &&
5882 (areawin->origin.y == areawin->save.y))
5883 select_add_element(ALL_TYPES);
5884 else {
5885 boxpts[0] = areawin->origin;
5886 boxpts[1].x = areawin->save.x;
5887 boxpts[1].y = areawin->origin.y;
5888 boxpts[2] = areawin->save;
5889 boxpts[3].x = areawin->origin.x;
5890 boxpts[3].y = areawin->save.y;
5891 selectarea(topobject, boxpts, 0);
5892 }
5893 break;
5894
5895 case(PAN_MODE):
5896 u2u_snap(&areawin->save);
5897
5898 #ifdef TCL_WRAPPER
5899 Tk_DeleteEventHandler(areawin->area, PointerMotionMask |
5900 ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5901 #else
5902 xcRemoveEventHandler(areawin->area, PointerMotionMask |
5903 ButtonMotionMask, False, (xcEventHandler)xlib_drag,
5904 NULL);
5905 #endif
5906 areawin->panx = areawin->pany = 0;
5907 if (op != XCF_Cancel)
5908 panbutton((u_int) 5, (areawin->width >> 1) - (x - areawin->origin.x),
5909 (areawin->height >> 1) - (y - areawin->origin.y), 0);
5910 break;
5911 default:
5912 break;
5913 }
5914
5915 /* Remove any selections */
5916 if ((eventmode == SELAREA_MODE) || (eventmode == PAN_MODE)
5917 || (eventmode == MOVE_MODE))
5918 {
5919 eventmode = NORMAL_MODE;
5920 areawin->redraw_needed = True;
5921 }
5922 else if (eventmode != MOVE_MODE && eventmode != EPATH_MODE &&
5923 eventmode != EPOLY_MODE && eventmode != ARC_MODE &&
5924 eventmode != EARC_MODE && eventmode != SPLINE_MODE &&
5925 eventmode != ESPLINE_MODE && eventmode != WIRE_MODE &&
5926 eventmode != ETEXT_MODE && eventmode != TEXT_MODE) {
5927 unselect_all();
5928 }
5929
5930 if (eventmode == NORMAL_MODE) {
5931
5932 /* Return any highlighted networks to normal */
5933 highlightnetlist(topobject, areawin->topinstance, 0);
5934
5935 XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5936 }
5937
5938 snap(x, y, &snappt);
5939 printpos(snappt.x, snappt.y);
5940 }
5941
5942 /*--------------------------------------------------------------*/
5943 /* Edit operations for instances. This is used to allow */
5944 /* numeric parameters to be adjusted from the hierarchical */
5945 /* level above, shielding the the unparameterized parts from */
5946 /* change. */
5947 /*--------------------------------------------------------------*/
5948
inst_op(genericptr editpart,int op,int x,int y)5949 void inst_op(genericptr editpart, int op, int x, int y)
5950 {
5951 UNUSED(editpart); UNUSED(op); UNUSED(x); UNUSED(y);
5952 }
5953
5954 /*--------------------------------------------------------------*/
5955 /* Operations for path components */
5956 /*--------------------------------------------------------------*/
5957
path_op(genericptr editpart,int op,int x,int y)5958 void path_op(genericptr editpart, int op, int x, int y)
5959 {
5960 polyptr newpoly;
5961 splineptr newspline;
5962 Boolean donecycles = False;
5963 UNUSED(x); UNUSED(y);
5964
5965 /* Don't allow point cycling in a multi-part edit. */
5966 /* Allowing it is just confusing. Instead, we treat */
5967 /* button 1 (cycle) like button 2 (finish). */
5968 if (op == XCF_Continue_Element && areawin->selects > 1)
5969 op = XCF_Finish_Element;
5970
5971 switch(ELEMENTTYPE(editpart)) {
5972 case (PATH): {
5973 pathptr newpath = (pathptr)editpart;
5974 short dotrack = True;
5975 pathptr editpath;
5976
5977 areawin->attachto = -1;
5978
5979 if (op != XCF_Continue_Element) {
5980 dotrack = False;
5981 }
5982 if (op == XCF_Continue_Element) {
5983 nextpathcycle(newpath, 1);
5984 patheditpush(newpath);
5985 }
5986 else if (op == XCF_Finish_Element) {
5987 path_mode_draw(xcDRAW_FINAL, newpath);
5988 incr_changes(topobject);
5989 }
5990 else { /* restore previous path from edit stack */
5991 free_single((genericptr)newpath);
5992 if (areawin->editstack->parts > 0) {
5993 if (op == XCF_Cancel) {
5994 editpath = TOPATH(areawin->editstack->plist);
5995 pathcopy(newpath, editpath);
5996 reset(areawin->editstack, NORMAL);
5997 }
5998 else {
5999 editpath = TOPATH(areawin->editstack->plist +
6000 areawin->editstack->parts - 1);
6001 pathcopy(newpath, editpath);
6002 free_single((genericptr)editpath);
6003 free(editpath);
6004 areawin->editstack->parts--;
6005 }
6006 if (areawin->editstack->parts > 0) {
6007 dotrack = True;
6008 nextpathcycle(newpath, 1);
6009 path_mode_draw(xcDRAW_EDIT, newpath);
6010 }
6011 else {
6012 XPoint warppt;
6013 user_to_window(areawin->origin, &warppt);
6014 warppointer(warppt.x, warppt.y);
6015 path_mode_draw(xcDRAW_FINAL, newpath);
6016 }
6017 }
6018 else {
6019 path_mode_draw(xcDRAW_EMPTY, newpath);
6020 topobject->parts--;
6021 free_single((genericptr)newpath);
6022 free(newpath);
6023 }
6024 }
6025 pwriteback(areawin->topinstance);
6026
6027 if (!dotrack) {
6028 /* Free the editstack */
6029 reset(areawin->editstack, NORMAL);
6030 xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6031 (xcEventHandler)trackelement, NULL);
6032 eventmode = NORMAL_MODE;
6033 donecycles = True;
6034 }
6035 } break;
6036
6037 case (POLYGON): {
6038 if (eventmode == BOX_MODE) {
6039 polyptr newbox;
6040
6041 newbox = (polyptr)editpart;
6042
6043 /* prevent length and/or width zero boxes */
6044 if (newbox->points->x != (newbox->points + 2)->x && (newbox->points
6045 + 1)->y != (newbox->points + 3)->y) {
6046 if (op != XCF_Cancel) {
6047 poly_mode_draw(xcDRAW_FINAL, newbox);
6048 incr_changes(topobject);
6049 if (!nonnetwork(newbox)) invalidate_netlist(topobject);
6050 register_for_undo(XCF_Box, UNDO_MORE, areawin->topinstance,
6051 newbox);
6052 }
6053 else {
6054 poly_mode_draw(xcDRAW_EMPTY, newbox);
6055 free_single((genericptr)newbox);
6056 free(newbox);
6057 topobject->parts--;
6058
6059 }
6060 }
6061 else {
6062 poly_mode_draw(xcDRAW_EMPTY, newbox);
6063 free_single((genericptr)newbox);
6064 free(newbox);
6065 topobject->parts--;
6066 }
6067
6068 xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6069 (xcEventHandler)trackbox, NULL);
6070 eventmode = NORMAL_MODE;
6071 }
6072 else { /* EPOLY_MODE */
6073 polyptr editpoly;
6074 short dotrack = True;
6075
6076 newpoly = (polyptr)editpart;
6077 areawin->attachto = -1;
6078
6079 if (op != XCF_Continue_Element) {
6080 dotrack = False;
6081 }
6082
6083 if (op == XCF_Continue_Element) {
6084 nextpolycycle(&newpoly, 1);
6085 polyeditpush(newpoly);
6086 }
6087 else if (op == XCF_Finish_Element) {
6088
6089 /* Check for degenerate polygons (all points the same). */
6090 int i;
6091 for (i = 1; i < newpoly->number; i++)
6092 if ((newpoly->points[i].x != newpoly->points[i - 1].x) ||
6093 (newpoly->points[i].y != newpoly->points[i - 1].y))
6094 break;
6095 if (i == newpoly->number) {
6096 poly_mode_draw(xcDRAW_EMPTY, newpoly);
6097 /* Remove this polygon with the standard delete */
6098 /* method (saves polygon on undo stack). */
6099 newpoly->type |= REMOVE_TAG;
6100 delete_tagged(areawin->topinstance);
6101 }
6102 else {
6103 poly_mode_draw(xcDRAW_FINAL, newpoly);
6104 if (!nonnetwork(newpoly)) invalidate_netlist(topobject);
6105 incr_changes(topobject);
6106 }
6107 }
6108 else {
6109 XPoint warppt;
6110 free_single((genericptr)newpoly);
6111 if (areawin->editstack->parts > 0) {
6112 if (op == XCF_Cancel) {
6113 editpoly = TOPOLY(areawin->editstack->plist);
6114 polycopy(newpoly, editpoly);
6115 reset(areawin->editstack, NORMAL);
6116 }
6117 else {
6118 editpoly = TOPOLY(areawin->editstack->plist +
6119 areawin->editstack->parts - 1);
6120 polycopy(newpoly, editpoly);
6121 free_single((genericptr)editpoly);
6122 free(editpoly);
6123 areawin->editstack->parts--;
6124 }
6125 if (areawin->editstack->parts > 0) {
6126 dotrack = True;
6127 nextpolycycle(&newpoly, -1);
6128 poly_mode_draw(xcDRAW_EDIT, newpoly);
6129 }
6130 else {
6131 XcTopSetForeground(newpoly->color);
6132 user_to_window(areawin->origin, &warppt);
6133 warppointer(warppt.x, warppt.y);
6134 poly_mode_draw(xcDRAW_FINAL, newpoly);
6135 }
6136 }
6137 else {
6138 poly_mode_draw(xcDRAW_EMPTY, newpoly);
6139 topobject->parts--;
6140 free(newpoly);
6141 }
6142 }
6143 pwriteback(areawin->topinstance);
6144
6145 if (!dotrack) {
6146 /* Free the editstack */
6147 reset(areawin->editstack, NORMAL);
6148
6149 xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6150 (xcEventHandler)trackelement, NULL);
6151 eventmode = NORMAL_MODE;
6152 donecycles = True;
6153 }
6154 }
6155 } break;
6156
6157 case (ARC): {
6158 arcptr newarc, editarc;
6159 short dotrack = True;
6160
6161 newarc = (arcptr)editpart;
6162
6163 if (op != XCF_Continue_Element) {
6164 dotrack = False;
6165 }
6166
6167 if (op == XCF_Continue_Element) {
6168 nextarccycle(&newarc, 1);
6169 arceditpush(newarc);
6170 }
6171
6172 else if (op == XCF_Finish_Element) {
6173 dotrack = False;
6174
6175 if (newarc->radius != 0 && newarc->yaxis != 0 &&
6176 (newarc->angle1 != newarc->angle2)) {
6177 XTopSetForeground(newarc->color);
6178 incr_changes(topobject);
6179 if (eventmode == ARC_MODE) {
6180 register_for_undo(XCF_Arc, UNDO_MORE, areawin->topinstance,
6181 newarc);
6182 }
6183 arc_mode_draw(xcDRAW_FINAL, newarc);
6184 }
6185 else {
6186
6187 /* Remove the record if the radius is zero. If we were */
6188 /* creating the arc, just delete it; it's as if it */
6189 /* never existed. If we were editing an arc, use the */
6190 /* standard delete method (saves arc on undo stack). */
6191
6192 arc_mode_draw(xcDRAW_EMPTY, newarc);
6193 if (eventmode == ARC_MODE) {
6194 free_single((genericptr)newarc);
6195 free(newarc);
6196 topobject->parts--;
6197 }
6198 else {
6199 newarc->type |= REMOVE_TAG;
6200 delete_tagged(areawin->topinstance);
6201 }
6202 }
6203 }
6204 else { /* Cancel: restore previous arc from edit stack */
6205 free_single((genericptr)newarc);
6206 if (areawin->editstack->parts > 0) {
6207 if (op == XCF_Cancel) {
6208 editarc = TOARC(areawin->editstack->plist);
6209 arccopy(newarc, editarc);
6210 copycycles(&(newarc->cycle), &(editarc->cycle));
6211 reset(areawin->editstack, NORMAL);
6212 }
6213 else {
6214 editarc = TOARC(areawin->editstack->plist +
6215 areawin->editstack->parts - 1);
6216 arccopy(newarc, editarc);
6217 copycycles(&(newarc->cycle), &(editarc->cycle));
6218 free_single((genericptr)editarc);
6219 free(editarc);
6220 areawin->editstack->parts--;
6221 }
6222 if (areawin->editstack->parts > 0) {
6223 dotrack = True;
6224 nextarccycle(&newarc, -1);
6225 arc_mode_draw(xcDRAW_EDIT, newarc);
6226 }
6227 else {
6228 if (eventmode != ARC_MODE) {
6229 XPoint warppt;
6230 user_to_window(areawin->origin, &warppt);
6231 warppointer(warppt.x, warppt.y);
6232 }
6233 arc_mode_draw(xcDRAW_FINAL, newarc);
6234 }
6235 }
6236 else {
6237 arc_mode_draw(xcDRAW_EMPTY, newarc);
6238 topobject->parts--;
6239 }
6240 }
6241 pwriteback(areawin->topinstance);
6242
6243 if (!dotrack) {
6244 /* Free the editstack */
6245 reset(areawin->editstack, NORMAL);
6246
6247 xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6248 (xcEventHandler)trackarc, NULL);
6249 eventmode = NORMAL_MODE;
6250 }
6251 } break;
6252
6253 case (SPLINE): {
6254 splineptr editspline;
6255 short dotrack = True;
6256
6257 newspline = (splineptr)editpart;
6258
6259 if (op != XCF_Continue_Element) {
6260 dotrack = False;
6261 }
6262
6263 if (op == XCF_Continue_Element) {
6264 /* Note: we work backwards through spline control points. */
6265 /* The reason is that when creating a spline, the sudden */
6266 /* move from the endpoint to the startpoint (forward */
6267 /* direction) is more disorienting than moving from the */
6268 /* endpoint to the endpoint's control point. */
6269
6270 nextsplinecycle(&newspline, -1);
6271 splineeditpush(newspline);
6272 }
6273
6274 /* unlikely but possible to create zero-length splines */
6275 else if (newspline->ctrl[0].x != newspline->ctrl[3].x ||
6276 newspline->ctrl[0].x != newspline->ctrl[1].x ||
6277 newspline->ctrl[0].x != newspline->ctrl[2].x ||
6278 newspline->ctrl[0].y != newspline->ctrl[3].y ||
6279 newspline->ctrl[0].y != newspline->ctrl[1].y ||
6280 newspline->ctrl[0].y != newspline->ctrl[2].y) {
6281 if (op == XCF_Finish_Element) {
6282 incr_changes(topobject);
6283 if (eventmode == SPLINE_MODE) {
6284 register_for_undo(XCF_Spline, UNDO_MORE, areawin->topinstance,
6285 newspline);
6286 }
6287 spline_mode_draw(xcDRAW_FINAL, newspline);
6288 }
6289 else { /* restore previous spline from edit stack */
6290 free_single((genericptr)newspline);
6291 if (areawin->editstack->parts > 0) {
6292 if (op == XCF_Cancel) {
6293 editspline = TOSPLINE(areawin->editstack->plist);
6294 splinecopy(newspline, editspline);
6295 reset(areawin->editstack, NORMAL);
6296 }
6297 else {
6298 editspline = TOSPLINE(areawin->editstack->plist +
6299 areawin->editstack->parts - 1);
6300 splinecopy(newspline, editspline);
6301 free_single((genericptr)editspline);
6302 free(editspline);
6303 areawin->editstack->parts--;
6304 }
6305 if (areawin->editstack->parts > 0) {
6306 dotrack = True;
6307 nextsplinecycle(&newspline, 1);
6308 spline_mode_draw(xcDRAW_EDIT, newspline);
6309 }
6310 else {
6311 if (eventmode != SPLINE_MODE) {
6312 XPoint warppt;
6313 user_to_window(areawin->origin, &warppt);
6314 warppointer(warppt.x, warppt.y);
6315 }
6316 spline_mode_draw(xcDRAW_FINAL, newspline);
6317 }
6318 }
6319 else {
6320 spline_mode_draw(xcDRAW_EMPTY, newspline);
6321 topobject->parts--;
6322 }
6323 }
6324 }
6325 else {
6326 spline_mode_draw(xcDRAW_EMPTY, newspline);
6327 free_single((genericptr)newspline);
6328 free(newspline);
6329 topobject->parts--;
6330 }
6331 pwriteback(areawin->topinstance);
6332
6333 if (!dotrack) {
6334 /* Free the editstack */
6335 reset(areawin->editstack, NORMAL);
6336
6337 xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6338 (xcEventHandler)trackelement, NULL);
6339 eventmode = NORMAL_MODE;
6340 donecycles = True;
6341 }
6342 } break;
6343 }
6344 calcbbox(areawin->topinstance);
6345
6346 /* Multiple-element edit: Some items may have been moved as */
6347 /* opposed to edited, and should be registered here. To do */
6348 /* this correctly, we must first unselect the edited items, */
6349 /* then register the move for the remaining items. */
6350
6351 if (donecycles) {
6352 short *eselect;
6353
6354 for (eselect = areawin->selectlist; eselect < areawin->selectlist +
6355 areawin->selects; eselect++)
6356 checkcycle(SELTOGENERIC(eselect), 0);
6357
6358 /* Remove all (remaining) cycles */
6359 for (eselect = areawin->selectlist; eselect < areawin->selectlist +
6360 areawin->selects; eselect++)
6361 removecycle(SELTOGENERICPTR(eselect));
6362
6363 /* Remove edits from the undo stack when canceling */
6364 if (op == XCF_Cancel || op == XCF_Cancel_Last) {
6365 if (xobjs.undostack && (xobjs.undostack->type == XCF_Edit)) {
6366 undo_finish_series();
6367 undo_action();
6368 }
6369 }
6370 }
6371 }
6372
6373 /*-------------------------------------------------------*/
6374 /* Recalculate values for a drawing-area widget resizing */
6375 /*-------------------------------------------------------*/
6376
resizearea(xcWidget w,caddr_t clientdata,caddr_t calldata)6377 void resizearea(xcWidget w, caddr_t clientdata, caddr_t calldata)
6378 {
6379 #ifndef TCL_WRAPPER
6380 Arg wargs[2];
6381 #endif
6382 XEvent discard;
6383 int savewidth = areawin->width, saveheight = areawin->height;
6384 XCWindowData *thiswin;
6385 #ifndef HAVE_CAIRO
6386 XGCValues values;
6387 #endif /* !HAVE_CAIRO */
6388 UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
6389
6390 if ((dpy != NULL) && xcIsRealized(areawin->area)) {
6391
6392 #ifdef TCL_WRAPPER
6393 areawin->width = Tk_Width(w);
6394 areawin->height = Tk_Height(w);
6395 #else
6396 XtSetArg(wargs[0], XtNwidth, &areawin->width);
6397 XtSetArg(wargs[1], XtNheight, &areawin->height);
6398 XtGetValues(areawin->area, wargs, 2);
6399 #endif
6400
6401 if (areawin->width != savewidth || areawin->height != saveheight) {
6402
6403 int maxwidth = 0, maxheight = 0;
6404 for (thiswin = xobjs.windowlist; thiswin != NULL; thiswin = thiswin->next) {
6405 if (thiswin->width > maxwidth) maxwidth = thiswin->width;
6406 if (thiswin->height > maxheight) maxheight = thiswin->height;
6407 }
6408 #if !defined(HAVE_CAIRO)
6409 if (dbuf != (Pixmap)NULL) XFreePixmap(dpy, dbuf);
6410 dbuf = XCreatePixmap(dpy, areawin->window, maxwidth, maxheight,
6411 DefaultDepthOfScreen(xcScreen(w)));
6412 #endif
6413 #ifdef HAVE_CAIRO
6414 /* TODO: probably make this a generalized function call, which */
6415 /* should be handled depending on the surface in the xtgui.c, */
6416 /* xcwin32.c, etc. files. */
6417 /* For now only support xlib surface */
6418 cairo_xlib_surface_set_size(areawin->surface, areawin->width,
6419 areawin->height);
6420 #else
6421 if (areawin->clipmask != (Pixmap)NULL) XFreePixmap(dpy, areawin->clipmask);
6422 areawin->clipmask = XCreatePixmap(dpy, areawin->window,
6423 maxwidth, maxheight, 1);
6424
6425 if (areawin->pbuf != (Pixmap)NULL) {
6426 XFreePixmap(dpy, areawin->pbuf);
6427 areawin->pbuf = XCreatePixmap(dpy, areawin->window,
6428 maxwidth, maxheight, 1);
6429 }
6430
6431 if (areawin->cmgc != (GC)NULL) XFreeGC(dpy, areawin->cmgc);
6432 values.foreground = 0;
6433 values.background = 0;
6434 areawin->cmgc = XCreateGC(dpy, areawin->clipmask,
6435 GCForeground | GCBackground, &values);
6436 #endif /* !HAVE_CAIRO */
6437
6438 /* Clear fixed_pixmap */
6439 if (areawin->fixed_pixmap) {
6440 #ifdef HAVE_CAIRO
6441 cairo_pattern_destroy(areawin->fixed_pixmap);
6442 areawin->fixed_pixmap = NULL;
6443 #else /* !HAVE_CAIRO */
6444 XFreePixmap(dpy, areawin->fixed_pixmap);
6445 areawin->fixed_pixmap = (Pixmap) NULL;
6446 #endif /* !HAVE_CAIRO */
6447 }
6448
6449 reset_gs();
6450
6451 /* Re-compose the directores to match the new dimensions */
6452 composelib(LIBLIB);
6453 composelib(PAGELIB);
6454
6455 /* Re-center image in resized window */
6456 zoomview(NULL, NULL, NULL);
6457 }
6458
6459 /* Flush all expose events from the buffer */
6460 while (XCheckWindowEvent(dpy, areawin->window, ExposureMask, &discard) == True);
6461 }
6462 }
6463
6464 /*----------------------*/
6465 /* Draw the grids, etc. */
6466 /*----------------------*/
6467
6468 #ifndef HAVE_CAIRO
draw_grids(void)6469 void draw_grids(void)
6470 {
6471 float x, y, spc, spc2, i, j, fpart;
6472 float major_snapspace, spc3;
6473
6474 spc = xobjs.pagelist[areawin->page]->gridspace * areawin->vscale;
6475 if (areawin->gridon && spc > 8) {
6476 fpart = (float)(-areawin->pcorner.x)
6477 / xobjs.pagelist[areawin->page]->gridspace;
6478 x = xobjs.pagelist[areawin->page]->gridspace *
6479 (fpart - (float)((int)fpart)) * areawin->vscale;
6480 fpart = (float)(-areawin->pcorner.y)
6481 / xobjs.pagelist[areawin->page]->gridspace;
6482 y = xobjs.pagelist[areawin->page]->gridspace *
6483 (fpart - (float)((int)fpart)) * areawin->vscale;
6484
6485 SetForeground(dpy, areawin->gc, GRIDCOLOR);
6486 for (i = x; i < (float)areawin->width; i += spc)
6487 DrawLine (dpy, areawin->window, areawin->gc, (int)(i + 0.5),
6488 0, (int)(i + 0.5), areawin->height);
6489 for (j = (float)areawin->height - y; j > 0; j -= spc)
6490 DrawLine (dpy, areawin->window, areawin->gc, 0, (int)(j - 0.5),
6491 areawin->width, (int)(j - 0.5));
6492 }
6493
6494 if (areawin->axeson) {
6495 XPoint originpt, zeropt;
6496 zeropt.x = zeropt.y = 0;
6497 SetForeground(dpy, areawin->gc, AXESCOLOR);
6498 user_to_window(zeropt, &originpt);
6499 DrawLine(dpy, areawin->window, areawin->gc, originpt.x, 0,
6500 originpt.x, areawin->height);
6501 DrawLine(dpy, areawin->window, areawin->gc, 0, originpt.y,
6502 areawin->width, originpt.y);
6503 }
6504
6505 /* bounding box goes beneath everything except grid/axis lines */
6506 UDrawBBox();
6507
6508 /* draw a little red dot at each snap-to point */
6509
6510 spc2 = xobjs.pagelist[areawin->page]->snapspace * areawin->vscale;
6511 if (areawin->snapto && spc2 > 8) {
6512 float x2, y2;
6513
6514 fpart = (float)(-areawin->pcorner.x)
6515 / xobjs.pagelist[areawin->page]->snapspace;
6516 x2 = xobjs.pagelist[areawin->page]->snapspace *
6517 (fpart - (float)((int)fpart)) * areawin->vscale;
6518 fpart = (float)(-areawin->pcorner.y)
6519 / xobjs.pagelist[areawin->page]->snapspace;
6520 y2 = xobjs.pagelist[areawin->page]->snapspace *
6521 (fpart - (float)((int)fpart)) * areawin->vscale;
6522
6523 #if defined(TCL_WRAPPER) && defined(XC_WIN32)
6524 {
6525 HDC hdc = CreateCompatibleDC(NULL);
6526 SelectObject(hdc, Tk_GetHWND(areawin->window));
6527 #endif
6528 SetForeground(dpy, areawin->gc, SNAPCOLOR);
6529 for (i = x2; i < areawin->width; i += spc2)
6530 for (j = areawin->height - y2; j > 0; j -= spc2)
6531 #if defined(TCL_WRAPPER) && defined(XC_WIN32)
6532 SetPixelV(hdc, (int)(i + 0.5), (int)(j - 0.05), areawin->gc->foreground);
6533 #endif
6534 DrawPoint (dpy, areawin->window, areawin->gc, (int)(i + 0.5),
6535 (int)(j - 0.5));
6536 #if defined(TCL_WRAPPER) && defined(XC_WIN32)
6537 DeleteDC(hdc);
6538 }
6539 #endif
6540 };
6541
6542 /* Draw major snap points (code contributed by John Barry) */
6543
6544 major_snapspace = xobjs.pagelist[areawin->page]->gridspace * 20;
6545 spc3 = major_snapspace * areawin->vscale;
6546 if (spc > 4) {
6547 fpart = (float)(-areawin->pcorner.x) / major_snapspace;
6548 x = major_snapspace * (fpart - (float)((int)fpart)) * areawin->vscale;
6549 fpart = (float)(-areawin->pcorner.y) / major_snapspace;
6550 y = major_snapspace * (fpart - (float)((int)fpart)) * areawin->vscale;
6551
6552 SetForeground(dpy, areawin->gc, GRIDCOLOR);
6553 for (i = x; i < (float)areawin->width; i += spc3) {
6554 for (j = (float)areawin->height - y; j > 0; j -= spc3) {
6555 XDrawArc(dpy, areawin->window, areawin->gc, (int)(i + 0.5) - 1,
6556 (int)(j - 0.5) - 1, 2, 2, 0, 360*64);
6557 }
6558 }
6559 }
6560
6561 SetBackground(dpy, areawin->gc, BACKGROUND);
6562 }
6563 #endif /* !HAVE_CAIRO */
6564
6565 /*------------------------------------------------------*/
6566 /* Draw fixed parts of the primary graphics window */
6567 /*------------------------------------------------------*/
6568
draw_fixed(void)6569 void draw_fixed(void)
6570 {
6571 Boolean old_ongoing;
6572 #ifndef HAVE_CAIRO
6573 Window old_window;
6574 #endif /* !HAVE_CAIRO */
6575
6576 if (xobjs.suspend >= 0) return;
6577 old_ongoing = areawin->redraw_ongoing;
6578 areawin->redraw_ongoing = True;
6579
6580 /* Set drawing context to fixed_pixmap */
6581 #ifdef HAVE_CAIRO
6582 cairo_identity_matrix(areawin->cr);
6583 cairo_push_group(areawin->cr);
6584 #else /* HAVE_CAIRO */
6585 old_window = areawin->window;
6586 areawin->window = areawin->fixed_pixmap;
6587 #endif /* HAVE_CAIRO */
6588
6589 /* Clear background */
6590 #ifdef HAVE_CAIRO
6591 if (xobjs.pagelist[areawin->page]->background.name != (char *)NULL) {
6592 #ifdef HAVE_GS
6593 copybackground();
6594 #else /* HAVE_GS */
6595 SetForeground(dpy, areawin->gc, BACKGROUND);
6596 cairo_paint(areawin->cr);
6597 #endif /* HAVE_GS */
6598 }
6599 else {
6600 SetForeground(dpy, areawin->gc, BACKGROUND);
6601 cairo_paint(areawin->cr);
6602 }
6603 #else /* HAVE_CAIRO */
6604 if (xobjs.pagelist[areawin->page]->background.name == (char *)NULL
6605 || (copybackground() < 0)) {
6606 SetForeground(dpy, areawin->gc, BACKGROUND);
6607 XFillRectangle(dpy, areawin->window, areawin->gc, 0, 0, areawin->width,
6608 areawin->height);
6609 }
6610 SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapRound, JoinBevel);
6611 #endif /* !HAVE_CAIRO */
6612
6613 newmatrix();
6614
6615 /* draw GRIDCOLOR lines for grid; mark axes in AXESCOLOR */
6616
6617 if (eventmode != CATALOG_MODE && eventmode != ASSOC_MODE
6618 && eventmode != FONTCAT_MODE && eventmode != EFONTCAT_MODE
6619 && eventmode != CATMOVE_MODE && eventmode != CATTEXT_MODE) {
6620
6621 draw_grids();
6622
6623 /* Determine the transformation matrix for the topmost object */
6624 /* and draw the hierarchy above the current edit object (if */
6625 /* "edit-in-place" is selected). */
6626
6627 if (areawin->editinplace == True) {
6628 if (areawin->stack != NULL) {
6629 pushlistptr lastlist = NULL, thislist;
6630 Matrix mtmp;
6631
6632 UPushCTM(); /* save our current state */
6633
6634 /* It's easiest if we first push the current page onto the stack, */
6635 /* then we don't need to treat the top-level page separately. We */
6636 /* pop it at the end. */
6637 push_stack(&areawin->stack, areawin->topinstance, NULL);
6638
6639 thislist = areawin->stack;
6640
6641 while ((thislist != NULL) &&
6642 (is_library(thislist->thisinst->thisobject) < 0)) {
6643
6644 /* Invert the transformation matrix of the instance on the stack */
6645 /* to get the proper transformation matrix of the drawing one */
6646 /* up in the hierarchy. */
6647
6648 UResetCTM(&mtmp);
6649 UPreMultCTM(&mtmp, thislist->thisinst->position,
6650 thislist->thisinst->scale, thislist->thisinst->rotation);
6651 InvertCTM(&mtmp);
6652 UPreMultCTMbyMat(DCTM, &mtmp);
6653
6654 lastlist = thislist;
6655 thislist = thislist->next;
6656
6657 /* The following will be true for moves between schematics and symbols */
6658 if ((thislist != NULL) && (thislist->thisinst->thisobject->symschem
6659 == lastlist->thisinst->thisobject))
6660 break;
6661 }
6662
6663 if (lastlist != NULL) {
6664 pushlistptr stack = NULL;
6665 SetForeground(dpy, areawin->gc, OFFBUTTONCOLOR);
6666 UDrawObject(lastlist->thisinst, SINGLE, DOFORALL,
6667 xobjs.pagelist[areawin->page]->wirewidth, &stack);
6668 /* This shouldn't happen, but just in case. . . */
6669 if (stack) free_stack(&stack);
6670 }
6671
6672 pop_stack(&areawin->stack); /* restore the original stack state */
6673 UPopCTM(); /* restore the original matrix state */
6674 }
6675 }
6676 }
6677
6678 /* draw all of the elements on the screen */
6679
6680 SetForeground(dpy, areawin->gc, FOREGROUND);
6681
6682 /* Initialize hierstack */
6683 if (areawin->hierstack) free_stack(&areawin->hierstack);
6684 UDrawObject(areawin->topinstance, TOPLEVEL, FOREGROUND,
6685 xobjs.pagelist[areawin->page]->wirewidth, &areawin->hierstack);
6686 if (areawin->hierstack) free_stack(&areawin->hierstack);
6687
6688 #ifdef HAVE_CAIRO
6689 if (areawin->fixed_pixmap)
6690 cairo_pattern_destroy(areawin->fixed_pixmap);
6691 areawin->fixed_pixmap = cairo_pop_group(areawin->cr);
6692 #else /* HAVE_CAIRO */
6693 areawin->window = old_window;
6694 #endif /* HAVE_CAIRO */
6695 areawin->redraw_ongoing = old_ongoing;
6696 }
6697
6698 /*--------------------------------------*/
6699 /* Draw the primary graphics window */
6700 /*--------------------------------------*/
6701
drawwindow(xcWidget w,caddr_t clientdata,caddr_t calldata)6702 void drawwindow(xcWidget w, caddr_t clientdata, caddr_t calldata)
6703 {
6704 XEvent discard;
6705 xcDrawType redrawtype = xcDRAW_EDIT;
6706 UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
6707
6708 if (areawin->area == NULL) return;
6709 if (!xcIsRealized(areawin->area)) return;
6710 if (xobjs.suspend >= 0) return;
6711
6712 #ifndef HAVE_CAIRO
6713 /* Make sure a fixed pixmap exists */
6714 if (!areawin->fixed_pixmap) {
6715 areawin->fixed_pixmap = XCreatePixmap(dpy, areawin->window,
6716 areawin->width, areawin->height,
6717 DefaultDepthOfScreen(xcScreen(areawin->area)));
6718 }
6719 #endif /* !HAVE_CAIRO */
6720
6721 /* Sanity check---specifically to track down an error */
6722 if ((areawin->selects == 1) && *(areawin->selectlist) >= topobject->parts) {
6723 Wprintf("Internal error!");
6724 areawin->selects = 0;
6725 unselect_all();
6726 }
6727
6728 if (areawin->redraw_needed)
6729 redrawtype = xcREDRAW_FORCED;
6730
6731 switch (eventmode) {
6732 case ARC_MODE: case EARC_MODE:
6733 arc_mode_draw(redrawtype, TOARC(EDITPART));
6734 break;
6735 case SPLINE_MODE: case ESPLINE_MODE:
6736 spline_mode_draw(redrawtype, TOSPLINE(EDITPART));
6737 break;
6738 case BOX_MODE: case EPOLY_MODE: case WIRE_MODE:
6739 poly_mode_draw(redrawtype, TOPOLY(EDITPART));
6740 break;
6741 case EPATH_MODE:
6742 path_mode_draw(redrawtype, TOPATH(EDITPART));
6743 break;
6744 case TEXT_MODE: case CATTEXT_MODE: case ETEXT_MODE:
6745 text_mode_draw(redrawtype, TOLABEL(EDITPART));
6746 break;
6747 case SELAREA_MODE:
6748 selarea_mode_draw(redrawtype, NULL);
6749 break;
6750 case RESCALE_MODE:
6751 rescale_mode_draw(redrawtype, NULL);
6752 break;
6753 case CATMOVE_MODE: case MOVE_MODE: case COPY_MODE:
6754 move_mode_draw(redrawtype, NULL);
6755 break;
6756 case ASSOC_MODE: case EINST_MODE: case FONTCAT_MODE: case EFONTCAT_MODE:
6757 case PAN_MODE: case NORMAL_MODE: case UNDO_MODE: case CATALOG_MODE:
6758 normal_mode_draw(redrawtype, NULL);
6759 }
6760
6761 /* flush out multiple expose/resize events from the event queue */
6762 while (XCheckWindowEvent(dpy, areawin->window, ExposureMask, &discard));
6763
6764 /* end by restoring graphics state */
6765 SetForeground(dpy, areawin->gc, areawin->gccolor);
6766
6767 areawin->redraw_needed = False;
6768 }
6769
6770 /*----------------------------------------------------------------------*/
6771 /* Draw the current window (areawin). Check if other windows contain */
6772 /* the same object or one of its ancestors. If so, redraw them, too. */
6773 /*----------------------------------------------------------------------*/
6774
drawarea(xcWidget w,caddr_t clientdata,caddr_t calldata)6775 void drawarea(xcWidget w, caddr_t clientdata, caddr_t calldata)
6776 {
6777 XCWindowDataPtr thiswin, focuswin;
6778
6779 if (xobjs.suspend >= 0) {
6780 if (xobjs.suspend == 0)
6781 xobjs.suspend = 1; /* Mark that a refresh is pending */
6782 return;
6783 }
6784
6785 focuswin = areawin;
6786
6787 for (thiswin = xobjs.windowlist; thiswin != NULL; thiswin = thiswin->next) {
6788 if (thiswin == focuswin) continue;
6789
6790 /* Note: need to check ancestry here, not just blindly redraw */
6791 /* all the windows all the time. */
6792 areawin = thiswin;
6793
6794 #ifdef HAVE_CAIRO
6795 /* Don't respond to an expose event if the graphics context */
6796 /* has not yet been created. */
6797 if (areawin->cr != NULL)
6798 #endif
6799 drawwindow(NULL, NULL, NULL);
6800 }
6801 areawin = focuswin;
6802 drawwindow(w, clientdata, calldata);
6803 }
6804
6805 /*-------------------------------------------------------------------------*/
6806