magic/windows/windCmdAM.c

1291 lines
29 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)
* ----------------------------------------------------------------------------
*/
void
windCursorCmd(w, cmd)
MagWindow *w;
TxCommand *cmd;
{
Point p_in, p_out;
int resulttype = DBW_SNAP_INTERNAL;
double cursx, cursy, oscale;
DBWclientRec *crec;
#ifdef MAGIC_WRAPPER
Tcl_Obj *listxy;
#endif
if (cmd->tx_argc == 2)
{
if (StrIsInt(cmd->tx_argv[1]))
{
if (GrSetCursorPtr != NULL)
(*GrSetCursorPtr)(atoi(cmd->tx_argv[1]));
return;
}
else if (*cmd->tx_argv[1] == 'l')
{
resulttype = DBW_SNAP_LAMBDA;
}
else if (*cmd->tx_argv[1] == 'u')
{
resulttype = DBW_SNAP_USER;
}
else if (*cmd->tx_argv[1] == 'm')
{
resulttype = DBW_SNAP_MICRONS;
}
else if (*cmd->tx_argv[1] == 'w')
{
resulttype = -1; // Use this value for "window"
}
else if (*cmd->tx_argv[1] == 's')
{
resulttype = -2; // Use this value for "screen"
}
else if (*cmd->tx_argv[1] != 'i')
{
TxError("Usage: cursor glyphnum\n");
TxError(" (or): cursor [internal | lambda | microns | user | window]\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_SNAP_INTERNAL)
ToolSnapToGrid(w, &p_out, (Rect *)NULL);
}
/* Transform the result to declared units with option "lambda" or "grid" */
switch (resulttype) {
case -2:
case -1:
cursx = (double)p_in.p_x;
cursy = (double)p_in.p_y;
break;
case DBW_SNAP_INTERNAL:
cursx = (double)p_out.p_x;
cursy = (double)p_out.p_y;
break;
case DBW_SNAP_LAMBDA:
cursx = (double)(p_out.p_x * DBLambda[0]) / (double)DBLambda[1];
cursy = (double)(p_out.p_y * DBLambda[0]) / (double)DBLambda[1];
break;
case DBW_SNAP_MICRONS:
oscale = (double)CIFGetOutputScale(1000);
cursx = (double)(p_out.p_x * oscale);
cursy = (double)(p_out.p_y * oscale);
break;
case DBW_SNAP_USER:
crec = (DBWclientRec *)w->w_clientData;
cursx = (double)((p_out.p_x - crec->dbw_gridRect.r_xbot)
/ (crec->dbw_gridRect.r_xtop - crec->dbw_gridRect.r_xbot));
cursy = (double)((p_out.p_y - crec->dbw_gridRect.r_ybot)
/ (crec->dbw_gridRect.r_ytop - crec->dbw_gridRect.r_ybot));
break;
}
#ifdef MAGIC_WRAPPER
listxy = Tcl_NewListObj(0, NULL);
if ((cursx == round(cursx)) && (cursy == round(cursy)))
{
Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewIntObj((int)cursx));
Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewIntObj((int)cursy));
}
else
{
Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewDoubleObj(cursx));
Tcl_ListObjAppendElement(magicinterp, listxy, Tcl_NewDoubleObj(cursy));
}
Tcl_SetObjResult(magicinterp, listxy);
#else
TxPrintf("%g %g\n", cursx, cursy);
#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;
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; /* Added by NP 11/15/04 */
else if (cmd->tx_argc > 1)
wc = WindGetClient(cmd->tx_argv[1], TRUE);
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 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. */
if (MacroKey(cmd->tx_argv[argstart], &verbose) == 0)
if (MacroKey(cmd->tx_argv[argstart + 1], &verbose) != 0)
{
wc = 0;
argstart++;
}
}
}
else
argstart++;
if (cmd->tx_argc == argstart)
{
h = HashLookOnly(&MacroClients, (char *)wc);
if (h == NULL)
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 [macro_name [string] [help_text]]\n", cmd->tx_argv[0]);
}