// mfcbind.cpp : Example of Binder-compatible utility functions
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

//BINDER:
//	This module contains functions specific to Binder support.
//  The code here is used to add special registry entries used by
//	Binder-Compatible DocObjects, in addition to registering
//	the standard entries generated by the MFC libraries.
//
//	The following keys are written only if no existing entries 
//	are found:
//		HKEY_CLASSES_ROOT\<ProgID>\DocObject
//		HKEY_CLASSES_ROOT\CLSID\<clsid>\DocObject
//		HKEY_CLASSES_ROOT\CLSID\<clsid>\Printable
//
//	The following keys will overwrite any existing values:
//		HKEY_CLASSES_ROOT\CLSID\<clsid>\DefaultExtension
//BINDER_END

#include "stdafx.h"
#include "mfcbind.h"
#include <afxpriv.h>	// for conversion helpers

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

//////////////////////////////////////////////////////////////////////////////
// data for MfcBinderUpdateRegistry functionality
//       %1 = Class ID, formatted as a string
//       %2 = ProgID
//       %3 = Document file extension
//       %4 = Document filter name displayed in File Open dialog
//
static const TCHAR szCLSIDDocObject[] = 
   _T("CLSID\\%1\\DocObject\0" _T("0"));
static const TCHAR szProgIDDocObject[] = 
   _T("%2\\DocObject\0" _T("0"));
static const TCHAR szPrintable[] = 
   _T("CLSID\\%1\\Printable\0");
static const TCHAR szDefaultExt[] = 
   _T("CLSID\\%1\\DefaultExtension\0%3,%4");

static const LPCTSTR rglpszBindRegister[] =
{
   szCLSIDDocObject, szProgIDDocObject, szPrintable, NULL
};

static const LPCTSTR rglpszBindOverwrite[] =
{
	szDefaultExt, NULL
};
				 
void MfcBinderUpdateRegistry(const CDocTemplate* pDocTemplate, 
                           OLE_APPTYPE nAppType, 
                           LPCTSTR* rglpszRegister,
                           LPCTSTR* rglpszOverwrite)
{
	USES_CONVERSION;
	ASSERT(pDocTemplate != NULL);

	// Find the COleTemplateServer associated with the document 
	// template
	COleTemplateServer* pServer = 
		(COleTemplateServer*)pDocTemplate->m_pAttachedFactory;

	if (pServer == NULL)
		return;

	// Do the normal registration for an OLE document type
	pServer->UpdateRegistry(nAppType, rglpszRegister, rglpszOverwrite);

	// Retrieve the values needed for the Binder-Compatible
	// registry entries. 
	CString strProgID;
	CString strFilterName;
	CString strFilterExt;

	TRY
	{
		if (!pDocTemplate->GetDocString(strProgID, CDocTemplate::regFileTypeId) || 
		    strProgID.IsEmpty())
		{
			AfxThrowUserException();
		}

		if (!pDocTemplate->GetDocString(strFilterName, CDocTemplate::filterName) || 
		    strFilterName.IsEmpty())
		{
			AfxThrowUserException();
		}
		
		if (!pDocTemplate->GetDocString(strFilterExt, CDocTemplate::filterExt) ||
    	   strFilterExt.IsEmpty())
		{
			AfxThrowUserException();
		}
	}
	CATCH(CResourceException, pEx)
	{
		TRACE0("Error: not enough information in DocTemplate to"
			" register Binder-compatible document\n");
		return;
	}
	END_CATCH

	LPOLESTR lpszClassID;
	::StringFromCLSID(pServer->GetClassID(), &lpszClassID);
	if (lpszClassID == NULL)
	{
		TRACE0("Error: Could not generate CLSID in MfcBinderUpdateRegistry\n");
	    return;
	}

	// Use the helper function AfxOleRegisterHelp() to add the 
	// Binder-Compatible entries to the registry.
	LPCTSTR rglpszSymbols[4];
	rglpszSymbols[0] = OLE2T(lpszClassID);
	rglpszSymbols[1] = strProgID;
	rglpszSymbols[2] = strFilterExt;
	rglpszSymbols[3] = strFilterName;

	BOOL bResult;
	bResult = AfxOleRegisterHelper((LPCTSTR*)rglpszBindRegister, 
	                               rglpszSymbols, 
	                               sizeof(rglpszSymbols)/sizeof(LPCTSTR), 
	                               FALSE);
	if (bResult)
		bResult = AfxOleRegisterHelper((LPCTSTR*)rglpszBindOverwrite, 
		                               rglpszSymbols, 
		                               sizeof(rglpszSymbols)/sizeof(LPCTSTR), 
		                               TRUE);

	// free memory for class ID
	ASSERT(lpszClassID != NULL);
	AfxFreeTaskMem(lpszClassID);

	if (!bResult)
		AfxMessageBox(BIND_IDP_FAILED_TO_AUTO_REGISTER);
}

HMENU MfcBinderMergeMenus(CMenu* pMenuShared, CMenu* pMenuSource,
                        LONG* lpMenuWidths, int iWidthIndex)
{
	BOOL  bHelpMergedAsSubMenu = FALSE;
	HMENU hMenuHelpSubMenu = NULL;

	// copy the popups from the pMenuSource
	int cMenuItems = pMenuSource->GetMenuItemCount();
	int cGroupWidth = 0; // number of items in current group
	int nPosition = 0;   // position in shared menu

	// insert at appropriate spot depending on iWidthIndex
	ASSERT(iWidthIndex == 0 || iWidthIndex == 1);
	if (iWidthIndex == 1)
		nPosition = (int)lpMenuWidths[0];
	
	int nItem;
	for (nItem = 0; nItem < cMenuItems; nItem++)
	{
		// get the HMENU of the popup
		HMENU hMenuPopup = ::GetSubMenu(pMenuSource->m_hMenu, nItem);

		// separators move us to next group
		UINT state = pMenuSource->GetMenuState(nItem, MF_BYPOSITION);
		if (hMenuPopup == NULL && (state & MF_SEPARATOR) != 0)
		{
			ASSERT(iWidthIndex <= 5);   // servers should not touch past 5
			lpMenuWidths[iWidthIndex] += cGroupWidth;
			cGroupWidth = 0;
			if (iWidthIndex < 5)
				nPosition += (int)lpMenuWidths[iWidthIndex+1];
			iWidthIndex += 2;
		}
		else
		{
			CMenu* pMenuHelp = NULL;
			// first check whether this is the Help group
			if (iWidthIndex == 5)
			{
				// if so, check to see if container has Help menu items
				if (lpMenuWidths[iWidthIndex] == 1)
				{
					// Get the container's Help menu
					pMenuHelp = pMenuShared->GetSubMenu(nPosition);
				}
			}

			// get the menu item text
			CString strItemText;
			int nLen = pMenuSource->GetMenuString(nItem, strItemText, MF_BYPOSITION);

			// popups are handled differently than normal menu items
			if (hMenuPopup != NULL)
			{
				if (::GetMenuItemCount(hMenuPopup) != 0)
				{
					// strip the HIBYTE because it contains a count of items
					state = LOBYTE(state) | MF_POPUP;   // must be popup

					// non-empty popup -- add it to the shared menu bar
					if (pMenuHelp != NULL)
					{
						// Container has help items - add as submenu
						// Name of our help menu should indicate the server
						CString strObjHelp(AfxGetAppName());
						strObjHelp += ' ';

						// add item name, skipping ampersands
						int nChar;
						for (nChar = 0; nChar < nLen; nChar++)
							if (strItemText[nChar] != '&')
								strObjHelp += strItemText[nChar];

						pMenuHelp->AppendMenu(MF_POPUP, (UINT)hMenuPopup,
							strObjHelp);

						// Clear the count of Help group items and
						// add Help menu to Window group
						lpMenuWidths[iWidthIndex] = 0;
						lpMenuWidths[iWidthIndex-1]++;

						bHelpMergedAsSubMenu = TRUE;
						hMenuHelpSubMenu = hMenuPopup;
						continue;
					}
					else
					{
						// if we didn't do special Help menu merging, just 
						// insert the menu
						pMenuShared->InsertMenu(nPosition, state | MF_BYPOSITION,
							(UINT)hMenuPopup, strItemText);
						nPosition++;
						cGroupWidth++;
					}
				}
			}
			else if (nLen > 0)
			{
				// only non-empty items are added
				ASSERT(!strItemText.IsEmpty());

				if (pMenuHelp != NULL)
				{
					// Container has help items - add to its submenu
					pMenuHelp->AppendMenu(MF_STRING, 
										pMenuSource->GetMenuItemID(nItem), 
										strItemText);

					// Clear the count of Help group items and
					// add Help menu to Window group
					lpMenuWidths[iWidthIndex] = 0;
					lpMenuWidths[iWidthIndex-1]++;

					bHelpMergedAsSubMenu = TRUE;
					continue;
				}
				else
				{
					// here the state does not contain a count in the HIBYTE
					pMenuShared->InsertMenu(nPosition, state | MF_BYPOSITION,
						pMenuSource->GetMenuItemID(nItem), strItemText);
					nPosition++;
					cGroupWidth++;
				}
			}
		}
	}

	// set the last group width
	if (!bHelpMergedAsSubMenu)
	{
		ASSERT(iWidthIndex <= 5);   // servers should not touch past 5
		lpMenuWidths[iWidthIndex] += cGroupWidth;
	}

	return hMenuHelpSubMenu;
}

void MfcBinderUnmergeMenus(CMenu* pMenuShared, CMenu* pMenuSource, 
                         CMenu* pMenuHelpPopup)
{
	int cOurItems = pMenuSource->GetMenuItemCount();
	int cMenuItems = pMenuShared->GetMenuItemCount();

	for (int i = cMenuItems-1; i >= 0; i--)
	{
		// check out the popup menus
		HMENU hMenuPopup = ::GetSubMenu(pMenuShared->m_hMenu, i);
		if (hMenuPopup != NULL)
		{
         // if we have a Help submenu, check to see if it appears
         // in this submenu somewhere
         if (pMenuHelpPopup)
         {
            int cPopupItems = ::GetMenuItemCount(hMenuPopup);
            for (int k = 0; k < cPopupItems; k++)
            {
               if (::GetSubMenu(hMenuPopup, k) == pMenuHelpPopup->m_hMenu)
               {
                  // remove the Help submenu
                  ::RemoveMenu(hMenuPopup, k, MF_BYPOSITION);
                  pMenuHelpPopup = NULL;
                  break;
               }
            }
		}
		else
			{
				// if it is one of ours, remove it from the pMenuShared
				for (int j = 0; j < cOurItems; j++)
				{
					if (::GetSubMenu(pMenuSource->m_hMenu, j) == hMenuPopup)
					{
						// remove the menu from pMenuShared
						pMenuShared->RemoveMenu(i, MF_BYPOSITION);
						break;
					}
				}
			}
		}
	}
}

