// Copyright (C) 2004 Joao Cardoso // // This file is part of PLplot. // // PLplot is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published // by the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // PLplot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with PLplot; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // // // plplotter.c // // Copyright 1993, 1994, 1995 // Maurice LeBrun mjl@dino.ph.utexas.edu // Institute for Fusion Studies University of Texas at Austin // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // // Based upon tkFrame.c from the TK 3.2 distribution: // // Copyright 1990 Regents of the University of California. // Permission to use, copy, modify, and distribute this // software and its documentation for any purpose and without // fee is hereby granted, provided that the above copyright // notice appear in all copies. The University of California // makes no representations about the suitability of this // software for any purpose. It is provided "as is" without // express or implied warranty. // //-------------------------------------------------------------------------- // // This module implements "plframe" widgets for the Tk toolkit. These are // frames that have extra logic to allow them to be interfaced with the // PLplot X driver. These are then drawn into and respond to keyboard and // mouse events. // // // #define DEBUG_ENTER // #define DEBUG // #define NEED_PLDEBUG #include "plserver.h" #include "pltkwd.h" #include "tcpip.h" #ifdef _WIN32 #define XSynchronize( display, bool ) { display->request++; } #define XSync( display, bool ) { display->request++; } #define XFlush( display ) #endif #ifdef DEBUG_ENTER // This version of dbug_enter works cross-platform #undef dbug_enter #define dbug_enter( a ) if ( plsc->debug ) { \ Tcl_Write( Tcl_GetStdChannel( TCL_STDERR ), a, -1 ); } #endif #ifndef _WIN32 #ifdef PL_HAVE_UNISTD_H #include #include #endif #endif // plplot_tkwin_ccmap is statically defined in pltkwd.h. Note that // tkwin.c also includes that header and uses that variable. #define NDEV 100 // Max number of output device types // // A data structure of the following type is kept for each // plframe that currently exists for this process: // typedef struct PlPlotter { // This is stuff taken from tkFrame.c Tk_Window tkwin; // Window that embodies the frame. NULL // means that the window has been destroyed // but the data structures haven't yet been // cleaned up. Display *display; // Display containing widget. Used, among // other things, so that resources can be // freed even after tkwin has gone away. Tcl_Interp *interp; // Interpreter associated with // widget. Used to delete widget // command. Tcl_Command widgetCmd; // Token for frame's widget command. Tk_3DBorder border; // Structure used to draw 3-D border and // background. int borderWidth; // Width of 3-D border (if any). int relief; // 3-d effect: TK_RELIEF_RAISED etc. int width; // Width to request for window. <= 0 means // don't request any size. int height; // Height to request for window. <= 0 means // don't request any size. Tk_Cursor cursor; // Current cursor for window, or None. int flags; // Various flags; see below for // definitions. // These are new to plframe widgets // control stuff int tkwin_initted; // Set first time widget is mapped PLStream *pls; // PLplot stream pointer PLINT ipls; // PLplot stream number PLINT ipls_save; // PLplot stream number, save files PLRDev *plr; // Renderer state information. Malloc'ed char *plpr_cmd; // Holds print command name. Malloc'ed // Used to allow active stuff on the plot int active_plot; // The plot responds to mouse movement etc. int isActive; // Used to turn event handling on and off. // Used to handle resize and expose events PLDisplay pldis; // Info about the display window int prevWidth; // Previous window width int prevHeight; // Previous window height // Support for save operations char *SaveFnam; // File name we are currently saving to. // Malloc'ed. const char **devDesc; // Descriptive names for file-oriented // devices. Malloc'ed. const char **devName; // Keyword names of file-oriented devices. // Malloc'ed. // Used in selecting & modifying plot or device area GC xorGC; // GC used for rubber-band drawing XPoint pts[5]; // Points for rubber-band drawing int continue_draw; // Set when doing rubber-band draws Tk_Cursor xhair_cursor; // cursor used for drawing PLFLT xl, xr, yl, yr; // Bounds on plot viewing area char *xScrollCmd; // Command prefix for communicating with // horizontal scrollbar. NULL means no // command to issue. Malloc'ed. char *yScrollCmd; // Command prefix for communicating with // vertical scrollbar. NULL means no // command to issue. Malloc'ed. // Used for flashing bop or eop condition char *bopCmd; // Proc to call at bop char *eopCmd; // Proc to call at eop // Used for drawing graphic crosshairs int xhairs; // Configuration option to turn on xhairs int drawing_xhairs; // Set if we are currently drawing xhairs XPoint xhair_x[2]; // Points for horizontal xhair line XPoint xhair_y[2]; // Points for vertical xhair line // Used for drawing a rubber band lilne segment int rband; // Configuration option to turn on rband int drawing_rband; // See if we are currently drawing rband XPoint rband_pt[2]; // Ends of rubber band line int double_buffer; // Double buffering configuration option } PlPlotter; // // Flag bits for plframes: // // REFRESH_PENDING: Non-zero means a DoWhenIdle handler // has already been queued to refresh // this window. // RESIZE_PENDING; Used to reschedule resize events // REDRAW_PENDING; Used to redraw contents of plot buffer // UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs // to be updated. // UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs // to be updated. // #define REFRESH_PENDING 1 #define RESIZE_PENDING 2 #define REDRAW_PENDING 4 #define UPDATE_V_SCROLLBAR 8 #define UPDATE_H_SCROLLBAR 16 // Defaults for plframes: #define DEF_PLFRAME_BG_COLOR "Black" #define DEF_PLFRAME_BG_MONO "White" #define DEF_PLFRAME_BORDER_WIDTH "0" #define DEF_PLFRAME_CURSOR ( (char *) NULL ) #define DEF_PLFRAME_HEIGHT "250" #define DEF_PLFRAME_RELIEF "flat" #define DEF_PLFRAME_WIDTH "250" // Configuration info static Tk_ConfigSpec configSpecs[] = { { TK_CONFIG_BOOLEAN, "-activeplot", (char *) NULL, (char *) NULL, "1", Tk_Offset( PlPlotter, active_plot ), TK_CONFIG_DONT_SET_DEFAULT }, { TK_CONFIG_BORDER, "-background", "background", "Background", DEF_PLFRAME_BG_COLOR, Tk_Offset( PlPlotter, border ), TK_CONFIG_COLOR_ONLY }, { TK_CONFIG_BORDER, "-background", "background", "Background", DEF_PLFRAME_BG_MONO, Tk_Offset( PlPlotter, border ), TK_CONFIG_MONO_ONLY }, { TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, (char *) NULL, 0, 0 }, { TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, (char *) NULL, 0, 0 }, { TK_CONFIG_STRING, "-bopcmd", "bopcmd", "PgCommand", (char *) NULL, Tk_Offset( PlPlotter, bopCmd ), TK_CONFIG_NULL_OK }, { TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", DEF_PLFRAME_BORDER_WIDTH, Tk_Offset( PlPlotter, borderWidth ), 0 }, { TK_CONFIG_BOOLEAN, "-doublebuffer", (char *) NULL, (char *) NULL, "0", Tk_Offset( PlPlotter, double_buffer ), TK_CONFIG_DONT_SET_DEFAULT }, { TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", DEF_PLFRAME_CURSOR, Tk_Offset( PlPlotter, cursor ), TK_CONFIG_NULL_OK }, { TK_CONFIG_STRING, "-eopcmd", "eopcmd", "PgCommand", (char *) NULL, Tk_Offset( PlPlotter, eopCmd ), TK_CONFIG_NULL_OK }, { TK_CONFIG_PIXELS, "-height", "height", "Height", DEF_PLFRAME_HEIGHT, Tk_Offset( PlPlotter, height ), 0 }, { TK_CONFIG_RELIEF, "-relief", "relief", "Relief", DEF_PLFRAME_RELIEF, Tk_Offset( PlPlotter, relief ), 0 }, { TK_CONFIG_PIXELS, "-width", "width", "Width", DEF_PLFRAME_WIDTH, Tk_Offset( PlPlotter, width ), 0 }, { TK_CONFIG_BOOLEAN, "-xhairs", (char *) NULL, (char *) NULL, "0", Tk_Offset( PlPlotter, xhairs ), TK_CONFIG_DONT_SET_DEFAULT }, { TK_CONFIG_BOOLEAN, "-rubberband", (char *) NULL, (char *) NULL, "0", Tk_Offset( PlPlotter, rband ), TK_CONFIG_DONT_SET_DEFAULT }, { TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", (char *) NULL, Tk_Offset( PlPlotter, xScrollCmd ), TK_CONFIG_NULL_OK }, { TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand", (char *) NULL, Tk_Offset( PlPlotter, yScrollCmd ), TK_CONFIG_NULL_OK }, { TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0 } }; // Forward declarations for procedures defined later in this file: // Externals int plPlotterCmd( ClientData, Tcl_Interp *, int, const char ** ); void PlplotterAtEop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ); void PlplotterAtBop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ); // These are invoked by the TK dispatcher static void DestroyPlPlotter( ClientData ); static void DisplayPlPlotter( ClientData ); static void PlPlotterInit( ClientData ); static void PlPlotterFirstInit( ClientData clientData ); static void PlPlotterConfigureEH( ClientData, XEvent * ); static void PlPlotterExposeEH( ClientData, XEvent * ); static void PlPlotterMotionEH( ClientData, register XEvent * ); static void PlPlotterEnterEH( ClientData, register XEvent * ); static void PlPlotterLeaveEH( ClientData, register XEvent * ); static void PlPlotterButtonPressEH( ClientData clientData, register XEvent * ); static int PlPlotterWidgetCmd( ClientData, Tcl_Interp *, int, CONST char ** ); static int ReadData( ClientData, int ); static void Install_cmap( PlPlotter *plPlotterPtr ); // These are invoked by PlPlotterWidgetCmd to process widget commands static int Closelink( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Cmd( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int ConfigurePlPlotter( Tcl_Interp *, PlPlotter *, int, CONST char **, int ); static int Draw( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Info( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Openlink( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Orient( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Page( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int NextPage( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Print( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Redraw( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Save( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int View( Tcl_Interp *, PlPlotter *, int, CONST char ** ); static int Scroll( Tcl_Interp *, PlPlotter * ); static int report( Tcl_Interp *, PlPlotter *, int, CONST char ** ); // Routines for manipulating graphic crosshairs static void ActiveState( register PlPlotter *plPlotterPtr, int on ); static void CreateXhairs( PlPlotter * ); static void DestroyXhairs( PlPlotter * ); static void DrawXhairs( PlPlotter *, int, int ); static void UpdateXhairs( PlPlotter * ); // Routines for manipulating the rubberband line static void CreateRband( PlPlotter * ); static void DestroyRband( PlPlotter * ); static void DrawRband( PlPlotter *, int, int ); static void UpdateRband( PlPlotter * ); // Utility routines static void gbox( PLFLT *, PLFLT *, PLFLT *, PLFLT *, CONST char ** ); static void UpdateVScrollbar( register PlPlotter * ); static void UpdateHScrollbar( register PlPlotter * ); // //-------------------------------------------------------------------------- // // plPlotterCmd -- // // This procedure is invoked to process the "plframe" Tcl // command. See the user documentation for details on what it // does. // // Results: // A standard Tcl result. // // Side effects: // See the user documentation. // //-------------------------------------------------------------------------- // int plPlotterCmd( ClientData PL_UNUSED( clientData ), Tcl_Interp *interp, int argc, const char **argv ) { Tk_Window tkwin; register PlPlotter *plPlotterPtr; register PLRDev *plr; int i, ndev; dbug_enter( "plPlotterCmd" ); if ( argc < 2 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " pathName ?options?\"", (char *) NULL ); return TCL_ERROR; } // Create the window. tkwin = Tk_CreateWindowFromPath( interp, Tk_MainWindow( interp ), argv[1], (char *) NULL ); if ( tkwin == NULL ) { return TCL_ERROR; } Tk_SetClass( tkwin, "Plframe" ); plPlotterPtr = (PlPlotter *) malloc( sizeof ( PlPlotter ) ); // Initialize in the same order as the members of the struct just // to keep track of what is initialized and what not. plPlotterPtr->tkwin = tkwin; plPlotterPtr->display = Tk_Display( tkwin ); plPlotterPtr->interp = interp; plPlotterPtr->widgetCmd = Tcl_CreateCommand( interp, Tk_PathName( plPlotterPtr->tkwin ), (Tcl_CmdProc *) PlPlotterWidgetCmd, (ClientData) plPlotterPtr, (Tcl_CmdDeleteProc *) NULL ); plPlotterPtr->border = NULL; //plPlotterPtr->borderWidth = ; //plPlotterPtr->relief = ; plPlotterPtr->width = Tk_Width( plPlotterPtr->tkwin ); plPlotterPtr->height = Tk_Height( plPlotterPtr->tkwin ); plPlotterPtr->cursor = None; plPlotterPtr->flags = 0; plPlotterPtr->tkwin_initted = 0; // Associate new PLplot stream with this widget plmkstrm( &plPlotterPtr->ipls ); plgpls( &plPlotterPtr->pls ); plPlotterPtr->ipls_save = 0; plPlotterPtr->plr = (PLRDev *) malloc( sizeof ( PLRDev ) ); plPlotterPtr->plpr_cmd = NULL; plPlotterPtr->active_plot = 1; plPlotterPtr->isActive = 0; plPlotterPtr->pldis.x = 0; plPlotterPtr->pldis.y = 0; plPlotterPtr->pldis.width = 0; plPlotterPtr->pldis.height = 0; plPlotterPtr->prevWidth = 0; plPlotterPtr->prevHeight = 0; plPlotterPtr->SaveFnam = NULL; // plPlotterPtr->devDesc = ; // plPlotterPtr->devName = ; plPlotterPtr->xorGC = NULL; // plPlotterPtr->pts = ; plPlotterPtr->continue_draw = 0; plPlotterPtr->xhair_cursor = None; plPlotterPtr->xl = 0.; plPlotterPtr->yl = 0.; plPlotterPtr->xr = 1.; plPlotterPtr->yr = 1.; plPlotterPtr->xScrollCmd = NULL; plPlotterPtr->yScrollCmd = NULL; plPlotterPtr->bopCmd = NULL; plPlotterPtr->eopCmd = NULL; plPlotterPtr->xhairs = 0; plPlotterPtr->drawing_xhairs = 0; // plPlotterPtr->xhair_x = ; // plPlotterPtr->xhair_y = ; plPlotterPtr->rband = 0; plPlotterPtr->drawing_rband = 0; // plPlotterPtr->rband_pt = ; plPlotterPtr->double_buffer = 1; plr = plPlotterPtr->plr; plr->pdfs = NULL; plr->iodev = (PLiodev *) malloc( sizeof ( PLiodev ) ); plr_start( plr ); // Tell the stream about myself plPlotterPtr->pls->plPlotterPtr = plPlotterPtr; // Set up stuff for rubber-band drawing plPlotterPtr->xhair_cursor = Tk_GetCursor( plPlotterPtr->interp, plPlotterPtr->tkwin, "crosshair" ); // Partially initialize Tk driver. plD_open_tkwin( plPlotterPtr->pls ); // Create list of valid device names and keywords for page dumps plPlotterPtr->devDesc = (const char **) malloc( (size_t) NDEV * sizeof ( char ** ) ); plPlotterPtr->devName = (const char **) malloc( (size_t) NDEV * sizeof ( char ** ) ); for ( i = 0; i < NDEV; i++ ) { plPlotterPtr->devDesc[i] = NULL; plPlotterPtr->devName[i] = NULL; } ndev = NDEV; plgFileDevs( &plPlotterPtr->devDesc, &plPlotterPtr->devName, &ndev ); // Start up event handlers and other good stuff Tk_CreateEventHandler( plPlotterPtr->tkwin, StructureNotifyMask, PlPlotterConfigureEH, (ClientData) plPlotterPtr ); Tk_CreateEventHandler( plPlotterPtr->tkwin, ExposureMask, PlPlotterExposeEH, (ClientData) plPlotterPtr ); // for active plot ActiveState( plPlotterPtr, 1 ); if ( ConfigurePlPlotter( interp, plPlotterPtr, argc - 2, (CONST char **) argv + 2, 0 ) != TCL_OK ) { Tk_DestroyWindow( plPlotterPtr->tkwin ); return TCL_ERROR; } Tk_MakeWindowExist( plPlotterPtr->tkwin ); PlPlotterFirstInit( (ClientData) plPlotterPtr ); Tk_GeometryRequest( plPlotterPtr->tkwin, 200, 200 ); Tcl_SetResult( interp, Tk_PathName( plPlotterPtr->tkwin ), TCL_VOLATILE ); return TCL_OK; } // //-------------------------------------------------------------------------- // // PlPlotterWidgetCmd -- // // This procedure is invoked to process the Tcl command that // corresponds to a plframe widget. See the user // documentation for details on what it does. // // Results: // A standard Tcl result. // // Side effects: // See the user documentation. // //-------------------------------------------------------------------------- // static int PlPlotterWidgetCmd( ClientData clientData, Tcl_Interp *interp, int argc, CONST char **argv ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; int result = TCL_OK; int length; char c; dbug_enter( "PlPlotterWidgetCmd" ); if ( argc < 2 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " option ?arg arg ...?\"", (char *) NULL ); return TCL_ERROR; } Tk_Preserve( (ClientData) plPlotterPtr ); c = argv[1][0]; length = (int) strlen( argv[1] ); // cmd -- issue a command to the PLplot library if ( ( c == 'c' ) && ( strncmp( argv[1], "cmd", (size_t) length ) == 0 ) ) { result = Cmd( interp, plPlotterPtr, argc - 2, argv + 2 ); } // configure else if ( ( c == 'c' ) && ( strncmp( argv[1], "cget", (size_t) length ) == 0 ) && ( length >= 2 ) ) { if ( argc != 3 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " cget option\"", (char *) NULL ); result = TCL_ERROR; goto done; } result = Tk_ConfigureValue( interp, plPlotterPtr->tkwin, configSpecs, (char *) plPlotterPtr, argv[2], 0 ); } else if ( ( c == 'c' ) && ( strncmp( argv[1], "configure", (size_t) length ) == 0 ) ) { if ( argc == 2 ) { result = Tk_ConfigureInfo( interp, plPlotterPtr->tkwin, configSpecs, (char *) plPlotterPtr, (char *) NULL, 0 ); } else if ( argc == 3 ) { result = Tk_ConfigureInfo( interp, plPlotterPtr->tkwin, configSpecs, (char *) plPlotterPtr, argv[2], 0 ); } else { result = ConfigurePlPlotter( interp, plPlotterPtr, argc - 2, argv + 2, TK_CONFIG_ARGV_ONLY ); } } // closelink -- Close a binary data link previously opened with openlink else if ( ( c == 'c' ) && ( strncmp( argv[1], "closelink", (size_t) length ) == 0 ) ) { if ( argc > 2 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], (char *) NULL ); result = TCL_ERROR; goto done; } else { result = Closelink( interp, plPlotterPtr, argc - 2, argv + 2 ); } } // draw -- rubber-band draw used in region selection else if ( ( c == 'd' ) && ( strncmp( argv[1], "draw", (size_t) length ) == 0 ) ) { if ( argc == 2 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " draw op ?options?\"", (char *) NULL ); result = TCL_ERROR; goto done; } else { result = Draw( interp, plPlotterPtr, argc - 2, argv + 2 ); } } // info -- returns requested info else if ( ( c == 'i' ) && ( strncmp( argv[1], "info", (size_t) length ) == 0 ) ) { result = Info( interp, plPlotterPtr, argc - 2, argv + 2 ); } // next page. called to cancel wait for page in tkwin driver else if ( ( c == 'n' ) && ( strncmp( argv[1], "nextpage", (size_t) length ) == 0 ) ) { result = NextPage( interp, plPlotterPtr, argc - 2, argv + 2 ); } // orient -- Set plot orientation else if ( ( c == 'o' ) && ( strncmp( argv[1], "orient", (size_t) length ) == 0 ) ) { result = Orient( interp, plPlotterPtr, argc - 2, argv + 2 ); } // openlink -- Open a binary data link (FIFO or socket) else if ( ( c == 'o' ) && ( strncmp( argv[1], "openlink", (size_t) length ) == 0 ) ) { if ( argc < 3 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " option ?arg arg ...?\"", (char *) NULL ); result = TCL_ERROR; goto done; } else { result = Openlink( interp, plPlotterPtr, argc - 2, argv + 2 ); } } // page -- change or return output page setup else if ( ( c == 'p' ) && ( strncmp( argv[1], "page", (size_t) length ) == 0 ) ) { result = Page( interp, plPlotterPtr, argc - 2, argv + 2 ); } // print -- prints plot else if ( ( c == 'p' ) && ( strncmp( argv[1], "print", (size_t) length ) == 0 ) ) { result = Print( interp, plPlotterPtr, argc - 2, argv + 2 ); } // redraw -- redraw plot else if ( ( c == 'r' ) && ( strncmp( argv[1], "redraw", (size_t) length ) == 0 ) ) { if ( argc > 2 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " redraw\"", (char *) NULL ); result = TCL_ERROR; goto done; } else { result = Redraw( interp, plPlotterPtr, argc - 2, argv + 2 ); } } // report -- find out useful info about the plframe (GMF) else if ( ( c == 'r' ) && ( strncmp( argv[1], "report", (size_t) length ) == 0 ) ) { result = report( interp, plPlotterPtr, argc - 2, argv + 2 ); } // save -- saves plot to the specified plot file type else if ( ( c == 's' ) && ( strncmp( argv[1], "save", (size_t) length ) == 0 ) ) { result = Save( interp, plPlotterPtr, argc - 2, argv + 2 ); } // view -- change or return window into plot else if ( ( c == 'v' ) && ( strncmp( argv[1], "view", (size_t) length ) == 0 ) ) { result = View( interp, plPlotterPtr, argc - 2, argv + 2 ); } // xscroll -- horizontally scroll window into plot else if ( ( c == 'x' ) && ( strncmp( argv[1], "xview", (size_t) length ) == 0 ) ) { int count, type; double width = (double) ( plPlotterPtr->xr - plPlotterPtr->xl ); double fraction; type = Tk_GetScrollInfo( interp, argc, argv, &fraction, &count ); switch ( type ) { case TK_SCROLL_ERROR: result = TCL_ERROR; goto done; case TK_SCROLL_MOVETO: plPlotterPtr->xl = (PLFLT) fraction; plPlotterPtr->xr = (PLFLT) ( fraction + width ); break; case TK_SCROLL_PAGES: plPlotterPtr->xl += (PLFLT) ( count * width * .9 ); plPlotterPtr->xr += (PLFLT) ( count * width * .9 ); break; case TK_SCROLL_UNITS: plPlotterPtr->xl += (PLFLT) ( count * width / 50 ); plPlotterPtr->xr += (PLFLT) ( count * width / 50 ); break; } if ( plPlotterPtr->xr > 1.0 ) { plPlotterPtr->xr = 1.0; plPlotterPtr->xl = (PLFLT) ( 1.0 - width ); } else if ( plPlotterPtr->xl < 0.0 ) { plPlotterPtr->xl = 0.0; plPlotterPtr->xr = (PLFLT) width; } Scroll( interp, plPlotterPtr ); } // yscroll -- vertically scroll window into plot else if ( ( c == 'y' ) && ( strncmp( argv[1], "yview", (size_t) length ) == 0 ) ) { int count, type; double height = plPlotterPtr->yr - plPlotterPtr->yl; double fraction; type = Tk_GetScrollInfo( interp, argc, argv, &fraction, &count ); switch ( type ) { case TK_SCROLL_ERROR: result = TCL_ERROR; goto done; case TK_SCROLL_MOVETO: plPlotterPtr->yl = (PLFLT) ( 1.0 - fraction - height ); plPlotterPtr->yr = (PLFLT) ( 1.0 - fraction ); break; case TK_SCROLL_PAGES: plPlotterPtr->yl -= (PLFLT) ( count * height * .9 ); plPlotterPtr->yr -= (PLFLT) ( count * height * .9 ); break; case TK_SCROLL_UNITS: plPlotterPtr->yl -= (PLFLT) ( count * height / 50 ); plPlotterPtr->yr -= (PLFLT) ( count * height / 50 ); break; } if ( plPlotterPtr->yr > 1.0 ) { plPlotterPtr->yr = 1.0; plPlotterPtr->yl = (PLFLT) ( 1.0 - height ); } else if ( plPlotterPtr->yl < 0.0 ) { plPlotterPtr->yl = 0.0; plPlotterPtr->yr = (PLFLT) height; } Scroll( interp, plPlotterPtr ); } // unrecognized widget command else { Tcl_AppendResult( interp, "bad option \"", argv[1], "\": must be closelink, cmd, configure, draw, info, nextpage ", "openlink, orient, page, print, redraw, save, view, ", "xview, or yview", (char *) NULL ); result = TCL_ERROR; } done: Tk_Release( (ClientData) plPlotterPtr ); return result; } // //-------------------------------------------------------------------------- // // DestroyPlPlotter -- // // This procedure is invoked by Tk_EventuallyFree or Tk_Release to // clean up the internal structure of a plframe at a safe time // (when no-one is using it anymore). // // Results: // None. // // Side effects: // Everything associated with the plframe is freed up. // //-------------------------------------------------------------------------- // static void DestroyPlPlotter( ClientData clientData ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; register PLRDev *plr = plPlotterPtr->plr; TkwDev *dev = (TkwDev *) plPlotterPtr->pls->dev; dbug_enter( "DestroyPlPlotter" ); dev->flags |= 0x3; if ( plPlotterPtr->border != NULL ) { Tk_Free3DBorder( plPlotterPtr->border ); } if ( plPlotterPtr->plpr_cmd != NULL ) { free( (char *) plPlotterPtr->plpr_cmd ); } if ( plPlotterPtr->cursor != None ) { Tk_FreeCursor( plPlotterPtr->display, plPlotterPtr->cursor ); } if ( plPlotterPtr->xhair_cursor != None ) { Tk_FreeCursor( plPlotterPtr->display, plPlotterPtr->xhair_cursor ); } if ( plPlotterPtr->xorGC != NULL ) { Tk_FreeGC( plPlotterPtr->display, plPlotterPtr->xorGC ); } if ( plPlotterPtr->yScrollCmd != NULL ) { free( (char *) plPlotterPtr->yScrollCmd ); } if ( plPlotterPtr->xScrollCmd != NULL ) { free( (char *) plPlotterPtr->xScrollCmd ); } if ( plPlotterPtr->SaveFnam != NULL ) { free( (char *) plPlotterPtr->SaveFnam ); } if ( plPlotterPtr->devDesc != NULL ) { free( (char *) plPlotterPtr->devDesc ); } if ( plPlotterPtr->devName != NULL ) { free( (char *) plPlotterPtr->devName ); } // Clean up data connection pdf_close( plr->pdfs ); free( (char *) plPlotterPtr->plr->iodev ); // Tell PLplot to clean up plsstrm( plPlotterPtr->ipls ); plend1(); // Delete main data structures free( (char *) plPlotterPtr->plr ); free( (char *) plPlotterPtr ); } // //-------------------------------------------------------------------------- // // PlPlotterConfigureEH -- // // Invoked by the Tk dispatcher on structure changes to a plframe. // // Results: // None. // // Side effects: // When the window gets deleted, internal structures get cleaned up. // When it gets resized, it is redrawn. // //-------------------------------------------------------------------------- // static void PlPlotterConfigureEH( ClientData clientData, register XEvent *eventPtr ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; register Tk_Window tkwin = plPlotterPtr->tkwin; dbug_enter( "PlPlotterConfigureEH" ); switch ( eventPtr->type ) { case ConfigureNotify: pldebug( "PLFrameConfigureEH", "ConfigureNotify\n" ); plPlotterPtr->flags |= RESIZE_PENDING; plPlotterPtr->width = Tk_Width( tkwin ); plPlotterPtr->height = Tk_Height( tkwin ); if ( ( tkwin != NULL ) && !( plPlotterPtr->flags & REFRESH_PENDING ) ) { Tcl_DoWhenIdle( DisplayPlPlotter, (ClientData) plPlotterPtr ); plPlotterPtr->flags |= REFRESH_PENDING; plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR; } break; case DestroyNotify: pldebug( "PLFrameConfigureEH", "DestroyNotify\n" ); Tcl_DeleteCommand( plPlotterPtr->interp, Tk_PathName( tkwin ) ); plPlotterPtr->tkwin = NULL; if ( plPlotterPtr->flags & REFRESH_PENDING ) { Tcl_CancelIdleCall( DisplayPlPlotter, (ClientData) plPlotterPtr ); } Tk_EventuallyFree( (ClientData) plPlotterPtr, (Tcl_FreeProc *) DestroyPlPlotter ); break; case MapNotify: pldebug( "PLFrameConfigureEH", "MapNotify\n" ); if ( plPlotterPtr->flags & REFRESH_PENDING ) { Tcl_CancelIdleCall( DisplayPlPlotter, (ClientData) plPlotterPtr ); } // Vince thinks we don't want these lines any more // We forced the window into existence when we created it. #if 0 // For some reason, "." must be mapped or PlPlotterInit will die (Note: // mapped & withdrawn or mapped in the withdrawn state is OK). Issuing // an update fixes this. I'd love to know why this occurs. // if ( !plPlotterPtr->tkwin_initted ) { Tcl_VarEval( plPlotterPtr->interp, "update", (char *) NULL ); } #endif // Set up window parameters and arrange for window to be refreshed Tcl_DoWhenIdle( PlPlotterInit, (ClientData) plPlotterPtr ); break; } } // //-------------------------------------------------------------------------- // // PlPlotterExposeEH -- // // Invoked by the Tk dispatcher on exposes of a plframe. // // Results: // None. // // Side effects: // Widget is redisplayed. // // Note: it's customary in Tk to collapse multiple exposes, so for best // performance without losing the window contents, I keep track of the // smallest single rectangle that can satisfy all expose events. If there // are any overlaid graphics (like crosshairs), however, we need to refresh // the entire plot in order to have a predictable outcome. // //-------------------------------------------------------------------------- static void PlPlotterExposeEH( ClientData clientData, register XEvent *eventPtr ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; XExposeEvent *event = (XExposeEvent *) eventPtr; register Tk_Window tkwin = plPlotterPtr->tkwin; dbug_enter( "PlPlotterExposeEH" ); pldebug( "PLFrameExposeEH", "Expose\n" ); // Set up the area to refresh if ( !( plPlotterPtr->drawing_xhairs || plPlotterPtr->drawing_rband ) ) { int x0_old, x1_old, y0_old, y1_old, x0_new, x1_new, y0_new, y1_new; x0_old = (int) plPlotterPtr->pldis.x; y0_old = (int) plPlotterPtr->pldis.y; x1_old = x0_old + (int) plPlotterPtr->pldis.width; y1_old = y0_old + (int) plPlotterPtr->pldis.height; x0_new = event->x; y0_new = event->y; x1_new = x0_new + event->width; y1_new = y0_new + event->height; plPlotterPtr->pldis.x = (unsigned int) MIN( x0_old, x0_new ); plPlotterPtr->pldis.y = (unsigned int) MIN( y0_old, y0_new ); plPlotterPtr->pldis.width = (unsigned int) MAX( x1_old, x1_new ) - plPlotterPtr->pldis.x; plPlotterPtr->pldis.height = (unsigned int) MAX( y1_old, y1_new ) - plPlotterPtr->pldis.y; } // Invoke DoWhenIdle handler to redisplay widget. if ( event->count == 0 ) { if ( ( tkwin != NULL ) && !( plPlotterPtr->flags & REFRESH_PENDING ) ) { Tcl_DoWhenIdle( DisplayPlPlotter, (ClientData) plPlotterPtr ); plPlotterPtr->width = Tk_Width( tkwin ); plPlotterPtr->height = Tk_Height( tkwin ); plPlotterPtr->flags |= REFRESH_PENDING; } } } // //-------------------------------------------------------------------------- // // PlPlotterMotionEH -- // // Invoked by the Tk dispatcher on MotionNotify events in a plframe. // Not invoked unless we are drawing graphic crosshairs. // // Results: // None. // // Side effects: // Graphic crosshairs are drawn. // //-------------------------------------------------------------------------- // static void PlPlotterMotionEH( ClientData clientData, register XEvent *eventPtr ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; XMotionEvent *event = (XMotionEvent *) eventPtr; dbug_enter( "PlPlotterMotionEH" ); if ( plPlotterPtr->drawing_xhairs ) { DrawXhairs( plPlotterPtr, event->x, event->y ); } if ( plPlotterPtr->drawing_rband ) { DrawRband( plPlotterPtr, event->x, event->y ); } } // //-------------------------------------------------------------------------- // // PlPlotterEnterEH -- // // Invoked by the Tk dispatcher on EnterNotify events in a plframe. // Not invoked unless we are drawing graphic crosshairs. // // Results: // None. // // Side effects: // Graphic crosshairs are updated. // //-------------------------------------------------------------------------- static void PlPlotterEnterEH( ClientData clientData, register XEvent *eventPtr ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; XCrossingEvent *crossingEvent = (XCrossingEvent *) eventPtr; dbug_enter( "PlPlotterEnterEH" ); if ( plPlotterPtr->xhairs ) { DrawXhairs( plPlotterPtr, crossingEvent->x, crossingEvent->y ); plPlotterPtr->drawing_xhairs = 1; } if ( plPlotterPtr->rband ) { plPlotterPtr->drawing_rband = 1; UpdateRband( plPlotterPtr ); DrawRband( plPlotterPtr, crossingEvent->x, crossingEvent->y ); } } // //-------------------------------------------------------------------------- // // PlPlotterLeaveEH -- // // Invoked by the Tk dispatcher on LeaveNotify events in a plframe. // Not invoked unless we are drawing graphic crosshairs. // // Results: // None. // // Side effects: // Graphic crosshairs are updated. // //-------------------------------------------------------------------------- static void PlPlotterLeaveEH( ClientData clientData, register XEvent * PL_UNUSED( eventPtr ) ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; dbug_enter( "PlPlotterLeaveEH" ); if ( plPlotterPtr->drawing_xhairs ) { UpdateXhairs( plPlotterPtr ); plPlotterPtr->drawing_xhairs = 0; } if ( plPlotterPtr->drawing_rband ) { UpdateRband( plPlotterPtr ); plPlotterPtr->drawing_rband = 0; } } static void PlPlotterButtonPressEH( ClientData clientData, register XEvent *eventPtr ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; XButtonEvent *event = (XButtonEvent *) eventPtr; // Get modifier keys switch ( event->state ) { case 256: // plain if ( plPlotterPtr->drawing_rband ) { UpdateRband( plPlotterPtr ); } if ( plPlotterPtr->rband ) CreateRband( plPlotterPtr ); break; } } //-------------------------------------------------------------------------- // CreateXhairs() // // Creates graphic crosshairs at current pointer location. //-------------------------------------------------------------------------- static void CreateXhairs( PlPlotter *plPlotterPtr ) { register Tk_Window tkwin = plPlotterPtr->tkwin; Window root, child; int root_x, root_y, win_x, win_y; unsigned int mask; // Find current pointer location and draw graphic crosshairs if pointer is // inside our window. if ( XQueryPointer( plPlotterPtr->display, Tk_WindowId( tkwin ), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask ) ) { #ifdef MAC_TCL // Mac Tk only has a partial implementation of the above function // so we must fix it Tk_GetRootCoords( tkwin, &win_x, &win_y ); win_x = root_x - win_x; win_y = root_y - win_y; #endif if ( win_x >= 0 && win_x < Tk_Width( tkwin ) && win_y >= 0 && win_y < Tk_Height( tkwin ) ) { DrawXhairs( plPlotterPtr, win_x, win_y ); plPlotterPtr->drawing_xhairs = 1; } } } //-------------------------------------------------------------------------- // DestroyXhairs() // // Destroys graphic crosshairs. //-------------------------------------------------------------------------- static void DestroyXhairs( PlPlotter *plPlotterPtr ) { // This draw removes the last set of graphic crosshairs UpdateXhairs( plPlotterPtr ); plPlotterPtr->drawing_xhairs = 0; } //-------------------------------------------------------------------------- // DrawXhairs() // // Draws graphic crosshairs at (x0, y0). The first draw erases the old set. //-------------------------------------------------------------------------- static void DrawXhairs( PlPlotter *plPlotterPtr, int x0, int y0 ) { register Tk_Window tkwin = plPlotterPtr->tkwin; int xmin = 0, xmax = Tk_Width( tkwin ) - 1; int ymin = 0, ymax = Tk_Height( tkwin ) - 1; if ( plPlotterPtr->drawing_xhairs ) UpdateXhairs( plPlotterPtr ); plPlotterPtr->xhair_x[0].x = (short) xmin; plPlotterPtr->xhair_x[0].y = (short) y0; plPlotterPtr->xhair_x[1].x = (short) xmax; plPlotterPtr->xhair_x[1].y = (short) y0; plPlotterPtr->xhair_y[0].x = (short) x0; plPlotterPtr->xhair_y[0].y = (short) ymin; plPlotterPtr->xhair_y[1].x = (short) x0; plPlotterPtr->xhair_y[1].y = (short) ymax; UpdateXhairs( plPlotterPtr ); } //-------------------------------------------------------------------------- // UpdateXhairs() // // Updates graphic crosshairs. If already there, they are erased. //-------------------------------------------------------------------------- static void UpdateXhairs( PlPlotter *plPlotterPtr ) { register Tk_Window tkwin = plPlotterPtr->tkwin; XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ), plPlotterPtr->xorGC, plPlotterPtr->xhair_x, 2, CoordModeOrigin ); XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ), plPlotterPtr->xorGC, plPlotterPtr->xhair_y, 2, CoordModeOrigin ); } //-------------------------------------------------------------------------- // CreateRband() // // Initiate rubber banding. //-------------------------------------------------------------------------- static void CreateRband( PlPlotter *plPlotterPtr ) { register Tk_Window tkwin = plPlotterPtr->tkwin; Window root, child; int root_x, root_y, win_x, win_y; unsigned int mask; // Find current pointer location, and initiate rubber banding. if ( XQueryPointer( plPlotterPtr->display, Tk_WindowId( tkwin ), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask ) ) { #ifdef MAC_TCL // Mac Tk only has a partial implementation of the above function // so we must fix it Tk_GetRootCoords( tkwin, &win_x, &win_y ); win_x = root_x - win_x; win_y = root_y - win_y; #endif if ( win_x >= 0 && win_x < Tk_Width( tkwin ) && win_y >= 0 && win_y < Tk_Height( tkwin ) ) { // Okay, pointer is in our window. plPlotterPtr->rband_pt[0].x = (short) win_x; plPlotterPtr->rband_pt[0].y = (short) win_y; DrawRband( plPlotterPtr, win_x, win_y ); plPlotterPtr->drawing_rband = 1; } else { // Hmm, somehow they turned it on without even being in the window. // Just put the anchor in top left, they'll soon realize this is a // mistake... plPlotterPtr->rband_pt[0].x = 0; plPlotterPtr->rband_pt[0].y = 0; DrawRband( plPlotterPtr, win_x, win_y ); plPlotterPtr->drawing_rband = 1; } } } //-------------------------------------------------------------------------- // DestroyRband() // // Turn off rubber banding. //-------------------------------------------------------------------------- static void DestroyRband( PlPlotter *plPlotterPtr ) { // This draw removes the residual rubber band. UpdateRband( plPlotterPtr ); plPlotterPtr->drawing_rband = 0; } //-------------------------------------------------------------------------- // DrawRband() // // Draws a rubber band from the anchor to the current cursor location. //-------------------------------------------------------------------------- static void DrawRband( PlPlotter *plPlotterPtr, int x0, int y0 ) { // If the line is already up, clear it. if ( plPlotterPtr->drawing_rband ) UpdateRband( plPlotterPtr ); plPlotterPtr->rband_pt[1].x = (short) x0; plPlotterPtr->rband_pt[1].y = (short) y0; UpdateRband( plPlotterPtr ); } //-------------------------------------------------------------------------- // UpdateRband() // // Updates rubber band. If already there, it is erased. //-------------------------------------------------------------------------- static void UpdateRband( PlPlotter *plPlotterPtr ) { register Tk_Window tkwin = plPlotterPtr->tkwin; XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ), plPlotterPtr->xorGC, plPlotterPtr->rband_pt, 2, CoordModeOrigin ); } // First-time initialization static void PlPlotterFirstInit( ClientData clientData ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; register Tk_Window tkwin = plPlotterPtr->tkwin; plsstrm( plPlotterPtr->ipls ); plsdev( "tkwin" ); // We should probably rename plsxwin to plstkwin plsxwin( (PLINT) Tk_WindowId( tkwin ) ); plspause( 0 ); plinit(); if ( plplot_tkwin_ccmap ) { Install_cmap( plPlotterPtr ); } plbop(); plPlotterPtr->tkwin_initted = 1; plPlotterPtr->width = Tk_Width( tkwin ); plPlotterPtr->height = Tk_Height( tkwin ); plPlotterPtr->prevWidth = plPlotterPtr->width; plPlotterPtr->prevHeight = plPlotterPtr->height; } // //-------------------------------------------------------------------------- // // PlPlotterInit -- // // Invoked to handle miscellaneous initialization after window gets // mapped. // // Results: // None. // // Side effects: // PLplot internal parameters and device driver are initialized. // //-------------------------------------------------------------------------- static void PlPlotterInit( ClientData clientData ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; // Set up window parameters and arrange for window to be refreshed plPlotterPtr->flags |= REFRESH_PENDING; plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR; // Draw plframe DisplayPlPlotter( clientData ); if ( plPlotterPtr->xhairs ) CreateXhairs( plPlotterPtr ); if ( plPlotterPtr->rband ) CreateRband( plPlotterPtr ); } // //-------------------------------------------------------------------------- // // Install_cmap -- // // Installs X driver color map as necessary when custom color maps // are used. // // Results: // None. // // Side effects: // Parent color maps may get changed. // //-------------------------------------------------------------------------- // static void Install_cmap( PlPlotter *plPlotterPtr ) { TkwDev *dev; #define INSTALL_COLORMAP_IN_TK #ifdef INSTALL_COLORMAP_IN_TK dev = (TkwDev *) plPlotterPtr->pls->dev; Tk_SetWindowColormap( Tk_MainWindow( plPlotterPtr->interp ), dev->tkwd->map ); // // If the colormap is local to this widget, the WM must be informed that // it should be installed when the widget gets the focus. The top level // window must be added to the end of its own list, because otherwise the // window manager adds it to the front (as required by the ICCCM). Thanks // to Paul Mackerras for providing this info in his TK photo widget. // #else int count = 0; Window top, colormap_windows[5]; top = Tk_WindowId( Tk_MainWindow( plPlotterPtr->interp ) ); colormap_windows[count++] = Tk_WindowId( plPlotterPtr->tkwin ); colormap_windows[count++] = top; if ( !XSetWMColormapWindows( plPlotterPtr->display, top, colormap_windows, count ) ) fprintf( stderr, "Unable to set color map property!\n" ); #endif } // //-------------------------------------------------------------------------- // // DisplayPlPlotter -- // // This procedure is invoked to display a plframe widget. // // Results: // None. // // Side effects: // Commands are output to X to display the plframe in its // current mode. // //-------------------------------------------------------------------------- // static void DisplayPlPlotter( ClientData clientData ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; register Tk_Window tkwin = plPlotterPtr->tkwin; dbug_enter( "DisplayPlPlotter" ); // Update scrollbars if needed if ( plPlotterPtr->flags & UPDATE_V_SCROLLBAR ) { UpdateVScrollbar( plPlotterPtr ); } if ( plPlotterPtr->flags & UPDATE_H_SCROLLBAR ) { UpdateHScrollbar( plPlotterPtr ); } plPlotterPtr->flags &= ~( UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR ); // If not mapped yet, just return and cancel pending refresh if ( ( plPlotterPtr->tkwin == NULL ) || !Tk_IsMapped( tkwin ) ) { plPlotterPtr->flags &= ~REFRESH_PENDING; return; } // All refresh events if ( plPlotterPtr->flags & REFRESH_PENDING ) { plPlotterPtr->flags &= ~REFRESH_PENDING; // Reschedule resizes to avoid occasional ordering conflicts with // the packer's resize of the window (this call must come last). if ( plPlotterPtr->flags & RESIZE_PENDING ) { plPlotterPtr->flags |= REFRESH_PENDING; plPlotterPtr->flags &= ~RESIZE_PENDING; Tcl_DoWhenIdle( DisplayPlPlotter, clientData ); return; } // Redraw border if necessary if ( ( plPlotterPtr->border != NULL ) && ( plPlotterPtr->relief != TK_RELIEF_FLAT ) ) { Tk_Draw3DRectangle( plPlotterPtr->tkwin, Tk_WindowId( tkwin ), plPlotterPtr->border, 0, 0, Tk_Width( tkwin ), Tk_Height( tkwin ), plPlotterPtr->borderWidth, plPlotterPtr->relief ); } // Redraw -- replay contents of plot buffer if ( plPlotterPtr->flags & REDRAW_PENDING ) { plPlotterPtr->flags &= ~REDRAW_PENDING; plsstrm( plPlotterPtr->ipls ); pl_cmd( PLESC_REDRAW, (void *) NULL ); } // Resize -- if window bounds have changed else if ( ( plPlotterPtr->width != plPlotterPtr->prevWidth ) || ( plPlotterPtr->height != plPlotterPtr->prevHeight ) ) { plPlotterPtr->pldis.width = (unsigned int) ( plPlotterPtr->width ); plPlotterPtr->pldis.height = (unsigned int) ( plPlotterPtr->height ); plsstrm( plPlotterPtr->ipls ); pl_cmd( PLESC_RESIZE, (void *) &( plPlotterPtr->pldis ) ); plPlotterPtr->prevWidth = plPlotterPtr->width; plPlotterPtr->prevHeight = plPlotterPtr->height; } // Expose -- if window bounds are unchanged else { if ( plPlotterPtr->drawing_xhairs ) { #ifdef MAC_TCL // For MacTk we just zap the whole window like this Tk_Draw3DRectangle( plPlotterPtr->tkwin, Tk_WindowId( tkwin ), plPlotterPtr->border, 0, 0, Tk_Width( tkwin ), Tk_Height( tkwin ), plPlotterPtr->borderWidth, plPlotterPtr->relief ); #else XClearWindow( plPlotterPtr->display, Tk_WindowId( tkwin ) ); #endif XFlush( plPlotterPtr->display ); plsstrm( plPlotterPtr->ipls ); pl_cmd( PLESC_EXPOSE, NULL ); } else { plsstrm( plPlotterPtr->ipls ); pl_cmd( PLESC_EXPOSE, (void *) &( plPlotterPtr->pldis ) ); } // Reset window bounds so that next time they are set fresh plPlotterPtr->pldis.x = (unsigned int) ( Tk_X( tkwin ) + Tk_Width( tkwin ) ); plPlotterPtr->pldis.y = (unsigned int) ( Tk_Y( tkwin ) + Tk_Height( tkwin ) ); plPlotterPtr->pldis.width = (unsigned int) -Tk_Width( tkwin ); plPlotterPtr->pldis.height = (unsigned int) -Tk_Height( tkwin ); } // Update graphic crosshairs if necessary if ( plPlotterPtr->drawing_xhairs ) { UpdateXhairs( plPlotterPtr ); } // Update rubber band if necessary. if ( plPlotterPtr->drawing_rband ) { UpdateRband( plPlotterPtr ); } } } //-------------------------------------------------------------------------- // Routines to process widget commands. //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // scol0 // // Sets a color in cmap0. //-------------------------------------------------------------------------- static int scol0( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int i, CONST char *col, int *p_changed ) { PLStream *pls = plPlotterPtr->pls; XColor xcol; PLINT r, g, b; if ( col == NULL ) { Tcl_AppendResult( interp, "color value not specified", (char *) NULL ); return TCL_ERROR; } if ( !XParseColor( plPlotterPtr->display, Tk_Colormap( plPlotterPtr->tkwin ), col, &xcol ) ) { Tcl_AppendResult( interp, "Couldn't parse color ", col, (char *) NULL ); return TCL_ERROR; } r = (unsigned) ( xcol.red & 0xFF00 ) >> 8; g = (unsigned) ( xcol.green & 0xFF00 ) >> 8; b = (unsigned) ( xcol.blue & 0xFF00 ) >> 8; if ( ( pls->cmap0[i].r != r ) || ( pls->cmap0[i].g != g ) || ( pls->cmap0[i].b != b ) ) { pls->cmap0[i].r = (unsigned char) r; pls->cmap0[i].g = (unsigned char) g; pls->cmap0[i].b = (unsigned char) b; *p_changed = 1; } return TCL_OK; } //-------------------------------------------------------------------------- // scol1 // // Sets a color in cmap1. //-------------------------------------------------------------------------- static int scol1( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int i, CONST char *col, CONST char *pos, CONST char *rev, int *p_changed ) { PLStream *pls = plPlotterPtr->pls; XColor xcol; PLFLT h, l, s, r, g, b, p; int reverse; if ( col == NULL ) { Tcl_AppendResult( interp, "color value not specified", (char *) NULL ); return TCL_ERROR; } if ( pos == NULL ) { Tcl_AppendResult( interp, "control point position not specified", (char *) NULL ); return TCL_ERROR; } if ( rev == NULL ) { Tcl_AppendResult( interp, "interpolation sense not specified", (char *) NULL ); return TCL_ERROR; } if ( !XParseColor( plPlotterPtr->display, Tk_Colormap( plPlotterPtr->tkwin ), col, &xcol ) ) { Tcl_AppendResult( interp, "Couldn't parse color ", col, (char *) NULL ); return TCL_ERROR; } r = (PLFLT) ( ( (unsigned) ( xcol.red & 0xFF00 ) >> 8 ) / 255.0 ); g = (PLFLT) ( ( (unsigned) ( xcol.green & 0xFF00 ) >> 8 ) / 255.0 ); b = (PLFLT) ( ( (unsigned) ( xcol.blue & 0xFF00 ) >> 8 ) / 255.0 ); plrgbhls( r, g, b, &h, &l, &s ); p = (PLFLT) ( atof( pos ) / 100.0 ); reverse = atoi( rev ); if ( ( pls->cmap1cp[i].c1 != h ) || ( pls->cmap1cp[i].c2 != l ) || ( pls->cmap1cp[i].c3 != s ) || ( pls->cmap1cp[i].p != p ) || ( pls->cmap1cp[i].alt_hue_path != reverse ) ) { pls->cmap1cp[i].c1 = h; pls->cmap1cp[i].c2 = l; pls->cmap1cp[i].c2 = s; pls->cmap1cp[i].p = p; pls->cmap1cp[i].alt_hue_path = reverse; *p_changed = 1; } return TCL_OK; } //-------------------------------------------------------------------------- // Cmd // // Processes "cmd" widget command. // Handles commands that go more or less directly to the PLplot library. // Most of these come out of the PLplot Tcl API support file. //-------------------------------------------------------------------------- static int Cmd( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { PLStream *pls = plPlotterPtr->pls; int length; char c3; int result = TCL_OK; char cmdlist[] = "plgcmap0 plgcmap1 plscmap0 plscmap1 plscol0 plscol1"; char * argv_cp; #ifdef DEBUG if ( pls->debug ) { int i; fprintf( stderr, "There are %d arguments to Cmd:", argc ); for ( i = 0; i < argc; i++ ) { fprintf( stderr, " %s", argv[i] ); } fprintf( stderr, "\n" ); } #endif // no option -- return list of available PLplot commands if ( argc == 0 ) return plTclCmd( cmdlist, interp, argc, argv ); // Make sure widget has been initialized before going any further if ( !plPlotterPtr->tkwin_initted ) { Tcl_VarEval( plPlotterPtr->interp, "update", (char *) NULL ); } // Set stream number and get ready to process the command plsstrm( plPlotterPtr->ipls ); c3 = argv[0][2]; length = (int) strlen( argv[0] ); // plgcmap0 -- get color map 0 // first arg is number of colors, the rest are hex number specifications if ( ( c3 == 'g' ) && ( strncmp( argv[0], "plgcmap0", (size_t) length ) == 0 ) ) { int i; unsigned long plcolor; char str[10]; sprintf( str, "%d", (int) pls->ncol0 ); Tcl_AppendElement( interp, str ); for ( i = 0; i < pls->ncol0; i++ ) { plcolor = (unsigned long) ( ( pls->cmap0[i].r << 16 ) | ( pls->cmap0[i].g << 8 ) | ( pls->cmap0[i].b ) ); sprintf( str, "#%06lx", ( plcolor & 0xFFFFFF ) ); Tcl_AppendElement( interp, str ); } result = TCL_OK; } // plgcmap1 -- get color map 1 // first arg is number of control points // the rest are hex number specifications followed by positions (0-100) else if ( ( c3 == 'g' ) && ( strncmp( argv[0], "plgcmap1", (size_t) length ) == 0 ) ) { int i; unsigned long plcolor; char str[10]; PLFLT h, l, s, r, g, b; int r1, g1, b1; sprintf( str, "%d", (int) pls->ncp1 ); Tcl_AppendElement( interp, str ); for ( i = 0; i < pls->ncp1; i++ ) { h = pls->cmap1cp[i].c1; l = pls->cmap1cp[i].c2; s = pls->cmap1cp[i].c3; plhlsrgb( h, l, s, &r, &g, &b ); r1 = MAX( 0, MIN( 255, (int) ( 256. * r ) ) ); g1 = MAX( 0, MIN( 255, (int) ( 256. * g ) ) ); b1 = MAX( 0, MIN( 255, (int) ( 256. * b ) ) ); plcolor = (unsigned long) ( ( r1 << 16 ) | ( g1 << 8 ) | ( b1 ) ); sprintf( str, "#%06lx", ( plcolor & 0xFFFFFF ) ); Tcl_AppendElement( interp, str ); sprintf( str, "%02d", (int) ( 100 * pls->cmap1cp[i].p ) ); Tcl_AppendElement( interp, str ); sprintf( str, "%01d", (int) ( pls->cmap1cp[i].alt_hue_path ) ); Tcl_AppendElement( interp, str ); } result = TCL_OK; } // plscmap0 -- set color map 0 // first arg is number of colors, the rest are hex number specifications else if ( ( c3 == 's' ) && ( strncmp( argv[0], "plscmap0", (size_t) length ) == 0 ) ) { int i, changed = 1, ncol0 = atoi( argv[1] ); char *col; if ( ncol0 > 16 || ncol0 < 1 ) { Tcl_AppendResult( interp, "illegal number of colors in cmap0: ", argv[1], (char *) NULL ); return TCL_ERROR; } pls->ncol0 = ncol0; for ( i = 0; i < pls->ncol0; i++ ) { argv_cp = plstrdup( argv[2 + i] ); col = strtok( argv_cp, " " ); if ( col == NULL ) break; if ( scol0( interp, plPlotterPtr, i, col, &changed ) != TCL_OK ) return TCL_ERROR; free_mem( argv_cp ); } if ( changed ) plP_state( PLSTATE_CMAP0 ); } // plscmap1 -- set color map 1 // first arg is number of colors, the rest are hex number specifications else if ( ( c3 == 's' ) && ( strncmp( argv[0], "plscmap1", (size_t) length ) == 0 ) ) { int i, changed = 1, ncp1 = atoi( argv[1] ); char *col, *pos, *rev; if ( ncp1 > 32 || ncp1 < 1 ) { Tcl_AppendResult( interp, "illegal number of control points in cmap1: ", argv[1], (char *) NULL ); return TCL_ERROR; } argv_cp = plstrdup( argv[2] ); col = strtok( argv_cp, " " ); pos = strtok( NULL, " " ); rev = strtok( NULL, " " ); for ( i = 0; i < ncp1; i++ ) { if ( col == NULL ) break; if ( scol1( interp, plPlotterPtr, i, col, pos, rev, &changed ) != TCL_OK ) return TCL_ERROR; col = strtok( NULL, " " ); pos = strtok( NULL, " " ); rev = strtok( NULL, " " ); } free_mem( argv_cp ); if ( changed ) { plsc->ncp1 = ncp1; plcmap1_calc(); } } // plscol0 -- set single color in cmap0 // first arg is the color number, the next is the color in hex else if ( ( c3 == 's' ) && ( strncmp( argv[0], "plscol0", (size_t) length ) == 0 ) ) { int i = atoi( argv[1] ), changed = 1; if ( i > pls->ncol0 || i < 0 ) { Tcl_AppendResult( interp, "illegal color number in cmap0: ", argv[1], (char *) NULL ); return TCL_ERROR; } if ( scol0( interp, plPlotterPtr, i, argv[2], &changed ) != TCL_OK ) return TCL_ERROR; if ( changed ) plP_state( PLSTATE_CMAP0 ); } // plscol1 -- set color of control point in cmap1 // first arg is the control point, the next two are the color in hex and pos else if ( ( c3 == 's' ) && ( strncmp( argv[0], "plscol1", (size_t) length ) == 0 ) ) { int i = atoi( argv[1] ), changed = 1; if ( i > pls->ncp1 || i < 0 ) { Tcl_AppendResult( interp, "illegal control point number in cmap1: ", argv[1], (char *) NULL ); return TCL_ERROR; } if ( scol1( interp, plPlotterPtr, i, argv[2], argv[3], argv[4], &changed ) != TCL_OK ) return TCL_ERROR; if ( changed ) plcmap1_calc(); } // Added by Vince, disabled by default since we want a minimal patch #ifdef USING_PLESC_COPY // plcopy -- copy a region of the plot; useful for scrolling plots // first 4 args are the source rectangle, next 2 args are the destination else if ( ( c3 == 'c' ) && ( strncmp( argv[0], "plcopy", (size_t) length ) == 0 ) ) { PLFLT xx[3], yy[3]; if ( argc != 7 ) { Tcl_AppendResult( interp, "Need exactly 6 arguments to copy.", (char *) NULL ); return TCL_ERROR; } xx[0] = atof( argv[1] ); yy[0] = atof( argv[2] ); xx[1] = atof( argv[3] ); yy[1] = atof( argv[4] ); xx[2] = atof( argv[5] ); yy[2] = atof( argv[6] ); plcopy( xx, yy ); } #endif // unrecognized, so give it to plTclCmd to take care of else result = plTclCmd( cmdlist, interp, argc, argv ); plflush(); return result; } static void ActiveState( register PlPlotter *plPlotterPtr, int on ) { if ( on ) { if ( !plPlotterPtr->isActive ) { Tk_CreateEventHandler( plPlotterPtr->tkwin, ButtonPressMask, PlPlotterButtonPressEH, (ClientData) plPlotterPtr ); Tk_CreateEventHandler( plPlotterPtr->tkwin, PointerMotionMask, PlPlotterMotionEH, (ClientData) plPlotterPtr ); Tk_CreateEventHandler( plPlotterPtr->tkwin, EnterWindowMask, PlPlotterEnterEH, (ClientData) plPlotterPtr ); Tk_CreateEventHandler( plPlotterPtr->tkwin, LeaveWindowMask, PlPlotterLeaveEH, (ClientData) plPlotterPtr ); // Switch to crosshair cursor. Tk_DefineCursor( plPlotterPtr->tkwin, plPlotterPtr->xhair_cursor ); } } else { if ( plPlotterPtr->isActive ) { Tk_DeleteEventHandler( plPlotterPtr->tkwin, ButtonPressMask, PlPlotterButtonPressEH, (ClientData) plPlotterPtr ); Tk_DeleteEventHandler( plPlotterPtr->tkwin, PointerMotionMask, PlPlotterMotionEH, (ClientData) plPlotterPtr ); Tk_DeleteEventHandler( plPlotterPtr->tkwin, EnterWindowMask, PlPlotterEnterEH, (ClientData) plPlotterPtr ); Tk_DeleteEventHandler( plPlotterPtr->tkwin, LeaveWindowMask, PlPlotterLeaveEH, (ClientData) plPlotterPtr ); // Switch back to boring old pointer Tk_DefineCursor( plPlotterPtr->tkwin, plPlotterPtr->cursor ); } } } // //-------------------------------------------------------------------------- // // ConfigurePlPlotter -- // // This procedure is called to process an argv/argc list, plus the Tk // option database, in order to configure (or reconfigure) a // plframe widget. // // Results: // The return value is a standard Tcl result. If TCL_ERROR is // returned, then interp->result contains an error message. // // Side effects: // Configuration information, such as text string, colors, font, etc. // get set for plPlotterPtr; old resources get freed, if there were // any. // //-------------------------------------------------------------------------- // static int ConfigurePlPlotter( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv, int flags ) { register Tk_Window tkwin = plPlotterPtr->tkwin; PLStream *pls = plPlotterPtr->pls; TkwDev *dev = (TkwDev *) pls->dev; TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd; XGCValues gcValues; unsigned long mask; int need_redisplay = 0; #ifdef DEBUG if ( pls->debug ) { int i; fprintf( stderr, "Arguments to configure are:" ); for ( i = 0; i < argc; i++ ) { fprintf( stderr, " %s", argv[i] ); } fprintf( stderr, "\n" ); } #endif dbug_enter( "ConfigurePlPlotter" ); if ( Tk_ConfigureWidget( interp, tkwin, configSpecs, argc, argv, (char *) plPlotterPtr, flags ) != TCL_OK ) { return TCL_ERROR; } // // Set background color using tkwin driver's pixel value. Done this way so // that (a) we can use r/w color cells, and (b) the BG pixel values as set // here and in the tkwin driver are consistent. // plsstrm( plPlotterPtr->ipls ); if ( PLColor_from_TkColor_Changed( &pls->cmap0[0], Tk_3DBorderColor( plPlotterPtr->border ) ) ) { need_redisplay = 1; // need to redraw as well as simply refresh the window plPlotterPtr->flags |= REDRAW_PENDING; } pltkwin_setBGFG( pls ); Tk_SetWindowBackground( tkwin, tkwd->cmap0[0].pixel ); Tk_SetWindowBorder( tkwin, tkwd->cmap0[0].pixel ); // Set up GC for rubber-band draws gcValues.background = tkwd->cmap0[0].pixel; gcValues.foreground = 0xFF; gcValues.function = GXxor; mask = GCForeground | GCBackground | GCFunction; if ( plPlotterPtr->xorGC != NULL ) Tk_FreeGC( plPlotterPtr->display, plPlotterPtr->xorGC ); plPlotterPtr->xorGC = Tk_GetGC( plPlotterPtr->tkwin, mask, &gcValues ); // Geometry settings Tk_SetInternalBorder( tkwin, plPlotterPtr->borderWidth ); if ( ( plPlotterPtr->width > 0 ) || ( plPlotterPtr->height > 0 ) ) { Tk_GeometryRequest( tkwin, plPlotterPtr->width, plPlotterPtr->height ); if ( ( plPlotterPtr->width != plPlotterPtr->prevWidth ) || ( plPlotterPtr->height != plPlotterPtr->prevHeight ) ) need_redisplay = 1; } // Create or destroy graphic crosshairs as specified if ( Tk_IsMapped( tkwin ) ) { if ( plPlotterPtr->xhairs ) { if ( !plPlotterPtr->drawing_xhairs ) CreateXhairs( plPlotterPtr ); } else { if ( plPlotterPtr->drawing_xhairs ) DestroyXhairs( plPlotterPtr ); } } // Create or destroy rubber band as specified if ( Tk_IsMapped( tkwin ) ) { if ( plPlotterPtr->rband ) { if ( !plPlotterPtr->drawing_rband ) CreateRband( plPlotterPtr ); } else { if ( plPlotterPtr->drawing_rband ) DestroyRband( plPlotterPtr ); } } // Sets or clears events for the plot ActiveState( plPlotterPtr, plPlotterPtr->active_plot ); if ( !pls->nopixmap ) { // can only adjust if this flag not set if ( plPlotterPtr->double_buffer != pls->db ) { pls->db = plPlotterPtr->double_buffer; // turn on/off dbl-buffering in the driver dev->write_to_window = !pls->db; } } else { plPlotterPtr->double_buffer = 0; } // Arrange for window to be refreshed if necessary if ( need_redisplay && Tk_IsMapped( tkwin ) && !( plPlotterPtr->flags & REFRESH_PENDING ) ) { Tcl_DoWhenIdle( DisplayPlPlotter, (ClientData) plPlotterPtr ); plPlotterPtr->flags |= REFRESH_PENDING; plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR; } return TCL_OK; } //-------------------------------------------------------------------------- // Draw // // Processes "draw" widget command. // Handles rubber-band drawing. //-------------------------------------------------------------------------- static int Draw( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { register Tk_Window tkwin = plPlotterPtr->tkwin; int result = TCL_OK; char c = argv[0][0]; int length = (int) strlen( argv[0] ); // Make sure widget has been initialized before going any further if ( !plPlotterPtr->tkwin_initted ) { Tcl_VarEval( plPlotterPtr->interp, "update", (char *) NULL ); } // init -- sets up for rubber-band drawing if ( ( c == 'i' ) && ( strncmp( argv[0], "init", (size_t) length ) == 0 ) ) { Tk_DefineCursor( tkwin, plPlotterPtr->xhair_cursor ); } // end -- ends rubber-band drawing else if ( ( c == 'e' ) && ( strncmp( argv[0], "end", (size_t) length ) == 0 ) ) { Tk_DefineCursor( tkwin, plPlotterPtr->cursor ); if ( plPlotterPtr->continue_draw ) { XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ), plPlotterPtr->xorGC, plPlotterPtr->pts, 5, CoordModeOrigin ); XSync( Tk_Display( tkwin ), 0 ); } plPlotterPtr->continue_draw = 0; } // rect -- draw a rectangle, used to select rectangular areas // first draw erases old outline else if ( ( c == 'r' ) && ( strncmp( argv[0], "rect", (size_t) length ) == 0 ) ) { if ( argc < 5 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", " draw rect x0 y0 x1 y1\"", (char *) NULL ); result = TCL_ERROR; } else { int x0, y0, x1, y1; int xmin = 0, xmax = Tk_Width( tkwin ) - 1; int ymin = 0, ymax = Tk_Height( tkwin ) - 1; x0 = atoi( argv[1] ); y0 = atoi( argv[2] ); x1 = atoi( argv[3] ); y1 = atoi( argv[4] ); x0 = MAX( xmin, MIN( xmax, x0 ) ); y0 = MAX( ymin, MIN( ymax, y0 ) ); x1 = MAX( xmin, MIN( xmax, x1 ) ); y1 = MAX( ymin, MIN( ymax, y1 ) ); if ( plPlotterPtr->continue_draw ) { XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ), plPlotterPtr->xorGC, plPlotterPtr->pts, 5, CoordModeOrigin ); XSync( Tk_Display( tkwin ), 0 ); } plPlotterPtr->pts[0].x = (short) x0; plPlotterPtr->pts[0].y = (short) y0; plPlotterPtr->pts[1].x = (short) x1; plPlotterPtr->pts[1].y = (short) y0; plPlotterPtr->pts[2].x = (short) x1; plPlotterPtr->pts[2].y = (short) y1; plPlotterPtr->pts[3].x = (short) x0; plPlotterPtr->pts[3].y = (short) y1; plPlotterPtr->pts[4].x = (short) x0; plPlotterPtr->pts[4].y = (short) y0; XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ), plPlotterPtr->xorGC, plPlotterPtr->pts, 5, CoordModeOrigin ); XSync( Tk_Display( tkwin ), 0 ); plPlotterPtr->continue_draw = 1; } } return result; } //-------------------------------------------------------------------------- // Info // // Processes "info" widget command. // Returns requested info. //-------------------------------------------------------------------------- static int Info( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { int length; char c; int result = TCL_OK; // no option -- return list of available info commands if ( argc == 0 ) { Tcl_SetResult( interp, "devkeys devnames", TCL_STATIC ); return TCL_OK; } c = argv[0][0]; length = (int) strlen( argv[0] ); // devkeys -- return list of supported device keywords if ( ( c == 'd' ) && ( strncmp( argv[0], "devkeys", (size_t) length ) == 0 ) ) { int i = 0; while ( plPlotterPtr->devName[i] != NULL ) Tcl_AppendElement( interp, plPlotterPtr->devName[i++] ); result = TCL_OK; } // devkeys -- return list of supported device types else if ( ( c == 'd' ) && ( strncmp( argv[0], "devnames", (size_t) length ) == 0 ) ) { int i = 0; while ( plPlotterPtr->devDesc[i] != NULL ) Tcl_AppendElement( interp, plPlotterPtr->devDesc[i++] ); result = TCL_OK; } // unrecognized else { Tcl_AppendResult( interp, "bad option to \"info\": must be ", "devkeys, devnames", (char *) NULL ); result = TCL_ERROR; } return result; } //-------------------------------------------------------------------------- // Openlink // // Processes "openlink" widget command. // Opens channel (FIFO or socket) for binary data transfer between client // and server. //-------------------------------------------------------------------------- static int Openlink( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) register PLRDev *plr = plPlotterPtr->plr; register PLiodev *iodev = plr->iodev; char c = argv[0][0]; int length = (int) strlen( argv[0] ); dbug_enter( "Openlink" ); // Open fifo if ( ( c == 'f' ) && ( strncmp( argv[0], "fifo", (size_t) length ) == 0 ) ) { if ( argc < 1 ) { Tcl_AppendResult( interp, "bad command -- must be: ", "openlink fifo ", (char *) NULL ); return TCL_ERROR; } if ( ( iodev->fd = open( argv[1], O_RDONLY ) ) == -1 ) { Tcl_AppendResult( interp, "cannot open fifo ", argv[1], " for read", (char *) NULL ); return TCL_ERROR; } iodev->type = 0; iodev->typeName = "fifo"; iodev->file = fdopen( iodev->fd, "rb" ); } // Open socket else if ( ( c == 's' ) && ( strncmp( argv[0], "socket", (size_t) length ) == 0 ) ) { if ( argc < 1 ) { Tcl_AppendResult( interp, "bad command -- must be: ", "openlink socket ", (char *) NULL ); return TCL_ERROR; } iodev->type = 1; iodev->typeName = "socket"; iodev->fileHandle = (char *) argv[1]; if ( Tcl_GetOpenFile( interp, iodev->fileHandle, 0, 1, ( ClientData ) & iodev->file ) != TCL_OK ) { return TCL_ERROR; } iodev->fd = fileno( iodev->file ); } // unrecognized else { Tcl_AppendResult( interp, "bad option to \"openlink\": must be ", "fifo or socket", (char *) NULL ); return TCL_ERROR; } plr->pdfs = pdf_bopen( NULL, 4200 ); Tcl_CreateFileHandler( iodev->fd, TK_READABLE, (Tk_FileProc *) ReadData, (ClientData) plPlotterPtr ); #endif return TCL_OK; } //-------------------------------------------------------------------------- // Closelink // // Processes "closelink" widget command. // CLoses channel previously opened with the "openlink" widget command. //-------------------------------------------------------------------------- static int Closelink( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int PL_UNUSED( argc ), CONST char ** PL_UNUSED( argv ) ) { #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) register PLRDev *plr = plPlotterPtr->plr; register PLiodev *iodev = plr->iodev; dbug_enter( "Closelink" ); if ( iodev->fd == 0 ) { Tcl_AppendResult( interp, "no link currently open", (char *) NULL ); return TCL_ERROR; } Tcl_DeleteFileHandler( iodev->fd ); pdf_close( plr->pdfs ); iodev->fd = 0; #endif return TCL_OK; } //-------------------------------------------------------------------------- // process_data // // Utility function for processing data and other housekeeping. //-------------------------------------------------------------------------- static int process_data( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ) { register PLRDev *plr = plPlotterPtr->plr; register PLiodev *iodev = plr->iodev; int result = TCL_OK; // Process data if ( plr_process( plr ) == -1 ) { Tcl_AppendResult( interp, "unable to read from ", iodev->typeName, (char *) NULL ); result = TCL_ERROR; } // Signal bop if necessary if ( plr->at_bop && plPlotterPtr->bopCmd != NULL ) { plr->at_bop = 0; if ( Tcl_Eval( interp, plPlotterPtr->bopCmd ) != TCL_OK ) fprintf( stderr, "Command \"%s\" failed:\n\t %s\n", plPlotterPtr->bopCmd, Tcl_GetStringResult( interp ) ); } // Signal eop if necessary if ( plr->at_eop && plPlotterPtr->eopCmd != NULL ) { plr->at_eop = 0; if ( Tcl_Eval( interp, plPlotterPtr->eopCmd ) != TCL_OK ) fprintf( stderr, "Command \"%s\" failed:\n\t %s\n", plPlotterPtr->eopCmd, Tcl_GetStringResult( interp ) ); } return result; } void PlplotterAtEop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ) { if ( plPlotterPtr->eopCmd != NULL ) { if ( Tcl_Eval( interp, plPlotterPtr->eopCmd ) != TCL_OK ) fprintf( stderr, "Command \"%s\" failed:\n\t %s\n", plPlotterPtr->eopCmd, Tcl_GetStringResult( interp ) ); } } void PlplotterAtBop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ) { if ( plPlotterPtr->bopCmd != NULL ) { if ( Tcl_Eval( interp, plPlotterPtr->bopCmd ) != TCL_OK ) fprintf( stderr, "Command \"%s\" failed:\n\t %s\n", plPlotterPtr->bopCmd, Tcl_GetStringResult( interp ) ); } } //-------------------------------------------------------------------------- // ReadData // // Reads & processes data. // Intended to be installed as a filehandler command. //-------------------------------------------------------------------------- static int ReadData( ClientData clientData, int mask ) { register PlPlotter *plPlotterPtr = (PlPlotter *) clientData; register Tcl_Interp *interp = plPlotterPtr->interp; register PLRDev *plr = plPlotterPtr->plr; register PLiodev *iodev = plr->iodev; register PDFstrm *pdfs = plr->pdfs; int result = TCL_OK; if ( mask & TK_READABLE ) { // Read from FIFO or socket plsstrm( plPlotterPtr->ipls ); #ifndef MAC_TCL if ( pl_PacketReceive( interp, iodev, pdfs ) ) { #else if ( 1 ) { #endif Tcl_AppendResult( interp, "Packet receive failed:\n\t %s\n", Tcl_GetStringResult( interp ), (char *) NULL ); return TCL_ERROR; } // If the packet isn't complete it will be put back and we just return. // Otherwise, the buffer pointer is saved and then cleared so that reads // from the buffer start at the beginning. // if ( pdfs->bp == 0 ) return TCL_OK; plr->nbytes = (int) pdfs->bp; pdfs->bp = 0; result = process_data( interp, plPlotterPtr ); } return result; } //-------------------------------------------------------------------------- // Orient // // Processes "orient" widget command. // Handles orientation of plot. //-------------------------------------------------------------------------- static int Orient( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { int result = TCL_OK; // orient -- return orientation of current plot window plsstrm( plPlotterPtr->ipls ); if ( argc == 0 ) { PLFLT rot; char result_str[128]; plgdiori( &rot ); sprintf( result_str, "%f", rot ); Tcl_SetResult( interp, result_str, TCL_VOLATILE ); } // orient -- Set orientation to else { plsdiori( (PLFLT) atof( argv[0] ) ); result = Redraw( interp, plPlotterPtr, argc - 1, argv + 1 ); } return result; } //-------------------------------------------------------------------------- // Print // // Processes "print" widget command. // Handles printing of plot, duh. // // Creates a temporary file, dumps the current plot to it in metafile // form, and then execs the "plpr" script to actually print it. Since we // output it in metafile form here, plpr must invoke plrender to drive the // output to the appropriate file type. The script is responsible for the // deletion of the plot metafile. //-------------------------------------------------------------------------- static int Print( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int PL_UNUSED( argc ), CONST char ** PL_UNUSED( argv ) ) { PLINT ipls; int result = TCL_OK; char *sfnam; FILE *sfile; #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) pid_t pid; #endif // Make sure widget has been initialized before going any further if ( !plPlotterPtr->tkwin_initted ) { Tcl_AppendResult( interp, "Error -- widget not plotted to yet", (char *) NULL ); return TCL_ERROR; } // Create stream for save plmkstrm( &ipls ); if ( ipls < 0 ) { Tcl_AppendResult( interp, "Error -- cannot create stream", (char *) NULL ); return TCL_ERROR; } // Open file for writes // Create and open temporary file // NB use fdopen to get a file stream from the existing file handle if ( ( sfile = pl_create_tempfile( &sfnam ) ) == NULL ) { Tcl_AppendResult( interp, "Error -- cannot open plot file for writing", (char *) NULL ); plend1(); if ( sfnam != NULL ) free( sfnam ); return TCL_ERROR; } // Initialize stream plsdev( "plmeta" ); plsfile( sfile ); plcpstrm( plPlotterPtr->ipls, 0 ); pladv( 0 ); // Remake current plot, close file, and switch back to original stream plreplot(); plend1(); plsstrm( plPlotterPtr->ipls ); // So far so good. Time to exec the print script. if ( plPlotterPtr->plpr_cmd == NULL ) plPlotterPtr->plpr_cmd = plFindCommand( "plpr" ); #if !defined ( MAC_TCL ) && !defined ( _WIN32 ) if ( ( plPlotterPtr->plpr_cmd == NULL ) || ( pid = fork() ) < 0 ) { Tcl_AppendResult( interp, "Error -- cannot fork print process", (char *) NULL ); result = TCL_ERROR; } else if ( pid == 0 ) { if ( execl( plPlotterPtr->plpr_cmd, plPlotterPtr->plpr_cmd, sfnam, (char *) 0 ) ) { fprintf( stderr, "Unable to exec print command.\n" ); free( sfnam ); _exit( 1 ); } } #endif free( sfnam ); return result; } //-------------------------------------------------------------------------- // NextPage // // Tells the tkwin driver to move along to the next page. //-------------------------------------------------------------------------- static int NextPage( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char ** PL_UNUSED( argv ) ) { TkwDev *dev = (TkwDev *) plPlotterPtr->pls->dev; if ( argc == 0 ) { dev->flags |= 2; } else { Tcl_AppendResult( interp, "wrong # args: should be \"", " nextpage\"", (char *) NULL ); return TCL_ERROR; } return TCL_OK; } //-------------------------------------------------------------------------- // Page // // Processes "page" widget command. // Handles parameters such as margin, aspect ratio, and justification // of final plot. //-------------------------------------------------------------------------- static int Page( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { // page -- return current device window parameters plsstrm( plPlotterPtr->ipls ); if ( argc == 0 ) { PLFLT mar, aspect, jx, jy; char result_str[128]; plgdidev( &mar, &aspect, &jx, &jy ); sprintf( result_str, "%g %g %g %g", mar, aspect, jx, jy ); Tcl_SetResult( interp, result_str, TCL_VOLATILE ); return TCL_OK; } // page -- set up page if ( argc < 4 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", " page mar aspect jx jy\"", (char *) NULL ); return TCL_ERROR; } plsdidev( (PLFLT) atof( argv[0] ), (PLFLT) atof( argv[1] ), (PLFLT) atof( argv[2] ), (PLFLT) atof( argv[3] ) ); return ( Redraw( interp, plPlotterPtr, argc - 1, argv + 1 ) ); } //-------------------------------------------------------------------------- // Redraw // // Processes "redraw" widget command. // Turns loose a DoWhenIdle command to redraw plot by replaying contents // of plot buffer. //-------------------------------------------------------------------------- static int Redraw( Tcl_Interp * PL_UNUSED( interp ), register PlPlotter *plPlotterPtr, int PL_UNUSED( argc ), CONST char ** PL_UNUSED( argv ) ) { dbug_enter( "Redraw" ); plPlotterPtr->flags |= REDRAW_PENDING; if ( ( plPlotterPtr->tkwin != NULL ) && !( plPlotterPtr->flags & REFRESH_PENDING ) ) { Tcl_DoWhenIdle( DisplayPlPlotter, (ClientData) plPlotterPtr ); plPlotterPtr->flags |= REFRESH_PENDING; } return TCL_OK; } //-------------------------------------------------------------------------- // Save // // Processes "save" widget command. // Saves plot to a file. //-------------------------------------------------------------------------- static int Save( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { int length; char c; FILE *sfile; // Make sure widget has been initialized before going any further if ( !plPlotterPtr->tkwin_initted ) { Tcl_AppendResult( interp, "Error -- widget not plotted to yet", (char *) NULL ); return TCL_ERROR; } // save -- save to already open file if ( argc == 0 ) { if ( !plPlotterPtr->ipls_save ) { Tcl_AppendResult( interp, "Error -- no current save file", (char *) NULL ); return TCL_ERROR; } plsstrm( plPlotterPtr->ipls_save ); plcpstrm( plPlotterPtr->ipls, 0 ); pladv( 0 ); plreplot(); plflush(); plsstrm( plPlotterPtr->ipls ); return TCL_OK; } c = argv[0][0]; length = (int) strlen( argv[0] ); // save to specified device & file if ( ( c == 'a' ) && ( strncmp( argv[0], "as", (size_t) length ) == 0 ) ) { if ( argc < 3 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", " save as device file\"", (char *) NULL ); return TCL_ERROR; } // If save previously in effect, delete old stream if ( plPlotterPtr->ipls_save ) { plsstrm( plPlotterPtr->ipls_save ); plend1(); } // Create stream for saves to selected device & file plmkstrm( &plPlotterPtr->ipls_save ); if ( plPlotterPtr->ipls_save < 0 ) { Tcl_AppendResult( interp, "Error -- cannot create stream", (char *) NULL ); plPlotterPtr->ipls_save = 0; return TCL_ERROR; } // Open file for writes if ( ( sfile = fopen( argv[2], "wb+" ) ) == NULL ) { Tcl_AppendResult( interp, "Error -- cannot open file ", argv[2], " for writing", (char *) NULL ); plPlotterPtr->ipls_save = 0; plend1(); return TCL_ERROR; } // Initialize stream plsdev( argv[1] ); plsfile( sfile ); plcpstrm( plPlotterPtr->ipls, 0 ); pladv( 0 ); // Remake current plot and then switch back to original stream plreplot(); plflush(); plsstrm( plPlotterPtr->ipls ); } // close save file else if ( ( c == 'c' ) && ( strncmp( argv[0], "close", (size_t) length ) == 0 ) ) { if ( !plPlotterPtr->ipls_save ) { Tcl_AppendResult( interp, "Error -- no current save file", (char *) NULL ); return TCL_ERROR; } else { plsstrm( plPlotterPtr->ipls_save ); plend1(); plPlotterPtr->ipls_save = 0; } } // unrecognized else { Tcl_AppendResult( interp, "bad option to \"save\": must be ", "as or close", (char *) NULL ); return TCL_ERROR; } return TCL_OK; } //-------------------------------------------------------------------------- // View // // Processes "view" widget command. // Handles translation & scaling of view into plot. //-------------------------------------------------------------------------- static int View( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { int length; int dontRedraw = 0; char c; PLFLT xl, xr, yl, yr; // view -- return current relative plot window coordinates plsstrm( plPlotterPtr->ipls ); if ( argc == 0 ) { char result_str[128]; plgdiplt( &xl, &yl, &xr, &yr ); sprintf( result_str, "%g %g %g %g", xl, yl, xr, yr ); Tcl_SetResult( interp, result_str, TCL_VOLATILE ); return TCL_OK; } c = argv[0][0]; length = (int) strlen( argv[0] ); // view bounds -- return relative device coordinates of bounds on current // plot window if ( ( c == 'b' ) && ( strncmp( argv[0], "bounds", (size_t) length ) == 0 ) ) { char result_str[128]; xl = 0.; yl = 0.; xr = 1.; yr = 1.; pldip2dc( &xl, &yl, &xr, &yr ); sprintf( result_str, "%g %g %g %g", xl, yl, xr, yr ); Tcl_SetResult( interp, result_str, TCL_VOLATILE ); return TCL_OK; } // view reset -- Resets plot if ( ( c == 'r' ) && ( strncmp( argv[0], "reset", (size_t) length ) == 0 ) ) { xl = 0.; yl = 0.; xr = 1.; yr = 1.; plsdiplt( xl, yl, xr, yr ); if ( argc > 1 && ( strcmp( argv[1], "wait" ) == 0 ) ) { // We're going to update in a while, so don't do it now dontRedraw = 1; } } // view select -- set window into plot space // Specifies in terms of plot window coordinates, not device coordinates else if ( ( c == 's' ) && ( strncmp( argv[0], "select", (size_t) length ) == 0 ) ) { if ( argc < 5 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", " view select xmin ymin xmax ymax\"", (char *) NULL ); return TCL_ERROR; } else { gbox( &xl, &yl, &xr, &yr, argv + 1 ); plsdiplt( xl, yl, xr, yr ); } } // view zoom -- set window into plot space incrementally (zoom) // Here we need to take the page (device) offsets into account else if ( ( c == 'z' ) && ( strncmp( argv[0], "zoom", (size_t) length ) == 0 ) ) { if ( argc < 5 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", " view zoom xmin ymin xmax ymax\"", (char *) NULL ); return TCL_ERROR; } else { gbox( &xl, &yl, &xr, &yr, argv + 1 ); pldid2pc( &xl, &yl, &xr, &yr ); plsdiplz( xl, yl, xr, yr ); if ( argc > 5 && ( strcmp( argv[5], "wait" ) == 0 ) ) { // We're going to update in a while, so don't do it now dontRedraw = 1; } } } // unrecognized else { Tcl_AppendResult( interp, "bad option \"", argv[1], "\": options to \"view\" are: bounds, reset, select, or zoom", (char *) NULL ); return TCL_ERROR; } // Update plot window bounds and arrange for plot to be updated plgdiplt( &xl, &yl, &xr, &yr ); plPlotterPtr->xl = xl; plPlotterPtr->yl = yl; plPlotterPtr->xr = xr; plPlotterPtr->yr = yr; plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR; if ( dontRedraw ) { return TCL_OK; } else { return Redraw( interp, plPlotterPtr, argc, argv ); } } //-------------------------------------------------------------------------- // Scroll // // Processes "xview or yview" widget command. // Handles horizontal/vert scroll-bar invoked translation of view into plot. //-------------------------------------------------------------------------- static int Scroll( Tcl_Interp *interp, register PlPlotter *plPlotterPtr ) { plsstrm( plPlotterPtr->ipls ); plsdiplt( plPlotterPtr->xl, plPlotterPtr->yl, plPlotterPtr->xr, plPlotterPtr->yr ); plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR; return ( Redraw( interp, plPlotterPtr, 0, NULL ) ); } //-------------------------------------------------------------------------- // report // // 4/17/95 GMF // Processes "report" widget command. //-------------------------------------------------------------------------- static int report( Tcl_Interp *interp, register PlPlotter *plPlotterPtr, int argc, CONST char **argv ) { PLFLT x, y; char res[40]; // fprintf( stdout, "Made it into report, argc=%d\n", argc ); if ( argc == 0 ) { Tcl_SetResult( interp, "report what?", TCL_STATIC ); return TCL_ERROR; } if ( !strcmp( argv[0], "wc" ) ) { TkwDev *dev = (TkwDev *) plPlotterPtr->pls->dev; PLGraphicsIn *gin = &( dev->gin ); if ( argc != 3 ) { Tcl_SetResult( interp, "Wrong # of args: report wc x y", TCL_STATIC ); return TCL_ERROR; } x = (PLFLT) atof( argv[1] ); y = (PLFLT) atof( argv[2] ); gin->dX = (PLFLT) x / ( dev->width - 1 ); gin->dY = (PLFLT) 1.0 - (PLFLT) y / ( dev->height - 1 ); // Try to locate cursor if ( plTranslateCursor( gin ) ) { snprintf( res, 40, "%f %f", gin->wX, gin->wY ); Tcl_SetResult( interp, res, TCL_VOLATILE ); return TCL_OK; } Tcl_SetResult( interp, "Cannot locate", TCL_STATIC ); return TCL_OK; } Tcl_SetResult( interp, "nonsensical request.", TCL_STATIC ); return TCL_ERROR; } //-------------------------------------------------------------------------- // Utility routines //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // UpdateVScrollbar // // Updates vertical scrollbar if needed. //-------------------------------------------------------------------------- static void UpdateVScrollbar( register PlPlotter *plPlotterPtr ) { char string[60]; int result; if ( plPlotterPtr->yScrollCmd == NULL ) return; sprintf( string, " %f %f", 1. - plPlotterPtr->yr, 1. - plPlotterPtr->yl ); result = Tcl_VarEval( plPlotterPtr->interp, plPlotterPtr->yScrollCmd, string, (char *) NULL ); if ( result != TCL_OK ) { Tcl_BackgroundError( plPlotterPtr->interp ); } } //-------------------------------------------------------------------------- // UpdateHScrollbar // // Updates horizontal scrollbar if needed. //-------------------------------------------------------------------------- static void UpdateHScrollbar( register PlPlotter *plPlotterPtr ) { char string[60]; int result; if ( plPlotterPtr->xScrollCmd == NULL ) return; sprintf( string, " %f %f", plPlotterPtr->xl, plPlotterPtr->xr ); result = Tcl_VarEval( plPlotterPtr->interp, plPlotterPtr->xScrollCmd, string, (char *) NULL ); if ( result != TCL_OK ) { Tcl_BackgroundError( plPlotterPtr->interp ); } } //-------------------------------------------------------------------------- // gbox // // Returns selection box coordinates. It's best if the TCL script does // bounds checking on the input but I do it here as well just to be safe. //-------------------------------------------------------------------------- static void gbox( PLFLT *xl, PLFLT *yl, PLFLT *xr, PLFLT *yr, CONST char **argv ) { PLFLT x0, y0, x1, y1; x0 = (PLFLT) atof( argv[0] ); y0 = (PLFLT) atof( argv[1] ); x1 = (PLFLT) atof( argv[2] ); y1 = (PLFLT) atof( argv[3] ); x0 = MAX( (PLFLT) 0., MIN( (PLFLT) 1., x0 ) ); y0 = MAX( (PLFLT) 0., MIN( (PLFLT) 1., y0 ) ); x1 = MAX( (PLFLT) 0., MIN( (PLFLT) 1., x1 ) ); y1 = MAX( (PLFLT) 0., MIN( (PLFLT) 1., y1 ) ); // Only need two vertices, pick the lower left and upper right *xl = MIN( x0, x1 ); *yl = MIN( y0, y1 ); *xr = MAX( x0, x1 ); *yr = MAX( y0, y1 ); }