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

#include <w_app.hpp>
#include <z_combo.hpp>
#include <z_ctype.hpp>
#include <z_str1.hpp>

LRESULT CALLBACK ComboStringProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
	// Route messages through the parent combo-box so that special
	// processing (drag & drop, edit mode, etc.) can be done.

	// Get a pointer to the combo box.
	HWND hCombo = GetParent(hwnd);
	ZafWindowObject *combo = ZafMSWindowsApp::ObjectFromHandle(hCombo);

	// Create a ZAF event.
	ZafEventStruct event(E_MSWINDOWS);
	event.osEvent.hwnd = hwnd;
	event.osEvent.message = wMsg;
	event.osEvent.wParam = wParam;
	event.osEvent.lParam = lParam;

	// Route the event. (Keyboard events are routed top-down.)
	ZafEventType inputType = event.InputType();
	if (combo->EditMode() && (inputType == E_KEY || inputType == E_MOUSE))
		return (combo->ZafWindowObject::Event(event));
	else if (inputType == E_KEY)
	{
		// Route the event to the root window to be processed top-down.
		// (The event has already been passed to the window manager.)
		event.route = combo;

		return (combo->RootObject()->Event(event));
	}
	else
		return (combo->Event(event));
}

// ----- ZafComboBox --------------------------------------------------------

ZafWindowObject *ZafComboBox::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	if (object->parent)
		return (object);

	object->SetSystemObject(false);
	object->SetParentDrawBorder(true);
	object->SetParentDrawFocus(true);
	object->SetParentPalette(true);

	// Add the object to the drop-down list.
	list->ZafWindow::Add(object, position);

	if (object->SupportObject())
	{
		if (screenID)
		{
			//??? this can probably be done efficiantly at the window level
			//??? using SetWindowLong.
			Event(S_DESTROY);
			Event(S_CREATE);
		}

		return (object);
	}

	// Update the current/selected attributes.
	if (object->Selected())
		NotifySelection(object, true);
	else if (Current() == object)
		object->SetSelected(true);

	// Insert the object into the OS control.
	if (screenID && !object->SupportObject())
	{
		int index = list->Index(object);
		if (ZafMSWindowsApp::convertText && OSDraw())
		{
			char *osText = zafCodeSet->ConvertToOSString(object->Text(), ZAF_NULLP(char), FALSE);
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
			SendMessageA(screenID, CB_INSERTSTRING, (WPARAM)index, (LPARAM)osText);
#else
			SendMessage(screenID, CB_INSERTSTRING, (WPARAM)index, (LPARAM)osText);
#endif
		}
		else
			SendMessage(screenID, CB_INSERTSTRING, (WPARAM)index, OSDraw() ? (LPARAM)object->Text() : (LPARAM)object);

		if (object->Selected())
			SendMessage(screenID, CB_SETCURSEL, (WPARAM)index, (LPARAM)0);
	}

	return (object);
}

void ZafComboBox::ConvertCoordinates(ZafCoordinateType newType)
{
	if (zafRegion.coordinateType != newType)
	{
		if (maxListHeight != -1)
		{
			maxListHeight = (int)display->ConvertYValue(maxListHeight,
				zafRegion.coordinateType, newType);
		}
		if (listWidth != -1)
			listWidth = (int)display->ConvertXValue(listWidth,
				zafRegion.coordinateType, newType);
		ZafWindow::ConvertCoordinates(newType);
	}
}

ZafRegionStruct ZafComboBox::ConvertToDrawRegion(const ZafWindowObject *object,
	const ZafRegionStruct *region) const
{
	if (object == this)
		return (ZafWindowObject::ConvertToDrawRegion(object));
	ZafRegionStruct drawRegion;
	if (region)
	{
		drawRegion = *region;
		drawRegion.left += object->zafRegion.left;
		drawRegion.right += object->zafRegion.left;
		drawRegion.top += object->zafRegion.top;
		drawRegion.bottom += object->zafRegion.top;
	}
	else
		// No conversion is necessary.
		drawRegion = object->zafRegion;
	return (drawRegion);
}

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

	if (bordered)
		DrawBorder(drawRegion, ccode);

	const int buttonWidth = 16;
	ZafRegionStruct buttonRegion = drawRegion;
	buttonRegion.left = buttonRegion.right - buttonWidth + 1;
	DrawShadow(buttonRegion, 2, ccode);
	display->SetBackground(ZAF_CLR_LIGHTGRAY);
	display->Rectangle(buttonRegion, 0, true);
	drawRegion.right -= buttonWidth;
	display->SetForeground(ZAF_CLR_BLACK);
	int left = buttonRegion.left + buttonRegion.Width() / 2 - 3;
	int top = buttonRegion.top + buttonRegion.Height() / 2 - 2;
	display->Line(left, top, left + 6, top); 
	display->Line(left + 1, top + 1, left + 5, top + 1); 
	display->Line(left + 2, top + 2, left + 4, top + 2); 
	display->Pixel(left + 3, top + 3); 

	ZafPaletteState state = PaletteState();

	display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
	display->Rectangle(drawRegion, 0, true);

	// Draw the text.
	const ZafIChar *text = Text();
	if (text && (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION || ccode == S_REDISPLAY_DATA))
	{
		// Set the text palette.
		display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));

		// Draw the text.
		//??? This function should clip text?
		display->Text(drawRegion, text, -1, ZAF_HZ_LEFT, ZAF_VT_TOP);
	}
	display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));

	EndDraw();
	return (ccode);
}

ZafEventType ZafComboBox::DrawBorder(ZafRegionStruct &region,
	ZafEventType ccode)
{
	return (ZafWindowObject::DrawBorder(region, ccode));
}

ZafEventType ZafComboBox::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	return (ZafWindowObject::DrawFocus(region, ccode));
}
#endif

ZafEventType ZafComboBox::DragDropEvent(const ZafEventStruct &event)
{
	ZafEventType ccode = event.type;

	switch (ccode)
	{
	case S_DROP_DEFAULT:
		if (windowManager->dragObject->MoveDraggable())
			ccode = S_DROP_MOVE;
		else if (windowManager->dragObject->CopyDraggable())
			ccode = S_DROP_COPY;
		else
		{
			ccode = S_ERROR;
			break;
		}
		//Continue.
	case S_DROP_COPY:
	case S_DROP_MOVE:
		if (Disabled())
			return (S_ERROR);
		else if (acceptDrop && this != windowManager->dragObject)
		{
			if (windowManager->dragObject->IsA(ID_ZAF_COMBO_BOX) ||
				windowManager->dragObject->IsA(ID_ZAF_VT_LIST) ||
				windowManager->dragObject->IsA(ID_ZAF_HZ_LIST))
			{
				// Move the selected objects.
				ZafWindow *dragList = DynamicPtrCast(windowManager->dragObject, ZafWindow);
				ZafWindowObject *object;
				if (windowManager->dragObject->IsA(ID_ZAF_COMBO_BOX))
					object = DynamicPtrCast(dragList, ZafComboBox)->First();
				else
					object = dragList->First();
				while (object)
				{
					ZafWindowObject *nextObject = object->Next();
					if (object->Selected())
					{
						if (ccode == S_DROP_COPY)
							Add(object->Duplicate());
						else
						{
							dragList->Subtract(object);
							Add(object);
						}
					}
					object = nextObject;
				}
			}
			else
			{
				const ZafIChar *dropText = windowManager->dragObject->Text();
				if (dropText && *dropText)
				{
					ZafString *newString = new ZafString(0, 0, 0, new ZafStringData(dropText));
					newString->SetBordered(false);
					Add(newString);
					if (ccode != S_DROP_COPY)
						windowManager->dragObject->SetText(ZafLanguageManager::blankString);
				}
				else
					ccode = S_ERROR;
			}
		}
		break;

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

	return (ccode);
}

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

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

	ccode = LogicalEvent(event);
	// Switch on the event type.
	switch (ccode)
	{
	case S_INITIALIZE:
	case S_DEINITIALIZE:
		{
		// Default to the base class.
		ccode = ZafWindowObject::Event(event);
		list->Event(event);
		}
		break;

	case S_REGISTER_OBJECT:
		{
		ZafWindowObject::Event(event);
		list->SetSystemObject(false);
		list->screenID = screenID;

		int index = 0;
		for (ZafWindowObject *object = list->First(); object; object = object->Next())
		{
			object->screenID = screenID;

			if (ZafMSWindowsApp::convertText && OSDraw())
			{
				char *osText = zafCodeSet->ConvertToOSString(object->Text(), ZAF_NULLP(char), FALSE);
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
				SendMessageA(screenID, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)osText);
#else
				SendMessage(screenID, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)osText);
#endif
			}
			else
				SendMessage(screenID, CB_INSERTSTRING, (WPARAM)-1, OSDraw() ? (LPARAM)object->Text() : (LPARAM)object);

			if (object->Selected())
				SendMessage(screenID, CB_SETCURSEL, (WPARAM)index, (LPARAM)0);
			index++;
		}
		}
		break;

	case S_ADD_OBJECT:
	case S_SUBTRACT_OBJECT:
		ccode = ZafWindow::Event(event);
		break;

	case N_NON_CURRENT:
		ccode = ZafWindowObject::Event(event);
		OSGetText();
		break;

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

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

void ZafComboBox::GetClip(ZafWindowObject *, ZafRegionStruct &, ZafRegionStruct &)
{
	// Handled automatically by Windows.
}

ZafWindowObject *ZafComboBox::NotifyFocus(ZafWindowObject *focusObject, bool setFocus)
{
	if (focusObject == this)
		return (ZafWindowObject::NotifyFocus(focusObject, setFocus));

	ZafWindowObject *invalidObject;
	if (!setFocus) // down
	{
		// Remove focus from the current branch.
		if (Current())
			Current()->NotifyFocus(focusObject, false);

		// Remove focus from the "this"
		invalidObject = ZafWindowObject::NotifyFocus(focusObject, false);
	}
	else if (!focus) // up
	{
		// Recurse the entire focus path.  Give this focusObject focus.
		invalidObject = ZafWindowObject::NotifyFocus(focusObject, true);

		// Set focus if validation succeeded.
		if (!invalidObject)
			SetCurrent(focusObject);
	}
	else // transition
	{
	 	// This window is the root of the focus path.  Transition focus
		// if focusObject doesn't already have it.
		if (Current() && Current() != focusObject)
			Current()->NotifyFocus(this, false);

		// Combo children can't perform field validation.
		invalidObject = ZAF_NULLP(ZafWindowObject);
		SetCurrent(focusObject);
	}

	if (focusObject != this && setFocus && !invalidObject)
	{
		int index = list->Index(focusObject);
		if (SendMessage(screenID, CB_GETCURSEL, (WPARAM)0, (LPARAM)0) != index)
			SendMessage(screenID, CB_SETCURSEL, (WPARAM)index, (LPARAM)0);
	}

	return (invalidObject);
}

ZafWindowObject *ZafComboBox::NotifySelection(ZafWindowObject *object, bool setSelected)
{
	if (setSelected)
	{
		SetCurrent(object);
		stringData.SetText(object->Text());
		if (screenID)
		{
			int index = list->Index(object);
			if (SendMessage(screenID, CB_GETCURSEL, (WPARAM)0, (LPARAM)0) != index)
				SendMessage(screenID, CB_SETCURSEL, (WPARAM)index, (LPARAM)0);
			SetChanged(true);
		}

	}
	return (object);
}

bool ZafComboBox::SetAutoSortData(bool autoSortData)
{
	// Set the list attribute.
	return (list->SetAutoSortData(autoSortData));
}

void ZafComboBox::SetListRegion(const ZafRegionStruct &region)
{
	if (!screenID)
	{
		list->zafRegion = region;
		maxListHeight = region.Height();
		listWidth = region.Width();
	}
}

int ZafComboBox::SetMaximumListHeight(int tMaxListHeight)
{
	if (!screenID)
		maxListHeight = tMaxListHeight;
	
	return (maxListHeight);
}

void ZafComboBox::SetRegion(const ZafRegionStruct &region)
{
	if (region != zafRegion && zafRegion.coordinateType != region.coordinateType)
	{
		if (maxListHeight != -1)
			maxListHeight = (int)display->ConvertYValue(maxListHeight,
				zafRegion.coordinateType, region.coordinateType);
	}
	ZafWindow::SetRegion(region);
}

ZafError ZafComboBox::SetText(const ZafIChar *text)
{
	if (!ViewOnly())
	{
		stringData.SetText(text);
		return (OSSetText());
	}

	return (ZAF_ERROR_NONE);
}

const ZafIChar *ZafComboBox::Text(void)
{
	OSGetText();
	return (stringData.Text());
}

ZafWindowObject *ZafComboBox::Subtract(ZafWindowObject *object)
{
	if (screenID && !object->SupportObject())
		SendMessage(screenID, CB_DELETESTRING, (WPARAM)list->Index(object), (LPARAM)0);

	list->Subtract(object);

	if (screenID && !list->First())
		SendMessage(screenID, CB_RESETCONTENT, (WPARAM)0, (LPARAM)0L);

	if (screenID && object->SupportObject())
	{
		//??? this can probably be done efficiently at the window level
		//??? using SetWindowLong.
		Event(S_DESTROY);
		Event(S_CREATE);
	}

	return (object);
}

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

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

bool ZafComboBox::SetFocus(bool setFocus)
{
	return (ZafWindowObject::SetFocus(setFocus));
}

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

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

void ZafComboBox::OSDestroy(void)
{
	if (screenID)
	{
		SendMessage(screenID, CB_RESETCONTENT, 0, 0);
		for (ZafWindowObject *object = list->First(); object; object = object->Next())
			object->Event(S_DEINITIALIZE);
	}
}

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

	ZafEventType ccode = event.type;

	switch (event.osEvent.message)
	{
	case WM_COMMAND:
		{
#if defined(ZAF_WIN32)
		HWND hwndCtrl = (HWND)event.osEvent.lParam;
#else
		HWND hwndCtrl = (HWND)LOWORD(event.osEvent.lParam);
#endif

		// Sometimes the WM_COMMAND goes to the child first and it needs to
		// both process it and pass it to the DefWindProc. Passing it to
		// DefWindProc usually results in it going to the parent window,
		// which sends it back, resulting in the child processing the
		// message twice, or worse, an infinite loop. Thus, on messages the
		// parent sends back, we zero out the hwnd in lParam so the child
		// knows it can process the message.
		if (hwndCtrl)
			ccode = ZafWindowObject::OSEvent(event);
		else
		{
#if defined(ZAF_WIN32)
			WORD wNotifyCode = HIWORD(event.osEvent.wParam);
#else
			WORD wNotifyCode = HIWORD(event.osEvent.lParam);
#endif
			if (wNotifyCode == CBN_SELCHANGE)
			{
				// Get index of the current item.
				int currentIndex = (int)SendMessage(screenID, CB_GETCURSEL, 0, 0);

				// Get the new current/selected object.
				ZafWindowObject *object = DynamicPtrCast(list->ZafList::Get(currentIndex), ZafWindowObject);
				if (object)
				{
					object->NotifyFocus(object, true);
					object->SetSelected(true);

					// On selection changes both the combo's callback and
					// the item's callback functions should be called.
					ccode = ZafComboBox::Event(ZafEventStruct(L_SELECT));
					(object->*(object->memberUserFunction))(event, L_SELECT);
				}
			}
			else if (wNotifyCode == CBN_CLOSEUP)
			{
				if (parent->IsA(ID_ZAF_TABLE_RECORD))
				{
					// This is only needed when an object such as the combo, with
					// a drop down list, are in a table record.  The problem is
					// that the region returned to us from BeginPaint at the
					// ZafTable level is the region of the record which contains
					// the combo and not the region for the table.  Because
					// BeginPaint excludes any drawing outside the update region
					// the ZafTable::Draw fails when drawing the other table
					// records.  For this reaons we need a different mechanism for
					// causing the table to be repainted.

					// Get the combo's list rectangle.
					RECT closeRect;
					SendMessage(screenID, CB_GETDROPPEDCONTROLRECT,
						0, (LPARAM)&closeRect);

					// Get the screen position relative to the root window.
					POINT point;
					point.x = closeRect.left;
					point.y = closeRect.top;
					ScreenToClient(RootObject()->OSScreenID(), &point);

					// Set up the region based on the root object.
					ZafEventStruct closeEvent(S_REDISPLAY_REGION);
					int width = closeRect.right - closeRect.left;
					int height = closeRect.bottom - closeRect.top;
					closeEvent.region.left = point.x;
					closeEvent.region.top = point.y;
					closeEvent.region.right = closeEvent.region.left + width;
					closeEvent.region.bottom = closeEvent.region.top + height;
					closeEvent.route = RootObject();

					// Place the event on the queue to avoid the race condition.
					eventManager->Put(closeEvent, Q_BEGIN);
				}
				ZafMSWindowsApp::dropDownCombo = 0;
				Event(N_CLOSE_COMBO_LIST);
			}
			else if (wNotifyCode == CBN_DROPDOWN)
			{
				ZafMSWindowsApp::dropDownCombo = screenID;
				Event(N_OPEN_COMBO_LIST);
			}
		}
		}
		break;

#if defined(ZAF_WIN32)
	case WM_CTLCOLOREDIT:
	case WM_CTLCOLORLISTBOX:
#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.
		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;

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

		// Begin drawing operation.
		display->BeginDraw(drawItemStruct->hDC, 0, zafRegion, zafRegion);

		if (drawItemStruct->itemID == (UINT)-1)
		{
			ZafRegionStruct focusRegion;
			focusRegion.coordinateType = ZAF_PIXEL;
			focusRegion.left = drawItemStruct->rcItem.left;
			focusRegion.top = drawItemStruct->rcItem.top;
			focusRegion.right = drawItemStruct->rcItem.right - 1;
			focusRegion.bottom = drawItemStruct->rcItem.bottom - 1;

			if (drawItemStruct->itemState & ODS_FOCUS)
				DrawFocus(focusRegion, S_CURRENT);
			else
				DrawFocus(focusRegion, S_NON_CURRENT);
		}
		else
		{
			ZafWindowObject *object = (ZafWindowObject *)drawItemStruct->itemData;
			object->zafRegion.left = drawItemStruct->rcItem.left;
			object->zafRegion.top = drawItemStruct->rcItem.top;
			object->zafRegion.right = drawItemStruct->rcItem.right - 1;
			object->zafRegion.bottom = drawItemStruct->rcItem.bottom - 1;

			if (drawItemStruct->itemAction & ODA_SELECT)
				drawItemStruct->itemAction |= ODA_DRAWENTIRE;

			if (drawItemStruct->itemAction & ODA_FOCUS)
			{
				if (drawItemStruct->itemState & ODS_FOCUS)
					DrawFocus(object->zafRegion, S_CURRENT);
				else
					DrawFocus(object->zafRegion, S_NON_CURRENT);
			}
			else
				object->Event(event); //???
//				object->OSEvent(event);
		}

		// End drawing operation.
		display->EndDraw();
		ccode = TRUE;
		}
		break;

	case WM_KEYDOWN:
		{
		int keyValue = (int)event.osEvent.wParam;

		if (keyValue == VK_RETURN || keyValue == VK_ESCAPE)
		{
			if ((keyValue == VK_RETURN && SendMessage(screenID, CB_GETDROPPEDSTATE, (WPARAM)0, (LPARAM)0)) ||
				keyValue == VK_ESCAPE)
			{
				SendMessage(screenID, CB_SHOWDROPDOWN, (WPARAM)false, (LPARAM)0);
				ccode = 0;
			}
			else if (keyValue == VK_RETURN)
			{
				if (event.osEvent.message == WM_KEYDOWN)
					ccode = ZafComboBox::Event(ZafEventStruct(L_SELECT));
				else
					ccode = 0;
			}
		}
		else if (keyValue == VK_TAB)
			ccode = S_UNKNOWN;
		else if (keyValue == VK_F1)
			ccode = ZafWindowObject::OSEvent(event);
		else
		{
			if (event.osEvent.hwnd == screenID)
				ccode = ZafWindowObject::OSEvent(event);
			else
			{
				DefaultCallback(event);
				ccode = S_UNKNOWN;
			}
			if (ccode == S_UNKNOWN)
			{
				// Translate the virtual key code to an ASCII value.
				UINT charValue = MapVirtualKey(keyValue, 2);
				if ((charValue >= '0' && charValue <= '9') ||
				 (charValue >= 'A' && charValue <= 'Z') ||
				 (keyValue >= VK_NUMPAD0 && keyValue <= VK_DIVIDE) ||
				 (keyValue >= VK_SPACE && keyValue <= VK_DOWN) ||
				 keyValue == VK_BACK)
					ccode = 0;
			}
		}
		}
		break;

	case WM_CHAR:
		if (event.osEvent.wParam == VK_TAB ||
			event.osEvent.wParam == VK_ESCAPE ||
			event.osEvent.wParam == VK_RETURN)
			ccode = 0;
		else
			ccode = ZafWindowObject::OSEvent(event);
		break;

/* START BLOCK COMMENT
**		case WM_LBUTTONUP:
**			{
**			ccode = ZafWindowObject::OSEvent(event);
**	
**			// Make sure up clicks get to the edit field during drag & drop
**			// operations.
**	//		if (GetCapture() == screenID)
**			{
**				HWND childID = GetWindow(screenID, GW_CHILD);
**				if (childID)
**					SendMessage(childID, event.osEvent.message, event.osEvent.wParam,
**						event.osEvent.lParam);
**			}
**			}
**			break;
END BLOCK COMMENT */

	case WM_MEASUREITEM:
		if (OSDraw())
			ccode = ZafWindowObject::OSEvent(event);
		else
		{
			MEASUREITEMSTRUCT *measureItemStruct = (MEASUREITEMSTRUCT *)event.osEvent.lParam;
			if (measureItemStruct->itemID == (UINT)-1)
			{
				int maxHeight = 0;
				int maxWidth = 0;
				for (ZafWindowObject *object = list->First(); object; object = object->Next())
				{
					if (!object->screenID)
						continue;
					object->Event(ZafEventStruct(S_COMPUTE_SIZE));
					int width = object->zafRegion.Width();
					int height = object->zafRegion.Height();
					if (width > maxWidth)
						maxWidth = width;
					if (height > maxHeight)
						maxHeight = height;
				}

				if (maxHeight == 0)
					maxHeight = display->cellHeight - display->preSpace - display->postSpace;

				measureItemStruct->itemWidth = maxWidth;
				measureItemStruct->itemHeight = maxHeight;
			}
			else
			{
				ZafWindowObject *object = (ZafWindowObject *)measureItemStruct->itemData;
				object->Event(ZafEventStruct(S_COMPUTE_SIZE));
				measureItemStruct->itemWidth = object->zafRegion.Width();
				measureItemStruct->itemHeight = object->zafRegion.Height();
			}
			ccode = TRUE;
		}
		break;

	case WM_PAINT:
	case WM_ERASEBKGND:
		// Don't call Draw().
		ccode = DefaultCallback(event);
		break;

	case WM_WINDOWPOSCHANGED:
		// Prevent change to zafRegion.
		ccode = DefaultCallback(event);
		break;

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

	return (ccode);
}

ZafError ZafComboBox::OSGetText(void)
{
	// Update the internal text.
	if (screenID && SystemObject() && OSDraw())
	{
		int textLength = (WORD)SendMessage(screenID, WM_GETTEXTLENGTH, (WPARAM)0, (LPARAM)0);
#if defined(ZAF_UNICODE)
		if (ZafMSWindowsApp::convertText)
		{
			char *osText = new char[textLength + 1];
#	if defined(ZAF_WIN32)
			SendMessageA(screenID, WM_GETTEXT, textLength + 1, (LONG)osText);
#	else
			// This test is needed because Windows 3.1 fails on the above
			// call to WM_GETTEXTLENGTH.  API bug.
			if (ZafMSWindowsApp::windowsPlatform == ZAF_WIN16)
			{
				const ZafIChar *text;
				text = list->Current() ? list->Current()->Text() : ZAF_NULLP(ZafIChar);
				stringData.SetText(text);
				delete []osText;
				return (ZAF_ERROR_NONE);
			}
			else
				SendMessage(screenID, WM_GETTEXT, textLength + 1, (LONG)osText);
#	endif
			stringData.SetOSText(osText);
			delete []osText;
		}
		else
		{
			ZafIChar *text = new ZafIChar[textLength + 1];
			SendMessage(screenID, WM_GETTEXT, textLength + 1, (LONG)text);
			stringData.SetText(text);
			delete []text;
		}
#else
		// This test is needed because Windows 3.1 fails on the above
		// call to WM_GETTEXTLENGTH.  API bug.
		if (ZafMSWindowsApp::windowsPlatform == ZAF_WIN16)
		{
			const ZafIChar *text;
			text = list->Current() ? list->Current()->Text() : ZAF_NULLP(ZafIChar);
			stringData.SetText(text);
		}
		else
		{
			char *text = new char[textLength + 1];
			SendMessage(screenID, WM_GETTEXT, textLength + 1, (LONG)text);
			if (ZafMSWindowsApp::convertText)
				stringData.SetOSText(text);
			else
				stringData.SetText(text);
			delete []text;
		}
#endif
	}
	return (ZAF_ERROR_NONE);
}

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

void ZafComboBox::OSRegisterObject(void)
{
	DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
	DWORD dwExStyle = 0;

	if (visible)
		dwStyle |= WS_VISIBLE;

	// Combo boxes always have borders.
#if defined(ZAF_MSWINDOWS_3D)
	if (ZafMSWindowsApp::native3D)
		dwExStyle |= WS_EX_CLIENTEDGE;
	else
		dwStyle |= WS_BORDER;
#else
	dwStyle |= WS_BORDER;
#endif

	if (Disabled() && !editMode)
		dwStyle |= WS_DISABLED;
	if (OSDraw())
	{
		if (ViewOnly())
			dwStyle |= CBS_DROPDOWNLIST;
		else
			dwStyle |= CBS_DROPDOWN | CBS_AUTOHSCROLL;
	}
	else
	{
		dwStyle |= CBS_OWNERDRAWVARIABLE;
		dwStyle |= CBS_DROPDOWNLIST;
	}

	if (list->VerticalScrollBar())
		dwStyle |= WS_VSCROLL;
	screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, ZAF_ITEXT("COMBOBOX"), dwStyle, ZAF_NULLP(ZafIChar), 0, dwExStyle);

	// Set the text.
	if (OSDraw())
	{
		bool saveChanged = changed;
		OSSetText();
		changed = saveChanged;
	}

	// Set the font.
	// Save zafRegion because setting the font automatically adjusts the 
	// combo height, and the initial sizing operation has not occured yet.
	ZafRegionStruct saveRegion = zafRegion;
	ZafPaletteStruct fontPalette = LogicalPalette(ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED);
	SendMessage(screenID, WM_SETFONT, (WPARAM)((ZafScreenDisplay *)display)->fontTable[fontPalette.font], (LPARAM)FALSE);
	zafRegion = saveRegion;

	HWND childID = GetWindow(screenID, GW_CHILD);
	if (childID)
		SetWindowLong(childID, GWL_WNDPROC, (LONG)ComboStringProc);
}

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

ZafError ZafComboBox::OSSetText(void)
{
	if (screenID)
	{
		if (ZafMSWindowsApp::convertText)
		{
			char *osText = zafCodeSet->ConvertToOSString(stringData.Text(), ZAF_NULLP(char), FALSE);
#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
		}
		else
			SendMessage(screenID, WM_SETTEXT, (WPARAM)0, (LPARAM)stringData.Text());
	}

	return (ZAF_ERROR_NONE);
}

void ZafComboBox::OSSize(void)
{
	if (zafRegion != oldRegion)
	{
		// List must be positioned at 0,0 to fix up draw coordinates 
		// since it is not a system object.
		list->zafRegion.bottom = list->zafRegion.Height() - 1;
		if (listWidth == -1)
			list->zafRegion.right = zafRegion.Width() - 1;
		else
			list->zafRegion.right = listWidth - 1;

		list->zafRegion.left = list->zafRegion.top = 0;

		if (SystemObject())
		{
			// Convert zafRegion to OS coordinates.
			ZafRegionStruct osRegion = parent ? parent->ConvertToOSRegion(this) :
				windowManager->ConvertToOSRegion(this);

			if (maxListHeight == -1)
				osRegion.bottom = osRegion.top + Display()->cellHeight - 1 +
                    list->zafRegion.Height() - 1;
			else
				osRegion.bottom += maxListHeight - 1;

			// Move/Size the combo box.
			MoveWindow(screenID, osRegion.left, osRegion.top, osRegion.Width(),
				osRegion.Height(), TRUE);
#if defined(ZAF_WIN32)
			if (listWidth != -1)
				SendMessage(screenID, CB_SETDROPPEDWIDTH, listWidth, 0);
#endif
			UpdateWindow(screenID);

			// Notify the object that it has been sized.
			ZafEventStruct notifyEvent;
			notifyEvent.region = oldRegion;
			notifyEvent.type = N_SIZE;
			Event(notifyEvent);

			oldRegion = zafRegion;
		}
		else
			ZafWindow::OSSize();
	}
}

void ZafComboBox::OSSort(void)
{
	if (screenID)
	{
		SendMessage(screenID, CB_RESETCONTENT, (WPARAM)0, (LPARAM)0);

		//??? Duplicate code.
		//??? Optimize Refresh.
		int index = 0;
		for (ZafWindowObject *object = list->First(); object; object = object->Next())
		{
			if (ZafMSWindowsApp::convertText && OSDraw())
			{
				char *osText = zafCodeSet->ConvertToOSString(object->Text(), ZAF_NULLP(char), FALSE);
#if defined(ZAF_WIN32) && defined(ZAF_UNICODE)
				SendMessageA(screenID, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)osText);
#else
				SendMessage(screenID, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)osText);
#endif
			}
			else
				SendMessage(screenID, CB_INSERTSTRING, (WPARAM)-1, OSDraw() ? (LPARAM)object->Text() : (LPARAM)object);

			if (object->Selected())
				SendMessage(screenID, CB_SETCURSEL, (WPARAM)index, (LPARAM)0);
			index++;
		}
	}
}

ZafError ZafComboBox::OSUpdatePalettes(ZafPaletteType, ZafPaletteType)
{
	if (screenID)
	{
		ZafPaletteStruct fontPalette = LogicalPalette(ZAF_PM_TEXT, ZAF_PM_ACTIVE | ZAF_PM_ENABLED);
		SendMessage(screenID, WM_SETREDRAW, (WPARAM)FALSE, 0);
		SendMessage(screenID, WM_SETFONT, (WPARAM)((ZafScreenDisplay *)display)->fontTable[fontPalette.font], (LPARAM)FALSE);
		if (automaticUpdate && visible)
			SendMessage(screenID, WM_SETREDRAW, (WPARAM)TRUE, 0);
		Redisplay();
	}

	return (ZAF_ERROR_NONE);
}

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

bool ZafComboBox::SetAcceptDrop(bool acceptDrop)
{
	return (ZafWindowObject::SetAcceptDrop(acceptDrop));
}

