Important: The information in this document is obsolete and should not be used for new development.
Writing Your Own Menu Definition Procedure
The Menu Manager uses the menu definition procedure and menu bar definition function to display and perform basic operations on menus and the menu bar. The
menu definition procedure performs all the drawing of menu items within a menu
and performs all the actions that might differ between one type of menu and another. The menu bar definition function draws the menu bar and performs most of the
drawing activities related to the display of menus when the user moves the cursor between menus.Apple provides a standard menu bar definition function, stored as a resource in the System file. The standard menu bar definition procedure is the
'MBDF'
resource with resource ID 0. When you create your menus and menu bar, by default the Menu Manager uses the standard menu bar definition function to manage them. Although the Menu Manager lets you provide your own menu bar definition function, Apple recommends that you always use the standard menu bar definition function.The Menu Manager uses the standard menu bar definition function to
Apple provides a standard menu definition procedure, stored as a resource in the System file. The standard menu definition procedure is the
- draw the menu bar
- clear the menu bar
- determine if the cursor is in the menu bar or any currently displayed menus
- calculate the left edges of menu titles
- highlight a menu title
- invert the entire menu bar
- erase the background color of a menu and draw the menu's structure (shadow)
- save or restore the bits behind a menu
'MDEF'
resource with resource ID 0. The standard menu definition procedure handles three types of menus: pull-down, pop-up, and hierarchical; it also implements scrolling in menus. When you define your menus, you specify the menu definition procedure that the Menu Manager should use when managing them. You'll usually want to use the standard definition procedure for your application. However, if you need a feature not provided by the standard menu definition procedure (for example, if you want to include more graphics in your menus), you can write your own menu definition procedure.The Menu Manager uses the standard menu definition procedure to
If you provide your own menu definition procedure, it should also perform these tasks. Your menu definition procedure should also support scrolling in menus and color in menus and provide support for Balloon Help.
- calculate a menu's dimensions
- draw the menu items in a menu
- highlight and unhighlight menu items as the user moves the cursor between them
- determine which item the user chose from a menu
If you provide your own menu definition procedure, store it in a resource of type
'MDEF'
and include its resource ID in the description of each menu that uses your own menu definition procedure. If you create a menu usingGetMenu
(orGetNewMBar
), the Menu Manager reads the menu definition procedure into memory and stores a handle to it in themenuProc
field of the menu's menu record.When your application uses
GetMenu
(orGetNewMBar
) to create a new menu that uses your menu definition procedure, the Menu Manager creates a menu record for the menu and fills in themenuID
,menuProc
,enableFlags
, andmenuData
fields according to the menu's resource description. The Menu Manager also reads in the data for each menu item and stores it as variable data at the end of the menu record. The menu definition procedure is responsible for interpreting the contents of the data. For example, the standard menu definition procedure interprets this data as described in "The Menu Resource" beginning on page 3-151. After reading in a resource description of a menu, the Menu Manager requests the menu definition procedure to calculate the size of
the menu and to store these values in themenuWidth
andmenuHeight
fields of the menu's menu record.Note that when drawing a menu, the Menu Manager first requests your menu definition procedure to calculate the dimensions (the menu rectangle) of the menu. Next the Menu Manager requests the menu bar definition function to draw the structure (shadow) of the menu and erase the contents of the menu to its background color. Then the Menu Manager requests your menu definition procedure to draw the items in the menu. As the user moves the cursor into and out of menu items, the Menu Manager requests your menu definition procedure to highlight and unhighlight items appropriately. Your menu definition procedure should also determine when to add scrolling indicators to a menu and scroll the menu appropriately when the cursor is in a scrolling item. Your menu definition is responsible for showing and removing any help balloons associated with a menu item.
When the Menu Manager requests your menu definition procedure to perform an action on a menu, it provides your procedure with a handle to its menu record. This allows your procedure to access the data in the menu record and to use any data in the variable data portion of the menu record to appropriately handle the menu items. However, your menu definition procedure should not assume that the A5 register is properly set up, so your procedure can't refer to any of the QuickDraw global variables.
The Menu Manager passes a value to your menu definition procedure in the
message
parameter that indicates the action your menu definition procedure should perform. The Menu Manager always passes a handle to the menu record of the menu that the operation should affect in the parametertheMenu
. Depending on the requested action, the Menu Manager passes additional information in other parameters.Listing 3-28 shows how you might declare a menu definition procedure.
Listing 3-28 A sample menu definition procedure
PROCEDURE MyMDEF (message: Integer; theMenu: MenuHandle; VAR menuRect: Rect; hitPt: Point; VAR whichItem: Integer); {any support routines used by the main program of your MDEF } { go here} BEGIN CASE message OF mDrawMsg: MyDrawMenu(theMenu, menuRect); mChooseMsg: MyChooseItem(theMenu, menuRect, hitPt, whichItem); mSizeMsg: MySizeTheMenu(theMenu); mPopUpMsg: MyCalcMenuRectForOpenPopUpBox(theMenu, hitPt, menuRect); END; END;The next sections describe in more detail how your menu definition procedure should respond when it receives themDrawMsg
,mChooseMsg
, ormSizeMsg
constant in themessage
parameter. For a complete description of the menu definition procedure and the parameters passed to your procedure by the Menu Manager, see "The Menu Definition Procedure" beginning on page 3-148.Calculating the Dimensions of a Menu
Whenever the Menu Manager creates a menu or needs to calculate the size of a menu that is managed by your menu definition procedure, the Menu Manager calls your procedure and specifies themSizeMsg
constant in themessage
parameter, requesting that your procedure calculate the size of the menu.Listing 3-29 on page 3-92 shows an application-defined support routine, MySizeTheMenu, used by the application's menu definition procedure. After
calculating the height and width of the menu's rectangle, the menu definition
procedure stores the values in themenuWidth
andmenuHeight
fields of the
menu's menu record.Listing 3-29 Calculating the size of a menu
PROCEDURE MySizeTheMenu(theMenu: MenuHandle); VAR itemDataPtr: Ptr; numItems: Integer; BEGIN HLock(Handle(theMenu)); WITH theMenu^^ DO BEGIN {menuData points to title of menu and additional item data} itemDataPtr := @menuData; {skip past the menu title} itemDataPtr := POINTER(ORD4(itemDataPtr)+ itemDataPtr^ +1); END; numItems := CountMItems(theMenu); {calculate the height of the menu--each item's height can vary } { according to whether the item has an icon or a script code defined. } { The height of the menu should not exceed the height of the } { screen minus the menu bar height. } { Store the height in the menu's menu record} theMenu^^.menuHeight := MyCalcMenuHeight(itemDataPtr, numItems); {calculate the width of the menu (the width of the longest item): } { for each item calculate the width as } { width = iconWidth + markWidth + textWidth + subMenuWidth } { + cmdKeyComboWidth } { If an item doesn't have a characteristic, use 0 as the width of } { that characteristic. } { To calculate the width of item's text, must consider script code and } { width of the font. } { The width of the menu should not exceed the right or left } { boundaries of the screen. } { Store the width in the menu's menu record} theMenu^^.menuWidth := MyCalcMenuWidth(itemDataPtr, numItems); HUnLock(Handle(theMenu)); END;Drawing Menu Items in a Menu
Whenever the user presses the mouse button while the cursor is in the menu title of a menu managed by your menu definition procedure, the Menu Manager calls the menu bar definition function to highlight the menu title, draw the structure of the menu, and erase the contents of the menu to its background color. The Menu Manager then calls your menu definition procedure and specifies themDrawMsg
constant in themessage
parameter, requesting that your procedure draw the menu items. When your menu definition procedure receives this constant, it should draw the menu items of the menu specified by the parametertheMenu
inside the rectangle specified by themenuRect
parameter. The Menu Manager sets the current graphics port to the Window Manager port before calling your menu definition procedure. Your menu definition procedure can determine how to draw the menu items by examining the data in the menu record.If your menu definition procedure supports color menus, your procedure should
check the application's menu color information table for the colors to use to draw
each item. If the application's menu color information table contains a color entry for
an item, draw the item using that color. If the table does not contain an item entry for
a particular item, use the default item color defined in the menu title entry. If a menu title entry doesn't exist, use the default item color defined in the menu bar entry. If the menu bar entry doesn't exist, draw the item using black on white.If your menu definition procedure supports scrolling menus, it should insert scrolling indicators if necessary when drawing the menu items.
Listing 3-30 shows an application-defined support routine, MyDrawMenu, used by the application's menu definition procedure. The MyDrawMenu procedure draws each item in the menu, according to the item's defined characteristics. Disabled items should be drawn using the colors returned by the
GetGray
function. Pass the RGB color of the item's background in thebkgnd
parameter to theGetGray
function; pass the RGB color of the item's enabled text in thefgnd
parameter. TheGetGray
function returnsTRUE
if there's an available color between the two specified colors and returns in thefgnd
parameter the color in which you should draw the item.Listing 3-30 Drawing menu items
PROCEDURE MyDrawMenu(theMenu: MenuHandle; menuRect: Rect); VAR numItems: Integer; itemRect: Rect; item: Integer; currentOffset: LongInt; nextOffset: LongInt; BEGIN numItems := CountMItems(theMenu); currentOffset := 0; nextOffset := 0; FOR item := 1 TO numItems DO BEGIN {calculate the enclosing rectangle for this item} itemRect := MyCalcItemRect(item, menuRect, currentOffset, nextOffset); {draw the item--index into the item-specific data from the menu record } { to get the characteristics of this menu item and draw the item } { according to its defined characteristics. For example, draw the item's } { text in its defined style & font of its defined script, draw any icon, } { mark, submenu indication, or keyboard equivalent, and draw each } { characteristic of the item according to its color entry in the menu's } { menu color information table. } { Draw disabled items in gray--use the GetGray function to return the } { appropriate color. Also draw dividers using the gray color } { returned by GetGray} MyDrawTheItem(item, itemRect, menuRect, currentOffset); END; {if your menu supports scrolling, insert scrolling indicators if needed} MyInsertScrollingArrows(menuRect); END;Determining Whether the Cursor Is in an Enabled Menu Item
Whenever the user drags the cursor into or out of a menu item of a displayed menu managed by your menu definition procedure, the Menu Manager calls your procedure and specifies themChooseMsg
constant in themessage
parameter, requesting that
your procedure determine whether the cursor is in a menu item and that your procedure highlight or unhighlight the menu item as appropriate. When your menu definition procedure receives this constant, it should use the menu rectangle specified in themenuRect
parameter, the mouse location specified in thehitPt
parameter, and the item number specified in thewhichItem
parameter to determine the proper action
to take.To see whether the user chose an enabled item, your menu definition procedure should determine whether the specified mouse location is inside the rectangle specified by the
menuRect
parameter, and, if so, it should check whether the menu is enabled. If the menu is enabled, your menu definition procedure should determine whether the mouse location specified in thehitPt
parameter is in an enabled menu item.If the mouse location is in an enabled menu item, your menu definition procedure should unhighlight the item specified by the whichItem parameter, highlight the new item, and return the new item number in whichItem.
If the mouse location isn't in an enabled menu item, your menu definition procedure should unhighlight the item specified by the whichItem parameter and return 0 in
the whichItem parameter.When your menu definition procedure draws a menu item in its highlighted state in a color menu, it should reverse the background color and the item color and then draw the menu item. When your menu definition procedure needs to return a menu item to its normal (unhighlighted) state, it should reset the background color and item color of that menu item and draw the menu item.
If your menu definition procedure supports scrolling menus, it should scroll the menu when the user moves the cursor into the area of the indicator, or when the cursor is directly above or below the menu. If the user can scroll the menu up (by dragging the cursor past the last item to view more items), place a downward-pointing triangular indicator in place of the last item in the menu. If the user can scroll the menu down
(by dragging the cursor past the first item to view the items originally at the top of
the menu), place an upward-pointing triangular indicator in place of the first item
in the menu.For all menus, your menu definition procedure should set the global variable
MenuDisable
appropriately each time a new item is highlighted. SetMenuDisable
to the menu ID and item number of the last menu item chosen, whether or not it's disabled. TheMenuChoice
function uses the value inMenuDisable
to determine if a chosen menu item is disabled.Listing 3-31 shows an application-defined support routine, MyChooseItem, used by the application's menu definition procedure. This routine determines which item, if any, the point specified by the
hitPt
parameter is in. If the item is in an enabled menu item that is different from the previous item, the MyChooseItem procedure unhighlights the old item and highlights the new item. However, the MyChooseItem procedure does not highlight the new item if the item is in a divider or disabled item.The procedure also removes any help balloons as appropriate and, if Balloon Help is turned on, displays any help balloon of the new item (for any item other than a divider or scrolling indicator). The MyChooseItem procedure returns the item number of the new item in the
whichItem
parameter or returns 0 if no item is chosen. Although not shown in the listing, if the item is a disabled item, the procedure returns 0 in thewhichItem
parameter and sets theMenuDisable
global variable to the menu ID and item number of the disabled item.Listing 3-31 Choosing menu items
PROCEDURE MyChooseItem (theMenu: MenuHandle; menuRect: Rect; hitPt: Point; VAR whichItem: Integer); VAR oldWhichItem: Integer; MenuChoicePtr: ^LongInt; numItems, item, max: Integer; itemChosen: Integer; inScroll: Integer; currentOffset: LongInt; nextOffset: LongInt; BEGIN oldWhichItem := whichItem; whichItem := 0; itemChosen := 0; MenuChoicePtr := POINTER(kLowMemMenuDisable); numItems := CountMItems(theMenu); {find out whether the hitPt is in an item's rectangle, and if so, } { determine which item} item := 1; max := numItems + 1; currentOffset := 0; nextOffset := 0; REPEAT itemRect := MyCalcItemRect(item, menuRect, currentOffset, nextOffset); IF PtInRect(hitPt, itemRect) THEN {hitPt is in this item} itemChosen := item; item := item + 1; UNTIL (item = MAX) OR (itemChosen <> 0); IF itemChosen = 0 THEN BEGIN {the mouse isn't in any item of this menu;unhighlight previous item} MyNotInMenu(menuRect, oldWhichItem); END ELSE BEGIN {the mouse is in this menu item. } { First see if a previous item was highlighted} IF ((oldWhichItem <> 0) AND (oldWhichItem <> itemChosen)) THEN BEGIN {a previous item was highlighted--unhighlight it} itemRect := MyCalcOldItemRect(oldWhichItem, menuRect); IF HMGetBalloons THEN {if Balloon Help is on then } HMRemoveBalloon;{ remove any balloon that might be showing} MyHighlightItem(itemRect, oldWhichItem, FALSE); END; IF HMGetBalloons and MyIsItemDivider(itemChosen) THEN {Balloon Help is on and item is divider} HMRemoveBalloon;{remove any balloon that might be showing} IF MyIsItemEnabled(itemChosen) THEN BEGIN {the item is enabled, so highlight the item the cursor is in} itemRect := MyCalcNewItemRect(itemChosen, menuRect, currentOffset); {the highlighting routine must also support scrolling correctly } { (if the cursor is in a scrolling item, don't highlight the item)} inScroll := MyIsScrollItem(itemChosen); MyHighlightItem(itemRect, itemChosen, inScroll); IF HMGetBalloons AND inScroll THEN HMRemoveBalloon {remove any balloon that might be showing} ELSE BEGIN {display help balloon for this item, if any} IF HMGetBalloons THEN BEGIN IF StillDown THEN {mouse button is still down in this item} {this routine sets up the needed parameters and then } { calls HMShowMenuBalloon} MyShowMenuBalloon(itemChosen, itemRect); END; END; END; END; END;