2017-04-25 14:41:48 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* 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 "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"
|
|
|
|
|
|
|
|
|
|
|
|
extern void DisplayWindow();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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();
|
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
|
{
|
2020-03-19 14:20:50 +01:00
|
|
|
|
char *fileName;
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
if (CmdIllegalChars(cmd->tx_argv[1], "[],", "Cell name"))
|
|
|
|
|
|
return;
|
2020-03-19 14:20:50 +01:00
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
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;
|
|
|
|
|
|
{
|
|
|
|
|
|
extern void DBScalePoint();
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* 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)
|
|
|
|
|
|
char *layers; /* Which layers are to be selected. */
|
|
|
|
|
|
bool less;
|
|
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
scx.scx_use = (CellUse *) window->w_surfaceID;
|
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
|
crec = (DBWclientRec *) window->w_clientData;
|
|
|
|
|
|
SelectArea(&scx, &mask, crec->dbw_bitmask);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* cmdSelectVisible --
|
|
|
|
|
|
*
|
|
|
|
|
|
* This is a utility procedure used by CmdSelect to do area
|
|
|
|
|
|
* selection of visible paint.
|
|
|
|
|
|
*
|
|
|
|
|
|
* 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 expanded subcells.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
|
void
|
|
|
|
|
|
cmdSelectVisible(layers, less)
|
|
|
|
|
|
char *layers; /* Which layers are to be selected. */
|
|
|
|
|
|
bool less;
|
|
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
scx.scx_use = (CellUse *) window->w_surfaceID;
|
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
|
crec = (DBWclientRec *) window->w_clientData;
|
|
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define SEL_AREA 0
|
|
|
|
|
|
#define SEL_VISIBLE 1
|
|
|
|
|
|
#define SEL_CELL 2
|
|
|
|
|
|
#define SEL_CLEAR 3
|
|
|
|
|
|
#define SEL_FLAT 4
|
|
|
|
|
|
#define SEL_HELP 5
|
|
|
|
|
|
#define SEL_KEEP 6
|
|
|
|
|
|
#define SEL_MOVE 7
|
|
|
|
|
|
#define SEL_PICK 8
|
|
|
|
|
|
#define SEL_SAVE 9
|
|
|
|
|
|
#define SEL_FEEDBACK 10
|
|
|
|
|
|
#define SEL_BOX 11
|
|
|
|
|
|
#define SEL_CHUNK 12
|
|
|
|
|
|
#define SEL_REGION 13
|
|
|
|
|
|
#define SEL_NET 14
|
|
|
|
|
|
#define SEL_SHORT 15
|
|
|
|
|
|
#define SEL_DEFAULT 16
|
|
|
|
|
|
|
|
|
|
|
|
static char *cmdSelectOption[] =
|
|
|
|
|
|
{
|
|
|
|
|
|
"area",
|
|
|
|
|
|
"visible",
|
|
|
|
|
|
"cell",
|
|
|
|
|
|
"clear",
|
|
|
|
|
|
"flat",
|
|
|
|
|
|
"help",
|
|
|
|
|
|
"keep",
|
|
|
|
|
|
"move",
|
|
|
|
|
|
"pick",
|
|
|
|
|
|
"save",
|
|
|
|
|
|
"feedback",
|
|
|
|
|
|
"box",
|
|
|
|
|
|
"chunk",
|
|
|
|
|
|
"region",
|
|
|
|
|
|
"net",
|
|
|
|
|
|
"short",
|
|
|
|
|
|
NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
static char *cmdSelectMsg[] =
|
|
|
|
|
|
{
|
|
|
|
|
|
"[more | less | nocycle] [layers]\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\"",
|
|
|
|
|
|
"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",
|
|
|
|
|
|
"[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;
|
|
|
|
|
|
TerminalPath tpath;
|
|
|
|
|
|
CellUse *use;
|
|
|
|
|
|
CellDef *rootBoxDef;
|
|
|
|
|
|
Transform trans, rootTrans, tmp1;
|
|
|
|
|
|
Point p, rootPoint;
|
|
|
|
|
|
Rect r, selarea;
|
|
|
|
|
|
ExtRectList *rlist;
|
|
|
|
|
|
int option;
|
|
|
|
|
|
int feedstyle;
|
|
|
|
|
|
bool layerspec;
|
|
|
|
|
|
bool degenerate;
|
|
|
|
|
|
bool more = FALSE, less = FALSE, samePlace = TRUE;
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
char *tclstr;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* How close two clicks must be to be considered the same point: */
|
|
|
|
|
|
|
|
|
|
|
|
#define MARGIN 2
|
|
|
|
|
|
|
|
|
|
|
|
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", "nocycle", "top", and "cell".
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
optionArgs = &cmd->tx_argv[2];
|
|
|
|
|
|
cmd->tx_argc--;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!strncmp(cmd->tx_argv[1], "less", arg1len))
|
|
|
|
|
|
{
|
|
|
|
|
|
more = FALSE;
|
|
|
|
|
|
less = TRUE;
|
|
|
|
|
|
optionArgs = &cmd->tx_argv[2];
|
|
|
|
|
|
cmd->tx_argc--;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!strncmp(cmd->tx_argv[1], "nocycle", arg1len))
|
|
|
|
|
|
{
|
|
|
|
|
|
samePlace = FALSE;
|
|
|
|
|
|
more = FALSE;
|
|
|
|
|
|
less = FALSE;
|
|
|
|
|
|
type = TT_SELECTBASE - 1; /* avoid cycling between types */
|
|
|
|
|
|
optionArgs = &cmd->tx_argv[2];
|
|
|
|
|
|
cmd->tx_argc--;
|
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
optionArgs = &cmd->tx_argv[2];
|
|
|
|
|
|
cmd->tx_argc--;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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])))
|
|
|
|
|
|
optionArgs = &cmd->tx_argv[2];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Check the option for validity. */
|
|
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc == 1)
|
|
|
|
|
|
option = SEL_DEFAULT;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2020-03-19 14:20:50 +01:00
|
|
|
|
char *fileName;
|
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
option = Lookup(optionArgs[0], cmdSelectOption);
|
|
|
|
|
|
if (option < 0 && cmd->tx_argc != 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("\"%s\" isn't a valid select option.\n", cmd->tx_argv[1]);
|
|
|
|
|
|
option = SEL_HELP;
|
|
|
|
|
|
cmd->tx_argc = 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (option < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
option = SEL_DEFAULT;
|
|
|
|
|
|
if (more || less)
|
|
|
|
|
|
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 (cmd->tx_argc > 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
usageError:
|
|
|
|
|
|
TxError("Bad arguments:\n select %s\n",
|
|
|
|
|
|
cmdSelectMsg[option+1]);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!(more || less)) SelectClear();
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
cmdSelectArea(optionArgs[1], less);
|
|
|
|
|
|
else cmdSelectArea("*,label,subcell", less);
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
|
|
|
|
* Select everything under the box, perhaps looking only at
|
|
|
|
|
|
* particular layers, but only if its visible.
|
|
|
|
|
|
*--------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
case SEL_VISIBLE:
|
|
|
|
|
|
if (cmd->tx_argc > 3) goto usageError;
|
|
|
|
|
|
if (!(more || less)) SelectClear();
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
cmdSelectVisible(optionArgs[1], less);
|
|
|
|
|
|
else cmdSelectVisible("*,label,subcell", less);
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
|
|
|
|
* Clear out all of the material in the selection.
|
|
|
|
|
|
*--------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
case SEL_CLEAR:
|
|
|
|
|
|
if ((more) || (less) || (cmd->tx_argc > 2)) goto usageError;
|
|
|
|
|
|
SelectClear();
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
|
|
|
|
* Make a copy of the selection at its present loction but do not
|
|
|
|
|
|
* clear the selection.
|
|
|
|
|
|
*--------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
case SEL_KEEP:
|
|
|
|
|
|
if ((more) || (less) || (cmd->tx_argc > 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) || (cmd->tx_argc != 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) || (cmd->tx_argc > 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) || (cmd->tx_argc > 2)) goto usageError;
|
|
|
|
|
|
SelectFlat();
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
|
|
|
|
* Save the selection as a new Magic cell on disk.
|
|
|
|
|
|
*--------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
case SEL_SAVE:
|
|
|
|
|
|
if (cmd->tx_argc != 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();
|
|
|
|
|
|
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 (cmd->tx_argc > 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 (cmd->tx_argc > 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 (cmd->tx_argc != 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 (cmd->tx_argc > 3) goto usageError;
|
|
|
|
|
|
if (cmd->tx_argc == 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 (cmd->tx_argc > 2) goto usageError;
|
|
|
|
|
|
if (cmd->tx_argc == 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
|
|
|
|
|
|
{
|
|
|
|
|
|
window = CmdGetRootPoint((Point *) NULL, &scx.scx_area);
|
|
|
|
|
|
}
|
|
|
|
|
|
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 (!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 (cmd->tx_argc > 3) 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.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2019-05-03 15:14:25 +02:00
|
|
|
|
if ((cmd->tx_argc == 3) && (optionArgs == &cmd->tx_argv[2]) &&
|
|
|
|
|
|
(more == FALSE) && (less == FALSE))
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
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 (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
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. */
|
|
|
|
|
|
|
|
|
|
|
|
if (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
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-12-15 02:21:05 +01:00
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
DBEraseLabelsByContent(cellDef, &label->lab_rect, -1, label->lab_text);
|
|
|
|
|
|
DBWLabelChanged(cellDef, newlab, DBW_ALLWINDOWS);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-12-15 02:21:05 +01:00
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
DBUndoEraseLabel(cellDef, label);
|
|
|
|
|
|
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
|
|
|
|
|
|
label->lab_rotate = *value;
|
|
|
|
|
|
DBFontLabelSetBBox(label);
|
|
|
|
|
|
DBUndoPutLabel(cellDef, label);
|
|
|
|
|
|
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-12-15 02:21:05 +01:00
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
DBUndoEraseLabel(cellDef, label);
|
|
|
|
|
|
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
|
|
|
|
|
|
label->lab_size = *value;
|
|
|
|
|
|
DBFontLabelSetBBox(label);
|
|
|
|
|
|
DBUndoPutLabel(cellDef, label);
|
|
|
|
|
|
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-12-15 02:21:05 +01:00
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
DBUndoEraseLabel(cellDef, label);
|
|
|
|
|
|
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
|
|
|
|
|
|
label->lab_just = *value;
|
|
|
|
|
|
DBFontLabelSetBBox(label);
|
|
|
|
|
|
DBUndoPutLabel(cellDef, label);
|
|
|
|
|
|
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-12-15 02:21:05 +01:00
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-12-15 02:21:05 +01:00
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-12-15 02:21:05 +01:00
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
DBUndoEraseLabel(cellDef, label);
|
|
|
|
|
|
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
|
|
|
|
|
|
label->lab_offset = *point;
|
|
|
|
|
|
DBFontLabelSetBBox(label);
|
|
|
|
|
|
DBUndoPutLabel(cellDef, label);
|
|
|
|
|
|
DBWLabelChanged(cellDef, label, DBW_ALLWINDOWS);
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-12-15 02:21:05 +01:00
|
|
|
|
else
|
2017-04-25 14:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*
|
|
|
|
|
|
* CmdSetLabel --
|
|
|
|
|
|
*
|
|
|
|
|
|
* Implement the "setlabel" command.
|
|
|
|
|
|
* Query or change properties of a (selected) label in the edit cell
|
|
|
|
|
|
*
|
|
|
|
|
|
* Usage:
|
|
|
|
|
|
* setlabel 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.
|
|
|
|
|
|
*
|
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#define SETLABEL_TEXT 0
|
|
|
|
|
|
#define SETLABEL_FONT 1
|
|
|
|
|
|
#define SETLABEL_FONTLIST 2
|
|
|
|
|
|
#define SETLABEL_JUSTIFY 3
|
|
|
|
|
|
#define SETLABEL_SIZE 4
|
|
|
|
|
|
#define SETLABEL_OFFSET 5
|
|
|
|
|
|
#define SETLABEL_ROTATE 6
|
|
|
|
|
|
#define SETLABEL_STICKY 7
|
|
|
|
|
|
#define SETLABEL_LAYER 8
|
|
|
|
|
|
#define SETLABEL_HELP 9
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
CmdSetLabel(w, cmd)
|
|
|
|
|
|
MagWindow *w;
|
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
|
{
|
|
|
|
|
|
int pos = -1, font = -1, size = 0, rotate = 0, flags = 0;
|
|
|
|
|
|
char **msg;
|
|
|
|
|
|
Point offset;
|
|
|
|
|
|
TileType ttype;
|
|
|
|
|
|
int option;
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
|
Tcl_Obj *lobj;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static char *cmdLabelYesNo[] = { "no", "false", "off", "0",
|
|
|
|
|
|
"yes", "true", "on", "1", 0 };
|
|
|
|
|
|
|
|
|
|
|
|
static char *cmdLabelSetOption[] =
|
|
|
|
|
|
{
|
|
|
|
|
|
"text <text> change/get label text",
|
|
|
|
|
|
"font <name> change/get label font",
|
|
|
|
|
|
"fontlist list available fonts",
|
|
|
|
|
|
"justify <position> change/get label justification",
|
|
|
|
|
|
"size <value> change/get label size",
|
|
|
|
|
|
"offset <x> <y> change/get label offset",
|
|
|
|
|
|
"rotate <degrees> change/get label rotation",
|
|
|
|
|
|
"sticky [true|false] change/get sticky property",
|
|
|
|
|
|
"layer <type> change/get layer type",
|
|
|
|
|
|
"help print this help info",
|
|
|
|
|
|
NULL
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc < 2 || cmd->tx_argc > 4)
|
|
|
|
|
|
option = SETLABEL_HELP;
|
|
|
|
|
|
else
|
|
|
|
|
|
option = Lookup(cmd->tx_argv[1], cmdLabelSetOption);
|
|
|
|
|
|
|
|
|
|
|
|
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 (EditCellUse)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
|
|
|
|
|
|
cmdLabelTextFunc, (cmd->tx_argc == 3) ?
|
|
|
|
|
|
(ClientData)cmd->tx_argv[2] : (ClientData)NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SETLABEL_FONT:
|
|
|
|
|
|
if (cmd->tx_argc >= 2 && cmd->tx_argc <= 4)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((cmd->tx_argc == 3) && StrIsInt(cmd->tx_argv[2]))
|
|
|
|
|
|
{
|
|
|
|
|
|
int font = atoi(cmd->tx_argv[2]);
|
|
|
|
|
|
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 ((cmd->tx_argc == 3 || cmd->tx_argc == 4) &&
|
|
|
|
|
|
!StrIsInt(cmd->tx_argv[2]))
|
|
|
|
|
|
{
|
|
|
|
|
|
font = DBNameToFont(cmd->tx_argv[2]);
|
|
|
|
|
|
if (font < -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
float scale = 1.0;
|
|
|
|
|
|
if ((cmd->tx_argc == 4) && StrIsNumeric(cmd->tx_argv[3]))
|
|
|
|
|
|
scale = (float)atof(cmd->tx_argv[3]);
|
|
|
|
|
|
if (DBLoadFont(cmd->tx_argv[2], scale) != 0)
|
|
|
|
|
|
TxError("Error loading font \"%s\"\n", cmd->tx_argv[2]);
|
|
|
|
|
|
font = DBNameToFont(cmd->tx_argv[2]);
|
|
|
|
|
|
if (font < -1) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (EditCellUse)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
|
|
|
|
|
|
cmdLabelFontFunc, (cmd->tx_argc == 3) ?
|
|
|
|
|
|
(ClientData)&font : (ClientData)NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SETLABEL_JUSTIFY:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
pos = GeoNameToPos(cmd->tx_argv[2], FALSE, TRUE);
|
|
|
|
|
|
if (pos < 0) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (EditCellUse)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
|
|
|
|
|
|
cmdLabelJustFunc, (cmd->tx_argc == 3) ?
|
|
|
|
|
|
(ClientData)&pos : (ClientData)NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SETLABEL_SIZE:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (StrIsNumeric(cmd->tx_argv[2]))
|
|
|
|
|
|
size = cmdScaleCoord(w, cmd->tx_argv[2], TRUE, TRUE, 8);
|
|
|
|
|
|
if (size <= 0) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (EditCellUse)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
|
|
|
|
|
|
cmdLabelSizeFunc, (cmd->tx_argc == 3) ?
|
|
|
|
|
|
(ClientData)&size : (ClientData)NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SETLABEL_OFFSET:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
char *yp;
|
|
|
|
|
|
if ((yp = strchr(cmd->tx_argv[2], ' ')) != NULL)
|
|
|
|
|
|
{
|
|
|
|
|
|
offset.p_x = cmdScaleCoord(w, cmd->tx_argv[2], TRUE, TRUE, 8);
|
|
|
|
|
|
offset.p_y = cmdScaleCoord(w, yp, TRUE, FALSE, 8);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("Usage: setlabel offset <x> <y>\n");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (cmd->tx_argc == 4)
|
|
|
|
|
|
{
|
|
|
|
|
|
offset.p_x = cmdScaleCoord(w, cmd->tx_argv[2], TRUE, TRUE, 8);
|
|
|
|
|
|
offset.p_y = cmdScaleCoord(w, cmd->tx_argv[3], TRUE, FALSE, 8);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (EditCellUse)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
|
|
|
|
|
|
cmdLabelOffsetFunc, (cmd->tx_argc != 2) ?
|
|
|
|
|
|
(ClientData)&offset : (ClientData)NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SETLABEL_ROTATE:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (StrIsInt(cmd->tx_argv[2]))
|
|
|
|
|
|
rotate = atoi(cmd->tx_argv[2]);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (EditCellUse)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
|
|
|
|
|
|
cmdLabelRotateFunc, (cmd->tx_argc == 3) ?
|
|
|
|
|
|
(ClientData)&rotate : (ClientData)NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SETLABEL_STICKY:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
option = Lookup(cmd->tx_argv[2], cmdLabelYesNo);
|
|
|
|
|
|
if (option < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
TxError("Unknown sticky option \"%s\"\n", cmd->tx_argv[2]);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
flags = (option <= 3) ? 0 : LABEL_STICKY;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (EditCellUse)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
|
|
|
|
|
|
cmdLabelStickyFunc, (cmd->tx_argc == 3) ?
|
|
|
|
|
|
(ClientData)&flags : (ClientData)NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SETLABEL_LAYER:
|
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!strcasecmp(cmd->tx_argv[2], "default"))
|
|
|
|
|
|
ttype = -1;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ttype = DBTechNoisyNameType(cmd->tx_argv[2]);
|
|
|
|
|
|
if (ttype < 0) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (EditCellUse)
|
|
|
|
|
|
{
|
|
|
|
|
|
SelEnumLabels(&DBAllTypeBits, TRUE, (bool *)NULL,
|
|
|
|
|
|
cmdLabelLayerFunc, (cmd->tx_argc == 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[1]);
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
if ((direction = GeoNameToPos(cmd->tx_argv[1], FALSE, TRUE)) < 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
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 ((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;
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SelectStretch(xdelta, ydelta);
|
|
|
|
|
|
}
|