1 /***************************************************************************
2 SCED - Schematic Capture Editor
3 JSPICE3 adaptation of Spice3e2 - Copyright (c) Stephen R. Whiteley 1992
4 Copyright 1990 Regents of the University of California. All rights reserved.
5 Authors: 1981 Giles C. Billingsley (parts of KIC layout editor)
6 1992 Stephen R. Whiteley
7 ****************************************************************************/
8
9 /*
10 * SCED displayable object management code.
11 */
12
13 #include "spice.h"
14 #include "sced.h"
15 #include "scedmacs.h"
16
17
18
19 /***********************************************************************
20 *
21 * Routine for deleting objects.
22 *
23 *
24 ***********************************************************************/
25
26 extern char *MenuDELET;
27 extern char *MenuUNDO;
28 #ifdef __STDC__
29 static void do_del(int,struct ka*);
30 #else
31 static void do_del();
32 #endif
33
34
35 void
Del(LookedAhead)36 Del(LookedAhead)
37
38 int *LookedAhead;
39 {
40 struct ks *SQDesc;
41 int Info = SQ_GONE;
42
43 MenuSelect(MenuDELET);
44 if (SelectQHead == NULL) {
45 ShowPrompt("There are no selected objects to delete.");
46 MenuDeselect(MenuDELET);
47 return;
48 }
49 SQComputeBB();
50 /* take care of instance markers */
51 SelectQBB.kaRight += 600;
52 SelectQBB.kaLeft -= 600;
53 SelectQBB.kaTop += 600;
54 SelectQBB.kaBottom -= 600;
55
56 do_del(SQ_GONE,&SelectQBB);
57 MenuDeselect(MenuDELET);
58
59 top:
60 switch (PointLoop(LookedAhead)) {
61 case PL_UND:
62 MenuSelect(MenuUNDO);
63 if (Info == SQ_GONE) Info = SQ_OLDSEL;
64 else Info = SQ_GONE;
65 do_del(Info,&SelectQBB);
66 MenuDeselect(MenuUNDO);
67 case PL_ESC:
68 case PL_PCW:
69 goto top;
70 case PL_CMD:
71 break;
72 }
73 if (Info == SQ_GONE) {
74 for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc) {
75 CDDelete(Parameters.kpCellDesc,SQDesc->ksPointer);
76 }
77 SQClear();
78 SelectQBB.kaLeft = SelectQBB.kaBottom =
79 SelectQBB.kaRight = SelectQBB.kaTop = 0;
80 Parameters.kpModified = True;
81 }
82 else
83 SQComputeBB();
84 }
85
86
87 static void
do_del(Info,BB)88 do_del(Info,BB)
89
90 int Info;
91 struct ka *BB;
92 {
93 struct ks *SQDesc;
94 /*
95 * Change Info field and redisplay.
96 */
97 for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc)
98 SQDesc->ksPointer->oInfo = Info;
99
100 EraseBox(BB);
101 Redisplay(BB);
102 DevUpdate();
103 }
104
105
106
107 /***********************************************************************
108 *
109 * Routines for managing wires.
110 *
111 *
112 ***********************************************************************/
113
114 extern char *MenuWIRES;
115 extern char *MenuUNDO;
116 #ifdef __STDC__
117 static void allocate_wire(int,struct p*,long,struct ka*,struct o**,int);
118 static void record_wire(struct o*);
119 static void zap_last_point(struct p**);
120 static void append_point(long,long,struct p**);
121 static void last_path_point(long*,long*,struct p*);
122 static struct p *allocate_path(long,long);
123 #else
124 static void allocate_wire();
125 static void record_wire();
126 static void zap_last_point();
127 static void append_point();
128 static void last_path_point();
129 static struct p *allocate_path();
130 #endif
131
132
133 void
Wires(LookedAhead)134 Wires(LookedAhead)
135
136 /* Wires command - create wires. */
137 int *LookedAhead;
138 {
139 struct p *Path;
140 struct ka BB,OldBB;
141 struct o *Pointer = NULL,*OldPointer = NULL;
142 long Width;
143 long X,Y,FX,FY;
144 int NumVertices = 0;
145 int ExpectFirstPoint = True;
146 int Modified = 0;
147 int Undo = False;
148
149 MenuSelect(MenuWIRES);
150 Path = NULL;
151
152 Width = 0;
153
154 ShowPrompt("Point to reference points.");
155 loop {
156 switch (PointLoop(LookedAhead)) {
157 case PL_UND:
158 MenuSelect(MenuUNDO);
159 if (NumVertices == 0) {
160 if (OldPointer == NULL) {
161 MenuDeselect(MenuUNDO);
162 goto quit;
163 }
164 if (Undo == False) {
165 OldPointer->oInfo = SQ_GONE;
166 Modified--;
167 Undo = True;
168 }
169 else {
170 OldPointer->oInfo = SQ_NEW;
171 Modified++;
172 Undo = False;
173 }
174 EraseBox(&OldBB);
175 Redisplay(&OldBB);
176 MenuDeselect(MenuUNDO);
177 continue;
178 }
179 if (NumVertices > 1)
180 zap_last_point(&Path);
181 CDDelete(Parameters.kpCellDesc,Pointer);
182 EraseBox(&BB);
183 Redisplay(&BB);
184 FBSetRubberBanding(0);
185 if (NumVertices == 1) {
186 ExpectFirstPoint = True;
187 }
188 else {
189 long tmpX, tmpY;
190
191 allocate_wire(1,Path,Width,&BB,&Pointer,5);
192 ShowWire(DrawingColor,Width,Path);
193 last_path_point(&X,&Y,Path);
194 tmpX = SCursor.kcX;
195 tmpY = SCursor.kcY;
196 SCursor.kcX = X;
197 SCursor.kcY = Y;
198 FBSetRubberBanding('l');
199 SCursor.kcX = tmpX;
200 SCursor.kcY = tmpY;
201 }
202 NumVertices--;
203 MenuDeselect(MenuUNDO);
204 continue;
205 case PL_ESC:
206 if (Not ExpectFirstPoint) {
207 CDDelete(Parameters.kpCellDesc,Pointer);
208 Pointer = NULL;
209 EraseBox(&BB);
210 Redisplay(&BB);
211 }
212 goto quit;
213 case PL_CMD:
214 if (Not ExpectFirstPoint) {
215 if (NumVertices == 1) {
216 CDDelete(Parameters.kpCellDesc,Pointer);
217 }
218 else {
219 Modified++;
220 record_wire(Pointer);
221 }
222 Pointer = NULL;
223 EraseBox(&BB);
224 Redisplay(&BB);
225 }
226 goto quit;
227 }
228 if (ExpectFirstPoint) {
229 NumVertices++;
230 ExpectFirstPoint = False;
231 FX = X = SCursor.kcX;
232 FY = Y = SCursor.kcY;
233 Path = allocate_path(X,Y);
234 allocate_wire(1,Path,Width,&BB,&Pointer,SQ_INCMPLT);
235 ShowWire(DrawingColor,Width,Path);
236 FBSetRubberBanding('l');
237 continue;
238 }
239 if (SCursor.kcX == X And SCursor.kcY == Y) {
240 if (NumVertices == 1) {
241 /* click twice to exit */
242 CDDelete(Parameters.kpCellDesc,Pointer);
243 Pointer = NULL;
244 goto quit;
245 }
246 else {
247 Pointer->oInfo = SQ_NEW;
248 record_wire(OldPointer);
249 OldPointer = Pointer;
250 Pointer = NULL;
251 OldBB = BB;
252 Modified++;
253 FBSetRubberBanding(0);
254 }
255 NumVertices = 0;
256 ExpectFirstPoint = True;
257 EraseBox(&BB);
258 Redisplay(&BB);
259 Undo = False;
260 }
261 else {
262 X = SCursor.kcX;
263 Y = SCursor.kcY;
264 append_point(X,Y,&Path);
265 CDDelete(Parameters.kpCellDesc,Pointer);
266 allocate_wire(1,Path,Width,&BB,&Pointer,SQ_INCMPLT);
267 FBSetRubberBanding(0);
268 FBSetRubberBanding('l');
269 ShowWire(DrawingColor,Width,Path);
270 NumVertices++;
271 }
272 }
273 quit:
274 record_wire(OldPointer);
275 record_wire(Pointer);
276 if (Modified)
277 Parameters.kpModified = True;
278 ErasePrompt();
279 FBSetRubberBanding(0);
280 MenuDeselect(MenuWIRES);
281 }
282
283
284 /* ARGSUSED */
285 void
ShowWire(Color,Width,Path)286 ShowWire(Color,Width,Path)
287
288 /* Display a wire. */
289 int Color;
290 long Width;
291 struct p *Path;
292 {
293 ShowPath(Color,Path,0);
294 }
295
296
297 int
IsManhattan(X1,Y1,X2,Y2)298 IsManhattan(X1,Y1,X2,Y2)
299
300 /* return True if coordinates are Manhattan. */
301 long X1,Y1,X2,Y2;
302 {
303 if (X1 == X2 Or Y1 == Y2)
304 return (True);
305 else return (False);
306 }
307
308
309 static void
allocate_wire(Layer,Path,Width,BB,Pointer,Info)310 allocate_wire(Layer,Path,Width,BB,Pointer,Info)
311
312 /* Create a new wire. */
313 int Layer;
314 struct p *Path;
315 long Width;
316 struct ka *BB;
317 struct o **Pointer;
318 int Info;
319 {
320 if (Not CDMakeWire(Parameters.kpCellDesc,
321 Layer,Width,Path,Pointer)) MallocFailed();
322 (*Pointer)->oInfo = Info;
323 CDStatusInt = CDBB(Parameters.kpCellDesc,*Pointer,
324 &BB->kaLeft,&BB->kaBottom,&BB->kaRight,&BB->kaTop);
325 OversizeBox(BB,Width);
326 }
327
328
329 static void
record_wire(Pointer)330 record_wire(Pointer)
331
332 /* Check the Info field, and delete if conditionally deleted.
333 * Otherwise set Info to SQ_OLD, and add default properties to wire.
334 */
335 struct o *Pointer;
336 {
337 int Info;
338
339 if (Pointer == NULL) return;
340 if (Pointer->oInfo == SQ_GONE)
341 CDDelete(Parameters.kpCellDesc,Pointer);
342 else {
343 Pointer->oInfo = SQ_OLD;
344 if (Pointer->oPrptyList == NULL)
345 AssignWireProperties(Pointer);
346 }
347 }
348
349
350 static void
zap_last_point(Path)351 zap_last_point(Path)
352
353 /* Create a new path with the last point removed. Delete the old path. */
354 struct p **Path;
355 {
356 struct p *OldPair;
357 struct p *NewPair;
358
359 OldPair = *Path;
360 if (OldPair == NULL Or OldPair->pSucc == NULL)
361 return;
362 if ((NewPair = alloc(p)) == NULL)
363 MallocFailed();
364 *Path = NewPair;
365 NewPair->pX = OldPair->pX;
366 NewPair->pY = OldPair->pY;
367 NewPair->pSucc = NULL;
368 OldPair = OldPair->pSucc;
369 while (OldPair->pSucc != NULL) {
370 if ((NewPair->pSucc = alloc(p)) == NULL)
371 MallocFailed();
372 NewPair = NewPair->pSucc;
373 NewPair->pX = OldPair->pX;
374 NewPair->pY = OldPair->pY;
375 NewPair->pSucc = NULL;
376 OldPair = OldPair->pSucc;
377 }
378 }
379
380
381 static void
append_point(X,Y,Path)382 append_point(X,Y,Path)
383
384 /* Create a new path with an added point. Delete the old path. */
385 struct p **Path;
386 long X,Y;
387 {
388 struct p *OldPair;
389 struct p *NewPair;
390
391 if (Path == NULL || *Path == NULL)
392 return;
393 OldPair = *Path;
394 if ((NewPair = alloc(p)) == NULL)
395 MallocFailed();
396 *Path = NewPair;
397 NewPair->pX = OldPair->pX;
398 NewPair->pY = OldPair->pY;
399 NewPair->pSucc = NULL;
400 OldPair = OldPair->pSucc;
401 while (OldPair != NULL) {
402 if ((NewPair->pSucc = alloc(p)) == NULL)
403 MallocFailed();
404 NewPair = NewPair->pSucc;
405 NewPair->pX = OldPair->pX;
406 NewPair->pY = OldPair->pY;
407 NewPair->pSucc = NULL;
408 OldPair = OldPair->pSucc;
409 }
410 /* Append the new reference point */
411 if ((NewPair->pSucc = alloc(p)) == NULL)
412 MallocFailed();
413 NewPair = NewPair->pSucc;
414 NewPair->pX = X;
415 NewPair->pY = Y;
416 NewPair->pSucc = NULL;
417 }
418
419
420 static void
last_path_point(X,Y,Path)421 last_path_point(X,Y,Path)
422
423 /* Return in pointers the last point in path. */
424 long *X,*Y;
425 struct p *Path;
426 {
427 struct p *p;
428
429 for (p = Path; p && p->pSucc; p = p->pSucc) ;
430 if (p) {
431 *X = p->pX;
432 *Y = p->pY;
433 }
434 }
435
436
437 static struct p *
allocate_path(X,Y)438 allocate_path(X,Y)
439
440 /* Allocate an element of a path list. */
441 long X,Y;
442 {
443 struct p *Path;
444
445 if ((Path = alloc(p)) == NULL) MallocFailed();
446 Path->pX = X;
447 Path->pY = Y;
448 Path->pSucc = NULL;
449 return (Path);
450 }
451
452
453 /***********************************************************************
454 *
455 * Routines for rendering line paths and polygons.
456 *
457 *
458 ***********************************************************************/
459
460
461 void
ShowPath(Color,Path,Terminate)462 ShowPath(Color,Path,Terminate)
463
464 /* display a line path. */
465 int Color;
466 struct p *Path;
467 int Terminate; /* If True, the path is closed */
468 {
469 struct p *Pair;
470 long firstX,firstY,X,Y,lastX,lastY;
471
472 Pair = Path;
473 firstX = lastX = Pair->pX;
474 firstY = lastY = Pair->pY;
475 Pair = Pair->pSucc;
476 while (Pair != NULL) {
477 X = Pair->pX;
478 Y = Pair->pY;
479 ShowLine(Color,lastX,lastY,X,Y);
480 lastX = X;
481 lastY = Y;
482 Pair = Pair->pSucc;
483 }
484 if (Terminate)
485 ShowLine(Color,firstX,firstY,lastX,lastY);
486 }
487
488
489 void
ShowPolygon(Color,Path)490 ShowPolygon(Color,Path)
491
492 /* Display a polygon. */
493 int Color;
494 struct p *Path;
495 {
496 struct p *Pair;
497 struct ka Fw, Cw;
498 static long PolygonBuffer[POLYGONBUFSIZE];
499 char oldRedisplay;
500 int i,n,nfine,ncoarse,n2;
501 long X,Y;
502
503
504 if (Color == HighlightingColor) {
505 ShowPath(Color,Path,True);
506 return;
507 }
508
509 for (n = 0,Pair = Path; Pair != NULL; n++,Pair = Pair->pSucc) ;
510 n = Min(MAXPOLYGONVERTICES,n);
511 n2 = n << 1;
512 ncoarse = nfine = n;
513
514 if (Parameters.kpRedisplayControl != FINEVIEWPORTONLY And
515 CurrentAOI.aInCoarse) {
516
517 for (i = 0,Pair = Path; i < n2; i += 2,Pair = Pair->pSucc) {
518 X = Pair->pX;
519 Y = Pair->pY;
520 TPoint(&X,&Y);
521 CoarseLToP(X,Y,PolygonBuffer[i],PolygonBuffer[i+1]);
522 }
523
524 FBPolygonClip(PolygonBuffer,&ncoarse,(struct ka*)&CurrentAOI.aLC);
525 FBPolygon(Color,FILL,PolygonBuffer,ncoarse);
526 }
527
528 if (Parameters.kpRedisplayControl != COARSEVIEWPORTONLY And
529 CurrentAOI.aInFine) {
530
531 for (i = 0,Pair = Path; i < n2; i += 2,Pair = Pair->pSucc) {
532 X = Pair->pX;
533 Y = Pair->pY;
534 TPoint(&X,&Y);
535 FineLToP(X,Y,PolygonBuffer[i],PolygonBuffer[i+1]);
536 }
537
538 FBPolygonClip(PolygonBuffer,&nfine,(struct ka*)&CurrentAOI.aLF);
539 FBPolygon(Color,FILL,PolygonBuffer,nfine);
540 }
541
542 }
543
544
545 /***********************************************************************
546 *
547 * Routines for managing rectangles.
548 *
549 *
550 ***********************************************************************/
551
552 #define ka_copy(BB1,BB2) BB1.kaLeft = BB2->kaLeft; \
553 BB1.kaRight = BB2->kaRight; \
554 BB1.kaBottom = BB2->kaBottom; \
555 BB1.kaTop = BB2->kaTop; \
556 TPoint(&BB1.kaLeft,&BB1.kaBottom); \
557 TPoint(&BB1.kaRight,&BB1.kaTop); \
558 if (BB1.kaLeft > BB1.kaRight) \
559 SwapInts(BB1.kaLeft,BB1.kaRight); \
560 if (BB1.kaBottom > BB1.kaTop) \
561 SwapInts(BB1.kaBottom,BB1.kaTop);
562
563
564 int
InBox(X,Y,AOI)565 InBox(X,Y,AOI)
566
567 /* return True if X,Y in AOI */
568 long X,Y;
569 struct ka *AOI;
570 {
571 if (((AOI->kaLeft <= X And X <= AOI->kaRight) Or
572 (AOI->kaRight <= X And X <= AOI->kaLeft)) And
573 ((AOI->kaBottom <= Y And Y <= AOI->kaTop) Or
574 (AOI->kaTop <= Y And Y <= AOI->kaBottom)))
575 return (True);
576 return (False);
577 }
578
579
580 void
OversizeBox(BB,Delta)581 OversizeBox(BB,Delta)
582
583 /* expand BB by Delta */
584 struct ka *BB;
585 int Delta;
586 {
587 BB->kaTop += Delta;
588 BB->kaRight += Delta;
589 BB->kaBottom -= Delta;
590 BB->kaLeft -= Delta;
591 }
592
593
594 void
OutlineBox(AOI)595 OutlineBox(AOI)
596
597 /* Draw a line box. AOI is pixel coordinates. */
598 struct ka *AOI;
599 {
600 int x1,y1,x2,y2;
601 x1 = AOI->kaLeft;
602 y1 = AOI->kaBottom;
603 x2 = AOI->kaRight;
604 y2 = AOI->kaTop;
605 DevLine(x1,y1,x2,y1);
606 DevLine(x2,y1,x2,y2);
607 DevLine(x2,y2,x1,y2);
608 DevLine(x1,y2,x1,y1);
609 }
610
611
612 void
ShowEmptyBox(Color,boxBB)613 ShowEmptyBox(Color,boxBB)
614
615 /* Draw a line box (window coordinates). */
616 int Color;
617 struct ka *boxBB;
618 {
619 struct ka BB,BB1;
620 int ShowLeft = True;
621 int ShowBottom = True;
622 int ShowRight = True;
623 int ShowTop = True;
624
625 ka_copy(BB,boxBB);
626
627 if (Parameters.kpRedisplayControl != FINEVIEWPORTONLY And
628 CurrentAOI.aInCoarse) {
629
630 CoarseLToP(BB.kaLeft,BB.kaBottom,BB1.kaLeft,BB1.kaBottom);
631 CoarseLToP(BB.kaRight,BB.kaTop,BB1.kaRight,BB1.kaTop);
632
633 if (BB1.kaLeft <= CurrentAOI.aRC And BB1.kaRight >= CurrentAOI.aLC And
634 BB1.kaBottom <= CurrentAOI.aTC And BB1.kaTop >= CurrentAOI.aBC) {
635
636 if (BB1.kaLeft < CurrentAOI.aLC) {
637 BB1.kaLeft = CurrentAOI.aLC;
638 ShowLeft = False;
639 }
640 if (BB1.kaBottom < CurrentAOI.aBC) {
641 BB1.kaBottom = CurrentAOI.aBC;
642 ShowBottom = False;
643 }
644 if (BB1.kaRight > CurrentAOI.aRC) {
645 BB1.kaRight = CurrentAOI.aRC;
646 ShowRight = False;
647 }
648 if (BB1.kaTop > CurrentAOI.aTC) {
649 BB1.kaTop = CurrentAOI.aTC;
650 ShowTop = False;
651 }
652 DevSetColor(Color);
653
654 if (ShowTop)
655 DevLine((int)BB1.kaLeft,(int)BB1.kaTop,
656 (int)BB1.kaRight,(int)BB1.kaTop);
657 if (ShowRight)
658 DevLine((int)BB1.kaRight,(int)BB1.kaTop,
659 (int)BB1.kaRight,(int)BB1.kaBottom);
660 if (ShowBottom)
661 DevLine((int)BB1.kaRight,(int)BB1.kaBottom,
662 (int)BB1.kaLeft,(int)BB1.kaBottom);
663 if (ShowLeft)
664 DevLine((int)BB1.kaLeft,(int)BB1.kaBottom,
665 (int)BB1.kaLeft,(int)BB1.kaTop);
666 }
667 }
668
669 ShowLeft = True;
670 ShowBottom = True;
671 ShowRight = True;
672 ShowTop = True;
673
674 if (Parameters.kpRedisplayControl != COARSEVIEWPORTONLY And
675 CurrentAOI.aInFine) {
676
677 FineLToP(BB.kaLeft,BB.kaBottom,BB1.kaLeft,BB1.kaBottom);
678 FineLToP(BB.kaRight,BB.kaTop,BB1.kaRight,BB1.kaTop);
679
680 if (BB1.kaLeft <= CurrentAOI.aRF And BB1.kaRight >= CurrentAOI.aLF And
681 BB1.kaBottom <= CurrentAOI.aTF And BB1.kaTop >= CurrentAOI.aBF) {
682
683 if (BB1.kaLeft < CurrentAOI.aLF) {
684 BB1.kaLeft = CurrentAOI.aLF;
685 ShowLeft = False;
686 }
687 if (BB1.kaBottom < CurrentAOI.aBF) {
688 BB1.kaBottom = CurrentAOI.aBF;
689 ShowBottom = False;
690 }
691 if (BB1.kaRight > CurrentAOI.aRF) {
692 BB1.kaRight = CurrentAOI.aRF;
693 ShowRight = False;
694 }
695 if (BB1.kaTop > CurrentAOI.aTF) {
696 BB1.kaTop = CurrentAOI.aTF;
697 ShowTop = False;
698 }
699 DevSetColor(Color);
700
701 if (ShowTop)
702 DevLine((int)BB1.kaLeft,(int)BB1.kaTop,
703 (int)BB1.kaRight,(int)BB1.kaTop);
704 if (ShowRight)
705 DevLine((int)BB1.kaRight,(int)BB1.kaTop,
706 (int)BB1.kaRight,(int)BB1.kaBottom);
707 if (ShowBottom)
708 DevLine((int)BB1.kaRight,(int)BB1.kaBottom,
709 (int)BB1.kaLeft,(int)BB1.kaBottom);
710 if (ShowLeft)
711 DevLine((int)BB1.kaLeft,(int)BB1.kaBottom,
712 (int)BB1.kaLeft,(int)BB1.kaTop);
713 }
714 }
715 }
716
717
718 void
EraseBox(boxBB)719 EraseBox(boxBB)
720
721 /* Erase area in boxBB (window coordinates). */
722 struct ka *boxBB;
723 {
724 struct ka BB,BB1;
725
726 SetCurrentAOI(boxBB);
727 ka_copy(BB,boxBB);
728
729 if (Parameters.kpRedisplayControl != FINEVIEWPORTONLY And
730 CurrentAOI.aInCoarse) {
731
732 CoarseLToP(BB.kaLeft,BB.kaBottom,BB1.kaLeft,BB1.kaBottom);
733 CoarseLToP(BB.kaRight,BB.kaTop,BB1.kaRight,BB1.kaTop);
734
735 if (BB1.kaLeft <= CurrentAOI.aRC And BB1.kaRight >= CurrentAOI.aLC And
736 BB1.kaBottom <= CurrentAOI.aTC And BB1.kaTop >= CurrentAOI.aBC) {
737
738 if (BB1.kaLeft < CurrentAOI.aLC)
739 BB1.kaLeft = CurrentAOI.aLC;
740 if (BB1.kaBottom < CurrentAOI.aBC)
741 BB1.kaBottom = CurrentAOI.aBC;
742 if (BB1.kaRight > CurrentAOI.aRC)
743 BB1.kaRight = CurrentAOI.aRC;
744 if (BB1.kaTop > CurrentAOI.aTC)
745 BB1.kaTop = CurrentAOI.aTC;
746 FBEraseBox(BB1.kaLeft,BB1.kaBottom,
747 BB1.kaRight,BB1.kaTop);
748 }
749 }
750
751 if (Parameters.kpRedisplayControl != COARSEVIEWPORTONLY And
752 CurrentAOI.aInFine) {
753
754 FineLToP(BB.kaLeft,BB.kaBottom,BB1.kaLeft,BB1.kaBottom);
755 FineLToP(BB.kaRight,BB.kaTop,BB1.kaRight,BB1.kaTop);
756
757 if (BB1.kaLeft <= CurrentAOI.aRF And BB1.kaRight >= CurrentAOI.aLF And
758 BB1.kaBottom <= CurrentAOI.aTF And BB1.kaTop >= CurrentAOI.aBF) {
759
760 if (BB1.kaLeft < CurrentAOI.aLF)
761 BB1.kaLeft = CurrentAOI.aLF;
762 if (BB1.kaBottom < CurrentAOI.aBF)
763 BB1.kaBottom = CurrentAOI.aBF;
764 if (BB1.kaRight > CurrentAOI.aRF)
765 BB1.kaRight = CurrentAOI.aRF;
766 if (BB1.kaTop > CurrentAOI.aTF)
767 BB1.kaTop = CurrentAOI.aTF;
768 FBEraseBox(BB1.kaLeft,BB1.kaBottom,
769 BB1.kaRight,BB1.kaTop);
770 }
771 }
772 }
773
774
775 /***********************************************************************
776 *
777 * Strch command.
778 * Move wire vertices around.
779 *
780 ***********************************************************************/
781
782 extern char *MenuSTRCH;
783 extern char *MenuUNDO;
784 #ifdef __STDC__
785 static void restore_stretch(void);
786 static void do_stretch_path(long,long,long,long,struct ka*,int);
787 static struct p *get_nearest_vertex(struct p*,long,long);
788 static void set_ref_to_vertex(long*,long*);
789 #else
790 static void restore_stretch();
791 static void do_stretch_path();
792 static struct p *get_nearest_vertex();
793 static void set_ref_to_vertex();
794 #endif
795
796
797 void
StretchPath(LookedAhead)798 StretchPath(LookedAhead)
799
800 /* Strch command, move vertices. */
801 int *LookedAhead;
802 {
803 struct ka NBB,OBB;
804 long RefX,RefY,LastRefX,LastRefY,MapX,MapY;
805 long RefTmpX, RefTmpY;
806 int FirstTime = True;
807 int GotOne = False;
808 int Undo = False;
809 int Modified = 0;
810 int Pt;
811 char Types[2];
812
813 MenuSelect(MenuSTRCH);
814
815 Types[0] = CDWIRE;
816 Types[1] = '\0';
817
818 if (AreTypesInQ(Types))
819 GotOne = True;
820
821 loop {
822 top:
823 if (Not GotOne) {
824 ShowPrompt("Point at wire to stretch");
825
826 switch (PointLoop(LookedAhead)) {
827 case PL_ESC:
828 case PL_CMD:
829 goto quit;
830 case PL_UND:
831 if (FirstTime) goto quit;
832 if (Undo == False) {
833 MenuSelect(MenuUNDO);
834 if (Parameters.kpShowTerminals)
835 DisplayTerminals(ERASE);
836 UndoReferenceTransform();
837 HYundoTransform();
838 SQRestore(True);
839 /* restored objects have Info = SQ_NEWSEL */
840 Modified--;
841 Undo = True;
842 NBB = OBB;
843 EraseBox(&OBB);
844 Redisplay(&OBB);
845 if (Parameters.kpShowTerminals)
846 DisplayTerminals(DISPLAY);
847 MenuDeselect(MenuUNDO);
848 break;
849 }
850 else {
851 if (Parameters.kpShowTerminals)
852 DisplayTerminals(ERASE);
853 /* should have only Info = SQ_NEW objects here */
854 do_stretch_path(RefX,RefY,MapX,MapY,&NBB,False);
855 Modified++;
856 EraseBox(&NBB);
857 Redisplay(&NBB);
858 if (Parameters.kpShowTerminals)
859 DisplayTerminals(DISPLAY);
860 Undo = False;
861 MenuDeselect(MenuUNDO);
862 continue;
863 }
864 case PL_PCW:
865 SelectTypes(Types);
866 if (Not AreTypesInQ(Types))
867 goto top;
868 SQComputeBB();
869 NBB = SelectQBB;
870 }
871 }
872
873 next:
874 ShowPrompt("Point to the vertex.");
875
876 switch (PointLoop(LookedAhead)) {
877 case PL_ESC:
878 case PL_CMD:
879 goto quit;
880 case PL_UND:
881 MenuSelect(MenuUNDO);
882 if (Not GotOne)
883 SQDesel(Types);
884 /* newly selected objects deleted, restored objects
885 * have Info = SQ_NEW.
886 */
887 else {
888 if (FirstTime) {
889 MenuDeselect(MenuUNDO);
890 goto quit;
891 }
892 if (Undo == False) {
893 if (Parameters.kpShowTerminals)
894 DisplayTerminals(ERASE);
895 UndoReferenceTransform();
896 HYundoTransform();
897 SQRestore(True);
898 /* restored objects have Info = SQ_NEWSEL */
899 Modified--;
900 Undo = True;
901 }
902 else {
903 if (Parameters.kpShowTerminals)
904 DisplayTerminals(ERASE);
905 /* should have only Info = SQ_NEWSEL objects here */
906 do_stretch_path(RefX,RefY,MapX,MapY,&NBB,True);
907 Modified++;
908 Undo = False;
909 }
910 }
911 EraseBox(&NBB);
912 Redisplay(&NBB);
913 if (GotOne && Parameters.kpShowTerminals)
914 DisplayTerminals(DISPLAY);
915 MenuDeselect(MenuUNDO);
916 goto top;
917 }
918
919 LastRefX = RefX;
920 LastRefY = RefY;
921 RefX = SCursor.kcX;
922 RefY = SCursor.kcY;
923 set_ref_to_vertex(&RefX,&RefY);
924 ShowPrompt("Point to where it should stretch.");
925
926 RefTmpX = SCursor.kcX;
927 RefTmpY = SCursor.kcY;
928 SCursor.kcX = RefX;
929 SCursor.kcY = RefY;
930 restore_stretch();
931 FBSetRubberBanding('s');
932 SCursor.kcX = RefTmpX;
933 SCursor.kcY = RefTmpY;
934 Pt = PointLoop(LookedAhead);
935 FBSetRubberBanding(0);
936
937 switch (Pt) {
938 case PL_ESC:
939 case PL_CMD:
940 goto quit;
941 case PL_UND:
942 MenuSelect(MenuUNDO);
943 RefX = LastRefX;
944 RefY = LastRefY;
945 MenuDeselect(MenuUNDO);
946 goto next;
947 }
948
949 SQRestore(False);
950 /* should have only Info = SQ_OLDSEL objects here */
951 FirstTime = False;
952
953 MapX = SCursor.kcX;
954 MapY = SCursor.kcY;
955
956 if (Parameters.kpShowTerminals) DisplayTerminals(ERASE);
957 do_stretch_path(RefX,RefY,MapX,MapY,&NBB,GotOne);
958 EraseBox(&NBB);
959 Redisplay(&NBB);
960 if (Parameters.kpShowTerminals) DisplayTerminals(DISPLAY);
961 OBB = NBB;
962 Modified++;
963 Undo = False;
964 }
965
966 quit:
967 ClearReferenceUndoFlags();
968 HYclearUndoFlags();
969 SQRestore(False);
970 if (Not GotOne And AreTypesInQ(Types)) {
971 SQComputeBB();
972 SQDesel(Types);
973 EraseBox(&SelectQBB);
974 Redisplay(&SelectQBB);
975 }
976 if (Modified)
977 Parameters.kpModified = True;
978 ErasePrompt();
979 MenuDeselect(MenuUNDO);
980 MenuDeselect(MenuSTRCH);
981 }
982
983
984 void
ShowStretch(MapX,MapY,RefX,RefY)985 ShowStretch(MapX,MapY,RefX,RefY)
986
987 /* Called from rubber banding routine. */
988 long RefX,RefY,MapX,MapY;
989 {
990 struct ks *SQDesc, *SQDesc1;
991 struct p *Path, *pp;
992 int Layer;
993 long Width;
994
995 for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc) {
996 if (SQDesc->ksPointer->oInfo != SQ_OLDSEL &&
997 SQDesc->ksPointer->oInfo != SQ_NEWSEL) continue;
998 if (SQDesc->ksPointer->oType != CDWIRE) continue;
999
1000 /* draw only once if in queue more than once */
1001 for (SQDesc1 = SQDesc->ksSucc; SQDesc1; SQDesc1 = SQDesc1->ksSucc)
1002 if (SQDesc1->ksPointer == SQDesc->ksPointer) break;
1003
1004 if (SQDesc1 == NULL) {
1005 CDWire(SQDesc->ksPointer,&Layer,&Width,&Path);
1006 pp = get_nearest_vertex(Path,RefX,RefY);
1007 pp->pX += MapX - RefX;
1008 pp->pY += MapY - RefY;
1009 ShowWire(HighlightingColor,Width,Path);
1010 pp->pX -= MapX - RefX;
1011 pp->pY -= MapY - RefY;
1012 }
1013 }
1014 }
1015
1016 static void
restore_stretch()1017 restore_stretch()
1018
1019 /* Repaint objects to be stretched. */
1020 {
1021 struct ks *SQDesc;
1022 struct p *Path;
1023 int Layer;
1024 long Width;
1025
1026 for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc) {
1027 if (SQDesc->ksPointer->oInfo == SQ_GONE) continue;
1028 if (SQDesc->ksPointer->oType != CDWIRE) continue;
1029
1030 CDWire(SQDesc->ksPointer,&Layer,&Width,&Path);
1031 ShowWire(DrawingColor,Width,Path);
1032 }
1033 }
1034
1035
1036 static void
do_stretch_path(RefX,RefY,MapX,MapY,NBB,SelectNew)1037 do_stretch_path(RefX,RefY,MapX,MapY,NBB,SelectNew)
1038
1039 /* Perform the stretch. */
1040 long RefX,RefY,MapX,MapY;
1041 struct ka *NBB;
1042 int SelectNew;
1043 {
1044 struct ks *SQDesc;
1045 struct ka OBB,BB;
1046 struct o *Pointer;
1047 struct p *Path, *pp;
1048 int Layer;
1049 long Width;
1050
1051 NBB->kaLeft = CDINFINITY;
1052 NBB->kaRight = -CDINFINITY;
1053 NBB->kaBottom = CDINFINITY;
1054 NBB->kaTop = -CDINFINITY;
1055
1056 for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc) {
1057 if (SQDesc->ksPointer->oInfo == SQ_GONE) continue;
1058 if (SQDesc->ksPointer->oType != CDWIRE) continue;
1059
1060 CDWire(SQDesc->ksPointer,&Layer,&Width,&Path);
1061 CDStatusInt = CDBB(Parameters.kpCellDesc,SQDesc->ksPointer,
1062 &OBB.kaLeft,&OBB.kaBottom,&OBB.kaRight,&OBB.kaTop);
1063
1064 Path = CopyPath(Path);
1065 pp = get_nearest_vertex(Path,RefX,RefY);
1066 pp->pX += MapX - RefX;
1067 pp->pY += MapY - RefY;
1068
1069 if (Not CDMakeWire(Parameters.kpCellDesc,Layer,Width,Path,
1070 &Pointer)) MallocFailed();
1071 AssignWireProperties(Pointer);
1072
1073 if (SelectNew)
1074 Pointer->oInfo = SQ_NEWSEL;
1075 else
1076 Pointer->oInfo = SQ_NEW;
1077
1078 SQInsert(Pointer);
1079
1080 CDStatusInt = CDBB(Parameters.kpCellDesc,Pointer,
1081 &BB.kaLeft,&BB.kaBottom,&BB.kaRight,&BB.kaTop);
1082
1083 if (OBB.kaLeft < BB.kaLeft) BB.kaLeft = OBB.kaLeft;
1084 if (OBB.kaRight > BB.kaRight) BB.kaRight = OBB.kaRight;
1085 if (OBB.kaBottom < BB.kaBottom) BB.kaBottom = OBB.kaBottom;
1086 if (OBB.kaTop > BB.kaTop) BB.kaTop = OBB.kaTop;
1087
1088 if (BB.kaLeft < NBB->kaLeft) NBB->kaLeft = BB.kaLeft;
1089 if (BB.kaRight > NBB->kaRight) NBB->kaRight = BB.kaRight;
1090 if (BB.kaBottom < NBB->kaBottom) NBB->kaBottom = BB.kaBottom;
1091 if (BB.kaTop > NBB->kaTop) NBB->kaTop = BB.kaTop;
1092
1093 TPush();
1094 TIdentity();
1095 TTranslate(MapX-RefX,MapY-RefY);
1096 TransformReferencePoint(SQDesc->ksPointer,RefX,RefY);
1097 HYtransformStretch(SQDesc->ksPointer,Pointer,RefX,RefY,MapX,MapY);
1098 TPop();
1099 SQDesc->ksPointer->oInfo = SQ_GONE;
1100 }
1101 }
1102
1103
1104 static struct p *
get_nearest_vertex(Path,X,Y)1105 get_nearest_vertex(Path,X,Y)
1106
1107 /* return the path vertex nearest X,Y. */
1108 struct p *Path;
1109 long X,Y;
1110 {
1111 double dx,dy,d,mind;
1112 struct p *p;
1113 int i,indx = 0;
1114
1115 mind = 1e30;
1116 for (p = Path,i = 0; p; p = p->pSucc,i++) {
1117 dx = p->pX - X;
1118 dy = p->pY - Y;
1119 d = dx*dx + dy*dy;
1120 if (d < mind) {
1121 mind = d;
1122 indx = i;
1123 }
1124 }
1125 for (p = Path, i = 0; i < indx; p = p->pSucc,i++) ;
1126 return (p);
1127 }
1128
1129
1130 static void
set_ref_to_vertex(X,Y)1131 set_ref_to_vertex(X,Y)
1132
1133 /* Return in pointers the vertex closest to the given coordinates. */
1134 long *X,*Y;
1135 {
1136 struct ks *SQDesc;
1137 struct o *Pointer;
1138 struct p *p,*Path;
1139 double dx,dy,d,mind;
1140 int i,indx;
1141
1142
1143 mind = 1e30;
1144 for (SQDesc = SelectQHead; SQDesc; SQDesc = SQDesc->ksSucc) {
1145
1146 if (SQDesc->ksPointer->oInfo == SQ_GONE) continue;
1147 if (SQDesc->ksPointer->oType != CDWIRE) continue;
1148 Path = ((struct w *)SQDesc->ksPointer->oRep)->wPath;
1149
1150 for (p = Path,i = 0; p; p = p->pSucc,i++) {
1151 dx = p->pX - *X;
1152 dy = p->pY - *Y;
1153 d = dx*dx + dy*dy;
1154 if (d < mind) {
1155 mind = d;
1156 indx = i;
1157 Pointer = SQDesc->ksPointer;
1158 }
1159 }
1160 }
1161 Path = ((struct w *)Pointer->oRep)->wPath;
1162 for (p = Path, i = 0; i < indx; p = p->pSucc,i++) ;
1163 *X = p->pX;
1164 *Y = p->pY;
1165 }
1166
1167
1168 /***********************************************************************
1169 *
1170 * Dot command.
1171 * Place dots at connections.
1172 *
1173 ***********************************************************************/
1174
1175 extern char *MenuDOTS;
1176 extern char *MenuUNDO;
1177
1178 static char DotP[] = {0,60, 40,40, 60,0, 40,-40, 0,-60, -40,-40,
1179 -60,0, -40,40, 0,60};
1180
1181 static struct p *DotList;
1182
1183 struct pp {
1184 int ppX, ppY;
1185 int ppType;
1186 struct pp *ppSucc;
1187 };
1188 static struct pp *VertexList;
1189
1190 struct clist {
1191 char *name;
1192 struct s *cd;
1193 struct clist *next;
1194 };
1195 static struct clist *CList;
1196
1197 #ifdef __STDC__
1198 static void clear_dots(struct s*);
1199 static void create_dots(struct s*);
1200 static void save_vertex(int,int,int);
1201 static void save_dot(int,int);
1202 static void add_dots(struct s*);
1203 static void list_cells(void);
1204 static void mlist(struct m*);
1205 static struct s *add_to_list(char*);
1206 #else
1207 static void clear_dots();
1208 static void create_dots();
1209 static void save_vertex();
1210 static void save_dot();
1211 static void add_dots();
1212 static void list_cells();
1213 static void mlist();
1214 static struct s *add_to_list();
1215 #endif
1216
1217
1218 void
Dots(LookedAhead)1219 Dots(LookedAhead)
1220
1221 int *LookedAhead;
1222 {
1223 struct clist *s;
1224
1225 MenuSelect(MenuDOTS);
1226 Parameters.kpShowDots = True;
1227 list_cells();
1228 for (s = CList; s; s = s->next)
1229 create_dots(s->cd);
1230 EraseLargeCoarseViewport();
1231 Redisplay(View->kvCoarseWindow);
1232
1233 loop {
1234 switch (PointLoop(LookedAhead)) {
1235 case PL_CMD:
1236 if (Matching(MenuDOTS))
1237 Parameters.kpCommand[0] = '\0';
1238 case PL_ESC:
1239 case PL_UND:
1240 goto quit;
1241 case PL_PCW:
1242 break;
1243 }
1244 }
1245 quit:
1246 for (s = CList; s; s = s->next)
1247 clear_dots(s->cd);
1248 for (; CList; CList = s) {
1249 s = CList->next;
1250 txfree((char*)CList);
1251 }
1252 CList = NULL;
1253 MenuDeselect(MenuDOTS);
1254 Parameters.kpShowDots = False;
1255 EraseLargeCoarseViewport();
1256 Redisplay(View->kvCoarseWindow);
1257 }
1258
1259
1260 static void
clear_dots(CellDesc)1261 clear_dots(CellDesc)
1262
1263 struct s *CellDesc;
1264 {
1265 struct g *GenDesc;
1266 struct o *Pointer;
1267
1268 if (Not CDInitGen(CellDesc,1,-CDINFINITY,-CDINFINITY,
1269 CDINFINITY,CDINFINITY,&GenDesc)) MallocFailed();
1270
1271 loop {
1272 CDGen(CellDesc,GenDesc,&Pointer);
1273 if (Pointer == NULL) break;
1274 if (Pointer->oInfo == SQ_GONE Or Pointer->oInfo == SQ_INCMPLT)
1275 continue;
1276 if (Pointer->oType == CDPOLYGON) {
1277 CDDelete(CellDesc,Pointer);
1278 }
1279 }
1280 }
1281
1282 static void
create_dots(CellDesc)1283 create_dots(CellDesc)
1284
1285 struct s *CellDesc;
1286 {
1287 struct g *GenDesc;
1288 struct o *Pointer;
1289 struct p *p;
1290 struct prpty *pd;
1291
1292 /* Process wires */
1293
1294 if (Not CDInitGen(CellDesc,1,-CDINFINITY,-CDINFINITY,
1295 CDINFINITY,CDINFINITY,&GenDesc)) MallocFailed();
1296
1297 loop {
1298 CDGen(CellDesc,GenDesc,&Pointer);
1299 if (Pointer == NULL) break;
1300 if (Pointer->oInfo == SQ_GONE Or Pointer->oInfo == SQ_INCMPLT)
1301 continue;
1302 if (Pointer->oType == CDWIRE) {
1303 p = ((struct w *)Pointer->oRep)->wPath;
1304 save_vertex(p->pX,p->pY,0);
1305 for (p = p->pSucc; p && p->pSucc; p = p->pSucc)
1306 save_vertex(p->pX,p->pY,1);
1307 if (p)
1308 save_vertex(p->pX,p->pY,0);
1309 }
1310 }
1311
1312 /* Process devices */
1313
1314 if (Not CDInitGen(CellDesc,0,-CDINFINITY,-CDINFINITY,CDINFINITY,
1315 CDINFINITY,&GenDesc)) MallocFailed();
1316
1317 loop {
1318 CDGen(CellDesc,GenDesc,&Pointer);
1319 if (Pointer == NULL) break;
1320 if (Pointer->oInfo == SQ_GONE) continue;
1321
1322 pd = Pointer->oPrptyList;
1323 for (; pd; pd = pd->prpty_Succ) {
1324 if (pd->prpty_Value != P_NODE)
1325 continue;
1326 save_vertex(pd->prpty_Data->p_node.x,
1327 pd->prpty_Data->p_node.y,0);
1328 }
1329 }
1330 add_dots(CellDesc);
1331 }
1332
1333
1334 static void
save_vertex(x,y,type)1335 save_vertex(x,y,type)
1336
1337 /* Consider saving point in vertex list; if it is already there,
1338 * update the type field or add it to the dot list.
1339 */
1340 int x, y, type;
1341 {
1342 struct pp *p;
1343
1344 for (p = VertexList; p; p = p->ppSucc) {
1345 if (p->ppX == x && p->ppY == y) {
1346 /* already there, if both types are zero,
1347 * update the stored entry to type 1 (so as
1348 * not to mark two abutting line segments)
1349 */
1350 if (p->ppType == 0 && type == 0)
1351 p->ppType = 1;
1352 else
1353 save_dot(x,y);
1354 return;
1355 }
1356 }
1357 p = alloc(pp);
1358 if (p == NULL) MallocFailed();
1359 p->ppX = x;
1360 p->ppY = y;
1361 p->ppType = type;
1362 p->ppSucc = VertexList;
1363 VertexList = p;
1364 }
1365
1366
1367 static void
save_dot(x,y)1368 save_dot(x,y)
1369
1370 /* Save point in the dot list; if it is already
1371 * there, just return.
1372 */
1373 int x, y;
1374 {
1375 struct p *p;
1376
1377 for (p = DotList; p; p = p->pSucc) {
1378 if (p->pX == x && p->pY == y)
1379 return;
1380 }
1381 p = alloc(p);
1382 if (p == NULL) MallocFailed();
1383 p->pX = x;
1384 p->pY = y;
1385 p->pSucc = DotList;
1386 DotList = p;
1387 }
1388
1389
1390 static void
add_dots(CellDesc)1391 add_dots(CellDesc)
1392
1393 /* Process and clear the dot and vertex lists. */
1394 struct s *CellDesc;
1395 {
1396 struct p *p, *pd, *Path;
1397 struct o *Pointer;
1398 int i;
1399
1400 for (pd = DotList; pd; pd = pd->pSucc) {
1401
1402 p = Path = alloc(p);
1403 if (p == NULL) MallocFailed();
1404 p->pX = pd->pX + DotP[0];
1405 p->pY = pd->pY + DotP[1];
1406 for (i = 1; i < 9; i++) {
1407 p->pSucc = alloc(p);
1408 p = p->pSucc;
1409 if (p == NULL) MallocFailed();
1410 p->pX = pd->pX + DotP[2*i];
1411 p->pY = pd->pY + DotP[2*i+1];
1412 }
1413 p->pSucc = NULL;
1414 if (!CDMakePolygon(CellDesc,1,Path,&Pointer))
1415 MallocFailed();
1416 }
1417 for (; DotList; DotList = p) {
1418 p = DotList->pSucc;
1419 txfree((char*)DotList);
1420 }
1421 for (; VertexList; VertexList = (struct pp*)p) {
1422 p = (struct p*)VertexList->ppSucc;
1423 txfree((char*)VertexList);
1424 }
1425 DotList = NULL;
1426 VertexList = NULL;
1427 }
1428
1429
1430 static void
list_cells()1431 list_cells()
1432
1433 /* get a list of cells in hierarchy */
1434 {
1435 CList = alloc(clist);
1436 if (CList == NULL)
1437 MallocFailed();
1438 CList->name = Parameters.kpCellName;
1439 CList->cd = Parameters.kpCellDesc;
1440 CList->next = NULL;
1441 mlist(Parameters.kpCellDesc->sMasterList);
1442 }
1443
1444
1445 static void
mlist(md)1446 mlist(md)
1447
1448 /* walk the master list and recursively add subcircuits */
1449 struct m *md;
1450 {
1451 struct s *cd;
1452
1453 for (; md; md = md->mSucc) {
1454 if (!IsCellInLib(md->mName)) {
1455 cd = add_to_list(md->mName);
1456 if (cd) {
1457 mlist(cd->sMasterList);
1458 }
1459 }
1460 }
1461 }
1462
1463
1464 static struct s *
add_to_list(name)1465 add_to_list(name)
1466
1467 /* add the cell if not already there, return descr of new addition */
1468 char *name;
1469 {
1470 struct clist *s;
1471 struct s *cd;
1472
1473 for (s = CList; s; s = s->next) {
1474 if (!strcmp(s->name,name))
1475 return (NULL);
1476 }
1477 if (!CDOpen(name,&cd,'r'))
1478 return (NULL);
1479 s = alloc(clist);
1480 if (s == NULL)
1481 MallocFailed();
1482 s->name = name;
1483 s->cd = cd;
1484 s->next = CList;
1485 CList = s;
1486 return (cd);
1487 }
1488