{__________________________________________________________________________________________________ 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): 2/17/93 kc Allocate pictures in the System Heap because they persist longer than the application heap. <81> 6/2/92 KST #1030643 : We have only 1 default message for the floating window instead of two (floating windows are all active). <80> 5/14/92 KST #1027497 : #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,: 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,: 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 #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. = force grayed controls to get the HMDisabled state in all cases. <67> 1/24/91 RLC #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 #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 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 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 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 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 get rid of LongIntPtr, now in Types.p <60> 12/3/90 RLC 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 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 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) 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; { } 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 } { } SetZone(SystemZone); { point at the system heap } { } { } pic := OpenPicture(visibleRect); TEUpdate(visibleRect,aTE); ClosePicture; SetZone(oldZone); { } { } with aTE^^ do inPort := tPort; end; end; otherwise if aTE<>NIL then begin oldZone := GetZone; { get the current heap zone } { } SetZone(SystemZone); { point at the system heap } { } { } pic := OpenPicture(visibleRect); TEUpdate(visibleRect,aTE); ClosePicture; { } SetZone(oldZone); { } { } 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.