#include "XPopMenu.h"
#include "XMenuBar.h"
#include "XMenuItm.h"
#include "XPoint.h"
#include "XRes.h"
#include "XResLib.h"
#include "XFrmWnd.h"
#include "XString.h"
#include "xexcept.h"

#include <stdlib.h>
#include <string.h>


/**** XMenuItem ***/

/*@ 
@class XMenuItem
@parent XMenu
@type overview
@symbol _
*/


/*@ XMenuItem::GetText(XString * s)
@group text functions
@remarks Get the text of the item
@parameters XString * buffer   buffer to hold the data
*/
void XMenuItem::GetText(XString * s)
{
	if (t)
	{
		SHORT l = strlen(t);

		memcpy(s->GetBuffer(l + 1), t, l + 1);
		s->ReleaseBuffer(l);
	}
}


XMenuItem :: XMenuItem()
{
	winhandle = 0;
	memset(&item, 0, sizeof(item));
	t = NULL;
}


XMenuItem :: XMenuItem(const char *text, SHORT const id, const LONG style, LONG const position, const BOOL isSeparator)
{
	memset(&item, 0, sizeof(item));
	winhandle = 0;
	if (isSeparator)
	{
		item.afStyle = MIS_SEPARATOR;
		item.iPosition = position;
		t = NULL;
	}
	else
	{
		t = (char *) malloc(strlen(text) + 1);
		strcpy(t, text);
		item.iPosition = position;
		item.afStyle = style;
		item.id = id;
	}
}


XMenuItem :: ~XMenuItem()
{
	if (t)
		free(t);
	winhandle = 0;
}


/*@ XMenuItem::SetText(const char *p)
@group text functions
@remarks Set the text of the item
@parameters XString * text    text to display
*/
void XMenuItem::SetText(const char *p)
{
	t = (char *) realloc(t, strlen(p) + 1);
	strcpy(t, p);
	winhandle = 0;
}


/**** XMenu ****/

BOOL XMenu::UpdateItem(const XMenuItem * item)
{
	return (BOOL) WinSendMsg(winhandle, MM_SETITEM, MPFROM2SHORT(0, TRUE), (MPARAM) & item->item);
}


void XMenu::SetItemHandle(const USHORT itemID, const ULONG handle)
{
	WinSendMsg(winhandle, MM_SETITEMHANDLE, MPFROMSHORT(itemID), (MPARAM) handle);
}


SHORT XMenu::GetItemPos(const USHORT itemID) const
{
	return SHORT1FROMMR(WinSendMsg(winhandle, MM_ITEMPOSITIONFROMID, MPFROM2SHORT(itemID, TRUE), 0));
}


USHORT XMenu::GetDefaultItemID(void) const
{
	return SHORT1FROMMR(WinSendMsg(winhandle, MM_QUERYDEFAULTITEMID, 0, 0));
}


void XMenu::SetDefaultItemID(const USHORT itemID) const
{
	WinSendMsg(winhandle, MM_SETDEFAULTITEMID, MPFROMSHORT(itemID), 0);
}


void XMenu::SelectItem(const USHORT itemID) const
{
	WinSendMsg(winhandle, MM_SELECTITEM, MPFROM2SHORT(itemID, TRUE), 0);
}


/*@ XMenu::GetItemText(const USHORT itemID, XString * buffer)
@group text functions
@remarks Get the text of an item
@parameters USHORT id          the id of the requested item
            XString * buffer   buffer to hold the data
*/
void XMenu::GetItemText(const USHORT itemID, XString * buffer)
{
	SHORT r = SHORT1FROMMR(WinSendMsg(winhandle, MM_QUERYITEMTEXT, MPFROM2SHORT(itemID, 512), MPFROMP(buffer->GetBuffer(512))));

	buffer->ReleaseBuffer(r);
}


XMenu :: XMenu(const XMenu * m)
{
	if (m)
		winhandle = m->winhandle;
}


/*@ XMenu::CascadeItem(const SHORT itemID)
@group misc
@remarks If you call CascadeItem() a menuitem with a submenu becomes cascaded, that means
a button is inserted which enables the submenu. If you have made a menuitem cascaded
you must select a default-item from the submenu of that menuitem by calling CheckItem().
@parameters SHORT itemId                 the menuItem to become cascade
*/
void XMenu::CascadeItem(const SHORT itemID)
{
	MENUITEM menuitem;
	ULONG style;

	WinSendMsg(winhandle, MM_QUERYITEM, MPFROM2SHORT(itemID, TRUE), MPFROMP(&menuitem));

	style = WinQueryWindowULong(menuitem.hwndSubMenu, QWL_STYLE);
	WinSetWindowULong(menuitem.hwndSubMenu, QWL_STYLE, style | MS_CONDITIONALCASCADE);
}


/*@ XMenu::GetItemCount(void) 
@group misc
@remarks Returns the count of items
@returns SHORT itemCount
*/
SHORT XMenu::GetItemCount(void) const
{
	return SHORT1FROMMR(WinSendMsg(winhandle, MM_QUERYITEMCOUNT, 0, 0));
}


/*@ XMenu::GetItemID(const SHORT pos) 
@group item attributes
@remarks Get the ID of an item from its position
@parameters SHORT pos    position of the item
@returns SHORT theID
*/
SHORT XMenu::GetItemID(const SHORT pos) const
{
	return SHORT1FROMMR(WinSendMsg(winhandle, MM_ITEMIDFROMPOSITION, MPFROMSHORT(pos), 0));
}


/*@ XMenu::DeleteItem(const SHORT id) 
@group inserting/removing items
@remarks Removes an item
@parameters SHORT id    the id of the item to delete
*/
void XMenu::DeleteItem(const SHORT id) const
{
	WinSendMsg(winhandle, MM_DELETEITEM, MPFROM2SHORT(id, TRUE), 0);
}


/*@ XMenu::GetItem(XMenuItem * it, const SHORT id)
@group misc
@remarks Get an item of a XMenuBar or XPopupMenu
@parameters XMenuItem * item             buffer to hold the information<BR>
            SHORT theID                  the ID of the requested menu-item
@returns    BOOL  success
*/
BOOL XMenu::GetItem(XMenuItem * it, const SHORT id)
{
	BOOL res = SHORT1FROMMR(WinSendMsg(winhandle, MM_QUERYITEM, MPFROM2SHORT(id, TRUE), MPFROMP(&it->item)));

	if (res)
	{
		SHORT l = SHORT1FROMMR(WinSendMsg(winhandle, MM_QUERYITEMTEXTLENGTH, MPFROM2SHORT(it->item.id, TRUE), 0));

		it->t = (char *) realloc(it->t, l + 1);
		WinSendMsg(winhandle, MM_QUERYITEMTEXT, MPFROM2SHORT(it->item.id, l + 1), (MPARAM) it->t);
		it->winhandle = it->item.hwndSubMenu;
	}
	return res;
}


/*@ XMenu::InsertItem(const XMenuItem * it, XMenuItem * subMenu)
@group inserting/removing items
@remarks inserting/removing items
@parameters XMenuItem * item      item to insert<BR>
            XMenuItem * submenu   a submenu where to insert the item. If nessacary
                                  a submenu is created (default is NULL).
*/
void XMenu::InsertItem(const XMenuItem * it, XMenuItem * subMenu)
{
	if (subMenu)
	{
		if (subMenu->item.hwndSubMenu == 0)
		{
			subMenu->winhandle = subMenu->item.hwndSubMenu = WinCreateWindow(HWND_OBJECT, WC_MENU, (PSZ) "", WS_VISIBLE, 0, 0, 0, 0, HWND_OBJECT, HWND_TOP, 991, NULL, NULL);
			subMenu->item.afStyle |= MIS_SUBMENU;
			WinSendMsg(winhandle, MM_DELETEITEM, MPFROM2SHORT(subMenu->item.id, TRUE), 0);
			WinSendMsg(subMenu->winhandle, MM_INSERTITEM, MPFROMP(&it->item), MPFROMP(it->t));
			WinSendMsg(winhandle, MM_INSERTITEM, MPFROMP(&subMenu->item), MPFROMP(subMenu->t));
			return;
		}
		WinSendMsg(subMenu->item.hwndSubMenu, MM_INSERTITEM, MPFROMP(&it->item), MPFROMP(it->t));
	}
	else
		WinSendMsg(winhandle, MM_INSERTITEM, MPFROMP(&it->item), MPFROMP(it->t));
}


/*@ XMenu::EnableItem(const SHORT id, const BOOL enable)
@group item attributes
@remarks Enables/disables
@parameters SHORT id     the id of the requested item<BR>
            BOOL enable  TRUE=enable, FALSE=disable
*/
void XMenu::EnableItem(const SHORT id, const BOOL enable)
{
	SHORT r = 0;

	if (enable == FALSE)
		r = MIA_DISABLED;

	WinSendMsg(winhandle, MM_SETITEMATTR, MPFROM2SHORT(id, TRUE), MPFROM2SHORT(MIA_DISABLED, r));
}


/*@ XMenu::RemoveItem(const SHORT itemId) 
@group inserting/removing items
@remarks Removes an item
@parameters SHORT id    the id of the item to remove
*/
void XMenu::RemoveItem(const SHORT itemId) const
{
	WinSendMsg(winhandle, MM_REMOVEITEM, MPFROM2SHORT(itemId, TRUE), 0);
}


/*@ XMenu::SetItemText(const SHORT itemId, const char *text) 
@group text functions
@remarks Set the text of an item
@parameters XString * text   text to display
*/
void XMenu::SetItemText(const SHORT itemId, const char *text) const
{
	WinSendMsg(winhandle, MM_SETITEMTEXT, MPFROM2SHORT(itemId, TRUE), (MPARAM) text);
}


/*@ XMenu::CheckItem(const SHORT itemId, const BOOL check)
@group item attributes
@remarks Set an item checked/not checked
@parameters SHORT id    the id of the requested item
            BOOL check  TRUE=check, FALSE=not checked
*/
void XMenu::CheckItem(const SHORT itemId, const BOOL check)
{
	SHORT r = MIA_CHECKED;

	if (check == FALSE)
		r = 0;

	WinSendMsg(winhandle, MM_SETITEMATTR, MPFROM2SHORT(itemId, TRUE), MPFROM2SHORT(MIA_CHECKED, r));
}


/*****  XPopupMenu *****/
/*@ 
@class XPopupMenu
@parent XMenu
@type overview
@symbol _
*/

/*@ XPopupMenu :: XPopupMenu(XResource * res, XWindow * ow)
@remarks Creates a popup-menu, to show it call XPopupMenu::Display()
@parameters  XResource * res      the resource which defines the menu<BR>
             XWindow * win        the owner-window
*/
XPopupMenu :: XPopupMenu(XResource * res, XWindow * ow):XMenu()
{
	owner = ow;
	winhandle = res->GetResourceLibrary()->LoadMenu(ow, res->GetID());
	WinSetWindowPtr(winhandle, 0, this);
}


/*@ XPopupMenu::Display(XPoint * p, USHORT def, ULONG style)
@remarks shows a created popup-menu
@parameters  <t 'ø' c=2>
				øXPoint * point   øThe point where to show the menu in window-coordinates of the owner
             øUSHORT defID   øThe default-menuitem to display under the mouse. Because the default
										style contains POSITIONONITEM this function may fail if this id is zero or unknown
										and the style has not been changed.
             øULONG style      øStyle to display the menu
				</t>
*/
void XPopupMenu::Display(XPoint * p, USHORT def, ULONG style)
{
	WinPopupMenu(owner->GetHandle(), owner->GetHandle(), winhandle, p->GetX(), p->GetY(), def, style);
}


/**** XMenuBar ****/
/*@ 
@class XMenuBar
@parent XMenu
@type overview
@symbol _
*/

/*@ XMenuBar :: XMenuBar(const XFrameWindow * fr, const XResource * res)
@group constructors/destructors
@remarks Create a menubar
@parameters  XFrameWindow * owner		The owner-window which will get the menubar.<BR>
				XResource * resource		A resource which defines the menubar (resource-id and library to load from).
@exceptions	If the method fails an exception of the type XException is thrown.
*/
XMenuBar :: XMenuBar(const XFrameWindow * fr, const XResource * res)
{
	if ((winhandle = WinLoadMenu(fr->frame, (HMODULE) res->GetResourceLibrary()->GetModuleHandle(), res->GetID())) == 0)
		OOLThrow("error loading menu - resources may missing", -10);

	WinSetWindowPtr(winhandle, 0, this);
	WinSetOwner(winhandle, fr->frame);
	WinSendMsg(fr->frame, WM_UPDATEFRAME, (MPARAM) FCF_MENU, (MPARAM) 0);
}


XMenuBar :: XMenuBar(const LONG hwnd)
{
	winhandle = hwnd;
	WinSetWindowPtr(winhandle, 0, this);
}


XMenuBar :: ~XMenuBar()
{
}


/*@ XMenuBar::Show(const BOOL show)
@group misc
@remarks Show/hide a menubar
@parameters BOOL show   TRUE=show, FALSE=hide
*/
void XMenuBar::Show(const BOOL show)
{
	if (show)
	{
		WinSetParent(winhandle, WinQueryWindow(winhandle, QW_OWNER), TRUE);
		WinSendMsg(WinQueryWindow(winhandle, QW_OWNER), WM_UPDATEFRAME, (MPARAM) FCF_MENU, 0);
	}
	else
	{
		WinSetParent(winhandle, HWND_OBJECT, FALSE);
		WinSendMsg(WinQueryWindow(winhandle, QW_OWNER), WM_UPDATEFRAME, (MPARAM) FCF_MENU, 0);
	}
}
