Important: The information in this document is obsolete and should not be used for new development.
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 theMenuSelect
function to allow the user to choose a menu command. TheMenuSelect
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 theMenuKey
function to map this combination to a keyboard equivalent. TheMenuKey
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
andMenuKey
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 theHiliteMenu
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.
- determine if the user pressed the mouse button while the cursor was in the menu bar
- adjust its menus--enabling and disabling commands according to the current state of the document--before displaying menus or before responding to the user's choice of a keyboard equivalent of a command
- determine if the user chose the keyboard equivalent of a menu command
- respond to the user when the user chooses a menu command
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 functionFindWindow
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 theMenuSelect
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. TheDoMouseDown
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 theFindWindow
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, theFindWindow
function returns the constantinMenuBar
. In Listing 3-18, if the mouse location associated with the mouse-down event is in the
menu bar, theDoMouseDown
procedure first calls another application-defined procedure,MyAdjustMenus
, to adjust the menus. Listing 3-19 shows theMyAdjustMenus
procedure.The
DoMouseDown
procedure then calls an application-defined procedure,DoMenuCommand
. TheDoMouseDown
procedure passes as a parameter to
theDoMenuCommand
procedure the value returned from theMenuSelect
function.The
MenuSelect
function displays menus and handles all user interaction until the user releases the mouse button. TheMenuSelect
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 callingMenuSelect
orMenuKey
. 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-definedMyGetWindowType
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 constantkNil
if there isn't a front window. (See the chapter
"Window Manager" in this book for a listing of theMyGetWindowType
procedure.)
TheMyAdjustMenus
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 returnsTRUE
if the current selection contains a single subscriber or a single publisher and returnsFALSE
otherwise. If the MySelectionContainsSubscriberOrPublisher function returnsTRUE
, the code sets the text for the Publisher Options (or Subscriber Options) command and enables the menu item. If the function returnsFALSE
, 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 theMenuKey
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 useMenuKey
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 themodifiers
field of the event record describing the key-down event. If
the Command key was also pressed, then your application should call theMenuKey
function. TheMenuKey
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, theMenuKey
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 themessage
field of the event record and
then examines themodifiers
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
. TheDoKeyDown
procedure passes as a parameter to
theDoMenuCommand
procedure the value returned from theMenuKey
function.Listing 3-24 shows the
DoMenuCommand
procedure.