Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
ChooseTkl.c
/* |
File: ChooseTkl.c |
Contains: just one enourmously long routine, DoChooseMsg. This |
routine handles the display of a menu after it's been pulled down or popped up |
and before it's erased. |
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 <Quickdraw.h> |
#include <Types.h> |
#include "ChooseTkl.h" |
#include "Concordia.h" |
#include "DrawTkl.h" |
#include "SizeTkl.h" |
/******************************************************************************\ |
* Constants & Macros |
\******************************************************************************/ |
#define noScroll ((short) 0) //Must not scroll |
#define upScroll ((short) 1) //Must scroll up |
#define downScroll ((short) 2) //Must scroll down |
/* System Globals */ |
#define sgHuhRect *((Rect *) 0x09FA) //MenuSelect seems to need this |
#define sgMenuDisable *((long *) 0x0B54) //MenuDisable system global (IM-V) |
/******************************************************************************\ |
* Function Prototypes |
\******************************************************************************/ |
void RedrawItem (MenuHandle, short, Rect *, short); |
#pragma segment Main |
/******************************************************************************\ |
* DoChooseMsg - Choose a menu item |
* |
* DoChooseMsg is an unbelievably long routine which processes the mChooseMsg |
* menu message for the menu specified by TheMenu. DoChooseMsg will determine |
* which menu items must be hilighted and unhighlighted, and will highlight and |
* unhighlight those items appropriately. If TheMenu has scroll arrows and the |
* location of the mouse (given in HitPt), is located in these scroll arrows, |
* DoChooseMsg will scroll the menu. DoChooseMsg will then draw any newly- |
* exposed menu items. When the end of TheMenu is reached while scrolling, the |
* scroll arrow at that end will be erased and the menu item behind it will be |
* drawn. If a scrolling menu was at one end when it's scrolled, DoChooseMsg |
* will draw a new scroll arrow at that end. The item number of the chosen item |
* is returned, or 0 if no item is chosen. |
* |
* Coding Notes |
* #A# - This loop calculates the item number and item rectangle of the item |
* being chosen, the item rectangle of the item that was chosen the last |
* time DoChooseMsg was called, and the sum of the heights of all of the |
* items in TheMenu. If no item is chosen or if the item is disabled, the |
* item number will be calculated as 0. |
* #B# - Read as: If the vertical coordinate in the TopMenuItem system global |
* doesn't coincide with the top of the menu rectangle, this menu has a top |
* scroll arrow. |
* #C# - Read as: If the vertical coordinate in the AtMenuBottom system global |
* doesn't coincide with the bottom of the menu rectangle, this menu has a |
* bottom scroll arrow. |
* #D# - If the menu has a top or bottom scroll arrow, set the clipping region so |
* that the scroll arrows won't be stomped on when items are hilighted and |
* unhilighted. |
* #E# - If we're scrolling and there's no scroll bar at the other end, it must |
* be drawn and the scroll rectangle adjusted for that. |
* #F# - If we've scrolled to an end of a menu, the scroll bar at that end must |
* be drawn over. |
* #G# - This loop draws the menu items that intersect with UpdateRect. |
* #H# - If we saved the clip region, we'd better restore it. |
* #I# - Side Effect: System globals TopMenuItem and AtMenuBottom are modified |
* here. Icky, Poo! |
* #J# - Set high word of MenuDisable system global to menu ID, and low word to |
* the item number. Yes folks, even if the item is disabled. |
* #K# - If the item is disabled, its item number will be the 2's complement of |
* whatever it should be. |
* #L# - Why do I have to do this? BEATS THE HELL OUT OF ME! |
* #M# - Even if the new item isn't hierarchical, it might still get stomped on |
* when a hierarchical menu is erased. |
* #N# - 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. |
* #O# - If a hierarchical menu is up right at the top or bottom of the screen |
* and the user moves the mouse into the scroll area of the main menu, we |
* get a mChooseMsg with the hit point in the scroll area before the child |
* menu is brought down. That would cause DoChooseMsg to scroll the main |
* menu while the child menu is still up. Because the Menu Bar Definition |
* Procedure saves the bits behind the child menu and because the child |
* menu slightly overlaps the main menu, scrolling while the hierarchical |
* menu is up causes a little bit of garbage to appear on the screen. To |
* prevent this, the DoDrawMsg routine clears a field in the MBSaveLoc |
* record to 0. DoChooseMsg checks this field. If itÕs set to 1, then |
* scrolling is done as usual. If itÕs clear, then scrolling is supressed |
* to give the menu bar definition procedure some time to bring down the |
* hierarchical menu first. |
\******************************************************************************/ |
short |
DoChooseMsg (MenuHandle TheMenu,Rect* MenuRect,Point HitPt,short WhichItem) |
{ |
Str255 *ItemString; //-> Menu item's string |
ItemInfoPtr ItemInfo; //-> Item info record |
short ItemHeight; //Height of menu item in pixels |
Rect ItemRect; //Item's rectangle in screen coords |
short TotHeight; //Total height of menu in pixels |
Rect NewRect; //Rectangle of newly-chosen item in global coords |
short NewItem; //Item number of newly-chosen item |
Rect OldRect; //Rectangle of old chosen item in global coords |
short OldItem; //Item number of old chosen item |
short CurrItem; //Item number of item being processed |
short NewIsHier; //True if NewItem specifies hierarchical item |
short OldIsHier; //True if OldItem specifies hierarchical item |
short HasTopScrl; //True if menu has top scroll arrow |
short HasBotScrl; //True if menu has bottom scroll arrow |
short MustScrl; //Which direction must menu scroll, if any? |
short ScrollAmt; //Scrolling distance in pixels |
RgnHandle SavedClip; //=> Clip region before DoChooseMsg is called |
Rect MenuClip; //Menu's clip rectangle, if scroll icons present |
RgnHandle UpdateRgn; //=> Menu update region when scrolled |
Rect UpdateRect; //Menu update rectangle when scrolled |
long EnableFlags; //Menu's enable flags |
Boolean dontScroll; /* True if scrolling shouldnÕt take place */ |
EnableFlags = (unsigned long) (**TheMenu).enableFlags; |
if (EnableFlags & 1) |
{ |
EnableFlags >>= (unsigned long) 1; //#N# |
EnableFlags |= 0x80000000; |
} |
else |
EnableFlags = (unsigned long) 0; |
SavedClip = (RgnHandle) null; |
NewRect = OldRect = *MenuRect; |
NewRect.top = NewRect.bottom = OldRect.top = OldRect.bottom = sgTopMenuItem; |
NewItem = 0; |
OldItem = 0; |
CurrItem = 1; |
TotHeight = 0; |
NewIsHier = OldIsHier = false; |
dontScroll = *((short *) ((*sgMBSaveLoc) + 16)) == 1; //#O# |
if (dontScroll) |
*((short *) ((*sgMBSaveLoc) + 16)) = 0; |
HLock ((Handle) TheMenu); |
ItemString = (Str255 *) (**TheMenu).menuData; |
ItemString = (Str255 *) ((Byte *) ItemString + strSize (*ItemString)); |
while ((*ItemString) [0] != '\0') //#A# |
{ |
ItemInfo = (ItemInfoPtr) ((Byte *) *ItemString + strSize (*ItemString)); |
ItemHeight = CalcItemHeight (*ItemString, ItemInfo); |
if (NewItem == 0) |
{ |
NewRect.bottom = NewRect.top + ItemHeight; |
if (HitPt.v > NewRect.top && HitPt.v <= NewRect.bottom) |
{ |
if (EnableFlags & 1) //#K# |
NewItem = CurrItem; |
else |
NewItem = -CurrItem; |
if (ItemInfo->kbdEquiv == (char) hMenuCmd) |
NewIsHier = true; |
} |
else |
NewRect.top += ItemHeight; |
} |
if (OldItem == 0) |
if (WhichItem == CurrItem) |
{ |
OldRect.bottom = OldRect.top + ItemHeight; |
OldItem = WhichItem; |
if (ItemInfo->kbdEquiv == (char) hMenuCmd) |
OldIsHier = true; |
} |
else |
OldRect.top += ItemHeight; |
TotHeight += ItemHeight; |
CurrItem += 1; |
ItemString = (Str255 *) (ItemInfo + 1); |
EnableFlags >>= 1; |
} |
HUnlock ((Handle) TheMenu); |
if (HitPt.h < MenuRect->left || HitPt.h > MenuRect->right) |
NewItem = 0; |
MustScrl = noScroll; |
HasTopScrl = HasBotScrl = false; |
if (sgTopMenuItem != MenuRect->top) //#B# |
{ |
HasTopScrl = true; |
if (HitPt.v < MenuRect->top + scrlIconHeight) |
{ |
NewItem = 0; |
MustScrl = downScroll; |
} |
} |
if (sgAtMenuBottom != MenuRect->bottom) //#C# |
{ |
HasBotScrl = true; |
if (HitPt.v > MenuRect->bottom - scrlIconHeight) |
{ |
NewItem = 0; |
MustScrl = upScroll; |
} |
} |
if (HasBotScrl || HasTopScrl) //#D# |
{ |
MenuClip = *MenuRect; |
if (HasBotScrl) |
MenuClip.bottom -= scrlIconHeight; |
if (HasTopScrl) |
MenuClip.top += scrlIconHeight; |
SavedClip = NewRgn (); |
GetClip (SavedClip); |
ClipRect (&MenuClip); |
} |
if (NewItem == 0) |
sgMenuDisable = 0L; |
else |
{ |
sgMenuDisable = (**TheMenu).menuID << (sizeof(long) / 2) | (NewItem > 0 ? |
NewItem : -NewItem); //#J# #K# |
if (NewItem < 0) |
NewItem = 0; |
} |
if (NewItem != 0 && NewIsHier) //#L# |
{ |
sgHuhRect = NewRect; |
*((Rect *) ((*sgMBSaveLoc) + 6)) = NewRect; |
} |
if (NewItem != OldItem) |
{ |
if (OldItem != 0) |
if (OldIsHier) |
RedrawItem (TheMenu, OldItem, &OldRect, false); |
else |
InvertRect (&OldRect); |
if (NewItem != 0) |
if (OldIsHier) //#M# |
RedrawItem (TheMenu, NewItem, &NewRect, true); |
else |
InvertRect (&NewRect); |
} |
if (!dontScroll && MustScrl && HitPt.h >= MenuRect->left && HitPt.h <= |
MenuRect->right) |
{ |
if (MustScrl == upScroll) |
{ |
if (! HasTopScrl) //#E# |
{ |
DrawScroll (MenuRect, topScroll); |
MenuClip.top += scrlIconHeight; |
} |
ScrollAmt = MenuClip.bottom - HitPt.v; |
if (ScrollAmt < MenuRect->bottom - sgAtMenuBottom) |
ScrollAmt = MenuRect->bottom - sgAtMenuBottom; |
UpdateRgn = NewRgn (); |
ScrollRect (&MenuClip, 0, ScrollAmt, UpdateRgn); |
UpdateRect = (**UpdateRgn).rgnBBox; |
DisposeRgn (UpdateRgn); |
sgTopMenuItem += ScrollAmt; //#I# |
sgAtMenuBottom += ScrollAmt; |
if (sgAtMenuBottom == MenuRect->bottom) //#F# |
UpdateRect.bottom += scrlIconHeight; |
if (SavedClip == (RgnHandle) null) |
{ |
SavedClip = NewRgn (); |
GetClip (SavedClip); |
} |
ClipRect (&UpdateRect); |
EraseRect (&UpdateRect); |
} |
else |
{ |
if (! HasBotScrl) //#E# |
{ |
DrawScroll (MenuRect, botScroll); |
MenuClip.bottom -= scrlIconHeight; |
} |
ScrollAmt = MenuClip.top - HitPt.v; |
if (ScrollAmt > MenuRect->top - sgTopMenuItem) |
ScrollAmt = MenuRect->top - sgTopMenuItem; |
UpdateRgn = NewRgn (); |
ScrollRect (&MenuClip, 0, ScrollAmt, UpdateRgn); |
UpdateRect = (**UpdateRgn).rgnBBox; |
DisposeRgn (UpdateRgn); |
sgTopMenuItem += ScrollAmt; |
sgAtMenuBottom += ScrollAmt; |
if (sgTopMenuItem == MenuRect->top) //#F# |
UpdateRect.top -= scrlIconHeight; |
if (SavedClip == (RgnHandle) null) |
{ |
SavedClip = NewRgn (); |
GetClip (SavedClip); |
} |
ClipRect (&UpdateRect); |
EraseRect (&UpdateRect); |
} |
SetRect (&ItemRect, MenuRect->left, sgTopMenuItem, MenuRect->right, |
sgTopMenuItem); |
EnableFlags = (unsigned long) (**TheMenu).enableFlags; |
if (EnableFlags & 1) |
{ |
EnableFlags >>= (unsigned long) 1; |
EnableFlags |= 0x80000000; |
} |
else |
EnableFlags = (unsigned long) 0; |
HLock ((Handle) TheMenu); |
ItemString = (Str255 *) (**TheMenu).menuData; |
ItemString = (Str255 *) ((Byte *) ItemString + strSize (*ItemString)); |
while ((*ItemString) [0] != '\0') //#G# |
{ |
ItemInfo = (ItemInfoPtr) ((Byte *) *ItemString + strSize |
(*ItemString)); |
ItemRect.bottom = ItemRect.top + CalcItemHeight (*ItemString, |
ItemInfo); |
if (ItemRect.top < UpdateRect.bottom && ItemRect.bottom > |
UpdateRect.top) |
DrawItem (*ItemString, ItemInfo, &ItemRect, EnableFlags & 1); |
EnableFlags >>= 1; |
ItemRect.top = ItemRect.bottom; |
ItemString = (Str255 *) (ItemInfo + 1); |
} |
HUnlock ((Handle) TheMenu); |
} |
if (SavedClip != (RgnHandle) null) //#H# |
{ |
SetClip (SavedClip); |
DisposeRgn (SavedClip); |
} |
return NewItem; |
} |
#pragma segment Main |
/******************************************************************************\ |
* RedrawItem - Redraw a menu item for hierarchical menus |
* |
* RedrawItem redraws a menu item that's been clobbered by a hierarchical menu. |
* The affected menu is specified by TheMenu. The item number of the menu item |
* to redraw is given in ItemNum, while the item's rectangle is given in |
* ItemRect. If the item is going to be selected, Select must be true so that |
* the item will be redrawn in inverse. Otherwise Select must be false so that |
* the item will be redrawn normally. |
\******************************************************************************/ |
static void |
RedrawItem (MenuHandle TheMenu,short ItemNum,Rect* ItemRect,short Select) |
{ |
Str255 *ItemString; //-> Menu item's string |
ItemInfoPtr ItemInfo; //-> Item info record |
short CurrItem; //Item number of item being checked |
TextStateRec TextState; //Current text modes, etc. |
CurrItem = 0; |
HLock ((Handle) TheMenu); |
ItemString = (Str255 *) (**TheMenu).menuData; |
ItemString = (Str255 *) ((Byte *) ItemString + strSize (*ItemString)); |
while (ItemNum != CurrItem && (*ItemString) [0] != '\0') |
{ |
ItemInfo = (ItemInfoPtr) ((Byte *) *ItemString + strSize |
(*ItemString)); |
CurrItem += 1; |
if (ItemNum == CurrItem) |
{ |
GetTextState (&TextState); |
EraseRect (ItemRect); |
DrawItem (*ItemString, ItemInfo, ItemRect, true); |
if (Select) |
InvertRect (ItemRect); |
SetTextState (&TextState); |
} |
else |
ItemString = (Str255 *) (ItemInfo + 1); |
} |
HUnlock ((Handle) TheMenu); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-08-28