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

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 »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, donā€™t remind me I could currently buy him this month Iā€™m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly ā€œborrowedā€ many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »

Price Scanner via MacPrices.net

13-inch M2 MacBook Airs in stock today at App...
Apple has 13ā€³ M2 MacBook Airs available for only $849 today in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Appleā€™s one-year warranty is included,... Read more
New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Appleā€™s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
Weā€™ve updated our iPhone Price Tracker with the latest carrier deals on Appleā€™s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobileā€™s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobileā€™s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13ā€³ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Appleā€™s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16ā€³ and 14ā€³ MacBook Pros along with 13ā€³ and 15ā€³ MacBook... Read more
Every model of Appleā€™s 13-inch M3 MacBook Air...
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 are the lowest currently available for new 13ā€³ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9ā€³ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more

Jobs Board

Solutions Engineer - *Apple* - SHI (United...
**Job Summary** An Apple Solution Engineer's primary role is tosupport SHI customers in their efforts to select, deploy, and manage Apple operating systems and Read more
DMR Technician - *Apple* /iOS Systems - Haml...
ā€¦relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.