\begindata{text,538487060} \textdsversion{12} \template{default} \define{global } \define{itemize } \formatnote{\chapter{Example 11: \indexi{External representation} Data streams}} \indexi{Data streams} \indexi{Data objects++Data stream} \indexi{Data objects++Reading} \indexi{Data objects++Writing} \indexi{Insets} Example 11 illustrates how to store data and read it from a file. We will modify the previous example to store (i.e. write) the coordinates of the hello world string in a file. Then, when the file is read in, the string will be redrawn at the stored coordinate position. The discussion that follows presents a step-by-step description of how to modify the \italic{helloworld} class in Example 10 to produce Example 11, which will read and write the coordinate position data of the \italic{hello world} string to permanent storage (a file). 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, which overrides the \italic{Read} and \italic{Write} methods of class \italic{dataobject}. a hello.c file -- will contain statements that import Andrew Toolkit classes used by the data object, initialize the class, and define the \italic{Read} and \italic{Write} methods. a hellov.ch file -- will contain the class definition for the \italic{helloworld} view, and is exactly the same as in the previous example. a hellov.c file -- will contain statements that import Andrew Toolkit classes used by the view and definitions of the methods. This will be similar to the previous example, with the addition of function that handle the input and output to and from the 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 10. a helloa.c -- will contain declarations needed by the Andrew Toolkit linking and loading facilities as well the definition of the \italic{helloworld} application method. This will be exactly the same as in Example 10. Makefile -- will contain the directions for compiling, linking and loading. This will be exactly the same as Example 10. } For a complete listing of these files, see \italic{Program Listing for Example 11} at the end of this section on p. \begindata{textref,539236104} \textdsversion{12} # ExElevenListing\ \enddata{textref,539236104} \view{textrefv,539236104,992,0,0}). The source code is available in the directory /usr/andrew/examples/ex11. Although the discussion of the steps refers directly to this example, the information generally applies to the creation of any application that reads and writes data. \begindata{bp,538929032} \enddata{bp,538929032} \view{bpv,538929032,993,0,0} \section{Running the example program} \formatnote{\bold{Action 1.} \formatnote{To run the program, at the \bold{command }prompt type runapp /usr/andrew/examples/ex11/helloa and press the Enter key. } \bold{Response.}\formatnote{ \italic{hello world} }The program will produce a window with centered in the body of the window, vertical and horizontal scroll bars, and a message line strip. \bold{Action 2.} \formatnote{ by dragging it with }Move \italic{hello world} in the window, the right mouse button, or moving it with left mouse button hits. \bold{Response. } \formatnote{ and the message line } \italic{hello world} will be redrawn will display the message "Hello world at" followed by the coordinates of the text string. \bold{Action 3.}\formatnote{ \bold{Response. }\formatnote{ displayed in the }Choose \bold{Write} from the menu cards. }The message "Write to file" will be message line. \bold{Action 4.}\formatnote{ Move \italic{hello }Type in a file name, and press Enter. world} around again, and choose \bold{Read} from the menu cards. \bold{Response.} \formatnote{ displayed in the message }The message "Read file" will be line. \bold{Action 5.}\formatnote{ \bold{Write} function }Type in the file name you used for the and hit Enter. \bold{Response.} \formatnote{ the position that } \italic{hello world }will be redrawn in was stored when you called the \bold{Write} function. If the screen was black when you chose Write, the screen will become black again, since that is the screen-color state that was stored. \bold{Action 6.}\formatnote{ choose \bold{Quit} }To quit the program, pop-up the menus and from the top card. \bold{Response.} \formatnote{ screen. }The window will disappear from the } \begindata{bp,538929224} \enddata{bp,538929224} \view{bpv,538929224,994,0,0} \section{Overview of the data stream} All Andrew Toolkit applications must store data in files in a datastream format. The datastream is a standard representation for data objects in files. \indexi{Data stream++Format} It consists of a standard header, a object-specific part, and a standard trailer. The header has the format: \\begindata\{\italic{classname},\italic{objid}\} where \italic{classname} is the name of the class, and \italic{objid} is an integer unique within the datastream. A newline following the header is not considered part of the data. The format of the enddata part is similar: \\enddata\{\italic{classname},\italic{objid}\} If either of the strings "\\begindata" or "\\enddata" occur within the object-specific part that is not actually the header for a sub-object, the backslash in front of the string should be escaped with another backslash. In general, escaping all backslashes with a backslash is safe. \subsection{Defining the helloworld data object class} \indexi{Data objects++Defining} \formatnote{ #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; data: long x,y; boolean blackOnWhite; \}; } A major function of the data object class is the abstraction of permanent storage. Subclasses of dataobject can override the \italic{Read} and \italic{Write} methods with procedures to read and write its associated data, enabling any code that deals with dataobjects in general to also deal with this specific class. \paragraph{Writing a data object to a file} \indexi{Data objects++Writing} The \italic{Write} method must write the contents of a dataobject to the given \italic{stdio} stream. The method takes four parameters. First, a pointer to the data object, and a file to be written to. Second, the \italic{writeId} parameter, which is a timestamp used to make sure we are not rewriting the same thing we just wrote. Therefore, we write only if the writeID is different than the stored copy of the last writeID. The \italic{level} parameter indicates whether or not this is the "toplevel" object being written (i.e., not a subobject within another object). An object is the toplevel object if the parameter is zero; thus, an object that is writing out subobjects should call their Write methods with a non-zero level parameter. This distinction between toplevel and subobjects is important especially if an object has both a datastream and a non-datastream representation (e.g., text -- see Example 14). If an object is not the toplevel object, it \italic{must} write itself out in datastream format, as it cannot make any assumptions about whether the objects writing it out can handle either format. Finally, the Write method should return its object's \italic{UniqueID}, which is the uniqueId for the object/file itself can be used to distinguish helloworld from other objects. The following is the Write method for helloworld: \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); fprintf(file,"\\\\enddata\{%s,%d\}\\n", class_GetTypeName(hw), helloworld_UniqueID(hw)); \} return helloworld_UniqueID(hw); \} } Helloworld's Write method only writes in the datastream format. The method first checks to make sure the Write is not a duplicate by looking at the last writeId, and then writes to \italic{file} the \italic{begindata} header, the 3 numbers that represent the \italic{helloworld} state (i.e. the numbers corresponding to x, y, and blackOnWhite), and the \italic{enddata} trailer. The class_GetTypeName calls gets the type for helloworld, and helloworld_UniqueID gets the uniqueID for helloworld. \paragraph{Reading a data object from a file} \indexi{Data objects++Reading} The \italic{Read} method takes a \italic{stdio} file pointer and an \italic{id}. The \italic{id} is either zero, indicating that no data stream header was found, or an integer guaranteed to be unique within the data stream. In the case that the \italic{id} is zero, some dataobjects may try and to read non-datastream formats, like a text object reading a normal text file or a raster object reading a graphics file; it probably should not try to read the \italic{enddata} either. Otherwise the stream is assumed to be one written by the object's Write routine, with the file pointer right after the \\begindata\{...\}. No version checking is done, although a particular object may implement its own. The following is helloworld's Read method: \formatnote{ long helloworld__Read(hw,file,id) struct helloworld *hw; FILE *file; long id; \{ char buf[100]; helloworld_SetID(hw,helloworld_UniqueID(hw)); if(fgets(buf,sizeof(buf),file)==NULL) return dataobject_PREMATUREEOF; /* the %hd tells sscanf that blackOnWhite is a short, not an int */ if(sscanf(buf,"%d %d %hd\\n",&hw->x,&hw->y,&hw->blackOnWhite)<3) return dataobject_BADFORMAT; if(fgets(buf,sizeof(buf),file)==NULL) /* read in the \\enddata\{...\} */ return dataobject_MISSINGENDDATAMARKER; return dataobject_NOREADERROR; \} } First, we set the ID for the read process by calling SetID with a pointer to the object and its uniqueID. The variable buf is used to store the text strings as they are read. The \italic{helloworld_Read} method tries to read two lines -- the object-specific data, and the enddata trailer. If it cannot read all the object-specific data, it returns a premature \smaller{EOF} error or a bad format error; if it cannot read the \italic{enddata}, it returns a missing-enddata error. \subsection{The view I/O interface} \indexi{View++I/O interface} \indexi{Data objects++View I/O interface} The \italic{helloworldview} class has two procedures that are on the \italic{helloworldview} menus -- Write and Read -- which call the appropriate procedure to write or read the \italic{helloworld} data object associated with the view. These procedures use the message string input facilities discussed in Example 9 to read in a filename from the user. \indexi{Messages++Displaying} \indexi{Messages++Questions} \paragraph{Writing} \formatnote{ static void writeHW(hwv,key) struct helloworldview *hwv; long key; \{ 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); \} \} } This procedure simply prompts the user for the filename, opens a file, calls the \italic{helloworld_Write} procedure on its dataobject, and closes the file. \paragraph{Reading} \formatnote{ static void readHW(hwv,key) struct helloworldview *hwv; long key; \{ 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\{ helloworld_Read(hw,fp,id); fclose(fp); helloworld_NotifyObservers(hw,0); \} \} \} \} \} } The readHW procedure is slightly more complicated than the writeHW procedure. First, it checks to see if the file requested exists. Second, it parses the first line of the file it opens to make sure that it is both a valid \italic{begindata} marker, and that the class is really a \italic{helloworld} dataobject. In all these cases, if the check comes back with an error, an appropriate error message is displayed in the program window to inform the user. If all the checks clear, the procedure finally calls the \italic{helloworld_Read} method with the \italic{id} from the \italic{begindata} header. \begindata{bp,538928968} \enddata{bp,538928968} \view{bpv,538928968,995,0,0} \paragraph{Binding the functions} \indexi{Bind} \formatnote{ static struct bind_Description helloworldviewBindings[]=\{ \{"helloworld-center", "\\003",0, "Hello World,Center",0, Center, "Center the helloworld string."\}, \{"helloworld-invert", "\\011",0, "Hello World,Invert",0, Invert, "Invert the helloworld string."\}, \{"helloworld-relocate", "\\022",0, "Hello World,Relocate",0, relocate, "Relocate the helloworld string."\}, \bold{\{"helloworld-read", NULL,0, "Hello World,Read",0, readHW, "Read in a new hello world."\}, \{"helloworld-write", NULL,0, "Hello World,Write",0, writeHW, "Write out the current hello world to a file."\}}, NULL \}; } The bold face section is added to bind the \italic{readHW} and \italic{writeHW} functions for the program. Note that these are menu commands only; there are no key bindings for the functions. The remainder of the program files and the Makefile and compilation methods are the same as in the previous example. \begindata{bp,538929288} \enddata{bp,538929288} \view{bpv,538929288,996,0,0} \begindata{texttag,539315976} \textdsversion{12} ExElevenListing\ \enddata{texttag,539315976} \view{texttagv,539315976,997,0,0} \section{Program listing for Example 11} \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; data: long x,y; boolean blackOnWhite; \}; \bold{\bold{\bold{\bold{hello.c}}}} #include <stdio.h> #include <class.h> #include "hello.eh" boolean helloworld__InitializeObject(classID,hw) struct classheader *classID; struct helloworld *hw; \{ hw->x=POSUNDEF; hw->y=POSUNDEF; hw->blackOnWhite=TRUE; return TRUE; \} long helloworld__Read(hw,file,id) struct helloworld *hw; FILE *file; long id; \{ char buf[100]; helloworld_SetID(hw,helloworld_UniqueID(hw)); if(fgets(buf,sizeof(buf),file)==NULL) return dataobject_PREMATUREEOF; /* the %hd tells scanf that blackOnWhite is a short, not an int */ if(sscanf(buf,"%d %d %hd\\n",&hw->x,&hw->y,&hw->blackOnWhite)<3) return dataobject_BADFORMAT; if(fgets(buf,sizeof(buf),file)==NULL) /* read in the \\enddata\{...\} */ return dataobject_MISSINGENDDATAMARKER; return dataobject_NOREADERROR; \} 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); fprintf(file,"\\\\enddata\{%s,%d\}\\n", class_GetTypeName(hw), helloworld_UniqueID(hw)); \} return helloworld_UniqueID(hw); \} \bold{hellov.ch} class helloworldview[hellov]: view \{ overrides: 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(); GetApplicationLayer() returns struct view *; DeleteApplicationLayer(struct view *v); GetInterface(int type) returns char *; classprocedures: InitializeClass() returns boolean; data: struct keystate *keystate; struct menulist *menulist; boolean haveInputFocus; boolean HaveDownTransition; long hitX,hitY; long x,y; boolean blackOnWhite; long frameX, frameY; long newFrameX, newFrameY; int vrWidth,vrHeight; \}; \bold{hellov.c} #include <stdio.h> #include <class.h> #include "hellov.eh" #include "graphic.ih" #include "rect.h" #include "keymap.ih" #include "keystate.ih" #include "menulist.ih" #include "scroll.ih" #include "bind.ih" #include "message.ih" #include "im.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 = hwv->newFrameY = 0; return TRUE; \} struct view *helloworldview__GetApplicationLayer(hwv) struct textview *hwv; \{ return (struct view *)scroll_Create(hwv,scroll_LEFT+scroll_BOTTOM); \} void helloworldview__DeleteApplicationLayer(hwv,scrollbar) struct textview *hwv; struct scroll *scrollbar; \{ scroll_Destroy(scrollbar); \} 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; 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 / 2; hw->y = hwv->frameY + hwv->vrHeight / 2; \} hwv->x=hw->x; hwv->y=hw->y; hwv->blackOnWhite=hw->blackOnWhite; helloworldview_SetTransferMode(hwv, graphic_COPY); if (hw->blackOnWhite) helloworldview_FillRect(hwv, &myVisualRect, helloworldview_WhitePattern(hwv)); else helloworldview_FillRect(hwv, &myVisualRect, helloworldview_BlackPattern(hwv)); helloworldview_SetTransferMode(hwv,graphic_INVERT); helloworldview_MoveTo(hwv, hw->x - hwv->frameX, hw->y - hwv->frameY); helloworldview_DrawString(hwv,"hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); \} void helloworldview__Update(hwv) struct helloworldview *hwv; \{ struct helloworld *hw=(struct helloworld *)hwv>header.view.dataobject; helloworldview_SetTransferMode(hwv, graphic_INVERT); if (hw->blackOnWhite!=hwv->blackOnWhite) \{ struct rectangle vr; helloworldview_GetVisualBounds(hwv,&vr); helloworldview_FillRect(hwv,&vr, helloworldview_BlackPattern(hwv)); hwv->blackOnWhite=hw->blackOnWhite; \} if (hwv->x!=hw->x || hwv->y!=hw->y || hwv->frameX!=hwv->newFrameX || hwv->frameY!=hwv->newFrameY) \{ 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); \} helloworldview_MoveTo(hwv, hwv->x-hwv->frameX, hwv->y-hwv->frameY); helloworldview_DrawString(hwv, "hello world", if(sscanf(header,"\\\\begindata\{%[^,],%d\}\\n", name,&id)!=2)\{ graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); hwv->x=hw->x; hwv->y=hw->y; hwv->frameX = hwv->newFrameX; hwv->frameY = hwv->newFrameY; helloworldview_MoveTo(hwv, hwv->x-hwv->frameX,hwv->y-hwv->frameY); helloworldview_DrawString(hwv, "hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); \} \} 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) 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[]=\{ \{"helloworld-center", "\\003",0, "Hello World,Center",0,0, Center, "Center the helloworld string."\}, \{"helloworld-invert", "\\011",0, "Hello World,Invert",0,0, Invert, "Invert the helloworld string."\}, \{"helloworld-relocate", "\\022",0, "Hello World,Relocate",0,0, relocate, "Relocate the helloworld string."\}, \{"helloworld-read", NULL,0, "Hello World,Read",0,0, readHW, "Read in a new hello world."\}, \{"helloworld-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 "im.ih" #include "frame.ih" #include "hello.ih" #include "hellov.ih" boolean helloworldapp__Start(hwapp) struct helloworldapp *hwapp; \{ struct helloworld *hw; struct helloworldview *hwv; struct view *applayer; struct frame *frame; struct im *im; if(!super_Start(hwapp)) return FALSE; hw=helloworld_New(); if(hw==NULL) return FALSE; hwv=helloworldview_New(); if(hwv==NULL) \{ helloworld_Destroy(hw); return FALSE; \} applayer=helloworldview_GetApplicationLayer(hwv); if(applayer==NULL) \{ helloworldview_Destroy(hwv); return FALSE; \} frame=frame_New(); if(frame==NULL) \{ helloworldview_DeleteApplicationLayer(hwv,applayer); return FALSE; \} im=im_Create(NULL); if(im==NULL) \{ frame_Destroy(frame); return FALSE; \} helloworldview_SetDataObject(hwv,hw); frame_SetView(frame,applayer); im_SetView(im,frame); helloworldview_WantInputFocus(hwv,hwv); return TRUE; \} \bold{Makefile} SRCDIR=/usr/andrew/ INCLUDES= -I. -I$\{SRCDIR\}include/atk -I$\{SRCDIR\}include INCLUDESRC = $\{SRCDIR\}include/atk CC=cc DEBUG = -g TOOLS = $\{SRCDIR\}bin/ CFLAGS= $\{DEBUG\} $\{INCLUDES\} CLASSFLAGS=$\{INCLUDES\} MAKEDO = $\{TOOLS\}makedo $\{DEBUG\} -b $\{TOOLS\} -d $\{SRCDIR\}lib CLASS = $\{TOOLS\}class .SUFFIXES: .ih .eh .ch .do .ch.ih: $\{CLASS\} $\{CLASSFLAGS\} $*.ch .ch.eh: $\{CLASS\} $\{CLASSFLAGS\} $*.ch .o.do: $\{MAKEDO\} $< all: helloa.do 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,999,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,538487060}