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

#include <z_combo.hpp>
#include <z_str1.hpp>
#include <z_string.hpp>
#if defined(ZAF_INDEPENDENT)
#	include "i_combo.hpp"
#endif
#define ZAF_COMBO_BOX_INFO
#include "gbl_def.cpp"

static ZafStringID _listName = ZAF_ITEXT("list");

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

ZafComboBox::ZafComboBox(int left, int top, int width, int height,
	int tMaxListHeight) : ZafWindow(left, top, width, 1), viewOnly(false),
	maxListHeight(tMaxListHeight), listWidth(-1)
{
	// Initialize the combo-box information.
	SetBordered(true);
	SetDestroyable(false);
	SetMoveable(false);

	if (maxListHeight != -1)
	{
		zafRegion.bottom = zafRegion.top + height - 1;
		list = new ZafVtList(0, 0, width, maxListHeight);
	}
	else
		list = new ZafVtList(0, 0, width, height);

	list->parent = this;
	list->SetDestroyable(false);
	list->SetTemporary(true);
#if !defined(ZAF_TEXT_MODE)
	list->SetParentPalette(true);
#endif
	list->SetStringID(_listName);
	SetSelectionType(ZAF_SINGLE_SELECTION);	// Also affects the list.
	SetSizeable(false);

#if defined(ZAF_MOTIF)
	comboItem = ZAF_NULLP(ZafWindowObject);
	stringData.SetDestroyable(false);
	if (maxListHeight != -1)
		listHeight = maxListHeight;
	else
		listHeight = height;
#elif defined(ZAF_INDEPENDENT) || defined(ZAF_CURSES)
	if (maxListHeight != -1)
		listHeight = maxListHeight;
	else
		listHeight = height;
	comboString = comboButton = mouseObject = ZAF_NULLP(ZafWindowObject);
#elif defined(ZAF_MACINTOSH)
	comboString = ZAF_NULLP(ZafWindowObject);
#endif
}

ZafComboBox::ZafComboBox(const ZafComboBox &copy) :
	ZafWindow(copy), viewOnly(copy.viewOnly),
	maxListHeight(copy.maxListHeight), listWidth(copy.listWidth)
{
	// Initialize the combo-box information.
	list = new ZafVtList(*copy.list);
	list->parent = this;

	// Remove any combo-item information.
	ZafList::Destroy();
#if defined(ZAF_MOTIF)
	comboItem = ZAF_NULLP(ZafWindowObject);
	stringData.SetDestroyable(false);
	listHeight = copy.listHeight;
#elif defined(ZAF_INDEPENDENT) || defined(ZAF_CURSES)
	comboString = comboButton = mouseObject = ZAF_NULLP(ZafWindowObject);
	listHeight = copy.listHeight;
#elif defined(ZAF_MACINTOSH)
	comboString = ZAF_NULLP(ZafWindowObject);
#endif
#if defined(ZAF_MOTIF) || defined(ZAF_MSWINDOWS)
	if (Current() && Current()->Selected())
		stringData.SetText(Current()->Text());
#endif
}

ZafComboBox::~ZafComboBox(void)
{
#if defined(ZAF_MOTIF)
	ZafWindow::Subtract(comboItem);
	delete comboItem;
#endif
	delete list;
}

void ZafComboBox::BroadcastEvent(const ZafEventStruct &event, int )
{
	// Broadcast the event to all children.
	ZafWindowObject *object;
	for (object = SupportFirst(); object; object = object->Next())
		object->Event(event);
	for (object = ComboFirst(); object; object = object->Next())
		object->Event(event);
}

bool ZafComboBox::Changed(void) const
{
	// Check the immediate flag.
	if (changed)
		return (true);

	// Check the children.
	for (ZafWindowObject *object = First(); object; object = object->Next())
		if (object->Changed())
			return (true);

	// Check the list.
	return (list->Changed());
}

ZafCompareFunction ZafComboBox::CompareFunction(void) const 
{ 
	return list->CompareFunction(); 
}

void ZafComboBox::Destroy(void)
{
	// Destroy the list elements (high level).
	OSDestroy();

	// Destroy the list elements (low level).
	list->Destroy();
}

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

ZafWindowObject *ZafComboBox::FocusObject(void) const
{
	// Return a pointer to self.
	if (list->Focus())
		return (list->Current() ? list->Current() : list);
	else if (focus)
		return ((ZafWindowObject *)this);
	return (ZAF_NULLP(ZafWindowObject));
}

ZafWindowObject *ZafComboBox::GetObject(ZafNumberID zNumberID)
{
	// Try to match on the current object.
	ZafWindowObject *match = ZafWindowObject::GetObject(zNumberID);

	if (!match && list)
		match = list->GetObject(zNumberID);

	return (match);
}

ZafWindowObject *ZafComboBox::GetObject(const ZafIChar *matchID)
{
	// Try to match on the current object.
	ZafWindowObject *match = ZafWindowObject::GetObject(matchID);

	if (!match && list)
	{
		// Check if doing a search with a path.  If so, and the next path
		// element is the list's stringID, pass the search down to the list.
		ZafIChar parseID[ZAF_MAXNAMELEN];
		ZafIChar *tParseID = parseID;
		for ( ; *matchID && *matchID != ZAF_DIRECTORY_SEPARATOR; matchID++, tParseID++)
			*tParseID = *matchID;
		*tParseID = '\0';
		if (*matchID == ZAF_DIRECTORY_SEPARATOR)
			matchID++;

		// If the path element mathces the list stringID, pass the search 
		// to the list.
		if (!strcmp(parseID, list->StringID()))
			match = list->GetObject(matchID);
		else
		{
			// Try to match with one of the children.
			ZafWindowObject *object = list->First();  /* Modified */
			while (object && !match)
			{
				if (!streq(parseID, object->StringID()))
					match = *matchID ? object->GetObject(matchID) : object;
				object = object->Next();
			}
		}
	}

	return (match);
}

#if defined(ZAF_UNICODE)
ZafWindowObject *ZafComboBox::GetObject(const char *matchID)
{
	ZafStringData tStringData(matchID);
	return (GetObject(tStringData.Text()));
}
#endif

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

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

ZafRegionStruct ZafComboBox::ListRegion(void) const
{
	ZafRegionStruct region;
	// Assignment must be done to eliminate compiler warning.
	region.left = 0;
	region.Assign(0, 0, listWidth, maxListHeight,
		list->zafRegion.coordinateType);

	return (region);
}

int ZafComboBox::ListWidth(void) const
{
 	return (listWidth);
}

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

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

int ZafComboBox::MaximumListHeight(void) const
{
 	return (maxListHeight);
}

bool ZafComboBox::SetAutomaticUpdate(bool setAutomaticUpdate)
{
	// Defer the first part to the base class.
	ZafWindowObject::SetAutomaticUpdate(setAutomaticUpdate);

	// Now mirror the list.
	return (list->SetAutomaticUpdate(automaticUpdate));
}

ZafLogicalColor ZafComboBox::SetBackgroundColor(ZafLogicalColor color, ZafLogicalColor mono)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add the background color entry.
	ZafPaletteStruct backgroundPalette = userPaletteData->GetPalette(ZAF_PM_BACKGROUND, ZAF_PM_NOT_SELECTED);
	backgroundPalette.colorBackground = color;
	backgroundPalette.monoBackground = mono;
	userPaletteData->AddPalette(ZAF_PM_BACKGROUND, ZAF_PM_NOT_SELECTED, backgroundPalette);

	// Add the selected text color entry.
	backgroundPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_SELECTED | ZAF_PM_ENABLED);
	backgroundPalette.colorForeground = color;
	backgroundPalette.monoForeground = mono;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_SELECTED | ZAF_PM_ENABLED, backgroundPalette);

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

bool ZafComboBox::SetBordered(bool )
{
#if defined(ZAF_TEXT_MODE)
	// bordered is false for this class in text mode.
	bordered = false;
#else 
	// bordered is true for this class.
	bordered = true;
#endif
	return (bordered);
}

bool ZafComboBox::SetChanged(bool setChanged)
{
	// Defer the first part to the base class.
	ZafWindow::SetChanged(setChanged);

	// Reflect the change to the list.
	list->SetChanged(changed);

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

ZafCompareFunction ZafComboBox::SetCompareFunction(ZafCompareFunction newCompareFunction)
{
	// Defer to the list.
	return (list->SetCompareFunction(newCompareFunction));
}

ZafCoordinateType ZafComboBox::SetCoordinateType(ZafCoordinateType newCoordinateType)
{
	// Make sure the attribute has changed.
	if (coordinateType != newCoordinateType && !screenID)
	{
		if (coordinateType == ZAF_CELL && zafRegion.Height() == 1)
		{
			ZafRegionStruct convertRegion = zafRegion;
			ConvertRegion(convertRegion, newCoordinateType);
			zafRegion.bottom = zafRegion.top + convertRegion.Height() - 1;
		}
		coordinateType = zafRegion.coordinateType = newCoordinateType;
        if (list->CoordinateType() != newCoordinateType)
            list->ConvertCoordinates(newCoordinateType);
		list->SetCoordinateType(newCoordinateType);
	}

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

bool ZafComboBox::SetCopyDraggable(bool setCopyDraggable)
{
	// Defer the first part to the base class.
	if (ComboFirst())
		copyDraggable = ComboFirst()->SetCopyDraggable(setCopyDraggable);
	else
		ZafWindowObject::SetCopyDraggable(setCopyDraggable);

	// Reflect the drag-drop capabilities at the list level.
	list->SetCopyDraggable(copyDraggable);

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

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

ZafLogicalFont ZafComboBox::SetFont(ZafLogicalFont font)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add the new entries.
	ZafPaletteStruct textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_NOT_SELECTED | ZAF_PM_ENABLED);
	textPalette.font = font;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_NOT_SELECTED | ZAF_PM_ENABLED, textPalette);
	textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_SELECTED | ZAF_PM_ENABLED);
	textPalette.font = font;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_SELECTED | ZAF_PM_ENABLED, textPalette);
	textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_ANY_STATE);
	textPalette.font = font;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_ANY_STATE, textPalette);

	// Return the current font.
	return (font);
}

const ZafIChar *ZafComboBox::SetHelpObjectTip(const ZafIChar *newHelpObjectTip)
{
	// Defer the first part to the base class.
	ZafWindowObject::SetHelpObjectTip(newHelpObjectTip);

	// Now mirror the combo sub-object.
	if (ComboFirst())
		ComboFirst()->SetHelpObjectTip(helpObjectTip);

	// Return the current help object tip.
	return (helpObjectTip);
}

const ZafIChar *ZafComboBox::SetHelpContext(const ZafIChar *newHelpContext)
{
	// Defer the first part to the base class.
	ZafWindowObject::SetHelpContext(newHelpContext);

	// Now mirror the combo sub-object.
	if (ComboFirst())
		ComboFirst()->SetHelpContext(helpContext);

	// Return the current help object tip.
	return (helpContext);
}

bool ZafComboBox::SetLinkDraggable(bool setLinkDraggable)
{
	// Defer the first part to the base class.
	if (ComboFirst())
		linkDraggable = ComboFirst()->SetLinkDraggable(setLinkDraggable);
	else
		ZafWindowObject::SetLinkDraggable(setLinkDraggable);

	// Reflect the drag-drop capabilities at the list level.
	list->SetLinkDraggable(linkDraggable);

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

int ZafComboBox::SetListWidth(int tListWidth)
{
	if (!screenID)
		listWidth = tListWidth;

	return (listWidth);
}

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

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

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

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

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

bool ZafComboBox::SetMoveDraggable(bool setMoveDraggable)
{
	// Defer the first part to the base class.
	if (ComboFirst())
		moveDraggable = ComboFirst()->SetMoveDraggable(setMoveDraggable);
	else
		ZafWindowObject::SetMoveDraggable(setMoveDraggable);

	// Reflect the drag-drop capabilities at the list level.
	list->SetMoveDraggable(moveDraggable);

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

bool ZafComboBox::SetNoncurrent(bool setNoncurrent)
{
	// Defer the first part to the base class.
	ZafWindowObject::SetNoncurrent(setNoncurrent);

	// Now mirror the combo sub-object and the list.
	if (ComboFirst())
		ComboFirst()->SetNoncurrent(noncurrent);

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

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

bool ZafComboBox::SetOSDraw(bool setOSDraw)
{
	// Make sure the attribute has changed.
	if (osDraw != setOSDraw && !screenID)
		osDraw = list->SetOSDraw(setOSDraw);

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

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

const ZafIChar *ZafComboBox::SetQuickTip(const ZafIChar *newQuickTip)
{
	// Defer the first part to the base class.
	ZafWindowObject::SetQuickTip(newQuickTip);

	// Now mirror the combo sub-object.
	if (ComboFirst())
		ComboFirst()->SetQuickTip(quickTip);

	// Return the current help object tip.
	return (quickTip);
}

ZafSelectionType ZafComboBox::SetSelectionType(ZafSelectionType )
{
	// selectionType is ZAF_SINGLE_SELECTION for this class.
	selectionType = ZAF_SINGLE_SELECTION;

	// Reflect changes to the list.
	list->SetSelectionType(ZAF_SINGLE_SELECTION);

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

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

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

ZafLogicalColor ZafComboBox::SetTextColor(ZafLogicalColor color, ZafLogicalColor mono)
{
	// Make sure there is a userPalette.
	if (!userPaletteData)
		SetUserPaletteData(new ZafPaletteData());

	// Add the text color entry.
	ZafPaletteStruct textPalette = userPaletteData->GetPalette(ZAF_PM_TEXT, ZAF_PM_NOT_SELECTED | ZAF_PM_ENABLED);
	textPalette.colorForeground = color;
	textPalette.monoForeground = mono;
	userPaletteData->AddPalette(ZAF_PM_TEXT, ZAF_PM_NOT_SELECTED | ZAF_PM_ENABLED, textPalette);

	// Add the selected text color entry.
	textPalette = userPaletteData->GetPalette(ZAF_PM_BACKGROUND, ZAF_PM_SELECTED);
	textPalette.colorBackground = color;
	textPalette.monoBackground = mono;
	userPaletteData->AddPalette(ZAF_PM_BACKGROUND, ZAF_PM_SELECTED, textPalette);

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

bool ZafComboBox::SetViewOnly(bool setViewOnly)
{
	// Check for equivalent flag.
	if (viewOnly != setViewOnly && !screenID)
	{
		// Reset the value and match the sub-object.
		ZafString *string = DynamicPtrCast(ComboFirst(), ZafString);
		if (string)
			viewOnly = string->SetViewOnly(setViewOnly);
		else
			viewOnly = setViewOnly;
	}

	// Return the current status.
	return (viewOnly);
}

void ZafComboBox::Sort(void)
{
	// Sort the list elements (low level).
	list->ZafList::Sort();

	// Sort the list elements (high level).
	OSSort();
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafComboBox::ZafComboBox(const ZafIChar *name, ZafObjectPersistence &persist) :
	ZafWindow(name, persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY)),
	list(ZAF_NULLP(ZafVtList)), maxListHeight(-1), listWidth(-1)
{
	// Read the data.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafComboBox::ReadData(persist);

	// Read the list data.
	for (ZafElement *element = first; !list && element; element= element->Next())
		list = DynamicPtrCast(element, ZafVtList);
	if (list)
		ZafList::Subtract(list);
	else
		list = new ZafVtList(0, 0, zafRegion.Width(), zafRegion.Height());
	list->parent = this;
	list->SetDestroyable(false);
	list->SetTemporary(true);
	list->SetParentPalette(true);
	ZafRegionStruct tRegion = list->zafRegion;
	tRegion.ConvertCoordinates(coordinateType);
	maxListHeight  = tRegion.Height();
	listWidth = tRegion.Width();
	list->zafRegion.Assign(0, 0, listWidth, tRegion.top, tRegion.coordinateType);
	SetSelectionType(ZAF_SINGLE_SELECTION);	// Also affects the list.

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

	// Initialize the combo-box information.
#if defined(ZAF_MOTIF)
	comboItem = ZAF_NULLP(ZafWindowObject);
	stringData.SetDestroyable(false);
	listHeight = list->zafRegion.Height();
#elif defined(ZAF_INDEPENDENT) || defined(ZAF_CURSES)
	comboString = comboButton = mouseObject = ZAF_NULLP(ZafWindowObject);
	listHeight = list->zafRegion.Height();
	// Make the list's width the same as zafRegion.Width().
	list->SetRegion(zafRegion);
#elif defined(ZAF_MACINTOSH)
	comboString = ZAF_NULLP(ZafWindowObject);
#endif
#if defined(ZAF_MOTIF) || defined(ZAF_MSWINDOWS)
	if (Current() && Current()->Selected())
		stringData.SetText(Current()->Text());
#endif
}

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

void ZafComboBox::ReadData(ZafObjectPersistence &persist)
{
	// Read the data.
	ZafUInt16 flag1;
	ZafFile *file = persist.CurrentFile();
	*file >> flag1;
	viewOnly = (flag1 & 0x0001) ? true : false;
}

void ZafComboBox::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)
		ZafComboBox::WriteData(persist);

	// Save the drop down list dimensions.
	ZafCoordinateType tCoord = zafRegion.coordinateType;
	ConvertCoordinates(coordinateType);
	ZafRegionStruct tRegion = list->zafRegion;
#if defined(ZAF_MSWINDOWS) || defined(ZAF_MACINTOSH)
	list->ConvertCoordinates(coordinateType);
	int listHeight = list->zafRegion.Height();
#endif
	list->zafRegion.Assign(0, listHeight, listWidth, maxListHeight, coordinateType);

	// Write the list data.
	list->Write(persist.PushLevel(list->ClassName(), list->ClassID(), ZAF_PERSIST_DIRECTORY));
	list->zafRegion = tRegion;
	persist.PopLevel();
	ConvertCoordinates(tCoord);

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

void ZafComboBox::WriteData(ZafObjectPersistence &persist)
{
	// Write the data.
	ZafUInt16 flag1 = 0;
	flag1 |= (ZafUInt16)(viewOnly ? 0x0001 : 0);
	ZafFile *file = persist.CurrentFile();
	*file << flag1;
}
#endif
