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)
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: #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.
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: #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