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