#include "Stdafx.h"
#include "DirectoryBrowseDialog.h"

using namespace System::Runtime::InteropServices;

namespace Mix{ namespace Tool{

////////////////////////////////////////////////////////////////////////////////////////////////////
// コールバック
////////////////////////////////////////////////////////////////////////////////////////////////////

int CALLBACK DirectoryBrowseProc( HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData )
{
	if( ( uMsg == BFFM_INITIALIZED ) &&
		( lpData != NULL ) )
	{
		HWND hParentWnd;
		RECT parentRect;
		RECT rect;
		POINT pos;

		hParentWnd = ::GetParent( hWnd );

		::GetWindowRect( hParentWnd, &parentRect );
		::GetWindowRect( hWnd, &rect );

		pos.x = parentRect.left + ( ( parentRect.right - parentRect.left ) - ( rect.right - rect.left ) ) / 2;
		pos.y = parentRect.top + ( ( parentRect.bottom - parentRect.top ) - ( rect.bottom - rect.top ) ) / 2;

		::SetWindowPos( hWnd, NULL, pos.x, pos.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE );

		::SendMessage( hWnd, BFFM_SETSELECTION, static_cast<WPARAM>( TRUE ), lpData );
	}
	else if( uMsg == BFFM_SELCHANGED )
	{
		BOOL bValidPath = TRUE;
		TCHAR szPath[_MAX_PATH];
		::ZeroMemory( szPath, sizeof( szPath ) );
		if( ::SHGetPathFromIDList( reinterpret_cast<LPCITEMIDLIST>( lParam ), szPath ) == TRUE )
		{
			::SendMessage( hWnd, BFFM_SETSTATUSTEXT, 0, reinterpret_cast<LPARAM>( szPath ) );

			//ディスクの空き容量を見る
			ULARGE_INTEGER ui64FreeBytesToCaller;
			ULARGE_INTEGER ui64TotalBytes;
			ULARGE_INTEGER ui64FreeBytes;
			if( ::GetDiskFreeSpaceEx( szPath, &ui64FreeBytesToCaller, &ui64TotalBytes, &ui64FreeBytes ) == TRUE )
			{
				if( ui64FreeBytesToCaller.QuadPart != 0 )
				{
					bValidPath = TRUE;
				}
				else
				{
					bValidPath = FALSE;
				}
			}
			else
			{
				bValidPath = FALSE;
			}
		}
		else
		{
			bValidPath = FALSE;
		}

		//OKボタン
		HWND hOkButton = ::GetDlgItem( hWnd, IDOK );
		if( hOkButton != NULL )
		{
			::EnableWindow( hOkButton, bValidPath );
		}
	}

	return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// DirectoryBrowseDialog
////////////////////////////////////////////////////////////////////////////////////////////////////

System::String^ DirectoryBrowseDialog::Title::get( void )
{
	return static_cast<System::String^>( m_Title->Clone() );
}

void DirectoryBrowseDialog::Title::set( System::String^ value )
{
	m_Title = ( value != nullptr )? static_cast<System::String^>( value->Clone() ) : L"";
}

DirectoryBrowseDialog::RootType DirectoryBrowseDialog::Root::get( void )
{
	return m_RootType;
}

void DirectoryBrowseDialog::Root::set( RootType value )
{
	m_RootType = value;
}

System::String^ DirectoryBrowseDialog::RootDirectoryName::get( void )
{
	return m_RootDirectoryName;
}

void DirectoryBrowseDialog::RootDirectoryName::set( System::String^ value )
{
	if( m_RootType == RootType::Specify )
	{
		m_RootDirectoryName = ( value != nullptr )? static_cast<System::String^>( value->Clone() ) : L"";
	}
	else
	{
		m_RootDirectoryName = L"";
	}
}

System::String^ DirectoryBrowseDialog::DirectoryName::get( void )
{
	return static_cast<System::String^>( m_DirectoryName->Clone() );
}

void DirectoryBrowseDialog::DirectoryName::set( System::String^ value )
{
	m_DirectoryName = ( value != nullptr )? static_cast<System::String^>( value->Clone() ) : L"";
}

System::String^ DirectoryBrowseDialog::ShortDirectoryName::get( void )
{
	return static_cast<System::String^>( m_ShortDirectoryName->Clone() );
}

void DirectoryBrowseDialog::ShortDirectoryName::set( System::String^ value )
{
	m_ShortDirectoryName = ( value != nullptr )? static_cast<System::String^>( value->Clone() ) : L"";
}

bool DirectoryBrowseDialog::EditBox::get( void )
{
	return m_bEditBox;
}

void DirectoryBrowseDialog::EditBox::set( bool value )
{
	m_bEditBox = value;
}

DirectoryBrowseDialog::DirectoryBrowseDialog( void ) :
m_Title( L"" ),
m_RootType( RootType::Desktop ),
m_RootDirectoryName( L"" ),
m_DirectoryName( L"" ),
m_ShortDirectoryName( L"" ),
m_bEditBox( false )
{
}

DirectoryBrowseDialog::~DirectoryBrowseDialog( void )
{
	delete m_RootDirectoryName;
	delete m_DirectoryName;
	delete m_Title;
}

System::Windows::Forms::DialogResult DirectoryBrowseDialog::ShowDialog( void )
{
	System::Windows::Forms::DialogResult result;

	INT_PTR retPtr = -1;
	LPMALLOC lpMemory = NULL;
	LPITEMIDLIST lpidlRoot = NULL;
	LPITEMIDLIST lpidlBrowse = NULL;
	wchar_t* pRootDirectoryName = NULL;
	wchar_t* pDirectoryName = NULL;
	wchar_t* pTitle = NULL;
	HWND hOwnerWnd = ::GetActiveWindow();
	BROWSEINFO bi;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 初期化
	////////////////////////////////////////////////////////////////////////////////////////////////////

	::ZeroMemory( &bi, sizeof( bi ) );

	if( ::SHGetMalloc( &lpMemory ) != S_OK )
	{
		throw gcnew System::Exception();
	}

	pTitle = reinterpret_cast<wchar_t*>( lpMemory->Alloc( MAX_PATH * 2 ) );
	if( pTitle != NULL )
	{
		IntPtr ptr = Marshal::StringToHGlobalUni( m_Title );
		::wcscpy_s( pTitle, MAX_PATH, static_cast<const wchar_t*>( ptr.ToPointer() ) );
		Marshal::FreeHGlobal( ptr );
	}
	else
	{
		lpMemory->Release();
		throw gcnew System::Exception();
	}

	pDirectoryName = reinterpret_cast<wchar_t*>( lpMemory->Alloc( MAX_PATH * 2 ) );
	if( pDirectoryName != NULL )
	{
		IntPtr ptr = Marshal::StringToHGlobalUni( m_DirectoryName );
		::wcscpy_s( pDirectoryName, MAX_PATH, static_cast<const wchar_t*>( ptr.ToPointer() ) );
		Marshal::FreeHGlobal( ptr );
	}
	else
	{
		lpMemory->Free( pTitle );
		lpMemory->Release();
		throw gcnew System::Exception();
	}

	pRootDirectoryName = reinterpret_cast<wchar_t*>( lpMemory->Alloc( MAX_PATH * 2 ) );
	if( pRootDirectoryName != NULL )
	{
		IntPtr ptr = Marshal::StringToHGlobalUni( m_RootDirectoryName );
		::wcscpy_s( pRootDirectoryName, MAX_PATH, static_cast<const wchar_t*>( ptr.ToPointer() ) );
		Marshal::FreeHGlobal( ptr );
	}
	else
	{
		lpMemory->Free( pDirectoryName );
		lpMemory->Free( pTitle );
		lpMemory->Release();
		throw gcnew System::Exception();
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ルートを決定
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_RootType == DirectoryBrowseDialog::RootType::Specify )
	{
		LPSHELLFOLDER lpShellDir = NULL;
		ULONG eatan = 0;

		if( ::SHGetDesktopFolder( &lpShellDir ) == S_OK )
		{
			if( lpShellDir->ParseDisplayName( NULL, NULL, pRootDirectoryName, &eatan, &lpidlRoot, NULL ) != S_OK )
			{
				if( ::SHGetSpecialFolderLocation( hOwnerWnd, CSIDL_DESKTOP, &lpidlRoot ) != S_OK )
				{
					lpMemory->Free( pRootDirectoryName );
					lpMemory->Free( pDirectoryName );
					lpMemory->Free( pTitle );
					lpMemory->Release();
					throw gcnew System::Exception();
				}
			}
		}
		else
		{
			if( ::SHGetSpecialFolderLocation( hOwnerWnd, CSIDL_DESKTOP, &lpidlRoot ) != S_OK )
			{
				lpMemory->Free( pRootDirectoryName );
				lpMemory->Free( pDirectoryName );
				lpMemory->Free( pTitle );
				lpMemory->Release();
				throw gcnew System::Exception();
			}
		}
	}
	else
	{
		int csidl;

		switch( m_RootType )
		{
		case RootType::Desktop:
			csidl = CSIDL_DESKTOP;
			break;
		case RootType::Programs:
			csidl = CSIDL_PROGRAMS;
			break;
		case RootType::ControlPanel:
			csidl = CSIDL_CONTROLS;
			break;
		case RootType::Printers:
			csidl = CSIDL_PRINTERS;
			break;
		case RootType::MyDocument:
			csidl = CSIDL_PERSONAL;
			break;
		case RootType::Favorite:
			csidl = CSIDL_FAVORITES;
			break;
		case RootType::Startup:
			csidl = CSIDL_STARTUP;
			break;
		case RootType::Recent:
			csidl = CSIDL_RECENT;
			break;
		case RootType::SendTo:
			csidl = CSIDL_SENDTO;
			break;
		case RootType::Trash:
			csidl = CSIDL_BITBUCKET;
			break;
		case RootType::StartMenu:
			csidl = CSIDL_STARTMENU;
			break;
		case RootType::MyComputer:
			csidl = CSIDL_DRIVES;
			break;
		case RootType::Network:
			csidl = CSIDL_NETWORK;
			break;
		}

		if( ::SHGetSpecialFolderLocation( hOwnerWnd, csidl, &lpidlRoot ) != S_OK )
		{
			lpMemory->Free( pRootDirectoryName );
			lpMemory->Free( pDirectoryName );
			lpMemory->Free( pTitle );
			lpMemory->Release();
			throw gcnew System::Exception();
		}

		::SHGetPathFromIDList( lpidlRoot, pRootDirectoryName );
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ダイアログを表示
	////////////////////////////////////////////////////////////////////////////////////////////////////

	bi.hwndOwner = hOwnerWnd;
	bi.pidlRoot = lpidlRoot;
	bi.pszDisplayName = pDirectoryName;
	bi.lpszTitle = pTitle;
	bi.ulFlags = ( m_bEditBox == true )? ( BIF_EDITBOX | BIF_NEWDIALOGSTYLE ) : BIF_NEWDIALOGSTYLE;
	bi.lpfn = DirectoryBrowseProc;
	bi.lParam = reinterpret_cast<LPARAM>( pDirectoryName );

	lpidlBrowse = ::SHBrowseForFolder( &bi );
	if( lpidlBrowse != NULL )
	{
		if( ::SHGetPathFromIDList( lpidlBrowse, pDirectoryName ) == TRUE )
		{
			int rootLength;
			int subStart;
			int subLength;
			wchar_t lastChar;

			m_RootDirectoryName = gcnew System::String( pRootDirectoryName );
			m_DirectoryName = gcnew System::String( pDirectoryName );

			rootLength = m_RootDirectoryName->Length;
			if( rootLength > 0 )
			{
				lastChar = m_RootDirectoryName[rootLength - 1];
				if( ( lastChar != '\\' ) &&
					( lastChar != '/' ) )
				{
					subStart = 1;
				}
				else
				{
					subStart = 0;
				}

				subStart += rootLength;
				subLength = m_DirectoryName->Length - subStart;

				m_ShortDirectoryName = m_DirectoryName->Substring( subStart, subLength );
			}
			else
			{
				m_ShortDirectoryName = static_cast<System::String^>( m_DirectoryName->Clone() );
			}

			result = System::Windows::Forms::DialogResult::OK;
		}

		lpMemory->Free( lpidlBrowse );
	}
	else
	{
		result = System::Windows::Forms::DialogResult::Cancel;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 解放
	////////////////////////////////////////////////////////////////////////////////////////////////////

	lpMemory->Free( lpidlRoot );
	lpMemory->Free( pRootDirectoryName );
	lpMemory->Free( pDirectoryName );
	lpMemory->Free( pTitle );
	lpMemory->Release();

	return result;
}

}}
