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