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); ToolMoveCorner(tcorner, &cmd->tx_p, TRUE, rootBoxDef);
break; 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; return;
} }
else if (DBWSnapToGrid != DBW_SNAP_USER) else if (DBWSnapToGrid != DBW_SNAP_USER)

View File

@ -2711,6 +2711,14 @@ CmdCopy(w, cmd)
return; 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: copyToPoint:
if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef)) if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
{ {

View File

@ -598,7 +598,7 @@ badusage:
* EditCellUse->cu_def. * EditCellUse->cu_def.
* *
* Usage: * Usage:
* erase [layers | cursor] * erase [layers | cursor | pick x y]
* *
* Results: * Results:
* None. * None.
@ -630,14 +630,23 @@ CmdErase(w, cmd)
windCheckOnlyWindow(&w, DBWclientID); windCheckOnlyWindow(&w, DBWclientID);
if (w == (MagWindow *) NULL) return; 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) 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; return;
} }
if (!ToolGetEditBox(&editRect)) return; if (!ToolGetEditBox(&editRect)) return;
if (EditCellUse == NULL) if (EditCellUse == NULL)
{ {
TxError("No cell def being edited!\n"); TxError("No cell def being edited!\n");
@ -654,7 +663,16 @@ CmdErase(w, cmd)
(void) CmdParseLayers("*,label", &mask); (void) CmdParseLayers("*,label", &mask);
else if (!strncmp(cmd->tx_argv[1], "cursor", 6)) 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; return;
} }
else if (!CmdParseLayers(cmd->tx_argv[1], &mask)) else if (!CmdParseLayers(cmd->tx_argv[1], &mask))

View File

@ -815,6 +815,14 @@ CmdMove(w, cmd)
return; 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: moveToPoint:
if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef)) if (!ToolGetBox(&rootDef, &rootBox) || (rootDef != SelectRootDef))
{ {
@ -871,7 +879,7 @@ moveToPoint:
* Paint the specified layers underneath the box in EditCellUse->cu_def. * Paint the specified layers underneath the box in EditCellUse->cu_def.
* *
* Usage: * Usage:
* paint <layers> | cursor * paint <layers> | cursor | pick x y
* *
* Results: * Results:
* None. * None.
@ -897,15 +905,34 @@ CmdPaint(w, cmd)
return; 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) 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; return;
} }
if (!strncmp(cmd->tx_argv[1], "cursor", 6)) 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; return;
} }
else if (!CmdParseLayers(cmd->tx_argv[1], &mask)) else if (!CmdParseLayers(cmd->tx_argv[1], &mask))
@ -957,10 +984,11 @@ CmdPaint(w, cmd)
*/ */
void void
CmdPaintEraseButton(w, butPoint, isPaint) CmdPaintEraseButton(w, refPoint, isPaint, isScreen)
MagWindow *w; 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 isPaint; /* True for paint, False for erase. */
bool isScreen; /* True for screen coordinates, False for root */
{ {
Rect rootRect, editRect, areaReturn; Rect rootRect, editRect, areaReturn;
TileTypeBitMask mask; TileTypeBitMask mask;
@ -974,7 +1002,15 @@ CmdPaintEraseButton(w, butPoint, isPaint)
} }
crec = (DBWclientRec *) w->w_clientData; 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, DBSeeTypesAll(((CellUse *)w->w_surfaceID), &rootRect,
crec->dbw_bitmask, &mask); crec->dbw_bitmask, &mask);

View File

@ -1311,7 +1311,21 @@ Okay:
} }
else else
{ {
char *aptr;
int i;
window = CmdGetRootPoint((Point *) NULL, &scx.scx_area); 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; if (window == NULL) return;
scx.scx_use = (CellUse *) window->w_surfaceID; scx.scx_use = (CellUse *) window->w_surfaceID;

View File

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

View File

@ -39,11 +39,12 @@ Erase paint from the layout inside the bounds of the cursor box.
<H3>Usage:</H3> <H3>Usage:</H3>
<BLOCKQUOTE> <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> <BLOCKQUOTE>
where <I>layers</I> is a comma-separated list of layer types 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, 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>
</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 of the (X11) cursor and erases these from the area of the cursor
box. <P> 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 Note that when applied to contact types, "<B>erase</B>" will erase
the entire contact, including the top and bottom metal types the entire contact, including the top and bottom metal types
comprising the contact. To remove the contact cuts without affecting 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> <H3>Usage:</H3>
<BLOCKQUOTE> <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> <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>
</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 position of the (X11) cursor and fills the cursor box with these
types. However, when no material (e.g., "space") is present under types. However, when no material (e.g., "space") is present under
the cursor, then all material and labels are erased from the 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> </BLOCKQUOTE>
<H3>Implementation Notes:</H3> <H3>Implementation Notes:</H3>

View File

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

View File

@ -856,31 +856,16 @@ txLogCommand(cmd)
return; return;
else if (!strcmp(postns, "*bypass")) else if (!strcmp(postns, "*bypass"))
return; 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; else if (!strcmp(postns, "setpoint")) return;
fprintf(txLogFile, "%s%s", pfix, cmd->tx_argv[0]); fprintf(txLogFile, "%s%s", pfix, cmd->tx_argv[0]);
for (i = 1; i < cmd->tx_argc; i++) 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"); fprintf(txLogFile, "\n");
} }
@ -1300,6 +1285,45 @@ txGetFileCommand(f, queue)
TxParseString(linep, queue, (TxInputEvent *) NULL); 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 #ifdef MAGIC_WRAPPER
/* /*

View File

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