/*++

Copyright (c) 1998 Microsoft Corporation

Module Name:

    FTMan

File Name:

	MainFrm.cpp

Abstract:

    Implementation of the CMainFrame class. It is the MFC main frame class for this application

Author:

    Cristian Teodorescu      October 20, 1998

Notes:

Revision History:

--*/

#include "stdafx.h"

#include "Item.h"
#include "FTListVw.h"
#include "FTTreeVw.h"
#include "MainFrm.h"
#include "Resource.h"

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

// Range limits for OnViewStyle and OnUpdateViewStyle
#define AFX_ID_VIEW_MINIMUM				ID_VIEW_SMALLICON
#define AFX_ID_VIEW_MAXIMUM				ID_VIEW_BYNAME

/////////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	//{{AFX_MSG_MAP(CMainFrame)
	ON_WM_CREATE()
	ON_COMMAND(ID_VIEW_TOGGLE, OnViewToggle)
	ON_COMMAND(ID_VIEW_REFRESH, OnViewRefresh)
	ON_WM_ACTIVATEAPP()
	ON_WM_DESTROY()
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
	ON_UPDATE_COMMAND_UI_RANGE(AFX_ID_VIEW_MINIMUM, AFX_ID_VIEW_MAXIMUM, OnUpdateViewStyles)
	ON_COMMAND_RANGE(AFX_ID_VIEW_MINIMUM, AFX_ID_VIEW_MAXIMUM, OnViewStyle)
END_MESSAGE_MAP()

static UINT indicators[] =
{
	ID_INDICATOR_NAME,		// Name of the tree selected item
	ID_INDICATOR_TYPE,		// Type of the tree selected item
	ID_INDICATOR_DISKS,		// Disks set of the tree selected item
	ID_INDICATOR_SIZE,		// Size of the tree selected item
	ID_INDICATOR_NOTHING,	// Just an empty indicator to let some space between the first 4 indicators and the last one
	ID_SEPARATOR,           // status line indicator
};

/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame() : m_bEnableAutoRefresh(FALSE), m_bAutoRefreshRequested(FALSE), m_unTimer(0)
{
	// TODO: add member initialization code here
	
}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	/*
	if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
		| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
	*/
	if (!m_wndToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP
		| CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
		!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
	{
		TRACE0(_T("Failed to create toolbar\n"));
		return -1;      // fail to create
	}
	m_wndToolBar.GetToolBarCtrl().ModifyStyle( 0, TBSTYLE_FLAT );

	if (!m_wndStatusBar.Create(this) ||
		!m_wndStatusBar.SetIndicators(indicators,
		  sizeof(indicators)/sizeof(UINT)))
	{
		TRACE0(_T("Failed to create status bar\n"));
		return -1;      // fail to create
	}
	// Try to compute the optimal size for the status bar indicators ( depending on the application font )

	int nAveCharWidth = 0;
	CDC* pDC = m_wndStatusBar.GetDC();
	if( pDC )
	{
		TEXTMETRIC textmetrics;
		if( pDC->GetTextMetrics(&textmetrics) )
			nAveCharWidth = textmetrics.tmAveCharWidth;
		m_wndStatusBar.ReleaseDC( pDC );
	}

	m_wndStatusBar.SetPaneInfo( 0, m_wndStatusBar.GetItemID(0), 0, nAveCharWidth ? (16 * nAveCharWidth) : 130 );
	m_wndStatusBar.SetPaneInfo( 1, m_wndStatusBar.GetItemID(1), 0, nAveCharWidth ? (22 * nAveCharWidth) : 150 );
	m_wndStatusBar.SetPaneInfo( 2, m_wndStatusBar.GetItemID(2), 0, nAveCharWidth ? (8 * nAveCharWidth) : 50 );
	m_wndStatusBar.SetPaneInfo( 3, m_wndStatusBar.GetItemID(3), 0, nAveCharWidth ? (8 * nAveCharWidth) : 60 );
	m_wndStatusBar.SetPaneInfo( 4, m_wndStatusBar.GetItemID(4), SBPS_NOBORDERS, nAveCharWidth ? (2 * nAveCharWidth) : 15 );
	m_wndStatusBar.SetPaneStyle( 5, SBPS_NOBORDERS | SBPS_STRETCH ); 

	// TODO: Delete these three lines if you don't want the toolbar to
	//  be dockable
	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_wndToolBar);

	return 0;
}

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
	CCreateContext* pContext)
{
	// create splitter window
	if (!m_wndSplitter.CreateStatic(this, 1, 2))
		return FALSE;

	if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CFTTreeView), CSize(200, 100), pContext) ||
		!m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CFTListView), CSize(100, 100), pContext))
	{
		m_wndSplitter.DestroyWindow();
		return FALSE;
	}

	// Create timer
	m_unTimer = SetTimer( ID_TIMER_EVENT, TIMER_ELAPSE, NULL );
	if( !m_unTimer )
		TRACE( _T("Failure installing timer\n") );

	return TRUE;
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return TRUE;
}

/*
Public method:		RefreshAll

Purpose:			Refreshes the content of both views by keeping ( when possible ) the expanded and 
					selected items.
					It is also possible to change the selected items and to add some expanded items
					
Parameters:			[IN] CItemIDSet *psetAddTreeExpandedItems
						Items to add to the currently expanded items set
					[IN] CItemIDSet *psetTreeSelectedItems 
						Items to select in the tree ( the currently selected items are neglected )
					[IN] CItemIDSet *psetListSelectedItems 
						Items to select in the list ( the currently selected items are neglected )

Return value:		TRUE if the refresh operation succeeded
*/

BOOL CMainFrame::RefreshAll( 
					CItemIDSet* psetAddTreeExpandedItems	/* =NULL */ ,
					CItemIDSet* psetTreeSelectedItems		/* =NULL */,
					CItemIDSet* psetListSelectedItems		/* =NULL */  )
{
	CWaitCursor		wc;
	TREE_SNAPSHOT	snapshotTree;
	LIST_SNAPSHOT	snapshotList;
	BOOL			bResult;
	CAutoRefresh	ar(FALSE);
	
	DisplayStatusBarMessage( IDS_STATUS_REFRESH );
	
	CFTTreeView* pTreeView = GetLeftPane();
	CFTListView* pListView = GetRightPane();

	// Get the snapshots of the tree and list views and do some changes ( if needed )
	pTreeView->GetSnapshot(snapshotTree);
	if( psetAddTreeExpandedItems )
		snapshotTree.setExpandedItems += *psetAddTreeExpandedItems;
	if( psetTreeSelectedItems )
		snapshotTree.setSelectedItems = *psetTreeSelectedItems;

	pListView->GetSnapshot( snapshotList );
	if( psetListSelectedItems )
		snapshotList.setSelectedItems = *psetListSelectedItems;

	// Refresh the tree view
	bResult =  pTreeView->Refresh(snapshotTree);

	// Set the snapshot pf the list view
	pListView->SetSnapshot( snapshotList );

	// All previous requests for auto refresh should be neglected now
	m_bAutoRefreshRequested = FALSE;

	DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE );	
	return bResult;
}


/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
	CFrameWnd::AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
	CFrameWnd::Dump(dc);
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers

CFTTreeView* CMainFrame::GetLeftPane()
{
	CWnd* pWnd = m_wndSplitter.GetPane(0, 0);
	CFTTreeView* pView = DYNAMIC_DOWNCAST(CFTTreeView, pWnd);
	return pView;
}

CFTListView* CMainFrame::GetRightPane()
{
	CWnd* pWnd = m_wndSplitter.GetPane(0, 1);
	CFTListView* pView = DYNAMIC_DOWNCAST(CFTListView, pWnd);
	return pView;
}

void CMainFrame::OnUpdateViewStyles(CCmdUI* pCmdUI)
{
	// TODO: customize or extend this code to handle choices on the
	// View menu.

	CFTListView* pView = GetRightPane(); 	

	// if the right-hand pane hasn't been created or isn't a view,
	// disable commands in our range

	if (pView == NULL)
		pCmdUI->Enable(FALSE);
	else
	{
		DWORD dwStyle = pView->GetStyle() & LVS_TYPEMASK;

		// if the command is ID_VIEW_LINEUP, only enable command
		// when we're in LVS_ICON or LVS_SMALLICON mode

		if (pCmdUI->m_nID == ID_VIEW_LINEUP)
		{
			if (dwStyle == LVS_ICON || dwStyle == LVS_SMALLICON)
				pCmdUI->Enable();
			else
				pCmdUI->Enable(FALSE);
		}
		else
		{
			// otherwise, use dots to reflect the style of the view
			pCmdUI->Enable();
			BOOL bChecked = FALSE;

			switch (pCmdUI->m_nID)
			{
			case ID_VIEW_DETAILS:
				bChecked = (dwStyle == LVS_REPORT);
				break;

			case ID_VIEW_SMALLICON:
				bChecked = (dwStyle == LVS_SMALLICON);
				break;

			case ID_VIEW_LARGEICON:
				bChecked = (dwStyle == LVS_ICON);
				break;

			case ID_VIEW_LIST:
				bChecked = (dwStyle == LVS_LIST);
				break;

			default:
				bChecked = FALSE;
				break;
			}

			pCmdUI->SetRadio(bChecked ? 1 : 0);
		}
	}
}


void CMainFrame::OnViewStyle(UINT nCommandID)
{
	// TODO: customize or extend this code to handle choices on the
	// View menu.
	CFTListView* pView = GetRightPane();

	// if the right-hand pane has been created and is a CFTListView,
	// process the menu commands...
	if (pView != NULL)
	{
		DWORD dwStyle = -1;
		BOOL  bExtendedNames = TRUE;

		switch (nCommandID)
		{
		case ID_VIEW_LINEUP:
			{
				// ask the list control to snap to grid
				CListCtrl& refListCtrl = pView->GetListCtrl();
				refListCtrl.Arrange(LVA_SNAPTOGRID);
			}
			break;

		// other commands change the style on the list control
		case ID_VIEW_DETAILS:
			dwStyle = LVS_REPORT;
			bExtendedNames = FALSE;
			break;

		case ID_VIEW_SMALLICON:
			dwStyle = LVS_SMALLICON;
			break;

		case ID_VIEW_LARGEICON:
			dwStyle = LVS_ICON;
			break;

		case ID_VIEW_LIST:
			dwStyle = LVS_LIST;
			break;
		}

		// change the style; window will repaint automatically
		if (dwStyle != -1)
		{
			pView->ModifyStyle(LVS_TYPEMASK, dwStyle);
			pView->DisplayItemsExtendedNames( bExtendedNames );
		}
	}
}

void CMainFrame::OnViewToggle() 
{
	// TODO: Add your command handler code here
	int nRow,nCol;
	if( !m_wndSplitter.GetActivePane(&nRow,&nCol) )
		return;
	ASSERT( nRow == 0 );
	ASSERT( nCol <= 1 );
	m_wndSplitter.SetActivePane(0,1-nCol, NULL);	
}

void CMainFrame::OnViewRefresh() 
{
	RefreshAll( NULL, NULL, NULL );	
}

void CMainFrame::OnActivateApp(BOOL bActive, HTASK hTask) 
{
	CFrameWnd::OnActivateApp(bActive, hTask);
	
	// TODO: Add your message handler code here

	if( bActive )
	{
		if( m_bEnableAutoRefresh )
		{
			TRACE( _T("OnActivateApp AutoRefresh\n") );
			RefreshAll();
		}
		else
			m_bAutoRefreshRequested = TRUE;
	}
	
}

void CMainFrame::OnDestroy() 
{
	if( m_unTimer )
		KillTimer( m_unTimer );

	CFrameWnd::OnDestroy();
	
	// TODO: Add your message handler code here
	
}

void CMainFrame::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	if( ( nIDEvent == ID_TIMER_EVENT ) && m_bEnableAutoRefresh && ( GetActiveWindow() == this ) )
	{
		//TRACE("OnTimer\n");
		CFTTreeView* pTreeView = GetLeftPane();
		pTreeView->RefreshOnTimer();		
	}
	
	CFrameWnd::OnTimer(nIDEvent);
}

/////////////////////////////////////////////////////////////////////////////////////////////
// Global functions related to the main frame

/*
Global function:	RefreshAll

Purpose:			Refreshes the content of both views of the mainframe by keeping ( when possible ) 
					the expanded and selected items.
					It is also possible to change the selected items and to add some expanded items
					
Parameters:			[IN] CItemIDSet *psetAddTreeExpandedItems
						Items to add to the currently expanded items set
					[IN] CItemIDSet *psetTreeSelectedItems 
						Items to select in the tree ( the currently selected items are neglected )
					[IN] CItemIDSet *psetListSelectedItems 
						Items to select in the list ( the currently selected items are neglected )

Return value:		TRUE if the refresh operation succeeded
*/

BOOL AfxRefreshAll(
					CItemIDSet* psetAddTreeExpandedItems	/* =NULL */ ,
					CItemIDSet* psetTreeSelectedItems		/* =NULL */,
					CItemIDSet* psetListSelectedItems		/* =NULL */ )
{
	CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
	ASSERT(pFrame->IsKindOf(RUNTIME_CLASS(CMainFrame)));

	return pFrame->RefreshAll( psetAddTreeExpandedItems, psetTreeSelectedItems, psetListSelectedItems );
}

/*
Global function:	AfxEnableAutoRefresh()

Purpose:			Enables / Disables the auto refresh of both views of the mainframe on WM_ACTIVATEAPP
					
Parameters:			[IN] BOOL bEnable
						Specifies whether the auto-refresh is to be enabled or disabled
					

Return value:		The previous status of the auto-refresh flag
*/

BOOL AfxEnableAutoRefresh( BOOL bEnable /* =TRUE */ )
{
	CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
	ASSERT(pFrame->IsKindOf(RUNTIME_CLASS(CMainFrame)));

	BOOL bPrevious = pFrame->m_bEnableAutoRefresh;
	
	// If the flag is enabling and an auto-refresh request came while it was disabled then proceed with auto-refresh
	if( bEnable && !bPrevious && pFrame->m_bAutoRefreshRequested)
	{
		TRACE( _T("Late OnActivateApp AutoRefresh\n") );
		AfxRefreshAll();
	}
	else if ( !bEnable && bPrevious )
		pFrame->m_bAutoRefreshRequested = FALSE;

	pFrame->m_bEnableAutoRefresh = bEnable;

	return bPrevious;
}


BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) 
{
	// TODO: Add your specialized code here and/or call the base class
	switch( nID )
	{
		case ID_VIEW_UP:
		case ID_INDICATOR_NAME:
		case ID_INDICATOR_TYPE:
		case ID_INDICATOR_DISKS:
		case ID_INDICATOR_SIZE:
		case ID_INDICATOR_NOTHING:
			CFTTreeView* pTreeView = GetLeftPane();
			return pTreeView->OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );

	}
	
	return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}