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

#include <z_table.hpp>
#include <z_mouse2.hpp>

static ZafNumberID _colNumberID = ZAF_NUMID_COLUMN_HEADER;
static ZafStringID _colStringID = ZAF_ITEXT("ZAF_COLUMN_HEADER");
static ZafNumberID _rowNumberID = ZAF_NUMID_ROW_HEADER;
static ZafStringID _rowStringID = ZAF_ITEXT("ZAF_ROW_HEADER");
static ZafNumberID _corNumberID = ZAF_NUMID_CORNER_HEADER;
static ZafStringID _corStringID = ZAF_ITEXT("ZAF_CORNER_HEADER");
static ZafStringID _virtualFieldName = ZAF_ITEXT("virtualField");

ZafTableHeader *ZafTable::ColumnTableHeader(void) const
{
	for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
		if (object->NumberID() == _colNumberID)
			return (DynamicPtrCast(object, ZafTableHeader));
	return (ZAF_NULLP(ZafTableHeader));
}

ZafTableHeader *ZafTable::CornerTableHeader(void) const
{
	for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
		if (object->NumberID() == _corNumberID)
			return (DynamicPtrCast(object, ZafTableHeader));
	return (ZAF_NULLP(ZafTableHeader));
}

ZafTableHeader *ZafTable::RowTableHeader(void) const
{
	for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
		if (object->NumberID() == _rowNumberID)
			return (DynamicPtrCast(object, ZafTableHeader));
	return (ZAF_NULLP(ZafTableHeader));
}

// ----- ZafTableHeader -----------------------------------------------------

ZafTableHeader::ZafTableHeader(int width, int height, ZafTableHeaderType tHeaderType) :
	ZafWindow(0, 0, width, height),
	headerType((ZafTableHeaderType)-1),
	virtualField(ZAF_NULLP(ZafWindowObject))
{
	SetHeaderType(tHeaderType);
	SetBordered(false);
	SetDestroyable(false);
	SetMoveable(false);
	SetNoncurrent(true);
	SetOSDraw(false);
	SetRegionType(ZAF_AVAILABLE_REGION);
	SetSelectionType(ZAF_MULTIPLE_SELECTION);
	SetSizeable(false);
	SetSupportObject(true);
}

ZafTableHeader::ZafTableHeader(const ZafTableHeader &copy) :
	ZafWindow(copy),
	headerType((ZafTableHeaderType)-1),
	virtualField(ZAF_NULLP(ZafWindowObject))
{
	// Need to call the headerType attribute function so it can set
	// numberID and stringID.
	SetHeaderType(copy.headerType);

	if (First())
	{
		ZafWindowObject *object;

		// ZafWindow copy constructor will have duplicated row header fields,
		// which are created by the table using virtualField.  So we need
		// to remove them.
		if (headerType == ZAF_ROW_HEADER)
		{
			while (First())
			{
				object = First();
				ZafWindow::Subtract(object);
				delete object;
			}
		}
		else
		{
			// The header objects were added by the ZafWindow 
			// copy constructor using the ZafWindow::Add() function.
			// They need to be added using ZafTableHeader::Add().
			ZafList tempList;
			while (First())
			{
				object = First();
				Subtract(object);
				tempList.Add(object);
			}
			while (tempList.First())
			{
				object = DynamicPtrCast(tempList.First(), ZafWindowObject);
				tempList.Subtract(object);
				Add(object);
			}
		}
	}

	if (copy.virtualField)
		SetVirtualField(copy.virtualField->Duplicate());
}

ZafTableHeader::~ZafTableHeader(void)
{
	// Delete the virtual field.
	if (virtualField)
		delete virtualField;
}

ZafWindowObject *ZafTableHeader::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Add the object to the table header.
	object->SetSystemObject(false);
#if !defined(ZAF_TEXT_MODE)
	object->SetParentDrawBorder(true);
#endif
	object->SetParentDrawFocus(true);
	object->SetParentPalette(true);
	ZafWindow::Add(object, position);

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

void ZafTableHeader::ConvertRegion(ZafRegionStruct &region, ZafCoordinateType newType)
{
	// Go directly to convert coordinates so preSpace and postSpace are not used.
	region.ConvertCoordinates(newType);
}

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

#	if defined(ZAF_INDEPENDENT)
	display->SetClipRegion(event.region);
#	endif

	// Draw the border.
	if (headerType == ZAF_ROW_HEADER)
	{
		if (virtualField)
		{
			ZafRegionStruct region = drawRegion;
			ZafPaletteState state = PaletteState();
			int top = region.top + (Last() ? Last()->zafRegion.bottom + 1 : 0);
			int height = Table()->itemHeight + Table()->focusSize;
			int bottom = top + height - 1;
			while (top < drawRegion.bottom)
			{
				region.left = drawRegion.left;
				region.right = drawRegion.right;
				region.top = top;
				region.bottom = (bottom < drawRegion.bottom) ? bottom : drawRegion.bottom;
				DrawBorder(region, ccode);
				display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
				display->Rectangle(region, 0, true);
				top = bottom + 1;
				bottom += height;
			}
		}
		else if (!First())
			DrawBorder(drawRegion, ccode);
	}
	else if (headerType == ZAF_CORNER_HEADER)
		DrawBorder(drawRegion, ccode);

	// Don't let the children make nested calls to BeginDraw().
	EndDraw();

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

	// Erase the remaining region.
	if (headerType == ZAF_COLUMN_HEADER || headerType == ZAF_CORNER_HEADER)
	{
		ZafRegionStruct childRegion = BeginDraw();

#	if defined(ZAF_INDEPENDENT)
		display->SetClipRegion(event.region);
#	endif

		int left = childRegion.left + (Last() ? Last()->zafRegion.right + 1 : 0);
		if (left <= childRegion.right)
		{
			// Erase the region to the right of the children.
			ZafRegionStruct backgroundRegion = childRegion;
			backgroundRegion.left = left;
			DrawBackground(backgroundRegion, ccode);
		}

		if (DynamicPtrCast(parent, ZafTable)->Grid())
		{
			// Draw the grid lines between children.
			ZafRegionStruct region = childRegion;
			ZafWindowObject *child = First();
			while (child)
			{
				region.left = region.right = child->zafRegion.right + 1;
				DrawBackground(region, ccode);
				child = child->Next();
			}
		}

		EndDraw();
	}

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

ZafEventType ZafTableHeader::DrawBorder(ZafRegionStruct &region, ZafEventType ccode)
{
	// Defer to the base class.
	DrawShadow(region, 1, ccode);

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

ZafEventType ZafTableHeader::DrawFocus(ZafRegionStruct &, ZafEventType ccode)
{
	// Object focus does not change.
	return (ccode);
}
#endif

ZafWindowObject *ZafTableHeader::Duplicate(void)
{
	return (new ZafTableHeader(*this));
}

ZafEventType ZafTableHeader::Event(const ZafEventStruct &event)
{
	// Check for zinc events.
	ZafEventType ccode = LogicalEvent(event);
	switch (ccode)
	{
	case S_INITIALIZE:
		ccode = ZafWindow::Event(event);
		if (virtualField)
			virtualField->Event(event);
		break;

	case S_COMPUTE_SIZE:
		{
		int height = zafRegion.bottom - zafRegion.top;
		int width = zafRegion.right - zafRegion.left;
		ccode = ZafWindowObject::Event(event);
		if (headerType == ZAF_CORNER_HEADER || headerType == ZAF_ROW_HEADER)
			zafRegion.right = zafRegion.left + width;
		if (headerType == ZAF_CORNER_HEADER || headerType == ZAF_COLUMN_HEADER)
			zafRegion.bottom = zafRegion.top + height;
		}
		break;

	case N_SIZE:
		// Pre-size the children.
		if (headerType == ZAF_COLUMN_HEADER)
		{
			ZafWindowObject *object = First();
			ZafWindowObject *vObject = Table()->VirtualRecord()->First();
			for ( ; object && vObject; object = object->Next(), vObject = vObject->Next())
			{
				object->zafRegion.top = 0;
				object->zafRegion.bottom = zafRegion.bottom - zafRegion.top;
				object->zafRegion.left = vObject->zafRegion.left;
#if defined(ZAF_MACINTOSH) || defined(ZAF_INDEPENDENT)
				if (Table()->Grid())
					object->zafRegion.right = vObject->zafRegion.right + 1;
				else	
					object->zafRegion.right = vObject->zafRegion.right;
#else
				if (object->Previous() && vObject->zafRegion.left)
					object->zafRegion.left += ZAF_HEADER_OBJECT_SHIFT;
				object->zafRegion.right = vObject->zafRegion.right + ZAF_HEADER_OBJECT_SHIFT;
#endif
				object->SetRegion(object->zafRegion);
			}
		}

		// Allow base class handling.
		ccode = ZafWindow::Event(event);

#if defined(ZAF_MSWINDOWS)
		if (headerType == ZAF_ROW_HEADER && zafRegion.Height() != oldRegion.Height())
		{
			ZafEventStruct redisplayEvent(S_REDISPLAY_REGION);
			redisplayEvent.region.left = 0;
			redisplayEvent.region.right = zafRegion.Width() - 1;
			if (zafRegion.Height() > oldRegion.Height())
				redisplayEvent.region.top = redisplayEvent.region.bottom = oldRegion.Height() - 1;
			else
				redisplayEvent.region.top = redisplayEvent.region.bottom = zafRegion.Height() - 1;
			Event(redisplayEvent);
		}
#endif
		break;

	// ----- Selection messages -----
	case L_BEGIN_SELECT:
		if (headerType == ZAF_ROW_HEADER)
		{
			for (ZafWindowObject *object = First(); object; object = object->Next())
				if (object->zafRegion.Overlap(event.position))
				{
					object->ToggleSelected();
					object->RedisplayData();

					// Update the table record.
					ZafWindowObject* rec = Table()->Get(Index(object));
#if defined(ZAF_MACINTOSH)
					rec->SetFocus(true);
#endif
					rec->SetSelected(object->Selected());
					break;
				}
		}
		break;

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

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

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

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

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

ZafWindowObject *ZafTableHeader::NotifySelection(ZafWindowObject *object, bool setSelected)
{
	// Defer to the base class, checking for selection error.
	ZafWindow::NotifySelection(object, setSelected);
	if (object->Selected() == setSelected)
		object->RedisplayData();

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

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

bool ZafTableHeader::SetBordered(bool )
{
	// bordered is false for this class.
	bordered = false;
	return (bordered);
}

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

bool ZafTableHeader::SetFocus(bool )
{
	// focus is false for this class.
	focus = false;
	return (focus);
}

ZafTableHeaderType ZafTableHeader::SetHeaderType(ZafTableHeaderType setHeaderType)
{
	// Make sure the attribute has changed.
	if (headerType != setHeaderType && !screenID)
	{
		headerType = setHeaderType;
		if (HeaderType() == ZAF_CORNER_HEADER)
		{
			SetNumberID(_corNumberID);
			SetStringID(_corStringID);
		}
		else if (HeaderType() == ZAF_COLUMN_HEADER)
		{
			SetNumberID(_colNumberID);
			SetStringID(_colStringID);
		}
		else if (HeaderType() == ZAF_ROW_HEADER)
		{
			SetNumberID(_rowNumberID);
			SetStringID(_rowStringID);
		}
	}

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

const ZafIChar *ZafTableHeader::SetHelpContext(const ZafIChar *)
{
	// help context is empty for this class.
	return (ZAF_NULLP(ZafIChar));
}

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

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

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

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

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

bool ZafTableHeader::SetNoncurrent(bool )
{
	// noncurrent is true for this class.
	noncurrent = true;
	return (noncurrent);
}

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

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

ZafRegionType ZafTableHeader::SetRegionType(ZafRegionType )
{
	// regionType is ZAF_AVAILABLE_REGION for this class.
	regionType = ZAF_AVAILABLE_REGION;
	return (regionType);
}

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

bool ZafTableHeader::SetSupportObject(bool )
{
	// supportObject is true for this class.
	supportObject = true;
	return (supportObject);
}

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

ZafUserFunction ZafTableHeader::SetUserFunction(ZafUserFunction )
{
	// userFunction is null for this class.
	userFunction = ZAF_NULLF(ZafUserFunction);
	return (userFunction);
}

ZafWindowObject *ZafTableHeader::SetVirtualField(ZafWindowObject *tVirtualField)
{
	// Make sure the attribute has changed.
	if (!screenID)
	{
		if (virtualField)
			delete virtualField;
		virtualField = tVirtualField;
	}

	if (virtualField)
		virtualField->parent = this;

	// Return the current virtual field.
	return (virtualField);
}

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

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

OSWindowID ZafTableHeader::OSScreenID(ZafScreenIDType type) const
{
	// Determine the proper widget.
	return (ZafWindow::OSScreenID(type));
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafTableHeader::ZafTableHeader(const ZafIChar *name, ZafObjectPersistence &persist) :
	ZafWindow(name, persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY)),
	headerType((ZafTableHeaderType)-1),
	virtualField(ZAF_NULLP(ZafWindowObject))
{
	// This is for data files created before 5.1, when the default was true.
	SetBordered(false);

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

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

	if (First())
	{
		ZafWindowObject *object = First();
		// ZafWindow persistent constructor loaded the virtualField (if there 
		// was one) and added it as a child, so need to remove it and set it 
		// as the virtualField if this is a row header.
		if (headerType == ZAF_ROW_HEADER)
		{
			Subtract(object);
			SetVirtualField(object);
		}
		// Otherwise, the header objects were added by the ZafWindow 
		// persistent constructor using the ZafWindow::Add() function.
		// They need to be added using ZafTableHeader::Add().
		else
		{
			ZafList tempList;
			while (First())
			{
				object = First();
				Subtract(object);
				tempList.Add(object);
			}
			while (tempList.First())
			{
				object = DynamicPtrCast(tempList.First(), ZafWindowObject);
				tempList.Subtract(object);
				Add(object);
			}
		}
	}
}

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

void ZafTableHeader::ReadData(ZafObjectPersistence &persist)
{
	// Read the data.
	ZafUInt16 flag1;
	ZafFile *file = persist.CurrentFile();
	*file >> flag1;
	// flag1
	headerType = (ZafTableHeaderType)((int)flag1 & 0x000F);
}

void ZafTableHeader::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);

	// Write children if not a row header.  Row header uses virtualField.
	if (headerType != ZAF_ROW_HEADER && persist.Error() == ZAF_ERROR_NONE)
		ZafWindow::WriteChildren(persist);
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafTableHeader::WriteData(persist);

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

void ZafTableHeader::WriteData(ZafObjectPersistence &persist)
{
	if (virtualField && !virtualField->StringID())
		virtualField->SetStringID(_virtualFieldName);

	// Write the header data.
	ZafUInt16 flag1 = 0;
	// flag1
	flag1 |= (ZafUInt16)(headerType & 0x000F);
	ZafFile *file = persist.CurrentFile();
	*file << flag1;

	// Write the virtualField.
	if (virtualField)
	{
		virtualField->Write(persist.PushLevel(virtualField->ClassName(), virtualField->ClassID(), ZAF_PERSIST_DIRECTORY));
		persist.PopLevel();
	}
}
#endif
