Figure 2   DragDropTreeCtrl

DragDropTreeCtrl.h


 #if !defined(AFX_DRAGDROPTREECTRL_H__63AC05AD_E0DC_11D1_8E53_006008A82731__INCLUDED_)
 #define AFX_DRAGDROPTREECTRL_H__63AC05AD_E0DC_11D1_8E53_006008A82731__INCLUDED_
 
 #if _MSC_VER >= 1000
 #pragma once
 #endif // _MSC_VER >= 1000
 // DragDropTreeCtrl.h : header file
 
 /////////////////////////////////////////////////////////////////////////////
 // CDragDropTreeCtrl window
 
 class CDragDropTreeCtrl : public CTreeCtrl
 {
 // Construction
 public:
     CDragDropTreeCtrl();
 
 // Attributes
 public:
 
 // Operations
 public:
 
 // Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CDragDropTreeCtrl)
     protected:
     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
     //}}AFX_VIRTUAL
 
 // Implementation
 public:
     virtual ~CDragDropTreeCtrl();
 
     // Generated message map functions
 protected:
     HTREEITEM HighlightDropTarget (CPoint point);
     int m_nDelayInterval;
     BOOL IsItemExpanded (HTREEITEM hItem);
     int m_nScrollMargin;
     int m_nScrollInterval;
     void CopyChildren (HTREEITEM hDest, HTREEITEM hSrc);
     void CopyTree (HTREEITEM hDest, HTREEITEM hSrc);
     void MoveTree (HTREEITEM hDest, HTREEITEM hSrc);
     BOOL IsChildOf (HTREEITEM hItem1, HTREEITEM hItem2);
     BOOL m_bDragging;
     CImageList* m_pImageList;
     HTREEITEM m_hDragItem;
     //{{AFX_MSG(CDragDropTreeCtrl)
     afx_msg void OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult);
     afx_msg void OnMouseMove(UINT nFlags, CPoint point);
     afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
     afx_msg void OnTimer(UINT nIDEvent);
     //}}AFX_MSG
 
     DECLARE_MESSAGE_MAP()
 };
 
 /////////////////////////////////////////////////////////////////////////////
 //{{AFX_INSERT_LOCATION}}
 // Microsoft Developer Studio will insert additional declarations immediately
 // before the previous line.
 
 #endif
 // !defined(AFX_DRAGDROPTREECTRL_H__63AC05AD_E0DC_11D1_8E53_006008A82731__INCLUDED_)

DragDropTreeCtrl.cpp


 // DragDropTreeCtrl.cpp : implementation file
 
 #include "stdafx.h"
 #include "TreeDemo.h"
 #include "DragDropTreeCtrl.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 /////////////////////////////////////////////////////////////////////////////
 // CDragDropTreeCtrl
 
 CDragDropTreeCtrl::CDragDropTreeCtrl()
 {
     m_hDragItem = NULL;
     m_pImageList = NULL;
     m_bDragging = FALSE;
     m_nDelayInterval = 500;     // Default delay interval = 500 milliseconds
     m_nScrollInterval = 200;    // Default scroll interval = 200 milliseconds
     m_nScrollMargin = 10;       // Default scroll margin = 10 pixels
 }
 
 CDragDropTreeCtrl::~CDragDropTreeCtrl()
 {
     // Delete the image list created by CreateDragImage.
     //
     if (m_pImageList != NULL)
         delete m_pImageList;
 }
 
 BEGIN_MESSAGE_MAP(CDragDropTreeCtrl, CTreeCtrl)
     //{{AFX_MSG_MAP(CDragDropTreeCtrl)
     ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBeginDrag)
     ON_WM_MOUSEMOVE()
     ON_WM_LBUTTONUP()
     ON_WM_TIMER()
     //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
 // CDragDropTreeCtrl message handlers
 
 BOOL CDragDropTreeCtrl::PreCreateWindow(CREATESTRUCT& cs) 
 {
     // Make sure the control's TVS_DISABLEDRAGDROP flag is not set.
     // If you subclass an existing tree view control rather than create
     // a CDragDropTreeCtrl outright, it's YOUR responsibility to see that
     // this flag isn't set.
     cs.style &= ~TVS_DISABLEDRAGDROP;
     return CTreeCtrl::PreCreateWindow(cs);
 }
 
 void CDragDropTreeCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
 {
     *pResult = 0;
     NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*) pNMHDR;
 
     //
     // Do nothing if the user is attempting to drag a top-level item.
     //
     HTREEITEM hItem = pNMTreeView->itemNew.hItem;
     if (GetParentItem (hItem) == NULL)
         return;
 
     // Create a drag image. If the assertion fails, you probably forgot
     // to assign an image list to the control with SetImageList. Create-
     // DragImage will not work if the control hasn't been assigned an
     // image list!
     m_pImageList = CreateDragImage (hItem);
     ASSERT (m_pImageList != NULL);
 
     if (m_pImageList != NULL) { // Just to be sure
         // Compute the coordinates of the "hot spot"--the location of the
         // cursor relative to the upper left corner of the item rectangle.
         CRect rect;
         GetItemRect (hItem, rect, TRUE);
         CPoint point (pNMTreeView->ptDrag.x, pNMTreeView->ptDrag.y);
         CPoint hotSpot = point;
         hotSpot.x -= rect.left;
         hotSpot.y -= rect.top;
 
         // Convert the client coordinates in "point" to coordinates relative
         // to the upper left corner of the control window.
         CPoint client (0, 0);
         ClientToScreen (&client);
         GetWindowRect (rect);
         point.x += client.x - rect.left;
         point.y += client.y - rect.top;
 
         // Capture the mouse and begin dragging.
         SetCapture ();
         m_pImageList->BeginDrag (0, hotSpot);
         m_pImageList->DragEnter (this, point);
         m_hDragItem = hItem;
         m_bDragging = TRUE;
     }
 }
 
 void CDragDropTreeCtrl::OnMouseMove(UINT nFlags, CPoint point) 
 {
     CTreeCtrl::OnMouseMove(nFlags, point);
 
     if (m_bDragging && m_pImageList != NULL) {
         // Stop the scroll timer if it's running.
         KillTimer (1);
 
         // Erase the Old drag image and draw a new one.
         m_pImageList->DragMove (point);
 
         // Highlight the drop target if the cursor is over an item.
         HTREEITEM hItem = HighlightDropTarget (point);
 
         // Modify the cursor to provide visual feedback to the user.
         // Note: It's important to do this AFTER the call to DragMove.
         ::SetCursor (hItem == NULL ?
             AfxGetApp ()->LoadStandardCursor (IDC_NO) :
             (HCURSOR) ::GetClassLong (m_hWnd, GCL_HCURSOR));
 
         // Set a timer if the cursor is at the top or bottom of the window,
         // or if it's over a collapsed item.
         CRect rect;
         GetClientRect (rect);
         int cy = rect.Height ();
 
         if ((point.y >= 0 && point.y <= m_nScrollMargin) ||
             (point.y >= cy - m_nScrollMargin && point.y <= cy) ||
             (hItem != NULL && ItemHasChildren (hItem) &&
             !IsItemExpanded (hItem)))
 
             SetTimer (1, m_nDelayInterval, NULL);
     }
 }
 
 void CDragDropTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
 {
     CTreeCtrl::OnLButtonUp(nFlags, point);
 
     if (m_bDragging && m_pImageList != NULL) {
         // Stop the scroll timer if it's running.
         KillTimer (1);
 
         // Terminate the dragging operation and release the mouse.
         m_pImageList->DragLeave (this);
         m_pImageList->EndDrag ();
         ::ReleaseCapture ();

         m_bDragging = FALSE;
         SelectDropTarget (NULL);
 
         // Delete the image list created by CreateDragImage.
         delete m_pImageList;
         m_pImageList = NULL;
 
         // Get the HTREEITEM of the drop target and exit now if it's NULL.
         UINT nFlags;
         HTREEITEM hItem = HitTest (point, &nFlags);
         if (hItem == NULL)
             return;
 
         // Display an error message if the move is illegal.
         if (hItem == m_hDragItem) {
             MessageBox (_T ("An item can't be dropped onto itself"));
             return;
         }
         else if (hItem == GetParentItem (m_hDragItem)) {
             MessageBox (_T ("An item can't be dropped onto its parent"));
             return;
         }
         else if (IsChildOf (hItem, m_hDragItem)) {
             MessageBox (_T ("An item can't be dropped onto one of its " \
                 "children"));
             return;
         }
 
         // Move the dragged item and its subitems (if any) to the drop point.
         MoveTree (hItem, m_hDragItem);
         m_hDragItem = NULL;
     }
 }
 
 void CDragDropTreeCtrl::OnTimer(UINT nIDEvent) 
 {
     CTreeCtrl::OnTimer(nIDEvent);
 
     //
     // Reset the timer.
     //
     SetTimer (1, m_nScrollInterval, NULL);
 
     // Get the current cursor position and window height.
     DWORD dwPos = ::GetMessagePos ();
     CPoint point (LOWORD (dwPos), HIWORD (dwPos));
     ScreenToClient (&point);
 
     CRect rect;
     GetClientRect (rect);
     int cy = rect.Height ();
 
     // Scroll the window if the cursor is near the top or bottom.
     if (point.y >= 0 && point.y <= m_nScrollMargin) {
         HTREEITEM hFirstVisible = GetFirstVisibleItem ();
         m_pImageList->DragShowNolock (FALSE);
         SendMessage (WM_VSCROLL, MAKEWPARAM (SB_LINEUP, 0), NULL);
         m_pImageList->DragShowNolock (TRUE);
 
         //
         // Kill the timer if the window did not scroll, or redraw the
         // drop target highlight if the window did scroll.
         //
         if (GetFirstVisibleItem () == hFirstVisible)
             KillTimer (1);
         else {
             HighlightDropTarget (point);
             return;
         }
     }
     else if (point.y >= cy - m_nScrollMargin && point.y <= cy) {
         HTREEITEM hFirstVisible = GetFirstVisibleItem ();
         m_pImageList->DragShowNolock (FALSE);
         SendMessage (WM_VSCROLL, MAKEWPARAM (SB_LINEDOWN, 0), NULL);
         m_pImageList->DragShowNolock (TRUE);
 
         //
         // Kill the timer if the window did not scroll, or redraw the
         // drop target highlight if the window did scroll.
         //
         if (GetFirstVisibleItem () == hFirstVisible)
             KillTimer (1);
         else {
             HighlightDropTarget (point);
             return;
         }
     }
 
     // If the cursor is hovering over a collapsed item, expand the tree.
     UINT nFlags;
     HTREEITEM hItem = HitTest (point, &nFlags);
 
     if (hItem != NULL && ItemHasChildren (hItem) && !IsItemExpanded (hItem)) {
         m_pImageList->DragShowNolock (FALSE);
         Expand (hItem, TVE_EXPAND);
         m_pImageList->DragShowNolock (TRUE);
         KillTimer (1);
         return;
     }
 }
 
 BOOL CDragDropTreeCtrl::IsChildOf(HTREEITEM hItem1, HTREEITEM hItem2)
 {
     HTREEITEM hParent = hItem1;
     while ((hParent = GetParentItem (hParent)) != NULL) {
         if (hParent == hItem2)
             return TRUE;
     }
     return FALSE;
 }
 
 void CDragDropTreeCtrl::MoveTree(HTREEITEM hDest, HTREEITEM hSrc)
 {
     CopyTree (hDest, hSrc);
     DeleteItem (hSrc);
 }
 
 void CDragDropTreeCtrl::CopyTree(HTREEITEM hDest, HTREEITEM hSrc)
 {
     // Get the attributes of item to be copied.
     int nImage, nSelectedImage;
     GetItemImage (hSrc, nImage, nSelectedImage);
     CString string = GetItemText (hSrc);
 
     // Create an exact copy of the item at the destination.
     HTREEITEM hNewItem = InsertItem (string, nImage, nSelectedImage, hDest);
 
     // If the item has subitems, copy the M, too.
     if (ItemHasChildren (hSrc))
         CopyChildren (hNewItem, hSrc);
 
     // Select the newly added item.
     SelectItem (hNewItem);
 }
 
 void CDragDropTreeCtrl::CopyChildren(HTREEITEM hDest, HTREEITEM hSrc)
 {
     // Get the first subitem.
     HTREEITEM hItem = GetChildItem (hSrc);
     ASSERT (hItem != NULL);
 
     // Create a copy of it at the destination.
     int nImage, nSelectedImage;
     GetItemImage (hItem, nImage, nSelectedImage);
     CString string = GetItemText (hItem);
     HTREEITEM hNewItem = InsertItem (string, nImage, nSelectedImage, hDest);
 
     // If the subitem has subitems, copy the M, too.
     if (ItemHasChildren (hItem))
         CopyChildren (hNewItem, hItem);
 
     // Do the same for other subitems of hSrc.
     while ((hItem = GetNextSiblingItem (hItem)) != NULL) {
         GetItemImage (hItem, nImage, nSelectedImage);
         string = GetItemText (hItem);
         hNewItem = InsertItem (string, nImage, nSelectedImage, hDest);
         if (ItemHasChildren (hItem))
             CopyChildren (hNewItem, hItem);
     }
 }
 
 BOOL CDragDropTreeCtrl::IsItemExpanded(HTREEITEM hItem)
 {
     return GetItemState (hItem, TVIS_EXPANDED) & TVIS_EXPANDED;
 }
 
 HTREEITEM CDragDropTreeCtrl::HighlightDropTarget(CPoint point)
 {
     // Find out which item (if any) the cursor is over.
     UINT nFlags;
     HTREEITEM hItem = HitTest (point, &nFlags);
 
     // Highlight the item, or unhighlight all items if the cursor isn't
     // over an item.
     m_pImageList->DragShowNolock (FALSE);
     SelectDropTarget (hItem);
     m_pImageList->DragShowNolock (TRUE);
 
     // Return the handle of the highlighted item.
     return hItem;
 }