/*===============================
Copyright © 1998-2002 Brian Hall

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*===============================*/
#ifndef __APPLE_CC__
#if !defined( __CONTROLS__ )
	#include <Controls.h>
#endif
#endif

#include "BlugsPrivate.h"


static pascal void				local_BLUserPaneDrawProc( ControlRef control, SInt16 part );
static pascal ControlPartCode	local_BLUserPaneHitTestProc( ControlRef control, Point where );
static pascal ControlPartCode	local_BLUserPaneTrackProc( ControlRef control, Point startPt,
															ControlActionUPP actionProc );
static pascal ControlPartCode	local_BLUserPaneKeyDownProc( ControlRef inControl, SInt16 inKeyCode,
															SInt16 inCharCode, SInt16 inModifiers );
static pascal void				local_BLUserPaneActivateProc( ControlRef control, Boolean activating );
static pascal ControlPartCode	local_BLUserPaneFocusProc( ControlRef control, ControlFocusPart part );
static pascal void				local_BLUserPaneIdleProc( ControlRef control );
static void						local_BLSetUpUserPaneProcs( void );
static OSErr					local_BLInstallUserPaneProcs( ControlRef inControl );
#if __BL_USE_CARBON_API__
static pascal OSStatus			local_BLControlEventsHandler( EventHandlerCallRef inCallRef,
															EventRef inEvent, void* inData );
static pascal OSStatus			local_BLOtherEventsHandler( EventHandlerCallRef inCallRef,
															EventRef inEvent, void* inData );
#endif

// Blugs can always handle tracking, activation and idle events.
#define blBasicUserPaneFeatures		(kControlHandlesTracking + kControlWantsActivate + \
									kControlWantsIdle + kControlSupportsEmbedding)

static struct
{
	long						numberOfUsers;
	ControlUserPaneDrawUPP		drawUPP;
	ControlUserPaneHitTestUPP	testUPP;
	ControlUserPaneTrackingUPP	trackingUPP;
	ControlUserPaneKeyDownUPP	downUPP;
	ControlUserPaneActivateUPP	activateUPP;
	ControlUserPaneFocusUPP		focusUPP;
	ControlUserPaneIdleUPP		idleUPP;
} _blUserPaneInfo = { 0, nil, nil, nil, nil, nil, nil, nil };

//===============================
BL_PROC OSErr BLMakeUserPaneControl( ControlRef* outControl, BlugsRef inList )
{
	OSErr			err;
	WindowRef		window;
	ControlRef		tempCntl = nil;
	SInt16			features;
	
	// Moses was here
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		_AssertStr( outControl != nil, "Error: nil ControlRef pointer." );
		if (!outControl) err = paramErr;
		else
		{
			// If the Appearance Manager is not installed, can't create a user pane control.
			if (!bl_Appearance) err = unimpErr;
			else
			{
				Rect	box;
				
				local_BLSetUpUserPaneProcs();
				// Get the window and the size of the control.
				window = bl_Window(inList);
				// Note: this covers the scroll bars also.  Which is why we set the scroll bar supervisor to here later on.
				box = _BLUserPaneRect( inList );
				features = blBasicUserPaneFeatures;
				// Blugs can optionally handle keyboard focus as well.
				if ( bl_CanFocus(inList) )
					features |= kControlSupportsFocus | kControlGetsFocusOnClick;
				// Create the new control.
				tempCntl = NewControl( window, &box, "\pBlugsUserPane", true, features, 0, 0,
										kControlUserPaneProc, bl_HiBitSet(inList) );
				// Install callbacks.
				err = local_BLInstallUserPaneProcs( tempCntl );
				// Store the control in the list so we can resize the control from the Blugs API.
				bl_UserPane(inList) = tempCntl;
			}
			// Need to set the scroll bar's supervisor so all calls are routed through the user pane.
			// All clicks associated with the Blugs list (title bars, scroll bars and cells) will report a part code
			// of kControlListBoxPart or kControlListBoxDoubleClickPart.

			// Note that EmbedControl may return an error code because there may be no root control for the window/dialog.
			// That's OK, it just means that the scroll bars will not hide/show and activate/deactivate when the UserPane control does.
			if (!err)
			{
				ControlRef	scroll;
				OSErr		ignoreErr;
				
				scroll = bl_VScroll(inList);
				if (scroll)
				{
					err = SetControlSupervisor( scroll, tempCntl );
					if (!err) ignoreErr = EmbedControl( scroll, tempCntl );
				}
				if (!err)
				{
					scroll = bl_HScroll(inList);
					if (scroll)
					{
						err = SetControlSupervisor( scroll, tempCntl );
						if (!err) ignoreErr = EmbedControl( scroll, tempCntl );
					}
				}
			}
		}
	}
	*outControl = tempCntl;
	return err;
}

//===============================
// Takes a preexisting user pane control and converts it to a Blugs control.
// if inSyncRects is true, then it forces the Blugs list to confirm to the control rectangle.
BL_PROC OSErr BLConvertUserPaneControl( Boolean inSyncRects, ControlRef ioControl, BlugsRef inList )
{
	OSErr			err;
	WindowRef		window;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		_AssertStr( ioControl != nil, "Passed nil ControlRef as parameter." );
		if (!ioControl) err = paramErr;
		else
		{
			// If the Appearance Manager is not installed, can't create a user pane control.
			if (!bl_Appearance) err = unimpErr;
			else
			{
				local_BLSetUpUserPaneProcs();
				// Get the window and the size of the control.
				window = bl_Window(inList);
				// Note: this covers the scroll bars also.  Which is why we set the scroll bar supervisor to here later on.
				if (inSyncRects)
				{
					Rect		box;
					
					GetControlBounds( ioControl, &box );
					if (bl_Border(inList)) InsetRect( &box, 2, 2 );
					err = BLSetRect( &box, inList );
				}
				// Don't create the new control...We wanna use the one we already have.
				SetControlReference( ioControl, bl_HiBitSet(inList) );
				// Install callbacks.
				err = local_BLInstallUserPaneProcs( ioControl );
				// Store the control in the list so we can resize the control from the Blugs API.
				bl_UserPane(inList) = ioControl;
			}
			if (!err)
			{
				ControlRef	scroll;
				OSErr		ignoreErr;
				
				scroll = bl_VScroll(inList);
				if (scroll)
				{
					err = SetControlSupervisor( scroll, ioControl );
					if (!err) ignoreErr = EmbedControl( scroll, ioControl );
				}
				if (!err)
				{
					scroll = bl_HScroll(inList);
					if (scroll)
					{
						err = SetControlSupervisor( scroll, ioControl );
						if (!err) ignoreErr = EmbedControl( scroll, ioControl );
					}
				}
			}
		}
	}
	return err;
}

//===============================
BL_PROC BlugsRef BLRefFromUserPaneControl( ControlRef inBlugsControl )
{
	BlugsRef	list = nil;
	
	_AssertStr( inBlugsControl != nil, "Error: passed nil ControlRef as parameter." );
	if ( inBlugsControl != nil )
	{
		OSErr	err;
		
		list = (BlugsRef)bl_HiBitClear( GetControlReference( inBlugsControl ) );
		err = _BLValidateList( list );
		if (err) list = nil;
	}
	return list;
}

//===============================
// Returns:
//	paramErr		bad control
//	nilHandleErr	bad list
BL_PROC OSErr BLDisposeUserPaneControl( Boolean inDisposeOfList, ControlRef inControl )
{
	OSErr	err;
	
	_AssertStr( inControl != nil, "Error: passed nil ControlRef as parameter." );
	if ( inControl == nil ) err = paramErr;
	else
	{
		BlugsRef		list;
		
		list = (BlugsRef)bl_HiBitClear( GetControlReference( inControl ) );
		_CheckHandlerFlag(list)
		err = _BLValidateList( list );
		if (!err)
		{
			if (inDisposeOfList)
			{
				ControlRef	root;
				
				err = GetRootControl( bl_Window(list), &root );
				if (!err)
				{
					if (bl_VScroll(list)) err = EmbedControl( bl_VScroll(list), root );
					if (bl_HScroll(list)) err = EmbedControl( bl_HScroll(list), root );
				}
				err = noErr;
			}
			else bl_UserPane(list) = nil;
			// 11/5/2001 Moses -->1.1
			// Calling DisposeControl on the user pane will dispose Blugs' scroll bars
			//	if an embedding hierarchy is established in the window.
			// So before disposing the user pane we manually re-embed the scroll bars
			//	in the root control. We do this whether we are disposing the list or not.
			DisposeControl( inControl );
			if ( _blUserPaneInfo.numberOfUsers != 0 ) _blUserPaneInfo.numberOfUsers--;
			if ( _blUserPaneInfo.numberOfUsers == 0 && _blUserPaneInfo.drawUPP != nil )
			{
				// If nobody is using them, dispose of the routine descriptors.
				DisposeControlUserPaneDrawUPP( _blUserPaneInfo.drawUPP );
				DisposeControlUserPaneHitTestUPP( _blUserPaneInfo.testUPP );
				DisposeControlUserPaneTrackingUPP( _blUserPaneInfo.trackingUPP );
				DisposeControlUserPaneKeyDownUPP( _blUserPaneInfo.downUPP );
				DisposeControlUserPaneActivateUPP( _blUserPaneInfo.activateUPP );
				DisposeControlUserPaneFocusUPP( _blUserPaneInfo.focusUPP );
				DisposeControlUserPaneIdleUPP( _blUserPaneInfo.idleUPP );
				_BLZeroMemory( (Ptr)&_blUserPaneInfo, sizeof(_blUserPaneInfo) );
			}
			if (inDisposeOfList) BLDispose( list );
		}
		
	}
	return err;
}


#pragma mark -
//===============================
Rect _BLUserPaneRect( BlugsRef inList )
{
	Rect	r;
	
	r = bl_Rect( inList );
	if (bl_Border(inList)) InsetRect( &r, -2, -2 );
	return r;
}

//===============================
static pascal void local_BLUserPaneDrawProc( ControlRef control, SInt16 part )
{
	#pragma unused (part)
	BlugsRef	list;
	
	list = (BlugsRef)bl_HiBitClear( GetControlReference( control ) );
	if (list)
	{
		if (bl_Autodraw(list)) BLUpdate( nil, list );
	}
}

//===============================
// 11/10/2001 Moses -->1.1
// Added check for grow box part.
// DO NOT call BLHitTest from this routine!
// (It recursively calls FindControl(UnderMouse);
//	trust me, I crashed twice before I figured it out.)
static pascal ControlPartCode local_BLUserPaneHitTestProc( ControlRef control, Point where )
{
	BlugsRef				list;
	ControlPartCode	part = kControlNoPart;
	
	list = (BlugsRef)bl_HiBitClear( GetControlReference( control ) );
	// Get the list rectangle which includes any scroll bars.
	if (list)
	{
		Rect		box;
		
		box = bl_Rect( list );
		if (bl_Test((*list)->privateFlags,blIsInCompositingWindow))
		{
			where.v += box.top;
			where.h += box.left;
		}
		if (bl_Border(list)) InsetRect( &box, -2, -2 );
		if (PtInRect( where, &box )) part = kControlListBoxPart;
		if (bl_HasGrow(list) && !bl_Aqua && bl_DrawGrow(list))
		{
			box = _BLGrowBoxRect( list );
			if (PtInRect( where, &box )) part = blGrowBoxPart;
		}
	}
	return part;
}

//===============================
static pascal ControlPartCode local_BLUserPaneTrackProc(ControlRef control, Point where, ControlActionUPP actionProc)
{
	BLClickResult		clickResult;
	ControlPartCode		result = kControlNoPart;
	ControlRef			hScroll, vScroll;

	if (actionProc == (ControlActionUPP)-1L || actionProc == nil)
	{
		EventModifiers	modifiers = 0;
		KeyMap			keys;
		Rect			box;
		BlugsRef		list;
		
		list = (BlugsRef)bl_HiBitClear( GetControlReference( control ) );
		vScroll = bl_VScroll( list );
		hScroll = bl_HScroll( list );
		box = bl_Rect( list );
		if (bl_Test((*list)->privateFlags,blIsInCompositingWindow))
		{
			where.v += box.top;
			where.h += box.left;
		}
		// Set the scroll bar supervisors to nil so they can handle themselves normally in the call to BLClick.
		if (vScroll) (void)SetControlSupervisor( vScroll, nil );
		if (hScroll) (void)SetControlSupervisor( hScroll, nil );
		GetKeys( keys );
		if (bl_Test(keys[1],1)) bl_Set(modifiers,shiftKey);
		if (bl_Test(keys[1],4)) bl_Set(modifiers,optionKey);
		if (bl_Test(keys[1],8)) bl_Set(modifiers,controlKey);
		if (bl_Test(keys[1],32768)) bl_Set(modifiers,cmdKey);
		clickResult = BLClick( TickCount(), where, modifiers, list );
		// Reset the scroll bar supervisors to this UserPane control so later clicks are reported properly.
		if (vScroll) (void)SetControlSupervisor( vScroll, control );
		if (hScroll) (void)SetControlSupervisor( hScroll, control );
		if (clickResult == blGrowBoxClickResult) result = blGrowBoxPart;
		else if ( clickResult == blCellRgnDoubleClickResult ) result = kControlListBoxDoubleClickPart;
		else result = kControlListBoxPart;
	}
	return result;
}

//===============================
// 10/16/2001 Moses -->1.1
// BLKey now called with correct parameter order. (message and when were reversed)
static pascal ControlPartCode local_BLUserPaneKeyDownProc( ControlRef inControl, SInt16 inKeyCode,
															SInt16 inCharCode, SInt16 inModifiers )
{
	UInt32			message = inCharCode + ( inKeyCode << 16L );
	BLKeyResult		keyResult;
	
	keyResult = BLKey( message, TickCount(), inModifiers,
						(BlugsRef)bl_HiBitClear(GetControlReference( inControl )));
	return kControlListBoxPart;
}

//===============================
static pascal void local_BLUserPaneActivateProc(ControlRef control, Boolean activating)
{	
	BLSetActive( activating, (BlugsRef)bl_HiBitClear(GetControlReference( control )) );
}

//===============================
static pascal ControlPartCode local_BLUserPaneFocusProc(ControlRef control, ControlFocusPart part)
{
	return BLSetFocusedPart( part, (BlugsRef)bl_HiBitClear(GetControlReference( control )) );
}

//===============================
static pascal void local_BLUserPaneIdleProc( ControlRef control )
{	Point		mouseLoc;

	GetMouse( &mouseLoc );
	BLIdle( mouseLoc, (BlugsRef)bl_HiBitClear(GetControlReference( control )) );
}

//===============================
static void local_BLSetUpUserPaneProcs( void )
{
	if ( _blUserPaneInfo.numberOfUsers == 0 && _blUserPaneInfo.drawUPP == nil )
	{
		// If nobody is using these yet, create the UserPane Routine Descriptors.
		_blUserPaneInfo.drawUPP		= NewControlUserPaneDrawUPP(local_BLUserPaneDrawProc);
		_blUserPaneInfo.testUPP		= NewControlUserPaneHitTestUPP(local_BLUserPaneHitTestProc);
		_blUserPaneInfo.trackingUPP	= NewControlUserPaneTrackingUPP(local_BLUserPaneTrackProc);
		_blUserPaneInfo.downUPP		= NewControlUserPaneKeyDownUPP(local_BLUserPaneKeyDownProc);
		_blUserPaneInfo.activateUPP	= NewControlUserPaneActivateUPP(local_BLUserPaneActivateProc);
		_blUserPaneInfo.focusUPP	= NewControlUserPaneFocusUPP(local_BLUserPaneFocusProc);
		_blUserPaneInfo.idleUPP		= NewControlUserPaneIdleUPP(local_BLUserPaneIdleProc);
	}
	_blUserPaneInfo.numberOfUsers++;
}

//===============================
// 10/16/2001 Moses -->1.1
// Split this off from BLMakeUserPaneControl and BLConvertUserPaneControl.
static OSErr local_BLInstallUserPaneProcs( ControlRef inControl )
{
	OSErr	err;
	
	err = SetControlData( inControl, kControlNoPart, kControlUserPaneDrawProcTag,
							sizeof(_blUserPaneInfo.drawUPP), (Ptr)&_blUserPaneInfo.drawUPP );
	if (err == noErr) err = SetControlData( inControl, kControlNoPart, kControlUserPaneHitTestProcTag,
											sizeof(_blUserPaneInfo.testUPP),
											(Ptr)&_blUserPaneInfo.testUPP );
	if (err == noErr) err = SetControlData( inControl, kControlNoPart, kControlUserPaneTrackingProcTag,
											sizeof(_blUserPaneInfo.trackingUPP),
											(Ptr)&_blUserPaneInfo.trackingUPP );
	if (err == noErr) err = SetControlData( inControl, kControlNoPart, kControlUserPaneKeyDownProcTag,
											sizeof(_blUserPaneInfo.downUPP),
											(Ptr)&_blUserPaneInfo.downUPP );
	if (err == noErr) err = SetControlData( inControl, kControlNoPart, kControlUserPaneActivateProcTag,
											sizeof(_blUserPaneInfo.activateUPP),
											(Ptr)&_blUserPaneInfo.activateUPP );
	if (err == noErr) err = SetControlData( inControl, kControlNoPart, kControlUserPaneFocusProcTag,
											sizeof(_blUserPaneInfo.focusUPP),
											(Ptr)&_blUserPaneInfo.focusUPP );
	if (err == noErr) err = SetControlData( inControl, kControlNoPart, kControlUserPaneIdleProcTag,
											sizeof(_blUserPaneInfo.idleUPP),
											(Ptr)&_blUserPaneInfo.idleUPP );
	return err;
}


#if __BL_USE_CARBON_API__
#pragma mark -
//===============================
// This param name was accidentally left unexported for
// the release of Jaguar.
#define kEventParamControlLikesDrag 'cldg'
static const EventTypeSpec	events[] =
{	{ kEventClassControl, kEventControlInitialize },
	{ kEventClassControl, kEventControlDispose },
	{ kEventClassControl, kEventControlHitTest },
	{ kEventClassControl, kEventControlDraw },
	{ kEventClassControl, kEventControlGetPartRegion },
	//{ kEventClassControl, kEventControlGetPartBounds },
	{ kEventClassControl, kEventControlGetData },
	{ kEventClassControl, kEventControlSetData },
	{ kEventClassControl, kEventControlTrack },
	{ kEventClassControl, kEventControlActivate },
	{ kEventClassControl, kEventControlDeactivate },
	{ kEventClassControl, kEventControlSetCursor },
	{ kEventClassControl, kEventControlSetFocusPart	},
	{ kEventClassControl, kEventControlGetFocusPart	},
	{ kEventClassControl, kEventControlGetClickActivation },
	{ kEventClassControl, kEventControlDragEnter },
	{ kEventClassControl, kEventControlDragLeave },
	{ kEventClassControl, kEventControlDragWithin },
	{ kEventClassControl, kEventControlDragReceive },
	{ kEventClassControl, kEventControlGetSizeConstraints },
	{ kEventClassControl, kEventControlEnabledStateChanged },
	{ kEventClassControl, kEventControlBoundsChanged },
	{ kEventClassControl, kEventControlOwningWindowChanged }
};

static const EventTypeSpec	otherEvents[] =
{	{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
	{ kEventClassCommand, kEventCommandProcess },
	{ kEventClassCommand, kEventCommandUpdateStatus }
};


//===============================
BL_PROC OSErr BLMakeCarbonControl( ControlRef* outControl, BlugsRef inList )
{
	OSErr							err = noErr;
	ControlRef						control = nil;
	static ToolboxObjectClassRef	toolboxClass = nil;
	static EventHandlerUPP			handlerUPP = nil;
	
	if (!toolboxClass)
	{
		BLEnter( 0 );
		if (!handlerUPP) handlerUPP = NewEventHandlerUPP( local_BLControlEventsHandler );
		err = RegisterToolboxObjectClass( CFSTR("com.Blugs.BlugsView"), nil,
											GetEventTypeCount( events ), events,
											handlerUPP, nil, &toolboxClass );
	}
	if (!err)
	{
		ControlDefSpec	spec;
  		Rect			bounds;
  		
  		err = CreateRootControl( bl_Window(inList), nil );
  		err = noErr;
  		spec.defType = kControlDefObjectClass;
  		spec.u.classRef = toolboxClass;
  		bounds = bl_Rect(inList);
  		err = CreateCustomControl( bl_Window(inList), &bounds, &spec, nil, &control );
		if (!err && control)
		{
			//ControlRef	scroll;
			
			SetControlDataHandle( control, (Handle)inList );
			err = InstallEventHandler( GetControlEventTarget( control ),
									NewEventHandlerUPP( local_BLOtherEventsHandler ),
									GetEventTypeCount( otherEvents ), otherEvents,
									control, nil );
			(*inList)->userPane = control;
			/*scroll = bl_VScroll(inList);
			if (scroll)
			{
				err = SetControlSupervisor( scroll, control );
				err = EmbedControl( scroll, control );
			}
			scroll = bl_HScroll(inList);
			if (scroll)
			{
				err = SetControlSupervisor( scroll, control );
				err = EmbedControl( scroll, control );
			}*/
			//_BLAdjustScrollBarSizesAndLocations( inList );
			if (bl_Visible(inList)) ShowControl( control );
		}
	}
	*outControl = control;
	return err;
}

//===============================
pascal static OSStatus local_BLControlEventsHandler( EventHandlerCallRef inCallRef, EventRef inEvent,
			 									void* inData )
{
	#pragma unused (inCallRef,inData)
	OSStatus			err;
	ControlPartCode		part = kControlNoPart;
	Point				where;
	UInt32				features, eventKind;
	DragReference		drag;
	Rect				listBounds;
	BlugsRef			list;
	ControlRef			control;
	
	eventKind = GetEventKind( inEvent );
	_AssertStr(GetEventClass( inEvent ) == kEventClassControl,"Internal error: local_BLControlEventsHandler called with non-controlevent");
	err = GetEventParameter( inEvent, kEventParamDirectObject, typeControlRef,
								nil, sizeof(control), nil, &control );
	if (!err && eventKind != kEventControlInitialize)
	{
		list = (BlugsRef)GetControlDataHandle( control );
		listBounds = bl_Rect(list);
	}
	err = eventNotHandledErr;
	switch (eventKind)
	{
		case kEventControlInitialize:
		features =	//kControlSupportsGhosting |
					kControlSupportsEmbedding |
					kControlSupportsFocus |
					kControlWantsIdle |
					kControlWantsActivate |
					kControlHandlesTracking |
					kControlSupportsDataAccess |
					//kControlHasSpecialBackground |
					kControlGetsFocusOnClick |
					kControlSupportsCalcBestRect |
					kControlSupportsLiveFeedback |
					kControlSupportsDragAndDrop |
					kControlSupportsGetRegion |
					//kControlSupportsFlattening |
					kControlSupportsSetCursor |
					kControlSupportsContextualMenus |
					kControlSupportsClickActivation |
					kControlIdlesWithTimer
					;
		err = SetEventParameter( inEvent, kEventParamControlFeatures, typeUInt32, sizeof(features), &features );
		break;
		
		case kEventControlDispose:
		BLDispose( list );
		SetControlDataHandle( control, nil );
		err = noErr;
		break;
		
		case kEventControlDraw:
		_BLUpdate( blUpdateBorder | blUpdateCorners, nil, list );
		err = noErr;
		break;
		
		case kEventControlGetPartRegion:
		{
			RgnHandle	rgn;
			
			(void)GetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode,
									nil, sizeof(part), nil, &part );
			(void)GetEventParameter( inEvent, kEventParamControlRegion, typeQDRgnHandle,
									nil, sizeof(rgn), nil, &rgn );
			//printf( "kEventControlGetPartRegion: part %d\n", part );
			if (part == kControlContentMetaPart ||
				part == kControlStructureMetaPart ||
				part == kControlListBoxPart)
			{
				//InsetRect( &listBounds, -3, -3 );
				if (bl_Test((*list)->privateFlags,blIsInCompositingWindow))
					OffsetRect( &listBounds, -listBounds.left, -listBounds.top );
				RectRgn( rgn, &listBounds );
			}
			err = noErr;
		}
		break;
		
		/*case kEventControlGetPartBounds:
		{
			(void)GetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode,
									nil, sizeof(part), nil, &part );
			printf( "kEventControlGetPartBounds: part %d\n", part );
			if (part == kControlContentMetaPart ||
				part == kControlStructureMetaPart ||
				part == kControlListBoxPart)
			{
				//InsetRect( &listBounds, -3, -3 );
				(void)SetEventParameter( inEvent, kEventParamControlPartBounds,
										typeQDRectangle, sizeof(listBounds), &listBounds );
			}
			err = noErr;
		}
		break;*/
		
		/* Sent when someone wants to find out what part of your control is
		* at a given point in local coordinates. You should set the
		* kEventParamControlPart parameter to the appropriate part. (Mac OS
		* X only) */
		case kEventControlHitTest:
		{
			//BLHitTestRec	test;
			
			(void)GetEventParameter( inEvent, kEventParamMouseLocation, typeQDPoint,
									nil, sizeof(where), nil, &where );
			//printf( "kEventControlHitTest: (%d, %d) ", where.h, where.v );
			if (bl_Test((*list)->privateFlags,blIsInCompositingWindow))
			{
				where.h += listBounds.left;
				where.v += listBounds.top;
			}
			if (PtInRect( where, &listBounds ))
			{
				/*Rect	scrollRect;
				
				if (bl_VScroll(list))
				{
					GetControlBounds( bl_VScroll(list), &scrollRect );
					if (PtInRect( where, &scrollRect )) part = blVScrollPart;
				}
				if (part == kControlNoPart && bl_HScroll(list))
				{
					GetControlBounds( bl_HScroll(list), &scrollRect );
					if (PtInRect(  where, &scrollRect )) part = blHScrollPart;
				}*/
				//printf( "Now (%d, %d), returned part %d coords (%d, %d)\n",
				//	where.h, where.v, test.part, test.cell.row, test.cell.col );
				//if (part == kControlNoPart)
				{
					part = kControlListBoxPart;
					err = noErr;
				}
				(void)SetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode,
										sizeof(part), &part );
			}
		}
		break;
		
		case kEventControlTrack:
		{
			ControlRef			hScroll, vScroll;
			EventModifiers		modifiers;
			
			(void)GetEventParameter( inEvent, kEventParamMouseLocation, typeQDPoint,
									nil, sizeof(where), nil, &where );
			if (bl_Test((*list)->privateFlags,blIsInCompositingWindow))
			{
				where.h += listBounds.left;
				where.v += listBounds.top;
			}
			//printf( "kEventControlTrack: {h: %d, v: %d}\n", where.h, where.v );
			(void)GetEventParameter( inEvent, kEventParamKeyModifiers, typeUInt32,
									nil, sizeof(modifiers), nil, &modifiers );
			vScroll = bl_VScroll(list);
			hScroll = bl_HScroll(list);
			// Set the scroll bar supervisors to nil so they can handle themselves normally in the call to BLClick.
			if (vScroll) (void)SetControlSupervisor( vScroll, nil );
			if (hScroll) (void)SetControlSupervisor( hScroll, nil );
			/*clickResult =*/ _BLClick( false, GetEventTime( inEvent ) / 60, where, modifiers, list );
			part = kControlListBoxPart;
			(void)SetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, sizeof(part), &part );
			if (vScroll) (void)SetControlSupervisor( vScroll, control );
			if (hScroll) (void)SetControlSupervisor( hScroll, control );
		}
		break;
		
		case kEventControlSetFocusPart:
		(void)GetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode,
									nil, sizeof(part), nil, &part );
		part = BLSetFocusedPart( part, list );
		(void)SetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, sizeof(part), &part );
		err = noErr;
		break;
		
		case kEventControlGetFocusPart:
		part = BLGetFocusedPart( list );
		(void)SetEventParameter( inEvent, kEventParamControlPart, typeControlPartCode, sizeof(part), &part );
		err = noErr;
		break;
		
		case kEventControlActivate:
		BLSetActive( true, list );
		err = noErr;
		break;
		
		case kEventControlDeactivate:
		BLSetActive( false, list );
		err = noErr;
		break;
		
		case kEventControlSetCursor:
		(void)GetEventParameter( inEvent, kEventParamMouseLocation, typeQDPoint,
									nil, sizeof(where), nil, &where );
		if (bl_Test((*list)->privateFlags,blIsInCompositingWindow))
		{
			where.h += listBounds.left;
			where.v += listBounds.top;
		}
		BLIdle( where, list );
		err = noErr;
		break;
		
		case kEventControlGetClickActivation:
		{
			//ClickActivationResult	result = kDoNotActivateAndHandleClick;
			ClickActivationResult	result = kActivateAndHandleClick;
			(void)SetEventParameter( inEvent, kEventParamClickActivation,
								typeClickActivationResult, sizeof(result), &result );
			err = noErr;
		}
		break;
	
		/* A drag has entered your control. If you at all wish to handle it,
		* you must return noErr from your event handler in response to this
		* event. If you return eventNotHandledErr, you will not receive
		* 'within' or 'leave' messages, nor will you be eligible to receive
		* the drag. This is done to try to be as efficient as possible wrt
		* sending events during the drag operation. */
		case kEventControlDragEnter:
		{
			Boolean			likesDrag = true;
			
			(void)GetEventParameter( inEvent, kEventParamDragRef, typeDragRef,
										nil, sizeof(drag), nil, &drag );
			err = BLTrackDrag( kDragTrackingEnterWindow, drag, list );
			if (!err) (void)SetEventParameter( inEvent, kEventParamControlLikesDrag,
								typeBoolean, sizeof(likesDrag), &likesDrag );
		}
		break;
		
		/* A drag has moved with your airspace (but not on any subcontrol).
		* If the user moves into a subcontrol, it becomes the target of the
		* drag and your control will not receive 'within' messages any
		* longer. */
		case kEventControlDragWithin:
		{
			(void)GetEventParameter( inEvent, kEventParamDragRef, typeDragRef,
										nil, sizeof(drag), nil, &drag );
			err = BLTrackDrag( kDragTrackingInWindow, drag, list );
		}
		break;

		/* A drag is leaving your view. You might want to do things like
		* unhighlight yourself, etc. */
		case kEventControlDragLeave:
		{
			(void)GetEventParameter( inEvent, kEventParamDragRef, typeDragRef,
										nil, sizeof(drag), nil, &drag );
			err = BLTrackDrag( kDragTrackingLeaveWindow, drag, list );
		}
		break;

		/* Congratulations, you are the lucky recipient of a drag. Enjoy it. */
		case kEventControlDragReceive:
		{
			(void)GetEventParameter( inEvent, kEventParamDragRef, typeDragRef,
										nil, sizeof(drag), nil, &drag );
			err = BLReceiveDrag( drag, list );
		}
		break;
	
		case kEventControlGetSizeConstraints:
		{
			UInt16	minW, minH;
			HISize	hiMin, hiMax;
			
			BLGetMinimumSize( &minW, &minH, list );
			hiMin.width = minW;
			hiMin.height = minH;
			hiMax.width = hiMax.height = 1000.0;
			(void)SetEventParameter( inEvent, kEventParamMinimumSize, typeHISize, sizeof(hiMin), &hiMin );
			(void)SetEventParameter( inEvent, kEventParamMaximumSize, typeHISize, sizeof(hiMax), &hiMax );
			err = noErr;
		}
		break;
		
		case kEventControlBoundsChanged:
		{
			Rect	newRect;
			
			//(void)GetEventParameter( inEvent, kEventParamCurrentBounds, typeQDRectangle,
			//							nil, sizeof(newRect), nil, &newRect );
			GetControlBounds( control, &newRect );
			//if (bl_Test((*list)->privateFlags,blIsInCompositingWindow))
			//	OffsetRect( &newRect, 0, listBounds.top );
			err = _BLSetRect( &newRect, list );
			err = noErr;
		}
		break;
		
		case kEventControlOwningWindowChanged:
		{
			WindowRef	newOwner;
			
			(void)GetEventParameter( inEvent, kEventParamControlCurrentOwningWindow, typeWindowRef,
										nil, sizeof(newOwner), nil, &newOwner );
			(*list)->window = newOwner;
		//#if TARGET_RT_MAC_MACHO
			GetWindowAttributes( newOwner, &features );
			if (bl_Test(features,kWindowCompositingAttribute))
				bl_Set((*list)->privateFlags,blIsInCompositingWindow);
		//#endif
			err = noErr;
		}
		break;
		
	//#if TARGET_RT_MAC_MACHO
		case kEventControlEnabledStateChanged:
		BLSetActive( IsControlActive( control ), list );
		err = noErr;
		break;
	//#endif
	}
	return err;
}

//===============================
pascal static OSStatus local_BLOtherEventsHandler( EventHandlerCallRef inCallRef, EventRef inEvent,
												void* inData )
{
	#pragma unused (inCallRef)
	OSStatus		err = eventNotHandledErr;
	UniChar			uniChar;
	UInt32			modifiers, kind, reply;
	ControlRef		control;
	BlugsRef		list;
	EventRef		originalEvent = nil;
	
	kind = GetEventKind( inEvent );
	control = inData;
	list = (BlugsRef)GetControlDataHandle( control );
	switch (GetEventClass( inEvent ))
	{
		case kEventClassTextInput:
		if (kind == kEventTextInputUnicodeForKeyEvent)
		{
			(void)GetEventParameter( inEvent, kEventParamTextInputSendText, typeUnicodeText,
										nil, sizeof(uniChar), nil, &uniChar );
			(void)GetEventParameter( inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef,
										nil, sizeof(originalEvent), nil, &originalEvent );
			(void)GetEventParameter( originalEvent, kEventParamKeyModifiers, typeUInt32,
										nil, sizeof(modifiers), nil, &modifiers );
			if (uniChar == kTabCharCode)
			{
				if (modifiers & shiftKey) ReverseKeyboardFocus( GetControlOwner( control ) );
				else AdvanceKeyboardFocus( GetControlOwner( control ) );
			}
			else BLKey( uniChar, GetEventTime( inEvent ) / 60.0, modifiers,  list );
			err = noErr;
		}
		break;
		
		case kEventClassCommand:
		switch (kind)
		{
			HICommandExtended	cmd;
			
			case kEventCommandUpdateStatus:
			modifiers = 0;
			(void)GetEventParameter( originalEvent, kEventParamDirectObject, typeHICommand,
										nil, sizeof(cmd), nil, &cmd );
			(void)GetEventParameter( originalEvent, kEventParamKeyModifiers, typeUInt32,
										nil, sizeof(modifiers), nil, &modifiers );
			reply = BLGetCommandStatus( cmd.commandID, modifiers, list );
			if (reply != blInlineEditCommandStatusIgnore)
			{
				if (cmd.attributes & kHICommandFromMenu)
				{
					if (reply == blInlineEditCommandStatusEnable)
						EnableMenuItem( cmd.source.menu.menuRef, cmd.source.menu.menuItemIndex );
					else DisableMenuItem( cmd.source.menu.menuRef, cmd.source.menu.menuItemIndex );
					err = noErr;
				}
			}
			break;
			
			case kEventCommandProcess:
			modifiers = 0;
			(void)GetEventParameter( originalEvent, kEventParamDirectObject, typeHICommand,
										nil, sizeof(cmd), nil, &cmd );
			(void)GetEventParameter( originalEvent, kEventParamKeyModifiers, typeUInt32,
										nil, sizeof(modifiers), nil, &modifiers );
			err = BLProcessCommand( cmd.commandID, modifiers, list );
			break;
		}
		break;
	}
	return err;
}


#endif //__BL_USE_CARBON_API__