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

#include <z_hlist.hpp>
#include <z_scrll2.hpp>
#include <z_str1.hpp>
#include <z_string.hpp>
#include "m_list.hpp"

// ----- ZafHzList ----------------------------------------------------------

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

static ZafWindowObject *FindBOL(ZafWindowObject *object)
{
	// Find the first object in the line.
	while (object && object->Previous() &&
		object->Previous()->zafRegion.left == object->zafRegion.left)
		object = object->Previous();
	return (object);
}

static ZafWindowObject *FindEOL(ZafWindowObject *object)
{
	// Find the last object in the line.
	while (object && object->Next() &&
		object->Next()->zafRegion.left == object->zafRegion.left)
		object = object->Next();
	return (object);
}

ZafWindowObject *ZafHzList::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())
		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)
	{
		// 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;
		int column = pObject ? pObject->zafRegion.left : 0;
		for (ZafWindowObject *temp = object; temp; temp = temp->Next())
		{
			ComputeNextPosition(temp, column, line);
			temp->Event(N_SIZE);
		}

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

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

void ZafHzList::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)
		{
			firstVisible = object->Next();
			if (!firstVisible)
				firstVisible = FindBOL(object->Previous());
		}

		// Check lastVisible.
		if (lastVisible == object)
		{
			lastVisible = FindEOL(object)->Next();
			if (!lastVisible)
				lastVisible = FindBOL(object->Previous());
		}
	}
}

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

	// Update firstVisible and lastVisible.
	if (object->zafRegion.top == 0 && object->zafRegion.left == 0)
		firstVisible = object;
	else if (!firstVisible)
		firstVisible = lastVisible = object;
	// Reset lastVisible if objects left is less than 0 and its right is less 
	// than the list's right or if it is wider then the list then it's left must
	// be less then list's right.
	if (object->zafRegion.left >= 0 && 
	 ((object->zafRegion.right <= clientRegion.right - clientRegion.left) ||
	  (object->zafRegion.Width() > clientRegion.Width() && object->zafRegion.left < clientRegion.right)))
		lastVisible = object;
}

void ZafHzList::ComputePreviousPosition(ZafWindowObject *object, int &column, int &line)
{
	// Set the object region.
	line -= itemHeight - ZAF_BORDER_SIZE;
	if (line < 0)
	{
		int delta = itemHeight - ZAF_BORDER_SIZE;
		column -= itemWidth - ZAF_BORDER_SIZE;
		line = (clientRegion.Height() - ZAF_BORDER_SIZE) / delta - 1;
		line = line * delta;
	}
	
	ZafRegionStruct region = object->Region();
	region.top = line;
	region.bottom = line + itemHeight - 1;
	region.left = column;
	region.right = column + itemWidth - 1;
	object->SetRegion(region);
}

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

ZafEventType ZafHzList::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_HZ_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 ZafHzList::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.
	int bottom = 0;
	ZafRegionStruct region = drawRegion; region.bottom = region.top - 1;
	for (ZafWindowObject *object = firstVisible; object; object = object->Next())
		if (object->zafRegion.left < drawRegion.right)
		{
			// Adjust the region so surrounding areas do not get drawn for
			// a focus change.
			region = object->zafRegion;
			region -= ZAF_BORDER_SIZE;

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

	// Clear any remaining blank area.
	display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE));
	region.top = region.bottom + 1;
	region.bottom = bottom;
	if (region.Overlap(clip))
		display->Rectangle(region, 0, true); // rest of the column.
	region.left = region.right + 1;
	region.top = 0;
	region.right = drawRegion.right;
	if (region.Overlap(clip))
		display->Rectangle(region, 0, true); // area to the right.
	region.left = 0;
	region.top = bottom + 1;
	region.bottom = drawRegion.bottom;
	if (region.Overlap(clip))
		display->Rectangle(region, 0, true); // area at the bottom.

	// Draw the region 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);
}

ZafEventType ZafHzList::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	// Draw the proper focus rectangle.
	MtfList *mtfObject = MtfList::GetMotifWidget(screenID);
	int width = mtfObject->primitive.highlight_thickness;
	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 ZafHzList::Event(const ZafEventStruct &event)
{
	// Check for zinc events.
	ZafWindowObject *object;
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	case S_INITIALIZE:
		itemWidth = itemHeight = 0;
		ccode = ZafWindow::Event(event);
		break;

	// ----- Position & Size messages -----
	case N_SIZE:
		// Check on first visible spacing.
		if (firstVisible)
		{
			int tCount = (clientRegion.Width() - ZAF_BORDER_SIZE) / (itemWidth - ZAF_BORDER_SIZE);
			ZafWindowObject *newFirst = firstVisible, *newLast = firstVisible;
			for ( ; tCount > 0 && newLast; tCount--)
				newLast = FindEOL(newLast->Next());
			for ( ; tCount > 0 && newFirst; tCount--)
				newFirst = FindBOL(newFirst->Previous());
			firstVisible = newFirst ? newFirst : ZAF_NULLP(ZafWindowObject);
		}

		{
		// Work down from firstVisible.
		int line = 0;
		int column = 0;
		object = firstVisible ? firstVisible : First();
		for ( ; object; object = object->Next())
		{
			object->Event(S_COMPUTE_SIZE);
			ComputeNextPosition(object, column, line);
		}

		// Work up from firstVisible.
		line = firstVisible ? firstVisible->zafRegion.top : 0;
		column = firstVisible ? firstVisible->zafRegion.left : 0;
		object = firstVisible ? firstVisible->Previous() : ZAF_NULLP(ZafWindowObject);
		for ( ; object; object = object->Previous())
		{
			object->Event(S_COMPUTE_SIZE);
			ComputePreviousPosition(object, column, line);
		}

		// Adjust the scroll information.
		ScrollEvent(S_HSCROLL_CHECK);
		ScrollEvent(S_HSCROLL_COMPUTE);
		}
		break;

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

		// Compute the cell sizes.
		ZafPositionStruct size;
		size.coordinateType = coordinateType;
		size.column = cellWidth;
		size.line = cellHeight;
		size.ConvertCoordinates(ZAF_PIXEL);
		itemWidth = size.column;
		itemHeight = size.line - display->preSpace + display->postSpace + ZAF_BORDER_SIZE;
		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);
		}
		}
		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);
		}
		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 (!Noncurrent())
		{
			if (ccode == L_BEGIN_SELECT && !Focus())
				SetFocus(true);
			else if (!Focus())
				break; // illegal selection process.
		}

		// Check for out-of-bounds.
		if (!clientRegion.Overlap(event.position, clientRegion.left, clientRegion.top))
			break;

		// 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.
				if (!Noncurrent())
					object->SetFocus(true);
				break;
			}

		// Check for a new selection.
		if (ccode == L_END_SELECT)
		{
			// Change the selection.
			object = Noncurrent() ? 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);
			}
		}
		break;

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

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

	case N_CURRENT:
		ccode = ZafWindow::Event(event);

		// Update the cancel object (Used with L_CANCEL).
		cancelObject = Current();
		break;

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

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

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

ZafEventType ZafHzList::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);

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

	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
			object = AdjustPrevious(FindBOL(pObject)); // Go to the top of the column.
		break;

	case L_PGDN:
		// Check for page actions.
		if (!object || !nObject)
			break; // No current object exists.
		else
			object = AdjustNext(FindEOL(nObject)); // Go to the bottom of the column.
		break;

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

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

	case L_LEFT:
		while (pObject && pObject->zafRegion.top != object->zafRegion.top)
			pObject = AdjustPrevious(pObject->Previous());
		if (pObject)
			object = AdjustPrevious(pObject);
		break;

	case L_RIGHT:
		while (nObject && nObject->zafRegion.top != object->zafRegion.top)
			nObject = AdjustNext(nObject->Next());
		if (nObject)
			object = AdjustNext(nObject);
		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.
		object->SetFocus(true);

		// Check the scroll information.
		ScrollEvent(S_HSCROLL_CHECK);
	}
	return (ccode);
}

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

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

	case S_HSCROLL:
	case N_HSCROLL:
		// 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 = FindEOL(firstVisible)->Next();
				lastVisible = FindEOL(lastVisible->Next());
			}
			for ( ; delta < 0 && firstVisible->Previous(); delta++)
			{
				firstVisible = FindBOL(firstVisible->Previous());
				lastVisible = FindBOL(lastVisible)->Previous();
			}

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

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

			// Check for existing repaint directives.
			if (!screenID || !Visible() || !XtIsRealized(screenID) || !XtWindow(screenID))
				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.left - oldFirst->zafRegion.left;
				XCopyArea(XtDisplay(screenID), XtWindow(screenID), ListPixmap((Widget)mtfObject),
					mtfObject->list.NormalGC, size, 0, width - size, height, 0, 0);
				XCopyArea(XtDisplay(screenID), ListPixmap((Widget)mtfObject), XtWindow(screenID),
					mtfObject->list.NormalGC, 0, 0, width - size, height, 0, 0);
				check.type = S_REDISPLAY_REGION;
				check.region.Assign(width - size, 0, 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.left - firstVisible->zafRegion.left;
				XCopyArea(XtDisplay(screenID), XtWindow(screenID), ListPixmap((Widget)mtfObject),
					mtfObject->list.NormalGC, 0, 0, width - size, height, 0, 0);
				XCopyArea(XtDisplay(screenID), ListPixmap((Widget)mtfObject), XtWindow(screenID),
					mtfObject->list.NormalGC, 0, 0, width - size, height, size, 0);
				check.type = S_REDISPLAY_REGION;
				check.region.Assign(0, 0, size, height, 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_HSCROLL_COMPUTE:
		// Check for a scroll-bar.
		{
		ZafScrollBar *scrollBar = HorizontalScrollBar();
		if (itemHeight && scrollBar && firstVisible && lastVisible)
		{
			// Reset the scroll values.
			int delta = itemWidth - ZAF_BORDER_SIZE;

			ZafScrollStruct hScroll;
			hScroll.delta = 0;
			hScroll.showing = delta > clientRegion.Width() ? 1 :
			 (clientRegion.Width() - ZAF_BORDER_SIZE) / delta;
			hScroll.current = (firstVisible->zafRegion.right - First()->zafRegion.left - ZAF_BORDER_SIZE) / delta;
			hScroll.minimum = 0;
			hScroll.maximum = (Last()->zafRegion.right - First()->zafRegion.left + 1 - ZAF_BORDER_SIZE) / delta - hScroll.showing;
			scrollBar->ScrollData()->SetScroll(hScroll);
		}
		}
		break;

	default:
		// No other messages apply to the horizontal list object.
		return (S_ERROR);
	}

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

ZafWindowObject *ZafHzList::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 *ZafHzList::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->SetFocus(true);
		else
			ZafList::SetCurrent(object);

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

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

bool ZafHzList::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_HSCROLL_COMPUTE);
		RedisplayData();
	}
	return (automaticUpdate);
}

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

ZafWindowObject *ZafHzList::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 ZafHzList::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);
}

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

void ZafHzList::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)
			palette.osPalette = mtfObject->list.InverseGC;
		else
			palette.osPalette = mtfObject->list.NormalGC;
	}
	else if (type == ZAF_PM_BACKGROUND)
		palette.osPalette = (state & ZAF_PM_SELECTED) ? mtfObject->list.NormalGC : mtfObject->list.InverseGC;
	else
		ZafWindowObject::OSMapPalette(palette, type, state);
}

OSWindowID ZafHzList::OSScreenID(ZafScreenIDType type) const
{
	// Return the os handle.
	return (ZafWindow::OSScreenID(type));
}

void ZafHzList::OSSize(void)
{
	ZafScrollBar *scrollBar = HorizontalScrollBar();
	if (scrollBar && scrollBar->screenID && Bordered())
	{
		int scrollBarSize = 0;
		XtVaGetValues(scrollBar->screenID, XmNheight, &scrollBarSize, 0);
		// If the list is to short the scroll bar will not have enough room
		// and will end up height 0 and core.  If height will be 0 make the
		// list short enough so that the scrollbar height is negative.
		if ((scrollBarSize + 2 * ZAF_SHADOW_SIZE) == zafRegion.Height())
			zafRegion.bottom--;
	}
	// Default to the base class.
	ZafWindowObject::OSSize();
}

void ZafHzList::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();
	}
}

void ZafHzList::OSRegisterObject(void)
{
	// Level 1 - Create the frame area.
	char *name = zafCodeSet->ConvertToOSString(stringID, 0, false);
	screenID = parent->OSScreenID(ZAF_CLIENTID);
	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);

#if defined(sun)
	MtfList *mtfObject = MtfList::GetMotifWidget(screenID);
	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) = XmSHADOW_IN;

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

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

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


