Extended the "paint" and "erase" commands to accept an option

"pick x y" which acts like "cursor", but operates on a database
coordinate instead of a pointer coordinate.  Made a few other
corrections to the command logging code so that it produces
valid output when the log file is sourced.
This commit is contained in:
Tim Edwards 2023-07-08 17:34:00 -04:00
parent 3890181ebe
commit 7a4a867d6e
11 changed files with 157 additions and 38 deletions

View File

@ -797,6 +797,13 @@ CmdBox(w, cmd)
ToolMoveCorner(tcorner, &cmd->tx_p, TRUE, rootBoxDef);
break;
}
/* Recast command as "box values" for logging purposes */
ToolGetBox(&rootBoxDef, &rootBox);
sprintf(cmd->tx_argstring, "box values %di %di %di %di",
rootBox.r_xbot, rootBox.r_ybot,
rootBox.r_xtop, rootBox.r_ytop);
TxRebuildCommand(cmd);
return;
}
else if (DBWSnapToGrid != DBW_SNAP_USER)

View File

@ -2711,6 +2711,14 @@ CmdCopy(w, cmd)
return;
}
/* Recast the command as "copy to x y" so that it no longer
* depends on the pointer position, for command logging.
*/
GeoTransPoint(&RootToEditTransform, &rootPoint, &editPoint);
sprintf(cmd->tx_argstring, "copy to %di %di\n", editPoint.p_x,
editPoint.p_y);
TxRebuildCommand(cmd);
copyToPoint:
if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
{

View File

@ -598,7 +598,7 @@ badusage:
* EditCellUse->cu_def.
*
* Usage:
* erase [layers | cursor]
* erase [layers | cursor | pick x y]
*
* Results:
* None.
@ -630,14 +630,23 @@ CmdErase(w, cmd)
windCheckOnlyWindow(&w, DBWclientID);
if (w == (MagWindow *) NULL) return;
if ((cmd->tx_argc == 4) && !strcmp(cmd->tx_argv[1], "pick"))
{
Point editPoint, rootPoint;
editPoint.p_x = cmdParseCoord(w, cmd->tx_argv[2], FALSE, TRUE);
editPoint.p_y = cmdParseCoord(w, cmd->tx_argv[3], FALSE, FALSE);
GeoTransPoint(&EditToRootTransform, &editPoint, &rootPoint);
CmdPaintEraseButton(w, &rootPoint, FALSE, FALSE);
return;
}
if (cmd->tx_argc > 2)
{
TxError("Usage: %s [<layers> | cursor]\n", cmd->tx_argv[0]);
TxError("Usage: %s [<layers> | cursor | pick x y]\n", cmd->tx_argv[0]);
return;
}
if (!ToolGetEditBox(&editRect)) return;
if (EditCellUse == NULL)
{
TxError("No cell def being edited!\n");
@ -654,7 +663,16 @@ CmdErase(w, cmd)
(void) CmdParseLayers("*,label", &mask);
else if (!strncmp(cmd->tx_argv[1], "cursor", 6))
{
CmdPaintEraseButton(w, &cmd->tx_p, FALSE);
Point editPoint, rootPoint;
CmdPaintEraseButton(w, &cmd->tx_p, FALSE, TRUE);
/* Recast the command as "erase pick x y" for logging purposes */
CmdGetRootPoint(&rootPoint, (Rect *)NULL);
GeoTransPoint(&RootToEditTransform, &rootPoint, &editPoint);
sprintf(cmd->tx_argstring, "erase pick %di %di", editPoint.p_x,
editPoint.p_y);
TxRebuildCommand(cmd);
return;
}
else if (!CmdParseLayers(cmd->tx_argv[1], &mask))

View File

@ -815,6 +815,14 @@ CmdMove(w, cmd)
return;
}
/* Recast the command as "move to x y" so that it no longer
* depends on the pointer position, for command logging.
*/
GeoTransPoint(&RootToEditTransform, &rootPoint, &editPoint);
sprintf(cmd->tx_argstring, "move to %di %di\n", editPoint.p_x,
editPoint.p_y);
TxRebuildCommand(cmd);
moveToPoint:
if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
{
@ -871,7 +879,7 @@ moveToPoint:
* Paint the specified layers underneath the box in EditCellUse->cu_def.
*
* Usage:
* paint <layers> | cursor
* paint <layers> | cursor | pick x y
*
* Results:
* None.
@ -897,15 +905,34 @@ CmdPaint(w, cmd)
return;
}
if ((cmd->tx_argc == 4) && !strcmp(cmd->tx_argv[1], "pick"))
{
Point editPoint, rootPoint;
editPoint.p_x = cmdParseCoord(w, cmd->tx_argv[2], FALSE, TRUE);
editPoint.p_y = cmdParseCoord(w, cmd->tx_argv[3], FALSE, FALSE);
GeoTransPoint(&EditToRootTransform, &editPoint, &rootPoint);
CmdPaintEraseButton(w, &rootPoint, TRUE, FALSE);
return;
}
if (cmd->tx_argc != 2)
{
TxError("Usage: %s <layers> | cursor\n", cmd->tx_argv[0]);
TxError("Usage: %s <layers> | cursor | pick x y\n", cmd->tx_argv[0]);
return;
}
if (!strncmp(cmd->tx_argv[1], "cursor", 6))
{
CmdPaintEraseButton(w, &cmd->tx_p, TRUE);
Point editPoint, rootPoint;
CmdPaintEraseButton(w, &cmd->tx_p, TRUE, TRUE);
/* Recast the command as "paint pick x y" for logging purposes */
CmdGetRootPoint(&rootPoint, (Rect *)NULL);
GeoTransPoint(&RootToEditTransform, &rootPoint, &editPoint);
sprintf(cmd->tx_argstring, "paint pick %di %di", editPoint.p_x,
editPoint.p_y);
TxRebuildCommand(cmd);
return;
}
else if (!CmdParseLayers(cmd->tx_argv[1], &mask))
@ -957,10 +984,11 @@ CmdPaint(w, cmd)
*/
void
CmdPaintEraseButton(w, butPoint, isPaint)
CmdPaintEraseButton(w, refPoint, isPaint, isScreen)
MagWindow *w;
Point *butPoint; /* Screen location at which button was raised */
Point *refPoint; /* Screen location at which button was raised */
bool isPaint; /* True for paint, False for erase. */
bool isScreen; /* True for screen coordinates, False for root */
{
Rect rootRect, editRect, areaReturn;
TileTypeBitMask mask;
@ -974,7 +1002,15 @@ CmdPaintEraseButton(w, butPoint, isPaint)
}
crec = (DBWclientRec *) w->w_clientData;
WindPointToSurface(w, butPoint, (Point *) NULL, &rootRect);
if (isScreen)
WindPointToSurface(w, refPoint, (Point *) NULL, &rootRect);
else
{
rootRect.r_ll.p_x = refPoint->p_x;
rootRect.r_ll.p_y = refPoint->p_y;
rootRect.r_ur.p_x = refPoint->p_x + 1;
rootRect.r_ur.p_y = refPoint->p_y + 1;
}
DBSeeTypesAll(((CellUse *)w->w_surfaceID), &rootRect,
crec->dbw_bitmask, &mask);

View File

@ -1311,7 +1311,21 @@ Okay:
}
else
{
char *aptr;
int i;
window = CmdGetRootPoint((Point *) NULL, &scx.scx_area);
/* Recast command with "at x y" at the end for logging */
for (i = 0; i < cmd->tx_argc; i++)
{
aptr = cmd->tx_argv[i] + strlen(cmd->tx_argv[i]);
*aptr = ' ';
}
sprintf(aptr + 1, "at %di %di", scx.scx_area.r_xbot,
scx.scx_area.r_ybot);
TxRebuildCommand(cmd);
}
if (window == NULL) return;
scx.scx_use = (CellUse *) window->w_surfaceID;

View File

@ -351,7 +351,7 @@ DBWBoxHandler(w, cmd)
if (button == TX_MIDDLE_BUTTON)
{
if (cmd->tx_buttonAction == TX_BUTTON_DOWN)
CmdPaintEraseButton(w, &cmd->tx_p, TRUE);
CmdPaintEraseButton(w, &cmd->tx_p, TRUE, TRUE);
return;
}

View File

@ -39,11 +39,12 @@ Erase paint from the layout inside the bounds of the cursor box.
<H3>Usage:</H3>
<BLOCKQUOTE>
<B>erase</B> [<I>layers</I>|<B>cursor</B>] <BR><BR>
<B>erase</B> [<I>layers</I>|<B>cursor</B>|<B>pick</B> <I>x y</I>] <BR><BR>
<BLOCKQUOTE>
where <I>layers</I> is a comma-separated list of layer types
to be erased, or <B>*</B> to indicate all paint but not labels,
or <B>$</B> to indicate both paint and labels.
or <B>$</B> to indicate both paint and labels; and where <I>x</I>
and <I>y</I> are values in the layout coordinate system.
</BLOCKQUOTE>
</BLOCKQUOTE>
@ -62,6 +63,11 @@ Erase paint from the layout inside the bounds of the cursor box.
of the (X11) cursor and erases these from the area of the cursor
box. <P>
The option "<B>erase pick</B> <I>x y</I>" works like "<B>erase
cursor</B>", but the type of material to erase is selected from
the given position in layout coordinates instead of a pointer
position. <P>
Note that when applied to contact types, "<B>erase</B>" will erase
the entire contact, including the top and bottom metal types
comprising the contact. To remove the contact cuts without affecting

View File

@ -33,9 +33,10 @@ Paint mask information into the current edit cell
<H3>Usage:</H3>
<BLOCKQUOTE>
<B>paint</B> <I>layers</I>|<B>cursor</B> <BR><BR>
<B>paint</B> <I>layers</I>|<B>cursor</B>|<B>pick</B> <I>x y</I> <BR><BR>
<BLOCKQUOTE>
where <I>layers</I> is a comma-separated list of types to paint.
where <I>layers</I> is a comma-separated list of types to paint,
and <I>x y</I> are coordinates in the layout coordinate system.
</BLOCKQUOTE>
</BLOCKQUOTE>
@ -52,7 +53,11 @@ Paint mask information into the current edit cell
position of the (X11) cursor and fills the cursor box with these
types. However, when no material (e.g., "space") is present under
the cursor, then all material and labels are erased from the
area of the cursor box.
area of the cursor box. <P>
The "<B>paint pick</B> <I>x y</I>" option works like "<B>paint
cursor</B>", but the type of paint is selected from the given
position in layout coordinates instead of a pointer position.
</BLOCKQUOTE>
<H3>Implementation Notes:</H3>

View File

@ -21,7 +21,7 @@ proc magic::suspendall {} {
set framename $window
}
if {[incr Winopts(${framename},suspend)] == 1} {
$window update suspend
$window updatedisplay suspend
}
}
}
@ -42,7 +42,7 @@ proc magic::resumeall {} {
incr Winopts($framename,suspend) -1
if { $Winopts(${framename},suspend) <= 0 } {
unset Winopts(${framename},suspend)
$window update resume
$window updatedisplay resume
}
}
}

View File

@ -856,31 +856,16 @@ txLogCommand(cmd)
return;
else if (!strcmp(postns, "*bypass"))
return;
/* Commands ending in "cursor" should be preceeded by a set point */
/* to indicate where the pointer was at the time of the command. */
if (!strcmp(cmd->tx_argv[cmd->tx_argc - 1], "cursor"))
{
if (cmd->tx_wid >= 0)
{
/* Command has a window associated with it. */
fprintf(txLogFile, "%ssetpoint %d %d %d\n",
pfix, cmd->tx_p.p_x, cmd->tx_p.p_y, cmd->tx_wid);
}
else
{
/* No window associated with the command. */
fprintf(txLogFile, "%ssetpoint %d %d\n",
pfix, cmd->tx_p.p_x, cmd->tx_p.p_y);
}
}
else if (!strcmp(postns, "setpoint")) return;
fprintf(txLogFile, "%s%s", pfix, cmd->tx_argv[0]);
for (i = 1; i < cmd->tx_argc; i++)
{
fprintf(txLogFile, " %s", cmd->tx_argv[i]);
bool needQuotes = (strchr(cmd->tx_argv[i], ' ') == NULL) ? FALSE : TRUE;
fprintf(txLogFile, " ");
if (needQuotes) fprintf(txLogFile, "\"");
fprintf(txLogFile, "%s", cmd->tx_argv[i]);
if (needQuotes) fprintf(txLogFile, "\"");
}
fprintf(txLogFile, "\n");
}
@ -1300,6 +1285,45 @@ txGetFileCommand(f, queue)
TxParseString(linep, queue, (TxInputEvent *) NULL);
}
/*
* ----------------------------------------------------------------------------
* TxRebuildCommand:
*
* Rebuild the arguments of a TxCommand structure. This assumes
* that a routine has rewritten the tx_argstring record with a
* modified command. Tokenize the tx_argstring and update the
* tx_argc count and tx_argv pointers.
*
* The purpose of this routine is to allow some command callbacks
* to change the command from one implying the use of pointer
* coordinates to an equivalent command that uses database units,
* for the purpose of logging the command.
*
* ----------------------------------------------------------------------------
*/
void
TxRebuildCommand(TxCommand *cmd)
{
char *cptr, *tptr, c;
cmd->tx_argc = 0;
tptr = cptr = cmd->tx_argstring;
do
{
c = *cptr;
if ((c == ' ') || (c == '\0'))
{
cmd->tx_argv[cmd->tx_argc] = tptr;
cmd->tx_argc++;
*cptr = '\0';
tptr = cptr + 1;
}
cptr++;
}
while (c != '\0');
}
#ifdef MAGIC_WRAPPER
/*

View File

@ -158,6 +158,7 @@ extern TxCommand *TxNewCommand();
extern void TxFreeCommand();
extern void TxParseString();
extern void TxDispatch();
extern void TxRebuildCommand();
extern int TxCommandNumber; /* Serial number of current command. */
#ifdef MAGIC_WRAPPER