PLplot: plplotter.c Source File

PLplot 5.10.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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