////////////////////////////////////////////////////////////////////////////
//
// splitex.cpp
// Based upon code from Oleg G. Galkin
// Modified to handle multiple hidden rows

#include "stdafx.h"
#include "splitex.h"

#if defined(_DEBUG) && !defined(MMGR)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

////////////////////////////////////////////////////////////////////////////
//
// CSplitterWndEx

CSplitterWndEx::CSplitterWndEx()
{
	m_arr = 0;
	m_length = 0;
}

CSplitterWndEx::~CSplitterWndEx()
{
	delete [] m_arr;
}

int CSplitterWndEx::Id_short(int row, int col)
{
	return AFX_IDW_PANE_FIRST + row * 16 + col;
}

void CSplitterWndEx::ShowRow(int r)
{
    ASSERT_VALID(this);
    ASSERT(m_nRows < m_nMaxRows);

	ASSERT(m_arr);
	ASSERT(r < m_length);
	ASSERT(m_arr[r] >= m_nRows);
	ASSERT(m_arr[r] < m_length);

	int rowNew = r;
	int cyNew = m_pRowInfo[m_arr[r]].nCurSize;
	int cyIdealNew = m_pRowInfo[m_arr[r]].nIdealSize;
	
	int new_val = 0;

	for (int i = rowNew - 1; i >= 0; i--)
		if (m_arr[i] < m_nRows)	// not hidden
		{
			new_val = m_arr[i] + 1;
			break;
		}

	int old_val = m_arr[rowNew];

    m_nRows++;  // add a row

    // fill the hided row
    int row;
    for (int col = 0; col < m_nCols; col++)
    {
		CWnd* pPaneShow = GetDlgItem(
			Id_short(old_val, col));
        ASSERT(pPaneShow != NULL);
        pPaneShow->ShowWindow(SW_SHOWNA);

		for (row = m_length - 1; row >= 0; row--)
        {
			if ((m_arr[row] >= new_val) &&
				(m_arr[row] < old_val))
			{
				CWnd* pPane = CSplitterWnd::GetPane(m_arr[row], col);
				ASSERT(pPane != NULL);
				pPane->SetDlgCtrlID(Id_short(m_arr[row] + 1, col));
			}
        }
		pPaneShow->SetDlgCtrlID(Id_short(new_val, col));
    }

	for (row = 0; row < m_length; row++)
		if ((m_arr[row] >= new_val) &&
			(m_arr[row] < old_val))
			m_arr[row]++;

	m_arr[rowNew] = new_val;

    //new panes have been created -- recalculate layout
	for (row = new_val + 1; row < m_length; row++)
	{
		if (m_arr[row]<m_nRows)
		{
			m_pRowInfo[m_arr[row]].nIdealSize = m_pRowInfo[m_arr[row-1]].nCurSize;
			m_pRowInfo[m_arr[row]].nCurSize = m_pRowInfo[m_arr[row-1]].nCurSize;
		}
	}
	if (cyNew>=0x10000)
	{
		int rowToResize=(cyNew>>16)-1;
		cyNew%=0x10000;
		cyIdealNew%=0x10000;
		m_pRowInfo[m_arr[rowToResize]].nCurSize-=cyNew+m_cxSplitter;
		m_pRowInfo[m_arr[rowToResize]].nIdealSize=m_pRowInfo[m_arr[rowToResize]].nCurSize;//-=cyIdealNew+m_cxSplitter;
	}

	m_pRowInfo[new_val].nIdealSize = cyNew;
	m_pRowInfo[new_val].nCurSize = cyNew;
    RecalcLayout();
}

void CSplitterWndEx::ShowColumn(int c)
{
    ASSERT_VALID(this);
    ASSERT(m_nCols < m_nMaxCols);

	ASSERT(m_arr);
	ASSERT(c < m_length);
	ASSERT(m_arr[c] >= m_nRows);
	ASSERT(m_arr[c] < m_length);

	int colNew = c;
	int cxNew = m_pColInfo[m_arr[c]].nCurSize;
	int cxIdealNew = m_pColInfo[m_arr[c]].nIdealSize;
	
	int new_val = 0;

	for (int i = colNew - 1; i >= 0; i--)
		if (m_arr[i] < m_nCols)	// not hidden
		{
			new_val = m_arr[i] + 1;
			break;
		}

	int old_val = m_arr[colNew];

    m_nCols++;  // add a col

    // fill the hided col
    int col;
    for (int row = 0; row < m_nRows; row++)
    {
		CWnd* pPaneShow = GetDlgItem(
			Id_short(row, old_val));
        ASSERT(pPaneShow != NULL);
        pPaneShow->ShowWindow(SW_SHOWNA);

		for (col = m_length - 1; col >= 0; col--)
        {
			if ((m_arr[col] >= new_val) &&
				(m_arr[col] < old_val))
			{
				CWnd* pPane = CSplitterWnd::GetPane(row, m_arr[col]);
				ASSERT(pPane != NULL);
				pPane->SetDlgCtrlID(Id_short(row, m_arr[col]+1));
			}
        }
		pPaneShow->SetDlgCtrlID(Id_short(row, new_val));
    }

	for (col = 0; col < m_length; col++)
		if ((m_arr[col] >= new_val) &&
			(m_arr[col] < old_val))
			m_arr[col]++;

	m_arr[colNew] = new_val;

    //new panes have been created -- recalculate layout
	for (col = new_val + 1; col < m_length; col++)
	{
		if (m_arr[col]<m_nCols)
		{
			m_pColInfo[m_arr[col]].nIdealSize = m_pColInfo[m_arr[col-1]].nCurSize;
			m_pColInfo[m_arr[col]].nCurSize = m_pColInfo[m_arr[col-1]].nCurSize;
		}
	}
	if (cxNew>=0x10000)
	{
		int colToResize=(cxNew>>16)-1;
		cxNew%=0x10000;
		cxIdealNew%=0x10000;
		m_pColInfo[m_arr[colToResize]].nCurSize-=cxNew+m_cySplitter;
		m_pColInfo[m_arr[colToResize]].nIdealSize=m_pColInfo[m_arr[colToResize]].nCurSize;//-=cxIdealNew+m_cySplitter;
	}

	m_pColInfo[new_val].nIdealSize = cxNew;
	m_pColInfo[new_val].nCurSize = cxNew;
    RecalcLayout();
}

void CSplitterWndEx::HideRow(int rowHide,int rowToResize)
{
    ASSERT_VALID(this);
    ASSERT(m_nRows > 1);

	if (m_arr)
		ASSERT(m_arr[rowHide] < m_nRows);

    // if the row has an active window -- change it
    int rowActive, colActive;

	if (!m_arr)
	{
		m_arr = new int[m_nRows];
		for (int i = 0; i < m_nRows; i++)
			m_arr[i] = i;
		m_length = m_nRows;
	}

	if (GetActivePane(&rowActive, &colActive) != NULL &&
        rowActive == rowHide) //colActive == rowHide)
    {
        if (++rowActive >= m_nRows)
			rowActive = 0;
        //SetActivePane(rowActive, colActive);

		SetActivePane(rowActive, colActive);
    }

    // hide all row panes
    for (int col = 0; col < m_nCols; col++)
    {
        CWnd* pPaneHide = CSplitterWnd::GetPane(m_arr[rowHide], col);
        ASSERT(pPaneHide != NULL);
	    pPaneHide->ShowWindow(SW_HIDE);

		for (int row = rowHide + 1; row < m_length; row++)
        {
			if (m_arr[row] < m_nRows )
			{
				CWnd* pPane = CSplitterWnd::GetPane(m_arr[row], col);
				ASSERT(pPane != NULL);
				pPane->SetDlgCtrlID(Id_short(row-1, col));
				m_arr[row]--;
			}
        }
        pPaneHide->SetDlgCtrlID(
			Id_short(m_nRows -1 , col));
    }

	int oldsize=m_pRowInfo[m_arr[rowHide]].nCurSize;
	for (int row=rowHide;row<(m_length-1);row++)
	{
		if (m_arr[row+1] < m_nRows )
		{
			m_pRowInfo[m_arr[row]].nCurSize=m_pRowInfo[m_arr[row+1]].nCurSize;
			m_pRowInfo[m_arr[row]].nIdealSize=m_pRowInfo[m_arr[row+1]].nCurSize;		
		}
	}
	if (rowToResize!=-1)
	{
		m_pRowInfo[m_arr[rowToResize]].nCurSize+=oldsize+m_cySplitter;
		m_pRowInfo[m_arr[rowToResize]].nIdealSize+=oldsize+m_cySplitter;
		oldsize+=0x10000*(rowToResize+1);
	}

	m_pRowInfo[m_nRows - 1].nCurSize =oldsize;
	m_pRowInfo[m_nRows - 1].nIdealSize =oldsize;
	
	m_arr[rowHide] = m_nRows-1;
	

    m_nRows--;
	RecalcLayout();
}

void CSplitterWndEx::HideColumn(int colHide, int colToResize)
{
    ASSERT_VALID(this);
    ASSERT(m_nCols > 1);

	if (m_arr)
		ASSERT(m_arr[colHide] < m_nCols);

    // if the col has an active window -- change it
    int colActive, rowActive;

	if (!m_arr)
	{
		m_arr = new int[m_nCols];
		for (int i = 0; i < m_nCols; i++)
			m_arr[i] = i;
		m_length = m_nCols;
	}

	if (GetActivePane(&rowActive, &colActive) != NULL &&
        colActive == colHide)
    {
        if (++colActive >= m_nCols)
			colActive = 0;
        SetActivePane(rowActive, colActive);
    }

    // hide all row panes
    for (int row = 0; row < m_nRows; row++)
    {
        CWnd* pPaneHide = CSplitterWnd::GetPane(row, m_arr[colHide]);
        ASSERT(pPaneHide != NULL);
	    pPaneHide->ShowWindow(SW_HIDE);

		for (int col = colHide + 1; col < m_length; col++)
        {
			if (m_arr[col] < m_nCols )
			{
				CWnd* pPane = CSplitterWnd::GetPane(row, m_arr[col]);
				ASSERT(pPane != NULL);
				pPane->SetDlgCtrlID(Id_short(row, col-1));
				m_arr[col]--;
			}
        }
        pPaneHide->SetDlgCtrlID(
			Id_short(row, m_nCols -1));
    }

	int oldsize = m_pColInfo[m_arr[colHide]].nCurSize;
	for (int col = colHide; col < (m_length - 1); ++col)
	{
		if (m_arr[col + 1] < m_nCols)
		{
			m_pColInfo[m_arr[col]].nCurSize = m_pColInfo[m_arr[col + 1]].nCurSize;
			m_pColInfo[m_arr[col]].nIdealSize = m_pColInfo[m_arr[col + 1]].nCurSize;		
		}
	}
	if (colToResize != -1)
	{
		m_pColInfo[m_arr[colToResize]].nCurSize += oldsize + m_cxSplitter;
		m_pColInfo[m_arr[colToResize]].nIdealSize += oldsize + m_cxSplitter;
		oldsize += 0x10000 * (colToResize + 1);
	}

	m_pColInfo[m_nCols - 1].nCurSize = oldsize;
	m_pColInfo[m_nCols - 1].nIdealSize = oldsize;
	
	m_arr[colHide] = m_nCols - 1;

    m_nCols--;
	RecalcLayout();
}

BEGIN_MESSAGE_MAP(CSplitterWndEx, CSplitterWnd)
//{{AFX_MSG_MAP(CSplitterWndEx)
  // NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CWnd* CSplitterWndEx::GetPane(int row, int col)
{
	if (!m_arr)
		return CSplitterWnd::GetPane(row,col);
	else
	{
		ASSERT_VALID(this);

		CWnd* pView = GetDlgItem(IdFromRowCol(m_arr[row], col));
		ASSERT(pView != NULL);  // panes can be a CWnd, but are usually CViews
		return pView;
	}
}

int CSplitterWndEx::IdFromRowCol(int row, int col) const
{
	ASSERT_VALID(this);
	ASSERT(row >= 0);
	ASSERT(row < (m_arr?m_length:m_nRows));
	ASSERT(col >= 0);
	ASSERT(col < m_nCols);

	return AFX_IDW_PANE_FIRST + row * 16 + col;
}

BOOL CSplitterWndEx::IsChildPane(CWnd* pWnd, int* pRow, int* pCol)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pWnd);

	UINT nID = ::GetDlgCtrlID(pWnd->m_hWnd);
	if (IsChild(pWnd) && nID >= AFX_IDW_PANE_FIRST && nID <= AFX_IDW_PANE_LAST)
	{
		if (pWnd->GetParent()!=this)
			return FALSE;
		if (pRow != NULL)
			*pRow = (nID - AFX_IDW_PANE_FIRST) / 16;
		if (pCol != NULL)
			*pCol = (nID - AFX_IDW_PANE_FIRST) % 16;
		ASSERT(pRow == NULL || *pRow < (m_arr?m_length:m_nRows));
		ASSERT(pCol == NULL || *pCol < m_nCols);
		return TRUE;
	}
	else
	{
		if (pRow != NULL)
			*pRow = -1;
		if (pCol != NULL)
			*pCol = -1;
		return FALSE;
	}
}

CWnd* CSplitterWndEx::GetActivePane(int* pRow, int* pCol)
	// return active view, NULL when no active view
{
	ASSERT_VALID(this);

	// attempt to use active view of frame window
	CWnd* pView = NULL;
	CFrameWnd* pFrameWnd = GetParentFrame();
	ASSERT_VALID(pFrameWnd);
	pView = pFrameWnd->GetActiveView();

	// failing that, use the current focus
	if (pView == NULL)
		pView = GetFocus();

	// make sure the pane is a child pane of the splitter
	if (pView != NULL && !IsChildPane(pView, pRow, pCol))
		pView = NULL;

	return pView;
}

BOOL CSplitterWndEx::IsRowHidden(int row)
{
	return m_arr[row]>=m_nRows;

}

void CSplitterWndEx::GetRowInfoEx(int row, int &cyCur, int &cyMin)
{
	if (!m_arr)
		GetRowInfo(row,cyCur,cyMin);
	else
	{
		if (m_pRowInfo[m_arr[row]].nCurSize>0x10000)
			cyCur=m_pRowInfo[m_arr[row]].nCurSize/0x10000;
		else
			cyCur=m_pRowInfo[m_arr[row]].nCurSize%0x10000;
		cyMin=0;
	}
}

__int64 CSplitterWndEx::GetSizes() const
{
	LARGE_INTEGER v;
	int dummy;
	int s1, s2;
	if (BarIsHorizontal())
	{
		GetRowInfo(0, s1, dummy);
		GetRowInfo(1, s2, dummy);
		v.HighPart = s1;
		v.LowPart = s2;
	}
	else
	{
		GetColumnInfo(0, s1, dummy);
		GetColumnInfo(1, s2, dummy);
		v.HighPart = s1;
		v.LowPart = s2;
	}
	return v.QuadPart;
}
