\begindata{text,539040528} \textdsversion{12} \template{default} \define{global

advertisement
\begindata{text,539040528}
\textdsversion{12}
\template{default}
\define{global
}
\define{itemize
}
\define{enumerate
}
\define{programexample
}
\define{footnote
attr:[Flags OverBar Int Set]
attr:[FontSize PreviousFontSize Point -2]}
\smaller{\indent{Copyright 1989 IBM
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear in
supporting documentation, and that the name of
IBM not be used in
advertising or publicity pertaining to distribution of the software
without specific, written prior permission.
IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
}}
\formatnote{\chapter{Example 1:
program}}
Creating a class and an application
This section describes how to create a simple class and how to create an
application program that uses the class. The application program will
create
a new window, create an instance of the class, and set the instance in
the
window. Then the instance of the class will draw the string
\italic{hello
world} in the window's center. This example program illustrates the
following
activities:
\itemize{declaring a class
defining class procedures and methods
drawing, using the Andrew Toolkit class \italic{Graphic}
compiling in the Andrew Toolkit environment
creating and running a stand-alone application program
}
After reading Example1, you will know the very basic program structure
needed
to create a simple Andrew Toolkit class and a stand-alone application
program
that uses an object that is an instance of that class. Later examples
will
introduce the structures you need in order to build an object that
responds to
user input (e.g., mouse, keyboard, menus), reads and writes to files, and
so
on.
The discussion that follows presents a step-by-step description of how to
write the example program. If you were to follow the steps, you would
produce
a program, called \italic{helloworldapp}, in five files:
\itemize{
a hellov.ch file -- will contain the class definition for the
\italic{helloworldview} class, the class whose instances draw the string
\italic{hello world}. The class \italic{helloworldview} will be a
subclass of
the Andrew Toolkit class \italic{view}.
a hellov.c file -- will contain statements that import Andrew Toolkit
classes
and define the class \italic{helloworldview}'s methods and class
procedures.
a helloa.ch file -- will contain the class definition for the application
program that will display an instance of the \italic{helloworldview}
class in
a window on the workstation. By convention, Andrew Toolkit application
modules are named \italic{<x>app}, where \italic{<x>} is the name of the
application or an abbreviation for it (e.g., \italic{helloworld}) and
\italic{app} is an abbreviation for the class \italic{application}.
a helloa.c file -- will contain declarations needed by the Andrew Toolkit
linking and loading facilities as well the definition of an application
method
that creates an instance of \italic{helloworldview}, creates a window,
puts
the object in the window, and enters an interaction loop.
Makefile -- will contain the directions for compiling, linking and
loading the
modules.
}
For a complete listing of these files, see \italic{Program Listing for
Example1}, p.
\begindata{textref,539236104}
\textdsversion{12}
# ExOneListing\
\enddata{textref,539236104}
\view{textrefv,539236104,1009,0,0}.
On a first reading of this section,
you
may find it useful to skim the program listing, then refer to the listing
when
needed as you study how to build the program. The source code is
available in
the directory /usr/andrew/examples/atk/ex1, together with the compiled
program.
Although the discussion of the steps refers directly to this example, the
information applies generally to the creation of any class or inset that
will
be used in a \indexi{Stand-alone application} and to the creation of any
stand-alone application.
\section{Running the example program}
Figure
\footnote{\
\begindata{textref,539315976}
\textdsversion{12}
ex1\
\enddata{textref,539315976}
\view{textrefv,539315976,1010,0,0}} depicts the application program as it
would appear on a workstation screen. Before reading the discussion of
the
example program, you may find it helpful to run the program on your
workstation. If you are running the X11 window manager, first type
\italic{setenv BE2WM
x11} from your Xterm window, before proceeding
with
the following steps. \indexi{X11++Environment variable}
\formatnote{\bold{Action 1:}
\bold{command }prompt type
\formatnote{To run the program, at the
\bold{/usr/andrew/examples/ex1/hw}
and press the Enter key.
}
\bold{Response: }The program will produce a window with \italic{hello
world}
centered in the body as in figure
\begindata{textref,539314952}
\textdsversion{12}
ex1\
\enddata{textref,539314952}
\view{textrefv,539314952,1011,0,0}.
\bold{Action 2:} Re-shape the program's window.
\bold{Response:} The object will respond to an update request and redraw
\italic{hello world }in the center.
\bold{Action 3: }Click with a mouse button.
\bold{Response:
}The program will make no response.
This simple object
does not respond to keyboard or mouse input and it has no menus.
Later
examples will illustrate how to extend the program so that the view
responds to user input.
\bold{Action 4:
title
}To quit the program, move the cursor to the window
bar, pop-up the menus and choose \bold{Zap} from the \italic{This Window}
card.
\bold{Response:
}The program will disappear from the screen.
}
Figure:
\formatnote{\formatnote{(Helloworld application program)}
}\
\begindata{bp,538929224}
\enddata{bp,538929224}
\view{bpv,538929224,1012,0,0}
\section{Creating a class}
Creating a class \indexi{Class++Creating} in the Andrew Toolkit involves
two
steps:
\enumerate{
Declaring the class as a subclass of an Andrew Toolkit class
Writing class procedures and methods for the class.
}
\subsection{The Andrew Toolkit class View} \indexi{View}
\indexi{Class++View}
In the next section, we will define the class \italic{helloworldview} to
be a
subclass of the Andrew Toolkit class \italic{view}. The class
\italic{view}
is at the heart of the Andrew Toolkit. It provides the methods, class
procedures and data structures needed to (1) display a data object in a
window, (2) request display updates and respond to such requests, and (3)
respond to mouse and keyboard inputs. Thus, to create any class that
interacts with the user, you should declare it as a subclass of
\italic{view}.
To simplify the exposition, Example1 does not include a data object and
does
not not respond to mouse and keyboard inputs, but it does respond to
requests
to update the display. In general, you will want to create a view-data
object
pair (called an inset) or some hierarchy of insets, and an application
module
to run/display the program. Insets will be discussed in greater detail
in
Examples 10 and 11.
\subsection{Choosing a name for a class} \indexi{Class++Naming}
\indexi{View++Naming}
The first step in creating a class is to pick a name for the class. The
name
is important for two reasons. First, most Toolkit objects are loaded
dynamically. \indexi{Dynamic loading} When the Andrew Toolkit encounters
the
name of a class to load dynamically, it searches for the class along a
class
path, a path that specifies a list of directories to search. Thus, if
you
want to avoid the Toolkit finding the wrong class, the name you pick
should be
unique and not conflict with already existing ones. The second reason
the
name is important is that, for some classes, in particular, for classes
that
are data objects, \indexi{Data object++Naming} the user will sometimes
type
the class name in order to obtain an instance of it. For example, the
user
types the name of a class in order to dynamically load an instance of it
into
the Andrew Toolkit-based editor, \bold{ez}. In such cases, the name
should be
memorable for users. Since the class in Example 1 is not a data object,
it
need only be unique, not memorable. We chose the class name
\italic{helloworldview}.
You should also pick a filename for the class. The easiest filename to
use is,
of course, the class name itself. However, for portability, filenames
should
be no longer than eight characters. If the class name is longer than
eight
letters, you should pick a shorter abbreviation to use as the filename.
We
chose the filename \italic{hellov} for the class \italic{helloworldview}.
(The
abbreviation is used only for files; the methods for this class and the
name
used to invoke it will still be \italic{helloworldview}.)
\indexi{Class++Filename}
There is no direct way to tell if the class name you chose is already
being
used. However, you can see whether the \italic{filename} you picked is on
your
class path. \indexi{Class path} In the \bold{command}, type:
\leftindent{\bold{whichdo \italic{<your class filename>}}}
The command \italic{whichdo} \indexi{ \italic{whichdo}} (\italic{which}
\italic{d}ynamic \italic{o}bject) is analogous to the \italic{Unix 4.2
BSD}
command \italic{which} (see \bold{help which} for more information); it
takes
a list of class names and looks for the files which the Andrew Toolkit
would
dynamically load in response to the names. Each argument is expanded if
it is
aliased, and searched for along the dynamic class path,
\smaller{CLASSPATH}.
Both aliases and path are taken from the user's \italic{.cshrc} file.
The
following is the recommended class path for inclusion in a
\italic{.cshrc}.:
\programexample{
if (! $?SYS) setenv SYS `/usr/andrew/bin/sys` setenv CLASSPATH
.:"$SYS":$HOME/lib/do/"$SYS":/usr/andrew/dlib/atk
}
Or, if you are running the Andrew Toolkit on top of the X11 window
manager,
type the following from your Xterm window:
\programexample{
setenv BE2WM x11
}
With this class path, the Andrew Toolkit dynamic object search will look
in
the \italic{current directory}, in \italic{/usr/andrew/bin/sys}, in the
user's
\italic{home directory} under \italic{lib/do} and in
\italic{/usr/andrew/dlib/atk}.
If you do not have a class path defined in your \italic{.cshrc}, you will
probably want to define one. The class path defaults to
\italic{/usr/andrew/dlib/atk}. \indexi{Class path++Default}
\subsection{Declaring a class} \indexi{Class++Declaring} \indexi{Class
declaration}
To create a class, you should declare the class in a \italic{.ch}, or
header
file, that has the filename you have chosen for your class. (Note that
the
file extension \indexi{File extensions} is by convention \bold{ .ch},
not\bold{ .h}). Thus the class declaration for the class
\italic{helloworldview} is in the file \italic{hellov.ch.}
The general format for a class declaration is: \indexi{Class
declaration++Format}
\example{
\bold{class} <class name> \bold{\bold{[}}<class file name>\bold{\bold{]
:}}
<super-class> \bold{[}<superclass file name>\bold{] \{} \bold{
overrides:}
list of inherited methods that this class is overriding \bold{
methods:}
list of methods for this class \bold{
macromethods:}
list of methods for this class that are macros \bold{
classprocedures:}
list of class procedures \bold{
macros:}
list of class procedures that are macros \bold{
data:}
data structures \bold{
\};}
}
If the filenames of the class or superclass are the same as their class
names,
you need not include them. You can also leave out the line "overrides:"
if
there are no overrides, or "methods:" if there are no methods, and so
forth.
The following is the declaration for the class \italic{helloworldview},
from
the file hellov.ch:
\programexample{
class helloworldview[hellov] : view \{ overrides:
FullUpdate(enum view_UpdateType type, long left, long top, long
width,
long height); \};
}
The declaration specifies that a class, named \italic{helloworldview,}
is to
be a subclass of the Andrew Toolkit class \italic{view}. The filename
for
\italic{helloworldview} is \italic{hellov}, and the filename for
\italic{view}
is \italic{view}. One method, \italic{FullUpdate}, will override
\indexi{Override} \indexi{Methods++Overriding} the class \italic{view}'s
\italic{FullUpdate} method, which it otherwise would have inherited. No
other
methods, special class procedures, or data are necessary in the class
declaration for this simple example.
\subsection{Class hierarchies and inheritance} \indexi{Class hierarchy}
\indexi{Inheritance} \indexi{Class++Hierarchy}
In the Andrew Toolkit, classes form a hierarchy. Subclasses
\indexi{Subclass}
inherit the methods and data structures of their parents, or
superclasses.
\indexi{Superclass}
Although \italic{helloworldview} declares no methods or data, it inherits
the
methods and data from its parent superclasses. The class \italic{view},
\italic{helloworldview}'s immediate parent, is part of the class
hierarchy,
\italic{basicobject}, \italic{observable}, \italic{view}, depicted in
Figure
\begindata{textref,539314696}
\textdsversion{12}
classhierarchy\
\enddata{textref,539314696}
\view{textrefv,539314696,1013,0,0}.
\formatnote{Figure: \formatnote{(Part of the Andrew Toolkit class
hierarchy)}
\center{basicobject
|
|
observable
|
|
view
|
|
helloworldview}
}
Therefore, \italic{helloworldview} inherits the methods and data
structures of
\italic{basicobject, observable} and \italic{view}. For example,
\italic{view}
provides the method\italic{ Hit}, which will be called when the user
clicks
with the mouse on the left or right button. Because
\italic{helloworldview}
does not override \italic{view_Hit}, it will inherit that method. So,
when
the user clicks with the mouse in a window containing a
\italic{helloworldview}
object, the Andrew Toolkit \italic{Interaction Manager}, which manages
interaction events, will call the method \italic{helloworldview}
inherits,
\italic{view_Hit}. \italic{view_Hit} does nothing in response to a mouse
hit,
so the \italic{helloworldview} object in this example will not respond to
mouse hits. Example 3 will show how to override \italic{view}'s method
so
that the class \italic{helloworldview's} instances will respond to mouse
hits.
In general, you will declare a class as a subclass of a superclass.
\indexi{Class declaration} \indexi{Subclass} \indexi{Superclass} If you
leave
the \italic{\bold{:} <super-class>} slot blank, the superclass defaults
to
\italic{basicobject}, which is the class at the top of the Andrew
Toolkit
class hierarchy.
\subsection{C data structure for a class} \indexi{Data structure}
\indexi{Class++Data structure} \indexi{Class preprocessor}
\indexi{Preprocessor} \indexi{Class++Preprocessor}
There is a class preprocessor that converts a class declaration to (1) a
C
structure for the class and (2) a set of macros that the system uses to
call
both the methods and class procedures associated with the class. The
preprocessor also generates code that defines class procedures common to
all
classes and some procedures required by the Class run-time system.
When processing a \italic{basic} class (i.e., a subclass of
\italic{basicobject}), \indexi{Basicobject} the preprocessor creates a
structure that contains the fields provided in the class'
\italic{data\bold{:}}
declaration, along with an initial first entry that points to the set of
methods associated with the class. When processing a \indexi{Subclass}
\italic{subclass} declaration, the preprocessor creates a structure that
contains the fields provided in the class' \italic{data\bold{:}}
declaration
appended to a structure containing the data associated with the
superclass.
\indexi{Superclass}
For example, the preprocessor creates the following structure for the
class
\italic{helloworldview}:
\programexample{
struct helloworldview \{
union \{
struct basicobject_methods *helloworldview_methods;
struct observable observable;
struct view view;
\} header;
\};
}
You will learn how to run the class preprocessor later in this example.
\subsection{Accessing data structures for a class} \indexi{Data
structure++Access}
You access a field defined in a subclass \indexi{Subclass} in the same
way you
access one in a normal C structure.
For example, given a pointer,
\indexi{Class++Pointer to} \italic{hwv}, to the object's
\italic{helloworldview struct}, you would write \italic{hwv->header.view}
to
access the field \italic{view}. \formatnote{@foot<Later examples will
introduce examples in which accessing fields in the structure is
useful.>}
To access a field defined in a superclass, \indexi{Superclass} you should
use
the class' access method for the field if one is defined. An access
method
\indexi{Class++Access method} is simply an encapsulation of a reference
to
the field. By using the access method, you avoid making your subclass
dependent on implementation details of the Andrew Toolkit (This is
especially
important since the implementation might change). If an access method
for the
field is not defined and you really need to access it, you can
dereference
through the header. For example, if you needed to find out how many
observers
a particular \italic{helloworldview} object had, you might need to access
the
class \italic{observable}'s \italic{nObservers} field. There is no
access
method defined for it. (Since access methods are defined for most fields
which you will need to access, you should spend some time thinking about
whether there is a better way to do what you want to do). Given a
pointer,
\italic{hwv}, to the object's \italic{helloworldview struct}, you would
write
\italic{hwv->header.observable.nObservers}.
\subsection{Writing class methods} \indexi{Class++Methods}
\indexi{Methods}
Up to this point, we have seen how to declare a class and have
introduced the
data structures generated by the class preprocessor. In this section, we
discuss how to write class methods and illustrate it by writing a class
method
for \italic{helloworldview} that allows it to respond to requests from
the
Andrew Toolkit \italic{Interaction Manager} to update the screen.
A \italic{method} is like a C procedure, except for how it is invoked.
When
you call a method, the system assumes that the first argument is the
object
(i.e., an instance of a class) that you are requesting to perform the
method.
Each class has a \italic{method dictionary} which associates the names
of
methods with the actual methods for carrying out the operation. When you
call
a method, the system searches\formatnote{@foot<
The system does not actually search on each invocation, although it is
useful
to think of it as a search. In order to increase efficiency, the search
is
done once and stored in a table for fast look-up.>}
the method dictionary of the object's class for a method to execute.
a
method is not found in the object's class, the system searches its
superclass'
method dictionary, and so on until it reaches the root
\italic{basicobject},
the root of the class hierarchy.
If
Methods increase the understandability of your code because they allow
you to
create useful data abstractions \indexi{Data abstraction} that hide
implementation details. For example, suppose you are writing an
application
in which you defined an abstract data type, \indexi{Abstract data types}
linked list, consisting of the data \italic{Element} and the operations,
\italic{InsertElement}, \italic{DeleteElement} and so forth. At the
level of
data abstractions, you would be dealing with a single data element and a
set
of operations. In a conventional programming language like C, however,
you
might need to create several actual data types and duplicate sets of
procedures to implement a single abstract data type. For example,
suppose
that you needed to work with both strings and integers. In that case,
you
would need to define two types of arrays, an array of \italic{int}s and
an
array of \italic{char*}, and create two procedures for the
\italic{InsertElement} operation, the one \italic{InsertInteger} and the
other
\italic{InsertString}. When you called these procedures, you would use
the
two different procedure names. Thus, in the implementation, you lose the
data
abstraction. An object-oriented system allows you keep the useful
abstract
representation. You would still need to \italic{write} two methods, one
for
integers and one for strings. But you could set your program up so that
you
could \italic{use the same method name}, for example,
\italic{InsertElement},
to invoke both methods. \indexi{Methods++Naming}
\paragraph{Calling a method} \indexi{Methods++Calling}
The call to a method is actually an indirect call via the list of methods
attached to an instance of the class. Thus, if you call
\italic{helloworldview_MoveTo}, for example, without an instance of the
class,
in this case \italic{hwv}, the program would not call
\italic{helloworldview__MoveTo} since the parameter would not contain the
list
of methods pointing to \italic{helloworldview__MoveTo}.
As mentioned previously, the \italic{helloworldview }class inherits
methods
from its superclasses \italic{observable} and \italic{view}.
\indexi{Superclass} When you use a method defined in another class, you
should call it with the class name of its first actual parameter, not the
class name of the method itself. \indexi{Methods++Inheritance} This is
because an intervening class may have redefined the method or may
redefine the
method in the future. Thus, to insure that you use the method
appropriate to
your place in the class hierarchy, you call
\italic{helloworldview_GetVisualBounds}, not
\italic{view_GetVisualBounds},
even though \italic{GetVisualBounds} is defined in \italic{view}, since
the
first parameter to the method is \italic{hwv}.
In general, for any object \italic{x} in \italic{classx}, (\italic{i.e.},
\italic{struct classx *x)}, you call \italic{classx_methodname (x, <other
parameters>)} in order to call either a method for object x or a method
for
any of \italic{x}'s superclasses. Thus, for object \italic{hwv} in the
class
\italic{helloworldview}, you write \italic{helloworldview_GetVisualBounds
(hwv, <other parameters>)} to call the \italic{view} method
\italic{GetVisualBounds}.
\paragraph{Defining vs. using class procedures and methods}
\indexi{Methods++Defining} \indexi{Class procedures++Defining}
Note that when you \italic{define} class procedures or methods, you must
use a
double underscore; when you \italic{use}, or call, the procedures or
methods,
you only use a single underscore.
For example, if you are creating the
class
\italic{helloworldview} and defining a \italic{FullUpdate} method, you
use the
double underscore, and write \italic{helloworldview__FullUpdate} as
above.
If you were to call helloworldview's \italic{FullUpdate} method, you
would
use a single underscore, i.e.,
\italic{helloworldview_FullUpdate}.\formatnote{@foot<
Actually, you could write \italic{FullUpdate} alone rather than
\italic{helloworldview_FullUpdate}. If you do so, however, you would
need to
run the \italic{.c} file through the \italic{Class} preprocessor.>}
\subsection{Writing a class method for full update requests} \indexi{Full
update} \indexi{Methods++Full update}
When you subclass an already existing Andrew Toolkit class, you will want
to
write methods that respond to the requests for which the class provides
methods. For example, when you subclass \indexi{Subclass} the Andrew
Toolkit
class, \italic{view}, you will typically want to write methods that
respond to
requests to update the screen, to handle keyboard input, and so forth.
In
this example, \italic{helloworldview}, we will write a method that
responds to
requests to update the screen. Later examples will introduce further
\italic{view} methods.
Whenever the user takes an action that requires the
\italic{helloworldview}
object's window to be redrawn, the Andrew Toolkit \italic{Interaction
Manager}
will call the \italic{FullUpdate} method. The method should do whatever
it
must do to redraw the display. \indexi{Redraw} In this example, when
\italic{helloworldview_FullUpdate} gets called, we would like it to draw
the
string \italic{hello world} in the center of the window.
To write the \italic{FullUpdate} method, you should declare it in a
\italic{.c}
file that has the same name as your class. The declaration for the
\italic{helloworldview}'s \italic{FullUpdate} method is in the file
\italic{hellov.c.} The following is the \italic{FullUpdate} method in
its
entirety, followed by a detailed explanation of its various parts:
\programexample{
void helloworldview__FullUpdate(hwv, type, left, top, width, height)
struct helloworldview *hwv;
enum view_UpdateType type;
long left;
long top;
long width;
long height; \{
int x,y;
struct rectangle VisualRect;
helloworldview_GetVisualBounds(hwv,&VisualRect);
x = rectangle_Left(&VisualRect) +
rectangle_Width(&VisualRect)/2;
y = rectangle_Top(&VisualRect) +
rectangle_Height(&VisualRect)/2;
helloworldview_MoveTo(hwv,x,y);
helloworldview_DrawString(hwv,"hello world",
graphic_BETWEENTOPANDBASELINE |
graphic_BETWEENLEFTANDRIGHT);
\}
}
\paragraph{Parameters and method definitions}
Whenever you override a superclass' method, you must use the same
parameters
in the method definition as in the superclass. \indexi{Overrides}
\indexi{Superclass} \indexi{Parameters} \indexi{Methods++Parameters}
\indexi{Methods++Overrides} (The parameters for all the methods are
documented
in Volume II.)
\begindata{bp,538928968}
\enddata{bp,538928968}
\view{bpv,538928968,1014,0,0}
For example, the following are the parameters for view's
\italic{FullUpdate}
method:
\programexample{
void helloworldview__FullUpdate(hwv, type, left, top, width, height)
struct helloworldview *hwv;
enum view_UpdateType type;
long left;
long top;
long width;
long height; \{
}
The \italic{FullUpdate} method has six parameters: \italic{hwv}, a
pointer to
the \italic{view} that needs to be redrawn; \italic{type}, the type of
redraw
and \italic{left}, \italic{top}, \italic{width} and \italic{height}, the
limits of what needs to be redrawn. The last five parameters allow you
to
optimize the redraw. We will ignore them in this simple example (see
\italic{FullUpdate} in the chapter, \bold{View}, Volume II).
\paragraph{Drawing on the screen: The class 'graphic'} \indexi{Drawing}
\indexi{Graphic} \indexi{Class++Graphic}
Responding for a request to update the screen requires drawing on the
screen.
The Andrew Toolkit has a class, \italic{graphic}, that provides the
class
procedures and methods necessary for drawing (see \bold{Graphic}, Volume
II).
Thus, to understand how to do a \italic{FullUpdate}, it is necessary to
introduce some graphic concepts.
The class \italic{graphic} provides an output interface to hardwaredependent
window management systems. \indexi{Window managers} As an application
programmer, you should understand why application programs should not
display
their pictures directly to an underlying window management system:
application programs must often be ported to other workstations that
will
eventually be deployed. The workstations often have similar, but not
identical display capabilities and the hardware can be significantly
different; the underlying window manager may be different. To insure
portability \indexi{Application program++Portability} of your
application, you
should never make direct calls to an underlying window management system;
you
should always use the \italic{graphic} class procedures and methods;
doing so
will maximize the device independence and portabilty of your application
program.
You should use instances of the class \italic{graphic} to do output to
workstation displays. For example, to draw text and put box around it,
you
would make calls to the methods and class procedures provided by the
class
\italic{graphic}. The class graphic provides methods for drawing lines,
shapes, and text. It also provides methods for transferring entire
blocks of
bits.
\formatnote{@blankspace(1 lines)}
\formatnote{@p(View interface to graphic)} \indexi{Class++View}
\indexi{Graphic++View interface}
\indexi{Class++Graphic}\indexi{View++Graphic
macromethods}
Each \italic{view} object has an associated \italic{graphic} object in
which
drawing takes place.
In addition, for each method that \italic{graphic}
provides, the class \italic{view} provides a corresponding
\italic{macromethod}. For example, the class \italic{graphic} provides
the
method \italic{DrawString}, a drawing primitive for drawing text on the
workstation screen. The graphic method \italic{DrawString} requires
three
parameters: an instance of a \italic{graphic}, the text to be drawn on
the
screen, and a specification of \italic{how} the text is to be drawn.
The
class \italic{view} provides a corresponding \italic{macromethod} that
takes
three parameters, an instance of a \italic{view}, the text to be drawn
and the
specification of \italic{how} it is to be drawn. The \italic{view}
macromethod calls the \italic{graphic} method, sending it the
\italic{view}'s
\italic{graphic} as its first parameter.
The following is an example of how \italic{view}'s \italic{DrawString}
macromethod could be defined:
\programexample{
DrawString(Text, Operation) (graphic_DrawString((self)->viewgraphic,
(Text),
(Operation)))
}
Note that the arguments to \italic{macromethods}, like the arguments to C
\italic{macros}, should not have side effects. \indexi{Macromethods}
\indexi{Macros} \indexi{Class++Macromethods}
\formatnote{\flushleft{\bold{Graphic rectangles}}}
\indexi{Graphic++Rectangles}
\indexi{Rectangles}
Drawing in the graphic is done in relation to rectangles. A
\italic{rectangle}
is specified by a point, \italic{(left, top)}, and a \italic{width} and
\italic{height} (Figure
\begindata{textref,539484168}
\textdsversion{12}
rectangle\
\enddata{textref,539484168}
\view{textrefv,539484168,1015,0,0}).
F\formatnote{igure:
\formatnote{(A rectangle)}
}
The Andrew Toolkit defines the following macros for setting the
dimensions of
a rectangle and finding out the values of those dimensions:
\indexi{Rectangles++Size}
\leftindent{\formatnote{\italic{rectangle_SetRectSize
dimensions
of a rectangle.
\italic{rectangle_Left
\italic{rectangle_Top
---
\italic{rectangle_Width
\italic{rectangle_Height
--
}set the
}get the value of the point, left.
}get the value of the the point, top.
---
}get the width.
}get the height.
}}
In the tutorial, we will introduce these macros as they arise in the
examples.
Figure: \formatnote{(The logical and visual rectangles)}
\indexi{Rectangles++Logical}\indexi{Rectangles++Visual}
The class \italic{graphic} supplies a \italic{logical rectangle} which
defines
the coordinate system for drawing. The units are pixels. \indexi{Pixel}
The
coordinates of the logical rectangle are \italic{initially }given by
(0,0) at
the origin on the rectangle's top left to 2\superscript{32}- 1,
2\superscript{32}-1 at its bottom right. You can draw anywhere in this
logical unsigned 32-bit coordinate space.
\indexi{Rectangles++Coordinates}
The logical rectangle, \italic{lr}, gives the coordinates that you can
scale
your drawing to. The class \italic{graphic} also provides a
\italic{visual
rectangle}, \italic{vr}, that gives the coordinates for what is
potentially
visible in the window. To illustrate \italic{logical rectangles} and
\italic{visual rectangles}, Figure
\begindata{textref,539484424}
\textdsversion{12}
lvrec\
\enddata{textref,539484424}
\view{textrefv,539484424,1016,0,0}, p.
\begindata{textref,539484680}
\textdsversion{12}
# lvrec\
\enddata{textref,539484680}
\view{textrefv,539484680,1017,0,0}, depicts a multi-media editor
displaying
some text (\smaller{ZORK}) and a drawing. Note that the drawing happens
to be
at the very bottom of the window and its logical rectangle, \italic{lr}
falls
outside the window boundary. \indexi{Window boundaries} If a graphic's
logical
rectangle falls outside the window boundary and the drawing is relative
to
\italic{lr}, then the drawing will be clipped to the graphic's visual
rectangle, \italic{vr}. The part of the drawing that falls within the
coordinates given by the \italic{visual rectangle} normally corresponds
to
what the user sees on the screen, except if another drawing were laid
over the
first (e.g., a message box could be temporarily laid on top all or part
of the
drawing).
\formatnote{Figure:
rectangles)}
}
\formatnote{(The logical and visual graphic
\formatnote{\bold{Scaling the drawing to the visual rectangle}}
\indexi{Drawing++Scaling} \indexi{Rectangles++Scaling}
\indexi{Graphic++Scaling}
Rather than scaling the drawing to the logical rectangle (and being
clipped)
as the example in Figure
\begindata{textref,539484936}
\textdsversion{12}
lvrect\
\enddata{textref,539484936}
\view{textrefv,539484936,1018,0,0} illustrated, a view can scale its
drawing
to its graphic's visual rectangle. For example, in order to draw the
string
\italic{hello world} in the center of the display,
\italic{helloworldview}
will scale its drawing to its graphic's visual rectangle. Whenever such
a
\italic{view} receives a call to do a \italic{FullUpdate} of its part of
the
screen, it must call \italic{GetVisualBounds} \indexi{
\italic{GetVisualBounds
}} \indexi{ \italic{view_GetVisualBounds}}
\indexi{ \italic{graphic_GetVisualBounds}}
\indexi{View++\italic{GetVisualBounds}}
\indexi{Graphic++\italic{GetVisualBounds}} in order to find out the size
of
its graphic's visual rectangle and get its coordinates.
\italic{GetVisualBounds} is a \italic{view} macromethod that takes two
arguments. The first argument is a pointer to a \italic{view}. The
second
argument is a \italic{struct rectangle}. \italic{GetVisualBounds} sets
the
rectangle to the coordinates of the view's graphic visual rectangle.
For
example, in \italic{helloworldview}'s \italic{FullUpdate} \indexi{Full
update}
method, the following lines get the coordinates of the visual rectangle:
\programexample{ helloworldview_GetVisualBounds(hwv, &VisualRect);
x = rectangle_Left(&VisualRect) + rectangle_Width(&VisualRect)/2;
y = rectangle_Top(&VisualRect) + rectangle_Height(&VisualRect)/2;
}
The statement, \italic{helloworldview_GetVisualBounds(hwv, &VisualRect)}
sets\italic{VisualRect} to the dimensions of \italic{hwv}'s visual
rectangle,
That is, that part of \italic{hwv}'s graphic rectangle that is
potentially
visible in the window. The statements that assign values to \italic{x}
and
\italic{y} calculate the horizontal and vertical centerpoints of the
visual
rectangle. The values of \italic{left} plus the \italic{width} divided
by two
yield the horizontal center point; the values of \italic{top} plus
\italic{height} divided by two yield the vertical.
\begindata{bp,538929288}
\enddata{bp,538929288}
\view{bpv,538929288,1019,0,0}
\formatnote{\bold{Moving the graphic point and drawing}}
\indexi{Graphic++Point} \indexi{Drawing}
The \italic{graphic} drawing primitives use a \italic{current point} as a
reference point. Drawing is done from the \italic{current point} to a
specified (x,y) coordinate. The macromethod
\italic{helloworldview_MoveTo} is
a \italic{view} macromethod that moves the graphic's \italic{current
point} to
a specified coordinate. It takes three arguments. The first is a
pointer to
a view, \italic{v}; the second and third are (x,y) coordinates. The
macromethod \italic{helloworld_MoveTo} \indexi{ \italic{MoveTo}}
\indexi{ \italic{graphic_MoveTo}} \indexi{ \italic{view_MoveTo}}
\indexi{Graphic++\italic{MoveTo}} \indexi{View++\italic{MoveTo}} moves
\italic{v}'s graphic drawing point to (x,y). Thus, before drawing, a
\italic{view} should move the drawing point, then call a drawing
primitive.
For example, to draw the string \italic{hello world} in the center of
the
window, the following part of \italic{helloworldview's}
\italic{FullUpdate}
\indexi{Full update} method moves the \italic{current point}, then calls
a
string-drawing primitive:
\programexample{helloworldview_MoveTo(hwv, x, y);
helloworldview_DrawString(hwv,"hello world",
graphic_BETWEENTOPANDBASELINE |graphic_BETWEENLEFTANDRIGHT);
}
The statement, \italic{helloworldview_MoveTo(hwv, x, y)}, moves
\italic{hwv}'s
graphic drawing point to coordinates (x,y), the center of \italic{hwv}'s
visual rectangle. The next statement,
\italic{helloworldview_DrawString},
\indexi{Graphic++\italic{DrawString}}
\indexi{View++\italic{DrawString}}\indexi{ \italic{DrawString}}
\indexi{ \italic{graphic_DrawString}} \indexi{ \italic{view_DrawString}}
is a
\italic{view} macromethod that actually draws \italic{hello world} in the
window at the center point. The alignment options
\smaller{BETWEENTOPANDBASELINE} and \smaller{BETWEENLEFTANDRIGHT} are
used to
center the string around the center coordinate (x,y). Other alignment
options
are available to start the string at point (x,y), to end the string at
(x,y),
and so forth (see \italic{graphic_DrawString}, \bold{Graphic.doc}). As
mentioned earlier, \italic{graphic} provides other drawing methods for
drawing
lines, polygons, and other shapes.
\section{Creating a stand-alone application program} \indexi{Stand-alone
application}
At this point, we have defined a complete subclass, \indexi{Subclass}
\italic{helloworldview}. In the Andrew Toolkit, such a class can be used
in
one of two ways: (1) It can be paired with a \italic{dataobject}
\indexi{Data
object} to form an inset \indexi{Inset} (i.e., a dataobject/view pair);
(2) it
can be used in a stand-alone application program, illustrated in this
example.
\formatnote{Note: insets can also be used in a stand-alone application
program. (See Example 10)}
\subsection{Applications and the base program, 'runapp'} \indexi{Runapp}
Andrew Toolkit applications are designed to run from a single base
program
called \italic{runapp}, short for \italic{run} \italic{app}lication).
The
program \italic{runapp} contains the code for all the basic components of
the
Andrew Toolkit. The program \italic{runapp} dynamically loads the code
that
is unique to individual applications into memory at run time.
\indexi{Dynamic
loading}
To understand why this scheme is desirable, it is useful to examine, in
brief,
part of the \italic{UNIX} memory management subsystem. \indexi{Memory
management} Each process on the UNIX system consists of three logical
memory
regions: text, data, and stack. The text region contains the set of
instructions that the machine executes for the process; the data region
contains global data; and the stack region contains the data local to a
subroutine. Several processes can share a region; the most usual case is
that
of several processes executing the same program and sharing one copy of
the
text region.
Suppose, for example, that a user were running an \bold{EZ} process and
two
\bold{command} processes. Figure
\begindata{textref,539485192}
\textdsversion{12}
processmem\
\enddata{textref,539485192}
\view{textrefv,539485192,1020,0,0} depicts the logical memory
organization for
the three processes, including their text regions, data regions, and
stack
regions. Note that the process \italic{typescript1} shares its text
region,
\italic{region a} with the process \italic{typescript2}.
Processes
running
separate programs, however, have separate text regions (e.g.,
\italic{region a}
for \bold{typescript} vs. \italic{region f} for \bold{ez}). Note further
that
even though all three processes have a great deal of text in common
(i.e., the
Andrew Toolkit library code for basicobject, observable, and view), the
\bold{ez} process cannot share the library code with the
\bold{typescript}
processes in a shared memory region: most \italic{UNIX} systems do not
support shared libraries.
\formatnote{Figure:
\formatnote{(Processes and memory regions)}
}
The \italic{runapp} scheme is designed to allow Andrew Toolkit
applications to
share significant portions of program code. Figure
\begindata{textref,539485448}
\textdsversion{12}
runapp\
\enddata{textref,539485448}
\view{textrefv,539485448,1021,0,0} depicts the logical memory
organization for
\bold{EZ} and two \bold{commands} as they would appear running under a
single \bold{runapp} process. \indexi{Runapp++Memory management} Thus,
applications can share significant amounts of code. Shared code leads to
the
following performance improvements: decreased paging activity, decreased
page
faults (key portions of code are almost always paged in), decreased
virtual
memory use, decreased number of file fetches (under a distributed file
system), and decreased file size of an application.
\formatnote{Figure:
\formatnote{(Runapp processes and memory regions)}
}
\section{The class Application} \indexi{Application}
\indexi{Class++Application}
The class \italic{application} provides an interface between
\italic{runapp}
and application programs. In particular, \italic{application} provides
the
following methods that \italic{runapp} will call when it loads an
application
into memory:
\formatnote{
\italic{ParseArgs(int argc,char **argv); -- }A method that should parse
the
command line arguments for the application. Returns boolean.
\indexi{ \italic{ParseArgs}}
\italic{ReadInitFile(); -- }A method that should read any
initialization
files for the application. \indexi{ \italic{ReadInitFile}}
\italic{Start(); -- }A method that should set-up the application so
that it
can start running (e.g., create instances of classes, windows, etc.).
Returns
boolean. \indexi{ \italic{Start}}
\italic{Run(); -- }A method that actually begins running the
application.
Returns int. \indexi{ \italic{Run}}
\italic{Fork(); -- }A method that frees the typescript when not
debugging.
Returns boolean. \indexi{ \italic{Fork}}
}
The program \italic{runapp} \indexi{Runapp} calls these methods when it
loads
an application into memory. In particular, it calls \italic{ParseArgs}
to
request the application to parse any command line arguments it may have,
then
call \italic{ReadInitFile} to request the application to read its
initialization file, if any, then calls \italic{Start} to request the
application program to create and initialize its objects and window, and
finally calls \italic{Run} which (if not debugging), calls the method
\italic{Fork} and enters the \italic{Interaction Manager's} keyboard
interaction loop that mediates communication between a user and the
application.
Thus, to set up as a stand-alone application program, you should create a
class that is a subclass of the Andrew Toolkit class,
\italic{application}.
The \italic{application} subclass should override those
\italic{application}
methods that you need. For example, for simplicity of exposition, this
introductory example has no command line arguments or initialization
files, so
it does not need to override \italic{ParseArgs} or \italic{ReadInitFile}.
Nor
does it need to override the method \italic{Run} or \italic{Fork}. Thus,
the
only override we need to make is to \italic{application}'s \italic{Start}
method, the method that should create instance of classes (e.g., an
instance
of \italic{helloworldview}, create a window, and so on.
In this example, we create a subclass, \italic{helloworldapp}, in the
file
\italic{helloa.ch}. By convention, Andrew Toolkit application modules are
named \italic{<x>app}, where \italic{<x>} is the name of the application
or an
\indexi{Application++Naming} \indexi{Application++Modules}
\indexi{Application
program} abbreviation for it (e.g., \italic{helloworld}) and \italic{app}
is
an abbreviation for the class \italic{application}. Note that both
classes are
more than eight letters long, so both have filename abbreviations.
\programexample{
class helloworldapp[helloa] : application[app]\{
overrides:
Start() returns boolean;
\};
}
\subsection{Writing a 'Start' method} \indexi{ \italic{
Start}}
To create a stand-alone application program, you must write a
\italic{Start}
method. The program \italic{runapp} will call your \italic{Start} method
in
order to run your program. Basically, the method should create an
instance of
the class, create a window, put the object in the window. For example,
for
\italic{helloworldview}, the application's \italic{Start} method should
create
an instance of the class \italic{helloworldview}, create a window, and
put the
\italic{helloworldview} instance in the window.
To write the \italic{Start} method, you should declare it in a
\italic{.c}
file that has the same name as your application class. The following is
the
declaration for \italic{helloworldapp}'s \italic{Start} method is in the
file
\italic{helloworldapp.c}, followed by a detailed explanation of its
various
parts:
\programexample{
boolean helloworldapp__Start(hwapp) struct helloworldapp *hwapp;
struct helloworldview *hwv;
struct im *im;
if(!super_Start(hwapp))
return FALSE;
hwv=helloworldview_New();
if(hwv==NULL)
return FALSE;
im=im_Create(NULL);
\{
if(im==NULL)\{
helloworldview_Destroy(hwv);
return FALSE;
\}
im_SetView(im,hwv);
return TRUE;
\}
}
\paragraph{Initializing the application}
\programexample{
if(!super_Start(hwapp))
return FALSE;
}
The first statement in an application's \italic{Start} method should be a
call
to its superclass' \italic{Start} method: \italic{if
(!super_Start(hwapp))
return FALSE;}. In this case, the call executes \italic{application}'s
\italic{Start} method, which initializes foreground and background colors
for
graphics to some default values. If the call is unsuccessful
(\italic{i.e.},
\italic{!super_Start(hwapp)}), you should return \smaller{FALSE}.
In general, when you precede a class' method name with the prefix
\italic{super}, \indexi{Methods++Naming} \indexi{Superclass} the system
searches for the method in the superclass of the currently executing
method's
class. Note that this may not necessarily be the superclass of the
object's
class. For example, if called from \italic{graphic_MoveTo},
\italic{super_MoveTo(hwv,x,y)} would begin the search for the method
\italic{MoveTo} in \italic{graphic}'s superclass, \italic{observable},
not in
\italic{hwv}'s superclass, \italic{view}.
\paragraph{Creating an instance of a class} \indexi{Class++Creating}
Most applications will create instances of objects that they will be
using.
In general, to create an instance of a class, you call the \italic{New}
class
\indexi{ \italic{New}} \indexi{Class procedures++\italic{New}} procedure.
For
a class \italic{x}, you call \italic{x_New}. To create an instance of
helloworldview, you write
\programexample{
hwv=helloworldview_New();
if(hwv==NULL)
return FALSE;
}
The call to \italic{helloworldview_New} creates an instance of the class
\italic{helloworldview} and assigns it to \italic{hwv}. If \italic{hwv}
is
\smaller{NULL}, the attempt to create the instance was unsuccessful and
you
should return \smaller{FALSE} (\italic{i.e.}, failure).
\paragraph{Creating an Interaction Manager and a window}
In addition to creating an instance of a class, a stand-alone application
will
want to create an \italic{Interaction Manager}, an instance of the Andrew
Toolkit class \italic{im} that manages the communication between the
underlying window manager and views. As a side effect, creating an
\italic{Interaction Manager} creates a window using whatever window
manager is
running on the workstation. Currently, the Andrew Toolkit runs on the X
window system and on Andrew's window manager, \italic{wm}. There is one
\italic{Interaction Manager} per window. \indexi{Windows} \indexi{Window
manager} \indexi{Interaction manager} \indexi{View++Interaction manager}
For example, the following creates an \italic{Interaction Manager},
\italic{im}, for the helloworldview application, \italic{helloworldapp}:
\programexample{
im=im_Create(NULL);
if(im==NULL)\{
helloworldview_Destroy(hwv);
return FALSE;
}
The call to
\italic{im_Create(NULL)} creates an \italic{Interaction
Manager}
(\italic{im}) view and, as a side effect, creates a window using whatever
window manager is running on the workstation.
If \italic{im} is \smaller{NULL}, then the attempt to create the
\italic{Interaction Manager} was unsuccessful and you should destroy any
objects, (e.g., \italic{helloworldview_Destroy (hwv)}) and return
\smaller{FALSE}, \italic{i.e.}, failure. \indexi{Objects++Destroying}
\subsection{The view tree} \indexi{View tree} \indexi{View}
The \italic{im} view is the root of the window's view tree. Recall the
views
represent space on your computer's display screen. Views are organized
into
trees. \italic{im_SetView (hwv, im)} puts \italic{hwv} in the view
hierarchy
with \italic{im}, which happens to be the root view, as the parent. The
screen space assigned to a view is always totally included within the
screen
space allocated to its parent view. Two sibling views, however, may find
their screen space overlapping in any way, as long as both siblings are
contained within their parent's screen space allocation.
It is important to understand the difference between the Andrew Toolkit
class
hierarchy and a view hierarchy. The class hierarchy defines a
\indexi{Class
hierarchy} \indexi{Class++Hierarchy} tree of parent-child relationships
among
classes; the view hierarchy defines a tree of parent-child relationships
among
\italic{view objects}, i.e., actual instances of the class \italic{view}.
In
the class hierarchy, defined through the statements \bold{class} <class
name>
\bold{\bold{:}} <super-class>, both the Andrew Toolkit class \italic{im}
and
the class \italic{helloworldview} are subclasses of the class
\italic{view;}
in the view \indexi{Superclass} \indexi{Subclass} hierarchy, defined by
calls
to \italic{im_SetView}, \indexi{Interaction
manager++\italic{SetView}}\indexi{ \italic{SetView}} the view
\italic{hwv}, an
instance of class \italic{helloworldview,}is a
\italic{im},
an instance of the class \italic{im}.
child of the view
\subsection{The interaction loop} \indexi{Interaction manager}
\indexi{Interaction loop}
After calling an application's \italic{Start} method, \italic{runapp}
calls
the application's \italic{Run} method. \indexi{ \italic{Run}} In this
example, \italic{helloworldapp} inherits \italic{application}'s
\italic{Run}
method, which frees the \bold{command} (provided the debugging flag is
not
set) and calls \italic{im_KeyboardProcessor}. \indexi{Interaction
manager++\italic{KeyboardProcessor}}
The call to \italic{im_KeyboardProcessor()} enters the
\italic{Interaction
Manager}'s interaction loop, the loop that mediates communication between
a
user (and the underlying window system) and the view(s) in the view tree
(e.g., helloworldview). Logically, the interaction loop can be thought
of as
having the following pseudo-code structure:
\programexample{
while (there is not an exit signal)
case (event)
an event on the event queue is scheduled to happen at this time:
call the event handler;
break;
there is user input pending:
call the child's appropriate method;
break;
there is file I/O on a file:
call the appropriate file I/O handler;
break;
the user has done something that requires a full update of the
screen:
call child's FullUpdate method;
break;
there are requests for updates on the want update list:
call the child's Update method;
break;
there are child processes that have died:
release the process to allow reuse of its resources;
break;
default:
wait for an event;
end /* while */
}
In general, you will not need to override \italic{application}'s
\italic{Run}
method. If you do override it, however, note that a call to
\italic{im_KeyboardProcessor} will normally be the last statement in the
\italic{Run} method, since once the interaction loop is entered,
subsequent
calls will not be processed until the loop is exited.
\section{Compiling and linking under the Andrew Toolkit}
\indexi{Compiling}
\indexi{Linking}
\section{The class preprocessor} \indexi{Class preprocessor}
\indexi{Class}
\indexi{Class++Preprocessor}
The \italic{Class} preprocessor takes a .ch file as input.
\indexi{Header
files} When it runs, it generates two files: (1) a .ih file which you
must
#include when you want to import (i.e., use) a class in another module;
and
(2) a .eh file which you must #include in the module that is defining the
class in order to export the class. There can be only one class
declaration
within a .ch file and only one class defined within a module. The Class
preprocessor copies all the contents of the .ch file except for the class
declaration. It uses the class declaration to generate a C structure for
the
class and a set of macros for calling the methods and class procedures
associated with the class. In the .eh file, it also generates code that
defines a set of class procedures:
\description{
\italic{x_New}\formatnote{
\indexi{ \italic{New}}
--
}Creates an instance of the class x
\italic{x_Initialize}\formatnote{ -- }Initializes an instance of the
class
x; called by x__New. \indexi{ \italic{Initialize}}
\italic{x_Destroy}\formatnote{ -- }Destroys an instance of the class x;
used
in conjunction with x__New. \indexi{ \italic{Destroy}}
\italic{x__Finalize}\formatnote{ -- }Destroys a class x; used if the
class
has been statically allocated or automatically allocated on the stack
(see
Class, Volume II). \indexi{ \italic{Finalize}}
}
Finally, the Class preprocessor creates several procedures required by
the
class run-time system.
For example, the Class preprocessor takes hellov.ch and generates two
files:
\italic{hellov.ih} and \italic{hellov.eh}. Both files contain a
\italic{struct helloworldview} declaration and macro definitions. In
addition, \italic{hellov.eh} contains the class procedures
helloworldview__New, helloworldview__Initialize, helloworldview__Destroy,
helloworldview__Finalize.
\section{Dynamic loading and modules} \indexi{Dynamic loading}
The dynamic loading system used by the Andrew Toolkit requires a single
class
per module. The module must contain all the code associated with the
class
and any data associated with the class. You can create a module by using
separately compiled C files and linking them. You may reference data
directly
between the files within a module, but you may not reference data
directly
between modules. (The Class system itself provides the mechanism for
making
indirect references to other modules.) These reference restrictions
result in
modules that are self-contained and that can be quickly dynamically
loaded
into a running program.
For small programs, the class procedures and methods for the class are
typically all located in a single \italic{.c} file. When the class is
compiled, it is easiest if the module name for this \italic{.c }file is
the
same as your class name. For example, the class procedures and methods
for an
view named \italic{piechart} should all be located in a file named
\italic{piechart.c}.
\subsection{Imports and exports}
The file\italic{ hellov.c}, the program file for the
\italic{helloworldview}'s
class procedures and methods, begins by importing the constructs it needs
from
the Andrew Toolkit library and exporting its class procedures and
methods:
\indexi{Exporting class procedures}
\example{
#include <class.h> #include "graphic.ih"
#include "hellov.eh"
}
\italic{hellov.c} must #include \italic{class.h} to access the Andrew
Toolkit
class facilities, and \italic{graphic.ih} to use the Andrew Toolkit
graphic
class. It must export \italic{hellov.eh}, the file containing the
declaration
of exported class procedures and methods.
In general, you must include all the classes that your class uses
directly or
indirectly. If you leave one out, when you run the program you will get
the
following run-time error message in your \bold{command}:
\indent{\italic{Could not find the class methods for <class name> (xxxxx)
exiting}}
where \italic{<class name>} will be the name of a specific class that
must be
included.
\section{Makefiles and compiling} \indexi{Compiling} \indexi{Makefile}
\formatnote{\example{SRCDIR=/usr/andrew/
INCLUDES= -I. -I$\{SRCDIR\}include/atk -I$\{SRCDIR\}include
INCLUDESRC = $\{SRCDIR\}include/atk
CC=cc
DEBUG = -g
TOOLS = $\{SRCDIR\}bin/
CFLAGS= $\{DEBUG\} $\{INCLUDES\}
CLASSFLAGS=$\{INCLUDES\}
MAKEDO = $\{TOOLS\}makedo $\{DEBUG\} -b $\{TOOLS\} -d $\{SRCDIR\}lib
CLASS = $\{TOOLS\}class
.SUFFIXES: .ih .eh .ch .do
.ch.ih:
$\{CLASS\} $\{CLASSFLAGS\} $*.ch
.ch.eh:
$\{CLASS\} $\{CLASSFLAGS\} $*.ch
.o.do:
$\{MAKEDO\} $<
all:
helloa.do hellov.do
helloa.do: helloa.o helloa.eh
hellov.do: hellov.o hellov.eh
helloa.o: helloa.c
helloa.o: $\{INCLUDESRC\}/app.ih
helloa.o: $\{INCLUDESRC\}/graphic.ih
helloa.o: $\{INCLUDESRC\}/im.ih
helloa.o: $\{INCLUDESRC\}/observe.ih
helloa.o: $\{INCLUDESRC\}/point.h
helloa.o: $\{INCLUDESRC\}/rect.h
helloa.o: $\{INCLUDESRC\}/view.ih
helloa.o: $\{SRCDIR\}include/class.h
helloa.o: hellov.ih
helloa.o: helloa.eh
helloa.eh helloa.ih: helloa.ch
helloa.eh helloa.ih: $\{INCLUDESRC\}/app.ih
hellov.o: hellov.c
hellov.o: $\{INCLUDESRC\}/graphic.ih
hellov.o: $\{INCLUDESRC\}/observe.ih
hellov.o: $\{INCLUDESRC\}/point.h
hellov.o: $\{INCLUDESRC\}/rect.h
hellov.o: $\{INCLUDESRC\}/view.ih
hellov.o: $\{SRCDIR\}include/class.h
hellov.o: hellov.eh
hellov.eh hellov.ih: hellov.ch
hellov.eh hellov.ih: $\{INCLUDESRC\}/graphic.ih
hellov.eh hellov.ih: $\{INCLUDESRC\}/observe.ih
hellov.eh hellov.ih: $\{INCLUDESRC\}/point.h
hellov.eh hellov.ih: $\{INCLUDESRC\}/rect.h
hellov.eh hellov.ih: $\{INCLUDESRC\}/view.ih
hellov.eh hellov.ih: $\{SRCDIR\}include/class.h}
}
The \italic{Makefile} \indexi{Makefiles} \indexi{ \italic{make}} for a
class
that will stand-alone is for the most part, like any other
\italic{Makefile}
(see \italic{make} in the online help pages). This particular Makefile
creates the dynamically loadable versions of helloworldapp and
helloworldview
from the corresponding .o and .eh files. The respective .o and .eh files
also
depends on information in the corresponding .c and .h files and from .ih
files of all classes that provide necessary information to this
particular
application (application, graphic, view, observable, im, point,
rectangle).
The list of classes that need to be included in the dependencies list is
determined by each individual application -- the rule of thumb is to
include
all classes that are explicitly called, their parents and ancestors, and
any
other non-classes (such as point and rectangle) that provide information
to
the classes. Note that class.h must also be included to create the .o
and .eh
files.
\subsection{Compiling and linking the class and the program}
To compile the class and the program using this \italic{Makefile}, you
should
have the files \italic{hellov.ch}, \italic{hellov.c}, \italic{helloa.ch},
\italic{helloa.c} and the \italic{Makefile} in a single directory in
which you
have read, write and list permissions. You may copy these files from
/usr/andrew/examples/atk/ex1.
Go to the directory in which you have put the files (make it the current
directory). Then at the \bold{command} prompt, type
cp /usr/andrew/examples/ex1/* .
\begindata{bp,538928712}
\enddata{bp,538928712}
\view{bpv,538928712,1022,0,0}
You will also need to change the protections on the files to +w.
Type
\leftindent{chmod +w *}
and press the Enter key.
(For more information on file protections, see \italic{chmod} in the
online
help pages.)
To compile and link the program, type
\bold{\leftindent{make}}
and press the Enter key.
\subsection{Running an application}
Andrew Toolkit applications are designed to run from a single base
program
called \italic{runapp}, short for \italic{run} \italic{app}lication).
The
program \italic{runapp} actually dynamically loads the application into
memory. \indexi{Runapp} \indexi{Application program++Running}
To run after compilation, type:
\bold{\leftindent{runapp helloa}}
and press the Enter key.
\begindata{bp,538271624}
\enddata{bp,538271624}
\view{bpv,538271624,1023,0,0}
\section{Program listing for Example 1}
\formatnote{
\bold{hellov.ch}
class helloworldview[hellov]: view \{
overrides:
FullUpdate(enum view_UpdateType type, long left, long top, long
width,
long height);
\};
\bold{hellov.c}
#include <class.h>
#include "hellov.eh"
#include "graphic.ih"
void helloworldview__FullUpdate(hwv, type, left, top, width, height)
struct helloworldview *hwv;
enum view_UpdateType type;
long left;
long top;
long width;
long height;
\{
int x,y;
struct rectangle VisualRect;
helloworldview_GetVisualBounds(hwv,&VisualRect);
x = rectangle_Left(&VisualRect) + rectangle_Width(&VisualRect)/2;
y = rectangle_Top(&VisualRect) + rectangle_Height(&VisualRect)/2;
helloworldview_MoveTo(hwv,x,y);
helloworldview_DrawString(hwv,"hello world",
graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT);
\}
\bold{helloa.ch}
class helloworldapp[helloa] : application[app]\{
overrides:
Start() returns boolean;
\};
\bold{helloa.c}
#include <class.h>
#include "helloa.eh"
#include "im.ih"
#include "hellov.ih"
boolean helloworldapp__Start(hwapp)
struct helloworldapp *hwapp;
\{
struct helloworldview *hwv;
struct im *im;
if(!super_Start(hwapp))
return FALSE;
hwv=helloworldview_New();
if(hwv==NULL)
return FALSE;
im=im_Create(NULL);
if(im==NULL)\{
helloworldview_Destroy(hwv);
return FALSE;
\}
im_SetView(im,hwv);
return TRUE;
\}
\bold{Makefile}
SRCDIR=/usr/andrew/
INCLUDES= -I. -I$\{SRCDIR\}include/atk -I$\{SRCDIR\}include
INCLUDESRC = $\{SRCDIR\}include/atk
CC=cc
DEBUG = -g
TOOLS = $\{SRCDIR\}bin/
CFLAGS= $\{DEBUG\} $\{INCLUDES\}
CLASSFLAGS=$\{INCLUDES\}
MAKEDO = $\{TOOLS\}makedo $\{DEBUG\} -b $\{TOOLS\} -d $\{SRCDIR\}lib
CLASS = $\{TOOLS\}class
.SUFFIXES: .ih .eh .ch .do
.ch.ih:
$\{CLASS\} $\{CLASSFLAGS\} $*.ch
.ch.eh:
$\{CLASS\} $\{CLASSFLAGS\} $*.ch
.o.do:
$\{MAKEDO\} $<
all:
helloa.do hellov.do
helloa.do: helloa.o helloa.eh
hellov.do: hellov.o hellov.eh
helloa.o: helloa.c
helloa.o: $\{INCLUDESRC\}/app.ih
helloa.o: $\{INCLUDESRC\}/graphic.ih
helloa.o: $\{INCLUDESRC\}/im.ih
helloa.o: $\{INCLUDESRC\}/observe.ih
helloa.o: $\{INCLUDESRC\}/point.h
helloa.o: $\{INCLUDESRC\}/rect.h
helloa.o: $\{INCLUDESRC\}/view.ih
helloa.o: $\{SRCDIR\}include/class.h
helloa.o: hellov.ih
helloa.o: helloa.eh
helloa.eh helloa.ih: helloa.ch
helloa.eh helloa.ih: $\{INCLUDESRC\}/app.ih
hellov.o: hellov.c
hellov.o: $\{INCLUDESRC\}/graphic.ih
hellov.o: $\{INCLUDESRC\}/observe.ih
hellov.o: $\{INCLUDESRC\}/point.h
hellov.o: $\{INCLUDESRC\}/rect.h
hellov.o: $\{INCLUDESRC\}/view.ih
hellov.o: $\{SRCDIR\}include/class.h
hellov.o: hellov.eh
hellov.eh hellov.ih: hellov.ch
hellov.eh hellov.ih: $\{INCLUDESRC\}/graphic.ih
hellov.eh hellov.ih: $\{INCLUDESRC\}/observe.ih
hellov.eh hellov.ih: $\{INCLUDESRC\}/point.h
hellov.eh hellov.ih: $\{INCLUDESRC\}/rect.h
hellov.eh hellov.ih: $\{INCLUDESRC\}/view.ih
hellov.eh hellov.ih: $\{SRCDIR\}include/class.h
}
\begindata{bp,537558784}
\enddata{bp,537558784}
\view{bpv,537558784,1025,0,0}
Copyright 1992 Carnegie Mellon University and IBM.
All rights reserved.
\smaller{\smaller{$Disclaimer:
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and that
both that copyright notice, this permission notice, and the following
disclaimer appear in supporting documentation, and that the names of
IBM, Carnegie Mellon University, and other copyright holders, not be
used in advertising or publicity pertaining to distribution of the
software
without specific, written prior permission.
IBM, CARNEGIE MELLON UNIVERSITY, AND THE OTHER COPYRIGHT HOLDERS
DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT
SHALL IBM, CARNEGIE MELLON UNIVERSITY, OR ANY OTHER COPYRIGHT HOLDER
BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
$
}}\enddata{text,539040528}
Download