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

#include <z_table.hpp>
#include <z_scrll2.hpp>
#include <z_str1.hpp>
#include <z_button.hpp>
#include <z_utils.hpp>
#define ZAF_TABLE_INFO
#include "gbl_def.cpp"

static ZafStringID _virtualRecordName = ZAF_ITEXT("virtualRecord");

// ----- ZafTable -----------------------------------------------------------

ZafTable::ZafTable(int left, int top, int width, int height,
	int headerWidth, int headerHeight, int rowHeight) :
	ZafWindow(left, top, width, height),
	virtualRecord(ZAF_NULLP(ZafTableRecord)),
	currentOffset(-1), maxOffset(-1), focusSize(1),
	readFunction(ZAF_NULLF(MemberTableFunction)),
	writeFunction(ZAF_NULLF(MemberTableFunction)),
	grid(true), itemWidth(0), itemHeight(0), focusRecord(-1), focusField(-1)
{
#if defined(ZAF_TEXT_MODE)
 	focusSize = 0;
#else
	SetBordered(true);
#endif
	SetDestroyable(false);
	SetMoveable(false);
	SetOSDraw(false);
	SetSelectionType(ZAF_MULTIPLE_SELECTION);
	SetSizeable(false);

	// Create the support objects.
	virtualRecord = new ZafTableRecord(width, rowHeight);
	virtualRecord->parent = this;
	if (headerWidth && headerHeight)
		Add(new ZafTableHeader(headerWidth, headerHeight, ZAF_CORNER_HEADER));
	if (headerHeight)
		Add(new ZafTableHeader(0, headerHeight, ZAF_COLUMN_HEADER));
	if (headerWidth)
	{
		ZafTableHeader *rowHeader = new ZafTableHeader(headerWidth, 0, ZAF_ROW_HEADER);
		ZafString *string = new ZafString(0, 0, headerWidth, ZAF_ITEXT(""), -1);
		string->SetHzJustify(ZAF_HZ_RIGHT);
		rowHeader->virtualField = string;
		Add(rowHeader);
	}

#if defined(ZAF_MOTIF)
	firstTimeDrawing = true;
	getChildPalette = false;
	sizing = true;
#endif

}

ZafTable::ZafTable(const ZafTable &copy) :
	ZafWindow(copy),
	virtualRecord(ZAF_NULLP(ZafTableRecord)),
	currentOffset(-1), maxOffset(copy.maxOffset), focusSize(copy.focusSize),
	readFunction(copy.readFunction), writeFunction(copy.writeFunction),
	grid(copy.grid), itemWidth(copy.itemWidth), itemHeight(copy.itemHeight),
	focusRecord(copy.focusRecord), focusField(copy.focusField)
{
	// ZafWindow copy constructor will have duplicated the visible records in
	// the table, so we need to get rid of those and start over.
	ZafWindowObject *object;
	while (First())
	{
		object = First();
		ZafWindow::Subtract(object);
		delete object;
	}
	virtualRecord = DynamicPtrCast(copy.virtualRecord->Duplicate(), ZafTableRecord);
	virtualRecord->parent = this;

#if defined(ZAF_MOTIF)
	firstTimeDrawing = true;
	getChildPalette = false;
	sizing = true;
#endif
}

ZafTable::~ZafTable(void)
{
	// Delete the virtual record.
	if (virtualRecord)
		delete virtualRecord;
}

ZafWindowObject *ZafTable::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Defer to the extended Add() function.
	return (Add(object, ZAF_NULLP(ZafIChar), position));
}

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

	// Check for a virtual record field.
	ZafTableRecord *record = DynamicPtrCast(object, ZafTableRecord);
	if (!record)
	{
		// Add the header information.
		ZafTableHeader *header = ColumnTableHeader();
		if (header)
			header->Add(new ZafString(0, 0, 1, title ? title : object->Text(), -1));

		// Check for override options.
		if (grid)
			object->SetBordered(false);
		ZafButton *button = DynamicPtrCast(object, ZafButton);
		if (button)
			button->SetAutoSize(false);

		// Add the object to the virtual record.
		return (virtualRecord->Add(object, position));
	}

	// Add the object to the table.
	object->SetSystemObject(false);
	object->SetParentDrawFocus(true);
	object->SetParentPalette(true);

	bool saveAutomaticUpdate = automaticUpdate;
	automaticUpdate = false;
	ZafWindow::Add(object, position);

	// Create a row field for the new record.
	ZafTableHeader *header = RowTableHeader();
	ZafWindowObject *rowField = ZAF_NULLP(ZafWindowObject);
	if (header && header->VirtualField())
	{
		rowField = header->VirtualField()->Duplicate();
		rowField->SetAutomaticUpdate(false);
		rowField = header->Add(rowField);
	}
	automaticUpdate = saveAutomaticUpdate;

	// Reposition the record if the parent is registered.
	if (screenID)
	{
		// Determine the object's list position.
		int line;
		if (Grid())
			line = record->Previous() ? record->Previous()->zafRegion.bottom + 1 + focusSize : focusSize;
		else
			line = record->Previous() ? record->Previous()->zafRegion.bottom + focusSize : focusSize;
		ZafOffset newOffset = record->Previous() ? record->Previous()->Offset() + 1 : 0;
		for (ZafTableRecord *temp = record; temp; newOffset++, temp = temp->Next())
		{

			// Read the record information.
			if (temp->Offset() != newOffset)
			{
				SeekOptimized(newOffset);
				temp->SetOffset(currentOffset);
				bool saveAutomaticUpdate = temp->automaticUpdate;
				temp->automaticUpdate = false;
				ReadRecord(*temp, rowField);
				temp->automaticUpdate = saveAutomaticUpdate;
			}

			// Position the record.
			ComputeNextPosition(temp, rowField, line);
			temp->OSSize();
			if (rowField)
			{
				rowField->OSSize();
				rowField->SetAutomaticUpdate(true);
			}

			// Determine the next row-field.
			if (rowField)
				rowField = rowField->Next();
		}
	}

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

void ZafTable::ComputeNextPosition(ZafTableRecord *object, ZafWindowObject *rowField, int &line)
{
	// Set the object region.
	object->zafRegion.left = virtualRecord->zafRegion.left;
	object->zafRegion.right = virtualRecord->zafRegion.right;
	object->zafRegion.top = line;
	object->zafRegion.bottom = line = line + itemHeight - 1;

	if (Grid())
		line += 1 + focusSize;
	else
		line += focusSize;

	if (rowField)
	{
		rowField->zafRegion.left = 0;
		rowField->zafRegion.top = rowField->Previous() ? object->zafRegion.top + ZAF_HEADER_OBJECT_SHIFT : 0;
		rowField->zafRegion.right = rowField->parent->zafRegion.Width() - 1;
		rowField->zafRegion.bottom = object->zafRegion.bottom + 1 + ZAF_HEADER_OBJECT_SHIFT;
	}
}

void ZafTable::ComputePreviousPosition(ZafTableRecord *object, ZafWindowObject *rowField, int &line)
{
	// Set the object region.
	object->zafRegion.left = virtualRecord->zafRegion.left;
	object->zafRegion.right = virtualRecord->zafRegion.right;
	object->zafRegion.bottom = line;
	object->zafRegion.top = line = line - itemHeight + 1;

	if (Grid())
		line -= 1 + focusSize;
	else
		line -= focusSize;

	if (rowField)
	{
		rowField->zafRegion.left = 0;
		rowField->zafRegion.top = rowField->Previous() ? object->zafRegion.top + ZAF_HEADER_OBJECT_SHIFT : 0;
		rowField->zafRegion.right = rowField->parent->zafRegion.Width() - 1;
		rowField->zafRegion.bottom = object->zafRegion.bottom + 1 + ZAF_HEADER_OBJECT_SHIFT;
	}
}

void ZafTable::ComputeRecordPositions(ZafOffset newOffset)
{
	// Find the row information.
	ZafTableHeader *header = RowTableHeader();
	ZafWindowObject *rowField = header ? header->First() : ZAF_NULLP(ZafWindowObject);

	// Position the existing records.
	int line = focusSize;
	int bottom = clientRegion.bottom - clientRegion.top;
	ZafTableRecord *object;

#if defined(ZAF_TEXT_MODE)
	// Add -1 after itemHeight for last item drawn in text mode.
	for (object = First(); object && line + itemHeight - 1 <= bottom && newOffset <= maxOffset; object = object->Next())
#else
	for (object = First(); object && line + itemHeight <= bottom && newOffset <= maxOffset; object = object->Next())
#endif
	{
		// Position the record.
		ComputeNextPosition(object, rowField, line);
		if (screenID)
		{
			object->OSSize();
			if (rowField)
				rowField->OSSize();
		}

		// Check for the focus record.
		if (focusRecord == newOffset)
		{
			SetCurrent(object);
			if (focusField != -1)
				object->SetCurrent(object->Get(focusField));
		}

		// Read the record information.
		if (object->Offset() != newOffset)
		{
			SeekOptimized(newOffset);
			object->SetOffset(currentOffset);
			ReadRecord(*object, rowField);
		}
		newOffset++;

		// Determine the next row-field.
		if (rowField && line < bottom)
			rowField = rowField->Next();
	}

	// Remove excess records.
	ZafWindowObject *firstDeleteObject = ZAF_NULLP(ZafWindowObject);
	if (object)
	{
		firstDeleteObject = object->Previous();
		object = Last();
	}

#if !defined(ZAF_INDEPENDENT)
	ZafEventStruct redisplayEvent(S_REDISPLAY_REGION);
#endif
	if (header)
	{
		rowField = header->Last();
#if defined(ZAF_MOTIF)
		// if the last row is deleted the header region must be redisplayed.
		if (rowField)
			redisplayEvent.region = rowField->zafRegion;
		else
			redisplayEvent.region = header->zafRegion;
#endif
	}				
	while (object && object != firstDeleteObject)
	{
		// Remove the object and associated rowField.

		ZafTableRecord *previousObject = object->Previous();

		Subtract(object);
		delete object;

		object = previousObject;

		if (rowField)
		{
			ZafWindowObject *previousRowField = rowField->Previous();

			header->Subtract(rowField);
			delete rowField;

			rowField = previousRowField;
		}
	}
#if defined(ZAF_MOTIF)
	if (header)
		header->Event(redisplayEvent);
#endif

	// Add new records to fill the table.
	newOffset = Last() ? Last()->Offset() + 1 : 0;

#if defined(ZAF_TEXT_MODE)
	// Add -1 after itemHeight so last record is added in text mode.
	while (line + itemHeight - 1 <= bottom && newOffset <= maxOffset)
#else
	while (line + itemHeight <= bottom && newOffset <= maxOffset)
#endif
	{
		ZafTableRecord *object = DynamicPtrCast(virtualRecord->Duplicate(), ZafTableRecord);
		Add(object);

		// Check for the focus record.
		if (focusRecord == newOffset)
		{
			if (focusField != -1)
				object->Get(focusField)->SetFocus(true);
			else
				ZafWindowObject::SetFocus(true);
		}

		if (Grid())
			line = object->zafRegion.bottom + 1 + focusSize;
		else
			line = object->zafRegion.bottom + focusSize;
		newOffset++;

#if defined(ZAF_MSWINDOWS)
		redisplayEvent.region = object->zafRegion;
		Event(redisplayEvent);
		if (header)
		{
			rowField = header->Get(Index(object));
			if (rowField)
			{
				redisplayEvent.region = rowField->zafRegion;
				header->Event(redisplayEvent);
			}
		}
#endif
	}

	// Check the vertical scroll area.
	ScrollEvent(S_VSCROLL_COMPUTE);
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafTable::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	// Compute the actual draw region.
	// Don't use BeginDraw(), since the records would make nested calls to it.
	ZafRegionStruct drawRegion = ConvertToDrawRegion(this, &clientRegion);

#	if defined(ZAF_INDEPENDENT)
	ZafRegionStruct clip = event.region;
#	else
	ZafRegionStruct clip = drawRegion;
	if (ccode == S_REDISPLAY_REGION)
		clip = event.region;
#	endif

	// Draw the table records.
	ZafEventStruct childEvent = event;
	ZafWindowObject *object;
	ZafRegionStruct region = drawRegion;
	region.bottom = region.top - 1;
	for (object = First(); object; object = object->Next())
	{
		region = object->zafRegion;
		region.HzShift(drawRegion.left);
		region.VtShift(drawRegion.top);
		if (region.Overlap(clip))
		{
			// The event's region is relative to the object.
			childEvent.region = event.region;
			childEvent.region.HzShift(-object->zafRegion.left);
			childEvent.region.VtShift(-object->zafRegion.top);
			object->Draw(childEvent, ccode);
		}
	}

	// Clear any remaining blank area below the last record.
	drawRegion = BeginDraw();

#	if defined(ZAF_INDEPENDENT)
	display->SetClipRegion(clip);
#	endif

	region.top = region.bottom + 1;
#	if defined(ZAF_MACINTOSH)
	region.right = clip.right;
	region.bottom = clip.bottom;
#	else
	region.right = drawRegion.right;
	region.bottom = drawRegion.bottom;
#	endif

	if (region.Overlap(clip))
		DrawBackground(region, ccode);

	// Draw the grid.
#	if defined(ZAF_MACINTOSH)
	DrawGrid(clip, clip);
#	else
	DrawGrid(drawRegion, clip);
#	endif

	// Draw the region rectangle.
	object = Current();
	if (object && Focus())
	{
		drawRegion = ConvertToDrawRegion(object);
		DrawFocus(drawRegion, S_CURRENT);
#		if defined(ZAF_MOTIF)
		ZafTableRecord *tableRecord = DynamicPtrCast(object, ZafTableRecord);
		if (!grid && (!sizing || firstTimeDrawing) && 
		 tableRecord && tableRecord->Last()) 
		{
			int right = tableRecord->Last()->zafRegion.right;
			display->SetMode(ZAF_MODE_XOR);
			display->SetPalette(LogicalPalette(ZAF_PM_LIGHT_SHADOW, ZAF_PM_CURRENT));
			if (object != First())
				display->Line(drawRegion.left, drawRegion.top - 1, right, 
				 drawRegion.top - 1);
			if (object != Last())
				display->Line(drawRegion.left, drawRegion.bottom + 1, right, 
				 drawRegion.bottom + 1);
			display->SetMode(ZAF_MODE_COPY);
		}
		sizing = firstTimeDrawing = false;
#		endif
	}

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

ZafEventType ZafTable::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	// Clear the background area.
	int left = region.left;
	int right = left + clientRegion.right - clientRegion.left;
	if (!Grid())
	{
		display->SetMode(ZAF_MODE_XOR);
		display->SetPalette(LogicalPalette(ZAF_PM_LIGHT_SHADOW, ZAF_PM_CURRENT));
		display->Line(left, region.top - 1, right, region.top - 1);
		display->Line(left, region.bottom + 1, right, region.bottom + 1);
		display->SetMode(ZAF_MODE_COPY);
		return (ccode);
	}
	
	if (ccode != S_CURRENT)
	{
		display->SetPalette(LogicalPalette(ZAF_PM_DARK_SHADOW, ZAF_PM_NOT_CURRENT));
		display->Line(left, region.top - 1, right, region.top - 1);
		display->Line(left, region.bottom + 1, right, region.bottom + 1);
	}

	// Draw the focus area.
	if (ccode == S_CURRENT)
	{
		display->SetPalette(LogicalPalette(ZAF_PM_OUTLINE, ZAF_PM_CURRENT));
		display->Line(left, region.top - 1, right, region.top - 1);
		display->Line(left, region.bottom + 1, right, region.bottom + 1);
	}

	// Return success.
	return (ccode);
}

void ZafTable::DrawGrid(const ZafRegionStruct &drawRegion, const ZafRegionStruct &clip)
{
	// Check the grid color.
	if (grid)
		display->SetPalette(LogicalPalette(ZAF_PM_DARK_SHADOW, ZAF_PM_NOT_CURRENT));
	else
		return;

	// Draw the grid.
	for (ZafWindowObject *object = virtualRecord->First(); object && object->Next(); object = object->Next())
	{
		int column = drawRegion.left + object->zafRegion.right + 1;
		if (object->zafRegion.Height() > Display()->cellHeight)
		{
			display->Line(column, drawRegion.top + object->zafRegion.top,
		 	column, drawRegion.top + object->zafRegion.bottom - object->zafRegion.top);
		}
		else
			display->Line(column, drawRegion.top, column, drawRegion.bottom);
	}

#if defined(ZAF_TEXT_MODE)
	for (int line = drawRegion.top; line < drawRegion.bottom; line += itemHeight + focusSize)
#else
	for (int line = drawRegion.top; line <= drawRegion.bottom; line += itemHeight + focusSize)
#endif
	{
		if (line >= clip.top && line <= clip.bottom)
			display->Line(drawRegion.left, line, drawRegion.right, line);
	}
}
#endif

ZafEventType ZafTable::Event(const ZafEventStruct &event)
{
	// Check for zinc events.
	if (editMode && event.InputType() == E_MOUSE)
		return (ZafWindowObject::Event(event));
	ZafEventType ccode = LogicalEvent(event);
	ZafTableRecord *object = Current();
	switch (ccode)
	{
	case S_INITIALIZE:
		ccode = ZafWindow::Event(event);

#if !defined(ZAF_TEXT_MODE)
		// Determine the record sizes.
		if (virtualRecord && virtualRecord->zafRegion.coordinateType != ZAF_PIXEL)
		{
			virtualRecord->ZafWindowObject::ConvertRegion(virtualRecord->zafRegion, ZAF_PIXEL);
			if (virtualRecord->CoordinateType() == ZAF_CELL)
				virtualRecord->zafRegion.bottom -= 2 * ZAF_SHADOW_SIZE;
		}
#endif
		virtualRecord->Event(event);
		break;

	// ----- Position & Size messages -----
	case N_SIZE:
		virtualRecord->zafRegion.right = clientRegion.Width() - 1;

		// Compute the record positions.
		itemWidth = clientRegion.Width();
		itemHeight = virtualRecord->zafRegion.Height();
		ComputeRecordPositions(First() ? First()->Offset() : 0);

		// Check the horizontal scroll area.
		ScrollEvent(S_HSCROLL_COMPUTE);
#		if defined(ZAF_MOTIF)
sizing = true;
		// Configure the table grid area.
		XtVaSetValues(screenID, XmNwidth, clientRegion.Width(), XmNheight, clientRegion.Height(), 0);
#		endif
		break;

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

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

				// Reset the focus.
				object->SetFocus(true);
				if (!object->Focus() && !noncurrent)
				{
					// Clean up from erroneous write.
					if (current && !Current()->Changed())
					{
						Current()->SetChanged(true);
						ZafTableHeader *header = RowTableHeader();
						// Get a pointer to the object in the row header if there is one.
						ZafWindowObject *rowField = header ? header->First() : ZAF_NULLP(ZafWindowObject);
						WriteRecord(*Current(), rowField);
					}
					return (ccode);
				}

#				if defined(ZAF_MACINTOSH)
				// Use position coordinates of the object.
				ZafEventStruct mEvent = event;
				mEvent.position.line -= object->zafRegion.top;
				mEvent.position.column -= object->zafRegion.left;
				object->Event(mEvent);
#				else
				object->Event(event);
#				endif
				break;
			}

		// Check for the current focus.
		if (!Focus())
			SetFocus(true);
		break;

	case L_CONTINUE_SELECT:
	case L_END_SELECT:
		// Check for a current item.
		if (EditMode())
			return (ZafWindowObject::Event(event));
		else if (Current())
			ccode = Current()->Event(event);
		break;

	// ----- Movement messages -----
	case L_LEFT:
	case L_RIGHT:
	case L_BOL:
	case L_EOL:
	case L_UP:
	case L_DOWN:
	case L_FIRST:
	case L_LAST:
	case L_PGDN:
	case L_PGUP:
	case L_PREVIOUS:
	case L_SELECT:
	{
		ZafEventType tempCcode;
		if (object)
			tempCcode = object->Event(event);
		if (!object || tempCcode == S_ERROR || tempCcode == S_UNKNOWN)
			ccode = MoveEvent(event);
	}
		break;

	case L_NEXT:
		// Check for movement off the table.
		if (event.key.shiftState & S_CTRL)
		{
			// Move to the next object.
			for (ZafWindowObject *focusObject = Next(); focusObject && focusObject != this; focusObject = focusObject->Next())
				if (!focusObject->Disabled() && !focusObject->Noncurrent())
				{
					focusObject->SetFocus(true);
					return (ccode);
				}
				else if (focusObject->Next())
					focusObject = focusObject->Next();
				else
					focusObject = DynamicPtrCast(parent, ZafWindow)->First();
		}
		else if (!object || object->Event(event) == S_ERROR)
			ccode = MoveEvent(event);
		break;

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

	case S_REDISPLAY:
	case S_REDISPLAY_DATA:
	case S_REDISPLAY_REGION:
		ccode = ZafWindowObject::Event(event);
		if (ccode == S_REDISPLAY_DATA)
		{
			ZafTableHeader *header = RowTableHeader();
			if (header)
				header->Event(event);
		}
		break;

	case S_DEINITIALIZE:
		{
		ccode = ZafWindow::Event(event);
		virtualRecord->Event(event);

		// Get a pointer to the row header if there is one.
		ZafTableHeader *header = RowTableHeader();
		// Get a pointer to the object in the row header if there is one.
		ZafWindowObject *rowField = header ? header->First() : ZAF_NULLP(ZafWindowObject);

		// 'object' is the table record.
		for (object = First(); object; object = object->Next())
		{
			// Write out any changed records.
			SeekOptimized(object->Offset());
			WriteRecord(*object, rowField);
			if (rowField)
				rowField = rowField->Next();
		}
		}
		break;

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

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

bool ZafTable::IsA(ZafClassID compareID) const
{
	return ((compareID == classID) ? true : ZafWindow::IsA(compareID));
}

bool ZafTable::IsA(ZafClassName compareName) const
{
	return (!strcmp(compareName, className) ? true : ZafWindow::IsA(compareName));
}

ZafLogicalEvent ZafTable::LogicalEvent(const ZafEventStruct &event)
{
	ZafLogicalEvent ccode = MapEvent(defaultEventMap, event);
	return ((ccode == L_NONE) ? ZafWindow::LogicalEvent(event) : ccode);
}

const ZafPaletteStruct *ZafTable::MapClassPalette(ZafPaletteType type, ZafPaletteState state)
{
	const ZafPaletteStruct *palette = MapPalette(defaultPaletteMap, type, state);
	return (palette ? palette : ZafWindow::MapClassPalette(type, state));
}

ZafEventType ZafTable::MoveEvent(const ZafEventStruct &event)
{
	// If no records, can't move, so return an error.
	if (!first)
		return (S_ERROR);

	// Determine the new selection.
	ZafEventStruct sEvent(S_VSCROLL);
	ZafTableRecord *object = Current();
	ZafTableRecord *pObject = object ? object->Previous() : ZAF_NULLP(ZafTableRecord);
	ZafTableRecord *nObject = object ? object->Next() : ZAF_NULLP(ZafTableRecord);
	int oldFieldIndex = (Current() && Current()->Current()) ? Current()->Current()->ListIndex() : -1;
	int fieldIndex = oldFieldIndex;

	ZafEventType ccode = LogicalEvent(event);
	switch (ccode)
	{
	case L_PREVIOUS:
		if (pObject)
			object = pObject;
		else if (!object)
		{
			// The current record is actually off the screen.
			if (focusField > 0)
				fieldIndex = focusField - 1;
			else
				fieldIndex = Last()->Last()->ListIndex();
			sEvent.scroll.delta = focusRecord - First()->Offset();
			ScrollEvent(sEvent);
			for (object = First(); object; object = object->Next())
				if (object->Offset() == focusRecord)
					break;
			break;
		}
		else if (object->Offset())
		{
			object->SetFocus(false);
			sEvent.scroll.delta = -1;
			ScrollEvent(sEvent);
			object = First();
		}
		else
			object = ZAF_NULLP(ZafTableRecord);
		if (object && object->Last())
			fieldIndex = object->Last()->ListIndex();
		break;

	case L_NEXT:
		if (nObject)
			object = nObject;
		else if (!object)
		{
			// The current record is actually off the screen.
			if (focusField < Last()->Last()->ListIndex())
				fieldIndex = focusField + 1;
			else
				fieldIndex = 0;
			sEvent.scroll.delta = focusRecord - Last()->Offset();
			ScrollEvent(sEvent);
			for (object = First(); object; object = object->Next())
				if (object->Offset() == focusRecord)
					break;
			break;
		}
		else if (object->Offset() < maxOffset)
		{
			object->SetFocus(false);
			sEvent.scroll.delta = 1;
			ScrollEvent(sEvent);
			object = Last();
		}
		else
			object = ZAF_NULLP(ZafTableRecord);
		if (object && object->First())
			fieldIndex = 0;
		break;

	case L_UP:
		if (object && object->Current())
			fieldIndex = object->Current()->ListIndex();
		if (pObject)
			object = pObject;
		else if (!object)
		{
			// The current record is actually off the screen.
			if (focusRecord > 0)
				focusRecord--;
			fieldIndex = focusField;
			sEvent.scroll.delta = focusRecord - First()->Offset();
			ScrollEvent(sEvent);
			for (object = First(); object; object = object->Next())
				if (object->Offset() == focusRecord)
					break;
		}
		else if (object->Offset())
		{
			object->SetFocus(false);
			sEvent.scroll.delta = -1;
			ScrollEvent(sEvent);
			object = First();
		}
		break;

	case L_DOWN:
		if (object && object->Current())
			fieldIndex = object->Current()->ListIndex();
		if (nObject)
			object = nObject;
		else if (!object)
		{
			// The current record is actually off the screen.
			fieldIndex = focusField;
			if (focusRecord < maxOffset)
				focusRecord++;
			sEvent.scroll.delta = focusRecord - Last()->Offset();
			ScrollEvent(sEvent);
			for (object = First(); object; object = object->Next())
				if (object->Offset() == focusRecord)
					break;
		}
		else if (object->Offset() < maxOffset)
		{
			object->SetFocus(false);
			sEvent.scroll.delta = 1;
			ScrollEvent(sEvent);
			object = Last();
		}
		break;

	case L_PGUP:
		// Check for page actions.
		if (!object)
		{
			// The current record is actually off the screen.
			focusRecord -= Count() - 1;
			if (focusRecord < 0)
				focusRecord = 0;
			fieldIndex = focusField;
			sEvent.scroll.delta = focusRecord - First()->Offset();
			ScrollEvent(sEvent);
		}
		if (object == First() && object->Offset() > 0)
		{
			// Go to the top of the previous page.
			object->SetFocus(false);
			sEvent.scroll.delta = ZafMin(1 - Count(), -1);
			if (-sEvent.scroll.delta > First()->Offset())
				sEvent.scroll.delta = -First()->Offset();
			ScrollEvent(sEvent);
		}
		object = First();
		break;

	case L_PGDN:
		// Check for page actions.
		if (!object)
		{
			// The current record is actually off the screen.
			focusRecord += Count() - 1;
			if (focusRecord > maxOffset)
				focusRecord = maxOffset;
			fieldIndex = focusField;
			sEvent.scroll.delta = focusRecord - Last()->Offset();
			ScrollEvent(sEvent);
		}
		if (object == Last() && object->Offset() < maxOffset)
		{
			// Go to the bottom of the next page.
			object->SetFocus(false);
			sEvent.scroll.delta = ZafMax(Count() - 1, 1);
			if (sEvent.scroll.delta > maxOffset - Last()->Offset())
				sEvent.scroll.delta = maxOffset - Last()->Offset();
			ScrollEvent(sEvent);
		}
		object = Last();
		break;

	case L_FIRST:
		if (First()->Offset() != 0)
		{
			sEvent.scroll.delta = -First()->Offset();
			ScrollEvent(sEvent);
		}
		object = First();
		break;

	case L_LAST:
		if (Last()->Offset() != maxOffset)
		{
			sEvent.scroll.delta = maxOffset - Last()->Offset();
			ScrollEvent(sEvent);
		}
		object = Last();
		break;
	}

	// Check the current object.
	if (object && (object != current || oldFieldIndex != fieldIndex))
	{
		// Reset the object focus.
		ZafWindowObject *field = (fieldIndex != -1) ? object->Get(fieldIndex) : ZAF_NULLP(ZafWindowObject);
		if (field)
		{
			field->SetFocus(true);
/* START BLOCK COMMENT
**				if (!field->Focus())
**					object->SetFocus(true);
END BLOCK COMMENT */
		}
		else
			object->SetFocus(true);

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

	return (ccode);
}

ZafWindowObject *ZafTable::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 && focusObject)
	{
		focusRecord = Current()->Offset();
		ZafRegionStruct drawRegion = BeginDraw();

		// Turn off the old item focus.
		if (object && object != focusObject)
		{
			drawRegion = ConvertToDrawRegion(object);
			DrawFocus(drawRegion, S_NON_CURRENT);
		}

		// Turn on the new item focus and update the focus information.
		drawRegion = ConvertToDrawRegion(focusObject);
		DrawFocus(drawRegion, setFocus ? S_CURRENT : S_NON_CURRENT);

		EndDraw();
	}

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

ZafEventType ZafTable::ScrollEvent(const ZafEventStruct &event)
{
	// Process the scrolling request.
	ZafEventType ccode = event.type;
	ZafTableRecord *record;
	ZafWindowObject *field;
	switch (ccode)
	{
	case S_HSCROLL_CHECK:
		// Check for a valid field.
		field = Current() ? Current()->Current() : ZAF_NULLP(ZafWindowObject);
		if (field && field->zafRegion.left != 0)
		{
			// Determine the scrolling values.
			int delta = 0;
			if (field->zafRegion.left < 0)
			{
				for ( ; field->zafRegion.left != 0; field = field->Next())
					delta--;
			}
			else if (field->zafRegion.right > clientRegion.right)
			{
				ZafWindowObject *temp = virtualRecord->First();
				while (temp && temp->zafRegion.left != 0)
					temp = temp->Next();
				int offset = field->zafRegion.right - clientRegion.right;
				while (temp && temp != field && offset > 0)
				{
					delta++;
					offset -= temp->zafRegion.Width();
					temp = temp->Next();
				}
			}

			// Scroll the table record.
			if (delta)
			{
				ZafEventStruct sEvent(S_HSCROLL);
				sEvent.scroll.delta = delta;
				ScrollEvent(sEvent);
			}
		}
		break;

	case S_VSCROLL_CHECK:
		break;

	case S_HSCROLL:
	case N_HSCROLL:
		if (event.scroll.delta && virtualRecord)
		{
			// Compute the offset.
			int delta = (int)event.scroll.delta;
			int offset = 0;
			for (field = virtualRecord->First(); field; field = field->Next())
				if (field->zafRegion.left == 0)
				{
					ZafWindowObject *temp = field;
					for ( ; delta > 0 && temp->Next(); temp = temp->Next())
						delta--;
					for ( ; delta < 0 && temp->Previous(); temp = temp->Previous())
						delta++;
					offset = -temp->zafRegion.left;
					break;
				}

			// Re-calculate the virtual record regions.
			for (field = virtualRecord->First(); field; field = field->Next())
			{
				field->zafRegion.left += offset;
				field->zafRegion.right += offset;
				field->SetRegion(field->zafRegion);
			}

			// Now, update the table header and records.
			ZafTableHeader *header = ColumnTableHeader();
			if (header)
			{
				header->SetAutomaticUpdate(false);
				header->Event(N_SIZE);
				header->SetAutomaticUpdate(true);
			}

			automaticUpdate = false;
			for (ZafWindowObject *object = First(); object; object = object->Next())
				object->Event(N_SIZE);
			ScrollEvent(S_HSCROLL_COMPUTE);
			automaticUpdate = true;
#if defined(ZAF_INDEPENDENT)
			OSInvalidateRegion(&clientRegion);
			ZafScrollBar *scrollBar = HorizontalScrollBar();
			if (scrollBar)
				scrollBar->Redisplay();
#else
			Redisplay();
#endif
		}
		break;

	case S_VSCROLL:
	case N_VSCROLL:
		if (event.scroll.delta && First())
		{
			ZafTableHeader *header = RowTableHeader();
			ZafWindowObject *rowField = ZAF_NULLP(ZafWindowObject);
			ZafTableRecord *oldCurrent = record = Current();

			// Remove the focus rectangle.
			if (record)
			{
				ZafRegionStruct region = record->BeginDraw();
				DrawFocus(region, S_NON_CURRENT);
				record->EndDraw();

				ZafWindowObject *focusObject = record->Current();
				if (focusObject && focusObject->screenID)
					record->DeactivateObject(focusObject);
			}

			// Shift the records to optimize reads and writes.
			ZafOffset delta = event.scroll.delta;
			if (-delta > First()->Offset())
				delta = -First()->Offset();
			if (delta > maxOffset - Last()->Offset())
				delta = maxOffset - Last()->Offset();
			ZafOffset newOffset = First()->Offset() + delta;

			ZafScrollBar *scrollBar = VerticalScrollBar();
			// Redraw client region
			if (scrollBar && (scrollBar->ScrollData()->Showing() == delta ||
				scrollBar->ScrollData()->Showing() == -delta))
			{
				ZafEventStruct tEvent;
				tEvent.type = N_NON_CURRENT;
				tEvent.windowObject = windowManager->focusObject;
				ZafEventType tCode = ZAF_ERROR_NONE;
				if (record && record->Current())
					tCode = record->Current()->Event(tEvent);
				if (tCode != ZAF_ERROR_NONE && tCode != ZAF_ERROR_LEAVE_INVALID)
				{
					// Clean up from erroneous write.
					if (!record->Changed())
					{
						record->SetChanged(true);
						ZafTableHeader *header = RowTableHeader();
						// Get a pointer to the object in the row header if there is one.
						ZafWindowObject *rowField = header ? header->First() : ZAF_NULLP(ZafWindowObject);
						WriteRecord(*record, rowField);
					}

					tEvent = event;
					tEvent.type = S_VSCROLL_SET;
					tEvent.scroll.delta = scrollBar->ScrollData()->Delta();
					scrollBar->Event(tEvent);
					return (tCode);
				}

				if (header)
					rowField = header->First();
				for (record = First(); record; record = record->Next())
				{
					WriteRecord(*record, rowField);
					if (rowField)
						rowField = rowField->Next();
				}

				Redisplay();
				if (header)
					header->Redisplay();
			}
			// Shift records up
			else if (delta > 0)
			{
				while (delta-- > 0)
				{
					record = First();
					if (header)
						rowField = header->First();

					if (record == Current())
					{
						ZafEventStruct tEvent;
						tEvent.type = N_NON_CURRENT;
						tEvent.windowObject = windowManager->focusObject;
						ZafEventType tCode = ZAF_ERROR_NONE;
						if (record && record->Current())
							tCode = record->Current()->Event(tEvent);
						if (tCode != ZAF_ERROR_NONE && tCode != ZAF_ERROR_LEAVE_INVALID)
						{
							// Clean up from erroneous write.
							if (!record->Changed())
							{
								record->SetChanged(true);
								ZafTableHeader *header = RowTableHeader();
								// Get a pointer to the object in the row header if 
								// there is one.
								ZafWindowObject *rowField = header ? header->First() :
									ZAF_NULLP(ZafWindowObject);
								WriteRecord(*record, rowField);
							}
							tEvent = event;
							tEvent.scroll.current += tEvent.scroll.delta - delta - 1;
							tEvent.type = S_VSCROLL_SET;
							tEvent.scroll.delta = scrollBar->ScrollData()->Delta();
							scrollBar->Event(tEvent);
							return (tCode);
						}
					}

					// Save record being scrolled off
					WriteRecord(*record, rowField);
					ZafList::Subtract(record);
					ZafList::Add(record);
					if (rowField)
					{
						header->ZafList::Subtract(rowField);
						header->ZafList::Add(rowField);
					}
				}
			}

			// Shift records down
			else if (delta < 0)
			{
				while (delta++ < 0)
				{
					record = Last();
					if (header)
						rowField = header->Last();

					if (record == Current())
					{
						ZafEventStruct tEvent;
						tEvent.type = N_NON_CURRENT;
						tEvent.windowObject = windowManager->focusObject;
						ZafEventType tCode = ZAF_ERROR_NONE;
						if (record && record->Current())
							tCode = record->Current()->Event(tEvent);
						if (tCode != ZAF_ERROR_NONE && tCode != ZAF_ERROR_LEAVE_INVALID)
						{
							// Clean up from erroneous write.
							if (!record->Changed())
							{
								record->SetChanged(true);
								ZafTableHeader *header = RowTableHeader();
								// Get a pointer to the object in the row header if 
								// there is one.
								ZafWindowObject *rowField = header ? header->First() :
									ZAF_NULLP(ZafWindowObject);
								WriteRecord(*record, rowField);
							}
							tEvent = event;
							tEvent.scroll.current += tEvent.scroll.delta - delta + 1;
							tEvent.type = S_VSCROLL_SET;
							tEvent.scroll.delta = scrollBar->ScrollData()->Delta();
							scrollBar->Event(tEvent);
							return (tCode);
						}
					}

					// Save record being scrolled off
					WriteRecord(*record, rowField);
					ZafList::Subtract(record);
					ZafList::Add(record, First());
					if (rowField)
					{
						header->ZafList::Subtract(rowField);
						header->ZafList::Add(rowField, header->First());
					}
				}
			}
			else if (delta && record)
				SetCurrent(ZAF_NULLP(ZafWindowObject));

			// Check the focus rectangle. Notify oldCurrent that it no
			// longer has focus. This must be done here since its data may get
			// changed in ComputeRecordPosition(), called below.
			if (!Current() && oldCurrent)
				oldCurrent->SetFocus(false);

			// Recalculate the field regions, read new record data into
			// records that were wrapped and set new current, if there is one.
#if !defined(ZAF_INDEPENDENT)
			automaticUpdate = false;
#endif
			ComputeRecordPositions(newOffset);
#if !defined(ZAF_INDEPENDENT)
			automaticUpdate = true;
#endif

			// Get pointer to "new" current record.
			record = Current();

			// Re-add the focus rectangle.
			if (record)
			{
				if (!oldCurrent)
					record->SetFocus(true);
				else
				{
					ZafRegionStruct region = record->BeginDraw();
					DrawFocus(region, S_CURRENT);
					record->EndDraw();
				}
			}

			// Check for existing repaint directives.
#			if defined(ZAF_MOTIF)
			ZafEventStruct check;
			if (screenID && Visible() && XtIsRealized(screenID) && XtWindow(screenID) &&
				(eventManager->Get(check, Q_NO_BLOCK | Q_NO_DESTROY | Q_NO_POLL | Q_END) != 0 || check.route != this))
			{
				check.route = this;
				check.type = S_REDISPLAY_DATA;
				eventManager->Put(check);
			}
#			elif defined(ZAF_INDEPENDENT)
/* START BLOCK COMMENT
**				// Only invalidate the items that need it.
**				OSInvalidateRegion(&clientRegion, true, false);
**				ZafTableHeader *rowHeader = RowTableHeader();
**				if (rowHeader)
**					rowHeader->Redisplay();
END BLOCK COMMENT */
#			else
			RedisplayData();
#			endif
		}
		break;

	case S_HSCROLL_COMPUTE:
		{
		// Check for a scroll-bar.
		ZafScrollBar *scrollBar = HorizontalScrollBar();

		if (scrollBar && virtualRecord && virtualRecord->First())
		{
			// Reset the scroll values.
			ZafScrollStruct hScroll;
			hScroll.delta = 1;
			hScroll.current = 0;
			hScroll.showing = 1;
			for (field = virtualRecord->First(); field; field = field->Next())
				if (field->zafRegion.left < 0)
					hScroll.current++;
			hScroll.minimum = 0;
			if ((virtualRecord->Last()->zafRegion.right > zafRegion.right - HeaderWidth()) ||
			 (virtualRecord->First()->zafRegion.left < zafRegion.left))
				hScroll.maximum = virtualRecord->Count() - hScroll.showing;
			else
				hScroll.maximum = 0;

			// Adjust the vertical scroll-bar values.
			scrollBar->ScrollData()->SetScroll(hScroll);
		}
		}
		break;

	case S_VSCROLL_COMPUTE:
		{
		// Check for a scroll-bar.
		ZafScrollBar *scrollBar = VerticalScrollBar();
		if (scrollBar && First())
		{
			// Reset the scroll values.
			ZafScrollStruct vScroll;
			vScroll.delta = 1;
			vScroll.showing = Count();
			vScroll.minimum = 0;
			vScroll.maximum = maxOffset - vScroll.showing + 1;
			vScroll.current = First()->Offset();

			// Adjust the vertical scroll-bar values.
			scrollBar->ScrollData()->SetScroll(vScroll);
		}
		else if(scrollBar && !First())
		{
			// Reset the scroll values.
			ZafScrollStruct vScroll;
			vScroll.delta = 0;
			vScroll.showing = 0;
			vScroll.minimum = 0;
			vScroll.maximum = 0;
			vScroll.current = 0;

			// Adjust the vertical scroll-bar values.
			scrollBar->ScrollData()->SetScroll(vScroll);
		}
		}
		break;

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

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

ZafError ZafTable::Flush(ZafOffset offset)
{
	// Find the row information.
	ZafError tError = ZAF_ERROR_NONE;
	ZafTableHeader *header = RowTableHeader();
	ZafWindowObject *rowField = header ? header->First() : ZAF_NULLP(ZafWindowObject);

	for (ZafTableRecord *record = First(); record; record = record->Next())
		if (offset == -1 || offset == record->Offset())
		{
			tError = SeekOptimized(record->Offset());
			if (!tError)
				tError = WriteRecord(*record, rowField);

			if (rowField)
				rowField = rowField->Next();
		}

	return (tError);
}

ZafLogicalColor ZafTable::HeaderBackgroundColor(ZafLogicalColor *color, ZafLogicalColor *mono)
{
	// Get the background color from the header objects.
	ZafLogicalColor tColor = ZAF_CLR_NULL;
	ZafWindowObject *object = SupportFirst();
	if (object)
	{
		object->BackgroundColor(&tColor, mono);
		if (color)
			*color = tColor;
	}

	// Return the current background color.
	return (tColor);
}

ZafLogicalColor ZafTable::HeaderTextColor(ZafLogicalColor *color, ZafLogicalColor *mono)
{
	// Get the text color from the header objects.
	ZafLogicalColor tColor = ZAF_CLR_NULL;
	ZafWindowObject *object = SupportFirst();
	if (object)
	{
		object->TextColor(&tColor, mono);
		if (color)
			*color = tColor;
	}

	// Return the current text color.
	return (tColor);
}

int ZafTable::HeaderHeight(void) const
{
	const ZafTableHeader *header = ColumnTableHeader();
	if (header)
		return (header->zafRegion.Height());

	return (0);
}

int ZafTable::HeaderWidth(void) const
{
	ZafTableHeader *header = RowTableHeader();
	if (header)
		return (header->zafRegion.Width());

	return (0);
}

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

	// Make sure the header also does the appropriate notification.
	ZafTableHeader *rowHeader = RowTableHeader();
	if (rowHeader)
	{
		ZafWindowObject *rowObj = rowHeader->Get(Index(object));
		rowHeader->NotifySelection(rowObj, setSelected);
	}

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

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

ZafTableRecord *ZafTable::Record(ZafOffset offset)
{
	if (maxOffset > -1 && offset >= First()->Offset() &&
	 (offset <= Last()->Offset() || Last()->Offset() == -1))
	{
		for (ZafTableRecord *record = First(); record; record = record->Next())
			if (record->Offset() == offset)
				return (record);
	}

	return (ZAF_NULLP(ZafTableRecord));
}

ZafError ZafTable::Repopulate(ZafOffset offset)
{
	ZafTableHeader *header = RowTableHeader();

	if (offset == -1)
	{
		ZafOffset repopOffset = first ? First()->Offset() : 0;

		// Setting record offsets to -1 will cause ComputeRecordPositions to
		// read the records' data.
		for (ZafTableRecord *record = First(); record; record = record->Next())
			record->SetOffset(-1);
		ComputeRecordPositions(repopOffset);
	}
	else
	{
		// Find the row information.
		ZafWindowObject *rowField = header ? header->First() : ZAF_NULLP(ZafWindowObject);

		for (ZafTableRecord *record = First(); record; record = record->Next())
		{
			if (offset == record->Offset())
			{
				ZafError tError;
				tError = SeekOptimized(offset);
				if (!tError)
					tError = ReadRecord(*record, rowField);
				if (tError)
					return (tError);
				break;
			}

			if (rowField)
				rowField = rowField->Next();
		}
	}

	return (ZAF_ERROR_NONE);
}

int ZafTable::RowHeight(void) const
{
	// Return the current value.
	return (virtualRecord->zafRegion.Height());
}

ZafOffset ZafTable::SetBottomOffset(ZafOffset bottomOffset)
{
	if (bottomOffset <= maxOffset)
	{
		ZafEventStruct scrollEvent(S_VSCROLL);
		scrollEvent.scroll.delta = bottomOffset - Last()->Offset();
		Event(scrollEvent);
	}

	return BottomOffset();
}

ZafCoordinateType ZafTable::SetCoordinateType(ZafCoordinateType newCoordinateType)
{
	// Make sure the attribute has changed.
	if (coordinateType != newCoordinateType && !screenID)
	{
		ZafWindowObject::SetCoordinateType(newCoordinateType);
		for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
			object->SetCoordinateType(coordinateType);
		virtualRecord->SetCoordinateType(coordinateType);
	}

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

ZafOffset ZafTable::SetFocusOffset(ZafOffset focusOffset)
{
	// Check if within valid range and if we even have any records.
	if (focusOffset >= 0 && focusOffset <= maxOffset && first)
	{
		if (focusOffset < First()->Offset())
		{
			ZafEventStruct scrollEvent(S_VSCROLL);
			scrollEvent.scroll.delta = focusOffset - First()->Offset();
			Event(scrollEvent);
		}
		else if (focusOffset > Last()->Offset())
		{
			ZafEventStruct scrollEvent(S_VSCROLL);
			scrollEvent.scroll.delta = focusOffset - Last()->Offset();
			Event(scrollEvent);
		}

		for (ZafTableRecord *record = First(); record; record = record->Next())
			if (record->Offset() == focusOffset)
			{
				// Reset the focus.
				ZafWindowObject *field = (focusField != -1) ? record->Get(focusField) : ZAF_NULLP(ZafWindowObject);
				if (field && !field->Disabled() && !field->Noncurrent())
					field->SetFocus(true);
				else
					record->SetFocus(true);
				break;
			}
	}

	return FocusOffset();
}

ZafLogicalColor ZafTable::SetHeaderBackgroundColor(ZafLogicalColor color, ZafLogicalColor mono)
{
	// Set the background color for the header objects.
	for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
		object->SetBackgroundColor(color, mono);
	return (color);
}

ZafLogicalColor ZafTable::SetHeaderTextColor(ZafLogicalColor color, ZafLogicalColor mono)
{
	// Set the text color for the header objects.
	for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
		object->SetTextColor(color, mono);
	return (color);
}

int ZafTable::SetHeaderHeight(int headerHeight)
{
	// Make sure the request is valid.
	if (!screenID)
	{
		ZafTableHeader *header = ColumnTableHeader();
		if (header)
			header->zafRegion.bottom = header->zafRegion.top + headerHeight - 1;
		header = CornerTableHeader();
		if (header)
			header->zafRegion.bottom = header->zafRegion.top + headerHeight - 1;
	}

	// Return the current value.
	return (headerHeight);
}

int ZafTable::SetHeaderWidth(int headerWidth)
{
	// Make sure the request is valid.
	if (!screenID)
	{
		ZafTableHeader *header = RowTableHeader();
		if (header)
			header->zafRegion.right = header->zafRegion.left + headerWidth - 1;
		header = CornerTableHeader();
		if (header)
			header->zafRegion.right = header->zafRegion.left + headerWidth - 1;
	}

	// Return the current value.
	return (headerWidth);
}

int ZafTable::SetRowHeight(int rowHeight, ZafCoordinateType newCoordinate)
{
	// Make sure the request is valid.
	if (!screenID)
	{
		virtualRecord->coordinateType = newCoordinate;
		virtualRecord->zafRegion.ConvertCoordinates(newCoordinate);
		virtualRecord->zafRegion.bottom = virtualRecord->zafRegion.top + rowHeight - 1;
	}

	// Return the current value.
	return (rowHeight);
}

ZafOffset ZafTable::SetTopOffset(ZafOffset topOffset)
{
	if (topOffset <= maxOffset)
	{
		ZafEventStruct scrollEvent(S_VSCROLL);
		scrollEvent.scroll.delta = topOffset - First()->Offset();
		Event(scrollEvent);
	}

	return TopOffset();
}

bool ZafTable::SetAcceptDrop(bool )
{
	// acceptDrop is false for this class.
	acceptDrop = false;
	return (acceptDrop);
}

ZafOffset ZafTable::SetCurrentOffset(ZafOffset newCurrentOffset)
{
	// If the maxOffset hasn't been set then there are no records.
	if (maxOffset == -1)
		return (maxOffset);

	// Make sure the setting has changed.
	if (!screenID)
		currentOffset = newCurrentOffset;
	else if (First()->Offset() > newCurrentOffset || Last()->Offset() < newCurrentOffset)
	{
		// Object is off the screen.
		ZafOffset newOffset = currentOffset;
		if (newOffset + Count() >= maxOffset)
			newOffset = maxOffset - Count() + 1;
		focusRecord = newCurrentOffset;
		ComputeRecordPositions(newOffset);
	}
	else
	{
		for (ZafTableRecord *record = First(); record; record = record->Next())
			if (record->Offset() == newCurrentOffset)
			{
				record->SetFocus(true);
				break;
			}
	}

	// Return the new current offset.
	return (currentOffset);
}

bool ZafTable::SetDestroyable(bool )
{
	// destroyable is false for this class.
	destroyable = false;
	return (destroyable);
}

bool ZafTable::SetGrid(bool setGrid)
{
	// Check the attribute.
	if (grid != setGrid && !screenID)
		grid = setGrid;

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

bool ZafTable::SetLocked(bool )
{
	// locked is false for this class.
	locked = false;
	return (locked);
}

ZafOffset ZafTable::SetMaxOffset(ZafOffset tMaxOffset)
{
	maxOffset = tMaxOffset;
	return (maxOffset);
}

bool ZafTable::SetMaximized(bool )
{
	// maximized is false for this class.
	maximized = false;
	return (maximized);
}

bool ZafTable::SetMinimized(bool )
{
	// minimized is false for this class.
	minimized = false;
	return (minimized);
}

bool ZafTable::SetModal(bool )
{
	// modal is false for this class.
	modal = false;
	return (modal);
}

bool ZafTable::SetMoveable(bool )
{
	// moveable is false for this class.
	moveable = false;
	return (moveable);
}

bool ZafTable::SetNormalHotKeys(bool )
{
	// normalHotKeys is false for this class.
	normalHotKeys = false;
	return (normalHotKeys);
}

ZafWindow *ZafTable::SetOwner(ZafWindow *)
{
	// owner is null for this class.
	owner = ZAF_NULLP(ZafWindow);
	return (owner);
}

bool ZafTable::SetSizeable(bool )
{
	// sizeable is false for this class.
	sizeable = false;
	return (sizeable);
}

bool ZafTable::SetTemporary(bool )
{
	// temporary is false for this class.
	temporary = false;
	return (temporary);
}

ZafTableRecord *ZafTable::SetVirtualRecord(ZafTableRecord *newVirtualRecord)
{
	// Make sure this is a valid request.
	if (!screenID && virtualRecord != newVirtualRecord)
	{
		if (virtualRecord)
			delete virtualRecord;
		virtualRecord = newVirtualRecord;

		// Update the record fields.
		ZafTableHeader *header = ColumnTableHeader();
		if (header)
			header->Destroy();
		for (ZafWindowObject *object = virtualRecord->First(); object; object = object->Next())
		{
			if (grid)
				object->SetBordered(false);
			if (header)
				header->Add(new ZafString(0, 0, 1, ZAF_NULLP(ZafIChar), -1));
		}
	}

	// Reset the virtual record parent.
	if (virtualRecord)
		virtualRecord->parent = this;

	// Return a pointer to the virtual record.
	return (virtualRecord);
}

ZafWindowObject *ZafTable::Subtract(ZafWindowObject *object)
{
	// Defer to the base class.
	return (ZafWindow::Subtract(object));
}

// ----- Data Members -------------------------------------------------------

ZafError ZafTable::DefaultReadFunction(ZafTableRecord &, ZafWindowObject *)
{
	return (ZAF_ERROR_INVALID);
}

ZafError ZafTable::DefaultWriteFunction(ZafTableRecord &, ZafWindowObject *)
{
	return (ZAF_ERROR_INVALID);
}

ZafError ZafTable::DeleteRecord(ZafOffset deleteOffset)
{
	if (maxOffset >= 0 && deleteOffset >=0 && deleteOffset <= maxOffset)
	{
		if (deleteOffset == maxOffset)
		{
			ZafTableRecord *record = Record(maxOffset - 1);
			if (record)
				record->SetFocus(true);
			else if (maxOffset == 0)
			{
				ZafRegionStruct drawRegion = First()->BeginDraw();
				DrawFocus(drawRegion, S_NON_CURRENT);
				First()->EndDraw();
			}
		}

		maxOffset--;

		if (First())
		{
			ZafTableRecord *record = Record(deleteOffset);
			if (record)
			{
				ZafWindowObject *focusObject = record->Current();
				if (focusObject && focusObject->screenID)
					record->DeactivateObject(focusObject);
			}

			ZafOffset firstOffset = First()->Offset();
			ZafOffset lastOffset = Last()->Offset();

			if (deleteOffset <= lastOffset)
			{
				if (deleteOffset < firstOffset)
					firstOffset--;
				for (record = First(); record; record = record->Next())
				{
					if (record->Offset() >= deleteOffset)
						record->SetOffset(-1);
				}

				ComputeRecordPositions(firstOffset);

				if (Current())
					Current()->Draw(ZafEventStruct(S_REDISPLAY), S_REDISPLAY);
			}
		}

		Event(S_VSCROLL_COMPUTE);

		return (ZAF_ERROR_NONE);
	}

	return (ZAF_ERROR_INVALID);
}

ZafError ZafTable::InsertRecord(ZafOffset insertOffset)
{
	if (insertOffset >= 0 && insertOffset <= maxOffset + 1)
	{
		maxOffset++;

		if (First())
		{
			ZafTableRecord *record = Record(insertOffset);
			if (record)
			{
				ZafWindowObject *focusObject = record->Current();
				if (focusObject && focusObject->screenID)
					record->DeactivateObject(focusObject);
			}

			ZafOffset firstOffset = First()->Offset();
			ZafOffset lastOffset = Last()->Offset();

			if (insertOffset <= lastOffset)
			{
				if (insertOffset < firstOffset)
					firstOffset++;
				for (record = First(); record; record = record->Next())
				{
					if (record->Offset() == insertOffset)
						focusRecord = record->Offset();

					// Setting record offsets to -1 will cause
					// ComputeRecordPositions to read the records' data.
					if (record->Offset() >= insertOffset)
						record->SetOffset(-1);
				}
			}

			ComputeRecordPositions(firstOffset);

			if (Current())
				Current()->Draw(ZafEventStruct(S_REDISPLAY), S_REDISPLAY);
		}
		else if (maxOffset == 0)
		{
			ComputeRecordPositions(0);
			if (focus && First())
				First()->SetFocus(true);
			else
				SetCurrent(First());

			if (Current())
				Current()->Draw(ZafEventStruct(S_REDISPLAY), S_REDISPLAY);
		}

		Event(S_VSCROLL_COMPUTE);

		return (ZAF_ERROR_NONE);
	}

	return (ZAF_ERROR_INVALID);
}

ZafError ZafTable::ReadRecord(ZafTableRecord &record, ZafWindowObject *row)
{
	// Make sure that wrapped records do not carry over
	// selected status.
	record.SetSelected(false);
	if (row)
		row->SetSelected(false);

	ZafError tError = ReadRecordData(record, row);

	// SetChanged recurses through its children.
	record.SetChanged(false);
	if (row)
		row->SetChanged(false);

	return (tError);
}

ZafError ZafTable::ReadRecordData(ZafTableRecord &record, ZafWindowObject *row)
{
	ZafError tError = ZAF_ERROR_NONE;
	if (readFunction)
		tError = (*readFunction)(record, row);

	return (tError);
}

ZafError ZafTable::SeekNextRecord(void)
{
	if (currentOffset > maxOffset)
		return (ZAF_ERROR_INVALID);
	currentOffset++;
	return (ZAF_ERROR_NONE);
}

ZafError ZafTable::SeekOptimized(ZafOffset newOffset)
{
	// Optimize the seek.
	if (currentOffset == newOffset)
		return (ZAF_ERROR_NONE); // no seek is necessary;
	else if (currentOffset + 1 == newOffset)
		return (SeekNextRecord());
	else if (currentOffset - 1 == newOffset)
		return (SeekPreviousRecord());
	else
		return (SeekRandomRecord(newOffset, ZAF_SEEK_START)); // no hope!
}

ZafError ZafTable::SeekPreviousRecord(void)
{
	if (currentOffset == 0)
		return (ZAF_ERROR_INVALID);
	currentOffset--;
	return (ZAF_ERROR_NONE);
}

ZafError ZafTable::SeekRandomRecord(ZafOffset offset, ZafSeek location)
{
	// Reset the location.
	if (location == ZAF_SEEK_CURRENT)
		offset += currentOffset;
	else if (location == ZAF_SEEK_END)
		offset += maxOffset;

	// Check for out-of-bounds.
	if (offset < 0 || offset > maxOffset)
		return (ZAF_ERROR_INVALID);

	// Reset the current value.
	currentOffset = offset;
	return (ZAF_ERROR_NONE);
}

ZafError ZafTable::SetColumnText(int columnNumber, const ZafIChar *text)
{
	// Try to find a matching entry.
	ZafTableHeader *header = ColumnTableHeader();
	if (header)
	{
		for (ZafWindowObject *object = header->First(); object; columnNumber--, object = object->Next())
			if (columnNumber == 0)
				return (object->SetText(text));
	}
	return (ZAF_ERROR_INVALID);
}

ZafOffset ZafTable::Tell(void)
{
	// Return the current position.
	return (currentOffset);
}

ZafError ZafTable::WriteRecord(ZafTableRecord &record, ZafWindowObject *row)
{
	// Write out any changed records.
	ZafError tError = ZAF_ERROR_NONE;
	if (record.Changed())
		tError = WriteRecordData(record, row);

	// SetChanged recurses through its children.
	record.SetChanged(false);
	return (tError);
}

ZafError ZafTable::WriteRecordData(ZafTableRecord &record, ZafWindowObject *row)
{
	ZafError tError = ZAF_ERROR_NONE;
	if (writeFunction)
		tError = (*writeFunction)(record, row);

	return (tError);
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafTable::ZafTable(const ZafIChar *name, ZafObjectPersistence &persist) :
	ZafWindow(name, persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY)),
	virtualRecord(ZAF_NULLP(ZafTableRecord)),
	currentOffset(-1), focusSize(1),
	readFunction(ZAF_NULLF(MemberTableFunction)),
	writeFunction(ZAF_NULLF(MemberTableFunction)),
	itemWidth(0), itemHeight(0), focusRecord(-1), focusField(-1)
{
	// ZafWindow persistent constructor loaded the virtualRecord and added it
	// as a child, so need to remove it.
	while (First())
	{
		ZafWindowObject *object = First();
		ZafWindow::Subtract(object);
		delete object;
	}

	if (persist.Error() == ZAF_ERROR_NONE)
		ZafTable::ReadData(persist);

	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());

#if defined(ZAF_MOTIF)
	firstTimeDrawing = true;
	getChildPalette = false;
	sizing = true;
#endif
}

ZafElement *ZafTable::Read(const ZafIChar *name, ZafObjectPersistence &persist)
{
	return (new ZafTable(name, persist));
}

void ZafTable::ReadData(ZafObjectPersistence &persist)
{
	// Read the data.
	ZafIChar virtualRecordName[ZAF_MAXNAMELEN];
	ZafFile *file = persist.CurrentFile();
	ZafUInt16 flag1;
	ZafInt32 value32;
	*file >> flag1 >> value32 >> virtualRecordName;
	grid = (flag1 & 0x0001) ? true : false;
	maxOffset = (ZafOffset)value32;

	// Read the virtualRecord.
	if (*virtualRecordName)
	{
		ZafFileSystem *fileSystem = persist.CurrentFileSystem();
		ZafFileInfoStruct fileInfo;

		// Get className, etc for the virtual record.
		fileSystem->FindFirst(fileInfo, virtualRecordName);

		// Get constructor from persistence table so can use derived table
		// record.
		ZafObjectConstructor objectConstructor =
			persist.GetObjectConstructor((ZafClassID)0, fileInfo.stringID);
		if (objectConstructor)
		{
			delete virtualRecord;

			persist.PushLevel(fileInfo.stringID, fileInfo.numberID, ZAF_PERSIST_DIRECTORY);
			virtualRecord = (ZafTableRecord *)(objectConstructor)(virtualRecordName, persist);
			persist.PopLevel();
			virtualRecord->parent = this;
		}
	}
}

void ZafTable::Write(ZafObjectPersistence &persist)
{
	// Bypass ZafWindow calls to keep the children from being saved.
	ZafWindowObject::Write(persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY));
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafWindow::WriteData(persist);
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafWindow::WriteSupportChildren(persist);
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafTable::WriteData(persist);

	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

void ZafTable::WriteData(ZafObjectPersistence &persist)
{
	if (virtualRecord && !virtualRecord->StringID())
	{
		virtualRecord->SetStringID(_virtualRecordName);
		virtualRecord->Event(S_INITIALIZE);
	}
	const ZafIChar *virtualRecordName = virtualRecord ? virtualRecord->StringID() : ZAF_NULLP(ZafIChar);

	ZafFile *file = persist.CurrentFile();
	ZafUInt16 flag1 = 0;
	flag1 |= (ZafUInt16)(grid ? 0x0001 : 0);
	ZafInt32 value32 = (ZafInt32)maxOffset;
	*file << flag1 << value32 << virtualRecordName;

	// Write the virtualRecord.
	if (virtualRecord)
		virtualRecord->Write(persist);
}
#endif
