
T h e   X m + +   U s e r ' s   G u i d e
=========================================


Preleminary draft, version 2 - 05/31/1993

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



1. Introduction
_______________


Xm++ is an application framework for the C++ language that should simplify
building applications on UNIX platforms using the X-Window System, the X-Toolkit
and OSF Motif or Athena user interface components (widgets).

The major aims in of Xm++ are:

* provide a programming interface to both the Motif- and Xaw- Toolkit in an
  object oriented language ('widget wrapper' functionality)
      Xm++ consists of C++ classes which implement their behavior using
      widgets of the two different toolkits.
* reduce the amount of code needed for the basic user interface functionality
      This is especially important for simple and frequent tasks like
      creating menues and dialogs. The Xm++ classes enhance the default
      behavior of the underlying widgets to cover the average needs of most
      applications. Some shorthands should ease specifying modifications to
      these defaults (for example styles to define appearance and behavior
      of a certain object).
* simplify the programming task
      The same techniques that redurce code also reduce complexity if used in
      combination with the easy to remember protocol of the Xm++ classes.
      Using Xm++ one has no need to know about the many Xm - functions, Xt -
      (intrinsics) functions and most of the numerous resource names, classes
      and possible values (except those, that should be redefineable by the
      end user). All necessary functions and resources are hidden in the Xm++
      object's constructor, destructor and member functions. Xm++ intends to
      be a 'Black Box' framework in respect to the application programmer.
* enhance functionality by providing new user interface objects
      Rather than creating new widgets with the intrinsics library, Xm++ uses
      itself as a 'WhiteBox' framework to enhance the set of user interface
      objects available to the programmer (e.g. toolbars and -boxes).

The following section explains how all these targets are covered and how 
the Xm++ features are used to build a Xm++ application.


2. Xm++ Basics
______________


This section demonstrates the employment of the basic elements
of the Xm++ framework - Windows, Menues, Dialogs and Controls -
using simple examples.

The full source code for all examples can be found in Appendix ##.
To "make" them you need a C++ compiler (AT&T Version 2.0 or later, or
gnu g++ 2.3.2 or later - view the README.GNU file in the lib directory for
restrictions when using gnu c++), the Xm++ main header file (xmObject.h)
and the Xm++ library (libXm++.a) installed.
After compilation you must link also with the X-, Xt- and Motif- or Xaw-
libraries in the right order:

Motif sample:

    CC sample.C -o xmsample -lXm++ -lXm -lXt -lX11

Xaw sample:

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

For writing Xm++ applications you don't need any Motif or Xaw include files
but you must have the libraries (libXm.* for Motif, libXaw.*, libXmu.* and 
libXext.* for Xaw) installed on your system.


2.1. Windows, Subpanes and Callbacks
------------------------------------

Creating a Window
=================

To have a very first example of an Xm++ application take a look at the
following code:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include "xmObject.h"


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

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

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you build this code and run it you will see a main window containing a
labeled subwindow where you can type in (this looks very simple, but if you
are using the toolkit you have to know a lot about the Xtk and Motif or Xaw
before such a window becomes visible on your screen).

How does it work?

If you are using the Xm++ library, you get one global instance of a class
'XmApp' that is created by the unix main() function. This class is a part of
the Xm++ framework. It is responsible for Xtk initialization and has one
undefined member function 'initialize()'. This function must be defined by
you, the application programmer and it is the starting point for every further
action.
To make something happen at the user interface, you must create at least
one 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 an
initialize() function for it.
The constructor does nothing but forwarding the object's name to the XmWindow
superclass. initialize() is an inherited virtual function which is called
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 which is needed for the correct
behavior of some Xm++ classes 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.
The last step is to get a correct typed pointer (explained below) to this newly
created child object by calling the XmWindow member 'edit(name)' and to call
the XmEdit member 'setText(string)' that sets its contents.

A comment about 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 child
objects, it is not necessary for subclasses to have member variables that are
pointers to children.
Every child object pointer can be obtained by an inline function which name
is identical to the class name of the object without the 'Xm' prefix (it
searches for an object with the given type and name and casts it to the
correct pointer type, see the Xm++.h header file). 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.


More Subpanes
=============

As you have added an edit subwindow to your main window you might want
to have also a listbox to display other information. Changing the 
initialize() method of 'XmTest' in the example above does it:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


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);
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you recompile and run the example again, you see the two subpanes.


Controlling the Application Window Size
=======================================

If you have tested the examples, you have seen that the windows are very
small, and you had to resize them to see the whole label text.

To specify a window size and location you have to add a new member function
to your window class:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In redefining this virtual function you override the default of the
superclass and the window will appear at the location and with the
dimensions that you assigned to the respective variables.


Callbacks
=========

Since applications are usually interactive we must introduce a method to
get information about user input. Xm++ uses the X-Windows callback concept
with the slight difference, that Xm++ callback functions are not global but
member functions of a specific object (everyone who has used pointers to
member functions in C++ knows about the difficulties to provide this...).

The common two ways of specifying a callback function are:

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

where callback_name is the Motif string constant for the callback, receiver
is the object whose member function will be called and callback_member_func
is a C++ 'pointer to member'. Two macros save typing:

cb(shortName)	expands to the Motif original:	XmNshortNameCallback
CB(MyClass::myMember)	expands to:	   ((cbProc )&MyClass::myMember)

If the first form is used to specify the callback (without specifying
callback_name), the object's defaultAction (e.g. activate for buttons,
valueChanged for edits) is assigned.

Caution: When writing applications for Xaw only or both Motif and Xaw,
you are recommended to use this form of specifying callbacks, because most
Xaw widgets have exactly one callback function. To take advantage of
additional Motif features (i.e. double click in a list box), you should use
conditional compilation (#ifndef XAW) for the respective callback settings -
and don't forget that this functionality is missing when when the application
runs under Xaw (i.e. you must have an open button too).

Callback member fuctions must have a void type and take one pointer
argument, for example:

	void MyClass::myMember(void*);

For a detailed description (how callbacks can be removed, what data the
pointer parameter of a callback member can hold, which callbacks are
applicable for the various Xm++ objects and which defaultActions are defined)
see the class reference in section ##.


Now, let's 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, CB(XmTest::listSelected));
}

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

...

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Note: A callback member must not be an inline function (you will get
a compiler error if you try to get a pointer to it).

After this modification the text pane changes its contents when you
select a list item - your first Xm++ callback is working!



2.2. 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 is always visible after creation as the well known menu bar
at the top of a window. After the dropdown has been created, labels 
as well as items or submenues can be added and removed.
A popup has only one label specified upon creation, has also items and/or
submenues but 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 refers only the Dropdown Menu because
a Popup Menu can be seen as a specific form of a dropdown.

A Menu Bar
==========

To see how menues work, modifiy your example once more:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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", CB(XmTest::menuSelected)), 
                    Entry("Menu Item &Two", CB(XmTest::menuSelected)),
                    NULLENTRY);
    menu->addSeparator();
    menu->addItem(	Entry("E&xit", CB(XmTest::quit)));
}

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

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

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Compile and run it - your 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 terminates the program.

Let's look what we have done to create the menu.
First we declare and define two new callback members for our test class, one
that prints out the selected item name and one that quits the application.
It is important that menue callbacks always get the menu item text as 
argument when they are called. You may declare them as void :: (char*)
functions, if you want to use this information.
The second thing is to create the menu. This is done by calling the inherited
member XmWindow::createDropdownMenu() which returns a pointer the the created
menu object.
To add labels to this new menu object we call its member addLabels() with a
null terminated list of strings (the & charcater designates the character to
be used in Motif as mnemonic). 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. Therefore one must call
setCurrentLabel() to specify which label should be the destination for
subsequent add calls.

The menu's addItems() member appends a null terminated list of Entry objects
to a menu. An Entry is a very simple object that carries name- and callback-
information. 
Those who are familar with C++ will recognize how that call works (it simply
creates a list of temporary Entry objects by calling their constructors),
those who are not should follow the above syntax.
Separators can be added by calling the menue's addSeparator() member.

The reference section contains the information how to specify the insert 
position of items, removing labels, items or separators, and disabling and 
enabling menu components.


Submenues
=========

(Currently available for Motif only)

Creating cascaded menues is also very simple; add a few lines to the
menu code of the XmTest() constructor...

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

    menu->addLabels("&File", "&Test", NULL);
    menu->setCurrentLabel("File");
    menu->addItems( Entry("Menu Item &One", CB(XmTest::menuSelected)), 
                    Entry("Menu Item &Two", CB(XmTest::menuSelected)),
                    NULLENTRY);
    XmSubMenu* sub = menu->addSubmenu("&Sub Menu...");
    sub->addItems(  Entry("Menu Item &Three", CB(XmTest::menuSelected)), 
                    Entry("Menu Item &Four", CB(XmTest::menuSelected)),
                    NULLENTRY);
    menu->addSeparator();
    menu->addItem(  Entry("E&xit", CB(XmTest::quit)));
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

...and the submenu will work. The applicable item operations on submenues
work exactly like their toplevel conterparts so there is no additional
explanation necessary.

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.


Resources
=========

These default 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 ##. If you want to know exactly how X-Resources are
organized and used consult the Xlib, Xtk and Motif / Xaw documentation.



2.3. Dialogs
------------

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


2.3.1. System Dialogs
- - - - - - - - - - -

In this first version of Xm++ the folowing system dialogs are included:

Message Box	  - you know it from various Motif applications
Prompter      - promts for a single line of Text
List Prompter - a prompter with an additional list
File Selector - the standard Motif file selection box

The advantage over the standard Motif way to use this simple things is,
that you need not to use callback functions for reacting to user input.
An example will show this - modify your sample application once more, edit
the menu callback member to make it look like this:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#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...");
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Running the application you will be prompted after each menu selection and
the further action depends on your Yes or No click in the message box. This
is possible because after creating a message box and calling its showMsg()
member this function does not return until the message box is closed.

Introducing Styles
==================

The XmMsgBox constructor takes a 'style' as the third argument
(XmSmboxQuestion in the case above). Xm++ Styles are integer constants
that are used to simplify the choice of Motif resources and/or window
manager properties. Some styles are defined out of 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 in
section ## shows, which styles are applicable to the different Xm++ objects.


To try the other available system dialog classes, replace the contents
of your menuSelected member:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// 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...");
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

All of these system dialogs can run modeless as well. Use the style
XmSdlgModeless and specify one function, 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).
The 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.

Note: The handling of modeless system dialogs will be enhanced in the
      next release of Xm++.



2.3.2. User Defined Dialogs
- - - - - - - - - - - - - -

The definition of contents, layout and functionality of user defined dialogs is
completely left to the 'user' of Xm++ (think of 'Programmer Defined Dialogs' in
respect to the end user). 

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 dialog- or resurce-editor).
Dialogs should be resolution independend (will be fulfilled in the next
Version of Xm++) but it makes no sense to make them resizeable because its
components should be sized as small as possible and as large as determined by
the designer. Therefore the automatic layout features of Motif / Xaw are used
by the Xm++ main window and toolbar (will be described later) classes, but not
here.

Xm++ provides the class XmUserDialog as interface to the Motif / Xaw Form
Widget and a lot of classes for handling the primitive user interface elements
like static text, buttons, lists and edit-fields (named 'controls' using 
Microsoft's terminology). In most cases one control maps to one specific Motif
widget, but controls may also consist of more than one widget or implement new
functionality.


Creating 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...
}

// cut your last version of the XmTest::menuSelected function, paste it
// below the sample dialog stuff and replace its contents with one line:

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you run the such modified example and select one of the menu items, 
your first self defined dialog will appear. You can edit text, click the
checkbox, radio buttons and push buttons (at the moment without any affect).

UserDialogs should be - like Main Windows - used by creating subclasses of
the XmUserDialog class. Besides the initWindowSize() function (you know it
already) there is only an createContents() function. This inherited virtual
function is thought to create all the child objects of the dialog. This
works like the main window's initialize() function with the only diffenence,
that this function is splitted into two other functions - createContents()
and initContents(). 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.).

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
(for a complete list of applicable styles for each control see the reference
section).

The control name is - as you learned with menues - the default label for all
controls showing text (staticText, buttons and checkBoxes). To change this
default there are two ways:

1) 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.

2) The setText() function - it is defined in the abstract class XmControl, the
    superclass of all controls, and can be used to change the label string
    in all controls showing text (it does not change the object's name).
    You should use setText() when creating a control that should hold a 
    large amount of text (like staticTexts), you must use it if you want 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, like other text-showing controls do. Therefore a label must be
assigned explicitly by application code or resources. Have you read the
resource example attendively? The group box label is a Motif subobject and
its resource path is your_object_path.group-label.what_to_change (usually
the labelString).


Adding Action
=============

For a dialog box to makes sense, something should happen in reaktion to user
interactions. Therefore we have to add callback functions for the controls.

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class SampleDialog : public XmUserDialog
{
    ...

	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, CB(SampleDialog::listSelect));
	checkBox("CheckBox")->setCallback(this, CB(SampleDialog::cbChanged));
	rb1->setState(TRUE);
	rb1->setCallback(this, CB(SampleDialog::rbChanged), TRUE, CB_OBJ_NAME);
	rb1->setCallback(this, CB(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(!strcmp(name, "Radio 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 your sample dialog still does nothing useful (it is
up to you now to create useful dialogs...) except showing, how the various
callbacks can be used to trigger changes in the dialog's state.

The priciples of callback functions were already dicussed in the main
window section. But if you look at the radio button callbacks in the code
above you see two additional parameters which were not used before:

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

The first boolean value means 'set or reset' and allows, if FALSE, to
remove a specific callback for a specific event. Caution: To remove a once
created callback successfully, callback name, receiving object and 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 Motif callback data
 CB_XM_HANDLE            (Widget)     the Motif widget id
 (void* )your_pointer    (void*)      your_pointer

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


Something on Layout
===================

As you have read in the introduction to this section, user dialogs should
be defined with a dialog editor. Such an editor should allow interactive
graphical layout of controls and then generate at least the add() statements
of the initContents() function (an intelligent tool would also allow to
specify the callback mebers, initial enabling or disabling and other features).

A very simple dialog editor is shipped with this release of Xm++. It produces
a file which contains the code of the initWindowSize() and createContents()
functions, a class template and a hexed binary description to allow re-editing
of a once created dialog. After copying and modifying the class template this
file should be #included in the application's implementation. 


Common Control member functions
===============================

These functions - some of them are virtual - are, like setText() mentioned
before, defined in the XmControl class and therefore inherited by all
controls:

char* defaultAction()     returns the name of the callback, which is set
                          if you give no callback name to setCallback()
bool isA(enum ctrlType)   allows to check the class of a control
bool changeStyle(xmStyle) styles are normally given as a constructor
                          argument, this function allows to change it afterwards
                          (only if the control has not been added to a
                          parent object)
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 (does not work
                          in this release!)
XmControl* disable()      make control insensitive
XmControl* enable()       make control sensitive
(inherited from XmObject)
XmObject* hide()          remove visual representation
XmObject* show()          recreate visual representation

The latter four functions (disable, enable, hide, show) can, as shown in the
example, be acessed in a convinient way via the parent dialog by giving only
the object names:

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

This saves code when changing multiple controls at once.

To learn about the individual functionality of the various controls consult
the class reference in section ##. You will find all information about member
functions, applicable styles and callbacks.


Controlling the tabbing order
=============================

The tabbing order of the controls within a dialog primary depends on the
order, in which the controls are created. If an application has no special
preferences, Motif creates default tab groups (between groups you move with
<tab> or <shift/tab>, inside groups with cursor keys) depending on the
control types.
If you want to define your own tab groups, you have to apply a special style
'XmStabStop' to each control, that should be a 'tab stop'.

When using Xaw, this kind of keyboard traversal is not available and the
tab stop style is ignored (but it will be wise to specify it, when it is
possible that your application should run under Motif someday).

Let us change the Sample dialog to demonstrate this. Modify the lines in
the initContents() function where controls are created and added in the
following manner:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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 Motif keyboard interface can be used to navigate in the dialog.


Pictures in Controls
====================

Xm++ also allows to create iconic user interface elements with static and
button controls. Let's do an example:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// add these bitmap definitions in the sample's global namespace...

#define ok_bmp_width 60
#define ok_bmp_height 25
static char ok_bmp_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07,
   0x00, 0x7f, 0x08, 0x04, 0x00, 0x00, 0xe0, 0x07, 0x80, 0x80, 0x08, 0x02,
   0x00, 0x00, 0xf0, 0x03, 0x40, 0x00, 0x09, 0x01, 0x00, 0x00, 0xf8, 0x00,
   0x20, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x20, 0x00, 0x4a, 0x00,
   0x00, 0x00, 0x0e, 0x00, 0x20, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x07, 0x00,
   0x20, 0x00, 0x3a, 0x00, 0x00, 0x80, 0x03, 0x00, 0x20, 0x00, 0x4a, 0x00,
   0x06, 0xc0, 0x01, 0x00, 0x20, 0x00, 0x8a, 0x80, 0x1f, 0xe0, 0x00, 0x00,
   0x20, 0x00, 0x0a, 0x81, 0x1f, 0x70, 0x00, 0x00, 0x40, 0x00, 0x09, 0x02,
   0x78, 0x38, 0x00, 0x00, 0x80, 0x80, 0x08, 0x04, 0xf0, 0x3d, 0x00, 0x00,
   0x00, 0x7f, 0x08, 0x08, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

#define cancel_bmp_width 60
#define cancel_bmp_height 25
static char cancel_bmp_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0xe0, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x10, 0x04, 0x00, 0x00,
   0x04, 0x00, 0x80, 0x07, 0x08, 0x00, 0x00, 0x00, 0x04, 0x07, 0xc0, 0x03,
   0x04, 0x00, 0x00, 0x00, 0x04, 0x1e, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00,
   0x04, 0x3c, 0x78, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0xf0, 0x1c, 0x00,
   0x02, 0x00, 0x00, 0x00, 0x04, 0xc0, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00,
   0x04, 0x80, 0x07, 0x00, 0x02, 0x60, 0x35, 0xc6, 0x04, 0x80, 0x0f, 0x00,
   0x02, 0x90, 0x4d, 0x29, 0x05, 0xc0, 0x1d, 0x00, 0x02, 0x10, 0x45, 0x21,
   0x05, 0xe0, 0x38, 0x00, 0x02, 0x10, 0x45, 0xe1, 0x04, 0x70, 0x60, 0x00,
   0x02, 0x10, 0x45, 0x21, 0x04, 0x30, 0xe0, 0x00, 0x04, 0x10, 0x45, 0x21,
   0x04, 0x18, 0xe0, 0x00, 0x08, 0x10, 0x45, 0x29, 0x04, 0x1c, 0xc0, 0x01,
   0x10, 0x94, 0x45, 0x29, 0x05, 0x0e, 0xc0, 0x01, 0xe0, 0x63, 0x45, 0xc6,
   0x04, 0x0e, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x03,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

// ...and add the following two lines (at the end of the function!)

void SampleDialog::initContents()
{
	...
	pushButton("Ok")->setImage(getCompiledImage(ok_bmp));
	pushButton("Cancel")->setImage(getCompiledImage(cancel_bmp));
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When re-running the sample you get iconic labels on the dialog's Ok and
Cancel buttons.

The label image is passed as a Pixmap, a X-Window data type. Xm++ allows
the generation of this data from two different sources:

X-Bitmap - the standard X-Window bitmap data format, created usually with
  the 'bitmap' editor (see man page). This bitmap data can either be included
  in the source file (like shown above) and converted with getCompiledImage()
  or loaded from a bitmap file at runtime with getRuntimeImage().
gif - the Compuserve GIF (tm) images. Unlike bitmaps you can use this format
  to create color icons. This format can only be used by loading from files
  with getRuntimeImage(), the filenames must (!) end with '.gif'. The
  conversion from a gif file to a Pixmap is done with a slightly modified
  version of the 'xgifload' code by Patrick J. Naughton, which has been added
  to the Xm++ library.

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

Try out loading images at runtime doing the following:

execute the command  '/usr/bin/X11/bitmap ok_bmp 60x25'

draw something, save it and do the same again for a file 'cancel_bmp'.
Change the last two lines in the initContents() function to:

	pushButton("Ok")->setImage(getRuntimeImage("ok_bmp"));
	pushButton("Cancel")->setImage(getRuntimeImage("cancel_bmp"));

Now run your sample again. The buttons should now show the result of
your drawing effort.

If you have access to gif pictures and utilities, scale gif images to
the aproppriate size (60x25  - Caution: the controls resize automatically
to the image size, applying an image of the wrong size may destroy your
dialog layout) and change the file name parameters for the getRuntimeImage()
statements (don't forget, that they must end with '.gif').

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.


Toolbars and Toolboxes
======================

A Toolbar is a special dialog which performs an automatic layout for 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.

Let's test toolbar and toolbox in our sample, 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();
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After running this you will have an idea on how these tool objects can be used.

The main reason for implementing a toolbar is to provide shorthands for menu
functions. If you click 'One' or 'Two' you do the same as using the similar
named menu items. Of course, you will have iconic buttons in real applications.
Another benefit of tool objects is to provide a few often needed controls
easily accessible to the user, thus avoiding popping up dialogs.
In the sample toolbar / box you also can create further toolboxes, type text,
select an option and start the insertion of the text 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)




3. Xm++ Class Reference
_______________________


(see separate draft reference.txt)
