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

#include <z_scrll2.hpp>
#include <z_vlist.hpp>
#include <z_str1.hpp>
#include <z_string.hpp>
#include "m_win.hpp"
#include "m_list.hpp"
#include "m_combo.hpp"

const int ARROW_SIZE = 11;

// These are defiined in m_str.cpp.
extern int LINUX_TILE_WIDTH;					 
extern int LINUX_TILE_HEIGHT;
extern char linuxTile[];

// ----- MtfList widget definition ------------------------------------------

XmOffsetPtr MtfList::offsets;

XtActionsRec MtfList::action[] =
{
	{ "ListAddMode",				(XtActionProc)noop },
	{ "ListAddPrevItem", 			(XtActionProc)noop },
	{ "ListAddNextItem", 			(XtActionProc)noop },
	{ "ListBeginData",				(XtActionProc)first_item },
	{ "ListBeginDataExtend",		(XtActionProc)first_extend },
	{ "ListBeginExtend",  			(XtActionProc)button_select },
	{ "ListBeginLine",  			(XtActionProc)noop },
	{ "ListBeginSelect",			(XtActionProc)button_select },
	{ "ListBeginToggle",  			(XtActionProc)button_select },
	{ "ListButtonMotion",			(XtActionProc)button_select },
	{ "ListCopyToClipboard",		(XtActionProc)noop },
	{ "ListCtrlNextItem",  			(XtActionProc)noop },
	{ "ListCtrlPrevItem",  			(XtActionProc)noop },
	{ "ListCtrlSelect",  			(XtActionProc)noop },
	{ "ListCtrlUnSelect",			(XtActionProc)noop },
	{ "ListEndData",				(XtActionProc)last_item },
	{ "ListEndDataExtend",			(XtActionProc)last_extend },
	{ "ListEndExtend",				(XtActionProc)button_select },
	{ "ListEndLine",				(XtActionProc)noop },
	{ "ListEndSelect",				(XtActionProc)button_select },
	{ "ListEndToggle",				(XtActionProc)button_select },
	{ "ListExtendAddNextItem",  	(XtActionProc)noop },
	{ "ListExtendAddSelect",		(XtActionProc)noop },
	{ "ListExtendAddUnSelect",		(XtActionProc)noop },
	{ "ListExtendAddPrevItem",  	(XtActionProc)noop },
	{ "ListExtendNextItem", 		(XtActionProc)next_extend },
	{ "ListExtendPrevItem", 		(XtActionProc)previous_extend },
	{ "ListFocusIn",				(XtActionProc)focus_in },
	{ "ListFocusOut",				(XtActionProc)focus_out },
	{ "ListItemSelect",				(XtActionProc)noop },
	{ "ListItemUnSelect",			(XtActionProc)noop },
	{ "ListKbdBeginExtend",	  		(XtActionProc)first_extend },
	{ "ListKbdBeginSelect",			(XtActionProc)begin_select },
	{ "ListKbdBeginToggle",	  		(XtActionProc)noop },
	{ "ListKbdCancel",				(XtActionProc)noop },
	{ "ListKbdCtrlSelect",			(XtActionProc)noop },
	{ "ListKbdCtrlUnSelect",  		(XtActionProc)noop },
	{ "ListKbdDeSelectAll",			(XtActionProc)deselect_all },
	{ "ListKbdEndExtend",  			(XtActionProc)last_extend },
	{ "ListKbdEndSelect",			(XtActionProc)noop },
	{ "ListKbdEndToggle",  			(XtActionProc)noop },
	{ "ListKbdSelectAll",			(XtActionProc)select_all },
	{ "ListKbdShiftSelect",			(XtActionProc)noop },
	{ "ListKbdShiftUnSelect",		(XtActionProc)noop },
	{ "ListLeftChar",				(XtActionProc)left_item },
	{ "ListLeftPage",				(XtActionProc)noop },
	{ "ListNextItem",	  			(XtActionProc)next_item },
	{ "ListNextPage",				(XtActionProc)next_page },
	{ "ListPrevItem",	  			(XtActionProc)previous_item },
	{ "ListPrevPage",				(XtActionProc)previous_page },
	{ "ListProcessDrag",			(XtActionProc)noop },
	{ "ListRightChar",  			(XtActionProc)right_item },
	{ "ListRightPage",  			(XtActionProc)noop },
	{ "ListScrollCursorVisible",	(XtActionProc)noop },
	{ "ListShiftCtrlSelect",		(XtActionProc)noop },
	{ "ListShiftCtrlUnSelect",		(XtActionProc)noop },
	{ "ListShiftNextItem",  		(XtActionProc)noop },
	{ "ListShiftPrevItem",  		(XtActionProc)noop },
	{ "ListShiftSelect",			(XtActionProc)noop },
	{ "ListShiftUnSelect",			(XtActionProc)noop },
	{ "List_ShiftCtrlPrevItem",		(XtActionProc)noop },
	{ "List_ShiftCtrlNextItem",		(XtActionProc)noop },
	{ "ZafMouse",					(XtActionProc)MtfWindowObject::mouse },
	{ "ZafCallDefault", 			(XtActionProc)call_default },
	{ "ZafDoubleClick",				(XtActionProc)double_click },
	{ "ZafDragDrop",				(XtActionProc)drag_drop },
	{ "ZafEnter",					(XtActionProc)enter },
	{ "ZafHelp", 					(XtActionProc)help },
	{ "ZafLeave",					(XtActionProc)leave },
	{ "ZafListMotion",		(XtActionProc)list_motion }
};

char MtfList::translations[] =
	"<Key>osfActivate:		ZafCallDefault()\n\
	 <Key>osfSelect:		ZafCallDefault()\n\
	 ~s ~m ~a <Key>Return:	ZafCallDefault()\n\
	 <Btn2Down>:			ZafDragDrop()\n\
	 <Btn3Down>:			ZafMouse()\n\
	 <Btn3Up>:				ZafMouse()\n\
	 <Btn3Motion>:			ZafMouse()\n\
	 <Motion>:				ZafListMotion()\n\
	 <Enter>:				ZafEnter()\n\
	 <Key>F1:				ZafHelp()\n\
	 <Key>osfHelp:			ZafHelp()\n\
	 <Leave>:				ZafLeave()";

MtfListClassRec MtfList::classRecord =
{
	// --- CoreClassPart ---
	{
		(WidgetClass)&xmListClassRec,	// superclass
		"ZafList", 						// class_name
		sizeof(MtfListPart), 		// widget_size (data part)
		MtfList::classInitialize,		// class_initialize
		0,								// class_part_initialize
		false,							// class inited
		(XtInitProc)initialize,			// initialize
		0,								// initialize_hook
		XtInheritRealize,				// realize
		action,							// actions
		XtNumber(action),				// num_actions
		0, 								// resources
		0, 								// num_resources
		NULLQUARK,						// xrm_class
		true,							// compress_motion
		XtExposeCompressMultiple,		// compress_exposure
		true,							// compress_enterleave
		false,							// visible_interest
		(XtWidgetProc)destroy_pixmap, 	// 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
	},
	// --- XmPrimitiveClassPart ---
	{
		0,								// border_highlight
		0,								// border_unhighlight
		0,								// translations
		0,								// arm_and_activate
		0,								// syn_resources
		0,								// num_syn_resources
		0								// extension
	},
	// --- MtfListClassPart ---
	{
		0									// extension
	}
};

void MtfList::button_select(MtfList *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// Set up the zinc event.
	ZafWindow *zafObject = GetZincObject(mtfObject);
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);

	// Choose the proper message.
	if (xEvent->xbutton.type == ButtonPress && mtfObject->list.DownTime &&
		(xEvent->xbutton.time < mtfObject->list.DownTime + (Time)mtfObject->list.ClickInterval))
	{
		event.type = L_DOUBLE_CLICK;
	    mtfObject->list.DownCount++;
       	mtfObject->list.DownTime = 0;
	}
	else if (xEvent->xbutton.type == ButtonPress)
	{
		event.type = L_BEGIN_SELECT;
	    mtfObject->list.DownCount = 1;
       	mtfObject->list.DownTime = xEvent->xbutton.time;
	}
	else if (xEvent->xbutton.type == ButtonRelease)
		event.type = L_END_SELECT;
	else
		event.type = L_CONTINUE_SELECT;
	zafObject->Event(event);
}

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

void MtfList::deselect_all(MtfList *mtfObject, XEvent *, String *, Cardinal *)
{
	// Check for a valid option.
	ZafWindow *zafObject = GetZincObject(mtfObject);
	if (zafObject->SelectionType() == ZAF_SINGLE_SELECTION)
		return;

	// De-select all the list fields.
	for (ZafWindowObject *object = zafObject->First(); object; object = object->Next())
		object->SetSelected(false);
}

void MtfList::destroy_pixmap(MtfList *mtfObject)
{
	// Destroy the pixmap.
	if (mtfObject && ListPixmap((Widget)mtfObject))
	{
		XFreePixmap(XtDisplay(mtfObject), ListPixmap((Widget)mtfObject));
		ListPixmap((Widget)mtfObject) = 0;
	}
}

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

void MtfList::enter(MtfList *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Chain to the base class.
	XtCallActionProc((Widget)mtfObject, "PrimitiveEnter", xEvent, param, *nparams);
	ZafEventStruct event(N_MOUSE_ENTER, xEvent);
	MouseObject(mtfObject) = ZAF_NULLP(ZafWindowObject);
	GetZincObject(mtfObject)->Event(event);
}

void MtfList::expose(MtfList *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);
}

void MtfList::focus_in(MtfList *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Chain to the base class.
	XtCallActionProc((Widget)mtfObject, "PrimitiveFocusIn", xEvent, param, *nparams);
	if (!mtfObject->primitive.have_traversal)
		return; // Object is not really getting the keyboard focus.

	// Reflect changes to Zinc.
	ZafWindow *zafObject = GetZincObject(mtfObject);
	zafObject->NotifyFocus(zafObject->Current(), true);
}

void MtfList::focus_out(MtfList *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Chain to the base class.
	XtCallActionProc((Widget)mtfObject, "PrimitiveFocusOut", xEvent, param, *nparams);

	// Update the field.
	ZafWindow *zafObject = GetZincObject(mtfObject);
	if (!zafObject->Current())
	{
		ZafRegionStruct drawRegion = zafObject->BeginDraw();
		drawRegion = zafObject->ClientRegion();
		drawRegion.HzShift(-drawRegion.left);
		drawRegion.VtShift(-drawRegion.top);
		zafObject->DrawFocus(drawRegion, S_NON_CURRENT);
		zafObject->EndDraw();
	}
}

void MtfList::initialize(MtfList *, MtfList *mtfObject, ArgList, Cardinal *)
{
	// Set the pixmap.
	ListPixmap((Widget)mtfObject) = 0;
}

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

void MtfList::list_motion(MtfList *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This event is received at the callback level.  Thus, the mouse
	// message has already passed through the window manager and must 
	// be interpreted as a logical event to prevent recursion.
	ZafWindow *zafObject = DynamicPtrCast(GetZincObject(mtfObject), ZafWindow);
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);

	// Choose the proper message.
	if (xEvent->type == MotionNotify)
	{
		ZafWindowObject *mObject = MouseObject(mtfObject);
		if (mObject && mObject->zafRegion.Overlap(event.position))
			return;
		else
		{
			if (mObject)
			{
				mObject->Event(N_MOUSE_LEAVE);
				MouseObject(mtfObject) = ZAF_NULLP(ZafWindowObject);
			}
			for (ZafWindowObject *object = zafObject->First(); object; object = object->Next())
				if (object->zafRegion.Overlap(event.position))
				{
					object->Event(N_MOUSE_ENTER);
					MouseObject(mtfObject) = object;
					break;
				}
		}
	}
}

void MtfList::repeat(MtfList *mtfObject, XtIntervalId *)
{
	ZafWindow *zafObject = GetZincObject(mtfObject);
	ListIntervalId((Widget)mtfObject) = 0;
	if (zafObject->Focus())
	{
		ZafEventStruct event(L_CONTINUE_SELECT, 0, LastMousePos((Widget)mtfObject));
		zafObject->Event(event);
	}
}

void MtfList::select_all(MtfList *mtfObject, XEvent *, String *, Cardinal *)
{
	// Check for a valid option.
	ZafWindow *zafObject = GetZincObject(mtfObject);
	if (zafObject->SelectionType() == ZAF_SINGLE_SELECTION)
		return;

	// Select all the list fields.
	for (ZafWindowObject *object = zafObject->First(); object; object = object->Next())
		object->SetSelected(true);
}

// ----- ZafVtList ----------------------------------------------------------

static ZafWindowObject *AdjustNext(ZafWindowObject *object)
{
	// Check the focus to the next selectable item.
	for (ZafWindowObject *newFocus = object; newFocus; newFocus = newFocus->Next())
		if (!newFocus->Disabled(false) && !newFocus->Noncurrent())
			return (newFocus);
	return (object);
}

static ZafWindowObject *AdjustPrevious(ZafWindowObject *object)
{
	// Check the focus to the previous selectable item.
	for (ZafWindowObject *newFocus = object; newFocus; newFocus = newFocus->Previous())
		if (!newFocus->Disabled(false) && !newFocus->Noncurrent())
			return (newFocus);
	return (object);
}

ZafWindowObject *ZafVtList::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Check for a support or invalid object.
	if (object->parent)
		return (object);
	else if (object->SupportObject())
		return (ZafWindow::Add(object, position));

	// Add the object to the list.
	if (OSDraw() || Temporary())
		object->SetBordered(false);	// turn off the border.
	object->SetSystemObject(false);
	object->SetParentPalette(true);
	ZafWindow::Add(object, position);

	// Reposition the item if the parent is registered.
	if (screenID && !Temporary())
	{
		// Optimize the update.
		bool saveUpdate = AutomaticUpdate();
		SetAutomaticUpdate(false);
		ZafWindowObject *oldFirst = firstVisible;

		// Determine the item positions.
		ZafWindowObject *pObject = object->Previous();
		int line = pObject ? pObject->zafRegion.bottom : 0;
		for (ZafWindowObject *temp = object; temp; temp = temp->Next())
		{
			ComputeNextPosition(temp, line);
			temp->Event(N_SIZE);
		}

		// Restore the update.
		if (saveUpdate && oldFirst == firstVisible)
		{
			automaticUpdate = saveUpdate;
			ScrollEvent(S_VSCROLL_COMPUTE);
			ZafEventStruct event(S_REDISPLAY_REGION);
			event.region = object->zafRegion;
			event.region.bottom = clientRegion.Height() - 1;
			Event(event);
		}
		else
			SetAutomaticUpdate(saveUpdate);
	}

	// Return the object pointer.
	return (object);
}

void ZafVtList::CheckMembers(ZafWindowObject *object)
{
	if (!object)
		firstVisible = lastVisible = cancelObject = ZAF_NULLP(ZafWindowObject);
	else
	{
		// Check cancelObject.
		if (cancelObject == object)
			cancelObject = ZAF_NULLP(ZafWindowObject);

		// Check firstVisible.
		if (firstVisible == object && Temporary())
			firstVisible = ZAF_NULLP(ZafWindowObject);
		else if (firstVisible == object)
		{
			firstVisible = object->Next();
			if (!firstVisible)
				firstVisible = object->Previous();
		}

		// Check lastVisible.
		if (lastVisible == object && Temporary())
			lastVisible = ZAF_NULLP(ZafWindowObject);
		else if (lastVisible == object)
		{
			lastVisible = object->Next();
			if (!lastVisible)
				lastVisible = object->Previous();
		}
	}
}

void ZafVtList::ComputeNextPosition(ZafWindowObject *object, int &line)
{
	// Set the object region.
	ZafRegionStruct region = object->Region();
	region.left = 0;
	region.right = itemWidth - 1;
	if (line && !Temporary())
		line -= ZAF_BORDER_SIZE - 1;
		
	region.top = line;
	region.bottom = line = line + itemHeight - 1;
	object->SetRegion(region);
	if (Temporary())
		line++;

	// Update firstVisible and lastVisible.
	if (object->zafRegion.top == 0)
		firstVisible = object;
	else if (!firstVisible)
		firstVisible = lastVisible = object;
	if (object->zafRegion.bottom >= 0 &&
		object->zafRegion.bottom <= clientRegion.bottom - clientRegion.top)
		lastVisible = object;
}

void ZafVtList::ComputePreviousPosition(ZafWindowObject *object, int &line)
{
	// Set the object region.
	ZafRegionStruct region = object->Region();
	region.left = 0;
	region.right = itemWidth - 1;
	if (line && !Temporary())
		line += ZAF_BORDER_SIZE - 1;

	region.bottom = line;
	region.top = line = line - itemHeight + 1;
	object->SetRegion(region);
}

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

ZafEventType ZafVtList::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
		{
			// Clear all selections if extended selection list.
			if (selectionType == ZAF_EXTENDED_SELECTION)
			{
				for (ZafWindowObject *item = First(); item; item = item->Next())
					item->SetSelected(false);
			}

			if (object->IsA(ID_ZAF_LIST))
				ccode = ZafWindow::DragDropEvent(event);
			else
			{
				const ZafIChar *dropText = object->Text();
				if (dropText && *dropText)
				{
					ZafString *newString = new ZafString(0, 0, 0, dropText, -1);
					newString->SetBordered(false);
					Add(newString);
					if (ccode != S_DROP_COPY)
						object->SetText(ZafLanguageManager::blankString);
				}
				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 ZafVtList::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	// Check for draw errors.
	if (!screenID)
		return (S_ERROR);

	// Compute the actual draw region.
	ZafRegionStruct drawRegion = BeginDraw();
	ZafRegionStruct clip = drawRegion;
	if (ccode == E_MOTIF || ccode == S_REDISPLAY_REGION)
		clip = event.region;

	// Draw the list items that appear on the screen.
	ZafRegionStruct region = drawRegion; region.bottom = region.top - 1;
	for (ZafWindowObject *object = firstVisible; object; object = object->Next())
        if (object->zafRegion.top < drawRegion.bottom)
		{
			// Adjust the region so surrounding areas do not get drawn for
			// a focus change.
			region = object->zafRegion;

			// Draw the item.
			if (region.Overlap(clip))
				object->Draw(event, object->Focus() ? S_CURRENT : S_NON_CURRENT);
		}
		else
			break; // Stop when items are no longer visible.

	// Draw the up/down arrows.
	if (Temporary() && firstVisible && lastVisible)
	{
		// Determine the size of the arrows.
		// up
		upArrow = firstVisible->zafRegion;
		upArrow.right -= 2 * ZAF_SHADOW_SIZE;
		upArrow.left = upArrow.right - ARROW_SIZE + 1;
		upArrow.top += (upArrow.Height() - ARROW_SIZE) / 2;
		upArrow.bottom = upArrow.top + ARROW_SIZE - 1;
		// down
		downArrow = lastVisible->zafRegion;
		downArrow.right -= 2 * ZAF_SHADOW_SIZE;
		downArrow.left = downArrow.right - ARROW_SIZE + 1;
		downArrow.bottom -= (downArrow.Height() - ARROW_SIZE) / 2;
		downArrow.top = downArrow.bottom - ARROW_SIZE + 1;
		// draw the arrows.
		DrawArrows();
	}

	// Clear any remaining blank area.
	region.top = region.bottom + 1;
	region.bottom = drawRegion.bottom;
	if (region.Overlap(clip))
	{
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE));
		display->Rectangle(region, 0, true);
	}

	// Draw the focus rectangle.
	if (Current() && Focus())
	{
		drawRegion = Current()->zafRegion;
		DrawFocus(drawRegion, S_CURRENT);
	}
	else if (Focus())
	{
		drawRegion = clientRegion;
		drawRegion.HzShift(-drawRegion.left);
		drawRegion.VtShift(-drawRegion.top);
		DrawFocus(drawRegion, S_CURRENT);
	}

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

void ZafVtList::DrawArrows(void)
{
	// Draw the scroll arrows.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();

	ZafPaletteStruct topShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
	ZafPaletteStruct bottomShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
	ZafPaletteStruct background = LogicalPalette(ZAF_PM_BACKGROUND, state);

	if (firstVisible && firstVisible->Previous())
	{
        ZafRegionStruct arrowRegion = upArrow;
        arrowRegion.HzShift(drawRegion.left);
	    arrowRegion.VtShift(drawRegion.top);
    	_XmDrawArrow(XtDisplay(screenID), XtWindow(screenID),
    		upDepressed ? bottomShadow.osPalette : topShadow.osPalette,
    		upDepressed ? topShadow.osPalette : bottomShadow.osPalette,
    		background.osPalette, arrowRegion.left - 1, arrowRegion.top - 1,
    		upArrow.Width() + 2, upArrow.Height() + 2, ZAF_SHADOW_SIZE,
    		XmARROW_UP);
    }

	if (lastVisible && lastVisible->Next())
	{
        ZafRegionStruct arrowRegion = downArrow;
    	arrowRegion.HzShift(drawRegion.left);
    	arrowRegion.VtShift(drawRegion.top);
    	_XmDrawArrow(XtDisplay(screenID), XtWindow(screenID),
    		downDepressed ? bottomShadow.osPalette : topShadow.osPalette,
    		downDepressed ? topShadow.osPalette : bottomShadow.osPalette,
    		background.osPalette, arrowRegion.left - 1, arrowRegion.top - 1,
    		downArrow.Width() + 2, downArrow.Height() + 2, ZAF_SHADOW_SIZE,
    		XmARROW_DOWN);
    }

	EndDraw();
}


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

ZafEventType ZafVtList::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	// Draw the proper focus rectangle.
	MtfList *mtfObject = MtfList::GetMotifWidget(screenID);
	int width = mtfObject->primitive.highlight_thickness;
	if (Temporary())
	{
		// Check for a valid list.
		if (!screenID || !XtIsRealized(screenID))
			return (ccode);

		// Check for a current item.
		if (ccode == S_CURRENT)
			return (DrawShadow(region, width, ccode));

		// Remove the shadow on non-current items.
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE));
		display->Rectangle(region, width);
		region -= width;
	}
	else if (ccode == S_CURRENT)
	{
		display->SetPalette(LogicalPalette(ZAF_PM_FOCUS, ZAF_PM_ANY_STATE));
		display->Rectangle(region, width);
		region -= width;
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE));
		display->Rectangle(region, 1);
		region--;
	}
	else
	{
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE));
		display->Rectangle(region, ++width);
		region -= width;
	}

	// Return success.
	return (ccode);
}
#endif

ZafEventType ZafVtList::Event(const ZafEventStruct &event)
{
	// Check for zinc events.
	ZafWindowObject *object;
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	case S_INITIALIZE:
		itemWidth = itemHeight = 0;
        upDepressed = downDepressed = false;
		ccode = ZafWindow::Event(event);
		break;

	case S_REGISTER_OBJECT:
		ccode = ZafWindow::Event(event);
		MouseObject(MtfList::GetMotifWidget(screenID)) = ZAF_NULLP(ZafWindowObject);
		
		// If the list is part of a combo box set any xdefaults for combo box.
		if (Temporary())
		{
			// 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(parent->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(parent->screenID, XmNbackground, &background, 
			 XmNforeground, &foreground, XmNtopShadowColor, &lightShadow,
			 XmNbottomShadowColor, &darkShadow, 0);

			XtVaSetValues(screenID, XmNbackground, background,
			 XmNforeground, foreground, XmNtopShadowColor, lightShadow,
			 XmNbottomShadowColor, darkShadow, XmNfontList, font, 0);
		}
		break;
	// ----- Position & Size messages -----
	case N_SIZE:
		{
		// Clean up the scroll pixmap.
		MtfList::destroy_pixmap(MtfList::GetMotifWidget(screenID));

		// Compute the cell sizes.
		if (!itemWidth || !itemHeight || OSDraw())
		{
			if (OSDraw())
			{
				// Use the font size to determine the size of the field.
				display->SetPalette(LogicalPalette(ZAF_PM_TEXT, PaletteState()));
				itemHeight = display->TextSize(StringID()).Height() + 2 * (ZAF_BORDER_SIZE + 1);
			}
			else
			{
				// Use the field that has the largest area.
				itemHeight = display->cellHeight;
				for (object = First(); object; object = object->Next())
				{
					object->Event(S_COMPUTE_SIZE);
					if (object->zafRegion.Height() > itemHeight)
						itemHeight = object->zafRegion.Height();
				}
				itemHeight -= display->preSpace + display->postSpace + ZAF_BORDER_SIZE;
			}
		}

		// Check for special temporary lists.
		if (Temporary())
		{
	      // Don't let temporary lists get too large.
	      int tCount = Count();
	      int maxSize = display->lines / 2;
			ZafComboBox *combo = DynamicPtrCast(parent, ZafComboBox);
	 		if (combo->MaximumListHeight() != -1 && maxSize > combo->MaximumListHeight())
	 			maxSize = combo->MaximumListHeight();
	      if (tCount * itemHeight > maxSize)
	         tCount = maxSize / itemHeight;
         // Compute the actual list size.
			if (First())
				zafRegion.bottom = zafRegion.top + tCount * itemHeight + 2 * ZAF_SHADOW_SIZE - 1;
			else
				zafRegion.bottom = zafRegion.top + itemHeight + 2 * ZAF_SHADOW_SIZE - 1;
			if (combo->ListWidth() != -1)
				zafRegion.right = zafRegion.left + combo->ListWidth();
			clientRegion.left = clientRegion.top = ZAF_SHADOW_SIZE;
			clientRegion.right = zafRegion.right - zafRegion.left - ZAF_SHADOW_SIZE;
			clientRegion.bottom = zafRegion.bottom - zafRegion.top - ZAF_SHADOW_SIZE;
			itemWidth = clientRegion.Width();
		}
		itemWidth = clientRegion.Width();

		// Make sure the client region is full of items.
		if (firstVisible)
		{
			int tCount = clientRegion.Height() / (itemHeight - 2);
			ZafWindowObject *newFirst = firstVisible, *newLast = firstVisible;
			for ( ; tCount > 0 && newLast; tCount--)
				newLast = newLast->Next();
			for ( ; tCount > 0 && newFirst; tCount--)
				newFirst = newFirst->Previous();
			firstVisible = newFirst ? newFirst : ZAF_NULLP(ZafWindowObject);
		}
		
		// Work down from firstVisible.
		int line = 0;
		object = firstVisible ? firstVisible : First();
		for ( ; object; object = object->Next())
			ComputeNextPosition(object, line);

		// Work up from firstVisible.
		line = firstVisible ? firstVisible->zafRegion.top : 0;
		object = firstVisible ? firstVisible->Previous() : ZAF_NULLP(ZafWindowObject);
		for ( ; object; object = object->Previous())
			ComputePreviousPosition(object, line);
		
		// Adjust the scroll information.
		ScrollEvent(S_VSCROLL_CHECK);
		ScrollEvent(S_VSCROLL_COMPUTE);
		}
		break;

	case S_SIZE:
	case S_COMPUTE_SIZE:
		// Defer to the base class.
		ccode = ZafWindowObject::Event(event);
		break;

	// ----- Selection messages -----
	case L_SELECT:
	case L_DOUBLE_CLICK:
		// Select or double-click the current item.
		object = Current();
		if (object && !object->Disabled(false))
		{
			if (SelectionType() != ZAF_SINGLE_SELECTION)
				object->ToggleSelected();
			else
				object->SetSelected(true);

			// Call the associated user function.
			ccode = (object->*(object->memberUserFunction))(event, ccode);
			if (Temporary())
				ccode = (parent->*(parent->memberUserFunction))(event, L_SELECT);
		}
		
		// Notify the parent if a temporary window.
		if (Temporary())
			parent->Event(S_CLOSE_TEMPORARY);
		break;

	case L_BEGIN_SELECT:
		// Clear the oldCurrent for extended selection lists.
		oldCurrent = ZAF_NULLP(ZafWindowObject);
	case L_CONTINUE_SELECT:
	case L_END_SELECT:
		// Check for the current focus.
		if (ccode == L_BEGIN_SELECT && !Focus())
				SetFocus(true);
		else if (!Focus() && !Noncurrent())
			break; // illegal selection process.

		// Check for out-of-bounds.
		if (!clientRegion.Overlap(event.position, clientRegion.left, clientRegion.top))
		{
			if (ccode == L_BEGIN_SELECT && Temporary())
				return (ZafVtList::Event(L_CANCEL));
			break;
		}

      // Check for special mouse handling.
      if (Temporary())
		{
			MtfList *mtfObject = MtfList::GetMotifWidget(screenID);
            if ((ccode == L_BEGIN_SELECT || (ccode == L_CONTINUE_SELECT && event.osEvent.type == 0)) &&
                upArrow.Overlap(event.position) && firstVisible && firstVisible->Previous())
            {
                upDepressed = true;
    			ZafEventStruct scrollEvent(S_VSCROLL);
    			scrollEvent.scroll.delta = -1;
    			ScrollEvent(scrollEvent);
            }
            else if ((ccode == L_BEGIN_SELECT || (ccode == L_CONTINUE_SELECT && event.osEvent.type == 0)) &&
                downArrow.Overlap(event.position) && lastVisible && lastVisible->Next())
            {
                downDepressed = true;
    			ZafEventStruct scrollEvent(S_VSCROLL);
    			scrollEvent.scroll.delta = 1;
    			ScrollEvent(scrollEvent);
            }

            // Update the arrow information.
            if (upDepressed || downDepressed)
            {
					// Reset the timer.
					if (ListIntervalId((Widget)mtfObject))
						XtRemoveTimeOut(ListIntervalId((Widget)mtfObject));
					ListIntervalId((Widget)mtfObject) = 0;

					// Handle the message.
					if (ccode == L_END_SELECT)
					{
						// Remove the timer.
						LastMousePos((Widget)mtfObject).Assign(-1, -1, ZAF_PIXEL);
						// Redraw the object.
						upDepressed = downDepressed = false;
						DrawArrows();
					}
					else if (upArrow.Overlap(event.position) || downArrow.Overlap(event.position))
					{
						// Start the repeat process.
						LastMousePos((Widget)mtfObject) = event.position;
						ListIntervalId((Widget)mtfObject) = XtAppAddTimeOut(((ZafScreenDisplay *)display)->xAppContext,
						  (ccode == L_BEGIN_SELECT) ? InitialDelay() : RepeatDelay(),
						  (XtTimerCallbackProc)mtfObject->repeat, (XtPointer)mtfObject);
					}
					return (ccode);
            }
		}

		// Maintain the old current for extended select list.
		if (ccode == L_BEGIN_SELECT)
			oldCurrent = Current();

		// Try to find an overlapping item.
		for (object = firstVisible; object; object = object->Next())
			if (object->zafRegion.Overlap(event.position))
			{
				// Check for non-selectable overlaps.
				if (object->Disabled(false))
					return (S_ERROR);

				// Reset the focus.
				object->SetFocus(true);
				if (Temporary() || Noncurrent())
					SetCurrent(object);
				break;
			}

		// Check for a new selection.
		if (ccode == L_END_SELECT)
		{
			object = Current();
			if (object && !object->Disabled(false))
			{
				// Change the selection.
				if ((SelectionType() == ZAF_EXTENDED_SELECTION && 
				 event.modifiers & S_SHIFT) || 
				 (SelectionType() == ZAF_EXTENDED_SELECTION && 
				 !(event.modifiers & S_CTRL)))
				{
					if (object->Selected())
						object->SetSelected(false);
					// If no control key treat as single select list.
					if (!(event.modifiers & S_CTRL))
					{
						selectionType = ZAF_SINGLE_SELECTION;
						object->SetSelected(true);
						selectionType = ZAF_EXTENDED_SELECTION;
					}
					// If shift key select everything between old current and current.
					if (event.modifiers & S_SHIFT)
					{
						ZafWindowObject *firstObj = ZAF_NULLP(ZafWindowObject);
						ZafWindowObject *lastObj = ZAF_NULLP(ZafWindowObject);
						// Find lowest index for starting point.
						if (Index(oldCurrent) < Index(Current()))
						{
							firstObj = oldCurrent;
							lastObj = Current();
						}
						else 
						{
							firstObj = Current();
							lastObj = oldCurrent;
						}
						// Select all of the object between firstObj and lastObj.
						for (; firstObj && firstObj != lastObj; firstObj = firstObj->Next())
							firstObj->SetSelected(true);
						// Make sure last object is selected. For case of Control Key.
						if (!lastObj->Selected())
							lastObj->SetSelected(true);
					}
				}
				// If extended list and control key treat as multiple select list.
				else if (SelectionType() == ZAF_MULTIPLE_SELECTION || 
				 (SelectionType() == ZAF_EXTENDED_SELECTION && 
				 event.modifiers & S_CTRL))
					object->ToggleSelected();
				// Single Select list.
				else
					object->SetSelected(true);

				// Call the associated user function.
				ccode = (object->*(object->memberUserFunction))(event, L_SELECT);
				if (Temporary())
					ccode = (parent->*(parent->memberUserFunction))(event, L_SELECT);
			}

			// Notify the parent if a temporary window.
			if (Temporary())
				parent->Event(S_CLOSE_TEMPORARY);
		}
		break;

	case L_CANCEL:
		// Notify the parent if a temporary window.
		if (cancelObject)
			cancelObject->SetFocus(true);
		if (Temporary())
			parent->Event(S_CLOSE_TEMPORARY);
		break;

	// ----- Miscellaneous -----
	case S_HOT_KEY:
		ccode = S_UNKNOWN;
		break;

	case S_BEGIN_DRAG:
		if (!Temporary())
			ccode = ZafWindow::Event(event);
		break;

	case N_CURRENT:
		// Update the cancel object (Used with L_CANCEL).
		ccode = ZafWindow::Event(event);
		cancelObject = Current();
		break;

	// ----- Default operation -----
	default:
		ccode = ZafWindow::Event(event);
		break;
	}

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

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

ZafEventType ZafVtList::MoveEvent(const ZafEventStruct &event)
{
	// Determine the new selection.
	ZafEventType ccode = event.type;
	ZafWindowObject *object = Current();
	ZafWindowObject *pObject = object ? object->Previous() : ZAF_NULLP(ZafWindowObject);
	ZafWindowObject *nObject = object ? object->Next() : ZAF_NULLP(ZafWindowObject);
	int height = clientRegion.bottom - clientRegion.top;

	switch (ccode)
	{
	case L_EXTEND_PREVIOUS:
	case L_UP:
	case L_PREVIOUS:
		if (pObject)
			object = AdjustPrevious(pObject);
		break;

	case L_EXTEND_NEXT:
	case L_DOWN:
	case L_NEXT:
		if (nObject)
			object = AdjustNext(nObject);
		break;

	case L_PGUP:
		// Check for page actions.
		if (!object || !pObject)
			break; // No current object exists.
		else if (Temporary())
			object = AdjustNext(First());
		else if (object == firstVisible)
		{
			// Go to the top of the previous page.
			for ( ; pObject->Previous(); pObject = pObject->Previous())
				if (object->zafRegion.bottom - pObject->Previous()->zafRegion.top > height)
					break;
			object = AdjustPrevious(pObject);
		}
		else
			object = AdjustPrevious(firstVisible);
		break;

	case L_PGDN:
		// Check for page actions.
		if (!object || !nObject)
			break; // No current object exists.
		else if (Temporary())
			object = AdjustPrevious(Last());
		else if (object == lastVisible)
		{
			// Go to the bottom of the next page.
			for ( ; nObject->Next(); nObject = nObject->Next())
				if (nObject->Next()->zafRegion.bottom - object->zafRegion.top > height)
					break;
			object = AdjustNext(nObject);
		}
		else
			object = AdjustNext(lastVisible);
		break;

	case L_EXTEND_FIRST:
	case L_FIRST:
		object = AdjustNext(First());
		break;

	case L_EXTEND_LAST:
	case L_LAST:
		object = AdjustPrevious(Last());
		break;
	}

	// Check the current object.
	if (object && object != current)
	{
		// Change the selected object.
		if (SelectionType() == ZAF_SINGLE_SELECTION)
		{
			// Make the Object selected.
			object->SetSelected(true);

			ccode = (object->*(object->memberUserFunction))(event, L_SELECT);
		}

		// Reset the current object.
		if (screenID)
			object->SetFocus(true);
		else
			ZafList::SetCurrent(object);

		// Check the scroll information.
		if (screenID)
			ScrollEvent(S_VSCROLL_CHECK);
	}
	return (ccode);
}

bool ZafVtList::SetAutomaticUpdate(bool setAutomaticUpdate)
{
	// Defer to the base class.
	if (automaticUpdate == setAutomaticUpdate)
		return (automaticUpdate);
	ZafWindowObject::SetAutomaticUpdate(setAutomaticUpdate);

	// Now, update the children.
	if (screenID && AutomaticUpdate() && SystemObject())
	{
		ScrollEvent(S_VSCROLL_COMPUTE);
		RedisplayData();
	}
	return (automaticUpdate);
}

ZafEventType ZafVtList::ScrollEvent(const ZafEventStruct &event)
{
	// Process the list scrolling requests.
	ZafEventType ccode = event.type;
	switch (event.type)
	{
	case S_VSCROLL_CHECK:
		// Check for a valid object.
		if (Current() && firstVisible && lastVisible)
		{
			// Determine the scrolling values.
			int delta = 0;
			ZafWindowObject *object = Current();
			for ( ; object->zafRegion.top < firstVisible->zafRegion.top; object = object->Next())
				delta--;
			for ( ; object->zafRegion.bottom > lastVisible->zafRegion.bottom; object = object->Previous())
				delta++;

			// Scroll the list elements or adjust the values.
			if (delta)
			{
				ZafEventStruct sEvent(S_VSCROLL);
				sEvent.scroll.delta = delta;
				ScrollEvent(sEvent);
			}
		}
		break;

	case S_VSCROLL:
	case N_VSCROLL:
		// Scroll the list elements according to the delta.
		if (event.scroll.delta && firstVisible && lastVisible)
		{
			// Determine the new first and last items.
			int delta = (int)event.scroll.delta;
			ZafWindowObject *oldFirst = firstVisible;
			for ( ; delta > 0 && lastVisible->Next(); delta--)
			{
				firstVisible = firstVisible->Next();
				lastVisible = lastVisible->Next();
			}
			for ( ; delta < 0 && firstVisible->Previous(); delta++)
			{
				firstVisible = firstVisible->Previous();
				lastVisible = lastVisible->Previous();
			}

			// Check for a valid scroll operation.
			if (delta == event.scroll.delta)
				return (ccode);
			else if (ccode == S_VSCROLL)
				ScrollEvent(S_VSCROLL_COMPUTE);

			// Recalculate the object regions.
			delta = oldFirst->zafRegion.top - firstVisible->zafRegion.top;
			for (ZafWindowObject *object = First(); object; object = object->Next())
			{
				ZafRegionStruct region = object->Region();
				region.VtShift(delta);
				object->SetRegion(region);
				object->Event(N_SIZE);
			}

			// Check for existing repaint directives.
			if (!screenID || !Visible() || !XtIsRealized(screenID) || !XtWindow(screenID))
				break;
            else if (Temporary())
            {
                Redisplay();
				break;
            }
			ZafEventStruct check;
			check.route = this;
			if (eventManager->Get(check, Q_NO_BLOCK | Q_NO_DESTROY | Q_NO_POLL | Q_END) == 0 && check.route == this)
			{
				// Optimize for keyboard operations.
				if (check.type == S_CONTINUE && ccode == S_VSCROLL)
				{
					check.type = S_REDISPLAY;
					eventManager->Put(check);
				}
				break;
			}

			// Optimize the redisplay of list items.
			MtfList *mtfObject = MtfList::GetMotifWidget(screenID);
			int width = mtfObject->core.width;
			int height = mtfObject->core.height;
			if (!ListPixmap((Widget)mtfObject))
				ListPixmap((Widget)mtfObject) = XCreatePixmap(XtDisplay(screenID), XtWindow(screenID),
					width, height, mtfObject->core.depth);

			if (event.scroll.delta == 1)
			{
				// Shift the current items up.
				int size = firstVisible->zafRegion.top - oldFirst->zafRegion.top;
				XCopyArea(XtDisplay(screenID), XtWindow(screenID), ListPixmap((Widget)mtfObject),
					mtfObject->list.NormalGC, 0, size, width, height - size, 0, 0);
				XCopyArea(XtDisplay(screenID), ListPixmap((Widget)mtfObject), XtWindow(screenID),
					mtfObject->list.NormalGC, 0, 0, width, height - size, 0, 0);
				check.type = S_REDISPLAY_REGION;
				check.region.Assign(0, height - size, width, size, ZAF_PIXEL);
				Draw(check, check.type);
				check.type = (ccode == S_VSCROLL) ? S_CONTINUE : 0;
			}
			else if (event.scroll.delta == -1)
			{
				// Shift the current items down.
				int size = oldFirst->zafRegion.top - firstVisible->zafRegion.top;
				XCopyArea(XtDisplay(screenID), XtWindow(screenID), ListPixmap((Widget)mtfObject),
					mtfObject->list.NormalGC, 0, 0, width, height - size, 0, 0);
				XCopyArea(XtDisplay(screenID), ListPixmap((Widget)mtfObject), XtWindow(screenID),
					mtfObject->list.NormalGC, 0, 0, width, height - size, 0, size);
				check.type = S_REDISPLAY_REGION;
				check.region.Assign(0, 0, width, size, ZAF_PIXEL);
				Draw(check, check.type);
				check.type = (ccode == S_VSCROLL) ? S_CONTINUE : 0;
			}
			else
				check.type = S_REDISPLAY;

			// Put draw optimizations on the event manager.
			if (check.type)
				eventManager->Put(check);
		}
		break;

	case S_VSCROLL_COMPUTE:
		// Check for a scroll-bar.
		{
		ZafScrollBar *scrollBar = VerticalScrollBar();
		if (itemHeight && scrollBar && firstVisible && lastVisible)
		{
			// Reset the scroll values.
			int delta = itemHeight - ZAF_BORDER_SIZE;

			ZafScrollStruct vScroll;
			vScroll.delta = 0;
			vScroll.showing = (clientRegion.Height() - ZAF_BORDER_SIZE) / delta;
			vScroll.current = (firstVisible->zafRegion.bottom - First()->zafRegion.top - ZAF_BORDER_SIZE) / delta;
			vScroll.minimum = 0;
			vScroll.maximum = (Last()->zafRegion.bottom - First()->zafRegion.top + 1 - ZAF_BORDER_SIZE) / delta - vScroll.showing;

			scrollBar->ScrollData()->SetScroll(vScroll);
		}
		}
		break;

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

	// ----- Default operation -----
	default:
		// No other messages apply to the vertical list object.
		return (S_ERROR);
	}

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

ZafWindowObject *ZafVtList::NotifyFocus(ZafWindowObject *focusObject, bool setFocus)
{
	// Defer to the base class.
	ZafWindowObject *object = Current();
	ZafWindowObject *invalidObject = ZafWindow::NotifyFocus(focusObject, setFocus);
	focusObject = Current();

	// Update the focus.
	if (screenID)
	{
		if (!focusObject)
		{
			// Draw a full window focus rectangle.
			ZafRegionStruct drawRegion = BeginDraw();
			drawRegion = clientRegion;
			drawRegion.HzShift(-drawRegion.left);
			drawRegion.VtShift(-drawRegion.top);
			DrawFocus(drawRegion, focus ? S_CURRENT : S_NON_CURRENT);
			EndDraw();
		}
		else
		{
			ZafRegionStruct drawRegion = BeginDraw();

			// Turn off the old item focus.
			if (!object)
			{
				drawRegion = clientRegion;
				drawRegion.HzShift(-drawRegion.left);
				drawRegion.VtShift(-drawRegion.top);
				DrawFocus(drawRegion, S_NON_CURRENT);
			}
			else if (object != focusObject)
			{
				drawRegion = object->zafRegion;
				DrawFocus(drawRegion, S_NON_CURRENT);
			}

			// Turn on the new item focus.
			drawRegion = focusObject->zafRegion;
			DrawFocus(drawRegion, setFocus ? S_CURRENT : S_NON_CURRENT);

			EndDraw();
		}
	}

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

ZafWindowObject *ZafVtList::NotifySelection(ZafWindowObject *object, bool setSelected)
{
	// Defer to the base class, checking for selection error.
	ZafWindow::NotifySelection(object, setSelected);

	if (SelectionType() == ZAF_SINGLE_SELECTION)
		if (Focus() && object->Selected())
			object->SetFocus(true);
		else
			ZafList::SetCurrent(object);

	if (object->Selected() == setSelected)
		object->RedisplayData();

	if (parent && parent->IsA(ID_ZAF_COMBO_BOX))
	{
		parent->NotifySelection(object, setSelected);
		if (object->Selected())
			SetCurrent(object);
	}

	// Return the object selection.
	return (object);
}

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

ZafWindowObject *ZafVtList::Subtract(ZafWindowObject *object)
{
	// Optimize the update.
	bool saveUpdate = AutomaticUpdate();
	SetAutomaticUpdate(false);

	// Remove the object from the list.
	CheckMembers(object);
	ZafWindowObject *nextObject = ZafWindow::Subtract(object);

	// Reposition items if the list is registered.
	if (screenID && !object->SupportObject())
		Event(N_SIZE);

	// Restore the update and return the next object.
	SetAutomaticUpdate(saveUpdate);
	return (nextObject);
}

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

void ZafVtList::OSDestroy(void)
{
	// Update the list members.
	CheckMembers(ZAF_NULLP(ZafWindowObject));

	// Notify each of the children.
	for (ZafWindowObject *object = First(); object; object = object->Next())
		object->Event(S_DEINITIALIZE);
	
	// Update the display
	if (screenID && AutomaticUpdate())
	{
       	// If there is a scrollbar reset its scroll range to 0. 
		ZafScrollBar *scrollBar = VerticalScrollBar();
		if (itemHeight && scrollBar)
		{
			// Reset the scroll values.
			int delta = itemHeight - ZAF_BORDER_SIZE;
		
			ZafScrollStruct vScroll;
			vScroll.delta = 0;
			vScroll.showing = (clientRegion.Height() - ZAF_BORDER_SIZE) / delta;
			vScroll.current = 0;
			vScroll.minimum = 0;
			vScroll.maximum = 0;
			scrollBar->ScrollData()->SetScroll(vScroll);
		}
		RedisplayData();
	}
}

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

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

	// Determine the proper palette.
	MtfList *mtfObject = MtfList::GetMotifWidget(screenID);
	if (type == ZAF_PM_TEXT || type == ZAF_PM_HOT_KEY)
	{
		if (state & ZAF_PM_DISABLED)
		{	
			XGCValues values;
			XtGCMask mask = GCForeground | GCBackground | GCFillStyle | GCTile;
			values.background = mtfObject->core.background_pixel;
			values.foreground = mtfObject->primitive.foreground;
			values.fill_style = FillTiled;
			values.tile = XmGetPixmapByDepth(XtScreen(screenID), "50_foreground",
			  values.foreground, values.background, mtfObject->core.depth);
			palette.osPalette = mtfObject->list.InsensitiveGC;
			XChangeGC(XtDisplay(screenID), palette.osPalette, mask, &values);
		}
		else if ((state & ZAF_PM_SELECTED) && !Temporary())
			palette.osPalette = mtfObject->list.InverseGC;
		else
			palette.osPalette = mtfObject->list.NormalGC;
	}
	else if (type == ZAF_PM_BACKGROUND)
	{
		if ((state & ZAF_PM_SELECTED) && !Temporary())
			palette.osPalette = mtfObject->list.NormalGC;
		else
			palette.osPalette = mtfObject->list.InverseGC;
	}
	else
		ZafWindowObject::OSMapPalette(palette, type, state);
}

OSWindowID ZafVtList::OSScreenID(ZafScreenIDType type) const
{
	// Return the os handle.
	if (type == ZAF_SHELLID && Temporary())
		return (screenID ? XtParent(XtParent(screenID)) : 0);
	return (ZafWindow::OSScreenID(type));
}

void ZafVtList::OSSize(void)
{
	ZafScrollBar *scrollBar = VerticalScrollBar();
	if (scrollBar && scrollBar->screenID && Bordered())
	{
		int scrollBarSize = 0;
		XtVaGetValues(scrollBar->screenID, XmNwidth, &scrollBarSize, 0);
		// If the list is to narrow the scroll bar will not have enough room
		// and will end up width 0 and core.  If width will be 0 make the
		// list narrow enough so that the scrollbar width is negative.
		if ((scrollBarSize + 2 * ZAF_SHADOW_SIZE) == zafRegion.Width())
			zafRegion.right--;
	}
	// Default to the base class.
	ZafWindowObject::OSSize();
}

void ZafVtList::OSSort(void)
{
	// Items must be re-positioned in the list.
	if (screenID)
	{
		// Reset the list variables.
		CheckMembers(ZAF_NULLP(ZafWindowObject));
		ZafWindowObject *object = Current();
		if (object && !object->Selected())
			object = AdjustNext(First());
		if (object != Current())
			SetCurrent(object);

		// Adjust the list.
		Event(N_SIZE);
		RedisplayData();
	}
	else if (Temporary())
		CheckMembers(ZAF_NULLP(ZafWindowObject));
}

void ZafVtList::OSRegisterObject(void)
{
	// Check the for a temporary list.
	char *name = zafCodeSet->ConvertToOSString(stringID, 0, false);
	screenID = parent->OSScreenID(ZAF_CLIENTID);
	if (Temporary())
	{
		// Pre-Level 1 - Create a popup shell.
		screenID = XtVaCreatePopupShell(name,
			overrideShellWidgetClass, screenID,
			// user information
			XmNuserData, this,
			// region
			XmNx, zafRegion.left,
			XmNy, zafRegion.top,
 			XmNwidth, 10,
			XmNheight, 10,
			// miscellaneous
			XmNsensitive, Disabled(false) ? false : true,
			XmNtraversalOn, Noncurrent() ? false : true,
			XmNallowShellResize, false,
			XmNoverrideRedirect, true,
			0);

		// Set initial shell values.
		((ShellRec *)screenID)->shell.popped_up = false;
	}

	// Level 1 - Create the frame area.
	screenID = XtVaCreateWidget(name,
		(WidgetClass)&MtfWindowFrame::classRecord, screenID,
		// user information
		XmNuserData, this,
		// miscellaneous
		XmNresizePolicy, XmRESIZE_NONE,
		XmNshadowThickness, Bordered() ? ZAF_SHADOW_SIZE : 0,
		XmNmarginWidth, 0,
		XmNmarginHeight, 0,
		0);

	// Level 2 - Create the client area.
	screenID = XtVaCreateManagedWidget(name,
		(WidgetClass)&MtfList::classRecord, screenID,
		// user information
		XmNuserData, this,
		// miscellaneous
		XmNallowOverlap, true,
		XmNlistSizePolicy, XmCONSTANT,
		XmNtraversalOn, Noncurrent() ? false : true,
		XmNborderWidth, 0,
		0);

	// Set additional default values.
	MtfList *mtfObject = MtfList::GetMotifWidget(screenID);
	ListIntervalId((Widget)mtfObject) = 0;
	LastMousePos((Widget)mtfObject).Assign(-1, -1, ZAF_PIXEL);
	
#if defined(sun)
	mtfObject->list.InsensitiveGC = XCreateGC(XtDisplay(screenID),
	 RootWindow(XtDisplay(screenID), DefaultScreen(XtDisplay(screenID))), 0, NULL);
#endif

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

	// Set additional default values.
	MtfWindowFrame *mtfFrame = MtfWindowFrame::GetMotifWidget(OSScreenID(ZAF_FRAMEID));
	mtfFrame->OverrideTranslations();
	Shadow_Type((Widget)mtfFrame) = Temporary() ? XmSHADOW_OUT : XmSHADOW_IN;

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

	// Add drag/drop capabilities.
	ZafDropSiteRegister(this);
}

ZafError ZafVtList::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	// Defer to the base class.
	return (ZafWindow::OSUpdatePalettes(zafTypes, osTypes));
}

