PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
plplotter.c
Go to the documentation of this file.
1 // Copyright (C) 2004 Joao Cardoso
2 //
3 // This file is part of PLplot.
4 //
5 // PLplot is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Library General Public License as published
7 // by the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // PLplot is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Library General Public License for more details.
14 //
15 // You should have received a copy of the GNU Library General Public License
16 // along with PLplot; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 //
19 //
20 
21 //
22 // plplotter.c
23 //
24 // Copyright 1993, 1994, 1995
25 // Maurice LeBrun mjl@dino.ph.utexas.edu
26 // Institute for Fusion Studies University of Texas at Austin
27 //
28 // This library is free software; you can redistribute it and/or
29 // modify it under the terms of the GNU Library General Public
30 // License as published by the Free Software Foundation; either
31 // version 2 of the License, or (at your option) any later version.
32 //
33 // This library is distributed in the hope that it will be useful,
34 // but WITHOUT ANY WARRANTY; without even the implied warranty of
35 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 // Library General Public License for more details.
37 //
38 // You should have received a copy of the GNU Library General Public
39 // License along with this library; if not, write to the Free Software
40 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
41 //
42 // Based upon tkFrame.c from the TK 3.2 distribution:
43 //
44 // Copyright 1990 Regents of the University of California.
45 // Permission to use, copy, modify, and distribute this
46 // software and its documentation for any purpose and without
47 // fee is hereby granted, provided that the above copyright
48 // notice appear in all copies. The University of California
49 // makes no representations about the suitability of this
50 // software for any purpose. It is provided "as is" without
51 // express or implied warranty.
52 //
53 //--------------------------------------------------------------------------
54 //
55 // This module implements "plframe" widgets for the Tk toolkit. These are
56 // frames that have extra logic to allow them to be interfaced with the
57 // PLplot X driver. These are then drawn into and respond to keyboard and
58 // mouse events.
59 //
60 //
61 // #define DEBUG_ENTER
62 // #define DEBUG
63 //
64 
65 #define NEED_PLDEBUG
66 #include "plserver.h"
67 #include "pltkwd.h"
68 #include "tcpip.h"
69 
70 #ifdef __WIN32__
71 #define XSynchronize( display, bool ) { display->request++; }
72 #define XSync( display, bool ) { display->request++; }
73 #define XFlush( display )
74 #endif
75 
76 #ifdef DEBUG_ENTER
77 // This version of dbug_enter works cross-platform
78 #undef dbug_enter
79 #define dbug_enter( a ) if ( plsc->debug ) { \
80  Tcl_Write( Tcl_GetStdChannel( TCL_STDERR ), a, -1 ); }
81 #endif
82 
83 #ifndef __WIN32__
84 #ifdef PL_HAVE_UNISTD_H
85 #include <unistd.h>
86 #include <fcntl.h>
87 #endif
88 #endif
89 
90 // plplot_tkwin_ccmap is statically defined in pltkwd.h. Note that
91 // tkwin.c also includes that header and uses that variable.
92 
93 #define NDEV 100 // Max number of output device types
94 
95 //
96 // A data structure of the following type is kept for each
97 // plframe that currently exists for this process:
98 //
99 
100 typedef struct PlPlotter
101 {
102 // This is stuff taken from tkFrame.c
103 
104  Tk_Window tkwin; // Window that embodies the frame. NULL
105  // means that the window has been destroyed
106  // but the data structures haven't yet been
107  // cleaned up.
108  Display *display; // Display containing widget. Used, among
109  // other things, so that resources can be
110  // freed even after tkwin has gone away.
111  Tcl_Interp *interp; // Interpreter associated with
112  // widget. Used to delete widget
113  // command.
114  Tcl_Command widgetCmd; // Token for frame's widget command.
115  Tk_3DBorder border; // Structure used to draw 3-D border and
116  // background.
117  int borderWidth; // Width of 3-D border (if any).
118  int relief; // 3-d effect: TK_RELIEF_RAISED etc.
119  int width; // Width to request for window. <= 0 means
120  // don't request any size.
121  int height; // Height to request for window. <= 0 means
122  // don't request any size.
123  Tk_Cursor cursor; // Current cursor for window, or None.
124  int flags; // Various flags; see below for
125  // definitions.
126 
127 // These are new to plframe widgets
128 
129 // control stuff
130 
131  int tkwin_initted; // Set first time widget is mapped
132  PLStream *pls; // PLplot stream pointer
133  PLINT ipls; // PLplot stream number
134  PLINT ipls_save; // PLplot stream number, save files
135 
136  PLRDev *plr; // Renderer state information. Malloc'ed
137  char *plpr_cmd; // Holds print command name. Malloc'ed
138 
139 // Used to allow active stuff on the plot
140  int active_plot; // The plot responds to mouse movement etc.
141  int isActive; // Used to turn event handling on and off.
142 
143 // Used to handle resize and expose events
144 
145  PLDisplay pldis; // Info about the display window
146  int prevWidth; // Previous window width
147  int prevHeight; // Previous window height
148 
149 // Support for save operations
150 
151  char *SaveFnam; // File name we are currently saving to.
152  // Malloc'ed.
153  const char **devDesc; // Descriptive names for file-oriented
154  // devices. Malloc'ed.
155  const char **devName; // Keyword names of file-oriented devices.
156  // Malloc'ed.
157 
158 // Used in selecting & modifying plot or device area
159 
160  GC xorGC; // GC used for rubber-band drawing
161  XPoint pts[5]; // Points for rubber-band drawing
162  int continue_draw; // Set when doing rubber-band draws
163  Tk_Cursor xhair_cursor; // cursor used for drawing
164  PLFLT xl, xr, yl, yr; // Bounds on plot viewing area
165  char *xScrollCmd; // Command prefix for communicating with
166  // horizontal scrollbar. NULL means no
167  // command to issue. Malloc'ed.
168  char *yScrollCmd; // Command prefix for communicating with
169  // vertical scrollbar. NULL means no
170  // command to issue. Malloc'ed.
171 
172 // Used for flashing bop or eop condition
173 
174  char *bopCmd; // Proc to call at bop
175  char *eopCmd; // Proc to call at eop
176 
177 // Used for drawing graphic crosshairs
178 
179  int xhairs; // Configuration option to turn on xhairs
180  int drawing_xhairs; // Set if we are currently drawing xhairs
181  XPoint xhair_x[2]; // Points for horizontal xhair line
182  XPoint xhair_y[2]; // Points for vertical xhair line
183 
184 // Used for drawing a rubber band lilne segment
185 
186  int rband; // Configuration option to turn on rband
187  int drawing_rband; // See if we are currently drawing rband
188  XPoint rband_pt[2]; // Ends of rubber band line
189  int double_buffer; // Double buffering configuration option
190 } PlPlotter;
191 
192 //
193 // Flag bits for plframes:
194 //
195 // REFRESH_PENDING: Non-zero means a DoWhenIdle handler
196 // has already been queued to refresh
197 // this window.
198 // RESIZE_PENDING; Used to reschedule resize events
199 // REDRAW_PENDING; Used to redraw contents of plot buffer
200 // UPDATE_V_SCROLLBAR: Non-zero means vertical scrollbar needs
201 // to be updated.
202 // UPDATE_H_SCROLLBAR: Non-zero means horizontal scrollbar needs
203 // to be updated.
204 //
205 
206 #define REFRESH_PENDING 1
207 #define RESIZE_PENDING 2
208 #define REDRAW_PENDING 4
209 #define UPDATE_V_SCROLLBAR 8
210 #define UPDATE_H_SCROLLBAR 16
211 
212 // Defaults for plframes:
213 
214 #define DEF_PLFRAME_BG_COLOR "Black"
215 #define DEF_PLFRAME_BG_MONO "White"
216 #define DEF_PLFRAME_BORDER_WIDTH "0"
217 #define DEF_PLFRAME_CURSOR ( (char *) NULL )
218 #define DEF_PLFRAME_HEIGHT "250"
219 #define DEF_PLFRAME_RELIEF "flat"
220 #define DEF_PLFRAME_WIDTH "250"
221 
222 // Configuration info
223 
224 static Tk_ConfigSpec configSpecs[] = {
225  { TK_CONFIG_BOOLEAN, "-activeplot", (char *) NULL, (char *) NULL,
226  "1", Tk_Offset( PlPlotter, active_plot ), TK_CONFIG_DONT_SET_DEFAULT },
227  { TK_CONFIG_BORDER, "-background", "background", "Background",
228  DEF_PLFRAME_BG_COLOR, Tk_Offset( PlPlotter, border ),
229  TK_CONFIG_COLOR_ONLY },
230  { TK_CONFIG_BORDER, "-background", "background", "Background",
231  DEF_PLFRAME_BG_MONO, Tk_Offset( PlPlotter, border ),
232  TK_CONFIG_MONO_ONLY },
233  { TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
234  (char *) NULL, 0, 0 },
235  { TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
236  (char *) NULL, 0, 0 },
237  { TK_CONFIG_STRING, "-bopcmd", "bopcmd", "PgCommand",
238  (char *) NULL, Tk_Offset( PlPlotter, bopCmd ), TK_CONFIG_NULL_OK },
239  { TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
240  DEF_PLFRAME_BORDER_WIDTH, Tk_Offset( PlPlotter, borderWidth ), 0 },
241  { TK_CONFIG_BOOLEAN, "-doublebuffer", (char *) NULL, (char *) NULL,
242  "0", Tk_Offset( PlPlotter, double_buffer ), TK_CONFIG_DONT_SET_DEFAULT },
243  { TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
244  DEF_PLFRAME_CURSOR, Tk_Offset( PlPlotter, cursor ), TK_CONFIG_NULL_OK },
245  { TK_CONFIG_STRING, "-eopcmd", "eopcmd", "PgCommand",
246  (char *) NULL, Tk_Offset( PlPlotter, eopCmd ), TK_CONFIG_NULL_OK },
247  { TK_CONFIG_PIXELS, "-height", "height", "Height",
248  DEF_PLFRAME_HEIGHT, Tk_Offset( PlPlotter, height ), 0 },
249  { TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
250  DEF_PLFRAME_RELIEF, Tk_Offset( PlPlotter, relief ), 0 },
251  { TK_CONFIG_PIXELS, "-width", "width", "Width",
252  DEF_PLFRAME_WIDTH, Tk_Offset( PlPlotter, width ), 0 },
253  { TK_CONFIG_BOOLEAN, "-xhairs", (char *) NULL, (char *) NULL,
254  "0", Tk_Offset( PlPlotter, xhairs ), TK_CONFIG_DONT_SET_DEFAULT },
255  { TK_CONFIG_BOOLEAN, "-rubberband", (char *) NULL, (char *) NULL,
256  "0", Tk_Offset( PlPlotter, rband ), TK_CONFIG_DONT_SET_DEFAULT },
257  { TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
258  (char *) NULL, Tk_Offset( PlPlotter, xScrollCmd ), TK_CONFIG_NULL_OK },
259  { TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
260  (char *) NULL, Tk_Offset( PlPlotter, yScrollCmd ), TK_CONFIG_NULL_OK },
261  { TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
262  (char *) NULL, 0, 0 }
263 };
264 
265 // Forward declarations for procedures defined later in this file:
266 
267 // Externals
268 
269 int plPlotterCmd( ClientData, Tcl_Interp *, int, const char ** );
270 void PlplotterAtEop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr );
271 void PlplotterAtBop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr );
272 
273 // These are invoked by the TK dispatcher
274 
275 static void DestroyPlPlotter( ClientData );
276 static void DisplayPlPlotter( ClientData );
277 static void PlPlotterInit( ClientData );
278 static void PlPlotterFirstInit( ClientData clientData );
279 static void PlPlotterConfigureEH( ClientData, XEvent * );
280 static void PlPlotterExposeEH( ClientData, XEvent * );
281 static void PlPlotterMotionEH( ClientData, register XEvent * );
282 static void PlPlotterEnterEH( ClientData, register XEvent * );
283 static void PlPlotterLeaveEH( ClientData, register XEvent * );
284 static void PlPlotterButtonPressEH( ClientData clientData, register XEvent * );
285 static int PlPlotterWidgetCmd( ClientData, Tcl_Interp *, int, CONST char ** );
286 static int ReadData( ClientData, int );
287 static void Install_cmap( PlPlotter *plPlotterPtr );
288 
289 // These are invoked by PlPlotterWidgetCmd to process widget commands
290 
291 static int Closelink( Tcl_Interp *, PlPlotter *, int, CONST char ** );
292 static int Cmd( Tcl_Interp *, PlPlotter *, int, CONST char ** );
293 static int ConfigurePlPlotter( Tcl_Interp *, PlPlotter *, int, CONST char **, int );
294 static int Draw( Tcl_Interp *, PlPlotter *, int, CONST char ** );
295 static int Info( Tcl_Interp *, PlPlotter *, int, CONST char ** );
296 static int Openlink( Tcl_Interp *, PlPlotter *, int, CONST char ** );
297 static int Orient( Tcl_Interp *, PlPlotter *, int, CONST char ** );
298 static int Page( Tcl_Interp *, PlPlotter *, int, CONST char ** );
299 static int NextPage( Tcl_Interp *, PlPlotter *, int, CONST char ** );
300 static int Print( Tcl_Interp *, PlPlotter *, int, CONST char ** );
301 static int Redraw( Tcl_Interp *, PlPlotter *, int, CONST char ** );
302 static int Save( Tcl_Interp *, PlPlotter *, int, CONST char ** );
303 static int View( Tcl_Interp *, PlPlotter *, int, CONST char ** );
304 static int Scroll( Tcl_Interp *, PlPlotter * );
305 static int report( Tcl_Interp *, PlPlotter *, int, CONST char ** );
306 
307 // Routines for manipulating graphic crosshairs
308 
309 static void ActiveState( register PlPlotter *plPlotterPtr, int on );
310 static void CreateXhairs( PlPlotter * );
311 static void DestroyXhairs( PlPlotter * );
312 static void DrawXhairs( PlPlotter *, int, int );
313 static void UpdateXhairs( PlPlotter * );
314 
315 // Routines for manipulating the rubberband line
316 
317 static void CreateRband( PlPlotter * );
318 static void DestroyRband( PlPlotter * );
319 static void DrawRband( PlPlotter *, int, int );
320 static void UpdateRband( PlPlotter * );
321 
322 // Utility routines
323 
324 static void gbox( PLFLT *, PLFLT *, PLFLT *, PLFLT *, CONST char ** );
325 static void UpdateVScrollbar( register PlPlotter * );
326 static void UpdateHScrollbar( register PlPlotter * );
327 
328 //
329 //--------------------------------------------------------------------------
330 //
331 // plPlotterCmd --
332 //
333 // This procedure is invoked to process the "plframe" Tcl
334 // command. See the user documentation for details on what it
335 // does.
336 //
337 // Results:
338 // A standard Tcl result.
339 //
340 // Side effects:
341 // See the user documentation.
342 //
343 //--------------------------------------------------------------------------
344 //
345 
346 int
347 plPlotterCmd( ClientData PL_UNUSED( clientData ), Tcl_Interp *interp,
348  int argc, const char **argv )
349 {
350  Tk_Window tkwin;
351  register PlPlotter *plPlotterPtr;
352  register PLRDev *plr;
353  int i, ndev;
354 
355  dbug_enter( "plPlotterCmd" );
356 
357  if ( argc < 2 )
358  {
359  Tcl_AppendResult( interp, "wrong # args: should be \"",
360  argv[0], " pathName ?options?\"", (char *) NULL );
361  return TCL_ERROR;
362  }
363 
364 // Create the window.
365 
366  tkwin = Tk_CreateWindowFromPath( interp, Tk_MainWindow( interp ), argv[1], (char *) NULL );
367  if ( tkwin == NULL )
368  {
369  return TCL_ERROR;
370  }
371  Tk_SetClass( tkwin, "Plframe" );
372 
373  plPlotterPtr = (PlPlotter *) malloc( sizeof ( PlPlotter ) );
374 
375  // Initialize in the same order as the members of the struct just
376  // to keep track of what is initialized and what not.
377  plPlotterPtr->tkwin = tkwin;
378  plPlotterPtr->display = Tk_Display( tkwin );
379  plPlotterPtr->interp = interp;
380  plPlotterPtr->widgetCmd =
381  Tcl_CreateCommand( interp, Tk_PathName( plPlotterPtr->tkwin ),
382  (Tcl_CmdProc *) PlPlotterWidgetCmd, (ClientData) plPlotterPtr,
383  (Tcl_CmdDeleteProc *) NULL );
384  plPlotterPtr->border = NULL;
385  //plPlotterPtr->borderWidth = <uninitialized>;
386  //plPlotterPtr->relief = <uninitialized>;
387  plPlotterPtr->width = Tk_Width( plPlotterPtr->tkwin );
388  plPlotterPtr->height = Tk_Height( plPlotterPtr->tkwin );
389  plPlotterPtr->cursor = None;
390  plPlotterPtr->flags = 0;
391  plPlotterPtr->tkwin_initted = 0;
392  // Associate new PLplot stream with this widget
393  plmkstrm( &plPlotterPtr->ipls );
394  plgpls( &plPlotterPtr->pls );
395  plPlotterPtr->ipls_save = 0;
396  plPlotterPtr->plr = (PLRDev *) malloc( sizeof ( PLRDev ) );
397  plPlotterPtr->plpr_cmd = NULL;
398  plPlotterPtr->active_plot = 1;
399  plPlotterPtr->isActive = 0;
400  plPlotterPtr->pldis.x = 0;
401  plPlotterPtr->pldis.y = 0;
402  plPlotterPtr->pldis.width = 0;
403  plPlotterPtr->pldis.height = 0;
404  plPlotterPtr->prevWidth = 0;
405  plPlotterPtr->prevHeight = 0;
406  plPlotterPtr->SaveFnam = NULL;
407  // plPlotterPtr->devDesc = <uninitialized, to be malloced?>;
408  // plPlotterPtr->devName = <uninitialized, to be malloced?>;
409  plPlotterPtr->xorGC = NULL;
410  // plPlotterPtr->pts = <uninitialized array>;
411  plPlotterPtr->continue_draw = 0;
412  plPlotterPtr->xhair_cursor = None;
413  plPlotterPtr->xl = 0.;
414  plPlotterPtr->yl = 0.;
415  plPlotterPtr->xr = 1.;
416  plPlotterPtr->yr = 1.;
417  plPlotterPtr->xScrollCmd = NULL;
418  plPlotterPtr->yScrollCmd = NULL;
419  plPlotterPtr->bopCmd = NULL;
420  plPlotterPtr->eopCmd = NULL;
421  plPlotterPtr->xhairs = 0;
422  plPlotterPtr->drawing_xhairs = 0;
423  // plPlotterPtr->xhair_x = <uninitialized array>;
424  // plPlotterPtr->xhair_y = <uninitialized array>;
425  plPlotterPtr->rband = 0;
426  plPlotterPtr->drawing_rband = 0;
427  // plPlotterPtr->rband_pt = <uninitialized array>;
428  plPlotterPtr->double_buffer = 1;
429 
430  plr = plPlotterPtr->plr;
431  plr->pdfs = NULL;
432  plr->iodev = (PLiodev *) malloc( sizeof ( PLiodev ) );
433  plr_start( plr );
434 
435  // Tell the stream about myself
436  plPlotterPtr->pls->plPlotterPtr = plPlotterPtr;
437 
438 // Set up stuff for rubber-band drawing
439 
440  plPlotterPtr->xhair_cursor =
441  Tk_GetCursor( plPlotterPtr->interp, plPlotterPtr->tkwin, "crosshair" );
442 
443 // Partially initialize Tk driver.
444 
445  plD_open_tkwin( plPlotterPtr->pls );
446 
447 // Create list of valid device names and keywords for page dumps
448 
449  plPlotterPtr->devDesc = (const char **) malloc( (size_t) NDEV * sizeof ( char ** ) );
450  plPlotterPtr->devName = (const char **) malloc( (size_t) NDEV * sizeof ( char ** ) );
451  for ( i = 0; i < NDEV; i++ )
452  {
453  plPlotterPtr->devDesc[i] = NULL;
454  plPlotterPtr->devName[i] = NULL;
455  }
456  ndev = NDEV;
457  plgFileDevs( &plPlotterPtr->devDesc, &plPlotterPtr->devName, &ndev );
458 
459 // Start up event handlers and other good stuff
460 
461  Tk_CreateEventHandler( plPlotterPtr->tkwin, StructureNotifyMask,
462  PlPlotterConfigureEH, (ClientData) plPlotterPtr );
463 
464  Tk_CreateEventHandler( plPlotterPtr->tkwin, ExposureMask,
465  PlPlotterExposeEH, (ClientData) plPlotterPtr );
466 
467 // for active plot
468  ActiveState( plPlotterPtr, 1 );
469 
470  if ( ConfigurePlPlotter( interp, plPlotterPtr, argc - 2, (CONST char **) argv + 2, 0 ) != TCL_OK )
471  {
472  Tk_DestroyWindow( plPlotterPtr->tkwin );
473  return TCL_ERROR;
474  }
475  Tk_MakeWindowExist( plPlotterPtr->tkwin );
476  PlPlotterFirstInit( (ClientData) plPlotterPtr );
477  Tk_GeometryRequest( plPlotterPtr->tkwin, 200, 200 );
478 
479  Tcl_SetResult( interp, Tk_PathName( plPlotterPtr->tkwin ), TCL_VOLATILE );
480 
481  return TCL_OK;
482 }
483 
484 //
485 //--------------------------------------------------------------------------
486 //
487 // PlPlotterWidgetCmd --
488 //
489 // This procedure is invoked to process the Tcl command that
490 // corresponds to a plframe widget. See the user
491 // documentation for details on what it does.
492 //
493 // Results:
494 // A standard Tcl result.
495 //
496 // Side effects:
497 // See the user documentation.
498 //
499 //--------------------------------------------------------------------------
500 //
501 
502 static int
503 PlPlotterWidgetCmd( ClientData clientData, Tcl_Interp *interp,
504  int argc, CONST char **argv )
505 {
506  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
507  int result = TCL_OK;
508  int length;
509  char c;
510 
511  dbug_enter( "PlPlotterWidgetCmd" );
512 
513  if ( argc < 2 )
514  {
515  Tcl_AppendResult( interp, "wrong # args: should be \"",
516  argv[0], " option ?arg arg ...?\"", (char *) NULL );
517  return TCL_ERROR;
518  }
519  Tk_Preserve( (ClientData) plPlotterPtr );
520  c = argv[1][0];
521  length = (int) strlen( argv[1] );
522 
523 // cmd -- issue a command to the PLplot library
524 
525  if ( ( c == 'c' ) && ( strncmp( argv[1], "cmd", (size_t) length ) == 0 ) )
526  {
527  result = Cmd( interp, plPlotterPtr, argc - 2, argv + 2 );
528  }
529 
530 // configure
531 
532  else if ( ( c == 'c' ) && ( strncmp( argv[1], "cget", (size_t) length ) == 0 )
533  && ( length >= 2 ) )
534  {
535  if ( argc != 3 )
536  {
537  Tcl_AppendResult( interp, "wrong # args: should be \"",
538  argv[0], " cget option\"",
539  (char *) NULL );
540  result = TCL_ERROR;
541  goto done;
542  }
543  result = Tk_ConfigureValue( interp, plPlotterPtr->tkwin, configSpecs,
544  (char *) plPlotterPtr, argv[2], 0 );
545  }
546  else if ( ( c == 'c' ) && ( strncmp( argv[1], "configure", (size_t) length ) == 0 ) )
547  {
548  if ( argc == 2 )
549  {
550  result = Tk_ConfigureInfo( interp, plPlotterPtr->tkwin, configSpecs,
551  (char *) plPlotterPtr, (char *) NULL, 0 );
552  }
553  else if ( argc == 3 )
554  {
555  result = Tk_ConfigureInfo( interp, plPlotterPtr->tkwin, configSpecs,
556  (char *) plPlotterPtr, argv[2], 0 );
557  }
558  else
559  {
560  result = ConfigurePlPlotter( interp, plPlotterPtr, argc - 2, argv + 2,
561  TK_CONFIG_ARGV_ONLY );
562  }
563  }
564 
565 // closelink -- Close a binary data link previously opened with openlink
566 
567  else if ( ( c == 'c' ) && ( strncmp( argv[1], "closelink", (size_t) length ) == 0 ) )
568  {
569  if ( argc > 2 )
570  {
571  Tcl_AppendResult( interp, "wrong # args: should be \"",
572  argv[0], (char *) NULL );
573  result = TCL_ERROR;
574  goto done;
575  }
576  else
577  {
578  result = Closelink( interp, plPlotterPtr, argc - 2, argv + 2 );
579  }
580  }
581 
582 // draw -- rubber-band draw used in region selection
583 
584  else if ( ( c == 'd' ) && ( strncmp( argv[1], "draw", (size_t) length ) == 0 ) )
585  {
586  if ( argc == 2 )
587  {
588  Tcl_AppendResult( interp, "wrong # args: should be \"",
589  argv[0], " draw op ?options?\"", (char *) NULL );
590  result = TCL_ERROR;
591  goto done;
592  }
593  else
594  {
595  result = Draw( interp, plPlotterPtr, argc - 2, argv + 2 );
596  }
597  }
598 
599 // info -- returns requested info
600 
601  else if ( ( c == 'i' ) && ( strncmp( argv[1], "info", (size_t) length ) == 0 ) )
602  {
603  result = Info( interp, plPlotterPtr, argc - 2, argv + 2 );
604  }
605 
606 // next page. called to cancel wait for page in tkwin driver
607 
608  else if ( ( c == 'n' ) && ( strncmp( argv[1], "nextpage", (size_t) length ) == 0 ) )
609  {
610  result = NextPage( interp, plPlotterPtr, argc - 2, argv + 2 );
611  }
612 
613 // orient -- Set plot orientation
614 
615  else if ( ( c == 'o' ) && ( strncmp( argv[1], "orient", (size_t) length ) == 0 ) )
616  {
617  result = Orient( interp, plPlotterPtr, argc - 2, argv + 2 );
618  }
619 
620 // openlink -- Open a binary data link (FIFO or socket)
621 
622  else if ( ( c == 'o' ) && ( strncmp( argv[1], "openlink", (size_t) length ) == 0 ) )
623  {
624  if ( argc < 3 )
625  {
626  Tcl_AppendResult( interp, "wrong # args: should be \"",
627  argv[0], " option ?arg arg ...?\"", (char *) NULL );
628  result = TCL_ERROR;
629  goto done;
630  }
631  else
632  {
633  result = Openlink( interp, plPlotterPtr, argc - 2, argv + 2 );
634  }
635  }
636 
637 // page -- change or return output page setup
638 
639  else if ( ( c == 'p' ) && ( strncmp( argv[1], "page", (size_t) length ) == 0 ) )
640  {
641  result = Page( interp, plPlotterPtr, argc - 2, argv + 2 );
642  }
643 
644 // print -- prints plot
645 
646  else if ( ( c == 'p' ) && ( strncmp( argv[1], "print", (size_t) length ) == 0 ) )
647  {
648  result = Print( interp, plPlotterPtr, argc - 2, argv + 2 );
649  }
650 
651 // redraw -- redraw plot
652 
653  else if ( ( c == 'r' ) && ( strncmp( argv[1], "redraw", (size_t) length ) == 0 ) )
654  {
655  if ( argc > 2 )
656  {
657  Tcl_AppendResult( interp, "wrong # args: should be \"",
658  argv[0], " redraw\"", (char *) NULL );
659  result = TCL_ERROR;
660  goto done;
661  }
662  else
663  {
664  result = Redraw( interp, plPlotterPtr, argc - 2, argv + 2 );
665  }
666  }
667 
668 // report -- find out useful info about the plframe (GMF)
669 
670  else if ( ( c == 'r' ) && ( strncmp( argv[1], "report", (size_t) length ) == 0 ) )
671  {
672  result = report( interp, plPlotterPtr, argc - 2, argv + 2 );
673  }
674 
675 // save -- saves plot to the specified plot file type
676 
677  else if ( ( c == 's' ) && ( strncmp( argv[1], "save", (size_t) length ) == 0 ) )
678  {
679  result = Save( interp, plPlotterPtr, argc - 2, argv + 2 );
680  }
681 
682 // view -- change or return window into plot
683 
684  else if ( ( c == 'v' ) && ( strncmp( argv[1], "view", (size_t) length ) == 0 ) )
685  {
686  result = View( interp, plPlotterPtr, argc - 2, argv + 2 );
687  }
688 
689 // xscroll -- horizontally scroll window into plot
690 
691  else if ( ( c == 'x' ) && ( strncmp( argv[1], "xview", (size_t) length ) == 0 ) )
692  {
693  int count, type;
694  double width = (double) ( plPlotterPtr->xr - plPlotterPtr->xl );
695 
696  double fraction;
697 
698  type = Tk_GetScrollInfo( interp, argc, argv, &fraction, &count );
699  switch ( type )
700  {
701  case TK_SCROLL_ERROR:
702  result = TCL_ERROR;
703  goto done;
704  case TK_SCROLL_MOVETO:
705  plPlotterPtr->xl = (PLFLT) fraction;
706  plPlotterPtr->xr = (PLFLT) ( fraction + width );
707  break;
708  case TK_SCROLL_PAGES:
709  plPlotterPtr->xl += (PLFLT) ( count * width * .9 );
710  plPlotterPtr->xr += (PLFLT) ( count * width * .9 );
711  break;
712  case TK_SCROLL_UNITS:
713  plPlotterPtr->xl += (PLFLT) ( count * width / 50 );
714  plPlotterPtr->xr += (PLFLT) ( count * width / 50 );
715  break;
716  }
717  if ( plPlotterPtr->xr > 1.0 )
718  {
719  plPlotterPtr->xr = 1.0;
720  plPlotterPtr->xl = (PLFLT) ( 1.0 - width );
721  }
722  else if ( plPlotterPtr->xl < 0.0 )
723  {
724  plPlotterPtr->xl = 0.0;
725  plPlotterPtr->xr = (PLFLT) width;
726  }
727  Scroll( interp, plPlotterPtr );
728  }
729 
730 // yscroll -- vertically scroll window into plot
731 
732  else if ( ( c == 'y' ) && ( strncmp( argv[1], "yview", (size_t) length ) == 0 ) )
733  {
734  int count, type;
735  double height = plPlotterPtr->yr - plPlotterPtr->yl;
736 
737  double fraction;
738 
739  type = Tk_GetScrollInfo( interp, argc, argv, &fraction, &count );
740  switch ( type )
741  {
742  case TK_SCROLL_ERROR:
743  result = TCL_ERROR;
744  goto done;
745  case TK_SCROLL_MOVETO:
746  plPlotterPtr->yl = (PLFLT) ( 1.0 - fraction - height );
747  plPlotterPtr->yr = (PLFLT) ( 1.0 - fraction );
748  break;
749  case TK_SCROLL_PAGES:
750  plPlotterPtr->yl -= (PLFLT) ( count * height * .9 );
751  plPlotterPtr->yr -= (PLFLT) ( count * height * .9 );
752  break;
753  case TK_SCROLL_UNITS:
754  plPlotterPtr->yl -= (PLFLT) ( count * height / 50 );
755  plPlotterPtr->yr -= (PLFLT) ( count * height / 50 );
756  break;
757  }
758  if ( plPlotterPtr->yr > 1.0 )
759  {
760  plPlotterPtr->yr = 1.0;
761  plPlotterPtr->yl = (PLFLT) ( 1.0 - height );
762  }
763  else if ( plPlotterPtr->yl < 0.0 )
764  {
765  plPlotterPtr->yl = 0.0;
766  plPlotterPtr->yr = (PLFLT) height;
767  }
768  Scroll( interp, plPlotterPtr );
769  }
770 
771 // unrecognized widget command
772 
773  else
774  {
775  Tcl_AppendResult( interp, "bad option \"", argv[1],
776  "\": must be closelink, cmd, configure, draw, info, nextpage ",
777  "openlink, orient, page, print, redraw, save, view, ",
778  "xview, or yview", (char *) NULL );
779 
780  result = TCL_ERROR;
781  }
782 
783 done:
784  Tk_Release( (ClientData) plPlotterPtr );
785  return result;
786 }
787 
788 //
789 //--------------------------------------------------------------------------
790 //
791 // DestroyPlPlotter --
792 //
793 // This procedure is invoked by Tk_EventuallyFree or Tk_Release to
794 // clean up the internal structure of a plframe at a safe time
795 // (when no-one is using it anymore).
796 //
797 // Results:
798 // None.
799 //
800 // Side effects:
801 // Everything associated with the plframe is freed up.
802 //
803 //--------------------------------------------------------------------------
804 //
805 
806 static void DestroyPlPlotter( ClientData clientData )
807 {
808  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
809  register PLRDev *plr = plPlotterPtr->plr;
810  TkwDev *dev = (TkwDev *) plPlotterPtr->pls->dev;
811 
812  dbug_enter( "DestroyPlPlotter" );
813 
814  dev->flags |= 0x3;
815 
816  if ( plPlotterPtr->border != NULL )
817  {
818  Tk_Free3DBorder( plPlotterPtr->border );
819  }
820  if ( plPlotterPtr->plpr_cmd != NULL )
821  {
822  free( (char *) plPlotterPtr->plpr_cmd );
823  }
824  if ( plPlotterPtr->cursor != None )
825  {
826  Tk_FreeCursor( plPlotterPtr->display, plPlotterPtr->cursor );
827  }
828  if ( plPlotterPtr->xhair_cursor != None )
829  {
830  Tk_FreeCursor( plPlotterPtr->display, plPlotterPtr->xhair_cursor );
831  }
832  if ( plPlotterPtr->xorGC != NULL )
833  {
834  Tk_FreeGC( plPlotterPtr->display, plPlotterPtr->xorGC );
835  }
836  if ( plPlotterPtr->yScrollCmd != NULL )
837  {
838  free( (char *) plPlotterPtr->yScrollCmd );
839  }
840  if ( plPlotterPtr->xScrollCmd != NULL )
841  {
842  free( (char *) plPlotterPtr->xScrollCmd );
843  }
844  if ( plPlotterPtr->SaveFnam != NULL )
845  {
846  free( (char *) plPlotterPtr->SaveFnam );
847  }
848  if ( plPlotterPtr->devDesc != NULL )
849  {
850  free( (char *) plPlotterPtr->devDesc );
851  }
852  if ( plPlotterPtr->devName != NULL )
853  {
854  free( (char *) plPlotterPtr->devName );
855  }
856 
857 // Clean up data connection
858 
859  pdf_close( plr->pdfs );
860  free( (char *) plPlotterPtr->plr->iodev );
861 
862 // Tell PLplot to clean up
863 
864  plsstrm( plPlotterPtr->ipls );
865  plend1();
866 
867 // Delete main data structures
868 
869  free( (char *) plPlotterPtr->plr );
870  free( (char *) plPlotterPtr );
871 }
872 
873 //
874 //--------------------------------------------------------------------------
875 //
876 // PlPlotterConfigureEH --
877 //
878 // Invoked by the Tk dispatcher on structure changes to a plframe.
879 //
880 // Results:
881 // None.
882 //
883 // Side effects:
884 // When the window gets deleted, internal structures get cleaned up.
885 // When it gets resized, it is redrawn.
886 //
887 //--------------------------------------------------------------------------
888 //
889 
890 static void
891 PlPlotterConfigureEH( ClientData clientData, register XEvent *eventPtr )
892 {
893  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
894  register Tk_Window tkwin = plPlotterPtr->tkwin;
895 
896  dbug_enter( "PlPlotterConfigureEH" );
897 
898  switch ( eventPtr->type )
899  {
900  case ConfigureNotify:
901  pldebug( "PLFrameConfigureEH", "ConfigureNotify\n" );
902  plPlotterPtr->flags |= RESIZE_PENDING;
903  plPlotterPtr->width = Tk_Width( tkwin );
904  plPlotterPtr->height = Tk_Height( tkwin );
905  if ( ( tkwin != NULL ) && !( plPlotterPtr->flags & REFRESH_PENDING ) )
906  {
907  Tcl_DoWhenIdle( DisplayPlPlotter, (ClientData) plPlotterPtr );
908  plPlotterPtr->flags |= REFRESH_PENDING;
909  plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR;
910  }
911  break;
912 
913  case DestroyNotify:
914  pldebug( "PLFrameConfigureEH", "DestroyNotify\n" );
915  Tcl_DeleteCommand( plPlotterPtr->interp, Tk_PathName( tkwin ) );
916  plPlotterPtr->tkwin = NULL;
917  if ( plPlotterPtr->flags & REFRESH_PENDING )
918  {
919  Tcl_CancelIdleCall( DisplayPlPlotter, (ClientData) plPlotterPtr );
920  }
921  Tk_EventuallyFree( (ClientData) plPlotterPtr, (Tcl_FreeProc *) DestroyPlPlotter );
922  break;
923 
924  case MapNotify:
925  pldebug( "PLFrameConfigureEH", "MapNotify\n" );
926  if ( plPlotterPtr->flags & REFRESH_PENDING )
927  {
928  Tcl_CancelIdleCall( DisplayPlPlotter, (ClientData) plPlotterPtr );
929  }
930 
931 // Vince thinks we don't want these lines any more
932 // We forced the window into existence when we created it.
933 #if 0
934  // For some reason, "." must be mapped or PlPlotterInit will die (Note:
935  // mapped & withdrawn or mapped in the withdrawn state is OK). Issuing
936  // an update fixes this. I'd love to know why this occurs.
937  //
938 
939  if ( !plPlotterPtr->tkwin_initted )
940  {
941  Tcl_VarEval( plPlotterPtr->interp, "update", (char *) NULL );
942  }
943 #endif
944  // Set up window parameters and arrange for window to be refreshed
945 
946  Tcl_DoWhenIdle( PlPlotterInit, (ClientData) plPlotterPtr );
947  break;
948  }
949 }
950 
951 //
952 //--------------------------------------------------------------------------
953 //
954 // PlPlotterExposeEH --
955 //
956 // Invoked by the Tk dispatcher on exposes of a plframe.
957 //
958 // Results:
959 // None.
960 //
961 // Side effects:
962 // Widget is redisplayed.
963 //
964 // Note: it's customary in Tk to collapse multiple exposes, so for best
965 // performance without losing the window contents, I keep track of the
966 // smallest single rectangle that can satisfy all expose events. If there
967 // are any overlaid graphics (like crosshairs), however, we need to refresh
968 // the entire plot in order to have a predictable outcome.
969 //
970 //--------------------------------------------------------------------------
971 
972 static void
973 PlPlotterExposeEH( ClientData clientData, register XEvent *eventPtr )
974 {
975  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
976  XExposeEvent *event = (XExposeEvent *) eventPtr;
977  register Tk_Window tkwin = plPlotterPtr->tkwin;
978 
979  dbug_enter( "PlPlotterExposeEH" );
980 
981  pldebug( "PLFrameExposeEH", "Expose\n" );
982 
983 // Set up the area to refresh
984 
985  if ( !( plPlotterPtr->drawing_xhairs || plPlotterPtr->drawing_rband ) )
986  {
987  int x0_old, x1_old, y0_old, y1_old, x0_new, x1_new, y0_new, y1_new;
988 
989  x0_old = (int) plPlotterPtr->pldis.x;
990  y0_old = (int) plPlotterPtr->pldis.y;
991  x1_old = x0_old + (int) plPlotterPtr->pldis.width;
992  y1_old = y0_old + (int) plPlotterPtr->pldis.height;
993 
994  x0_new = event->x;
995  y0_new = event->y;
996  x1_new = x0_new + event->width;
997  y1_new = y0_new + event->height;
998 
999  plPlotterPtr->pldis.x = (unsigned int) MIN( x0_old, x0_new );
1000  plPlotterPtr->pldis.y = (unsigned int) MIN( y0_old, y0_new );
1001  plPlotterPtr->pldis.width = (unsigned int) MAX( x1_old, x1_new ) - plPlotterPtr->pldis.x;
1002  plPlotterPtr->pldis.height = (unsigned int) MAX( y1_old, y1_new ) - plPlotterPtr->pldis.y;
1003  }
1004 
1005 // Invoke DoWhenIdle handler to redisplay widget.
1006 
1007  if ( event->count == 0 )
1008  {
1009  if ( ( tkwin != NULL ) && !( plPlotterPtr->flags & REFRESH_PENDING ) )
1010  {
1011  Tcl_DoWhenIdle( DisplayPlPlotter, (ClientData) plPlotterPtr );
1012  plPlotterPtr->width = Tk_Width( tkwin );
1013  plPlotterPtr->height = Tk_Height( tkwin );
1014  plPlotterPtr->flags |= REFRESH_PENDING;
1015  }
1016  }
1017 }
1018 
1019 //
1020 //--------------------------------------------------------------------------
1021 //
1022 // PlPlotterMotionEH --
1023 //
1024 // Invoked by the Tk dispatcher on MotionNotify events in a plframe.
1025 // Not invoked unless we are drawing graphic crosshairs.
1026 //
1027 // Results:
1028 // None.
1029 //
1030 // Side effects:
1031 // Graphic crosshairs are drawn.
1032 //
1033 //--------------------------------------------------------------------------
1034 //
1035 
1036 static void
1037 PlPlotterMotionEH( ClientData clientData, register XEvent *eventPtr )
1038 {
1039  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
1040  XMotionEvent *event = (XMotionEvent *) eventPtr;
1041 
1042  dbug_enter( "PlPlotterMotionEH" );
1043 
1044  if ( plPlotterPtr->drawing_xhairs )
1045  {
1046  DrawXhairs( plPlotterPtr, event->x, event->y );
1047  }
1048  if ( plPlotterPtr->drawing_rband )
1049  {
1050  DrawRband( plPlotterPtr, event->x, event->y );
1051  }
1052 }
1053 
1054 //
1055 //--------------------------------------------------------------------------
1056 //
1057 // PlPlotterEnterEH --
1058 //
1059 // Invoked by the Tk dispatcher on EnterNotify events in a plframe.
1060 // Not invoked unless we are drawing graphic crosshairs.
1061 //
1062 // Results:
1063 // None.
1064 //
1065 // Side effects:
1066 // Graphic crosshairs are updated.
1067 //
1068 //--------------------------------------------------------------------------
1069 
1070 static void
1071 PlPlotterEnterEH( ClientData clientData, register XEvent *eventPtr )
1072 {
1073  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
1074  XCrossingEvent *crossingEvent = (XCrossingEvent *) eventPtr;
1075 
1076  dbug_enter( "PlPlotterEnterEH" );
1077 
1078  if ( plPlotterPtr->xhairs )
1079  {
1080  DrawXhairs( plPlotterPtr, crossingEvent->x, crossingEvent->y );
1081  plPlotterPtr->drawing_xhairs = 1;
1082  }
1083  if ( plPlotterPtr->rband )
1084  {
1085  plPlotterPtr->drawing_rband = 1;
1086  UpdateRband( plPlotterPtr );
1087  DrawRband( plPlotterPtr, crossingEvent->x, crossingEvent->y );
1088  }
1089 }
1090 
1091 //
1092 //--------------------------------------------------------------------------
1093 //
1094 // PlPlotterLeaveEH --
1095 //
1096 // Invoked by the Tk dispatcher on LeaveNotify events in a plframe.
1097 // Not invoked unless we are drawing graphic crosshairs.
1098 //
1099 // Results:
1100 // None.
1101 //
1102 // Side effects:
1103 // Graphic crosshairs are updated.
1104 //
1105 //--------------------------------------------------------------------------
1106 
1107 static void
1108 PlPlotterLeaveEH( ClientData clientData, register XEvent * PL_UNUSED( eventPtr ) )
1109 {
1110  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
1111 
1112  dbug_enter( "PlPlotterLeaveEH" );
1113 
1114  if ( plPlotterPtr->drawing_xhairs )
1115  {
1116  UpdateXhairs( plPlotterPtr );
1117  plPlotterPtr->drawing_xhairs = 0;
1118  }
1119  if ( plPlotterPtr->drawing_rband )
1120  {
1121  UpdateRband( plPlotterPtr );
1122  plPlotterPtr->drawing_rband = 0;
1123  }
1124 }
1125 
1126 static void
1127 PlPlotterButtonPressEH( ClientData clientData, register XEvent *eventPtr )
1128 {
1129  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
1130  XButtonEvent *event = (XButtonEvent *) eventPtr;
1131 
1132  // Get modifier keys
1133  switch ( event->state )
1134  {
1135  case 256: // plain
1136  if ( plPlotterPtr->drawing_rband )
1137  {
1138  UpdateRband( plPlotterPtr );
1139  }
1140  if ( plPlotterPtr->rband )
1141  CreateRband( plPlotterPtr );
1142  break;
1143  }
1144 }
1145 
1146 //--------------------------------------------------------------------------
1147 // CreateXhairs()
1148 //
1149 // Creates graphic crosshairs at current pointer location.
1150 //--------------------------------------------------------------------------
1151 
1152 static void
1153 CreateXhairs( PlPlotter *plPlotterPtr )
1154 {
1155  register Tk_Window tkwin = plPlotterPtr->tkwin;
1156  Window root, child;
1157  int root_x, root_y, win_x, win_y;
1158  unsigned int mask;
1159 
1160 
1161 // Find current pointer location and draw graphic crosshairs if pointer is
1162 // inside our window.
1163 
1164  if ( XQueryPointer( plPlotterPtr->display, Tk_WindowId( tkwin ),
1165  &root, &child, &root_x, &root_y, &win_x, &win_y,
1166  &mask ) )
1167  {
1168  #ifdef MAC_TCL
1169  // Mac Tk only has a partial implementation of the above function
1170  // so we must fix it
1171  Tk_GetRootCoords( tkwin, &win_x, &win_y );
1172  win_x = root_x - win_x;
1173  win_y = root_y - win_y;
1174  #endif
1175  if ( win_x >= 0 && win_x < Tk_Width( tkwin ) &&
1176  win_y >= 0 && win_y < Tk_Height( tkwin ) )
1177  {
1178  DrawXhairs( plPlotterPtr, win_x, win_y );
1179  plPlotterPtr->drawing_xhairs = 1;
1180  }
1181  }
1182 }
1183 
1184 //--------------------------------------------------------------------------
1185 // DestroyXhairs()
1186 //
1187 // Destroys graphic crosshairs.
1188 //--------------------------------------------------------------------------
1189 
1190 static void
1191 DestroyXhairs( PlPlotter *plPlotterPtr )
1192 {
1193 // This draw removes the last set of graphic crosshairs
1194 
1195  UpdateXhairs( plPlotterPtr );
1196  plPlotterPtr->drawing_xhairs = 0;
1197 }
1198 
1199 //--------------------------------------------------------------------------
1200 // DrawXhairs()
1201 //
1202 // Draws graphic crosshairs at (x0, y0). The first draw erases the old set.
1203 //--------------------------------------------------------------------------
1204 
1205 static void
1206 DrawXhairs( PlPlotter *plPlotterPtr, int x0, int y0 )
1207 {
1208  register Tk_Window tkwin = plPlotterPtr->tkwin;
1209  int xmin = 0, xmax = Tk_Width( tkwin ) - 1;
1210  int ymin = 0, ymax = Tk_Height( tkwin ) - 1;
1211 
1212  if ( plPlotterPtr->drawing_xhairs )
1213  UpdateXhairs( plPlotterPtr );
1214 
1215  plPlotterPtr->xhair_x[0].x = (short) xmin; plPlotterPtr->xhair_x[0].y = (short) y0;
1216  plPlotterPtr->xhair_x[1].x = (short) xmax; plPlotterPtr->xhair_x[1].y = (short) y0;
1217 
1218  plPlotterPtr->xhair_y[0].x = (short) x0; plPlotterPtr->xhair_y[0].y = (short) ymin;
1219  plPlotterPtr->xhair_y[1].x = (short) x0; plPlotterPtr->xhair_y[1].y = (short) ymax;
1220 
1221  UpdateXhairs( plPlotterPtr );
1222 }
1223 
1224 //--------------------------------------------------------------------------
1225 // UpdateXhairs()
1226 //
1227 // Updates graphic crosshairs. If already there, they are erased.
1228 //--------------------------------------------------------------------------
1229 
1230 static void
1231 UpdateXhairs( PlPlotter *plPlotterPtr )
1232 {
1233  register Tk_Window tkwin = plPlotterPtr->tkwin;
1234 
1235  XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ),
1236  plPlotterPtr->xorGC, plPlotterPtr->xhair_x, 2,
1237  CoordModeOrigin );
1238 
1239  XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ),
1240  plPlotterPtr->xorGC, plPlotterPtr->xhair_y, 2,
1241  CoordModeOrigin );
1242 }
1243 
1244 //--------------------------------------------------------------------------
1245 // CreateRband()
1246 //
1247 // Initiate rubber banding.
1248 //--------------------------------------------------------------------------
1249 
1250 static void
1251 CreateRband( PlPlotter *plPlotterPtr )
1252 {
1253  register Tk_Window tkwin = plPlotterPtr->tkwin;
1254  Window root, child;
1255  int root_x, root_y, win_x, win_y;
1256  unsigned int mask;
1257 
1258 // Find current pointer location, and initiate rubber banding.
1259 
1260  if ( XQueryPointer( plPlotterPtr->display, Tk_WindowId( tkwin ),
1261  &root, &child, &root_x, &root_y, &win_x, &win_y,
1262  &mask ) )
1263  {
1264  #ifdef MAC_TCL
1265  // Mac Tk only has a partial implementation of the above function
1266  // so we must fix it
1267  Tk_GetRootCoords( tkwin, &win_x, &win_y );
1268  win_x = root_x - win_x;
1269  win_y = root_y - win_y;
1270  #endif
1271  if ( win_x >= 0 && win_x < Tk_Width( tkwin ) &&
1272  win_y >= 0 && win_y < Tk_Height( tkwin ) )
1273  {
1274  // Okay, pointer is in our window.
1275  plPlotterPtr->rband_pt[0].x = (short) win_x;
1276  plPlotterPtr->rband_pt[0].y = (short) win_y;
1277 
1278  DrawRband( plPlotterPtr, win_x, win_y );
1279  plPlotterPtr->drawing_rband = 1;
1280  }
1281  else
1282  {
1283  // Hmm, somehow they turned it on without even being in the window.
1284  // Just put the anchor in top left, they'll soon realize this is a
1285  // mistake...
1286 
1287  plPlotterPtr->rband_pt[0].x = 0;
1288  plPlotterPtr->rband_pt[0].y = 0;
1289 
1290  DrawRband( plPlotterPtr, win_x, win_y );
1291  plPlotterPtr->drawing_rband = 1;
1292  }
1293  }
1294 }
1295 
1296 //--------------------------------------------------------------------------
1297 // DestroyRband()
1298 //
1299 // Turn off rubber banding.
1300 //--------------------------------------------------------------------------
1301 
1302 static void
1303 DestroyRband( PlPlotter *plPlotterPtr )
1304 {
1305 // This draw removes the residual rubber band.
1306 
1307  UpdateRband( plPlotterPtr );
1308  plPlotterPtr->drawing_rband = 0;
1309 }
1310 
1311 //--------------------------------------------------------------------------
1312 // DrawRband()
1313 //
1314 // Draws a rubber band from the anchor to the current cursor location.
1315 //--------------------------------------------------------------------------
1316 
1317 static void
1318 DrawRband( PlPlotter *plPlotterPtr, int x0, int y0 )
1319 {
1320 // If the line is already up, clear it.
1321 
1322  if ( plPlotterPtr->drawing_rband )
1323  UpdateRband( plPlotterPtr );
1324 
1325  plPlotterPtr->rband_pt[1].x = (short) x0; plPlotterPtr->rband_pt[1].y = (short) y0;
1326 
1327  UpdateRband( plPlotterPtr );
1328 }
1329 
1330 //--------------------------------------------------------------------------
1331 // UpdateRband()
1332 //
1333 // Updates rubber band. If already there, it is erased.
1334 //--------------------------------------------------------------------------
1335 
1336 static void
1337 UpdateRband( PlPlotter *plPlotterPtr )
1338 {
1339  register Tk_Window tkwin = plPlotterPtr->tkwin;
1340 
1341  XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ),
1342  plPlotterPtr->xorGC, plPlotterPtr->rband_pt, 2,
1343  CoordModeOrigin );
1344 }
1345 
1346 // First-time initialization
1347 static void PlPlotterFirstInit( ClientData clientData )
1348 {
1349  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
1350  register Tk_Window tkwin = plPlotterPtr->tkwin;
1351 
1352  plsstrm( plPlotterPtr->ipls );
1353  plsdev( "tkwin" );
1354 // We should probably rename plsxwin to plstkwin
1355  plsxwin( (PLINT) Tk_WindowId( tkwin ) );
1356  plspause( 0 );
1357  plinit();
1358  if ( plplot_tkwin_ccmap )
1359  {
1360  Install_cmap( plPlotterPtr );
1361  }
1362  plbop();
1363 
1364  plPlotterPtr->tkwin_initted = 1;
1365  plPlotterPtr->width = Tk_Width( tkwin );
1366  plPlotterPtr->height = Tk_Height( tkwin );
1367  plPlotterPtr->prevWidth = plPlotterPtr->width;
1368  plPlotterPtr->prevHeight = plPlotterPtr->height;
1369 }
1370 
1371 //
1372 //--------------------------------------------------------------------------
1373 //
1374 // PlPlotterInit --
1375 //
1376 // Invoked to handle miscellaneous initialization after window gets
1377 // mapped.
1378 //
1379 // Results:
1380 // None.
1381 //
1382 // Side effects:
1383 // PLplot internal parameters and device driver are initialized.
1384 //
1385 //--------------------------------------------------------------------------
1386 
1387 static void
1388 PlPlotterInit( ClientData clientData )
1389 {
1390  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
1391 
1392 // Set up window parameters and arrange for window to be refreshed
1393 
1394  plPlotterPtr->flags |= REFRESH_PENDING;
1395  plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR;
1396 
1397 // Draw plframe
1398 
1399  DisplayPlPlotter( clientData );
1400 
1401  if ( plPlotterPtr->xhairs )
1402  CreateXhairs( plPlotterPtr );
1403 
1404  if ( plPlotterPtr->rband )
1405  CreateRband( plPlotterPtr );
1406 }
1407 
1408 //
1409 //--------------------------------------------------------------------------
1410 //
1411 // Install_cmap --
1412 //
1413 // Installs X driver color map as necessary when custom color maps
1414 // are used.
1415 //
1416 // Results:
1417 // None.
1418 //
1419 // Side effects:
1420 // Parent color maps may get changed.
1421 //
1422 //--------------------------------------------------------------------------
1423 //
1424 
1425 static void
1426 Install_cmap( PlPlotter *plPlotterPtr )
1427 {
1428  TkwDev *dev;
1429 
1430 #define INSTALL_COLORMAP_IN_TK
1431 #ifdef INSTALL_COLORMAP_IN_TK
1432  dev = (TkwDev *) plPlotterPtr->pls->dev;
1433  Tk_SetWindowColormap( Tk_MainWindow( plPlotterPtr->interp ), dev->tkwd->map );
1434 
1435 //
1436 // If the colormap is local to this widget, the WM must be informed that
1437 // it should be installed when the widget gets the focus. The top level
1438 // window must be added to the end of its own list, because otherwise the
1439 // window manager adds it to the front (as required by the ICCCM). Thanks
1440 // to Paul Mackerras for providing this info in his TK photo widget.
1441 //
1442 
1443 #else
1444  int count = 0;
1445  Window top, colormap_windows[5];
1446 
1447  top = Tk_WindowId( Tk_MainWindow( plPlotterPtr->interp ) );
1448 
1449  colormap_windows[count++] = Tk_WindowId( plPlotterPtr->tkwin );
1450  colormap_windows[count++] = top;
1451 
1452  if ( !XSetWMColormapWindows( plPlotterPtr->display,
1453  top, colormap_windows, count ) )
1454  fprintf( stderr, "Unable to set color map property!\n" );
1455 #endif
1456 }
1457 
1458 //
1459 //--------------------------------------------------------------------------
1460 //
1461 // DisplayPlPlotter --
1462 //
1463 // This procedure is invoked to display a plframe widget.
1464 //
1465 // Results:
1466 // None.
1467 //
1468 // Side effects:
1469 // Commands are output to X to display the plframe in its
1470 // current mode.
1471 //
1472 //--------------------------------------------------------------------------
1473 //
1474 
1475 static void
1476 DisplayPlPlotter( ClientData clientData )
1477 {
1478  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
1479  register Tk_Window tkwin = plPlotterPtr->tkwin;
1480 
1481  dbug_enter( "DisplayPlPlotter" );
1482 
1483 // Update scrollbars if needed
1484 
1485  if ( plPlotterPtr->flags & UPDATE_V_SCROLLBAR )
1486  {
1487  UpdateVScrollbar( plPlotterPtr );
1488  }
1489  if ( plPlotterPtr->flags & UPDATE_H_SCROLLBAR )
1490  {
1491  UpdateHScrollbar( plPlotterPtr );
1492  }
1493  plPlotterPtr->flags &= ~( UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR );
1494 
1495 // If not mapped yet, just return and cancel pending refresh
1496 
1497  if ( ( plPlotterPtr->tkwin == NULL ) || !Tk_IsMapped( tkwin ) )
1498  {
1499  plPlotterPtr->flags &= ~REFRESH_PENDING;
1500  return;
1501  }
1502 
1503 // All refresh events
1504 
1505  if ( plPlotterPtr->flags & REFRESH_PENDING )
1506  {
1507  plPlotterPtr->flags &= ~REFRESH_PENDING;
1508 
1509  // Reschedule resizes to avoid occasional ordering conflicts with
1510  // the packer's resize of the window (this call must come last).
1511 
1512  if ( plPlotterPtr->flags & RESIZE_PENDING )
1513  {
1514  plPlotterPtr->flags |= REFRESH_PENDING;
1515  plPlotterPtr->flags &= ~RESIZE_PENDING;
1516  Tcl_DoWhenIdle( DisplayPlPlotter, clientData );
1517  return;
1518  }
1519 
1520  // Redraw border if necessary
1521 
1522  if ( ( plPlotterPtr->border != NULL ) &&
1523  ( plPlotterPtr->relief != TK_RELIEF_FLAT ) )
1524  {
1525  Tk_Draw3DRectangle( plPlotterPtr->tkwin, Tk_WindowId( tkwin ),
1526  plPlotterPtr->border, 0, 0, Tk_Width( tkwin ), Tk_Height( tkwin ),
1527  plPlotterPtr->borderWidth, plPlotterPtr->relief );
1528  }
1529 
1530  // Redraw -- replay contents of plot buffer
1531 
1532  if ( plPlotterPtr->flags & REDRAW_PENDING )
1533  {
1534  plPlotterPtr->flags &= ~REDRAW_PENDING;
1535  plsstrm( plPlotterPtr->ipls );
1536  pl_cmd( PLESC_REDRAW, (void *) NULL );
1537  }
1538 
1539  // Resize -- if window bounds have changed
1540 
1541  else if ( ( plPlotterPtr->width != plPlotterPtr->prevWidth ) ||
1542  ( plPlotterPtr->height != plPlotterPtr->prevHeight ) )
1543  {
1544  plPlotterPtr->pldis.width = (unsigned int) ( plPlotterPtr->width );
1545  plPlotterPtr->pldis.height = (unsigned int) ( plPlotterPtr->height );
1546 
1547  plsstrm( plPlotterPtr->ipls );
1548  pl_cmd( PLESC_RESIZE, (void *) &( plPlotterPtr->pldis ) );
1549  plPlotterPtr->prevWidth = plPlotterPtr->width;
1550  plPlotterPtr->prevHeight = plPlotterPtr->height;
1551  }
1552 
1553  // Expose -- if window bounds are unchanged
1554 
1555  else
1556  {
1557  if ( plPlotterPtr->drawing_xhairs )
1558  {
1559  #ifdef MAC_TCL
1560  // For MacTk we just zap the whole window like this
1561  Tk_Draw3DRectangle( plPlotterPtr->tkwin, Tk_WindowId( tkwin ),
1562  plPlotterPtr->border, 0, 0, Tk_Width( tkwin ), Tk_Height( tkwin ),
1563  plPlotterPtr->borderWidth, plPlotterPtr->relief );
1564  #else
1565  XClearWindow( plPlotterPtr->display, Tk_WindowId( tkwin ) );
1566  #endif
1567  XFlush( plPlotterPtr->display );
1568  plsstrm( plPlotterPtr->ipls );
1569  pl_cmd( PLESC_EXPOSE, NULL );
1570  }
1571  else
1572  {
1573  plsstrm( plPlotterPtr->ipls );
1574  pl_cmd( PLESC_EXPOSE, (void *) &( plPlotterPtr->pldis ) );
1575  }
1576 
1577  // Reset window bounds so that next time they are set fresh
1578 
1579  plPlotterPtr->pldis.x = (unsigned int) ( Tk_X( tkwin ) + Tk_Width( tkwin ) );
1580  plPlotterPtr->pldis.y = (unsigned int) ( Tk_Y( tkwin ) + Tk_Height( tkwin ) );
1581  plPlotterPtr->pldis.width = (unsigned int) -Tk_Width( tkwin );
1582  plPlotterPtr->pldis.height = (unsigned int) -Tk_Height( tkwin );
1583  }
1584 
1585  // Update graphic crosshairs if necessary
1586 
1587  if ( plPlotterPtr->drawing_xhairs )
1588  {
1589  UpdateXhairs( plPlotterPtr );
1590  }
1591 
1592  // Update rubber band if necessary.
1593 
1594  if ( plPlotterPtr->drawing_rband )
1595  {
1596  UpdateRband( plPlotterPtr );
1597  }
1598  }
1599 }
1600 
1601 //--------------------------------------------------------------------------
1602 // Routines to process widget commands.
1603 //--------------------------------------------------------------------------
1604 
1605 //--------------------------------------------------------------------------
1606 // scol0
1607 //
1608 // Sets a color in cmap0.
1609 //--------------------------------------------------------------------------
1610 
1611 static int
1612 scol0( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
1613  int i, CONST char *col, int *p_changed )
1614 {
1615  PLStream *pls = plPlotterPtr->pls;
1616  XColor xcol;
1617  PLINT r, g, b;
1618 
1619  if ( col == NULL )
1620  {
1621  Tcl_AppendResult( interp, "color value not specified",
1622  (char *) NULL );
1623  return TCL_ERROR;
1624  }
1625 
1626  if ( !XParseColor( plPlotterPtr->display,
1627  Tk_Colormap( plPlotterPtr->tkwin ), col, &xcol ) )
1628  {
1629  Tcl_AppendResult( interp, "Couldn't parse color ", col,
1630  (char *) NULL );
1631  return TCL_ERROR;
1632  }
1633 
1634  r = (unsigned) ( xcol.red & 0xFF00 ) >> 8;
1635  g = (unsigned) ( xcol.green & 0xFF00 ) >> 8;
1636  b = (unsigned) ( xcol.blue & 0xFF00 ) >> 8;
1637 
1638  if ( ( pls->cmap0[i].r != r ) ||
1639  ( pls->cmap0[i].g != g ) ||
1640  ( pls->cmap0[i].b != b ) )
1641  {
1642  pls->cmap0[i].r = (unsigned char) r;
1643  pls->cmap0[i].g = (unsigned char) g;
1644  pls->cmap0[i].b = (unsigned char) b;
1645  *p_changed = 1;
1646  }
1647 
1648  return TCL_OK;
1649 }
1650 
1651 //--------------------------------------------------------------------------
1652 // scol1
1653 //
1654 // Sets a color in cmap1.
1655 //--------------------------------------------------------------------------
1656 
1657 static int
1658 scol1( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
1659  int i, CONST char *col, CONST char *pos, CONST char *rev, int *p_changed )
1660 {
1661  PLStream *pls = plPlotterPtr->pls;
1662  XColor xcol;
1663  PLFLT h, l, s, r, g, b, p;
1664  int reverse;
1665 
1666  if ( col == NULL )
1667  {
1668  Tcl_AppendResult( interp, "color value not specified",
1669  (char *) NULL );
1670  return TCL_ERROR;
1671  }
1672 
1673  if ( pos == NULL )
1674  {
1675  Tcl_AppendResult( interp, "control point position not specified",
1676  (char *) NULL );
1677  return TCL_ERROR;
1678  }
1679 
1680  if ( rev == NULL )
1681  {
1682  Tcl_AppendResult( interp, "interpolation sense not specified",
1683  (char *) NULL );
1684  return TCL_ERROR;
1685  }
1686 
1687  if ( !XParseColor( plPlotterPtr->display,
1688  Tk_Colormap( plPlotterPtr->tkwin ), col, &xcol ) )
1689  {
1690  Tcl_AppendResult( interp, "Couldn't parse color ", col,
1691  (char *) NULL );
1692  return TCL_ERROR;
1693  }
1694 
1695  r = (PLFLT) ( ( (unsigned) ( xcol.red & 0xFF00 ) >> 8 ) / 255.0 );
1696  g = (PLFLT) ( ( (unsigned) ( xcol.green & 0xFF00 ) >> 8 ) / 255.0 );
1697  b = (PLFLT) ( ( (unsigned) ( xcol.blue & 0xFF00 ) >> 8 ) / 255.0 );
1698 
1699  plrgbhls( r, g, b, &h, &l, &s );
1700 
1701  p = (PLFLT) ( atof( pos ) / 100.0 );
1702  reverse = atoi( rev );
1703 
1704  if ( ( pls->cmap1cp[i].h != h ) ||
1705  ( pls->cmap1cp[i].l != l ) ||
1706  ( pls->cmap1cp[i].s != s ) ||
1707  ( pls->cmap1cp[i].p != p ) ||
1708  ( pls->cmap1cp[i].alt_hue_path != reverse ) )
1709  {
1710  pls->cmap1cp[i].h = h;
1711  pls->cmap1cp[i].l = l;
1712  pls->cmap1cp[i].s = s;
1713  pls->cmap1cp[i].p = p;
1714  pls->cmap1cp[i].alt_hue_path = reverse;
1715  *p_changed = 1;
1716  }
1717  return TCL_OK;
1718 }
1719 
1720 //--------------------------------------------------------------------------
1721 // Cmd
1722 //
1723 // Processes "cmd" widget command.
1724 // Handles commands that go more or less directly to the PLplot library.
1725 // Most of these come out of the PLplot Tcl API support file.
1726 //--------------------------------------------------------------------------
1727 
1728 static int
1729 Cmd( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
1730  int argc, CONST char **argv )
1731 {
1732  PLStream *pls = plPlotterPtr->pls;
1733  int length;
1734  char c3;
1735  int result = TCL_OK;
1736  char cmdlist[] = "plgcmap0 plgcmap1 plscmap0 plscmap1 plscol0 plscol1";
1737  char * argv_cp;
1738 
1739 #ifdef DEBUG
1740  if ( pls->debug )
1741  {
1742  int i;
1743  fprintf( stderr, "There are %d arguments to Cmd:", argc );
1744  for ( i = 0; i < argc; i++ )
1745  {
1746  fprintf( stderr, " %s", argv[i] );
1747  }
1748  fprintf( stderr, "\n" );
1749  }
1750 #endif
1751 
1752 // no option -- return list of available PLplot commands
1753 
1754  if ( argc == 0 )
1755  return plTclCmd( cmdlist, interp, argc, argv );
1756 
1757 // Make sure widget has been initialized before going any further
1758 
1759  if ( !plPlotterPtr->tkwin_initted )
1760  {
1761  Tcl_VarEval( plPlotterPtr->interp, "update", (char *) NULL );
1762  }
1763 
1764 // Set stream number and get ready to process the command
1765 
1766  plsstrm( plPlotterPtr->ipls );
1767 
1768  c3 = argv[0][2];
1769  length = (int) strlen( argv[0] );
1770 
1771 // plgcmap0 -- get color map 0
1772 // first arg is number of colors, the rest are hex number specifications
1773 
1774  if ( ( c3 == 'g' ) && ( strncmp( argv[0], "plgcmap0", (size_t) length ) == 0 ) )
1775  {
1776  int i;
1777  unsigned long plcolor;
1778  char str[10];
1779 
1780  sprintf( str, "%d", (int) pls->ncol0 );
1781  Tcl_AppendElement( interp, str );
1782  for ( i = 0; i < pls->ncol0; i++ )
1783  {
1784  plcolor = (unsigned long) ( ( pls->cmap0[i].r << 16 ) |
1785  ( pls->cmap0[i].g << 8 ) |
1786  ( pls->cmap0[i].b ) );
1787 
1788  sprintf( str, "#%06lx", ( plcolor & 0xFFFFFF ) );
1789  Tcl_AppendElement( interp, str );
1790  }
1791  result = TCL_OK;
1792  }
1793 
1794 // plgcmap1 -- get color map 1
1795 // first arg is number of control points
1796 // the rest are hex number specifications followed by positions (0-100)
1797 
1798  else if ( ( c3 == 'g' ) && ( strncmp( argv[0], "plgcmap1", (size_t) length ) == 0 ) )
1799  {
1800  int i;
1801  unsigned long plcolor;
1802  char str[10];
1803  PLFLT h, l, s, r, g, b;
1804  int r1, g1, b1;
1805 
1806  sprintf( str, "%d", (int) pls->ncp1 );
1807  Tcl_AppendElement( interp, str );
1808  for ( i = 0; i < pls->ncp1; i++ )
1809  {
1810  h = pls->cmap1cp[i].h;
1811  l = pls->cmap1cp[i].l;
1812  s = pls->cmap1cp[i].s;
1813 
1814  plhlsrgb( h, l, s, &r, &g, &b );
1815 
1816  r1 = MAX( 0, MIN( 255, (int) ( 256. * r ) ) );
1817  g1 = MAX( 0, MIN( 255, (int) ( 256. * g ) ) );
1818  b1 = MAX( 0, MIN( 255, (int) ( 256. * b ) ) );
1819 
1820  plcolor = (unsigned long) ( ( r1 << 16 ) | ( g1 << 8 ) | ( b1 ) );
1821 
1822  sprintf( str, "#%06lx", ( plcolor & 0xFFFFFF ) );
1823  Tcl_AppendElement( interp, str );
1824 
1825  sprintf( str, "%02d", (int) ( 100 * pls->cmap1cp[i].p ) );
1826  Tcl_AppendElement( interp, str );
1827 
1828  sprintf( str, "%01d", (int) ( pls->cmap1cp[i].alt_hue_path ) );
1829  Tcl_AppendElement( interp, str );
1830  }
1831  result = TCL_OK;
1832  }
1833 
1834 // plscmap0 -- set color map 0
1835 // first arg is number of colors, the rest are hex number specifications
1836 
1837  else if ( ( c3 == 's' ) && ( strncmp( argv[0], "plscmap0", (size_t) length ) == 0 ) )
1838  {
1839  int i, changed = 1, ncol0 = atoi( argv[1] );
1840  char *col;
1841 
1842  if ( ncol0 > 16 || ncol0 < 1 )
1843  {
1844  Tcl_AppendResult( interp, "illegal number of colors in cmap0: ",
1845  argv[1], (char *) NULL );
1846  return TCL_ERROR;
1847  }
1848 
1849  pls->ncol0 = ncol0;
1850  for ( i = 0; i < pls->ncol0; i++ )
1851  {
1852  argv_cp = plstrdup( argv[2 + i] );
1853  col = strtok( argv_cp, " " );
1854  if ( col == NULL )
1855  break;
1856 
1857  if ( scol0( interp, plPlotterPtr, i, col, &changed ) != TCL_OK )
1858  return TCL_ERROR;
1859  free_mem( argv_cp );
1860  }
1861 
1862  if ( changed )
1864  }
1865 
1866 // plscmap1 -- set color map 1
1867 // first arg is number of colors, the rest are hex number specifications
1868 
1869  else if ( ( c3 == 's' ) && ( strncmp( argv[0], "plscmap1", (size_t) length ) == 0 ) )
1870  {
1871  int i, changed = 1, ncp1 = atoi( argv[1] );
1872  char *col, *pos, *rev;
1873 
1874  if ( ncp1 > 32 || ncp1 < 1 )
1875  {
1876  Tcl_AppendResult( interp,
1877  "illegal number of control points in cmap1: ",
1878  argv[1], (char *) NULL );
1879  return TCL_ERROR;
1880  }
1881 
1882  argv_cp = plstrdup( argv[2] );
1883  col = strtok( argv_cp, " " );
1884  pos = strtok( NULL, " " );
1885  rev = strtok( NULL, " " );
1886  for ( i = 0; i < ncp1; i++ )
1887  {
1888  if ( col == NULL )
1889  break;
1890 
1891  if ( scol1( interp, plPlotterPtr,
1892  i, col, pos, rev, &changed ) != TCL_OK )
1893  return TCL_ERROR;
1894 
1895  col = strtok( NULL, " " );
1896  pos = strtok( NULL, " " );
1897  rev = strtok( NULL, " " );
1898  }
1899 
1900  free_mem( argv_cp );
1901 
1902  if ( changed )
1903  {
1904  plsc->ncp1 = ncp1;
1905  plcmap1_calc();
1906  }
1907  }
1908 
1909 // plscol0 -- set single color in cmap0
1910 // first arg is the color number, the next is the color in hex
1911 
1912  else if ( ( c3 == 's' ) && ( strncmp( argv[0], "plscol0", (size_t) length ) == 0 ) )
1913  {
1914  int i = atoi( argv[1] ), changed = 1;
1915 
1916  if ( i > pls->ncol0 || i < 0 )
1917  {
1918  Tcl_AppendResult( interp, "illegal color number in cmap0: ",
1919  argv[1], (char *) NULL );
1920  return TCL_ERROR;
1921  }
1922 
1923  if ( scol0( interp, plPlotterPtr, i, argv[2], &changed ) != TCL_OK )
1924  return TCL_ERROR;
1925 
1926  if ( changed )
1928  }
1929 
1930 // plscol1 -- set color of control point in cmap1
1931 // first arg is the control point, the next two are the color in hex and pos
1932 
1933  else if ( ( c3 == 's' ) && ( strncmp( argv[0], "plscol1", (size_t) length ) == 0 ) )
1934  {
1935  int i = atoi( argv[1] ), changed = 1;
1936 
1937  if ( i > pls->ncp1 || i < 0 )
1938  {
1939  Tcl_AppendResult( interp, "illegal control point number in cmap1: ",
1940  argv[1], (char *) NULL );
1941  return TCL_ERROR;
1942  }
1943 
1944  if ( scol1( interp, plPlotterPtr,
1945  i, argv[2], argv[3], argv[4], &changed ) != TCL_OK )
1946  return TCL_ERROR;
1947 
1948  if ( changed )
1949  plcmap1_calc();
1950  }
1951 
1952 // Added by Vince, disabled by default since we want a minimal patch
1953 #ifdef USING_PLESC_COPY
1954 // plcopy -- copy a region of the plot; useful for scrolling plots
1955 // first 4 args are the source rectangle, next 2 args are the destination
1956 
1957  else if ( ( c3 == 'c' ) && ( strncmp( argv[0], "plcopy", (size_t) length ) == 0 ) )
1958  {
1959  PLFLT xx[3], yy[3];
1960  if ( argc != 7 )
1961  {
1962  Tcl_AppendResult( interp, "Need exactly 6 arguments to copy.",
1963  (char *) NULL );
1964  return TCL_ERROR;
1965  }
1966  xx[0] = atof( argv[1] );
1967  yy[0] = atof( argv[2] );
1968  xx[1] = atof( argv[3] );
1969  yy[1] = atof( argv[4] );
1970  xx[2] = atof( argv[5] );
1971  yy[2] = atof( argv[6] );
1972  plcopy( xx, yy );
1973  }
1974 #endif
1975 
1976 // unrecognized, so give it to plTclCmd to take care of
1977 
1978  else
1979  result = plTclCmd( cmdlist, interp, argc, argv );
1980 
1981  plflush();
1982  return result;
1983 }
1984 
1985 static void ActiveState( register PlPlotter *plPlotterPtr, int on )
1986 {
1987  if ( on )
1988  {
1989  if ( !plPlotterPtr->isActive )
1990  {
1991  Tk_CreateEventHandler( plPlotterPtr->tkwin, ButtonPressMask,
1992  PlPlotterButtonPressEH, (ClientData) plPlotterPtr );
1993 
1994  Tk_CreateEventHandler( plPlotterPtr->tkwin, PointerMotionMask,
1995  PlPlotterMotionEH, (ClientData) plPlotterPtr );
1996 
1997  Tk_CreateEventHandler( plPlotterPtr->tkwin, EnterWindowMask,
1998  PlPlotterEnterEH, (ClientData) plPlotterPtr );
1999 
2000  Tk_CreateEventHandler( plPlotterPtr->tkwin, LeaveWindowMask,
2001  PlPlotterLeaveEH, (ClientData) plPlotterPtr );
2002  // Switch to crosshair cursor.
2003 
2004  Tk_DefineCursor( plPlotterPtr->tkwin, plPlotterPtr->xhair_cursor );
2005  }
2006  }
2007  else
2008  {
2009  if ( plPlotterPtr->isActive )
2010  {
2011  Tk_DeleteEventHandler( plPlotterPtr->tkwin, ButtonPressMask,
2012  PlPlotterButtonPressEH, (ClientData) plPlotterPtr );
2013  Tk_DeleteEventHandler( plPlotterPtr->tkwin, PointerMotionMask,
2014  PlPlotterMotionEH, (ClientData) plPlotterPtr );
2015 
2016  Tk_DeleteEventHandler( plPlotterPtr->tkwin, EnterWindowMask,
2017  PlPlotterEnterEH, (ClientData) plPlotterPtr );
2018 
2019  Tk_DeleteEventHandler( plPlotterPtr->tkwin, LeaveWindowMask,
2020  PlPlotterLeaveEH, (ClientData) plPlotterPtr );
2021  // Switch back to boring old pointer
2022 
2023  Tk_DefineCursor( plPlotterPtr->tkwin, plPlotterPtr->cursor );
2024  }
2025  }
2026 }
2027 
2028 
2029 //
2030 //--------------------------------------------------------------------------
2031 //
2032 // ConfigurePlPlotter --
2033 //
2034 // This procedure is called to process an argv/argc list, plus the Tk
2035 // option database, in order to configure (or reconfigure) a
2036 // plframe widget.
2037 //
2038 // Results:
2039 // The return value is a standard Tcl result. If TCL_ERROR is
2040 // returned, then interp->result contains an error message.
2041 //
2042 // Side effects:
2043 // Configuration information, such as text string, colors, font, etc.
2044 // get set for plPlotterPtr; old resources get freed, if there were
2045 // any.
2046 //
2047 //--------------------------------------------------------------------------
2048 //
2049 
2050 static int
2051 ConfigurePlPlotter( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2052  int argc, CONST char **argv, int flags )
2053 {
2054  register Tk_Window tkwin = plPlotterPtr->tkwin;
2055  PLStream *pls = plPlotterPtr->pls;
2056  TkwDev *dev = (TkwDev *) pls->dev;
2057  TkwDisplay *tkwd = (TkwDisplay *) dev->tkwd;
2058  XGCValues gcValues;
2059  unsigned long mask;
2060  int need_redisplay = 0;
2061 
2062 #ifdef DEBUG
2063  if ( pls->debug )
2064  {
2065  int i;
2066  fprintf( stderr, "Arguments to configure are:" );
2067  for ( i = 0; i < argc; i++ )
2068  {
2069  fprintf( stderr, " %s", argv[i] );
2070  }
2071  fprintf( stderr, "\n" );
2072  }
2073 #endif
2074 
2075  dbug_enter( "ConfigurePlPlotter" );
2076 
2077  if ( Tk_ConfigureWidget( interp, tkwin, configSpecs,
2078  argc, argv, (char *) plPlotterPtr, flags ) != TCL_OK )
2079  {
2080  return TCL_ERROR;
2081  }
2082 
2083 //
2084 // Set background color using tkwin driver's pixel value. Done this way so
2085 // that (a) we can use r/w color cells, and (b) the BG pixel values as set
2086 // here and in the tkwin driver are consistent.
2087 //
2088 
2089  plsstrm( plPlotterPtr->ipls );
2090  if ( PLColor_from_TkColor_Changed( &pls->cmap0[0],
2091  Tk_3DBorderColor( plPlotterPtr->border ) ) )
2092  {
2093  need_redisplay = 1;
2094  // need to redraw as well as simply refresh the window
2095  plPlotterPtr->flags |= REDRAW_PENDING;
2096  }
2097  pltkwin_setBGFG( pls );
2098 
2099  Tk_SetWindowBackground( tkwin, tkwd->cmap0[0].pixel );
2100  Tk_SetWindowBorder( tkwin, tkwd->cmap0[0].pixel );
2101 
2102  // Set up GC for rubber-band draws
2103 
2104  gcValues.background = tkwd->cmap0[0].pixel;
2105  gcValues.foreground = 0xFF;
2106  gcValues.function = GXxor;
2107  mask = GCForeground | GCBackground | GCFunction;
2108 
2109  if ( plPlotterPtr->xorGC != NULL )
2110  Tk_FreeGC( plPlotterPtr->display, plPlotterPtr->xorGC );
2111 
2112  plPlotterPtr->xorGC = Tk_GetGC( plPlotterPtr->tkwin, mask, &gcValues );
2113 
2114 // Geometry settings
2115 
2116  Tk_SetInternalBorder( tkwin, plPlotterPtr->borderWidth );
2117  if ( ( plPlotterPtr->width > 0 ) || ( plPlotterPtr->height > 0 ) )
2118  {
2119  Tk_GeometryRequest( tkwin, plPlotterPtr->width, plPlotterPtr->height );
2120  if ( ( plPlotterPtr->width != plPlotterPtr->prevWidth ) ||
2121  ( plPlotterPtr->height != plPlotterPtr->prevHeight ) )
2122  need_redisplay = 1;
2123  }
2124 
2125 // Create or destroy graphic crosshairs as specified
2126 
2127  if ( Tk_IsMapped( tkwin ) )
2128  {
2129  if ( plPlotterPtr->xhairs )
2130  {
2131  if ( !plPlotterPtr->drawing_xhairs )
2132  CreateXhairs( plPlotterPtr );
2133  }
2134  else
2135  {
2136  if ( plPlotterPtr->drawing_xhairs )
2137  DestroyXhairs( plPlotterPtr );
2138  }
2139  }
2140 
2141 // Create or destroy rubber band as specified
2142 
2143  if ( Tk_IsMapped( tkwin ) )
2144  {
2145  if ( plPlotterPtr->rband )
2146  {
2147  if ( !plPlotterPtr->drawing_rband )
2148  CreateRband( plPlotterPtr );
2149  }
2150  else
2151  {
2152  if ( plPlotterPtr->drawing_rband )
2153  DestroyRband( plPlotterPtr );
2154  }
2155  }
2156 // Sets or clears events for the plot
2157  ActiveState( plPlotterPtr, plPlotterPtr->active_plot );
2158 
2159  if ( !pls->nopixmap )
2160  {
2161  // can only adjust if this flag not set
2162  if ( plPlotterPtr->double_buffer != pls->db )
2163  {
2164  pls->db = plPlotterPtr->double_buffer;
2165  // turn on/off dbl-buffering in the driver
2166  dev->write_to_window = !pls->db;
2167  }
2168  }
2169  else
2170  {
2171  plPlotterPtr->double_buffer = 0;
2172  }
2173 
2174 // Arrange for window to be refreshed if necessary
2175  if ( need_redisplay && Tk_IsMapped( tkwin )
2176  && !( plPlotterPtr->flags & REFRESH_PENDING ) )
2177  {
2178  Tcl_DoWhenIdle( DisplayPlPlotter, (ClientData) plPlotterPtr );
2179  plPlotterPtr->flags |= REFRESH_PENDING;
2180  plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR;
2181  }
2182 
2183  return TCL_OK;
2184 }
2185 
2186 //--------------------------------------------------------------------------
2187 // Draw
2188 //
2189 // Processes "draw" widget command.
2190 // Handles rubber-band drawing.
2191 //--------------------------------------------------------------------------
2192 
2193 static int
2194 Draw( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2195  int argc, CONST char **argv )
2196 {
2197  register Tk_Window tkwin = plPlotterPtr->tkwin;
2198  int result = TCL_OK;
2199  char c = argv[0][0];
2200  int length = (int) strlen( argv[0] );
2201 
2202 // Make sure widget has been initialized before going any further
2203 
2204  if ( !plPlotterPtr->tkwin_initted )
2205  {
2206  Tcl_VarEval( plPlotterPtr->interp, "update", (char *) NULL );
2207  }
2208 
2209 // init -- sets up for rubber-band drawing
2210 
2211  if ( ( c == 'i' ) && ( strncmp( argv[0], "init", (size_t) length ) == 0 ) )
2212  {
2213  Tk_DefineCursor( tkwin, plPlotterPtr->xhair_cursor );
2214  }
2215 
2216 // end -- ends rubber-band drawing
2217 
2218  else if ( ( c == 'e' ) && ( strncmp( argv[0], "end", (size_t) length ) == 0 ) )
2219  {
2220  Tk_DefineCursor( tkwin, plPlotterPtr->cursor );
2221  if ( plPlotterPtr->continue_draw )
2222  {
2223  XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ),
2224  plPlotterPtr->xorGC, plPlotterPtr->pts, 5,
2225  CoordModeOrigin );
2226  XSync( Tk_Display( tkwin ), 0 );
2227  }
2228 
2229  plPlotterPtr->continue_draw = 0;
2230  }
2231 
2232 // rect -- draw a rectangle, used to select rectangular areas
2233 // first draw erases old outline
2234 
2235  else if ( ( c == 'r' ) && ( strncmp( argv[0], "rect", (size_t) length ) == 0 ) )
2236  {
2237  if ( argc < 5 )
2238  {
2239  Tcl_AppendResult( interp, "wrong # args: should be \"",
2240  " draw rect x0 y0 x1 y1\"", (char *) NULL );
2241  result = TCL_ERROR;
2242  }
2243  else
2244  {
2245  int x0, y0, x1, y1;
2246  int xmin = 0, xmax = Tk_Width( tkwin ) - 1;
2247  int ymin = 0, ymax = Tk_Height( tkwin ) - 1;
2248 
2249  x0 = atoi( argv[1] );
2250  y0 = atoi( argv[2] );
2251  x1 = atoi( argv[3] );
2252  y1 = atoi( argv[4] );
2253 
2254  x0 = MAX( xmin, MIN( xmax, x0 ) );
2255  y0 = MAX( ymin, MIN( ymax, y0 ) );
2256  x1 = MAX( xmin, MIN( xmax, x1 ) );
2257  y1 = MAX( ymin, MIN( ymax, y1 ) );
2258 
2259  if ( plPlotterPtr->continue_draw )
2260  {
2261  XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ),
2262  plPlotterPtr->xorGC, plPlotterPtr->pts, 5,
2263  CoordModeOrigin );
2264  XSync( Tk_Display( tkwin ), 0 );
2265  }
2266 
2267  plPlotterPtr->pts[0].x = (short) x0; plPlotterPtr->pts[0].y = (short) y0;
2268  plPlotterPtr->pts[1].x = (short) x1; plPlotterPtr->pts[1].y = (short) y0;
2269  plPlotterPtr->pts[2].x = (short) x1; plPlotterPtr->pts[2].y = (short) y1;
2270  plPlotterPtr->pts[3].x = (short) x0; plPlotterPtr->pts[3].y = (short) y1;
2271  plPlotterPtr->pts[4].x = (short) x0; plPlotterPtr->pts[4].y = (short) y0;
2272 
2273  XDrawLines( Tk_Display( tkwin ), Tk_WindowId( tkwin ),
2274  plPlotterPtr->xorGC, plPlotterPtr->pts, 5,
2275  CoordModeOrigin );
2276  XSync( Tk_Display( tkwin ), 0 );
2277 
2278  plPlotterPtr->continue_draw = 1;
2279  }
2280  }
2281 
2282  return result;
2283 }
2284 
2285 //--------------------------------------------------------------------------
2286 // Info
2287 //
2288 // Processes "info" widget command.
2289 // Returns requested info.
2290 //--------------------------------------------------------------------------
2291 
2292 static int
2293 Info( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2294  int argc, CONST char **argv )
2295 {
2296  int length;
2297  char c;
2298  int result = TCL_OK;
2299 
2300 // no option -- return list of available info commands
2301 
2302  if ( argc == 0 )
2303  {
2304  Tcl_SetResult( interp, "devkeys devnames", TCL_STATIC );
2305  return TCL_OK;
2306  }
2307 
2308  c = argv[0][0];
2309  length = (int) strlen( argv[0] );
2310 
2311 // devkeys -- return list of supported device keywords
2312 
2313  if ( ( c == 'd' ) && ( strncmp( argv[0], "devkeys", (size_t) length ) == 0 ) )
2314  {
2315  int i = 0;
2316  while ( plPlotterPtr->devName[i] != NULL )
2317  Tcl_AppendElement( interp, plPlotterPtr->devName[i++] );
2318 
2319  result = TCL_OK;
2320  }
2321 
2322 // devkeys -- return list of supported device types
2323 
2324  else if ( ( c == 'd' ) && ( strncmp( argv[0], "devnames", (size_t) length ) == 0 ) )
2325  {
2326  int i = 0;
2327  while ( plPlotterPtr->devDesc[i] != NULL )
2328  Tcl_AppendElement( interp, plPlotterPtr->devDesc[i++] );
2329 
2330  result = TCL_OK;
2331  }
2332 
2333 // unrecognized
2334 
2335  else
2336  {
2337  Tcl_AppendResult( interp, "bad option to \"info\": must be ",
2338  "devkeys, devnames", (char *) NULL );
2339 
2340  result = TCL_ERROR;
2341  }
2342 
2343  return result;
2344 }
2345 
2346 //--------------------------------------------------------------------------
2347 // Openlink
2348 //
2349 // Processes "openlink" widget command.
2350 // Opens channel (FIFO or socket) for binary data transfer between client
2351 // and server.
2352 //--------------------------------------------------------------------------
2353 
2354 static int
2355 Openlink( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2356  int argc, CONST char **argv )
2357 {
2358 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
2359  register PLRDev *plr = plPlotterPtr->plr;
2360  register PLiodev *iodev = plr->iodev;
2361 
2362  char c = argv[0][0];
2363  int length = (int) strlen( argv[0] );
2364 
2365  dbug_enter( "Openlink" );
2366 
2367 // Open fifo
2368 
2369  if ( ( c == 'f' ) && ( strncmp( argv[0], "fifo", (size_t) length ) == 0 ) )
2370  {
2371  if ( argc < 1 )
2372  {
2373  Tcl_AppendResult( interp, "bad command -- must be: ",
2374  "openlink fifo <pathname>",
2375  (char *) NULL );
2376  return TCL_ERROR;
2377  }
2378  if ( ( iodev->fd = open( argv[1], O_RDONLY ) ) == -1 )
2379  {
2380  Tcl_AppendResult( interp, "cannot open fifo ", argv[1],
2381  " for read", (char *) NULL );
2382  return TCL_ERROR;
2383  }
2384  iodev->type = 0;
2385  iodev->typeName = "fifo";
2386  iodev->file = fdopen( iodev->fd, "rb" );
2387  }
2388 
2389 // Open socket
2390 
2391  else if ( ( c == 's' ) && ( strncmp( argv[0], "socket", (size_t) length ) == 0 ) )
2392  {
2393  if ( argc < 1 )
2394  {
2395  Tcl_AppendResult( interp, "bad command -- must be: ",
2396  "openlink socket <sock-id>",
2397  (char *) NULL );
2398  return TCL_ERROR;
2399  }
2400  iodev->type = 1;
2401  iodev->typeName = "socket";
2402  iodev->fileHandle = (char *) argv[1];
2403 
2404  if ( Tcl_GetOpenFile( interp, iodev->fileHandle,
2405  0, 1, ( ClientData ) & iodev->file ) != TCL_OK )
2406  {
2407  return TCL_ERROR;
2408  }
2409  iodev->fd = fileno( iodev->file );
2410  }
2411 
2412 // unrecognized
2413 
2414  else
2415  {
2416  Tcl_AppendResult( interp, "bad option to \"openlink\": must be ",
2417  "fifo or socket", (char *) NULL );
2418 
2419  return TCL_ERROR;
2420  }
2421 
2422  plr->pdfs = pdf_bopen( NULL, 4200 );
2423  Tcl_CreateFileHandler( iodev->fd,
2424  TK_READABLE, (Tk_FileProc *) ReadData,
2425  (ClientData) plPlotterPtr );
2426 
2427 #endif
2428 
2429  return TCL_OK;
2430 }
2431 
2432 //--------------------------------------------------------------------------
2433 // Closelink
2434 //
2435 // Processes "closelink" widget command.
2436 // CLoses channel previously opened with the "openlink" widget command.
2437 //--------------------------------------------------------------------------
2438 
2439 static int
2440 Closelink( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2441  int PL_UNUSED( argc ), CONST char ** PL_UNUSED( argv ) )
2442 {
2443 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
2444  register PLRDev *plr = plPlotterPtr->plr;
2445  register PLiodev *iodev = plr->iodev;
2446 
2447  dbug_enter( "Closelink" );
2448 
2449  if ( iodev->fd == 0 )
2450  {
2451  Tcl_AppendResult( interp, "no link currently open", (char *) NULL );
2452  return TCL_ERROR;
2453  }
2454 
2455  Tcl_DeleteFileHandler( iodev->fd );
2456  pdf_close( plr->pdfs );
2457  iodev->fd = 0;
2458 #endif
2459  return TCL_OK;
2460 }
2461 
2462 //--------------------------------------------------------------------------
2463 // process_data
2464 //
2465 // Utility function for processing data and other housekeeping.
2466 //--------------------------------------------------------------------------
2467 
2468 static int
2469 process_data( Tcl_Interp *interp, register PlPlotter *plPlotterPtr )
2470 {
2471  register PLRDev *plr = plPlotterPtr->plr;
2472  register PLiodev *iodev = plr->iodev;
2473  int result = TCL_OK;
2474 
2475 // Process data
2476 
2477  if ( plr_process( plr ) == -1 )
2478  {
2479  Tcl_AppendResult( interp, "unable to read from ", iodev->typeName,
2480  (char *) NULL );
2481  result = TCL_ERROR;
2482  }
2483 
2484 // Signal bop if necessary
2485 
2486  if ( plr->at_bop && plPlotterPtr->bopCmd != NULL )
2487  {
2488  plr->at_bop = 0;
2489  if ( Tcl_Eval( interp, plPlotterPtr->bopCmd ) != TCL_OK )
2490  fprintf( stderr, "Command \"%s\" failed:\n\t %s\n",
2491  plPlotterPtr->bopCmd, Tcl_GetStringResult( interp ) );
2492  }
2493 
2494 // Signal eop if necessary
2495 
2496  if ( plr->at_eop && plPlotterPtr->eopCmd != NULL )
2497  {
2498  plr->at_eop = 0;
2499  if ( Tcl_Eval( interp, plPlotterPtr->eopCmd ) != TCL_OK )
2500  fprintf( stderr, "Command \"%s\" failed:\n\t %s\n",
2501  plPlotterPtr->eopCmd, Tcl_GetStringResult( interp ) );
2502  }
2503 
2504  return result;
2505 }
2506 
2507 void PlplotterAtEop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr )
2508 {
2509  if ( plPlotterPtr->eopCmd != NULL )
2510  {
2511  if ( Tcl_Eval( interp, plPlotterPtr->eopCmd ) != TCL_OK )
2512  fprintf( stderr, "Command \"%s\" failed:\n\t %s\n",
2513  plPlotterPtr->eopCmd, Tcl_GetStringResult( interp ) );
2514  }
2515 }
2516 
2517 void PlplotterAtBop( Tcl_Interp *interp, register PlPlotter *plPlotterPtr )
2518 {
2519  if ( plPlotterPtr->bopCmd != NULL )
2520  {
2521  if ( Tcl_Eval( interp, plPlotterPtr->bopCmd ) != TCL_OK )
2522  fprintf( stderr, "Command \"%s\" failed:\n\t %s\n",
2523  plPlotterPtr->bopCmd, Tcl_GetStringResult( interp ) );
2524  }
2525 }
2526 
2527 //--------------------------------------------------------------------------
2528 // ReadData
2529 //
2530 // Reads & processes data.
2531 // Intended to be installed as a filehandler command.
2532 //--------------------------------------------------------------------------
2533 
2534 static int
2535 ReadData( ClientData clientData, int mask )
2536 {
2537  register PlPlotter *plPlotterPtr = (PlPlotter *) clientData;
2538  register Tcl_Interp *interp = plPlotterPtr->interp;
2539 
2540  register PLRDev *plr = plPlotterPtr->plr;
2541  register PLiodev *iodev = plr->iodev;
2542  register PDFstrm *pdfs = plr->pdfs;
2543  int result = TCL_OK;
2544 
2545  if ( mask & TK_READABLE )
2546  {
2547  // Read from FIFO or socket
2548 
2549  plsstrm( plPlotterPtr->ipls );
2550  #ifndef MAC_TCL
2551  if ( pl_PacketReceive( interp, iodev, pdfs ) )
2552  {
2553  #else
2554  if ( 1 )
2555  {
2556  #endif
2557  Tcl_AppendResult( interp, "Packet receive failed:\n\t %s\n",
2558  Tcl_GetStringResult( interp ), (char *) NULL );
2559  return TCL_ERROR;
2560  }
2561 
2562  // If the packet isn't complete it will be put back and we just return.
2563  // Otherwise, the buffer pointer is saved and then cleared so that reads
2564  // from the buffer start at the beginning.
2565  //
2566  if ( pdfs->bp == 0 )
2567  return TCL_OK;
2568 
2569  plr->nbytes = (int) pdfs->bp;
2570  pdfs->bp = 0;
2571  result = process_data( interp, plPlotterPtr );
2572  }
2573 
2574  return result;
2575 }
2576 
2577 //--------------------------------------------------------------------------
2578 // Orient
2579 //
2580 // Processes "orient" widget command.
2581 // Handles orientation of plot.
2582 //--------------------------------------------------------------------------
2583 
2584 static int
2585 Orient( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2586  int argc, CONST char **argv )
2587 {
2588  int result = TCL_OK;
2589 
2590 // orient -- return orientation of current plot window
2591 
2592  plsstrm( plPlotterPtr->ipls );
2593 
2594  if ( argc == 0 )
2595  {
2596  PLFLT rot;
2597  char result_str[128];
2598  plgdiori( &rot );
2599  sprintf( result_str, "%f", rot );
2600  Tcl_SetResult( interp, result_str, TCL_VOLATILE );
2601  }
2602 
2603 // orient <rot> -- Set orientation to <rot>
2604 
2605  else
2606  {
2607  plsdiori( (PLFLT) atof( argv[0] ) );
2608  result = Redraw( interp, plPlotterPtr, argc - 1, argv + 1 );
2609  }
2610 
2611  return result;
2612 }
2613 
2614 //--------------------------------------------------------------------------
2615 // Print
2616 //
2617 // Processes "print" widget command.
2618 // Handles printing of plot, duh.
2619 //
2620 // Creates a temporary file, dumps the current plot to it in metafile
2621 // form, and then execs the "plpr" script to actually print it. Since we
2622 // output it in metafile form here, plpr must invoke plrender to drive the
2623 // output to the appropriate file type. The script is responsible for the
2624 // deletion of the plot metafile.
2625 //--------------------------------------------------------------------------
2626 
2627 static int
2628 Print( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2629  int PL_UNUSED( argc ), CONST char ** PL_UNUSED( argv ) )
2630 {
2631  PLINT ipls;
2632  int result = TCL_OK;
2633  char *sfnam;
2634  FILE *sfile;
2635 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
2636  pid_t pid;
2637 #endif
2638 
2639 // Make sure widget has been initialized before going any further
2640 
2641  if ( !plPlotterPtr->tkwin_initted )
2642  {
2643  Tcl_AppendResult( interp, "Error -- widget not plotted to yet",
2644  (char *) NULL );
2645  return TCL_ERROR;
2646  }
2647 
2648 // Create stream for save
2649 
2650  plmkstrm( &ipls );
2651  if ( ipls < 0 )
2652  {
2653  Tcl_AppendResult( interp, "Error -- cannot create stream",
2654  (char *) NULL );
2655  return TCL_ERROR;
2656  }
2657 
2658 // Open file for writes
2659 
2660  // Create and open temporary file
2661  // NB use fdopen to get a file stream from the existing file handle
2662  if ( ( sfile = pl_create_tempfile( &sfnam ) ) == NULL )
2663  {
2664  Tcl_AppendResult( interp,
2665  "Error -- cannot open plot file for writing",
2666  (char *) NULL );
2667  plend1();
2668  if ( sfnam != NULL )
2669  free( sfnam );
2670  return TCL_ERROR;
2671  }
2672 
2673 // Initialize stream
2674 
2675  plsdev( "plmeta" );
2676  plsfile( sfile );
2677  plcpstrm( plPlotterPtr->ipls, 0 );
2678  pladv( 0 );
2679 
2680 // Remake current plot, close file, and switch back to original stream
2681 
2682  plreplot();
2683  plend1();
2684  plsstrm( plPlotterPtr->ipls );
2685 
2686 // So far so good. Time to exec the print script.
2687 
2688  if ( plPlotterPtr->plpr_cmd == NULL )
2689  plPlotterPtr->plpr_cmd = plFindCommand( "plpr" );
2690 
2691 #if !defined ( MAC_TCL ) && !defined ( __WIN32__ )
2692  if ( ( plPlotterPtr->plpr_cmd == NULL ) || ( pid = fork() ) < 0 )
2693  {
2694  Tcl_AppendResult( interp,
2695  "Error -- cannot fork print process",
2696  (char *) NULL );
2697  result = TCL_ERROR;
2698  }
2699  else if ( pid == 0 )
2700  {
2701  if ( execl( plPlotterPtr->plpr_cmd, plPlotterPtr->plpr_cmd, sfnam,
2702  (char *) 0 ) )
2703  {
2704  fprintf( stderr, "Unable to exec print command.\n" );
2705  free( sfnam );
2706  _exit( 1 );
2707  }
2708  }
2709 #endif
2710  free( sfnam );
2711  return result;
2712 }
2713 
2714 //--------------------------------------------------------------------------
2715 // NextPage
2716 //
2717 // Tells the tkwin driver to move along to the next page.
2718 //--------------------------------------------------------------------------
2719 
2720 static int
2721 NextPage( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2722  int argc, CONST char ** PL_UNUSED( argv ) )
2723 {
2724  TkwDev *dev = (TkwDev *) plPlotterPtr->pls->dev;
2725  if ( argc == 0 )
2726  {
2727  dev->flags |= 2;
2728  }
2729  else
2730  {
2731  Tcl_AppendResult( interp, "wrong # args: should be \"",
2732  " nextpage\"", (char *) NULL );
2733  return TCL_ERROR;
2734  }
2735  return TCL_OK;
2736 }
2737 
2738 //--------------------------------------------------------------------------
2739 // Page
2740 //
2741 // Processes "page" widget command.
2742 // Handles parameters such as margin, aspect ratio, and justification
2743 // of final plot.
2744 //--------------------------------------------------------------------------
2745 
2746 static int
2747 Page( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2748  int argc, CONST char **argv )
2749 {
2750 // page -- return current device window parameters
2751 
2752  plsstrm( plPlotterPtr->ipls );
2753 
2754  if ( argc == 0 )
2755  {
2756  PLFLT mar, aspect, jx, jy;
2757  char result_str[128];
2758 
2759  plgdidev( &mar, &aspect, &jx, &jy );
2760  sprintf( result_str, "%g %g %g %g", mar, aspect, jx, jy );
2761  Tcl_SetResult( interp, result_str, TCL_VOLATILE );
2762  return TCL_OK;
2763  }
2764 
2765 // page <mar> <aspect> <jx> <jy> -- set up page
2766 
2767  if ( argc < 4 )
2768  {
2769  Tcl_AppendResult( interp, "wrong # args: should be \"",
2770  " page mar aspect jx jy\"", (char *) NULL );
2771  return TCL_ERROR;
2772  }
2773 
2774  plsdidev( (PLFLT) atof( argv[0] ), (PLFLT) atof( argv[1] ),
2775  (PLFLT) atof( argv[2] ), (PLFLT) atof( argv[3] ) );
2776  return ( Redraw( interp, plPlotterPtr, argc - 1, argv + 1 ) );
2777 }
2778 
2779 //--------------------------------------------------------------------------
2780 // Redraw
2781 //
2782 // Processes "redraw" widget command.
2783 // Turns loose a DoWhenIdle command to redraw plot by replaying contents
2784 // of plot buffer.
2785 //--------------------------------------------------------------------------
2786 
2787 static int
2788 Redraw( Tcl_Interp * PL_UNUSED( interp ), register PlPlotter *plPlotterPtr,
2789  int PL_UNUSED( argc ), CONST char ** PL_UNUSED( argv ) )
2790 {
2791  dbug_enter( "Redraw" );
2792 
2793  plPlotterPtr->flags |= REDRAW_PENDING;
2794  if ( ( plPlotterPtr->tkwin != NULL ) &&
2795  !( plPlotterPtr->flags & REFRESH_PENDING ) )
2796  {
2797  Tcl_DoWhenIdle( DisplayPlPlotter, (ClientData) plPlotterPtr );
2798  plPlotterPtr->flags |= REFRESH_PENDING;
2799  }
2800 
2801  return TCL_OK;
2802 }
2803 
2804 //--------------------------------------------------------------------------
2805 // Save
2806 //
2807 // Processes "save" widget command.
2808 // Saves plot to a file.
2809 //--------------------------------------------------------------------------
2810 
2811 static int
2812 Save( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2813  int argc, CONST char **argv )
2814 {
2815  int length;
2816  char c;
2817  FILE *sfile;
2818 
2819 // Make sure widget has been initialized before going any further
2820 
2821  if ( !plPlotterPtr->tkwin_initted )
2822  {
2823  Tcl_AppendResult( interp, "Error -- widget not plotted to yet",
2824  (char *) NULL );
2825  return TCL_ERROR;
2826  }
2827 
2828 // save -- save to already open file
2829 
2830  if ( argc == 0 )
2831  {
2832  if ( !plPlotterPtr->ipls_save )
2833  {
2834  Tcl_AppendResult( interp, "Error -- no current save file",
2835  (char *) NULL );
2836  return TCL_ERROR;
2837  }
2838  plsstrm( plPlotterPtr->ipls_save );
2839  plcpstrm( plPlotterPtr->ipls, 0 );
2840  pladv( 0 );
2841  plreplot();
2842  plflush();
2843  plsstrm( plPlotterPtr->ipls );
2844  return TCL_OK;
2845  }
2846 
2847  c = argv[0][0];
2848  length = (int) strlen( argv[0] );
2849 
2850 // save to specified device & file
2851 
2852  if ( ( c == 'a' ) && ( strncmp( argv[0], "as", (size_t) length ) == 0 ) )
2853  {
2854  if ( argc < 3 )
2855  {
2856  Tcl_AppendResult( interp, "wrong # args: should be \"",
2857  " save as device file\"", (char *) NULL );
2858  return TCL_ERROR;
2859  }
2860 
2861  // If save previously in effect, delete old stream
2862 
2863  if ( plPlotterPtr->ipls_save )
2864  {
2865  plsstrm( plPlotterPtr->ipls_save );
2866  plend1();
2867  }
2868 
2869  // Create stream for saves to selected device & file
2870 
2871  plmkstrm( &plPlotterPtr->ipls_save );
2872  if ( plPlotterPtr->ipls_save < 0 )
2873  {
2874  Tcl_AppendResult( interp, "Error -- cannot create stream",
2875  (char *) NULL );
2876  plPlotterPtr->ipls_save = 0;
2877  return TCL_ERROR;
2878  }
2879 
2880  // Open file for writes
2881 
2882  if ( ( sfile = fopen( argv[2], "wb+" ) ) == NULL )
2883  {
2884  Tcl_AppendResult( interp, "Error -- cannot open file ", argv[2],
2885  " for writing", (char *) NULL );
2886  plPlotterPtr->ipls_save = 0;
2887  plend1();
2888  return TCL_ERROR;
2889  }
2890 
2891  // Initialize stream
2892 
2893  plsdev( argv[1] );
2894  plsfile( sfile );
2895  plcpstrm( plPlotterPtr->ipls, 0 );
2896  pladv( 0 );
2897 
2898  // Remake current plot and then switch back to original stream
2899 
2900  plreplot();
2901  plflush();
2902  plsstrm( plPlotterPtr->ipls );
2903  }
2904 
2905 // close save file
2906 
2907  else if ( ( c == 'c' ) && ( strncmp( argv[0], "close", (size_t) length ) == 0 ) )
2908  {
2909  if ( !plPlotterPtr->ipls_save )
2910  {
2911  Tcl_AppendResult( interp, "Error -- no current save file",
2912  (char *) NULL );
2913  return TCL_ERROR;
2914  }
2915  else
2916  {
2917  plsstrm( plPlotterPtr->ipls_save );
2918  plend1();
2919  plPlotterPtr->ipls_save = 0;
2920  }
2921  }
2922 
2923 // unrecognized
2924 
2925  else
2926  {
2927  Tcl_AppendResult( interp, "bad option to \"save\": must be ",
2928  "as or close", (char *) NULL );
2929 
2930  return TCL_ERROR;
2931  }
2932 
2933  return TCL_OK;
2934 }
2935 
2936 //--------------------------------------------------------------------------
2937 // View
2938 //
2939 // Processes "view" widget command.
2940 // Handles translation & scaling of view into plot.
2941 //--------------------------------------------------------------------------
2942 
2943 static int
2944 View( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
2945  int argc, CONST char **argv )
2946 {
2947  int length;
2948  int dontRedraw = 0;
2949  char c;
2950  PLFLT xl, xr, yl, yr;
2951 
2952 // view -- return current relative plot window coordinates
2953 
2954  plsstrm( plPlotterPtr->ipls );
2955 
2956  if ( argc == 0 )
2957  {
2958  char result_str[128];
2959  plgdiplt( &xl, &yl, &xr, &yr );
2960  sprintf( result_str, "%g %g %g %g", xl, yl, xr, yr );
2961  Tcl_SetResult( interp, result_str, TCL_VOLATILE );
2962  return TCL_OK;
2963  }
2964 
2965  c = argv[0][0];
2966  length = (int) strlen( argv[0] );
2967 
2968 // view bounds -- return relative device coordinates of bounds on current
2969 // plot window
2970 
2971  if ( ( c == 'b' ) && ( strncmp( argv[0], "bounds", (size_t) length ) == 0 ) )
2972  {
2973  char result_str[128];
2974  xl = 0.; yl = 0.;
2975  xr = 1.; yr = 1.;
2976  pldip2dc( &xl, &yl, &xr, &yr );
2977  sprintf( result_str, "%g %g %g %g", xl, yl, xr, yr );
2978  Tcl_SetResult( interp, result_str, TCL_VOLATILE );
2979  return TCL_OK;
2980  }
2981 
2982 // view reset -- Resets plot
2983 
2984  if ( ( c == 'r' ) && ( strncmp( argv[0], "reset", (size_t) length ) == 0 ) )
2985  {
2986  xl = 0.; yl = 0.;
2987  xr = 1.; yr = 1.;
2988  plsdiplt( xl, yl, xr, yr );
2989  if ( argc > 1 && ( strcmp( argv[1], "wait" ) == 0 ) )
2990  {
2991  // We're going to update in a while, so don't do it now
2992  dontRedraw = 1;
2993  }
2994  }
2995 
2996 // view select -- set window into plot space
2997 // Specifies in terms of plot window coordinates, not device coordinates
2998 
2999  else if ( ( c == 's' ) && ( strncmp( argv[0], "select", (size_t) length ) == 0 ) )
3000  {
3001  if ( argc < 5 )
3002  {
3003  Tcl_AppendResult( interp, "wrong # args: should be \"",
3004  " view select xmin ymin xmax ymax\"",
3005  (char *) NULL );
3006  return TCL_ERROR;
3007  }
3008  else
3009  {
3010  gbox( &xl, &yl, &xr, &yr, argv + 1 );
3011  plsdiplt( xl, yl, xr, yr );
3012  }
3013  }
3014 
3015 // view zoom -- set window into plot space incrementally (zoom)
3016 // Here we need to take the page (device) offsets into account
3017 
3018  else if ( ( c == 'z' ) && ( strncmp( argv[0], "zoom", (size_t) length ) == 0 ) )
3019  {
3020  if ( argc < 5 )
3021  {
3022  Tcl_AppendResult( interp, "wrong # args: should be \"",
3023  " view zoom xmin ymin xmax ymax\"",
3024  (char *) NULL );
3025  return TCL_ERROR;
3026  }
3027  else
3028  {
3029  gbox( &xl, &yl, &xr, &yr, argv + 1 );
3030  pldid2pc( &xl, &yl, &xr, &yr );
3031  plsdiplz( xl, yl, xr, yr );
3032  if ( argc > 5 && ( strcmp( argv[5], "wait" ) == 0 ) )
3033  {
3034  // We're going to update in a while, so don't do it now
3035  dontRedraw = 1;
3036  }
3037  }
3038  }
3039 
3040 // unrecognized
3041 
3042  else
3043  {
3044  Tcl_AppendResult( interp, "bad option \"", argv[1],
3045  "\": options to \"view\" are: bounds, reset, select, or zoom",
3046  (char *) NULL );
3047 
3048  return TCL_ERROR;
3049  }
3050 
3051 // Update plot window bounds and arrange for plot to be updated
3052 
3053  plgdiplt( &xl, &yl, &xr, &yr );
3054  plPlotterPtr->xl = xl;
3055  plPlotterPtr->yl = yl;
3056  plPlotterPtr->xr = xr;
3057  plPlotterPtr->yr = yr;
3058  plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR;
3059 
3060  if ( dontRedraw )
3061  {
3062  return TCL_OK;
3063  }
3064  else
3065  {
3066  return Redraw( interp, plPlotterPtr, argc, argv );
3067  }
3068 }
3069 
3070 //--------------------------------------------------------------------------
3071 // Scroll
3072 //
3073 // Processes "xview or yview" widget command.
3074 // Handles horizontal/vert scroll-bar invoked translation of view into plot.
3075 //--------------------------------------------------------------------------
3076 
3077 static int
3078 Scroll( Tcl_Interp *interp, register PlPlotter *plPlotterPtr )
3079 {
3080  plsstrm( plPlotterPtr->ipls );
3081  plsdiplt( plPlotterPtr->xl, plPlotterPtr->yl, plPlotterPtr->xr, plPlotterPtr->yr );
3082 
3083  plPlotterPtr->flags |= UPDATE_V_SCROLLBAR | UPDATE_H_SCROLLBAR;
3084  return ( Redraw( interp, plPlotterPtr, 0, NULL ) );
3085 }
3086 
3087 
3088 //--------------------------------------------------------------------------
3089 // report
3090 //
3091 // 4/17/95 GMF
3092 // Processes "report" widget command.
3093 //--------------------------------------------------------------------------
3094 
3095 static int
3096 report( Tcl_Interp *interp, register PlPlotter *plPlotterPtr,
3097  int argc, CONST char **argv )
3098 {
3099  PLFLT x, y;
3100  char res[40];
3101 // fprintf( stdout, "Made it into report, argc=%d\n", argc );
3102 
3103  if ( argc == 0 )
3104  {
3105  Tcl_SetResult( interp, "report what?", TCL_STATIC );
3106  return TCL_ERROR;
3107  }
3108 
3109  if ( !strcmp( argv[0], "wc" ) )
3110  {
3111  TkwDev *dev = (TkwDev *) plPlotterPtr->pls->dev;
3112  PLGraphicsIn *gin = &( dev->gin );
3113 
3114  if ( argc != 3 )
3115  {
3116  Tcl_SetResult( interp, "Wrong # of args: report wc x y", TCL_STATIC );
3117  return TCL_ERROR;
3118  }
3119 
3120  x = (PLFLT) atof( argv[1] );
3121  y = (PLFLT) atof( argv[2] );
3122 
3123  gin->dX = (PLFLT) x / ( dev->width - 1 );
3124  gin->dY = (PLFLT) 1.0 - (PLFLT) y / ( dev->height - 1 );
3125 
3126  // Try to locate cursor
3127 
3128  if ( plTranslateCursor( gin ) )
3129  {
3130  snprintf( res, 40, "%f %f", gin->wX, gin->wY );
3131  Tcl_SetResult( interp, res, TCL_VOLATILE );
3132  return TCL_OK;
3133  }
3134 
3135  Tcl_SetResult( interp, "Cannot locate", TCL_STATIC );
3136  return TCL_OK;
3137  }
3138 
3139  Tcl_SetResult( interp, "nonsensical request.", TCL_STATIC );
3140  return TCL_ERROR;
3141 }
3142 
3143 //--------------------------------------------------------------------------
3144 // Utility routines
3145 //--------------------------------------------------------------------------
3146 
3147 //--------------------------------------------------------------------------
3148 // UpdateVScrollbar
3149 //
3150 // Updates vertical scrollbar if needed.
3151 //--------------------------------------------------------------------------
3152 
3153 static void
3154 UpdateVScrollbar( register PlPlotter *plPlotterPtr )
3155 {
3156  char string[60];
3157  int result;
3158 
3159  if ( plPlotterPtr->yScrollCmd == NULL )
3160  return;
3161 
3162  sprintf( string, " %f %f", 1. - plPlotterPtr->yr, 1. - plPlotterPtr->yl );
3163 
3164  result = Tcl_VarEval( plPlotterPtr->interp, plPlotterPtr->yScrollCmd, string,
3165  (char *) NULL );
3166 
3167  if ( result != TCL_OK )
3168  {
3169  Tcl_BackgroundError( plPlotterPtr->interp );
3170  }
3171 }
3172 
3173 //--------------------------------------------------------------------------
3174 // UpdateHScrollbar
3175 //
3176 // Updates horizontal scrollbar if needed.
3177 //--------------------------------------------------------------------------
3178 
3179 static void
3180 UpdateHScrollbar( register PlPlotter *plPlotterPtr )
3181 {
3182  char string[60];
3183  int result;
3184 
3185  if ( plPlotterPtr->xScrollCmd == NULL )
3186  return;
3187 
3188  sprintf( string, " %f %f", plPlotterPtr->xl, plPlotterPtr->xr );
3189 
3190  result = Tcl_VarEval( plPlotterPtr->interp, plPlotterPtr->xScrollCmd, string,
3191  (char *) NULL );
3192 
3193  if ( result != TCL_OK )
3194  {
3195  Tcl_BackgroundError( plPlotterPtr->interp );
3196  }
3197 }
3198 
3199 //--------------------------------------------------------------------------
3200 // gbox
3201 //
3202 // Returns selection box coordinates. It's best if the TCL script does
3203 // bounds checking on the input but I do it here as well just to be safe.
3204 //--------------------------------------------------------------------------
3205 
3206 static void
3207 gbox( PLFLT *xl, PLFLT *yl, PLFLT *xr, PLFLT *yr, CONST char **argv )
3208 {
3209  PLFLT x0, y0, x1, y1;
3210 
3211  x0 = (PLFLT) atof( argv[0] );
3212  y0 = (PLFLT) atof( argv[1] );
3213  x1 = (PLFLT) atof( argv[2] );
3214  y1 = (PLFLT) atof( argv[3] );
3215 
3216  x0 = MAX( (PLFLT) 0., MIN( (PLFLT) 1., x0 ) );
3217  y0 = MAX( (PLFLT) 0., MIN( (PLFLT) 1., y0 ) );
3218  x1 = MAX( (PLFLT) 0., MIN( (PLFLT) 1., x1 ) );
3219  y1 = MAX( (PLFLT) 0., MIN( (PLFLT) 1., y1 ) );
3220 
3221 // Only need two vertices, pick the lower left and upper right
3222 
3223  *xl = MIN( x0, x1 );
3224  *yl = MIN( y0, y1 );
3225  *xr = MAX( x0, x1 );
3226  *yr = MAX( y0, y1 );
3227 }
3228 
3229 
3230 
3231 
3232 
3233 
3234 
3235 
3236 
3237 
3238 
3239 
3240 
3241 
3242