Order the book from O'Reilly

Previous PageTable Of ContentsIndexNext Page

In this chapter:

 7.  Menus

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

Top Of Page

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

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

Top Of Page

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

Top Of Page

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

Top Of Page

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
Copyright © 1999, O'Rielly and Associates, Inc.
Published on the web by permission of O'Rielly and Associates, Inc. Contents modified for web display.

Previous PageTop Of PageTable Of ContentsIndexNext Page