Skip to content

Instantly share code, notes, and snippets.

@an01f01
Created June 9, 2021 21:34
Show Gist options
  • Save an01f01/d2143cd6061f96c8ae128abb343a3668 to your computer and use it in GitHub Desktop.
Save an01f01/d2143cd6061f96c8ae128abb343a3668 to your computer and use it in GitHub Desktop.
#include "mfc_multi_select_ctree_ctr.h"
BEGIN_MESSAGE_MAP(CTreeCtrlX, CTreeCtrl)
ON_WM_LBUTTONDOWN()
ON_WM_KEYDOWN()
END_MESSAGE_MAP()
void CTreeCtrlX::OnLButtonDown(UINT nFlags, CPoint point)
{
// Set focus to control if key strokes are needed.
// Focus is not automatically given to control on lbuttondown
m_dwDragStart = GetTickCount();
if(nFlags & MK_CONTROL )
{
// Control key is down
UINT flag;
HTREEITEM hItem = HitTest( point, &flag );
if( hItem )
{
// Toggle selection state
UINT uNewSelState =
GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED ?
0 : TVIS_SELECTED;
// Get old selected (focus) item and state
HTREEITEM hItemOld = GetSelectedItem();
UINT uOldSelState = hItemOld ?
GetItemState(hItemOld, TVIS_SELECTED) : 0;
// Select new item
if( GetSelectedItem() == hItem )
SelectItem( NULL ); // to prevent edit
CTreeCtrl::OnLButtonDown(nFlags, point);
// Set proper selection (highlight) state for new item
SetItemState(hItem, uNewSelState, TVIS_SELECTED);
// Restore state of old selected item
if (hItemOld && hItemOld != hItem)
SetItemState(hItemOld, uOldSelState, TVIS_SELECTED);
m_hItemFirstSel = NULL;
return;
}
}
else if(nFlags & MK_SHIFT)
{
// Shift key is down
UINT flag;
HTREEITEM hItem = HitTest( point, &flag );
// Initialize the reference item if this is the first shift selection
if( !m_hItemFirstSel )
m_hItemFirstSel = GetSelectedItem();
// Select new item
if( GetSelectedItem() == hItem )
SelectItem( NULL ); // to prevent edit
CTreeCtrl::OnLButtonDown(nFlags, point);
if( m_hItemFirstSel )
{
SelectItems( m_hItemFirstSel, hItem );
return;
}
}
else
{
// Normal - remove all selection and let default
// handler do the rest
ClearSelection();
m_hItemFirstSel = NULL;
}
CTreeCtrl::OnLButtonDown(nFlags, point);
}
void CTreeCtrlX::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if ( (nChar==VK_UP || nChar==VK_DOWN) && GetKeyState( VK_SHIFT )&0x8000)
{
// Initialize the reference item if this is the first shift selection
if( !m_hItemFirstSel )
{
m_hItemFirstSel = GetSelectedItem();
ClearSelection();
}
// Find which item is currently selected
HTREEITEM hItemPrevSel = GetSelectedItem();
HTREEITEM hItemNext;
if ( nChar==VK_UP )
hItemNext = GetPrevVisibleItem( hItemPrevSel );
else
hItemNext = GetNextVisibleItem( hItemPrevSel );
if ( hItemNext )
{
// Determine if we need to reselect previously selected item
BOOL bReselect =
!( GetItemState( hItemNext, TVIS_SELECTED ) & TVIS_SELECTED );
// Select the next item - this will also deselect the previous item
SelectItem( hItemNext );
// Reselect the previously selected item
if ( bReselect )
SetItemState( hItemPrevSel, TVIS_SELECTED, TVIS_SELECTED );
}
return;
}
else if( nChar >= VK_SPACE )
{
m_hItemFirstSel = NULL;
ClearSelection();
}
CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CTreeCtrlX::ClearSelection()
{
// This can be time consuming for very large trees
// and is called every time the user does a normal selection
// If performance is an issue, it may be better to maintain
// a list of selected items
for ( HTREEITEM hItem=GetRootItem(); hItem!=NULL; hItem=GetNextItem( hItem, TVGN_NEXT ) )
if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
SetItemState( hItem, 0, TVIS_SELECTED );
}
// SelectItems - Selects items from hItemFrom to hItemTo. Does not
// - select child item if parent is collapsed. Removes
// - selection from all other items
// hItemFrom - item to start selecting from
// hItemTo - item to end selection at.
BOOL CTreeCtrlX::SelectItems(HTREEITEM hItemFrom, HTREEITEM hItemTo)
{
HTREEITEM hItem = GetRootItem();
// Clear selection upto the first item
while ( hItem && hItem!=hItemFrom && hItem!=hItemTo )
{
hItem = GetNextVisibleItem( hItem );
SetItemState( hItem, 0, TVIS_SELECTED );
}
if ( !hItem )
return FALSE; // Item is not visible
SelectItem( hItemTo );
// Rearrange hItemFrom and hItemTo so that hItemFirst is at top
if( hItem == hItemTo )
{
hItemTo = hItemFrom;
hItemFrom = hItem;
}
// Go through remaining visible items
BOOL bSelect = TRUE;
while ( hItem )
{
// Select or remove selection depending on whether item
// is still within the range.
SetItemState( hItem, bSelect ? TVIS_SELECTED : 0, TVIS_SELECTED );
// Do we need to start removing items from selection
if( hItem == hItemTo )
bSelect = FALSE;
hItem = GetNextVisibleItem( hItem );
}
return TRUE;
}
HTREEITEM CTreeCtrlX::GetFirstSelectedItem()
{
for ( HTREEITEM hItem = GetRootItem(); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_NEXT ) )
if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
return hItem;
return NULL;
}
HTREEITEM CTreeCtrlX::GetNextSelectedItem( HTREEITEM hItem )
{
for ( hItem = GetNextItem( hItem, TVGN_NEXT ); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_NEXT ) )
if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
return hItem;
return NULL;
}
HTREEITEM CTreeCtrlX::GetPrevSelectedItem( HTREEITEM hItem )
{
for ( hItem = GetNextItem( hItem, TVGN_PREVIOUS ); hItem!=NULL; hItem = GetNextItem( hItem, TVGN_PREVIOUS ) )
if ( GetItemState( hItem, TVIS_SELECTED ) & TVIS_SELECTED )
return hItem;
return NULL;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment