sys7.1-doc-wip/Toolbox/DataPubsMgr/dpEvents.inc.p
2019-07-27 22:37:48 +08:00

545 lines
17 KiB
OpenEdge ABL

{ File: dpEvents.inc.p
Written by: Nick Kledzik
Copyright: © 1989-1990 by Apple Computer, Inc., all rights reserved.
This file is used in these builds: BigBang
Change History (most recent first):
<15> 2/27/91 ngk MM, #83903: Moved sending of read events from
dp_EditionMgrBackGroundTask to dpPubSync in dpPubIO.inc.p
<14> 12/14/90 ngk <VL>Use Sync calls and set ioFVersnum for MFS volumes.
<13> 7/2/90 ngk Added meta-param for interact level to open appleevent.
PostOpenDoc
now returns if app is running. Time out of scroll message now tries
for another publisher.
<12> 6/20/90 ngk change CanonicalFIleSpec to FSSpec
<11> 5/31/90 ngk change globals from a handle to a pointer. Added
dp_EditionMgrVolumeNotifier.
<10> 4/19/90 ngk Changed 'odoc' to 'open' to when posting to Finder to launch an
app.
<9> 4/7/90 ngk Changed backGroundTask to use dpPubSync. Use new Failure
handling.
<8> 3/20/90 ngk PostPPCEvent changed to PostHighLevelEvent. Now call
NewAliasMinimal instead of NewAlias, for open AppleEvent.
<7> 3/20/90 ngk change to new apple events 7.0a8
<6> 3/10/90 ngk Get rid of post by PSN kludge in System mode. Moved locking and
unlocking of package to dispatcher.
<5> 2/16/90 ngk Fix bug in polling. Used to not check update mode of subscribers
before sending read events.
<4> 1/8/90 ngk Changed PostSectionEvent to have NIL toApp mean current
application.
<3> 1/6/90 ngk Renamed routines. Moved once only int to dpInit.p
<2> 12/19/89 ngk fix bug in dp_EditionMgrBackGroundTask. Dereferencing NIL goto handle
<2.8> 12/3/89 ngk Fixed some unsafe dereferences when calling TickCount.
<2.7> 11/13/89 ngk Added Locking and restoring of Pack11 in all public routines
<2.6> 11/10/89 ngk Commented out Begin/EndSytemMode around PostPPCEvent
<2.5> 11/4/89 ngk Use AppleEvents.p Interface file Changed AppRefNum to be a
handle to app's globals.
<2.4> 10/24/89 ngk Moved some inlines to dpMisc.p and glue. Fixed post by signature
bug.
<2.3> 10/13/89 ngk Now intialize gotoHere.
<2.2> 10/13/89 ngk Added goto publisher timeout to dp_EditionMgrBackGroundTask
<2.1> 10/10/89 ngk Now use ProcessSerialNumber and Begin/EndSystemMode
<2.0> 10/2/89 ngk Changed polling to be called by SystemTask.
<1.9> 9/18/89 ngk Change dpPostOpenDoc to send an AppleEvent to the Finder, if
launch needed.
<1.8> 9/7/89 ngk Changed dp_EditionMgrBackGroundTask to use way cool, efficient polling
<1.7> 8/29/89 ngk Use qUseAppName with AppRefNum. Changed polling to check all pub
files which do not have a publisher on this machine.
<1.6> 8/8/89 ngk Change dp_PostSectionEvent to use classID. Changed dp_EditionMgrBackGroundTask to
return an OSErr for consistency. Used new new PubCBLinks in
walking PubCBs.
<1.5> 6/27/89 ngk Switched to completely use HighLevelEvents for section events
and open doc events.
<1.4> 6/15/89 ngk Enhanced dpPostOpenDoc to almost use HighLevelEvents still have
HighLevelEvents stuff commented out
<1.3> 6/11/89 ngk Tried to use HighLevelEvents instead of pseudoEvents
<1.2> 5/31/89 ngk Changed to use Alias manager instead of SofaLinks
<1.1> 5/29/89 ngk Added dpPostOpenDoc for GotoPublisher
<1.0> 5/19/89 ngk Submitted for first time
To Do:
}
TYPE
SectionEventMessage = RECORD
header: AETFHeader; { 'aevt', 1, 1 }
endOfMetaData: Keyword; { always ';;;;' }
directObjKey: KeyWord; { always '----' }
dataType: ParamType; { always 'tid ' }
dataLength: LONGINT; { always 4 }
tempIDType: ResType; { always 'sect' }
tempID: LONGINT; { always SectionHandle }
END;
SimpleOpenMessage = RECORD
header: AETFHeader; { 'aevt', 1, 1 }
metaKey: KeyWord; { always 'inte' }
metaType: ParamType; { always 'enum' }
metaLength: LONGINT; { always 4 }
metaData: ResType; { always 'cans' }
endOfMetaData: Keyword; { always ';;;;' }
directObjKey: KeyWord; { always '----' }
dataType: ParamType; { always 'alis' }
dataLength: LONGINT; { always ?? }
data: SignedByte; { beginning of alias }
END;
{------------- dp_PostSectionEvent -------------}
FUNCTION dp_PostSectionEvent(sectionH: SectionHandle; toApp: AppRefNum; messageID: ResType): OSErr;
VAR
anEvent: HighLevelEventRecord;
aSectionMsg: SectionEventMessage;
inSystemMode: BOOLEAN;
ignore: OSErr;
fi: FailInfo;
BEGIN
{ set up failure handling }
IF isFailure(fi, dp_PostSectionEvent) THEN
BEGIN
IF inSystemMode
THEN ignore := EndSystemMode;
EXIT(dp_PostSectionEvent);
END; {if}
{ map NIL to this App }
IF toApp = NIL
THEN FailOSErr(dp_GetCurrentAppRefNum(toApp));
WITH anEvent DO
BEGIN
class := sectionEventMsgClass;
ID := messageID;
END; {with}
WITH aSectionMsg DO
BEGIN
header.signature := LONGINT('aevt');
header.majorVersion := 1;
header.minorVersion := 1;
endOfMetaData := aeEndOfMetaDataKeyword;
directObjKey := aeDirectObjectKeyword;
dataType := aeTemporaryIDParamType;
dataLength := 8;
tempIDType := rSectionType;
tempID := ORD(sectionH);
END; {with}
FailOSErr(BeginSystemMode); inSystemMode := TRUE;
dp_PostSectionEvent := PostHighLevelEvent(EventRecord(anEvent), @toApp^^.appPSN, {refCon}0,
@aSectionMsg, SizeOf(aSectionMsg), receiverIDisPSN);
FailOSErr(EndSystemMode); inSystemMode := FALSE;
Success(fi);
END; { dp_PostSectionEvent }
FUNCTION GetVolModDate(vRefNum: INTEGER): TimeStamp;
TYPE
VCBPtr = ^VCB;
VAR
p: VCBPtr;
BEGIN
p := VCBPtr(GetVCBQHdr^.qHead);
WHILE (p<>NIL) & (p^.vcbVRefNum <> vRefNum)
DO p := VCBPtr(p^.qLink);
IF p <> NIL
THEN GetVolModDate := p^.vcbLsMod
{$IFC qCatchFailures }
ELSE DebugStr('GetVolModDate: could not find vRefNum');
{$ENDC}
;
END; { GetVolModDate }
FUNCTION GetDirModDate(vRefNum: INTEGER; dirID: LONGINT): TimeStamp;
VAR
PBC: CInfoPBRec;
BEGIN
WITH PBC DO
BEGIN
ioNamePtr := NIL;
ioVRefNum := vRefNum;
ioFDirIndex := -1;
ioFVersNum := 0;
ioDrDirID := dirID;
END; {with}
IF PBGetCatInfoSync(@PBC) = noErr
THEN GetDirModDate := PBC.ioDrMdDat
{$IFC qCatchFailures }
ELSE DebugStr('GetDirModDate: PBGetCatInfo returned an error');
{$ENDC}
;
END; { GetDirModDate }
PROCEDURE dpTimeOutGoto;
VAR
editionVol: INTEGER;
editionCNode: LONGINT;
publisherID: LONGINT;
pubSectionH: SectionHandle;
pubApp: AppRefNum;
PROCEDURE FindAPublisher(sectionH: SectionHandle; inApp: AppRefNum);
BEGIN
WITH sectionH^^ DO
BEGIN
IF publisherID = sectionID
{&} THEN IF bTST(kind, kCanWriteEditionsBIt) THEN
BEGIN
WITH PubCBHandle(controlBlock)^^ DO
BEGIN
IF info.container.theFile.vRefNum = editionVol
{&} THEN IF pubCNodeID = editionCNode THEN
BEGIN
pubSectionH := sectionH;
pubApp := inApp;
END;
END; {with}
END; {if}
END; {with}
END; { FindAPublisher }
BEGIN
pubSectionH := NIL;
WITH dpGetGlobalsLocation^^ DO
BEGIN
{ remember pertainent goto info }
WITH gotoHere^^ DO
BEGIN
editionCNode := editionID;
editionVol := editionVRefNum;
publisherID := sectionID;
END; {with}
{ dispose of goto info }
gotoHere := NIL;
DisposHandle(Handle(gotoHere));
END; {with}
{ scan through registered sections. remember the last publisher to the edition }
dpForEachSectionDo(FindAPublisher);
{ a publisher to correct edition is registered, send it a goto event}
IF pubSectionH <> NIL THEN
IF dp_PostSectionEvent(pubSectionH, pubApp, sectionScrollMsgID) <> noErr THEN;
END; { dpTimeOutGoto }
{
dp_EditionMgrBackGroundTask does two major things. 1) see if any shared edition files
have changed (i.e file on AppleShare volume). 2) close any edition files
files that are open for use by subscribers, and none of them are currently reading.
method:
Walk PubCB list. It is known to be sorted by volume and then by dirID.
Look ahead to next file.
If this one and next one are both on the same volume, then look in the VCB
for the lastModeDate. If the last mod date is the same as this.lastVolMod
then skip the polling for all pubCBs with this volume. If the dates are
different then when polling, set each PubCBs lastVolMod to be the new date.
If this one and next one are both in the same folder, then do a GetCatInfo
on that folder. If the folder mod date is the same as this.lastDirMod then
skip the polling for all pubCBs in this folder. If the dates are
different then when polling, set each PubCBs lastDirMod to be the new date.
If a PubCB has no publisher & is open & no subscribers are doing I/O
then close the file. This is part of the slop of keeping files open
for multiple subscribers - for efficiency.
### there is currently a bug when this is used with EditionOpenerProc's.
The smart polling will not work with non-edition files if they are not
saved with a "safe save" (write new file, rename, delete). This is because
an updated file will not cause the folder mod date to be touched. Thus,
this polling will miss those files if there is more than one in the same
folder.
}
{------------- dp_EditionMgrBackGroundTask -------------}
FUNCTION dp_EditionMgrBackGroundTask: OSErr;
VAR
emGlobals: EdtnMgrGlobalsPtr;
headPubCB: PubCBLinkHandle;
aPubCB: PubCBHandle;
PBH: HParamBlockRec;
ignore: OSErr;
lookAHeadPubCB: PubCBHandle;
thisVolMod: TimeStamp;
thisVol: INTEGER;
skipThisVol: Boolean;
thisDirMod: TimeStamp;
thisDir: INTEGER;
skipThisDir: BOOLEAN;
validDirMod: BOOLEAN;
theGoto: GotoInfoHandle;
curTickCount: LONGINT;
fi: FailInfo;
BEGIN
{ must have failure handler }
IF isFailure(fi, dp_EditionMgrBackGroundTask) THEN
BEGIN
EXIT(dp_EditionMgrBackGroundTask);
END; {if}
emGlobals := dpGetGlobalsLocation^;
{ walk PubCB list }
thisVol := 1000;
FailOSErr(dpGetPubCBListHeadNode(headPubCB));
aPubCB := PubCBHandle(headPubCB^^.nextPubCB);
WHILE PubCBLinkHandle(aPubCB) <> headPubCB DO
BEGIN
lookAHeadPubCB := PubCBHandle(PubCBLinkHandle(aPubCB)^^.nextPubCB);
{ see if we've walked into another volume }
IF thisVol <> aPubCB^^.info.container.theFile.vRefNum THEN
BEGIN
{ we have a different volume, see if }
{ anything has changed since we last looked. }
{ note: because GetVolModDate is a cheap call }
{ its OK to call it even if there is only one }
{ file we care about on the volume. }
thisVol := aPubCB^^.info.container.theFile.vRefNum;
thisDir := 0;
thisDirMod := 0;
thisVolMod := GetVolModDate(thisVol);
skipThisVol := (thisVolMod = aPubCB^^.lastVolMod); { nothing changed }
END; {if different volume }
IF NOT skipThisVol THEN
BEGIN
{ something on thisVol has changed, }
{ so see if it was a file we care about }
{ see if we've walked into another folder }
IF thisDir <> aPubCB^^.info.container.theFile.parID THEN
BEGIN
{ we have a different folder, see if there are }
{ multiple edition files in it }
thisDir := aPubCB^^.info.container.theFile.parID;
IF ( lookAHeadPubCB <> PubCBHandle(headPubCB) )
AND (lookAHeadPubCB^^.info.container.theFile.parID = thisDir) THEN
BEGIN
{ at least two files in this folder, }
{ so check if anything in the folder has changed }
thisDirMod := GetDirModDate(thisVol, thisDir);
skipThisDir := (thisDirMod = aPubCB^^.lastDirMod); { nothing changed }
END ELSE
BEGIN
{ only one file in this folder }
{ no need to check folder mod date }
thisDirMod := 0;
skipThisDir := FALSE;
END;
END;
IF NOT skipThisDir THEN
BEGIN
{ something in thisDir has changed, so look at each file we care about }
{ make sure control block has lastest data }
IgnoreOSErr(dpPubSync(aPubCB));
{ <15, #83903> Removed call to dpNotifySubscribers from here. }
{ <15, #83903> It is now done in dpPubSync. }
END; {if NOT skipThisDir}
END; {if NOT skipThisVol}
WITH aPubCB^^ DO
BEGIN
{ make sure every pubCB has the correct last mode date }
{ for its folder and volume }
{aPubCB^^.}lastVolMod := thisVolMod;
IF thisDirMod <> 0
THEN {aPubCB^^.}lastDirMod := thisDirMod;
{ if no subscribers are doing I/O and file is open, close it }
IF {aPubCB^^.}publisherCount = 0 { just subscribers }
{&} THEN IF {aPubCB^^.}openMode <> dmNotOpen { file is open }
{&} THEN IF {aPubCB^^.}usageInfo^^.totalIOCount = 0 THEN { no I/O }
BEGIN
ignore := dpPubCloseFile(aPubCB, NOT kFlush);
END;
END; {with}
aPubCB := lookAHeadPubCB;
END; {while}
{ check goto timeout }
theGoto := emGlobals^.gotoHere;
curTickCount := TickCount;
IF (theGoto <> NIL) {&} THEN IF (theGoto^^.timeout > curTickCount) THEN
BEGIN
{ looks like publisher will never register, look to see if it already is registered }
dpTimeOutGoto;
END; {if}
{ set next time to poll }
emGlobals^.nextPollTime := curTickCount+ kPollPeriod;
Success(fi);
END; { dp_EditionMgrBackGroundTask }
{------------- dp_EditionMgrVolumeNotifier -------------}
{$PUSH} {$Z+} { export this routine, without requiring every other unit to know about VolumeNoticeBlkPtr }
FUNCTION dp_EditionMgrVolumeNotifier(pb: VolumeNoticeBlkPtr): OSErr;
{$POP}
VAR
headPubCB: PubCBLinkHandle;
aPubCB: PubCBHandle;
BEGIN
IF pb^.VNBNotice = VNAboutToUnmount THEN
BEGIN
{ walk pub control block list, if any editions are in use }
{ on the volume that is about to unmount, then stop it! }
IF dpGetPubCBListHeadNode(headPubCB) = noErr THEN
BEGIN
aPubCB := PubCBHandle(headPubCB^^.nextPubCB);
WHILE PubCBLinkHandle(aPubCB) <> headPubCB DO
BEGIN
WITH aPubCB^^.info.container.theFile, pb^ DO
BEGIN
IF vRefNum = VNBVolume THEN
BEGIN
{ tell Process Mgr to not take this volume away }
dp_EditionMgrVolumeNotifier := fBsyErr;
EXIT(dp_EditionMgrVolumeNotifier);
END;
END; {with}
aPubCB := PubCBHandle(PubCBLinkHandle(aPubCB)^^.nextPubCB);
END; {while}
END; {if}
END; {if}
dp_EditionMgrVolumeNotifier := noErr;
END; { dp_EditionMgrVolumeNotifier }
{------------- dpPostOpenDoc -------------}
FUNCTION dpPostOpenDoc(document: FSSpec; VAR appIsRunning: BOOLEAN): OSErr;
VAR
info: FInfo;
anAppGlobal: PerAppGlobalsHandle;
emGlobals: EdtnMgrGlobalsPtr;
thePSN: ProcessSerialNumber;
anEvent: HighLevelEventRecord;
inSystemMode: BOOLEAN;
appRunning: BOOLEAN;
thisApp: AppRefNum;
aevtOpenPtr: ^SimpleOpenMessage;
aevtOpenLen: Size;
msgBuff: ARRAY [1..500] OF INTEGER; { enough room for biggest alias }
aliasH: AliasHandle;
finderSignature:OSType;
fi: FailInfo;
ignore: OSErr;
BEGIN
inSystemMode := FALSE;
{ set up failure handling }
IF isFailure(fi, dpPostOpenDoc) THEN
BEGIN
IF inSystemMode
THEN ignore := EndSystemMode;
EXIT(dpPostOpenDoc);
END; {if}
{ get docs creator }
FailOSErr(FSpGetFInfo(document, info));
{ see if it is a running app }
emGlobals := dpGetGlobalsLocation^;
IF emGlobals = NIL
THEN FailOSErr(editionMgrInitErr);
anAppGlobal := emGlobals^.perAppListHead;
appRunning := FALSE;
WHILE anAppGlobal <> NIL DO
BEGIN
WITH anAppGlobal^^ DO
BEGIN
IF LONGINT({anAppGlobal^^.}signature) = LONGINT(info.fdCreator) THEN
BEGIN
appRunning := TRUE;
thePSN := {anAppGlobal^^.}appPSN;
LEAVE; {while}
END;
anAppGlobal := {anAppGlobal^^.}nextApp;
END; {with}
END; {while}
{ make a minimal alias to the document }
FailOSErr(NewAliasMinimal(document, aliasH));
{ fill out apple event for opening document }
aevtOpenPtr := @msgBuff;
WITH aevtOpenPtr^ DO
BEGIN
header.signature := LONGINT('aevt');
header.majorVersion := 1;
header.minorVersion := 1;
metaKey := 'inte';
metaType := 'enum';
metaLength := 4;
metaData := 'cans';
endOfMetaData := aeEndOfMetaDataKeyword;
directObjKey := aeDirectObjectKeyword;
dataType := aeAliasParamType;
dataLength := aliasH^^.aliasSize;
BlockMove(Ptr(aliasH^), @data, dataLength);
{ -2 is kludge to take .alias field off the end of AppleEventSimpleMessage.directObjectFile }
aevtOpenLen := SizeOf(SimpleOpenMessage) - 2 + dataLength;
END; {with}
{ now that we've copied the alias into the buffer, we can lose it }
DisposHandle(Handle(aliasH));
WITH anEvent DO
BEGIN
class := standardAppleEventMsgClass;
ID := 'odoc';
END; {with}
IF appRunning THEN
BEGIN
{ send open doc message directly to app }
FailOSErr(BeginSystemMode); inSystemMode := TRUE;
FailOSErr(PostHighLevelEvent(EventRecord(anEvent), @thePSN, 0,
Ptr(aevtOpenPtr), aevtOpenLen, receiverIDisPSN));
FailOSErr(EndSystemMode); inSystemMode := FALSE;
END ELSE
BEGIN
{ app needs to be launched, send open event to Finder }
finderSignature := 'MACS';
anEvent.ID := 'open'; { finder wants open instead of odoc }
FailOSErr(BeginSystemMode); inSystemMode := TRUE;
FailOSErr(PostHighLevelEvent(EventRecord(anEvent), Ptr(finderSignature), 0,
Ptr(aevtOpenPtr), aevtOpenLen, receiverIDisSIGNATURE));
FailOSErr(EndSystemMode); inSystemMode := FALSE;
END; {if}
appIsRunning := appRunning;
Success(fi);
END; { dpPostOpenDoc }