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

#include <z_app.hpp>
#include <z_msgwin.hpp>
#include <z_stdarg.hpp>
#include <z_border.hpp>
#include <z_sys.hpp>
#include <z_icon1.hpp>
#include <z_text.hpp>
#include <z_title.hpp>
#include <z_string.hpp>
#include <z_utils.hpp>
#define ZAF_MESSAGE_WINDOW_INFO
#include "lang_def.cpp"
#include "gbl_def.cpp"

// ----- ZafMessageWindow ---------------------------------------------------

#if defined(ZAF_INDEPENDENT)
static ZafPaletteMap ZAF_FARDATA textFieldPaletteMap[] =
{
	{ ZAF_PM_ANY_TYPE, ZAF_PM_ANY_STATE, { ZAF_LINE_SOLID, ZAF_PTN_SOLID_FILL, ZAF_CLR_BLACK, ZAF_CLR_LIGHTGRAY, ZAF_MONO_WHITE, ZAF_MONO_BLACK, ZAF_FNT_DIALOG } }
};
#endif

ZafMessageWindow::ZafMessageWindow(ZafStringData *zTitle,
	ZafIconImage zIconImage, ZafDialogFlags zMsgFlags,
	ZafDialogFlags zDefFlag, const ZafIChar *format, ...) :
	ZafDialogWindow(0, 0, 0, 0),
	msgFlags(0), defFlag(0), iconImage(zIconImage),
	icon(ZAF_NULLP(ZafIcon)), textField(ZAF_NULLP(ZafText)),
	title(ZAF_NULLP(ZafTitle)), firstButton(ZAF_NULLP(ZafButton))
{
	SetDestroyable(false);
	SetSizeable(false);

	// Get message strings.
	messages = ZafLanguageManager::Allocate(className, defaultMessages);

	// Get the formatted text.
	ZafIChar *text = new ZafIChar[1024];
	va_list arguments;
	va_start(arguments, format);
	ZafStandardArg::vsprintf(text, format, &arguments);
	va_end(arguments);

	SetMessageFlags(zMsgFlags);
	SetDefaultMessageFlag(zDefFlag);

	// Use the %s format argument in case the text has %'s in it; otherwise
	// it would crash when SetMessage() tries to find corresponding arguments.
	SetMessage(ZAF_ITEXT("%s"), text);
	title = new ZafTitle(zTitle ? zTitle : new ZafStringData(*zTitle));
	SetIconImage(iconImage);

	delete []text;
}

ZafMessageWindow::ZafMessageWindow(const ZafIChar *zTitle,
	ZafIconImage zIconImage, ZafDialogFlags zMsgFlags,
	ZafDialogFlags zDefFlag, const ZafIChar *format, ...) :
	ZafDialogWindow(0, 0, 0, 0),
	msgFlags(0), defFlag(0), iconImage(zIconImage),
	icon(ZAF_NULLP(ZafIcon)), textField(ZAF_NULLP(ZafText)),
	title(ZAF_NULLP(ZafTitle)), firstButton(ZAF_NULLP(ZafButton))
{
	SetDestroyable(false);
	SetSizeable(false);

	// Get message strings.
	messages = ZafLanguageManager::Allocate(className, defaultMessages);

	// Get the formatted text.
	ZafIChar *text = new ZafIChar[1024];
	va_list arguments;
	va_start(arguments, format);
	ZafStandardArg::vsprintf(text, format, &arguments);
	va_end(arguments);

	SetMessageFlags(zMsgFlags);
	SetDefaultMessageFlag(zDefFlag);

	// Use the %s format argument in case the text has %'s in it; otherwise
	// it would crash when SetMessage() tries to find corresponding arguments.
	SetMessage(ZAF_ITEXT("%s"), text);
	title = new ZafTitle(new ZafStringData(zTitle));
	SetIconImage(iconImage);

	delete []text;
}

ZafMessageWindow::ZafMessageWindow(ZafStringData *zTitle,
	ZafIconImage zIconImage, ZafDialogFlags zMsgFlags,
	ZafDialogFlags zDefFlag, ZafUInt16 bufferSize, const ZafIChar *format, ...) :
	ZafDialogWindow(0, 0, 0, 0),
	msgFlags(0), defFlag(0), iconImage(zIconImage),
	icon(ZAF_NULLP(ZafIcon)), textField(ZAF_NULLP(ZafText)),
	title(ZAF_NULLP(ZafTitle)), firstButton(ZAF_NULLP(ZafButton))
{
	SetDestroyable(false);
	SetSizeable(false);

	// Get message strings.
	messages = ZafLanguageManager::Allocate(className, defaultMessages);

	// Get the formatted text.
	ZafIChar *text = new ZafIChar[bufferSize];
	va_list arguments;
	va_start(arguments, format);
	ZafStandardArg::vsprintf(text, format, &arguments);
	va_end(arguments);

	SetMessageFlags(zMsgFlags);
	SetDefaultMessageFlag(zDefFlag);

	// Use the %s format argument in case the text has %'s in it; otherwise
	// it would crash when SetMessage() tries to find corresponding arguments.
	SetMessage(bufferSize, ZAF_ITEXT("%s"), text);
	title = new ZafTitle(zTitle ? zTitle : new ZafStringData(*zTitle));
	SetIconImage(iconImage);

	delete []text;
}

ZafMessageWindow::ZafMessageWindow(const ZafMessageWindow &copy) :
	ZafDialogWindow(copy),
	msgFlags(0), defFlag(0), iconImage(0),
	icon(ZAF_NULLP(ZafIcon)), textField(ZAF_NULLP(ZafText)),
	title(ZAF_NULLP(ZafTitle)),
	firstButton(ZAF_NULLP(ZafButton))
{
	// Get message strings.
	messages = ZafLanguageManager::Allocate(className, defaultMessages);

	SetDefaultButton(ZAF_NULLP(ZafButton));
	Destroy();

	SetMessageFlags(copy.msgFlags);
	SetDefaultMessageFlag(copy.defFlag);

	// Use the %s format argument in case the text has %'s in it; otherwise
	// it would crash when SetMessage() tries to find corresponding arguments.
	if (copy.textField)
		SetMessage(ZAF_ITEXT("%s"), copy.textField->Text());
	else
		SetMessage(ZAF_NULLP(ZafIChar));
	SetIconImage(copy.iconImage);
}

ZafMessageWindow::~ZafMessageWindow(void)
{
	// Free the language information.
	ZafLanguageManager::Free(messages);

	// If the title was created but never added (because the message window
	// was never added to the window manager) delete the title.
	if (title && !title->parent)
		delete title;

	// If the textField was created but never added (because the message window
	// was never added to the window manager) delete the textField.
	if (textField && !textField->parent)
		delete textField;
}

void ZafMessageWindow::AddFields(void)
{
	// Remove any button fields in case some flags were removed.
	if (firstButton)
		SetDefaultButton(ZAF_NULLP(ZafButton));
	ZafWindowObject *object = firstButton;
	while (object)
	{
		ZafWindowObject *nextObject = object->Next();
		Subtract(object);
		delete object;
		object = nextObject;
	}

	firstButton = ZAF_NULLP(ZafButton);

	// Add border.
	if (!Border())
		Add(new ZafBorder);

	// Add the title and system button (if specified).
	if (title && !Title())
	{
		Add(new ZafSystemButton(ZAF_NATIVE_SYSTEM_BUTTON));
		Add(title);
	}

	// Add the icon (if applicable).
	// NOTE: ZAF_APPLICATION_ICON is 0 (the first in the icon array), so it may
	// not be used in this constructor, since iconImage is an unsigned value.
	if (iconImage)
	{
		if (icon)
		{
			Subtract(icon);
			delete icon;
		}

		icon = new ZafIcon(2, 1, ZAF_NULLP(ZafStringData));
		icon->SetIconImage(iconImage);
		icon->SetBordered(false);
		icon->SetNoncurrent(true);
		Add(icon);
	}

	if (textField)
		Add(textField);

	// Add the buttons.
	int index = 0;
	ZafDialogFlags testFlag = 1;
	do
	{
		if (msgFlags & testFlag)
		{
			ZafIChar hotKeyChar = '\0';
			int hotKeyIndex = -1;
			ZafButton *button = new ZafButton(0, 0, 0, 0,
				messages->GetMessage((ZafNumberID)testFlag, false, &hotKeyChar, &hotKeyIndex),
				ZAF_NULLP(ZafBitmapData), ZAF_NATIVE_BUTTON);
			button->SetAllowDefault(true);
			button->SetAutoSize(true);
			button->SetHotKey(hotKeyChar, hotKeyIndex);
			button->SetSendMessageWhenSelected(true);
			button->SetValue(testFlag == ZAF_DIALOG_HELP ? S_HELP : S_DIALOG_FIRST + index);
			Add(button);
			if (testFlag == defFlag)
				SetDefaultButton(button);
			if (!firstButton)
				firstButton = button;
			if (testFlag == defFlag)
				current = button;
		}
		index++;
		testFlag <<= 1;
	} while (testFlag);
}

ZafDialogFlags ZafMessageWindow::ClearMessageFlags(ZafDialogFlags zMsgFlags)
{
	if (!screenID)
		msgFlags &= (ZafUInt16)~zMsgFlags;

	return (msgFlags);
}

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

ZafEventType ZafMessageWindow::Event(const ZafEventStruct &event)
{
	// Check for zinc events.
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	case S_INITIALIZE:
		AddFields();
		ZafDialogWindow::Event(event);
		break;

	case S_COMPUTE_SIZE:
#if	defined(ZAF_INDEPENDENT)
		// If oldRegion == -10000 it means the window is just getting initialized
		// so compute the size of the children and center the window. 
		if (oldRegion.left == -10000 && oldRegion.top == -10000 &&
		 oldRegion.right == -10000 && oldRegion.bottom == -10000)
#endif
		{
		// Compute the text size.
		int textHeight = 0;
		int textWidth = 0;
		if (textField)
		{
			const ZafIChar *text = textField->Text();

			// Initialize the display so that accurate text sizes can be obtained.
			BeginDraw();
			display->SetPalette(textField->LogicalPalette(ZAF_PM_TEXT, PaletteState()));

#			if defined(ZAF_MOTIF)
			ZafRegionStruct textSize = display->TextSize(text);
			textHeight = textSize.Height() + 2 * ZAF_BORDER_SIZE + 1;
			textWidth = textSize.Width() + 2 * ZAF_MARGIN_SIZE + 1;
#			else
			int index = 0;
			int lineHeight = display->TextSize(ZAF_ITEXT("Mg")).Height();
			const ZafIChar *linePtr = text;
			do
			{
				if (text[index] == '\n' || text[index] == '\r' || text[index] == '\0')
				{
					int isCR = (text[index] == '\r');
					ZafIChar saveChar = text[index];
					textField->StringData()->SetChar(index, '\0');

					ZafRegionStruct textSize = display->TextSize(linePtr);
					textHeight += lineHeight;
#					if defined(ZAF_MSWINDOWS)
					textHeight++;
#					endif
					if (textSize.Width() > textWidth)
						textWidth = textSize.Width();

					textField->StringData()->SetChar(index, saveChar);
					if (isCR && text[index + 1] == '\n')
						index++;
					linePtr = text + index + 1;
				}
			} while (text[index++] != '\0');
#			endif

			// Restore the display.
			EndDraw();
		}

#if !defined(ZAF_TEXT_MODE)
		// Add a few pixels to allow for margins;
		textWidth += 2 * ZAF_MARGIN_SIZE;
		textHeight += 2 * ZAF_MARGIN_SIZE;
#else
        textWidth += ZAF_MARGIN_SIZE;
        textHeight += ZAF_MARGIN_SIZE;
#endif

		// Compute the size and number of buttons.
		int buttons = 0;
		int maxTextSize = 0;
		ZafWindowObject *object;
		if (firstButton)
		{
			// Initialize the display so that accurate text sizes can be obtained.
			BeginDraw();
			display->SetFont(firstButton->LogicalPalette(ZAF_PM_TEXT, ZAF_PM_ANY_STATE).font);

			for (object = firstButton; object; object = object->Next())
			{
				ZafRegionStruct textSize = display->TextSize(object->Text());
				if (textSize.Width() > maxTextSize)
					maxTextSize = textSize.Width();
				buttons++;
			}

			// Restore the display.
			EndDraw();
		}
		int buttonWidth = ZafMax(maxTextSize + 2 * display->cellWidth, 10 * display->cellWidth);

		// Allow the icon to size itself.
		if (icon)
			icon->Event(event);

		// Compute the client size.
		int clientWidth = textWidth + 4 * display->cellWidth +
#if !defined(ZAF_TEXT_MODE)
			(icon ? icon->zafRegion.Width() + 2 * display->cellWidth : 0);
#else
            0;
#endif
		if (clientWidth < (buttons * (buttonWidth + display->cellWidth)) + 3 * display->cellWidth)
			clientWidth = (buttons * (buttonWidth + display->cellWidth)) + 3 * display->cellWidth;
		int clientHeight = textHeight + 4 * display->cellHeight;

		// Position the text field.
		if (textField)
		{
			ZafRegionStruct textRegion;
#if !defined(ZAF_TEXT_MODE)
			textRegion.coordinateType = ZAF_PIXEL;
			textRegion.left = 2 * display->cellWidth + (icon ? icon->zafRegion.Width() + 2 * display->cellWidth : 0);
#else
            textRegion.coordinateType = ZAF_CELL;
            textRegion.left = 2 * display->cellWidth;
#endif
			textRegion.top = display->cellHeight;
			textRegion.right = textRegion.left + textWidth - 1;
			textRegion.bottom = textRegion.top + textHeight - 1;
			textField->SetRegion(textRegion);
		}

		// Position the buttons.
		int left = (clientWidth - buttons * (buttonWidth + display->cellWidth) + display->cellWidth) / 2;
		int top = ZafMax(textHeight, icon ? icon->zafRegion.Height() : 0) + 2 * display->cellHeight;
#if defined(ZAF_TEXT_MODE)
		top++;
#endif
		for (object = firstButton; object; object = object->Next())
		{
			object->Event(event);
			int buttonHeight = object->zafRegion.Height();

			ZafRegionStruct region;
#if !defined(ZAF_TEXT_MODE)
			region.coordinateType = ZAF_PIXEL;
#else
            region.coordinateType = ZAF_CELL;
#endif
			region.left = left;
			region.right = region.left + buttonWidth - 1;
			region.bottom = top + display->cellHeight - display->postSpace - 1;
			region.top = region.bottom - buttonHeight + 1;
			object->SetRegion(region);

			left += buttonWidth + display->cellWidth;
		}

		// Set the client region.
#if !defined(ZAF_TEXT_MODE)
		clientRegion.coordinateType = ZAF_PIXEL;
#else
        clientRegion.coordinateType = ZAF_CELL;
#endif
		clientRegion.left = clientRegion.top = 0;
		clientRegion.right = clientWidth  - 1;
		clientRegion.bottom = clientHeight - 1;

		// Center the window.
		zafRegion = clientRegion;
   		zafRegion.right += display->cellWidth;
  		zafRegion.bottom += display->cellHeight;

		windowManager->Center(this);
		}
		break;

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

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

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

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

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

bool ZafMessageWindow::SetAutomaticUpdate(bool )
{
	// automaticUpdate is true for this class.
	automaticUpdate = true;
	return (automaticUpdate);
}

bool ZafMessageWindow::SetBordered(bool )
{
	// bordered is always false for this class.
	bordered = false;
	return (bordered);
}

ZafDialogFlags ZafMessageWindow::SetDefaultMessageFlag(ZafDialogFlags zDefFlag)
{
	if (!screenID)
		defFlag = zDefFlag;

	return (defFlag);
}

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

bool ZafMessageWindow::SetDisabled(bool )
{
	// disabled is false for this class.
	disabled = false;
	return (disabled);
}

const ZafIChar *ZafMessageWindow::SetHelpObjectTip(const ZafIChar *)
{
	// helpObjectTip cannot be set for this class.
	return (ZAF_NULLP(ZafIChar));
}

ZafIconImage ZafMessageWindow::SetIconImage(ZafIconImage zIconImage)
{
	if (!screenID)
		iconImage = zIconImage;

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

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

void ZafMessageWindow::SetMessage(const ZafIChar *format, ...)
{
	// Get the formatted text.
	ZafIChar *text = new ZafIChar[1024];
	va_list arguments;
	va_start(arguments, format);
	ZafStandardArg::vsprintf(text, format, &arguments);
	va_end(arguments);

	if (!screenID)
	{
		if (textField)
			textField->SetText(text);
		else
		{
			// Create the text field.
			textField = new ZafText(0, 0, 0, 0, new ZafStringData(text));
			textField->SetBordered(false);
			textField->SetNoncurrent(true);
			textField->SetViewOnly(true);
			textField->SetParentPalette(true);
			textField->SetWordWrap(false);
#if !defined(ZAF_TEXT_MODE)
			textField->SetOSDraw(false);
#endif
#if defined(ZAF_INDEPENDENT)
			textField->SetUserPaletteData(new ZafPaletteData(textFieldPaletteMap, true));
#endif
		}
	}

	delete []text;
}

void ZafMessageWindow::SetMessage(ZafUInt16 bufferSize, const ZafIChar *format, ...)
{
	// Get the formatted text.
	ZafIChar *text = new ZafIChar[bufferSize];
	va_list arguments;
	va_start(arguments, format);
	ZafStandardArg::vsprintf(text, format, &arguments);
	va_end(arguments);

	if (!screenID)
	{
		if (textField)
			textField->SetText(text);
		else
		{
			// Create the text field.
			textField = new ZafText(0, 0, 0, 0, new ZafStringData(text));
			textField->SetBordered(false);
			textField->SetNoncurrent(true);
			textField->SetViewOnly(true);
			textField->SetParentPalette(true);
			textField->SetWordWrap(false);
#if !defined(ZAF_TEXT_MODE)
			textField->SetOSDraw(false);
#endif
#if defined(ZAF_INDEPENDENT)
			textField->SetUserPaletteData(new ZafPaletteData(textFieldPaletteMap, true));
#endif
		}
	}

	delete []text;
}

ZafDialogFlags ZafMessageWindow::SetMessageFlags(ZafDialogFlags zMsgFlags)
{
	if (!screenID)
		msgFlags |= zMsgFlags;

	return (msgFlags);
}

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

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

ZafRegionType ZafMessageWindow::SetRegionType(ZafRegionType )
{
	// regionType is ZAF_INSIDE_REGION for this class.
	regionType = ZAF_INSIDE_REGION;
	return (regionType);
}

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

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

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

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafMessageWindow::ZafMessageWindow(const ZafIChar *name, ZafObjectPersistence &persist) :
	ZafDialogWindow(name, persist.PushLevel(className, classID, ZAF_PERSIST_ROOT_DIRECTORY)),
	msgFlags(0), defFlag(0), iconImage(0),
	icon(ZAF_NULLP(ZafIcon)), textField(ZAF_NULLP(ZafText)),
	title(ZAF_NULLP(ZafTitle)), firstButton(ZAF_NULLP(ZafButton))
{
	// Read the data.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafMessageWindow::ReadData(persist);

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

	// Get message strings.
	messages = ZafLanguageManager::Allocate(className, defaultMessages);
}

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

void ZafMessageWindow::ReadData(ZafObjectPersistence &persist)
{
	// Read the data.
	ZafIChar titleName[ZAF_MAXNAMELEN], text[1024];
	ZafFile *file = persist.CurrentFile();
	*file >> iconImage >> msgFlags >> defFlag >> text >> titleName;

	// Use the %s format argument in case the text has %'s in it; otherwise
	// it would crash when SetMessage() tries to find corresponding arguments.
	if (*text)
		SetMessage(ZAF_ITEXT("%s"), text);

	// Add the title and system button (if specified).
	if (*titleName)
		title = new ZafTitle(new ZafStringData(titleName, persist));
}

void ZafMessageWindow::Write(ZafObjectPersistence &persist)
{
	// Bypass ZafWindow calls to keep the children from being saved.
	ZafWindowObject::Write(persist.PushLevel(className, classID, ZAF_PERSIST_ROOT_DIRECTORY));
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafWindow::WriteData(persist);
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafMessageWindow::WriteData(persist);

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

void ZafMessageWindow::WriteData(ZafObjectPersistence &persist)
{
	// Write the data.
	ZafFile *file = persist.CurrentFile();
	*file << iconImage;
	*file << msgFlags;
	*file << defFlag;
	const ZafIChar *text = textField ? textField->Text() : ZAF_NULLP(ZafIChar);
	ZafStringData *stringData = ZAF_NULLP(ZafStringData);
	const ZafIChar *titleName = ZAF_NULLP(ZafIChar);
	if (title)
		stringData = title->StringData();
	if (stringData)
		titleName = stringData->StringID();

	*file << text << titleName;

	// Write the string data.
	if (stringData)
		stringData->Write(persist);
}
#endif
