/* 
Copyright (c) 1991-2000 UserLand Software, Inc. 

Permission is hereby granted, free of charge, to any person obtaining a 
copy of this software and associated documentation files (the 
"Software"), to deal in the Software without restriction, including 
without limitation the rights to use, copy, modify, merge, publish, 
distribute, sublicense, and/or sell copies of the Software, and to 
permit persons to whom the Software is furnished to do so, subject to the 
following conditions: 

The above copyright notice and this permission notice shall be 
included in all copies or substantial portions of the Software. 

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
*/ 

#include <AppleEvents.h>
#include <Components.h> 
#include <Gestalt.h>
#include <Processes.h>
#include "uisharing.h"
#include "uisinternal.h"


#define longsizeof(x) ((long)sizeof(x))


tyWindowSharingGlobals wsGlobals = {0, 0, false};

tyMenuSharingGlobals msGlobals = {0, 0};

static Boolean flwindowsharing = true, flmenusharing = true;

static ProcPtr scripterrorcallback = nil;

	

Boolean uisDisposeSharedMenus (void); /*for compatibility with earlier menu sharing toolkits*/

Boolean uisIsSharedMenu (short);

void uisEnableSharedMenus (Boolean);

Boolean uisRunSharedMenuItem (short, short);



static Boolean HaveComponentManager (void) {

	long result;
	
	if (Gestalt (gestaltComponentMgr, &result) != noErr)
		return (false);
	
	return (result != 0);
	} /*HaveComponentManager*/


static Boolean isFrontProcess () {
	
	ProcessSerialNumber currentprocess, frontprocess;
	Boolean fl;
	
	GetCurrentProcess (¤tprocess);
	
	GetFrontProcess (&frontprocess);
	
	SameProcess (¤tprocess, &frontprocess, &fl);
	
	return (fl);
	} /*isFrontProcess*/
	
	
static pascal ComponentResult windoweventGlue (ComponentInstance comp, EventRecord *ev, tyWindowSharingGlobals *wsGlobals) 
	
	= ComponentCallNow (wsEventHandlerCommand, longsizeof (EventRecord *) + longsizeof (tyWindowSharingGlobals *)); 
	/*windoweventGlue*/


static pascal ComponentResult windowiscardGlue (ComponentInstance comp, WindowPtr w) 
	
	= ComponentCallNow (wsWindowIsCardCommand, longsizeof (WindowPtr)); 
	/*windowiscardGlue*/
	
	
static pascal ComponentResult closewindowGlue (ComponentInstance comp, WindowPtr w) 
	
	= ComponentCallNow (wsCloseWindowCommand, longsizeof (WindowPtr)); 
	/*windowiscardGlue*/
	

static pascal ComponentResult initsharedmenusGlue (ComponentInstance)
	
	= ComponentCallNow (msInitSharedMenusCommand, 0L); 
	/*initsharedmenusGlue*/
	

static pascal ComponentResult sharedmenuhitGlue (ComponentInstance, short, short, Boolean *)
	
	= ComponentCallNow (msSharedMenuHitCommand, longsizeof (short) + longsizeof (short) + longsizeof (Boolean *)); 
	/*sharedmenuhitGlue*/
	

static pascal ComponentResult sharedscriptrunningGlue (ComponentInstance, Boolean *)
	
	= ComponentCallNow (msSharedScriptRunningCommand, longsizeof (Boolean *));
	/*sharedscriptrunningGlue*/
	

static pascal ComponentResult cancelsharedscriptGlue (ComponentInstance)
	
	= ComponentCallNow (msCancelSharedScriptCommand, 0L); 
	/*cancelsharedscriptGlue*/
	

static pascal ComponentResult checksharedmenusGlue (ComponentInstance, short)
	
	= ComponentCallNow (msCheckSharedMenusCommand, longsizeof (short)); 
	/*checksharedmenusGlue*/
	

static pascal ComponentResult disposesharedmenusGlue (ComponentInstance)
	
	= ComponentCallNow (msDisposeSharedMenusCommand, 0L); 
	/*disposesharedmenusGlue*/
	

static pascal ComponentResult issharedmenuGlue (ComponentInstance, short, Boolean *)
	
	= ComponentCallNow (msIsSharedMenuCommand, longsizeof (short)); 
	/*issharedmenuGlue*/
	

static pascal ComponentResult enablesharedmenusGlue (ComponentInstance, Boolean)
	
	= ComponentCallNow (msEnableSharedMenusCommand, longsizeof (Boolean)); 
	/*enablesharedmenusGlue*/
	

static pascal ComponentResult runsharedmenuitemGlue (ComponentInstance, short, short)
	
	= ComponentCallNow (msRunSharedMenuItemCommand, longsizeof (short) + longsizeof (short)); 
	/*runsharedmenuitemGlue*/
	

static pascal ComponentResult setscripterrorcallbackGlue (ComponentInstance, ProcPtr)
	
	= ComponentCallNow (msSetScriptErrorCallbackCommand, longsizeof (ProcPtr)); 
	/*setscripterrorcallbackGlue*/


static pascal ComponentResult stubstartGlue (ComponentInstance comp) 

	= ComponentCallNow (wsStubStartCommand, 0L); 
	/*stubstartGlue*/


static pascal ComponentResult runhandleGlue (ComponentInstance comp, Handle h, short flscriptedcard, Point pt, uisEventCallback filter) 
	
	= ComponentCallNow (wsRunFromHandleCommand, longsizeof (Handle) + longsizeof (short) + longsizeof (Point) + longsizeof (uisEventCallback)); 
	/*runhandleGlue*/


static pascal ComponentResult runmodalhandleGlue (ComponentInstance comp, Handle h, short flscriptedcard, Point pt, uisEventCallback filter) 
	
	= ComponentCallNow (wsRunModalHandleCommand, longsizeof (Handle) + longsizeof (short) + longsizeof (Point) + longsizeof (uisEventCallback)); 
	/*runmodalhandleGlue*/


static pascal ComponentResult setobjectvalueGlue (ComponentInstance comp, Handle hcard, Str255 name, Handle value) 
	
	= ComponentCallNow (wsSetObjectValueCommand, longsizeof (Handle) + longsizeof (char *) + longsizeof (Handle)); 
	/*setobjectvalueGlue*/
	

static pascal ComponentResult getobjectvalueGlue (ComponentInstance comp, Handle hcard, Str255 name, Handle *value) 
	
	= ComponentCallNow (wsGetObjectValueCommand, longsizeof (Handle) + longsizeof (char *) + longsizeof (Handle *)); 
	/*getobjectvalueGlue*/
	

static pascal ComponentResult getobjecthandleGlue (ComponentInstance comp, Handle hcard, Str255 name, Handle *h) 
	
	= ComponentCallNow (wsGetObjectHandleCommand, longsizeof (Handle) + longsizeof (char *) + longsizeof (Handle *)); 
	/*getobjecthandleGlue*/
	

static pascal ComponentResult recalcGlue (ComponentInstance comp, Handle h) 
	
	= ComponentCallNow (wsRecalcObjectCommand, longsizeof (Handle)); 
	/*recalcGlue*/


static pascal ComponentResult editGlue (ComponentInstance comp, short editcommand) 
	
	= ComponentCallNow (wsEditCommand, longsizeof (short)); 
	/*editGlue*/


static pascal ComponentResult updateGlue (ComponentInstance comp, Handle h) 
	
	= ComponentCallNow (wsUpdateCommand, longsizeof (Handle)); 
	/*updateGlue*/


static Boolean IACgetbinaryparam (AppleEvent *event, OSType keyword, Handle *hbinary, OSType *binarytype, OSErr *errcode) {
	
	AEDesc result;
	OSErr ec;
	
	*errcode = AEGetParamDesc (event, (AEKeyword) keyword, typeWildCard, &result);
	
	if (*errcode != noErr) 
		return (false);
	
	*hbinary = result.dataHandle;
	
	*binarytype = result.descriptorType;
	
	return (true);
	} /*IACgetbinaryparam*/


static Boolean IACgetstringparam (AppleEvent *event, OSType keyword, Str255 s, OSErr *errcode) {
	
	AEDesc result;
	Handle htext;
	long lenstring;
	
	*errcode = AEGetParamDesc (event, (AEKeyword) keyword, typeChar, &result);
	
	if (*errcode != noErr) 
		return (false);
	
	htext = result.dataHandle;
	
	lenstring = GetHandleSize (htext);
	
	if (lenstring > 255)
		lenstring = 255;
		
	s [0] = (unsigned char) lenstring;
	
	BlockMove (*htext, &s [1], lenstring);
	
	return (true);
	} /*IACgetstringparam*/
	
	
static Boolean IACgetlongparam (AppleEvent *event, OSType keyword, long *val, OSErr *errcode) {
	
	OSErr ec;
	DescType actualtype;
	Size actualsize;
	
	*errcode = AEGetParamPtr (
		
		event, (AEKeyword) keyword, typeLongInteger, 
		
		&actualtype, (Ptr) val, sizeof (long), &actualsize);
	
	if (*errcode != noErr) 
		return (false);
	
	return (true);
	} /*IACgetlongparam*/
	

static void IACreturnboolean (AppleEvent *event, Boolean fl) {
	
	AEPutParamPtr (event, keyDirectObject, typeBoolean, (Ptr) &fl, sizeof (Boolean));
	} /*IACreturnboolean*/


static pascal OSErr handleopencardwindow (AppleEvent *event, AppleEvent *reply, long refcon) {

	/*	
	installed as an AE handler, intended to be called by scripts that
	want to open a shared window in the application's window list.
	
	it's a tradeoff -- burn a little memory in every UI sharing-aware
	app, or make the SHUI server component perform an unnatural act. the
	problem is the component call needs a component instance. only the 
	host app knows which instance we're talking about. the server could
	manage its data according to ProcessSerialNumber, but this is not a
	natural way of doing things for Mac components. not impossible, just
	a bunch of extra code in a very hard place to debug. 3/21/93 DW
	
	here's a one-line script that calls this handler...
	
	appleEvent ('KAHL', 'SHUI', 'OPEN', '----', number ('zzzz'), 'card', scratchpad.cards.yyy)
	*/
	
	OSErr ec;
	OSType idcomponent; 
	OSType binarytype;
	Handle packedcard;
	
	if (!IACgetlongparam (event, keyDirectObject, (long *) &idcomponent, &ec))
		return (ec);

	if (!IACgetbinaryparam (event, 'card', &packedcard, &binarytype, &ec))
		return (ec);
		
	IACreturnboolean (event, uisOpenHandle (packedcard, false, 0, 0, nil));
	
	return (noErr);
	} /*handleopencardwindow*/
	
	
static void checkInstance (ComponentInstance *instance) {
	
	/*
	if the server disappears, disable our connection with the
	component by setting the instance to 0.
	*/
	
	ComponentInstance x = *instance;
	
	if (x != 0) {
	
		if (GetComponentVersion (x) == badComponentInstance) /*no longer valid*/
			x = 0;
		}
		
	*instance = x;
	} /*checkInstance*/
	

static Boolean connectWithServers (void) {

	if (flwindowsharing && (wsGlobals.windowserver == 0)) {
	
		AEInstallEventHandler ('SHUI', 'OPEN', (EventHandlerProcPtr) &handleopencardwindow, 0, false);
		
		wsGlobals.windowserver = OpenDefaultComponent (wsComponentType, wsComponentSubType);
		}
	
	if (flmenusharing && (msGlobals.menuserver == 0)) {
	
		msGlobals.menuserver = OpenDefaultComponent (msComponentType, msComponentSubType);
	
		if (msGlobals.menuserver != 0) {
	
			if (initsharedmenusGlue (msGlobals.menuserver) == noErr) {
		
				if (setscripterrorcallbackGlue (msGlobals.menuserver, scripterrorcallback) == noErr)
					return (true);
				}
				
			CloseComponent (msGlobals.menuserver); /*error initializing menu sharing*/
			
			msGlobals.menuserver = 0;
			
			flmenusharing = false; /*try to connect only once*/
			}
		}
	} /*connectWithServers*/
	

Boolean uisDisposeSharedMenus (void) { /*dispose menu array and handles it contains*/
	
	if (msGlobals.menuserver == 0)
		return (false);
		
	return (disposesharedmenusGlue (msGlobals.menuserver) == noErr);
	} /*uisDisposeSharedMenus*/


Boolean uisIsSharedMenu (short idmenu) { /*return true if the indicated menu is one of the shared menus*/
	
	Boolean flshared;
	
	if (msGlobals.menuserver != 0) {
		
		if (issharedmenuGlue (msGlobals.menuserver, idmenu, &flshared) == noErr)
			return (flshared);
		}
		
	return (false);
	} /*uisIsSharedMenu*/


void uisEnableSharedMenus (Boolean flenable) { /*enables or disables the shared menus*/
	
	if (msGlobals.menuserver != 0)
		enablesharedmenusGlue (msGlobals.menuserver, flenable);
	} /*uisEnableSharedMenus*/


Boolean uisRunSharedMenuItem (short idmenu, short iditem) {
	 
	/*
	call the menu server to run the script linked to the indicated menu item.
	*/
	
	if (msGlobals.menuserver == 0)
		return (false);
		
	return (runsharedmenuitemGlue (msGlobals.menuserver, idmenu, iditem) == noErr);
	} /*uisRunSharedMenuItem*/
	

Boolean uisSharedMenuHit (short idmenu, short iditem) {

	Boolean flshareditem;
	
	if (msGlobals.menuserver == 0) 
		return (false);
		
	if (sharedmenuhitGlue (msGlobals.menuserver, idmenu, iditem, &flshareditem) != noErr)
		return (false);
		
	return (flshareditem); /*client handles if it wasn't a shared item*/
	} /*uisSharedMenuHit*/
	
	
Boolean uisHandleEvent (EventRecord *ev, Boolean *flcloseallwindows) {
	
	static Boolean flswitchedin = true;
	
	/*watch "juggler" events pass thru the app, remember whether we're in or out*/ {
	
		if ((*ev).what == osEvt) 
			flswitchedin = ((*ev).message & resumeFlag) != 0;
		}
	
	/*check the connection with our servers on each event*/ {
	
		connectWithServers ();
	
		checkInstance (&wsGlobals.windowserver);
	
		checkInstance (&msGlobals.menuserver);
		}
	
	if (wsGlobals.windowserver != 0) {
		
		Boolean fl;
	
		wsGlobals.flcloseallwindows = false;
	
		fl = windoweventGlue (wsGlobals.windowserver, ev, &wsGlobals);
	
		*flcloseallwindows = wsGlobals.flcloseallwindows;
		
		if (fl)
			return (true); /*we handled the event*/
		}
	
	if (msGlobals.menuserver != 0) {
		
		switch ((*ev).what) {
			
			case nullEvent:
				if (flswitchedin) 
					checksharedmenusGlue (msGlobals.menuserver, msGlobals.idinsertafter);
					
				break;
			} /*switch*/
		}
		
	return (false); /*client app should handle the event*/
	} /*uisHandleEvent*/
	

Boolean uisIsSharedWindow (WindowPtr w) {
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	if (w == nil)
		return (false);
		
	return (windowiscardGlue (wsGlobals.windowserver, w));
	} /*uisIsSharedWindow*/
	
	
Boolean uisCloseSharedWindow (WindowPtr w) {
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	if (!uisIsSharedWindow (w))
		return (false); /*we didn't close it*/
		
	return (closewindowGlue (wsGlobals.windowserver, w)); 
	} /*uisCloseSharedWindow*/
	
	
void uisCloseAllSharedWindows (void) {
	
	WindowPtr w = FrontWindow ();
	WindowPtr wnext;
	
	while (w != nil) {
		
		wnext = (WindowPtr) (*(WindowPeek) w).nextWindow;
		
		if (uisIsSharedWindow (w))
			uisCloseSharedWindow (w);
		
		w = wnext; 
		} /*while*/
	} /*uisCloseAllSharedWindows*/
	

Boolean uisStubStart (void) {
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	wsGlobals.errorcode = stubstartGlue (wsGlobals.windowserver);
	
	return (wsGlobals.errorcode == noErr);
	} /*uisStubStart*/


Boolean uisOpenHandle (Handle h, Boolean flscriptedcard, short top, short left, uisEventCallback filter) {
	
	Point pt;
	
	pt.h = left; pt.v = top;
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	wsGlobals.errorcode = runhandleGlue (wsGlobals.windowserver, h, flscriptedcard, pt, filter);
	
	return (wsGlobals.errorcode == noErr);
	} /*uisOpenHandle*/
	
	
Boolean uisOpenCardResource (short id, Boolean flscriptedcard, short top, short left, uisEventCallback filter) {
	
	Handle h;
	
	h = GetResource ('CARD', id);
	
	if (h == nil) {
		
		wsGlobals.errorcode = ResError ();
		
		return (false);
		}
		
	DetachResource (h);
	
	return (uisOpenHandle (h, flscriptedcard, top, left, filter));
	} /*uisOpenCardResource*/
	
	
Boolean uisRunModalHandle (Handle h, Boolean flscriptedcard, short top, short left, uisEventCallback filter) {
	
	Point pt;
	
	pt.h = left; pt.v = top;
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	wsGlobals.errorcode = runmodalhandleGlue (wsGlobals.windowserver, h, flscriptedcard, pt, filter);
	
	return (wsGlobals.errorcode == noErr);
	} /*uisRunModalHandle*/
	
	
Boolean uisRunModalResource (short id, Boolean flscriptedcard, short top, short left, uisEventCallback filter) {
	
	Handle h;
	
	h = GetResource ('CARD', id);
	
	if (h == nil) {
		
		wsGlobals.errorcode = ResError ();
		
		return (false);
		}
		
	DetachResource (h);
	
	return (uisRunModalHandle (h, flscriptedcard, top, left, filter));
	} /*uisRunModalResource*/
	
	
Boolean uisSetObjectValue (Handle hcard, Str255 name, Handle hvalue) {
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	return (setobjectvalueGlue (wsGlobals.windowserver, hcard, name, hvalue));
	} /*uisSetObjectValue*/
	
	
Boolean uisGetObjectValue (Handle hcard, Str255 name, Handle *hvalue) {
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	return (getobjectvalueGlue (wsGlobals.windowserver, hcard, name, hvalue));
	} /*uisGetObjectValue*/
	
	
Boolean uisUpdate (Handle hcard) {
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	return (updateGlue (wsGlobals.windowserver, hcard));
	} /*uisUpdate*/
	
	
Boolean uisGetObjectHandle (Handle hcard, Str255 name, Handle *hvalue) {
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	return (getobjecthandleGlue (wsGlobals.windowserver, hcard, name, (Handle *) hvalue));
	} /*uisGetObjectHandle*/
	

Boolean uisRecalcObject (Handle hobject) {
	
	if (wsGlobals.windowserver == 0)
		return (false);
		
	return (recalcGlue (wsGlobals.windowserver, hobject));
	} /*uisRecalcObject*/
	
	
Boolean uisEdit (short editcommand) {
		
	if (wsGlobals.windowserver == 0)
		return (false);
		
	return (editGlue (wsGlobals.windowserver, editcommand));
	} /*uisEdit*/
	
	
Boolean uisInit (ProcPtr callback, short idinsertafter, OSType idclientapp, unsigned short bitarray) {
	
	/*
	initialize window sharing and menu sharing. 
	
	the callback is a routine that receives a Str255 as a parameter. it should
	display the string in a modal dialog box and wait for the user to click 
	on OK. it's used for reporting script compiling or runtime errors. pass in
	nil if you want errors to not be reported to the user (not a great thing
	to do to your users!).
	
	idinsertafter tells menu sharing where to begin allocating new menu ids.
	it's the same parameter that was passed to CheckSharedMenus in previous
	incarnations of menu sharing.
	
	idclientapp is the 4-character identifier of the client app. if it's
	'wxyz', then we'll get the shared menus at system.menubars.wxyz. If you
	pass in 0, the menu sharing server uses the Process Manager to get your
	id, as the creator id of the running application. This feature is supported
	in the UI sharing API only, as of June 12, 1993. A modification needs to
	be made to Frontier to support this fully.
	
	DW 8/18/93: added a 16-bit bit-array that allows the caller to modify
	the behavior of UI Sharing. if it's set to 0 (the usual case) you get 
	all the features.
	*/
	
	if (bitarray & noMenuSharing)
		flmenusharing = false;
		
	if (bitarray & noWindowSharing)
		flwindowsharing = false;
	
	wsGlobals.windowserver = 0;
	
	msGlobals.menuserver = 0;
	
	if ((!flmenusharing) && (!flwindowsharing))
		return (true);
	
	if (!HaveComponentManager ()) {
		
		flmenusharing = flwindowsharing = false;
		
		return (false);
		}
	
	msGlobals.idinsertafter = idinsertafter;
	
	scripterrorcallback = callback;
	
	connectWithServers ();
		
	return (true);
	} /*uisInit*/
	
	
void uisClose (void) {

	if (wsGlobals.windowserver != 0)		
		CloseComponent (wsGlobals.windowserver);
	
	if (msGlobals.menuserver != 0) 
		CloseComponent (msGlobals.menuserver);
	} /*uisClose*/