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