DrawTkl.c

/*
    File:       DrawTkl.c
 
    Contains:   the code to process the mDrawMsg message from the Menu
                Manager.  This source file contains the routines to do ALL drawing, including
                the scroll icons.
 
    Written by:     
 
    Copyright:  Copyright © 1991-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):
                8/10/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
 
/******************************************************************************\
* Header Files
\******************************************************************************/
 
#include <Memory.h>
#include <Menus.h>
#include <Resources.h>
#include <ToolUtils.h>
#include <Icons.h>
#include <Types.h>
#include "Concordia.h"
#include "DrawTkl.h"
#include "SizeTkl.h"
 
 
/******************************************************************************\
* Constants & Macros
\******************************************************************************/
 
#define scrlSICNID -12288 //Resource ID of scroll arrows SICN
 
 
/******************************************************************************\
* Type Declarations
\******************************************************************************/
 
/* Small icon data */
typedef Byte SICNData [32];
 
 
/******************************************************************************\
* Function Prototypes
\******************************************************************************/
 
void DimRect (Rect *);
void DrawSICN (SICNData **, short, short, short);
void DrawICON (Handle, Rect *);
 
 
#pragma segment Main
/******************************************************************************\
* DoDrawMsg - Draw a menu
*
* DoDrawMsg draws the menu specified by TheMenu.  The rectangle in global coord-
* inates that the menu occupies is specified in MenuRect.  If the system global
* TopMenuItem is not equal to the top coordinate of MenuRect, a top scroll bar
* is drawn.  If the menu extends below the bottom of MenuRect, a bottom scroll
* bar is drawn.  The Menu Manager (I guess) sets TopMenuItem properly for pull-
* down menus, while the popup menu tackler (DoPopupMsg) sets TopMenuItem
* properly for popup menus.  DoDrawMsg sets the system global AtMenuBottom to
* the bottom of the entire menu, regardless of whether it fits on the screen or
* not.
*
* Coding Notes
* #A# - The sole purpose of this loop is to find the total height of the menu in 
*       pixels ignoring the size of the screen.  Seems like kind of a waste,
*       doesn't it?
* #B# - The clip rectangle, MenuClip is only set if the menu has scroll icons.
*       This is determined here.
* #C# - This loop draws each of the menu items.
* #D# - Side-effect: system global AtMenuBottom set to coordinate of bottom of
*       Menu regardless of whether the menu fits or not.
* #E# - We donÕt need the menuEnabled bit any more so get rid of it by shifting
*       it out.  Then we have to set the high bit so that all items beyond the
*       31st will be enabled.  With arithmetic right shifting, this high bit
*       will be preserved.
\******************************************************************************/
 
void
DoDrawMsg (TheMenu, MenuRect)
    MenuHandle TheMenu;   //=> Menu to draw >>
    Rect       *MenuRect; //-> Menu's rectangle in global coords >>
    {
    Str255      *ItemString; //-> Menu item's string
    ItemInfoPtr ItemInfo;    //-> Item info
    Rect        ItemRect;    //Item's rectangle
    Rect        TotMenuRect; //Menu's rectangle w/o clipping to screen
    RgnHandle   SaveClip;    //=> Saved clip region
    Rect        MenuClip;    //Clip region of menu if menu scrolls
    long        EnableFlags; //Menu's enable flags
 
    SaveClip = (RgnHandle) null;
    if ((**TheMenu).enableFlags & 1)
        {
        EnableFlags = (unsigned long) (**TheMenu).enableFlags >> 1; //#E#
        EnableFlags |= 0x80000000;
        }
    else
        EnableFlags = 0;
    SetRect (&TotMenuRect, MenuRect->left, sgTopMenuItem, MenuRect->right,
            sgTopMenuItem);
    HLock ((Handle) TheMenu);
    ItemString = (Str255 *) (**TheMenu).menuData;
    ItemString = (Str255 *) ((Byte *) ItemString + strSize (*ItemString));
    while ((*ItemString) [0] != (char) 0) //#A#
        {
        ItemInfo = (ItemInfoPtr) ((Byte *) *ItemString + strSize (*ItemString));
        TotMenuRect.bottom += CalcItemHeight (*ItemString, ItemInfo);
        ItemString = (Str255 *) (ItemInfo + 1);
        }
    HUnlock ((Handle) TheMenu);
    MenuClip = *MenuRect;
    if (TotMenuRect.top != MenuRect->top || TotMenuRect.bottom != MenuRect->
            bottom) //#B#
        {
        if (TotMenuRect.top != MenuRect->top)
            {
            DrawScroll (MenuRect, topScroll);
            MenuClip.top += scrlIconHeight;
            }
        if (TotMenuRect.bottom != MenuRect->bottom)
            {
            DrawScroll (MenuRect, botScroll);
            MenuClip.bottom -= scrlIconHeight;
            }
        SaveClip = NewRgn ();
        GetClip (SaveClip);
        ClipRect (&MenuClip);
        }
    HLock ((Handle) TheMenu);
    ItemString = (Str255 *) (**TheMenu).menuData;
    ItemString = (Str255 *) ((Byte *) ItemString + strSize (*ItemString));
    ItemRect = TotMenuRect;
    ItemRect.bottom = ItemRect.top;
    while ((*ItemString) [0] != (char) 0) //#C#
        {
        ItemInfo = (ItemInfoPtr) ((Byte *) *ItemString + strSize (*ItemString));
        ItemRect.bottom += CalcItemHeight (*ItemString, ItemInfo);
        if (ItemRect.bottom > MenuClip.top && ItemRect.top < MenuClip.bottom)
            DrawItem (*ItemString, ItemInfo, &ItemRect, (short) (EnableFlags &
                    1));
        ItemString = (Str255 *) (ItemInfo + 1);
        ItemRect.top = ItemRect.bottom;
        EnableFlags >>= 1;
        }
    HUnlock ((Handle) TheMenu);
    if (SaveClip != (RgnHandle) null)
        {
        SetClip (SaveClip);
        DisposeRgn (SaveClip);
        }
    sgAtMenuBottom = TotMenuRect.bottom; //#D#
    *((short *) ((*sgMBSaveLoc) + 16)) = 1;
    }
 
 
#pragma segment Main
/******************************************************************************\
* DrawItem - Draw a menu item
*
* DrawItem draws a menu item whose text is specified by ItemString and whose
* item information is specified by ItemData.  The rectangle that the item
* occupies on the screen is specified by ItemRect.  If the item is enabled,
* Enabled must be true, otherwise it must be false.  DrawItem also draws mark
* characters (like check marks); big, scaled, and disabled icons, dims items if
* they're not enabled, and draws gray lines if the item string begins with a
* hyphen.  Any command key characters that aren't printable are ignored.  If the
* specified item icon can't be found, it is not drawn (of course), but space is
* left for it.
*
* Coding Notes:
* #A# - This calculation figures out the proper vertical coordinate to begin
*       drawing the item string so that it's centered vertically in ItemRect.
* #B# - Draw the mark character unless this is a hierarchical menu item.
* #C# - Draw the command-key equivalent
* #E# - Draw the item icon
* #F# - With these command-key equivalents, icons are half their size.
* #G# - If couldn't load icon, ignore drawing and continue
* #H# - With this command-key equivalent, icon is dimmed.
* #I# - These values will be used to set the location of the item string.
\******************************************************************************/
 
void
DrawItem (Str255 ItemString,ItemInfoPtr ItemData,Rect* ItemRect,short Enabled)
{
    short        HorzPos;   //Horizontal position to draw item
    short        VertPos;   //Vertical position to draw item
    short        MarkWidth; //Width of item mark
    Handle       ItemIcon;  //=> Item's icon
    Rect         IconRect;  //Rectangle of icon
    short        IconSize;  //Size of icon in pixels
    FontInfo     CurrFont;  //Current font's characteristics
    PenState     CurrPen;   //Current characteristics of graphics pen
    short        CurrFile;  //Refnum of current resource file
    SICNData     **SICNRes; //=> SICN resource
    TextStateRec TextState; //Current characteristics of text drawing
 
    GetPenState (&CurrPen);
    if (ItemString [1] == '-')
        {
        PenSize (1, 1);
        PenMode (patOr);
        PenPat (&qd.gray);
        MoveTo (ItemRect->left, ItemRect->top + ItemRect->bottom >> 1);
        Line (ItemRect->right - ItemRect->left, 0);
        }
    else
        {
        PenMode (patOr);
        GetTextState (&TextState);
        TextFace (normal);
        TextMode (srcOr);
        GetFontInfo (&CurrFont);
        MarkWidth = CurrFont.widMax;
        if (ItemData->markChar != '\0' || ItemData->kbdEquiv != '\0')
            {
            VertPos = ((short) ((ItemRect->bottom - ItemRect->top) - (CurrFont.
                    ascent + CurrFont.descent + CurrFont.leading) >> 1) +
                    CurrFont.ascent + ItemRect->top); //#A#
            if (ItemData->markChar != (char) 0 && ItemData->kbdEquiv !=
                    hMenuCmd) //#B#
                {
                HorzPos = ItemRect->left + itemHorzMarg;
                MoveTo (HorzPos, VertPos);
                DrawChar (ItemData->markChar);
                }
            if (ItemData->kbdEquiv > '!') //#C#
                {
                HorzPos = ItemRect->right - itemHorzMarg -
                        CurrFont.widMax - CharWidth (cmmdCharCode);
                MoveTo (HorzPos, VertPos);
                DrawChar (cmmdCharCode);
                DrawChar (ItemData->kbdEquiv);
                }
            else if (ItemData->kbdEquiv == hMenuCmd)
                {
                HorzPos = ItemRect->right - sicnSize;
                VertPos = (short) ((ItemRect->bottom - ItemRect->top - (short)
                        sicnSize) >> 1) + ItemRect->top;
                CurrFile = CurResFile ();
                UseResFile (0);
                if ((SICNRes = (SICNData **) Get1Resource ('SICN', scrlSICNID))
                        != (SICNData **) null)
                    DrawSICN (SICNRes, heirArrow, HorzPos, VertPos);
                UseResFile (CurrFile);
                }
            }
        if (ItemData->iconNum != (Byte) 0) //#E#
            {
            IconSize = iconSize;
            if (ItemData->kbdEquiv == (char) 0x1D || ItemData->kbdEquiv ==
                    (char) 0x1F) //#F#
                IconSize >>= 1;
            if ((ItemIcon = GetIcon (ItemData->iconNum + 256)) != (Handle) null) //#G#
                {
                IconRect.top = (ItemRect->bottom - ItemRect->top -
                        IconSize >> 1) + ItemRect->top;
                IconRect.left = ItemRect->left + itemHorzMarg + MarkWidth +
                        markItemGap;
                IconRect.bottom = IconRect.top + IconSize;
                IconRect.right = IconRect.left + IconSize;
                DrawICON (ItemIcon, &IconRect);
                if (ItemData->kbdEquiv == (char) 0x1F) //#H#
                    DimRect (&IconRect);
                }
            IconSize += iconItemGap; //#I#
            }
        else
            IconSize = 0; //#I#
        TextFace (ItemData->charStyle);
        GetFontInfo (&CurrFont);
        VertPos = (short) ((ItemRect->bottom - ItemRect->top) - (CurrFont.
                ascent + CurrFont.descent + CurrFont.leading) >> 1) + CurrFont.
                ascent + ItemRect->top;
        HorzPos = ItemRect->left + itemHorzMarg + MarkWidth + markItemGap +
                IconSize;
        MoveTo (HorzPos, VertPos);
        DrawString (ItemString);
        SetTextState (&TextState);
        if (! Enabled)
            DimRect (ItemRect);
        }
    SetPenState (&CurrPen);
    }
 
 
#pragma segment Main
/******************************************************************************\
* DimRect - Dim a rectangle
*
* DimRect dims the rectangle specified by ItemRect by bit-clearing it with a
* medium gray pattern.  Normally, we could do this just by drawing over the item
* with the standard gray pattern, but there's a problem: what if the menu has
* scrolled an odd number of pixels and we have to draw part of a dimmed menu
* item?  We'll end up with the gray pattern that's out of phase with the part of
* the menu item that was already drawn.  To solve this, DimRect creates its own
* pattern "on the fly" in GrayPat.  Two gray patterns can be created, each one
* being out of out of phase with the other by one pixel.  The particular pattern
* is chosen by checking to see whether the system global, TopMenuItem, is odd or
* even.  The current pen state is unchanged.
\******************************************************************************/
 
static void
DimRect (TheRect)
    Rect *TheRect; //-> Rectangle to dim >>
    {
    PenState CurrPen;     //Current characteristics of graphics pen
    short    PatElem;     //Pattern element
    short    GrayPat [4]; //Gray pattern (not 8 bytes 'cause it's easier here
    short    PatInd;      //Index into pattern array
 
    GetPenState (&CurrPen);
    PenMode (patBic);
    if ((sgTopMenuItem & 0x0001) == 0)
        PatElem = (short) 0xAA55;
    else
        PatElem = (short) 0x55AA;
    for (PatInd = 0; PatInd < 4; PatInd++)
        GrayPat [PatInd] = PatElem;
    PenPat ((Pattern*) GrayPat);
    PaintRect (TheRect);
    SetPenState (&CurrPen);
    }
 
 
#pragma segment Main
/******************************************************************************\
* DrawScroll - Draw a scroll arrow
*
* DrawScroll draws a top scroll arrow if Which is topScroll, and it draws a
* bottom scroll arrow if which is botScroll.  The menu's rectangle must be
* specified in MenuRect.
\******************************************************************************/
 
void
DrawScroll (Rect* MenuRect,short Which)
{
    SICNData **SICNRes; //=> SICN resource
    short    HorzPos;   //Horizontal position of icon
    short    VertPos;   //Vertical position of icon
    Rect     ScrlRect;  //Rectangle of scroll icon area
    PenState CurrPen;   //Current pen state
    short    CurrFile;  //Current resource file's refnum
 
    HorzPos = (short) ((MenuRect->right - MenuRect->left - sicnSize) >> 1) +
            MenuRect->left;
    ScrlRect = *MenuRect;
    GetPenState (&CurrPen);
    PenPat (&qd.black);
    PenMode (patCopy);
    PenSize (1, 1);
    if (Which == topScroll)
        {
        ScrlRect.bottom = ScrlRect.top + scrlIconHeight;
        EraseRect (&ScrlRect);
        MoveTo (MenuRect->left, MenuRect->top + scrlIconHeight - 1);
        Line (MenuRect->right - MenuRect->left, 0);
        VertPos = MenuRect->top + 1;
        }
    else
        {
        ScrlRect.top = ScrlRect.bottom - scrlIconHeight;
        EraseRect (&ScrlRect);
        MoveTo (MenuRect->left, MenuRect->bottom - scrlIconHeight);
        Line (MenuRect->right - MenuRect->left, 0);
        VertPos = MenuRect->bottom - 1 - sicnSize;
        }
    PenMode (patOr);
    CurrFile = CurResFile ();
    UseResFile (0);
    if ((SICNRes = (SICNData **) Get1Resource ('SICN', scrlSICNID)) !=
            (SICNData **) null)
        DrawSICN (SICNRes, Which, HorzPos, VertPos);
    UseResFile (CurrFile);
    SetPenState (&CurrPen);
    }
 
 
#pragma segment Main
/******************************************************************************\
* DrawSICN - Draw a small icon
*
* DrawSICN draws the small icon specified by TheIcon into the current port.
* Each SICNData can hold any number of SICNs, so Index specifies which SICN to
* draw, 0 specifying the first.  HorzPos and VertPos specify the horizontal and
* vertical position to draw the top-left corner of the icon in local
* coordinates.  If TheIcon doesn't specify a SICN or if Index is out of range,
* garbage will be drawn.  The transfer mode will be the equivalent of the
* current pen mode of the current port.  For example, if the current pen mode is
* patXor, the SICN will be drawn in srcXor mode.
\******************************************************************************/
 
static void
DrawSICN (SICNData** TheIcon,short Index,short HorzPos,short VertPos)
{
    BitMap  IconBits;  //Bitmap for icon
    GrafPtr CurrPort;  //-> Current GrafPort
    Rect    SICNRect;  //Rectangle to draw icon into
    short   TransMode; //Transfer mode to use
 
    HLock ((Handle) TheIcon);
    SetRect (&IconBits.bounds, 0, 0, sicnSize, sicnSize);
    IconBits.rowBytes = 2;
    IconBits.baseAddr = (Ptr) (*TheIcon + Index);
    SetRect (&SICNRect, HorzPos, VertPos, HorzPos + sicnSize, VertPos +
            sicnSize);
    GetPort (&CurrPort);
    if (CurrPort->pnMode == patCopy || CurrPort->pnMode == notPatCopy)
        TransMode = srcCopy;
    else if (CurrPort->pnMode == patOr || CurrPort->pnMode == notPatOr)
        TransMode = srcOr;
    else if (CurrPort->pnMode == patXor || CurrPort->pnMode == notPatXor)
        TransMode = srcXor;
    else if (CurrPort->pnMode == patBic || CurrPort->pnMode == notPatBic)
        TransMode = srcBic;
    CopyBits (&IconBits, &CurrPort->portBits, &IconBits.bounds, &SICNRect,
            TransMode, (RgnHandle) null);
    HUnlock ((Handle) TheIcon);
    }
 
 
#pragma segment Main
/******************************************************************************\
* DrawICON - Draw an icon
*
* DrawICON draws the icon specified by TheIcon into the current port.  IconRect
* specifies the size and location to draw the icon in local coordinates.  If
* TheIcon doesn't specify an ICON, garbage will be drawn.  The transfer mode
* will be the equivalent of the current pen mode of the current port.  For
* example, if the current pen mode is patXor, the ICON will be drawn in srcXor
* mode.
\******************************************************************************/
 
static void
DrawICON (TheIcon, IconRect)
    Handle TheIcon; //=> Icon data >>
    Rect   *IconRect; //-> Rectangle to draw icon into >>
    {
    BitMap  IconBits;  //Bitmap for icon
    GrafPtr CurrPort;  //-> Current GrafPort
    short   TransMode; //Transfer mode to use
 
    HLock (TheIcon);
    SetRect (&IconBits.bounds, 0, 0, iconSize, iconSize);
    IconBits.rowBytes = 4;
    IconBits.baseAddr = *TheIcon;
    GetPort (&CurrPort);
    if (CurrPort->pnMode == patCopy || CurrPort->pnMode == notPatCopy)
        TransMode = srcCopy;
    else if (CurrPort->pnMode == patOr || CurrPort->pnMode == notPatOr)
        TransMode = srcOr;
    else if (CurrPort->pnMode == patXor || CurrPort->pnMode == notPatXor)
        TransMode = srcXor;
    else if (CurrPort->pnMode == patBic || CurrPort->pnMode == notPatBic)
        TransMode = srcBic;
    CopyBits (&IconBits, &CurrPort->portBits, &IconBits.bounds, IconRect,
            TransMode, (RgnHandle) null);
    HUnlock (TheIcon);
    }