\begindata{text,538947568} \textdsversion{12} \template{default} \define{global } \define{itemize } \formatnote{\chapter{Example 6: Scroll bars}} \indexi{Scroll bars} Example 6 illustrates how to create an object that has scroll bars. A view may not have been allocated enough screen space to be able to display its entire area. Scroll bars allow users to change the portion of the view that is visible on the screen. The Andrew Toolkit contains a class, \italic{scroll}, that provides bars for scrolling a view vertically and horizontally. \indexi{Scroll} \indexi{Class++Scroll} The example program in this section will build upon the program in Example 5. The program will have the same menu and keyboard functionality as Example 5, but will present the \italic{helloworldview} object with vertical and horizontal scroll bars. The discussion that follows presents a step-by-step description of how to modify the \italic{helloworldview} class in Example 5 to produce Example 6. If you were to follow the steps, you would produce a program, called \italic{helloworldapp}, in five files: \itemize{ a hellov.ch file -- will contain the class definition for helloworldview. It will be exactly the same as Example 5, except it will override view's GetInterface method, the method that the scroll and helloworldview objects will use to communicate about the interface. a hellov.c file -- will contain statements that import Andrew Toolkit classes and define the object's methods and class procedures. We will add methods for communicating with the scroll bar object. a helloa.ch file -- will contain the class definition for the application program that will display an instance of the class in a window. This will be exactly the same as in Example 5. 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. We will include procedures to create the scroll bar, and modify the SetView call so that the scroll bar will be linked into the view tree. Makefile -- will contain the directions for compiling, linking and loading. This will be exactly the same as Example 5. } For a complete listing of these files, see \italic{Program listing for example 6} at the end of this section on p. \begindata{textref,539314952} \textdsversion{12} # ExSixListing\ \enddata{textref,539314952} \view{textrefv,539314952,897,0,0}. The source code is available in the directory /usr/andrew/examples/ex6. Although the discussion of the steps refers directly to this example, the information generally applies to the creation of any class that will use scroll bars. \begindata{bp,538929416} \enddata{bp,538929416} \view{bpv,538929416,898,0,0} \section{Running the example program} \formatnote{\formatnote{To run the program, at the \bold{command }prompt type \example{runapp /usr/andrew/examples/ex6/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 and a scroll bar on the left and on the bottom of the window as in Figure \begindata{textref,539315976} \textdsversion{12} ex6\ \enddata{textref,539315976} \view{textrefv,539315976,899,0,0}. Menu and key functionality is the same as in Example 5. \bold{Action 2.}\formatnote{ }Drag one of the scroll bar elevators (the white portion of the scroll bar) up or down using the mouse. \bold{Response. }\formatnote{ }The string \italic{hello world} will be drawn in the window corresponding to the elevator movement, or the string will disappear from the window completely if it is moved beyond the range of the visual rectangle. } \begindata{bp,538929736} \enddata{bp,538929736} \view{bpv,538929736,900,0,0} \section{Overview of the scroll bar}\indexi{Scroll bars} The scroll bar represents the entire view. The striped area at the top of the scroll bar column represents the beginning of the view; the striped area at the bottom, the end of the view. (If a window is very short, the striped areas will not appear in the scroll bar.) The white bar represents that portion of the view currently visible in the window and its location in the view. If the view is very long, the white bar will be small, because only a small portion of the view will be displayed in the window. If the view is very short, the white bar will run the length of the scroll bar, indicating that the entire view is visible. If a user adds or removes information in a window, the white bar will change size to reflect the changes. The black box inside the scroll bar indicates the location of some object of interest, for example, the text caret. In this example, it will indicate the location of the \italic{hello world} string. More technically, the scroll bar allows the user to change the mapping between the coordinate system that the view uses internally and the visible portion on the screen. This mapping can be done in two dimensions--vertical and horizontal, as in the \italic{helloworldview} example. The scroll bar package queries the view for information it needs in order to represent the view. The scroll bar will get the following information from the view: \itemize{ the view's range--the range that the entire scroll bar will represent, i.e., the vertical or horizontal dimensions of the view. \indexi{Scroll bars++View range} the view's visible range--the range that the elevator will represent, i.e., the range of the view that is visible in the area allocated to its display. \indexi{Scroll bars++View visible range} the view's dot--the range that the scroll bar's black rectangle should represent. \indexi{Scroll bars++Dot} } In addition, the scroll bar controls the view by calling a procedure that set the view's frame, i.e., gives directions to the view to move its display in response to the user's manipulation of the scroll bar. The view must provide the procedures for allowing the scroll bar to get information from the view and to set the view's frame. \indexi{Frame} \indexi{Scroll bar++Frame} In the view tree hierarchy, the scroll bar is the parent of the view it is scrolling. In this example, the \italic{im} view is the parent of the \italic{scroll} which is the parent of the \italic{helloworldview} object. \indexi{Scroll bar++View tree hierarchy} \begindata{bp,538271624} \enddata{bp,538271624} \view{bpv,538271624,901,0,0} \section{Modifying the class definition} \subsection{Declaring the class} If you are creating a subclass of view that will be scrollable, you must override view's \italic{GetInterface} \indexi{ \italic{GetInterface}} \indexi{ \italic{view_GetInterface}} \indexi{Methods++\italic{GetInterface}} method. This method provides a way for the scroll bar to communicate with the view about user's scrolling requests. In addition, you will want to add at least two new elements to the data structure. The first must hold the (x,y) offset from the top left of the view's displayed image to the top left of the view's visual rectangle. A second must hold new (x,y) offset pending an update. The following is the new class declaration for the example class \italic{helloworldview}: \formatnote{ 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(); \bold{GetInterface(int type) returns char *;} classprocedures: InitializeClass() returns boolean; data: struct keystate *keystate; struct menulist *menulist; boolean HaveDownTransition; boolean haveInputFocus; long x,y; long distX,distY; boolean blackOnWhite; long newX,newY; boolean newBlackOnWhite; \bold{long frameX, frameY; long newFrameX, newFrameY; int vrWidth,vrHeight;} \}; } Example 6's \italic{helloworldview} overrides view's \italic{GetInterface} method. It will store the (x,y) offset in (\italic{frameX}, \italic{frameY}) and the new offset pending an update in (\italic{newFrameX}, \italic{newFrameY}). Finally, \italic{vrWidth} and \italic{vrHeight} will hold the width and height of \italic{helloworldview}'s visual rectangle, simply to avoid the need to recalculate them. \begindata{bp,538928968} \enddata{bp,538928968} \view{bpv,538928968,902,0,0} \subsection{The scroll bar interface} \indexi{Scroll bars++Interface} The scroll bar interface consists of a set of four function pointers that the scroll bar will call in order to get information about the view that it is scrolling and to control the view. The interface is defined by a structure \italic{scrollfns} (scroll functions )that contains four "function" pointers: \indexi{Scroll bars++Scroll functions} \itemize{ a pointer to a routine that returns state information about the view \italic{SetFrame}--a pointer to a routine that positions the view according to parameters specified by the scrollbar \indexi{ \italic{SetFrame}} \italic{EndZone}--a pointer to a routine that positions the view when the user clicks in the end zones of the scrollbar. \indexi{ \italic{EndZone}} \italic{WhatIsAt}--a pointer to a routine that returns information about position \indexi{ \italic{WhatIsAt}} } For each axis (vertical or horizontal) that you want to be scrollable, you must declare a scroll bar interface structure and define a set of four interface routines. The names of the routines are arbitrary, but the parameters and actions that they perform are not. For example, the following declares a scroll bar interface for \italic{helloworldview} from hellov.c: \formatnote{ 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 \}; } In the \italic{helloworldview} object, the routines pointed to by \italic{horizInterface} are used to maintain the horizontal scrollbar, and \italic{vertInterface}, the vertical one. \paragraph{The interface routine for getting state information} \indexi{Scroll bars++State information} \indexi{ \italic{Getinfo}} The scroll bar will call a view's getinfo routine when it needs to know about the state of the view. Your \italic{getinfo} routine should set the state information for the ranges \italic{total}, \italic{seen}, and \italic{dot.} Each of these consists of two fields: \italic{beg}, the beginning of the range, and \italic{end}, its end. A general structure looks as follows: \formatnote{ static void getinfo(v, total, seen, dot) struct view *v; struct range *total, *seen, *dot; } \itemize{ \italic{Total} is the the range that the entire scrollbar represents. You should set the beginning of the range to 0 and end of the range to the width or height of the entire view. \italic{Seen} is the range that the elevator represents. You should set the beginning of the range to the view's top-left visual coordinates; the end to the bottom-right visual coordinates. \italic{Dot} is the range that the black rectangle represents, usually something that is selected, or the point of current interest. } In this example program, \italic{helloworldview} provides two routines that provide state information, \italic{xgetinfo} for the horizontal scroll bar and \italic{ygetinfo} for the vertical. The names of the routines are arbitrary, but they must correspond to the names declared in the scroll bar interface declaration. \formatnote{ static void xgetinfo(hwv, total, seen, dot) struct helloworldview *hwv; struct range *total, *seen, *dot; \{ total->beg = 0; total->end = TOTALSIZE; seen->beg = hwv->frameX; seen->end = hwv->frameX + hwv->vrWidth; dot->beg = dot->end = hwv->x; \} static void ygetinfo(hwv, total, seen, dot) struct helloworldview *hwv; struct range *total, *seen, *dot; \{ total->beg = 0; total->end = TOTALSIZE; seen->beg = hwv->frameY; seen->end = hwv->frameY + hwv->vrHeight; dot->beg = dot->end = hwv->y; \} } In \italic{xgetinfo}, the statements \italic{total->beg = 0} and \italic{total->end = \smaller{TOTALSIZE}} sets the range total to the range of the helloworldview coordinate space (which in this example is fixed, ranging from 0 to \smaller{TOTALSIZE}). The statements \italic{seen->beg = hwv->frameX} and \italic{seen->end = hwv->frameX + hwv->vrWidth} sets the range seen to \italic{helloworldview}'s visual rectangle, the portion displayed on the screen from the edge in (frameX, frameY) extending for the (width, height) of the view. The statement \italic{dot->beg = dot->end = hwv->x} simply sets the range dot to the position of the \italic{hello world} string within total; because it has zero size, the black marker in the scrollbar will simply reflect the position (not the size) of the \italic{hello world} string. The routine \italic{ygetinfo} is analogous to \italic{xgetinfo}. \begindata{bp,538929224} \enddata{bp,538929224} \view{bpv,538929224,903,0,0} \paragraph{Setting the view's frame upon user's scrolling requests} The setframe routine gets called whenever scrolling occurs, and should set the view in its frame so that it's position reflects the new position of the scrollbar. It tells \italic{view} to move its display (and remember, this should all happen only in the particular axis which is being scrolled by this routine) so that \italic{position}, in its own coordinate system (its \italic{total} range, as given to the\italic{ getinfo} call), is located at a pixel offset of \italic{coordinate}; \italic{outof} is the total number of pixels in this axis of the view, so \italic{coordinate}/\italic{outof} is the fraction from the edge of \italic{view} where the \italic{position}'s coordinate should be moved to. \indexi{Scroll bars++Frame} \indexi{ \italic{SetFrame}} The following is the general structure of the \italic{setframe} routine: \formatnote{ static void setframe(v, posn, coord, outof) struct view *v; int posn; long coord, outof; } \italic{helloworldview} provides two routines that set the frame, \italic{xsetframe} for the horizontal scroll bar and \italic{ysetframe} for the vertical. The names of the routines are arbitrary, but they must correspond to the names declared in the scroll bar interface declaration. \formatnote{ static void xsetframe(hwv, posn, coord, outof) struct helloworldview *hwv; int posn; long coord, outof; \{ hwv->newFrameX = posn - hwv->vrWidth * coord / 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, coord, outof) struct helloworldview *hwv; int posn; long coord, outof; \{ hwv->newFrameY = posn - hwv->vrHeight * coord / outof; if (hwv->newFrameY + hwv->vrHeight > TOTALSIZE) hwv->newFrameY = TOTALSIZE - hwv->vrHeight; else if (hwv->newFrameY < 0) hwv->newFrameY = 0; helloworldview_WantUpdate(hwv, hwv); \} } In \italic{xsetframe}, the statements \italic{hwv->newFrameX = posn hwv->vrWidth * coord / outof} positions point \italic{posn} at \italic{coord}/\italic{outof} from the top, left edge of the view. The \italic{if/else if} statement simply checks that the resulting position is within \italic{hwv}'s range. The routine \italic{ysetframe} is analogous. \paragraph{A routine to manage endzone hits} \indexi{Scroll bars++Endzone hits} \indexi{ \italic{Endzone}} The following is the general structure of the endzone routine: \formatnote{ static void endzone(v, zone, action) struct view *v; int zone; enum view_MouseAction action; } The endzone routine gets called when the user clicks in the scrollbar endzone; \italic{zone} is either \smaller{scroll_TOPENDZONE} or \smaller{scroll_BOTTOMENDZONE}, for the top or bottom zone, and \italic{action} is how the user clicked (enum view_MouseAction, see View, Vol. 2). If the pointer to this procedure in the \italic{scrollfns} structure is \smaller{NULL}, this procedure defaults to scrolling the view to the beginning/end. This is the case in the \italic{helloworldview} example, therefore the routine is not included explicitly. \paragraph{Finding the coordinates of the string} \indexi{Scroll bars++String coordinates} \indexi{ \italic{Whatisat}} \formatnote{ static long whatisat(v, coord, outof) struct view *v; long coord; long outof; } This routine should return the coordinate (in the total range of the particular axis of \italic{view}), of what is located at the \italic{coordinate}'s pixel (out of \italic{outof} total pixels). In the helloworldview view, we define two sets of routines to correspond to these, one for each axis, to give to the scrolling code. \formatnote{ static long xwhat(hwv, coord, outof) struct helloworldview *hwv; long coord, outof; \{ return hwv->frameX + hwv->vrWidth * coord / outof; \} static long ywhat(hwv, coord, outof) struct helloworldview *hwv; long coord, outof; \{ return hwv->frameY + hwv->vrHeight * coord / outof; \} } In \italic{xwhat}, the statement \italic{return hwv->frameX + hwv>vrWidth * coord / outof} returns what's coord/outof from the edge of the view, \italic{hwv}. The routine \italic{ywhat} is analogous. \begindata{bp,538929288} \enddata{bp,538929288} \view{bpv,538929288,904,0,0} \subsection{Initializing the helloworldview object} \indexi{Objects++Initializing} \indexi{ \italic{InitializeObject}} \formatnote{ boolean helloworldview__InitializeObject(classID, hwv) struct classheader *classID; struct helloworldview *hwv; \{ hwv->x = POSUNDEF; hwv->y = POSUNDEF; hwv->HaveDownTransition = FALSE; hwv->haveInputFocus = FALSE; hwv->keystate = keystate_Create(hwv, helloworldviewKeymap); hwv->blackOnWhite = TRUE; hwv->newBlackOnWhite = TRUE; hwv->menulist = menulist_DuplicateML(helloworldviewMenulist, hwv); \bold{hwv->newFrameX = hwv->newFrameY = 0;} return TRUE; } The \italic{InitializeObject} procedure is exactly the same as Example5, except that\italic{ frameX} and \italic{frameY} must be initialized also. They are initialized to 0, so that the \italic{helloworldview} will initially display the top, leftmost corner of \italic{helloworldview}'s coordinate system. \subsection{Displaying the 'hello world' string} The major difference in displaying the \italic{hello world} string from the previous example is that the position of the string within the view can now change without the the string's position in its own coordinate space changing. We calculate the screen position, which is the difference between the internal position of the string and the offset of the the edge of the view from the origin of the internal coordinate system, and display the string. It is not necessary to make sure the string is actually visible, because a view's output is automatically clipped to the boundary of the rectangle allocated for it. \paragraph{The full update procedure} \indexi{Full update} \formatnote{ 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 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 (hwv->x == POSUNDEF) \{ hwv->x = hwv->frameX + hwv->vrWidth / 2; hwv->y = hwv->frameY + hwv->vrHeight / 2; hwv->newX = hwv->x; hwv->newY = hwv->y; \} else \{ hwv->x = hwv->newX; hwv->y = hwv->newY; \} helloworldview_SetTransferMode(hwv, graphic_COPY); if (hwv->blackOnWhite) \{ helloworldview_FillRect(hwv, &myVisualRect, helloworldview_WhitePattern(hwv)); \} else \{ helloworldview_FillRect(hwv, &myVisualRect, helloworldview_BlackPattern(hwv)); \} helloworldview_SetTransferMode(hwv, graphic_INVERT); helloworldview_MoveTo(hwv, hwv->x - hwv->frameX, hwv->y - hwv>frameY); helloworldview_DrawString(hwv,"hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); \} } The first set of tests make sure that the viewed portion of the \italic{helloworldview} space does not extend past its edge, and if it does, adjusts the left/top boundary so that the right/bottom edge of the view corresponds exactly with the right/bottom edge of the helloworldview space. Thus, there is never a question of a negative (newFrameX, newFrameY) even if the TOTALSIZE is smaller than the visual rectangle. The check of \italic{hwv->x} against \smaller{POSUNDEF} centers the string within the currently viewed portion, and the \italic{MoveTo} before the actual display of the string takes the coordinate mapping into account, as described above. \paragraph{The update procedure} \indexi{Update} The changes to the \italic{Update} method are essentially the same as those to the \italic{FullUpdate} method. It must also test whether (frameX, frameY) have changed, and update if this has happened. \formatnote{ void helloworldview__Update(hwv) struct helloworldview *hwv; \{ if (hwv->newBlackOnWhite != hwv->blackOnWhite) \{ struct rectangle vr; helloworldview_GetVisualBounds(hwv,&vr); helloworldview_FillRect(hwv, &vr, helloworldview_BlackPattern(hwv)); hwv->blackOnWhite = hwv->newBlackOnWhite; \} \bold{if (hwv->newX != hwv->x || hwv->frameX != hwv->newFrameX || hwv->newY != hwv->y || hwv->frameY != hwv->newFrameY) }\{ helloworldview_MoveTo(hwv, hwv->x - hwv->frameX, hwv->y - hwv>frameY); helloworldview_DrawString(hwv, "hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); hwv->x = hwv->newX; hwv->y = hwv->newY; \bold{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); \} \} } \subsection{Getting the scroll bar interface} \indexi{Scroll bar++Interface} \indexi{ \italic{GetInterface}} \indexi{ \italic{view_GetInterface}} The GetInterface method is what the scrollbar calls to get the pointers to the above scroll bar interface routines. As described in the beginning, we have already packed them into two \italic{scrollfns} structures. We just have to check what is being asked for (which is specified by the \italic{type} string), and return it when appropriate. \formatnote{ 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; \} } \section{Creating the stand-alone program} \subsection{Importing Andrew Toolkit classes} \indexi{Importing} \formatnote{ #include <class.h> #include "hellov.eh" #include "graphic.ih" #include "rect.h" #include "keymap.ih" #include "keystate.ih" #include "menulist.ih" #include "bind.ih" \bold{#include "scroll.ih"} } \italic{scroll.ih} is also included to import the structures used to interface with the scrolling package. \subsection{Creating an instance of a scroll} \indexi{Scroll bars++Creating an instance} \indexi{ \italic{Start}} \formatnote{ #include <class.h> #include "helloa.eh" #include "im.ih" \bold{#include "scroll.ih"} #include "hellov.ih" boolean helloworldapp__Start(hwapp) struct helloworldapp *hwapp; \{ struct helloworldview *hwv; struct im *im; \bold{struct scroll *scroll;} if(!super_Start(hwapp)) return FALSE; hwv=helloworldview_New(); if(hwv==NULL) return FALSE; \bold{scroll=scroll_Create(hwv,scroll_LEFT|scroll_BOTTOM); if(scroll==NULL)\{ helloworldview_Destroy(hwv); return FALSE; \}} im=im_Create(NULL); if(im==NULL)\{ helloworldview_Destroy(hwv); scroll_Destroy(scroll); return FALSE; \} \bold{im_SetView(im,scroll);} helloworldview_WantInputFocus(hwv,hwv); return TRUE; \} } In addition to the helloworldview and im structures, we must declare the scroll bar structure, with \italic{struct scroll *scroll}. Then, after we create the new view, we create the scroll bar. The scroll_Create \indexi{ \italic{scroll_Create}} procedure creates a scroll object for the view, \italic{hwv.} The object has two scroll bars, one on the left and one at the bottom. In addition, it makes \italic{scroll} the parent of \italic{hwv}. After the scroll bar has been created, we can create the im associated with this application. Finally, \italic{im_SetView (im, scroll)} \indexi{Scroll bars++View tree hierarchy} sets \italic{scroll} in the view tree with \italic{im} as its parent. Thus, \italic{im} is the parent of \italic{scroll} which is the parent of \italic{hwv}. Each creation method (e.g. helloworldview_New, scroll_Create) tests the object to be created against NULL, so that if the Interaction Manager did not succeed in creating that object, all previously created objects are explicitly destroyed with the appropriate Destroy method, and a FALSE is returned to alert the failure. This check is not absolutely necessary, but it is a useful construction for complex codes involving large numbers of objects. \begindata{bp,538929032} \enddata{bp,538929032} \view{bpv,538929032,905,0,0} \begindata{texttag,539236104} \textdsversion{12} ExSixListing\ \enddata{texttag,539236104} \view{texttagv,539236104,906,0,0} \section{Program listing for Example 6} \formatnote{ \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(); GetInterface(int type) returns char *; classprocedures: InitializeClass() returns boolean; data: struct keystate *keystate; struct menulist *menulist; boolean HaveDownTransition; boolean haveInputFocus; long x,y; long distX,distY; boolean blackOnWhite; long newX,newY; boolean newBlackOnWhite; long frameX, frameY; long newFrameX, newFrameY; int vrWidth,vrHeight; \}; \bold{hellov.c} #include <class.h> #include "hellov.eh" #include "graphic.ih" #include "rect.h" #include "keymap.ih" #include "keystate.ih" #include "menulist.ih" #include "bind.ih" #include "scroll.ih" #define POSUNDEF -1 #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->x = POSUNDEF; hwv->y = POSUNDEF; hwv->HaveDownTransition = FALSE; hwv->haveInputFocus = FALSE; hwv->keystate = keystate_Create(hwv, helloworldviewKeymap); hwv->blackOnWhite = TRUE; hwv->newBlackOnWhite = TRUE; hwv->menulist = menulist_DuplicateML(helloworldviewMenulist, hwv); hwv->newFrameX = hwv->newFrameY = 0; return TRUE; \} 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 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 (hwv->x == POSUNDEF) \{ hwv->x = hwv->frameX + hwv->vrWidth / 2; hwv->y = hwv->frameY + hwv->vrHeight / 2; hwv->newX = hwv->x; hwv->newY = hwv->y; \} else \{ hwv->x = hwv->newX; hwv->y = hwv->newY; \} helloworldview_SetTransferMode(hwv, graphic_COPY); if (hwv->blackOnWhite) \{ helloworldview_FillRect(hwv, &myVisualRect, helloworldview_WhitePattern(hwv)); \} else \{ helloworldview_FillRect(hwv, &myVisualRect, helloworldview_BlackPattern(hwv)); \} helloworldview_SetTransferMode(hwv, graphic_INVERT); helloworldview_MoveTo(hwv, hwv->x - hwv->frameX, hwv->y - hwv>frameY); helloworldview_DrawString(hwv,"hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); \} void helloworldview__Update(hwv) struct helloworldview *hwv; \{ /* TransferMode is graphic_INVERT from the last FullUpdate */ if (hwv->newBlackOnWhite != hwv->blackOnWhite) \{ struct rectangle vr; helloworldview_GetVisualBounds(hwv,&vr); helloworldview_FillRect(hwv, &vr, helloworldview_BlackPattern(hwv)); hwv->blackOnWhite = hwv->newBlackOnWhite; \} if (hwv->newX != hwv->x || hwv->frameX != hwv->newFrameX || hwv->newY != hwv->y || hwv->frameY != hwv->newFrameY) \{ helloworldview_MoveTo(hwv, hwv->x - hwv->frameX, hwv->y - hwv>frameY); helloworldview_DrawString(hwv, "hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); hwv->x = hwv->newX; hwv->y = hwv->newY; 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; \{ if(hwv->HaveDownTransition) switch(action)\{ case view_RightUp: hwv->HaveDownTransition=FALSE; /* fall through */ case view_RightMovement: hwv->newX=x-hwv->distX; hwv->newY=y-hwv->distY; break; case view_LeftUp: hwv->HaveDownTransition=FALSE; hwv->newX=x; hwv->newY=y; break; case view_LeftMovement: /* do nothing */ break; default: /* re-synchronize mouse */ hwv->HaveDownTransition=FALSE; \} if(!hwv->HaveDownTransition) switch(action)\{ case view_RightDown: hwv->distX=x-hwv->x; hwv->distY=y-hwv->y; /* fall through */ case view_LeftDown: hwv->HaveDownTransition=TRUE; helloworldview_WantInputFocus(hwv,hwv); break; \} helloworldview_WantUpdate(hwv,hwv); 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; \{ hwv->newX = hwv->newFrameX + hwv->vrWidth / 2; hwv->newY = hwv->newFrameY + hwv->vrHeight / 2; helloworldview_WantUpdate(hwv, hwv); \} static void Invert(hwv, rock) struct helloworldview *hwv; long rock; \{ hwv->newBlackOnWhite = ! hwv->newBlackOnWhite; helloworldview_WantUpdate(hwv, hwv); \} static void xgetinfo(hwv, total, seen, dot) struct helloworldview *hwv; struct range *total, *seen, *dot; \{ total->beg = 0; total->end = TOTALSIZE; seen->beg = hwv->frameX; seen->end = hwv->frameX + hwv->vrWidth; dot->beg = dot->end = hwv->x; \} static void ygetinfo(hwv, total, seen, dot) struct helloworldview *hwv; struct range *total, *seen, *dot; \{ total->beg = 0; total->end = TOTALSIZE; seen->beg = hwv->frameY; seen->end = hwv->frameY + hwv->vrHeight; dot->beg = dot->end = hwv->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."\}, NULL \}; boolean helloworldview__InitializeClass(classID) struct classheader *classID; \{ helloworldviewMenulist=menulist_New(); helloworldviewKeymap=keymap_New(); bind_BindList(helloworldviewBindings, helloworldviewKeymap,helloworldviewMenulist, &helloworldview_classinfo); return TRUE; \} \begindata{bp,538928712} \enddata{bp,538928712} \view{bpv,538928712,907,0,0} \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 "scroll.ih" #include "hellov.ih" boolean helloworldapp__Start(hwapp) struct helloworldapp *hwapp; \{ struct helloworldview *hwv; struct im *im; struct scroll *scroll; if(!super_Start(hwapp)) return FALSE; hwv=helloworldview_New(); if(hwv==NULL) return FALSE; scroll=scroll_Create(hwv,scroll_LEFT|scroll_BOTTOM); if(scroll==NULL)\{ helloworldview_Destroy(hwv); return FALSE; \} im=im_Create(NULL); if(im==NULL)\{ helloworldview_Destroy(hwv); scroll_Destroy(scroll); return FALSE; \} im_SetView(im,scroll); 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 hellov.do helloa.do: helloa.o helloa.eh hellov.do: hellov.o hellov.eh helloa.o: helloa.c helloa.o: $\{INCLUDESRC\}/app.ih helloa.o: $\{INCLUDESRC\}/graphic.ih helloa.o: $\{INCLUDESRC\}/im.ih helloa.o: $\{INCLUDESRC\}/observe.ih helloa.o: $\{INCLUDESRC\}/point.h helloa.o: $\{INCLUDESRC\}/rect.h helloa.o: $\{INCLUDESRC\}/view.ih helloa.o: $\{SRCDIR\}include/class.h helloa.o: hellov.ih helloa.o: helloa.eh helloa.eh helloa.ih: helloa.ch helloa.eh helloa.ih: $\{INCLUDESRC\}/app.ih hellov.o: hellov.c hellov.o: $\{INCLUDESRC\}/graphic.ih hellov.o: $\{INCLUDESRC\}/observe.ih hellov.o: $\{INCLUDESRC\}/point.h hellov.o: $\{INCLUDESRC\}/rect.h hellov.o: $\{INCLUDESRC\}/view.ih hellov.o: $\{SRCDIR\}include/class.h hellov.o: hellov.eh hellov.eh hellov.ih: hellov.ch hellov.eh hellov.ih: $\{INCLUDESRC\}/graphic.ih hellov.eh hellov.ih: $\{INCLUDESRC\}/observe.ih hellov.eh hellov.ih: $\{INCLUDESRC\}/point.h hellov.eh hellov.ih: $\{INCLUDESRC\}/rect.h hellov.eh hellov.ih: $\{INCLUDESRC\}/view.ih hellov.eh hellov.ih: $\{SRCDIR\}include/class.h } \begindata{bp,537558784} \enddata{bp,537558784} \view{bpv,537558784,909,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 and without fee is hereby granted, provided # that the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of IBM not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. # # THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ANY 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,538947568}