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 
AquaSalMenuItems
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?