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

advertisement
\begindata{text,539013432}
\textdsversion{12}
\template{default}
\define{global
}
\define{itemize
}
\formatnote{\chapter{Example 18: Dynamically loading objects}}
\indexi{Dynamic loading}
This example will show how to modify the program in Example17 so that any
object can be dynamically loaded in place of the child text view.
\indexi{Views++Children}
While we have not discussed dynamic loading in previous examples, that
feature
is an integral part of the Andrew Toolkit environment. Among other
things,
the facility allows application programs to be created that can include
or
insert arbitrary objects upon demand without any advance knowledge about
the
object. In this example, we will modify Example 17 so that it can
include any
arbitrary object as the child object. We will add a menu item that will
allow
the user to specify what object should be inserted into the child object
space.
This feature is similar to the insert-object feature in the ATK editor,
EZ.
The example illustrates the following activities:
\itemize{
Creating and binding a change object function that will take user input.
Linking the new object into the view tree. \indexi{View tree}
Setting the data object for the child correctly.
objects++Setting}
\indexi{Data
Modifying the ObservedChange method to keep track of the changing child.
\indexi{ \italic{ObservedChange}}
Reading the new object. \indexi{ \italic{Read}}
}
The discussion that follows presents a step-by-step description of how to
modify the \italic{helloworld} class in Example 17 to produce Example 18.
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. This will be exactly the same as in
Example
17.
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 modify the Read method for helloworld to create a new
buffer
for the dynamically loaded object, and notify observers that the object
has
changed.
a hellov.ch file -- will contain the class definition for the
\italic{helloworld} view. We will override the ObservedChange method,
and add
an additional data definition which will keep track of whether the
sub/child
view should be redrawn.
a hellov.c file -- will contain statements that import Andrew Toolkit
classes
used by the view and definitions of the methods. We will create a new
function, Change Object, to the menus, which will allow the user to
specify
the type of object to be dynamically loaded into the sub-view.
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 17.
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 17.
Makefile -- will contain the directions for compiling, linking and
loading.
This will be exactly the same as in Example 17.
}
For a complete listing of these files, see \italic{Program Listing for
Example
18 } at the end of this section on p.
\begindata{textref,539315976}
\textdsversion{12}
# ExEighteenListing\
\enddata{textref,539315976}
\view{textrefv,539315976,945,0,0}. The source code is available in the
directory /usr/andrew/examples/ex18, together with the compiled program.
Although the discussion refers directly to this example, the information
applies generally to the creating of any application that can dynamically
load
an object.
\section{Running the example program}
\formatnote{\bold{Action 1.}
\bold{command }prompt type
\formatnote{ To run the program, at the
runapp /usr/andrew/examples/ex18/helloa
and press the Enter key.
}
\bold{Response.}\formatnote{
exactly as
in the previous example.
\bold{Action 2.}\formatnote{
\bold{Hello World} menu card.
}The program will produce two window
}Choose \italic{Change Object} from the
\bold{Response. }\formatnote{
}The message line in the window you chose
the
menu option from will respond with \italic{Type of new object:}.
\bold{Action 3.}\formatnote{
\bold{Response. }\formatnote{
the
\bold{Paste} option only.
\bold{Action 4.}\formatnote{
\italic{table}.
}Pop up the menus again.
}There will only be one menu card with
}Type in an object type name, for example,
\bold{Response.} \formatnote{
}The child object in both windows will be
replaced with the table object.
\bold{Action 5.}\formatnote{
table
object, giving it
}Click within the child object, now a
the input focus.
\bold{Response.}\formatnote{
}You will see the blank table cells and
the
table cursor as in the figure on the next page. The menus will also be
the
menus for table, not hello world or text.
\bold{Action 6.}\formatnote{
the
object to
}Click outside the child object and change
\italic{hello world}.
\bold{Response.}\formatnote{
the
child object
window.
world
}You will see the helloworld object inside
You can move around both the entire child object, or the hello
object inside the child object; in either case, the message line will
keep
track of the coordinate positions.
\bold{Action 7.}\formatnote{
menu
cards.
}To quit the program choose Quit from the
}\
\begindata{bp,538928712}
\enddata{bp,538928712}
\view{bpv,538928712,946,0,0}
\section{An overview of dynamic loading}
The dynamic loading facility of the Andrew Toolkit environment provides a
powerful extension mechanism and allows the set of components used by an
application to be virtually unlimited. It can also be used to add
additional
components to the basic Toolkit without having to rebuild applications.
For example, if a member of the music department creates a music object
and
embeds that object into a text object (or any object that allows
embedding of
other objects), the code for the music object will be dynamically loaded
into
the application. Except for a slight delay for loading the code, the
user of
the editor is unaware that the music object was not statically loaded.
The
user is also unaware that the music object was not part of the original
system. The editor did not have to be recompiled, relinked, or otherwise
modified to use the new music object. Further, all users of the text
object
automatically acquire the ability to use the music object: it can be sent
in a
mail messages easily as it can be edited in a document.
Finally, dynamic loading also allows us to run all our applications from
a
single base program. The program \italic{runapp}, which we have been
using to
run all the example programs, contains all the basic objects of the
toolkit.
The code for each individual example is then dynamically loaded in at
run
time. Since most Unix systems do not provide shared libraries, this
allows
multiple toolkit applications to share a significant portion of code.
This
leads to performance improvements in a number of areas: \indexi{Runapp}
\indexi{Dynamic loading++Run time}
\itemize{
paging activity is reduced.
key portions of the code are almost always paged in thus improving user
performance.
virtual memory use decreases.
file fetch time decreases if running under a distributed file system.
the file size of an application is reduced.
}
In this example, we will use the application program created in Example
17,
and modify it so that it will dynamically load any object as the child
object
(sub-view).
\section{Modifying the class definition}
\subsection{Modifying the view definition} \indexi{Views++Defining}
\formatnote{
#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);
\bold{ObservedChanged(struct helloworld *obj, long val);}
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{boolean redrawSubView;}
\};
}
In the helloworldview class definition, we add the ObservedChanged method
to
the list of overrides. The new ObservedChanged method will keep track of
the
changing child object.
\subsection{Initializing the view} \indexi{Views++Initializing}
\formatnote{
\bold{#include "observe.ih"}
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;
\bold{hwv->redrawSubView=TRUE;}
return TRUE;
\}
}
The boolean parameter redrawSubView keeps track of whether or not the
child
object should be redrawn. It is initialized as TRUE.
\subsection{Overriding the ObservedChanged method}
\indexi{ \italic{ObservedChange}}
\formatnote{
void helloworldview__ObservedChanged(hwv,changed,val)
struct helloworldview *hwv;
struct helloworld *changed;
long val;
\{
struct helloworld *hw=(struct helloworld*)hwv>header.view.dataobject;
if(changed==hw)
switch(val)\{
case helloworld_SubObjectChanged:
setSubView(hwv,hw->dobj);
break;
case observable_OBJECTDESTROYED:
setSubView(hwv,NULL);
break;
\}
super_ObservedChanged(hwv,changed,val);
\}
}
If you are creating an object that observes another, you should create an
override to the ObservedChanged method which takes appropriate action
when the
observed object changes (for example, call WantUpdate). In this case,
the
ObservedChange method is defined so that if the child object changes,
i.e. if
\italic{helloworld_SubObjectChanged} is TRUE, the new object will be set
as
the child object/sub-view; if the object is destroyed, the child
object/sub-view will be set to NULL. Both cases call the setSubView
function
described below.
\subsection{Setting the subview} \indexi{Children}
\indexi{Views++Child} \indexi{View tree}
\indexi{Application layer}
\formatnote{
static void setSubView(hwv,dobj)
struct helloworldview *hwv;
struct dataobject *dobj;
\{
if(hwv->view!=NULL)\{
view_UnlinkTree(hwv->applayer);
if(hwv->view!=hwv->applayer)
view_DeleteApplicationLayer(hwv->view,hwv->applayer);
view_Destroy(hwv->view);
\}
if(dobj!=NULL)\{
hwv->view=(struct view*)class_NewObject(dataobject_ViewName(dobj));
hwv->applayer=view_GetApplicationLayer(hwv->view);
if(hwv->applayer==NULL)
hwv->applayer=hwv->view;
view_SetDataObject(hwv->view,dobj);
view_LinkTree(hwv->applayer,hwv);
\}else
hwv->view=hwv->applayer=NULL;
hwv->redrawSubView=TRUE;
\}
}
The function \italic{setSubView} is created to handle various
linking/unlinking operations necessary when the child object changes.
The
function takes a pointer to the helloworldview and a data object. It the
view
is nonexistent, the application layer associated with the view is
unlinked and
deleted, and the view is explicitly destroyed. If the data object is
null, a
new object is created and a view and application layer are assigned.
Then,
the data object is set to the view, the view's application layer is
linked to
the view in the view tree. If neither the view nor the data object is
null,
the application layer is set to NULL, and the redrawSubView parameter is
set
to TRUE to indicate that the sub view should be redrawn. The parameter
is
used by the Update and FullUpdate methods.
\subsection{Changing the child object} \indexi{Data objects++Changing}
\formatnote{
static void changeObj(hwv,rock)
struct helloworldview *hwv;
long rock;
\{
char objtype[100],msgbuf[100];
struct helloworld *hw=(struct helloworld*)hwv>header.view.dataobject;
struct dataobject *oldDobj=hw->dobj;
message_AskForString(hwv,0,"Type of new object:
",NULL,objtype,sizeof(objtype));
if(!class_IsTypeByName(objtype,"dataobject"))\{
sprintf(msgbuf,"%s is not a dataobject!",objtype);
message_DisplayString(hwv,1,msgbuf);
return;
\}
hw->dobj=(struct dataobject *)class_NewObject(objtype);
if(hw->dobj==NULL)\{
message_DisplayString(hwv,1,"Error creating the object!");
hw->dobj=oldDobj;
return;
\}
helloworld_NotifyObservers(hw,helloworld_SubObjectChanged);
dataobject_Destroy(oldDobj); /* get rid of the old one */
\}
}
The changeObj function handles the actual change of the child object. It
is
bound to the menu item "Change Object." (See description below). The
function prompts the user for a type of new object, returning an error if
the
input does not match with the list of available object type names. If
the
data object cannot be found, an "error creating the object" error message
is
returned. Finally, NotifyObservers is called to inform all observers
that the
sub view has changed, and the old object is destroyed.
\subsection{Binding the change object function}
\formatnote{
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."\},
\bold{\{"helloworldview-change-obj",NULL,0, "Hello World,Change
Object",0,0,
changeObj, "Change the object in the square."\},}
NULL
\};
}
The changeObj function is bound to the menu item, \italic{Change Object}.
There is no corresponding key command for the function.
\subsection{Modifying the SetDataObject method} \indexi{
\italic{SetDataObject}}
\formatnote{
void helloworldview__SetDataObject(hwv,hw)
struct helloworldview *hwv;
struct helloworld *hw;
\{
hwv->x=hw->x;
hwv->y=hw->y;
hwv->blackOnWhite=hw->blackOnWhite;
\bold{setSubView(hwv,hw->dobj);
super_SetDataObject(hwv,hw);}
\}
}
The SetDataObject function is changed slightly from the version in
Example 17.
Since we created a setSubView function to handle the application layer
and
setting the data object for the child view, we call that directly.
\subsection{Reading in a new object}
\indexi{ \italic{Read}}
\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;
\bold{else\{
if(strcmp(classNameBuf,class_GetTypeName(hw->dobj))!=0)\{
/* the type of the sub-object has changed */
struct dataobject *oldDobj;
if(!class_IsTypeByName(classNameBuf,"dataobject"))
return dataobject_BADFORMAT;
oldDobj=hw->dobj;
hw->dobj=(struct dataobject*)class_NewObject(classNameBuf);
helloworld_NotifyObservers(hw,
helloworld_SubObjectChanged);
dataobject_Destroy(oldDobj);
\}}
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;
\}
}
Since the data object of the subview may change, the Read method is
modified
so that the transferring can take place. This involves checking the type
of
the sub-view data object. If the new data object type does not match the
old
data object type (i.e. it is a different object), then the old object is
destroyed and the new object is created, and all observers are notified
of the
change.
\subsection{Modifying the full update and update methods} \indexi{Full
update}\indexi{Update}
To modify the full update method, we add:
\formatnote{
hwv->redrawSubView=FALSE;)}
at end of the FullUpdate definition. There is a similar modification of
Update. The remainder of the differences are specific to this example,
and
not of general interest.
\begindata{bp,538928968}
\enddata{bp,538928968}
\view{bpv,538928968,947,0,0}
\begindata{texttag,539236104}
\textdsversion{12}
ExEighteenListing\
\enddata{texttag,539236104}
\view{texttagv,539236104,948,0,0}
\section{Program listing for Example 18}
\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;
\};
#define helloworld_SubObjectChanged 1 /* constant for NotifyObservers */
\bold{hello.c}
#include <stdio.h>
#include <class.h>
#include "hello.eh"
#include "dataobj.ih"
#include "text.ih"
#include "fontdesc.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\{
if(strcmp(classNameBuf,class_GetTypeName(hw->dobj))!=0)\{
/* the type of the sub-object has changed */
struct dataobject *oldDobj;
if(!class_IsTypeByName(classNameBuf,"dataobject"))
return dataobject_BADFORMAT;
oldDobj=hw->dobj;
hw->dobj=(struct dataobject *)class_NewObject(classNameBuf);
helloworld_NotifyObservers(hw,
helloworld_SubObjectChanged);
dataobject_Destroy(oldDobj);
\}
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);
ObservedChanged(struct helloworld *obj, long val);
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;
boolean redrawSubView;
\};
\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 "observe.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;
hwv->redrawSubView=TRUE;
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);
\}
static void setSubView(hwv,dobj)
struct helloworldview *hwv;
struct dataobject *dobj;
\{
if(hwv->view!=NULL)\{
view_UnlinkTree(hwv->applayer);
if(hwv->view!=hwv->applayer)
view_DeleteApplicationLayer(hwv->view,hwv->applayer);
view_Destroy(hwv->view);
\}
if(dobj!=NULL)\{
hwv->view=(struct view
*)class_NewObject(dataobject_ViewName(dobj));
hwv->applayer=view_GetApplicationLayer(hwv->view);
if(hwv->applayer==NULL)
hwv->applayer=hwv->view;
view_SetDataObject(hwv->view,dobj);
view_LinkTree(hwv->applayer,hwv);
\}else
hwv->view=hwv->applayer=NULL;
hwv->redrawSubView=TRUE;
\}
void helloworldview__SetDataObject(hwv,hw)
struct helloworldview *hwv;
struct helloworld *hw;
\{
hwv->x=hw->x;
hwv->y=hw->y;
hwv->blackOnWhite=hw->blackOnWhite;
setSubView(hwv,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);
\}
void helloworldview__ObservedChanged(hwv,changed,val)
struct helloworldview *hwv;
struct helloworld *changed;
long val;
\{
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
if(changed==hw)
switch(val)\{
case helloworld_SubObjectChanged:
setSubView(hwv,hw->dobj);
break;
case observable_OBJECTDESTROYED:
setSubView(hwv,NULL);
break;
\}
super_ObservedChanged(hwv,changed,val);
\}
#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);
hwv->redrawSubView=FALSE;
\}
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 ||
hwv->redrawSubView)\{
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);
hwv->redrawSubView=FALSE;
\}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 changeObj(hwv,rock)
struct helloworldview *hwv;
long rock;
\{
char objtype[100],msgbuf[100];
struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject;
struct dataobject *oldDobj=hw->dobj;
message_AskForString(hwv,0,"Type of new object:
",NULL,objtype,sizeof(objtype));
if(!class_IsTypeByName(objtype,"dataobject"))\{
sprintf(msgbuf,"%s is not a dataobject!",objtype);
message_DisplayString(hwv,1,msgbuf);
return;
\}
hw->dobj=(struct dataobject *)class_NewObject(objtype);
if(hw->dobj==NULL)\{
message_DisplayString(hwv,1,"Error creating the object!");
hw->dobj=oldDobj;
return;
\}
helloworld_NotifyObservers(hw,helloworld_SubObjectChanged);
dataobject_Destroy(oldDobj); /* get rid of the old one */
\}
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."\},
\{"helloworldview-change-obj",NULL,0, "Hello World,Change
Object",0,0,
changeObj, "Change the object in the square."\},
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 "fontdesc.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,950,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,539013432}
Download