/* 
 * Copyright (c) [2010-2019] zhenfei.mzf@gmail.com rights reserved.
 * 
 * AntiSpy is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 *
 *     http://license.coscl.org.cn/MulanPSL
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v1 for more details.
*/
// xySplitterWnd.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"
#include "xySplitterWnd.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CxSplitterWnd

CxSplitterWnd::CxSplitterWnd()
{
}

CxSplitterWnd::~CxSplitterWnd()
{
}

BOOL CxSplitterWnd::BindWithControl(CWnd *parent, DWORD ctrlId)
{
    m_Parent = parent;
    m_minLeft = m_minRight = 10;

    SubclassWindow(m_Parent->GetDlgItem(ctrlId)->GetSafeHwnd());

    // Make sure to get mouse message from the dialog window
    DWORD style = GetStyle();
    ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, style | SS_NOTIFY);

    return TRUE;
}

void CxSplitterWnd::Unbind(void)
{
    DetachAllPanes();
    UnsubclassWindow();
}

void CxSplitterWnd::SetMinWidth(int left, int right)
{
    m_minLeft = left;
    m_minRight = right;
}

BOOL CxSplitterWnd::AttachAsLeftPane(DWORD ctrlId)
{
    m_leftIds.Add(ctrlId);
    return TRUE;
}

BOOL CxSplitterWnd::AttachAsRightPane(DWORD ctrlId)
{
    m_rightIds.Add(ctrlId);
    return TRUE;
}

BOOL CxSplitterWnd::DetachAllPanes(void)
{
    m_leftIds.RemoveAll();
    m_rightIds.RemoveAll();
    return TRUE;
}

void CxSplitterWnd::RecalcLayout(void)
{
    CWnd *pane;
    RECT rcBar, rcPane;

    GetWindowRect(&rcBar);
    m_Parent->ScreenToClient(&rcBar);

    int i;
    DWORD id;

    for (i=0; i<m_leftIds.GetSize(); i++) {
        id = m_leftIds.GetAt(i);
	    pane = m_Parent->GetDlgItem(id);
        pane->GetWindowRect(&rcPane);
        m_Parent->ScreenToClient(&rcPane);
        rcPane.right = rcBar.left - 1;
        pane->MoveWindow(&rcPane, FALSE);
    }

    for (i=0; i<m_rightIds.GetSize(); i++) {
        id = m_rightIds.GetAt(i);
	    pane = m_Parent->GetDlgItem(id);
        pane->GetWindowRect(&rcPane);
        m_Parent->ScreenToClient(&rcPane);
        rcPane.left = rcBar.right + 1;
        pane->MoveWindow(&rcPane, FALSE);
    }

    m_Parent->Invalidate();
}

BOOL CxSplitterWnd::GetMouseClipRect(LPRECT rectClip, CPoint point)
{
    RECT rectOrg, rectTarget, rectParent, rectPane;
    int i;
    DWORD id;

    GetWindowRect(&rectOrg);
    m_Parent->GetClientRect(&rectParent);
    m_Parent->ClientToScreen(&rectParent);

    rectTarget = rectOrg;
    rectTarget.left = rectParent.left + m_minLeft;
    for (i=0; i<m_leftIds.GetSize(); i++) {
        id = m_leftIds.GetAt(i);
        m_Parent->GetDlgItem(id)->GetWindowRect(&rectPane);
        if (rectTarget.left < rectPane.left + m_minLeft) {
            rectTarget.left = rectPane.left + m_minLeft;
        }
    }

    rectTarget.right = rectParent.right - m_minRight;
    for (i=0; i<m_rightIds.GetSize(); i++) {
        id = m_rightIds.GetAt(i);
        m_Parent->GetDlgItem(id)->GetWindowRect(&rectPane);
        if (rectTarget.right > rectPane.right - m_minRight) {
            rectTarget.right = rectPane.right - m_minRight;
        }
    }

    if (rectTarget.left >= rectTarget.right) {
        TRACE("No room to drag the x-splitter bar");
        return FALSE;
    }

    rectClip->left = rectTarget.left + point.x;
    rectClip->right = rectTarget.right - (rectOrg.right - rectOrg.left - point.x) + 1;
    rectClip->top = rectOrg.top;
    rectClip->bottom = rectOrg.bottom;

    return TRUE;
}

BEGIN_MESSAGE_MAP(CxSplitterWnd, CWnd)
	//{{AFX_MSG_MAP(CxSplitterWnd)
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CxSplitterWnd message handlers

BOOL CxSplitterWnd::OnSetCursor(CWnd* /*pWnd*/, UINT /*nHitTest*/, UINT /*message*/) 
{
	// TODO: Add your message handler code here and/or call default
    ::SetCursor(AfxGetApp()->LoadCursor(/*AFX_IDC_HSPLITBAR*/IDC_Y_CURSOR));
    return TRUE;
	
	//return CWnd::OnSetCursor(pWnd, nHitTest, message);
}


void CxSplitterWnd::OnLButtonDown(UINT /*nFlags*/, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default

	// don't handle if capture already set
	if (::GetCapture() != NULL) return;

    // don't handle if no room to drag
    RECT rectMouseClip;
    if (!GetMouseClipRect(&rectMouseClip, point)) return;
    ::ClipCursor(&rectMouseClip);

	// set capture to the window which received this message
	SetCapture();
	ASSERT(this == CWnd::GetCapture());

    // get DC for drawing
	CDC* pDrawDC;
    pDrawDC = m_Parent->GetDC();
	ASSERT_VALID(pDrawDC);

    int     curX, curY;
	int     xDiff, yDiff;
	CRect   rectOrg, rectCur, rectOld;
    CSize   sizeBar;

    GetWindowRect(rectOrg);
    sizeBar = CSize(rectOrg.Width(), rectOrg.Height());

    m_Parent->ScreenToClient(rectOrg);
    pDrawDC->DrawDragRect(&rectOrg, sizeBar, NULL, sizeBar);
    rectOld = rectCur = rectOrg;
    xDiff = yDiff = 0;

	// get messages until capture lost or cancelled/accepted
	for (;;) {
		MSG msg;
		VERIFY(::GetMessage(&msg, NULL, 0, 0));

		if (CWnd::GetCapture() != this)
			break;

		switch (msg.message) {
		// handle movement/accept messages
		case WM_MOUSEMOVE:
			// handle resize cases (and part of move)
            curX = (int)(short)LOWORD(msg.lParam);
            curY = (int)(short)HIWORD(msg.lParam);

            xDiff = curX - point.x;
            yDiff = curY - point.y;

            rectCur = rectOrg;
            rectCur.left += xDiff;
            rectCur.right += xDiff;
            pDrawDC->DrawDragRect(&rectCur, sizeBar, &rectOld, sizeBar);
            rectOld = rectCur;

            break;

		// handle cancel messages
		case WM_KEYDOWN:
			if (msg.wParam != VK_ESCAPE)
				break;
		case WM_LBUTTONUP:
		case WM_RBUTTONDOWN:
            goto ExitLoop;

		// just dispatch rest of the messages
		default:
			DispatchMessage(&msg);
			break;
		}
	}

ExitLoop:
    pDrawDC->DrawDragRect(&rectCur, sizeBar, NULL, sizeBar);

    m_Parent->ReleaseDC(pDrawDC);
    ReleaseCapture();
    ::ClipCursor(NULL);

    if (xDiff == 0) return;

    // move the splitter bar & re-position the attached panes if necessary
    MoveWindow(rectCur, FALSE);
    RecalcLayout();

	m_Parent->SendMessage(WM_SPLITTER_MOVED, xDiff, GetDlgCtrlID());

    //CWnd::OnLButtonDown(nFlags, point);
}

/////////////////////////////////////////////////////////////////////////////
// CySplitterWnd

CySplitterWnd::CySplitterWnd()
{
}

CySplitterWnd::~CySplitterWnd()
{
}

BOOL CySplitterWnd::BindWithControl(CWnd *parent, DWORD ctrlId)
{
    m_Parent = parent;

    SubclassWindow(m_Parent->GetDlgItem(ctrlId)->GetSafeHwnd());
    
    // Make sure to get mouse message from the dialog window
    DWORD style = GetStyle();
    ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, style | SS_NOTIFY);

    return TRUE;
}

void CySplitterWnd::Unbind(void)
{
    DetachAllPanes();
    UnsubclassWindow();
}

void CySplitterWnd::SetMinHeight(int above, int below)
{
    m_minAbove = above;
    m_minBelow = below;
}

BOOL CySplitterWnd::AttachAsAbovePane(DWORD ctrlId)
{
    m_aboveIds.Add(ctrlId);
    return TRUE;
}

BOOL CySplitterWnd::AttachAsBelowPane(DWORD ctrlId)
{
    m_belowIds.Add(ctrlId);
    return TRUE;
}

BOOL CySplitterWnd::DetachAllPanes(void)
{
    m_aboveIds.RemoveAll();
    m_belowIds.RemoveAll();
    return TRUE;
}

void CySplitterWnd::RecalcLayout(void)
{
    CWnd *pane;
    RECT rcBar, rcPane;

    GetWindowRect(&rcBar);
    m_Parent->ScreenToClient(&rcBar);

    int i;
    DWORD id;

    for (i=0; i<m_aboveIds.GetSize(); i++) {
        id = m_aboveIds.GetAt(i);
	    pane = m_Parent->GetDlgItem(id);
        pane->GetWindowRect(&rcPane);
        m_Parent->ScreenToClient(&rcPane);
        rcPane.bottom = rcBar.top - 1;
        pane->MoveWindow(&rcPane, FALSE);
    }

    for (i=0; i<m_belowIds.GetSize(); i++) {
        id = m_belowIds.GetAt(i);
	    pane = m_Parent->GetDlgItem(id);
        pane->GetWindowRect(&rcPane);
        m_Parent->ScreenToClient(&rcPane);
        rcPane.top = rcBar.bottom + 1;
        pane->MoveWindow(&rcPane, FALSE);
    }

	m_Parent->SendMessage(WM_RESIZE_ALL_PROC_WND);
    m_Parent->Invalidate();
}

BOOL CySplitterWnd::GetMouseClipRect(LPRECT rectClip, CPoint point)
{
    RECT rectOrg, rectTarget, rectParent, rectPane;
    int i;
    DWORD id;

    GetWindowRect(&rectOrg);
    m_Parent->GetClientRect(&rectParent);
    m_Parent->ClientToScreen(&rectParent);

    rectTarget = rectOrg;
    rectTarget.top = rectParent.top + m_minAbove;
    for (i=0; i<m_aboveIds.GetSize(); i++) {
        id = m_aboveIds.GetAt(i);
        m_Parent->GetDlgItem(id)->GetWindowRect(&rectPane);
        if (rectTarget.top < rectPane.top + m_minAbove) {
            rectTarget.top = rectPane.top + m_minAbove;
        }
    }

    rectTarget.bottom = rectParent.bottom - m_minBelow;
    for (i=0; i<m_belowIds.GetSize(); i++) {
        id = m_belowIds.GetAt(i);
        m_Parent->GetDlgItem(id)->GetWindowRect(&rectPane);
        if (rectTarget.bottom > rectPane.bottom - m_minBelow) {
            rectTarget.bottom = rectPane.bottom - m_minBelow;
        }
    }

    if (rectTarget.top >= rectTarget.bottom) {
        TRACE("No room to drag the y-splitter bar");
        return FALSE;
    }

    rectClip->left = rectOrg.left;
    rectClip->right = rectOrg.right;
    rectClip->top = rectTarget.top + point.y;
    rectClip->bottom = rectTarget.bottom - (rectOrg.bottom - rectOrg.top - point.y) + 1;

    return TRUE;
}


BEGIN_MESSAGE_MAP(CySplitterWnd, CWnd)
	//{{AFX_MSG_MAP(CySplitterWnd)
	ON_WM_LBUTTONDOWN()
	ON_WM_SETCURSOR()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CySplitterWnd message handlers

BOOL CySplitterWnd::OnSetCursor(CWnd* /*pWnd*/, UINT /*nHitTest*/, UINT /*message*/) 
{
	// TODO: Add your message handler code here and/or call default
    ::SetCursor(AfxGetApp()->LoadCursor(/*AFX_IDC_VSPLITBAR*/IDC_X_CURSOR));
    return TRUE;
	
	//return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

void CySplitterWnd::OnLButtonDown(UINT /*nFlags*/, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default

	// don't handle if capture already set
	if (::GetCapture() != NULL) return;

    // don't handle if no room to drag
    RECT rectMouseClip;
    if (!GetMouseClipRect(&rectMouseClip, point)) return;
    ::ClipCursor(&rectMouseClip);

	// set capture to the window which received this message
	SetCapture();
	ASSERT(this == CWnd::GetCapture());

    // get DC for drawing
	CDC* pDrawDC;
    pDrawDC = m_Parent->GetDC();
	ASSERT_VALID(pDrawDC);

    int     curX, curY;
	int     xDiff, yDiff;
	CRect   rectOrg, rectCur, rectOld;
    CSize   sizeBar;

    GetWindowRect(rectOrg);
    sizeBar = CSize(rectOrg.Width(), rectOrg.Height());

    m_Parent->ScreenToClient(rectOrg);
    pDrawDC->DrawDragRect(&rectOrg, sizeBar, NULL, sizeBar);
    rectOld = rectCur = rectOrg;
    xDiff = yDiff = 0;

	// get messages until capture lost or cancelled/accepted
	for (;;) {
		MSG msg;
		VERIFY(::GetMessage(&msg, NULL, 0, 0));

		if (CWnd::GetCapture() != this)
			break;

		switch (msg.message) {
		// handle movement/accept messages
		case WM_MOUSEMOVE:
			// handle resize cases (and part of move)
            curX = (int)(short)LOWORD(msg.lParam);
            curY = (int)(short)HIWORD(msg.lParam);

            xDiff = curX - point.x;
            yDiff = curY - point.y;

            rectCur = rectOrg;
            rectCur.top += yDiff;
            rectCur.bottom += yDiff;
            pDrawDC->DrawDragRect(&rectCur, sizeBar, &rectOld, sizeBar);
            rectOld = rectCur;

            break;

		// handle cancel messages
		case WM_KEYDOWN:
			if (msg.wParam != VK_ESCAPE)
				break;
		case WM_LBUTTONUP:
		case WM_RBUTTONDOWN:
            goto ExitLoop;

		// just dispatch rest of the messages
		default:
			DispatchMessage(&msg);
			break;
		}
	}

ExitLoop:
    pDrawDC->DrawDragRect(&rectCur, sizeBar, NULL, sizeBar);

    m_Parent->ReleaseDC(pDrawDC);
    ReleaseCapture();
    ::ClipCursor(NULL);

    if (yDiff == 0) return;

    // move the splitter bar & re-position the attached panes if necessary
    MoveWindow(rectCur, FALSE);
    RecalcLayout();

	m_Parent->SendMessage(WM_SPLITTER_MOVED, yDiff, GetDlgCtrlID());

    //CWnd::OnLButtonDown(nFlags, point);
}
