///////////////////////////////////
//User.h−ユーザー定義変数、関数等
///////////////////////////////////

////////////
//定数の定義
////////////
#define UP 0
#define DOWN 1
#define NO_OF_SUBITEM 4

////////////////
//外部変数の宣言
////////////////
//------------------------------
//スプリッターインスタンスの生成
//------------------------------
#define	IDC_SPLIT	5000
CSPLIT g_Split(100);		//2021年11月21日修正

//------------------------
//シェルインスタンスの生成
//------------------------
CSHELL g_Shell(FileHandler.GetHandle());
CSTR g_SelectedFile = "";		//選択されたファイルのパス＋ファイル名

//------------------------------
//ツリービュ―インスタンスの生成
//------------------------------
#define	IDC_TREEVIEW	6000
CTREEVIEW g_tv(16, 16, 16, ILC_COLOR4 | ILC_MASK, 0);	//ビットマップサイズと個数（2021年11月21日修正）
LONG g_Style;					//Window Style用変数
//ツリービュ―用
HTREEITEM g_hRoot = 0;
HTREEITEM g_hSelected = 0;
CSTR g_Root = "デスクトップ";	//ルートのドライブ、フォールダーの初期化
char g_CurrentPath[MAX_PATH];	//ツリービュ―で選択されたフォールダーへのパス

//------------------------------
//リストビューインスタンスの生成
//------------------------------
#define	IDC_LISTVIEW	7000
CLISTVIEW g_lv;
//リストビューで選択されるファイル名
char g_FileName[MAX_PATH];
//リストビューの選択行（LVNI_SELECTED | LVNI_FOCUSED−複数選択の場合もあるので注意）
int g_SelRow = -1;

//------------------------------
//ポップアップメニュ−のハンドル
//------------------------------
HMENU g_hPopup;			//メニューハンドル
bool g_Context = FALSE;	//コンテキストメニュー切り替えフラグ

//////////////////////////
//サブアイテムのソート関連
//////////////////////////
//CSHELLから呼ばれるソートの為の比較関数
//仮想フォルダ(virtualAttributes)>通常のフォルダ(folderAttributes)>ファイル(fileAttributes)）
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) {

	IShellFolder     *pShellFolder = (IShellFolder*)lParamSort;
	PIDLIST_ABSOLUTE pidlAbsolute1 = (PIDLIST_ABSOLUTE)lParam1;
	PIDLIST_ABSOLUTE pidlAbsolute2 = (PIDLIST_ABSOLUTE)lParam2;
	PITEMID_CHILD    pidlChild1 = ILFindLastID(pidlAbsolute1);
	PITEMID_CHILD    pidlChild2 = ILFindLastID(pidlAbsolute2);
	SFGAOF           fileAttributes = SFGAO_FILESYSTEM;
	SFGAOF           folderAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM;
	SFGAOF           virtualAttributes = SFGAO_FOLDER;
	SFGAOF           attributes1 = SFGAO_FILESYSTEM | SFGAO_FOLDER;
	SFGAOF           attributes2 = SFGAO_FILESYSTEM | SFGAO_FOLDER;
	int              nResult = -1;
	//属性の取得と設定
	pShellFolder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlChild1, &attributes1);
	pShellFolder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlChild2, &attributes2);
	attributes1 &= SFGAO_FILESYSTEM | SFGAO_FOLDER;
	attributes2 &= SFGAO_FILESYSTEM | SFGAO_FOLDER;
	//比較処理
	if(attributes1 == attributes2)
		return pShellFolder->CompareIDs(0, pidlChild1, pidlChild2) * -1;
	if(attributes1 == virtualAttributes)
		nResult = -1;
	else if(attributes1 == folderAttributes) {
		if(attributes2 == virtualAttributes)
			nResult = 1;
		else
			nResult = -1;
	}
	else if(attributes1 == fileAttributes)
		nResult = 1;
	else
		nResult = -1;
	return nResult;
}

//リストビューから呼ばれるソートの為の比較関数
int sortsubno[NO_OF_SUBITEM];	//列とソート方法の配列
//ソートコールバック関数
int CALLBACK CompProc(LPARAM lp1, LPARAM lp2, LPARAM lpSort) {

	static int nItem1, nItem2;
	static char buf1[MAX_PATH], buf2[MAX_PATH];
	//アイテムの検索
	nItem1 = g_lv.FindItem(-1, NULL, lp1);
	nItem2 = g_lv.FindItem(-1, NULL, lp2);
	//アイテム情報の取得
	g_lv.GetItemText(nItem1, (int)lpSort, buf1, MAX_PATH);
	g_lv.GetItemText(nItem2, (int)lpSort, buf2, MAX_PATH);
	if(sortsubno[(int)lpSort] == UP)
		return(strcmp(buf1, buf2));
	else
		return(strcmp(buf1, buf2) * -1);
}

////////////////////////////
//コンテキストメニューの表示
////////////////////////////
//hWnd（ツリービュ―、リストビュー）のアイテムのコンテキストメニュー表示
bool ShowContextMenu(PIDLIST_ABSOLUTE pidlAbsolute, HWND hWnd) {

	int					nId;
	HRESULT				hr;
	POINT				pt;
	HMENU				hPopup;
	IContextMenu		*pContextMenu;
	IShellFolder		*pShellFolder;
	PITEMID_CHILD		pidlChild;
	CMINVOKECOMMANDINFO	ici;
	//
	SHBindToParent(pidlAbsolute, IID_PPV_ARGS(&pShellFolder), NULL);
	/*
	SHSTDAPI SHBindToParent(
		PCIDLIST_ABSOLUTE pidl,
		REFIID			riid,
		void			**ppv,
		PCUITEMID_CHILD	*ppidlLast
	);
	このマクロは引数に指定された変数（**ppv）から適切なIIDを割り出すため、
	IIDが変数の型と矛盾することは完全になくなる。IID_PPV_ARGSマクロは、割り
	出したIIDと変数のアドレスをカンマで区切って（"riid, **ppv"）返す。
	上記例ではIShellFolder型からフォールダーオブジェクトへのポインターを返す。
	*/
	pidlChild = ILFindLastID(pidlAbsolute);	//PIDL中最後のITEMID_CHILDへのポインターを返す
	//特定のファイルやフォールダーの実行可能な処理オブジェクトを取り出す
	hr = pShellFolder->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST *)&pidlChild, IID_IContextMenu, NULL, (void **)&pContextMenu);
	if(hr != S_OK) {
		pShellFolder->Release();	//メモリー解放
		return FALSE;
	}
	//ポップアップメニューオブジェクトの作成とコンテキストメニューコマンドの追加
	hPopup = CreatePopupMenu();
	pContextMenu->QueryContextMenu(hPopup, 0, 1, 0x7fff, CMF_NORMAL);
    //マウス位置の取得とポップアップメニューの表示
	GetCursorPos(&pt);
	nId = TrackPopupMenu(hPopup, TPM_RETURNCMD, pt.x, pt.y, 0, hWnd, NULL);
	//ポップアップメニュー表示に失敗
	if (nId == 0) {
		pContextMenu->Release();	//メモリー解放
		pShellFolder->Release();	//メモリー解放
		return FALSE;
	}
	ici.cbSize		= sizeof(CMINVOKECOMMANDINFO);
	ici.fMask		= 0;
	ici.hwnd		= hWnd;
	ici.lpVerb		= (LPCSTR)MAKEINTRESOURCE(nId - 1);
	ici.lpParameters= NULL;
	ici.lpDirectory	= NULL;
	ici.nShow		= SW_SHOW;
	//コンテキストメニューで指定したコマンドの実行
	hr = pContextMenu->InvokeCommand(&ici);
	//メモリー解放
	pContextMenu->Release();
	pShellFolder->Release();
	return hr == S_OK;
}

//WM_NOTIFYの内容によりツリービュ―、リストビューのコンテキストメニューを表示
bool SetContextMenu(LPNMHDR lpNmhdr, LPARAM lParam) {

	PIDLIST_ABSOLUTE pidlAbsolute;	//絶対PIDL
	if(lpNmhdr->hwndFrom == g_lv.GetHandle()) {			//リストビューの場合
		LVITEM item;
		//NMITEMACTIVATEのiItemメンバーは使われていない場合-1となる
		if(((LPNMITEMACTIVATE)lParam)->iItem == -1)
			pidlAbsolute = g_Shell.GetCurFolder();
		else {
			item.mask = LVIF_PARAM;
			item.iItem = ((LPNMITEMACTIVATE)lParam)->iItem;
			item.iSubItem = 0;
			//リストビューアイテム情報取得
			ListView_GetItem(g_lv.GetHandle(), &item);
			//当該アイテム（ファイル・フォールダー）の絶対PIDL
			pidlAbsolute = (PIDLIST_ABSOLUTE)item.lParam;
		}
	}
	else if(lpNmhdr->hwndFrom == g_tv.GetHandle())		//ツリービューの場合
		pidlAbsolute = g_Shell.GetCurFolder();
	else
		return FALSE;
	//コンテキストメニュー表示
	return ShowContextMenu(pidlAbsolute, lpNmhdr->hwndFrom);
}

/*
参考情報
【IID_PPV_ARGS マクロ】
自動的に使用されるインタフェースポインタの型に基づいて要求されたインターフェイスのIID値を供給して、インターフェイスポインタを取得するために使用します。
これは、コンパイル時に渡された値の型を確認することにより、一般的なコーディングエラーを回避できます。
void IID_PPV_ARGS(
	T **pType
);
[パラメーター]
pType
型Tのオブジェクトが要求されている種類を決定するために使用されるインターフェイスポインタのアドレスです。マクロは、このパラメータを介してインターフェイスポインタを返します

[戻り値]
このマクロは値を返しません。

[解説]
インターフェイスポインタを取得する方法で一般的な構文は、次の2つのパラメータ（特にQueryInterfaceとCoCreateInstance）が含まれています。
[入力]パラメータは、通常の型REFIID、取得するためのインターフェイスのIIDを指定します。
[出力]パラメーターは、通常の型void **、インターフェイス ポインターを受け取ることができます。
このマクロは、IIDおよびインターフェイスポインタの型が一致しないようなコーディングエラーを防ぐインターフェイスポインタの型に基づいて、IIDを計算します。
Windows開発者は、常に別のIIDおよびインターフェイスポインタのパラメータを必要とする任意の方法でこのマクロを使用する必要があります。

次の例は、IPropertyStoreを用いてメモリプロパティストアオブジェクトを作成するIID_PPV_ARGSの使用方法を示します。

IPropertyStore *pPropertyStore;
CoCreateInstance(CLSID_PropertyStore, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pPropertyStore));
*/