sys7.1-doc-wip/Toolbox/AppleEventMgr/AEInteract.inc1.p
2019-07-27 22:37:48 +08:00

387 lines
17 KiB
OpenEdge ABL

{
File: AEInteract.inc1.p
Contains: xxx put contents here xxx
Written by: xxx put writers here xxx
Copyright: © 1990 by Apple Computer, Inc., all rights reserved.
This file is used in these builds: BigBang
Change History (most recent first):
<4> 3/15/91 Lai BM,#83128: Remove notification when interacting with event from
another process.
<3> 2/13/91 Lai BM,#82759: In AE_InteractWithUser, timeOutInTicks are not
translated to actual value before attempting to switch layer,
now this is fixed by calling CalculateTimeOut in the front of
the procedure.
<2> 1/11/91 Lai change msgAddrTargetID to match change in AEUtil.p
<1> 12/13/90 Lai first checked in
To Do:
}
{--------------------------------------------------------------------------------}
{ ask the front application to take me to the front by sending an AppleEvent }
{ later write to use AEM calls to see if it is shorter }
PROCEDURE AskToComeToFront(theMsg: MsgHdrHdl;
waitTillTime: LONGINT);
TYPE
CansBuffer = RECORD
aevtMarker: AEKeyWord;
version: LONGINT;
timeOutKeyword: AEKeyWord;
timeOutType: DescType;
timeOutSize: LONGINT;
timeOutValue: LONGINT;
metaTerminator: AEKeyWord;
END;
VAR
theEvent: EventRecord;
aCansBuffer: CansBuffer;
BEGIN
WITH theEvent DO
BEGIN
theEvent.message := LONGINT(kCoreEventClass);
LONGINT(where) := LONGINT(kCanSwitchIfInteract);
modifiers := 0;
END;
WITH aCansBuffer DO
BEGIN
aevtMarker := kCoreEventClass;
version := kVersionOne;
metaTerminator := aeEndOfMetaDataKeyword;
timeOutKeyword := keyBeforeTime;
timeOutType := typeLongInteger;
timeOutSize := SizeOf(LONGINT);
timeOutValue := waitTillTime;
END;
WITH theMsg^^ DO
IgnoreOSErr(PostHighLevelEvent(theEvent, Ptr(msgAddrTarget.asTargetID.sessionID), returnID,
@aCansBuffer, SizeOf(aCansBuffer),
receiverIDisSessionID));
END;
{--------------------------------------------------------------------------------}
{ we try to determine if we can interact with the user }
{ may be no interaction is allowed either because the client don't want it or the
server machine don't want to have any interaction }
{ if interaction is allowed, we still need to have get a change to do it }
{ if the server is the front application, you get it immediately }
{ if the server is not in front, switch to front if the client is in front and
grant you the permission to go to the front, otherwise post a notification and
wait until your application is in the front }
FUNCTION AE_InteractWithUser(timeOutInTicks: LONGINT; { how long are you willing to wait }
nmReqPtr: NMRecPtr; { your own custom notification }
idleProc: IdleProcPtr { your own idle procedure }
): OSErr; { return the error }
LABEL 999, 1000;
VAR
myPSN, frontPSN, fromPSN: ProcessSerialNumber;
err: OSErr;
myNMRec: NMRec;
nowInFront, localProcess, switchDirectly: Boolean;
interactType: OSType;
dummyType: DescType;
actualSize: LONGINT;
waitSetFrontProcess: LONGINT;
curInteract: AEInteractAllowed;
flag: SignedByte;
scaled: Boolean;
aevtSource: AEEventSource;
currentDesc: AppleEvent;
{ this may be nested procedure but actual compiled code show it is shorter }
FUNCTION GetDefaultID(VAR theID: integer): Boolean;
{ get the ID of the application ICON }
VAR
tempHandle: Handle;
i, j, k, localID: integer;
count1: integer;
count2: integer;
countFREF: integer;
countBNDL: integer;
aPtr: IntegerPtr;
hasID: Boolean;
aProcessInfoRec: ProcessInfoRec;
BEGIN
GetDefaultID := FALSE;
WITH aProcessInfoRec DO
BEGIN
processInfoLength := SizeOf(ProcessInfoRec); { we only need processSignature }
processName := NIL; { who cares }
processAppSpec := NIL;
END;
IF GetProcessInformation(myPSN, aProcessInfoRec) = noErr THEN
BEGIN
countFREF := CountResources('FREF');
{ we first try to find the resource ID, by first looking at FREF resource and find the local ID
of APPL, then we look at the BNDL resource to find the ID corresponding to the local ID }
FOR i := 1 TO countFREF DO
BEGIN
{ look through all 'FREF' resources }
tempHandle := GetIndResource('FREF', i);
IF (tempHandle <> NIL) & (LONGINTPtr(tempHandle^)^ = LONGINT('APPL')) THEN
BEGIN
localID := IntegerPtr(ord(tempHandle^) + 4)^;
countBNDL := CountResources('BNDL');
FOR j := 1 TO countBNDL DO
BEGIN { we try to find the BNDL from this application }
tempHandle := GetIndResource('BNDL', j);
IF (tempHandle <> NIL) & (LONGINTPtr(tempHandle^)^ =
LONGINT(aProcessInfoRec.processSignature)) THEN
BEGIN { this is the right BNDL }
aPtr := IntegerPtr(ord(tempHandle^) + 6);
count1 := aPtr^;
aPtr := IntegerPtr(ord(aPtr) + 2);
FOR j := 0 TO count1 DO
BEGIN
IF LONGINTPtr(aPtr)^ = LONGINT('FREF') THEN
BEGIN
aPtr := IntegerPtr(ord(aPtr) + 4); { skip over FREF }
count2 := aPtr^;
aPtr := IntegerPtr(ord(aPtr) + 2); { skip over count }
FOR k := 0 TO count2 DO
BEGIN
IF aPtr^ = localID THEN
BEGIN
GetDefaultID := TRUE;
theID := IntegerPtr(ord(aPtr) + 2)^;
Exit(GetDefaultID);
END;
aPtr := IntegerPtr(ord(aPtr) + 4); { skip to next one }
END;
Exit(GetDefaultID);
END
ELSE
BEGIN
aPtr := IntegerPtr(ord(aPtr) + 4); { skip over type name }
aPtr := IntegerPtr(ord(aPtr) + 4 * aPtr^ + 6); { skip over
this type }
END;
END; { look for FREF }
END; { is the right BNDL }
END; { search all BNDL loop }
END; { is FREF for APPL }
END; { search all FREF loop }
END; { no err in GetProcessInformation }
END;
FUNCTION GetDefaultSICN: Handle;
{ get a default SICN from the application }
VAR
tempHandle: Handle;
tryICN: Handle;
srcBits, dstBits: BitMap;
theID: integer;
BEGIN
tempHandle := NIL;
IF GetDefaultID(theID) THEN
BEGIN
{ we find out the default ID, use it to get the resource }
tempHandle := GetResource('SICN', theID);
IF tempHandle <> NIL THEN
BEGIN
{ we have a SICN, use it }
flag := HGetState(tempHandle);
HNoPurge(tempHandle);
scaled := FALSE;
END
ELSE
BEGIN
{ we do not have a small icon, then try to scale it down from the icon }
tryICN := GetResource('ICN#', theID); { first we try to read it in }
IF tryICN <> NIL THEN { yes, we have one }
BEGIN
flag := HGetState(tryICN);
HLock(tryICN); { lock it down first }
tempHandle := NewHandle(32); { create a handle for a small icon }
IF tempHandle <> NIL THEN
BEGIN { copy it in }
HLock(tempHandle);
WITH srcBits DO
BEGIN
SetRect(bounds, 0, 0, 32, 32);
rowBytes := 4;
baseAddr := tryICN^
END;
WITH dstBits DO
BEGIN
SetRect(bounds, 0, 0, 16, 16);
rowBytes := 2;
baseAddr := tempHandle^
END;
{ scale it down }
CopyBits(srcBits, dstBits, srcBits.bounds, dstBits.bounds, srcCopy,
NIL);
HUnLock(tempHandle);
scaled := TRUE;
END;
HSetState(tryICN, flag);
END;
END;
END;
GetDefaultSICN := tempHandle;
END;
BEGIN
err := noErr;
CalculateTimeOut(timeOutInTicks);
IgnoreOSErr(GetFrontProcess(frontPSN));
IgnoreOSErr(GetCurrentProcess(myPSN));
IgnoreOSErr(SameProcess(frontPSN, myPSN, nowInFront)); { are we already in front? }
interactType := kCanInteract; { this is the default if it is not specified in the message }
switchDirectly := FALSE;
IgnoreOSErr(AE_GetTheCurrentEvent(currentDesc));
EventSource(MsgHdrHdl(currentDesc.dataHandle), aevtSource, fromPSN); { where is it from? }
IF aevtSource <> kAEUnknownSource THEN
BEGIN { we have a message }
IgnoreOSErr(AE_GetInteractionAllowed(curInteract));
{ find the interact level specified by the message }
IgnoreOSErr(AE_GetKeyPtr(MakeMeta(currentDesc), keyInteractLevelAttr, typeEnumerated,
dummyType, @interactType, 4, actualSize));
IF LONGINT(interactType) = LONGINT(kNeverInteract) THEN { the message specified no
interact }
GOTO 999;
WITH MsgHdrHdl(currentDesc.dataHandle)^^ DO
BEGIN
IF (aevtSource = kAELocalProcess) OR (aevtSource = kAERemoteProcess) THEN
BEGIN
{ now we know it is not send-to-self }
IF (curInteract = kAEInteractWithSelf) THEN GOTO 999; { the server don't want to
interact with others }
IF aevtSource = kAELocalProcess THEN
BEGIN
{ if msg came from front process, we can switch directly }
IF (interactType[4] = 's') THEN { and they give us permission }
IgnoreOSErr(SameProcess(frontPSN, fromPSN, switchDirectly));
END
ELSE
BEGIN
{ this comes from another machine }
IF curInteract <> kAEInteractWithAll THEN GOTO 999;
END;
END;
END
END;
IF NOT nowInFront THEN { only need to do something if nor already in front }
BEGIN { not the front process, try to get to the front }
IF timeOutInTicks = 0 THEN
err := errAETimeOut
ELSE
BEGIN
IF switchDirectly THEN
BEGIN
waitSetFrontProcess := 15; { we can wait this long for it to switch }
{ but we don't have that much time we have to make do with less }
IF timeOutInTicks < waitSetFrontProcess THEN
waitSetFrontProcess := timeOutInTicks;
{ this is the amount left over }
timeOutInTicks := timeOutInTicks - waitSetFrontProcess;
AskToComeToFront(MsgHdrHdl(currentDesc.dataHandle), TickCount + waitSetFrontProcess);
err := GeneralWait(waitSetFrontProcess, idleProc, NIL, NIL);
IF err = errAETimeOut THEN
switchDirectly := FALSE { we fail to switch before time is up }
ELSE { we did not use it all up, so add it back }
timeOutInTicks := timeOutInTicks + waitSetFrontProcess;
END;
IF switchDirectly THEN { remember it so we can go back }
MsgHdrHdl(currentDesc.dataHandle)^^.switchFromPSN := fromPSN
ELSE
BEGIN
{ otherwise we just use notification }
IF nmReqPtr = NIL THEN
BEGIN { if no notification record is provided, we make our own }
WITH myNMRec DO
BEGIN
qType := nmType;
nmMark := 1;
nmIcon := GetDefaultSICN;
IF nmIcon = NIL THEN
nmSound := Handle( - 1)
ELSE
nmSound := NIL;
nmStr := NIL;
nmResp := NIL;
END;
nmReqPtr := @myNMRec;
END;
IgnoreOSErr(NMInstall(nmReqPtr));
{ now we call the general wait routine, last two parameter are nil means wait to come to front }
err := GeneralWait(timeOutInTicks, idleProc, NIL, NIL);
IgnoreOSErr(NMRemove(nmReqPtr));
IF ord(nmReqPtr) = ord(@myNMRec) THEN
WITH myNMRec DO { it is our own, and it was installed, we have to take care
of it }
IF nmIcon <> NIL THEN
BEGIN { we have a small icon }
IF scaled THEN
DisposHandle(nmIcon) { we created it, so we free it }
ELSE
HSetState(nmIcon, flag); { it is from resource, just restore its
state }
END;
END;
END;
END;
GOTO 1000;
999:
err := errAENoUserInteraction;
1000:
AE_InteractWithUser := err;
END;
{--------------------------------------------------------------------------------}
{ Used by the server if the command received is going to take some time to process. }
{ Use the reply to get the necessary information to send a 'wait' back to the client }
{ This will cause the client to reset its timeout loop and wait longer }
FUNCTION AE_ResetTimer(VAR reply: AppleEvent): OSErr;
VAR
theEvent: EventRecord;
BEGIN
AE_ResetTimer := errAEReplyNotValid;
IF reply.dataHandle <> NIL THEN
BEGIN
WITH MsgHdrHdl(reply.dataHandle)^^ DO
BEGIN
IF LONGINT(thisAEEventClass) = LONGINT(kCoreEventClass) THEN
IF LONGINT(thisAEEventID) = LONGINT(kAEAnswer) THEN
IF LONGINT(msgAddrType) = LONGINT(typeSessionID) THEN
BEGIN
theEvent.message := LONGINT(kCoreEventClass);
LONGINT(theEvent.where) := LONGINT(kAEWaitLonger);
theEvent.modifiers := 0;
AE_ResetTimer := PostHighLevelEvent(theEvent,
Ptr(msgAddrTarget.asTargetID.sessionID),
returnID, NIL, 0,
receiverIDisSessionID);
END;
END;
END;
END;
{--------------------------------------------------------------------------------}