/*===============================
|	CheckboxHandler.c
|
|	Blugs 1.1 handler for checkboxes
|	www.blugs.com
|
|	Copyright © 1998-2001 Brian Hall
|	You may use these routines in your software without restriction.
|
|===============================*/

#ifdef __APPLE_CC__
#include <Carbon/Carbon.h>
#else
#ifndef __BALLOONS__
#include <Balloons.h>
#endif

#ifndef __TEXTUTILS__
#include <TextUtils.h>
#endif

#ifndef __TOOLUTILS__
#include <ToolUtils.h>
#endif
#endif

#include "CheckboxHandler.h"
#include "BlugsAppearanceExtras.h"



static void			local_CheckboxDraw( Rect r, CheckboxHdl h, BlugsRef list, Boolean selected,
										UInt16 indent );
static Rect			local_CheckboxBoxRect( Rect cellRect, UInt16 indent );
static CheckboxHdl	local_CheckboxAllocateStorage( void );
static void			local_CheckboxSet( CheckboxHdl h, BLHandlerParamPtr param );
static void			local_CheckboxGet( CheckboxHdl h, BLHandlerParamPtr param );
static UInt32		local_CheckboxClick( Point where, Rect cellRect, CheckboxHdl h, BlugsRef list, UInt16 indent );



pascal UInt32 CheckboxHandler(		BLMessage			message,
									BLHandlerParamPtr	param		)
{
	UInt32		answer = 0;
	CheckboxHdl	h = nil;
	
	if (param) h = (CheckboxHdl)param->ioStorage;
	switch (message)
	{
		case blHandlerInitMsg:
		/* 	We intercept clicks to check/uncheck.
			We hilite only the text rectangle. */
		answer = blWantsClick | blWantsHilite;
		break;
		
		case blHandlerDeinitMsg:
		/* No special deinitialization. */
		break;
		
		case blCellInitMsg:
		param->ioStorage = (Handle)local_CheckboxAllocateStorage();
		break;
		
		case blCellDeinitMsg:
		if (h) DisposeHandle( (Handle)h );
		break;
		
		case blCellDrawMsg:
		local_CheckboxDraw( param->inRect, h, param->inList, false, param->inIndent );
		break;
		
		case blCellSetDataMsg:
		local_CheckboxSet( h, param );
		break;
		
		case blCellClearDataMsg:
		if (h)
		{
			(*h)->string[0] = 0;
			(*h)->value = kThemeButtonOff;
		}
		break;
		
		case blCellGetDataMsg:
		if (h) local_CheckboxGet( h, param ); 
		break;
		
		
		case blCellClickMsg:
		answer = local_CheckboxClick( param->inEventWhereLocal, param->inRect, h, param->inList,
								param->inIndent );
		break;
		
		default:
		break;
	}
	return answer;
}

//===============================
static void local_CheckboxDraw( Rect r, CheckboxHdl h, BlugsRef list, Boolean selected, UInt16 indent )
{
	ThemeDrawState		state;
	Boolean				active;
	Rect				boxRect;
	
	active = BLIsActive(list);
	if (!active) state = kThemeStateInactive;
	else if (selected) state = kThemeStatePressed;
	else state = kThemeStateActive;
	boxRect = local_CheckboxBoxRect( r, indent );
	indent = boxRect.right + 8 - r.left;
	BLDrawStringVerticallyCentered( (*h)->string, &r, boxRect.right + 8 - r.left );
	BLDrawCheckBox( boxRect, state, (*h)->value );
}

//===============================
// We have to use 16 pixels as the checkbox rectangle to avoid clipping under Aqua.
// Should check the Aqua Menus Gestalt to see if we're under OS X.
static Rect local_CheckboxBoxRect( Rect cellRect, UInt16 indent )
{
	Rect		boxRect;
	SInt32		boxSize;
#if __BL_USE_CARBON_API__
	OSStatus	err = unimpErr;
#endif
	
#if __BL_USE_CARBON_API__
	// Note: this asserts on 8.6 w/ DebugCarbonLib 1.04.
	//	Apparently only the scroll bar codes are valid.
	// We only need it under Aqua.
	if ((BLEnvironment() & blHasAqua) == blHasAqua)
	{
		err = GetThemeMetric( kThemeMetricCheckBoxHeight, &boxSize );
	}
	if (err) // Do it the normal way if there is an error....
#endif
		boxSize = 12;
	boxRect.left = cellRect.left + indent;
	boxRect.right = boxRect.left + boxSize;
	boxRect.top =  cellRect.top + ((cellRect.bottom - cellRect.top - boxSize) / 2);
	boxRect.bottom = boxRect.top + boxSize;
	return boxRect;
}

//===============================
static CheckboxHdl local_CheckboxAllocateStorage( void )
{
	CheckboxHdl	h;
	
	h = (CheckboxHdl)NewHandle( sizeof(CheckboxRec) );
	(*h)->string[0] = 0;
	(*h)->value = kThemeButtonOff;
	return h;
}

//===============================
static void local_CheckboxSet( CheckboxHdl h, BLHandlerParamPtr param )
{
	SInt16		resID, strIndex;
	Str255		str;
	
	if (h)
	{
		switch (param->ioDataFlavor)
		{
			// Load a string
			case kCheckboxStringListResourceFlavor:
			resID = HiWord(*(SInt32*)(param->ioDataBuffer));
			strIndex = LoWord(*(SInt32*)(param->ioDataBuffer));
			GetIndString( str, resID, strIndex );
			BlockMoveData( &(str[0]), &((*h)->string[0]), 32 );
			if (str[0] > 31) (*h)->string[0] = 31;
			else (*h)->string[0] = str[0];
			(*h)->value = kThemeButtonOff;
			break;
			
			// Change the state of the checkbox
			case kCheckboxValueFlavor:
			(*h)->value = *(ThemeButtonValue*)(param->ioDataBuffer);
			break;
		}
	}
}

//===============================
static void local_CheckboxGet( CheckboxHdl h, BLHandlerParamPtr param )
{
	UInt32	bytes = 0;
	void*	src;
	
	switch (param->ioDataFlavor)
	{
		case kCheckboxTextFlavor:
		bytes = (param->ioDataSize < (*h)->string[0]) ? param->ioDataSize : (*h)->string[0];
		src = &((*h)->string[1]);
		break;
		
		case kCheckboxValueFlavor:
		bytes = sizeof(ThemeButtonValue);
		src = &(*h)->value;
		break;
	}
	if (bytes && param->ioDataBuffer) BlockMoveData( src, param->ioDataBuffer, bytes );
	param->ioDataSize = bytes;
}

//===================================
static UInt32 local_CheckboxClick( Point where, Rect cellRect, CheckboxHdl h, BlugsRef list, UInt16 indent )
{
	Rect	boxRect;
	UInt32	response = 0;
	
	boxRect = local_CheckboxBoxRect( cellRect, indent );
	if (PtInRect(where,&boxRect))
	{
		Boolean	wasIn = true, playThemeSound = false;
		Point	mouse;
	#if __BL_USE_CARBON_API__
		Boolean	aqua;
		
		aqua = ((BLEnvironment() & blHasAqua) == blHasAqua);
		if (aqua)
		{
			BackColor( whiteColor );
			EraseRect( &cellRect );
		}
	#endif
		if (BLAppearanceVersion() >= 0x0110) playThemeSound = true;
		if (playThemeSound) PlayThemeSound( kThemeSoundCheckboxPress );
		ForeColor( blackColor );
		local_CheckboxDraw( cellRect, h, list, true, indent );
		while (StillDown())
		{
			GetMouse( &mouse );
			if (wasIn != PtInRect(mouse,&boxRect))
			{
				//ForeColor( blackColor );
				
			#if TARGET_API_MAC_CARBON
				if (aqua)
				{
					BackColor( whiteColor );
					EraseRect( &cellRect );
				}
			#endif
				local_CheckboxDraw( cellRect, h, list, !wasIn, indent );
				if (playThemeSound)
					PlayThemeSound( (wasIn) ? kThemeSoundCheckboxEnter : kThemeSoundCheckboxExit );
				wasIn = !wasIn;
			}
		}
		if (wasIn)
		{
			if ((*h)->value == kThemeButtonOff) (*h)->value = kThemeButtonOn;
			else (*h)->value = kThemeButtonOff;
		}
		if (playThemeSound) PlayThemeSound( kThemeSoundCheckboxRelease );
		response = blCellClickHandled | blCellClickUpdate;
	}
	return response;
}

