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

#include <z_title.hpp>
#include "m_notebk.hpp"

// The default size of a scrollbar is hard-coded in Motif 1.2 to be 11.  For
// more information on the default size see the static Initialize() function
// in ScrollBar.c.  In addition, the initial and repeat delay times were
// taken from ScrollBar.c as the default values.
const int ARROW_SIZE	= 11;

// ----- MtfNotebook widget definition --------------------------------------

XmOffsetPtr MtfNotebook::offsets;

XtActionsRec MtfNotebook::action[] =
{
	{ "ManagerGadgetArm",			(XtActionProc)arm },
	{ "ManagerGadgetMultiArm", 		(XtActionProc)arm },	
	{ "ManagerGadgetActivate",		(XtActionProc)activate },
	{ "ManagerGadgetMultiActive",	(XtActionProc)activate },
	{ "ZafCallDefault",				(XtActionProc)call_default },
	{ "ZafDragDrop",				(XtActionProc)drag_drop },
	{ "ZafEnter",					(XtActionProc)enter },
	{ "ZafHelp", 					(XtActionProc)help },
	{ "ZafLeave",					(XtActionProc)leave }
};

char MtfNotebook::translations[] =
	"<Key>osfActivate:		ZafCallDefault()\n\
	 <Key>osfSelect:		ZafCallDefault()\n\
	 ~s ~m ~a <Key>Return:	ZafCallDefault()\n\
	 <Btn2Down>:			ZafDragDrop()\n\
	 <Enter>:				ZafEnter()\n\
	 <Key>F1:				ZafHelp()\n\
	 <Key>osfHelp:			ZafHelp()\n\
	 <Leave>:				ZafLeave()";

MtfNotebookClassRec MtfNotebook::classRecord =
{
	// --- CoreClassPart ---
	{
		(WidgetClass)&xmManagerClassRec,	// superclass
		"ZafNotebook",						// class_name
		sizeof(MtfNotebookPart), 			// widget_size (data part)
		MtfNotebook::classInitialize,		// class_initialize
		0,									// class_part_initialize
		false,								// class inited
		0,									// initialize
		0,									// initialize_hook
		XtInheritRealize,					// realize
		action, 							// actions
		XtNumber(action), 					// num_actions
		0, 									// resources
		0, 									// num_resources
		NULLQUARK,							// xrm
		true,								// compress_motion
		XtExposeCompressMultiple,			// compress_exposure
		true,								// compress_enterleave
		false,								// visible_interest
		0, 									// destroy
		(XtWidgetProc)resize,				// resize
		(XtExposeProc)expose,				// expose
		0,									// set_values
		0, 									// set_values_hook
		XtInheritSetValuesAlmost,			// set_values_almost
		0, 									// get_values_hook
		XtInheritAcceptFocus,				// accept_focus
		XtVersionDontCheck,					// version
		0, 									// callback_private
		XtInheritTranslations,				// tm_table
		XtInheritQueryGeometry, 			// query_geometry
		XtInheritDisplayAccelerator,		// display_accelerator
		0 									// extension
	},
	// --- CompositeClassPart ---
	{
		gmgr,								// geometry_manager
		XtInheritChangeManaged,				// change_managed
		XtInheritInsertChild,				// insert_child
		XtInheritDeleteChild,				// delete_child
		0									// extension
	},
	// --- ConstraintClassPart ---
	{
		0,									// resources
		0,									// num_resources
		0,									// constraint_size
		0,									// initialize
		0,									// destroy
		0,									// set_values
		0									// extension
	},
	// --- XmManagerClassPart ---
	{
		XtInheritTranslations,				// translations
		0,									// syn_resources
		0,									// num_syn_resources
		0,									// syn_constraint_resources
		0,									// num_syn_constraint_resources
		XmInheritParentProcess,				// parent_process
		0									// extension
	},
	// --- MtfNotebookClassPart ---
	{
		0									// extension
	}
};

void MtfNotebook::activate(MtfNotebook *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This action occurs on the first "up-click" portion of a mouse click.

	// Match up zinc.
	ZafWindow *zafObject = GetZincObject(mtfObject);
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);
	event.type = L_END_SELECT;
	zafObject->Event(event);
}

void MtfNotebook::arm(MtfNotebook *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This action occurs on the first "down-click" portion of a mouse click.

	// Try to find a matching tab.
	ZafNotebook *zafObject = GetZincObject(mtfObject);
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);

	event.type = L_BEGIN_SELECT;
	zafObject->Event(event);
}

void MtfNotebook::classInitialize()
{
	XmResolvePartOffsets((WidgetClass) &(MtfNotebook::classRecord),
	 &(MtfNotebook::offsets));
}

void MtfNotebook::drag_drop(MtfNotebook *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// Begin the drag/drop operation.
	ZafEventStruct event(S_BEGIN_DRAG, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

void MtfNotebook::enter(MtfNotebook *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Perform the motif enter and match with zinc.
	XtCallActionProc((Widget)mtfObject, "ManagerEnter", xEvent, param, *nparams);
	ZafEventStruct event(N_MOUSE_ENTER, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

void MtfNotebook::expose(MtfNotebook *mtfObject, XEvent *xEvent, Region xRegion)
{
	// Draw the object.
	ZafEventStruct event(E_MOTIF, xEvent);
	XRectangle xRect;
	XClipBox(xRegion, &xRect);
  	event.region.Assign(xRect.x, xRect.y, xRect.width, xRect.height, ZAF_PIXEL);
	GetZincObject(mtfObject)->Draw(event, E_MOTIF);
}

XtGeometryResult MtfNotebook::gmgr(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry * )
{
	// Update the child geometry.
	if (request->request_mode & CWX)
		widget->core.x = request->x;
	if (request->request_mode & CWY)
		widget->core.y = request->y;
	if (request->request_mode & CWWidth)
		widget->core.width = request->width;
	if (request->request_mode & CWHeight)
		widget->core.height = request->height;
	if (request->request_mode & CWBorderWidth)
		widget->core.border_width = request->border_width;
    return (XtGeometryYes);
}

void MtfNotebook::leave(MtfNotebook *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Perform the motif enter and match with zinc.
	XtCallActionProc((Widget)mtfObject, "ManagerLeave", xEvent, param, *nparams);
	ZafEventStruct event(N_MOUSE_LEAVE, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

// ----- ZafNotebookTab -----------------------------------------------------

enum ZafTabType
{
	ZAF_TAB_LEFT,
	ZAF_TAB_FULL,
	ZAF_TAB_RIGHT
};

class ZafNotebookTab : public ZafButton
{
public:
	ZafTabType tabType;

	ZafNotebookTab(ZafStringData *stringData);
	ZafNotebookTab(const ZafNotebookTab &copy);
	ZafWindowObject *Duplicate(void) { return (new ZafNotebookTab(*this)); }
	ZafEventType Draw(const ZafEventStruct &event, ZafEventType ccode);
	ZafEventType DrawShadow(ZafRegionStruct &region, int depth, ZafEventType ccode);
	ZafEventType Event(const ZafEventStruct &event);
	bool SetSupportObject(bool supportObject);

	ZafNotebookTab *Next(void) const { return((ZafNotebookTab *)next); }
	ZafNotebookTab *Previous(void) const { return((ZafNotebookTab *)previous); }
};

ZafNotebookTab::ZafNotebookTab(ZafStringData *zStringData) :
	ZafButton(0, 0, 0, 1, zStringData, ZAF_NULLP(ZafBitmapData), ZAF_NATIVE_BUTTON),
	tabType(ZAF_TAB_FULL)
{
	SetAllowToggling(false);
	SetSelectOnDownClick(true);
	SetHzJustify(ZAF_HZ_CENTER);
	SetSupportObject(true);
	SetSystemObject(false);
	SetOSDraw(false);
	SetNoncurrent(true);
	SetDepth(0);
	SetParentPalette(true);
}

ZafNotebookTab::ZafNotebookTab(const ZafNotebookTab &copy) :
	ZafButton(copy)
{
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafNotebookTab::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Check for valid draw operations.
	if (!screenID || tabType != ZAF_TAB_FULL)
		return (S_ERROR);

	// Compute the actual draw region.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();
	ZafNotebook *notebook = DynamicPtrCast(parent, ZafNotebook);
	ccode = (notebook->SupportCurrent() == this) ? S_CURRENT : S_NON_CURRENT;

	// Draw the tab border and text.
	DrawShadow(drawRegion, ZAF_SHADOW_SIZE, ccode);
	display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));
	display->Text(drawRegion, Text(), -1, hzJustify, vtJustify, hotKeyIndex);

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

ZafEventType ZafNotebookTab::DrawShadow(ZafRegionStruct &region, int shadowDepth, ZafEventType ccode)
{
	int leftFill = 1;
	int border = shadowDepth;
	ZafPaletteState state = PaletteState();
	ZafPaletteStruct topShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
	ZafPaletteStruct bottomShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);

	// Clear the background.
	if (ccode != S_CURRENT)
	{
		display->SetPalette(parent->parent->LogicalPalette(ZAF_PM_BACKGROUND, state));
		display->Rectangle(region.left, region.top, region.right, region.top + shadowDepth + 1, 0, true);
		region.top += shadowDepth;
		border = 1;
	}

	// Draw the shadow.
	for (int i = 0; i < border; i++)
	{
		// Left.
		display->SetPalette(topShadow);
		display->Line(region.left, region.top + shadowDepth, region.left,
			region.bottom + leftFill, 1);
		display->Line(region.left, region.top + shadowDepth,
			region.left + shadowDepth, region.top, 1);
		if (i + 1 != border) // fill space created by 45 degree angle.
			display->Line(region.left, region.top + shadowDepth + 1,
				region.left + shadowDepth + 1, region.top, 1);
		if (previous)
			leftFill--;

		// Top.
		display->Line(region.left + shadowDepth, region.top,
			region.right - shadowDepth - 1, region.top, 1);

		// Right.
		display->SetPalette(bottomShadow);
		display->Line(region.right - shadowDepth, region.top,
			region.right, region.top + shadowDepth, 1);
		if (i + 1 != border) // fill space created by 45 degree angle.
			display->Line(region.right - shadowDepth - 1, region.top,
				region.right, region.top + shadowDepth + 1, 1);
		display->Line(region.right, region.top + shadowDepth,
			region.right, region.bottom, 1);

		// Update the tab region.
		region.top++;
		region.left++;
		region.right--;
	}

	// Clear the rest of the button.
	display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
	display->Rectangle(region.left + 1, region.top, region.right - 1, region.top, 0, true);
	display->Rectangle(region.left, region.top + 1, region.right, region.bottom, 0, true);
	region--;

	// Return the status.
	return (ccode);
}
#endif

ZafEventType ZafNotebookTab::Event(const ZafEventStruct &event)
{
	// Check for zinc events.
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
	case S_DEINITIALIZE:
		ccode = ZafButton::Event(event);
		break;

	case S_COMPUTE_SIZE:
		// Adjust the tab size.
		if (parent->screenID)
		{
  			BeginDraw();
			display->SetPalette(LogicalPalette(ZAF_PM_TEXT, ZAF_PM_ANY_STATE));
			ZafRegionStruct region = display->TextSize(Text());
			EndDraw();
			zafRegion.right = zafRegion.left + region.Width() +
				2 * (ZAF_BORDER_SIZE + ZAF_SHADOW_SIZE + ZAF_MARGIN_SIZE);
			zafRegion.bottom = zafRegion.top + region.Height();
		}
		break;

	// ----- Actions -----
	case S_CURRENT:
	case S_NON_CURRENT:
		// Update the tab.
		Draw(event, ccode);
		break;

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

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

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

// ----- ZafNotebook --------------------------------------------------------

ZafWindowObject *ZafNotebook::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Modify the addition to be the notebook page, not it's tab.
	if (object->parent)
		return (object);
	else if (object->SupportObject())
		return (ZafWindow::Add(object, position));

	// Check for a new page.
	if (Index(object) == -1)
	{
		// Create the notebook tab.
		ZafWindow *window = DynamicPtrCast(object, ZafWindow);
		ZafTitle *title = window->Title();
		if (title)
		{
			ZafWindowObject *tabPosition = ZAF_NULLP(ZafWindowObject);
			if (position)
				tabPosition = DynamicPtrCast(support.Get(Index(position)), ZafWindowObject);
			ZafWindowObject *tab = new ZafNotebookTab(new ZafStringData(title->Text()));
			ZafWindow::Add(tab, tabPosition);

			// Set the page disabled if necessary.
			tab->SetDisabled(window->Disabled());

			window->ZafWindow::Subtract(title);
			delete title;
		}
		else
			ZafWindow::Add(new ZafNotebookTab(new ZafStringData()));
	}

	// Add the notebook page.
	object->SetParentPalette(true);
	ZafWindow::Add(object, position);
	if (object == current)
		support.SetCurrent(support.Get(Index(object)));
	else if (screenID)
	{
		XtUnmanageChild(object->OSScreenID(ZAF_FRAMEID));
		Event(N_SIZE);
	}

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

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafNotebook::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	// Compute the actual draw region.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();
	ZafRegionStruct clip = drawRegion;
	if (ccode == E_MOTIF || ccode == S_REDISPLAY_REGION)
		clip = event.region;                            

	// Compute the scroll-arrows.
	ZafRegionStruct arrowRegion;
	arrowRegion.coordinateType = ZAF_PIXEL;
	arrowRegion.left = drawRegion.left + leftArrow.left;
	arrowRegion.top = drawRegion.top + leftArrow.top;
	arrowRegion.right = drawRegion.left + rightArrow.right;
	arrowRegion.bottom = drawRegion.top + rightArrow.bottom;
	arrowRegion++;

	// Draw the notebook shadows.
	DrawShadow(drawRegion, ZAF_SHADOW_SIZE, ccode);

	// Draw the tabs.
	ZafRegionStruct region = drawRegion;
	region.right = region.left - 1;
	region.bottom = region.top - 1;
	for (ZafNotebookTab *tab = (ZafNotebookTab *)SupportFirst(); tab; tab = tab->Next())
		if (tab->tabType == ZAF_TAB_FULL)
		{
			region = tab->zafRegion;
			if (region.Overlap(clip))
				tab->Draw(event, ccode);
		}

	// Clear the remaining area of the notebook.
	region.left = region.right + 1;
	region.right = zafRegion.right - zafRegion.left;
	if (region.Overlap(clip))
	{
		display->SetPalette(parent->LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE));
		display->Rectangle(region, 0, true); // rest of the tab row.
	}

	// Draw the notebook arrows.
	ZafWindowObject::DrawShadow(arrowRegion, 1, ccode);
	display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
	display->Rectangle(arrowRegion, 0, true);
	DrawArrows(false, false);

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

void ZafNotebook::DrawArrows(bool leftDepressed, bool rightDepressed)
{
	// Draw the scroll arrows.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();

	ZafPaletteStruct topShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
	ZafPaletteStruct bottomShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
	ZafPaletteStruct background = LogicalPalette(ZAF_PM_BACKGROUND, state);

	ZafRegionStruct arrowRegion = leftArrow;
	arrowRegion.HzShift(drawRegion.left);
	arrowRegion.VtShift(drawRegion.top);

	ZafNotebookTab *tab = DynamicPtrCast(SupportCurrent(), ZafNotebookTab);
	ZafNotebookTab *prevTab = NULL, *nextTab = NULL;

	if (tab)
	{
	  prevTab = tab->Previous();
	  nextTab = tab->Next();
	}

	while(prevTab && prevTab->Disabled())
	  prevTab = prevTab->Previous();

	while(nextTab && nextTab->Disabled())
	  nextTab = nextTab->Next();
	
	if (Current() && prevTab)
		_XmDrawArrow(XtDisplay(screenID), XtWindow(screenID),
		leftDepressed ? bottomShadow.osPalette : topShadow.osPalette,
		leftDepressed ? topShadow.osPalette : bottomShadow.osPalette,
		background.osPalette, arrowRegion.left - 1, arrowRegion.top - 1,
		arrowRegion.Width() + 2, arrowRegion.Height() + 2, ZAF_SHADOW_SIZE,
		XmARROW_LEFT);

	arrowRegion = rightArrow;
	arrowRegion.HzShift(drawRegion.left);
	arrowRegion.VtShift(drawRegion.top);
	if (Current() && nextTab)
		_XmDrawArrow(XtDisplay(screenID), XtWindow(screenID),
		rightDepressed ? bottomShadow.osPalette : topShadow.osPalette,
		rightDepressed ? topShadow.osPalette : bottomShadow.osPalette,
		background.osPalette, arrowRegion.left - 1, arrowRegion.top - 1,
		arrowRegion.Width() + 2, arrowRegion.Height() + 2, ZAF_SHADOW_SIZE,
		XmARROW_RIGHT);

	EndDraw();
}

ZafEventType ZafNotebook::DrawShadow(ZafRegionStruct &region, int depth, ZafEventType ccode)
{
	ZafPaletteState state = PaletteState();
	ZafPaletteStruct topShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
	ZafPaletteStruct bottomShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
	ZafPaletteStruct background = LogicalPalette(ZAF_PM_BACKGROUND, state);

	// Draw the shadow.
	region.top = SupportLast() ? SupportLast()->zafRegion.bottom + 1 : 0;
	for (int i = 0; i < depth; i++)
	{
		// Top.
		ZafWindowObject *object = SupportCurrent();
		if (object)
		{
			// Clear the current tab's bottom to bring it to the front.
			display->SetPalette(background);
			display->Rectangle(object->zafRegion.left - i, region.top,
				object->zafRegion.right + i, region.top, 0, true);
			// Draw the top notebook line.
			display->SetPalette(topShadow);
			display->Line(region.left, region.top,
				object->zafRegion.left - i, region.top, 1);
			display->Line(object->zafRegion.right + i, region.top,
				region.right, region.top, 1);
		}
		else
		{
			// Clear the whole top notebook line.
			display->SetPalette(topShadow);
			display->Line(region.left, region.top, region.right, region.top, 1);
		}

		// Main notebook body.
		display->Line(region.left, region.top - i, region.left, region.bottom, 1);
		display->SetPalette(bottomShadow);
		display->Line(region.left + 1, region.bottom, region.right, region.bottom, 1);
		display->Line(region.right, region.top, region.right, region.bottom - 1, 1);
		region--;
	}

	// Clear the remaining border area.
	display->SetPalette(background);
	display->Rectangle(region, 0, true);
	region -= ZAF_MARGIN_SIZE;

	// Return the status.
	return (ccode);
}
#endif

ZafEventType ZafNotebook::Event(const ZafEventStruct &event)
{
	// Check for Motif key events.
	ZafEventType ccode = event.type;
	if (ccode == E_MOTIF && event.InputType() == E_KEY)
		ccode = LogicalEvent(event);

	if (editMode && (ccode == L_BEGIN_SELECT || ccode == L_END_SELECT))
	{
		ZafWindowObject *tab;
		for (tab = SupportFirst(); tab; tab = tab->Next())
			if (tab->zafRegion.Overlap(event.position))
				break;

		if (!tab && ((!leftArrow.Overlap(event.position) && 
			!rightArrow.Overlap(event.position)) || 
			(SupportCurrent() == SupportFirst() && 
			leftArrow.Overlap(event.position)) || (SupportCurrent() ==
			SupportLast() && rightArrow.Overlap(event.position))))
			return (ZafWindowObject::Event(event));
	}
	// Check for zinc events.
	ZafWindowObject *object;
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		ccode = ZafWindow::Event(event);

		if (!Current())
			SetCurrent(First());
		if (!SupportCurrent())
			support.SetCurrent(SupportFirst());
		break;

	// ----- Position & Size messages -----
	case N_SIZE:
		{
		// Compute the optimal tab sizes.
		int totalPages = 0;
		int minWidth = zafRegion.Width();
		int minHeight = zafRegion.Height();
		for (object = SupportFirst(); object; object = object->Next(), totalPages++)
		{
			ZafNotebookTab *tab = DynamicPtrCast(object, ZafNotebookTab);
			tab->Event(S_COMPUTE_SIZE);
			if (minWidth > tab->zafRegion.Width())
				minWidth = tab->zafRegion.right - tab->zafRegion.left;
			if (minHeight > tab->zafRegion.Height())
				minHeight = tab->zafRegion.bottom - tab->zafRegion.top;
		}

		// Determine the fixed tab width and height.
		int width = tabWidth;
		if (!width)
			width = totalPages ? (zafRegion.Width() / totalPages) : zafRegion.Width();
		if (width < minWidth)
			width = minWidth;
		int height = tabHeight;
		if (!height)
			height = display->cellHeight - 2 * (ZAF_BORDER_SIZE + ZAF_SHADOW_SIZE) -
				display->preSpace - display->postSpace;
		if (height < minHeight)
			height = minHeight;

		// Determine the size of the arrows.
		// right
		rightArrow.coordinateType = ZAF_PIXEL;
		rightArrow.right = zafRegion.right - zafRegion.left - ZAF_BORDER_SIZE;
		rightArrow.bottom = height - ZAF_BORDER_SIZE;
		rightArrow.left = rightArrow.right - ARROW_SIZE + 1;
		rightArrow.top = rightArrow.bottom - ARROW_SIZE + 1;
		// left
		leftArrow.coordinateType = ZAF_PIXEL;
		leftArrow.right = rightArrow.left - 2;
		leftArrow.left = leftArrow.right - ARROW_SIZE + 1;
		leftArrow.bottom = rightArrow.bottom;
		leftArrow.top = rightArrow.top;

		// Adjust the tab sizes.
		int column = 0, line = 0;
		for (object = SupportFirst(); object; object = object->Next())
		{
			// Check for column parameters.
			ZafNotebookTab *tab = DynamicPtrCast(object, ZafNotebookTab);
			if (!tabWidth)
				width = tab->zafRegion.Width();

			// Position the tab.
			tab->zafRegion.left = column;
			tab->zafRegion.top = line;
			tab->zafRegion.right = column + width - 1;
			tab->zafRegion.bottom = line + height - 1;
			column = tab->zafRegion.right + 1;
		}

		// Adjust the page sizes.
		line = SupportFirst() ? SupportFirst()->zafRegion.bottom + 1 : 0;
		for (object = First(); object; object = object->Next())
		{
			object->zafRegion.left = 0;
			object->zafRegion.top = line;
			object->zafRegion.right = zafRegion.right - zafRegion.left;
			object->zafRegion.bottom = zafRegion.bottom - zafRegion.top;
			object->zafRegion -= ZAF_SHADOW_SIZE + ZAF_BORDER_SIZE;
		 	// OSSize must be processed by each page for combo boxes to work.
			object->OSSize();
			// If page is not current make sure it is unmanaged.
			if (!(object == Current()) && XtIsManaged(object->OSScreenID(ZAF_FRAMEID)))
				XtUnmanageChild(object->OSScreenID(ZAF_FRAMEID));
		}

		// Adjust the client region.
		clientRegion.left = clientRegion.top = 0;
		clientRegion.right = screenID->core.width - 1;
		clientRegion.bottom = screenID->core.height - 1;

		// Check tab visibility.
		ScrollEvent(S_HSCROLL_COMPUTE);
		}
		break;

	case S_SIZE:
	case S_COMPUTE_SIZE:
		ccode = ZafWindowObject::Event(event);
		break;

	// ----- Movement -----
	case L_BEGIN_SELECT:
		{
		ZafNotebookTab *tab = DynamicPtrCast(SupportCurrent(), ZafNotebookTab);
		if (tab && leftArrow.Overlap(event.position))
		{
			tab = tab->Previous();
			while (tab && tab->Disabled())
			  tab = tab->Previous();
			if (!tab)
				break;
			DrawArrows(true, false);
		}
		else if (tab && rightArrow.Overlap(event.position))
		{
			tab = tab->Next();
			while (tab && tab->Disabled())
			  tab = tab->Next();
			if (!tab)
				break;
			DrawArrows(false, true);
		}
		else
		{
			// Check for overlap on a page tab.
			for (tab = DynamicPtrCast(SupportFirst(), ZafNotebookTab); tab; tab = tab->Next())
				if (tab->zafRegion.Overlap(event.position) && tab->Visible())
					break;
		}

		// Reset the current page.
		if (tab && !tab->Disabled())
		{
			SetCurrentPage(support.Index(tab));
			DrawArrows(false, false);
		}
		}
		break;

	case L_CONTINUE_SELECT:
	case L_END_SELECT:
		break;

	case L_FIRST:
		// Move to the first notebook page.
		if (First() != Current())
			SetCurrentPage(First());
		break;

	case L_LAST:
		// Move to the last notebook page.
		if (Last() != Current())
			SetCurrentPage(Last());
		break;

	case L_PGUP:
		// Move up one notebook page.
		if (!Current())
			SetCurrentPage(First());
		else if (Current()->Previous())
			SetCurrentPage(Current()->Previous());
		break;

	case L_PGDN:
		// Move down one notebook page.
		if (!Current())
			SetCurrentPage(Last());
		else if (Current()->Next())
			SetCurrentPage(Current()->Next());
		break;

	case S_HOT_KEY:
		{
		int pageIndex = 0;
		ZafNotebookTab *tab = (ZafNotebookTab *)SupportFirst();
		while (tab && tab->HotKeyChar() != event.key.value)
		{
			tab = (ZafNotebookTab *)tab->Next();
			pageIndex++;
		}

		if (tab && !tab->Disabled())
			SetCurrentPage(pageIndex);
		else
			ccode = current ? Current()->Event(event) : S_UNKNOWN;
		}
		break;

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

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

ZafRegionStruct ZafNotebook::MaxRegion(ZafWindowObject *object,
	ZafVtJustify vWeight, ZafHzJustify hWeight)
{
	// Defer to the base class.
	return (ZafWindow::MaxRegion(object, vWeight, hWeight));
}

bool ZafNotebook::PageDisabled(int page)
{
	ZafWindowObject *tab = (ZafWindowObject *)support.Get(page);
	return (tab->Disabled());
}

ZafEventType ZafNotebook::ScrollEvent(const ZafEventStruct &event)
{
	// Process the scrolling request.  Scrolling on the notebook is 
	// done with the tabs.
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	case S_HSCROLL_COMPUTE:
	case S_HSCROLL_CHECK:
		// Make sure the current page's tab is on the notebook.
		{
		int totalWidth = leftArrow.left;
		int deltaX = 0;
		ZafNotebookTab *tab = DynamicPtrCast(SupportCurrent(), ZafNotebookTab);
		if (!tab)
			return (S_ERROR);
		if (tab->zafRegion.left < 0)
		{
			// Shift the tabs to the right.
			deltaX = -tab->zafRegion.left;
		}
		else if ((tab->zafRegion.right >= totalWidth) || (tab->Next() && tab->zafRegion.right >= totalWidth))
		{
			// Shift the tabs to the left.
			for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
				if (tab->zafRegion.right - object->zafRegion.left < totalWidth)
				{
					deltaX = -object->zafRegion.left;
					break;
				}
		}

		// Shift the tabs.
		bool clear = (screenID && Visible() && XtIsRealized(screenID) && XtWindow(screenID)) ? true : false;
		for (tab = DynamicPtrCast(SupportFirst(), ZafNotebookTab); tab; tab = tab->Next())
		{
			// Adjust the region.
			if (clear && tab->tabType == ZAF_TAB_FULL)
				XClearArea(XtDisplay(screenID), XtWindow(screenID),
					tab->zafRegion.left, tab->zafRegion.top,
					tab->zafRegion.Width(), tab->zafRegion.Height(), true);

			// Adjust the region.
			tab->zafRegion.left += deltaX;
			tab->zafRegion.right += deltaX;

			// Check the tabs type.
			bool tVisible;
			if (tab->zafRegion.left < 0)
			{
				tab->tabType = ZAF_TAB_LEFT;
				tVisible = (tab->zafRegion.right > 0) ? true : false;
			}
			else if ((tab->zafRegion.right >= totalWidth) || (tab->Next() && tab->zafRegion.right >= totalWidth))
			{
				tab->tabType = ZAF_TAB_RIGHT;
				tVisible = (tab->zafRegion.left < totalWidth) ? true : false;
			}
			else
			{
				tab->tabType = ZAF_TAB_FULL;
				tVisible = true;
			}

			// Redisplay the tab.
			if (tab->Visible() != tVisible)
				tab->SetVisible(tVisible);
		}
		}
		break;

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

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

bool ZafNotebook::SetAutomaticUpdate(bool setAutomaticUpdate)
{
	// Defer to the base class.
	if (automaticUpdate == setAutomaticUpdate)
		return (automaticUpdate);
	ZafWindowObject::SetAutomaticUpdate(setAutomaticUpdate);

	// Now, update the children.
	if (screenID && AutomaticUpdate() && SystemObject())
	{
		Event(N_SIZE);
		if (Current())
			XtManageChild(Current()->OSScreenID(ZAF_FRAMEID));
	}
	return (automaticUpdate);
}

int ZafNotebook::SetCurrentPage(int index)
{
	if (CurrentPage() == index || index < FirstPage() || index > LastPage())
		return (CurrentPage());

	// Get the current objects.
	ZafWindow *page = DynamicPtrCast(Get(index), ZafWindow);
	ZafNotebookTab *tab = DynamicPtrCast(support.Get(index), ZafNotebookTab);
	if (!page || page == Current())
		return (index);
	else if (!screenID)
	{
		SetCurrent(page);
		support.SetCurrent(tab);
		return (index);
	}

	// Reposition the pages.
	if (Current() && XtIsManaged(Current()->OSScreenID(ZAF_FRAMEID)))
	{
		XtUnmanageChild(Current()->OSScreenID(ZAF_FRAMEID));
	}
	else if (Current())
	{
		for (int i = FirstPage(); i < LastPage(); i++)
		{
			ZafWindow *managedPage = DynamicPtrCast(Get(i), ZafWindow);
			if (XtIsManaged(managedPage->OSScreenID(ZAF_FRAMEID)))
			{
				XtUnmanageChild(managedPage->OSScreenID(ZAF_FRAMEID));
				break;
			}
		}
	}
	page->OSSize();
	XtManageChild(page->OSScreenID(ZAF_FRAMEID));
	if (focus)
		page->SetFocus(true);
	else
		SetCurrent(page);

	ZafRegionStruct drawRegion = BeginDraw();

	// Update the tabs.
	if (tab->tabType == ZAF_TAB_FULL)
	{
		// No scrolling is necessary.
		ZafNotebookTab *oldTab = DynamicPtrCast(SupportCurrent(), ZafNotebookTab);
		support.SetCurrent(tab);
		if (oldTab)
			oldTab->Draw(S_REDISPLAY, S_REDISPLAY);
		tab->Draw(S_REDISPLAY, S_REDISPLAY);
	}
	else
	{
		// Scroll the current tab into view.
		support.SetCurrent(tab);
		ScrollEvent(S_HSCROLL_CHECK);
	}

	// Redraw the notebook shadow.
	DrawShadow(drawRegion, ZAF_SHADOW_SIZE, S_REDISPLAY);
	EndDraw();

	Event(N_CHANGE_PAGE);

	// Return the current page.
	return (index);
}

ZafIChar ZafNotebook::SetTabHotKey(int page, ZafIChar hotKeyChar, int index)
{
	// Find the associated tab.
	ZafNotebookTab *tab = (ZafNotebookTab *)support.Get(page);
	return (tab ? tab->SetHotKey(hotKeyChar, index) : '\0');
}

ZafError ZafNotebook::SetTabText(const ZafIChar *text, int page)
{
	// Set the text.
	ZafWindowObject *object = (ZafWindowObject *)((page == -1) ? SupportCurrent() : support.Get(page));
	return (object ? object->SetText(text) : ZAF_ERROR_INVALID);
}

bool ZafNotebook::SetPageDisabled(int page, bool setDisabled)
{
	ZafWindowObject *tab = (ZafWindowObject *)support.Get(page);
	return (tab ? tab->SetDisabled(setDisabled) : false);
}

ZafWindowObject *ZafNotebook::Subtract(ZafWindowObject *object)
{
	// Check for support objects.
	if (object->SupportObject())
		return (ZafWindow::Subtract(object));

	// Save some page information, then remove the object.
	bool saveUpdate = AutomaticUpdate();
	SetAutomaticUpdate(false);
	ZafWindow *page = DynamicPtrCast(object, ZafWindow);
	int index = Index(page);
	object = ZafWindow::Subtract(object);

	// Re-add the title to the page.
	if (page && index >= 0)
	{
		ZafWindowObject *title = (ZafWindowObject *)support.Get(index);
		if (title)
		{
			support.Subtract(title);
			page->ZafWindow::Add(new ZafTitle(title->Text()));
			delete title;
		}
	}

	// Defer the rest to the base class.
	SetAutomaticUpdate(saveUpdate);
	return (object);
}

ZafIChar ZafNotebook::TabHotKeyChar(int page) const
{
	// Find the associated tab.
	for (ZafNotebookTab *tab = (ZafNotebookTab *)support.First(); tab; page--, tab = (ZafNotebookTab *)tab->Next())
		if (page == 0)
			return (tab->HotKeyChar());
	return ('\0');
}

int ZafNotebook::TabHotKeyIndex(int page) const
{
	// Find the associated tab.
	for (ZafNotebookTab *tab = (ZafNotebookTab *)support.First(); tab; page--, tab = (ZafNotebookTab *)tab->Next())
		if (page == 0)
			return (tab->HotKeyIndex());
	return (-1);
}

const ZafIChar *ZafNotebook::TabText(int page)
{
	// Get the text.
	ZafWindowObject *object = (ZafWindowObject *)((page == -1) ? SupportCurrent() : support.Get(page));
	return (object ? object->Text() : ZAF_NULLP(ZafIChar));
}

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

void ZafNotebook::OSDestroy(void)
{
	// Destroy the notebook tabs and scroll arrows.
	support.Destroy();

	// Let the base class process.
	ZafWindow::OSDestroy();
}

ZafEventType ZafNotebook::OSEvent(const ZafEventStruct &)
{
	return (S_ERROR);
}

void ZafNotebook::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	// Defer to the base class.
	ZafWindow::OSMapPalette(palette, type, state);
}

void ZafNotebook::OSRegisterObject(void)
{
	// Create the notebook widget.
	char *name = zafCodeSet->ConvertToOSString(stringID, 0, false);
	screenID = parent->OSScreenID(ZAF_CLIENTID);
	screenID = XtVaCreateWidget(name,
		(WidgetClass)&MtfNotebook::classRecord, screenID,
		// user information
		XmNuserData, this,
		// miscellaneous
		XmNtraversalOn, Noncurrent() ? false : true,
		0);

	// Override translations.
	XtTranslations translationTable = XtParseTranslationTable(MtfNotebook::translations);
	XtOverrideTranslations(screenID, translationTable);

	// Add edit mode overrides.
	ZafEditModeRegister(this);

	// Add drag/drop capabilities.
	ZafDropSiteRegister(this);
}

OSWindowID ZafNotebook::OSScreenID(ZafScreenIDType type) const
{
	// Return the client widget.
	return (ZafWindowObject::OSScreenID(type));
}

ZafError ZafNotebook::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	// Make sure the widget can be updated.
	if (!screenID || !AutomaticUpdate() || !SystemObject())
		return (ZAF_ERROR_INVALID);

	// This class bypasses setting its own values so the background won't
	// clear to some funny color.  Also, support children are automatically
	// redrawn with the parent color, so only normal children need the
	// color updates.
	for (ZafWindowObject *object = First(); object; object = object->Next())
		object->OSUpdatePalettes(zafTypes, osTypes);
	if (XtIsManaged(screenID))
		Redisplay();
	return (ZAF_ERROR_NONE);
}

