Apple IIGS Toolbox - Build a menu from resources

System Software 5.0 and later provide the NewMenu2 family of routines, which can construct menus from resource-based templates.

This article shows how to build a menu with resource IDs and resource templates.

Note:
This article uses ORCA C, available from Juiced.GS. Byteworks sells all of their software as Opus II, including the C compiler.

At the top of the menu system is the MenuBar. The MenuBar holds one or more Menus. The end user doesn’t see the MenuBar, they see the Menu(s) the MenuBar holds.

Menus hold zero or more MenuItems.

For this demonstration menu, I will use the following layout:

Menu
    File
        Open
        ---
        Quit
    Edit
        Find
        Find Again
        ---
        Select All

Sharing ids between C code and resources

The ORCA Rez compiler needs items declared before they’re used, so let’s start with declaring the menu IDs for all the menu items. Since these IDs will be used in both the C code (for identifying menu items) and the resources, I suggest putting common definitions in a separate header file. This article assumes it’s named samplemenu.h.

Note: Menu and menu-item IDs should be unique within the application.
Avoid $0000 and $FFFF, which the Menu Manager reserves for special/internal use.

/* samplemenu.h */

#define fileOpen         256
#define fileSeparator    257
#define fileQuit         258

#define editFind         260
#define editFindAgain    261
#define editSeparator    262
#define editSelectAll    263

Above the MenuItems are the Menus. Those need to be defined as well.

#define fileMenu         270
#define editMenu         271

The top-level menu rMenuBar resource also needs a resource ID.

#define menuBarId        280

Adding strings

To define a string for use as a resource, declare an ID for it in the shared header then add it to the .rez file. The rPString type is a Pascal-style string.

/* samplemenu.h */

#define fileOpenString          300
#define fileSeparatorString     301
#define fileQuitString          302
#define fileMenuString          303

/* samplemenu.rez */

resource rPString (fileOpenString) { "Open..." };
resource rPString (fileSeparatorString) { "-" };
resource rPString (fileQuitString) { "Quit" };

resource rPString (fileMenuString) { "File" };

Why Pascal strings?

Resources are referenced by id and resource type combination. I define the resource IDs using unique ids to simplify the demonstration. Resource IDs can be re-used across types. So an rMenuItem could share a resource ID with the rPString for that rMenuItem.

When we declare strings in resources and refer to them by their resource id, the ID tells the Menu Manager which resource to retrieve. The Menu Manager supplies the resource type rPString when loading strings by resource ID. That is why we have to use Pascal strings as well.

Building out MenuItems

The MenuItem resource type is rMenuItem. This has the menu ID, the item character (accelerator), item alternate character, whether the item is checked, flags for the string, and the string reference.

If you’re reading Volume 3, chapter 37, you might assume you’ll have to recreate the structure of every resource field somehow, including fields and flags. That is not the case, and the ORCA resource compiler will emit errors if you do.

The ORCA Types.rez includes definitions for the resource types and configures them for a specific use. This means supplying default values for fields and structures for lists of items.

For MenuItem, the Toolbox Reference says the template version must be set to zero, for example. The ORCA Types.rez definition handles this for you.

The reference type is a two-bit value (0=pointer, 1=handle, 2=resource ID) that must be moved into the appropriate bit field. The Types.rez file supplies the field-position constants such as ItemTitleRefShift, so multiplying by the reference value places the bits in the correct position.

/* samplemenu.h */
#define refByPointer    $0000
#define refByHandle     $0001
#define refByResourceId $0002

/* samplemenu.rez */
resource rMenuItem (fileOpen) {
    fileOpen,           /* menu item ID */
    "\000",             /* Keystroke equivalent to this menu item. */
    "\000",             /* Alternate keystroke equivalent to this menu item. */
    0,                  /* Character to show when item is checked. For 0, don't show anything. */
    refByResourceId * ItemTitleRefShift,    /* shift value into position */
    fileOpenString
};

resource rMenuItem (fileSeparator) {
    fileSeparator,
    "\000",
    "\000",
    0,
    /* Separators are normally disabled */
    (refByResourceId * ItemTitleRefShift) + rMIDisabled,
    fileSeparatorString
};

resource rMenuItem (fileQuit) {
    fileQuit,
    "\000",
    "\000",
    0,
    refByResourceId * ItemTitleRefShift,
    fileQuitString
};

Now we have enough MenuItem resources to build the File Menu.

resource rMenu (fileMenu) {
    fileMenu,
    (refByResourceId * MenuTitleRefShift) + (refByResourceId * ItemRefShift) + fAllowCache,
    fileMenuString,
    {
        fileOpen,
        fileSeparator,
        fileQuit
    }
};

At the top level, we only need to describe which menus are contained in the MenuBar.

resource rMenuBar (menuBarId) {
    {
        fileMenu,
        editMenu
    }
};

Bringing it together

Conceptually, the resource hierarchy now looks like:

rMenuBar
  |
  +-- rMenu "File"
  |     |
  |     +-- rPString "File"
  |     +-- rMenuItem "Open..."
  |     |     +-- rPString "Open..."
  |     +-- rMenuItem separator
  |     |     +-- rPString "-"
  |     +-- rMenuItem "Quit"
  |           +-- rPString "Quit"
  |
  +-- rMenu "Edit"
        ...

Loading the MenuBar

To use this template style, create the MenuBar with NewMenuBar2().

#include <Menu.h>

MenuBarRecHndl menuBarHandle = NewMenuBar2(refByResourceId, (Long)menuBarId, (Pointer)NULL);
SetSysBar(menuBarHandle);
SetMenuBar(NULL);
FixMenuBar();
DrawMenuBar();

The NewMenuBar2() function will traverse the resource template structure starting with the top-level MenuBar and will load the resources from the IDs supplied.

The reason we call SetSysBar(menuBarHandle); and SetMenuBar(NULL); is to avoid the system menu bar drawing over our menu before it’s installed. This is covered in Apple IIGS Toolbox Reference Volume 3, p. 37-25.

Padding menu strings with spaces

The Menu Manager doesn’t automatically create pleasant spacing between menus.
To fix this, Apple Technote #5, “Window and Menu Titles” suggests placing one space on both sides of the menu string for 320 graphics mode, and two for 640 mode.

For 640x200 graphics mode, that would look like:

resource rPString (fileMenuString) { "  File  " };

Further reading
Apple IIGS Toolbox Reference, Volume 3, chapter 37
ORCA/C 2.0 A C Compiler and Development System for the Apple IIGS, chapter 10
ORCA/C Types.rez for ORCA-specific resource declarations