This article gives you a high-level introduction to programming with XView, a GUI toolkit that complements the OpenLook interface.
There are many window systems out there in the computer world—some are taking control of the desktop, while others are still strong in their workstation niche. Just as most of us would prefer to program in a high-level language rather than assembly, XView provides us with a high-level approach to GUI development. XView components come with default settings that make sense, so that a newly created button, for example, will look and act “correct” as soon as it is made. The API for XView is at once both simple and extensive. Simple, because we can do most jobs using only three different library routines, and extensive because we can control nearly all aspects of each component with those routines.
Let's jump right in and take a look at the simple XView program shown in Listing 1. Make sure you have XView and the OpenLook libraries on your system; otherwise, you'll get compile errors. Put this example into a file called sample.c and compile it using the following command:
cc -o sample -I$OPENWINHOME/include\ -L$OPENWINHOME/lib\ -L/usr/X11/lib sample.c -lxview -lolgx -lX11
where OPENWINHOME is the home directory on your system for OpenWindows, usually /usr/openwin.
When you run sample.c, you'll be presented with a simple text editor, a functionality of the standard XView text sub-window component. Type in the text window, using the keyboard cursor keys to move around. Highlight words, lines and paragraphs with mouse clicks and drags. The right mouse button brings up a menu to open, save and insert files, exit the text and search for strings. Grab the end-stop of the slider and pull it down—you now have two views of the same text.
This is truly neat, but how did we get here? Let's walk through the sample program together.
First, we include two header files: one for generic XView information and one for the text sub-window component. If we had additional components (perhaps a pair of push buttons to open and save files), we would also include the header file for those panel components.
Next, inside of our main routine, we declare a Frame variable, which is used by XView as a place to put other components. It becomes an invisible layer between our text sub-window and the window decorations from the window manager.
Next, we call xv_init to initialize XView and pass the command-line arguments to it. (Check the xview man page for all the command-line options.) With this xv_init call, we first come upon a strong theme that runs through all XView calls—a null terminated list of attribute, value pairs. The first argument to xv_init is defined in the XView headers. xv_init expects to see two more arguments: the address of main's argument count and the argument values. To tell xv_init that we have nothing more for it to do, we pass one final argument of NULL to terminate the argument list.
On the next line, we create our frame, using xv_create. In the first argument we specify the parent of this component. Since we're just creating the outermost frame, we have no parent, and we use XV_NULL. The second argument specifies the type of component we want to create, in this case a FRAME. Notice that the remaining arguments form a null-terminated list of following attribute, value pairs. For example, the following attribute, value pair:
FRAME_LABEL, argv[0]
sets the label of the frame, which becomes the text on the window's title bar. If we wanted to set the size of the frame on the screen, we would simply add two pairs to set the width and height (before the terminating NULL):
XV_HEIGHT, 200, XV_WIDTH, 400,Similar attributes are used to set the maximum and minimum sizes of the frame, the initial position on the screen, the header and footer, etc.
Moving on, we create the text sub-window. We pass in our frame variable to this xv_create call, because we want the frame to become the parent of the text sub-window, affecting its size and position. The type of component we're creating is TEXTSW for a text sub-window, and we are adding no other options to it (although many are available).
Next, we call xv_main_loop, which handles all X events for the window that appears on the screen. It calls lower-level xlib routines to create the window, slider and canvas. It listens for mouse and keyboard input and handles it. xv_main_loop doesn't return until the window is closed by the user. At that point, our program can quietly exit.
Before we go further, there are two other important XView routines we need to know: xv_set and xv_get. As their names imply, they are used to set and to get attribute values for XView components.
In sample.c, we saw how to set the size of the frame when it was created. We also could have set the size after it was created with the xv_set call in this way:
xv_set(frame, XV_HEIGHT, 200, XV_WIDTH, 400, NULL);
Although the vertical formatting isn't necessary, it shows more clearly what is happening: namely, using the frame component, we have set its height to 200 and its width to 400.
Suppose that at some point in the program, we needed to determine the position of the frame on the screen:
int x, y; x = (int)xv_get(frame, XV_X); y = (int)xv_get(frame, XV_Y);
This fragment retrieves the x and y coordinates of the frame on the screen. Yes, it looks like we violated the rule of using a null-terminated list, but since xv_get returns a single value, we fetch only one attribute; therefore, xv_get accepts only two arguments.
Let's extend this sample program by adding two buttons to it. One button will insert a fixed text string into the text sub-window, and the other button will erase the text sub-window. To do this, we will need a place to put them: namely, a control panel which is implemented in the PANEL package. The buttons themselves are panel items called PANEL_BUTTONs. We associate a subroutine, or “callback” routine, with a button, to be called when the user clicks on the button. The callback routines will manipulate the text sub-window. The second version of the sample.c program is shown in Listing 2.
Let's start with the new code inside our main. We created a panel inside the frame, positioned in the upper-left corner (x=0, y=0), extending to the right edge, 30 pixels tall and borderless.
Next, we added two buttons to the panel (not the frame), each with different button labels and different callback routines. For this example, XView handles the chore of positioning the buttons within the panel. If we wanted, we could use the XV_X and XV_Y attributes to position the buttons within the control panel.
Note that a hierarchy (see Figure 1) is forming. The frame is a parent of the panel and the text sub-window. The panel is a parent of the two buttons. When we resize the frame, its child components resize with it. The user interface could become quite sophisticated (i.e., complex) but still remain manageable, because of the relations that form among the frames, panels and other components. Our callback routines are invoked by other routines deep within XView and are passed the component and the event that produced this call (in this simple case, the button and the mouse-button-up event).
For the insert string callback routine, we use the global handle to the text sub-window and call an auxiliary routine to insert the literal text into the text pane. For the clear_window callback routine, we use another helper function to reset the text sub-window, which erases all the text from its pane. Although we use xv_set and xv_get to manipulate the attributes of the XView components, some components have a nice set of helper functions to make our job easier. The text sub-window is one such component.
Although this sample program doesn't give you an earth-shattering application, it does show you the core features of XView:
attribute,value pairs
null-terminated lists
xv_init and xv_main_loop for setup and event handling
xv_create, xv_set, xv_get for component attributes
callback functions for event handling
This article has demonstrated the simplicity, elegance and beauty of XView. Perhaps you will be inspired to look into it further.