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

#include <w_app.hpp>
#include <z_htips.hpp>
#include <z_notebk.hpp>
#include <z_title.hpp>
#include <z_win.hpp>

#define DEFAULT_TAB_HEIGHT		24
#define SCROLL_BUTTON_SIZE		19
#define ZAF_NOTEBOOK_MARGIN		4
//??? What values should be used.
#define NOTEBOOK_SCROLL_LEFT	(ZafEventType)-310
#define NOTEBOOK_SCROLL_RIGHT	(ZafEventType)-311

ZafEventType ScrollButtonCallback(ZafWindowObject *object, ZafEventStruct &, ZafEventType ccode)
{
	if (ccode == L_SELECT)
	{
		ZafButton *button = DynamicPtrCast(object, ZafButton);
		button->parent->Event(button->Value());
	}

	return 0;
}

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

class ZafNotebookTab : public ZafWindowObject
{
public:
	ZafNotebookTab(void);
	ZafNotebookTab(ZafNotebookTab &copy);

	virtual ZafEventType Draw(const ZafEventStruct &event, ZafEventType ccode);
	virtual ZafWindowObject *Duplicate(void) { return new ZafNotebookTab(*this); };

	const ZafIChar *Text(void);
	virtual  ZafError SetText(const ZafIChar *text);

	void SaveSupportObjects(ZafWindow *page);
	void RestoreSupportObjects(ZafWindow *page);

	ZafIChar HotKeyChar() {	return (title->ZafButton::HotKeyChar()); }
	int HotKeyIndex() { return (title->ZafButton::HotKeyIndex()); }
	ZafIChar SetHotKey(ZafIChar hotKeyChar, int index) { return (title->ZafButton::SetHotKey(hotKeyChar, index)); }

protected:
	ZafTitle *title;
	ZafList supportList;
};

ZafNotebookTab::ZafNotebookTab() :
	ZafWindowObject(0, 0, 0, 0),
	title(ZAF_NULLP(ZafTitle))
{
	// Initialize the notebook tab information.
//??? This call doesn't work for derived objects.  Maybe it shouldn't exist.
//??? SetSupportObject(true);
	supportObject = true;
	systemObject = false;
}

ZafNotebookTab::ZafNotebookTab(ZafNotebookTab &copy) :
	ZafWindowObject(copy), title(ZAF_NULLP(ZafTitle))
{
	ZafWindowObject *object = (ZafWindowObject *)copy.supportList.First();
	while (object)
	{
		ZafWindowObject *dupObject = object->Duplicate();
		supportList.Add(dupObject);
		if (dupObject->IsA(ID_ZAF_TITLE))
			title = DynamicPtrCast(dupObject, ZafTitle);
		object = object->Next();
	}
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafNotebookTab::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Begin the drawing operation.
	ZafRegionStruct drawRegion = BeginDraw();

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

	if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION)
	{
		// Adjust the drawing region... selected tab draws bigger.
		if (!selected)
		{
			drawRegion -= 2;
			drawRegion.bottom++;
		}

		// Adjust outline region for selected tabs.
		ZafRegionStruct outlineRegion = drawRegion;
		if (selected)
			outlineRegion.bottom -= 1;

		// Generate palettes for each of the shadow colors.
		ZafPaletteStruct palette3DLight;
		ZafPaletteStruct palette3DShadow;
		ZafPaletteStruct palette3DHiLight;
		ZafPaletteStruct palette3DDarkShadow;
		palette3DLight = palette3DHiLight = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
		palette3DShadow = palette3DDarkShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
		palette3DHiLight.colorForeground = palette3DHiLight.colorBackground;
		palette3DHiLight.osPalette.colorForeground = palette3DHiLight.osPalette.colorBackground;
		palette3DDarkShadow.colorForeground = palette3DDarkShadow.colorBackground;
		palette3DDarkShadow.osPalette.colorForeground = palette3DDarkShadow.osPalette.colorBackground;

		// Draw the tab outline.
		display->SetPalette(palette3DHiLight);
		display->Line(outlineRegion.left, outlineRegion.bottom, outlineRegion.left, outlineRegion.top + 2);
		display->Line(outlineRegion.left + 2, outlineRegion.top, outlineRegion.right - 2, outlineRegion.top);
		display->Pixel(outlineRegion.left + 1, outlineRegion.top + 1);
		display->SetPalette(palette3DLight);
		display->Line(outlineRegion.left + 1, outlineRegion.bottom, outlineRegion.left + 1, outlineRegion.top + 2);
		display->Line(outlineRegion.left + 2, outlineRegion.top + 1, outlineRegion.right - 2, outlineRegion.top + 1);
		display->SetPalette(palette3DDarkShadow);
		display->Line(outlineRegion.right, outlineRegion.top + 2, outlineRegion.right, outlineRegion.bottom);
		display->Pixel(outlineRegion.right - 1, outlineRegion.top + 1);
		display->SetPalette(palette3DShadow);
		display->Line(outlineRegion.right - 1, outlineRegion.top + 2, outlineRegion.right - 1, outlineRegion.bottom);
		drawRegion.left += 2;
		drawRegion.top += 2;
		drawRegion.right -= 2;

		// Erase the background.
		DrawBackground(drawRegion, ccode);
	}

	// Draw the focus rect.
//	DrawFocus(drawRegion, ccode);

	if (ccode == S_REDISPLAY_DATA)
		DrawBackground(drawRegion, ccode);

	// Draw the text.
	if (ccode == S_REDISPLAY || ccode == S_REDISPLAY_REGION || ccode == S_REDISPLAY_DATA)
	{
		display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));
		display->Text(drawRegion, Text(), -1, ZAF_HZ_CENTER, ZAF_VT_CENTER,
			HotKeyIndex());
	}

	// End the drawing operation.
	EndDraw();

	return (ccode);
}
#endif

void ZafNotebookTab::SaveSupportObjects(ZafWindow *page)
{
	ZafWindowObject *object = page->SupportFirst();
	while (object)
	{
		ZafWindowObject *nextObject = object->Next();

		// Remove invalid support objects (geometry manager is valid).
		ZafNumberID objID = object->NumberID();
		if (objID < 0 && objID != ZAF_NUMID_GEOMETRY)
		{
			page->Subtract(object);
			supportList.Add(object);

			if (object->IsA(ID_ZAF_TITLE))
				title = DynamicPtrCast(object, ZafTitle);
		}
		object = nextObject;
	}
}

void ZafNotebookTab::RestoreSupportObjects(ZafWindow *page)
{
	for (ZafWindowObject *object = (ZafWindowObject *)supportList.Last(); object; object = (ZafWindowObject *)supportList.Last())
	{
		supportList.Subtract(object);
		page->Add(object, page->SupportFirst());
	}
}

const ZafIChar *ZafNotebookTab::Text(void)
{
	if (title)
		return title->Text();
	else
		return ZAF_NULLP(ZafIChar);
}

ZafError ZafNotebookTab::SetText(const ZafIChar *text)
{
	ZafError error = title ? title->SetText(text) : ZAF_ERROR_INVALID_TARGET;
	if (screenID)
	{
//???#if defined(ZAF_WIN32)
//???		if (ZafMSWindowsApp::commonControlsAvailable)
//???		{
//???			TC_ITEM tabItem;
//???			tabItem.mask = TCIF_TEXT;
//???			tabItem.pszText = (PSZ)text;
//???			tabItem.cchTextMax = strlen(text);
//???			SendMessage(screenID, TCM_SETITEM, (WPARAM)ListIndex(), (LPARAM)&tabItem);
//???		}
//???		else
//???			Event(ZafEventStruct(S_REDISPLAY));
//???#else
		parent->Event(N_SIZE);
		ZafWindowObject *object;
		for (object = DynamicPtrCast(parent, ZafWindow)->SupportFirst();
			object; object = object->Next())
			object->Redisplay();
//???#endif
	}

	return error;
}

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

ZafWindowObject *ZafNotebook::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	ZafWindow *page = DynamicPtrCast(object, ZafWindow);
	if (page)
	{
		page->SetParentPalette(true);
		page->SetRegionType(ZAF_AVAILABLE_REGION);
		ZafNotebookTab *tab = new ZafNotebookTab;
		tab->SaveSupportObjects(page);
		tab->SetDisabled(page->Disabled());

		ZafWindow::Add(page, position);
		if (screenID)
			page->Event(ZafEventStruct(S_DESTROY));
		ZafWindow::Add(tab, (ZafWindowObject *)support.Get(Index(page)));

		if (!current)
			SetCurrentPage(page);
		if (screenID)
			Event(N_SIZE);
		return (page);
	}
	else
		return (ZAF_NULLP(ZafWindowObject));
}

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

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafNotebook::Draw(const ZafEventStruct &event, ZafEventType ccode)
{
	// Begin the drawing operation.
	ZafRegionStruct drawRegion = BeginDraw();

	// Save region and draw border later since tabs will overwrite it if it
	// is drawn before they are.
	ZafRegionStruct borderRegion = drawRegion;
	if (Bordered())
		drawRegion -= ZAF_BORDER_SIZE;

	ZafRegionStruct tabRegion = drawRegion;
	tabRegion.bottom = tabRegion.top + (tabHeight ? tabHeight : DEFAULT_TAB_HEIGHT);
	display->SetPalette(parent->LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE));
	display->Rectangle(tabRegion, 0, true);

	// Outline the client area.
	drawRegion.top += (tabHeight ? tabHeight : DEFAULT_TAB_HEIGHT) - ZAF_SHADOW_SIZE;
	DrawShadow(drawRegion, 2, ccode);

	DrawBackground(drawRegion, ccode);

	// Draw the tabs, current tab last.
	ZafEventStruct childEvent = event;
	ZafWindowObject *currentTab = (ZafWindowObject *)support.Get(CurrentPage());
	int index = 0;
	for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
	{
		if (index++ >= scrollIndex && object != currentTab && object->zafRegion.Overlap(event.region))
			object->Draw(event, ccode);
	}
	if (CurrentPage() >= scrollIndex && currentTab->zafRegion.Overlap(event.region))
		currentTab->Draw(event, ccode);

	if (Bordered())
		DrawBorder(borderRegion, ccode);

	// End the drawing operation.
	EndDraw();

	return (ccode);
}
#endif

ZafEventType ZafNotebook::Event(const ZafEventStruct &event)
{
	ZafEventType ccode = event.type;

	if (ccode == E_OSEVENT)
		return ZafNotebook::OSEvent(event);

	switch (ccode)
	{
	case S_COMPUTE_SIZE:
		ccode = ZafWindow::Event(event);
		if (!tabHeight)
			tabHeight = DEFAULT_TAB_HEIGHT;
		break;

	case S_INITIALIZE:
		{
		ccode = ZafWindow::Event(event);
//???#if defined(ZAF_WIN32)
//???		if (ZafMSWindowsApp::commonControlsAvailable)
//???			break;
//???#endif

//??? scrollIndex should be initialized smarter?
		scrollIndex = 0;

		ZafButton *button = new ZafButton(0, 0, 0, 0, ZAF_ITEXT("<"));
		button->SetAutoSize(false);
		button->SetNoncurrent(true);
		button->SetValue(NOTEBOOK_SCROLL_LEFT);
		button->SetUserFunction(ScrollButtonCallback);
		if (scrollIndex == 0)
			button->SetDisabled(true);
		button->parent = this;
		leftScrollButton = button;

		button = new ZafButton(0, 0, 0, 0, ZAF_ITEXT(">"));
		button->SetAutoSize(false);
		button->SetNoncurrent(true);
		button->SetValue(NOTEBOOK_SCROLL_RIGHT);
		button->SetUserFunction(ScrollButtonCallback);
		button->parent = this;
		rightScrollButton = button;

		leftScrollButton->Event(event);
		rightScrollButton->Event(event);

		SetCurrent(Get(currentPage));
		}
		break;

	case S_REGISTER_OBJECT:
		{
		ccode = ZafWindowObject::Event(event);

//???#if defined(ZAF_WIN32)
//???		if (ZafMSWindowsApp::commonControlsAvailable)
//???		{
//???			int index = 0;
//???			for (ZafWindowObject *tab = SupportFirst(); tab; tab = tab->Next())
//???			{
//???				TC_ITEM tabItem;
//???				tabItem.mask = TCIF_TEXT;
//???				tabItem.pszText = (PSZ)tab->Text();
//???//				tabItem.cchTextMax = strlen(tab->Text());
//???				SendMessage(screenID, TCM_INSERTITEM, (WPARAM)index, (LPARAM)&tabItem);
//???				index++;
//???			}
//???		}
//???		else
//???			for (ZafWindowObject *tab = SupportFirst(); tab; tab = tab->Next())
//???				tab->screenID = screenID;
//???#else
		for (ZafWindowObject *tab = SupportFirst(); tab; tab = tab->Next())
			tab->screenID = screenID;
//???#endif

		if (current)
		{
//???#if defined(ZAF_WIN32)
//???			if (ZafMSWindowsApp::commonControlsAvailable)
//???				SendMessage(screenID, TCM_SETCURSEL, (WPARAM)Index(current), (LPARAM)0);
//???			else
//???			{
//???				ZafWindowObject *currentTab = (ZafWindowObject *)support.Get(Index(current));
//???				currentTab->SetSelected(true);
//???			}
//???#else
//???#endif
			Current()->Event(event);
		}

//???#if defined(ZAF_WIN32)
//???		if (!ZafMSWindowsApp::commonControlsAvailable)
//???		{
//???			leftScrollButton->Event(event);
//???			rightScrollButton->Event(event);
//???		}
//???#else
		leftScrollButton->Event(event);
		rightScrollButton->Event(event);
//???#endif
		}
		break;

	case N_SIZE:
		{
		// Size the pages.
		ZafRegionStruct maxRegion = MaxRegion(this);
		ZafWindowObject *object;
		for (object = First(); object; object = object->Next())
			object->zafRegion = maxRegion;

//???#if defined(ZAF_WIN32)
//???		if (ZafMSWindowsApp::commonControlsAvailable)
//???			break;
//???#endif
		// Compute the tab sizes.
		display->SetDisplayContext(GetDC(screenID));
		display->SetFont(LogicalPalette(ZAF_PM_TEXT, 0).font);
		int left = Bordered() ? ZAF_BORDER_SIZE : 0;
		for (object = SupportFirst(); object; object = object->Next())
		{
			ZafRegionStruct textSize = display->TextSize(object->Text());
			object->zafRegion.left = left;
			if (tabWidth > 0)
				left += tabWidth;
			else
				left += textSize.Width() + 2 * display->cellWidth;
			object->zafRegion.right = left + 3;
			object->zafRegion.top = Bordered() ? ZAF_BORDER_SIZE : 0;
			object->zafRegion.bottom = (tabHeight ? tabHeight : DEFAULT_TAB_HEIGHT) - 1 + (Bordered() ? ZAF_BORDER_SIZE : 0);
		}
		ReleaseDC(screenID, display->RestoreDisplayContext());

		if (scrollIndex > 0)
		{
			int scrollValue = DynamicPtrCast(support.Get(scrollIndex - 1), ZafWindowObject)->zafRegion.right;
			for (object = SupportFirst(); object; object = object->Next())
			{
				object->zafRegion.left -= scrollValue;
				object->zafRegion.right -= scrollValue;
			}
		}

		// Adjust the tab scrolling.
		while (scrollIndex > 0)
		{
			int scrollValue = DynamicPtrCast(support.Get(scrollIndex - 1), ZafWindowObject)->zafRegion.Width();
			int tabSpace = zafRegion.Width();
			if (Bordered())
				tabSpace -= 2 * ZAF_BORDER_SIZE;
			if (scrollIndex > 1)
				tabSpace -= 2 * leftScrollButton->zafRegion.Width();
			if (tabSpace - SupportLast()->zafRegion.right - (Bordered() ? ZAF_BORDER_SIZE : 0) >= scrollValue)
				Event(NOTEBOOK_SCROLL_LEFT);
			else
				break;
		}

		if (leftScrollButton->Visible())
		{
			ZafEventStruct redisplayEvent(S_REDISPLAY_REGION);
			redisplayEvent.region = parent->ConvertToOSRegion(this, &leftScrollButton->zafRegion);
			redisplayEvent.region.right += leftScrollButton->zafRegion.Width();
			parent->Event(redisplayEvent);
		}

		// Size the scroll buttons.
		if (!SupportFirst() || (scrollIndex == 0 &&
			SupportLast()->zafRegion.right - 2 < zafRegion.Width()))
		{
			leftScrollButton->SetVisible(false);
			rightScrollButton->SetVisible(false);
		}
		else
		{
			ZafRegionStruct scrollButtonRegion;
			scrollButtonRegion.coordinateType = ZAF_PIXEL;
			scrollButtonRegion.right = zafRegion.Width() - 1 - (Bordered() ? ZAF_BORDER_SIZE : 0);
			scrollButtonRegion.left = scrollButtonRegion.right - SCROLL_BUTTON_SIZE + 1;
			scrollButtonRegion.top = 2 + (Bordered() ? ZAF_BORDER_SIZE : 0);
			scrollButtonRegion.bottom = scrollButtonRegion.top + SCROLL_BUTTON_SIZE - 1;
			rightScrollButton->Event(ZafEventStruct(S_SIZE, 0, scrollButtonRegion));
			scrollButtonRegion.left -= SCROLL_BUTTON_SIZE;
			scrollButtonRegion.right -= SCROLL_BUTTON_SIZE;
			leftScrollButton->Event(ZafEventStruct(S_SIZE, 0, scrollButtonRegion));

			if (scrollIndex < support.Count() - 1 &&
				SupportLast()->zafRegion.right > leftScrollButton->zafRegion.left)
				rightScrollButton->SetDisabled(false);
			else
				rightScrollButton->SetDisabled(true);

			leftScrollButton->SetVisible(true);
			rightScrollButton->SetVisible(true);
		}

		if (Current())
		{
			Current()->Event(S_COMPUTE_SIZE);
			Current()->OSSize();
		}

		// Update background around page.
		ZafEventStruct updateEvent(S_REDISPLAY_REGION);
		updateEvent.region.coordinateType = ZAF_PIXEL;
		updateEvent.region.right = oldRegion.Width() - 1;
		updateEvent.region.left = updateEvent.region.right - 10;
		updateEvent.region.top = 0;
		updateEvent.region.bottom = oldRegion.Height() - 1;
		ZafWindowObject::Event(updateEvent);
		updateEvent.region.left = 0;
		updateEvent.region.top = updateEvent.region.bottom - 10;
		ZafWindowObject::Event(updateEvent);
		updateEvent.region.right = zafRegion.Width() - 1;
		updateEvent.region.left = updateEvent.region.right - 10;
		updateEvent.region.top = 0;
		updateEvent.region.bottom = zafRegion.Height() - 1;
		ZafWindowObject::Event(updateEvent);
		updateEvent.region.left = 0;
		updateEvent.region.top = updateEvent.region.bottom - 10;
		ZafWindowObject::Event(updateEvent);

		if (current)
			Current()->Redisplay();
		}
		break;

	case S_DEINITIALIZE:
		// Check for a deleted help object.
		if (!parent && windowManager->helpObject && windowManager->helpObject->RootObject() == this)
		{
			windowManager->helpObject = ZAF_NULLP(ZafWindowManager);
			ZafEventStruct tEvent;
			tEvent.type = DH_SET_HELP_OBJECT;
			tEvent.windowObject = ZAF_NULLP(ZafWindowObject);
			eventManager->Event(tEvent, E_HELPTIPS);
		}

		// Only the current page should get the deinitialize event;
		ccode = ZafWindowObject::Event(event);
		if (current)
			Current()->Event(event);

		frameID = 0;


//???#if defined(ZAF_WIN32)
//???		if (ZafMSWindowsApp::commonControlsAvailable)
//???			break;
//???#endif
		// Clean up the scroll buttons.
		delete leftScrollButton;
		leftScrollButton = ZAF_NULLP(ZafButton);
		delete rightScrollButton;
		rightScrollButton = ZAF_NULLP(ZafButton);
		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;

	case NOTEBOOK_SCROLL_LEFT:
	case NOTEBOOK_SCROLL_RIGHT:
		{
		int scrollValue = 0;
		if (ccode == NOTEBOOK_SCROLL_LEFT && scrollIndex > 0)
		{
			scrollIndex--;
			ZafWindowObject *tab = (ZafWindowObject *)support.Get(scrollIndex);
			scrollValue = tab->zafRegion.Width() - 4;
//???			if (scrollIndex == 0)
//???				scrollValue -= 6;
		}
		else if (ccode == NOTEBOOK_SCROLL_RIGHT && SupportLast()->zafRegion.right - 2 >= zafRegion.Width() - 2 * SCROLL_BUTTON_SIZE)
		{
			ZafWindowObject *tab = (ZafWindowObject *)support.Get(scrollIndex);
			scrollValue = -tab->zafRegion.Width() + 4;
//???			if (scrollIndex == 0)
//???				scrollValue += 6;
			scrollIndex++;
		}

		if (scrollValue)
		{
			for (ZafWindowObject *object = SupportFirst(); object; object = object->Next())
			{
				object->zafRegion.left += scrollValue;
				object->zafRegion.right += scrollValue;
			}

			ZafRegionStruct redisplayRegion = zafRegion;
			redisplayRegion.bottom = redisplayRegion.top + (tabHeight ? tabHeight : DEFAULT_TAB_HEIGHT) - 1 + (Bordered() ? ZAF_BORDER_SIZE : 0);
			parent->Event(ZafEventStruct(S_REDISPLAY_REGION, 0, redisplayRegion));
		}

		if (scrollIndex == 0)
			leftScrollButton->SetDisabled(true);
		else
			leftScrollButton->SetDisabled(false);
		if (SupportLast()->zafRegion.right > leftScrollButton->zafRegion.left)
			rightScrollButton->SetDisabled(false);
		else
			rightScrollButton->SetDisabled(true);
		}
		break;

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

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

ZafRegionStruct ZafNotebook::MaxRegion(ZafWindowObject *, ZafVtJustify, ZafHzJustify)
{
	ZafRegionStruct maxRegion;
	maxRegion.coordinateType = ZAF_PIXEL;
//???#if defined ZAF_WIN32
//???	if (ZafMSWindowsApp::commonControlsAvailable)
//???	{
//???		RECT rect;
//???		rect.left = rect.top = 0;
//???		rect.right = zafRegion.Width() - 1;
//???		rect.bottom = zafRegion.Height() - 1;
//???		SendMessage(screenID, TCM_ADJUSTRECT, (WPARAM)FALSE, (LPARAM)&rect);
//???
//???		maxRegion.left = rect.left;
//???		maxRegion.top = rect.top;
//???		maxRegion.right = rect.right;
//???		maxRegion.bottom = rect.bottom;
//???	}
//???	else
//???	{
//???		maxRegion.left = ZAF_SHADOW_SIZE + ZAF_NOTEBOOK_MARGIN;
//???		maxRegion.top = tabHeight + ZAF_NOTEBOOK_MARGIN;
//???		maxRegion.right = zafRegion.Width() - ZAF_SHADOW_SIZE - ZAF_NOTEBOOK_MARGIN - 1;
//???		maxRegion.bottom = zafRegion.Height() - ZAF_SHADOW_SIZE - ZAF_NOTEBOOK_MARGIN - 1;
//???	}
//???#else
	maxRegion.left = ZAF_SHADOW_SIZE + ZAF_NOTEBOOK_MARGIN;
	maxRegion.top = (tabHeight ? tabHeight : DEFAULT_TAB_HEIGHT) + ZAF_NOTEBOOK_MARGIN;
	maxRegion.right = zafRegion.Width() - ZAF_SHADOW_SIZE - ZAF_NOTEBOOK_MARGIN - 1;
	maxRegion.bottom = zafRegion.Height() - ZAF_SHADOW_SIZE - ZAF_NOTEBOOK_MARGIN - 1;

	if (Bordered())
		maxRegion -= ZAF_BORDER_SIZE;
//???#endif

	return maxRegion;
}

int ZafNotebook::SetCurrentPage(int setCurrentPage)
{
	if (CurrentPage() != setCurrentPage && setCurrentPage >= FirstPage() && setCurrentPage <= LastPage())
	{
//???#if defined(ZAF_WIN32)
//???			if (ZafMSWindowsApp::commonControlsAvailable)
//???				SendMessage(screenID, TCM_SETCURSEL, (WPARAM)setCurrentPage, (LPARAM)0);
//???			else
//???			{
//???#endif
		if (current)
		{
			ZafWindowObject *currentTab = (ZafWindowObject *)support.Get(Index(current));
			currentTab->SetSelected(false);
			if (screenID)
			{
				ZafRegionStruct redisplayRegion = currentTab->zafRegion;
				redisplayRegion.left += zafRegion.left;
				redisplayRegion.top += zafRegion.top;
				redisplayRegion.right += zafRegion.left;
				redisplayRegion.bottom += zafRegion.top;
				parent->Event(ZafEventStruct(S_REDISPLAY_REGION, 0, redisplayRegion));
				Current()->Event(ZafEventStruct(S_DESTROY));
			}
		}
		SetCurrent(Get(setCurrentPage));
		if (current)
		{
			ZafWindowObject *currentTab = (ZafWindowObject *)support.Get(Index(current));
			currentTab->SetSelected(true);

			if (screenID)
			{
				int currentTabIndex = support.Index(currentTab);

				// Scroll tab into view, if necessary, or just redisplay.
				if (leftScrollButton->Visible() && currentTab->zafRegion.right > leftScrollButton->zafRegion.left)
					while (currentTab->zafRegion.right > leftScrollButton->zafRegion.left)
						Event(NOTEBOOK_SCROLL_RIGHT);
				else if (leftScrollButton->Visible() && scrollIndex - 1 >= currentTabIndex)
					while (scrollIndex - 1 >= currentTabIndex)
						Event(NOTEBOOK_SCROLL_LEFT);
				else
				{
					ZafRegionStruct redisplayRegion = currentTab->zafRegion;
					redisplayRegion.left += zafRegion.left;
					redisplayRegion.top += zafRegion.top;
					redisplayRegion.right += zafRegion.left;
					redisplayRegion.bottom += zafRegion.top;
					parent->Event(ZafEventStruct(S_REDISPLAY_REGION, 0, redisplayRegion));
				}

				// Activate the new page.
				Current()->Event(ZafEventStruct(S_CREATE));
				if (Focus())
					Current()->SetFocus(true);
			}
		}
//???#if defined(ZAF_WIN32)
//???			}
//???#endif

		Event(N_CHANGE_PAGE);
	}

	currentPage = CurrentPage();
	return currentPage;
}

ZafEventType ZafNotebook::ScrollEvent(const ZafEventStruct &)
{
	// No processing is done at this level.
	return (S_ERROR);
}

bool ZafNotebook::SetAutomaticUpdate(bool setAutomaticUpdate)
{
	// Defer to the base class.
	return (ZafWindow::SetAutomaticUpdate(setAutomaticUpdate));
}

ZafWindowObject *ZafNotebook::Subtract(ZafWindowObject *object)
{
	ZafWindow *page = DynamicPtrCast(object, ZafWindow);
	if (page)
	{
		ZafNotebookTab *tab = (ZafNotebookTab *)support.Get(Index(page));
		ZafWindow::Subtract(tab);
		ZafWindow::Subtract(page);

		// Won't be a tab when first loading a persisted notebook.
		if (tab)
		{
			tab->RestoreSupportObjects(page);
			delete tab;
		}
	}

	return page;
}

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);
}

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)
{
	ZafWindowObject *tab = (ZafWindowObject *)support.Get(page);
	return tab->SetText(text);
}

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

const ZafIChar *ZafNotebook::TabText(int page)
{
	ZafWindowObject *tab = (ZafWindowObject *)support.Get(page);
	return tab->Text();
}

// ----- 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 &event)
{
	if (editMode && event.InputType() == E_MOUSE)
	{
		ConvertToZafEvent((ZafEventStruct &)event);
		ZafWindowObject *tab;
		for (tab = SupportFirst(); tab; tab = tab->Next())
		{
			if (tab->zafRegion.Overlap(event.position))
				break;
		}
		if (!tab &&
			(!leftScrollButton->Visible() || !leftScrollButton->zafRegion.Overlap(event.position)) &&
			(!rightScrollButton->Visible() || !rightScrollButton->zafRegion.Overlap(event.position)))
			return (ZafWindowObject::Event(event));
		else
		{
			editMode = false;
			OSEvent(event);
			editMode = true;
		}
	}

	ZafEventType ccode = event.osEvent.message;
	switch (ccode)
	{
	case WM_KEYDOWN:
		{
		int keyValue = (int)event.osEvent.wParam;
		if (keyValue == VK_TAB && GetKeyState(VK_CONTROL) & 0x8000)
		{
			if (GetKeyState(VK_SHIFT) & 0x8000)
				SetCurrentPage(CurrentPage() - 1);
			else
				SetCurrentPage(CurrentPage() + 1);
		}
		else
			ccode = ZafWindow::OSEvent(event);

		if (ccode == S_UNKNOWN)
		{
			if (keyValue == VK_NEXT)
				SetCurrentPage(CurrentPage() + 1);
			else if (keyValue == VK_PRIOR)
				SetCurrentPage(CurrentPage() - 1);
		}
		}
		break;

	case WM_LBUTTONDOWN:
		{
		ccode = ZafWindow::OSEvent(event);

//???#if defined(ZAF_WIN32)
//???		if (ZafMSWindowsApp::commonControlsAvailable)
//???			break;
//???#endif
		ConvertToZafEvent((ZafEventStruct &)event);
		if (leftScrollButton->Visible() &&
			leftScrollButton->zafRegion.Overlap(event.position))
			break;
		int index = 0;
		for (ZafWindowObject *tab = SupportFirst(); tab; tab = tab->Next())
		{
			if (tab->zafRegion.Overlap(event.position) && !tab->Disabled())
			{
				SetCurrentPage(index);
				break;
			}
			index++;
		}
		}
		break;

//???#if defined(ZAF_WIN32)
//???	case WM_NOTIFY:
//???		{
//???		NMHDR *notificationHeader = (NMHDR *)event.osEvent.lParam;
//???
//???		if (notificationHeader->code == TCN_SELCHANGE)
//???		{
//???			int newPage = SendMessage(screenID, TCM_GETCURSEL, (WPARAM)0, (LPARAM)0);
//???			if (newPage != currentPage)
//???			{
//???				if (current)
//???					Current()->Event(ZafEventStruct(S_DESTROY));
//???				SetCurrent(ZafList::Get(newPage));
//???				Current()->Event(ZafEventStruct(S_CREATE));
//???				currentPage = newPage;
//???			}
//???		}
//???		}
//???		break;
//???#endif

	case WM_PAINT:
	case WM_ERASEBKGND:
//???#if defined(ZAF_WIN32)
//???		if (ZafMSWindowsApp::commonControlsAvailable)
//???			ccode = DefaultCallback(event);
//???		else
//???			ccode = ZafWindowObject::Event(event);
//???#else
		ccode = ZafWindowObject::OSEvent(event);
//???#endif
		break;

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

	return (ccode);
}

void ZafNotebook::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	ZafWindow::OSMapPalette(palette, type, state);
}

void ZafNotebook::OSRegisterObject(void)
{
//???#if defined(ZAF_WIN32)
//???	if (ZafMSWindowsApp::commonControlsAvailable)
//???	{
//???		DWORD dwStyle = WS_CHILD | TCS_TABS | TCS_RAGGEDRIGHT | WS_VISIBLE;
//???
//???		// Create the notebook.
//???		InitCommonControls();
//???		screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, WC_TABCONTROL, dwStyle);
//???	}
//???	else
//???	{
//???		// Set the Windows styles.
//???		DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
//???		if (visible)
//???			dwStyle |= WS_VISIBLE;
//???		if (disabled && !editMode)
//???			dwStyle |= WS_DISABLED;
//???
//???		// Create the window.
//???		screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, NULL, dwStyle);
//???	}
//???#else
	// Set the Windows styles.
	DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
	if (visible)
		dwStyle |= WS_VISIBLE;
	if (Disabled() && !editMode)
		dwStyle |= WS_DISABLED;

	// Create the window.
	screenID = ZafMSWindowsApp::CreateSubclassedWindow(this, NULL, dwStyle);
//???#endif
}

OSWindowID ZafNotebook::OSScreenID(ZafScreenIDType) const
{
	return (screenID);
}

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

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafNotebook::DrawShadow(ZafRegionStruct &region, int depth, ZafEventType ccode)
{
	return (ZafWindowObject::DrawShadow(region, depth, ccode));
}
#endif

ZafError ZafNotebook::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	return (ZafWindow::OSUpdatePalettes(zafTypes, osTypes));
}
