Carbon menus
This is the second blog entry in
mini-serie about native menus for
OpenOffice.org.
Today, I'll describe how menus are created and displayed in my sample Carbon application. I rewrote
the example so it matches better the OpenOffice.org native menus API.
I'll now describe, how the above image can be generated using Carbon instead of Gimp ;-) Let's start
with the small description of the terms menubar and menu. Please read the article
The Menu Bar and Its Menus in the
Apple
Human Interface Guidelines which describes them very well.
As you can see on the image above, we have defined two menus inside application menu - menu File and
menu Edit. They are contained in the invisible "RootMenu" which is a container for them. In
OpenOffice.org, we will use one RootMenu for every OpenOffice.org window.
So how can we create the RootMenu?
MenuRef rootMenu;
CreateNewMenu(0, 0, &rootMenu);
It is very simple - we will call
CreateNewMenu
function and the new menu will be
returned in the variable
rootMenu
. We already know OpenOffice.org native menus API, so
we can see RootMenu as an equivalent to
SalMenu
created with
bMenuBar =
TRUE
. This means that our class
AquaSalMenu
will add new member to reference the
RootMenu's
MenuRef
and the function
AquaSalInstance::CreateMenu
will set
it when called with
bMenuBar = TRUE
to create the menu bar (and will set
its
bMenuBar
member to
TRUE
to reflect it is a menubar).
So we have a menu bar now. The menu bar is not shown yet, we will actually display it later. Now we
can create the actual menus to be attached to the menu bar (RootMenu):
MenuRef myMenu;
CreateNewMenu(1, 0, &myMenu);
Actual menus are created using the same function as the menu bar (RootMenu) itself. Yes, it's true -
we will see later what is the difference (hint: calling the
function
SetRootMenu
). Every top-level menu is represented using
its
MenuRef
reference. As the same applies to
MenuBar
itself (it
is
AquaSalMenu
too), we can share the same member for
MenuRef
of the menu
bar and menu and use
bMenuBar
member to distinguish between menus and menu bars.
Now, I'll describe the first main difference between both APIs. In OpenOffice.org native menu API,
the string "File" is associated to the empty member item in the MenuBar. In Carbon, "File" is the
title of the menu and is associated with it, not with the menu bar itself:
SetMenuTitleWithCFString( myMenu, CFSTR("File") );
So now, the menu created above is named as "File", it is still empty and still not shown. So we can
fill it with items:
MenuItemIndex item;
AppendMenuItemTextWithCFString( myMenu, CFSTR("Open"), 0, 0, &item );
AppendMenuItemTextWithCFString( myMenu, CFSTR("Close"), 0, 0, &item );
AppendMenuItemTextWithCFString( myMenu, CFSTR("Exit"), 0, 0, &item );
We have just added three menu items (they will be mapped directly to
AquaSalMenuItem
s
created using
AquaSalInstance::CreateMenuItem
) to the menu. The returned value in the
variable
item
together with the
MenuRef
myMenu
will be stored
for future reference (have you already noticed that our menu items are not functional, they are only
items in menus? ;-).
Now that we have menu "completely" defined, we can add it into the menu bar (link it with the menu
bar). We will first insert unnamed empty item into the menu bar and then will set its hierarchical
menu to the just defined "File" menu. This is different in OpenOffice.org native menus API, because
in OpenOffice.org, you insert menu item "File" into menu bar and then you
SetPopupMenu
of it to the File menu which is untitled (it only contains menu-items "Open", "Close", ...):
MenuItemIndex myMenuIndex;
AppendMenuItemTextWithCFString( rootMenu, NULL, 0, 0, &myMenuIndex );
SetMenuItemHierarchicalMenu(rootMenu, myMenuIndex, myMenu);
The function
AppendMenuItemTextWithCFString
will create empty menu item in the menu bar
and the function
SetMenuItemHierarchicalMenu
will set its hierarchical menu to our
"File" menu defined above.
Now we can do the same with the "Edit" menu:
CreateNewMenu(1, 0, &myMenu);
SetMenuTitleWithCFString( myMenu, CFSTR("Edit") );
AppendMenuItemTextWithCFString( myMenu, CFSTR("Search"), 0, 0, &item );
AppendMenuItemTextWithCFString( myMenu, CFSTR("Replace"), 0, 0, &item );
AppendMenuItemTextWithCFString( rootMenu, NULL, 0, 0, &myMenuIndex );
SetMenuItemHierarchicalMenu(rootMenu, myMenuIndex, myMenu);
So now we have the menu bar defined, two menus connected to it, but it still is not displayed. This
way, we can define more menu bars, but we still have to set one of them to be displayed. This is
what
SetRootMenu
is doing:
SetRootMenu(rootMenu);
This function sets the RootMenu's part of the application menu to the menu referenced by its
MenuRef
. We will have to call this function when changing windows (frames) because of
different menus in different windows. This also means that we have to
extend
AquaSalFrame
class to contain also its RootMenu's
MenuRef
.
This entry was written to make it clear how menus in OpenOffice.org and menus in Carbon
differ. I have not yet investigated how to pass menu selection for processing, how separators work,
how menu shortcuts work etc. This is not the goal of this blog entry. My first goal is to have the
menus actually displayed first. I hope you found it interesting to read it. If so, please drop me
a line. I hope I (or Pierre) will find the time this week to make this into the actual code for you
to test! Please bear with us. I'm no Carbon hacker. I'm learning on the go. BTW, can you help us?