// Zinc Application Framework - W_PROMPT.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_prompt.hpp>

// ----- ZafPrompt ---------------------------------------------------------

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafPrompt::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Begin the drawing operation.
	ZafRegionStruct drawRegion = BeginDraw();
	display->SetClipRegion(drawRegion);

	// Fill the background.
	if (!TransparentBackground() || editMode)
		DrawBackground(drawRegion, ccode == S_REDISPLAY_DATA ? S_REDISPLAY : ccode);

	if (editMode)
	{
		display->SetLineStyle(ZAF_LINE_DOTTED);
		display->SetForeground(ZAF_CLR_BLACK);
		display->Rectangle(drawRegion);
	}

	if (!autoSize || editMode)
	{
		if (bordered)
			drawRegion.top += ZAF_BORDER_SIZE + 1; // (Border + margin)
	}

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

		// Set the text palette.
		display->SetPalette(textPalette);

		// Draw the text.
		display->Text(drawRegion.left, drawRegion.top,
			drawRegion.right - 1, drawRegion.bottom - 1, *stringData,
			-1, HzJustify(), VtJustify(), hotKeyIndex, false);
	}

	// End the drawing operation.
	EndDraw();

	return (ccode);
}
#endif

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

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

	// Process ZAF events.
	switch (ccode)
	{
	case S_COMPUTE_SIZE:
		zafRegion = saveRegion;
		ccode = ZafWindowObject::Event(event);
		if (autoSize && !EditMode())
		{
			// Get the text size.
//??? A more portable method of getting text size (outside of Draw()) is needed?
			display->SetDisplayContext(GetDC(screenID));
			display->SetFont(LogicalPalette(ZAF_PM_TEXT, 0).font);
			ZafRegionStruct size = display->TextSize(*stringData);
			ReleaseDC(screenID, display->RestoreDisplayContext());

			// Add a pixel for disabled text.
			size.right++;
			size.bottom++;

			// Perform vertical justification.
			if (VtJustify() == ZAF_VT_CENTER)
			{
				int centerY = zafRegion.top + zafRegion.Height() / 2;
				zafRegion.top = centerY - size.Height() / 2;
				zafRegion.bottom = zafRegion.top + size.Height() - 1;
			}
			else if (VtJustify() == ZAF_VT_BOTTOM)
				zafRegion.top = zafRegion.bottom - size.Height() + 1;
			else
			{
				// Make baselines line up.
				// Bordered() means that the baseline should line up with a
				// bordered string field, NOT that a border should be drawn.
				if (Bordered())
					zafRegion.top += ZAF_BORDER_SIZE + 1; // (Border + margin)
				zafRegion.bottom = zafRegion.top + size.Height() - 1;
			}

			// Perform horizontal justification.
			if (HzJustify() == ZAF_HZ_CENTER)
			{
				int centerX = zafRegion.left + zafRegion.Width() / 2;
				zafRegion.left = centerX - size.Width() / 2;
				zafRegion.right = zafRegion.left + size.Width() - 1;
			}
			else if (HzJustify() == ZAF_HZ_RIGHT)
				zafRegion.left = zafRegion.right - size.Width() + 1;
			else
				zafRegion.right = zafRegion.left + size.Width() - 1;
		}
		break;

	case S_HOT_KEY:
		if (event.key.value == hotKeyChar)
		{
			if (Next())
				Next()->SetFocus(true);
		}
		else
			ccode = S_UNKNOWN;
		break;

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

	case N_SIZE:
		Redisplay();
		break;

	case S_UPDATE_OBJECT:
		OSSetText();
		break;

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

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

bool ZafPrompt::SetDisabled(bool setDisabled)
{
	if (setDisabled != disabled)
	{
		if (screenID && automaticUpdate &&
			(!osDraw || transparentBackground || !autoSize))
		{
			SendMessage(screenID, WM_SETREDRAW, (WPARAM)FALSE, 0);
			ZafWindowObject::SetDisabled(setDisabled);
			SendMessage(screenID, WM_SETREDRAW, (WPARAM)TRUE, 0);
			Redisplay();
		}
		else
			ZafWindowObject::SetDisabled(setDisabled);
	}

	return disabled;
}

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

ZafEventType ZafPrompt::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_CTLCOLORSTATIC:
#else
	case WM_CTLCOLOR:
#endif
		{
		// Get the palettes.
		ZafPaletteState state = PaletteState();
		ZafPaletteStruct textPalette = LogicalPalette(ZAF_PM_TEXT, state);
		ZafPaletteStruct backgroundPalette = LogicalPalette(ZAF_PM_BACKGROUND, state);

		// Get the OS colors from the text palette.
		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;
		}
		break;

	// Allow prompts to get mouse clicks.
	case WM_NCHITTEST:
		ccode = HTCLIENT;
		break;

	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
#if defined(ZAF_WIN32)
		SetForegroundWindow(RootObject()->OSScreenID(ZAF_FRAMEID));
#else
		SetActiveWindow(RootObject()->OSScreenID(ZAF_FRAMEID));
#endif
		break;

	case WM_PAINT:
	case WM_ERASEBKGND:
		if (OSDraw() && !transparentBackground && autoSize && !editMode &&
			VtJustify() == ZAF_VT_TOP)
			ccode = DefaultCallback(event);
		else
			ccode = ZafWindowObject::OSEvent(event);
		break;

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

	return (ccode);
}

void ZafPrompt::OSRegisterObject(void)
{
	// Set the Windows style flags.
	DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
	DWORD dwExStyle = 0;

	if (visible)
		dwStyle |= WS_VISIBLE;

	// Set justification.
	if (HzJustify() == ZAF_HZ_CENTER)
		dwStyle |= SS_CENTER;
	else if (HzJustify() == ZAF_HZ_RIGHT)
		dwStyle |= SS_RIGHT;
	else
		dwStyle |= SS_LEFT;

	if (Disabled() && !editMode)
		dwStyle |= WS_DISABLED;

	// Create the prompt.
	screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, ZAF_ITEXT("STATIC"), dwStyle, ZAF_NULLP(ZafIChar), 0, dwExStyle);

	// 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();
}

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

	// Set the text with the OS.
	if (screenID)
	{
		if (autoSize)
		{
			// Resize the prompt for the new text.
			Event(ZafEventStruct(S_COMPUTE_SIZE));
			OSSize();
		}

		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
			{
				// Prevent the prompt from wrapping the text.
				if (visible)
					ShowWindow(OSScreenID(ZAF_FRAMEID), SW_HIDE);
				::SendMessage(screenID, WM_SETTEXT, (WPARAM)0, (LPARAM)drawText);
				if (visible)
					ShowWindow(OSScreenID(ZAF_FRAMEID), SW_SHOW);
			}

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

	return ZAF_ERROR_NONE;
}

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

void ZafPrompt::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
 	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_WINDOWTEXT);
		}
		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_WINDOW);
		}
	}
	else
		ZafWindowObject::OSMapPalette(palette, type, state);
}

ZafError ZafPrompt::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	return (ZafWindowObject::OSUpdatePalettes(zafTypes, osTypes));
}

