home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / maths / plplot / plplot_2 / drivers / tk / plframe.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-25  |  59.7 KB  |  2,187 lines

  1. /* $Id: plframe.c,v 1.39 1994/07/25 06:44:22 mjl Exp $
  2.  * $Log: plframe.c,v $
  3.  * Revision 1.39  1994/07/25  06:44:22  mjl
  4.  * Wrapped the include of unistd.h in a HAVE_UNISTD_H.
  5.  *
  6.  * Revision 1.38  1994/07/19  22:31:41  mjl
  7.  * All device drivers: enabling macro renamed to PLD_<driver>, where <driver>
  8.  * is xwin, ps, etc.  See plDevs.h for more detail.  All internal header file
  9.  * inclusion changed to /not/ use a search path so that it will work better
  10.  * with makedepend.
  11.  *
  12.  * Revision 1.37  1994/07/01  22:38:48  mjl
  13.  * Minor modification required by new X resource allocation scheme.
  14.  *
  15.  * Revision 1.36  1994/07/01  20:37:21  mjl
  16.  * Force an "update" when widget is initially mapped, but before startup
  17.  * procedure is invoked.  Ensures that "." has been mapped and that X is
  18.  * in a sane state before continuing (otherwise new toplevels containing
  19.  * plframes can cause a core dump).  Also some cruft elimination.
  20.  *
  21.  * Revision 1.35  1994/06/30  18:40:15  mjl
  22.  * Cleaning up to remove gcc -Wall warnings, and other miscellanea.
  23.  *
  24.  * Revision 1.34  1994/06/23  22:32:47  mjl
  25.  * Split off bulk of Tcl API in to separate file, where it can be used
  26.  * more flexibly.
  27.  *
  28.  * Revision 1.33  1994/06/16  19:03:18  mjl
  29.  * Changed "cmd plline" and "cmd plpoin" widget commands to use new Matrix
  30.  * notation/capabilities/etc.
  31.  *
  32.  * Revision 1.32  1994/06/15  17:21:32  furnish
  33.  * Fix cleanup so that killing a plframe doesn't core the app.
  34.  *
  35.  * Revision 1.31  1994/06/10  20:46:10  furnish
  36.  * Mirror plpoin.  More of the API still needs doing.
  37.  *
  38.  * Revision 1.30  1994/06/09  20:15:30  mjl
  39.  * Changed plplot direct widget commands ("<widget> cmd <command> <args>") to
  40.  * begin with a "pl", e.g. scol<?> to plscol<?>, etc.  To make going between
  41.  * the C and Tcl API's as natural as possible.  Added new direct widget
  42.  * commands plenv, plcol, pllab, and plline.  These were formerly in
  43.  * tkshell.c; having them here makes it trivial to associate independent
  44.  * streams with different widgets without user intervention.  The call to
  45.  * plinit() now done automatically when the widget is first mapped, and
  46.  * the "<widget> cmd init" command no longer supported.  Some reorganization.
  47.  */
  48.  
  49. /*----------------------------------------------------------------------*\
  50.  * 
  51.  * plframe.c --
  52.  *
  53.  *    This module implements "plframe" widgets for the Tk toolkit.
  54.  *    These are frames that have extra logic to allow them to be
  55.  *    interfaced with the PLPlot X driver.  These are then drawn
  56.  *    into and respond to keyboard and mouse events.
  57.  *
  58.  * Maurice LeBrun
  59.  * IFS, University of Texas at Austin
  60.  * 30-Apr-1993
  61.  *
  62.  * Based upon tkFrame.c from the TK 3.2 distribution:
  63.  *
  64.  * Copyright 1990 Regents of the University of California.
  65.  * Permission to use, copy, modify, and distribute this
  66.  * software and its documentation for any purpose and without
  67.  * fee is hereby granted, provided that the above copyright
  68.  * notice appear in all copies.  The University of California
  69.  * makes no representations about the suitability of this
  70.  * software for any purpose.  It is provided "as is" without
  71.  * express or implied warranty.
  72. \*----------------------------------------------------------------------*/
  73.  
  74. #include "plserver.h"
  75. #include "plplotX.h"
  76.  
  77. #ifdef HAVE_UNISTD_H
  78. #include <unistd.h>
  79. #endif
  80. #include <fcntl.h>
  81.  
  82. extern int plplot_ccmap;
  83.  
  84. #define NDEV    20        /* Max number of output device types */
  85.  
  86. /* If set, BUFFER_FIFO causes FIFO i/o to be buffered */
  87.  
  88. #define BUFFER_FIFO 1
  89.  
  90. /* If set, causes a file handler to be used with FIFO */
  91.  
  92. #define FH_FIFO 0
  93.  
  94. /* A handy command wrapper */
  95.  
  96. #define plframe_cmd(code) \
  97.     if ((code) == TCL_ERROR) return (TCL_ERROR);
  98.  
  99. /*
  100.  * A data structure of the following type is kept for each
  101.  * plframe that currently exists for this process:
  102.  */
  103.  
  104. typedef struct {
  105.  
  106.     /* This is stuff taken from tkFrame.c */
  107.  
  108.     Tk_Window tkwin;        /* Window that embodies the frame.  NULL
  109.                  * means that the window has been destroyed
  110.                  * but the data structures haven't yet been
  111.                  * cleaned up.*/
  112.     Display *display;        /* Display containing widget.  Used, among
  113.                  * other things, so that resources can be
  114.                  * freed even after tkwin has gone away. */
  115.     Tcl_Interp *interp;        /* Interpreter associated with
  116.                  * widget.  Used to delete widget
  117.                  * command.  */
  118.     Tk_3DBorder border;        /* Structure used to draw 3-D border and
  119.                  * background. */
  120.     int borderWidth;        /* Width of 3-D border (if any). */
  121.     int relief;            /* 3-d effect: TK_RELIEF_RAISED etc. */
  122.     int width;            /* Width to request for window.  <= 0 means
  123.                  * don't request any size. */
  124.     int height;            /* Height to request for window.  <= 0 means
  125.                  * don't request any size. */
  126.     char *geometry;        /* Geometry that user requested.  NULL
  127.                  * means use width and height instead. 
  128.                  * Malloc'ed. */
  129.     Cursor cursor;        /* Current cursor for window, or None. */
  130.     int flags;            /* Various flags;  see below for
  131.                  * definitions. */
  132.  
  133.     /* These are new to plframe widgets */
  134.  
  135.     /* control stuff */
  136.  
  137.     int tkwin_initted;        /* Set first time widget is mapped */
  138.     PLStream *plsc;        /* PLPlot stream pointer */
  139.     PLINT ipls;            /* PLPlot stream number */
  140.     PLINT ipls_save;        /* PLPlot stream number, save files */
  141.  
  142.     PLRDev *plr;        /* Renderer state information.  Malloc'ed */
  143.     XColor *bgColor;        /* Background color */
  144.     char *plpr_cmd;        /* Holds print command name.  Malloc'ed */
  145.  
  146.     /* Used to distinguish resize from expose events */
  147.  
  148.     int prevWidth;        /* Previous window width */
  149.     int prevHeight;        /* Previous window height */
  150.  
  151.     /* Support for save operations */
  152.  
  153.     char *SaveFnam;        /* File name we are currently saving to.
  154.                    Malloc'ed. */
  155.     char **devDesc;        /* Descriptive names for file-oriented 
  156.                  * devices.  Malloc'ed. */
  157.     char **devName;        /* Keyword names of file-oriented devices.
  158.                  * Malloc'ed. */
  159.  
  160.     /* Used in selecting & modifying plot or device area */
  161.  
  162.     GC xorGC;            /* GC used for rubber-band drawing */
  163.     XPoint pts[5];        /* Points for rubber-band drawing */
  164.     int continue_draw;        /* Set when doing rubber-band draws */
  165.     Cursor draw_cursor;        /* cursor used for drawing */
  166.     PLFLT xl, xr, yl, yr;    /* Bounds on plot viewing area */
  167.     char *yScrollCmd;        /* Command prefix for communicating with
  168.                  * vertical scrollbar.  NULL means no
  169.                  * command to issue.  Malloc'ed. */
  170.     char *xScrollCmd;        /* Command prefix for communicating with
  171.                  * horizontal scrollbar.  NULL means no
  172.                  * command to issue.  Malloc'ed. */
  173.  
  174.     /* Used for flashing bop or eop condition */
  175.  
  176.     char *bopCmd;        /* Proc to call at bop */
  177.     char *eopCmd;        /* Proc to call at eop */
  178.  
  179. } PlFrame;
  180.  
  181. /*
  182.  * Flag bits for plframes:
  183.  *
  184.  * REFRESH_PENDING:        Non-zero means a DoWhenIdle handler
  185.  *                has already been queued to refresh
  186.  *                this window.
  187.  * CLEAR_NEEDED;        Need to clear the window when redrawing.
  188.  * RESIZE_PENDING;        Used to reschedule resize events
  189.  * REDRAW_PENDING;        Used to redraw contents of plot buffer
  190.  * UPDATE_V_SCROLLBAR:        Non-zero means vertical scrollbar needs
  191.  *                to be updated.
  192.  * UPDATE_H_SCROLLBAR:        Non-zero means horizontal scrollbar needs
  193.  *                to be updated.
  194.  */
  195.  
  196. #define REFRESH_PENDING        1
  197. #define CLEAR_NEEDED        2
  198. #define RESIZE_PENDING        4
  199. #define REDRAW_PENDING        8
  200. #define UPDATE_V_SCROLLBAR    16
  201. #define UPDATE_H_SCROLLBAR    32
  202.  
  203. /*
  204.  * Defaults for plframes:
  205.  */
  206.  
  207. #define DEF_PLFRAME_BG_COLOR        "Black"
  208. #define DEF_PLFRAME_BG_MONO        "White"
  209. #define DEF_PLFRAME_BORDER_WIDTH    "0"
  210. #define DEF_PLFRAME_CURSOR        ((char *) NULL)
  211. #define DEF_PLFRAME_GEOMETRY        ((char *) NULL)
  212. #define DEF_PLFRAME_HEIGHT        "0"
  213. #define DEF_PLFRAME_RELIEF        "flat"
  214. #define DEF_PLFRAME_WIDTH        "0"
  215.  
  216. /* Configuration info */
  217.  
  218. static Tk_ConfigSpec configSpecs[] = {
  219.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  220.     DEF_PLFRAME_BG_COLOR, Tk_Offset(PlFrame, border),
  221.     TK_CONFIG_COLOR_ONLY},
  222.     {TK_CONFIG_COLOR, (char *) NULL, (char *) NULL, (char *) NULL,
  223.     (char *) NULL, Tk_Offset(PlFrame, bgColor),
  224.     TK_CONFIG_COLOR_ONLY},
  225.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  226.     DEF_PLFRAME_BG_MONO, Tk_Offset(PlFrame, border),
  227.     TK_CONFIG_MONO_ONLY},
  228.     {TK_CONFIG_COLOR, (char *) NULL, (char *) NULL, (char *) NULL,
  229.     (char *) NULL, Tk_Offset(PlFrame, bgColor),
  230.     TK_CONFIG_MONO_ONLY},
  231.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  232.     (char *) NULL, 0, 0},
  233.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  234.     (char *) NULL, 0, 0},
  235.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  236.     DEF_PLFRAME_BORDER_WIDTH, Tk_Offset(PlFrame, borderWidth), 0},
  237.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  238.     DEF_PLFRAME_CURSOR, Tk_Offset(PlFrame, cursor), TK_CONFIG_NULL_OK},
  239.     {TK_CONFIG_STRING, "-geometry", "geometry", "Geometry",
  240.     DEF_PLFRAME_GEOMETRY, Tk_Offset(PlFrame, geometry), TK_CONFIG_NULL_OK},
  241.     {TK_CONFIG_STRING, "-bopcmd", "bopcmd", "PgCommand",
  242.     (char *) NULL, Tk_Offset(PlFrame, bopCmd), TK_CONFIG_NULL_OK},
  243.     {TK_CONFIG_STRING, "-eopcmd", "eopcmd", "PgCommand",
  244.     (char *) NULL, Tk_Offset(PlFrame, eopCmd), TK_CONFIG_NULL_OK},
  245.     {TK_CONFIG_PIXELS, "-height", "height", "Height",
  246.     DEF_PLFRAME_HEIGHT, Tk_Offset(PlFrame, height), 0},
  247.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  248.     DEF_PLFRAME_RELIEF, Tk_Offset(PlFrame, relief), 0},
  249.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  250.     DEF_PLFRAME_WIDTH, Tk_Offset(PlFrame, width), 0},
  251.     {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
  252.     (char *) NULL, Tk_Offset(PlFrame, xScrollCmd), TK_CONFIG_NULL_OK},
  253.     {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
  254.     (char *) NULL, Tk_Offset(PlFrame, yScrollCmd), TK_CONFIG_NULL_OK},
  255.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  256.     (char *) NULL, 0, 0}
  257. };
  258.  
  259. /*
  260.  * Forward declarations for procedures defined later in this file:
  261.  */
  262.  
  263. /* Externals */
  264.  
  265. int   plFrameCmd         (ClientData, Tcl_Interp *, int, char **);
  266.  
  267. /* These are invoked by the TK dispatcher */
  268.  
  269. static void  DestroyPlFrame    (ClientData);
  270. static void  DisplayPlFrame    (ClientData);
  271. static void  PlFrameInit    (ClientData);
  272. static void  PlFrameEventProc    (ClientData, XEvent *);
  273. static int   PlFrameWidgetCmd    (ClientData, Tcl_Interp *, int, char **);
  274. static int   ReadData        (ClientData, int);
  275. static void  Install_cmap    (PlFrame *plFramePtr);
  276.  
  277. /* These are invoked by PlFrameWidgetCmd to process widget commands */
  278.  
  279. static int   Cmd        (Tcl_Interp *, PlFrame *, int, char **);
  280. static int   ConfigurePlFrame    (Tcl_Interp *, PlFrame *, int, char **, int);
  281. static int   Draw        (Tcl_Interp *, PlFrame *, int, char **);
  282. static int   Info        (Tcl_Interp *, PlFrame *, int, char **);
  283. static int   Openlink        (Tcl_Interp *, PlFrame *, int, char **);
  284. static int   Orient        (Tcl_Interp *, PlFrame *, int, char **);
  285. static int   Page        (Tcl_Interp *, PlFrame *, int, char **);
  286. static int   Print        (Tcl_Interp *, PlFrame *, int, char **);
  287. static int   Redraw        (Tcl_Interp *, PlFrame *, int, char **);
  288. static int   Save        (Tcl_Interp *, PlFrame *, int, char **);
  289. static int   View        (Tcl_Interp *, PlFrame *, int, char **);
  290. static int   xScroll        (Tcl_Interp *, PlFrame *, int, char **);
  291. static int   yScroll        (Tcl_Interp *, PlFrame *, int, char **);
  292.  
  293. /* Utility routines */
  294.  
  295. static void  gbox        (PLFLT *, PLFLT *, PLFLT *, PLFLT *, char **);
  296. static void  UpdateVScrollbar    (register PlFrame *);
  297. static void  UpdateHScrollbar    (register PlFrame *);
  298.  
  299. /*
  300.  *---------------------------------------------------------------------------
  301.  *
  302.  * plFrameCmd --
  303.  *
  304.  *    This procedure is invoked to process the "plframe" Tcl
  305.  *    command.  See the user documentation for details on what it
  306.  *    does.
  307.  *
  308.  * Results:
  309.  *    A standard Tcl result.
  310.  *
  311.  * Side effects:
  312.  *    See the user documentation.
  313.  *
  314.  *---------------------------------------------------------------------------
  315.  */
  316.  
  317. int
  318. plFrameCmd(ClientData clientData, Tcl_Interp *interp,
  319.        int argc, char **argv)
  320. {
  321.     Tk_Window tkwin = (Tk_Window) clientData;
  322.     Tk_Window new;
  323.     register PlFrame *plFramePtr;
  324.     register PLRDev *plr;
  325.     int i, ndev;
  326.     XGCValues gcValues;
  327.     unsigned long mask;
  328.  
  329.     dbug_enter("plFrameCmd");
  330.  
  331.     if (argc < 2) {
  332.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  333.         argv[0], " pathName ?options?\"", (char *) NULL);
  334.     return TCL_ERROR;
  335.     }
  336.  
  337.     /*
  338.      * Create the window.
  339.      */
  340.  
  341.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  342.     if (new == NULL) {
  343.     return TCL_ERROR;
  344.     }
  345.  
  346.     plFramePtr = (PlFrame *) ckalloc(sizeof(PlFrame));
  347.     plFramePtr->tkwin = new;
  348.     plFramePtr->display = Tk_Display(new);
  349.     plFramePtr->interp = interp;
  350.     plFramePtr->xorGC = NULL;
  351.     plFramePtr->border = NULL;
  352.     plFramePtr->geometry = NULL;
  353.     plFramePtr->cursor = None;
  354.     plFramePtr->draw_cursor = None;
  355.     plFramePtr->flags = 0;
  356.     plFramePtr->prevWidth = Tk_Width(plFramePtr->tkwin);
  357.     plFramePtr->prevHeight = Tk_Height(plFramePtr->tkwin);
  358.     plFramePtr->continue_draw = 0;
  359.     plFramePtr->ipls = 0;
  360.     plFramePtr->ipls_save = 0;
  361.     plFramePtr->tkwin_initted = 0;
  362.     plFramePtr->bgColor = NULL;
  363.     plFramePtr->plpr_cmd = NULL;
  364.     plFramePtr->bopCmd = NULL;
  365.     plFramePtr->eopCmd = NULL;
  366.     plFramePtr->yScrollCmd = NULL;
  367.     plFramePtr->xScrollCmd = NULL;
  368.     plFramePtr->xl = 0.;
  369.     plFramePtr->yl = 0.;
  370.     plFramePtr->xr = 1.;
  371.     plFramePtr->yr = 1.;
  372.     plFramePtr->SaveFnam = NULL;
  373.  
  374.     plFramePtr->plr = (PLRDev *) ckalloc(sizeof(PLRDev));
  375.     plr = plFramePtr->plr;
  376.     plr->pdfs = NULL;
  377.     plr->iodev = (PLiodev *) ckalloc(sizeof(PLiodev));
  378.     plr_start(plr);
  379.  
  380. /* Associate new PLplot stream with this widget */
  381.  
  382.     plmkstrm(&plFramePtr->ipls);
  383.     plgpls(&plFramePtr->plsc);
  384.  
  385. /* Set up stuff for rubber-band drawing */
  386.  
  387.     gcValues.background = 0;
  388.     gcValues.foreground = 1;
  389.     gcValues.function = GXxor;
  390.     mask = GCForeground | GCBackground | GCFunction;
  391.     plFramePtr->xorGC = Tk_GetGC(plFramePtr->tkwin, mask, &gcValues);
  392.  
  393.     plFramePtr->draw_cursor =
  394.     Tk_GetCursor(plFramePtr->interp, plFramePtr->tkwin, "crosshair");
  395.  
  396. /* Create list of valid device names and keywords for page dumps */
  397.  
  398.     plFramePtr->devDesc = (char **) ckalloc(NDEV * sizeof(char **));
  399.     plFramePtr->devName = (char **) ckalloc(NDEV * sizeof(char **));
  400.     for (i = 0; i < NDEV; i++) {
  401.     plFramePtr->devDesc[i] = NULL;
  402.     plFramePtr->devName[i] = NULL;
  403.     }
  404.     plgFileDevs(&plFramePtr->devDesc, &plFramePtr->devName, &ndev);
  405.  
  406. /* Start up event handlers and other good stuff */
  407.  
  408.     Tk_SetClass(new, "plframe");
  409.     Tk_CreateEventHandler(plFramePtr->tkwin, ExposureMask|StructureNotifyMask,
  410.               PlFrameEventProc, (ClientData) plFramePtr);
  411.  
  412.     Tcl_CreateCommand(interp, Tk_PathName(plFramePtr->tkwin),
  413.         PlFrameWidgetCmd, (ClientData) plFramePtr, (void (*)()) NULL);
  414.  
  415.     if (ConfigurePlFrame(interp, plFramePtr, argc-2, argv+2, 0) != TCL_OK) {
  416.     Tk_DestroyWindow(plFramePtr->tkwin);
  417.     return TCL_ERROR;
  418.     }
  419.     interp->result = Tk_PathName(plFramePtr->tkwin);
  420.  
  421.     return TCL_OK;
  422. }
  423.  
  424. /*
  425.  *---------------------------------------------------------------------------
  426.  *
  427.  * PlFrameWidgetCmd --
  428.  *
  429.  *    This procedure is invoked to process the Tcl command that
  430.  *    corresponds to a plframe widget.  See the user
  431.  *    documentation for details on what it does.
  432.  *
  433.  * Results:
  434.  *    A standard Tcl result.
  435.  *
  436.  * Side effects:
  437.  *    See the user documentation.
  438.  *
  439.  *---------------------------------------------------------------------------
  440.  */
  441.  
  442. static int
  443. PlFrameWidgetCmd(ClientData clientData, Tcl_Interp *interp,
  444.          int argc, char **argv)
  445. {
  446.     register PlFrame *plFramePtr = (PlFrame *) clientData;
  447.     int result = TCL_OK;
  448.     int length;
  449.     char c;
  450.  
  451.     dbug_enter("PlFrameWidgetCmd");
  452.  
  453.     if (argc < 2) {
  454.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  455.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  456.     return TCL_ERROR;
  457.     }
  458.     Tk_Preserve((ClientData) plFramePtr);
  459.     c = argv[1][0];
  460.     length = strlen(argv[1]);
  461.  
  462. /* cmd -- issue a command to the PLPlot library */
  463.  
  464.     if ((c == 'c') && (strncmp(argv[1], "cmd", length) == 0)) {
  465.     result = Cmd(interp, plFramePtr, argc-2, argv+2);
  466.     }
  467.  
  468. /* configure */
  469.  
  470.     else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  471.     if (argc == 2) {
  472.         result = Tk_ConfigureInfo(interp, plFramePtr->tkwin, configSpecs,
  473.             (char *) plFramePtr, (char *) NULL, 0);
  474.     }
  475.     else if (argc == 3) {
  476.         result = Tk_ConfigureInfo(interp, plFramePtr->tkwin, configSpecs,
  477.             (char *) plFramePtr, argv[2], 0);
  478.     }
  479.     else {
  480.         result = ConfigurePlFrame(interp, plFramePtr, argc-2, argv+2,
  481.             TK_CONFIG_ARGV_ONLY);
  482.     }
  483.     }
  484.  
  485. /* draw -- rubber-band draw used in region selection */
  486.  
  487.     else if ((c == 'd') && (strncmp(argv[1], "draw", length) == 0)) {
  488.     if (argc == 2) {
  489.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  490.             argv[0], " draw op ?options?\"", (char *) NULL);
  491.         result = TCL_ERROR;
  492.         goto done;
  493.     }
  494.     else {
  495.         result = Draw(interp, plFramePtr, argc-2, argv+2);
  496.     }
  497.     }
  498.  
  499. /* info -- returns requested info */
  500.  
  501.     else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  502.     result = Info(interp, plFramePtr, argc-2, argv+2);
  503.     }
  504.  
  505. /* orient -- Set plot orientation */
  506.  
  507.     else if ((c == 'o') && (strncmp(argv[1], "orient", length) == 0)) {
  508.     result = Orient(interp, plFramePtr, argc-2, argv+2);
  509.     }
  510.  
  511. /* openlink -- Open the data link */
  512.  
  513.     else if ((c == 'o') && (strncmp(argv[1], "openlink", length) == 0)) {
  514.     if (argc < 3) {
  515.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  516.             argv[0], " option ?arg arg ...?\"", (char *) NULL);
  517.         result = TCL_ERROR;
  518.         goto done;
  519.     }
  520.     else {
  521.         result = Openlink(interp, plFramePtr, argc-2, argv+2);
  522.     }
  523.     }
  524.  
  525. /* page -- change or return output page setup */
  526.  
  527.     else if ((c == 'p') && (strncmp(argv[1], "page", length) == 0)) {
  528.     result = Page(interp, plFramePtr, argc-2, argv+2);
  529.     }
  530.  
  531. /* print -- prints plot */
  532.  
  533.     else if ((c == 'p') && (strncmp(argv[1], "print", length) == 0)) {
  534.     result = Print(interp, plFramePtr, argc-2, argv+2);
  535.     }
  536.  
  537. /* redraw -- redraw plot */
  538.  
  539.     else if ((c == 'r') && (strncmp(argv[1], "redraw", length) == 0)) {
  540.     if (argc > 2) {
  541.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  542.             argv[0], " redraw\"", (char *) NULL);
  543.         result = TCL_ERROR;
  544.         goto done;
  545.     }
  546.     else {
  547.         result = Redraw(interp, plFramePtr, argc-2, argv+2);
  548.     }
  549.     }
  550.  
  551. /* save -- saves plot to the specified plot file type */
  552.  
  553.     else if ((c == 's') && (strncmp(argv[1], "save", length) == 0)) {
  554.     result = Save(interp, plFramePtr, argc-2, argv+2);
  555.     }
  556.  
  557. /* view -- change or return window into plot */
  558.  
  559.     else if ((c == 'v') && (strncmp(argv[1], "view", length) == 0)) {
  560.     result = View(interp, plFramePtr, argc-2, argv+2);
  561.     }
  562.  
  563. /* xscroll -- horizontally scroll window into plot */
  564.  
  565.     else if ((c == 'x') && (strncmp(argv[1], "xscroll", length) == 0)) {
  566.     if (argc == 2 || argc > 3) {
  567.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  568.             argv[0], " xscroll pixel\"", (char *) NULL);
  569.         result = TCL_ERROR;
  570.         goto done;
  571.     }
  572.     else {
  573.         result = xScroll(interp, plFramePtr, argc-2, argv+2);
  574.     }
  575.     }
  576.  
  577. /* yscroll -- vertically scroll window into plot */
  578.  
  579.     else if ((c == 'y') && (strncmp(argv[1], "yscroll", length) == 0)) {
  580.     if (argc == 2 || argc > 3) {
  581.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  582.             argv[0], " yscroll pixel\"", (char *) NULL);
  583.         result = TCL_ERROR;
  584.         goto done;
  585.     }
  586.     else {
  587.         result = yScroll(interp, plFramePtr, argc-2, argv+2);
  588.     }
  589.     }
  590.  
  591. /* unrecognized widget command */
  592.  
  593.     else {
  594.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  595.      "\":  must be cmd, configure, draw, info, ",
  596.      "openlink, orient, page, print, redraw, save, view, ",
  597.      "xscroll, or yscroll", (char *) NULL);
  598.  
  599.     result = TCL_ERROR;
  600.     }
  601.  
  602.  done:
  603.     Tk_Release((ClientData) plFramePtr);
  604.     return result;
  605. }
  606.  
  607. /*
  608.  *---------------------------------------------------------------------------
  609.  *
  610.  * DestroyPlFrame --
  611.  *
  612.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release to
  613.  *    clean up the internal structure of a plframe at a safe time
  614.  *    (when no-one is using it anymore).
  615.  *
  616.  * Results:
  617.  *    None.
  618.  *
  619.  * Side effects:
  620.  *    Everything associated with the plframe is freed up.
  621.  *
  622.  *---------------------------------------------------------------------------
  623.  */
  624.  
  625. static void
  626. DestroyPlFrame(ClientData clientData)
  627. {
  628.     register PlFrame *plFramePtr = (PlFrame *) clientData;
  629.     register PLRDev *plr = plFramePtr->plr;
  630.  
  631.     dbug_enter("DestroyPlFrame");
  632.  
  633.     if (plFramePtr->border != NULL) {
  634.     Tk_Free3DBorder(plFramePtr->border);
  635.     }
  636.     if (plFramePtr->bgColor != NULL) {
  637.     Tk_FreeColor(plFramePtr->bgColor);
  638.     }
  639.     if (plFramePtr->plpr_cmd != NULL) {
  640.     ckfree((char *) plFramePtr->plpr_cmd);
  641.     }
  642.     if (plFramePtr->geometry != NULL) {
  643.     ckfree((char *) plFramePtr->geometry);
  644.     }
  645.     if (plFramePtr->cursor != None) {
  646.     Tk_FreeCursor(plFramePtr->display, plFramePtr->cursor);
  647.     }
  648.     if (plFramePtr->draw_cursor != None) {
  649.     Tk_FreeCursor(plFramePtr->display, plFramePtr->draw_cursor);
  650.     }
  651.     if (plFramePtr->xorGC != NULL) {
  652.     Tk_FreeGC(plFramePtr->display, plFramePtr->xorGC);
  653.     }
  654.     if (plFramePtr->yScrollCmd != NULL) {
  655.     ckfree((char *) plFramePtr->yScrollCmd);
  656.     }
  657.     if (plFramePtr->xScrollCmd != NULL) {
  658.     ckfree((char *) plFramePtr->xScrollCmd);
  659.     }
  660.     if (plFramePtr->SaveFnam != NULL) {
  661.     ckfree((char *) plFramePtr->devDesc);
  662.     }
  663.     if (plFramePtr->devDesc != NULL) {
  664.     ckfree((char *) plFramePtr->devDesc);
  665.     }
  666.     if (plFramePtr->devName != NULL) {
  667.     ckfree((char *) plFramePtr->devName);
  668.     }
  669.  
  670. /* Clean up data connection */
  671.  
  672.     pdf_close(plr->pdfs);
  673.     ckfree((char *) plFramePtr->plr->iodev);
  674.  
  675. /* Delete main data structures */
  676.  
  677.     ckfree((char *) plFramePtr->plr);
  678.     ckfree((char *) plFramePtr);
  679.  
  680. /* Tell PLPlot to clean up */
  681.  
  682.     plsstrm( plFramePtr->ipls );
  683.     plend1();
  684. }
  685.  
  686. /*
  687.  *---------------------------------------------------------------------------
  688.  *
  689.  * PlFrameEventProc --
  690.  *
  691.  *    Invoked by the Tk dispatcher on exposes and structure
  692.  *    changes to a plframe. 
  693.  *
  694.  * Results:
  695.  *    None.
  696.  *
  697.  * Side effects:
  698.  *    When the window gets deleted, internal structures get cleaned up.
  699.  *    When it gets exposed, it is redisplayed.
  700.  *
  701.  *---------------------------------------------------------------------------
  702.  */
  703.  
  704. static void
  705. PlFrameEventProc(ClientData clientData, register XEvent *eventPtr)
  706. {
  707.     register PlFrame *plFramePtr = (PlFrame *) clientData;
  708.  
  709.     dbug_enter("PlFrameEventProc");
  710.  
  711.     switch (eventPtr->type) {
  712.     case Expose:
  713.     if (eventPtr->xexpose.count == 0) {
  714.         if ((plFramePtr->tkwin != NULL) &&
  715.         !(plFramePtr->flags & REFRESH_PENDING)) {
  716.  
  717.         Tk_DoWhenIdle(DisplayPlFrame, (ClientData) plFramePtr);
  718.         plFramePtr->flags |= REFRESH_PENDING;
  719.         }
  720.     }
  721.     break;
  722.  
  723.     case ConfigureNotify:
  724.     plFramePtr->flags |= RESIZE_PENDING;
  725.     if ((plFramePtr->tkwin != NULL) &&
  726.         !(plFramePtr->flags & REFRESH_PENDING)) {
  727.  
  728.         Tk_DoWhenIdle(DisplayPlFrame, (ClientData) plFramePtr);
  729.         plFramePtr->flags |= REFRESH_PENDING;
  730.         plFramePtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
  731.     }
  732.     break;
  733.  
  734.     case DestroyNotify:
  735.     Tcl_DeleteCommand(plFramePtr->interp, Tk_PathName(plFramePtr->tkwin));
  736.     plFramePtr->tkwin = NULL;
  737.     if (plFramePtr->flags & REFRESH_PENDING) {
  738.         Tk_CancelIdleCall(DisplayPlFrame, (ClientData) plFramePtr);
  739.     }
  740.     Tk_EventuallyFree((ClientData) plFramePtr, DestroyPlFrame);
  741.     break;
  742.  
  743.     case MapNotify:
  744.     if (plFramePtr->flags & REFRESH_PENDING) {
  745.         Tk_CancelIdleCall(DisplayPlFrame, (ClientData) plFramePtr);
  746.     }
  747.  
  748. /* For some strange reason, "." must be mapped or PlFrameInit will die */
  749. /* (Note: mapped & withdrawn or mapped in the withdrawn state is OK */
  750. /* Issuing an update fixes this.  I'd love to know why this occurs. */
  751.  
  752.     if ( ! plFramePtr->tkwin_initted) {
  753.         Tcl_VarEval(plFramePtr->interp, "update", (char *) NULL);
  754.     }
  755.     Tk_DoWhenIdle(PlFrameInit, (ClientData) plFramePtr);
  756.     break;
  757.     }
  758. }
  759.  
  760. /*
  761.  *---------------------------------------------------------------------------
  762.  *
  763.  * PlFrameInit --
  764.  *
  765.  *    Invoked to handle miscellaneous initialization after window gets
  766.  *    mapped.  
  767.  *
  768.  * Results:
  769.  *    None.
  770.  *
  771.  * Side effects:
  772.  *    PLPlot internal parameters and device driver are initialized.
  773.  *
  774.  *---------------------------------------------------------------------------
  775.  */
  776.  
  777. static void
  778. PlFrameInit(ClientData clientData)
  779. {
  780.     register PlFrame *plFramePtr = (PlFrame *) clientData;
  781.     register Tk_Window tkwin = plFramePtr->tkwin;
  782.  
  783. /* Set up window parameters and arrange for window to be refreshed */
  784.  
  785.     plFramePtr->flags &= REFRESH_PENDING;
  786.     plFramePtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
  787.  
  788. /* First-time initialization */
  789.  
  790.     if ( ! plFramePtr->tkwin_initted) {
  791.     plsstrm(plFramePtr->ipls);
  792.     plsdev("xwin");
  793.     plsxwin(Tk_WindowId(tkwin));
  794.     plspause(0);
  795.     plinit();
  796.     if (plplot_ccmap)
  797.         Install_cmap(plFramePtr);
  798.  
  799.     pladv(0);
  800.  
  801.     plFramePtr->tkwin_initted = 1;
  802.     plFramePtr->prevWidth = Tk_Width(tkwin);
  803.     plFramePtr->prevHeight = Tk_Height(tkwin);
  804.     }
  805.     else {
  806.     plFramePtr->flags |= RESIZE_PENDING;
  807.     }
  808.  
  809. /* Draw plframe */
  810.  
  811.     DisplayPlFrame(clientData);
  812. }
  813.  
  814. /*
  815.  *---------------------------------------------------------------------------
  816.  *
  817.  * Install_cmap --
  818.  *
  819.  *    Installs X driver color map as necessary when custom color maps
  820.  *    are used. 
  821.  *
  822.  * Results:
  823.  *    None.
  824.  *
  825.  * Side effects:
  826.  *    Parent color maps may get changed.
  827.  *
  828.  *---------------------------------------------------------------------------
  829.  */
  830.  
  831. static void
  832. Install_cmap(PlFrame *plFramePtr)
  833. {
  834.     XwDev *dev;
  835.  
  836. #define INSTALL_COLORMAP_IN_TK
  837. #ifdef  INSTALL_COLORMAP_IN_TK
  838.     dev = (XwDev *) plFramePtr->plsc->dev;
  839.     Tk_SetWindowColormap(Tk_MainWindow(plFramePtr->interp), dev->xwd->map);
  840.  
  841. /*
  842.  * If the colormap is local to this widget, the WM must be informed that
  843.  * it should be installed when the widget gets the focus.  The top level
  844.  * window must be added to the end of its own list, because otherwise the
  845.  * window manager adds it to the front (as required by the ICCCM).  Thanks
  846.  * to Paul Mackerras for providing this info in his TK photo widget.
  847.  */
  848.  
  849. #else
  850.     int count = 0;
  851.     Window top, colormap_windows[5];
  852.  
  853.     top = Tk_WindowId(Tk_MainWindow(plFramePtr->interp));
  854.  
  855.     colormap_windows[count++] = Tk_WindowId(plFramePtr->tkwin);
  856.     colormap_windows[count++] = top;
  857.  
  858.     if ( ! XSetWMColormapWindows(plFramePtr->display,
  859.                  top, colormap_windows, count))
  860.       fprintf(stderr, "Unable to set color map property!\n");
  861. #endif
  862. }
  863.  
  864. /*
  865.  *---------------------------------------------------------------------------
  866.  *
  867.  * DisplayPlFrame --
  868.  *
  869.  *    This procedure is invoked to display a plframe widget.
  870.  *
  871.  * Results:
  872.  *    None.
  873.  *
  874.  * Side effects:
  875.  *    Commands are output to X to display the plframe in its
  876.  *    current mode.
  877.  *
  878.  *---------------------------------------------------------------------------
  879.  */
  880.  
  881. static void
  882. DisplayPlFrame(ClientData clientData)
  883. {
  884.     register PlFrame *plFramePtr = (PlFrame *) clientData;
  885.     register Tk_Window tkwin = plFramePtr->tkwin;
  886.     PLWindow window;
  887.  
  888.     dbug_enter("DisplayPlFrame");
  889.  
  890. /* Update scrollbars if needed */
  891.  
  892.     if (plFramePtr->flags & UPDATE_V_SCROLLBAR) {
  893.     UpdateVScrollbar(plFramePtr);
  894.     }
  895.     if (plFramePtr->flags & UPDATE_H_SCROLLBAR) {
  896.     UpdateHScrollbar(plFramePtr);
  897.     }
  898.     plFramePtr->flags &= ~(UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR);
  899.  
  900. /* If not mapped yet, return and cancel pending refresh */
  901.  
  902.     if ((plFramePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  903.     plFramePtr->flags &= ~REFRESH_PENDING;
  904.     return;
  905.     }
  906.  
  907. /* Clear window if needed */
  908.  
  909.     if (plFramePtr->flags & CLEAR_NEEDED) {
  910.     XClearWindow(plFramePtr->display, Tk_WindowId(tkwin));
  911.     plFramePtr->flags &= ~CLEAR_NEEDED;
  912.     }
  913.  
  914. /* Redraw border if necessary */
  915.  
  916.     if ((plFramePtr->border != NULL)
  917.         && (plFramePtr->relief != TK_RELIEF_FLAT)) {
  918.     Tk_Draw3DRectangle(plFramePtr->display, Tk_WindowId(tkwin),
  919.         plFramePtr->border, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  920.         plFramePtr->borderWidth, plFramePtr->relief);
  921.     }
  922.  
  923. /* All refresh events */
  924.  
  925.     if (plFramePtr->flags & REFRESH_PENDING) {
  926.     plFramePtr->flags &= ~REFRESH_PENDING;
  927.  
  928. /* Reschedule resizes to avoid occasional ordering conflicts with */
  929. /* the packer's resize of the window (this call must come last). */
  930.  
  931.     if (plFramePtr->flags & RESIZE_PENDING) {
  932.         plFramePtr->flags |= REFRESH_PENDING;
  933.         plFramePtr->flags &= ~RESIZE_PENDING;
  934.         Tk_DoWhenIdle(DisplayPlFrame, clientData);
  935.         return;
  936.     }
  937.  
  938. /* Redraw -- replay contents of plot buffer */
  939.  
  940.     if (plFramePtr->flags & REDRAW_PENDING) {
  941.         plFramePtr->flags &= ~REDRAW_PENDING;
  942.         plsstrm(plFramePtr->ipls);
  943.         pl_cmd(PLESC_REDRAW, (void *) NULL);
  944.         return;
  945.     }
  946.  
  947. /* Resize -- if window bounds have changed */
  948.  
  949.     if ((plFramePtr->prevWidth != Tk_Width(tkwin)) ||
  950.         (plFramePtr->prevHeight != Tk_Height(tkwin))) {
  951.  
  952.         window.width = Tk_Width(tkwin);
  953.         window.height = Tk_Height(tkwin);
  954.  
  955.         plsstrm(plFramePtr->ipls);
  956.         pl_cmd(PLESC_RESIZE, (void *) &window);
  957.         plFramePtr->prevWidth = Tk_Width(tkwin);
  958.         plFramePtr->prevHeight = Tk_Height(tkwin);
  959.     }
  960.  
  961. /* Expose -- if window bounds are unchanged */
  962.  
  963.     else {
  964.         plsstrm(plFramePtr->ipls);
  965.         pl_cmd(PLESC_EXPOSE, NULL);
  966.     }
  967.     }
  968. }
  969.  
  970. /*----------------------------------------------------------------------*\
  971.  * Routines to process widget commands.
  972. \*----------------------------------------------------------------------*/
  973.  
  974. /*----------------------------------------------------------------------*\
  975.  * Cmd
  976.  *
  977.  * Processes "cmd" widget command.
  978.  * Handles commands that go more or less directly to the PLPlot library.
  979.  * Most of these come out of the PLplot Tcl API support file.
  980. \*----------------------------------------------------------------------*/
  981.  
  982. static int
  983. Cmd(Tcl_Interp *interp, register PlFrame *plFramePtr,
  984.     int argc, char **argv)
  985. {
  986.     PLStream *plsc = plFramePtr->plsc;
  987.     int length;
  988.     char c3;
  989.     int result = TCL_OK;
  990.     char cmdlist[] = "plgcmap0 plgcmap1 plscmap0 plscmap1 plscol0 plscol1";
  991.  
  992. /* no option -- return list of available PLPlot commands */
  993.  
  994.     if (argc == 0) 
  995.     return plTclCmd(cmdlist, interp, argc, argv);
  996.  
  997.     c3 = argv[0][2];
  998.     length = strlen(argv[0]);
  999.     plsstrm(plFramePtr->ipls);
  1000.  
  1001. /* plgcmap0 -- get color map 0 */
  1002. /* first arg is number of colors, the rest are hex number specifications */
  1003.  
  1004.     if ((c3 == 'g') && (strncmp(argv[0], "plgcmap0", length) == 0)) {
  1005.     int i;
  1006.     unsigned long plcolor;
  1007.     char str[10];
  1008.  
  1009.     sprintf(str, "%d", (int) plsc->ncol0);
  1010.     Tcl_AppendElement(interp, str);
  1011.     for (i = 0; i < plsc->ncol0; i++) {
  1012.         plcolor = ((plsc->cmap0[i].r << 16) | 
  1013.                (plsc->cmap0[i].g << 8) |
  1014.                (plsc->cmap0[i].b));
  1015.  
  1016.         sprintf(str, "#%06lx", (plcolor & 0xFFFFFF));
  1017.         Tcl_AppendElement(interp, str);
  1018.     }
  1019.     result = TCL_OK;
  1020.     }
  1021.  
  1022. /* plgcmap1 -- get color map 1 */
  1023. /* first arg is number of control points */
  1024. /* the rest are hex number specifications followed by positions (0-100) */
  1025.  
  1026.     else if ((c3 == 'g') && (strncmp(argv[0], "plgcmap1", length) == 0)) {
  1027.     int i;
  1028.     unsigned long plcolor;
  1029.     char str[10];
  1030.     PLFLT h, l, s, r, g, b;
  1031.     int r1, g1, b1;
  1032.  
  1033.     sprintf(str, "%d", (int) plsc->ncp1);
  1034.     Tcl_AppendElement(interp, str);
  1035.     for (i = 0; i < plsc->ncp1; i++) {
  1036.         h = plsc->cmap1cp[i].h;
  1037.         l = plsc->cmap1cp[i].l;
  1038.         s = plsc->cmap1cp[i].s;
  1039.  
  1040.         plHLS_RGB(h, l, s, &r, &g, &b);
  1041.  
  1042.         r1 = MAX(0, MIN(255, (int) (256. * r)));
  1043.         g1 = MAX(0, MIN(255, (int) (256. * g)));
  1044.         b1 = MAX(0, MIN(255, (int) (256. * b)));
  1045.  
  1046.         plcolor = ((r1 << 16) | (g1 << 8) | (b1));
  1047.  
  1048.         sprintf(str, "#%06lx", (plcolor & 0xFFFFFF));
  1049.         Tcl_AppendElement(interp, str);
  1050.  
  1051.         sprintf(str, "%02d", (int) (100*plsc->cmap1cp[i].p));
  1052.         Tcl_AppendElement(interp, str);
  1053.     }
  1054.     result = TCL_OK;
  1055.     }
  1056.  
  1057. /* plscmap0 -- set color map 0 */
  1058. /* first arg is number of colors, the rest are hex number specifications */
  1059.  
  1060.     else if ((c3 == 's') && (strncmp(argv[0], "plscmap0", length) == 0)) {
  1061.     int i, j = 1, status, ncol0 = atoi(argv[j]);
  1062.     char *color;
  1063.     XColor xcolor;
  1064.  
  1065.     if (ncol0 > 16 || ncol0 < 1) {
  1066.         Tcl_AppendResult(interp, "illegal number of colors in cmap0: ",
  1067.                  argv[1], (char *) NULL);
  1068.         return TCL_ERROR;
  1069.     }
  1070.  
  1071.     plsc->ncol0 = ncol0;
  1072.     color = strtok(argv[2], " ");
  1073.     for (i = 0; i < plsc->ncol0; i++) {
  1074.         if ( color == NULL )
  1075.         break;
  1076.  
  1077.         status = XParseColor(plFramePtr->display,
  1078.                  Tk_Colormap(plFramePtr->tkwin),
  1079.                  color, &xcolor);
  1080.  
  1081.         if ( ! status) {
  1082.         fprintf(stderr, "Couldn't parse color %s\n", color);
  1083.         break;
  1084.         }
  1085.  
  1086.         plsc->cmap0[i].r = (unsigned) (xcolor.red   & 0xFF00) >> 8;
  1087.         plsc->cmap0[i].g = (unsigned) (xcolor.green & 0xFF00) >> 8;
  1088.         plsc->cmap0[i].b = (unsigned) (xcolor.blue  & 0xFF00) >> 8;
  1089.  
  1090.         color = strtok(NULL, " ");
  1091.     }
  1092.  
  1093.     plP_state(PLSTATE_CMAP0);
  1094.     }
  1095.  
  1096. /* plscmap1 -- set color map 1 */
  1097. /* first arg is number of colors, the rest are hex number specifications */
  1098.  
  1099.     else if ((c3 == 's') && (strncmp(argv[0], "plscmap1", length) == 0)) {
  1100.     int i, j = 1, status, ncp1 = atoi(argv[j]);
  1101.     char *color, *pos;
  1102.     XColor xcolor;
  1103.     PLFLT r[32], g[32], b[32], l[32];
  1104.  
  1105.     if (ncp1 > 32 || ncp1 < 1) {
  1106.         Tcl_AppendResult(interp,
  1107.                  "illegal number of control points in cmap1: ",
  1108.                  argv[1], (char *) NULL);
  1109.         return TCL_ERROR;
  1110.     }
  1111.  
  1112.     color = strtok(argv[2], " ");
  1113.     pos = strtok(NULL, " ");
  1114.     for (i = 0; i < ncp1; i++) {
  1115.         if ( color == NULL )
  1116.         break;
  1117.  
  1118.         status = XParseColor(plFramePtr->display,
  1119.                  Tk_Colormap(plFramePtr->tkwin),
  1120.                  color, &xcolor);
  1121.  
  1122.         if ( ! status) {
  1123.         Tcl_AppendResult(interp, "Couldn't parse color ", color,
  1124.                  (char *) NULL);
  1125.         return TCL_ERROR;
  1126.         }
  1127.  
  1128.         r[i] = ((PLFLT) ((unsigned) (xcolor.red   & 0xFF00) >> 8)) / 255.0;
  1129.         g[i] = ((PLFLT) ((unsigned) (xcolor.green & 0xFF00) >> 8)) / 255.0;
  1130.         b[i] = ((PLFLT) ((unsigned) (xcolor.blue  & 0xFF00) >> 8)) / 255.0;
  1131.         l[i] = atof(pos) / 100.0;
  1132.  
  1133.         color = strtok(NULL, " ");
  1134.         pos = strtok(NULL, " ");
  1135.     }
  1136.  
  1137.     plscmap1l(1, ncp1, l, r, g, b);
  1138.     }
  1139.  
  1140. /* plscol0 -- set single color in cmap0 */
  1141. /* first arg is the color number, the next is the color in hex */
  1142.  
  1143.     else if ((c3 == 's') && (strncmp(argv[0], "plscol0", length) == 0)) {
  1144.     int i = atoi(argv[1]), status;
  1145.     char *color = argv[2];
  1146.     XColor xcolor;
  1147.     PLINT r, g, b;
  1148.  
  1149.     if (i > plsc->ncol0 || i < 0) {
  1150.         Tcl_AppendResult(interp, "illegal color number in cmap0: ",
  1151.                  argv[1], (char *) NULL);
  1152.         return TCL_ERROR;
  1153.     }
  1154.  
  1155.     if ( color == NULL ) {
  1156.         Tcl_AppendResult(interp, "color value not specified",
  1157.                  (char *) NULL);
  1158.         return TCL_ERROR;
  1159.     }
  1160.  
  1161.     status = XParseColor(plFramePtr->display,
  1162.                  Tk_Colormap(plFramePtr->tkwin),
  1163.                  color, &xcolor);
  1164.  
  1165.     if ( ! status) {
  1166.         Tcl_AppendResult(interp, "Couldn't parse color ", color,
  1167.                  (char *) NULL);
  1168.         return TCL_ERROR;
  1169.     }
  1170.  
  1171.     r = (unsigned) (xcolor.red   & 0xFF00) >> 8;
  1172.     g = (unsigned) (xcolor.green & 0xFF00) >> 8;
  1173.     b = (unsigned) (xcolor.blue  & 0xFF00) >> 8;
  1174.  
  1175.     if ( (plsc->cmap0[i].r != r) ||
  1176.          (plsc->cmap0[i].g != g) ||
  1177.          (plsc->cmap0[i].b != b) ) {
  1178.  
  1179.         plsc->cmap0[i].r = r;
  1180.         plsc->cmap0[i].g = g;
  1181.         plsc->cmap0[i].b = b;
  1182.  
  1183.         plP_state(PLSTATE_CMAP0);
  1184.     }
  1185.     }
  1186.  
  1187. /* plscol1 -- set color of control point in cmap1 */
  1188. /* first arg is the color number, the next two are the color in hex and pos */
  1189.  
  1190.     else if ((c3 == 's') && (strncmp(argv[0], "plscol1", length) == 0)) {
  1191.     int i = atoi(argv[1]), status;
  1192.     char *color, *pos;
  1193.     XColor xcolor;
  1194.     PLFLT h, l, s, r, g, b, p;
  1195.  
  1196.     if (i > plsc->ncp1 || i < 0) {
  1197.         Tcl_AppendResult(interp, "illegal control point number in cmap1: ",
  1198.                  argv[1], (char *) NULL);
  1199.         return TCL_ERROR;
  1200.     }
  1201.  
  1202.     if ( (color = argv[2]) == NULL ) {
  1203.         Tcl_AppendResult(interp, "color value not specified",
  1204.                  (char *) NULL);
  1205.         return TCL_ERROR;
  1206.     }
  1207.  
  1208.     if ( (pos = argv[3]) == NULL ) {
  1209.         Tcl_AppendResult(interp, "control point position not specified",
  1210.                  (char *) NULL);
  1211.         return TCL_ERROR;
  1212.     }
  1213.  
  1214.     status = XParseColor(plFramePtr->display,
  1215.                  Tk_Colormap(plFramePtr->tkwin),
  1216.                  color, &xcolor);
  1217.  
  1218.     if ( ! status) {
  1219.         Tcl_AppendResult(interp, "Couldn't parse color ", color,
  1220.                  (char *) NULL);
  1221.         return TCL_ERROR;
  1222.     }
  1223.  
  1224.     r = ((unsigned) (xcolor.red   & 0xFF00) >> 8) / 255.0;
  1225.     g = ((unsigned) (xcolor.green & 0xFF00) >> 8) / 255.0;
  1226.     b = ((unsigned) (xcolor.blue  & 0xFF00) >> 8) / 255.0;
  1227.  
  1228.     plRGB_HLS(r, g, b, &h, &l, &s);
  1229.  
  1230.     p = atof(pos) / 100.0;
  1231.  
  1232.     if ( (plsc->cmap1cp[i].h != h) ||
  1233.          (plsc->cmap1cp[i].l != l) ||
  1234.          (plsc->cmap1cp[i].s != s) ||
  1235.          (plsc->cmap1cp[i].p != p) ) {
  1236.          
  1237.         plsc->cmap1cp[i].h = h;
  1238.         plsc->cmap1cp[i].l = l;
  1239.         plsc->cmap1cp[i].s = s;
  1240.         plsc->cmap1cp[i].p = p;
  1241.  
  1242.         plcmap1_calc();
  1243.         plP_state(PLSTATE_CMAP0);
  1244.     }
  1245.     }
  1246.  
  1247. /* unrecognized, give it to plTclCmd to take care of */
  1248.  
  1249.     else 
  1250.     result = plTclCmd(cmdlist, interp, argc, argv);
  1251.  
  1252.     plflush();
  1253.     return result;
  1254. }
  1255.  
  1256. /*
  1257.  *---------------------------------------------------------------------------
  1258.  *
  1259.  * ConfigurePlFrame --
  1260.  *
  1261.  *    This procedure is called to process an argv/argc list, plus the Tk
  1262.  *    option database, in order to configure (or reconfigure) a 
  1263.  *    plframe widget.
  1264.  *
  1265.  * Results:
  1266.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  1267.  *    returned, then interp->result contains an error message.
  1268.  *
  1269.  * Side effects:
  1270.  *    Configuration information, such as text string, colors, font, etc.
  1271.  *    get set for plFramePtr; old resources get freed, if there were
  1272.  *    any.
  1273.  *
  1274.  *---------------------------------------------------------------------------
  1275.  */
  1276.  
  1277. static int
  1278. ConfigurePlFrame(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1279.          int argc, char **argv, int flags)
  1280. {
  1281.     PLColor plbg;
  1282.  
  1283. #ifdef DEBUG
  1284.     int i;
  1285.     fprintf(stderr, "Arguments to configure are:");
  1286.     for (i = 0; i < argc; i++) {
  1287.     fprintf(stderr, " %s", argv[i]);
  1288.     }
  1289.     fprintf(stderr, "\n");
  1290. #endif
  1291.  
  1292.     dbug_enter("ConfigurePlFrame");
  1293.  
  1294.     if (Tk_ConfigureWidget(interp, plFramePtr->tkwin, configSpecs,
  1295.         argc, argv, (char *) plFramePtr, flags) != TCL_OK) {
  1296.     return TCL_ERROR;
  1297.     }
  1298.  
  1299. /* Set background color.  Need to store in the PLPlot stream structure */
  1300. /* since redraws are handled from the PLPlot X driver. */
  1301.  
  1302.     Tk_SetBackgroundFromBorder(plFramePtr->tkwin, plFramePtr->border);
  1303.  
  1304.     plsstrm(plFramePtr->ipls);
  1305.     PLColor_from_XColor(&plbg, plFramePtr->bgColor);
  1306.     plscolbg(plbg.r, plbg.g, plbg.b);
  1307.  
  1308.     Tk_SetInternalBorder(plFramePtr->tkwin, plFramePtr->borderWidth);
  1309.     if (plFramePtr->geometry != NULL) {
  1310.     int height, width;
  1311.  
  1312.     if (sscanf(plFramePtr->geometry, "%dx%d", &width, &height) != 2) {
  1313.         Tcl_AppendResult(interp, "bad geometry \"", plFramePtr->geometry,
  1314.             "\": expected widthxheight", (char *) NULL);
  1315.         return TCL_ERROR;
  1316.     }
  1317.     Tk_GeometryRequest(plFramePtr->tkwin, width, height);
  1318.     }
  1319.     else if ((plFramePtr->width > 0) && (plFramePtr->height > 0)) {
  1320.     Tk_GeometryRequest(plFramePtr->tkwin, plFramePtr->width,
  1321.         plFramePtr->height);
  1322.     }
  1323.  
  1324.     if (Tk_IsMapped(plFramePtr->tkwin)
  1325.         && !(plFramePtr->flags & REFRESH_PENDING)) {
  1326.     Tk_DoWhenIdle(DisplayPlFrame, (ClientData) plFramePtr);
  1327.     plFramePtr->flags |= REFRESH_PENDING|CLEAR_NEEDED;
  1328.     plFramePtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
  1329.     }
  1330.     return TCL_OK;
  1331. }
  1332.  
  1333. /*----------------------------------------------------------------------*\
  1334.  * Draw
  1335.  *
  1336.  * Processes "draw" widget command.
  1337.  * Handles rubber-band drawing.
  1338. \*----------------------------------------------------------------------*/
  1339.  
  1340. static int
  1341. Draw(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1342.      int argc, char **argv)
  1343. {
  1344.     register Tk_Window tkwin = plFramePtr->tkwin;
  1345.     int result = TCL_OK;
  1346.     char c = argv[0][0];
  1347.     int length = strlen(argv[0]);
  1348.  
  1349. /* init -- sets up for rubber-band drawing */
  1350.  
  1351.     if ((c == 'i') && (strncmp(argv[0], "init", length) == 0)) {
  1352.     Tk_DefineCursor(tkwin, plFramePtr->draw_cursor);
  1353.     }
  1354.  
  1355. /* end -- ends rubber-band drawing */
  1356.  
  1357.     else if ((c == 'e') && (strncmp(argv[0], "end", length) == 0)) {
  1358.  
  1359.     Tk_DefineCursor(tkwin, plFramePtr->cursor);
  1360.     plFramePtr->continue_draw = 0;
  1361.     }
  1362.  
  1363. /* rect -- draw a rectangle, used to select rectangular areas */
  1364. /* first draw erases old outline */
  1365.  
  1366.     else if ((c == 'r') && (strncmp(argv[0], "rect", length) == 0)) {
  1367.     if (argc < 5) {
  1368.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1369.                  " draw rect x0 y0 x1 y1\"", (char *) NULL);
  1370.         result = TCL_ERROR;
  1371.     }
  1372.     else {
  1373.         int x0, y0, x1, y1;
  1374.         int xmin = 0, xmax = Tk_Width(tkwin) - 1;
  1375.         int ymin = 0, ymax = Tk_Height(tkwin) - 1;
  1376.  
  1377.         x0 = atoi(argv[1]);
  1378.         y0 = atoi(argv[2]);
  1379.         x1 = atoi(argv[3]);
  1380.         y1 = atoi(argv[4]);
  1381.  
  1382.         x0 = MAX(xmin, MIN(xmax, x0));
  1383.         y0 = MAX(ymin, MIN(ymax, y0));
  1384.         x1 = MAX(xmin, MIN(xmax, x1));
  1385.         y1 = MAX(ymin, MIN(ymax, y1));
  1386.  
  1387.         if (plFramePtr->continue_draw) {
  1388.         XDrawLines(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1389.                plFramePtr->xorGC, plFramePtr->pts, 5,
  1390.                CoordModeOrigin);
  1391.         XSync(Tk_Display(tkwin), 0);
  1392.         }
  1393.  
  1394.         plFramePtr->pts[0].x = x0; plFramePtr->pts[0].y = y0;
  1395.         plFramePtr->pts[1].x = x1; plFramePtr->pts[1].y = y0;
  1396.         plFramePtr->pts[2].x = x1; plFramePtr->pts[2].y = y1;
  1397.         plFramePtr->pts[3].x = x0; plFramePtr->pts[3].y = y1;
  1398.         plFramePtr->pts[4].x = x0; plFramePtr->pts[4].y = y0;
  1399.  
  1400.         XDrawLines(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1401.                plFramePtr->xorGC, plFramePtr->pts, 5,
  1402.                CoordModeOrigin);
  1403.         XSync(Tk_Display(tkwin), 0);
  1404.  
  1405.         plFramePtr->continue_draw = 1;
  1406.     }
  1407.     }
  1408.  
  1409.     return result;
  1410. }
  1411.  
  1412. /*----------------------------------------------------------------------*\
  1413.  * Info
  1414.  *
  1415.  * Processes "info" widget command.
  1416.  * Returns requested info.
  1417. \*----------------------------------------------------------------------*/
  1418.  
  1419. static int
  1420. Info(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1421.      int argc, char **argv)
  1422. {
  1423.     int length;
  1424.     char c;
  1425.     int result = TCL_OK;
  1426.  
  1427. /* no option -- return list of available info commands */
  1428.  
  1429.     if (argc == 0) {
  1430.     Tcl_SetResult(interp, "devices", TCL_STATIC);
  1431.     return TCL_OK;
  1432.     }
  1433.  
  1434.     c = argv[0][0];
  1435.     length = strlen(argv[0]);
  1436.  
  1437. /* devices -- return list of supported device types */
  1438.  
  1439.     if ((c == 'd') && (strncmp(argv[0], "devices", length) == 0)) {
  1440.     int i = 0;
  1441.     while (plFramePtr->devDesc[i] != NULL) 
  1442.         Tcl_AppendElement(interp, plFramePtr->devDesc[i++]);
  1443.  
  1444.     result = TCL_OK;
  1445.     }
  1446.  
  1447. /* unrecognized */
  1448.  
  1449.     else {
  1450.     Tcl_AppendResult(interp, "bad option to \"info\": must be ", 
  1451.      "devices", (char *) NULL);
  1452.  
  1453.     result = TCL_ERROR;
  1454.     }
  1455.  
  1456.     return result;
  1457. }
  1458.  
  1459. /*----------------------------------------------------------------------*\
  1460.  * Openlink
  1461.  *
  1462.  * Processes "openlink" widget command.
  1463.  * Opens channel (FIFO or socket) for data transfer between client and
  1464.  * server.
  1465. \*----------------------------------------------------------------------*/
  1466.  
  1467. static int
  1468. Openlink(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1469.      int argc, char **argv)
  1470. {
  1471.     register PLRDev *plr = plFramePtr->plr;
  1472.     register PLiodev *iodev = plr->iodev;
  1473.  
  1474.     char c = argv[0][0];
  1475.     int length = strlen(argv[0]);
  1476.  
  1477.     dbug_enter("Openlink");
  1478.  
  1479. /* Open fifo */
  1480.  
  1481.     if ((c == 'f') && (strncmp(argv[0], "fifo", length) == 0)) {
  1482.  
  1483.     if (argc < 1) {
  1484.         Tcl_AppendResult(interp, "bad command -- must be: ",
  1485.                  "openlink fifo <pathname>",
  1486.                  (char *) NULL);
  1487.         return TCL_ERROR;
  1488.     }
  1489.     if ((iodev->fd = open (argv[1], O_RDONLY)) == -1) {
  1490.         Tcl_AppendResult(interp, "cannot open fifo ", argv[1],
  1491.                  " for read", (char *) NULL);
  1492.         return TCL_ERROR;
  1493.     }
  1494.     iodev->type = 0;
  1495.     iodev->typename = "fifo";
  1496.     iodev->file = fdopen(iodev->fd, "rb");
  1497.     }
  1498.  
  1499. /* Open socket */
  1500.  
  1501.     else if ((c == 's') && (strncmp(argv[0], "socket", length) == 0)) {
  1502.  
  1503.     if (argc < 1) {
  1504.         Tcl_AppendResult(interp, "bad command -- must be: ",
  1505.                  "openlink socket <sock-id>",
  1506.                  (char *) NULL);
  1507.         return TCL_ERROR;
  1508.     }
  1509.     iodev->type = 1;
  1510.     iodev->typename = "socket";
  1511.     iodev->filehandle = argv[1];
  1512.     if (Tcl_GetOpenFile(interp, iodev->filehandle,
  1513.                 0, 1, &iodev->file) != TCL_OK) {
  1514.         return TCL_ERROR;
  1515.     }
  1516.     iodev->fd = fileno(iodev->file);
  1517.     }
  1518.  
  1519. /* unrecognized */
  1520.  
  1521.     else {
  1522.     Tcl_AppendResult(interp, "bad option to \"openlink\": must be ", 
  1523.      "fifo or socket", (char *) NULL);
  1524.  
  1525.     return TCL_ERROR;
  1526.     }
  1527.  
  1528.     plr->pdfs = pdf_bopen( NULL, 4200 );
  1529.     Tk_CreateFileHandler(iodev->fd, TK_READABLE, (Tk_FileProc *) ReadData,
  1530.              (ClientData) plFramePtr);
  1531.  
  1532.     return TCL_OK;
  1533. }
  1534.  
  1535. /*----------------------------------------------------------------------*\
  1536.  * process_data
  1537.  *
  1538.  * Utility function for processing data and other housekeeping.
  1539. \*----------------------------------------------------------------------*/
  1540.  
  1541. static int
  1542. process_data(Tcl_Interp *interp, register PlFrame *plFramePtr)
  1543. {
  1544.     register PLRDev *plr = plFramePtr->plr;
  1545.     register PLiodev *iodev = plr->iodev;
  1546.     int result = TCL_OK;
  1547.  
  1548. /* Process data */
  1549.  
  1550.     if (plr_process(plr) == -1) {
  1551.     Tcl_AppendResult(interp, "unable to read from ", iodev->typename,
  1552.              (char *) NULL);
  1553.     result = TCL_ERROR;
  1554.     }
  1555.  
  1556. /* Signal bop if necessary */
  1557.  
  1558.     if (plr->at_bop && plFramePtr->bopCmd != NULL) {
  1559.     plr->at_bop = 0;
  1560.     if (Tcl_Eval(interp, plFramePtr->bopCmd) != TCL_OK)
  1561.         fprintf(stderr, "Command \"%s\" failed:\n\t %s\n",
  1562.             plFramePtr->bopCmd, interp->result);
  1563.     }
  1564.  
  1565. /* Signal eop if necessary */
  1566.  
  1567.     if (plr->at_eop && plFramePtr->eopCmd != NULL) {
  1568.     plr->at_eop = 0;
  1569.     if (Tcl_Eval(interp, plFramePtr->eopCmd) != TCL_OK)
  1570.         fprintf(stderr, "Command \"%s\" failed:\n\t %s\n",
  1571.             plFramePtr->eopCmd, interp->result);
  1572.     }
  1573.  
  1574.     return result;
  1575. }
  1576.  
  1577. /*----------------------------------------------------------------------*\
  1578.  * ReadData
  1579.  *
  1580.  * Reads & processes data.
  1581.  * Intended to be installed as a filehandler command.
  1582. \*----------------------------------------------------------------------*/
  1583.  
  1584. static int
  1585. ReadData(ClientData clientData, int mask)
  1586. {
  1587.     register PlFrame *plFramePtr = (PlFrame *) clientData;
  1588.     register Tcl_Interp *interp = plFramePtr->interp;
  1589.  
  1590.     register PLRDev *plr = plFramePtr->plr;
  1591.     register PLiodev *iodev = plr->iodev;
  1592.     register PDFstrm *pdfs = plr->pdfs;
  1593.     int result = TCL_OK;
  1594.  
  1595.     if (mask & TK_READABLE) {
  1596.  
  1597. /* Read from FIFO or socket */
  1598.  
  1599.     plsstrm(plFramePtr->ipls);
  1600.     if (pl_PacketReceive(interp, iodev, pdfs)) {
  1601.         Tcl_AppendResult(interp, "Packet receive failed:\n\t %s\n",
  1602.                  interp->result, (char *) NULL);
  1603.         return TCL_ERROR;
  1604.     }
  1605.  
  1606. /* If the packet isn't complete it will be put back and we just return.
  1607.  * Otherwise, the buffer pointer is saved and then cleared so that reads
  1608.  * from the buffer start at the beginning.
  1609.  */
  1610.     if (pdfs->bp == 0)
  1611.         return TCL_OK;
  1612.  
  1613.     plr->nbytes = pdfs->bp;
  1614.     pdfs->bp = 0;
  1615.     result = process_data(interp, plFramePtr);
  1616.     }
  1617.  
  1618.     return result;
  1619. }
  1620.  
  1621. /*----------------------------------------------------------------------*\
  1622.  * Orient
  1623.  *
  1624.  * Processes "orient" widget command.
  1625.  * Handles orientation of plot.
  1626. \*----------------------------------------------------------------------*/
  1627.  
  1628. static int
  1629. Orient(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1630.        int argc, char **argv)
  1631. {
  1632.     int result = TCL_OK;
  1633.  
  1634. /* orient -- return orientation of current plot window */
  1635.  
  1636.     plsstrm(plFramePtr->ipls);
  1637.  
  1638.     if (argc == 0) {
  1639.     PLFLT rot;
  1640.     char result_str[128];
  1641.     plgdiori(&rot);
  1642.     sprintf(result_str, "%f", rot);
  1643.     Tcl_SetResult(interp, result_str, TCL_VOLATILE);
  1644.     }
  1645.  
  1646. /* orient <rot> -- Set orientation to <rot> */
  1647.  
  1648.     else {
  1649.     plsdiori(atof(argv[0]));
  1650.     result = Redraw(interp, plFramePtr, argc-1, argv+1);
  1651.     }
  1652.  
  1653.     return result;
  1654. }
  1655.  
  1656. /*----------------------------------------------------------------------*\
  1657.  * Print
  1658.  *
  1659.  * Processes "print" widget command.
  1660.  * Handles printing of plot, duh.
  1661.  *
  1662.  * Creates a temporary file, dumps the current plot to it in metafile
  1663.  * form, and then execs the "plpr" script to actually print it.  Since we
  1664.  * output it in metafile form here, plpr must invoke plrender to drive the
  1665.  * output to the appropriate file type.  The script is responsible for the
  1666.  * deletion of the plot metafile.
  1667.  *
  1668.  * This way of printing is reasonably flexible while retaining a measure
  1669.  * of security.  Methods involving setting up the command to print from
  1670.  * the Tcl side are not recommended at present since the print command
  1671.  * could be changed to something dangerous (like an rm *).
  1672. \*----------------------------------------------------------------------*/
  1673.  
  1674. static int
  1675. Print(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1676.        int argc, char **argv)
  1677. {
  1678.     PLINT ipls;
  1679.     int result = TCL_OK;
  1680.     char *sfnam;
  1681.     FILE *sfile;
  1682.     pid_t pid;
  1683.  
  1684. /* Create stream for save */
  1685.  
  1686.     plmkstrm(&ipls);
  1687.     if (ipls < 0) {
  1688.     Tcl_AppendResult(interp, "Error -- cannot create stream", 
  1689.              (char *) NULL);
  1690.     return TCL_ERROR;
  1691.     }
  1692.  
  1693. /* Open file for writes */
  1694.  
  1695.     sfnam = (char *) tmpnam(NULL);
  1696.  
  1697.     if ((sfile = fopen(sfnam, "wb+")) == NULL) {
  1698.     Tcl_AppendResult(interp, 
  1699.              "Error -- cannot open plot file for writing",
  1700.              (char *) NULL);
  1701.     plend1();
  1702.     return TCL_ERROR;
  1703.     }
  1704.  
  1705. /* Initialize stream */
  1706.  
  1707.     plsdev("plmeta");
  1708.     plsfile(sfile);
  1709.     plcpstrm(plFramePtr->ipls, 0);
  1710.     pladv(0);
  1711.  
  1712. /* Remake current plot, close file, and switch back to original stream */
  1713.  
  1714.     plreplot();
  1715.     plend1();
  1716.     plsstrm(plFramePtr->ipls);
  1717.  
  1718. /* So far so good.  Time to exec the print script. */
  1719.  
  1720.     if (plFramePtr->plpr_cmd == NULL)
  1721.     plFramePtr->plpr_cmd = plFindCommand("plpr");
  1722.  
  1723.     if ((plFramePtr->plpr_cmd == NULL) || (pid = fork()) < 0) {
  1724.     Tcl_AppendResult(interp, 
  1725.              "Error -- cannot fork print process",
  1726.              (char *) NULL);
  1727.     result = TCL_ERROR;
  1728.     }
  1729.     else if (pid == 0) {
  1730.     if (execl(plFramePtr->plpr_cmd, plFramePtr->plpr_cmd, sfnam, 
  1731.           (char *) 0)) {
  1732.         fprintf(stderr, "Unable to exec print command.\n");
  1733.         _exit(1);
  1734.     }
  1735.     }
  1736.  
  1737.     return result;
  1738. }
  1739.  
  1740. /*----------------------------------------------------------------------*\
  1741.  * Page
  1742.  *
  1743.  * Processes "page" widget command.
  1744.  * Handles parameters such as margin, aspect ratio, and justification
  1745.  * of final plot.
  1746. \*----------------------------------------------------------------------*/
  1747.  
  1748. static int
  1749. Page(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1750.      int argc, char **argv)
  1751. {
  1752.  
  1753. /* page -- return current device window parameters */
  1754.  
  1755.     plsstrm(plFramePtr->ipls);
  1756.  
  1757.     if (argc == 0) {
  1758.     PLFLT mar, aspect, jx, jy;
  1759.     char result_str[128];
  1760.  
  1761.     plgdidev(&mar, &aspect, &jx, &jy);
  1762.     sprintf(result_str, "%g %g %g %g", mar, aspect, jx, jy);
  1763.     Tcl_SetResult(interp, result_str, TCL_VOLATILE);
  1764.     return TCL_OK;
  1765.     }
  1766.  
  1767. /* page <mar> <aspect> <jx> <jy> -- set up page */
  1768.  
  1769.     if (argc < 4) {
  1770.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  1771.              " page mar aspect jx jy\"", (char *) NULL);
  1772.     return TCL_ERROR;
  1773.     } 
  1774.  
  1775.     plsdidev(atof(argv[0]), atof(argv[1]), atof(argv[2]), atof(argv[3]));
  1776.     return(Redraw(interp, plFramePtr, argc-1, argv+1));
  1777. }
  1778.  
  1779. /*----------------------------------------------------------------------*\
  1780.  * Redraw
  1781.  *
  1782.  * Processes "redraw" widget command.
  1783.  * Turns loose a DoWhenIdle command to redraw plot by replaying contents
  1784.  * of plot buffer.
  1785. \*----------------------------------------------------------------------*/
  1786.  
  1787. static int
  1788. Redraw(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1789.        int argc, char **argv)
  1790. {
  1791.     dbug_enter("Redraw");
  1792.  
  1793.     plFramePtr->flags |= REDRAW_PENDING;
  1794.     if ((plFramePtr->tkwin != NULL) &&
  1795.     !(plFramePtr->flags & REFRESH_PENDING)) {
  1796.  
  1797.     Tk_DoWhenIdle(DisplayPlFrame, (ClientData) plFramePtr);
  1798.     plFramePtr->flags |= REFRESH_PENDING;
  1799.     }
  1800.  
  1801.     return TCL_OK;
  1802. }
  1803.  
  1804. /*----------------------------------------------------------------------*\
  1805.  * Save
  1806.  *
  1807.  * Processes "save" widget command.
  1808.  * Saves plot to a file.
  1809. \*----------------------------------------------------------------------*/
  1810.  
  1811. static int
  1812. Save(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1813.      int argc, char **argv)
  1814. {
  1815.     int length, idev;
  1816.     char c;
  1817.     FILE *sfile;
  1818.  
  1819. /* save -- save to already open file */
  1820.  
  1821.     if (argc == 0) {
  1822.     if ( ! plFramePtr->ipls_save) {
  1823.         Tcl_AppendResult(interp, "Error -- no current save file", 
  1824.                  (char *) NULL);
  1825.         return TCL_ERROR;
  1826.     }
  1827.     plsstrm(plFramePtr->ipls_save);
  1828.     plcpstrm(plFramePtr->ipls, 0);
  1829.     pladv(0);
  1830.     plreplot();
  1831.     plflush();
  1832.     plsstrm(plFramePtr->ipls);
  1833.     return TCL_OK;
  1834.     }
  1835.  
  1836.     c = argv[0][0];
  1837.     length = strlen(argv[0]);
  1838.  
  1839. /* save to specified device & file */
  1840.  
  1841.     if ((c == 'a') && (strncmp(argv[0], "as", length) == 0)) {
  1842.     if (argc < 3) {
  1843.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1844.                  " save as device file\"", (char *) NULL);
  1845.         return TCL_ERROR;
  1846.     } 
  1847.     idev = atoi(argv[1]);
  1848.  
  1849. /* If save previously in effect, delete old stream */
  1850.  
  1851.     if (plFramePtr->ipls_save) {
  1852.         plsstrm(plFramePtr->ipls_save);
  1853.         plend1();
  1854.     }
  1855.  
  1856. /* Create stream for saves to selected device & file */
  1857.  
  1858.     plmkstrm(&plFramePtr->ipls_save);
  1859.     if (plFramePtr->ipls_save < 0) {
  1860.         Tcl_AppendResult(interp, "Error -- cannot create stream", 
  1861.                  (char *) NULL);
  1862.         plFramePtr->ipls_save = 0;
  1863.         return TCL_ERROR;
  1864.     }
  1865.  
  1866. /* Open file for writes */
  1867.  
  1868.     if ((sfile = fopen(argv[2], "wb+")) == NULL) {
  1869.         Tcl_AppendResult(interp, "Error -- cannot open file ", argv[2],
  1870.                  " for writing", (char *) NULL);
  1871.         plFramePtr->ipls_save = 0;
  1872.         plend1();
  1873.         return TCL_ERROR;
  1874.     }
  1875.  
  1876. /* Initialize stream */
  1877.  
  1878.     plsdev(plFramePtr->devName[idev]);
  1879.     plsfile(sfile);
  1880.     plcpstrm(plFramePtr->ipls, 0);
  1881.     pladv(0);
  1882.  
  1883. /* Remake current plot and then switch back to original stream */
  1884.  
  1885.     plreplot();
  1886.     plflush();
  1887.     plsstrm(plFramePtr->ipls);
  1888.     }
  1889.  
  1890. /* close save file */
  1891.  
  1892.     else if ((c == 'c') && (strncmp(argv[0], "close", length) == 0)) {
  1893.  
  1894.     if ( ! plFramePtr->ipls_save) {
  1895.         Tcl_AppendResult(interp, "Error -- no current save file", 
  1896.                  (char *) NULL);
  1897.         return TCL_ERROR;
  1898.     }
  1899.     else {
  1900.         plsstrm(plFramePtr->ipls_save);
  1901.         plend1();
  1902.         plFramePtr->ipls_save = 0;
  1903.     }
  1904.     }
  1905.  
  1906. /* unrecognized */
  1907.  
  1908.     else {
  1909.     Tcl_AppendResult(interp, "bad option to \"save\": must be ", 
  1910.      "as or close", (char *) NULL);
  1911.  
  1912.     return TCL_ERROR;
  1913.     }
  1914.  
  1915.     return TCL_OK;
  1916. }
  1917.  
  1918. /*----------------------------------------------------------------------*\
  1919.  * View
  1920.  *
  1921.  * Processes "view" widget command.
  1922.  * Handles translation & scaling of view into plot.
  1923. \*----------------------------------------------------------------------*/
  1924.  
  1925. static int
  1926. View(Tcl_Interp *interp, register PlFrame *plFramePtr,
  1927.      int argc, char **argv)
  1928. {
  1929.     int length;
  1930.     char c;
  1931.     PLFLT xl, xr, yl, yr;
  1932.  
  1933. /* view -- return current relative plot window coordinates */
  1934.  
  1935.     plsstrm(plFramePtr->ipls);
  1936.  
  1937.     if (argc == 0) {
  1938.     char result_str[128];
  1939.     plgdiplt(&xl, &yl, &xr, &yr);
  1940.     sprintf(result_str, "%g %g %g %g", xl, yl, xr, yr);
  1941.     Tcl_SetResult(interp, result_str, TCL_VOLATILE);
  1942.     return TCL_OK;
  1943.     }
  1944.  
  1945.     c = argv[0][0];
  1946.     length = strlen(argv[0]);
  1947.  
  1948. /* view bounds -- return relative device coordinates of bounds on current */
  1949. /* plot window */
  1950.  
  1951.     if ((c == 'b') && (strncmp(argv[0], "bounds", length) == 0)) {
  1952.     char result_str[128];
  1953.     xl = 0.; yl = 0.;
  1954.     xr = 1.; yr = 1.;
  1955.     pldip2dc(&xl, &yl, &xr, &yr);
  1956.     sprintf(result_str, "%g %g %g %g", xl, yl, xr, yr);
  1957.     Tcl_SetResult(interp, result_str, TCL_VOLATILE);
  1958.     return TCL_OK;
  1959.     }
  1960.  
  1961. /* view reset -- Resets plot */
  1962.  
  1963.     if ((c == 'r') && (strncmp(argv[0], "reset", length) == 0)) {
  1964.     xl = 0.; yl = 0.;
  1965.     xr = 1.; yr = 1.;
  1966.     plsdiplt(xl, yl, xr, yr);
  1967.     }
  1968.  
  1969. /* view select -- set window into plot space */
  1970. /* Specifies in terms of plot window coordinates, not device coordinates */
  1971.  
  1972.     else if ((c == 's') && (strncmp(argv[0], "select", length) == 0)) {
  1973.     if (argc < 5) {
  1974.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1975.                  " view select xmin ymin xmax ymax\"",
  1976.                  (char *) NULL);
  1977.         return TCL_ERROR;
  1978.     }
  1979.     else {
  1980.         gbox(&xl, &yl, &xr, &yr, argv+1);
  1981.         plsdiplt(xl, yl, xr, yr);
  1982.     }
  1983.     }
  1984.  
  1985. /* view zoom -- set window into plot space incrementally (zoom) */
  1986. /* Here we need to take the page (device) offsets into account */
  1987.  
  1988.     else if ((c == 'z') && (strncmp(argv[0], "zoom", length) == 0)) {
  1989.     if (argc < 5) {
  1990.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1991.                  " view zoom xmin ymin xmax ymax\"",
  1992.                  (char *) NULL);
  1993.         return TCL_ERROR;
  1994.     }
  1995.     else {
  1996.         gbox(&xl, &yl, &xr, &yr, argv+1);
  1997.         pldid2pc(&xl, &yl, &xr, &yr);
  1998.         plsdiplz(xl, yl, xr, yr);
  1999.     }
  2000.     }
  2001.  
  2002. /* unrecognized */
  2003.  
  2004.     else {
  2005.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  2006.      "\":  options to \"view\" are: bounds, reset, select, or zoom",
  2007.      (char *) NULL);
  2008.  
  2009.     return TCL_ERROR;
  2010.     }
  2011.  
  2012. /* Update plot window bounds and arrange for plot to be updated */
  2013.  
  2014.     plgdiplt(&xl, &yl, &xr, &yr);
  2015.     plFramePtr->xl = xl;
  2016.     plFramePtr->yl = yl;
  2017.     plFramePtr->xr = xr;
  2018.     plFramePtr->yr = yr;
  2019.     plFramePtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
  2020.  
  2021.     return(Redraw(interp, plFramePtr, argc, argv));
  2022. }
  2023.  
  2024. /*----------------------------------------------------------------------*\
  2025.  * xScroll
  2026.  *
  2027.  * Processes "xscroll" widget command.
  2028.  * Handles horizontal scroll-bar invoked translation of view into plot.
  2029. \*----------------------------------------------------------------------*/
  2030.  
  2031. static int
  2032. xScroll(Tcl_Interp *interp, register PlFrame *plFramePtr,
  2033.     int argc, char **argv)
  2034. {
  2035.     int x0, width = Tk_Width(plFramePtr->tkwin);
  2036.     PLFLT xl, xr, yl, yr, xlen;
  2037.  
  2038.     plsstrm(plFramePtr->ipls);
  2039.  
  2040.     xlen = plFramePtr->xr - plFramePtr->xl;
  2041.     x0 = atoi(argv[0]);
  2042.     xl = x0 / (double) width;
  2043.     xl = MAX( 0., MIN((1. - xlen), xl));
  2044.     xr = xl + xlen;
  2045.  
  2046.     yl = plFramePtr->yl;
  2047.     yr = plFramePtr->yr;
  2048.  
  2049.     plFramePtr->xl = xl;
  2050.     plFramePtr->xr = xr;
  2051.  
  2052.     plsdiplt(xl, yl, xr, yr);
  2053.  
  2054.     plFramePtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
  2055.     return(Redraw(interp, plFramePtr, argc, argv));
  2056. }
  2057.  
  2058. /*----------------------------------------------------------------------*\
  2059.  * yScroll
  2060.  *
  2061.  * Processes "yscroll" widget command.
  2062.  * Handles vertical scroll-bar invoked translation of view into plot.
  2063. \*----------------------------------------------------------------------*/
  2064.  
  2065. static int
  2066. yScroll(Tcl_Interp *interp, register PlFrame *plFramePtr,
  2067.     int argc, char **argv)
  2068. {
  2069.     int y0, height = Tk_Height(plFramePtr->tkwin);
  2070.     PLFLT xl, xr, yl, yr, ylen;
  2071.  
  2072.     plsstrm(plFramePtr->ipls);
  2073.  
  2074.     ylen = plFramePtr->yr - plFramePtr->yl;
  2075.     y0 = atoi(argv[0]);
  2076.     yr = 1. - y0 / (double) height;
  2077.     yr = MAX( 0.+ylen, MIN(1., yr));
  2078.     yl = yr - ylen;
  2079.  
  2080.     xl = plFramePtr->xl;
  2081.     xr = plFramePtr->xr;
  2082.  
  2083.     plFramePtr->yl = yl;
  2084.     plFramePtr->yr = yr;
  2085.  
  2086.     plsdiplt(xl, yl, xr, yr);
  2087.  
  2088.     plFramePtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
  2089.     return(Redraw(interp, plFramePtr, argc, argv));
  2090. }
  2091.  
  2092. /*----------------------------------------------------------------------*\
  2093.  * Utility routines
  2094. \*----------------------------------------------------------------------*/
  2095.  
  2096. /*----------------------------------------------------------------------*\
  2097.  * UpdateVScrollbar
  2098.  *
  2099.  * Updates vertical scrollbar if needed.
  2100. \*----------------------------------------------------------------------*/
  2101.  
  2102. static void
  2103. UpdateVScrollbar(register PlFrame *plFramePtr)
  2104. {
  2105.     int height = Tk_Height(plFramePtr->tkwin);
  2106.     char string[60];
  2107.     int totalUnits, windowUnits, firstUnit, lastUnit, result;
  2108.  
  2109.     if (plFramePtr->yScrollCmd == NULL)
  2110.     return;
  2111.  
  2112.     totalUnits  = height;
  2113.     firstUnit   = 0.5 + (float) height * (1. - plFramePtr->yr);
  2114.     lastUnit    = 0.5 + (float) height * (1. - plFramePtr->yl);
  2115.     windowUnits = lastUnit - firstUnit;
  2116.     sprintf(string, " %d %d %d %d",
  2117.         totalUnits, windowUnits, firstUnit, lastUnit);
  2118.  
  2119.     result = Tcl_VarEval(plFramePtr->interp, plFramePtr->yScrollCmd, string,
  2120.              (char *) NULL);
  2121.  
  2122.     if (result != TCL_OK) {
  2123.     Tk_BackgroundError(plFramePtr->interp);
  2124.     }
  2125. }
  2126.  
  2127. /*----------------------------------------------------------------------*\
  2128.  * UpdateHScrollbar
  2129.  *
  2130.  * Updates horizontal scrollbar if needed.
  2131. \*----------------------------------------------------------------------*/
  2132.  
  2133. static void
  2134. UpdateHScrollbar(register PlFrame *plFramePtr)
  2135. {
  2136.     int width = Tk_Width(plFramePtr->tkwin);
  2137.     char string[60];
  2138.     int totalUnits, windowUnits, firstUnit, lastUnit, result;
  2139.  
  2140.     if (plFramePtr->xScrollCmd == NULL)
  2141.     return;
  2142.  
  2143.     totalUnits  = width;
  2144.     firstUnit   = 0.5 + (float) width * plFramePtr->xl;
  2145.     lastUnit    = 0.5 + (float) width * plFramePtr->xr;
  2146.     windowUnits = lastUnit - firstUnit;
  2147.     sprintf(string, " %d %d %d %d",
  2148.         totalUnits, windowUnits, firstUnit, lastUnit);
  2149.  
  2150.     result = Tcl_VarEval(plFramePtr->interp, plFramePtr->xScrollCmd, string,
  2151.              (char *) NULL);
  2152.  
  2153.     if (result != TCL_OK) {
  2154.     Tk_BackgroundError(plFramePtr->interp);
  2155.     }
  2156. }
  2157.  
  2158. /*----------------------------------------------------------------------*\
  2159.  * gbox
  2160.  *
  2161.  * Returns selection box coordinates.  It's best if the TCL script does
  2162.  * bounds checking on the input but I do it here as well just to be safe.
  2163. \*----------------------------------------------------------------------*/
  2164.  
  2165. static void
  2166. gbox(PLFLT *xl, PLFLT *yl, PLFLT *xr, PLFLT *yr, char **argv)
  2167. {
  2168.     float x0, y0, x1, y1;
  2169.  
  2170.     x0 = atof(argv[0]);
  2171.     y0 = atof(argv[1]);
  2172.     x1 = atof(argv[2]);
  2173.     y1 = atof(argv[3]);
  2174.  
  2175.     x0 = MAX(0., MIN(1., x0));
  2176.     y0 = MAX(0., MIN(1., y0));
  2177.     x1 = MAX(0., MIN(1., x1));
  2178.     y1 = MAX(0., MIN(1., y1));
  2179.  
  2180. /* Only need two vertices, pick the lower left and upper right */
  2181.  
  2182.     *xl = MIN(x0, x1);
  2183.     *yl = MIN(y0, y1);
  2184.     *xr = MAX(x0, x1);
  2185.     *yr = MAX(y0, y1);
  2186. }
  2187.