////////////////////////////
// CSPLITクラス定義ファイル
// ECCSkelton用CVSPLITクラス
// を省略して改造したもの
// Copyright by Ysama 2022
////////////////////////////

#define	_SPLITBORDER	1					//境界線の幅

class CSPLIT : public CCTRL
{
public:		//メンバー変数
	int m_VBorder;							//水平区切り線の縦位置
	int m_ClientWidth;						//自分のクライアント領域の幅
	int m_ClientHeight;						//自分のクライアント領域の高さ
	HCURSOR m_NormCrs;						//標準カーソル
	HCURSOR m_HCursor;						//水平矢印カーソル
	bool m_VSwitch;							//縦境界移動許可フラグ
	int m_CtrlID[2];						//各コントロールのID(0-L, 1-R)
public:		//メンバー関数
	CSPLIT(int);							//コンストラクター
	~CSPLIT();								//デコンストラクター
	bool Create(HWND, HINSTANCE, HMENU);	//CSPLITウィンドウ作成関数
	void AutoSize(HWND, HWND);				//自動サイズ調整
	static LRESULT CALLBACK DummyProc(HWND, UINT, WPARAM, LPARAM);	//ダミーのコールバック関数
	virtual LRESULT CALLBACK SplitProc(HWND, UINT, WPARAM, LPARAM);	//本来のコールバック関数
	void SetBorders(int);					//境界の位置を設定する
	void MoveCtrl();						//コントロールを移動させる
	HWND SetCtrl(int, int);					//コントロールを配置する
	HWND GetCtrl(int);						//コントロールのハンドルを返す
	void DeleteCtrl(int);					//コントロールを削除する
};

//////////////////////
// メンバー関数の実装
//////////////////////
//コンストラクター(初期化のみ）
CSPLIT::CSPLIT(int v) {		//2021年11月21日修正

	m_hWnd			= 0;
	m_dwExStyle		= 0;
	m_lpClassName	= L"SPLITTER";
	m_lpWindowName	= NULL;
	m_dwStyle		= WS_CHILD | WS_CLIPCHILDREN;
	m_x				= 0;
	m_y				= 0;
	m_Width			= 0;
	m_Height		= 0;
	m_hParent		= NULL;
	m_hMenu			= NULL;
	m_hInstance		= 0;
	m_lpParam		= NULL;
	//カーソル形状情報
	m_NormCrs = LoadCursor(NULL, IDC_ARROW);	//標準カーソル
	m_HCursor = LoadCursor(NULL, IDC_SIZEWE);	//水平矢印カーソル
	m_VSwitch = FALSE;
	//境界位置の初期化
	m_VBorder		= v;
}

//デストラクター
CSPLIT::~CSPLIT() {
	DestroyWindow(m_hWnd);
}

//ウィンドウ作成関数
bool CSPLIT::Create(HWND hParent, HINSTANCE hInstance, HMENU ID) {

	//ウィンドウクラスの登録
	WNDCLASSEX WndClass;
	WndClass.cbSize		= sizeof(WNDCLASSEX);			//この構造体のサイズ
	WndClass.style		= CS_HREDRAW | CS_VREDRAW;		//縦横移動、サイズ変更時再描画
	WndClass.lpfnWndProc= DummyProc;					//ダミーコールバック関数ポインター
	WndClass.cbClsExtra	= 0;							//この構造体以降に置くバイト数
	WndClass.cbWndExtra	= 0;							//インスタンス以降に置くバイト数
	WndClass.hInstance	= m_hInstance = hInstance;		//このウィンドウのインスタンス
	WndClass.hIcon			= NULL;						//このウィンドウクラスのアイコン
	WndClass.hCursor		= m_NormCrs;				//このウィンドウクラスのカーソル
	WndClass.hbrBackground	= (HBRUSH)(COLOR_BTNFACE + 1);//このウィンドウクラスの背景色
	WndClass.lpszMenuName	= NULL;						//このウィンドウクラスのメニュー
	WndClass.lpszClassName	= m_lpClassName;			//このウィンドウクラスの名称
	WndClass.hIconSm		= NULL;						//最小化時の小さいアイコン
	if(!RegisterClassExW(&WndClass)) {
		MessageBoxW(0, L"ウィンドウを登録できませんでした。", L"エラー",
				MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
		return FALSE;
	}
	CCTRL::Create(L"", WS_CHILD | WS_CLIPCHILDREN, 0, m_hParent = hParent, ID);
	//プロパティに書き込む
 	SetPropW(m_hWnd, L"CSPLIT", (HANDLE)this);
	return TRUE;
}

//スプリッターのサイズを親ウィンドウに合わせる
void CSPLIT::AutoSize(HWND hStatusBar = 0, HWND hToolBar = 0) {

	RECT rect;
	UINT x, y, w, h;
	//親ウィンドウのサイズを取得
	GetClientRect(m_hParent, &rect);
	x = rect.left;
	y = rect.top;
	w = rect.right - rect.left;
	h = rect.bottom - rect.top;
	//ツールバーの高さを取得
	if(hToolBar) {
		GetWindowRect(hToolBar, &rect);
		y += rect.bottom - rect.top;
		h -= rect.bottom - rect.top;
	}
	//ステータスバーの高さを取得
	if(hStatusBar) {
		GetWindowRect(hStatusBar, &rect);
		h -= rect.bottom - rect.top;
	}
	//スプリッターのサイズをあわせる
	MoveWindow(m_hWnd, x, y, w, h, TRUE);
	//CSPLITウィンドウの位置、サイズを保存
	GetClientRect(m_hWnd, &rect);
	m_ClientWidth = rect.right - rect.left;
	m_ClientHeight = rect.bottom - rect.top;
}

//ダミーコールバック関数
LRESULT CALLBACK CSPLIT::DummyProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) {

	//Initメンバー関数が書き込んだこのクラスのインスタンスへのポインターを読む
	CSPLIT* pSplit = (CSPLIT*)GetPropW(hWnd, L"CSPLIT");
	//もしそれが読み込めたら
	if(pSplit) {
		//本来のコールバック関数（SplitProc）を呼ぶ
		LRESULT lResult = pSplit->SplitProc(hWnd, Message, wParam, lParam);
		//WM_DESTROYメッセージがきた段階でウィンドウプロパティを廃棄する
		if (Message == WM_DESTROY)
			RemovePropW(hWnd, L"CSPLIT");
		return lResult;
	}
	else
		//ウィンドウズの処理に任せる
		return DefWindowProcW(hWnd, Message, wParam, lParam);
}

//本来のコールバック関数（仮想関数）
LRESULT CALLBACK CSPLIT::SplitProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) {

	switch(Message) {
		case WM_CREATE:
			//親のクライアント領域にあわせる
			RECT rec;
			GetClientRect(m_hParent, &rec);
			SendMessageW(m_hParent, WM_SIZE, SIZE_RESTORED,
						MAKELONG(rec.right - rec.left, rec.bottom - rec.top));
			return 0;
		case WM_NOTIFY:
			//親にメッセージを包み隠さず報告
			SendMessageW(m_hParent, WM_NOTIFY, wParam, lParam);
			break;
		case WM_LBUTTONDOWN:
			//カーソル形状を変化させて垂直境界の移動を許可する
			if(LOWORD(lParam) > (m_VBorder - _SPLITBORDER) && LOWORD(lParam) < (m_VBorder + _SPLITBORDER)) {
				SetCursor(m_HCursor);
				m_VSwitch = TRUE;
				SetCapture(hWnd);
			}
			break;
		case WM_MOUSEMOVE:
			//左ボタンが押されていれば、カーソル形状を変化させて境界移動を行なう
			if(wParam & MK_LBUTTON) {
				POINTS pt = MAKEPOINTS(lParam);
				if(m_VSwitch) {
					SetCursor(m_HCursor);
					m_VBorder = pt.x;
					if(m_VBorder < _SPLITBORDER)
						m_VBorder = _SPLITBORDER;
					else if(m_VBorder > m_ClientWidth - _SPLITBORDER)
						m_VBorder = m_ClientWidth - _SPLITBORDER;
					MoveCtrl();
				}
			}
			//左ボタンが押されていなければカーソル形状を変化させるだけ
			else {
				if(LOWORD(lParam) > (m_VBorder - _SPLITBORDER) && LOWORD(lParam) < (m_VBorder + _SPLITBORDER))
					SetCursor(m_HCursor);
			}
			break;
		case WM_LBUTTONUP:
			//カーソル形状を元に戻し、境界移動を禁止する
			m_VSwitch = FALSE;
			SetCursor(m_NormCrs);
			ReleaseCapture();
			break;
		case WM_SIZE:
			//CSPLITウィンドウのサイズを保存
			m_ClientWidth = LOWORD(lParam);
			m_ClientHeight = HIWORD(lParam);
			//Ctrl[0]だけならばクライアント領域全体とする
			if(!m_CtrlID[1])
				m_VBorder = m_ClientWidth;
			MoveCtrl();
			break;
		default:
			return DefWindowProcW(hWnd, Message, wParam, lParam);
	}
	return 0;
}

//境界の位置を設定する
void CSPLIT::SetBorders(int v) {

	m_VBorder = v;
	MoveCtrl();
}

//コントロールを移動させる
void CSPLIT::MoveCtrl() {

	//ちらつき防止のために、ウインドウの更新を一時ストップ
	LockWindowUpdate(m_hWnd);
	if(m_CtrlID[0])
		MoveWindow(GetCtrl(0), 0, 0, m_VBorder - _SPLITBORDER * 2, m_ClientHeight, TRUE);
	if(m_CtrlID[1])
		MoveWindow(GetCtrl(1), m_VBorder + _SPLITBORDER, 0, m_ClientWidth - m_VBorder - _SPLITBORDER * 2,  m_ClientHeight, TRUE);
	LockWindowUpdate(NULL);
	//再開しただけでは書き直されないので、再描画
	UpdateWindow(m_hWnd);
}

//コントロールを配置する
HWND CSPLIT::SetCtrl(int pos, int ID) {

	//既に作成されていれば何もしないで帰る
	if(m_CtrlID[pos])	return FALSE;
	//コントロールIDの登録
	m_CtrlID[pos] = ID;
	//このSplitterをコントロールの親にする
	SetParent(GetDlgItem(m_hParent, m_CtrlID[pos]), m_hWnd);
	//コントロールの移動
	MoveCtrl();
	return GetDlgItem(m_hWnd, m_CtrlID[pos]);
}

//コントロールのハンドルを返す
HWND CSPLIT::GetCtrl(int pos) {

	return GetDlgItem(m_hWnd, m_CtrlID[pos]);
}

//コントロールを削除する
void CSPLIT::DeleteCtrl(int pos) {

	//ウィンドウを破壊する
	DestroyWindow(GetDlgItem(m_hWnd, m_CtrlID[pos]));
	m_CtrlID[pos] = 0;	//不存在の状態
}
