/*
 EasySFTP - Copyright (C) 2010 Kuri-Applications

 AddrCBox.cpp - implementations of CAddressComboBox and CVirtualAddressComboBox
 */

#include "StdAfx.h"
#include "EasySFTP.h"
#include "AddrCBox.h"

#include "IDList.h"

#define COMBO_PADDING_Y  0

class CAddressComboBoxMain;

class CAddressComboEditBox : public CMyWindow
{
public:
	CAddressComboEditBox(CAddressComboBoxMain* pParent) : m_pParent(pParent) { }

protected:
	virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
	LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam);
	LRESULT OnKeyDown(WPARAM wParam, LPARAM lParam);
	LRESULT OnChar(WPARAM wParam, LPARAM lParam);

private:
	CAddressComboBoxMain* m_pParent;
};

class CAddressComboBoxMain : public CMyWindow
{
public:
#pragma warning(disable:4355)
	CAddressComboBoxMain(CAddressComboBox* pParent)
		: m_pParent(pParent)
		, m_wndEdit(this)
		, m_bIgnoreSetFocus(false)
#pragma warning(default:4355)
	{ }

	virtual void PostNcDestroy() { delete this; }

protected:
	virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
	LRESULT OnCreate(WPARAM wParam, LPARAM lParam);
	LRESULT OnCommand(WPARAM wParam, LPARAM lParam);
	LRESULT OnEnable(WPARAM wParam, LPARAM lParam);
	LRESULT OnSize(WPARAM wParam, LPARAM lParam);
	LRESULT OnSetFont(WPARAM wParam, LPARAM lParam);
	//LRESULT OnKeyDown(WPARAM wParam, LPARAM lParam);
	LRESULT OnMouseWheel(WPARAM wParam, LPARAM lParam);

public:
	TEXTMETRIC m_tm;
	//int m_nCurSel;
	CAddressComboBox* m_pParent;
	bool m_bIgnoreSetFocus;
	CAddressComboEditBox m_wndEdit;
};

////////////////////////////////////////////////////////////////////////////////

struct CAddressComboBoxItemData
{
	PIDLIST_ABSOLUTE lpidl;
	//IShellFolder* pFolder;
	int iLevel;
	bool bCurrent;
	int iIcon;
	//HICON hIcon, hIconSel;
	_StringW strDisplayName;
	CAddressComboBoxItemData* pParent;
	CAddressComboBoxItemData* pChild;
	CAddressComboBoxItemData* pNext;

public:
	CAddressComboBoxItemData()
	{
		//hIcon = hIconSel = NULL;
		iIcon = -1;
	}
};

CAddressComboBox::CAddressComboBox(void)
{
	m_bUseDisplayName = false;
	m_nCurSel = -1;
	::SHGetDesktopFolder(&m_pFolderRoot);
	m_pFolder = NULL;
}

CAddressComboBox::~CAddressComboBox(void)
{
	if (m_pFolder)
		m_pFolder->Release();
	m_pFolderRoot->Release();
}

HWND CAddressComboBox::Create(int x, int y, int cx, int cy, HWND hWndParent, UINT uID)
{
	//return CreateExW(0, L"ComboBox", NULL,
	//	WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
	//	CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OWNERDRAWFIXED,
	//	x, y, cx, cy, hWndParent, (HMENU) UIntToPtr(uID));
	return CreateExW(0, L"Static", NULL,
		WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
		x, y, cx, cy, hWndParent, (HMENU) UIntToPtr(uID));
}

// 既に子が1つある場合、重複追加はしない (2つ以上の場合は考慮していない)
// 戻り値: MyComputerがあればそのデータを返す
static CAddressComboBoxItemData* __stdcall AddChildren(HWND hWnd,
	CAddressComboBoxItemData* pParent, IShellFolder* pFolder)
{
	CAddressComboBoxItemData* pData, * pData2, * pChild, * pRet;
	PITEMID_CHILD lp;
	PIDLIST_ABSOLUTE lp2;
	HRESULT hr;
	IEnumIDList* pEnum;
	SFGAOF rgf;
	int iLevel;
	bool bHasEasySFTPRoot = false;

	pRet = NULL;
	iLevel = pParent->iLevel + 1;
	if (iLevel != 1)
		bHasEasySFTPRoot = true;
	hr = pFolder->EnumObjects(hWnd,
		SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN | SHCONTF_FASTITEMS,
		&pEnum);
	if (hr == S_OK)
	{
		pData2 = NULL;
		pChild = pParent->pChild;
		pParent->pChild = NULL;
		while (true)
		{
			hr = pEnum->Next(1, &lp, NULL);
			if (hr != S_OK)
			{
				if (bHasEasySFTPRoot)
					break;
				lp = (PITEMID_CHILD) ::DuplicateItemIDList(theApp.m_pidlEasySFTP);
				rgf = SFGAO_FILESYSTEM;
				bHasEasySFTPRoot = true;
			}
			else
			{
				rgf = SFGAO_FILESYSTEM;
				hr = pFolder->GetAttributesOf(1, &lp, &rgf);
				if (FAILED(hr))
					break;
			}
			lp2 = AppendItemIDList(pParent->lpidl, lp);
			::CoTaskMemFree(lp);
			//if (!IsMyComputerIDList(lp2) &&
			//	!(rgf & SFGAO_FILESYSTEM))
			//{
			//	::CoTaskMemFree(lp2);
			//	continue;
			//}
			if (!bHasEasySFTPRoot && IsEqualIDList(lp2, theApp.m_pidlEasySFTP))
				bHasEasySFTPRoot = true;
			if (pChild && IsEqualIDList(pChild->lpidl, lp2))
			{
				pData = pChild;
				pChild = NULL;
			}
			else
			{
				//if (pData2 == NULL)
				//	level++;
				pData = new CAddressComboBoxItemData();
				pData->pParent = pParent;
				pData->iLevel = iLevel;
				pData->bCurrent = false;
				pData->pChild = NULL;
				pData->lpidl = lp2;
			}
			if (pData2)
				pData2->pNext = pData;
			else
				pParent->pChild = pData;
			pData->pNext = NULL;
			pData2 = pData;
			if (IsMyComputerIDList(lp2))
				pRet = pData;
		}
		pEnum->Release();
		if (pChild)
		{
			if (pData2)
				pData2->pNext = pChild;
			else
				pParent->pChild = pChild;
		}
	}
	return pRet;
}

static void __stdcall AddChildrenToComboBox(HWND hWnd, HWND hWndCombo, CAddressComboBoxItemData* pData, CAddressComboBoxItemData* pCur)
{
	int i;
	while (pData)
	{
		i = (int) (::SendMessage(hWndCombo, CB_ADDSTRING, 0, (LPARAM) pData));
		if (pData == pCur)
			::SendMessage(hWnd, CB_SETCURSEL, (WPARAM) IntToPtr(i), 0);
		if (pData->pChild)
			AddChildrenToComboBox(hWnd, hWndCombo, pData->pChild, pCur);
		pData = pData->pNext;
	}
}

void CAddressComboBox::ChangeCurrentFolder(PCIDLIST_ABSOLUTE lpidl)
{
	int nCount = (int) (::SendMessage(m_pWndComboBox->m_hWnd, CB_GETCOUNT, 0, 0));

	CAddressComboBoxItemData* pData, * pData2, * pChild, * pTop, * pCur;
	PIDLIST_ABSOLUTE lp;
	//LPITEMIDLIST lp2;
	IShellFolder* pFolder;
	int level;
	HRESULT hr;

	lp = (PIDLIST_ABSOLUTE) DuplicateItemIDList(lpidl);
	pData2 = NULL;
	pChild = NULL;
	level = 0;
	while (lp)
	{
		pData = new CAddressComboBoxItemData();
		if (pData2)
		{
			pData2->pParent = pData;
			pData->pChild = pData2;
		}
		else
			pData->pChild = NULL;
		if (pData->bCurrent = !pChild)
			pChild = pData;
		pData->lpidl = lp;
		pData->pParent = NULL;
		pData->pNext = NULL;
		pData2 = pData;
		//level++;
		lp = RemoveOneChild(lp);
	}
	pCur = pChild;
	pData = pTop = pData2;
	level = 0;
	while (pData)
	{
		pData->iLevel = level++;
		pData = pData->pChild;
	}

	//if (IsDesktopIDList(lpidl))
	//{
	//	pFolder = pDesktop;
	//	pDesktop->AddRef();
	//}
	//else
	//{
	//	hr = pDesktop->BindToObject(lpidl, NULL, IID_IShellFolder, (void**) &pFolder);
	//}
	//if (SUCCEEDED(hr))
	//{
	//	IEnumIDList* pEnum;
	//	SFGAOF rgf;

	//	hr = pFolder->EnumObjects(m_hWnd,
	//		SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN | SHCONTF_FASTITEMS,
	//		&pEnum);
	//	if (hr == S_OK)
	//	{
	//		pData2 = NULL;
	//		while (true)
	//		{
	//			hr = pEnum->Next(1, &lp, NULL);
	//			if (hr != S_OK)
	//				break;
	//			rgf = SFGAO_FILESYSTEM;
	//			hr = pFolder->GetAttributesOf(1, (LPCITEMIDLIST*) &lp, &rgf);
	//			if (FAILED(hr))
	//				break;
	//			lp2 = AppendItemIDList(lpidl, lp);
	//			::CoTaskMemFree(lp);
	//			if (!IsMyComputerIDList(lp2) &&
	//				!(rgf & SFGAO_FILESYSTEM))
	//			{
	//				::CoTaskMemFree(lp2);
	//				continue;
	//			}
	//			//if (pData2 == NULL)
	//			//	level++;
	//			pData = new CAddressComboBoxItemData();
	//			if (pData2)
	//			{
	//				pData2->pNext = pData;
	//				pData->pParent = pData2->pParent;
	//				pData->iLevel = pData2->iLevel;
	//			}
	//			else
	//			{
	//				pData->pParent = pChild;
	//				pData->iLevel = pChild->iLevel + 1;
	//				pChild->pChild = pData;
	//				pChild = pData;
	//			}
	//			pData->bCurrent = false;
	//			pData->pChild = NULL;
	//			pData->lpidl = lp2;
	//			pData->pNext = NULL;
	//			pData2 = pData;
	//		}
	//		pEnum->Release();
	//	}
	//	pFolder->Release();
	//}

	pData = AddChildren(m_hWnd, pTop, m_pFolderRoot);
	if (pData)
	{
		hr = m_pFolderRoot->BindToObject(pData->lpidl, NULL, IID_IShellFolder, (void**) &pFolder);
		if (SUCCEEDED(hr))
		{
			AddChildren(m_hWnd, pData, pFolder);
			pFolder->Release();
		}
	}

	//AddChildrenToComboBox(m_pWndComboBox->m_hWnd, pTop, pCur);
	AddChildrenToComboBox(m_hWnd, m_pWndComboBox->m_hWnd, pTop, pCur);
	//cur = --level;
	//pData2 = pChild;
	//while (pData2)
	//{
	//	::SendMessage(m_pWndComboBox->m_hWnd, CB_INSERTSTRING, 0, (LPARAM) pData2);
	//	pData2->iLevel = level;
	//	if (pData2->pNext)
	//		pData2 = pData2->pNext;
	//	else
	//		pData2 = pData2->pParent;
	//}
	while (nCount--)
		::SendMessage(m_pWndComboBox->m_hWnd, CB_DELETESTRING, (WPARAM) IntToPtr(nCount), 0);

	//((CAddressComboBoxMain*) m_pWndComboBox)->m_nCurSel = PtrToInt(::SendMessage(m_pWndComboBox->m_hWnd, CB_GETCURSEL, 0, 0));
	m_nCurSel = (int) (::SendMessage(m_pWndComboBox->m_hWnd, CB_GETCURSEL, 0, 0));
	{
		if (IsDesktopIDList(lpidl))
		{
			FillData(pTop);
			m_strRealPath = pTop->strDisplayName;
		}
		else
		{
			IShellFolder* pParent;
			lp = RemoveOneChild(lpidl);
			if (IsDesktopIDList(lp))
			{
				pParent = m_pFolderRoot;
				pParent->AddRef();
				hr = S_OK;
			}
			else
				hr = m_pFolderRoot->BindToObject(lp, NULL, IID_IShellFolder, (void**) &pParent);
			::CoTaskMemFree(lp);
			if (SUCCEEDED(hr))
			{
				STRRET strret;
				strret.pOleStr = NULL;
				strret.uType = STRRET_WSTR;
				lp = (PIDLIST_ABSOLUTE) GetChildItemIDList(lpidl);
				hr = pParent->GetDisplayNameOf((PCITEMID_CHILD) lp,
					SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, &strret);
				if (SUCCEEDED(hr))
				{
					switch (strret.uType)
					{
						case STRRET_WSTR:
							m_strRealPath = strret.pOleStr;
							::CoTaskMemFree(strret.pOleStr);
							break;
						case STRRET_CSTR:
							m_strRealPath = strret.cStr;
							break;
						case STRRET_OFFSET:
							m_strRealPath = (LPCSTR) (((LPCBYTE) lp) + strret.uOffset);
							break;
					}
				}
				else if (!::SHGetPathFromIDListW(lpidl, m_strRealPath.GetBuffer(MAX_PATH)))
				{
					::SHGetPathFromIDListA(lpidl, m_strRealPath.GetBufferA(MAX_PATH));
					m_strRealPath.ReleaseBufferA();
				}
				else
					m_strRealPath.ReleaseBuffer();
				::CoTaskMemFree(lp);
				pParent->Release();
			}
		}
	}
	UpdateRealPath(m_strRealPath);
}

PCIDLIST_ABSOLUTE CAddressComboBox::GetSelectedFolder() const
{
	int i;
	//i = PtrToInt(::SendMessage(m_pWndComboBox->m_hWnd, CB_GETCURSEL, 0, 0));
	i = m_nCurSel;
	//if (i == CB_ERR)
	if (i == -1)
		return NULL;
	CAddressComboBoxItemData* pData = (CAddressComboBoxItemData*)
		::SendMessage(m_pWndComboBox->m_hWnd, CB_GETITEMDATA, (WPARAM) IntToPtr(i), 0);
	return pData ? pData->lpidl : NULL;
}

PCIDLIST_ABSOLUTE CAddressComboBox::FindItemFromDisplayName(LPCWSTR lpszDisplayName) const
{
	int nCount;
	nCount = (int) (::SendMessage(m_pWndComboBox->m_hWnd, CB_GETCOUNT, 0, 0));
	for (int i = 0; i < nCount; i++)
	{
		CAddressComboBoxItemData* pData = (CAddressComboBoxItemData*)
			::SendMessage(m_pWndComboBox->m_hWnd, CB_GETITEMDATA, (WPARAM) IntToPtr(i), 0);
		FillData(pData);
		if (pData && pData->strDisplayName.Compare(lpszDisplayName) == 0)
			return pData->lpidl;
	}
	return NULL;
}

void CAddressComboBox::UpdateRealPath(LPCWSTR lpszRealPath)
{
	((CAddressComboBoxMain*) m_pWndComboBox)->m_wndEdit.SetWindowTextW(lpszRealPath);
}

void CAddressComboBox::RestoreTextBox()
{
	::SendMessage(m_hWnd, CB_SETCURSEL, (WPARAM) IntToPtr(m_nCurSel), 0);
	((CAddressComboBoxMain*) m_pWndComboBox)->m_wndEdit.SetWindowTextW(m_strRealPath);
}

HWND CAddressComboBox::GetEditWindow() const
{
	return ((CAddressComboBoxMain*) m_pWndComboBox)->m_wndEdit;
}

void CAddressComboBox::SendNotify(int code)
{
	NMHDR nmh;
	nmh.code = code;
	nmh.hwndFrom = m_hWnd;
	nmh.idFrom = (UINT_PTR) ::GetDlgCtrlID(m_hWnd);
	::SendMessage(::GetParent(m_hWnd), WM_NOTIFY, (WPARAM) nmh.idFrom, (LPARAM)(LPNMHDR) &nmh);
}

LRESULT CAddressComboBox::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	HANDLE_PROC_MESSAGE(WM_CREATE, OnCreate);
	HANDLE_PROC_MESSAGE(WM_COMMAND, OnCommand);
	HANDLE_PROC_MESSAGE(WM_ENABLE, OnEnable);
	HANDLE_PROC_MESSAGE(WM_SIZE, OnSize);
	HANDLE_PROC_MESSAGE(WM_SETFONT, OnSetFont);
	HANDLE_PROC_MESSAGE(WM_SETFOCUS, OnSetFocus);
	HANDLE_PROC_MESSAGE(CB_SETCURSEL, OnSetCurSel);
	HANDLE_PROC_MESSAGE(WM_MEASUREITEM, OnMeasureItem);
	HANDLE_PROC_MESSAGE(WM_DRAWITEM, OnDrawItem);
	HANDLE_PROC_MESSAGE(WM_DELETEITEM, OnDeleteItem);

#if (CB_GETEDITSEL != 0x140)
	#error Unknown identifiers of CB_* messages
#endif
	if (message >= CB_GETEDITSEL && message <= CB_MSGMAX)
	{
		LRESULT lr = ::SendMessage(m_pWndComboBox->m_hWnd, message, wParam, lParam);
		if (message == CB_ADDSTRING || message == CB_INSERTSTRING ||
			message == CB_DELETESTRING || message == CB_RESETCONTENT)
		{
			m_nCurSel = (int) (::SendMessage(m_pWndComboBox->m_hWnd, CB_GETCURSEL, 0, 0));
#if (CB_ERR != -1)
			if (m_nCurSel == CB_ERR)
				m_nCurSel = -1;
#endif
			if (m_nCurSel == -1)
			{
				m_strRealPath.Empty();
				UpdateRealPath(L"");
			}
		}
		return lr;
	}
	if (message == WM_GETTEXT || message == WM_GETTEXTLENGTH)
	{
		if (m_bUniWindow)
			return ::SendMessageW(((CAddressComboBoxMain*) m_pWndComboBox)->m_wndEdit, message, wParam, lParam);
		else
			return ::SendMessageA(((CAddressComboBoxMain*) m_pWndComboBox)->m_wndEdit, message, wParam, lParam);
	}

	return CMyWindow::WindowProc(message, wParam, lParam);
}

LRESULT CAddressComboBox::OnCreate(WPARAM wParam, LPARAM lParam)
{
	m_pfnWndProc = NULL;

	CAddressComboBoxMain* pMain = new CAddressComboBoxMain(this);
	m_pWndComboBox = pMain;
	if (m_bUniWindow)
	{
		LPCREATESTRUCTW lpcs = (LPCREATESTRUCTW) lParam;
		if (!pMain->CreateExW(0, L"ComboBox", NULL,
			WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
			CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OWNERDRAWFIXED,
			0, 0, lpcs->cx, lpcs->cy, m_hWnd, lpcs->hMenu))
			return 1;
	}
	else
	{
		LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA) lParam;
		if (!pMain->CreateExW(0, L"ComboBox", NULL,
			WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
			CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OWNERDRAWFIXED,
			0, 0, lpcs->cx, lpcs->cy, m_hWnd, lpcs->hMenu))
			return 1;
	}
	{
		RECT rc;
		::GetWindowRect(pMain->m_hWnd, &rc);
		::SetWindowPos(m_hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
	}

	PIDLIST_ABSOLUTE pidlDesktop;
	SHFILEINFO sfi;
	HRESULT hr;

	hr = ::SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop);
	if (FAILED(hr))
		return 1;
	memset(&sfi, 0, sizeof(sfi));
	m_himlSystemSmall = (HIMAGELIST) ::SHGetFileInfo((LPCTSTR) pidlDesktop, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
	::CoTaskMemFree(pidlDesktop);
	if (!m_himlSystemSmall)
		return 1;

	return 0;
}

LRESULT CAddressComboBox::OnCommand(WPARAM wParam, LPARAM lParam)
{
	if ((HWND) lParam == m_pWndComboBox->m_hWnd)
	{
		LRESULT lr;
		register CAddressComboBoxMain* pMain = (CAddressComboBoxMain*) m_pWndComboBox;
		switch (HIWORD(wParam))
		{
			case CBN_DROPDOWN:
				pMain->m_bIgnoreSetFocus = true;
				::SetFocus(pMain->m_hWnd);
				pMain->m_bIgnoreSetFocus = false;
				m_bUseDisplayName = true;
				::ShowWindow(pMain->m_wndEdit, SW_HIDE);
				lr = ::SendMessage(::GetParent(m_hWnd), WM_COMMAND, wParam, (LPARAM) m_hWnd);
				break;
			case CBN_CLOSEUP:
				lr = ::SendMessage(::GetParent(m_hWnd), WM_COMMAND, wParam, (LPARAM) m_hWnd);
				::ShowWindow(pMain->m_wndEdit, SW_SHOW);
				::SetFocus(pMain->m_wndEdit.m_hWnd);
				break;
			case CBN_SELCHANGE:
			{
				int i = (int) (::SendMessage(pMain->m_hWnd, CB_GETCURSEL, 0, 0));
				//m_bNeedRestore = (m_nCurSel != i);
				if (i != CB_ERR)
				{
					CAddressComboBoxItemData* pItem = (CAddressComboBoxItemData*)
						::SendMessage(pMain->m_hWnd, CB_GETITEMDATA, (WPARAM) IntToPtr(i), 0);
					if (pItem)
					{
						FillData(pItem);
						pMain->m_wndEdit.SetWindowTextW(pItem->strDisplayName);
						::SendMessage(pMain->m_wndEdit, EM_SETSEL, (WPARAM) 0, (LPARAM) 0);
						::SendMessage(pMain->m_wndEdit, EM_SETSEL, (WPARAM) 0, (LPARAM) -1);
					}
				}
			}
			lr = ::SendMessage(::GetParent(m_hWnd), WM_COMMAND, wParam, (LPARAM) m_hWnd);
			break;
			case CBN_SELENDOK:
				m_nCurSel = (int) (::SendMessage(pMain->m_hWnd, CB_GETCURSEL, 0, 0));
				m_bUseDisplayName = false;
				return ::SendMessage(::GetParent(m_hWnd), WM_COMMAND, wParam, (LPARAM) m_hWnd);
			case CBN_SELENDCANCEL:
				m_bUseDisplayName = false;
				::SendMessage(m_hWnd, CB_SETCURSEL, (WPARAM) IntToPtr(m_nCurSel), 0);
				return ::SendMessage(::GetParent(m_hWnd), WM_COMMAND, wParam, (LPARAM) m_hWnd);
			case CBN_SETFOCUS:
				if (!pMain->m_bIgnoreSetFocus && pMain->m_wndEdit.m_hWnd)
					::SetFocus(pMain->m_wndEdit.m_hWnd);
				return ::SendMessage(::GetParent(m_hWnd), WM_COMMAND, wParam, (LPARAM) m_hWnd);
			case CBN_KILLFOCUS:
				lr = ::SendMessage(::GetParent(m_hWnd), WM_COMMAND, wParam, (LPARAM) m_hWnd);
				break;
			default:
				lr = ::SendMessage(::GetParent(m_hWnd), WM_COMMAND, wParam, (LPARAM) m_hWnd);
				if (pMain->m_wndEdit.m_hWnd)
					::SetFocus(pMain->m_wndEdit.m_hWnd);
				break;
		}
		//wParam = MAKEWPARAM(::GetDlgCtrlID(m_hWnd), HIWORD(wParam));
		return lr;
	}
	return Default(wParam, lParam);
}

LRESULT CAddressComboBox::OnEnable(WPARAM wParam, LPARAM lParam)
{
	::EnableWindow(m_pWndComboBox->m_hWnd, wParam != 0);
	return Default(wParam, lParam);
}

LRESULT CAddressComboBox::OnSetFont(WPARAM wParam, LPARAM lParam)
{
	::SendMessage(m_pWndComboBox->m_hWnd, WM_SETFONT, wParam, lParam);
	return Default(wParam, lParam);
}

LRESULT CAddressComboBox::OnSetFocus(WPARAM wParam, LPARAM lParam)
{
	::SetFocus(m_pWndComboBox->m_hWnd);
	return 0;
}

LRESULT CAddressComboBox::OnSize(WPARAM wParam, LPARAM lParam)
{
	RECT rc;
	::GetClientRect(m_hWnd, &rc);
	::MoveWindow(m_pWndComboBox->m_hWnd, 0, 0, rc.right, rc.bottom, TRUE);
	return 0;
}

LRESULT CAddressComboBox::OnSetCurSel(WPARAM wParam, LPARAM lParam)
{
	register CAddressComboBoxMain* pMain = (CAddressComboBoxMain*) m_pWndComboBox;
	LRESULT lr = ::SendMessage(pMain->m_hWnd, CB_SETCURSEL, wParam, lParam);

	m_nCurSel = (int) (wParam);

	if (!m_bUseDisplayName)
		UpdateRealPath(m_strRealPath);
	else
	{
		CAddressComboBoxItemData* pItem = (CAddressComboBoxItemData*) ::SendMessage(pMain->m_hWnd, CB_GETITEMDATA, wParam, 0);
		FillData(pItem);
		pMain->m_wndEdit.SetWindowTextW(pItem->strDisplayName);
	}

	return lr;
}

LRESULT CAddressComboBox::OnMeasureItem(WPARAM wParam, LPARAM lParam)
{
	LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;

	HFONT hFont;
	HDC hDC;
	HGDIOBJ hgdi;
	TEXTMETRIC tm;
	int cy;

	//hFont = (HFONT) ::SendMessage(m_hWnd, WM_GETFONT, 0, 0);
	hFont = theApp.m_hFontWindow;
	hDC = ::GetDC(m_hWnd);
	hgdi = ::SelectObject(hDC, hFont);
	::GetTextMetrics(hDC, &tm);
	::SelectObject(hDC, hgdi);
	::ReleaseDC(m_hWnd, hDC);

	cy = tm.tmHeight + COMBO_PADDING_Y * 2;
	if (cy < 16 + COMBO_PADDING_Y * 2)
		cy = 16 + COMBO_PADDING_Y * 2;
	lpmis->itemWidth = 0;
	lpmis->itemHeight = cy;
	return 0;
}

void CAddressComboBox::FillData(void* _pData) const
{
	register CAddressComboBoxItemData* pData = (CAddressComboBoxItemData*) _pData;

	if (pData->iIcon == -1)
	{
		UINT uFlags;
		SHFILEINFO_UNION sfi;
		_StringW str;

		uFlags = SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_DISPLAYNAME | SHGFI_PIDL;
		if (pData->bCurrent)
			uFlags |= SHGFI_OPENICON;
		sfi.w.szDisplayName[0] = 0;
		if (!::SHGetFileInfoW((LPCWSTR) pData->lpidl, 0, &sfi.w, sizeof(sfi.w), uFlags))
		{
			sfi.a.szDisplayName[0] = 0;
			::SHGetFileInfoA((LPCSTR) pData->lpidl, 0, &sfi.a, sizeof(sfi.a), uFlags);
			str = sfi.a.szDisplayName;
			pData->iIcon = sfi.a.iIcon;
			//uFlags |= SHGFI_SELECTED;
			//uFlags &= ~SHGFI_DISPLAYNAME;
			//::SHGetFileInfoA((LPCSTR) pData->lpidl, 0, &sfi.a, sizeof(sfi.a), uFlags);
			//pData->iIconSel = sfi.a.iIcon;
		}
		else
		{
			pData->iIcon = sfi.w.iIcon;
			str = sfi.w.szDisplayName;
			//uFlags |= SHGFI_SELECTED;
			//uFlags &= ~SHGFI_DISPLAYNAME;
			//::SHGetFileInfoW((LPCWSTR) pData->lpidl, 0, &sfi.w, sizeof(sfi.w), uFlags);
			//pData->iIconSel = sfi.w.iIcon;
		}
		pData->strDisplayName = str;
	}
}

void CAddressComboBox::DeleteData(void* _pData) const
{
	register CAddressComboBoxItemData* pData = (CAddressComboBoxItemData*) _pData;
	//if (pData->pFolder)
	//	pData->pFolder->Release();
	//::DestroyIcon(pData->hIcon);
	//::DestroyIcon(pData->hIconSel);
	if (pData->lpidl)
		::CoTaskMemFree(pData->lpidl);
	delete pData;
}

LRESULT CAddressComboBox::OnDrawItem(WPARAM wParam, LPARAM lParam)
{
	LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) lParam;

	CAddressComboBoxItemData* pData;

	if (lpdis->itemID == -1)
		pData = NULL;
	else
		pData = (CAddressComboBoxItemData*) lpdis->itemData;
	RECT rc;
	TEXTMETRIC tm;
	SIZE sz;
	COLORREF cr;
	int nMode;

	::FillRect(lpdis->hDC, &lpdis->rcItem, ::GetSysColorBrush(COLOR_WINDOW));

	if (!pData)
	{
		if ((lpdis->itemState & ODS_SELECTED) && !(lpdis->itemState & ODS_COMBOBOXEDIT))
			::DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
	}
	else
	{
		::GetTextMetrics(lpdis->hDC, &tm);
		FillData(pData);

		memcpy(&rc, &lpdis->rcItem, sizeof(rc));

		if (!(lpdis->itemState & ODS_COMBOBOXEDIT))
			rc.left += pData->iLevel * 10;
		::ImageList_Draw(m_himlSystemSmall, pData->iIcon, lpdis->hDC, rc.left, rc.top,
			(lpdis->itemState & ODS_SELECTED) ? ILD_SELECTED | ILD_TRANSPARENT : ILD_TRANSPARENT);
		//::DrawIconEx(lpdis->hDC, rc.left, rc.top, sfi.w.hIcon, 0, 0, 0, NULL, DI_NORMAL);
		rc.left += 18;

		//if (!(lpdis->itemState & ODS_COMBOBOXEDIT))
		{
			_StringW str;
			nMode = ::SetBkMode(lpdis->hDC, TRANSPARENT);
			str = pData->strDisplayName;
			::GetTextExtentPoint32W(lpdis->hDC, str, (int) str.GetLength(), &sz);
			rc.right = rc.left + sz.cx + 4;
			if ((lpdis->itemState & ODS_SELECTED) && !(lpdis->itemState & ODS_COMBOBOXEDIT))
				::DrawFocusRect(lpdis->hDC, &rc);
			rc.left++;
			rc.right--;
			rc.top++;
			rc.bottom--;
			if ((lpdis->itemState & ODS_SELECTED) && !(lpdis->itemState & ODS_COMBOBOXEDIT))
			//if (lpdis->itemState & ODS_SELECTED)
			{
				::FillRect(lpdis->hDC, &rc, ::GetSysColorBrush(COLOR_HIGHLIGHT));
				cr = ::SetTextColor(lpdis->hDC, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
			}
			else
				cr = ::SetTextColor(lpdis->hDC, ::GetSysColor(COLOR_WINDOWTEXT));

			//rc.left++;
			rc.left += 2;
			rc.top += ((rc.bottom - rc.top) - tm.tmHeight) / 2;
			::TextOutW(lpdis->hDC, rc.left, rc.top, str, (int) str.GetLength());

			::SetTextColor(lpdis->hDC, cr);
			::SetBkMode(lpdis->hDC, nMode);
		}
	}

	return 0;
}

LRESULT CAddressComboBox::OnDeleteItem(WPARAM wParam, LPARAM lParam)
{
	LPDELETEITEMSTRUCT lpdis = (LPDELETEITEMSTRUCT) lParam;
	CAddressComboBoxItemData* pData = (CAddressComboBoxItemData*) lpdis->itemData;
	if (pData)
		DeleteData(pData);
	return 0;
}

////////////////////////////////////////////////////////////////////////////////

LRESULT CAddressComboBoxMain::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	HANDLE_PROC_MESSAGE(WM_CREATE, OnCreate);
	HANDLE_PROC_MESSAGE(WM_COMMAND, OnCommand);
	HANDLE_PROC_MESSAGE(WM_ENABLE, OnEnable);
	HANDLE_PROC_MESSAGE(WM_SIZE, OnSize);
	HANDLE_PROC_MESSAGE(WM_SETFONT, OnSetFont);
	//HANDLE_PROC_MESSAGE(WM_KEYDOWN, OnKeyDown);
	HANDLE_PROC_MESSAGE(WM_MOUSEWHEEL, OnMouseWheel);

	return CMyWindow::WindowProc(message, wParam, lParam);
}

LRESULT CAddressComboBoxMain::OnCreate(WPARAM wParam, LPARAM lParam)
{
	if (!m_wndEdit.CreateExW(0, L"Edit", NULL,
		WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL | ES_LEFT,
		0, 0, 0, 0, m_hWnd, NULL, NULL, NULL))
		return 1;

	HDC hDC;
	HGDIOBJ hgdi;

	hDC = ::GetDC(m_hWnd);
	hgdi = ::SelectObject(hDC, (HGDIOBJ)(HFONT) ::SendMessage(m_hWnd, WM_GETFONT, 0, 0));
	::GetTextMetrics(hDC, &m_tm);
	::SelectObject(hDC, hgdi);
	::ReleaseDC(m_hWnd, hDC);

	return Default(wParam, lParam);
}

LRESULT CAddressComboBoxMain::OnSize(WPARAM wParam, LPARAM lParam)
{
	Default(wParam, lParam);

	RECT rc;
	int y, cy;
	int cxButton;

	::GetClientRect(m_hWnd, &rc);
	cxButton = ::GetSystemMetrics(SM_CXVSCROLL);
	cy = rc.bottom - 2 - COMBO_PADDING_Y * 2;
	if (cy < m_tm.tmHeight)
		y = COMBO_PADDING_Y + 1;
	else
		y = (cy - m_tm.tmHeight) / 2 + COMBO_PADDING_Y + 1;
	::MoveWindow(m_wndEdit, 2 + 2 + 1 + 18, y,
		rc.right - cxButton - 2 - 2 - 1 - 18, m_tm.tmHeight, TRUE);

	return 0;
}

LRESULT CAddressComboBoxMain::OnCommand(WPARAM wParam, LPARAM lParam)
{
	switch (HIWORD(wParam))
	{
		case EN_SETFOCUS:
			//::SendMessage(m_hWnd, WM_SETFOCUS, 0, 0);
			::SendMessage(m_wndEdit, EM_SETSEL, (WPARAM) 0, (LPARAM) 0);
			::SendMessage(m_wndEdit, EM_SETSEL, (WPARAM) 0, (LPARAM) -1);
			break;
		case EN_KILLFOCUS:
		{
			HWND h = ::GetFocus();
			::InvalidateRect(m_hWnd, NULL, TRUE);
			if (h && h != m_hWnd)
				::SendMessage(m_hWnd, WM_KILLFOCUS, (WPARAM) h, 0);
		}
		break;
		//case LBN_SELCHANGE:
		//	break;
		default:
			Default(wParam, lParam);
	}

	return 0;
}

LRESULT CAddressComboBoxMain::OnEnable(WPARAM wParam, LPARAM lParam)
{
	::EnableWindow(m_wndEdit, wParam != 0);
	return Default(wParam, lParam);
}

LRESULT CAddressComboBoxMain::OnSetFont(WPARAM wParam, LPARAM lParam)
{
	Default(wParam, lParam);

	::SendMessage(m_wndEdit, WM_SETFONT, wParam, lParam);

	RECT rc;
	HDC hDC;
	HGDIOBJ hgdi;

	hDC = ::GetDC(m_hWnd);
	hgdi = ::SelectObject(hDC, (HGDIOBJ)(HFONT) wParam);
	::GetTextMetrics(hDC, &m_tm);
	::SelectObject(hDC, hgdi);
	::ReleaseDC(m_hWnd, hDC);
	::GetClientRect(m_hWnd, &rc);

	::SendMessage(m_hWnd, WM_SIZE, 0, MAKELPARAM(rc.right, rc.bottom));

	return 0;
}

//LRESULT CAddressComboBoxMain::OnKeyDown(WPARAM wParam, LPARAM lParam)
//{
//	if ((UINT) wParam == VK_ESCAPE && m_wndComboLBox.m_hWnd && ::IsWindowVisible(m_wndComboLBox))
//	{
//		::ShowWindow(m_wndComboLBox, SW_HIDE);
//		::SendMessage(m_hWnd, CB_SETCURSEL, (WPARAM) m_nCurSel, 0);
//		return 0;
//	}
//
//	if (!::SendMessage(m_hWnd, CB_GETDROPPEDSTATE, 0, 0))
//	{
//		if (wParam == VK_DOWN)
//			return ::SendMessage(m_hWnd, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0);
//		else if (wParam == VK_UP)
//			return 0;
//	}
//	return Default(wParam, lParam);
//}

LRESULT CAddressComboBoxMain::OnMouseWheel(WPARAM wParam, LPARAM lParam)
{
	if (::SendMessage(m_hWnd, CB_GETDROPPEDSTATE, 0, 0))
		return Default(wParam, lParam);
	else
		// ignore
		return 0;
}

////////////////////////////////////////////////////////////////////////////////

LRESULT CAddressComboEditBox::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	HANDLE_PROC_MESSAGE(WM_LBUTTONDOWN, OnLButtonDown);
	HANDLE_PROC_MESSAGE(WM_KEYDOWN, OnKeyDown);
	HANDLE_PROC_MESSAGE(WM_CHAR, OnChar);
	return CMyWindow::WindowProc(message, wParam, lParam);
}

LRESULT CAddressComboEditBox::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
	if (::GetFocus() != m_hWnd)
		::SetFocus(m_hWnd);
	else
		Default(wParam, lParam);

	return 0;
}

LRESULT CAddressComboEditBox::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
	if (wParam == VK_RETURN)
	{
		m_pParent->m_pParent->SendNotify(ACBN_TEXTRETURN);
		return 0;
	}
	else if (wParam == VK_ESCAPE)
	{
		m_pParent->m_pParent->RestoreTextBox();
		return 0;
	}

	return Default(wParam, lParam);
}

LRESULT CAddressComboEditBox::OnChar(WPARAM wParam, LPARAM lParam)
{
	if (wParam == VK_RETURN)
	{
		//m_pParent->m_pParent->SendNotify(ACBN_TEXTRETURN);
		return 0;
	}

	return Default(wParam, lParam);
}

////////////////////////////////////////////////////////////////////////////////

CVirtualAddressComboBox::CVirtualAddressComboBox()
{
	m_wchDelimiter = L'/';
}

void CVirtualAddressComboBox::ChangeCurrentFolder(LPCWSTR lpszDirectoryAbsolute)
{
	int nCount = (int) (::SendMessage(m_hWnd, CB_GETCOUNT, 0, 0));

	CAddressComboBoxItemData* pData, * pData2, * pChild, * pTop, * pCur;
	LPWSTR lpw, lpwPos;
	int level;

	lpw = _wcsdup(lpszDirectoryAbsolute);
	lpwPos = wcsrchr(lpw, m_wchDelimiter);
	pData2 = NULL;
	pChild = NULL;
	level = 0;
	while (true)
	{
		pData = new CAddressComboBoxItemData();
		if (pData2)
		{
			pData2->pParent = pData;
			pData->pChild = pData2;
		}
		else
			pData->pChild = NULL;
		if (pData->bCurrent = !pChild)
			pChild = pData;
		pData->lpidl = (PIDLIST_ABSOLUTE) _wcsdup(lpw);
		if (!lpwPos)
			pData->strDisplayName = lpw;
		else if (lpwPos == lpw && !lpw[1])
			pData->strDisplayName.LoadString(IDS_ROOTDIR);
		else
			pData->strDisplayName = lpwPos + 1;
		pData->pParent = NULL;
		pData->pNext = NULL;
		pData2 = pData;

		if (lpwPos)
		{
			if (lpwPos == lpw && lpw[1])
				lpwPos[1] = 0;
			else
				*lpwPos-- = 0;
			if (lpwPos >= lpw)
				lpwPos = wcsrchr(lpw, m_wchDelimiter);
			else
				break;
		}
		else
			break;
	}
	free(lpw);
	pCur = pChild;
	pData = pTop = pData2;
	level = 0;
	while (pData)
	{
		pData->iLevel = level++;
		pData = pData->pChild;
	}

	AddChildrenToComboBox(m_hWnd, m_pWndComboBox->m_hWnd, pTop, pCur);

	while (nCount--)
		::SendMessage(m_hWnd, CB_DELETESTRING, (WPARAM) IntToPtr(nCount), 0);

	m_strDirectory = m_strRealPath = lpszDirectoryAbsolute;
	UpdateRealPath(lpszDirectoryAbsolute);
}

LPCWSTR CVirtualAddressComboBox::GetSelectedFolder() const
{
	int i;
	i = (int) (::SendMessage(m_hWnd, CB_GETCURSEL, 0, 0));
	if (i == CB_ERR)
		return NULL;
	CAddressComboBoxItemData* pData = (CAddressComboBoxItemData*)
		::SendMessage(m_hWnd, CB_GETITEMDATA, (WPARAM) IntToPtr(i), 0);
	return pData ? (LPCWSTR) pData->lpidl : NULL;
}

void CVirtualAddressComboBox::Empty()
{
	::SendMessage(m_hWnd, CB_RESETCONTENT, 0, 0);
}

static void __stdcall GetFirstLocalDrive(_StringW& rstrDrive)
{
	DWORD dw = ::GetLogicalDrives();
	int i = 0;
	UINT u;
	char szPath[4], chRet;
	chRet = 0;
	szPath[1] = ':';
	szPath[2] = '\\';
	szPath[3] = 0;
	while (i < 26)
	{
		if (dw & (1 << i))
		{
			szPath[0] = 'A' + i;
			u = ::GetDriveTypeA(szPath);
			//if (u == DRIVE_REMOTE)
			//{
			//	chRet = szPath[0];
			//	break;
			//}
			//else if ((u == DRIVE_FIXED) && !chRet)
			//	chRet = szPath[0];
			if (u == DRIVE_FIXED)
			{
				chRet = szPath[0];
				break;
			}
		}
		i++;
	}
	if (!chRet)
		chRet = 'C';
	szPath[0] = chRet;
	rstrDrive = szPath;
}

void CVirtualAddressComboBox::FillData(void* _pData) const
{
	CAddressComboBoxItemData* pData = (CAddressComboBoxItemData*) _pData;

	if (pData->iIcon == -1)
	{
		_StringW str(pData->strDisplayName);
		UINT uFlags;
		SHFILEINFO_UNION sfi;
		DWORD dwAttr = FILE_ATTRIBUTE_DIRECTORY;

		if (pData->pParent == NULL)
		{
			GetFirstLocalDrive(str);
			//str = L"P:\\";
			//dwAttr = FILE_ATTRIBUTE_VIRTUAL;
		}
		uFlags = SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES;
		if (pData->bCurrent)
			uFlags |= SHGFI_OPENICON;
		if (!::SHGetFileInfoW(str, dwAttr, &sfi.w, sizeof(sfi.w), uFlags))
		{
			::SHGetFileInfoA(str, dwAttr, &sfi.a, sizeof(sfi.a), uFlags);
			pData->iIcon = sfi.a.iIcon;
		}
		else
			pData->iIcon = sfi.w.iIcon;
	}
}

void CVirtualAddressComboBox::DeleteData(void* _pData) const
{
	CAddressComboBoxItemData* pData = (CAddressComboBoxItemData*) _pData;

	if (pData->lpidl)
		free((LPWSTR) pData->lpidl);
	delete pData;
}
