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

advertisement
\begindata{text,538489940}
\textdsversion{12}
\template{default}
\define{global
}
\define{itemize
}
\formatnote{\chapter{Example 17:
Creating a view that has children}}
\indexi{View++With children} \indexi{Children} \indexi{Child views}
\indexi{Managing children} \indexi{Insets++Child}
The perspective on the Andrew Toolkit interaction up to now has been from
that
of the child view: Examples 1-16 illustrated how to build a class that
would
respond appropriately if included as a child in other views. This
example
shifts to the parent's perspective: It illustrates how to build a view
that
includes a child.
In addition, this example illustrates a principle that guided much of the
Andrew system development: When application programmers are implementing
new
views, they should use views that already exist as building blocks for
their
own views whenever possible. For example, the Andrew mail program uses
textview/text inset as a building block. The mail view builds on top of
the
textview/text code without changing textview/text at all, but simply adds
new
commands to implement functionality needed for mail. Example 17 will
also use
a text inset as its child view. Of course, a view's child can also be a
completely new view. Using an already existing view simply saves effort.
Example 17 is similar in functionality to Example 16. Instead of
displaying
the string, ``hello world,'' however, it displays a textview with a text
data
object that contains ``hello world.'' The user interface is the same,
with
the exception that mouse hits inside of the textview go to the textview,
not
directly to helloworld, so the user must click outside of the textview
rectangle to drag the \italic{helloworld} string.
The example illustrates the following activities:
\itemize{
Creating a child view at the appropriate time
Allocating screen space for a child
Inserting the child into the view tree
Passing requests (e.g., mouse hits) to your child
Passing your child's requests (e.g., update, input focus) up the view
tree
}
The discussion that follows presents a step-by-step description of how to
modify the \italic{helloworld} class in Example 16 to produce Example 17.
If
you were to follow the steps, you would produce a program, called
\italic{helloworldapp}, in seven files:
\itemize{
a hello.ch file -- will contain the class definition for the
\italic{helloworld }data object. We will override the InitializeObject
and
FinalizeObject classprocedures.
a hello.c file -- will contain statements that import Andrew Toolkit
classes
used by the data object, initialize the class, and define the Read and
Write
methods. We will add much of the style code from helloa.c, override the
InitializeObject and FinalizeObject class procedures, and modify the
Write
method.
a hellov.ch file -- will contain the class definition for the
\italic{helloworld} view. We will override the SetDataObject and the
LinkTree
methods, and add a few new data structures.
a hellov.c file -- will contain statements that import Andrew Toolkit
classes
used by the view and definitions of the methods. We will be modifying
many of
the method definitions to convert helloworldview from a child object to a
parent object.
a helloa.ch file -- will contain the class definition for the application
program that will display an instance of the class in a window. This
will be
exactly the same as in Example 16.
a helloa.c -- will contain declarations needed by the Andrew Toolkit
linking
and loading facilities as well the definition of the
\italic{helloworldview}
application method. This will be exactly the same as in Example 16.
Makefile -- will contain the directions for compiling, linking and
loading.
This will be exactly the same as Example 16.
}
For a complete listing of these files, see \italic{Program Listing for
Example
17 } at the end of this section on p.
\begindata{textref,539236104}
\textdsversion{12}
# ExSeventeenListing\
\enddata{textref,539236104}
\view{textrefv,539236104,951,0,0}. The source code is available in the
directory /usr/andrew/examples/ex17, together with the compiled program.
Although the discussion refers directly to this example, which
illustrates a
view with a single child, the information applies generally to the
creating of
any view with any number of children.
\formatnote{\bold{Action 1.}
\bold{command }prompt type
\formatnote{To run the program, at the
runapp /usr/andrew/examples/atk/ex17/helloa
and press the Enter key.
}
\bold{Response.}\formatnote{
}The program will produce two windows,
each
split into two lpairs as shown in the figure on the previous page. The
left
lpairs have both left and bottom scroll bars, and contain another window
(a
child text view displaying \bold{hello}\italic{world}) with a scroll bar.
The
right lpair contains a text view displaying \bold{hello}\italic{world}
and has
only the left scroll bar.
\bold{Action 2.} \formatnote{
of
the left lpairs.
\bold{Response. } \formatnote{
location of the mouse hit.
}Click with the left mouse button in one
}The child text view will move to the
\bold{Action 3.}\formatnote{
button
pressed down.
}Drag the mouse with the right mouse
\bold{Response. }\formatnote{
dragged
around the lpair window.
}The child text view window will be
\bold{Action 4.}\formatnote{
cards.
}Press both mouse buttons to see the menu
\bold{Response.} \formatnote{
}Two menu cards, one with \bold{Quit} and
the
other with \bold{Hello World} will appear. The Hello World functions
(i.e.
Relocate, Write, Read, Invert, Center) are exactly the same as in the
previous
examples.
\bold{Action 5.}\formatnote{
}Click with a mouse button inside the
child
text view, then pop up the menus.
\bold{Response.} \formatnote{
}The text menu cards will appear, since
it is
now the child text view that has the input focus.
\bold{Action 6.}\formatnote{
}The rest of the program has the same
functionality as in previous examples. To quit the program, choose Quit
from
the menu cards.
\bold{Response.} \formatnote{
screen.
}Both windows will disappear from the
}\
\begindata{bp,538928712}
\enddata{bp,538928712}
\view{bpv,538928712,952,0,0}
\section{Modifying the class definition}
\subsection{Creating a view from component views}
\indexi{Views++Creating}
\indexi{View++Building blocks}
You should consider creating a new view from existing building blocks
whenever
part of your application's user interface shares significant similarities
to
an existing view's user interface. For example, the user interface for
the
body of a mail message is similar to the user interface provided by
\italic{textview}, so it makes sense to build the body of a mail message
from
\italic{textview}. A new view that you build from an existing component
can
override the component's user interface behavior, so the user interface
can
differ somewhat from the component's. A rule of thumb for deciding how
different the two can be is the following: If you are considering
creating a
new view from an existing building block, but find yourself needing to
make
changes to large numbers of the building block's methods, you will
probably be
better off implementing a new view altogether; on the other hand, if
there are
large numbers of methods that you can use as is, or you can use by simply
adding new methods, the building block approach will save you effort.
\subsection{Defining the data object} \indexi{Data objects++Defining}
If you are building a view that is going to have a child views and the
children have associated data objects, you must declare the
\italic{dataobject}
class procedures \italic{InitializeObject} and \italic{FinalizeObject.}
These
class procedures are the places where you will create the child views'
data
objects and delete them, respectively. You must also modify the
\italic{dataobject}'s data definition to add a pointers to the child
views'
data objects.
For example, the following is the new class declaration for the example
dataobject, \italic{helloworld}:
\formatnote{
class helloworld[hello]: dataobject[dataobj] \{
overrides:
Read(FILE *file,long id) returns long;
Write(FILE *file,long writeId,int level) returns long;
\bold{classprocedures:
}InitializeObject(struct helloworld *hw) returns boolean;\bold{
FinalizeObject(struct helloworld *hw);}
data:
long x,y;
boolean blackOnWhite;
\bold{struct dataobject *dobj;}
\};
}
In previous examples, to display ``hello world'' we used a constant
string
that was cheap to generate, so we did not need to explicitly store it.
But in
this example, to display "hello world" we will use a child view, in this
case
a \italic{textview} whose data object is a \italic{text}. The statement
\italic{ struct dataobject *dobj;} declares a pointer to it.
In previous examples, \italic{FinalizeObject} was not declared, since it
had
no work to do. In this example, it will free the space allocated to the
child
view's data object, \italic{text}.
\subsection{Creating the children's data objects} \indexi{Data
objects++Creating children}
If you are creating a new view with children, you must create the
children's
data objects and keep pointers to them in the new view's associated data
object. You should also initialize the children's data objects. These
activities should be done in the associated dataobject's
\italic{InitializeObject} class procedure.
For example, the following is the \italic{InitializeObject} procedure for
\italic{helloworld}:
\formatnote{
boolean helloworld__InitializeObject(classID,hw)
struct classheader *classID;
struct helloworld *hw;
\{
hw->x = POSUNDEF;
hw->y = POSUNDEF;
hw->blackOnWhite = TRUE;
hw->dobj=createInitialDobj();
return TRUE;
\}
struct dataobject *createInitialDobj()
\{
struct text *text=text_New();
struct style *bold=style_New(),*italic=style_New();
style_SetName(bold,"bold");
style_AddNewFontFace(bold,fontdesc_Bold);
style_SetName(italic,"italic");
style_AddNewFontFace(italic,fontdesc_Italic);
text_InsertCharacters(text,0,"Hello world!",sizeof("Hello world!")1);
text_AddStyle(text,0,5,bold);
text_AddStyle(text,6,5,italic);
return (struct dataobject *)text;
\}
}
In this example, the \italic{helloworldview} will have a single child, a
\italic{textview}. The statement \italic{hw->text=text_New()} in the
createInitialDobj procedure creates an instance of \italic{textview}'s
data
object, \italic{text}, and stores a pointer to it in \italic{hw->text}.
The remaining statements, \italic{text_InsertCharacters} and
\italic{text_AddStyle}, initialize the child data object. It is
analogous to
the use with text objects previously.
\subsection{Deleting the children's data objects} \indexi{Data
objects++Deleting children}
When a parent object with children is deleted, it should free up the
children's storage, provided of course that there are no other pointers
to the
children. The freeing of storage should be done in the parent's
\italic{FinalizeObject} class procedure by calls to the children's
\italic{Destroy} or \italic{Finalize} procedures.
For example, the following is helloworld's FinalizeObject procedure:
\formatnote{
void helloworld__FinalizeObject(classID,hw)
struct classheader *classID;
struct helloworld *hw;
\{
dataobject_Destroy(hw->dobj);
\}
}
The statement \italic{dataobject_Destroy(hw->dobj)} frees the storage
allocated to the text data object pointed to by \italic{hw->dobj}.
\subsection{Writing a data object with children to a file} \indexi{Data
objects++Writing children}
The \italic{Write} method must write the contents of the data object to a
file. When a view/data object is constructed from component views and
data
objects, the parent data object's Write method must call the children's
Write
methods.
For example, the following is Example 17's \italic{Write} method:
\formatnote{
long helloworld__Write(hw,file,writeId,level)
struct helloworld *hw;
FILE *file;
long writeId;
int level;
\{
if(writeId!=helloworld_GetWriteID(hw))\{ /* only write a given
version
once */
helloworld_SetWriteID(hw,writeId);
fprintf(file,"\\\\begindata\{%s,%d\}\\n",
class_GetTypeName(hw), helloworld_UniqueID(hw));
fprintf(file,"%d %d %d\\n",hw->x,hw->y,hw->blackOnWhite);
\bold{dataobject_Write(hw->dobj,file,writeId,level);}
fprintf(file,"\\\\enddata\{%s,%d\}\\n",
class_GetTypeName(hw), helloworld_UniqueID(hw));
\}
return helloworld_UniqueID(hw);
\}
}
The \italic{Write} method is the same as Example 11, with the exception
of the
statement \italic{dataobject_Write(hw->dobj,file,writeId,level)}. Like
before, the method writes out the position and color (black or white) of
the
helloworld dataobject, \italic{hw}, but the statement
\italic{dataobject_Write}
writes out the text data object, \italic{hw->dobj}, as well--inside the
helloworld dataobject.
Recall that the \italic{level} parameter in the \italic{Write} method
indicates whether or not the "top level" object is being written (i.e.,
not a
sub-object within another object in the datastream object hierarchy).
\subsection{Reading a data object with children from a file} \indexi{Data
objects++Reading children}
The \italic{Read} method for a data object with children is similar to
the
\italic{Read} methods already discussed, but the method must also read in
any
embedded data objects. To read embedded objects, the method must scan
for the
\\begindata header that the object should have written when its
\italic{Write}
routine was called. If the header exists, then calling the \italic{Read}
method for the object will read the object, up to and including its
\\enddata
marker. After the embedded object returns success, the parent's
\italic{Read}
method should read any other data, including its own \\enddata marker.
For example, the following is Example 17's \italic{Read} method:
\formatnote{
long helloworld__Read(hw,file,id)
struct helloworld *hw;
FILE *file;
long id;
\{
char buf[100],classNameBuf[100];
long retVal,dobjObjId;
helloworld_SetID(hw,helloworld_UniqueID(hw));
if(fgets(buf,sizeof(buf),file)==NULL ||
/* the %hd tells scanf that blackOnWhite is a short, not an int*/
sscanf(buf,"%d %d %hd\\n",&hw->x, &hw->y,&hw->blackOnWhite)<3||
fgets(buf,sizeof(buf),file)==NULL ||
sscanf(buf,"\\\\begindata\{%[^,],%d\}\\n",
classNameBuf,&dobjObjId) <2)
retVal=dataobject_PREMATUREEOF;
else\{
retVal=dataobject_Read(hw->dobj,file,id);
if(retVal==dataobject_NOREADERROR)
if(fgets(buf,sizeof(buf),file)==NULL)
/* read the \\enddata\{...\}*/
retVal=dataobject_MISSINGENDDATAMARKER;
\}
return retVal;
\}
}
This method is similar to previous \italic{Read} methods, but after
reading in
the \italic{x}, \italic{y}, & \italic{blackOnWhite} values correctly, the
method attempts to read in the embedded text object. The statement
\italic{sscanf(buf,"\\\\begindata\{%[^,],%d\}\\n",classNameBuf,&textObjId
)<2)}
scans for the \\begindata header, returning
\italic{dataobject_\smaller{PREMATUREEOF}} upon an error. If it is of
type
dataobject, the statement \italic{retVal=dataobject_Read(hw>dobj,file,id)}
reads the data object (in this case, text) up to an including its
\\enddata
marker. If the dataobject_Read was successful (i.e., \italic{if
(retVal==dataobject_\smaller{NOREADERROR}) }succeeds) the statement
\italic{if(fgets(buf,sizeof(buf),file)==NULL)} performs a quick "check"
for an
\\enddata marker, returning
\italic{dataobject_\smaller{MISSINGENDDATAMARKER}}
upon a failure, success otherwise.
\subsection{Defining the view} \indexi{View++Defining}
If you are building a view that is going to have a child views, you must
override the \italic{view} methods \italic{LinkTree} and
\italic{SetDataObject}. The LinkTree method must link your child view's
into
the view tree; the SetDataObject method must associate the child views
with
their respective data objects. You must also modify the \italic{view}'s
data
definition to add appropriate pointers to the child views.
For example, the following are the new elements in the class declaration
for
the example view, \italic{helloworldview}:
\formatnote{
class helloworldview[hellov]: view \{
overrides:
\bold{SetDataObject(struct helloworld *hw);}
FullUpdate(enum view_UpdateType type, long left, long top, long
width, long right);
Update();
Hit (enum view_MouseAction action, long x, long y, long
numberOfClicks)
returns struct view *;
ReceiveInputFocus();
LoseInputFocus();
GetInterface(int type) returns char *;
GetApplicationLayer() returns struct view *;
DeleteApplicationLayer(struct view *);
\bold{LinkTree(struct view *parent);}
classprocedures:
InitializeClass() returns boolean;
data:
struct keystate *keystate;
struct menulist *menulist;
boolean HaveDownTransition;
boolean haveInputFocus;
long hitX,hitY;
int x,y;
boolean blackOnWhite;
long frameX, frameY;
long newFrameX, newFrameY;
int vrWidth,vrHeight;
\bold{struct view *applayer;
struct view *view;}
\};
}
The helloworld view must now have a \italic{view} to display the text
data
object. Since we would like a scrollable view (in this case, a
textview), we
also keep a pointer to the view's application layer, \italic{applayer}.
\subsection{Creating the children's views} \indexi{Views++Creating
children}
If you are creating a new view with children, you must create the
children's
views. This activity should be done in the parent view's
\italic{InitializeObject} class procedure.
For example, the following is the \italic{InitializeObject} procedure for
\italic{helloworldview}:
\formatnote{
boolean helloworldview__InitializeObject(classID,hwv)
struct classheader *classID;
struct helloworldview *hwv;
\{
hwv->haveInputFocus=FALSE;
hwv->HaveDownTransition=FALSE;
hwv->keystate=keystate_Create(hwv,helloworldviewKeymap);
hwv->menulist=menulist_DuplicateML(helloworldviewMenulist,hwv);
hwv->newFrameX=0;
hwv->newFrameY=0;
\bold{
hwv->view=NULL;
hwv->applayer=NULL;}
return TRUE;
\}
}
\subsection{Deleting the children's views} \indexi{Views++Deleting
children}
When a parent view with children is deleted, it should free up the
children's
storage, provided of course that there are no other pointers to the
children.
The freeing of storage should be done in the parent's
\italic{FinalizeObject}
class procedure by calls to the children's \italic{Destroy} or
\italic{Finalize} procedures.
For example, the following is \italic{helloworldview}'s FinalizeObject
procedure:
\formatnote{
void helloworldview__FinalizeObject(classID,hwv)
struct classheader *classID;
struct helloworldview *hwv;
\{
view_DeleteApplicationLayer(hwv->view,hwv->applayer);
view_Destroy(hwv->view);
\}
}
The statement \italic{view_DeleteApplicationLayer(hwv->view,hwv>applayer)}
frees the storage allocated for \italic{hwv->applayer}. The statement
\italic{view_Destroy(hwv->view)} frees the storage allocated for
\italic{hwv->view}.
\subsection{Inserting the children into the view tree}
\indexi{View tree++Children}
\indexi{ \italic{LinkTree}}
When building a new view out of other component views, your new view must
insert the child views in the view tree, and allocate screen space to
these
child views. The insertion of child views under a parent view in the
view
tree is done with the view method, \italic{LinkTree}. You must override
the
method and write a method that calls \italic{LinkTree} for each of its
children, and then calls \italic{super_LinkTree}.
For example, for \italic{helloworldview}:
\formatnote{
void helloworldview__LinkTree(hwv,parent)
struct helloworldview *hwv;
struct view *parent;
\{
if(hwv->applayer!=NULL)
view_LinkTree(hwv->applayer,hwv);
super_LinkTree(hwv,parent);
\}
}
The statement \italic{view_LinkTree(hwv->view, hwv)} links \italic{hwv}'s
application layer to the view.
The statement
\italic{super_LinkTree(hwv,parent}) links \italic{hwv} into the view
tree.
The example program described here links the child into the view tree and
leaves it.
For applications that require dynamic view hierarchies, the
Andrew Toolkit provides a method for taking children out of the view tree
-\italic{view_UnlinkTree} (see View, Vol. II).
\subsection{Associating the child views with their data objects}
\indexi{Data objects++Child views}
\indexi{Views++Child data objects}
When building a new view out of other component views, your new view must
associate the child views with their data objects. The association of
the
child views with their data objects must be done with the view method,
\italic{SetDataObject}. You must override the method and write a method
that
calls \italic{SetDataObject} for each of its children, and then calls
\italic{super_SetDataObject}.
For example, for \italic{helloworldview}:
\formatnote{
void helloworldview__SetDataObject(hwv,hw)
struct helloworldview *hwv;
struct helloworld *hw;
\{
hwv->x=hw->x;
hwv->y=hw->y;
hwv->blackOnWhite=hw->blackOnWhite;
hwv->view=(struct view*)
>dobj));
class_NewObject(dataobject_ViewName(hw-
hwv->applayer=view_GetApplicationLayer(hwv->view);
view_SetDataObject(hwv->view,hw->dobj);
super_SetDataObject(hwv,hw);
\}
}
The first three statements initialize view data from the data object.
(Such
initializations cannot be done in the view's \italic{InitializeObject}
method,
since the view does not have a handle on its data object there.)
The next statement creates a new object and assignes it a default view
based
on the data. Then an application layer is found for the view.
The statement \italic{view_SetDataObject(hwv->view,hw->dobj)} associates
\italic{hwv}'s child, \italic{hwv->view}, with its dataobject,
\italic{hw->dobj}.
The statement \italic{super_SetDataObject(hwv,hw)} associates
\italic{hwv}
itself with its dataobject, \italic{hw}.
\subsection{Writing the method for full update requests} \indexi{Full
update}
When building a new view out of child views, your new view must call the
FullUpdate method for all its children within its FullUpdate method.
Before
calling a child's FullUpdate method, the parent should:
\itemize{
optionally negotiate with the child about its desired size (see
\italic{view_DesiredSize}, View, Vol. II)
allocate space to the child view with \italic{view_InsertView}
}
You have the option to initiate a negotiation with the child
how
much size it wants, given a particular space offer. In this
call
the child view's \italic{view_DesiredSize} method, giving it
a
certain amount of space, and requesting that it tell you how
it
actually needs. \indexi{Size negotiation}
view about
case you can
an offer of
much space
\indexi{View++Size}
\indexi{ \italic{DesiredSize}}
A view's \italic{DesiredSize} procedure can be called anytime after
view_LinkTree has been called on the view. This means that the view's
parent
pointer points to the actual view's parent. This enables the view to
discover
what type of display it will be displayed upon, and to make calls that
return
font dimensional information. However, the view's space allocation has
not
yet been set, or more precisely, may change again. The
\italic{view_DesiredSize} procedure is an optional procedure and if no
such
procedure is provided by the view's author, the default action is for the
view
to claim all of the screen space it is offered. Note that a parent view
does
not have a responsibility to call a child view's \italic{DesiredSize}:
the
parent view may already know how much screen space to allocate to the
child
based upon the parent's own dimensions, for example. This is the case in
this
example program.
To allocate space to a child view, you call view_InsertView which sets
the
child's local bounds rectangle and its visible rectangle. The local
bounds
rectangle defines the space allocated to the child view, while the
visible
rectangle indicates how much of this space is actually visible on the
screen
(assuming this can be expressed as a single rectangle). Normally you
would
make this call at the end of a size negotiation and before calling the
child's
\italic{FullUpdate} method.
Once a child's \italic{DesiredSize} method is optionally called, and the
child's \italic{InsertView} and \italic{FullUpdate} methods invoked, the
child
view is essentially "born," that is, the child will take its place, if
any, on
the screen display.
The following is the \italic{FullUpdate} method for
\italic{helloworldview}:
\formatnote{
#define WIDTH 100
#define HEIGHT 100
void helloworldview__FullUpdate(hwv,type,left,top,width,height)
struct helloworldview *hwv;
enum view_UpdateType type;
long left;
long top;
long width;
long height;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
struct rectangle myVisualRect,rec;
helloworldview_GetVisualBounds(hwv,&myVisualRect);
hwv->vrWidth=rectangle_Width(&myVisualRect);
hwv->vrHeight=rectangle_Height(&myVisualRect);
if (hwv->newFrameX+hwv->vrWidth>TOTALSIZE)
hwv->newFrameX=TOTALSIZE-hwv->vrWidth;
if (hwv->newFrameY+hwv->vrHeight>TOTALSIZE)
hwv->newFrameY=TOTALSIZE-hwv->vrHeight;
hwv->frameX=hwv->newFrameX;
hwv->frameY=hwv->newFrameY;
if(hw->x==POSUNDEF)\{
hw->x=hwv->frameX+(hwv->vrWidth-WIDTH)/2;
hw->y=hwv->frameY+(hwv->vrHeight-HEIGHT)/2;
\}
hwv->x=hw->x;
hwv->y=hw->y;
hwv->blackOnWhite=hw->blackOnWhite;
/* rec is the rectangle in which the
child view */
/* will be displayed, plus a one pixel border. */
\bold{rectangle_SetRectSize(&rec,
hwv->x-hwv->frameX-1,hwv->y-hwv->
frameY-1,WIDTH+1,HEIGHT+1);}
helloworldview_SetTransferMode(hwv,graphic_COPY);
if(hw->blackOnWhite)\{
helloworldview_FillRect(hwv, &myVisualRect,
helloworldview_WhitePattern(hwv));
/* if on white background, draw a rectangle around it*/
\bold{helloworldview_DrawRect(hwv,&rec);}
\}
else\{
helloworldview_FillRect(hwv, &myVisualRect,
helloworldview_BlackPattern(hwv));
/* Most views expect a white background,*/
/* so make one for the sub-view */
helloworldview_FillRect (hwv, &rec,
helloworldview_WhitePattern (hwv));]
\}
/* Get rid of the border pixel */
\bold{ rec.top++;
rec.left++;
rec.width--;
rec.height--;}
/*
InsertView sets the child view's coordinate system */
\bold{view_InsertView(hwv->applayer,hwv,&rec);}
/* FullUpdate draws the child view */
\bold{view_FullUpdate(hwv>applayer,view_FullRedraw,0,0,WIDTH,HEIGHT);}
\}
}
Rather than negotiate about size, the statement
\italic{rectangle_SetRectSize
(&rec, hwv->x-hwv->frameX-1, hwv->y-hwv-> frameY-1, \smaller{WIDTH}+1,
\smaller{HEIGHT}+1)} calculates a size for the \italic{hwv}'s child based
on
\italic{hwv}'s size.
The statement \italic{view_InsertView (hwv->applayer, hwv, &rec)} sets
\italic{hwv}'s child to have the logical and visible coordinates
specified by
\italic{&rec}.
The statement
\italic{view_FullUpdate(hwv>applayer,view_FullRedraw,0,0,\smaller{WIDTH,HEIGHT\
})} calls the child's \italic{FullUpdate} method.
\subsection{Updating the screen partially} \indexi{Update}
If you are creating a view that will have children, then you may need to
handle partial updates to the children. The following \italic{Update}
method
from Example 17 is illustrative:
\formatnote{
void helloworldview__Update(hwv)
struct helloworldview *hwv;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
helloworldview_SetTransferMode(hwv, graphic_COPY);
\formatnote{if(hwv->x!=hw->x ||
hwv->y!=hw->y ||
hwv->frameX!=hwv->newFrameX ||
hwv->frameY!=hwv->newFrameY ||
hwv->blackOnWhite!=hw->blackOnWhite)\{
struct rectangle rec;
if(hwv->x!=hw->x ||
hwv->y!=hw->y)\{
static char buf[100];
sprintf(buf,"Hello world at (%d,%d)",hw->x,hw->y);
message_DisplayString(hwv,0,buf);
\}
}
if(hw->blackOnWhite!=hwv->blackOnWhite)\{
struct rectangle vr;
helloworldview_GetVisualBounds(hwv,&vr);
\formatnote{ if(hw->blackOnWhite)
helloworldview_FillRect(hwv,&vr,helloworldview_WhitePattern(hwv));
else
helloworldview_FillRect(hwv,&vr,helloworldview_BlackPattern(hwv));
hwv->blackOnWhite=hw->blackOnWhite;
}
\}
/* includes 1 pixel border */
\formatnote{rectangle_SetRectSize(&rec,
hwv->x-hwv->frameX-1,hwv->y-hwv->
frameY-1,WIDTH+2,HEIGHT+2);
/* erase the old
child view */
if(hw->blackOnWhite)
helloworldview_FillRect(hwv,&rec,
helloworldview_WhitePattern(hwv));
else
helloworldview_FillRect(hwv,
&rec,helloworldview_BlackPattern(hwv))
}
hwv->x=hw->x;
hwv->y=hw->y;
hwv->frameX=hwv->newFrameX;
hwv->frameY=hwv->newFrameY;
/* Re-set the child view's size and coordinates */
\formatnote{ rectangle_SetRectSize(&rec,
hwv->x-hwv->frameX,hwv->y-hwv->frameY, WIDTH,HEIGHT);
view_InsertView(hwv->applayer,hwv,&rec);
/* Completely redraw it, since it's moved */
if(hw->blackOnWhite)
helloworldview_DrawRectSize(hwv, hwv->x-hwv->frameX-1,
hwv->y-hwv->frameY-1, WIDTH+1,HEIGHT+1);
view_FullUpdate(hwv->applayer,view_FullRedraw,0,0,WIDTH,HEIGHT);
}
/* If the view hasn't moved, simply let it do any updating that it
needs to
*/
\bold{
\}else
view_Update(hwv->applayer);
}\}
}
\subsection{Passing mouse events to children} \indexi{Mouse events}
\indexi{ \italic{Hit}}
In general, it is up to the parent view to pass interesting events down
to its
children by calling the child's specialized methods.
There is also one standard view method that a parent view often invokes
in a
child view: the \italic{Hi}t method. Quite often when a mouse hit
occurs in
a region of the screen allocated to a child view, the correct thing to do
is
simply pass the mouse hit to the child view. Example 17's Hit method
illustrates this strategy:
\formatnote{
struct view *helloworldview__Hit(hwv,action,x,y,numberOfClicks)
struct helloworldview *hwv;
enum view_MouseAction action;
long x;
long y;
long numberOfClicks;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
/* We send mouse hits to the child, hwv->view */
/* if they fall within its visible rectangle.*/
if(!hwv->HaveDownTransition &&\formatnote{
x>=(hwv->x-hwv->frameX) && x<(hwv->x-hwv->frameX+WIDTH) &&
y>=(hwv->y-hwv->frameY) && y<(hwv->y-hwv->frameY+HEIGHT))\{
view_Hit(hwv->applayer,
action,
x-(hwv->x-hwv->frameX),
y-(hwv->y-hwv->frameY),
numberOfClicks);
}
if(hwv->HaveDownTransition)
switch(action)\{
case view_RightUp:
hwv->HaveDownTransition=FALSE;
/* fall through */
case view_RightMovement:
hw->x+=x-hwv->hitX;
hw->y+=y-hwv->hitY;
hwv->hitX=x;
hwv->hitY=y;
break;
case view_LeftUp:
hwv->HaveDownTransition=FALSE;
hw->x=x+hwv->frameX;
hw->y=y+hwv->frameY;
break;
case view_LeftMovement:
/* do nothing */
break;
default:
/* re-synchronize mouse */
hwv->HaveDownTransition=FALSE;
\}
if(!hwv->HaveDownTransition)
switch(action)\{
case view_RightDown:
hwv->hitX=x;
hwv->hitY=y;
/* fall through */
case view_LeftDown:
hwv->HaveDownTransition=TRUE;
helloworldview_WantInputFocus(hwv,hwv);
break;
\}
helloworld_NotifyObservers(hw,0);
return (struct view *)hwv;
\}
}
Note that two of the parameters to the Hit method are the \italic{x} and
\italic{y} coordinates of the mouse hit, and these coordinates are
expressed
relative to the parent view's local bounds rectangle, so the parent
view's Hit
method must subtract the difference in origins between its view and the
child's view before calling the child's Hit method. Don't forget that
the
child's Hit method returns a value, so you should return this value as
the
value of your own Hit method.
\subsection{Menus and keystates} \indexi{Menus} \indexi{Keystate}
\indexi{Keymap} \indexi{Input focus}
The child's \italic{Hit} method may request the input focus. If granted
the
input focus, the child view's \italic{ReceiveInputFocus} method may post
menus
or keystates. The parent view has full control over these possibilities.
In
particular, the parent view may intercept, and possibly ignore, requests
for
the input focus from child views by providing a
\italic{view_WantInputFocus}
method. This method will be invoked every time a child (or any
descendant) of
the parent view requests the input focus. The first parameter to this
method
will be the parent, and the second parameter will be the view that
desires the
input focus. The parent method can discard the request by simply
returning,
in which case the descendant will never receive the input focus.
Alternatively, the parent can permit the request to continue up the view
tree
by invoking its own parent's \italic{view_WantInputFocus} method with the
first parameter equal to the its parent's view and the second parameter
being
the view desiring the input focus.
Once a child view is granted the input focus by Andrew Toolkit, its
\italic{ReceiveInputFocus} method will be called. In this method, a
child
view may post menus or keystates (providing key-to-command binds).
Again, a
parent view may intercept these requests and modify them. To intercept
menu
posting requests from its children, a parent should provide a
\italic{PostMenus} method; to intercept keystate posting requests from
your
children, the parent should provide a \italic{PostKeyState} method. Note
that
if you do not provide \italic{PostMenus} or \italic{PostKeyState}
methods,
defaults will be provided that allow you children's posts to proceed up
the
view tree unimpeded.
Your view's \italic{PostMenus} method is called with two parameters, the
first
being a pointer to your view and the second being the menu list being
posted
by one of your child views. Your \italic{PostMenus} method has several
options. It can discard the request by simply returning, which will
result in
the child's menus being discarded. It can allow the request by calling
its
own parent's \italic{PostMenus} method with the same menu list. Finally,
your
view can change the menulist being posted, by either making a copy of the
menulist with changes of your own, chaining your own menulist changes to
the
original menulist or completely replacing the menulist with a new
menulist of
your own design (see Menu List, Vol. II).
Your view's \italic{PostKeyState} method is called with two parameters, a
pointer to your view and a pointer to the keystate being posted. Your
view
can allow the post to continue unimpeded by invoking its own parent with
the
same keystate structure. Your view can discard the keystate by simply
returning, in which case the last keystate actually posted will remain in
effect. Or your view can post its own keystate, by invoking its parent's
\italic{PostKeyState} method with a new second parameter. Keystates can
be
threaded together in a list, just like menulists (Keystate, Vol. II).
Remaining differences are specific to Example 17 and are not of general
interest.
\begindata{bp,538929032}
\enddata{bp,538929032}
\view{bpv,538929032,953,0,0}
\begindata{texttag,539315976}
\textdsversion{12}
ExSeventeenListing\
\enddata{texttag,539315976}
\view{texttagv,539315976,954,0,0}
\section{Program listing for Example 17}
\formatnote{
\bold{hello.ch}
#define POSUNDEF (-1)
class helloworld[hello]: dataobject [dataobj]\{
overrides:
Read(FILE *file,long id) returns long;
Write(FILE *file,long writeId,int level) returns long;
classprocedures:
InitializeObject(struct helloworld *hw) returns boolean;
FinalizeObject(struct helloworld *hw);
data:
long x,y;
boolean blackOnWhite;
struct dataobject *dobj;
\};
\bold{hello.c}
#include <stdio.h>
#include <class.h>
#include "hello.eh"
#include "dataobj.ih"
#include "text.ih"
#include "style.ih"
struct dataobject *createInitialDobj()
\{
struct text *text=text_New();
struct style *bold=style_New(),*italic=style_New();
style_SetName(bold,"bold");
style_AddNewFontFace(bold,fontdesc_Bold);
style_SetName(italic,"italic");
style_AddNewFontFace(italic,fontdesc_Italic);
text_InsertCharacters(text,0,"Hello world!",sizeof("Hello world!")1);
text_AddStyle(text,0,5,bold);
text_AddStyle(text,6,5,italic);
return (struct dataobject *)text;
\}
boolean helloworld__InitializeObject(classID,hw)
struct classheader *classID;
struct helloworld *hw;
\{
hw->x = POSUNDEF;
hw->y = POSUNDEF;
hw->blackOnWhite = TRUE;
hw->dobj=createInitialDobj();
return TRUE;
\}
void helloworld__FinalizeObject(classID,hw)
struct classheader *classID;
struct helloworld *hw;
\{
dataobject_Destroy(hw->dobj);
\}
long helloworld__Read(hw,file,id)
struct helloworld *hw;
FILE *file;
long id;
\{
char buf[100],classNameBuf[100];
long retVal,dobjObjId;
helloworld_SetID(hw,helloworld_UniqueID(hw));
if(fgets(buf,sizeof(buf),file)==NULL ||
/* the %hd tells scanf that blackOnWhite is a short, not an int */
sscanf(buf,"%d %d %hd\\n",&hw->x,&hw->y,&hw->blackOnWhite)<3 ||
fgets(buf,sizeof(buf),file)==NULL ||
sscanf(buf,"\\\\begindata\{%[^,],%d\}\\n",
classNameBuf,&dobjObjId)<2)
retVal=dataobject_PREMATUREEOF;
else\{
retVal=dataobject_Read(hw->dobj,file,id);
if(retVal==dataobject_NOREADERROR)
if(fgets(buf,sizeof(buf),file)==NULL) /* read in the
\\enddata\{...\} */
retVal=dataobject_MISSINGENDDATAMARKER;
\}
return retVal;
\}
long helloworld__Write(hw,file,writeId,level)
struct helloworld *hw;
FILE *file;
long writeId;
int level;
\{
if(writeId!=helloworld_GetWriteID(hw))\{ /* only write a given
version
once */
helloworld_SetWriteID(hw,writeId);
fprintf(file,"\\\\begindata\{%s,%d\}\\n",
class_GetTypeName(hw), helloworld_UniqueID(hw));
fprintf(file,"%d %d %d\\n",hw->x,hw->y,hw->blackOnWhite);
dataobject_Write(hw->dobj,file,writeId,level);
fprintf(file,"\\\\enddata\{%s,%d\}\\n",
class_GetTypeName(hw), helloworld_UniqueID(hw));
\}
return helloworld_UniqueID(hw);
\}
\bold{hellov.ch}
#include "rect.h"
class helloworldview[hellov]: view \{
overrides:
SetDataObject(struct helloworld *hw);
FullUpdate(enum view_UpdateType type, long left, long top, long
width,
long height);
Update();
Hit (enum view_MouseAction action, long x, long y, long
numberOfClicks)
returns struct view *;
ReceiveInputFocus();
LoseInputFocus();
GetInterface(int type) returns char *;
GetApplicationLayer() returns struct view *;
DeleteApplicationLayer(struct view *);
LinkTree(struct view *parent);
classprocedures:
InitializeClass() returns boolean;
data:
struct keystate *keystate;
struct menulist *menulist;
boolean HaveDownTransition;
boolean haveInputFocus;
long hitX,hitY;
int x,y;
boolean blackOnWhite;
long frameX, frameY;
long newFrameX, newFrameY;
int vrWidth,vrHeight;
struct view *view;
struct view *applayer;
\};
\bold{hellov.c}
#include <stdio.h>
#include <class.h>
#include "hellov.eh"
#include "graphic.ih"
#include "fontdesc.ih"
#include "rect.h"
#include "point.h"
#include "keymap.ih"
#include "keystate.ih"
#include "menulist.ih"
#include "scroll.ih"
#include "bind.ih"
#include "message.ih"
#include "im.ih"
#include "dataobj.ih"
#include "view.ih"
#include "hello.ih"
#define TOTALSIZE 1500
static void xgetinfo(), xsetframe(), ygetinfo(), ysetframe();
static long xwhat(), ywhat();
static struct scrollfns horizInterface = \{
xgetinfo, xsetframe, NULL, xwhat
\};
static struct scrollfns vertInterface = \{
ygetinfo, ysetframe, NULL, ywhat
\};
static struct keymap *helloworldviewKeymap;
static struct menulist *helloworldviewMenulist;
boolean helloworldview__InitializeObject(classID,hwv)
struct classheader *classID;
struct helloworldview *hwv;
\{
hwv->haveInputFocus=FALSE;
hwv->HaveDownTransition=FALSE;
hwv->keystate=keystate_Create(hwv,helloworldviewKeymap);
hwv->menulist=menulist_DuplicateML(helloworldviewMenulist,hwv);
hwv->newFrameX=0;
hwv->newFrameY=0;
hwv->view=NULL;
hwv->applayer=NULL;
return TRUE;
\}
void helloworldview__FinalizeObject(classID,hwv)
struct classheader *classID;
struct helloworldview *hwv;
\{
if(hwv->view!=NULL)\{
view_DeleteApplicationLayer(hwv->view,hwv->applayer);
view_Destroy(hwv->view);
\}
\}
void helloworldview__LinkTree(hwv,parent)
struct helloworldview *hwv;
struct view *parent;
\{
if(hwv->applayer!=NULL)
view_LinkTree(hwv->applayer,hwv);
super_LinkTree(hwv,parent);
\}
void helloworldview__SetDataObject(hwv,hw)
struct helloworldview *hwv;
struct helloworld *hw;
\{
hwv->x=hw->x;
hwv->y=hw->y;
hwv->blackOnWhite=hw->blackOnWhite;
hwv->view=(struct view *)class_NewObject(dataobject_ViewName(hw>dobj));
hwv->applayer=view_GetApplicationLayer(hwv->view);
view_SetDataObject(hwv->view,hw->dobj);
super_SetDataObject(hwv,hw);
\}
struct view *helloworldview__GetApplicationLayer(hwv)
struct helloworldview *hwv;
\{
return (struct view *)scroll_Create(hwv,scroll_LEFT+scroll_BOTTOM);
\}
void helloworldview__DeleteApplicationLayer(hwv,scrollbar)
struct helloworldview *hwv;
struct scroll *scrollbar;
\{
scroll_Destroy(scrollbar);
\}
#define WIDTH 100
#define HEIGHT 100
void helloworldview__FullUpdate(hwv,type,left,top,width,height)
struct helloworldview *hwv;
enum view_UpdateType type;
long left;
long top;
long width;
long height;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
struct rectangle myVisualRect,rec;
helloworldview_GetVisualBounds(hwv,&myVisualRect);
hwv->vrWidth=rectangle_Width(&myVisualRect);
hwv->vrHeight=rectangle_Height(&myVisualRect);
if (hwv->newFrameX+hwv->vrWidth>TOTALSIZE)
hwv->newFrameX=TOTALSIZE-hwv->vrWidth;
if (hwv->newFrameY+hwv->vrHeight>TOTALSIZE)
hwv->newFrameY=TOTALSIZE-hwv->vrHeight;
hwv->frameX=hwv->newFrameX;
hwv->frameY=hwv->newFrameY;
if(hw->x==POSUNDEF)\{
hw->x=hwv->frameX+(hwv->vrWidth-WIDTH)/2;
hw->y=hwv->frameY+(hwv->vrHeight-HEIGHT)/2;
\}
hwv->x=hw->x;
hwv->y=hw->y;
hwv->blackOnWhite=hw->blackOnWhite;
rectangle_SetRectSize(&rec,
hwv->x-hwv->frameX-1,hwv->y-hwv->
frameY-1,
WIDTH+1,HEIGHT+1);
helloworldview_SetTransferMode(hwv,graphic_COPY);
if(hw->blackOnWhite)\{
helloworldview_FillRect(hwv,&myVisualRect,
helloworldview_WhitePattern(hwv));
/* if on white background, draw a rectangle around it */
helloworldview_DrawRect(hwv,&rec);
\}else\{
helloworldview_FillRect(hwv,&myVisualRect,
helloworldview_BlackPattern(hwv));
helloworldview_FillRect(hwv,&rec,
helloworldview_WhitePattern(hwv));
\}
rec.top++;
rec.left++;
rec.width--;
rec.height--;
view_InsertView(hwv->applayer,hwv,&rec);
view_FullUpdate(hwv->applayer,view_FullRedraw,0,0,WIDTH,HEIGHT);
\}
void helloworldview__Update(hwv)
struct helloworldview *hwv;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
helloworldview_SetTransferMode(hwv, graphic_COPY);
if(hwv->x!=hw->x ||
hwv->y!=hw->y ||
hwv->frameX!=hwv->newFrameX ||
hwv->frameY!=hwv->newFrameY ||
hwv->blackOnWhite!=hw->blackOnWhite)\{
struct rectangle rec;
if(hwv->x!=hw->x ||
hwv->y!=hw->y)\{
static char buf[100];
sprintf(buf,"Hello world at (%d,%d)",hw->x,hw->y);
message_DisplayString(hwv,0,buf);
\}
if(hw->blackOnWhite!=hwv->blackOnWhite)\{
struct rectangle vr;
helloworldview_GetVisualBounds(hwv,&vr);
if(hw->blackOnWhite)
helloworldview_FillRect(hwv,&vr,
helloworldview_WhitePattern(hwv));
else
helloworldview_FillRect(hwv,&vr,
helloworldview_BlackPattern(hwv));
hwv->blackOnWhite=hw->blackOnWhite;
\}
/* includes 1 pixel border */
rectangle_SetRectSize(&rec,
hwv->x-hwv->frameX-1,hwv->y-hwv->
frameY-1,
WIDTH+2,HEIGHT+2);
if(hw->blackOnWhite)
helloworldview_FillRect(hwv, &rec,
helloworldview_WhitePattern(hwv));
else
helloworldview_FillRect(hwv, &rec,
helloworldview_BlackPattern(hwv));
hwv->x=hw->x;
hwv->y=hw->y;
hwv->frameX=hwv->newFrameX;
hwv->frameY=hwv->newFrameY;
rectangle_SetRectSize(&rec,hwv->x-hwv->frameX,
hwv->y-hwv->frameY,WIDTH,HEIGHT);
view_InsertView(hwv->applayer,hwv,&rec);
if(hw->blackOnWhite)
helloworldview_DrawRectSize(hwv,hwv->x-hwv->
frameX-1,
hwv->y-hwv->frameY-1,WIDTH+1,HEIGHT+1);
view_FullUpdate(hwv->applayer,view_FullRedraw,0,0,
WIDTH,HEIGHT);
\}else
view_Update(hwv->applayer);
\}
struct view *helloworldview__Hit(hwv,action,x,y,numberOfClicks)
struct helloworldview *hwv;
enum view_MouseAction action;
long x;
long y;
long numberOfClicks;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
if(!hwv->HaveDownTransition &&
x>=(hwv->x-hwv->frameX) && x<(hwv->x-hwv->frameX+WIDTH) &&
y>=(hwv->y-hwv->frameY) && y<(hwv->y-hwv->frameY+HEIGHT))
return view_Hit(hwv->applayer,
action,
x-(hwv->x-hwv->frameX),
y-(hwv->y-hwv->frameY),
numberOfClicks);
if(hwv->HaveDownTransition)
switch(action)\{
case view_RightUp:
hwv->HaveDownTransition=FALSE;
/* fall through */
case view_RightMovement:
hw->x+=x-hwv->hitX;
hw->y+=y-hwv->hitY;
hwv->hitX=x;
hwv->hitY=y;
break;
case view_LeftUp:
hwv->HaveDownTransition=FALSE;
hw->x=x+hwv->frameX;
hw->y=y+hwv->frameY;
break;
case view_LeftMovement:
/* do nothing */
break;
default:
/* re-synchronize mouse */
hwv->HaveDownTransition=FALSE;
\}
if(!hwv->HaveDownTransition)
switch(action)\{
case view_RightDown:
hwv->hitX=x;
hwv->hitY=y;
/* fall through */
case view_LeftDown:
hwv->HaveDownTransition=TRUE;
helloworldview_WantInputFocus(hwv,hwv);
break;
\}
helloworld_NotifyObservers(hw,0);
return (struct view *)hwv;
\}
void helloworldview__ReceiveInputFocus(hwv)
struct helloworldview *hwv;
\{
hwv->haveInputFocus=TRUE;
hwv->keystate->next=NULL;
helloworldview_PostKeyState(hwv,hwv->keystate);
helloworldview_PostMenus(hwv,hwv->menulist);
\}
void helloworldview__LoseInputFocus(hwv)
struct helloworldview *hwv;
\{
hwv->haveInputFocus=FALSE;
\}
static void Center(hwv,rock)
struct helloworldview *hwv;
long rock;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
hw->x = hwv->newFrameX + hwv->vrWidth / 2;
hw->y = hwv->newFrameY + hwv->vrHeight / 2;
helloworld_NotifyObservers(hw,0);
\}
static void Invert(hwv, rock)
struct helloworldview *hwv;
long rock;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
hw->blackOnWhite=!hw->blackOnWhite;
helloworld_NotifyObservers(hw,0);
\}
static void relocate(hwv,rock)
struct helloworldview *hwv;
long rock;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
char buf[100];
int x,y;
message_AskForString(hwv,0,"New location (x,y):
",NULL,buf,sizeof(buf));
if(sscanf(buf,"%d,%d",&x,&y)!=2)
message_DisplayString(hwv,1,"Bad format; must be: number,number");
else\{
hw->x=x;
hw->y=y;
helloworld_NotifyObservers(hw,0);
\}
\}
static void readHW(hwv,rock)
struct helloworldview *hwv;
long rock;
\{
char file[100], msgBuf[100];
FILE *fp;
message_AskForString(hwv,0,"Read file: ",NULL,file,sizeof(file));
fp=fopen(file,"r");
if(fp==NULL)\{
sprintf(msgBuf,"Couldn't open %s for reading.", file);
message_DisplayString(hwv,1,msgBuf);
\}else\{
char header[100];
if(fgets(header,sizeof(header),fp)==NULL)\{
sprintf(msgBuf,"Premature end-of-file in %s.",file);
message_DisplayString(hwv,1,msgBuf);
\}else\{
char name[20];
int id;
if(sscanf(header,"\\\\begindata\{%[^,],%d\}\\n",
name,&id)!=2)\{
sprintf(msgBuf,
"%s doesn't contain a valid datastream header.",
file);
message_DisplayString(hwv,1,msgBuf);
\}else\{
struct helloworld *hw=
(struct helloworld *)hwv->header.view.dataobject;
if(strcmp(name,class_GetTypeName(hw))!=0)\{
sprintf(msgBuf,
"%s doesn't contain a helloworld dataobject.",
file);
message_DisplayString(hwv,1,msgBuf);
\}else\{
/* FINALLY, read the object in... */
helloworld_Read(hw,fp,id);
fclose(fp);
helloworld_NotifyObservers(hw,0);
\}
\}
\}
\}
\}
static void writeHW(hwv,rock)
struct helloworldview *hwv;
long rock;
\{
char file[100], msgBuf[100];
FILE *fp;
message_AskForString(hwv,0,"Write file: ",NULL,file,sizeof(file));
fp=fopen(file,"w");
if(fp==NULL)\{
sprintf(msgBuf,"Couldn't open %s for writing.",file);
message_DisplayString(hwv,1,msgBuf);
\}else\{
struct helloworld *hw=
(struct helloworld *)hwv->header.view.dataobject;
helloworld_Write(hw,fp,im_GetWriteID(),0);
fclose(fp);
\}
\}
static void xgetinfo(hwv, total, seen, dot)
struct helloworldview *hwv;
struct range *total, *seen, *dot;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
total->beg = 0;
total->end = TOTALSIZE;
seen->beg = hwv->frameX;
seen->end = hwv->frameX + hwv->vrWidth;
dot->beg = dot->end = hw->x;
\}
static void ygetinfo(hwv, total, seen, dot)
struct helloworldview *hwv;
struct range *total, *seen, *dot;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
total->beg = 0;
total->end = TOTALSIZE;
seen->beg = hwv->frameY;
seen->end = hwv->frameY + hwv->vrHeight;
dot->beg = dot->end = hw->y;
\}
static void xsetframe(hwv, posn, cord, outof)
struct helloworldview *hwv;
int posn;
long cord, outof;
\{
hwv->newFrameX = posn - hwv->vrWidth * cord / outof;
if (hwv->newFrameX + hwv->vrWidth > TOTALSIZE)
hwv->newFrameX = TOTALSIZE - hwv->vrWidth;
else if (hwv->newFrameX < 0)
hwv->newFrameX = 0;
helloworldview_WantUpdate(hwv, hwv);
\}
static void ysetframe(hwv, posn, cord, outof)
struct helloworldview *hwv;
int posn;
long cord, outof;
\{
hwv->newFrameY = posn - hwv->vrHeight * cord / outof;
if (hwv->newFrameY + hwv->vrHeight > TOTALSIZE)
hwv->newFrameY = TOTALSIZE - hwv->vrHeight;
else if (hwv->newFrameY < 0)
hwv->newFrameY = 0;
helloworldview_WantUpdate(hwv, hwv);
\}
static long xwhat(hwv, cord, outof)
struct helloworldview *hwv;
long cord, outof;
\{
return hwv->frameX + hwv->vrWidth * cord / outof;
\}
static long ywhat(hwv, cord, outof)
struct helloworldview *hwv;
long cord, outof;
\{
return hwv->frameY + hwv->vrHeight * cord / outof;
\}
char *helloworldview__GetInterface(hwv, type)
struct helloworldview *hwv;
char *type;
\{
if (strcmp(type, "scroll,vertical") == 0)
return (char *) &vertInterface;
else if (strcmp(type, "scroll,horizontal") == 0)
return (char *) &horizInterface;
else
return NULL;
\}
static struct bind_Description helloworldviewBindings[]=\{
\{"helloworldview-center", "\\003",0, "Hello World,Center",0,0,
Center,
"Center the helloworldview string."\},
\{"helloworldview-invert", "\\011",0, "Hello World,Invert",0,0,
Invert,
"Invert the helloworldview string."\},
\{"helloworldview-relocate", "\\022",0, "Hello World,Relocate",0,0,
relocate, "Relocate the helloworld string."\},
\{"helloworldview-read", NULL,0, "Hello World,Read",0,0, readHW,
"Read in
a new hello world."\},
\{"helloworldview-write", NULL,0, "Hello World,Write",0,0, writeHW,
"Write
out the current hello world to a file."\},
NULL
\};
boolean helloworldview__InitializeClass(classID)
struct classheader *classID;
\{
helloworldviewMenulist=menulist_New();
helloworldviewKeymap=keymap_New();
bind_BindList(helloworldviewBindings,
helloworldviewKeymap,
helloworldviewMenulist,
&helloworldview_classinfo);
return TRUE;
\}
\bold{helloa.ch}
class helloworldapp[helloa] : application[app]\{
overrides:
Start() returns boolean;
\};
\bold{helloa.c}
#include <class.h>
#include "helloa.eh"
#include "dataobj.ih"
#include "view.ih"
#include "im.ih"
#include "frame.ih"
#include "lpair.ih"
#include "text.ih"
#include "textv.ih"
#include "style.ih"
#include "hello.ih"
static struct view *appLayerOrDestroy(v)
struct view *v;
\{
if(v==NULL)
return NULL;
else\{
struct view *al=view_GetApplicationLayer(v);
if(al==NULL)\{
view_Destroy(v);
return NULL;
\}
return al;
\}
\}
static boolean makeSplitWindow(dobj1,dobj2)
struct dataobject *dobj1,*dobj2;
\{
struct view *v1,*v2;
struct view *al1,*al2,*lpAl;
struct frame *frame;
struct im *im;
struct lpair *lp;
al1=appLayerOrDestroy(v1=(struct view
*)class_NewObject(dataobject_ViewName(dobj1)));
if(al1==NULL)
return FALSE;
al2=appLayerOrDestroy(v2=(struct view
*)class_NewObject(dataobject_ViewName(dobj2)));
if(al2==NULL) \{
view_DeleteApplicationLayer(v1,al1);
view_Destroy(v1);
return FALSE;
\}
lpAl=appLayerOrDestroy((struct view *)(lp=lpair_New()));
if(lpAl==NULL) \{
view_DeleteApplicationLayer(v2,al2);
view_Destroy(v2);
return FALSE;
\}
/* this call makes a left/right split, with the given
* percentage allocated to the left view
*/
lpair_HSplit(lp,al1,al2,40 /* percent */,TRUE /* movable boundary
*/);
frame=frame_New();
if(frame==NULL)
\{
lpair_DeleteApplicationLayer(lp,lpAl);
lpair_Destroy(lp);
return FALSE;
\}
im=im_Create(NULL);
if(im==NULL) \{
frame_Destroy(frame);
return FALSE;
\}
view_SetDataObject(v1,dobj1);
view_SetDataObject(v2,dobj2);
frame_SetView(frame,lpAl);
im_SetView(im,frame);
view_WantInputFocus(v1,v1);
return TRUE;
\}
boolean helloworldapp__Start(hwapp)
struct helloworldapp *hwapp;
\{
struct helloworld *hw;
struct text *t;
struct style *bold,*italic;
if(!super_Start(hwapp))
return FALSE;
hw=helloworld_New();
if(hw==NULL)
return FALSE;
t=text_New();
if(t==NULL) \{
helloworld_Destroy(hw);
return FALSE;
\}
bold=style_New();
if(bold==NULL)\{
text_Destroy(t);
return FALSE;
\}
style_AddNewFontFace(bold,fontdesc_Bold);
italic=style_New();
if(italic==NULL) \{
style_Destroy(bold);
return FALSE;
\}
style_AddNewFontFace(italic,fontdesc_Italic);
text_InsertCharacters(t,0,"Hello world!",sizeof("Hello world!")-1);
text_AddStyle(t,0,5,bold);
text_AddStyle(t,6,5,italic);
if(!makeSplitWindow((struct dataobject *)hw,(struct dataobject *)t)
||
!makeSplitWindow((struct dataobject *)hw,(struct dataobject *)t))
\{
style_Destroy(italic);
return FALSE;
\}
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 hello.do hellov.do
helloa.do: helloa.o helloa.eh
hello.do:
hello.o hello.eh
hellov.do: hellov.o hellov.eh
helloa.o: helloa.c
helloa.o: $\{INCLUDESRC\}/app.ih
helloa.o: $\{INCLUDESRC\}/atom.ih
helloa.o: $\{INCLUDESRC\}/dataobj.ih
helloa.o: $\{INCLUDESRC\}/frame.ih
helloa.o: $\{INCLUDESRC\}/graphic.ih
helloa.o: $\{INCLUDESRC\}/im.ih
helloa.o: $\{INCLUDESRC\}/lpair.ih
helloa.o: $\{INCLUDESRC\}/message.ih
helloa.o: $\{INCLUDESRC\}/namespc.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: hello.ih
helloa.o: hellov.ih
helloa.o: helloa.eh
helloa.eh helloa.ih: helloa.ch
helloa.eh helloa.ih: $\{INCLUDESRC\}/app.ih
hello.o: hello.c
hello.o: $\{INCLUDESRC\}/atom.ih
hello.o: $\{INCLUDESRC\}/dataobj.ih
hello.o: $\{INCLUDESRC\}/namespc.ih
hello.o: $\{INCLUDESRC\}/observe.ih
hello.o: $\{SRCDIR\}include/class.h
hello.o: hello.eh
hello.eh hello.ih: hello.ch
hello.eh hello.ih: $\{INCLUDESRC\}/atom.ih
hello.eh hello.ih: $\{INCLUDESRC\}/dataobj.ih
hello.eh hello.ih: $\{INCLUDESRC\}/namespc.ih
hello.eh hello.ih: $\{INCLUDESRC\}/observe.ih
hellov.o: hellov.c
hellov.o: $\{INCLUDESRC\}/atom.ih
hellov.o: $\{INCLUDESRC\}/bind.ih
hellov.o: $\{INCLUDESRC\}/dataobj.ih
hellov.o: $\{INCLUDESRC\}/graphic.ih
hellov.o: $\{INCLUDESRC\}/keymap.ih
hellov.o: $\{INCLUDESRC\}/keystate.ih
hellov.o: $\{INCLUDESRC\}/menulist.ih
hellov.o: $\{INCLUDESRC\}/message.ih
hellov.o: $\{INCLUDESRC\}/namespc.ih
hellov.o: $\{INCLUDESRC\}/observe.ih
hellov.o: $\{INCLUDESRC\}/point.h
hellov.o: $\{INCLUDESRC\}/rect.h
hellov.o: $\{INCLUDESRC\}/scroll.ih
hellov.o: $\{INCLUDESRC\}/view.ih
hellov.o: $\{SRCDIR\}include/class.h
hellov.o: hello.ih
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,956,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,538489940}
Download