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

#include <z_sys.hpp>
#include "m_utils.hpp"

extern "C"
{
#include _MwmUtil_
#include _AtomMgr_
#include _Protocols_
}

static void ZincProtocolCallback(Widget, ZafWindowObject *client, XtPointer)
{
	client->Event(L_SELECT);
}

const int ZAF_MDI_BUTTON_SIZE = 22;

// ----- ZafSystemButton ----------------------------------------------------

ZafWindowObject *ZafSystemButton::Add(ZafWindowObject *object, ZafWindowObject *position)
{
	// Add the item to the menu.
	return (menu.Add(object, position));
}

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafSystemButton::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Check for a valid sub-window.
	if (!screenID || !SystemObject())
		return (S_ERROR);

	// Compute the actual draw region.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();

	// Draw button border.
	DrawShadow(drawRegion, (Depressed() || Selected()) ? -depth : depth, ccode);

	display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
	display->Rectangle(drawRegion, 0, true);

	const int ZAF_MDI_BOX_WIDTH = 10;
	const int ZAF_MDI_BOX_HEIGHT = 6;
	drawRegion.left += (drawRegion.Width() - ZAF_MDI_BOX_WIDTH) / 2;
	drawRegion.top += (drawRegion.Height() - ZAF_MDI_BOX_HEIGHT) / 2;
	drawRegion.right = drawRegion.left + ZAF_MDI_BOX_WIDTH - 1;
	drawRegion.bottom = drawRegion.top + ZAF_MDI_BOX_HEIGHT - 1;
	DrawShadow(drawRegion, 1, ccode);

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

ZafEventType ZafSystemButton::Event(const ZafEventStruct &event)
{
	// Check for special sub-window code.
	ZafEventType ccode = event.type;
	if (SystemObject())
	{
		switch (ccode)
		{
		case S_INITIALIZE:
			ccode = ZafButton::Event(event);
			ZafButton::SetSelectOnDoubleClick(true);
			ZafButton::SetSendMessageWhenSelected(true);
			break;

		case S_COMPUTE_SIZE:
			// This widget will be in the motif frame, not in the client area.
			ZafWindowObject::Event(event);
			zafRegion.bottom = zafRegion.top + ZAF_MDI_BUTTON_SIZE - 1;
			zafRegion.right = zafRegion.left + ZAF_MDI_BUTTON_SIZE - 1;
			ZafButton::SetValue(S_MDI_CLOSE);
			depth = 1;
			break;

		case L_BEGIN_SELECT:
			if (!parent->Focus())
				parent->SetFocus(true);
			ccode = ZafButton::Event(event);
			break;

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

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

	// Check for zinc events.
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		// Set the parent decorations.
		if (parent && !parent->parent)
			DynamicPtrCast(parent, ZafWindow)->SetDecoration(MWM_DECOR_MENU);
		break;

	case S_DEINITIALIZE:
		// Clear the parent decorations.
		if (parent && !parent->parent)
			DynamicPtrCast(parent, ZafWindow)->ClearDecoration(MWM_DECOR_MENU);
		break;

	case S_COMPUTE_SIZE:
		// This widget will be in the motif frame, not in the client area.
		zafRegion.Assign(-1, -1, -1, -1, ZAF_PIXEL);
		break;

	case S_REGISTER_OBJECT:
		// System button is not registered, but its menu items are set 
		// through mwm functions and protocol.
		if (parent && !parent->parent)
		{
			ZafWindowObject *object;
			ZafWindow *window = DynamicPtrCast(parent, ZafWindow);
			OSWindowID shellID = window->OSScreenID(ZAF_SHELLID);
			if (!shellID)
				break; // illegal parent type.
			else if (SystemButtonType() == ZAF_NATIVE_SYSTEM_BUTTON)
			{
				// Determine the overriding menu functions.
				long mwmFunctions = MWM_FUNC_ALL | MWM_FUNC_MAXIMIZE |
				 MWM_FUNC_MINIMIZE | MWM_FUNC_RESIZE | MWM_FUNC_MOVE  |
				 MWM_FUNC_CLOSE;
				for (object = window->SupportFirst(); object; object = object->Next())
					if (object->IsA(ID_ZAF_MAXIMIZE_BUTTON) && window->Sizeable())
						mwmFunctions &= ~MWM_FUNC_MAXIMIZE;
					else if (object->IsA(ID_ZAF_MINIMIZE_BUTTON))
						mwmFunctions &= ~MWM_FUNC_MINIMIZE;
				if (window->Sizeable())
					mwmFunctions &= ~MWM_FUNC_RESIZE;
				if (window->Moveable())
					mwmFunctions &= ~MWM_FUNC_MOVE;
				if (!window->Locked())
					mwmFunctions &= ~MWM_FUNC_CLOSE;

				if (mwmFunctions != MWM_FUNC_ALL)
					XtVaSetValues(shellID, XmNmwmFunctions, mwmFunctions, 0);
			}
			else
			{
				// Determine the default menu functions.
				long mwmFunctions = 0;
				if (!window->Locked())
					mwmFunctions |= MWM_FUNC_CLOSE;
				if (window->Moveable())
					mwmFunctions |= MWM_FUNC_MOVE;
				if (window->Sizeable())
					mwmFunctions |= MWM_FUNC_RESIZE;
				for (object = window->SupportFirst(); object; object = object->Next())
					if (object->IsA(ID_ZAF_MAXIMIZE_BUTTON))
						mwmFunctions |= MWM_FUNC_MAXIMIZE;
					else if (object->IsA(ID_ZAF_MINIMIZE_BUTTON))
						mwmFunctions |= MWM_FUNC_MINIMIZE;
				XtVaSetValues(shellID, XmNmwmFunctions, mwmFunctions, 0);

				// Determine additional protocol items.
				Atom zafAtom = 0, zafProperty = 0;
				char mwmMenu[512];
				char *mwmMenuPtr = mwmMenu;
				ZafPopUpItemType type;
				for (ZafPopUpItem *item = DynamicPtrCast(menu.First(), ZafPopUpItem);
					item; item = DynamicPtrCast(item->Next(), ZafPopUpItem))
					if (item->NumberID() < ZAF_NUMID_OPT_CLOSE || item->NumberID() > ZAF_NUMID_OPT_RESTORE)
					{
						// Check for an allocated zinc property.
						type = item->ItemType();
						if (type != ZAF_ABOUT_OPTION &&
							type != ZAF_SEPARATOR &&
							type != ZAF_SWITCH_OPTION &&
							type != ZAF_EXIT_OPTION &&
							type != ZAF_COPY_OPTION &&
							type != ZAF_CUT_OPTION &&
							type != ZAF_PASTE_OPTION &&
							type != ZAF_NORMAL_ITEM)
							continue; // other cases were handled in the previous section.
						else if (!zafProperty)
						{
							zafProperty = XmInternAtom(XtDisplay(shellID), _XA_MWM_MESSAGES, false);
							XmAddWMProtocols(shellID, &zafProperty, 1);
						}

						// Determine the item parameters.
						char itemHotKey = item->HotKeyChar();
						const char *osText = zafCodeSet->ConvertToOSString(item->Text(), ZAF_NULLP(char), false);
						char itemText[ZAF_MAXNAMELEN];
						int i = 0, j = 0;
						while (osText && osText[i])
						{
							// Motif doesn't allow raw spaces in a system option.
							if (osText[i] == ' ')
								itemText[j++] = '\\';
							itemText[j++] = osText[i++];
						}
						itemText[j] = '\0';

						// Add the protocol to the window shell.
						if (type == ZAF_SEPARATOR)
						{
							sprintf(mwmMenuPtr, "separator f.separator\n");
						}
						else if (itemHotKey)
							sprintf(mwmMenuPtr, "%s _%c Ctrl<Key>%c f.send_msg %ld\n", itemText, itemHotKey, itemHotKey, (long)zafAtom);
						else
							sprintf(mwmMenuPtr, "%s f.send_msg %ld\n", itemText, (long)zafAtom);
						mwmMenuPtr += strlen(mwmMenuPtr);
						XmAddProtocolCallback(shellID, zafProperty, zafAtom++,
							(XtCallbackProc)ZincProtocolCallback, (XtPointer)item);
					}
				if (mwmMenuPtr != mwmMenu)
					XtVaSetValues(shellID, XmNmwmMenu, mwmMenu, 0);
			}
		}
		break;

	// ----- Motif or unknown messages -----
	case E_MOTIF:
		ccode = ZafWindowObject::Event(event);
		break;

	default:
		return (S_UNKNOWN);
	}

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

ZafSystemButtonType ZafSystemButton::SetSystemButtonType(ZafSystemButtonType newSystemButtonType)
{
	// Make sure the request is valid.
	if (systemButtonType == newSystemButtonType || screenID)
		return (systemButtonType);
	systemButtonType = newSystemButtonType;

	// Destroy old system menu options.
	menu.Destroy();

	// Populate with generic items.
	ZafPopUpItem *popItem;
	if (systemButtonType == ZAF_NATIVE_SYSTEM_BUTTON)
	{
		// Add the pop-up menu items.
		popItem = new ZafPopUpItem(messages->GetMessage(ZAF_RESTORE_OPTION));
		popItem->SetItemType(ZAF_RESTORE_OPTION);
		popItem->SetSendMessageWhenSelected(true);
		menu.Add(popItem);
		popItem = new ZafPopUpItem(messages->GetMessage(ZAF_MOVE_OPTION));
		popItem->SetItemType(ZAF_MOVE_OPTION);
		popItem->SetSendMessageWhenSelected(true);
		menu.Add(popItem);
		popItem = new ZafPopUpItem(messages->GetMessage(ZAF_SIZE_OPTION));
		popItem->SetItemType(ZAF_SIZE_OPTION);
		popItem->SetSendMessageWhenSelected(true);
		menu.Add(popItem);
		popItem = new ZafPopUpItem(messages->GetMessage(ZAF_MINIMIZE_OPTION));
		popItem->SetItemType(ZAF_MINIMIZE_OPTION);
		popItem->SetSendMessageWhenSelected(true);
		menu.Add(popItem);
		popItem = new ZafPopUpItem(messages->GetMessage(ZAF_MAXIMIZE_OPTION));
		popItem->SetItemType(ZAF_MAXIMIZE_OPTION);
		popItem->SetSendMessageWhenSelected(true);
		menu.Add(popItem);
		popItem = new ZafPopUpItem(ZAF_NULLP(ZafIChar));
		popItem->SetItemType(ZAF_SEPARATOR);
		menu.Add(popItem);
	}
	popItem = new ZafPopUpItem(messages->GetMessage(ZAF_CLOSE_OPTION));
	popItem->SetItemType(ZAF_CLOSE_OPTION);
	popItem->SetSendMessageWhenSelected(true);
	menu.Add(popItem);

	// Return the current type.
	return (systemButtonType);
}

ZafWindowObject *ZafSystemButton::Subtract(ZafWindowObject *object)
{
	// Defer to the menu object.
	return (menu.Subtract(object));
}

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

void ZafSystemButton::OSRegisterObject(void)
{
	// Only sub-window system buttons needs to be registered.
	if (SystemObject())
	{
		ZafButton::OSRegisterObject();
		XtVaSetValues(screenID, XmNhighlightThickness, 0, 0);
	}
}

void ZafSystemButton::OSSize(void)
{
	// Only sub-window system buttons needs to be sized.
	if (SystemObject())
		ZafButton::OSSize();
}

ZafError ZafSystemButton::OSUpdatePalettes(ZafPaletteType zafTypes, ZafPaletteType osTypes)
{
	// Skip the button level.
	return (ZafWindowObject::OSUpdatePalettes(zafTypes, osTypes));
}
