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

#include <w_app.hpp>
#include <z_ctype.hpp>
#include <z_group.hpp>

// ----- ZafGroup ---------------------------------------------------------

ZafPositionStruct ZafGroup::ConvertToOSPosition(const ZafWindowObject *object,
	const ZafPositionStruct *position) const
{
	ZafPositionStruct osPosition = ZafWindowObject::ConvertToOSPosition(object, position);
	if (bordered)
	{
		osPosition.column += ZAF_BORDER_SIZE;
		osPosition.line += display->cellHeight * 3 / 4;
	}
	return (osPosition);
}

ZafPositionStruct ZafGroup::ConvertToZafPosition(const ZafWindowObject *object,
	const ZafPositionStruct *position) const
{
	ZafPositionStruct zafPosition = ZafWindowObject::ConvertToZafPosition(object, position);
	if (bordered)
	{
		zafPosition.column -= ZAF_BORDER_SIZE;
		zafPosition.line -= display->cellHeight * 3 / 4;
	}
	return (zafPosition);
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafGroup::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	ZafRegionStruct drawRegion = BeginDraw();

	// Draw background before the border, since the border does not surround
	// the entire object.
	if (ccode == S_REDISPLAY_DATA)
	{
		ZafRegionStruct eraseRegion = drawRegion;
		eraseRegion.bottom = eraseRegion.top + display->cellHeight - 1;
		DrawBackground(eraseRegion, ccode);
	}
	else
		DrawBackground(drawRegion, ccode);

	if (bordered)
		DrawBorder(drawRegion, ccode);

	EndDraw();
	return (ccode);
}

ZafEventType ZafGroup::DrawBorder(ZafRegionStruct &region, ZafEventType ccode)
{
	ZafPaletteState state = PaletteState();
	display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));

	ZafPaletteStruct lightShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, ZAF_PM_ANY_STATE);
	ZafPaletteStruct darkShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, ZAF_PM_ANY_STATE);

	const ZafIChar *text = Text();

	int width = (text && text[0]) ?
		display->TextSize(text).Width() + 2 * display->cellWidth : 0;

	ZafCoordinate polygon[12];
	polygon[10] = region.left + display->cellWidth;
	polygon[0] = polygon[10] + width;
	polygon[1] = polygon[3] = polygon[9] = polygon[11] = region.top + display->cellHeight / 2;
	polygon[2] = polygon[4] = region.left + zafRegion.Width() - 2;
	polygon[5] = polygon[7] = region.top + zafRegion.Height() - 2;
	polygon[6] = polygon[8] = region.left;

	display->SetPalette(darkShadow);
	display->Polygon(6, polygon);

	display->SetPalette(lightShadow);
	display->Line(region.left + 1, polygon[1] + 1, polygon[10], polygon[1] + 1);
	display->Line(polygon[0], polygon[1] + 1, region.left + zafRegion.Width() - 3, polygon[1] + 1);
	display->Line(region.left + 1, polygon[1] + 1, region.left + 1, region.top + zafRegion.Height() - 3);
	display->Line(region.left + zafRegion.Width() - 1, polygon[1], region.left + zafRegion.Width() - 1,
		region.top + zafRegion.Height() - 1);
	display->Line(region.left, region.top + zafRegion.Height() - 1,
		region.left + zafRegion.Width() - 2, region.top + zafRegion.Height() - 1);

	display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));
	if (text && text[0])
		display->Text(region.left + 2 * display->cellWidth, region.top + display->cellHeight / 4,
			text, -1, hotKeyIndex, false);

	return (ccode);
}
#endif

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

	if (ccode == E_OSEVENT)
		return ZafGroup::OSEvent(event);

	switch (ccode)
	{
	case S_HOT_KEY:
		if (event.key.value == hotKeyChar && !Disabled())
			SetFocus(true);
		else
			ccode = ZafWindow::Event(event);
		break;

	case S_REDISPLAY:
	case S_REDISPLAY_REGION:
		if (screenID && AutomaticUpdate())
		{
			ZafEventStruct redisplayEvent(S_REDISPLAY_REGION);
			redisplayEvent.region = parent->ConvertToOSRegion(this,
				(ccode == S_REDISPLAY_REGION) ?
				&event.region : ZAF_NULLP(ZafRegionStruct));
			ccode = parent->Event(redisplayEvent);
		}
		break;

	case S_REDISPLAY_DATA:
		ccode = ZafWindowObject::Event(event);
		BroadcastEvent(event);
		break;

	default:
		ccode = ZafWindow::Event(event);
	}

	return (ccode);
}

ZafRegionStruct ZafGroup::MaxRegion(ZafWindowObject *, ZafVtJustify, ZafHzJustify)
{
	ZafRegionStruct maxRegion;
	maxRegion.coordinateType = ZAF_PIXEL;
	maxRegion.left = maxRegion.top = 0;
	maxRegion.right = zafRegion.Width() - 1;
	maxRegion.bottom = zafRegion.Height() - 1;
	if (bordered)
	{
		maxRegion.right -= ZAF_BORDER_SIZE * 2;
		maxRegion.bottom -= display->cellHeight * 3 / 4 - ZAF_BORDER_SIZE;
	}

	return (maxRegion);
}

ZafWindowObject *ZafGroup::NotifyFocus(ZafWindowObject *focusObject, bool setFocus)
{
	// Defer to the base class.
	ZafWindowObject *invalidObject = ZafWindow::NotifyFocus(focusObject, setFocus);
	if (setFocus && autoSelect && SelectionType() == ZAF_SINGLE_SELECTION)
		focusObject->SetSelected(true);
	return (invalidObject);
}

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

bool ZafGroup::SetDisabled(bool setDisabled)
{
	// Defer to the base class.
	return (ZafWindow::SetDisabled(setDisabled));
}

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

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

	ZafEventType ccode = event.osEvent.message;

	switch (ccode)
	{
#if defined(ZAF_WIN32)
	case WM_CTLCOLORBTN:
	case WM_CTLCOLORSTATIC:
		{
		HWND hwndStatic = (HWND)event.osEvent.lParam;
#else
	case WM_CTLCOLOR:
		{
		HWND hwndStatic = (HWND)LOWORD(event.osEvent.lParam);
#endif
		if (hwndStatic == screenID)
		{
			// Get the palettes.
			ZafPaletteStruct textPalette = LogicalPalette(ZAF_PM_TEXT, PaletteState());
			ZafPaletteStruct backgroundPalette = parent->LogicalPalette(ZAF_PM_BACKGROUND, parent->PaletteState());

			// Get the OS colors.
			COLORREF colorForeground = (textPalette.colorForeground == ZAF_CLR_DEFAULT) ?
				textPalette.osPalette.colorForeground :
				((ZafScreenDisplay *)display)->colorTable[textPalette.colorForeground];
			COLORREF colorBackground = (backgroundPalette.colorBackground == ZAF_CLR_DEFAULT) ?
				backgroundPalette.osPalette.colorBackground :
				((ZafScreenDisplay *)display)->colorTable[backgroundPalette.colorBackground];

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

			// Get a background brush.
			HBRUSH backgroundBrush = ((ZafScreenDisplay *)display)->GetGdiCacheBrush(backgroundPalette);
			((ZafScreenDisplay *)display)->ReleaseGdiCacheObject(backgroundBrush);
			ccode = (ZafEventType)backgroundBrush;
		}
		else
			ccode = ZafWindow::Event(event);
#if defined(ZAF_WIN32)
		}
#else
		}
#endif
		break;

	case WM_KEYDOWN:
		switch (event.osEvent.wParam)
		{
		case VK_TAB:
			ccode = S_UNKNOWN;
			break;

		case VK_UP:
		case VK_DOWN:
		case VK_LEFT:
		case VK_RIGHT:
			{
			ZafWindowObject *curObj = Current();
			ccode = ZafWindow::OSEvent(event);
			if (AutoSelect() && curObj != Current())
				(Current()->*(Current()->memberUserFunction))(event, L_SELECT);
			}
			break;

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

	case WM_PAINT:
		if (OSDraw())
			ccode = DefaultCallback(event);
		else
			ccode = ZafWindowObject::OSEvent(event);
		break;

	case WM_NCHITTEST:
		ccode = HTCLIENT;
		break;

	case WM_COMMAND:
		{
#if defined(ZAF_WIN32)
		HWND hwndCtrl = (HWND)event.osEvent.lParam;
#else
		HWND hwndCtrl = (HWND)LOWORD(event.osEvent.lParam);
#endif
		if (!hwndCtrl || hwndCtrl == screenID)
			return (0);
		else
			ccode = ZafWindow::OSEvent(event);
		}
		break;

	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_LBUTTONDBLCLK:
	case WM_RBUTTONDBLCLK:
	case WM_MBUTTONDBLCLK:
		ccode = 0;
		break;

	default:
		ccode = ZafWindow::OSEvent(event);
	}

	return (ccode);
}

void ZafGroup::OSRegisterObject(void)
{
	DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
	if (bordered)
		dwStyle |= BS_GROUPBOX;
	if (visible)
		dwStyle |= WS_VISIBLE;
	if (Disabled() && !editMode)
		dwStyle |= WS_DISABLED;

	if (Bordered())
	{
		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);

		OSSetText();
	}
	else
		screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, NULL, dwStyle, *stringData);
}

OSWindowID ZafGroup::OSScreenID(ZafScreenIDType) const
{
	return (screenID);
}

ZafError ZafGroup::OSSetText(void)
{
	//??? Same as button... duplicate code.
	if (screenID)
	{
		if (SystemObject() && OSDraw())
		{
			const ZafIChar *drawText = stringData ?
				stringData->Text() : ZafLanguageManager::blankString;
			ZafIChar *hotKeyText = ZAF_NULLP(ZafIChar);

			// Fix ampersand characters so that they don't draw as hot keys.
			const ZafIChar *ampersand = strchr(drawText, '&');
			int hotKeyDelta = 0;
			if (ampersand)
			{
				// Count the '&' characters.
				int numAmpersands = 0;
				while (ampersand)
				{
					numAmpersands++;
					ampersand = strchr(ampersand + 1, '&');
				}

				hotKeyText = new ZafIChar[strlen(drawText) + numAmpersands + 1];

				int hotKeyTextIndex = 0;
				const ZafIChar *sourceText = drawText;
				ampersand = strchr(sourceText, '&');
				strncpy(&hotKeyText[hotKeyTextIndex], sourceText, (int)(ampersand - sourceText + 1));
				hotKeyTextIndex += (int)(ampersand - sourceText + 1);
				while (ampersand)
				{
					if (hotKeyIndex > -1 && ampersand - sourceText <= hotKeyIndex)
						hotKeyDelta++;

					hotKeyText[hotKeyTextIndex++] = '&';
					const ZafIChar *nextAmpersand = strchr(ampersand + 1, '&');
					if (nextAmpersand)
					{
						strncpy(&hotKeyText[hotKeyTextIndex], ampersand + 1,
							(int)(nextAmpersand - ampersand));
						hotKeyTextIndex += (int)(nextAmpersand - ampersand);
					}
					else
						strcpy(&hotKeyText[hotKeyTextIndex], ampersand + 1);
					ampersand = nextAmpersand;
				}

				drawText = hotKeyText;
			}

			if (hotKeyIndex > -1 && hotKeyIndex < (int)strlen(drawText))
			{
				ZafIChar *tempText = hotKeyText;
				hotKeyText = new ZafIChar[strlen(drawText) + 2];
				strncpy(hotKeyText, drawText, hotKeyIndex + hotKeyDelta);
				hotKeyText[hotKeyIndex + hotKeyDelta] = '&';
				strcpy(&hotKeyText[hotKeyIndex + hotKeyDelta +1], &drawText[hotKeyIndex + hotKeyDelta]);
				drawText = hotKeyText;

				if (tempText)
					delete tempText;
			}

			if (ZafMSWindowsApp::convertText)
			{
				char *osText = zafCodeSet->ConvertToOSString(drawText);
#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)drawText);

			if (hotKeyText)
				delete []hotKeyText;
		}
		else
			RedisplayData();
	}

	return ZAF_ERROR_NONE;
}

ZafError ZafGroup::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	return (ZafWindow::OSUpdatePalettes(zafTypes, osTypes));
}

