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