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