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

#include <w_app.hpp>
#include "w_imglst.hpp"
#include <z_scrll2.hpp>
#include <z_tree.hpp>
#include <z_string.hpp>
#include <z_utils.hpp>

const int ARROW_SIZE = 13;

// ----- ZafTreeItem ------------------------------------------------------

ZafWindowObject *ZafTreeItem::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	ZafTreeItem *item = DynamicPtrCast(object, ZafTreeItem);

	if (item)
	{
		// current is not used (only TreeList's current is used) but it
		// must be set so that ZafWindow::Add() doesn't assign focus.
		current = this;
		ZafWindow::Add(object, position);

		if (screenID)
		{
			ZafTreeList *treeList = TreeList();
			treeList->ResizeVisibleItems(ZAF_NULLP(ZafTreeItem));
			treeList->Event(S_VSCROLL_COMPUTE);
			if (treeList->ItemInView(item) || item == treeList->ViewLast())
			{
				ZafRegionStruct redrawRegion;
				redrawRegion.left = redrawRegion.top = 0;
				redrawRegion.right = treeList->ClientRegion().Width() - 1;
				redrawRegion.bottom = treeList->ClientRegion().Height() - 1;
				treeList->Event(ZafEventStruct(S_REDISPLAY_REGION, 0, redrawRegion));
			}
		}
	}

	return object;
}

ZafRegionStruct ZafTreeItem::ConvertToDrawRegion(const ZafWindowObject *object,
	const ZafRegionStruct *zafRegion) const
{
	ZafRegionStruct drawRegion;
	if (zafRegion)
//		return *zafRegion;
		drawRegion = *zafRegion;
	else
//		return object->zafRegion;
		drawRegion = object->zafRegion;

	ZafScrollBar *scrollBar = TreeList()->HorizontalScrollBar();
	if (scrollBar)
	{
		int scrollValue = (int)scrollBar->ScrollData()->Current();
		drawRegion.left -= scrollValue;
//		drawRegion.right -= scrollValue;
	}

	return drawRegion;
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafTreeItem::Draw(const ZafEventStruct &, ZafEventType ccode)
{
//??? Drawing not optimized yet.
	// Begin the drawing operation.
	ZafRegionStruct drawRegion = BeginDraw();

	ZafPaletteState state = PaletteState();

	if (ccode != S_REDISPLAY_DATA)
	{
		// Erase the background.
		DrawBackground(drawRegion, ccode);

		// Draw the expansion button.
		ZafRegionStruct region = expandRegion;
		region.left += drawRegion.left;
		region.right += drawRegion.left;
		region--;
		if (expandable)
		{
			if (Expanded())
			{
				region.left += (region.Width() - expandBitmap->Width()) / 2;
				region.top += (region.Height() - expandBitmap->Height()) / 2;
				display->Bitmap(region.left, region.top, *expandBitmap);
			}
			else
			{
				region.left += (region.Width() - compressBitmap->Width()) / 2;
				region.top += (region.Height() - compressBitmap->Height()) / 2;
				display->Bitmap(region.left, region.top, *compressBitmap);
			}
		}

		// Draw the bitmap.
		if (Selected() && selectedBitmap)
			display->Bitmap(drawRegion.left + bitmapRegion.left, bitmapRegion.top, *selectedBitmap);
		else if (!Selected() && normalBitmap)
			display->Bitmap(drawRegion.left + bitmapRegion.left, bitmapRegion.top, *normalBitmap);

		// Draw the tree lines.
		ZafTreeList *treeList = TreeList();
		if (treeList->DrawLines())
		{
			int top = drawRegion.top;
			int bottom = drawRegion.bottom;
			int centerLine = (top + bottom) / 2;
			int centerColumn = drawRegion.left + (expandRegion.left + expandRegion.right) / 2;
			display->SetPalette(LogicalPalette(ZAF_PM_OUTLINE, state));

			ZafTreeItem *root = DynamicPtrCast(parent, ZafTreeItem);
			if (expanded && First())
				display->Line(centerColumn, expandRegion.bottom, centerColumn, bottom);
			if (root)
			{
				centerColumn = drawRegion.left + (root->expandRegion.left + root->expandRegion.right) / 2;
				display->Line(centerColumn, centerLine, drawRegion.left + expandRegion.left - 1, centerLine);
				display->Line(centerColumn, top, centerColumn, Next() ? bottom : centerLine - 1);
			}
			while (root)
				if (root->Next())
				{
					root = DynamicPtrCast(root->parent, ZafTreeItem);
					if (root)
					{
						centerColumn = drawRegion.left + (root->expandRegion.left + root->expandRegion.right) / 2;
						display->Line(centerColumn, top, centerColumn, bottom);
					}
				}
				else
					root = DynamicPtrCast(root->parent, ZafTreeItem);
		}
	}

	// Compute the focus region.
	display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));
	ZafRegionStruct focusRegion = textRegion;
	focusRegion.left += drawRegion.left;
	focusRegion.right += drawRegion.left;
	ZafRegionStruct textSize = display->TextSize(*stringData);
	int centerY = focusRegion.top + focusRegion.Height() / 2;
	focusRegion.top = centerY - textSize.Height() / 2 - 1;
	focusRegion.bottom = focusRegion.top + textSize.Height();
	focusRegion.right = focusRegion.left + textSize.Width() + 1;
	focusRegion.left--;

	if (ccode == S_REDISPLAY_DATA)
	{
		// Erase the background.
		ZafRegionStruct backgroundRegion = drawRegion;
		backgroundRegion.left = focusRegion.left;
		DrawBackground(backgroundRegion, ccode);
	}

	// Draw the text.
	display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));
	display->Rectangle(focusRegion, 0, true);
	display->Text(focusRegion, *stringData, -1);

	// Draw the focus rectangle.
	if (Focus())
		DrawFocus(focusRegion, S_CURRENT);

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

ZafWindowObject *ZafTreeItem::Subtract(ZafWindowObject *object)
{
	if (screenID && !SystemObject())
	{
		ZafTreeList *treeList = TreeList();
		ZafTreeItem *treeItem = DynamicPtrCast(object, ZafTreeItem);
		bool recompute = treeList->ItemInView(treeItem) ? true : false;

		// Redirect pointers.
		ZafTreeItem *replaceItem = ZAF_NULLP(ZafTreeItem);
		ZafTreeItem *tReplaceItem = DynamicPtrCast(treeItem->Next(), ZafTreeItem);

		// If object had a next, it is the replaceItem.
		if (tReplaceItem)
			replaceItem = tReplaceItem;
		// Otherwise, will need to get the ViewNext of the object's ViewPrevious.
		else
			tReplaceItem = treeItem->ViewPrevious();

		object->SetSelected(false);
		ZafWindow::Subtract(object);

		// Get ViewNext if don't already have the replaceItem.
		if (!replaceItem)
			replaceItem = tReplaceItem->ViewNext() ? tReplaceItem->ViewNext() : tReplaceItem;

		ZafWindowObject *currentTest = treeList->Current();
		while (currentTest && currentTest != object)
		{
			currentTest = currentTest->parent;
			if (currentTest == treeList)
				currentTest = ZAF_NULLP(ZafWindowObject);
		}
		if (currentTest)
			treeList->current = replaceItem;
		if (treeList->firstVisible == treeItem || treeItem->Index(treeList->firstVisible) != -1)
			treeList->firstVisible = replaceItem;
		if (treeList->SelectionType() == ZAF_SINGLE_SELECTION && treeList->current == replaceItem)
			replaceItem->SetSelected(true);

		if (recompute)
		{
			treeList->ResizeVisibleItems(replaceItem);
			ZafRegionStruct redrawRegion;
			redrawRegion.left = redrawRegion.top = 0;
			redrawRegion.right = treeList->ClientRegion().Width() - 1;
			redrawRegion.bottom = treeList->ClientRegion().Height() - 1;
			treeList->Event(ZafEventStruct(S_REDISPLAY_REGION, 0, redrawRegion));
		}
	}
	else
		ZafWindow::Subtract(object);

	return object;
}

ZafEventType ZafTreeItem::Event(const ZafEventStruct &event)
{
	// Check for zinc events.
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
	case S_DEINITIALIZE:
		// Initialize the object and its children.
/* START BLOCK COMMENT
**	#if defined(ZAF_WIN32)
**			if (ZafMSWindowsApp::commonControlsAvailable)
**				systemObject = true;
**	#endif
END BLOCK COMMENT */
		ccode = ZafWindowObject::Event(event);
		BroadcastEvent(event);
		break;

/* START BLOCK COMMENT
**	#if defined(ZAF_WIN32)
**		case S_DESTROY:
**			if (ZafMSWindowsApp::commonControlsAvailable)
**				SendMessage(OSScreenID(ZAF_TREEID), TVM_DELETEITEM, (WPARAM)0, (LPARAM)screenID);
**			else
**			{
**				if (focus)
**				{
**					Event(ZafEventStruct(N_NON_CURRENT));
**					Event(ZafEventStruct(S_NON_CURRENT));
**				}
**			}
**			Event(ZafEventStruct(S_DEINITIALIZE));
**			break;
**	#else
END BLOCK COMMENT */
	case S_DESTROY:
		if (focus)
		{
			Event(ZafEventStruct(N_NON_CURRENT));
			Event(ZafEventStruct(S_NON_CURRENT));
		}
		Event(ZafEventStruct(S_DEINITIALIZE));
		break;
//#endif

	// ----- Position & Size messages -----
	case S_COMPUTE_SIZE:
		{
//??? This code is wrong, and never used.
		// Get the text size.
		display->SetDisplayContext(GetDC(screenID));
		display->SetFont(LogicalPalette(ZAF_PM_TEXT, 0).font);
		ZafRegionStruct textSize = display->TextSize(*stringData);
		ReleaseDC(screenID, display->RestoreDisplayContext());

		// Adjust the size.
		zafRegion.left = zafRegion.top = 0;
		zafRegion.right = TreeList()->ClientRegion().Width() - 1;
		zafRegion.bottom = ZafMax(textSize.Height() - 1, ARROW_SIZE - 1);
		zafRegion.bottom = ZafMax(zafRegion.bottom, 
			ZafMax((normalBitmap ? normalBitmap->Height() - 1 : 0), (selectedBitmap ? selectedBitmap->Height() - 1 : 0)));
		}
		break;


	case S_CURRENT:
	case S_NON_CURRENT:
		{
		ccode = ZafWindowObject::Event(event);
		ZafTreeList *treeList = TreeList();
		if (treeList->ItemInView(this))
			Draw(event, ccode);
		}
		break;

	case N_SIZE:
/* START BLOCK COMMENT
**	#if defined(ZAF_WIN32)
**			if (ZafMSWindowsApp::commonControlsAvailable)
**				break;
**	#endif
END BLOCK COMMENT */
		// Compute the tree item's sub-regions.
		{
		int column = zafRegion.left;
		int line = zafRegion.top;

		// expand/compress.
		expandRegion.coordinateType = bitmapRegion.coordinateType =
			textRegion.coordinateType = zafRegion.coordinateType;
		expandRegion.left = column + ZAF_MARGIN_SIZE + ViewLevel() * (ARROW_SIZE + ZAF_MARGIN_SIZE);
		if (expandable)
		{
			expandRegion.top = line + (zafRegion.Height() - ARROW_SIZE) / 2;
			expandRegion.right = expandRegion.left + ARROW_SIZE - 1;
			expandRegion.bottom = expandRegion.top + ARROW_SIZE - 1;
			column = expandRegion.right + ZAF_MARGIN_SIZE;
		}
		else
		{
			column = expandRegion.right = expandRegion.left;
			expandRegion.top = expandRegion.bottom = (zafRegion.top + zafRegion.bottom) / 2;
		}

		// bitmap.
		if (normalBitmap || selectedBitmap)
		{
			int height = ZafMax((normalBitmap ? normalBitmap->Height() : 0), (selectedBitmap ? selectedBitmap->Height() : 0));
			int width = ZafMax((normalBitmap ? normalBitmap->Width() : 0), (selectedBitmap ? selectedBitmap->Width() : 0));
			bitmapRegion.left = column;
			bitmapRegion.top = line + (zafRegion.Height() - height) / 2;
			bitmapRegion.right = bitmapRegion.left + width - 1;
			bitmapRegion.bottom = bitmapRegion.top + height - 1;
			column = bitmapRegion.right + ZAF_MARGIN_SIZE;
		}
		else
		{
			bitmapRegion.left = bitmapRegion.right = -1;
			bitmapRegion.top = bitmapRegion.bottom = -1;
		}

		// text
		display->SetDisplayContext(GetDC(screenID));
		display->SetFont(LogicalPalette(ZAF_PM_TEXT, 0).font);
		ZafRegionStruct textSize = display->TextSize(*stringData);
		ReleaseDC(screenID, display->RestoreDisplayContext());

		textRegion.left = column;
		textRegion.top = zafRegion.top;
//		textRegion.right = zafRegion.right;
		textRegion.right = textRegion.left + textSize.Width() - 1;;
		textRegion.bottom = zafRegion.bottom;
		}
		break;

	case S_REGISTER_OBJECT:
		// Define the children to have the same screenID as their parent.
/* START BLOCK COMMENT
**	#if defined(ZAF_WIN32)
**			if (!ZafMSWindowsApp::commonControlsAvailable)
**				screenID = parent->screenID;
**	#else
END BLOCK COMMENT */
		screenID = parent->screenID;
//#endif
		ccode = ZafWindow::Event(event);
		break;

	// ----- Unknown or error messages -----
	default:
		ccode = ZafWindowObject::Event(event);
		break;
	}

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

ZafRegionStruct ZafTreeItem::MaxRegion(ZafWindowObject *object, ZafVtJustify vWeight, ZafHzJustify hWeight)
{
	return (parent->MaxRegion(object, vWeight, hWeight));
}

ZafWindowObject *ZafTreeItem::NotifySelection(ZafWindowObject *selectedObject, bool setSelected)
{
	// Defer to the parent.
	return (parent->NotifySelection(selectedObject, setSelected));
}

// ----- Atrributes ---------------------------------------------------------

bool ZafTreeItem::SetExpanded(bool setExpanded)
{
	// Make sure the attribute has changed.
	if (expandable && expanded != setExpanded)
	{
		// Update the expanded status.
		expanded = setExpanded;

		// Update the tree.
/* START BLOCK COMMENT
**	#if defined(ZAF_WIN32)
**			if (ZafMSWindowsApp::commonControlsAvailable)
**			{
**				TV_ITEM item;
**				item.mask = TVIF_STATE;
**				item.hItem = (HTREEITEM)screenID;
**				item.stateMask = TVIS_EXPANDED;
**				item.state = expanded ? TVIS_EXPANDED : 0;
**				SendMessage(OSScreenID(ZAF_TREEID), TVM_SETITEM, (WPARAM)0, (LPARAM)&item);
**			}
**			else if (screenID)
**			{
**				ZafTreeList *treeList = TreeList();
**				treeList->ResizeVisibleItems(this);
**				treeList->Event(S_VSCROLL_COMPUTE);
**				treeList->Event(S_REDISPLAY);
**			}
**	#else
END BLOCK COMMENT */
		if (screenID)
		{
			ZafTreeList *treeList = TreeList();
			if (setExpanded)
			{
				if (treeList->ItemInView(this))
					treeList->ResizeVisibleItems(this);
			}
			else
			{
				if (treeList->firstVisible->parent == this)
				{
					treeList->firstVisible = this;
					treeList->ResizeVisibleItems(this);
				}
				else if (treeList->ItemInView(this))
					treeList->ResizeVisibleItems(this);
			}
			treeList->Event(S_VSCROLL_COMPUTE);
			treeList->Event(S_HSCROLL_COMPUTE);
			ZafRegionStruct redrawRegion;
			redrawRegion.left = 0;
			redrawRegion.top = zafRegion.top;
			redrawRegion.right = treeList->ClientRegion().Width() - 1;
			redrawRegion.bottom = (setExpanded && !first) ? zafRegion.bottom : treeList->ClientRegion().Height() - 1;
			treeList->Event(ZafEventStruct(S_REDISPLAY_REGION, 0, redrawRegion));
		}
//#endif
	}

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

bool ZafTreeItem::SetSelected(bool setSelected)
{
	// Defer to the root-base class.
	bool saveSelected = selected;
	ZafWindowObject::SetSelected(setSelected);
	if (selected != saveSelected && screenID && TreeList()->ItemInView(this))
		Draw(ZafEventStruct(L_SELECT), L_SELECT);
	return (selected);
}

void ZafTreeItem::Sort(void)
{
	// Sort on the Zinc level.
	ZafList::Sort();
	for (ZafTreeItem *item = (ZafTreeItem *)first; item; item = (ZafTreeItem *)item->Next())
		item->Sort();
}

bool ZafTreeItem::ToggleExpanded(void)
{
	// Toggle the expansion.
	return (SetExpanded(!expanded));
}

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

OSWindowID ZafTreeItem::OSScreenID(ZafScreenIDType ) const
{
	// Return the os handle.
/* START BLOCK COMMENT
**		if (type == ZAF_TREEID)
**			return parent->OSScreenID(type);
**		else
END BLOCK COMMENT */
		return screenID;
}

void ZafTreeItem::OSRegisterObject(void)
{
/* START BLOCK COMMENT
**	#if defined(ZAF_WIN32)
**		if (ZafMSWindowsApp::commonControlsAvailable)
**		{
**			ZafTreeList *treeList = TreeList();
**
**			int normalImageIndex = normalBitmap ? treeList->ImageList()->AddImage(normalBitmap) : 0;
**			int selectedImageIndex = selectedBitmap ? treeList->ImageList()->AddImage(selectedBitmap) : 0;
**
**			TV_INSERTSTRUCT treeViewInsertStruct;
**			treeViewInsertStruct.hParent = parent->IsA(ID_ZAF_TREE_ITEM) ? (HTREEITEM)parent->screenID : TVI_ROOT;
**			treeViewInsertStruct.hInsertAfter = previous ? (HTREEITEM)Previous()->screenID : TVI_FIRST;
**			treeViewInsertStruct.item.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM | TVIF_STATE;
**			if (normalBitmap)
**				treeViewInsertStruct.item.mask |= TVIF_IMAGE;
**			if (selectedBitmap)
**				treeViewInsertStruct.item.mask |= TVIF_SELECTEDIMAGE;
**			treeViewInsertStruct.item.state = expanded ? TVIS_EXPANDED : 0;
**			treeViewInsertStruct.item.stateMask = TVIS_EXPANDED;
**	//??? Character conversion needed...
**			treeViewInsertStruct.item.pszText = (char *)stringData->Text();
**			treeViewInsertStruct.item.iImage = normalImageIndex;
**			treeViewInsertStruct.item.iSelectedImage = selectedImageIndex;
**			treeViewInsertStruct.item.cChildren = first ? 1 : 0;
**			treeViewInsertStruct.item.lParam = (LPARAM)this;
**
**			screenID = (OSWindowID)SendMessage(OSScreenID(ZAF_TREEID), TVM_INSERTITEM, (WPARAM)0, (LPARAM)&treeViewInsertStruct);
**		}
**		else
**			screenID = parent->screenID;
**	#else
END BLOCK COMMENT */
	screenID = parent->screenID;
//#endif
}

// ----- Stubs --------------------------------------------------------------

ZafPositionStruct ZafTreeItem::ConvertToOSPosition(const ZafWindowObject *object,
	const ZafPositionStruct *zafPosition) const
{
	return (ZafWindowObject::ConvertToOSPosition(object, zafPosition));
}

ZafPositionStruct ZafTreeItem::ConvertToZafPosition(const ZafWindowObject *object,
	const ZafPositionStruct *osPosition) const
{
	return (ZafWindowObject::ConvertToZafPosition(object, osPosition));
}

ZafEventType ZafTreeItem::DragDropEvent(const ZafEventStruct &event)
{
	return (ZafWindowObject::DragDropEvent(event));
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafTreeItem::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	// Defer to the base class.
	return (parent->DrawFocus(region, ccode));
}
#endif

void ZafTreeItem::GetClip(ZafWindowObject *, ZafRegionStruct &, ZafRegionStruct &)
{
	// Handled automatically by Windows.
}

void ZafTreeItem::OSDestroy(void)
{
	// Defer to the base class.
	ZafWindow::OSDestroy();
}

ZafEventType ZafTreeItem::OSEvent(const ZafEventStruct &event)
{
	return (ZafWindow::OSEvent(event));
}

void ZafTreeItem::OSSize(void)
{
	ZafWindowObject::OSSize();
}

ZafWindowObject *ZafTreeItem::NotifyFocus(ZafWindowObject *object, bool focus)
{
	return (ZafWindow::NotifyFocus(object, focus));
}

ZafPaletteState ZafTreeItem::PaletteState(void)
{
	ZafPaletteState state = ZafWindowObject::PaletteState();
	if (TreeList()->Current() == this && Focus())
		state |= ZAF_PM_CURRENT;
	else
		state &= ~ZAF_PM_CURRENT;

	return (state);
}

