C++ Tutorials‎ > ‎

Menubar Basics

In this tutorial, we're going to learn how to write a basic program that displays a menubar at the top of the screen and allows the user to select options from the menu and then cleanly quit the program. It may not sound like much, but when using the Mac Toolkit all these things must be done explicitly, so we're going to take it a step at a time.

First, I will list the entirety of the code that we will use in this part of the tutorial. Afterwards, I will go over it line-by-line and explain what it does.

#include <MacWindows.h>
#include <Events.h>
#include <Dialogs.h>
#include <ToolUtils.h>
#include <Sound.h>

// constants
const short defaultMenubar = 128;
const short menuApple = 128;
const short menuFile = 129;
const short menuitemAbout = 1;
const short menuitemQuit = 1;

// globals
if __MWERKS__ == 0
QDGlobals qd;
#endif

// function prototypes
void Initialize();
void MainLoop();
void Terminate();

void main() {
    Initialize();

    MainLoop();

    Terminate();
}

// Initialize
void Initialize() {
    InitGraf(&qd.thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(nil);
    InitCursor();

    Handle menubar = GetNewMBar(defaultMenubar);
    SetMenuBar(menubar);
    DrawMenuBar();
}

void MainLoop() {

    EventRecord event;

    while (true) {
        if (GetNextEvent(everyEvent, &event)) {
            if (event.what == mouseDown) {
                WindowPtr clickedWindow;
                short clickedPart = FindWindow(event.where, &clickedWindow);

                if (clickedPart == inMenuBar) {
                    long menuAction = MenuSelect(event.where);
                    if (menuAction > 0) {
                        short menu = HiWord(menuAction);
                        short item = LoWord(menuAction);
                        if (menu == menuApple) {
                            if (item == menuitemAbout) {
                                SysBeep(1);
                            }
                        } else if (menu == menuFile) {
                            if (item == menuitemQuit) {
                                Terminate();
                            }
                        }
                        HiliteMenu(0);
                    }
                }
            }
        }
    }
}

void Terminate() {
    ExitToShell();
}

Now to go over it line by line:

#include <Dialogs.h>
#include <ToolUtils.h>
#include <Sound.h>

Three new header files provide various functions. Dialogs.h provides initialization functions for dialog windows as well as pulling in header files for several other parts of the Macintosh GUI, such as windows, menubars, and mouse control. ToolUtils.h provides functions for performing bitwise manipulation of variables (LoWord() and HiWord() are the ones used in this program), and Sound.h is used to make the system beep. (SysBeep())

// constants
const short defaultMenubar = 128;
const short menuApple = 128;
const short menuFile = 129;
const short menuitemAbout = 1;
const short menuitemQuit = 1;

These constants are used to refer to the IDs of the resources we will include in our program later on. It's good practice to use constants instead of using the numbers directly in case you find you have to change the ID of a common resource later on.

// globals
if __MWERKS__ == 0
QDGlobals qd;
#endif

This declares the QuickDraw global, which is used to access information like the screen dimensions. It's already declared if you're using CodeWarrior, so the __MWERKS__ macro keeps it from being defined again.

void Initialize() {
    InitGraf(&qd.thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(nil);
    InitCursor();

Our Initialize() function has several new additions. InitGraf() initializes QuickDraw, and TEInit() initializes TextEdit controls. (True, we don't allow the user to enter any text in our program yet, but we will eventually.) The rest of the initialization functions should be self-explanatory.

    Handle menubar = GetNewMBar(defaultMenubar);
    SetMenuBar(menubar);
    DrawMenuBar();
 
This part of the code tells the Toolkit to create a new Menubar using the resource with the ID in defaultMenuBar (which in our program is 128.) The next line sets the current menubar to this newly created menubar, and then redraws the menubar so that our new menubar appears on the screen.

if (event.what == mouseDown) {
    WindowPtr clickedWindow;
    short clickedPart = FindWindow(event.where, &clickedWindow);

We have removed the code that checked for keypresses for now in favor of handling mouseclicks. The FindWindow() function uses information in the EventRecord to tell our program where on the screen the user has clicked. We pass in a WindowPtr object which is populated with a pointer to the window the user clicked inside of (if our program had any windows to click inside of.) Even though we don't, we can still use the information returned by the function to control the menubar.

if (clickedPart == inMenuBar) {
    long menuAction = MenuSelect(event.where);
    if (menuAction > 0) {
        short menu = HiWord(menuAction);
        short item = LoWord(menuAction);

If the user clicks inside the menubar, we have to use the MenuSelect() function to find out which menu and item they have clicked. For some reason, these are stored together in a 32-bit long value, which we have to break up into two 16-bit short values using the HiWord() and LoWord() functions. We can then look at those values to determine the menu and the item clicked.

if (menu == menuApple) {
    if (item == menuitemAbout) {
        SysBeep(1);
    }

If the user clicks the Apple Menu->About This Program, call SysBeep(). This makes the computer emit a short beep. In a real program, you'd probably call a function that displayed an "About This Program" dialog box.

} else if (menu == menuFile) {
    if (item == menuitemQuit) {
        Terminate();
    }
}

If the user clicks File->Quit, terminate the program. Pretty simple.

HiliteMenu(0);

When the user clicks a menu item, Mac OS flashes the menu containing that item to provide visual feedback to the user. The menu will stay highlighted until your program is done doing whatever work needs to be done, so that the user will know that their action had an effect. This means that it is the responsibility of your program to un-highlight the menubar once it's done processing the click event. HiliteMenu() highlights whatever menu item is passed into it. If a 0 is passed in, it highlights nothing (thus un-highlighting the previously highlighted item.)

The Terminate function is the same as the last time. Now we are done with the code portion.

At this point we will need to include a resource file into our program. If you do not understand the difference between resource and data forks, you should go read about them now. They are fairly well documented in various places on the internet so I will not go into too much detail here. There is a good description of them on page 1-11 of Inside Macintosh: Toolbox Essentials, which is still available as of 4-24-2012 from Apple's website here. You will need to use the program ResEdit (download from Apple here) or another resource editor. This tutorial will assume you are using ResEdit.

Open ResEdit and create a new resource file in your project directory. Call it test.rsrc. Make sure to add it to your project in MPW or CodeWarrior. To do this in CodeWarrior Pro 6, just drag it onto the list of project files. Other versions may vary. For MPW, see Using Resources in MPW.

Create a new resource by selecting Resource->Create New Resource or ⌘K. Make a new resource of the MENU type. This is a type of resource that defines menu items, which are displayed at the top of the screen when your program runs. For the title, choose  (Apple Menu). Add a new item to the menu by selecting Resource->Create New Item. For the text, put "About This Program" (or whatever you like, it doesn't matter.)

Close the Apple Menu resource and add another MENU resource. For this one, set the title to "File" and add an item labeled "Quit".

Now go back to the main ResEdit window and create a new resource of the type MBAR. This is a menu bar resource that contains a list of MENU items. Select Resource->Insert New Field(s) twice (you may have to click the little row of stars to enable this) and set the Menu res IDs to 128 and 129. Those should correspond to the IDs of the MENU resources you created earlier.

Save the resource file and close ResEdit.

Compile the program and run it. The menubar should change to show just the Apple logo and the File menu. Clicking the Apple logo->About This Program will cause the computer to beep, while File->Quit allows you to exit the program.





Comments