/*++ 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; iSetAreMembersInserted(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 }