Files
magic/windows/windCmdAM.c
T
R. Timothy Edwards ae6d26578e Fixed issues with the "macro" command that have existed since the
"tool" command was changed to be overridden by a Tcl command.  Due
to macro clients being registered with the tool name instead of
just "layout", the "macro" command with no arguments or with a
window client argument was just broken.  In the process of fixing
this, I realized that there was a conflict between the use of
"netlist" as a window name and also as the name of a tool, so I
changed the tool name to "nettool", a change which should be
transparent to the end user.  Otherwise, "macro netlist" returns
the key bindings for the window, and the only way to get the key
bindings for the tool is to make the tool active and then use
"macro" without arguments.  One remaining issue is that there is
no syntax error that will cause the list of valid windows and
tools to be printed.  Probably "macro help" should print usage
information instead of acting like "macro" with no arguments.
2026-04-13 12:26:42 -04:00

1385 lines
31 KiB
C

/* windCmdAM.c -
*
* This file contains Magic command routines for those commands
* that are valid in all windows.
*
* *********************************************************************
* * 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/windows/windCmdAM.c,v 1.2 2008/12/11 04:20:15 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/types.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <errno.h>
#include <math.h> /* for round() function */
#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "utils/malloc.h"
#include "utils/runstats.h"
#include "utils/macros.h"
#include "utils/signals.h"
#include "graphics/graphics.h"
#include "utils/styles.h"
#include "textio/txcommands.h"
#include "graphics/glyphs.h"
#include "windows/windInt.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "dbwind/dbwind.h"
#include "utils/utils.h"
#include "cif/cif.h"
/* Forward declarations */
void windDoMacro();
/*
* ----------------------------------------------------------------------------
*
* windBorderCmd --
*
* Change the flag which says whether new windows will have a border.
*
* Usage:
* windborder [on|off]
*
* Results:
* None.
*
* Side effects:
* A flag is changed.
* In Tcl, if no options are presented, the border status is returned
* in the command exit status.
*
* ----------------------------------------------------------------------------
*/
void
windBorderCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int place;
bool value;
static const char * const onoff[] = {"on", "off", 0};
static const bool truth[] = {TRUE, FALSE};
if (cmd->tx_argc > 2) goto usage;
else if (cmd->tx_argc == 1)
{
if (w == (MagWindow *)NULL)
{
TxError("No window specified for caption command\n");
goto usage;
}
value = (w->w_flags & WIND_BORDER) ? 0 : 1;
#ifdef MAGIC_WRAPPER
/* this cast to remove const is ok, TCL_STATIC conveys the storage handling of this string */
Tcl_SetResult(magicinterp, (char *)onoff[value], TCL_STATIC);
#else
TxPrintf("Window border is %s\n", onoff[value]);
#endif
return;
}
place = Lookup(cmd->tx_argv[1], onoff);
if (place < 0)
goto usage;
if (truth[place])
{
WindDefaultFlags |= WIND_BORDER;
TxPrintf("New windows will have a border.\n");
}
else {
WindDefaultFlags &= ~WIND_BORDER;
TxPrintf("New windows will not have a border.\n");
}
return;
usage:
TxError("Usage: %s [on|off]\n", cmd->tx_argv[0]);
return;
}
/*
* ----------------------------------------------------------------------------
*
* windCaptionCmd --
*
* Change the flag which says whether new windows will have a title caption.
*
* Usage:
* windcaption [on|off]
*
* Results:
* None.
*
* Side effects:
* A flag is changed.
* In Tcl, if no options are presented, the window title caption is
* returned in the command status.
*
* ----------------------------------------------------------------------------
*/
void
windCaptionCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int place;
Rect ts;
static const char * const onoff[] = {"on", "off", 0};
static const bool truth[] = {TRUE, FALSE};
if (cmd->tx_argc > 2) goto usage;
else if (cmd->tx_argc == 1)
{
if (w == (MagWindow *)NULL)
{
TxError("No window specified for caption command\n");
goto usage;
}
#ifdef MAGIC_WRAPPER
Tcl_SetResult(magicinterp, w->w_caption, TCL_STATIC);
#else
TxPrintf("Window caption is \"%s\"\n", w->w_caption);
#endif
return;
}
place = Lookup(cmd->tx_argv[1], onoff);
if (place < 0)
goto usage;
if (truth[place])
{
WindDefaultFlags |= WIND_CAPTION;
TxPrintf("New windows will have a title caption.\n");
}
else {
WindDefaultFlags &= ~WIND_CAPTION;
TxPrintf("New windows will not have a title caption.\n");
}
return;
usage:
TxError("Usage: %s [on|off]\n", cmd->tx_argv[0]);
return;
}
/*
* ----------------------------------------------------------------------------
*
* windCenterCmd --
*
* Implement the "center" command.
* Move a window's view to center the point underneath the cursor, or to
* the specified coordinate (in surface units).
*
* Usage:
* center [x y]
* center horizontal|vertical f
*
* Results:
* None.
*
* Side effects:
* The view in the window underneath the cursor is changed
* to center the point underneath the cursor.
*
* ----------------------------------------------------------------------------
*/
void
windCenterCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Point rootPoint;
Rect newArea, oldArea;
if (w == NULL)
{
TxError("Point to a window first.\n");
return;
}
if (cmd->tx_argc == 1)
{
if ((w->w_flags & WIND_SCROLLABLE) == 0)
{
TxError("Sorry, can't scroll this window.\n");
return;
}
WindPointToSurface(w, &cmd->tx_p, &rootPoint, (Rect *) NULL);
}
else if (cmd->tx_argc == 3)
{
if ((w->w_flags & WIND_SCROLLABLE) == 0)
{
TxError("Sorry, can't scroll this window.\n");
return;
}
if (cmd->tx_argv[1][0] == 'h' || cmd->tx_argv[1][0] == 'v')
{
double frac;
if (!StrIsNumeric(cmd->tx_argv[2]))
{
TxError("Must specify a fractional value.\n");
return;
}
frac = atof(cmd->tx_argv[2]);
if (cmd->tx_argv[1][0] == 'h')
{
rootPoint.p_y = 0;
rootPoint.p_x = w->w_bbox->r_xbot + frac *
(w->w_bbox->r_xtop - w->w_bbox->r_xbot) -
(w->w_surfaceArea.r_xtop + w->w_surfaceArea.r_xbot)/2;
}
else
{
rootPoint.p_x = 0;
rootPoint.p_y = w->w_bbox->r_ybot + frac *
(w->w_bbox->r_ytop - w->w_bbox->r_ybot) -
(w->w_surfaceArea.r_ytop + w->w_surfaceArea.r_ybot)/2;
}
WindScroll(w, &rootPoint, (Point *)NULL);
return;
}
else
{
if (!StrIsInt(cmd->tx_argv[1]) || !StrIsInt(cmd->tx_argv[2]))
{
TxError("Coordinates must be integer values\n");
return;
}
rootPoint.p_x = atoi(cmd->tx_argv[1]);
rootPoint.p_y = atoi(cmd->tx_argv[2]);
}
}
else
{
TxError("Usage: center [x y]\n");
TxError(" center horizontal|vertical f\n");
return;
}
oldArea = w->w_surfaceArea;
newArea.r_xbot = rootPoint.p_x - (oldArea.r_xtop - oldArea.r_xbot)/2;
newArea.r_xtop = newArea.r_xbot - oldArea.r_xbot + oldArea.r_xtop;
newArea.r_ybot = rootPoint.p_y - (oldArea.r_ytop - oldArea.r_ybot)/2;
newArea.r_ytop = newArea.r_ybot - oldArea.r_ybot + oldArea.r_ytop;
WindMove(w, &newArea);
}
/*
* ----------------------------------------------------------------------------
* windCloseCmd --
*
* Close the window that is pointed at.
*
* Results:
* None.
*
* Side effects:
* The window is closed, and the client is notified. The client may
* refuse to have the window closed, in which case nothing happens.
* ----------------------------------------------------------------------------
*/
void
windCloseCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if ((cmd->tx_argc == 2) && GrWindowNamePtr)
{
char *mwname;
for (w = windTopWindow; w != (MagWindow *)NULL; w = w->w_nextWindow)
{
mwname = (*GrWindowNamePtr)(w);
if (!strcmp(mwname, cmd->tx_argv[1]))
break;
}
if (w == NULL)
{
TxError("Window named %s cannot be found\n", cmd->tx_argv[1]);
return;
}
}
if (w == (MagWindow *) NULL)
{
TxError("Point to a window first\n");
return;
}
if (!WindDelete(w))
{
TxError("Unable to close that window\n");
return;
}
}
#ifdef MAGIC_WRAPPER
/*
* ----------------------------------------------------------------------------
* windBypassCmd --
*
* Run a magic command independently of the command line. That is,
* if a command is being typed on the command line, the input
* redirection will not be reset by the execution of this command.
* To avoid having such commands interfere with the selection
* mechanism, save and restore the command count.
*
* Results:
* None.
*
* Side effects:
*
*
* ----------------------------------------------------------------------------
*/
void
windBypassCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int saveCount;
if (cmd->tx_argc == 1)
{
TxError("Usage: *bypass <command>\n");
return;
}
/* Dispatch the referenced command */
saveCount = TxCommandNumber;
TxTclDispatch((ClientData)w, cmd->tx_argc - 1, cmd->tx_argv + 1, FALSE);
TxCommandNumber = saveCount;
if (TxInputRedirect == TX_INPUT_PENDING_RESET)
TxInputRedirect = TX_INPUT_REDIRECTED;
}
#endif /* MAGIC_WRAPPER */
/*
* ----------------------------------------------------------------------------
* windCrashCmd --
*
* Generate a core dump.
*
* Results:
* None.
*
* Side effects:
* Dumps core by calling niceabort().
*
* ----------------------------------------------------------------------------
*/
void
windCrashCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if (cmd->tx_argc != 1)
{
TxError("Usage: *crash\n");
return;
}
TxPrintf("OK -- crashing...\n");
TxFlush();
niceabort();
}
/*
* ----------------------------------------------------------------------------
* windCursorCmd --
*
* Report the cursor position in Magic (internal) coordinates
* If an argument of a number is given, then the cursor icon
* is changed to the glyph of that number.
*
* Results:
* None.
*
* Side effects:
* Prints coordinates (non-Tcl version)
* Return value set to the cursor position as a list (Tcl version)
*
* NOTE: "box position {*}[cursor]" will produce the wrong result if
* "units" have been left as default, because "cursor" will generate
* internal values and "box position" will expect lambda values. Use
* "box move bl cursor" instead.
* ----------------------------------------------------------------------------
*/
#define CURSOR_INTERNAL 0
#define CURSOR_LAMBDA 1
#define CURSOR_USER 2
#define CURSOR_GRID 3
#define CURSOR_MICRONS 4
#define CURSOR_WINDOW 5
#define CURSOR_SCREEN 6
void
windCursorCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Point p_in, p_out;
int resulttype, saveunits, idx;
double cursx, cursy, oscale;
char *dispx, *dispy;
DBWclientRec *crec;
#ifdef MAGIC_WRAPPER
Tcl_Obj *listxy;
#endif
static const char * const cmdCursorOption[] =
{ "internal", "lambda", "user", "grid", "microns", "window", "screen",
"units", 0 };
/* The original behavior was to use internal
* units by default. This remains the case
* unless units are set with the "units"
* command, in which case units follow the
* specified units.
*/
if (DBWUnits == DBW_UNITS_DEFAULT)
resulttype = DBW_UNITS_INTERNAL;
else
resulttype = DBWUnits;
if (cmd->tx_argc == 2)
{
if (StrIsInt(cmd->tx_argv[1]))
{
if (GrSetCursorPtr != NULL)
(*GrSetCursorPtr)(atoi(cmd->tx_argv[1]));
return;
}
else {
idx = Lookup(cmd->tx_argv[1], cmdCursorOption);
switch (idx)
{
case CURSOR_INTERNAL:
resulttype = DBW_UNITS_INTERNAL;
break;
case CURSOR_LAMBDA:
resulttype = DBW_UNITS_LAMBDA;
break;
case CURSOR_USER:
case CURSOR_GRID:
resulttype = DBW_UNITS_USER;
break;
case CURSOR_MICRONS:
resulttype = DBW_UNITS_MICRONS;
break;
case CURSOR_WINDOW:
resulttype = -1; // Use this value for "window"
break;
case CURSOR_SCREEN:
resulttype = -2; // Use this value for "screen"
break;
default:
TxError("Usage: cursor glyphnum\n");
TxError(" (or): cursor [internal | lambda | microns | user"
" | window | units]\n");
return;
}
}
}
if (GrGetCursorPosPtr == NULL)
return;
if (resulttype == -2)
GrGetCursorRootPos(w, &p_in);
else
GrGetCursorPos(w, &p_in);
if (resulttype >= 0)
{
WindPointToSurface(w, &p_in, &p_out, (Rect *)NULL);
/* Snap the cursor position if snap is in effect */
if (DBWSnapToGrid != DBW_UNITS_INTERNAL)
ToolSnapToGrid(w, &p_out, (Rect *)NULL);
}
/* Transform the result to declared units with option "lambda" or "grid" */
saveunits = DBWUnits;
if (resulttype < 0)
{
/* Not really internal units, but that prints integer units verbatim */
DBWUnits = DBW_UNITS_INTERNAL;
cursx = (double)p_in.p_x;
cursy = (double)p_in.p_y;
}
else
{
DBWUnits = resulttype;
cursx = (double)p_out.p_x;
cursy = (double)p_out.p_y;
}
dispx = DBWPrintValue(cursx, w, TRUE);
dispy = DBWPrintValue(cursy, w, FALSE);
DBWUnits = saveunits;
#ifdef MAGIC_WRAPPER
listxy = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewStringObj(dispx, -1));
Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewStringObj(dispy, -1));
Tcl_SetObjResult(magicinterp, listxy);
#else
TxPrintf("%s %s\n", dispx, dispy);
#endif
}
/*
* ----------------------------------------------------------------------------
* windDebugCmd --
*
* Change to a new debugging mode.
*
* Results:
* None.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
void
windDebugCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if (cmd->tx_argc != 1) goto usage;
windPrintCommands = !windPrintCommands;
TxError("Window command debugging set to %s\n",
(windPrintCommands ? "TRUE" : "FALSE"));
return;
usage:
TxError("Usage: *winddebug\n");
}
/*
* ----------------------------------------------------------------------------
* windDumpCmd --
*
* Dump out debugging info.
*
* Results:
* None.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
void
windDumpCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
(void) windDump();
}
#ifndef MAGIC_WRAPPER
/*
* ----------------------------------------------------------------------------
*
* windEchoCmd --
*
* Echo the arguments
*
* Results:
* None.
*
* Side effects:
* Text may appear on the terminal
*
* ----------------------------------------------------------------------------
*/
void
windEchoCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
int i;
bool newline = TRUE;
for (i = 1; i < cmd->tx_argc; i++)
{
if (i != 1)
TxPrintf(" ");
if ( (i == 1) && (strcmp(cmd->tx_argv[i], "-n") == 0) )
newline = FALSE;
else
TxPrintf("%s", cmd->tx_argv[i]);
}
if (newline)
TxPrintf("\n");
TxFlush();
}
#endif
/*
* ----------------------------------------------------------------------------
*
* windFilesCmd --
*
* Find out what files are currently open.
*
* Usage:
* *files
*
* Side Effects:
* None.
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
void
windFilesCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
#define NUM_FD 20 /* max number of open files per process */
int fd;
struct stat buf;
int unopen, open;
open = unopen = 0;
for (fd = 0; fd < NUM_FD; fd++) {
if (fstat(fd, &buf) != 0) {
if (errno == EBADF)
unopen++;
else
TxError("file descriptor %d: %s\n", fd, strerror(errno));
}
else {
char *type;
switch (buf.st_mode & S_IFMT) {
case S_IFDIR: {type = "directory"; break;}
case S_IFCHR: {type = "character special"; break;}
case S_IFBLK: {type = "block special"; break;}
case S_IFREG: {type = "regular"; break;}
case S_IFLNK: {type = "symbolic link"; break;}
case S_IFSOCK: {type = "socket"; break;}
default: {type = "unknown"; break;}
}
TxError("file descriptor %d: open (type: '%s', inode number %ld)\n",
fd, type, buf.st_ino);
open++;
}
}
TxError("%d open files, %d unopened file descriptors left\n", open, unopen);
}
/*
* ----------------------------------------------------------------------------
*
* windGrowCmd --
*
* Grow a window to full-screen size or back to previous size.
*
* Results:
* None.
*
* Side effects:
* Text may appear on the terminal
*
* ----------------------------------------------------------------------------
*/
void
windGrowCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
if (w == NULL)
{
TxError("Point to a window first.\n");
return;
};
WindFullScreen(w);
}
/*
* ----------------------------------------------------------------------------
* windGrstatsCmd --
*
* Take statistics on the graphics code.
*
* Results:
* None.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
void
windGrstatsCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
char *RunStats(), *rstatp;
static struct tms tlast, tdelta;
int i, style, count;
int us;
extern int GrNumClipBoxes;
int usPerRect, rectsPerSec;
if (cmd->tx_argc < 2 || cmd->tx_argc > 3)
{
TxError("Usage: grstats num [ style ]\n");
return;
}
if (!StrIsInt(cmd->tx_argv[1]) ||
(cmd->tx_argc == 3 && !StrIsInt(cmd->tx_argv[2])))
{
TxError("Count & style must be numeric\n");
return;
}
if (w == (MagWindow *) NULL)
{
TxError("Point to a window first.\n");
return;
}
count = atoi(cmd->tx_argv[1]);
if (cmd->tx_argc == 3)
style = atoi(cmd->tx_argv[2]);
else
style = -1;
WindUpdate();
if (style >= 0)
GrLock(w, TRUE);
(void) RunStats(RS_TINCR, &tlast, &tdelta);
GrNumClipBoxes = 0;
for (i = 0; i < count; i++)
{
if (SigInterruptPending)
break;
if (style < 0)
{
WindAreaChanged(w, (Rect *) NULL);
WindUpdate();
}
else
{
Rect r;
#define GRSIZE 15
#define GRSPACE 20
r.r_xbot = w->w_screenArea.r_xbot - GRSIZE/2;
r.r_ybot = w->w_screenArea.r_ybot - GRSIZE/2;
r.r_xtop = r.r_xbot + GRSIZE - 1;
r.r_ytop = r.r_ybot + GRSIZE - 1;
GrClipBox(&w->w_screenArea, STYLE_ERASEALL);
GrSetStuff(style);
while (r.r_xbot <= w->w_screenArea.r_xtop)
{
while (r.r_ybot <= w->w_screenArea.r_ytop)
{
GrFastBox(&r);
r.r_ybot += GRSPACE;
r.r_ytop += GRSPACE;
}
r.r_xbot += GRSPACE;
r.r_xtop += GRSPACE;
r.r_ybot = w->w_screenArea.r_ybot - GRSIZE/2;
r.r_ytop = r.r_ybot + GRSIZE - 1;
}
}
}
rstatp = RunStats(RS_TINCR, &tlast, &tdelta);
us = tdelta.tms_utime * (1000000 / 60);
usPerRect = us / MAX(1, GrNumClipBoxes);
rectsPerSec = 1000000 / MAX(1, usPerRect);
TxPrintf("[%s]\n%d rectangles, %d uS, %d uS/rectangle, %d rects/sec\n",
rstatp, GrNumClipBoxes, us, usPerRect, rectsPerSec);
if (style >= 0)
GrUnlock(w);
}
/*
* ----------------------------------------------------------------------------
* windHelpCmd --
*
* Just a dummy proc. (Only for this particular, global, client)
* This is just here so that there is an entry in our help table!
*
* Results:
* None.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
void
windHelpCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
ASSERT(FALSE, windHelpCmd);
}
/*
* ----------------------------------------------------------------------------
* windLogCommandsCmd --
*
* Log the commands and button pushes in a file.
*
* Syntax:
* logcommands start <filename> Open and start a new command log file
* logcommands stop End and close a command log file
* logcommands update Refresh display after each log command
* logcommands suspend Suspend logging of commands
* logcommands resume Resume logging of commands
*
* Legacy syntax:
* logcommands <filename> [update] Start a new command log file with updating
* logcommands End and close a command log file
*
* Results:
* None.
*
* Side effects:
* None.
* ----------------------------------------------------------------------------
*/
#define LOG_CMD_START 0 // Create a new log file (default behavior)
#define LOG_CMD_STOP 1 // End a log file and stop logging.
#define LOG_CMD_UPDATE 2 // Update display after every logged command.
#define LOG_CMD_SUSPEND 3 // Suspend command logging.
#define LOG_CMD_RESUME 4 // Resume command logging.
void
windLogCommandsCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
char *fileName = NULL;
unsigned char flags = 0;
int idx = LOG_CMD_STOP;
static const char * const logKeywords[] = {"start", "stop", "update", "suspend", "resume", 0};
if ((cmd->tx_argc < 1) || (cmd->tx_argc > 3)) goto usage;
if (cmd->tx_argc > 1)
{
idx = Lookup(cmd->tx_argv[1], logKeywords);
if (idx < 0) fileName = cmd->tx_argv[1];
}
if (cmd->tx_argc == 3)
{
int i;
if (fileName == NULL)
fileName = cmd->tx_argv[2];
else
{
/* Legacy behavior: Allow "logcommands <filename> update"
* to mean "logcommands start <filename> ; logcommands update"
*/
i = Lookup(cmd->tx_argv[2], logKeywords);
if (i != LOG_CMD_UPDATE) goto usage;
flags = TX_LOG_UPDATE;
idx = LOG_CMD_START;
}
}
switch (idx)
{
case LOG_CMD_START:
if (fileName == NULL) break;
TxLogStart(fileName, w);
if (flags & TX_LOG_UPDATE)
TxLogUpdate();
return;
case LOG_CMD_STOP:
TxLogStop();
return;
case LOG_CMD_UPDATE:
TxLogUpdate();
return;
case LOG_CMD_SUSPEND:
TxLogSuspend();
return;
case LOG_CMD_RESUME:
TxLogResume();
return;
}
usage:
TxError("Usage: %s [start|stop|update|suspend|resume [filename]]\n",
cmd->tx_argv[0]);
}
/*
* ----------------------------------------------------------------------------
*
* windIntMacroCmd --
*
* Define a new interactive macro.
*
* Results:
* None.
*
* Side effects:
* Calls windDoMacro.
*
* ----------------------------------------------------------------------------
*/
void
windIntMacroCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
windDoMacro(w, cmd, TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* windMacroCmd --
*
* Define a new macro.
*
* Results:
* None.
*
* Side effects:
* Calls windDoMacro
*
* ----------------------------------------------------------------------------
*/
void
windMacroCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
windDoMacro(w, cmd, FALSE);
}
/*
* ----------------------------------------------------------------------------
*
* windDoMacro --
*
* Working function for CmdIntMacro and CmdMacro
*
* Results:
* None.
*
* Side effects:
* Causes the macro package to define a new macro.
*
* ----------------------------------------------------------------------------
*/
void
windDoMacro(w, cmd, interactive)
MagWindow *w;
TxCommand *cmd;
bool interactive;
{
char *cp, *cn;
char nulltext[] = "";
char ch;
int ct, argstart, verbose;
bool any, iReturn;
bool do_list = FALSE;
bool do_help = FALSE;
bool do_reverse = FALSE;
char *searchterm = NULL;
char *clientName = NULL;
macrodef *cMacro;
HashTable *clienttable;
HashEntry *h;
HashSearch hs;
WindClient wc;
/* If the first argument is a window name, we attempt to */
/* retrieve a client ID from it. This overrides the actual */
/* window the command was called from, so technically we */
/* can define macros for clients from inside other clients. */
/* The main use, though, is to define macros for a client */
/* from a script, rc file, or command-line. */
/* Default to the layout window if the command has no */
/* associated window. */
argstart = 1;
if (cmd->tx_argc == 1)
wc = DBWclientID; /* Default client */
else if (cmd->tx_argc > 1)
{
wc = WindGetClient(cmd->tx_argv[1], TRUE);
if (wc != NULL)
{
clientName = cmd->tx_argv[1];
argstart++;
}
else
{
/* Check if argument is a known layout button handler */
if (DBWButtonHandlerIndex(cmd->tx_argv[1]) != -1)
{
clientName = cmd->tx_argv[1];
argstart++;
}
}
}
while (cmd->tx_argc > argstart)
{
if (!strcmp(cmd->tx_argv[argstart], "list"))
{
do_list = TRUE;
argstart++;
}
else if (!strcmp(cmd->tx_argv[argstart], "help"))
{
do_help = TRUE;
argstart++;
}
else if (!strcmp(cmd->tx_argv[argstart], "search"))
{
if (cmd->tx_argc > (argstart + 1))
{
argstart++;
searchterm = cmd->tx_argv[argstart];
}
argstart++;
}
else if (!strcmp(cmd->tx_argv[argstart], "-reverse"))
{
do_reverse = TRUE;
argstart++;
}
else if (!strcmp(cmd->tx_argv[argstart], "copy"))
{
if (cmd->tx_argc > (argstart + 1))
{
argstart++;
if (wc == (WindClient)NULL)
{
if (w != NULL)
wc = w->w_client;
else
wc = DBWclientID;
}
MacroCopy(wc, cmd->tx_argv[argstart]);
/* If tool name did not previously exist, then add
* it to the list of known tool names, so that it
* can be found later by the "macro" command.
*/
if (DBWButtonHandlerIndex(cmd->tx_argv[argstart]) == -1)
DBWAddButtonHandler(cmd->tx_argv[argstart],
(const cb_database_buttonhandler_t)NULL,
0, (const char *)NULL);
}
return;
}
else break;
}
/* If client wasn't specified, use window default, else use */
/* DBW client. */
if (wc == (WindClient)NULL)
{
if (w != NULL)
wc = w->w_client;
else
wc = DBWclientID;
if (cmd->tx_argc > (argstart + 1))
{
/* The first argument, if there is one after resolving */
/* all of the optional arugments, should be a key. */
/* If it doesn't look like one, then check if the */
/* next argument looks like a key, which would indicate */
/* an unregistered client as the first argument. A */
/* macro retrieved from an unregistered client returns */
/* nothing but does not generate an error. This allows */
/* the default macro set to declare macros for, e.g., the */
/* wind3d client and fail quietly if magic was compiled */
/* without OpenGL support. */
if (MacroKey(cmd->tx_argv[argstart], &verbose) == 0)
if (MacroKey(cmd->tx_argv[argstart + 1], &verbose) != 0)
return;
}
}
/* If a clientName wasn't given, but wc is DBWclientID, then get
* the clientName from the default button handler.
*/
if ((clientName == NULL) && (wc == DBWclientID))
clientName = DBWGetButtonHandler();
if (cmd->tx_argc == argstart)
{
if (clientName == NULL)
h = NULL;
else
h = HashLookOnly(&MacroClients, (char *)clientName);
if (h == NULL)
{
#ifdef MAGIC_WRAPPER
Tcl_Obj *lobj;
lobj = Tcl_NewListObj(0, NULL);
#endif
TxError("Cannot get macro list from current window.\n");
#ifndef MAGIC_WRAPPER
TxError("List of known macro clients:\n");
#endif
/* If clientName was not in MacroClients, then what is? */
HashStartSearch(&hs);
while ((h = HashNext(&MacroClients, &hs)) != NULL)
{
char *clientName = h->h_key.h_name;
#ifdef MAGIC_WRAPPER
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewStringObj(clientName, -1));
#else
TxError("%s ", clientName);
#endif
}
#ifdef MAGIC_WRAPPER
Tcl_SetObjResult(magicinterp, lobj);
#else
TxError("\n");
#endif
return;
}
else
{
clienttable = (HashTable *)HashGetValue(h);
if (clienttable == (HashTable *)NULL)
{
TxError("No such client.\n");
return;
}
}
cn = NULL;
any = FALSE;
ch = 0;
HashStartSearch(&hs);
while (TRUE)
{
if (cn)
{
/* this loop uses 'continue' after 'cn' was assigned from malloc below, so
* this ensures it is freed up at the start of the next iteration.
*/
freeMagic(cn);
cn = NULL;
}
h = HashNext(clienttable, &hs);
if (h == NULL) break;
cMacro = (macrodef *) HashGetValue(h);
if (cMacro == (macrodef *)NULL) break;
cn = MacroName((spointertype)h->h_key.h_ptr);
/* "imacro list" returns only interactive macros. */
if (interactive && !cMacro->interactive) continue;
if (do_help)
cp = (cMacro->helptext == NULL) ? cMacro->macrotext : cMacro->helptext;
else
cp = cMacro->macrotext;
if (cp == (char *)NULL) cp = (char *)(&nulltext[0]);
if (searchterm != NULL)
{
/* Refine results by keyword search */
if (!strstr(cp, searchterm)) continue;
}
if (do_list)
{
#ifdef MAGIC_WRAPPER
// NOTE: Putting cp before cn makes it easier to
// generate a reverse lookup hash table for matching
// against menu items, to automatically generate
// the "accelerator" text.
if (do_reverse)
Tcl_AppendElement(magicinterp, cp);
Tcl_AppendElement(magicinterp, cn);
if (!do_reverse)
Tcl_AppendElement(magicinterp, cp);
#else
TxPrintf("%s = \"%s\"\n", cn, cp);
#endif
}
else
{
if (cMacro->interactive)
TxPrintf("Interactive macro '%s' %s \"%s\"\n",
cn, (do_help) ? "" : "contains", cp);
else
TxPrintf("Macro '%s' %s \"%s\"\n",
cn, (do_help) ? "" : "contains", cp);
}
any = TRUE;
}
if (!any)
{
if (!do_list) TxPrintf("No macros are defined for this client.\n");
}
return;
}
else if (cmd->tx_argc == (argstart + 1))
{
ct = MacroKey(cmd->tx_argv[argstart], &verbose);
if (ct == 0)
{
if (verbose)
TxError("Unrecognized macro name %s\n", cmd->tx_argv[argstart]);
return;
}
if (do_help)
cp = MacroRetrieveHelp(wc, ct);
else
cp = MacroRetrieve(wc, ct, &iReturn);
if (cp != NULL)
{
cn = MacroName(ct);
if (do_list)
{
#ifdef MAGIC_WRAPPER
Tcl_SetResult(magicinterp, cp, TCL_VOLATILE);
#else
TxPrintf("%s\n", cp);
#endif
}
else
{
if (iReturn)
{
TxPrintf("Interactive macro '%s' contains \"%s\"\n",
cn, cp);
}
else
{
TxPrintf("Macro '%s' contains \"%s\"\n",
cn, cp);
}
}
freeMagic(cp);
freeMagic(cn);
}
return;
}
else if (cmd->tx_argc == (argstart + 2))
{
int verbose;
ct = MacroKey(cmd->tx_argv[argstart], &verbose);
if (ct == 0)
{
if (verbose)
TxError("Unrecognized macro name %s\n", cmd->tx_argv[argstart]);
return;
}
argstart++;
if (cmd->tx_argv[argstart][0] == '\0') MacroDelete(wc, ct);
else if (do_help)
MacroDefineHelp(wc, ct, cmd->tx_argv[argstart]);
else if (interactive)
MacroDefine(wc, ct, cmd->tx_argv[argstart], NULL, TRUE);
else
MacroDefine(wc, ct, cmd->tx_argv[argstart], NULL, FALSE);
return;
}
else if (cmd->tx_argc == (argstart + 3))
{
int verbose;
ct = MacroKey(cmd->tx_argv[argstart], &verbose);
if (ct == 0)
{
if (verbose)
TxError("Unrecognized macro name %s\n", cmd->tx_argv[argstart]);
return;
}
argstart++;
if (cmd->tx_argv[argstart][0] == '\0') MacroDelete(wc, ct);
else if (interactive) MacroDefine(wc, ct, cmd->tx_argv[argstart],
cmd->tx_argv[argstart + 1], TRUE);
else MacroDefine(wc, ct, cmd->tx_argv[argstart],
cmd->tx_argv[argstart + 1], FALSE);
return;
}
TxError("Usage: %s [client] [macro_name [string] [help_text]]\n", cmd->tx_argv[0]);
}