\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}