/*
	File:		TSMUtility.c

	Contains:	Text Services Manager Utility routines.

	Written by:	Kenny SC. Tung & Kurita san.

	Copyright:	© 1991-1992 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<60>	10/30/92	DTY		Use Get/Set macros to access ExpandMem.
		<59>	  8/4/92	KST		#1038311 <gbm>: <HA>: Undo the previous fix and put the fix in
									ScriptMgr:ScriptMgrKeyGetSet.a
		<58>	  8/1/92	KST		#1038311 <JH>: Documentation change.
		<57>	  8/1/92	KST		#1038311 <JH>: Can't call FixTSMDocument() to the app. in the
									SetDefaultInputMethod() either.
		<56>	 7/31/92	KST		#1038311 <JH>: In xCloseServiceWindow(), make sure the window
									that we are going to close is not nil and it is truely a
									floating window.
		<55>	 7/27/92	KST		#1037539 <JH>: Return tsmScriptHasNoIMErr instead of
									tsmUnknownErr in SetDefaultInputMethod() and
									GetDefaultInputMethod() to match the ERS.
		<54>	  7/8/92	KST		#1033475 <JH>: In SetTextServiceLanguage(), when we activate an
									input method, make sure we are not using old JIM.
		<53>	  7/6/92	KST		#1033507,1033505 <JH>: Don't change the tsmScript/Language if
									utOpenIM4Document() failed.
		<52>	 6/30/92	KST		#1030903,1033475 <smb>: We had an agreement with the keyboard
									menu code that TSM will call SetScript(false) in
									SetDefaultInputMethod().
		<51>	 6/30/92	KST		#1033456 <JH>: If utJustDeactivateIM4Document() returns
									DocNotActiveErr, then ignore the error.
		<50>	 6/26/92	KST		#1033450,1033456,1031316 <JH>: utCloseIM4Document() will now
									take an extra parameter to indicate if it should send
									HidePallete() command to input method. Only set it to true when
									called in SetDefaultInputMethod().
		<49>	 6/22/92	KST		#1030843 <JH>: When there is no current active document, and
									switch from old JIM to TSM-styled input method, need to reset
									UseOldJIM flag.
		<48>	 6/17/92	KST		#1030843 <JH>: For the previous fix, in xUseInputWindow, there
									is a possibility that the current active document could be nil.
									Need to check that.
		<47>	 6/17/92	KST		#1030843 <JH>: Changed the function name from
									utaIsApplicationTSMAware to utaIsAppTSMAwareAndNotUsingIW to be
									more precise and descriptive.
		<46>	 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.
		<45>	 6/13/92	KST		#1031142,1030881 <JH>: When user turns UseInputWindow off and
									the current active document is SWM's, then need to call
									FixTSMDocument before deactivating that document.
		<44>	 6/10/92	YK		#1031298 <JH>: Make sure that the default input method exists.
		<43>	 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. This involved many
									many changes.
		<42>	 6/10/92	KST		#1031634, #1031924 <JH>: Fixed the bug -- "Balloon Help is broken
									causing the system to crash as menus are pulled." in FindServiceWindow.
		<41>	  6/2/92	JH		#1028635,1030481,1030631,1030634,1030647 <KST>: Adding a check
									in FindServiceWindow for a nil menulist handle in low memory.
									FindServiceWindow gets called a lot now by input methods at odd
									times, and if the menulist low-mem has nil in it (yes, it can)
									the normal FindWindow call crashes trying to jump to an MDEF
									proc referenced off of nil. So we check here. If it is nil we
									just return indesk.
		<40>	 5/20/92	KST		Ooops, I added a comment which had syntax error.
		<39>	 5/20/92	KST		#1030422 <JH>: Fixed a bug in SetDefaultInputMethod.
		<38>	 5/19/92	KST		#1029247 <hsK>: GetDefaultInputMethod, SetDefaultInputMethod,
									and SetTextServiceLanguage routines should check if user is
									passing the correct language code corresponding to the script
									code.
		<37>	 5/13/92	JH		<KST>: Fixing NewServiceWindow so that it marks floating windows
									with the proper windowkind.
		<36>	  5/8/92	KST		When we switch from non-CJK script to TSM-styled IM, we must
									clear the ScriptMgr's secret "UseOldJapaneseIM" flag.
		<35>	  5/6/92	KST		#1028642,<JH>: Fixed the bug: "floating pallete stays when
									switch from Akiko to Roman in TSM aware application".
		<34>	  5/1/92	KST		#1028301,<JH>: SetTSMDialogState, RestoreTSMDialogState,
									TSMChangeCursor, TSMRestoreCursor calls are no longer needed and
									should be removed from the source code.
		<33>	  4/9/92	KST		JH,modifying xNewServiceWindow to mark a falg in refcon. Adding
									stuff to support SWM as a driver. We don't need
									xSetTSMDialogState and xRestoreTSMDialogState call anymore. Will
									remove them in the next release.
		<32>	 3/30/92	KST		JH, 1025008, 1025009. First attempt to solve the pallete/menu
									bar flickering problem. Also, we synchronize the keyboard menu
									with the script of the TSM document.
		<31>	 3/23/92	KST		Code changed according to the comments from the code review.
		<30>	 3/18/92	KST		In "xSetTextServiceLanguage", if user switches key script, then
									I'll call FixTSMDocument.
		<29>	 3/12/92	KST		Make sure handles are locked properly.
		<28>	  3/6/92	KST		In "xSetTextServiceLanguage", when user switches input method,
									if there is currently an active document which belongs to SWM,
									then send an AE to ask her to close the input window.
		<27>	  3/2/92	KST		In "utOpenActivateDefaultIM" and "utOpenIM4Document", it now
									takes a Boolean flag to indicate if we should activate the input
									method.
		<26>	 2/28/92	KST		Added 2 new calls "SetTSMDialogState", and
									"RestoreTSMDialogState".
		<25>	 2/11/92	DCL		Changed the name of TSMEqu.[aph] to TextServices.[aph] for
									better read-ability.
		<24>	 2/10/92	KST		Moved all internal routines to TSMInternalUtil.c file.
		<23>	 2/10/92	KST		Change the default input method resource format, we don't store
									script code now. Script is implied by array index. Instead, we
									store the old IM FEP ID in the resource record.
		<22>	  2/3/92	KST		If we switch from old to new input method, need to check if we
									are coming from old JIM and need to inform Process Manager.
		<21>	  2/1/92	KST		If there is no active document, we still need to inform PM
									whether to use SWM or not.
		<20>	  2/1/92	KST		We don't have a global flag in TSM globals to indicate using
									bottom-line window.
		<19>	 1/31/92	YK		with KST: Added xFindServiceWindow.
		<18>	 1/31/92	KST		Update the input method table in the TSM global in
									"xGetDefaultInputMethod".
		<17>	 1/31/92	KST		Activate SWM only if we are not using old Japanese Input Method.
		<16>	 1/29/92	KST		Input method synchronized with the script/language only when the
									correct environment has been set up in SetTextServiceLanguage.
		<15>	 1/16/92	KST		When we search for a default input method, need to set the mask
									field to search for the correct script.
		<14>	 1/15/92	KST		GetDefaultInputMethod should not open the input method.
		<13>	 1/13/92	YK		Put comments. Call GetToolTrapAddress instead of GetTrapAddress.
		<12>	 1/13/92	KST		Activate SWM only if we can open the default input method.
		<11>	 1/11/92	KST		Call Process Manager's "RouteEventToSWM" routine only if 
									P.M. has been loaded.
		<10>	 1/11/92	KST		Adding code to support SWM Background application.
		 <9>	 1/10/92	KST		Call "LoadResource" if the resource handle points to nil.
		 <8>	  1/9/92	KST		In "UseInputWindow", If idocID ==  0, it affects only one (¥¥
									current ¥¥) application, not the whole system.
		 <7>	  1/8/92	KST		In xGetDefaultInputMethod, if the default input method is not
									loaded in the system, another input method is opened by query
									Component Manager.
		 <6>	12/31/91	KST		We now save default input method and "UseInputWindow" flag in
									resource file across reboot. A new scheme is adopted to allow a
									single document to use input window.
		 <5>	12/20/91	KST		Making progress toward Alpha. Changed "inline" to "TSM".
		 <4>	12/10/91	KST		Adding new code for the D5 build.
		 <3>	11/26/91	YK		Added code for the floating window.
		 <2>	11/23/91	KST		Check in the first time for Cube-E.

*/



#include	<Types.h>
#include	<Memory.h>
#include	<Windows.h>
#include	<Layers.h>
#include	<Script.h>
#include	<Resources.h>
#include	<Traps.h>

#include	<MFPrivate.h>
#include	<TextServices.h>				/*25*/
#include	<TSMPrivate.h>


/*	********************************************************************************
 *	File:	"TSMUtility.c"
 *
 *	External routines:
 *		xSetDefaultInputMethod
 *		xGetDefaultInputMethod
 *		xSetTextServiceLanguage
 *		xGetTextServiceLanguage
 *		xUseInputWindow
 *		xNewServiceWindow
 *		xCloseServiceWindow
 *		xGetFrontServiceWindow
 *		xFindServiceWindow
 *		
 *	******************************************************************************** */

/*
Note: ¥¥	The "TSMVarsPtr->tsmUseOldJInputMethodP" flag is meaningful only when the current
	  ¥¥	script is Japanese. This means only test/change the flag in Japanese script !!
	  
	  ¥¥	Thru out this and other files, I used 'Akiko' to refer to a TSM styled new Input Method.
*/

/*
 *	returns true if the old Input Method whose ID is fepID is installed.  (implememted in TSMDispatch.a)
 */
extern pascal Boolean OldJapaneseInputMethodExists( short fepID);




/*	***************************************************************************	*
 *	Function:	xSetDefaultInputMethod											*
 *				Set the default Input Method (saved across reboot).				*
 *				Called when user changes input method from the keyboard menu	*
 *				or by application.												*
 *				Assume we'll not be called if user switches to old Japanese IM.	*
 *																				*
 *	Input:		Pointer to an Input Method component,							*
 *				Pointer to a ScriptLanguageRecord buffer.						*
 *	Output:		noErr = if successful,											*
 *				tsmUnsupScriptLanguageErr = if no IM supports that script.		*
 *	Side effect:The (new) Default Input Method is opened in here.				*
 *				And the change is write to disk (in system resource file).		*
 *	Note:		In this routine, we do not change tsmCurrentSLRec.fScript !		*
 *																				*
 *	Modification History:														*
 *	10Jan92	KST	When we switch from one default input method to another in the	*
 *				same script, the old one is closed and the new one is opened.	*
 *	23Jan92	KST	When we switch input method, we might switch the keyscript too.	*
 *	24Mar92	KST	Need to call utForceDeactivateIM4Document instead of			*
 *				utDeactivateIM4Document.										*
 *	26Mar92	KST	We now keep track of the script used by this document.			*
 *				If we switch script, then change the script information for		*
 *				the current active document.									*
 *	19May92	KST	We should check if user is passing the correct language code	*
 *				corresponding to the script code. If not, return paramErr.<#38>	*
 *	19May92	KST	We had an agreement with the keyboard menu code that TSM will	*
 *				call SetScript(kUseOldJapaneseIM,false).						*
 *	01Aug92	KST	When we switch from CIM1 to CIM2 to Akiko and back to CIM1, TSM	*
 *				still keeps the menuhandle of CIM2. Need to get the menu from	*
 *				CIM1 by just clearing the menuHandle in TSM globals. Will get	*
 *				the menu from CIM1 in utOpenIM4Document().						*
 *	***************************************************************************	*/
pascal OSErr xSetDefaultInputMethod ( Component aComp, ScriptLanguageRecord *slRecPtr )
{

	OSErr					err = noErr;
	Boolean					need2UpdateDefIMp = false;			/* false = do not need to update default I.M. setting */
	Boolean					need2UpdateScript = false;			/* false = do not need to update current key script */
	short					imIndex;
	ComponentDescription	cdRec;
	ScriptCode				newScript, currentScript;
	TSMVarsRec				*TSMVarsPtr = GetExpandMemTSMGlobals();
	//TSMDocRecord			*currentActiveDocPtr;
	MenuHandle				serviceMenuHandle = nil;
	TSMDocumentID			currentTSMDocument;
	Boolean					forceUpdateResource = false;


	/* First, verify if the script/language code are correct ...	<#38> */
	if (utBadSandL(slRecPtr))	return paramErr;				 /* <#38> */
		
	/* see if there is such an Input Method supports the script/language  */
	err = GetComponentInfo(aComp, &cdRec, nil, nil, nil);
	if (err)					return err;
	
	
	/* There are serveral cases to consider:					-- 23Jan92
		1. Default IM and current script are not changed. In this case, this is just a NOOP.
		2. Script is not changed, just IM is changed. I need to close the old IM, set the default and open the new one.
		   In this case, don't call "xSetTextServiceLanguage" because it does nothing when script is the same.
		3. Script has changed -- there are two possible cases for that script:
			a). I.M. is the same:	User just wants to switch from one I.M to the other, no default setting is affected.
									I need to deactivate the I.M for the current script, and then call "xSetTextServiceLanguage".
			b). I.M. is different:	User wants to switch to a different script and also changes the default I.M.
									I need to close the old I.M. for that script, set the default,
									and then call "xSetTextServiceLanguage".
		4. And of course for each case, we need to consider if the Japanese is using old input method.
	*/


	
	newScript = slRecPtr->fScript;								/* new setting */
	if (utScriptIsNotCCJK(newScript))							/* does the script needs IM? */
		return tsmScriptHasNoIMErr;								/* only CJK uses IM */

	currentScript = TSMVarsPtr->tsmCurrentSLRec.fScript;		/* current script */
	currentTSMDocument = TSMVarsPtr->tsmActiveDocID;			/* current active document */
	
	if (currentScript == smJapanese || currentScript == smTradChinese ||
		currentScript == smKorean   || currentScript == smSimpChinese) {	/* if currently in CCJK script */

		imIndex = utGetScriptIndex(currentScript);				/* index for the current script */
		/* First check if the current open input method is the same as the new one, */
		if (TSMVarsPtr->tsmDefIMInfoTable[imIndex].gComponent == aComp && currentScript == newScript) {	/* same component and same script */
			/* When we use old JIM, tsmDefIMInfoTable[smJapanese].gComponent could still points to Akiko's	*/
			/* So we need to special case for Japanese script to activate its IM, oh I hate this ....		*/
			/* For otherwise, this is just a NOOP */
			if (currentScript == smJapanese) {					/* Japanese could switch from old to new IM */
				if (TSMVarsPtr->tsmUseOldJInputMethodP) {		/* if we are switching   from old to new IM */
					(void) RouteEventToSWM(true);				/* start sending events to SWM */
					TSMVarsPtr->tsmUseOldJInputMethodP = false;	/* falg = use the new JIM now  */
					SetScript(smJapanese, kUseOldJapaneseIM, (long) false);		/* then disable the secret flag !!	<#52> */
					/* need to activate the current open input method ...		<27Mar92> */
					if (currentTSMDocument) {					/* if there is a current active document */
						err = utActivateIM4Document(currentTSMDocument, smJapanese);
						if (err == tsmTSNotOpenErr) {
							/* It works until we have a TSM aware application, if we create a document in Roman	*
							 * script and the japanese default IM is Akiko. The document will have no Component	*
							 * and no Instance, utActivateIM4Document will fail if we switch from Roman to SJip	*
							 * first and then switch to Akiko, for there is no Component to activate ......		*/
							
							err = utOpenIM4Document(currentTSMDocument, aComp, smJapanese, true);	/* open and active */
						}
					}
					err = utUpdateDefaultInputMethodResource(aComp, slRecPtr);	/* need to update the resource <08Feb92 #23> */
				}
			}
			return err;											/* and treat this as a noop */
		}

		/* else, script or IM changed */
		/* If script has not changed, then check if we are switching form old to new IM ...  */
		if (newScript == currentScript) {					/* script is the same ...  <#39> */
			/* First, to check if the current input method has been set up in the TSM globals ... */
			/* This happens when we switch from old JIM to new JIM for the first time, oh no, not again !! */
			/* ie., I mean if you boot with SJIP as the default input method. */
			/* or input method changed, ie., oldJIM -> newJIM1 -> oldJIM -> newJIM2.	<#49> */
			if (TSMVarsPtr->tsmDefIMInfoTable[imIndex].gComponent == nil || TSMVarsPtr->tsmDefIMInfoTable[imIndex].gComponent != aComp) {	/* <#49> */
				if (currentScript == smJapanese) {					/* Component changed */
					if (TSMVarsPtr->tsmUseOldJInputMethodP) {		/* if we are switching from old to new */
						(void) RouteEventToSWM(true);				/* start sending events to SWM */
						TSMVarsPtr->tsmUseOldJInputMethodP = false;	/* use the new JIM */
						SetScript(smJapanese, kUseOldJapaneseIM, (long) false);		/* then disable the secret flag !!	<#52> */
					}
				}
//				else {
//					/* this happens when system script is CJK with no coresponding IM at boot time */
//					DebugStr("\pSetDefaultIM: current tsmDefIMInfoTable is nil");
//				}
				TSMVarsPtr->tsmDefIMInfoTable[imIndex].gComponent  = aComp;		/* save this new comp */
				TSMVarsPtr->tsmDefIMInfoTable[imIndex].gMenuHandle = nil;		/* no menu for now .. */
			}
		}

	}

	if (currentScript != newScript) {									/* script is not the same, special check if we are switching to Japanese now ... */
		imIndex = utGetScriptIndex(newScript);							/* index for the new script */
		/* In the case of SJip -> CJK/Roman -> Akiko, the default IM resource is still saying we are using old JIM !!	*/
		if (newScript == smJapanese) {									/* for Japanese again !!!! */
			/* if this tsmUseOldJInputMethodP was set, then the resource file must be updated! */
			forceUpdateResource = TSMVarsPtr->tsmUseOldJInputMethodP;	/* <#36> */
			if (forceUpdateResource) {									/* if we were using the old JIM	<#39> */
				SetScript(smJapanese, kUseOldJapaneseIM, (long) false);	/* then disable the secret flag !!	<#52> */
				TSMVarsPtr->tsmUseOldJInputMethodP = false;				/* so reset the flag -- use the new JIM now <#39> */
			}
		}
	}



	/* If Script and input method Component are the same, we already returned.		*/
	/* So here, we must have switched to a new input method OR to a new script ...	*/
	if (currentScript == newScript) {								/* same script but different IM */
		/* For the current document: need to close the old IM. and open a new one */
		/* And need to set and save the default I.M. in system resource file. */
		if (currentTSMDocument) {									/* if there is a current doc */
		//	currentActiveDocPtr = (TSMDocRecord *) *((Handle) currentTSMDocument);			/* dereference it	<01Aug92 #57> */
		//	if (currentActiveDocPtr->iDocPSNRecord == TSMVarsPtr->tsmSWMsPSNRecord) {		/* doc belongs to SWM		<#57> */
				/* Cannot send AE to application because A5 might not be correct, but we must fix text for SWM.	<01Aug92 #57> *
				 * To ask SWM fixing the text is OK because SWM does not have A5 and the posted text is sent to the		 #57> *
				 * application in the correct context -- our changes to the Process Manager is doing the right thing.	 #57> */
				(void) xFixTSMDocument(currentTSMDocument);			/* fix current input area before closing the input method */
		//	}
			utCloseIM4Document(currentTSMDocument, currentScript, true);	/* close the old one and hide its pallete */

#ifdef 0	// code should be deleted
			// apparently, Akiko has solved this problem, and this code can be removed now. */
			currentActiveDocPtr = (TSMDocRecord *) *((Handle) currentTSMDocument);			/* <27Mar92 #32> */
			if (currentActiveDocPtr->iDocPSNRecord == TSMVarsPtr->tsmSWMsPSNRecord) {		/* doc belongs to SWM	<#32> */
				/* this is another long story. The current application is apparently non-TSM aware,		*
				 * (doc belongs to SWM), if we open the input method now, input method such as Akiko	*
				 * will save the A5 of the current non-TSM aware application. When Akiko send AE to SWM	*
				 * it tries to restore the A5 -- which is not SWM's A5. So the system bumb!!	<#32>	*/
				(void) xDeactivateTSMDocument(currentTSMDocument);	/* tsmActiveDocID will be cleared ....  */
			}
			else
#endif

			err = utOpenIM4Document(currentTSMDocument, aComp, currentScript, true);	/* and open the new one		<#27> */
			if (err)		return err;								/* how to handle this error? */
		}															/* end if (currentTSMDocument) */
		TSMVarsPtr->tsmDefIMInfoTable[imIndex].gComponent = aComp;	/* save this new Component */
		need2UpdateDefIMp = true;									/* need to update default I.M. */
		need2UpdateScript = false;									/* script has not changed */
	}		/* end if same script */
	else {	/* else, different script -- must disable the IM for the current script.			*
			 * you may wonder where do I activate the new script's IM for the current document,	*
			 * it's done in xSetTextServiceLanguage. It will change iDocUseThisScript too.		*/
		if ( currentTSMDocument &&									/* if there is a current document */
			 utScriptIsCCJKAndNeedsIM( currentScript ) ) {			/* and the current script is CCJK	<#45> */
			/* cannot send AE to application because A5 might not be correct when this is called by KBMenu.		<01Aug92 #57> */
			err = xFixTSMDocument(currentTSMDocument);			/* fix the current input area		<#45> */
			utForceDeactivateIM4Document(currentTSMDocument, currentScript);	/* deactivate the IM for the current script */
		}
		need2UpdateScript = true;									/* script has changed, always update TSM's internal state */
		
		imIndex = utGetScriptIndex(newScript);						/* index for the new script */
		if (TSMVarsPtr->tsmDefIMInfoTable[imIndex].gComponent == aComp) {		/* same IM for the new script */
			need2UpdateDefIMp = false;								/* do not need to update default I.M. */
		}
		else {														/* new script and new IM */
			need2UpdateDefIMp = true;								/* need to update default I.M. */
			if (currentTSMDocument)									/* if there is a current doc */
				utCloseIM4Document(currentTSMDocument, newScript, true);		/* close the old IM and hide its pallete */
			TSMVarsPtr->tsmDefIMInfoTable[imIndex].gComponent  = aComp;			/* save this new comp */
			TSMVarsPtr->tsmDefIMInfoTable[imIndex].gMenuHandle = nil;			/* no menu for now .. */
		}
		
		if (forceUpdateResource)									/* SJip => Roman => Akiko		<#36> */
			need2UpdateDefIMp = true;								/* need to update default I.M.	<#36> */
	}



	/* Save the resource in System file, as defined in TSMPrivate.H */

	if (need2UpdateDefIMp) {									/* need to update the resource first */
		err = utUpdateDefaultInputMethodResource(aComp, slRecPtr);
		if (err)		return err;
	}

	if (need2UpdateScript)										/* this will open or activate the default IM */
		err = xSetTextServiceLanguage(slRecPtr);				/* and it will change iDocUseThisScript too. */

	return err;
}	/* end xSetDefaultInputMethod */



/*	**********************************************************************	*
 *	Function:	xGetDefaultInputMethod										*
 *				Get the default Input Method saved by TSM which supports	*
 *				the script/language specified in slRecPtr.					*
 *	Input:		Pointer to a ScriptLanguageRecord buffer.					*
 *	Output:		Return noErr and the default component in "ts" if found.	*
 *	Side effect:If the default Input method has never been set, this call	*
 *				will ask the CM for a default and save it with TSM Mgr.		*
 *	¥¥ Note:																*
 *			Even we are using old JIM, this call still returns the new		*
 *			default	Japanese Input Method Component saved in TSM.			*
 *																			*
 *	Modification History:													*
 *	19May92	KST	We should check if user is passing the correct language		*
 *				code corresponding to the script code. If not, returns		*
 *				paramErr. 										<#38>		*
 *	**********************************************************************	*/
pascal OSErr xGetDefaultInputMethod ( Component *aComp, ScriptLanguageRecord *slRecPtr )
{
	OSErr					err;
	short					index;
	ComponentDescription	cdRec;
	TSMVarsRec				*TSMVarsPtr = GetExpandMemTSMGlobals();
	ScriptIDRecordPtr		scriptIDRecPtr;


	/* First, verify if the script/language code are correct ...	<#38> */
	if (utBadSandL(slRecPtr))	return paramErr;				/*  <#38> */
	
	if (utScriptIsNotCCJK(slRecPtr->fScript))					/* does the script have an IM ? */
		return tsmScriptHasNoIMErr;								/* return if no (don't check old JIM) */

	err = utGetTSMDefaultInputMethod(&cdRec, slRecPtr);			/* search from resource */
	if (err == noErr) {											/* we found the default saved by TSM */
		/* but verify if the component is currently registered in the system */
		cdRec.componentFlagsMask = bScriptLanguageMask;			/* verify the script/language code		<16Jan92 #15> */
		*aComp = FindNextComponent(nil, &cdRec);				/* can we find the component? */
		if (*aComp) {											/* if we find the Component */
			index = utGetScriptIndex(slRecPtr->fScript);		/* set global		-- <31Jan92 #18> */
			TSMVarsPtr->tsmDefIMInfoTable[index].gComponent = *aComp;		/* update the in memory copy */
			return noErr;										/* yes, cool */
		}
	}
	else if ( (err == tsmInputMethodIsOldErr)
				&& OldJapaneseInputMethodExists((short) cdRec.componentSubType) ) {		/* default is the old IM */
			scriptIDRecPtr = (ScriptIDRecordPtr) aComp;							/* cast it into special record	<06Feb92 #23> */
			scriptIDRecPtr->sidScript	= (ScriptCode) cdRec.componentType;		/* return script				<06Feb92 #23> */
			scriptIDRecPtr->sidFEPID	= (short)	   cdRec.componentSubType;	/* 		and FEP ID				<06Feb92 #23> */
			TSMVarsPtr->tsmUseOldJInputMethodP = true;							/* set the flag	 				<06Feb92 #23> */
			return tsmInputMethodIsOldErr;										/* return special error			<06Feb92 #23> */
		 }

	/* default has not been set, or it was set but can not be found now, ask CM for a default */
	cdRec.componentType			= kTextService;
	cdRec.componentSubType		= kInputMethodService;
	cdRec.componentManufacturer	= 0;							/* don't care */
	
	cdRec.componentFlags		= utSLRec2CompFlag(slRecPtr);
	cdRec.componentFlagsMask	= bScriptLanguageMask;
	
	*aComp = FindNextComponent(nil, &cdRec);					/* find the default component */
	if (*aComp == nil)			return tsmInputMethodNotFoundErr;

	/* use this component as the default, but before return, set our default */
	return utUpdateDefaultInputMethodResource (*aComp, slRecPtr);

}


/*	******************************************************************************	*
 *	Function:	xSetTextServiceLanguage												*
 *				Set the current text service script/language.						*
 *				Called by ScriptMgr:ScriptMgrKeyGetSet and ScriptMgrSysMenuPatch.a	*
 *	Input:		Pointer to a ScriptLanguageRecord buffer.							*
 *	Output:		tsmUnsupScriptLanguageErr if S/L is not supported by the system.	*
 *				noErr = OK															*
 *	Side effect:1. Input method synchronized with the script/language, if and		*
 *				   only if the script has changed.									*
 *				2. If the script has changed and there is a tsmPreviousDocument,	*
 *				   then the previous document will be cleared. (take down pallete)	*
 *	¥¥ Note ¥¥	If script/language is not changed, this call is a NOOP.				*
 *																					*
 *	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 is using a non-TSM aware application and		*
 *				switches keyscript/IM.										<28>	*
 *	26Mar92	KST	We now keep track of the script used by this document. If we switch	*
 *				script and there is a current active document, then change the		*
 *				script information saved for the current active document.			*
 *	05May92	KST	When we switch to non-CJK script system, need to check if there is	*
 *				previous document needs to be cleaned up. For now floating pallete	*
 *				stays when switch from Akiko to Roman in TSM aware application".<35>*
 *	19May92	KST	We should check if user is passing the correct language code		*
 *				corresponding to the script code. If not, returns paramErr. <#38>	*
 *	******************************************************************************	*/
pascal OSErr xSetTextServiceLanguage( ScriptLanguageRecord *slRecPtr )
{
	TSMVarsRec			*TSMVarsPtr = GetExpandMemTSMGlobals();
	ScriptCode			oldScript, newScript;
	LangCode			oldLanguage;
	OSErr				err = noErr;							/* make sure it's initialized */
	ProcessSerialNumber	PSNRecord;
	TSMDocRecord		*currentActiveDocPtr;



	/* First, verify if the script/language code are correct ...	<#38> */
	if (utBadSandL(slRecPtr))	return paramErr;				/*  <#38> */


	newScript = slRecPtr->fScript;								/* to generate better code on MPW C */
	oldScript = TSMVarsPtr->tsmCurrentSLRec.fScript;
	oldLanguage = TSMVarsPtr->tsmCurrentSLRec.fLanguage;
	if (oldScript == newScript && oldLanguage == slRecPtr->fLanguage)
		return noErr;											/* nothing changed, return right now */


	/* Switching to a new script ... */

	if (utSupportedSL(slRecPtr)) {								/* check if the system supports this script */
	

		if (TSMVarsPtr->tsmActiveDocID) {						/* if currently has active document		<01Feb92 #21> */
			utFixDocumentAndCloseSWMWindow(oldScript);			/* fix text, deactivate IM, and deactivate SWM's document */
		}

		TSMVarsPtr->tsmCurrentSLRec.fScript		= newScript;
		TSMVarsPtr->tsmCurrentSLRec.fLanguage	= slRecPtr->fLanguage;

		/* I should not do this. Someone should call InformTSM ... */
		if (newScript == smJapanese) {							/* only check for Japanese script */
			if (GetScript(smJapanese, kUseOldJapaneseIM)) {		/* are we using the old script? */
				TSMVarsPtr->tsmUseOldJInputMethodP = true;		/* yes, use the old JIM */
			}
		}
		
		if (!TSMVarsPtr->tsmPMgrActiveP) {						/* if PM not active */
			if	( GetExpandMemProcessMgrExists() )				/* PM is active now */
				TSMVarsPtr->tsmPMgrActiveP = true;				/* this should only excute once */
			else												/* if PM is still not active, return now */
				return noErr;									/* we are still in the early booting process */
		}		/* now Process Manager has boot */


		/* If the new script uses imput method then synchronize the Input method with the script. */
		/* Open the default input method of the current script (for the current active document)		<23Jan92 #16> */
		/* If the system is using old Japanese input method, then don't activate SWM.					<12Jan92 #12> */

		if (TSMVarsPtr->tsmActiveDocID) {						/* if currently has active document		<01Feb92 #21> */
			/* this document must belongs to a TSM aware application, we need to open a new IM for it	<05Jun92 #43> */
			/* then open or activate the new default IM for the document.								<23Jan92 #16> */

			/* first, I need to change the script info saved for this document ...						<28Mar92 #32> */
			currentActiveDocPtr = (TSMDocRecord *) *((Handle) TSMVarsPtr->tsmActiveDocID);			/*  <28Mar92 #32> */
			currentActiveDocPtr->iDocUseThisScript	= newScript;	/* now change to use this script	<28Mar92 #32> */
			
			/* second, I need to deactivate the old input method of the previous script ...	*/
			(void) utForceDeactivateIM4Document(TSMVarsPtr->tsmActiveDocID, oldScript);
			
			/* third, I need to activate/open the new input method of the current script .. */
			/* if the current script is one of CCJK, we know we're not using the old JIM.	*/
			if (utScriptIsCCJKAndNeedsIM(newScript)) {			/* if this is one of CCJK script			<02Jul92 #53> */
				err = utOpenActivateDefaultIM(TSMVarsPtr->tsmActiveDocID, true);		 /* activate flag = true	<#53> */
				if (err) {										/* no memory, for example	<#53> */
					if (err != tsmInputMethodNotFoundErr) {		
					/*	If no IM, KBMenu will call us and change the script without checking	*/
					/*	for any error, so I can't restore any state info but return ...			*/
#ifdef BuildTSMInit
						DebugStr("\pCan't open the new IM");	/* restore and leave		<#53> */
#endif
						TSMVarsPtr->tsmCurrentSLRec.fScript		= oldScript;			/*	<#53> */
						TSMVarsPtr->tsmCurrentSLRec.fLanguage	= oldLanguage;			/*	<#53> */
						currentActiveDocPtr = (TSMDocRecord *) *((Handle) TSMVarsPtr->tsmActiveDocID);				/*  <#53> */
						currentActiveDocPtr->iDocUseThisScript	= oldScript;			/* restore to old script	<#53> */
						/* and now, activate the input method for the old script ...		<#53> */
						utActivateIM4Document(TSMVarsPtr->tsmActiveDocID, oldScript);	/*	<#53> */
					}
					return err;															/*	<#53> */
				}
			}
		}
		/* if there is no current active document, say we are in the Finder now, then the				<01Feb92 #21> */
		/* default IM will be opened when a document becomes active, or is been created.				<01Feb92 #21> */

		if (utScriptIsNotCCJK(newScript))						/* if this is not CCJK script			<01Feb92 #21> */
			(void) RouteEventToSWM(false);						/* do not send events to SWM */
		else {													/* else, this is one of CCJK */
			if (newScript == smJapanese) {						/* only check for Japanese script */
				if (GetScript(smJapanese, kUseOldJapaneseIM)) {	/* are we using the old script? */
					(void) RouteEventToSWM(false);				/* yes, don't send events to SWM */
					TSMVarsPtr->tsmUseOldJInputMethodP = true;	/* use the old JIM */
					return err;
				}
			}
			
			/* Current script is one of CJK, and requires TSM input method */
			/* then inform process Manager to start sending events to SWM  */
			
			(void) GetCurrentProcess(&PSNRecord);					/* get current process's PSN */
			if (utaIsAppTSMAwareAndNotUsingIW(&PSNRecord) == noErr)	/* if the current application is TSM aware and not using IW */
				(void) RouteEventToSWM(false);						/* then do not send events to SWM */
			else													/* if the current application is not TSM aware */
				(void) RouteEventToSWM(true);						/* then always sending events to SWM */
		}

		/* if we switch from a TSM doc using Chinese to Finder (Roman) */
		if (TSMVarsPtr->tsmPreviousDocID)						/* clean it up		-- <#35> */
				utCleanUpPreviousDoc(TSMVarsPtr->tsmPreviousDocID, newScript);

		return err;
	}
	return tsmUnsupScriptLanguageErr;
}


/*	***********************************************************	*
 *	Function:	xGetTextServiceLanguage							*
 *				Get the current text service script/language.	*
 *	Input:		Pointer to a ScriptLanguageRecord buffer.		*
 *	Output:		Always succeed.									*
 *				At boot time, Script Manager set this up.		*
 *	Side effect:none.											*
 *	***********************************************************	*/
pascal OSErr xGetTextServiceLanguage( ScriptLanguageRecord *slRecPtr )
{
	TSMVarsRec	*TSMVarsPtr = GetExpandMemTSMGlobals();

	slRecPtr->fScript	= TSMVarsPtr->tsmCurrentSLRec.fScript;
	slRecPtr->fLanguage = TSMVarsPtr->tsmCurrentSLRec.fLanguage;

	return noErr;
}


/*	***********************************************************************	*
 *	Function:	xUseInputWindow												*
 *																			*
 *	Input:		If idocID not nil, then affects only this document,			*
 *				else, affects global setting.								*
 *				useFloatWindow = Boolean flag.								*
 *	Output:		noErr if all OK,											*
 *				tsmInvalidDocIDErr = invalid id,							*
 *																			*
 *	Side effect:															*
 *			1. Process Manager is informed if the current app is using		*
 *			   input window or not.											*
 *			2. If useFloatWindow is false, then the default input method	*
 *			   will be opened/activated for the decument.					*
 *	07Jan91	Design changes:													*
 *	If idocID ==  0, it affects only one (¥¥ current ¥¥) application.		*
 *	If idocID == valid doc id, it applies to that document only.			*
 *	¥¥ SWM should never call this routine. Otherwise ... ¥¥					*
 *																			*
 * Modification history:													*
 *	24Mar92	KST	Need to call utSoftDeactivateIM4Document instead of			*
 *				utDeactivateIM4Document. UtSoftDeactivateIM4Document		*
 *				will not remove IM's pallete and menu.						*
 *	04Jun92	KST	We keep two active documents when using input window.		*
 *				Change the call from utSoftDeactivateIM4Document to			*
 *				utJustDeactivateIM4Document. This call will not change the	*
 *				state info in the TSM global.						<#43>	*
 *	**********************************************************************	*/
pascal OSErr xUseInputWindow( TSMDocumentID idocID, Boolean useFloatWindow )
{
	TSMVarsRec			*TSMVarsPtr = GetExpandMemTSMGlobals();
	TSMDocRecord		*idocRecPtr;
	OSErr				err = noErr;
	ProcessSerialNumber	PSNRecord;
	ScriptCode			currentScript;
	TSMDocumentID		currentTSMActiveDocID;
	Boolean				currentTSMDocIsSWM = false;
	Boolean				appsGlobalFlag, stillActiveP=false;


	currentScript			= TSMVarsPtr->tsmCurrentSLRec.fScript;
	currentTSMActiveDocID	= TSMVarsPtr->tsmActiveDocID;
	if ( currentTSMActiveDocID 		&&							/* it could be nil		<#48> */
		((TSMDocRecordPtr) *(Handle) currentTSMActiveDocID)->iDocPSNRecord == TSMVarsPtr->tsmSWMsPSNRecord )
			currentTSMDocIsSWM = true;							/* current doc belongs to SWM */


	if (idocID) {												/* toggle this document's status */
		if (idocID == -1)										/* global effect */
			return paramErr;									/* changed the design -- no system wide flag */

		// else, this document only ...
		err = utVerifyTSMDocID(idocID);							/* I assume this is the current active document */
		if (err != noErr)										/* invalid id */
			return err;

		idocRecPtr = (TSMDocRecord *) *((Handle) idocID);			/* dereference the handle */
		/* Is this document using input window now?? If not why bother calling me ?? */
		if ( idocRecPtr->iDocUseInputWindowP == useFloatWindow )	/* state never changed */
			return noErr;											/* get out of here ! */

		idocRecPtr->iDocUseInputWindowP = useFloatWindow;			/* local flag is set */
		utaUpdateDocFlagInPSNTable(idocID);							/* update the UseInputWindow flag in the PSN table */
																	/* and will inform Process Manager about this */

		/* application could call this routine with any document, might not be the current one !!! */
		if (useFloatWindow) {										/* this document is using input window now */

			if (TSMVarsPtr->tsmUseInputWindowDocID == idocID)		/* it is the current active document */
				// The current active document is not changed after this call but its current IM is deactivated ....
				stillActiveP = true;								/* we need to keep it active because it might have other text services open */
			else
				stillActiveP = false;								/* this is another doc */
			
			(void) utJustDeactivateIM4Document(idocID, currentScript, stillActiveP);	/* so deactivate the input method but keep active */
		}
		else {														/* this document is NOT using input window now */
			/* if the document/script needs an input method, then we open the default IM for this document */
			/* But first check if there is current active document which is SWM's doc. If it is then need to deactivate it.	*/
			/* The tsmActiveDocID got to be either SWM's document or this idocID. */
			if (currentTSMActiveDocID != nil		&&				/* if there is a current active document */
				TSMVarsPtr->tsmUseInputWindowDocID == idocID) {		/* and we're changing the state of it .. */					
				if ( currentTSMDocIsSWM ) {							/* doc belongs to SWM */
					(void) xFixTSMDocument( currentTSMActiveDocID );/* fix text if any		<#45> */
					err = utJustDeactivateIM4Document(currentTSMActiveDocID, currentScript, false);		/* shut it down */

					if (err == noErr) {
						utCloseSWMWindow(true);						/* ask SWM to close her input window and flush the buffer */
						((TSMDocRecord *) *((Handle) currentTSMActiveDocID))->iDocUseThisScript = kNoSuchScript;
					}
				}
				
				if (utDocNeedsInputMethod(currentScript, &idocRecPtr->iDocPSNRecord))
					(void) utActivateThisDocsInputMethod(idocID);	/* activate the input method */
	
				TSMVarsPtr->tsmActiveDocID = idocID;				/* this one will become the current active doc */
			}

#ifdef BuildTSMInit
			else	Debugger();										/* can't be nil ?? */

			// Debugging .......
			idocRecPtr = (TSMDocRecord *) *((Handle) idocID);		/* dereference the handle */
			if ( !idocRecPtr->iDocActiveP )		Debugger();			/* not active ?? */
#endif
		}
	}
	else {					/* If idocID ==  0, it affects the (current) application, all documents. */
		(void) GetCurrentProcess(&PSNRecord);							/* get current process's PSN */

		// If the global flag is the same, then this is a noop ....
		err = utaGetAppsUseInputWindowFlag( &PSNRecord, &appsGlobalFlag );
		if (err == tsmNeverRegisteredErr )		return err;				/* not TSM aware */

		if ( appsGlobalFlag == useFloatWindow )							/* state is the same */
			return noErr;												/* get out of here */

		(void) utaUpdateAppFlagInPSNTable(&PSNRecord, useFloatWindow);	/* affect the application */

		if ( currentTSMActiveDocID ) {									/* if there is a current active document, do something */

			/*	DocumentX		Global state	Current Doc		Action
			
				use IW			use IW			SWM's			none
				use IW			use IW			documentX		utJustDeactivateIM4Document
				not use IW		use IW			SWM's			can not happen (for a non-TSM aware application)
				not use IW		use IW			documentX		utJustDeactivateIM4Document
				
				use IW			not use IW		SWM's			none
				use IW			not use IW		documentX		none
				not use IW		not use IW		SWM's			utJustDeactivateIM4Document and ask SWM to take down its window (global)
				not use IW		not use IW		documentX		none
			*/

			if (useFloatWindow) {										/* ON: change this document to use input window now */
				stillActiveP = true;
				if ( !currentTSMDocIsSWM )								/* doc does not belong to SWM */
					err = utJustDeactivateIM4Document( currentTSMActiveDocID, currentScript, true );
					// If it is SWM's document, (the document is already using input window) then leave it alone ......
			}
			else {														/* OFF: this document is NOT using input window now */
				if ( currentTSMDocIsSWM ) {								/* doc belongs to SWM */
					idocRecPtr = ((TSMDocRecordPtr) *(Handle) TSMVarsPtr->tsmUseInputWindowDocID);		/* app's document */
					if ( !idocRecPtr->iDocUseInputWindowP ) {			/* the document is not using IW, then must be the global effect */
						(void) xFixTSMDocument( currentTSMActiveDocID );/* fix text if any		<#45> */
						err = utJustDeactivateIM4Document(currentTSMActiveDocID, currentScript, false);
						utCloseSWMWindow(true);							/* ask SWM to close her input window and flush the buffer */
						
						if (utDocNeedsInputMethod(currentScript, &idocRecPtr->iDocPSNRecord))
							err = utActivateThisDocsInputMethod(TSMVarsPtr->tsmUseInputWindowDocID);	/* activate the input method */
#ifdef BuildTSMInit
						if (err)		Debugger();
#endif
						TSMVarsPtr->tsmActiveDocID = TSMVarsPtr->tsmUseInputWindowDocID;
					}
				}
#ifdef BuildTSMInit
				else	Debugger();										/* where is SWM ? */
#endif
			}
		}
	}
	
	if ( stillActiveP )													/* always inform Process Manager to	*/
			(void) RouteEventToSWM(true);								/* start sending events to SWM ...	*/

	if (err == tsmDocNotActiveErr)										/* ignore this error */
			return noErr;
	else	return err;
}


/*	*******************************************************************	*
 *	Function:	xNewServiceWindow										*	
 *	Input:		lots of parameters to create a window					*
 *				and a ComponentInstance which owns this window.			*
 *	Output:		returns an OSErr and the WindowPtr.						*
 *																		*
 *	Side effect: Creates a floting layer by calling the Process 		*
 *				 Manager if the layer has never been created.			*
 *																		*
 *	¥¥ Note: If ts == kCurrentProcess, then we assume the window is		*
 *		 	 created for the application, else it's for Text Services,	*
 *			 and ts should be a Component Instance.						*
 *	*******************************************************************	*/
pascal OSErr xNewServiceWindow(	void				*wStorage,
								const Rect			*boundsRect,
								ConstStr255Param	title,
								Boolean				visible,
								short				theProcID,
								WindowPtr			behind,
								Boolean				goAwayFlag,
								ComponentInstance	ts,
								WindowPtr			*window )
{
	LayerPtr			floatLayer;
	LayerPtr			curLayer;
	WindowPeek			theWindow;
	OSErr				err;
	//ProcessSerialNumber	PSNRecord;
	THz					aZone;


	theWindow = nil;
	err = NewFloatLayer( &floatLayer);					/* Call the Process Manager		*/
	if ( err==noErr) {									/*  to create OR locate a floating layer	*/

		aZone = utSwapZone( SystemZone() );				// create the window in system zone
		// ¥¥ need to fix 4 the next release ......
		curLayer = SwapCurLayer( floatLayer);			/* New window in the floating layer		*/
		theWindow = (WindowPeek) NewWindow(	wStorage,
											boundsRect,
											title,
											visible,
											theProcID,
											behind,
											goAwayFlag,
											(long)ts	);
		if ( theWindow != nil ) {
		
			if ((long)ts == kCurrentProcess ) {
				theWindow->windowKind  = applicationFloatKind;
				theWindow->refCon = 0;
			}
			else {
				theWindow->windowKind  = systemFloatKind;
				theWindow->refCon = (long)ts;				/* window belongs to SWM driver */
			}

		} else {
			err = MemError();
		}

		SetCurLayer( curLayer);							/* Reset the current layer */
		(void)utSwapZone(aZone);
	}
	*window = (WindowPtr)theWindow;						/* Return this floating window */
	return err;
}


/*	***********************************************************	*
 *	Function:	xCloseServiceWindow								*
 *	Input:		the floating window to close.					*
 *	Output:		returns an OSErr.								*
 *																*
 *	Side effect: probably none.									*
 *																*
 *	Modification history:										*
 *	31Jul92	KST	Make sure the window that we are going to close	*
 *				is not nil and it is truely a floating window.	*
 *	***********************************************************	*/
pascal OSErr xCloseServiceWindow(WindowPtr window )
{
	LayerPtr	floatLayer;
	LayerPtr	curLayer;
	OSErr		err;

	// First make sure we are closing the right kind of window ......
	if (window													&&		/* window is not nil and ... */
		( ((WindowPeek) window)->windowKind == systemFloatKind	||		/* ....it is my type of baby */
		  ((WindowPeek) window)->windowKind == applicationFloatKind) ) {
		err = NewFloatLayer( &floatLayer);								/* Call the Process Manager		*/
		if ( err==noErr) {												/*  to get the floating layer	*/
	
			curLayer = SwapCurLayer( floatLayer);						/* Close the window			*/
			CloseWindow( window);										/*  in the floating layer	*/
			SetCurLayer( curLayer);										/* Reset the current layer	*/
	
		}
		return err;
	}
	return	paramErr;													/* not a floating window */
}


/*	***********************************************************	*
 *	Function:	xGetFrontServiceWindow							*
 *	Input:		the pointer to the WindowPtr to get an window.	*
 *	Output:		the front most window in the floting layer.		*
 *																*
 *	Side effect: A floating layer will be created if it is not	*
 *				 already been created -- fix in next release.	*
 *	***********************************************************	*/
pascal OSErr xGetFrontServiceWindow(WindowPtr *window )
{
	LayerPtr	floatLayer;
	LayerPtr	curLayer;
	OSErr		err;

	*window = nil;
	err = NewFloatLayer( &floatLayer);					/* Call the Process Manager		*/
	if ( err==noErr) {									/*  to get the floating layer	*/

		curLayer = SwapCurLayer( floatLayer);			/* Get the front window		*/
		*window = FrontWindow();						/*  in the floating layer	*/
		SetCurLayer( curLayer);							/* Reset the current layer	*/

	}
	return err;
}


/*	******************************************************************************	*
 *	Function:	xFindServiceWindow													*
 *	Input:		the pointer to the WindowPtr to get an window.						*
 *				thePoint = point of interest.										*
 *	Output:		which part of which window, if any, the 'thePoint'					*
 *				was pressed in.	the window is returned in theWindow.				*
 *																					*
 * Side effect: Side effect: A floating layer will be created if it is not			*
 *				already been created -- will fix this in the next release.			*
 * Modification history:															*
 *	10Jun92	KST	We patched HMGetBalloon to call FindServiceWindow for supporting	*
 *				floating window. FindWindow will call MBDF when the mouse is pressed*
 *				However, MBDF will call HMGetBalloon when Help is on and the mouse	*
 *				is pressed in the menu bar, HMGetBalloon will call MBDF because of	*
 *				our patch. The MBDF code lock the menulist handle on entry, but 	*
 *				unlock it on exit. So after the HMGetBalloon call, the handle is not*
 *				locked but MBDF keeps a dereferenced pointer, and ..... system will *
 *				eventually crash. The fix is to save the handle state before and	*
 *				restore it after the FindWindow call.						<#42>	*
 *	*******************************************************************************	*/
 
#define 		MenuList 		0xA1C

pascal short xFindServiceWindow(Point thePoint,WindowPtr *theWindow)
{
	LayerPtr	floatLayer;
	LayerPtr	curLayer;
	short		retValue;
	OSErr		err;
	long		*menulistPtr=MenuList;
	char		handleState;

	*theWindow	= nil;
	
	if (*menulistPtr == 0) {								/* if no menulist, then can't call FindWindow,		*/
		return inDesk;										/* ROM tries to get MBDF resource	<26May92 Tokyo>	*/
	}														/* and it does not check for error if can't get it	*/

	retValue	= inDesk;
	err = NewFloatLayer( &floatLayer);						/* Call the Process Manager ...		*/
	if ( err == noErr) {									/* ... to get the floating layer	*/

		curLayer = SwapCurLayer( floatLayer);				/* Find the window	*/
		
		/* When MBDF returns, it unlocks the handle, this makes it not reentrant. */
		handleState = HGetState( (Handle) *menulistPtr );	/* save the handle state		<#42> */
		retValue = FindWindow( thePoint, theWindow);		/* in the floating layer */
		HSetState( (Handle) *menulistPtr, handleState);		/* restore the handle state		<#42> */

		SetCurLayer( curLayer);								/* Reset the current layer	*/

	}
	return retValue;
}

