Programming AmigaOS in C

18. Defining Menus

AMigaOS supports menus which are accessible via the Screen Title bar at the top of the screen. Menus can have multiple headers e.g. File, Edit, Icons, Windows, Options etc and under each header have sub-items such as Open, Save, Save As, Close, Quit. Sub items can been ticked, have images and keyboard shortcuts assigned as well. To use Menus you need to use the Menu and MenuItem structures, and then attached them to a Window.

The Menu structure is used to define the top level menu header, from the intuition.h header file:

struct Menu
   {
   struct Menu *NextMenu;   /* same level */
   WORD LeftEdge, TopEdge;  /* position of the select box */
   WORD Width, Height;      /* dimensions of the select box */
   UWORD Flags;             /* flag definitions */
   BYTE *MenuName;          /* text for this Menu Header */
   struct MenuItem *FirstItem; /* pointer to first in chain */
 /* these mysteriously-named variables are for internal use only */
   WORD JazzX, JazzY, BeatX, BeatY;
 };

Here you can provide the address of the NextMenu if there are more than one menus, the position and size of the selection box on the screen bar, any specific flags, the name of the menu and the address of the first MenuItem attached to the menu. The Jazz and Beat values can be left as 0. Normally, the menus are defined in reverse order, the menu items first and then the Menu structures after it so that the linked lists can be set up correctly.

Next, there is the MenuItem structure that defines the tasks to be done under each main Menu header:

struct MenuItem
   {
   struct MenuItem *NextItem;  /* pointer to next in chained list */
   WORD LeftEdge, TopEdge;     /* position of the select box */
   WORD Width, Height;         /* dimensions of the select box */
   UWORD Flags;                /* see the defines below */
   LONG MutualExclude;         /* set bits mean this item excludes that */
   APTR ItemFill;              /* points to Image, IntuiText, or NULL */
   APTR SelectFill;            /* points to Image, IntuiText, or NULL */
   BYTE Command;               /* only if appliprog sets the COMMSEQ flag */
   struct MenuItem *SubItem;   /* if non-zero, points to MenuItem for submenu */
   UWORD NextSelect;           /* Menu no of next selected item when drag-selecting items    */ 
 };

Here you can provide the address of the Next MenuItem if there are more than one menu item, the position and size of the selection box on the screen bar, any specific flags, mutual exclusion value if using grouping of menus (e.g. select a colour, Font etc), pointers to Image or Intuitext structures for the name or an image to appear on the menu for ItemFill or Select file, a command value, a point to sub MenuItems if you want to branch out menus, and the number of next menu item if a user can drag select menu items.

You cannot provide just a name like Menu, you have to provide an IntuiText structure which can format the name of MenuItem as well in a specific font, size and colour, if you wish. The format of the IntuiText structure is below:

struct IntuiText
   {
   UBYTE FrontPen, BackPen;  /* the pen numbers for the rendering */
   UBYTE DrawMode;           /* the mode for rendering the text */
   WORD LeftEdge;            /* relative start location for the text */
   WORD TopEdge;             /* relative start location for the text */
   struct TextAttr *ITextFont; /* if NULL, you accept the default */
   UBYTE *IText;               /* pointer to null-terminated text */
   struct IntuiText *NextText; /* pointer to another IntuiText to render */
};

Here you provide the pen numbers to render the text, the drawing mode, the position, a pointer to a ITextFont structure to determine font type, the IText name to provide the Menus name and an optional pointer to another Intuitext structure. Alternatively, you can provide a pointer to an image if you wish rather than text:

struct Image
   {
   WORD LeftEdge;   /* starting offset relative to some origin */
   WORD TopEdge;    /* starting offsets relative to some origin */
   WORD Width;      /* pixel size (though data is word-aligned) */
   WORD Height;
   WORD Depth;      /* >= 0, for images you create */
   UWORD *ImageData;   /* pointer to the actual word-aligned bits */
   UBYTE PlanePick, PlaneOnOff;
   struct Image *NextImage;
 };
 

Here you can provide the offset position of the image and its size and depth (number of colour layers), a pointer to the image data itself and Plane bits to pick which image planes to render and finally an optionage pointer to another Image.

Please note than the IntuiTexts and Image structure should be defined before the MenuItem structures that are calling them.

19. Adding and Removing Menus from a Window

To add or remove a menu from a window, you need to use the following commands:

BOOL SetMenuStrip( struct Window *window, struct Menu *menu )

void ClearMenuStrip( struct Window *window )

Use the SetMenuStrip command after the Window has been defined and opened and provide the address of the Window and the address of the last Menu structure in the linked list. Before closing a window, you must use ClearMenuStrip command first to remove the menu from the screen before closing a Window. Attaching menus to windows rather than screens can allow your program to have flexibilty so if you had multiple windows open you can customise the menu to suit the appropiate window, for example, in a paint package or video program you can have smaller windows with their own menus.

20. Enabling and Disabling Menu Items

It is possible to enable or disable menu items, when you don`t wish a user to have access to a menu function. This can be done with the OnMenu and OffMenu functions:

void OffMenu( struct Window *window, unsigned long menuNumber)

void OnMenu( struct Window *window, unsigned long menuNumber )

Just provide the menu number of the item to enable or disable. If you change the CHECKED or ITEMENABLED values of menu items then you can refresh the menu using ResetMenuStrip command which is similar and faster than using SetMenuStrip command.

21. Menu Example

/* Menu Example */
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <intuition/intuition.h>
/* Menu definition */
   
   struct IntuiText text1 = 
   { 0, 1, JAM2, 4, 2, NULL, "Quit", NULL };
   struct IntuiText text2 =
   { 0, 1, JAM2, 4, 2, NULL, "Print", NULL };
   struct IntuiText text3 = 
   { 0, 1, JAM2, 4, 2, NULL, "Save", NULL };
   struct IntuiText text4 = 
   { 0, 1, JAM2, 4, 2, NULL, "Open", NULL };
   
   struct MenuItem item1 =
   { NULL, 0, 0, 48, 12, ITEMTEXT|ITEMENABLED|HIGHCOMP, 0, &text4, &text4, NULL,    NULL, 0 };
   struct MenuItem item2 = 
   { &item1, 0, 12, 48, 12, ITEMTEXT|ITEMENABLED|HIGHCOMP, 0, &text3, &text3, NULL,    NULL, 0 };
   struct MenuItem item3 =
   { &item2, 0, 24, 48, 12, ITEMTEXT|ITEMENABLED|HIGHCOMP, 0, &text2, &text2, NULL,    NULL, 0 };
   struct MenuItem item4 =
   { &item3, 0, 36, 48, 12, ITEMTEXT|ITEMENABLED|HIGHCOMP, 0, &text1, &text1, NULL,    NULL, 0 };
   
   struct Menu menu1 = 
   { NULL, 0, 0, 48, 12, MENUENABLED, "File", &item4, 0, 0, 0, 0    };
/* Main program */
int main(void) {
   struct Window *myWindow;
   int closewin = FALSE;
   struct IntuiMessage *msg;
   ULONG msgClass;
   
   myWindow = OpenWindowTags(NULL,
   WA_Left, 20, WA_Top, 20,
   WA_Width, 200, WA_Height, 150,
   WA_IDCMP, IDCMP_CLOSEWINDOW,
   WA_Flags, WFLG_SIZEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET    | WFLG_ACTIVATE,
   WA_Title, "My Window",
   WA_PubScreenName, "Workbench",
   TAG_DONE);
   /* Attach menu to window */
   SetMenuStrip (myWindow, &menu1);
   
   while (closewin == FALSE) {
      Wait(1L << myWindow->UserPort->mp_SigBit);
      msg = GT_GetIMsg(myWindow->UserPort);
      msgClass = msg->Class;
      GT_ReplyIMsg(msg);
      if (msgClass == IDCMP_CLOSEWINDOW) {
          ClearMenuStrip(myWindow); /* remove    menu */
          CloseWindow(myWindow);
          closewin = TRUE;
       }
   }
   return(0);
}

NB: for AmigaOS 4: Prefix OpenWindowTags, SetMenuStrip, ClearMenuStrip, CloseWindow with 'IIntuition->' Prefix Wait with 'IExec->'. Prefix GT_GetIMsg and GT_ReplyIMsg with 'IGadTools->'.

As you can see its quite a large program with the menu definitions in it. If writing a large program, it would be advisable to put includes and structure definitions into a header file away from the program code to keep it small and more managable. For the menu definitions, notice that the y poition increases by 12 points for each menu item and that the flags are set so that item is ITEMTEXT display, it is enabled and HIGHCOMP ensures that the menu item is highlighted when the user selects it. In the main program the two command to set and clear the menu strip have been added in the appropiate places.

Now we need to find out which menu item was selected and do some simple processing of a menu.

Next Page