mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-26 01:49:19 +00:00
1643 lines
72 KiB
C
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 );
|
|
}
|
|
};
|
|
|
|
|