ngspice/xgraph/xgraph.c

1283 lines
32 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* $Header$ */
/*
* xgraph - A Simple Plotter for X
*
* David Harrison
* University of California, Berkeley
* 1986, 1987, 1988, 1989
*
* Please see copyright.h concerning the formal reproduction rights
* of this software.
*
* $Log$
* Revision 1.2 2011-04-27 18:30:17 rlar
* code cleanup
*
* Revision 1.1 2004/01/25 09:00:49 pnenzi
*
* Added xgraph plotting program.
*
* Revision 1.3 1999/12/19 00:52:07 heideman
* warning suppresion, slightly different flot ahndling
*
* Revision 1.2 1999/12/03 23:17:46 heideman
* apply xgraph_no_animation.patch
*
* Revision 1.1.1.1 1999/12/03 23:15:52 heideman
* xgraph-12.0
*
*/
#ifndef lint
static char rcsid[] = "$Id$";
#endif
#include "copyright.h"
#include <stdio.h>
#include <math.h>
#include <pwd.h>
#include <ctype.h>
#include "xgraph.h"
#include "xtb.h"
#include "hard_devices.h"
#include "params.h"
extern void init_X();
extern void do_error();
#ifdef DO_DER
extern void Bounds();
#endif /* DO_DER */
static char *tildeExpand();
static void ReverseIt();
static void Traverse();
static int XErrHandler();
NewDataSet PlotData[MAXSETS],
DataD1[MAXSETS],
DataD2[MAXSETS];
XSegment *Xsegs[2]; /* Point space for X */
/* Basic transformation stuff */
double llx,
lly,
urx,
ury; /* Bounding box of all data */
static XContext win_context = (XContext) 0;
/* Other globally set defaults */
Display *disp; /* Open display */
Visual *vis; /* Standard visual */
Colormap cmap; /* Standard colormap */
int screen; /* Screen number */
int depth; /* Depth of screen */
int numFiles = 0; /* Number of input files */
char *inFileNames[MAXSETS]; /* File names */
/* Total number of active windows */
int Num_Windows = 0;
char *Prog_Name;
char *disp_name;
main(argc, argv)
int argc;
char *argv[];
/*
* This sets up the hard-wired defaults and reads the X defaults.
* The command line format is: xgraph [host:display].
*/
{
Window primary,
NewWindow();
XEvent theEvent;
LocalWin *win_info;
Cursor zoomCursor;
FILE *strm;
XColor fg_color,
bg_color;
char keys[MAXKEYS];
int nbytes,
idx,
maxitems = 0,
flags;
int errs = 0;
/* Open up new display */
Prog_Name = argv[0];
disp_name = "";
/* Parse the argument list looking for input files */
flags = ParseArgs(argc, argv, 0);
if (flags == D_XWINDOWS) {
disp = XOpenDisplay(disp_name);
if (!disp) {
(void) fprintf(stderr,
"%s: cannot open display `%s'\n",
argv[0], disp_name);
exit(1);
}
XSetErrorHandler(XErrHandler);
}
/* Set up hard-wired defaults and allocate spaces */
InitSets(flags);
/* Read X defaults and override hard-coded defaults */
if (PM_INT("Output Device") == D_XWINDOWS)
ReadDefaults();
/* Read the data into the data sets */
llx = lly = MAXFLOAT;
urx = ury = -MAXFLOAT;
for (idx = 0; idx < numFiles; idx++) {
strm = fopen(inFileNames[idx], "r");
if (!strm) {
(void) fprintf(stderr, "Warning: cannot open file `%s'\n",
inFileNames[idx]);
}
else {
if ((maxitems = ReadData(strm, inFileNames[idx])) < 0) {
errs++;
}
(void) fclose(strm);
}
}
if (!numFiles) {
if ((maxitems = ReadData(stdin, (char *) 0)) < 0) {
errs++;
}
}
if (errs) {
(void) fprintf(stderr, "Problems found with input data.\n");
exit(1);
}
/* Parse the argument list to set options */
(void) ParseArgs(argc, argv, 1);
if (PM_BOOL("Animate")) param_set("TitleText",STR,"Animated X Graph");
if (maxitems == 0) {
(void) fprintf(stderr, "Nothing to plot.\n");
exit(1);
}
Xsegs[0] = (XSegment *) Malloc((unsigned) (maxitems * sizeof(XSegment)));
Xsegs[1] = (XSegment *) Malloc((unsigned) (maxitems * sizeof(XSegment)));
/* Reverse Video Hack */
if (PM_BOOL("ReverseVideo"))
ReverseIt();
hard_init();
if (PM_BOOL("Debug")) {
if (PM_INT("Output Device") == D_XWINDOWS)
(void) XSynchronize(disp, 1);
param_dump();
}
/* Logarithmic and bounding box computation */
flags = 0;
if (PM_BOOL("LogX"))
flags |= LOG_X;
if (PM_BOOL("LogY"))
flags |= LOG_Y;
if (PM_BOOL("StackGraph"))
flags |= STK;
if (PM_BOOL("FitX"))
flags |= FITX;
if (PM_BOOL("FitY"))
flags |= FITY;
Traverse(flags);
/* Nasty hack here for bar graphs */
if (PM_BOOL("BarGraph")) {
double base;
llx -= PM_DBL("BarWidth");
urx += PM_DBL("BarWidth");
base = PM_DBL("BarBase");
if (base < lly)
lly = base;
if (base > ury)
ury = base;
}
/* Create initial window */
if (PM_INT("Output Device") == D_XWINDOWS) {
double asp;
asp = 1.0;
xtb_init(disp, screen, PM_PIXEL("Foreground"), PM_PIXEL("Background"),
PM_FONT("LabelFont"));
primary = NewWindow(Prog_Name,
PM_DBL("XLowLimit"), PM_DBL("YLowLimit"),
PM_DBL("XHighLimit"), PM_DBL("YHighLimit"),
asp,0);
if (!primary) {
(void) fprintf(stderr, "Main window would not open\n");
exit(1);
}
zoomCursor = XCreateFontCursor(disp, XC_sizing);
fg_color = PM_COLOR("Foreground");
bg_color = PM_COLOR("Background");
XRecolorCursor(disp, zoomCursor, &fg_color, &bg_color);
Num_Windows = 1;
while (Num_Windows > 0) {
XNextEvent(disp, &theEvent);
if (xtb_dispatch(&theEvent) != XTB_NOTDEF)
continue;
if (XFindContext(theEvent.xany.display,
theEvent.xany.window,
win_context, (caddr_t *) & win_info)) {
/* Nothing found */
continue;
}
switch (theEvent.type) {
case Expose:
if (theEvent.xexpose.count <= 0) {
XWindowAttributes win_attr;
XGetWindowAttributes(disp, theEvent.xany.window, &win_attr);
win_info->dev_info.area_w = win_attr.width;
win_info->dev_info.area_h = win_attr.height;
init_X(win_info->dev_info.user_state);
EraseData(win_info);
DrawWindow(win_info);
}
break;
case KeyPress:
nbytes = XLookupString(&theEvent.xkey, keys, MAXKEYS,
(KeySym *) 0, (XComposeStatus *) 0);
for (idx = 0; idx < nbytes; idx++) {
if (keys[idx] == CONTROL_D) {
/* Delete this window */
DelWindow(theEvent.xkey.window, win_info);
}
else if (keys[idx] == CONTROL_C) {
/* Exit program */
Num_Windows = 0;
}
else if (keys[idx] == 'h') {
PrintWindow(theEvent.xany.window, win_info);
}
}
break;
case ButtonPress:
/* Handle creating a new window */
Num_Windows += HandleZoom(Prog_Name,
&theEvent.xbutton,
win_info, zoomCursor);
break;
default:
(void) fprintf(stderr, "Unknown event type: %x\n",
theEvent.type);
break;
}
}
}
else {
int Device = PM_INT("Output Device");
int dflag = strcmp(PM_STR("Disposition"), "To Device") == 0;
primary = NewWindow(Prog_Name,
PM_DBL("XLowLimit"), PM_DBL("YLowLimit"),
PM_DBL("XHighLimit"), PM_DBL("YHighLimit"),
1.0,0);
do_hardcopy(Prog_Name, primary,
hard_devices[Device].dev_init,
dflag ? hard_devices[Device].dev_spec : 0,
PM_STR("FileOrDev"), (double) 19,
hard_devices[Device].dev_title_font,
hard_devices[Device].dev_title_size,
hard_devices[Device].dev_axis_font,
hard_devices[Device].dev_axis_size,
PM_BOOL("Document") * D_DOCU);
}
return 0;
}
#define BLACK_THRES 30000
static void
ReversePix(param_name)
char *param_name; /* Name of color parameter */
/*
* Looks up `param_name' in the parameters database. If found, the
* color is examined and judged to be either black or white based
* upon its red, green, and blue intensities. The sense of the
* color is then reversed and reset to its opposite.
*/
{
params val;
if (param_get(param_name, &val)) {
if ((val.pixv.value.red < BLACK_THRES) &&
(val.pixv.value.green < BLACK_THRES) &&
(val.pixv.value.blue < BLACK_THRES)) {
/* Color is black */
param_reset(param_name, "white");
}
else {
/* Color is white */
param_reset(param_name, "black");
}
}
else {
(void) fprintf(stderr, "Cannot reverse color `%s'\n", param_name);
}
}
static void
ReverseIt()
/*
* This routine attempts to implement reverse video. It steps through
* all of the important colors in the parameters database and makes
* black white (and vice versa).
*/
{
int i;
char buf[1024];
for (i = 0; i < MAXATTR; i++) {
(void) sprintf(buf, "%d.Color", i);
ReversePix(buf);
}
ReversePix("Foreground");
ReversePix("Border");
ReversePix("ZeroColor");
ReversePix("Background");
}
static void
Traverse(flags)
int flags; /* Options */
/*
* Traverses through all of the data applying certain options to the
* data and computing the overall bounding box. The flags are:
* LOG_X Take the log of the X axis
* LOG_Y Take the log of the Y axis
* STK Stack coordinates.
* FITX Fit x-coordinates from zero to one
* FITY Fit y-coordinates from zero to one
*/
{
int i,
j;
PointList *spot;
PointList *pspot;
static char *paramstr[] =
{
"Cannot plot negative %s values\n",
"when the logarithmic option is selected.\n",
"Number of points in %d and %d don't match for stacking.\n",
"Point %d in %d and %d doesn't match for stacking.\n",
"Set %d has 0 %s.\n"
};
if (flags & (FITX|FITY))
for (i = 0; i < MAXSETS; i++)
for (spot = PlotData[i].list; spot; spot = spot->next) {
float minx, maxx, miny, maxy;
minx = maxx = spot->xvec[0];
maxy = miny = spot->yvec[0];
for (j = 1; j < spot->numPoints; j++) {
minx = MIN(minx, spot->xvec[j]);
miny = MIN(miny, spot->yvec[j]);
maxx = MAX(maxx, spot->xvec[j]);
maxy = MAX(maxy, spot->yvec[j]);
}
maxx = maxx - minx;
maxy = maxy - miny;
if (maxx == 0.0) {
(void) fprintf(stderr, paramstr[3], i, "width");
maxx = 1.0;
}
if (maxy == 0.0) {
(void) fprintf(stderr, paramstr[3], i, "height");
maxy = 1.0;
}
switch (flags & (FITX|FITY)) {
case FITX:
for (j = 0; j < spot->numPoints; j++)
spot->xvec[j] = (-minx + spot->xvec[j]) / maxx;
break;
case FITY:
for (j = 0; j < spot->numPoints; j++)
spot->yvec[j] = (-miny + spot->yvec[j]) / maxy;
break;
case FITX|FITY:
for (j = 0; j < spot->numPoints; j++) {
spot->xvec[j] = (-minx + spot->xvec[j]) / maxx;
spot->yvec[j] = (-miny + spot->yvec[j]) / maxy;
}
break;
default:
abort();
}
}
if (flags & STK)
for (i = 1; i < MAXSETS; i++) {
for (spot = PlotData[i].list, pspot = PlotData[i - 1].list;
spot && pspot; spot = spot->next, pspot = pspot->next) {
if (spot->numPoints != pspot->numPoints) {
(void) fprintf(stderr, paramstr[2], i - 1, i);
exit(1);
}
for (j = 0; j < spot->numPoints; j++) {
if (spot->xvec[j] != pspot->xvec[j]) {
(void) fprintf(stderr, paramstr[3], j, i - 1, i);
exit(1);
}
spot->yvec[j] += pspot->yvec[j];
}
}
}
for (i = 0; i < MAXSETS; i++) {
for (spot = PlotData[i].list; spot; spot = spot->next) {
for (j = 0; j < spot->numPoints; j++) {
if (flags & LOG_Y) {
if (spot->yvec[j] > 0.0) {
spot->yvec[j] = log10(spot->yvec[j]);
}
else if (spot->yvec[j] == 0)
spot->yvec[j] = 0.0;
else {
(void) fprintf(stderr, paramstr[0], "Y");
(void) fprintf(stderr, paramstr[1]);
exit(1);
}
}
if (flags & LOG_X) {
if (spot->xvec[j] > 0.0) {
spot->xvec[j] = log10(spot->xvec[j]);
}
else if (spot->xvec[j] == 0)
spot->xvec[j] = 0.0;
else {
(void) fprintf(stderr, paramstr[0], "X");
(void) fprintf(stderr, paramstr[1]);
exit(1);
}
}
/* Update global bounding box */
if (spot->xvec[j] < llx)
llx = spot->xvec[j];
if (spot->xvec[j] > urx)
urx = spot->xvec[j];
if (spot->yvec[j] < lly)
lly = spot->yvec[j];
if (spot->yvec[j] > ury)
ury = spot->yvec[j];
}
}
}
}
/*
* Button handling functions
*/
/*ARGSUSED*/
xtb_hret
del_func(win, bval, info)
Window win; /* Button window */
int bval; /* Button value */
char *info; /* User information */
/*
* This routine is called when the `Close' button is pressed in
* an xgraph window. It causes the window to go away.
*/
{
Window the_win = (Window) info;
LocalWin *win_info;
xtb_bt_set(win, 1, (char *) 0, 0);
if (!XFindContext(disp, the_win, win_context, (caddr_t *) & win_info)) {
if (win_info->flags & HARDCOPY_IN_PROGRESS) {
do_error("Can't close window while\nhardcopy dialog is posted.\n");
xtb_bt_set(win, 0, (char *) 0, 0);
}
else {
DelWindow(the_win, win_info);
}
}
return XTB_HANDLED;
}
/*ARGSUSED*/
xtb_hret
hcpy_func(win, bval, info)
Window win; /* Button Window */
int bval; /* Button value */
char *info; /* User Information */
/*
* This routine is called when the hardcopy button is pressed
* in an xgraph window. It causes the output dialog to be
* posted.
*/
{
Window the_win = (Window) info;
LocalWin *win_info;
xtb_bt_set(win, 1, (char *) 0, 0);
if (!XFindContext(disp, the_win, win_context, (caddr_t *) & win_info)) {
win_info->flags |= HARDCOPY_IN_PROGRESS;
PrintWindow(the_win, win_info);
win_info->flags &= (~HARDCOPY_IN_PROGRESS);
}
xtb_bt_set(win, 0, (char *) 0, 0);
return XTB_HANDLED;
}
static
/*ARGSUSED*/
xtb_hret
abt_func(win, bval, info)
Window win; /* Button window */
int bval; /* Button value */
char *info; /* User information */
{
static char *msg_fmt =
"Version %s\n\
XGraph + Animation and Derivatives\n\
Modification of code by David Harrison\n\
University of California, Berkeley\n\
(davidh@ic.Berkeley.EDU or \n\
...!ucbvax!ucbic!davidh)\n\
Animation, differentiation, and a few other\n\
new features added by Paul Walker,\n\
National Center for Supercomputer Applications\n\
and Univ. Illinois at U-C Dept of Physics.\n\
Send comments or suggestions to\n\
pwalker@ncsa.uiuc.edu\n";
static int active = 0;
char msg_buf[1024];
if (!active) {
active = 1;
xtb_bt_set(win, 1, (char *) 0, 0);
(void) sprintf(msg_buf, msg_fmt, VERSION_STRING);
msg_box("XGraph", msg_buf);
xtb_bt_set(win, 0, (char *) 0, 0);
active = 0;
}
return XTB_HANDLED;
}
#ifdef DO_DER
static
/*ARGSUSED PW*/
xtb_hret
rew_func(win, bval, info)
Window win; /* Button window */
int bval; /* Button value */
char *info; /* User information */
{
/* This routine added, Paul Walker, to rewind the animation and start
it over. The only even moderatly tricky part is erasing the last
item, which still lives, and redrawing the axis. I do it by just
copying the "Expose" information from the main routine. */
Window the_win = (Window) info;
LocalWin *win_info;
/* Set animation to True */
param_set("Animate",BOOL,"on");
if (!XFindContext(disp, the_win, win_context, (caddr_t *) & win_info)) {
XWindowAttributes win_attr;
XGetWindowAttributes(disp, the_win, &win_attr);
win_info->dev_info.area_w = win_attr.width;
win_info->dev_info.area_h = win_attr.height;
init_X(win_info->dev_info.user_state);
EraseData (win_info);
DrawWindow(win_info);
}
return XTB_HANDLED;
}
static
/*ARGSUSED PW*/
xtb_hret
der_func(win, bval, info)
Window win; /* Button window */
int bval; /* Button value */
char *info; /* User information */
{
/* This routine added, Paul Walker, to rewind the animation and start
it over. The only even moderatly tricky part is erasing the last
item, which still lives, and redrawing the axis. I do it by just
copying the "Expose" information from the main routine. */
Window the_win = (Window) info;
Window new_win;
LocalWin *win_info;
double loX,loY,hiX,hiY,asp;
static char *msg_fmt =
"Version %s\n\
Currently unable to display\n\
or calculate derivatives\n\
higher than 2nd order\n";
static int active = 0;
char msg_buf[1024];
XFindContext(disp, the_win, win_context, (caddr_t *) & win_info);
if (win_info->DOrder == 2) {
if (!active) {
active = 1;
xtb_bt_set(win, 1, (char *) 0, 0);
(void) sprintf(msg_buf, msg_fmt, VERSION_STRING);
msg_box("XGraph", msg_buf);
xtb_bt_set(win, 0, (char *) 0, 0);
active = 0;
}
return XTB_HANDLED;
}
Num_Windows += 1;
asp = 1.0;
Bounds(&loX,&loY,&hiX,&hiY,win_info->DOrder+1);
new_win = NewWindow("Derivatives", loX, loY, hiX, hiY, asp,
win_info->DOrder+1);
if (!XFindContext(disp, new_win, win_context, (caddr_t *) & win_info)) {
XWindowAttributes win_attr;
XGetWindowAttributes(disp, new_win, &win_attr);
win_info->dev_info.area_w = win_attr.width;
win_info->dev_info.area_h = win_attr.height;
init_X(win_info->dev_info.user_state);
EraseData (win_info);
DrawWindow(win_info);
}
return XTB_HANDLED;
}
static
/*ARGSUSED PW*/
xtb_hret
rpl_func(win, bval, info)
Window win; /* Button window */
int bval; /* Button value */
char *info; /* User information */
{
/* Puts us back into static mode ... */
Window the_win = (Window) info;
LocalWin *win_info;
/* Set animation to True */
param_set("Animate",BOOL,"off");
if (!XFindContext(disp, the_win, win_context, (caddr_t *) & win_info)) {
XWindowAttributes win_attr;
XGetWindowAttributes(disp, the_win, &win_attr);
win_info->dev_info.area_w = win_attr.width;
win_info->dev_info.area_h = win_attr.height;
init_X(win_info->dev_info.user_state);
EraseData (win_info);
DrawWindow(win_info);
}
return XTB_HANDLED;
}
/* End change PW */
#endif /* DO_DER */
#define NORMSIZE 600
#define MINDIM 100
Window
NewWindow(progname, lowX, lowY, upX, upY, asp, DO)
char *progname; /* Name of program */
double lowX,
lowY; /* Lower left corner */
double upX,
upY; /* Upper right corner */
double asp; /* Aspect ratio */
int DO; /* Derivative Order. */
/*
* Creates and maps a new window. This includes allocating its
* local structure and associating it with the XId for the window.
* The aspect ratio is specified as the ratio of width over height.
*/
{
Window new_window;
LocalWin *new_info;
static Cursor theCursor = (Cursor) 0;
XSizeHints sizehints;
XSetWindowAttributes wattr;
XWMHints wmhints;
XColor fg_color,
bg_color;
int geo_mask;
int width,
height;
unsigned long wamask;
char defSpec[120];
double pad;
new_info = (LocalWin *) Malloc(sizeof(LocalWin));
new_info->DOrder = DO;
if (upX > lowX) {
new_info->loX = lowX;
new_info->hiX = upX;
}
else {
new_info->loX = llx;
new_info->hiX = urx;
}
if (upY > lowY) {
new_info->loY = lowY;
new_info->hiY = upY;
}
else {
new_info->loY = lly;
new_info->hiY = ury;
}
/* Increase the padding for aesthetics */
if (new_info->hiX - new_info->loX == 0.0) {
pad = MAX(0.5, fabs(new_info->hiX / 2.0));
new_info->hiX += pad;
new_info->loX -= pad;
}
if (new_info->hiY - new_info->loY == 0) {
pad = MAX(0.5, fabs(ury / 2.0));
new_info->hiY += pad;
new_info->loY -= pad;
}
/* Add 10% padding to bounding box (div by 20 yeilds 5%) */
pad = (new_info->hiX - new_info->loX) / 20.0;
new_info->loX -= pad;
new_info->hiX += pad;
pad = (new_info->hiY - new_info->loY) / 20.0;
new_info->loY -= pad;
new_info->hiY += pad;
/* Aspect ratio computation */
if (asp < 1.0) {
height = NORMSIZE;
width = ((int) (((double) NORMSIZE) * asp));
}
else {
width = NORMSIZE;
height = ((int) (((double) NORMSIZE) / asp));
}
height = MAX(MINDIM, height);
width = MAX(MINDIM, width);
if (PM_INT("Output Device") == D_XWINDOWS) {
(void) sprintf(defSpec, "%dx%d+100+100", width, height);
wamask = CWBackPixel | CWBorderPixel | CWColormap;
wattr.background_pixel = PM_PIXEL("Background");
wattr.border_pixel = PM_PIXEL("Border");
wattr.colormap = cmap;
sizehints.flags = PPosition | PSize;
sizehints.x = sizehints.y = 100;
sizehints.width = width;
sizehints.height = height;
geo_mask = XParseGeometry(PM_STR("Geometry"),
&sizehints.x, &sizehints.y,
(unsigned int *) &sizehints.width,
(unsigned int *) &sizehints.height);
if (geo_mask & (XValue|YValue))
sizehints.flags = (sizehints.flags & ~PPosition) | USPosition;
if (geo_mask & (WidthValue | HeightValue))
sizehints.flags = (sizehints.flags & ~PSize) | USSize;
new_window = XCreateWindow(disp, RootWindow(disp, screen),
sizehints.x, sizehints.y,
(unsigned int) sizehints.width,
(unsigned int) sizehints.height,
(unsigned int) PM_INT("BorderSize"),
depth, InputOutput, vis,
wamask, &wattr);
if (new_window) {
xtb_frame cl_frame,
hd_frame,
ab_frame,
rw_frame,
rp_frame,
dx_frame;
XStoreName(disp, new_window, progname);
XSetIconName(disp, new_window, progname);
wmhints.flags = InputHint | StateHint;
wmhints.input = True;
wmhints.initial_state = NormalState;
XSetWMHints(disp, new_window, &wmhints);
XSetWMNormalHints(disp, new_window, &sizehints);
/* Set device info */
set_X(new_window, &(new_info->dev_info));
if (!PM_BOOL("NoButton")) {
/* Make buttons */
xtb_bt_new(new_window, "Close", del_func,
(xtb_data) new_window, &cl_frame);
new_info->close = cl_frame.win;
XMoveWindow(disp, new_info->close, (int) BTNPAD, (int) BTNPAD);
xtb_bt_new(new_window, "Hdcpy", hcpy_func,
(xtb_data) new_window, &hd_frame);
new_info->hardcopy = hd_frame.win;
XMoveWindow(disp, new_info->hardcopy,
(int) (BTNPAD + cl_frame.width + BTNINTER),
BTNPAD);
xtb_bt_new(new_window, "About", abt_func,
(xtb_data) new_window, &ab_frame);
new_info->about = ab_frame.win;
XMoveWindow(disp, new_info->about,
(int) (BTNPAD + cl_frame.width + BTNINTER +
hd_frame.width + BTNINTER), BTNPAD);
#ifdef DO_DER
/* These buttons added PW */
xtb_bt_new(new_window, "Anim", rew_func,
(xtb_data) new_window, &rw_frame);
new_info->rewind = rw_frame.win;
XMoveWindow(disp, new_info->rewind,
(int) (BTNPAD + cl_frame.width + BTNINTER +
hd_frame.width + BTNINTER +
ab_frame.width + BTNINTER), BTNPAD);
xtb_bt_new(new_window, "Replot", rpl_func,
(xtb_data) new_window, &rp_frame);
new_info->replot = rp_frame.win;
XMoveWindow(disp, new_info->replot,
(int) (BTNPAD + cl_frame.width + BTNINTER +
hd_frame.width + BTNINTER +
ab_frame.width + BTNINTER +
rw_frame.width + BTNINTER), BTNPAD);
xtb_bt_new(new_window, "Deriv", der_func,
(xtb_data) new_window, &dx_frame);
new_info->deriv = dx_frame.win;
XMoveWindow(disp, new_info->deriv,
(int) (BTNPAD + cl_frame.width + BTNINTER +
hd_frame.width + BTNINTER +
ab_frame.width + BTNINTER +
rw_frame.width + BTNINTER +
rp_frame.width + BTNINTER), BTNPAD);
#endif /* DO_DER */
new_info->flags = 0;
}
XSelectInput(disp, new_window,
ExposureMask | KeyPressMask | ButtonPressMask);
if (!theCursor) {
theCursor = XCreateFontCursor(disp, XC_top_left_arrow);
fg_color = PM_COLOR("Foreground");
bg_color = PM_COLOR("Background");
XRecolorCursor(disp, theCursor, &fg_color, &bg_color);
}
XDefineCursor(disp, new_window, theCursor);
if (!win_context) {
win_context = XUniqueContext();
}
XSaveContext(disp, new_window, win_context, (caddr_t) new_info);
XMapWindow(disp, new_window);
return new_window;
}
else {
return (Window) 0;
}
}
else {
new_info->dev_info.area_h = 1.0;
new_info->dev_info.area_w = 1.0;
return ((Window) new_info);
}
}
DelWindow(win, win_info)
Window win; /* Window */
LocalWin *win_info; /* Local Info */
/*
* This routine actually deletes the specified window and
* decrements the window count.
*/
{
xtb_data info;
XDeleteContext(disp, win, win_context);
xtb_bt_del(win_info->close, &info);
xtb_bt_del(win_info->hardcopy, &info);
xtb_bt_del(win_info->about, &info);
Free((char *) win_info);
XDestroyWindow(disp, win);
Num_Windows -= 1;
}
PrintWindow(win, win_info)
Window win; /* Window */
LocalWin *win_info; /* Local Info */
/*
* This routine posts a dialog asking about the hardcopy
* options desired. If the user hits `OK', the hard
* copy is performed.
*/
{
ho_dialog(win, Prog_Name, (char *) win_info);
}
static XRectangle boxEcho;
static GC echoGC = (GC) 0;
#define DRAWBOX \
if (startX < curX) { \
boxEcho.x = startX; \
boxEcho.width = curX - startX; \
} else { \
boxEcho.x = curX; \
boxEcho.width = startX - curX; \
} \
if (startY < curY) { \
boxEcho.y = startY; \
boxEcho.height = curY - startY; \
} else { \
boxEcho.y = curY; \
boxEcho.height = startY - curY; \
} \
XDrawRectangles(disp, win, echoGC, &boxEcho, 1);
#define TRANX(xval) \
(((double) ((xval) - wi->XOrgX)) * wi->XUnitsPerPixel + wi->UsrOrgX)
#define TRANY(yval) \
(wi->UsrOppY - (((double) ((yval) - wi->XOrgY)) * wi->YUnitsPerPixel))
int
HandleZoom(progname, evt, wi, cur)
char *progname;
XButtonPressedEvent *evt;
LocalWin *wi;
Cursor cur;
{
Window win,
new_win;
Window root_rtn,
child_rtn;
XEvent theEvent;
int startX,
startY,
curX,
curY,
newX,
newY,
stopFlag,
numwin = 0;
int root_x,
root_y;
unsigned int mask_rtn;
double loX,
loY,
hiX,
hiY,
asp;
win = evt->window;
if (XGrabPointer(disp, win, True,
(unsigned int) (ButtonPressMask | ButtonReleaseMask |
PointerMotionMask | PointerMotionHintMask),
GrabModeAsync, GrabModeAsync,
win, cur, CurrentTime) != GrabSuccess) {
XBell(disp, 0);
return 0;
}
if (echoGC == (GC) 0) {
unsigned long gcmask;
XGCValues gcvals;
gcmask = GCForeground | GCFunction;
gcvals.foreground = PM_PIXEL("ZeroColor") ^ PM_PIXEL("Background");
gcvals.function = GXxor;
echoGC = XCreateGC(disp, win, gcmask, &gcvals);
}
startX = evt->x;
startY = evt->y;
XQueryPointer(disp, win, &root_rtn, &child_rtn, &root_x, &root_y,
&curX, &curY, &mask_rtn);
/* Draw first box */
DRAWBOX;
stopFlag = 0;
while (!stopFlag) {
XNextEvent(disp, &theEvent);
switch (theEvent.xany.type) {
case MotionNotify:
XQueryPointer(disp, win, &root_rtn, &child_rtn, &root_x, &root_y,
&newX, &newY, &mask_rtn);
/* Undraw the old one */
DRAWBOX;
/* Draw the new one */
curX = newX;
curY = newY;
DRAWBOX;
break;
case ButtonRelease:
DRAWBOX;
XUngrabPointer(disp, CurrentTime);
stopFlag = 1;
if ((startX - curX != 0) && (startY - curY != 0)) {
/* Figure out relative bounding box */
loX = TRANX(startX);
loY = TRANY(startY);
hiX = TRANX(curX);
hiY = TRANY(curY);
if (loX > hiX) {
double temp;
temp = hiX;
hiX = loX;
loX = temp;
}
if (loY > hiY) {
double temp;
temp = hiY;
hiY = loY;
loY = temp;
}
/* physical aspect ratio */
asp = ((double) ABS(startX - curX)) /
((double) ABS(startY - curY));
new_win = NewWindow(progname, loX, loY, hiX, hiY, asp,
wi->DOrder);
if (new_win) {
numwin = 1;
}
else {
numwin = 0;
}
}
else {
numwin = 0;
}
break;
default:
printf("unknown event: %d\n", theEvent.xany.type);
break;
}
}
return numwin;
}
#define RND(val) ((int) ((val) + 0.5))
/*ARGSUSED*/
void
do_hardcopy(prog, info, init_fun, dev_spec, file_or_dev, maxdim,
ti_fam, ti_size, ax_fam, ax_size, doc_p)
char *prog; /* Program name for Xdefaults */
char *info; /* Some state information */
int (*init_fun) (); /* Hardcopy init function */
char *dev_spec; /* Device specification (if any) */
char *file_or_dev; /* Filename or device spec */
double maxdim; /* Maximum dimension in cm */
char *ti_fam,
*ax_fam; /* Font family names */
double ti_size,
ax_size; /* Font sizes in points */
int doc_p; /* Documentation predicate */
/*
* This routine resets the function pointers to those specified
* by `init_fun' and causes a screen redisplay. If `dev_spec'
* is non-zero, it will be considered a sprintf string with
* one %s which will be filled in with `file_or_dev' and fed
* to popen(3) to obtain a stream. Otherwise, `file_or_dev'
* is considered to be a file and is opened for writing. The
* resulting stream is fed to the initialization routine for
* the device.
*/
{
LocalWin *curWin = (LocalWin *) info;
LocalWin thisWin;
FILE *out_stream;
char buf[MAXBUFSIZE],
err[MAXBUFSIZE],
ierr[ERRBUFSIZE];
char tilde[MAXBUFSIZE * 10];
int final_w,
final_h,
flags;
double ratio;
if (dev_spec) {
(void) sprintf(buf, dev_spec, file_or_dev);
out_stream = popen(buf, "w");
if (!out_stream) {
sprintf(err, "Unable to issue command:\n %s\n", buf);
do_error(err);
return;
}
}
else {
tildeExpand(tilde, file_or_dev);
out_stream = fopen(tilde, "w");
if (!out_stream) {
sprintf(err, "Unable to open file `%s'\n", tilde);
do_error(err);
return;
}
}
if (curWin != (LocalWin *) 0) {
thisWin = *curWin;
ratio = ((double) thisWin.dev_info.area_w) /
((double) thisWin.dev_info.area_h);
}
else
ratio = 1.0;
if (thisWin.dev_info.area_w > thisWin.dev_info.area_h) {
final_w = RND(maxdim * 10000.0 * PM_DBL("Scale"));
final_h = RND(maxdim / ratio * 10000.0 * PM_DBL("Scale"));
}
else {
final_w = RND(maxdim * ratio * 10000.0 * PM_DBL("Scale"));
final_h = RND(maxdim * 10000.0 * PM_DBL("Scale"));
}
ierr[0] = '\0';
flags = 0;
if (doc_p)
flags |= D_DOCU;
if (init_fun (out_stream, final_w, final_h, ti_fam, ti_size,
ax_fam, ax_size, flags, &(thisWin.dev_info), ierr)) {
DrawWindow(&thisWin);
if (thisWin.dev_info.xg_end) {
thisWin.dev_info.xg_end(thisWin.dev_info.user_state);
}
}
else {
do_error(ierr);
}
if (dev_spec) {
(void) pclose(out_stream);
}
else {
(void) fclose(out_stream);
}
}
static char *
tildeExpand(out, in)
char *out; /* Output space for expanded file name */
char *in; /* Filename with tilde */
/*
* This routine expands out a file name passed in `in' and places
* the expanded version in `out'. It returns `out'.
*/
{
char username[50],
*userPntr;
struct passwd *userRecord;
out[0] = '\0';
/* Skip over the white space in the initial path */
while ((*in == ' ') ||(*in == '\t'))
in ++;
/* Tilde? */
if (in[0] == TILDE) {
/* Copy user name into 'username' */
in ++;
userPntr = &(username[0]);
while ((*in !='\0') &&(*in !='/')) {
*(userPntr++) = *(in ++);
}
*(userPntr) = '\0';
/* See if we have to fill in the user name ourselves */
if (strlen(username) == 0) {
userRecord = getpwuid(getuid());
}
else {
userRecord = getpwnam(username);
}
if (userRecord) {
/* Found user in passwd file. Concatenate user directory */
strcat(out, userRecord->pw_dir);
}
}
/* Concantenate remaining portion of file name */
strcat(out, in);
return out;
}
#define ERR_MSG_SIZE 2048
/*ARGSUSED*/
static int
XErrHandler(disp_ptr, evt)
Display *disp_ptr;
XErrorEvent *evt;
/*
* Displays a nicely formatted message and core dumps.
*/
{
char err_buf[ERR_MSG_SIZE],
mesg[ERR_MSG_SIZE],
number[ERR_MSG_SIZE];
char *mtype = "XlibMessage";
XGetErrorText(disp_ptr, evt->error_code, err_buf, ERR_MSG_SIZE);
(void) fprintf(stderr, "X Error: %s\n", err_buf);
XGetErrorDatabaseText(disp_ptr, mtype, "MajorCode",
"Request Major code %d", mesg, ERR_MSG_SIZE);
(void) fprintf(stderr, mesg, evt->request_code);
(void) sprintf(number, "%d", evt->request_code);
XGetErrorDatabaseText(disp_ptr, "XRequest", number, "", err_buf,
ERR_MSG_SIZE);
(void) fprintf(stderr, " (%s)\n", err_buf);
abort();
}