
The Xm++ User's Guide

#####################


Copyright (c) 1994 Bernhard Strassl, Sabine Binder
Vienna User Interface Group
Institute for Applied Computer Science and Information Systems
University of Vienna, Austria


CONTENTS
========

1.	Introduction
2.	Building Xm++ Programs
3.	Windows, Subpanes, and Callbacks
3.1.	Creating a Window
3.2.	Controlling the Application Window Size
3.3.	Adding Another Subpane
3.4.	Callbacks
4.	Menues
4.1.	A Menu Bar
4.2.	Submenues
5.	Dialogs
5.1.	System Dialogs
5.2.	Introducing Styles
5.3.	User Defined Dialogs
5.4.	Adding Action
6.	The Dialog Editor
7.	Common Control member functions
7.1.	Keyboard Traversal
7.2.	Images in Controls
8.	Toolbars and Toolboxes

Appendix A - X11 Resources
1.	Menues
2. 	Dialogs


1. INTRODUCTION
===============

Xm++ is an application framework for the C++ language that simplifies building applications on 
UNIX / X Window and TIP  platforms. 

The aim of Xm++ is to achieve the simplicity of Smalltalk in building user interfaces while 
maintaining the advantages of C++. It completely encapsulates the API of any underlying toolkit 
like OSF Motif, Athena widgets or the TIP RCI (Remote Client Iinterface) library.

Xm++ consists of C++ classes which implement their behavior using widgets of the underlying 
toolkits. But these classes were not designed by simply writing C++ interfaces for some toolkit's 
widget API. The Xm++ class hierarchy represents a widely toolkit independent 'Meta-Framework'. 
All of the toolkit-specific aspects are hidden inside the (currently three) implementations of the 
framework. An application programmer can use an easy-to-remember method interface and has no 
need for knowledge about any of the underlying toolkits (i.e. about the many Xm-functions, Xt-
intrinsics functions, most of the numerous resource names, classes and possible values).

Xm++ minimizes the amount of code needed for basic user interface functionality and simplifies 
the programming task. This is especially important for simple and frequent tasks like creating 
menues and dialogs.

Xm++ also provides some user interface objects which aren't available in all supported GUI 
environments. Such classes are implemented by reusing existing Xm++ classes in a way that 
minimizes platform dependent work (e. g. toolbars and toolboxes).

The following chapters explain how Xm++ can be used to build applications.


2. BUILDING XM++ PROGRAMS
=========================

Since this guide suggests 'learning on the job' you will have to build a lot of example programs. 
Therefore we start with some hints how to do this in a UNIX environment. If you are using TIP 
RCI on a non-UNIX system (i.e. MS-Windows) you have to consult the TIP specific 
documentation and you may skip the instructions below.

To 'make' a Xm++ program you need to install a C++ compiler (AT&T CC or gnu g++), the 
Xm++ main header file (xmObject.h) and the Xm++ libraries for the desired GUI (libXm++.a for 
Motif, libXmaw++.a for Athena widgets or libXmrci++.a for TIP RCI).

If you have to do the installation yourself be sure to read the release's INSTALL and README 
files about operating systems, compiler versions and instructions for building the Xm++ libraries 
on your system.

After compiling your program you must link it with the respective libraries in the correct order:

Motif sample:
	CC sample.C -0 xmsample -lXm++ -lXm -lXt -lX11

Xaw sample:
	CC -DXAW sample.C -o awsample -lXmaw++ -lXaw -lXmu -lXext -lXt -lX11

TIP sample:
	CC -DRCI sample.C -o xrsample -lXmrci++ -lipc -lminlib -lcoll

You should have a look at the .../src/xmplus/samples/Makefile to see how to maintain include 
paths and library directories in a multi-platform environment. The easiest way may be copying this 
file to your home directory and modifying it for your needs.

In the following chapters about windows, subpanes and callbacks you will learn step by step how 
to write simple Xm++ applications.


3. WINDOWS, SUBPANES, AND CALLBACKS
===================================

This section demonstrates the usage of basic elements of the Xm++ framework - Windows, and 
callbacks - using simple examples.

3.1. CREATING A WINDOW
----------------------

The following code gives a first example of an Xm++ application:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "xmObject.h"

class XmTest : public XmWindow
{
	void initialize();
public:
	XmTest() : XmWindow("Xm++ Test Window") {}
};

void XmTest::initialize()
{
	addSubpane(Edit, "TestEdit", "Type in here:",y,h,callback);
	edit("TestEdit")->setText("Hello World");
}

void XmApp::initialize()
{
	(new XmTest)->open();
};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Running this code you will see a minimum sized main widow. After enlarging it you will find a 
labeled subwindow where you can type in.

###Fig. 1###

A pane, as used in the example, is a rectangular area within a resizeable parent object; its size can 
be defined in relation to its parents size. For more description of addSubpane and edit look at 
the Xm++ Class Reference, class XmPaneArea and XmDialogPane.

how it works

Using the Xm++ library, you get one global instance of a class XmApp which is created by the 
UNIX main() function. This class is a part of the Xm++ framework. It is responsible for the 
underlying toolkit's initialization and has one undefined member function initialize(). This function 
must be defined by the application programmer and is the starting point for every further 
operation.

To make something happen on the user interface, you must create at least one top level user 
interface object.

In the example above the application declares a class XmTest by subclassing one of the Xm++ 
main window classes and defines a constructor and a function initialize() for it.

The constructor only forwards the objects name to the XmWindow superclass. initialize() is an 
inherited virtual function which is called automatically before a window is realized. All Xm++ 
specific initialization like creating and initializing child objects should be done here (and not in the 
constructor because the C++ virtual function mechanism obviously does not work there).

In this example the XmWindow member 'addSubpane(type, name, label)' is called to create a 
labeled and automatically laid out subwindow, that can be edited by the user.

The final step is to set the contents of the newly created child object. The XmWindow member 
function 'edit(name)' returns a pointer to the child and the XmEdit member 'setText(string)' sets 
its contents.

A comment on accessing child objects:

Because C++ is a strongly typed language, member functions can be called only for the actual 
type. Since every manager object (XmWindow, XmDialogWindow, XmGroupBox,...) holds a 
collection of pointers to its children, it is not necessary for subclasses to have extra member 
variables for this.

A child object pointer can be obtained by calling an inline function formed by the class name of the 
object without the 'Xm' prefix with the object name as argument.

Example:

void MyDialog::f1()
{
	add(new XmListBox("myList",...));
	add(new XmEdit("myEdit",...));
}

void MyDialog::f2()
{
	edit("myEdit")->setText(listBox("myList")->selectedItem());
}

Since C++ has no 'Message Cascading' like Smalltalk, it saves program space and increases 
performance to define a pointer to a child object inside a function, if this child is accessed several 
times:

void MyDialog::f()
{
	XmListBox* lb = listBox("myList");

	lb->addAll(...);
	lb->selectIndex(5);
	lb->disable(),
}



If no child of the given type with the given name is found, Xm++ prints a warning to the error 
output and returns a NULL pointer. If you use this pointer without checking this condition (like in 
the examples above), your program will crash. Therefore you should either be sure to test 
everything in the debugging phase, or to check every returned pointer before using it.


3.2. CONTROLLING THE APPLICATION WINDOW SIZE
--------------------------------------------

In the above example the application windows have been quite small, we even had to resize them 
to see the whole label text.

To specify a window size and location we have to add a new member function to our window 
class:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class XmTest : public XmWindow
{
	bool initWindowSize(in& x, int& y, int& w, int& h)
		{ x=10; y=10; w=500; h=500; return(TRUE); }
};

x,y	position of the window on the screen
w,h	size of the window
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(That X and Y do work depends on your window manager configuration.)

In redefining this virtual function you override the default of the superclass and the window 
appears at the location and with the dimensions that we assign to the respective variables.


3.3. ADDING ANOTHER SUBPANE
---------------------------

As a next step we add a listbox to our window. We edit the initialize() method of 'XmTest':

void XmTest::initialize()
{
	addSubpane(Edit, "TestEdit", "Type in here:");
	addSubpane(ListBox, "TestList", "Click here:");
	edit("TestEdit")->setText("Hello World.");
	listBox("TestList")->addAll("first","second","third",NULL);
}

We recompile and run the example again, two subpanes - a listBox and an edit - will now appear.

###Fig. 2###


3.4. CALLBACKS
--------------

Xm++ uses a callback concept for handling input from the user. In contrary to other C++ class 
libraries, which support only global callback functions, Xm++ callbacks can be member functions 
of any class inheriting from XmRootClass - an empty class which servers as a base for all classes 
using callbacks. Most of the Xm++ framework is rooted by this class therefore we can implement 
callback members in subclasses of nearly all Xm++ classes. Usually we will attach them to our 
toplevel Objects (which are subclasses of XmWindow or XmUserDialog).

The method for specifiying a callback function is:

	object->setCallback(receiver, callback_member_func,...);

receiver			object instance who will receive the callback
callback_member_func		C++ 'pointer to member' of object class which implements the 	
				callback function

Optional arguments (which have default parameters and are not explained here) allow to remove 
an existing callback or to specify which information should be passed by Xm++ when calling the 
callback.

The form above sets an Object's default callback (i.e. activate for buttons, value changed for edits, 
select for listboxes). Some Xm++ classes provide additional callbacks which can be set with 
functions in the form setXxxCallback(...) where Xxx is the name of the callback:

i.e. listbox->setDoubleclickCallback(receiver, callback_member_func,...);

The following macro must be used for the callback_member_func argument to ensure the callback 
working with all supported compilers:

CBK(MyClass,MyMemberfunction)

Caution: Xm++ uses a generic pointer-to-member data type for all callbacks (this convenient way 
would not be possible otherwise), so it is left to the application programmer to ensure that the 
receiver is really an instance of the class specified in the CBK macro! Doing wrong would result in 
bad program crashes, but the author's expierience is that none seems to have problems with this.

Callback member functions:

They must be of type void and take one pointer argument, example:

	void MyClass::myMember(void*);

Note: A callback member must not be an inline function.


how to create our first callback

We add this new feature to our example application:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class XmTest : public XmWindow
{
public:

	void listSelected(void*);		// the callback member
};

void XmTest::initialize()
{
	listBox("TestList")->setCallback(this, CBK(XmTest, listSelected));
}

void XmTest::listSelected(void*)
{
	edit("TestEdit")->setText(listBox("TestList")->selectedItem());
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

With this modification the text pane changes its contents when we select a list item.

For a more detailed description of callbacks in general and about the callback's data pointer 
parameter see the class reference manual.


4. MENUES
=========

There are two kinds of menues available: popup menues and dropdown menues.

A dropdown consists of labels and items and/or submenues associated to each label and it is 
always visible after creation. After the dropdown has been created, labels as well as items or 
submenues can be added and removed.

A popup has only one label, it has also items and/or submenues but it appears only when the right 
mouse button or a defined accelerator key is pressed. The handling of items and submenues is 
identical to a dropdown menu.

The following description emphasizes on dropdown menues, popup menues are handled quite 
identically.


4.1. A MENU BAR
---------------

how to create a menu

We modify our example once more to introduce a dropdown menu:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class XmTest : public XmWindow
{

public:

	void menuSelected(char*);	// menu callback member
	void quit(void*);			// menu callback member
};

void XmTest::initialize()
{
	XmDropdownMenu* menu = createDropdownMenu();

	menu->addLabels("&File", "&Test", NULL);
	menu->setCurrentLabel("File");
	menu->addItems(Entry("Menu Item &One", CBK(XmTest,menuSelected)),
				Entry("Menu Item &Two", CBK(XmTest,menuSelected)),
				NULLENTRY);
	menu->addSeparator();
	menu->addItem(Entry("E&xit", CBK(XmTest,quit)));
}

void XmTest::menuSelected(char* n)
{
	edit("TestEdit")->setText(n);
}

extern "C" { void exit(int); }

void XmTest::quit(void*)
{
	exit(0);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Our refined application now has a menu bar with one label and three entries. Selecting the first or 
the second writes the name of the menu entry into the text pane, selecting the third entry ('Exit') 
terminates the program.

###Fig. 3###

First of all we define two additional callback members in our test class, one that prints out the 
selected item name and one that quits the application. Menue callbacks always get the menu item 
text as argument when they are called, therefore we declare the callbacks as void::(char*) 
functions (most compilers allow this hack) to use the information without casting.

Now we are ready to create the menu. This is done by calling the inherited member 
XmWindow::createDropdownMenu() which returns a pointer to the created menu object.

Note: In the current implementation of Xm++ menu bar should be created before any other 
window contents is added (in some environments it may not be visible otherwise).

To add labels to this new menu object we call its member addLabels() with a null terminated list 
of strings (the & character designates the character to be used as mnemonic in all environments 
where they are supported). When menu items are added, they will be appended to the items of the 
'current label' recognized internally by the menu object. It defaults to the last label created, call 
setCurrentLabel() to specify a different label as destination for subsequent add calls.

The addItems() member function appends a null terminated list of Entry objects to a menu. An 
Entry is a very simple object that carries name- and callback-information. Separators can be added 
by calling the menues addSeparator() member.

See the Xm++ class reference on how to specify the insert position of items, remove labels, items 
or separators, and disable or enable menu components.


4.2. SUBMENUES
--------------

how to create a submenu

Creating cascaded menues is also very easy.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void XmTest::initialize()
{
	XmSubMenu* sub = menu->addSubmenu("&Sub Menu...");
	sub->addItems(Entry("Menu Item &Three", CBK(XmTest,menuSelected)),
				Entry("Menu Item &Four", CBK(XmTest,menuSelected)),
				NULLENTRY);
	menu->addSeparator();
	menu->addItem(Entry("E&xit", CBK(XmTest,quit)));
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If we add this few lines to the menu code we get a submenu working. The items are handled 
exactly like their toplevel counterparts.

Each menu label, item, submenu or separator is identified by its name when used in the various 
menu member functions. With exception of separators, which can have an arbitrary name (default 
is nothing) all menu components use their component name as the default label text.

The recent Xm++ version also supports checkable menu items.


5. DIALOGS
==========

Xm++ distinguishes between two different types of dialogs: System Dialogs and User Defined 
Dialogs. System Dialogs are classes that provide an extended programming interface to a couple 
of custom dialogs like message boxes and prompters. The User Dialog class represents an empty 
form and has functions for maintaining hierarchies of subobjects like edits, buttons, lists and other 
controls.


5.1. SYSTEM DIALOGS
-------------------

The dialog subclass XmSystemDialog is the ancestor for predefined dialogs. Their appearance 
can be configured by using styles (described below). You need not to use callback functions to 
deal with user input - a special Xm++ mechanism allows you to halt the execution of the calling 
function until a system dialog has been completed by the user.

Xm++ currently supports the folowing system dialogs:

Message Box:		well known in any GUI system
Prompter:		prompts for a single line of Text
List Prompter:		a prompter with an additional list
File Selector:		a file selection box

We supplement our example with a simple message box:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define MSG "Are you sure,\nthat you are sure?"

void XmTest::menuSelected(char* n)
{
	if(new XmMsgBox(MSG, "Xm++ Test", XmSmboxQuestion, this)->showMsg())
	{	edit("TestEdit")->setText(n);
		if(!getDropdown()->disableItem(n))
			getDropdown()->submenuAt("Sub Menu")->disableItem(n);
	}
	else
		edit("TestEdit")->setText("I was not sure...");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now the application asks after each menu selection and sets the edit text accordingly.

###Fig. 4###


5.2. INTRODUCING STYLES
-----------------------

Xm++ styles are integer constants that are used to simplify controlling an object's appearance and 
behavior. They can be ORed together and are handed over as a single parameter. The object 
decodes the parameter to set various resources to certain values according to the particular widget 
set used.

The XmMsgBox constructor takes a 'style' as the third argument (XmSmboxQuestion in the 
case above). Some styles are composed from other styles, for example:

XmSmboxQuestion = XmSdlgAppModal | XmSmsgYesNo | XmSmsgQuestion

XmSdlgAppModal:	sets the input mode resource in the dialog shell widget
XmSmsgYesNo:	changes the labels of the system dialog buttons
XmSmsgQuestion:	selects the appropriate Xm function to create a message
		box with a question mark icon

Styles can be specified in the constructors of top level windows, system dialogs, user defined 
dialogs and controls. The Xm++ class reference manual shows, which styles are applicable to the 
different Xm++ objects.

The following example code uses other Xm++ system dialogs. We simply change the 
menuSelected function:

/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/ Prompter

#define MSG "Enter some text:"

void XmTest::menuSelected(char* n)
{
	char* reply;

	if(reply = new XmPrompter(MSG, "Xm++ Test", n, 
				XmSdlgAppModal, this)->prompt())
		edit("TestEdit")->setText(reply);
	else
		edit("TestEdit")->setText("I didn't answer...");
}

// List Prompter

#define MSG "Enter text or select in list:"
char* list[] = { "one", "two", "three", "four", NULL };

void XmTest::menuSelected(char*)
{
	char* reply;

	if(reply = new XmListPrompter(&list, MSG, "Xm++ Test", "<none>",
						XmSdlgAppModal, this)->prompt())
		edit("TestEdit")->setText(reply);
	else
		edit("TestEdit")->setText("I made no choice...");
}

// File Selector Box

void XmTest::menuSelected(char*)
{
	char* reply;

	if(reply = new XmFileSelector("*", XmSdlgAppModal, this)->promptFile())
		edit("TestEdit")->setText(reply);
	else
		edit("TestEdit")->setText("I selected no file...");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

###Fig. 5###

The example showed only modal dialogs, but all system dialogs can run modeless as well. Use the 
style XmSdlgModeless and specify the default (OK-) callback as an additional last argument in 
the constructor (and do not use the members showMsg() or prompt(), they work only with 
modal dialogs). A modeless dialog is popped up automatically after creation. To destroy it simply 
delete it from within the OK-callback. By clicking the cancel button the destructor is called 
automatically.

(The handling of modeless system dialogs should be enhanced in future releases of Xm++.)


5.3. USER DEFINED DIALOGS
-------------------------

The definition of contents, layout and functionality of user defined dialogs is completely left to the 
'user' of Xm++, the application programmer. A well designed dialog as a part of a good user 
interface of an application can only be the result of an individual layout procedure with an 
appropriate graphic tool (usually called interface builder, dialog- or resurce-editor). Dialogs 
should be resolution independent (will be hopefully supported in future versions of Xm++) but it 
makes no sense to make them resizeable because their components should be sized as small as 
possible and as large as necessary. Therefore any automatic layout features of underlying toolkits 
(i.e. Motif / Xaw) are used by the Xm++ main window and toolbar classes (see below), but not 
here.

Xm++ provides the class XmUserDialog as a container object and a lot of classes for handling 
the primitive user interface elements like static text, buttons, lists and edit-fields (child widgets, or 
'controls' using Microsoft's terminology).

how to create a UserDialog

To get a first idea, how these UserDialogs can be used in a program, we add a sample dialog to 
our example application:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SampleDialog : public XmUserDialog
{
	void createContents();
	void initContents();

	bool initWindowSize(int&, int&, int& w, int& h)
		{ w = 445; h = 345; return(TRUE); }
public:
	SampleDialog(XmObject* parent) : XmUserDialog("Sample Dialog", parent)
		{ }
};

void SampleDialog::createContents()
{
	add(new XmStaticText("StaticText", 19, 27, 371, 22));
	add(new XmListBox("listBox", 22, 66, 170, 178));
	add(new XmEdit("editText", 220, 68, 187, 33));
	add(new XmCheckBox("Check&Box", 225, 110, 176, 27));
	add(new XmGroupBox("radioGroup", 222, 140, 184, 104));
	add(new XmRadioButton("Radio &One", 244, 176, 154, 27));
	add(new XmRadioButton("Radio &Two", 244, 206, 159, 27));
	add(new XmPushButton("&Ok", 214, 272, 96, 38));
	add(new XmPushButton("&Cancel", 324, 272, 96, 38));
}

void SampleDialog::initContents()
{
	// currently unused...
}

// move the the XmTest::menuSelected function below the 
// sample dialog stuff and make it look like this:

void XmTest::menuSelected(char*)
{
	(new SampleDialog(this))->open();
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If we run the modified example and select one of the menu items, our first self defined dialog will 
appear. We can edit text, click the checkbox, radio buttons and push buttons (at the moment 
without any effect).

UserDialogs should be created by subclassing the XmUserDialog class. Besides the 
initWindowSize() function there is a createContents() function. This inherited virtual function 
is thought to create all the child objects of the dialog. The main window's initialize() function calls 
createContents() and initContents() in order before a dialog is opened. As the 
createContents() will be automatically generated when using a dialog editor, there should be no 
need to edit it. When implementing a dialog's functionality (setting initial contents of controls, 
enabling/disabling, setting callbacks, etc.) the necessary code should be written into the 
initContents() function.

To display a newly created UserDialog you can call initialize() and realize(), or - like in the 
example above - the open() function, which does both.

A control is created and added in one simple statement. The common constructor arguments for 
all controls are:

	Control::Control(name, x, y, width, height, style);

All arguments except the name have defaults. Coordinates are in pixel units (will be enhanced in 
future releases). The style parameter can be used for different purposes, some of them will be 
explained in the following examples (a complete list of applicable styles for each control can be 
found in the reference manual).

The control name is (similar to menue implementation) also the default label for all controls 
showing text (staticText, buttons and checkBoxes). The setText() function (defined in the 
abstract class XmControl, the superclass of all controls) can be used to change the label string. 
setText() should be used anyway when creating controls that hold more than a minimum amount 
of text (like staticTexts), it must be used to label a group box (see note below).

Example:

...
char* myInfo;	// holds many lines of text...

add((new XmStaticText("staticText", 19, 27, 371, 300))->setText(myInfo));
...
add((new XmGroupBox("radioGroup", 222, 140, 184, 104))->setText("Radio 
Group"));
...

Special note on group boxes: Because it should be possible to have group boxes without any 
label, the group box does not use the control name for default. A label must be assigned explicitly 
by the application code.


5.4. ADDING ACTION
------------------

For something to happen in reaction to user interactions with the dialog box, we have to add 
callback functions for the controls.

We expand the SampleDialog class declaration and initContents() function to implement some 
callback functions:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SampleDialog : public XmUserDialog
{
	...

	// callback members
	void listSelect(XmListBox*);
	void textEdited(XmEdit*);
	void cbChanged(XmCheckBox*);
	void rbChanged(char*);
	void closeIt(void*);
};

void SampleDialog::initContents()
{
	XmListBox* lb = listBox("listBox");
	XmRadioButton* rb1 = radioButton("Radio One");
	XmRadioButton* rb2 = radioButton("Radio Two");

	lb->addAll("One", "Two", "Three", NULL);
	lb->setCallback(this, CBK(SampleDialog,listSelect));
	checkBox("CheckBox")->setCallback(this, CBK(SampleDialog,cbChanged));
	rb1->setState(TRUE);
	rb1->setCallback(this, CBK(SampleDialog,rbChanged), TRUE, CB_OBJ_NAME);
	rb1->setCallback(this, CBK(SampleDialog,rbChanged), TRUE, CB_OBJ_NAME);
	pushButton("Ok")->setCallback(this, CB(SampleDialog::closeIt));
	pushButton("Cancel")->setCallback(this, CB(SampleDialog::closeIt));

	disable("Ok", "Cancel", NULL);
}

void SampleDialog::listSelect(XmListBox* lb)
{
	staticText("StaticText")->setText(lb->selectedItem());
}

void SampleDialog::cbChanged(XmCheckBox* cb)
{
	staticText("StaticText")->setText(cb->getState() ? "ON" : "OFF");
	listBox("listBox")->insert(0, edit("editText")->getText());
}

void SampleDialog::rbChanged(char* name)
{	if(strstr(name, "One"))
	{	enable("listBox", "editText", "CheckBox", NULL);
		disable("Ok", "Cancel", NULL);
	}
	else
	{	disable("listBox", "editText", "CheckBox", NULL);
		enable("Ok", "Cancel", NULL);
	}
}
void SampleDialog::closeIt(void*)
{
	delete this;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After this modification the sample dialog shows how the various callbacks can be used to trigger 
changes in the dialog state.

If we look at the radio button callbacks in the example above we find two additional parameters 
which have not been used before:

	rb->setCallback(..., TRUE, CB_OBJ_NAME);

The first boolean parameter specifies whether a callback for a specific event should be set or 
removed. To remove a callback successfully, the callback name, the receiving object and the 
member function pointer must be the same as when it was created.

The second value is a pointer or a pointer constant, which causes the Xm++ event handling to call 
a callback member with a certain argument:


setCallback parameter		callback member called with

CB_OBJ_PTR			(XmControl*) 		pointer to control	(default)
CB_OBJ_NAME			(char*)			pointer to control's name
CB_XM_DATA			(XtCallbackStruct*)	pointer to toolkit callback data
CB_XM_HANDLE			(Widget)		the toolkit widget id
(void* )your_pointer		(void* )		your_pointer

The CB_XM_* stuff is used internally by Xm++, the constants are provided for possible 
enhancements and should not be used by application programmers.


6. THE DIALOG EDITOR
====================

User dialogs should be designed with some direct manipulative interface builder. Such a tool 
allows interactive graphical layout of controls and generates source code which is compiled and 
linked with the application.

A very simple dialog editor (xmdlg) is provided with the Xm++ distribution. It produces a file 
containing a class declaration template, the code of the initWindowSize() and createContents() 
functions, and a hexed binary description to allow re-editing of a once created dialog. After saving 
a new dialog for the first time the class template (residing inside a comment) must be copied 
somewhere into an application's header file in order to complete it by adding callbacks other 
individual functions. The dialog file can be compiled separately or #included in the application's 
implementation - in either case it has to be recompiled after any subsequent changes using the 
dialog editor.

xmdlg  Features

The current release still has no 'official' WYSIWYG support (it only works under certain 
conditions). In the editing area controls are drawn as simple rectangles with text, a preview option 
allows to pop up the 'real' dialog for testing. 

Author's note: It turned out that not showing a specific look and feel during editing makes it 
easier to layout dialogs which should be used in different GUI environments, i.e. Motif, Xaw and 
MS-Windows.

xmdlg allows creating, moving, resizing and deleting controls, assigning control names and text, 
and specifying dialog and control styles. It also provides simple clipboard operations, an adjustable 
editing grid and a tool for changing the control order (which is necessary for keyboard traversal on 
GUIs that support it).

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Example file created by xmdlg:

/*Hello, this is Re 0.001...
	DemoDialog

 @data:
 G1 G3 44 65 6D 6F 44 69 61 6C 6F 67 SC G4 44 65 6D 6F 20 44 69
 61 6C 6F 67 SC G5 A1 00 34 00 D6 01 56 01 SC G6 EC 00 02 00 SC
 ....
 00 00 SC G2 0A 00 00 00 SC G3 43 6F 6D 62 6F 42 6F 78 SC G4 43
 6F 6D 62 6F SC G5 3F 01 F8 00 82 00 22 00 SC G6 00 00 04 00 SC
 G0

*/

/* default class template for: DemoDialog

class DemoDialog : public XmUserDialog
{
	bool initWindowSize(int&, int&, int&, int&);
	void createContents();
	void initContents();	//should be defined by the application
public:
	DemoDialog(char* name = "Demo Dialog", XmObject* parent = NULL) : 
		XmUserDialog(name, parent, 0x0L | XmSbordered | XmSmoveable | XmScloseable |
				 XmStitled | XmSsysMenu | XmSdlgAppModal)
	{ }
	// application functions...
};

*/

bool DemoDialog::initWindowSize(int&, int&, int& w, int& h)
{
	w = 470;
	h = 342;
	return(TRUE);
}

void DemoDialog::createContents()
{
	XmGroupBox* groups[10];
	int groupCount = 0;

	add((groups[groupCount + 1] = new XmGroupBox("GroupBox", 18, 25, 278, 285, 0x0))->setText("GroupBox")); groupCount++;
	groups[groupCount]->add((new XmStaticImage("Image", 31, 40, 50, 50, 0x0)));
	groups[groupCount]->add((new XmRadioButton("RadioButton1", 17, 124, 80, 25, 0x0))->setText("Radio 1"));
	groups[groupCount]->add((new XmRadioButton("RadioButton2", 17, 157, 80, 25, 0x0))->setText("Radio 2"));
	groups[groupCount]->add((new XmRadioButton("RadioButton3", 17, 192, 80, 25, 0x0))->setText("Radio 3"));
	groups[groupCount]->add((new XmPushButton("PushButton", 14, 239, 98, 30, 0x0))->setText("PushButton"));
	groups[groupCount]->add((new XmCheckBox("CheckBox", 150, 240, 106, 25, 0x0))->setText("CheckBox"));
	groups[groupCount]->add((new XmStaticText("StaticText", 113, 40, 125, 18, 0x0 | XmSleft))->setText("single line Edit"));
	groups[groupCount]->add((new XmStaticText("StaticText", 115, 98, 122, 18, 0x0 | XmSleft))->setText("multi line Edit"));
	groups[groupCount]->add((new XmEdit("Edit1", 115, 67, 145, 22, 0x0)));
	groups[groupCount]->add((new XmEdit("Edit2", 115, 121, 147, 100, 0x0 | XmSmultiLine))); groupCount--;
	add((new XmStaticText("StaticText", 343, 23, 80, 18, 0x0 | XmSleft))->setText("ListBox"));
	add((new XmStaticText("StaticText", 343, 217, 80, 18, 0x0 | XmSright))->setText("ComboBox"));
	add((new XmPushButton("QuitButton", 346, 291, 80, 30, 0x0))->setText("Quit"));
	add((new XmListBox("ListBox", 319, 50, 129, 152, 0x0)));
	add((new XmComboBox("ComboBox", 319, 248, 130, 34, 0x0 | XmSdropdown)));
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


7. COMMON CONTROL MEMBER FUNCTIONS
==================================

Controls are objects that allow display and entry of application data, some of them also support 
the layout of children. The XmControl class defines a set of common functions to be inherited by 
all its descendants. A brief overview of these functions:

bool isA(enum ctrlType)		allows to determine the class of a control at runtime
bool move(int, int)		changes location (x, y, relative to parent)
bool resize(int, int)		changes size (width, height)
bool reframe(int, int, int, int) performs move() and resize() (x, y, w, h)
XmControl* setText(char*)	change Text String (when applicable)
XmControl* setFont(char*)	change Text Font (when applicable)
char* getText()			return current Text (when applicable)
XmControl* setImage(Pixmap)	change icon Data (when applicable)
XmControl* setFocus()		aquire the dialog input focus
XmControl* disable()		make control insensitive (grayed)
XmControl* enable()		make control sensitive
(inherited from XmObject)
XmObject* hide()		remove visual representation
XmObject* show()		recreate visual representation

If multiple controls should be changed in a disable, enable, hide or show operation this can also be 
done in a convenient way with functions of the parent dialog, i.e.

XmUserDialog::disable(char*, ...);	control names as NULL terminated argument list -or
XmUserDialog::disable(char**);	control names in NULL terminated array

You may now continue experimenting with our sample program using the functions above. To 
learn about the individual functionality of the various controls consult the class reference. There 
you find all information about member functions, applicable styles and callbacks.


7.1.	KEYBOARD TRAVERSAL
--------------------------

Most of today's GUI environments support mnemonics for menues, controls and keyboard 
navigation between controls in a dialog. 

Xm++ supports Motif/MS-Windows style tab groups - between groups you move with <tab> or 
<shift/tab>, inside a group with cursor keys. The tabbing order of controls within a dialog depends 
on the order, in which the controls are created. If an application has no special preferences, Xm++ 
defines default groups depending on control types.

The special style 'XmStabStop' can be used to define own tab groups. It has to be applied to each 
control, that should be a 'tab stop'. When using environments which don't implement keyboard 
traversal (i.e. Xaw), mnemonics and the tab stop style are ignored.

Usually the dialog editor is used to define control order and tab groups. In the following example 
we change our dialog implementation by hand. We modify the lines in the initContents() function 
where controls are created and added by adding the 'XmStabStop' style parameter:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SampleDialog::createContents()
{
	add(new XmStaticText("StaticText", 19, 27, 371, 22));
	add(new XmListBox("listBox", 22, 66, 170, 178, XmStabStop));
	add(new XmEdit("editText", 220, 68, 187, 33, XmStabStop));
	add(new XmCheckBox("Check&Box", 225, 110, 176, 27, XmStabStop));
	add(new XmGroupBox("radioGroup", 222, 140, 184, 104));
	add(new XmRadioButton("Radio &One", 244, 176, 154, 27, XmStabStop));
	add(new XmRadioButton("Radio &Two", 244, 206, 159, 27));
	add(new XmPushButton("&Ok", 214, 272, 96, 38, XmStabStop));
	add(new XmPushButton("&Cancel", 324, 272, 96, 38, XmStabStop));
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Now the keyboard interface can be used to navigate in the dialog.


7.2. IMAGES IN CONTROLS
-----------------------

Xm++ also allows to create iconic user interface elements with static and button controls.

The current implementation uses CompuServe GIF (TM) image data format. Although the 
UNIX/X versions of Xm++ still support X11 bitmaps, new applications should prefer GIF images 
in order to be compatible with TIP on non-X platforms. Xm++ does not support a bunch of 
different image formats because this would mean a lot of unnecessary work for the TIP server 
implementations. There are many tools available to convert nearly any image format to GIF files.

Assuming we have two GIF files 'ok.gif' and 'cancel.gif' containing nice 60 x 25 pictures, we only 
have to add two statements at the sample dialog's initContents() function:


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SampleDialog::initContents()
{
	...
	pushButton("Ok")->setImage(getRuntimeImage("ok.gif"));
	pushButton("Cancel")->setImage(getRuntimeImage("cancel.gif"));
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(Hint: Use your favorite image editor to create these pictures. If it doesn't support GIFs use a 
conversion program.)

Our example dialog now has got iconic labels on its Ok and Cancel buttons.

Where the Xm++ library looks for images load is determined by the global variable (char* ) 
XmImageDir. Default is NULL and the images are loaded from the current directory. When set to 
a path name string, Xm++ searches for image files in the given directory.

Images should always have the aproppriate size as controls may resize automatically to the image 
size (depend on which tookit used). Applying an image of the wrong size may destroy your dialog 
layout. File name parameters for getRuntimeImage() statements must end with (lowercase) '.gif'.


8. TOOLBARS AND TOOLBOXES
=========================

A Toolbar is a special dialog which automatically performs the layout of its controls. It is created 
in horizontal or vertical orientation and can have one or multiple rows (or columns). Toolbars can 
be either attached to a main window or can be contained in an own window - the result is called 
toolbox.

To test toolbar and toolbox in our sample, we add two functions to the XmTest class:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class XmTest : public XmWindow
{
	...
	void initToolbar(XmToolBar*);
	void doitSelected(XmToolBar*);   // toolbar callback member
	void newBoxSelected(void*);      // toolbar callback member
};

void XmTest::initialize()
{
	...
	initToolbar(addToolbar(XmStop));
}

void XmTest::initToolbar(XmToolBar* tb)
{
	tb->add(new XmPushButton("&One"),
		this, CB(XmTest::menuSelected), CB_OBJ_NAME);
	tb->add(new XmPushButton("&Two"),
		this, CB(XmTest::menuSelected), CB_OBJ_NAME);
	tb->add(new XmPushButton("&Box..."),
		this, CB(XmTest::newBoxSelected));
	tb->add(new XmEdit("toolbarEdit"));
	tb->add(new XmCheckBox("&Twice"));
	tb->add(new XmPushButton("&Doit"), this, CB(XmTest::doitSelected), tb);
}

void XmTest::doitSelected(XmToolBar* tb)
{
	int repeat = tb->checkBox("Twice")->getState() ? 2 : 1;

	while(repeat--)
		listBox("TestList")->add(tb->edit("toolbarEdit")->getText());
}

void XmTest::newBoxSelected(XmToolBar* tb)
{
	XmToolBox* tbx = new XmToolBox("ToolBox", this, XmSvertical, 2);
	initToolbar(tbx);
	tbx->realize();
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


One reason to use a toolbar is to provide shorthands for menu entries. If we click 'One' or 'Two' 
we get the same as when using the repsective named menu items. Of course, one usually uses 
iconic buttons in real applications. Another benefit of tool objects is to provide a few frequently 
used controls easily accessible to the user, thus avoiding popping up dialogs. In the sample toolbar 
/ box we can also create further toolboxes, type text, select an option and insert lines into the main 
window's list pane.

A toolbar for a main window is created with the XmWindow::addToolbar() member function, 
the style parameter specifies, where the new bar should be attached and its orientation (top and 
bottom bars horizontal, left or right bars vertical). If once created, the toolbar can be treated as an 
ordinary dialog, with the exception that the x/y position of controls needs not to be specified.

A Toolbox is a transient window in combination with a toolbar, the class XmToolBox inherits 
from a window class (XmDialogWindow) and from the XmToolBar class. In the XmToolBox 
constructor you can speicify the orientation with a style and an integer value for the number of 
rows/columns. The newly created toolbox can subsequently be treated like a toolbar (i.e. initialized 
with the same function initToolbar() in the code above).

Programmers with less expierence with multiple inheritance should note, that a toolbox pointer 
must be casted to a toolbar pointer in order to access its toolbar member functions:

	XmToolBox* box = new XmToolBox("ToolBox", this, XmSvertical, 2);

	box->add(aControl);		// causes compiler error, bacause the add function
						// is inherited from XmDialogWindow and XmToolBar

	((XmToolBar* )box)->add(aControl);	// correct (because not ambigous)
or
	XmToolBar* bar = box;		// legal (because the box inherits from the bar)
	bar->add(aControl);		// correct (because not ambigous)



APPENDIX A - X11 RESOURCES
==========================

1. MENUES
---------

Default menu labels can be overridden by specifying external resources. As X-Windows user you 
know, that X has a resource database where information about various clients can be stored. When 
you add new or modify existing entries in this database you can change the visual presentation 
and/or behavior of clients.

Be aware that you need different resource files for the Motif and Xaw version of your application 
- this is because the two toolkits have different resource names for their widget's components 
(check the reference manual).

To have an example (Motif) for a Xm++ menu, try the following:

1. Create a new file named 'sample.rsc'
2. Edit this file, copy and paste the paragraph below:

sample*Xm++ Test Window*File.labelString:						Datei
sample*Xm++ Test Window*File.mnemonic:						D
sample*Xm++ Test Window*Test.labelString:						Probieren
sample*Xm++ Test Window*Test.mnemonic:						P
sample*Xm++ Test Window*Menu Item One.labelString:				Eintrag Eins
sample*Xm++ Test Window*Menu Item One.mnemonic:				E
sample*Xm++ Test Window*Menu Item Two.labelString:				Eintrag Zwei
sample*Xm++ Test Window*Menu Item Two.mnemonic:				Z
sample*Xm++ Test Window*Sub Menu.labelString:					Unter-Menue
sample*Xm++ Test Window*Sub Menu.mnemonic:					U
sample*Xm++ Test Window*Sub Menu*Menu Item Three.labelString:	Eintrag Drei
sample*Xm++ Test Window*Sub Menu*Menu Item Three.mnemonic:	D
sample*Xm++ Test Window*Sub Menu*Menu Item Four.labelString:	Eintrag Vier
sample*Xm++ Test Window*Sub Menu*Menu Item Four.mnemonic:	V
sample*Xm++ Test Window*Exit.labelString:						Ende
sample*Xm++ Test Window*Exit.mnemonic:						E

3. Save the file and type the shell command:

	/usr/bin/X11/xrdb -merge sample.rsc

4. Start your sample application again

You will see your application with a german menu bar, maybe not very useful for you but you can 
change the names according to your own ideas if you know about the resource syntax for Xm++ 
menues:


application_name*main_window_name*label_or_submenue_or_item_name.motif_rsc

application_name	the name of your executable program
main_window_name	the name of the window which's menu bar should be changed
label_or_submenue_or_item_name	the component name
motif_rsc		the Motif (or Xaw) resource you want to change, in the case of menue 		
			components
			labelString | mnemonic | kbdAccelerator

The resource entry above changes all components with the given name in every menu of the given 
window (menu bar and popup menu). If you have equally named components in a hierarchy, use a 
more specific syntax to describe the resource path:

application_name*main_window_name*dropdown*...
application_name*main_window_name*popup*...
  to change components in only one of these menu hierarchies

application_name*main_window_name*dropdown*label_name-menu-pane*...
  to change components in the hierarchy below the label with label_name
  (add '-menu-pane' to the label name, e.g. ...*Test-menu-pane*... for
  a label named 'Test')

application_name*main_window_name*submenu1_name*submenu2_name*...
  to change components in a specific level of cascaded menues

To get more information about the usage of external resources in the Xm++ framework see 
appendix A. If you want to know exactly how X-Resources are organized and used consult the 
Xlib, Xtk and Motif / Xaw documentation.


2. DIALOGS
----------

Dialog resources work exactly like menu resources. If you want to have your application 
externally configurable, add descriptions to your resource file:

sample*Sample Dialog*StaticText.labelString:			Das ist ein Text.
sample*Sample Dialog*CheckBox.labelString:			Pruefschachtel
sample*Sample Dialog*CheckBox.mnemonic:			P
sample*Sample Dialog*radioGroup.group-label.labelString: Radio Gruppe
sample*Sample Dialog*Radio One.labelString:			Radioknopf Eins
sample*Sample Dialog*Radio One.mnemonic:			E
sample*Sample Dialog*Radio Two.labelString:			Radioknopf Zwei
sample*Sample Dialog*Radio Two.mnemonic:			Z
sample*Sample Dialog*Ok.labelString:				Weiter
sample*Sample Dialog*Ok.mnemonic:				W
sample*Sample Dialog*Cancel.labelString:			Abbruch
sample*Sample Dialog*Cancel.mnemonic:				A

After reloading your resources ('xrdb -merge sample.rsc') you have
translated all textual information in your dialog.


X-Bitmaps can also be used in external resources. Comment out the pushBotton()->setImage() 
statements and edit your resource file: Change the resource statements

sample*Sample Dialog*Ok.labelString:				Weiter
sample*Sample Dialog*Ok.mnemonic:				W
sample*Sample Dialog*Cancel.labelString:			Abbruch
sample*Sample Dialog*Cancel.mnemonic:				A

to

sample*Sample Dialog*Ok.labelType:				XmPIXMAP
sample*Sample Dialog*Ok.labelPixmap:				my_ok_bmp
sample*Sample Dialog*Cancel.labelType:				XmPIXMAP
sample*Sample Dialog*Cancel.labelPixmap:				my_cancel_bmp

Create the new my_... bitmaps and the application will use them as the
new button labels.

 The Toolkit Interface Protocol (TIP) is a new style client/server GUI developed by the author of Xm++ and 
CommonInteract. It uses toolkit level (rather than graphics level like X11) exchange of GUI information between 
client and server. The benefits of this approach are minimal communication bandwidth needs and the possiblilty to 
run an application in different GUI environments (currently supported are X11 and MS-Windows ). If you want to 
know more about TIP contact the author.
