Intuition Menus — Section 3.8

This article synthesises Section 3.8 ("Menus") of Amiga C for Advanced Programmers by Bleek, Jennrich, Schulz (Abacus / Data Becker, ~1991). It covers the Intuition menu model: how to construct a menu strip (MenuStrip + Menu + MenuItem), attach it to a window, and handle IDCMP_MENUPICK events.

Section 3.8 is one of the longest in Chapter 3 (~50 pages, PDF pages 293–346). The book devotes extensive space to menus because the editor example uses menus heavily (File / Edit / Search menus).

Editorial note: page numbers in this article refer to the PDF page index in the raw OCR source. The book covers the classic Intuition menu model (the MenuStrip pattern). OS 2.0+ provides PopUpMenu() and the modern Layout-based menu construction; OS 4 has further changes.


Why menus? (page 293)

The book motivates menus as the answer to "where do I put all the commands?":

"We could use the keyboard exclusively, then draw a keyboard overlay to help the user remember. That's kind of silly, since the Amiga has Intuition. Maybe we can code user access to functions and commands through gadgets. That's better, but since we still need room for an input and output window pair, where do we put the gadgets?"

Menus are the standard Amiga mechanism for commands that don't need to be visible all the time. Each top-level menu (e.g. "File") contains a list of items (e.g. "Open", "Save", "Quit"). Sub-items nest further.


The MenuStrip structure (page 295)

A menu strip is a linked list:

MenuStrip → Menu → MenuItem → (SubItem → MenuItem → ...)

In code:

struct Menu *strip;                /* the entire strip */
struct Menu *menu;                 /* one top-level menu (e.g. "File") */
struct MenuItem *item;            /* one item (e.g. "Open") */
struct MenuItem *subitem;         /* sub-item (e.g. "Save As...") */

The strip is built by linking these via NextMenu (between menus) and NextItem (between items and sub-items).


Constructing a menu (page 296)

struct NewMenu nm[] = {
    /* NM_TITLE = top-level menu */
    { NM_TITLE, "File", 0, 0, 0, 0 },
    /* NM_ITEM = item */
    { NM_ITEM,  "Open",  'O', 0, 0, 0 },
    { NM_SUB,   "Open File...",  'O', 0, 0, 0 },
    { NM_ITEM,  "Save",  'S', 0, 0, 0 },
    { NM_SUB,   "Save As...",  'A', 0, 0, 0 },
    { NM_ITEM,  NM_BARLABEL, 0, 0, 0, 0 },  /* separator */
    { NM_ITEM,  "Quit",  'Q', 0, 0, 0 },

    { NM_TITLE, "Edit", 0, 0, 0, 0 },
    { NM_ITEM,  "Cut",   'X', 0, 0, 0 },
    { NM_ITEM,  "Copy",  'C', 0, 0, 0 },
    { NM_ITEM,  "Paste", 'V', 0, 0, 0 },

    { NM_END }
};

strip = CreateMenusA(nm, NULL);

CreateMenusA() (OS 2.0+) takes a flat NewMenu array terminated by NM_END, and returns a struct Menu * ready to be attached to a window via SetMenuStrip().

The book's pre-OS-2.0 examples build the menu by hand, linking Menu and MenuItem structures via NextMenu / NextItem. The book notes (page 297) that CreateMenusA() is the "modern" way and gives both examples.

The NewMenu array elements

Field Meaning
nm_Type NM_TITLE, NM_ITEM, NM_SUB, or NM_END
nm_Label Text of the menu/item (or NM_BARLABEL for a separator)
nm_CommKey Shortcut key (uppercase letter for Right-Amiga shortcut)
nm_Flags NM_FLAGDISABLED etc.
nm_UserData Application-specific
nm_MutualExclude Mutual-exclusion mask (similar to gadgets)

Attaching the menu to a window (page 298)

if (!SetMenuStrip(window, strip)) {
    /* failure: out of memory or invalid menu structure */
}

This sets the strip as the active menu for the window. The user can now pull down the menus and see your items.

To remove: ClearMenuStrip(window).

The book emphasises that you should ClearMenuStrip() before closing the window, and that you should FreeMenus(strip) (or FreeMenusA(strip, NULL)) after clearing — otherwise the strip leaks.


Handling MENUPICK events (page 305)

When the user picks a menu item, Intuition posts an IDCMP_MENUPICK message with Code set to the menu number. Decode it with ItemAddress():

case IDCMP_MENUPICK:
    {
        UWORD code = msg->Code;
        while (code != MENUNULL) {
            struct MenuItem *item = ItemAddress(strip, code);
            if (item) {
                /* dispatch on item, or item->UserData */
                /* for sub-menus, follow item->SubItem */
            }
            code = item->NextSelect;  /* or MENUNULL if no more */
        }
    }
    break;

code encodes the menu number as (menu_num << 8) | item_num. ItemAddress() walks the strip and returns the MenuItem *.

The book gives a worked dispatcher for the editor's File menu (page 305-310): Open / Save / Quit dispatch via MenuUserData() (the modern nm_UserData field).


To make an item have sub-items:

struct NewMenu nm[] = {
    { NM_TITLE, "File", 0, 0, 0, 0 },
    { NM_ITEM,  "Recent", 0, 0, 0, 0 },
    { NM_SUB,   "file1.txt", '1', 0, 0, 0 },
    { NM_SUB,   "file2.txt", '2', 0, 0, 0 },
    { NM_SUB,   "file3.txt", '3', 0, 0, 0 },
    /* ... */
    { NM_END }
};

The NM_SUB rows are children of the previous NM_ITEM row. The menu cascades to the right when the user hovers the parent.


Flags and disabling items

The book's sections 3.8.3 and 3.8.4 cover flags:

  • NM_FLAGDISABLED — start disabled
  • MENUENABLED / MENUDISABLED — runtime on/off
  • CHECKIT / CHECKED — toggleable item with state
  • COMMSEQ — Right-Amiga shortcut
  • HIGHFLAGS / HIGHCOMP — selected appearance

Toggle an item at runtime:

item->Flags ^= CHECKED;       /* flip check state */
ResetMenuStrip(window, strip);  /* redraw */

Disable an item:

OffMenu(window, item);       /* OS 2.0+ */

Custom menu items (page 335)

For menus with custom rendering (icons, custom text), the book covers:

  • IntuiText for custom labels (different fonts, styles)
  • Custom rendering via the menu's MenuItem.FirstItem chain
  • Highlighting (selected appearance) via HIGHFLAGS / HIGHCOMP flags

The book notes that custom menus are more work than gadgets for the same effect; gadgets are usually preferred for in-window controls.

Modern equivalent: Layout-based menus (V39+) automate much of the geometry; the menuclass BOOPSI class (V39+) provides a fully OO rendering model.


The MENUVERIFY mechanism (page 340)

A IDCMP_MENUVERIFY event arrives when the user is about to pick a menu, before the actual selection. This lets you:

  • Suppress the menu if the operation is unsafe (e.g. during a save)
  • Reorganise the menu on the fly
  • Implement custom keyboard handling

Pattern:

case IDCMP_MENUVERIFY:
    ReplyMsg((struct Message *)msg);   /* reply first */
    if (some_condition) {
        /* suppress menu */
        msg->Code = MENUCANCEL;
    } else {
        /* re-show menu */
        ResetMenuStrip(window, strip);
    }
    break;

Note: you must reply to the message before doing anything else in the verify handler — Intuition needs the reply to release the menu state machine.


What's NOT in section 3.8

  • PopUpMenu() / PopUpMenuAsync() (V39+) — context-menu API. The book covers only the standard menu strip.
  • menuclass (BOOPSI, V39+) — modern menu class system.
  • Menu localisation — the book predates AmigaOS 2.0 locale support; menus in OS 2.0+ can be translated via the catalog system.
  • Layout / Position-controlled menus (OS 4) — not in scope.
  • Reaction / MUI menus — third-party.

Modern migration notes

Use this Instead of this Why
CreateMenusA / LayoutMenusA (V39+) Hand-linked Menu/MenuItem chains Less error-prone
PopUpMenu (V39+) Sub-menus with weird positioning Sane cascade behaviour
IA_Label + IA_CommandKey (BOOPSI) Direct field manipulation OO-style
ResetMenuStrip after OnMenu / OffMenu RefreshMenu (V36-) Modern API

But the IDCMP_MENUPICK dispatch pattern carries forward unchanged — even V39+ menus still emit the same event class.


Sources