/XMOTIF
XmPgplot
widget is the name that was given to the
widget as the first argument of
XtVaCreateManagedWidget()
when the widget
was created. The full device specification used to open a given
widget to PGPLOT with
cpgbeg()
or
cpgopen()
can
also be obtained through the
xmp_device_name()
convenience function.
XmPgplot
widget window is 256×256
pixels. This can be overridden by setting the
XmNheight
and XmNwidth
X resources when
creating the widget.
PseudoColor
,
StaticColor
, GrayScale
,
StaticGray
, and TrueColor
are supported. By
default, colors are allocated from the colormap used by the parent
window of the widget, unless it has too few colors, in which case the
window uses the black and white pixels of the screen to implement
a monochrome window.
Note that with PseudoColor
and GrayScale
colormaps, colors of already drawn graphics will respond to changes
to their color representations, whereas with other colormap types,
color representation changes will only effect the colors of
subsequently drawn graphics.
An asynchronous alternative
to the cpgcurs()
and cpgband()
procedures is provided. This provides the same functionality as
cpgband()
except
that once armed, cursor input is delivered to the application via
callback functions.
Facilities are also provided to support applications that want to
handle cursor input themselves via
XtAddEventHandler()
. This includes optional
augmentation of the X cursor with rubber-bands, and functions for
converting between X-window coordinates and world coordinates.
XmPgplot
widgets that can be open to PGPLOT
simultaneously. Note, however that PGPLOT does set limits on the
total number of open devices. Currently (April 96) this limit is
8.
XmPgplot.h
XmPgplot
widget functions or resources are
used.
cpgplot.h
libXmPgplot.a
libXmPgplot.a
appear
before the main PGPLOT library. For example, a
snapshot of the link line under UNIX would be:
-lXmPgplot -lcpgplot -lpgplot -lXm -lXt -lX11
libcpgplot.a
and libpgplot.a
XMPGPLOT.OBJ
XMPGPLOT.OBJ
in addition to the the main
PGPLOT library.
GRPCKG.OLB and CPGPLOT.OLB
XMOTIF.OPT
XmPgplot
widget driver. It is a
linker options file that lists the PGPLOT, Motif,
X-toolkit and X11 libraries. It should be used like:
LINK PROGRAM,PGPLOT_DIR:PGMOTIF.OPT/OPT
XtVaCreateManagedWidget()
when each PGPLOT widget is
created, but they can also be placed in app-defaults files or, on
POSIX-compliant systems, the .Xdefaults
file in your
home directory. Under VMS this file is called
DECW$USER_DEFAULTS:DECW$XDEFAULTS.DAT
. Note that by
default DECW$USER_DEFAULTS is defined as SYS$LOGIN.
XmPgplot
widgets allocate colors from the Visual and
Colormap specified via the XmNcolormap
and
XmNvisual
resources. These default to
CopyFromParent
, as they do in most other
widgets. As a result, XmPgplot
widgets normally allocate
colors from the default colormap and visual of the screen. If
the default colormap of the screen is insufficient for your
needs and you need to allocate a private colormap then I
suggest that you register the result with the top-level
widget of the application rather than with a specific
XmPgplot
widget. This will then be inherited by
all widgets in the application, including XmPgplot
widgets. The steps to perform to allocate a private colormap
and assign it to the top-level widget of the application are
as follows:
XtVaAppinitialize()
as you normally
would.
w_top
.
XtDisplay(w_top)
to acquire the
Display
Xlib context object.
XtVaSetValues(w_top, XmNcolormap, colormap, XmNvisual, visual, NULL);
Alternatively if you really want to assign private
colormaps to individual XmPgplot
widgets then note that to get
the window manager to automatically install the colormap of a
widget window, one has to set the
WM_COLORMAP_WINDOWS
property on the
top-level window of the host application. The X toolkit
provides the function XtSetWMColormapWindows()
to do this.
Other X resources that effect the way that colors are allocated are:
XmpNminColors
XmpNmaxColors
XmpNmaxColors
resource values of the two
widgets add up to no more than 100.
XmPgplot
widgets use an
off screen pixmap for buffering and backing store. This is
kept fixed in size for the duration of each page, and is only
resized to fit the window when
cpgpage()
is
called. If the widget window is resized to a smaller size, then
part of the plot will become obscured until the next page is
started. The XmPgplot
widget driver supports two
optional ways to improve this situation.
XmPgplot
widget to be a Motif
ScrolledWindow
widget then the PGPLOT
widget will automatically adopt the scroll-bars of its
parent and arrange that they scroll the underlying pixmap
relative to the window. Then if the window is resized to a
smaller size the scroll bars can be used to see the
obscured part of the plot.
cpgpage()
procedure so as to synchronize the size of the pixmap with
the window, and then re-draw any graphics that you want to
re-appear there. Registering such a callback is just a
matter of calling XtAddCallback()
with the
PGPLOT widget as the first argument,
XmNresizeCallback
as the second argument,
your callback function as the third argument and an
optional client-context data argument as the final
argument. The callback function should be declared like:
void
whatever(w,
client_data, call_data)
Widget w
XmPgplot
widget that has been resized.
XtPointer client_data
client_data
pointer that was
registered with XtAddCallback()
. It should
be cast back to its actual type before use.
XtPointer call_data
NULL
.
cpgcurs()
and
cpgband()
cursor-input functions are not recommended for use in Motif
applications because they block the toolkit event loop. Instead
an alternative callback system, designed to mimic cpgband()
but
without blocking the event loop, has been provided. Arming and
disarming the cursor is achieved through two functions:
int xmp_arm_cursor(widget, mode, xref, yref, callback,
client_data)
mode
argument. It
also optionally registers a callback function to be passed
the world coordinates and character of key-press and
button-press events. The cursor can subsequently be disarmed
via a call to xmp_disarm_cursor()
. The cursor is
automatically disarmed when the device is closed to PGPLOT
by
cpgclos()
or
cpgend()
, and
whenever the cursor is re-armed.
Note that when the cursor is augmented, the rubber band is
redrawn every time that the PGPLOT buffer is flushed. This
happens after every call to PGPLOT functions unless
sensible use is made of
cpgbbuf()
and
cpgebuf()
to
buffer sequential calls. Be sure to use these functions
properly if you don't want your PGPLOT code to be slow.
The arguments of xmp_arm_cursor()
are:
Widget widget
int mode
XMP_NORM_CURSOR
XMP_LINE_CURSOR
(xref,yref)
and
the pointer.
XMP_RECT_CURSOR
(xref,yref)
and the pointer.
XMP_YRNG_CURSOR
xref
and the other through the
pointer.
XMP_XRNG_CURSOR
yref
and the other through the pointer.
XMP_HLINE_CURSOR
XMP_VLINE_CURSOR
XMP_CROSS_CURSOR
float xref, yref
XtCallbackProc callback
- The cursor-input callback
function, or 0 if not required.
call_data
argument is a pointer to a struct of the following
declaration, cast to (XtPointer)
.
typedef struct { float x,y; /* The world-coordinate position of the cursor */ char key; /* The key pressed by the user. Mouse buttons */ /* are encode as 'A','D','X' (left to right) */ } XmpCursorCallbackStruct;
The callback function will be called whenever a key (if the widget has keyboard input focus) or pointer button is pressed while the pointer is in the associated PGPLOT widget.
void *client_data
- Client context data.
The return value of xmp_arm_cursor()
is 0 if
successful or 1 if the specified widget is not an open PGPLOT
widget or the callback
argument is NULL
.
int xmp_disarm_cursor(widget)
xmp_arm_cursor()
for the specified
widget. Note that this function can be called even when the
cursor has not been previously armed or when the widget is
not open to PGPLOT.
The xmp_disarm_cursor()
function returns
0 if successful or 1 if the specified widget is not a PGPLOT
widget.
xmp_arm_cursor()
provides a
convenient but limited cursor-input facility, designed to
mimic
cpgband()
.
What it doesn't provide is a general means of responding to
the full variety of X-window events. For example, it doesn't
report Motion events via its callback. This section
describes the facilities available for establishing one's
own cursor-input handlers.
To display a rubber band cursor without using its callback
facility, call xmp_arm_cursor()
with its
callback argument specified as 0. This tells the widget's
cursor event handler not to select for button-press and
key-press events and disables the special interpretation of
mouse-buttons and Tab keys with respect to
keyboard-focus management .
To register input event handlers externally to
xmp_arm_cursor()
use the standard
XtAddEventHander()
. In order to convert from
the reported X-window coordinates to PGPLOT
world-coordinates, use the
xmp_pixel_to_world()
function described later.
For example, having created a Label widget
w_label
and a PGPLOT widget
w_plot
, the following code would result in a
world-coordinate readout of the cursor being displayed in
the label whenever the cursor was moved over the PGPLOT
widget.
XtAddEventHandler(w_plot, PointerMotionMask, False, report_cursor, (XtPointer)w_label); ... static void report_cursor(Widget w, XtPointer context, XEvent *event, Boolean *continue_dispatch) { Widget w_label = (Widget) context; char text[80]; float wx, wy; /* * Convert from X-window coordinates to world coordinates. */ if(xmp_pixel_to_world(w, event->xmotion.x, event->xmotion.y, &wx, &wy) == 0) { sprintf(text, "X=%-10g Y=%-10g", wx, wy); XtVaSetValues(w_label, XtVaTypedArg, XmNlabelString, XmRString, text, strlen(text)+1, NULL); } *continue_dispatch = True; }
XmPgplot
cursor is armed, then read on about
the awkward issue of how an XmPgplot
widget
receives the input focus.
Alternatively, if you only care about receiving cursor input
from button presses, then you should set the
XmNtraversalOn
widget resource to
False
, otherwise some button-presses will be
lost to focus management.
When a Motif application has the keyboard input focus, the
Motif library decides which of the application's widgets will
receive keyboard input. This is independent of the method used
by the window manager for top-level windows. Motif supports two
schemes, either of which can be selected by setting the value of the
XmNkeyboardFocusPolicy
resource of the top-level widget
to one of the following values:
XmEXPLICIT
(The default focus policy).
This implements a click-to-focus model whereby the user must explicitly set the input focus either by pressing the TAB key repeatedly until the required widget is reached, or by moving the pointer into the widget window and pressing a mouse button.
Unfortunately, this means that when a PGPLOT widget
doesn't have the keyboard input focus, the first button
press is used to acquire the input focus, rather than
being reported as cursor-input. Similarly, once the
widget has the keyboard input focus, TAB characters are
used to move the input focus to the next widget rather
than being delivered as cursor input. This is confusing
to users, so it is better to either use the
XmPOINTER
focus
policy, or to tell the
widget that keyboard input is not desired, as described
above.
In order to allow users to determine in advance whether an
XmPgplot
widget has input focus, the border of
the widget is changed from the background color of its
parent to white. This color was chosen so as to be distinct
from the default black background color of
XmPgplot
widgets. The color and other aspects
of highlighting can be changed via the highlighting
resources of the Primitive
widget.
Note that X conventions discourage applications from
moving the keyboard focus unless under user direction.
However, in cases where the user presses a button who's
primary function is to activate an XmPgplot
cursor for user input, you might consider actively
setting the keyboard input focus to the respective
XmPgplot
widget via a statement of the
following form,
XmProcessTraversal(widget, XmTRAVERSE_CURRENT);placed just after the call to
xmp_arm_cursor(widget, ..)
. This statement
has no effect if the focus policy is not
XmEXPLICIT
.
XmPOINTER
In this scheme the widget in which the pointer lies is the one that receives keyboard input, and button presses are always unambiguously treated as cursor input. This avoids all keyboard focus complications. However, some users don't like it because they like to be able to move the pointer out of the window in which they are typing.
Note that the above resource values can either be hard-coded via
the call to XtVaAppInitialize()
or specified in
the application's app-defaults file.
If having read this, you still want to be told about
keyboard input in addition to mouse-button input, but you
don't like the way xmp_arm_cursor()
manages the
keyboard input focus, then you can write your own using the
facilities described above under
"Advanced cursor-input".
int xmp_pixel_to_world(widget, px, py, wx, wy)
Widget widget
int px, py
float *wx, *wy
wx
and
wy
will be assigned the PGPLOT world coordinates
that correspond to pixel px
, py
.
The function returns 0 on success or 1 if the widget is invalid.
int xmp_world_to_pixel(widget, wx, wy, px, py)
Widget widget
float wx, wy
int *px, *py
px
and
py
will be assigned the X-window coordinates
of the pixel nearest to world coordinate wx
,
wy
.
The function returns 0 on success or 1 if the widget is invalid.
int xmp_device_name(Widget widget)
cpgopen()
or
cpgbeg()
functions to open the specified XmPgplot
widget to
PGPLOT. An example of how this can be used with
cpgopen()
is shown later. The
returned string is part of the specified widget and must be
treated as read-only by the caller.
The form of the returned string is
"widget_name/XMOTIF"
where
widget_name
is the name that was given to the
widget when it was created.
If the specified widget is not a PGPLOT widget then an error
message will be emitted and the returned device name will be
"/null"
.
int xmp_device_id(Widget widget)
cpgopen()
function, PGPLOT returns an integral identifier. This may
then be used, via the
cpgslct()
function, to select which of the open PGPLOT devices is to
be the current graphics device. The
xmp_device_id()
convenience function returns
the PGPLOT identifier associated with the given XmPgplot
widget. This can be useful in callbacks, where the
Widget
argument passed to the callback function
can then be used to select the widget as the current PGPLOT
device.
If the Widget
argument passed to the
xmp_device_id()
function either hasn't been
opened to PGPLOT or hasn't been re-opened after being
closed, then xmp_device_id()
emits an error
message and returns 0.
XmPgplot
widgets inherit all of the X
resources of
the Core
and Primitive
Motif widget
classes.
Note that the XmNbackground
and
XmNforeground
resources change the color
representations of pgplot color indexes 0 and 1. The default
background is black and the default foreground is white. Thus to
create an XmPgplot
widget with these colors swapped,
one could type:
plot = XtVaCreateManagedWidget("plot", xmPgplotWidgetClass, parent, XmNheight, 400, XmNwidth, 400, XmpNmaxColors, 50, XmNtraversalOn, False, XtVaTypedArg, XmNbackground, XmRString, "white", strlen("white")+1, XtVaTypedArg, XmNforeground, XmRString, "black", strlen("black")+1, NULL);
XtVaCreateManagedWidget
. The first argument
to XtVaCreateManagedWidget
must be the device name
by which you wish to refer to the widget in
cpgbeg()
or
cpgopen()
,
and the second argument must be
xmPgplotWidgetClass
. The third argument specifies
the parent widget. If you want the widget to have scroll bars then
this should be a Motif ScrollBar
widget. The
remaining arguments are a list of X resource-value pairs
terminated by a NULL
argument. These should be used
to configure the widget, via the resources listed earlier.
A non-variadic "convenience" function for creating a
PGPLOT widget is also available, called
XmCreatePgplot()
and the equivalent function to
create both a PGPLOT widget and an associated
ScrollBar
widget is called
XmCreateScrolledPgplot()
. Note that in both cases
you should apply XtManageChild()
to the returned
widget.
Before a PGPLOT widget can be used from PGPLOT it has to be
opened by calling
cpgbeg()
or
cpgopen()
.
This can be done any time after the PGPLOT widget has
been realized.
Note that PGPLOT now supports multiple open PGPLOT devices via
cpgopen()
,
cpgslct()
and
cpgclos()
.
You can thus create and have multiple PGPLOT widgets open to
PGPLOT simultaneously. If you do this, be sure to call
cpgslct()
in
each callback to ensure that the intended PGPLOT widget is
addressed. The id to pass to
cpgslct()
to
select a given XmPgplot
widget can be obtained via
the xmp_device_id()
convenience function.
Please see the previous sections on configuration, resize options and how to get cursor input.
An example of creating a PGPLOT widget as the child of a scroll bar widget is as follows:
#include <stdio.h> #include <X11/Intrinsic.h> #include <Xm/Xm.h> #include <Xm/ScrolledW.h> #include "XmPgplot.h" #include "cpgplot.h" int main(int argc, char *argv[]) { XtAppContext app; /* Application context */ Widget w_top; /* The top-level widget of the application */ Widget w_scroll; /* Scroll-bar widget */ Widget w_pgplot1; /* PGPLOT widget */ /* * Initialize Motif and request a pointer following * keyboard-focus policy. */ XtSetLanguageProc(NULL, NULL, NULL); w_top = XtVaAppInitialize(&app, "Pgplot", NULL, 0, &argc, argv, NULL, XmNkeyboardFocusPolicy, XmPOINTER, NULL); /* * Create a ScrollBar widget. */ w_scroll = XtVaCreateManagedWidget("pgplot_scrollbar", xmScrolledWindowWidgetClass, w_top, NULL); /* * Create a PGPLOT widget as a child of the scroll-bar widget. * Give it an initial size of 300x300 pixels and allow it to * allocate up to 60 colors from the colormap used by the parent * widget. */ w_pgplot1 = XtVaCreateManagedWidget("pgplot1", xmPgplotWidgetClass, w_scroll, XmNheight, 300, XmNwidth, 300, XmpNmaxColors, 60); /* * Create and display the application windows. */ XtRealizeWidget(w_top); /* * Open w_pgplot1 as the current PGPLOT device. */ if(cpgopen(xmp_device_name(w_pgplot1)) <= 0) { fprintf(stderr, "Failed to open PGPLOT widget: %s\n", xmp_device_name(w_pgplot1)); exit(1); }; /* * Proceed with the Xt event loop. */ XtAppMainLoop(app); /* NOT REACHED */ return 0; };
This example doesn't actually do anything useful except create a PGPLOT widget with scroll-bars and open it to PGPLOT. To make the example useful you would have to arrange for something to draw into the PGPLOT widget. This could be a work procedure, a callback registered to a communication stream for logging incoming data, or a push-button callback.
The pgmdemo
demo program provides a much more
complete example. The source code for it can be found in the PGPLOT
distribution in pgplot/drivers/xmotif/pgmdemo.c
. It is
compiled automatically when the xmdriv
driver is
uncommented in drivers.list
during PGPLOT
installation.
XmNtraversalOn
widget
resource to False
now correctly disables
keyboard-focus management. Previously this resource was
ignored.
xmp_arm_cursor
now
allows one to specify 0 for its callback argument. This allows
people to use the rubber-band cursor augmentation facility
without incuring the side-effects of its callback
facility. This paves the way for programmers to use
XtAddEventHandler
to register more advanced cursor-event handlers than would
otherwise be possible.
cpgbbuf()
and
cpgebuf()
prudently to buffer sequential pgplot calls.
XmNbackground
and XmNforeground
resources were being overriden by the default pgplot
background and foreground colors whenever a widget was opened
to pgplot. This made these resources useless. They now work
correctly. If not specified, the default background and
foreground colors will be black and white (as before) rather
than taking on the conventional blue and black Motif colors.
See the Inherited widget resources
section for more details.