#include "BlugsPrivate.h"
#if !TARGET_RT_MAC_MACHO
#include <fp.h>
#endif


static void		local_VAReheap16( UInt16* arrayP, UInt32 left, UInt32 right,
										VASortProcPtr proc, UInt32 refCon );
static void		local_VAReheapOther( Ptr arrayP, UInt32 itemSize, UInt32 left, UInt32 right,
										VASortProcPtr proc, UInt32 refCon, Ptr temp );
static void		local_VAHeapsort16( UInt16* arrayP, UInt32 nItems, VASortProcPtr proc,
										UInt32 refCon);
static void		local_VAHeapsortOther( Ptr arrayP, UInt32 nItems, UInt32 itemSize,
										VASortProcPtr proc, UInt32 refCon);
#if __BL_USE_CARBON_API__
static OSStatus	local_BLLoadFrameworkBundle( CFStringRef framework, CFBundleRef *bundlePtr );
#endif


#pragma mark ¥¥¥Memory¥¥¥
#if !TARGET_RT_MAC_MACHO
#if !TARGET_API_MAC_CARBON
#if defined(__MWERKS__)
static const double gZeroDouble = 0.0;

//===============================
// p is in r3, bytes in r4
// I now use r6 to hold result of ANDing destination address; no longer have to worry about
//	clobbering and reloading r0.
asm void _BLAsmZeroMemory( register Ptr inData, register UInt32 inBytes )
{
	#pragma unused (inData,inBytes)
	cmplwi		r3,0			// Check for nil pointer
	beqlr						// Return if equal (to nil)
	li			r0,0			// Load zero value
	cmplwi		r4,16			// If less than 16 bytes to copy...
	ble			@SetupCopyByte	//	copy byte by bye at end
@AlignByte
	rlwinm.		r6,r3,0,31,31	// Check bit 31
	beq			@AlignShort		// If bit 31 is clear, try to align to short
	stb			r0,0(r3)		// Store zero byte to ptr
	addi		r3,r3,1			// Increment ptr
	subi		r4,r4,1			// Decrement bytes to copy
@AlignShort
	rlwinm.		r6,r3,0,30,30	// Check bit 30
	beq			@AlignLong		// If bit 30 is clear, try to align to long
	sth			r0,0(r3)		// Store zero short to ptr
	addi		r3,r3,2			// Increment ptr
	subi		r4,r4,2			// Decrement bytes to copy
@AlignLong
	rlwinm.		r6,r3,0,29,29	// Check bit 29
	beq			@SetupCopyDouble// If bit 29 is clear, try to align to double
	stw			r0,0(r3)		// Store zero long to ptr
	addi		r3,r3,4			// Increment ptr
	subi		r4,r4,4			// Decrement bytes to copy
@SetupCopyDouble
	lfd			fp0,gZeroDouble	// Set fp register 0 to zero value. There is no
								//	floating-point load immediate instruction (too bad) so we
								//	have to use a global.
	subi		r3,r3,8			// Back up so store double with update instruction (stfdu)
								//	can add 8 to ptr to get store location
@CheckDouble
	cmplwi		r4,8			// At least 8 bytes left to copy?
	blt			@DoneWithDouble	// Clean up if not
@ZeroDouble
	stfdu		fp0,8(r3)		// Store double to memory offset by immediate value
								//	(in this case, 8) and increment r3 by that amount
	subi		r4,r4,8			// Decrement bytes to copy
	b			@CheckDouble	// Loop over double-storing code
@DoneWithDouble
	addi		r3,r3,8			// Reset r3 to point to next byte to be zeroed
@SetupCopyByte
	subi		r3,r3,1			// Do the same trick as with doubles, except we
								//	only 'autoincrement' by 1
@CheckByte
	cmplwi		r4,0			// Zero bytes left to copy?
	beqlr						// Return if so
@CopyByte
	stbu		r0,1(r3)		// Store byte to memory offset by the immediate value (1)
								//	and autoincrement by 1. This is like saying *(++p) = 0;
	subi		r4,r4,1			// Decrement bytes left
	b			@CheckByte		// Loop over byte-storing (exit condition is there)
}
#endif // target PPC/68K/MrC/Yada yada yada
#endif // not Carbon
#endif // Mach-O

#pragma mark ¥¥¥Idle Events¥¥¥
#if __BL_USE_CARBON_API__
//===============================
pascal void _BLTimerProc( EventLoopTimerRef inTimer, void *inUserData )
{
	#pragma unused (inTimer)
	if (!bl_Test((*(BlugsRef)inUserData)->privateFlags,blSuppressIdle))
	{
		Point	mouse;
		
		GetMouse( &mouse );
		BLIdle( mouse, inUserData );
	}
}
#endif
//===============================
// If there's an edit session, that should take priority over any handlers which (for whatever
//	twisted reasons of their own) want idle events.
BL_PROC void BLIdle( Point inMouse, BlugsRef inList )
{
	OSErr		err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		// No idle activity if list is hidden or inactive.
		if (bl_Active(inList) && bl_Visible(inList))
		{
			GWorldPtr				saveWorld;
			Boolean					restore;
			Rect					viewRect;
			BLHandlerParamBlock		pb;
			UInt32					response;
			Boolean					adjustedCursor = false;
			BLHitTestRec			test;
			//SInt16					helpIndex = 0;
			CursHandle				curs = nil;
			
			viewRect = _BLViewRect( inList );
			restore = _BLGotoWindow( &saveWorld, inList );
			if (bl_InlineNow(inList))
			{
				BLCell		inlineCell;
				
				err = BLGetCellFromUID( &bl_InlineCell(inList), &inlineCell, inList );
				if (!err) err = _BLCellRect( inlineCell, &pb.inRect, inList );
				err = _BLCellRect( inlineCell, &pb.inRect, inList );
				if (!err)
				{
					RgnHandle	saveClip;
					
					pb.inIndent = _BLIndent( inlineCell, inList );
					pb.inEventWhereLocal = inMouse;
					saveClip = _BLClipToRectIntersection( &pb.inRect, &viewRect );
					if (saveClip)
					{
						response = _BLCallHandlerForCell( blCellIdleMsg, inlineCell, &pb, inList );
						if (bl_Test(response,blCellIdleAdjustedCursor)) adjustedCursor = true;
						SetClip( saveClip );
						DisposeRgn( saveClip );
					}
				}
			}
			//hitTest.cellRgn = nil; // _BLHitTest will thus not return blCellRegionPart
			(void)BLHitTest( inMouse, &test, inList );
			
			// Allow cells, titles, and top left to get idle events if their handlers permit
			if (test.part == blCellPart || test.part == blHTitleBarTitlePart ||
				test.part == blVTitleBarTitlePart || test.part == blTopLeftPart)
			{
				UInt32			features = 0;
				UInt16			nthTitle;
				BLTitleBarHdl	theBar = nil;
				
				if (test.part == blCellPart)
					err = _BLGetCellHandlerFeatures( test.cell, &features, inList );
				else
				{
					BLContentType	type;
					
					if (_BLCellToTitle( test.cell, &nthTitle, &theBar, inList))
					{
						type = _BLGetTitleContentType( nthTitle, theBar );
						if (type) err = _BLGetHandlerFeatures( type, &features );
					}
				}
				if (!err && bl_Test(features,blWantsIdle))
				{
					pb.inRect = test.rect;
					pb.inIndent = _BLIndent( test.cell, inList );
					pb.inEventWhereLocal = inMouse;
					response = _BLCallHandlerForCell( blCellIdleMsg, test.cell, &pb, inList );
					if (bl_Test(response,blCellIdleAdjustedCursor)) adjustedCursor = true;
					if (bl_Test(response,blCellIdleNeedUpdate))
					{
						if (test.part == blCellPart) _BLUpdateCell( blUpdateBlit, test.cell, inList );
						else _BLUpdateTitle( nthTitle, theBar, inList );
					}
				}
			}
			
			// Do cursor adjustment if necessary
			if (!adjustedCursor && test.part != blNoPart)
			{
				UInt16			nRows, nCols;
				
				nRows = bl_NRows(inList);
				nCols = bl_NCols(inList);
				switch (test.part)
				{
					case blHTitleBarTitlePart:
					case blHTitleBarFillerPart:
					if (test.u.zone == blZoneBottom) curs = _blGlobals.rowResizeCursHdl;
					else if ((test.u.zone == blZoneRight || test.u.zone == blZoneLeft) && nCols)
					{
						curs = _blGlobals.colResizeCursHdl;
					}
					break;
					
					case blVTitleBarTitlePart:
					case blVTitleBarFillerPart:
					if (test.u.zone == blZoneRight) curs = _blGlobals.colResizeCursHdl;
					else if ((test.u.zone == blZoneTop || test.u.zone == blZoneBottom) && nRows)
					{
						curs = _blGlobals.rowResizeCursHdl;
					}
					break;
				}
			}
			if (!curs)
			{
				if (!adjustedCursor)
				{
					Rect	listRect;
					
					listRect = bl_Rect(inList);
					if (PtInRect(inMouse,&listRect))
					{
						// I'd use theme cursors for everything, but there is no
						//	kThemeResizeUpDownCursor so we'd risk inconsistency
						//	not to mention extra special case code.
						if (bl_AllegroAppearance) err = SetThemeCursor( kThemeArrowCursor );
						else err = unimpErr;
						if (err) InitCursor();
					}
				}
			}
			else SetCursor( *curs );
			_BLWaitingForInlineEdit( inList );
			if (restore) _BLSwapPort( saveWorld, nil );
		} // list is visible and active
	} // valid list
}

//===============================
#if __BL_USE_CARBON_API__
typedef pascal OSStatus (*TrackMouseLocationProcPtr)( GrafPtr inPort, Point* outPt,
													MouseTrackingResult* outResult );
#endif

#if !TARGET_RT_MAC_MACHO
//===============================
void _BLTrackMouseLocation( Point* ioMouse, MouseTrackingResult* outResult )
{
#if TARGET_API_MAC_CARBON
	OSErr								err = cfragNoSymbolErr;
	static TrackMouseLocationProcPtr	proc = nil;
	
	if (proc != (TrackMouseLocationProcPtr)0xDEADBEEF)
	{
		if (!proc)
		{
			CFragConnectionID	connID;
			Ptr					mainSym;
			Str255				errorMsg;
			
			err = GetSharedLibrary( "\pCarbonLib", kPowerPCCFragArch, kLoadCFrag, &connID, &mainSym, errorMsg );
			if (!err)
			{
				// See if TrackMouseLocation is in CarbonLib.
				err = FindSymbol( connID, "\pTrackMouseLocation", (Ptr*)&proc, nil );
				if (err) proc = (TrackMouseLocationProcPtr)0xDEADBEEF;
			}
		}
	}
	if (!err) (*proc)( nil, ioMouse, outResult );
	else
#endif
	{
		Point					oldMouse;
		MouseTrackingResult		result = kMouseTrackingMousePressed;
		
		oldMouse = *ioMouse;
		GetMouse( ioMouse );
		if (!StillDown()) result = kMouseTrackingMouseReleased;
		else if (ioMouse->h != oldMouse.h || ioMouse->v != oldMouse.v) result = kMouseTrackingMouseMoved;
		*outResult = result;
	}
}
#endif


#pragma mark ¥¥¥Registering User Procs¥¥¥
//===============================
BL_PROC OSErr BLGetCallbacks( BLCallbacksPtr outCallbacks, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) BlockMoveData( (BLCallbacksPtr)&(*inList)->callbacks, outCallbacks,
							sizeof(BLCallbacksRec) );
	else _BLZeroMemory( (Ptr)outCallbacks, sizeof(BLCallbacksPtr) );
	return err;
}

//===============================
BL_PROC OSErr BLSetCallbacks( const BLCallbacksPtr inCallbacks, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		// 11/6/2001 Moses -->1.1
		// nil callbacks pointer means zero all callbacks.
		if (inCallbacks) BlockMoveData( inCallbacks, (BLCallbacksPtr)&(*inList)->callbacks,
										sizeof(BLCallbacksRec) );
		else _BLZeroMemory( (Ptr)&(*inList)->callbacks, sizeof(BLCallbacksRec) );
		_BLUpdateWorld( blUpdateBlit | blUpdateCorners, nil, inList );
	}
	return err;
}

//===============================
BL_PROC OSErr BLRegisterBackgroundProc( BLBackgroundProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		(*inList)->callbacks.backgroundProc = inProc;
		_BLUpdateWorld( blUpdateBlit, nil, inList );
	}
	return err;
}

//===============================
BL_PROC OSErr BLRegisterBevelButtonProc( BLBevelButtonProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		(*inList)->callbacks.bevelButtonProc = inProc;
		_BLUpdateWorld( blUpdateBlit | blUpdateCorners, nil, inList );
	}
	return err;
}

//===============================
BL_PROC OSErr BLRegisterBorderProc( BLBorderProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		(*inList)->callbacks.borderProc = inProc;
		_BLUpdateWorld( blUpdateBlit, nil, inList );
	}
	return err;
}

//===============================
BL_PROC OSErr BLRegisterHiliteProc( BLHiliteProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		(*inList)->callbacks.hiliteProc = inProc;
		_BLUpdateWorld( blUpdateBlit, nil, inList );
	}
	return err;
}

//===============================
BL_PROC OSErr BLRegisterRowExpandProc( BLRowExpandProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	
	if (!err)
	{
		_AssertStr(bl_Disclosure(inList),"Notice: this routine is unnecessary for non-disclosure lists.");
		(*inList)->callbacks.rowExpandProc = inProc;
	}
	return err;
}

//===============================
BL_PROC OSErr BLRegisterSecondarySortColumnProc( BLSecondarySortColumnProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		_AssertStr(bl_Sortable(inList),"Notice: this routine is unnecessary for unsortable lists.");
		(*inList)->callbacks.secondaryProc = inProc;
		// Should we sort the list in response?
	}
	return err;
}

//===============================
BL_PROC OSErr BLRegisterPreDragProc( BLPreDragProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) (*inList)->callbacks.preDragProc = inProc;
	return err;
}

//===============================
BL_PROC OSErr BLRegisterDragDataProc( BLDragDataProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) (*inList)->callbacks.dragDataProc = inProc;
	return err;
}

//===============================
BL_PROC OSErr BLRegisterDropValidationProc( BLDropValidationProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) (*inList)->callbacks.dropValidationProc = inProc;
	return err;
}

//===============================
BL_PROC OSErr BLRegisterDropProc( BLDropProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) (*inList)->callbacks.dropProc = inProc;
	return err;
}

//===============================
BL_PROC OSErr BLRegisterPostDragProc( BLPostDragProcPtr inProc, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) (*inList)->callbacks.postDragProc = inProc;
	return err;
}


#pragma mark ¥¥¥Utility¥¥¥
#if __BL_USE_CARBON_API__

#if TARGET_RT_MAC_MACHO
#define bl_printf		printf
#else
typedef int (*printfPtr)(const char *format, ...);
#define bl_printf		(*p)
#endif
//===============================
BL_PROC void BLDumpList( const char* inName, BlugsRef inList )
{
	char				typeStr[5];
	BLTitleBarRef		bar;
	
// We only have to get access to the BSD function if not Mach-O
#if !TARGET_RT_MAC_MACHO
	static printfPtr	p = nil;
	
	if (!p) p = _BLGetBSDFunctionPtr( CFSTR("printf") );
	if (p)
#endif
	{
		typeStr[4] = 0;
		bl_printf( "===================================\n" );
		bl_printf( "%s\n", inName );
		if ((*inList)->listFlags == 0) bl_printf( "List flags: (none)\n" );
		else bl_printf( "List flags:\n" );
		if (bl_Test((*inList)->listFlags,blResizableHeight)) bl_printf( "  blResizableHeight\n" );
		if (bl_Test((*inList)->listFlags,blResizableWidth)) bl_printf( "  blResizableWidth\n" );
		if (bl_Test((*inList)->listFlags,blHorizontalScroll)) bl_printf( "  blHorizontalScroll\n" );
		if (bl_Test((*inList)->listFlags,blVerticalScroll)) bl_printf( "  blVerticalScroll\n" );
		if (bl_Test((*inList)->listFlags,blLiveScroll)) bl_printf( "  blLiveScroll\n" );
		if (bl_Test((*inList)->listFlags,blSmallScroll)) bl_printf( "  blSmallScroll\n" );
		if (bl_Test((*inList)->listFlags,blAutodraw)) bl_printf( "  blAutodraw\n" );
		if (bl_Test((*inList)->listFlags,blHasGrow)) bl_printf( "  blHasGrow\n" );
		if (bl_Test((*inList)->listFlags,blDrawGrow)) bl_printf( "  blDrawGrow\n" );
		if (bl_Test((*inList)->listFlags,blCanFocus)) bl_printf( "  blCanFocus\n" );
		if (bl_Test((*inList)->listFlags,blVisible)) bl_printf( "  blVisible\n" );
		if (bl_Test((*inList)->listFlags,blActive)) bl_printf( "  blActive\n" );
		if (bl_Test((*inList)->listFlags,blInlineEditOnClick)) bl_printf( "  blInlineEditOnClick\n" );
		if (bl_Test((*inList)->listFlags,blDisclosure)) bl_printf( "  blDisclosure\n" );
		if (bl_Test((*inList)->listFlags,blBorderMetrics)) bl_printf( "  blBorderMetrics\n" );
		if (bl_Test((*inList)->listFlags,blDrawBorder)) bl_printf( "  blDrawBorder\n" );
		if (bl_Test((*inList)->listFlags,blSortable)) bl_printf( "  blSortable\n" );
		if (bl_Test((*inList)->listFlags,blDrawSortButton)) bl_printf( "  blDrawSortButton\n" );
		if (bl_Test((*inList)->listFlags,blTable)) bl_printf( "  blTable\n" );
		if (bl_Test((*inList)->listFlags,blOnlyOne)) bl_printf( "  blOnlyOne\n" );
		if (bl_Test((*inList)->listFlags,blUseSense)) bl_printf( "  blUseSense\n" );
		if (bl_Test((*inList)->listFlags,blNoExtend)) bl_printf( "  blNoExtend\n" );
		if (bl_Test((*inList)->listFlags,blNoDisjoint)) bl_printf( "  blNoDisjoint\n" );
		if (bl_Test((*inList)->listFlags,blPinRight)) bl_printf( "  blPinRight\n" );
		if ((*inList)->privateFlags == 0) bl_printf( "Private flags: (none)\n" );
		else bl_printf( "Private flags:\n" );
		if (bl_Test((*inList)->privateFlags,blInlineEditNow)) bl_printf( "  blInlineEditNow\n" );
		if (bl_Test((*inList)->privateFlags,blDraggingHorzTitle)) bl_printf( "  blDraggingHorzTitle\n" );
		if (bl_Test((*inList)->privateFlags,blDraggingVertTitle)) bl_printf( "  blDraggingVertTitle\n" );
		if (bl_Test((*inList)->privateFlags,blDraggingContent)) bl_printf( "  blDraggingContent\n" );
		if (bl_Test((*inList)->privateFlags,blFocused)) bl_printf( "  blFocused\n" );
		if (bl_Test((*inList)->privateFlags,blHaveDoubleClick)) bl_printf( "  blHaveDoubleClick\n" );
		if (bl_Test((*inList)->privateFlags,blPendingInline)) bl_printf( "  blPendingInline\n" );
		if (bl_Test((*inList)->privateFlags,blNeedUpdate)) bl_printf( "  blNeedUpdate\n" );
		if (bl_Test((*inList)->privateFlags,blHaveDrawnInsertionCaret)) bl_printf( "  blHaveDrawnInsertionCaret\n" );
		if (bl_Test((*inList)->privateFlags,blHaveDrawnDragHilite)) bl_printf( "  blHaveDrawnDragHilite\n" );
		if (bl_Test((*inList)->privateFlags,blDraggingTitle)) bl_printf( "  blDraggingTitle\n" );
		if (bl_Test((*inList)->privateFlags,blNeedFocusErase)) bl_printf( "  blNeedFocusErase\n" );
		bl_printf( " Rectangle: width %d to %d, height %d to %d\n", bl_Rect(inList).left,
				bl_Rect(inList).right, bl_Rect(inList).top, bl_Rect(inList).bottom );
		bl_printf( " %d rows, %d columns\n", bl_NRows(inList), bl_NCols(inList) );
		bl_printf( " Visible cells: row %d to %d, column %d to %d\n", (*inList)->visCells.left,
			(*inList)->visCells.right, (*inList)->visCells.top, (*inList)->visCells.bottom );
		bl_printf( " Last click time %d, last key time %d\n", (*inList)->lastClickTime, (*inList)->lastKeyTime );
		bl_printf( " Pending inline time %d\n", (*inList)->pendingInlineTime );
		bl_printf( " Horizontal scroll location %d, vertical scroll location %d\n", (*inList)->hScrollLoc, (*inList)->vScrollLoc );
		bl_printf( " Total height %d, total width %d\n", (*inList)->totalHeight, (*inList)->totalWidth );
		bl_printf( " Next row UID %d%d\n", (*inList)->nextRowID.hi, (*inList)->nextRowID.lo );
		bl_printf( " Next column UID %d%d\n", (*inList)->nextColID.hi, (*inList)->nextColID.lo );
		bl_printf( " Representative column %d%d\n", (*inList)->rep.hi, (*inList)->rep.lo );
		if ((*inList)->userPane)
		{
			UInt32	features = 0;
			
			(void)GetControlFeatures( (*inList)->userPane, &features );
			bl_printf( "Control features:\n" );
			if (bl_Test(features,kControlSupportsGhosting)) bl_printf( "  kControlSupportsGhosting\n" );
			if (bl_Test(features,kControlSupportsEmbedding)) bl_printf( "  kControlSupportsEmbedding\n" );
			if (bl_Test(features,kControlSupportsFocus)) bl_printf( "  kControlSupportsFocus\n" );
			if (bl_Test(features,kControlWantsIdle)) bl_printf( "  kControlWantsIdle\n" );
			if (bl_Test(features,kControlWantsActivate)) bl_printf( "  kControlWantsActivate\n" );
			if (bl_Test(features,kControlHandlesTracking)) bl_printf( "  kControlHandlesTracking\n" );
			if (bl_Test(features,kControlSupportsDataAccess)) bl_printf( "  kControlSupportsDataAccess\n" );
			if (bl_Test(features,kControlHasSpecialBackground)) bl_printf( "  kControlHasSpecialBackground\n" );
			if (bl_Test(features,kControlGetsFocusOnClick)) bl_printf( "  kControlGetsFocusOnClick\n" );
			if (bl_Test(features,kControlSupportsCalcBestRect)) bl_printf( "  kControlSupportsCalcBestRect\n" );
			if (bl_Test(features,kControlSupportsLiveFeedback)) bl_printf( "  kControlSupportsLiveFeedback\n" );
			if (bl_Test(features,kControlHasRadioBehavior)) bl_printf( "  kControlHasRadioBehavior\n" );
			if (bl_Test(features,kControlSupportsDragAndDrop)) bl_printf( "  kControlSupportsDragAndDrop\n" );
			if (bl_Test(features,kControlAutoToggles)) bl_printf( "  kControlAutoToggles\n" );
			if (bl_Test(features,kControlSupportsGetRegion)) bl_printf( "  kControlSupportsGetRegion\n" );
			if (bl_Test(features,kControlSupportsFlattening)) bl_printf( "  kControlSupportsFlattening\n" );
			if (bl_Test(features,kControlSupportsSetCursor)) bl_printf( "  kControlSupportsSetCursor\n" );
			if (bl_Test(features,kControlSupportsContextualMenus)) bl_printf( "  kControlSupportsContextualMenus\n" );
			if (bl_Test(features,kControlSupportsClickActivation)) bl_printf( "  kControlSupportsClickActivation\n" );
			if (bl_Test(features,kControlIdlesWithTimer)) bl_printf( "  kControlIdlesWithTimer\n" );
		}
		bar = bl_HTitle(inList);
		if (bar)
		{
			bl_printf( " Horizontal title bar info:\n" );
			bl_printf( "    top left storage: %08X\n", (*bar)->topLeftStorage );
			bl_printf( "    top left content: %d\n", (*bar)->topLeftContent );
			bl_printf( "    default content type: %d\n", (*bar)->defaultContentType );
			bl_printf( "    thickness: %d\n", (*bar)->thickness );
			bl_printf( "    which selected: %d, %d\n", (*bar)->whichSelected.hi, (*bar)->whichSelected.lo );
			if ((*bar)->flags == 0) bl_printf( "    title bar flags: (none)\n" );
			else bl_printf( "    title bar flags: %s %s\n",
						(bl_Test((*bar)->flags,blTitleBarResizableThickness)) ? "blTitleBarResizableThickness" : "",
						(bl_Test((*bar)->flags,blTitleBarReorderable)) ? "blTitleBarReorderable" : "" );
			if ((*bar)->defaultTitleFlags == 0) bl_printf( "    default title flags: (none)\n" );
			else bl_printf( "    default title flags: %s\n",
						(bl_Test((*bar)->defaultTitleFlags,blTitleSelectable)) ? "blTitleSelectable" : "" );
		}
		bar = bl_VTitle(inList);
		if (bar)
		{
			bl_printf( " Vertical title bar info:\n" );
			bl_printf( "    top left storage: %08X\n", (*bar)->topLeftStorage );
			bl_printf( "    top left content: %d\n", (*bar)->topLeftContent );
			bl_printf( "    default content type: %d\n", (*bar)->defaultContentType );
			bl_printf( "    thickness: %d\n", (*bar)->thickness );
			bl_printf( "    which selected: %d, %d\n", (*bar)->whichSelected.hi, (*bar)->whichSelected.lo );
			if ((*bar)->flags == 0) bl_printf( "    title bar flags: (none)\n" );
			else bl_printf( "    title bar flags: %s %s\n",
						(bl_Test((*bar)->flags,blTitleBarResizableThickness)) ? "blTitleBarResizableThickness" : "",
						(bl_Test((*bar)->flags,blTitleBarReorderable)) ? "blTitleBarReorderable" : "" );
			if ((*bar)->defaultTitleFlags == 0) bl_printf( "    default title flags: (none)\n" );
			else bl_printf( "    default title flags: %s\n",
						(bl_Test((*bar)->defaultTitleFlags,blTitleSelectable)) ? "blTitleSelectable" : "" );
		}
	}
}
#endif

//===============================
UInt32 _BLProcessFlags( UInt32 inWhichFlags, UInt32 inFlags, UInt32 inOldFlags )
{
	inOldFlags &= (~((inFlags ^ inWhichFlags) & inWhichFlags));
	inOldFlags |= (inFlags & inWhichFlags);
	return inOldFlags;
}

//===============================
UInt32 _BLCellToNthCell( BLCell inCell, BlugsRef inList )
{
	UInt32		n = 0;
	
	if (bl_ValidCell(inCell))
	{
		UInt16		nCols;
		nCols = bl_NCols(inList);
		if (nCols)
		{
			n = nCols * (inCell.row - 1);
			n += inCell.col;
		}
	}
	return n;
}

//===============================
BLCell _BLNthCellToCell( UInt32 inNthCell, BlugsRef inList )
{
	BLCell		cell = {0,0};
	
	if (inNthCell)
	{
		UInt16		nCols;
		
		nCols = bl_NCols(inList);
		if (nCols)
		{
			cell.col = inNthCell % nCols;
			if (!cell.col) cell.col = nCols;
			cell.row = ((inNthCell - cell.col) / nCols) + 1;
		}
	}
	return cell;
}

//===============================
Boolean _BLCellToTitle( BLCell inCell, UInt16* outTitle, BLTitleBarHdl* outBar, BlugsRef inList )
{
	Boolean	isTitle = false;
	// Try VTitle first, because we want to return that if it's the top left corner.
	if (!inCell.col)
	{
		*outTitle = inCell.row;
		*outBar = bl_VTitle(inList);
		isTitle = true;
	}
	else if (!inCell.row)
	{
		*outTitle = inCell.col;
		*outBar = bl_HTitle(inList);
		isTitle = true;
	}
	return isTitle;
}

#if !__BL_USE_CARBON_API__
#if defined(__MWERKS__)
//===============================
asm Boolean _BLAsmPtInRect( register Point inPoint, register const Rect* inRect )
{
	#pragma unused (inPoint,inRect)
	//lwz		r3,0(r3)	// Load entire point into r3
	lwz		r0,0(r4)	// Load top left into r0
	srawi	r5,r3,0x10	// p->v to r5 (hi half word of r3 sign-extended)
	srawi	r6,r0,0x10	// rect->top to r6 (hi half word of r0 sign-extended)
	cmpw	r5,r6		// Compare p->v with rect->top
	blt		_No			// Return false if v < top
	extsh	r3,r3		// Sign-extend p->h
	extsh	r0,r0		// Sign-extend rect->left
	cmpw	r3,r0		// Compare p->h with rect->left
	blt		_No			// Return false if h < left
	lwz		r4,4(r4)	// Load bottom right into r4
	srawi	r6,r4,0x10	// rect->bottom to r6 (hi half word of r4 sign-extended)
	cmpw	r5,r6		// Compare p->v with rect->bottom
	bge		_No			// Return false if v >= bottom
	extsh	r4,r4		// Sign-extend rect->right
	cmpw	r3,r4		// Compare p->h with rect->right
	bge		_No			// Return false if h >= right
	li		r3,1		// Load true return value
	blr
_No:
	li		r3,0		// Load false return value
	blr
}

//===============================
// Executes in about 40% of the time the OS version takes.
asm Boolean _BLAsmEmptyRect( register const Rect* inRect )
{
	#pragma unused (inRect)
	lha		r4,0(r3)	// Left into r4
	lha		r5,4(r3)	// Right into r5
	cmpw	r4,r5		// Compare left with right
	bge		_Yes		// Return true if left is greater than or equal to right
	lha		r4,2(r3)	// Top into r4
	lha		r5,6(r3)	// Bottom into r5
	cmpw	r4,r5		// Compare top with bottom
	bge		_Yes		// Return true if top is greater than or equal to bottom
	li		r3,0		// Load false return value
	blr
_Yes:
	li		r3,1		// Load true return value
	blr
}
#endif


//===============================
OSStatus _BLInvalWindowRect( WindowRef inWindow, const Rect* inRect )
{
	GWorldPtr	saveWorld;
	Boolean		restore;
	
	restore = _BLSwapPort( GetWindowPort( inWindow ), &saveWorld );
	InvalRect( inRect );
	if (restore) SetGWorld( saveWorld, nil );
	return noErr;
}

//===============================
Rect* _BLGetPortBounds( CGrafPtr inPort, Rect* outBounds )
{
	*outBounds = inPort->portRect;
	return outBounds;
}

//===============================
Rect* _BLGetRegionBounds( const RgnHandle inRgn, Rect *outBounds )
{
	*outBounds = (*inRgn)->rgnBBox;
	return outBounds;
}

#endif //!__BL_USE_CARBON_API__


//===============================
// If the two rectangles overlap, saves the current clip to a new region,
//	clips to the intersection, and returns the region.
// If no overlap, does not save or modify the current clip, and returns nil.
RgnHandle _BLClipToRectIntersection( const Rect* inRect1, const Rect* inRect2 )
{
	Rect		intersection;
	RgnHandle	rgn;
	
	if (SectRect( inRect1, inRect2, &intersection ))
	{
		rgn = NewRgn();
		if (rgn)
		{
			GetClip( rgn );
			ClipRect( &intersection );
		}
	}
	else rgn = nil;
	return rgn;
}

//===============================
// Call this before calling content handler with an inline edit message,
//	or any time it is necessary to draw entirely within the viewRect.
// Clips to intersection of cell rect and view rect. Returns old clip region or nil
//	if no intersection.
// If non-nil region is returned, call SetClip when done, then dispose of region.
RgnHandle _BLClipToViewRectIntersection( const Rect* inRect, BlugsRef inList )
{
	RgnHandle	saveClip;
	Rect		viewRect;
	
	viewRect = _BLViewRect( inList );
	saveClip = _BLClipToRectIntersection( inRect, &viewRect );
	return saveClip;
}

//===============================
void _BLNewNumberOnMovement( UInt16* ioNumber, UInt16 inCount, UInt16 inMoveThis, UInt16 inHere )
{
	UInt16	number;
	
	number = *ioNumber;
	// See if number is one of the entries moved.
	if (number >= inMoveThis && number < inMoveThis + inCount)
	{
		number = inHere + number - inMoveThis + 1;
		if (inMoveThis <= inHere) number -= inCount;
	}
	else if (number > inMoveThis && number <= inHere) number -= inCount;
	else if (number < inMoveThis && number > inHere) number += inCount;
	*ioNumber = number;
}

//===============================
void _BLRgnMinusRect( RgnHandle inRgn, const Rect* inRect )
{
	static RgnHandle	rgn = nil;
	
	if (!rgn) rgn = NewRgn();
	RectRgn( rgn, inRect );
	DiffRgn( inRgn, rgn, inRgn );
}

//===============================
void _BLIncrUID( BLUID* ioID )
{
	if (ioID->lo == 0xFFFFFFFF) (ioID->hi)++;
	else (ioID->lo)++;
}

//===============================
Boolean _BLEqualUID( BLUID* inID1, BLUID* inID2 )
{
	Boolean	equal = false;
	
	if (inID1->hi == inID2->hi) if (inID1->lo == inID2->lo) equal = true;
	return equal;
}

//===============================
Boolean _BLEqualCellUID( BLCellUID* inID1, BLCellUID* inID2 )
{
	Boolean	equal = false;
	
	if (_BLEqualUID( &inID1->rowID, &inID2->rowID ))
		if (_BLEqualUID( &inID1->colID, &inID2->colID )) equal = true;
	return equal;
}


#pragma mark ¥¥¥Parameter Validation¥¥¥
//===============================
// Returns:
//	notInitErr (-900) Blugs is not initialized.
//	nilHandleErr(-109) Bad list reference.
OSErr _BLValidateList( BlugsRef inList )
{
	OSErr	err;
	
	_AssertStr(bl_Inited,"Error: Blugs is not initialized.");
	if (!bl_Inited) err = notInitErr; // -900
	else
	{
		_AssertStr( inList != nil, "Error: nil list." );
		if (!inList) err = nilHandleErr;
		else
		{
			
		#if __BL_USE_CARBON_API__
			if (!IsHandleValid( (Handle)inList ))
			{
				_AssertStr(false,"Error: invalid list reference (IsHandleValid returned false).");
				err = nilHandleErr;
			}
		#else
			_AssertStr( *inList != nil, "Error: nil list." );
			if (!*inList) err = nilHandleErr;
		#endif
		
			else err = noErr;
		}
	}
	return err;
}

//===============================
// Returns:
//	notInitErr (-900) Blugs is not initialized.
//	nilHandleErr(-109) Bad list reference.
OSErr _BLValidateTitleBar( BLTitleBarHdl inBar )
{
	OSErr	err;
	
	if (!bl_Inited)
	{
		_AssertStr(false,"Error: Blugs is not initialized.");
		err = notInitErr; // -900
	}
	else
	{
		if (!inBar)
		{
			_AssertStr(false,"Error: nil title bar.");
			err = nilHandleErr;
		}
		else
		{
		
		#if __BL_USE_CARBON_API__
			if (!IsHandleValid( (Handle)inBar ))
			{
				_AssertStr(false,"Error: invalid title bar reference (IsHandleValid returned false)." );
				err = nilHandleErr;
			}
		#else
			if (!*inBar)
			{
				_AssertStr(false,"Error: nil title bar.");
				err = nilHandleErr;
			}
		#endif
		
			else err = noErr;
		}
	}
	return err;
}

//===============================
// If inCheckZero, make sure there are both title bars if passed 0.
// Otherwise ignore zero input. (Like from BLSelectTitle: 0 is OK there)
OSErr _BLValidateTitle( Boolean inCheckZero, UInt16 inTitle, BLTitleBarHdl inBar, BlugsRef inList )
{
	UInt16	nTitles;
	OSErr	err = noErr;
	
	if (bl_BarVert(inBar)) nTitles = bl_NRows(inList);
	else nTitles = bl_NCols(inList);
	if (inTitle > nTitles)
	{
		_AssertStr(false,"Error: title does not exist.");
		err = inputOutOfBounds;
	}
	else
	{
		if (inCheckZero && !inTitle)
		{
			if (!bl_HasHTitle(inList) || !bl_HasVTitle(inList))
			{
				_AssertStr(false,"Error: title #0 does not exist.");
				err = inputOutOfBounds;
			}
		}
	}
	return err;
}

//===============================
OSErr _BLValidateRow( UInt16 inRow, BlugsRef inList )
{
	OSErr	err;
	
	if (!inRow || inRow > bl_NRows(inList))
	{
		_AssertStr(false,"Error: row does not exist.");
		err = inputOutOfBounds;
	}
	else err = noErr;
	return err;
}

//===============================
OSErr _BLValidateColumn( UInt16 inColumn, BlugsRef inList )
{
	OSErr	err;
	
	_AssertStr( inColumn && inColumn <= bl_NCols(inList), "Error: column does not exist." );
	if (!inColumn || inColumn > bl_NCols(inList)) err = inputOutOfBounds;
	else err = noErr;
	return err;
}

//===============================
OSErr _BLValidateCell( BLCell inCell, BlugsRef inList )
{
	OSErr	err;
	
	if (!bl_ValidCell(inCell))
	{
		err = inputOutOfBounds;
		_AssertStr(false,"Error: invalid cell");
	}
	else
	{
		if (!bl_CellInRange(inCell,inList))
		{
			err = inputOutOfBounds;
			_AssertStr(false,"Error: cell does not exist");
		}
		else err = noErr;
	}
	return err;
}

//===============================
// Validates a cell when the cell may also refer to a title or top left.
OSErr _BLValidateCellOrTitle( BLCell inCell, BlugsRef inList )
{
	BLTitleBarHdl	vBar, hBar;
	OSErr			err = noErr;
	
	vBar = bl_VTitle(inList);
	hBar = bl_HTitle(inList);
	// Column 0 means vertical title bar.
	if (!inCell.col)
	{
		if (!vBar)
		{
			_AssertStr(false,"Error: cell refers to nonexistent vertical title bar.");
			err = inputOutOfBounds;
		}
	}
	// Row 0 means horizontal title bar.
	if (!inCell.row)
	{
		if (!hBar)
		{
			_AssertStr(false,"Error: cell refers to nonexistent horizontal title bar.");
			err = inputOutOfBounds;
		}
	}
	if (!bl_CellInRange(inCell,inList))
	{
		_AssertStr(false,"Error: cell does not exist");
		err = inputOutOfBounds;
	}
	return err;
}

//===============================
OSErr _BLValidateWidget( Boolean inVertical, UInt16 inWidget, BlugsRef inList )
{
	Handle	widgets;
	UInt16	nWidgets;
	OSErr	err = noErr;
	
	nWidgets = (inVertical) ? (*inList)->nVWidgets : (*inList)->nHWidgets;
	widgets = (inVertical) ? (*inList)->vWidgets : (*inList)->hWidgets;
	if (!nWidgets || !widgets || !inWidget || inWidget > nWidgets) err = inputOutOfBounds;
	return err;
}


#pragma mark ¥¥¥Variable-size Array¥¥¥
//===============================
#define va_NthItemPtr(n,arrayP,itemSize)		(arrayP + ((n - 1) * itemSize))
#define va_NthItemPtr16(n,arrayP)				((UInt16*)arrayP + n - 1)
#define va_NthItemPtr32(n,arrayP)				((UInt32*)arrayP + n - 1)

//===============================
Handle _VANew( UInt32 inNItems, UInt32 inItemSize, Boolean inZero )
{
	Handle	arrayH;
	UInt32	size = inNItems * inItemSize;
	
	arrayH = NewHandle( size );
	if (arrayH)
	{
		if (inZero && size) _BLZeroMemory( *arrayH, size );
	}
	return arrayH;
}

//===============================
OSErr _VAAdd( UInt32 count, UInt32 addHere, UInt32 nItems, UInt32 itemSize, Handle arrayH, Boolean zero )
{
	UInt32		neededSize;
	OSErr		err;
	
	neededSize = (nItems + count) * itemSize;
	SetHandleSize( arrayH, neededSize );
	err = MemError();
	if (!err && itemSize)
	{
		Ptr			arrayP, sourceP;
		
		arrayP = *arrayH;
		sourceP = va_NthItemPtr(addHere,arrayP,itemSize);
		if (addHere <= nItems) BlockMoveData( sourceP, va_NthItemPtr(addHere + count,arrayP,itemSize),
												(nItems - addHere + 1) * itemSize );
		// Optionally zero the added items
		if (zero) _BLZeroMemory( sourceP, count * itemSize );
	}
	return err;
}

//===============================
void _VARemove( UInt32 inCount, UInt32 inItem, UInt32 inNItems, UInt32 inItemSize, Handle inArray )
{
	_AssertStr( inItem != 0, "Internal error: cannot delete element #0." );
	_AssertStr( inCount != 0, "Internal notice: deleting 0 items has no effect." );
	_AssertStr( inCount <= inNItems, "Internal notice: trying to delete more items than exist." );
	_AssertStr( inItem + inCount - 1 <= inNItems, "Internal notice: trying to delete more items than exist." );
	if (inCount && inNItems && inItem)
	{
		Ptr			arrayP;
		UInt16		entriesBelow, entriesAbove;
		UInt32		neededSize;
		
		if (inCount > inNItems) inCount = inNItems;
		if (inItem + inCount - 1 > inNItems) inCount = inNItems + 1 - inItem;
		arrayP = *inArray;
		entriesBelow = inItem - 1;
		entriesAbove = inNItems - inCount - entriesBelow;
		// Move to target of shift
		arrayP += entriesBelow * inItemSize;
		BlockMoveData(	arrayP + (inCount * inItemSize), arrayP, entriesAbove * inItemSize );
		neededSize = (inNItems - inCount) * inItemSize;
		SetHandleSize( inArray, neededSize );
	}
}

/*===============================
OSErr _VAMove( UInt32 count, UInt32 moveThis, UInt32 here, UInt32 itemSize, Handle arrayH )
{
	Ptr			arrayP, moveThisPtr, herePtr, temp;
	UInt32		copySize;
	
	_AssertStr( moveThis != here, "Notice: tried to move data to where it already is." );
	if (moveThis == here) return noErr;
	copySize = count * itemSize;
	temp = NewPtr( copySize );
	if (!temp) return memFullErr;
	arrayP = *arrayH;
	moveThisPtr = arrayP + ((moveThis - 1) * itemSize);
	herePtr = arrayP + ((here - 1) * itemSize);
	BlockMoveData( moveThisPtr, temp, copySize );
	if (here > moveThis)
		BlockMoveData( moveThisPtr + copySize, moveThisPtr, itemSize * (here - moveThis) );
	else BlockMoveData(	herePtr, herePtr + copySize, itemSize * (moveThis - here) );
	BlockMoveData( temp, herePtr, copySize );
	DisposePtr( temp );
	return noErr;
}*/

//===============================
// A specific entry is the target of movement.
// We try to copy inCount entries, starting with inSource, so that inSource becomes
//	the entry after inDest.
// It's fine to pass 0 for inDest. The items get moved to the front of the array.
OSErr _VAMoveAfter( UInt32 inCount, UInt32 inSource, UInt32 inDest, UInt32 inItemSize,
					UInt32 inItemCount, Handle inArray )
{
	OSErr		err = noErr;
	
	if (inSource != inDest && inCount)
	{
		_AssertStr( inSource && inSource <= inItemCount, "Internal error: source does not exist." );
		_AssertStr( inDest <= inItemCount, "Internal error: destination does not exist." );
		// Make sure count is not greater than or equal to the total number of items.
		_AssertStr( inCount < inItemCount, "Internal error: trying to move too many items." );
		if (inSource && inSource <= inItemCount && inDest <= inItemCount && inCount < inItemCount)
		{
			UInt32	copySize, maxCount;
			Ptr		temp;
			
			// Check the inCount parameter to make sure we're not trying to move too many items.
			// The maximum count is the moved entry and all above it.
			maxCount = inItemCount - inSource + 1;
			if (inCount <= maxCount)
			{
				// Also, if the destination is greater than the source, we can't move the
				//	destination along with the source items.
				// If we have {abcdefgh} and we move 3 entries starting with #2 (b),
				//	the first entry we can move after is #5 (e). first = source + count.
				if (inDest < inSource || (inDest > inSource && inDest >= inSource + inCount))
				{
					copySize = inCount * inItemSize;
					temp = NewPtr( copySize );
					if (temp)
					{
						Ptr		data, sourcePtr, destPtr, bmSourcePtr, bmDestPtr;
						UInt32	bmSize;
						
						data = *inArray;
						sourcePtr = va_NthItemPtr(inSource,data,inItemSize);
						// Store moved items in temp buffer.
						BlockMoveData( sourcePtr, temp, copySize );
						// Shift to fill the gap.
						if (inDest > inSource)
						{
							// We move the entries above the moved stuff, and the destination
							//	element down to the vacant space.
							// If we have {abcdefgh} and we move 3 entries starting with #2 (b),
							//	after 6 (f), we want to have {a---efgh}, shift 2 elements down by
							//	the number of elements we're moving, to get {aef---gh},
							//	then put our moved stuff back in to get {aefbcdgh}.
							// So the number of elements shifted is dest - source - count + 1.
							// Source of shift is inSource + inCount. Target of the shift is inSource.
							UInt32	nEntriesShifted;
							
							nEntriesShifted = inDest - inSource - inCount + 1;
							bmSourcePtr = sourcePtr + copySize;
							bmDestPtr = sourcePtr;
							bmSize = inItemSize * nEntriesShifted;
							// The destination ptr for replacing the buffered entries, will be
							//	the destination of the shift + nEntriesShifted
							destPtr = bmDestPtr + (nEntriesShifted * inItemSize);
						}
						else
						{
							// Shift lower entries up, shifting by the number of bytes moved.
							// If inDest = 0, we shift entry 1 up. We shift a number of entries
							//	equal to source - dest - 1.
							bmSourcePtr = va_NthItemPtr(inDest + 1,data,inItemSize);
							bmDestPtr = bmSourcePtr + copySize;
							bmSize = (inSource - inDest - 1) * inItemSize;
							// When we copy our stuff back, we just put it at location inDest + 1.
							// That's because inDest is the entry we put our stuff *after*.
							destPtr = bmSourcePtr;
						}
						BlockMoveData( bmSourcePtr, bmDestPtr, bmSize );
						// Store moved items in new location.
						BlockMoveData( temp, destPtr, copySize );
						DisposePtr( temp );
					}
					else err = memFullErr;
				}
				else err = inputOutOfBounds;
			}
			else err = inputOutOfBounds;
		}
		else err = inputOutOfBounds;
	}
	return err;
}


//===============================
// Requires a sorted array!
// Returns nthItem; zero if no match.
// If no match and 'outNext' pointer is valid, returns the next element or zero if search item
//	would be the last one.
UInt32 _VASearch( Handle inArrayH, Ptr inArrayP, UInt32 inNItems, UInt32 inItemSize,
					VASearchProcPtr inProc, UInt32 inRefCon, UInt32 inSearchData,
					UInt32* outNext )
{
	UInt32		next = 0, matchedItem = 0;
	
	if (inNItems)
	{
		SInt32		left = 0, right = inNItems - 1;
		UInt32		index;
		
		if (inProc)
		{
			SInt32		response;
			SInt8		handleState;
			
			if (inArrayH)
			{
				handleState = HGetState( inArrayH );
				HLock( inArrayH );
				inArrayP = *inArrayH;
			}
			do
			{
				index = (left + right) / 2;
				response = (*inProc)( va_NthItemPtr(index+1,inArrayP,inItemSize),
									inSearchData, inRefCon );
				if (response == blEqual)
				{
					matchedItem = index + 1;
					if (outNext)
					{
						// Return the next one unless the matched item is the last item
						if (matchedItem < inNItems) next = matchedItem + 1;
					}
					break;
				}
				// Search item is greater
				if (response == blSearchDataIsGreater) left = index + 1;
				else right = index - 1;
			} while (right >= left);
			if (!matchedItem)
			{
				if (outNext)
				{
					// If the response was that the search data was greater for the last index,
					//	get the one after the index if it exists.
					if (response == blSearchDataIsGreater)
					{
						next = index + 2;
						if (next > inNItems) next = 0;
					}
					// Otherwise this is the one we want
					else next = index + 1;
				}
			}
			if (inArrayH) HSetState( inArrayH, handleState );
		} // if inProc
	} // if inNItems
	if (outNext) *outNext = next;
	return matchedItem;
}

//===============================
// We might need to sort a handle, maybe we need to sort from a pointer.
// Pass one and pass nil for the other and the right one will be sorted.
//typedef UInt32 (*VASortProcPtr)( const Ptr data1P, const Ptr data2P, UInt32 refCon );
void _VASort( Handle arrayH, Ptr arrayP, UInt32 nItems, UInt32 itemSize, VASortProcPtr proc,
				UInt32 refCon )
{
	UInt8	handleState;
	
	if (arrayH)
	{
		handleState = HGetState( arrayH );
		HLock( arrayH );
		arrayP = *arrayH;
	}
    switch (itemSize)
    {
		case sizeof(UInt16):
		local_VAHeapsort16( (UInt16*)arrayP, nItems, proc, refCon );
		break;
		
		// We have no 32-bit item sizes currently.
		case sizeof(UInt32):
		_AssertStr(false,"Internal notice: need to reenable local_VAHeapsort32." );
		//local_VAHeapsort32( (UInt32*)arrayP, nItems, proc, refCon );
		//break;

		default:
		local_VAHeapsortOther( arrayP, nItems, itemSize, proc, refCon );
		break;
    }
    if (arrayH) HSetState( arrayH, handleState );
}

//===============================
static void local_VAReheap16( UInt16* arrayP, UInt32 left, UInt32 right,
								VASortProcPtr proc, UInt32 refCon )
{
	UInt32	j, j2;
	
	j = left;
	j2 = j << 1;
	while (j2 < right)
	{
		j = j2;
		if ((*proc)((Ptr)(va_NthItemPtr16(j+1,arrayP)), (Ptr)(va_NthItemPtr16(j,arrayP)),
					refCon) >= blFirstIsGreater ) ++j;
		j2 = j << 1;
	}
	if (j2 == right) j = right;

	while ((left < j) && (*proc)((Ptr)(va_NthItemPtr16(left,arrayP)),
									(Ptr)(va_NthItemPtr16(j,arrayP)),
									refCon) >= blFirstIsGreater ) j >>= 1;

	if (left < j)
	{
	    do
	    {
			UInt16	temp;
			
			temp = *va_NthItemPtr16(left,arrayP);
			*va_NthItemPtr16(left,arrayP) = *va_NthItemPtr16(j,arrayP);
			*va_NthItemPtr16(j,arrayP) = temp;
	        j >>= 1;
	    }
	    while ( left < j );
	}
}

//===============================
static void local_VAReheapOther( Ptr arrayP, UInt32 itemSize, UInt32 left, UInt32 right,
								VASortProcPtr proc, UInt32 refCon, Ptr temp )
{
    UInt32	j, j2;
	
	j = left;
	j2 = j << 1;
	while (j2 < right)
	{
		j = j2;
		if ((*proc)(va_NthItemPtr(j+1,arrayP,itemSize),
					va_NthItemPtr(j,arrayP,itemSize),
					refCon) >= blFirstIsGreater ) ++j;
		j2 = j << 1;
	}
	if (j2 == right) j = right;

	while ((left < j) && (*proc)(va_NthItemPtr(left,arrayP,itemSize),
								va_NthItemPtr(j,arrayP,itemSize),
								refCon) >= blFirstIsGreater ) j >>= 1;

	if (left < j)
	{
		Ptr	leftP = va_NthItemPtr(left,arrayP,itemSize);
	    do
	    {
			Ptr	rightP = va_NthItemPtr(j,arrayP,itemSize);

			BlockMoveData( leftP, temp, itemSize );
			BlockMoveData( rightP, leftP, itemSize );
			BlockMoveData( temp, rightP, itemSize );
	        j >>= 1;
	    }
	    while ( left < j );
	}
}

//===============================
static void local_VAHeapsort16( UInt16* arrayP, UInt32 nItems, VASortProcPtr proc,
								UInt32 refCon )
{
	UInt32	i;

	for (i = nItems >> 1; i >= 1; --i) local_VAReheap16( arrayP, i, nItems, proc, refCon );

	for ( i = nItems; i >= 2; --i )
	{
		UInt16	temp;
		
		temp = *va_NthItemPtr16(1,arrayP);
		*va_NthItemPtr16(1,arrayP) = *va_NthItemPtr16(i,arrayP);
		*va_NthItemPtr16(i,arrayP) = temp;
		local_VAReheap16( arrayP, 1, i - 1, proc, refCon );
	}
}

//===============================
static void local_VAHeapsortOther( Ptr arrayP, UInt32 nItems, UInt32 itemSize,
									VASortProcPtr proc, UInt32 refCon )
{
	Ptr		temp;
	
	temp = NewPtr( itemSize );
	if (temp)
	{
		Ptr		leftP;
		UInt32	i;
		
		leftP = va_NthItemPtr(1,arrayP,itemSize);
		
		for (i = nItems >> 1; i >= 1; --i)
			local_VAReheapOther( arrayP, itemSize, i, nItems, proc, refCon, temp );
		
		for (i = nItems; i >= 2; --i)
		{
			Ptr	rightP = va_NthItemPtr(i,arrayP,itemSize);

			BlockMoveData( leftP, temp, itemSize );
			BlockMoveData( rightP, leftP, itemSize );
			BlockMoveData( temp, rightP, itemSize );
			local_VAReheapOther( arrayP, itemSize, 1, i - 1, proc, refCon, temp );
		}
		DisposePtr( temp );
	}
}


#if __BL_USE_CARBON_API__
#if !TARGET_RT_MAC_MACHO
#pragma mark ¥¥¥Framework Access¥¥¥
//===============================
void* _BLGetBSDFunctionPtr( CFStringRef inFunction )
{
	OSStatus			err;
	static CFBundleRef 	sysBundle = nil;
	void*				p = nil;
	
	if (!sysBundle)
	{
		err = local_BLLoadFrameworkBundle( CFSTR("System.framework"), &sysBundle );
		if (!err) p = CFBundleGetFunctionPointerForName( sysBundle, inFunction );
	}
	return p;
}

//===============================
void* _BLGetCarbonFunctionPtr( CFStringRef inFunction )
{
	OSStatus			err;
	static CFBundleRef 	sysBundle = nil;
	void*				p = nil;
	
	if (!sysBundle)
	{
		err = local_BLLoadFrameworkBundle( CFSTR("Carbon.framework"), &sysBundle );
		if (!err) p = CFBundleGetFunctionPointerForName( sysBundle, inFunction );
	}
	return p;
}

//===============================
static OSStatus local_BLLoadFrameworkBundle( CFStringRef inFramework, CFBundleRef *outBundle )
{
	OSStatus 	err;
	FSRef 		frameworksFolderRef;
	CFURLRef	baseURL = nil;
	CFURLRef	bundleURL = nil;
	
	*outBundle = nil;
	// Find the frameworks folder and create a URL for it.
	err = FSFindFolder( kOnAppropriateDisk, kFrameworksFolderType, true, &frameworksFolderRef );
	if (!err)
	{
		baseURL = CFURLCreateFromFSRef( kCFAllocatorSystemDefault, &frameworksFolderRef );
		if (!baseURL) err = coreFoundationUnknownErr;
	}
	// Append the name of the framework to the URL.
	if (!err)
	{
		bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, inFramework, false);
		if (bundleURL == nil) err = coreFoundationUnknownErr;
	}
	// Create a bundle based on that URL and load the bundle into memory.
	// We never unload the bundle, which is reasonable in this case because 
	// the sample assumes that you'll be calling functions from this 
	// framework throughout the life of your application.
	if (!err)
	{
		*outBundle = CFBundleCreate( kCFAllocatorSystemDefault, bundleURL );
		if (*outBundle == nil) err = coreFoundationUnknownErr;
	}
	if (!err)
	{
	    if (!CFBundleLoadExecutable( *outBundle )) err = coreFoundationUnknownErr;
	}
	// Clean up.
	if (err != noErr && *outBundle != nil) CFRelease(*outBundle);
	if (bundleURL != nil) CFRelease(bundleURL);
	if (baseURL != nil) CFRelease(baseURL);
	return err;
}
#endif
#endif