Window Menu Bars
Volume Number: | | 7
|
Issue Number: | | 10
|
Column Tag: | | Color Workshop
|
Related Info: Menu Manager Memory Manager Color Quickdraw
Window Menu Bars Revisited
By John A. Love, III, Springfield, VA, MacTutor Contributing Editor
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
[ John is a member of the Washington Apple Pi Users Group from the greater Washington D.C. metropolitan area and can be reached on AppleLink {D3471} and on GEnie {J.LOVE7} ... with very, very special thanks to Ben Cranston from the Programmers Special Interest Group of Washington Apple Pi for his brilliance and his extraordinary patience ... ]
This article builds on James Matthews work presented in MacTutor, Nov 1988. My significant additions include:
Color menus.
Hierarchical menus.
Multiple windows with the capability to place varying Window MenuBars (WMB) in different windows.
Zooming and growable windows that allow the user to scroll the Window MenuBars horizontally by clicking on my horizontal Menu Scroll Activators (MSA).
By the way, youll notice that the above is a THINK Pascal Project, using version 3.Ø.2 and the appropriate Interface files and Libraries provided on the System 7 CD-ROM. Yes...my application is not only System 7 compatible, but System 7 friendly...but more on this later.
There are two fundamental sets of PROCEDUREs and FUNCTIONs that are mandatory:
Some of Skippy Whites Off-Screen Map routines that were originally presented by MacDTS on CD-ROM. The ones I use are modified to address off-screen BitMaps/PixMaps rather than off-screen Devices. I presented my modifications in MacTutor, Nov 1990, Spiffy Color Demo, Part I. I am re-running them below because:
a) They really ought to serve as a programming standard and, therefore, are worth repeating.
b) I now use the FUNCTION CreateOffScreen to return a OffScreenRecHdl since I draw the windows WMB in the associated off-screen Port & subsequently blit the Ports bitMap/pixMap back to the screen using CopyBits. Since each window can have a different WMB, each window can have a different OffScreenRecHdl for blitting back upon the occurrence of Update and Activate Events. I originally implemented this logic so I could have these OffScreenRec handles hang around even when said window had been de-activated or dragged partly beyond the physical edge of the screen or dragged to a secondary monitor. However, I then decided to dispose of these handles immediately following the blitting back to screen ... otherwise, I would then have to periodically test, for example, if any of this dragging had occured and, if so, re-create all these off-screen Maps anyway.
James Matthews routines such as wInsertMenu and wDrawMenuBar. I will attempt to lead you readers through the logic with particular emphasis on color and hierarchical Menus.
One note of caution ... I will not be presenting alot of the conventional parts of the code, for example the GetNextEvent/WaitNextEvent loop, the cursor-changing PROC etc. The entire THINK Pascal Project, Pascal source code and SARez source( w/included resources) consumes a tad over 600K which Kirk Chase will gladly sell you for a mere pittance.
Before the drum roll ends, I will also be talking about snipits of code that are just plain spiffy. For example, how do you retrieve a windows window type ... before you generate a wrong answer such as a simple call to GetWVariant, watch out ... the variation codes of several rDocProc types duplicate those of several standard types. So, stay tuned ...
First in line is a complete listing of all external routines USEd by each Pascal UNIT ... the big picture, so-to-speak. I will amplify only on a few of them, those that are crucial to my off-screen BitMap/PixMap gymnastics as well as to Window MenuBars.
{1}
UNIT wBMMiscSubs;
INTERFACE
USES
Types, Quickdraw, Menus, TextEdit, Traps, Sound, wBMGlobals;
PROCEDURE InitManagers;
FUNCTION TestForColor (VAR pixelDepth: INTEGER): BOOLEAN;
PROCEDURE LocalGlobal (VAR r: Rect);
PROCEDURE GlobalLocal (VAR r: Rect);
FUNCTION TrapIsAvailable (theTrap: INTEGER): BOOLEAN;
FUNCTION WNEisImplemented: BOOLEAN;
PROCEDURE PlaySound (mySound: Str255);
FUNCTION GetStripAddressMask: LONGINT;
FUNCTION QuickStrip (myPtr: Ptr): Ptr;
FUNCTION GetMouseMovement (gMouse0: Point): Size;
FUNCTION DoubleClick: BOOLEAN;
PROCEDURE DimRgn (rgn: RgnHandle);
FUNCTION Max (a, b: INTEGER): INTEGER;
FUNCTION Min (a, b: INTEGER): INTEGER;
FUNCTION GetWindowPartColor (window: WindowPtr; part: INTEGER; VAR
color: RGBColor): BOOLEAN;
UNIT OffscreenSubs;
INTERFACE
USES
Quickdraw, wBMInterface, wBMGlobals, wBMMiscSubs;
FUNCTION GetMaxAreaDevice (globalRect: Rect): GDHandle;
FUNCTION CreateOffScreen (VAR myRect: Rect): OffScreenRecHdl;
PROCEDURE ToOnScreen (COSHdl: OffScreenRecHdl);
PROCEDURE DisposOffScreen (VAR COSHdl: OffScreenRecHdl);
UNIT wBMScrollSubs;
INTERFACE
USES
Quickdraw, wBMGlobals, wBMMiscSubs;
FUNCTION ScrollHoriz (wp: WindowPtr): ControlHandle;
FUNCTION ScrollVert (wp: WindowPtr): ControlHandle;
PROCEDURE ScrollShow (wp: WindowPtr);
PROCEDURE ScrollHide (wp: WindowPtr);
PROCEDURE InvalidScroll (wp: WindowPtr);
PROCEDURE ValidScroll (wp: WindowPtr);
PROCEDURE ScrollResize (wp: WindowPtr);
FUNCTION GetWindowType (window: WindowPtr): INTEGER;
UNIT wBarMenuProc;
INTERFACE
USES
Quickdraw, wBMInterface, wBMGlobals, wBMMiscSubs, OffScreenSubs, wBMScrollSubs;
FUNCTION wInitMenus: wMenuBarListHdl;
FUNCTION wGetNewMBar (wp: WindowPtr; wMenuBarID: INTEGER): wMenuBarHandle;
FUNCTION wGetMenuBar (wp: WindowPtr): wMenuBarHandle;
PROCEDURE wSetMenuBar (theMenuBar: wMenuBarHandle; wp: WindowPtr);
PROCEDURE wAddWMB (theMenuBar: wMenuBarHandle);
PROCEDURE wDeleteWMB (wp: WindowPtr);
PROCEDURE wClearMenuBarList (theMenuBarList: wMenuBarListHdl);
PROCEDURE wInsertMenu (theMenuBar: wMenuBarHandle; theMenu: MenuHandle;
beforeID: INTEGER);
PROCEDURE wDeleteMenu (theMenuBar: wMenuBarHandle; menuID: INTEGER);
PROCEDURE GetWBMrects (wp: WindowPtr; VAR WBMrect, leftMSArect, rightMSArect:
Rect);
{ only local: }
{ PROCEDURE wSetColorMenu (menusID, itemsID: INTEGER); }
PROCEDURE wDrawMenuBar (theMenuBar: wMenuBarHandle);
PROCEDURE wScrollMenuBar (theMenuBar: wMenuBarHandle);
PROCEDURE wGetMSA (theMenuBar: wMenuBarHandle);
PROCEDURE wDrawMSA (theMenuBar: wMenuBarHandle);
PROCEDURE wClearMenuBar (theMenuBar: wMenuBarHandle);
FUNCTION wMenuSelect (theMenuBar: wMenuBarHandle; startPt: Point): LONGINT;
FUNCTION wMenuKey (theMenuBar: wMenuBarHandle; ch: char): LONGINT;
PROCEDURE wHiliteMenu (theMenuBar: wMenuBarHandle; menuID: INTEGER);
PROCEDURE wChangeMenuBarSize (wp: WindowPtr; zooming: BOOLEAN);
UNIT wBMWindSubs;
INTERFACE
USES
Quickdraw, Palettes, wBMInterface, wBMGlobals, wBMMiscSubs, wBMScrollSubs,
wBarMenuProc, wBMBalloons;
FUNCTION InitWindowStorage: allWSHdl;
PROCEDURE CalcWindowRect (window: WindowPtr; VAR r: Rect);
PROCEDURE DisplayWindow (window: WindowPtr; TLPR: Point);
FUNCTION DoNewWindow (windowID, wMBARID: INTEGER; VAR offset: Point):
BOOLEAN;
PROCEDURE CloseOurWindow (window: WindowPtr);
PROCEDURE DoCloseAll;
Next is my complete wBMGlobals.p file. I wouldnt even bother with it except for the RECORD types that serve as the chief worker-bees for off-screen BitMaps/PixMaps, WMBs and multiple windows.
{2}
UNIT wBMGlobals;
INTERFACE
{ ------------------------------------------ }
{ Global constants: }
{ ------------------------------------------ }
CONST
{ General constant: }
Enter = 3;
ohFudge = 1; { Guess what ????? }
{ low-memory global: }
ROM85Loc = $28E;
{ Specific constants ... }
{ Stuff for main Menu ... }
kMBarDisplayed = 128;
mainMBARID = kMBarDisplayed;
AppleMenuID = 1;
AboutItem = 1;
AdisabledItem = 2;
{ ---- }
FileMenuID = 2;
NewWindowItem = 1;
CloseWindowItem = 2;
FdisabledItem = 3;
QuitItem = 4;
{ ---- }
EditMenuID = 3;
UndoItem = 1;
EdisabledItem = 2;
CutItem = 3;
CopyItem = 4;
PasteItem = 5;
ClearItem = 6;
{ Stuff for Window Menu ... }
kMBarNotDisplayed = 129;
wMBARID = kMBarNotDisplayed;
wAppleMenuID = 1001;
wAboutItem = 1;
wAdisabledItem = 2;
{ ---- }
wFileMenuID = 1002;
wNewWindowItem = 1;
wCloseWindowItem = 2;
wFdisabledItem = 3;
wQuitItem = 4;
{ ---- }
wEditMenuID = 1003;
wUndoItem = 1;
wEdisabledItem = 2;
wCutItem = 3;
wCopyItem = 4;
wPasteItem = 5;
wClearItem = 6;
{ -------------------------------------------- }
{ Hierarchical Menus ... }
{ -------------------------------------------- }
wNewHierMenuID = 104;
wNewHierItem = 1;
wCloseHierMenuID = 105;
wCloseHierItem = 1;
wHierHierMenuID = 106;
wHierHierItem = 1;
{ Window goodies ... }
maxWindows = 20; { A # subject only to memory. }
kDefaultWindowID = 1001;
mainWindowID = kDefaultWindowID;
newWindowID = mainWindowID + 1;
frame = 1; { Window parts in Pixels ... }
shadow = 1;
title = 18;
horizScrollID = 128;
vertScrollID = 129;
scrollWidth = 16;
scrollHeight = scrollWidth;
growBoxSize = scrollWidth - frame;
{ Miscellaneous stuff ... }
logoID = 131;
{ --------------------------------------------------------------------------------------------------------------
}
{ ... for Error handling in my Off-screen map routine(s): }
{ --------------------------------------------------------------------------------------------------------------
}
NewCOSHdlError = -10000;
MaxDevError = -15000;
NewBaseAddrPtrError = -20000;
CloneHdlError = -25000;
{ ------------------------------------ }
{ MultiFinder stuff: }
{ ------------------------------------ }
{ _WaitNextEvent = $A860; -- in new Traps.p interface }
{ _Unimplemented = $A89F; }
SysEnvironsVersion = 1;
{ OSEvent is the event number of the suspend/resume and }
{ mouse-moved Events sent by MultiFinder. Once you }
{ determine that an event is an OSEvent, look at the }
{ High byte of the message sent with the event to }
{ determine which kind it is. To differentiate between }
{ suspend & resume, look at the resumeMask bit. }
OSEvent = app4Evt;
suspendResumeMessage = 1;
mouseMovedMessage = $FA;
resumeMask = 1;
{ ------------------------------------------------------------------
}
{ ... for Window Menu Bar routines: }
{ ------------------------------------------------------------------
}
noneHilited = -1; { Stored in wMenuBar.hilited }
{ if nada. }
menuTitleBit = 31; { Offset for Menu title bit in }
{ enableFlags. }
flashDelay = 10; { For blinking Menu title }
{ and Menu item. }
normalSize = 12; { Radius FPD stuff ... }
chicago16 = 16; { Special FONT. }
dontScrollMenu = 0; { Pass to wDrawMenuBar }
{ if appropriate. }
atEnd = 0; { ... as in InsertMenu(MenuHdl, 0); }
noMBARrsrc = -1; { There aint any ... }
mceMenuBar = 0; { mctID & mctItem for }
{ color Menus ... }
mceMenuTitle = 0;
zooming = TRUE;
regMenu = 0; { NOT a Hierarchical Menu. }
mainMenu = 0; { NO parent Menu. }
{ ------------------------------------------ }
{ Global types: }
{ ------------------------------------------ }
TYPE
{ General stuff: }
RgnHandlePtr = ^RgnHandle;
wordPtr = ^INTEGER;
longPtr = ^LONGINT;
{ BitMapPtr = ^BitMap; -- in new QuickDraw.p interface }
{ The following is required to avoid calling }
{ _GetNewWindow with NIL as the wStroage }
{ parameter. Otherwise, we risk fragmenting }
{ the Application Heap. }
{ }
{ [Adapted from Dan Westons Assembly Code] }
oneWStorage = RECORD
inUse: BOOLEAN;
fill: BOOLEAN;
ws: WindowRecord;
END;
allWStorage = RECORD
ones: ARRAY[1..maxWindows] OF oneWStorage;
END;
allWSPtr = ^allWStorage;
allWSHdl = ^allWSPtr;
{ Off Screen stuff: }
OffScreenRec = RECORD
CreateOffScreenError: OSErr;
oldDevice: GDHandle;
origPort: GrafPtr;
drawingRect: Rect;
myMaxDevice: GDHandle;
myBits: Handle;
offGrafPort: GrafPort;
offGrafPtr: GrafPtr;
offCGrafPort: CGrafPort;
offCGrafPtr: CGrafPtr;
ourCTHandle: CTabHandle;
offBitMapPtr, onScreenBitsPtr: BitMapPtr;
END;
OffScreenRecPtr = ^OffScreenRec;
OffScreenRecHdl = ^OffScreenRecPtr;
{ ... for Window Menus: }
wMenuRec = RECORD
mh: MenuHandle;
menuType: INTEGER; { Regular or Hierarchical. }
parentID: INTEGER; { Hierarchical stuff ... }
menuParentRgn, cumParentRgn: RgnHandle;
titleRect: Rect;
MenuDownOSHdl: OffScreenRecHdl;
END;
wMenuBar = RECORD
numMenus: INTEGER;
barLength: INTEGER;
titleHilited: INTEGER; { Menu # or noneHilited. }
leftScrollPoly, rightScrollPoly: PolyHandle;
{ ... to Menu Scroll Activators (MSA). }
saveCumScrollMenuX: INTEGER;
barOSHdl: OffScreenRecHdl;
wp: WindowPtr;
wMCTable: MCTableHandle;
wMenus: ARRAY[0..0] OF wMenuRec;
END;
wMenuBarPtr = ^wMenuBar;
wMenuBarHandle = ^wMenuBarPtr;
{ ---------- }
wMenuBarList = RECORD
numWindows: INTEGER;
wMBars: ARRAY[0..0] OF wMenuBarHandle;
END;
wMenuBarListPtr = ^wMenuBarList;
wMenuBarListHdl = ^wMenuBarListPtr;
aDynamicBalloon = RECORD { ... for my NEW dynamic windows. }
dynamicStrID: INTEGER;
dynamicStrIndex: INTEGER;
dynamicR: Rect;
END;
{ The BIG guys: }
RadBWStatus = PACKED RECORD
{ For Internal use, ONLY !! }
Signature, CPFlags, SSDelay, VertOffset: Char;
LargeFontEn, PluggedIn: Char; { PluggedIn = DontRepos }
MacBits: BitMapPtr;
BigTicksPtr: Ptr;
BigTicks: LONGINT;
Reserved1: LONGINT;
TopBigRAM, IdleHook: Ptr;
Reserved2: LONGINT;
CursorHook: Ptr;
END;
RadBWStatPtr = ^RadBWStatus;
RadBWStatHdl = ^RadBWStatPtr;
{ ---------- }
RadIIStatus = PACKED RECORD
AutoCenter, AutoLower, TearOffMenus, ScreenDump: Char;
LargeMenus, ScreenSaver, SaverActive, Reserved: Char;
ScreenSaverDelay, InitVers: INTEGER;
CardID: PACKED ARRAY[0..5] OF Char;
ROMVers: PACKED ARRAY[0..5] OF INTEGER;
END;
RadIIStatPtr = ^RadIIStatus;
RadIIStatHdl = ^RadIIStatPtr;
{ ---------- }
PivotDataStruct = PACKED RECORD
flipped, command, dLogFlags, xInternal, topOffset, bottomOffset:
Char;
tileFactor: INTEGER;
reserved: PACKED ARRAY[0..4] OF Char;
movementFlags: Char;
parameter, result: LONGINT;
resultRect: Rect;
END;
PivotDSPtr = ^PivotDataStruct;
PivotDSHand = ^PivotDSPtr;
{ ---------- }
radiusType = (none, radBW, radII);
RadiusData = RECORD
PivotHdl: PivotDSHand;
CASE radType : radiusType OF
none: (
);
radBW: (
BWHdl: RadBWStatHdl;
);
radII: (
IIHdl: RadIIStatHdl;
);
END;
RadiusDataPtr = ^RadiusData;
RadiusDataHdl = ^RadiusDataPtr;
{ -------------------------------------- }
{ Global variables: }
{ -------------------------------------- }
VAR
{ ... for Main PROGRAM: }
gStripAddressMask: LONGINT;
oldPort: GrafPtr;
lScreen, gScreen, visRect, updateRect: Rect;
ROM: wordPtr;
mBarHt: INTEGER;
AppleMenu, FileMenu, EditMenu: MenuHandle;
aMac2, hasGrowIcon: BOOLEAN;
colorDepth: INTEGER;
TheWindow, FW: WindowPtr;
windowCount, newCount, windType: INTEGER;
windowStorage: allWSHdl;
Event: EventRecord;
windowLoc: INTEGER;
ignore, moreNew, brandNew: BOOLEAN;
offset, deltaOffset: Point;
{ ... to avoid flickering Menu Bar: }
prevDA, prevWind, currDA, myAppl, applWind: BOOLEAN;
Done, InWindow, InWBMenu: BOOLEAN;
WNE, InForeGround: BOOLEAN;
Sleep, finalTicks: LONGINT;
ourControl: ControlHandle;
{ Window Menus ... }
pnState: PenState;
WBMrect, leftMSArect, rightMSArect, gWBMrect: Rect;
mbHState: SignedByte;
oldClip, onScreenRgn: RgnHandle;
oldForeColor, oldBackColor: RGBColor; { Color stuff ... }
oldMCTable, newMCTable: MCTableHandle;
found: BOOLEAN;
mBarList: wMenuBarListHdl;
mBar: wMenuBarHandle;
newBarListSize, newWMBSize: Size;
titleWidth, scrollPolyDXY: INTEGER;
{ Pixels between adjacent Menus & }
{ pixels to invert on each side }
{ of Menu title: }
betweenTitles, invertOverlap: INTEGER;
aboveBelowItem, menuFrame, menuShadow, frameShad: INTEGER;
whichMenu, whichItem: INTEGER;
{ Radius BIG screens ... }
WMgrPort: GrafPtr;
FPDRsrc: RadiusData;
BIGfont: Handle;
sizeFont: INTEGER;
{ System 7.0 ... }
startBalloons, balloonsUp, saveBalloons: BOOLEAN;
HelpMenu: MenuHandle;
origNumHelpItems: INTEGER;
dynamicBalloons: ARRAY[0..2] OF aDynamicBalloon;
lastBalloon: INTEGER;
IMPLEMENTATION
END. { UNIT = wBMGlobals }
Speaking of multiple windows and one of those snipits I promised you, the principal problem here centers on the fact that the majority of code Ive seen repeatedly calls GetNewWindow, passing NIL as the wStorage Pointer. Bad news!!! The window record will be allocated as a non-relocatable object on the heap in which case you risk generating a fragmented heap. Dan Weston, in his positively super two-book set, The Complete Book of Macintosh Assembly Language Programming, passes a Pointer to the ws field of a oneWStorage RECORD and sets its inUse field. You see ... weve initialized a NewHandle to a allWStorage RECORD shortly after our call to InitManagers. This new handle contains, say, 20 oneWStorage RECORDs:
{3}
FUNCTION InitWindowStorage: allWSHdl;
{ Call this hummer after InitManagers. }
VAR
wsHdl: allWSHdl;
BEGIN
InitWindowStorage := NIL; { Worry-wart !! }
wsHdl := allWSHdl(NewClearHandle(SizeOf(allWStorage)));
IF MemError = noErr THEN
BEGIN
MoveHHi(Handle(wsHdl));
HLock(Handle(wsHdl));
;
windowCount := 0;
InitWindowStorage := wsHdl;
END; { IF noErr }
END; { InitWindowStorage }
Every time you call GetNewWindow, you scan these 20 and stop at the first
whose inUse = FALSE & set it to TRUE.
FUNCTION DoNewWindow (windowID, wMBARID: INTEGER; VAR offset: Point):
BOOLEAN;
{ IF NOT DoNewWindow( ) THEN }
{ OhOh; }
VAR
i: INTEGER;
FUNCTION FindWStorage (VAR index: INTEGER): BOOLEAN;
BEGIN
index := 1;
WITH windowStorage^^ DO
BEGIN
WHILE (index <= maxWindows) & ones[index].inUse DO
index := index + 1;
;
IF index <= maxWindows THEN
BEGIN
ones[index].inUse := TRUE;
FindWStorage := TRUE;
END
ELSE { no more windows allowed }
FindWStorage := FALSE;
END; { WITH }
END; { FindWStorage }
BEGIN{ DoNewWindow }
DoNewWindow := FALSE;
;
moreNew := moreNew & FindWStorage(i); { i is VARed. }
IF NOT moreNew THEN
EXIT(DoNewWindow);
IF aMac2 THEN
TheWindow := GetNewCWindow(windowID, @windowStorage^^.ones[i].ws,
WindowPtr(-1))
ELSE
TheWindow := GetNewWindow(windowID, @windowStorage^^.ones[i].ws, WindowPtr(-1));
;
IF TheWindow = NIL THEN
BEGIN
{ Reverse effect of FindWStorage: }
windowStorage^^.ones[i].inUse := FALSE;
EXIT(DoNewWindow);
END;
;
SetPort(TheWindow);
IF windowID = newWindowID THEN
BEGIN
ourControl := GetNewControl(horizScrollID, TheWindow);
ourControl := GetNewControl(vertScrollID, TheWindow);
END; { retrieve Scroll Bars }
{ oldOffset --> newOffset: }
offset := GetTLWindPortRect(TheWindow, offset);
mBar := wGetNewMBar(TheWindow, wMBARID);
IF mBar <> NIL THEN
wAddWMB(mBar);
ScrollResize(TheWindow); { Does nada if NO Scroll Bars. }
DisplayWindow(TheWindow, offset);
{ Since an Update Event draws the MSAs & the Window Menu, }
{ Bar we do NOT want these drawn by the DoActivate PROC: }
brandNew := TRUE;
DoNewWindow := TRUE;
;
windowCount := windowCount + 1;
moreNew := moreNew & (windowCount < maxWindows);
END; { DoNewWindow }
Every time you call CloseWindow, you scan the same 20 and stop when inUse = TRUE and WindowPtr = @oneWStorage.ws . At this point, of course, we reset the former to FALSE so that my DoNewWindow FUNCTION finds it available.
{4}
{ ---------------------------------------------- }
{ One at a time, folks !! }
{ ---------------------------------------------- }
PROCEDURE CloseOurWindow (window: WindowPtr);
VAR
myPic: PicHandle;
pal: PaletteHandle;
aux: BOOLEAN;
auxWind: AuxWinHndl;
The next snipit centers on my comparison of FrontWindows resultant WindowPtr with @oneWStorage.ws as Ive already talked about..
PROBLEM-- oneWStorage is a field in a LOCKED allWStorage handle and, therefore, @oneWStorage.ws has its high bit set. Guess what ... FrontWindows WindowPtr has its high bit clear. I borrowed GetStripAddressMask and QuickStrip from Tech Note #213.
{5}
PROCEDURE DisposeWStorage (wp: WindowPtr);
VAR
i: INTEGER;
found: BOOLEAN;
wsHState: SignedByte;
BEGIN
found := FALSE;
{ oneWStorage dsec 0 }
{ inUse byte ; = 0 }
{ fill byte ; = 1 }
{ ws byte WindowSize ; = 2 }
{ dend }
{ ... }
{ move.l windowStorage,a0 }
{ move.l (a0),a4 ; Locked Master Ptr. }
{ lea ws(a4),a1 ; Bit #31 also set. }
{ cmpa.l wp,a1 ; wp = 8(a6) }
{ ... }
wsHState := HGetState(Handle(windowStorage));
windowStorage^ := allWSPtr(QuickStrip(Ptr(windowStorage^)));
WITH windowStorage^^ DO
BEGIN
FOR i := 1 TO maxWindows DO
IF ones[i].inUse THEN
IF wp = @ones[i].ws THEN
BEGIN
found := TRUE;
Leave;
END;
IF found THEN
ones[i].inUse := FALSE {Undo effect of FindWStorage.}
ELSE { should NOT happen !! }
;
END; { WITH }
;
{ We have NOT done anything to move memory, }
{ therefore _MoveHHi is NOT required. }
HSetState(Handle(windowStorage), wsHState);
END; { DisposeWStorage }
BEGIN { CloseOurWindow }
IF window = NIL THEN
EXIT(CloseOurWindow);
IF WindowPeek(window)^.windowKind < 0 THEN
CloseDeskAcc(WindowPeek(window)^.windowKind)
ELSE
BEGIN
IF aMac2 THEN
BEGIN
pal := GetPalette(window);
IF pal <> NIL THEN
DisposePalette(pal);
END; { IF aMac2 }
myPic := GetWindowPic(window);
IF myPic <> NIL THEN
BEGIN
HUnlock(Handle(myPic));
ReleaseResource(Handle(myPic));
END; { IF myPic <> NIL }
wDeleteWMB(window);
DisposeWStorage(window);
CloseWindow(window);
WITH offset DO
BEGIN
h := h - deltaOffset.h;
IF h < 0 THEN
h := 0;
v := v - deltaOffset.v;
IF v < 0 THEN
v := 0;
END; { WITH }
windowCount := windowCount - 1;
;
IF windowCount = 0 THEN
BEGIN
{ In case a lingering DA doesnt properly }
{ handle its doCursor routine: }
InitCursor;
{ Wait till last is gone because all windows }
{ share a common Color Table. }
IF aMac2 THEN
BEGIN
aux := GetAuxWin(window, auxWind);
IF aux THEN
ReleaseResource(Handle(auxWind));
END; { IF aMac2 }
END; { IF no more windows }
moreNew := TRUE;
END; { NOT a Desk Accessory window }
END; { CloseOurWindow }
Then comes my OffScreenRec which doesnt make sense until we see the code later on ... so cool it ...
Next comes the stuff for WMBs. The wMenuBarList consists of a window count and an array of wMenuBarHandles. Gotta have the count so we know how many handles. Each of these handles consists of:
a) menu count and associated array of individual wMenuRecs. Again, this count tells us how many.
b) barLength tells us if were clicking the Mouse on ANY active Menu. If so, then we call PtInRect for the titleRect of each wMenuRec to find out which Menu.
c) titleHilited enters with wHiliteMenu which is called as our MouseDown travels from Menu-to-Menu: un-Hilite the old titleRect and Hilite the new.
d) left & right PolyHandles, my MSAs for horizontal scrolling of the WMB. Click on one of these dudes to scroll. If we scroll part of a titleRect out of view, call FillPoly(polyH, black); when we scroll back into view, call FillPoly(polyH, white) followed by FramePoly(polyH).
e) gotta save the amount weve scrolled the WMB so when we zoom back in from just zooming out, we can re-scroll to where we were.
f) its the OffScreenRecHdl we scroll for instantaneous blitting on screen.
g) the MCTableHandle handles color ... natch !!
h) each wMenuRec basically is just a Menuhandle together with some added info:
1) the menuType determines the placement of the titleRect ... for a regular Menu, its in the main WMB and for a hierarchical Menu, its the rect of the parent item.
2) the parentID for a hierarchical Menu is recorded to help us determine which wMenus[i] were looking at.
3) the two RgnHandles play in the wMenuSelect FUNCTION as our MouseDown bounces between regular, hierarchical and even hierarchical-hierarchical Menus. We create another OffScreenREcHdl for the drawing of the pulled-down Menu. Because of hierarchical Menus, several of these off-screen beauties may exist simultaneously. So weve gotta keep track of these off-screen handles so we can correctly dispose of them when we veer off-course ... either to a different Menu entirely or to a different item, an item that does NOT have a sub-Menu. The above RgnHandles help us to do just that.
4) The drawing of the pulled-down Menus occurs within MenuDownOSHdl.
Finally, holding up the rear, the BIG guns from Radius ...
Another snipit enters in the UNIT wBMMiscSubs with InitManagers. Instead of calling MoreMasters 10 times or whatever number turns you on, check out Tech Note #53.
Now, my off-screen gymnastics without many comments since the routines are essentially repeated from one of my former articles:
{6}
UNIT OffscreenSubs;
INTERFACE
USES
wBMInterface, wBMGlobals, wBMMiscSubs;
FUNCTION GetMaxAreaDevice (globalRect: Rect): GDHandle;
FUNCTION CreateOffScreen (VAR myRect: Rect): OffScreenRecHdl;
PROCEDURE ToOnScreen (COSHdl: OffScreenRecHdl);
PROCEDURE DisposOffScreen (VAR COSHdl: OffScreenRecHdl);
{ ********** }
FUNCTION GetMaxAreaDevice (globalRect: Rect): GDHandle;
{ Find the greatest overlap device }
{ for the given global rectangle. }
VAR
area: LONGINT;
maxArea: LONGINT;
device: GDHandle;
intersection: Rect;
BEGIN
GetMaxAreaDevice := NIL;
;
maxArea := 0;
;
device := GetDeviceList;
WHILE device <> NIL DO
BEGIN
IF TestDeviceAttribute(device, screenDevice) THEN
IF TestDeviceAttribute(device, screenActive) THEN
IF SectRect(globalRect, device^^.gdRect, intersection) THEN
BEGIN
WITH intersection DO
area := LONGINT(right - left) * LONGINT(bottom - top);
IF area > maxArea THEN
BEGIN
GetMaxAreaDevice := device;
maxArea := area;
END; { IF area > maxArea }
END; { IF SectRect ... }
device := GetNextDevice(device);
END; { WHILE device <> NIL }
END; { GetMaxAreaDevice }
FUNCTION CreateOffScreen (VAR myRect: Rect): OffScreenRecHdl;
{ Reference: Tech Note #120 }
{ with special thanks to: }
{ Forrest Tanaka and }
{ Jon Zap of MacDTS }
{ }
{ NOTE: Local window coordinates are input, but local }
{ device coordinates are returned for drawing purposes. }
VAR
offRowBytes, sizeOfOff, tempSeed: LONGINT;
localRect, globRect: Rect;
i, maxDepth: INTEGER;
err: OSErr;
COSHdl: OffScreenRecHdl;
PROCEDURE ErrorOut (error: OSErr);
BEGIN
IF error = NewCOSHdlError THEN
CreateOffScreen := NIL
ELSE
BEGIN
COSHdl^^.CreateOffScreenError := error;
CreateOffScreen := COSHdl;
END;
EXIT(CreateOffScreen);
END; { ErrorOut }
BEGIN { CreateOffScreen }
COSHdl := OffScreenRecHdl(NewClearHandle(SizeOf(OffScreenRec)));
;
IF MemError <> noErr THEN
ErrorOut(NewCOSHdlError);
;
MoveHHi(Handle(COSHdl));
HLock(Handle(COSHdl)); { Lock this sucker down !! }
WITH COSHdl^^ DO
BEGIN
{ CreateOffScreenError := noErr; -- We hope !! }
GetPort(origPort); { Used by ToOnScreen. }
drawingRect := myRect; { Saved for use after }
{ call to ToOnScreen. }
globRect := myRect; { Were about to switch }
{ the Port to off-screen: }
LocalGlobal(globRect);
IF NOT aMac2 THEN
BEGIN
offGrafPtr := @offGrafPort;
OpenPort(offGrafPtr);
maxDepth := 1;
END { ... a low-life machine }
ELSE
BEGIN
myMaxDevice := GetMaxDevice (globRect);
IF myMaxDevice = NIL THEN
ErrorOut(MaxDevError);
oldDevice := GetGDevice;
SetGDevice(myMaxDevice);
;
offCGrafPtr := @offCGrafPort; { Initialize this guy. }
OpenCPort(offCGrafPtr);
MoveHHi(Handle(offCGrafPtr^.portPixMap)); { Arrgh !! }
HLock(Handle(offCGrafPtr^.portPixMap));
maxDepth := offCGrafPtr^.portPixMap^^.pixelSize;
END; { ELSE: aMac2 }
{ CanNOT use my GlobalLocal PROC because we may have dragged }
{ our window to a secondary screen. Global here is with }
{ respect to the main screen and Local is with respect to }
{ the secondary screen. }
{ }
{ From Forrest Tanaka: }
{ When GetMaxDevice returns the secondary screens GDevice & }
{ we set that to the current GDevice, then OpenCPort creates }
{ a CGrafPort which has a portRect=GetMainDevice^^.gdPMap^^. }
{ bounds which is in the global coordinates for all the }
{ screens pixel images with a topLeft = (0,0). The new }
{ CGrafPort.portPixMap^^.bounds is in the local coordinates }
{ of the secondary screen with a topLeft = (0,-640), e.g. }
{ }
{ In effect, the port that OpenCPort gives you is NOT a }
{ port because the portRect pertains to the wrong screen. }
{ This means that calling GlobalLocal shifts the localRect }
{ waaaaay over somewhere ... BECAUSE the difference between }
{ the above portRect and portPixMap^^.bounds is SO large!!! }
localRect := globRect;
WITH screenBits.bounds DO
OffsetRect(localRect, left, top);
;
IF aMac2 THEN
BEGIN
RectRgn(offCGrafPort.visRgn, localRect)
offCGrafPort.portRect := localRect;
END
ELSE
BEGIN
RectRgn(offGrafPort.visRgn, localRect);
offGrafPort.portRect := localRect;
END;
WITH localRect DO
BEGIN
offRowBytes := (maxDepth * (right - left) + 15) DIV 16;
IF ODD(offRowBytes) THEN
offRowBytes := offRowBytes + 1;
offRowBytes := offRowBytes * 2;
sizeOfOff := LONGINT(bottom - top) * offRowBytes;
END; { WITH }
myBits := NewClearHandle(sizeOfOff);
IF MemError <> noErr THEN
ErrorOut(NewBaseAddrPtrError);
MoveHHi(myBits);
HLock(myBits);
IF aMac2 THEN
BEGIN
WITH offCGrafPtr^.portPixMap^^ DO
BEGIN
baseAddr := myBits^;
rowBytes := offRowBytes + $8000; bounds := localRect;
END; { WITH }
offBitMapPtr := BitMapPtr(offCGrafPtr^.portPixMap^);
END { IF aMac2 }
ELSE { definitely ... YUCKY black-and-white. }
BEGIN
WITH offGrafPtr^.portBits DO
BEGIN
baseAddr := myBits^;
rowBytes := offRowBytes;
bounds := localRect;
END;
offBitMapPtr := @offGrafPtr^.portBits;
END;
IF aMac2 THEN
BEGIN
ourCTHandle := myMaxDevice^^.gdPMap^^.pmTable;
err := HandToHand(Handle(ourCTHandle)); { Clone it. }
IF err <> noErr THEN
ErrorOut(CloneHdlError);
WITH ourCTHandle^^ DO
FOR i := 0 TO ctSize DO
ctTable[i].value := i;
ourCTHandle^^.ctFlags := BAND(ourCTHandle^^.ctFlags, $7FFF);
tempSeed := GetCTSeed; { Thanks, Scott Knaster !! }
ourCTHandle^^.ctSeed := tempSeed;
offCGrafPtr^.portPixMap^^.pmTable := ourCTHandle;
END; { IF aMac2 }
myRect := localRect; { Return local device coords. }
END; { WITH COSHdl^^ DO }
ErrorOut(noErr); { Whew !! }
END; { CreateOffScreen}
{ ******************* }
{ Back to Square 1: }
{ ******************* }
PROCEDURE ToOnScreen (COSHdl: OffScreenRecHdl);
BEGIN
WITH COSHdl^^ DO { COSHdl is locked coming in. }
BEGIN
SetPort(origPort);
IF aMac2 THEN
SetGDevice(oldDevice);
END; { WITH }
END; { ToOnScreen}
PROCEDURE DisposOffScreen (VAR COSHdl: OffScreenRecHdl);
LABEL
100;
BEGIN
IF COSHdl = NIL THEN
EXIT(DisposOffScreen);
WITH COSHdl^^ DO
BEGIN
IF CreateOffScreenError = MaxDevError THEN
GOTO 100;
{ NewBaseAddrPtrError or CloneHdlError or noErr ... }
IF aMac2 THEN
BEGIN
IF CreateOffScreenError = noErr THEN
DisposHandle(Handle(ourCTHandle));
HUnlock(Handle(offCGrafPtr^.portPixMap));
CloseCPort(offCGrafPtr);
END
ELSE
ClosePort(offGrafPtr);
IF CreateOffScreenError <> NewBaseAddrPtrError THEN
BEGIN
HUnlock(myBits);
DisposHandle(myBits);
END;
END; { WITH }
100:
HUnlock(Handle(COSHdl));
DisposHandle(Handle(COSHdl));
COSHdl := NIL; { Mark as gone ... }
END; { DisposOffScreen }
END. { UNIT = OffscreenSubs }
Another snipit centers on the fact that my HandleCursor PROC demands knowledge of what kind of window FrontWindow is:
{7}
FUNCTION GetWindowType (window: WindowPtr): INTEGER;
{ Here, window type = Window Definition ID. }
CONST
RomMapInsertLoc = $B9E;
mapTrue = $FFFF;
VAR
varCode, WDEFRsrcID, wType: INTEGER;
WDEFHandle: Handle;
WDEFType: ResType;
WDEFName: Str255;
BEGIN
varCode := GetWVariant(window);
{ Now, what about rDocProc types since their Variation }
{ Codes duplicate those of some standard types such as }
{ documentProc & dBoxProc. I could call: }
{ }
{ regionSize := WindowPeek(window)^.strucRgn^^.rgnSize; }
{ }
{ If regionSize = 10, then rgnBBox is rectangular; so }
{ if <> 10, weve got an rDocProc. HOWEVER, if the }
{ window is invisible because Ive not yet called }
{ _ShowWindow, rgnBBox is empty and regionSize STILL }
{ equals 10. The solution is simple ... call }
{ GetWindowType when the window is being activated. }
{ Better yet ... the solution presented below, thanks }
{ to MacDTS, avoids this workaround. In addition, }
{ MacDTS solution avoids the pitfalls of Murphy }
{ inventing a totally new window type with a regionSize }
{ that dupes that of rDocProc. }
WDEFHandle := Handle(QuickStrip(Ptr(WindowPeek(window)^.windowDefProc)));
;
LoadResource(WDEFHandle); { May have been purged ... }
{ !! Thanks !!, Ben Cranston: }
wordPtr(RomMapInsertLoc)^ := mapTrue;
GetResInfo(WDEFHandle, WDEFRsrcID, WDEFType, WDEFName);
wType := 16 * WDEFRsrcID + varCode;
IF (wType = documentProc) | (wType = zoomDocProc) THEN
hasGrowIcon := TRUE
ELSE
hasGrowIcon := FALSE;
;
hasGrowIcon := hasGrowIcon | (ScrollHoriz(window) <> NIL) | (ScrollVert(window)
<> NIL);
GetWindowType := wType;
END; { GetWindowType }
Another snipit goodie pertains to determining if were staring at a regular or a Desk Accessory Menu. I need this hummer because my HandleCursor routine is a part of my doPeriodic loop
{8}
FUNCTION daMenu: BOOLEAN;
{ I know ... NEVER assume knowledge of Menu }
{ Record structures ... BUT ... }
CONST
MenuListLoc = $A1C;
TYPE
rMenuRec = RECORD
menuOH: MenuHandle;
menuLeft: INTEGER; { Left edge of Menu. }
END; { rMenuRec }
hMenuRec = RECORD
menuHOH: MenuHandle;
reserved: INTEGER;
END; { hMenuRec }
MenuList = RECORD
lastMenu: INTEGER; { Offset to last regular }
{ MenuHandle. }
lastRight: INTEGER; { Right edge of last }
{ Menus title. }
mbResID: INTEGER;
rMenu: ARRAY[0..0] OF rMenuRec;
{ The following fields are also present: }
{ }
{ lastHMenu: INTEGER; -- Offset from here to last }
{ hierarchical Menu. }
{ menuTitleSave: PixMapHandle; }
{ When my daMenu routine is called, there are NO }
{ hierarchical Menus: }
{ hMenu: ARRAY[0..0] OF hMenuRec; }
END; { MenuList }
MenuListPtr = ^MenuList;
MenuListHdl = ^MenuListPtr;
VAR
MLHdl: MenuListHdl;
nbrMenusX6, menuCounter, theMenuID: INTEGER;
BEGIN
MLHdl := MenuListHdl(longPtr(MenuListLoc)^);
nbrMenusX6 := ORD(MLHdl^^.lastMenu);
menuCounter := (nbrMenusX6 DIV 6) - 1;
WHILE menuCounter >= 0 DO
BEGIN
theMenuID := MLHdl^^.rMenu[menuCounter].menuOH^^.menuID;
{ Watch out !!! with System 7 ... }
{ ... the Help Menu (kHMHelpMenuID = -16490) & }
{ ... the Application Menu (ID = ???) }
IF (theMenuID < 0) & (theMenuID >= -16384) THEN
Leave;
menuCounter := menuCounter - 1;
END; { scanning the MenuBar }
{ I could have used: }
{ daMenu := theMenuID < 0; }
{ because I KNOW my app has menus. However, to make }
{ this routine applicable to ANY app, what if ANY app }
{ had zip menus and there were no DA menus, then I }
{ would have to initialize with: }
{ theMenuID := 0; }
{ putting an extra statement in my time-critical }
{ doPeriodic loop. }
daMenu := menuCounter >= 0;
END; { daMenu }
Here comes the firehose full of code pertaining to Window MenuBars ... some of the stuff, such as MenuDefProc you folks will recognize as unchanged from James Matthews article while others I did change slightly (e.g., Jims MenuDefGlue). Anywho, folks, they are waiting for you on disk ...
This next routine is called by wInitMenus in order to sprinkle the Radius magic here and there:
{9}
PROCEDURE InitBigScreen (VAR RadStatus: RadiusData; VAR fontSize: INTEGER);
CONST
largeMenuBar = 5; { Bit # in CPFlags field }
{ for non-MacII. }
RadiusID = 0;
VAR
statusHdl, pivotHand: Handle;
BEGIN
SetResLoad(TRUE);
pivotHand := PivotDSHand(GetNamedResource(INFO, Radius Pivot Display));
IF pivotHand = NIL THEN
LoadResource(pivotHand);
RadStatus.PivotHdl := PivotDSHand(pivotHand);
IF NOT aMac2 THEN
statusHdl := GetNamedResource(INFO, Radius Display)
ELSE
statusHdl := GetNamedResource(INFO, Radius II Display);
IF statusHdl = NIL THEN
BEGIN
LoadResource(statusHdl);
IF statusHdl = NIL THEN {Still !!! }
BEGIN
RadStatus.radType := none;
fontSize := normalSize;
EXIT(InitBigScreen);
END; { STILL! }
END; { Zip }
IF NOT aMac2 THEN
BEGIN
IF BTST(RadBWStatHdl(statusHdl)^^.CPFlags, largeMenuBar)
THEN
BEGIN
RadBWStatHdl(statusHdl)^^.LargeFontEn := chr(1);
AddResource(statusHdl, INFO, RadInfoID, Radius Display);
fontSize := chicago16;
{ ID = 128*font number + size: }
BIGfont := GetResource(FONT, 128 * systemFont + chicago16);
IF BIGfont = NIL THEN
LoadResource(BIGfont);
RadBWStatHdl(statusHdl)^^.LargeFontEn := chr(0);
RadBWStatHdl(statusHdl)^^.PluggedIn:= chr(0);
AddResource(statusHdl, INFO, RadInfoID, Radius Display);
END
ELSE
fontSize := normalSize;
;
RadStatus.radType := radBW;
RadStatus.BWHdl := RadBWStatHdl(statusHdl);
END
ELSE { aMac2 }
BEGIN
IF ord(RadIIStatHdl(statusHdl)^^.LargeMenus) <> 0 THEN
BEGIN
fontSize := chicago16;
BIGfont := GetResource(FONT, chicago16);
IF BIGfont = NIL THEN
LoadResource(BIGfont);
END
ELSE
fontSize := normalSize;
;
RadStatus.radType := radII;
RadStatus.IIHdl := RadIIStatHdl(statusHdl);
END;
END; { InitBigScreen }
Speaking of wInitMenus, note that we set the values of four parms with/without the Radius big screen monitors:
1) between titles -- see James Matthews article.
2) aboveBelowItem -- prevents half a menu item string showing for a vertically scrolling Menu.
3) menuFrame, menuShadow -- box around a pulled-down Menu.
scrollPolyDXY plays in wGetMSA. Jim discusses invertOverlap.
{10}
FUNCTION wInitMenus: wMenuBarListHdl;
{ Call immediately after InitManagers: }
{ }
{ Creates a wMenuBarList and quantifies }
{ assorted & sordid global parms. }
VAR
wMBL: wMenuBarListHdl;
BEGIN
wMBL := wMenuBarListHdl(NewClearHandle(SizeOf(wMenuBarList)));
;
IF MemError <> noErr THEN
BEGIN
wInitMenus := NIL;
EXIT(wInitMenus);
END; { Whoops !! }
{ wClearMenuBarList(wMBL); -- NOT needed here !! }
wInitMenus := wMBL;
{ ++ stuff for BIGees: }
InitBigScreen(FPDRsrc, sizeFont);
IF sizeFont > normalSize THEN
BEGIN
betweenTitles := 18;
aboveBelowItem := 3;
menuFrame := 2 * frame;
menuShadow := 2 * shadow;
END
ELSE { small potatoes }
BEGIN
betweenTitles := 14;
aboveBelowItem := 2;
menuFrame := frame;
menuShadow := shadow;
END; { ELSE }
frameShad := menuFrame + menuShadow;
scrollPolyDXY := mBarHt DIV 2;
{ Neighboring MENUs share inverted space: }
invertOverlap := (betweenTitles DIV 2) + 1;
END; { wInitMenus }
wGetNewMBar is called every time I create a new window. Please pay close attention to the code that addresses color. This is where I store the attached Menu resources Menu Color Table (MCTableHandle) into wMenuBarHandle^^.wMCTable. I need this info in order to draw the Menus colors.
{11}
FUNCTION wGetNewMBar (wp: WindowPtr; wMenuBarID: INTEGER): wMenuBarHandle;
{ Pass wMenuBarID = noMBARrsrc if you wish to start fresh }
{ and call wInsertMenu yourself. }
CONST
none = 0;
TYPE
rMenuBar = RECORD
numMenus: INTEGER;
menuIDs: ARRAY[0..0] OF INTEGER;
END;
rMenuBarPtr = ^rMenuBar;
rMenuBarHdl = ^rMenuBarPtr;
VAR
rMBar: rMenuBarHdl;
mh: MenuHandle;
i: INTEGER;
theWorld: SysEnvRec;
itDoesntMatter: OSErr;
BEGIN
wGetNewMBar := NIL; { Assume the pits !! }
;
IF wp = NIL THEN
EXIT(wGetNewMBar);
rMBar := rMenuBarHdl(GetResource(MBAR, wMenuBarID));
{ Out with the old Window Menu Bar if theres one }
{ and in with the new: }
wDeleteWMB(wp);
;
mBar := wMenuBarHandle(NewClearHandle(SizeOf(wMenuBar)));
IF MemError <> noErr THEN
BEGIN
IF rMBar <> NIL THEN
ReleaseResource(Handle(rMBar));
EXIT(wGetNewMBar);
END; { Whoops !! }
mBar^^.titleHilited := noneHilited;
wSetMenuBar(mBar, wp);
{ & so sue me -- Im paranoid !! }
IF (rMBar <> NIL) & (rMBar^^.numMenus > 0) THEN
BEGIN
{ Save & restore main Menu Color Table }
{ so that GetMenu does NOT change it. }
IF aMac2 THEN
oldMCTable := GetMCInfo;
FOR i := 0 TO (rMBar^^.numMenus - 1) DO
BEGIN
mh := GetMenu(rMBar^^.menuIDs[i]);
IF mh <> NIL THEN { Paranoid-ville again !! }
BEGIN
IF i = 0 THEN { = Apple Menu }
BEGIN
itDoesntMatter := SysEnvirons(1, theWorld);
IF theWorld.systemVersion < $0700 THEN
BEGIN
SetItemIcon(mh, AboutItem, none);
SetItemCmd(mh, AboutItem, char(none));
END;
AddResMenu(mh, DRVR); { + DAs }
END; { Apple Menu }
wInsertMenu(mBar, mh, atEnd);
{ For calling GetMenu & AddResMenu lots. }
DetachResource(Handle(mh));
END; { IF mh <> NIL }
END; { FOR }
;
ReleaseResource(Handle(rMBar));
IF aMac2 THEN
BEGIN
newMCTable := GetMCInfo; { Safe on the Stack !! }
SetMCInfo(oldMCTable);
{ Save for drawing & selecting. }
mbar^^.wMCTable := newMCTable;
END; { IF aMac2}
END; { IF a rMBar }
wGetNewMBar := mBar;
END; { wGetNewMBar }
wGetMenuBar is used to retrieve the given windows wMenuBarHandle for subsequent feeding to wDrawMenuBar and wDrawMSA for Update and Activate Events.
{12}
FUNCTION wGetMenuBar (wp: WindowPtr): wMenuBarHandle;
VAR
i: INTEGER;
BEGIN
found := false;
i := 0;
;
wGetMenuBar := NIL;
IF mBarList = NIL THEN
EXIT(wGetMenuBar);
WITH mBarList^^ DO
BEGIN
IF numWindows > 0 THEN
WHILE (NOT found) & (i < numWindows) DO
IF WMBars[i]^^.wp = wp THEN
found := true
ELSE
i := i + 1 { End of WHILE }
ELSE { zip windows }
;
IF found THEN
wGetMenuBar := WMBars[i];
END; { WITH }
END; { wGetMenuBar }
Just as James Matthews did it:
PROCEDURE wSetMenuBar (theMenuBar: wMenuBarHandle; wp: WindowPtr);
BEGIN
theMenuBar^^.wp := wp; { Simple, aint it ?!!? }
END; { wSetMenuBar }
For example:
windPtr := GetNewWindow(...);
menuBar := wGetNewMBar(windPtr, barID);
IF menuBar <> NIL THEN
wAddWMB(menuBar);
PROCEDURE wAddWMB (theMenuBar: wMenuBarHandle);
VAR
i: INTEGER;
BEGIN
newBarListSize := SizeOf(wMenuBarList) + (mBarList^^.numWindows + 1)
* 4;
IF newBarListSize > GetHandleSize(Handle(mBarList)) THEN
IF MemError = noErr THEN
SetHandleSize(Handle(mBarList), newBarListSize);
IF MemError = noErr THEN
WITH mBarList^^ DO
BEGIN
WMBars[numWindows] := theMenuBar;
numWindows := numWindows + 1
END; { WITH }
END; { wAddWMB }
Used when closing a window:
{13}
PROCEDURE wDeleteWMB (wp: WindowPtr);
VAR
i, j: INTEGER;
BEGIN
found := false;
i := 0;
IF mBarList^^.numWindows > 0 THEN
BEGIN
WHILE (NOT found) & (i < mBarList^^.numWindows) DO
IF mBarList^^.WMBars[i]^^.wp = wp THEN
found := true
ELSE
i := i + 1; { End of WHILE }
IF found THEN
BEGIN
wClearMenuBar(mBarList^^.WMBars[i]);
DisposHandle(Handle(mBarList^^.WMBars[i]));
{ Not the last one in the List. }
IF i <> (mBarList^^.numWindows - 1) THEN
FOR j := (i + 1) TO (mBarList^^.numWindows - 1) DO
mBarList^^.WMBars[j - 1] := mBarList^^.WMBars[j]
ELSE
{ Delete the last one. Already done, }
{ so nada required here. }
;
mBarList^^.numWindows := mBarList^^.numWindows - 1;
newBarListSize := GetHandleSize(Handle(mBarList)) - 4;
IF MemError = noErr THEN
SetHandleSize(Handle(mBarList), newBarListSize);
END; { IF found }
END { IF numWindows >0 }
ELSE { zip windows }
;
END; { wDeleteWMB }
To tell you the truth, I do NOT even use wClearMenuBarList, but its here for the sake of symmetry with wDrawMenuBar.
{14}
PROCEDURE wClearMenuBarList (theMenuBarList: wMenuBarListHdl);
{ For now, I do NOT use the passed parm because its a }
{ global. This may change in the future, however. }
CONST
WindowListLoc = $9D6; { 1st window in linked list. }
VAR
window: WindowPeek;
BEGIN
window := WindowPeek(longPtr(WindowListLoc));
WHILE window <> NIL DO
BEGIN
wDeleteWMB(WindowPtr(window));
window := window^.nextWindow;
END; { WHILE }
{ The following has already happened after execution }
{ of above WHILE loop: }
{ }
{ theMenuBarList^^.numWindows := 0; }
{ SetHandleSize(Handle(theMenuBarList), }
{ SizeOf(wMenuBarList)); }
END; { wClearMenuBarList }
These two routines are called by wInsertMenu & wDeleteMenu, respectively.
{15}
FUNCTION IncreaseSize (theMenuBar: wMenuBarHandle): OSErr;
VAR
hState: SignedByte;
BEGIN
newWMBSize := SizeOf(wMenuBar) + (theMenuBar^^.numMenus + 1) * SizeOf(wMenuRec);
IF newWMBSize > GetHandleSize(Handle(theMenuBar)) THEN
IF MemError = noErr THEN
BEGIN
hState := HGetState(Handle(theMenuBar));
HUnlock(Handle(theMenuBar));
SetHandleSize(Handle(theMenuBar), newWMBSize);
{ Just in case entry state is locked: }
MoveHHi(Handle(theMenuBar));
HSetState(Handle(theMenuBar), hState);
END;
;
IncreaseSize := MemError;
END; { IncreaseSize }
FUNCTION DecreaseSize (theMenuBar: wMenuBarHandle): OSErr;
VAR
hState: SignedByte;
BEGIN
newWMBSize := SizeOf(wMenuBar) + (theMenuBar^^.numMenus - 1) * SizeOf(wMenuRec);
IF newWMBSize < GetHandleSize(Handle(theMenuBar)) THEN
IF MemError = noErr THEN
BEGIN
hState := HGetState(Handle(theMenuBar));
HUnlock(Handle(theMenuBar));
SetHandleSize(Handle(theMenuBar), newWMBSize);
MoveHHi(Handle(theMenuBar));
HSetState(Handle(theMenuBar), hState);
END;
;
DecreaseSize := MemError;
END; { DecreaseSize }
Called when inserting & deleting a Menu so the Menus titleRect can be quantified.
{16}
FUNCTION GetTitleWidth (theMenu: MenuHandle): INTEGER;
VAR
oldTxSize, oldTxFont, oldTxMode: INTEGER;
oldTxStyle: Style;
BEGIN
oldTxSize := thePort^.txSize;
oldTxFont := thePort^.txFont;
oldTxStyle := thePort^.txFace;
TextSize(sizeFont);
TextFont(systemFont);
TextFace([]);
;
GetTitleWidth := StringWidth(theMenu^^.menuData);
;
TextSize(oldTxSize);
TextFont(oldTxFont);
TextFace(oldTxStyle);
END; { GetTitleWidth }
This routine is present for the same reason as aboveBelowItem as discussed above.
See wMenuSelect.
FUNCTION GetItemIconSize (theMenu: MenuHandle; item: INTEGER): INTEGER;
CONST
reducedIconCmd = $1D;
smallIconCmd = $1E;
VAR
iconNbr: Byte;
sizeIcon: INTEGER;
theCICN: CIconHandle;
cmdChar: char;
BEGIN
GetItemIcon(theMenu, item, iconNbr);
IF iconNbr = 0 THEN
sizeIcon := 0
ELSE
BEGIN
IF aMac2 THEN
BEGIN
theCICN := GetCIcon(iconNbr + 256);
IF theCICN <> NIL THEN
BEGIN
WITH theCICN^^.iconPMap.bounds DO
sizeIcon := bottom - top;
DisposCIcon(theCICN);
END
ELSE { no cicn }
sizeIcon := 32;
END { aMac2 }
ELSE
sizeIcon := 32;
GetItemCmd(theMenu, item, cmdChar);
IF (cmdChar = chr(reducedIconCmd)) | (cmdChar = chr(smallIconCmd))
THEN
sizeIcon := sizeIcon DIV 2;
END; { Has either an ICON, a CICN, a reduced icon or a SICN }
GetItemIconSize := sizeIcon;
END; { GetItemIconSize }
Continued in next frame
|
|
Volume Number: | | 7
|
Issue Number: | | 10
|
Column Tag: | | Color Workshop
|
Related Info: Menu Manager Memory Manager Color Quickdraw
Window Menu Bars Revisited (code)
When I insert a hierarchical Menu, I do so after all regular Menus. Also note that I account for the fact that all Menu items #32 and beyond are considered enabled as stipulated by the Menu Manager.
The arithmetic for determining the titleRect for a regular Menu dittos James Matthews algorithm. The titleRect for a hierarchical Menu is simply the rect for the parent item. Weve just seen a picture worth at least two pages of prose ...
Note below that I store menuType and parentID in the wMenuRec RECORD. For a regular Menu, parentID = 0. I need these two pieces of info so I can subsequently determine what Menu type Im looking at and, if a hierarchical Menu, so I can back-calculate to the Menus enclosing frame (see RedrawParentItem within wMenuSelect).
{17}
PROCEDURE wInsertMenu (theMenuBar: wMenuBarHandle; theMenu: MenuHandle;
beforeID: INTEGER);
{ Insert a Menu into a defined wMenuBar: }
VAR
hierID, index, i, j: INTEGER;
found, enabled: BOOLEAN;
parentMenuHdl: MenuHandle;
oldPort: GrafPtr;
cmdChar, parentMark: char;
parentItemRect, parentFrameRect: Rect;
currentHeight, prevHeight, sizeIcon: INTEGER;
BEGIN
IF beforeID = atEnd THEN
BEGIN
i := 0;
WHILE (i < theMenuBar^^.numMenus) & (theMenuBar^^.wMenus[i].menuType
= regMenu) DO
i := i + 1;
IF i < theMenuBar^^.numMenus THEN
{ Insert after end of regular portion of Window Menu Bar, }
{ which occurs just prior to the Hierarchical portion. }
BEGIN
wInsertMenu(theMenuBar, theMenu, theMenuBar^^.wMenus[i].mh^^.menuID);
EXIT(wInsertMenu);
END
ELSE
BEGIN
IF IncreaseSize(theMenuBar) <> noErr THEN
EXIT(wInsertMenu);
titleWidth := GetTitleWidth(theMenu);
END; { ELSE: i = numMenus }
END { beforeID = atEnd }
ELSE IF beforeID = hierMenu THEN
BEGIN
hierID := theMenu^^.menuID;
i := 0;
found := false;
;
{ Scan entire wMenuBar. }
WHILE (i < theMenuBar^^.numMenus) & NOT found DO
BEGIN
parentMenuHdl := theMenuBar^^.wMenus[i].mh;
{ Scan each menu. }
FOR j := 1 TO CountMItems(parentMenuHdl) DO
BEGIN
GetItemCmd(parentMenuHdl, j, cmdChar);
IF cmdChar = char(hMenuCmd) THEN
BEGIN
GetItemMark(parentMenuHdl, j, parentMark);
IF ord(parentMark) = hierID THEN
BEGIN
found := true;
Leave; { FOR loop }
END; { IF ord() }
END; { Hierarchical Menu }
END; { FOR }
i := i + 1; { IF found, i = correct # plus 1. }
END; { WHILE }
IF found THEN { ... is it enabled ??? }
IF j < 32 THEN
enabled := BitTst(@theMenuBar^^.wMenus[i - 1].mh^^.enableFlags,
menuTitleBit - j)
ELSE
enabled := true; { Items 32 & beyond. }
IF found & enabled & (IncreaseSize(theMenuBar) = noErr) THEN
BEGIN
{ tis re-locked by IncreaseSize PROC. }
WITH theMenuBar^^, wMenus[numMenus] DO
BEGIN
mh := theMenu; { Place at end = Hierarchical portion. }
menuType := hierMenu;
parentID := parentMenuHdl^^.menuID;
{ These fields are filled-in later by wMenuSelect }
{ for both Hierarchical and Regular menus: }
{ }
{ menuParentRgn, cumParentRgn and MenuDownOSHdl }
{ }
{ Youll notice that I ignore these fields when }
{ inserting and deleting a Regular menu because }
{ Hierarchical menus are inserted AFTER all Regular }
{ menus and are deleted immediately after use. }
END; { WITH theMenuBar^^, wMenus[numMenus] }
{ titleRect = parent items rect. }
{ }
{ For starters, setup left & right coordinates: }
WITH parentItemRect DO
BEGIN
{ [i-1] belongs to parentMenuHdl. }
WITH theMenuBar^^.wMenus[i - 1] DO
IF menuType = regMenu THEN
left := titleRect.left + menuFrame
ELSE
left := titleRect.right - 4 * menuFrame + menuFrame;
CalcMenuSize(parentMenuHdl);
right := left + parentMenuHdl^^.menuWidth;
END; { WITH parentItemRect }
WITH theMenuBar^^.wMenus[i - 1] DO
IF menuType = regMenu THEN
currentHeight := titleRect.bottom + menuFrame
ELSE
currentHeight := titleRect.top + menuFrame;
FOR index := 1 TO j DO
BEGIN
prevHeight := currentHeight;
sizeIcon := GetItemIconSize(parentMenuHdl, index);
currentHeight := currentHeight + aboveBelowItem + Max(sizeFont,
sizeIcon) + aboveBelowItem;
END; { FOR }
{ Complete the other two dimensions. }
WITH parentItemRect DO
BEGIN
top := prevHeight;
bottom := currentHeight;
END; { WITH parentItemRect }
WITH theMenuBar^^, wMenus[i - 1] DO
BEGIN
{ Change ONLY right & left coords ... }
parentFrameRect := parentItemRect;
WITH parentFrameRect DO
BEGIN
left := left - menuFrame;
right := right + menuFrame + menuShadow;
END; { WITH parentFrameRect }
GetPort(oldPort);
{ ---- }
SetPort(wp);
LocalGlobal(parentFrameRect);
{ WMgrPorts portBits.bounds in global coordinates: }
WITH gScreen DO
BEGIN
IF parentFrameRect.right > right - 2 * menuFrame THEN { Shift
left. }
IF menuType = regMenu THEN
OffsetRect(parentFrameRect, right - 2 * menuFrame - parentFrameRect.right,
0)
ELSE
OffsetRect(parentFrameRect, -(titleRect.right - titleRect.left
- 8 * menuFrame + (parentFrameRect.right - parentFrameRect.left)), 0);
;
IF parentFrameRect.left < left + 2 * menuFrame THEN { Shift
right. }
OffsetRect(parentFrameRect, left + 2 * menuFrame - parentFrameRect.left,
0);
END; { WITH gScreen }
{ Back to local window coordinates. }
GlobalLocal(parentFrameRect);
{ ---- }
SetPort(oldPort);
parentItemRect := parentFrameRect;
WITH parentItemRect DO
BEGIN
left := left + menuFrame;
right := right - menuFrame - menuShadow;
END;
;
{ [numMenus] belongs to inserted sub-Menu: }
wMenus[numMenus].titleRect := parentItemRect;
numMenus := numMenus + 1;
END; { WITH theMenuBar^^, wMenus[i - 1] }
END; { IF found & enabled & IncreaseSize }
EXIT(wInsertMenu);
END { beforeID = hierMenu }
ELSE { Smack in the middle somewhere !! }
BEGIN
IF IncreaseSize(theMenuBar) <> noErr THEN
EXIT(wInsertMenu);
titleWidth := GetTitleWidth(theMenu);
WITH theMenuBar^^ DO
BEGIN
i := 0;
WHILE (i < numMenus) & (wMenus[i].mh^^.menuID <> beforeID) DO
i := i + 1;
;
IF i <> numMenus THEN
BEGIN
FOR j := numMenus DOWNTO (i + 1) DO
BEGIN
wMenus[j].mh := wMenus[j - 1].mh;
wMenus[j].menuType := wMenus[j - 1].menuType;
wMenus[j].parentID := wMenus[j - 1].parentID;
{ Overkill just to copy top & bottom ... }
wMenus[j].titleRect := wMenus[j - 1].titleRect;
{ ... now, adjust right & left if a regular Menu. }
IF wMenus[j].menuType = regMenu THEN
OffsetRect(wMenus[j].titleRect, titleWidth + betweenTitles, 0);
END; { FOR j := numMenus DOWNTO (i + 1) }
END; { IF i <> numMenus }
END; { WITH }
END; { in middle }
{ Some wierd arithmetic ... }
WITH theMenuBar^^, wMenus[i].titleRect DO
BEGIN
top := wp^.portRect.top;
;
bottom := top + mBarHt - menuFrame;
;
left := wp^.portRect.left;
{ Make room for Menu Scroll Activator -- see wDrawMSA: }
IF i = 0 THEN
left := left + 2 * scrollPolyDXY + menuFrame + betweenTitles - invertOverlap
ELSE
left := left + wMenus[i - 1].titleRect.right - invertOverlap + betweenTitles
- invertOverlap;
;
right := left + invertOverlap + titleWidth + invertOverlap;
IF numMenus = 0 THEN { 1st call to wInsertMenu. }
barLength := betweenTitles + titleWidth + betweenTitles
ELSE
barLength := barLength + titleWidth + betweenTitles;
wMenus[i].mh := theMenu;
wMenus[i].menuType := regMenu;
wMenus[i].parentID := mainMenu;
numMenus := numMenus + 1;
END; { WITH }
END; { wInsertMenu }
wDeleteMenu simply reverses the effect of wInsertMenu.
{18}
PROCEDURE wDeleteMenu (theMenuBar: wMenuBarHandle; menuID: INTEGER);
VAR
i, j: INTEGER;
BEGIN
i := 0;
WITH theMenuBar^^ DO
WHILE (i < numMenus) & (wMenus[i].mh^^.menuID <> menuID) DO
i := i + 1; { End of 1st WITH }
;
IF i <> theMenuBar^^.numMenus THEN
BEGIN
IF theMenuBar^^.wMenus[i].menuType = regMenu THEN
titleWidth := GetTitleWidth(theMenuBar^^.wMenus[i].mh);
WITH theMenuBar^^ DO
BEGIN
IF wMenus[i].menuType = regMenu THEN
IF numMenus = 1 THEN { Soon to be zero. }
barLength := 0
ELSE
barLength := barLength - titleWidth - betweenTitles;
FOR j := (i + 1) TO (numMenus - 1) DO
BEGIN
wMenus[j - 1].mh := wMenus[j].mh;
wMenus[j - 1].menuType := wMenus[j].menuType;
wMenus[j - 1].parentID := wMenus[j].parentID;
wMenus[j - 1].titleRect := wMenus[j].titleRect;
IF wMenus[j - 1].menuType = regMenu THEN
OffsetRect(wMenus[j - 1].titleRect, -(titleWidth + betweenTitles),
0);
END; { FOR j := (i + 1) TO (numMenus - 1) }
END; { 2nd WITH }
IF DecreaseSize(theMenuBar) = noErr THEN
;
theMenuBar^^.numMenus := theMenuBar^^.numMenus - 1;
END; { IF i <> theMenuBar^^.numMenus }
END; { wDeleteMenu }
I use GetWBMrects primarily to quantify the rects containing my MSAs which, in turn, play in my DoMouseDown PROC.
{19}
PROCEDURE GetWBMrects (wp: WindowPtr; VAR WBMrect, leftMSArect, rightMSArect:
Rect);
PROCEDURE ZeroRect (VAR r: Rect);
BEGIN
WITH r DO
BEGIN
top := 0;
left := 0;
bottom := 0;
right := 0;
END; { WITH }
END; { ZeroRect }
BEGIN { GetWBMrects }
IF wGetMenuBar(wp) <> NIL THEN
BEGIN
{ + quantifies hasGrowIcon for update Event: }
windType := GetWindowType(wp);
WBMrect := wp^.portRect;
WITH WBMrect DO
BEGIN
IF hasGrowIcon THEN
right := right - (scrollWidth - frame);
bottom := top + mBarHt;
END;
leftMSArect := WBMrect;
WITH leftMSArect DO
right := left + 2 * scrollPolyDXY + menuFrame;
rightMSArect := WBMrect;
WITH rightMSArect DO
left := right - 2 * scrollPolyDXY - menuFrame;
END { window has a Window Bar Menu }
ELSE
BEGIN
ZeroRect(WBMrect);
ZeroRect(leftMSArect);
ZeroRect(rightMSArect);
END; { ELSE }
dynamicBalloons[0].dynamicStrID := 128;
IF windType = rDocProc THEN
dynamicBalloons[0].dynamicStrIndex := 3
ELSE
dynamicBalloons[0].dynamicStrIndex := 6;
dynamicBalloons[0].dynamicR := leftMSArect;
;
dynamicBalloons[1].dynamicStrID := 128;
IF windType = rDocProc THEN
dynamicBalloons[1].dynamicStrIndex := 2
ELSE
dynamicBalloons[1].dynamicStrIndex := 5;
dynamicBalloons[1].dynamicR := WBMrect;
{ Between MSAs:. }
InsetRect(dynamicBalloons[1].dynamicR, leftMSArect.right, 0);
;
dynamicBalloons[2].dynamicStrID := 128;
IF windType = rDocProc THEN
dynamicBalloons[2].dynamicStrIndex := 4
ELSE
dynamicBalloons[2].dynamicStrIndex := 7;
dynamicBalloons[2].dynamicR := rightMSArect;
END; { GetWBMrects }
GetColors, RestoreColors and wSetColorMenu play with color Menus and all three are called by both wDrawMenuBar and wMenuSelect:
{20}
PROCEDURE GetColors;
BEGIN
IF aMac2 THEN
BEGIN
GetBackColor(oldBackColor);
GetForeColor(oldForeColor);
END; { IF }
END; { GetColors }
PROCEDURE RestoreColors;
BEGIN
IF aMac2 THEN
BEGIN
RGBBackColor(oldBackColor);
RGBForeColor(oldForeColor);
END; { IF }
END; { RestoreColors }
PROCEDURE wSetColorMenu (menusID, itemsID: INTEGER);
{ Without redefining Apples built-in MenuDefProc, }
{ were restricting ourselves to ALL Item text }
{ (including the mark & CMD-key) being he same }
{ color and ditto for the background color of ALL }
{ Items. }
VAR
MCTBEntry: MCEntryPtr;
{ What we actually got back from GetMCTBEntry }
{ compared with what we asked for, the latter }
{ being the original parms passed to wSetColorMenu: }
returnedMenuID, returnedMenuItem: INTEGER;
FUNCTION GetMCTBEntry (VAR menuID, itemID: INTEGER): MCEntryPtr;
VAR
mctbEntryPtr: MCEntryPtr;
BEGIN
GetMCTBEntry := NIL; { NOT very optimistic, are we ?!!? }
mctbEntryPtr := GetMCEntry(menuID, itemID);
IF mctbEntryPtr = NIL THEN
{ Could NOT find what we asked for, so recurse }
{ UP the chain: }
{ }
{ if no specified Item entry, then look for }
{ Title entry }
{ if no asked-for Title entry, then look for }
{ MenuBar entry }
{ if no specified MenuBar entry, then our }
{ mctb resource is NOT correct }
BEGIN
IF (menuID <> mceMenuBar) & (itemID <> mceMenuTitle) THEN
BEGIN { Asked for item }
itemID := mceMenuTitle;
GetMCTBEntry := GetMCTBEntry(menuID, itemID);
END
ELSE IF (menuID <> mceMenuBar) & (itemID = mceMenuTitle) THEN
BEGIN { Asked for title }
menuID := mceMenuBar;
itemID := mceMenuBar;
GetMCTBEntry := GetMCTBEntry(menuID, itemID);
END
ELSE IF (menuID = mceMenuBar) & (itemID = mceMenuBar) THEN
; { Ran out of steam !!! }
END
ELSE
GetMCTBEntry := mctbEntryPtr;
END; { GetMCTBEntry }
BEGIN { wSetColorMenu }
IF NOT aMac2 THEN
EXIT(wSetColorMenu);
{ VARed ... what we actually get back from GetMCTBEntry. }
returnedMenuID := menusID;
returnedMenuItem := itemsID;
;
MCTBEntry := GetMCTBEntry(returnedMenuID, returnedMenuItem);
IF MCTBEntry = NIL THEN
EXIT(wSetColorMenu);
WITH MCTBEntry^ DO
IF (returnedMenuID <> mceMenuBar) & (returnedMenuItem <> mceMenuTitle)
THEN
BEGIN { Asked for AND got a Menu Item. }
RGBBackColor(mctRGB4);
RGBForeColor(mctRGB2);
END { an Item element}
ELSE IF (returnedMenuID <> mceMenuBar) & (returnedMenuItem = mceMenuTitle)
THEN
BEGIN
RGBBackColor(mctRGB4);
IF itemsID = mceMenuTitle THEN
{ Asked for AND got a Menu Title. }
RGBForeColor(mctRGB1)
ELSE { Asked for an Item, recursed to a Title. }
RGBForeColor(mctRGB3);
END { a Title element }
ELSE IF (returnedMenuID = mceMenuBar) & (returnedMenuItem = mceMenuBar)
THEN
BEGIN
{ Here, if you ask for the MenuBar entry, I assume }
{ youre just interested in the background color }
{ because youre coloring the overall MenuBar via }
{ _EraseRect. If youre really interested in the }
{ background color of the pulled-down Menu, then }
{ ask for a Menu Title or a Menu Item entry. }
IF menusID = mceMenuBar THEN
{ Got what we asked for !! }
RGBBackColor(mctRGB4)
ELSE IF itemsID = mceMenuTitle THEN
{ Asked for a Title, recursed to MenuBar. }
BEGIN
RGBBackColor(mctRGB2);
RGBForeColor(mctRGB1);
END
ELSE { Asked for an Item, recursed TWICE to MenuBar. }
BEGIN
RGBBackColor(mctRGB2);
RGBForeColor(mctRGB3);
END
END; { got back a MenuBar element}
{ End of WITH }
END; { wSetColorMenu }
Note that I lock theMenuBar passed to wDrawMenuBar, but do NOT have that luxury with wMenuSelect. The reason for the latter is because I recurse however many times it takes to get to the appropriate hierarchical Menu no matter how deep its buried. More on this recursing & cursing later ...
{21}
PROCEDURE wDrawMenuBar (theMenuBar: wMenuBarHandle);
{ Draw a wMenuBar with appropriate highlighting: }
LABEL
100, 200;
VAR
active: BOOLEAN;
y0, i: INTEGER;
lsWBMrect, offBarRect, titleR, visDrawingRect: Rect;
titleRgn: RgnHandle;
{ Keep safe on Stack to avoid Memory Manager blues ... }
safeTitle: Str255;
BEGIN
IF theMenuBar^^.numMenus = 0 THEN { Nada !! }
EXIT(wDrawMenuBar);
mbHState := HGetState(Handle(theMenuBar));
MoveHHi(Handle(theMenuBar));
{ ... because some TRAPs called inside the }
{ WITH block move memory. }
HLock(Handle(theMenuBar));
GetPort(oldPort);
SetPort(theMenuBar^^.wp);
oldClip := NewRgn;
GetClip(oldClip);
;
GetColors;
IF aMac2 THEN
BEGIN
oldMCTable := GetMCInfo;
SetMCInfo(theMenuBar^^.wMCTable);
END; { IF aMac2 }
{ GetWBMrects already called by wDrawMSA. }
lsWBMrect := WBMrect;
LocalGlobal(lsWBMrect);
{ Local window coordinates are input to & local screen }
{ coordinates are returned from CreateOffScreen: }
WITH theMenuBar^^ DO
BEGIN
active := (ORD(WindowPeek(wp)^.hilited) <> 0);
{ offBarRect may end up being wider than windows portRect. }
WITH offBarRect DO
BEGIN
top := WBMrect.top;
bottom := WBMrect.bottom - menuFrame;
left := leftMSArect.right;
right := left + barLength;
IF right < rightMSArect.left THEN
right := rightMSArect.left;
END; { WITH offBarRect }
IF barOSHdl <> NIL THEN
BEGIN
LocalGlobal(offBarRect);
WITH lScreen.bounds DO { _GlobalToLocal }
OffsetRect(offBarRect, left, top);
GOTO 100;
END; { Still there }
barOSHdl := CreateOffScreen(offBarRect);
IF (QuickStrip(Ptr(barOSHdl)) = NIL) | (barOSHdl^^.CreateOffScreenError
<> noErr) THEN
GOTO 200;
{ ELSE draw into off-screen Port as follows: }
TextSize(sizeFont);
TextFont(systemFont);
TextFace([]);
TextMode(srcOr);
{ We canNOT call _GlobalToLocal here because weve }
{ changed the portBits.bounds rectangle of our }
{ off-screen port within CreateOffScreen. }
WITH lScreen.bounds DO { Bounds BEFORE change. }
OffsetRect(lsWBMrect, left, top);
;
ClipRect(offBarRect);
wSetColorMenu(mceMenuBar, mceMenuBar);
EraseRect(offBarRect); { Eliminate all stray matter. }
y0 := (mBarHt - menuFrame - sizeFont) DIV 2;
;
FOR i := 0 TO (numMenus - 1) DO
BEGIN
IF wMenus[i].menuType = hierMenu THEN
Cycle;
titleR := wMenus[i].titleRect;
{ Convert to local screen coordinates: }
OffsetRect(titleR, lsWBMrect.left, lsWBMrect.top);
{Eliminate the overshoot placed there to invert title,}
{because all we wish to do is draw the title string: }
InsetRect(titleR, invertOverlap, 0);
;
MoveTo(titleR.left, titleR.bottom - y0);
wSetColorMenu(wMenus[i].mh^^.menuID, mceMenuTitle);
safeTitle := wMenus[i].mh^^.menuData;
DrawString(safeTitle);
{Gray-out disabled Menu titles ONLY if window is active.}
{We dim the whole Window Bar Menu below if window isnt.}
{The latter allows for CMD-dragging the window. }
IF active & NOT BitTst(@wMenus[i].mh^^.enableFlags, menuTitleBit)
THEN
BEGIN
titleRgn := NewRgn;
RectRgn(titleRgn, titleR);
DimRgn(titleRgn);
DisposeRgn(titleRgn);
END; { Gray-out disabled title }
END; { FOR ... drawing off-screen }
100:
ToOnScreen(barOSHdl); { Back to Square 1. }
WITH barOSHdl^^ DO
BEGIN
visDrawingRect := drawingRect;
ClipRect(drawingRect);
BackColor(whiteColor); { So funny colorization }
ForeColor(blackColor); { does NOT happen !! }
;
{ Adjust if overlapping right MSA. }
WITH visDrawingRect DO
IF right > rightMSArect.left THEN
right := rightMSArect.left;
onScreenRgn := NewRgn;
RectRgn(onScreenRgn, visDrawingRect);
CalcVis(WindowPeek(wp)); { Radius TOM INIT. }
SectRgn(onScreenRgn, wp^.visRgn, onScreenRgn);
CopyBits(offBitMapPtr^, thePort^.portBits, offBarRect, drawingRect,
srcCopy, onScreenRgn);
{ Consider youre CMD-dragging ... }
IF NOT active THEN
DimRgn(onScreenRgn);
ValidRgn(onScreenRgn);
DisposeRgn(onScreenRgn);
END; { WITH barOSHdl^^ }
200:
DisposOffScreen(barOSHdl);
END; { WITH theMenuBar^^ }
{ Deliberately last ... were safe now !! }
HSetState(Handle(theMenuBar), mbHState);
RestoreColors;
IF aMac2 THEN
SetMCInfo(oldMCTable);
;
SetClip(oldClip);
DisposeRgn(oldClip);
SetPort(oldPort);
{ Appropriate highlighting = none. }
wHiliteMenu(theMenuBar, 0);
END; { wDrawMenuBar }
Heres another PROC wherein I need access to my MSA rects. When I scroll, I merely offset the titleRects of the regular Menus titleRects (the hierarchical Menus and their titleRects are NOT present except when Im choosing a Menu item within wMenuSelect). After offseting the titleRects, I redraw the MSAs and then the windows MenuBar ... simple aint it ?!*!?
{22}
PROCEDURE wScrollMenuBar (theMenuBar: wMenuBarHandle);
CONST
toRight = 1;
toLeft = -1;
VAR
mouseLoc: Point;
nbrMenus, direction, temp, deltaScroll, i: INTEGER;
BEGIN
nbrMenus := theMenuBar^^.numMenus;
GetMouse(mouseLoc);
IF PtInRect(mouseLoc, leftMSArect) THEN
direction := toRight
ELSE IF PtInRect(mouseLoc, rightMSArect) THEN
direction := toLeft
ELSE
EXIT(wScrollMenuBar); { Should NOT happen !! }
WHILE WaitMouseUp DO
BEGIN
IF direction = toRight THEN
BEGIN
temp := leftMSArect.right + betweenTitles - invertOverlap - mBar^^.wMenus[0].titleRect.left;
IF temp <= 0 THEN
Leave
ELSE
deltaScroll := Min(StringWidth(A), temp);
END
ELSE { direction = toLeft }
BEGIN
temp := rightMSArect.left - (mBar^^.wMenus[nbrMenus - 1].titleRect.right
- invertOverlap + betweenTitles);
IF temp >= 0 THEN
Leave
ELSE
{ Absolute value. }
deltaScroll := Min(StringWidth(A), -temp);
END;
deltaScroll := direction * deltaScroll;
FOR i := 0 TO (nbrMenus - 1) DO
OffsetRect(theMenuBar^^.wMenus[i].titleRect, deltaScroll, 0);
wDrawMSA(theMenuBar);
wDrawMenuBar(theMenuBar);
END; { WHILE }
END; { wScrollMenuBar }
Get em and weep, I mean draw :
PROCEDURE wGetMSA (theMenuBar: wMenuBarHandle);
VAR
dx, dy, over, back, up, down: INTEGER;
tempPoly: PolyHandle;
BEGIN
WITH WBMrect DO
BEGIN
GetPort(oldPort);
SetPort(theMenuBar^^.wp);
GetWBMrects(theMenuBar^^.wp, WBMrect, leftMSArect, rightMSArect);
{ ----------------------------------------------------------------------
}
{ Get MSA at left of Window Bar Menu: }
dx := 3 * (scrollPolyDXY DIV 2);
dy := (bottom - menuFrame - top - scrollPolyDXY) DIV 2;
over := scrollPolyDXY;
back := -over;
up := -scrollPolyDXY;
down := scrollPolyDXY DIV 2;
IF theMenuBar^^.leftScrollPoly = NIL THEN
BEGIN
{ I cant believe I did this ?!*!? }
{ theMenuBar^^.leftScrollPoly := OpenPoly; }
tempPoly := OpenPoly;
theMenuBar^^.leftScrollPoly := tempPoly;
MoveTo(left + dx, bottom - menuFrame - dy);
Line(0, up); { Vertical part ... }
Line(back, down); { ... horizontal parts: }
Line(over, down);
ClosePoly;
END; { Doesnt exist, so make a new one }
{ ------------------------------------------------------------------------
}
{ Get MSA at right of Window Bar Menu: }
IF theMenuBar^^.rightScrollPoly = NIL THEN
BEGIN
tempPoly := OpenPoly;
theMenuBar^^.rightScrollPoly := tempPoly;
MoveTo(right - dx, bottom - menuFrame - dy);
Line(0, up); { Vertical part ... }
Line(over, down); { ... horizontal parts: }
Line(back, down);
ClosePoly;
END; { IF }
SetPort(oldPort);
END; { WITH WBMrect }
END; { wGetMSA }
PROCEDURE wDrawMSA (theMenuBar: wMenuBarHandle);
{ ... AND the bottom line bordering the Window Bar Menu. }
LABEL
100;
VAR
dx, dy: INTEGER;
aux: BOOLEAN;
frameColor: RGBColor;
BEGIN
WITH WBMrect DO
BEGIN
GetPort(oldPort);
SetPort(theMenuBar^^.wp);
GetPenState(pnState);
{ ------------------------------------------------------------------------------------------------------------
}
{ Get the Window Bar Menu rect & draw a line beneath it: }
wGetMSA(theMenuBar); { Calls GetWBMrects. }
gWBMrect := WBMrect;
LocalGlobal(gWBMrect);
IF NOT SectRect(gWBMrect, gScreen, visRect) THEN
GOTO 100;
GlobalLocal(visRect);
onScreenRgn := NewRgn;
RectRgn(onScreenRgn, visRect);
SectRgn(onScreenRgn, theMenuBar^^.wp^.visRgn, onScreenRgn);
oldClip := NewRgn;
GetClip(oldClip);
SetClip(onScreenRgn);
;
aux := GetWindowPartColor(theMenuBar^^.wp, wFrameColor, frameColor);
IF (colorDepth > 1) & aux THEN
BEGIN
GetForeColor(oldForeColor);
RGBForeColor(frameColor);
END; { Draw in color }
;
PenNormal;
PenSize(menuFrame, menuFrame);
MoveTo(left, bottom - menuFrame);
Line(right - left, 0);
{ ---------------------------------------------------------------- }
{ Draw MSA at left of Window Menu: }
IF theMenuBar^^.wMenus[0].titleRect.left < leftMSArect.right + betweenTitles
- invertOverlap THEN
FillPoly(theMenuBar^^.leftScrollPoly, black)
ELSE
BEGIN
FillPoly(theMenuBar^^.leftScrollPoly, white);
FramePoly(theMenuBar^^.leftScrollPoly);
END;
{ ------------------------------------------------------------------------------------------------------
}
{ Draw vertical line separating MSA from Menu titles: }
MoveTo(left + 2 * scrollPolyDXY, bottom - menuFrame);
Line(0, -(bottom - top - menuFrame));
{ --------------------------------------------------------------------------
}
{ Draw MSA at right of Window Bar Menu: }
IF theMenuBar^^.wMenus[theMenuBar^^.numMenus - 1].titleRect.right
- invertOverlap + betweenTitles > rightMSArect.left THEN
FillPoly(theMenuBar^^.rightScrollPoly, black)
ELSE
BEGIN
FillPoly(theMenuBar^^.rightScrollPoly, white);
FramePoly(theMenuBar^^.rightScrollPoly);
END;
;
MoveTo(right - 2 * scrollPolyDXY - menuFrame, bottom - menuFrame);
{ + vertical }
Line(0, -(bottom - top - menuFrame)); { line. }
IF ORD(WindowPeek(theMenuBar^^.wp)^.hilited) = 0 THEN
DimRgn(onScreenRgn);
ValidRgn(onScreenRgn);
DisposeRgn(onScreenRgn);
SetClip(oldClip);
DisposeRgn(oldClip);
IF (colorDepth > 1) & aux THEN
RGBForeColor(oldForeColor);
100:
SetPenState(pnState);
SetPort(oldPort);
ValidRect(visRect);
END; { WITH }
END; { wDrawMSA }
Kill the polyHandles. Note that barOSHdl is always = NIL because immediately after I create this OffScreenRecHdl within wDrawMenuBar and subsequently bit it back on-screen, I then call DisposOffscreen.
{23}
PROCEDURE wClearMenuBar (theMenuBar: wMenuBarHandle);
{ Called internally by wDeleteWMB to }
{ kiss some Handles goodbye. }
BEGIN
mbHState := HGetState(Handle(theMenuBar));
MoveHHi(Handle(theMenuBar));
HLock(Handle(theMenuBar));
WITH theMenuBar^^ DO
BEGIN
wHiliteMenu(theMenuBar, 0);
{ So sue me ... Im paranoid !! }
IF leftScrollPoly <> NIL THEN
BEGIN
KillPoly(leftScrollPoly);
leftScrollPoly := NIL; { Mark as gone ... }
END;
;
IF rightScrollPoly <> NIL THEN
BEGIN
KillPoly(rightScrollPoly);
rightScrollPoly := NIL;
END;
IF barOSHdl <> NIL THEN
DisposOffScreen(barOSHdl); { Sets barOSHdl := NIL }
END; { WITH }
HSetState(Handle(theMenuBar), mbHState);
END; { wClearMenuBar }
Now, the recursing & cursing ...
The local routine ItDoesFit is used when youve dragged the window close to the screens phycical bottom edge, not leaving enough room to draw at least three items if theres more than three to begin with.
GetParentMenuInfo is used to quantify the index for both RgnHandles, cumParentRgn & menuParentRgn, when dealing with hierarchical Menus.
See the intro comments for RedrawParentItem for its purpose in life.
wMenuSelect is no longer the nightmare it initially was, thanks to Ben Cranston. Initially I had locked the passed wMenuBarHandle & surrounded darn near the entire PROC with a WITH theMenuBar^^. But then, with recursion for a hierarchical Menu, I called wInsertMenu which immediately unlocked wMenuBarHandle & called MoveHHi. Oh where, o where did the handle go ... thanks Ben !!!!!
{24}
FUNCTION wMenuSelect (theMenuBar: wMenuBarHandle; startPt: Point): LONGINT;
{ Pull down the Menus and let the user select an Item: }
{ NOTE -- startPt is in Global coordinates, but }
{ ONLY to be compatible with _MenuSelect. }
LABEL
100, 200;
CONST
TopMenuItemLoc = $A0A;
AtMenuBottomLoc = $A0C;
MenuFlashAddr = $A24;
HiliteMode = $938;
VAR
oldPort: GrafPtr; { Many Locals because of recursion }
oldClip: RgnHandle;
oldForeColor, oldBackColor: RGBColor;
screenBounds, betweenMSAs, visAllTitles, visThisTitle, visString:
Rect;
menuRect, menuFrameRect, lsMenuRect, lsWBMrect, lsTitleRect: Rect;
lStartPt, hierPt, NILPt: Point;
itemsHeight, i, j, blink: INTEGER;
saveScrollInfo: LONGINT;
menuFlashP: wordPtr;
strayed: BOOLEAN;
cmdChar, itemMark: char;
hierMenuHdl: MenuHandle;
origMCTable, parentMCTable, hierMCTable: MCTableHandle;
hierSelect: LONGINT;
onCScreen: CGrafPort;
onCScreenPtr: CGrafPtr;
onBWScreen: GrafPort;
onBWScreenPtr: GrafPtr;
MenuDownOSHdl: OffScreenRecHdl;
{ Murphys Memory Manager ... }
tempRgn1, tempRgn2, tempRgn3: RgnHandle;
FUNCTION ItDoesFit (theMenu: MenuHandle; maxMenuHeight: INTEGER; VAR
cumMenuHeight: INTEGER): BOOLEAN;
{ Get maximum cum height that can fit }
{ within the specified max height. }
VAR
prevMenuHeight, index, sizeItemIcon: INTEGER;
BEGIN
ItDoesFit := true; { Be upbeat !! }
IF theMenu^^.menuHeight <= maxMenuHeight THEN
BEGIN
cumMenuHeight := theMenu^^.menuHeight;
EXIT(ItDoesFit);
END;
cumMenuHeight := 0; { For starters ... }
FOR index := 1 TO CountMItems(theMenu) DO
BEGIN
prevMenuHeight := cumMenuHeight;
sizeItemIcon := GetItemIconSize(theMenu, index);
cumMenuHeight := cumMenuHeight + aboveBelowItem + Max(sizeFont, sizeItemIcon)
+ aboveBelowItem;
IF cumMenuHeight > maxMenuHeight THEN
Leave; { FOR loop }
END; { FOR }
IF index - 1 < 3 THEN
ItDoesFit := false
ELSE IF sizeItemIcon = 0 THEN
{ ... of first item NOT shown. }
cumMenuHeight := prevMenuHeight
ELSE
{ Prevents drawing of the top pixels of }
{ any icon in first item NOT shown: }
cumMenuHeight := prevMenuHeight - aboveBelowItem;
END; { ItDoesFit }
FUNCTION GetParentMenuInfo (theMenuBar: wMenuBarHandle; hierMenuNbr:
INTEGER): LONGINT;
{ Returns parent menu # in Hi word and }
{ parent item # in the low word: }
{ NEVER say it can NEVER fail !!! }
VAR
hierID, parentMenuNum, j: INTEGER;
parentMenuHdl: MenuHandle;
cmdChar, parentMark: char;
temp: LONGINT;
BEGIN
WITH theMenuBar^^ DO
BEGIN
parentMenuNum := 0;
WHILE (parentMenuNum < numMenus) & (wMenus[hierMenuNbr].parentID
<> wMenus[parentMenuNum].mh^^.menuID) DO
parentMenuNum := parentMenuNum + 1;
IF parentMenuNum = numMenus THEN
{ It better NOT be !!! }
;
hierID := wMenus[hierMenuNbr].mh^^.menuID;
parentMenuHdl := wMenus[parentMenuNum].mh;
END; { WITH }
FOR j := 1 TO CountMItems(parentMenuHdl) DO
BEGIN
GetItemCmd(parentMenuHdl, j, cmdChar);
IF cmdChar = char(hMenuCmd) THEN
BEGIN
GetItemMark(parentMenuHdl, j, parentMark);
IF ord(parentMark) = hierID THEN
Leave; { FOR loop }
END;
END; { FOR }
temp := parentMenuNum;
temp := BitShift(temp, 16) + j;
GetParentMenuInfo := temp;
END; { GetParentMenuInfo }
PROCEDURE RedrawParentItem (theMenuBar: wMenuBarHandle; hierMenuNbr:
INTEGER);
{ Apples MenuDefProc does NOT call _InverRect for a }
{ color Mac, but rather _EraseRect followed by a }
{ redraw of the Menu item with reversed background }
{ and hilite colors. As you can see below, however, }
{ I am prone to cheating: }
{ CONST }
{ TopMenuItemLoc = $A0A; }
{ AtMenuBottomLoc = $A0C; }
VAR
saveScrollInfo: LONGINT; { MUST be local !!! }
parentMenuInfo: LONGINT;
parentMenuNum, redrawitem: INTEGER;
redrawPt: Point;
parentMenuHdl: MenuHandle;
parentMenuRect: Rect;
BEGIN
IF NOT aMac2 THEN
EXIT(RedrawParentItem);
parentMenuInfo := GetParentMenuInfo(theMenuBar, hierMenuNbr);
parentMenuNum := HiWord(parentMenuInfo);
redrawItem := LoWord(parentMenuInfo);
WITH theMenuBar^^.wMenus[parentMenuNum] DO
BEGIN
parentMenuHdl := mh;
parentMenuRect := menuParentRgn^^.rgnBBox;
END;
ClipRect(parentMenuRect);
WITH lScreen.bounds DO { LocalToGlobal }
OffsetRect(parentMenuRect, -left, -top);
SetPt(redrawPt, 0, 0);
saveScrollInfo := longPtr(TopMenuItemLoc)^;
wordPtr(TopMenuItemLoc)^ := parentMenuRect.top;
wordPtr(AtMenuBottomLoc)^ := parentMenuRect.bottom;
MenuDefGlue(mChooseMsg, parentMenuHdl, parentMenuRect, redrawPt, redrawItem);
longPtr(TopMenuItemLoc)^ := saveScrollInfo;
END; { RedrawParentItem }
BEGIN { wMenuSelect }
{ Note that starting here, we sure could use }
{ WITH theMenuBar^^ & WITH wMenus[i] statements }
{ for shorthand. However, wed then need to call }
{ HLock. This would be okay except when we later }
{ implement Hierarchical menus via a recursive to }
{ ourself (wMenuSelect). This unnecessarily }
{ keeps a locked Handle around for a long time. }
{ [ See The Secret Life of the Memory Manager }
{ by Richard Clark n develop, Volume 1, }
{ Issue 2, April 1990 ] }
{ In addition, for Hierarchical menus, we call }
{ wInsertMenu and wDeleteMenu which in turn call }
{ the IncreaseSize and DecreaseSize PROCs. These }
{ latter PROCs move theMenuBar because we need }
{ to temporarily unlock this handle therefore }
GetPort(oldPort);
SetPort(theMenuBar^^.wp);
oldClip := NewRgn;
GetClip(oldClip);
;
IF aMac2 THEN
BEGIN
{ GetColors is local due to recursion. }
GetBackColor(oldBackColor);
GetForeColor(oldForeColor);
origMCTable := GetMCInfo;
SetMCInfo(theMenuBar^^.wMCTable);
END; { IF aMac2 }
;
menuFlashP := wordPtr(MenuFlashAddr);
WHILE WaitMouseUp DO
BEGIN
whichItem := 0; { May NOT be in a titleRect. }
{ Find the Menu title that user is selecting. }
i := theMenuBar^^.numMenus - 1;
GlobalToLocal(startPt); { Local window coordinates. }
WHILE (i >= 0) & NOT PtInRect(startPt, theMenuBar^^.wMenus[i].titleRect)
DO
i := i - 1;
IF i >= 0 THEN { Have Menu drop ... }
BEGIN
lsTitleRect := theMenuBar^^.wMenus[i].titleRect;
;
IF theMenuBar^^.wMenus[i].menuType = regMenu THEN
BEGIN
{ Otherwise strayed = TRUE momentarily as you hit the }
{ line bordering titleRect & menuRect in the process }
{ of pulling down a MENU (see below): }
lsTitleRect.bottom := lsTitleRect.bottom + menuFrame;
{ ... AND if the Window Menu Bar is scrolled ... }
{ dont forget about the invertOverlap arithmetic }
{ which would enable the Mouse to be over the expanded }
{ titleRect WITHOUT the first/last title }
{ character being visible. The equations below avoid }
{ this happenstance. In addition, menuFrame is used }
{ to account for a character width > an image width as }
{ well as a character origin > 0. }
screenBounds := gScreen; { = GrayRgns rgnBBox. }
GlobalLocal(screenBounds);
betweenMSAs := WBMrect;
InsetRect(betweenMSAs, leftMSArect.right, 0);
IF NOT SectRect(screenBounds, betweenMSAs, visAllTitles) THEN
GOTO 100; { Will NOT happen !! }
IF NOT SectRect(visAllTitles, lsTitleRect, visThisTitle) THEN
GOTO 100; { Ditto !! }
visString := visThisTitle;
WITH visString DO
BEGIN
IF (leftMSArect.right > right - invertOverlap - 2 * menuFrame)
| (screenBounds.left > right - invertOverlap - 2 * menuFrame) THEN
right := right - invertOverlap - 2 * menuFrame;
;
IF (rightMSArect.left < left + invertOverlap + 2 * menuFrame) |
(screenBounds.right < left + invertOverlap + 2 * menuFrame) THEN
left := left + invertOverlap + 2 * menuFrame;
END; { WITH visString }
IF EmptyRect(visString) THEN
GOTO 100;
END; { a regular Menu }
LocalGlobal(lsTitleRect);
{ Needed for vertical scrolling: }
lsWBMrect := WBMrect;
{ ... or rightMSArect.left: }
InsetRect(lsWBMrect, leftMSArect.right, 0);
LocalGlobal(lsWBMrect);
LocalToGlobal(startPt);
CalcMenuSize(theMenuBar^^.wMenus[i].mh);
WITH theMenuBar^^.wMenus[i], titleRect DO
IF menuType = regMenu THEN
SetRect(menuFrameRect, left, bottom, left + menuFrame + mh^^.menuWidth
+ frameShad, bottom + menuFrame + mh^^.menuHeight + frameShad)
ELSE
SetRect(menuFrameRect, right - 4 * menuFrame, top, right - 4 *
menuFrame + menuFrame + mh^^.menuWidth + frameShad, top + menuFrame +
mh^^.menuHeight + frameShad);
{ If Menu overlaps the screens edges, }
{ trim and/or shift it: }
LocalGlobal(menuFrameRect);
WITH gScreen DO
BEGIN
IF menuFrameRect.bottom > bottom - 2 * menuFrame THEN
menuFrameRect.bottom := bottom - 2 * menuFrame;
;
IF menuFrameRect.right > right - 2 * menuFrame THEN
{ Shift left. }
IF theMenuBar^^.wMenus[i].menuType = regMenu THEN
OffsetRect(menuFrameRect, right - 2 * menuFrame - menuFrameRect.right,
0)
ELSE
OffsetRect(menuFrameRect, -(theMenuBar^^.wMenus[i].titleRect.right
- theMenuBar^^.wMenus[i].titleRect.left - 8 * menuFrame + (menuFrameRect.right
- menuFrameRect.left)), 0);
;
IF menuFrameRect.left < left + 2 * menuFrame THEN
{ Shift right. }
OffsetRect(menuFrameRect, left + 2 * menuFrame - menuFrameRect.left,
0);
END; { WITH gScreen }
GlobalLocal(menuFrameRect); { --> local window coords. }
WITH menuFrameRect DO
IF ItDoesFit(theMenuBar^^.wMenus[i].mh, bottom - top - menuFrame
- frameShad, itemsHeight) THEN
bottom := top + menuFrame + itemsHeight + frameShad
ELSE IF theMenuBar^^.wMenus[i].menuType = regMenu THEN
BEGIN { Damn SysBeep drove me nuts/nuttier !!! }
wHiliteMenu(theMenuBar, theMenuBar^^.wMenus[i].mh^^.menuID);
Delay(flashDelay * 2, finalTicks);
wHiliteMenu(theMenuBar, 0);
Delay(flashDelay * 2, finalTicks);
GOTO 100;
END
ELSE
BEGIN
{ From Open(C)Port on the previous go-around. }
SetPort(oldPort);
WITH lScreen.bounds DO
OffsetRect(lsTitleRect, left, top); { GlobalToLocal }
Delay(flashDelay * 2, finalTicks);
IF aMac2 THEN
{ i = Hierarchical Menu # }
RedrawParentItem(theMenuBar, i)
ELSE
BEGIN
ClipRect(lsTitleRect);
InvertRect(lsTitleRect);
END;
Delay(flashDelay * 2, finalTicks);
{ So we can reset its clipRgn on exit: }
SetPort(theMenuBar^^.wp);
Leave;
END; { does NOT fit & WITH menuFrameRect }
{ MenuDefProcs mChooseMsg handles hiliting }
{ for a Hierarchical menu item: }
IF theMenuBar^^.wMenus[i].menuType = regMenu THEN
wHiliteMenu(theMenuBar, theMenuBar^^.wMenus[i].mh^^.menuID);
MenuDownOSHdl := CreateOffScreen(menuFrameRect);
IF (QuickStrip(Ptr(MenuDownOSHdl)) = NIL) | (MenuDownOSHdl^^.CreateOffScreenError
<> noErr) THEN
BEGIN
DisposOffScreen(MenuDownOSHdl);
GOTO 200;
END; { Whoops !! }
ClipRect(menuFrameRect); { Draw off-screen ... }
EraseRect(menuFrameRect); { Eliminate all stray matter. }
{ An alternate ToOnScreen so our Menu can overlap }
{ the windows boundaries rather than be confined }
{ to just its portRect: }
IF aMac2 THEN
BEGIN
SetGDevice(MenuDownOSHdl^^.oldDevice);
onCScreenPtr := @onCScreen;
OpenCPort(onCScreenPtr);
{ For multiple screens ... }
RectRgn(onCScreenPtr^.visRgn, gScreen);
onCScreenPtr^.portRect := gScreen;
END { IF aMac2 }
ELSE
BEGIN
onBWScreenPtr := @onBWScreen;
OpenPort(onBWScreenPtr);
RectRgn(onBWScreenPtr^.visRgn, gScreen);
onBWScreenPtr^.portRect := gScreen;
END; { ELSE = Yucky black-and-white }
ClipRect(menuFrameRect);
{ Save current screen image: }
BackColor(whiteColor);
ForeColor(blackColor);
CopyBits(thePort^.portBits, MenuDownOSHdl^^.offBitMapPtr^, menuFrameRect,
menuFrameRect, srcCopy, NIL);
TextSize(sizeFont);
TextFont(systemFont);
TextFace([]);
TextMode(srcOr);
{ NOW, the magic ~~ Mike Shuster, MacTutor <Dec, 85> }
menuRect := menuFrameRect;
WITH menuRect DO
BEGIN { Draw frame, then shadow. }
GetPenState(pnState);
PenNormal;
right := right - menuShadow;
bottom := bottom - menuShadow;
wSetColorMenu(theMenuBar^^.wMenus[i].mh^^.menuID, mceMenuTitle);
EraseRect(menuRect); { Inside shadow. }
PenSize(menuFrame, menuFrame);
FrameRect(menuRect);
{ Inside frame AND shadow: }
InsetRect(menuRect, menuFrame, menuFrame);
;
MoveTo(left + menuShadow, bottom + menuFrame);
PenSize(menuShadow, menuShadow);
Line((right - left + menuFrame), 0);
MoveTo(right + menuFrame, bottom + frameShad);
Line(0, -(bottom - top + menuFrame));
SetPenState(pnState);
END; { WITH menuRect }
lsMenuRect := menuRect; { Save for later ... }
WITH lScreen DO
BEGIN
OffsetRect(menuRect, -left, -top); { LocalToGlobal }
OffsetRect(lsTitleRect, left, top); { GlobalToLocal }
OffsetRect(lsWBMrect, left, top);
END; { WITH lScreen }
{ ... or parentID = mainMenu. }
IF theMenuBar^^.wMenus[i].menuType = regMenu THEN
BEGIN
tempRgn1 := NewRgn;
RectRgn(tempRgn1, lsWBMrect);
theMenuBar^^.wMenus[i].cumParentRgn := tempRgn1;
tempRgn2 := NewRgn;
RectRgn(tempRgn2, lsMenuRect);
theMenuBar^^.wMenus[i].menuParentRgn := tempRgn2;
END
ELSE
BEGIN
{ i = Hierarchical Menu # }
j := HiWord(GetParentMenuInfo(theMenuBar, i));
tempRgn1 := NewRgn;
tempRgn2 := theMenuBar^^.wMenus[j].cumParentRgn;
tempRgn3 := theMenuBar^^.wMenus[j].menuParentRgn;
UnionRgn(tempRgn2, tempRgn3, tempRgn1);
theMenuBar^^.wMenus[i].cumParentRgn := tempRgn1;
tempRgn2 := NewRgn;
RectRgn(tempRgn2, lsMenuRect);
theMenuBar^^.wMenus[i].menuParentRgn := tempRgn2;
END;
{ Start fresh with each scrollable Menu: }
saveScrollInfo := longPtr(TopMenuItemLoc)^;
wordPtr(TopMenuItemLoc)^ := menuRect.top;
wordPtr(AtMenuBottomLoc)^ := menuRect.bottom;
{ I encountered a few cosmetic problems with Apples }
{ MenuDefProc. }
{ 1) the disabled dotted line will overwrite the right }
{ border of the frame. }
{ 2) if there is an icon immediately below the last }
{ item that shows in a scrolling menu, the bottom }
{ border of the frame is overwritten by a portion }
{ of said icon. }
{ The call to ClipRect solves both: }
ClipRect(lsMenuRect);
{ whichItem := 0; -- at beginning of WHILE WaitMouseUp DO }
MenuDefGlue(mDrawMsg, theMenuBar^^.wMenus[i].mh, menuRect, startPt,
whichItem);
strayed := false; { = Mouse on Menu Item OR on Menu }
{ title OR NOT on another Menu. }
WHILE WaitMouseup & NOT strayed DO
{ While still selecting in this Menu ... }
BEGIN
GetMouse(startPt);
lStartPt := startPt;
{ LocalToGlobal }
SubPt(lScreen.topLeft, startPt);
MenuDefGlue(mChooseMsg, theMenuBar^^.wMenus[i].mh, menuRect, startPt,
whichItem);
IF whichItem <> 0 THEN { Item is enabled. }
BEGIN
GetItemCmd(theMenuBar^^.wMenus[i].mh, whichItem, cmdChar);
IF cmdChar = char(hMenuCmd) THEN { a sub Menu ... }
BEGIN
IF aMac2 THEN
parentMCTable := theMenuBar^^.wMCTable;
hierPt := startPt;
GetItemMark(theMenuBar^^.wMenus[i].mh, whichItem, itemMark);
hierMenuHdl := GetMenu(ord(itemMark));
wInsertMenu(theMenuBar, hierMenuHdl, hierMenu);
DetachResource(Handle(hierMenuHdl));
IF aMac2 THEN
BEGIN
hierMCTable := GetMCInfo;
SetMCInfo(parentMCTable);
theMenuBar^^.wMCTable := hierMCTable;
END; { IF aMac2 }
hierSelect := wMenuSelect(theMenuBar, hierPt);
wDeleteMenu(theMenuBar, ord(itemMark));
;
whichMenu := HiWord(hierSelect);
whichItem := LoWord(hierSelect);
IF whichItem <> 0 THEN
{ So a parent Menu Item does NOT blink: }
BEGIN
strayed := true;
Leave;
END;
END
ELSE { a regular Menu }
whichMenu := theMenuBar^^.wMenus[i].mh^^.menuID;
END; { enabled Item }
strayed := NOT PtInRgn(lStartPt, theMenuBar^^.wMenus[i].menuParentRgn)
& NOT PtInRect(lStartPt, lsTitleRect) & PtInRgn(lStartPt, theMenuBar^^.wMenus[i].cumParentRgn);
END; { WHILE WaitMouseup & NOT strayed }
IF (whichItem <> 0) & NOT strayed THEN
FOR blink := 1 TO menuFlashP^ DO
BEGIN
SetPt(NILPt, 0, 0);
MenuDefGlue(mChooseMsg, theMenuBar^^.wMenus[i].mh, menuRect, NILPt,
whichItem);
Delay(flashDelay DIV 4, finalTicks);
MenuDefGlue(mChooseMsg, theMenuBar^^.wMenus[i].mh, menuRect, startPt,
whichItem);
Delay(flashDelay DIV 4, finalTicks);
END; { blinking the selected item }
ClipRect(menuFrameRect);
BackColor(whiteColor);
ForeColor(blackColor);
{ Blit back original screen image. For a Hierarchical menu }
{ item, the off-screen bitMap/pixMap will include a part of }
{ the inverted parent item. Therefore, we must first blit }
{ back and THEN re-invert !!! }
CopyBits(MenuDownOSHdl^^.offBitMapPtr^, thePort^.portBits, menuFrameRect,
menuFrameRect, srcCopy, NIL);
{ Un-hilite the title: }
IF theMenuBar^^.wMenus[i].menuType = regMenu THEN
wHiliteMenu(theMenuBar, 0)
ELSE IF aMac2 THEN
{ i = Hierarchical Menu # }
RedrawParentItem(theMenuBar, i)
ELSE
BEGIN
ClipRect(lsTitleRect);
InvertRect(lsTitleRect);
END;
IF aMac2 THEN
CloseCPort(onCScreenPtr)
ELSE
ClosePort(onBWScreenPtr);
SetPort(theMenuBar^^.wp);
{ Original menuFrameRect in window coordinates: }
ValidRect(MenuDownOSHdl^^.drawingRect);
DisposOffScreen(MenuDownOSHdl);
DisposeRgn(theMenuBar^^.wMenus[i].menuParentRgn);
DisposeRgn(theMenuBar^^.wMenus[i].cumParentRgn);
longPtr(TopMenuItemLoc)^ := saveScrollInfo;
IF theMenuBar^^.wMenus[i].menuType = hierMenu THEN
Leave;
END { IF i >= 0 }
ELSE { User is not over a Menu. }
;
100:
GetMouse(startPt);
LocalToGlobal(startPt);
END; { WHILE WaitMouseUp }
IF whichItem = 0 THEN
wMenuSelect := 0
ELSE
wMenuSelect := BitShift(whichMenu, 16) + whichItem;
200:
IF aMac2 THEN
BEGIN
RGBBackColor(oldBackColor); { SetColors is local }
RGBForeColor(oldForeColor); { due to recursion. }
SetMCInfo(origMCTable);
END; { aMac2 }
;
SetClip(oldClip);
DisposeRgn(oldClip);
SetPort(oldPort);
END; { wMenuSelect }
Theres nothing super different in wMenuKey from Jim Matthews code except I utilized GetItemCmd to shorten it a tad.
{25}
FUNCTION wMenuKey (theMenuBar: wMenuBarHandle; ch: char): LONGINT;
VAR
i, j, whichMenu, whichItem: INTEGER;
found, enabled: BOOLEAN;
cmdChar: char;
FUNCTION equalChars (c1, c2: char): BOOLEAN;
{ Filter out difference between UPPER and lower case: }
BEGIN
IF c1 IN [a..z] THEN
c1 := char(ord(c1) + ord(A) - ord(a));
IF c2 IN [a..z] THEN
c2 := char(ord(c2) + ord(A) - ord(a));
equalChars := (c1 = c2);
END; { equalChars }
BEGIN { wMenuKey }
mbHState := HGetState(Handle(theMenuBar));
MoveHHi(Handle(theMenuBar));
HLock(Handle(theMenuBar));
found := false;
i := 0;
WITH theMenuBar^^ DO
BEGIN
WHILE (NOT found) & (i < numMenus) DO
BEGIN
WITH wMenus[i] DO
FOR j := 1 TO CountMItems(mh) DO
BEGIN
GetItemCmd(mh, j, cmdChar);
IF equalChars(cmdChar, ch) THEN
BEGIN
found := true;
whichMenu := mh^^.menuID;
whichItem := j;
Leave; { FOR loop }
END; { IF equalChars }
END; { FOR scanning each individual Menu & }
{ WITH wMenus[i] }
i := i + 1; { IF found, i = correct # plus 1. }
END; { WHILE scanning each whole Menu }
IF found THEN
BEGIN
{ The Item is enabled if both it AND }
{ its Menu title are enabled: }
IF j < 32 THEN
enabled := BitTst(@wMenus[i - 1].mh^^.enableFlags, menuTitleBit
- j)
ELSE
enabled := true; { Items 32 & beyond. }
enabled := enabled & BitTst(@wMenus[i - 1].mh^^.enableFlags, menuTitleBit);
END; { IF found }
END; { WITH theMenuBar^^ }
HSetState(Handle(theMenuBar), mbHState);
IF found & enabled THEN
BEGIN
wHiliteMenu(theMenuBar, whichMenu);
Delay(flashDelay, finalTicks);
wHiliteMenu(theMenuBar, 0);
wMenuKey := BitShift(whichMenu, 16) + whichItem;
END { IF found & enabled }
ELSE
wMenuKey := 0;
END; { wMenuKey }
The prolific comments tell the whole gory story ...
{26}
PROCEDURE wHiliteMenu (theMenuBar: wMenuBarHandle; menuID: INTEGER);
{ NOTE that this PROC is used only for a regular Menu item. }
{ Apples MenuDefProc handles a hierarchical Menu item. }
{ For a regular Menu item, we invert the portion of the }
{ titleRect that is both totally visible on the screen and }
{ between the two MSAs. }
CONST
{ What happened to a simple InvertRect ?!!? }
HiliteMode = $938;
VAR
origPort: GrafPtr;
origClip: RgnHandle;
i: INTEGER;
screenBounds, betweenMSAs, visAllTitles, visThisTitle, visString:
Rect;
{ Gotta be local to avoid interference }
{ with the global = mbHState: }
lmbHState: SignedByte;
oldMCTable: MCTableHandle; { Color stuff ... }
oldForeColor, oldBackColor, hiliteForeColor, hiliteBackColor: RGBColor;
oldTxSize, oldTxFont, oldTxMode: INTEGER;
oldTxStyle: Style;
BEGIN
{ Needed because wHiliteMenu is called with pressing }
{ a CMD-key whereupon a window may not be around. }
IF FrontWindow = NIL THEN
EXIT(wHiliteMenu);
lmbHState := HGetState(Handle(theMenuBar));
IF NOT BitTst(@lmbHState, 0) THEN
BEGIN
MoveHHi(Handle(theMenuBar));
HLock(Handle(theMenuBar));
END; { NOT already locked !! }
WITH theMenuBar^^ DO
BEGIN
GetPort(origPort);
SetPort(wp);
origClip := NewRgn;
GetClip(origClip);
;
{ Just as with wMenuSelect PROC because wMenuKey calls us. }
oldTxSize := thePort^.txSize;
oldTxFont := thePort^.txFont;
oldTxStyle := thePort^.txFace;
oldTxMode := thePort^.txMode;
TextSize(sizeFont);
TextFont(systemFont);
TextFace([]);
TextMode(srcOr);
;
IF aMac2 THEN
BEGIN
GetForeColor(oldForeColor);
GetBackColor(oldBackColor);
oldMCTable := GetMCInfo;
SetMCInfo(theMenuBar^^.wMCTable);
END;
screenBounds := gScreen;
GlobalLocal(screenBounds);
betweenMSAs := WBMrect;
{ ... or rightMSArect.left: }
InsetRect(betweenMSAs, leftMSArect.right, 0);
IF SectRect(screenBounds, betweenMSAs, visAllTitles) THEN
BEGIN
{ Un-highlight previously highlighted Menu title by }
{ re-inverting it or re-drawing it if on a machine }
{ with Color QuickDraw. This is similiar to the }
{ gyrations within Apples MenuDefProc. The latter }
{ does NOT call _InverRect for a color Mac, but }
{ rather _EraseRect followed by a redraw of the }
{ Menu item. }
IF titleHilited <> noneHilited THEN
BEGIN
WITH wMenus[titleHilited], titleRect DO
BEGIN
IF SectRect(visAllTitles, titleRect, visThisTitle) THEN
BEGIN { ALWAYS true if hilited. }
ClipRect(visThisTitle);
IF aMac2 THEN { See wDrawMenuBar PROC. }
BEGIN
wSetColorMenu(mceMenuBar, mceMenuBar);
EraseRect(visThisTitle);
MoveTo(left + invertOverlap, bottom - (mBarHt - menuFrame - sizeFont)
DIV 2);
wSetColorMenu(mh^^.menuID, mceMenuTitle);
DrawString(mh^^.menuData);
END
ELSE
InvertRect(visThisTitle);
ValidRect(visThisTitle);
END; { IF SectRect: visThisTitle NOT empty }
END; { WITH }
;
titleHilited := noneHilited; { So we dont re-invert. }
END; { IF hilited }
IF menuID <> 0 THEN
BEGIN
i := 0;
WHILE (i < numMenus) & (wMenus[i].mh^^.menuID <> menuID) DO
i := i + 1;
IF i <> numMenus THEN
WITH wMenus[i] DO
BEGIN
IF SectRect(visAllTitles, titleRect, visThisTitle) THEN
BEGIN
visString := visThisTitle;
WITH visString DO
BEGIN
IF (leftMSArect.right > right - invertOverlap - 2 * menuFrame)
| (screenBounds.left > right - invertOverlap - 2 * menuFrame) THEN
right := right - invertOverlap - 2 * menuFrame;
;
IF (rightMSArect.left < left + invertOverlap + 2 * menuFrame)
| (screenBounds.right < left + invertOverlap + 2 * menuFrame) THEN
left := left + invertOverlap + 2 * menuFrame;
END; { WITH visString }
IF NOT EmptyRect(visString) THEN
BEGIN
ClipRect(visThisTitle);
IF aMac2 THEN
BEGIN
{ Texts color becomes the background color and the }
{ MenuBars background color becomes the foreground. }
wSetColorMenu(menuID, mceMenuTitle);
GetForeColor(hiliteBackColor);
HiliteColor(hiliteBackColor);
wSetColorMenu(mceMenuBar, mceMenuBar);
{ GetBackColor(hiliteForeColor); }
{ RGBBackColor(hiliteForeColor); }
BitClr(Ptr(HiliteMode), pHiliteBit);
END; { on a color machine }
InvertRect(visThisTitle);
ValidRect(visThisTitle);
;
titleHilited := i;
END; { visString NOT empty }
END; { IF SectRect: visThisTitle NOT empty }
END; { IF i <> numMenus & WITH }
END { IF menuID <> 0 }
ELSE { as in HiliteMenu(0). }
; { Taken care of by IF titleHilited <> noneHilited }
END; { IF SectRect: visAllTitles NOT empty }
IF aMac2 THEN
BEGIN
SetMCInfo(oldMCTable);
RGBForeColor(oldForeColor);
RGBBackColor(oldBackColor);
END;
;
TextSize(oldTxSize);
TextFont(oldTxFont);
TextFace(oldTxStyle);
TextMode(oldTxMode);
;
SetClip(origClip);
DisposeRgn(origClip);
SetPort(origPort);
END; { WITH theMenuBar^^ }
HSetState(Handle(theMenuBar), lmbHState);
END; { wHiliteMenu }
Called in response to zooming & growing the window.
Note: NOT zooming = growing.
{27}
PROCEDURE wChangeMenuBarSize (wp: WindowPtr; zooming: BOOLEAN);
BEGIN
mBar := wGetMenuBar(wp);
;
IF mBar <> NIL THEN
BEGIN { Only a part of wClearMenuBar ... }
GetWBMrects(wp, WBMrect, leftMSArect, rightMSArect);
wHiliteMenu(mBar, 0);
IF NOT zooming THEN
{ doZoom calls InvalRect(wp^.portRect) }
InvalRect(rightMSArect);
IF mBar^^.rightScrollPoly <> NIL THEN
BEGIN
KillPoly(mBar^^.rightScrollPoly);
mBar^^.rightScrollPoly := NIL; { Mark as gone !! }
END;
IF zooming & (mBar^^.leftScrollPoly <> NIL) THEN
BEGIN
KillPoly(mBar^^.leftScrollPoly);
mBar^^.leftScrollPoly := NIL;
END;
END; { window has a WBM }
END; { wChangeMenuBarSize }
END. { UNIT = wBarMenuProc }
Thank goodness its over ...
I lie ... even though I have NOT presented the source in wBMBalloons.p here, the application incorporates Balloon Help as in big System 7.0 ...and heres a sneak preview ...
{28}
UNIT wBMBalloons;
INTERFACE
USES
Types, Quickdraw, Menus, TextEdit, Traps, GestaltEqu, Balloons, wBMGlobals,
wBMMiscSubs, wBMScrollSubs;
FUNCTION HelpManagerActive: BOOLEAN;
FUNCTION BalloonsOn: BOOLEAN;
FUNCTION BalloonShowing: BOOLEAN;
PROCEDURE FindAndShowDynamicBalloons (balloonsUp: BOOLEAN; window: WindowPtr);
PROCEDURE HideBalloons (balloonsUp: BOOLEAN);
PROCEDURE ShowBalloons (balloonsUp: BOOLEAN);
PROCEDURE ResetBalloons (balloonsUp: BOOLEAN);
NOW its over, really and truly ..
Well ... almost!!! What about AppleEvents? ... its on disk!!
{29}
UNIT myAppleEvents;
INTERFACE
USES
Types, Memory, OSUtils, Quickdraw, Events, Files, AppleTalk, PPCToolbox,
Processes, EPPC, Notification, AppleEvents, Script, Packages, Dialogs,
CTBUtilities, Connections, GestaltEqu, wBMGlobals, wBMMiscSubs;
FUNCTION AppleEventsActive: BOOLEAN;
FUNCTION PPCToolboxActive: BOOLEAN;
FUNCTION DoAEOpenApplication (message: AppleEvent; reply: AppleEvent;
refcon: LONGINT): OSErr;
FUNCTION DoAEOpenDocuments (message: AppleEvent; reply: AppleEvent;
refcon: LONGINT): OSerr;
FUNCTION DoAEPrintDocuments (message: AppleEvent; reply: AppleEvent;
refcon: LONGINT): OSErr;
FUNCTION DoAEQuitApplication (message: AppleEvent; reply: AppleEvent;
refcon: LONGINT): OSErr;
FUNCTION InitAppleEvents: OSErr;
FUNCTION MissedAnyParameters (VAR event: EventRecord; message: AppleEvent):
BOOLEAN;
PROCEDURE DoHighLevelEvent (event: EventRecord);
P.P.S The SARez resource code is on dis k too
SEE!!!
the little guy here this must prove Im done -->