//	Zinc Application Framework - W_BUTTON.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#define OEMRESOURCE			// Windows button messages and flags.
#define ZAF_BUTTON_INFO		// Check Box and Radio button images.
#include "img_def.cpp"
#include <w_app.hpp>
#include <z_button.hpp>
#include <z_ctype.hpp>
#include <z_keymap.hpp>
#include <z_string.hpp>

// Utility to reduce code duplication.
static ZafEventType Select(ZafButton *button, const ZafEventStruct &event, ZafEventType ccode)
{
	// Toggle selection if appropriate.
	// Don't allow de-selection of objects in single select window.
	if (button->AllowToggling() && (!button->Selected() ||
		DynamicPtrCast(button->parent, ZafWindow)->SelectionType() != ZAF_SINGLE_SELECTION))
		button->SetSelected(!button->Selected());

	// Call the user function.
	return (button->*button->memberUserFunction)(event, ccode);
}

// ----- ZafButton ---------------------------------------------------------

//??? stringData & imageData parameters should be reversed.
//??? Are ClearImage() and ClearText() necessary?

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafButton::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Begin drawing operation.
	ZafRegionStruct drawRegion = BeginDraw();

	if (editMode && allowDefault)
	{
		display->SetPalette(parent->LogicalPalette(ZAF_PM_BACKGROUND, parent->PaletteState()));
		display->Rectangle(drawRegion, 0, true);

		display->SetLineStyle(ZAF_LINE_DOTTED);
		display->SetForeground(ZAF_CLR_BLACK);
		display->Rectangle(drawRegion);

		drawRegion -= ZAF_DEFBTN_MARGIN;
	}

	ZafPaletteState state = PaletteState();

	ZafWindow *root = DynamicPtrCast(RootObject(), ZafWindow);
	bool defaultButton = (focus || (root->DefaultButton(true) == this &&
		root->DefaultButton(false) == this));

	// Draw check boxes and radio buttons.
	if (ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
	{
		state &= ~ZAF_PM_SELECTED;

		// Erase the background.
		// (Background palette doesn't affect radio buttons and check boxes.)
		//??? A better way is needed... maybe pass state parameter with draw functions.
		bool saveSelected = selected;
		selected = false;
		DrawBackground(drawRegion, ccode);
		selected = saveSelected;

		// Set the text palette.
		ZafPaletteStruct textPalette = LogicalPalette(ZAF_PM_TEXT, state);
		display->SetPalette(textPalette);

		// Compute regions.
		ZafRegionStruct textRegion;
		if (stringData)
			textRegion = display->TextSize(*stringData);
		else
		{
			textRegion.coordinateType = ZAF_PIXEL;
			textRegion.left = textRegion.top = 1;
			textRegion.right = textRegion.bottom = 0;
		}
		ZafRegionStruct imageRegion;
		imageRegion.coordinateType = ZAF_PIXEL;
		imageRegion.left = imageRegion.top = 1;
		imageRegion.right = checkBitmap->width;
		imageRegion.bottom = checkBitmap->height;
		ComputeDrawRegions(drawRegion, imageRegion, textRegion, 4);

		// Draw the check or radio bitmap.
		if (ButtonType() == ZAF_RADIO_BUTTON)
			display->Bitmap(imageRegion.left, imageRegion.top, selected ?
				(depressed ? *radioSelectedDepressedBitmap : *radioSelectedBitmap) :
				(depressed ? *radioDepressedBitmap : *radioBitmap));
		else
			display->Bitmap(imageRegion.left, imageRegion.top, selected ?
				(depressed ? *checkSelectedDepressedBitmap : *checkSelectedBitmap) :
				(depressed ? *checkDepressedBitmap : *checkBitmap));

		// Draw the focus. 
		DrawFocus(textRegion, ccode);

		// Draw the text.
		if (stringData && (ccode == S_REDISPLAY || ccode == S_REDISPLAY_DATA ||
			ccode == S_REDISPLAY_REGION || ccode == L_SELECT))
		{
			if (Disabled())
			{
				ZafPaletteStruct lightPalette = textPalette;
				lightPalette.colorForeground = lightPalette.colorBackground;
				lightPalette.osPalette.colorForeground = lightPalette.osPalette.colorBackground;
				display->SetPalette(lightPalette);
				display->Text(textRegion.left + 1, textRegion.top + 1, textRegion.right + 1,
					textRegion.bottom + 1, *stringData, -1, ZAF_HZ_CENTER, ZAF_VT_CENTER, hotKeyIndex, false);
			}

			display->SetPalette(textPalette);
			display->Text(textRegion, *stringData, -1, ZAF_HZ_CENTER, ZAF_VT_CENTER, hotKeyIndex, false);
		}

		// End the drawing operation.
		EndDraw();
		return (ccode);
	}

	// Draw "normal" buttons.  (Not check boxes or radio buttons.)
	if (ccode == L_SELECT)
	{
		if (ButtonType() != ZAF_FLAT_BUTTON)
		{
			// Draw the border/default button indicator.
			if (bordered || focus || defaultButton)
				DrawBorder(drawRegion, ccode);

			// Draw the shadow.
			DrawShadow(drawRegion, depressed ? -depth : depth, S_REDISPLAY);

			// Compute the Focus Region.
			ZafRegionStruct focusRegion = drawRegion;
			focusRegion--;

			// Erase focus rect.
			if (focus)
			{
				DrawFocus(focusRegion, S_REDISPLAY);
				focusRegion++;
			}

			// BitBlt button contents.
			if (depressed)
			{
				drawRegion.right--;
				drawRegion.bottom--;
				display->RegionCopy(drawRegion, drawRegion.left + 1, drawRegion.top + 1);
			}
			else
			{
				drawRegion.left++;
				drawRegion.top++;
				display->RegionCopy(drawRegion, drawRegion.left - 1, drawRegion.top - 1);
				display->Line(drawRegion.left, drawRegion.bottom, drawRegion.right, drawRegion.bottom);
				display->Line(drawRegion.right, drawRegion.bottom - 1, drawRegion.right, drawRegion.top);
			}

			// Restore focus rect.
			if (focus)
				DrawFocus(focusRegion, S_REDISPLAY);

			// Erase old bitblt position.
			display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
			if (depressed)
			{
				display->Line(drawRegion.left, drawRegion.top, drawRegion.left, drawRegion.bottom);
				display->Line(drawRegion.left + 1, drawRegion.top, drawRegion.right, drawRegion.top);
			}
			else
			{
				display->Line(drawRegion.left, drawRegion.bottom, drawRegion.right, drawRegion.bottom);
				display->Line(drawRegion.right, drawRegion.bottom - 1, drawRegion.right, drawRegion.top);
			}

		}

		// Only the depressed state needed to be updated.
		EndDraw();
		return (ccode);
	}

//??? Optimization.
//	if (ccode == S_SHOW_DEFAULT || ccode == S_HIDE_DEFAULT)
//	{
//		if (ccode == S_SHOW_DEFAULT)
//		{
//			DrawBorder(drawRegion, S_REDISPLAY);
//			if (ButtonType() != ZAF_FLAT_BUTTON)
//				DrawShadow(drawRegion, depressed ? -depth : depth, S_REDISPLAY);
//		}
//		else
//		{
//			if (ButtonType() != ZAF_FLAT_BUTTON)
//				DrawShadow(drawRegion, depressed ? -depth : depth, S_REDISPLAY);
//			display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
//			display->Rectangle(drawRegion, 1);
//		}
//
//		// Only the default state needed to be updated.
//		EndDraw();
//		return (ccode);
//	}

	// Draw a one pixel border.
	if (defaultButton || Focus() || Bordered())
		DrawBorder(drawRegion, ccode);

	// Draw the 3D shadow.
	if (ButtonType() != ZAF_FLAT_BUTTON)
		DrawShadow(drawRegion, depressed ? -depth : depth, ccode);

	// Erase the background.
	DrawBackground(drawRegion, ccode);

	// If noncurrent, bump in a pixel.  Otherwise, bump in as necessary and
	// draw focus.
	if (Noncurrent())
		drawRegion--;
	else
	{
		// Adjust the focus region.
		if (defaultButton || Focus() || Bordered())
			drawRegion--;
		else
			drawRegion-= 2;

		// Draw the focus rectangle.
		DrawFocus(drawRegion, ccode);
	}

	// Set the clipping region.
	display->SetClipRegion(drawRegion);

	if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_DATA || ccode == S_REDISPLAY_REGION)
	{
		// Create a depression sensation by moving drawRegion down and right.
		if (depressed && ButtonType() != ZAF_FLAT_BUTTON)
		{
			drawRegion.left++;
			drawRegion.top++;
			drawRegion.right++;
			drawRegion.bottom++;
		}

		// Set the text palette.
		ZafPaletteStruct textPalette = LogicalPalette(ZAF_PM_TEXT, state);
		display->SetPalette(textPalette);

		// Compute regions.
		ZafRegionStruct textRegion;
		if (stringData)
			textRegion = display->TextSize(*stringData);
		else
		{
			textRegion.coordinateType = ZAF_PIXEL;
			textRegion.left = textRegion.top = 1;
			textRegion.right = textRegion.bottom = 0;
		}
		ZafRegionStruct imageRegion;
		imageRegion.coordinateType = ZAF_PIXEL;
		imageRegion.left = imageRegion.top = 1;
		if (bitmapData)
		{
			imageRegion.right = bitmapData->width;
			imageRegion.bottom = bitmapData->height;
		}
		else
			imageRegion.right = imageRegion.bottom = 0;
		ComputeDrawRegions(drawRegion, imageRegion, textRegion, 4);

		// Draw the bitmap.
		if (bitmapData)
			display->Bitmap(imageRegion.left, imageRegion.top, *bitmapData);

		// Draw the text.
		if (stringData)
		{
			if (Disabled())
			{
				ZafPaletteStruct darkPalette = LogicalPalette(ZAF_PM_TEXT, state);
				ZafPaletteStruct lightPalette = darkPalette;
				lightPalette.colorForeground = lightPalette.colorBackground;
				lightPalette.osPalette.colorForeground = lightPalette.osPalette.colorBackground;
				display->SetPalette(lightPalette);
				display->Text(textRegion.left + 1, textRegion.top + 1, textRegion.right + 1,
					textRegion.bottom + 1, *stringData, -1, HzJustify(), ZAF_VT_CENTER, hotKeyIndex, false);
				display->SetPalette(darkPalette);
			}
			display->Text(textRegion, *stringData, -1, ZAF_HZ_CENTER, ZAF_VT_CENTER, hotKeyIndex, false);
		}
	}

	// End the drawing operation.
	EndDraw();
	return (ccode);
}

ZafEventType ZafButton::DrawBorder(ZafRegionStruct &region, ZafEventType ccode)
{
	if (ParentDrawBorder())
	{
		ZafRegionStruct borderRegion = parent->ConvertToDrawRegion(this);
		return parent->DrawBorder(borderRegion, ccode);
	}

#if defined(ZAF_MSWINDOWS_3D)
	if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION)
	{
		// Draw a single pixel border.
		display->SetPalette(LogicalPalette(ZAF_PM_OUTLINE, PaletteState()));
		display->Rectangle(region, 1, false);
	}

	// Update the draw region.
	region--;
#else
	ccode = ZafWindowObject::DrawBorder(region, ccode);
#endif

	return (ccode);
}
#endif

ZafEventType ZafButton::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = event.type;

	// Process Windows Messages.
	if (ccode == E_OSEVENT)
		return ZafButton::OSEvent(event);
	
	switch (ccode)
	{
	case S_COMPUTE_SIZE:
		zafRegion = saveRegion;
		ccode = ZafWindowObject::Event(event);
		if (autoSize)
		{
			// Get the text size.
			display->SetDisplayContext(GetDC(screenID));
			display->SetFont(LogicalPalette(ZAF_PM_TEXT, 0).font);
			ZafRegionStruct textRegion;
			if (stringData)
				textRegion = display->TextSize(*stringData);
			else
			{
				textRegion.coordinateType = ZAF_PIXEL;
				textRegion.left = textRegion.top = 1;
				textRegion.right = textRegion.bottom = 0;
			}
			ReleaseDC(screenID, display->RestoreDisplayContext());

			ZafRegionStruct imageRegion;
			imageRegion.coordinateType = ZAF_PIXEL;
			imageRegion.left = imageRegion.top = 1;
			if (ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
			{
				imageRegion.right = checkBitmap->width;
				imageRegion.bottom = checkBitmap->height;
			}
			else if (bitmapData)
			{
				imageRegion.right = bitmapData->width;
				imageRegion.bottom = bitmapData->height;
			}
			else
				imageRegion.right = imageRegion.bottom = 0;

			ZafRegionStruct drawRegion;
			drawRegion.coordinateType = zafRegion.coordinateType;
			drawRegion.left = drawRegion.top = 1;
			drawRegion.right = drawRegion.bottom = 0;
			ComputeDrawRegions(drawRegion, imageRegion, textRegion, 4);
			int height = drawRegion.Height();

			if (ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
			{
				int centerY = zafRegion.top + zafRegion.Height() / 2;
				zafRegion.top = centerY - height / 2;
				zafRegion.bottom = zafRegion.top + height - 1;
			}
			else
			{
				if (!bitmapData)
					height = 23;
				else
				{
					if (ButtonType() == ZAF_NATIVE_BUTTON || ButtonType() == ZAF_3D_BUTTON)
						height += 2 * depth;
					height += 6;
				}

				zafRegion.top = zafRegion.bottom - height + 1;
			}
		}

		if (editMode && allowDefault)
			zafRegion += ZAF_DEFBTN_MARGIN;
		break;

	case S_CURRENT:
	case S_NON_CURRENT:
		ccode = ZafWindowObject::Event(event);
		if (buttonType != ZAF_CHECK_BOX && buttonType != ZAF_RADIO_BUTTON &&
			(ccode == S_CURRENT || DynamicPtrCast(RootObject(), ZafWindow)->DefaultButton(true) != this))
				Event(ZafEventStruct(S_REDISPLAY_DEFAULT));
		break;

	case S_REDISPLAY_DEFAULT:
		if (allowDefault && SystemObject() && screenID)
		{
			if (OSDrawable() && OSDraw())
			{
				ZafWindow *root = DynamicPtrCast(RootObject(), ZafWindow);
				bool isDefault = (focus || (root->DefaultButton(true) == this &&
					root->DefaultButton(false) == this));

				DWORD dwStyle =	GetWindowLong(screenID, GWL_STYLE);
				dwStyle &= 0xFFFFFFF0L;
				if (isDefault)
					dwStyle |= BS_DEFPUSHBUTTON;
				else
					dwStyle |= BS_PUSHBUTTON;
				::SendMessage(screenID, BM_SETSTYLE, (WPARAM)LOWORD(dwStyle), MAKELPARAM(TRUE, 0));
			}
			else
//??? Optimization.
//???				Draw(ZafEventStruct(S_REDISPLAY_DEFAULT), S_REDISPLAY_DEFAULT);
				Redisplay();
		}
		break;

	case S_HOT_KEY:
		if (event.key.value == HotKeyChar() && !Disabled())
			Event(ZafEventStruct(L_SELECT));
		else
			ccode = S_UNKNOWN;
		break;

	case L_SELECT:
	case L_DOUBLE_CLICK:
		if (selectOnDoubleClick)
			Select(this, event, ccode);
		else
			Select(this, event, L_SELECT);
		break;

	default:
		ccode = ZafWindowObject::Event(event);
		break;
	}

	// Return the control code.
	return (ccode);
}

// ----- Attributes ---------------------------------------------------------

bool ZafButton::SetNoncurrent(bool setNoncurrent)
{
	// Defer to the base class.
	return (ZafWindowObject::SetNoncurrent(setNoncurrent));
}

bool ZafButton::SetSelected(bool setSelected)
{
	if (setSelected != selected)
	{
		ZafWindowObject::SetSelected(setSelected);

		// Update the button control.
		if (screenID && SystemObject())
		{
			if (ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
			{
				::SendMessage(screenID, BM_SETCHECK, selected ? 1 : 0, 0L);
				if (!OSDrawable() || !OSDraw())
					Draw(ZafEventStruct(L_SELECT), L_SELECT);
			}
			else if (AllowToggling() && depressed != selected)
			{
				depressed = selected;
				Draw(ZafEventStruct(L_SELECT), L_SELECT);
			}
		}
	}

	return (selected);
}

//??? should only be used internally???
bool ZafButton::SetDepressed(bool tDepressed)
{
	// Make sure the attribute has changed.
	if (depressed != tDepressed)
	{
		// Change the attribute.
		depressed = tDepressed;

		// Update the object, if appropriate.
		if (screenID && !selected)
			Draw(S_REDISPLAY, S_REDISPLAY);
	}

	// Return the current attribute.
	return depressed;
}

// ----- OS Specific Functions ----------------------------------------------

bool ZafButton::OSDrawable()
{
	// See if the button can be drawn by windows.
	if (editMode || ButtonType() == ZAF_FLAT_BUTTON)
		return (false);
	if (ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
	{
		if (hzJustify != ZAF_HZ_LEFT || vtJustify != ZAF_VT_CENTER)
			return (false);
	}
	else
	{
		if (hzJustify != ZAF_HZ_CENTER || vtJustify != ZAF_VT_CENTER ||
			allowToggling || bitmapData || userPaletteData)
			return (false);
	}
	return (true);
}

ZafEventType ZafButton::OSEvent(const ZafEventStruct &event)
{
	if (editMode && event.InputType() == E_MOUSE)
		return (ZafWindowObject::Event(event));

	ZafEventType ccode = event.type;

	switch (event.osEvent.message)
	{
#if defined(ZAF_WIN32)
	case WM_CTLCOLORBTN:
#else
	case WM_CTLCOLOR:
#endif
		{
		ccode = ZafWindowObject::OSEvent(event);

		if (ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
		{
			ZafPaletteStruct paletteForeground = LogicalPalette(ZAF_PM_TEXT, PaletteState());
			ZafPaletteStruct paletteBackground = parent->LogicalPalette(ZAF_PM_BACKGROUND, parent->PaletteState());
			COLORREF colorForeground = (paletteForeground.colorForeground == ZAF_CLR_DEFAULT) ?
				paletteForeground.osPalette.colorForeground :
				((ZafScreenDisplay *)display)->colorTable[paletteForeground.colorForeground];
			COLORREF colorBackground = (paletteBackground.colorBackground == ZAF_CLR_DEFAULT) ?
				paletteBackground.osPalette.colorBackground :
				((ZafScreenDisplay *)display)->colorTable[paletteBackground.colorBackground];

			SelectPalette((HDC)event.osEvent.wParam, ((ZafScreenDisplay *)display)->logicalPalette, FALSE);
			::SetTextColor((HDC)event.osEvent.wParam, colorForeground);
			SetBkColor((HDC)event.osEvent.wParam, colorBackground);

			HBRUSH backgroundBrush = ((ZafScreenDisplay *)display)->GetGdiCacheBrush(paletteBackground);
			((ZafScreenDisplay *)display)->ReleaseGdiCacheObject(backgroundBrush);
			ccode = (ZafEventType)backgroundBrush;
		}
		}
		break;

	case WM_DRAWITEM:
		{
		DRAWITEMSTRUCT *drawItemStruct = (DRAWITEMSTRUCT *)event.osEvent.lParam;

		if (drawItemStruct->CtlType == ODT_BUTTON)
		{
			// Set depressed state.
			bool oldDepressed = depressed;
			if (drawItemStruct->itemState & ODS_SELECTED)
				depressed = true;
			else if (!AllowToggling() || ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
				depressed = false;
			else
				depressed = selected;

			if (drawItemStruct->itemAction & ODA_SELECT &&
				AllowToggling() && depressed == oldDepressed)
				ccode = TRUE;
			else
				ccode = ZafWindowObject::OSEvent(event);
		}
		else
		{
			// Button is in a list.
			if (buttonType == ZAF_3D_BUTTON || buttonType == ZAF_NATIVE_BUTTON)
				depressed = (drawItemStruct->itemState & ODS_SELECTED) ? true : false;
			if (drawItemStruct->itemAction & ODA_SELECT)
			{
				drawItemStruct->itemAction &= ~ODA_SELECT;
				drawItemStruct->itemAction |= ODA_DRAWENTIRE;
			}
			ccode = ZafWindowObject::OSEvent(event);
		}
		}
		break;

	case WM_KEYDOWN:
		switch (event.osEvent.wParam)
		{
		case VK_RETURN:
			// The ENTER key should select all types of buttons but the
			// default button, if it exists, should get it.
			if (!Disabled())
			{
				ccode = 0;
				// A normal 3D button should be called rather than a default button.
				ZafButton *defaultButton = DynamicPtrCast(RootObject(), ZafWindow)->DefaultButton();
				if (!defaultButton || buttonType != ZAF_CHECK_BOX && buttonType != ZAF_RADIO_BUTTON)
					ccode = Event(L_SELECT);
			}
			break;

		case VK_SPACE:
			ccode = DefaultCallback(event);
			// Test for a reapeat key message.
			if (event.osEvent.lParam & 0x40000000L)
			{
				if  (AutoRepeatSelection())
					Select(this, event, L_SELECT);
			}
			else if (SelectOnDownClick())
				Select(this, event, L_SELECT);
			break;

		default:
			ccode = ZafWindowObject::OSEvent(event);
		}
		break;

	case WM_KEYUP:
		switch (event.osEvent.wParam)
		{
		case VK_RETURN:
			ccode = 0;
			break;

		case VK_SPACE:
			if (!SelectOnDownClick())
				Select(this, event, L_SELECT);
			ccode = DefaultCallback(event);
			break;

		default:
			ccode = ZafWindowObject::OSEvent(event);
		}
		break;

	case WM_LBUTTONDOWN:
		if (Noncurrent())
		{
			SetCapture(screenID);
			::SendMessage(screenID, BM_SETSTATE, TRUE, 0);
		}
		else
			ccode = ZafWindowObject::Event(event);
		if (SelectOnDownClick())
			Select(this, event, L_SELECT);
		if (AutoRepeatSelection())
			SetTimer(screenID, ZAF_BUTTON_TIMER_ID, InitialDelay(), NULL);
		break;

	case WM_LBUTTONDBLCLK:
		ccode = ::SendMessage(screenID, WM_LBUTTONDOWN,	event.osEvent.wParam, event.osEvent.lParam);
		if (SelectOnDoubleClick())
			Select(this, event, L_DOUBLE_CLICK);
		break;

	case WM_LBUTTONUP:
		if (AutoRepeatSelection())
	  		KillTimer(screenID, ZAF_BUTTON_TIMER_ID);
		if (!SelectOnDownClick())
		{
			// See if button is in a "highlighted" (depressed) state, 
			// indicating that the mouse has not moved off of the button.
			if (::SendMessage(screenID, BM_GETSTATE, 0, 0) & 0x0004)
				Select(this, event, L_SELECT);
		}
		if (Noncurrent())
		{
			ReleaseCapture();
			::SendMessage(screenID, BM_SETSTATE, FALSE, 0);
		}
		else
			ccode = ZafWindowObject::Event(event);
		break;

	case WM_MOUSEMOVE:
		if (Noncurrent() && GetCapture() == screenID && event.osEvent.wParam & MK_LBUTTON)
		{
			ZafPositionStruct position;
			position.coordinateType = zafRegion.coordinateType;
			position.column = zafRegion.left + LOWORD(event.osEvent.lParam);
			position.line = zafRegion.top + HIWORD(event.osEvent.lParam);
				
			if (zafRegion.Overlap(position))
				::SendMessage(screenID, BM_SETSTATE, TRUE, 0);
			else
				::SendMessage(screenID, BM_SETSTATE, FALSE, 0);
		}
		else
		{
			ccode = ZafWindowObject::Event(event);
			if (windowManager->dragObject)
				ccode = DefaultCallback(event);
		}
		break;

	case WM_PAINT:
		// Draw() gets called by WM_DRAWITEM messages.
		ccode = DefaultCallback(event);
		break;

	case WM_ERASEBKGND:
		if (OSDraw() && OSDrawable())
			ccode = DefaultCallback(event);
		else
			ccode = ZafWindowObject::Event(event);
		break;

	case WM_TIMER:
		// See if button is in a "highlighted" (depressed) state, 
		// indicating that the mouse has not moved off of the button.
		if (AutoRepeatSelection() && (::SendMessage(screenID, BM_GETSTATE, 0, 0) & 0x0004))
		{
			Select(this, event, L_SELECT);
			SetTimer(screenID, ZAF_BUTTON_TIMER_ID, RepeatDelay(), NULL);
		}
		ccode = 0;
		break;

	default:
		ccode = ZafWindowObject::OSEvent(event);
		break;
	}

	return (ccode);
}

void ZafButton::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	// Get default colors from the operating system.
	if (type == ZAF_PM_TEXT)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
		{
			if (state & ZAF_PM_DISABLED)
				palette.osPalette.colorForeground = GetSysColor(COLOR_3DSHADOW);
			else
				palette.osPalette.colorForeground = GetSysColor(COLOR_BTNTEXT);
		}
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
		{
			if (state & ZAF_PM_DISABLED)
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DHILIGHT);
			else
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);
		}
	}
	else if (type == ZAF_PM_BACKGROUND)
	{
		if (ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
		{
			ZafPaletteStruct parentPalette = parent->LogicalPalette(type, state);
			if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
				palette.osPalette.colorForeground = (parentPalette.colorForeground == ZAF_CLR_DEFAULT) ?
					parentPalette.osPalette.colorForeground : ((ZafScreenDisplay *)display)->colorTable[parentPalette.colorForeground];
			if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
				palette.osPalette.colorBackground = (parentPalette.colorBackground == ZAF_CLR_DEFAULT) ?
					parentPalette.osPalette.colorBackground : ((ZafScreenDisplay *)display)->colorTable[parentPalette.colorBackground];
		}
		else
		{
			if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
				palette.osPalette.colorForeground = GetSysColor(COLOR_3DFACE);
			if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);
		}
	}
	else if (type == ZAF_PM_FOCUS)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
			palette.osPalette.colorForeground = GetSysColor(COLOR_3DFACE);
	}
	else if (focus && depressed && (type == ZAF_PM_LIGHT_SHADOW || type == ZAF_PM_DARK_SHADOW))
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
		{
			if (type == ZAF_PM_LIGHT_SHADOW)
				palette.osPalette.colorForeground = GetSysColor(COLOR_3DSHADOW);
			else if (type == ZAF_PM_DARK_SHADOW)
				palette.osPalette.colorForeground = GetSysColor(COLOR_3DSHADOW);
		}
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
		{
			if (type == ZAF_PM_LIGHT_SHADOW)
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);
			else if (type == ZAF_PM_DARK_SHADOW)
				palette.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);
		}
	}
	else
		ZafWindowObject::OSMapPalette(palette, type, state);
}

void ZafButton::OSRegisterObject(void)
{
	// Set the Windows styles.
	DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;

	if (!OSDraw() || !OSDrawable())
		dwStyle |= BS_OWNERDRAW;
	else
	{

		if (ButtonType() == ZAF_CHECK_BOX)
			dwStyle |= BS_CHECKBOX;
		else if (ButtonType() == ZAF_RADIO_BUTTON)
			dwStyle |= BS_RADIOBUTTON;
		else
		{
			ZafWindow *root = DynamicPtrCast(RootObject(), ZafWindow);
			bool isDefault = (focus || (root->DefaultButton(true) == this &&
				root->DefaultButton(false) == this));
			if (allowDefault && SystemObject() && isDefault)
			 	dwStyle |= BS_DEFPUSHBUTTON;
			else
				dwStyle |= BS_PUSHBUTTON;
		}

		if (HzJustify() == ZAF_HZ_RIGHT)
			dwStyle |= BS_LEFTTEXT;
	}
	if (Disabled() && !editMode)
		dwStyle |= WS_DISABLED;
	if (visible)
		dwStyle |= WS_VISIBLE;
	if (Bordered() && OSDraw() && OSDrawable())
		dwStyle |= WS_BORDER;

	// Create the control.
	screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, ZAF_ITEXT("BUTTON"), dwStyle);

	// Set the font.
	ZafPaletteStruct fontPalette = LogicalPalette(ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED);
	::SendMessage(screenID, WM_SETFONT, (WPARAM)((ZafScreenDisplay *)display)->fontTable[fontPalette.font], (LPARAM)FALSE);

	// Set the text.
	OSSetText();

	// Initialize selection state.
	if (ButtonType() == ZAF_CHECK_BOX || ButtonType() == ZAF_RADIO_BUTTON)
		::SendMessage(screenID, BM_SETCHECK, selected, 0);
}

ZafError ZafButton::OSSetImage(void)
{
	if (screenID)
	{
		Event(ZafEventStruct(S_COMPUTE_SIZE));
		OSSize();
		Redisplay();
	}

	return ZAF_ERROR_NONE;
}


ZafError ZafButton::OSSetText(void)
{
	if (screenID && parent->IsA(ID_ZAF_LIST) && parent->OSDraw())
		return (ZafWindowObject::OSSetText());

	if (screenID)
	{
		if (SystemObject())
		{
			const ZafIChar *drawText = stringData ?
				stringData->Text() : ZafLanguageManager::blankString;

			ZafIChar *hotKeyText = ZafMSWindowsApp::HotKeyText(drawText, hotKeyIndex);

			if (ZafMSWindowsApp::convertText)
			{
				char *osText = zafCodeSet->ConvertToOSString(hotKeyText);
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
				::SendMessageA(screenID, WM_SETTEXT, (WPARAM)0, (LPARAM)osText);
#else
				::SendMessage(screenID, WM_SETTEXT, (WPARAM)0, (LPARAM)osText);
#endif
				delete []osText;
			}
			else
				::SendMessage(screenID, WM_SETTEXT, (WPARAM)0, (LPARAM)hotKeyText);

			delete []hotKeyText;

			if (!OSDraw() || !OSDrawable())
				Event(ZafEventStruct(S_REDISPLAY));
		}
	}

	return ZAF_ERROR_NONE;
}

ZafError ZafButton::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	if (osDraw && screenID && systemObject)
	{
		// Determine whethor the button is to be drawn by the Windows or ZAF.
	 	LONG dwStyle = GetWindowLong(screenID, GWL_STYLE);
		bool ownerDraw = (dwStyle & BS_OWNERDRAW) ? true : false;
		bool osDrawable = OSDrawable();

		// If the button cannot be drawn by Windows, but the button is not set
		// as ownerdraw (not to be confused with the user setting osDraw)
		// set the BS_OWNERDRAW style. At some point, it would be nice to
		// turn off the BS_OWNERDRAW style if the button became osDrawable.
		// This would require figuring out all the button styles, though.
		if (!ownerDraw && !osDrawable)
			::SendMessage(screenID, BM_SETSTYLE, (WPARAM)BS_OWNERDRAW, MAKELPARAM(FALSE, 0));
	}

    // Defer to the base class.
   	return (ZafWindowObject::OSUpdatePalettes(zafTypes, osTypes));
}

// ----- Stubs --------------------------------------------------------------

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafButton::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	return (ZafWindowObject::DrawFocus(region, ccode));
}

ZafEventType ZafButton::DrawShadow(ZafRegionStruct &region, int depth, ZafEventType ccode)
{
	return (ZafWindowObject::DrawShadow(region, depth, ccode));
}
#endif

void ZafButton::OSSize(void)
{
	ZafWindowObject::OSSize();
}

bool ZafButton::SetDisabled(bool setDisabled)
{
	return (ZafWindowObject::SetDisabled(setDisabled));
}

bool ZafButton::SetVisible(bool setVisible)
{
	return (ZafWindowObject::SetVisible(setVisible));
}
