mac-rom/Toolbox/HelpMgr/BalloonPACK.p
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

3481 lines
114 KiB
OpenEdge ABL

{__________________________________________________________________________________________________
File: BalloonPACK.p
Contains: Pascal support routines for _Pack14 (HelpMgr)
Written by: Randy Carr
Copyright: © 1989-1992 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<SM3> 2/17/93 kc Allocate pictures in the System Heap because they persist longer
than the application heap.
<81> 6/2/92 KST #1030643 <JH>: We have only 1 default message for the floating
window instead of two (floating windows are all active).
<80> 5/14/92 KST #1027497 <JH>: #1025797,#1027497: In '__HMGetWindowPartCode',
defined 2 new window parts when mouse is over a floating window.
Provided default message so that Balloon help can work when
mouse is over a floating window.
<79> 4/23/92 JH #1027497,<YK>: CubeE adds a IMlayer for use by floating windows
that input windows need. Unfortunately, the Help mgr doesn't
know anything about this new layer. So I'm changing
__HMGetWindowPartCode to call the TSM call FindServiceWindow
before calling FindWindow. If FindServiceWindow returns a part
code in a real window we use GetParent to find IMlayer. Swap
that layer in, let the help mgr do its thing, and then swap the
old layer back.
<78> 4/4/92 csd #1017165,<BBM>: In HMShowWindowPartBalloon, set the port to the
BalloonWindow before calling ReplaceText since ReplaceText uses
the current portÕs font and script information.
<77> 3/23/92 JSM OSEvents.p is obsolete, use Events.p.
<76> 11/21/91 csd #83334: Used a single string with ReplaceText instead a two
strings with Concat to build "This window belongs to the
application ÒxxxÓ. To make this window active, click in it."
<75> 11/3/91 KSM In theFuture, we should not arbitrarily limit the size of
balloons (per Randy).
<74> 4/1/91 RLC vl, #85955- FIx bug that wasn't respecting current process when
tracking dialogs that contain a help item in the DITL handle.
<73> 3/19/91 RLC ksm,#BH-TCA-0083 - FIx bug that wasn't passing window method
parameter to HMShowBalloon() for 'hdlg' & 'hrct' resources.
<72> 3/13/91 RLC ksm,#84586 - Change reset of help msgs using a text edit handle
that are passed to HMShowBalloon to force the text edit handle
to the help mgr font.
<71> 2/20/91 RLC ksm,#BBSLostItsMind - Add a bracket back in that somehow got
left out of a comment in the HMSelectBestVariant routine.
<70> 2/20/91 RLC ksm,#82854 - Add a check in HMScanTemplateItems that makes sure
that the window we're examining is a dialog if the type if
kHMDialogResType. The call to FindDItem in HMSameItemState can
otherwise be made on a non-Dialog window (i.e. Crash!).
<69> 2/19/91 RLC ksm,#81544 Fix balloon positioner to make sure all variants are
tried when initial variant does not fit. This fixes the clipped
balloon for Empty Trash and KeyCaps. GO5 approved.
<68> 2/8/91 RLC <ksm> #82161 Add a call to strip the null character from the
processes that are desk accessories. This causes text edit to
word break the process name leaving beginning and ending quotes
on different lines of the balloon. <BHTCA0080> = force grayed
controls to get the HMDisabled state in all cases.
<67> 1/24/91 RLC <gbm> #81591 Change __HMFillInDialogMessage() calls to pass the
resource file refnum from HMScanTemplateItems(). Those routines
will call GetResource() for file refnum<>-1 otherwise call
Get1Resource() [which means specifically search a resource file
for a help resource]. Also changed occurrances of hmmPic to
hmmPict as per Scott Boyd's changes to Balloons.p.
<66> 1/22/91 RLC <dc> #81592 Fix bug that leaves a balloon up if the mouse
moves from a system tracked balloon to the menubar and remove
the extraneous tip parameter from HMShowTitleBalloon() routine.
<65> 1/10/91 RLC <stb> Change the code for HMTrackModalItems and
HMScanTemplateItems to not set the hmgLastItem and
hmgLastItemState when the 'hdlg' resource that we're tracking
returns a hmSkippedBalloon for an item's message. This makes
sure that the 'hdlg' resource indicated by the HMSetDialogResID
setting gets checked in case the resource comes later in the
resource map.
<64> 1/10/91 RLC <ksm> Fix __HMPinTipAndAltRect routine to do the right thing if
tip is outside screenbounds and after correction is still
outside altRect.
<63> 12/17/90 RLC <ksm> Change HMSetBalloons to always remove the balloon before
disposing of the layer when balloon help is turned off else we
risk orphaning the balloon window in a non-existent layer.
<62> 12/14/90 RLC <ngk> Add check to __HMShowWindowPartBalloon so that unhilited
windows that are behind a movable modal get the right balloon.
(i.e. you can switch to another layer, but you cannot bring an
inactive window to the front) Remove HMNewBestWorld conditional
and the older positioning code that was excluded by it. Change
giant case statement into table driven HMVarCodeToBias() asm
routine (saves 238 bytes). Changed kHMFinderOnly to
kHMActiveApplOther to support menu items in Process menu that
are marked. Make HMCountDITLHelpItems external so that Standard
file can call it from the private interface.
<61> 12/14/90 dba <JDR> get rid of LongIntPtr, now in Types.p
<60> 12/3/90 RLC <ksm> Add check to __HMShowWindowPartBalloon so that unhilited
windows that are behind a movable modal get the right balloon.
(i.e. you can switch to another layer, but you cannot bring an
inactive window to the front.
<59> 11/16/90 RLC <ksm> Always call HMFillInMenuMessage when a modal dialog is up
so that the disabled AppleMenu items get the right help string.
Fix bug that would not clear the pic VAR if HMCalculateBalloon
was called too frequently and not enough time elapsed. Roll in
new HMBestWorld calculation to fix all those agonizing balloons
that hang offscreen bugs. The new routine is simpler, smaller,
and tries more ways to get the balloon to fit.
<58> 10/4/90 RLC <ksm> Fix mem leak that wasn't always disposing of the picture
handles. The bug was that if the mouse was moving quickly
enough to cause HMCalculateBalloon to abort its calculation,
HMCalculateBalloon would return hmBalloonAborted, but with a
valid picHandle. In this case, the caller must dispose of
the picture by calling KillPicture. Remove references to
hmgContentWindow and hmgOurBalloon variables as these were
internal to the .a code of which is now calling the external
routine HMScanTemplateItems. Fixed bug in HMScanTemplate that
wasn't obeying the hmAbsoluteCoords option in the resource.
Change code that handles picture resources or picHandles when
showing a balloon: we don't copy the picture data, we just use
the pic as is and stuff it in the windowPic field. Call
DisposeWindow instead of CloseWindow for custom balloon winds.
<57> 9/30/90 RLC Change hits in the 'inGrow' message of a window to be
'inContent'. Reduce more redundant code in TrackModalHelp
routines by consolidating some code into:__HMSameItemState (this
saved >370 bytes).
<56> 9/26/90 RLC Make a change to the automatically generated window part
balloons to allow non-front windows that are hilighted to get
'front window' balloons. (This will allow windoids and other
palette windows that respect the window record's hilited boolean
to work correctly).
<55> 9/25/90 RLC Consolidate by having HMExtractHelpMsg call HMGetIndHelpMsg
instead of having separate (duplicate) code (saves >370 bytes).
Change HMShowMenuBalloon call to only call HMRemoveBalloon if a
new balloon is about to be displayed and add a call to
HMRemoveBalloon if HMSavedBitsÉ is called.
<54> 9/21/90 RLC Add code to the HMGetBalloons toggle to create both help window
and layer when the flag goes true and tear down the window &
layer for false. This means that the help layer won't be
burdening the window list hierarchy unless balloon help is on.
<53> 9/16/90 RLC (KSM also) Utilize itemReserved longword in HMShowMenuBalloon
routine to draw that string instead of fetching a help message
from the 'hmnu' resource.
<52> 9/15/90 RLC Repair positioner code in HMBestWorld that was calculating
balloon tip position incorrectly for balloons that failed to fit
on screen after 2 re-fits.
<51> 9/13/90 RLC Change references to our global handle (HMGlobalHandle) into
references to a global ptr (HMGlobalPtr), rename all routines
in this package to have __HM in front so that Balloons.p can
be part of uses statement, remove all multiple derefs to use
a single with as we don't have to worry about relocation of
handle's ptr.
<50> 8/31/90 RLC Remove duplicate and extraneous equates.
<49> 8/20/90 RLC Fix offscreen balloons bug that allows a balloon to hang off the
edge.
<48> 8/20/90 RLC Repair Philip's merge of the old file.
<46> 7/17/90 RLC Fix HMExtract calling conventions & styled text edit balloon
bugs.
<45> 7/16/90 RLC Change format of Styled-Text edit.
<44> 7/13/90 RLC Just about every interface routine got changed. Improved speed
of WDEF calc routines. Shortened code for everything. Made
multiple helpItems in dialogs work, fixed offset as well.
<43> 7/6/90 RLC Repair skip items in modal dialog to really be skipped instead
of always removing balloons.
<42> 7/5/90 RLC Fix multiple 'hdlg' occurrances.
<41> 7/2/90 RLC Some short changes
<40> 6/19/90 RLC More correction in variant central.
<39> 6/18/90 RLC Repair pinning algo again & add variant sensitivity/
<38> 6/14/90 RLC Include BalloonDefs.p for HMGlobalRecord.
<37> 6/8/90 KSM Update for new IsFrontWindowModal.
<36> 6/7/90 RLC Optimize a bit, fix misc positioning bugs.
<35> 6/5/90 RLC Fix HMSetDialogResID & system menu title balloon tip.
<34> 6/1/90 RLC Update global record
<33> 5/31/90 RLC Fix possible address error for 68K machines in HMShowBalloon.
<32> 5/29/90 RLC Add support routines for new HMGetHelpMenu selector.
<31> 5/4/90 RLC Patches CONST changes.
<30> 5/3/90 RLC Fix bugs in modal dialog balloons, launching, etc.
<29> 4/19/90 RLC Add scriptmgr help font & size to HMSet & HMGet font calls.
<28> 4/17/90 RLC Add reference to new utility routine: HMFillCitationString.
<27> 4/10/90 KSM New constants for system menu patch.
<26> 4/5/90 RLC Add support for the 'hovr' resource.
<25> 4/3/90 RLC Fix Modal Dialog tracking
<24> 4/2/90 RLC Reconnect HMSetDialogResID & HMSetMenuResID.
<23> 3/30/90 RLC Make sure MF rect is in line with MultiFinder.
<23> 3/28/90 RLC Add support for appends to the help menu.
<21> 3/26/90 RLC Fix global lasts for menus AND saveBits options.
<20> 3/22/90 RLC Remove hardcoded strings except for testing.
<19> 3/19/90 RLC Bug Fixes galore (58881 thru 62272).
<18> 3/15/90 RLC Fix all error return codes, abort remove if point is in our help
balloon.
<17> 3/7/90 RLC Fix the pt not in current layer stuff.
<16> 2/23/90 RLC Repair HMSetMenuResID for Pascal3.1 & add other layer support.
<15> 2/22/90 dnf Per RLC's instructions: Comment out code in HMSetMenuResID
Function
<14> 2/8/90 RLC Fix scanning of window parts to always return the correct
windowpart balloon.
<13> 2/7/90 RLC Add in change to respect tip & hotRect parameters in 'hdlg'
resources.
<12> 2/5/90 RLC Add support for delay in balloon drawing.
<11> 1/31/90 RLC Revise HMGlobalRecord again.
<10> 1/26/90 RLC Increase Performance.
<9> 1/26/90 RLC Update hmgGlobalRecord and support for a modal condition
(again).
<8> 1/25/90 RLC Change shape of Balloon look.
<7> 1/19/90 RLC Update selectors and error codes.
<6> 1/11/90 RLC Change mechanism for controlling how system balloons appear.
<5> 1/9/90 RLC Add support for new resource format.
<4> 1/3/90 RLC Fix to remove balloons in a modal dialog case where the mouse
leaves the dialog.
<3> 1/3/90 RC Change hdlgSaveBits option to only save bits if on.
<2> 12/20/89 RC Add change to support Menu title balloons better.
<4.1> 12/15/89 RLC Added TipProc, theProc, & variant support.
<4.0> 12/6/89 RLC Tweak tip when horizontal balloon crosses hotRect.
<3.9> 12/1/89 RLC Fix HMScanTemplate routine check to see if dialog is a modal
dialog before setting flag.
<3.8> 11/30/89 RLC Fixed bug in HMTrackTemplate code that couldn't handle HMSkip
types & updated HMGlobalsRecord
<3.7> 11/21/89 RLC Temp fix for bug in _DrawPicture & remember to TEDispose the
TENew.
<3.6> 11/20/89 RLC Update routines that find the helpItem parameter in a 'DITL'
template handle.
<3.5> 11/16/89 RLC Change parameter interface to HMTrackTemplateHelpItems.
<3.4> 11/15/89 RLC Change 'hdlg' support to correctly handle local offsets.
<3.3> 11/13/89 RLC Changed dialog tracking routines, window parts tracker.
<3.2> 11/9/89 RLC Fix bug in window part balloons for grow box.
<3.1> 11/7/89 RLC Add support for window parts tracking.
<3.0> 11/2/89 RLC Removed all non-PACK14 routines.
<2.9> 10/27/89 RLC Fix bug when displaying khmmPict and PicHandle types that didn't
set origin correctly.
<2.8> 10/1/89 RLC Added new routine HMGetStyledTE & Updated HMBalloonRect for
HelpMsg types: StyledTE & STR.
<2.7> 9/28/89 RLC Fixed bug that didn't correct the Balloon tip if on GDevice
monitors.
<2.6> 9/27/89 RLC Fixed positioner for Balloons in ModalDialogs.
<2.5> 9/25/89 RLC Added more support for MultiFinder appl icon.
<2.4> 9/22/89 RLC Fixed bug in HMGetGDevice & potential dangling pointer bug in
HMSaveBalloonBits
<2.3> 9/21/89 RLC Changed Balloon WDEF ID to 126.
<2.2> 9/21/89 RLC Added better error checking on message passing to HMBalloonRect
& HMCalcBalloon routines.
<2.1> 9/20/89 RLC Bug Fixes, change hmnu scanning routines to make sure
HMSkipItems never display balloons.
<2.0> 9/19/89 RLC Bug Fix in ShowMenuTitle.
<1.9> 9/19/89 RLC Added support for Multifinder Balloons.
<1.8> 9/18/89 RLC Dialog Template scan routines changed.
<1.7> 9/15/89 RLC Changed Modal Dialog Tracking to use PopupMenu instead of ICON.'
<1.6> 9/15/89 RLC Changed Debuggers to Assert calls.
<1.5> 9/13/89 RLC Repaired Balloon Calculation routines, made MenuTitle Balloons
better.
<1.4> 8/29/89 RLC Changed HMCalc & Changed all Balloon routines to use new calling
convention.
8/22/89 RCarr Rewrite nearly every interface!
<1.3> 8/10/89 RLC Added code for autocontents & added patch to _InitWindows
8/8/89 RCarr Optimize by preallocating Help Layer @ _InitWindows time, fix
bug that was calling _KillPicture twice when calling
_DisposeWindow.
7/31/89 RCarr Added support for autocontents patches for help mgr.
<1.2> 7/20/89 RLC Added patchcode to support help menu & move HMBalloonRect from C
to Pascal.
7/20/89 RCarr Added tons of support stuff & Help Menu
<1.1> 7/13/89 RLC Add HMBalloonRect to pack
<1.0> 6/29/89 RLC Created Today.
5/22/89 RCarr New Today!
__________________________________________________________________________________________________}
{$ifc UNDEFINED HelpMgrTesting}
{$setc HelpMgrTesting = false}
{$endc}
{$setc AllowCloseViewChanges = false}
{$ifc HelpMgrTesting}
{$D+}
{$INIT+}
{$elsec}
{$D-}
{$INIT-}
{$endc}
UNIT BalloonPack;
INTERFACE
USES
Types, QuickDraw, ToolUtils, Fonts, Events, Controls,
Windows, Dialogs, Files, Menus, Desk, Traps, OSUtils, GestaltEqu,
Resources, Memory, Packages, FixMath, Errors, Layers, Processes, MFPrivate, MenuMgrPriv, DialogsPriv, Balloons,
TextServices; {<79>}
CONST
{ Start of internal CONST }
hmGetNextHelpItem = -873;
hmWasAppleMenu = -874;
{System STR# resource indexes}
kHMMenuTitleIndex = 1;
kHMAboutHelpIndex = 2;
kHMAboutHelpDisabled = 3;
kHMReserved = 4;
kHMWhatIsHide = 5;
kHMInDragIndex = 6;
kHMInGrowIndex = 7;
kHMInGoAwayIndex = 8;
kHMInZoomIndex = 9;
kHMMultiFinderIndex = 10;
kHMInScrollBar = 11;
kHMInDisabledScrollBar = 12;
kHMInOtherWindow = 13;
kHMInLayerPreamble = 14;
kHMInLayerBody = 15;
kHMOutsideModalWindow = 16;
kHMAppleMenuTitle = 17;
kHMShowHideEnabled = 18;
kHMHideOthersEnabled = 19;
kHMShowAllEnabled = 20;
kHMShowHideDisabled = 21;
kHMHideOthersDisabled = 22;
kHMShowAllDisabled = 23;
kHMEasy1Access = 24;
kHMEasy2Access = 25;
kHMEasy3Access = 26;
kHMActiveApplEnabled = 27;
kHMActiveApplDisabled = 28;
kHMActiveApplChecked = 29;
kHMActiveApplOther = 30;
kHMActiveApplDisabledTitle = 31;
kHMActiveApplDisabledItem = 32;
kHMInFrontFloatingWindow = 33; { for IM, mouse is over a floating window }
kHMDelayTix = 5;
{Dialog item template type}
kHelpItem = 1;
{Dialog item template offsets}
kHelpItemType = 1; {new, allows the helpItem to contain more than just a resID}
kHelpItemData = 3; {offset in HelpItem array item in DITL to read ResID}
kHelpItemOffset = 5; {offset to reach overriding append offset for hdlg}
{helpItem type constants}
kScanhdlg = 1;
kScanhrct = 2;
kScanAppendhdlg = 8; { <58> for some reason, this was a 3 in previous, sync up w/ Types.r }
{ internal balloon creation consts (Tweak these values until we die) }
DicksOffsetPixels = 9; {was 9+3 for Dick's design}
HMMenuOffsetPixels = 8;
kHMFrameThickness = 6;
kHMVertContentMargin = 13;
kHMHorizContentMargin = 13;
kHMScreenBorder = 20;
kHMCornerDiameter = 16;
kHMPointerSize = 18;
kHMBottomMargin = 50;
kHMShadowThickness = 2;
kHMVertFrameMargin = (kHMPointerSize + (kHMShadowThickness * 4));
kHMHorizFrameMargin = (kHMPointerSize + (kHMShadowThickness * 4));
kHMMiscMargin = 5;
maxTETextArea = 50 * 340; {50 lines @ 340 pixels/line}
MBarHeight = $BAA;
UnImplementedTrap = $9F;
wasWindow = 0;
wasMenu = 1;
kTitleType = 1;
kDialogType = 2;
kMenuType = 3;
kRectType = 4;
kEAType = 5;
kWindPartType = 6;
{hmgMenuID flag values}
kwasMultiFinder = -2;
kwasWindowPart = -3;
kwasMenuTitle = -4;
kwasCleared = -5;
kwasEasyAccess = -6;
kGetFont = 0;
kSetFont = 1;
kVariantToRBS = true;
kRBSToVariant = false;
kShiftBit = 0;
kBottomBit = 1;
kRightBit = 2;
TYPE
EventRecPtr = ^EventRecord;
RBSRecord = Packed RECORD { this is it boys! }
CASE INTEGER OF
0: (
Filler1 : 0..8191;
rightValue : 0..1; { if 1 then balloon is hanging right }
bottomValue : 0..1; { if 1 then balloon is hanging down }
shiftValue : 0..1; { if 1 then balloon has vertical tip }
);
1: (
Whole : INTEGER;
);
END;
biasRecord = Packed RECORD
hShift : INTEGER;
vShift : INTEGER;
hCornerVector : INTEGER;
vCornerVector : INTEGER;
hBaseVector : INTEGER;
vBaseVector : INTEGER;
END;
{$I BalloonDefs.p}
FUNCTION __HMShowBalloon(aHelpMsg: HMMessageRecord;
tip: Point;
alternateRect: RectPtr;
tipProc: Ptr;
theProc,variant: INTEGER;
method: INTEGER): OsErr;
FUNCTION __HMRemoveBalloon: OsErr;
FUNCTION __HMGetBalloons: BOOLEAN;
FUNCTION __HMSetBalloons(flag: BOOLEAN): OsErr;
FUNCTION __HMShowMenuBalloon(itemNum,itemMenuID: INTEGER;
itemFlags,itemReserved: LongInt;
tip: Point; alternateRect: RectPtr; tipProc: Ptr;
theProc,variant: INTEGER): OsErr;
FUNCTION __HMIsBalloon: BOOLEAN;
FUNCTION __HMSetFont(font: INTEGER): OsErr;
FUNCTION __HMSetFontSize(fontSize: INTEGER): OsErr;
FUNCTION __HMGetFont(VAR font: INTEGER): OsErr;
FUNCTION __HMGetFontSize(VAR fontSize: INTEGER): OsErr;
FUNCTION __HMSetDialogResID(theResID: INTEGER): OsErr;
FUNCTION __HMGetDialogResID(VAR theResID: INTEGER): OsErr;
FUNCTION __HMSetMenuResID(theMenuID,theResID: INTEGER): OsErr;
FUNCTION __HMGetMenuResID(theMenuID: INTEGER; VAR theResID: INTEGER): OsErr;
FUNCTION __HMBalloonRect(aHelpMsg: HMMessageRecord; VAR coolRect: Rect): OsErr;
FUNCTION __HMBalloonPict(aHelpMsg: HMMessageRecord; VAR coolPict: PicHandle): OsErr;
FUNCTION __HMScanTemplateItems(whichID,whichResFile: INTEGER; whichType: ResType): OsErr;
FUNCTION __HMGetBalloonWindow(VAR window: WindowPtr): OsErr;
{private calls}
FUNCTION __HMCountDITLHelpItems(d: DialogPtr): INTEGER;
FUNCTION __HMModalDialogMenuSetup(ModalDialogComingUp: BOOLEAN): OsErr;
FUNCTION __HMTrackModalHelpItems: OsErr; { tracks items in modal dialogs and alerts }
FUNCTION __HMDrawBalloonFrame(rgn: RgnHandle): OsErr;
FUNCTION __HMSetupBalloonRgns( varCode: INTEGER;
VAR structRgn: RgnHandle;
VAR contentRgn: RgnHandle;
VAR bounds: Rect): OsErr;
PROCEDURE __HMShowEasyAccessBalloon;
PROCEDURE __HMShowTitleBalloon(menuID: INTEGER; flags: LongInt);
FUNCTION __HMShowContentBalloon(tip: Point; alternateRect: Rect;
theProc,variant: INTEGER;
aHelpMsg: HMMessageRecord; options: LongInt): OsErr;
FUNCTION __HMGetWindowPartCode(VAR window: WindowPtr): INTEGER;
FUNCTION __HMShowWindowPartBalloon(window: WindowPtr; windowCode: INTEGER): BOOLEAN;
FUNCTION __HMFillFrontProcessString(s: StringPtr): OsErr;
FUNCTION __HMExtractHelpMsg(whichType: ResType;
whichResID, whichMsg, whichState: INTEGER;
VAR aHelpMsg: HMMessageRecord): OsErr;
IMPLEMENTATION
FUNCTION __HMGetScreenGDevice(pt: Point): GDHandle; FORWARD;
FUNCTION __HMSaveBalloonBits(aHelpMsg: HMMessageRecord;
tip: Point;
alternateRect: RectPtr;
tipProc: Ptr;
variant: INTEGER;
balloonType: INTEGER): OsErr; FORWARD;
FUNCTION __HMCalculateBalloon( aHelpMsg: HMMessageRecord;
tip: Point;
alternateRect: RectPtr;
VAR varCode: INTEGER;
VAR bounds: Rect; VAR pic: PicHandle;
balloonType: INTEGER;
abortable: BOOLEAN): OsErr; FORWARD;
PROCEDURE __HMTwitchVarCode(isVariantToRBS: BOOLEAN;
FromValue: INTEGER; VAR ToValue: INTEGER); EXTERNAL;
PROCEDURE __HMVarCodeToBiasRecord(varCode: INTEGER; VAR bias: biasRecord); EXTERNAL;
FUNCTION __HMSlopMouse(tip: Point): BOOLEAN; EXTERNAL;
FUNCTION __HMCallTipProc(tipProc: Ptr;
tip: Point;
structure: RgnHandle;
VAR bounds: Rect;
VAR varCode: INTEGER): OsErr; EXTERNAL;
PROCEDURE __HMSetGetFontSizeGlobal(setit: INTEGER; VAR fontsize: LongInt); EXTERNAL;
FUNCTION __HMSaveBitsInRect(theRect: Rect): PixMapHandle; EXTERNAL;
PROCEDURE __HMRestoreBitsToRect(thePM: PixMapHandle); EXTERNAL;
FUNCTION __HMFillCitationString(origString,stuffString: StringPtr;
key: Char) : OsErr; EXTERNAL;
FUNCTION __HMCallCustomWDEFCalc(v: INTEGER; w: WindowPtr): INTEGER; EXTERNAL;
FUNCTION __HMGetIndHelpMsg(whichType: ResType;
whichResID, whichMsg, whichState: INTEGER;
VAR options: LongInt;
VAR tip: Point;
altRect: RectPtr;
VAR theProc: INTEGER; VAR variant: INTEGER;
VAR aHelpMsg: HMMessageRecord;
VAR count: INTEGER): OsErr; EXTERNAL;
FUNCTION __HMBalloonBulk: OsErr; EXTERNAL;
FUNCTION __HMFillInMenuMessage(menuID,item,resID: INTEGER; flags: LongInt;
VAR variant: INTEGER;
VAR aHelpMsg: HMMessageRecord): OsErr; EXTERNAL;
FUNCTION __HMFillInDialogMessage( resID,item,whichHelpItem,
totalHelpItems,state,offset: INTEGER;
resFile: INTEGER;
VAR options: LongInt;
VAR tip: Point; altRect: RectPtr;
VAR theProc: INTEGER; VAR variant: INTEGER;
VAR aHelpMsg: HMMessageRecord): OsErr; EXTERNAL;
FUNCTION __HMScanHRCTResource(whichWindow: WindowPtr; whichResID: INTEGER; whichPt: Point;
VAR whichMsg: INTEGER): OsErr; EXTERNAL;
FUNCTION __HMGetMenuItemStatus(mh: MenuHandle; flags: LongInt;
item: INTEGER): INTEGER; EXTERNAL;
FUNCTION __HMGetHelpGlobal: HMGlobalPtr; EXTERNAL;
FUNCTION __HMGetRealWindowHitResult(p: Point; w: WindowPtr): INTEGER; EXTERNAL;
FUNCTION __HMGetGlobalMouse: Point; EXTERNAL;
PROCEDURE __HMGetTextFontStuff(VAR theFont: INTEGER; VAR theSize: INTEGER); EXTERNAL;
FUNCTION __HMShiftKey: BOOLEAN; EXTERNAL;
PROCEDURE __HMStuffWindowInfo(window: WindowPtr; varCode: INTEGER; bounds: Rect); EXTERNAL;
PROCEDURE __HMStripNullFromProcessName(s: StringPtr); EXTERNAL;
{$ifc HelpMgrTesting}
PROCEDURE Assert(b: BOOLEAN; s: STR255);
BEGIN
if b then DebugStr(s);
END;
{$endc}
PROCEDURE __HMDisposeRgn(r: RgnHandle);
BEGIN
if r<>NIL then
DisposeRgn(r);
END;
FUNCTION __HMCanPictureBeKilled(kind: INTEGER): BOOLEAN; { <59> Code saver }
BEGIN
__HMCanPictureBeKilled := (kind <> khmmPict) and (kind <> khmmPictHandle);
END;
FUNCTION __HMIsThisAModalDialog: BOOLEAN;
VAR class : INTEGER;
BEGIN
__HMIsThisAModalDialog := false;
if GetFrontWindowModalClass(class) = noErr then
__HMIsThisAModalDialog := (class = dBoxProc);
END;
FUNCTION __HMFillFrontProcessString(s: StringPtr): OsErr;
VAR PSN : ProcessSerialNumber;
info : ProcessInfoRec;
result : OsErr;
BEGIN
result := GetFrontProcess(PSN);
if result=noErr then
begin
with info do
begin
processInfoLength := Sizeof(ProcessInfoRec);
processName := s;
processAppSpec := NIL;
end;
result := GetProcessInformation(PSN,info);
end;
__HMFillFrontProcessString := result;
END;
FUNCTION __HMGetStyledHandleResource( textRect: Rect;
textResType: ResType;
textID: INTEGER): TEHandle;
VAR hTE : TEHandle;
hStyl : Handle;
hText : Handle;
BEGIN
__HMGetStyledHandleResource := NIL; { assume that we didn't get it }
if LongInt(textResType) = LongInt(kHMTETextResType) then
begin
hTE := TEStylNew(textRect,textRect);
if hTE<>NIL then
begin
hText := GetResource(kHMTETextResType,textID); { we hope this is purgeable! }
if hText<>NIL then { set up the TEHandle w the char data }
begin
hStyl := GetResource(kHMTEStyleResType,textID);
HLock(hText);
TESetText(Ptr(hText^),GetHandleSize(hText),hTE);
HUnlock(hText);
if hStyl<>NIL then
SetStylScrap(0,$7FF,stScrpHandle(hStyl),false,hTE);
end else
begin
if hTE<>NIL then
TEDispose(hTE);
hTE := NIL;
end;
end;
__HMGetStyledHandleResource := hTE;
end;
END;
FUNCTION __HMBalloonPict(aHelpMsg: HMMessageRecord; VAR coolPict: PicHandle): OsErr;
VAR tip : Point;
bounds : Rect;
varCode : INTEGER;
BEGIN
LongInt(tip) := 0;
varCode := 0;
__HMBalloonPict := __HMCalculateBalloon(aHelpMsg,tip,NIL,
varCode,bounds,coolPict,wasWindow,false {can't abort balloon if mouse is moving});
END;
FUNCTION __HMBalloonRect(aHelpMsg: HMMessageRecord; VAR coolRect: Rect): OsErr;
VAR aTE : TEHandle;
fInfo : FontInfo;
lineHeight : LongInt;
ph : PicHandle;
textH : Handle;
textArea : LongInt;
ht,wd : LongInt;
numLines : LongInt;
r,rp : LongInt;
tempstr : STR255;
tempRect : Rect;
result : INTEGER;
resizeAble : BOOLEAN;
tempFont : INTEGER;
tempSize : INTEGER;
FUNCTION FastSQRT(val: LongInt): LongInt;
BEGIN
FastSQRT := BSR(FracSqrt(Fract(val)),15);
{ in C: FastSQRT = (long) FracSqrt((Fract)val) >> 15;}
END;
PROCEDURE ResizeTE(teh: TEHandle; width, height: INTEGER);
VAR ter : Rect;
BEGIN
with teh^^ do
begin
ter := destRect;
with ter do
begin
bottom := top + height;
right := left + width;
end;
destRect := ter;
viewRect := ter;
end;
TECalText(teh);
END;
BEGIN
GetFontInfo(fInfo);
with fInfo do
lineHeight := ascent + descent + leading;
__HMBalloonRect := noErr;
wd := 0;
ht := 0;
aTE := NIL;
result := noErr;
resizeAble := false;
SetRect(tempRect,5,5,100,100); { Start with this rect initially }
case aHelpMsg.hmmHelpType of
khmmString,khmmStringRes,khmmSTRRes:
begin
with aHelpMsg do
begin
tempstr := ''; { zero for failures }
if hmmHelpType = khmmString then
tempstr := hmmString
else if hmmHelpType = khmmSTRRes then
begin
textH := Handle(GetString(hmmSTRRes));
if textH<>NIL then
tempstr := StringHandle(textH)^^;
end else
GetIndString(tempstr,hmmStringRes.hmmResID,
hmmStringRes.hmmIndex);
if length(tempstr) > 0 then
begin
aTE := TENew(tempRect,tempRect);
{$ifc HelpMgrTesting}
Assert(aTE=NIL,'HMBalloonRect[X]: a te handle was NIL');
{$endc}
if aTE<>NIL then
begin
TESetText(pointer(ord(@tempstr)+1),
length(tempstr),aTE);
textArea := lineHeight * StringWidth(tempstr);
end else result := memFullErr;
end else result := paramErr;
end;
end;
khmmPict,khmmPictHandle:
begin
if aHelpMsg.hmmHelpType = khmmPict then
ph := PicHandle(GetResource('PICT',aHelpMsg.hmmPict))
else ph := aHelpMsg.hmmPictHandle;
if ph<>NIL then
begin
with ph^^.picFrame do
begin
wd := right-left;
ht := bottom-top;
end;
end else
if aHelpMsg.hmmHelpType = khmmPict then
result := resNotFound
else result := paramErr;
{$ifc HelpMgrTesting}
Assert(ph=NIL,'HMBalloonRect[2]: a pic handle was NIL');
{$endc}
end;
khmmTEHandle,khmmTERes:
begin
if (aHelpMsg.hmmHelpType = khmmTERes) then
aTE := __HMGetStyledHandleResource(tempRect,kHMTETextResType,aHelpMsg.hmmTERes)
else aTE := aHelpMsg.hmmTEHandle;
if aTE<>NIL then
begin
resizeAble := (aTE^^.txSize<>-1);
textH := aTE^^.hText;
HLock(textH);
if resizeAble then
with aTE^^ do
begin
{ <72> don't need these anymore
tempFont := txFont;
tempSize := txSize;
}
__HMGetTextFontStuff(txFont,txSize);
{ *** how do we reset the old TEHandle's font & size helpmgr font here? }
GetFontInfo(fInfo);
with fInfo do
lineHeight := ascent + descent + leading;
end else
lineHeight := TEGetHeight(0,$7FF,aTE) div aTE^^.nLines;
textArea := lineHeight * TextWidth(textH^, 0, aTE^^.teLength);
HUnlock(textH);
{$IFC NOT theFuture} {<75>}
if textArea > maxTETextArea then
result := paramErr;
{$ENDC}
end else result := paramErr;
end;
otherwise
begin
{$ifc HelpMgrTesting}
NumToString(aHelpMsg.hmmHelpType,tempstr);
DebugStr(concat('Bad HelpType (',tempstr,') passed to HMBalloonRect'));
{$endc}
__HMBalloonRect := hmUnknownHelpType;
Exit(__HMBalloonRect);
end;
end;
if result<>noErr then
begin
__HMBalloonRect := result;
Exit(__HMBalloonRect);
end;
if aTE<>NIL then { for khmmPict or khmmPictHandle case, don't do resizing}
begin
if textArea=0 then
r := 0 else
r := FastSQRT(textArea);
ht := (r * 1000) div 1272; {(1272/1000)**2 approx golden ratio}
{ make height a multiple of line height }
rp := ht mod lineHeight;
if (rp <> 0) then
ht := ht + lineHeight - rp;
numLines := ht div lineHeight;
wd := textArea div ht;
{ *** make sure longest word fits, someday! }
ResizeTE(aTE, wd, ht);
while (TEGetHeight(0,$7FF,aTE)>ht) do {loop until the # of lines fits in bounds}
begin
if (TEGetHeight(0,$7FF,aTE)>ht) then
begin
wd := (ht * 3236) div 2000;
ResizeTE(aTE, wd, ht);
end;
if (TEGetHeight(0,$7FF,aTE)>ht) then
begin
wd := ((ht + lineHeight) * 3236) div 2000;
ResizeTE(aTE, wd, ht);
end;
if (TEGetHeight(0,$7FF,aTE)>ht) then
begin
wd := (ht * 3236) div 2000;
ht := ht + lineHeight;
ResizeTE(aTE, wd, ht);
end;
end;
if (TEGetHeight(0,$7FF,aTE)<ht) then
begin
ht := TEGetHeight(0,$7FF,aTE);
ResizeTE(aTE, wd, ht); {resize again so HMCalc won't have to also}
end;
{ <72> don't reset the font and size of the text edit handle passed, as we've just calculated
the balloon for this text edit handle using the help mgr font
if resizeAble then
begin
with aTE^^ do
begin
txFont := tempFont;
txSize := tempSize;
end;
end;
}
with __HMGetHelpGlobal^ do
if hmgKeepTEHandle then
hmgTEHandle := aTE else
if aHelpMsg.hmmHelpType <> khmmTEHandle then {don't toss the supplied handle in khmmTEHandle case}
TEDispose(aTE);
end;
SetRect(coolRect,0,0,wd,ht);
END;
FUNCTION __HMGetBalloons: BOOLEAN;
BEGIN
__HMGetBalloons := __HMGetHelpGlobal^.hmgWhatIs;
END;
{$ifc AllowCloseViewChanges}
FUNCTION __HMIsCloseViewActive: BOOLEAN;
VAR result : LongInt;
BEGIN
__HMIsCloseViewActive := true;
(*
__HMIsCloseViewActive := false;
if Gestalt('BSDa',result) = noErr then
__HMIsCloseViewActive := (result <> 0);
*)
END;
{$endc}
FUNCTION __HMRemoveBalloon: OsErr;
VAR window : WindowPtr;
windowPic : PicHandle;
oldPort : GrafPtr;
wPort : GrafPtr;
oldLayer : LayerPtr;
floatLayer : LayerPtr;
bounds : Rect;
refcon : LongInt;
balloonDown : BOOLEAN;
pt : Point;
windowCode : INTEGER;
floatwindowCode : INTEGER;
ignoreErr : OSErr;
BEGIN
with __HMGetHelpGlobal^ do
begin
{$ifc AllowCloseViewChanges}
if __HMIsBalloon then
if __HMIsCloseViewActive then
if __HMShiftKey then
begin
hmgCloseViewCount := 1 {means remove current balloon when the shift key goes up};
__HMRemoveBalloon := hmCloseViewActive;
exit(__HMRemoveBalloon);
end;
{$endc}
if not hmgWhatIs then
__HMRemoveBalloon := hmHelpDisabled;
window := hmgWindow;
balloonDown := false; { assume that a balloon wasn't removed }
if (window<>NIL) then
begin
if hmgLayer<>NIL then
oldLayer := SwapCurLayer(hmgLayer);
windowPic := GetWindowPic(window); { <59> call toolbox instead of WindowPeeking record }
refcon := GetWRefCon(window); { <59> call toolbox instead of WindowPeeking record }
if windowPic<>NIL then { <58> make sure picture was actually there! }
if __HMCanPictureBeKilled(refcon) then { <59> don't 'kill' a resource! }
KillPicture(windowPic); { <54> but be sure to kill the picture }
SetWindowPic(window,NIL); { <59> call toolbox instead of WindowPeeking record }
if window = hmgHelpWindow then { we don't toss our window <54> }
begin
SetWRefCon(window,0); { <59> call toolbox instead of WindowPeeking record }
HideWindow(window); { just hide it <54> }
end else
DisposeWindow(window); { this does a KillPicture(windowPic) automagically! }
if hmgLayer<>NIL then
SetCurLayer(oldLayer);
hmgWindow := NIL;
hmgTitleBalloon := 0; { clear the flag that says that a menu title balloon is up }
hmgItemNum := -1;
balloonDown := true;
end;
if hmgSavedBitsHandle<>NIL then { <57> we don't need to set wmgrport anymore }
begin
__HMRestoreBitsToRect(hmgSavedBitsHandle);
hmgSavedBitsHandle := NIL; { zero the handle [flag] }
hmgItemNum := -1; { force new balloon to get shown }
balloonDown := true;
end;
with hmgSlopRect do
begin
LongInt(topLeft) := 0;
LongInt(botRight):= 0;
end;
with hmgItemRect do
begin
LongInt(topLeft) := 0;
LongInt(botRight):= 0;
end;
hmgLastPartCode := 0;
hmgLastWindowPtr := NIL;
hmgLastMenuID := -1;
hmgLastEnabled := 0;
hmgLastItem := 0;
hmgState := -1;
hmgMenuID := kwasCleared;
{$ifc AllowCloseViewChanges}
if balloonDown then
hmgCloseViewCount := 0;
{$endc}
end; { with __HMGetHelpGlobal^ }
{ do this nonsense 'cuz the WindowMgr clips the WmgrPort to menubar,
the result is that the pulldown menus get drawn w/o being erased first! }
SetRect(bounds,-32768, -32768, 32767,32767);
GetPort(oldPort);
GetWMgrPort(wPort);
SetPort(wPort);
ClipRect(bounds);
SetPort(oldport);
if balloonDown then
__HMRemoveBalloon := noErr
else __HMRemoveBalloon := hmNoBalloonUp;
END;
FUNCTION __HMIsBalloon: BOOLEAN;
BEGIN
with __HMGetHelpGlobal^ do
__HMIsBalloon := (hmgWindow<>NIL) | (hmgSavedBitsHandle<>NIL);
END;
FUNCTION __HMSetBalloons(flag: BOOLEAN): OsErr; { the entire routine was rewritten <54> RLC }
VAR aLayer : LayerPtr;
fontAndSize : HMFontRecord;
gp : HMGlobalPtr;
oldLayer : LayerPtr;
oldZone : THz;
bounds : Rect;
chain : WindowPeek;
aMenu : MenuHandle;
result : OsErr;
BEGIN
__HMSetBalloons := noErr; { in case we're exiting early }
gp := __HMGetHelpGlobal;
if gp<>NIL then
with gp^ do
begin
if hmgWhatIs = flag then exit(__HMSetBalloons); { the state is already setup so exit now }
{ reset the script info again }
LongInt(fontAndSize) := GetScript(INTEGER(GetEnvirons(smSysScript)), smScriptHelpFondSize);
LongInt(hmgFontAndSize) := LongInt(fontAndSize); { all this LongInt stuff is a code saver }
if flag then { let's create the layer and window now! }
begin
chain := LayerPeek(GetRootLayer)^.subWindows; { get the first children of root }
if chain<>NIL then { there is a child }
begin
{ *** Assert if the child is our help layer then everything is set already (how could this happen?) }
{$ifc HelpMgrTesting}
Assert(chain = WindowPeek(hmgLayer),'HMSetBalloons: Help layer was not deallocated!');
{walk the children of the root, looking for our help layer}
repeat
chain := chain^.nextWindow;
until (chain = NIL) | (chain = WindowPeek(hmgLayer));
Assert(chain<>NIL,'HMSetBalloons: Help layer was still in layer chain!');
{$endc}
{ get and set the rootlayer }
oldLayer := SwapCurLayer(GetRootLayer);
oldZone := GetZone; { get the current heap zone }
SetZone(SystemZone); { point at the system heap }
aLayer := NIL;
{ create the help layer, having its rgns in sys heap }
result := NewLayer(aLayer,true{visible},true{neveractive},
pointer(-1){infront},LongInt($BADFACE){refcon});
if result<>noErr then
begin
{$ifc HelpMgrTesting}
DebugStr('HMSetBalloons: Unable to create a help layer');
{$endc}
aLayer := NIL; { this seems to be garbage sometimes when there isn't the memory }
end else
begin
SetCurLayer(aLayer);
SetRect(bounds,0,0,100,100);
hmgHelpWindow := NewWindow(@hmgHelpWRecord,bounds,'Balloon',
false,(16 * kBalloonWDEFID) + 0 {default varCode},pointer(-1),false,0);
end;
SetZone(oldZone); { restore the heap zone }
hmgLayer := aLayer; { make our globals have that layer }
SetCurLayer(oldLayer); { restore the current layer }
end; { else there wasn't any root layer (how can this happen?) }
end else
begin { tear down the layer and window }
{$ifc HelpMgrTesting}
if hmgHelpWindow = NIL then
begin
DebugStr('HMSetBalloons: we tried to deallocate the help window twice!');
exit(__HMSetBalloons);
end;
if hmgLayer = NIL then
begin
DebugStr('HMSetBalloons: we tried to deallocate the help layer twice!');
exit(__HMSetBalloons);
end;
{$endc}
if __HMRemoveBalloon=noErr then; { <63> always hide the balloons if help is turned off }
oldLayer := SwapCurLayer(hmgLayer);
CloseWindow(hmgHelpWindow); { close the window because we've got our own storage for the wRecord }
hmgHelpWindow := NIL;
DisposeWindow(hmgLayer); { dispose window because we allocated it in the System heap via NIL }
hmgLayer := NIL;
SetCurLayer(oldLayer); { restore the current layer }
end;
hmgWhatIs := flag; { Set the HMGetBalloons global flag }
end;
__HMSetBalloons := noErr;
END;
FUNCTION __HMShowBalloon(aHelpMsg: HMMessageRecord;
tip: Point;
alternateRect: RectPtr;
tipProc: Ptr;
theProc,variant: INTEGER;
method: INTEGER): OsErr;
VAR oldPort : GrafPtr;
oldZone : THz;
bounds : Rect;
wBounds : Rect;
temprect : Rect;
gp : HMGlobalPtr;
pic : PicHandle;
window : WindowPtr;
oldLayer : LayerPtr;
initialWindowPtr: WindowPtr;
initialPartCode : INTEGER;
procID : INTEGER;
varCode : INTEGER;
wVarCode : INTEGER;
result : OsErr;
oldState : SignedByte;
PROCEDURE __HMSetLastGlobals;
BEGIN
with gp^ do
begin
if alternateRect<>NIL then
hmgSlopRect := alternateRect^ else
SetRect(hmgSlopRect,0,0,0,0);
hmgLastPartCode := initialPartCode;
hmgLastWindowPtr := initialWindowPtr;
if hmgLastWindowPtr<>NIL then
begin
if BAnd($C000,CGrafPtr(hmgLastWindowPtr)^.portVersion)<>0 then
temprect := CGrafPtr(hmgLastWindowPtr)^.portPixMap^^.bounds
else temprect := hmgLastWindowPtr^.portBits.bounds;
with hmgLastWindowPtr^.portRect do
begin
hmgLastWidth := (right-left);
hmgLastHeight := (bottom-top);
hmgLastLeft := temprect.left - left;
hmgLastTop := temprect.top - top;
end;
end else
begin
hmgLastWidth := 0;
hmgLastHeight := 0;
hmgLastLeft := 0;
hmgLastTop := 0;
end;
end;
END;
BEGIN
gp := __HMGetHelpGlobal;
if __HMGetBalloons
{$ifc AllowCloseViewChanges}
& (gp^.hmgCloseViewCount = 0) {CloseView is active, shift key was down, & balloon was up}
{$endc} then
begin
__HMShowBalloon := hmBalloonAborted;
if __HMRemoveBalloon=noErr then;
initialPartCode := __HMGetWindowPartCode(initialWindowPtr);
method := BAnd($0007,method); { strip off hi bits of method }
{ *** we currently support only:
kHMRegularWindow = 0; Create a regular window floating above all windows
kHMSaveBitsNoWindow = 1; Just save the bits and draw (for MDEF calls
kHMSaveBitsWindow = 2; Regular window, save bits behind, AND generate update event
}
if method = kHMSaveBitsNoWindow then
begin
if theProc<>0 then {only support proc 0 when saving bits}
__HMShowBalloon := hmOperationUnsupported else
begin
result := __HMSaveBalloonBits(aHelpMsg,tip,alternateRect,
tipProc,variant,wasWindow);
if result=noErr then
__HMSetLastGlobals;
__HMShowBalloon := result;
end;
end else
begin { it must have been a kHMRegularWindow or kHMSaveBitsWindow method }
GetPort(oldPort);
with gp^ do
if hmgLayer<>NIL then
oldLayer := SwapCurLayer(hmgLayer);
varCode := variant;
result := __HMCalculateBalloon(aHelpMsg,tip,alternateRect,
varCode,bounds,pic,wasWindow,true { will abort balloon if mouse moves });
if result = noErr then
begin
if theProc<>0 then
begin
procID := theProc;
varCode := variant; { override just calculated varCode in __HMCalculateBalloon
as we have our own defProc whose variant could be different }
if GetResource('WDEF',theProc)=NIL then { we couldn't load the custom 'WDEF' }
result := resNotFound;
end else
procID := kBalloonWDEFID; { use our balloon 'WDEF' }
if result = noErr then
with gp^ do
begin
if theProc<>0 then
begin
oldZone := GetZone; { get the current heap zone }
SetZone(SystemZone); { point at the system heap }
{ only call new window if theProc isn't our balloon definition <54> }
window := NewWindow(NIL,bounds,'appBalloon',false,(16 * procID) + varCode,
pointer(-1),false,0);
SetZone(oldZone);
end else
begin
{ change our balloon window's defproc variant to be the new one <54> }
__HMStuffWindowInfo(hmgHelpWindow,varCode,bounds);
{ make the showwindow call recalculate the window's regions <54> }
with WindowPeek(hmgHelpWindow)^ do
begin
SetEmptyRgn(strucRgn);
SetEmptyRgn(contRgn);
end;
{ make the current balloon our window <54> }
window := hmgHelpWindow;
end;
if window<>NIL then
begin
SetPort(window);
if (method = kHMSaveBitsWindow) | (tipProc<>NIL) then { <58> check tip procedure too }
begin
if __HMCallCustomWDEFCalc(wCalcRgns,window)=noErr then;
if tipProc<>NIL then { <58> give the caller a chance to abort }
begin
with WindowPeek(window)^ do { <58> using the balloon window informationÉ }
begin
wBounds := bounds;
wVarCode := varCode;
result := __HMCallTipProc(tipProc,tip,strucRgn,wBounds,wVarCode); { <58> see if caller doesn't like balloon }
if result = noErr then { <58> balloon might have changed a bit, so update info }
if (wVarCode<>varCode) |
(LongInt(wBounds.topLeft)<>LongInt(bounds.topLeft)) |
(LongInt(wBounds.botRight)<>LongInt(bounds.botRight)) then
begin
bounds := wBounds;
varCode := wVarCode;
__HMStuffWindowInfo(window,varCode,bounds); { <58> restuff the window info }
SetEmptyRgn(strucRgn); { <58> will cause window to recalc when visible }
SetEmptyRgn(contRgn);
end;
end;
end;
if result = noErr then { <58> either there wasn't a tip proc or caller didn't abort }
if (method = kHMSaveBitsWindow) then
with WindowPeek(window)^ do
begin
wBounds := strucRgn^^.rgnBBox;
hmgSavedBitsHandle := __HMSaveBitsInRect(wBounds); { save bits behind window b4 putting up window }
{ make the ShowWindow() call recalculate the window's regions }
SetEmptyRgn(strucRgn);
SetEmptyRgn(contRgn);
end;
end;
if result = noErr then
begin
SetWindowPic(window,pic); { <59> Call toolbox instead of WindowPeeking record }
SetWRefCon(window,aHelpMsg.hmmHelpType); { <59> Call toolbox instead of WindowPeeking record }
ShowWindow(window);
{ The code below makes the balloon contents appear immediately
instead of waiting for the window manager to draw the picture }
BeginUpdate(window);
if pic^ = NIL then { <58> the pic was really a resource that was purged }
LoadResource(Handle(pic));
if pic^ <> NIL then { <58> the pic was successfully reloaded (if purged) }
begin
oldState := HGetState(Handle(pic)); { <58> remember pic handle's state }
HNoPurge(Handle(pic)); { <58> keep it around (if resource) }
with pic^^.picFrame do
SetRect(bounds,0,0,right-left,bottom-top); { <58> force picture to window coords }
DrawPicture(pic,bounds);
HSetState(Handle(pic),oldState);
end;
EndUpdate(window);
hmgWindow := window;
end else { there was an abort returned from calling tip proc, so toss the window }
begin
if window <> hmgHelpWindow then
begin
DisposeWindow(window); { <58> Dispose the wind if the window was custom }
pic := NIL; { *** } { <59> NIL picHandle, DisposeWindow() kills the pic automatically }
end;
if (hmgSavedBitsHandle <> NIL) then
begin
if DiscardBits(LongInt(hmgSavedBitsHandle))=noErr then;
hmgSavedBitsHandle := NIL; { <58> NIL this in case someone calls HMRemoveBalloon() }
end;
end;
end;
end;
end;
if result <> noErr then { some sort of error or abort occurred, so toss the picture (if any) }
if pic <> NIL then
if __HMCanPictureBeKilled(aHelpMsg.hmmHelpType) then { <59> KillPicture if type wasn't resource based }
KillPicture(pic);
__HMShowBalloon := result;
with gp^ do
if (hmgLayer<>NIL) then
SetCurLayer(oldLayer);
SetPort(oldPort);
__HMSetLastGlobals;
end;
end else __HMShowBalloon := hmHelpDisabled;
END;
PROCEDURE __HMShowTitleBalloon(menuID: INTEGER; flags: LongInt);
VAR aHelpMsg : HMMessageRecord;
altRect : Rect;
pt : Point;
width : INTEGER;
variant : INTEGER;
resID : INTEGER;
result : OsErr;
gp : HMGlobalPtr;
BEGIN
gp := __HMGetHelpGlobal;
with gp^ do
if (hmgItemNum = 0) then if (menuID = hmgMenuID) then
Exit(__HMShowTitleBalloon);
if __HMGetMenuResID(menuID,resID)<>noErr then
resID := -1;
variant := 0; { default in the case of the Apple Menu }
if __HMFillInMenuMessage(menuID,0,resID,flags,variant,aHelpMsg)<>noErr then
Exit(__HMShowTitleBalloon);
if GetMenuTitleRect(menuID,altRect)=noErr then;
with altRect do
begin
width := right-left;
pt.h := left + BSR(width,1);
pt.v := IntegerPtr(MBarHeight)^;
top := top - 1;
bottom := top + pt.v;
end;
result := __HMShowBalloon(aHelpMsg,pt,@altRect,NIL,0,variant,2);
if result=noErr then
with gp^ do
begin
hmgLastMenuID := menuID;
hmgLastEnabled := flags;
hmgLastItem := 0;
hmgItemNum := 0;
hmgMenuID := menuID;
hmgTitleBalloon := 1; { when this is non-zero, the show hide patch won't toss this balloon }
end;
END;
FUNCTION __HMShowMenuBalloon(itemNum,itemMenuID: INTEGER;
itemFlags,itemReserved: LongInt;
tip: Point; alternateRect: RectPtr; tipProc: Ptr;
theProc,variant: INTEGER): OsErr;
VAR aHelpMsg : HMMessageRecord;
menuBarRect : Rect;
menuHeight : INTEGER;
overVariant : INTEGER;
gp : HMGlobalPtr;
pt : Point;
result : OsErr;
resID : INTEGER;
BEGIN
gp := __HMGetHelpGlobal;
__HMShowMenuBalloon := hmSameAsLastBalloon; {assume that it's already displayed}
if __HMGetBalloons
{$ifc AllowCloseViewChanges}
& (gp^.hmgCloseViewCount = 0) {CloseView is active, shift key was down, & balloon was up}
{$endc}
then
with gp^ do
begin
if (hmgLastItem = itemNum) then
if (hmgLastMenuID = itemMenuID) then
if (hmgLastEnabled = itemFlags) then EXIT(__HMShowMenuBalloon);
if itemNum <= 0 then { if 0 then mouse was in a title, if -1 then it was a dash }
begin
if __HMRemoveBalloon=noErr then; { toss any existing Balloons, if 0 or -1 }
Exit(__HMShowMenuBalloon);
end;
if __HMGetMenuResID(itemMenuID,resID)<>noErr then
resID := -1;
if alternateRect<>NIL then
hmgItemRect := alternateRect^;
hmgItemNum := itemNum;
hmgMenuID := itemMenuID;
overVariant := variant;
{ <59> Ignore itemReserved parameter menu or item is disabled }
if (itemReserved <> 0) then
begin
IF not BTST(itemFlags,0) THEN { The whole menu is disabled }
itemReserved := 0
ELSE IF (itemNum <= 31) THEN { The item has a flag bit }
IF NOT BTST(itemFlags,itemNum) THEN { That item is disabled }
itemReserved := 0;
end;
if (itemReserved = 0) or (ODD(itemReserved)) then { we need to fetch the msg out of the 'hmnu' resource }
begin
result := __HMFillInMenuMessage(itemMenuID,itemNum,resID,itemFlags,overVariant,aHelpMsg);
if result<>noErr then { we didn't find anything in the 'hmnu' or the 'hmnu' was missing }
begin
__HMShowMenuBalloon := result;
exit(__HMShowMenuBalloon);
end;
end else { let's assume that itemReserved is a StringHandle to the message string }
with aHelpMsg do
begin
hmmHelpType := khmmString;
hmmString := StringHandle(itemReserved)^^; { copy the string data }
result := noErr;
end;
with aHelpMsg do
if hmmHelpType = khmmString then
if length(hmmString)=0 then
begin
__HMShowMenuBalloon := paramErr;
exit(__HMShowMenuBalloon);
end;
if overVariant<>0 then
variant := overVariant;
result := __HMSaveBalloonBits(aHelpMsg,tip,alternateRect,tipProc,variant,wasMenu);
if result=noErr then
begin
if alternateRect<>NIL then
hmgSlopRect := alternateRect^
else SetRect(hmgSlopRect,0,0,0,0);
hmgLastItem := itemNum;
hmgLastMenuID := itemMenuID;
hmgLastEnabled := itemFlags;
end;
end else
result := hmHelpDisabled;
__HMShowMenuBalloon := result;
END;
FUNCTION __HMSetFont(font: INTEGER): OsErr;
VAR fontlong : LongInt;
BEGIN
__HMSetGetFontSizeGlobal(kGetFont,fontlong);
with __HMGetHelpGlobal^ do
begin
hmgFontAndSize.hmgFont := font;
__HMSetGetFontSizeGlobal(kSetFont,LongInt(hmgFontAndSize));
end;
__HMSetFont := noErr;
END;
FUNCTION __HMSetFontSize(fontSize: INTEGER): OsErr;
VAR fontlong : LongInt;
BEGIN
__HMSetGetFontSizeGlobal(kGetFont,fontlong);
with __HMGetHelpGlobal^ do
begin
hmgFontAndSize.hmgFontSize := fontSize;
__HMSetGetFontSizeGlobal(kSetFont,LongInt(hmgFontAndSize));
end;
__HMSetFontSize := noErr;
END;
FUNCTION __HMGetFont(VAR font: INTEGER): OsErr;
BEGIN
font := __HMGetHelpGlobal^.hmgFontAndSize.hmgFont;
__HMGetFont := noErr;
END;
FUNCTION __HMGetFontSize(VAR fontSize: INTEGER): OsErr;
BEGIN
fontSize := __HMGetHelpGlobal^.hmgFontAndSize.hmgFontSize;
__HMGetFontSize := noErr;
END;
{ <58> HMFind now will find BOTH menu & dialog PSN driven indices in open apps }
FUNCTION __HMFind(queue: HHMHelpQueueRecord; theMenuID: INTEGER;
VAR index: INTEGER; PSN: ProcessSerialNumber): INTEGER;
VAR i : INTEGER;
count : INTEGER;
BEGIN
__HMFind := -1; { say we didn't find it in the array }
if queue<>NIL then
begin
count := queue^^.queueCount;
for i := 1 to count do
with queue^^.queueRecords[i] do
begin
if (theMenuID = slotID) then { theMenuID will always be equal when searching the dialog queue }
if (PSN.lowLongOfPSN = slotPID.lowLongOfPSN) then
if (PSN.highLongOfPSN = slotPID.highLongOfPSN) then
begin
__HMFind := slotResID;
index := i;
leave;
end;
end;
end;
END;
FUNCTION __HMSetDialogResID(theResID: INTEGER): OsErr; { <58> rewritten to work with the current PSN instead of just one index }
VAR i : INTEGER;
result : OsErr;
offset : LongInt;
p : LongInt;
queue : HHMHelpQueueRecord;
PSN : ProcessSerialNumber;
BEGIN
result := GetCurrentProcess(PSN); { <58> make this work for the current process who is assigning the res ID }
if result=noErr then { <58> only use Process IDs that are good }
begin
queue := __HMGetHelpGlobal^.hmgDialogQueue; { <58> use the dialog queue }
if __HMFind(queue,0 {allow only 1 entry in dialogs},i,PSN)<>-1 then { <58> we found a setup index, just replace the record's data }
begin
if theResID = -1 then { <58> remove this record from our queue }
begin
{believe it or not, the calculation below IS 32 bit clean}
p := ord4(@queue^^.queueRecords[i]); { <58> }
offset := p - ord4(queue^); { <58> }
{ <58> this does a SetHandleSize smaller so it should always (gulp!) succeed }
if Munger(Handle(queue),offset,NIL,SIZEOF(HMHelpSlot),Ptr(-1),0)=0 then; { <58> }
with queue^^ do
queueCount := queueCount - 1; { <58> }
{ *** <58> do we need to hide any currently displayed balloon here? }
end else
queue^^.queueRecords[i].slotResID := theResID; { <58> }
end else { <58> the ID returned from HMFind was -1 which means add a new element to the queue }
begin
SetHandleSize(Handle(queue), GetHandleSize(Handle(queue)) + SIZEOF(HMHelpSlot)); { <58> }
result := memError; { <58> }
if result=noErr then { <58> }
begin
with queue^^ do { <58> }
begin
queueCount := queueCount + 1;
with queueRecords[queueCount] do { <58> }
begin
{queueRecords[queueCount].}slotPID := PSN; { <58> }
{queueRecords[queueCount].}slotResID := theResID; { <58> }
{queueRecords[queueCount].}slotID := 0; { <58> }
end;
end;
end;
end;
end;
__HMSetDialogResID := result; { <58> }
END;
FUNCTION __HMGetDialogResID(VAR theResID: INTEGER): OsErr;
VAR i : INTEGER;
PSN : ProcessSerialNumber;
BEGIN
__HMGetDialogResID := resNotFound; { <58> assume that we didn't find any dialog res id assignment }
with __HMGetHelpGlobal^ do
begin
if hmgDialogQueue^^.queueCount > 0 then { <58> quick check to see if anybody has ever overridden any dialog res ID }
if GetCurrentProcess(PSN)=noErr then { <58> only use Process IDs that are good }
begin
theResID := __HMFind(hmgDialogQueue,0,i,PSN); { <58> returns assigned res id for the PSN's dialog override }
if theResID<>-1 then
__HMGetDialogResID := noErr; { <58> return noErr if we found an assignment }
end;
end;
END;
FUNCTION __HMSetMenuResID(theMenuID,theResID: INTEGER): OsErr; { <58> now can return ProcessMgr error by calling GetCurrentProcess }
VAR i : INTEGER;
result : OsErr;
offset : LongInt;
p : LongInt;
queue : HHMHelpQueueRecord;
PSN : ProcessSerialNumber;
BEGIN
result := GetCurrentProcess(PSN);
if result=noErr then { only use Process IDs that are good }
begin
__HMSetMenuResID := noErr;
queue := __HMGetHelpGlobal^.hmgMenuQueue;
if __HMFind(queue,theMenuID,i,PSN)<>-1 then { we found this theMenuID in the list already, returns found index in i for array }
begin
if theResID = -1 then { remove this record }
begin
{believe it or not, the calculation below IS 32 bit clean}
p := ord4(@queue^^.queueRecords[i]); {ord(Handle(queue^^.queueRecords[i])^);}
offset := p - ord4(queue^);
{ the following routine will remove the data at the given offset:
offset := p - ord(queue^);
count to remove := SIZEOF(record); (size of our record HMHelpSlot)
the trick to munger in this case below is that the last parameter must be zero
as we're not replacing anything & munger calls SetHandleSize for us!
and since this is a delete (SetHandleSize smaller), it will always (gulp!) succeedÉ }
if Munger(Handle(queue),offset,NIL,SIZEOF(HMHelpSlot),Ptr(-1),0)=0 then;
with queue^^ do
queueCount := queueCount - 1;
end else
queue^^.queueRecords[i].slotResID := theResID;
end else { the ID was -1 which means add a new record }
begin
SetHandleSize(Handle(queue), GetHandleSize(Handle(queue)) + SIZEOF(HMHelpSlot));
result := memError;
if result=noErr then
begin
with queue^^ do
begin
queueCount := queueCount + 1;
with queueRecords[queueCount] do
begin
{queueRecords[queueCount].}slotPID := PSN;
{queueRecords[queueCount].}slotResID := theResID;
{queueRecords[queueCount].}slotID := theMenuID;
end;
end;
end;
end;
end;
__HMSetMenuResID := result;
END;
FUNCTION __HMGetMenuResID(theMenuID: INTEGER; VAR theResID: INTEGER): OsErr;
VAR i : INTEGER;
PSN : ProcessSerialNumber;
BEGIN
__HMGetMenuResID := resNotFound; { assume that we didn't find any resource }
{ <58> note: don't bother with quick check that HMGetDialogResID does by checking the queueCount field
in the menu queue. Finder always overrides the hmnu res id's when it is launched, so, there
is always something in the queue }
if GetCurrentProcess(PSN)=noErr then { only use Process IDs that are good }
begin
theResID := __HMFind(__HMGetHelpGlobal^.hmgMenuQueue,theMenuID,i,PSN); { <58> pass the queue now }
if theResID<>-1 then
__HMGetMenuResID := noErr;
end;
END;
FUNCTION __HMModalDialogMenuSetup(ModalDialogComingUp: BOOLEAN): OsErr;
VAR mh : MenuHandle;
i : INTEGER;
BEGIN
__HMModalDialogMenuSetup := noErr;
mh := GetMHandle(kHMHelpMenuID);
if (mh<>NIL) then
begin
for i := 1 to CountMItems(mh) do
begin
if (i<>kHMShowBalloonsItem) then
if (ModalDialogComingUp) then
DisableItem(mh,i) else
EnableItem(mh,i);
end;
end else
__HMModalDialogMenuSetup := hmHelpManagerNotInited;
END;
FUNCTION __HMGetScreenGDevice(pt: Point): GDHandle;
VAR gh : GDHandle;
BEGIN
gh := GetDeviceList;
while gh<>NIL do
begin
if TestDeviceAttribute(gh,screenDevice) then
if TestDeviceAttribute(gh,screenActive) then
if PtInRect(pt,gh^^.gdRect) then
leave;
gh := GetNextDevice(gh); { *** faster if we use: gh^^.gdNextGD }
end;
__HMGetScreenGDevice := gh;
END;
PROCEDURE __HMGetVariantRect(rbs: RBSRecord; VAR fullRect: Rect); { Returns the rect that can encompass the balloon }
VAR theword : IntegerPtr;
BEGIN
with fullRect do
begin
theword := @right;
if BTST(rbs.whole, kShiftBit) then
theword := @bottom;
end;
theword^ := theword^ + kHMPointerSize;
END;
PROCEDURE __HMSelectProperVariant( balloonBounds: Rect; { bounds of balloon w/o tip }
altRect: Rect; { alternate rectangle }
screenRect: Rect; { bounds of monitor we're about to display on }
VAR rbs: RBSRecord; { balloon's variant word, right, bottom, shift values }
VAR tip: Point { balloon's initial tip position }
);
CONST
horizontal = 0;
vertical = 1;
VAR dL : INTEGER;
dR : INTEGER;
dT : INTEGER;
dB : INTEGER;
retries : INTEGER;
width : INTEGER;
height : INTEGER;
border : INTEGER;
deltaH : INTEGER;
deltaV : INTEGER;
whichAxis : INTEGER;
axisCount : INTEGER;
hangRight : INTEGER; { 1 if balloon hangs right }
hangDown : INTEGER; { 1 if balloon hangs down }
hangShift : INTEGER; { 1 if balloon has a vertical tip }
bestRight : INTEGER;
bestDown : INTEGER;
bestShift : INTEGER;
bestTip : Point;
bestArea : LongInt;
area : LongInt;
areaH : LongInt;
areaV : LongInt;
destRect : Rect;
areaRect : Rect;
{ ¥ First: See if tip's position delegates having a vertical (shift = 1) tip.
¥ Second: Try the horizontal position of the balloon to see if it fits with the
original tip and variant. The routine below is written to calculate balloons
in a specific order. Instead of just computing a balloon that fits, we walk
thru choices of variants to produce what appears better rather than what is
just mathematically appropriate.
Every second time that a variant is changed, the balloon
shift (whether or not a vertical tip is used) is toggled. This ensures that
every variant is tried in order to get the balloon to fit without clipping.
¥ Third: Try the vertical position.
¥ Fourth: If all variants have been tried then use the best balloon that we had calculated
along the way. The best balloon is the one with the most area.
}
BEGIN
border := IntegerPtr(MBarHeight)^ + 2; { compute the menubar's height plus room for the line & 1 pix }
hangRight := rbs.rightValue; { get right hang direction in local (if hangRight = 1 then the balloon hangs right) }
hangDown := rbs.bottomValue; { get downward hang direction in local (if hangDown = 1 then the balloon hangs down) }
hangShift := rbs.shiftValue; { get shift value in local (if hangShift = 1 then the balloon has a vertical tip) }
if hangShift = 0 then
with screenRect do
if (tip.v < top + border) or
(tip.v > bottom - border) then { the balloon tip is in menubar area or near very bottom of screen bounds }
begin
hangShift := 1; { force a vertical tip as we're near the top or bottom of screen }
end;
whichAxis := horizontal; { start with the horizontal axis first }
axisCount := 0; { reset the counter for keeping track of how many times we've tried either horizontal or vertically }
bestArea := -1; { initialize the bestArea local that keeps track of the balloon with the greatest area }
for retries := 1 to 8 do { keep computing until we tried 8 times }
begin
destRect := balloonBounds; { reset destination rect to be the balloon bounds EACH time thru the loop }
with destRect do { recalculate the dimensions of the balloon }
begin
if hangShift = 0 then
right := right + kHMPointerSize
else bottom := bottom + kHMPointerSize; { include space for tip dimensions }
width := right - left + kHMHorizFrameMargin; { include balloon horizontal margin }
height := bottom - top + kHMVertFrameMargin; { include balloon vertical margin }
end;
with screenRect do
begin
dR := (right - tip.h); { compute the space between the tip and the right edge of the screen bounds }
dL := (tip.h - left); { compute the space between the tip and the left edge of the screen bounds }
dB := (bottom - tip.v); { compute the space between the tip and the bottom edge of the screen bounds }
dT := (tip.v - (top + border)); { compute the space between the tip and the top edge of the screen bounds (minus border) }
end;
deltaH := dR; { Assume balloon hangs right so use deltaH for right }
if hangRight = 0 then { T => Nope, it hangs left, so use left deltaH }
deltaH := dL;
areaH := width; { remember this balloon's width so that if no balloons fit, we }
if width > deltaH then { can use the 'best' balloon as a last resortÉ }
areaH := deltaH;
deltaV := dB; { Assume balloon hangs down so use deltaV for bottom }
if hangDown = 0 then { T => Nope, it hangs up, so use top deltaV }
deltaV := dT;
areaV := height; { remember this balloon's height so that if no balloons fit, we }
if height > deltaV then { can use the 'best' balloon as a last resortÉ }
areaV := deltaV;
area := areaV * areaH; { compute this balloons area in case all balloons fail to fit }
if area > bestArea then { this balloon's area was larger than the last, so copy the balloon's params }
begin
bestRight := hangRight;
bestDown := hangDown;
bestShift := hangShift;
bestTip := tip;
bestArea := area; { and save the bestArea for the next go 'round }
end;
case whichAxis of
horizontal:
begin
if (width > deltaH) then { the balloon was too wide to fit, try tweeking the tip &/or variant }
begin
axisCount := axisCount + 1; { increment the counter for walking thru the balloon variants }
if axisCount <> 4 then { don't move the tip the 4th time we've checked the horiz size }
begin
tip.h := altRect.left + (altRect.right - tip.h); { move tip to the opposite side of the altRect }
end else
hangShift := 1 - hangShift; { change from a vertical tip to a horizontal one or horiz to vertical }
hangRight := 1 - hangRight; { change the balloon hanging right to hanging left or left to right }
end else
begin { we successfully fit horizontally }
whichAxis := vertical; { so now try the vertical }
axisCount := 0; { reset the counter }
cycle; { to the next for retries }
end;
end;
vertical:
begin
if (height > deltaV) then { the balloon was too tall to fit, try tweeking the tip &/or variant }
begin
axisCount := axisCount + 1; { increment the counter for walking thru the balloon variants }
if axisCount <> 4 then { don't move the tip the 4th time we've checked the vertical size }
begin
tip.v := altRect.top + (altRect.bottom - tip.v); { move tip to the opposite side of the altRect }
end else
hangShift := 1 - hangShift; { change from a vertical tip to a horizontal one or horiz to vertical }
hangDown := 1 - hangDown; { change the balloon hanging down to hanging up or down to up }
end else
begin
leave; { both the horizontal and vertical fit so exit now }
end;
end;
end;
if retries >= 8 then { balloon didn't fit, use the which one was best }
begin
hangRight := bestRight;
hangDown := bestDown;
hangShift := bestShift;
tip := bestTip;
leave;
end
else if (axisCount mod 2) = 0 then { every second try, change the shift factor }
begin
hangShift := 1 - hangShift; { change from a vertical tip to a horizontal one or horiz to vertical }
end;
end;
rbs.rightValue := hangRight; { reset the rbs value to use the just tweaked (possibly) hanging values }
rbs.bottomValue := hangDown;
rbs.shiftValue := hangShift;
END;
FUNCTION __HMPinTipAndAltRect(screenBounds: Rect; VAR tip: Point; VAR altRect: Rect): BOOLEAN; { Returns false if no intersection }
VAR deltaLeft : LongInt;
deltaTop : LongInt;
width,height : LongInt;
newAltRect : Rect;
insideScreenBounds : BOOLEAN;
BEGIN
{ see if the altRect is really in the screen bounds (and clip it to the screen bounds at the same time) }
insideScreenBounds := SectRect(screenBounds,altRect,newAltRect);
if insideScreenBounds then { at least part of the altRect is visible }
begin
if not EqualRect(altRect,newAltRect) then { if part of the altRect is clipped, then use the ratio for the tip inside the altRect }
begin
{ use the original altRect to determine the tip deltas between the right & left, top & bottom with respect to the tip }
with altRect do
begin
deltaLeft := (tip.h - left); { this is the space between the right edge of the altRect and the horizontal part of the tip }
deltaTop := (tip.v - top);
width := right - left; { get old altRect width and height }
height := bottom - top;
end;
if width = 0 then width := 1; { make sure were not zero }
if height = 0 then height := 1;
with newAltRect do
begin
tip.h := left + (ORD4((right - left) * deltaLeft) div width); { compute ratio of tip within rect }
tip.v := top + (ORD4((bottom - top) * deltaTop) div height);
end;
altRect := newAltRect;
LongInt(tip) := PinRect(altRect,tip); { always repin the tip to the new altRect }
end;
end;
__HMPinTipAndAltRect := insideScreenBounds;
END;
FUNCTION __HMBestWorld(worldBounds: Rect; tip: Point; areaRect: Rect; balloonType: INTEGER;
VAR balloonBounds: Rect; VAR variant: INTEGER): OsErr;
VAR rbs : RBSRecord;
i,j : INTEGER;
deltaL : INTEGER;
deltaR : INTEGER;
deltaT : INTEGER;
deltaB : INTEGER;
result : INTEGER;
boundsWidth : INTEGER;
boundsHeight : INTEGER;
tweek : INTEGER;
moveValue : Point;
destRect : Rect;
offset : Point;
rbsOK : BOOLEAN;
oldPort : GrafPtr;
BEGIN
rbsOK := false;
__HMTwitchVarCode(kVariantToRBS,variant,rbs.whole);
{ Compute the 'best' rbs that will fit the balloon in the worldBounds }
__HMSelectProperVariant(balloonBounds, areaRect, worldBounds, rbs, tip);
destRect := balloonBounds; { start with the balloon bounds }
__HMGetVariantRect(rbs,destRect); { tweak the bounds to take in account the tip placement }
with balloonBounds do
begin
boundsWidth := right - left;
boundsHeight := bottom - top;
left := tip.h - ((1-rbs.rightValue) * (destRect.right - destRect.left));
top := tip.v - ((1-rbs.bottomValue) * (destRect.bottom - destRect.top));
right := left + boundsWidth;
bottom := top + boundsHeight;
end;
LongInt(offset) := 0;
if BTST(rbs.whole,kShiftBit) then
offset.v := kHMPointerSize * rbs.bottomValue
else
offset.h := kHMPointerSize * rbs.rightValue;
offset.h := offset.h + (rbs.rightValue * kHMFrameThickness) - ((1-rbs.rightValue) * kHMPointerSize);
offset.v := offset.v + (rbs.bottomValue * kHMFrameThickness) - ((1-rbs.bottomValue) * kHMPointerSize);
OffsetRect(balloonBounds,offset.h,offset.v);
__HMTwitchVarCode(kRBSToVariant,INTEGER(rbs),variant);
__HMBestWorld := noErr;
END;
FUNCTION __HMCalculateBalloon( aHelpMsg: HMMessageRecord;
tip: Point;
alternateRect: RectPtr;
VAR varCode: INTEGER;
VAR bounds: Rect; VAR pic: PicHandle;
balloonType: INTEGER;
abortable: BOOLEAN): OsErr;
VAR gp : HMGlobalPtr;
screenBounds : Rect;
areaRect : Rect;
visibleRect : Rect;
gh : GDHandle;
oldDevice : GDHandle;
oldPort : GrafPtr;
tPort : GrafPtr;
picPort : GrafPort;
err : INTEGER;
pt : Point;
slopTip : Point;
aTE : TEHandle;
tix : LongInt;
sloppy : BOOLEAN;
hasColorQD : BOOLEAN;
oldZone : THz; { <SM2> }
BEGIN
__HMCalculateBalloon := hmBalloonAborted; { make default case aborted }
gp := __HMGetHelpGlobal;
pic := NIL; { <59> make sure pic is NIL in case we exit early }
if abortable then
begin
tix := Tickcount;
with gp^ do
begin
if (hmgDelay - (tix - hmgTix)) > 0 then
exit(__HMCalculateBalloon); { exit if not enough time has elapsed }
hmgTix := tix;
end;
end;
oldDevice := NIL;
sloppy := false;
slopTip := __HMGetGlobalMouse;
hasColorQD := gp^.hmgHasColorQD;
if hasColorQD then
begin
{ *** do we want to use the tip's GDevice or the mouse point's ? }
gh := __HMGetScreenGDevice(slopTip); { use the mouse's GDevice }
if gh<>NIL then
begin
screenBounds := gh^^.gdRect;
oldDevice := GetGDevice;
SetGDevice(gh);
end else
exit(__HMCalculateBalloon); { how could the mouse be offscreen? }
end else
begin
screenBounds := GetGrayRgn^^.rgnBBox;
screenBounds.top := 0;
end;
if alternateRect<>NIL then
begin
areaRect := alternateRect^;
if not __HMPinTipAndAltRect(screenBounds,tip,areaRect) then
begin
if oldDevice<>NIL then { =NIL for non-color ports }
SetGDevice(oldDevice);
exit(__HMCalculateBalloon);
end;
end else
begin
LongInt(tip) := PinRect(screenBounds,tip);
with areaRect do
begin
topLeft := tip;
bottom := tip.v + 1;
right := tip.h + 1;
end;
end;
{$ifc HelpMgrTesting}
Assert(EmptyRect(areaRect),'HMCalculateBalloon: Empty area Rect!');
{$endc}
GetPort(oldPort);
{if hasColorQD then
OpenCPort(@picPort)
else }OpenPort(@picPort);
SetPort(@picPort);
with gp^ do
begin
TextFont(hmgFontAndSize.hmgFont);
TextSize(hmgFontAndSize.hmgFontSize);
hmgKeepTEHandle := true; {we don't want the __HMBalloonRect() call to toss the TEHandle (if any)}
hmgTEHandle := NIL; {in case __HMBalloonRect fails}
end;
SetRect(bounds,0,0,0,0);
err := __HMBalloonRect(aHelpMsg,bounds);
__HMCalculateBalloon := err;
if err=noErr then
begin
err := __HMBestWorld(screenBounds,tip,areaRect,balloonType,bounds,varCode);
{$ifc HelpMgrTesting}
if err<>noErr then
DebugStr('Unable to fit balloon on screenÉ');
{$endc}
end;
if err<>noErr then
begin
{if hasColorQD then
CloseCPort(@picPort)
else} ClosePort(@picPort);
SetPort(oldPort);
if oldDevice<>NIL then { always NIL for non-color ports }
SetGDevice(oldDevice);
exit(__HMCalculateBalloon);
end;
with bounds do
begin
SetRect(visibleRect,0,0,right-left,bottom-top);
right := right + kHMHorizContentMargin;
bottom := bottom + kHMVertContentMargin;
end;
aTE := gp^.hmgTEHandle;
if not __HMSlopMouse(slopTip) or not abortable then
begin
with aHelpMsg do
case hmmHelpType of
khmmPict,khmmPictHandle:
if hmmHelpType = khmmPict then
pic := PicHandle(GetResource('PICT',hmmPict))
else pic := hmmPictHandle;
khmmTEHandle:
begin
aTE := hmmTEHandle;
if aTE<>NIL then
begin
with aTE^^ do
begin
tPort := inPort;
inPort := @picPort;
end;
oldZone := GetZone; { get the current heap zone } { <SM2> }
SetZone(SystemZone); { point at the system heap } { <SM2> }
{ <SM2> }
pic := OpenPicture(visibleRect);
TEUpdate(visibleRect,aTE);
ClosePicture;
SetZone(oldZone); { <SM2> }
{ <SM2> }
with aTE^^ do
inPort := tPort;
end;
end;
otherwise
if aTE<>NIL then
begin
oldZone := GetZone; { get the current heap zone } { <SM2> }
SetZone(SystemZone); { point at the system heap } { <SM2> }
{ <SM2> }
pic := OpenPicture(visibleRect);
TEUpdate(visibleRect,aTE);
ClosePicture;
{ <SM2> }
SetZone(oldZone); { <SM2> }
{ <SM2> }
end;
end;
end else Sloppy := true;
if aHelpMsg.hmmHelpType<>khmmTEHandle then
if aTE<>NIL then
TEDispose(aTE);
with gp^ do
begin
hmgTEHandle := NIL;
hmgKeepTEHandle := false;
end;
{if hasColorQD then
CloseCPort(@picPort)
else} ClosePort(@picPort);
SetPort(oldPort);
if oldDevice<>NIL then {=NIL for non-color ports}
SetGDevice(oldDevice);
if Sloppy | (pic = NIL) then
begin
gp^.hmgItemNum := -1;
__HMCalculateBalloon := memFullErr;
if pic<>NIL then
begin
if __HMCanPictureBeKilled(aHelpMsg.hmmHelpType) then { <59> Kill picture if it wasn't resource based }
KillPicture(pic);
pic := NIL;
end;
if Sloppy then
__HMCalculateBalloon := hmBalloonAborted;
exit(__HMCalculateBalloon);
end;
{ <58> the third call to HMSlopMouse below means that HMCalculate can return w/ an error
but still return (in VAR pic) a valid picture handle, the calling code must call
KillPicture() to dispose of it in this case!! Memory leak bug in 7.0b1 }
if (abortable and __HMSlopMouse(slopTip)) or EmptyRect(bounds) then { <58> add abortable }
__HMCalculateBalloon := hmBalloonAborted;
END;
FUNCTION __HMSetupBalloonRgns( varCode: INTEGER;
VAR structRgn: RgnHandle;
VAR contentRgn: RgnHandle;
VAR bounds: Rect): OsErr;
{ Balloon Tip Variants
The diagram below relates the varcode parameter value to the bubble
tip position and orientation. The table below shows the Unit
Vectors which are used to draw the tip.
1 2
* *
|\ /|
| \ / | NOTES
0 *---------------------* 3
\ | | / a. The 8 asterisks indicate the 8
\| |/ bubble tips.
| Bubble |
| Structure | b. The digits are the varcode values for each
| Rectangle | bubble tip.
/| |\
/ | | \
7 *---------------------* 4 c. The hCorner,vCorner columns below are a unit vector from
| / \ | the corner to the bubble tip.
|/ \|
* * d. The hBase,vBase columns are a unit vector from the
bubble tip to the base of the diagonal.
6 5
Pointing
varcode Hotspot Location Direction hCorner vCorner hBase vBase
------- ---------------- --------- ------ ------- ------- -------
0 Top, Left Left -1 0 1 1
1 Top, Left Up 0 -1 1 1
2 Top, Right Up 0 -1 -1 1
3 Top, Right Right 1 0 -1 1
4 Bottom, Right Right 1 0 -1 -1
5 Bottom, Right Down 0 1 -1 -1
6 Bottom, Left Down 0 1 1 -1
7 Bottom, Left Left -1 0 1 -1
}
VAR frame : Rect;
originalFrame : Rect;
tempRgn : RgnHandle;
bias : biasRecord;
BEGIN
RectRgn(contentRgn, bounds);
frame := bounds;
InsetRect(frame,-kHMFrameThickness,-kHMFrameThickness);
originalFrame := frame;
varCode := BAnd($0007,varCode); { strip off hi bits of varCode, in case the varCode is out of range }
with frame.topleft do
begin
if varCode >= 4 then
v := frame.bottom;
if (varCode >= 2) then if (varCode <= 5) then
h := frame.right;
__HMVarCodeToBiasRecord(varCode,bias);
with bias do
begin
h := h + (kHMPointerSize * hCornerVector);
v := v + (kHMPointerSize * vCornerVector);
OpenRgn;
FrameRoundRect(originalFrame, kHMCornerDiameter, kHMCornerDiameter);
MoveTo(h,v);
Line(hBaseVector * kHMPointerSize,VBaseVector * kHMPointerSize);
Line(DicksOffsetPixels * hShift, DicksOffsetPixels * vShift);
LineTo(h,v);
CloseRgn(structRgn);
end;
end;
tempRgn := GetGrayRgn;
SectRgn(tempRgn, structRgn, structRgn);
SectRgn(tempRgn, contentRgn, contentRgn);
__HMSetupBalloonRgns := noErr;
END;
FUNCTION __HMDrawBalloonFrame(rgn: RgnHandle): OsErr;
VAR oldClip : RgnHandle;
BEGIN
oldClip := NewRgn;
{$ifc HelpMgrTesting}
Assert(oldClip=NIL,'HMDrawBalloonFrame[1]: NIL Region for NewRgn call');
{$endc}
GetClip(oldClip);
EraseRgn(rgn);
FrameRgn(rgn);
SetClip(rgn);
OffsetRgn(rgn, -1, -1);
FrameRgn(rgn);
OffsetRgn(rgn, 1, 1);
SetClip(oldClip);
__HMDisposeRgn(oldClip);
__HMDrawBalloonFrame := noErr;
END;
FUNCTION __HMSaveBalloonBits(aHelpMsg: HMMessageRecord;
tip: Point;
alternateRect: RectPtr;
tipProc: Ptr;
variant: INTEGER;
balloonType: INTEGER): OsErr;
VAR bounds : Rect;
bitsbounds : Rect;
oldBounds : Rect;
pic : PicHandle;
result : OsErr;
varCode : INTEGER;
wVarCode : INTEGER;
wBounds : Rect;
oldPort : GrafPtr;
wPort : GrafPtr;
oldClipRgn : RgnHandle;
wideOpenRgn : RgnHandle;
aStructRgn : RgnHandle;
aContentRgn : RgnHandle;
oldState : SignedByte;
BEGIN
GetPort(oldPort);
GetWMgrPort(wPort);
SetPort(wPort);
oldClipRgn := NewRgn;
if oldClipRgn<>NIL then
begin
GetClip(oldClipRgn);
wideOpenRgn := NewRgn;
if wideOpenRgn<>NIL then
begin
SetRectRgn(wideOpenRgn,-32768,-32768,32767,32767);
SetClip(wideOpenRgn);
__HMDisposeRgn(wideOpenRgn);
end;
varCode := variant;
result := __HMCalculateBalloon(aHelpMsg,tip,alternateRect,varCode,bounds,pic,balloonType,true{can be aborted});
if result = noErr then { we can display a balloon }
begin
if __HMRemoveBalloon=noErr then; { toss any existing Balloons, if we get this far }
aStructRgn := NewRgn;
aContentRgn := NewRgn;
result := __HMSetupBalloonRgns(varCode,aStructRgn,aContentRgn,bounds);
if tipProc<>NIL then
begin
wBounds := bounds;
wVarCode := varCode;
result := __HMCallTipProc(tipProc,tip,aStructRgn,wBounds,wVarCode); { <58> give the caller a chance to abort }
if result = noErr then { <58> balloon might have changed a bit, so update info }
if (wVarCode<>varCode) |
(LongInt(wBounds.topLeft)<>LongInt(bounds.topLeft)) |
(LongInt(wBounds.botRight)<>LongInt(bounds.botRight)) then
begin
bounds := wBounds;
varCode := wVarCode;
if __HMSetupBalloonRgns(varCode,aStructRgn,aContentRgn,bounds)=noErr then; { <58> recalculate the balloon's regions }
end;
end else
result := noErr;
if result = noErr then
with __HMGetHelpGlobal^ do
begin
bitsbounds := aStructRgn^^.rgnBBox;
hmgSavedBitsHandle := __HMSaveBitsInRect(bitsbounds);
if hmgSavedBitsHandle<>NIL then
begin
if __HMDrawBalloonFrame(aStructRgn)=noErr then;
if pic<>NIL then
begin
SetClip(aContentRgn);
if pic^ = NIL then { <58> the pic was really a resource that was purged }
LoadResource(Handle(pic));
if pic^ <> NIL then { <58> the pic was successfully reloaded (if purged) }
begin
oldState := HGetState(Handle(pic)); { <58> remember pic handle's state }
HNoPurge(Handle(pic)); { <58> keep it around (if resource) }
oldBounds := pic^^.picFrame; { <58> keep old picture bounds as we're offsetting the frame }
with pic^^.picFrame do
begin
bounds.right := bounds.left + (right-left);
bounds.bottom := bounds.top + (bottom-top);
end;
DrawPicture(pic,bounds);
pic^^.picFrame := oldBounds; { <58> restore old picture bounds }
HSetState(Handle(pic),oldState); { <58> restore picture handle state }
end;
end;
end else
result := memFullErr;
end;
__HMDisposeRgn(aStructRgn);
__HMDisposeRgn(aContentRgn);
end;
if (pic<>NIL) then
if __HMCanPictureBeKilled(aHelpMsg.hmmHelpType) then { <59> Kill picture if it wasn't resource based }
KillPicture(pic); { bug before <58> [used to be before DisposeRgn(aStructRgn) above] }
SetClip(oldClipRgn);
DisposeRgn(oldClipRgn); { we don't need to NIL check handle here }
end else
result := memFullErr;
SetPort(oldPort);
__HMSaveBalloonBits := result;
END;
FUNCTION __HMGetHelpItemResID(d: DialogPtr; whichOne: INTEGER; VAR kind: INTEGER; VAR offset: INTEGER): INTEGER;
VAR numItems : INTEGER;
dataSize : INTEGER;
i : INTEGER;
hItems : hItemList;
pItem : pDITLItem;
USB : SByteArray;
count : INTEGER;
BEGIN
__HMGetHelpItemResID := -1;
count := 1;
hItems := hItemList(DialogPeek(d)^.items);
numItems := hItems^^.dlgMaxIndex + 1;
pItem := @hItems^^.DITLItems;
for i := 1 to numItems do { code below is handle safe! }
begin
USB.Int := 0;
USB.SBArray[1] := pItem^.itmData[0];
with pItem^ do
if BAnd(itmType,$7F)=kHelpItem then
if count = whichOne then
begin
kind := IntegerPtr(@itmdata[kHelpItemType])^;
{ always check the ranges we know in case in the future we use HelpItem for something else }
if (kind = kScanhdlg) or (kind = kScanhrct) or (kind = kScanAppendhdlg) then
begin
__HMGetHelpItemResID := IntegerPtr(@itmdata[kHelpItemData])^;
offset := 0;
if kind = kScanAppendhdlg then
offset := IntegerPtr(@itmdata[kHelpItemOffset])^;
end;
leave;
end else
count := count + 1;
dataSize := BAnd(USB.Int + 1, $FFFE);
pItem := pDITLItem(ptr(ord4(@pItem^) + datasize + sizeof(DITLItem)));
end;
END;
FUNCTION __HMCountDITLHelpItems(d: DialogPtr): INTEGER;
VAR numItems : INTEGER;
i : INTEGER;
itemType : INTEGER;
itemHandle : Handle;
itemRect : Rect;
count : INTEGER;
BEGIN
count := 0;
numItems := hItemList(DialogPeek(d)^.items)^^.dlgMaxIndex + 1;
for i := 1 to numItems do
begin
GetDItem(d,i,itemType,itemHandle,itemRect);
if BAnd(itemType,$7F)=kHelpItem then
count := count + 1;
end;
__HMCountDITLHelpItems := count;
END;
FUNCTION __HMSameItemState(theDialog: DialogPtr; thePoint: Point;
VAR item: INTEGER; VAR state: INTEGER; VAR itemRect: Rect): BOOLEAN;
{ Finds an item in theDialog at thePoint and returns true if the item & state were the same, therefore,
no balloon needs to be redrawn. It returns the item & state as VAR params.
}
VAR value : INTEGER;
itemType : INTEGER;
itemHandle : Handle;
BEGIN
__HMSameItemState := false; { say that we need to redraw the balloon }
item := FindDItem(theDialog,thePoint);
if item >= 0 then
begin
item := item + 1; { convert to GetDItem offset (always 1 based) }
GetDItem(theDialog,item,itemType,itemHandle,itemRect); { get the item's info }
if BAnd(itemType,$80)<>0 then { item was disabled }
state := kHMDisabledItem else
state := kHMEnabledItem; { otherwise it was enabled }
value := BAnd(itemType,$7F); { toss item's disabled bit }
if (value >= ctrlItem + btnCtrl) then
if (value <= ctrlItem + resCtrl) then { if item is a button, checkbox, or 'CNTL' thenÉ }
with ControlHandle(itemHandle)^^ do { convert it's state to it's value }
begin
if contrlHilite = 255 then { the item's control was gray, make it a disabled item }
state := kHMDisabledItem;
if value = ctrlItem + btnCtrl then
begin
if GetCtlMax(ControlHandle(itemHandle))>1 then
if state = kHMEnabledItem then
state := kHMCheckedItem
else state := kHMOtherItem;
end else
if state = kHMEnabledItem then { <68> BHTCA0080, check for 'checked' or 'other' only if enabled }
begin
if contrlValue = 1 then { the item's control value was 1, make it a checked item }
state := kHMCheckedItem
else if contrlValue > 1 then { the item's control value was >1, make it an 'other' item }
state := kHMOtherItem;
end;
end;
with __HMGetHelpGlobal^ do { return true if item & state were the same as last time }
__HMSameItemState := (hmgState = state) and (hmgItemNum = item);
end;
END;
FUNCTION __HMTrackModalHelpItems: OsErr;
VAR d : DialogPtr;
result : OsErr;
itemRect : Rect;
helpTemplateID : INTEGER;
founditem : INTEGER;
eachHelpItem : INTEGER;
state : INTEGER;
theProc : INTEGER;
variant : INTEGER;
index : INTEGER;
altRect : Rect;
pt : Point;
err : OsErr;
i : INTEGER;
aHelpMsg : HMMessageRecord;
gp : HMGlobalPtr;
oldPort : GrafPtr;
options : LongInt;
helpItemCount : INTEGER;
kind : INTEGER;
offset : INTEGER;
count : INTEGER;
method : LongInt;
currentPSN : ProcessSerialNumber;
frontPSN : ProcessSerialNumber;
sameResult : BOOLEAN;
BEGIN
if __HMGetHelpGlobal^.hmgOurDialog then { if our dialog is in front then call bulk once to kick menuselect }
if __HMBalloonBulk=noErr then;
__HMTrackModalHelpItems := noErr;
{ we already checked to see if Balloon Help mode was on }
d := FrontWindow;
if d=NIL then exit(__HMTrackModalHelpItems);
{ quik check to see if the front window is truely a dialog }
if WindowPeek(d)^.windowKind<>DialogKind then exit(__HMTrackModalHelpItems);
{ <74> now make sure that we're being executed from the frontmost process, otherwise exit }
err := GetCurrentProcess(currentPSN); { <74> }
if err = noErr then { <74> }
begin { <74> }
err := GetFrontProcess(frontPSN); { <74> }
if err = noErr then { <74> }
err := SameProcess(currentPSN,frontPSN,sameResult); { <74> }
end; { <74> }
if not sameResult or (err <> noErr) then { <74> }
exit(__HMTrackModalHelpItems); { <74> }
helpItemCount := __HMCountDITLHelpItems(d); { returns how many help items are in the dialog's item list }
if helpItemCount>0 then { there weren't any help items in the dialog so scram! }
begin
GetPort(oldPort);
SetPort(d);
GetMouse(pt);
if PtInRect(pt,d^.portrect) then { make sure local mouse is in window portrect }
begin
result := -1;
gp := __HMGetHelpGlobal;
if __HMSameItemState(d,pt,foundItem,state,itemRect) then
begin
SetPort(oldPort);
Exit(__HMTrackModalHelpItems);
end;
for eachHelpItem := 1 to helpItemCount do { check each help item present in the dialog item list }
begin
helpTemplateID := __HMGetHelpItemResID(d,eachHelpItem,kind,offset);
if helpTemplateID <> -1 then
begin
if (kind = kScanhrct) then
begin
result := __HMScanHRCTResource(WindowPtr(d),helpTemplateID,pt,index);
if result = noErr then
begin
with gp^ do { check to see if these were the same as last time, if so, exit! }
if (helpTemplateID = hmgState) then if (index = hmgItemNum) then
begin
SetPort(oldPort);
Exit(__HMTrackModalHelpItems);
end;
result := __HMGetIndHelpMsg(kHMRectListResType,helpTemplateID,
index,kHMEnabledItem,options,pt,@itemRect,
theProc,variant,aHelpMsg,count);
if result = noErr then
begin
state := helpTemplateID;
founditem := index;
end;
end;
end else
begin
if founditem >= 0 then
result := __HMFillInDialogMessage( helpTemplateID,founditem,
eachHelpItem,helpItemCount,
state,offset,-1{current resfile},options,pt,@altRect,
theProc,variant,aHelpMsg);
if result = noErr then
with itemRect do { correct the dialog item's hot rect [<59> always add in altRect params] }
begin
left := left + altRect.left;
top := top + altRect.top;
right := right + altRect.right;
bottom := bottom + altRect.bottom;
end;
end;
case result of
noErr,hmSkippedBalloon:
leave;
otherwise { including hmGetNextHelpItem }
cycle;
end; { case }
end else { end helpTemplateID=-1 }
{$ifc HelpMgrTesting}
DebugStr('HMGetHelpItemResID returned a -1 for a hdlg resource ID');
{$endc}
end; { end of for loop }
if result = noErr then { we hit something; either an hdlg driven item or hrct driven rect }
begin
altRect := itemRect;
with altRect do
begin
LocalToGlobal(topLeft); { note: the port is set already }
LocalToGlobal(botRight);
if LongInt(pt)=0 then
begin
pt := botRight;
pt.h := pt.h - 10;
pt.v := pt.v - 10;
end else { setup the tip as it wasn't the special 'EmptyPt' case }
if (kind <> kScanhrct) then
begin
pt.h := pt.h + left; { for 'hdlg's move the tip relative to the item's rect }
pt.v := pt.v + top;
end else
LocalToGlobal(pt); { for 'hrct's just convert the tip to global coords }
end;
method := BSR(BAnd(options,$0000000C),2);
result := __HMShowBalloon(aHelpMsg,pt,@altRect,
NIL,theProc,variant,method);
if result = noErr then
with gp^ do
begin
hmgLastWindowPtr := d;
hmgItemNum := founditem;
hmgState := state;
end;
end;
end;
SetPort(oldPort);
end; { helpItemCount>0 }
END;
FUNCTION __HMShowContentBalloon( tip: Point; alternateRect: Rect;
theProc,variant: INTEGER;
aHelpMsg: HMMessageRecord; options: LongInt): OsErr;
BEGIN
{Note: port is already set, and front window cannot be NIL!}
LocalToGlobal(tip); {yucko!}
LocalToGlobal(alternateRect.topLeft);
LocalToGlobal(alternateRect.botRight);
if BTst(options,kBTSTAbsoluteCoords) then
with FrontWindow^.portrect,tip do
begin
{tip.}h := {tip.}h + left;
{tip.}v := {tip.}v + top;
OffsetRect(alternateRect,left,top);
end;
__HMShowContentBalloon := __HMShowBalloon(aHelpMsg,tip,@alternateRect,NIL,theProc,variant,0);
{ hmgLastBalloon information is set in BalloonPack.a, when a balloon comes up }
END;
PROCEDURE __HMShowEasyAccessBalloon;
VAR aHelpMsg : HMMessageRecord;
altRect : Rect;
pt : Point;
result : LongInt;
BEGIN
if Gestalt(gestaltEasyAccessAttr,result) = noErr then { Easy Access is installed }
if GetMBARRect(altRect)=noErr then
begin
altRect.left := altRect.right-10;
if PtInRect(__HMGetGlobalMouse,altRect) then
with __HMGetHelpGlobal^ do
begin
result := BAnd($0007,result); { toss hi bits so that Don Brady can use the call for somethin' else }
if (hmgMenuID = kwasEasyAccess) then if (hmgItemNum = result) then
Exit(__HMShowEasyAccessBalloon);
if (result = 0) then
begin
if __HMIsBalloon then
if __HMRemoveBalloon=noErr then;
exit(__HMShowEasyAccessBalloon);
end;
with aHelpMsg do
begin
hmmHelpType := khmmString;
GetIndString(hmmString,kHMHelpID,kHMEasy1Access-1+result);
if length(hmmString)=0 then
exit(__HMShowEasyAccessBalloon);
end;
pt.h := altRect.left + 5;
pt.v := altRect.bottom;
if __HMShowBalloon(aHelpMsg,pt,@altRect,NIL,0,2,0)=noErr then
begin
hmgItemNum := result;
hmgMenuID := kwasEasyAccess;
hmgTitleBalloon := 1; { treat this balloon as if it was a menu title balloon }
end;
end;
end;
END;
FUNCTION __HMGetWindowPartCode(VAR window: WindowPtr): INTEGER;
{ returns the typical FindWindow result codes:
inDesk = 0;
inMenuBar = 1;
inSysWindow = 2;
inContent = 3;
inDrag = 4;
inGrow = 5;
inGoAway = 6;
inZoomIn = 7;
inZoomOut = 8;
Note: Don't call this unless curLayer == applicationLayer
}
VAR layer : LayerPtr;
saveLayer : LayerPtr;
pt : Point;
windowCode : INTEGER;
variant : INTEGER;
err : OsErr;
PSN : ProcessSerialNumber;
info : ProcessInfoRec;
modalClass : INTEGER;
windowIsFloater : BOOLEAN;
gp : HMGlobalPtr;
BEGIN
pt := __HMGetGlobalMouse;
{
look in the IMlayer first, and explicitly set savelayer to NIL
}
saveLayer := NIL;
windowIsFloater := false;
windowCode := FindServiceWindow(pt,window);
if ( windowCode <> inDesk ) and ( windowCode <> inMenuBar ) then { appears that we have something in the IMlayer }
begin
saveLayer := SwapCurLayer(LayerPtr(GetParent(window)));
windowIsFloater := true;
end else
windowCode := FindWindow(pt,window);
if windowCode = inSysWindow then
windowCode := __HMGetRealWindowHitResult(pt,window);
case windowCode of
inGrow:
windowCode := inContent;
{ *** someday, figure out whether the grow box is really visible!
the code below doesn't work correctly as developers really
don't use the correct type of window for their applications!
begin
variant := GetWVariant(window);
if variant <> documentProc then
windowCode := inContent;
end;
}
inZoomOut:
windowCode := inZoomIn;
end;
if (windowCode = inDesk) and (window<>FrontWindow) then { we're in the desk or in another layer's window }
begin
saveLayer := SwapCurLayer(NIL); {set curLayer to RootLayer}
windowCode := FindWindow(pt,window);
SetCurLayer(saveLayer);
saveLayer := NIL; {<79> set this to NIL so we can test below}
if (GetFrontWindowModalClass(modalClass) = noErr) and (modalClass = dBoxProc) then { <60> don't put up a balloon for window areas outside a modal dialog }
windowCode := kHMOutsideModalWindow;
with __HMGetHelpGlobal^ do
begin
if (window=hmgLayer) or (window=hmgWindow) then
begin
windowCode := hmgLastPartCode;
window := hmgLastWindowPtr;
end else
if (windowCode <> inDesk) and (windowCode <> kHMOutsideModalWindow) then { <61> and if code isnt kHMOutsideModalWindow }
begin
if GetProcessFromLayer(PSN,window)=noErr then { works w/ WindowPtrs }
begin
with info do
begin
processInfoLength := Sizeof(ProcessInfoRec);
processName := @hmgProcessName;
processAppSpec := NIL;
end;
if GetProcessInformation(PSN,info)=noErr then
begin
windowCode := kHMInLayerPreamble;
__HMStripNullFromProcessName(@hmgProcessName);
end;
end else windowCode := kHMInOtherWindow;
end else
begin
{ *** the window code was inDesk (probably the Finder desktop), so lets do somethin' }
end;
end;
end else
begin
{ if (window<>NIL) then if (ORD(WindowPeek(window)^.hilited) = ORD(false)) and ( NOT windowIsFloater) then } { mouse isn't in a hilited window and it isn't a floater }
if (window<>NIL) then
if (ORD(WindowPeek(window)^.hilited) = ORD(false)) then { mouse isn't in a hilited window }
begin
if ( windowIsFloater ) then { it is a floater }
begin
if ( windowCode = inContent ) then { and the mouse is in content region }
windowCode := kHMInFrontFloatingWindow; { special Balloon for frontmost floater }
end
else { not a floater }
begin
windowCode := kHMInOtherWindow;
if (GetFrontWindowModalClass(modalClass) = noErr) then
if (modalClass = movableDBoxProc) or (modalClass = dBoxProc) then { <60> don't put up a balloon for window areas outside a modal dialog }
windowCode := kHMOutsideModalWindow;
end
end;
end;
__HMGetWindowPartCode := windowCode;
if saveLayer <> NIL then { <79> if we are in the IMlayer restore to the saved layer }
SetCurLayer(saveLayer);
END;
FUNCTION __HMShowWindowPartBalloon(window: WindowPtr; windowCode: INTEGER): BOOLEAN;
{ returns true if a Balloon was shown }
VAR aHelpMsg : HMMessageRecord;
w : WindowPtr;
pt : Point;
i : INTEGER;
thisFile : INTEGER;
count : INTEGER;
aID : INTEGER;
mapCode : INTEGER;
balloonProc : INTEGER;
balloonVariant : INTEGER;
aType : ResType;
overrideH : Handle;
tempstr : STR255;
overridden : BOOLEAN;
tempBaseStr : Handle; {<76>}
tempSubstituteStr : Handle; {<76>}
anErr : INTEGER; {<76>}
savedPort : GrafPtr; {<78>}
BEGIN
__HMShowWindowPartBalloon := false;
balloonProc := 0;
balloonVariant := 0;
overridden := false;
count := CountResources('hovr');
if count>0 then
begin
thisFile := CurResFile;
for i := 1 to count do
begin
overrideH := GetIndResource('hovr',i);
if HomeResFile(overrideH)=thisFile then
begin
GetResInfo(overrideH,aID,aType,tempstr);
with HMhfdrRecordHdl(overrideH)^^ do
begin
balloonProc := theProc;
balloonVariant := variant;
end;
{ Remember that FindWindow returns:
inDrag = 4;
inGrow = 5;
inGoAway = 6;
inZoomIn = 7;
inZoomOut = 8;
}
case windowCode of
inDrag..inZoomIn: mapCode := windowCode - 3; { convert to our indexes }
kHMInOtherWindow :
mapCode := 5;
kHMInLayerPreamble..kHMInLayerBody:
mapCode := 6;
kHMOutsideModalWindow:
mapCode := 7;
otherwise mapCode := windowCode;
end;
overridden := __HMExtractHelpMsg(aType,aID,mapCode,0,aHelpMsg)=noErr;
if overridden then
leave;
end;
end;
end;
if not overridden then
with aHelpMsg do
begin
hmmHelpType := khmmString;
case windowCode of
kHMInOtherWindow..kHMOutsideModalWindow:
begin
GetIndString(hmmString,kHMHelpID,windowCode);
if windowCode=kHMInLayerPreamble then
begin
GetPort(savedPort); {<78>}
w := __HMGetHelpGlobal^.hmgHelpWindow; {<78>}
IF w <> NIL THEN {<78>}
SetPort(w); {<78>}
tempBaseStr := NewHandle(Length(hmmString)); {<76>}
tempSubstituteStr := NewHandle(Length(__HMGetHelpGlobal^.hmgProcessName)); {<76>}
IF (tempBaseStr <> NIL) AND (tempSubstituteStr <> NIL) THEN BEGIN {<76>}
BlockMove(@hmmString[1], tempBaseStr^, Length(hmmString)); {<76>}
with __HMGetHelpGlobal^ do {<76>}
BlockMove(@hmgProcessName[1], tempSubstituteStr^, Length(hmgProcessName)); {<76>}
anErr := ReplaceText(tempBaseStr, tempSubstituteStr, '^0'); {<76>}
hmmString[0] := Chr(GetHandleSize(tempBaseStr)); {<76>}
BlockMove(tempBaseStr^, @hmmString[1], Ord(hmmString[0])); {<76>}
END; {<76>}
DisposeHandle(tempBaseStr); {<76>}
DisposeHandle(tempSubstituteStr); {<76>}
SetPort(savedPort); {<78>}
end;
end;
kHMInFrontFloatingWindow: {<80>}
begin {<80>}
GetIndString(hmmString, kHMHelpID, windowCode); {<80>}
end; {<80>}
otherwise
GetIndString(hmmString,kHMHelpID,windowCode+(kHMInDragIndex-inDrag));
end;
if length(hmmString)=0 then { yikes! what happened to our string resource? }
{$ifc HelpMgrTesting}
hmmString := 'The window parts help resource was not foundÉ';
{$elsec}
Exit(__HMShowWindowPartBalloon);
{$endc}
end;
if __HMShowBalloon(aHelpMsg,__HMGetGlobalMouse,NIL,NIL,balloonProc,balloonVariant,0) = noErr then
with __HMGetHelpGlobal^ do
begin
hmgItemNum := windowCode;
hmgMenuID := kwasWindowPart;
__HMShowWindowPartBalloon := true;
end;
END;
FUNCTION __HMGetBalloonWindow(VAR window: WindowPtr): OsErr;
BEGIN
window := __HMGetHelpGlobal^.hmgWindow;
__HMGetBalloonWindow := noErr;
END;
FUNCTION __HMScanTemplateItems(whichID,whichResFile: INTEGER; whichType: ResType): OsErr;
VAR d : DialogPtr;
result : OsErr;
founditem : INTEGER;
state : INTEGER;
theProc : INTEGER;
variant : INTEGER;
oldResFile : INTEGER;
count : INTEGER;
index : INTEGER;
itemRect : Rect;
altRect : Rect;
pt : Point;
err : OsErr;
aHelpMsg : HMMessageRecord;
gp : HMGlobalPtr;
oldPort : GrafPtr;
options : LongInt;
BEGIN
__HMScanTemplateItems := paramErr;
d := FrontWindow;
if d=NIL then exit(__HMScanTemplateItems);
if (LongInt(whichType) = LongInt(kHMDialogResType)) or (LongInt(whichType) = LongInt(kHMRectListResType)) then
begin
__HMScanTemplateItems := hmSameAsLastBalloon; { assume that it's already displayed }
GetPort(oldPort);
SetPort(d);
GetMouse(pt);
if PtInRect(pt,d^.portrect) then { make sure local mouse is in window portrect }
begin
gp := __HMGetHelpGlobal;
if (LongInt(whichType) = LongInt(kHMDialogResType)) then { see if the mouse is in any of the dialog item rects }
begin
{ <67> the following check MUST be short circuit! }
if (WindowPeek(d)^.windowKind<>DialogKind) | (__HMSameItemState(d,pt,foundItem,state,itemRect)) then begin
SetPort(oldPort);
Exit(__HMScanTemplateItems);
end;
if whichResFile<>-1 then
begin
oldResFile := CurResFile;
UseResFile(whichResFile);
end;
result := __HMFillInDialogMessage(whichID,founditem,0,0,state,0{offset},whichResFile,options,
pt,@altRect,theProc,variant,aHelpMsg);
if result = noErr then
with itemRect do { correct the dialog item's ÒhotÓ rect [<59> always add in altRect params] }
begin
left := left + altRect.left;
top := top + altRect.top;
right := right + altRect.right;
bottom := bottom + altRect.bottom;
end;
end else { it must have been an hrct resource that we wanted to scan }
begin
if whichResFile<>-1 then
begin
oldResFile := CurResFile;
UseResFile(whichResFile);
end;
result := __HMScanHRCTResource(WindowPtr(d),whichID,pt,index);
if result = noErr then
begin
with gp^ do { check to see if these were the same as last time, if so, exit! }
if (hmgState = whichID) then if (hmgItemNum = index) then
begin
if whichResFile<>-1 then
UseResFile(oldResFile);
SetPort(oldPort);
Exit(__HMScanTemplateItems);
end;
result := __HMGetIndHelpMsg(kHMRectListResType,whichID,
index,kHMEnabledItem,options,pt,@itemRect,
theProc,variant,aHelpMsg,count);
if result = noErr then
begin
state := whichID;
founditem := index;
end;
end;
end;
if result <> noErr then
begin
if whichResFile<>-1 then
UseResFile(oldResFile);
SetPort(oldPort);
__HMScanTemplateItems := result;
exit(__HMScanTemplateItems);
end;
altRect := itemRect;
with altRect do
begin
LocalToGlobal(topLeft); { watch the port! Make sure it is set! }
LocalToGlobal(botRight);
if LongInt(pt)=0 then
begin
pt := botRight;
pt.h := pt.h - 10;
pt.v := pt.v - 10;
end else { setup the tip as it wasn't the special 'EmptyPt' case }
if (LongInt(whichType) = LongInt(kHMDialogResType)) then
begin
pt.h := pt.h + left; { for 'hdlg's move the tip relative to the item's rect }
pt.v := pt.v + top;
end else
LocalToGlobal(pt); { for 'hrct's just convert the tip to global coords }
end;
if BTst(options,kBTSTAbsoluteCoords) then
with WindowPtr(d)^.portrect,pt do
begin
{pt.}h := {pt.}h + left;
{pt.}v := {pt.}v + top;
OffsetRect(altRect,left,top);
end;
result := __HMShowBalloon(aHelpMsg,pt,@altRect,
NIL,theProc,variant,BSR(BAnd(options,$0000000C),2) { convert hmSaveBitsNoWindow & hmSaveBitsWindow to method parameter });
if whichResFile<>-1 then
UseResFile(oldResFile);
if result = noErr then
with gp^ do
begin
hmgItemNum := founditem;
hmgState := state;
end;
__HMScanTemplateItems := result;
end else { mouse wasn't in window's port rect so return hmBalloonAborted }
__HMScanTemplateItems := hmBalloonAborted;
SetPort(oldPort);
end;
END;
FUNCTION __HMExtractHelpMsg( whichType: ResType;
whichResID, whichMsg, whichState: INTEGER;
VAR aHelpMsg: HMMessageRecord): OsErr;
VAR options : LongInt;
tip : Point;
altRect : Rect;
theProc : INTEGER;
variant : INTEGER;
count : INTEGER;
BEGIN
__HMExtractHelpMsg := __HMGetIndHelpMsg(whichType,whichResID,whichMsg,whichState,
options,tip,@altRect,theProc,variant,aHelpMsg,count);
END;
END.