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

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

1643 lines
72 KiB
C

/*
File: TSMFunctions.c
Contains: Text Services Manager functions.
Written by: Kenny SC. Tung
Copyright: © 1991-1992 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<56> 10/30/92 DTY Use Get/Set macros to access ExpandMem.
<55> 8/4/92 DCL Changed spelling of tsmUnSupportedTypeErr to
tsmUnsupportedTypeErr (lowercased the s)
<54> 7/16/92 KST #1034952 <Kda>: Clear the 'kTSMEventBeenCalled' bit in
TSMEvent().
<53> 7/13/92 KST #1035594 <JH>: In xActivateTSMDocument(), I assumed when a TSM
aware application calls ActivateTSMDocument(), the other
document has been deactivated (as the ERS said). This is
apparently not a wise assumption because application writer
could be as dumb as me and will make mistake. (Like the one they
do to produce this bug).
<52> 7/9/92 KST #1035067 <JH>: When there is no TSM document in a TSM aware
application, we call UseInputWindow (nil, true). But that fix
did not check re-entrant case. So this bug happens. The fix is
to check the flag, if it is already set, then we know we have
been called again and just return.
<51> 7/8/92 KST #1033475 <JH>: Changed the routine name utHasNoInputMethod() to
utScriptIsNotCCJK() to be more descriptive.
<50> 7/6/92 KST #1033507,1033505 <JH>: Clear the kIMJustSetCursor bit before the
SetTextServiceCursor() call so that IM can change cursor from
inside of the input area.
<49> 6/26/92 KST <JH>: In case application opens the input method as a text
service (event we documented not to do so), do not remove/delete
its menu.
<48> 6/26/92 KST #1033450,1033456,1031316 <JH>: When creating a new TSM document,
we just open an input method's instance but don't activate it.
<47> 6/23/92 KST Added an important comment for DeleteTSMDocument. (Just
documentation changes only).
<46> 6/17/92 KST #1032783 <JH>: Checking for interface type must use '&&' not
'||'. All right, John, stop laughing.
<45> 6/17/92 KST #1032783 <JH>: The refcon was putting in the AE record with the
wrong keyword, it should be keyAETSMDocumentRefcon.
<44> 6/17/92 KST #1032643 <JH>: When an application deletes all its TSM
documents, TSM will save some states info and turn global
UseInputWindow on for that app. This will allow user to use IM's
pallete and menu. Also when switch to Finder and then switch
back, the pencil will be correct.
<43> 6/15/92 KST Removed debugger statements.
<42> 6/14/92 KST #M. Sang, <JH>: 1. If a document is using input window and we
deactivate that document, FixTSMDocument was called in the wrong
place. 2. When DeleteTSMDocument calls utCloseIM4Document, we
remove IM's menu only from system menu list but keep it in app's
menu so that when a TSM aware application (using Akiko in Roman
promary script) deletes all the TSM documents, Akiko's menu will
still stays in the menu bar.
<41> 6/14/92 KST #1032466 <JH>: Fixed the bug when running a TSM aware
application in Roman primary script, select SJip and then Akiko,
Akiko is not activated.
<40> 6/10/92 KST #1031142,1030881 <JH>: Since we removed the docID param from
TSMEvent, UseInputWindow has been broken because keydown event
is routed to SWM recursively. Fix it with a TSM global keeping
track of which doc is using the input window. And added a new
private call for SWM -- TSMEventFromSWM.
<39> 6/2/92 JH #1028635,1030481,1030631,1030634,1030647 <KST>: Added calls to
the new routine in InternalUtils to add input method menus as
system menus.
<38> 5/18/92 KST #1029814 <JH>: NewTSMDocument should check the supported
interface type and number of interfaces.
<37> 5/6/92 KST #1028642,<JH>: Fixed the bug: "floating pallete stays when
switch from Akiko to Roman in TSM aware application".
<36> 5/1/92 KST #1027482,<JH>: If input method changed cursor, then we set a
flag in TSM globals to indicate so.
<35> 5/1/92 JH Use the result of SetTextServiceCursor to set a flag telling our
SetCursor patch whether it should let SetCursor calls go through
or not.
<34> 4/20/92 YK #1026761: Before calling InsertMenu and DrawMenuBar, make sure
that the menu handle is not NIL.
<33> 4/10/92 KST JH,Fixed a bug when we launch a TSM aware app, the menu is not
displayed. Fix in "utTryRemoveMenu".
<32> 4/9/92 KST JH,modifying utNewTSMDocument to support the fact that SWM is no
longer an application but a driver.
<31> 3/30/92 KST JH, 1025008, 1025009. First attempt to solve the pallete/menu
bar flickering problem. xDeactivateTSMDocument will not remove
IM's pallete and menu. We now also change keyboard menu to
synchronize the keyscript with the TSM document.
<30> 3/23/92 KST Code changed according to the comments from the code review.
<29> 3/20/92 KST JH, In "utNewTSMDocument", I should dereference the handle after
the utRecordDocumentID call.
<28> 3/18/92 KST Pass menu select call to text services only if the menu ID
matches with the menu ID created by that text services.
<27> 3/17/92 KST Allow creating TSM document in any scripts, not restricted to
only CJK script.
<26> 3/16/92 KST Removed DocumentID parameter from TSMEvent, TSMMenuSelect, and
SetTSMCursor calls. It applies to the current active TSM
document.
<25> 3/12/92 KST We store a refcon in each document record. NewTSMDocument now
takes one more parameter. In "xSendAEtoClient", we return that
refcon in Apple event record.
<24> 3/6/92 KST Added documentation for change #22. Also released memory for
some objects in AESend.
<23> 3/2/92 KST Added a private TSM call "NewTSMDocument4SWM". Also
"utOpenDefaultIM" now takes a Boolean flag.
<22> 2/29/92 KST In xActivateTSMDocument, if there is currently an active
document which belongs to SWM, then send an AE to ask her to
close the input window.
<21> 2/28/92 KST FixTSMDocument and SetTSMCursor didn't pass the call to the
current Input Method.
<20> 2/27/92 KST Cannot call "DeleteMenu" when the TSM aware application is
exiting to shell. Use our new call "utDeleteMenu".
<19> 2/26/92 KST Took out debugger break.
<18> 2/22/92 KST When sending event to input method Component, we set the system
to system mode.
<17> 2/11/92 DCL Changed the name of TSMEqu.[aph] to TextServices.[aph] for
better read-ability.
<16> 2/10/92 KST Moved all internal routines to TSMInternalUtil.c file.
<15> 2/1/92 KST We don't have a global flag in TSM globals to indicate using
bottom window. Change the byte to indicate the number of active
text services for ease of debugging.
<14> 1/30/92 KST Removed Debugger code.
<13> 1/29/92 KST Open Input method only when the correct environment has set up.
Now we open one input method for each document (not shared).
<12> 1/16/92 KST Renamed error code.
<11> 1/13/92 KST In "utOpenDefaultIM", if the default input method is already
open, we return noErr. We also call Initiate and Activate
TextService after successful open. Currently IM is never
deactivated.
<10> 1/11/92 KST Adding code to support old Japanese IM and use input window.
<9> 1/9/92 KST Adding code to support input method synchronization.
<8> 1/8/92 KST In NewTSMDocument, the default input method for the current
script is automatically opened if there is one.
<7> 1/4/92 KST Add a new routine "CloseTextService". Also "OpenTextService" now
returns component instance to the caller.
<6> 12/31/91 KST We now record all open DocumentIDs so that it can be cleaned up
upon process termination.
<5> 12/20/91 KST Making progress toward Alpha. Changed "inline" to "TSM", and
added new calls.
<4> 12/10/91 KST Adding a new "InlineAwareRegister" call.
<3> 11/26/91 KST Low level TSM routines are now dispatched directly to the
Component Manager.
<2> 11/23/91 KST Check in the first time for Cube-E.
*/
/* ********************************************************************************
* File: "TSMFunctions.c"
*
* Written by Kenny SC. Tung
*
* Modification History:
* 18Nov91 KSCT New today.
*
* External routines:
* xNewTSMDocument
* xDeleteTSMDocument
* xDeactivateTSMDocument
* xActivateTSMDocument
* xTSMEvent
* xTSMMenuSelect
* xFixTSMDocument
* xSetTSMCursor
* xGetServiceList
* xOpenTextService
* xCloseTextService
* xSendAEtoClient
* ******************************************************************************** */
#include <Script.h>
#include <MFPrivate.h>
#include <TextServices.h>
#include <TSMPrivate.h>
#ifndef __DEVICES__
#include <Devices.h>
#endif
/* ************************************ *
* *
* ¥¥ TSM routines start from here ¥¥ *
* *
* ************************************ */
/* High level TSM routines ... */
/* ********************************************************************** *
* Function: utNewTSMDocument *
* Code shared by xNewTSMDocument4SWM and xNewTSMDocument. *
* ActivateP is false when called by SWM and SWM only!!! *
* Because SWM is not an app now, so we need to fake a PSN. *
* Input: activateP = false means called by SWM ! *
* *
* Modification History: *
* 02Mar92 KST New today. SWM creates the document in the background, if *
* it uses xNewTSMDocument, then input method's pallete will *
* be visible on the Desktop just after booting the system. *
* 06Mar92 KST We now store an application's refcon in each document. *
* 26Mar92 KST We now keep track of the script used by this document. *
* 07Apr92 KST If the current script is not one of CJK, then need to set *
* iDocUseThisScript to kNoSuchScript. Otherwise the keyscript *
* will be changed to the wrong script when this document is *
* activated. (this fix is basically for SWM). *
* 09Apr92 KST If activateP is false, then we are called by SWM, fabricate *
* a PSN using SWM's drive refnum. *
* 18May92 KST I should check the supported interface type and the number *
* of interfaces. (only 1 now) *
* ********************************************************************** */
OSErr
utNewTSMDocument( short numOfInterface,
OSType supportedInterfaceTypes[],
TSMDocumentID *idocIDPtr,
long refcon,
Boolean activateP)
{
Handle aHandle;
long tempLong = sizeof(TSMDocRecord);
short i;
OSErr err;
ScriptCode currentScript;
TSMDocRecord *idocRecPtr;
ProcessSerialNumber PSNRecord;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
if (numOfInterface != 1) return tsmUnsupportedTypeErr; /* support only one type now */
// When we define a new type in the future, we need to calculate the handle size like this ...
// tempLong += sizeof(OSType) * (numOfInterface - 1); /* record size is NOT fixed */
if (supportedInterfaceTypes[0] != kTextService && /* type has to be 'tsvc' */
supportedInterfaceTypes[0] != TSMTEInterfaceType ) /* ....... or 'tmTE' */
return tsmUnsupportedTypeErr;
aHandle = NewHandleClear(tempLong);
if (aHandle == nil) return memFullErr; /* no memory */
if ( activateP ) /* called by a TSM aware app */
(void) GetCurrentProcess(&PSNRecord); /* get current process's PSN */
else { /* this is our dummied up SWM PSN */
PSNRecord.highLongOfPSN = TSMVarsPtr->tsmSWMDrvrRefNum << 16; /* so dummy one up */
PSNRecord.lowLongOfPSN = TSMVarsPtr->tsmSWMDrvrRefNum << 16;
}
err = utaRecordDocumentID(aHandle, &PSNRecord); /* record the ID */
if (err) { /* app is not TSM aware */
DisposeHandle(aHandle); /* release the handle */
*idocIDPtr = nil;
return err; /* and say goodbye */
}
/* initialize the TSM document record ... */
currentScript = TSMVarsPtr->tsmCurrentSLRec.fScript;
idocRecPtr = (TSMDocRecord *) *aHandle; /* dereference the handle after the call <#29> */
idocRecPtr->iDocPSNRecord.highLongOfPSN = PSNRecord.highLongOfPSN; /* owner of this TSM document */
idocRecPtr->iDocPSNRecord.lowLongOfPSN = PSNRecord.lowLongOfPSN;
idocRecPtr->iDocSignature = kTSMDocumentSignature;
idocRecPtr->iDocOpenTextServiceN= 0;
idocRecPtr->iDocCIHead = nil;
idocRecPtr->iDocActiveP = false;
idocRecPtr->iDocUseInputWindowP = false; /* local flag only */
idocRecPtr->iDocRefcon = refcon; /* private storage for application <#25> */
idocRecPtr->iDocInterfaceN = numOfInterface;
if ( activateP ) /* called by a TSM aware application <#37> */
idocRecPtr->iDocUseThisScript = currentScript; /* inline-aware doc uses current script <#37> */
else idocRecPtr->iDocUseThisScript = kNoSuchScript; /* SWM's doc has no initial script, neutral <#37> */
for (i=0; i<numOfInterface; i++)
idocRecPtr->iDocOSTypeArray[i] = supportedInterfaceTypes[i];
*idocIDPtr = aHandle; /* return the handle as ID */
if (utScriptIsCCJKAndNeedsIM(currentScript)) /* If the current script needs IM */
err = utOpenActivateDefaultIM(*idocIDPtr, false); /* open the default IM but don't activate it !! */
else
err = noErr; /* no input method */
return err;
}; /* utNewTSMDocument */
/* ********************************************************************** *
* Function: xNewTSMDocument4SWM *
* SWM creates a new TSM aware Document. *
* Input: numOfInterface = number of supported interfaces, *
* supportedInterfaceTypes = array of OSTypes. *
* Output: If no error, return the handle as idocID. *
* Error code: GetCurrentProcess, *
* tsmNeverRegisteredErr = app is not TSM aware, *
* Side effect:Allocate a handle from application heap. *
* This document ID is also recorded in TSM's PSN table. *
* The default input method for the current script is opened *
* if there is one, BUT it is not activated. *
* *
* Modification History: *
* 02Mar92 KST New today. SWM creates the document in the background, if *
* it uses xNewTSMDocument, then input method's pallete will *
* be visible on the Desktop just after booting the system. *
* ********************************************************************** */
pascal OSErr xNewTSMDocument4SWM(short numOfInterface, OSType supportedInterfaceTypes[], TSMDocumentID *idocID)
{
/* Do not activate the input method, and refcon = 0 ... */
return utNewTSMDocument(numOfInterface, supportedInterfaceTypes, idocID, 0, false);
}
/* ********************************************************************** *
* Function: xNewTSMDocument *
* Application creates a new TSM aware Document. *
* Input: numOfInterface = number of supported interfaces, *
* supportedInterfaceTypes = array of OSTypes. *
* Output: If no error, return the handle as idocID. *
* Error code: GetCurrentProcess, *
* tsmNeverRegisteredErr = app is not TSM aware, *
* Side effect:Allocate a handle from application heap. *
* This document ID is also recorded in TSM's PSN table. *
* The default input method for the current script is opened *
* if there is one, and it is always activated. *
* *
* Modification History: *
* 10Jan92 KST The default input method is not opened for each document. *
* Instead, it is always open and saved in TSM globals. *
* 06Mar92 KST We now have a new parameter -- refcon. *
* ********************************************************************** */
pascal OSErr xNewTSMDocument(short numOfInterface, OSType supportedInterfaceTypes[], TSMDocumentID *idocID, long refcon)
{
/* Do activate the input method ... */
return utNewTSMDocument(numOfInterface, supportedInterfaceTypes, idocID, refcon, true);
}
/* *********************************************************************** *
* Function: xDeleteTSMDocument *
* Application wants to close the TSM aware Document. *
* Release all memory used by this doc and text services. *
* Input: idocID = TSM Document ID *
* Output: noErr = no error, *
* tsmInvalidDocIDErr = invalid document ID, *
* tsmTSMDocBusyErr = document is still active, *
* Side effect: 1. Will call "xDeactivateTSMDocument" for the document ID. *
* All component instances are closed, and memory freed. *
* The ID is also removed from TSM's PSN table. *
* 2. Since we don't want to remove IM's pallete/menu, but *
* now we are deleting the document, what can we do to *
* remember the info for this document? We create a copy *
* of the document and give it a special signature. Keep *
* this special doc. in the tsmPreviousDocID field. *
* The meaning of a shadow ID are: *
* 1. The real document ID has been deleted. *
* 2. This handle is from system heap. And is just a copy of the real ID. *
* 3. Instance has been closed, openedDefIMCInstance does not mean *
* anything even it is not nil !!! *
* 4. The size of the handle is always the sizeof(TSMDocRecord). *
* *
* Modification history: *
* 30Mar92 KST Before we dispose of the ID Handle, or before closing the *
* input methods, to be precise. We need to check if the doc *
* ID is saved in the TSMglobal's tsmPreviousDocID field. If *
* it is, then we have to do something special. (make a copy) *
* *********************************************************************** */
pascal OSErr xDeleteTSMDocument(TSMDocumentID idocID)
{ OSErr err;
short i, count;
CIListRec *aListRecPtr;
TSMDocRecord *idocRecPtr;
Handle anewHandle;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
TSMAppsRecord *appRecordPtr;
err = utVerifyTSMDocID(idocID);
if (err != noErr) /* invalid id */
return err;
err = xDeactivateTSMDocument(idocID); /* deactivate the document, and ... */
/* this will set TSMVarsPtr->tsmPreviousDocID, because we *
* don't want to hide the pallete when we delete the document. */
if (TSMVarsPtr->tsmPreviousDocID == idocID) { /* new 30Mar92 #31 */
anewHandle = NewHandleSysClear(sizeof(TSMDocRecord)); /* from system heap */
if (anewHandle != nil) { /* has memory */
BlockMove(*((Handle) idocID), *anewHandle, sizeof(TSMDocRecord)); /* copy the handle and change its signature */
((TSMDocRecord *) *anewHandle)->iDocSignature = kTSMShadowDocumentSig;
}
TSMVarsPtr->tsmPreviousDocID = anewHandle; /* replace it with a temp doc ID or nil */
}
/* .. close all opened input methods .... */
(void) utCloseIM4Document(idocID, smJapanese, false); /* ignore error (it might not be open) */
(void) utCloseIM4Document(idocID, smTradChinese,false);
(void) utCloseIM4Document(idocID, smSimpChinese,false);
(void) utCloseIM4Document(idocID, smKorean, false);
HLock((Handle) idocID);
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
aListRecPtr = idocRecPtr->iDocCIHead;
for (i=0; i<idocRecPtr->iDocOpenTextServiceN; i++) { /* close all opened components */
if (aListRecPtr) { /* just to be safe */
if (aListRecPtr->ciServiceMenu && /* if the text service has a menu */
(*(aListRecPtr->ciServiceMenu))->menuID != -kTSMSystemMenuID) { /* and this is not a system menu */
if (aListRecPtr->ciMenuVisibleP) { /* if it is visible, then remove it */
utDeleteMenu((*(aListRecPtr->ciServiceMenu))->menuID); /* call our delete menu routine */
aListRecPtr->ciMenuVisibleP = false; /* in case some stupid guy open the input method as a text service */
}
DisposeHandle((Handle) aListRecPtr->ciServiceMenu); /* release the menu */
aListRecPtr->ciServiceMenu = nil; /* no menu now */
}
CloseComponent(aListRecPtr->ciTS); /* ignore error */
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
else break;
}
HUnlock((Handle) idocID);
err = utaRemoveDocumentID((Handle) idocID, &count, &appRecordPtr); /* remove the ID from TSM internal table */
DisposeHandle((Handle) idocID); /* release the handle */
if (err == noErr && count == 0) { /* alert: the TSM aware application has no more document */
if (!appRecordPtr->psnTSMCalledUseIWP) { /* if this is a re-entrant call, don't do it again <#52> */
appRecordPtr->psnOldUseIWGlobalState = appRecordPtr->psnAppUseInputWindowP; /* save the previous state */
err = xUseInputWindow(nil, true); /* turn use input window ON */
if (err == noErr) {
appRecordPtr->psnTSMCalledUseIWP = true; /* mark TSM did this */
}
else {
appRecordPtr->psnTSMCalledUseIWP = false;
appRecordPtr->psnAppUseInputWindowP = appRecordPtr->psnOldUseIWGlobalState; /* restore state */
}
}
}
return err;
};
/* ************************************************************************** *
* Function: xDeactivateTSMDocument *
* Deactivate an TSM aware Document. *
* Input: idocID = TSM Document ID *
* Output: noErr If no error, *
* tsmInvalidDocIDErr = invalid id, *
* tsmDocNotActiveErr = the document is not activated, *
* Side effect:Set active falg to false. *
* Deactivate all opened Text Services. *
* Deactivate the Input Method of the current script. *
* After the call, tsmUseInputWindowDocID = nil, *
* tsmActiveDocID = nil. *
* *
* Modification history: *
* 24Mar92 KST Need to call utSoftDeactivateIM4Document, not utDeactivateIM4Document *
* UtSoftDeactivateIM4Document will not remove IM's pallete and menu. *
* 28Mar92 KST For SWM, once we deactivate the document, the iDocUseThisScript *
* field should be nullified 'cause SWM's doc is shared by 4 CCJK *
* scripts. This makes the whole thing so completed. *
* 05Jun92 KST If tsmActiveDocID == tsmUseInputWindowDocID, then deactivate the ID. *
* If tsmActiveDocID != tsmUseInputWindowDocID, then tsmActiveDocID has *
* to be SWM's doc. I'll deactivate both two documents ! *
* ********************************************************************************** */
pascal OSErr xDeactivateTSMDocument(TSMDocumentID idocID)
{ OSErr err, appUseIWError;
short i;
ScriptCode currentScript;
CIListRec *aListRecPtr;
TSMDocRecord *idocRecPtr;
ComponentResult cmResult;
ProcessSerialNumber PSNRecord;
Boolean deactivateSWMDocP;
TSMDocumentID currentTSMActiveDocID;
DefIMInstanceRecord *openedDefIMRecordPtr;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
err = utVerifyTSMDocID(idocID);
if (err != noErr) /* invalid id */
return err;
currentScript = TSMVarsPtr->tsmCurrentSLRec.fScript;
if (! ((TSMDocRecordPtr) *(Handle) idocID)->iDocActiveP ) { /* if doc is not active */
/* If user just create and then delete the document, it is not active but
* there could be an open input method ................. <26Jun92 #48> */
HLock((Handle) idocID); /* lock the handle */
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
for (i=0; i<kSavedDefaultIMCount; i++) {
openedDefIMRecordPtr = &(idocRecPtr->iDocDefIMInstanceArray[i]);
if (openedDefIMRecordPtr->openedDefIMCInstance && /* if there is one open */
openedDefIMRecordPtr->openedDefIMActiveP) { /* and the input method is active */
err = DeactivateTextService(openedDefIMRecordPtr->openedDefIMCInstance); /* what the app can do on error ??? */
if (err == noErr)
openedDefIMRecordPtr->openedDefIMActiveP = false; /* not active anymore */
}
}
HUnlock((Handle) idocID); /* unlock the handle */
return tsmDocNotActiveErr; /* stop right now */
}
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
if (idocRecPtr->iDocPSNRecord == TSMVarsPtr->tsmSWMsPSNRecord) /* doc belongs to SWM */
deactivateSWMDocP = true;
else deactivateSWMDocP = false;
(void) GetCurrentProcess(&PSNRecord); /* get current process's PSN */
appUseIWError = utaIsAppTSMAwareAndNotUsingIW(&PSNRecord); /* for TSM aware app */
/* don't fix text 'cause it might send to another application ..... */
//if (appUseIWError == tsmUseInputWindowErr) /* use input window */
// (void) xFixTSMDocument( TSMVarsPtr->tsmActiveDocID ); /* then fix its input area ... <14Jun93 #42> */
/* deactivate the default input method of the current keyscript -- <27Jan92> */
if ( utDocNeedsInputMethod(currentScript, &idocRecPtr->iDocPSNRecord) ) {
// if ( deactivateSWMDocP ) /* if this is SWM */
// (void) xFixTSMDocument( idocID ); /* then fix its input area ... <14Jun93 #42> */
(void) utSoftDeactivateIM4Document(idocID, currentScript );
TSMVarsPtr->tsmActiveTSCount--; /* one less active TS -- <01Feb92 #15> */
}
else
if (appUseIWError == tsmUseInputWindowErr ) { /* use input window, just deactivate the IM */
/* Don't hide the pallete/menu, just save the document ID we are deactivating */
(void) utJustDeactivateIM4Document(idocID, currentScript, false ); /* <#48> */
TSMVarsPtr->tsmPreviousDocID = idocID; /* save this ID <#13> */
TSMVarsPtr->tsmPreviousScript = currentScript; /* save this script <#13> */
}
err = noErr;
HLock((Handle) idocID); /* lock the handle */
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
aListRecPtr = idocRecPtr->iDocCIHead;
for (i=0; i<idocRecPtr->iDocOpenTextServiceN; i++) { /* call each open text service */
if (aListRecPtr) { /* just to be safe */
cmResult = DeactivateTextService(aListRecPtr->ciTS); /* what the app can do on error ??? */
if (cmResult) {
err = cmResult; /* change the error to short */
break;
}
TSMVarsPtr->tsmActiveTSCount--; /* one less active TS -- <01Feb92 #15> */
/* if the text service has a menu, remove it from menu bar */
if (aListRecPtr->ciServiceMenu) { /* if the text service has a menu */
if (aListRecPtr->ciMenuVisibleP) { /* and is visible */
if ((*(aListRecPtr->ciServiceMenu))->menuID != -kTSMSystemMenuID) { /* if this is not a system menu */
utDeleteMenu((*(aListRecPtr->ciServiceMenu))->menuID); /* call our delete menu routine */
aListRecPtr->ciMenuVisibleP = false; /* in case some stupid guy open the input method as a text service */
}
}
}
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
}
if (err == noErr) {
idocRecPtr->iDocActiveP = false;
currentTSMActiveDocID = TSMVarsPtr->tsmUseInputWindowDocID;
if ( currentTSMActiveDocID == idocID ) /* app will deactivate this one first */
TSMVarsPtr->tsmUseInputWindowDocID = nil;
#ifdef BuildTSMInit
else
if ( currentTSMActiveDocID ) Debugger();
#endif
currentTSMActiveDocID = TSMVarsPtr->tsmActiveDocID; /* then look at this current document */
if ( currentTSMActiveDocID ) {
if (currentTSMActiveDocID != idocID) { /* clear the global ... */
if ( ((TSMDocRecordPtr) *(Handle) currentTSMActiveDocID)->iDocPSNRecord == TSMVarsPtr->tsmSWMsPSNRecord) { /* current doc belongs to SWM */
/* this is because the doc is using input window, so we have 2 active documents */
err = utJustDeactivateIM4Document(currentTSMActiveDocID, TSMVarsPtr->tsmCurrentSLRec.fScript, false );
if (err == noErr) {
utCloseSWMWindow(false); /* ask SWM to close her input window */
((TSMDocRecord *) *((Handle) currentTSMActiveDocID))->iDocUseThisScript = kNoSuchScript;
}
}
#ifdef BuildTSMInit
else Debugger();
#endif
}
TSMVarsPtr->tsmActiveDocID = nil; /* no current document now */
TSMVarsPtr->tsmActiveTSCount = 0;
}
}
HUnlock((Handle) idocID);
return err;
};
/* *********************************************************** *
* Because there is no way to remove the menu after the TSM *
* aware app has been swapped out, we need to remove all other *
* IM's menu before we put out ours. *
* *********************************************************** */
void utTryRemoveMenu( TSMVarsRec *TSMVarsPtr )
{
short pos;
short currentIndex;
ComponentMenuRec *aCMRecordPtr;
currentIndex = utGetScriptIndex(TSMVarsPtr->tsmCurrentSLRec.fScript);
for (pos=0; pos<kSavedDefaultIMCount; pos++) {
if (pos != currentIndex) { /* leave alone the current one */
aCMRecordPtr = &(TSMVarsPtr->tsmDefIMInfoTable[pos]); /* #33 */
if (aCMRecordPtr->gMenuHandle) /* if it has a menu */
utDeleteMenu((*aCMRecordPtr->gMenuHandle)->menuID);
}
}
}
/* ************************************************************************** *
* Function: xActivateTSMDocument *
* Activate an TSM aware Document. *
* Input: idocID = TSM Document ID *
* Output: noErr If no error, *
* tsmInvalidDocIDErr = invalid id, *
* Side effect:Set active falg to true, *
* deactivate any currently active TSM aware document, *
* activate all opened Text Services too. *
* Update the 'UseInputWindow' flag in the PSN table. *
* *
* Modification History: *
* 04Mar92 KST If there is currently an active document which belongs to SWM, *
* then send an AE to ask her to close the input window if it is *
* visible. This happens when user switches from non-TSM aware *
* application to a TSM document. *
* 27Mar92 KST We now change keyboard menu to synchronize the keyscript *
* with the TSM document. *
* 09Apr92 KST Because there is no way to remove the menu after the TSM *
* aware app has been swapped out, we need to remove all other *
* IM's menu before we put out ours. *
* 07Jun92 KST To make UseInputWindow to work for each document, we have to *
* maintain a parallel active documentID -- tsmUseInputWindowDoc. *
* 08Jun92 KST If the app is using input window, then we need to delay SWM's *
* ActivateTSMDocument call until the app activates its document. *
* 10Jul92 KST Do not assume when a TSM aware application calls *
* ActivateTSMDocument(), the other document has been deactivated. *
* ************************************************************************** */
pascal OSErr xActivateTSMDocument(TSMDocumentID idocID)
{ OSErr err;
short i;
Boolean swmsDocIDp = false, /* true if the current active doc belongs to SWM */
activatingSWMsDocP = false, /* true if we are activating SWM's document */
useInputWindowCalledByTSMP = false, /* true if use InputWindow is called by TSM 'cause app has no document */
useInputWindowSpecialCase = false;
CIListRec *aListRecPtr;
TSMDocRecord *idocRecPtr;
ComponentResult cmResult;
ProcessSerialNumber PSNRecord;
TSMDocumentID theCurrentActiveDocID, theUseInputWindowDocID; /* <#53> */
ScriptCode theCurrentScript;
TSMAppsRecord *appRecordPtr;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
err = utVerifyTSMDocID(idocID);
if (err != noErr) /* invalid id */
return err;
if ( ((TSMDocRecordPtr) *(Handle) idocID)->iDocActiveP) { /* if we are currently active already ... */
return noErr; /* if you want to call it, I'll make you happy */
}
/* For a TSM aware application, usually tsmUseInputWindowDocID = tsmActiveDocID = current active document,
* but if this app calls UseInputWindow, then start typing, SWM will activate her document.
* In this case, I will:
* 1. keep tsmUseInputWindowDocID but change tsmActiveDocID = SWM's document,
* 2. deactivate the input method used by tsmUseInputWindowDocID, but keep TSs active!
* 3. activate SWM's document.
*/
theCurrentActiveDocID = TSMVarsPtr->tsmActiveDocID; /* optimize it */
theUseInputWindowDocID = TSMVarsPtr->tsmUseInputWindowDocID; /* too <#53> */
theCurrentScript = TSMVarsPtr->tsmCurrentSLRec.fScript;
if ( ((TSMDocRecordPtr) *(Handle) idocID)->iDocPSNRecord == TSMVarsPtr->tsmSWMsPSNRecord )
activatingSWMsDocP = true; /* true if we are activating SWM's document */
(void) GetCurrentProcess(&PSNRecord); /* get current process's PSN */
(void) utaGetTSMAppRecordPtr(&PSNRecord, &appRecordPtr);/* get the app record */
err = utaIsAppTSMAwareAndNotUsingIW(&PSNRecord); /* 2 calls should be combined */
// if (err == tsmNeverRegisteredErr) /* if the current application is not TSM aware */
// return err; /* this is OK, SWM is activating her doc */
if (err == tsmUseInputWindowErr) { /* if the current application is using Input Window */
if ( activatingSWMsDocP ) { /* if we are activating SWM's document */
/* If we are activating SWM's document and the document/app is using Input Window, then *
* we need to do this special check. Since SWM calls ActivateTSMDocument at every *
* 'doAccRun' time if the current app is using Input Window. We have to delay activating *
* SWM's document if the TSM aware app has not activated its own document. If we don't *
* delay SWM's call, then there will be 2 problems: *
* 1. We'll activate SWM document twice. If the two documents are using the same script, *
* then this is only a minor inefficiency for the first time is unnecessary. *
* 2. If the two documents are not using the same script, and the two documents are both *
* using Input Window, then the first time will mess up the state information kept by *
* TSM. ie., If you switch from Akiko to KIM, Akiko's pallete will stay on the screen. */
if (theCurrentActiveDocID == nil) { /* should yield to the app */
/* We should yield to the TSM aware app, but now we want to solve the NO TSM docuemnt bug *
* by always turning global UseInputWindow on. Soooooo, now we have to allow SWM activates *
* its document in this special case. Oh, I am sick and tired of this project !! <17Jun92> */
if ( !appRecordPtr->psnTSMCalledUseIWP ) /* TSM didn't call use input window */
return noErr; /* I hope the explanation above is clear enough */
/* if not, don't feel bad. I don't either ..... */
useInputWindowCalledByTSMP = true; /* now we know this is because TSM forced us .. */
}
if (theUseInputWindowDocID && /* this means the current app is TSM aware */
theCurrentActiveDocID == theUseInputWindowDocID ) {
useInputWindowSpecialCase = true; /* TSM aware app uses SWM's document, app's document is already activated */
}
}
}
if (theCurrentActiveDocID != nil) { /* another document is currently active ? */
if (useInputWindowSpecialCase) { /* do not deactivate the current document (it's using input window) */
/* Application activated its document, now tsmActiveDocID == tsmUseInputWindowDoc, if the app or *
* the document is using input window, then SWM will activate her document at 'doAccRun' time, *
* in this case, we don't want to deactivate the tsmActiveDocID, instead, we keep 2 active IDs. *
* When the application calls xUseInputWindow for one document, I know TSM already called *
* "utJustDeactivateIM4Document". But if the application is using Input Window globally, then the *
* document's Input Method has not been deactivated. So we have to call it here again. *
* utJustDeactivateIM4Document can be called more than once for the same document. */
// The current active document is not changed after this call but its current IM is deactivated ....
(void) utJustDeactivateIM4Document(theCurrentActiveDocID, theCurrentScript, true); /* so deactivate the input method but keep active */
}
else {
/* deactivate any currently active TSM aware document, there can be only one. */
if ( ((TSMDocRecordPtr) *(Handle) theCurrentActiveDocID)->iDocPSNRecord == TSMVarsPtr->tsmSWMsPSNRecord) /* doc belongs to SWM <#22> */
swmsDocIDp = true; /* true if the doc belongs to SWM */
else swmsDocIDp = false;
/* can't fix text ... */
(void) xDeactivateTSMDocument( theCurrentActiveDocID ); /* ignore error */
if (swmsDocIDp) /* doc belongs to SWM <#22> */
utCloseSWMWindow(false); /* ask SWM to close her input window <#22> */
if ( theUseInputWindowDocID && /* <#53> */
theCurrentActiveDocID != theUseInputWindowDocID ) { /* <#53> */
(void) xDeactivateTSMDocument( theUseInputWindowDocID ); /* <#53> */
TSMVarsPtr->tsmUseInputWindowDocID = nil; /* <#53> */
} /* <#53> */
} /* no current active document now */
TSMVarsPtr->tsmActiveDocID = nil; /* no active document now */
}
HLock((Handle) idocID); /* lock the handle */
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
/* Now, We want to change keyboard menu to synchronize the keyscript with this TSM document */
if (idocRecPtr->iDocUseThisScript != kNoSuchScript &&
idocRecPtr->iDocUseThisScript != theCurrentScript ) {
/* We have to be careful here because KeyScript will end up calling SetTextServiceLanguage. *
* Are we reentrant? SetTextServiceLanguage should never call xActivateTSMDocument, I hope! *
*/
theCurrentScript = idocRecPtr->iDocUseThisScript; /* current script will be changed */
KeyScript(theCurrentScript); /* change it to this document's keyscript */
}
err = noErr;
if ( !useInputWindowSpecialCase) { /* SWM will never open text services */
/* If there is a menu belongs to other IM, then remove it. */
// utTryRemoveMenu(TSMVarsPtr);
/* activate all the text services associated with the document */
aListRecPtr = idocRecPtr->iDocCIHead;
for (i=0; i<idocRecPtr->iDocOpenTextServiceN; i++) { /* call each open text service */
if (aListRecPtr) { /* just to be safe */
cmResult = ActivateTextService(aListRecPtr->ciTS); /* what the app can do on error ??? */
if (cmResult) { /* some error */
err = cmResult; /* change the error to short */
break;
}
TSMVarsPtr->tsmActiveTSCount++; /* bump the counter -- <01Feb92 #15> */
if (aListRecPtr->ciServiceMenu) { /* if it has a menu */
InsertMenu(aListRecPtr->ciServiceMenu, 0);
DrawMenuBar();
aListRecPtr->ciMenuVisibleP = true;
}
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
}
}
if (!useInputWindowCalledByTSMP) { /* if this is not a forced activate call by TSM */
if (appRecordPtr->psnTSMCalledUseIWP) { /* then restore the whole bloody mess */
appRecordPtr->psnTSMCalledUseIWP = false;
appRecordPtr->psnAppUseInputWindowP = appRecordPtr->psnOldUseIWGlobalState; /* restore state */
}
}
if (err == noErr) {
idocRecPtr->iDocActiveP = true; /* document is active */
TSMVarsPtr->tsmActiveDocID = idocID; /* save the current active doc id */
if ( !useInputWindowSpecialCase ) /* doc does not belongs to SWM */
TSMVarsPtr->tsmUseInputWindowDocID = idocID; /* then keep it as parallel ID */
utaUpdateDocFlagInPSNTable(idocID); /* update the PSN table use input window flag for the document */
if (utDocNeedsInputMethod(theCurrentScript, &idocRecPtr->iDocPSNRecord)) {
err = utActivateThisDocsInputMethod(idocID);
if (err == noErr)
TSMVarsPtr->tsmActiveTSCount++; /* bump the counter -- <01Feb92 #15> */
}
else {
/* We switch from doc1 (Japanese) to doc2 (Roman), here we have changed the current script to *
* Roman, so we are not going to activate any IM dor doc2. But now Akiko's pallete is still *
* hanging around because the previous doc has not been cleaned up !! -- <#37> */
if (TSMVarsPtr->tsmPreviousDocID)
utCleanUpPreviousDoc(TSMVarsPtr->tsmPreviousDocID, TSMVarsPtr->tsmCurrentSLRec.fScript);
}
}
HUnlock((Handle) idocID);
return err;
}; /* end xActivateTSMDocument */
/* ************************************************************************** *
* Function: utTSMEvent2InputMethod -- Send events to input method. *
* Input: Pointer to a EventRecord and documentation id. *
* Output: Return false if input method doesn't process the event. *
* Side effect:None. *
* Modification History: *
* 03Jun92 KST New today, need this call to solve the UseInputWindow problem. *
* ************************************************************************** */
Boolean utTSMEvent2InputMethod( EventRecord *eventPtr,
ScriptCode currentScript,
TSMDocumentID idocID) /* ID already verified */
{
TSMDocRecord *idocRecPtr;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
DefIMInstanceRecord *openedDefIMRecordPtr;
ComponentResult cmResult = 0;
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
if (idocRecPtr->iDocActiveP == false) /* if not active */
return false; /* stop right now */
/* Now first pass the event to the default input method of the current key script if we're using one */
/* Check if the document is using input window or old JIM, if it is, don't pass the event on */
openedDefIMRecordPtr = &(idocRecPtr->iDocDefIMInstanceArray[utGetScriptIndex(currentScript)]);
if (openedDefIMRecordPtr->openedDefIMCInstance) { /* has open input method */
// ???????
if (!openedDefIMRecordPtr->openedDefIMActiveP) /* if the input method is NOT active */
return false; /* stop right now */
TSMVarsPtr->tsmPatchFlags |= kInTSMEventCall; /* say we are inside of the TextServiceEvent call <#40> */
(void) BeginSystemMode(); /* input method is treated as system service <#18> */
cmResult = TextServiceEvent(openedDefIMRecordPtr->openedDefIMCInstance, 1, eventPtr);
(void) EndSystemMode(); /* close the window <#18> */
TSMVarsPtr->tsmPatchFlags &= ~kInTSMEventCall; /* say we are out of the TextServiceEvent call <#40> */
if (cmResult) /* input method handled the call */
return true; /* so break now ... */
}
return false; /* input method does NOT handled the call */
}
/* ************************************************************************** *
* Function: TSMEventFromSWM -- private call for SWM only. *
* SWM passes all events to TSM. *
* (This is actually the original TSMEvent code). *
* Input: Pointer to a EventRecord and documentation id. *
* Output: Return false if input method doesn't process the event. *
* Side effect:None. *
* Note: SWM only handles input, it does not deal with text services. *
* Modification History: *
* 03Jun92 KST New today, need this call to solve the UseInputWindow problem. *
* ************************************************************************** */
pascal Boolean xTSMEventFromSWM(EventRecord *eventPtr) // pass all events to TSM
{
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
ScriptCode currentScript = TSMVarsPtr->tsmCurrentSLRec.fScript;
TSMDocumentID idocID;
idocID = TSMVarsPtr->tsmActiveDocID; /* use current TSM document ID as default <#26> */
if (utDocNeedsInputMethod(currentScript, &TSMVarsPtr->tsmSWMsPSNRecord)) {
if (utVerifyTSMDocID(idocID) != noErr) /* invalid id */
return false; /* nobody handled the event */
return utTSMEvent2InputMethod( eventPtr, currentScript, idocID ); /* use common code -- for input method */
}
return false ; /* if does not need input method, return false */
};
/* ****************************************************************** *
* Function: xTSMEvent *
* Application passes all events to TSM. *
* We will call all the open text services until one *
* handles it or no more TS to call. *
* *
* Special case for Input Method: only one IM will be *
* called which matches the current keyboard script. *
* Input: Pointer to a EventRecord and documentation id. *
* Output: Return false if text service doesn't process the event. *
* Side effect:None. *
* *
* Modification History: *
* 10Jan92 KST If the system is using old Japanese input method, or *
* if the document is using input window, then don't pass *
* the event to text services. *
* 10Jan92 KST The default input method is not opened for each. *
* document, it is always open and saved in TSM globals. *
* 16Mar92 KST Removed DocumentID parameter. It applies to the current *
* active TSM document. Will return false if there is no *
* TSM document, or the document is not active. <#26> *
* 03Jun92 KST Changed this code to be called only by TSM aware apps *
* so that we can handle if the doc/app is using the input *
* window. SWM will no longer use this call. *
* ****************************************************************** */
pascal Boolean xTSMEvent(EventRecord *eventPtr) /* pass all events to TSM by application */
{ /* SWM shall never call this routine !!! */
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
ScriptCode currentScript = TSMVarsPtr->tsmCurrentSLRec.fScript;
Boolean result;
TSMDocumentID idocID;
OSErr err;
short numOfEvents = 1; /* always sending 1 event */
CIListRec *aListRecPtr;
ComponentResult cmResult = 0;
TSMDocRecord *idocRecPtr;
ProcessSerialNumber PSNRecord;
// This flag is set in SWM's _ModalDialogPatch, and we reset this flag when TSMEvent is called.
TSMVarsPtr->tsmPatchFlags &= ~kTSMEventBeenCalled; /* <#54> */
/* For a TSM aware application, usually tsmUseInputWindowDocID = tsmActiveDocID = current active document,
* but if this app calls UseInputWindow, then tsmUseInputWindowDocID = current active document, and
* tsmActiveDocID = SWM's document. When app opens a text service, the text service is recorded with
* the app's document, and therefore when event is sent to text services, we should use tsmUseInputWindowDocID.
*/
idocID = TSMVarsPtr->tsmUseInputWindowDocID; /* use tsmUseInputWindowDocID document ID as default <#40> */
// I know if the app is using input window, then the IM with tsmUseInputWindowDocID is deactivated.
// But this is all right becuase in this case, we will not send event to IM any way ....
if (utVerifyTSMDocID(idocID) != noErr) /* invalid id */
return false; /* nobody handled the event */
err = GetCurrentProcess(&PSNRecord);
if (err) return false; /* what is this error? */
/* now is the tricky part, return false if:
1. the current application is not TSM aware, or
2. the app is TSM aware, but is using input window, or
3. the app is TSM aware, but is using old JIM. */
if (utDocNeedsInputMethod(currentScript, &PSNRecord)) { /* if app needs input method */
result = utTSMEvent2InputMethod( eventPtr, currentScript, idocID ); /* use common code -- for input method */
if (result) /* event handled by IM */
return true; /* so reutnr now */
}
/* If input method didn't handle the event, pass the event to other open text services ... */
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
if (idocRecPtr->iDocOpenTextServiceN == 0) /* if no open text services */
return false; /* stop right now */
HLock((Handle) idocID); /* lock the handle */
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
aListRecPtr = idocRecPtr->iDocCIHead;
while (aListRecPtr) { /* call each open TS */
TSMVarsPtr->tsmPatchFlags |= kInTSMEventCall; /* say we are inside of the TextServiceEvent call <#40> */
cmResult = TextServiceEvent(aListRecPtr->ciTS, numOfEvents, eventPtr);
TSMVarsPtr->tsmPatchFlags &= ~kInTSMEventCall; /* say we are out of the TextServiceEvent call <#40> */
if (cmResult) /* text service handled the call */
break; /* so break now ... */
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
HUnlock((Handle) idocID);
if (cmResult) /* if text service handled the event */
return true; /* and return true */
else return false; /* didn't handle the event */
};
/* ****************************************************************** *
* Function: xTSMMenuSelect *
* User chooses a menu item, pass it to text service. *
* We will call all the open text services until one *
* handles it or no more TS to call. *
* Input: idocID *
* menuResult *
* Output: false if text service doesn't handle the menu selection *
* true if text service handles the menu selection. *
* *
* Side effect:None. *
* *
* Modification History: *
* 10Jan92 KST If the system is using old Japanese input method, or *
* if the document is using input window, then don't pass *
* the event to text services. *
* 16Mar92 KST Removed DocumentID parameter. It applies to the current *
* active TSM document. Will return false if there is no *
* TSM document, or the document is not active. <#26> *
* ****************************************************************** */
pascal Boolean xTSMMenuSelect(long menuResult) // pass menu selection to TSM
{
short menuItem, menuID, i;
CIListRec *aListRecPtr;
ComponentResult cmResult = 0;
TSMDocRecord *idocRecPtr;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
DefIMInstanceRecord *openedDefIMRecordPtr;
TSMDocumentID idocID;
idocID = TSMVarsPtr->tsmActiveDocID; /* use current TSM document ID as default <#26> */
if (utVerifyTSMDocID(idocID) != noErr) /* invalid id */
return false;
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
if (idocRecPtr->iDocActiveP == false) /* if not active */
return false; /* stop right now */
menuID = menuResult >> 16;
menuItem = (short) menuResult & 0xFFFF;
/* Now first pass the event to the default input method of the current key script */
/* Check if the document is using input window or old JIM, if it is, don't pass the event on */
if (utDocNeedsInputMethod(TSMVarsPtr->tsmCurrentSLRec.fScript, &idocRecPtr->iDocPSNRecord)) {
/* Note: "utDocNeedsInputMethod" does not move memory */
openedDefIMRecordPtr = &(idocRecPtr->iDocDefIMInstanceArray[utGetScriptIndex(TSMVarsPtr->tsmCurrentSLRec.fScript)]);
if (openedDefIMRecordPtr->openedDefIMCInstance) { /* double check if it is open */
#ifdef BuildTSMInit
if (!openedDefIMRecordPtr->openedDefIMActiveP) /* if the input method is NOT active */
Debugger(); /* let me know */
#endif
if (menuID == -kTSMSystemMenuID ) { /* do I need to check this ??? */
(void) BeginSystemMode(); /* input method is treated as system service <#18> */
cmResult = TextServiceMenuSelect(openedDefIMRecordPtr->openedDefIMCInstance, openedDefIMRecordPtr->openedDefIMServiceMenuHdl, menuItem);
(void) EndSystemMode(); /* close the window really quick <#18> */
if (cmResult) /* input method handled the selection */
return true; /* so break now ... */
}
}
}
HLock((Handle) idocID); /* lock the handle */
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
aListRecPtr = idocRecPtr->iDocCIHead;
for (i=0; i<idocRecPtr->iDocOpenTextServiceN; i++) { /* call each open text service */
if (aListRecPtr && aListRecPtr->ciServiceMenu) { /* call if it has a menu */
if ((*(aListRecPtr->ciServiceMenu))->menuID == menuID) { /* for the right menu */
cmResult = TextServiceMenuSelect(aListRecPtr->ciTS, aListRecPtr->ciServiceMenu, menuItem);
if (cmResult) /* text service handled the call */
break; /* so break now ... */
}
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
}
HUnlock((Handle) idocID);
if (cmResult) /* if text service handled the event */
return true; /* and return true */
else return false; /* didn't handle the event */
};
/* ****************************************************************** *
* Function: xFixTSMDocument *
* Confirm the current active input area. *
* Input: idocID *
* Output: noErr If no error, *
* tsmInvalidDocIDErr = invalid doc id, *
* tsmDocNotActiveErr = the document is not activated, *
* tsmNoOpenTSErr = no open text service. *
* Side effect:None. *
* *
* ****************************************************************** */
pascal OSErr xFixTSMDocument(TSMDocumentID idocID)
{ OSErr err;
TSMDocRecord *idocRecPtr;
ComponentResult cmResult = 0;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
DefIMInstanceRecord *openedDefIMRecordPtr;
err = utVerifyTSMDocID(idocID);
if (err != noErr) /* invalid id */
return err;
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
if (!idocRecPtr->iDocActiveP) /* if not active, can not continue */
return tsmDocNotActiveErr;
/* Now first pass the event to the default input method of the current key script if we're using one */
/* Check if the document is using input window or old JIM, if it is, don't pass the event on */
if (utDocNeedsInputMethod(TSMVarsPtr->tsmCurrentSLRec.fScript, &idocRecPtr->iDocPSNRecord)) {
/* Note: "utDocNeedsInputMethod" does not move memory */
openedDefIMRecordPtr = &(idocRecPtr->iDocDefIMInstanceArray[utGetScriptIndex(TSMVarsPtr->tsmCurrentSLRec.fScript)]);
if (openedDefIMRecordPtr->openedDefIMCInstance) { /* already open */
if (!openedDefIMRecordPtr->openedDefIMActiveP) /* if the input method is NOT active */
return tsmTSNotOpenErr; /* let me know */
(void) BeginSystemMode(); /* input method is treated as system service <#18> */
cmResult = FixTextService(openedDefIMRecordPtr->openedDefIMCInstance);
(void) EndSystemMode(); /* close the window really quick <#18> */
}
else cmResult = tsmNoOpenTSErr;
}
return ((OSErr) cmResult);
};
/* ****************************************************************** *
* Function: xSetTSMCursor *
* Give each text service a chance to set its cursor. *
* We will call all the open text services until one *
* handles it or no more TS to call. *
* Input: idocID *
* mousePos *
* Output: false if text service doesn't set the cursor, *
* true if text service set the cursor. *
* *
* Side effect:None. *
* *
* Modification History: *
* 16Mar92 KST Removed DocumentID parameter. It applies to the current *
* active TSM document. Will return false if there is no *
* TSM document, or the document is not active. <#26> *
* ****************************************************************** */
pascal Boolean xSetTSMCursor(Point mousePos)
{
short i;
ComponentResult cmResult = 0;
CIListRec *aListRecPtr;
TSMDocRecord *idocRecPtr;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
DefIMInstanceRecord *openedDefIMRecordPtr;
TSMDocumentID idocID;
idocID = TSMVarsPtr->tsmActiveDocID; /* use current TSM document ID as default <#26> */
if ( utVerifyTSMDocID(idocID) != noErr ) /* invalid id */
return false;
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
if (idocRecPtr->iDocActiveP == false) /* if not active */
return false; /* stop right now */
/* Now first pass the event to the default input method of the current key script if we're using one */
/* Check if the document is using input window or old JIM, if it is, don't pass the event on */
if (utDocNeedsInputMethod(TSMVarsPtr->tsmCurrentSLRec.fScript, &idocRecPtr->iDocPSNRecord)) {
/* Note: "utDocNeedsInputMethod" does not move memory */
openedDefIMRecordPtr = &(idocRecPtr->iDocDefIMInstanceArray[utGetScriptIndex(TSMVarsPtr->tsmCurrentSLRec.fScript)]);
if (openedDefIMRecordPtr->openedDefIMCInstance) { /* already open */
#ifdef BuildTSMInit
if (!openedDefIMRecordPtr->openedDefIMActiveP) /* if the input method is NOT active */
Debugger(); /* let me know */
#endif
(void) BeginSystemMode(); /* input method is treated as system service <#18> */
TSMVarsPtr->tsmPatchFlags &= ~kIMJustSetCursor; /* clear the flag so that IM can change the cursor again <#50> */
cmResult = SetTextServiceCursor(openedDefIMRecordPtr->openedDefIMCInstance, mousePos);
/* if cmResult then SetCursor becomes a Noop until Flag is cleared by a 0 cmResult*/
cmResult ? TSMVarsPtr->tsmPatchFlags |= kIMJustSetCursor : TSMVarsPtr->tsmPatchFlags &= ~kIMJustSetCursor;
(void) EndSystemMode(); /* close the window really quick <#18> */
if (cmResult != 0) /* input method handled the call */
return true; /* so return true */
}
}
HLock((Handle) idocID); /* lock the handle */
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
aListRecPtr = idocRecPtr->iDocCIHead;
for (i=0; i<idocRecPtr->iDocOpenTextServiceN; i++) { /* call each open text service */
if (aListRecPtr) { /* to be safe */
cmResult = SetTextServiceCursor(aListRecPtr->ciTS, mousePos);
if (cmResult) /* text service handled the call */
break; /* so break now ... */
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
else break;
}
HUnlock((Handle) idocID);
if (cmResult) return true;
else return false;
};
/* ****************************************************************** *
* Function: xGetServiceList *
* Get all text services. *
* Input: numOfInterface *
* supportedInterfaceTypes *
* Output: false if text service doesn't set the cursor, *
* true if text service set the cursor. *
*
* Side effect:If serviceInfo is nil, we will allocate the memory. *
* ****************************************************************** */
pascal OSErr xGetServiceList(short numOfInterface, OSType supportedInterfaceTypes[], TextServiceListHandle *serviceInfo, long *seedValue)
{
ComponentDescription cd, dummyCD;
Component aComp;
short pos, index;
long count;
OSErr err;
Handle componentName = NewHandle(kComponentNameLength);
if (componentName == nil) /* no memory */
return memFullErr;
if (numOfInterface == 0) /* can't be 0 */
return paramErr;
if (*serviceInfo == nil) { /* allocate the handle */
*serviceInfo = (TextServiceListHandle) NewHandle(sizeof(TextServiceList));
if (*serviceInfo == nil) /* no memory */
return memFullErr;
}
(**serviceInfo)->fTextServiceCount = 0;
for (pos=0; pos<numOfInterface; pos++) {
cd.componentType = supportedInterfaceTypes[pos];
cd.componentSubType =
cd.componentManufacturer =
cd.componentFlags =
cd.componentFlagsMask = 0; /* don't care */
count = CountComponents(&cd);
if (count > 0) { /* have TS component registered */
aComp = FindNextComponent(nil, &cd); /* find the first component */
if (aComp == nil) return tsmTextServiceNotFoundErr;
index = 0;
while (aComp) {
if (count == 0) {
#ifdef BuildTSMInit
Debugger(); /* debug for now */
#endif
break;
}
err = GetComponentInfo(aComp, &dummyCD, componentName, nil, nil);
if (err == noErr) { /* got the info */
/* check if the serviceInfo handle is big enough to receive this info */
err = utMakeRoom4SInfoHandle(*serviceInfo, index);
if (err != noErr)
return err;
(**serviceInfo)->fServices[index].fComponent = aComp; /* save the component */
BlockMove(*componentName, (**serviceInfo)->fServices[index].fItemName, kComponentNameLength);
(**serviceInfo)->fTextServiceCount++;
index++;
}
count--;
aComp = FindNextComponent(aComp, &cd);
} /* end while */
} /* end if */
} /* end for */
*seedValue = GetComponentListModSeed();
return noErr;
};
/* ********************************************************************** *
* Function: xOpenTextService *
* User chooses to open a text service, add this text *
* service to the TSM document record. *
* Note: IM is not opened thru this code ! (we use utOpenIM4Document) *
* Input: idocID *
* aComp = the component to open. *
* aCompInstance = a pointer to a Component Instance. *
* Output: noErr if no error, *
* *
* Side effect:A CIListRec record is created if the TS is opened the *
* first time and is save in a linked list in idocID. *
* "InitiateTextService" is always called. *
* Issues: If the TS is already open, we return noErr??? *
* Does the document has to be active when this call is made? *
* *
* Modification History: *
* 04Jan92 KSCT We now return the component instance to the caller, *
* so that it can be closed later. *
* ********************************************************************** */
pascal OSErr xOpenTextService(TSMDocumentID idocID, Component aComp, ComponentInstance *aCompInstance)
{ ComponentResult cmResult = 0;
CIListRec *aListRecPtr;
TSMDocRecord *idocRecPtr;
Boolean alreadyOpenedP = false;
OSErr err;
short i;
ComponentInstance ts = nil;
MenuHandle serviceMenuHandle = nil;
ComponentDescription cdRec;
*aCompInstance = nil; /* return nil if failed -- <04Jan92 #7> */
err = utVerifyTSMDocID(idocID);
if (err != noErr) /* invalid id */
return err;
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
aListRecPtr = idocRecPtr->iDocCIHead; /* get the first open TS */
for (i=0; i<idocRecPtr->iDocOpenTextServiceN; i++) { /* search each open text service */
if (aListRecPtr) { /* just to be safe */
if (aListRecPtr->ciTheComponent == aComp) { /* already opened (according to Peter H. I can test for component equality */
alreadyOpenedP = true; /* set flag to true */
ts = aListRecPtr->ciTS; /* keep its ComponentInstance */
break; /* so break now ... */
}
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
} /* end for */
if (!alreadyOpenedP) { /* need to open the first time */
err = GetComponentInfo(aComp, &cdRec, nil, nil, nil);
if (err) /* if can't get info, stop all the nonsense right here */
return err;
ts = OpenComponent(aComp);
if (ts == nil) /* can't open it */
return tsmCantOpenComponentErr; /* Component Manager never tells us what's wrong -- too bad */
/* Component is open now ... */
aListRecPtr = (CIListRec *) NewPtr(sizeof(CIListRec));
if (aListRecPtr == nil) {
CloseComponent(ts);
return memFullErr; /* sorry */
}
/* fill in the fields of the record */
aListRecPtr->ciTheComponent = aComp; /* save the component */
aListRecPtr->ciTS = ts; /* save the instance */
aListRecPtr->ciSLRecord.fScript = mGetScriptCode(cdRec);
aListRecPtr->ciSLRecord.fLanguage = mGetLanguageCode(cdRec);
aListRecPtr->ciTakeActiveInputP = (cdRec.componentFlags & bTakeActiveEvent) ? true : false;
aListRecPtr->ciMenuVisibleP = false;
aListRecPtr->ciServiceMenu = nil;
cmResult = GetTextServiceMenu( ts, &serviceMenuHandle);
if (cmResult == tsmComponentNoErr) {
aListRecPtr->ciServiceMenu = serviceMenuHandle;
if ( serviceMenuHandle!=nil ) {
InsertMenu(serviceMenuHandle, 0);
DrawMenuBar();
aListRecPtr->ciMenuVisibleP = true;
}
}
/* the call will not move memory */
utAppendTS2Document((TSMDocRecordPtr) *((Handle) idocID), aListRecPtr); /* add to the list */
}
*aCompInstance = ts; /* return the instance -- <04Jan92 #7> */
cmResult = InitiateTextService(ts); /* inform Component to start servicing */
/* what to do if there is an error ???? */
cmResult = ActivateTextService(ts); /* and activate the service */
/* what to do if there is an error ???? */
if (alreadyOpenedP) /* here, cmResult should = noErr */
cmResult = tsmComponentAlreadyOpenErr; /* return a warning */
return ((OSErr) cmResult); /* return this as result */
};
/* ************************************************************************** *
* Function: xCloseTextService *
* User chooses to close a text service, remove this text *
* service from the TSM document record, and release its memory. *
* Input: idocID *
* aCompInstance = the component instance to close. *
* Output: noErr if no error, *
*
* Side effect:The CIListRec record is removed from the linked list in idocID, *
* the open text services counter is decremented by one, *
* and if the text service has a menu, the menu is released and *
* removed from the menu bar. *
* Modification History: *
* 04Jan92 KSCT New today. *
* ************************************************************************** */
pascal OSErr xCloseTextService(TSMDocumentID idocID, ComponentInstance aCompInstance)
{ ComponentResult cmResult = 0;
CIListRec *aListRecPtr=nil, *prevRecPtr=nil;
TSMDocRecord *idocRecPtr;
OSErr err;
err = utVerifyTSMDocID(idocID);
if (err != noErr) /* invalid id */
return err;
HLock((Handle) idocID);
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
if (idocRecPtr->iDocOpenTextServiceN) { /* if we have open text services */
aListRecPtr = idocRecPtr->iDocCIHead; /* get the first open TS record in the linked list */
while (aListRecPtr) { /* find the match */
if (aListRecPtr->ciTS == aCompInstance) { /* found, remove it from the list */
if (aListRecPtr == idocRecPtr->iDocCIHead) /* this is the first one in the list */
idocRecPtr->iDocCIHead = aListRecPtr->ciNextCIListRec;
else
prevRecPtr->ciNextCIListRec = aListRecPtr->ciNextCIListRec;
if (aListRecPtr->ciServiceMenu && /* if the text service has a menu */
(*(aListRecPtr->ciServiceMenu))->menuID != -kTSMSystemMenuID) { /* and this is not a system menu */
if (aListRecPtr->ciMenuVisibleP) { /* if it is visible, then remove it */
utDeleteMenu((*(aListRecPtr->ciServiceMenu))->menuID); /* call our delete menu routine */
aListRecPtr->ciMenuVisibleP = false; /* in case some stupid guy open the input method as a text service */
}
DisposeHandle((Handle) aListRecPtr->ciServiceMenu); /* release the menu */
aListRecPtr->ciServiceMenu = nil; /* no menu now */
}
idocRecPtr->iDocOpenTextServiceN--; /* decrement the counter of open instances */
DisposePtr((Ptr) aListRecPtr); /* release its memory */
HUnlock((Handle) idocID);
return CloseComponent(aCompInstance); /* close the component and return */
}
prevRecPtr = aListRecPtr;
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
}
/* no text service to close, or the instance is not found */
HUnlock((Handle) idocID);
return tsmNoOpenTSErr;
}
/* used only by "xSendAEtoClient" */
OSErr utMapInstance2Refcon(ComponentInstance aCompInstance,
DocumentTable *docTablePtr,
long *refcon)
{
short i, total;
TSMDocumentID idocID;
TSMDocRecord *idocRecPtr;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
CIListRec *aListRecPtr;
DefIMInstanceRecord *openedDefIMRecordPtr;
if (TSMVarsPtr->tsmCacheInstance == aCompInstance) { /* in the cache */
*refcon = TSMVarsPtr->tsmCacheRefcon; /* don't need to search */
return noErr;
}
total = docTablePtr->docTableEntryN; /* number of IDs recorded */
for (i=0; i<total; i++) {
idocID = docTablePtr->docIDStart[i];
if (idocID == nil) continue; /* empty slot doesn't count */
idocRecPtr = (TSMDocRecord *) *((Handle) idocID); /* dereference the handle */
#ifdef TSMDebug
if (idocRecPtr->iDocSignature != kTSMDocumentSignature) {
DebugStr("\pTSM: Map refcon failed");
break;
}
#endif
/* first check if this instance is an input method */
openedDefIMRecordPtr = &(idocRecPtr->iDocDefIMInstanceArray[utGetScriptIndex(TSMVarsPtr->tsmCurrentSLRec.fScript)]);
if (openedDefIMRecordPtr->openedDefIMCInstance == aCompInstance) {
*refcon = idocRecPtr->iDocRefcon; /* return refcon */
TSMVarsPtr->tsmCacheInstance = aCompInstance; /* save the mapping in the cache */
TSMVarsPtr->tsmCacheRefcon = *refcon;
return noErr;
}
/* if this instance is not an input method, check other text services ... */
if (idocRecPtr->iDocOpenTextServiceN) { /* if we have open text services */
aListRecPtr = idocRecPtr->iDocCIHead; /* get the first open TS record in the linked list */
while (aListRecPtr) { /* find the match */
if (aListRecPtr->ciTS == aCompInstance) { /* found */
*refcon = idocRecPtr->iDocRefcon; /* return refcon */
TSMVarsPtr->tsmCacheInstance = aCompInstance; /* save the mapping in the cache */
TSMVarsPtr->tsmCacheRefcon = *refcon;
return noErr;
}
aListRecPtr = aListRecPtr->ciNextCIListRec;
}
}
} /* end for */
return tsmInputMethodNotFoundErr; /* return any non-zero error */
}
/* ****************************************************************** *
* Function: xSendAEtoClient *
* Sending callback AppleEvents to the application. *
* Input: theAppleEvent, ...... *
* Output: ????. *
* *
* Side effect:If the target app is not TSM aware, send the AE to SWM *
* background application. *
* Modification history: *
* 06Mar92 KST Map the instance to document ID and return the refcon *
* in each event record. *
* 06Mar92 KST If the doc is using input window, events were routed *
* to SWM. But if the event was sent from TS, then the *
* event should be routed to the Application. *
* ****************************************************************** */
pascal OSErr xSendAEtoClient(AppleEvent *theAppleEvent,
AppleEvent *reply,
AESendMode sendMode,
AESendPriority sendPriority,
long timeOutInTicks,
IdleProcPtr idleProc,
EventFilterProcPtr filterProc )
{
OSErr err;
ProcessSerialNumber PSNRecord;
TSMVarsRec *TSMVarsPtr = GetExpandMemTSMGlobals();
AEAddressDesc addrDescriptor;
ComponentInstance aComponentInstance = nil;
ComponentDescription cdRec;
DescType returnedType;
long tempLong;
DocumentTable *docTablePtr;
Boolean eventFromTextServiceP = false; /* true = AE sent from IM, false = sent from TS */
#pragma unused (addrDescriptor)
err = GetCurrentProcess(&PSNRecord);
if (err) return tsmUnknownErr; /* what is this ? */
err = AEGetParamPtr(theAppleEvent, keyAEServerInstance, typeComponentInstance, &returnedType,
(Ptr) &aComponentInstance, sizeof(ComponentInstance), &tempLong); /* get ComponentInstance */
if (err == noErr) { /* we got the Component instance */
err = GetComponentInfo( (Component) aComponentInstance, &cdRec, nil, nil, nil);
if (err == noErr) { /* got the info */
eventFromTextServiceP = cdRec.componentSubType == kInputMethodService ? false : true;
}
}
if (eventFromTextServiceP || /* if the AE is sent from Text Service, then send it the application */
(utaGetTSMAwareDocTable(&PSNRecord, &docTablePtr) == noErr) ) { /* or TSM aware app and is not using input window */
if (aComponentInstance) { /* we got the Component instance */
err = utMapInstance2Refcon(aComponentInstance, docTablePtr, &tempLong);
if (err == noErr) { /* mapped the instance into refcon */
err = AEPutParamPtr(theAppleEvent, keyAETSMDocumentRefcon, typeLongInteger, (Ptr) &tempLong, 4);
#ifdef TSMDebug
if (err)
DebugStr ( "\pPut refcon failed" );
#endif
}
} /* endif we have the instance */
return AESend(theAppleEvent, reply, sendMode, sendPriority, timeOutInTicks, idleProc, filterProc );
}
else { /* send this AE to SWM, SWM doesn't need refcon 'cause it has only one document */
return TSMVarsPtr->tsmSendAEProc( TSMVarsPtr->tsmSWMDrvrRefNum, theAppleEvent, reply );
}
};