1 /*-------------------------------------------------------------------------*/
2 /* libraries.c --- xcircuit routines for the builtin and user libraries    */
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 <string.h>
12 #include <stdlib.h>
13 
14 #ifndef XC_WIN32
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #endif
18 #ifdef TCL_WRAPPER
19 #include <tk.h>
20 #else
21 #ifndef XC_WIN32
22 #include "Xw/Xw.h"
23 #endif
24 #endif
25 
26 #include <math.h>
27 
28 /*-------------------------------------------------------------------------*/
29 /* Local includes							   */
30 /*-------------------------------------------------------------------------*/
31 
32 #include "colordefs.h"
33 #include "xcircuit.h"
34 
35 /*----------------------------------------------------------------------*/
36 /* Function prototype declarations                                      */
37 /*----------------------------------------------------------------------*/
38 #include "prototypes.h"
39 
40 /*-------------------------------------------------------------------------*/
41 /* Global Variable definitions						   */
42 /*-------------------------------------------------------------------------*/
43 
44 extern Display	*dpy;   /* Works well to make this globally accessible */
45 extern Cursor	appcursors[NUM_CURSORS];
46 extern Globaldata xobjs;
47 extern XCWindowData *areawin;
48 extern char _STR[150];
49 extern short fontcount;
50 extern fontinfo *fonts;
51 extern Boolean was_preselected;
52 extern int number_colors;
53 extern colorindex *colorlist;
54 
55 /*---------------------------------------------------------*/
56 /* Find the Helvetica font for use in labeling the objects */
57 /*---------------------------------------------------------*/
58 
findhelvetica()59 short findhelvetica()
60 {
61    short fval;
62 
63    if (fontcount == 0) loadfontfile("Helvetica");
64 
65    for (fval = 0; fval < fontcount; fval++)
66       if (!strcmp(fonts[fval].psname, "Helvetica"))
67 	 break;
68 
69    /* If not there, use the first Helvetica font */
70 
71    if (fval == fontcount) {
72       for (fval = 0; fval < fontcount; fval++)
73          if (!strcmp(fonts[fval].family, "Helvetica"))
74 	    break;
75    }
76 
77    /* If still not there, use the first non-Symbol font */
78    /* If this doesn't work, then the libraries are probably misplaced. . .*/
79 
80    if (fval == fontcount) {
81       for (fval = 0; fval < fontcount; fval++)
82          if (strcmp(fonts[fval].family, "Symbol"))
83 	    break;
84    }
85 
86    return fval;
87 }
88 
89 /*-------------------------------------------*/
90 /* Return to drawing window from the library */
91 /*-------------------------------------------*/
92 
catreturn()93 void catreturn()
94 {
95    /* Pop the object being edited from the push stack. */
96 
97    popobject(NULL, (pointertype)1, NULL);
98 }
99 
100 /*------------------------------------------------------*/
101 /* Find page number from cursor position		*/
102 /* Mode = 0:  Look for exact corresponding page number  */
103 /*   and return -1 if out-of-bounds			*/
104 /* Mode = 1:  Look for position between pages, return	*/
105 /*   page number of page to the right.  		*/
106 /*------------------------------------------------------*/
107 
pageposition(short libmode,int x,int y,int mode)108 int pageposition(short libmode, int x, int y, int mode)
109 {
110    int xin, yin, bpage, pages;
111    int gxsize, gysize, xdel, ydel;
112 
113    pages = (libmode == PAGELIB) ? xobjs.pages : xobjs.numlibs;
114    computespacing(libmode, &gxsize, &gysize, &xdel, &ydel);
115    window_to_user(x, y, &areawin->save);
116 
117    if (mode == 0) {	/* On-page */
118       if (areawin->save.x >= 0 && areawin->save.y <= 0) {
119          xin = areawin->save.x / xdel;
120          yin = areawin->save.y / ydel;
121          if (xin < gxsize && yin > -gysize) {
122             bpage = (xin % gxsize) - (yin * gxsize);
123             if (bpage < pages)
124 	       return bpage;
125          }
126       }
127       return -1;
128    }
129    else {		/* Between-pages */
130       xin = (areawin->save.x + (xdel >> 1)) / xdel;
131       if (xin > gxsize) xin = gxsize;
132       if (xin < 0) xin = 0;
133       yin = areawin->save.y  / ydel;
134       if (yin > 0) yin = 0;
135       if (yin < -gysize) yin = -gysize;
136       bpage = (xin % (gxsize + 1)) + 1 - (yin * gxsize);
137       if (bpage > pages + 1) bpage = pages + 1;
138       return bpage;
139    }
140 }
141 
142 /*------------------------------------------------------*/
143 /* Find the number of other pages linked to the		*/
144 /* indicated page (having the same filename, and	*/
145 /* ignoring empty pages).  result is the total number	*/
146 /* of pages in the output file.				*/
147 /*------------------------------------------------------*/
148 
pagelinks(int page)149 short pagelinks(int page)
150 {
151    int i;
152    short count = 0;
153 
154    for (i = 0; i < xobjs.pages; i++)
155       if (xobjs.pagelist[i]->pageinst != NULL)
156 	 if (xobjs.pagelist[i]->pageinst->thisobject->parts > 0)
157 	    if ((i == page) || (xobjs.pagelist[i]->filename &&
158 			xobjs.pagelist[page]->filename &&
159 			(!filecmp(xobjs.pagelist[i]->filename,
160 			xobjs.pagelist[page]->filename))))
161 	       count++;
162 
163    return count;
164 }
165 
166 /*------------------------------------------------------*/
167 /* This is an expanded version of pagelinks() (above),	*/
168 /* to deal with the separate issues of independent top-	*/
169 /* level schematics and subcircuits.  For the indicated	*/
170 /* page, return a list of pages depending on the mode:	*/
171 /*							*/
172 /* mode = INDEPENDENT: independent top-level pages	*/
173 /* mode = DEPENDENT: dependent pages (subcircuits)	*/
174 /* mode = PAGE_DEPEND: subcircuits of the current page,	*/
175 /* mode = LINKED_PAGES: subcircuits of the current	*/
176 /*	page, including parameter links			*/
177 /* mode = TOTAL_PAGES: independent pages + subcircuits  */
178 /* mode = ALL_PAGES: all pages in xcircuit		*/
179 /*							*/
180 /* The list is the size of the number of pages, and	*/
181 /* entries corresponding to the requested mode are set	*/
182 /* nonzero (the actual number indicates the number of	*/
183 /* references to the page, which may or may not be	*/
184 /* useful to know).					*/
185 /*							*/
186 /* It is the responsibility of the calling routine to	*/
187 /* free the memory allocated for the returned list.	*/
188 /*------------------------------------------------------*/
189 
pagetotals(int page,short mode)190 short *pagetotals(int page, short mode)
191 {
192    int i;
193    short *counts, *icount;
194 
195    if (xobjs.pagelist[page]->pageinst == NULL) return NULL;
196 
197    counts = (short *)malloc(xobjs.pages * sizeof(short));
198    icount = (short *)malloc(xobjs.pages * sizeof(short));
199    for (i = 0; i < xobjs.pages; i++) {
200       *(counts + i) = 0;
201       *(icount + i) = 0;
202    }
203 
204    /* Find all the subcircuits of this page */
205 
206    if (mode != ALL_PAGES)
207       findsubschems(page, xobjs.pagelist[page]->pageinst->thisobject,
208 		0, counts, (mode == LINKED_PAGES) ? TRUE : FALSE);
209 
210    /* Check independent entries (top-level pages which are not	*/
211    /* subcircuits of another page, but have the same filename	*/
212    /* as the page we started from).  Set the counts entry to -1	*/
213    /* to mark each independent page.				*/
214 
215    if (mode != PAGE_DEPEND)
216       for (i = 0; i < xobjs.pages; i++)
217          if (xobjs.pagelist[i]->pageinst != NULL)
218 	    if (xobjs.pagelist[i]->pageinst->thisobject->parts > 0)
219 	    {
220 	       if (mode == ALL_PAGES)
221 		  (*(counts + i)) = 1;
222 	       else
223 	       {
224 	          if ((i == page) || (xobjs.pagelist[i]->filename
225 			&& xobjs.pagelist[page]->filename
226 			&& (!filecmp(xobjs.pagelist[i]->filename,
227 			xobjs.pagelist[page]->filename))))
228 		     if ((mode == INDEPENDENT) || (*(counts + i) == 0))
229 		        (*(icount + i))++;
230 	       }
231 	    }
232 
233    /* Check other dependent entries (top-level pages which are 	*/
234    /* subcircuits of any independent page).			*/
235 
236    if ((mode == DEPENDENT) || (mode == TOTAL_PAGES) || (mode == LINKED_PAGES))
237    {
238       for (i = 0; i < xobjs.pages; i++)
239 	 if ((i != page) && (*(icount + i) > 0))
240 	    findsubschems(i, xobjs.pagelist[i]->pageinst->thisobject,
241 		0, counts, (mode == LINKED_PAGES) ? TRUE : FALSE);
242    }
243 
244    if (mode == INDEPENDENT)
245    {
246       free((char *)counts);
247       return icount;
248    }
249    else
250    {
251       if ((mode == TOTAL_PAGES) || (mode == LINKED_PAGES)) {
252 	 /* merge dependent and independent */
253 	 for (i = 0; i < xobjs.pages; i++)
254 	    if (*(icount + i) > 0)
255 	       (*(counts + i))++;
256       }
257       free((char *)icount);
258       return counts;
259    }
260 }
261 
262 /*---------------------------------------------------------*/
263 /* Test whether a library instance is a "virtual" instance */
264 /*---------------------------------------------------------*/
265 
is_virtual(objinstptr thisinst)266 Boolean is_virtual(objinstptr thisinst) {
267    int libno;
268    liblistptr ilist;
269 
270    libno = libfindobject(thisinst->thisobject, NULL);
271 
272    for (ilist = xobjs.userlibs[libno].instlist; ilist != NULL; ilist = ilist->next)
273       if ((ilist->thisinst == thisinst) && (ilist->virtual == TRUE))
274 	 return TRUE;
275 
276    return FALSE;
277 }
278 
279 /*------------------------------------------------------*/
280 /* Test whether an object is a page, and return the	*/
281 /* page number if it is.  Otherwise, return -1.		*/
282 /*------------------------------------------------------*/
283 
is_page(objectptr thisobj)284 int is_page(objectptr thisobj)
285 {
286    int i;
287 
288    for (i = 0; i < xobjs.pages; i++)
289       if (xobjs.pagelist[i]->pageinst != NULL)
290          if (xobjs.pagelist[i]->pageinst->thisobject == thisobj) return i;
291 
292    return -1;
293 }
294 
295 /*------------------------------------------------------*/
296 /* Test whether an object is a library, and return the	*/
297 /* library number if it is.  Otherwise, return -1.	*/
298 /*------------------------------------------------------*/
299 
is_library(objectptr thisobj)300 int is_library(objectptr thisobj)
301 {
302    int i;
303 
304    for (i = 0; i < xobjs.numlibs; i++)
305       if (xobjs.libtop[i + LIBRARY]->thisobject == thisobj) return i;
306 
307    return -1;
308 }
309 
310 /*------------------------------------------------------*/
311 /* Check for library name (string).  Because XCircuit   */
312 /* generates the text "Library: <filename>" for		*/
313 /* library names, we also check against <filename>	*/
314 /* only in these names (this library name syntax is	*/
315 /* deprecated, but the check is retained for backwards	*/
316 /* compatibility).					*/
317 /*							*/
318 /* If no library matches the given name, return -1.	*/
319 /*------------------------------------------------------*/
320 
NameToLibrary(char * libname)321 int NameToLibrary(char *libname)
322 {
323    char *slib;
324    int i;
325 
326    for (i = 0; i < xobjs.numlibs; i++) {
327       slib = xobjs.libtop[i + LIBRARY]->thisobject->name;
328       if (!strcmp(libname, slib)) {
329 	 return i;
330       }
331       else if (!strncmp(slib, "Library: ", 9)) {
332 	 if (!strcmp(libname, slib + 9)) {
333 	    return i;
334          }
335       }
336    }
337    return -1;
338 }
339 
340 /*------------------------------------------------------*/
341 /* Move an object and all of its virtual instances from	*/
342 /* one library to another.				*/
343 /*------------------------------------------------------*/
344 
libmoveobject(objectptr thisobject,int libtarget)345 int libmoveobject(objectptr thisobject, int libtarget)
346 {
347    int j, libsource;
348    liblistptr spec, slast, srch;
349 
350    libsource = libfindobject(thisobject, &j);
351 
352    if (libsource == libtarget) return libsource; /* nothing to do */
353    if (libsource < 0) return libsource;		 /* object not in the library */
354 
355    /* Move the object from library "libsource" to library "libtarget" */
356 
357    xobjs.userlibs[libtarget].library = (objectptr *)
358 		realloc(xobjs.userlibs[libtarget].library,
359 		(xobjs.userlibs[libtarget].number + 1) * sizeof(objectptr));
360 
361    *(xobjs.userlibs[libtarget].library + xobjs.userlibs[libtarget].number) = thisobject;
362    xobjs.userlibs[libtarget].number++;
363 
364    for (; j < xobjs.userlibs[libsource].number; j++)
365       *(xobjs.userlibs[libsource].library + j) =
366 		*(xobjs.userlibs[libsource].library + j + 1);
367       xobjs.userlibs[libsource].number--;
368 
369    /* Move all instances from library "libsource" to library "libtarget" */
370 
371    slast = NULL;
372    for (spec = xobjs.userlibs[libsource].instlist; spec != NULL;) {
373       if (spec->thisinst->thisobject == thisobject) {
374 
375 	 /* Add to end of spec list in target */
376 	 srch = xobjs.userlibs[libtarget].instlist;
377 	 if (srch == NULL)
378 	    xobjs.userlibs[libtarget].instlist = spec;
379 	 else {
380 	    for (; srch->next != NULL; srch = srch->next);
381 	    spec->next = srch->next;
382 	    srch->next = spec;
383 	 }
384 
385 	 if (slast != NULL) {
386 	    slast->next = spec->next;
387 	    spec = slast->next;
388 	 }
389 	 else {
390 	    xobjs.userlibs[libsource].instlist = spec->next;
391 	    spec = xobjs.userlibs[libsource].instlist;
392 	 }
393       }
394       else {
395 	 slast = spec;
396 	 spec = spec->next;
397       }
398    }
399 
400    return libsource;
401 }
402 
403 /*------------------------------------------------------*/
404 /* Determine which library contains the specified	*/
405 /* object.  If found, return the library number, or	*/
406 /* else return -1 if the object was not found in any	*/
407 /* library.  If "partidx" is non-null, fill with the	*/
408 /* integer offset of the object from the beginning of	*/
409 /* the library.						*/
410 /*------------------------------------------------------*/
411 
libfindobject(objectptr thisobject,int * partidx)412 int libfindobject(objectptr thisobject, int *partidx)
413 {
414    int i, j;
415    objectptr libobj;
416 
417    for (i = 0; i < xobjs.numlibs; i++) {
418       for (j = 0; j < xobjs.userlibs[i].number; j++) {
419          libobj = *(xobjs.userlibs[i].library + j);
420 	 if (libobj == thisobject) {
421 	    if (partidx != NULL) *partidx = j;
422 	    return i;
423 	 }
424       }
425    }
426    return -1;
427 }
428 
429 /*------------------------------------------------------*/
430 /* ButtonPress handler during page catalog viewing mode */
431 /*------------------------------------------------------*/
432 
pagecat_op(int op,int x,int y)433 void pagecat_op(int op, int x, int y)
434 {
435    int bpage;
436    short mode;
437 
438    for (mode = 0; mode < LIBRARY; mode++) {
439       if (areawin->topinstance == xobjs.libtop[mode]) break;
440    }
441    if (mode == LIBRARY) return;  /* Something went wrong if this happens */
442 
443    if (op != XCF_Cancel) {
444       if ((bpage = pageposition(mode, x, y, 0)) >= 0) {
445 
446 	 if (eventmode == ASSOC_MODE) {
447 	    if (mode == PAGELIB) {
448 	       /* using changepage() allows use of new page for schematic */
449 	       changepage(bpage);
450 	       /* associate the new schematic */
451 	       schemassoc(topobject, areawin->stack->thisinst->thisobject);
452 	       /* pop back to calling (symbol) page */
453 	       catreturn();
454 	       eventmode = NORMAL_MODE;
455 	    }
456 	    else {
457 	       areawin->lastlibrary = bpage;
458 	       startcatalog(NULL, (pointertype)(LIBRARY + bpage), NULL);
459 	    }
460 	    return;
461          }
462 	 else if (op == XCF_Select) {
463 	    if (mode == PAGELIB)    /* No such method for LIBLIB is defined. */
464 	       select_add_element(OBJINST);
465 	 }
466 	 else if ((op == XCF_Library_Pop) || (op == XCF_Finish)) {
467 
468 	    /* like catreturn(), but don't actually go to the popped page */
469 	    unselect_all();
470 	    eventmode = NORMAL_MODE;
471 	    if (mode == PAGELIB) {
472 	       newpage(bpage);
473 	    }
474 	    else {
475 	       startcatalog(NULL, (pointertype)(LIBRARY + bpage), NULL);
476 	    }
477 	    return;
478 	 }
479       }
480    }
481    else {
482       eventmode = NORMAL_MODE;
483       catreturn();
484    }
485 }
486 
487 /*------------------------------------------------------------------------------*/
488 /* Subroutine to find the correct scale and position of the object instance	*/
489 /* representing an entire page in the page directory.				*/
490 /*------------------------------------------------------------------------------*/
491 
pageinstpos(short mode,short tpage,objinstptr drawinst,int gxsize,int gysize,int xdel,int ydel)492 void pageinstpos(short mode, short tpage, objinstptr drawinst, int gxsize,
493 	int gysize, int xdel, int ydel)
494 {
495    objectptr libobj = drawinst->thisobject;
496    float scalex, scaley;
497 
498    drawinst->position.x = (tpage % gxsize) * xdel;
499    drawinst->position.y = -(tpage / gxsize + 1) * ydel;
500 
501    /* center the object on its page bounding box */
502 
503    if (drawinst->bbox.width == 0 || drawinst->bbox.height == 0) {
504       drawinst->scale = 0.45 * libobj->viewscale;
505       drawinst->position.x += 0.05 * xdel - libobj->pcorner.x * drawinst->scale;
506       drawinst->position.y += 0.05 * ydel - libobj->pcorner.y * drawinst->scale;
507    }
508    else {
509       scalex = (0.9 * xdel) / drawinst->bbox.width;
510       scaley = (0.9 * ydel) / drawinst->bbox.height;
511       if (scalex > scaley) {
512          drawinst->scale = scaley;
513 	 drawinst->position.x -= (drawinst->bbox.lowerleft.x * scaley);
514          drawinst->position.x += (xdel - (drawinst->bbox.width * scaley)) / 2;
515          drawinst->position.y += 0.05 * ydel - drawinst->bbox.lowerleft.y
516 			* drawinst->scale;
517       }
518       else {
519          drawinst->scale = scalex;
520 	 drawinst->position.y -= (drawinst->bbox.lowerleft.y * scalex);
521          drawinst->position.y += (ydel - (drawinst->bbox.height * scalex)) / 2;
522          drawinst->position.x += 0.05 * xdel - drawinst->bbox.lowerleft.x
523 			* drawinst->scale;
524       }
525    }
526 }
527 
528 /*--------------------------------------------------------------*/
529 /* Make a new instance for inserting into the page directory	*/
530 /*--------------------------------------------------------------*/
531 
newpageinst(objectptr pageobj)532 objinstptr newpageinst(objectptr pageobj)
533 {
534    objinstptr newinst = (objinstptr) malloc(sizeof(objinst));
535    instancedefaults(newinst, pageobj, 0, 0);
536    newinst->type = OBJINST;
537    newinst->color = DEFAULTCOLOR;
538    newinst->style = NORMAL;	/* Do not scale linewidth with page scale */
539    return newinst;
540 }
541 
542 /*-----------------------------------------------------------*/
543 /* Find spacing of objects for pages in the page directories */
544 /*-----------------------------------------------------------*/
545 
computespacing(short mode,int * gxsize,int * gysize,int * xdel,int * ydel)546 void computespacing(short mode, int *gxsize, int *gysize, int *xdel, int *ydel)
547 {
548    int pages = (mode == PAGELIB) ? xobjs.pages : xobjs.numlibs;
549 
550    *gxsize = (int)sqrt((double)pages) + 1;
551    *gysize = 1 + pages / (*gxsize);
552 
553    /* 0.5 is the default vscale;  g#size is the number of pages per line */
554 
555    *xdel = areawin->width / (0.5 * (*gxsize));
556    *ydel = areawin->height / (0.5 * (*gysize));
557 }
558 
559 /*-------------------------------------------------------------------*/
560 /* Draw the catalog of page ordering or the library master directory */
561 /*-------------------------------------------------------------------*/
562 
composepagelib(short mode)563 void composepagelib(short mode)
564 {
565    genericptr *pgen;
566    objinstptr drawinst;
567    objectptr libobj, directory = xobjs.libtop[mode]->thisobject;
568    short i;
569    polyptr *drawbox;
570    labelptr *pagelabel;
571    stringpart *strptr;
572    pointlist pointptr;
573    int margin, xdel, ydel, gxsize, gysize;
574    int pages = (mode == PAGELIB) ? xobjs.pages : xobjs.numlibs;
575    short fval = findhelvetica();
576 
577    /* Like the normal libraries, instances come from a special list, so	 */
578    /* they should not be destroyed, but will be null'd out and retrieved */
579    /* from the list.							 */
580 
581    for (pgen = directory->plist; pgen < directory->plist + directory->parts; pgen++)
582       if (IS_OBJINST(*pgen)) *pgen = NULL;
583 
584    reset(directory, NORMAL);
585 
586    /* generate the list of object instances */
587 
588    directory->plist = (genericptr *)malloc(sizeof(genericptr));
589    directory->parts = 0;
590 
591    computespacing(mode, &gxsize, &gysize, &xdel, &ydel);
592    margin = xdel / 40;	/* margin between pages */
593 
594    for (i = 0; i < pages; i++) {
595       drawinst = (mode == PAGELIB) ? xobjs.pagelist[i]->pageinst :
596 		xobjs.libtop[i + LIBRARY];
597       if (drawinst != NULL) {
598 	 libobj = drawinst->thisobject;
599 
600 	 /* This is a stop-gap measure. . . should be recalculating the bounds of */
601 	 /* the instance on every action, not just before arranging the library.  */
602 	 drawinst->bbox.lowerleft.x = libobj->bbox.lowerleft.x;
603 	 drawinst->bbox.lowerleft.y = libobj->bbox.lowerleft.y;
604 	 drawinst->bbox.width = libobj->bbox.width;
605 	 drawinst->bbox.height = libobj->bbox.height;
606 	 /* End stop-gap measure */
607 
608 	 PLIST_INCR(directory);
609 	 *(directory->plist + directory->parts) = (genericptr)drawinst;
610 	 directory->parts++;
611          pageinstpos(mode, i, drawinst, gxsize, gysize, xdel, ydel);
612       }
613 
614       /* separate pages (including empty ones) with bounding boxes */
615 
616       NEW_POLY(drawbox, directory);
617       (*drawbox)->color = LOCALPINCOLOR;   /* default red */
618       (*drawbox)->style = NORMAL;      	   /* CLOSED */
619       (*drawbox)->width = 1.0;
620       (*drawbox)->number = 4;
621       (*drawbox)->points = (pointlist) malloc(4 * sizeof(XPoint));
622       (*drawbox)->passed = NULL;
623       (*drawbox)->cycle = NULL;
624       pointptr = (*drawbox)->points;
625       pointptr->x = (i % gxsize) * xdel + margin;
626       pointptr->y = -(i / gxsize) * ydel - margin;
627       pointptr = (*drawbox)->points + 1;
628       pointptr->x = ((i % gxsize) + 1) * xdel - margin;
629       pointptr->y = -(i / gxsize) * ydel - margin;
630       pointptr = (*drawbox)->points + 2;
631       pointptr->x = ((i % gxsize) + 1) * xdel - margin;
632       pointptr->y = -((i / gxsize) + 1) * ydel + margin;
633       pointptr = (*drawbox)->points + 3;
634       pointptr->x = (i % gxsize) * xdel + margin;
635       pointptr->y = -((i / gxsize) + 1) * ydel + margin;
636 
637       /* each page gets its name printed at the bottom */
638 
639       if (drawinst != NULL) {
640 	 NEW_LABEL(pagelabel, directory);
641 	 labeldefaults(*pagelabel, False, (pointptr->x + (pointptr-1)->x) / 2,
642 			pointptr->y - 5);
643 	 (*pagelabel)->color = DEFAULTCOLOR;
644 	 (*pagelabel)->scale = 0.75;
645 	 (*pagelabel)->string->data.font = fval;
646          (*pagelabel)->passed = NULL;
647 	 strptr = makesegment(&((*pagelabel)->string), NULL);
648 	 strptr->type = TEXT_STRING;
649 	 strptr->data.string = (char *) malloc(1 + strlen(libobj->name));
650 	 strcpy(strptr->data.string, libobj->name);
651 	 (*pagelabel)->anchor = TOP | NOTBOTTOM | NOTLEFT;
652       }
653    }
654 
655    /* calculate a bounding box for this display */
656    /* and center it in its window */
657 
658    calcbbox(xobjs.libtop[mode]);
659    centerview(xobjs.libtop[mode]);
660 }
661 
662 /*------------------------------------------------------------*/
663 /* Update the page or library directory based on new bounding */
664 /* box information for the page or library passed in "tpage". */
665 /*------------------------------------------------------------*/
666 
updatepagelib(short mode,short tpage)667 void updatepagelib(short mode, short tpage)
668 {
669    objectptr compobj, libinst = xobjs.libtop[mode]->thisobject;
670    objinstptr pinst;
671    genericptr *gelem;
672    int i, xdel, ydel, gxsize, gysize, lpage;
673 
674    /* lpage is the number of the page as found on the directory page */
675    lpage = (mode == PAGELIB) ? tpage : tpage - LIBRARY;
676    compobj = (mode == PAGELIB) ? xobjs.pagelist[tpage]->pageinst->thisobject
677 		: xobjs.libtop[tpage]->thisobject;
678 
679    computespacing(mode, &gxsize, &gysize, &xdel, &ydel);
680 
681    for (i = 0; i < libinst->parts; i++) {
682       gelem = libinst->plist + i;
683       if (IS_OBJINST(*gelem)) {
684 	 pinst = TOOBJINST(gelem);
685          if (pinst->thisobject == compobj) {
686 	    /* recalculate scale and position of the object instance */
687             pageinstpos(mode, lpage, pinst, gxsize, gysize, xdel, ydel);
688 	    break;
689 	 }
690       }
691    }
692 
693    /* if there is no instance for this page, then recompose the whole library */
694 
695    if (i == libinst->parts) composelib(mode);
696 }
697 
698 /*----------------------*/
699 /* Rearrange pages	*/
700 /*----------------------*/
701 
pagecatmove(int x,int y)702 void pagecatmove(int x, int y)
703 {
704    int bpage;
705    objinstptr exchobj;
706    Pagedata *ipage, **testpage, **tpage2;
707 
708    if (areawin->selects == 0) return;
709    else if (areawin->selects > 2) {
710       Wprintf("Select maximum of two objects.");
711       return;
712    }
713 
714    /* Get the page corresponding to the first selected object */
715 
716    exchobj = SELTOOBJINST(areawin->selectlist);
717    for (testpage = xobjs.pagelist; testpage < xobjs.pagelist + xobjs.pages; testpage++)
718       if (*testpage != NULL && (*testpage)->pageinst == exchobj)
719 	 break;
720 
721    /* If two objects are selected, then exchange their order */
722 
723    if (areawin->selects == 2) {
724       exchobj = SELTOOBJINST(areawin->selectlist + 1);
725       for (tpage2 = xobjs.pagelist; tpage2 < xobjs.pagelist + xobjs.pages; tpage2++)
726 	 if (*tpage2 != NULL && (*tpage2)->pageinst == exchobj)
727 	    break;
728 
729       ipage = *testpage;
730       *testpage = *tpage2;
731       *tpage2 = ipage;
732    }
733 
734    /* If one object selected; find place to put from cursor position */
735 
736    else if ((bpage = pageposition(PAGELIB, x, y, 1)) >= 0) {
737       int k, epage;
738       Pagedata *eptr;
739 
740       /* Find page number of the original page */
741 
742       epage = (int)(testpage - xobjs.pagelist);
743       eptr = *(xobjs.pagelist + epage);
744 
745       /* move page (epage) to position between current pages */
746       /* (bpage - 2) and (bpage - 1) by shifting pointers.   */
747 
748       if ((bpage - 1) < epage) {
749 	 for (k = epage - 1; k >= bpage - 1; k--) {
750 	    *(xobjs.pagelist + k + 1) = *(xobjs.pagelist + k);
751 	    renamepage(k + 1);
752 	 }
753 	 *(xobjs.pagelist + bpage - 1) = eptr;
754 	 renamepage(bpage - 1);
755       }
756       else if ((bpage - 2) > epage) {
757 	 for (k = epage + 1; k <= bpage - 2; k++) {
758 	    *(xobjs.pagelist + k - 1) = *(xobjs.pagelist + k);
759 	    renamepage(k - 1);
760 	 }
761 	 *(xobjs.pagelist + bpage - 2) = eptr;
762 	 renamepage(bpage - 2);
763       }
764    }
765 
766    unselect_all();
767    composelib(PAGELIB);
768    drawarea(NULL, NULL, NULL);
769 }
770 
771 /*-----------------------------------------*/
772 /* Draw the catalog of predefined elements */
773 /*-----------------------------------------*/
774 
composelib(short mode)775 void composelib(short mode)
776 {
777    genericptr *pgen;
778    objinstptr drawinst;
779    labelptr *drawname;
780    objectptr libobj, libpage = xobjs.libtop[mode]->thisobject;
781    liblistptr spec;
782    int xpos = 0, ypos = areawin->height << 1;
783    int nypos = 220, nxpos;
784    short fval;
785    short llx, lly, urx, ury, width, height, xcenter;
786 
787    int totalarea, targetwidth;
788    double scale, savescale;
789    short savemode;
790    XPoint savepos;
791 
792    /* Also make composelib() a wrapper for composepagelib() */
793    if ((mode > FONTLIB) && (mode < LIBRARY)) {
794       composepagelib(mode);
795       return;
796    }
797 
798    /* The instances on the library page come from the library's		*/
799    /* "instlist".  So that we don't destroy the actual instance when we	*/
800    /* call reset(), we find the pointer to the instance and NULL it.	*/
801 
802    for (pgen = libpage->plist; pgen < libpage->plist + libpage->parts; pgen++)
803       if (IS_OBJINST(*pgen)) *pgen = NULL;
804 
805    /* Before resetting, save the position and scale.  We will restore	*/
806    /* them at the end.							*/
807 
808    savepos = libpage->pcorner;
809    savescale = libpage->viewscale;
810    reset(libpage, NORMAL);
811 
812    /* Return if library defines no objects or virtual instances */
813 
814    if (xobjs.userlibs[mode - LIBRARY].instlist == NULL) return;
815 
816    /* Find the Helvetica font for use in labeling the objects */
817 
818    fval = findhelvetica();
819 
820    /* Attempt to produce a library with the same aspect ratio as the	*/
821    /* drawing window.  This is only approximate, and does not take	*/
822    /* into account factors such as the length of the name string.	*/
823 
824    totalarea = 0;
825    for (spec = xobjs.userlibs[mode - LIBRARY].instlist; spec != NULL;
826                 spec = spec->next) {
827       libobj = spec->thisinst->thisobject;
828 
829       /* "Hidden" objects are not drawn */
830       if (libobj->hidden == True) continue;
831 
832       drawinst = spec->thisinst;
833       drawinst->position.x = 0;
834       drawinst->position.y = 0;
835 
836       /* Get the bounding box of the instance in the page's coordinate system */
837       calcinstbbox((genericptr *)(&drawinst), &llx, &lly, &urx, &ury);
838       width = urx - llx;
839       height = ury - lly;
840       width += 30;	/* space padding */
841       height += 30;	/* height padding */
842       if (width < 200) width = 200;	/* minimum box width */
843       if (height < 220) height = 220;	/* minimum box height */
844       totalarea += (width * height);
845    }
846 
847    scale = (double)totalarea / (double)(areawin->width * areawin->height);
848    targetwidth = (int)(sqrt(scale) * (double)areawin->width);
849 
850    /* generate the list of object instances and their labels */
851 
852    savemode = eventmode;
853    eventmode = CATALOG_MODE;
854    for (spec = xobjs.userlibs[mode - LIBRARY].instlist; spec != NULL;
855 		spec = spec->next) {
856       libobj = spec->thisinst->thisobject;
857 
858       /* "Hidden" objects are not drawn */
859       if (libobj->hidden == True) continue;
860 
861       drawinst = spec->thisinst;
862       drawinst->position.x = 0;
863       drawinst->position.y = 0;
864 
865       /* Generate the part;  unlike the usual NEW_OBJINST, the	*/
866       /* instance record isn't allocated.			*/
867 
868       PLIST_INCR(libpage);
869       *(libpage->plist + libpage->parts) = (genericptr)drawinst;
870       libpage->parts++;
871 
872       /* Get the bounding box of the instance in the page's coordinate system */
873       calcinstbbox((genericptr *)(&drawinst), &llx, &lly, &urx, &ury);
874       xcenter = (llx + urx) >> 1;
875       width = urx - llx;
876       height = ury - lly;
877 
878       /* Add an ad-hoc spacing rule of 30 for padding space between objects */
879       width += 30;
880 
881       /* Prepare the object name and determine its width.  Adjust width	*/
882       /* needed for object on the library page if necessary.		*/
883 
884       if (fval < fontcount) {
885 	 stringpart *strptr;
886 	 TextExtents tmpext;
887 
888 	 NEW_LABEL(drawname, libpage);
889 	 labeldefaults(*drawname, False, 0, 0);
890 	 (*drawname)->color = (spec->virtual) ?
891 			OFFBUTTONCOLOR : DEFAULTCOLOR;
892          (*drawname)->scale = 0.75;
893 	 (*drawname)->string->data.font = fval;
894 	 strptr = makesegment(&((*drawname)->string), NULL);
895 	 strptr->type = TEXT_STRING;
896 
897          strptr->data.string = strdup(libobj->name);
898          (*drawname)->anchor = TOP | NOTBOTTOM | NOTLEFT;
899 
900 	 /* If the label is longer than the object width, then	*/
901 	 /* adjust positions accordingly.  Note that as an ad-	*/
902 	 /* hoc spacing rule, a padding of 30 is put between	*/
903 	 /* objects; this padding is reduced to 5 		*/
904 
905 	 tmpext = ULength(*drawname, drawinst, NULL);
906 
907 	 /* Ad-hoc spacing rule is 5 for labels */
908 	 tmpext.width += 5;
909 
910 	 if (tmpext.width > width)
911 	    width = tmpext.width;
912       }
913 
914       /* Minimum allowed width is 200 */
915       if (width < 200) width = 200;
916 
917       /* Determine the area needed on the page to draw the object */
918 
919       nxpos = xpos + width;
920       if ((nxpos > targetwidth) && (xpos > 0)) {
921 	 nxpos -= xpos;
922 	 xpos = 0;
923 	 ypos -= nypos;
924 	 nypos = 200;
925       }
926 
927       if (height > (nypos - 50)) nypos = height + 50;
928 
929       drawinst->position.x = xpos + (width >> 1) - xcenter;
930       drawinst->position.y = ypos - (height + lly);
931       if (height <= 170) drawinst->position.y -= ((170 - height) >> 1);
932       drawinst->color = DEFAULTCOLOR;
933 
934       if (fval < fontcount) {
935          (*drawname)->position.x = xpos + (width >> 1);
936 
937          if (height > 170)
938             (*drawname)->position.y = drawinst->position.y + lly - 10;
939          else
940             (*drawname)->position.y = ypos - 180;
941 
942       }
943       xpos = nxpos;
944    }
945    eventmode = savemode;
946 
947    /* Compute the bounding box of the library page */
948    calcbbox(xobjs.libtop[mode]);
949 
950    /* Update the library directory */
951    updatepagelib(LIBLIB, mode);
952 
953    /* Restore original view position */
954    libpage->pcorner = savepos;
955    libpage->viewscale = savescale;
956 }
957 
958 /*----------------------------------------------------------------*/
959 /* Find any dependencies on an object.				  */
960 /*   Return values:  0 = no dependency, 1 = dependency on page,	  */
961 /*	2 = dependency in another library object.		  */
962 /*   Object/Page with dependency (if any) returned in "compobjp". */
963 /*----------------------------------------------------------------*/
964 
finddepend(objinstptr libobj,objectptr ** compobjp)965 short finddepend(objinstptr libobj, objectptr **compobjp)
966 {
967    genericptr *testobj;
968    short page, i, j;
969    objectptr *compobj;
970 
971    for (i = 0; i < xobjs.numlibs; i++) {
972       for (j = 0; j < xobjs.userlibs[i].number; j++) {
973 	 compobj = xobjs.userlibs[i].library + j;
974          *compobjp = compobj;
975 
976          for (testobj = (*compobj)->plist; testobj < (*compobj)->plist
977 	          + (*compobj)->parts; testobj++) {
978 	    if (IS_OBJINST(*testobj)) {
979 	       if (TOOBJINST(testobj)->thisobject == libobj->thisobject) return 2;
980 	    }
981 	 }
982       }
983    }
984 
985    /* also look in the xobjs.pagelist */
986 
987    for (page = 0; page < xobjs.pages; page++) {
988       if (xobjs.pagelist[page]->pageinst == NULL) continue;
989       compobj = &(xobjs.pagelist[page]->pageinst->thisobject);
990       *compobjp = compobj;
991       for (testobj = (*compobj)->plist; testobj < (*compobj)->plist
992 	       + (*compobj)->parts; testobj++) {
993 	 if (IS_OBJINST(*testobj)) {
994 	    if (TOOBJINST(testobj)->thisobject == libobj->thisobject) return 1;
995 	 }
996       }
997    }
998    return 0;
999 }
1000 
1001 /*--------------------------------------------------------------*/
1002 /* Virtual copy:  Make a separate copy of an object on the same	*/
1003 /* library page as the original, representing an instance of	*/
1004 /* the object with different parameters.  The object must have	*/
1005 /* parameters for this to make sense, so check for parameters	*/
1006 /* before allowing the virtual copy.				*/
1007 /*--------------------------------------------------------------*/
1008 
catvirtualcopy()1009 void catvirtualcopy()
1010 {
1011    short i, *newselect;
1012    objinstptr libobj, libinst;
1013 
1014    if (areawin->selects == 0) return;
1015    else if ((i = is_library(topobject)) < 0) return;
1016 
1017    /* Check for existance of parameters in the object for each */
1018    /* selected instance */
1019 
1020    for (newselect = areawin->selectlist; newselect < areawin->selectlist
1021 	  + areawin->selects; newselect++) {
1022       libobj = SELTOOBJINST(newselect);
1023       libinst = addtoinstlist(i, libobj->thisobject, TRUE);
1024       instcopy(libinst, libobj);
1025       tech_mark_changed(GetObjectTechnology(libobj->thisobject));
1026    }
1027 
1028    clearselects();
1029    composelib(LIBRARY + i);
1030    drawarea(NULL, NULL, NULL);
1031 }
1032 
1033 /*----------------------------------------------------------------*/
1034 /* "Hide" an object (must have a dependency or else it disappears)*/
1035 /*----------------------------------------------------------------*/
1036 
cathide()1037 void cathide()
1038 {
1039    int i;
1040    short *newselect;
1041    objectptr *compobj;
1042    objinstptr libobj;
1043 
1044    if (areawin->selects == 0) return;
1045 
1046    /* Can only hide objects which are instances in other objects; */
1047    /* Otherwise, object would be "lost".			  */
1048 
1049    for (newselect = areawin->selectlist; newselect < areawin->selectlist
1050 	  + areawin->selects; newselect++) {
1051       libobj = SELTOOBJINST(newselect);
1052 
1053       if (finddepend(libobj, &compobj) == 0) {
1054 	 Wprintf("Cannot hide: no dependencies");
1055       }
1056       else { 		/* All's clear to hide. */
1057 	 libobj->thisobject->hidden = True;
1058       }
1059    }
1060 
1061    clearselects();
1062 
1063    if ((i = is_library(topobject)) >= 0) composelib(LIBRARY + i);
1064 
1065    drawarea(NULL, NULL, NULL);
1066 }
1067 
1068 /*----------------------------------------------------------------*/
1069 /* Delete an object from the library if there are no dependencies */
1070 /*----------------------------------------------------------------*/
1071 
catdelete()1072 void catdelete()
1073 {
1074    short *newselect, *libpobjs;
1075    int i;
1076    /* genericptr *testobj, *tobj; (jdk) */
1077    objinstptr libobj;
1078    liblistptr ilist, llist;
1079    objectptr *libpage, *compobj, *tlib, *slib;
1080 
1081    if (areawin->selects == 0) return;
1082 
1083    if ((i = is_library(topobject)) >= 0) {
1084       libpage = xobjs.userlibs[i].library;
1085       libpobjs = &xobjs.userlibs[i].number;
1086    }
1087    else
1088       return;  /* To-do: Should have a mechanism here for deleting pages! */
1089 
1090    for (newselect = areawin->selectlist; newselect < areawin->selectlist
1091 	  + areawin->selects; newselect++) {
1092       libobj = SELTOOBJINST(newselect);
1093 
1094       /* If this is just a "virtual copy", simply remove it from the list */
1095 
1096       llist = NULL;
1097       for (ilist = xobjs.userlibs[i].instlist; ilist != NULL;
1098 		llist = ilist, ilist = ilist->next) {
1099 	 if ((ilist->thisinst == libobj) && (ilist->virtual == TRUE)) {
1100 	    if (llist == NULL)
1101 	       xobjs.userlibs[i].instlist = ilist->next;
1102 	    else
1103 	       llist->next = ilist->next;
1104 	    break;
1105 	 }
1106       }
1107       if (ilist != NULL) {
1108          free(ilist);
1109 	 continue;
1110       }
1111 
1112       /* Cannot delete an object if another object uses an instance of it, */
1113       /* or if the object is used on a page.				   */
1114 
1115       if (finddepend(libobj, &compobj)) {
1116 	 Wprintf("Cannot delete: dependency in \"%s\"", (*compobj)->name);
1117       }
1118       else { 		/* All's clear to delete.		     	   */
1119 
1120          /* Clear the undo stack so that any references to this object	*/
1121 	 /* won't screw up the database (this could be kinder & gentler	*/
1122 	 /* by first checking if there are any references to the object	*/
1123 	 /* in the undo stack. . .					*/
1124 
1125 	 flush_undo_stack();
1126 
1127 	 /* Next, remove the object from the library page. */
1128 
1129 	 for (tlib = libpage; tlib < libpage + *libpobjs; tlib++)
1130 	    if ((*tlib) == libobj->thisobject) {
1131 	       for (slib = tlib; slib < libpage + *libpobjs - 1; slib++)
1132 		  (*slib) = (*(slib + 1));
1133 	       (*libpobjs)--;
1134 	       break;
1135 	    }
1136 
1137 	 /* Next, remove all instances of the object on	the library page. */
1138 
1139          llist = NULL;
1140          for (ilist = xobjs.userlibs[i].instlist; ilist != NULL;
1141 		llist = ilist, ilist = ilist->next) {
1142 	    if (ilist->thisinst->thisobject == libobj->thisobject) {
1143 	       if (llist == NULL) {
1144 	          xobjs.userlibs[i].instlist = ilist->next;
1145 	          free(ilist);
1146 	          if (!(ilist = xobjs.userlibs[i].instlist)) break;
1147 	       }
1148 	       else {
1149 	          llist->next = ilist->next;
1150 	          free(ilist);
1151 	          if (!(ilist = llist)) break;
1152 	       }
1153 	    }
1154 	 }
1155 
1156 	 /* Finally, delete the object (permanent---no undoing this!) */
1157          tech_mark_changed(GetObjectTechnology(libobj->thisobject));
1158 	 reset(libobj->thisobject, DESTROY);
1159       }
1160    }
1161 
1162    clearselects();
1163 
1164    if ((i = is_library(topobject)) >= 0) {
1165       composelib(LIBRARY + i);
1166    }
1167 
1168    drawarea(NULL, NULL, NULL);
1169 }
1170 
1171 /*------------------------------------------------------*/
1172 /* Linked list rearrangement routines			*/
1173 /*------------------------------------------------------*/
1174 
linkedlistswap(liblistptr * spec,int o1,int o2)1175 void linkedlistswap(liblistptr *spec, int o1, int o2)
1176 {
1177    liblistptr s1, s1m, s2, s2m, stmp;
1178    int j;
1179 
1180    if (o1 == o2) return;
1181 
1182    s1m = NULL;
1183    s1 = *spec;
1184    for (j = 0; j < o1; j++) {
1185       s1m = s1;
1186       s1 = s1->next;
1187    }
1188 
1189    s2m = NULL;
1190    s2 = *spec;
1191    for (j = 0; j < o2; j++) {
1192       s2m = s2;
1193       s2 = s2->next;
1194    }
1195 
1196    if (s2m)
1197       s2m->next = s1;
1198    else
1199       *spec = s1;
1200 
1201    if (s1m)
1202       s1m->next = s2;
1203    else
1204       *spec = s2;
1205 
1206    stmp = s1->next;
1207    s1->next = s2->next;
1208    s2->next = stmp;
1209 }
1210 
1211 /*------------------------------------------------------*/
1212 
linkedlistinsertafter(liblistptr * spec,int o1,int o2)1213 void linkedlistinsertafter(liblistptr *spec, int o1, int o2)
1214 {
1215   liblistptr s1, s1m, s2; /* , s2m, stmp; (jdk) */
1216    int j;
1217 
1218    if ((o1 == o2) || (o1 == (o2 + 1))) return;
1219 
1220    s1m = NULL;
1221    s1 = *spec;
1222    for (j = 0; j < o1; j++) {
1223       s1m = s1;
1224       s1 = s1->next;
1225    }
1226 
1227    s2 = *spec;
1228    for (j = 0; j < o2; j++)
1229       s2 = s2->next;
1230 
1231    if (s1m)
1232       s1m->next = s1->next;
1233    else
1234       *spec = s1->next;
1235 
1236    if (o2 == -1) {   /* move s1 to front */
1237       s1->next = *spec;
1238       *spec = s1;
1239    }
1240    else {
1241       s1->next = s2->next;
1242       s2->next = s1;
1243    }
1244 }
1245 
1246 /*------------------------------------------------------*/
1247 /* Set the "changed" flag in a library if any object	*/
1248 /* in that library has changed.				*/
1249 /*							*/
1250 /* If "technology" is NULL, check all objects,		*/
1251 /* otherwise only check objects with a matching		*/
1252 /* technology.						*/
1253 /*------------------------------------------------------*/
1254 
tech_set_changes(TechPtr refns)1255 void tech_set_changes(TechPtr refns)
1256 {
1257    TechPtr ns;
1258    int i, j;
1259    objectptr thisobj;
1260 
1261    for (i = 0; i < xobjs.numlibs; i++) {
1262       for (j = 0; j < xobjs.userlibs[i].number; j++) {
1263          thisobj = *(xobjs.userlibs[i].library + j);
1264          if (getchanges(thisobj) > 0) {
1265             ns = GetObjectTechnology(thisobj);
1266 	    if ((refns == NULL) || (refns == ns))
1267 	       ns->flags |= TECH_CHANGED;
1268 	 }
1269       }
1270    }
1271 }
1272 
1273 /*------------------------------------------------------*/
1274 /* Mark a technology as having been modified.		*/
1275 /*------------------------------------------------------*/
1276 
tech_mark_changed(TechPtr ns)1277 void tech_mark_changed(TechPtr ns)
1278 {
1279    if (ns != NULL) ns->flags |= TECH_CHANGED;
1280 }
1281 
1282 /*------------------------------------------------------*/
1283 /* Rearrange objects in the library			*/
1284 /*------------------------------------------------------*/
1285 
catmove(int x,int y)1286 void catmove(int x, int y)
1287 {
1288   int i, j, k, s1, s2, ocentx, ocenty, rangey, l; /* rangex, (jdk) */
1289    liblistptr spec;
1290    objinstptr exchobj, lobj;
1291 
1292    /* make catmove() a wrapper for pagecatmove() */
1293 
1294    if ((i = is_library(topobject)) < 0) {
1295       pagecatmove(x, y);
1296       return;
1297    }
1298 
1299    if (areawin->selects == 0) return;
1300 
1301    /* Add selected object or objects at the cursor position */
1302 
1303    window_to_user(x, y, &areawin->save);
1304 
1305    s2 = -1;
1306    for (j = 0, spec = xobjs.userlibs[i].instlist; spec != NULL;
1307 		spec = spec->next, j++) {
1308       lobj = spec->thisinst;
1309       for (k = 0; k < areawin->selects; k++) {
1310 	 exchobj = SELTOOBJINST(areawin->selectlist + k);
1311 	 if (lobj == exchobj) break;
1312       }
1313       if (k < areawin->selects) continue;	/* ignore items being moved */
1314 
1315       ocentx = lobj->position.x + lobj->bbox.lowerleft.x
1316 	        + (lobj->bbox.width >> 1);
1317       ocenty = lobj->position.y + lobj->bbox.lowerleft.y
1318 	        + (lobj->bbox.height >> 1);
1319       rangey = (lobj->bbox.height > 200) ?
1320 	        (lobj->bbox.height >> 1) : 100;
1321 
1322       if ((areawin->save.y < ocenty + rangey) && (areawin->save.y
1323 	         > ocenty - rangey)) {
1324 	 s2 = j - 1;
1325 	 if (areawin->save.x < ocentx) break;
1326 	 else s2 = j;
1327       }
1328    }
1329    if ((s2 == -1) && (spec == NULL)) {
1330       if (areawin->save.y <
1331 		xobjs.libtop[i + LIBRARY]->thisobject->bbox.lowerleft.y)
1332 	 s2 = j - 1;
1333       else if (areawin->save.y <=
1334 		xobjs.libtop[i + LIBRARY]->thisobject->bbox.lowerleft.y +
1335 		xobjs.libtop[i + LIBRARY]->thisobject->bbox.height) {
1336 	 unselect_all();
1337 	 Wprintf("Could not find appropriate place to insert object");
1338 	 return;
1339       }
1340    }
1341 
1342    /* Find object number s2 (because s2 value may change during insertion) */
1343    if (s2 > -1) {
1344       for (k = 0, spec = xobjs.userlibs[i].instlist; k < s2; spec = spec->next, k++);
1345       lobj = spec->thisinst;
1346    }
1347    else lobj = NULL;
1348 
1349    /* Move items; insert them after item s2 in order selected */
1350 
1351    j = i;
1352    for (k = 0; k < areawin->selects; k++) {
1353 
1354       /* Find number of lobj (may have changed) */
1355 
1356       if (lobj == NULL)
1357 	 s2 = -1;
1358       else {
1359          for (s2 = 0, spec = xobjs.userlibs[i].instlist; spec != NULL;
1360 		spec = spec->next, s2++)
1361 	    if (spec->thisinst == lobj)
1362 	       break;
1363       }
1364 
1365       exchobj = SELTOOBJINST(areawin->selectlist + k);
1366       for (s1 = 0, spec = xobjs.userlibs[i].instlist; spec != NULL;
1367 		spec = spec->next, s1++)
1368          if (spec->thisinst == exchobj)
1369 	    break;
1370 
1371       if (spec == NULL) {
1372 	 /* Object came from another library */
1373          if ((l = libmoveobject(exchobj->thisobject, i)) >= 0) j = l;
1374       }
1375       else {
1376          linkedlistinsertafter(&(xobjs.userlibs[i].instlist), s1, s2);
1377       }
1378    }
1379 
1380    unselect_all();
1381    composelib(LIBRARY + i);
1382    if (j != i) {
1383       composelib(LIBRARY + j);
1384       centerview(xobjs.libtop[LIBRARY + j]);
1385    }
1386 
1387    drawarea(NULL, NULL, NULL);
1388 }
1389 
1390 /*------------------------------------------------------*/
1391 /* Make a duplicate of an object, put in the User	*/
1392 /* Library or the current library (if we're in one).	*/
1393 /*							*/
1394 /* Updated 2/8/2014 for use with technology namespaces:	*/
1395 /* it is most likely that the copied object is to be	*/
1396 /* modified but kept in the same namespace.  So, when	*/
1397 /* renaming the object, prepend "_" to the object name	*/
1398 /* instead of the namespace prefix.			*/
1399 /*------------------------------------------------------*/
1400 
copycat()1401 void copycat()
1402 {
1403    short *newselect;
1404    objectptr *newobj, *curlib, oldobj;
1405    objinstptr libobj;
1406    oparamptr ops, newops;
1407    int i, libnum;
1408    char *cptr;
1409 
1410    libnum = is_library(topobject);
1411    if (libnum < 0) libnum = USERLIB - LIBRARY;  /* default */
1412 
1413    for (newselect = areawin->selectlist; newselect < areawin->selectlist
1414 	  + areawin->selects; newselect++) {
1415 
1416       libobj = SELTOOBJINST(newselect);
1417       oldobj = libobj->thisobject;
1418 
1419       /* generate new entry in user library */
1420 
1421       curlib = (objectptr *) realloc(xobjs.userlibs[libnum].library,
1422 	 (xobjs.userlibs[libnum].number + 1) * sizeof(objectptr));
1423       xobjs.userlibs[libnum].library = curlib;
1424       newobj = xobjs.userlibs[libnum].library + xobjs.userlibs[libnum].number;
1425       *newobj = (objectptr) malloc(sizeof(object));
1426       xobjs.userlibs[libnum].number++;
1427 
1428       /* give the new object a unique name */
1429 
1430       cptr = strstr(oldobj->name, "::");
1431       if (cptr == NULL)
1432 	 sprintf((*newobj)->name, "_%s", oldobj->name);
1433       else {
1434 	 strcpy((*newobj)->name, oldobj->name);
1435 	 sprintf((*newobj)->name + (cptr - oldobj->name) + 2, "_%s", cptr + 2);
1436       }
1437       checkname(*newobj);
1438 
1439       /* copy other object properties */
1440 
1441       (*newobj)->bbox.width = oldobj->bbox.width;
1442       (*newobj)->bbox.height = oldobj->bbox.height;
1443       (*newobj)->bbox.lowerleft.x = oldobj->bbox.lowerleft.x;
1444       (*newobj)->bbox.lowerleft.y = oldobj->bbox.lowerleft.y;
1445       (*newobj)->pcorner.x = oldobj->pcorner.x;
1446       (*newobj)->pcorner.y = oldobj->pcorner.y;
1447       (*newobj)->viewscale = oldobj->viewscale;
1448       /* don't attach the same schematic. . . */
1449       (*newobj)->symschem = NULL;
1450       /* don't copy highlights */
1451       (*newobj)->highlight.netlist = NULL;
1452       (*newobj)->highlight.thisinst = NULL;
1453 
1454       /* Copy the parameter structure */
1455       (*newobj)->params = NULL;
1456       for (ops = oldobj->params; ops != NULL; ops = ops->next) {
1457 	 newops = (oparamptr)malloc(sizeof(oparam));
1458 	 newops->next = (*newobj)->params;
1459 	 newops->key = strdup(ops->key);
1460 	 (*newobj)->params = newops;
1461 	 newops->type = ops->type;
1462 	 newops->which = ops->which;
1463 	 switch (ops->type) {
1464 	    case XC_INT:
1465 	       newops->parameter.ivalue = ops->parameter.ivalue;
1466 	       break;
1467 	    case XC_FLOAT:
1468 	       newops->parameter.fvalue = ops->parameter.fvalue;
1469 	       break;
1470 	    case XC_STRING:
1471 	       newops->parameter.string = stringcopy(ops->parameter.string);
1472 	       break;
1473 	    case XC_EXPR:
1474 	       newops->parameter.expr = strdup(ops->parameter.expr);
1475 	       break;
1476 	 }
1477       }
1478 
1479       (*newobj)->schemtype = oldobj->schemtype;
1480       (*newobj)->netnames = NULL;
1481       (*newobj)->ports = NULL;
1482       (*newobj)->calls = NULL;
1483       (*newobj)->polygons = NULL;
1484       (*newobj)->labels = NULL;
1485       (*newobj)->valid = False;
1486       (*newobj)->traversed = False;
1487       (*newobj)->hidden = False;
1488 
1489       /* copy over all the elements of the original object */
1490 
1491       (*newobj)->parts = 0;
1492       (*newobj)->plist = (genericptr *)malloc(sizeof(genericptr));
1493 
1494       for (i = 0; i < oldobj->parts; i++) {
1495 	 switch(ELEMENTTYPE(*(oldobj->plist + i))) {
1496 	    case(PATH): {
1497 	       register pathptr *npath, cpath = TOPATH(oldobj->plist + i);
1498 
1499 	       NEW_PATH(npath, (*newobj));
1500 	       pathcopy(*npath, cpath);
1501 	       } break;
1502 
1503 	    case(ARC): {
1504 	       register arcptr *narc, carc = TOARC(oldobj->plist + i);
1505 
1506 	       NEW_ARC(narc, (*newobj));
1507 	       arccopy(*narc, carc);
1508                } break;
1509 
1510 	    case(POLYGON): {
1511 	       register polyptr *npoly, cpoly = TOPOLY(oldobj->plist + i);
1512 
1513 	       NEW_POLY(npoly, (*newobj));
1514 	       polycopy(*npoly, cpoly);
1515                } break;
1516 
1517 	    case(SPLINE): {
1518 	       register splineptr *nspl, cspl = TOSPLINE(oldobj->plist + i);
1519 
1520 	       NEW_SPLINE(nspl, (*newobj));
1521 	       splinecopy(*nspl, cspl);
1522       	       } break;
1523 
1524 	    case(LABEL): {
1525 	       register labelptr *nlabel, clabel = TOLABEL(oldobj->plist + i);
1526 
1527 	       NEW_LABEL(nlabel, (*newobj));
1528 	       labelcopy(*nlabel, clabel);
1529                } break;
1530 
1531 	    case(OBJINST): {
1532 	       register objinstptr *ninst, cinst = TOOBJINST(oldobj->plist + i);
1533 
1534 	       NEW_OBJINST(ninst, (*newobj));
1535 	       instcopy(*ninst, cinst);
1536                } break;
1537          }
1538       }
1539    }
1540 
1541    /* make instance for library and measure its bounding box */
1542    addtoinstlist(USERLIB - LIBRARY, *newobj, FALSE);
1543 
1544    composelib(USERLIB);
1545    unselect_all();
1546 
1547    if (areawin->topinstance == xobjs.libtop[USERLIB])
1548       drawarea(NULL, NULL, NULL);
1549    else startcatalog(NULL, (pointertype)USERLIB, NULL);
1550    zoomview(NULL, NULL, NULL);
1551 }
1552 
1553 /*--------------------------------------------------------*/
1554 /* ButtonPress handler during normal catalog viewing mode */
1555 /*--------------------------------------------------------*/
1556 
catalog_op(int op,int x,int y)1557 void catalog_op(int op, int x, int y)
1558 {
1559    short *newselect;
1560    objinstptr *newobject, *libobj;
1561    objectptr libpage = topobject;
1562    short ocentx, ocenty, rangex, rangey, xdiff, ydiff, flag = 0;
1563    XPoint oldpos;
1564 
1565    /* Make catalog_op() a wrapper for pagecat_op() */
1566 
1567    if (is_library(topobject) < 0) {
1568       pagecat_op(op, x, y);
1569       return;
1570    }
1571 
1572    /* If XCF_Cancel was invoked, return without a selection. */
1573 
1574    if (op == XCF_Cancel) {
1575       eventmode = NORMAL_MODE;
1576       catreturn();
1577    }
1578    else {
1579 
1580       window_to_user(x, y, &areawin->save);
1581 
1582       for (libobj = (objinstptr *)topobject->plist; libobj <
1583 		(objinstptr *)topobject->plist + topobject->parts; libobj++) {
1584 	 if (IS_OBJINST(*libobj)) {
1585 
1586  	    ocentx = (*libobj)->position.x + (*libobj)->bbox.lowerleft.x
1587 	        + ((*libobj)->bbox.width >> 1);
1588  	    ocenty = (*libobj)->position.y + (*libobj)->bbox.lowerleft.y
1589 	        + ((*libobj)->bbox.height >> 1);
1590 
1591 	    rangex = ((*libobj)->bbox.width > 200) ?
1592 	        ((*libobj)->bbox.width >> 1) : 100;
1593 	    rangey = ((*libobj)->bbox.height > 200) ?
1594 	        ((*libobj)->bbox.height >> 1) : 100;
1595 
1596 	    if (areawin->save.x > ocentx - rangex && areawin->save.x <
1597 		   ocentx + rangex && areawin->save.y < ocenty + rangey
1598 		   && areawin->save.y > ocenty - rangey) {
1599 
1600 	       /* setup to move object around and draw the selected object */
1601 
1602 	       if (eventmode == ASSOC_MODE) {
1603 
1604 	          /* revert to old page */
1605 		  topobject->viewscale = areawin->vscale;
1606 		  topobject->pcorner = areawin->pcorner;
1607 	          areawin->topinstance = (areawin->stack == NULL) ?
1608 			   xobjs.pagelist[areawin->page]->pageinst
1609 			   : areawin->stack->thisinst;
1610 		  /* associate the new object */
1611 		  schemassoc(topobject, (*libobj)->thisobject);
1612    	          setpage(TRUE);
1613 		  catreturn();
1614 		  eventmode = NORMAL_MODE;
1615 	       }
1616 	       else if ((op == XCF_Library_Pop) || (op == XCF_Library_Copy)) {
1617 		  int saveselects;
1618 
1619 	          /* revert to old page */
1620 
1621 		  topobject->viewscale = areawin->vscale;
1622 		  topobject->pcorner = areawin->pcorner;
1623 	          areawin->topinstance = (areawin->stack == NULL) ?
1624 			   xobjs.pagelist[areawin->page]->pageinst
1625 			   : areawin->stack->thisinst;
1626 
1627 	          /* retrieve drawing window state and set position of object to
1628 	             be correct in reference to that window */
1629 
1630 	          snap(x, y, &oldpos);
1631 
1632 		  saveselects = areawin->selects;
1633 		  areawin->selects = 0;
1634    	          setpage(FALSE);
1635 		  areawin->selects = saveselects;
1636 
1637 	          snap(x, y, &areawin->save);
1638 	          xdiff = areawin->save.x - oldpos.x;
1639 	          ydiff = areawin->save.y - oldpos.y;
1640 
1641                   /* collect all of the selected items */
1642 
1643 	          for (newselect = areawin->selectlist; newselect <
1644 		     areawin->selectlist + areawin->selects; newselect++) {
1645 		     NEW_OBJINST(newobject, topobject);
1646 		     instcopy(*newobject, TOOBJINST(libpage->plist + *newselect));
1647 		     /* color should be recast as current color */
1648 		     (*newobject)->color = areawin->color;
1649 		     /* position modified by (xdiff, ydiff) */
1650 		     (*newobject)->position.x += xdiff;
1651 		     (*newobject)->position.y += ydiff;
1652 
1653 	             u2u_snap(&((*newobject)->position));
1654 		     *newselect = (short)(newobject - (objinstptr *)topobject->plist);
1655 		     if ((*newobject)->thisobject == (*libobj)->thisobject)
1656 		        flag = 1;
1657 	          }
1658 
1659                   /* add final object to the list of object instances */
1660 
1661 	          if (!flag) {
1662 		     NEW_OBJINST(newobject, topobject);
1663 		     instcopy(*newobject, *libobj);
1664 		     (*newobject)->color = areawin->color;
1665                      (*newobject)->position.x = areawin->save.x;
1666 	             (*newobject)->position.y = areawin->save.y;
1667 
1668 	             /* add this object to the list of selected items */
1669 
1670 	             newselect = allocselect();
1671                      *newselect = (short)(newobject - (objinstptr *)topobject->plist);
1672 
1673 	          }
1674 	          if (op == XCF_Library_Copy) {
1675 
1676 		     /* Key "c" pressed for "copy" (default binding) */
1677 
1678                      XDefineCursor(dpy, areawin->window, COPYCURSOR);
1679                      eventmode = COPY_MODE;
1680 #ifndef TCL_WRAPPER
1681                      xcAddEventHandler(areawin->area, PointerMotionMask |
1682 				ButtonMotionMask, False,
1683 				(xcEventHandler)xlib_drag, NULL);
1684 #endif
1685 	          }
1686 	          else {
1687                      eventmode = MOVE_MODE;
1688 		     was_preselected = FALSE;
1689 	 	     register_for_undo(XCF_Library_Pop, UNDO_MORE, areawin->topinstance,
1690 		  		areawin->selectlist, areawin->selects);
1691 	          }
1692 #ifdef TCL_WRAPPER
1693 		  /* fprintf(stderr, "Creating event handler for xctk_drag: "); */
1694 		  /* printeventmode();		*/
1695                   Tk_CreateEventHandler(areawin->area, PointerMotionMask |
1696 			ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
1697 #endif
1698                   catreturn();
1699                }
1700 
1701                /* Select the closest element and stay in the catalog.	   */
1702 	       /* Could just do "select_element" here, but would not cover */
1703 	       /* the entire area in the directory surrounding the object. */
1704 
1705 	       else if (op == XCF_Select) {
1706 		  short newinst = (short)(libobj - (objinstptr *)topobject->plist);
1707 		  /* (ignore this object if it is already in the list of selects) */
1708 		  for (newselect = areawin->selectlist; newselect <
1709 			areawin->selectlist + areawin->selects; newselect++)
1710 		     if (*newselect == newinst) break;
1711 		  if (newselect == areawin->selectlist + areawin->selects) {
1712 		     newselect = allocselect();
1713 		     *newselect = newinst;
1714 		     XcTopSetForeground(SELECTCOLOR);
1715 		     UDrawObject(*libobj, SINGLE, SELECTCOLOR,
1716 				xobjs.pagelist[areawin->page]->wirewidth, NULL);
1717 		  }
1718 	       }
1719 	       break;
1720 	    }
1721 	 }
1722       }
1723    }
1724 }
1725 
1726 /*------------------------------*/
1727 /* Switch to the next catalog	*/
1728 /*------------------------------*/
1729 
changecat()1730 void changecat()
1731 {
1732    int i, j;
1733 
1734    if ((i = is_library(topobject)) < 0) {
1735       if (areawin->lastlibrary >= xobjs.numlibs) areawin->lastlibrary = 0;
1736       j = areawin->lastlibrary;
1737       eventmode = CATALOG_MODE;
1738    }
1739    else {
1740       j = (i + 1) % xobjs.numlibs;
1741       if (j == i) {
1742 	 Wprintf("This is the only library.");
1743 	 return;
1744       }
1745       areawin->lastlibrary = j;
1746    }
1747 
1748    if (eventmode == CATMOVE_MODE)
1749       delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
1750 
1751    startcatalog(NULL, (pointertype)(j + LIBRARY), NULL);
1752 }
1753 
1754 /*--------------------------------------*/
1755 /* Begin catalog viewing mode		*/
1756 /*--------------------------------------*/
1757 
startcatalog(xcWidget w,pointertype libmod,caddr_t nulldata)1758 void startcatalog(xcWidget w, pointertype libmod, caddr_t nulldata)
1759 {
1760    if (xobjs.libtop == NULL) return;	/* No libraries defined */
1761 
1762    if ((xobjs.libtop[libmod]->thisobject == NULL) ||
1763 		(areawin->topinstance == xobjs.libtop[libmod])) return;
1764 
1765    if (libmod == FONTLIB) {
1766       XDefineCursor (dpy, areawin->window, DEFAULTCURSOR);
1767       if (eventmode == TEXT_MODE)
1768          eventmode = FONTCAT_MODE;
1769       else
1770          eventmode = EFONTCAT_MODE;
1771    }
1772    else if (eventmode == ASSOC_MODE) {
1773       XDefineCursor (dpy, areawin->window, DEFAULTCURSOR);
1774    }
1775    else if (libmod == PAGELIB || libmod == LIBLIB) {
1776       XDefineCursor (dpy, areawin->window, DEFAULTCURSOR);
1777       eventmode = CATALOG_MODE;
1778    }
1779    else if (eventmode != CATMOVE_MODE) {
1780       /* Don't know why I put this here---causes xcircuit to redraw	*/
1781       /* the schematic view when switching between library pages.	*/
1782       // finish_op(XCF_Cancel, 0, 0);
1783       eventmode = CATALOG_MODE;
1784    }
1785 
1786    /* Push the current page onto the push stack, unless	we're going */
1787    /* to a library from the library directory or vice versa, or	    */
1788    /* library to library.					    */
1789 
1790    if (!(((is_library(topobject) >= 0)
1791 		|| (areawin->topinstance == xobjs.libtop[LIBLIB])
1792 		|| (areawin->topinstance == xobjs.libtop[PAGELIB]))
1793 		&& libmod >= PAGELIB)) {
1794       push_stack(&areawin->stack, areawin->topinstance, NULL);
1795    }
1796 
1797    /* set library as new object */
1798 
1799    topobject->viewscale = areawin->vscale;
1800    topobject->pcorner = areawin->pcorner;
1801    areawin->topinstance = xobjs.libtop[libmod];
1802 
1803    if (libmod == FONTLIB)
1804       setpage(FALSE);
1805    else {
1806       setpage(TRUE);
1807       transferselects();
1808    }
1809 
1810    /* draw the new screen */
1811 
1812    refresh(NULL, NULL, NULL);
1813 }
1814 
1815 /*-------------------------------------------------------------------------*/
1816