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