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

#include <w_app.hpp>
#include <z_spin.hpp>
#include <z_str1.hpp>

#define UPDOWN_HEIGHT_TO_WIDTH(h) (h * 3 / 4)

#if !defined(ZAF_WIN32)
unsigned int SPIN_STATE_UP = 			0x0001;
unsigned int SPIN_STATE_DOWN = 			0x0002;
unsigned int SPIN_STATE_OVERLAP_UP = 	0x0004;
unsigned int SPIN_STATE_OVERLAP_DOWN = 	0x0008;
#endif

#if defined(ZAF_WIN32)
LRESULT CALLBACK SpinEditProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
	// Create a ZAF event.
	ZafEventStruct event(E_MSWINDOWS);
	event.osEvent.hwnd = hwnd;
	event.osEvent.message = wMsg;
	event.osEvent.wParam = wParam;
	event.osEvent.lParam = lParam;

	// Send the event.
	if (event.InputType() == E_MOUSE)
	{
		// Get a pointer to the combo box.
		HWND hwndSpin = GetParent(hwnd);
		ZafWindowObject *spin = ZafMSWindowsApp::ObjectFromHandle(hwndSpin);

		// Route messages through the parent so that special edit mode
		// processing can be done.
		return (spin->Event(event));
	}
	else
		return (DefaultCallback(event));
}
#endif

// ----- ZafSpinControl ------------------------------------------------------

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafSpinControl::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
//	if (!systemObject)
//		return (ZafWindowObject::Draw(event, ccode));

	// Begin drawing operation.
	ZafRegionStruct drawRegion = BeginDraw();

//	if (!systemObject || ccode == S_CURRENT || ccode == S_NON_CURRENT)
	if (ccode == S_CURRENT || ccode == S_NON_CURRENT)
	{
		EndDraw();
		return ccode;
	}

#if defined(ZAF_WIN32)
	if (!SystemObject())
#endif
	{
	// Compute drawing coordinates for the up arrow button.
	ZafRegionStruct spinRegion = drawRegion;
	spinRegion.left = spinRegion.right - UPDOWN_HEIGHT_TO_WIDTH(spinRegion.Height()) + 1;
	int buttonHeight = spinRegion.Height() / 2;
	int top2 = spinRegion.top + buttonHeight;
	int centerX = spinRegion.Width() / 2;
	int centerY = buttonHeight / 2;

	ZafPaletteStruct buttonPal = { ZAF_LINE_SOLID, ZAF_PTN_SOLID_FILL, ZAF_CLR_DEFAULT, ZAF_CLR_DEFAULT, ZAF_MONO_BLACK, ZAF_MONO_WHITE, ZAF_FNT_DIALOG };
	buttonPal.osPalette.colorForeground = buttonPal.osPalette.colorBackground = GetSysColor(COLOR_3DFACE);

	ZafRegionStruct buttonRegion = spinRegion;
	buttonRegion.bottom = top2 - 1;
#if !defined(ZAF_WIN32)
	if ((spinState & (SPIN_STATE_UP | SPIN_STATE_OVERLAP_UP)) == (SPIN_STATE_UP | SPIN_STATE_OVERLAP_UP))
	{
		// Draw the up arrow button in a depressed state.
		DrawShadow(buttonRegion, -2, S_REDISPLAY);
		display->SetPalette(buttonPal);
		display->Rectangle(buttonRegion, 0, true);

		display->SetForeground(ZAF_CLR_BLACK);
		display->SetBackground(ZAF_CLR_BLACK);
		ZafCoordinate polyPoints[6];
		polyPoints[0] = spinRegion.left + centerX - centerX / 3 + 1;
		polyPoints[1] = spinRegion.top + centerY + centerX / 6 + 1;
		polyPoints[2] = spinRegion.left + centerX + centerX / 3 + 1;
		polyPoints[3] = polyPoints[1];
		polyPoints[4] = spinRegion.left + centerX + 1;
		polyPoints[5] = polyPoints[1] - centerX / 3;
		display->Polygon(3, polyPoints, 1, true, true);
	}
	else
#endif
	{
		// Draw the up arrow button in an un-depressed state.
		DrawShadow(buttonRegion, 2, S_REDISPLAY);

		display->SetPalette(buttonPal);
		display->Rectangle(buttonRegion, 0, true);

		display->SetForeground(ZAF_CLR_BLACK);
		display->SetBackground(ZAF_CLR_BLACK);
		ZafCoordinate polyPoints[6];
		polyPoints[0] = spinRegion.left + centerX - centerX / 3;
		polyPoints[1] = spinRegion.top + centerY + centerX / 6;
		polyPoints[2] = spinRegion.left + centerX + centerX / 3;
		polyPoints[3] = polyPoints[1];
		polyPoints[4] = spinRegion.left + centerX;
		polyPoints[5] = polyPoints[1] - centerX / 3;
		display->Polygon(3, polyPoints, 1, true, true);
	}

	// Compute drawing coordinates for the down arrow button.
	buttonRegion = spinRegion;
	buttonRegion.top = top2;
	buttonRegion.bottom = top2 + buttonHeight - 1;
#if !defined(ZAF_WIN32)
	if ((spinState & (SPIN_STATE_DOWN | SPIN_STATE_OVERLAP_DOWN)) == (SPIN_STATE_DOWN | SPIN_STATE_OVERLAP_DOWN))
	{
		// Draw the down arrow button in a depressed state.
		DrawShadow(buttonRegion, -2, S_REDISPLAY);
		display->SetPalette(buttonPal);
		display->Rectangle(buttonRegion, 0, true);

		display->SetForeground(ZAF_CLR_BLACK);
		display->SetBackground(ZAF_CLR_BLACK);
		ZafCoordinate polyPoints[6];
		polyPoints[0] = spinRegion.left + centerX - centerX / 3 + 1;
		polyPoints[1] = top2 + centerY - centerX / 6 + 1;
		polyPoints[2] = spinRegion.left + centerX + centerX / 3 + 1;
		polyPoints[3] = polyPoints[1];
		polyPoints[4] = spinRegion.left + centerX + 1;
		polyPoints[5] = polyPoints[1] + centerX / 3;
		display->Polygon(3, polyPoints, 1, true, true);
	}
	else
#endif
	{
		// Draw the down arrow button in an un-depressed state.
		DrawShadow(buttonRegion, 2, S_REDISPLAY);
		display->SetPalette(buttonPal);
		display->Rectangle(buttonRegion, 0, true);

		display->SetForeground(ZAF_CLR_BLACK);
		display->SetBackground(ZAF_CLR_BLACK);
		ZafCoordinate polyPoints[6];
		polyPoints[0] = spinRegion.left + centerX - centerX / 3;
		polyPoints[1] = top2 + centerY - centerX / 6;
		polyPoints[2] = spinRegion.left + centerX + centerX / 3;
		polyPoints[3] = polyPoints[1];
		polyPoints[4] = spinRegion.left + centerX;
		polyPoints[5] = polyPoints[1] + centerX / 3;
		display->Polygon(3, polyPoints, 1, true, true);
	}
	}
//#endif

	if (!systemObject)
	{
		zafRegion.right -= UPDOWN_HEIGHT_TO_WIDTH(drawRegion.Height());
		ZafWindowObject::Draw(event, ccode);
		zafRegion.right += UPDOWN_HEIGHT_TO_WIDTH(drawRegion.Height());
	}

	// End the drawing operation.
	EndDraw();

	return (ccode);
}
#endif

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

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

	// Process ZAF events.
	switch (ccode)
	{
#if defined(ZAF_WIN32)
//??? Wont work for spin children added at run-time.
	case S_REGISTER_OBJECT:
		{
		ccode = ZafWindow::Event(event);
		ZafString *spinString = DynamicPtrCast(First(), ZafString);
		if (spinString)
		{
			spinString->SetParentPalette(true);
//???			SendMessage(hwndUpDown, UDM_SETBUDDY, (LPARAM)spinString->screenID, 0);
		}

//		if (editMode)
		if (systemObject)
			SetWindowLong(hwndUpDown, GWL_WNDPROC, (LONG)SpinEditProc);
		}
		break;
#else
//??? Wont work for spin children added at run-time.
	case S_REGISTER_OBJECT:
		{
		ZafString *spinString = DynamicPtrCast(First(), ZafString);
		if (spinString)
			spinString->SetParentPalette(true);
		ccode = ZafWindow::Event(event);

/* START BLOCK COMMENT
**			if (editMode)
**			{
**				HWND hwndChild = GetWindow(screenID, GW_CHILD);
**				while (hwndChild)
**				{
**					SetWindowLong(hwndChild, GWL_WNDPROC, (LONG)SpinChildEditProc);
**					hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
**				}
**			}
END BLOCK COMMENT */
		}
		break;
#endif

#if !defined(ZAF_WIN32)
	case S_INITIALIZE:
		spinState = 0;
		ccode = ZafWindow::Event(event);
		break;
#endif

	case N_SIZE:
		{
		int spinButtonWidth = UPDOWN_HEIGHT_TO_WIDTH(zafRegion.Height());

		ZafString *spinString = DynamicPtrCast(First(), ZafString);
		if (spinString)
		{
			spinString->zafRegion.left = spinString->zafRegion.top = 0;
			spinString->zafRegion.right = zafRegion.Width() - spinButtonWidth - 1;
			spinString->zafRegion.bottom = zafRegion.Height() - 1;
			spinString->OSSize();
		}

#if defined(ZAF_WIN32)
		if (systemObject)
		{
			MoveWindow(hwndUpDown, zafRegion.Width() - spinButtonWidth - 1, 0, spinButtonWidth, zafRegion.Height() - 1, TRUE);
			UpdateWindow(hwndUpDown);
		}
#else
		Redisplay();
#endif
		}
		break;

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

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

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

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

	// Switch on the windows message.
	ZafEventType ccode = event.osEvent.message;
	switch (ccode)
	{
#if defined(ZAF_WIN32)
	case WM_NOTIFY:
		return (0);

	case WM_VSCROLL:
		{
		if (Disabled())
			break;
		if (!Noncurrent())
			SetFocus(true);
		ZafString *spinString = DynamicPtrCast(First(), ZafString);
		if (spinString)
		{
			int pos = (int)HIWORD(event.osEvent.wParam);
			if (pos < 1)
				spinString->Decrement(delta, WrappedData());
			else if (pos > 1)
				spinString->Increment(delta, WrappedData());
			if (systemObject)
				SendMessage(hwndUpDown, UDM_SETPOS, 0, MAKELONG(1, 0));

			// Call the user function.
			(this->*memberUserFunction)(event, L_SELECT);
		}
		}
		return(0);
#else
	case WM_LBUTTONDBLCLK:
	case WM_LBUTTONDOWN:
		{
		if (Disabled())
			break;
		ccode = ZafWindow::Event(event);
		SetCapture(screenID);
		ConvertToZafEvent((ZafEventStruct &)event);
		ZafString *spinString = DynamicPtrCast(First(), ZafString);
		if (spinString)
			spinString->SetFocus(true);
		if (event.position.line < zafRegion.Height() / 2)
		{
			spinState = SPIN_STATE_UP | SPIN_STATE_OVERLAP_UP;
			if (spinString)
				spinString->Increment(delta, WrappedData());
		}
		else
		{
			spinState = SPIN_STATE_DOWN | SPIN_STATE_OVERLAP_DOWN;
			if (spinString)
				spinString->Decrement(delta, WrappedData());
		}

		// Call the user function.
		(this->*memberUserFunction)(event, L_SELECT);

		Draw(event, S_REDISPLAY);
		SetTimer(screenID, ZAF_BUTTON_TIMER_ID, InitialDelay(), NULL);
		}
		break;

	case WM_MOUSEMOVE:
		{
		ccode = ZafWindow::Event(event);
		unsigned int oldState = spinState;
		spinState &= ~(SPIN_STATE_OVERLAP_UP | SPIN_STATE_OVERLAP_DOWN);
		ConvertToZafEvent((ZafEventStruct &)event);
		ZafRegionStruct buttonRegion;
		buttonRegion.coordinateType = ZAF_PIXEL;
		buttonRegion.left = zafRegion.Width() - UPDOWN_HEIGHT_TO_WIDTH(zafRegion.Height()) + 1;
		buttonRegion.top = 0;
		buttonRegion.right = zafRegion.Width() - 1;
		buttonRegion.bottom = zafRegion.Height() - 1;
		if (buttonRegion.Overlap(event.position))
		{
			if (event.position.line < zafRegion.Height() / 2)
				spinState |= SPIN_STATE_OVERLAP_UP;
			else
				spinState |= SPIN_STATE_OVERLAP_DOWN;
		}

		if (spinState != oldState)
			Draw(event, S_REDISPLAY);
		}
		break;

	case WM_LBUTTONUP:
  		KillTimer(screenID, ZAF_BUTTON_TIMER_ID);
		ReleaseCapture();
		spinState = 0;
		Draw(event, S_REDISPLAY);
		ccode = ZafWindow::Event(event);
		break;

	case WM_TIMER:
		{
		ZafString *spinString = DynamicPtrCast(First(), ZafString);
		if (spinString)
		{
			if ((spinState & (SPIN_STATE_UP | SPIN_STATE_OVERLAP_UP)) == (SPIN_STATE_UP | SPIN_STATE_OVERLAP_UP))
			{
				spinString->Increment(delta, WrappedData());

				// Call the user function.
				(this->*memberUserFunction)(event, L_SELECT);
			}
			else if ((spinState & (SPIN_STATE_DOWN | SPIN_STATE_OVERLAP_DOWN)) == (SPIN_STATE_DOWN | SPIN_STATE_OVERLAP_DOWN))
			{
				spinString->Decrement(delta, WrappedData());

				// Call the user function.
				(this->*memberUserFunction)(event, L_SELECT);
			}
		}
		SetTimer(screenID, ZAF_BUTTON_TIMER_ID, RepeatDelay(), NULL);
		ccode = 0;
		}
		break;
#endif

#if defined(ZAF_WIN32)
	case WM_CTLCOLOREDIT:
#else
	case WM_CTLCOLOR:
#endif
		{
		ZafString *spinString = DynamicPtrCast(First(), ZafString);
		if (spinString)
			ccode = spinString->OSEvent(event);
		else
			ccode = ZafWindowObject::Event(event);
		}
		break;

	case WM_KEYDOWN:
		{
		ZafString *spinString = DynamicPtrCast(First(), ZafString);
		if (spinString)
		{
			if (event.osEvent.wParam == VK_UP)
			{
				spinString->Increment(delta, WrappedData());
				return (0);
			}
			else if (event.osEvent.wParam == VK_DOWN)
			{
				spinString->Decrement(delta, WrappedData());
				return (0);
			}
		}
		ccode = ZafWindow::Event(event);
		}
		break;

#if defined(ZAF_WIN32)
	case WM_PAINT:
		ccode = DefaultCallback(event);
		break;
#else
	case WM_PAINT:
	case WM_ERASEBKGND:
		ccode = ZafWindowObject::Event(event);
		break;
#endif

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

	return (ccode);
}

void ZafSpinControl::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	// Get default colors from the operating system.
	if (type == ZAF_PM_BACKGROUND)
	{
		if (palette.colorForeground == ZAF_CLR_DEFAULT && palette.osPalette.colorForeground == 0xFFFFFFFFL)
			palette.osPalette.colorForeground = GetSysColor(COLOR_WINDOW);
		if (palette.colorBackground == ZAF_CLR_DEFAULT && palette.osPalette.colorBackground == 0xFFFFFFFFL)
			palette.osPalette.colorBackground = GetSysColor(COLOR_WINDOW);
	}
	else
		ZafWindow::OSMapPalette(palette, type, state);
}

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

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

#if defined(ZAF_WIN32)
	screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, NULL, dwStyle, ZAF_NULLP(ZafIChar), 0, dwExStyle);
	hwndUpDown = CreateWindowEx(0L, UPDOWN_CLASS, NULL, WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0, screenID, 0, ZafMSWindowsApp::hInstance, NULL);
	SendMessage(hwndUpDown, UDM_SETRANGE, 0, MAKELONG(2, 0));
 	SendMessage(hwndUpDown, UDM_SETPOS, 0, MAKELONG(1, 0));
#else
	screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, NULL, dwStyle, ZAF_NULLP(ZafIChar), 0, dwExStyle);
#endif
}

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

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

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

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

bool ZafSpinControl::SetAcceptDrop(bool setAcceptDrop)
{
	return (ZafWindowObject::SetAcceptDrop(setAcceptDrop));
}

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

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