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