

.de PS
.in +10
..
.de PE
.in -10
..

.T tclMotif - a tutorial

.H 1 Basics

.H 2 Introduction

tclMotif is a binding of the tcl language to the Motif widget set.
This allows access to most of the features of Motif through the
simple language tcl, instead of the more complex C language that Motif is
written in. 

There are major gains in this, primarily in code size and complexity: at
tclMotif program typically is one tenth the length of the corresponding C
program. It also dramatically simplifies some of the more complex areas of
Motif programming, particularly in the handling of strings: tclMotif looks
after the C code that otherwise the programmer would have to deal with. 

There is a downside, but it is comparatively minor. tcl is an interpreted
language with a single data-type: string. There is a speed penalty in
converting to and from strings. This would bar tcl from, say, a realistic
language for matrix manipulations. However, sitting above a large and
complex widget toolkit such as Motif, this speed penalty is not noticeable.
tcl does not have the structuring and hiding capabilities of C, nor the
object language mechanisms of C++. This may cause problems in very large
projects, though tcl programs of over 100,000 lines in length (probably
equivalent to 1,000,000 lines of C) do exist.

.H 2 "Starting moat

tclMotif is usually accessed through an interpreter called ``moat''. This
is an interpreter that can either run interactively or on a tclMotif file.
If the command is typed with no arguments then it displays the prompt
.PS
%
.PE
This acts just like the ordinary tcl interpreter ``tclsh'', and tcl
commands can be typed into this interpreter just like into tclsh. The only
difference is that it will also accept the additional Motif commands
supplied by tclMotif.

When run with a single argument that is the name of a file as in
.PS
moat example1
.PE
it will read tclMotif commands from the file (here, ``example1'') and
execute them.

There is a third way of running tclMotif, and this is the most common use.
It relies on a number of things to make it work, though. First, the
``moat'' program must be installed in a standard place. Most tcl-based
interpreters such as tclsh and moat assume that this is in the directory
/usr/local/bin. If neccessary, you or your system supervisor should ensure
that moat is either in this directory or that a link is made from this
directory to it. Secondly, yous must be running a suitable command shell.
All of the common shells such as sh, ksh, csh, bash, ksh, tcsh, zsh are
fine. With these given, you can run a file as a tclMotif progam program by
.AL 1
.LI
Making the first line of the file
.PS
#!/usr/local/bin/moat
.PE
.LI
Making the file executable by
.PS
chmod u+x <file>
.PE
.LE
From then on, using the filename as a command will invoke the moat
interpreter to read and execute the rest of the file.

.H 2 "Hello World

Motif programs run under an event driven model. That is, objects are
created that respond to events such as button presses, key strokes, and
other things such as being covered and uncovered by other windows. To
manage this, Motif relies on a toolkit called Xt that allows objects to be
created and handles the events to the obects. Any Xt-based system must
start up the Xt world, create some objects and then hand control to an Xt
event handler that sits in a loop reading and processing events.

In tclMotif, the Xt world is started by a call to ``xtAppInitialize''. This
creates a special object ``.'' which is used to build all other objects
from. Two special calls are made to this object: ``realiseWidget'' that
creates windows for all objects, and ``mainLoop'' that enters the event
processing loop.

The actual objects are supplied by Motif. For ``Hello World'' a very
simple object can be used because it just has to display a piece of text.
This object is a Label. It is created by a call ``xmLabel'', and has its
labelString value set to ``Hello world''
.PS
xtAppInitialize
xmLabel .label -labelString "Hello world"
. realizeWidget
. mainLoop
.PE

All widgets are ultimately descended from the toplevel widget ``.''. The
hierarchy is shown explicitly in the widget names, so ``.label'' is an
immediate child of ``.''. This is similar to the path name for files in
Unix, with the ``.'' taking the place  of the Unix file separator ``/''.
The third and fourth lines are method calls for the  ``.'' object to
create windows and enter the event loop.

.H 2 "Creating widgets

Motif has a large number of widgets. In C, there is a function to create
each one of them as XmCreate<widget>. tclMotif simplifies this a little to
xm<widget>. So in the example above ``xmLabel'' creates a Label widget.
Similarly, ``xmText'' creates a Text widget, ``xmList'' creates a List
widget and so on. A program that creates a PushButton widget showing
``Hello world'' is
.PS
xtAppInitialize
xmPushButton .label -labelString "Hello world"
. realizeWidget
. mainLoop
.PE
where only the widget creation command has changed.

Each widget has a parent, so that widgets form a tree. The toplevel widget
is ``.'' and forms the root of this tree. The toplevel widget is created by
the command ``xtAppInitialize''.  A widget is known by name, which is
specified as the first argument to the widget creation command. This name
contains the full path from ``.''. In the examples shown, this path is very
short, as each widget is just the child of ``.'' It is worth noting at this
point that the toplevel widget can only have one widget child at a time
(except for dialog widgets which are discussed later).

In general, an application will consist of a non-trivial tree of widgets.
Some widgets are \fI container \fP widgets, whose purpose is to contain and
manage their children. The major containers are RowColumn, Form and
MainWindow.

In most instances, a MainWindow widget will be the immediate child of ``.'',
as this is set up to support a menu bar and a principal window containing
the application, with optionally some scrollbars to control the visible
area of the application.

A Form is used for arbitrary geometry arrangements, whereas a RowColumn is
used for regular arrangements. In this example, a RowColumn inside a
MainWindow holds a set of Labels:
.PS
xmMainWindow .main
xmRowColumn .main.rc
xmLabel .main.rc.label1
xmLabel .main.rc.label2
xmLabel .main.rc.label3
.PE

A widget when created will not show on the screen. Windows have to be
created, which is done by ``. realizeWidget''. In addition, a container
will not show a widget unless it has been placed under the geometry
management of the parent. This is done either at creation time with an
optional second parameter ``managed'' or by a special method
``manageChild'':
.PS
xmLabel .main.rc.label1 managed 
xmLabel .main.rc.label2
.main.rc.label2 manageChild
.PE

.H 2 "Resource control

A widget has many configurable properties. For example, in a Label widget
the label itself can be set; in addition, the font used to render the label
is configurable, as well as whether it should be centred or justified to
the right or left. Width, height, foreground and background colour are
configurable properties for most widgets. Each of these properties is
called a \fI resource \fP, and the value is called a \fI resource value\fP.

The set of resources and possible values is documented for each widget.
For example, the ArrowButton widget has a resource ``arrowDirection'' with four
possible values: arrow_up, arrow_down, arrow_left and arrow_right. Resources
can be set using the method ``setValues'' for each widget, or at creation
time.

To set resources at creation time, a list of resource names/resource value
pairs follows the widget name. For example,
.PS
xmArrowButton .arrow managed \
    -arrowDirection arrow_up \
    -width 100 \
    -height 100
.PE

To set resources at any time, the set of name/value pairs follows the
method name, as in
.PS
.arrow setValues \
    -arrowDirection arrow_down \
    -width 50
.PE

In a similar manner, resource values can be obtained from a widget by using
the method getValues. This also takes a list of pairs, this time the
resource name and a tcl variable to store the value in. For example, to
find the width and height of a widget and store them in variables w and h
respectively,
.PS
.arrow getValues \
    -height h \
    -width w
.PE

.H 2 "Callback functions

When a button is pushed, when a list item is selected, when a key is
pressed using a Text widget, etc, the widget reacts in some way. For
example, the pressed button appears to sink into the surface, the list item
is highlighted, and the character pressed is entered into the Text widget.
This is also the time that suitable user code is executed. When the widget
executes one of the internal actions that changes its state it can call tcl
code that has been registered with the widget. Such code is attached to a
``callback'' and is called ``callback code''. Each widget has a documented
set of callbacks. For example, for PushButton there is the
activateCallback. activateCallback code is executed whenever the button is
pressed and released. In a similar manner, when the List is in single-select mode,
whenever an item is selected code attached to the singleSelectionCallback
is executed.

Code is attached to the callback by using the appropriate callback method.
There is a callback method for each of a widget's callbacks, and the code
is given as the argument to this method. For example, to say ``Ouch''
whnever the button is pressed and released, 
.PS
.btn activateCallback {puts stdout "Ouch"}
.PE

Any tcl code may be placed in the callback. For example, a call to a
function may be made, or a series of calls may be made. For example,
.PS
.btn activateCallback { 
    puts stdout "That"
    puts stdout "hurt!"
}
.PE

Callback code is executed in the global context, so that any unevaluated
variables are evaluated at runtime in the global environment. That is,
in
.PS
.btn activateCallback {puts stdout $msg}
.PE
the braces {} protect the variable msg against expansion when the code is
attached to the callback. When the code is actually executed the variable
msg will be evaluated as a global variable.

When a callback is invoked, there is often a great deal of useful
information that is available to the callback code. The simplest and most
commonly used of these is the widget name. This and other information can
be found by means of a pattern which always starts with ``%''. For example,
the pattern for the widget name is ``%w'':
.PS
.btn activateCallback \
    {puts stdout "widget %w was pushed"}
.PE

.H Rowcolumn

.H Form

.H 2 Menus

.H 2 "Dialog widgets

.H 2 List

.H 2 Text

.H 2 "Extended example

xmeditor is an editor based on the Text widget. It gains all of its edit
functionality from the Text widget, so that this program is basically just
a wrapper around this widget. Nevertheless, it shows a large number of the
features of tclMotif. This example is a modification of the
xmeditor example that appears as an example in the Motif distribution.

.H 3 "Xt world
There is a section to setup the basic geometry and start the Xt world
going:
.PS
xtAppInitialize \
        -fallbackResources {
                {*XmText.columns: 80}
                {*XmText.rows: 24}
        }

xmMainWindow .main managed

# menu stuff to create .main.menuBar goes in here
# ....

xmScrolledText .main.text managed \
        -editMode multi_line_edit
.main.text modifyVerifyCallback {set fileSaved 0}


.main setValues -menuBar .main.menuBar \
                -workWindow [.main.text parent]

. realizeWidget
. mainLoop
.PE

The fallback resources set the default sizes of the Text widget. These can be
overridden by user or app-defaults files.
The application resides inside a MainWindow, which contains a menu bar and a 
ScrolledText widget. The relation of the children within the MainWindow by
the line
.PS
.main setValues -menuBar .main.menuBar \
                -workWindow [.main.text parent]
.PE
which sets the menuBar and workWindow resources. Note that the 
.I parent
of the Text widget has to be used. This is the only way to get at the
ScrolledWindow that is the real child of the MainWindow.

The Text widget by default is a single-line editor. This is clearly
inappropriate here and is not even something that you would want to
leave to user resource files. So the editMode resource is hard-coded
to have the value ``multi_line_edit''.
There is a callback set for the Text widget: whenever any text is entered
into the widget, changing its contents, the global variable fileSaved is
set to false, so that the application can determine that the file on disk
is not the same as in the widget.

Finally, all windows are created by calling ``realizeWidget'' on the
widget ``.'', and mainLoop is entered on the same widget.

.H 3 "Menu system

The menu system consists of three toplevel entries: File, Edit and Help.
Under File is the pulldown with New, Open, Save, Save As..., Quit. Under
Edit is the pulldown with entries Cut, Copy, Paste and Clear. Help has only
one entry, About.

The subset of this that defines the menubar and the file pulldown is
.PS
xmMenuBar .main.menuBar managed

# file pulldown
xmPulldownMenu .main.filePane
xmCascadeButton .main.menuBar.File managed \
        -subMenuId .main.filePane

xmPushButton .main.filePane.New managed
.main.filePane.New activateCallback newFileCB

xmPushButton .main.filePane.Open managed
.main.filePane.Open activateCallback openFileCB

xmPushButton .main.filePane.Save managed
.main.filePane.Save activateCallback "saveFile"

xmPushButton .main.filePane.SaveAs managed
.main.filePane.SaveAs activateCallback "saveFileAsCB"

xmPushButton .main.filePane.Quit managed
.main.filePane.Quit activateCallback quitFileCB
.PE

A small comment is in order: the buttons pick up their labelString value by
default from their name, which is File, New, etc. This gives the default as
English names. If other names are desired, they may be set using resources
files. 

Each of these buttons calls a callback function. These functions will be
discussed later.

.H 3 "File state

The contents of the editor may be the same as the external file. This
will occur after a Save and before any changes have occurred, after
the file has been loaded, or when a new file has been created. In this
case, many actions such as exiting the editor or loading a new file
can be done without any problems. On the other hand, the two may have
different contents. In this case many actions may need to call up a
dialog of some kind. In xmeditor this state is represented by the tcl
variable ``fileSaved''. In various places the value of this is set to
1 (true). It is set to 0 (false) every time a change is made to the
widget contents through an edit action. This is handled very easily in
the ``modifyVerifyCallback'' which is called on every text entry by the
user:
.PS
.main.text modifyVerifyCallback {set fileSaved 0}
.PE

.H 3 "Saving and loading

Two simple procedures are just concerned with saving the text from the
Text widget and loading it from a file. These are ``saveFile'' and 
``loadFile''. The Motif part of saveFile of interest is extracting
the contents from the Text widget:
.PS
set str [.main.text getString]
.PE
Similarly, from loadFile, lines are read fromthe file and placed in the
Text widget by
.PS
.main.text setString ""
while {[gets $fileID str] >= 0} {
    .main.text insert [.main.text getLastPosition]] "$str\n"
}
.PE
This reads lines successively and inserts them at the end of the text in
the widget.

.H 3 "Save dialog

One of the more complex functions is ``saveFileDialog''. This function
is called when an action such as repalcing the contents of the Text
widget with new text has been requested, but the current contents have
not been saved. The proper action in this case is to request confirmation
from the user to save or discard the contents. Usually such a confirmation
is run as a 
.I modal
dialog
i.e. it must be answered before any other interaction with the application
is allowed.

Creating the dialog is straightforward, as it is just an errorDialog with
messageString set to a ``Save file?'' message. making it modal is done
in several stages. First the dialog is made modal by setting the
``dialogStyle'' resource to dialog_full_application_modal. 
This in itself is enough to ensure that no
interaction with the rest of the application can take place.
.PS
xmErrorDialog .fileSaveDialog managed \
    -messageString "Save $currentFile?" \
    -dialogStyle dialog_full_application_modal
.PE

While this sets the interaction style with the application, it does not set
the control flow in the program. The use of such modal dialogs is often
in linear code:
.PS
if modal_dialog returns true
   do something
else
   do something else
.PE
To ensure this kind of logic, the dialog function itself cannot return
until the dialog is over. The simplest way of ensuring this is to enter
an event loop within the dialog function, which only terminates when the
dialog is over. This in turn will only happen when the user presses either
the Cancel or OK buttons. So an event loop must be set up which only
terminates when the Cancel or OK callbacks are invoked:
.PS
global stillModal
global answer

.fileSaveDialog okCallback {set stillModal 0; set answer 1}
.fileSaveDialog cancelCallback {set stillModal 0; set answer 0}

set stillModal 1
while {$stillModal} {
    . processEvent
}
.PE

.H 3 "File selection

At various points, a file must be chosen. This should be done by a 
FileSelection dialog. This selects a file and loads it:
.PS
xmFileSelectionDialog .fsd managed
    .fsd.Help unmanageChild
    .fsd okCallback {loadFile %w %value}
    .fsd cancelCallback {[.fsd parent] destroyWidget}
.PE
