// CCustomTreeCtrl.cpp : implementation file
//
#include "stdafx.h"
#include "CustomTreeCtrl.h"
#include "CCVersatileTreeCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include <algorithm>
enum ImagePosIndex
{
//Image to represent an item with no check box and collapsed state
IMAGE_NORMAL_INDEX = 0,
//Image to represent an item with no check box and expanded state
IMAGE_EXPAND_INDEX,
//Image to represent an item in collapsed state with check box in checked state
IMAGE_CHECKED_NORMAL_INDEX ,
//Image to represent an item in collapsed state with check box in unchecked state
IMAGE_UNCHECKED_NORMAL_INDEX ,
//Image to represent an item in expanded state with check box in checked state
IMAGE_CHECKED_EXPANDED_INDEX ,
//Image to represent an item in expanded state with check box in unchecked state
IMAGE_UNCHECKED_EXPANDED_INDEX,
//Image to represet an item in collapsed and disabled state
IMAGE_DISABLED_STATE
};
//###########################################################################
// GLOBAL FUNCTIONS
//###########################################################################
//This function is used to export the given bitmap to a file specified by path.
//Internally used to verify the bimap scaling, prefixing a bitmap with another bitmap etc
BOOL SaveBitmap(HDC hDC,HBITMAP hBitmap,CString szPath)
{
FILE * fp= NULL;
fp = fopen(szPath.GetBuffer(1),"wb");
if(fp == NULL)
return false;
BITMAP Bm;
BITMAPINFO BitInfo;
ZeroMemory(&BitInfo, sizeof(BITMAPINFO));
BitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitInfo.bmiHeader.biBitCount = 0;
if(!::GetDIBits(hDC, hBitmap, 0, 0, NULL, &BitInfo, DIB_RGB_COLORS))
return (false);
Bm.bmHeight = BitInfo.bmiHeader.biHeight;
Bm.bmWidth = BitInfo.bmiHeader.biWidth;
BITMAPFILEHEADER BmHdr;
BmHdr.bfType = 0x4d42; // 'BM' WINDOWS_BITMAP_SIGNATURE
BmHdr.bfSize = (((3 * Bm.bmWidth + 3) & ~3) * Bm.bmHeight)
+ sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER);
BmHdr.bfReserved1 = BmHdr.bfReserved2 = 0;
BmHdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER);
BitInfo.bmiHeader.biCompression = 0;
// Writing Bitmap File Header ////
fwrite(&BmHdr,sizeof(BITMAPFILEHEADER),1,fp);
fwrite(&BitInfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
BYTE *pData = new BYTE[BitInfo.bmiHeader.biSizeImage + 5];
if(!::GetDIBits(hDC, hBitmap, 0, Bm.bmHeight,
pData, &BitInfo, DIB_RGB_COLORS))
return (false);
if(pData != NULL)
fwrite(pData,1,BitInfo.bmiHeader.biSizeImage,fp);
fclose(fp);
delete (pData);
return (true);
}
//***************************************************************************
//This is a callback function called for each item when the items are iterated
// from a given start item to end item.
int FindItemsInRect( LOOPINFO* pLoopInfo )
{
if( !pLoopInfo->hItem )
return -1;
if( !pLoopInfo->pParent )
return -1;
CVersatileTreeCtrl* pCustomTree = pLoopInfo->pTree;
//Get the rectangle which is passed as the user parameter
CRect* rc = (CRect*)(pLoopInfo->m_param1);
//Get the rect of the item
CRect itemRect;
pCustomTree->GetItemRect( pLoopInfo->hItem ,&itemRect,false);
CString str = pCustomTree->GetItemText( pLoopInfo->hItem );
pCustomTree->ClientToScreen( &itemRect );
CRect resRect;
bool bFound = false;
if( itemRect.top >= rc->top && itemRect.bottom <= rc->bottom )
{
bFound = true;
}
if( bFound )
{
pCustomTree->m_aItemsInRect.Add( pLoopInfo->hItem );
}
return 0;
}
//***************************************************************************
//This is a call back function to called for each item when the items are iterated
// from a given start item to end item.Based on the pLoopInfo's param the item may
// be selected or deselected
int SelectItems( LOOPINFO* pLoopInfo )
{
//Do the validity check on the item and parent
if( !pLoopInfo->hItem )
return -1;
if( !pLoopInfo->pParent )
return -1;
//Get the tree control from the looping info
CVersatileTreeCtrl* pCustomTree = pLoopInfo->pTree;
//Just to check the items's label
CString str;
str = pCustomTree->GetItemText(pLoopInfo->hItem);
//Get the flag which gives the selection state that user wants to set
bool bFlag = false;
if( pLoopInfo->m_param1 )
bFlag = * ( (bool*)(pLoopInfo->m_param1) );
//Set the item state of hItem as deselected
if( bFlag )
{
pCustomTree->SetItemState( pLoopInfo->hItem,TVIS_SELECTED,TVIS_SELECTED );
}
else
{
pCustomTree->SetItemState( pLoopInfo->hItem,~TVIS_SELECTED,TVIS_SELECTED );
}
return 0;
}
//***************************************************************************
//This is a call back function to called for each item when the items are iterated
// from a given start item to end item.For each item custom draw is carried out by
//considering the state of the item [highlighted,selected],color of the item,bold/normal etc
int DrawItem( LOOPINFO* pLoopInfo )
{
//Do the validity check on the item and parent
if( !pLoopInfo->hItem )
return -1;
//Get the tree control from the looping info
CVersatileTreeCtrl* pCustomTree = pLoopInfo->pTree;
//Get the label rectangle area of the item
CRect labelRect;
pCustomTree->GetItemRect( pLoopInfo->hItem,&labelRect,true );
//GetItemRect works only for expanded items.So if it is not shown/collpased then the
//rect will carry junk value
if( labelRect.left < 0 || labelRect.right < 0 || labelRect.top < 0 || labelRect.bottom < 0 )
return -1;
//Set the black color as default
COLORREF cItemColor = RGB(0,0,0 );
//Set the font weight as normal
bool bItemBold = false;
//Get the all the attributes of the item like text color, bold etc from the custom data associated with the item
PCUSTOMITEMDATA pCustomData = (PCUSTOMITEMDATA) pCustomTree->GetItemData( pLoopInfo->hItem );
if( pCustomData )
{
//If the item is in enabled state, the get the user specified color while inserting the item
if( pCustomData->m_bEnable )
{
cItemColor = pCustomData->m_cItemColor;
}
//Set the color as gray
else
{
cItemColor = ::GetSysColor(COLOR_GRAYTEXT);
}
bItemBold = pCustomData->m_bIsBold;
}
//Create client dc to draw
CClientDC pDC( pCustomTree );
//Set the text color to the item text color
pDC.SetTextColor( cItemColor );
//Check Whether the item is highlighted or selected
UINT selflag = TVIS_DROPHILITED | TVIS_SELECTED;
UINT uItemSelState = pCustomTree->GetItemState(pLoopInfo->hItem,selflag);
//Get the font of the tree control and use the same
CFont* pFont = pCustomTree->GetFont();
LOGFONT lf;
pFont->GetLogFont(&lf );
//If the item is set to BOLD then change the font weight to bold
if( bItemBold )
{
lf.lfWeight = FW_BOLD;
}
//Create a new font with font data obtained from tree and set to DC
CFont newFont;
newFont.CreateFontIndirect( &lf );
pDC.SelectObject(&newFont );
//Get the text of the item to be drawn
CString sItem = pCustomTree->GetItemText( pLoopInfo->hItem );
//Check if the box has the check box or not
if( !pCustomData->m_bChecked )
{
//Offset the labelrect to half of the image size to the left side.This is done because
//if the item doesnt have checkbox means first we are copying the image bitmap and after
//that empty bitmap [ as there is no check box ] is copied.so we will have a white space between
//the image and the label which is equal to half of the image size.
int imgW,imgH;
ImageList_GetIconSize( pCustomTree->GetImageList(TVSIL_NORMAL)->GetSafeHandle(),&imgW,&imgH);
labelRect.left = labelRect.left - (imgW/2);
}
//Delete the text drawn by the system by drawing a rectangle with the color of the
//tree control's background color and we are going to draw the label again
CRect bkRect = labelRect;
//Add some padding to completely cover the text which is drawn by default by the tree
bkRect.right = bkRect.right + bkRect.Width()/3;
COLORREF bkColor = pCustomTree->GetBkColor();
CBrush bkbrush( bkColor );
CGdiObject* pOldBrush = pDC.SelectObject(&bkbrush);
CPen bkPen( PS_SOLID,1,bkColor);
CPen* pOldpen = pDC.SelectObject(&bkPen);
pDC.Rectangle( labelRect );
pDC.SelectObject(pOldBrush);
pDC.SelectObject(pOldpen);
if( uItemSelState & selflag )
{
//If so draw the highlighting rectangle.But already a highlighting rectangle is drawn
//when CTreeCtrl::OnPaint() is being called.After that we are drawing the text for each
//item.So the text is drawn over the highlighting rect.So we have to draw a new highlighting
//rect.Inflate the rect by 1 or 2 pixlel so that it completely hides the highlighting
//rectangle drawn by the system
//If the item is selected, as the background goes to the COLOR_HIGHLIGHT the text
//should be in COLOR_HIGHLIGHTTEXT to have clear visibility.
pDC.SetTextColor( ::GetSysColor(COLOR_HIGHLIGHTTEXT) );
//Get the extents [Width,Height] of the label of the item in pixels
CSize sz = pDC.GetTextExtent(sItem);
//Convert the ordinates of the label rectangle into screen coordinates
pCustomTree->ClientToScreen( &labelRect );
//Compute the bounding rectangle
CRect rc;
rc.left = labelRect.left;
rc.top = labelRect.top;
//To give some cushioning i'm just adding quarter of its width
rc.right = rc.left + labelRect.Width() + labelRect.Width()/4;
rc.bottom = rc.top + labelRect.Height() ;
//Create a pen of highlight color to draw the border of the highlighting rectangle
CPen highlightPen;
highlightPen.CreatePen(PS_SOLID,1,::GetSysColor( COLOR_HIGHLIGHT ) );
pDC.SelectObject( &highlightPen);
//Create brush with highlighting color to draw the highlight rectangle
CBrush highlightBrush( ::GetSysColor( COLOR_HIGHLIGHT ) );
//If the tree has the focus then draw the background inthe highlight color
if( GetFocus( ) == pCustomTree->GetSafeHwnd() )
{
//Select the brush with highlighting color
pOldBrush = pDC.SelectObject( &highlightBrush);
}
else
{
//Set the text color to the item text color
pDC.SetTextColor( cItemColor );
}
//We computed the rc using the cooridinates of the labelRect which is in screen
//coordinates.Convert back to client coordinates.Draw the highlighting rectangle
pCustomTree->ScreenToClient( &rc );
pDC.Rectangle(rc);
//Similarly conver the labelRect to client cooridnates
pCustomTree->ScreenToClient( &labelRect );
//Set the background mode to transparent so that highlighting rectangle
//is not overwritten by bounding rectangle of the text while doing TextOut
pDC.SetBkMode(TRANSPARENT);
bool bIgnoreDrawText = false;
if( pCustomTree->GetEditControl( ) )
{
bIgnoreDrawText = true;
}
//Draw the text
if( !bIgnoreDrawText )
{
CString s = "****";
s += sItem;
//OutputDebugString( s.GetBuffer() );
//OutputDebugString("\n");
labelRect.right +=labelRect.Width()/4;
pDC.DrawText( sItem,labelRect,DT_SINGLELINE | DT_LEFT | DT_VCENTER);
}
//Restore the old objects
pDC.SelectObject( &pOldpen);
pDC.SelectObject( &pOldBrush);
}
else
{
labelRect.right +=labelRect.Width()/4;
CString s = "+++";
s += sItem;
//OutputDebugString( s.GetBuffer() );
//OutputDebugString("\n");
pDC.DrawText( sItem,labelRect,DT_SINGLELINE | DT_LEFT | DT_VCENTER);
}
return 0;
}
//***************************************************************************
//DESCRIPTION:
// This is a call back function to called for each item when the items are
// iterated from a given start item to end item.Expand/Collapse the items
// recursively
int ExpandItem( LOOPINFO* pLoopInfo )
{
//Do the validity check on the item and parent
if( !pLoopInfo->hItem )
return -1;
//Get the tree control from the looping info
CVersatileTreeCtrl* pCustomTree = pLoopInfo->pTree;
//Get the flag which gives the expand state that user wants to set
bool bExpand = false;
if( pLoopInfo->m_param1 )
bExpand = * ( (bool*)(pLoopInfo->m_param1) );
if( bExpand )
pCustomTree->Expand(pLoopInfo->hItem,TVE_EXPAND ) ;
else
pCustomTree->Expand(pLoopInfo->hItem,TVE_COLLAPSE ) ;
return 0;
}
//***************************************************************************
//DESCRIPTION:
// This is a call back function to called for each item when the items are
// iterated from a given start item to end item.Check the items data with
// the user given data.If matches stop iterating and return
int SearchItemData( LOOPINFO* pLoopInfo )
{
//Do the validity check on the item and parent
if( !pLoopInfo->hItem )
return -1;
//Get the tree control from the looping info
CVersatileTreeCtrl* pCustomTree = pLoopInfo->pTree;
//Get the data to be searched
void* pUserData = pLoopInfo->m_param1;
//Get the item data
void* pItemData = (void*)pCustomTree->GetItemData(pLoopInfo->hItem);
//Item found
if( pItemData == pUserData )
{
if( pLoopInfo->m_pResult )
{
HTREEITEM* hResItem = (HTREEITEM*)pLoopInfo->m_pResult;
*hResItem = pLoopInfo->hItem;
return 1;
}
}
return 0;
}
//###########################################################################
// CVersatileTreeCtrl
//###########################################################################
/////////////////////////////////////////////////////////////////////////////
// CVersatileTreeCtrl
CVersatileTreeCtrl::CVersatileTreeCtrl()
{
m_hDragSourceItem = NULL;
m_hDragTargetItem = NULL;
m_pDragImageList = NULL;
m_defaultCursor = ::LoadCursor(NULL,IDC_ARROW);
m_noCursor = ::LoadCursor(NULL,IDC_NO);
m_bIsBandingON = false;
m_bIsDragging = false;
m_pImgList = NULL;
m_hRClickItem = NULL;
m_bMoveItem = true;
m_cMaskColor = RGB(0,128,128);
m_bContinueScan = true;
}
CVersatileTreeCtrl::~CVersatileTreeCtrl()
{
if( m_pImgList )
{
m_pImgList->DeleteImageList();
delete m_pImgList;
}
if( m_pDragImageList )
{
m_pDragImageList->DeleteImageList();
delete m_pDragImageList;
}
}
BEGIN_MESSAGE_MAP(CVersatileTreeCtrl, CTreeCtrl)
//{{AFX_MSG_MAP(CVersatileTreeCtrl)
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT, OnEndlabeledit)
ON_NOTIFY_REFLECT(TVN_BEGINLABELEDIT, OnBeginlabeledit)
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemexpanding)
ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
ON_WM_LBUTTONUP()
ON_NOTIFY_REFLECT(NM_CLICK, OnClickLButton)
ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged)
ON_NOTIFY_REFLECT(TVN_SELCHANGING, OnSelchanging)
ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
ON_MESSAGE(TVN_STATEICON_CLICKED, OnChkBoxClicked )
ON_WM_PAINT()
//ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblclk)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CVersatileTreeCtrl message handlers
/***************************************************************************
/DESCRIPTION:
/-----------
/ To set the background color for the tree control
/
/PARAM
/------
/ bkColor[IN] - New background color to be set
/
/RESULT:
/-------
/ old background color of the control
*/
void CVersatileTreeCtrl::SetBackgroundColor( COLORREF bkColor )
{
//SetBkColor function can also be used
// SetBkColor( bkColor );
//TVM_SETBKCOLOR can also be used by passing the color as the lparam
// SendMessage( TVM_SETBKCOLOR,0, (LPARAM)bkColor );
TreeView_SetBkColor( GetSafeHwnd(),bkColor );
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To set the bold font for the label
/
/PARAM
/------
/ hItem[in] - Item to which the font to be set as bold
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::SetBoldFont(HTREEITEM hItem )
{
if( !hItem )
return;
//SetItemState( hItem,TVIS_BOLD,TVIS_BOLD );
Expand(hItem,TVE_EXPAND);
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To set the color for the item text
/
/PARAM
/------
/ hItem[in] - Item to which the color to be set
/ col[in] - New color of the item
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::SetItemColor( HTREEITEM hItem, COLORREF col )
{
if( !hItem )
return;
//Get the item data
PCUSTOMITEMDATA pCustomData = (PCUSTOMITEMDATA) GetItemData( hItem );
if( pCustomData )
pCustomData->m_cItemColor = col;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To set checkbox for a given item
/
/PARAM
/------
/ hItem[in] - Item to be set
/ col[in] - TRUE to set the checkbox,false to remove
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::SetCheckBox( HTREEITEM hItem,bool bCheck )
{
if( !hItem )
return;
//Get the item data
PCUSTOMITEMDATA pCustomData = (PCUSTOMITEMDATA) GetItemData( hItem );
//If it already has the checkbox dont do it
if( bCheck )
{
if( pCustomData->m_bChecked )
return;
pCustomData->m_bChecked = bCheck;
pCustomData->m_bCheckState = true;
}
else if ( !bCheck )
{
if( !pCustomData->m_bChecked )
return;
pCustomData->m_bChecked = bCheck;
}
if( bCheck )
{
//Add the checkbox image
//Check the state of the item expanded/collapsed
bool bItemExpanded = false;
if( GetItemState( hItem,TVIS_EXPANDED) & TVIS_EXPANDED)
bItemExpanded = true;
UINT ChkBmpID = IDB_BITMAP_CHECK_16;
UINT unChkBmpID = IDB_BITMAP_UNCHECK;
UINT uBmpID = pCustomData->m_uNormalImage;
UINT uExpBmpID = pCustomData->m_uExpandedImage;
AddCheckStateImages( uBmpID,ChkBmpID,unChkBmpID,pCustomData );
AddExpStateImages( uBmpID,uExpBmpID,ChkBmpID,unChkBmpID,pCustomData );
}
SetImage( hItem );
GetParent()->SendMessage( TVN_ITEM_CHECK_TOGGLE,(WPARAM)(&m_vecCheckedItems),NULL );
}
/****************************************************************************
/DESCRIPTION:
/-----------
/ To check/uncheck a checkboxed item
/
/PARAM
/------
/ hItem[in] - Item to be set
/ bCheck[in] - TRUE to set the checkbox,false to remove
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::SetCheck( HTREEITEM hItem,bool bCheck )
{
//Check for item
if( !hItem )
return;
//Get the item data
PCUSTOMITEMDATA pCustomData = (PCUSTOMITEMDATA) GetItemData( hItem );
//Check the item is a checkboxed one
if( !pCustomData->m_bChecked )
return;
//Check the state of the check box is same as of bCheck
if( pCustomData->m_bCheckState == bCheck )
return;
//Set the appropriate the image
if( bCheck )
{
//Add to the list of checked items
m_vecCheckedItems.insert( hItem );
}
else
{
//Remove from the list of checked items
m_vecCheckedItems.erase(hItem);
}
//Set the image for the item based on its state
SetImage( hItem );
//Notify the dialog about the checked item
GetParent()->SendMessage( TVN_ITEM_CHECK_TOGGLE,(WPARAM)hItem,(LPARAM)bCheck );
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To enable/disable the item
/
/PARAM
/------
/ hItem[in] - Item to be enabled/disabled
/ bEnable[in] - True for enable,false for disable
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::EnableItem( HTREEITEM hItem,bool bEnable )
{
if( !hItem )
return;
//Get the item data
PCUSTOMITEMDATA pCustomData = (PCUSTOMITEMDATA) GetItemData( hItem );
//Already the item is enabled. Dont proceed further
if( pCustomData->m_bEnable == bEnable )
return;
//Store the enable/disable state
pCustomData->m_bEnable = bEnable;
//Item to disabled.If the disable image is not there generate it
if( !bEnable )
{
//Store the expand state of the item before collapsing
pCustomData->m_bExpStateBefDisable =( GetItemState( hItem, TVIS_EXPANDED ) & TVIS_EXPANDED );
//Before disabling the item, collpase it
Expand( hItem,TVE_COLLAPSE );
//Disabled image is not being generated yet.
if( pCustomData->m_iDisableIndex == -1 )
{
//I THOUGHT OF USING GetImageInfo by passing the image index and using the
//HBITMAP INFO AVAILABLE IN THE IMAGEINFO.BUT SOMEHOW IT DOESNT WORK
//Check whether the disabled image for the normal image of the item
//is generated already or not
map<UINT, vector<int> >::iterator it = m_mapBitmapID.find(pCustomData->m_uNormalImage);
if( it != m_mapBitmapID.end( ) && it->second.size() == 7)
{
//Already there is a disabled image corresponding to pCustomData->m_uNormalImage
pCustomData->m_iDisableIndex = it->second.at(IMAGE_DISABLED_STATE);
}
else
{
//Get the index of the normal image of the item from the imagelist
int normalImgIndex,selImageIndex;
GetItemImage(hItem,normalImgIndex,selImageIndex);
//Get the bitmap of the item
CBitmap disableBmp;
bool res = GetBitampFromImageList( normalImgIndex,disableBmp );
//Generate the disabled image
GenerateDisableImage( disableBmp);
//Add it to the image list
int index = m_pImgList->Add( &disableBmp,m_cMaskColor );
//Add the index to the disabled index array
int iBmpFound = -1;
iBmpFound = GetBitmapIndexFromImgList( pCustomData->m_uNormalImage );
pCustomData->m_iDisableIndex = index;
it->second.push_back( index );
}
}
}
//Restore the normal position of the item
else
{
//Before disabling, if the item was in expanded state, restore it back
if( pCustomData->m_bExpStateBefDisable )
{
Expand(hItem,TVE_EXPAND);
}
}
//Set the image for the item based on its state
SetImage( hItem );
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To expand the given item [ All the items under the given items are expanded]
/
/PARAM
/------
/ hItem[in] - Item to be expanded
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::ExpandTree(HTREEITEM hItem,bool bExpand /*= true*/ )
{
IterateItems( ExpandItem,hItem,NULL,&bExpand );
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ Called when the user starts its label editing
/
/PARAM
/------
/ pNMHDR[in] - Contains the data about the item edited
/ pResult[in/out] - Result of the edition
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::OnBeginlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
// TODO: Add your control notification handler code here
//Get the custom data and check whether item is editbale
HTREEITEM hItem = pTVDispInfo->item.hItem;
if( hItem )
{
//Get the custom data from the item
PCUSTOMITEMDATA pCustomData = (PCUSTOMITEMDATA) GetItemData( hItem );
if( pCustomData )
{
//If the item is not editable or disabled dont allow the renaming
if( !pCustomData->m_bEditable || !pCustomData->m_bEnable )
{
*pResult = 1;
return;
}
}
}
Invalidate( );
*pResult = 0;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ Called when the user completes its label editing
/
/PARAM
/------
/ pNMHDR[in] - Contains the data about the item edited
/ pResult[in/out] - Result of the edition
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
// TODO: Add your control notification handler code here
//Set the new text received from dispinfo structure to the item
if( pTVDispInfo->item.pszText != NULL)
{
SetItemText( pTVDispInfo->item.hItem,pTVDispInfo->item.pszText);
m_vecSelectedItems.clear( );
m_vecSelectedItems.push_back( pTVDispInfo->item.hItem );
GetParent()->SendMessage( TVN_SELECTION_CHANGED,(WPARAM)(&m_vecSelectedItems),NULL );
}
*pResult = 0;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To insert a item in the tree using the custom data
/
/PARAM
/------
/ lpInsertStruct[in] - Custom data of item to be inserted
/
/RESULT:
/-------
/ void
*/
HTREEITEM CVersatileTreeCtrl::InsertItem(PCUSTOMINSERTSTRUCT lpInsertStruct )
{
//Add the image to the imagelist
//------------------------------
int res = AddImageToList( lpInsertStruct );
//Insert the item
HTREEITEM hItem = CTreeCtrl::InsertItem( &lpInsertStruct->m_tvIS );
//Set the custom data as the item data
SetItemData( hItem,(DWORD)(lpInsertStruct->m_pCustomData));
bool flag = ItemHasChildren(lpInsertStruct->m_tvIS.hParent);
if( lpInsertStruct->m_pCustomData->m_bChecked )
{
if( lpInsertStruct->m_pCustomData->m_bCheckState )
{
m_vecCheckedItems.insert( hItem );
}
GetParent()->SendMessage( TVN_ITEM_CHECK_TOGGLE,(WPARAM)(&m_vecCheckedItems),NULL );
}
SetImage(hItem);
return hItem;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ Called when the item is expanding
/
/PARAM
/------
/ pNMHDR[in] - Contains the data about the item edited
/ pResult[in/out] - Result of the edition
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
//If the click is on the checkbox then dont allow expand/collpase
if( !m_bAllowExpand )
{
*pResult = 1;
CUSTNMHDR chdr;
chdr.m_hdr.hwndFrom = m_hWnd;
chdr.m_hdr.idFrom = ::GetDlgCtrlID(m_hWnd);
chdr.m_hdr.code = TVN_STATEICON_CLICKED;
chdr.m_hItem = pNMTreeView->itemNew.hItem;
chdr.m_data = (PCUSTOMITEMDATA) GetItemData(pNMTreeView->itemNew.hItem);
SendMessage(TVN_STATEICON_CLICKED, (WPARAM)&chdr, (LPARAM)chdr.m_hdr.idFrom );
return;
}
//Get the item data
PCUSTOMITEMDATA pData = (PCUSTOMITEMDATA) GetItemData(pNMTreeView->itemNew.hItem);
//Similarly if the item is disabled dont allow expand
if( !pData->m_bEnable && pNMTreeView->action == TVE_EXPAND)
{
*pResult = 1;
return;
}
bool bItemExpanded = false;
if( pNMTreeView->action == TVE_EXPAND )
bItemExpanded = true;
//Set image based on expand/collpase state
SetExpandImage( pNMTreeView->itemNew.hItem,bItemExpanded );
*pResult = 0;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ Called when the item is dragged
/
/PARAM
/------
/ pNMHDR[in] - Contains the data about the item edited
/ pResult[in/out] - Result of the edition
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
//Get the item being clicked for dragging
LPNMTREEVIEW lpnmTreeView = (LPNMTREEVIEW )pNMHDR;
//Check the clicked point lies in one of the selected item.
//1. If so, drag all the selected items
//2. Drag only the item where left mouse button is clicked
CollectSelectedItems();
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
//Clear the dragged items vector
m_vecDraggedItems.clear( );
//If the item being dragged in one of the selected items, then user intention
//is to drag all the selected items
int iSelItemCtr = 0;
bool bDragSelectedItems = false;
for( ; iSelItemCtr < m_vecSelectedItems.size( ); iSelItemCtr++ )
{
RECT rc;
GetItemRect( m_vecSelectedItems[iSelItemCtr],&rc,FALSE );
CRect crc;
crc.left = rc.left; crc.bottom=rc.bottom;crc.top=rc.top; crc.right=rc.right;
if( crc.PtInRect( pNMTreeView->ptDrag ) )
{
bDragSelectedItems = true;
m_vecDraggedItems = m_vecSelectedItems;
break;
}
}
//If not, the user is willing drag the item he is clicked
if( !bDragSelectedItems )
{
m_vecDraggedItems.push_back( pNMTreeView->itemNew.hItem );
}
//Find out the node to be dragged
//-------------------------------
CPoint pt;
pt = pNMTreeView->ptDrag;
//Find out the item to dragged using hit test
m_hDragSourceItem =pNMTreeView->itemNew.hItem;
m_hDragTargetItem = NULL;
//Mark that dragging in progress
m_bIsDragging = true;
//If already the drag image list exists then delete it
if( m_pDragImageList )
{
m_pDragImageList->DeleteImageList();
delete m_pDragImageList;
}
//Create the drag image for the selected items which are to be dragged
m_pDragImageList = GetImageListForDrag( m_vecDraggedItems );
if( m_pDragImageList )
{
//Start dragging the image
m_pDragImageList->BeginDrag( 0,CPoint(0,0));
//Lock updates to the window specified by first parameter (this)
//and displays the drag image at the position specified by pt.
m_pDragImageList->DragEnter( this,pt );
//Capture the mouse
SetCapture();
}
else
m_bIsDragging = true;
*pResult = 0;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To copy an item to another item
/
/PARAM
/------
/ hSourceItem[in] - Item to be copied
/ hTargetItem[in] - Target where the source to be copied
/ bMoveSibling[in] - Whether the sibling has to be moved or not.When this function
called for first time from some other function this is false.
Where as when it calls itself recursively this will be true.
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::CopyItem(HTREEITEM hSourceItem,HTREEITEM hTargetItem,bool bMoveSibling /*=false*/)
{
if( !hSourceItem || !hTargetItem )
return;
if( hSourceItem == hTargetItem )
return;
//Get the info about the child item
TVITEM tvItem;
tvItem.mask = TVIF_IMAGE | TVIF_STATE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_HANDLE;
char buf[128];
tvItem.pszText = buf;
tvItem.cchTextMax = 128;
tvItem.hItem = hSourceItem;
GetItem(&tvItem);
CUSTOMINSERTSTRUCT tvIS;
tvIS.m_tvIS.hParent = hTargetItem;
tvIS.m_tvIS.hInsertAfter = TVI_LAST;
tvIS.m_tvIS.item = tvItem;
//Get the item data of the hSourceItem
PCUSTOMITEMDATA pData = (PCUSTOMITEMDATA) GetItemData(hSourceItem);
tvIS.m_pCustomData = pData;
CString str1 = GetItemText( hSourceItem );
CString str2 = GetItemText( hTargetItem );
HTREEITEM hInsertedItem = InsertItem(&tvIS);
if( GetItemState( hSourceItem,TVIS_SELECTED ) & TVIS_SELECTED )
{
SetItemState( hInsertedItem,TVIS_SELECTED,TVIS_SELECTED );
//Add the inserted item to the m_vecSelectedItems as well
m_vecSelectedItems.push_back(hInsertedItem);
}
//This is quite important.Say When source item becomes the parent to the target item
//the copy will be called recursively.Comment this line and try to drag a item onto its
//one of the child item.
if( hSourceItem == m_hOrigTgt )
return;
HTREEITEM hChildItem, hSiblingItem ;
hChildItem = GetChildItem(hSourceItem);
while( hChildItem )
{
//Get the sibling of the given child item
hSiblingItem = GetNextSiblingItem( hChildItem );
str1 = GetItemText( hChildItem );
str2 = GetItemText( hSiblingItem );
CopyItem( hChildItem, hInsertedItem );
hChildItem = hSiblingItem;
}
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To delete an items and all its children recursively and remove the item to
/ be deleted from the vector holding the selected items and the vector holding
// the checked items
/
/PARAM
/------
/ hTreeItem[in] - Item to be deleted
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::DeleteItem( HTREEITEM hTreeItem )
{
if( !hTreeItem )
return;
//If it is there in selected items array then remove it
int iSelItemCnt = 0;
for( ; iSelItemCnt < m_vecSelectedItems.size(); iSelItemCnt++ )
{
if( m_vecSelectedItems[iSelItemCnt] == hTreeItem )
{
m_vecSelectedItems.erase( m_vecSelectedItems.begin( ) + iSelItemCnt );
//Inform the dialog
GetParent()->SendMessage( TVN_SELECTION_CHANGED,(WPARAM)(&m_vecSelectedItems),NULL );
}
}
//Similarly from the checked items array
set<HTREEITEM>::iterator it = m_vecCheckedItems.find( hTreeItem );
if( it != m_vecCheckedItems.end( ) )
{
m_vecCheckedItems.erase( it );
//Inform the dialog
GetParent()->SendMessage( TVN_ITEM_CHECK_TOGGLE,(WPARAM)(&m_vecCheckedItems),NULL );
}
CTreeCtrl::DeleteItem( hTreeItem );
}
//***************************************************************************
void CVersatileTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
TV_HITTESTINFO tvHitInfo;
tvHitInfo.pt = point;
HTREEITEM hClickedItem = HitTest(&tvHitInfo );
m_bAllowExpand = true;
//Handling the click on the check box.
//If the click is ON the item icon find out whethere it is ON the check box
//or on the image
if( hClickedItem )
{
PCUSTOMITEMDATA pCustData = (PCUSTOMITEMDATA) GetItemData(hClickedItem);
if( pCustData && pCustData->m_bChecked && pCustData->m_bEnable )
{
if( tvHitInfo.flags & TVHT_ONITEMICON )
{
CPoint pt = point;
//Convert the point from client to screen coordinate
ClientToScreen(&pt );
//Get the full rect area
CRect fullRect;
GetItemRect( hClickedItem,&fullRect,FALSE );
//Convert the rect into screen ordinates
ClientToScreen( &fullRect );
//Get the rect area of the label
CRect labelRect;
GetItemRect( hClickedItem,&labelRect,TRUE );
//Convert the rect into screen ordinates
ClientToScreen( &labelRect );
//Get the rect of the image
CRect imgRect;
imgRect.left = fullRect.left;
imgRect.top = fullRect.top;
imgRect.right = imgRect.left + ( labelRect.left - fullRect.left );
imgRect.bottom = fullRect.bottom;
//ImgRect contains both the check box image and image of the button
//We want to know whether the check box is clicked or not. So get
//the left half of the ImgRect which is nothing but the check box rect.
int imgW,imgH;
ImageList_GetIconSize( m_pImgList->GetSafeHandle(),&imgW,&imgH);
imgRect.right -= imgW/2;
//Now check the click point is there in this rect. If so the click is on the
//check box of the item
if( imgRect.PtInRect( pt ) )
{
m_bAllowExpand = false;
CUSTNMHDR chdr;
chdr.m_hdr.hwndFrom = m_hWnd;
chdr.m_hdr.idFrom = ::GetDlgCtrlID(m_hWnd);
chdr.m_hdr.code = TVN_STATEICON_CLICKED;
chdr.m_hItem = hClickedItem;
chdr.m_data = (PCUSTOMITEMDATA) GetItemData(hClickedItem);
SendMessage(TVN_STATEICON_CLICKED, (WPARAM)&chdr, (LPARAM)chdr.m_hdr.idFrom );
return;
}
}
}
}
//If the click is not on the item and not on the item button means enable the
//banding
if( !(tvHitInfo.flags & TVHT_ONITEM) && !(tvHitInfo.flags & TVHT_ONITEMBUTTON) )
{
if( GetEditControl( ) )
{
CTreeCtrl::OnLButtonDown(nFlags, point);
return;
}
//if there is no selected item then ON the banding
m_bIsBandingON = true;
//Store the starting point
m_startPt = m_endPt = point;
//Remove the selection
int i = 0;
for( ; i < m_vecSelectedItems.size(); i++ )
{
HTREEITEM hItem = m_vecSelectedItems[i];
SetItemState( m_vecSelectedItems[i],~TVIS_SELECTED,TVIS_SELECTED );
}
//This is important.If you dont select the NULL item, the GetSelectedItem will
//always return the previous selected item.For example,select item1.Click the mouse
//button somewhere on the tree and not on any of the tree item. So in previous loop
//we have removed the selection flag of item1 [ highlighting of the items goes].
//Now again click on item1.But the tree gives the item1 as selected [eventhough it is
//not highlighted ]item1 and because of this we are not able to select item1.So make
//forcefully that no item is selected by calling SelectItem(NULL ).Now it works.Great!!!!
SelectItem( NULL );
//Send the notification to the parent dialog about the selected items.
//Collect the selected items. May be parent may seek this data.
CollectSelectedItems();
GetParent()->SendMessage( TVN_SELECTION_CHANGED,(WPARAM)(&m_vecSelectedItems),NULL );
SetCapture();
return;
}
//Capture the mouse
SetCapture();
Invalidate();
CTreeCtrl::OnLButtonDown(nFlags, point);
}
//***************************************************************************
void CVersatileTreeCtrl::ShowItemInfo(HTREEITEM hItem )
{
if( !hItem )
return;
CString str = GetItemText( hItem );
TVITEM infoItem;
infoItem.hItem = hItem;
infoItem.mask = TVIF_TEXT ;
char label[128];
infoItem.pszText = label;
infoItem.cchTextMax = 128;
GetItem( &infoItem );
MessageBox( infoItem.pszText);
}
//***************************************************************************
void CVersatileTreeCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
//If dragging is enabled then drag image
if( m_bIsDragging )
{
m_hDragTargetItem = NULL;
if( m_pDragImageList )
{
// Move the drag image to the next position
m_pDragImageList->DragMove(point);
//DragShowNolock:Shows or hides the drag image during a drag operation,
//without locking the window.
//As the window is locked during the BeginDrag,if you dont unlock
//using DragShowNoLock(), the previosly highlighted target wont be
//refreshed.So it will remain in highlighted state.
m_pDragImageList->DragShowNolock(false );
//Based on the mouse position keep updating the target node for the drop operation
UINT flags;
m_hDragTargetItem = HitTest( point,&flags );
//If the target is not a valid one for dropping the show the nocursor
PCUSTOMITEMDATA pTargetData = NULL;
::SetCursor( m_defaultCursor);
if( m_hDragTargetItem )
{
pTargetData = (PCUSTOMITEMDATA) GetItemData(m_hDragTargetItem);
//if both source and target are same,show invalid cursor
if( m_hDragSourceItem == m_hDragTargetItem )
{
::SetCursor( m_noCursor );
}
if( !pTargetData->m_bDropTarget )
::SetCursor( m_noCursor );
}
if( m_hDragTargetItem )
{
//Highlight the target item
SelectDropTarget( m_hDragTargetItem );
//Expand the target item if it is not in the disbaled state
if( pTargetData )
if( pTargetData->m_bEnable )
Expand( m_hDragTargetItem,TVE_EXPAND);
//Again lock the window
m_pDragImageList->DragShowNolock(true );
}
}
}
else if( m_bIsBandingON )
{
CClientDC dc(this);
InvertRectangle( &dc,m_startPt,m_endPt );
InvertRectangle( &dc,m_startPt,point );
m_endPt = point;
}
CTreeCtrl::OnMouseMove(nFlags, point);
}
//***************************************************************************
void CVersatileTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if( m_bIsDragging )
{
//Unlock the window
m_pDragImageList->DragLeave(this);
//End the drag operation
m_pDragImageList->EndDrag();
//Get the data associated with the drop target
PCUSTOMITEMDATA pTargetData = NULL;
if(m_hDragTargetItem )
{
if( m_hDragTargetItem )
pTargetData = (PCUSTOMITEMDATA) GetItemData(m_hDragTargetItem);
}
//Check whether the target is a valid one for drop operation
if( pTargetData && pTargetData->m_bDropTarget )
{
//Drop the selected items one by one over the target item
int iSelItemCnt = 0;
//Target should not be in the dragged item collection
if( std::find(m_vecDraggedItems.begin( ),m_vecDraggedItems.end( ), m_hDragTargetItem ) == m_vecDraggedItems.end( ) )
{
//Take a copy of selected items in a temp array and manipulate it.Because
//when an item is deleted OnSelChanged is called which inturn collects the
//list of selected items in m_vecSelectedItems.So it is being overwritten and
//causes chaos.
for( ; iSelItemCnt < m_vecDraggedItems.size(); iSelItemCnt++ )
{
//Dont allow to drop the root item.IF the parent is NULL then
//its a root item
HTREEITEM hDragParentItem = GetParentItem( m_vecDraggedItems[iSelItemCnt] );
if( !hDragParentItem )
{
//If the parent item of this item is already being deleted, we wont process further
//But it has to be removed from the m_vecDraggedItems and m_vecSelectedItems
vector<HTREEITEM>::iterator itSel = std::find( m_vecSelectedItems.begin( ), m_vecSelectedItems.end( ), m_vecDraggedItems[iSelItemCnt] );
if( itSel != m_vecSelectedItems.end( ) )
{
m_vecSelectedItems.erase( itSel );
}
set<HTREEITEM>::iterator itDrag = std::find( m_vecCheckedItems.begin( ), m_vecCheckedItems.end( ), m_vecDraggedItems[iSelItemCnt] );
if( itDrag != m_vecCheckedItems.end( ) )
{
m_vecCheckedItems.erase( itDrag );
}
continue;
}
//If the target is the child of the dragged item, then dont allow
if( GetParentItem(m_hDragTargetItem) == m_vecDraggedItems[iSelItemCnt] )
continue;
//If target and source both are same then dont allow
if( m_vecDraggedItems[iSelItemCnt] == m_hDragTargetItem )
continue;
PCUSTOMITEMDATA pSourceData = NULL;
if( m_vecDraggedItems[iSelItemCnt] )
pSourceData = (PCUSTOMITEMDATA) GetItemData(m_vecDraggedItems[iSelItemCnt]);
//Check the hierarchy of the source and target matches
if( pSourceData && pTargetData)
{
//If the parent of the source is same as the target
//item then dont allow.Because already the source is in the target
HTREEITEM hSourceParent = GetParentItem( m_vecDraggedItems[iSelItemCnt] );
if( hSourceParent != m_hDragTargetItem )
{
//delete the drage image list
if( m_pDragImageList )
{
m_pDragImageList->DeleteImageList();
delete m_pDragImageList;
m_pDragImageList = NULL;
}
m_hOrigTgt = m_hDragTargetItem;
CopyItem( m_vecDraggedItems[iSelItemCnt],m_hDragTargetItem);
m_hOrigTgt = NULL;
//It the item has to be moved then delete the source item after
//copyiing into target
if( m_bMoveItem )
{
//Remove the item from the selection array
HTREEITEM hItem = m_vecDraggedItems.at( iSelItemCnt );
m_vecDraggedItems.erase( m_vecDraggedItems.begin( ) + iSelItemCnt);
DeleteItem( hItem );
iSelItemCnt--;
}
UpdateWindow();
}
}
}
GetParent()->SendMessage( TVN_SELECTION_CHANGED,(WPARAM)(&m_vecSelectedItems),NULL );
}
}
}
//if the banding is ON then remove the banding flag and rectangle drawn
if( m_bIsBandingON )
{
m_bIsBandingON = false;
CClientDC dc(this);
InvertRectangle( &dc,m_startPt,m_endPt );
if( m_startPt != m_endPt )
{
CRect rect;
if( m_startPt.y < m_endPt.y )
{
rect.left = m_startPt.x;
rect.top = m_startPt.y;
rect.right = m_endPt.x;
rect.bottom = m_endPt.y;
}
else
{
rect.left = m_endPt.x;
rect.top = m_endPt.y;
rect.right = m_startPt.x;
rect.bottom = m_startPt.y;
}
//Find the items inside the rectangle
ItemsInRect( rect );
}
}
if( m_pDragImageList )
m_pDragImageList->DragShowNolock(FALSE );
m_bIsDragging = false;
SelectDropTarget(NULL);
//Release the mouse
ReleaseCapture();
ShowCursor( true );
CTreeCtrl::OnLButtonUp(nFlags, point);
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To iterate all the items and call the callback function for each item
/
/PARAM
/------
/ func[in] - Call back function to be called on each item
/ hStart[in] - Start node for the iteration
/ hEnd[in] - End node to stop the iteration
/ pInfo[in] - User parameter
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::IterateItems( ScanCallBackFunc func,
HTREEITEM hIterStart /*= NULL*/,
HTREEITEM hIterEnd, /*= NULL*/
void* pInfo /*=NULL*/
)
{
//If there is no start then take the root item
HTREEITEM hStart = GetRootItem();
if( !hIterStart )
hIterStart = hStart;
m_bContinueScan = true;
m_bStartFound = false;
ScanItems( func,hStart,hIterStart,hIterEnd,pInfo );
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To iterate all the items and call the callback function for each item
/
/PARAM
/------
/ func[in] - Call back function to be called on each item
/ hStart[in] - Start node of the tree
/ hIterStart[in] - Start node for the iteration
/ hIterEnd[in] - End node to stop the iteration
/ pInfo[in] - User parameter
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::ScanItems( ScanCallBackFunc func,
HTREEITEM hStart,
HTREEITEM hIterStart /*= NULL*/,
HTREEITEM hIterEnd, /*= NULL*/
void* pInfo /*= NULL*/
)
{
//Loop from start till the end is reached
while( hStart && m_bContinueScan )
{
//Check whether we have reached the start.Till you reach the start dont
//call the CB Function for the intermediate items
if( hStart == hIterStart )
m_bStartFound = true;
LOOPINFO lInfo;
lInfo.pTree = this;
lInfo.hItem = hStart;
lInfo.pParent = GetParent();
lInfo.m_param1 = pInfo;
CString str = GetItemText( hStart );
CRect rc;
GetItemRect( hStart,&rc,true );
//Callback is called for the current item
int bRes = 0;
if( m_bStartFound )
{
bRes = func( &lInfo );
}
if( bRes == 1 )
{
m_bContinueScan = false;
return;
}
//Check whether we have reached iterating upto the end node.If reached
//then dont call the CB function.
if( hStart == hIterEnd )
m_bStartFound = false;
//Get the childern of the current item and call recursively
HTREEITEM hChild = NULL;
if( ItemHasChildren( hStart ) )
{
hChild = GetChildItem( hStart );
ScanItems( func,hChild,hIterStart,hIterEnd,pInfo);
}
HTREEITEM hNext;
hNext = GetNextSiblingItem( hStart );
hStart = hNext;
}
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To invert the rectangle being drawn
/
/PARAM
/------
/ pDC[in] - Device context used for drawing
/ startPt[in] - Start point of the rectangle
/ endPt[in] - End point of the rectangle
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::InvertRectangle( CDC* pDC,CPoint startPt,CPoint endPt )
{
//CBrush brush;
//brush.CreateSolidBrush (RGB (49, 106, 197));
//CBrush* pOldBrush = pDC->SelectObject(&brush );
CGdiObject* pOldBrush = pDC->SelectStockObject(NULL_BRUSH );
//CPen pen (PS_SOLID, 1, RGB (49, 106, 197));
CPen pen (PS_DOT, 1, RGB (49, 106, 197));
CPen* pOldPen = pDC->SelectObject (&pen);
int nOldMode = pDC->SetROP2( R2_NOTXORPEN );
pDC->Rectangle( startPt.x,startPt.y,endPt.x,endPt.y);
pDC->SetROP2(nOldMode);
pDC->SelectObject(pOldBrush );
pDC->SelectObject (pOldPen);
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To find the items inside the given rectangle
/
/PARAM
/------
/ rect[in] - Bounding rectangle area
/RESULT:
/-------
/ void
*/
bool CVersatileTreeCtrl::ItemsInRect(CRect rect)
{
//First deselect all the items
bool bFlag = false;
IterateItems( SelectItems,NULL,NULL,&bFlag );
UpdateWindow();
ClientToScreen(&rect);
m_aItemsInRect.RemoveAll();
IterateItems( FindItemsInRect,NULL,NULL,&rect );
int i = 0;
for( ; i < m_aItemsInRect.GetSize(); i++ )
{
SetItemState( m_aItemsInRect[i],TVIS_SELECTED,TVIS_SELECTED);
}
//Send the notification to the parent dialog about the selected items
CollectSelectedItems();
GetParent()->SendMessage( TVN_SELECTION_CHANGED,(WPARAM)(&m_vecSelectedItems),NULL );
return true;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To get the first selected item in the tree
/
/PARAM
/------
/ void
/RESULT:
/-------
/ First selected item if any,otherwise NULL
*/
HTREEITEM CVersatileTreeCtrl::GetFirstSelectedItem() const
{
for(HTREEITEM hItem = GetRootItem(); hItem; hItem = GetNextVisibleItem(hItem))
if(GetItemState(hItem, UINT(TVIS_SELECTED)) & TVIS_SELECTED)
return hItem;
return 0;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To get the next selected item in the tree
/
/PARAM
/------
/ void
/RESULT:
/-------
/ Selected item if any,otherwise NULL
*/
HTREEITEM CVersatileTreeCtrl::GetNextSelectedItem(HTREEITEM hItem) const
{
if( !hItem )
return 0;
for( hItem = GetNextVisibleItem(hItem); hItem; hItem = GetNextVisibleItem(hItem))
{
if(GetItemState(hItem, UINT(TVIS_SELECTED)) & TVIS_SELECTED)
return hItem;
}
return 0;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To Collect the selected items in an array
/
/PARAM
/------
/ void
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::CollectSelectedItems()
{
m_vecSelectedItems.clear();
for(HTREEITEM hItem = GetFirstSelectedItem(); hItem != 0; hItem = GetNextSelectedItem(hItem))
{
m_vecSelectedItems.push_back(hItem);
}
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To generate the imagelist for the dragging. If multiple items are being dragged
/ we have to pile up all the image of the selected items as single drag image
/
/PARAM
/------
/ void
/RESULT:
/-------
/ Create imagelist if any,otherwise NULL
*/
CImageList* CVersatileTreeCtrl::GetImageListForDrag(vector<HTREEITEM>& cArrDragItems )
{
//If there is no selected item for the drag return NULL
CImageList* pNewImageList = NULL;
if( cArrDragItems.size() == 0 )
return NULL;
//Get the imagelist of the tree
CImageList* pTreeImgList = GetImageList(TVSIL_NORMAL );
CClientDC dc(this);
CDC cMemDC;
CBitmap cBitmap;
int ImgWidth,ImgHeight;
//Loop through all the selected items and get the size of the bitmap to be created
int iDragItemCount = 0;
CRect rc;
for( ; iDragItemCount < cArrDragItems.size(); iDragItemCount++ )
{
HTREEITEM hSelItem = cArrDragItems[iDragItemCount];
CRect itemRect;
GetItemRect( hSelItem ,&itemRect,false);
CImageList* pImgList = GetImageList( TVSIL_NORMAL );
ImageList_GetIconSize( pImgList->GetSafeHandle(),&ImgWidth,&ImgHeight );
if( iDragItemCount == 0 )
{
rc.left = 0;
rc.top = 0;
rc.right = ImgWidth + itemRect.Width();
rc.bottom = ImgHeight;
}
else
{
if( rc.Width() < (itemRect.Width()+ImgWidth) )
{
rc.right = itemRect.Width()+32;
}
rc.bottom += itemRect.Height();
}
}
//Create the memory dc
if(!cMemDC.CreateCompatibleDC(&dc))
return NULL;
//Create the bitmap of the required size
if(!cBitmap.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height()))
return NULL;
CBitmap* pOldMemDCBitmap = cMemDC.SelectObject( &cBitmap );
//Fill the bitmap with green color which is used as the mask
cMemDC.FillSolidRect(0,0,rc.Width(), rc.Height(), RGB(0, 255, 0));
CFont * pFontOld = cMemDC.SelectObject(GetFont());
//Loop through all the selected items and get their image
iDragItemCount = 0;
CImageList* pSelImageList = NULL;
CImageList * pImageList = GetImageList(TVSIL_NORMAL);
CRect imgRect,textRect,itemRect;
for( ; iDragItemCount < cArrDragItems.size(); iDragItemCount++ )
{
HTREEITEM hSelItem = cArrDragItems[iDragItemCount];
PCUSTOMITEMDATA pData = (PCUSTOMITEMDATA) GetItemData(hSelItem);
CString str;
str = GetItemText( hSelItem );
//if( iDragItemCount == 0 )
GetItemRect( hSelItem,&itemRect,true );
if( iDragItemCount == 0 )
{
imgRect.left = 0;
imgRect.top = 0;
imgRect.right = ImgWidth;
imgRect.bottom = ImgHeight;
//If the item has check box
if( pData->m_bChecked )
{
textRect.left = ImgWidth;
}
else
{
//Left shift the textRect by half the width of the imageRect width as
//there is an empty space between the image of the item and the text
textRect.left = ImgWidth/2;
}
textRect.top = 0;
textRect.right = textRect.left + itemRect.Width();
textRect.bottom = itemRect.Height();
}
else
{
imgRect.top += ImgHeight;
imgRect.bottom += ImgHeight;
//If the item has check box
if( pData->m_bChecked )
{
textRect.left = ImgWidth;
}
else
{
//Left shift the textRect by half the width of the imageRect width as
//there is an empty space between the image of the item and the text
textRect.left = ImgWidth/2;
}
textRect.top = textRect.bottom;
textRect.right = textRect.left + itemRect.Width();
textRect.bottom = textRect.top + itemRect.Height();
}
if( hSelItem )
{
//Get the image of the selected item
int nImg, nSelImg;
GetItemImage(hSelItem,nImg,nSelImg);
HICON hIcon = pImageList->ExtractIcon(nImg);
//Draw this icon on to the memory dc
::DrawIconEx(cMemDC.m_hDC, imgRect.left, imgRect.top, hIcon, ImgWidth, ImgHeight, 0, 0, DI_NORMAL);
//Next draw the text
cMemDC.DrawText(str, textRect, DT_LEFT| DT_VCENTER | DT_SINGLELINE|DT_NOPREFIX);
DestroyIcon(hIcon);
}
}
cMemDC.SelectObject( pOldMemDCBitmap );
pNewImageList = new CImageList;
BITMAP bm;
cBitmap.GetBitmap(&bm);
CBitmap bmp;
pNewImageList->Create( bm.bmWidth,bm.bmHeight,ILC_COLOR32 | ILC_MASK,1,1);
// Here green is used as mask color
pNewImageList->Add(&cBitmap, RGB(0, 255, 0));
return pNewImageList;
}
///***************************************************************************
void CVersatileTreeCtrl::OnClickLButton(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
//TODO: Add your control notification handler code here
//Do the hittest to find out which item is being clicked
CPoint pt;
//Get the current position of the cursor
::GetCursorPos(& pt );
//Cursor position is in screen coordiantes.Convert it to the client
//Systen
ScreenToClient(&pt );
HTREEITEM hClickItem = HitTest(pt);
if( !hClickItem )
return;
//Get the control and shift key states
bool bCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
bool bShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
HTREEITEM hSelItem = GetSelectedItem() ;
//If the Control Key is ON
if( bCtrl )
{
//NOTE1: If the clicked item was already has the focus then no TVN_SELCHANGE
//message will be genereated.So I'm handling this situation here;
//If the item was selected already deselect it otherwise select it
//if( (hSelItem==hClickItem) && GetItemState( hClickItem,TVIS_SELECTED) & TVIS_SELECTED )
if( GetItemState( hClickItem,TVIS_SELECTED) & TVIS_SELECTED )
{
SetItemState( hClickItem,~TVIS_SELECTED,TVIS_SELECTED);
//Once after deselecting the item, it goes into edit mode.To avoid this
//I'm just setting the *pResult = 1 which avoids editing the label
*pResult = 1;
//Send the notification to the parent dialog about the selected items
CollectSelectedItems();
GetParent()->SendMessage( TVN_SELECTION_CHANGED,(WPARAM)(&m_vecSelectedItems),NULL );
}
UpdateWindow();
}
}
//***************************************************************************
void CVersatileTreeCtrl::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
//Get the old selected item and new item
HTREEITEM hOldSelItem = pNMTreeView->itemOld.hItem;
HTREEITEM hNewSelItem = pNMTreeView->itemNew.hItem;
CString str1,str2;
str1 = GetItemText( hOldSelItem );
str2= GetItemText( hNewSelItem );
//Get the action by which the selection is generated
UINT& action = pNMTreeView->action;
//Get the shift and control key state
bool bCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
bool bShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
//If control key is ON then select the old item also.Anyhow the new item is selected
//OnSelchanging
if((action == TVC_BYMOUSE && bCtrl) )
{
//Select the old item
if( hOldSelItem && m_bOldItemSelected )
{
SetItemState(hOldSelItem, UINT(TVIS_SELECTED), UINT(TVIS_SELECTED));
}
}
//Similary if already item1 is in selection.Now SHift + Down arrow key is pressed
//Old = item1 and new = item2 and i have to select both item1 and item2
else if ( (action == TVC_BYKEYBOARD) && bShift )
{
//Select the old item
if( hOldSelItem && m_bOldItemSelected )
{
SetItemState(hOldSelItem, UINT(TVIS_SELECTED), UINT(TVIS_SELECTED));
}
}
//If the selection is by mouse and shfit key is also used then select all the items
//starting from old to new.All the intermediate items between old to new is selected
else if( (action == TVC_BYMOUSE) && bShift )
{
bool bSelect = true;
//Suppose if the hOldSelItem is down in the hierarchy than the hNewSelItem
//swap the items for looping
CRect rc1 ,rc2;
GetItemRect(hOldSelItem,&rc1,true );
GetItemRect(hNewSelItem,&rc2,true );
if( rc2.top < rc1.top )
IterateItems( SelectItems,hNewSelItem,hOldSelItem,&bSelect );
else
IterateItems( SelectItems,hOldSelItem,hNewSelItem,&bSelect );
}
//If control and shift key is in off state and user clicked on an item.Means he wants to
//remove whatever the previous selected items and wants to select only the clicked item
if( !bCtrl && !bShift && hNewSelItem )
{
//Remove all the old selection
bool bSelect = false;
IterateItems( SelectItems,NULL,NULL,&bSelect );
//Select only the new item
SetItemState(hNewSelItem, UINT(TVIS_SELECTED), UINT(TVIS_SELECTED));
}
//May be the developer who uses the control programatically sets the selection.
//He wants to retain the old selection in addition to the new item
if( action == TVC_UNKNOWN && m_bOldItemSelected && hOldSelItem )
{
SetItemState(hOldSelItem, UINT(TVIS_SELECTED), UINT(TVIS_SELECTED));
}
UpdateWindow() ;
*pResult = 0;
//Send the notification to the parent dialog about the selected items
CollectSelectedItems();
GetParent()->SendMessage( TVN_SELECTION_CHANGED,(WPARAM)(&m_vecSelectedItems),NULL );
//Invalidate();
}
//***************************************************************************
void CVersatileTreeCtrl::OnSelchanging(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
// TODO: Add your control notification handler code here
//Get the old selected item and new item
HTREEITEM hOldSelItem = pNMTreeView->itemOld.hItem;
HTREEITEM hNewSelItem = pNMTreeView->itemNew.hItem;
//-------------------------------------------------------------------------------------------------------------------------
//Get the seletion state of the old item.This will become false in the following 2 cases.
//CASE1: when the user does the first selection ie., there is no selection at all in the tree,
// and user clicks an item for selection.In this case olditem = NULL
// and newitem=clickeditem.
//CASE2: Select item1 first [ newitem=clickeditem[!TVIS_SELECTED] olditem = NULL].
// Press ctrl key and select item2[ newitem=clickeditem[!TVIS_SELECTED] olditem = item1[TVIS_SELECTED]].
// Press ctrl key and deselect item2.Now in this case we wont get control here.It will be
// handled by OnClickLButton which deselects item2.Again press ctrl key and deselect the
// item1 [ newitem=item[TVIS_SELECTED] olditem = item2[!TVIS_SELECTED]].
m_bOldItemSelected = hOldSelItem && (GetItemState(hOldSelItem, UINT(TVIS_SELECTED)) & TVIS_SELECTED);
//-----------------------------------------------------------------------------------------------------------------------------
//Get the shift and control key state
bool bCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
bool bShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
//Get the action by which the selection is generated
UINT& action = pNMTreeView->action;
//If the action is by mouse and ctrl is key on at the same time
if((action == TVC_BYMOUSE && bCtrl) )
{
//If the new item was already in selected state, then deselect it.This may happen say for example
//item1 is first selected and by pressing ctrl key item2 is selected.Now we have 2 items in selection
//Now if the user presses ctrl key and clicks on item1.Now our olditem is item2[TVIS_SELECTED] and new item is
//item1[TVIS_SELECTED] and at the same time item1 is in the selected state and our ctrl key is ON
if( hNewSelItem )
{
if( (GetItemState(hNewSelItem,TVIS_SELECTED)&TVIS_SELECTED) )
{
//Deselect the new item
SetItemState(hNewSelItem, UINT(~TVIS_SELECTED), UINT(TVIS_SELECTED));
UpdateWindow() ;
//abort change of selection.Otherwise TVN_SELCHANGED message will be generated.
*pResult = 0 ;
}
}
}
//If say item1 and item2 is already selected.Now the user presses shift + UP arrow key.
//Now our olditem = item2[TVIS_SELECTED] and newitem = item1[TVIS_SELECTED].So we have
//to keep the item1 as selected and item2 as deselected one.
if( (action== TVC_BYKEYBOARD) && bShift )
{
//If the new item is in selected state then remove the selection of the old item
//i.e, the selection of item2
if( GetItemState(hNewSelItem,TVIS_SELECTED)&TVIS_SELECTED )
m_bOldItemSelected = false;
}
*pResult = 0;
}
//***************************************************************************
void CVersatileTreeCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_KEYDOWN* pTVKeyDown = (TV_KEYDOWN*)pNMHDR;
// TODO: Add your control notification handler code here
CollectSelectedItems();
if( pTVKeyDown->wVKey == VK_DELETE )
{
//Delete the selected items
int iSelItemCnt = 0;
for( ; iSelItemCnt < m_vecSelectedItems.size(); iSelItemCnt++ )
{
SetItemData( m_vecSelectedItems[iSelItemCnt],0 );
DeleteItem( m_vecSelectedItems[iSelItemCnt] );
}
}
if( pTVKeyDown->wVKey == VK_F2 )
{
if( m_vecSelectedItems.size() == 1 )
EditLabel( m_vecSelectedItems[0] );
}
UpdateWindow();
*pResult = 0;
}
/*****************************************************************************
/DESCRIPTION:
/-----------
/ To add the image prepared with the checkbox into the list
/
/PARAM
/------
/ lpInsertStruct[in] - Custom data of item to be inserted
/
/RESULT:
/-------
/ Index of the image in the imagelist
*/
int CVersatileTreeCtrl::AddImageToList( PCUSTOMINSERTSTRUCT lpInsertStruct )
{
//HERE WE HAVE 8 BITMAPS TOTALLY.LETS SEE
// 1. CHECK IMAGE
// 2. MASK FOR THE CHECK
// 3. NORMAL IMAGE OF ITEM
// 4. MASK IMAGE OF THE ITEM
// 5. UNCHECK IMAGE
// 6. MASK FOR THE UNCHCKED.THIS IS SAME AS #2
// 7. EXPANDED IMAGE [SOME ITEMS DOESNT HAVE EXPANDED IMAGE.IN THIS CASE THIS IS SAME AS #3]
// 8. MASK FOR THE EXPANDED IMAGE
//Resource ID for the normal image
int iBmpID = lpInsertStruct->m_pCustomData->m_uNormalImage;
//Resource ID for the expanded image
int iExpandedBmpID = lpInsertStruct->m_pCustomData->m_uExpandedImage;
//Get checkbox is needed for the item and state of the checkbox
bool bNeedChkBox = lpInsertStruct->m_pCustomData->m_bChecked;
bool bCheckState = lpInsertStruct->m_pCustomData->m_bCheckState;
//Index of the inserted image from the image list
int index = 0;
//Resource ID's of the prefixing bitmaps [CHECKED/UNCHECKED/AND MASK FOR THE TWO]
int noPrefixBmpID,ChkBmpID,unChkBmpID;
//If check box is needed then assign the resource for the CHECK,UNCHECK AND MASK
if( bNeedChkBox )
{
noPrefixBmpID = IDB_BITMAP_EMPTY;
ChkBmpID = IDB_BITMAP_CHECK_16;
unChkBmpID = IDB_BITMAP_UNCHECK;
}
//Otherwise all the check/uncheck and their mask are empty
else
{
noPrefixBmpID = ChkBmpID = unChkBmpID = IDB_BITMAP_EMPTY;
}
//Add the normal state image
index = AddNoramlStateImages( iBmpID,iExpandedBmpID,lpInsertStruct);
if( index )
return 0;
//Add the images for the check state
AddCheckStateImages( iBmpID,ChkBmpID,unChkBmpID,lpInsertStruct->m_pCustomData );
//Add the expanded state images
AddExpStateImages( iBmpID,iExpandedBmpID,ChkBmpID,unChkBmpID,lpInsertStruct->m_pCustomData );
if( iBmpID == iExpandedBmpID )
{
lpInsertStruct->m_pCustomData->m_iCheckedExpandedIndex = lpInsertStruct->m_pCustomData->m_iCheckedNormalIndex;
lpInsertStruct->m_pCustomData->m_iUnCheckedExpandedIndex = lpInsertStruct->m_pCustomData->m_iUnCheckedNormalIndex;
}
return 0;
}
/*****************************************************************************
/DESCRIPTION:
/-----------
/ To add the images for normal and expanded state [ WITHOUT CHECKBOX ]
/
/PARAM
/------
/ uBmpID[in] - ID for the normal image
/ uExpBmpID[in] - ID for the expanded image
/ lpInsertStruct[in] - Custom Insert structure
/
/RESULT:
/-------
/ Index of the normal image
*/
int CVersatileTreeCtrl::AddNoramlStateImages( UINT uBmpID,UINT uExpBmpID,PCUSTOMINSERTSTRUCT lpInsertStruct )
{
if( !lpInsertStruct )
return -1;
//if already the image is there
int iBmpFound = -1;
iBmpFound = GetBitmapIndexFromImgList( uBmpID );
//If bitmap is found then return
int addIndex = 0;
if( iBmpFound > -1 )
{
vector<int> &vecExistingImages = m_mapBitmapID[uBmpID];
lpInsertStruct->m_pCustomData->m_iNormalIndex = vecExistingImages[IMAGE_NORMAL_INDEX ];
lpInsertStruct->m_pCustomData->m_iExpandedIndex = vecExistingImages[IMAGE_EXPAND_INDEX];
lpInsertStruct->m_pCustomData->m_iCheckedNormalIndex = vecExistingImages[IMAGE_CHECKED_NORMAL_INDEX ];
lpInsertStruct->m_pCustomData->m_iUnCheckedNormalIndex = vecExistingImages[IMAGE_UNCHECKED_NORMAL_INDEX ];
lpInsertStruct->m_pCustomData->m_iCheckedExpandedIndex = vecExistingImages[IMAGE_CHECKED_EXPANDED_INDEX ];
lpInsertStruct->m_pCustomData->m_iUnCheckedExpandedIndex= vecExistingImages[IMAGE_UNCHECKED_EXPANDED_INDEX ];
if( !lpInsertStruct->m_pCustomData->m_bChecked )
{
lpInsertStruct->m_tvIS.item.iImage = lpInsertStruct->m_pCustomData->m_iNormalIndex;
lpInsertStruct->m_tvIS.item.iSelectedImage = lpInsertStruct->m_pCustomData->m_iNormalIndex;
}
else
{
lpInsertStruct->m_tvIS.item.iImage = lpInsertStruct->m_pCustomData->m_iCheckedNormalIndex;
lpInsertStruct->m_tvIS.item.iSelectedImage =lpInsertStruct->m_pCustomData->m_iCheckedNormalIndex;
}
return 1;
}
vector<int> vecImages;
//PREPARE NORMAL IMAGE
int iNormndex = AddImage( uBmpID,IDB_BITMAP_EMPTY );
lpInsertStruct->m_pCustomData->m_iNormalIndex = iNormndex;
lpInsertStruct->m_pCustomData->m_iExpandedIndex = iNormndex;
//If the item is given expansion image also
if( uBmpID != uExpBmpID )
{
//Prepare expanded image with out any checkbox
int iExpIndex = AddImage( uExpBmpID,IDB_BITMAP_EMPTY );
lpInsertStruct->m_pCustomData->m_iExpandedIndex = iExpIndex;
}
vecImages.push_back( lpInsertStruct->m_pCustomData->m_iNormalIndex );
vecImages.push_back( lpInsertStruct->m_pCustomData->m_iExpandedIndex );
m_mapBitmapID.insert( make_pair(uBmpID,vecImages) );
return 0;
}
/*****************************************************************************
/DESCRIPTION:
/-----------
/ To add the images for the check,uncheck images for normal state
/
/PARAM
/------
/ uBmpID[in] - ID for the normal image
/ uCheckBmpID[in] - ID of the check image
/ uUnCheckBmpID[in] - ID of the uncheck image
/ pCustData[in] - Custom data where the image index is stored
/
/RESULT:
/-------
/ Index of the image
*/
int CVersatileTreeCtrl::AddCheckStateImages(UINT uBmpID,UINT uCheckBmpID,UINT uUnCheckBmpID,PCUSTOMITEMDATA pCustData )
{
if( !pCustData )
return -1;
//Get the index of the normal image
int iBmpFound = -1;
iBmpFound = GetBitmapIndexFromImgList( uBmpID );
if( iBmpFound == -1 )
return -1;
vector<int> &vecExistingImages = m_mapBitmapID[uBmpID];
if( !pCustData->m_bChecked )
{
vecExistingImages.push_back( -1 );
vecExistingImages.push_back( -1 );
return -1;
}
//Prepare the normal state image for the item with check box ON
//1. Append checkbox image with the image represeting the item
//2. If the item does not have check box, then add an empty image with the image represeting the item
int iNormCheckIndex = AddImage( uBmpID,uCheckBmpID);
pCustData->m_iCheckedNormalIndex = iNormCheckIndex;
int iNormUnCheckIndex = -1;
//Prepare NORMALIMAGE with UNCHECK.Add the UNCHECK with NORMAL IMAGE.In case if the
//item doesn't have check box CHECK is replaced by EMPTY
if( uUnCheckBmpID != uCheckBmpID )
iNormUnCheckIndex = AddImage( uBmpID,uUnCheckBmpID );
pCustData->m_iUnCheckedNormalIndex = iNormUnCheckIndex;
vecExistingImages.push_back( pCustData->m_iCheckedNormalIndex );
vecExistingImages.push_back( pCustData->m_iUnCheckedNormalIndex );
return iNormCheckIndex;
}
/*****************************************************************************
/DESCRIPTION:
/-----------
/ To add the images for the check,uncheck images for normal state
/
/PARAM
/------
/ uBmpID[in] - ID for the normal image
/ uExpBmpID[in] - ID for the expanded image
/ uCheckBmpID[in] - ID of the check image
/ uUnCheckBmpID[in] - ID of the uncheck image
/ pCustData[in] - Custom data where the image index is stored
/
/RESULT:
/-------
/ Index of the image
*/
int CVersatileTreeCtrl::AddExpStateImages(UINT uBmpID,UINT uExpBmpID,UINT uCheckBmpID,UINT uUnCheckBmpID,PCUSTOMITEMDATA pCustData )
{
if( !pCustData )
return -1;
vector<int> &vecExistingImages = m_mapBitmapID[uBmpID];
//If there is no specific expanded image given to the item, the image representing normal and expanded
//states are used to represet expanded state as well
if( uBmpID == uExpBmpID )
{
m_mapBitmapID[uBmpID].push_back( m_mapBitmapID[uBmpID].at(IMAGE_CHECKED_NORMAL_INDEX ) );
m_mapBitmapID[uBmpID].push_back( m_mapBitmapID[uBmpID].at(IMAGE_UNCHECKED_NORMAL_INDEX ) );
return -1;
}
//Get the index of the normal image
int iBmpFound = -1;
iBmpFound = GetBitmapIndexFromImgList( uBmpID );
if( iBmpFound == -1)
return -1;
//Prepare the expanded image for the item with check box ON
//1. Append checkbox image with the image represeting the item
//2. If the item does not have check box, then add an empty image with the image represeting the item
int iExpCheckndex = AddImage( uExpBmpID,uCheckBmpID );
pCustData->m_iCheckedExpandedIndex = iExpCheckndex;
//Prepare the expanded image for the item with check box OFF
//1. Append checkbox OFF image with the image represeting the item.
//2. If the item does not have check box, then add an empty image with the image represeting the item
int iExpUncheckIndex = AddImage( uExpBmpID,uUnCheckBmpID );
pCustData->m_iUnCheckedExpandedIndex = iExpUncheckIndex;
//Add the images to the vector holding all the images associated with the item
vecExistingImages.push_back( pCustData->m_iCheckedExpandedIndex );
vecExistingImages.push_back( pCustData->m_iUnCheckedExpandedIndex );
return iExpCheckndex;
}
/*****************************************************************************
/DESCRIPTION:
/-----------
/ To prepare the bitmap along with the check box
/
/PARAM
/------
/ nBmpID[in] - ID of the bitmap
/ resBmp[in] - Prepared Bitmap object
/ newBmpW[out] - Width of the new bitmap
/ newBmpH[out] - Height of the newbitmap
/ iPrefixBmpID[in] - ID of the bitmap to be prefixed [ CHECK/UNCHECK/EMPTY]
/
/RESULT:
/-------
/ Index of the image if it is already in the image list,otherwise 0
*/
int CVersatileTreeCtrl::PrepareImageWithPrefixBmp( UINT nBmpID,CBitmap& resBmp,int& newBmpW,int& newBmpH,UINT iPrefixBmpID /*= -1*/ )
{
CBitmap bmp;
//Create the dc
CClientDC dc( this );
//Load the given bimap resource
bmp.LoadBitmap( nBmpID );
BITMAP bm,chkbm;
//res is Nonzero if successful; otherwise 0.
int res = bmp.GetBitmap(&bm);
//Get the size of the checkbox bitmap
CBitmap chkBmp;
chkBmp.LoadBitmap( iPrefixBmpID );
res = chkBmp.GetBitmap(&chkbm);
newBmpW = 2* bm.bmWidth;
newBmpH = bm.bmHeight;
//If prefix bitmap is empty then add the image first and empty bitmap viceversa
bool prefixFirst = true;
if( IDB_BITMAP_EMPTY == iPrefixBmpID )
prefixFirst = false;
if( chkbm.bmWidth != bm.bmWidth || chkbm.bmHeight != bm.bmHeight )
{
//Get the size of the bitmap according to that checkbox bitmap has to be
//scaled
CBitmap scaledBmp;
ScaleBitmap( chkBmp, chkbm.bmWidth,chkbm.bmHeight, bm.bmWidth,bm.bmHeight,scaledBmp);
if( prefixFirst )
{
AddBitamps( &scaledBmp,&bmp,&resBmp,newBmpW,newBmpH );
}
else
{
AddBitamps( &bmp,&scaledBmp,&resBmp,newBmpW,newBmpH );
}
}
else
{
if( prefixFirst )
{
AddBitamps( &chkBmp,&bmp,&resBmp,newBmpW,newBmpH );
}
else
{
AddBitamps( &bmp,&chkBmp,&resBmp,newBmpW,newBmpH );
}
}
return -1;
}
/*****************************************************************************
/DESCRIPTION:
/-----------
/ To scale a given bitmp
/
/PARAM
/------
/ bmp[in] - Bitmap to be scaled
/ Width[in] - width of the bitmap
/ Height[in] - Height of the bitmap
/ newWidth[in] - New width of the bitmap
/ newHeight[in] - New Height of the bitmap
/ resBmp[out] - Resulting scaled bitmap
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::ScaleBitmap( CBitmap& bmp,int Width,int Height,int newWidth,int newHeight,CBitmap& resBmp )
{
CClientDC dc( this );
//Create the memory dc
CDC cMemDC1,cMemDC2;
cMemDC1.CreateCompatibleDC(&dc );
cMemDC2.CreateCompatibleDC(&dc );
//Select the bitmap into the memdc
CBitmap* pOldBmp1 = cMemDC1.SelectObject(&bmp);
//Create a bitmap to the new size and width
resBmp.CreateCompatibleBitmap(&dc,newWidth,newHeight );
CBitmap* pOldBmp2 = cMemDC2.SelectObject(&resBmp );
//Now copy the bitmap from memdc1 to memdc2
cMemDC2.StretchBlt(0,0,newWidth,newHeight,&cMemDC1,0,0,Width,Height,SRCCOPY);
cMemDC1.SelectObject(pOldBmp1);
cMemDC2.SelectObject(pOldBmp2);
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To combine to given bitmaps
/
/PARAM
/------
/ pBmp1[in] - First bitmap Reference
/ pBmp2[in] - Second bitmap Reference
/ resBmp[in] - Resulting bitmap
/ W[in] - New height of the bitmap.If -1 then autocomputation is done
/ H[in] - New height of the bitmap.If -1 then autocomputation is done
/
/RESULT:
/-------
/ void
*/
bool CVersatileTreeCtrl:: AddBitamps( CBitmap* pBmp1,CBitmap* pBmp2,CBitmap* resBmp,int W /*=-1*/,int H/*=-1*/ )
{
int newBmpW = W;
int newBmpH = H;
CClientDC dc(this);
BITMAP bm1,bm2;
pBmp1->GetBitmap(&bm1);
pBmp2->GetBitmap(&bm2);
//If the user doesnt know the new size of the bitamp then compute it
if( newBmpW == -1 || newBmpH == - 1)
{
newBmpW = bm1.bmWidth + bm2.bmWidth;
newBmpH = bm1.bmHeight + bm2.bmHeight;
}
//Create the 3 memory DCs.
CDC cMemDC1,cMemDC2,CMemDCTgt;
//This memory dc is used to copy the checkbox bitmap
if(!cMemDC1.CreateCompatibleDC(&dc))
return false;
//This memory dc is used to copy the image bitmap of the item
if(!cMemDC2.CreateCompatibleDC(&dc))
return false;
//This dc is used to create and copy the combined bitmap [ checkbox bmp + image bmp ]
if(!CMemDCTgt.CreateCompatibleDC(&dc))
return false;
//First copy the check box bitmap into memDC1
CBitmap* oldBmp1 = cMemDC1.SelectObject( pBmp1 );
//Copy the image bitmap into memDC2
CBitmap* oldBmp2 = cMemDC2.SelectObject( pBmp2 );
//Create the bitmap of the required size [ Combine both the bitmaps ]
if(!resBmp->CreateCompatibleBitmap(&dc, newBmpW, newBmpH ))
return false;
//Now select the combined bitmap into the CMemDCTgt
CBitmap* oldBmp3 = CMemDCTgt.SelectObject( resBmp );
//Then first copy the checkbox bitmap from the position 0,0 on the target
CMemDCTgt.BitBlt( 0,0,bm1.bmWidth,bm1.bmHeight,&cMemDC1,0,0,SRCCOPY );
//Next copy the image bitmap from the position (chkbm.bmWidth,chkbm.bmHeight ) on the
//target.Because already we have copied checkbox.
CMemDCTgt.BitBlt( bm1.bmWidth,0,bm2.bmWidth,bm2.bmHeight,&cMemDC2,0,0,SRCCOPY );
//Now restore the oldbitmaps
cMemDC1.SelectObject( oldBmp1 );
cMemDC2.SelectObject( oldBmp2 );
CMemDCTgt.SelectObject( oldBmp3 );
//SaveBitmap( dc.GetSafeHdc(),*resBmp,"D:\\chk.bmp");
return true;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ Message handler for the check box click
/
/PARAM
/------
/ pNMHDR[in] - Custom data structure
/
/RESULT:
/-------
/ void
*/
LRESULT CVersatileTreeCtrl::OnChkBoxClicked( WPARAM wp, LPARAM lp)
{
LPCUSTNMHDR pNMHDR = ( LPCUSTNMHDR)wp;
PCUSTOMITEMDATA pCustData = (PCUSTOMITEMDATA)( pNMHDR->m_data );
if( !pCustData )
return 1;
if( pCustData->m_bChecked )
{
//Set the new state
pCustData->m_bCheckState = !pCustData->m_bCheckState;
if( pCustData->m_bCheckState )
{
m_vecCheckedItems.insert( pNMHDR->m_hItem );
}
else
{
m_vecCheckedItems.erase( pNMHDR->m_hItem );
}
bool bItemExpanded = false;
if( GetItemState( pNMHDR->m_hItem,TVIS_EXPANDED) & TVIS_EXPANDED)
bItemExpanded = true;
//Set the image for the item based on its state
SetImage( pNMHDR->m_hItem );
GetParent()->SendMessage( TVN_ITEM_CHECK_TOGGLE,(WPARAM)(&m_vecCheckedItems),NULL );
}
return 0;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To get the index of the bitmap from the image list
/
/PARAM
/------
/ uBitmapID[in] - ID of the bitmap
/
/RESULT:
/-------
/ Index of the bitmap if found,-1 otherwise
*/
int CVersatileTreeCtrl::GetBitmapIndexFromImgList( UINT uBitmapID )
{
//Search the bitmap in the m_mapBitmapID
int iBmpCtr = 0;
map<UINT, vector<int> >::iterator it = m_mapBitmapID.find(uBitmapID);
if( it != m_mapBitmapID.end( ) )
{
return ( std::distance( m_mapBitmapID.begin( ), it ) );
}
return -1;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To add a given image and its mask to the list
/
/PARAM
/------
/ uBmpID[in] - ID of the bitmap
/ uPrefixBmpID - Resource id of the prefix bitmap
/
/RESULT:
/-------
/ Index of the bitmap in the image list
*/
int CVersatileTreeCtrl::AddImage( UINT uBmpID,UINT uPrefixBmpID )
{
int newBmpW,newBmpH;
CBitmap imageBmp;
CClientDC dc(this);
//Else prepare the image with its prefix.Similarly the mask of the image with its prefix
PrepareImageWithPrefixBmp( uBmpID,imageBmp,newBmpW,newBmpH,uPrefixBmpID );
//If there is no image list create it
if( !m_pImgList )
{
m_pImgList = new CImageList;
m_pImgList->Create( newBmpW,newBmpH,ILC_COLOR32 | ILC_MASK,5,5);
SetImageList(m_pImgList,TVSIL_NORMAL);
}
HBITMAP hNmp = imageBmp.operator HBITMAP();
int index = -1;
index = m_pImgList->Add( &imageBmp,m_cMaskColor );
//Get the image for the respective index
IMAGEINFO imgInfo;
m_pImgList->GetImageInfo(index,&imgInfo);
return index;
}
//***************************************************************************
void CVersatileTreeCtrl::OnPaint()
{
CTreeCtrl::OnPaint();
int n = GetVisibleCount();
IterateItems( DrawItem,NULL,NULL,NULL );
}
//***************************************************************************
void CVersatileTreeCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
TV_HITTESTINFO tvHitInfo;
tvHitInfo.pt = point;
m_hRClickItem = NULL;
m_hRClickItem = HitTest(&tvHitInfo );
//Get the custom data of the item
if( m_hRClickItem )
{
//If the item was already in selected state, then show only the context menu
if( GetItemState( m_hRClickItem,TVIS_SELECTED ) & TVIS_SELECTED )
{
//Dont do any thing. Just show the context menu
}
else
{
//If there are some already selected items, deselect them and select the right clicked item
//and show the context menu
int iSelItemCtr = 0;
for( ; iSelItemCtr < m_vecSelectedItems.size( ); iSelItemCtr++ )
{
SetItemState(m_vecSelectedItems[iSelItemCtr],~TVIS_SELECTED,TVIS_SELECTED );
}
SelectItem(NULL);
//Select the right clicked item
SetItemState(m_hRClickItem,TVIS_SELECTED,TVIS_SELECTED );
//As this is the only selected item, have only this in the m_vecSelectedItems
m_vecSelectedItems.clear( );
m_vecSelectedItems.push_back( m_hRClickItem );
GetParent()->SendMessage( TVN_SELECTION_CHANGED,(WPARAM)(&m_vecSelectedItems),NULL );
}
//Show the menu associated with the right clicked item
PCUSTOMITEMDATA pCustomData = (PCUSTOMITEMDATA) GetItemData(m_hRClickItem);
//Get the menu id associated with the item
UINT uMenuID = pCustomData->m_uMenuID;
if( uMenuID == -1 )
return;
//Load the menu
CMenu menu;
menu.LoadMenu( uMenuID );
//Get the rectangle of the the item
CRect rc;
GetItemRect( m_hRClickItem,&rc,true);
CPoint bottomRight;
bottomRight.x = rc.right;
bottomRight.y = rc.bottom;
ClientToScreen(&bottomRight);
CMenu* pMenu = menu.GetSubMenu(0);
pMenu->TrackPopupMenu( TPM_LEFTBUTTON ,bottomRight.x,bottomRight.y,GetParent() );
}
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To Create the disabled image for an item
/
/PARAM
/------
/ bmp[out] - Resuting disabled image
/
/
/RESULT:
/-------
/ void
*/
void CVersatileTreeCtrl::GenerateDisableImage( CBitmap& bmp )
{
SetBitMapColor( bmp,::GetSysColor(COLOR_GRAYTEXT),RGB(0,128,128) );
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To set the color in the bitmap
/
/PARAM
/------
/ bmp[out] - Resuting disabled image
/ color[in] - Color to be set for each pixel of the bitmap
/ excludeColor - Color to be excluded from setting
/
/
/RESULT:
/-------
/ Index of the bitmap if found,-1 otherwise
*/
void CVersatileTreeCtrl::SetBitMapColor( CBitmap& bmp,COLORREF color,COLORREF excludeColor)
{
CClientDC dc(this);
//Get bitmap size and height
BITMAP bm;
bmp.GetBitmap(&bm);
WORD biBits = 32 * bm.bmPlanes;
//Get the info about the bitmap
BITMAPINFO bi;
ZeroMemory(&bi,sizeof(bi));
bi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
bi.bmiHeader.biHeight = bm.bmHeight;
bi.bmiHeader.biWidth = bm.bmWidth;
bi.bmiHeader.biPlanes = bm.bmPlanes;
bi.bmiHeader.biBitCount = biBits;
//Get the size of the bitmap in number of bytes.Pass 5th param as NULL.So
//that it will give you the size
GetDIBits( dc,bmp,0,bm.bmHeight,NULL,&bi,DIB_RGB_COLORS);
//Next create the array of bytes to get the color values
//NOTE: lower bottom of the bitmap is origin to take the bytes.i.e, the lower bottom
//line is first parsed,then the line above the lower bottom etc.,
int nbBytes = bi.bmiHeader.biSizeImage;
LPBYTE pBits = new BYTE[ nbBytes ];
//Get the color values from the bitmap
GetDIBits( dc,bmp,0,bm.bmHeight,pBits,&bi,DIB_RGB_COLORS);
int byteCtr = 0;
if( bi.bmiHeader.biBitCount == 32 )
{
//if biBitCount = 32 then each pixel is represented by 32 bits.That is 4 bytes.
//Highbyte is not used.RGB is in reversed order i.e, 2,1,0 and not 0,1,2
for( ; byteCtr < nbBytes/4; byteCtr++ )
{
BYTE red = pBits[ byteCtr*4+2];
BYTE green = pBits[ byteCtr*4+1];
BYTE blue = pBits[ byteCtr*4+0];
COLORREF col = RGB( red,green,blue );
if( col != excludeColor )
{
pBits[ byteCtr*4+2] = GetRValue( color );
pBits[ byteCtr*4+1] = GetGValue( color );
pBits[ byteCtr*4+0] = GetBValue( color );
}
}
//Set the newly set color array to the bitmap
SetDIBits( dc,bmp,0,bm.bmHeight,pBits,&bi,DIB_RGB_COLORS);
delete pBits;
pBits = NULL;
//SaveBitmap( dc.GetSafeHdc(),bmp,"D:\\chk.bmp");
}
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To get the bitmap of the image from the image list
/
/PARAM
/------
/ iIndex[in] - Index of the image
/ bmp[out] - Resulting bitmap
/
/
/RESULT:
/-------
/ True if successfull,false otherwise
*/
bool CVersatileTreeCtrl::GetBitampFromImageList( int iIndex,CBitmap& bmp )
{
//Get the image list of the tree control
CImageList* pImageList = GetImageList(TVSIL_NORMAL);
if( !pImageList )
return false;
//Get the count of images in the imagelist
int iImgCt = pImageList->GetImageCount();
if( iIndex < 0 || iIndex >= iImgCt )
return false;
//Get the size of image at the specified index
int imgW,imgH;
ImageList_GetIconSize( pImageList->GetSafeHandle(),&imgW,&imgH);
//Extract the icon of the item
HICON hIcon = pImageList->ExtractIcon(iIndex);
//Convert the icon into bitmap
GetBitmapFromIcon( hIcon,imgW,imgH,bmp);
return true;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To convert the icon into bitmap
/
/PARAM
/------
/ hIcon[out] - Handle to the icon to be converted
/ ImgWidth[in] - Width of the icon
/ ImgHeight[in] - Height of the icon
/
/
/RESULT:
/-------
/ True if successful,false otherwise
*/
bool CVersatileTreeCtrl::GetBitmapFromIcon( HICON hIcon,int ImgWidth,int ImgHeight,CBitmap& cBitmap )
{
if( !hIcon )
return false;
//Create DC
CClientDC dc(this);
//Create memory dc
CDC cMemDC;
if ( !cMemDC.CreateCompatibleDC(&dc) )
return false;
//Create the bitmap of required size
if(!cBitmap.CreateCompatibleBitmap(&dc, ImgWidth, ImgHeight))
return false;
//Select the bitmap into the memory DC
CBitmap* pOldBmp = cMemDC.SelectObject(&cBitmap );
//Fill the background with the masking color
cMemDC.FillSolidRect(0,0,ImgWidth,ImgHeight, RGB(0, 128, 128));
//Now draw the icon using DrawIconEx
DrawIconEx(cMemDC.m_hDC, 0, 0, hIcon, ImgWidth, ImgHeight, 0, 0, DI_NORMAL);
//Now restore the old bitmap
cMemDC.SelectObject(pOldBmp );
return true;
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To set the image index based on the state of the item
/
/PARAM
/------
/ hItem[in] - Item to which the image to be set
/
/RESULT:
/-------
/ True if successful,false otherwise
*/
void CVersatileTreeCtrl::SetImage( HTREEITEM hItem )
{
//Check for item
if( !hItem )
return;
//Get the custom data
PCUSTOMITEMDATA pCustData = (PCUSTOMITEMDATA) GetItemData(hItem);
if( !pCustData )
return;
//if the item is enabled
if( pCustData->m_bEnable )
{
//Is the item expanded
bool bItemExpanded = false;
if( GetItemState( hItem,TVIS_EXPANDED) & TVIS_EXPANDED)
bItemExpanded = true;
SetExpandImage( hItem,bItemExpanded );
}
else
{
//TODO : DISABLED IMAGE
int iBmpFound = -1;
iBmpFound = GetBitmapIndexFromImgList( pCustData->m_uNormalImage );
SetItemImage( hItem, pCustData->m_iDisableIndex,pCustData->m_iDisableIndex);
}
Invalidate();
}
/***************************************************************************
/DESCRIPTION:
/-----------
/ To set the image when the item is expanded
/
/PARAM
/------
/ hItem[in] - Item to which the image to be set
/ bItemExpanded - Item is expanded or collpased
/
/RESULT:
/-------
/ True if successful,false otherwise
*/
void CVersatileTreeCtrl::SetExpandImage( HTREEITEM hItem,bool bItemExpanded/*=true*/ )
{
//Get the custom data
PCUSTOMITEMDATA pCustData = (PCUSTOMITEMDATA) GetItemData(hItem);
if( !pCustData )
return;
//Get the bitmap id associated with the item
int iBmpFound = -1;
iBmpFound = GetBitmapIndexFromImgList( pCustData->m_uNormalImage );
if( iBmpFound < 0 )
return;
int imgIndex = -1;
//Check the item has the checkbox
if( pCustData->m_bChecked )
{
//If the item is checked
if( pCustData->m_bCheckState )
{
//If the item is expanded
if( bItemExpanded )
{
imgIndex = pCustData->m_iCheckedExpandedIndex;
}
//Collapsed
else
{
imgIndex = pCustData->m_iCheckedNormalIndex;
}
}
//Item is not checked
else
{
//If the item is expanded
if( bItemExpanded )
{
imgIndex = pCustData->m_iUnCheckedExpandedIndex;
}
//Collapsed
else
{
imgIndex = pCustData->m_iUnCheckedNormalIndex;
}
}
}
//If the item doesnt have checkbox
else
{
//If the item is expanded
if( bItemExpanded )
{
imgIndex = pCustData->m_iExpandedIndex;
}
else
{
imgIndex = pCustData->m_iNormalIndex;
}
}
SetItemImage( hItem, imgIndex,imgIndex);
}
int CVersatileTreeCtrl::IsNormalCheckedExists( UINT uBmpID )
{
return 0;
}
//***************************************************************************
void CVersatileTreeCtrl::SetMoveDraggedItem( bool flag )
{
m_bMoveItem = flag;
}
void CVersatileTreeCtrl::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
}