\begindata{text,538945640} \textdsversion{12} \template{default} \define{global } \define{itemize } \formatnote{\chapter{Example 4: input} \indexi{Key bindings} Keyboard input}]} \indexi{Keyboard \indexi{Keymap} \indexi{Keystate} \indexi{Bind} \indexi{Key commands} Example 4 illustrates how to create a program that responds to keyboard input. The Andrew Toolkit provides three classes--\italic{Keymap,} \italic{Keystate} and \italic{Bind}--that together allow objects to respond to input from the keyboard. \indexi{Class++Keymap} \indexi{Class++Keystate} \indexi{Class++Bind} Like Example 3, this program draws \italic{hello world} initially at the center of the window. If the user clicks on the left or the right mouse button, the program draws \italic{hello world} centered at the location of the mouse cursor when the user lets up on the button. In addition, the program responds to two key commands: Ctrl-c and Ctrl-i. If the user presses Ctrl-c, the program centers \italic{hello world} in the window. If the user presses Ctrl-i, the program inverts the screen, from white to black or black to white. The discussion that follows shows how to modify the \italic{helloworldview} class in Example 3 to produce Example 4. If you were to follow along, you would produce a program, called \italic{helloworldapp}, in five files: \itemize{ a hellov.ch file -- will contain the class definition for \italic{helloworldview.} We will modify the class definition from Example 3 to override \italic{View}'s methods for receiving and losing the input focus. general, the In \italic{Interaction Manager} sends keyboard input to the view with the input focus. In addition, we will add a class procedure for initializing \italic{helloworldview}'s keymap and for binding keys to command procedures. Finally, we will add some data to the class for keeping track of whether \italic{helloworldview} has the input focus and whether the screen should be black on white or white on black. a hellov.c file -- will contain statements that import Andrew Toolkit classes and define the object's methods and class procedures. We will define two new methods: The first for receiving the input focus and the second for losing it. We will also define a class procedure for initializing the keymap and for binding keys to command procedures. Finally, we will define the command procedures for centering \italic{hello world} and for inverting the screen. 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 3. 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 add a statement to request the input focus for the view immediately upon start-up. Makefile -- will contain the directions for compiling, linking and loading. This will be exactly the same as Example 3. }For a complete listing of these files, see \italic{Program listing for Example 4 } at the end of this section on p. \begindata{textref,539315976} \textdsversion{12} # ExFourListing\ \enddata{textref,539315976} \view{textrefv,539315976,917,0,0}. The source code is available in the directory /usr/andrew/examples/ex4, together with the compiled program. Although the discussion of the steps refers directly to this example, the information generally applies to the creation of any stand-alone application program that will respond to keyboard input. \section{Running the example program} \formatnote{\bold{Action 1.}\formatnote{ } \formatnote{To run the program, at the \bold{command }prompt type runapp /usr/andrew/examples/ex4/helloa and press the Enter key. } \bold{Response.}\formatnote{ \italic{hello world} }The program will produce a window with centered in the body. \bold{Action 2.}\formatnote{ window, position the }To move \italic{hello world} in the mouse cursor within the program's window, click on the left mouse button, and drag the mouse. \bold{Response. }\formatnote{ wherever you move the } \italic{hello world} will be drawn mouse. \bold{Action 3.}\formatnote{ } \formatnote{@beginTmultiple To use the keyboard to center \italic{hello world} in the center of the window, type \italic{Ctrl-c} } \bold{Response. }\formatnote{ the center of the window. \bold{Action 4.}\formatnote{ } \italic{hello world} will be drawn in }\formatnote{To invert the drawing, type \italic{Ctrl-i} } \bold{Response.}\formatnote{ case, it will change }The background will invert. In this from white to black. \bold{Action 5.}\formatnote{ the window title }To quit the program, move the cursor to bar, pop-up the menus and choose \bold{Zap} from the \italic{This Window} card. \bold{Response.}\formatnote{ screen. }The program will disappear from the } \begindata{bp,538929288} \enddata{bp,538929288} \view{bpv,538929288,918,0,0} \section{Keyboard input concepts} \indexi{Keyboard input++Concepts} If you use the methods that \italic{Keymap,} \italic{Keystate} and \italic{Bind} provide in order to bind a sequence of keys to a procedure, then when the user types that sequence, the \italic{Interaction Manager} invokes the corresponding procedure. For example, if you bind \italic{Ctrl-x Ctrl-s} to a procedure called \italic{Write_Buffer_to_File}, then when the user types \italic{Ctrl-x Ctrl-s}, the \italic{Interaction Manager} invokes the \italic{Write_Buffer_to_File} procedure. Likewise, if you bind the key stroke \italic{a} to \italic{Insert_Character}, then when the user types an \italic{a}, the \italic{Interaction Manager} will invoke the procedure \italic{Insert_Character}. \subsection{Key maps} \indexi{Key maps} The Andrew Toolkit uses key maps for keyboard input. Formally, a key map is a function that maps a sequence of keyboard characters to a procedure. To understand how key maps work, it is useful to think of a key map as an array with 128 entries, one for each \smaller{ASCII} character. Each entry in the key map array may have one of three values or types of bindings: a procedure, another key map, or a special value used to indicate that the character has no binding. \paragraph{Procedure bindings} \indexi{Procedure bindings} \indexi{Key bindings} Whenever the user types a character, the character is used as an index into the key map array. If the array value for the character is a procedure, then the mapping is done and the procedure is executed. For example, suppose the key map \italic{edit_BasicCommands} maps \italic{Ctrl-s} to the procedure \italic{edit_SearchForward}. Then when the user types a \italic{Ctrl-s}, the Interaction Manager maps the key to the procedure \italic{edit_SearchForward}, which it then invokes. \paragraph{Sub-key map bindings } \indexi{Key bindings++Sub-key map} If the array value for the character that a user types is another key map, then the specified key map will be used to map the next character that the user types. For example, suppose that the key map \italic{edit_BasicCommands} maps \italic{Ctrl-x} to another key map, \italic{edit_eXtendedCommands,} and that \italic{Ctrl-s} in the sub-key map \italic{edit_eXtendedCommands} maps to the procedure \italic{edit_WriteBuffertoFile}. Then, when the user types \italic{Ctrl-x}, the key map \italic{edit_eXtendedCommands} will be used to interpret the next character that the user types. If the user types \italic{Ctrl-s}, then that will map to the procedure \italic{edit_WriteBufferToFile}, which will be executed. A key map that itself has sub-key maps defines an implicit tree, where one key map is the root keymap, and the other sub-key maps are branches. As an applications programmer, you do not need to worry about sub-key maps. The Andrew Toolkit defines them automatically when you bind a sequence of keys to a procedure. \paragraph{No bindings} Finally, if the character that the user types indicates there is no binding, then the key map returns that there was no procedure bound to that sequence \italic{Interaction Manager} then ignores the maps to a value that a status code indicating of keys. key. The \subsection{Key states} \indexi{Key states} There is normally only one key map per class since the key map can usually be shared among all instances of a view. A major function of the key state is to associate an instance of a view with the key map for its class. A second function of key states is to keep track of the state of the key mapping. If key maps only mapped single keys to procedures, then there would be no need to keep track of the state of the mapping. To map \italic{sequences} of keys to procedures, however, requires keeping state information. For instance, the mapping process must know which key map is being used to evaluate the next incoming character. \paragraph{Keystate chains} \indexi{Key state chains} In the Andrew ToolKit, an object can contain other objects; for example, a multi-media editor may contain a drawing editor. Since an object may not know what other objects it will contain in advance, there must be a protocol that allows objects to coordinate the key bindings that each object may desire. If the multi-media editor and the drawing editor both want to bind \italic{Ctrl-s}, for instance, there must be a way to decide which binding takes priority. \italic{Keystate chains }establish the priority of menu items. Keystate chains work according to the following protocol: Each object posts the keystates that it wants. If the object is contained in another object, the parent object determines the placement of the child's keystate in the keystate chain. If a keystate ks\italic{\subscript{j }}occurs after the keystate ks\italic{\subscript{i}} on the keystate chain, then ks\italic{\subscript{j}}'s keystate takes precedence over ks\italic{\subscript{i}}'s. Thus, a parent object can decide that its keystate should have precedence over its child's, or that its child's keystate should have precedence. The usual way to decide precedence is based on input focus: the keystate of the object with the input focus should have precedence. \subsection{Flow of control for keyboard input} \indexi{Keyboard input++Flow of control} \indexi{Key bindings++Flow of control} The \italic{Interaction Manager} sends keyboard input to the view with the input focus. A view can request the input focus from the \italic{Interaction Manager}. \indexi{Interaction manager++Keyboard input} Only one view at a time can have the input focus. \indexi{Input focus} Most application programs start up by creating a set of views and asking the \italic{Interaction Manager} to make one of the views the input focus. A user can request a view to make itself the input focus by clicking in its visual rectangle. If a view wishes to respond to such user requests, it should request the \italic{Interaction Manager} to give it the input focus upon receiving the \italic{DownTransition} in its \italic{Hit} method. When the \italic{Interaction Manager} gives a view the input focus, it notifies the view by calling a method, \italic{ReceiveInputFocus}. A view's \italic{ReceiveInputFocus} method must post a \italic{keystate} to its parent. The \italic{keystate} provides the \italic{Interaction Manager} with the information it needs to interpret the user's keyboard input appropriately for the view. If the view is to highlight itself when it has the input focus, its \italic{ReceiveInputFocus} method should also request an \italic{Update} from its parent in order to draw the highlighting. When the \italic{Interaction Manager} takes the input focus away from a view, it notifies the view by calling another method, \italic{LoseInputFocus}. A view's \italic{LoseInputFocus} method should do whatever it needs to do before losing the input focus. For example, if the view needs to de-highlight itself, it should request an update from it parent in order to dehighlight. In this example program, the highlighting and dehighlighting of the view is handled by the Hit procedure, which will be described in more detail later in this chapter. \section{Modifying the class definition} \subsection{Declaring the class} \indexi{Class++Declaring} In order to respond to keyboard input, Example 4 overrides \italic{View}'s \italic{ReceiveInputFocus} and \italic{LoseInputFocus} methods and declares the class procedure, \italic{InitializeClass}. \indexi{ \italic{ReceiveInputFocus}} \indexi{ \italic{LoseInputFocus}} \indexi{ \italic{InitializeClass}} \indexi{Class++Initializing} \indexi{Input focus} If you are creating a subclass of view that will respond to keyboard input, as in the helloworldview example program, you must override two methods: \italic{ReceiveInputFocus} and \italic{LoseInputFocus}. These methods provide a way for the \italic{Interaction Manager} to communicate with a view that it has received the input focus or has lost the input focus. In addition, you should declare a class procedure called \italic{InitializeClass}. \italic{InitializeClass} is called only once-the first time the class is loaded. Thus, it is an appropriate place to do initializations that need to be done for each class rather than for each object in the class. The mapping of keys to command procedures typically needs to be done once for the class rather than for each object in the class, so you should normally do it in \italic{InitializeClass} rather than \italic{InitializeObject}. Finally, you will want to add two new elements to the data structure. The first will be a pointer to the class' \italic{keystate,} the object that provides the \italic{Interaction Manager} with a state-transition machine for interpreting the sequence of keys that the user may type. When the view's \italic{ReceiveInputFocus} method is called, it must post this \italic{keystate} to its parent. The second is a \italic{boolean} to keep track of whether the view has the input focus or not. This will be used to guide the object's \italic{Hit} method about requesting the input focus, and by \italic{Update} and \italic{FullUpdate} methods about what highlighting is appropriate. 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 *; \bold{ReceiveInputFocus(); LoseInputFocus();} classprocedures: InitializeClass()returns boolean; data: \bold{struct keystate *keystate;} long x,y; long distX,distY; long newX,newY; boolean HaveDownTransition; \bold{boolean haveInputFocus; boolean blackOnWhite; boolean newBlackOnWhite;} \}; } The class \italic{helloworldview} will store its \italic{keystate} object in \italic{keystate}. It will use the boolean, \italic{haveInputFocus}, to keep keep track of whether it has the input focus. The booleans \italic{blackOnWhite} and \italic{newBlackOnWhite} will keep track of inverting the display and are specific to Example 4. \subsection{Describing the key bindings} \indexi{Key bindings++Description} \indexi{Bind description} All key bindings for a class must be described in a \italic{bind description}. Binding descriptions contain the following elements: \itemize{ \bold{User invocation name}. In addition to allowing users to invoke a procedure by typing the sequence of keys it is bound to, many applications allow users to invoke a procedure by its name. For example, in the EMACS editor, the user can save the current file by typing \italic{Ctrl-x Ctrls} or by typing \italic{ESC-X write-current-file.} The user invocation name is the name the user types to invoke the procedure, for example, \italic{write-current-file}. Unlike EMACS, the name should begin with the name of the object to avoid collisions among dynamically loaded objects. For example, \italic{helloworldview} uses \italic{helloworld-center} and \italic{helloworld-invert} as its user invocation names. \bold{Key sequence.} This is the \smaller{ASCII} string representation for the sequence of keys that the user will type to invoke the function. For example, Ctrl-x Ctrl-s would be represented as \italic{" \\\\030\\\\023"}. \bold{Key data.} The data, if any, that should be passed to the procedure when the user invokes the procedure through the key sequence. In this case, the data is a "rock," or constant value. \bold{Menu entry.} The menu, if any, that will invoke the procedure. Menus will be discussed in Example 5. \bold{Menu data.} The data, if any, that should be passed to the procedure when the user invokes the procedure through the menu item. In this case, the data is a "rock," or constant value. \bold{Menu mask.} The mask, if any, that controls the circumstances under which the menu item will appear. For example, EZ uses menu masks to control the menus that appear when a region is selected and when there is no selected region. It should be 0 unless you want to use them. \bold{Procedure.} A pointer to the procedure. If the procedure is not given, the module name must be given. The module should supply the procedure. For example, EZ invokes the spelling module without supplying a pointer to the procedure and the spelling module supplies it. \bold{Brief documentation.} A string that gives a brief description of the procedure (something suitable for a command-line description \italic{a la} \smaller{EMACS} \italic{apropos}). For example, \italic{write-currentfile} might have the description "Saves current file." \bold{Module name.} found. The name of the module in which the procedure can be } The description of the key bindings should be stored in a \smaller{NULL} terminated array of struct \italic{bind_Description}. Each entry in the array should be a description of a single key binding. For example, the two key bindings for \italic{helloworldview} are described by the following array, \italic{helloworldviewBindings} in \italic{hellov.c}: \formatnote{static struct bind_Description helloworldviewBindings[]=\{ \{"helloworld-center", "\\003", 0, NULL, 0, 0, Center, "Center the helloworld string."\}, \{"helloworld-invert", "\\011",0, NULL, 0, 0, Invert, "Invert the helloworld string."\}, NULL \}; } The first entry in the array describes the binding of the key \italic{Ctrl-c} to the procedure \italic{Center}. The first item in the first entry, \italic{"helloworld-center,"} is the user invocation name. The second, \italic{"\\003,"} is the \smaller{ASCII} string representation of \italic{Ctrl-c}. The next item, \italic{0}, represents the data to be passed to the procedure; in this example, none. The next three entries, \smaller{NULL}, \italic{0}, \italic{0}, are the menu entry, menu data, and menu masks; again, in this example, none. The next item, \italic{Center}, is the name of the procedure that will be bound to the key \italic{Ctrl-c}. The next item, \italic{"Center the helloworld string,"} is the brief documentation. Likewise, the second entry in the array binds the key \italic{Ctrl-i} to the procedure \italic{Invert}. The third entry in the array is \smaller{NULL} and indicates the end of the description of the key bindings. In general, a bind description must have the user invocation name of the procedure, either the key sequence or the menu entry that invokes the procedure, the brief documentation, and either the pointer to the procedure or the module in which the procedure may be found. The rock data \indexi{Rock} \indexi{Parameters++Rock} is not generally used, but it can be a useful option. For example, in creating a procedure to do screen inversions, you could create two key command or menu options, "black-to-white," and "white-to-black." Instead of actually writing two separate procedures, however, you could write one general "invert" procedure, and pass the procedure an appropriate rock value which would tell the procedure which kind of inversion to perform. In this example, we do not use the rock parameter, since it is easier to have the Update method handle the inversions. \subsection{Creating the command functions} You must define a command function for each binding of a key sequence to a function. The name of the function must match the name that you used in the \italic{bind_Description} structure. For example, \italic{helloworldview} defines two command functions in \italic{hellov.c:} \italic{Center} for \italic{Ctrl-c} and \italic{Invert} for \italic{Ctrl-i}: \formatnote{ static void Center(hwv, rock) struct helloworldview *hwv; long rock; \{ struct rectangle myVisualRect; helloworldview_GetVisualBounds(hwv,&myVisualRect); hwv->newX = rectangle_Left(&myVisualRect) + rectangle_Width(&myVisualRect) / 2; hwv->newY = rectangle_Top(&myVisualRect) + rectangle_Height(&myVisualRect) / 2; helloworldview_WantUpdate(hwv, hwv); \} static void Invert(hwv, rock) struct helloworldview *hwv; long rock; \{ hwv->newBlackOnWhite = ! hwv->newBlackOnWhite; helloworldview_WantUpdate(hwv, hwv); \} } The actions of these functions are specific to the \italic{helloworldview} example. \italic{Center} re-centers the string by calculating the center point, and requesting a simple update redraw. \italic{Invert} looks for the current color of the screen (white or black), sets the \italic{newBlackOnWhite} variable to the opposite color, and requests a simple update redraw for the screen. Note that the first argument to any command procedure must be a pointer to the class. In this example, \italic{struct helloworldview *hwv}. The second parameter to the Invert procedure, rock, is a constant value passed along to the procedure. \subsection{Creating the key map} \indexi{Key maps++Creating} The \italic{keymap} represents the set of bindings from the key sequences to the functions to be performed. The same \italic{keymap} can normally be shared among all instances of a view. Thus, the \italic{keymap} should normally be created in \italic{InitializeClass,} \indexi{ \italic{InitializeClass}} \indexi{Class++Initializing} a class procedure that is called only once-the first time the class is loaded. Likewise, the structure declaration for the \italic{keymap} can be done in the module rather In general, to do keyboard input, you should declare an \italic{InitializeClass} procedure that creates a new \italic{keymap} and associates the keybindings for the class with the newly created \italic{keymap}. For example, the following creates a \italic{keymap} and associates the \italic{keymap} with the key bindings for \italic{helloworldview} in the file \italic{hellov.c}: \formatnote{ static struct keymap *helloworldviewKeymap; boolean helloworldview__InitializeClass(classID) struct classheader *classID; \{ helloworldviewKeymap=keymap_New(); bind_BindList(helloworldviewBindings, helloworldviewKeymap,NULL,&helloworldview_classinfo); return TRUE; \} } The line \italic{static struct keymap *helloworldviewKeymap} declares the key map structure for this class. Then, \italic{helloworldviewKeymap=keymap_New()} creates a new key map and stores it in \italic{helloworldviewKeymap}. Finally, a call to the \italic{bind_BindList} \indexi{ \italic{bind_BindList}} \indexi{ \italic{BindList}} method associates the key bindings in \italic{helloworldviewBindings} with the newly created key map, \italic{helloworldviewKeymap}. The method \italic{bind_BindList} takes four parameters: The first is a description of the key bindings, \italic{struct bind_Description}; \indexi{Key bindings++Description} \indexi{Bind description} the second is a keymap, \italic{struct keymap}; the third is a list of menus, \italic{struct menulist} (in this example \smaller{NULL}); and the fourth is the class information, \italic{struct classinfo}. The final parameter, \italic{struct classinfo}, \indexi{Classinfo} needs some explanation. When the class preprocessor runs, it generates a \italic{classinfo} structure for each class. In this example, the preprocessor creates helloworld_classinfo, which is a static global structure defined in the file \italic{hellov.eh}. For any class \italic{x}, to access the class information from the class that exports it, you must write \italic{&x_classinfo} (e.g., \italic{&helloworld_classinfo}). To access the class information from everywhere else, you write \italic{class_load("x")} . For instance, a file, compile.c, which supplies procedures for use with the class \italic{textview} (and therefore needs to pass the address of a \italic{textview_classinfo} structure to bind_BindList or to a proctable_DefineProc) would use class_load("textview"), not &textview_classinfo. \indexi{Exporting++Class information} \indexi{Importing++Class information} \subsection{Creating a key state & initializing the input focus} \indexi{Key states++Creating} \indexi{Input focus++Initializing} The \italic{keystate} represents particular \italic{view}. Thus, requires a \italic{keystate}, and it should \italic{InitializeObject} method for the class. The newly part of the view's data for later use in addition, the \italic{InitializeObject} method does not have the input focus. the binding of a \italic{keymap} to a each instance of a \italic{view} be created in the created keystate should be stored as \italic{ReceiveInputFocus}. In should set a flag to indicate that it In this example, the following \italic{InitializeObject} \indexi{ \italic{InitializeObject}} method, in \italic{hellov.c} creates a \italic{keystate} and associates the \italic{keystate} with the \italic{keymap} for \italic{hwv}, an instance of the view \italic{helloworldview}: \formatnote{ #define POSUNDEF -1 boolean helloworldview__InitializeObject(classID, hwv) struct classheader *classID; struct helloworldview *hwv; \{ hwv->x = POSUNDEF; hwv->y = POSUNDEF; hwv-> HaveDownTransition = FALSE; \bold{hwv->haveInputFocus = FALSE; hwv->keystate = keystate_Create(hwv, helloworldviewKeymap); hwv->blackOnWhite = TRUE; hwv->newBlackOnWhite = TRUE;} return TRUE; \} } \italic{hw->keystate = keystate_Create(hw, helloworldKeymap)} creates a \italic{keystate} that binds \italic{hwv}, an instance of the \italic{view} \italic{helloworldview}, to the \italic{keymap} \italic{helloworldviewKeymap }and stores it in \italic{hwv->keystate}. The statement \italic{hwv->haveInputFocus=FALSE} indicates that hwv does not have the input focus. \subsection{User requests for the input focus} \indexi{Input focus++Requesting} By convention, the user can cause a \italic{view} to make itself the input focus by clicking in its space on the screen. If the \italic{view} wishes to respond to the user request, it in turn should request the Interaction Manager to give it the input focus upon receiving the \italic{DownTransition} in its \italic{Hit} method. In general, if you are building a \italic{view} that will respond to keyboard input, a request for the input focus should be provided in the \italic{view}'s \italic{Hit} method by \italic{WantInputFocus} call. It should be noted that the request for input focus must be explicit. The following, in \italic{hellov.c}, is the \italic{helloworldview_Hit} method. It is written so that if the view does not have the input focus, it requests the input focus upon a down click of the left or right mouse button. This is the same as in the previous example, but the input focus discussion is more relevant for this chapter. \formatnote{ 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; \} } The case statement \italic{view_LeftDown:} requests the input focus from \italic{hwv}'s parent in the view tree. Normally, \italic{hwv}'s parent will pass the input focus request to its parent and so on to the \italic{Interaction Manager}. If the request is granted, the \italic{Interaction Manager} will call \italic{hwv}'s \italic{ReceiveInputFocus} method, discussed below. The method \italic{WantInputFocus} takes two parameters. The first is the view from which you are making the request for the input focus, in this case \italic{hwv}; the second parameter is the view to give the input focus to, normally a pointer to your own view, in this case \italic{hwv}. \subsection{Receiving the input focus} \indexi{Input focus++Receiving} \indexi{ \italic{ReceiveInputFocus}} When the \italic{Interaction Manager} gives a view the input focus, it notifies the view by calling a method, \italic{ReceiveInputFocus}. If you are writing a view that does keyboard input, its \italic{ReceiveInputFocus} method must do three things. First, it should set a flag that indicates it has the input focus. You will need to write other methods (\italic{e.g.}, Hit, Update, etc.) so that they test this flag and act accordingly. For example, upon receiving a down transition, a \italic{Hit} method should test the flag to check if it has the input focus before requesting it. Second, since the \italic{Interaction Manager} keeps a linked list of keystates that correspond to the keybindings that views in the view tree have posted, a view must set the next keystate to NULL to indicate that there are no more keystates in the list \italic{before} posting a keystate in \italic{ReceiveInputFocus}. Failure to do so will result in an infinite loop. Finally, the view should post a \italic{keystate} to its parent. The \italic{keystate} provides the \italic{Interaction Manager} with the information it needs to interpret the user's keyboard input appropriately for the view. If the view is to highlight itself when it has the input focus, its \italic{ReceiveInputFocus} method should also request an \italic{Update} from its parent in order to draw the highlighting. The following, in \italic{hellov.c}, is \italic{helloworldview}'s \italic{ReceiveInputFocus} method: \formatnote{ void helloworldview__ReceiveInputFocus(hwv) struct helloworldview *hwv; \{ hwv->haveInputFocus = TRUE; hwv->keystate->next = NULL; helloworldview_PostKeyState(hwv, hwv->keystate); helloworldview_PostMenus(hwv, NULL); \} } The statement \italic{hwv->haveInputFocus = \smaller{TRUE}} sets a flag to indicate that \italic{hwv} has the input focus. \italic{hwv->keystate>next = \smaller{NULL}} sets the next keystate to \smaller{\italic{NULL}} before posting the keystate. Finally, the statement \italic{helloworld_PostKeyState(hwv,hwv->keystate)} posts the keystate \italic{hwv->keystate} for \italic{hwv} to the \italic{Interaction Manager}. The final statement, \italic{helloworldview_PostMenus(hwv, NULL);} is used to get the input focus for the Quit menu card, and will be explained in more detail in Example 5. If you are creating a view that is overriding view's \italic{ReceiveInputFocus} method, even if your view does not have a \italic{keystate} it should post a \italic{keystate} of \smaller{NULL}. \subsection{Losing the input focus} \indexi{Input focus++Losing} \indexi{ \italic{LoseInputFocus}} When the \italic{Interaction Manager} takes the input focus away from a view, it notifies the view by calling another method, \italic{LoseInputFocus}. A view's \italic{LoseInputFocus} method should set a flag indicating that it no longer has the input focus, then do whatever it needs to do before losing the input focus. For example, if the view needs to de-highlight itself, it should request an update from it parent in order to de-highlight. You do not need to worry about \italic{un}posting a view's keystates when it loses the input focus. That is handled automatically. You must, however, repost the keystates every time the view gets the input focus back, as discussed above. The following is \italic{helloworldview}'s \italic{LoseInputFocus} method: \formatnote{ void helloworldview__LoseInputFocus(hwv) struct helloworldview *hwv; \{ hwv->haveInputFocus = FALSE; \} } The statement \italic{hwv>haveInputFocus = \smaller{FALSE}} sets a flag that indicates \italic{hwv} does not have the input focus. \subsection{Requesting the input focus upon start-up} \indexi{Input focus++Requesting} If you are creating an application that does keyboard input, then it should request the input focus after putting the object in the view tree and before entering the keyboard processor interaction loop. If the input focus is not requested, the key commands will not operate until the first call to FullUpdate, which occurs after a mouse action. The following statement, in \italic{helloa.c}, immediately following the im_SetView statement, requests the input focus for \italic{hwv} upon start-up: \formatnote{ helloworldview_WantInputFocus(hwv,hwv); } \subsection{Full update and update requests} If a view highlights when it has the input focus, its \italic{FullUpdate} and \italic{Update} methods must test the input focus flag and redraw highlighted when it is \smaller{TRUE}, de-highlighted when it is \smaller{FALSE}. Because \italic{helloworldview} does not highlight when it has the input focus, the following changes in \italic{FullUpdate} and \italic{Update} methods are specific to \italic{helloworldview} and will be of interest only to those who wish to see the example carried to completion. \begindata{bp,538929032} \enddata{bp,538929032} \view{bpv,538929032,919,0,0} \paragraph{Full update request} \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); if (hwv->x == POSUNDEF) \{ hwv->x = rectangle_Left(&myVisualRect) + rectangle_Width(&myVisualRect) / 2; hwv->y = rectangle_Top(&myVisualRect) + rectangle_Height(&myVisualRect) / 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->y); helloworldview_DrawString(hwv,"hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); \} } We add a black/white inversion function, so that if the rectangle is black-on-white, then the program switches it to white-on-black, and vice versa. Also, newX and newY must be included in the rectangle calculations so that their initial values will not be zero. If they are, and the first action the user performs in the program window is a key command, the "hello world" string will go to coordinates (0,0) instead of staying in the center. \begindata{bp,538928712} \enddata{bp,538928712} \view{bpv,538928712,920,0,0} \paragraph{Update request} \formatnote{ 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->newY != hwv->y) \{ helloworldview_MoveTo(hwv, hwv->x, hwv->y); helloworldview_DrawString(hwv, "hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); hwv->x = hwv->newX; hwv->y = hwv->newY; helloworldview_MoveTo(hwv, hwv->x, hwv->y); helloworldview_DrawString(hwv, "hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); \} \} } Again, this is similar to the previous example with the addition of the inversion function. \section{Importing Andrew Toolkit procedures} \formatnote{ #include <class.h> #include "hellov.eh" #include "graphic.ih" \bold{#include "rect.h" #include "keymap.ih" #include "keystate.ih" #include "bind.ih"} } In previous examples we imported only the procedures in class.h, graphic.ih, and hellov.eh. The added complexity of this example requires several additional imports: rect.h contains procedures for managing rectangles that are used by graphic; keymap.ih, keystate.ih, and bind.ih, contain procedures needed by the various calls to keymap, keystate, and bind methods. The compilation procedures are the same as in the previous example. \begindata{bp,538928968} \enddata{bp,538928968} \view{bpv,538928968,921,0,0} \begindata{texttag,539236104} \textdsversion{12} ExFourListing\ \enddata{texttag,539236104} \view{texttagv,539236104,922,0,0} \section{Program listing for Example 4} \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(); classprocedures: InitializeClass()returns boolean; data: struct keystate *keystate; long x,y; long distX,distY; long newX,newY; boolean HaveDownTransition; boolean haveInputFocus; boolean blackOnWhite; boolean newBlackOnWhite; \}; \bold{hellov.c} #include <class.h> #include "hellov.eh" #include "graphic.ih" #include "rect.h" #include "keymap.ih" #include "keystate.ih" #include "bind.ih" #define POSUNDEF -1 static struct keymap *helloworldviewKeymap; 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; 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); if (hwv->x == POSUNDEF) \{ hwv->x = rectangle_Left(&myVisualRect) + rectangle_Width(&myVisualRect) / 2; hwv->y = rectangle_Top(&myVisualRect) + rectangle_Height(&myVisualRect) / 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->y); 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->newY != hwv->y) \{ helloworldview_MoveTo(hwv, hwv->x, hwv->y); helloworldview_DrawString(hwv, "hello world", graphic_BETWEENTOPANDBASELINE | graphic_BETWEENLEFTANDRIGHT); hwv->x = hwv->newX; hwv->y = hwv->newY; helloworldview_MoveTo(hwv, hwv->x, hwv->y); 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, NULL); \} void helloworldview__LoseInputFocus(hwv) struct helloworldview *hwv; \{ hwv->haveInputFocus = FALSE; \} static void Center(hwv, rock) struct helloworldview *hwv; long rock; \{ struct rectangle myVisualRect; helloworldview_GetVisualBounds(hwv,&myVisualRect); hwv->newX = rectangle_Left(&myVisualRect) + rectangle_Width(&myVisualRect) / 2; hwv->newY = rectangle_Top(&myVisualRect) + rectangle_Height(&myVisualRect) / 2; helloworldview_WantUpdate(hwv, hwv); \} static void Invert(hwv, rock) struct helloworldview *hwv; long rock; \{ hwv->newBlackOnWhite = ! hwv->newBlackOnWhite; helloworldview_WantUpdate(hwv, hwv); \} static struct bind_Description helloworldviewBindings[]=\{ \{"helloworld-center", "\\003",0, NULL,0,0, Center, "Center the helloworld string."\}, \{"helloworld-invert", "\\011",0, NULL,0,0, Invert, "Invert the helloworld string."\}, NULL \}; boolean helloworldview__InitializeClass(classID) struct classheader *classID; \{ helloworldviewKeymap=keymap_New(); bind_BindList(helloworldviewBindings, helloworldviewKeymap,NULL,&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 "hellov.ih" boolean helloworldapp__Start(hwapp) struct helloworldapp *hwapp; \{ struct helloworldview *hwv; struct im *im; if(!super_Start(hwapp)) return FALSE; hwv=helloworldview_New(); if(hwv==NULL) return FALSE; im=im_Create(NULL); if(im==NULL)\{ helloworldview_Destroy(hwv); return FALSE; \} im_SetView(im,hwv); 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,924,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,538945640}