
#include "stdafx.h"

#include "wincvs_winutil.h"
#include "MultiString.h"
#include "wincvs.h"
#include "MainFrm.h"
#include "CustomizeMenus.h"
#include "CvsPrefs.h"
#include "TextBinary.h"


/////////////////////////////////////////////////////////////////////////////
// helper functions declaration

CWnd* FindNextSiblingWindow(CWnd* pWnd, BOOL CheckTabStop);
CWnd* FindNextChildWindow(CWnd* pWnd, BOOL CheckTabStop);
CWnd* GetNextParentWindow(CWnd* pWnd);

CWnd* FindPrevSiblingWindow(CWnd* pWnd, BOOL CheckTabStop);
CWnd* GetPrevParentWindow(CWnd* pWnd);
CWnd* GetEndOfDecendantWindow(CWnd* pWnd);

/////////////////////////////////////////////////////////////////////////////
// inline function(s)

inline BOOL IsTargetWindow(CWnd* pWnd, BOOL CheckTabStop)
{
	return ((!CheckTabStop) || (WS_TABSTOP & pWnd->GetStyle()));
}

/////////////////////////////////////////////////////////////////////////////
// implementation

CWnd* FindNextWindow(CWnd* pWnd, BOOL CheckTabStop)
{
	ASSERT_VALID(pWnd);
	if (pWnd)
	{
		CWnd* pNextWnd;
		pNextWnd = FindNextChildWindow(pWnd, CheckTabStop);
		if (pNextWnd)
			return pNextWnd;

		pNextWnd = FindNextSiblingWindow(pWnd, CheckTabStop);
		if (pNextWnd)
			return pNextWnd;

		CWnd* pParentWnd = GetNextParentWindow(pWnd);
		while ((pParentWnd) && (pNextWnd != pWnd))
		{
			pNextWnd = FindNextChildWindow(pParentWnd, CheckTabStop);
			if (pNextWnd)
				return pNextWnd;
			pParentWnd = GetNextParentWindow(pParentWnd);
		}
	}
	return pWnd;
}

CWnd* GetNextParentWindow(CWnd* pWnd)
{
	if (pWnd)
	{
		if (pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
			return pWnd->GetTopWindow();

		CFrameWnd* pFrameWnd = pWnd->GetParentFrame();
		ASSERT_VALID(pFrameWnd);
		CWnd* pParentWnd = pWnd->GetParent();
		ASSERT_VALID(pParentWnd);

		CWnd* pCrntWnd;
		BOOL bFound = FALSE;
		while ((!bFound) && (pParentWnd != pFrameWnd))
		{
			pCrntWnd = pParentWnd->GetNextWindow();
			if (pCrntWnd)
				bFound = TRUE;
			else
				pParentWnd = pParentWnd->GetParent();
		}
		return (bFound) ? pCrntWnd : pParentWnd->GetTopWindow();
	}
	return NULL;
}

CWnd* FindNextSiblingWindow(CWnd* pWnd, BOOL CheckTabStop)
{
	if (pWnd)
	{
		CWnd* pSiblingWnd = pWnd->GetNextWindow();
		while (pSiblingWnd)
		{
			if (IsTargetWindow(pSiblingWnd, CheckTabStop))
				return pSiblingWnd;
			CWnd* pChildWnd = FindNextChildWindow(pSiblingWnd, CheckTabStop);
			if (pChildWnd)
				return pChildWnd;
			pSiblingWnd = pSiblingWnd->GetNextWindow();
		}
	}
	return NULL;
}

CWnd* FindNextChildWindow(CWnd* pWnd, BOOL CheckTabStop)
{
	if (pWnd)
	{
		CWnd* pChildWnd = pWnd->GetTopWindow();
		while (pChildWnd)
		{
			if (IsTargetWindow(pChildWnd, CheckTabStop))
				return pChildWnd;
			CWnd* pSiblingWnd = FindNextSiblingWindow(pChildWnd, CheckTabStop);
			if (pSiblingWnd)
				return pSiblingWnd;
			pChildWnd = pChildWnd->GetTopWindow();
		}
	}
	return NULL;
}

CWnd* FindPrevWindow(CWnd* pWnd, BOOL CheckTabStop)
{
	ASSERT_VALID(pWnd);
	if (pWnd)
	{
		CWnd* pWndCrntLevel = pWnd;
		while (TRUE)
		{
			CWnd* pFoundWnd;
			pFoundWnd = FindPrevSiblingWindow(pWndCrntLevel, CheckTabStop);
			if (pFoundWnd)
				return pFoundWnd;
			pWndCrntLevel = GetPrevParentWindow(pWndCrntLevel);
			if (!pWndCrntLevel)
			{
				pFoundWnd = GetEndOfDecendantWindow(pWnd->GetParentFrame());
				if (IsTargetWindow(pFoundWnd, CheckTabStop))
					return pFoundWnd;
				pWndCrntLevel = pFoundWnd;
			}
		} 
	}
	return NULL;
}

CWnd* GetPrevParentWindow(CWnd* pWnd)
{
	if (pWnd)
	{
		if (!pWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
		{
			CWnd* pParentWnd = pWnd->GetParent();
			ASSERT_VALID(pParentWnd);
			return pParentWnd;
		}
	}
	return NULL;
}

CWnd* FindPrevSiblingWindow(CWnd* pWnd, BOOL CheckTabStop)
{
	if (pWnd)
	{
		CWnd* pSiblingWnd = pWnd->GetWindow(GW_HWNDPREV);
		while (pSiblingWnd)
		{
			if (IsTargetWindow(pSiblingWnd, CheckTabStop))
				return pSiblingWnd;
			pSiblingWnd = pSiblingWnd->GetWindow(GW_HWNDPREV);
		}
	}
	return NULL;
}

CWnd* GetEndOfDecendantWindow(CWnd* pWnd)
{
	CWnd* pTmpWnd = pWnd;
	CWnd* pPreTmpWnd;
	while (pTmpWnd)
	{
		pPreTmpWnd = pTmpWnd;
		pTmpWnd = pTmpWnd->GetWindow(GW_CHILD);
		if (pTmpWnd)
		{
			pPreTmpWnd = pTmpWnd;
			pTmpWnd = pTmpWnd->GetWindow(GW_HWNDLAST);
		}
	}
	return pPreTmpWnd;
}

static void updateUIMenu(CMenu *pMyMenu, CView* pThis)
{
	CCmdUI state;
	state.m_pMenu = pMyMenu;

	state.m_nIndexMax = pMyMenu->GetMenuItemCount();
	for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax;
	  state.m_nIndex++)
	{
		state.m_nID = pMyMenu->GetMenuItemID(state.m_nIndex);
		if (state.m_nID == 0)
			continue; // menu separator or invalid cmd - ignore it

		ASSERT(state.m_pOther == NULL);
		ASSERT(state.m_pMenu != NULL);
		if (state.m_nID == (UINT)-1)
		{
			// possibly a popup menu, route to first item of that popup
			state.m_pSubMenu = pMyMenu->GetSubMenu(state.m_nIndex);
			if (state.m_pSubMenu == NULL ||
				(state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
				state.m_nID == (UINT)-1)
			{
				continue;       // first item of popup can't be routed to
			}
			state.DoUpdate(pThis, FALSE);    // popups are never auto disabled
		}
		else
		{
			// normal menu item
			// Auto enable/disable if frame window has 'm_bAutoMenuEnable'
			//    set and command is _not_ a system command.
			state.m_pSubMenu = NULL;
			state.DoUpdate(pThis, TRUE);
		}

#	if 0 /* ??? */
		// adjust for menu deletions and additions
		UINT nCount = pMyMenu->GetMenuItemCount();
		if (nCount < state.m_nIndexMax)
		{
			state.m_nIndex -= (state.m_nIndexMax - nCount);
			while (state.m_nIndex < nCount &&
				pMyMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
			{
				state.m_nIndex++;
			}
		}
		state.m_nIndexMax = nCount;
#	endif
	}
}

CMenu* GetSubCMenu(int nStartId, CView* pThis)
{
	CMenu* pTopMenu = AfxGetMainWnd()->GetMenu();
	ASSERT(pTopMenu != 0L);
	if(pTopMenu == 0L)
		return NULL;

	CMenu* pMyMenu=NULL;
	int iPos;
	for (iPos = pTopMenu->GetMenuItemCount()-1; iPos >= 0; iPos--)
	{
		CMenu* pMenu = pTopMenu->GetSubMenu(iPos);
		if (pMenu && pMenu->GetMenuItemID(0) == nStartId)
		{
			pMyMenu = pMenu;
			break;
		}
	}
	ASSERT(pMyMenu != 0L);
	if(pMyMenu == 0L)
		return NULL;

	updateUIMenu(pMyMenu, pThis);

	return pMyMenu;
}

CMenu* _GetMenuEntryRecurs(CMenu *menu, int nStartId, int & pos)
{
	if(menu == 0L)
		return 0l;

	int iPos;
	for (iPos = 0; iPos < menu->GetMenuItemCount(); iPos++)
	{
		UINT id = menu->GetMenuItemID(iPos);
		if(id == nStartId)
		{
			pos = iPos;
			return menu;
		}

		if(id == (UINT)-1)
		{
			CMenu *res = _GetMenuEntryRecurs(menu->GetSubMenu(iPos), nStartId, pos);
			if(res != 0L)
				return res;
		}
	}

	return 0L;
}

CMenu* GetMenuEntry(int nStartId, int & pos)
{
	CMenu* pTopMenu = AfxGetMainWnd()->GetMenu();
	ASSERT(pTopMenu != 0L);

	return _GetMenuEntryRecurs(pTopMenu, nStartId, pos);
}

CMMenuString::CMMenuString(unsigned int maxstr, const char *uniqueName, char * const *defaultStr) :
	CPersistent(uniqueName, kNoClass), fMaxStr(maxstr)
{
	if(defaultStr != 0L)
	{
		int i = 0;
		while(defaultStr[i] != 0L)
		{
			Insert(defaultStr[i++]);
		}
	}
}

CMMenuString::~CMMenuString()
{
}

unsigned int CMMenuString::SizeOf(void) const
{
	NAMESPACE(std) vector<UStr>::const_iterator i;
	unsigned int thesize = sizeof(int);
	
	for(i = fAllStrs.begin(); i != fAllStrs.end(); ++i)
	{
		thesize += (*i).length() + 1;
	}
	return thesize;
}

const void *CMMenuString::GetData(void) const
{
	unsigned int thesize = SizeOf(), tmpsize;
	((CMMenuString *)this)->fBuf.AdjustSize(thesize);
	char *tmp = ((CMMenuString *)this)->fBuf;
	*(int *)tmp = fAllStrs.size();
	tmp += sizeof(int) / sizeof(char);
	
	NAMESPACE(std) vector<UStr>::const_iterator i;
	for(i = fAllStrs.begin(); i != fAllStrs.end(); ++i)
	{
		tmpsize = (*i).length() + 1;
		memcpy(tmp, (const char *)(*i), tmpsize * sizeof(char));
		tmp += tmpsize;
	}
	
	return (char *)fBuf;
}

void CMMenuString::SetData(const void *ptr, unsigned int size)
{
	const char *tmp = (char *)ptr;
	unsigned int thesize;
	fAllStrs.erase(fAllStrs.begin(), fAllStrs.end());
	thesize = *(int *)tmp;
	tmp += sizeof(int) / sizeof(char);
	for(unsigned int i = 0; i < thesize && i < fMaxStr; i++)
	{
		fAllStrs.push_back(UStr(tmp));
		tmp += strlen(tmp) + 1;
	}
}

void CMMenuString::Insert(const char *newstr)
{
	TouchTimeStamp();
	fAllStrs.push_back(UStr(newstr));
}

char *sDefBrowserMenu[] = 
{
	"ID_VIEW_UPDATE",
	"ID_VIEW_COMMIT",
	"ID_SEPARATOR",
	"ID_VIEW_EXPLORE",
	"ID_VIEW_QUERYUPDATE",
	"ID_VIEW_RELOAD",
	"ID_SEPARATOR",
	"ID_VIEW_IMPORT",
	"ID_VIEW_CHECKOUT",
	0L
};

char *sDefFilesMenu[] = 
{
	"ID_VIEW_UPDATE",
	"ID_VIEW_COMMIT",
	"ID_SEPARATOR",
	"ID_VIEW_EXPLORE",
	"ID_VIEW_QUERYUPDATE",
	"ID_VIEW_RELOAD",
	"ID_SEPARATOR",
	"ID_VIEW_EDITSEL",
	"ID_VIEW_EDITSELDEF",
	"ID_SEPARATOR",
	"ID_VIEW_DIFF",
	"ID_VIEW_LOG",
	"ID_VIEW_STATUS",
	"ID_VIEW_GRAPH",
	"ID_QUERY_ANNOTATE",
	0L
};

static CMMenuString gFilesMenu(99, "P_FilesMenu", sDefFilesMenu);
static CMMenuString gBrowserMenu(99, "P_BrowserMenu", sDefBrowserMenu);

CMenu* GetCustomMenu(int whichmenu, CView* pThis)
{
	CMMenuString *multiMenu;

	switch(whichmenu)
	{
	case kCustomBrowserMenu:
		multiMenu = &gBrowserMenu;
		break;
	case kCustomFilesMenu:
		multiMenu = &gFilesMenu;
		break;
	default:
		ASSERT(0);
		return 0L;
	}

	CCJMenu *res = new CCJMenu;
	VERIFY(res->CreatePopupMenu());

	std::vector<UStr> & list = multiMenu->GetList();
	std::vector<UStr>::iterator i;

	for(i = list.begin(); i != list.end(); ++i)
	{
		const CustInvertTable *tab = CustGetByKey(*i);
		if(tab == 0L)
		{
			TRACE1("Hum... was '%s' an old command ? Removing it...",
				(const char *)*i);
			list.erase(i);
			i = list.begin();
		}
	}

	for(i = list.begin(); i != list.end(); ++i)
	{
		const CustInvertTable *tab = CustGetByKey(*i);

		if(tab->cmd == ID_SEPARATOR)
		{
			res->AppendMenu(MF_SEPARATOR);
		}
		else
		{
			CString str = tab->title;
			FormatMenuString(tab->cmd, str);
			res->AppendMenu(MF_STRING, tab->cmd, str, -1);
		}
	}

	res->AppendMenu(MF_SEPARATOR);
	res->AppendMenu(MF_STRING, whichmenu == kCustomFilesMenu ?
		ID_CUST_FILES_MENU : ID_CUST_FOLDER_MENU,
		CString("Customize this menu..."), -1);

	updateUIMenu(res, pThis);

	return res;
}

void CustomizeMenus(int whichmenu)
{
	CMMenuString *multiMenu;

	switch(whichmenu)
	{
	case kCustomBrowserMenu:
		multiMenu = &gBrowserMenu;
		break;
	case kCustomFilesMenu:
		multiMenu = &gFilesMenu;
		break;
	default:
		ASSERT(0);
		return;
	}

	CCustomizeMenus custom(multiMenu);
	if(custom.DoModal() == IDOK)
	{
		gCvsPrefs.save();
	}
}

bool FormatMenuString(int cmd, CString& str)
{
	bool res = false;
	
	switch(cmd)
	{
	case ID_VIEW_EDITSELDEF:
		{
			CString viever("default viewer");
			if( gCvsPrefs.Viewer() != 0L )
			{
				CStr uppath, folder;
				SplitPath(gCvsPrefs.Viewer(), uppath, folder);
				
				viever = folder.empty() ? (uppath.empty() ? gCvsPrefs.Viewer() : uppath) : folder;
				if( viever.Find(" ") > -1 )
				{
					viever = "\"" + viever + "\"";
				}
			}
			str = "View &with " + viever;
			
		}
		res = TRUE;
		break;

	default:
		break;
	}

	return res;
}
