1 /*
2  * CmdRS.c --
3  *
4  * Commands with names beginning with the letters R through S.
5  *
6  *     *********************************************************************
7  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
8  *     * Permission to use, copy, modify, and distribute this              *
9  *     * software and its documentation for any purpose and without        *
10  *     * fee is hereby granted, provided that the above copyright          *
11  *     * notice appear in all copies.  The University of California        *
12  *     * makes no representations about the suitability of this            *
13  *     * software for any purpose.  It is provided "as is" without         *
14  *     * express or implied warranty.  Export of this software outside     *
15  *     * of the United States of America may require an export license.    *
16  *     *********************************************************************
17  */
18 
19 #ifndef lint
20 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/commands/CmdRS.c,v 1.13 2010/06/24 12:37:15 tim Exp $";
21 #endif  /* not lint */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <time.h>
28 
29 #include "tcltk/tclmagic.h"
30 #include "utils/magic.h"
31 #include "utils/stack.h"
32 #include "utils/geometry.h"
33 #include "utils/utils.h"
34 #include "tiles/tile.h"
35 #include "utils/hash.h"
36 #include "utils/styles.h"
37 #include "database/database.h"
38 #include "database/fonts.h"
39 #include "windows/windows.h"
40 #include "dbwind/dbwind.h"
41 #include "utils/main.h"
42 #include "commands/commands.h"
43 #include "textio/textio.h"
44 #include "graphics/graphics.h"
45 #include "utils/tech.h"
46 #include "drc/drc.h"
47 #include "textio/txcommands.h"
48 #include "utils/malloc.h"
49 #include "utils/netlist.h"
50 #include "netmenu/netmenu.h"
51 #include "select/select.h"
52 #include "utils/signals.h"
53 #include "sim/sim.h"
54 
55 extern void DisplayWindow();
56 
57 /* Used by CmdSetLabel() */
58 Label *DefaultLabel;
59 
60 /*
61  * ----------------------------------------------------------------------------
62  *
63  * CmdRandom
64  *
65  * 	Generate a random integer or set the random seed.  This is mainly
66  *	used for seeding the random number generator for the GDS write
67  *	routine when GDS write is assigning a random prefix to ensure
68  *	uniqueness of names in a GDS library read from a file instead of
69  *	generated from the database.
70  *
71  * Results:
72  *	None.
73  *
74  * Side effects:
75  *	None.
76  *
77  * ----------------------------------------------------------------------------
78  */
79 
80 void
CmdRandom(w,cmd)81 CmdRandom(w, cmd)
82     MagWindow *w;
83     TxCommand *cmd;
84 {
85     int value;
86 
87     if (cmd->tx_argc == 1)
88     {
89 	/* Return a random number */
90 
91 #ifdef MAGIC_WRAPPER
92 	Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(random()));
93 #else
94 	TxPrintf("%d", random());
95 #endif
96     }
97     else if ((cmd->tx_argc >= 2) && (!strcmp(cmd->tx_argv[1], "seed")))
98     {
99 	if (cmd->tx_argc == 3)
100 	{
101 	    value = atoi(cmd->tx_argv[2]);
102 	}
103 	else
104 	{
105 	    value = (int)time(NULL);
106 	}
107 	srandom(value);
108     }
109     else
110     {
111 	TxPrintf("usage: random [seed [<value>]]\n");
112 	return;
113     }
114 }
115 
116 #if !defined(NO_SIM_MODULE) && defined(RSIM_MODULE)
117 /*
118  * ----------------------------------------------------------------------------
119  *
120  * CmdRsim
121  *
122  * 	Starts Rsim under Magic.
123  *
124  * Results:
125  *	None.
126  *
127  * Side effects:
128  *	Rsim is forked.
129  *
130  * ----------------------------------------------------------------------------
131  */
132 
133 void
CmdRsim(w,cmd)134 CmdRsim(w, cmd)
135     MagWindow *w;
136     TxCommand *cmd;
137 {
138 
139     if ((cmd->tx_argc == 1) && (!SimRsimRunning)) {
140 	TxPrintf("usage: rsim [options] file\n");
141 	return;
142     }
143     if ((cmd->tx_argc != 1) && (SimRsimRunning)) {
144 	TxPrintf("Simulator already running.  You cannot start another.\n");
145 	return;
146     }
147     windCheckOnlyWindow(&w, DBWclientID);
148     if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
149 	TxError("Put the cursor in a layout window.\n");
150 	return;
151     }
152     if (cmd->tx_argc != 1) {
153 	cmd->tx_argv[cmd->tx_argc] = (char *) 0;
154 	SimStartRsim(cmd->tx_argv);
155     }
156     SimConnectRsim(FALSE);
157 
158 }
159 #endif
160 
161 
162 /*
163  * ----------------------------------------------------------------------------
164  *
165  * CmdSave --
166  *
167  * Implement the "save" command.
168  * Writes the EditCell out to a disk file.
169  *
170  * Usage:
171  *	save [file]
172  *
173  * Results:
174  *	None.
175  *
176  * Side effects:
177  *	Writes the cell out to file, if specified, or the file
178  *	associated with the cell otherwise.
179  *	Updates the caption in the window if the name of the edit
180  *	cell has changed.
181  *	Clears the modified bit in the cd_flags.
182  *
183  * ----------------------------------------------------------------------------
184  */
185 
186 void
CmdSave(w,cmd)187 CmdSave(w, cmd)
188     MagWindow *w;
189     TxCommand *cmd;
190 {
191     CellDef *locDef;
192 
193     if (cmd->tx_argc > 2)
194     {
195 	TxError("Usage: %s [file]\n", cmd->tx_argv[0]);
196 	return;
197     }
198 
199     /* The "save" command can turn a read-only cell into a writeable	*/
200     /* cell, or vice versa.  We should have more checks on the status	*/
201     /* of the resulting file. . . .  For now, we patch things up so	*/
202     /* that doing "save" on a read-only cell doesn't crash magic.	*/
203 
204     if (EditCellUse == NULL)
205     {
206 	locDef = ((CellUse *)w->w_surfaceID)->cu_def;
207 	locDef->cd_flags &= ~CDNOEDIT;
208     }
209     else
210 	locDef = EditCellUse->cu_def;
211 
212     DBUpdateStamps();
213     if (cmd->tx_argc == 2)
214     {
215 	char *fileName;
216 
217 	if (CmdIllegalChars(cmd->tx_argv[1], "[],", "Cell name"))
218 	    return;
219 
220 	cmdSaveCell(locDef, cmd->tx_argv[1], FALSE, TRUE);
221     }
222     else cmdSaveCell(locDef, (char *) NULL, FALSE, TRUE);
223 }
224 
225 
226 /*
227  * ----------------------------------------------------------------------------
228  *
229  * CmdScaleGrid --
230  *
231  *	This procedure scales magic units with respect to lambda.  A scaling
232  *	of 2 to 1, for example, makes the magic units half their former size,
233  *	allowing magic to read CIF files with components on the half-lambda
234  *	grid without the necessity of altering the technology file for the
235  *	process.
236  *
237  * Usage:
238  *	scalegrid a b	(or)	scalegrid a/b	(or) scalegrid a:b
239  *
240  *	"a" and "b" are integers
241  *
242  * Results:
243  *	magic internal units are scaled by factor a/b.
244  *
245  * Side Effects:
246  *	cifinput and cifoutput scale factors are multiplied by a/b.
247  *	All drc widths and distances are multiplied by b/a.
248  *	All layout tile coordinates are multiplied by b/a.
249  *	The current drawing scale and position are altered to maintain
250  *	   the current view.
251  *	All windows are redrawn in case scaling has caused round-off
252  *	   truncation of tile coordinates.
253  *	Geometry alterations are not undoable!
254  *
255  * ----------------------------------------------------------------------------
256  */
257 
258 void
CmdScaleGrid(w,cmd)259 CmdScaleGrid(w, cmd)
260     MagWindow *w;
261     TxCommand *cmd;
262 {
263     extern void DBScalePoint();
264     int scalen, scaled;
265     char *argsep;
266     Rect rootBox;
267     CellDef *rootBoxDef;
268 
269     if ((cmd->tx_argc == 2) || (cmd->tx_argc == 3))
270     {
271 	if (cmd->tx_argc == 2)
272 	{
273 	    if (((argsep = strchr(cmd->tx_argv[1], ':')) != NULL) ||
274 			((argsep = strchr(cmd->tx_argv[1], '/')) != NULL))
275 	    {
276 		*argsep++ = '\0';
277 	        if (!StrIsInt(argsep))
278 		    goto scalegridusage;
279 		else
280 		    scaled = atoi(argsep);
281 	    }
282 	    else
283 		goto scalegridusage;
284 	}
285 	else
286 	{
287 	    if (!StrIsInt(cmd->tx_argv[2]))
288 		goto scalegridusage;
289 	    else
290 		scaled = atoi(cmd->tx_argv[2]);
291 	}
292 
293 	if (!StrIsInt(cmd->tx_argv[1]))
294 	    goto scalegridusage;
295 	else
296 	    scalen = atoi(cmd->tx_argv[1]);
297 
298 	if (scalen <= 0 || scaled <= 0) goto scalegridusage;
299 	else if (scalen == scaled) goto scalegridreport;
300 
301 	/* Reduce fraction by the greatest common factor */
302 
303 	ReduceFraction(&scalen, &scaled);
304 
305 	/* Check against CIF output style to see if we're violating a	*/
306 	/* minimum grid resolution limit.				*/
307 
308 	if (CIFTechLimitScale(scalen, scaled) != 0)
309 	{
310 	    TxError("Grid scaling is finer than limit set by the process!\n");
311 	    return;
312 	}
313 
314 	/* Scale cifinput and cifoutput */
315 
316 	CIFTechInputScale(scalen, scaled, TRUE);
317 	CIFTechOutputScale(scalen, scaled);
318 
319 	/* Scale drc rules */
320 
321 	DRCTechScale(scalen, scaled);
322 
323 	/* Scale extract parameters */
324 
325 	ExtTechScale(scalen, scaled);
326 
327 	/* Scale wiring parameters */
328 
329 	WireTechScale(scalen, scaled);
330 
331 	/* Scale LEF parameters */
332 
333 #ifdef LEF_MODULE
334 	LefTechScale(scalen, scaled);
335 #endif
336 
337 #ifdef ROUTE_MODULE
338 	/* Scale core router parameters */
339 
340 	RtrTechScale(scalen, scaled);
341 
342 	/* Scale maze router parameters (must come after DRCTechScale) */
343 
344 	MZAfterTech();
345 	IRAfterTech();
346 
347 #endif
348 	/* Scale all tiles */
349 
350 	DBScaleEverything(scaled, scalen);
351 
352 	/* Save the current scale factor */
353 
354 	DBLambda[0] *= scalen;
355 	DBLambda[1] *= scaled;
356 	ReduceFraction(&DBLambda[0], &DBLambda[1]);
357 
358 	/* Rescale cursor box */
359 
360 	if (ToolGetBox(&rootBoxDef, &rootBox))
361 	{
362 	    DBScalePoint(&rootBox.r_ll, scaled, scalen);
363 	    DBScalePoint(&rootBox.r_ur, scaled, scalen);
364 	    ToolMoveBox(TOOL_BL, &rootBox.r_ll, FALSE, rootBoxDef);
365 	    ToolMoveCorner(TOOL_TR, &rootBox.r_ur, FALSE, rootBoxDef);
366 	}
367 
368 	/* Adjust all window viewing scales and positions and redraw */
369 
370 	WindScale(scaled, scalen);
371 
372 	/* This is harsh.  Might work to scale all distance measures in */
373 	/* the undo record, but this is simple and direct.		*/
374 
375 	UndoFlush();
376 
377 	/* TxPrintf("Magic internal unit scaled by %.3f\n",
378 		(float)scalen / (float)scaled);	*/
379 
380 scalegridreport:
381 	TxPrintf("%d Magic internal unit%s = %d Lambda\n",
382 		DBLambda[1], (DBLambda[1] == 1) ? "" : "s", DBLambda[0]);
383 
384 	return;
385     }
386 
387 scalegridusage:
388     TxError("Usage:  scalegrid a b, where a and b are strictly positive integers\n");
389     return;
390 }
391 
392 /*
393  * ----------------------------------------------------------------------------
394  *
395  * CmdSee --
396  *
397  * 	This procedure is used to enable or disable display of certain
398  *	things on the screen.
399  *
400  * Usage:
401  *	see [no] stuff
402  *
403  *	Stuff consists of mask layers or the keyword "allSame"
404  *
405  * Results:
406  *	None.
407  *
408  * Side effects:
409  *	The indicated mask layers are enabled or disabled from being
410  *	displayed in the current window.
411  *
412  * ----------------------------------------------------------------------------
413  */
414 
415 void
CmdSee(w,cmd)416 CmdSee(w, cmd)
417     MagWindow *w;
418     TxCommand *cmd;
419 {
420     int flags;
421     bool off;
422     char *arg;
423     TileType i, j;
424     TileTypeBitMask mask, *rmask;
425     DBWclientRec *crec;
426 
427     windCheckOnlyWindow(&w, DBWclientID);
428     if ((w == NULL) || (w->w_client != DBWclientID))
429     {
430 	TxError("Point to a layout window first.\n");
431 	return;
432     }
433     crec = (DBWclientRec *) w->w_clientData;
434 
435     arg = (char *) NULL;
436     off = FALSE;
437     flags = 0;
438     if (cmd->tx_argc > 1)
439     {
440 	if (strcmp(cmd->tx_argv[1], "no") == 0)
441 	{
442 	    off = TRUE;
443 	    if (cmd->tx_argc > 2) arg = cmd->tx_argv[2];
444 	}
445 	else arg = cmd->tx_argv[1];
446 	if ((cmd->tx_argc > 3) || ((cmd->tx_argc == 3) && !off))
447 	{
448 	    TxError("Usage: see [no] layers|allSame\n");
449 	    return;
450 	}
451     }
452 
453     /* Figure out which things to set or clear.  Don't ever make space
454      * invisible:  that doesn't make any sense.
455      */
456 
457     if (arg != NULL)
458     {
459 	if (strcmp(arg, "allSame") == 0)
460 	{
461 	    mask = DBZeroTypeBits;
462 	    flags = DBW_ALLSAME;
463 	}
464 	else
465 	{
466 	    if (!CmdParseLayers(arg, &mask))
467 		return;
468 	}
469     }
470     else mask = DBAllTypeBits;
471 
472     if (TTMaskHasType(&mask, L_LABEL))
473 	flags |= DBW_SEELABELS;
474     if (TTMaskHasType(&mask, L_CELL))
475 	flags |= DBW_SEECELLS;
476     TTMaskClearType(&mask, L_LABEL);
477     TTMaskClearType(&mask, L_CELL);
478     TTMaskClearType(&mask, TT_SPACE);
479 
480     if (off)
481     {
482 	for (i = 0; i < DBNumUserLayers; i++)
483 	{
484 	    if (TTMaskHasType(&mask, i))
485 		TTMaskClearMask(&crec->dbw_visibleLayers,
486 			&DBLayerTypeMaskTbl[i]);
487 	}
488 	for (; i < DBNumTypes; i++)
489 	{
490 	    /* This part handles stacked contact types.  The display	*/
491 	    /* routine calls DBTreeSrUniqueTiles(), which displays	*/
492 	    /* stacked contact types only on their home plane.  Thus	*/
493 	    /* if we select a contact type, we should also select all	*/
494 	    /* stacking types for that contact that are on the same	*/
495 	    /* plane.							*/
496 
497 	    rmask = DBResidueMask(i);
498 	    for (j = 0; j < DBNumUserLayers; j++)
499 		if (TTMaskHasType(rmask, j))
500 		    if (TTMaskHasType(&mask, j))
501 			if (DBPlane(i) == DBPlane(j))
502 			    TTMaskClearMask(&crec->dbw_visibleLayers,
503 					&DBLayerTypeMaskTbl[i]);
504 	}
505 	crec->dbw_flags &= ~flags;
506     }
507     else
508     {
509 	for (i = 0; i < DBNumUserLayers; i++)
510 	{
511 	    if (TTMaskHasType(&mask, i))
512 		TTMaskSetMask(&crec->dbw_visibleLayers,
513 			&DBLayerTypeMaskTbl[i]);
514 	}
515 	for (; i < DBNumTypes; i++)
516 	{
517 	    rmask = DBResidueMask(i);
518 	    for (j = 0; j < DBNumUserLayers; j++)
519 		if (TTMaskHasType(rmask, j))
520 		    if (TTMaskHasType(&mask, j))
521 			if (DBPlane(i) == DBPlane(j))
522 			    TTMaskSetMask(&crec->dbw_visibleLayers,
523 					&DBLayerTypeMaskTbl[i]);
524 	}
525 	crec->dbw_flags |= flags;
526     }
527     WindAreaChanged(w, &w->w_screenArea);
528     return;
529 }
530 
531 #define SEL_AREA	 0
532 #define SEL_VISIBLE	 1
533 #define SEL_CELL	 2
534 #define SEL_LABELS	 3
535 #define SEL_INTERSECT	 4
536 #define SEL_CLEAR	 5
537 #define SEL_FLAT	 6
538 #define SEL_HELP	 7
539 #define SEL_KEEP	 8
540 #define SEL_MOVE	 9
541 #define SEL_PICK	10
542 #define SEL_SAVE	11
543 #define SEL_FEEDBACK	12
544 #define SEL_BBOX	13
545 #define SEL_BOX		14
546 #define SEL_CHUNK	15
547 #define SEL_REGION	16
548 #define SEL_NET		17
549 #define SEL_SHORT	18
550 #define SEL_DEFAULT	19
551 
552 /*
553  * ----------------------------------------------------------------------------
554  *
555  * cmdSelectArea --
556  *
557  * 	This is a utility procedure used by CmdSelect to do area
558  *	selection.
559  *
560  * Results:
561  *	None.
562  *
563  * Side effects:
564  *	The selection is augmented to contain all the information on
565  *	layers that is visible under the box, including paint, labels,
566  *	and unexpanded subcells.
567  *
568  * ----------------------------------------------------------------------------
569  */
570 
571 	/* ARGSUSED */
572 void
cmdSelectArea(layers,less,option,globmatch)573 cmdSelectArea(layers, less, option, globmatch)
574     char *layers;			/* Which layers are to be selected. */
575     bool less;
576     int option;				/* Option from defined list above */
577     char *globmatch;			/* Optional match string for labels */
578 {
579     SearchContext scx;
580     TileTypeBitMask mask;
581     int windowMask, xMask;
582     DBWclientRec *crec;
583     MagWindow *window;
584 
585     bzero(&scx, sizeof(SearchContext));
586     window = ToolGetBoxWindow(&scx.scx_area, &windowMask);
587     if (window == NULL)
588     {
589 	TxPrintf("The box isn't in a window.\n");
590 	return;
591     }
592 
593     /* Since the box may actually be in multiple windows, we have to
594      * be a bit careful.  If the box is only in one window, then there's
595      * no problem.  If it's in more than window, the cursor must
596      * disambiguate the windows.
597      */
598 
599     xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
600     if ((windowMask & ~xMask) != 0)
601     {
602 	window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
603         xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
604 	if ((windowMask & xMask) == 0)
605 	{
606 	    TxPrintf("The box is in more than one window;  use the cursor\n");
607 	    TxPrintf("to select the one you want to select from.\n");
608 	    return;
609 	}
610     }
611     if (CmdParseLayers(layers, &mask))
612     {
613 	if (TTMaskEqual(&mask, &DBSpaceBits))
614 	    (void) CmdParseLayers("*,label", &mask);
615 	TTMaskClearType(&mask, TT_SPACE);
616     }
617     else return;
618 
619     if (less)
620       {
621 	(void) SelRemoveArea(&scx.scx_area, &mask, globmatch);
622 	return;
623       }
624 
625     scx.scx_use = (CellUse *) window->w_surfaceID;
626     scx.scx_trans = GeoIdentityTransform;
627     crec = (DBWclientRec *) window->w_clientData;
628     if (option == SEL_VISIBLE)
629     {
630 	int i;
631 	for (i = 0; i < DBNumUserLayers; i++)
632 	{
633 	    if((TTMaskHasType(&mask, i)) && !(TTMaskHasType(&crec->dbw_visibleLayers, i)))
634 		TTMaskClearType(&mask, i);
635 	}
636     }
637     SelectArea(&scx, &mask, crec->dbw_bitmask, globmatch);
638 }
639 
640 /*
641  * ----------------------------------------------------------------------------
642  *
643  * cmdIntersectArea --
644  *
645  * 	This is a utility procedure used by CmdSelect to do area
646  *	selection itersect.
647  *
648  * Results:
649  *	None.
650  *
651  * Side effects:
652  *	The selection is pared down to contain only the part of the
653  *	original selection that intersects with the type "layer".
654  *
655  * ----------------------------------------------------------------------------
656  */
657 
658 void
cmdIntersectArea(layer)659 cmdIntersectArea(layer)
660     char *layer;		/* The layer to intersect with */
661 {
662     TileType ttype;
663     SearchContext scx;
664     int windowMask, xMask;
665     DBWclientRec *crec;
666     MagWindow *window;
667     char *lptr;
668     bool negate = FALSE;
669 
670     bzero(&scx, sizeof(SearchContext));
671     window = ToolGetBoxWindow(&scx.scx_area, &windowMask);
672     if (window == NULL)
673     {
674 	TxPrintf("The box isn't in a window.\n");
675 	return;
676     }
677 
678     /* Since the box may actually be in multiple windows, we have to
679      * be a bit careful.  If the box is only in one window, then there's
680      * no problem.  If it's in more than window, the cursor must
681      * disambiguate the windows.
682      */
683 
684     xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
685     if ((windowMask & ~xMask) != 0)
686     {
687 	window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
688         xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
689 	if ((windowMask & xMask) == 0)
690 	{
691 	    TxPrintf("The box is in more than one window;  use the cursor\n");
692 	    TxPrintf("to select the one you want to select from.\n");
693 	    return;
694 	}
695     }
696 
697     scx.scx_use = (CellUse *) window->w_surfaceID;
698     scx.scx_trans = GeoIdentityTransform;
699     crec = (DBWclientRec *) window->w_clientData;
700 
701     /* Special behavior:  "!" or "~" in front of layer name intersects	*/
702     /* with NOT(layer).							*/
703 
704     lptr = layer;
705     if ((*lptr == '~') || (*lptr == '!'))
706     {
707 	negate = TRUE;
708 	lptr++;
709     }
710 
711     ttype = DBTechNameType(lptr);
712     if (ttype < 0) {
713 	TxError("Cannot parse layer type \"%s\".\n", layer);
714 	return;
715     }
716     SelectIntersect(&scx, ttype, crec->dbw_bitmask, negate);
717 }
718 
719 /*
720  * ----------------------------------------------------------------------------
721  *
722  * CmdSelect --
723  *
724  * Implement the "select" command.
725  *
726  * Usage:
727  *	select [option args]
728  *
729  * Results:
730  *	None.
731  *
732  * Side effects:
733  *	The current selection is modified.  See the user documentation
734  *	for all the possible things this command can do.
735  *
736  * ----------------------------------------------------------------------------
737  */
738 
739 void
CmdSelect(w,cmd)740 CmdSelect(w, cmd)
741     MagWindow *w;
742     TxCommand *cmd;
743 {
744     TileTypeBitMask mask;
745     SearchContext scx;
746     DBWclientRec *crec;
747     MagWindow *window;
748 
749     /* The two tables below define the allowable selection options, and
750      * also the message printed out by ":select help" to describe the
751      * options (can't use the same table for both because of the presence
752      * of the "more" option).  Note that there's one more entry in the
753      * second table, due to a help message for ":select" with no arguments.
754      */
755 
756     static char *cmdSelectOption[] =
757     {
758 	"area",
759 	"visible",
760 	"cell",
761 	"labels",
762 	"intersection",
763 	"clear",
764 	"flat",
765 	"help",
766 	"keep",
767 	"move",
768 	"pick",
769 	"save",
770 	"feedback",
771 	"bbox",
772 	"box",
773 	"chunk",
774 	"region",
775 	"net",
776 	"short",
777 	NULL
778     };
779     static char *cmdSelectMsg[] =
780     {
781 	"[more | less | nocycle] [layers]\n"
782         "                                [de]select paint chunk/region/net under\n"
783  	"                                cursor, or [de]select subcell if cursor\n"
784 	"	                         over space",
785 	"[more | less] area [layers]     [de]select all info under box in layers",
786 	"[more | less] visible [layers]  [de]select all visible info under box in layers",
787 	"[more | less | top] cell [name] [de]select cell under cursor, or \"name\"",
788 	"[do | no] labels		 [do not] select subcell labels",
789 	"[more | less] intersection [layers]	[de]select intersection of layers",
790 	"clear                           clear selection",
791 	"flat				 flatten the contents of the selection",
792 	"help                            print this message",
793 	"keep                            copy the selection to the layout",
794 	"move                            move selection to a location in the layout",
795 	"pick                            delete selection from layout",
796 	"save file                       save selection on disk in file.mag",
797 	"feedback [style]		 copy selection to feedback",
798 	"bbox				 return the bounding box of the selection",
799 	"[more | less] box | chunk | region | net [layers]\n"
800 	"				 [de]select chunk/region/net specified by\n"
801 	"				 the lower left corner of the current box",
802 	"short name1 name2		 find shorting path between two labels",
803 	NULL
804     };
805 
806     static TileType type = TT_SELECTBASE-1;
807 				/* Type of material being pointed at.
808 				 * Remembered across commands so that when
809 				 * multiples types are pointed to, consecutive
810 				 * selections will cycle through them.
811 				 */
812     static Rect lastArea = {-100, -100, -200, -200};
813 				/* Used to remember region around what was
814 				 * pointed at in the last select command:  a
815 				 * new selection in this area causes the next
816 				 * bigger thing to be selected.
817 				 */
818     static int lastCommand;	/* Serial number of last command:  the next
819 				 * bigger thing is only selected when there
820 				 * are several select commands in a row.
821 				 */
822     static Rect chunkSelection;	/* Used to remember the size of the last chunk
823 				 * selected.
824 				 */
825     static int level = 0;	/* How big a piece to select.  See definitions
826 				 * below.
827 				 */
828     static CellUse *lastUse;	/* The last cellUse selected.  Used to step
829 				 * through multiple uses underneath the cursor.
830 				 */
831     static Point lastIndices;	/* The array indices of the last cell selected.
832 				 * also used to step through multiple uses.
833 				 */
834     static bool lessCycle = FALSE, lessCellCycle = FALSE;
835     char path[200], *printPath, **msg, **optionArgs, *feedtext, *pstr, *globmatch;
836     TerminalPath tpath;
837     CellUse *use;
838     CellDef *rootBoxDef;
839     Transform trans, rootTrans, tmp1;
840     Point p, rootPoint;
841     Rect r, selarea;
842     ExtRectList *rlist;
843     int option;
844     int feedstyle;
845     bool layerspec;
846     bool degenerate;
847     bool more = FALSE, less = FALSE, samePlace = TRUE;
848     unsigned char labelpolicy = SEL_DO_LABELS;
849 #ifdef MAGIC_WRAPPER
850     char *tclstr;
851     Tcl_Obj *lobj;
852 #endif
853 
854 /* How close two clicks must be to be considered the same point: */
855 
856 #define MARGIN 2
857 
858     globmatch = NULL;
859     bzero(&scx, sizeof(SearchContext));
860     windCheckOnlyWindow(&w, DBWclientID);
861     if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
862     {
863 	TxError("Put the cursor in a layout window\n");
864 	return;
865     }
866 
867     /* See if "more" was given.  If so, just strip off the "more" from
868      * the argument list and set the "more" flag.  Similarly for options
869      * "less", "do", "no", "nocycle", "top", and "cell".
870      */
871 
872     if (cmd->tx_argc >= 2)
873     {
874 	int arg1len = strlen(cmd->tx_argv[1]);
875 
876 	optionArgs = &cmd->tx_argv[1];
877 	if (!strncmp(cmd->tx_argv[1], "more", arg1len))
878 	{
879 	    more = TRUE;
880 	    less = FALSE;
881 	    optionArgs = &cmd->tx_argv[2];
882 	    cmd->tx_argc--;
883 	}
884 	else if (!strncmp(cmd->tx_argv[1], "less", arg1len))
885 	{
886 	    more = FALSE;
887 	    less = TRUE;
888 	    optionArgs = &cmd->tx_argv[2];
889 	    cmd->tx_argc--;
890 	}
891 	else if (!strncmp(cmd->tx_argv[1], "nocycle", arg1len))
892 	{
893 	    samePlace = FALSE;
894 	    labelpolicy = SEL_NO_LABELS;
895 	    more = FALSE;
896 	    less = FALSE;
897 	    type = TT_SELECTBASE - 1;	   /* avoid cycling between types */
898 	    optionArgs = &cmd->tx_argv[2];
899 	    cmd->tx_argc--;
900 	}
901 	else if (!strncmp(cmd->tx_argv[1], "same", arg1len))
902 	{
903 	    /* Force this to be the same as the last selection command,	*/
904 	    /* even if there were other commands in between.		*/
905 	    lastCommand = TxCommandNumber - 1;
906 	    optionArgs = &cmd->tx_argv[2];
907 	    cmd->tx_argc--;
908 	}
909 	else if (!strncmp(cmd->tx_argv[1], "do", arg1len))
910 	{
911 	    labelpolicy = SEL_DO_LABELS;
912 	    optionArgs = &cmd->tx_argv[2];
913 	    cmd->tx_argc--;
914 	}
915 	else if (!strncmp(cmd->tx_argv[1], "no", arg1len))
916 	{
917 	    labelpolicy = SEL_NO_LABELS;
918 	    optionArgs = &cmd->tx_argv[2];
919 	    cmd->tx_argc--;
920 	}
921 	else if (!strncmp(cmd->tx_argv[1], "simple", arg1len))
922 	{
923 	    labelpolicy = SEL_SIMPLE_LABELS;
924 	    optionArgs = &cmd->tx_argv[2];
925 	    cmd->tx_argc--;
926 	}
927 
928 	else if (!strncmp(cmd->tx_argv[1], "top", arg1len))
929 	{
930 	    if ((cmd->tx_argc >= 3) && !strncmp(cmd->tx_argv[2],
931 			"cell", strlen(cmd->tx_argv[2])))
932 		optionArgs = &cmd->tx_argv[2];
933 	}
934     }
935 
936     /* Check the option for validity. */
937 
938     if (cmd->tx_argc == 1)
939 	option = SEL_DEFAULT;
940     else
941     {
942 	char *fileName;
943 
944 	option = Lookup(optionArgs[0], cmdSelectOption);
945 	if (option < 0 && cmd->tx_argc != 2)
946 	{
947 	    TxError("\"%s\" isn't a valid select option.\n", cmd->tx_argv[1]);
948 	    option = SEL_HELP;
949 	    cmd->tx_argc = 2;
950 	}
951 	else if (option < 0)
952 	{
953 	    option = SEL_DEFAULT;
954 	    if (more || less)
955 		optionArgs = &cmd->tx_argv[1];
956 	    else
957 		optionArgs = &cmd->tx_argv[0];
958 	}
959 
960 	/* options other than SEL_DEFAULT and the ones that cycle
961 	 * through (SEL_BOX/CHUNK/REGION/NET) force "level" back
962 	 * to 0.
963 	 */
964 	if (option != SEL_BOX && option != SEL_CHUNK && option !=
965 		SEL_REGION && option != SEL_NET)
966 	    level = 0;
967     }
968 
969 #ifndef NO_SIM_MODULE
970     SimRecomputeSel = TRUE;
971 #endif
972 
973     switch (option)
974     {
975 	/*--------------------------------------------------------------------
976 	 * Select everything under the box, perhaps looking only at
977 	 * particular layers.
978 	 *--------------------------------------------------------------------
979 	 */
980 
981 	case SEL_AREA:
982 	    if (cmd->tx_argc > 4)
983 	    {
984 		usageError:
985 		TxError("Bad arguments:\n    select %s\n",
986 			cmdSelectMsg[option+1]);
987 		return;
988 	    }
989 	    if (cmd->tx_argc == 4)
990 		globmatch = optionArgs[2];	/* Label matching by glob */
991 	    if (!(more || less)) SelectClear();
992 	    if (cmd->tx_argc >= 3)
993 		cmdSelectArea(optionArgs[1], less, option, globmatch);
994 	    else cmdSelectArea("*,label,subcell", less, option, globmatch);
995 	    return;
996 
997 	/*--------------------------------------------------------------------
998 	 * Select everything under the box, perhaps looking only at
999 	 * particular layers, but only if its visible.
1000 	 *--------------------------------------------------------------------
1001 	 */
1002 
1003 	case SEL_VISIBLE:
1004 	    if (cmd->tx_argc > 3) goto usageError;
1005 	    if (!(more || less)) SelectClear();
1006 	    if (cmd->tx_argc == 3)
1007 		cmdSelectArea(optionArgs[1], less, option, globmatch);
1008 	    else cmdSelectArea("*,label,subcell", less, option, globmatch);
1009 	    return;
1010 
1011 	/*--------------------------------------------------------------------
1012 	 * Select area that is the intersection of all the specified layers
1013 	 *--------------------------------------------------------------------
1014 	 */
1015 
1016 	case SEL_INTERSECT:
1017 	    if (cmd->tx_argc > 3) goto usageError;
1018 	    cmdIntersectArea(optionArgs[1]);
1019 	    return;
1020 
1021 	/*--------------------------------------------------------------------
1022 	 * Clear out all of the material in the selection.
1023 	 *--------------------------------------------------------------------
1024 	 */
1025 
1026 	case SEL_CLEAR:
1027 	    if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
1028 	    SelectClear();
1029 	    return;
1030 
1031 	case SEL_LABELS:
1032 	    SelectDoLabels = labelpolicy;
1033 	    if (SelectDoLabels)
1034 		TxPrintf("Selection includes subcell labels\n");
1035 	    else
1036 		TxPrintf("Selection ignores subcell labels\n");
1037 	    return;
1038 
1039 	/*--------------------------------------------------------------------
1040 	 * Print out help information.
1041 	 *--------------------------------------------------------------------
1042 	 */
1043 
1044 	case SEL_HELP:
1045 	    TxPrintf("Selection commands are:\n");
1046 	    for (msg = &(cmdSelectMsg[0]); *msg != NULL; msg++)
1047 		TxPrintf("    select %s\n", *msg);
1048 	    return;
1049 
1050 	/*--------------------------------------------------------------------
1051 	 * Print or return the bounding box of the selection
1052 	 *--------------------------------------------------------------------
1053 	 */
1054 
1055 	case SEL_BBOX:
1056 	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
1057 
1058 #ifdef MAGIC_WRAPPER
1059 	    lobj = Tcl_NewListObj(0, NULL);
1060 	    Tcl_ListObjAppendElement(magicinterp, lobj,
1061 			Tcl_NewIntObj(selarea.r_xbot));
1062 	    Tcl_ListObjAppendElement(magicinterp, lobj,
1063 			Tcl_NewIntObj(selarea.r_ybot));
1064 	    Tcl_ListObjAppendElement(magicinterp, lobj,
1065 			Tcl_NewIntObj(selarea.r_xtop));
1066 	    Tcl_ListObjAppendElement(magicinterp, lobj,
1067 			Tcl_NewIntObj(selarea.r_ytop));
1068 	    Tcl_SetObjResult(magicinterp, lobj);
1069 #else
1070 	    TxPrintf("Select bounding box: %d %d %d %d\n",
1071 		    selarea.r_xbot, selarea.r_ybot,
1072 		    selarea.r_xtop, selarea.r_ytop);
1073 #endif
1074 	    return;
1075 
1076 	/*--------------------------------------------------------------------
1077 	 * Make a copy of the selection at its present loction but do not
1078 	 * clear the selection.
1079 	 *--------------------------------------------------------------------
1080 	 */
1081 
1082 	 case SEL_KEEP:
1083 	    if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
1084 	    SelectAndCopy1();
1085 	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
1086 	    DBWHLRedraw(SelectRootDef, &selarea, FALSE);
1087 	    return;
1088 
1089 	/*--------------------------------------------------------------------
1090 	 * Move the selection relative to the cell def
1091 	 *--------------------------------------------------------------------
1092 	 */
1093 
1094 	 case SEL_MOVE:
1095 	    if ((more) || (less) || (cmd->tx_argc != 4)) goto usageError;
1096 
1097 	    p.p_x = cmdParseCoord(w, cmd->tx_argv[2], FALSE, TRUE);
1098 	    p.p_y = cmdParseCoord(w, cmd->tx_argv[3], FALSE, FALSE);
1099 
1100 	    /* Erase first, then recompute the transform */
1101 	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
1102 	    DBWHLRedraw(SelectRootDef, &selarea, TRUE);
1103 
1104 	    GeoTransTranslate(p.p_x, p.p_y,
1105 			&GeoIdentityTransform, &SelectUse->cu_transform);
1106 
1107 	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
1108 	    DBWHLRedraw(SelectRootDef, &selarea, FALSE);
1109 	    return;
1110 
1111 	/*--------------------------------------------------------------------
1112 	 * Delete the selection from the layout, but don't clear the selection
1113 	 * cell.  This allows the selection to be dragged around independently
1114 	 * of the layout.
1115 	 *--------------------------------------------------------------------
1116 	 */
1117 
1118 	 case SEL_PICK:
1119 	    if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
1120 	    SelectDelete("picked", FALSE);
1121 	    DBWHLRedraw(SelectRootDef, &selarea, FALSE);
1122 	    return;
1123 
1124 	/*--------------------------------------------------------------------
1125 	 * Flatten the contents of the selection cell, but keep the result
1126 	 * within the selection cell.
1127 	 *--------------------------------------------------------------------
1128 	 */
1129 
1130 	case SEL_FLAT:
1131 	    if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
1132 	    SelectFlat();
1133 	    return;
1134 
1135 	/*--------------------------------------------------------------------
1136 	 * Save the selection as a new Magic cell on disk.
1137 	 *--------------------------------------------------------------------
1138 	 */
1139 
1140 	 case SEL_SAVE:
1141 	    if (cmd->tx_argc != 3) goto usageError;
1142 
1143 	    /* Be sure to paint DRC check information into the cell before
1144 	     * saving it!  Otherwise DRC problems may not be detected.  Also
1145 	     * be sure to adjust labels in the cell.
1146 	     */
1147 
1148 	    DBAdjustLabels(SelectDef, &TiPlaneRect);
1149 	    DBPaintPlane(SelectDef->cd_planes[PL_DRC_CHECK],
1150 		    &SelectDef->cd_bbox,
1151 		    DBStdPaintTbl(TT_CHECKPAINT, PL_DRC_CHECK),
1152 		    (PaintUndoInfo *) NULL);
1153 
1154 	    DBUpdateStamps();
1155 	    cmdSaveCell(SelectDef, cmd->tx_argv[2], FALSE, FALSE);
1156 	    return;
1157 
1158 	/*--------------------------------------------------------------------
1159 	 * Copy the selection into a feedback area for permanent display
1160 	 *--------------------------------------------------------------------
1161 	 */
1162 	 case SEL_FEEDBACK:
1163 	    feedtext = NULL;
1164 	    feedstyle = STYLE_ORANGE1;
1165 	    if (cmd->tx_argc > 2)
1166 	    {
1167 		/* Get style (To do) */
1168 		feedstyle = GrGetStyleFromName(cmd->tx_argv[2]);
1169 		if (feedstyle == -1)
1170 		{
1171 		    TxError("Unknown style %s\n", cmd->tx_argv[2]);
1172 		    TxError("Use a number or one of the long names in the"
1173 					" .dstyle file\n");
1174 		    return;
1175 		}
1176 		if (cmd->tx_argc > 3)
1177 		    feedtext = cmd->tx_argv[3];
1178 	    }
1179 	    SelCopyToFeedback(SelectRootDef, SelectUse, feedstyle,
1180 			(feedtext == NULL) ? "selection" : feedtext);
1181 	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
1182 	    DBWHLRedraw(SelectRootDef, &selarea, FALSE);
1183 	    return;
1184 
1185 	/*--------------------------------------------------------------------
1186 	 * Given a net selection and two labels, determine the shortest
1187 	 * connecting path between the two labels.
1188 	 *--------------------------------------------------------------------
1189 	 */
1190 	case SEL_SHORT:
1191 	    if (cmd->tx_argc != 4) goto usageError;
1192 	    rlist = SelectShort(cmd->tx_argv[2], cmd->tx_argv[3]);
1193 
1194 	    if (rlist == NULL)
1195 	    {
1196 		TxError("No shorting path found between source and destination!\n");
1197 		return;
1198 	    }
1199 
1200 	    /* Delete selection and replace with contents of rlist */
1201 
1202 	    /* (To do:  Alternately just return a list of the contents	*/
1203 	    /* of rlist)						*/
1204 	    SelectClear();
1205 
1206 	    while (rlist != NULL)
1207 	    {
1208 		/* Paint rlist back into SelectDef */
1209 		DBPaint(SelectDef, &rlist->r_r, rlist->r_type);
1210 
1211 		/* cleanup as we go */
1212 		freeMagic(rlist);
1213 		rlist = rlist->r_next;
1214 	    }
1215 
1216 	    /* Force erase and redraw of the selection */
1217 	    DBReComputeBbox(SelectDef);
1218 	    DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS,
1219 			(TileTypeBitMask *)NULL);
1220 	    GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
1221 	    DBWHLRedraw(SelectRootDef, &selarea, FALSE);
1222 	    break;
1223 
1224 	case SEL_BOX: case SEL_CHUNK: case SEL_REGION: case SEL_NET:
1225 	    if (cmd->tx_argc > 3) goto usageError;
1226 	    if (cmd->tx_argc == 3)
1227 		layerspec = TRUE;
1228 	    else
1229 		layerspec = FALSE;
1230 	    goto Okay;
1231 
1232 	/*--------------------------------------------------------------------
1233 	 * The default case (no args):  see what's under the cursor.  Select
1234 	 * paint if there is any, else select a cell.  In both cases,
1235 	 * multiple clicks cycle through larger and larger selections.  The
1236 	 * SEL_CELL option also comes here (to share initialization code) but
1237 	 * quickly branches away.
1238 	 *--------------------------------------------------------------------
1239 	 */
1240 
1241 	case SEL_DEFAULT:
1242 	    if (cmd->tx_argc > 2) goto usageError;
1243 	    if (cmd->tx_argc == 2)
1244 		layerspec = TRUE;
1245 	    else
1246 		layerspec = FALSE;
1247 	    goto Okay;
1248 	case SEL_CELL:
1249 	    layerspec = FALSE;
1250 	    degenerate = FALSE;
1251 Okay:
1252 	    if (!(more || less)) SelectClear();
1253 	    if (option == SEL_BOX || option == SEL_CHUNK || option == SEL_REGION
1254 			|| option == SEL_NET)
1255 	    {
1256 		int windowMask, xMask;
1257 
1258 		if (!ToolGetBox (&rootBoxDef, NULL)) {
1259 		    TxError ("Box tool must be present\n");
1260 		    return;
1261 		}
1262 		window = ToolGetBoxWindow (&scx.scx_area, &windowMask);
1263 		if (!window)
1264 		    TxError ("Box tool does not exist in any window\n");
1265 		xMask = ((DBWclientRec *)window->w_clientData)->dbw_bitmask;
1266 		if ((windowMask & ~xMask) != 0) {
1267 		    window = CmdGetRootPoint ((Point *) NULL, (Rect *) NULL);
1268 		    xMask = ((DBWclientRec *)window->w_clientData)->dbw_bitmask;
1269 		    if (windowMask & xMask == 0) {
1270 			TxError("Box present in multiple windows; use the"
1271 				"cursor\nto select the one you want\n");
1272 			return;
1273 		    }
1274 		}
1275 		scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
1276 		scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1;
1277 		degenerate = TRUE;
1278 	    }
1279 	    else
1280 	    {
1281 	        window = CmdGetRootPoint((Point *) NULL, &scx.scx_area);
1282 	    }
1283 	    if (window == NULL) return;
1284 	    scx.scx_use = (CellUse *) window->w_surfaceID;
1285 	    scx.scx_trans = GeoIdentityTransform;
1286 	    crec = (DBWclientRec *) window->w_clientData;
1287 	    DBSeeTypesAll(scx.scx_use, &scx.scx_area, crec->dbw_bitmask, &mask);
1288 	    TTMaskAndMask(&mask, &crec->dbw_visibleLayers);
1289 	    TTMaskAndMask(&mask, &DBAllButSpaceAndDRCBits);
1290 
1291 	    /* See if we're pointing at the same place as we were the last time
1292 	     * this command was invoked, and if this command immediately follows
1293 	     * another selection comand.  If not, it is important to set lastUse
1294 	     * to NULL, otherwise trouble occurs if lastUse is an instance that
1295 	     * was deleted (note that this is not foolproof:  deleting an
1296 	     * instance followed by selecting an instance that was occupying the
1297 	     * same space WILL cause a crash).
1298 	     */
1299 
1300 	    if (!GEO_ENCLOSE(&cmd->tx_p, &lastArea)
1301 		    || ((lastCommand + 1) != TxCommandNumber))
1302 	    {
1303 		samePlace = FALSE;
1304 		lastUse = NULL;
1305 	    }
1306 
1307 	    lastArea.r_xbot = cmd->tx_p.p_x - MARGIN;
1308 	    lastArea.r_ybot = cmd->tx_p.p_y - MARGIN;
1309 	    lastArea.r_xtop = cmd->tx_p.p_x + MARGIN;
1310 	    lastArea.r_ytop = cmd->tx_p.p_y + MARGIN;
1311 	    lastCommand = TxCommandNumber;
1312 
1313 	    /* If there's material under the cursor, select some paint.
1314 	     * Repeated selections at the same place result in first a
1315 	     * chunk being selected, then a region of a particular type,
1316 	     * then a whole net.
1317 	     */
1318 
1319 	    if (!TTMaskIsZero(&mask) && (option != SEL_CELL))
1320 	    {
1321 		if (layerspec == TRUE)
1322 		{
1323 		    /* User specified a layer.  Use the smallest type
1324 		     * specified by the user and present under the
1325 		     * box/cursor to begin the selection
1326 		     */
1327 		    TileTypeBitMask uMask;
1328 
1329 		    if (CmdParseLayers (optionArgs[1], &uMask))
1330 		    {
1331 			if (TTMaskEqual (&uMask, &DBSpaceBits))
1332 			    CmdParseLayers ("*,label", &uMask);
1333 		    }
1334 		    else
1335 		    {
1336 			TxError ("Invalid layer specification\n");
1337 			return;
1338 		    }
1339 
1340 		    TTMaskAndMask (&mask, &uMask);
1341 
1342 		    if (TTMaskIsZero(&mask))
1343 		    {
1344 			/* If the area was degenerate (point or line box) */
1345 			/* try looking in the other direction before	  */
1346 			/* giving up (added by Tim 3/18/07)		  */
1347 
1348 			if (degenerate)
1349 			{
1350 			    scx.scx_area.r_xtop--;
1351 			    scx.scx_area.r_ytop--;
1352 			    scx.scx_area.r_xbot--;
1353 			    scx.scx_area.r_ybot--;
1354 
1355 			    DBSeeTypesAll(scx.scx_use, &scx.scx_area,
1356 					crec->dbw_bitmask, &mask);
1357 			    TTMaskAndMask(&mask, &crec->dbw_visibleLayers);
1358 			    TTMaskAndMask(&mask, &DBAllButSpaceAndDRCBits);
1359 			    TTMaskAndMask (&mask, &uMask);
1360 			    if (TTMaskIsZero(&mask))
1361 			    {
1362 				TxError ("No paint of this type under "
1363 					"or next to the cursor/box\n");
1364 				return;
1365 			    }
1366 			}
1367 			else
1368 			{
1369 			    TxError ("No paint of this type under the cursor/box\n");
1370 			    return;
1371 			}
1372 		    }
1373 		}
1374 
1375 		/* Set connectivity searching level */
1376 
1377 		if (option == SEL_CHUNK || option == SEL_REGION || option == SEL_NET)
1378 		{
1379 		    samePlace = TRUE;
1380 		    level = option;
1381 
1382 		    if (level != SEL_CHUNK)
1383 		    {
1384 			int count = 0;
1385 			for (type++; count <= DBNumUserLayers; type++, count++)
1386 			{
1387 			    if (type >= DBNumUserLayers)
1388 				type = TT_SELECTBASE;
1389 			    if (TTMaskHasType(&mask, type)) break;
1390 			}
1391 		    }
1392 		}
1393 		else
1394 		{
1395 		    if (samePlace && lessCycle == less)
1396 		    {
1397 			level++;
1398 			if (level > SEL_NET) level = SEL_CHUNK;
1399 		    }
1400 		    else level = SEL_CHUNK;
1401 
1402 		    if ((level == 1) || (level != SEL_CHUNK &&
1403 				!TTMaskHasType (&mask, type))) {
1404 			/* User specified a new mask, and the current tile
1405 			 * type being expanded is not in the set of types
1406 			 * which the user wants us to use => reset level
1407 			 */
1408 			level = SEL_CHUNK;
1409 			SelectClear();
1410 		    }
1411 		}
1412 
1413 		lessCycle = less;
1414 
1415 		if (level == SEL_CHUNK)
1416 		{
1417 		    /* Pick a tile type to use for selection.  If there are
1418 		     * several different types under the cursor, pick one of
1419 		     * them.  This code remembers which type was used to
1420 		     * choose last time, so that consecutive selections will
1421 		     * use different types.
1422 		     */
1423 
1424 		    int count = 0;
1425 		    for (type++; count <= DBNumUserLayers; type++, count++)
1426 		    {
1427 			if (type >= DBNumUserLayers)	/* used to be DBNumTypes */
1428 			    type = TT_SELECTBASE;
1429 			if (TTMaskHasType(&mask, type)) break;
1430 		    }
1431 
1432 		    /* Check for selection between DBNumUserLayers & DBNumTypes */
1433 		    /* Normally we don't want to use these types;  only if one  */
1434 		    /* is the only bit set in the list.				*/
1435 
1436 		    if (count == DBNumUserLayers)
1437 			for (type = DBNumUserLayers; type < DBNumTypes; type++)
1438 			    if (TTMaskHasType(&mask, type)) break;
1439 
1440 		    /* Sanity check */
1441 		    if (type == DBNumTypes) return;
1442 
1443 		    SelectChunk(&scx, type, crec->dbw_bitmask, &chunkSelection, less);
1444 		    if (!less)
1445 		      DBWSetBox(scx.scx_use->cu_def, &chunkSelection);
1446 		}
1447 		if (level == SEL_REGION)
1448 		{
1449 		    /* If a region has the same size as the preceding chunk,
1450 		     * then we haven't added anything to the selection, so
1451 		     * go on immediately and select the whole net.
1452 		     */
1453 
1454 		    Rect area;
1455 
1456 		    SelectRegion(&scx, type, crec->dbw_bitmask, &area, less);
1457 		    if (GEO_SURROUND(&chunkSelection, &area))
1458 			level = SEL_NET;
1459 		}
1460 		if (level == SEL_NET)
1461 		{
1462 		    SelectNet(&scx, type, crec->dbw_bitmask, (Rect *) NULL, less);
1463 		}
1464 
1465 		return;
1466 	    }
1467 
1468 	/*--------------------------------------------------------------------
1469 	 * We get here either if the SEL_CELL option is requested, or under
1470 	 * the SEL_DEFAULT case where there's no paint under the mouse.  In
1471 	 * this case, select a subcell.
1472 	 *--------------------------------------------------------------------
1473 	 */
1474 
1475 	    if (layerspec == TRUE)
1476 	    {
1477 		TileTypeBitMask uMask;
1478 
1479 		if (CmdParseLayers (optionArgs[1], &uMask))
1480 		{
1481 		    TxError ("No paint of this type under the cursor/box\n");
1482 		}
1483 		else
1484 		{
1485 		    TxError ("Invalid layer specification\n");
1486 		}
1487 		return;
1488 	    }
1489 
1490 	    if (cmd->tx_argc > 3) goto usageError;
1491 
1492 	    /* If an explicit cell use id is provided, look for that cell
1493 	     * and select it.  In this case, defeat all of the "multiple
1494 	     * click" code.
1495 	     */
1496 
1497 	    if ((cmd->tx_argc == 3) && (optionArgs == &cmd->tx_argv[2]) &&
1498 		(more == FALSE) && (less == FALSE))
1499 	    {
1500 		use = lastUse = scx.scx_use;
1501 		p.p_x = scx.scx_use->cu_xlo;
1502 		p.p_y = scx.scx_use->cu_ylo;
1503 		trans = GeoIdentityTransform;
1504 		printPath = scx.scx_use->cu_id;
1505 	    }
1506 	    else if (cmd->tx_argc == 3)
1507 	    {
1508 		SearchContext scx2;
1509 
1510 		bzero(&scx2, sizeof(SearchContext));
1511 		DBTreeFindUse(optionArgs[1], scx.scx_use, &scx2);
1512 		use = scx2.scx_use;
1513 		if (use == NULL)
1514 		{
1515 		    TxError("Couldn't find a cell use named \"%s\"\n",
1516 			    optionArgs[1]);
1517 		    return;
1518 		}
1519 		trans = scx2.scx_trans;
1520 		p.p_x = scx2.scx_x;
1521 		p.p_y = scx2.scx_y;
1522 		printPath = optionArgs[1];
1523 		samePlace = FALSE;
1524 		lastArea.r_xbot = lastArea.r_ybot = -1000;
1525 		lastArea.r_xtop = lastArea.r_ytop = -1000;
1526 	    }
1527 	    else
1528 	    {
1529 		/* Find the cell underneath the cursor.  If this is a
1530 		 * second or later click at the same position, select
1531 		 * the "next" cell underneath the point (see comments
1532 		 * in DBSelectCell() for what "next" means).
1533 		 */
1534 
1535 		tpath.tp_first = tpath.tp_next = path;
1536 		tpath.tp_last = &path[(sizeof path) - 2];
1537 		if ((lastUse == scx.scx_use) || !samePlace || (lessCellCycle != less))
1538 		    lastUse = NULL;
1539 		lessCellCycle = less;
1540 		use = DBSelectCell(scx.scx_use, lastUse, &lastIndices,
1541 			&scx.scx_area, crec->dbw_bitmask, &trans, &p, &tpath);
1542 
1543 		/* Use the window's root cell if nothing else is found. */
1544 
1545 		if (use == NULL)
1546 		{
1547 		    use = lastUse = scx.scx_use;
1548 		    p.p_x = scx.scx_use->cu_xlo;
1549 		    p.p_y = scx.scx_use->cu_ylo;
1550 		    trans = GeoIdentityTransform;
1551 		    printPath = scx.scx_use->cu_id;
1552 		}
1553 		else
1554 		{
1555 		    printPath = strchr(path, '/');
1556 		    if (printPath == NULL)
1557 			printPath = path;
1558 		    else printPath++;
1559 		}
1560 	    }
1561 
1562 	    lastUse = use;
1563 	    lastIndices = p;
1564 
1565 	    /* The translation stuff is funny, since we got one
1566 	     * element of the array, but not necessarily the
1567 	     * lower-left element.  To get the transform for the
1568 	     * array as a whole, subtract off for the indx of
1569 	     * the element.
1570 	     */
1571 
1572 	    GeoInvertTrans(DBGetArrayTransform(use, p.p_x, p.p_y), &tmp1);
1573 	    GeoTransTrans(&tmp1, &trans, &rootTrans);
1574 
1575 	    if (less)
1576 	      SelectRemoveCellUse(use, &rootTrans);
1577 	    else
1578 	      SelectCell(use, scx.scx_use->cu_def, &rootTrans, samePlace);
1579 	    GeoTransRect(&trans, &use->cu_def->cd_bbox, &r);
1580 	    DBWSetBox(scx.scx_use->cu_def, &r);
1581 
1582 #ifdef MAGIC_WRAPPER
1583 	    /* Remove any backslash escapes so that Tcl_escape() doesn't
1584 	     * double-escape them.
1585 	     */
1586 	    for (pstr = printPath; *pstr != '\0';)
1587 		if ((*pstr == '\\') && ((*(pstr + 1) == '[') || (*(pstr + 1) == ']')))
1588 		    memmove(pstr, pstr + 1, 1 + strlen(pstr + 1));
1589 		else pstr++;
1590 	    tclstr = Tcl_escape(printPath);
1591 	    Tcl_SetResult(magicinterp, tclstr, TCL_DYNAMIC);
1592 #else
1593 	    TxPrintf("Selected cell is %s (%s)\n", use->cu_def->cd_name,
1594 		    printPath);
1595 #endif
1596 	    return;
1597     }
1598 }
1599 
1600 /*
1601  *-----------------------------------------------------------------------
1602  * Callback functions used with CmdSetLabel to change individual labels
1603  *-----------------------------------------------------------------------
1604  */
1605 
1606 int
cmdLabelTextFunc(label,cellUse,transform,text)1607 cmdLabelTextFunc(label, cellUse, transform, text)
1608     Label *label;
1609     CellUse *cellUse;
1610     Transform *transform;
1611     char *text;
1612 {
1613     Label *newlab;
1614     CellDef *cellDef = cellUse->cu_def;
1615 #ifdef MAGIC_WRAPPER
1616     Tcl_Obj *lobj;
1617 #endif
1618 
1619     if (text == NULL)
1620     {
1621 #ifdef MAGIC_WRAPPER
1622 	lobj = Tcl_GetObjResult(magicinterp);
1623 	Tcl_ListObjAppendElement(magicinterp, lobj,
1624 			Tcl_NewStringObj(label->lab_text, -1));
1625 	Tcl_SetObjResult(magicinterp, lobj);
1626 #else
1627 	TxPrintf("%s\n", label->lab_text);
1628 #endif
1629     }
1630     else
1631     {
1632 	if (strcmp(text, label->lab_text))
1633 	{
1634 	    newlab = DBPutFontLabel(cellDef, &label->lab_rect, label->lab_font,
1635 			label->lab_size, label->lab_rotate, &label->lab_offset,
1636 			label->lab_just, text, label->lab_type, label->lab_flags,
1637 			label->lab_port);
1638 	    DBEraseLabelsByContent(cellDef, &label->lab_rect, -1, label->lab_text);
1639 	    DBWLabelChanged(cellDef, newlab, DBW_ALLWINDOWS);
1640 	}
1641     }
1642     return 0;
1643 }
1644 
1645 int
cmdLabelRotateFunc(label,cellUse,transform,value)1646 cmdLabelRotateFunc(label, cellUse, transform, value)
1647     Label *label;
1648     CellUse *cellUse;
1649     Transform *transform;
1650     int *value;
1651 {
1652     CellDef *cellDef = cellUse->cu_def;
1653 #ifdef MAGIC_WRAPPER
1654     Tcl_Obj *lobj;
1655 #endif
1656 
1657     if (value == NULL)
1658     {
1659 #ifdef MAGIC_WRAPPER
1660 	lobj = Tcl_GetObjResult(magicinterp);
1661 	Tcl_ListObjAppendElement(magicinterp, lobj,
1662 			Tcl_NewIntObj(label->lab_rotate));
1663 	Tcl_SetObjResult(magicinterp, lobj);
1664 #else
1665 	TxPrintf("%d\n", label->lab_rotate);
1666 #endif
1667     }
1668     else
1669     {
1670 	DBUndoEraseLabel(cellDef, label);
1671 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1672 	label->lab_rotate = *value;
1673 	DBFontLabelSetBBox(label);
1674 	DBUndoPutLabel(cellDef, label);
1675 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1676     }
1677     return 0;
1678 }
1679 
1680 int
cmdLabelSizeFunc(label,cellUse,transform,value)1681 cmdLabelSizeFunc(label, cellUse, transform, value)
1682     Label *label;
1683     CellUse *cellUse;
1684     Transform *transform;
1685     int *value;
1686 {
1687     CellDef *cellDef = cellUse->cu_def;
1688 #ifdef MAGIC_WRAPPER
1689     Tcl_Obj *lobj;
1690 #endif
1691 
1692     if (value == NULL)
1693     {
1694 #ifdef MAGIC_WRAPPER
1695 	lobj = Tcl_GetObjResult(magicinterp);
1696 	Tcl_ListObjAppendElement(magicinterp, lobj,
1697 			Tcl_NewDoubleObj((double)label->lab_size / 8.0));
1698 	Tcl_SetObjResult(magicinterp, lobj);
1699 #else
1700 	TxPrintf("%g\n", (double)label->lab_size / 8.0);
1701 #endif
1702     }
1703     else
1704     {
1705 	DBUndoEraseLabel(cellDef, label);
1706 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1707 	label->lab_size = *value;
1708 	DBFontLabelSetBBox(label);
1709 	DBUndoPutLabel(cellDef, label);
1710 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1711     }
1712     return 0;
1713 }
1714 
1715 int
cmdLabelJustFunc(label,cellUse,transform,value)1716 cmdLabelJustFunc(label, cellUse, transform, value)
1717     Label *label;
1718     CellUse *cellUse;
1719     Transform *transform;
1720     int *value;
1721 {
1722     CellDef *cellDef = cellUse->cu_def;
1723 #ifdef MAGIC_WRAPPER
1724     Tcl_Obj *lobj;
1725 #endif
1726 
1727     if (value == NULL)
1728     {
1729 #ifdef MAGIC_WRAPPER
1730 	lobj = Tcl_GetObjResult(magicinterp);
1731 	Tcl_ListObjAppendElement(magicinterp, lobj,
1732 			Tcl_NewStringObj(GeoPosToName(label->lab_just), -1));
1733 	Tcl_SetObjResult(magicinterp, lobj);
1734 #else
1735 	TxPrintf("%s\n", GeoPosToName(label->lab_just));
1736 #endif
1737     }
1738     else
1739     {
1740 	DBUndoEraseLabel(cellDef, label);
1741 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1742 	label->lab_just = *value;
1743 	DBFontLabelSetBBox(label);
1744 	DBUndoPutLabel(cellDef, label);
1745 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1746     }
1747     return 0;
1748 }
1749 
1750 int
cmdLabelLayerFunc(label,cellUse,transform,value)1751 cmdLabelLayerFunc(label, cellUse, transform, value)
1752     Label *label;
1753     CellUse *cellUse;
1754     Transform *transform;
1755     int *value;
1756 {
1757     CellDef *cellDef = cellUse->cu_def;
1758     TileType ttype;
1759 #ifdef MAGIC_WRAPPER
1760     Tcl_Obj *lobj;
1761 #endif
1762 
1763     if (value == NULL)
1764     {
1765 #ifdef MAGIC_WRAPPER
1766 	lobj = Tcl_GetObjResult(magicinterp);
1767 	Tcl_ListObjAppendElement(magicinterp, lobj,
1768 			Tcl_NewStringObj(DBTypeLongNameTbl[label->lab_type], -1));
1769 	Tcl_SetObjResult(magicinterp, lobj);
1770 #else
1771 	TxPrintf("%s\n", DBTypeLongNameTbl[label->lab_type]);
1772 #endif
1773     }
1774     else
1775     {
1776 	ttype = (TileType)(*value);
1777 	if (label->lab_type != ttype)
1778 	{
1779 	    DBUndoEraseLabel(cellDef, label);
1780 	    label->lab_type = ttype;
1781 	    DBUndoPutLabel(cellDef, label);
1782 	    DBCellSetModified(cellDef, TRUE);
1783 	}
1784     }
1785     return 0;
1786 }
1787 
1788 int
cmdLabelStickyFunc(label,cellUse,transform,value)1789 cmdLabelStickyFunc(label, cellUse, transform, value)
1790     Label *label;
1791     CellUse *cellUse;
1792     Transform *transform;
1793     int *value;
1794 {
1795     CellDef *cellDef = cellUse->cu_def;
1796     int newvalue;
1797 #ifdef MAGIC_WRAPPER
1798     Tcl_Obj *lobj;
1799 #endif
1800 
1801     if (value == NULL)
1802     {
1803 #ifdef MAGIC_WRAPPER
1804 	lobj = Tcl_GetObjResult(magicinterp);
1805 	Tcl_ListObjAppendElement(magicinterp, lobj,
1806 			Tcl_NewBooleanObj((label->lab_flags & LABEL_STICKY) ? 1 : 0));
1807 	Tcl_SetObjResult(magicinterp, lobj);
1808 #else
1809 	TxPrintf("%s\n", (label->lab_flags & LABEL_STICKY) ? "true" : "false");
1810 #endif
1811     }
1812     else
1813     {
1814 	newvalue = label->lab_flags;
1815 	newvalue &= ~LABEL_STICKY;
1816 	newvalue |= *value;
1817 	if (newvalue != label->lab_flags)
1818 	{
1819 	    /* Label does not change appearance, just need to record the change */
1820 	    DBUndoEraseLabel(cellDef, label);
1821 	    label->lab_flags = newvalue;
1822 	    DBUndoPutLabel(cellDef, label);
1823 	}
1824     }
1825     return 0;
1826 }
1827 
1828 int
cmdLabelOffsetFunc(label,cellUse,transform,point)1829 cmdLabelOffsetFunc(label, cellUse, transform, point)
1830     Label *label;
1831     CellUse *cellUse;
1832     Transform *transform;
1833     Point *point;
1834 {
1835     CellDef *cellDef = cellUse->cu_def;
1836 #ifdef MAGIC_WRAPPER
1837     Tcl_Obj *lobj, *pobj;
1838 #endif
1839 
1840     if (point == NULL)
1841     {
1842 #ifdef MAGIC_WRAPPER
1843 	lobj = Tcl_GetObjResult(magicinterp);
1844 	pobj = Tcl_NewListObj(0, NULL);
1845 	Tcl_ListObjAppendElement(magicinterp, lobj, pobj);
1846 	Tcl_ListObjAppendElement(magicinterp, pobj,
1847 			Tcl_NewDoubleObj((double)label->lab_offset.p_x / 8.0));
1848 	Tcl_ListObjAppendElement(magicinterp, pobj,
1849 			Tcl_NewDoubleObj((double)label->lab_offset.p_y / 8.0));
1850 	Tcl_SetObjResult(magicinterp, lobj);
1851 #else
1852 	TxPrintf("%g %g\n", (double)(label->lab_offset.p_x) / 8.0,
1853 		(double)(label->lab_offset.p_y) / 8.0);
1854 #endif
1855     }
1856     else
1857     {
1858 	DBUndoEraseLabel(cellDef, label);
1859 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1860 	label->lab_offset = *point;
1861 	DBFontLabelSetBBox(label);
1862 	DBUndoPutLabel(cellDef, label);
1863 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1864     }
1865     return 0;
1866 }
1867 
1868 int
cmdLabelFontFunc(label,cellUse,transform,font)1869 cmdLabelFontFunc(label, cellUse, transform, font)
1870     Label *label;
1871     CellUse *cellUse;
1872     Transform *transform;
1873     int *font;
1874 {
1875     CellDef *cellDef = cellUse->cu_def;
1876 #ifdef MAGIC_WRAPPER
1877     Tcl_Obj *lobj;
1878 #endif
1879 
1880     if (font == NULL)
1881     {
1882 #ifdef MAGIC_WRAPPER
1883 	lobj = Tcl_GetObjResult(magicinterp);
1884 	if (label->lab_font == -1)
1885 	    Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj("default", 7));
1886 	else
1887 	    Tcl_ListObjAppendElement(magicinterp, lobj,
1888 			Tcl_NewStringObj(DBFontList[label->lab_font]->mf_name, -1));
1889 	Tcl_SetObjResult(magicinterp, lobj);
1890 #else
1891 	if (label->lab_font == -1)
1892 	    TxPrintf("default\n");
1893 	else
1894 	    TxPrintf("%s\n", DBFontList[label->lab_font]->mf_name);
1895 #endif
1896     }
1897     else
1898     {
1899 	DBUndoEraseLabel(cellDef, label);
1900 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1901 	label->lab_font = *font;
1902 	if ((*font > -1) && (label->lab_size == 0)) label->lab_size = DBLambda[1];
1903 	DBFontLabelSetBBox(label);
1904 	DBUndoPutLabel(cellDef, label);
1905 	DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
1906     }
1907     return 0;
1908 }
1909 
1910 /*
1911  * ----------------------------------------------------------------------------
1912  *
1913  * CmdSetLabel --
1914  *
1915  * Implement the "setlabel" command.
1916  * Query or change properties of a (selected) label in the edit cell
1917  *
1918  * Usage:
1919  *	setlabel [-default] option [name]
1920  *
1921  * Option may be one of:
1922  *	text
1923  *	font
1924  *	justify
1925  *	size
1926  *	offset
1927  *	rotate
1928  *	layer
1929  *
1930  * Results:
1931  *	None.
1932  *
1933  * Side effects:
1934  *	Modified label entry.  The "setlabel font" command may load font
1935  *	information if the requested font is not already in the font list.
1936  *	"setlabel font <name>" can be used without any select to load fonts
1937  *	from a startup script.
1938  *
1939  *	Use with "-default" causes the DefaultLabel structure to be created
1940  *	(if not existing already) and set with the given value.  Subsequent
1941  *	use of the "label" command will start with the given defaults, then
1942  *	apply whatever non-default values are specified in the command.
1943  *
1944  * ----------------------------------------------------------------------------
1945  */
1946 
1947 #define SETLABEL_TEXT		0
1948 #define SETLABEL_FONT		1
1949 #define SETLABEL_FONTLIST	2
1950 #define SETLABEL_JUSTIFY	3
1951 #define SETLABEL_SIZE		4
1952 #define SETLABEL_OFFSET		5
1953 #define SETLABEL_ROTATE		6
1954 #define SETLABEL_STICKY		7
1955 #define SETLABEL_LAYER		8
1956 #define SETLABEL_HELP		9
1957 
1958 void
CmdSetLabel(w,cmd)1959 CmdSetLabel(w, cmd)
1960     MagWindow *w;
1961     TxCommand *cmd;
1962 {
1963     int pos = -1, font = -1, size = 0, rotate = 0, flags = 0;
1964     int locargc, argstart = 1;
1965     char **msg;
1966     Point offset;
1967     TileType ttype;
1968     int option;
1969     bool doDefault = FALSE;
1970 #ifdef MAGIC_WRAPPER
1971     Tcl_Obj *lobj;
1972 #endif
1973 
1974     static char *cmdLabelYesNo[] = { "no", "false", "off", "0",
1975 		"yes", "true", "on", "1", 0 };
1976 
1977     static char *cmdLabelSetOption[] =
1978     {
1979 	"text <text>		change/get label text",
1980 	"font <name>		change/get label font",
1981 	"fontlist		list available fonts",
1982 	"justify <position>	change/get label justification",
1983 	"size <value>		change/get label size",
1984 	"offset <x> <y>		change/get label offset",
1985 	"rotate <degrees>	change/get label rotation",
1986 	"sticky [true|false]	change/get sticky property",
1987 	"layer <type>		change/get layer type",
1988 	"help			print this help info",
1989 	NULL
1990     };
1991 
1992     locargc = cmd->tx_argc;
1993     if (locargc > 2)
1994     {
1995 	if (!strncmp(cmd->tx_argv[1], "-def", 4))
1996 	{
1997 	    if (DefaultLabel == NULL)
1998 	    {
1999 		DefaultLabel = (Label *)mallocMagic(sizeof(Label));
2000 		/* Set default defaults (lab_text is ignored) */
2001 		DefaultLabel->lab_just = -1;
2002 		DefaultLabel->lab_size = 0;
2003 		DefaultLabel->lab_font = -1;
2004 		DefaultLabel->lab_rotate = 0;
2005 		DefaultLabel->lab_flags = 0;
2006 		DefaultLabel->lab_offset.p_x = 0;
2007 		DefaultLabel->lab_offset.p_y = 0;
2008 		DefaultLabel->lab_type = (TileType)(-1);
2009 	    }
2010 	    doDefault = TRUE;
2011 	    locargc--;
2012 	    argstart++;
2013 	}
2014     }
2015 
2016     if (locargc < 2 || locargc > 4)
2017 	option = SETLABEL_HELP;
2018     else
2019 	option = Lookup(cmd->tx_argv[argstart], cmdLabelSetOption);
2020 
2021     switch (option)
2022     {
2023 	case SETLABEL_FONTLIST:
2024 #ifdef MAGIC_WRAPPER
2025 	    lobj = Tcl_NewListObj(0, NULL);
2026 	    for (font = 0; font < DBNumFonts; font++)
2027 		Tcl_ListObjAppendElement(magicinterp, lobj,
2028 			Tcl_NewStringObj(DBFontList[font]->mf_name, -1));
2029 	    Tcl_SetObjResult(magicinterp, lobj);
2030 #else
2031 	    for (font = 0; font < DBNumFonts; font++)
2032 		TxPrintf("%s ", DBFontList[font]->mf_name);
2033 	    TxPrintf("\n");
2034 #endif
2035 	    break;
2036 
2037 	case SETLABEL_TEXT:
2038 	    if (doDefault)
2039 	    {
2040 		TxError("Cannot set a default label text.\n");
2041 	    }
2042 	    else if (EditCellUse)
2043 	    {
2044 		SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
2045 			cmdLabelTextFunc, (locargc == 3) ?
2046 			(ClientData)cmd->tx_argv[argstart + 1] : (ClientData)NULL);
2047 	    }
2048 	    break;
2049 
2050 	case SETLABEL_FONT:
2051 	    if (locargc >= 2 && locargc <= 4)
2052 	    {
2053 		/* This option is used to see the font name corresponding
2054 		 * to a font number.
2055 		 */
2056 		if ((locargc == 3) && StrIsInt(cmd->tx_argv[argstart + 1]))
2057 		{
2058 		    int font = atoi(cmd->tx_argv[argstart + 1]);
2059 		    if (font < -1 || font >= DBNumFonts)
2060 		    {
2061 			if (DBNumFonts == 0)
2062 			    TxError("No vector outline fonts are loaded.\n");
2063 			else
2064 			    TxError("Font index out of range (0 to %d)\n",
2065 					DBNumFonts - 1);
2066 		    }
2067 		    else if (font == -1)
2068 			TxPrintf("default\n");
2069 		    else
2070 			TxPrintf("%s\n", DBFontList[font]->mf_name);
2071 		}
2072 		else if ((locargc == 3 || locargc == 4) &&
2073 				!StrIsInt(cmd->tx_argv[argstart + 1]))
2074 		{
2075 		    font = DBNameToFont(cmd->tx_argv[argstart + 1]);
2076 		    if (font < -1)
2077 		    {
2078 			float scale = 1.0;
2079 			if ((locargc == 4) && StrIsNumeric(cmd->tx_argv[argstart + 2]))
2080 			    scale = (float)atof(cmd->tx_argv[argstart + 2]);
2081 			if (DBLoadFont(cmd->tx_argv[argstart + 1], scale) != 0)
2082 			    TxError("Error loading font \"%s\"\n", cmd->tx_argv[argstart + 1]);
2083 			font = DBNameToFont(cmd->tx_argv[argstart + 1]);
2084 			if (font < -1) break;
2085 		    }
2086 		}
2087 
2088 		if (doDefault)
2089 		{
2090 		    if (locargc == 2)
2091 		    {
2092 		    	font = DefaultLabel->lab_font;
2093 			if (font == -1)
2094 #ifdef MAGIC_WRAPPER
2095 		            Tcl_SetResult(magicinterp, "default", TCL_VOLATILE);
2096 #else
2097 		    	    TxPrintf("default\n");
2098 #endif
2099 			else
2100 #ifdef MAGIC_WRAPPER
2101 			    Tcl_SetObjResult(magicinterp,
2102 				Tcl_NewStringObj(DBFontList[font]->mf_name, -1));
2103 #else
2104 			    TxPrintf("%s\n", DBFontList[font]->mf_name);
2105 #endif
2106 		    }
2107 		    else
2108 			DefaultLabel->lab_font = font;
2109 		}
2110 		else if (EditCellUse)
2111 		{
2112 		    SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
2113 				cmdLabelFontFunc, (locargc == 3) ?
2114 				(ClientData)&font : (ClientData)NULL);
2115 		}
2116 	    }
2117 	    break;
2118 
2119 	case SETLABEL_JUSTIFY:
2120 	    if (locargc == 3)
2121 	    {
2122 		pos = GeoNameToPos(cmd->tx_argv[argstart + 1], FALSE, TRUE);
2123 		if (pos < 0) break;
2124 	    }
2125 	    if (doDefault)
2126 	    {
2127 		if (locargc == 2)
2128 		{
2129 #ifdef MAGIC_WRAPPER
2130 		    Tcl_SetObjResult(magicinterp,
2131 				Tcl_NewStringObj(GeoPosToName(DefaultLabel->lab_just),
2132 				-1));
2133 #else
2134 		    TxPrintf("%s\n", GeoPosToName(DefaultLabel->lab_just));
2135 #endif
2136 		}
2137 		else
2138 		    DefaultLabel->lab_just = pos;
2139 	    }
2140 	    else if (EditCellUse)
2141 	    {
2142 		SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
2143 			cmdLabelJustFunc, (locargc == 3) ?
2144 			(ClientData)&pos : (ClientData)NULL);
2145 	    }
2146 	    break;
2147 
2148 	case SETLABEL_SIZE:
2149 	    if (locargc == 3)
2150 	    {
2151 		if (StrIsNumeric(cmd->tx_argv[argstart + 1]))
2152 		    size = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8);
2153 		if (size <= 0) break;
2154 	    }
2155 	    if (doDefault)
2156 	    {
2157 		if (locargc == 2)
2158 		{
2159 #ifdef MAGIC_WRAPPER
2160 		    Tcl_SetObjResult(magicinterp,
2161 				Tcl_NewIntObj(DefaultLabel->lab_size));
2162 #else
2163 		    TxPrintf("%d\n", DefaultLabel->lab_size);
2164 #endif
2165 		}
2166 		else
2167 		    DefaultLabel->lab_size = size;
2168 	    }
2169 	    else if (EditCellUse)
2170 	    {
2171 		SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
2172 			cmdLabelSizeFunc, (locargc == 3) ?
2173 			(ClientData)&size : (ClientData)NULL);
2174 	    }
2175 	    break;
2176 
2177 	case SETLABEL_OFFSET:
2178 	    if (locargc == 3)
2179 	    {
2180 		char *yp;
2181 		if ((yp = strchr(cmd->tx_argv[argstart + 1], ' ')) != NULL)
2182 		{
2183 		    offset.p_x = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8);
2184 		    offset.p_y = cmdScaleCoord(w, yp, TRUE, FALSE, 8);
2185 		}
2186 		else
2187 		{
2188 		    TxError("Usage:  setlabel offset <x> <y>\n");
2189 		    return;
2190 		}
2191 	    }
2192 	    else if (locargc == 4)
2193 	    {
2194 		offset.p_x = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8);
2195 		offset.p_y = cmdScaleCoord(w, cmd->tx_argv[argstart + 2], TRUE, FALSE, 8);
2196 	    }
2197 	    if (doDefault)
2198 	    {
2199 		if (locargc == 2)
2200 		{
2201 #ifdef MAGIC_WRAPPER
2202 	 	    Tcl_Obj *lobj;
2203 		    Tcl_NewListObj(0, NULL);
2204 	    	    Tcl_ListObjAppendElement(magicinterp, lobj,
2205 				Tcl_NewIntObj(DefaultLabel->lab_offset.p_x));
2206 	    	    Tcl_ListObjAppendElement(magicinterp, lobj,
2207 				Tcl_NewIntObj(DefaultLabel->lab_offset.p_y));
2208 		    Tcl_SetObjResult(magicinterp, lobj);
2209 #else
2210 		    TxPrintf("%d %d\n", DefaultLabel->lab_offset.p_x,
2211 				DefaultLabel->lab_offset.p_y);
2212 #endif
2213 		}
2214 		else
2215 		    DefaultLabel->lab_offset = offset;
2216 	    }
2217 	    else if (EditCellUse)
2218 	    {
2219 		SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
2220 			cmdLabelOffsetFunc, (locargc != 2) ?
2221 			(ClientData)&offset : (ClientData)NULL);
2222 	    }
2223 	    break;
2224 
2225 	case SETLABEL_ROTATE:
2226 	    if (locargc == 3)
2227 	    {
2228 		if (StrIsInt(cmd->tx_argv[argstart + 1]))
2229 		    rotate = atoi(cmd->tx_argv[argstart + 1]);
2230 	    }
2231 	    if (doDefault)
2232 	    {
2233 		if (locargc == 2)
2234 		{
2235 #ifdef MAGIC_WRAPPER
2236 		    Tcl_SetObjResult(magicinterp,
2237 				Tcl_NewIntObj(DefaultLabel->lab_rotate));
2238 #else
2239 		    TxPrintf("%d\n", DefaultLabel->lab_rotate);
2240 #endif
2241 		}
2242 		else
2243 		    DefaultLabel->lab_rotate = rotate;
2244 	    }
2245 	    else if (EditCellUse)
2246 	    {
2247 		SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
2248 			cmdLabelRotateFunc, (locargc == 3) ?
2249 			(ClientData)&rotate : (ClientData)NULL);
2250 	    }
2251 	    break;
2252 
2253 	case SETLABEL_STICKY:
2254 	    if (locargc == 3)
2255 	    {
2256 		option = Lookup(cmd->tx_argv[argstart + 1], cmdLabelYesNo);
2257 		if (option < 0)
2258 		{
2259 		    TxError("Unknown sticky option \"%s\"\n", cmd->tx_argv[argstart + 1]);
2260 		    break;
2261 		}
2262 		flags = (option <= 3) ? 0 : LABEL_STICKY;
2263 	    }
2264 	    if (doDefault)
2265 	    {
2266 		if (locargc == 2)
2267 		{
2268 #ifdef MAGIC_WRAPPER
2269 		    Tcl_SetObjResult(magicinterp,
2270 				Tcl_NewBooleanObj((DefaultLabel->lab_flags &
2271 				LABEL_STICKY) ? TRUE : FALSE));
2272 #else
2273 		    TxPrintf("%d\n", (DefaultLabel->lab_flags & LABEL_STICKY) ? 1 : 0);
2274 #endif
2275 		}
2276 		else
2277 		    DefaultLabel->lab_flags = flags;
2278 	    }
2279 	    else if (EditCellUse)
2280 	    {
2281 		SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
2282 			cmdLabelStickyFunc, (locargc == 3) ?
2283 			(ClientData)&flags : (ClientData)NULL);
2284 	    }
2285 	    break;
2286 
2287 	case SETLABEL_LAYER:
2288 	    if (locargc == 3)
2289 	    {
2290 		if (!strcasecmp(cmd->tx_argv[argstart + 1], "default"))
2291 		    ttype = -1;
2292 		else
2293 		{
2294 		    ttype = DBTechNoisyNameType(cmd->tx_argv[argstart + 1]);
2295 		    if (ttype < 0) break;
2296 		}
2297 	    }
2298 	    if (doDefault)
2299 	    {
2300 		if (locargc == 2)
2301 		{
2302 		    if (DefaultLabel->lab_type == (TileType)(-1))
2303 #ifdef MAGIC_WRAPPER
2304 		        Tcl_SetResult(magicinterp, "default", TCL_VOLATILE);
2305 #else
2306 		    	TxPrintf("default\n");
2307 #endif
2308 		    else
2309 #ifdef MAGIC_WRAPPER
2310 		        Tcl_SetResult(magicinterp,
2311 				DBTypeLongNameTbl[DefaultLabel->lab_type],
2312 				TCL_VOLATILE);
2313 #else
2314 		    	TxPrintf("%s\n", DBTypeLongNameTbl[DefaultLabel->lab_type]);
2315 #endif
2316 		}
2317 		else
2318 		    DefaultLabel->lab_type = ttype;
2319 	    }
2320 	    else if (EditCellUse)
2321 	    {
2322 		SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
2323 			cmdLabelLayerFunc, (locargc == 3) ?
2324 			(ClientData)&ttype : (ClientData)NULL);
2325 	    }
2326 	    break;
2327 
2328 	case SETLABEL_HELP:
2329 	    TxError("Usage:  setlabel [option], where [option] is one of:\n");
2330 	    for (msg = &(cmdLabelSetOption[0]); *msg != NULL; msg++)
2331 	    {
2332 	        TxError("    %s\n", *msg);
2333 	    }
2334 	    break;
2335 
2336 	default:
2337 	    TxError("Unknown setlabel option \"%s\"\n", cmd->tx_argv[argstart]);
2338 	    break;
2339     }
2340 }
2341 
2342 /*
2343  * ----------------------------------------------------------------------------
2344  *
2345  * CmdSideways --
2346  *
2347  * Implement the "sideways" command.
2348  *
2349  * Usage:
2350  *	sideways
2351  *
2352  * Results:
2353  *	None.
2354  *
2355  * Side effects:
2356  *	The selection and box are flipped left-to-right, using the
2357  *	center of the selection as the axis for flipping.
2358  *
2359  * ----------------------------------------------------------------------------
2360  */
2361 
2362 void
CmdSideways(w,cmd)2363 CmdSideways(w, cmd)
2364     MagWindow *w;
2365     TxCommand *cmd;
2366 {
2367     Transform trans;
2368     Rect rootBox, bbox;
2369     CellDef *rootDef;
2370 
2371     if (cmd->tx_argc != 1)
2372     {
2373 	TxError("Usage: %s\n", cmd->tx_argv[0]);
2374 	return;
2375     }
2376     if (!ToolGetEditBox((Rect *)NULL)) return;
2377 
2378     /* To flip the selection sideways, first flip it around the
2379      * y-axis, then move it back so its lower-left corner is in
2380      * the same place that it used to be.
2381      */
2382 
2383     GeoTransRect(&GeoSidewaysTransform, &SelectDef->cd_bbox, &bbox);
2384     GeoTranslateTrans(&GeoSidewaysTransform,
2385 	SelectDef->cd_bbox.r_xbot - bbox.r_xbot,
2386 	SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans);
2387 
2388     SelectTransform(&trans);
2389 
2390     /* Flip the box, if it exists and is in the same window as the
2391      * selection.
2392      */
2393 
2394     if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef))
2395     {
2396 	Rect newBox;
2397 
2398 	GeoTransRect(&trans, &rootBox, &newBox);
2399 	DBWSetBox(rootDef, &newBox);
2400     }
2401 
2402     return;
2403 }
2404 
2405 /*
2406  * ----------------------------------------------------------------------------
2407  *
2408  * CmdShell
2409  *
2410  * Implement the "shell" or "!" command.
2411  *
2412  * Usage:
2413  *	shell [command]
2414  *
2415  * Results:
2416  *	Executes the command in a unix shell
2417  *
2418  * Side effects:
2419  *	May alter unix files
2420  *
2421  * ----------------------------------------------------------------------------
2422  */
2423 
2424 void
CmdShell(w,cmd)2425 CmdShell(w, cmd)
2426     MagWindow *w;
2427     TxCommand *cmd;
2428 {
2429     int i, cmdlength;
2430     char *command;
2431 
2432     if (cmd->tx_argc != 1) {
2433 	cmdlength = 1;
2434 	for (i = 1; i < cmd->tx_argc; i++) {
2435 	    cmdlength = cmdlength + strlen(cmd->tx_argv[i]) + 1;
2436 	}
2437 	command = mallocMagic((unsigned) (cmdlength));
2438 	(void) strcpy(command, cmd->tx_argv[1]);
2439 	for (i = 2; i < cmd->tx_argc; i++) {
2440 	    strcat(command, " ");
2441 	    strcat(command, cmd->tx_argv[i]);
2442 	}
2443 	system(command);
2444 	freeMagic(command);
2445     }
2446 }
2447 
2448 /*
2449  * ----------------------------------------------------------------------------
2450  *
2451  * CmdSgraph
2452  *
2453  * Implement the "sgraph" command.
2454  *
2455  * Usage:
2456  *	sgraph [off|add|delete|debug]
2457  *	sgraph [show|auto] [vertical|horizontal]
2458  *
2459  * Results:
2460  *	None.
2461  *
2462  * Side effects:
2463  *	Calls the 'stretchgraph' module, if present.
2464  *
2465  * ----------------------------------------------------------------------------
2466  */
2467 
2468 #ifdef	LLNL
2469 int (*CmdStretchCmd)() = NULL;
2470     /* ARGSUSED */
2471 
2472 void
CmdSgraph(w,cmd)2473 CmdSgraph(w, cmd)
2474     MagWindow *w;
2475     TxCommand *cmd;
2476 {
2477     if (CmdStretchCmd != NULL)
2478     {
2479 	(*CmdStretchCmd)(w, cmd);
2480     }
2481     else
2482     {
2483 	TxError("Sorry, the sgraph command doesn't work in this version.\n");
2484 	TxError("(Magic was not linked with stretchgraph module.)\n");
2485     }
2486 }
2487 #endif	/* LLNL */
2488 
2489 #if !defined(NO_SIM_MODULE) && defined(RSIM_MODULE)
2490 /*
2491  * ----------------------------------------------------------------------------
2492  *
2493  * CmdStartRsim
2494  *
2495  * 	This command starts Rsim under Magic, escapes Rsim, and returns
2496  *	back to Magic.
2497  *
2498  * Results:
2499  *	Rsim is forked from Magic.
2500  *
2501  * Side effects:
2502  *	None.
2503  *
2504  * ----------------------------------------------------------------------------
2505  */
2506 
2507 void
CmdStartRsim(w,cmd)2508 CmdStartRsim(w, cmd)
2509     MagWindow *w;
2510     TxCommand *cmd;
2511 {
2512     static char rsimstr[] = "rsim";
2513 
2514     if ((cmd->tx_argc == 1) && (!SimRsimRunning)) {
2515 	TxPrintf("usage: startrsim [options] file\n");
2516 	return;
2517     }
2518     if ((cmd->tx_argc != 1) && (SimRsimRunning)) {
2519 	TxPrintf("Simulator already running.  You cannont start another.\n");
2520 	return;
2521     }
2522     windCheckOnlyWindow(&w, DBWclientID);
2523     if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
2524 	TxError("Put the cursor in a layout window.\n");
2525 	return;
2526     }
2527 
2528     /* change argv[0] to be "rsim" and send it to Rsim_start */
2529 
2530     cmd->tx_argv[0] = rsimstr;
2531     if (cmd->tx_argc != 1) {
2532 	cmd->tx_argv[cmd->tx_argc] = (char *) 0;
2533 	SimStartRsim(cmd->tx_argv);
2534     }
2535     SimConnectRsim(TRUE);
2536 }
2537 
2538 /*
2539  * ----------------------------------------------------------------------------
2540  *
2541  * CmdSimCmd
2542  *
2543  *	Applies the given rsim command to the currently selected nodes.
2544  *
2545  * Results:
2546  *	Whatever rsim replys to the commands input.
2547  *
2548  * Side effects:
2549  *	None.
2550  *
2551  * ----------------------------------------------------------------------------
2552  */
2553 
2554 void
CmdSimCmd(w,cmd)2555 CmdSimCmd(w, cmd)
2556     MagWindow *w;
2557     TxCommand *cmd;
2558 {
2559     static char cmdbuf[200];
2560     char 	*strptr;
2561     char 	*nodeCmd;
2562     int 	i;
2563 
2564     if (!SimRsimRunning) {
2565 	TxPrintf("You must first start the simulator by using the rsim command.\n");
2566 	return;
2567     }
2568     if (cmd->tx_argc == 1) {
2569 	TxPrintf("usage: simcmd command [options]\n");
2570 	return;
2571     }
2572     windCheckOnlyWindow(&w, DBWclientID);
2573     if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
2574 	TxError("Put the cursor in a layout window.\n");
2575 	return;
2576     }
2577 
2578     /* check to see whether to apply the command to each node selected,
2579      * or whether to just ship the command to rsim without any node
2580      * names.
2581      */
2582     nodeCmd = SimGetNodeCommand( cmd->tx_argv[1] );
2583 
2584     strcpy( cmdbuf, (nodeCmd != NULL) ? nodeCmd : cmd->tx_argv[1] );
2585     strptr = cmdbuf + strlen(cmdbuf);
2586     *strptr++ = ' ';
2587     *strptr = '\0';
2588 
2589     for (i = 2; i <= cmd->tx_argc - 1; i++) {
2590 	strcpy(strptr, cmd->tx_argv[i]);
2591 	strcat(strptr, " ");
2592 	strptr += strlen(strptr) + 1;
2593     }
2594 
2595     if (nodeCmd != NULL) {
2596 	SimSelection(cmdbuf);
2597     }
2598     else {
2599 	SimRsimIt(cmdbuf, "");
2600 
2601         while (TRUE) {
2602 	    if (!SimGetReplyLine(&strptr)) {
2603 		break;
2604 	    }
2605 	    if (!strptr) {
2606 		break;
2607 	    }
2608 	    TxPrintf("%s\n", strptr);
2609 	}
2610     }
2611 }
2612 #endif
2613 
2614 
2615 /*
2616  * ----------------------------------------------------------------------------
2617  *
2618  * CmdSnap --
2619  *
2620  * Set the box snapping to align either with the nearest user-defined grid,
2621  * the nearest integer lambda value, or turn snapping off (aligns to internal
2622  * units).
2623  *
2624  * Results:
2625  *	None.
2626  *
2627  * Side effects:
2628  *	See above.
2629  *
2630  * ----------------------------------------------------------------------------
2631  */
2632 
2633 #define SNAP_OFF		0
2634 #define SNAP_INTERNAL		1
2635 #define SNAP_LAMBDA		2
2636 #define SNAP_GRID		3
2637 #define SNAP_USER		4
2638 #define SNAP_ON			5
2639 #define SNAP_LIST		6
2640 
2641 void
CmdSnap(w,cmd)2642 CmdSnap(w, cmd)
2643     MagWindow *w;
2644     TxCommand *cmd;
2645 {
2646     static char *names[] = { "off", "internal", "lambda", "grid", "user", "on",
2647 		"list", 0 };
2648     int n = SNAP_LIST;
2649     DBWclientRec *crec;
2650 
2651     if (cmd->tx_argc < 2) goto printit;
2652 
2653     n = Lookup(cmd->tx_argv[1], names);
2654     if (n < 0)
2655     {
2656 	TxPrintf("Usage: snap [internal | lambda | user]\n");
2657 	return;
2658     }
2659     switch (n)
2660     {
2661 	case SNAP_OFF: case SNAP_INTERNAL:
2662 	    DBWSnapToGrid = DBW_SNAP_INTERNAL;
2663 	    return;
2664 	case SNAP_LAMBDA:
2665 	    DBWSnapToGrid = DBW_SNAP_LAMBDA;
2666 	    return;
2667 	case SNAP_GRID: case SNAP_USER: case SNAP_ON:
2668 	    DBWSnapToGrid = DBW_SNAP_USER;
2669 	    return;
2670     }
2671 
2672 printit:
2673     if (n == SNAP_LIST)  /* list */
2674 #ifdef MAGIC_WRAPPER
2675 	Tcl_SetResult(magicinterp,
2676 		(DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" :
2677 		((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user"),
2678 		TCL_VOLATILE);
2679 #else
2680 	TxPrintf("%s\n", (DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" :
2681 		((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user"));
2682 #endif
2683     else
2684 	TxPrintf("Box is aligned to %s grid\n",
2685 		(DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" :
2686 		((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user"));
2687 }
2688 
2689 
2690 
2691 /*
2692  * ----------------------------------------------------------------------------
2693  *
2694  * CmdSplit --
2695  *
2696  * Split a tile with a diagonal (nonmanhattan geometry)
2697  *
2698  * Results:
2699  *	None.
2700  *
2701  * Side effects:
2702  *	Modifies the geometry of the edit cell.
2703  *
2704  * ----------------------------------------------------------------------------
2705  */
2706 
2707 void
CmdSplit(w,cmd)2708 CmdSplit(w, cmd)
2709     MagWindow *w;
2710     TxCommand *cmd;
2711 {
2712     Rect editRect, expRect;
2713     TileTypeBitMask mask1, mask2, *cmask;
2714     TileType t, dir, side, diag;
2715     int pNum, direction;
2716     PaintUndoInfo ui;
2717 
2718     windCheckOnlyWindow(&w, DBWclientID);
2719     if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
2720     {
2721 	TxError("Put the cursor in a layout window\n");
2722 	return;
2723     }
2724 
2725     if (cmd->tx_argc != 3 && cmd->tx_argc != 4)
2726     {
2727 	TxError("Usage: %s dir layer [layer2]\n", cmd->tx_argv[0]);
2728 	return;
2729     }
2730 
2731     if (!ToolGetEditBox(&editRect)) return;
2732 
2733     if (!CmdParseLayers(cmd->tx_argv[2], &mask1))
2734         return;
2735 
2736     /* Remove any inactive layers */
2737     TTMaskAndMask(&mask1, &DBActiveLayerBits);
2738 
2739     if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0)
2740 	return;
2741 
2742     if (GEO_RECTNULL(&editRect)) return;	    /* Nothing to do */
2743 
2744     if (cmd->tx_argc == 4)
2745     {
2746 	if (!CmdParseLayers(cmd->tx_argv[3], &mask2))
2747 	    return;
2748 	TTMaskClearType(&mask2, TT_SPACE);
2749     }
2750     else
2751 	TTMaskZero(&mask2);
2752 
2753     TTMaskClearType(&mask1, TT_SPACE);
2754 
2755     direction = (direction >> 1) - 1;
2756     dir = (direction & 0x1) ? 0 : TT_DIRECTION;
2757 
2758     for (t = TT_SPACE + 1; t < DBNumTypes; t++)
2759     {
2760 	side = (direction & 0x2) ? 0 : TT_SIDE;
2761 	for (cmask = &mask1; cmask != NULL; cmask = ((cmask == &mask1) ? &mask2 : NULL))
2762 	{
2763 	    if (cmask == &mask2) side = (side) ? 0 : TT_SIDE;
2764 	    diag = DBTransformDiagonal(TT_DIAGONAL | dir | side, &RootToEditTransform);
2765 
2766 	    if (TTMaskHasType(cmask, t))
2767 	    {
2768 		EditCellUse->cu_def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
2769 		ui.pu_def = EditCellUse->cu_def;
2770 		for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
2771 		    if (DBPaintOnPlane(t, pNum))
2772 		    {
2773 			ui.pu_pNum = pNum;
2774 			DBNMPaintPlane(EditCellUse->cu_def->cd_planes[pNum],
2775 				diag, &editRect, DBStdPaintTbl(t, pNum), &ui);
2776 			GEO_EXPAND(&editRect, 1, &expRect);
2777 			DBMergeNMTiles(EditCellUse->cu_def->cd_planes[pNum],
2778 				&expRect, &ui);
2779 		    }
2780 	    }
2781 	}
2782     }
2783 
2784     SelectClear();
2785     DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask1);
2786     DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask2);
2787     DBReComputeBbox(EditCellUse->cu_def);
2788     DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &editRect);
2789 }
2790 
2791 /*
2792  * ----------------------------------------------------------------------------
2793  *
2794  * CmdSplitErase --
2795  *
2796  * Erase a diagonal section from a tile (nonmanhattan geometry)
2797  *
2798  * Results:
2799  *	None.
2800  *
2801  * Side effects:
2802  *	Modifies the geometry of the edit cell.
2803  *
2804  * ----------------------------------------------------------------------------
2805  */
2806 
2807 void
CmdSplitErase(w,cmd)2808 CmdSplitErase(w, cmd)
2809     MagWindow *w;
2810     TxCommand *cmd;
2811 {
2812     Rect editRect, expRect;
2813     TileTypeBitMask mask;
2814     TileType t, dir, side, diag;
2815     int pNum, direction;
2816     PaintUndoInfo ui;
2817 
2818     windCheckOnlyWindow(&w, DBWclientID);
2819     if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
2820     {
2821 	TxError("Put the cursor in a layout window\n");
2822 	return;
2823     }
2824 
2825     if (cmd->tx_argc != 2 && cmd->tx_argc != 3)
2826     {
2827 	TxError("Usage: %s dir [layer]\n", cmd->tx_argv[0]);
2828 	return;
2829     }
2830 
2831     if (!ToolGetEditBox(&editRect)) return;
2832 
2833     if (GEO_RECTNULL(&editRect)) return;	    /* Nothing to do */
2834 
2835     if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0)
2836 	return;
2837 
2838     if (cmd->tx_argc == 2)
2839 	(void) CmdParseLayers("*", &mask);
2840     else if (!CmdParseLayers(cmd->tx_argv[2], &mask))
2841 	return;
2842 
2843     if (TTMaskEqual(&mask, &DBSpaceBits))
2844 	(void) CmdParseLayers("*,label", &mask);
2845     TTMaskClearType(&mask, TT_SPACE);
2846     if (TTMaskIsZero(&mask))
2847 	return;
2848 
2849     /* Remove any inactive layers */
2850     TTMaskAndMask(&mask, &DBActiveLayerBits);
2851 
2852     direction = (direction >> 1) - 1;
2853     dir = (direction & 0x1) ? 0 : TT_DIRECTION;
2854 
2855     for (t = TT_SPACE + 1; t < DBNumTypes; t++)
2856     {
2857 	side = (direction & 0x2) ? 0 : TT_SIDE;
2858 	diag = DBTransformDiagonal(TT_DIAGONAL | dir | side, &RootToEditTransform);
2859 
2860 	if (TTMaskHasType(&mask, t))
2861 	{
2862 	    EditCellUse->cu_def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
2863 	    ui.pu_def = EditCellUse->cu_def;
2864 	    for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
2865 		if (DBPaintOnPlane(t, pNum))
2866 		{
2867 		    ui.pu_pNum = pNum;
2868 		    DBNMPaintPlane(EditCellUse->cu_def->cd_planes[pNum],
2869 				diag, &editRect, DBStdEraseTbl(t, pNum), &ui);
2870 		    GEO_EXPAND(&editRect, 1, &expRect);
2871 		    DBMergeNMTiles(EditCellUse->cu_def->cd_planes[pNum],
2872 				&expRect, &ui);
2873 		}
2874 	}
2875     }
2876 
2877     SelectClear();
2878     DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask);
2879     DBReComputeBbox(EditCellUse->cu_def);
2880     DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &editRect);
2881 }
2882 
2883 
2884 /*
2885  * ----------------------------------------------------------------------------
2886  *
2887  * CmdStretch --
2888  *
2889  * Implement the "stretch" command.
2890  *
2891  * Usage:
2892  *	stretch [direction [distance]]
2893  *
2894  * Results:
2895  *	None.
2896  *
2897  * Side effects:
2898  *	Moves everything that's currently selected, erases material that
2899  *	the selection would sweep over, and fills in material behind the
2900  *	selection.
2901  *
2902  * ----------------------------------------------------------------------------
2903  */
2904 
2905 void
CmdStretch(w,cmd)2906 CmdStretch(w, cmd)
2907     MagWindow *w;
2908     TxCommand *cmd;
2909 {
2910     Transform t;
2911     Rect rootBox, newBox;
2912     CellDef *rootDef;
2913     int xdelta, ydelta;
2914 
2915     if (cmd->tx_argc > 3)
2916     {
2917 	TxError("Usage: %s [direction [amount]]\n", cmd->tx_argv[0]);
2918 	return;
2919     }
2920 
2921     if (cmd->tx_argc > 1)
2922     {
2923 	int indx, amountx, amounty;
2924 
2925 	if (!ToolGetEditBox((Rect *)NULL)) return;
2926 
2927 	indx = GeoNameToPos(cmd->tx_argv[1], TRUE, TRUE);
2928 	if (indx < 0) return;
2929 
2930 	if (cmd->tx_argc >= 3)
2931 	{
2932 	    switch (indx)
2933 	    {
2934 		case GEO_EAST: case GEO_WEST:
2935 		    amountx = cmdParseCoord(w, cmd->tx_argv[2], TRUE, TRUE);
2936 		    amounty = 0;
2937 		    break;
2938 		case GEO_NORTH: case GEO_SOUTH:
2939 		    amountx = 0;
2940 		    amounty = cmdParseCoord(w, cmd->tx_argv[2], TRUE, FALSE);
2941 		    break;
2942 		default:
2943 		    amountx = amounty = 0;	/* Should not happen */
2944 		    break;
2945 	    }
2946 	}
2947 	else
2948 	{
2949 	    amountx = cmdParseCoord(w, "1l", TRUE, TRUE);
2950 	    amounty = cmdParseCoord(w, "1l", TRUE, FALSE);
2951 	}
2952 
2953 	switch (indx)
2954 	{
2955 	    case GEO_NORTH:
2956 		xdelta = 0;
2957 		ydelta = amounty;
2958 		break;
2959 	    case GEO_SOUTH:
2960 		xdelta = 0;
2961 		ydelta = -amounty;
2962 		break;
2963 	    case GEO_EAST:
2964 		xdelta = amountx;
2965 		ydelta = 0;
2966 		break;
2967 	    case GEO_WEST:
2968 		xdelta = -amountx;
2969 		ydelta = 0;
2970 		break;
2971 	    default:
2972 		ASSERT(FALSE, "Bad direction in CmdStretch");
2973 		return;
2974 	}
2975 	GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t);
2976 
2977 	/* Move the box by the same amount as the selection, if the
2978 	 * box exists.
2979 	 */
2980 
2981 	if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef))
2982 	{
2983 	    GeoTransRect(&t, &rootBox, &newBox);
2984 	    DBWSetBox(rootDef, &newBox);
2985 	}
2986     }
2987     else
2988     {
2989 	/* Use the displacement between the box lower-left corner and
2990 	 * the point as the transform.  Round off to a Manhattan distance.
2991 	 */
2992 
2993 	Point rootPoint;
2994 	MagWindow *window;
2995 	int absX, absY;
2996 
2997 	if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
2998 	{
2999 	    TxError("\"Stretch\" uses the box lower-left corner as a place\n");
3000 	    TxError("    to pick up the selection for stretching, but the\n");
3001 	    TxError("    box isn't in a window containing the selection.\n");
3002 	    return;
3003 	}
3004 	window = ToolGetPoint(&rootPoint, (Rect *) NULL);
3005 	if ((window == NULL) ||
3006 	    (EditRootDef != ((CellUse *) window->w_surfaceID)->cu_def))
3007 	{
3008 	    TxError("\"Stretch\" uses the point as the place to put down a\n");
3009 	    TxError("    the selection, but the point doesn't point to the\n");
3010 	    TxError("    edit cell.\n");
3011 	    return;
3012 	}
3013 	xdelta = rootPoint.p_x - rootBox.r_xbot;
3014 	ydelta = rootPoint.p_y - rootBox.r_ybot;
3015 	if (xdelta < 0) absX = -xdelta;
3016 	else absX = xdelta;
3017 	if (ydelta < 0) absY = -ydelta;
3018 	else absY = ydelta;
3019 	if (absY <= absX) ydelta = 0;
3020 	else xdelta = 0;
3021 	GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t);
3022 	GeoTransRect(&t, &rootBox, &newBox);
3023 	DBWSetBox(rootDef, &newBox);
3024     }
3025 
3026     SelectStretch(xdelta, ydelta);
3027 }
3028