1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 #include <X11/Xlib.h>
6 #include <X11/Xutil.h>
7 #include <X11/keysym.h>
8 
9 #ifdef USE_RIVET
10 #include "rvpgplot.h"  /* This includes rivet.h (which includes tcl.h & tk.h) */
11 #else
12 #include <tk.h>
13 #include "tkpgplot.h"  /* This includes tcl.h */
14 #endif
15 
16 #include "pgxwin.h"
17 
18 /*
19  * VAX VMS includes etc..
20  */
21 #ifdef VMS
22 #include <descrip.h>
23 #include <ssdef.h>
24 typedef struct dsc$descriptor_s VMS_string;
25 #define VMS_STRING(dsc, string) \
26   dsc.dsc$w_length = strlen(string); \
27   dsc.dsc$b_dtype = DSC$K_DTYPE_T; \
28   dsc.dsc$b_class = DSC$K_CLASS_S; \
29   dsc.dsc$a_pointer = string;
30 #endif
31 
32 /*
33  * Compose the pgplot-callable driver function name.
34  * Allow tkdriv to be calleable by FORTRAN using the two commonest
35  * calling conventions. Both conventions append length arguments for
36  * each FORTRAN string at the end of the argument list, and convert the
37  * name to lower-case, but one post-pends an underscore to the function
38  * name (PG_PPU) while the other doesn't. Note the VMS is handled
39  * separately below. For other calling conventions you must write a
40  * C wrapper routine to call tkdriv() or tkdriv_().
41  */
42 #ifdef PG_PPU
43 #ifdef RIVET
44 #define DRIV rvdriv_    /* Rivet with PG_PPU defined */
45 #else
46 #define DRIV tkdriv_    /* Normal Tk with PG_PPU defined */
47 #endif
48 #else
49 #ifdef RIVET
50 #define DRIV rvdriv     /* Rivet with PG_PPU undefined */
51 #else
52 #define DRIV tkdriv     /* Normal Tk with PG_PPU undefined */
53 #endif
54 #endif
55 
56 /*
57  * List widget defaults. Note that the macros that are prefixed
58  * TKPG_STR_ are for use in the configSpecs resource database. These
59  * have to be strings.
60  */
61 #define TKPG_MIN_WIDTH 64           /* Minimum width (pixels) */
62 
63 #define TKPG_MIN_HEIGHT 64          /* Minimum height (pixels) */
64 
65 #define TKPG_DEF_WIDTH 256          /* Default width (pixels) */
66 #define TKPG_STR_DEF_WIDTH "256"    /* String version of TKPG_DEF_WIDTH */
67 
68 #define TKPG_DEF_HEIGHT 256         /* Default height (pixels) */
69 #define TKPG_STR_DEF_HEIGHT "256"   /* String version of TKPG_DEF_HEIGHT */
70 
71 #define TKPG_MIN_COLORS 2           /* Min number of colors per colormap */
72 #define TKPG_STR_MIN_COLORS "2"     /* String version of TKPG_MIN_COLORS */
73 
74 #define TKPG_DEF_COLORS 100         /* Default number of colors to try for */
75 #define TKPG_STR_DEF_COLORS "100"   /* String version of TKPG_DEF_COLORS */
76 
77 #define TKPG_MAX_COLORS 255         /* Max number of colors per colormap */
78 
79 #define TKPG_DEF_HIGHLIGHT_WIDTH 2  /* Default width of traversal highlight */
80 #define TKPG_STR_DEF_HIGHLIGHT_WIDTH "2"/* String ver of TKPG_DEF_HIGHLIGHT_WIDTH */
81 #define TKPG_STR_MARGIN_DEF "20"    /* The default number of pixels of */
82                                     /*  extra space to allocate around the */
83                                     /*  edge of the plot area. */
84 
85 /*
86  * Specify the name to prefix errors with.
87  */
88 #define TKPG_IDENT "PgplotWidget"
89 
90 typedef struct TkPgplot TkPgplot;
91 
92 /*
93  * Declare a container for a list of PGPLOT widgets.
94  */
95 typedef struct {
96   TkPgplot *head;       /* The head of the list of widgets */
97 } TkPgplotList;
98 
99 /*
100  * A context descriptor for managing parent ScrolledWindow scroll-bars.
101  */
102 typedef struct {
103 #ifdef RIVET
104   Callback xScrollCmd;  /* Rivet X-axis update-scrollbar callback */
105   Callback yScrollCmd;  /* Rivet Y-axis update-scrollbar callback */
106 #else
107   char *xScrollCmd;     /* Tcl X-axis scrollbar-update command */
108   char *yScrollCmd;     /* Tcl Y-axis scrollbar-update command */
109 #endif
110   unsigned x;           /* Pixmap X coordinate of top left corner of window */
111   unsigned y;           /* Pixmap Y coordinate of top left corner of window */
112 } TkpgScroll;
113 
114 /*
115  * This container records state-values that are modified by X events.
116  */
117 typedef struct {
118   unsigned long mask;  /* Event mask registered to tkpg_EventHandler() */
119   int focus_acquired;  /* True when we have keyboard-input focus */
120   int cursor_active;   /* True when cursor augmentation is active */
121 } TkpgEvents;
122 
123 struct TkPgplot {
124 #ifdef RIVET
125   RIVET_CLASS_DECL
126 #endif
127                       /* Widget context */
128   Tk_Window tkwin;          /* Tk's window object */
129   Display *display;         /* The X display of the window */
130   Tcl_Interp *interp;       /* The application's TCL interpreter */
131   char buffer[81];          /* A work buffer for constructing result strings */
132 
133                       /* Public resource attributes */
134   int max_colors;           /* The max number of colors needed */
135   int min_colors;           /* The min number of colors needed */
136   int req_width;            /* The requested widget width (pixels) */
137   int req_height;           /* The requested widget height (pixels) */
138   int highlight_thickness;  /* The width of the highlight border */
139   XColor *highlightBgColor; /* The inactive traversal highlight color */
140   XColor *highlightColor;   /* The active traversal highlight color */
141   XColor *normalFg;         /* Normal foreground color (color index 1) */
142   Tk_3DBorder border;       /* 3D border structure */
143   int borderWidth;          /* The width of the 3D border */
144   int relief;               /* Relief of the 3D border */
145   char *takeFocus;          /* "1" to allow focus traversal, "0" to disallow */
146   Cursor cursor;            /* The active cursor of the window */
147   int share;                /* True if shared colors are desired */
148   int padx,pady;            /* Extra padding margin widths (pixels) */
149                       /* Private attributes */
150   TkPgplot *next;           /* The next widget of a list of PGPLOT Xt widgets */
151   int tkslct_id;            /* The device ID returned to PGPLOT by the */
152                             /* open-workstation driver opcode, and used for */
153                             /* subsequent device selection via the */
154                             /* select-plot driver opcode */
155   int pgslct_id;            /* The device ID returned to the application by */
156                             /* pgopen() for subsequent device selection with */
157                             /* the pgslct() function */
158   char *device;             /* A possible PGPLOT cpgbeg() file string */
159   TkpgScroll scroll;        /* Used to maintain parent scroll bars */
160   TkpgEvents events;        /* X event context */
161   PgxWin *pgx;              /* PGPLOT generic X-window context descriptor */
162 };
163 
164 static TkPgplot *new_TkPgplot(Tcl_Interp *interp, Tk_Window main_w, char *name,
165 			      int argc, char *argv[]);
166 static TkPgplot *del_TkPgplot(TkPgplot *tkpg);
167 
168 
169 /*
170  * Describe all recognized widget resources.
171  */
172 static Tk_ConfigSpec configSpecs[] = {
173 
174   {TK_CONFIG_BORDER,
175      "-background", "background", "Background",
176      "Black", Tk_Offset(TkPgplot, border), 0},
177   {TK_CONFIG_SYNONYM,
178      "-bg", "background", (char *) NULL, NULL, 0, 0},
179 
180   {TK_CONFIG_COLOR,
181      "-foreground", "foreground", "Foreground",
182      "White", Tk_Offset(TkPgplot, normalFg), 0},
183   {TK_CONFIG_SYNONYM,
184      "-fg", "foreground", (char *) NULL, NULL, 0, 0},
185 
186   {TK_CONFIG_ACTIVE_CURSOR,
187      "-cursor", "cursor", "Cursor",
188      "", Tk_Offset(TkPgplot, cursor), TK_CONFIG_NULL_OK},
189 
190   {TK_CONFIG_PIXELS,
191      "-borderwidth", "borderWidth", "BorderWidth",
192      "0", Tk_Offset(TkPgplot, borderWidth), 0},
193   {TK_CONFIG_SYNONYM,
194      "-bd", "borderWidth", (char *) NULL, NULL, 0, 0},
195 
196   {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
197      "raised", Tk_Offset(TkPgplot, relief), 0},
198 
199   {TK_CONFIG_PIXELS,
200      "-height", "height", "Height",
201      TKPG_STR_DEF_HEIGHT, Tk_Offset(TkPgplot, req_height), 0},
202 
203   {TK_CONFIG_PIXELS,
204      "-width", "width", "Width",
205      TKPG_STR_DEF_WIDTH, Tk_Offset(TkPgplot, req_width), 0},
206 
207   {TK_CONFIG_COLOR,
208      "-highlightbackground", "highlightBackground", "HighlightBackground",
209      "grey", Tk_Offset(TkPgplot, highlightBgColor), 0},
210 
211   {TK_CONFIG_COLOR,
212      "-highlightcolor", "highlightColor", "HighlightColor",
213      "White", Tk_Offset(TkPgplot, highlightColor), 0},
214 
215   {TK_CONFIG_PIXELS,
216      "-highlightthickness", "highlightThickness", "HighlightThickness",
217      TKPG_STR_DEF_HIGHLIGHT_WIDTH, Tk_Offset(TkPgplot, highlight_thickness), 0},
218 
219   {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
220      "0", Tk_Offset(TkPgplot, takeFocus), TK_CONFIG_NULL_OK},
221 
222 #ifdef RIVET
223   {TK_CONFIG_CUSTOM,
224      "-xscrollcommand", "xScrollCommand", "ScrollCommand",
225      0, Tk_Offset(TkPgplot, scroll.xScrollCmd),
226      TK_CONFIG_NULL_OK, &rivet_custom_callback_option},
227 #else
228   {TK_CONFIG_STRING,
229      "-xscrollcommand", "xScrollCommand", "ScrollCommand",
230      "", Tk_Offset(TkPgplot, scroll.xScrollCmd),
231      TK_CONFIG_NULL_OK},
232 #endif
233 
234 #ifdef RIVET
235   {TK_CONFIG_CUSTOM,
236      "-yscrollcommand", "yScrollCommand", "ScrollCommand",
237      0, Tk_Offset(TkPgplot, scroll.yScrollCmd),
238      TK_CONFIG_NULL_OK, &rivet_custom_callback_option},
239 #else
240   {TK_CONFIG_STRING,
241      "-yscrollcommand", "yScrollCommand", "ScrollCommand",
242      "", Tk_Offset(TkPgplot, scroll.yScrollCmd),
243      TK_CONFIG_NULL_OK},
244 #endif
245 
246   {TK_CONFIG_INT,
247      "-mincolors", "minColors", "MinColors",
248      TKPG_STR_MIN_COLORS, Tk_Offset(TkPgplot, min_colors), 0},
249 
250   {TK_CONFIG_INT,
251      "-maxcolors", "maxColors", "MaxColors",
252      TKPG_STR_DEF_COLORS, Tk_Offset(TkPgplot, max_colors), 0},
253 
254   {TK_CONFIG_BOOLEAN,
255      "-share", "share", "Share",
256      0, Tk_Offset(TkPgplot, share), 0},
257 
258   {TK_CONFIG_PIXELS,
259      "-padx", "padX", "Pad",
260      TKPG_STR_MARGIN_DEF, Tk_Offset(TkPgplot, padx), 0},
261 
262   {TK_CONFIG_PIXELS,
263      "-pady", "padY", "Pad",
264      TKPG_STR_MARGIN_DEF, Tk_Offset(TkPgplot, pady), 0},
265 
266   {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
267      (char *) NULL, 0, 0}
268 };
269 
270 /* Enumerate the PGPLOT class widget lists */
271 
272 #define TKPG_ACTIVE_WIDGETS 1
273 #define TKPG_FREE_WIDGETS 2
274 
275 static TkPgplotList *tkpg_WidgetList(int type);
276 
277 static TkPgplot *tkpg_FindWidgetByName(char *name, int type, TkPgplot **prev);
278 static TkPgplot *tkpg_FindWidgetByID(int tkslct_id, int type, TkPgplot **prev);
279 
280 static TkPgplot *tkpg_RemoveWidget(char *name, int type);
281 static TkPgplot *tkpg_PrependWidget(TkPgplot *tkpg, int type);
282 static TkPgplot *tkpg_CurrentWidget(char *context);
283 
284 static TkPgplot *tkpg_open_widget(char *name);
285 static TkPgplot *tkpg_close_widget(char *name);
286 
287 static void tkpg_NewPixmap(PgxWin *pgx, unsigned width, unsigned height);
288 static void tkpg_update_scroll_bars(TkPgplot *tkpg);
289 static void tkpg_update_clip(TkPgplot *tkpg);
290 static void tkpg_update_border(TkPgplot *tkpg);
291 
292 static int PgplotCmd(ClientData context, Tcl_Interp *interp, int argc,
293 		     char *argv[]);
294 
295 static int tkpg_InstanceCommand(ClientData context, Tcl_Interp *interp,
296 				int argc, char *argv[]);
297 static int tkpg_InstanceCommand_return(ClientData context, int iret);
298 
299 static int tkpg_Configure(TkPgplot *tkpg, Tcl_Interp *interp,
300 			  int argc, char *argv[], int flags);
301 static void tkpg_expose_handler(TkPgplot *tkpg, XEvent *event);
302 static void tkpg_draw_focus_highlight(TkPgplot *tkpg);
303 static void tkpg_draw_3d_border(TkPgplot *tkpg);
304 static int tkpg_refresh_window(TkPgplot *tkpg);
305 
306 static void tkpg_ClrCursor(TkPgplot *tkpg);
307 static void tkpg_EventHandler(ClientData context, XEvent *event);
308 static void tkpg_CursorHandler(ClientData context, XEvent *event);
309 
310 static Tk_Window tkpg_toplevel_of_path(Tcl_Interp *interp, Tk_Window main_w,
311 				       char *path);
312 
313 /*
314  * Enumerate supported pgband() cursor types.
315  */
316 typedef enum {
317   TKPG_NORM_CURSOR  = 0, /* Un-augmented X cursor */
318   TKPG_LINE_CURSOR  = 1, /* Line cursor between ref and pointer */
319   TKPG_RECT_CURSOR  = 2, /* Rectangular cursor between ref and pointer */
320   TKPG_YRNG_CURSOR  = 3, /* Two horizontal lines, at ref.x and pointer.x */
321   TKPG_XRNG_CURSOR  = 4, /* Two vertical lines, at ref.y and pointer.y */
322   TKPG_HLINE_CURSOR = 6, /* Horizontal line cursor at y=ref.y */
323   TKPG_VLINE_CURSOR = 5, /* Vertical line cursor at x=ref.x */
324   TKPG_CROSS_CURSOR = 7  /* Cross-hair cursor centered on the pointer */
325 } TkpgCursorMode;
326 
327 static int tkpg_SetCursor(TkPgplot *tkpg, TkpgCursorMode mode,
328 		 	  float xref, float yref, int ci);
329 #ifdef RIVET
330 static void tkpg_FreeProc(ClientData context);
331 #else
332 static void tkpg_FreeProc(char *context);
333 #endif
334 
335 static int tkpg_scrollbar_callback(TkPgplot *tkpg, Tcl_Interp *interp,
336 				   char *widget, char *view, int argc,
337 				   char *argv[]);
338 static int tkpg_scrollbar_error(TkPgplot *tkpg, Tcl_Interp *interp,
339 				char *widget, char *view, int argc,
340 				char *argv[]);
341 
342 static int tkpg_tcl_setcursor(TkPgplot *tkpg, Tcl_Interp *interp,
343 			      int argc, char *argv[]);
344 static int tkpg_tcl_world(TkPgplot *tkpg, Tcl_Interp *interp,
345 			  char *widget, int argc, char *argv[]);
346 static int tkpg_tcl_pixel(TkPgplot *tkpg, Tcl_Interp *interp,
347 			  char *widget, int argc, char *argv[]);
348 static int tkpg_tcl_id(TkPgplot *tkpg, Tcl_Interp *interp,
349 		       char *widget, int argc, char *argv[]);
350 static int tkpg_tcl_device(TkPgplot *tkpg, Tcl_Interp *interp,
351 			   char *widget, int argc, char *argv[]);
352 
353 #ifdef RIVET
354 static void del_RvPgplot(ClientData obj);
355 
356 static Rivet_class_struct PgplotClassObj = {
357 	0,
358 	"Pgplot",
359 	PgplotCmd,
360 	tkpg_InstanceCommand,
361 	del_RvPgplot,
362 	0,
363 	configSpecs,
364 	0,
365 };
366 
367 Rivetclass      PgplotClass = &PgplotClassObj;
368 #endif
369 
370 /*
371  * The following file-scope container records the list of active and
372  * inactive PGPLOT widgets.
373  */
374 static struct {
375   int id_counter;              /* Used to give widgets unique identifiers */
376   TkPgplotList active_widgets; /* List of active widgets */
377   TkPgplotList free_widgets;   /* List of unnassigned widgets */
378 } tkPgplotClassRec = {
379   0,         /* id_counter */
380   {NULL},    /* active_widgets */
381   {NULL},    /* free_widgets */
382 };
383 
384 /*
385  * The following macro defines the event mask used by the cursor event
386  * handler. It is here to ensure that Tk_CreateEventHandler() and
387  * Tk_DeleteEventHandler() are presented with identical event masks.
388  */
389 #define CURSOR_EVENT_MASK ((unsigned long)(EnterWindowMask | LeaveWindowMask | \
390 					   PointerMotionMask))
391 /*
392  * The following macro defines the event mask normally used by the widget.
393  */
394 #define NORMAL_EVENT_MASK ((unsigned long)(StructureNotifyMask | \
395 					   ExposureMask | FocusChangeMask))
396 
397 /*.......................................................................
398  * Provide a package initialization procedure. This creates the Tcl
399  * "pgplot" widget creation command.
400  *
401  * Input:
402  *  interp  Tcl_Interp *  The TCL interpreter of the application.
403  * Output:
404  *  return         int    TCL_OK    - Success.
405  *                        TCL_ERROR - Failure.
406  */
407 #ifdef RIVET
Rvpgplot_Init(Tcl_Interp * interp)408 int Rvpgplot_Init(Tcl_Interp *interp)
409 #else
410 int Tkpgplot_Init(Tcl_Interp *interp)
411 #endif
412 {
413 /*
414  * Get the main window of the application.
415  */
416   Tk_Window main_w = Tk_MainWindow(interp);
417 /*
418  * If Tk_Init() hasn't been called, then there won't be a main window
419  * yet. In such cases, Tk_MainWindow() places a suitable error message
420  * in interp->result.
421  */
422   if(!main_w)
423     return TCL_ERROR;
424 /*
425  * Create the TCL command that is to be used for creating PGPLOT widgets.
426  */
427   Tcl_CreateCommand(interp, "pgplot", PgplotCmd, (ClientData) main_w, 0);
428   return TCL_OK;
429 }
430 
431 /*.......................................................................
432  * This function provides the TCL command that creates a PGPLOT widget.
433  *
434  * Input:
435  *  context   ClientData    The client_data argument specified in
436  *                          TkPgplot_Init() when PgplotCmd was registered.
437  *                          This is the main window cast to (ClientData).
438  *  interp    Tcl_Interp *  The TCL intrepreter.
439  *  argc             int    The number of command arguments.
440  *  argv            char ** The array of 'argc' command arguments.
441  *                          argv[0] = "pgplot"
442  *                          argv[1] = the name to give the new widget.
443  *                          argv[2..argc-1] = attribute settings.
444  * Output:
445  *  return           int    TCL_OK    - Success.
446  *                          TCL_ERROR - Failure.
447  */
PgplotCmd(ClientData context,Tcl_Interp * interp,int argc,char * argv[])448 static int PgplotCmd(ClientData context, Tcl_Interp *interp, int argc,
449 		     char *argv[])
450 {
451   Tk_Window main_tkw = (Tk_Window)context; /* The application main window */
452   TkPgplot *tkpg;                          /* The new widget instance object */
453 /*
454  * Make sure that a name for the new widget has been provided.
455  */
456   if(argc < 2) {
457     Tcl_AppendResult(interp, "Wrong number of arguments - should be \'",
458 		     argv[0], " pathName \?options\?\'", NULL);
459     return TCL_ERROR;
460   };
461 /*
462  * Allocate the widget-instance object.
463  */
464   tkpg = new_TkPgplot(interp, main_tkw, argv[1], argc-2, argv+2);
465   if(!tkpg)
466     return TCL_ERROR;
467   return TCL_OK;
468 }
469 
470 /*.......................................................................
471  * Create a new widget instance object.
472  *
473  * Input:
474  *  interp   Tcl_Interp *  The TCL interpreter object.
475  *  main_w    Tk_Window    The main window of the application.
476  *  name           char *  The name to give the new widget.
477  *  argc            int    The number of argument in argv[]
478  *  argv           char ** Any configuration arguments.
479  * Output:
480  *  return     TkPgplot *  The new PGPLOT widget, or NULL on error.
481  *                         If NULL is returned then the context of the
482  *                         error will have been recorded in the result
483  *                         field of the interpreter.
484  */
new_TkPgplot(Tcl_Interp * interp,Tk_Window main_w,char * name,int argc,char * argv[])485 static TkPgplot *new_TkPgplot(Tcl_Interp *interp, Tk_Window main_w, char *name,
486 			      int argc, char *argv[])
487 {
488   TkPgplot *tkpg;  /* The new widget object */
489   PgxWin *pgx;     /* The PGPLOT X window object of the widget */
490   Tk_Window top_w; /* The top-level window parent of 'name' */
491 /*
492  * Get the toplevel window associated with the pathname in 'name'.
493  */
494   top_w = tkpg_toplevel_of_path(interp, main_w, name);
495   if(!top_w)
496     return NULL;
497 /*
498  * Allocate the container.
499  */
500   tkpg = (TkPgplot *) malloc(sizeof(TkPgplot));
501   if(!tkpg) {
502     Tcl_AppendResult(interp, "Insufficient memory to create ", name, NULL);
503     return NULL;
504   };
505 /*
506  * Before attempting any operation that might fail, initialize the container
507  * at least up to the point at which it can safely be passed to
508  * del_TkPgplot().
509  */
510   tkpg->tkwin = NULL;
511   tkpg->display = Tk_Display(main_w);
512   tkpg->interp = interp;
513   tkpg->max_colors = TKPG_DEF_COLORS;
514   tkpg->min_colors = TKPG_MIN_COLORS;
515   tkpg->req_width = TKPG_DEF_WIDTH;
516   tkpg->req_height = TKPG_DEF_HEIGHT;
517   tkpg->highlight_thickness = TKPG_DEF_HIGHLIGHT_WIDTH;
518   tkpg->highlightBgColor = NULL;
519   tkpg->highlightColor = NULL;
520   tkpg->normalFg = NULL;
521   tkpg->border = NULL;
522   tkpg->borderWidth = 0;
523   tkpg->relief = TK_RELIEF_RAISED;
524   tkpg->takeFocus = NULL;
525   tkpg->cursor = None;
526   tkpg->share = 0;
527   tkpg->padx = 0;
528   tkpg->pady = 0;
529   tkpg->next = NULL;
530   tkpg->tkslct_id = tkPgplotClassRec.id_counter++;
531   tkpg->pgslct_id = 0;
532   tkpg->device = NULL;
533   tkpg->scroll.xScrollCmd = NULL;
534   tkpg->scroll.yScrollCmd = NULL;
535   tkpg->scroll.x = 0;
536   tkpg->scroll.y = 0;
537   tkpg->events.mask = NoEventMask;
538   tkpg->events.focus_acquired = 0;
539   tkpg->events.cursor_active = 0;
540   tkpg->pgx = NULL;
541 /*
542  * Allocate the PGPLOT-window context descriptor.
543  */
544   pgx = tkpg->pgx = new_PgxWin(tkpg->display, Tk_ScreenNumber(top_w),
545 			(void *) tkpg, name, 0, tkpg_NewPixmap);
546   if(!pgx) {
547     Tcl_AppendResult(interp, "Unable to create Pgplot window object for: ",
548 		     name, NULL);
549     return NULL;
550   };
551 /*
552  * Compose a sample PGPLOT device-specification for use in opening this
553  * widget to PGPLOT.
554  */
555   tkpg->device = (char *) malloc(sizeof(char) *
556 				 (strlen(name)+1+strlen(TK_PGPLOT_DEVICE)+1));
557   if(!tkpg->device) {
558     Tcl_AppendResult(interp, "Insufficient memory for ", name, NULL);
559     return NULL;
560   };
561   sprintf(tkpg->device, "%s/%s", name, TK_PGPLOT_DEVICE);
562 /*
563  * Ensure that the toplevel window parent of the new window exists,
564  * before attempting to determine its visual.
565  */
566   Tk_MakeWindowExist(top_w);
567 /*
568  * Create the widget window from the specified path.
569  */
570   tkpg->tkwin = Tk_CreateWindowFromPath(interp, main_w, name, NULL);
571   if(!tkpg->tkwin)
572     return del_TkPgplot(tkpg);
573 /*
574  * Give the widget a class name.
575  */
576   Tk_SetClass(tkpg->tkwin, "Pgplot");
577 /*
578  * Register an event handler.
579  */
580   tkpg->events.mask = NORMAL_EVENT_MASK;
581   Tk_CreateEventHandler(tkpg->tkwin, tkpg->events.mask, tkpg_EventHandler,
582 			(ClientData) tkpg);
583 /*
584  * Create the TCL command that will allow users to configure the widget.
585  */
586   Tcl_CreateCommand(interp, name, tkpg_InstanceCommand, (ClientData) tkpg, 0);
587 /*
588  * Parse command line defaults into tkpg so that tkpg->min_colors,
589  * tkpg->max_colors and tkpg->share are known.
590  */
591   if(Tk_ConfigureWidget(interp, tkpg->tkwin, configSpecs, argc, argv,
592 			(char *) tkpg, 0) == TCL_ERROR)
593     return del_TkPgplot(tkpg);
594 /*
595  * If requested, try to allocate read/write colors.
596  * If this fails arrange to try shared colors.
597  */
598   if(!tkpg->share && !pgx_window_visual(pgx, Tk_WindowId(top_w),
599 				       tkpg->min_colors, tkpg->max_colors, 0))
600     tkpg->share = 1;
601 /*
602  * Allocate shared colors?
603  */
604   if(tkpg->share) {
605     if(!pgx_window_visual(pgx, Tk_WindowId(top_w), tkpg->min_colors,
606 			tkpg->max_colors, 1)) {
607       Tcl_AppendResult(interp, "Unable to allocate any colors for ",name,NULL);
608       return del_TkPgplot(tkpg);
609     };
610   };
611 /*
612  * Force Tk to create the window.
613  */
614   Tk_MakeWindowExist(tkpg->tkwin);
615 /*
616  * Fill in details about the window in pgx.
617  */
618   pgx->window = Tk_WindowId(tkpg->tkwin);
619 /*
620  * Create and initialize a graphical context descriptor. This is where
621  * Line widths, line styles, fill styles, plot color etc.. are
622  * recorded.
623  */
624   {
625     XGCValues gcv;
626     gcv.graphics_exposures = False;
627     pgx_start_error_watch(pgx);
628     pgx->expose_gc = XCreateGC(pgx->display, pgx->window, (unsigned long)
629 			       (GCGraphicsExposures), &gcv);
630     if(pgx_end_error_watch(pgx) || pgx->expose_gc==NULL) {
631       Tcl_AppendResult(interp,
632 	       "Failed to allocate a graphical context for ", name, NULL);
633       return del_TkPgplot(tkpg);
634     };
635   };
636 /*
637  * Parse the command-line arguments again and install the relevant
638  * defaults into the color descriptor created by pgx_window_visual().
639  */
640   if(tkpg_Configure(tkpg, interp, argc, argv, 0))
641     return del_TkPgplot(tkpg);
642 /*
643  * If the widget has scroll-bars make sure that they agree with the
644  * window.
645  */
646   tkpg_update_scroll_bars(tkpg);
647   tkpg_update_clip(tkpg);
648 /*
649  * Replace the share configuration attribute with the actual
650  * value that was acheived.
651  */
652   tkpg->share = pgx->color->readonly;
653 /*
654  * Prepend the new widget to the list of unassigned widgets to be
655  * used by pgbeg().
656  */
657   tkpg_PrependWidget(tkpg, TKPG_FREE_WIDGETS);
658 /*
659  * Return the widget name.
660  */
661   Tcl_SetResult(interp, Tk_PathName(tkpg->tkwin), TCL_STATIC);
662   return tkpg;
663 }
664 
665 /*.......................................................................
666  * Delete a TkPgplot widget.
667  *
668  * Input:
669  *  tkpg    TkPgplot *   The widget to be deleted.
670  * Output:
671  *  return  TkPgplot *   Always NULL.
672  */
del_TkPgplot(TkPgplot * tkpg)673 static TkPgplot *del_TkPgplot(TkPgplot *tkpg)
674 {
675   if(tkpg) {
676     if(tkpg->pgx) {
677       PgxWin *pgx = tkpg->pgx;
678 /*
679  * Remove the device from the appropriate list of PGPLOT widgets.
680  */
681       tkpg_RemoveWidget(pgx->name, pgx->state ? TKPG_ACTIVE_WIDGETS :
682 		                                TKPG_FREE_WIDGETS);
683 /*
684  * Delete the Tcl command attached to the widget.
685  */
686       Tcl_DeleteCommand(tkpg->interp, pgx->name);
687 /*
688  * Delete the window context descriptor.
689  */
690       tkpg->pgx = del_PgxWin(tkpg->pgx);
691     };
692 /*
693  * Delete the device name string.
694  */
695     if(tkpg->device)
696       free(tkpg->device);
697     tkpg->device = NULL;
698 /*
699  * Clear the cursor.
700  */
701     tkpg_ClrCursor(tkpg);
702 /*
703  * Delete resource values.
704  */
705     if(tkpg->display)
706       Tk_FreeOptions(configSpecs, (char *) tkpg, tkpg->display, 0);
707 /*
708  * Remove the DestroyNotify event handler before destroying the
709  * window. Otherwise this function would call itself recursively
710  * and pgx would be free'd twice.
711  */
712     if(tkpg->events.mask != NoEventMask) {
713       Tk_DeleteEventHandler(tkpg->tkwin, tkpg->events.mask,
714 			    tkpg_EventHandler, (ClientData) tkpg);
715       tkpg->events.mask = NoEventMask;
716     };
717 /*
718  * Zap the window.
719  */
720     if(tkpg->tkwin) {
721       Tk_DestroyWindow(tkpg->tkwin);
722       tkpg->tkwin = NULL;
723     };
724 /*
725  * Delete the container.
726  */
727     free(tkpg);
728   };
729   return NULL;
730 }
731 
732 #ifdef RIVET
733 /*.......................................................................
734  * This is a rivet-friendly wrapper around del_TkPgplot().
735  */
del_RvPgplot(ClientData obj)736 static void del_RvPgplot(ClientData obj)
737 {
738   del_TkPgplot((TkPgplot *) obj);
739 }
740 #endif
741 
742 /*.......................................................................
743  * This function is called upon by the pgxwin toolkit whenever the
744  * pixmap used as backing store needs to be resized.
745  *
746  * Input:
747  *  pgx      PgxWin *   The pgxwin toolkit context descriptor.
748  *  width  unsigned     The desired new pixmap width.
749  *  height unsigned     The desired new pixmap height.
750  */
tkpg_NewPixmap(PgxWin * pgx,unsigned width,unsigned height)751 static void tkpg_NewPixmap(PgxWin *pgx, unsigned width, unsigned height)
752 {
753   TkPgplot *tkpg = (TkPgplot *) pgx->context;
754 /*
755  * Reset the scrollbars then hand the job of allocating the
756  * pixmap back to the pgxwin toolkit.
757  */
758   tkpg->scroll.x = 0;
759   tkpg->scroll.y = 0;
760   tkpg_update_scroll_bars(tkpg);
761   pgx_new_pixmap(pgx, width, height);
762   return;
763 }
764 
765 /*.......................................................................
766  * Whenever the size of a pixmap and/or window of a PGPLOT winget are
767  * changed, this function should be called to adjust scroll bars.
768  *
769  * Input:
770  *  tkpg     TkPgplot *  The pgplot widget instance.
771  */
tkpg_update_scroll_bars(TkPgplot * tkpg)772 static void tkpg_update_scroll_bars(TkPgplot *tkpg)
773 {
774   TkpgScroll *scroll = &tkpg->scroll;
775 #ifndef RIVET
776   char scroll_args[60];  /* Scrollbar set-command arguments */
777 #endif
778 /*
779  * Block widget deletion, so that if one of the scroll-bar callbacks
780  * deletes the widget we won't end up using a deleted tkpg pointer.
781  */
782   Tk_Preserve((ClientData)tkpg);
783 /*
784  * Update the horizontal scroll-bar if there is one.
785  */
786   if(scroll->xScrollCmd) {
787     double pixmap_width = pgx_pixmap_width(tkpg->pgx);
788     double first, last;
789     if(pixmap_width < 1.0) {
790       first = 0.0;
791       last = 1.0;
792     } else {
793       first = scroll->x / pixmap_width;
794       last = (scroll->x + Tk_Width(tkpg->tkwin)) / pixmap_width;
795     };
796 #ifdef RIVET
797     rivet_scrollbar_update((Rivetobj)tkpg, scroll->xScrollCmd, first, last);
798 #else
799     sprintf(scroll_args, " %f %f", first, last);
800     (void) Tcl_VarEval(tkpg->interp, scroll->xScrollCmd, scroll_args, NULL);
801 #endif
802   };
803 /*
804  * Update the vertical scroll-bar if there is one.
805  */
806   if(scroll->yScrollCmd) {
807     double pixmap_height = pgx_pixmap_height(tkpg->pgx);
808     double first, last;
809     if(pixmap_height < 1.0) {
810       first = 0.0;
811       last = 1.0;
812     } else {
813       first = scroll->y / pixmap_height;
814       last = (scroll->y + Tk_Height(tkpg->tkwin)) / pixmap_height;
815     };
816 #ifdef RIVET
817     rivet_scrollbar_update((Rivetobj)tkpg, scroll->yScrollCmd, first, last);
818 #else
819     sprintf(scroll_args, " %f %f", first, last);
820     (void) Tcl_VarEval(tkpg->interp, scroll->yScrollCmd, scroll_args, NULL);
821 #endif
822   };
823 /*
824  * Tell pgplot about the current scroll and pan values.
825  */
826   pgx_scroll(tkpg->pgx, scroll->x, scroll->y);
827 /*
828  * Unblock widget deletion.
829  */
830   Tk_Release((ClientData)tkpg);
831   return;
832 }
833 
834 /*.......................................................................
835  * Update the clip-area of the window to prevent pgxwin functions from
836  * drawing over the highlight-borders.
837  *
838  * Input:
839  *  tkpg    TkPgplot *  The pgplot widget instance.
840  */
tkpg_update_clip(TkPgplot * tkpg)841 static void tkpg_update_clip(TkPgplot *tkpg)
842 {
843   (void) pgx_update_clip(tkpg->pgx, 1, Tk_Width(tkpg->tkwin),
844 			 Tk_Height(tkpg->tkwin),
845 			 tkpg->highlight_thickness + tkpg->borderWidth);
846 }
847 
848 /*.......................................................................
849  * Find an inactive PGPLOT widget of a given name, open it to PGPLOT,
850  * and move it to the head of the active list of widgets.
851  *
852  * Input:
853  *  name         char *  The name of the widget to be opened.
854  * Output:
855  *  tkpg     TkPgplot *  The selected widget, or NULL on error.
856  */
tkpg_open_widget(char * name)857 static TkPgplot *tkpg_open_widget(char *name)
858 {
859   TkPgplot *tkpg;
860 /*
861  * Remove the named widget from the free-widget list.
862  */
863   tkpg = tkpg_RemoveWidget(name, TKPG_FREE_WIDGETS);
864   if(!tkpg) {
865     if(tkpg_FindWidgetByName(name, TKPG_ACTIVE_WIDGETS, NULL)) {
866       fprintf(stderr, "%s: Widget %s is already open.\n", TKPG_IDENT, name);
867     } else {
868       fprintf(stderr, "%s: Can't open non-existent widget (%s).\n",
869 	      TKPG_IDENT, name ? name : "(null)");
870     };
871     return NULL;
872   };
873 /*
874  * Pre-pend the widget to the active list.
875  */
876   tkpg_PrependWidget(tkpg, TKPG_ACTIVE_WIDGETS);
877 /*
878  * Open the connection to the PgxWin library.
879  */
880   pgx_open(tkpg->pgx);
881   if(!tkpg->pgx->state)
882     tkpg_close_widget(name);
883 /*
884  * Reset the background and foreground colors to match the current
885  * configuration options.
886  */
887   pgx_set_background(tkpg->pgx, Tk_3DBorderColor(tkpg->border));
888   pgx_set_foreground(tkpg->pgx, tkpg->normalFg);
889 /*
890  * Reset its scroll-bars.
891  */
892   tkpg_update_scroll_bars(tkpg);
893   return tkpg;
894 }
895 
896 /*.......................................................................
897  * Find an active PGPLOT widget of a given name, close it to PGPLOT and
898  * move it to the head of the inactive list of widgets.
899  *
900  * Input:
901  *  name       char *  The name of the widget.
902  * Output:
903  *  return TkPgplot *  The selected widget, or NULL if not found.
904  */
tkpg_close_widget(char * name)905 static TkPgplot *tkpg_close_widget(char *name)
906 {
907   TkPgplot *tkpg;
908 /*
909  * Remove the widget from the active list.
910  */
911   tkpg = tkpg_RemoveWidget(name, TKPG_ACTIVE_WIDGETS);
912   if(!tkpg) {
913     fprintf(stderr, "%s: Request to close non-existent widget (%s).\n",
914 	    TKPG_IDENT, name ? name : "(null)");
915     return NULL;
916   };
917 /*
918  * Remove cursor handler.
919  */
920   tkpg_ClrCursor(tkpg);
921 /*
922  * Close the connection to the PgxWin library.
923  */
924   pgx_close(tkpg->pgx);
925 /*
926  * Invalidate the pgslct() id. The next time that the widget is opened
927  * to PGPLOT a different value will likely be used.
928  */
929   tkpg->pgslct_id = 0;
930 /*
931  * Prepend the widget to the free list.
932  */
933   tkpg_PrependWidget(tkpg, TKPG_FREE_WIDGETS);
934   return tkpg;
935 }
936 
937 /*.......................................................................
938  * Lookup a widget by name from a given list of widgets.
939  *
940  * Input:
941  *  name       char *  The name of the widget.
942  *  type        int    The enumerated name of the list to search,
943  *                     from:
944  *                       TKPG_ACTIVE_WIDGETS
945  *                       TKPG_FREE_WIDGETS
946  * Output:
947  *  prev   TkPgplot ** *prev will either be NULL if the widget
948  *                     was at the head of the list, or be the
949  *                     widget in the list that immediately precedes
950  *                     the specified widget.
951  *  return TkPgplot *  The located widget, or NULL if not found.
952  */
tkpg_FindWidgetByName(char * name,int type,TkPgplot ** prev)953 static TkPgplot *tkpg_FindWidgetByName(char *name, int type, TkPgplot **prev)
954 {
955   TkPgplotList *widget_list; /* The list to be searched */
956   widget_list = tkpg_WidgetList(type);
957   if(widget_list && name) {
958     TkPgplot *last = NULL;
959     TkPgplot *node = widget_list->head;
960     for( ; node; last = node, node = node->next) {
961       if(strcmp(node->pgx->name, name)==0) {
962 	if(prev)
963 	  *prev = last;
964 	return node;
965       };
966     };
967   };
968 /*
969  * Widget not found.
970  */
971   if(prev)
972     *prev = NULL;
973   return NULL;
974 }
975 
976 /*.......................................................................
977  * Lookup a widget by its PGPLOT id from a given list of widgets.
978  *
979  * Input:
980  *  tkslct_id   int    The number used by PGPLOT to select the
981  *                     device.
982  *  type        int    The enumerated name of the list to search,
983  *                     from:
984  *                       TKPG_ACTIVE_WIDGETS
985  *                       TKPG_FREE_WIDGETS
986  * Output:
987  *  prev   TkPgplot ** *prev will either be NULL if the widget
988  *                     was at the head of the list, or be the
989  *                     widget in the list that immediately precedes
990  *                     the specified widget.
991  *  return TkPgplot *  The located widget, or NULL if not found.
992  */
tkpg_FindWidgetByID(int tkslct_id,int type,TkPgplot ** prev)993 static TkPgplot *tkpg_FindWidgetByID(int tkslct_id, int type, TkPgplot **prev)
994 {
995   TkPgplotList *widget_list; /* The list to be searched */
996   widget_list = tkpg_WidgetList(type);
997   if(widget_list) {
998     TkPgplot *last = NULL;
999     TkPgplot *node = widget_list->head;
1000     for( ; node; last = node, node = node->next) {
1001       if(tkslct_id == node->tkslct_id) {
1002 	if(prev)
1003 	  *prev = last;
1004 	return node;
1005       };
1006     };
1007   };
1008 /*
1009  * Widget not found.
1010  */
1011   if(prev)
1012     *prev = NULL;
1013   return NULL;
1014 }
1015 
1016 /*.......................................................................
1017  * Lookup one of the PGPLOT class widget lists by its enumerated type.
1018  *
1019  * Input:
1020  *  type            int   The enumerated name of the list, from:
1021  *                          TKPG_ACTIVE_WIDGETS
1022  *                          TKPG_FREE_WIDGETS
1023  * Output:
1024  *  return TkPgplotList * The widget list, or NULL if not recognized.
1025  */
tkpg_WidgetList(int type)1026 static TkPgplotList *tkpg_WidgetList(int type)
1027 {
1028   switch(type) {
1029   case TKPG_ACTIVE_WIDGETS:
1030     return &tkPgplotClassRec.active_widgets;
1031   case TKPG_FREE_WIDGETS:
1032     return &tkPgplotClassRec.free_widgets;
1033   default:
1034     fprintf(stderr, "tkpg_WidgetList: No such list.\n");
1035   };
1036   return NULL;
1037 }
1038 
1039 /*.......................................................................
1040  * Remove a given widget from one of the PGPLOT class widget lists.
1041  *
1042  * Input:
1043  *  name       char *  The name of the widget to be removed from
1044  *                     the list.
1045  *  type        int    The enumerated name of the list from which to
1046  *                     remove the widget, from:
1047  *                       TKPG_ACTIVE_WIDGETS
1048  *                       TKPG_FREE_WIDGETS
1049  * Output:
1050  *  return TkPgplot *  The removed widget, or NULL if not found.
1051  */
tkpg_RemoveWidget(char * name,int type)1052 static TkPgplot *tkpg_RemoveWidget(char *name, int type)
1053 {
1054   TkPgplotList *widget_list; /* The list to remove the widget from */
1055   TkPgplot *tkpg = NULL;     /* The widget being removed */
1056   TkPgplot *prev;            /* The widget preceding tkpg in the list */
1057 /*
1058  * Get the widget list.
1059  */
1060   widget_list = tkpg_WidgetList(type);
1061   if(widget_list) {
1062     tkpg = tkpg_FindWidgetByName(name, type, &prev);
1063     if(tkpg) {
1064       if(prev) {
1065 	prev->next = tkpg->next;
1066       } else {
1067 	widget_list->head = tkpg->next;
1068       };
1069       tkpg->next = NULL;
1070     };
1071   };
1072   return tkpg;
1073 }
1074 
1075 /*.......................................................................
1076  * Prepend a PGPLOT widget to a given PGPLOT class widget list.
1077  *
1078  * Input:
1079  *  tkpg   TkPgplot *  The widget to add to the list.
1080  *  type        int    The enumerated name of the list to add to, from:
1081  *                       TKPG_ACTIVE_WIDGETS
1082  *                       TKPG_FREE_WIDGETS
1083  * Output:
1084  *  return TkPgplot *  The added widget (the same as tkpg), or NULL
1085  *                     on error.
1086  */
tkpg_PrependWidget(TkPgplot * tkpg,int type)1087 static TkPgplot *tkpg_PrependWidget(TkPgplot *tkpg, int type)
1088 {
1089   TkPgplotList *widget_list;  /* The list to prepend the widget to */
1090 /*
1091  * Get the widget list.
1092  */
1093   widget_list = tkpg_WidgetList(type);
1094   if(widget_list) {
1095     tkpg->next = widget_list->head;
1096     widget_list->head = tkpg;
1097   };
1098   return tkpg;
1099 }
1100 
1101 /*.......................................................................
1102  * Return the currently selected PGPLOT device.
1103  *
1104  * Input:
1105  *  context     char *  If no TkPgplot device is currently selected
1106  *                      and context!=NULL then, an error message of
1107  *                      the form printf("%s: ...\n", context) will
1108  *                      be written to stderr reporting that no
1109  *                      device is open.
1110  * Output:
1111  *  return  TkPgplot *  The currently selected PGPLOT device, or
1112  *                      NULL if no device is currently selected.
1113  */
tkpg_CurrentWidget(char * context)1114 static TkPgplot *tkpg_CurrentWidget(char *context)
1115 {
1116   TkPgplot *tkpg = tkPgplotClassRec.active_widgets.head;
1117   if(!tkpg && context)
1118     fprintf(stderr, "%s: No /%s device is currently selected.\n", context,
1119 	    TK_PGPLOT_DEVICE);
1120   return tkpg;
1121 }
1122 
1123 /*.......................................................................
1124  * This is the only external entry point to the tk device driver.
1125  * It is called by PGPLOT to open, perform operations on, return
1126  * information about and close tk windows.
1127  *
1128  * Input:
1129  *  ifunc   int *  The PGPLOT operation code to be executed.
1130  * Input/output:
1131  *  rbuf  float *  A general buffer for input/output of float values.
1132  *  nbuf    int *  Where relevant this is used to return the number of
1133  *                 elements in rbuf[]. Also used on input to specify
1134  *                 number of pixels in the line-of-pixels primitive.
1135  *  chr    char *  A general buffer for string I/O.
1136  *  lchr    int *  Where relevant this is used to send and return the
1137  *                 number of significant characters in chr.
1138  * Input:
1139  *  len     int    Added to the call line by the FORTRAN compiler.
1140  *                 This contains the declared size of chr[].
1141  */
1142 #ifdef VMS
DRIV(ifunc,rbuf,nbuf,chrdsc,lchr)1143 void DRIV(ifunc, rbuf, nbuf, chrdsc, lchr)
1144      int *ifunc;
1145      float rbuf[];
1146      int *nbuf;
1147      struct dsc$descriptor_s *chrdsc; /* VMS FORTRAN string descriptor */
1148      int *lchr;
1149 {
1150   int len = chrdsc->dsc$w_length;
1151   char *chr = chrdsc->dsc$a_pointer;
1152 #else
1153 void DRIV(ifunc, rbuf, nbuf, chr, lchr, len)
1154  int   *ifunc, *nbuf, *lchr;
1155  int   len;
1156  float rbuf[];
1157  char  *chr;
1158 {
1159 #endif
1160 /*
1161  * Get the active widget if there is one.
1162  */
1163   TkPgplot *tkpg = tkpg_CurrentWidget(NULL);
1164   PgxWin *pgx = tkpg ? tkpg->pgx : NULL;
1165   int i;
1166 /*
1167  * Flush buffered opcodes.
1168  */
1169   pgx_pre_opcode(pgx, *ifunc);
1170 /*
1171  * Branch on the specified PGPLOT opcode.
1172  */
1173   switch(*ifunc) {
1174 
1175 /*--- IFUNC=1, Return device name ---------------------------------------*/
1176 
1177   case 1:
1178     {
1179       char *dev_name = TK_PGPLOT_DEVICE " (widget_path/" TK_PGPLOT_DEVICE ")";
1180       strncpy(chr, dev_name, len);
1181       *lchr = strlen(dev_name);
1182       for(i = *lchr; i < len; i++)
1183 	chr[i] = ' ';
1184     };
1185     break;
1186 
1187 /*--- IFUNC=2, Return physical min and max for plot device, and range
1188                of color indices -----------------------------------------*/
1189   case 2:
1190     rbuf[0] = 0.0;
1191     rbuf[1] = -1.0;  /* Report no effective max plot width */
1192     rbuf[2] = 0.0;
1193     rbuf[3] = -1.0;  /* Report no effective max plot height */
1194     rbuf[4] = 0.0;
1195     rbuf[5] = (pgx && !pgx->bad_device) ? pgx->color->ncol-1 : 1;
1196     *nbuf = 6;
1197     break;
1198 
1199 /*--- IFUNC=3, Return device resolution ---------------------------------*/
1200 
1201   case 3:
1202     pgx_get_resolution(pgx, &rbuf[0], &rbuf[1]);
1203     rbuf[2] = 1.0;		/* Device coordinates per pixel */
1204     *nbuf = 3;
1205     break;
1206 
1207 /*--- IFUNC=4, Return misc device info ----------------------------------*/
1208 
1209   case 4:
1210     chr[0] = 'I'; /* Interactive device */
1211     chr[1] = 'X'; /* Cursor is available and opcode 27 is desired */
1212     chr[2] = 'N'; /* No dashed lines */
1213     chr[3] = 'A'; /* Area fill available */
1214     chr[4] = 'T'; /* Thick lines */
1215     chr[5] = 'R'; /* Rectangle fill available */
1216     chr[6] = 'P'; /* Line of pixels available */
1217     chr[7] = 'N'; /* Don't prompt on pgend */
1218     chr[8] = 'Y'; /* Can return color representation */
1219     chr[9] = 'N'; /* Not used */
1220     chr[10]= 'S'; /* Area-scroll available */
1221     *lchr = 11;
1222     break;
1223 
1224 /*--- IFUNC=5, Return default file name ---------------------------------*/
1225 
1226   case 5:
1227     chr[0] = '\0';  /* Default name is "" */
1228     *lchr = 0;
1229     break;
1230 
1231 /*--- IFUNC=6, Return default physical size of plot ---------------------*/
1232 
1233   case 6:
1234     pgx_def_size(pgx, Tk_Width(tkpg->tkwin), Tk_Height(tkpg->tkwin), rbuf, nbuf);
1235     break;
1236 
1237 /*--- IFUNC=7, Return misc defaults -------------------------------------*/
1238 
1239   case 7:
1240     rbuf[0] = 1.0;
1241     *nbuf = 1;
1242     break;
1243 
1244 /*--- IFUNC=8, Select plot ----------------------------------------------*/
1245 
1246   case 8:
1247     {
1248       TkPgplot *new_tkpg = tkpg_FindWidgetByID((int)(rbuf[1]+0.5),
1249 					   TKPG_ACTIVE_WIDGETS, NULL);
1250       if(new_tkpg) {
1251 	new_tkpg->pgslct_id = (int) (rbuf[0]+0.5);
1252 	tkpg_RemoveWidget(new_tkpg->pgx->name, TKPG_ACTIVE_WIDGETS);
1253 	tkpg_PrependWidget(new_tkpg, TKPG_ACTIVE_WIDGETS);
1254       } else {
1255 	fprintf(stderr, "%s: [Select plot] No such open device.\n", TKPG_IDENT);
1256       };
1257     };
1258     break;
1259 
1260 /*--- IFUNC=9, Open workstation -----------------------------------------*/
1261 
1262   case 9:
1263 /*
1264  * Assign the returned device unit number and success indicator.
1265  * Assume failure to open until the workstation is open.
1266  */
1267     rbuf[0] = rbuf[1] = 0.0;
1268     *nbuf = 2;
1269 /*
1270  * Prepare the display name.
1271  */
1272     if(*lchr >= len) {
1273       fprintf(stderr, "%s: Widget name too long.\n", TKPG_IDENT);
1274       return;
1275     } else {
1276       chr[*lchr] = '\0';
1277     };
1278 /*
1279  * Get the requested widget from the free widget list.
1280  */
1281     tkpg = tkpg_open_widget(chr);
1282     if(!tkpg)
1283       return;
1284     rbuf[0] = tkpg->tkslct_id; /* The number used to select this device */
1285     rbuf[1] = 1.0;
1286     *nbuf = 2;
1287     break;
1288 
1289 /*--- IFUNC=10, Close workstation ---------------------------------------*/
1290 
1291   case 10:
1292 /*
1293  * Remove the device from the list of open devices.
1294  */
1295     if(pgx)
1296       tkpg_close_widget(pgx->name);
1297     break;
1298 
1299 /*--- IFUNC=11, Begin picture -------------------------------------------*/
1300 
1301   case 11:
1302     pgx_begin_picture(pgx, rbuf);
1303     break;
1304 
1305 /*--- IFUNC=12, Draw line -----------------------------------------------*/
1306 
1307   case 12:
1308     pgx_draw_line(pgx, rbuf);
1309     break;
1310 
1311 /*--- IFUNC=13, Draw dot ------------------------------------------------*/
1312 
1313   case 13:
1314     pgx_draw_dot(pgx, rbuf);
1315     break;
1316 
1317 /*--- IFUNC=14, End picture ---------------------------------------------*/
1318 
1319   case 14:
1320     break;
1321 
1322 /*--- IFUNC=15, Select color index --------------------------------------*/
1323 
1324   case 15:
1325     pgx_set_ci(pgx, (int) (rbuf[0] + 0.5));
1326     break;
1327 
1328 /*--- IFUNC=16, Flush buffer. -------------------------------------------*/
1329 
1330   case 16:
1331     pgx_flush(pgx);
1332     break;
1333 
1334 /*--- IFUNC=17, Read cursor. --------------------------------------------*/
1335 
1336   case 17:
1337     if(tkpg)
1338       tkpg_ClrCursor(tkpg);
1339     pgx_read_cursor(pgx, rbuf, chr, nbuf, lchr);
1340     break;
1341 
1342 /*--- IFUNC=18, Erase alpha screen. -------------------------------------*/
1343   /* (Not implemented: no alpha screen) */
1344   case 18:
1345     break;
1346 
1347 /*--- IFUNC=19, Set line style. -----------------------------------------*/
1348   /* (Not implemented: should not be called) */
1349   case 19:
1350     break;
1351 
1352 /*--- IFUNC=20, Polygon fill. -------------------------------------------*/
1353 
1354   case 20:
1355     pgx_poly_fill(pgx, rbuf);
1356     break;
1357 
1358 /*--- IFUNC=21, Set color representation. -------------------------------*/
1359 
1360   case 21:
1361     {
1362       int ci = (int)(rbuf[0]+0.5);
1363       pgx_set_rgb(pgx, ci, rbuf[1],rbuf[2],rbuf[3]);
1364       if(ci==0)
1365 	tkpg_update_border(tkpg);
1366     };
1367     break;
1368 
1369 /*--- IFUNC=22, Set line width. -----------------------------------------*/
1370 
1371   case 22:
1372     pgx_set_lw(pgx, rbuf[0]);
1373     break;
1374 
1375 /*--- IFUNC=23, Escape --------------------------------------------------*/
1376     /* (Not implemented: ignored) */
1377   case 23:
1378     break;
1379 
1380 /*--- IFUNC=24, Rectangle Fill. -----------------------------------------*/
1381 
1382   case 24:
1383     pgx_rect_fill(pgx, rbuf);
1384     break;
1385 
1386 /*--- IFUNC=25, ---------------------------------------------------------*/
1387   /* (Not implemented: ignored) */
1388   case 25:
1389     break;
1390 
1391 /*--- IFUNC=26, Line of pixels ------------------------------------------*/
1392 
1393   case 26:
1394     pgx_pix_line(pgx, rbuf, nbuf);
1395     break;
1396 
1397 /*--- IFUNC=27, World-coordinate scaling --------------------------------*/
1398 
1399   case 27:
1400     pgx_set_world(pgx, rbuf);
1401     break;
1402 
1403 /*--- IFUNC=29, Query color representation ------------------------------*/
1404   case 29:
1405     pgx_get_rgb(pgx, rbuf, nbuf);
1406     break;
1407 
1408 /*--- IFUNC=30, Scroll rectangle ----------------------------------------*/
1409   case 30:
1410     pgx_scroll_rect(pgx, rbuf);
1411     break;
1412 
1413 /*--- IFUNC=?, ----------------------------------------------------------*/
1414 
1415   default:
1416     fprintf(stderr, "%s: Ignoring unimplemented opcode=%d.\n",
1417 	    TKPG_IDENT, *ifunc);
1418     *nbuf = -1;
1419     break;
1420   };
1421   return;
1422 }
1423 
1424 /*.......................................................................
1425  * This function services TCL commands for a given widget.
1426  *
1427  * Input:
1428  *  context   ClientData    The tkpg widget cast to (ClientData).
1429  *  interp    Tcl_Interp *  The TCL intrepreter.
1430  *  argc             int    The number of command arguments.
1431  *  argv            char ** The array of 'argc' command arguments.
1432  * Output:
1433  *  return           int    TCL_OK    - Success.
1434  *                          TCL_ERROR - Failure.
1435  */
tkpg_InstanceCommand(ClientData context,Tcl_Interp * interp,int argc,char * argv[])1436 static int tkpg_InstanceCommand(ClientData context, Tcl_Interp *interp,
1437 			       int argc, char *argv[])
1438 {
1439   TkPgplot *tkpg = (TkPgplot *) context;
1440   char *widget;     /* The name of the widget */
1441   char *command;    /* The name of the command */
1442 /*
1443  * Get the name of the widget.
1444  */
1445   widget = argv[0];
1446 /*
1447  * Get the name of the command.
1448  */
1449   if(argc < 2) {
1450     Tcl_AppendResult(interp, "Missing arguments to ", widget, " command.",
1451 		     NULL);
1452     return TCL_ERROR;
1453   };
1454   command = argv[1];
1455 /*
1456  * Prevent untimely deletion of the widget while this function runs.
1457  * Note that following this statement you must return via
1458  * tkpg_InstanceCommand_return() to ensure that Tk_Release() gets called.
1459  */
1460   Tk_Preserve(context);
1461 /*
1462  * Check for recognized command names.
1463  */
1464   if(strcmp(command, "xview") == 0) {             /* X-axis scroll-bar update */
1465     return tkpg_InstanceCommand_return(context,
1466 		      tkpg_scrollbar_callback(tkpg, interp, widget, command,
1467 					      argc-2, argv+2));
1468   } else if(strcmp(command, "yview") == 0) {      /* Y-axis scroll-bar update */
1469     return tkpg_InstanceCommand_return(context,
1470 		      tkpg_scrollbar_callback(tkpg, interp, widget, command,
1471 					      argc-2, argv+2));
1472   } else if(strcmp(command, "configure") == 0) {  /* Configure widget */
1473 /*
1474  * Check the number of configure arguments.
1475  */
1476     switch(argc - 2) {
1477     case 0:   /* Return the values of all configuration options */
1478       return tkpg_InstanceCommand_return(context,
1479 			Tk_ConfigureInfo(interp, tkpg->tkwin, configSpecs,
1480 					 (char *) tkpg, NULL, 0));
1481       break;
1482     case 1:   /* Return the value of a single given configuration option */
1483       return tkpg_InstanceCommand_return(context,
1484 		        Tk_ConfigureInfo(interp, tkpg->tkwin, configSpecs,
1485 					 (char *) tkpg, argv[2], 0));
1486       break;
1487     default:  /* Change one of more of the configuration options */
1488       return tkpg_InstanceCommand_return(context,
1489 			tkpg_Configure(tkpg, interp, argc-2, argv+2,
1490 				       TK_CONFIG_ARGV_ONLY));
1491       break;
1492     };
1493   } else if(strcmp(command, "cget") == 0) {  /* Get a configuration value */
1494     if(argc != 3) {
1495       Tcl_AppendResult(interp, "Wrong number of arguments to \"", widget,
1496 		       " cget\" command", NULL);
1497       return tkpg_InstanceCommand_return(context, TCL_ERROR);
1498     } else {
1499       return tkpg_InstanceCommand_return(context,
1500 			Tk_ConfigureValue(interp, tkpg->tkwin, configSpecs,
1501 					  (char *) tkpg, argv[2], 0));
1502     };
1503   } else if(strcmp(command, "setcursor") == 0) { /* Augment the cursor */
1504     return tkpg_InstanceCommand_return(context,
1505 			tkpg_tcl_setcursor(tkpg, interp, argc - 2, argv + 2));
1506   } else if(strcmp(command, "clrcursor") == 0) { /* Clear cursor augmentation */
1507     tkpg_ClrCursor(tkpg);
1508     return tkpg_InstanceCommand_return(context, TCL_OK);
1509   } else if(strcmp(command, "world") == 0) {  /* Pixel to world coordinates */
1510     return tkpg_InstanceCommand_return(context,
1511 				       tkpg_tcl_world(tkpg, interp, widget,
1512 						      argc-2, argv+2));
1513   } else if(strcmp(command, "pixel") == 0) {  /* World to pixel coordinates */
1514     return tkpg_InstanceCommand_return(context,
1515 				       tkpg_tcl_pixel(tkpg, interp, widget,
1516 						      argc-2, argv+2));
1517   } else if(strcmp(command, "id") == 0) {     /* PGPLOT id of widget */
1518     return tkpg_InstanceCommand_return(context,
1519 				       tkpg_tcl_id(tkpg, interp, widget,
1520 						   argc-2, argv+2));
1521   } else if(strcmp(command, "device") == 0) { /* PGPLOT name for the widget */
1522     return tkpg_InstanceCommand_return(context,
1523 				       tkpg_tcl_device(tkpg, interp, widget,
1524 						       argc-2, argv+2));
1525   };
1526 /*
1527  * Unknown command name.
1528  */
1529   Tcl_AppendResult(interp, "Unknown command \"", widget, " ", command, "\"",
1530 		   NULL);
1531   return tkpg_InstanceCommand_return(context, TCL_ERROR);
1532 }
1533 
1534 /*.......................................................................
1535  * This is a private cleanup-return function of tkpg_InstanceCommand().
1536  * It should be used to return from said function after Tk_Preserve() has
1537  * been called. It calls Tk_Release() on the widget to unblock deletion
1538  * and returns the specified error code.
1539  *
1540  * Input:
1541  *  context   ClientData    The tkpg widget cast to (ClientData).
1542  *  iret             int    TCL_OK or TCL_ERROR.
1543  * Output:
1544  *  return           int    The value of iret.
1545  */
tkpg_InstanceCommand_return(ClientData context,int iret)1546 static int tkpg_InstanceCommand_return(ClientData context, int iret)
1547 {
1548   Tk_Release(context);
1549   return iret;
1550 }
1551 
1552 /*.......................................................................
1553  * This function is services TCL commands for a given widget.
1554  *
1555  * Input:
1556  *  tkpg        TkPgplot *  The widget record to be configured.
1557  *  interp    Tcl_Interp *  The TCL intrepreter.
1558  *  argc             int    The number of configuration arguments.
1559  *  argv            char ** The array of 'argc' configuration arguments.
1560  *  flags            int    The flags argument of Tk_ConfigureWidget():
1561  *                           0                - No flags.
1562  *                           TK_CONFIG_ARGV   - Override the X defaults
1563  *                                              database and the configSpecs
1564  *                                              defaults.
1565  * Output:
1566  *  return           int    TCL_OK    - Success.
1567  *                          TCL_ERROR - Failure.
1568  */
tkpg_Configure(TkPgplot * tkpg,Tcl_Interp * interp,int argc,char * argv[],int flags)1569 static int tkpg_Configure(TkPgplot *tkpg, Tcl_Interp *interp,
1570 			  int argc, char *argv[], int flags)
1571 {
1572 /*
1573  * Get the X-window pgplot object.
1574  */
1575   PgxWin *pgx = tkpg->pgx;
1576 /*
1577  * Install the new defaults in tkpg.
1578  */
1579   if(Tk_ConfigureWidget(interp, tkpg->tkwin, configSpecs, argc, argv,
1580 			(char *) tkpg, flags) == TCL_ERROR)
1581     return TCL_ERROR;
1582 /*
1583  * Install the background color in PGPLOT color-index 0.
1584  */
1585   pgx_set_background(pgx, Tk_3DBorderColor(tkpg->border));
1586 /*
1587  * Install the foreground color in PGPLOT color-index 1.
1588  */
1589   pgx_set_foreground(pgx, tkpg->normalFg);
1590 /*
1591  * Install changes to window attributes.
1592  */
1593   {
1594     XSetWindowAttributes attr;  /* The attribute-value container */
1595     unsigned long mask = 0;     /* The set of attributes that have changed */
1596     attr.background_pixel = pgx->color->pixel[0];
1597     mask |= CWBackPixel;
1598     attr.colormap = pgx->color->cmap;
1599     mask |= CWColormap;
1600     attr.border_pixel = pgx->color->pixel[0];
1601     mask |= CWBorderPixel;
1602     attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
1603       KeyPressMask | KeyReleaseMask;
1604     mask |= CWDontPropagate;
1605     Tk_ChangeWindowAttributes(tkpg->tkwin, mask, &attr);
1606   };
1607 /*
1608  * Tell Tk what window size we want.
1609  */
1610   Tk_GeometryRequest(tkpg->tkwin, tkpg->req_width, tkpg->req_height);
1611 /*
1612  * Tell pgxwin that the clip margin may have changed.
1613  */
1614   tkpg_update_clip(tkpg);
1615 /*
1616  * Update the optional window margins.
1617  */
1618   pgx_set_margin(pgx, tkpg->padx, tkpg->pady);
1619 /*
1620  * Refresh the window.
1621  */
1622   tkpg_refresh_window(tkpg);
1623   return TCL_OK;
1624 }
1625 
1626 /*.......................................................................
1627  * This is the main X event callback for Pgplot widgets.
1628  *
1629  * Input:
1630  *  context   ClientData    The tkpg widget cast to (ClientData).
1631  *  event         XEvent *  The event that triggered the callback.
1632  */
tkpg_EventHandler(ClientData context,XEvent * event)1633 static void tkpg_EventHandler(ClientData context, XEvent *event)
1634 {
1635   TkPgplot *tkpg = (TkPgplot *) context;
1636 /*
1637  * Determine what type of event triggered this call.
1638  */
1639   switch(event->type) {
1640   case ConfigureNotify:   /* The window has been resized */
1641     tkpg->scroll.x = 0;
1642     tkpg->scroll.y = 0;
1643     tkpg_update_clip(tkpg);
1644     tkpg_update_scroll_bars(tkpg);
1645     tkpg_refresh_window(tkpg);
1646     break;
1647   case DestroyNotify:     /* The window has been destroyed */
1648 /*
1649  * Delete the cursor event handler to prevent further use by user.
1650  */
1651     tkpg_ClrCursor(tkpg);
1652 /*
1653  * Delete the main event handler to prevent prolonged use.
1654  */
1655     Tk_DeleteEventHandler(tkpg->tkwin, tkpg->events.mask, tkpg_EventHandler,
1656 			  (ClientData) tkpg);
1657 /*
1658  * Tell del_TkPgplot() that we have already deleted the event mask.
1659  */
1660     tkpg->events.mask = NoEventMask;
1661 /*
1662  * Force the functions in pgxwin.c to discard subsequent graphics.
1663  */
1664     if(tkpg->pgx)
1665       tkpg->pgx->window = None;
1666 /*
1667  * Queue deletion of tkpg until all references to the widget have been
1668  * completed.
1669  */
1670     Tk_EventuallyFree(context, tkpg_FreeProc);
1671     break;
1672   case FocusIn:           /* Keyboard-input focus has been acquired */
1673     tkpg->events.focus_acquired = 1;
1674     tkpg_draw_focus_highlight(tkpg);
1675     break;
1676   case FocusOut:          /* Keyboard-input focus has been lost */
1677     tkpg->events.focus_acquired = 0;
1678     tkpg_draw_focus_highlight(tkpg);
1679     break;
1680   case Expose:            /* Redraw the specified area */
1681     tkpg_expose_handler(tkpg, event);
1682     break;
1683   };
1684   return;
1685 }
1686 
1687 /*.......................................................................
1688  * The expose-event handler for PGPLOT widgets.
1689  *
1690  * Input:
1691  *  tkpg   TkPgplot * The Tk Pgplot widget.
1692  *  event    XEvent   The expose event that invoked the callback.
1693  */
tkpg_expose_handler(TkPgplot * tkpg,XEvent * event)1694 static void tkpg_expose_handler(TkPgplot *tkpg, XEvent *event)
1695 {
1696 /*
1697  * Re-draw the focus-highlight border.
1698  */
1699   tkpg_draw_focus_highlight(tkpg);
1700 /*
1701  * Re-draw the 3D borders.
1702  */
1703   tkpg_draw_3d_border(tkpg);
1704 /*
1705  * Re-draw the damaged area.
1706  */
1707   pgx_expose(tkpg->pgx, event);
1708   return;
1709 }
1710 
1711 /*.......................................................................
1712  * Re-draw the focus highlight border if it has a finite size.
1713  *
1714  * Input:
1715  *  tkpg    TkPgplot * The Tk Pgplot widget.
1716  */
tkpg_draw_focus_highlight(TkPgplot * tkpg)1717 static void tkpg_draw_focus_highlight(TkPgplot *tkpg)
1718 {
1719   Window w = Tk_WindowId(tkpg->tkwin);
1720 /*
1721  * Re-draw the focus-highlight border.
1722  */
1723   if(tkpg->highlight_thickness != 0) {
1724     GC gc = Tk_GCForColor(tkpg->events.focus_acquired ?
1725 			  tkpg->highlightColor : tkpg->highlightBgColor,
1726 			  w);
1727     Tk_DrawFocusHighlight(tkpg->tkwin, gc, tkpg->highlight_thickness, w);
1728   };
1729   return;
1730 }
1731 
1732 /*.......................................................................
1733  * Re-draw the 3D border if necessary.
1734  *
1735  * Input:
1736  *  tkpg    TkPgplot * The Tk Pgplot widget.
1737  */
tkpg_draw_3d_border(TkPgplot * tkpg)1738 static void tkpg_draw_3d_border(TkPgplot *tkpg)
1739 {
1740   Tk_Window tkwin = tkpg->tkwin;
1741   Window w = Tk_WindowId(tkwin);
1742 /*
1743  * Re-draw the focus-highlight border.
1744  */
1745   if(tkpg->border && tkpg->borderWidth > 0) {
1746     int margin = tkpg->highlight_thickness;
1747     Tk_Draw3DRectangle(tkwin, w, tkpg->border, margin, margin,
1748 		       Tk_Width(tkwin) - 2*margin, Tk_Height(tkwin) - 2*margin,
1749 		       tkpg->borderWidth, tkpg->relief);
1750   };
1751   return;
1752 }
1753 
1754 /*.......................................................................
1755  * Augment the cursor of a given widget.
1756  *
1757  * Input:
1758  *  tkpg       TkPgplot * The PGPLOT widget to connect a cursor to.
1759  *  mode TkpgCursorMode   The type of cursor augmentation.
1760  *  xref,yref     float   The world-coordinate reference point for band-type
1761  *                        cursors.
1762  *  ci              int   The color index with which to plot the cursor,
1763  *                        or -1 to select the current foreground color.
1764  * Output:
1765  *  return          int   TCL_OK or TCL_ERROR.
1766  */
tkpg_SetCursor(TkPgplot * tkpg,TkpgCursorMode mode,float xref,float yref,int ci)1767 static int tkpg_SetCursor(TkPgplot *tkpg, TkpgCursorMode mode,
1768 			  float xref, float yref, int ci)
1769 {
1770   PgxWin *pgx = tkpg->pgx;
1771   float rbuf[2];
1772 /*
1773  * Remove any existing cursor augmentation.
1774  */
1775   tkpg_ClrCursor(tkpg);
1776 /*
1777  * Mark the cursor as active.
1778  */
1779   tkpg->events.cursor_active = 1;
1780 /*
1781  * Convert xref, yref from world coordinates to device coordinates.
1782  */
1783   rbuf[0] = xref;
1784   rbuf[1] = yref;
1785   pgx_world2dev(pgx, rbuf);
1786 /*
1787  * Raise the cursor.
1788  */
1789   if(pgx_set_cursor(pgx, ci, (int)mode, 0, rbuf, rbuf)) {
1790     Tcl_AppendResult(tkpg->interp, "Unable to display cursor.\n", NULL);
1791     tkpg_ClrCursor(tkpg);
1792     return TCL_ERROR;
1793   };
1794 /*
1795  * If the pointer is currently in the window, record its position
1796  * and draw the cursor.
1797  */
1798   if(pgx_locate_cursor(pgx))
1799     pgx_draw_cursor(pgx);
1800 /*
1801  * Create an event handler to handle asychronous cursor input.
1802  */
1803   Tk_CreateEventHandler(tkpg->tkwin, CURSOR_EVENT_MASK,
1804 			tkpg_CursorHandler, (ClientData) tkpg);
1805   return TCL_OK;
1806 }
1807 
1808 /*.......................................................................
1809  * This is the X event callback for Pgplot cursor events. It is called
1810  * only when the cursor augmentation has been established by
1811  * tkpg_SetCursor() and not cleared by tkpg_ClrCursor().
1812  *
1813  * Input:
1814  *  context   ClientData    The tkpg widget cast to (ClientData).
1815  *  event         XEvent *  The event that triggered the callback.
1816  */
tkpg_CursorHandler(ClientData context,XEvent * event)1817 static void tkpg_CursorHandler(ClientData context, XEvent *event)
1818 {
1819   TkPgplot *tkpg = (TkPgplot *) context;
1820   PgxWin *pgx = tkpg->pgx;
1821   float rbuf[2];
1822   char key;
1823 /*
1824  * Handle the event. Note that button-press and keyboard events
1825  * have not been selected so the return values are irrelevent.
1826  */
1827   (void) pgx_cursor_event(pgx, event, rbuf, &key);
1828 /*
1829  * Handle errors.
1830  */
1831   if(pgx->bad_device)
1832     tkpg_ClrCursor(tkpg);
1833   return;
1834 }
1835 
1836 /*.......................................................................
1837  * Clear the cursor of a given widget.
1838  *
1839  *  tkpg  TkPgplot * The widget to disconnect the cursor from.
1840  */
tkpg_ClrCursor(TkPgplot * tkpg)1841 static void tkpg_ClrCursor(TkPgplot *tkpg)
1842 {
1843   if(tkpg) {
1844     PgxWin *pgx = tkpg->pgx;
1845 /*
1846  * Do nothing if the cursor is inactive.
1847  */
1848     if(tkpg->events.cursor_active) {
1849 /*
1850  * Remove the current event handler.
1851  */
1852       Tk_DeleteEventHandler(tkpg->tkwin, CURSOR_EVENT_MASK,
1853 			    tkpg_CursorHandler, (ClientData) tkpg);
1854 /*
1855  * Reset the cursor context to its inactive state.
1856  */
1857       tkpg->events.cursor_active = 0;
1858 /*
1859  * Erase the cursor.
1860  */
1861       pgx_erase_cursor(pgx);
1862       pgx_set_cursor(pgx, 0, TKPG_NORM_CURSOR, 0, NULL, NULL);
1863     };
1864   };
1865   return;
1866 }
1867 
1868 /*.......................................................................
1869  * Augment the cursor as specified in the arguments of the setcursor
1870  * widget command.
1871  *
1872  * Input:
1873  *  tkpg        TkPgplot *  The widget record to be configured.
1874  *  interp    Tcl_Interp *  The TCL intrepreter.
1875  *  argc             int    The number of configuration arguments.
1876  *  argv            char ** The array of 'argc' configuration arguments.
1877  *                           [0] The type of cursor augmentation, from:
1878  *                                norm  - Un-augmented X cursor
1879  *                                line  - Line cursor between ref and pointer
1880  *                                rect  - Rectangle between ref and pointer
1881  *                                yrng  - Horizontal lines at ref.x & pointer.x
1882  *                                xrng  - Vertical lines at ref.y & pointer.y
1883  *                                hline - Horizontal line cursor at y=ref.y
1884  *                                vline - Vertical line cursor at x=ref.x
1885  *                                cross - Pointer centered cross-hair
1886  *                           [1] The X-axis world coordinate at which
1887  *                               to anchor rect,yrng and xrng cursors.
1888  *                           [2] The Y-axis world coordinate at which
1889  *                               to anchor rect,yrng and xrng cursors.
1890  *                           [3] The color index of the cursor.
1891  *  flags            int    The flags argument of Tk_ConfigureWidget():
1892  *                           0                - No flags.
1893  *                           TK_CONFIG_ARGV   - Override the X defaults
1894  *                                              database and the configSpecs
1895  *                                              defaults.
1896  * Output:
1897  *  return           int    TCL_OK    - Success.
1898  *                          TCL_ERROR - Failure.
1899  */
tkpg_tcl_setcursor(TkPgplot * tkpg,Tcl_Interp * interp,int argc,char * argv[])1900 static int tkpg_tcl_setcursor(TkPgplot *tkpg, Tcl_Interp *interp,
1901 			      int argc, char *argv[])
1902 {
1903   TkpgCursorMode mode;  /* Cursor augmentation mode */
1904   double xref,yref;    /* The X and Y reference positions of the cursor */
1905   int ci;              /* The color index used to draw the cursor */
1906   int found = 0;       /* True once the mode has been identified */
1907   int i;
1908 /*
1909  * List the correspondence between cursor-mode names and pgband() mode
1910  * enumerators.
1911  */
1912   struct {
1913     TkpgCursorMode mode;
1914     char *name;
1915   } modes[] = {
1916     {TKPG_NORM_CURSOR, "norm"},  /* Un-augmented X cursor */
1917     {TKPG_LINE_CURSOR, "line"},  /* Line cursor between ref and pointer */
1918     {TKPG_RECT_CURSOR, "rect"},  /* Rectangle between ref and pointer */
1919     {TKPG_YRNG_CURSOR, "yrng"},  /* Horizontal lines at ref.x & pointer.x */
1920     {TKPG_XRNG_CURSOR, "xrng"},  /* Vertical lines at ref.y & pointer.y */
1921     {TKPG_HLINE_CURSOR, "hline"},/* Horizontal line cursor at y=ref.y */
1922     {TKPG_VLINE_CURSOR, "vline"},/* Vertical line cursor at x=ref.x */
1923     {TKPG_CROSS_CURSOR, "cross"},/* Pointer centered cross-hair */
1924   };
1925 /*
1926  * Check that we have the expected number of arguments.
1927  */
1928   if(argc != 4) {
1929     Tcl_AppendResult(interp, "Wrong number of arguments. Should be: \"",
1930 		     tkpg->pgx->name, " setcursor mode x y ci",
1931 		     NULL);
1932     return TCL_ERROR;
1933   };
1934 /*
1935  * Make sure that the widget is currently open to PGPLOT.
1936  */
1937   if(tkpg->pgslct_id == 0) {
1938     Tcl_AppendResult(interp, tkpg->pgx->name,
1939 		     " setcursor: Widget not open to PGPLOT.", NULL);
1940     return TCL_ERROR;
1941   };
1942 /*
1943  * Lookup the cursor mode.
1944  */
1945   mode = TKPG_NORM_CURSOR;
1946   for(i=0; !found && i<sizeof(modes)/sizeof(modes[0]); i++) {
1947     if(strcmp(modes[i].name, argv[0]) == 0) {
1948       found = 1;
1949       mode = modes[i].mode;
1950     };
1951   };
1952 /*
1953  * Mode not found?
1954  */
1955   if(!found) {
1956     Tcl_AppendResult(interp, "Unknown PGPLOT cursor mode \"", argv[0],
1957 		     "\". Should be one of:", NULL);
1958     for(i=0; i<sizeof(modes)/sizeof(modes[0]); i++)
1959       Tcl_AppendResult(interp, " ", modes[i].name, NULL);
1960     return TCL_ERROR;
1961   };
1962 /*
1963  * Read the cursor X and Y coordinate.
1964  */
1965   if(Tcl_GetDouble(interp, argv[1], &xref) == TCL_ERROR ||
1966      Tcl_GetDouble(interp, argv[2], &yref) == TCL_ERROR)
1967     return TCL_ERROR;
1968 /*
1969  * Get the color index to use when drawing the cursor.
1970  */
1971   if(Tcl_GetInt(interp, argv[3], &ci) == TCL_ERROR)
1972     return TCL_ERROR;
1973 /*
1974  * Delegate the rest of the work to tkpg_SetCursor().
1975  */
1976   return tkpg_SetCursor(tkpg, mode, xref, yref, ci);
1977 }
1978 
1979 /*.......................................................................
1980  * This is a Tk_FreeProc() wrapper function around del_TkPgplot(),
1981  * suitable for use with Tk_EventuallyFree().
1982  *
1983  * Input:
1984  *  context   ClientData  The tkpg widget to be deleted, cast to
1985  *                        ClientData.
1986  */
1987 #ifdef RIVET
tkpg_FreeProc(ClientData context)1988 static void tkpg_FreeProc(ClientData context)
1989 #else
1990 static void tkpg_FreeProc(char *context)
1991 #endif
1992 {
1993   (void) del_TkPgplot((TkPgplot *) context);
1994 }
1995 
1996 #ifdef RIVET
1997 /*.......................................................................
1998  * Return an unambiguous PGPLOT device-specification that can be used
1999  * as the FILE argument of cpgbeg() to open a given Rivet PGPLOT widget.
2000  *
2001  * Input:
2002  *  widget Rivetobj   A rivet pgplot widget.
2003  * Output:
2004  *  return     char * The PGPLOT device-specication. Note that the returned
2005  *                    string is owned by the widget driver and must not be
2006  *                    free()d or overwritten.
2007  */
rvp_device_name(Rivetobj widget)2008 char *rvp_device_name(Rivetobj widget)
2009 {
2010   TkPgplot *tkpg = (TkPgplot *) widget;
2011   return tkpg->device;
2012 }
2013 
2014 /*.......................................................................
2015  * Return the pgslct_id of the given Rivet pgplot widget. This can then
2016  * be used with the cpgslct() function to select the widget as the currently
2017  * active widget.
2018  *
2019  * Input:
2020  *  widget Rivetobj   A rivet pgplot widget.
2021  * Output:
2022  *  return     int   The PGPLOT device-id. This will be 0 if the widget
2023  *                   is not currently open to PGPLOT.
2024  */
rvp_device_id(Rivetobj widget)2025 int rvp_device_id(Rivetobj widget)
2026 {
2027   TkPgplot *tkpg = (TkPgplot *) widget;
2028   return tkpg->pgslct_id;
2029 }
2030 
2031 /*.......................................................................
2032  * Convert from X window pixel coordinates to PGPLOT world coordinates.
2033  *
2034  * Input:
2035  *  widget Rivetobj    A rivet pgplot widget.
2036  *  px, py      int    The X-window pixel coordinates to be converted.
2037  *  wx, wy    float *  The corresponding PGPLOT world coordinates are
2038  *                     assigned to the variables pointed to by wx and wy.
2039  * Output:
2040  *  return      int    0 - OK.
2041  *                     1 - Error.
2042  */
rvp_xwin2world(Rivetobj widget,int px,int py,float * wx,float * wy)2043 int rvp_xwin2world(Rivetobj widget, int px, int py, float *wx, float *wy)
2044 {
2045   TkPgplot *tkpg = (TkPgplot *) widget;
2046   float rbuf[2];
2047 /*
2048  * Convert from pixels to world coordinates.
2049  */
2050   if(pgx_win2dev(tkpg->pgx, px, py, rbuf) ||
2051      pgx_dev2world(tkpg->pgx, rbuf))
2052     return 1;
2053 /*
2054  * Assign the return values.
2055  */
2056   *wx = rbuf[0];
2057   *wy = rbuf[1];
2058   return 0;
2059 }
2060 
2061 /*.......................................................................
2062  * Convert from PGPLOT world coordinates to X window pixel coordinates.
2063  *
2064  * Input:
2065  *  widget Rivetobj    A rivet pgplot widget.
2066  *  wx, wy    float    The PGPLOT world coordinates to be converted.
2067  *  px, py      int *  The corresponding X-window pixel coordinates are
2068  *                     assigned to the variables pointed to by px and py.
2069  * Output:
2070  *  return      int    0 - OK.
2071  *                     1 - Error.
2072  */
rvp_world2xwin(Rivetobj widget,float wx,float wy,int * px,int * py)2073 int rvp_world2xwin(Rivetobj widget, float wx, float wy, int *px, int *py)
2074 {
2075   TkPgplot *tkpg = (TkPgplot *) widget;
2076   float rbuf[2];
2077 /*
2078  * Convert from world coordinates to pixel coordinates.
2079  */
2080   rbuf[0] = wx;
2081   rbuf[1] = wy;
2082   if(pgx_world2dev(tkpg->pgx, rbuf) ||
2083      pgx_dev2win(tkpg->pgx, rbuf, px, py))
2084     return 1;
2085   return 0;
2086 }
2087 
2088 #endif
2089 
2090 /*.......................................................................
2091  * Refresh the contents of the window.
2092  *
2093  * Input:
2094  *  tkpg        TkPgplot *  The widget record to be configured.
2095  * Output:
2096  *  return           int    0 - OK.
2097  *                          1 - Error.
2098  */
tkpg_refresh_window(TkPgplot * tkpg)2099 static int tkpg_refresh_window(TkPgplot *tkpg)
2100 {
2101   if(Tk_IsMapped(tkpg->tkwin)) {
2102     tkpg_draw_focus_highlight(tkpg);
2103     tkpg_draw_3d_border(tkpg);
2104     return pgx_scroll(tkpg->pgx, tkpg->scroll.x, tkpg->scroll.y);
2105   };
2106   return 0;
2107 }
2108 
2109 /*.......................................................................
2110  * Whenever the color representation of the background color is changed
2111  * via PGPLOT, this function is called to update the Tk 3D border.
2112  *
2113  * Input:
2114  *  tkpg     TkPgplot *   The associated PGPLOT widget.
2115  */
tkpg_update_border(TkPgplot * tkpg)2116 static void tkpg_update_border(TkPgplot *tkpg)
2117 {
2118   XColor *bg;     /* The new background color */
2119   char cname[20]; /* The color as a string of the form #rrrrggggbbbb */
2120   Tk_3DBorder bd; /* The new Tk border */
2121 /*
2122  * Get the PGPLOT background color.
2123  */
2124   bg = &tkpg->pgx->color->xcolor[0];
2125 /*
2126  * Tk_Get3DBorder requires a standard X color resource string.
2127  */
2128   sprintf(cname, "#%4.4hx%4.4hx%4.4hx", bg->red, bg->green, bg->blue);
2129   bd = Tk_Get3DBorder(tkpg->interp, tkpg->tkwin, cname);
2130   if(bd) {
2131 /*
2132  * Replace the previous border with the new one.
2133  */
2134     if(tkpg->border)
2135       Tk_Free3DBorder(tkpg->border);
2136     tkpg->border = bd;
2137     tkpg_draw_3d_border(tkpg);
2138   } else {
2139     fprintf(stderr, "Tk_Get3DBorder failed: %s\n", tkpg->interp->result);
2140   };
2141 }
2142 
2143 /*.......................................................................
2144  * Respond to an xview or yview scrollbar command.
2145  *
2146  * Input:
2147  *  tkpg        TkPgplot *  The widget record to be configured.
2148  *  interp    Tcl_Interp *  The TCL intrepreter.
2149  *  widget          char *  The name of the PGPLOT widget.
2150  *  view            char *  "xview" or "yview".
2151  *  argc             int    The number of configuration arguments.
2152  *  argv            char ** The array of 'argc' configuration arguments.
2153  * Output:
2154  *  return           int    TCL_OK    - Success.
2155  *                          TCL_ERROR - Failure.
2156  */
tkpg_scrollbar_callback(TkPgplot * tkpg,Tcl_Interp * interp,char * widget,char * view,int argc,char * argv[])2157 static int tkpg_scrollbar_callback(TkPgplot *tkpg, Tcl_Interp *interp,
2158 				  char *widget, char *view, int argc,
2159 				  char *argv[])
2160 {
2161   int window_size;  /* The size of the window along the direction of motion */
2162   int pixmap_size;  /* The size of the pixmap along the direction of motion */
2163   int new_start_pos;/* The new pixmap coord of the top|left of the window */
2164   int old_start_pos;/* The old pixmap coord of the top|left of the window */
2165 /*
2166  * Fill in the current scroll-statistics along the requested direction.
2167  */
2168   if(*view == 'x') {
2169     window_size = Tk_Width(tkpg->tkwin);
2170     pixmap_size = pgx_pixmap_width(tkpg->pgx);
2171     old_start_pos = tkpg->scroll.x;
2172   } else {
2173     window_size = Tk_Height(tkpg->tkwin);
2174     pixmap_size = pgx_pixmap_height(tkpg->pgx);
2175     old_start_pos = tkpg->scroll.y;
2176   };
2177 /*
2178  * The first argument specifies what form of scrollbar command has
2179  * been received (see 'man scrollbar' for details).
2180  */
2181   if(argc < 1) {
2182     return tkpg_scrollbar_error(tkpg, interp, widget, view, argc, argv);
2183 /*
2184  * The moveto command requests a new start position as a
2185  * fraction of the pixmap size.
2186  */
2187   } else if(strcmp(argv[0], "moveto")==0) {
2188     double fractional_position;
2189     if(argc != 2)
2190       return tkpg_scrollbar_error(tkpg, interp, widget, view, argc, argv);
2191 /*
2192  * Read the fractional position.
2193  */
2194     if(Tcl_GetDouble(interp, argv[1], &fractional_position) == TCL_ERROR)
2195       return TCL_ERROR;
2196     new_start_pos = fractional_position * pixmap_size;
2197 /*
2198  * The "scroll" command specifies an increment to move the pixmap by
2199  * and the units to which the increment refers.
2200  */
2201   } else if(strcmp(argv[0], "scroll")==0) {
2202     int scroll_increment;
2203     if(argc != 3)
2204       return tkpg_scrollbar_error(tkpg, interp, widget, view, argc, argv);
2205 /*
2206  * Read the scroll-increment.
2207  */
2208     if(Tcl_GetInt(interp, argv[1], &scroll_increment) == TCL_ERROR)
2209       return TCL_ERROR;
2210 /*
2211  * The unit of the increment can either be "units", which in our case
2212  * translates to a single pixel, or "pages", which corresponds to the
2213  * width/height of the window.
2214  */
2215     if(strcmp(argv[2], "units")==0) {
2216       new_start_pos = old_start_pos + scroll_increment;
2217     } else if(strcmp(argv[2], "pages")==0) {
2218       int page_size = window_size - 2 *
2219 	(tkpg->highlight_thickness + tkpg->borderWidth);
2220       if(page_size < 0)
2221 	page_size = 0;
2222       new_start_pos = old_start_pos + scroll_increment * page_size;
2223     } else {
2224       return tkpg_scrollbar_error(tkpg, interp, widget, view, argc, argv);
2225     };
2226   } else {
2227     Tcl_AppendResult(interp, "Unknown xview command \"", argv[0], "\"", NULL);
2228     return TCL_ERROR;
2229   };
2230 /*
2231  * Keep the pixmap visible.
2232  */
2233   if(new_start_pos < 0 || window_size > pixmap_size) {
2234     new_start_pos = 0;
2235   } else if(new_start_pos + window_size > pixmap_size) {
2236     new_start_pos = pixmap_size - window_size;
2237   };
2238 /*
2239  * Record the top left corner of the new scrolling-area.
2240  */
2241   if(*view == 'x')
2242     tkpg->scroll.x = new_start_pos;
2243   else
2244     tkpg->scroll.y = new_start_pos;
2245 /*
2246  * Update the scrolled area and the scrollbar slider.
2247  */
2248   tkpg_update_scroll_bars(tkpg);
2249   return TCL_OK;
2250 }
2251 
2252 /*.......................................................................
2253  * This is a private error-return function of tkpg_scrollbar_callback().
2254  *
2255  * Input:
2256  *  tkpg        TkPgplot *  The widget record.
2257  *  interp    Tcl_Interp *  The TCL intrepreter.
2258  *  widget          char *  The name of the PGPLOT widget.
2259  *  view            char *  "xview" or "yview".
2260  *  argc             int    The number of arguments in argv.
2261  *  argv            char ** The array of 'argc' configuration arguments.
2262  * Output:
2263  *  return           int    TCL_ERROR and the context of the error
2264  *                          is recorded in interp->result.
2265  */
tkpg_scrollbar_error(TkPgplot * tkpg,Tcl_Interp * interp,char * widget,char * view,int argc,char * argv[])2266 static int tkpg_scrollbar_error(TkPgplot *tkpg, Tcl_Interp *interp,
2267 			       char *widget, char *view, int argc,
2268 			       char *argv[])
2269 {
2270   int i;
2271   Tcl_AppendResult(interp, "Bad command: ", widget, " ", view, NULL);
2272   for(i=0; i<argc; i++)
2273     Tcl_AppendResult(interp, " ", argv[i], NULL);
2274   Tcl_AppendResult(interp, "\nAfter \"widget [xy]view\", use one of:\n \"moveto <fraction>\" or \"scroll -1|1 units|pages\"", NULL);
2275   return TCL_ERROR;
2276 }
2277 
2278 
2279 /*.......................................................................
2280  * Implement the Tcl world function. This converts an X-window
2281  * pixel coordinate to the corresponding PGPLOT world coordinate.
2282  *
2283  * Input:
2284  *  tkpg        TkPgplot *  The widget record.
2285  *  interp    Tcl_Interp *  The TCL intrepreter.
2286  *  widget          char *  The name of the PGPLOT widget.
2287  *  argc             int    The number of configuration arguments.
2288  *  argv            char ** The array of 'argc' configuration arguments.
2289  *                           [0] The coordinate axes to convert, from:
2290  *                                "x"  - Convert an X-axis coord.
2291  *                                "y"  - Convert a Y-axis coord.
2292  *                                "xy" - Convert a an X Y axis pair.
2293  *                           [1] An X-axis pixel coordinate if [0][0] is
2294  *                               'x'.
2295  *                               A Y-axis pixel coordinate if [0][0] is
2296  *                               'y'.
2297  *                           [2] This is only expected if [0]=="xy". It
2298  *                               should then contain the Y-axis
2299  *                               coordinate to be converted.
2300  * Output:
2301  *  return           int    TCL_OK    - Success.
2302  *                          TCL_ERROR - Failure.
2303  */
tkpg_tcl_world(TkPgplot * tkpg,Tcl_Interp * interp,char * widget,int argc,char * argv[])2304 static int tkpg_tcl_world(TkPgplot *tkpg, Tcl_Interp *interp,
2305 			 char *widget, int argc, char *argv[])
2306 {
2307   int xpix, ypix;     /* The input X window coordinate */
2308   float rbuf[2];      /* The conversion buffer */
2309   char *axis;         /* The axis specification string */
2310   enum {BAD_AXIS, X_AXIS, Y_AXIS, XY_AXIS}; /* Enumerated axis type */
2311   int axtype;         /* The decoded axis type */
2312   char *usage = " world [x <xpix>]|[y <xpix>]|[xy <xpix> <ypix>]";
2313 /*
2314  * Check that an axis specification argument has been provided.
2315  */
2316   if(argc < 1) {
2317     Tcl_AppendResult(interp, "Usage: ", widget, usage, NULL);
2318     return TCL_ERROR;
2319   };
2320 /*
2321  * Decode the axis type and check the expected argument count.
2322  */
2323   axis = argv[0];
2324   axtype = BAD_AXIS;
2325   switch(*axis++) {
2326   case 'x':
2327     switch(*axis++) {
2328     case 'y':
2329       if(*axis == '\0' && argc == 3)
2330 	axtype = XY_AXIS;
2331       break;
2332     case '\0':
2333       if(argc == 2)
2334 	axtype = X_AXIS;
2335       break;
2336     };
2337     break;
2338   case 'y':
2339     if(*axis == '\0' && argc == 2)
2340       axtype = Y_AXIS;
2341     break;
2342   };
2343 /*
2344  * Unrecognised axis description?
2345  */
2346   if(axtype == BAD_AXIS) {
2347     Tcl_AppendResult(interp, "Usage: ", widget, usage, NULL);
2348     return TCL_ERROR;
2349   };
2350 /*
2351  * Get the pixel coordinates to be converted.
2352  */
2353   switch(axtype) {
2354   case X_AXIS:
2355     if(Tcl_GetInt(interp, argv[1], &xpix) == TCL_ERROR)
2356       return TCL_ERROR;
2357     ypix = 0;
2358     break;
2359   case Y_AXIS:
2360     xpix = 0;
2361     if(Tcl_GetInt(interp, argv[1], &ypix) == TCL_ERROR)
2362       return TCL_ERROR;
2363     break;
2364   case XY_AXIS:
2365     if(Tcl_GetInt(interp, argv[1], &xpix) == TCL_ERROR ||
2366        Tcl_GetInt(interp, argv[2], &ypix) == TCL_ERROR)
2367       return TCL_ERROR;
2368     break;
2369   };
2370 /*
2371  * Convert the pixel coordinates to world coordinates.
2372  */
2373   pgx_win2dev(tkpg->pgx, xpix, ypix, rbuf);
2374   pgx_dev2world(tkpg->pgx, rbuf);
2375 /*
2376  * Write the world coordinate(s) into the reply string.
2377  */
2378   switch(axtype) {
2379   case X_AXIS:
2380     Tcl_PrintDouble(interp, rbuf[0], tkpg->buffer);
2381     Tcl_AppendResult(interp, tkpg->buffer, NULL);
2382     break;
2383   case Y_AXIS:
2384     Tcl_PrintDouble(interp, rbuf[1], tkpg->buffer);
2385     Tcl_AppendResult(interp, tkpg->buffer, NULL);
2386     break;
2387   case XY_AXIS:
2388     Tcl_PrintDouble(interp, rbuf[0], tkpg->buffer);
2389     Tcl_AppendResult(interp, tkpg->buffer, NULL);
2390     Tcl_PrintDouble(interp, rbuf[1], tkpg->buffer);
2391     Tcl_AppendResult(interp, tkpg->buffer, NULL);
2392     break;
2393   };
2394   return TCL_OK;
2395 }
2396 
2397 /*.......................................................................
2398  * Implement the Tcl pixel function. This converts PGPLOT world
2399  * coordinates to X-window pixel coordinates.
2400  *
2401  * Input:
2402  *  tkpg        TkPgplot *  The widget record.
2403  *  interp    Tcl_Interp *  The TCL intrepreter.
2404  *  widget          char *  The name of the PGPLOT widget.
2405  *  argc             int    The number of configuration arguments.
2406  *  argv            char ** The array of 'argc' configuration arguments.
2407  *                           [0] The coordinate axes to convert, from:
2408  *                                "x"  - Convert an X-axis coord.
2409  *                                "y"  - Convert a Y-axis coord.
2410  *                                "xy" - Convert a an X Y axis pair.
2411  *                           [1] An X-axis world coordinate if [0][0] is
2412  *                               'x'.
2413  *                               A Y-axis world coordinate if [0][0] is
2414  *                               'y'.
2415  *                           [2] This is only expected if [0]=="xy". It
2416  *                               should then contain the Y-axis
2417  *                               coordinate to be converted.
2418  * Output:
2419  *  return           int    TCL_OK    - Success.
2420  *                          TCL_ERROR - Failure.
2421  */
tkpg_tcl_pixel(TkPgplot * tkpg,Tcl_Interp * interp,char * widget,int argc,char * argv[])2422 static int tkpg_tcl_pixel(TkPgplot *tkpg, Tcl_Interp *interp,
2423 			 char *widget, int argc, char *argv[])
2424 {
2425   double wx, wy;      /* The world X and Y coordinates to be converted */
2426   int xpix, ypix;     /* The output X window coordinate */
2427   float rbuf[2];      /* The conversion buffer */
2428   char *axis;         /* The axis specification string */
2429   enum {BAD_AXIS, X_AXIS, Y_AXIS, XY_AXIS}; /* Enumerated axis type */
2430   int axtype;         /* The decoded axis type */
2431   char *usage = " pixel [x <x>]|[y <x>]|[xy <x> <y>]";
2432 /*
2433  * Check that an axis specification argument has been provided.
2434  */
2435   if(argc < 1) {
2436     Tcl_AppendResult(interp, "Usage: ", widget, usage, NULL);
2437     return TCL_ERROR;
2438   };
2439 /*
2440  * Decode the axis type and check the expected argument count.
2441  */
2442   axis = argv[0];
2443   axtype = BAD_AXIS;
2444   switch(*axis++) {
2445   case 'x':
2446     switch(*axis++) {
2447     case 'y':
2448       if(*axis == '\0' && argc == 3)
2449 	axtype = XY_AXIS;
2450       break;
2451     case '\0':
2452       if(argc == 2)
2453 	axtype = X_AXIS;
2454       break;
2455     };
2456     break;
2457   case 'y':
2458     if(*axis == '\0' && argc == 2)
2459       axtype = Y_AXIS;
2460     break;
2461   };
2462 /*
2463  * Unrecognised axis description?
2464  */
2465   if(axtype == BAD_AXIS) {
2466     Tcl_AppendResult(interp, "Usage: ", widget, usage, NULL);
2467     return TCL_ERROR;
2468   };
2469 /*
2470  * Get the pixel coordinates to be converted.
2471  */
2472   switch(axtype) {
2473   case X_AXIS:
2474     if(Tcl_GetDouble(interp, argv[1], &wx) == TCL_ERROR)
2475       return TCL_ERROR;
2476     wy = 0;
2477     break;
2478   case Y_AXIS:
2479     wx = 0;
2480     if(Tcl_GetDouble(interp, argv[1], &wy) == TCL_ERROR)
2481       return TCL_ERROR;
2482     break;
2483   case XY_AXIS:
2484     if(Tcl_GetDouble(interp, argv[1], &wx) == TCL_ERROR ||
2485        Tcl_GetDouble(interp, argv[2], &wy) == TCL_ERROR)
2486       return TCL_ERROR;
2487     break;
2488   };
2489 /*
2490  * Convert the world coordinate to pixel coordinates.
2491  */
2492   rbuf[0] = wx;
2493   rbuf[1] = wy;
2494   pgx_world2dev(tkpg->pgx, rbuf);
2495   pgx_dev2win(tkpg->pgx, rbuf, &xpix, &ypix);
2496 /*
2497  * Write the pixel coordinate(s) into the reply string.
2498  */
2499   switch(axtype) {
2500   case X_AXIS:
2501     sprintf(tkpg->buffer, "%d", xpix);
2502     Tcl_AppendResult(interp, tkpg->buffer, NULL);
2503     break;
2504   case Y_AXIS:
2505     sprintf(tkpg->buffer, "%d", ypix);
2506     Tcl_AppendResult(interp, tkpg->buffer, NULL);
2507     break;
2508   case XY_AXIS:
2509     sprintf(tkpg->buffer, "%d %d", xpix, ypix);
2510     Tcl_AppendResult(interp, tkpg->buffer, NULL);
2511     break;
2512   };
2513   return TCL_OK;
2514 }
2515 
2516 /*.......................................................................
2517  * Implement the Tcl "return PGPLOT id" function.
2518  *
2519  * Input:
2520  *  tkpg        TkPgplot *  The widget record.
2521  *  interp    Tcl_Interp *  The TCL intrepreter.
2522  *  widget          char *  The name of the PGPLOT widget.
2523  *  argc             int    The number of configuration arguments.
2524  *  argv            char ** The array of 'argc' configuration arguments.
2525  *                          (None are expected).
2526  * Output:
2527  *  return           int    TCL_OK    - Success.
2528  *                          TCL_ERROR - Failure.
2529  */
tkpg_tcl_id(TkPgplot * tkpg,Tcl_Interp * interp,char * widget,int argc,char * argv[])2530 static int tkpg_tcl_id(TkPgplot *tkpg, Tcl_Interp *interp,
2531 		      char *widget, int argc, char *argv[])
2532 {
2533 /*
2534  * There shouldn't be any arguments.
2535  */
2536   if(argc != 0) {
2537     Tcl_AppendResult(interp, "Usage: ", widget, " id", NULL);
2538     return TCL_ERROR;
2539   };
2540 /*
2541  * Return the id in the Tcl result string.
2542  */
2543   sprintf(tkpg->buffer, "%d", tkpg->pgslct_id);
2544   Tcl_AppendResult(interp, tkpg->buffer, NULL);
2545   return TCL_OK;
2546 }
2547 
2548 /*.......................................................................
2549  * Implement the Tcl "return PGPLOT device specifier" function.
2550  *
2551  * Input:
2552  *  tkpg        TkPgplot *  The widget record.
2553  *  interp    Tcl_Interp *  The TCL intrepreter.
2554  *  widget          char *  The name of the PGPLOT widget.
2555  *  argc             int    The number of configuration arguments.
2556  *  argv            char ** The array of 'argc' configuration arguments.
2557  *                          (None are expected).
2558  * Output:
2559  *  return           int    TCL_OK    - Success.
2560  *                          TCL_ERROR - Failure.
2561  */
tkpg_tcl_device(TkPgplot * tkpg,Tcl_Interp * interp,char * widget,int argc,char * argv[])2562 static int tkpg_tcl_device(TkPgplot *tkpg, Tcl_Interp *interp,
2563 			   char *widget, int argc, char *argv[])
2564 {
2565 /*
2566  * There shouldn't be any arguments.
2567  */
2568   if(argc != 0) {
2569     Tcl_AppendResult(interp, "Usage: ", widget, " device", NULL);
2570     return TCL_ERROR;
2571   };
2572 /*
2573  * Return the device specifier in the Tcl result string.
2574  */
2575   Tcl_AppendResult(interp, tkpg->device, NULL);
2576   return TCL_OK;
2577 }
2578 
2579 /*.......................................................................
2580  * Return the toplevel window ID of a given tk pathname.
2581  *
2582  * Input:
2583  *  interp  Tcl_Interp *  The TCL intrepreter.
2584  *  main_w   Tk_Window    The main window of the application.
2585  *  path          char *  The tk path name of a window.
2586  * Output:
2587  *  return   Tk_Window    The top-level window of the path, or NULL if
2588  *                        it doesn't exist. In the latter case an error
2589  *                        message will have been appended to interp->result.
2590  */
tkpg_toplevel_of_path(Tcl_Interp * interp,Tk_Window main_w,char * path)2591 static Tk_Window tkpg_toplevel_of_path(Tcl_Interp *interp, Tk_Window main_w,
2592 				       char *path)
2593 {
2594   char *endp;   /* The element in path[] following the first path component */
2595   char *first;  /* A copy of the first component of the pathname */
2596   int length;   /* The length of the first component of the pathname */
2597   Tk_Window w;  /* The Tk window of the first component of the pathname */
2598 /*
2599  * The first character of the path should be a dot.
2600  */
2601   if(!path || *path == '\0' || *path != '.') {
2602     Tcl_AppendResult(interp, "Unknown window: ", path ? path : "(null)",
2603 		     NULL);
2604     return NULL;
2605   };
2606 /*
2607  * Find the end of the first component of the pathname.
2608  */
2609   for(endp=path+1; *endp && *endp != '.'; endp++)
2610     ;
2611   length = endp - path;
2612 /*
2613  * Make a copy of the name of the first component of the path name.
2614  */
2615   first = malloc(length + 1);
2616   if(!first) {
2617     Tcl_AppendResult(interp, "Ran out of memory while finding toplevel window.",
2618 		     NULL);
2619     return NULL;
2620   };
2621   strncpy(first, path, length);
2622   first[length] = '\0';
2623 /*
2624  * Lookup the corresponding window.
2625  */
2626   w = Tk_NameToWindow(interp, first, main_w);
2627 /*
2628  * Discard the copy.
2629  */
2630   free(first);
2631 /*
2632  * If the window doesn't exist, Tk_NameToWindow() is documented to place
2633  * an error message in interp->result, so just return the error condition.
2634  */
2635   if(!w)
2636     return NULL;
2637 /*
2638  * If the looked up window is a toplevel window return it, otherwise
2639  * the toplevel for the specified path must be the main window.
2640  */
2641   return Tk_IsTopLevel(w) ? w : main_w;
2642 }
2643