2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* CmdFI.c --
|
|
|
|
|
*
|
|
|
|
|
* Commands with names beginning with the letters F through I.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * 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. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/commands/CmdFI.c,v 1.4 2010/06/24 12:37:15 tim Exp $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#ifdef USE_READLINE
|
|
|
|
|
#ifdef HAVE_READLINE
|
|
|
|
|
#include <readline/readline.h>
|
|
|
|
|
#include <readline/history.h>
|
|
|
|
|
#else
|
|
|
|
|
#include "readline/readline/readline.h"
|
|
|
|
|
#include "readline/readline/history.h"
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
#include "utils/undo.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "utils/main.h"
|
|
|
|
|
#include "commands/commands.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "utils/macros.h"
|
|
|
|
|
#include "drc/drc.h"
|
|
|
|
|
#include "textio/txcommands.h"
|
|
|
|
|
#include "utils/styles.h"
|
|
|
|
|
#include "graphics/graphics.h"
|
|
|
|
|
#include "extract/extract.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "select/select.h"
|
|
|
|
|
#include "sim/sim.h"
|
|
|
|
|
#include "gcr/gcr.h"
|
|
|
|
|
|
|
|
|
|
/* The following structure is used by CmdFill to keep track of
|
|
|
|
|
* areas to be filled.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
struct cmdFillArea
|
|
|
|
|
{
|
|
|
|
|
Rect cfa_area; /* Area to fill. */
|
|
|
|
|
TileType cfa_type; /* Type of material. */
|
|
|
|
|
struct cmdFillArea *cfa_next; /* Next in list of areas to fill. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* The following structure passes arguments needed by the */
|
|
|
|
|
/* DBWFeedbackAdd command to the feedPolyFunc() callback routine. */
|
|
|
|
|
|
|
|
|
|
struct cmdFPArg
|
|
|
|
|
{
|
|
|
|
|
CellDef *def;
|
|
|
|
|
int style;
|
|
|
|
|
char *text;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define FEEDMAGNIFY 20 /* Allow feedback to be drawn to 1/20 of an */
|
|
|
|
|
/* internal unit. */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* feedPolyFunc --
|
|
|
|
|
*
|
|
|
|
|
* This procedure generates feedback entries in a polygonal area
|
|
|
|
|
* by calling DBWFeedbackArea per tile on simple 1-bit types in
|
|
|
|
|
* a plane. The use of the temporary plane allows us to use the
|
|
|
|
|
* same path decomposition routine used for CIF input, polygon
|
|
|
|
|
* drawing, and wire segments.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
feedPolyFunc(tile, arg)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
struct cmdFPArg *arg;
|
|
|
|
|
{
|
|
|
|
|
Rect area;
|
|
|
|
|
TiToRect(tile, &area);
|
|
|
|
|
|
|
|
|
|
DBWFeedbackAdd(&area, arg->text, arg->def, FEEDMAGNIFY,
|
|
|
|
|
arg->style |
|
|
|
|
|
(TiGetTypeExact(tile) & (TT_DIAGONAL | TT_DIRECTION | TT_SIDE)));
|
|
|
|
|
/* (preserve information about the geometry of a diagonal tile) */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdFeedback --
|
|
|
|
|
*
|
|
|
|
|
* Implement the "feedback" command, which provides facilities
|
|
|
|
|
* for querying and manipulating feedback information provided
|
|
|
|
|
* by other commands when they have troubles or want to highlight
|
|
|
|
|
* certain things.
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* feedback option [additional_args]
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Depends on the option.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#undef CLEAR
|
|
|
|
|
#define ADD 0
|
|
|
|
|
#define CLEAR 1
|
|
|
|
|
#define COUNT 2
|
|
|
|
|
#define FIND 3
|
|
|
|
|
#define FEED_HELP 4
|
|
|
|
|
#define SAVE 5
|
|
|
|
|
#define WHY 6
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdFeedback(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
static char *cmdFeedbackOptions[] =
|
|
|
|
|
{
|
|
|
|
|
"add text [style] [points...] create new feedback area over box",
|
|
|
|
|
"clear [substring] clear all or selected feedback info",
|
|
|
|
|
"count count # feedback entries",
|
|
|
|
|
"find [nth] put box over next [or nth] entry",
|
|
|
|
|
"help print this message",
|
|
|
|
|
"save file save feedback areas in file",
|
|
|
|
|
"why print all feedback messages under box",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
static char *cmdFeedbackStyleNames[] =
|
|
|
|
|
{
|
|
|
|
|
"dotted", "medium", "outline", "pale", "solid", NULL
|
|
|
|
|
};
|
|
|
|
|
static int cmdFeedbackStyles[] =
|
|
|
|
|
{
|
|
|
|
|
STYLE_DOTTEDHIGHLIGHTS, STYLE_MEDIUMHIGHLIGHTS,
|
|
|
|
|
STYLE_OUTLINEHIGHLIGHTS, STYLE_PALEHIGHLIGHTS,
|
|
|
|
|
STYLE_SOLIDHIGHLIGHTS, -1
|
|
|
|
|
};
|
|
|
|
|
static int nth = 0; /* Last entry displayed in
|
|
|
|
|
* "feedback find".
|
|
|
|
|
*/
|
|
|
|
|
int option, i, style, pstart;
|
|
|
|
|
Rect box, r;
|
|
|
|
|
char *text, **msg;
|
|
|
|
|
CellDef *rootDef;
|
|
|
|
|
HashTable table;
|
|
|
|
|
HashEntry *h;
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc < 2)
|
|
|
|
|
{
|
|
|
|
|
badusage:
|
|
|
|
|
TxPrintf("Wrong number of arguments for \"feedback\" command.\n");
|
|
|
|
|
TxPrintf("Type \":feedback help\" for help.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
option = Lookup(cmd->tx_argv[1], cmdFeedbackOptions);
|
|
|
|
|
if (option < 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("%s isn't a valid feedback option. Try one of:\n",
|
|
|
|
|
cmd->tx_argv[1]);
|
|
|
|
|
TxError(" add find\n");
|
|
|
|
|
TxError(" clear help\n");
|
|
|
|
|
TxError(" count save\n");
|
|
|
|
|
TxError(" save\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pstart = 4; /* argument # where point list starts, if any */
|
|
|
|
|
switch (option)
|
|
|
|
|
{
|
|
|
|
|
case ADD:
|
|
|
|
|
style = STYLE_PALEHIGHLIGHTS;
|
|
|
|
|
if (cmd->tx_argc > 3)
|
|
|
|
|
{
|
|
|
|
|
i = Lookup(cmd->tx_argv[3], cmdFeedbackStyleNames);
|
|
|
|
|
if (i < 0)
|
|
|
|
|
{
|
|
|
|
|
if (StrIsNumeric(cmd->tx_argv[3]))
|
|
|
|
|
pstart = 3;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
style = GrGetStyleFromName(cmd->tx_argv[3]);
|
|
|
|
|
if (style < 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("%s isn't a valid display style. Try one of:\n",
|
|
|
|
|
cmd->tx_argv[3]);
|
|
|
|
|
TxError(" dotted, pale, medium, solid, outline,\n");
|
|
|
|
|
TxError(" or a long name from the .dstyle file\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (style < 1 || style >= TECHBEGINSTYLES)
|
|
|
|
|
{
|
|
|
|
|
TxError("Numbered styles must be between 1 and %d\n",
|
|
|
|
|
TECHBEGINSTYLES - 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else style = cmdFeedbackStyles[i];
|
|
|
|
|
}
|
|
|
|
|
if (cmd->tx_argc - pstart > 0)
|
|
|
|
|
{
|
|
|
|
|
/* points must be in X Y pairs */
|
|
|
|
|
if ((cmd->tx_argc - pstart) & 1) goto badusage;
|
|
|
|
|
|
|
|
|
|
if (w == NULL) return;
|
|
|
|
|
rootDef = ((CellUse *) w->w_surfaceID)->cu_def;
|
|
|
|
|
|
|
|
|
|
/* Read coordinates in FEEDMAGNIFY x internal units, */
|
|
|
|
|
/* then scale the highlight accordingly. */
|
|
|
|
|
|
|
|
|
|
if ((cmd->tx_argc - pstart) == 2)
|
|
|
|
|
{
|
|
|
|
|
/* Single point highlight. Style MUST be outlined */
|
|
|
|
|
/* or else the point won't be visible. */
|
|
|
|
|
|
|
|
|
|
if (GrStyleTable[style].outline == 0)
|
|
|
|
|
style = STYLE_OUTLINEHIGHLIGHTS;
|
|
|
|
|
|
|
|
|
|
box.r_xbot = box.r_xtop = cmdScaleCoord(w, cmd->tx_argv[pstart++],
|
|
|
|
|
FALSE, TRUE, FEEDMAGNIFY);
|
|
|
|
|
box.r_ybot = box.r_ytop = cmdScaleCoord(w, cmd->tx_argv[pstart++],
|
|
|
|
|
FALSE, FALSE, FEEDMAGNIFY);
|
|
|
|
|
|
|
|
|
|
DBWFeedbackAdd(&box, cmd->tx_argv[2], rootDef, FEEDMAGNIFY, style);
|
|
|
|
|
}
|
|
|
|
|
else if ((cmd->tx_argc - pstart) == 4)
|
|
|
|
|
{
|
|
|
|
|
/* Single line highlight. Style MUST be outlined, */
|
|
|
|
|
/* or else the line won't be visible. */
|
|
|
|
|
|
|
|
|
|
if (GrStyleTable[style].outline == 0)
|
|
|
|
|
style = STYLE_OUTLINEHIGHLIGHTS;
|
|
|
|
|
r.r_xbot = cmdScaleCoord(w, cmd->tx_argv[pstart++],
|
|
|
|
|
FALSE, TRUE, FEEDMAGNIFY);
|
|
|
|
|
r.r_ybot = cmdScaleCoord(w, cmd->tx_argv[pstart++],
|
|
|
|
|
FALSE, FALSE, FEEDMAGNIFY);
|
|
|
|
|
r.r_xtop = cmdScaleCoord(w, cmd->tx_argv[pstart++],
|
|
|
|
|
FALSE, TRUE, FEEDMAGNIFY);
|
|
|
|
|
r.r_ytop = cmdScaleCoord(w, cmd->tx_argv[pstart++],
|
|
|
|
|
FALSE, FALSE, FEEDMAGNIFY);
|
|
|
|
|
if (r.r_xbot != r.r_xtop && r.r_ybot != r.r_ytop)
|
|
|
|
|
{
|
|
|
|
|
/* Line at an angle. The hack of setting TT_SIDE */
|
|
|
|
|
/* without setting TT_DIAGONAL allows the drawing */
|
|
|
|
|
/* routine to treat this case as a line, not a */
|
|
|
|
|
/* triangle. */
|
|
|
|
|
|
|
|
|
|
style |= TT_SIDE;
|
|
|
|
|
if ((r.r_xbot > r.r_xtop && r.r_ybot < r.r_ytop) ||
|
|
|
|
|
(r.r_xbot < r.r_xtop && r.r_ybot > r.r_ytop))
|
|
|
|
|
style |= TT_DIRECTION;
|
|
|
|
|
}
|
|
|
|
|
GeoCanonicalRect(&r, &box);
|
|
|
|
|
DBWFeedbackAdd(&box, cmd->tx_argv[2], rootDef, FEEDMAGNIFY, style);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Plane *plane;
|
|
|
|
|
PaintResultType ptable[2] = {1, 1}; /* simple 1 bit table */
|
|
|
|
|
TileTypeBitMask feedSimpleMask;
|
|
|
|
|
Point *plist;
|
|
|
|
|
int points, j;
|
|
|
|
|
struct cmdFPArg fpargs;
|
|
|
|
|
|
|
|
|
|
points = (cmd->tx_argc - pstart) >> 1;
|
|
|
|
|
plist = (Point *)mallocMagic(points * sizeof(Point));
|
|
|
|
|
fpargs.def = rootDef;
|
|
|
|
|
fpargs.style = style;
|
|
|
|
|
fpargs.text = cmd->tx_argv[2];
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
for (i = 0, j = pstart; i < points; i++)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
plist[i].p_x = cmdScaleCoord(w, cmd->tx_argv[j++],
|
|
|
|
|
FALSE, TRUE, FEEDMAGNIFY);
|
|
|
|
|
plist[i].p_y = cmdScaleCoord(w, cmd->tx_argv[j++],
|
|
|
|
|
FALSE, FALSE, FEEDMAGNIFY);
|
|
|
|
|
}
|
|
|
|
|
plane = DBNewPlane((ClientData)0);
|
|
|
|
|
TTMaskZero(&feedSimpleMask);
|
|
|
|
|
TTMaskSetType(&feedSimpleMask, 1);
|
|
|
|
|
PaintPolygon(plist, points, plane, ptable,
|
|
|
|
|
(PaintUndoInfo *)NULL, FALSE);
|
|
|
|
|
i = DBWFeedbackCount;
|
|
|
|
|
DBSrPaintArea((Tile *)NULL, plane, &TiPlaneRect, &feedSimpleMask,
|
|
|
|
|
feedPolyFunc, (ClientData)&fpargs);
|
|
|
|
|
TiFreePlane(plane);
|
|
|
|
|
|
|
|
|
|
if (i == DBWFeedbackCount)
|
|
|
|
|
{
|
|
|
|
|
/* Pathological condition---feedback area is so thin */
|
|
|
|
|
/* that the decomposition to rectangles and triangles */
|
|
|
|
|
/* was degenerate. If so, then we can just treat the */
|
|
|
|
|
/* plist as several lines and/or points. */
|
|
|
|
|
|
|
|
|
|
if (GrStyleTable[style].outline == 0)
|
|
|
|
|
style = STYLE_OUTLINEHIGHLIGHTS;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < points - 1; i++)
|
|
|
|
|
{
|
|
|
|
|
r.r_xbot = plist[i].p_x;
|
|
|
|
|
r.r_ybot = plist[i].p_y;
|
|
|
|
|
r.r_xtop = plist[i + 1].p_x;
|
|
|
|
|
r.r_ytop = plist[i + 1].p_y;
|
|
|
|
|
if (r.r_xbot != r.r_xtop && r.r_ybot != r.r_ytop)
|
|
|
|
|
{
|
|
|
|
|
style |= TT_SIDE;
|
|
|
|
|
if ((r.r_xbot > r.r_xtop && r.r_ybot < r.r_ytop) ||
|
|
|
|
|
(r.r_xbot < r.r_xtop && r.r_ybot > r.r_ytop))
|
|
|
|
|
style |= TT_DIRECTION;
|
|
|
|
|
}
|
|
|
|
|
GeoCanonicalRect(&r, &box);
|
|
|
|
|
DBWFeedbackAdd(&box, cmd->tx_argv[2], rootDef,
|
|
|
|
|
FEEDMAGNIFY, style);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
freeMagic(plist);
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
w = ToolGetBoxWindow(&box, (int *) NULL);
|
|
|
|
|
if (w == NULL) return;
|
|
|
|
|
rootDef = ((CellUse *) w->w_surfaceID)->cu_def;
|
|
|
|
|
DBWFeedbackAdd(&box, cmd->tx_argv[2], rootDef, 1, style);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case CLEAR:
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
DBWFeedbackClear(cmd->tx_argv[2]);
|
|
|
|
|
else if (cmd->tx_argc == 2)
|
|
|
|
|
DBWFeedbackClear(NULL);
|
|
|
|
|
else
|
|
|
|
|
goto badusage;
|
|
|
|
|
nth = 0;
|
|
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case COUNT:
|
|
|
|
|
if (cmd->tx_argc != 2) goto badusage;
|
2020-05-23 23:26:32 +02:00
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(DBWFeedbackCount));
|
|
|
|
|
#else
|
2017-04-25 14:41:48 +02:00
|
|
|
TxPrintf("There are %d feedback areas.\n", DBWFeedbackCount);
|
2020-05-23 23:26:32 +02:00
|
|
|
#endif
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case FIND:
|
|
|
|
|
if (cmd->tx_argc > 3) goto badusage;
|
|
|
|
|
if (DBWFeedbackCount == 0)
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("There are no feedback areas right now.\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
{
|
|
|
|
|
nth = atoi(cmd->tx_argv[2]);
|
|
|
|
|
if ((nth > DBWFeedbackCount) || (nth <= 0))
|
|
|
|
|
{
|
|
|
|
|
TxError("Sorry, but only feedback areas 1-%d exist.\n",
|
|
|
|
|
DBWFeedbackCount);
|
|
|
|
|
nth = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
nth += 1;
|
|
|
|
|
if (nth > DBWFeedbackCount) nth = 1;
|
|
|
|
|
}
|
|
|
|
|
text = DBWFeedbackNth(nth-1, &box, &rootDef, (int *) NULL);
|
|
|
|
|
ToolMoveBox(TOOL_BL, &box.r_ll, FALSE, rootDef);
|
|
|
|
|
ToolMoveCorner(TOOL_TR, &box.r_ur, FALSE, rootDef);
|
|
|
|
|
TxPrintf("Feedback #%d: %s\n", nth, text);
|
|
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case FEED_HELP:
|
|
|
|
|
if (cmd->tx_argc > 2) goto badusage;
|
|
|
|
|
TxPrintf("Feedback commands have the form \"feedback option\",\n");
|
|
|
|
|
TxPrintf("where option is one of:\n");
|
|
|
|
|
for (msg = cmdFeedbackOptions; *msg != NULL; msg++)
|
|
|
|
|
TxPrintf("%s\n", *msg);
|
|
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case SAVE:
|
|
|
|
|
if (cmd->tx_argc != 3) goto badusage;
|
|
|
|
|
f = PaOpen(cmd->tx_argv[2], "w", (char *) NULL, ".",
|
|
|
|
|
(char *) NULL, (char **) NULL);
|
|
|
|
|
if (f == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Can't open file %s.\n", cmd->tx_argv[2]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < DBWFeedbackCount; i++)
|
|
|
|
|
{
|
|
|
|
|
int j, style;
|
|
|
|
|
text = DBWFeedbackNth(i, &box, (CellDef **) NULL, &style);
|
|
|
|
|
fprintf(f, "box %d %d %d %d\n", box.r_xbot, box.r_ybot,
|
|
|
|
|
box.r_xtop, box.r_ytop);
|
|
|
|
|
fprintf(f, "feedback add \"");
|
|
|
|
|
|
|
|
|
|
/* Be careful to backslash any quotes in the text! */
|
|
|
|
|
|
|
|
|
|
for ( ; *text != 0; text += 1)
|
|
|
|
|
{
|
|
|
|
|
if (*text == '"') fputc('\\', f);
|
|
|
|
|
fputc(*text, f);
|
|
|
|
|
}
|
|
|
|
|
fputc('"', f);
|
|
|
|
|
for (j = 0; cmdFeedbackStyles[j] >= 0; j++)
|
|
|
|
|
{
|
|
|
|
|
if (cmdFeedbackStyles[j] == style)
|
|
|
|
|
{
|
|
|
|
|
fprintf(f, " %s", cmdFeedbackStyleNames[j]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fprintf(f, "\n");
|
|
|
|
|
}
|
|
|
|
|
(void) fclose(f);
|
|
|
|
|
break;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case WHY:
|
|
|
|
|
if (cmd->tx_argc > 2) goto badusage;
|
|
|
|
|
w = ToolGetBoxWindow(&box, (int *) NULL);
|
|
|
|
|
if (w == NULL) return;
|
|
|
|
|
rootDef = ((CellUse *) w->w_surfaceID)->cu_def;
|
|
|
|
|
HashInit(&table, 16, 0);
|
|
|
|
|
for (i = 0; i < DBWFeedbackCount; i++)
|
|
|
|
|
{
|
|
|
|
|
Rect area;
|
|
|
|
|
CellDef *fbRootDef;
|
|
|
|
|
|
|
|
|
|
text = DBWFeedbackNth(i, &area, &fbRootDef, (int *) NULL);
|
|
|
|
|
if (rootDef != fbRootDef) continue;
|
|
|
|
|
if (!GEO_OVERLAP(&box, &area)) continue;
|
|
|
|
|
h = HashFind(&table, text);
|
|
|
|
|
if (HashGetValue(h) == 0) TxPrintf("%s\n", text);
|
|
|
|
|
HashSetValue(h, 1);
|
|
|
|
|
}
|
|
|
|
|
HashKill(&table);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdFill --
|
|
|
|
|
*
|
|
|
|
|
* Implement the "fill" command. Find all paint touching one side
|
|
|
|
|
* of the box, and paint it across to the other side of the box. Can
|
|
|
|
|
* operate in any of four directions.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Usage:
|
2017-04-25 14:41:48 +02:00
|
|
|
* fill direction [layers]
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Modifies the edit cell definition.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Data passed between CmdFill and cmdFillFunc: */
|
|
|
|
|
|
|
|
|
|
int cmdFillDir; /* Direction in which to fill. */
|
|
|
|
|
Rect cmdFillRootBox; /* Root coords of box. */
|
|
|
|
|
struct cmdFillArea *cmdFillList; /* List of areas to fill. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdFill(w, cmd)
|
|
|
|
|
MagWindow *w; /* Window in which command was invoked. */
|
|
|
|
|
TxCommand *cmd; /* Describes the command that was invoked. */
|
|
|
|
|
{
|
|
|
|
|
TileTypeBitMask maskBits;
|
|
|
|
|
Rect editBox;
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
extern int cmdFillFunc();
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc < 2 || cmd->tx_argc > 3)
|
|
|
|
|
{
|
|
|
|
|
TxError("Usage: %s direction [layers]\n", cmd->tx_argv[0]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
|
|
|
if ( w == (MagWindow *) NULL )
|
|
|
|
|
{
|
|
|
|
|
TxError("Point to a window\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find and check validity of position argument. */
|
|
|
|
|
|
|
|
|
|
cmdFillDir = GeoNameToPos(cmd->tx_argv[1], TRUE, TRUE);
|
|
|
|
|
if (cmdFillDir < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Figure out which layers to fill. */
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc < 3)
|
|
|
|
|
maskBits = DBAllButSpaceAndDRCBits;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!CmdParseLayers(cmd->tx_argv[2], &maskBits))
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Figure out which material to search for and invoke a search
|
|
|
|
|
* procedure to find it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!ToolGetEditBox(&editBox)) return;
|
|
|
|
|
GeoTransRect(&EditToRootTransform, &editBox, &cmdFillRootBox);
|
|
|
|
|
scx.scx_area = cmdFillRootBox;
|
|
|
|
|
switch (cmdFillDir)
|
|
|
|
|
{
|
|
|
|
|
case GEO_NORTH:
|
|
|
|
|
scx.scx_area.r_ytop = scx.scx_area.r_ybot + 1;
|
|
|
|
|
scx.scx_area.r_ybot -= 1;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_SOUTH:
|
|
|
|
|
scx.scx_area.r_ybot = scx.scx_area.r_ytop - 1;
|
|
|
|
|
scx.scx_area.r_ytop += 1;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_EAST:
|
|
|
|
|
scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
|
|
|
|
|
scx.scx_area.r_xbot -= 1;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_WEST:
|
|
|
|
|
scx.scx_area.r_xbot = scx.scx_area.r_xtop - 1;
|
|
|
|
|
scx.scx_area.r_xtop += 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
scx.scx_use = (CellUse *) w->w_surfaceID;
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
cmdFillList = (struct cmdFillArea *) NULL;
|
|
|
|
|
|
|
|
|
|
(void) DBTreeSrTiles(&scx, &maskBits,
|
|
|
|
|
((DBWclientRec *) w->w_clientData)->dbw_bitmask,
|
|
|
|
|
cmdFillFunc, (ClientData) NULL);
|
|
|
|
|
|
|
|
|
|
/* Now that we've got all the material, scan over the list
|
|
|
|
|
* painting the material and freeing up the entries on the list.
|
|
|
|
|
*/
|
|
|
|
|
while (cmdFillList != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBPaint(EditCellUse->cu_def, &cmdFillList->cfa_area,
|
|
|
|
|
cmdFillList->cfa_type);
|
|
|
|
|
freeMagic((char *) cmdFillList);
|
|
|
|
|
cmdFillList = cmdFillList->cfa_next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SelectClear();
|
|
|
|
|
DBAdjustLabels(EditCellUse->cu_def, &editBox);
|
|
|
|
|
DRCCheckThis(EditCellUse->cu_def, TT_CHECKPAINT, &editBox);
|
|
|
|
|
DBWAreaChanged(EditCellUse->cu_def, &editBox, DBW_ALLWINDOWS, &maskBits);
|
|
|
|
|
DBReComputeBbox(EditCellUse->cu_def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Important note: these procedures can't paint the tiles directly,
|
|
|
|
|
* because a search is in progress over the same planes and if we
|
|
|
|
|
* paint here it may mess up the search. Instead, the procedures
|
|
|
|
|
* save areas on a list. The list is post-processed to paint the
|
|
|
|
|
* areas once the search is finished.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
cmdFillFunc(tile, cxp)
|
|
|
|
|
Tile *tile; /* Tile to fill with. */
|
|
|
|
|
TreeContext *cxp; /* Describes state of search. */
|
|
|
|
|
{
|
|
|
|
|
Rect r1, r2;
|
|
|
|
|
struct cmdFillArea *cfa;
|
|
|
|
|
|
|
|
|
|
TiToRect(tile, &r1);
|
|
|
|
|
GeoTransRect(&cxp->tc_scx->scx_trans, &r1, &r2);
|
|
|
|
|
GeoClip(&r2, &cmdFillRootBox);
|
|
|
|
|
switch (cmdFillDir)
|
|
|
|
|
{
|
|
|
|
|
case GEO_NORTH:
|
|
|
|
|
r2.r_ytop = cmdFillRootBox.r_ytop;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_SOUTH:
|
|
|
|
|
r2.r_ybot = cmdFillRootBox.r_ybot;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_EAST:
|
|
|
|
|
r2.r_xtop = cmdFillRootBox.r_xtop;
|
|
|
|
|
break;
|
|
|
|
|
case GEO_WEST:
|
|
|
|
|
r2.r_xbot = cmdFillRootBox.r_xbot;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
GeoTransRect(&RootToEditTransform, &r2, &r1);
|
|
|
|
|
cfa = (struct cmdFillArea *) mallocMagic(sizeof(struct cmdFillArea));
|
|
|
|
|
cfa->cfa_area = r1;
|
|
|
|
|
cfa->cfa_type = TiGetType(tile);
|
|
|
|
|
cfa->cfa_next = cmdFillList;
|
|
|
|
|
cmdFillList = cfa;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdFindBox --
|
|
|
|
|
*
|
|
|
|
|
* Center the display on a corner of the box. If 'zoom', then make the box
|
|
|
|
|
* fill the window.
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* findbox [zoom]
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The window underneath the cursor is moved.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdFindBox(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
CellDef *boxDef;
|
|
|
|
|
Rect box;
|
|
|
|
|
|
|
|
|
|
if (w == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Point to a window first.\n");
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!ToolGetBox(&boxDef, &box))
|
|
|
|
|
{
|
|
|
|
|
TxError("Put the box in a window first.\n");
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (boxDef != (((CellUse *) w->w_surfaceID)->cu_def))
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
TxError("The box is not in the same coordinate %s",
|
2017-04-25 14:41:48 +02:00
|
|
|
"system as the window.\n");
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (cmd->tx_argc == 1)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/* center view on box */
|
|
|
|
|
Point rootPoint;
|
|
|
|
|
Rect newArea, oldArea;
|
|
|
|
|
|
|
|
|
|
rootPoint.p_x = (box.r_xbot + box.r_xtop)/2;
|
|
|
|
|
rootPoint.p_y = (box.r_ybot + box.r_ytop)/2;
|
|
|
|
|
|
|
|
|
|
oldArea = w->w_surfaceArea;
|
|
|
|
|
newArea.r_xbot = rootPoint.p_x - (oldArea.r_xtop - oldArea.r_xbot)/2;
|
|
|
|
|
newArea.r_xtop = newArea.r_xbot - oldArea.r_xbot + oldArea.r_xtop;
|
|
|
|
|
newArea.r_ybot = rootPoint.p_y - (oldArea.r_ytop - oldArea.r_ybot)/2;
|
|
|
|
|
newArea.r_ytop = newArea.r_ybot - oldArea.r_ybot + oldArea.r_ytop;
|
|
|
|
|
|
|
|
|
|
WindMove(w, &newArea);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
int expand;
|
|
|
|
|
|
|
|
|
|
/* zoom in to box */
|
|
|
|
|
|
|
|
|
|
if (strcmp(cmd->tx_argv[1], "zoom") != 0) goto usage;
|
|
|
|
|
|
|
|
|
|
/* Allow a 5% ring around the box on each side. */
|
|
|
|
|
|
|
|
|
|
expand = (box.r_xtop - box.r_xbot)/20;
|
|
|
|
|
if (expand < 2) expand = 2;
|
|
|
|
|
box.r_xtop += expand;
|
|
|
|
|
box.r_xbot -= expand;
|
|
|
|
|
expand = (box.r_ytop - box.r_ybot)/20;
|
|
|
|
|
if (expand < 2) expand = 2;
|
|
|
|
|
box.r_ytop += expand;
|
|
|
|
|
box.r_ybot -= expand;
|
|
|
|
|
|
|
|
|
|
WindMove(w, &box);
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
usage:
|
|
|
|
|
TxError("Usage: findbox [zoom]\n");
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2020-11-20 16:10:32 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* cmdFindLabelFunc --
|
|
|
|
|
*
|
|
|
|
|
* Callback function from CmdFindLabel. Return 1 to stop the search on the
|
|
|
|
|
* Nth instance of the label named "label", where N is passed through the
|
|
|
|
|
* client data as lsr_occur.
|
|
|
|
|
*
|
|
|
|
|
* The client data record lsr_rect is left pointing to the label location
|
|
|
|
|
* when the Nth label instance is found.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef struct _labsearchrec
|
|
|
|
|
{
|
|
|
|
|
Rect lsr_rect;
|
|
|
|
|
int lsr_occur;
|
|
|
|
|
} LabSearchRec;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int cmdFindLabelFunc(rect, name, label, cdarg)
|
|
|
|
|
Rect *rect;
|
|
|
|
|
char *name;
|
|
|
|
|
Label *label;
|
|
|
|
|
LabSearchRec *cdarg;
|
|
|
|
|
{
|
|
|
|
|
if (cdarg->lsr_occur == 0)
|
|
|
|
|
{
|
|
|
|
|
cdarg->lsr_rect = *rect;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
cdarg->lsr_occur--;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdFindLabel --
|
|
|
|
|
*
|
|
|
|
|
* Find a label and set the box to it.
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* findlabel [-glob] name
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None. In TCL, the -glob option will generate a list of matching
|
|
|
|
|
* nodes returned in the interpreter result.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The box may be moved. If "-glob" is specified, in the non-Tcl
|
|
|
|
|
* version, matching label names are printed.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdFindLabel(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
CellDef *boxDef;
|
|
|
|
|
CellUse *labUse;
|
2020-11-20 16:10:32 +01:00
|
|
|
Rect box;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *labname;
|
2020-11-20 16:10:32 +01:00
|
|
|
int found, occur, plainargs;
|
2017-04-25 14:41:48 +02:00
|
|
|
bool doglob = FALSE; /* csh-style glob matching (see utils/match.c) */
|
2020-11-20 16:10:32 +01:00
|
|
|
LabSearchRec lsr;
|
2017-04-25 14:41:48 +02:00
|
|
|
int dbListLabels(); /* forward declaration */
|
|
|
|
|
|
2020-11-20 16:10:32 +01:00
|
|
|
plainargs = cmd->tx_argc;
|
|
|
|
|
if ((plainargs > 2) && !strncmp(cmd->tx_argv[1], "-glob", 5))
|
|
|
|
|
{
|
|
|
|
|
plainargs--;
|
2017-04-25 14:41:48 +02:00
|
|
|
doglob = TRUE;
|
2020-11-20 16:10:32 +01:00
|
|
|
}
|
|
|
|
|
if ((plainargs != 2) && (plainargs != 3))
|
2017-04-25 14:41:48 +02:00
|
|
|
goto usage;
|
|
|
|
|
|
2020-11-20 16:10:32 +01:00
|
|
|
occur = 0;
|
|
|
|
|
if (plainargs == 3)
|
|
|
|
|
{
|
|
|
|
|
char *occurstr = cmd->tx_argv[plainargs - 1];
|
|
|
|
|
if (StrIsInt(occurstr))
|
|
|
|
|
occur = atoi(occurstr);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (w == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Point to a window first.\n");
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!ToolGetBox(&boxDef, &box))
|
|
|
|
|
{
|
|
|
|
|
TxError("Put the box in a window first.\n");
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (boxDef != (((CellUse *) w->w_surfaceID)->cu_def))
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
TxError("The box is not in the same coordinate %s",
|
2017-04-25 14:41:48 +02:00
|
|
|
"system as the window.\n");
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
2020-11-20 16:10:32 +01:00
|
|
|
labname = cmd->tx_argv[1 + (doglob) ? 1 : 0];
|
2017-04-25 14:41:48 +02:00
|
|
|
labUse = EditCellUse;
|
|
|
|
|
if (labUse == NULL) labUse = (CellUse *)w->w_surfaceID;
|
|
|
|
|
|
|
|
|
|
if (doglob)
|
|
|
|
|
{
|
|
|
|
|
/* Pattern-matching label search */
|
|
|
|
|
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
|
|
|
|
|
scx.scx_use = labUse;
|
|
|
|
|
scx.scx_area = labUse->cu_def->cd_bbox;
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
|
|
|
|
|
DBSearchLabel(&scx, &DBAllButSpaceAndDRCBits, 0, labname,
|
|
|
|
|
dbListLabels, (ClientData) 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Exact-match label search (corrected by Nishit, 10/14/04) */
|
|
|
|
|
|
2020-11-20 16:10:32 +01:00
|
|
|
lsr.lsr_occur = occur;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
found = DBSrLabelLoc(labUse, labname, cmdFindLabelFunc,
|
2020-11-20 16:10:32 +01:00
|
|
|
(ClientData) &lsr);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (found) {
|
2020-11-20 16:10:32 +01:00
|
|
|
if (lsr.lsr_rect.r_xbot == lsr.lsr_rect.r_xtop)
|
|
|
|
|
lsr.lsr_rect.r_xtop++;
|
|
|
|
|
if (lsr.lsr_rect.r_ybot == lsr.lsr_rect.r_ytop)
|
|
|
|
|
lsr.lsr_rect.r_ytop++;
|
|
|
|
|
ToolMoveBox(TOOL_BL, &lsr.lsr_rect.r_ll, FALSE, labUse->cu_def);
|
|
|
|
|
ToolMoveCorner(TOOL_TR, &lsr.lsr_rect.r_ur, FALSE, labUse->cu_def);
|
2017-04-25 14:41:48 +02:00
|
|
|
} else {
|
|
|
|
|
TxError("Couldn't find label %s\n", labname);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
usage:
|
|
|
|
|
TxError("Usage: findlabel [-glob] label_name\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Callback routine for listing pattern-matched labels.
|
|
|
|
|
* Always return zero to keep the search going.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
dbListLabels(scx, label, tpath, cdarg)
|
|
|
|
|
SearchContext *scx;
|
|
|
|
|
Label *label; /* Pointer to label structure */
|
|
|
|
|
TerminalPath *tpath; /* Full pathname of terminal */
|
|
|
|
|
ClientData cdarg; /* (unused) */
|
|
|
|
|
{
|
|
|
|
|
char *n = tpath->tp_next;
|
|
|
|
|
char c = *n;
|
|
|
|
|
strcpy(n, label->lab_text);
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_AppendElement(magicinterp, tpath->tp_first);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("%s\n", tpath->tp_first);
|
|
|
|
|
#endif
|
|
|
|
|
*n = c;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdFlush --
|
|
|
|
|
*
|
|
|
|
|
* Implement the "flush" command.
|
|
|
|
|
* Throw away all changes made within magic to the specified cell,
|
|
|
|
|
* and re-read it from disk. If no cell is specified, the default
|
2020-12-10 18:13:48 +01:00
|
|
|
* is the current edit cell. If "-dereference" is specified as an
|
|
|
|
|
* option, then re-read the cell from the search path instead of
|
|
|
|
|
* the file path that has been associated with the cell.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Usage:
|
2020-12-10 18:13:48 +01:00
|
|
|
* flush [cellname] [-dereference]
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* THIS IS NOT UNDO-ABLE!
|
|
|
|
|
* Modifies the specified CellDef.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdFlush(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
CellDef *def;
|
|
|
|
|
int action;
|
|
|
|
|
static char *actionNames[] = { "no", "yes", 0 };
|
|
|
|
|
char *prompt;
|
2020-12-10 18:13:48 +01:00
|
|
|
bool dereference = FALSE;
|
|
|
|
|
|
|
|
|
|
if (!strncmp(cmd->tx_argv[cmd->tx_argc - 1], "-deref", 6))
|
|
|
|
|
{
|
|
|
|
|
dereference = TRUE;
|
|
|
|
|
cmd->tx_argc--;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (cmd->tx_argc > 2)
|
|
|
|
|
{
|
2020-12-10 18:13:48 +01:00
|
|
|
TxError("Usage: flush [cellname] [dereference]\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc == 1)
|
|
|
|
|
{
|
|
|
|
|
if (EditCellUse != NULL)
|
|
|
|
|
def = EditCellUse->cu_def;
|
|
|
|
|
else
|
|
|
|
|
def = ((CellUse *)w->w_surfaceID)->cu_def;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
def = DBCellLookDef(cmd->tx_argv[1]);
|
|
|
|
|
if (def == (CellDef *) NULL)
|
|
|
|
|
{
|
|
|
|
|
/* an error message has already been printed by the database */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (def->cd_flags & (CDMODIFIED|CDSTAMPSCHANGED|CDBOXESCHANGED))
|
|
|
|
|
{
|
|
|
|
|
prompt = TxPrintString("Really throw away all changes made"
|
|
|
|
|
" to cell %s? ", def->cd_name);
|
|
|
|
|
action = TxDialog(prompt, actionNames, 0);
|
|
|
|
|
if (action == 0) /* No */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-10 18:13:48 +01:00
|
|
|
cmdFlushCell(def, dereference);
|
2017-04-25 14:41:48 +02:00
|
|
|
SelectClear();
|
|
|
|
|
TxPrintf("[Flushed]\n");
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdGetcell --
|
|
|
|
|
*
|
|
|
|
|
* Implement the ":getcell" command.
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* getcell cellName [child refPointChild] [parent refPointParent]
|
|
|
|
|
*
|
|
|
|
|
* where the refPoints are either a label name, e.g., SOCKET_A, or an x-y
|
|
|
|
|
* pair of integers, e.g., 100 200. The words "child" and "parent" are
|
|
|
|
|
* keywords, and may be abbreviated.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Makes cellName a subcell of the edit cell, positioned so
|
|
|
|
|
* that refPointChild in the child cell (or the lower-left
|
|
|
|
|
* corner of its bounding box) ends up at location refPointParent
|
|
|
|
|
* in the edit cell (or the location of the box tool's lower-left).
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdGetcell(w, cmd)
|
|
|
|
|
MagWindow *w; /* Window in which command was invoked. */
|
|
|
|
|
TxCommand *cmd; /* Describes command arguments. */
|
|
|
|
|
{
|
|
|
|
|
CellUse dummy, *newUse;
|
|
|
|
|
Transform editTrans;
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
CellDef *def;
|
|
|
|
|
Rect newBox;
|
|
|
|
|
|
|
|
|
|
/* Leaves scx.scx_trans set to the transform from the child to root */
|
|
|
|
|
if (!cmdDumpParseArgs("getcell", w, cmd, &dummy, &scx))
|
|
|
|
|
return;
|
|
|
|
|
def = dummy.cu_def;
|
|
|
|
|
|
|
|
|
|
/* Create the new use. */
|
|
|
|
|
newUse = DBCellNewUse(def, (char *) NULL);
|
|
|
|
|
if (!DBLinkCell(newUse, EditCellUse->cu_def))
|
|
|
|
|
{
|
|
|
|
|
(void) DBCellDeleteUse(newUse);
|
|
|
|
|
TxError("Could not link in new cell\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GeoTransTrans(&scx.scx_trans, &RootToEditTransform, &editTrans);
|
|
|
|
|
DBSetTrans(newUse, &editTrans);
|
|
|
|
|
if (DBCellFindDup(newUse, EditCellUse->cu_def) != NULL)
|
|
|
|
|
{
|
|
|
|
|
DBCellDeleteUse(newUse);
|
|
|
|
|
TxError("Can't place a cell on an exact copy of itself.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
DBPlaceCell(newUse, EditCellUse->cu_def);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Reposition the box tool to around the gotten cell to show
|
|
|
|
|
* that it has become the current cell.
|
|
|
|
|
*/
|
|
|
|
|
GeoTransRect(&EditToRootTransform, &newUse->cu_bbox, &newBox);
|
|
|
|
|
DBWSetBox(EditRootDef, &newBox);
|
|
|
|
|
|
|
|
|
|
/* Select the new use */
|
|
|
|
|
SelectClear();
|
|
|
|
|
SelectCell(newUse, EditRootDef, &scx.scx_trans, FALSE);
|
|
|
|
|
|
|
|
|
|
/* Redisplay and mark for design-rule checking */
|
|
|
|
|
DBReComputeBbox(EditCellUse->cu_def);
|
|
|
|
|
DBWAreaChanged(EditCellUse->cu_def, &newUse->cu_bbox,
|
|
|
|
|
DBW_ALLWINDOWS, &DBAllButSpaceBits);
|
|
|
|
|
DRCCheckThis(EditCellUse->cu_def, TT_CHECKSUBCELL, &newUse->cu_bbox);
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
/* If using the TCL wrapper, set the TCL return value to the */
|
|
|
|
|
/* name of the new use. */
|
|
|
|
|
if (newUse->cu_id)
|
|
|
|
|
Tcl_SetResult(magicinterp, newUse->cu_id, TCL_VOLATILE);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
#ifndef NO_SIM_MODULE
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdGetnode --
|
|
|
|
|
*
|
|
|
|
|
* Implement the "getnode" command.
|
|
|
|
|
* Returns the name of the node pointed by the mouse
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* getnode
|
|
|
|
|
* getnode abort [string]
|
|
|
|
|
* getnode alias [on | off]
|
|
|
|
|
* getnode globals [on | off]
|
|
|
|
|
* getnode fast
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The GetNode hash tables may be modified.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdGetnode(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
#define TBLSIZE 50
|
|
|
|
|
#define STRINGS 0
|
|
|
|
|
|
|
|
|
|
bool is_fast = FALSE;
|
|
|
|
|
|
|
|
|
|
/* check arguments to command */
|
|
|
|
|
|
|
|
|
|
switch (cmd->tx_argc) {
|
2020-05-23 23:13:14 +02:00
|
|
|
case 1 :
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
case 2 :
|
2017-04-25 14:41:48 +02:00
|
|
|
if (strcmp("abort", cmd->tx_argv[1]) == 0) {
|
|
|
|
|
if (!SimInitGetnode) {
|
|
|
|
|
HashKill(&SimGetnodeTbl);
|
|
|
|
|
SimInitGetnode = TRUE;
|
|
|
|
|
SimRecomputeSel = TRUE;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp("fast", cmd->tx_argv[1]) == 0) {
|
|
|
|
|
is_fast = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (!strcmp("alias", cmd->tx_argv[1])) {
|
|
|
|
|
TxPrintf("Aliases %s\n", (SimGetnodeAlias) ? "on" : "off");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (strncmp("global", cmd->tx_argv[1], 6) == 0) {
|
|
|
|
|
TxPrintf("Node names ending in ! are %s\n",
|
|
|
|
|
(SimIgnoreGlobals) ? "local (off)" : "global (on)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto badusage;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
case 3 :
|
2017-04-25 14:41:48 +02:00
|
|
|
if (strcmp("alias", cmd->tx_argv[1]) == 0) {
|
|
|
|
|
if (strcmp("on", cmd->tx_argv[2]) == 0) {
|
|
|
|
|
if (!SimGetnodeAlias) {
|
|
|
|
|
HashInit(&SimGNAliasTbl, 120, STRINGS);
|
|
|
|
|
}
|
|
|
|
|
SimGetnodeAlias = TRUE;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp("off", cmd->tx_argv[2]) == 0) {
|
|
|
|
|
if (SimGetnodeAlias) {
|
|
|
|
|
HashKill(&SimGNAliasTbl);
|
|
|
|
|
}
|
|
|
|
|
SimGetnodeAlias = FALSE;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto badusage;
|
|
|
|
|
}
|
|
|
|
|
else if (strncmp("global", cmd->tx_argv[1], 6) == 0) {
|
|
|
|
|
if (strcmp("off", cmd->tx_argv[2]) == 0) {
|
|
|
|
|
SimIgnoreGlobals = TRUE;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp("on", cmd->tx_argv[2]) == 0) {
|
|
|
|
|
SimIgnoreGlobals = FALSE;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto badusage;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp("abort", cmd->tx_argv[1]) == 0) {
|
|
|
|
|
if (SimInitGetnode) {
|
|
|
|
|
HashInit(&SimGetnodeTbl, TBLSIZE, STRINGS);
|
|
|
|
|
SimInitGetnode = FALSE;
|
|
|
|
|
}
|
|
|
|
|
SimRecomputeSel = TRUE;
|
|
|
|
|
HashFind(&SimGetnodeTbl, cmd->tx_argv[2]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
goto badusage;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default :
|
|
|
|
|
goto badusage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
|
|
|
if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
|
|
|
|
|
{
|
|
|
|
|
TxError("Put the cursor in a layout window\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if( is_fast == TRUE )
|
|
|
|
|
{
|
|
|
|
|
SimRecomputeSel = TRUE;
|
|
|
|
|
SimGetsnode();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
SimGetnode();
|
|
|
|
|
|
|
|
|
|
if (SimGetnodeAlias) { /* "erase" the hash table */
|
|
|
|
|
HashKill(&SimGNAliasTbl);
|
|
|
|
|
HashInit(&SimGNAliasTbl, 120, STRINGS);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
badusage:
|
|
|
|
|
TxError("Usage: getnode [abort [str]]\n");
|
|
|
|
|
TxError(" or: getnode alias [on | off]\n");
|
|
|
|
|
TxError(" or: getnode globals [on | off]\n");
|
|
|
|
|
TxError(" or: getnode fast\n");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdGrid --
|
|
|
|
|
*
|
|
|
|
|
* Implement the "gridspace" command.
|
|
|
|
|
* Toggle the grid on or off in the selected window.
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* gridspace [spacing [spacing [xorig yorig]]]
|
|
|
|
|
* gridspace on|off|box|state|help|multiple
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None, except to enable or disable grid display.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define GRID_BOX 0
|
|
|
|
|
#define GRID_HELP 1
|
|
|
|
|
#define GRID_MULTIPLE 2
|
|
|
|
|
#define GRID_OFF 3
|
|
|
|
|
#define GRID_ON 4
|
|
|
|
|
#define GRID_STATE 5
|
|
|
|
|
#define GRID_TOGGLE 6
|
|
|
|
|
#define GRID_WHAT 7
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdGrid(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
int option, locargc;
|
|
|
|
|
int xSpacing, ySpacing, xOrig, yOrig, multiple;
|
|
|
|
|
DBWclientRec *crec;
|
|
|
|
|
char *boxvalues;
|
|
|
|
|
static char *cmdGridOptions[] =
|
|
|
|
|
{
|
|
|
|
|
"box [values] report the box representing the user grid",
|
|
|
|
|
"help print this message",
|
|
|
|
|
"multiple [m] set the grid multiple for drawing grids at large scales",
|
|
|
|
|
"off turn off the user grid",
|
|
|
|
|
"on turn on the user grid",
|
|
|
|
|
"state report the state of the user grid",
|
|
|
|
|
"toggle toggle the state (on/off) of the user grid",
|
|
|
|
|
"what (equivalent to option \"box\")",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
|
|
|
if (w == (MagWindow *) NULL) return;
|
|
|
|
|
crec = (DBWclientRec *) w->w_clientData;
|
|
|
|
|
locargc = cmd->tx_argc;
|
|
|
|
|
|
|
|
|
|
if (locargc == 1)
|
|
|
|
|
option = GRID_TOGGLE;
|
|
|
|
|
else if ((locargc == 2) && !strcmp(cmd->tx_argv[1], "0"))
|
|
|
|
|
option = GRID_OFF;
|
|
|
|
|
else
|
|
|
|
|
option = Lookup(cmd->tx_argv[1], cmdGridOptions);
|
|
|
|
|
|
|
|
|
|
/* Process various options with two arguments */
|
|
|
|
|
switch (option)
|
|
|
|
|
{
|
|
|
|
|
case GRID_BOX:
|
|
|
|
|
if (locargc > 2)
|
|
|
|
|
{
|
|
|
|
|
locargc--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case GRID_WHAT:
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
boxvalues = (char *)Tcl_Alloc(50);
|
|
|
|
|
sprintf(boxvalues, "%d %d %d %d",
|
|
|
|
|
crec->dbw_gridRect.r_xbot, crec->dbw_gridRect.r_ybot,
|
|
|
|
|
crec->dbw_gridRect.r_xtop, crec->dbw_gridRect.r_ytop);
|
|
|
|
|
Tcl_SetResult(magicinterp, boxvalues, TCL_DYNAMIC);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#else
|
2020-05-23 23:13:14 +02:00
|
|
|
TxPrintf("Grid unit box is (%d, %d) to (%d, %d)\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
crec->dbw_gridRect.r_xbot, crec->dbw_gridRect.r_ybot,
|
|
|
|
|
crec->dbw_gridRect.r_xtop, crec->dbw_gridRect.r_ytop);
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case GRID_STATE:
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_SetObjResult(magicinterp,
|
|
|
|
|
Tcl_NewBooleanObj(crec->dbw_flags & DBW_GRID));
|
|
|
|
|
#else
|
2020-05-23 23:13:14 +02:00
|
|
|
TxPrintf("Grid is %s\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
(crec->dbw_flags & DBW_GRID) ? "on" : "off");
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case GRID_HELP:
|
|
|
|
|
|
|
|
|
|
TxPrintf("Usage: grid [xSpacing [ySpacing [xOrig yOrig]]]]\n");
|
|
|
|
|
TxPrintf("or grid <option>\n");
|
|
|
|
|
TxPrintf("where <option> is one of: "
|
|
|
|
|
"on, off, state, box, what, help, or multiple.\n");
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case GRID_OFF:
|
|
|
|
|
|
|
|
|
|
if (crec->dbw_flags & DBW_GRID)
|
|
|
|
|
{
|
|
|
|
|
crec->dbw_flags &= ~DBW_GRID;
|
|
|
|
|
WindAreaChanged(w, (Rect *) NULL);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case GRID_ON:
|
|
|
|
|
if (!(crec->dbw_flags & DBW_GRID))
|
|
|
|
|
{
|
|
|
|
|
crec->dbw_flags |= DBW_GRID;
|
|
|
|
|
WindAreaChanged(w, (Rect *) NULL);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case GRID_MULTIPLE:
|
|
|
|
|
if (locargc == 2)
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_SetObjResult(magicinterp,
|
|
|
|
|
Tcl_NewIntObj(GrGridMultiple));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else if (StrIsInt(cmd->tx_argv[2]))
|
|
|
|
|
{
|
|
|
|
|
multiple = atoi(cmd->tx_argv[2]);
|
|
|
|
|
if (multiple < 1 || multiple > 255)
|
|
|
|
|
TxError("Usage: grid multiple <integer value 1-255>\n");
|
|
|
|
|
GrGridMultiple = (unsigned char)multiple;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(cmd->tx_argv[2], "off"))
|
|
|
|
|
GrGridMultiple = (unsigned char)1;
|
|
|
|
|
else
|
|
|
|
|
TxError("Usage: grid multiple <integer value 1-255>\n");
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case GRID_TOGGLE:
|
|
|
|
|
crec->dbw_flags ^= DBW_GRID;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((option == GRID_BOX) || (option < 0))
|
|
|
|
|
{
|
|
|
|
|
int argstart = (option == GRID_BOX) ? 2 : 1;
|
|
|
|
|
|
|
|
|
|
if ((locargc == 4) || (locargc > 5))
|
|
|
|
|
{
|
|
|
|
|
TxError("Usage: %s [xSpacing [ySpacing [xOrig yOrig]]]]\n",
|
|
|
|
|
cmd->tx_argv[0]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xSpacing = cmdParseCoord(w, cmd->tx_argv[argstart], TRUE, TRUE);
|
|
|
|
|
if (xSpacing <= 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Grid spacing must be greater than zero.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ySpacing = xSpacing;
|
|
|
|
|
xOrig = yOrig = 0;
|
|
|
|
|
|
|
|
|
|
if (locargc >= 3)
|
|
|
|
|
{
|
|
|
|
|
ySpacing = cmdParseCoord(w, cmd->tx_argv[argstart + 1], TRUE, FALSE);
|
|
|
|
|
if (ySpacing <= 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Grid spacing must be greater than zero.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (locargc == 5)
|
|
|
|
|
{
|
|
|
|
|
xOrig = cmdParseCoord(w, cmd->tx_argv[argstart + 2], FALSE, TRUE);
|
|
|
|
|
yOrig = cmdParseCoord(w, cmd->tx_argv[argstart + 3], FALSE, FALSE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
crec->dbw_gridRect.r_xbot = xOrig;
|
|
|
|
|
crec->dbw_gridRect.r_ybot = yOrig;
|
|
|
|
|
crec->dbw_gridRect.r_xtop = xOrig + xSpacing;
|
|
|
|
|
crec->dbw_gridRect.r_ytop = yOrig + ySpacing;
|
|
|
|
|
crec->dbw_flags |= DBW_GRID;
|
|
|
|
|
}
|
|
|
|
|
WindAreaChanged(w, (Rect *) NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef USE_READLINE
|
|
|
|
|
void
|
|
|
|
|
CmdHistory(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
HIST_ENTRY *he;
|
|
|
|
|
int list_reverse = 0;
|
|
|
|
|
int list_numbers = 1;
|
|
|
|
|
int print_help = 0;
|
|
|
|
|
int num_to_list = history_length;
|
|
|
|
|
|
|
|
|
|
if( cmd->tx_argc > 1 ) {
|
|
|
|
|
for(i=1; i<cmd->tx_argc; i++) {
|
|
|
|
|
if( strcmp(cmd->tx_argv[i], "help") == 0 ) {
|
|
|
|
|
print_help = 1;
|
|
|
|
|
break;
|
|
|
|
|
} else if( strcmp(cmd->tx_argv[i], "n") == 0 ) {
|
|
|
|
|
list_numbers = 0;
|
|
|
|
|
} else if( strcmp(cmd->tx_argv[i], "r") == 0 ) {
|
|
|
|
|
list_reverse = 1;
|
|
|
|
|
} else {
|
|
|
|
|
num_to_list = atoi(cmd->tx_argv[i]);
|
|
|
|
|
if( num_to_list == 0 ) {
|
|
|
|
|
TxError("Bad arg to history: '%s'\n", cmd->tx_argv[i]);
|
|
|
|
|
print_help = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( print_help ) {
|
|
|
|
|
TxError("Usage: history [n] [r] [<num>]\n"
|
|
|
|
|
" 'n' suppresses printing numbers\n"
|
|
|
|
|
" 'r' prints the list in reverse\n"
|
|
|
|
|
" '<num>' is the number of entries to print\n"
|
|
|
|
|
" 'help' prints this message\n"
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
int begin = MAX(history_length - num_to_list, 0);
|
|
|
|
|
int end = history_length;
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
for(i=begin,j=end-1; i<end; i++,j--) {
|
|
|
|
|
int idx = (list_reverse) ? j : i;
|
|
|
|
|
HIST_ENTRY *he = history_get(idx);
|
|
|
|
|
if( he != (HIST_ENTRY *)NULL && he->line != (char *)NULL ) {
|
|
|
|
|
if( list_numbers ) {
|
|
|
|
|
TxPrintf("\t%4d %s\n", idx, he->line);
|
|
|
|
|
} else {
|
|
|
|
|
TxPrintf("%s\n", he->line);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdIdentify --
|
|
|
|
|
*
|
|
|
|
|
* Implement the "identify" command.
|
|
|
|
|
* Sets the instance identifier for the currently selected cell.
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* identify use_id
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Modifies the instance identifier for the selected cell (the
|
|
|
|
|
* first selected cell, if there are many).
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdIdentify(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
extern int cmdIdFunc(); /* Forward reference. */
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc != 2)
|
|
|
|
|
{
|
|
|
|
|
TxError("Usage: identify use_id\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-02 20:50:32 +02:00
|
|
|
/* NOTE: Relaxing the definition of illegal characters in cell use IDs */
|
|
|
|
|
/* by allowing brackets. Possibly the list can be reduced further. */
|
|
|
|
|
|
|
|
|
|
/* if (CmdIllegalChars(cmd->tx_argv[1], "[],/", "Cell use id")) */
|
|
|
|
|
if (CmdIllegalChars(cmd->tx_argv[1], ",/", "Cell use id"))
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (SelEnumCells(FALSE, (int *) NULL, (SearchContext *) NULL,
|
|
|
|
|
cmdIdFunc, (ClientData) cmd->tx_argv[1]) == 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("There isn't a selected subcell; can't change ids.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
int
|
|
|
|
|
cmdIdFunc(selUse, use, transform, newId)
|
|
|
|
|
CellUse *selUse; /* Use from selection cell. */
|
|
|
|
|
CellUse *use; /* Use from layout that corresponds to
|
|
|
|
|
* selUse.
|
|
|
|
|
*/
|
|
|
|
|
Transform *transform; /* Not used. */
|
|
|
|
|
char *newId; /* New id for cell use. */
|
|
|
|
|
{
|
|
|
|
|
if (EditCellUse == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Top-level cell is not editable---cannot change identifier"
|
|
|
|
|
" of child cell %s.\n", use->cu_id);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (!DBIsChild(use, EditCellUse))
|
|
|
|
|
{
|
|
|
|
|
TxError("Cell %s (%s) isn't a child of the edit cell.\n",
|
|
|
|
|
use->cu_id, use->cu_def->cd_name);
|
|
|
|
|
TxError(" Cell identifier not changed.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (use->cu_parent == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Cell instance is a window top-level and cannot be changed.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!DBReLinkCell(use, newId))
|
|
|
|
|
{
|
|
|
|
|
TxError("New name isn't unique within its parent definition.\n");
|
|
|
|
|
TxError(" Cell identifier not changed.\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Change the id of the cell in the selection too, so that they
|
|
|
|
|
* stay in sync.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
(void) DBReLinkCell(selUse, newId);
|
|
|
|
|
|
|
|
|
|
DBWAreaChanged(use->cu_parent, &use->cu_bbox,
|
|
|
|
|
(int) ~(use->cu_expandMask), &DBAllButSpaceBits);
|
|
|
|
|
DBWHLRedraw(EditRootDef, &selUse->cu_bbox, TRUE);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TileType
|
|
|
|
|
CmdFindNetProc(nodename, use, rect, warn_not_found)
|
|
|
|
|
char *nodename;
|
|
|
|
|
CellUse *use;
|
|
|
|
|
Rect *rect;
|
|
|
|
|
bool warn_not_found;
|
|
|
|
|
{
|
|
|
|
|
char *s,*s2;
|
|
|
|
|
SearchContext scx, scx2;
|
|
|
|
|
Transform trans, newtrans, tmp;
|
|
|
|
|
Label *label;
|
|
|
|
|
Rect localrect;
|
|
|
|
|
int pnum, xpos, ypos;
|
|
|
|
|
char *xstr, *ystr;
|
2021-10-08 16:58:10 +02:00
|
|
|
bool locvalid = FALSE, usefound = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
TileType ttype;
|
|
|
|
|
|
|
|
|
|
scx.scx_use = use;
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
s = nodename;
|
|
|
|
|
trans = GeoIdentityTransform;
|
|
|
|
|
while (s2 = strchr(s, '/'))
|
|
|
|
|
{
|
|
|
|
|
*s2 = '\0';
|
|
|
|
|
DBTreeFindUse(s, scx.scx_use, &scx2);
|
|
|
|
|
use = scx2.scx_use;
|
|
|
|
|
if (use == NULL)
|
|
|
|
|
{
|
2021-10-08 16:58:10 +02:00
|
|
|
/* Assume slash is part of the name and try to find locally */
|
|
|
|
|
*s2 = '/';
|
|
|
|
|
s = nodename;
|
|
|
|
|
usefound = FALSE;
|
|
|
|
|
goto checklocal;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
GeoTransTrans(DBGetArrayTransform(use, scx2.scx_x, scx2.scx_y),
|
|
|
|
|
&use->cu_transform, &tmp);
|
|
|
|
|
GeoTransTrans(&tmp, &trans, &newtrans);
|
|
|
|
|
trans = newtrans;
|
|
|
|
|
scx = scx2;
|
|
|
|
|
*s2 = '/';
|
|
|
|
|
s = s2 + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this node name is in the format of automatically-generated */
|
|
|
|
|
/* node names, then parse the node string for X and Y values and */
|
|
|
|
|
/* go to that point (transformed to the top level of the design */
|
|
|
|
|
/* hierarchy). */
|
|
|
|
|
|
2021-01-07 21:19:36 +01:00
|
|
|
/* see extract/extBasic.c for the format of the node, found */
|
|
|
|
|
/* in extMakeNodeNumPrint(). */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if ((xstr = strchr(s, '_')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
bool isNeg = FALSE;
|
|
|
|
|
|
|
|
|
|
/* The characters up to the leading '_' should match one of the */
|
2020-05-23 23:13:14 +02:00
|
|
|
/* "short names" for a plane in this technology. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
*xstr = '\0';
|
|
|
|
|
for (pnum = PL_TECHDEPBASE; pnum < DBNumPlanes; pnum++)
|
|
|
|
|
if (!strcmp(s, DBPlaneShortName(pnum)))
|
|
|
|
|
break;
|
|
|
|
|
*xstr = '_';
|
|
|
|
|
if (pnum != DBNumPlanes)
|
|
|
|
|
{
|
|
|
|
|
xstr++;
|
|
|
|
|
if (*xstr == 'n')
|
|
|
|
|
{
|
|
|
|
|
isNeg = TRUE;
|
|
|
|
|
xstr++;
|
|
|
|
|
}
|
|
|
|
|
if (sscanf(xstr, "%d", &xpos) == 1)
|
|
|
|
|
{
|
|
|
|
|
if (isNeg) xpos = -xpos;
|
|
|
|
|
if ((ystr = strchr(xstr, '_')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
isNeg = FALSE;
|
|
|
|
|
ystr++;
|
|
|
|
|
if (*ystr == 'n')
|
|
|
|
|
{
|
|
|
|
|
isNeg = TRUE;
|
|
|
|
|
ystr++;
|
|
|
|
|
}
|
|
|
|
|
if (sscanf(ystr, "%d", &ypos) == 1)
|
|
|
|
|
{
|
|
|
|
|
if (isNeg) ypos = -ypos;
|
|
|
|
|
localrect.r_xbot = xpos;
|
|
|
|
|
localrect.r_ybot = ypos;
|
|
|
|
|
localrect.r_xtop = xpos + 1;
|
|
|
|
|
localrect.r_ytop = ypos + 1;
|
2020-05-23 23:13:14 +02:00
|
|
|
/* TxPrintf("Node is on the plane \"%s\"\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
DBPlaneLongNameTbl[pnum]); */
|
|
|
|
|
locvalid = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is the original version, and assumes a format for node */
|
|
|
|
|
/* coordinates that is no longer generated by magic. It is kept */
|
|
|
|
|
/* here for backward compatibility. */
|
|
|
|
|
|
2021-10-08 16:58:10 +02:00
|
|
|
if ((locvalid == FALSE) && (sscanf(s, "%d_%d_%d", &pnum, &xpos, &ypos) == 3))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-10-08 16:58:10 +02:00
|
|
|
xpos = ((xpos & 0x1) ? -1 : 1) * xpos / 2;
|
|
|
|
|
ypos = ((ypos & 0x1) ? -1 : 1) * ypos / 2;
|
2017-04-25 14:41:48 +02:00
|
|
|
localrect.r_xbot = xpos;
|
|
|
|
|
localrect.r_ybot = ypos;
|
|
|
|
|
localrect.r_xtop = xpos + 1;
|
|
|
|
|
localrect.r_ytop = ypos + 1;
|
|
|
|
|
locvalid = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-08 16:58:10 +02:00
|
|
|
checklocal:
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (locvalid == TRUE)
|
|
|
|
|
{
|
|
|
|
|
int findTile();
|
|
|
|
|
CellDef *targetdef = use->cu_def;
|
|
|
|
|
Plane *plane = targetdef->cd_planes[pnum];
|
|
|
|
|
|
|
|
|
|
ttype = TT_SPACE; /* revert to space in case of failure */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Find the tile type of the tile at the specified point which */
|
|
|
|
|
/* exists on the plane pnum. */
|
|
|
|
|
|
2019-03-23 00:58:47 +01:00
|
|
|
DBSrPaintArea(NULL, plane, &localrect, &DBAllTypeBits, findTile,
|
|
|
|
|
(ClientData) &ttype);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (label = scx.scx_use->cu_def->cd_labels; label;
|
|
|
|
|
label = label->lab_next)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(label->lab_text, s)) break;
|
|
|
|
|
}
|
|
|
|
|
if (label)
|
|
|
|
|
{
|
|
|
|
|
localrect = label->lab_rect;
|
|
|
|
|
ttype = label->lab_type;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (warn_not_found)
|
2021-10-08 16:58:10 +02:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
TxError("Couldn't find label %s\n", s);
|
2021-10-08 16:58:10 +02:00
|
|
|
if (!usefound)
|
|
|
|
|
TxError("Couldn't find use referenced in hierarchical name\n");
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
return TT_SPACE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
GeoTransRect(&trans, &localrect, rect);
|
|
|
|
|
return ttype;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdGoto --
|
|
|
|
|
*
|
|
|
|
|
* Implements goto command
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* goto nodename [-nocomplain]
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None. In TCL version, returns the material type
|
|
|
|
|
* of the node, so that the node can be uniquely determined
|
|
|
|
|
* by an automated script. If the node cannot be found, a
|
|
|
|
|
* null list is returned.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* changes box location.
|
|
|
|
|
*
|
|
|
|
|
* Notes:
|
|
|
|
|
* Due to the way global node names are handled, "getnode" and
|
|
|
|
|
* "goto" are not necessarily reversible if "getnode globals on"
|
|
|
|
|
* is set! In this case, use the glob option for findlabel
|
|
|
|
|
* to determine any valid local label belonging to the global
|
|
|
|
|
* node (a node cannot be global unless it is labeled). This
|
|
|
|
|
* method, although awkward, can be much faster for large layouts
|
|
|
|
|
* due to the time required for "getnode" to uniquely determine
|
|
|
|
|
* the name of a large network such as power or ground.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdGoto(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
char *s, *nodename = cmd->tx_argv[1];
|
|
|
|
|
Rect rect;
|
|
|
|
|
CellUse *use;
|
|
|
|
|
int locargc;
|
|
|
|
|
bool nocomplain = FALSE;
|
|
|
|
|
TileType ttype;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
windCheckOnlyWindow(&w, DBWclientID);
|
|
|
|
|
if ((w == (MagWindow *) NULL) || (w->w_client != DBWclientID))
|
|
|
|
|
{
|
|
|
|
|
TxError("Put the cursor in a layout window\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
locargc = cmd->tx_argc;
|
|
|
|
|
if (locargc == 3)
|
|
|
|
|
{
|
|
|
|
|
if (!strncmp(cmd->tx_argv[2], "-nocom", 5))
|
|
|
|
|
{
|
|
|
|
|
nocomplain = TRUE;
|
|
|
|
|
locargc--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (locargc != 2)
|
|
|
|
|
{
|
|
|
|
|
TxError("usage: goto nodename [-nocomplain]\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* CmdFindNetProc() does all the work */
|
|
|
|
|
use = (CellUse *)w->w_surfaceID;
|
|
|
|
|
ttype = CmdFindNetProc(nodename, use, &rect, !nocomplain);
|
|
|
|
|
if (ttype == TT_SPACE) return;
|
|
|
|
|
|
|
|
|
|
ToolMoveBox(TOOL_BL, &rect.r_ll, FALSE, use->cu_def);
|
|
|
|
|
ToolMoveCorner(TOOL_TR, &rect.r_ur, FALSE, use->cu_def);
|
|
|
|
|
|
|
|
|
|
/* Return the tile type so we know what we're looking at if there */
|
2020-05-23 23:13:14 +02:00
|
|
|
/* are multiple layers drawn at the indicated point. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_SetResult(magicinterp, DBTypeLongName(ttype), NULL);
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("node %s is type %s\n", s, DBTypeLongName(ttype));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* Filter function for finding the tile type of the specified node
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
findTile(tile, rtype)
|
|
|
|
|
Tile *tile;
|
|
|
|
|
TileType *rtype;
|
|
|
|
|
{
|
|
|
|
|
TileType ttype;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (IsSplit(tile))
|
|
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
if (SplitSide(tile))
|
|
|
|
|
ttype = SplitRightType(tile);
|
|
|
|
|
else
|
|
|
|
|
ttype = SplitLeftType(tile);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ttype = TiGetTypeExact(tile);
|
|
|
|
|
*rtype = ttype;
|
|
|
|
|
return 1; /* stop search */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* The following are from DBcellcopy.c; slightly modified for present
|
2017-04-25 14:41:48 +02:00
|
|
|
* purposes.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
FlatCopyAllLabels(scx, mask, xMask, targetUse)
|
|
|
|
|
SearchContext *scx;
|
|
|
|
|
TileTypeBitMask *mask;
|
2020-05-23 23:13:14 +02:00
|
|
|
int xMask;
|
2017-04-25 14:41:48 +02:00
|
|
|
CellUse *targetUse;
|
|
|
|
|
{
|
|
|
|
|
int flatCopyAllLabels();
|
|
|
|
|
char pathstring[FLATTERMSIZE];
|
|
|
|
|
TerminalPath tpath;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2020-02-14 14:48:47 +01:00
|
|
|
pathstring[0] = '\0';
|
2017-04-25 14:41:48 +02:00
|
|
|
tpath.tp_first = tpath.tp_next = pathstring;
|
|
|
|
|
tpath.tp_last = pathstring + FLATTERMSIZE;
|
|
|
|
|
|
|
|
|
|
DBTreeSrLabels(scx, mask, xMask, &tpath, TF_LABEL_ATTACH,
|
|
|
|
|
flatCopyAllLabels, (ClientData) targetUse);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
flatCopyAllLabels(scx, lab, tpath, targetUse)
|
|
|
|
|
SearchContext *scx;
|
|
|
|
|
Label *lab;
|
|
|
|
|
TerminalPath *tpath;
|
|
|
|
|
CellUse *targetUse;
|
|
|
|
|
{
|
|
|
|
|
Rect labTargetRect;
|
|
|
|
|
int targetPos;
|
2021-12-13 04:09:31 +01:00
|
|
|
unsigned short flags = 0;
|
|
|
|
|
unsigned int port = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
CellDef *def;
|
|
|
|
|
char labelname[1024];
|
|
|
|
|
char *n, *f, c;
|
|
|
|
|
|
2021-01-08 03:47:16 +01:00
|
|
|
/* Ignore null labels */
|
|
|
|
|
if (*lab->lab_text == '\0') return 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
def = targetUse->cu_def;
|
|
|
|
|
if (!GEO_LABEL_IN_AREA(&lab->lab_rect, &(scx->scx_area))) return 0;
|
|
|
|
|
GeoTransRect(&scx->scx_trans, &lab->lab_rect, &labTargetRect);
|
|
|
|
|
targetPos = GeoTransPos(&scx->scx_trans, lab->lab_just);
|
|
|
|
|
|
|
|
|
|
/* Eliminate duplicate labels. Don't pay any attention to layers
|
|
|
|
|
* in deciding on duplicates: if text and position match, it's a
|
|
|
|
|
* duplicate.
|
|
|
|
|
*/
|
|
|
|
|
/* 9/12/05---The utility of eliminating duplicate labels has been
|
|
|
|
|
* put into question by observing the O(n^2) behavior causing
|
|
|
|
|
* significant flattening times for layouts with many labels.
|
|
|
|
|
* Label text will not be duplicated anyway, because each label
|
|
|
|
|
* contains the heirarchical names.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* (void) DBEraseLabelsByContent(def, &labTargetRect, -1, lab->lab_text); */
|
|
|
|
|
|
|
|
|
|
/* (Added 9/10/04) Make sure that the target flattened label is
|
|
|
|
|
* not a port; possibly we should retain ports taken from the
|
|
|
|
|
* top level cell, but certainly not any others.
|
|
|
|
|
*/
|
2021-03-25 19:39:29 +01:00
|
|
|
if (tpath && (*tpath->tp_first) == '\0')
|
2021-12-13 04:09:31 +01:00
|
|
|
{
|
2021-03-25 19:39:29 +01:00
|
|
|
flags = lab->lab_flags;
|
2021-12-13 04:09:31 +01:00
|
|
|
port = lab->lab_port;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* To-do Feb. 2008: Translate target rotation and offset */
|
|
|
|
|
|
|
|
|
|
n = tpath->tp_next;
|
|
|
|
|
f = tpath->tp_first;
|
|
|
|
|
c = *n;
|
|
|
|
|
strcpy(n, lab->lab_text);
|
|
|
|
|
DBPutFontLabel(def, &labTargetRect, lab->lab_font, lab->lab_size,
|
|
|
|
|
lab->lab_rotate, &lab->lab_offset, targetPos,
|
2021-12-13 04:09:31 +01:00
|
|
|
f, lab->lab_type, flags, port);
|
2017-04-25 14:41:48 +02:00
|
|
|
*n = c;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdFlatten --
|
|
|
|
|
*
|
|
|
|
|
* Implements flatten command
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* flatten [-<option>] destname
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* creates new cell def with all the desired info.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdFlatten(w, cmd)
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
int rval, xMask;
|
2021-03-25 19:39:29 +01:00
|
|
|
bool dolabels, dobox, toplabels, invert, doports;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *destname;
|
|
|
|
|
CellDef *newdef;
|
|
|
|
|
CellUse *newuse;
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
CellUse *flatDestUse;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
destname = cmd->tx_argv[cmd->tx_argc - 1];
|
|
|
|
|
xMask = CU_DESCEND_ALL;
|
|
|
|
|
dolabels = TRUE;
|
2017-08-02 04:14:42 +02:00
|
|
|
toplabels = FALSE;
|
2020-12-29 17:51:15 +01:00
|
|
|
dobox = FALSE;
|
2021-03-25 19:39:29 +01:00
|
|
|
doports = TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
rval = 0;
|
|
|
|
|
if (cmd->tx_argc > 2)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 1; i < (cmd->tx_argc - 1); i++)
|
|
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
if (!strncmp(cmd->tx_argv[i], "-no", 3))
|
|
|
|
|
{
|
|
|
|
|
invert = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (!strncmp(cmd->tx_argv[i], "-do", 3))
|
|
|
|
|
{
|
|
|
|
|
invert = FALSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
rval = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-08-02 04:14:42 +02:00
|
|
|
|
|
|
|
|
if (strlen(cmd->tx_argv[i]) > 3)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2017-08-02 04:14:42 +02:00
|
|
|
switch(cmd->tx_argv[i][3])
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-12-29 17:51:15 +01:00
|
|
|
case 'b':
|
|
|
|
|
dobox = (invert) ? FALSE : TRUE;
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'l':
|
2017-08-02 04:14:42 +02:00
|
|
|
dolabels = (invert) ? FALSE : TRUE;
|
|
|
|
|
break;
|
|
|
|
|
case 't':
|
|
|
|
|
toplabels = (invert) ? FALSE : TRUE;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
2021-03-25 19:39:29 +01:00
|
|
|
case 'p':
|
|
|
|
|
doports = (invert) ? FALSE : TRUE;
|
|
|
|
|
break;
|
2017-04-25 14:41:48 +02:00
|
|
|
case 's':
|
2017-08-02 04:14:42 +02:00
|
|
|
xMask = (invert) ? CU_DESCEND_NO_SUBCKT : CU_DESCEND_ALL;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case 'v':
|
2017-08-02 04:14:42 +02:00
|
|
|
xMask = (invert) ? CU_DESCEND_NO_VENDOR : CU_DESCEND_ALL;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
2021-03-25 19:39:29 +01:00
|
|
|
TxError("options are: -nolabels, -nosubcircuits, -noports, "
|
2020-12-29 17:51:15 +01:00
|
|
|
"-novendor, -dotoplabels, -dobox\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (cmd->tx_argc != 2)
|
|
|
|
|
rval = -1;
|
|
|
|
|
|
|
|
|
|
if (rval != 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("usage: flatten [-<option>...] destcell\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* create the new def */
|
2021-06-23 23:41:32 +02:00
|
|
|
newdef = DBCellLookDef(destname);
|
|
|
|
|
if ((newdef != NULL) && (dobox == FALSE))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TxError("%s already exists\n",destname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-06-23 23:41:32 +02:00
|
|
|
else if (newdef == NULL)
|
|
|
|
|
{
|
|
|
|
|
newdef = DBCellNewDef(destname);
|
|
|
|
|
ASSERT(newdef, "CmdFlatten");
|
|
|
|
|
DBCellSetAvail(newdef);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
newuse = DBCellNewUse(newdef, (char *) NULL);
|
|
|
|
|
(void) StrDup(&(newuse->cu_id), "Flattened cell");
|
|
|
|
|
DBSetTrans(newuse, &GeoIdentityTransform);
|
|
|
|
|
newuse->cu_expandMask = CU_DESCEND_SPECIAL;
|
|
|
|
|
flatDestUse = newuse;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (EditCellUse)
|
|
|
|
|
scx.scx_use = EditCellUse;
|
|
|
|
|
else
|
|
|
|
|
scx.scx_use = (CellUse *)w->w_surfaceID;
|
|
|
|
|
|
2020-12-29 17:51:15 +01:00
|
|
|
if (dobox)
|
|
|
|
|
{
|
|
|
|
|
CellDef *boxDef;
|
|
|
|
|
|
|
|
|
|
if (!ToolGetBox(&boxDef, &scx.scx_area))
|
|
|
|
|
{
|
|
|
|
|
TxError("Put the box in a window first.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (boxDef != scx.scx_use->cu_def)
|
|
|
|
|
{
|
|
|
|
|
TxError("The box is not in the edit cell!\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
scx.scx_area = scx.scx_use->cu_def->cd_bbox;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
|
2020-12-29 17:51:15 +01:00
|
|
|
UndoDisable();
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
DBCellCopyAllPaint(&scx, &DBAllButSpaceAndDRCBits, xMask, flatDestUse);
|
|
|
|
|
if (dolabels)
|
|
|
|
|
FlatCopyAllLabels(&scx, &DBAllTypeBits, xMask, flatDestUse);
|
2017-08-02 04:14:42 +02:00
|
|
|
else if (toplabels)
|
|
|
|
|
{
|
|
|
|
|
int savemask = scx.scx_use->cu_expandMask;
|
|
|
|
|
scx.scx_use->cu_expandMask = CU_DESCEND_SPECIAL;
|
|
|
|
|
DBCellCopyAllLabels(&scx, &DBAllTypeBits, CU_DESCEND_SPECIAL, flatDestUse);
|
|
|
|
|
scx.scx_use->cu_expandMask = savemask;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (xMask != CU_DESCEND_ALL)
|
|
|
|
|
DBCellCopyAllCells(&scx, xMask, flatDestUse, (Rect *)NULL);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2021-06-23 23:41:32 +02:00
|
|
|
DBCellDeleteUse(flatDestUse);
|
2017-04-25 14:41:48 +02:00
|
|
|
UndoEnable();
|
|
|
|
|
}
|