#include <windows.h>
#include "cshellext.h"
#include "resource.h"

extern long ObjectCount ;
extern HINSTANCE hDllInst ;

/*
constructor/destructor 
*/

CShellExt::CShellExt()
{
	m_RefCount = 1 ; 
	InterlockedIncrement (&ObjectCount) ;
    return;
}


CShellExt::~CShellExt(void)
{  
	InterlockedDecrement (&ObjectCount) ;
    return;
}


/*
IUnknown methods
*/

HRESULT CShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv)
{
    if (riid == IID_IUnknown || riid == IID_IShellExtInit)
    {
	//	MessageBox (NULL, "Query IShellExtInit", "", 0) ;
        *ppv = (IShellExtInit *)this;  
        AddRef();
        return S_OK ;
    }
    else if (riid == IID_IShellPropSheetExt)
    {
	//	MessageBox (NULL, "Query IShellPropSheetExt", "", 0) ;
        *ppv = (IShellPropSheetExt *)this;  
        AddRef();
        return S_OK ;
	}
	else if (riid == IID_IDropTarget)
	{
	//	MessageBox (NULL, "Query IDropTarget", "", 0) ;
        *ppv = (IDropTarget *)this;  
        AddRef();
        return S_OK ;
	}
	else if (riid == IID_IShellCopyHook)
	{
		MessageBox (NULL, "Query IID_IShellCopyHook", "", 0) ;
        *ppv = (ICopyHook *)this;  
        AddRef();
        return S_OK ;
	}
	else if (riid == IID_IContextMenu)
	{
	//	MessageBox (NULL, "Query IContextMenu", "", 0) ;
        *ppv = (IContextMenu *)this;  
        AddRef();
        return S_OK ;
	}
	else if (riid == IID_IPersistFile)
	{
	//	MessageBox (NULL, "Query IPersistFile", "", 0) ;
    	 *ppv = (IPersistFile *)this;  
        AddRef();
        return S_OK ;
	}
	else if (riid == IID_IExtractIcon)
	{
		*ppv = (IExtractIcon *) this ;
		AddRef( ) ;
		return S_OK ;
	}
	else
	{
    	*ppv = NULL;       
    	return  E_NOINTERFACE ;
    }
}


STDMETHODIMP_(ULONG) CShellExt::AddRef(void)
{
    m_RefCount ++ ;
    return m_RefCount ;
}


STDMETHODIMP_(ULONG) CShellExt::Release(void)
{
    
    m_RefCount -- ;
    
    if (m_RefCount == 0L)
    {
        delete this;
    }	return 0 ;

    return m_RefCount ;
}

/*
IShellExtInit Interface
*/

STDMETHODIMP CShellExt::Initialize (LPCITEMIDLIST pidlFolder, LPDATAOBJECT lpd, HKEY hkeyProgID)
{

	FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL} ;
	STGMEDIUM stg ;
	HRESULT hr = E_FAIL ;

	if (lpd)
	{
		hr = lpd->GetData (&fe, &stg);

/*
HDROP might contain more than one file. We only know how to handle one at a time.
*/

		if (hr == S_OK && DragQueryFile ((HDROP)stg.hGlobal, (UINT)(-1), NULL, 0) == 1)
		{
			DragQueryFile ((HDROP)stg.hGlobal, 0, m_FileName, sizeof (m_FileName));
			hr = S_OK ;
		}
		else
		{
		   hr = E_FAIL;
		}
 
		ReleaseStgMedium (&stg);
	}

	return hr ;
}

/*
Helper functions
*/

BOOL IsFirstCharA (char *pFileName)
{
	BOOL retval ;

	HANDLE hFile = CreateFile (pFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, NULL) ;

	if (hFile == INVALID_HANDLE_VALUE) 
	{
		return FALSE ;
	}

	HANDLE hMap = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL) ;
	char *pChar = (char *)MapViewOfFile (hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0) ;

	if (pChar == NULL)
	{
		retval = FALSE ;
	}
	else
	{
		if (*pChar == 'A')
		{
			retval = TRUE ;
		}
		else
		{
			retval = FALSE ;
		}
	}
	UnmapViewOfFile (pChar) ;
	CloseHandle (hMap) ;
	CloseHandle (hFile) ;
	return retval ;
}

void SetFirstCharA (char *pFileName, BOOL isit)
{
	HANDLE hFile = CreateFile (pFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, NULL) ;

	if (hFile == INVALID_HANDLE_VALUE) return ;

	HANDLE hMap = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL) ;
	char *pChar = (char *)MapViewOfFile (hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0) ;

	if (pChar != NULL)
	{
		if (isit == TRUE)
		{
			*pChar = 'A' ;
		}
		else
		{
			*pChar = ' ' ;
		}
	}

	UnmapViewOfFile (pChar) ;
	CloseHandle (hMap) ;
	CloseHandle (hFile) ;
}

BOOL APIENTRY PropExtDlgProc( HWND hDlg, UINT message, UINT wParam,	LONG lParam)
{

	switch (message)
	{
		case WM_INITDIALOG:
		{
			PROPSHEETPAGE *psp = (PROPSHEETPAGE *) lParam ;
/*
Attach file name to window for later use.
*/
			char *pName = (char *) psp->lParam ;
			SetProp (hDlg, "CurrentFileName", (void *)psp->lParam ) ; 

			if (IsFirstCharA (pName) == TRUE)
			{
				CheckDlgButton (hDlg, IDC_CHECK1, BST_CHECKED) ;
			}
			else
			{
				CheckDlgButton (hDlg, IDC_CHECK1, BST_UNCHECKED) ;
			}
			return TRUE ;
		}

		case WM_DESTROY:
		{
			char *cp = (char *)GetProp (hDlg, "CurrentFileName") ;
			free (cp) ;
			RemoveProp (hDlg, "CurrentFileName") ;
			return TRUE ;
		}

		case WM_NOTIFY:
    		switch (((NMHDR FAR *) lParam)->code) 
    		{

				case PSN_APPLY:
					char *pName ;
					pName = (char *)GetProp (hDlg, "CurrentFileName") ;

					if (IsDlgButtonChecked (hDlg, IDC_CHECK1))
					{
						SetFirstCharA (pName, TRUE) ;
					}
					else
					{
						SetFirstCharA (pName, FALSE) ;
					}
 	           		SetWindowLong(hDlg,	DWL_MSGRESULT, TRUE);
					break;

				case PSN_KILLACTIVE:
	           		SetWindowLong(hDlg,	DWL_MSGRESULT, FALSE);
					return 1;
					break;

				case PSN_RESET:
	           		SetWindowLong(hDlg,	DWL_MSGRESULT, FALSE);
					break;
    	}

	}
	return FALSE;   
}


/*
IShellPropSheetEx Interface
*/
STDMETHODIMP CShellExt::AddPages (LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
{

    PROPSHEETPAGE psp;
	
    psp.dwSize      = sizeof(psp) ;  
    psp.dwFlags     =  0 ; 
    psp.hInstance   = hDllInst ;

	char *cp = (char *)malloc (sizeof(m_FileName)) ;
	lstrcpy (cp, m_FileName) ;
	psp.lParam = (LPARAM) cp ;

    psp.pszTemplate = MAKEINTRESOURCE(IDD_DIALOG1) ;
    psp.pfnDlgProc  = (DLGPROC)PropExtDlgProc ;


    HPROPSHEETPAGE hPage = CreatePropertySheetPage(&psp);
    
	if (hPage)
    {
        if (!lpfnAddPage(hPage, lParam))
		{
            DestroyPropertySheetPage(hPage);
		}
    }

	return S_OK ;
}

STDMETHODIMP CShellExt::ReplacePage (UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam)
{
	return E_NOTIMPL ;
}

/*
IDropTarget methods
*/

STDMETHODIMP CShellExt::DragEnter(LPDATAOBJECT pIDataSource, 
	DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
	FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
	STGMEDIUM stg;

	if (pIDataSource && (pIDataSource->GetData (&fe, &stg) == S_OK))
	{
		UINT nFiles = DragQueryFile ((HDROP)stg.hGlobal, (UINT) -1, NULL, 0);

		for (UINT i = 0 ; i < nFiles ; i ++)
		{
			MessageBeep (0) ;
		}
	}

	DragOver(grfKeyState, pt, pdwEffect) ;
	ReleaseStgMedium (&stg) ;
    
	return S_OK ;
}
                      

STDMETHODIMP CShellExt::DragOver(DWORD grfKeyState, POINTL pt, 
	LPDWORD pdwEffect)
{
	*pdwEffect = DROPEFFECT_COPY ;
	return S_OK ;
}


/*
Drag operation is leaving our window without dropping.  Release our hold
on the data object.
*/

STDMETHODIMP CShellExt::DragLeave(void)
{
    return S_OK;
}


STDMETHODIMP CShellExt::Drop(LPDATAOBJECT pIDataSource, DWORD grfKeyState, 
	POINTL pt, LPDWORD pdwEffect)
{  
	FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
	STGMEDIUM stg;

	if (pIDataSource && (pIDataSource->GetData (&fe, &stg) == S_OK))
	{

		char names [1024] ;
		int length = 0 ;

		UINT nFiles = DragQueryFile ((HDROP)stg.hGlobal, (UINT) -1, NULL, 0);

		for (UINT i = 0 ; i < nFiles ; i ++)
		{
			length += DragQueryFile ((HDROP)stg.hGlobal, i, &names [length], sizeof (names)-length) ;
			names [length] = '\n' ;
			length ++ ;
			names [length] = 0x00 ;
		}
	
		MessageBox (NULL, names, "Dropped Files", 0) ;
	}


	DragOver(grfKeyState, pt, pdwEffect) ;
	ReleaseStgMedium (&stg) ;
		
	return S_OK;
}


/*
IContextMenu Interface
*/

#define	IDC_FIRSTCHARA	0

STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
	UINT flags ;

	if (IsFirstCharA (m_FileName) == TRUE)
	{
		flags = MF_BYPOSITION | MF_STRING | MF_CHECKED ;
	}
	else
	{
		flags = MF_BYPOSITION | MF_STRING | MF_UNCHECKED ;
	}

	InsertMenu (hMenu, indexMenu, flags, idCmdFirst + IDC_FIRSTCHARA, "&First char 'A'");

	return 1 ;
}

STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
{

	if (LOWORD (lpici->lpVerb) == IDC_FIRSTCHARA)
	{
		if (IsFirstCharA (m_FileName))
		{
			SetFirstCharA (m_FileName, FALSE) ;
		}
		else
		{
			SetFirstCharA (m_FileName, TRUE) ;
		}
	}
	else
	{
		MessageBox (NULL, "Illegal InvokeCommand", "", 0) ;
	}

	return S_OK ;
}

STDMETHODIMP CShellExt::GetCommandString(UINT idCmd, UINT uType, UINT * pwReserved, LPSTR  pszName, UINT cchMax)
{
	if (idCmd == IDC_FIRSTCHARA)
	{
		OSVERSIONINFO osvi ;
		GetVersionEx (&osvi) ;

		if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
		{
			wcsncpy ((WCHAR *) pszName, L"Toggle first character to 'A'", cchMax) ;
		}
		else
		{
			lstrcpyn (pszName, "Toggle first character to 'A'", cchMax) ;
		}
	}
	return S_OK ;
}

/*
IPersistFile methods
*/

STDMETHODIMP CShellExt::Load (LPCOLESTR pFileName, DWORD grfMode)
{  
	WideCharToMultiByte (CP_ACP, 0,(LPCWSTR)pFileName, -1,   
		m_FileName, sizeof (m_FileName), NULL, NULL); 

	return S_OK ;
}


STDMETHODIMP CShellExt::GetCurFile(LPOLESTR * ppszFile)
{
    
	return E_NOTIMPL;
}


STDMETHODIMP CShellExt::GetClassID(LPCLSID pClsID)
{
    return E_NOTIMPL;
}


STDMETHODIMP CShellExt::IsDirty(void)
{
    return E_NOTIMPL ;
}

STDMETHODIMP CShellExt::Save(LPCOLESTR pFileName, BOOL fRemember)
{

	return E_NOTIMPL ;
}


STDMETHODIMP CShellExt::SaveCompleted(LPCOLESTR pszFile)
{
    return E_NOTIMPL;
}


STDMETHODIMP CShellExt::GetIconLocation (UINT   uFlags, LPSTR szIconFile,
			UINT   cchMax, int   * piIndex, UINT  * pwFlags)
{
	*pwFlags = GIL_DONTCACHE ;
	return S_FALSE ;
}

STDMETHODIMP CShellExt::Extract (LPCSTR pszFile, UINT   nIconIndex, HICON   *phiconLarge,
			HICON   *phiconSmall, UINT    nIconSize)
{

	MessageBeep (0) ;

/*
Create a memory DC comaptible with the user's screen. Create a bitmap
the size of an icon that is compatible with it. 
*/

	HDC hScreenDC = CreateDC ("DISPLAY", NULL, NULL, NULL) ;
	HDC hdc = CreateCompatibleDC (hScreenDC) ;

	HBITMAP hbm = CreateCompatibleBitmap (hScreenDC, 32, 32) ;

/*
Open file and read enough of its contents to show in icon.
*/

	HANDLE hFile = CreateFile (m_FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL, NULL) ;
	if (hFile == INVALID_HANDLE_VALUE)
	{
		return S_FALSE ;
	}

	char buf [100] ; DWORD nActual ;
	ReadFile (hFile, buf, sizeof(buf), &nActual, NULL) ;
	CloseHandle (hFile) ;

/*
Select the bitmap into the memory DC, draw on it, then take it out.
*/
	LOGFONT lf = {10, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, "Times New Roman" } ;

	HFONT hf = CreateFontIndirect (&lf) ;
	SelectObject (hdc, hf) ;

	HBITMAP hOld = SelectObject (hdc, hbm) ;

	RECT r ;
	r.top = r.left = 0 ;
	r.right = r.bottom = 32
		;
	DrawText (hdc, buf, nActual, &r, DT_WORDBREAK)  ;
//	SelectObject (hdc, hOld) ;

/*
Create a monochrome bitmap for the AND mask.
*/
	
	char bits [1024] ;
	FillMemory (&bits, 1024, 0x00) ;

/*
Actually create the icon.
*/

		
	BITMAP bm ;
	GetObject(hbm, sizeof(BITMAP), &bm) ;

	char MyBits [10240] ;
	GetBitmapBits (hbm, 10240, MyBits) ;

	HICON hIcon = CreateIcon (hDllInst, 32, 32, 
		(BYTE)bm.bmPlanes, (BYTE)bm.bmBitsPixel,
		(const BYTE *) bits, 
		(const BYTE *)MyBits) ;

	*phiconLarge = hIcon ;
	*phiconSmall = hIcon ;

/*
Clean up.
*/
	DeleteDC (hdc) ;
	DeleteDC (hScreenDC) ;
	DeleteObject (hbm) ;
	DeleteObject (hf) ;

	return S_OK ;
}

