In this chapter:
In this chapter, we explain how to create menus. Along with a discussion of the menu source code, we highlight potential problems and show workarounds for them. First, however, we need to clarify some terminology and describe the user interface for menus.
Menu User Interface |
Every Palm application that contains menus uses the same framework for them. If you look at Figure 7-1, you see a sample menubar containing two menus: Customer and Options. The open Customer menu contains three menu items: New Customer, Delete Customer, Edit Customer.
Figure 7- 1.
Application menubar, menus, and menu items
Note that menu items commonly have shortcuts associated with them. These are Graffiti letters that are unique to each menu item. By doing the stroke-and-letter shortcut, the user can perform the operation without first selecting the menu item. For example, the "/ N" brings up a New Customer form. As a rule, you should add these shortcuts to menu items wherever necessary and always with standard menu items. Make sure that the most frequent operations have shortcuts, and don't put a shortcut on an infrequent action (such as the About Box).
Common Menu Shortcuts
Table 7-1 contains common menus and the standard shortcut letters used with them. Keep the same letters so that users can expect the same behavior from different applications. Items with an asterisk are less common.
-Table 7- 1.
Standard Shortcut Letters
Record |
Edit |
Options |
|||
New<Item> |
N |
Undo |
U |
*Font |
F |
Delete <Item> |
D |
Cut |
X |
Preferences |
R |
*Attach <Item> |
A |
Copy |
C |
*Display Options |
Y |
Beam <Item> |
B |
Paste |
P |
*HotSync Options |
H |
*Purge |
E |
Select All |
S |
||
Keyboard |
K |
||||
Graffiti Help |
G |
Arranging Menus
Menus can also be arranged with separator bars in them to group similar items together (see Figure 7-2). Note that menus and menu items are never dimmed (grayed out). We discuss how to handle menu items that aren't applicable in certain situations in "Handling Unusable Menu Items" on page 190.
Standard Menu Items
Edit menu
Most forms with a text field should have an Edit menu containing, at a minimum, Undo, Cut, Copy, Paste. Most Edit menus also include Select All, Keyboard, and Graffiti Help. See Figure 7-2 for a standard Edit menu.
NOTE: Password dialogs shouldn't support Cut, Copy, or Paste. |
About application
You should have an About myApplication menu item; it is usually found in an Options menu. This menu should bring up an alert/dialog containing information about your application (who wrote it, version number, name, email address, web site, and technical support information). This dialog is often called an About Box.
Applications Can Have Multiple Sets of Menus
A set of menus is always associated with a particular form or window in the application. Thus, if you look at the Order form of our Sales application in Figure 7-2, you see that it has its own new set of menus.
Figure 7- 2.
The Order form of the Sales application
You should also note that different forms in an application may share a similar set of menus and menu items. For example, the Order form and the Customer Details form both have an Edit menu with the same items (see Figure 7-3).
Figure 7- 3.
The Edit menu in two different forms
Menu Resources |
Menus are created and stored as resources in your application. Depending on the development environment you use, you make the menus in different ways. First, we show you how to create menus with PilRC (the GNU PalmPilot SDK resource creator) and then using CodeWarrior's Constructor. In either case, the menus end up in a .PRC resource file.
The .PRC file
The .PRC file contains an MBAR resource for each menubar in your application. These MBAR resources are in turn composed of a number of menus, each of which contains menu items. Each menu item has associated with it a menu ID and, optionally, a shortcut key. When you design your menubars, you need to make sure that no two menu items in menus displayed at the same time have the same key shortcut-each shortcut must be unique.
Using PilRC
To create your .PRC file using GCC, use the PilRC Resource Compiler. PilRC allows you to directly create menubar resources; later you will learn that this is a tremendous advantage. PilRC is a textual, rather than a graphical, resource editor.
Here's a simple MBAR (ID 1000) Resource with two menus, each with two items (the item IDs are 1001, 1002, 1011, and 1012):
MENU 1000 BEGIN PULLDOWN "Menu1" BEGIN MENUITEM "Item1" 1001 "I" MENUITEM "Item2" 1002 END PULLDOWN "Menu2" BEGIN MENUITEM "Item3" 1011 MENUITEM "Item4" 1012 END END
To define the shortcut keys of menu items in PilRC, simply supply the character surrounded by double quotes. In our simple example, the first menu item has a shortcut key of "I".
NOTE: Of course, you'll commonly use named constants instead of raw numbers in your .RCP file. Here is a good technique for numbering your resources: make your MBAR resource IDs multiples of 1000 and your menu resource IDs multiples of 10 starting 1 unit higher (this assumes that no menu will have more than 10 items in it). For example: |
MBAR MENU |
1000 1001 |
1011 |
2000 2001 |
2011 |
2021 |
Using Constructor
While simple to use, Constructor does give you some problems with menu construction. First, look at how Constructor creates menus, and then we will describe the problems.
How Constructor creates menus
You create menus in a Constructor project by graphically laying out menu elements. Figure 7-4 shows you how simple this is to do. The left side of Figure 7-4 contains a simple Constructor project with a couple of menubars and menus. The record menu on the right side contains some menu items (two of which have shortcuts) that were selected from the Constructor Edit menu.
Figure 7- 4.
A small project showing the graphical interface for menu creation in Constructor
How Constructor creates MENU resources
Constructor doesn't directly create MBAR resources in the format needed by a .PRC file. Instead, it creates MENU resources (one for each menu). First, you graphically lay out the menus, then Constructor takes over and generates unique resource IDs for all these menus (see Figure 7-5). It does so by keeping track of the MENU resources in an MBAR resource via a list of MENU resource IDs.
-Figure 7- 5.
Editing a MENU resource in Constructor
When you edit a MENU resource, you can edit the resource ID, the text of each menu item, and the shortcut key. You can do all of this in Constructor. What you can't do is edit a menu ID. Here is why: CodeWarrior uses PalmRez, a post-linking tool, to create the MBAR resource in the .PRC file. It uses the MBAR and MENU resources in your .RSC file. PalmRez assigns the menu IDs of each item sequentially, starting with a base ID stored in the MENU resource itself.
This base ID is not the MENU resource ID, and you can't see it in Constructor. The base ID is used by Constructor to automatically generate the menu IDs. When you create a new menu, duplicate one, or modify the resource ID of a menu, Constructor automatically changes the base ID as well.
Figure 7-6 shows the relationship between the MBAR and the MENU resources you edit in Constructor and the final MBAR resource in the .PRC file.
Figure 7- 6.
Conceptual relationship between MENU and MBAR resources in Constructor and the MBAR resource in a .PRC file
Two problems with menus
Generating menus with Constructor can lead to two problems. The first one has to do with duplicate menus. Because of the way PalmRez processes the MENU resources (deleting each MENU resource as it processes it), you can't share one MENU in more than one MBAR. This is a bigger problem than you might at first imagine. For example, in our Sales application we have identical Edit menus in our Customer Details and Order forms (Figure 7-3). Even though they are the same, we still have to create two separate menus in Constructor. That means more code to maintain and the possibility of more mistakes.
The second problem has to do with the way base IDs are created. Constructor sets the base ID of a menu to the menu's resource ID. This makes it impossible in Constructor for different menus to share the same menu IDs.
If you have simple menus and menubars, with no need to have the same menu or menu items multiple times, Constructor works fine. Otherwise, switch to creating your menus textually.
Creating your menus textually with PalmRez
PalmRez is a resource compiler like PilRC, but uses a different format for resources. PalmRez is based on the Macintosh Programmer's Workshop (MPW) Rez tool, which is designed to create Macintosh resources.
PalmRez compiles files with the .r extension. Instead of creating your menus and menubars in Constructor, you create a .r file that contains your menu and menubar definitions.
PalmRez has to be told the format of MENU and MBAR resources. Here's a file, MenuDefs.r, that contains the definitions of those types:
type 'MENU' { integer SYS_EDIT_MENU = 10000; // base menu ID fill byte[12]; pstring; // menu title array { pstring SEPARATOR = "-"; // item text fill byte; char NONE = "\$00"; // Graffiti shortcut fill byte[2]; }; byte = 0; // terminator }; type 'MBAR' { integer = $$CountOf(menus); array menus { integer; // menu ID }; };
Include MenuDefs.r in your resource file. Here's an example MyMenus.r file defining a menubar with two menus in it:
#include "MenuDefs.r" resource 'MENU' (1001) { 1001, // base ID "Menu1", { "Item1", "I"; "Item2", NONE; } }; resource 'MENU' (1011) { 1011, // base ID "Menu2", { "Item3", NONE; "Item4", NONE; } }; resource 'MBAR' (1000) { {1001, 1011} };
Associating Menubars with Forms
When you create a form, you specify the ID of a menubar to go along with it. A form with the value of 0 has no associated menubar. The Palm OS automatically uses the menubar of a form while the form is active. More than one form can use the same menubar.
Specifying the menubar of a form in Constructor
If you look at Figure 7-7, you will see that you simply supply the resource value of a menubar ID that you want that form to use.
Figure 7- 7.
Forms have a menubar ID; this one has a menubar ID of 1000
Specifying the menubar of a form in PilRC
Specifying a menubar ID for a particular form is just as simple in PilRC:
FORM ID 1000 at (0, 0, 160, 160) MENUID 1000 BEGIN END
Application Code for Menus |
There's not a lot of code that needs to be added to support menus. Further, what you do add is straightforward and in some cases standard from application to application. The three routines that have some responsibility for handling menus are:
There is some cookbook code to add that handles the Edit menu, and we need to handle the About menu, as well.
MenuHandleEvent
This routine is responsible for handling menu-specific events. Chapter 4, Structure of an Application, contains a description of MenuHandleEvent and its role within your main event loop. Here is an example found in a main event loop:
do { EvtGetEvent(&event, evtWaitForever); if (! SysHandleEvent(&event)) if (! MenuHandleEvent(0, &event, &error)) if (! ApplicationHandleEvent(&event)) FrmDispatchEvent(&event); } while (event.eType != appStopEvent);
MyFormHandleEvent
Your form's event handler receives an event of type menuEvent if a menu item is chosen. If you have more than one or two menu items handled by a form, it is customary to put the menu item dispatching in a separate routine, MyFormHandleMenuEvent. Here is our event handler:
static Boolean MyFormHandleEvent(EventPtr event) { Boolean handled = false; #ifdef __GNUC__ CALLBACK_PROLOGUE #endif switch (event->eType) { /* code removed */ case menuEvent: handled = MyFormHandleMenuEvent(event->data.menu.itemID); break; /* code removed */ } #ifdef __GNUC__ CALLBACK_EPILOGUE #endif return handled; }
MyFormHandleMenuEvent
This is the routine that actually handles the menu items:
static Boolean MyFormHandleMenuEvent(Word menuID) { Boolean handled = false; /* declarations removed */ switch (menuID) { case MenuItem1: // code removed that handles MenuItem1 handled = true; break; case MenuItem2: // code removed that handles MenuItem2 handled = true; break; } return handled; }
Handling Items in the Edit Menu
The good news about the Edit menu is that there is a cookbook approach to handling each of the menu items. The bad news is that it takes a slight amount of work to avoid duplicating this cookbook code throughout your application. We show you how to avoid duplicated code in "A Procedure for Handling Common Menu Items" later in this chapter.
First, let's look at the cookbook code for handling each of the edit menu items:
// returns field that has the focus, if any, including in embedded tables static FieldPtr GetFocusObjectPtr (void) { FormPtr frm; Word focus; FormObjectKind objType; frm = FrmGetActiveForm (); focus = FrmGetFocus (frm); if (focus == noFocus) return (NULL); objType = FrmGetObjectType (frm, focus); if (objType == frmFieldObj) return (FrmGetObjectPtr (frm, focus)); else if (objType == frmTableObj) return (TblGetCurrentField (FrmGetObjectPtr (frm, focus))); return NULL; } Boolean void MyFormHandleMenuEvent(Word menuID) { FieldPtr fld; switch (menuID) { /* code for other menu items removed */ case EditUndo: case EditCut: case EditCopy: case EditPaste: case EditSelectAll: fld = GetFocusObjectPtr(); if (!fld) return false; if (menuID == EditUndo) FldUndo(fld); else if (menuID == EditCut) FldCut(fld); else if (menuID == EditCopy) FldCopy(fld); else if (menuID == EditPaste) FldPaste(fld); else if (menuID == EditSelectAll) FldSetSelection (fld, 0, FldGetTextLength (fld)); return true; case EditKeyboard: SysKeyboardDialog(kbdDefault); return true; case EditGrafitti: SysGraffitiReferenceDialog(referenceDefault); return true; } return false; }
The emphasized calls are standard Palm OS calls that you use to handle the Edit menu. The cookbook can be used with each of your menubars that contain an Edit menu.
The About Menu
The Palm OS provides a routine, AbtShowAbout, that allows the display of an application name and icon (see Figure 7-8). As you can see, it isn't appropriate for anything but the built-in applications.
Figure 7- 8.
AbtShowAbout shows a 3Com-specific About Box
It is more useful to handle the About menu item by creating a simple alert and displaying it with FrmAlert (see Figure 7-9):
case OptionsAbout: FrmAlert(AboutBoxAlert); break;
This is fine if all you want is some text. If you have pictures, however, create a modal form and display it with FrmDoDialog. "Modal Dialogs" on page 101 describes how to do that.
Figure 7- 9.
An About Box displayed using FrmAlert
Menu Erase Status
There is a problem with menus and refreshing the display of the Palm screen that you should take into account in your applications. Before describing the fix to the problem, let us explain what the user does and when the problem occurs.
When the user chooses a menu item using a shortcut key, the Menu Manager displays the status of this task in the lower left of the display. First, the Menu Manager displays the word "Command" (see Figure 7-10) to indicate that a stroke has been noticed. If the user then writes a valid shortcut key, the Menu Manager displays the menu item name (see Figure 7-11) and dispatches the menu event for the application to handle.
Figure 7- 10.
Menu status after entering a shortcut character
Figure 7- 11.
Menu status after entering a shortcut character and then a menu shortcut key
This shortcut key status is shown on the screen for a couple of seconds: just enough time for the user to read it and get feedback that the Palm device has noticed the stroke. After this, the status update automatically goes away.
There is one case in which you need to clear the status yourself because a problem occurs. The Palm OS notes when the user chooses a menu item using a shortcut key and saves the screen bits underneath the area where the word "Command" is displayed. Once the timer goes off, the bits are restored. If you have changed the screen contents in the meantime, the bits that are restored are stale. Figure 7-12 shows the problem.
Figure 7- 12.
Menu code changing contents of lower left of screen without calling MenuEraseStatus
A common case where your menu code would change the screen contents is in an alert or another form. Nicely enough, the Palm OS catches this case automatically and erases the status for you. You will have trouble, however, when you change the contents of the current form. Here's some sample code that shows the problem in Figure 7-12 (the code shows a previously hidden form object):
case ShowLabelMenuItem: { Word index; FormPtr frm; frm = FrmGetActiveForm(); index = FrmGetObjectIndex(frm, CustomersTestLabel); FrmShowObject(frm, index); } break;
Deal with this problem by doing your own erasing. The call to clear the status is MenuEraseStatus. The fix to the code that exhibits the problem is simply a call to MenuEraseStatus before modifying the screen:
case ShowLabelMenuItem: { Word index; FormPtr frm; MenuEraseStatus(); frm = FrmGetActiveForm(); index = FrmGetObjectIndex(frm, CustomersTestLabel); FrmShowObject(frm, index); } break;
You have to be careful with this fix, however, as it is a double-edged sword. You don't want to call MenuEraseStatus unnecessarily, as there is a price to pay. When you call it, the user gets only a very brief glimpse of the confirmed menu item. You wiped out the confirmed menu item when you restored the screen bits. This cure is still better than the problem, however, as a mess on the screen is worse than wiping out the status quickly.
NOTE: A good way to ensure that you have implemented MenuEraseStatus when necessary is to use shortcut characters in your testing. This lets you determine when you need to make a call to MenuEraseStatus to clean up screen trash. |
NOTE: The folks at Palm Computing are getting wiser. Unfortunately, not until OS 2.0 did they fix this problem some of the time. The earlier 1.0 OS does not even erase the status before putting up another form. If you're supporting the 1.0 OS, you need to call MenuEraseStatus in any menu-handling code that puts up a form or alert. |
Forms that have buttons at the bottom that don't ever change are obviously not affected by this problem. For these forms, the menu status automatic timed erasing works just fine. It's only the few forms with changing data at the bottom left that are affected.
Handling Unusable Menu Items
The Menu Manager APIs don't provide a mechanism for adding or deleting menu items dynamically. In addition, there's no way to visually disable them (by graying them). This, of course, immediately raises the question of what you should do if there are menus or menu items that can't be used in certain situations.
One possibility is to present an alert to the user explaining why it's not possible to do what was requested. That's the strategy used by the built-in Expense application when the user tries to delete an item and nothing is selected (see Figure 7-13).
Figure 7- 13.
Deleting an item in Expense when nothing is selected
This is certainly better than having the menu item appear and disappear as an item is selected and deselected-a tactic guaranteed to make users foam at the mouth. Disappearing and reappearing things make many people doubt their sanity, as they often have absolutely no idea how to make a menu item reappear.
A good time to remove a menu item
There are cases, however, where you do want to remove menu items. For example, you may have a menu item that will never be present on a user's device. An obvious case of this is beaming, which is available only if OS 3.0 is present. A well-designed application ought to figure out what OS it is running under and behave accordingly. It should have the Beam item show on 3.0 devices and disappear on pre-3.0 devices.
In order to implement this nice design, you actually use a rather simplistic solution-two menu bars, each with its own copy of the menus. One of the menus has a Beam item, the other doesn't.
NOTE: Since applications built with CodeWarrior (Release 4, as of this writing) have their menu IDs automatically assigned, you should create these menus carefully. To make sure that menu items that are in both menubars remain in the same position, put the Beam menu item at the bottom of the 3.0 version. |
Specify one menubar as the form's menubar as part of the resource (let's make it the one with the Beam item). You may need to change the menubar at runtime using FrmSetMenu, which changes the menubar ID of a form. Make the change when you open the form with code like this:
if (sysGetROMVerMajor(gRomVersion) < 3) FrmSetMenu(FrmGetActiveForm(), CustomersnobeamMenuBar);
Tools for implementing duplicate menus
If you want to have multiple menus that share the same menu IDs, you need to create your menus textually. If you use PilRC, you're doing that already (just make sure duplicate menu items share the same menu ID). If you use CodeWarrior, you need to create an .r file with the textual menus (duplicate menus should share the same base ID).
A Procedure for Handling Common Menu Items
We have already noted that you often have more than one form with an Edit menu-especially in forms with text fields. It might also make sense to have your About menu item present often. In such cases, you should use some common method to handle these and other standard menu items.
You typically put the About menu in the Options menu. Because the Options menu can and does occur in more than one form, it makes a lot of sense to leave the About menu in every instance. It is less confusing to the user if it is always there.
Your first step is to use the same menu IDs for the shared menu items. Next, you need a function to handle the common menu items such as HandleCommonMenuItems. It should work for the standard Edit menu items, as well as the About menu item. Example 7-1 shows the code to use.
-Example 7- 1. A Routine to Handle Menu Items Common to More than One Form
static Boolean HandleCommonMenuItems(Word menuID) { FieldPtr fld; switch (menuID) { case EditUndo: case EditCut: case EditCopy: case EditPaste: case EditSelectAll: fld = GetFocusObjectPtr(); if (!fld) return false; if (menuID == EditUndo) FldUndo(fld); else if (menuID == EditCut) FldCut(fld); else if (menuID == EditCopy) FldCopy(fld); else if (menuID == EditPaste) FldCopy(fld); else if (menuID == EditSelectAll) FldSetSelection (fld, 0, FldGetTextLength (fld)); return true; case EditKeyboard: SysKeyboardDialog(kbdDefault); return true; case EditGrafitti: SysGraffitiReferenceDialog(referenceDefault); return true; case OptionsAbout: FrmAlert(AboutBoxAlert); return true; default: return false; } }
Call HandleCommonMenuItems from each of your menu-handling routines:
Boolean void MyFormHandleMenuEvent(Word menuID) { if (HandleCommonMenuItems(menuID) return true; else switch (menuID) { // other items here } }
Adding Menus to the Sample Application |
Now it is time to add the menus to our Sales application. The menubars are added first. Next, we set up our definitions for our menu items and menubars. Once that is in place, we can create our code to handle common menu items and the functions we need to handle our forms. Our last step is to make sure the main event loop in our application correctly calls our menu-handling function.
The Menubars
The application has five menubars, the first of which is shown in Figure 7-14. This menubar is for the Order form, which contains the menus Record, Edit, and Options.
Figure 7- 14.
The Order menubar on a pre-3.0 device
The second menubar is like the first, but has a Beam Customer item at the end of the Record menu (see Figure 7-15).
-Figure 7- 15.
The Order menubar on a 3.0 or later device
The third menubar, DialogWithInputField, is used for dialogs that have textual input fields (see Figure 7-16).
Figure 7- 16.
The menus for dialogs with input fields
The fourth and fifth bars are used separately, depending on whether the application is running on a 3.0 or earlier device. As you can see in Figure 7-17, the difference is whether beaming shows up as a menu item. We have different menus for different devices so that a pre-3.0 user doesn't get confused about either the application's or device's capability.
Figure 7- 17.
The Customer menus for 3.0 and pre-3.0 devices
Menu Definitions
The first thing to do is get our menu definitions set up all neat and tidy. Example 7-2 shows the menu item definitions we've created in a separate text file. Example 7-3 shows the definition of the menubars for the order items in PilRC format (used with GCC). Example 7-4 shows the definition in PalmRez format (used with CodeWarrior).
Example 7- 2. SalesMenus.h, Defining Constants for Menus and Menubars
#define CustomersMenuBar 1000 #define CustomersNoBeamMenuBar 1100 #define OrderMenuBar 1200 #define OrderNoBeamMenuBar 1300 #define DialogWithInputFieldMenuBar 1400 #define CustomersCustomerMenu 1001 #define CustomersOptionsMenu 1011 #define CustomersNoBeamCustomerMenu 1101 #define CustomersNoBeamOptionsMenu 1111 #define OrderRecordMenu 1201 #define OrderEditMenu 1211 #define OrderOptionsMenu 1221 #define OrderNoBeamRecordMenu 1301 #define OrderNoBeamEditMenu 1311 #define OrderNoBeamOptionsMenu 1321 #define DialogWithInputFieldEditMenu 1401 #define DialogWithInputFieldOptionsMenu 1411 #define CustomerBase 2001 #define CustomerNewCustomer 2001 #define CustomerBeamAllCustomers 2002 #define OptionsBase 2101 #define OptionsAboutSales 2101 #define RecordBase 2201 #define RecordDeleteItem 2201 #define RecordDeleteCustomer 2202 #define RecordCustomerDetails 2203 #define RecordBeamCustomer 2204 #define EditBase 2301 #define EditUndo 2301 #define EditCut 2302 #define EditCopy 2303 #define EditPaste 2304 #define EditSelectAll 2305 // separator #define EditKeyboard 2307 #define EditGrafitti 2308
Example 7- 3. Part of Sales.rcp File, Used for Menus with GCC
#include "SalesMenus.h" MENU ID OrderMenuBar BEGIN PULLDOWN "Record" BEGIN MENUITEM "Delete Item..." ID RecordDeleteItem "D" MENUITEM "Delete Customer..." ID RecordDeleteCustomer MENUITEM "Customer Information..." ID RecordCustomerDetails "E" MENUITEM "Beam Customer" ID RecordBeamCustomer "B" END PULLDOWN "Edit" BEGIN MENUITEM "Undo" ID EditUndo "U" MENUITEM "Cut" ID EditCut "X" MENUITEM "Copy" ID EditCopy "C" MENUITEM "Paste" ID EditPaste "P" MENUITEM "Select All" ID EditSelectAll "S" MENUITEM "-" AUTOID MENUITEM "Keyboard" ID EditKeyboard "K" MENUITEM "Grafitti " ID EditGrafitti "G" END PULLDOWN "Options" BEGIN MENUITEM "About Sales" ID OptionsAboutSales END END MENU ID OrderNoBeamMenuBar BEGIN PULLDOWN "Record" BEGIN MENUITEM "Delete Item..." ID RecordDeleteItem "D" MENUITEM "Delete Customer..." ID RecordDeleteCustomer MENUITEM "Customer Information..." ID RecordCustomerDetails "E" END PULLDOWN "Edit" BEGIN MENUITEM "Undo" ID EditUndo "U" MENUITEM "Cut" ID EditCut "X" MENUITEM "Copy" ID EditCopy "C" MENUITEM "Paste" ID EditPaste "P" MENUITEM "Select All" ID EditSelectAll "S" MENUITEM "-" AUTOID MENUITEM "Keyboard" ID EditKeyboard "K" MENUITEM "Grafitti " ID EditGrafitti "G" END PULLDOWN "Options" BEGIN MENUITEM "About Sales" ID OptionsAboutSales END END MENU ID DialogWithInputFieldMenuBar BEGIN PULLDOWN "Edit" BEGIN MENUITEM "Undo" ID EditUndo "U" MENUITEM "Cut" ID EditCut "X" MENUITEM "Copy" ID EditCopy "C" MENUITEM "Paste" ID EditPaste "P" MENUITEM "Select All" ID EditSelectAll "S" MENUITEM "-" AUTOID MENUITEM "Keyboard" ID EditKeyboard "K" MENUITEM "Grafitti " ID EditGrafitti "G" END PULLDOWN "Options" BEGIN MENUITEM "About Sales" ID OptionsAboutSales END END MENU ID CustomersMenuBar BEGIN PULLDOWN "Customer" BEGIN MENUITEM "New Customer" ID CustomerNewCustomer "N" MENUITEM "Beam all Customers" ID CustomerBeamAllCustomers "B" END PULLDOWN "Options" BEGIN MENUITEM "About Sales" ID OptionsAboutSales END END MENU ID CustomersNoBeamMenuBar BEGIN PULLDOWN "Customer" BEGIN MENUITEM "New Customer" ID CustomerNewCustomer "N" END PULLDOWN "Options" BEGIN MENUITEM "About Sales" ID OptionsAboutSales END END
Example 7- 4. . Sales.r, Used for Menus with CodeWarrior
#include "MenuDefs.r" #include "SalesMenus.h" resource 'MENU' (OrderRecordMenu) { RecordBase, "Record", { "Delete Item...", "D"; "Delete Customer...", NONE; "Customer Information...", "E"; "Beam Customer", "B"; } }; resource 'MENU' (OrderEditMenu) { EditBase, "Edit", { "Undo", "U"; "Cut", "X"; "Copy", "C"; "Paste", "P"; "Select All", "S"; SEPARATOR, NONE; "Keyboard", "K"; "Graffiti", "G"; } }; resource 'MENU' (OrderOptionsMenu) { OptionsBase, "Options", { "About Sales", NONE; } }; resource 'MENU' (OrderNoBeamRecordMenu) { RecordBase, "Record", { "Delete Item...", "D"; "Delete Customer...", NONE; "Customer Information...", "E"; } }; resource 'MENU' (OrderNoBeamEditMenu) { EditBase, "Edit", { "Undo", "U"; "Cut", "X"; "Copy", "C"; "Paste", "P"; "Select All", "S"; SEPARATOR, NONE; "Keyboard", "K"; "Graffiti", "G"; } }; resource 'MENU' (OrderNoBeamOptionsMenu) { OptionsBase, "Options", { "About Sales", NONE; } }; resource 'MBAR' (OrderMenuBar) { {OrderRecordMenu, OrderEditMenu, OrderOptionsMenu} }; resource 'MBAR' (OrderNoBeamMenuBar) { {OrderNoBeamRecordMenu, OrderNoBeamEditMenu, OrderNoBeamOptionsMenu} }; resource 'MENU' (DialogWithInputFieldEditMenu) { EditBase, "Edit", { "Undo", "U"; "Cut", "X"; "Copy", "C"; "Paste", "P"; "Select All", "S"; SEPARATOR, NONE; "Keyboard", "K"; "Graffiti", "G"; } }; resource 'MENU' (DialogWithInputFieldOptionsMenu) { OptionsBase, "Options", { "About Sales", NONE; } }; resource 'MBAR' (DialogWithInputFieldMenuBar) { {DialogWithInputFieldEditMenu, DialogWithInputFieldOptionsMenu} }; resource 'MENU' (CustomersCustomerMenu) { CustomerBase, "Customer", { "New Customer...", "N"; "Beam all Customers", "B"; } }; resource 'MENU' (CustomersOptionsMenu) { OptionsBase, "Options", { "About Sales", NONE; } }; resource 'MENU' (CustomersNoBeamCustomerMenu) { CustomerBase, "Customer", { "New Customer...", "N"; } }; resource 'MENU' (CustomersNoBeamOptionsMenu) { OptionsBase, "Options", { "About Sales", NONE; } }; resource 'MBAR' (CustomersMenuBar) { {CustomersCustomerMenu, CustomersOptionsMenu} }; resource 'MBAR' (CustomersNoBeamMenuBar) { {CustomersNoBeamCustomerMenu, CustomersNoBeamOptionsMenu} };
Handling Common Menus
The Sales application has a HandleCommonMenuItems, as shown earlier in Example 7-1. The ItemHandleEvent routine calls HandleCommonMenuItems in case of a menu event:
static Boolean ItemHandleEvent(EventPtr event) { Boolean handled = false; #ifdef __GNUC__ CALLBACK_PROLOGUE #endif switch (event->eType) { // code deleted that handles other kinds of events case menuEvent: handled = HandleCommonMenuItems(event->data.menu.itemID); } #ifdef __GNUC__ CALLBACK_EPILOGUE #endif return handled; }
OrderHandleMenuEvent is responsible for the menu items for the Order form:
static Boolean OrderHandleMenuEvent(Word menuID) { Boolean handled = false; if (HandleCommonMenuItems(menuID)) handled = true; else switch (menuID) { case RecordDeleteItem: if (!gCellSelected) FrmAlert(NoItemSelectedAlert); else // code deleted that deletes an item handled = true; break; case RecordCustomerDetails: // code deleted that opens customer details dialog handled = true; break; case RecordBeamCustomer: BeamCustomer( GetRecordNumberForCustomer(gCurrentOrder->customerID)); handled = true; break; case RecordDeleteCustomer: // code deleted that deletes a customer break; } return handled; }
It is called by OrderHandleEvent if a menu event occurs:
static Boolean OrderHandleEvent(EventPtr event) { Boolean handled = false; #ifdef __GNUC__ CALLBACK_PROLOGUE #endif switch (event->eType) { // code deleted that handles other kinds of events case menuEvent: handled = OrderHandleMenuEvent(event->data.menu.itemID); } #ifdef __GNUC__ CALLBACK_EPILOGUE #endif return handled; }
The New Customer/Edit Customer dialog has an event handler that has to handle the common menu items:
static Boolean CustomerHandleEvent(EventPtr event) { #ifdef __GNUC__ CALLBACK_PROLOGUE #endif // code removed that handles other types of events } else if (event->eType == menuEvent) { if (HandleCommonMenuItems(event->data.menu.itemID)) return true; } #ifdef __GNUC__ CALLBACK_EPILOGUE #endif return false; }
Checking the OS Version Number
The Customers form has two different menubars, one with a Beam item. Here's where one is changed if we're running on a pre-3.0 system:
static void CustomersFormOpen(void) { // code removed that initializes the customer list if (sysGetROMVerMajor(gRomVersion) < 3) FrmSetMenu(FrmGetActiveForm(), CustomersNoBeamMenuBar); }
The Customers Form
Here's the menu-handling code for the Customers form:
static Boolean CustomersHandleMenuEvent(Word menuID) { Boolean handled = false; if (HandleCommonMenuItems(menuID)) return true; else switch (menuID) { case CustomerNewCustomer: // code deleted that creates a new customer handled = true; break; case CustomerBeamAllCustomers: // code deleted that beams all customers handled = true; break; } return handled; } static Boolean CustomersHandleEvent(EventPtr event) { Boolean handled = false; #ifdef __GNUC__ CALLBACK_PROLOGUE #endif switch (event->eType) { case menuEvent: handled = CustomersHandleMenuEvent(event->data.menu.itemID); break; // code deleted that handles other events } #ifdef __GNUC__ CALLBACK_EPILOGUE #endif return handled; }
This is all the code and definitions necessary to make our menus work. You saw that our strategy for menus included a design preference for making menu items completely disappear if the application is present on a device that doesn't use the feature (as in beaming). There were also a few problems you encountered when you create duplicate types of menus and when handling the display of the Graffiti shortcut status in the bottom left corner of the unit.
At this point, the Sales application is almost complete-you have all the essential UI elements and code in place. What is left are just a few bits, though they are important bits. You will add support for these features the next chapter for tables, find, and beaming.
* This is almost certain to change in future releases of the SDK. Check your version to see if Palm Computing has changed AbtShowAbout to support third-party About Boxes.
Palm Programming: The Developer's Guide