hello.c:
#include <Xm/Xm.h>
#include <Xm/PushB.h>
void say_hello(w, client_data, event_data)
Widget w;
XtPointer client_data;
XtPointer event_data;
{
printf("Hello!\n");
}
main(argc, argv)
int argc;
char *argv[];
{
Widget toplevel, button;
XtAppContext app;
XmString label;
toplevel = XtVaAppInitialize(&app, "Hello", NULL, 0,
&argc, argv, NULL, NULL);
label = XmStringCreateSimple("Push here to say hello");
button = XtVaCreateManagedWidget("pushme", xmPushButtonWidgetClass, toplevel,
XmNlabelString, label,
NULL);
XmStringFree(label);
XtAddCallback(button, XmNactivateCallback, say_hello, NULL);
XtRealizeWidget(toplevel);
XtAppMainLoop(app);
}
To Compile the code you will need a makefile like this:
Makefile:
MOTIFHOME = /usr/dt
CFLAGS = -g -I$(MOTIFHOME)/include -I$(OPENWINHOME)/include
LIBS = -R$(MOTIFHOME)/lib -R$(OPENWINHOME)/lib -L$(MOTIFHOME)/lib -L$(OPENWINHOME)/lib -lXm -lXt -lX11
PROG = hello
OBJS = hello.c
$(PROG): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(PROG) $(LIBS)
Discussion Of Hello.c
The first function is a callback procedure and is called when the
button is pressed. All callbacks have the same parameters, the widget
that the message came from, client data and event data. The client data
can be used to pass a pointer or some other information across the application.
The first line of main() (XtVaAppInitialize) initializes the connection to the
X-server and passes the command line parameters (so that -display etc can be
interpreted). The function returns the id of a shell widget which is used as
the parent for the button. "Hello" is used for the application resource name.
XmStringCreateSimple creates an XmString which is used for the label of the
button. An XmString is a type which encapsulates a text-string and a font.
XmStringCreateSimple creates an XmString with the default font.
XtVaCreateManagedWidget makes the button widget. It's parent is the toplevel
widget. The first parameter is the resource name of the widget which can be
used for setting various resources from and .Xdefaults/Xresources file.
e.g.
Hello*pushme.fontList: -*-helvetica-bold-r-*-*-17-*-*-*-*-*-*-*
in a defaults file will tell the application to make the button appear with
the text in a different font (you can run "xfontsel" to choose a font-string).
Using resource files to specify some widget settings means that recompilation
is not needed for many visual changes, as the binary just needs to be re-run
with the new file.
XmStringFree frees the XmString data-structure (the widget takes a copy
of the XmString so it is not needed any more)
XtAddCallback tells the widget to call the say_hello function when the
button is activated.
XtRealizeWidget sets up the whole widget hierachy ready for use.
XtAppMainLoop is the programs event loop and execution will never get past
this line. It is an infinite loop which gets and dispatches events
forever.
Container.c
The next program shows a RowColumn container widget with two buttons
as children.
container.c:
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
void say_hello(w, client_data, event_data)
Widget w;
XtPointer client_data;
XtPointer event_data;
{
printf("Hello!\n");
}
void quit_program(w, client_data, event_data)
Widget w;
XtPointer client_data;
XtPointer event_data;
{
printf("Bye!\n");
exit(0);
}
main(argc, argv)
int argc;
char *argv[];
{
Widget toplevel, button1, button2, container;
XtAppContext app;
XmString label;
toplevel = XtVaAppInitialize(&app, "Container", NULL, 0,
&argc, argv, NULL, NULL);
container = XtVaCreateManagedWidget("box", xmRowColumnWidgetClass, toplevel,
NULL);
label = XmStringCreateSimple("Push here to say hello");
button1 = XtVaCreateManagedWidget("pushme", xmPushButtonWidgetClass, container,
XmNlabelString, label,
NULL);
XmStringFree(label);
XtAddCallback(button1, XmNactivateCallback, say_hello, NULL);
label = XmStringCreateSimple("Push here to quit");
button2 = XtVaCreateManagedWidget("pushme", xmPushButtonWidgetClass, container,
XmNlabelString, label,
NULL);
XmStringFree(label);
XtAddCallback(button2, XmNactivateCallback, quit_program, NULL);
XtRealizeWidget(toplevel);
XtAppMainLoop(app);
}
You should be able to alter the makefile from the first example to make it work on
this example and the subsequent ones.
Discussion Of Container.c
xmRowColumnWidgetClass is the class of a container widget. Any widgets which
are added to a RowColumn widget are constrained into rows and columns.
RowColumns have a few resources which can be added to change the layout
of the children.
If you add:
XmNorientation, XmHORIZONTAL,
to the argument list of the create line for the RowColumn, the RowColumn widget
will lay its children out horizontally.
A bulletin-board widget is another composite widget that can be used to hold
children widgets. Each child can have x and y values indicating a position.
Add "#include <Xm/BulletinB.h>" to the list of include files at the top
and change "xmRowColumnWidgetClass" to "xmBulletinBoardWidgetClass".
If you compile and run the program then the button widgets will both
be at 0,0 in the BulletinBoard. To position the buttons you need to add
some arguments to the functions that create the buttons:
e.g.
button1 = XtVaCreateManagedWidget("pushme", xmPushButtonWidgetClass, container,
XmNlabelString, label,
XmNx, 100,
XmNy, 100,
NULL);
button2 = XtVaCreateManagedWidget("pushme", xmPushButtonWidgetClass, container,
XmNlabelString, label,
XmNx, 200,
XmNy, 200,
NULL);
Text.c
The next program demonstrates the Text widget and the Form widget, another
type of composite widget. The program displays a scrolled-text window
with two buttons underneath it. If the window is resized then the buttons
remain under the text window after the text window resizes. One button
will print the text, contained in the text window, to the shell window
that the program was run from. The other button exits the program.
text.c:
#include <Xm/Xm.h>
#include <Xm/PushB.h>
#include <Xm/Text.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
void print_text(w, client_data, event_data)
Widget w;
XtPointer client_data;
XtPointer event_data;
{
Widget text = (Widget)client_data;
char *string;
string = XmTextGetString(text);
printf("%s\n", string);
free(string);
}
void quit_program(w, client_data, event_data)
Widget w;
XtPointer client_data;
XtPointer event_data;
{
exit(0);
}
main(argc, argv)
int argc;
char *argv[];
{
Widget toplevel, form, scrollw, button, text, container;
XtAppContext app;
XmString label;
Arg al[10];
int ac;
toplevel = XtVaAppInitialize(&app, "Editor", NULL, 0,
&argc, argv, NULL, NULL);
form = XtVaCreateManagedWidget("form", xmFormWidgetClass, toplevel,
NULL);
ac = 0;
XtSetArg(al[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
XtSetArg(al[ac], XmNscrollVertical, TRUE); ac++;
text = XmCreateScrolledText(form, "Text", al, ac);
XtManageChild(text);
scrollw = XtParent(text);
container = XtVaCreateManagedWidget("box", xmRowColumnWidgetClass, form,
XmNrightAttachment, XmATTACH_FORM,
XmNleftAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_FORM,
XmNorientation, XmHORIZONTAL,
NULL);
XtVaSetValues(scrollw,
XmNrightAttachment, XmATTACH_FORM,
XmNleftAttachment, XmATTACH_FORM,
XmNtopAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_WIDGET,
XmNbottomWidget, container,
NULL);
label = XmStringCreateSimple("Print");
button = XtVaCreateManagedWidget("pushme", xmPushButtonWidgetClass, container,
XmNlabelString, label,
NULL);
XmStringFree(label);
XtAddCallback(button, XmNactivateCallback, print_text, (XtPointer)text);
label = XmStringCreateSimple("Quit");
button = XtVaCreateManagedWidget("pushme", xmPushButtonWidgetClass, container,
XmNlabelString, label,
NULL);
XmStringFree(label);
XtAddCallback(button, XmNactivateCallback, quit_program, NULL);
XtRealizeWidget(toplevel);
XtAppMainLoop(app);
}
A form widget is used when you need to "join" widgets together so that you
can resize a window and maintain relative positions or alter window
sizes. This might be useful in a text editor, where a user wants to
resize the window to make a larger work area.
Children inside a form widget have resources which tell the form how to
manage them. e.g. XmNrightAttachment, XmATTACH_FORM indicates that the
right side of the widget should always be "joined" to the edge of the form.
XmNbottomAttachment, XmATTACH_WIDGET indicates that the bottom edge of the
widget is to be joined to the top of another widget which is specified by
XmNbottomWidget.
The argument lists for children within forms can become very complicated.
If you were developing a complicated interface with forms then it might be
best to use resource files for the attachment and positioning, saving
compilation time.
The first function in the program is a callback which prints the contents
of the text widget to the shell window. The id of the text widget is passed
via the clientdata parameter. This means that a global is not needed to
store the id of the text-widget, and that the callback could be used
by other buttons and text widgets. The "XtAddCallback" call after
the creation of the "Print" button passes the id of the text widget to
the button-callback. It must be cast to an XtPointer and then cast back to
a widget in the callback routine. This method can be used to pass
simple variables (ints, chars) or pointers to your own data-structures.
The text widget is created by the motif function-call "XmCreateScrolledText".
This is known as a Motif convienience function. It actually creates two
widgets, a ScrolledWindow containing a Text widget. The id of the text
widget is returned by the call, so you must find its parent (the scrolled
window by using the Xt function "XtParent". Motif convienience functions
do not automatically manage the child so you must ensure that XtManageChild()
is called for the widget to appear.
Convienience functions must use the arg-array technique for passing
resource settings, you cannot use the "Va" variable arguments method
as in XtVaCreate...
If you compile and run the program you will see a small text window
with two buttons beneath it. The text widget has resources which control
the number of rows and columns displayed. These are XmNrows and XmNcolumns.
Modify the program so that the text widget starts up with a size of 80x25.
Also try setting XmNwordWrap to true. You can see that, with a bit more
code to load and save files, it is possible to write a full text-editor.
Now Read X-Windows Workshop Part Two
Sean Butler
sean@comp.lancs.ac.uk
Doc Status: Under Development...
This page is based upon notes originally written by
AAI/AI-ED Group
Computing Department
Lancaster University