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

#include <z_keymap.hpp> // For the racode ENTER in ZafComboBox::Event
#include <z_string.hpp>
#include <z_mouse2.hpp>
#include <z_scrll2.hpp>
#include "m_combo.hpp"
#include "m_str.hpp"
#include "m_button.hpp"

const int SELECTION_WIDTH	= 18;
const int SELECTION_HEIGHT	= 9;
const int ARROW_SIZE = 11;

// ----- MtfComboBox widget definition --------------------------------------

XmOffsetPtr MtfComboBox::offsets;

XtActionsRec MtfComboBox::action[] =
{
	{ "ManagerGadgetArm",	(XtActionProc)arm },
	{ "ZafDragDrop",		(XtActionProc)drag_drop },
	{ "ZafEnter",			(XtActionProc)enter },
	{ "ZafHelp", 			(XtActionProc)help },
	{ "ZafLeave",			(XtActionProc)leave }
};

char MtfComboBox::translations[] =
	"<Btn2Down>:	ZafDragDrop()\n\
	 <Enter>:		ZafEnter()\n\
	 <Key>F1:		ZafHelp()\n\
	 <Key>osfHelp:	ZafHelp()\n\
	 <Leave>:		ZafLeave()";

MtfComboBoxClassRec MtfComboBox::classRecord =
{
	// --- CoreClassPart ---
	{
		(WidgetClass)&xmBulletinBoardClassRec,	// superclass
		"ZafComboBox",							// class_name
		sizeof(MtfComboBoxPart),					// widget_size (data part)
		MtfComboBox::classInitialize,		// class_initialize
		0,										// class_part_initialize
		false,									// class inited
		0,										// initialize
		0,										// initialize_hook
		XtInheritRealize,						// realize
		action, 								// actions
		XtNumber(action), 						// num_actions
		0, 										// resources
		0, 										// num_resources
		NULLQUARK,								// xrm
		true,									// compress_motion
		XtExposeCompressMultiple,				// compress_exposure
		true,									// compress_enterleave
		false,									// visible_interest
		0, 										// destroy
		(XtWidgetProc)resize,					// resize
		(XtExposeProc)expose,					// expose
		0,										// set_values
		0, 										// set_values_hook
		XtInheritSetValuesAlmost,				// set_values_almost
		0, 										// get_values_hook
		XtInheritAcceptFocus,					// accept_focus
		XtVersionDontCheck,					// version
		0, 										// callback_private
		XtInheritTranslations,					// tm_table
		XtInheritQueryGeometry, 				// query_geometry
		XtInheritDisplayAccelerator,			// display_accelerator
		0 										// extension
	},
	// --- CompositeClassPart ---
	{
		gmgr,									// geometry_manager
		XtInheritChangeManaged,					// change_managed
		XtInheritInsertChild,					// insert_child
		XtInheritDeleteChild,					// delete_child
		0										// extension
	},
	// --- ConstraintClassPart ---
	{
		0,										// resources
		0,										// num_resources
		0,										// constraint_size
		0,										// initialize
		0,										// destroy
		0,										// set_values
		0										// extension
	},
	// --- XmManagerClassPart ---
	{
		XtInheritTranslations,					// translations
		0,										// syn_resources
		0,										// num_syn_resources
		0,										// syn_constraint_resources
		0,										// num_syn_constraint_resources
		XmInheritParentProcess,					// parent_process
		0										// extension
	},
	// --- XmBulletinBoardClassPart ---
	{
		false,									// always_install_accelerators
		0,										// geo_matrix_create
		XmInheritFocusMovedProc,				// focus_moved_proc
		0										// extension
	},
	// --- MtfComboBoxClassPart ---
	{
		0									// extension
	}
};

void MtfComboBox::arm(MtfComboBox *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This action occurs on the first "down-click" portion of a mouse click.

	// Perform the motif arm.
	XmProcessTraversal((Widget)mtfObject, XmTRAVERSE_CURRENT);

	// Try to find a matching tab.
	ZafComboBox *zafObject = GetZincObject(mtfObject);
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);

	event.type = L_BEGIN_SELECT;
	zafObject->Event(event);
}

void MtfComboBox::classInitialize()
{
	XmResolvePartOffsets((WidgetClass) &(MtfComboBox::classRecord),
	 &(MtfComboBox::offsets));
}

void MtfComboBox::drag_drop(MtfComboBox *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// Begin the drag/drop operation.
	ZafEventStruct event(S_BEGIN_DRAG, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

void MtfComboBox::enter(MtfComboBox *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Perform the motif enter and match with zinc.
	XtCallActionProc((Widget)mtfObject, "ManagerEnter", xEvent, param, *nparams);
	ZafEventStruct event(N_MOUSE_ENTER, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

void MtfComboBox::expose(MtfComboBox *mtfObject, XEvent *xEvent, Region xRegion)
{
	// Draw the object.
	ZafEventStruct event(E_MOTIF, xEvent);
	XRectangle xRect;
	XClipBox(xRegion, &xRect);
  	event.region.Assign(xRect.x, xRect.y, xRect.width, xRect.height, ZAF_PIXEL);
	GetZincObject(mtfObject)->Draw(event, E_MOTIF);
}

XtGeometryResult MtfComboBox::gmgr(Widget , XtWidgetGeometry * , XtWidgetGeometry * )
{
	// ZAF automatically handles geometry of children.
    return (XtGeometryNo);
}

void MtfComboBox::leave(MtfComboBox *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Perform the motif enter and match with zinc.
	XtCallActionProc((Widget)mtfObject, "ManagerLeave", xEvent, param, *nparams);
	ZafEventStruct event(N_MOUSE_LEAVE, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

// ----- ZafComboString -----------------------------------------------------

class ZafComboString : public ZafString
{
public:
	ZafVtList *list;

	ZafComboString(ZafStringData *stringData);
	~ZafComboString();
	ZafEventType DragDropEvent(const ZafEventStruct &event);
	ZafEventType Draw(const ZafEventStruct &event, ZafEventType ccode);
	ZafEventType Event(const ZafEventStruct &event);
	virtual void OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state);
	virtual void OSRegisterObject(void);
	virtual ZafError SetText(const ZafIChar *text);
};

ZafComboString::ZafComboString(ZafStringData *tStringData) :
	ZafString(0, 0, 1, tStringData),
	list(ZAF_NULLP(ZafVtList))
{
	SetAutoClear(false);
	SetParentPalette(true);
}

ZafComboString::~ZafComboString()
{
	SetStringData(ZAF_NULLP(ZafStringData));
}

ZafEventType ZafComboString::DragDropEvent(const ZafEventStruct &event)
{
	// Defer to the parent class.
	return (parent->DragDropEvent(event));
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafComboString::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	// Check for edit mode.
	return (ZafString::Draw(event, ccode));
}
#endif

ZafEventType ZafComboString::Event(const ZafEventStruct &event)
{
	// Check for special logical events.
	ZafEventType ccode = event.type;
	if (ccode == E_MOTIF && event.InputType() == E_KEY && list)
		ccode = list->LogicalEvent(event);

	// Check for zinc events.
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		ccode = ZafString::Event(event);
		list = DynamicPtrCast(parent, ZafComboBox)->list;
		list->Event(event);
		break;

	// ----- Movement messages -----
	case L_UP:
	case L_DOWN:
	case L_NEXT:
	case L_PREVIOUS:
	case L_FIRST:
	case L_LAST:
	case L_PGDN:
	case L_PGUP:
	case L_CANCEL:
	case L_SELECT:
		// Handle movement according to the state of the vertical list.
		if (list->Focus())
			ccode = list->Event(ccode);
		else if (ccode == L_SELECT || ccode == L_NEXT || ccode == L_PREVIOUS)
			ccode = ZafString::Event(event);
		else
		{
			// Modify the list selection.
			ZafWindowObject *oldCurrent = list->Current();
			list->Event(ccode);

			// Call the associated user function directly, since the 
			// object is changing at the base combo-box level.
			ZafWindowObject *newCurrent = list->Current();
			if (oldCurrent != newCurrent)
			{
				ccode = (newCurrent->*(newCurrent->memberUserFunction))(event, L_SELECT);
				ccode = (parent->*(parent->memberUserFunction))(event, L_SELECT);
			}

			// Check current again, since it may have changed from the 
			// associated member function. Then update the combo-string
			// information.
			newCurrent = list->Current();
			if (oldCurrent != newCurrent)
				SetText(newCurrent->Text());
		}
		break;

	case L_BEGIN_SELECT:
		// Pop-up the combo-list.
		if (editMode)
			parent->Event(event);
		else
			ccode = ZafString::Event(event);
		break;

	// ----- Miscelleaneous -----
	case N_MOUSE_ENTER:
	case N_MOUSE_LEAVE:
		parent->ZafWindowObject::Event(event);
		break;

	// ----- Default events -----
	default:
		ccode = ZafString::Event(event);
		break;
	}

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

void ZafComboString::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
 // Check for a non-system object.
	if (!screenID)
		return;
	else 
		parent->OSMapPalette(palette, type, state);
	return;
}

void ZafComboString::OSRegisterObject(void)
{
	// Defer registration to the base class.
	ZafString::OSRegisterObject();
}

bool ZafComboBox::SetAcceptDrop(bool setAcceptDrop)
{
	// Make sure the attribute has changed.
	if (acceptDrop != setAcceptDrop)
	{
		// Activate/Deactivate the Motif drop capabilities.
		if (comboItem && comboItem->screenID)
			acceptDrop = comboItem->SetAcceptDrop(setAcceptDrop);
		else
			acceptDrop = setAcceptDrop;
	}

	// Reflect the drag-drop capabilities at the list level.
	list->SetAcceptDrop(acceptDrop);

	// Return the current attribute.
	return (acceptDrop);
}

ZafError ZafComboString::SetText(const ZafIChar *text)
{
	// Bypass the low-level to prevent recursion.
	if (!stringData->Text())
		stringData->SetText(text);
	else
		stringData->SetText(text ? text : ZafLanguageManager::blankString);
	return (OSSetText());
}

// ----- ZafComboOpaque -----------------------------------------------------

class ZafComboOpaque : public ZafButton
{
public:
	ZafVtList *list;
	bool listOpened;

	ZafComboOpaque(ZafStringData *stringData);
	~ZafComboOpaque();
	ZafEventType DragDropEvent(const ZafEventStruct &event);
	ZafEventType Draw(const ZafEventStruct &event, ZafEventType ccode);
	ZafEventType Event(const ZafEventStruct &event);
	virtual void OSRegisterObject(void);
};

ZafComboOpaque::ZafComboOpaque(ZafStringData *tStringData) :
	ZafButton(0, 0, 1, 1, tStringData, ZAF_NULLP(ZafBitmapData), ZAF_NATIVE_BUTTON),
	list(ZAF_NULLP(ZafVtList)), listOpened(false)
{
	SetBordered(false);
	SetButtonType(ZAF_FLAT_BUTTON);
	SetAutoSize(false);
	SetParentPalette(true);
}

ZafComboOpaque::~ZafComboOpaque()
{
	SetStringData(ZAF_NULLP(ZafStringData));
}

ZafEventType ZafComboOpaque::DragDropEvent(const ZafEventStruct &event)
{
	// Defer to the parent class.
	return (parent->DragDropEvent(event));
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafComboOpaque::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	if (!screenID)
		return (S_ERROR);

	// Draw the current object.  This is done by switching the object's 
	// screen identification during the drawing period.
	ZafRegionStruct drawRegion = BeginDraw();

	if (!list->Current())
	{
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, PaletteState()));
		display->Rectangle(drawRegion, 0, true);
	}
	else
	{
		// Get the object information.
		ZafWindowObject *object = list->Current();

		// Set the object members.
		ZafRegionStruct tRegion = object->zafRegion;
		OSWindowID tScreenID = object->screenID;
		ZafWindowObject *tParent = object->parent;
		bool tChanged = object->Changed();
		bool tSelected = object->Selected();
		bool tBordered = object->Bordered();
		bool tAutoUpdate = AutomaticUpdate();

		object->zafRegion = zafRegion;
		object->zafRegion.HzShift(-zafRegion.left);
		object->zafRegion.VtShift(-zafRegion.top);
		object->parent = this;
		object->screenID = screenID;
		object->ZafWindowObject::SetSelected(false);
		object->SetBordered(false);

		// Draw the item.
		object->Draw(event, ccode);

		// Restore the object members. 
		// Set automaticUpdate of combo to false to prevent infinate redisplay loop.
		automaticUpdate = false;
		object->zafRegion = tRegion;
		object->parent = tParent;
		object->screenID = tScreenID;
		object->ZafWindowObject::SetSelected(tSelected);
		object->SetBordered(tBordered);
		object->SetChanged(tChanged);
		// Restore automaticUpdate of combo.
		automaticUpdate = tAutoUpdate;
	}
	EndDraw();

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

ZafEventType ZafComboOpaque::Event(const ZafEventStruct &event)
{
	// Check for special logical events.
	ZafEventType ccode = event.type;
	if (ccode == E_MOTIF && event.InputType() == E_KEY)
		ccode = list->LogicalEvent(event);

	// Check for zinc events.
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		ccode = ZafButton::Event(event);
		list = DynamicPtrCast(parent, ZafComboBox)->list;
		break;

	case S_COMPUTE_SIZE:
		// Skip the button level.
		ccode = ZafWindowObject::Event(event);
		break;

	// ----- Movement messages -----
	case L_UP:
	case L_DOWN:
	case L_NEXT:
	case L_PREVIOUS:
	case L_FIRST:
	case L_LAST:
	case L_PGDN:
	case L_PGUP:
	case L_CANCEL:
	case L_SELECT:
		// Handle movement according to the state of the vertical list.
		if (list->Focus())
			ccode = list->Event(ccode);
		else if (ccode == L_SELECT)
		{
			if (!listOpened)
				parent->Event(ccode);
			listOpened = (listOpened == true) ? false : true;
		}
		else if (ccode == L_NEXT || ccode == L_PREVIOUS)
			ccode = ZafButton::Event(event);
		else
		{
			// Modify the list selection.
			ZafWindowObject *oldCurrent = list->Current();
			list->Event(ccode);

			// Call the associated user function directly, since the 
			// object is changing at the base combo-box level.
			ZafWindowObject *newCurrent = list->Current();
			if (oldCurrent != newCurrent)
			{
				ccode = (newCurrent->*(newCurrent->memberUserFunction))(event, L_SELECT);
				ccode = (parent->*(parent->memberUserFunction))(event, L_SELECT);
			}

			// Check current again, since it may have changed from the 
			// associated member function. Then update the combo-string
			// information.
			newCurrent = list->Current();
			if (oldCurrent != newCurrent)
				Redisplay();
		}
		break;

	case L_BEGIN_SELECT:
		// Pop-up the combo-list.
		if (editMode)
			parent->Event(event);
		else
			parent->Event(L_SELECT);
		break;

	// ----- Miscelleaneous -----
	case N_MOUSE_ENTER:
	case N_MOUSE_LEAVE:
		parent->ZafWindowObject::Event(event);
		break;

	case N_NON_CURRENT:
		listOpened = false;
		// Continue to default.
	// ----- Default events -----
	default:
		ccode = ZafButton::Event(event);
		break;
	}

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

void ZafComboOpaque::OSRegisterObject(void)
{
	// Defer registration to the base class.
	ZafButton::OSRegisterObject();

	MtfButton::GetMotifWidget(screenID)->primitive.highlight_thickness = 0;
}

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

ZafWindowObject *ZafComboBox::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Check for window items, else defer to the list object.
	if (object == comboItem)
		return (ZafWindow::Add(object, position));
	return (list->Add(object, position));
}

ZafRegionStruct ZafComboBox::ConvertToDrawRegion(const ZafWindowObject *object,
	const ZafRegionStruct *tZafRegion) const
{
	// Defer to the base class.
	return (ZafWindowObject::ConvertToDrawRegion(object, tZafRegion));
}

ZafEventType ZafComboBox::DragDropEvent(const ZafEventStruct &event)
{
	// Determine the drop message.
	ZafEventType ccode = event.type;
	ZafWindowObject *object = windowManager->dragObject;
	switch (ccode)
	{
	// --- Drop operations ---
	case S_DROP_COPY:
	case S_DROP_MOVE:
		// Make sure the drop operation is valid.
		if (Disabled() || !AcceptDrop() || !object || object == this)
			ccode = S_ERROR;
		else if (object->IsA(ID_ZAF_COMBO_BOX))
		{
			// Get the drag object.
			ZafComboBox *combo = DynamicPtrCast(object, ZafComboBox);
			object = combo->list->Current();

			// Perform the specified drop operation.
			if (object)
			{
				if (ccode == S_DROP_COPY)
					object = object->Duplicate();
				else
					combo->list->Subtract(object);
				object->SetSelected(false);
				list->Add(object);
			}
			else
				ccode = S_ERROR;
		}
		else if (object->IsA(ID_ZAF_LIST))
			ccode = list->DragDropEvent(event);
		else
			ccode = S_ERROR;
		break;

	// ----- Base class messages -----
	default:
		ccode = ZafWindowObject::DragDropEvent(event);
		break;
	}

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

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafComboBox::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	// Compute the actual draw region.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();

	// Draw the combo-box and the selection area.
	if (ViewOnly())
	{
		DrawFocus(drawRegion, ccode);
		DrawShadow(drawRegion, ZAF_SHADOW_SIZE, ccode);
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
		display->Rectangle(drawRegion, 0, true);
	}
	else
	{
		drawRegion.top += ZAF_SHADOW_SIZE;
		drawRegion.bottom -= ZAF_SHADOW_SIZE;
		drawRegion.left = selectRegion.left - ZAF_SHADOW_SIZE;
		drawRegion.right = selectRegion.right + ZAF_SHADOW_SIZE;

		DrawShadow(drawRegion, -ZAF_SHADOW_SIZE, ccode);
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
		display->Rectangle(drawRegion, 0, true);
	}
	DrawArrow(false);

	// Draw the non-system children.
	for (ZafWindowObject *object = ComboFirst(); object; object = object->Next())
		if (!object->SystemObject())
			object->Draw(event, ccode);

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

void ZafComboBox::DrawArrow(bool depressed)
{
	// Draw the selection arrow.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();

	ZafRegionStruct arrowRegion = selectRegion;
	arrowRegion.HzShift(drawRegion.left);
	arrowRegion.VtShift(drawRegion.top);
	if (ViewOnly())
		DrawShadow(arrowRegion, depressed ? -ZAF_SHADOW_SIZE : ZAF_SHADOW_SIZE, state);
	else
	{
		ZafPaletteStruct topShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
		ZafPaletteStruct bottomShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
		ZafPaletteStruct background = LogicalPalette(ZAF_PM_BACKGROUND, state);
		_XmDrawArrow(XtDisplay(screenID), XtWindow(screenID),
			depressed ? bottomShadow.osPalette : topShadow.osPalette,
			depressed ? topShadow.osPalette : bottomShadow.osPalette,
			background.osPalette, arrowRegion.left - 1, arrowRegion.top - 1,
			selectRegion.Width() + 2, selectRegion.Height() + 2,
			ZAF_SHADOW_SIZE, XmARROW_DOWN);
	}

	EndDraw();
}

ZafEventType ZafComboBox::DrawBorder(ZafRegionStruct &region, ZafEventType ccode)
{
	// Defer to the base class.
	return (ZafWindowObject::DrawBorder(region, ccode));
}

ZafEventType ZafComboBox::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	// Check for a valid system object.
	if (!SystemObject())
		return (parent->DrawFocus(region, ccode));

	// Only draw focus for opaque combo-boxes.
	int width = ZAF_BORDER_SIZE;
	if ((EditMode() || ViewOnly()) && 
	 !(region.Height() < zafRegion.Height() && region.Width() < zafRegion.Width()))
	{
		display->SetPalette(LogicalPalette(focus ? ZAF_PM_FOCUS : ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE));
		display->Rectangle(region, width);
		region -= width;
	}
	return (ccode);
}
#endif

ZafEventType ZafComboBox::Event(const ZafEventStruct &event)
{
	// Check for special logical events.
	ZafEventType ccode = event.type;
	if (ccode == E_MOTIF && event.InputType() == E_KEY)
	{
		if (list->Focus())
		{	
			ccode = list->LogicalEvent(event);
			return ((ccode != E_MOTIF) ? list->Event(ccode) : S_ERROR);
		}
		ConvertToZafEvent((ZafEventStruct &)event);
		if (event.rawCode == ENTER && event.modifiers & S_KEYUP)
			(this->*memberUserFunction)(event, L_SELECT);
	}

	// Check for zinc events.
	ZafWindowObject *object;
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		// Remove any previous combo-items.
		if (comboItem)
		{
			ZafWindow::Subtract(comboItem);
			delete comboItem;
		}

		stringData.SetDestroyable(false);

		// Determine the proper type of child.
		if (ViewOnly())
			comboItem = new ZafComboOpaque(&stringData);
		else
			comboItem = new ZafComboString(&stringData);

		// Add the object to the combo-box.
		ZafWindow::Add(comboItem);

		// Reset the system-object settings.
		ccode = ZafWindow::Event(event);
		list->Event(event);
		break;

	case S_DEINITIALIZE:
		// Restore the system-object settings.
		ccode = ZafWindow::Event(event);
		list->Event(event);
		break;

	case S_REGISTER_OBJECT:
	{
		// Reset the system-object settings.
		ccode = ZafWindowObject::Event(event);
		
		// Get the font used by the combo box and save it for the combo list.
		XmFontList font = 0;
	 	static XtResource resource = { "fontList", "FontList", "FontList", sizeof(XmFontList), 0, "Immediate", (XtPointer)0 };
		XtGetApplicationResources(screenID, &font, &resource, 1, 0, 0);

		// Get any other resources that may have been set through an Xdefaults file.
		Pixel background;
		Pixel foreground;
		Pixel lightShadow;
		Pixel darkShadow;
		XtVaGetValues(screenID, XmNbackground, &background, 
		 XmNforeground, &foreground, XmNtopShadowColor, &lightShadow,
		 XmNbottomShadowColor, &darkShadow, 0);
		
		for (object = ComboFirst(); object; object = object->Next())
		{
			object->screenID = 0;
			object->SetSystemObject((EditMode() || !SystemObject()) ? false : true);
			object->Event(event);
			// Set the resources on the object.
			XtVaSetValues(object->screenID, XmNbackground, background,
			 XmNforeground, foreground, XmNtopShadowColor, lightShadow,
			 XmNbottomShadowColor, darkShadow, XmNfontList, font, 0);
		}
	}
		break;

	case S_DESTROY:
		// Make sure the popup list is closed.
		Event(S_CLOSE_TEMPORARY);
	 	ccode = ZafWindowObject::Event(event);
		break;

	case N_SIZE:
		// Compute the list, comto-item and select region.
		{
		bool editable = ViewOnly() ? false : true;
		// go to combo-item.
		comboItem->zafRegion.left = comboItem->zafRegion.top = 0;
		comboItem->zafRegion.right = zafRegion.right - zafRegion.left;
		comboItem->zafRegion.bottom = zafRegion.bottom - zafRegion.top - ZAF_BORDER_SIZE;
		if (!editable)
			comboItem->zafRegion -= ZAF_BORDER_SIZE + ZAF_SHADOW_SIZE;
		// go to selectRegion.
		selectRegion = comboItem->zafRegion;
		if (!editable)
		{
			selectRegion.coordinateType = ZAF_PIXEL;
			selectRegion.right -= ZAF_MARGIN_SIZE;
			selectRegion.left = selectRegion.right - SELECTION_WIDTH + 1;
			selectRegion.top += (selectRegion.Height() - SELECTION_HEIGHT) / 2;
			selectRegion.bottom = selectRegion.top + SELECTION_HEIGHT - 1;
		}
		else
		{
			selectRegion.coordinateType = ZAF_PIXEL;
			selectRegion.right -= ZAF_MARGIN_SIZE + 1;
			selectRegion.left = selectRegion.right - ARROW_SIZE + 1;
			selectRegion.top += (selectRegion.Height() - ARROW_SIZE) / 2;
			selectRegion.bottom = selectRegion.top + ARROW_SIZE - 1;
		}
		// now back to combo-item.
		if (!editable)
			comboItem->zafRegion.left += ZAF_BORDER_SIZE;
		comboItem->zafRegion.right = selectRegion.left - ZAF_BORDER_SIZE;
		comboItem->Event(N_SIZE);
		comboItem->OSSize();
		// now modify vertical list.
		list->zafRegion.coordinateType = ZAF_PIXEL;
		list->zafRegion.left = ZAF_BORDER_SIZE;
		list->zafRegion.right = selectRegion.left - 2 * ZAF_BORDER_SIZE;
		}
		break;

	// ----- Activation messages -----
	case L_BEGIN_SELECT:
		// Make sure the list should be popped-up.
		if (!zafRegion.Overlap(event.position, zafRegion.left, zafRegion.top) ||
			(comboItem->IsA(ID_ZAF_STRING) && !selectRegion.Overlap(event.position)))
			break;
		else if (editMode)
			return (ZafWindowObject::Event(event));
		// Continue to L_SELECT.
	case L_SELECT:
		// Move the list into position.
		{
		list->Event(S_CREATE);
		list->itemWidth = list->itemHeight = 0;
		list->oldRegion.Assign(-1, -1, -1, -1);

		int width = list->zafRegion.right - list->zafRegion.left;
		int height = list->zafRegion.bottom - list->zafRegion.top;
		list->zafRegion.left = list->zafRegion.top = ZAF_BORDER_SIZE;
		for (Widget widget = screenID; widget; widget = XtParent(widget))
		{
			list->zafRegion.left += widget->core.x;
			list->zafRegion.top += widget->core.y;
		}
		// If combobox is view only position current item of list over combobox.
		if (ViewOnly() && list->Current())
			list->zafRegion.top -= list->Current()->zafRegion.top;
		// If combobox is not view only position list below combobox.
		if (!ViewOnly())
			list->zafRegion.top += zafRegion.Height();
		list->zafRegion.right = list->zafRegion.left + width;
		list->zafRegion.bottom = list->zafRegion.top + height;

		// Make sure the list is on the screen.
		if (list->zafRegion.left < 0)
			list->zafRegion.HzShift(-list->zafRegion.left);
		else if (list->zafRegion.right >= display->columns)
			list->zafRegion.HzShift(display->columns - list->zafRegion.right - 1);

		if (!(list->zafRegion.top < 0 && 
		 list->zafRegion.bottom >= display->lines))
		{
			if (list->zafRegion.top < 0)
				list->zafRegion.VtShift(-list->zafRegion.top);
			else if (list->zafRegion.bottom >= display->lines)
				list->zafRegion.VtShift(display->lines - list->zafRegion.bottom - 1);
		}

		// Update the selection area.
		DrawArrow(true);

		// Manage the list.
		Widget shellID = list->OSScreenID(ZAF_SHELLID);
		Widget frameID = list->OSScreenID(ZAF_FRAMEID);
		XtConfigureWidget(shellID, list->zafRegion.left,
			list->zafRegion.top, list->zafRegion.Width(),
			list->zafRegion.Height(), 0);
		XtManageChild(frameID);
		XtPopupSpringLoaded(shellID);
		XtGrabPointer(list->screenID, false, (unsigned)(ButtonPressMask |
			ButtonReleaseMask | PointerMotionMask | EnterWindowMask |
			LeaveWindowMask), GrabModeAsync, GrabModeAsync, None, None,
			CurrentTime);

		// Modify the focus.  This must be done manually, since the object
		// never receives real focus from Motif.
		if (Noncurrent())
		{
			focus = true; // fake list out by setting my focus true.
			list->NotifyFocus(list->Current(), true);
		}
		else
			list->NotifyFocus(list->Current(), true);

		Event(N_OPEN_COMBO_LIST);
		// Modify the mouse cursor.
		ZafEventStruct cursorEvent(DM_SELECT);
		cursorEvent.screenID = list->screenID;
		eventManager->Event(cursorEvent, E_MOUSE);
		}
		break;

	case S_CLOSE_TEMPORARY:
		// Pop-down the list.
		{
		if (!list->screenID)
			break;
		Widget shellID = list->OSScreenID(ZAF_SHELLID);
		XtUngrabPointer(list->screenID, CurrentTime);
		XtPopdown(shellID);
		XtUnrealizeWidget(shellID);
		list->Event(S_DESTROY);
		XtDestroyWidget(shellID);

		// Modify the focus.  This must be done manually, since the object
		// never receives real focus from Motif.
		if (Noncurrent())
		{
			list->NotifyFocus(list->Current(), false);
			focus = false; // restore the faked out combo-box.
		}
		else
			comboItem->SetFocus(true);

		// Reset the combo-item information.
		if (list->Current() && comboItem->IsA(ID_ZAF_STRING))
			comboItem->SetText(list->Current()->Text());
		else
			comboItem->Redisplay();
		comboItem->Event(ccode);

		// Update the selection area.
		DrawArrow(false);
		}
		break;

	case S_CURRENT:
	case S_NON_CURRENT:
		// Update the combo-box highlight.
		if (screenID && SystemObject())
		{
			ccode = ZafWindow::Event(event);
			ZafRegionStruct drawRegion = BeginDraw();
			DrawFocus(drawRegion, ccode);
			EndDraw();
		}
		break;

	case S_REDISPLAY:
	case S_REDISPLAY_DATA:
	case S_REDISPLAY_REGION:
		// First, defer to the base class.
		ccode = ZafWindowObject::Event(event);

		// Next, broadcast to all of the children.
		if (ccode == S_REDISPLAY_REGION)
		{
			for (object = ComboFirst(); object; object = object->Next())
				if (object->SystemObject() && object->zafRegion.Overlap(event.region))
					object->Event(S_REDISPLAY);
		}
		else
		{
			for (object = SupportFirst(); object; object = object->Next())
				if (object->SystemObject())
					object->Event(event);
			for (object = ComboFirst(); object; object = object->Next())
				if (object->SystemObject())
					object->Event(event);
		}
		break;

	// ----- Unknown or error messages -----
	default:
		ccode = ZafWindow::Event(event);
		break;
	}

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

void ZafComboBox::GetClip(ZafWindowObject *object, ZafRegionStruct &clip, ZafRegionStruct &virtualRegion)
{
	// Defer to the base class.
	ZafWindowObject::GetClip(object, clip, virtualRegion);
}

ZafWindowObject *ZafComboBox::MatchInput(ZafIChar )
{
	return (Current());
}

ZafWindowObject *ZafComboBox::NotifyFocus(ZafWindowObject *focusObject, bool setFocus)
{
	// Check for self notification.
	if (focusObject == this)
		return (ZafWindowObject::NotifyFocus(focusObject, setFocus));

	// Check for algorithm direction.
	ZafWindowObject *invalidObject = ZAF_NULLP(ZafWindowObject);
	if (!setFocus) // down
	{
		// Remove focus from the current branch.
		if (ComboCurrent())
			invalidObject = ComboCurrent()->NotifyFocus(focusObject, false);

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

	 	// This window is now the root of the focus path.  Transition focus
		// if focusObject doesn't already have it.
		if (!invalidObject && ComboCurrent() && ComboCurrent() != focusObject)
			invalidObject = ComboCurrent()->NotifyFocus(this, false);

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

		// Set focus if validation succeeded.
		if (!invalidObject)
			ZafList::SetCurrent(focusObject);
	}

	// Return the object focus.
	return (invalidObject);
}

ZafWindowObject *ZafComboBox::NotifySelection(ZafWindowObject *selectedObject, bool setSelected)
{
	// Defer to the base class.
	if (setSelected)
		stringData.SetText(selectedObject->Text());
	return (selectedObject);
}

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

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

bool ZafComboBox::SetFocus(bool setFocus)
{
	// Push the focus down if there is a current object.
	ZafWindowObject *object = ComboFirst();
	if (object && setFocus)
 		object->SetFocus(setFocus);
	else if (object)
	{
 		object->SetFocus(setFocus);
		ZafWindowObject::SetFocus(setFocus);
	}
	else
		ZafWindowObject::SetFocus(setFocus);

	// Return the current attribute.
	return (focus);
}

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

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

ZafWindowObject *ZafComboBox::Subtract(ZafWindowObject *object)
{
	// Check for window items, else defer to the list object.
	if (object == comboItem)
	{
		comboItem = ZAF_NULLP(ZafWindowObject);
		return (ZafWindow::Subtract(object));
	}
	return (list->Subtract(object));
}

const ZafIChar *ZafComboBox::Text(void)
{
	// Return the current object contents.
	ZafWindowObject *object = ComboFirst();
	if (ViewOnly() || !OSDraw())
		object = list->Current();
	return (object ? object->Text() : ZAF_NULLP(ZafIChar));
}

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

void ZafComboBox::OSDestroy(void)
{
}

ZafEventType ZafComboBox::OSEvent(const ZafEventStruct &)
{
	return (S_ERROR);
}

ZafError ZafComboBox::OSGetText(void)
{
	return (ZAF_ERROR_NONE);
}

void ZafComboBox::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
		// Defer to the base class.
		ZafWindow::OSMapPalette(palette, type, state);
}

OSWindowID ZafComboBox::OSScreenID(ZafScreenIDType type) const
{
	// Defer to the base class.
	return (ZafWindowObject::OSScreenID(type));
}

void ZafComboBox::OSSize(void)
{
	// Defer to the base class.
	ZafWindowObject::OSSize();
}

void ZafComboBox::OSSort(void)
{
	// Push sorting to the list level.
	list->OSSort();
}

void ZafComboBox::OSRegisterObject(void)
{
	// Create the motif widget.
	char *name = zafCodeSet->ConvertToOSString(stringID, 0, false);

	screenID = parent->OSScreenID(ZAF_CLIENTID);
	screenID = XtVaCreateWidget(name,
		(WidgetClass)&MtfComboBox::classRecord, screenID,
		// user information
		XmNuserData, this,
		// miscellaneous
		XmNsensitive, Disabled(false) ? false : true,
		XmNtraversalOn, Noncurrent() ? false : true,
		XmNmarginWidth, 0,
		XmNmarginHeight, 0,
		0);

	// Override translations.
	XtTranslations translationTable = XtParseTranslationTable(MtfComboBox::translations);
	XtOverrideTranslations(screenID, translationTable);

	// Add edit mode overrides.
	ZafEditModeRegister(this);

	// Reflect attributes to the comboItem.
	comboItem->SetAcceptDrop(acceptDrop);
	comboItem->SetCopyDraggable(copyDraggable);
	comboItem->SetDisabled(disabled);
	comboItem->SetHelpContext(helpContext);
	comboItem->SetHelpObjectTip(helpObjectTip);
	comboItem->SetLinkDraggable(linkDraggable);
	comboItem->SetMoveDraggable(moveDraggable);
	comboItem->SetNoncurrent(noncurrent);
	comboItem->SetQuickTip(quickTip);
}

ZafError ZafComboBox::OSSetText(void)
{
	return (ZAF_ERROR_NONE);
}

ZafError ZafComboBox::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	// Make sure the widget can be updated.
	if (!screenID || !AutomaticUpdate() || !SystemObject())
		return (ZAF_ERROR_INVALID);

	// This class bypasses setting its own values so the background won't
	// clear to some funny color.  Also, support children are automatically
	// redrawn with the parent color, so only normal children need the
	// color updates.
	ZafWindowObject *object;
	int width = screenID->core.width;
	int height = screenID->core.height;
	for (object = ComboFirst(); object; object = object->Next())
		object->OSUpdatePalettes(zafTypes, osTypes);
	list->OSUpdatePalettes(zafTypes, osTypes);
	if (width != screenID->core.width || height != screenID->core.height)
		XtConfigureWidget(screenID, screenID->core.x, screenID->core.y, width, height, 0);

	// Redisplay the combo-box and return success.
	Redisplay();
	return (ZAF_ERROR_NONE);
}

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

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

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

