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

#include <z_mouse2.hpp>
#include <z_split.hpp>
#include <z_string.hpp>
#define ZAF_SPLITTER_INFO
#include "gbl_def.cpp"

#define DEFAULT_THICKNESS 3

// ----- ZafSplitter --------------------------------------------------------

ZafSplitter::ZafSplitter(int left, int top, int width, int height,
	ZafSplitterType _splitterType, int _position, bool setPercent) :
	ZafWindowObject(left, top, width, height),
	percent(setPercent), position(_position), thickness(DEFAULT_THICKNESS),
	splitterType(_splitterType), nextPaneObject(ZAF_NULLP(ZafWindowObject)),
	previousPaneObject(ZAF_NULLP(ZafWindowObject)), nextPaneObjectID(0),
	previousPaneObjectID(0)
{
	SetLive(true);
	SetFont(ZAF_FNT_NULL);
	SetNoncurrent(true);
	SetOSDraw(false);
	splitRegion = zafRegion;
}

ZafSplitter::ZafSplitter(ZafSplitterType _splitterType, int _position, bool setPercent) :
	ZafWindowObject(0, 0, 0, 0),
	percent(setPercent), position(_position), thickness(DEFAULT_THICKNESS),
	splitterType(_splitterType), nextPaneObject(ZAF_NULLP(ZafWindowObject)),
	previousPaneObject(ZAF_NULLP(ZafWindowObject)), nextPaneObjectID(0),
	previousPaneObjectID(0)
{
	SetLive(true);
	SetFont(ZAF_FNT_NULL);
	SetNoncurrent(true);
	SetOSDraw(false);
	splitRegion = zafRegion;
}

ZafSplitter::ZafSplitter(const ZafSplitter &copy) :
	ZafWindowObject(copy), live(copy.live), percent(copy.percent),
	position(copy.position), thickness(copy.thickness),
	splitRegion(copy.splitRegion), splitterType(copy.splitterType),
	nextPaneObject(ZAF_NULLP(ZafWindowObject)),
	previousPaneObject(ZAF_NULLP(ZafWindowObject)),
	nextPaneObjectID(copy.nextPaneObjectID),
	previousPaneObjectID(copy.previousPaneObjectID)
{
	if (!nextPaneObjectID && copy.nextPaneObject)
		nextPaneObjectID = copy.nextPaneObject->NumberID();
	if (!previousPaneObjectID && copy.previousPaneObject)
		previousPaneObjectID = copy.previousPaneObject->NumberID();
}

ZafSplitter::~ZafSplitter(void)
{
}

void ZafSplitter::ConvertCoordinates(ZafCoordinateType newCoordinateType)
{
	if (!percent)
	{
		ZafRegionStruct convertRegion;
		convertRegion.coordinateType = splitRegion.coordinateType;
		convertRegion.left = convertRegion.top = position;
		convertRegion.right = convertRegion.bottom = 0;

		// Don't use absolute coordinates.
		ZafRegionType saveRegionType = regionType;
		regionType = ZAF_INSIDE_REGION;
		ConvertRegion(convertRegion, newCoordinateType);
		regionType = saveRegionType;

		if (splitterType == ZAF_HORIZONTAL_SPLITTER)
			position = convertRegion.top;
		else
			position = convertRegion.left;
	}

	ConvertRegion(splitRegion, newCoordinateType);
	ZafWindowObject::ConvertCoordinates(newCoordinateType);
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafSplitter::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	ZafRegionStruct drawRegion = BeginDraw();

	// Get the state of the object.
	ZafPaletteState state = PaletteState();

	if (bordered)
	{
		// Get 3D "shadow" colors.
		// display->Rectangle() is used to draw lines with the background
		// colors of the palettes.
		ZafPaletteStruct lightShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
		ZafPaletteStruct darkShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);

		display->SetPalette(lightShadow);
		if (splitterType == ZAF_HORIZONTAL_SPLITTER)
		{
			display->Rectangle(drawRegion.left, drawRegion.top, drawRegion.right, drawRegion.top, 0, true);
			drawRegion.top++;
			display->Line(drawRegion.left, drawRegion.top, drawRegion.right, drawRegion.top);
			drawRegion.top++;
		}
		else
		{
			display->Rectangle(drawRegion.left, drawRegion.top, drawRegion.left, drawRegion.bottom, 0, true);
			drawRegion.left++;
			display->Line(drawRegion.left, drawRegion.top, drawRegion.left, drawRegion.bottom);
			drawRegion.left++;
		}
		display->SetPalette(darkShadow);
		if (splitterType == ZAF_HORIZONTAL_SPLITTER)
		{
			display->Rectangle(drawRegion.left, drawRegion.bottom, drawRegion.right, drawRegion.bottom, 0, true);
			drawRegion.bottom--;
			display->Line(drawRegion.left, drawRegion.bottom, drawRegion.right, drawRegion.bottom);
			drawRegion.bottom--;
		}
		else
		{
			display->Rectangle(drawRegion.right, drawRegion.top, drawRegion.right, drawRegion.bottom, 0, true);
			drawRegion.right--;
			display->Line(drawRegion.right, drawRegion.top, drawRegion.right, drawRegion.bottom);
			drawRegion.right--;
		}
	}

	DrawBackground(drawRegion, ccode);

	EndDraw();

	return (ccode);
}
#endif

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

ZafEventType ZafSplitter::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = LogicalEvent(event);

	switch (ccode)
	{
	case S_INITIALIZE:
		ccode = ZafWindowObject::Event(event);
		if (!nextPaneObject && nextPaneObjectID)
			SetNextPaneObject(parent->GetObject(nextPaneObjectID));
		if (!previousPaneObject && previousPaneObjectID)
			SetPreviousPaneObject(parent->GetObject(previousPaneObjectID));
		break;

	case N_SIZE:
	case N_MOVE:
		if (previousPaneObject)
		{
			previousPaneObject->OSSize();
#if defined(ZAF_MSWINDOWS)
			if (Live())
				UpdateWindow(previousPaneObject->OSScreenID(ZAF_FRAMEID));
#elif defined(ZAF_INDEPENDENT)
			if (Live())
				windowManager->RepairDamage();
#endif
			// Make sure the size is propogated, there might be
			// more splitters.
			if (previousPaneObject->IsA(ID_ZAF_SPLITTER))
				previousPaneObject->Event(N_SIZE);
		}
		if (nextPaneObject)
		{
			nextPaneObject->OSSize();
#if defined(ZAF_MSWINDOWS)
			if (Live())
				UpdateWindow(nextPaneObject->OSScreenID(ZAF_FRAMEID));
#elif defined(ZAF_INDEPENDENT)
			if (Live())
				windowManager->RepairDamage();
#endif
			// Make sure the size is propogated, there might be
			// more splitters.
			if (nextPaneObject->IsA(ID_ZAF_SPLITTER))
				nextPaneObject->Event(N_SIZE);
		}
		break;

	case L_BEGIN_SELECT:
		if (!Disabled())
			Modify(event);
		break;

	case N_MOUSE_ENTER:
		ccode = ZafWindowObject::Event(event);
		if (!Disabled())
		{
			if (splitterType == ZAF_HORIZONTAL_SPLITTER)
				eventManager->SetDeviceImage(E_MOUSE, DM_HORIZONTAL_SPLIT);
			else
				eventManager->SetDeviceImage(E_MOUSE, DM_VERTICAL_SPLIT);
		}
		break;

	case N_MOUSE_LEAVE:
		ccode = ZafWindowObject::Event(event);
		if (!Disabled() && !editMode)
#if defined(ZAF_MOTIF)
			XUndefineCursor(((ZafScreenDisplay *)display)->xDisplay,
				XtWindow(OSScreenID()));
#endif
			eventManager->SetDeviceImage(E_MOUSE, DM_VIEW);
		break;

#if defined(ZAF_MSWINDOWS)
	case E_OSEVENT:
		if (!Disabled() && event.osEvent.message == WM_SETCURSOR)
			ccode = TRUE;
		else
			ccode = ZafWindowObject::Event(event);
		break;
#elif defined(ZAF_MACINTOSH)
	case L_VIEW:
		// Block from going to the base class, since we don't want the mouse
		// image to reset. The image was already set in N_MOUSE_ENTER.
		break;
#endif

	default:
		ccode = ZafWindowObject::Event(event);
	}

	return (ccode);
}

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

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

ZafEventType ZafSplitter::Modify(const ZafEventStruct &modifyEvent)
{
	editMode = true;
	ZafPositionStruct originalPosition = modifyEvent.position;

	if (percent)
	{
		if (splitterType == ZAF_HORIZONTAL_SPLITTER)
			position = zafRegion.top - splitRegion.top;
		else
			position = zafRegion.left - splitRegion.left;
		percent = false;
	}

#if defined(ZAF_MSWINDOWS)
	SetCapture(modifyEvent.osEvent.hwnd);
#elif defined(ZAF_MACINTOSH)
	windowManager->SetMouseEventRoute(this);
#endif

	int deltaPosition = 0;
	if (!live)
		XOR(deltaPosition);

	ZafEventType ccode;
	do
	{
		ZafEventStruct event;
		eventManager->Get(event, Q_NORMAL);
		ccode = LogicalEvent(event);

		// Process the event.
		if (ccode == L_CONTINUE_SELECT || ccode == L_END_SELECT)
		{
			if (!live)
				XOR(deltaPosition);

			int maxPosition;
			if (splitterType == ZAF_HORIZONTAL_SPLITTER)
			{
				deltaPosition = event.position.line - originalPosition.line;
				maxPosition = splitRegion.Height() - thickness - (bordered ? 2 * ZAF_BORDER_SIZE : 0);
			}
			else
			{
				deltaPosition = event.position.column - originalPosition.column;
				maxPosition = splitRegion.Width() - thickness - (bordered ? 2 * ZAF_BORDER_SIZE : 0);
			}

			if (position + deltaPosition < 0)
				deltaPosition = -position;
			else if (position + deltaPosition > maxPosition)
				deltaPosition = maxPosition - position;

			if (live)
			{
				position += deltaPosition;
				SetRegion(splitRegion);
				OSSize();
#if defined(ZAF_MSWINDOWS)
				UpdateWindow(screenID);
#elif defined(ZAF_INDEPENDENT)
				windowManager->RepairDamage();
#endif
			}
			else
				XOR(deltaPosition);

//			if (ccode == L_END_SELECT)
//				windowManager->Event(event);
		}
#if defined(ZAF_MSWINDOWS)
		else if (!live && event.type == E_OSEVENT &&
			(event.osEvent.message == WM_PAINT ||
			event.osEvent.message == WM_DRAWITEM))
		{
			XOR(deltaPosition);
			windowManager->Event(event);
			XOR(deltaPosition);
		}
#endif
		else
			windowManager->Event(event);

	} while (ccode != L_END_SELECT);

	if (!live)
	{
		XOR(deltaPosition);
		position += deltaPosition;
		SetRegion(splitRegion);
		OSSize();
#if defined(ZAF_MOTIF)
		Event(N_MOVE);
#endif
	}

#if defined(ZAF_MSWINDOWS)
	ReleaseCapture();
#elif defined(ZAF_MACINTOSH)
	windowManager->SetMouseEventRoute(ZAF_NULLP(ZafWindowObject));
#endif

	editMode = false;
	return (ccode);
}

int ZafSplitter::Position(bool tPercent) const
{
	if (tPercent && !percent)
	{
		int divisor;
		// Make sure that the divisor is not equal to 0.
		if (splitterType == ZAF_HORIZONTAL_SPLITTER)
			divisor = splitRegion.Height() - thickness - (bordered ? 2 * ZAF_BORDER_SIZE : 0);
		else
			divisor = splitRegion.Width() - thickness - (bordered ? 2 * ZAF_BORDER_SIZE : 0);
		if (divisor < 1)
			return (100);
		else
			return ((int)(((position * 100L + (divisor / 2 - 1)) / divisor)));
	}
	else
		return (position);
}

ZafRegionStruct ZafSplitter::Region(void) const
{
	return (splitRegion);
}

void ZafSplitter::XOR(int tPosition)
{
	ZafRegionStruct drawRegion = parent->BeginDraw();
	ZafRegionStruct barRegion = zafRegion;
	barRegion.left += drawRegion.left;
	barRegion.right += drawRegion.left;
	barRegion.top += drawRegion.top;
	barRegion.bottom += drawRegion.top;

 	if (splitterType == ZAF_HORIZONTAL_SPLITTER)
	{
		barRegion.top += tPosition - (Bordered() ? 0 : ZAF_BORDER_SIZE);
		barRegion.bottom = barRegion.top + thickness + 2 * ZAF_BORDER_SIZE - 1;
	}
	else
	{
		barRegion.left += tPosition - (Bordered() ? 0 : ZAF_BORDER_SIZE);
		barRegion.right = barRegion.left + thickness + 2 * ZAF_BORDER_SIZE - 1;
	}

#if defined(ZAF_MOTIF)
	Widget frame = OSScreenID(ZAF_FRAMEID);
	Widget boundary = XtParent(frame);
	OSDisplayContext xDisplay = XtDisplay(boundary); 
	Drawable xDrawable = XtWindow(boundary);
	XGCValues gcValues;
	gcValues.function = GXinvert;
	gcValues.line_width = 2;
	gcValues.line_style = LineSolid;
	gcValues.subwindow_mode = IncludeInferiors;
	gcValues.background = gcValues.foreground = (unsigned long)~0;
	GC xorGC = XCreateGC(xDisplay, xDrawable, GCFunction | GCLineWidth |
		GCLineStyle | GCSubwindowMode | GCForeground | GCBackground,
		&gcValues);
	XDrawRectangle(xDisplay, xDrawable, xorGC, barRegion.left, barRegion.top,
		barRegion.Width(), barRegion.Height());
	XFreeGC(xDisplay, xorGC);
#else
	display->SetMode(ZAF_MODE_XOR);
	display->SetForeground(ZAF_CLR_WHITE);
	display->SetBackground(ZAF_CLR_WHITE);
	display->Rectangle(barRegion, 1, true);
#endif

	EndDraw();
}

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

bool ZafSplitter::SetCopyDraggable(bool)
{
	// copyDraggable is false for this class.
	copyDraggable = false;
	return (copyDraggable);
}

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

ZafLogicalFont ZafSplitter::SetFont(ZafLogicalFont)
{
	// font is ZAF_FNT_NULL for this class.
	return (ZAF_FNT_NULL);
}

const ZafIChar *ZafSplitter::SetHelpContext(const ZafIChar *)
{
	// helpContext is null for this class.
	return (ZAF_NULLP(ZafIChar));
}

bool ZafSplitter::SetLinkDraggable(bool)
{
	// linkDraggable is false for this class.
	linkDraggable = false;
	return (linkDraggable);
}

#if defined(ZAF_MOTIF) || defined(ZAF_INDEPENDENT)
bool ZafSplitter::SetLive(bool)
{
	live = false;
#else
bool ZafSplitter::SetLive(bool setLive)
{
	live = setLive;
#endif
	return (live);
}

bool ZafSplitter::SetMoveDraggable(bool)
{
	// moveDraggable is false for this class.
	moveDraggable = false;
	return (moveDraggable);
}

void ZafSplitter::SetNextPaneObject(ZafWindowObject *object)
{
 	nextPaneObject = object;
	if (screenID && nextPaneObject)
	{
		SetRegion(splitRegion);
		nextPaneObject->OSSize();
	}
}

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

bool ZafSplitter::SetOSDraw(bool)
{
	// osDraw is false for this class.
	osDraw = false;
	return (osDraw);
}

bool ZafSplitter::SetParentDrawBorder(bool)
{
	// parentDrawBorder is false for this class.
	parentDrawBorder = false;
	return (parentDrawBorder);
}

bool ZafSplitter::SetParentDrawFocus(bool)
{
	// parentDrawFocus is false for this class.
	parentDrawFocus = false;
	return (parentDrawFocus);
}

void ZafSplitter::SetPosition(int setPosition, bool setPercent)
{
	position = setPosition;
	percent = setPercent;

	if (screenID)
	{
		if (percent && Visible())
		{
			if (splitterType == ZAF_HORIZONTAL_SPLITTER)
			{
				zafRegion.top = splitRegion.top + (int)((splitRegion.Height() -
					thickness - (bordered ? 2 * ZAF_BORDER_SIZE : 0)) *
					(long)position / 100);
				position = zafRegion.top = splitRegion.top;
				zafRegion.bottom = zafRegion.top + thickness + (bordered ? 2 * ZAF_BORDER_SIZE : 0) - 1;
			}
			else
			{
				zafRegion.left = splitRegion.left + (int)((splitRegion.Width() -
					thickness - (bordered ? 2 * ZAF_BORDER_SIZE : 0)) *
					(long)position / 100);
				position = zafRegion.left - splitRegion.left;
				zafRegion.right = zafRegion.left + thickness + (bordered ? 2 * ZAF_BORDER_SIZE : 0) - 1;
			}
			percent = false;
		}

		SetRegion(splitRegion);
		OSSize();
	}
}

void ZafSplitter::SetPreviousPaneObject(ZafWindowObject *object)
{
 	previousPaneObject = object;
	if (screenID && previousPaneObject)
	{
		SetRegion(splitRegion);
		previousPaneObject->OSSize();
	}
}

void ZafSplitter::SetRegion(const ZafRegionStruct &region)
{
	if (percent && screenID && Visible())
	{
		if (splitterType == ZAF_HORIZONTAL_SPLITTER)
			position = zafRegion.top - splitRegion.top;
		else
			position = zafRegion.left - splitRegion.left;
		percent = false;
	}

	zafRegion = splitRegion = region;

	if (splitterType == ZAF_HORIZONTAL_SPLITTER)
	{
		if (percent)
			zafRegion.top = splitRegion.top + (int)((splitRegion.Height() -
				thickness - (bordered ? 2 * ZAF_BORDER_SIZE : 0)) *
				(long)position / 100);
		else
			zafRegion.top = splitRegion.top + position;
		zafRegion.bottom = zafRegion.top + thickness + (bordered ? 2 * ZAF_BORDER_SIZE : 0) - 1;
	}
	else
	{
		if (percent)
			zafRegion.left = splitRegion.left + (int)((splitRegion.Width() -
				thickness - (bordered ? 2 * ZAF_BORDER_SIZE : 0)) *
				(long)position / 100);
		else
			zafRegion.left = splitRegion.left + position;
		zafRegion.right = zafRegion.left + thickness + (bordered ? 2 * ZAF_BORDER_SIZE : 0) - 1;
	}

	if (previousPaneObject)
	{
		ZafRegionStruct paneRegion = splitRegion;
	 	if (splitterType == ZAF_HORIZONTAL_SPLITTER)
			paneRegion.bottom = zafRegion.top - 1;
		else
			paneRegion.right = zafRegion.left - 1;
		previousPaneObject->SetRegion(paneRegion);
	}

	if (nextPaneObject)
	{
		ZafRegionStruct paneRegion = splitRegion;
	 	if (splitterType == ZAF_HORIZONTAL_SPLITTER)
			paneRegion.top = zafRegion.bottom + 1;
		else
			paneRegion.left = zafRegion.right + 1;
		nextPaneObject->SetRegion(paneRegion);
	}
}

bool ZafSplitter::SetSelected(bool)
{
	// selected is false for this class.
	selected = false;
	return (selected);
}

void ZafSplitter::SetSplitterType(ZafSplitterType setSplitterType)
{
	if (!screenID)
		splitterType = setSplitterType;
}

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

ZafLogicalColor ZafSplitter::SetTextColor(ZafLogicalColor, ZafLogicalColor)
{
	// text color is ZAF_CLR_NULL for this class.
	return (ZAF_CLR_NULL);
}

void ZafSplitter::SetThickness(int setThickness)
{
	if (!screenID)
		thickness = setThickness;
}	

// ----- Persistence --------------------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafSplitter::ZafSplitter(const ZafIChar *name, ZafObjectPersistence &persist) :
	ZafWindowObject(name, persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY)),
	nextPaneObject(ZAF_NULLP(ZafWindowObject)), previousPaneObject(ZAF_NULLP(ZafWindowObject)),
	nextPaneObjectID(0), previousPaneObjectID(0)
{
	splitRegion = zafRegion;

	// Read the data.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafSplitter::ReadData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());

#if defined(ZAF_MOTIF)
	SetLive(false);
#endif
	SetFont(ZAF_FNT_NULL);
	SetNoncurrent(true);
}

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

void ZafSplitter::ReadData(ZafObjectPersistence &persist)
{
	ZafFile *file = persist.CurrentFile();

	// Read the data.
	ZafUInt16 flags;
	*file >> flags >> position >> thickness >> nextPaneObjectID >> previousPaneObjectID;

	splitterType = (ZafSplitterType)(((int)flags & 0x000F));
	SetLive((flags & 0x0010) ? true : false);
	percent = (flags & 0x0020) ? true : false;
}

void ZafSplitter::Write(ZafObjectPersistence &persist)
{
	// Write the object.
	ZafRegionStruct tempRegion = zafRegion;
	zafRegion = splitRegion;
	ZafWindowObject::Write(persist.PushLevel(className, classID, ZAF_PERSIST_DIRECTORY));
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafSplitter::WriteData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
	zafRegion = tempRegion;
}

void ZafSplitter::WriteData(ZafObjectPersistence &persist)
{
	// Write the data.
	ZafUInt16 flags = 0;
	flags |= (ZafUInt16)(splitterType & 0x000F);
	flags |= (ZafUInt16)(live ? 0x0010 : 0);
	flags |= (ZafUInt16)(percent ? 0x0020 : 0);

	ZafRegionStruct convertRegion;
	convertRegion.coordinateType = ZAF_PIXEL;
	convertRegion.left = convertRegion.top = position;
	convertRegion.right = convertRegion.bottom = 0;

	// Don't use absolute coordinates.
	ZafRegionType saveRegionType = regionType;
	regionType = ZAF_INSIDE_REGION;
	ConvertRegion(convertRegion, coordinateType);
	regionType = saveRegionType;

	int savePosition;
	if (splitterType == ZAF_HORIZONTAL_SPLITTER)
		savePosition = convertRegion.top;
	else
		savePosition = convertRegion.left;


	ZafFile *file = persist.CurrentFile();
	*file << flags << savePosition << thickness;
	*file << (ZafInt16)(nextPaneObject ? nextPaneObject->NumberID() : 0);
	*file << (ZafInt16)(previousPaneObject ? previousPaneObject->NumberID() : 0);
}
#endif
