windows-nt/Source/XPSP1/NT/drivers/ftapi/ftman/fttreevw.cpp
2020-09-26 16:20:57 +08:00

1167 lines
29 KiB
C++

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
FTMan
File Name:
FTTreeVw.cpp
Abstract:
Implementation of the CFTTreeView class. It is a tree view displaying:
- all logical volumes
- all physical partitions that are not logical volumes
existing in the system
Author:
Cristian Teodorescu October 20, 1998
Notes:
Revision History:
--*/
#include "stdafx.h"
#include "Actions.h"
#include "FTDoc.h"
#include "FTListVw.h"
#include "FTTreeVw.h"
#include "Item.h"
#include "LogVol.h"
#include "MainFrm.h"
#include "PhPart.h"
#include "Resource.h"
#include "RootFree.h"
#include "RootVol.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CFTTreeView
IMPLEMENT_DYNCREATE(CFTTreeView, CTreeView)
BEGIN_MESSAGE_MAP(CFTTreeView, CTreeView)
//{{AFX_MSG_MAP(CFTTreeView)
ON_WM_DESTROY()
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding)
ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged)
ON_COMMAND(ID_ITEM_EXPAND, OnItemExpand)
ON_NOTIFY_REFLECT(NM_RCLICK, OnRclick)
ON_COMMAND(ID_VIEW_UP, OnViewUp)
ON_UPDATE_COMMAND_UI(ID_VIEW_UP, OnUpdateViewUp)
ON_COMMAND(ID_ACTION_ASSIGN, OnActionAssign)
ON_UPDATE_COMMAND_UI(ID_ACTION_ASSIGN, OnUpdateActionAssign)
ON_COMMAND(ID_ACTION_FTBREAK, OnActionFtbreak)
ON_UPDATE_COMMAND_UI(ID_ACTION_FTBREAK, OnUpdateActionFtbreak)
ON_COMMAND(ID_ACTION_CREATE_EXTENDED_PARTITION, OnActionCreateExtendedPartition)
ON_UPDATE_COMMAND_UI(ID_ACTION_CREATE_EXTENDED_PARTITION, OnUpdateActionCreateExtendedPartition)
ON_COMMAND(ID_ACTION_CREATE_PARTITION, OnActionCreatePartition)
ON_UPDATE_COMMAND_UI(ID_ACTION_CREATE_PARTITION, OnUpdateActionCreatePartition)
ON_COMMAND(ID_ACTION_DELETE, OnActionDelete)
ON_UPDATE_COMMAND_UI(ID_ACTION_DELETE, OnUpdateActionDelete)
ON_COMMAND(ID_ACTION_FTINIT, OnActionFtinit)
ON_UPDATE_COMMAND_UI(ID_ACTION_FTINIT, OnUpdateActionFtinit)
ON_COMMAND(ID_ACTION_FTSWAP, OnActionFtswap)
ON_UPDATE_COMMAND_UI(ID_ACTION_FTSWAP, OnUpdateActionFtswap)
//}}AFX_MSG_MAP
// Status bar indicators handlers
ON_UPDATE_COMMAND_UI(ID_INDICATOR_NAME, OnUpdateIndicatorName)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_TYPE, OnUpdateIndicatorType)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_DISKS, OnUpdateIndicatorDisks)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_SIZE, OnUpdateIndicatorSize)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_NOTHING, OnUpdateIndicatorNothing)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CFTTreeView construction/destruction
CFTTreeView::CFTTreeView()
{
// TODO: add construction code here
}
CFTTreeView::~CFTTreeView()
{
}
BOOL CFTTreeView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CTreeView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CFTTreeView drawing
void CFTTreeView::OnDraw(CDC* pDC)
{
CFTDocument* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
}
void CFTTreeView::OnInitialUpdate()
{
MY_TRY
CTreeView::OnInitialUpdate();
// Set the "look and style" of the tree control
GetTreeCtrl().ModifyStyle(0, TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_SHOWSELALWAYS );
// Create the image list associated with the tree control
CImageList* pImageList = new CImageList();
// The background color for mask is pink. All image's pixels of this color will take
// the view's background color.
if( pImageList->Create( IDB_IMAGELIST, 16, 15, RGB( 255, 0, 255 ) ) )
GetTreeCtrl().SetImageList(pImageList, TVSIL_NORMAL);
else
AfxMessageBox( IDS_ERR_CREATE_IMAGELIST, MB_ICONSTOP );
// Load the popup menu
m_menuPopup.LoadMenu(IDM_POPUP);
// TODO: You may populate your TreeView with items by directly accessing
// its tree control through a call to GetTreeCtrl().
// I commented this because the first refresh is done on WM_ACTIVATEAPP ( see MainFrm.cpp )
//Refresh();
AfxEnableAutoRefresh(TRUE);
MY_CATCH_AND_REPORT
}
/////////////////////////////////////////////////////////////////////////////
// CFTTreeView diagnostics
#ifdef _DEBUG
void CFTTreeView::AssertValid() const
{
CTreeView::AssertValid();
}
void CFTTreeView::Dump(CDumpContext& dc) const
{
CTreeView::Dump(dc);
}
CFTDocument* CFTTreeView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CFTDocument)));
return (CFTDocument*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// Tree handling methods
HTREEITEM CFTTreeView::InsertItem( CItemData* pData, HTREEITEM hParent, HTREEITEM hInsertAfter )
{
MY_TRY
// Just in case
if( pData == NULL )
return NULL;
if( hParent != NULL )
ASSERT( pData->GetParentData() == (CItemData*)(GetTreeCtrl().GetItemData( hParent ) ) );
else
ASSERT( pData->GetParentData() == NULL );
TV_INSERTSTRUCT tvstruct;
tvstruct.hParent = hParent;
tvstruct.hInsertAfter = hInsertAfter;
tvstruct.item.iImage = tvstruct.item.iSelectedImage = pData->GetImageIndex();
CString strDisplayName;
pData->GetDisplayExtendedName(strDisplayName);
tvstruct.item.pszText = (LPTSTR)(LPCTSTR)strDisplayName;
tvstruct.item.cChildren = pData->GetMembersNumber()>0 ? 1 : 0 ;
tvstruct.item.lParam = (LPARAM)pData;
tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM;
// Insert the item
HTREEITEM hItem = GetTreeCtrl().InsertItem(&tvstruct);
// Update the m_hTreeItem member of pData
pData->SetTreeItem(hItem);
// If the item reports at least one member then a dummy child must be created so it
// can be expanded later
if( pData->GetMembersNumber() > 0 )
{
tvstruct.hParent = hItem;
tvstruct.hInsertAfter = TVI_LAST;
tvstruct.item.iImage = 0;
tvstruct.item.iSelectedImage = 0;
tvstruct.item.pszText = _T("Dummy");
tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
GetTreeCtrl().InsertItem(&tvstruct);
}
return hItem;
MY_CATCH_REPORT_AND_RETURN_NULL
}
//Redisplay the name and the image of the item
BOOL CFTTreeView::RefreshItem( HTREEITEM hItem )
{
MY_TRY
TVITEM tvitem;
tvitem.hItem = hItem;
tvitem.mask = TVIF_PARAM;
if( !GetTreeCtrl().GetItem( &tvitem ) )
return FALSE;
CItemData* pData = (CItemData*)(tvitem.lParam);
CString strDisplayName;
pData->GetDisplayExtendedName(strDisplayName);
tvitem.pszText = (LPTSTR)(LPCTSTR)strDisplayName;
tvitem.iImage = tvitem.iSelectedImage = pData->GetImageIndex();
tvitem.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE ;
return GetTreeCtrl().SetItem( &tvitem );
MY_CATCH_REPORT_AND_RETURN_FALSE
}
BOOL CFTTreeView::AddItemMembers(HTREEITEM hItem)
{
MY_TRY
CAutoRefresh ar(FALSE);
if( !hItem )
return TRUE;
CWaitCursor wc;
// Get the data associated with the item
TVITEM tvItem;
tvItem.hItem = hItem;
tvItem.stateMask = TVIS_SELECTED;
tvItem.mask = TVIF_PARAM | TVIF_STATE ;
if( !GetTreeCtrl().GetItem(&tvItem) )
return FALSE;
CItemData* pData = (CItemData*)tvItem.lParam;
ASSERT(pData);
// If the members are already inserted then return
if( pData->AreMembersInserted() )
return TRUE;
// Delete old subtree but let the root alive
if( !DeleteItemSubtree(hItem, FALSE ) )
return FALSE;
// Get the members of this item
CObArray arrMembers;
CString strErrors;
pData->ReadMembers(arrMembers, strErrors);
if( !strErrors.IsEmpty() )
{
AfxMessageBox( strErrors, MB_ICONSTOP );
wc.Restore();
}
// Add new items to the tree
for( int i=0, nInsertedMembers = 0; i<arrMembers.GetSize(); i++ )
{
CItemData* pMemberData = (CItemData*)(arrMembers[i]);
ASSERT(pMemberData);
if( InsertItem(pMemberData, hItem, TVI_LAST ) )
nInsertedMembers++;
else
{
// Item data was not inserted in the tree so it must be deleted here
delete pMemberData;
}
}
arrMembers.RemoveAll();
// Now the item has its members inserted in the tree
pData->SetAreMembersInserted(TRUE);
ASSERT( tvItem.hItem = hItem );
tvItem.cChildren = nInsertedMembers;
tvItem.mask = TVIF_CHILDREN ;
GetTreeCtrl().SetItem(&tvItem);
// If the item is selected synchronize the list view with the new list of members
if( tvItem.state & TVIS_SELECTED )
{
CMainFrame* pFrame = STATIC_DOWNCAST(CMainFrame, GetParentFrame() );
CFTListView* pListView = pFrame->GetRightPane();
if(pListView)
pListView->SynchronizeMembersWithTree(pData);
}
return TRUE;
MY_CATCH_REPORT_AND_RETURN_FALSE
}
BOOL CFTTreeView::DeleteItemSubtree(HTREEITEM hItem, BOOL bDeleteSubtreeRoot /*=TRUE*/)
{
if( !hItem )
return TRUE;
// Remove all members
HTREEITEM hChild = GetTreeCtrl().GetChildItem(hItem);
while( hChild != NULL )
{
HTREEITEM hTemp = GetTreeCtrl().GetNextSiblingItem(hChild);
DeleteItemSubtree(hChild);
hChild = hTemp;
}
TVITEM tvItem;
tvItem.hItem = hItem;
tvItem.mask = TVIF_PARAM;
if( GetTreeCtrl().GetItem(&tvItem) )
{
CItemData* pItemData = (CItemData*)tvItem.lParam;
if( bDeleteSubtreeRoot )
{
if( pItemData )
delete pItemData;
return GetTreeCtrl().DeleteItem(hItem);
}
else
{
// The members of this item are no more in the tree
if( pItemData )
pItemData->SetAreMembersInserted(FALSE);
return TRUE;
}
}
return FALSE;
}
BOOL CFTTreeView::DeleteAllItems()
{
BOOL bResult = TRUE;
GetTreeCtrl().SelectItem(NULL);
HTREEITEM hItem = GetTreeCtrl().GetRootItem();
while( hItem )
{
HTREEITEM hTemp = GetTreeCtrl().GetNextSiblingItem( hItem );
bResult = DeleteItemSubtree(hItem) && bResult;
hItem = hTemp;
}
return bResult;
}
// Adds the snapshot of a subtree ( expanded items, selected items ) to the given snapshot
void CFTTreeView::AddSubtreeSnapshot( HTREEITEM hSubtreeRoot, TREE_SNAPSHOT& snapshot )
{
MY_TRY
if( hSubtreeRoot == NULL )
return;
UINT unItemState = GetTreeCtrl().GetItemState( hSubtreeRoot, TVIS_EXPANDED | TVIS_SELECTED );
if( !unItemState )
return;
CItemData* pData = (CItemData*)(GetTreeCtrl().GetItemData(hSubtreeRoot));
ASSERT(pData);
CItemID idItem( *pData );
if( unItemState & TVIS_EXPANDED )
snapshot.setExpandedItems.Add(idItem);
if( unItemState & TVIS_SELECTED )
snapshot.setSelectedItems.Add(idItem);
HTREEITEM hChildItem = GetTreeCtrl().GetChildItem(hSubtreeRoot);
while( hChildItem != NULL )
{
AddSubtreeSnapshot( hChildItem, snapshot );
hChildItem = GetTreeCtrl().GetNextSiblingItem(hChildItem);
}
MY_CATCH_AND_REPORT
}
// Get the snapshot of the tree ( expanded items, selected items )
void CFTTreeView::GetSnapshot( TREE_SNAPSHOT& snapshot )
{
MY_TRY
CWaitCursor wc;
snapshot.setExpandedItems.RemoveAll();
snapshot.setSelectedItems.RemoveAll();
HTREEITEM hItem = GetTreeCtrl().GetRootItem();
while( hItem )
{
AddSubtreeSnapshot( hItem, snapshot );
hItem = GetTreeCtrl().GetNextSiblingItem( hItem );
}
MY_CATCH_AND_REPORT
}
// Refresh the content of a subtree according with a certain snapshot ( expanded items, selected items )
BOOL CFTTreeView::RefreshSubtree( HTREEITEM hSubtreeRoot, TREE_SNAPSHOT& snapshot )
{
MY_TRY
BOOL bResult = TRUE;
CItemData* pData = (CItemData*)(GetTreeCtrl().GetItemData( hSubtreeRoot));
ASSERT(pData);
CItemID idItem( *pData );
if( snapshot.setExpandedItems.InSet( idItem ) )
{
GetTreeCtrl().Expand( hSubtreeRoot, TVE_EXPAND );
HTREEITEM hChildItem =GetTreeCtrl().GetChildItem(hSubtreeRoot);
while( hChildItem != NULL )
{
bResult = RefreshSubtree( hChildItem, snapshot );
hChildItem = GetTreeCtrl().GetNextSiblingItem(hChildItem);
}
}
if( snapshot.setSelectedItems.InSet( idItem ) )
GetTreeCtrl().SelectItem( hSubtreeRoot );
return bResult;
MY_CATCH_REPORT_AND_RETURN_FALSE
}
// Refresh the content of the tree view according to a certain snapshot ( expanded items, selected items )
BOOL CFTTreeView::Refresh( TREE_SNAPSHOT& snapshot)
{
MY_TRY
CAutoRefresh ar(FALSE);
LockWindowUpdate();
DeleteAllItems();
// 1. Add the volumes root item and expand it is necessary
// Create a volumes root item ...
CRootVolumesData* pRootVolData = new CRootVolumesData;
// Read its data ...
CString strErrors;
pRootVolData->ReadItemInfo(strErrors);
if( !strErrors.IsEmpty() )
AfxMessageBox( strErrors, MB_ICONSTOP );
// Add it to the tree
HTREEITEM hRoot = InsertItem( pRootVolData, NULL, TVI_FIRST );
if( !hRoot )
{
delete pRootVolData;
UnlockWindowUpdate();
return FALSE;
}
// Refresh its subtree
BOOL bResult = RefreshSubtree( hRoot, snapshot );
// 2. Add the free spaces root item and expand it if necessary
// Create a free spaces root item ...
CRootFreeSpacesData* pRootFreeData = new CRootFreeSpacesData;
// Read its data ...
pRootFreeData->ReadItemInfo(strErrors);
if( !strErrors.IsEmpty() )
AfxMessageBox( strErrors, MB_ICONSTOP );
// Add it to the tree
hRoot = InsertItem( pRootFreeData, NULL, TVI_LAST );
if( !hRoot )
{
DeleteAllItems();
delete pRootFreeData;
UnlockWindowUpdate();
return FALSE;
}
// Refresh its subtree
bResult = RefreshSubtree( hRoot, snapshot ) && bResult;
UnlockWindowUpdate();
return bResult;
MY_CATCH_REPORT_AND_RETURN_FALSE
}
// Refresh the content of the tree view.
BOOL CFTTreeView::Refresh()
{
CWaitCursor wc;
CAutoRefresh ar(FALSE);
TREE_SNAPSHOT snapshot;
GetSnapshot(snapshot);
return Refresh(snapshot);
}
// Performs some minor refreshment for the tree items. This should be called every TIMER_ELAPSE milliseconds.
void CFTTreeView::RefreshOnTimer()
{
MY_TRY
CAutoRefresh( FALSE );
HTREEITEM hRootItem = GetTreeCtrl().GetRootItem();
if( hRootItem == NULL )
return;
CObArray arrRefreshedItems;
CWordArray arrRefreshFlags;
ScanSubtreeOnTimer( hRootItem, arrRefreshedItems, arrRefreshFlags);
if( arrRefreshedItems.GetSize() > 0 )
{
CWaitCursor wc;
DisplayStatusBarMessage( IDS_STATUS_REFRESH );
TRACE(_T("OnTimerRefresh: Some items under surveillance need some refreshment\n"));
CObArray arrMount;
for( int i = 0; i < arrRefreshedItems.GetSize(); i++ )
{
if( arrRefreshFlags[i] & ROTT_GotDriveLetterAndVolumeName )
arrMount.Add( arrRefreshedItems[i] );
}
if( arrMount.GetSize() > 0 )
QueryMountList( arrMount );
HTREEITEM hSelectedItem = GetTreeCtrl().GetSelectedItem();
CMainFrame* pFrame = STATIC_DOWNCAST(CMainFrame, GetParentFrame() );
CFTListView* pListView = pFrame->GetRightPane();
for( i = 0; i < arrRefreshedItems.GetSize(); i++ )
{
// Refresh every item in this array
CItemData* pData = (CItemData*)(arrRefreshedItems[i]);
pData->SetImageIndex( pData->ComputeImageIndex() );
RefreshItem( pData->GetTreeItem() );
// If the item is also displayed in the list view ( equivalent with its parent being selected in
// the tree view ) then refresh it there too
if( ( pData->GetParentData() ) &&
( pData->GetParentData()->GetTreeItem() == hSelectedItem ) &&
pListView )
pListView->RefreshItem( pData->GetListItem() );
}
DisplayStatusBarMessage( AFX_IDS_IDLEMESSAGE );
}
MY_CATCH_AND_REPORT
}
// Scans a subtree for:
// 1. Initializing stripe sets with parity that are not initializing anymore
// 2. Regenerating mirror sets or stripe sets with parity that are not regenerating anymore
// 3. Root volumes whose drive letter and volume name were eventually found
void CFTTreeView::ScanSubtreeOnTimer( HTREEITEM hSubtreeRoot, CObArray& arrRefreshedItems, CWordArray& arrRefreshFlags)
{
MY_TRY
ASSERT( hSubtreeRoot );
CItemData* pData = (CItemData*)(GetTreeCtrl().GetItemData( hSubtreeRoot ) );
ASSERT( pData );
ASSERT( pData->GetTreeItem() == hSubtreeRoot );
WORD wRefreshFlags = 0;
// Check if the drive letter and volume name are OK
if( pData->IsRootVolume() && pData->GetVolumeName().IsEmpty() )
{
if( pData->ReadDriveLetterAndVolumeName() )
{
pData->SetValid(TRUE);
wRefreshFlags |= ROTT_GotDriveLetterAndVolumeName;
}
}
// Check for mirror sets and stripe sets with parity who just ended the regeneration of a member
// Check also for stripe sets with parity who just ended their initialization
if( pData->IsValid() && ( pData->GetItemType() == IT_LogicalVolume ) )
{
CLogicalVolumeData* pLogVolData = ( CLogicalVolumeData*)pData;
if( ( pLogVolData->m_nVolType == FtMirrorSet ) ||
( pLogVolData->m_nVolType == FtStripeSetWithParity ) )
{
if( pLogVolData->m_StateInfo.stripeState.UnhealthyMemberState == FtMemberRegenerating )
{
CString strErrors;
if( pLogVolData->ReadFTInfo( strErrors ) &&
pLogVolData->m_StateInfo.stripeState.UnhealthyMemberState != FtMemberRegenerating )
wRefreshFlags |= ROTT_EndRegeneration;
}
}
if( pLogVolData->m_nVolType == FtStripeSetWithParity )
{
if( pLogVolData->m_StateInfo.stripeState.IsInitializing )
{
CString strErrors;
if( pLogVolData->ReadFTInfo( strErrors ) &&
!pLogVolData->m_StateInfo.stripeState.IsInitializing )
wRefreshFlags |= ROTT_EndInitialization;
}
}
}
if( wRefreshFlags )
{
arrRefreshedItems.Add(pData);
arrRefreshFlags.Add(wRefreshFlags);
}
// Now check the item's subtree
if( !pData->AreMembersInserted() )
return;
HTREEITEM hChildItem = GetTreeCtrl().GetChildItem(hSubtreeRoot);
USHORT unMember = 0;
while( hChildItem != NULL )
{
if( wRefreshFlags & ( ROTT_EndRegeneration | ROTT_EndInitialization ) )
{
// Add all children to the refresh list
CItemData* pChildData = (CItemData*)(GetTreeCtrl().GetItemData( hChildItem ) );
ASSERT( pData );
if( unMember == ((CLogicalVolumeData*)pData)->m_StateInfo.stripeState.UnhealthyMemberNumber )
pChildData->SetMemberStatus( ((CLogicalVolumeData*)pData)->m_StateInfo.stripeState.UnhealthyMemberState );
else
pChildData->SetMemberStatus( FtMemberHealthy );
arrRefreshedItems.Add( pChildData );
arrRefreshFlags.Add( wRefreshFlags );
}
ScanSubtreeOnTimer(hChildItem, arrRefreshedItems, arrRefreshFlags );
hChildItem = GetTreeCtrl().GetNextSiblingItem(hChildItem);
unMember++;
}
MY_CATCH
}
void CFTTreeView::GetSelectedItems( CObArray& arrSelectedItems )
{
MY_TRY
arrSelectedItems.RemoveAll();
HTREEITEM hItem = GetTreeCtrl().GetSelectedItem();
if( hItem != NULL )
{
CItemData* pData = (CItemData*)( GetTreeCtrl().GetItemData(hItem) );
ASSERT(pData);
arrSelectedItems.Add(pData);
}
MY_CATCH_AND_REPORT
}
/////////////////////////////////////////////////////////////////////////////
// CFTTreeView message handlers
void CFTTreeView::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
*pResult = 0;
HTREEITEM hItem = pNMTreeView->itemNew.hItem;
ASSERT( hItem );
CItemData* pData = (CItemData*)(pNMTreeView->itemNew.lParam);
// When collapsing change the image for root items ( closed folder )
if( pNMTreeView->action == TVE_COLLAPSE )
{
if( pData &&
( ( pData->GetItemType() == IT_RootVolumes ) ||
( pData->GetItemType() == IT_RootFreeSpaces ) ) )
{
pData->SetImageIndex( II_Root );
GetTreeCtrl().SetItemImage( pNMTreeView->itemNew.hItem, II_Root, II_Root );
}
return;
}
// We are handling only the expandation of the item
if( pNMTreeView->action == TVE_EXPAND )
{
// When expanding change the image for root items ( open folder )
if( pData &&
( ( pData->GetItemType() == IT_RootVolumes ) ||
( pData->GetItemType() == IT_RootFreeSpaces ) ) )
{
pData->SetImageIndex( II_RootExpanded );
GetTreeCtrl().SetItemImage( pNMTreeView->itemNew.hItem, II_RootExpanded, II_RootExpanded );
}
// Now it's time to prepare the item for expandation
// Add all members of the item to the tree ( if they are already added to the tree AddItemMembers will return
// without doing anything
AddItemMembers(hItem);
}
}
void CFTTreeView::OnDestroy()
{
GetTreeCtrl().SelectItem( NULL );
// Delete all items and destroy their associated data
DeleteAllItems();
// Delete the image list
CImageList* pImageList = GetTreeCtrl().GetImageList(TVSIL_NORMAL);
if( pImageList )
{
pImageList->DeleteImageList();
delete pImageList;
}
// Destroy the popup menu
m_menuPopup.DestroyMenu();
CTreeView::OnDestroy();
// TODO: Add your message handler code here
}
void CFTTreeView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
*pResult = 0;
CItemData* pNewData = (CItemData*)(pNMTreeView->itemNew.lParam);
// Add all members of the item to the tree ( if they are already added to the tree AddItemMembers will return
// without doing anything
if( pNewData )
AddItemMembers( pNewData->GetTreeItem() );
CMainFrame* pFrame = STATIC_DOWNCAST(CMainFrame, GetParentFrame() );
CFTListView* pListView = pFrame->GetRightPane();
if(pListView)
pListView->SynchronizeMembersWithTree(pNewData);
}
void CFTTreeView::OnItemExpand()
{
// TODO: Add your command handler code here
HTREEITEM hItem = GetTreeCtrl().GetSelectedItem( );
if( !hItem)
return;
// Now reset the ExpandedOnce flag so the tree will receive OnItemExpanding notification
TVITEM tvItem;
tvItem.hItem = hItem;
tvItem.state = 0;
tvItem.stateMask = TVIS_EXPANDEDONCE;
tvItem.mask = TVIF_STATE;
GetTreeCtrl().SetItem(&tvItem);
// Now toggle the status of the item ( expanded / collapsed )
GetTreeCtrl().Expand( hItem, TVE_TOGGLE );
}
void CFTTreeView::OnRclick(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
POINT pt;
GetCursorPos( &pt );
TVHITTESTINFO tvhittestinfo;
tvhittestinfo.pt = pt;
ScreenToClient( &(tvhittestinfo.pt) );
GetTreeCtrl().HitTest( &tvhittestinfo );
if( ( tvhittestinfo.hItem != NULL ) && ( tvhittestinfo.flags & TVHT_ONITEM ) )
{
GetTreeCtrl().SelectItem( tvhittestinfo.hItem );
CMenu* pPopup = m_menuPopup.GetSubMenu(0);
if( pPopup != NULL )
pPopup->TrackPopupMenu( TPM_LEFTALIGN, pt.x, pt.y, AfxGetMainWnd(), NULL);
}
}
void CFTTreeView::OnViewUp()
{
HTREEITEM hItem = GetTreeCtrl().GetSelectedItem();
ASSERT( hItem );
HTREEITEM hParentItem = GetTreeCtrl().GetParentItem( hItem );
ASSERT( hParentItem );
GetTreeCtrl().SelectItem( hParentItem );
}
void CFTTreeView::OnUpdateViewUp(CCmdUI* pCmdUI)
{
HTREEITEM hItem = GetTreeCtrl().GetSelectedItem();
if( hItem == NULL )
goto label_disable;
if( GetTreeCtrl().GetParentItem( hItem ) == NULL )
goto label_disable;
pCmdUI->Enable(TRUE);
return;
label_disable:
pCmdUI->Enable(FALSE);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// FT Actions
void CFTTreeView::OnActionAssign()
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
ActionAssign( arrSelectedItems );
}
void CFTTreeView::OnUpdateActionAssign(CCmdUI* pCmdUI)
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
UpdateActionAssign( pCmdUI, arrSelectedItems );
}
void CFTTreeView::OnActionFtbreak()
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
ActionFtbreak( arrSelectedItems );
}
void CFTTreeView::OnUpdateActionFtbreak(CCmdUI* pCmdUI)
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
UpdateActionFtbreak( pCmdUI, arrSelectedItems );
}
void CFTTreeView::OnActionCreateExtendedPartition()
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
ActionCreateExtendedPartition( arrSelectedItems );
}
void CFTTreeView::OnUpdateActionCreateExtendedPartition(CCmdUI* pCmdUI)
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
UpdateActionCreateExtendedPartition( pCmdUI, arrSelectedItems );
}
void CFTTreeView::OnActionCreatePartition()
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
ActionCreatePartition( arrSelectedItems );
}
void CFTTreeView::OnUpdateActionCreatePartition(CCmdUI* pCmdUI)
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
UpdateActionCreatePartition( pCmdUI, arrSelectedItems );
}
void CFTTreeView::OnActionDelete()
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
ActionDelete( arrSelectedItems );
}
void CFTTreeView::OnUpdateActionDelete(CCmdUI* pCmdUI)
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
UpdateActionDelete( pCmdUI, arrSelectedItems );
}
void CFTTreeView::OnActionFtinit()
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
ActionFtinit( arrSelectedItems );
}
void CFTTreeView::OnUpdateActionFtinit(CCmdUI* pCmdUI)
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
UpdateActionFtinit( pCmdUI, arrSelectedItems );
}
void CFTTreeView::OnActionFtswap()
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
ActionFtswap( arrSelectedItems );
}
void CFTTreeView::OnUpdateActionFtswap(CCmdUI* pCmdUI)
{
CObArray arrSelectedItems;
GetSelectedItems( arrSelectedItems );
UpdateActionFtswap( pCmdUI, arrSelectedItems );
}
void CFTTreeView::OnUpdateIndicatorName(CCmdUI* pCmdUI)
{
MY_TRY
pCmdUI->Enable(TRUE);
HTREEITEM hItem = GetTreeCtrl().GetSelectedItem();
if( hItem == NULL )
{
pCmdUI->SetText( _T("") );
return;
}
CItemData* pData = (CItemData*)(GetTreeCtrl().GetItemData(hItem));
ASSERT( pData );
CString str;
pData->GetDisplayName( str );
pCmdUI->SetText( str );
MY_CATCH
}
void CFTTreeView::OnUpdateIndicatorType(CCmdUI* pCmdUI)
{
MY_TRY
pCmdUI->Enable(TRUE);
HTREEITEM hItem = GetTreeCtrl().GetSelectedItem();
if( hItem == NULL )
{
pCmdUI->SetText( _T("") );
return;
}
CItemData* pData = (CItemData*)(GetTreeCtrl().GetItemData(hItem));
ASSERT( pData );
CString str;
pData->GetDisplayType( str );
pCmdUI->SetText( str );
MY_CATCH
}
void CFTTreeView::OnUpdateIndicatorDisks(CCmdUI* pCmdUI)
{
MY_TRY
pCmdUI->Enable(TRUE);
HTREEITEM hItem = GetTreeCtrl().GetSelectedItem();
if( hItem == NULL )
{
pCmdUI->SetText( _T("") );
return;
}
CItemData* pData = (CItemData*)(GetTreeCtrl().GetItemData(hItem));
ASSERT( pData );
CString str;
pData->GetDisplayDisksSet( str );
pCmdUI->SetText( str );
MY_CATCH
}
void CFTTreeView::OnUpdateIndicatorSize(CCmdUI* pCmdUI)
{
MY_TRY
pCmdUI->Enable(TRUE);
HTREEITEM hItem = GetTreeCtrl().GetSelectedItem();
if( hItem == NULL )
{
pCmdUI->SetText( _T("") );
return;
}
CItemData* pData = (CItemData*)(GetTreeCtrl().GetItemData(hItem));
ASSERT( pData );
CString str;
pData->GetDisplaySize( str );
pCmdUI->SetText( str );
MY_CATCH
}
void CFTTreeView::OnUpdateIndicatorNothing(CCmdUI* pCmdUI)
{
MY_TRY
pCmdUI->Enable(TRUE);
pCmdUI->SetText( _T("") );
MY_CATCH
}
/////////////////////////////////////////////////////////////////////////////
// Friend methods
/*
Global function: GetVolumeReplacementForbiddenDisksSet
Purpose: Retrieves the set of disks whose volumes cannot be used as replacements for a
certain volume in a logical volume set ( stripe, mirror, stripe with parity, volume set )
The function uses the volume hierarchy of the ( left pane )tree view
Parameters: [IN] CFTTreeView* pTreeView
Pointer to the tree view containing the volume hierarchy
[IN] CItemData* pVolumeData
Pointer to the volume data
[OUT] CULONSet& setDisks
The forbidden disks set
Return value: -
*/
void GetVolumeReplacementForbiddenDisksSet( CFTTreeView* pTreeView, CItemData* pVolumeData, CULONGSet& setDisks )
{
MY_TRY
ASSERT( pTreeView );
ASSERT( pVolumeData );
setDisks.RemoveAll();
HTREEITEM hVolumeItem = pVolumeData->GetTreeItem();
ASSERT( hVolumeItem );
HTREEITEM hParentItem = pTreeView->GetTreeCtrl().GetParentItem( hVolumeItem );
// If our item has no parent then return an empty disks set
if( hParentItem == NULL )
return;
CItemData* pParentData = (CItemData*)(pTreeView->GetTreeCtrl().GetItemData( hParentItem ));
// hParentItem should be a valid item of the tree
ASSERT( pParentData );
// If the parent is not a logical volume return an empty disks set
if( pParentData->GetItemType() != IT_LogicalVolume )
return;
// If the parent is a single FT partition return an empty disks set
if( ((CLogicalVolumeData*)pParentData)->m_nVolType == FtPartition )
return;
// Now get the forbidden disks set of our item's parent
GetVolumeReplacementForbiddenDisksSet( pTreeView, pParentData, setDisks );
// Now add to the forbidden disks set the reunion of all our item's siblings disks sets
// Attention: Only when the parent is a stripe, mirror or stripe set with parity! Not for volume sets!!
if( ( ((CLogicalVolumeData*)pParentData)->m_nVolType == FtStripeSet ) ||
( ((CLogicalVolumeData*)pParentData)->m_nVolType == FtMirrorSet ) ||
( ((CLogicalVolumeData*)pParentData)->m_nVolType == FtStripeSetWithParity ) )
{
HTREEITEM hSiblingItem =pTreeView->GetTreeCtrl().GetChildItem(hParentItem);
while( hSiblingItem != NULL )
{
if( hSiblingItem != hVolumeItem )
{
CItemData* pSiblingData = (CItemData*)(pTreeView->GetTreeCtrl().GetItemData(hSiblingItem));
ASSERT(pSiblingData);
setDisks += pSiblingData->GetDisksSet();
}
hSiblingItem = pTreeView->GetTreeCtrl().GetNextSiblingItem(hSiblingItem);
}
}
MY_CATCH_AND_THROW
}