Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Macintosh Toolbox Essentials /
Chapter 3 - Menu Manager / Using the Menu Manager


Handling User Choice of a Menu Command

If the user presses the mouse button while the cursor is in the menu bar, your application should first adjust its menus (enable or disable menu items and add marks to or remove marks from any items as appropriate to the user's context) and then call the MenuSelect function to allow the user to choose a menu command. The MenuSelect function handles all user interaction until the user releases the mouse button and returns a value as its function result that indicates which (if any) menu item the user chose.

For a command with a keyboard equivalent, your application should allow the user to choose the command by pressing the keys that correspond to the keyboard equivalent
of that menu command. If the user presses the Command key and another key, your application should adjust its menus and then call the MenuKey function to map this combination to a keyboard equivalent. The MenuKey function returns as its function result a value that indicates the corresponding menu and menu item of the keyboard equivalent.

When the user chooses a menu command, your application should perform the action associated with that command. The MenuSelect and MenuKey functions highlight the menu title of the menu containing the chosen menu command. After your application performs any operation associated with the menu command chosen by the user, your application should unhighlight the menu title by using the HiliteMenu procedure.

However, if in response to a menu command your application displays a window that contains editable text (such as a modal dialog box), you should unhighlight the menu title immediately so that the user can access the Edit menu or other appropriate menus. In other words, any time the user can use a menu, make sure that the menu title is
not highlighted.

When the user chooses a menu command that involves an operation that takes a long time, display the animated wristwatch cursor or display a status dialog box to give the user feedback that the operation is in progress.

If you want the users of your application to be able to record their actions (such as menu commands, text input, or any sequence of actions) for later playback, your application should send itself Apple events whenever a user performs a significant action. To do this for menu commands, your application typically sends itself an Apple event to perform the action associated with the chosen menu command. For example, when a user chooses the New command from the File menu, your application can choose to send itself a Create Element event. Your application then creates the new document in response to this event. For information on sending Apple events in response to menu commands, see Inside Macintosh: Interapplication Communication.

The next sections show how your application can

The next sections also show how your application should respond when the user chooses an item from the Apple or Help menu.

Handling Mouse-Down Events in the Menu Bar

You can determine when the user has pressed the mouse button while the cursor is in the menu bar by examining the event record for a mouse-down event. You can use the Window Manager function FindWindow to map the mouse location at the time of the mouse-down event to a corresponding area of the screen. If the cursor was in the menu bar, your application should call the MenuSelect function, allowing the user to choose a menu command.

Listing 3-17 shows an application-defined procedure, DoEvent, that determines whether a mouse-down event occurred and, if so, calls another application-defined procedure to handle the mouse-down event. (For a complete discussion of how to handle events, see the "Event Manager" chapter in this book.)

Listing 3-17 Determining whether a mouse-down event occurred

PROCEDURE DoEvent (event: EventRecord);
BEGIN
   CASE event.what OF
      mouseDown:                 {handle mouse-down event}
         DoMouseDown(event); 
      {handle other events appropriately}
   END; {of CASE}
END;
Listing 3-18 shows an application-defined procedure, DoMouseDown, that handles mouse-down events. The DoMouseDown procedure determines where the cursor was when the mouse button was pressed and then responds appropriately.

Listing 3-18 Determining when the cursor is in the menu bar

PROCEDURE DoMouseDown (event: EventRecord);
VAR
   part:       Integer;
   thisWindow: WindowPtr;
BEGIN
   part := FindWindow(event.where, thisWindow);
   CASE part OF
      inMenuBar:{mouse down in menu bar, respond appropriately}
         BEGIN
            {adjust marks and enabled state of menu items}
            MyAdjustMenus;
            {let user choose a menu command if desired}
            DoMenuCommand(MenuSelect(event.where)); 
         END;  
      {handle other mouse-down events appropriately}
   END; {of CASE}
END;
You can use the FindWindow function to map the mouse location at the time the
user pressed the mouse button to general areas of the screen. If the mouse location
is in the menu bar, the FindWindow function returns the constant inMenuBar. In Listing 3-18, if the mouse location associated with the mouse-down event is in the
menu bar, the DoMouseDown procedure first calls another application-defined procedure, MyAdjustMenus, to adjust the menus. Listing 3-19 shows the MyAdjustMenus procedure.

The DoMouseDown procedure then calls an application-defined procedure, DoMenuCommand. The DoMouseDown procedure passes as a parameter to
the DoMenuCommand procedure the value returned from the MenuSelect function.

The MenuSelect function displays menus and handles all user interaction until the user releases the mouse button. The MenuSelect function returns a long integer indicating whether the user chose a menu command, and if so, it indicates which menu and which command the user chose.

Listing 3-24 on page 3-81 shows the DoMenuCommand procedure.

Adjusting the Menus of an Application

Your application should always adjust its menus before calling MenuSelect or MenuKey. For example, you should enable and disable any menu items as necessary
and add checkmarks or dashes to items that are attributes. When you adjust your application's menus, you should enable and disable menu items according to the type
of window that is in the front. For example, when a document window is the frontmost window, you should enable items as appropriate for that document window. When
a modeless dialog box or modal dialog box is the frontmost window, enable those
items as appropriate to that particular dialog box. Listing 3-19 shows an application- defined routine, MyAdjustMenus, that adjusts the menus of the SurfWriter
application appropriately.

The MyAdjustMenus procedure first determines what kind of window is in front
and then adjusts the application's menus appropriately. The application-defined MyGetWindowType procedure returns a value that indicates whether the window
is a document window, a dialog window, or a window belonging to a desk accessory.
It also returns the constant kNil if there isn't a front window. (See the chapter
"Window Manager" in this book for a listing of the MyGetWindowType procedure.)
The MyAdjustMenus procedure calls other application-defined routines to adjust the menus as appropriate for the given window type.

Listing 3-19 Adjusting an application's menus

PROCEDURE MyAdjustMenus;
VAR
   window:              WindowPtr;
   windowType:          Integer;
BEGIN
   window := FrontWindow;
   windowType := MyGetWindowType(window);
   CASE windowType OF 
   kMyDocWindow:
   BEGIN {document window is in front, adjust items appropriately}
      MyAdjustFileMenuForDocWindow;
      MyAdjustEditMenuForDocWindow;
      {adjust other menus as needed}
   END;  {of adjusting menus for a document window}
   kMyDialogWindow:
      {adjust menus accordingly for any dialog box}
      MyAdjustMenusForDialogs; 
   kDAWindow:{adjust menus accordingly for a DA window}
      MyAdjustMenusForDA; 
   kNil:{adjust menus accordingly when there isn't a front window}
      MyAdjustMenusNoWindows;
   END; {of CASE}
   DrawMenuBar;
END;
Listing 3-20 shows the application-defined procedure MyAdjustFileMenuForDocWindow. This procedure enables and disables the File menu for the application's document window, according to the state of the document. For example, this application always allows the user to create a new document or open
a file, so the code enables the New and Open menu items. The code also enables the Close, Save As, Page Setup, Print, and Quit menu items. If the user has modified the
file since last saving it, the code enables the Save command; otherwise, it disables the Save command.

Listing 3-20 Adjusting the File menu for a document window

PROCEDURE MyAdjustFileMenuForDocWindow;
VAR
   window:     WindowPtr;
   menu:       MenuHandle;
   myData:     MyDocRecHnd;
BEGIN
   window := FrontWindow;
   menu := GetMenuHandle(mFile); {get a handle to the File menu}
   IF menu = NIL THEN            {add your own error handling}
      EXIT (MyAdjustFileMenuForDocWindow);
   EnableItem(menu, iNew);
   EnableItem(menu, iOpen);
   EnableItem(menu, iClose);
   myData := MyDocRecHnd(GetWRefCon(window));
   IF myData^^.windowDirty THEN
      EnableItem(menu, iSave)
   ELSE
      DisableItem(menu, iSave);
   EnableItem(menu, iSaveAs);
   EnableItem(menu, iPageSetup);
   EnableItem(menu, iPrint);
   EnableItem(menu, iQuit);
END;
Listing 3-21 shows the application-defined MyAdjustEditMenuForDocWindow procedure.

Listing 3-21 Adjusting the Edit menu for a document window

PROCEDURE MyAdjustEditMenuForDocWindow;
VAR
   window:              WindowPtr;
   menu:                MenuHandle;
   selection, undo:     Boolean;
   isSubscriber:        Boolean;
   undoText:            Str255;
   offset:              LongInt;
BEGIN
   window := FrontWindow;
   menu := GetMenuHandle(mEdit); {get a handle to the Edit menu}
   IF menu = NIL THEN            {add your own error handling}
      EXIT (MyAdjustEditMenuForDocWindow);
   undo := MyIsLastActionUndoable(undoText);
   IF undo THEN   {if action can be undone}
   BEGIN
      SetMenuItemText(menu, iUndo, undoText);
      EnableItem(menu, iUndo);
   END
   ELSE           {if action can't be undone}
   BEGIN
      SetMenuItemText(menu, iUndo, gCantUndo);
      DisableItem(menu, iUndo);
   END;
   selection := MySelection(window);
   IF selection THEN
   BEGIN    {enable editing items if there's a selection}
      EnableItem(menu, iCut); 
      EnableItem(menu, iCopy);
      EnableItem(menu, iCreatePublisher);
   END
   ELSE
   BEGIN    {disable editing items if there isn't a selection}
      DisableItem(menu, iCut); 
      DisableItem(menu, iCopy);
      DisableItem(menu, iCreatePublisher);
   END;
   IF GetScrap(NIL, 'TEXT', offset) > 0 THEN
      EnableItem(menu, iPaste)   {enable if something to paste}
   ELSE
      DisableItem(menu, iPaste); {disable if nothing to paste}
   EnableItem(menu, iSelectAll);
   EnableItem(menu, iSubscribeTo);
   IF MySelectionContainsSubscriberOrPublisher(isSubcriber) THEN
   BEGIN {selection contains a single subscriber or publisher}
      IF isSubscriber THEN {selection contains a subscriber}
         SetMenuItemText(menu, iPubSubOptions, gSubOptText)
      ELSE                 {selection contains a publisher}
         SetMenuItemText(menu, iPubSubOptions, gPubOptText);
      EnableItem(menu, iPubSubOptions);
   END
   ELSE  {selection contains either no subscribers or publishers }
         { or contains at least one subscriber and one publisher}
      DisableItem(menu, iPubSubOptions);
   IF (gPubCount > 0) OR (gSubCount > 0) THEN
      EnableItem(menu, iShowHideBorders)
   ELSE
      DisableItem(menu, iShowHideBorders);
END; 
The procedure in Listing 3-21 adjusts the items in the Edit menu as appropriate for a document window of the application. The code enables the Undo command if the application can undo the last command, enables the Cut and Copy commands if there's a selection that can be cut or copied, enables the Paste command if there's text data in the scrap, and enables the menu items relating to publishers and subscribers appropriately, according to whether the current selection contains a publisher or subscriber. The application-defined MySelectionContainsSubscriberOrPublisher function returns TRUE if the current selection contains a single subscriber or a single publisher and returns FALSE otherwise. If the MySelectionContainsSubscriberOrPublisher function returns TRUE, the code sets the text for the Publisher Options (or Subscriber Options) command and enables the menu item. If the function returns FALSE, the code disables the Publisher Options (or Subscriber Options) command.

Determining if the User Chose a Keyboard Equivalent

Keyboard equivalents of commands allow the user to invoke a menu command from the keyboard. You can determine if the user chose the keyboard equivalent of a menu command by examining the event record for a key-down event. If the user pressed the Command key in combination with another 1-byte character, you can determine if this combination maps to a Command-key equivalent by using the MenuKey function.

If your application supports keyboard equivalents that use other modifier keys in addition to the Command key, your application should examine the modifiers
field and take any appropriate action; depending on the modifier keys you use,
your application may or may not be able to use MenuKey to map the key to the
menu command.

Listing 3-22 shows an application-defined procedure, DoEvent, that determines whether a key-down event occurred and, if so, calls an application-defined routine to handle the key-down event.

Listing 3-22 Determining when a key is pressed

PROCEDURE DoEvent (event: EventRecord);
BEGIN
   CASE event.what OF
      keyDown, autoKey:       {handle keyboard events}
         DoKeyDown(event);
      {handle other events appropriately}
   END; {of CASE}
END;
If your application determines that the user pressed a key, you need to determine whether the user chose the keyboard equivalent of a menu command. You can do this by examining the modifiers field of the event record describing the key-down event. If
the Command key was also pressed, then your application should call the MenuKey function. The MenuKey function scans the current menu list for a menu item that has a matching keyboard equivalent and returns the menu and menu item, if any. Although you should not define the same keyboard equivalent for more than one command, the MenuKey function scans the menus from right to left, scanning the items from top to bottom, and returns the first matching keyboard equivalent that it finds.

If your application uses other keyboard equivalents in addition to Command-key equivalents, you can examine the state of the modifier keys and use the Event Manager function KeyTranslate, if necessary, to help map the keyboard equivalent to a particular menu item. See the discussion of 'KCHR' resources in Inside Macintosh: Text for information on how various keyboard combinations map to specific character codes.

Listing 3-23 shows an application's DoKeyDown procedure that handles key-down events and determines if a keyboard equivalent was pressed.

Listing 3-23 Checking a key-down event for a keyboard equivalent

PROCEDURE DoKeyDown (event: EventRecord);
VAR
   key:        Char;
BEGIN
   key := CHR(BAnd(event.message, charCodeMask));
   IF BAnd(event.modifiers, cmdKey) <> 0 THEN
   BEGIN       {Command key down}
      IF event.what = keyDown THEN
      BEGIN                {first enable/disable/check }
         MyAdjustMenus;    { menu items properly}
         DoMenuCommand(MenuKey(key));{handle the menu command}
      END;
   END
   ELSE
      MyHandleKeyDown(event);
END;
Listing 3-23 extracts the pressed key from the message field of the event record and
then examines the modifiers field to determine if the Command key was also pressed. If so, the application first adjusts its menus and then calls an application-defined procedure, DoMenuCommand. The DoKeyDown procedure passes as a parameter to
the DoMenuCommand procedure the value returned from the MenuKey function.

Listing 3-24 shows the DoMenuCommand procedure.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
11 JUL 1996