MenuHandler.c

/*
    File:       MenuHandler.c
 
    Contains:   Menu-handling routines
 
    Written by: Forrest Tanaka  
 
    Copyright:  Copyright © 1988-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/13/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
 
/******************************************************************************\
* Header Files
\******************************************************************************/
 
#ifndef THINK_C
#include <Desk.h>
#include <Errors.h>
#include <Memory.h>
#include <Menus.h>
#include <Resources.h>
#endif
 
#include "ColorReset.h"
#include "MenuHandler.h"
#include "PictDocument.h"
 
 
/******************************************************************************\
* Constants
\******************************************************************************/
 
#define rMenuBar 128 /* Resource ID of this applicationÕs MBAR resource */
 
#define kNumMenuGuides (mLastMenu - mApple + 1) /* Number of menu guide recs */
 
 
/******************************************************************************\
* Types
\******************************************************************************/
 
typedef struct
{
    MenuHandle theMenu; /* Handle to this guideÕs menu */
    long       enables; /* Current enable flags */
} MenuGuide;
 
 
/******************************************************************************\
* Prototypes
\******************************************************************************/
 
void DoAppleMenu(
    short menuItem);
 
void DoFileMenu(
    short menuItem);
 
void DoEditMenu(
    short menuItem);
 
void DoDisplayMenu(
    short menuItem);
 
void ResetMenuItems(void);
 
void FixDAMenus(void);
 
 
/******************************************************************************\
* Variables
\******************************************************************************/
 
MenuGuide gMenuGuides[kNumMenuGuides]; /* ApplicationÕs menu guide records */
 
 
/******************************************************************************\
* Public: StartMenus
*
* The menu guide array is initialized with the menu handles and enable flags of
* all menus.
*
* If GetNewMBar couldnÕt load the MBAR resource, then it returns nil and the
* error code is in ResError, and I can deal with the error elegantly.  But, if
* the GetNewMBar couldnÕt load the menus themselves, then itÕll probably crash.
\******************************************************************************/
 
OSErr StartMenus()
{
    Handle menuBar;   /* Handle to the menu bar from the MBAR resource */
    short  menuIndex; /* Index into menu guide records */
    OSErr  error;
 
    /* Load in the menu bar */
    menuBar = GetNewMBar( rMenuBar );
    if (menuBar != nil)
    {
        /* Set it, then dispose of it because SetMenuBar makes a copy */
        SetMenuBar( menuBar );
        DisposeHandle( menuBar );
 
        /* Add the desk accessories to the Apple menu */
        AppendResMenu( GetMenuHandle( mApple ), 'DRVR' );
 
        /* Initialize the menu guide array */
        for (menuIndex = 0; menuIndex < kNumMenuGuides; menuIndex++)
        {
            gMenuGuides[menuIndex].theMenu = GetMenuHandle( menuIndex + mApple );
            gMenuGuides[menuIndex].enables = (**gMenuGuides[menuIndex].
                    theMenu).enableFlags;
        }
 
        /* Draw the menu bar */
        DrawMenuBar();
        error = noErr;
    }
    else
    {
        error = ResError();
        if (error == noErr)
            error = resNotFound;
        if (error != resNotFound && error != memFullErr)
            error = dsSysErr;
    }
    return error;
}
 
 
/******************************************************************************\
* Public: DoMenuChoice
*
* This routine should be self-explanatory.
\******************************************************************************/
 
void DoMenuChoice(
    long menuChoice) /* Return value from MenuSelect or MenuKey */
{
    short menuNum;  /* Menu number of chosen menu */
    short menuItem; /* Item number of chosen menu item */
 
    if (menuChoice != 0)
    {
        /* Get the chosen menu item and menu number */
        menuNum = hiWord( menuChoice );
        menuItem = loWord( menuChoice );
 
        /* Dispatch the appropriate menu-handling routine */
        if (menuNum == mApple)
            DoAppleMenu( menuItem );
        else if (menuNum == mFile)
            DoFileMenu( menuItem );
        else if (menuNum == mEdit)
            DoEditMenu( menuItem );
        else if (menuNum == mDisplay)
            DoDisplayMenu( menuItem );
        HiliteMenu( 0 );
    }
}
 
 
/******************************************************************************\
* Private: DoAppleMenu - Handle an Apple menu item choice
*
* This routine is called whenever itÕs determined that the chosen menu item was
* in the Apple menu.  If the chosen menu item thatÕs passed in the menuItem
* parameter wasnÕt the About item, the name of the menu item is retrieved and
* then OpenDeskAcc is called with this name so that the desk accessory by that
* name is opened.  The Process Manager can launch desk accessories, but
* OpenDeskAcc should still be used if the user chooses any item in the Apple
* menu.
\******************************************************************************/
 
static void DoAppleMenu(
    short menuItem) /* Chosen menu item */
{
    Str255 daName; /* Name of the chosen DA */
    short  refNum; /* Reference number of the DA, ignored */
 
    if (menuItem != iAbout)
    {
        GetMenuItemText( GetMenuHandle( mApple ), menuItem, /*<*/daName );
        refNum = OpenDeskAcc( daName );
    }
}
 
 
/******************************************************************************\
* Private: DoFileMenu - Handle a File menu item choice
*
* This routine is called whenever itÕs determined that the chosen menu item was
* in the File menu.  The item number of the chosen menu item is passed in the
* menuItem parameter.
\******************************************************************************/
 
static void DoFileMenu(
    short menuItem) /* Chosen menu item */
{
    if (menuItem == iOpen)
        (void)DoOpenPictDoc();
    else if (menuItem == iClose)
    {
        if (IsPictDocWindow( FrontWindow() ))
            DoClosePictDoc( FrontWindow() );
    }
    else if (menuItem == iSaveAs)
    {
        if (IsPictDocWindow( FrontWindow() ))
            DoSaveAsPictDoc( FrontWindow() );
    }
    else if (menuItem == iQuit)
        DoQuit();
}
 
 
/******************************************************************************\
* Private: DoEditMenu - Handle a Edit menu item choice
*
* This routine is called whenever itÕs determined that the chosen menu item was
* in the Edit menu.  The item number of the chosen menu item is passed in the
* menuItem parameter.
\******************************************************************************/
 
static void DoEditMenu(
    short menuItem) /* Chosen menu item */
{
    SystemEdit( menuItem - 1 );
}
 
 
/******************************************************************************\
* Private: DoEditMenu - Handle a Edit menu item choice
*
* This routine is called whenever itÕs determined that the chosen menu item was
* in the Edit menu.  The item number of the chosen menu item is passed in the
* menuItem parameter.
\******************************************************************************/
 
static void DoDisplayMenu(
    short menuItem) /* Chosen menu item */
{
    if (menuItem == 1)
        RestoreColorsPalette();
    else if (menuItem == 2)
        RestoreColorsSlam();
}
 
 
/******************************************************************************\
* Public: FixMenus
*
* FixMenus first disables every available menu item.  Then the most basic menu
* items are enabled.  The windowKind field of the front window is then checked.
* If there is a window open, FixMenus calls a routine thatÕs responsible for
* that kind of window to enable any menu items that are relevant to that kind of
* window.
*
* If the front window is a modal dialog, then the basic set of menu items are
* ! enabled, and the entire Apple menu is disabled.
*
* After this is done, the menu bar might have to be redrawn to reflect the new
* conditions.  So, FixMenus go through every menu to determine if the state of
* the entire menu has changed.  The MenuGuide records are used to help determine
* this.  If the state of any many has changed, then the menu bar is redrawn.
\******************************************************************************/
 
void FixMenus()
{
    WindowPtr  currWindow; /* Pointer to the front-most window */
    MenuHandle aMenu;      /* Handle to menu being enabled */
    long       oldEnables; /* True if 1+ items enabled when FixMenus called */
    long       newEnables; /* True if 1+ items enabled after menus fixed */
    Boolean    mustRedraw; /* True if menu bar has to be redrawn */
    short      numItems;   /* Number of items in a menu */
    short      menuIndex;  /* Index into menu guide array */
 
    /* Start by disabling all menus */
    ResetMenuItems();
 
    /* Front-most window determines most menu enabling/disabling */
    currWindow = FrontWindow();
 
    /* Depending on the type of the front window, enable appropriate items */
    if (IsDAWindow( currWindow ))
        FixDAMenus();
    else if (IsPictDocWindow( currWindow ))
        FixPictDocMenus( currWindow );
 
    /* Enable some menus */
    aMenu = GetMenuHandle( mFile );
    EnableItem( aMenu, iOpen );
 
    /* Assume we donÕt have to redraw the menu bar */
    mustRedraw = false;
 
    /* Check through every menu to see if there are any enabled items in it */
    for (menuIndex = 0; menuIndex < kNumMenuGuides; menuIndex++)
    {
        /* Grab the old and new enable flags excluding flag for entire menu */
        oldEnables = gMenuGuides[menuIndex].enables & 0xFFFFFFFE;
        newEnables = (**gMenuGuides[menuIndex].theMenu).enableFlags &
                0xFFFFFFFE;
 
        /* Shift left so that we only see flags for existing items */
        numItems = CountMItems(gMenuGuides [menuIndex].theMenu);
        oldEnables = oldEnables << (31 - numItems);
        newEnables = newEnables << (31 - numItems);
 
        /* Determine if the menu bar must be redrawn */
        if (((oldEnables != 0) && (newEnables == 0)) || numItems == 0)
        {
            /* Had some items enabled, now has no items enabled, redraw */
            DisableItem( gMenuGuides[menuIndex].theMenu, 0 );
            mustRedraw = true;
        }
        else if ((oldEnables == 0) && (newEnables != 0))
        {
            /* Had no items enabled, now has some items enabled, redraw */
            EnableItem( gMenuGuides[menuIndex].theMenu, 0) ;
            mustRedraw = true;
        }
 
        /* Update our copy of the enable flags */
        gMenuGuides[menuIndex].enables = (**gMenuGuides[menuIndex].theMenu).
                enableFlags;
    }
 
    /* If at least one menu has changed state, must redraw the menu bar */
    if (mustRedraw)
        DrawMenuBar();
}
 
 
/******************************************************************************\
* Private: ResetMenuItems - Disable any disableable items and clear marks
*
* Disabling all the menu items is done bruteforcedly.  It could easily be done
* by looping through each menu and disabling every item that comes up (disabling
* the Font menu is done this way), but I thought doing it using the brute-force
* method was clearer.  Then again. . .
\******************************************************************************/
 
static void ResetMenuItems ()
{
    MenuHandle aMenu; /* Handle to each menu weÕre disabling */
 
    /* Disable items in the File menu */
    aMenu = GetMenuHandle( mFile );
    DisableItem( aMenu, iOpen );
    DisableItem( aMenu, iClose );
    DisableItem( aMenu, iSaveAs );
 
    /* Disable items in the Edit menu */
    aMenu = GetMenuHandle( mEdit );
    DisableItem( aMenu, iUndo );
    DisableItem( aMenu, iCut );
    DisableItem( aMenu, iCopy );
    DisableItem( aMenu, iPaste );
    DisableItem( aMenu, iClear );
}
 
 
/******************************************************************************\
* Private: FixDAMenus - Enable any menu items relevant for desk accessories
*
* Desk accessories can use the Edit menu, so the standard Edit menu items are
* enabled.
\******************************************************************************/
 
static void FixDAMenus()
{
    MenuHandle aMenu; /* Handle to each menu weÕre enabling */
 
    /* Disable items in the Process menu */
    aMenu = GetMenuHandle( mEdit );
    EnableItem( aMenu, iUndo );
    EnableItem( aMenu, iCut );
    EnableItem( aMenu, iCopy );
    EnableItem( aMenu, iPaste );
    EnableItem( aMenu, iClear );
}
 
 
/******************************************************************************\
* Public: DisableAllMenus
*
* 
\******************************************************************************/
 
void DisableAllMenus()
{
    short menuIndex; /* Index into menu guide array */
 
    /* Check through every menu to see if there are any enabled items in it */
    for (menuIndex = 0; menuIndex < kNumMenuGuides; menuIndex++)
        DisableItem( gMenuGuides[menuIndex].theMenu, 0 );
}
 
/******************************************************************************\
* Public: RestoreAllMenus
*
* 
\******************************************************************************/
 
void ReenableAllMenus()
{
    short menuIndex; /* Index into menu guide array */
 
    /* Check through every menu to see if there are any enabled items in it */
    for (menuIndex = 0; menuIndex < kNumMenuGuides; menuIndex++)
        if ( gMenuGuides[menuIndex].enables & 1 )
            EnableItem( gMenuGuides[menuIndex].theMenu, 0 );
}