TweetFollow Us on Twitter

MACINTOSH C CARBON

Demonstration Program Menus1

Goto Contents

// *******************************************************************************************
// Menus1.c                                                                CLASSIC EVENT MODEL
// *******************************************************************************************
// 
// This program:
//
// o  Opens a window.
//
// o  Creates these pull-down menus: Apple, File, Edit, Font, Size, Special, and Window.
//
// o  Displays text in the window indicating the menu selection made by the user.
//
// The Apple menu includes an "About." menu item for the program.
//
// The second menu item in the Special menu contains a submenu.
//
// The Font and Window menus are created programmatically using the functions
// CreateStandardFontMenu and CreateStandardWindowMenu.
//
// The implementation of the Size menu is nominal only.  The current size is indicated with a
// checkmark; however, the number of sizes shown is not font-dependent and there is no "Other"
// item.
//
// Because the primary purpose of the program is to demonstrate menu creation and handling, no
// code is included to update and activate/deactivate the window or to respond to events which
// are not relevant to the demonstration. 
//
// The program is terminated by selecting Quit from the File menu, by pressing the keyboard
// equivalent for that item (Command-Q), or by clicking in the window's go-away box/close
// button.
//  
// The program utilises the following resources:
//
// o  A 'plst' resource.
//
// o  A 'WIND' resource (purgeable) (initially not visible).
//
// o  An 'MBAR' resource (preload, non-purgeable).
//
// o  'MENU' resources for the Apple, File, Edit, Font, Size, and Special drop-down menus
//    and the submenu (all preload, all non-purgeable).
//
// o  A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, 
//    doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************

// .................................................................................. includes

#include <Carbon.h>

// ................................................................................... defines

#define rMenubar          128
#define mAppleApplication 128
#define  iAbout           1
#define mFile             129
#define  iQuit            12
#define mEdit             130
#define  iUndo            1
#define  iCut             3
#define  iCopy            4
#define  iPaste           5
#define  iClear           6
#define mFont             131
#define mSize             132
#define  iTen             1
#define  iTwelve          2
#define  iEighteen        3
#define  iTwentyFour      4
#define mSpecial          133
#define  iFirstItem       1
#define  iSecondItem      2
#define mWindow           134
#define mSubmenu          135
#define mFirstFontSubMenu 136
#define  iFirstSubItem    1
#define  iSecondSubItem   2
#define rWindowResource   128
  
// .......................................................................... global variables

Boolean        gDone;
ItemCount      gFontMenuHierMenuCount;
MenuItemIndex  gCurrentFontSizeItem = 2;
MenuItemIndex  gCurrentFontMenuItem;
MenuItemIndex  gCurrentFontSubMenuItem;
MenuRef        gCurrentFontSubMenuRef = NULL;

// ....................................................................... function prototypes

void  main                   (void);
void  doPreliminaries        (void);
OSErr quitAppEventHandler    (AppleEvent *,AppleEvent *,SInt32);
void  doGetMenus             (void);
void  doEvents               (EventRecord *);
void  doMouseDown            (EventRecord *);
void  doAdjustMenus          (void);
void  doMenuChoice           (SInt32);
void  doAppleApplicationMenu (MenuItemIndex);
void  doFileMenu             (MenuItemIndex);
void  doEditMenu             (MenuItemIndex);
void  doFontMenu             (MenuID,MenuItemIndex);
void  doSizeMenu             (MenuItemIndex);
void  doSpecialMenu          (MenuItemIndex);
void  doSubMenus             (MenuItemIndex);
void  drawItemString         (Str255);

// ************************************************************************************** main

void  main(void)
{
  EventRecord eventStructure;
  WindowRef   windowRef;

  // ........................................................................ do preliminaries

  doPreliminaries();
  
  // ........................................................................... open a window
    
  if(!(windowRef = GetNewCWindow(rWindowResource,NULL,(WindowRef) -1)))
  {
    SysBeep(10);
    ExitToShell();
  }

  SetPortWindowPort(windowRef);

  // ............................................. set up menu bar and menus, then show window
  
  doGetMenus();
  ShowWindow(windowRef);

  // .............................................................................. event loop

  gDone = false;

  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&eventStructure,180,NULL))
      doEvents(&eventStructure);
  }
}

// *************************************************************************** doPreliminaries

void  doPreliminaries(void)
{
  OSErr osError;

  MoreMasterPointers(640);
  InitCursor();
  FlushEvents(everyEvent,0);

  osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
                            NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
                            0L,false);
  if(osError != noErr)
    ExitToShell();
}

// **************************************************************************** doQuitAppEvent

OSErr  quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
  OSErr    osError;
  DescType returnedType;
  Size     actualSize;

  osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0,
                              &actualSize);

  if(osError == errAEDescNotFound)
  {
    gDone = true;
    osError = noErr;
  } 
  else if(osError == noErr)
    osError = errAEParamMissed;

  return osError;
}

// ******************************************************************************** doGetMenus

void  doGetMenus(void)
{
  MenuBarHandle menubarHdl;
  SInt32        response;
  MenuRef       menuRef;
  OSStatus      osError;
  Str255        smallSystemFontName, itemString;
  SInt16        numberOfItems,a;

  // ............................................................... get and set, and menu bar

  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);

  // ........... if running on Mac OS X, delete Quit item and preceding divider from File menu

  Gestalt(gestaltMenuMgrAttr,&response);
  if(response & gestaltMenuMgrAquaLayoutMask)
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
    {
      DeleteMenuItem(menuRef,iQuit);
      DeleteMenuItem(menuRef,iQuit - 1);
      DisableMenuItem(menuRef,0);
    }
  }

  // .............................. create hirearchical Font menu, checkmark small system font

  menuRef = GetMenuRef(mFont);
  if(menuRef != NULL)
  {
    osError = CreateStandardFontMenu(menuRef,0,mFirstFontSubMenu,kHierarchicalFontMenuOption,
                                     &gFontMenuHierMenuCount);
    if(osError != noErr)
      ExitToShell();
  }
  else
    ExitToShell();

   GetFontName(kThemeSmallSystemFont,smallSystemFontName);
  numberOfItems = CountMenuItems(menuRef);
  for(a=1;a<numberOfItems + 1;a++)
  {
    GetMenuItemText(menuRef,a,itemString);
    if(EqualString(itemString,smallSystemFontName,false,false))
    {
      CheckMenuItem(menuRef,a,true);
      gCurrentFontMenuItem = a;
      break;
    }
  }

  // ............................................ create Window menu and insert into menu list

  CreateStandardWindowMenu(0,&menuRef);
  SetMenuID(menuRef,mWindow);
  InsertMenu(menuRef,0);

  // ................................................ get submenus and insert into window list

  menuRef = GetMenu(mSubmenu);
  if(menuRef != NULL)
    InsertMenu(menuRef,hierMenu);
  else
    ExitToShell();

  // ........................ set initial font size and checkmark associated item in Size menu

  doSizeMenu(gCurrentFontSizeItem);

  // ........................................................................... draw menu bar

  DrawMenuBar();
 }

// ********************************************************************************** doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  switch(eventStrucPtr->what)
  {
    case kHighLevelEvent:
      AEProcessAppleEvent(eventStrucPtr);
      break;

    case mouseDown:
      doMouseDown(eventStrucPtr);
      break;

    case keyDown:
      if((eventStrucPtr->modifiers & cmdKey) != 0)
      {
        doAdjustMenus();
        doMenuChoice(MenuEvent(eventStrucPtr));
      }
      break;

    case updateEvt:
      BeginUpdate((WindowRef) eventStrucPtr->message);
      EndUpdate((WindowRef) eventStrucPtr->message);
      break;
  }
}

// ******************************************************************************* doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  WindowRef      windowRef;
  WindowPartCode partCode;
  SInt32         menuChoice;
  
  partCode = FindWindow(eventStrucPtr->where,&windowRef);
  
  switch(partCode)
  {
    case inMenuBar:
      doAdjustMenus();
      menuChoice = MenuSelect(eventStrucPtr->where);
      doMenuChoice(menuChoice);
      break;

    case inContent:
      if(windowRef != FrontWindow())
        SelectWindow(windowRef);
      break;

    case inDrag:
      DragWindow(windowRef,eventStrucPtr->where,NULL);
      break;

    case inGoAway:
      if(TrackGoAway(windowRef,eventStrucPtr->where))
        gDone = true;
      break;
  }
}

// ***************************************************************************** doAdjustMenus

void  doAdjustMenus(void)
{
  // Adjust menus here.
}

// ****************************************************************************** doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  MenuID        menuID;
  MenuItemIndex menuItem;
    
  menuID    = HiWord(menuChoice);
  menuItem  = LoWord(menuChoice);

  if(menuID == 0)
    return;

  if(menuID == mFont || ((menuID >= mFirstFontSubMenu) && 
                         (menuID <= mFirstFontSubMenu + gFontMenuHierMenuCount)))
    doFontMenu(menuID,menuItem);
  else
  {
    switch(menuID)
    {
      case mAppleApplication:
        doAppleApplicationMenu(menuItem);
        break;

      case mFile:
        doFileMenu(menuItem);
        break;

      case mEdit:
        doEditMenu(menuItem);
        break;

      case mSize:
        doSizeMenu(menuItem);
        break;

      case mSpecial:
        doSpecialMenu(menuItem);
        break;

      case mSubmenu:
        doSubMenus(menuItem);
        break;
    }
  }

  HiliteMenu(0);
}

// ******************************************************************** doAppleApplicationMenu

void  doAppleApplicationMenu(MenuItemIndex menuItem)
{
  if(menuItem == iAbout)
    drawItemString("\pAbout Menus1");
}

// ******************************************************************************** doFileMenu

void  doFileMenu(MenuItemIndex menuItem)
{
  if(menuItem  == iQuit)
    gDone = true;
}

// ******************************************************************************** doEditMenu

void  doEditMenu(MenuItemIndex menuItem)
{
  switch(menuItem)
  {
    case iUndo:
      drawItemString("\pUndo");
      break;

    case iCut:
      drawItemString("\pCut");
      break;

    case iCopy:
      drawItemString("\pCopy");
      break;

    case iPaste:
      drawItemString("\pPaste");
      break;

    case iClear:
      drawItemString("\pClear");
      break;
  }
}

// ******************************************************************************** doFontMenu

void  doFontMenu(MenuID menuID,MenuItemIndex menuItem)
{
  MenuRef      menuRef, fontMenuRef;
  OSStatus     osError;
  FMFontFamily currentFontFamilyReference;
  FMFontStyle  currentFontStyle;
  Str255       fontName, styleName, itemName;
  SInt16       a, numberOfFontMenuItems;
  
  menuRef = GetMenuRef(menuID);

  osError = GetFontFamilyFromMenuSelection(menuRef,menuItem,¤tFontFamilyReference,
                                           ¤tFontStyle);
  if(osError == noErr)
  {
    TextFont(currentFontFamilyReference);
    TextFace(currentFontStyle);

    GetFontName(currentFontFamilyReference,fontName);
    drawItemString(fontName);

    if(menuID == mFont)
    {
      CheckMenuItem(menuRef,gCurrentFontMenuItem,false);
      gCurrentFontMenuItem = menuItem;
      CheckMenuItem(menuRef,gCurrentFontMenuItem,true);

      if(gCurrentFontSubMenuRef != NULL)
        CheckMenuItem(gCurrentFontSubMenuRef,gCurrentFontSubMenuItem,false);
    }
    else
    {
      if(gCurrentFontSubMenuRef != NULL)
        CheckMenuItem(gCurrentFontSubMenuRef,gCurrentFontSubMenuItem,false);
      gCurrentFontSubMenuRef = menuRef;
      gCurrentFontSubMenuItem = menuItem;
      CheckMenuItem(gCurrentFontSubMenuRef,gCurrentFontSubMenuItem,true);

      fontMenuRef = GetMenuRef(mFont);    
      CheckMenuItem(fontMenuRef,gCurrentFontMenuItem,false);

      numberOfFontMenuItems = CountMenuItems(fontMenuRef);

      for(a=1;a<=numberOfFontMenuItems;a++)
      {
        GetMenuItemText(fontMenuRef,a,itemName);
        if(EqualString(fontName,itemName,true,true))
        {
          gCurrentFontMenuItem = a;
          break;
        }
      }

      SetItemMark(fontMenuRef,gCurrentFontMenuItem,'-');

      GetMenuItemText(menuRef,menuItem,styleName);
      DrawString("\p ");
      DrawString(styleName);
    }
  }
  else
    ExitToShell();
}

// ******************************************************************************** doSizeMenu

void  doSizeMenu(MenuItemIndex menuItem)
{  
  MenuRef sizeMenuRef;  

  switch(menuItem)
  {
    case iTen:
      TextSize(10);
      break;

    case iTwelve:
      TextSize(12);
      break;

    case iEighteen:
      TextSize(18);
      break;

    case iTwentyFour:
      TextSize(24);
      break;
  }

  sizeMenuRef = GetMenuRef(mSize);

  CheckMenuItem(sizeMenuRef,gCurrentFontSizeItem,false);
  CheckMenuItem(sizeMenuRef,menuItem,true);
  
  gCurrentFontSizeItem = menuItem;
  
  drawItemString("\pSize change");
}

// ***************************************************************************** doSpecialMenu

void  doSpecialMenu(MenuItemIndex menuItem)
{
  if(menuItem == iFirstItem)
    drawItemString("\pFirst Item");
}

// ******************************************************************************** doSubMenus

void  doSubMenus(MenuItemIndex menuItem)
{
  switch(menuItem)
  {
    case iFirstSubItem:
      drawItemString("\pSubitem 1");
      break;

    case iSecondSubItem:
      drawItemString("\pSubitem 2");
      break;
  }
}

// **************************************************************************** drawItemString

void  drawItemString(Str255 eventString)
{
  RgnHandle tempRegion;
  WindowRef windowRef;
  Rect      scrollBox;

  windowRef = FrontWindow();
  tempRegion = NewRgn();

  GetWindowPortBounds(windowRef,&scrollBox);
  
  ScrollRect(&scrollBox,0,-24,tempRegion);
  DisposeRgn(tempRegion);
  
  MoveTo(8,286);
  DrawString(eventString);
}

// *******************************************************************************************

Demonstration Program Menus1 Comments

When this program is run, the user should choose items from all menus, including the Apple
menu.  Selections should be made using the mouse and, where appropriate, the Command key
equivalents.  The user should also note the effects on the menu bar of clicking outside, then
inside, the program's window, that is, of sending the program to the background and returning
it to the foreground.

defines

Constants are established for the pull-down and submenu menu IDs and associated resource IDs,
menu item numbers and subitem numbers.

The Menu Manager function CreateStandardFontMenu will be used to create a hierarchical Font
menu and mFirstFontSubMenu establishes the ID of the first Font menu submenu to be created.

The last line establishes a constant for the resource ID of the 'WIND' resource.

Global Variables

gFontMenuHierMenuCount will be assigned a value representing the number of submenus created by
the Menu Manager function CreateStandardFontMenu.

GCurrentFontSizeItem will be assigned the menu item number of the chosen font size.

gCurrentFontMenuItem and gCurrentFontSubMenuItem will be used in the Font menu handling
function to specify which menu and submenu items are to have a checkmark added or cleared.
gCurrentFontSubMenuRef will be assigned a reference to the menu object for the currently chosen
Font menu submenu.

main

The main() function creates a window and makes its graphics port the current port, calls
doGetMenus to set up the menus, shows the window and enters the main event loop.

doPreliminaries

The large number of master pointers created by MoreMasterPointers in this program allows for
the likely creation of a large number of submenus by the Menu Manager function
CreateStandardFontMenu.

When the program is run on Mac OS X, the Quit item will be in the Application menu.  The system
informs the program of the user's choice of this item via a high-level event known as an Apple
event, more specifically, an Apple event known as the Quit Application event.  The call to
AEInstallEventHandler installs quitAppEventHandler as the handler for this high-level event.
(Apple events and Apple event handlers are explained at Chapter 10.)

quitAppEventHandler

quitAppEventHandler is the handler for the Quit Application event installed in doPreliminaries. 
Basically, it sets the global variable gDone to true, which causes the program to terminate
from the main event loop.

doGetMenus

doGetMenus sets up the menu bar and the various menus.

At the first block, GetNewMBar reads in the 'MENU' resources for each menu specified in the
'MBAR' resource and creates a menu object for each of those menus.  (Note that the error
handling here and in other areas of this program is somewhat rudimentary: the program simply
terminates.)  The call to SetMenuBar makes the newly created menu list the current list.

The call to Gestalt determines whether the application is running on Mac OS 8/9 or Mac OS X. 
If the application is running on Mac OS X, GetMenuRef is called to get a reference to the menu
object for the File menu and DeleteMenuItem is called to delete the Quit item and its preceding
divider from the menu.

The third block utilizes the relatively new Menu Manager function CreateStandardFontMenu to
create a hierarchical font menu.  A reference to the Font menu object is passed in the first
parameter.  The third parameter specifies the menu ID for the first submenu to be created.  The
fourth parameter specifies that the Font menu be created as a hierarchical menu.  The fifth
parameter receives a value representing the number of submenus created.  CreateStandardFontMenu
itself inserts these submenus into the submenu portion of the menu list.

The fourth block checkmarks the Font menu item containing the name of the small system font. 
GetFontName gets the name of the small system font and CountMenuItems counts the number of
items in the Font menu.  The for loop then walks the items in the Font menu looking for a
match.  When it finds a match, CheckMenuItem is called to checkmark the item, the global
variable which keeps track of the currently selected font is assigned the number of that item,
and the for loop is exited.

The fifth block creates the Window menu using the Window Manager function
CreateStandardWindowMenu.  The accessor function SetMenuID sets the menu's ID and InsertMenu
inserts the menu into the menu list.  (Setting the menu ID is for illustrative purposes only
because the ID is not used in this demonstration.  Since the system handles the standard Window
menu automatically, an ID is ordinarily only required for menu adjustment purposes when the
menu has been customised.)

The sixth block inserts a further single submenu (to be attached to the second item in the
Special menu) into the submenu portion of the menu list.  GetNewMBar does not read in the
resource descriptions of submenus, so the first step is to read in the 'MENU' resource with
GetMenu.  InsertMenu inserts a menu reference for this menu into the menu list at the location
specified in the second parameter to this call.  Using the constant hierMenu (-1) as the second
parameter causes the menu to be installed in the submenu portion of the menu list.

The last line causes a checkmark to be set against the Size menu item corresponding to the
initialised value of the global variable gCurrentFontSizeItem.

DrawMenuBar draws the menu bar.

Note that, in Carbon, the contents of the Apple Menu Items folder are automatically added to
the Apple menu.

doEvents

doEvents switches according to the type of low-level or Operating System event received. 
Further processing is called for in the case of mouse-down or Command key equivalents, these
being central to the matter of menu handling.

At the keyDown case, a check is made of the modifiers field of the event structure to establish
whether the Command key was also pressed at the time.  If so, menu enabling/disabling is
attended to before the call to MenuEvent establishes whether the character code is associated
with a currently enabled menu or submenu item in the menu list.  If a match is found, MenuEvent
returns a long integer containing the menu ID in the high word and the item number in the low
word, otherwise it returns 0 in the high word.  This long integer is then passed to the
function doMenuChoice.

doMouseDown

doMouseDown first establishes the window and window part in which the mouse-down event
occurred, and switches accordingly.  This demonstration program is specifically concerned with
mouse-downs in the menu bar and the content region of the window.

If the event occurred in this program's menu bar, menu enabling/disabling is attended to before
the call to MenuSelect.  MenuSelect tracks the user's actions until the mouse button is
released, at which time it returns a long integer.  If the user actually chose a menu item,
this long integer contains the menu ID in the high word and the item number in the low word,
otherwise it contains 0 in the high word.  This long integer is passed to the function 
doMenuChoice.

If the mouse-down event occurred in the content region of the window, and if the window to
which the mouse-down refers is not the front window, SelectWindow is called to effect basic
window activation/deactivation.

doAdjustMenus

doAdjustMenus is called when a mouse-down occurs in the menu bar and when examination of a
key-down event reveals that a menu item's keyboard equivalent has been pressed.  No action is
required in this simple program.

Later demonstration programs contain examples of menu adjustment functions.

doMenuChoice

doMenuChoice takes the long integer returned by the MenuSelect and MenuEvent calls, extracts
the high word (the menu ID) and the low word (the menu item number) and switches according to
the menu ID.

At the first two lines, the menu ID and the menu item number are extracted from the long
integer.  The next two lines will cause an immediate return if the high word equals 0, (meaning
that either the mouse button was released when the pointer was outside the menu box or
MenuEvent found no menu list match for the key pressed in conjunction with the Command key).

If the menu ID represents either the Font menu or one of the Font menuÕs submenus, the menu
handling function doFontMenu is called.  Otherwise, the function switches on the menu ID so
that the appropriate individual menu handling function is called.  Note the handling of the
submenu attached to the second item in the Special menu (case mSubMenu).

The Window menu is handled automatically by the system.

MenuEvent and MenuSelect leave the menu title highlighted if an item was actually chosen. 
Accordingly, the last line unhighlights the menu title when the action associated with the
user's drop-down menu choice is complete.

doAppleApplicationMenu

doAppleApplicationMenu takes the short integer representing the menu item.  If this value
represents the first item in the Mac OS 8/9 Apple menu or Mac OS X Application menu (the
inserted "About..." item), text representing this item is drawn in the scrolling display.

If other items in the Mac OS 8/9 Apple menu are chosen, the system automatically opens the
chosen object and passes control that object.

doFileMenu

doFileMenu handles choices from the File menu when the program is run on Mac OS 8/9.  In this
demonstration, only the Quit item is enabled, all other items having been disabled in the File
menu's 'MENU' resource.  When this item is chosen, the global variable gDone is set to true,
causing termination of the program.

doEditMenu

doEditMenu switches according to the menu item number, drawing text representing the chosen
item in the window.

doFontMenu

doFontMenu first gets a reference to the Font menu object.  This, together with the menu item
number, is passed in a call to the function GetFontFamilyFromMenuSelection.  This function
returns a reference to the font family and a value representing the font style.  (A font family
reference represents a collection of fonts with the same design characteristics, e.g., Arial,
Arial Bold, Arial Italic, and Arial Bold Italic.  Font style values are, for example, 0 =
plain, 1 = bold, 2 = italic, 3 = bold italic).

The font family reference and the font style value are passed in calls to TextFont and
TextFace, which will cause subsequent text drawing to be conducted in the specified font and
style.  The call to GetFontName gets the fontÕs name from the font family reference and the
function drawItemString draws that name in the window.

The remaining code is mainly concerned with checkmarking the newly-chosen Font menu item and
submenu item, and unchecking the previously chosen items.

If the menu ID represents the Font menu (meaning that a menu item without an attached submenu
was chosen), the previously chosen item is unchecked, a global variable stores the item number
of the newly-chosen item preparatory to the next call to doFontmenu, and the newly chosen item
is checked.  If a submenu item has previously been chosen, and thus checked, it is unchecked.

If, on the other hand, the menuID represents one of the Font menuÕs submenus:

o If a submenu item has previously been chosen, that item is unchecked.  A reference to the
  submenu object is assigned to a global variable, the menu item number is stored in another
  global variable preparatory to the next call to doFontmenu, and the newly chosen submenu item
  is checked.

o The next two lines uncheck the previously checked Font menu item.

o The for loop walks the Font menu looking for a match between item names and the font name
  previously extracted from the font family reference.  When a match is found, the loop exits,
  the loop variable containing the item number where the match was found.  This is stored in a
  global variable preparatory to the next call to doFontMenu, and is also passed in the call to
  CheckMenuItem to check that item.

o	The last block gets the style name from the menu object and draws that next to the font name
in the window.

doSizeMenu

doSizeMenu switches according to the menu item chosen in the Size menu, sets the text size for
all text drawing to that size, unchecks the current size item, and checks the newly chosen
item.  gCurrentSize is then set to the chosen menu item number before the function returns.

doSpecialMenu

doSpecialMenu handles a choice of the first item in the Special menu.  Since the second item is
the title of a submenu, only the first item is attended to in this function.

doSubMenus

doSubMenus switches according to the chosen item in the submenu attached to the second menu
item in the Special menu.

drawItemString

The function drawItemString is incidental to the demonstration, being called by the menu
selection handling functions to draw text in the application's window to reflect the user's
menu choices.

Demonstration Program Menus2

// *******************************************************************************************
// Menus2.c                                                                CLASSIC EVENT MODEL
// *******************************************************************************************
// 
// This program is based on Menus1.  The basic differences between this program and Menus1 are
// as follows:
//
// o  'xmnu' resources are used to extend the 'MENU' resources for some menus.
//
// o  Extended modifier keys (Shift, Option, and Control) are used to extend the Command-key
//    equivalents for two menu items in the Style menus.
//
// o  There are two Style menus (Style ('xmnu') and Style (Programmatic).  The two Style menus
//    are intended to demonstrate assigning extended modifier keys to a menu item (1) via an
//    'xmnu' resource and (2)  programmatically.
//
// o  Command IDs are assigned to all menu items except those in the system-managed menus and
//    the Font menu, and the associated menu handling code branches according to the command
//    ID of the chosen menu item (as opposed to menu ID and menu item).
//
// o  The Font menu is non-hierarchical.  It is also WYSIWYG, meaning that each item is drawn
//    in that font.
//
// o  The delete-to-the-left, delete-to-the-right, page-up, and page-down keys are assigned as 
//    Command-key equivalents in the Size menu, and the glyphs are adjusted where necessary.
//
// o  The submenu is attached to the second item in the Special menu programmatically rather 
//    than via the 'MENU' resource.
// 
// o  Colour icons are included in the menu items in the submenu.
//
// o  Balloon help is provided, via 'hmnu' resources, for all menus.
//
// The extended modifier keys in the Style ('xmnu') menu are assigned via the 'xmnu' resource
// for that menu.  The extended  modifier keys in the Style (Programmatic) menu are assigned 
// programmatically .  
//
// The command IDs for items in the File, Edit, and Style ('xmnu') menus are assigned via the
// 'xmnu' resources for those menus.  The command IDs for the items in the Style
// (Programmatic), Size, and Special menus, and the submenu, are assigned programmatically.
//
// The colour icon in the first submenu item is assigned via the 'MENU' resource.  The colour
// icon in the second item is assigned programmatically via a call to
// SetMenuItemIconHandle.
//
// The program utilises the following resources:
//
// o  A 'plst' resource.
//
// o  A 'WIND' resource (purgeable) (initially not visible).
//
// o  An 'MBAR' resource (preload, non-purgeable).
//
// o  'MENU' resources for the drop-down menus and submenu (all preload, all non-purgeable).
//
// o  'xmnu' resources (preload, purgeable) for the drop-down menus (except the system-managed
//     menus and the Font menu) and the submenu.
//
// o  'hmnu' resources (purgeable) providing balloon help for menus and menu items.
//
// o  Two 'cicn' resources (purgeable) for the items in the submenu.
//
// o  A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, 
//    doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************

// .................................................................................. includes

#include <Carbon.h>

// ................................................................................... defines

#define rMenubar          128
#define mAppleApplication 128
#define mFile             129
#define  iQuit            12
#define mFont             131
#define mStyleXmnu        132
#define mStyleProg        133
#define  iPlain           1
#define  iBold            3
#define  iItalic          4
#define  iOutline         6
#define  iUnderline       5
#define  iShadow          7
#define mSize             134
#define  iTen             1
#define  iTwelve          2
#define  iEighteen        3
#define  iTwentyFour      4
#define mSpecial          135
#define  iFirst           1
#define  iSecond          2
#define mSubmenu          136
#define  iBat             1
#define  iBowl            2
#define rWindowResource   128
#define rColourIcon       258

// .......................................................................... global variables

Boolean       gRunningOnX          = false;
Boolean       gDone;
MenuItemIndex gCurrentFontMenuItem = 0;
Style         gCurrentStyle        = 0;
MenuItemIndex gCurrentSizeMenuItem = 2;

// ....................................................................... function prototypes

void  main                 (void);
void  doPreliminaries      (void);
OSErr  quitAppEventHandler (AppleEvent *,AppleEvent *,SInt32);
void  doGetMenus           (void);
void  doEvents             (EventRecord *);
void  doMouseDown          (EventRecord *);
void  doAdjustMenus        (void);
void  doMenuChoice         (SInt32);
void  doCommand            (MenuCommand);
void  doFontMenu           (MenuItemIndex);
void  doCheckStyleMenuItem (MenuID);
void  doCheckSizeMenuItem  (MenuItemIndex);
void  drawItemString       (Str255);

// ************************************************************************************** main

void  main(void)
{
  EventRecord eventStructure;
  WindowRef   windowRef;
  RGBColor    foreColour = { 0xFFFF,0xFFFF,0xFFFF };
  RGBColor    backColour = { 0x4444,0x4444,0x9999 };
  Rect        portRect;
  
  // ........................................................................ do preliminaries

  doPreliminaries();
  
  // ........................................................................... open a window
    
  if(!(windowRef = GetNewCWindow(rWindowResource,NULL,(WindowRef) -1)))
  {
    SysBeep(10);
    ExitToShell();
  }

  SetPortWindowPort(windowRef);
  TextSize(10);
  RGBBackColor(&backColour);
  RGBForeColor(&foreColour);

  // ............................................. set up menu bar and menus, then show window
  
  doGetMenus();
  ShowWindow(windowRef);
  GetWindowPortBounds(windowRef,&portRect);
  EraseRect(&portRect);

  // .............................................................................. event loop

  gDone = false;

  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&eventStructure,180,NULL))
      doEvents(&eventStructure);
  }
}

// *************************************************************************** doPreliminaries

void  doPreliminaries(void)
{
  OSErr osError;

  MoreMasterPointers(32);
  InitCursor();
  FlushEvents(everyEvent,0);

  osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
                            NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
                            0L,false);
  if(osError != noErr)
    ExitToShell();
}

// **************************************************************************** doQuitAppEvent

OSErr  quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
  OSErr    osError;
  DescType returnedType;
  Size     actualSize;

  osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0,
                              &actualSize);

  if(osError == errAEDescNotFound)
  {
    gDone = true;
    osError = noErr;
  } 
  else if(osError == noErr)
    osError = errAEParamMissed;

  return osError;
}

// ******************************************************************************** doGetMenus

void  doGetMenus(void)
{
  MenuBarHandle menubarHdl;
  SInt32        response;
  MenuRef       menuRef;
  OSStatus      osError;
  ItemCount     hierMenuCount;
  SInt16        a, numberOfItems, fontNumber;
  Str255        fontName, smallSystemFontName;
  CIconHandle   cicnHdl;

  // .................................................................... get and set menu bar

  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);

  Gestalt(gestaltMenuMgrAttr,&response);
  if(response & gestaltMenuMgrAquaLayoutMask)
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
    {
      DeleteMenuItem(menuRef,iQuit);
      DeleteMenuItem(menuRef,iQuit - 1);
      DisableMenuItem(menuRef,0);
    }

    gRunningOnX = true;
  }

  // ....................................................... set up Font menu and make WYSIWYG

  GetFontName(kThemeSmallSystemFont,smallSystemFontName);

  menuRef = GetMenuRef(mFont);
  if(menuRef != NULL)
  {
    osError = CreateStandardFontMenu(menuRef,0,0,kNilOptions,&hierMenuCount);
    if(osError == noErr)
    {
      numberOfItems = CountMenuItems(menuRef);
      for(a=1;a<=numberOfItems;a++)
      {
        GetMenuItemText(menuRef,a,fontName);
        GetFNum(fontName,&fontNumber);
        SetMenuItemFontID(menuRef,a,fontNumber);

        if(EqualString(fontName,smallSystemFontName,false,false))
        {
          CheckMenuItem(menuRef,a,true);
          gCurrentFontMenuItem = a;
        }
      }
    }
    else ExitToShell();
  }
  else
    ExitToShell();

  // ................ programmatically set the extended modifiers in Style (Programmatic) menu

  menuRef = GetMenuRef(mStyleProg);
  SetMenuItemModifiers(menuRef,iOutline,kMenuShiftModifier + kMenuOptionModifier
                       + kMenuControlModifier);
  SetMenuItemModifiers(menuRef,iShadow,kMenuShiftModifier + kMenuOptionModifier);

  // .... insert submenu into menu list and programmatically attach it to Special menu, item 2

  menuRef = GetMenu(mSubmenu);
  if(menuRef != NULL)
  {
    InsertMenu(menuRef,hierMenu);
    menuRef = GetMenuRef(mSpecial);
    SetMenuItemHierarchicalID(menuRef,iSecond,mSubmenu);
  }
  else
    ExitToShell();

  // ...... programmatically set command IDs for second Style, Size, Special menus and submenu

  menuRef = GetMenuRef(mStyleProg);
  SetMenuItemCommandID(menuRef,iPlain,     'plai');
  SetMenuItemCommandID(menuRef,iBold,      'bold');
  SetMenuItemCommandID(menuRef,iItalic,    'ital');
  SetMenuItemCommandID(menuRef,iUnderline, 'unde');
  SetMenuItemCommandID(menuRef,iOutline,   'outl');
  SetMenuItemCommandID(menuRef,iShadow,    'shad');

  menuRef = GetMenuRef(mSize);
  SetMenuItemCommandID(menuRef,iTen,       'ten ');
  SetMenuItemCommandID(menuRef,iTwelve,    'twel');
  SetMenuItemCommandID(menuRef,iEighteen,  'eigh');
  SetMenuItemCommandID(menuRef,iTwentyFour,'twen');

  menuRef = GetMenuRef(mSpecial);
  SetMenuItemCommandID(menuRef,iFirst,     'firs');

  menuRef = GetMenuRef(mSubmenu);
  SetMenuItemCommandID(menuRef,iBat,       'bat ');
  SetMenuItemCommandID(menuRef,iBowl,      'bowl');
  
  // .......................... programmatically set the icon for the Bowl item in the submenu

  cicnHdl = GetCIcon(rColourIcon);
  SetMenuItemIconHandle(menuRef,iBowl,kMenuColorIconType,(Handle) cicnHdl);

  // ....... programmatically set Command-key equivalents to Size menu items and adjust glyphs

  menuRef = GetMenuRef(mSize);
  SetItemCmd(menuRef,iTen,0x08);
  SetMenuItemKeyGlyph(menuRef,iTen,kMenuDeleteLeftGlyph);
  SetItemCmd(menuRef,iTwelve,0x7f);
  SetMenuItemKeyGlyph(menuRef,iTwelve,kMenuDeleteRightGlyph);
  SetItemCmd(menuRef,iEighteen,0x0b);
  SetMenuItemKeyGlyph(menuRef,iEighteen,kMenuPageUpGlyph);
  SetItemCmd(menuRef,iTwentyFour,0x0c);
  SetMenuItemKeyGlyph(menuRef,iTwentyFour,kMenuPageDownGlyph);

  // ........... programmatically exclude the mark column and set the font in the Special menu

  menuRef = GetMenuRef(mSpecial);
  SetMenuExcludesMarkColumn(menuRef,true);
    
  GetFNum("\pGadget",&fontNumber);
  if(fontNumber != 0)
    SetMenuFont(menuRef,fontNumber,12);
    
  // ............................ if running on Mac OS X, create Help menu and insert one item

  if(gRunningOnX)
  {
    HMGetHelpMenu(&menuRef,NULL);
    InsertMenuItem(menuRef,"\pMenus Help",0);
    SetMenuItemCommandID(menuRef,1,'help');
  }

  // ................................... set initial font, style, and size, and checkmark them

  doCheckStyleMenuItem(mStyleXmnu);
  doCheckStyleMenuItem(mStyleProg);
  doCheckSizeMenuItem(iTen);

  // ........................................................................... draw menu bar

  DrawMenuBar();
}

// ********************************************************************************** doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  switch(eventStrucPtr->what)
  {
    case kHighLevelEvent:
      AEProcessAppleEvent(eventStrucPtr);
      break;

    case mouseDown:
      doMouseDown(eventStrucPtr);
      break;

    case keyDown:
      if((eventStrucPtr->modifiers & cmdKey) != 0)
      {
        doAdjustMenus();
        doMenuChoice(MenuEvent(eventStrucPtr));
      }
      break;

    case updateEvt:
      BeginUpdate((WindowRef) eventStrucPtr->message);
      EndUpdate((WindowRef) eventStrucPtr->message);
      break;
  }
}

// ******************************************************************************* doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  WindowRef      windowRef;
  WindowPartCode partCode;
  SInt32         menuChoice;

  partCode = FindWindow(eventStrucPtr->where,&windowRef);
  
  switch(partCode)
  {
    case inMenuBar:
      doAdjustMenus();
      menuChoice = MenuSelect(eventStrucPtr->where);
      doMenuChoice(menuChoice);
      break;

    case inContent:
      if(windowRef != FrontWindow())
        SelectWindow(windowRef);
      break;

    case inDrag:
      DragWindow(windowRef,eventStrucPtr->where,NULL);
      break;

    case inGoAway:
      if(TrackGoAway(windowRef,eventStrucPtr->where))
        gDone = true;
      break;
  }
}

// ***************************************************************************** doAdjustMenus

void  doAdjustMenus(void)
{
  // Adjust menus here.  Use EnableMenuCommand and DisableMenuCommand to enable/disable those
  // menu items with command IDs.
}

// ****************************************************************************** doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  MenuID        menuID;
  MenuItemIndex menuItem;
  OSErr         osErr;
  MenuCommand   commandID;

  menuID   = HiWord(menuChoice);
  menuItem = LoWord(menuChoice);

  if(menuID == 0)
    return;
  else if(menuID == mFont)
    doFontMenu(menuItem);
  else
  {
    osErr = GetMenuItemCommandID(GetMenuRef(menuID),menuItem,&commandID);
    if(osErr == noErr && commandID != 0)
      doCommand(commandID);
  }

  HiliteMenu(0);
}

// ********************************************************************************* doCommand

void  doCommand(MenuCommand commandID)
{
  MenuRef menuRef;

  switch(commandID)
  {
    // ................................................................ Apple/Application menu

    case 'abou':                                                                      // About
      drawItemString("\pAbout Menus2");
      break;

    // ............................................................................. File menu

    case 'quit':                                                                       // Quit
      gDone = true;
      break;
      
    // ............................................................................. Edit menu

    case 'undo':                                                                       // Undo
      drawItemString("\pUndo");
      break;

    case 'cut ':                                                                        // Cut
      drawItemString("\pCut");
      break;

    case 'copy':                                                                       // Copy
      drawItemString("\pCopy");
      break;

    case 'past':                                                                      // Paste
      drawItemString("\pPaste");
      break;

    case 'clea':                                                                      // Clear
      drawItemString("\pClear");
      break;

    // .......................................... Style ('xmnu') and Style (Programmatic) menu

    case 'plai':                                                                      // Plain
      gCurrentStyle = 0;
      doCheckStyleMenuItem(mStyleXmnu);
      doCheckStyleMenuItem(mStyleProg);
      break;

    case 'bold':                                                                       // Bold
      if(gCurrentStyle & bold)
        gCurrentStyle -= bold;
      else
        gCurrentStyle |= bold;
      doCheckStyleMenuItem(mStyleXmnu);
      doCheckStyleMenuItem(mStyleProg);
      break;

    case 'ital':                                                                    // Italics
      if(gCurrentStyle & italic)
        gCurrentStyle -= italic;
      else
      gCurrentStyle |= italic;
      doCheckStyleMenuItem(mStyleXmnu);
      doCheckStyleMenuItem(mStyleProg);
      break;

    case 'unde':                                                                  // Underline
      if(gCurrentStyle & underline)
        gCurrentStyle -= underline;
      else
        gCurrentStyle |= underline;
      doCheckStyleMenuItem(mStyleXmnu);
      doCheckStyleMenuItem(mStyleProg);
      break;

    case 'outl':                                                                    // Outline
      if(gCurrentStyle & outline)
        gCurrentStyle -= outline;
      else
        gCurrentStyle |= outline;
      doCheckStyleMenuItem(mStyleXmnu);
      doCheckStyleMenuItem(mStyleProg);
      break;

    case 'shad':                                                                     // Shadow
      if(gCurrentStyle & shadow)
        gCurrentStyle -= shadow;
      else
        gCurrentStyle |= shadow;
      doCheckStyleMenuItem(mStyleXmnu);
      doCheckStyleMenuItem(mStyleProg);
      break;

    // ............................................................................. Size menu

    case 'ten ':                                                                         // 10
      TextSize(10);
      doCheckSizeMenuItem(iTen);
      break;

    case 'twel':                                                                         // 12
      TextSize(12);
      doCheckSizeMenuItem(iTwelve);
      break;

    case 'eigh':                                                                         // 18
      TextSize(18);
      doCheckSizeMenuItem(iEighteen);
      break;

    case 'twen':                                                                         // 24
      TextSize(24);
      doCheckSizeMenuItem(iTwentyFour);
      break;
        
    // .......................................................................... Special menu
  
    case 'firs':                                                                      // First
      drawItemString("\pFirst Item");
      break;

    // ............................................................................... submenu

    case 'bat ':                                                                        // Bat
      menuRef = GetMenuRef(mSubmenu);
      DisableMenuItem(menuRef,iBat);
      EnableMenuItem(menuRef,iBowl);
      drawItemString("\pBat");
      break;

    case 'bowl':                                                                       // Bowl
      menuRef = GetMenuRef(mSubmenu);
      DisableMenuItem(menuRef,iBowl);
      EnableMenuItem(menuRef,iBat);
      drawItemString("\pBowl");
      break;

    case 'help':
      AHGotoPage(CFSTR("Menus Help"),CFSTR("Menus.htm"),NULL);
      break;
  }
}

// ******************************************************************************** doFontMenu

void  doFontMenu(MenuItemIndex menuItem)
{
  MenuRef      menuRef;
  OSStatus     osError;
  FMFontFamily currentFontFamilyReference;
  FMFontStyle  fontStyle;
  Str255       fontName;
  
  menuRef = GetMenuRef(mFont);

  osError = GetFontFamilyFromMenuSelection(menuRef,menuItem,¤tFontFamilyReference,
            &fontStyle);

  if(osError == noErr || osError == menuPropertyNotFoundErr)
  {
    TextFont(currentFontFamilyReference);

    CheckMenuItem(menuRef,gCurrentFontMenuItem,false);
    gCurrentFontMenuItem = menuItem;
    CheckMenuItem(menuRef,gCurrentFontMenuItem,true);

    GetMenuItemText(menuRef,menuItem,fontName);
    drawItemString(fontName);
  }
  else
    ExitToShell();
}

// ********************************************************************** doCheckStyleMenuItem

void  doCheckStyleMenuItem(MenuID menuID)
{
  MenuRef        styleMenuRef;
  static Boolean stringAlreadyDrawnOnce = false;

  styleMenuRef = GetMenuRef(menuID);

  CheckMenuItem(styleMenuRef,iPlain,    gCurrentStyle == 0);
  CheckMenuItem(styleMenuRef,iBold,      gCurrentStyle & bold);
  CheckMenuItem(styleMenuRef,iItalic,    gCurrentStyle & italic);
  CheckMenuItem(styleMenuRef,iUnderline,gCurrentStyle & underline);
  CheckMenuItem(styleMenuRef,iOutline,  gCurrentStyle & outline);
  CheckMenuItem(styleMenuRef,iShadow,    gCurrentStyle & shadow);
  
  TextFace(gCurrentStyle);

  if(!stringAlreadyDrawnOnce)
    drawItemString("\pStyle change");

  stringAlreadyDrawnOnce = !stringAlreadyDrawnOnce;
}

// *********************************************************************** doCheckSizeMenuItem

void  doCheckSizeMenuItem(MenuItemIndex menuItem)
{  
  MenuRef sizeMenuRef;  

  sizeMenuRef = GetMenuRef(mSize);

  CheckMenuItem(sizeMenuRef,gCurrentSizeMenuItem,false);
  CheckMenuItem(sizeMenuRef,menuItem,true);
  
  gCurrentSizeMenuItem = menuItem;
  
  drawItemString("\pSize change");
}

// **************************************************************************** drawItemString

void  drawItemString(Str255 eventString)
{
  RgnHandle tempRegion;
  WindowRef windowRef;
  Rect      scrollBox;
  
  windowRef = FrontWindow();
  tempRegion = NewRgn();

  GetWindowPortBounds(windowRef,&scrollBox);

  ScrollRect(&scrollBox,0,-30,tempRegion);
  DisposeRgn(tempRegion);
  
  MoveTo(8,286);
  DrawString(eventString);
}

// *******************************************************************************************

Demonstration Program Menus2 Comments

When this program is run, the user should choose Show Balloons from the Help menu and make menu
choices from all menus, including the Apple menu.  Choices should be made using the mouse and,
where appropriate, the keyboard equivalents.  The user should note:

o The extended modifier keys assigned to the last two items in the Style menus.

o The Command-key equivalents assigned to the items in the Size menu.  (These are, in order,
  delete-to-the-left key, delete-to-the-right key, page-up key, and page-down key.)

o That the Font menu is WYSIWYG.

o That the marking character column has been deleted from the Special menu and the menu items
  in this menu are drawn in the Gadget font (assuming it is available).

o That the items in the submenu attached to the second item in the Special menu have colour
  icons.

o The balloon help provided for all menus and menu items.
The Menus2 demonstration program package also includes a demonstration of Apple Help, including
the methodology used to create an item in the Mac OS 8/9 Help menu.  The Apple Guide file
titled "Menus Guide", which will cause a "Menus Help" item to be created in the Mac OS 8/9 Help
menu, should be retained in the same folder as the Menus2 application.  An alias of the folder
titled "Menus Help" should be placed in the Help folder in the System Folder (Mac OS 8/9) and
in the user's Help folder (~/Library/Documentation/Help) (Mac OS X).  You will then be able to
access the help content by choosing Menus Help from the Help menu.

The help content does not provide user assistance for Menus2 programs as such.  Rather, it
provides a brief description of how to provide user assistance for your application using Apple
Help.

Because this demonstration program is based on Menus1, the following comments exclude those for
the functions that remain unchanged.

main

The calls to RGBBackColor and RGBForeColor set the window background and foreground colours to,
respectively, dark blue and white.

doGetMenus

doGetMenus sets up the menu bar and the various menus.

GetNewMBar reads in the 'MENU' resources for each menu specified in the 'MBAR' resource and
creates a menu object for each of those menus.  (Note that the error handling here and in other
areas of this program is somewhat rudimentary: the program simply terminates.)  SetMenuBar
makes the newly created menu list the current list.

The next block utilizes the Menu Manager function CreateStandardFontMenu in the creation of a
non-hierarchical Font menu.  Following the call to CreateStandardFontMenu, the process of
making the menu WYSIWYG begins.  The call to CountMenuItems returns the number of items in the
menu.  Then, for each of these items, GetMenuItemText gets the font's name, GetFNum gets the
font number associated with the font name, and SetMenuItemFontID sets the font for the menu
item.  In the following if block, the current item is checkmarked if the item name equals the
name of the small system font, and the global variable which keeps track of the currently
selected font is assigned the item number.

The next block programmatically assigns extended modifier keys to the Outline and Shadow items
in the Style (Programmatic) menu.  The SetMenuItemModifiers calls assign Shift-Option-Control
to the Outline item and Shift-Option to the Shadow item.  (The extended modifier keys for the
same two items in the Style ('xmnu') menu are assigned in the associated 'xmnu' resources.)

The next block inserts the application's single submenu into the submenu portion of the menu
list and programmatically attaches it to the Special menu's second menu item.  GetNewMBar does
not read in the resource descriptions of submenus, so the first step is to read in the 'MENU'
resource with GetMenu.  InsertMenu inserts a menu object for this menu into the menu list at
the location specified in the second parameter to this call.  (Using the constant hierMenu (-1)
as the second parameter causes the menu to be installed in the submenu portion of the menu
list.)  The call to GetMenuRef gets a reference to the Special menu, which is used in the
following call to SetMenuHierarchicalID to attach the submenu to the second item in the Special
menu.

The following rather large block programmatically assigns command IDs to all items in the Style
(Programmatic), Size, and Special menus and the submenu.  (Command IDs for the File and Style
('xmnu') menus are assigned in the associated 'xmnu' resources.  It is not possible to assign
command IDs to the items in the Font menu.)  The Command IDs are defined in the
four-character-code format, which packs four one-byte characters together in a 32-bit value. 
For example, 'plai' expressed as hexadecimal is 0x706C6169.  70 is the ASCII code for p, 6C is
the ASCII code for l, and 69 is the ASCII code for i.

The following block programmatically assigns a colour icon to the second item in the submenu. 
The call to GetCIcon creates a CIcon data structure and initializes it from data read in from
the specified 'cicn' resource.  The handle to this structure is then passed as the last
parameter in the SetMenuItemIconHandle, the third parameter specifying that the type of icon is
a colour icon.  (The colour icon for the first item in the submenu is assigned in the
associated 'xmnu' resource.)

The next block programmatically assigns command-key equivalents to the items of the Size menu. 
(Because the keys assigned are the two delete keys and the page-up and page-down keys, it is
not possible to make these assignments within the 'MENU' resource.)  Also, a substitute glyph
must be assigned, otherwise the correct glyphs will not be displayed.  The calls to SetItemCmd
assign the specified key to the menu item, and a substitute glyph is assigned via calls to
SetMenuItemGlyph.  If this is not done, the glyphs displayed will not be the correct visual
representations of the keys.  (These substitute glyphs could also have been specified in the
keyboard glyph fields for these items in the menu's 'xmnu' resource.)

In the next block, SetMenuExcludesMarkColumn is called to delete the marking character column
from the Special menu and SetMenuFont is called to set the font for the menu items in this menu
to Gadget (assuming that font is present).

In the next block, and only if the program is running on Mac OS X, HMGetHelpMenu is called to
create a Help menu, InsertMenuItem is called to insert a single item in that menu, and
SetMenuItemCommandID assigns a command ID to that item.

The next block sets checkmarks against the appropriate font, style and size menu items
according to the initialised values of the associated global variables.

The call to DrawMenuBar draws the menu bar

Note that, in Carbon, the contents of the Apple Menu Items folder are automatically added to
the Apple menu.

doMenuChoice

doMenuChoice extracts the menu ID and menu item number from the long integer returned by the
MenuSelect and MenuEvent calls.  An immediate return is made if the high word equals 0.  The
function "special cases" the Font menus, calling the function for handling choices from that
menu.  Otherwise, GetMenuItemCommandID is called.  GetMenuItemCommandID returns zero as the
function result if the call is successful, and a pointer to an integer representing the value
of the item's command ID will be returned in the third parameter.  If the call is successful,
and if a zero is not returned in the third parameter, a command ID exists for the item. 
Accordingly, the command ID is passed in a call to the function doCommand.

MenuSelect and MenuEvent leave the menu title highlighted if an item was actually chosen. 
Accordingly, the last line unhighlights the menu title when the action associated with the
user's drop-down menu choice is complete. 

doCommand

doCommand handles choices from those menus whose items have command IDs.

Note that the initial handling of all of the remaining menu items, regardless of which menu
they belong to, is attended to within the one switch in the one function.  The responses to the
user choosing the various menu items is the same as in Menus1, except that the code relating to
checkmarking the Style menu items has been added and the code for checkmarking the Size menu
items and storing the current size has been divided between this function a further handling
function (doCheckSizeMenuItem).

At the block titled Style ('xmnu') and Style (Programmatic) menu, bits in the global variable
gCurrentStyle are set or unset according to the font styles selected.  The code reflects the
fact that Bold, Italic, Underline, Outline and Shadow style selections are additive, not
mutually exclusive, and that a selection of Plain must unset all bits in gCurrentStyle.  The
code also reflects the requirement that, except in the case of the Plain item, the selection of
a checked item must cause that item to be unchecked, and vice versa.  With gCurrentStyle set,
the function doCheckStyleMenuItem is called to check/uncheck the relevant menu items as
appropriate.

Note that the handling of the two submenu items has been changed to make the items mutually
exclusive. 

The 'help' command ID case applies only when the program is run on Mac OS X.  The function
AHGoToPage is called to deliver a request to load the specified HTML file in the specified Help
book folder to the Help Viewer application.

doCheckStyleMenuItem

doCheckStyleMenuItem is called from doMenuChoice when an item in the Style menu is chosen. 
With the appropriate bit settings of gCurrentStyle attended to within doMenuChoice, a reference
to the Style menu object is obtained.  This is required for the six CheckMenuItem calls, which
check or uncheck the individual menu items according to whether the third parameter evaluates
to, respectively, true or false.

The call to TextFace sets the style for subsequent text drawing.  The last line draws some text
to prove that the desired effect was achieved.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Netflix Games expands its catalogue with...
It is a good time to be a Netflix subscriber this month. I presume there's a good show or two, but we are, of course, talking about their gaming service that seems to be picking up steam lately. May is adding five new titles, and there are some... | Read more »
Seven Knights Idle Adventure drafts in a...
Seven Knights Idle Adventure is opening up more stages, passing the 15k mark, and players may find themselves in need of more help to clear these higher stages. Well, the cavalry has arrived with the introduction of the Legendary Hero Iris, as... | Read more »
AFK Arena celebrates five years of 100 m...
Lilith Games is quite the behemoth when it comes to mobile games, with Rise of Kingdom and Dislyte firmly planting them as a bit name. Also up there is AFK Arena, which is celebrating a double whammy of its 5th anniversary, as well as blazing past... | Read more »
Fallout Shelter pulls in ten times its u...
When the Fallout TV series was announced I, like I assume many others, assumed it was going to be an utter pile of garbage. Well, as we now know that couldn't be further from the truth. It was a smash hit, and this success has of course given the... | Read more »
Recruit two powerful-sounding students t...
I am a fan of anime, and I hear about a lot that comes through, but one that escaped my attention until now is A Certain Scientific Railgun T, and that name is very enticing. If it's new to you too, then players of Blue Archive can get a hands-on... | Read more »
Top Hat Studios unveils a new gameplay t...
There are a lot of big games coming that you might be excited about, but one of those I am most interested in is Athenian Rhapsody because it looks delightfully silly. The developers behind this project, the rather fancy-sounding Top Hat Studios,... | Read more »
Bound through time on the hunt for sneak...
Have you ever sat down and wondered what would happen if Dr Who and Sherlock Holmes went on an adventure? Well, besides probably being the best mash-up of English fiction, you'd get the Hidden Through Time series, and now Rogueside has announced... | Read more »
The secrets of Penacony might soon come...
Version 2.2 of Honkai: Star Rail is on the horizon and brings the culmination of the Penacony adventure after quite the escalation in the latest story quests. To help you through this new expansion is the introduction of two powerful new... | Read more »
The Legend of Heroes: Trails of Cold Ste...
I adore game series that have connecting lore and stories, which of course means the Legend of Heroes is very dear to me, Trails lore has been building for two decades. Excitedly, the next stage is upon us as Userjoy has announced the upcoming... | Read more »
Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »

Price Scanner via MacPrices.net

Apple Watch Ultra 2 on sale for $50 off MSRP
Best Buy is offering Apple Watch Ultra 2 models for $50 off MSRP on their online store this week. Sale prices available for online orders only, in-store prices may vary. Order online, and choose free... Read more
Apple introduces the new M4-powered 11-inch a...
Today, Apple revealed the new 2024 M4 iPad Pro series, boasting a surprisingly thin and light design that pushes the boundaries of portability and performance. Offered in silver and space black... Read more
Apple introduces the new 2024 11-inch and 13-...
Apple has unveiled the revamped 11-inch and brand-new 13-inch iPad Air models, upgraded with the M2 chip. Marking the first time it’s offered in two sizes, the 11-inch iPad Air retains its super-... Read more
Apple discontinues 9th-gen iPad, drops prices...
With today’s introduction of the new 2024 iPad Airs and iPad Pros, Apple has (finally) discontinued the older 9th-generation iPad with a home button. In response, they also dropped prices on 10th-... Read more
Apple AirPods on sale for record-low prices t...
Best Buy has Apple AirPods on sale for record-low prices today starting at only $79. Buy online and choose free shipping or free local store pickup (if available). Sale price for online orders only,... Read more
13-inch M3 MacBook Airs on sale for $100 off...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices, along with Amazon’s, are the lowest currently available for new 13″... Read more
Amazon is offering a $100 discount on every 1...
Amazon has every configuration and color of Apple’s 13″ M3 MacBook Air on sale for $100 off MSRP, now starting at $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD): $999 $100 off... Read more
Sunday Sale: Take $150 off every 15-inch M3 M...
Amazon is now offering a $150 discount on every configuration and color of Apple’s M3-powered 15″ MacBook Airs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 15″ M3 MacBook... Read more
Apple’s 24-inch M3 iMacs are on sale for $150...
Amazon is offering a $150 discount on Apple’s new M3-powered 24″ iMacs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 24″ M3 iMac/8-core GPU/8GB/256GB: $1149.99, $150 off... Read more
Verizon has Apple AirPods on sale this weeken...
Verizon has Apple AirPods on sale for up to 31% off MSRP on their online store this weekend. Their prices are the lowest price available for AirPods from any Apple retailer. Verizon service is not... Read more

Jobs Board

IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: May 8, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Nurse Anesthetist - *Apple* Hill Surgery Ce...
Nurse Anesthetist - Apple Hill Surgery Center Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
LPN-Physician Office Nurse - Orthopedics- *Ap...
LPN-Physician Office Nurse - Orthopedics- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Supervisor/Therapist Rehabilitation Medicine...
Supervisor/Therapist Rehabilitation Medicine - Apple Hill (Outpatient Clinic) - Day Location: York Hospital, York, PA Schedule: Full Time Sign-On Bonus Eligible Read more
BBW Sales Support- *Apple* Blossom Mall - Ba...
BBW Sales Support- APPLE BLOSSOM MALL Brand: Bath & Body Works Location: Winchester, VA, US Location Type: On-site Job ID: 04388 Job Area: Store: Sales and Support Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.