/*===============================
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
*===============================*/
#include "BlugsPrivate.h"

#pragma mark ¥¥¥Module Data¥¥¥


// This may be a waste of good memory!
// Some of the shades are rather close to each other, like AD and A5, 9C and 94.
// I swear these are the shades I identified with GraphicConverter, but I do not have access
// to the current version of Apple's greyscale specs. This should be close.

// The color for the bottom of the Aqua list view header button (i.e., title) is 9A.
// We can use _99Col and see if it looks good enough. (It should look better since Aqua
//	mostly draws on a white background and we don't need all that much contrast.)

// 10/28/2001 Moses -->1.1
// These colors were off a little; my old 6100 did not support millions
//	of colors so my screen shot analysis was inaccurate. I believe these are
//	the correct Appearance shades.
static const RGBColor
				_FFCol = {0xFFFF,0xFFFF,0xFFFF},
				_EECol = {0xEEEE,0xEEEE,0xEEEE},
				_DDCol = {0xDDDD,0xDDDD,0xDDDD},
				_CCCol = {0xCCCC,0xCCCC,0xCCCC},
				_BBCol = {0xBBBB,0xBBBB,0xBBBB},
				_AACol = {0xADAD,0xADAD,0xADAD},
				_99Col = {0x9999,0x9999,0x9999},
				_88Col = {0x8888,0x8888,0x8888},
				_77Col = {0x7777,0x7777,0x7777}, // This color can be used for inactive text
				_66Col = {0x6666,0x6666,0x6666},
				_55Col = {0x5555,0x5555,0x5555},
				_44Col = {0x4444,0x4444,0x4444},
				_33Col = {0x3333,0x3333,0x3333},
				_22Col = {0x2222,0x2222,0x2222},
				_11Col = {0x1111,0x1111,0x1111},
				_00Col = {0x0000,0x0000,0x0000},
				// For drawing our own drag hiliting.
				_DragBlueCol	= {0xAAAA,0xAAAA,0xFFFF},
				// Colors used for drawing disclosure triangles
				_PressedBlueCol = {0x3333,0x3333,0x9999},
				_LtBlueCol		= {0xCCCC,0xCCCC,0xFFFF},
				// Can use this for focus too (pre-Appearance)
				_MedBlueCol		= {0x9999,0x9999,0xFFFF},
				_DkBlueCol		= {0x6666,0x6666,0xCCCC};
				


static void		local_BLDrawBorder( BlugsRef inList );
static void		local_BLDrawFocus( Rect inRect, Boolean inFocus );
static void		local_BLDrawCorners( BlugsRef inList );
static void		local_DrawWidgets( BlugsRef inList );
static void		local_BLDrawSortButtonLine( Point inDrawLineHere, UInt8 inLength,
											Boolean inIsActive );
static void		local_BLSetCellTextColor( Boolean inIsActive );
static void		local_BLSetTitleTextColor( ThemeDrawState inState );
#if !TARGET_RT_MAC_MACHO
static void		local_BLDrawCellBorders( BLCell inCell, const Rect* inRect, BlugsRef inList );
#else
static void		local_BLDrawCellBordersCG( BLCell inCell, Rect inRect, BlugsRef inList );
#endif
static Boolean	local_BLDragging( BLCell inCell, const Rect* inRect, BlugsRef inList );
static void		local_BLHiliteCell( BLCell inCell, BLHandlerParamPtr inParamP,
									UInt32 inHandlerFeatures, BlugsRef inList );
static void		local_BLDrawHiliteRect( Boolean inRestoreBack, Boolean inActive,
										const Rect* inRect );
static void		local_BLDrawHiliteRgn( Boolean inRestoreBack, Boolean inActive, RgnHandle inRegion );
static void		local_BLDrawBackground( BLCell inCell, const Rect* inRect, BlugsRef inList );
static Boolean	local_BLGetBottomBlankRect( Rect* outRect, BlugsRef inList );
static Boolean	local_BLGetRightBlankRect( Rect* outRect, BlugsRef inList );
static void		local_BLDrawIntersectingTitles( Boolean inVertical, const Rect* inUpdateRect,
												BlugsRef inList );
static void 	local_BLDrawBevelButton( BLPart inPart, UInt16 inTitle, const Rect* inRect,
										ThemeDrawState inState, ThemeButtonValue inValue,
										BlugsRef inList );
static void		local_BLDrawGrowBox( Boolean inIsActive, Boolean inGrow, BlugsRef inList );
#if !TARGET_RT_MAC_MACHO
static void		local_BLDrawDisclosureTriangle( Rect inRect, BLDisclosureTriangleState inState,
												Boolean inActive, Boolean inPressed );
#else
static void		local_BLDrawDragInsertionCaretCG( SInt16 inH, SInt16 inV, SInt16 inRight );
static void		local_BLDrawMarqueeCG( Rect inRect );
#endif
static void		local_BLDrawListBoxFrame( const Rect* inRect, ThemeDrawState inState );


#if TARGET_RT_MAC_MACHO
#define bl_HIViewSetNeedsDisplay		HIViewSetNeedsDisplay
#else
typedef OSStatus (*HIViewSetNeedsDisplayPtr)( ControlRef inView, Boolean inNeedsDisplay );
#define bl_HIViewSetNeedsDisplay		(*p)
#endif


/*
Drawn offscreen:
	Cells
	Background
	Title bars
	Top left dummy placard
Drawn onscreen:
	Border
	Focus
	Scroll bars
	Dummy placards/sort button
	Inline edit cell
*/

#pragma mark -
#pragma mark ¥¥¥Onscreen Drawing¥¥¥
//===============================
BL_PROC OSErr BLSetAutodraw( Boolean inDraw, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		// Do nothing if no change internally.
		if (inDraw != bl_Autodraw(inList))
		{
			bl_Toggle((*inList)->listFlags,blAutodraw);
			if (inDraw && bl_NeedUpdate(inList))
				_BLUpdateWorld( blUpdateBlit | blUpdateBorder | blUpdateCorners, nil, inList );
		}
	}
	return err;
}

//===============================
BL_PROC void BLUpdate( RgnHandle inUpdateRgn, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) _BLUpdate( blUpdateBorder | blUpdateCorners, inUpdateRgn, inList );
}

//===============================
// blUpdateBlit and blUpdateBlitAll options are ignored
void _BLUpdate( BLUpdateOptions inOptions, RgnHandle inUpdateRgn, BlugsRef inList )
{
#if __BL_USE_CARBON_API__
#if !TARGET_RT_MAC_MACHO
	static HIViewSetNeedsDisplayPtr	p = nil;
	
	if (!p) p = _BLGetCarbonFunctionPtr( CFSTR("HIViewSetNeedsDisplay") );
#endif
#endif
	if (bl_Visible(inList))
	{
		WindowRef		window;
		GWorldPtr		world;
		
		window = bl_Window(inList);
		world = bl_World(inList);
		_AssertStr(world != nil,"Internal error: list has no GWorld.");
		if (world && window)
		{
			GWorldPtr		saveWorld;
			PixMapHandle	pm;
			Rect			onscreenRect, offscreenRect, updateRect, controlRect;
			Boolean			inlineNow, drawBorder;
			GDHandle		saveGD;
			RGBColor		saveFore, saveBack;
			ControlRef		hScroll, vScroll;
			
			hScroll = bl_HScroll(inList);
			vScroll = bl_VScroll(inList);
			inlineNow = bl_InlineNow(inList);
			drawBorder = bl_Border(inList);
			GetGWorld( &saveWorld, &saveGD );
			SetGWorld( GetWindowPort( window ), nil );
			if (bl_Aqua)
			{
				if (bl_Test(inOptions,blUpdateCorners))
				{
					local_BLDrawCorners( inList );
					bl_Set(inOptions,blUpdateBorder);
				}
				if (bl_Test(inOptions,blUpdateBorder)) local_BLDrawBorder( inList );
				RGBForeColor( &saveFore );
				RGBBackColor( &saveBack );
			}
			// Copy the main GWorld to the window
			// First figure how much to draw
			offscreenRect = _BLOffscreenRect( inList );
			if (inUpdateRgn)
			{
				GetRegionBounds( inUpdateRgn, &updateRect );
				SectRect( &updateRect, &offscreenRect, &onscreenRect );
			}
			else
			{
				updateRect = bl_Rect(inList);
				onscreenRect = offscreenRect;
			}
			GetForeColor( &saveFore );
			GetBackColor( &saveBack );
			ForeColor( blackColor );
			BackColor( whiteColor );
			pm = GetGWorldPixMap( world );
			if (LockPixels( pm ))
			{
				RgnHandle		inlineRgn = nil;
				
				// Have to mask out the rect occupied by the current inline edit process,
				//	which is assumed to be drawing onscreen.
				if (inlineNow)
				{
					Rect		inlineRect;
					BLCell		editCell;
					OSErr		err;
					
					err = BLGetCellFromUID( &bl_InlineCell(inList), &editCell, inList );
					if (!err) err = _BLCellRect( editCell, &inlineRect, inList );
					if (!err)
					{
						RgnHandle		onscreenRgn = NewRgn();
						
						inlineRgn = NewRgn();
						RectRgn( inlineRgn, &inlineRect );
						RectRgn( onscreenRgn, &onscreenRect );
						// Cut a hole in the region of our blit rectangle for the inline cell.
						// The inline region may be empty, if the cell is outside the update region.
						DiffRgn( onscreenRgn, inlineRgn, inlineRgn );
						DisposeRgn( onscreenRgn );
						_BLUpdateCellOnscreen( editCell, &inlineRect, inList );
					}
				}
				// Inline rgn is nil if no inline edit.
			#if __BL_USE_CARBON_API__
				CopyBits( (BitMapPtr)*pm, GetPortBitMapForCopyBits( GetWindowPort( window ) ),
							&onscreenRect, &onscreenRect, srcCopy, inlineRgn );
			#else
				CopyBits( (BitMapPtr)*pm, (BitMapPtr)*GetPortPixMap( GetWindowPort( window ) ),
							&onscreenRect, &onscreenRect, srcCopy, inlineRgn );
			#endif
				if (inlineRgn) DisposeRgn( inlineRgn );
				UnlockPixels( pm );
			}
			// Recycle the onscreen rect to hold the entire list rectangle.
			if (hScroll)
			{
				// 10/1/2001 Moses -->1.1
				// Added Carbon control sanity check.
				#if __BL_USE_CARBON_API__
					_AssertStr(IsValidControlHandle( hScroll ),"Internal error: unknown hScroll.");
				#endif
				GetControlBounds( hScroll, &controlRect );
				if (SectRect( &updateRect, &controlRect, &controlRect ))
				{
				#if __BL_USE_CARBON_API__
					//if (bl_Test((*inList)->privateFlags,blIsInCompositingWindow))
					if (bl_Jaguar)
					{
					#if !TARGET_RT_MAC_MACHO
						if (p)
					#endif
						bl_HIViewSetNeedsDisplay( hScroll, true );
					}
					else
				#endif
					Draw1Control( hScroll );
				}
			}
			if (vScroll)
			{
				// 10/5/2001 Moses -->1.1
				// Added Carbon control sanity check.
				#if __BL_USE_CARBON_API__
					_AssertStr(IsValidControlHandle( vScroll ),"Internal error: unknown vScroll.");
				#endif
				GetControlBounds( vScroll, &controlRect );
				if (SectRect( &updateRect, &controlRect, &controlRect ))
				{
				#if __BL_USE_CARBON_API__
					//if (bl_Test((*inList)->privateFlags,blIsInCompositingWindow))
					if (bl_Jaguar)
					{
					#if !TARGET_RT_MAC_MACHO
						if (p)
					#endif
						bl_HIViewSetNeedsDisplay( vScroll, true );
					}
					else
				#endif
					Draw1Control( vScroll );
				}
			}
			RGBForeColor( &saveFore );
			RGBBackColor( &saveBack );
			if (!bl_Aqua)
			{
				if (bl_Test(inOptions,blUpdateCorners))
				{
					local_BLDrawCorners( inList );
				#if __BL_USE_CARBON_API__
					if (bl_Aqua) bl_Set(inOptions,blUpdateBorder);
				#endif
				}
				if (bl_Test(inOptions,blUpdateBorder)) local_BLDrawBorder( inList );
				// 10/16/2001 Moses -->1.1
				// Make sure colors are restored: local_BLDrawBorder & local_BLDrawCorners
				//	may alter colors yet again.
				RGBForeColor( &saveFore );
				RGBBackColor( &saveBack );
			}
			SetGWorld( saveWorld, saveGD );
		#if __BL_USE_CARBON_API__
			QDFlushPortBuffer( GetWindowPort( window ), nil );
		#endif
		} // List GWorld and port are non-nil.
	} // List is visible
}

//===============================
// When the list is focused, we draw after drawing the border.
// Otherwise we draw (erase, actually) before drawing the border.
static void local_BLDrawBorder( BlugsRef inList )
{
	Rect		focusRect, borderRect;
	Boolean		focus, canFocus, active, focusAfter = false;
	
	focus = bl_Focused(inList);
	canFocus = bl_CanFocus(inList);
	active = bl_Active(inList);
	focusRect = bl_Rect(inList);
	borderRect = focusRect;
	//if (bl_Aqua) InsetRect( &focusRect, 1, 1 );
	if (bl_HasHScroll(inList))
	{
		focusRect.bottom--;
		borderRect.bottom--;
	}
	if (bl_HasVScroll(inList))
	{
		focusRect.right--;
		borderRect.right--;
	}
	if (canFocus)
	{
		if (bl_Test((*inList)->privateFlags,blNeedFocusErase))
		{
			local_BLDrawFocus( focusRect, false );
			bl_Clear((*inList)->privateFlags,blNeedFocusErase);
			bl_Clear((*inList)->listFlags,blFocused);
		}
		//if (!focus || !active) local_BLDrawFocus( focusRect, false );
		else if (focus && active) focusAfter = true;
	}
	if (bl_Border(inList)) local_BLDrawListBoxFrame( &borderRect, (active) ?
													kThemeStateActive : kThemeStateInactive );
	if (focusAfter) local_BLDrawFocus( focusRect, true );
}


//===============================
static void local_BLDrawFocus( Rect inRect, Boolean inFocus )
{
	RGBColor	saveFore, saveBack;
	
	GetForeColor( &saveFore );
	GetBackColor( &saveBack );
#if !__BL_USE_CARBON_API__
	if (bl_Appearance)
#endif
	{
		//if (bl_Aqua) InsetRect( &inRect, -1, -1 );
		DrawThemeFocusRect( &inRect, inFocus );
	}
#if !__BL_USE_CARBON_API__
	else
	{
		InsetRect( &inRect, -3, -3 );
		PenSize( 2, 2 );
		if (inFocus) RGBForeColor( &_MedBlueCol );
		else RGBForeColor( &saveBack );
		FrameRect( &inRect );
		PenNormal();
	}
#endif
	RGBForeColor( &saveFore );
	RGBBackColor( &saveBack );
}

//===============================
// The top left is handled offscreen; need not do it here.
static void local_BLDrawCorners( BlugsRef inList )
{
	Boolean			hasGrow, active;
	ControlRef		vScroll, hScroll;
	
	vScroll = bl_VScroll(inList);
	hScroll = bl_HScroll(inList);
	hasGrow = bl_HasGrow(inList);
	active = bl_Active(inList);
	if (bl_SortButton(inList))
	{
		Rect			r;
		BLSortState		state;
		
		state = _BLSortButtonState( inList );
		r = _BLSortButtonRect( inList );
		_BLDrawSortButton( &r, state, inList );
	}
	if (bl_DrawGrow(inList) && !bl_Aqua && (hScroll || vScroll))
		local_BLDrawGrowBox( active, hasGrow, inList );
	local_DrawWidgets( inList );
}

//===================================
static void local_DrawWidgets( BlugsRef inList )
{
	UInt16			nWidgets, i, flags;
	OSErr			err;
	Rect			r;
	ThemeDrawState	state;
	
	state = (bl_Active(inList)) ? kThemeStateActive : kThemeStateInactive;
	if (bl_HScroll(inList))
	{
		nWidgets = BLCountWidgets( false, inList );
		for (i = 1; i <= nWidgets; i++)
		{
			err = BLGetWidgetRect( false, i, &r, inList );
			if (!err)
			{
				flags = _BLGetWidgetFlags( false, i, inList );
				_BLDrawWidget( false, i, &r, (bl_Test(flags,blWidgetOn)) ?
								kThemeStatePressed : state, inList );
			}
		}
	}
	if (bl_VScroll(inList))
	{
		nWidgets = BLCountWidgets( true, inList );
		for (i = 1; i <= nWidgets; i++)
		{
			err = BLGetWidgetRect( true, i, &r, inList );
			if (!err)
			{
				flags = _BLGetWidgetFlags( true, i, inList );
				_BLDrawWidget( true, i, &r, (bl_Test(flags,blWidgetOn)) ?
								kThemeStatePressed : state, inList );
			}
		}
	}
	
}

//===================================
// We want to be able to call this from the selection routine without redrawing a bunch
//	of stuff. That's why this routine is no longer static.
enum
{
	blUp,
	blDown,
	blUnsorted
};

// Array of vertical offsets at which a given line will be drawn for a given state
// 2,4,6,8 -pixel lines for each of three states for the large variety
// 2,4,6 -pixel lines for small
#if !TARGET_RT_MAC_MACHO
static const UInt8		largeVArray[3][4] = {{5,7,9,11},{11,9,7,5},{5,9,11,7}},
						smallVArray[3][3] = {{5,7,9},{9,7,5},{5,9,7}};
#endif
// If the scroll bars are 11 pixels, draw three lines, 2, 4, and 6 pixels in length.
// If 16-pixel scroll bars, draw four lines, 2, 4, 6, and 8 pixels in length.
void _BLDrawSortButton( const Rect* inRect, BLSortState inState, BlugsRef inList )
{
	Boolean				active;
	ThemeDrawState		buttonState;
	
	active = (!bl_Test(inState,blSortButtonInactive));
	buttonState = (active) ? ((bl_Test(inState,blSortButtonPressed)) ?
								kThemeStatePressed : kThemeStateActive) : kThemeStateInactive;
	local_BLDrawBevelButton( blSortButtonPart, 0, inRect, buttonState, kThemeButtonOff, inList );
#if !TARGET_RT_MAC_MACHO
	if (!bl_Aqua)
	{
		Boolean				down, smallScroll;
		UInt8				upDownUnsortedState, i, hOffsetBase;
		RGBColor			saveFore;
		Point				drawLineHere;
		
		smallScroll = bl_SmallScroll(inList);
		GetForeColor( &saveFore );
		down = bl_Test(inState,blSortStateLargeToSmall);
		hOffsetBase = (smallScroll) ? 4 : 7;
		if (down) upDownUnsortedState = blDown;
		else
		{
			if (bl_Test(inState,blSortStateSorted)) upDownUnsortedState = blUp;
			else upDownUnsortedState = blUnsorted;
		}
		if (bl_Sortable(inList))
		{
			const UInt8*		whichArray;
			
			// Draw lines, first the 2, 4, and 6-pixel lines in order.
			whichArray = (smallScroll) ? &smallVArray[upDownUnsortedState][0] :
										&largeVArray[upDownUnsortedState][0];
			drawLineHere.h = inRect->left + hOffsetBase;
			for (i = 0; i < 3; i++)
			{
				drawLineHere.v = inRect->top + whichArray[i];
				local_BLDrawSortButtonLine( drawLineHere, (i << 1) + 2, active );
				drawLineHere.h -= 1;
			}
			// If the button is large enough, draw the 8-pixel line.
			if (!smallScroll)
			{
				drawLineHere.v = inRect->top + whichArray[3];
				local_BLDrawSortButtonLine( drawLineHere, 8, active );
			}
		}
		RGBForeColor( &saveFore );
	}
#endif
}

//===================================
// Draws a line of specified length at the current pen location, and shading
//	under each line when active
static void local_BLDrawSortButtonLine( Point inDrawLineHere, UInt8 inLength, Boolean inIsActive )
{
	MoveTo( inDrawLineHere.h, inDrawLineHere.v );
	RGBForeColor( (inIsActive) ? &_44Col : &_AACol );
	Line( 1, 0 );
	if (inLength > 2)
	{
		RGBForeColor( (inIsActive) ? &_22Col : &_99Col );
		if (inLength > 4)
		{
			Line( 1, 0 );
			RGBForeColor( (inIsActive) ? &_11Col : &_88Col );
			Line( inLength - 3, 0 );
		}
		else Line( 2, 0 );
	}
	RGBForeColor( (inIsActive) ? &_00Col : &_77Col );
	Line( 0, 0 );
	if (inIsActive)
	{
		RGBForeColor( &_BBCol );
		MoveTo( inDrawLineHere.h + 1, inDrawLineHere.v + 1 );
		Line( inLength - 1, 0 );
	}
}

//===============================
BL_PROC Boolean BLIsVisible( BlugsRef inList )
{
	OSErr	err;
	Boolean	visible = false;
	
	err = _BLValidateList( inList );
	if (!err) visible = (bl_Visible(inList));
	return visible;
}

//===============================
// 10/23/2001 Moses -->1.1
// Now returns OSErr, reallocates GWorld if global flags say to do so.
BL_PROC OSErr BLSetVisible( Boolean inMakeVisible, BlugsRef inList )
{
	OSErr		err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		Boolean			wasVisible;
		
		wasVisible = bl_Visible(inList);
		// Do nothing if there is no change of visibility.
		if (inMakeVisible != wasVisible)
		{
			ControlRef		hScroll, vScroll;
			
			// The list metrics could have changed while invisible,
			//	make sure scroll bars reflect that.
			_BLRecalc( blRecalcScroll, 0, inList );
			hScroll = bl_HScroll(inList);
			vScroll = bl_VScroll(inList);
			if (inMakeVisible)
			{
				if (bl_DumpGWorlds && !bl_World(inList)) err = _BLNewWorld( inList );
				if (hScroll) ShowControl( hScroll );
				if (vScroll) ShowControl( vScroll );
				bl_Set((*inList)->listFlags,blVisible);
				if (bl_DumpGWorlds)
					_BLUpdateWorld( blUpdateBlit | blUpdateBorder | blUpdateCorners, nil, inList );
				else _BLUpdate( blUpdateBorder | blUpdateCorners, nil, inList );
			}
			else
			{
				Rect		r;
				
				r = bl_Rect(inList);
				InsetRect( &r, -3, -3 );
				InvalWindowRect( bl_Window(inList), &r );
				bl_Clear((*inList)->listFlags,blVisible);
				if (hScroll) HideControl( hScroll );
				if (vScroll) HideControl( vScroll );
				if (bl_DumpGWorlds) _BLDisposeWorld( inList );
			}
		} // Change in visibility.
	}
	return err;
}

//===============================
// This for the benefit of our friends writing content handlers wanting to draw correctly
//	when disabled.
BL_PROC Boolean BLIsActive( BlugsRef inList )
{
	OSErr	err;
	Boolean	active = false;
	
	err = _BLValidateList( inList );
	if (!err) active = (bl_Active(inList));
	return active;
}

//===============================
BL_PROC void BLSetActive( Boolean inMakeActive, BlugsRef inList )
{
	ControlRef		hScroll, vScroll;
	Boolean			wasActive;
	OSErr			err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		wasActive = bl_Active(inList);
		// If no change in active state, do nothing.
		if (wasActive != inMakeActive)
		{
			hScroll = bl_HScroll(inList);
			vScroll = bl_VScroll(inList);
			if (inMakeActive)
			{
				bl_Set((*inList)->listFlags,blActive);
			#if !__BL_USE_CARBON_API__
				if (bl_Appearance)
			#endif
				{
					if (hScroll) ActivateControl( hScroll );
					if (vScroll) ActivateControl( vScroll );
				}
			#if !TARGET_API_MAC_CARBON
				else
				{
					if (hScroll) HiliteControl( hScroll, kControlNoPart );
					if (vScroll) HiliteControl( vScroll, kControlNoPart );
				}
			#endif
			
			}
			else
			{
				if (bl_InlineNow(inList)) _BLEndInlineEdit( true, inList );
				bl_Clear((*inList)->listFlags,blActive);
			#if !__BL_USE_CARBON_API__
				if (bl_Appearance)
			#endif
				{
					if (vScroll) DeactivateControl( vScroll );
					if (hScroll) DeactivateControl( hScroll );
				}
			#if !TARGET_API_MAC_CARBON
				else
				{
					if (vScroll) HiliteControl( vScroll, kControlInactivePart );
					if (hScroll) HiliteControl( hScroll, kControlInactivePart );
				}
			#endif
			}
			if (bl_Focused(inList)) bl_Set((*inList)->privateFlags,blNeedFocusErase);
			_BLUpdateWorld( blUpdateBlit | blUpdateBorder | blUpdateCorners, nil, inList );
		} // Change in active status.
	}
}

// Kyle
// Note: We should have some other part codes defined for inline editing.
//   The focus could be an "kBLInlineEditPart".
//	Also, when changing to kControlFocusNoPart, any inline edit session should end.
// Moses
//	Added some support for the inline edit part, but should that be callable from the
//	public API? (We already have BLBeginInlineEdit.)

// Moses again (11/3/99)
//	I think this should allow users to respond appropriately to tab keys. All internal
//	supoport for tab keys is removed. (Tho you wouldn't know it from the size of the binaries.)
BL_PROC ControlPartCode	BLSetFocusedPart( ControlPartCode inPart, BlugsRef inList )
{
	OSErr			err;
	ControlPartCode	part = kControlFocusNoPart;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		Boolean			oldFocus;
		
		// Keep track of the old state of focus.
		oldFocus = bl_Focused( inList );
		switch (inPart)
		{
			case kControlFocusNoPart:		// Turn off all focusing.
			bl_Clear( (*inList)->privateFlags, blFocused );
			if (bl_InlineNow(inList)) _BLEndInlineEdit( true, inList );
			break;
		
			// Switch the state from off/on to on/off if no inline edit session.
			case kControlFocusNextPart:
			if (bl_InlineNow(inList)) _BLMoveInlineEdit( true, inList );
			else bl_Toggle( (*inList)->privateFlags, blFocused );
			break;
			
			case kControlFocusPrevPart:
			if (bl_InlineNow(inList)) _BLMoveInlineEdit( false, inList );
			else bl_Toggle( (*inList)->privateFlags, blFocused );
			break;

			case kControlListBoxPart:			// Turn on focusing of entire list.
			bl_Set( (*inList)->privateFlags, blFocused );
			if (bl_InlineNow(inList)) _BLEndInlineEdit( true, inList );
			break;
		
			case blInlineEditPart:
			// This is called when starting an inline edit. Should not be called by host app.
			// Just defocuses the entire list.
			bl_Clear( (*inList)->privateFlags, blFocused );
			break;
			
			default:	// Simply return the state of focus; so do nothing here.
			break;
		}

		// If the focus changed, redraw if the blCanFocus feature is turned on.
		if (bl_CanFocus(inList) && oldFocus != bl_Focused(inList))
		{
			GWorldPtr		saveWorld;
			Boolean			restore;
			
			if (!bl_Focused(inList)) bl_Set((*inList)->privateFlags,blNeedFocusErase);
			restore = _BLGotoWindow( &saveWorld, inList );
			local_BLDrawBorder( inList );
			if (restore) SetGWorld( saveWorld, nil );
		}
		part = BLGetFocusedPart( inList );
	}
	return part;
}

//===============================
BL_PROC ControlPartCode BLGetFocusedPart( BlugsRef inList )
{
	ControlPartCode	result = kControlFocusNoPart;
	OSErr			err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		if (bl_InlineNow(inList)) result = blInlineEditPart;
		else if (bl_Focused(inList)) result = kControlListBoxPart;
	}
	return result;
}

//===============================
BL_PROC OSErr BLGetColumnFontStyle( UInt16 inColumn, ControlFontStylePtr outFontStyle, BlugsRef inList )
{
	OSErr			err;
	
	err = _BLValidateList( inList );
	_AssertStr( inColumn <= bl_NCols(inList), "Error: column does not exist." );
	if (!err && inColumn <= bl_NCols(inList))
	{
		if (inColumn) _BLGetColumnFontStyle( inColumn, outFontStyle, inList );
		else
		{
			BLTitleBarHdl		vTitle;
			
			vTitle = bl_VTitle(inList);
			if (vTitle) BlockMoveData( &(*vTitle)->fontStyle, outFontStyle, sizeof(ControlFontStyleRec) );
			else err = paramErr;
		}
	}
	return err;
}

//===============================
BL_PROC OSErr BLSetColumnFontStyle( UInt16 inColumn, const ControlFontStyleRec* inFontStyle,
									BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	_AssertStr( inColumn <= bl_NCols(inList), "Error: column does not exist." );
	if (!err && inColumn <= bl_NCols(inList))
	{
		if (inColumn)
		{
			_BLSetColumnFontStyle( inColumn, inFontStyle, inList );
			_BLUpdateView( blUpdateBlit, inList );
		}
		else
		{
			BLTitleBarHdl		vTitle;
			
			vTitle = bl_VTitle(inList);
			if (vTitle)
			{
				BlockMoveData( inFontStyle, &(*vTitle)->fontStyle, sizeof(ControlFontStyleRec) );
				_BLUpdateBarOnly( vTitle, inList );
			}
			else err = inputOutOfBounds;
		}
	}
	return err;
}

#pragma mark -
#pragma mark ¥¥¥Offscreen Drawing¥¥¥
//===============================
// No title bars are set up at this point
OSErr _BLNewWorld( BlugsRef inList )
{
	GWorldPtr	gw, saveWorld;
	OSErr		err;
	Rect		r, globalR;
	SInt16		depth = 0;
	Boolean		restore;
	
	// There is no way on earth we could be passed a nil list from BLNew. Just "can't happen".
	// (Famous last words)
	//if (!inList) return paramErr;
	restore = _BLGotoWindow( &saveWorld, inList );
	r = _BLOffscreenRect( inList );
	globalR = r;
	_BLLocalToGlobalRect( &globalR );
	if (bl_Test(_blGlobals.userSettings,blMax16BitGWorlds)) depth = 16;
	else if (bl_Test(_blGlobals.userSettings,blMax8BitGWorlds)) depth = 8;
	err = NewGWorld( &gw, depth, &globalR, nil, nil, (bl_TempGWorlds ? useTempMem : 0) );
	if (err == noErr && gw != nil)
	{
		SetGWorld( gw, nil );
		SetOrigin( r.left, r.top );
	}
	bl_World(inList) = gw;
	if (restore) SetGWorld( saveWorld, nil );
	return err;
}

//===============================
// 10/23/2001 Moses -->1.1
// Added this routine so we could trap errors in GWorld allocation.
void _BLDisposeWorld( BlugsRef inList )
{
	GWorldPtr	world;
	
	world = bl_World(inList);
	if (world)
	{
		// Apparently it's potentially a Bad Thing to dispose the current
		//	GWorld. Make sure thePort is something else.
		(void)_BLGotoWindow( nil, inList );
		DisposeGWorld( world );
		bl_World(inList) = nil;
	}
}

//===============================
Boolean _BLGotoWorld( GWorldPtr* outSaveWorld, BlugsRef inList )
{
	Boolean	restore;
	
	if (!bl_World(inList))
	{
		GDHandle	dev;
		
		if (outSaveWorld) GetGWorld( outSaveWorld, &dev );
		restore = false;
	}
	else restore = _BLSwapPort( bl_World(inList), outSaveWorld );
	return restore;
}

//===============================
// Returns true if the GWorld needs to be restored.
Boolean _BLGotoWindow( GWorldPtr* outSaveWorld, BlugsRef inList )
{
	return _BLSwapPort( GetWindowPort( bl_Window(inList) ), outSaveWorld );
}

#if !TARGET_RT_MAC_MACHO
//===============================
Boolean _BLSwapPort( CGrafPtr inNewPort, CGrafPtr* outOldPort )
{
	GWorldPtr	world;
	GDHandle	dev;
	Boolean		changed;
	
	GetGWorld( &world, &dev );
	if (inNewPort != world)
	{
		SetGWorld( inNewPort, nil );
		changed = true;
	}
	if (outOldPort) *outOldPort = world;
	return changed;
}
#endif

//===============================
// updateRgn is local to the GWorld
void _BLUpdateWorld( BLUpdateOptions inOptions, RgnHandle inUpdateRgn, BlugsRef inList )
{
	if (!bl_Autodraw(inList)) bl_SetNeedUpdate(inList);
	else
	{
		GWorldPtr		world, saveWorld;
		PixMapHandle	pm;
		
		world = bl_World(inList);
		if (!world) bl_SetNeedUpdate(inList);
		else
		{
			Boolean			restore;
			
			restore = _BLGotoWorld( &saveWorld, inList );
			pm = GetGWorldPixMap( world );
			if (LockPixels( pm ))
			{
				RgnHandle		saveClip = NewRgn();
				Rect			updateRect;
				
				GetClip( saveClip );
				if (inUpdateRgn) GetRegionBounds( inUpdateRgn, &updateRect );
				else updateRect = _BLOffscreenRect( inList );
				if (!EmptyRect( &updateRect ))
				{
					Rect			blankRect, intersection, titleRect;
					BLTitleBarHdl	hTitle, vTitle;
					BLCell			nullCell = {0,0};
					
					if (local_BLGetBottomBlankRect( &blankRect, inList ))
					{
						if (SectRect( &updateRect, &blankRect, &intersection ))
							local_BLDrawBackground( nullCell, &intersection, inList );
					}
					if (local_BLGetRightBlankRect(&blankRect, inList))
					{
						if (SectRect( &updateRect, &blankRect, &intersection ))
							local_BLDrawBackground( nullCell, &intersection, inList );
					}
					_BLDrawIntersectingCells( &updateRect, inList );
					// Draw the title bars if they exist and the draw region overlaps them.
					hTitle = bl_HTitle(inList);
					vTitle = bl_VTitle(inList);
					if (hTitle)
					{
						titleRect = _BLTitleBarRect( hTitle, inList );
						if (SectRect( &titleRect, &updateRect, &intersection ))
							local_BLDrawIntersectingTitles( false, &intersection, inList );
					}
					if (vTitle)
					{
						titleRect = _BLTitleBarRect( vTitle, inList );
						// Merge the top left rectangle into the vTitle rect if it is present.
						if (hTitle) titleRect.top = bl_Rect(inList).top;
						if (SectRect( &titleRect, &updateRect, &intersection ))
							local_BLDrawIntersectingTitles( true, &intersection, inList );
					}
				}
				SetClip( saveClip );
				UnlockPixels( pm );
				if (bl_Test(inOptions,blUpdateBlit))
				{
					// If blitting all, we want to blit the entire offscreen rect.
					// Recycle the saveClip since we're done with it.
					if (bl_Test(inOptions,blUpdateBlitAll))
					{
						if (inUpdateRgn) updateRect = _BLOffscreenRect( inList );
						RectRgn( saveClip, &updateRect );
						_BLUpdate( inOptions, saveClip, inList );
					}
					else _BLUpdate( inOptions, inUpdateRgn, inList );
				}
				DisposeRgn( saveClip );
			} // LockPixels OK
			if (restore) SetGWorld( saveWorld, nil );
		} // List GWorld exists.
	} // if autodraw is on
}

//===============================
void _BLUpdateView( BLUpdateOptions inOptions, BlugsRef inList )
{
	RgnHandle	listRgn;
	Rect		viewRect;
	
	listRgn = NewRgn();
	viewRect = _BLViewRect( inList );
	RectRgn( listRgn, &viewRect );
	_BLUpdateWorld( inOptions, listRgn, inList );
	DisposeRgn( listRgn );
}

//===============================
// All routines that call this one validate the list handle; no need to do again.
void _BLUpdateCell( BLUpdateOptions inOptions, BLCell inCell, BlugsRef inList )
{
	if (!bl_Autodraw(inList)) bl_SetNeedUpdate(inList);
	else
	{
		Rect		cellRect;
		OSErr		err;
		
		err = _BLCellRect( inCell, &cellRect, inList );
		if (!err && !EmptyRect(&cellRect))
		{
			GWorldPtr		world;
			
			world = bl_World(inList);
			if (world)
			{
				PixMapHandle	pm;
				GWorldPtr		saveWorld;
				Boolean			restore;
				
				restore = _BLGotoWorld( &saveWorld, inList );
				pm = GetGWorldPixMap( world );
				if (LockPixels( pm ))
				{
					RgnHandle	saveClip;
					
					saveClip = _BLClipToViewRectIntersection( &cellRect, inList );
					if (saveClip)
					{
						RgnHandle	updateRgn = NewRgn();
						
						GetClip( updateRgn );
						_BLDrawCell( (bl_Test(inOptions,blUpdateTriangle)) ?
									blCellDrawNoOptions : blCellDrawSuppressTriangle,
									inCell, &cellRect, inList );
						SetClip( saveClip );
						DisposeRgn( saveClip );
						UnlockPixels( pm );
						if (bl_Test(inOptions,blUpdateBlit)) _BLUpdate( inOptions, updateRgn, inList );
						DisposeRgn( updateRgn );
					}
				}
				if (restore) SetGWorld( saveWorld, nil );
			}
		} // non-empty cell rectangle
	} // if autodraw is enabled
}

//===============================
// This is a special routine that calls a redraw, except onscreen, for edit cells.
// Called from _BLUpdate() and BLKey() for inline edit key handling.
void _BLUpdateCellOnscreen( BLCell inCell, const Rect* inCellRect, BlugsRef inList )
{
	if (!EmptyRect(inCellRect))
	{
		GWorldPtr	saveWorld;
		Boolean		restore;
		RgnHandle	saveClip;
		
		restore = _BLGotoWindow( &saveWorld, inList );
		saveClip = _BLClipToViewRectIntersection( inCellRect, inList );
		if (saveClip)
		{
			_BLDrawCell( blCellDrawNoOptions, inCell, inCellRect, inList );
			SetClip( saveClip );
			DisposeRgn( saveClip );
		}
		if (restore) _BLSwapPort( saveWorld, nil );
	}
}

//===============================
// Draw all cells that intersect the update rect
// Need not save the clip because _BLUpdateWorld restores when done here.
// 9/2/2000 BSH.
// Problem with the intersecting cells and title rows:
//	If a row is a title row only {row,1} should be drawn, no matter what cells are visible.
void _BLDrawIntersectingCells( const Rect* inUpdateRect, BlugsRef inList )
{
	Rect		drawRect, cellRect;
	BLCellRange	visCells;
	BLCell		cell;
	
	visCells = (*inList)->visCells;
	//if (!bl_NRows(list) || !bl_NCols(list)) return;
	// It's not too hard to end up with a zero in one of the visCells fields
	// This routines cause an assertion if that's the case
	if (visCells.left && visCells.top && visCells.right && visCells.bottom)
	{
		for (cell.row = visCells.top; cell.row <= visCells.bottom; cell.row++)
		{
			UInt16			startCol, endCol;
			
			if (bl_Test(_BLGetRowFlags( cell.row, inList ),blRowIsTitleRow))
			{
				startCol = 1;
				endCol = 1;
			}
			else
			{
				startCol = visCells.left;
				endCol = visCells.right;
			}	
			for (cell.col = startCol; cell.col <= endCol; cell.col++)
			{
				OSErr	err;
				
				err = _BLCellRect( cell, &cellRect, inList );
				if (!err && SectRect( &cellRect, inUpdateRect, &drawRect ))
				{
					ClipRect( &drawRect );
					_BLDrawCell( blCellDrawNoOptions, cell, &cellRect, inList );
				}
			}
		}
	}
}

//===============================
// Draw a cell in the current GWorld.
// All routines that call this one validate the list handle; no need to do again.
// Do not call this routine without doing the setup work (clip, set offscreen, etc.)
// The drawTriangle param is false only when toggling disclosure triangles and making
//	drag image; need to blit the background without a triangle in those cases.
void _BLDrawCell( BLCellDrawOptions inOptions, BLCell inCell, const Rect* inRect, BlugsRef inList )
{
	OSErr		err;
	
	err = _BLValidateCell( inCell, inList );
	if (!err)
	{
		// If disclosure, and the cell is undisclosed, we don't want to draw
		if (!EmptyRect( inRect ))
		{
			UInt16			rowDataFlags;
			
			rowDataFlags = _BLGetRowFlags( inCell.row, inList );
			// We refuse to draw a title row cell, except the first one
			if (!bl_Test(rowDataFlags,blRowIsTitleRow) || inCell.col == 1)
			{
				RGBColor		saveFore, saveBack;
				BLCellUID		cellUID;
			#if __TARGET_RT_MAC_MACHO__
				UInt32	saveFlags;
				
				if (bl_Jaguar) saveFlags = QDSwapTextFlags( kQDSupportedFlags );
			#endif
				(void)BLGetCellUID( inCell, &cellUID, inList );
				GetForeColor( &saveFore );
				GetBackColor( &saveBack );
				// See if this column or row is being dragged, and if so draw in a
				//	grayed-out state with no content.
				if (!local_BLDragging( inCell, inRect, inList ))
				{
					Boolean				selected, active, doingInlineEditCell;
					BLIndexPtr			index;
					BLContentType		content;
					BLHiliteProcPtr		hiliteProc;
					UInt32				features;
					BLHandlerParamBlock	pb;
					
					active = bl_Active(inList);
					index = bl_CellIndexAddr(inCell,inList);
					selected = bl_Selected(index);
					content = _BLGetCellContentType( inCell, inList );
					local_BLDrawBackground( inCell, inRect, inList );
					hiliteProc = (*inList)->callbacks.hiliteProc;
					doingInlineEditCell = (bl_InlineNow(inList) &&
											_BLEqualCellUID( &bl_InlineCell(inList), &cellUID ));
					err = _BLGetHandlerFeatures( content, &features );
					pb.inRect = *inRect;
					// Let handler do its hiliting only if there is no hilite proc.
					pb.inIsSelected = (!hiliteProc && selected);
					pb.inIndent = _BLIndent( inCell, inList );
					// Draw hiliting one way or another
					// If it's the inline cell, don't hilite, instead draw focus.
					if (!doingInlineEditCell && selected)
					{
						if (hiliteProc) (*hiliteProc)( inRect, inList );
						else if (!bl_Test(features,blWantsHilite))
							local_BLHiliteCell( inCell, &pb, features, inList );
					}
					// Draw disclosure triangle if needed
					if (inCell.col == (*inList)->disclosureColumn)
					{
						if (bl_Test(rowDataFlags,blRowHasChildren) &&
							!bl_Test(inOptions,blCellDrawSuppressTriangle))
						{
							if (bl_Disclosure(inList))
							{
								Rect						triangleRect;
								BLDisclosureTriangleState	triangleState = 0;
								
								triangleRect = _BLTriangleRect( *inRect,
									_BLGetRowDisclosureLevel( inCell.row, inList ), inList ); 
								if (bl_Test(rowDataFlags,blRowIsExpanded)) bl_Set(triangleState,blTriStateDownBit);
								if (bl_Test(rowDataFlags,blTrianglePressed)) bl_Set(triangleState,blTriStatePressedBit);
								_BLDrawDisclosureTriangle( inCell, triangleRect, triangleState, active, inList );
							}
						}
					}
					// Draw the cell
					if (content)
					{
						UInt32			answer;
					
						local_BLSetCellTextColor( active );
						answer = _BLCallHandlerForCell( blCellDrawMsg, inCell, &pb, inList );
						if (doingInlineEditCell)
						{
							if (bl_Test(features,blWantsInlineFocus))
							{
								RgnHandle	rgn = NewRgn();
								Rect		focusRect;
								
								_BLInlineRegion( rgn, inCell, inRect, inList );
								GetRegionBounds( rgn, &focusRect );
								local_BLDrawFocus( focusRect, true );
								DisposeRgn( rgn );
							}
						}
					}
					// Draw divider lines between cells.
					if (active && !bl_Test(inOptions,blCellDrawSuppressBorders))
					{
					#if TARGET_RT_MAC_MACHO
						local_BLDrawCellBordersCG( inCell, *inRect, inList );
					#else
						local_BLDrawCellBorders( inCell, inRect, inList );
					#endif
					}
				} // not dragging cell
				RGBForeColor( &saveFore );
				RGBBackColor( &saveBack );
			#if __TARGET_RT_MAC_MACHO__
				if (bl_Jaguar) saveFlags = QDSwapTextFlags( saveFlags );
			#endif
			} // not invisible title row column
		} // cell has height
	} // valid cell
}

#pragma mark -
//===============================
// Set to normal black if active, dimmed text color if inactive.
static void local_BLSetCellTextColor( Boolean inIsActive )
{
	if (bl_Appearance)
	{
		GDHandle	dev;
		
		// Appearance doesn't seem to have a dimming effect for inactive list text. Weird.
		// We'll try to use a compatible inactive color.
		dev = GetGDevice();
		SetThemeTextColor( (inIsActive) ? kThemeTextColorListView : kThemeTextColorWindowHeaderInactive,
							(*(*dev)->gdPMap)->pixelSize, (*(*dev)->gdPMap)->pixelSize > 1 );
	}
	else RGBForeColor( (inIsActive) ? &_00Col : &_77Col );
}

//===============================
// Try to mimic the Finder. Black if active, white if pressed, neutral grey that
//	shows up on a dimmed bevel button if inactive.
static void local_BLSetTitleTextColor( ThemeDrawState inState )
{
	if (bl_Appearance)
	{
		GDHandle		dev;
		ThemeTextColor	color;
		
		dev = GetGDevice();
		if (inState == kThemeStateActive) color = kThemeTextColorBevelButtonActive;
		else if (inState == kThemeStateInactive) color = kThemeTextColorBevelButtonInactive;
		else color = kThemeTextColorBevelButtonPressed;
		SetThemeTextColor( color, (*(*dev)->gdPMap)->pixelSize, (*(*dev)->gdPMap)->pixelSize > 1 );
	}
	else
	{
		const RGBColor*	colorP;
		
		if (inState == kThemeStateActive) colorP = &_00Col;
		else if (inState == kThemeStateInactive) colorP = &_77Col;
		else colorP = &_FFCol;
		RGBForeColor( colorP );
	}
}

#if !TARGET_RT_MAC_MACHO
//===============================
// Let's try this just for the heck of it: no borders if inactive.
static void local_BLDrawCellBorders( BLCell inCell, const Rect* inRect, BlugsRef inList )
{
	Boolean			drawRowBorder, drawColBorder;
	
	PenNormal();
	drawRowBorder = bl_Test(_BLGetRowFlags( inCell.row, inList ),blRowDrawBorder);
	drawColBorder = bl_Test(_BLGetColumnFlags( inCell.col, inList ),blColumnDrawBorder);
	if (drawRowBorder || drawColBorder)
	{
		BLBorderProcPtr	borderProc;
		
		borderProc = (*inList)->callbacks.borderProc;
		if (borderProc) (*borderProc)( drawRowBorder, drawColBorder, inCell, inRect, inList );
		else
		{
		#if !TARGET_API_MAC_CARBON
			if (bl_Appearance)
		#endif
			{
				GDHandle			dev;
				
				dev = GetGDevice();
				SetThemePen( kThemeBrushListViewSeparator, (**(**dev).gdPMap).pixelSize,
							(**(**dev).gdPMap).pixelSize > 1 );
			}
			// Since the background is drawn in some kind of grey color w/ or w/o Appearance,
			//	a white separator like Platinum appr should be less obtrusive than black.
		#if !TARGET_API_MAC_CARBON
			else RGBForeColor( &_FFCol );
		#endif
			if (drawRowBorder)
			{
				MoveTo( inRect->left, inRect->bottom - 1 );
				LineTo( inRect->right - 1, inRect->bottom - 1 );
			}
			if (drawColBorder)
			{
				MoveTo( inRect->right - 1, inRect->bottom - 1 );
				LineTo( inRect->right - 1, inRect->top - 1 );
			}
		}
	}
}

#else
//===============================
static void local_BLDrawCellBordersCG( BLCell inCell, Rect inRect, BlugsRef inList )
{
	Boolean			drawRowBorder, drawColBorder;
	
	drawRowBorder = bl_Test(_BLGetRowFlags( inCell.row, inList ),blRowDrawBorder);
	drawColBorder = bl_Test(_BLGetColumnFlags( inCell.col, inList ),blColumnDrawBorder);
	if (drawRowBorder || drawColBorder)
	{
		BLBorderProcPtr	borderProc;
		
		borderProc = (*inList)->callbacks.borderProc;
		if (borderProc) (*borderProc)( drawRowBorder, drawColBorder, inCell, &inRect, inList );
		else
		{
			GWorldPtr		world;
			GDHandle		dev;
			CGContextRef	context;
			RGBColor		rgb;
			Rect			r, viewRect;
			SInt32			ht;
			
			GetGWorld( &world, &dev );
			GetPortBounds( world, &r );
			ht = r.bottom - r.top;
			(void)GetThemeBrushAsColor( kThemeBrushListViewSeparator, (**(**dev).gdPMap).pixelSize,
										(**(**dev).gdPMap).pixelSize > 1, &rgb );
			QDBeginCGContext( world, &context );
			viewRect = _BLViewRect( inList );
			if (inRect.left <= viewRect.left) inRect.left = viewRect.left + 1;
			if (inRect.top <= viewRect.top) inRect.top = viewRect.top + 1;
			CGContextSetLineWidth( context, 1.0 );
			CGContextBeginPath( context );
			if (drawRowBorder)
			{
				CGContextMoveToPoint( context, inRect.left, ht - inRect.bottom );
				CGContextAddLineToPoint( context, inRect.right - 1, ht - inRect.bottom );
			}
			if (drawColBorder)
			{
				CGContextMoveToPoint( context, inRect.right - 1, ht - inRect.bottom );
				CGContextAddLineToPoint( context, inRect.right - 1, ht - inRect.top );
			}
			CGContextClosePath( context );
			CGContextSetLineCap( context, kCGLineCapSquare );
			CGContextSetRGBStrokeColor( context, (float)rgb.red / 65536.0,
										(float)rgb.green / 65536.0, (float)rgb.blue / 65536.0, 1.0 );
			CGContextDrawPath( context, kCGPathStroke );
			QDEndCGContext( world, &context );
		}
	}
}
#endif

//===============================
// Returns true if it drew the cell in a contentless grayed-out state because row or
//	column is being dragged.
static Boolean local_BLDragging( BLCell inCell, const Rect* inRect, BlugsRef inList )
{
	Boolean	beingDragged = false;
	
#if !__BL_USE_CARBON_API__
	if ( bl_TranslucentDrags )
#endif
	{
		BLTitlePtr		title;
		BLTitleBarHdl	titleBar;
		
		titleBar = bl_HTitle(inList);
		if (titleBar) 
		{
			title = bl_NthTitleAddr( inCell.col, titleBar );
			beingDragged = bl_Test( title->flags, blTitleBeingDragged );
		}
		if (!beingDragged)
		{
			titleBar = bl_VTitle(inList);
			if (titleBar)
			{
				title = bl_NthTitleAddr( inCell.row, titleBar );
				beingDragged = bl_Test( title->flags, blTitleBeingDragged );
			}
		}
		if (beingDragged)
		{
			// Temporarily clip to the entire cell rectangle
			RgnHandle	saveClip;
			
			saveClip = _BLClipToViewRectIntersection( inRect, inList );
			if (saveClip)
			{
			#if __BL_USE_CARBON_API__
				Pattern		pattern; // Needed but spurious for Carbon qd accessors
				
				if (bl_Aqua)
				{
					GDHandle	dev;
			
					// Use the appearance darker color for sorted columns when appropriate
					dev = GetGDevice();
					SetThemeBackground( kThemeBrushListViewBackground,
										(*(*dev)->gdPMap)->pixelSize,
										(*(*dev)->gdPMap)->pixelSize > 1 );
					EraseRect( inRect );
				}
				else
			#endif
				{
					ForeColor( whiteColor );
					PaintRect( inRect );
					ForeColor( blackColor );
					PenPat( GetQDGlobalsLightGray( &pattern ) );
					PaintRect( inRect );
					PenPat( GetQDGlobalsBlack( &pattern ) );
				}
				// We'll restore the foreground color in the calling routine.
				SetClip( saveClip );
				DisposeRgn( saveClip );
			}
		}
	}
	return beingDragged;
}

//===============================
// If the content handler returns an empty region, we use the entire cell rect to do
//	the hiliting.
static void local_BLHiliteCell( BLCell inCell, BLHandlerParamPtr inParamP,
								UInt32 inHandlerFeatures, BlugsRef inList )
{
	RgnHandle		rgn = nil;
	Rect			r;
	Boolean			restoreBack = false;
	//OSErr			err = noErr;
			
	r = inParamP->inRect;
	if (bl_Test(inHandlerFeatures,blWantsRegion))
	{
		rgn = NewRgn();
		if (rgn)
		{
			inParamP->inRegion = rgn;
			_BLCallHandlerForCell( blCellRegionMsg, inCell, inParamP, inList );
			if (EmptyRgn( rgn ))
			{
				DisposeRgn( rgn );
				rgn = nil;
			}
			restoreBack = true;
		}
	}
	if (rgn)
	{
		local_BLDrawHiliteRgn( restoreBack, bl_Active(inList), rgn );
		DisposeRgn( rgn );
	}
	else local_BLDrawHiliteRect( restoreBack, bl_Active(inList), &r );
}

//===============================
void BLDrawHiliteRect( Boolean inActive, const Rect* inRect )
{
	local_BLDrawHiliteRect( true, inActive, inRect );
}

//===============================
void BLDrawHiliteRgn( Boolean inActive, RgnHandle inRgn )
{
	local_BLDrawHiliteRgn( true, inActive, inRgn );
}

//===============================
// Under pre-OS X 10.1 we, if inactive, use a hollow frame in the LM hilite color.
// 10.1 and later, we use the Theme brushes and always draw a solid rect.
static void local_BLDrawHiliteRect( Boolean inRestoreBack, Boolean inActive,
									const Rect* inRect )
{
	RgnHandle		rgn = nil;
	RGBColor		saveBack, hiliteCol;
	OSErr			err = paramErr;
	
	GetBackColor( &saveBack );
#if TARGET_API_MAC_CARBON
	if (bl_OSVers >= 0x1010)
	{
		GDHandle	dev;
		
		dev = GetMainDevice();
		err = GetThemeBrushAsColor( inActive ? kThemeBrushPrimaryHighlightColor :
									kThemeBrushSecondaryHighlightColor,
									(*(*dev)->gdPMap)->pixelSize,
									(*(*dev)->gdPMap)->pixelSize > 1,
									&hiliteCol );
  	}
	if (err)
#endif
		LMGetHiliteRGB( &hiliteCol );
	// This is for the disclosure triangle under Themes:
	//	we want to erase to the hilite color if possible.
	RGBBackColor( &hiliteCol );
#if TARGET_API_MAC_CARBON
	if (bl_OSVers < 0x1010)
#endif
	if (!inActive)
	{
		Rect		smallerRect;
		rgn = NewRgn();
		if (rgn)
		{
			smallerRect = *inRect;
			InsetRect( &smallerRect, 1, 1 );
			RectRgn( rgn, inRect );
			_BLRgnMinusRect( rgn, &smallerRect );
			// 11/3/2001 Moses -->1.1
			// When drawing outline hilite we do want to restore,
			//	otherwide DrawThemeButton may erase to saved inactive hilite
			//	color, which will typically not cover the same region as
			//	a disclosure triangle.
			inRestoreBack = true;
		}
	}
	if (!rgn) EraseRect( inRect );
	else
	{
		EraseRgn( rgn );
		DisposeRgn( rgn );
	}
	if (inRestoreBack) RGBBackColor( &saveBack );
}

//===============================
// Under pre-OS X 10.1 we, if inactive, use a hollow frame in the LM hilite color.
// 10.1 and later, we use the Theme brushes and always draw a solid rect.
static void local_BLDrawHiliteRgn( Boolean inRestoreBack, Boolean inActive, RgnHandle inRegion )
{
	RgnHandle		rgn = nil;
	RGBColor		saveBack, hiliteCol;
	OSErr			err = paramErr;
	
	GetBackColor( &saveBack );
#if TARGET_API_MAC_CARBON
	if (bl_OSVers >= 0x1010)
	{
		GDHandle	dev;
		
		dev = GetMainDevice();
		err = GetThemeBrushAsColor( inActive ? kThemeBrushPrimaryHighlightColor :
									kThemeBrushSecondaryHighlightColor,
									(*(*dev)->gdPMap)->pixelSize,
									(*(*dev)->gdPMap)->pixelSize > 1,
									&hiliteCol );
  	}
	if (err)
#endif
		LMGetHiliteRGB( &hiliteCol );
	// This is for the disclosure triangle under Themes:
	//	we want to erase to the hilite color if possible.
	RGBBackColor( &hiliteCol );
#if TARGET_API_MAC_CARBON
	if (bl_OSVers < 0x1010)
#endif
	if (!inActive)
	{
		rgn = NewRgn();
		CopyRgn( inRegion, rgn );
		InsetRgn( rgn, 1, 1 );
		DiffRgn( inRegion, rgn, rgn );
		// 11/3/2001 Moses -->1.1
		// When drawing outline hilite we do want to restore,
		//	otherwide DrawThemeButton may erase to saved inactive hilite
		//	color, which will typically not cover the same region as
		//	a disclosure triangle.
		inRestoreBack = true;
	}
	if (!rgn) EraseRgn( inRegion );
	else
	{
		EraseRgn( rgn );
		DisposeRgn( rgn );
	}
	if (inRestoreBack) RGBBackColor( &saveBack );
}

//===============================
// Hey! Assumes the correct port has been set up
// This can be called with a null cell when drawing the blank rects.
// 10/9/2001 Moses -->1.1
// BLBackgroundProcPtr can return OSErr != noErr to make Blugs draw default background.
void local_BLDrawBackground( BLCell inCell, const Rect* inRect, BlugsRef inList )
{
	BLBackgroundProcPtr	proc;
	Boolean				validCell, sortCol = false; // Is this the sorted column?
	OSErr				err = eventNotHandledErr;
	
	validCell = bl_ValidCell(inCell);
	if (validCell && bl_Sortable(inList))
		if (inCell.col == BLGetColumnFromUID( &(*inList)->primarySortColumn, inList )) sortCol = true;
	proc = (*inList)->callbacks.backgroundProc;
	// Only allow the background proc for real cells.
	if (validCell && proc) err = (*proc)( sortCol, inCell, inRect, inList );
	if (err)
	{
		if (bl_Appearance)
		{
			GDHandle	dev;
			
			// Use the appearance darker color for sorted columns when appropriate
			dev = GetGDevice();
			SetThemeBackground( (sortCol) ? kThemeBrushListViewSortColumnBackground :
								kThemeBrushListViewBackground,
								(*(*dev)->gdPMap)->pixelSize, (*(*dev)->gdPMap)->pixelSize > 1 );
		}
		else RGBBackColor( (sortCol) ? &_DDCol : &_EECol );
		EraseRect( inRect );
	}
}

//===============================
static Boolean local_BLGetBottomBlankRect( Rect* outRect, BlugsRef inList )
{
	UInt16		filledHt;
	Rect		viewRect;
	Boolean		hasBlank;
	
	viewRect = _BLViewRect( inList );
	filledHt = bl_TotalHt(inList) - bl_VScrollLoc(inList);
	if (filledHt >= bl_Height(viewRect)) hasBlank = false;
	else
	{
		*outRect = viewRect;
		outRect->top += filledHt;
		hasBlank = true;
	}
	return hasBlank;
}

//===============================
static Boolean local_BLGetRightBlankRect( Rect* outRect, BlugsRef inList )
{
	UInt16		filledWd;
	Rect		viewRect;
	Boolean		hasBlank;
	
	viewRect = _BLViewRect( inList );
	filledWd = bl_TotalWd(inList) - bl_HScrollLoc(inList);
	if (filledWd >= bl_Width(viewRect)) hasBlank = false;
	else
	{
		*outRect = viewRect;
		outRect->left += filledWd;
		hasBlank = true;
	}
	return hasBlank;
}


#pragma mark -
#pragma mark ¥¥¥Title Bar Drawing¥¥¥
//===============================
static void local_BLDrawIntersectingTitles( Boolean inVertical, const Rect* inUpdateRect,
											BlugsRef inList )
{
	Rect			unclaimedRect, topLeftRect;
	BLCellRange		visCells;
	UInt16			title, endTitle, nRows, nCols;
	BLTitleBarHdl	theBar;
	Boolean			drawTitles = true, drawTopLeft = false;
	
	visCells = (*inList)->visCells;
	nRows = bl_NRows(inList);
	nCols = bl_NCols(inList);
	if (inVertical)
	{
		theBar = bl_VTitle(inList);
		title = visCells.top;
		endTitle = visCells.bottom;
		if (!nRows) drawTitles = false;
		// Check for top left corner
		if (bl_HTitle(inList))
		{
			drawTopLeft = true;
			(void)_BLTitleRect( 0, theBar, &topLeftRect, inList );
		}
	}
	else
	{
		theBar = bl_HTitle(inList);
		title = visCells.left;
		endTitle = visCells.right;
		if (!nCols) drawTitles = false;
	}
	if (drawTitles)
	{
		// It's not too hard to end up with a zero in one of the visCells fields
		if (title && endTitle)
		{
			Rect		drawRect, titleRect;
			
			for (; title <= endTitle; title++)
			{
				OSErr	err;
				
				err = _BLTitleRect( title, theBar, &titleRect, inList );
				if (!err && SectRect( &titleRect, inUpdateRect, &drawRect ))
				{
					ClipRect( &drawRect );
					_BLDrawTitle( title, theBar, inList );
				}
			}
		}
	}
	if (drawTopLeft)
	{
		ClipRect( &topLeftRect );
		_BLDrawTitle( 0, theBar, inList );
	}
	if (_BLTitleBarUnclaimedRect( inVertical, theBar, &unclaimedRect, inList ))
	{
		ThemeDrawState		state;
		
		ClipRect( &unclaimedRect );
		state = (bl_Active(inList)) ? kThemeStateActive : kThemeStateInactive;
		local_BLDrawBevelButton( (inVertical) ? blVTitleBarFillerPart : blHTitleBarFillerPart,
										0, &unclaimedRect, state, kThemeButtonOff, inList );
	}
}

//===============================
void _BLUpdateBarOnly( BLTitleBarHdl inBar, BlugsRef inList )
{
	RgnHandle	tbRgn;
	Rect		tbRect;
	
	tbRgn = NewRgn();
	tbRect = _BLTitleBarRect( inBar, inList );
	RectRgn( tbRgn, &tbRect );
	_BLUpdateWorld( blUpdateBlit, tbRgn, inList );
	DisposeRgn( tbRgn );
}

//===============================
void _BLUpdateTitle( UInt16 inTitle, BLTitleBarHdl inBar, BlugsRef inList )
{
	Rect		titleRect;
	OSErr		err;
	
	err = _BLTitleRect( inTitle, inBar, &titleRect, inList );
	if (!err)
	{
		Rect		r;
		
		r = _BLOffscreenRect( inList );
		if (SectRect( &r, &titleRect, &titleRect ))
		{
			RgnHandle	titleRgn = NewRgn();
			
			RectRgn( titleRgn, &titleRect );
			_BLUpdateWorld( blUpdateBlit, titleRgn, inList );
			DisposeRgn( titleRgn );
		}
	}
}

//===============================
void _BLDrawTitle( UInt16 inTitle, BLTitleBarHdl inBar, BlugsRef inList )
{
	Rect		titleRect;
	OSErr		err;
	
	err = _BLTitleRect( inTitle, inBar, &titleRect, inList );
	if (!err && !EmptyRect(&titleRect))
	{
		UInt16				titleFlags;
		RGBColor			saveFore, saveBack;
		ThemeDrawState		state;
		BLHandlerParamBlock	pb;
		
		// Fake title flags if top left corner.
		if (inTitle) titleFlags = bl_NthTitleFlags(inTitle,inBar);
		else titleFlags = 0;
		// Prepare to draw the background or erase.
		GetForeColor( &saveFore );
		GetBackColor( &saveBack );
		ForeColor( blackColor );
		//RGBForeColor( &_00Col );
		RGBBackColor( &_FFCol );
		// If we're dragging a translucent image of this title, draw the title in a grayed out state.
		if (bl_TranslucentDrags && bl_Test(titleFlags,blTitleBeingDragged))
		{
			RgnHandle	saveClip;
			
			#if __BL_USE_CARBON_API__
			Pattern			pattern;
			#endif
			
			saveClip = NewRgn();
			if (saveClip)
			{
				Rect	barRect;
				
				barRect = _BLTitleBarRect( inBar, inList );
				GetClip( saveClip );
				ClipRect( &barRect );
			}
			if (bl_Aqua)
			{
				//RGBForeColor( &_9CCol );
				//PaintRect( &titleRect );
				local_BLDrawBevelButton( (bl_BarVert(inBar)) ? blVTitleBarTitlePart : blHTitleBarTitlePart,
										inTitle, &titleRect, kThemeStateActive, kThemeButtonOff, inList );
			}
			else
			{
				InsetRect( &titleRect, 1, 1 );
				EraseRect( &titleRect );
				PenPat( GetQDGlobalsLightGray( &pattern ) );
				PaintRect( &titleRect );
				PenPat( GetQDGlobalsBlack( &pattern ) );
				InsetRect( &titleRect, -1, -1 );
				FrameRect( &titleRect );
			}
			if (saveClip)
			{
				SetClip( saveClip );
				DisposeRgn( saveClip );
			}
		}
		else
		{
			ThemeButtonValue	value = kThemeButtonOff;
			BLCell				cell = {0,0};
			Boolean				listActive;
			BLPart				part;
			
			listActive = bl_Active(inList);
			state = (listActive) ? kThemeStateActive : kThemeStateInactive;
			if (inTitle)
			{
				UInt16	selectedTitle;
				
				if (bl_BarVert(inBar))
				{
					part = blVTitleBarTitlePart;
					cell.row = inTitle;
					selectedTitle = BLGetRowFromUID( &(*inBar)->whichSelected, inList );
				}
				else
				{
					part = blHTitleBarTitlePart;
					cell.col = inTitle;
					selectedTitle = BLGetColumnFromUID( &(*inBar)->whichSelected, inList );
				}
				if (inTitle == selectedTitle) value = kThemeButtonOn;
				if (bl_Test(titleFlags,blTitleTempSelected)) state = kThemeStatePressed;
			}
			else
			{
				inBar = bl_VTitle(inList);
				part = blTopLeftPart;
			}
			local_BLDrawBevelButton( part, inTitle, &titleRect, state, value, inList );
			local_BLSetTitleTextColor( (state == kThemeStatePressed) ?
										state : ((listActive) ? kThemeStateActive : kThemeStateInactive ));
			//RGBForeColor( (bl_Test(titleFlags,blTitleTempSelected)) ? &_FFCol :
			//				((listActive) ? &_00Col : &_73Col ) );
			// Handler takes responsibility for drawing inside bevels.
			pb.inIndent = _BLIndent( cell, inList );
			pb.inRect = titleRect;
			_BLCallHandlerForTitle( blCellDrawMsg, inTitle, &pb, inBar, inList );
		} // not dragging
		RGBForeColor( &saveFore );
		RGBBackColor( &saveBack );
	} // title rect not empty
}

#pragma mark -
#pragma mark ¥¥¥Utility¥¥¥
//===================================
// Draws the appropriate button; could be the draw proc installed by user.
static void local_BLDrawBevelButton( BLPart inPart, UInt16 inTitle, const Rect* inRect,
									ThemeDrawState inState, ThemeButtonValue inValue, BlugsRef inList )
{
	BLBevelButtonProcPtr	proc;
	RGBColor				saveFore, saveBack;
	Boolean					drawn = false;
	
	GetForeColor( &saveFore );
	GetBackColor( &saveBack );
	proc = (*inList)->callbacks.bevelButtonProc;
	if (proc) drawn = (*proc)( inPart, inTitle, inRect, inState, inValue, inList );
	if (!drawn)
	{
		Rect		drawingRect;	// May draw top or left outside of this to avoid gutters.
		RgnHandle	saveClip = NewRgn();
		
		GetClip( saveClip );
		drawingRect = *inRect;
	#if !TARGET_RT_MAC_MACHO
		if (!bl_Aqua && bl_BorderMetrics(inList))
		{
			if (inPart == blVTitleBarTitlePart || inPart == blVTitleBarFillerPart ||
				inPart == blTopLeftPart) drawingRect.left--;
			if (inPart != blVTitleBarTitlePart && inPart != blVTitleBarFillerPart) drawingRect.top--;
		}
	#endif
		if (inPart == blSortButtonPart) ClipRect( inRect );
	#if __BL_USE_CARBON_API__
		if (bl_Aqua && (inPart == blHTitleBarTitlePart ||
						inPart == blHTitleBarFillerPart ||
						inPart == blTopLeftPart ||
						inPart == blSortButtonPart))
		{
			ThemeButtonDrawInfo	newInfo;
			
			newInfo.state = inState;
			newInfo.value = inValue;
			newInfo.adornment = 0;
			if (inPart == blHTitleBarTitlePart || inPart == blHTitleBarFillerPart)
			{
				BLTitleBarHdl	hBar;
				UInt16			whichSelected;
				
				if (inTitle > 0) drawingRect.left--;
				hBar = bl_HTitle(inList);
				whichSelected = BLGetColumnFromUID( &(*hBar)->whichSelected, inList );
				if (whichSelected)
				{
					// If regular (non-filler) title, check if selected title is one less
					//	or one greater than this one.
					if (inPart == blHTitleBarTitlePart)
					{
						if (whichSelected == inTitle - 1)
							newInfo.adornment = kThemeAdornmentHeaderButtonLeftNeighborSelected;
						else if (whichSelected == inTitle + 1)
							newInfo.adornment = kThemeAdornmentHeaderButtonRightNeighborSelected;
						else if (whichSelected == inTitle)
						{
							if (!bl_Test((*inList)->sortState,blSortStateLargeToSmall))
								newInfo.adornment |= kThemeAdornmentHeaderButtonSortUp;
						}
					}
					else
					{
						if (whichSelected == bl_NCols(inList))
							newInfo.adornment = kThemeAdornmentHeaderButtonLeftNeighborSelected;
					}
				}
			}
			//BackColor( whiteColor );
			//EraseRect( &drawingRect );
			//newInfo.adornment |= kThemeAdornmentHeaderButtonNoShadow;
			DrawThemeButton( &drawingRect, kThemeListHeaderButton, &newInfo, nil, nil, nil, 0 );
			// Draw grey line under the button.
			if (!bl_Jaguar)
			{
				MoveTo( drawingRect.left, drawingRect.bottom - 1 );
				RGBForeColor( &_44Col );
				LineTo( drawingRect.right - 1, drawingRect.bottom - 1 );
			}
		}
		else
	#endif // __BL_USE_CARBON_API__
		BLDrawBevelButton( &drawingRect, inState, inValue );
		SetClip( saveClip );
		DisposeRgn( saveClip );
	}
	RGBForeColor( &saveFore );
	RGBBackColor( &saveBack );
	PenNormal();
}


//===================================
// This is the 'small' variety.
// State can be kThemeStateActive, kThemeStatePressed, or kThemeStateInactive.
// Value can be kThemeButtonOn or kThemeButtonOff.
BL_PROC void BLDrawBevelButton( const Rect* inRect, ThemeDrawState inState,
								ThemeButtonValue inValue )
{
	if (bl_AllegroAppearance)
	{
		ThemeButtonDrawInfo	newInfo;
		
	#if __BL_USE_CARBON_API__
		// This is to get rid of garbage background pixels since the buttons are translucent.
		if (bl_Aqua)
		{
			ForeColor( whiteColor );
			PaintRect( inRect );
		}
	#endif
		newInfo.state = inState;
		newInfo.value = inValue;
		newInfo.adornment = 0;
		DrawThemeButton( inRect, kThemeSmallBevelButton, &newInfo, nil, nil, nil, 0 );
	}
#if !TARGET_RT_MAC_MACHO
	else
	{
		MoveTo( inRect->left, inRect->bottom - 1 );
		
		if (inState == kThemeStateInactive)
		{
			SInt16	rMinus1, bMinus1;
			
			rMinus1 = inRect->right - 1;
			bMinus1 = inRect->bottom - 1;
			
			// Paint entire area
			RGBForeColor( (inValue == kThemeButtonOn) ? &_BBCol : &_DDCol );
			PaintRect( inRect );
			// Left and top outer line
			RGBForeColor( (inValue == kThemeButtonOn) ? &_77Col : &_AACol );
			LineTo( inRect->left, inRect->top );
			LineTo( rMinus1, inRect->top );
			// Right bottom outer line
			RGBForeColor( &_99Col );
			LineTo( rMinus1, bMinus1 );
			LineTo( inRect->left + 1, bMinus1 );
		}
		else // active or pressed
		{
			SInt16	hMinus2, hMinus4, wMinus2, wMinus4;
			
			// Moses 10/21/2000
			// kThemeButtonOn is, for our own drawing, exactly the same as kThemeStatePressed.
			// So we map it to kThemeStatePressed.
			if (inValue == kThemeButtonOn) inState = kThemeStatePressed;
			hMinus2 = blp_Height(inRect) - 2;
			hMinus4 = hMinus2 - 2;
			wMinus2 = blp_Width(inRect) - 2;
			wMinus4 = wMinus2 - 2;
			
			// Paint entire area
			RGBForeColor( (inState == kThemeStatePressed) ? &_88Col : &_CCCol );
			PaintRect( inRect );
			// Bottom left and top right pixels
			RGBForeColor( (inState == kThemeStatePressed) ? &_22Col : &_55Col );
			FrameRect( inRect );
			// Left and top outer line
			Move( 0, -1 );
			RGBForeColor( (inState == kThemeStatePressed) ? &_11Col : &_66Col );
			Line( 0, -hMinus2 );
			Line( wMinus2, 0 );
			// Right bottom outer line
			Move( 1, 1 );
			RGBForeColor( (inState == kThemeStatePressed) ? &_44Col : &_33Col );
			Line( 0, hMinus2 );
			Line( -wMinus2, 0 );
			// Left and top inner line
			Move( 0, -2 );
			RGBForeColor( (inState == kThemeStatePressed) ? &_55Col : &_FFCol );
			Line( 0, -hMinus4 );
			Line( wMinus4, 0 );
			// Right bottom inner line
			RGBForeColor( (inState == kThemeStatePressed) ? &_AACol : &_88Col );
			Move( 1, 1 );
			Line( 0, hMinus4 );
			Line( -wMinus4, 0 );
		}
	}
#endif
}

//===================================
// If inGrow is true, then it's a grow box, otherwise it's a no-grow box.
// Use Appearance 1.1 if available, otherwise use bevel button and draw texture
//	if it's a grow box.
static void local_BLDrawGrowBox( Boolean inIsActive, Boolean inGrow, BlugsRef inList )
{
	Boolean			small;
	ThemeDrawState	state;
	Rect			r;
	
	r = _BLGrowBoxRect( inList );
	state = (inIsActive) ? kThemeStateActive : kThemeStateInactive;
	small = bl_SmallScroll(inList);
	if (bl_AllegroAppearance)
	{
		Point		origin;
		OSStatus	status;
		
	#if __BL_USE_CARBON_API__
		if (bl_Aqua) OffsetRect( &r, -1, -1 );
	#endif
		origin.h = r.left;
		origin.v = r.top;
		if (inGrow)
		{
		#if __BL_USE_CARBON_API__
			if (bl_Aqua)
			{
				// Don't ask me why this works.
				// Aqua often ignores the call to DrawThemeStandaloneGrowBox
				//	unless we do this.
				RGBForeColor( &_FFCol );
				PaintRect( &r );
			}
		#endif
			status = DrawThemeStandaloneGrowBox( origin, kThemeGrowRight | kThemeGrowDown,
														small, state );
		}
		else
		{
			// I get resNotFound on Aqua 10.0.3 so I don't think standalone grow boxes
			//	are supported there. We'll use DrawThemePlacard instead.
		#if __BL_USE_CARBON_API__
			if (bl_Aqua) BLDrawPlacard( &r, state );
			else
		#endif
			status = DrawThemeStandaloneNoGrowBox( origin, kThemeGrowRight | kThemeGrowDown,
													small, state );
		}
	}
	else
	{
		local_BLDrawBevelButton( blGrowBoxPart, 0, &r, state, kThemeButtonOff, inList );
		if (inGrow && inIsActive)
		{
			PenNormal();
			ForeColor( blackColor );
			//RGBForeColor( &_00Col );
			// Outline needs to be black; Blugs frames bevel buttons w/ grey
			FrameRect( &r );
			RGBForeColor( &_FFCol );
			if (!small)
			{
				MoveTo( r.left + 8, r.top + 4 );
				Line( -1, 0 );
				Line( -3, 3 );
				Move( 6, -1 );
				Line( -1, 0 );
				Line( -3, 3 );
				RGBForeColor( &_CCCol );
				Move( 1, -4 );
				Line( -2, 2 );
				Move( 4, 0 );
				Line( -2, 2 );
				RGBForeColor( &_77Col );
				Move( 1, -4 );
				Line( -3, 3 );
				Move( 5, -1 );
				Line( -3, 3 );
				RGBForeColor( &_AACol );
				Move( 2, -5 );
				Line( -3, 3 );
			}
			else
			{
				MoveTo( r.left + 5, r.top + 2 );
				Line( -1, 0 );
				Line( -2, 2 );
				Move( 5, 0 );
				Line( -1, 0 );
				Line( -2, 2 );
				RGBForeColor( &_CCCol );
				Move( 0, -3 );
				Line( -1, 1 );
				Move( 3, 1 );
				Line( -1, 1 );
				RGBForeColor( &_77Col );
				Move( 0, -3 );
				Line( -2, 2 );
				Move( 4, 0 );
				Line( -2, 2 );
				RGBForeColor( &_AACol );
				Move( 1, -4 );
				Line( -2, 2 );
			}
			// Instructions common to both cases:
			Move( -2, 0 );
			Line( 2, 2 );
		}
	}
}

//===================================
void _BLDrawDisclosureTriangle( BLCell inCell, Rect inRect,
								BLDisclosureTriangleState inState, Boolean inActive,
								BlugsRef inList )
{
	Boolean		pressed = bl_Test(inState,blTriStatePressedBit);
	
	if (bl_AllegroAppearance)
	{
		OSStatus			err;
		ThemeButtonDrawInfo	newInfo = {kThemeStateActive,kThemeDisclosureRight,0};
		ThemeEraseUPP		eraseProc;
		BLDisclosureData	data;
		
		if (pressed) newInfo.state = kThemeStatePressed;
		else if (!inActive) newInfo.state = kThemeStateInactive;
		if (bl_Test(inState,blTriStateDownBit)) newInfo.value = kThemeDisclosureDown;
		data.cell = inCell;
		data.list = inList;
		eraseProc = NewThemeEraseUPP( (ThemeEraseProcPtr)_BLDisclosureEraseProc );
		err = DrawThemeButton( &inRect, kThemeDisclosureButton, &newInfo, nil, eraseProc, nil, (UInt32)&data );
		DisposeThemeEraseUPP( eraseProc );
	}
#if !TARGET_RT_MAC_MACHO
	else local_BLDrawDisclosureTriangle( inRect, inState, inActive, pressed );
#endif
}

#if !TARGET_RT_MAC_MACHO
//===================================
// Draw a disclosure triangle using our own code.
// This is not needed for Mach-O targets; they can presuppose OS X Appearance Manager!
static void local_BLDrawDisclosureTriangle( Rect inRect, BLDisclosureTriangleState inState,
											Boolean inActive, Boolean inPressed )
{
	//Boolean		pressed = bl_Test(inState,blTriStatePressedBit);
	PolyHandle	poly;
	RGBColor	saveFore;
		
	GetForeColor( &saveFore );
	poly = OpenPoly();
	if (bl_Test(inState,blTriStateDownBit))
	{
		MoveTo( inRect.left, inRect.top + 4 );
		Line( 5, 5 );
		Line( 5, -5 );
		Line( -10, 0 );
	}
	else if (bl_Test(inState,blTriStateIntermediateBit))
	{
		MoveTo( inRect.left + 1, inRect.top + 9 );
		Line( 8, 0 );
		Line( 0, -8 );
		Line( -8, 8 );
	}
	else // right
	{
		MoveTo( inRect.left + 3, inRect.top );
		Line( 5, 5 );
		Line( -5, 5 );
		Line( 0, -10 );
	}
	ClosePoly();
	if (inActive)
	{
		RGBForeColor( (inPressed) ? &_PressedBlueCol : &_MedBlueCol );
		PaintPoly( poly );
		ForeColor( blackColor );
		FramePoly( poly );
		if (!inPressed)
		{
			if (!bl_Test(inState,blTriStateIntermediateBit)) // right or down
			{
				RGBForeColor( &_LtBlueCol );
				if (bl_Test(inState,blTriStateDownBit))
				{
					MoveTo( inRect.left + 2, inRect.top + 5 );
					Line( 5, 0 );
					Move( 0, 1 );
					RGBForeColor( &_DkBlueCol );
					Line( -2, 2 );
					MoveTo( inRect.left + 6, inRect.top + 9 );
				}
				else // right
				{
					MoveTo( inRect.left + 4, inRect.top + 2 );
					Line( 0, 5 );
					Move( 1, 0 );
					RGBForeColor( &_DkBlueCol );
					Line( 2, -2 );
					MoveTo( inRect.left + 4, inRect.top + 10 );
					
				}
				// Shadow
				RGBForeColor( &_88Col );
				Line( 4, -4 );
				Move( 1, 0 );
				RGBForeColor( &_BBCol );
				Line( -5, 5 );
			}
		} // if not pressed
	} // if active
	else
	{
		RGBForeColor( &_AACol );
		PaintPoly( poly );
	} // not active
	KillPoly( poly );
	RGBForeColor( &saveFore );
}
#endif //!TARGET_RT_MAC_MACHO


//===============================
// Draws a portion of the cell, within which triangle drawing has been suspended,
//	to the target rectangle.
pascal void _BLDisclosureEraseProc( const Rect* inRect, BLDisclosureData* inData, SInt16 inDepth,
									Boolean inColor )
{
#pragma unused (inRect,inDepth,inColor)
	BlugsRef		list;
	BLCell			cell;
	Rect			r;
	
	Boolean				selected, doingInlineEditCell = false;
	BLIndexPtr			index;
	BLContentType		content;
	BLHiliteProcPtr		hiliteProc;
	UInt32				features;
	BLHandlerParamBlock	pb;
	OSErr				err;
	//BLCellUID			cellUID;
	
	list = inData->list;
	cell = inData->cell;
	NormalizeThemeDrawingState();
	//local_BLDrawBackground( data->cell, inRect, data->list );
	(void)_BLCellRect( cell, &r, list );
	
	index = bl_CellIndexAddr(cell,list);
	selected = bl_Selected(index);
	hiliteProc = (*list)->callbacks.hiliteProc;
	content = _BLGetCellContentType( cell, list );
	err = _BLGetHandlerFeatures( content, &features );
	pb.inRect = r;
	// Let handler do its hiliting only if there is no hilite proc.
	pb.inIsSelected = (!hiliteProc && selected);
	pb.inIndent = _BLIndent( cell, list );
	local_BLDrawBackground( cell, &r, list );
	if (bl_InlineNow(list))
	{
		BLCellUID			cellUID;
		
		(void)BLGetCellUID( cell, &cellUID, list );
		if (_BLEqualCellUID( &bl_InlineCell(list), &cellUID )) doingInlineEditCell = true;
	}
	// Draw hiliting one way or another.
	// If it's the inline cell, don't hilite, instead draw focus.
	if (!doingInlineEditCell && selected)
	{
		if (hiliteProc) (*hiliteProc)( &r, list );
		else if (!bl_Test(features,blWantsHilite)) local_BLHiliteCell( cell, &pb, features, list );
	}
#if TARGET_API_MAC_CARBON
	{
		CGrafPtr		port;
		
		GetPort( &port );
		QDFlushPortBuffer( port, nil );
	}
#endif
}


//===============================
// Draws a nifty arrow to indicate the row and indent of dragging in a disclosure list.
//	The inV param is the row of pixels that makes up the arrow's point,
//	which looks best overlapping the cell border. So you pass cellRect.bottom - 1 basically,
//	and in the inH coordinate you offset listRect.left by some appropriate multiple
//	of the list indent setting.
// This can draw over the edge of the list, so make sure we're clipped.
void _BLDrawDragInsertionCaret( SInt16 inH, SInt16 inV, SInt16 inRight )
{
#if TARGET_RT_MAC_MACHO
	local_BLDrawDragInsertionCaretCG( inH, inV, inRight );
#else
	{
		PolyHandle	poly;
	#if __BL_USE_CARBON_API__
		Pattern		pattern;
	#endif
		
		ForeColor( blackColor );
		poly = OpenPoly();
		MoveTo( inH, inV - 2 );
		Line( 0, 4 );
		Line( 2, -2 );
		Line( -2, -2 );
		ClosePoly();
		PaintPoly( poly );
		FramePoly( poly );
		KillPoly( poly );
		PenPat( GetQDGlobalsGray( &pattern ) );
		MoveTo( inH + 6, inV );
		LineTo( inRight - 1, inV );
		PenPat( GetQDGlobalsBlack( &pattern ) );
	}
#endif
}

#if TARGET_RT_MAC_MACHO
//===============================
static void local_BLDrawDragInsertionCaretCG( SInt16 inH, SInt16 inV, SInt16 inRight )
{
	GWorldPtr		world;
	GDHandle		dev;
	CGContextRef	context;
	Rect			r;
	SInt32			ht;
	CGPoint			points[3];
	
	GetGWorld( &world, &dev );
	GetPortBounds( world, &r );
	ht = r.bottom - r.top;
	QDBeginCGContext( world, &context );
	CGContextSetLineWidth( context, 1.0 );
	CGContextSetLineCap( context, kCGLineCapSquare );
	CGContextBeginPath( context );
	points[0].x = inH; points[0].y = ht - inV + 3;
	points[1].x = inH + 4; points[1].y = ht - inV;
	points[2].x = inH; points[2].y = ht - inV - 3;
	CGContextAddLines( context, &points[0], 3 );
	CGContextClosePath( context );
	CGContextSetRGBFillColor( context, 0.0, 0.0, 0.0, 1.0 );
	CGContextFillPath( context );
	CGContextBeginPath( context );
	points[0].x = 1.0;
	points[0].y = 8.0;
	CGContextSetLineDash( context, 0.0, &points[0].x, 2 );
	points[0].x = inH + 6; points[0].y = ht - inV;
	points[1].x = inRight - 1; points[1].y = ht - inV;
	CGContextAddLines( context, &points[0], 2 );
	CGContextClosePath( context );
	CGContextSetRGBStrokeColor( context, 0.0, 0.0, 0.0, 1.0 );
	CGContextDrawPath( context, kCGPathStroke );
	CGContextFlush( context );
	QDEndCGContext( world, &context );
}
#endif

//===============================
void _BLPaintDragHilite( Rect inViewRect, BlugsRef inList )
{
	if (!bl_Test((*inList)->privateFlags,blHaveDrawnDragHilite))
	{
		RgnHandle	hiliteRgn = NewRgn(), tempRgn = NewRgn();
		SInt32		saveMode;
		RGBColor	saveFore;
		
		RectRgn( hiliteRgn, &inViewRect );
		InsetRect( &inViewRect, 3, 3 );
		_BLRgnMinusRect( hiliteRgn, &inViewRect );
		GetForeColor( &saveFore );
		RGBForeColor( &_DragBlueCol );
	#if TARGET_API_MAC_CARBON
		saveMode = GetPortPenMode( GetWindowPort( bl_Window(inList) ) );
	#else
		saveMode = bl_Window(inList)->pnMode;
	#endif
		PenMode( adMin );
		PaintRgn( hiliteRgn );
		DisposeRgn( hiliteRgn );
		PenMode( saveMode );
		RGBForeColor( &saveFore );
		bl_Set((*inList)->privateFlags,blHaveDrawnDragHilite);
	}
}

//===============================
void _BLDrawMarquee( PixMapHandle inPixMap, const Rect* inRect )
{
	if (LockPixels( inPixMap ))
	{
	#if __BL_USE_CARBON_API__
		Pattern		pattern;
		
		if (bl_Aqua)
		{
		#if TARGET_RT_MAC_MACHO
			local_BLDrawMarqueeCG( *inRect );
		#else
			GWorldPtr	world;
			GDHandle	dev;
			
			GetGWorld( &world, &dev );
			PenMode( adMin );
			RGBForeColor( &_DDCol );
			PaintRect( inRect );
			PenNormal();
			RGBForeColor( &_EECol );
			FrameRect( inRect );
		#endif
		}
		else
	#endif
		{
			PenSize( 2, 2 );
			PenMode(  patXor );
			PenPat( GetQDGlobalsGray( &pattern ) );
			FrameRect( inRect );
			UnlockPixels( inPixMap );
			PenNormal();
		}
	}
}

#if TARGET_RT_MAC_MACHO
//===============================
static void local_BLDrawMarqueeCG( Rect inRect )
{
	GWorldPtr		world;
	GDHandle		dev;
	CGContextRef	context;
	CGRect			r;
	SInt32			ht;
	Rect			bounds;
	
	GetGWorld( &world, &dev );
	GetPortBounds( world, &bounds );
	ht = bounds.bottom - bounds.top;
	r = CGRectMake( inRect.left + 1, ht - inRect.bottom,
					inRect.right - inRect.left,
					inRect.bottom - inRect.top - 1 );
	QDBeginCGContext( world, &context );
	//CGContextSetLineWidth( context, 1.0 );
	CGContextSetLineCap( context, kCGLineCapSquare );
	CGContextBeginPath( context );
	CGContextAddRect( context, r );
	CGContextClosePath( context );
	CGContextSetRGBFillColor( context, 0.0, 0.0, 0.0, 0.1 );
	//CGContextFillPath( context );
	CGContextSetRGBStrokeColor( context, 1.0, 1.0, 1.0, 1.0 );
	CGContextDrawPath( context, kCGPathFillStroke );
	CGContextFlush( context );
	QDEndCGContext( world, &context );
}
#endif

//===============================
// Draws a frame outside the rectangle. Does not fill.
static void local_BLDrawListBoxFrame( const Rect* inRect, ThemeDrawState inState )
{
	Rect	r;
		
	r = *inRect;
	// Check for Appearance in non-Carbon
#if !TARGET_API_MAC_CARBON
	if (bl_Appearance)
#endif
		/*if (bl_Aqua && !bl_Jaguar)
		{
			InsetRect( &r, -1, -1 );
			RGBForeColor( &_55Col );
			FrameRect( &r );
		}
		else*/ DrawThemeListBoxFrame( inRect, inState );

#if !TARGET_API_MAC_CARBON
	else
	{
		InsetRect( &r, -2, -2 );
		// Draw outer part.
		if (inState == kThemeStateActive)
		{
			RGBForeColor( &_88Col );
			MoveTo( r.left, r.bottom - 2 );
			LineTo( r.left, r.top );
			LineTo( r.right - 2, r.top );
			RGBForeColor( &_FFCol );
			Move( 1, 1 );
			LineTo( r.right - 1, r.bottom - 1 );
			LineTo( r.left + 1, r.bottom - 1 );
		}
		else
		{
			RGBForeColor( &_DDCol );
			FrameRect( &r );
		}
		InsetRect( &r, 1, 1 );
		// Draw inner part.
		RGBForeColor( (inState == kThemeStateActive) ? &_00Col : &_55Col );
		FrameRect( &r );
	}
#endif
}

//===================================
// 10/24/2001 Moses -->1.1
// Added to support scroll bar widgets.
BL_PROC void BLDrawPlacard( const Rect* inRect, ThemeDrawState inState )
{
#if !TARGET_API_MAC_CARBON
	if (bl_Appearance)
#endif
		DrawThemePlacard( inRect, inState );
#if !TARGET_API_MAC_CARBON
	else
	{
		RGBForeColor( (inState == kThemeStatePressed) ? &_88Col : &_DDCol );
		PaintRect( inRect );
		if (inState == kThemeStateInactive) RGBForeColor( &_88Col );
		else
		{
			MoveTo( inRect->left + 1, inRect->bottom - 3 );
			RGBForeColor( (inState == kThemeStateActive) ? &_FFCol : &_55Col );
			LineTo( inRect->left + 1, inRect->top + 1 );
			LineTo( inRect->right - 3, inRect->top + 1 );
			Move( 1, 1 );
			RGBForeColor( (inState == kThemeStateActive) ? &_88Col : &_AACol );
			LineTo( inRect->right - 2, inRect->bottom - 2 );
			LineTo( inRect->left + 2, inRect->bottom - 2 );
			RGBForeColor( &_00Col );
		}
		FrameRect( inRect );
	}
#endif
}

//===============================
// 10/24/2001 Moses -->1.1
// Added to support scroll bar widgets.
void _BLDrawWidget( Boolean inVertical, UInt16 inWidget, const Rect* inRect,
					ThemeDrawState inState, BlugsRef inList )
{
	RGBColor			saveFore, saveBack;
	BLHandlerParamBlock	pb;
	//BLCell				cell = {0,0};
	Boolean				listActive;
	Rect				r;
	
	// Prepare to draw the background or erase.
	GetForeColor( &saveFore );
	GetBackColor( &saveBack );
	ForeColor( blackColor );
	BackColor( whiteColor );
	listActive = bl_Active(inList);
	r = *inRect;
#if TARGET_API_MAC_CARBON
	if (!bl_Aqua)
#endif
	if (inVertical) r.bottom++;
	else r.right++;
	BLDrawPlacard( &r, inState );
	local_BLSetTitleTextColor( (inState == kThemeStatePressed) ?
								inState : ((listActive) ? kThemeStateActive : kThemeStateInactive ));
	// Handler takes responsibility for drawing inside bevels.
	pb.inIndent = bl_Indent(inList);
	pb.inRect = *inRect;
	pb.inIsSelected = (inState == kThemeStatePressed);
	_BLCallHandlerForWidget( blCellDrawMsg, inVertical, inWidget, &pb, inList );
	RGBForeColor( &saveFore );
	RGBBackColor( &saveBack );
}
