magic/commands/CmdRS.c

3244 lines
82 KiB
C

/*
* CmdRS.c --
*
* Commands with names beginning with the letters R through S.
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#ifndef lint
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 $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/stack.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "utils/styles.h"
#include "database/database.h"
#include "database/fonts.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/main.h"
#include "commands/commands.h"
#include "textio/textio.h"
#include "graphics/graphics.h"
#include "utils/tech.h"
#include "drc/drc.h"
#include "textio/txcommands.h"
#include "utils/malloc.h"
#include "utils/netlist.h"
#include "netmenu/netmenu.h"
#include "select/select.h"
#include "utils/signals.h"
#include "sim/sim.h"
/* C99 compat */
#include "cif/cif.h"
#include "lef/lef.h"
#include "extract/extract.h"
#include "irouter/irouter.h"
#include "mzrouter/mzrouter.h"
#include "router/router.h"
#include "wiring/wiring.h"
extern void DisplayWindow();
/* Used by CmdSetLabel() */
Label *DefaultLabel;
/*
* ----------------------------------------------------------------------------
*
* CmdRandom
*
* Generate a random integer or set the random seed. This is mainly
* used for seeding the random number generator for the GDS write
* routine when GDS write is assigning a random prefix to ensure
* uniqueness of names in a GDS library read from a file instead of
* generated from the database.
*
* Results:
* None.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
void
CmdRandom(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int value;
if (cmd->tx_argc == 1)
{
/* Return a random number */
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(random()));
#else
TxPrintf("%d", random());
#endif
}
else if ((cmd->tx_argc >= 2) && (!strcmp(cmd->tx_argv[1], "seed")))
{
if (cmd->tx_argc == 3)
{
value = atoi(cmd->tx_argv[2]);
}
else
{
value = (int)time(NULL);
}
srandom(value);
}
else
{
TxPrintf("usage: random [seed [<value>]]\n");
return;
}
}
#if !defined(NO_SIM_MODULE) && defined(RSIM_MODULE)
/*
* ----------------------------------------------------------------------------
*
* CmdRsim
*
* Starts Rsim under Magic.
*
* Results:
* None.
*
* Side effects:
* Rsim is forked.
*
* ----------------------------------------------------------------------------
*/
void
CmdRsim(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if ((cmd->tx_argc == 1) && (!SimRsimRunning)) {
TxPrintf("usage: rsim [options] file\n");
return;
}
if ((cmd->tx_argc != 1) && (SimRsimRunning)) {
TxPrintf("Simulator already running. You cannot start another.\n");
return;
}
windCheckOnlyWindow(&w, DBWclientID);
if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
TxError("Put the cursor in a layout window.\n");
return;
}
if (cmd->tx_argc != 1) {
cmd->tx_argv[cmd->tx_argc] = (char *) 0;
SimStartRsim(cmd->tx_argv);
}
SimConnectRsim(FALSE);
}
#endif
/*
* ----------------------------------------------------------------------------
*
* CmdSave --
*
* Implement the "save" command.
* Writes the EditCell out to a disk file.
*
* Usage:
* save [file]
*
* Results:
* None.
*
* Side effects:
* Writes the cell out to file, if specified, or the file
* associated with the cell otherwise.
* Updates the caption in the window if the name of the edit
* cell has changed.
* Clears the modified bit in the cd_flags.
*
* ----------------------------------------------------------------------------
*/
void
CmdSave(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
CellDef *locDef;
if (cmd->tx_argc > 2)
{
TxError("Usage: %s [file]\n", cmd->tx_argv[0]);
return;
}
/* The "save" command can turn a read-only cell into a writeable */
/* cell, or vice versa. We should have more checks on the status */
/* of the resulting file. . . . For now, we patch things up so */
/* that doing "save" on a read-only cell doesn't crash magic. */
if (EditCellUse == NULL)
{
locDef = ((CellUse *)w->w_surfaceID)->cu_def;
locDef->cd_flags &= ~CDNOEDIT;
}
else
locDef = EditCellUse->cu_def;
DBUpdateStamps(locDef);
if (cmd->tx_argc == 2)
{
char *fileName;
if (CmdIllegalChars(cmd->tx_argv[1], "[],", "Cell name"))
return;
cmdSaveCell(locDef, cmd->tx_argv[1], FALSE, TRUE);
}
else cmdSaveCell(locDef, (char *) NULL, FALSE, TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* CmdScaleGrid --
*
* This procedure scales magic units with respect to lambda. A scaling
* of 2 to 1, for example, makes the magic units half their former size,
* allowing magic to read CIF files with components on the half-lambda
* grid without the necessity of altering the technology file for the
* process.
*
* Usage:
* scalegrid a b (or) scalegrid a/b (or) scalegrid a:b
*
* "a" and "b" are integers
*
* Results:
* magic internal units are scaled by factor a/b.
*
* Side Effects:
* cifinput and cifoutput scale factors are multiplied by a/b.
* All drc widths and distances are multiplied by b/a.
* All layout tile coordinates are multiplied by b/a.
* The current drawing scale and position are altered to maintain
* the current view.
* All windows are redrawn in case scaling has caused round-off
* truncation of tile coordinates.
* Geometry alterations are not undoable!
*
* ----------------------------------------------------------------------------
*/
void
CmdScaleGrid(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int scalen, scaled;
char *argsep;
Rect rootBox;
CellDef *rootBoxDef;
if ((cmd->tx_argc == 2) || (cmd->tx_argc == 3))
{
if (cmd->tx_argc == 2)
{
if (((argsep = strchr(cmd->tx_argv[1], ':')) != NULL) ||
((argsep = strchr(cmd->tx_argv[1], '/')) != NULL))
{
*argsep++ = '\0';
if (!StrIsInt(argsep))
goto scalegridusage;
else
scaled = atoi(argsep);
}
else
goto scalegridusage;
}
else
{
if (!StrIsInt(cmd->tx_argv[2]))
goto scalegridusage;
else
scaled = atoi(cmd->tx_argv[2]);
}
if (!StrIsInt(cmd->tx_argv[1]))
goto scalegridusage;
else
scalen = atoi(cmd->tx_argv[1]);
if (scalen <= 0 || scaled <= 0) goto scalegridusage;
else if (scalen == scaled) goto scalegridreport;
/* Reduce fraction by the greatest common factor */
ReduceFraction(&scalen, &scaled);
/* Check against CIF output style to see if we're violating a */
/* minimum grid resolution limit. */
if (CIFTechLimitScale(scalen, scaled) != 0)
{
TxError("Grid scaling is finer than limit set by the process!\n");
return;
}
/* Scale cifinput and cifoutput */
CIFTechInputScale(scalen, scaled, TRUE);
CIFTechOutputScale(scalen, scaled);
/* Scale drc rules */
DRCTechScale(scalen, scaled);
/* Scale extract parameters */
ExtTechScale(scalen, scaled);
/* Scale wiring parameters */
WireTechScale(scalen, scaled);
/* Scale LEF parameters */
#ifdef LEF_MODULE
LefTechScale(scalen, scaled);
#endif
#ifdef ROUTE_MODULE
/* Scale core router parameters */
RtrTechScale(scalen, scaled);
/* Scale maze router parameters (must come after DRCTechScale) */
MZAfterTech();
IRAfterTech();
#endif
/* Scale all tiles */
DBScaleEverything(scaled, scalen);
/* Save the current scale factor */
DBLambda[0] *= scalen;
DBLambda[1] *= scaled;
ReduceFraction(&DBLambda[0], &DBLambda[1]);
/* Rescale cursor box */
if (ToolGetBox(&rootBoxDef, &rootBox))
{
DBScalePoint(&rootBox.r_ll, scaled, scalen);
DBScalePoint(&rootBox.r_ur, scaled, scalen);
ToolMoveBox(TOOL_BL, &rootBox.r_ll, FALSE, rootBoxDef);
ToolMoveCorner(TOOL_TR, &rootBox.r_ur, FALSE, rootBoxDef);
}
/* Adjust all window viewing scales and positions and redraw */
WindScale(scaled, scalen);
/* This is harsh. Might work to scale all distance measures in */
/* the undo record, but this is simple and direct. */
UndoFlush();
/* TxPrintf("Magic internal unit scaled by %.3f\n",
(float)scalen / (float)scaled); */
scalegridreport:
TxPrintf("%d Magic internal unit%s = %d Lambda\n",
DBLambda[1], (DBLambda[1] == 1) ? "" : "s", DBLambda[0]);
return;
}
scalegridusage:
TxError("Usage: scalegrid a b, where a and b are strictly positive integers\n");
return;
}
/*
* ----------------------------------------------------------------------------
*
* CmdSee --
*
* This procedure is used to enable or disable display of certain
* things on the screen.
*
* Usage:
* see [no] stuff
*
* Stuff consists of mask layers or the keyword "allSame"
*
* Results:
* None.
*
* Side effects:
* The indicated mask layers are enabled or disabled from being
* displayed in the current window.
*
* ----------------------------------------------------------------------------
*/
void
CmdSee(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int flags;
bool off;
char *arg;
TileType i, j;
TileTypeBitMask mask, *rmask;
DBWclientRec *crec;
windCheckOnlyWindow(&w, DBWclientID);
if ((w == NULL) || (w->w_client != DBWclientID))
{
TxError("Point to a layout window first.\n");
return;
}
crec = (DBWclientRec *) w->w_clientData;
arg = (char *) NULL;
off = FALSE;
flags = 0;
if (cmd->tx_argc > 1)
{
if (strcmp(cmd->tx_argv[1], "no") == 0)
{
off = TRUE;
if (cmd->tx_argc > 2) arg = cmd->tx_argv[2];
}
else arg = cmd->tx_argv[1];
if ((cmd->tx_argc > 3) || ((cmd->tx_argc == 3) && !off))
{
TxError("Usage: see [no] layers|allSame\n");
return;
}
}
/* Figure out which things to set or clear. Don't ever make space
* invisible: that doesn't make any sense.
*/
if (arg != NULL)
{
if (strcmp(arg, "allSame") == 0)
{
mask = DBZeroTypeBits;
flags = DBW_ALLSAME;
}
else
{
if (!CmdParseLayers(arg, &mask))
return;
}
}
else mask = DBAllTypeBits;
if (TTMaskHasType(&mask, L_LABEL))
flags |= DBW_SEELABELS;
if (TTMaskHasType(&mask, L_CELL))
flags |= DBW_SEECELLS;
TTMaskClearType(&mask, L_LABEL);
TTMaskClearType(&mask, L_CELL);
TTMaskClearType(&mask, TT_SPACE);
if (off)
{
for (i = 0; i < DBNumUserLayers; i++)
{
if (TTMaskHasType(&mask, i))
TTMaskClearMask(&crec->dbw_visibleLayers,
&DBLayerTypeMaskTbl[i]);
}
for (; i < DBNumTypes; i++)
{
/* This part handles stacked contact types. The display */
/* routine calls DBTreeSrUniqueTiles(), which displays */
/* stacked contact types only on their home plane. Thus */
/* if we select a contact type, we should also select all */
/* stacking types for that contact that are on the same */
/* plane. */
rmask = DBResidueMask(i);
for (j = 0; j < DBNumUserLayers; j++)
if (TTMaskHasType(rmask, j))
if (TTMaskHasType(&mask, j))
if (DBPlane(i) == DBPlane(j))
TTMaskClearMask(&crec->dbw_visibleLayers,
&DBLayerTypeMaskTbl[i]);
}
crec->dbw_flags &= ~flags;
}
else
{
for (i = 0; i < DBNumUserLayers; i++)
{
if (TTMaskHasType(&mask, i))
TTMaskSetMask(&crec->dbw_visibleLayers,
&DBLayerTypeMaskTbl[i]);
}
for (; i < DBNumTypes; i++)
{
rmask = DBResidueMask(i);
for (j = 0; j < DBNumUserLayers; j++)
if (TTMaskHasType(rmask, j))
if (TTMaskHasType(&mask, j))
if (DBPlane(i) == DBPlane(j))
TTMaskSetMask(&crec->dbw_visibleLayers,
&DBLayerTypeMaskTbl[i]);
}
crec->dbw_flags |= flags;
}
WindAreaChanged(w, &w->w_screenArea);
return;
}
#define SEL_AREA 0
#define SEL_VISIBLE 1
#define SEL_CELL 2
#define SEL_LABELS 3
#define SEL_INTERSECT 4
#define SEL_CLEAR 5
#define SEL_FLAT 6
#define SEL_HELP 7
#define SEL_KEEP 8
#define SEL_MOVE 9
#define SEL_PICK 10
#define SEL_SAVE 11
#define SEL_FEEDBACK 12
#define SEL_BBOX 13
#define SEL_BOX 14
#define SEL_CHUNK 15
#define SEL_REGION 16
#define SEL_NET 17
#define SEL_SHORT 18
#define SEL_DEFAULT 19
/*
* ----------------------------------------------------------------------------
*
* cmdSelectArea --
*
* This is a utility procedure used by CmdSelect to do area
* selection.
*
* Results:
* None.
*
* Side effects:
* The selection is augmented to contain all the information on
* layers that is visible under the box, including paint, labels,
* and unexpanded subcells.
*
* ----------------------------------------------------------------------------
*/
/* ARGSUSED */
void
cmdSelectArea(layers, less, option, globmatch)
char *layers; /* Which layers are to be selected. */
bool less;
int option; /* Option from defined list above */
char *globmatch; /* Optional match string for labels */
{
SearchContext scx;
TileTypeBitMask mask;
int windowMask, xMask;
DBWclientRec *crec;
MagWindow *window;
bzero(&scx, sizeof(SearchContext));
window = ToolGetBoxWindow(&scx.scx_area, &windowMask);
if (window == NULL)
{
TxPrintf("The box isn't in a window.\n");
return;
}
/* Since the box may actually be in multiple windows, we have to
* be a bit careful. If the box is only in one window, then there's
* no problem. If it's in more than window, the cursor must
* disambiguate the windows.
*/
xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
if ((windowMask & ~xMask) != 0)
{
window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
if ((windowMask & xMask) == 0)
{
TxPrintf("The box is in more than one window; use the cursor\n");
TxPrintf("to select the one you want to select from.\n");
return;
}
}
if (CmdParseLayers(layers, &mask))
{
if (TTMaskEqual(&mask, &DBSpaceBits))
(void) CmdParseLayers("*,label", &mask);
TTMaskClearType(&mask, TT_SPACE);
}
else return;
if (less)
{
(void) SelRemoveArea(&scx.scx_area, &mask, globmatch);
return;
}
scx.scx_use = (CellUse *) window->w_surfaceID;
scx.scx_trans = GeoIdentityTransform;
crec = (DBWclientRec *) window->w_clientData;
if (option == SEL_VISIBLE)
{
int i;
for (i = 0; i < DBNumUserLayers; i++)
{
if((TTMaskHasType(&mask, i)) && !(TTMaskHasType(&crec->dbw_visibleLayers, i)))
TTMaskClearType(&mask, i);
}
}
SelectArea(&scx, &mask, crec->dbw_bitmask, globmatch);
}
/*
* ----------------------------------------------------------------------------
*
* cmdIntersectArea --
*
* This is a utility procedure used by CmdSelect to do area
* selection itersect.
*
* Results:
* None.
*
* Side effects:
* The selection is pared down to contain only the part of the
* original selection that intersects with the type "layer".
*
* ----------------------------------------------------------------------------
*/
void
cmdIntersectArea(layer)
char *layer; /* The layer to intersect with */
{
TileType ttype;
SearchContext scx;
int windowMask, xMask;
DBWclientRec *crec;
MagWindow *window;
char *lptr;
bool negate = FALSE;
bzero(&scx, sizeof(SearchContext));
window = ToolGetBoxWindow(&scx.scx_area, &windowMask);
if (window == NULL)
{
TxPrintf("The box isn't in a window.\n");
return;
}
/* Since the box may actually be in multiple windows, we have to
* be a bit careful. If the box is only in one window, then there's
* no problem. If it's in more than window, the cursor must
* disambiguate the windows.
*/
xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
if ((windowMask & ~xMask) != 0)
{
window = CmdGetRootPoint((Point *) NULL, (Rect *) NULL);
xMask = ((DBWclientRec *) window->w_clientData)->dbw_bitmask;
if ((windowMask & xMask) == 0)
{
TxPrintf("The box is in more than one window; use the cursor\n");
TxPrintf("to select the one you want to select from.\n");
return;
}
}
scx.scx_use = (CellUse *) window->w_surfaceID;
scx.scx_trans = GeoIdentityTransform;
crec = (DBWclientRec *) window->w_clientData;
/* Special behavior: "!" or "~" in front of layer name intersects */
/* with NOT(layer). */
lptr = layer;
if ((*lptr == '~') || (*lptr == '!'))
{
negate = TRUE;
lptr++;
}
ttype = DBTechNameType(lptr);
if (ttype < 0) {
TxError("Cannot parse layer type \"%s\".\n", layer);
return;
}
SelectIntersect(&scx, ttype, crec->dbw_bitmask, negate);
}
/*
* ----------------------------------------------------------------------------
*
* CmdSelect --
*
* Implement the "select" command.
*
* Usage:
* select [option args]
*
* Results:
* None.
*
* Side effects:
* The current selection is modified. See the user documentation
* for all the possible things this command can do.
*
* ----------------------------------------------------------------------------
*/
void
CmdSelect(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
TileTypeBitMask mask;
SearchContext scx;
DBWclientRec *crec;
MagWindow *window;
/* The two tables below define the allowable selection options, and
* also the message printed out by ":select help" to describe the
* options (can't use the same table for both because of the presence
* of the "more" option). Note that there's one more entry in the
* second table, due to a help message for ":select" with no arguments.
*/
static char *cmdSelectOption[] =
{
"area",
"visible",
"cell",
"labels",
"intersection",
"clear",
"flat",
"help",
"keep",
"move",
"pick",
"save",
"feedback",
"bbox",
"box",
"chunk",
"region",
"net",
"short",
NULL
};
static char *cmdSelectMsg[] =
{
"[more | less | nocycle] [layers] [at x y]\n"
" [de]select paint chunk/region/net under\n"
" cursor, or [de]select subcell if cursor\n"
" over space",
"[more | less] area [layers] [de]select all info under box in layers",
"[more | less] visible [layers] [de]select all visible info under box in layers",
"[more | less | top] cell [name] [de]select cell under cursor, or \"name\"",
"[do | no] labels [do not] select subcell labels",
"[more | less] intersection [layers] [de]select intersection of layers",
"clear clear selection",
"flat flatten the contents of the selection",
"help print this message",
"keep copy the selection to the layout",
"move move selection to a location in the layout",
"pick delete selection from layout",
"save file save selection on disk in file.mag",
"feedback [style] copy selection to feedback",
"bbox return the bounding box of the selection",
"[more | less] box | chunk | region | net [layers]\n"
" [de]select chunk/region/net specified by\n"
" the lower left corner of the current box",
"short name1 name2 find shorting path between two labels",
NULL
};
static TileType type = TT_SELECTBASE-1;
/* Type of material being pointed at.
* Remembered across commands so that when
* multiples types are pointed to, consecutive
* selections will cycle through them.
*/
static Rect lastArea = {{-100, -100}, {-200, -200}};
/* Used to remember region around what was
* pointed at in the last select command: a
* new selection in this area causes the next
* bigger thing to be selected.
*/
static int lastCommand; /* Serial number of last command: the next
* bigger thing is only selected when there
* are several select commands in a row.
*/
static Rect chunkSelection; /* Used to remember the size of the last chunk
* selected.
*/
static int level = 0; /* How big a piece to select. See definitions
* below.
*/
static CellUse *lastUse; /* The last cellUse selected. Used to step
* through multiple uses underneath the cursor.
*/
static Point lastIndices; /* The array indices of the last cell selected.
* also used to step through multiple uses.
*/
static bool lessCycle = FALSE, lessCellCycle = FALSE;
char path[200], *printPath, **msg, **optionArgs, *feedtext, *pstr, *globmatch;
TerminalPath tpath;
CellUse *use;
CellDef *rootBoxDef;
Transform trans, rootTrans, tmp1;
Point p, rootPoint, atPoint;
Rect r, selarea;
ExtRectList *rlist;
int option;
int feedstyle;
int isqual; // Command has a qualifier argument like "more", "less", etc.
int primargs; // Number of arguments other than qualifiers
bool layerspec;
bool degenerate;
bool doat = FALSE;
bool more = FALSE, less = FALSE, samePlace = TRUE, dotop = FALSE;
unsigned char labelpolicy = SEL_DO_LABELS;
#ifdef MAGIC_WRAPPER
char *tclstr;
Tcl_Obj *lobj;
#endif
/* How close two clicks must be to be considered the same point: */
#define MARGIN 2
globmatch = NULL;
bzero(&scx, sizeof(SearchContext));
windCheckOnlyWindow(&w, DBWclientID);
if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
{
TxError("Put the cursor in a layout window\n");
return;
}
/* See if "more" was given. If so, just strip off the "more" from
* the argument list and set the "more" flag. Similarly for options
* "less", "do", "no", "nocycle", "top", and "cell".
*/
isqual = 0; // Track any qualifier argument
if (cmd->tx_argc >= 2)
{
int arg1len = strlen(cmd->tx_argv[1]);
optionArgs = &cmd->tx_argv[1];
if (!strncmp(cmd->tx_argv[1], "more", arg1len))
{
more = TRUE;
less = FALSE;
isqual = 1;
optionArgs = &cmd->tx_argv[2];
}
else if (!strncmp(cmd->tx_argv[1], "less", arg1len))
{
more = FALSE;
less = TRUE;
isqual = 1;
optionArgs = &cmd->tx_argv[2];
}
else if (!strncmp(cmd->tx_argv[1], "nocycle", arg1len))
{
samePlace = FALSE;
labelpolicy = SEL_NO_LABELS;
more = FALSE;
less = FALSE;
isqual = 1;
type = TT_SELECTBASE - 1; /* avoid cycling between types */
optionArgs = &cmd->tx_argv[2];
}
else if (!strncmp(cmd->tx_argv[1], "same", arg1len))
{
/* Force this to be the same as the last selection command, */
/* even if there were other commands in between. */
lastCommand = TxCommandNumber - 1;
isqual = 1;
optionArgs = &cmd->tx_argv[2];
}
else if (!strncmp(cmd->tx_argv[1], "do", arg1len))
{
labelpolicy = SEL_DO_LABELS;
isqual = 1;
optionArgs = &cmd->tx_argv[2];
}
else if (!strncmp(cmd->tx_argv[1], "no", arg1len))
{
labelpolicy = SEL_NO_LABELS;
isqual = 1;
optionArgs = &cmd->tx_argv[2];
}
else if (!strncmp(cmd->tx_argv[1], "simple", arg1len))
{
labelpolicy = SEL_SIMPLE_LABELS;
isqual = 1;
optionArgs = &cmd->tx_argv[2];
}
else if (!strncmp(cmd->tx_argv[1], "top", arg1len))
{
if ((cmd->tx_argc >= 3) && !strncmp(cmd->tx_argv[2],
"cell", strlen(cmd->tx_argv[2])))
{
isqual = 1;
dotop = TRUE;
optionArgs = &cmd->tx_argv[2];
}
}
doat = FALSE;
if ((cmd->tx_argc - isqual > 3) && !strcmp(cmd->tx_argv[cmd->tx_argc - 3], "at"))
{
Point editPoint;
doat = TRUE;
editPoint.p_x = cmdParseCoord(w, cmd->tx_argv[cmd->tx_argc - 2],
FALSE, TRUE);
editPoint.p_y = cmdParseCoord(w, cmd->tx_argv[cmd->tx_argc - 1],
FALSE, FALSE);
GeoTransPoint(&EditToRootTransform, &editPoint, &atPoint);
/* After registering "doat", ignore the arguments */
cmd->tx_argc -= 3;
}
}
/* Check the option for validity. */
primargs = cmd->tx_argc - isqual;
if (primargs == 1)
option = SEL_DEFAULT;
else
{
char *fileName;
option = Lookup(optionArgs[0], cmdSelectOption);
if ((option < 0) && (primargs != 2))
{
TxError("\"%s\" isn't a valid select option.\n", cmd->tx_argv[1]);
option = SEL_HELP;
cmd->tx_argc = 2;
primargs = 2;
}
else if (option < 0)
{
option = SEL_DEFAULT;
if (more || less || (!samePlace))
optionArgs = &cmd->tx_argv[1];
else
optionArgs = &cmd->tx_argv[0];
}
/* options other than SEL_DEFAULT and the ones that cycle
* through (SEL_BOX/CHUNK/REGION/NET) force "level" back
* to 0.
*/
if (option != SEL_BOX && option != SEL_CHUNK && option !=
SEL_REGION && option != SEL_NET)
level = 0;
}
#ifndef NO_SIM_MODULE
SimRecomputeSel = TRUE;
#endif
switch (option)
{
/*--------------------------------------------------------------------
* Select everything under the box, perhaps looking only at
* particular layers.
*--------------------------------------------------------------------
*/
case SEL_AREA:
if (primargs > 4)
{
usageError:
TxError("Bad arguments:\n select %s\n",
cmdSelectMsg[option + 1]);
return;
}
if (primargs == 4)
globmatch = optionArgs[2]; /* Label matching by glob */
if (!(more || less)) SelectClear();
if (primargs >= 3)
cmdSelectArea(optionArgs[1], less, option, globmatch);
else cmdSelectArea("*,label,subcell", less, option, globmatch);
return;
/*--------------------------------------------------------------------
* Select everything under the box, perhaps looking only at
* particular layers, but only if its visible.
*--------------------------------------------------------------------
*/
case SEL_VISIBLE:
if (primargs > 3) goto usageError;
if (!(more || less)) SelectClear();
if (primargs == 3)
cmdSelectArea(optionArgs[1], less, option, globmatch);
else cmdSelectArea("*,label,subcell", less, option, globmatch);
return;
/*--------------------------------------------------------------------
* Select area that is the intersection of all the specified layers
*--------------------------------------------------------------------
*/
case SEL_INTERSECT:
if (primargs > 3) goto usageError;
cmdIntersectArea(optionArgs[1]);
return;
/*--------------------------------------------------------------------
* Clear out all of the material in the selection.
*--------------------------------------------------------------------
*/
case SEL_CLEAR:
if ((more) || (less) || (primargs > 2)) goto usageError;
SelectClear();
return;
case SEL_LABELS:
SelectDoLabels = labelpolicy;
if (SelectDoLabels)
TxPrintf("Selection includes subcell labels\n");
else
TxPrintf("Selection ignores subcell labels\n");
return;
/*--------------------------------------------------------------------
* Print out help information.
*--------------------------------------------------------------------
*/
case SEL_HELP:
TxPrintf("Selection commands are:\n");
for (msg = &(cmdSelectMsg[0]); *msg != NULL; msg++)
TxPrintf(" select %s\n", *msg);
return;
/*--------------------------------------------------------------------
* Print or return the bounding box of the selection
*--------------------------------------------------------------------
*/
case SEL_BBOX:
GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
#ifdef MAGIC_WRAPPER
lobj = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewIntObj(selarea.r_xbot));
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewIntObj(selarea.r_ybot));
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewIntObj(selarea.r_xtop));
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewIntObj(selarea.r_ytop));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("Select bounding box: %d %d %d %d\n",
selarea.r_xbot, selarea.r_ybot,
selarea.r_xtop, selarea.r_ytop);
#endif
return;
/*--------------------------------------------------------------------
* Make a copy of the selection at its present loction but do not
* clear the selection.
*--------------------------------------------------------------------
*/
case SEL_KEEP:
if ((more) || (less) || (primargs > 2)) goto usageError;
SelectAndCopy1();
GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
DBWHLRedraw(SelectRootDef, &selarea, FALSE);
return;
/*--------------------------------------------------------------------
* Move the selection relative to the cell def
*--------------------------------------------------------------------
*/
case SEL_MOVE:
if ((more) || (less) || (primargs != 4)) goto usageError;
p.p_x = cmdParseCoord(w, cmd->tx_argv[2], FALSE, TRUE);
p.p_y = cmdParseCoord(w, cmd->tx_argv[3], FALSE, FALSE);
/* Erase first, then recompute the transform */
GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
DBWHLRedraw(SelectRootDef, &selarea, TRUE);
GeoTransTranslate(p.p_x, p.p_y,
&GeoIdentityTransform, &SelectUse->cu_transform);
GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
DBWHLRedraw(SelectRootDef, &selarea, FALSE);
return;
/*--------------------------------------------------------------------
* Delete the selection from the layout, but don't clear the selection
* cell. This allows the selection to be dragged around independently
* of the layout.
*--------------------------------------------------------------------
*/
case SEL_PICK:
if ((more) || (less) || (primargs > 2)) goto usageError;
SelectDelete("picked", FALSE);
DBWHLRedraw(SelectRootDef, &selarea, FALSE);
return;
/*--------------------------------------------------------------------
* Flatten the contents of the selection cell, but keep the result
* within the selection cell.
*--------------------------------------------------------------------
*/
case SEL_FLAT:
if ((more) || (less) || (primargs > 2)) goto usageError;
SelectFlat();
return;
/*--------------------------------------------------------------------
* Save the selection as a new Magic cell on disk.
*--------------------------------------------------------------------
*/
case SEL_SAVE:
if (primargs != 3) goto usageError;
/* Be sure to paint DRC check information into the cell before
* saving it! Otherwise DRC problems may not be detected. Also
* be sure to adjust labels in the cell.
*/
DBAdjustLabels(SelectDef, &TiPlaneRect);
DBPaintPlane(SelectDef->cd_planes[PL_DRC_CHECK],
&SelectDef->cd_bbox,
DBStdPaintTbl(TT_CHECKPAINT, PL_DRC_CHECK),
(PaintUndoInfo *) NULL);
DBUpdateStamps(SelectDef);
cmdSaveCell(SelectDef, cmd->tx_argv[2], FALSE, FALSE);
return;
/*--------------------------------------------------------------------
* Copy the selection into a feedback area for permanent display
*--------------------------------------------------------------------
*/
case SEL_FEEDBACK:
feedtext = NULL;
feedstyle = STYLE_ORANGE1;
if (primargs > 2)
{
/* Get style (To do) */
feedstyle = GrGetStyleFromName(cmd->tx_argv[2]);
if (feedstyle == -1)
{
TxError("Unknown style %s\n", cmd->tx_argv[2]);
TxError("Use a number or one of the long names in the"
" .dstyle file\n");
return;
}
if (primargs > 3)
feedtext = cmd->tx_argv[3];
}
SelCopyToFeedback(SelectRootDef, SelectUse, feedstyle,
(feedtext == NULL) ? "selection" : feedtext);
GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
DBWHLRedraw(SelectRootDef, &selarea, FALSE);
return;
/*--------------------------------------------------------------------
* Given a net selection and two labels, determine the shortest
* connecting path between the two labels.
*--------------------------------------------------------------------
*/
case SEL_SHORT:
if (primargs != 4) goto usageError;
rlist = SelectShort(cmd->tx_argv[2], cmd->tx_argv[3]);
if (rlist == NULL)
{
TxError("No shorting path found between source and destination!\n");
return;
}
/* Delete selection and replace with contents of rlist */
/* (To do: Alternately just return a list of the contents */
/* of rlist) */
SelectClear();
while (rlist != NULL)
{
/* Paint rlist back into SelectDef */
DBPaint(SelectDef, &rlist->r_r, rlist->r_type);
/* cleanup as we go */
freeMagic(rlist);
rlist = rlist->r_next;
}
/* Force erase and redraw of the selection */
DBReComputeBbox(SelectDef);
DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS,
(TileTypeBitMask *)NULL);
GeoTransRect(&SelectUse->cu_transform, &SelectDef->cd_bbox, &selarea);
DBWHLRedraw(SelectRootDef, &selarea, FALSE);
break;
case SEL_BOX: case SEL_CHUNK: case SEL_REGION: case SEL_NET:
if (primargs > 3) goto usageError;
if (primargs == 3)
layerspec = TRUE;
else
layerspec = FALSE;
goto Okay;
/*--------------------------------------------------------------------
* The default case (no args): see what's under the cursor. Select
* paint if there is any, else select a cell. In both cases,
* multiple clicks cycle through larger and larger selections. The
* SEL_CELL option also comes here (to share initialization code) but
* quickly branches away.
*--------------------------------------------------------------------
*/
case SEL_DEFAULT:
if (primargs == 2)
layerspec = TRUE;
else
layerspec = FALSE;
goto Okay;
case SEL_CELL:
layerspec = FALSE;
degenerate = FALSE;
Okay:
if (!(more || less)) SelectClear();
if (option == SEL_BOX || option == SEL_CHUNK || option == SEL_REGION
|| option == SEL_NET)
{
int windowMask, xMask;
if (!ToolGetBox (&rootBoxDef, NULL)) {
TxError ("Box tool must be present\n");
return;
}
window = ToolGetBoxWindow (&scx.scx_area, &windowMask);
if (!window)
TxError ("Box tool does not exist in any window\n");
xMask = ((DBWclientRec *)window->w_clientData)->dbw_bitmask;
if ((windowMask & ~xMask) != 0) {
window = CmdGetRootPoint ((Point *) NULL, (Rect *) NULL);
xMask = ((DBWclientRec *)window->w_clientData)->dbw_bitmask;
if ((windowMask & xMask) == 0) {
TxError("Box present in multiple windows; use the"
"cursor\nto select the one you want\n");
return;
}
}
scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1;
degenerate = TRUE;
}
else if (doat)
{
scx.scx_area.r_xbot = atPoint.p_x;
scx.scx_area.r_ybot = atPoint.p_y;
scx.scx_area.r_xtop = atPoint.p_x + 1;
scx.scx_area.r_ytop = atPoint.p_y + 1;
windCheckOnlyWindow(&w, DBWclientID);
window = w;
}
else
{
char *aptr;
int i;
window = CmdGetRootPoint((Point *) NULL, &scx.scx_area);
/* Recast command with "at x y" at the end for logging */
for (i = 0; i < cmd->tx_argc; i++)
{
aptr = cmd->tx_argv[i] + strlen(cmd->tx_argv[i]);
*aptr = ' ';
}
sprintf(aptr + 1, "at %di %di", scx.scx_area.r_xbot,
scx.scx_area.r_ybot);
TxRebuildCommand(cmd);
}
if (window == NULL) return;
scx.scx_use = (CellUse *) window->w_surfaceID;
scx.scx_trans = GeoIdentityTransform;
crec = (DBWclientRec *) window->w_clientData;
DBSeeTypesAll(scx.scx_use, &scx.scx_area, crec->dbw_bitmask, &mask);
TTMaskAndMask(&mask, &crec->dbw_visibleLayers);
TTMaskAndMask(&mask, &DBAllButSpaceAndDRCBits);
/* See if we're pointing at the same place as we were the last time
* this command was invoked, and if this command immediately follows
* another selection comand. If not, it is important to set lastUse
* to NULL, otherwise trouble occurs if lastUse is an instance that
* was deleted (note that this is not foolproof: deleting an
* instance followed by selecting an instance that was occupying the
* same space WILL cause a crash).
*/
if (doat)
{
if (!GEO_ENCLOSE(&scx.scx_area.r_ll, &lastArea)
|| ((lastCommand + 1) != TxCommandNumber))
{
samePlace = FALSE;
lastUse = NULL;
}
lastArea.r_xbot = scx.scx_area.r_xbot - MARGIN;
lastArea.r_ybot = scx.scx_area.r_ybot - MARGIN;
lastArea.r_xtop = scx.scx_area.r_xbot + MARGIN;
lastArea.r_ytop = scx.scx_area.r_ybot + MARGIN;
}
else
{
if (!GEO_ENCLOSE(&cmd->tx_p, &lastArea)
|| ((lastCommand + 1) != TxCommandNumber))
{
samePlace = FALSE;
lastUse = NULL;
}
lastArea.r_xbot = cmd->tx_p.p_x - MARGIN;
lastArea.r_ybot = cmd->tx_p.p_y - MARGIN;
lastArea.r_xtop = cmd->tx_p.p_x + MARGIN;
lastArea.r_ytop = cmd->tx_p.p_y + MARGIN;
}
lastCommand = TxCommandNumber;
/* If there's material under the cursor, select some paint.
* Repeated selections at the same place result in first a
* chunk being selected, then a region of a particular type,
* then a whole net.
*/
if (!TTMaskIsZero(&mask) && (option != SEL_CELL))
{
if (layerspec == TRUE)
{
/* User specified a layer. Use the smallest type
* specified by the user and present under the
* box/cursor to begin the selection
*/
TileTypeBitMask uMask;
if (CmdParseLayers (optionArgs[1], &uMask))
{
if (TTMaskEqual (&uMask, &DBSpaceBits))
CmdParseLayers ("*,label", &uMask);
}
else
{
TxError ("Invalid layer specification\n");
return;
}
TTMaskAndMask (&mask, &uMask);
if (TTMaskIsZero(&mask))
{
/* If the area was degenerate (point or line box) */
/* try looking in the other direction before */
/* giving up (added by Tim 3/18/07) */
if (degenerate)
{
scx.scx_area.r_xtop--;
scx.scx_area.r_ytop--;
scx.scx_area.r_xbot--;
scx.scx_area.r_ybot--;
DBSeeTypesAll(scx.scx_use, &scx.scx_area,
crec->dbw_bitmask, &mask);
TTMaskAndMask(&mask, &crec->dbw_visibleLayers);
TTMaskAndMask(&mask, &DBAllButSpaceAndDRCBits);
TTMaskAndMask (&mask, &uMask);
if (TTMaskIsZero(&mask))
{
TxError ("No paint of this type under "
"or next to the cursor/box\n");
return;
}
}
else
{
TxError ("No paint of this type under the cursor/box\n");
return;
}
}
}
/* Set connectivity searching level */
if (option == SEL_CHUNK || option == SEL_REGION || option == SEL_NET)
{
samePlace = TRUE;
level = option;
if (level != SEL_CHUNK)
{
int count = 0;
for (type++; count <= DBNumUserLayers; type++, count++)
{
if (type >= DBNumUserLayers)
type = TT_SELECTBASE;
if (TTMaskHasType(&mask, type)) break;
}
}
}
else
{
if (samePlace && lessCycle == less)
{
level++;
if (level > SEL_NET) level = SEL_CHUNK;
}
else level = SEL_CHUNK;
if ((level == 1) || (level != SEL_CHUNK &&
!TTMaskHasType (&mask, type))) {
/* User specified a new mask, and the current tile
* type being expanded is not in the set of types
* which the user wants us to use => reset level
*/
level = SEL_CHUNK;
SelectClear();
}
}
lessCycle = less;
if (level == SEL_CHUNK)
{
/* Pick a tile type to use for selection. If there are
* several different types under the cursor, pick one of
* them. This code remembers which type was used to
* choose last time, so that consecutive selections will
* use different types.
*/
int count = 0;
for (type++; count <= DBNumUserLayers; type++, count++)
{
if (type >= DBNumUserLayers) /* used to be DBNumTypes */
type = TT_SELECTBASE;
if (TTMaskHasType(&mask, type)) break;
}
/* Check for selection between DBNumUserLayers & DBNumTypes */
/* Normally we don't want to use these types; only if one */
/* is the only bit set in the list. */
if (count == DBNumUserLayers)
for (type = DBNumUserLayers; type < DBNumTypes; type++)
if (TTMaskHasType(&mask, type)) break;
/* Sanity check */
if (type == DBNumTypes) return;
SelectChunk(&scx, type, crec->dbw_bitmask, &chunkSelection, less);
if (!less)
DBWSetBox(scx.scx_use->cu_def, &chunkSelection);
}
if (level == SEL_REGION)
{
/* If a region has the same size as the preceding chunk,
* then we haven't added anything to the selection, so
* go on immediately and select the whole net.
*/
Rect area;
SelectRegion(&scx, type, crec->dbw_bitmask, &area, less);
if (GEO_SURROUND(&chunkSelection, &area))
level = SEL_NET;
}
if (level == SEL_NET)
{
SelectNet(&scx, type, crec->dbw_bitmask, (Rect *) NULL, less);
}
return;
}
/*--------------------------------------------------------------------
* We get here either if the SEL_CELL option is requested, or under
* the SEL_DEFAULT case where there's no paint under the mouse. In
* this case, select a subcell.
*--------------------------------------------------------------------
*/
if (layerspec == TRUE)
{
TileTypeBitMask uMask;
if (CmdParseLayers (optionArgs[1], &uMask))
{
TxError ("No paint of this type under the cursor/box\n");
}
else
{
TxError ("Invalid layer specification\n");
}
return;
}
if (primargs > 3)
if (strcmp(cmd->tx_argv[cmd->tx_argc - 3], "at"))
goto usageError;
/* If an explicit cell use id is provided, look for that cell
* and select it. In this case, defeat all of the "multiple
* click" code.
*/
if (((primargs == 3) || (primargs == 6)) &&
(optionArgs == &cmd->tx_argv[2]) &&
(more == FALSE) && (less == FALSE))
{
use = lastUse = scx.scx_use;
p.p_x = scx.scx_use->cu_xlo;
p.p_y = scx.scx_use->cu_ylo;
trans = GeoIdentityTransform;
printPath = scx.scx_use->cu_id;
}
else if ((primargs == 3) || (primargs == 6))
{
SearchContext scx2;
bzero(&scx2, sizeof(SearchContext));
DBTreeFindUse(optionArgs[1], scx.scx_use, &scx2);
use = scx2.scx_use;
if (use == NULL)
{
TxError("Couldn't find a cell use named \"%s\"\n",
optionArgs[1]);
return;
}
trans = scx2.scx_trans;
p.p_x = scx2.scx_x;
p.p_y = scx2.scx_y;
printPath = optionArgs[1];
samePlace = FALSE;
lastArea.r_xbot = lastArea.r_ybot = -1000;
lastArea.r_xtop = lastArea.r_ytop = -1000;
}
else
{
/* Find the cell underneath the cursor. If this is a
* second or later click at the same position, select
* the "next" cell underneath the point (see comments
* in DBSelectCell() for what "next" means).
*/
tpath.tp_first = tpath.tp_next = path;
tpath.tp_last = &path[(sizeof path) - 2];
if ((lastUse == scx.scx_use) || !samePlace || (lessCellCycle != less))
lastUse = NULL;
lessCellCycle = less;
use = DBSelectCell(scx.scx_use, lastUse, &lastIndices,
&scx.scx_area, crec->dbw_bitmask, &trans, &p, &tpath);
/* Use the window's root cell if nothing else is found, */
/* or if the command was "select top cell". */
if ((dotop == TRUE) || (use == NULL))
{
use = lastUse = scx.scx_use;
p.p_x = scx.scx_use->cu_xlo;
p.p_y = scx.scx_use->cu_ylo;
trans = GeoIdentityTransform;
printPath = scx.scx_use->cu_id;
}
else
{
printPath = strchr(path, '/');
if (printPath == NULL)
printPath = path;
else printPath++;
}
}
lastUse = use;
lastIndices = p;
/* The translation stuff is funny, since we got one
* element of the array, but not necessarily the
* lower-left element. To get the transform for the
* array as a whole, subtract off for the indx of
* the element.
*/
GeoInvertTrans(DBGetArrayTransform(use, p.p_x, p.p_y), &tmp1);
GeoTransTrans(&tmp1, &trans, &rootTrans);
if (less)
SelectRemoveCellUse(use, &rootTrans);
else
SelectCell(use, scx.scx_use->cu_def, &rootTrans, samePlace);
GeoTransRect(&trans, &use->cu_def->cd_bbox, &r);
DBWSetBox(scx.scx_use->cu_def, &r);
#ifdef MAGIC_WRAPPER
/* Remove any backslash escapes so that Tcl_escape() doesn't
* double-escape them.
*/
for (pstr = printPath; *pstr != '\0';)
if ((*pstr == '\\') && ((*(pstr + 1) == '[') || (*(pstr + 1) == ']')))
memmove(pstr, pstr + 1, 1 + strlen(pstr + 1));
else pstr++;
tclstr = Tcl_escape(printPath);
Tcl_SetResult(magicinterp, tclstr, TCL_DYNAMIC);
#else
TxPrintf("Selected cell is %s (%s)\n", use->cu_def->cd_name,
printPath);
#endif
return;
}
}
/*
*-----------------------------------------------------------------------
* Callback functions used with CmdSetLabel to change individual labels
*-----------------------------------------------------------------------
*/
int
cmdLabelTextFunc(label, cellUse, transform, text)
Label *label;
CellUse *cellUse;
Transform *transform;
char *text;
{
Label *newlab;
CellDef *cellDef = cellUse->cu_def;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
if (text == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewStringObj(label->lab_text, -1));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%s\n", label->lab_text);
#endif
}
else if (strcmp(text, label->lab_text))
{
newlab = DBPutFontLabel(cellDef, &label->lab_rect, label->lab_font,
label->lab_size, label->lab_rotate, &label->lab_offset,
label->lab_just, text, label->lab_type, label->lab_flags,
label->lab_port);
DBEraseLabelsByContent(cellDef, &label->lab_rect, -1, label->lab_text);
DBWLabelChanged(cellDef, newlab, DBW_ALLWINDOWS);
DBWHLRedraw(SelectRootDef, &SelectDef->cd_extended, TRUE);
DBWAreaChanged(SelectDef, &SelectDef->cd_extended, DBW_ALLWINDOWS,
&DBAllButSpaceBits);
DBCellSetModified(cellDef, TRUE);
}
return 0;
}
int
cmdLabelRotateFunc(label, cellUse, transform, value)
Label *label;
CellUse *cellUse;
Transform *transform;
int *value;
{
CellDef *cellDef = cellUse->cu_def;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
Rect selarea;
if (value == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewIntObj(label->lab_rotate));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%d\n", label->lab_rotate);
#endif
}
else if (label->lab_rotate != *value)
{
DBUndoEraseLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
label->lab_rotate = *value;
DBFontLabelSetBBox(label);
DBUndoPutLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
DBCellSetModified(cellDef, TRUE);
}
return 0;
}
int
cmdLabelSizeFunc(label, cellUse, transform, value)
Label *label;
CellUse *cellUse;
Transform *transform;
int *value;
{
CellDef *cellDef = cellUse->cu_def;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
if (value == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewDoubleObj((double)label->lab_size / 8.0));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%g\n", (double)label->lab_size / 8.0);
#endif
}
else if (label->lab_size != *value)
{
DBUndoEraseLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
label->lab_size = *value;
DBFontLabelSetBBox(label);
DBUndoPutLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
DBCellSetModified(cellDef, TRUE);
}
return 0;
}
int
cmdLabelJustFunc(label, cellUse, transform, value)
Label *label;
CellUse *cellUse;
Transform *transform;
int *value;
{
CellDef *cellDef = cellUse->cu_def;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
if (value == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewStringObj(GeoPosToName(label->lab_just), -1));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%s\n", GeoPosToName(label->lab_just));
#endif
}
else if (label->lab_just != *value)
{
DBUndoEraseLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
label->lab_just = *value;
DBFontLabelSetBBox(label);
DBUndoPutLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
DBCellSetModified(cellDef, TRUE);
}
return 0;
}
int
cmdLabelLayerFunc(label, cellUse, transform, value)
Label *label;
CellUse *cellUse;
Transform *transform;
int *value;
{
CellDef *cellDef = cellUse->cu_def;
TileType ttype;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
if (value == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewStringObj(DBTypeLongNameTbl[label->lab_type], -1));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%s\n", DBTypeLongNameTbl[label->lab_type]);
#endif
}
else
{
ttype = (TileType)(*value);
if (label->lab_type != ttype)
{
DBUndoEraseLabel(cellDef, label);
label->lab_type = ttype;
DBUndoPutLabel(cellDef, label);
DBCellSetModified(cellDef, TRUE);
}
}
return 0;
}
int
cmdLabelStickyFunc(label, cellUse, transform, value)
Label *label;
CellUse *cellUse;
Transform *transform;
int *value;
{
CellDef *cellDef = cellUse->cu_def;
int newvalue;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
if (value == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewBooleanObj((label->lab_flags & LABEL_STICKY) ? 1 : 0));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%s\n", (label->lab_flags & LABEL_STICKY) ? "true" : "false");
#endif
}
else
{
newvalue = label->lab_flags;
newvalue &= ~LABEL_STICKY;
newvalue |= *value;
if (newvalue != label->lab_flags)
{
/* Label does not change appearance, just need to record the change */
DBUndoEraseLabel(cellDef, label);
label->lab_flags = newvalue;
DBUndoPutLabel(cellDef, label);
DBCellSetModified(cellDef, TRUE);
}
}
return 0;
}
int
cmdLabelOffsetFunc(label, cellUse, transform, point)
Label *label;
CellUse *cellUse;
Transform *transform;
Point *point;
{
CellDef *cellDef = cellUse->cu_def;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj, *pobj;
#endif
if (point == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
pobj = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(magicinterp, lobj, pobj);
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewDoubleObj((double)label->lab_offset.p_x / 8.0));
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewDoubleObj((double)label->lab_offset.p_y / 8.0));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%g %g\n", (double)(label->lab_offset.p_x) / 8.0,
(double)(label->lab_offset.p_y) / 8.0);
#endif
}
else if (!GEO_SAMEPOINT(label->lab_offset, *point))
{
DBUndoEraseLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
label->lab_offset = *point;
DBFontLabelSetBBox(label);
DBUndoPutLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
DBCellSetModified(cellDef, TRUE);
}
return 0;
}
int
cmdLabelRectFunc(label, cellUse, transform, rect)
Label *label;
CellUse *cellUse;
Transform *transform;
Rect *rect;
{
CellDef *cellDef = cellUse->cu_def;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj, *pobj;
#endif
if (rect == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
pobj = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(magicinterp, lobj, pobj);
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewIntObj((double)label->lab_rect.r_xbot));
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewIntObj((double)label->lab_rect.r_ybot));
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewIntObj((double)label->lab_rect.r_xtop));
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewIntObj((double)label->lab_rect.r_ytop));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%d %d %d %d\n",
label->lab_rect.r_xbot, label->lab_rect.r_ybot,
label->lab_rect.r_xtop, label->lab_rect.r_ytop);
#endif
}
else if (!GEO_SAMERECT(label->lab_rect, *rect))
{
DBUndoEraseLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
label->lab_rect = *rect;
DBFontLabelSetBBox(label);
DBUndoPutLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
DBCellSetModified(cellDef, TRUE);
}
return 0;
}
int
cmdLabelFontFunc(label, cellUse, transform, font)
Label *label;
CellUse *cellUse;
Transform *transform;
int *font;
{
CellDef *cellDef = cellUse->cu_def;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
if (font == NULL)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_GetObjResult(magicinterp);
if (label->lab_font == -1)
Tcl_ListObjAppendElement(magicinterp, lobj, Tcl_NewStringObj("default", 7));
else
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewStringObj(DBFontList[label->lab_font]->mf_name, -1));
Tcl_SetObjResult(magicinterp, lobj);
#else
if (label->lab_font == -1)
TxPrintf("default\n");
else
TxPrintf("%s\n", DBFontList[label->lab_font]->mf_name);
#endif
}
else if (label->lab_font != *font)
{
DBUndoEraseLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
label->lab_font = *font;
if ((*font > -1) && (label->lab_size == 0)) label->lab_size = DBLambda[1];
DBFontLabelSetBBox(label);
DBUndoPutLabel(cellDef, label);
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
DBCellSetModified(cellDef, TRUE);
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* CmdSetLabel --
*
* Implement the "setlabel" command.
* Query or change properties of a (selected) label in the edit cell
*
* Usage:
* setlabel [-default] option [name]
*
* Option may be one of:
* text
* font
* justify
* size
* offset
* rotate
* layer
*
* Results:
* None.
*
* Side effects:
* Modified label entry. The "setlabel font" command may load font
* information if the requested font is not already in the font list.
* "setlabel font <name>" can be used without any select to load fonts
* from a startup script.
*
* Use with "-default" causes the DefaultLabel structure to be created
* (if not existing already) and set with the given value. Subsequent
* use of the "label" command will start with the given defaults, then
* apply whatever non-default values are specified in the command.
*
* ----------------------------------------------------------------------------
*/
#define SETLABEL_BOX 0
#define SETLABEL_FONT 1
#define SETLABEL_FONTLIST 2
#define SETLABEL_JUSTIFY 3
#define SETLABEL_LAYER 4
#define SETLABEL_OFFSET 5
#define SETLABEL_ROTATE 6
#define SETLABEL_SIZE 7
#define SETLABEL_STICKY 8
#define SETLABEL_TEXT 9
#define SETLABEL_HELP 10
void
CmdSetLabel(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int pos = -1, font = -1, size = 0, rotate = 0, flags = 0;
int locargc, argstart = 1;
char **msg;
Point offset;
Rect rect;
TileType ttype;
int option;
bool doDefault = FALSE;
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
#endif
static char *cmdLabelYesNo[] = { "no", "false", "off", "0",
"yes", "true", "on", "1", 0 };
static char *cmdLabelSetOption[] =
{
"box <lx> <ly> <ux> <uy> change/get label box",
"font <name> change/get label font",
"fontlist list available fonts",
"justify <position> change/get label justification",
"layer <type> change/get layer type",
"offset <x> <y> change/get label offset",
"rotate <degrees> change/get label rotation",
"size <value> change/get label size",
"sticky [true|false] change/get sticky property",
"text <text> change/get label text",
"help print this help info",
NULL
};
locargc = cmd->tx_argc;
if (locargc > 2)
{
if (!strncmp(cmd->tx_argv[1], "-def", 4))
{
if (DefaultLabel == NULL)
{
DefaultLabel = (Label *)mallocMagic(sizeof(Label));
/* Set default defaults (lab_text is ignored) */
DefaultLabel->lab_just = -1;
DefaultLabel->lab_size = 0;
DefaultLabel->lab_font = -1;
DefaultLabel->lab_rotate = 0;
DefaultLabel->lab_flags = 0;
DefaultLabel->lab_offset.p_x = 0;
DefaultLabel->lab_offset.p_y = 0;
DefaultLabel->lab_type = (TileType)(-1);
}
doDefault = TRUE;
locargc--;
argstart++;
}
}
if (locargc < 2)
option = SETLABEL_HELP;
else
option = Lookup(cmd->tx_argv[argstart], cmdLabelSetOption);
if ((option != SETLABEL_BOX) && (locargc > 4))
option = SETLABEL_HELP;
else if ((option == SETLABEL_BOX) && (locargc > 6))
option = SETLABEL_HELP;
switch (option)
{
case SETLABEL_FONTLIST:
#ifdef MAGIC_WRAPPER
lobj = Tcl_NewListObj(0, NULL);
for (font = 0; font < DBNumFonts; font++)
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewStringObj(DBFontList[font]->mf_name, -1));
Tcl_SetObjResult(magicinterp, lobj);
#else
for (font = 0; font < DBNumFonts; font++)
TxPrintf("%s ", DBFontList[font]->mf_name);
TxPrintf("\n");
#endif
break;
case SETLABEL_TEXT:
if (doDefault)
{
TxError("Cannot set a default label text.\n");
}
else if (EditCellUse)
{
SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelTextFunc, (locargc == 3) ?
(ClientData)cmd->tx_argv[argstart + 1] : (ClientData)NULL);
}
break;
case SETLABEL_FONT:
if (locargc >= 2 && locargc <= 4)
{
/* This option is used to see the font name corresponding
* to a font number.
*/
if ((locargc == 3) && StrIsInt(cmd->tx_argv[argstart + 1]))
{
int font = atoi(cmd->tx_argv[argstart + 1]);
if (font < -1 || font >= DBNumFonts)
{
if (DBNumFonts == 0)
TxError("No vector outline fonts are loaded.\n");
else
TxError("Font index out of range (0 to %d)\n",
DBNumFonts - 1);
}
else if (font == -1)
TxPrintf("default\n");
else
TxPrintf("%s\n", DBFontList[font]->mf_name);
}
else if ((locargc == 3 || locargc == 4) &&
!StrIsInt(cmd->tx_argv[argstart + 1]))
{
font = DBNameToFont(cmd->tx_argv[argstart + 1]);
if (font < -1)
{
float scale = 1.0;
if ((locargc == 4) && StrIsNumeric(cmd->tx_argv[argstart + 2]))
scale = (float)atof(cmd->tx_argv[argstart + 2]);
if (DBLoadFont(cmd->tx_argv[argstart + 1], scale) != 0)
TxError("Error loading font \"%s\"\n", cmd->tx_argv[argstart + 1]);
font = DBNameToFont(cmd->tx_argv[argstart + 1]);
if (font < -1) break;
}
}
if (doDefault)
{
if (locargc == 2)
{
font = DefaultLabel->lab_font;
if (font == -1)
#ifdef MAGIC_WRAPPER
Tcl_SetResult(magicinterp, "default", TCL_VOLATILE);
#else
TxPrintf("default\n");
#endif
else
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp,
Tcl_NewStringObj(DBFontList[font]->mf_name, -1));
#else
TxPrintf("%s\n", DBFontList[font]->mf_name);
#endif
}
else
DefaultLabel->lab_font = font;
}
else if (EditCellUse)
{
SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelFontFunc, (locargc == 3) ?
(ClientData)&font : (ClientData)NULL);
}
}
break;
case SETLABEL_JUSTIFY:
if (locargc == 3)
{
pos = GeoNameToPos(cmd->tx_argv[argstart + 1], FALSE, TRUE);
if (pos < 0) break;
}
if (doDefault)
{
if (locargc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp,
Tcl_NewStringObj(GeoPosToName(DefaultLabel->lab_just),
-1));
#else
TxPrintf("%s\n", GeoPosToName(DefaultLabel->lab_just));
#endif
}
else
DefaultLabel->lab_just = pos;
}
else if (EditCellUse)
{
SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelJustFunc, (locargc == 3) ?
(ClientData)&pos : (ClientData)NULL);
}
break;
case SETLABEL_SIZE:
if (locargc == 3)
{
if (StrIsNumeric(cmd->tx_argv[argstart + 1]))
size = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8);
if (size <= 0) break;
}
if (doDefault)
{
if (locargc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp,
Tcl_NewIntObj(DefaultLabel->lab_size));
#else
TxPrintf("%d\n", DefaultLabel->lab_size);
#endif
}
else
DefaultLabel->lab_size = size;
}
else if (EditCellUse)
{
SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelSizeFunc, (locargc == 3) ?
(ClientData)&size : (ClientData)NULL);
}
break;
case SETLABEL_OFFSET:
if (locargc == 3)
{
char *yp;
if ((yp = strchr(cmd->tx_argv[argstart + 1], ' ')) != NULL)
{
*yp = '\0';
offset.p_x = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8);
offset.p_y = cmdScaleCoord(w, yp + 1, TRUE, FALSE, 8);
}
else
{
TxError("Usage: setlabel offset <x> <y>\n");
return;
}
}
else if (locargc == 4)
{
offset.p_x = cmdScaleCoord(w, cmd->tx_argv[argstart + 1], TRUE, TRUE, 8);
offset.p_y = cmdScaleCoord(w, cmd->tx_argv[argstart + 2], TRUE, FALSE, 8);
}
if (doDefault)
{
if (locargc == 2)
{
#ifdef MAGIC_WRAPPER
lobj = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewIntObj(DefaultLabel->lab_offset.p_x));
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewIntObj(DefaultLabel->lab_offset.p_y));
Tcl_SetObjResult(magicinterp, lobj);
#else
TxPrintf("%d %d\n", DefaultLabel->lab_offset.p_x,
DefaultLabel->lab_offset.p_y);
#endif
}
else
DefaultLabel->lab_offset = offset;
}
else if (EditCellUse)
{
SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelOffsetFunc, (locargc != 2) ?
(ClientData)&offset : (ClientData)NULL);
}
break;
case SETLABEL_BOX:
if (doDefault)
{
TxError("Cannot set a default label box.\n");
}
else if (EditCellUse)
{
if (locargc == 3)
{
/* Useful with [box values] */
char *yp = cmd->tx_argv[argstart + 1];
rect.r_xbot = cmdScaleCoord(w, yp, TRUE, TRUE, 1);
yp = strchr(yp, ' ');
if (yp != NULL)
{
*yp = '\0';
yp++;
while (isspace(*yp) && (*yp != '\0')) yp++;
rect.r_ybot = cmdScaleCoord(w, yp, TRUE, FALSE, 1);
yp = strchr(yp, ' ');
if (yp != NULL)
{
*yp = '\0';
yp++;
while (isspace(*yp) && (*yp != '\0')) yp++;
rect.r_xtop = cmdScaleCoord(w, yp, TRUE, TRUE, 1);
yp = strchr(yp, ' ');
if (yp != NULL)
{
*yp = '\0';
yp++;
while (isspace(*yp) && (*yp != '\0')) yp++;
rect.r_ytop = cmdScaleCoord(w, yp, TRUE, FALSE, 1);
}
else
TxError("Label box requires four coordinates.\n");
}
else
{
rect.r_xtop = rect.r_xbot;
rect.r_ytop = rect.r_ybot;
yp = cmd->tx_argv[argstart + 1];
}
}
else
TxError("Label box requires four coordinates.\n");
if (yp == NULL) break; /* Error condition */
}
else if (locargc == 6)
{
rect.r_xbot = cmdScaleCoord(w, cmd->tx_argv[argstart + 1],
TRUE, TRUE, 1);
rect.r_ybot = cmdScaleCoord(w, cmd->tx_argv[argstart + 2],
TRUE, FALSE, 1);
rect.r_xtop = cmdScaleCoord(w, cmd->tx_argv[argstart + 3],
TRUE, TRUE, 1);
rect.r_ytop = cmdScaleCoord(w, cmd->tx_argv[argstart + 4],
TRUE, FALSE, 1);
}
SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelRectFunc,
((locargc == 6) || (locargc == 3)) ?
(ClientData)&rect : (ClientData)NULL);
}
break;
case SETLABEL_ROTATE:
if (locargc == 3)
{
if (StrIsInt(cmd->tx_argv[argstart + 1]))
rotate = atoi(cmd->tx_argv[argstart + 1]);
}
if (doDefault)
{
if (locargc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp,
Tcl_NewIntObj(DefaultLabel->lab_rotate));
#else
TxPrintf("%d\n", DefaultLabel->lab_rotate);
#endif
}
else
DefaultLabel->lab_rotate = rotate;
}
else if (EditCellUse)
{
SelEnumLabelsMirror(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelRotateFunc, (locargc == 3) ?
(ClientData)&rotate : (ClientData)NULL);
}
break;
case SETLABEL_STICKY:
if (locargc == 3)
{
option = Lookup(cmd->tx_argv[argstart + 1], cmdLabelYesNo);
if (option < 0)
{
TxError("Unknown sticky option \"%s\"\n", cmd->tx_argv[argstart + 1]);
break;
}
flags = (option <= 3) ? 0 : LABEL_STICKY;
}
if (doDefault)
{
if (locargc == 2)
{
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp,
Tcl_NewBooleanObj((DefaultLabel->lab_flags &
LABEL_STICKY) ? TRUE : FALSE));
#else
TxPrintf("%d\n", (DefaultLabel->lab_flags & LABEL_STICKY) ? 1 : 0);
#endif
}
else
DefaultLabel->lab_flags = flags;
}
else if (EditCellUse)
{
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelStickyFunc, (locargc == 3) ?
(ClientData)&flags : (ClientData)NULL);
}
break;
case SETLABEL_LAYER:
if (locargc == 3)
{
if (!strcasecmp(cmd->tx_argv[argstart + 1], "default"))
ttype = -1;
else
{
ttype = DBTechNoisyNameType(cmd->tx_argv[argstart + 1]);
if (ttype < 0) break;
}
}
if (doDefault)
{
if (locargc == 2)
{
if (DefaultLabel->lab_type == (TileType)(-1))
#ifdef MAGIC_WRAPPER
Tcl_SetResult(magicinterp, "default", TCL_VOLATILE);
#else
TxPrintf("default\n");
#endif
else
#ifdef MAGIC_WRAPPER
Tcl_SetResult(magicinterp,
DBTypeLongNameTbl[DefaultLabel->lab_type],
TCL_VOLATILE);
#else
TxPrintf("%s\n", DBTypeLongNameTbl[DefaultLabel->lab_type]);
#endif
}
else
DefaultLabel->lab_type = ttype;
}
else if (EditCellUse)
{
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
cmdLabelLayerFunc, (locargc == 3) ?
(ClientData)&ttype : (ClientData)NULL);
}
break;
case SETLABEL_HELP:
TxError("Usage: setlabel [option], where [option] is one of:\n");
for (msg = &(cmdLabelSetOption[0]); *msg != NULL; msg++)
{
TxError(" %s\n", *msg);
}
break;
default:
TxError("Unknown setlabel option \"%s\"\n", cmd->tx_argv[argstart]);
break;
}
}
/*
* ----------------------------------------------------------------------------
*
* CmdSideways --
*
* Implement the "sideways" command.
*
* Usage:
* sideways
*
* Results:
* None.
*
* Side effects:
* The selection and box are flipped left-to-right, using the
* center of the selection as the axis for flipping.
*
* ----------------------------------------------------------------------------
*/
void
CmdSideways(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Transform trans;
Rect rootBox, bbox;
CellDef *rootDef;
if (cmd->tx_argc != 1)
{
TxError("Usage: %s\n", cmd->tx_argv[0]);
return;
}
if (!ToolGetEditBox((Rect *)NULL)) return;
/* To flip the selection sideways, first flip it around the
* y-axis, then move it back so its lower-left corner is in
* the same place that it used to be.
*/
GeoTransRect(&GeoSidewaysTransform, &SelectDef->cd_bbox, &bbox);
GeoTranslateTrans(&GeoSidewaysTransform,
SelectDef->cd_bbox.r_xbot - bbox.r_xbot,
SelectDef->cd_bbox.r_ybot - bbox.r_ybot, &trans);
SelectTransform(&trans);
/* Flip the box, if it exists and is in the same window as the
* selection.
*/
if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef))
{
Rect newBox;
GeoTransRect(&trans, &rootBox, &newBox);
DBWSetBox(rootDef, &newBox);
}
return;
}
/*
* ----------------------------------------------------------------------------
*
* CmdShell
*
* Implement the "shell" or "!" command.
*
* Usage:
* shell [command]
*
* Results:
* Executes the command in a unix shell
*
* Side effects:
* May alter unix files
*
* ----------------------------------------------------------------------------
*/
void
CmdShell(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int i, cmdlength;
char *command;
if (cmd->tx_argc != 1) {
cmdlength = 1;
for (i = 1; i < cmd->tx_argc; i++) {
cmdlength = cmdlength + strlen(cmd->tx_argv[i]) + 1;
}
command = mallocMagic((unsigned) (cmdlength));
(void) strcpy(command, cmd->tx_argv[1]);
for (i = 2; i < cmd->tx_argc; i++) {
strcat(command, " ");
strcat(command, cmd->tx_argv[i]);
}
system(command);
freeMagic(command);
}
}
/*
* ----------------------------------------------------------------------------
*
* CmdSgraph
*
* Implement the "sgraph" command.
*
* Usage:
* sgraph [off|add|delete|debug]
* sgraph [show|auto] [vertical|horizontal]
*
* Results:
* None.
*
* Side effects:
* Calls the 'stretchgraph' module, if present.
*
* ----------------------------------------------------------------------------
*/
#ifdef LLNL
int (*CmdStretchCmd)() = NULL;
/* ARGSUSED */
void
CmdSgraph(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if (CmdStretchCmd != NULL)
{
(*CmdStretchCmd)(w, cmd);
}
else
{
TxError("Sorry, the sgraph command doesn't work in this version.\n");
TxError("(Magic was not linked with stretchgraph module.)\n");
}
}
#endif /* LLNL */
#if !defined(NO_SIM_MODULE) && defined(RSIM_MODULE)
/*
* ----------------------------------------------------------------------------
*
* CmdStartRsim
*
* This command starts Rsim under Magic, escapes Rsim, and returns
* back to Magic.
*
* Results:
* Rsim is forked from Magic.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
void
CmdStartRsim(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
static char rsimstr[] = "rsim";
if ((cmd->tx_argc == 1) && (!SimRsimRunning)) {
TxPrintf("usage: startrsim [options] file\n");
return;
}
if ((cmd->tx_argc != 1) && (SimRsimRunning)) {
TxPrintf("Simulator already running. You cannont start another.\n");
return;
}
windCheckOnlyWindow(&w, DBWclientID);
if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
TxError("Put the cursor in a layout window.\n");
return;
}
/* change argv[0] to be "rsim" and send it to Rsim_start */
cmd->tx_argv[0] = rsimstr;
if (cmd->tx_argc != 1) {
cmd->tx_argv[cmd->tx_argc] = (char *) 0;
SimStartRsim(cmd->tx_argv);
}
SimConnectRsim(TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* CmdSimCmd
*
* Applies the given rsim command to the currently selected nodes.
*
* Results:
* Whatever rsim replys to the commands input.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
void
CmdSimCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
static char cmdbuf[200];
char *strptr;
char *nodeCmd;
int i;
if (!SimRsimRunning) {
TxPrintf("You must first start the simulator by using the rsim command.\n");
return;
}
if (cmd->tx_argc == 1) {
TxPrintf("usage: simcmd command [options]\n");
return;
}
windCheckOnlyWindow(&w, DBWclientID);
if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID)) {
TxError("Put the cursor in a layout window.\n");
return;
}
/* check to see whether to apply the command to each node selected,
* or whether to just ship the command to rsim without any node
* names.
*/
nodeCmd = SimGetNodeCommand( cmd->tx_argv[1] );
strcpy( cmdbuf, (nodeCmd != NULL) ? nodeCmd : cmd->tx_argv[1] );
strptr = cmdbuf + strlen(cmdbuf);
*strptr++ = ' ';
*strptr = '\0';
for (i = 2; i <= cmd->tx_argc - 1; i++) {
strcpy(strptr, cmd->tx_argv[i]);
strcat(strptr, " ");
strptr += strlen(strptr) + 1;
}
if (nodeCmd != NULL) {
SimSelection(cmdbuf);
}
else {
SimRsimIt(cmdbuf, "");
while (TRUE) {
if (!SimGetReplyLine(&strptr)) {
break;
}
if (!strptr) {
break;
}
TxPrintf("%s\n", strptr);
}
}
}
#endif
/*
* ----------------------------------------------------------------------------
*
* CmdSnap --
*
* Set the box snapping to align either with the nearest user-defined grid,
* the nearest integer lambda value, or turn snapping off (aligns to internal
* units).
*
* Results:
* None.
*
* Side effects:
* See above.
*
* ----------------------------------------------------------------------------
*/
#define SNAP_OFF 0
#define SNAP_INTERNAL 1
#define SNAP_LAMBDA 2
#define SNAP_GRID 3
#define SNAP_USER 4
#define SNAP_ON 5
#define SNAP_LIST 6
void
CmdSnap(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
static char *names[] = { "off", "internal", "lambda", "grid", "user", "on",
"list", 0 };
int n = SNAP_LIST;
DBWclientRec *crec;
if (cmd->tx_argc < 2) goto printit;
n = Lookup(cmd->tx_argv[1], names);
if (n < 0)
{
TxPrintf("Usage: snap [internal | lambda | user]\n");
return;
}
switch (n)
{
case SNAP_OFF: case SNAP_INTERNAL:
DBWSnapToGrid = DBW_SNAP_INTERNAL;
return;
case SNAP_LAMBDA:
DBWSnapToGrid = DBW_SNAP_LAMBDA;
return;
case SNAP_GRID: case SNAP_USER: case SNAP_ON:
DBWSnapToGrid = DBW_SNAP_USER;
return;
}
printit:
if (n == SNAP_LIST) /* list */
#ifdef MAGIC_WRAPPER
Tcl_SetResult(magicinterp,
(DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" :
((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user"),
TCL_VOLATILE);
#else
TxPrintf("%s\n", (DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" :
((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user"));
#endif
else
TxPrintf("Box is aligned to %s grid\n",
(DBWSnapToGrid == DBW_SNAP_INTERNAL) ? "internal" :
((DBWSnapToGrid == DBW_SNAP_LAMBDA) ? "lambda" : "user"));
}
/*
* ----------------------------------------------------------------------------
*
* CmdSplit --
*
* Split a tile with a diagonal (nonmanhattan geometry)
*
* Results:
* None.
*
* Side effects:
* Modifies the geometry of the edit cell.
*
* ----------------------------------------------------------------------------
*/
void
CmdSplit(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Rect editRect, expRect;
TileTypeBitMask mask1, mask2, *cmask;
TileType t, dir, side, diag;
int pNum, direction;
PaintUndoInfo ui;
windCheckOnlyWindow(&w, DBWclientID);
if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
{
TxError("Put the cursor in a layout window\n");
return;
}
if (cmd->tx_argc != 3 && cmd->tx_argc != 4)
{
TxError("Usage: %s dir layer [layer2]\n", cmd->tx_argv[0]);
return;
}
if (!ToolGetEditBox(&editRect)) return;
if (!CmdParseLayers(cmd->tx_argv[2], &mask1))
return;
/* Remove any inactive layers */
TTMaskAndMask(&mask1, &DBActiveLayerBits);
if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0)
return;
if (GEO_RECTNULL(&editRect)) return; /* Nothing to do */
if (cmd->tx_argc == 4)
{
if (!CmdParseLayers(cmd->tx_argv[3], &mask2))
return;
TTMaskClearType(&mask2, TT_SPACE);
}
else
TTMaskZero(&mask2);
TTMaskClearType(&mask1, TT_SPACE);
direction = (direction >> 1) - 1;
dir = (direction & 0x1) ? 0 : TT_DIRECTION;
for (t = TT_SPACE + 1; t < DBNumTypes; t++)
{
side = (direction & 0x2) ? 0 : TT_SIDE;
for (cmask = &mask1; cmask != NULL; cmask = ((cmask == &mask1) ? &mask2 : NULL))
{
if (cmask == &mask2) side = (side) ? 0 : TT_SIDE;
diag = DBTransformDiagonal(TT_DIAGONAL | dir | side, &RootToEditTransform);
if (TTMaskHasType(cmask, t))
{
EditCellUse->cu_def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
ui.pu_def = EditCellUse->cu_def;
for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
if (DBPaintOnPlane(t, pNum))
{
ui.pu_pNum = pNum;
DBNMPaintPlane(EditCellUse->cu_def->cd_planes[pNum],
diag, &editRect, DBStdPaintTbl(t, pNum), &ui);
GEO_EXPAND(&editRect, 1, &expRect);
DBMergeNMTiles(EditCellUse->cu_def->cd_planes[pNum],
&expRect, &ui);
}
}
}
}
SelectClear();
DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask1);
DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask2);
DBReComputeBbox(EditCellUse->cu_def);
DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &editRect);
}
/*
* ----------------------------------------------------------------------------
*
* CmdSplitErase --
*
* Erase a diagonal section from a tile (nonmanhattan geometry)
*
* Results:
* None.
*
* Side effects:
* Modifies the geometry of the edit cell.
*
* ----------------------------------------------------------------------------
*/
void
CmdSplitErase(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Rect editRect, expRect;
TileTypeBitMask mask;
TileType t, dir, side, diag;
int pNum, direction;
PaintUndoInfo ui;
windCheckOnlyWindow(&w, DBWclientID);
if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
{
TxError("Put the cursor in a layout window\n");
return;
}
if (cmd->tx_argc != 2 && cmd->tx_argc != 3)
{
TxError("Usage: %s dir [layer]\n", cmd->tx_argv[0]);
return;
}
if (!ToolGetEditBox(&editRect)) return;
if (GEO_RECTNULL(&editRect)) return; /* Nothing to do */
if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0)
return;
if (cmd->tx_argc == 2)
(void) CmdParseLayers("*", &mask);
else if (!CmdParseLayers(cmd->tx_argv[2], &mask))
return;
if (TTMaskEqual(&mask, &DBSpaceBits))
(void) CmdParseLayers("*,label", &mask);
TTMaskClearType(&mask, TT_SPACE);
if (TTMaskIsZero(&mask))
return;
/* Remove any inactive layers */
TTMaskAndMask(&mask, &DBActiveLayerBits);
direction = (direction >> 1) - 1;
dir = (direction & 0x1) ? 0 : TT_DIRECTION;
for (t = TT_SPACE + 1; t < DBNumTypes; t++)
{
side = (direction & 0x2) ? 0 : TT_SIDE;
diag = DBTransformDiagonal(TT_DIAGONAL | dir | side, &RootToEditTransform);
if (TTMaskHasType(&mask, t))
{
EditCellUse->cu_def->cd_flags |= CDMODIFIED|CDGETNEWSTAMP;
ui.pu_def = EditCellUse->cu_def;
for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
if (DBPaintOnPlane(t, pNum))
{
ui.pu_pNum = pNum;
DBNMPaintPlane(EditCellUse->cu_def->cd_planes[pNum],
diag, &editRect, DBStdEraseTbl(t, pNum), &ui);
GEO_EXPAND(&editRect, 1, &expRect);
DBMergeNMTiles(EditCellUse->cu_def->cd_planes[pNum],
&expRect, &ui);
}
}
}
SelectClear();
DBWAreaChanged(EditCellUse->cu_def, &editRect, DBW_ALLWINDOWS, &mask);
DBReComputeBbox(EditCellUse->cu_def);
DRCCheckThis (EditCellUse->cu_def, TT_CHECKPAINT, &editRect);
}
/*
* ----------------------------------------------------------------------------
*
* CmdStretch --
*
* Implement the "stretch" command.
*
* Usage:
* stretch [direction [distance]]
*
* Results:
* None.
*
* Side effects:
* Moves everything that's currently selected, erases material that
* the selection would sweep over, and fills in material behind the
* selection.
*
* ----------------------------------------------------------------------------
*/
void
CmdStretch(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Transform t;
Rect rootBox, newBox;
CellDef *rootDef;
int xdelta, ydelta;
if (cmd->tx_argc > 3)
{
TxError("Usage: %s [direction [amount]]\n", cmd->tx_argv[0]);
return;
}
if (cmd->tx_argc > 1)
{
int indx, amountx, amounty;
if (!ToolGetEditBox((Rect *)NULL)) return;
indx = GeoNameToPos(cmd->tx_argv[1], TRUE, TRUE);
if (indx < 0) return;
if (cmd->tx_argc >= 3)
{
switch (indx)
{
case GEO_EAST: case GEO_WEST:
amountx = cmdParseCoord(w, cmd->tx_argv[2], TRUE, TRUE);
amounty = 0;
break;
case GEO_NORTH: case GEO_SOUTH:
amountx = 0;
amounty = cmdParseCoord(w, cmd->tx_argv[2], TRUE, FALSE);
break;
default:
amountx = amounty = 0; /* Should not happen */
break;
}
}
else
{
amountx = cmdParseCoord(w, "1l", TRUE, TRUE);
amounty = cmdParseCoord(w, "1l", TRUE, FALSE);
}
switch (indx)
{
case GEO_NORTH:
xdelta = 0;
ydelta = amounty;
break;
case GEO_SOUTH:
xdelta = 0;
ydelta = -amounty;
break;
case GEO_EAST:
xdelta = amountx;
ydelta = 0;
break;
case GEO_WEST:
xdelta = -amountx;
ydelta = 0;
break;
default:
ASSERT(FALSE, "Bad direction in CmdStretch");
return;
}
GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t);
/* Move the box by the same amount as the selection, if the
* box exists.
*/
if (ToolGetBox(&rootDef, &rootBox) && (rootDef == SelectRootDef))
{
GeoTransRect(&t, &rootBox, &newBox);
DBWSetBox(rootDef, &newBox);
}
}
else
{
/* Use the displacement between the box lower-left corner and
* the point as the transform. Round off to a Manhattan distance.
*/
Point rootPoint;
MagWindow *window;
int absX, absY;
if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
{
TxError("\"Stretch\" uses the box lower-left corner as a place\n");
TxError(" to pick up the selection for stretching, but the\n");
TxError(" box isn't in a window containing the selection.\n");
return;
}
window = ToolGetPoint(&rootPoint, (Rect *) NULL);
if ((window == NULL) ||
(EditRootDef != ((CellUse *) window->w_surfaceID)->cu_def))
{
TxError("\"Stretch\" uses the point as the place to put down a\n");
TxError(" the selection, but the point doesn't point to the\n");
TxError(" edit cell.\n");
return;
}
xdelta = rootPoint.p_x - rootBox.r_xbot;
ydelta = rootPoint.p_y - rootBox.r_ybot;
if (xdelta < 0) absX = -xdelta;
else absX = xdelta;
if (ydelta < 0) absY = -ydelta;
else absY = ydelta;
if (absY <= absX) ydelta = 0;
else xdelta = 0;
GeoTransTranslate(xdelta, ydelta, &GeoIdentityTransform, &t);
GeoTransRect(&t, &rootBox, &newBox);
DBWSetBox(rootDef, &newBox);
/* Recast the command in the usual 3-argument form for logging */
if (ydelta > 0)
sprintf(cmd->tx_argstring, "stretch n %di", ydelta);
else if (ydelta < 0)
sprintf(cmd->tx_argstring, "stretch s %di", -ydelta);
else if (xdelta > 0)
sprintf(cmd->tx_argstring, "stretch e %di", xdelta);
else
sprintf(cmd->tx_argstring, "stretch w %di", -xdelta);
TxRebuildCommand(cmd);
}
SelectStretch(xdelta, ydelta);
}