
#include "StdAfx.h"
#include "ShellPidl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////
//
// This source is part of CShellTree - Selom Ofori
// 
// Version: 1.02 (any previously unversioned copies are older/inferior
//
// This code is free for all to use. Mutatilate it as much as you want
// See MFCENUM sample from microsoft

// 
// FUNCTIONS THAT DEAL WITH PIDLs
//

/****************************************************************************
*
*  FUNCTION: Next(LPCITEMIDLIST pidl)
*
*  PURPOSE:  Gets the next PIDL in the list 
*
****************************************************************************/
LPITEMIDLIST
CShellPidl::
Next(
	LPCITEMIDLIST pidl 
)
{
	LPSTR lpMem = (LPSTR)pidl;

	lpMem += pidl->mkid.cb;

	return (LPITEMIDLIST)lpMem;
}

/****************************************************************************
*
*  FUNCTION: GetSize(LPCITEMIDLIST pidl)
*
*  PURPOSE:  Gets the size of the PIDL 
*
****************************************************************************/
UINT
CShellPidl::
GetSize( LPCITEMIDLIST pidl )
{
	UINT cbTotal = 0;
	if( pidl )
	{
		cbTotal += sizeof(pidl->mkid.cb);	/* Null terminator */
		while( pidl->mkid.cb )
		{
			cbTotal += pidl->mkid.cb;
			pidl = Next( pidl );
		}
	}
	return cbTotal;
}

/****************************************************************************
*
*  FUNCTION: CreatePidl(UINT cbSize)
*
*  PURPOSE:  Allocates a PIDL 
*
****************************************************************************/
LPITEMIDLIST
CShellPidl::
CreatePidl(
	UINT cbSize
)
{
	LPMALLOC     lpMalloc = NULL;
	LPITEMIDLIST pidl     = NULL;
	HRESULT      hr;

	hr = SHGetMalloc( &lpMalloc );

	if( FAILED(hr) )
		return 0;

	pidl = (LPITEMIDLIST)lpMalloc->Alloc( cbSize );

	if( pidl )
		ZeroMemory( pidl, cbSize ); /* Zero-init for external task alloc */

	if( lpMalloc )
		lpMalloc->Release();

	return pidl;
}

/****************************************************************************
*
*  FUNCTION: ConcatPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
*
*  PURPOSE:  Concatenates two PIDLs 
*
****************************************************************************/
LPITEMIDLIST
CShellPidl::
ConcatPidls(
	LPCITEMIDLIST pidl1,
	LPCITEMIDLIST pidl2
)
{
	LPITEMIDLIST pidlNew;
	UINT cb1;
	UINT cb2;

	if( pidl1 )  /* May be NULL */
		cb1 = GetSize( pidl1 ) - sizeof(pidl1->mkid.cb);
	else
		cb1 = 0;

	cb2 = GetSize( pidl2 );

	pidlNew = CreatePidl( cb1 + cb2 );
	if( pidlNew )
	{
		if( pidl1 )
			memcpy( pidlNew, pidl1, cb1 );
		memcpy( ((LPSTR)pidlNew) + cb1, pidl2, cb2 );
	}
	return pidlNew;
}

/****************************************************************************
*
*  FUNCTION: CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi)
*
*  PURPOSE:  Copies the ITEMID 
*
****************************************************************************/
LPITEMIDLIST
CShellPidl::
CopyITEMID(
	LPMALLOC     lpMalloc,
	LPITEMIDLIST lpi
)
{
	LPITEMIDLIST lpiTemp;

	lpiTemp = (LPITEMIDLIST)lpMalloc->Alloc( lpi->mkid.cb + sizeof(lpi->mkid.cb) );
	CopyMemory( (PVOID)lpiTemp, (CONST VOID *)lpi, lpi->mkid.cb + sizeof(lpi->mkid.cb) );

	return lpiTemp;
}

/****************************************************************************
*
*  FUNCTION: GetName(LPSHELLFOLDER lpsf,LPITEMIDLIST  lpi,DWORD dwFlags,
*                    LPSTR         lpFriendlyName)
*
*  PURPOSE:  Gets the friendly name for the folder 
*
****************************************************************************/
BOOL
CShellPidl::
GetName(
	LPSHELLFOLDER lpsf,
	LPITEMIDLIST  lpi,
	DWORD         dwFlags,
	LPSTR         lpFriendlyName
)
{
	BOOL   bSuccess=TRUE;
	STRRET str;

	if( NOERROR == lpsf->GetDisplayNameOf( lpi,dwFlags, &str ) )
	{
		switch( str.uType )
		{
			case STRRET_WSTR:
				WideCharToMultiByte( CP_ACP,                   // CodePage
									 0,		                   // dwFlags
									 str.pOleStr,              // lpWideCharStr
									 -1,                       // cchWideChar
									 lpFriendlyName,           // lpMultiByteStr
									 MAX_PATH,
									 //sizeof(lpFriendlyName), // cchMultiByte, wrong. sizeof on a pointer, psk, psk
									 NULL,                     // lpDefaultChar,
									 NULL);                    // lpUsedDefaultChar

				break;

			case STRRET_OFFSET:
				lstrcpy( lpFriendlyName, (LPSTR)lpi + str.uOffset );
				break;

			case STRRET_CSTR:
				lstrcpy( lpFriendlyName, (LPSTR)str.cStr );
				break;

			default:
				bSuccess = FALSE;
				break;
		}
	}
	else
		bSuccess = FALSE;

	return bSuccess;
}

/****************************************************************************
*
*  FUNCTION: GetFullyQualPidl(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi)
*
*  PURPOSE:  Gets the Fully qualified Pidls for the folder 
*
****************************************************************************/
LPITEMIDLIST
CShellPidl::
GetFullyQualPidl(
	LPSHELLFOLDER lpsf,
	LPITEMIDLIST  lpi
)
{
	OLECHAR szOleChar[ MAX_PATH + 1 ];
	char    szBuff   [ MAX_PATH + 1 ];
	LPSHELLFOLDER lpsfDeskTop;
	LPITEMIDLIST  lpifq;
	ULONG ulEaten, ulAttribs;
	HRESULT hr;

	if( !GetName( lpsf, lpi, SHGDN_FORPARSING, szBuff ) )
		return NULL;

	hr=SHGetDesktopFolder( &lpsfDeskTop );

	if( FAILED(hr) )
		return NULL;

	MultiByteToWideChar( CP_ACP,
						 MB_PRECOMPOSED,
						 szBuff,
						 -1,
						 (USHORT *)szOleChar,
						 sizeof(szOleChar) );

	hr = lpsfDeskTop->ParseDisplayName( NULL,
										NULL,
										szOleChar,
										&ulEaten,
										&lpifq,
										&ulAttribs );

	lpsfDeskTop->Release();

	if( FAILED(hr) )
		return NULL;

	return lpifq;
}

/****************************************************************************
*
*  FUNCTION: DoTheMenuThing(HWND hwnd, 
*                           LPSHELLFOLDER lpsfParent,
*                           LPITEMIDLIST  lpi,
*                           LPPOINT lppt)
*
*  PURPOSE: Displays a popup context menu, given a parent shell folder,
*           relative item id and screen location.
*
*  PARAMETERS:
*    hwnd       - Parent window handle
*    lpsfParent - Pointer to parent shell folder.
*    lpi        - Pointer to item id that is relative to lpsfParent
*    lppt       - Screen location of where to popup the menu.
*
*  RETURN VALUE:
*    Returns TRUE on success, FALSE on failure
*
****************************************************************************/
BOOL
CShellPidl::
DoTheMenuThing(
	HWND hwnd,
	LPSHELLFOLDER lpsfParent,
	LPITEMIDLIST  lpi,
	LPPOINT lppt
)
{
	CMINVOKECOMMANDINFO cmi;
	LPCONTEXTMENU lpcm;
	char    szTemp[64];
	int     idCmd;
	HMENU   hMenu;
	BOOL    bSuccess = TRUE;
	HRESULT hr;

	hr = lpsfParent->GetUIObjectOf(
		hwnd,
		1, /* Number of objects to get attributes of */
		(const struct _ITEMIDLIST **)&lpi,
		IID_IContextMenu,
		0,
		(LPVOID *)&lpcm );
	if (SUCCEEDED(hr))  
	{
		hMenu = CreatePopupMenu();

		if (hMenu)
		{
			hr=lpcm->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
			if (SUCCEEDED(hr))
			{
				idCmd = TrackPopupMenu(hMenu, 
					TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, 
					lppt->x, lppt->y, 0, hwnd, NULL);

				if (idCmd)
				{
					cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
					cmi.fMask  = 0;
					cmi.hwnd   = hwnd;
					cmi.lpVerb = MAKEINTRESOURCE(idCmd-1);
					cmi.lpParameters = NULL;
					cmi.lpDirectory  = NULL;
					cmi.nShow        = SW_SHOWNORMAL;
					cmi.dwHotKey     = 0;
					cmi.hIcon        = NULL;
					hr=lpcm->InvokeCommand(&cmi);
					if (!SUCCEEDED(hr))  
					{
						wsprintf(szTemp, "InvokeCommand failed. hr=%lx", hr);
						AfxMessageBox(szTemp);
					}
				}

			}
			else
				bSuccess = FALSE;

			DestroyMenu(hMenu);
		}
		else
			bSuccess = FALSE;

		lpcm->Release();
	}
	else
	{
		wsprintf(szTemp, "GetUIObjectOf failed! hr=%lx", hr);
		AfxMessageBox(szTemp );
		bSuccess = FALSE;
	}
	return bSuccess;
}

/****************************************************************************
*
*  FUNCTION: GetIcon(LPITEMIDLIST lpi, UINT uFlags)
*
*  PURPOSE:  Gets the index for the current icon.  Index is index into system
*            image list.
*
*  PARAMETERS:
*    lpi    - Fully qualified item id list for current item.
*    uFlags - Flags for SHGetFileInfo()
*
*  RETURN VALUE:
*    Icon index for current item.
****************************************************************************/
int
CShellPidl::
GetItemIcon(
	LPITEMIDLIST lpi,
	UINT uFlags
)
{
	SHFILEINFO sfi;

	SHGetFileInfo( (LPCSTR)lpi,
				   0,
				   &sfi,
				   sizeof(SHFILEINFO),
				   uFlags );

	return sfi.iIcon;
}
