// Copyright 1993, 1994, 1995 // Maurice LeBrun mjl@dino.ph.utexas.edu // Institute for Fusion Studies University of Texas at Austin // // Copyright (C) 2004 Joao Cardoso // Copyright (C) 2004 Andrew Ross // // 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 // // // 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 DEBUGx #define NEED_PLDEBUG #include "plserver.h" #include "plxwd.h" #include "tcpip.h" #ifdef PL_HAVE_UNISTD_H #include #endif #include #undef HAVE_ITCL #define NDEV 100 // Max number of output device types in menu // If set, BUFFER_FIFO causes FIFO i/o to be buffered #define BUFFER_FIFO 1 // If set, causes a file handler to be used with FIFO #define FH_FIFO 0 // A handy command wrapper #define plframe_cmd( code ) \ if ( ( code ) == TCL_ERROR ) return ( TCL_ERROR ); // Backward compatibility junk #if TCL_MAJOR_VERSION <= 7 && TCL_MINOR_VERSION <= 4 #define Tk_Cursor Cursor #endif // // A data structure of the following type is kept for each // plframe that currently exists for this process: // typedef struct { // 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. #ifdef HAVE_ITCL Tcl_Command widgetCmd; // Token for frame's widget command. #endif 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 XColor *bgColor; // Background color char *plpr_cmd; // Holds print command name. Malloc'ed // 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 } PlFrame; // // 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 "0" #define DEF_PLFRAME_RELIEF "flat" #define DEF_PLFRAME_WIDTH "0" // Configuration info static Tk_ConfigSpec configSpecs[] = { { TK_CONFIG_BORDER, "-background", "background", "Background", DEF_PLFRAME_BG_COLOR, Tk_Offset( PlFrame, border ), TK_CONFIG_COLOR_ONLY, NULL }, // // {TK_CONFIG_COLOR, (char *) NULL, (char *) NULL, (char *) NULL, // (char *) NULL, Tk_Offset(PlFrame, bgColor), // TK_CONFIG_COLOR_ONLY}, // #ifndef MAC_TCL { TK_CONFIG_COLOR, "-plbg", "plbackground", "Plbackground", DEF_PLFRAME_BG_COLOR, Tk_Offset( PlFrame, bgColor ), TK_CONFIG_COLOR_ONLY, NULL }, #endif { TK_CONFIG_BORDER, "-background", "background", "Background", DEF_PLFRAME_BG_MONO, Tk_Offset( PlFrame, border ), TK_CONFIG_MONO_ONLY, NULL }, // // {TK_CONFIG_COLOR, (char *) NULL, (char *) NULL, (char *) NULL, // (char *) NULL, Tk_Offset(PlFrame, bgColor), // TK_CONFIG_MONO_ONLY}, // #ifndef MAC_TCL { TK_CONFIG_COLOR, "-plbg", (char *) NULL, (char *) NULL, DEF_PLFRAME_BG_MONO, Tk_Offset( PlFrame, bgColor ), TK_CONFIG_MONO_ONLY, NULL }, #endif { TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, (char *) NULL, 0, 0, NULL }, { TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, (char *) NULL, 0, 0, NULL }, { TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", DEF_PLFRAME_BORDER_WIDTH, Tk_Offset( PlFrame, borderWidth ), 0, NULL }, { TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", DEF_PLFRAME_CURSOR, Tk_Offset( PlFrame, cursor ), TK_CONFIG_NULL_OK, NULL }, { TK_CONFIG_STRING, "-bopcmd", "bopcmd", "PgCommand", (char *) NULL, Tk_Offset( PlFrame, bopCmd ), TK_CONFIG_NULL_OK, NULL }, { TK_CONFIG_STRING, "-eopcmd", "eopcmd", "PgCommand", (char *) NULL, Tk_Offset( PlFrame, eopCmd ), TK_CONFIG_NULL_OK, NULL }, { TK_CONFIG_PIXELS, "-height", "height", "Height", DEF_PLFRAME_HEIGHT, Tk_Offset( PlFrame, height ), 0, NULL }, { TK_CONFIG_RELIEF, "-relief", "relief", "Relief", DEF_PLFRAME_RELIEF, Tk_Offset( PlFrame, relief ), 0, NULL }, { TK_CONFIG_PIXELS, "-width", "width", "Width", DEF_PLFRAME_WIDTH, Tk_Offset( PlFrame, width ), 0, NULL }, { TK_CONFIG_BOOLEAN, "-xhairs", (char *) NULL, (char *) NULL, "0", Tk_Offset( PlFrame, xhairs ), TK_CONFIG_DONT_SET_DEFAULT, NULL }, { TK_CONFIG_BOOLEAN, "-rubberband", (char *) NULL, (char *) NULL, "0", Tk_Offset( PlFrame, rband ), TK_CONFIG_DONT_SET_DEFAULT, NULL }, { TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", (char *) NULL, Tk_Offset( PlFrame, xScrollCmd ), TK_CONFIG_NULL_OK, NULL }, { TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand", (char *) NULL, Tk_Offset( PlFrame, yScrollCmd ), TK_CONFIG_NULL_OK, NULL }, { TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, NULL } }; // Forward declarations for procedures defined later in this file: // Externals int plFrameCmd( ClientData, Tcl_Interp *, int, const char ** ); // These are invoked by the TK dispatcher #if TK_MAJOR_VERSION < 4 || ( TK_MAJOR_VERSION == 4 && TK_MINOR_VERSION == 0 ) #define FreeProcArg ClientData #else #define FreeProcArg char * #endif static void DestroyPlFrame( FreeProcArg ); static void DisplayPlFrame( ClientData ); static void PlFrameInit( ClientData ); static void PlFrameConfigureEH( ClientData, XEvent * ); static void PlFrameExposeEH( ClientData, XEvent * ); static void PlFrameMotionEH( ClientData, register XEvent * ); static void PlFrameEnterEH( ClientData, register XEvent * ); static void PlFrameLeaveEH( ClientData, register XEvent * ); static void PlFrameKeyEH( ClientData, register XEvent * ); static int PlFrameWidgetCmd( ClientData, Tcl_Interp *, int, const char ** ); static int ReadData( ClientData, int ); static void Install_cmap( PlFrame *plFramePtr ); // These are invoked by PlFrameWidgetCmd to process widget commands static int Closelink( Tcl_Interp *, PlFrame *, int, const char ** ); static int Cmd( Tcl_Interp *, PlFrame *, int, const char ** ); static int ColorManip( Tcl_Interp *, PlFrame *, int, const char ** ); static int ConfigurePlFrame( Tcl_Interp *, PlFrame *, int, const char **, int ); static int Draw( Tcl_Interp *, PlFrame *, int, const char ** ); static int Info( Tcl_Interp *, PlFrame *, int, const char ** ); static int Openlink( Tcl_Interp *, PlFrame *, int, const char ** ); static int Orient( Tcl_Interp *, PlFrame *, int, const char ** ); static int Page( Tcl_Interp *, PlFrame *, int, const char ** ); static int Print( Tcl_Interp *, PlFrame *, int, const char ** ); static int Redraw( Tcl_Interp *, PlFrame *, int, const char ** ); static int Save( Tcl_Interp *, PlFrame *, int, const char ** ); static int View( Tcl_Interp *, PlFrame *, int, const char ** ); static int xScroll( Tcl_Interp *, PlFrame *, int, const char ** ); static int yScroll( Tcl_Interp *, PlFrame *, int, const char ** ); static int report( Tcl_Interp *, PlFrame *, int, const char ** ); // Routines for manipulating graphic crosshairs static void CreateXhairs( PlFrame * ); static void DestroyXhairs( PlFrame * ); static void DrawXhairs( PlFrame *, int, int ); static void UpdateXhairs( PlFrame * ); // Routines for manipulating the rubberband line static void CreateRband( PlFrame * ); static void DestroyRband( PlFrame * ); static void DrawRband( PlFrame *, int, int ); static void UpdateRband( PlFrame * ); // Callbacks from plplot library static void process_bop( void *, int * ); static void process_eop( void *, int * ); // Utility routines static void gbox( PLFLT *, PLFLT *, PLFLT *, PLFLT *, const char ** ); static void UpdateVScrollbar( register PlFrame * ); static void UpdateHScrollbar( register PlFrame * ); // //-------------------------------------------------------------------------- // // plFrameCmd -- // // 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 plFrameCmd( ClientData PL_UNUSED( clientData ), Tcl_Interp *interp, int argc, const char **argv ) { Tk_Window new; register PlFrame *plFramePtr; register PLRDev *plr; int i, ndev; dbug_enter( "plFrameCmd" ); if ( argc < 2 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " pathName ?options?\"", (char *) NULL ); return TCL_ERROR; } // Create the window. new = Tk_CreateWindowFromPath( interp, Tk_MainWindow( interp ), argv[1], (char *) NULL ); if ( new == NULL ) { return TCL_ERROR; } plFramePtr = (PlFrame *) ckalloc( sizeof ( PlFrame ) ); // Initialize in the same order as the members of the struct just // to keep track of what is initialized and what not. plFramePtr->tkwin = new; plFramePtr->display = Tk_Display( new ); plFramePtr->interp = interp; //plFramePtr->widgetCMD = plFramePtr->border = NULL; //plFramePtr->borderWidth = //plFramePtr->relief = plFramePtr->width = Tk_Width( plFramePtr->tkwin ); plFramePtr->height = Tk_Height( plFramePtr->tkwin ); plFramePtr->cursor = None; plFramePtr->flags = 0; plFramePtr->tkwin_initted = 0; // Associate new PLplot stream with this widget plmkstrm( &plFramePtr->ipls ); plgpls( &plFramePtr->pls ); plFramePtr->ipls_save = 0; plFramePtr->plr = (PLRDev *) ckalloc( sizeof ( PLRDev ) ); plFramePtr->bgColor = NULL; plFramePtr->plpr_cmd = NULL; plFramePtr->pldis.x = 0; plFramePtr->pldis.y = 0; plFramePtr->pldis.width = 0; plFramePtr->pldis.height = 0; plFramePtr->prevWidth = 0; plFramePtr->prevHeight = 0; plFramePtr->SaveFnam = NULL; // plFramePtr->devDesc = ; // plFramePtr->devName = ; plFramePtr->xorGC = NULL; // plFram Ptr->pts = ; plFramePtr->continue_draw = 0; plFramePtr->xhair_cursor = None; plFramePtr->xl = 0.; plFramePtr->yl = 0.; plFramePtr->xr = 1.; plFramePtr->yr = 1.; plFramePtr->xScrollCmd = NULL; plFramePtr->yScrollCmd = NULL; plFramePtr->bopCmd = NULL; plFramePtr->eopCmd = NULL; plFramePtr->xhairs = 0; plFramePtr->drawing_xhairs = 0; // plFram Ptr->xhair_x = ; // plFram Ptr->xhair_y = ; plFramePtr->rband = 0; plFramePtr->drawing_rband = 0; // plFram Ptr->rband_pt = ; plr = plFramePtr->plr; plr->pdfs = NULL; plr->at_bop = 0; plr->at_eop = 0; plr->iodev = (PLiodev *) ckalloc( sizeof ( PLiodev ) ); plr_start( plr ); // Set up stuff for rubber-band drawing plFramePtr->xhair_cursor = Tk_GetCursor( plFramePtr->interp, plFramePtr->tkwin, "crosshair" ); // Partially initialize X driver. pllib_init(); plsdev( "xwin" ); pllib_devinit(); plP_esc( PLESC_DEVINIT, NULL ); // Create list of valid device names and keywords for page dumps plFramePtr->devDesc = (const char **) ckalloc( NDEV * sizeof ( char ** ) ); plFramePtr->devName = (const char **) ckalloc( NDEV * sizeof ( char ** ) ); for ( i = 0; i < NDEV; i++ ) { plFramePtr->devDesc[i] = NULL; plFramePtr->devName[i] = NULL; } ndev = NDEV; plgFileDevs( &plFramePtr->devDesc, &plFramePtr->devName, &ndev ); // Start up event handlers and other good stuff Tk_SetClass( plFramePtr->tkwin, "Plframe" ); Tk_CreateEventHandler( plFramePtr->tkwin, StructureNotifyMask, PlFrameConfigureEH, (ClientData) plFramePtr ); Tk_CreateEventHandler( plFramePtr->tkwin, ExposureMask, PlFrameExposeEH, (ClientData) plFramePtr ); #ifdef HAVE_ITCL plFramePtr->widgetCmd = #endif Tcl_CreateCommand( interp, Tk_PathName( plFramePtr->tkwin ), (Tcl_CmdProc *) PlFrameWidgetCmd, (ClientData) plFramePtr, (Tcl_CmdDeleteProc *) NULL ); #ifdef HAVE_ITCL Itk_SetWidgetCommand( plFramePtr->tkwin, plFramePtr->widgetCmd ); #endif if ( ConfigurePlFrame( interp, plFramePtr, argc - 2, argv + 2, 0 ) != TCL_OK ) { #ifdef HAVE_ITCL Itk_SetWidgetCommand( plFramePtr->tkwin, (Tcl_Command) NULL ); #endif Tk_DestroyWindow( plFramePtr->tkwin ); return TCL_ERROR; } Tcl_SetResult( interp, Tk_PathName( plFramePtr->tkwin ), TCL_VOLATILE ); return TCL_OK; } // //-------------------------------------------------------------------------- // // PlFrameWidgetCmd -- // // 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 PlFrameWidgetCmd( ClientData clientData, Tcl_Interp *interp, int argc, const char **argv ) { register PlFrame *plFramePtr = (PlFrame *) clientData; int result = TCL_OK; int length; char c; char res[20]; dbug_enter( "PlFrameWidgetCmd" ); #ifdef DEBUG { int i; PLStream *pls; plgpls( pls ); printf( "Current stream %d, frame stream %d\n", pls->ipls, plFramePtr->ipls ); printf( "PlFrameWidgetCmd: " ); for ( i = 0; i < argc; i++ ) printf( " %s", argv[i] ); printf( "\n" ); } #endif if ( argc < 2 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " option ?arg arg ...?\"", (char *) NULL ); return TCL_ERROR; } Tk_Preserve( (ClientData) plFramePtr ); c = argv[1][0]; length = (int) strlen( argv[1] ); // First, before anything else, we have to set the stream to be the one that // corresponds to this widget. plsstrm( plFramePtr->ipls ); // cmd -- issue a command to the PLplot library if ( ( c == 'c' ) && ( strncmp( argv[1], "cmd", (size_t) length ) == 0 ) ) { result = Cmd( interp, plFramePtr, argc - 2, argv + 2 ); } // cget else if ( ( c == 'c' ) && ( strncmp( argv[1], "cget", (size_t) length ) == 0 ) ) { if ( argc > 2 ) { Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], " cget