/////////////////////////////
// CVSPLITクラス定義ファイル
/////////////////////////////

#define	_SPLITBORDER	2

class CVSPLIT : public CCTRL
{
public:		//メンバー変数
	int m_VBorder;							//水平区切り線の縦位置
	int m_HBorder1;							//垂直区切り線の横位置
	int m_HBorder2;							//垂直区切り線の横位置
	int m_ClientWidth;						//自分のクライアント領域の幅
	int m_ClientHeight;						//自分のクライアント領域の高さ
	HCURSOR m_NormCrs;						//標準カーソル
	HCURSOR m_HCursor;						//水平矢印カーソル
	HCURSOR m_VCursor;						//垂直矢印カーソル
	bool m_VSwitch;							//縦境界移動許可フラグ
	bool m_HSwitch1;						//横境界１移動許可フラグ
	bool m_HSwitch2;						//横境界２移動許可フラグ
	int m_CtrlID[4];						//各コントロールのID(0-UL, 1-UR, 2LL, 3-LR)
	CTAB* m_Tab[4];							//Tabを使う場合のポインター(0-UL, 1-UR, 2LL, 3-LR)

public:		//メンバー関数
	CVSPLIT();									//コンストラクター（１）
	CVSPLIT(int, int, int);							//コンストラクター（２）
	~CVSPLIT();										//デコンストラクター
	bool Create(HWND, HINSTANCE, int);				//CVSPLITウィンドウ作成関数
	void AutoSize(HWND, HWND);						//自動サイズ調整
	static LRESULT CALLBACK DummyProc(HWND, UINT, WPARAM, LPARAM);	//ダミーのコールバック関数
	virtual LRESULT CALLBACK SplitProc(HWND, UINT, WPARAM, LPARAM);	//本来のコールバック関数
	void SetBorders(int, int, int);					//境界の位置を設定する
	void MoveCtrl();								//コントロールを移動させる
	HWND SetCtrl(int, int);							//コントロールを配置する
	HWND GetCtrl(int);								//コントロールのハンドルを返す
	void DeleteCtrl(int);							//コントロールを削除する
	CTAB* SetTab(int, int, int, int, int);			//Tabを配置する
	CTAB* GetTab(int);								//Tabのポインターを返す
	void DeleteTab(int);							//Tabを削除する
};

//////////////////////
// メンバー関数の実装
//////////////////////
//コンストラクター(１）
CVSPLIT::CVSPLIT() {

	m_hWnd			= 0;
	m_dwExStyle		= 0;
	m_lpClassName	= "VSPLITTER";
	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_VCursor = LoadCursor(NULL, IDC_SIZENS);	//垂直矢印カーソル
	m_VSwitch = m_HSwitch1 = m_HSwitch2 = FALSE;
	//境界位置の初期化
	m_VBorder		= 150;
	m_HBorder1		= 300;
	m_HBorder2		= 300;
}

//コンストラクター(２）
CVSPLIT::CVSPLIT(int v, int h1, int h2) {

	m_hWnd			= 0;
	m_dwExStyle		= 0;
	m_lpClassName	= "VSPLITTER";
	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_VCursor = LoadCursor(NULL, IDC_SIZENS);	//垂直矢印カーソル
	m_VSwitch = m_HSwitch1 = m_HSwitch2 = FALSE;
	//境界位置の初期化
	m_VBorder		= v;
	m_HBorder1		= h1;
	m_HBorder2		= h2;
}

//デストラクター
CVSPLIT::~CVSPLIT() {

	//Tabを破壊する
	for(int i = 0; i < 4; i++)
		if(m_Tab[i])	delete m_Tab[i];
	DestroyWindow(m_hWnd);
}

//ウィンドウ作成関数
bool CVSPLIT::Create(HWND hParent, HINSTANCE hInstance, int 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(!RegisterClassEx(&WndClass)) {
		MessageBox(0, "ウィンドウを登録できませんでした。", "エラー",
				MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
		return FALSE;
	}
	CCTRL::Create("", WS_CHILD | WS_CLIPCHILDREN, 0, m_hParent = hParent, ID);
	//プロパティに書き込む
 	SetProp(m_hWnd, "CVSPLIT", (HANDLE)this);
	return TRUE;
}

//スプリッターのサイズを親ウィンドウに合わせる
void CVSPLIT::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);
	//CVSPLITウィンドウの位置、サイズを保存
	GetClientRect(m_hWnd, &rect);
	m_ClientWidth = rect.right - rect.left;
	m_ClientHeight = rect.bottom - rect.top;
}

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

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

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

	switch(Message) {
		case WM_CREATE:
			//親のクライアント領域にあわせる
			RECT rec;
			GetClientRect(m_hParent, &rec);
			SendMessage(m_hParent, WM_SIZE, SIZE_RESTORED,
						MAKELONG(rec.right - rec.left, rec.bottom - rec.top));
			return 0;
		case WM_NOTIFY:
			//親にメッセージを包み隠さず報告
			SendMessage(m_hParent, WM_NOTIFY, (int)m_hMenu, lParam);
			//Tabコントロール制御
			for(int i = 0; i < 4; i++)
				if(m_Tab[i])	m_Tab[i]->FitSiblings(wParam);
			break;
		case WM_LBUTTONDOWN:
			//カーソル形状を変化させて水平境界１の移動を許可する
			if(LOWORD(lParam) < m_VBorder - _SPLITBORDER) {
				if(HIWORD(lParam) >= m_HBorder1 - _SPLITBORDER &&
					HIWORD(lParam) <= m_HBorder1 + _SPLITBORDER) {
					SetCursor(m_VCursor);
					m_HSwitch1 = TRUE;
					SetCapture(hWnd);
				}
			}
			//カーソル形状を変化させて水平境界２の移動を許可する
			else if(LOWORD(lParam) > m_VBorder + _SPLITBORDER) {
				if(HIWORD(lParam) >= m_HBorder2 - _SPLITBORDER &&
					HIWORD(lParam) <= m_HBorder2 + _SPLITBORDER) {
					SetCursor(m_VCursor);
					m_HSwitch2 = TRUE;
					SetCapture(hWnd);
				}
			}
			//カーソル形状を変化させて垂直境界の移動を許可する
			else {
				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();
				}
				if(m_HSwitch1) {
					SetCursor(m_VCursor);
					m_HBorder1 = pt.y;
					if(m_HBorder1 < _SPLITBORDER)
						m_HBorder1 = _SPLITBORDER;
					else if(m_HBorder1 > m_ClientHeight - _SPLITBORDER)
						m_HBorder1 = m_ClientHeight - _SPLITBORDER;
					MoveCtrl();
				}
				if(m_HSwitch2) {
					SetCursor(m_VCursor);
					m_HBorder2 = pt.y;
					if(m_HBorder2 < _SPLITBORDER)
						m_HBorder2 = _SPLITBORDER;
					else if(m_HBorder2 > m_ClientHeight - _SPLITBORDER)
						m_HBorder2 = m_ClientHeight - _SPLITBORDER;
					MoveCtrl();
				}
			}
			//左ボタンが押されていなければカーソル形状を変化させるだけ
			else {
				if(LOWORD(lParam) < m_VBorder - _SPLITBORDER) {
					if(HIWORD(lParam) >= m_HBorder1 - _SPLITBORDER &&
						HIWORD(lParam) <= m_HBorder1 + _SPLITBORDER) {
						SetCursor(m_VCursor);
					}
				}
				else if(LOWORD(lParam) > m_VBorder + _SPLITBORDER) {
					if(HIWORD(lParam) >= m_HBorder2 - _SPLITBORDER &&
						HIWORD(lParam) <= m_HBorder2 + _SPLITBORDER) {
						SetCursor(m_VCursor);
					}
				}
				else
					SetCursor(m_HCursor);
			}
			break;
		case WM_LBUTTONUP:
			//カーソル形状を元に戻し、境界移動を禁止する
			m_VSwitch = FALSE;
			m_HSwitch1 = FALSE;
			m_HSwitch2 = FALSE;
			SetCursor(m_NormCrs);
			ReleaseCapture();
			for(int i = 0; i < 4; i++)
				if(m_Tab[i])	m_Tab[i]->FitSiblings(m_CtrlID[i]);
			break;
		case WM_SIZE:
			//CVSPLITウィンドウのサイズを保存
			m_ClientWidth = LOWORD(lParam);
			m_ClientHeight = HIWORD(lParam);
			//コントロールのサイズ調整−m_CtrlID[2/3]に合わせてm_CtrlID[0/1]を動かす
			//ここの動作はCHSPLITクラスと異なります（上を調査して下は動かさない）
			if(!m_CtrlID[1] && !m_CtrlID[3])
				m_VBorder = m_ClientWidth;
			RECT rect;
			if(!m_CtrlID[2])
				m_HBorder1 = m_ClientHeight;
			else {
				GetWindowRect(GetDlgItem(m_hWnd, m_CtrlID[2]), &rect);
				m_HBorder1 = m_ClientHeight - rect.bottom + rect.top - _SPLITBORDER * 2;
			}
			if(!m_CtrlID[3])
				m_HBorder2 = m_ClientHeight;
			else {
				GetWindowRect(GetDlgItem(m_hWnd, m_CtrlID[3]), &rect);
				m_HBorder2 = m_ClientHeight - rect.bottom + rect.top - _SPLITBORDER * 2;
			}
			MoveCtrl();
			break;
		default:
			return DefWindowProc(hWnd, Message, wParam, lParam);
	}
	return 0;
}

//境界の位置を設定する
void CVSPLIT::SetBorders(int v, int h1, int h2) {

	m_VBorder = v;
	m_HBorder1 = h1;
	m_HBorder2 = h2;
	MoveCtrl();
}

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

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

//コントロールを配置する
HWND CVSPLIT::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 CVSPLIT::GetCtrl(int pos) {

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

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

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

//Tabを配置する
CTAB* CVSPLIT::SetTab(int pos, int ID, int w = 16, int h  = 16, int c = ILC_COLOR4) {

	//既に作成されていれば何もしないで帰る
	if(m_CtrlID[pos])	return FALSE;
	//コントロールIDの登録
	m_CtrlID[pos] = ID;
	//CTABクラスのオブジェクトを生成する
	m_Tab[pos] = new CTAB(w, h, c);
	m_Tab[pos]->Create(m_hWnd, m_hInstance, ID);
	//コントロールの移動
	MoveCtrl();
	return m_Tab[pos];
}

//Tabのポインターを返す
CTAB* CVSPLIT::GetTab(int pos) {

	return m_Tab[pos];
}

//Tabを削除する
void CVSPLIT::DeleteTab(int pos) {

	//Tabのページを破壊する
	for(int i = 0, j = m_Tab[pos]->TabCount(); i < j; i++) {
		TabCtrl_SetCurSel(m_Tab[pos]->m_hWnd, i);
		m_Tab[pos]->DeletePage();
	}
	//Tabを破壊する
	DestroyWindow(GetDlgItem(m_hWnd, m_CtrlID[pos]));
	delete m_Tab[pos];
	m_Tab[pos] = 0;
	m_CtrlID[pos] = 0;	//不存在の状態
}
