1 //*******************************************************************************
4 // You may use this source code, compile or redistribute it as part of your application
5 // for free. You cannot redistribute it as a part of a software development
6 // library without the agreement of the author. If the sources are
7 // distributed along with the application, you should leave the original
8 // copyright notes in the source code without any changes.
9 // This code can be used WITHOUT ANY WARRANTIES at your own risk.
11 // For the latest updates to this code, check this site:
12 // http://www.masmex.com
15 // Copyright(C) 2000 Philip Oldaker <email: philip@masmex.com>
16 //*******************************************************************************
18 //////////////////////////////////////////////////////////////////////////////////////--*/
19 // UIDragDropTree.cpp : implementation file
24 #include "UIDragDropTree.h"
25 #include "cbformats.h"
31 static char THIS_FILE[] = __FILE__;
34 IMPLEMENT_DYNAMIC(CUIDragDropTree,CTreeCtrl)
35 /////////////////////////////////////////////////
37 // For OLE drag and drop
39 /////////////////////////////////////////////////
40 void CUI_TreeDropTarget::OnDragLeave(CWnd* pWnd)
42 CUIDragDropTree *pTree = static_cast<CUIDragDropTree*>(pWnd);
45 pTree->KillDragTimer();
46 pTree->SelectDropTarget(NULL);
47 CDD_OleDropTargetInfo Info(pWnd->GetSafeHwnd());
48 Info.SetItem(pTree->GetDropHilightItem());
49 BOOL bRet = pTree->SendMessage(WM_APP_OLE_DD_LEAVE,(WPARAM)&Info);
51 bRet = pTree->GetParent()->SendMessage(WM_APP_OLE_DD_LEAVE,(WPARAM)&Info);
54 DROPEFFECT CUI_TreeDropTarget::OnDragEnter( CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point )
56 // ASSERT_KINDOF(CUIDragDropTree,pWnd);
57 CUIDragDropTree *pTree = static_cast<CUIDragDropTree*>(pWnd);
59 return DROPEFFECT_NONE;
60 ((CUIDragDropTree*)pWnd)->SetDragTimer();
61 CDD_OleDropTargetInfo Info(pWnd->GetSafeHwnd(),point,pDataObject);
62 m_dwEnterKeyboardState = dwKeyState;
63 Info.SetKeyboardState(dwKeyState);
64 Info.SetItem(pTree->GetDropHilightItem());
65 BOOL bRet = pTree->SendMessage(WM_APP_OLE_DD_ENTER,(WPARAM)&Info);
67 bRet = pTree->GetParent()->SendMessage(WM_APP_OLE_DD_ENTER,(WPARAM)&Info);
68 return Info.GetDropEffect();
71 DROPEFFECT CUI_TreeDropTarget::OnDragOver( CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point )
73 // disallow if the control key pressed
75 CUIDragDropTree *pTree = static_cast<CUIDragDropTree*>(pWnd);
77 return DROPEFFECT_NONE;
78 // ASSERT_KINDOF(CUIDragDropTree,pWnd);
79 CDD_OleDropTargetInfo Info(pWnd->GetSafeHwnd(),point,pDataObject);
80 m_dwKeyboardState = dwKeyState;
81 Info.SetKeyboardState(dwKeyState);
82 // WM_APP_OLE_DD_OVER message sent in SelectCurrentTarget
83 DROPEFFECT dropEffect = pTree->SelectCurrentTarget(&Info);
84 CUI_ImageDropTarget::OnDragOver(pWnd,pDataObject,dwKeyState,point);
88 BOOL CUI_TreeDropTarget::OnDrop( CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point )
90 CUIDragDropTree *pTree = static_cast<CUIDragDropTree*>(pWnd);
93 // ASSERT_KINDOF(CUIDragDropTree,pWnd);
94 pTree->KillDragTimer();
95 HTREEITEM hItem = pTree->GetDropHilightItem();
99 CDD_OleDropTargetInfo Info(pWnd->GetSafeHwnd(),point,pDataObject);
100 Info.SetDropEffect(dropEffect);
102 if (m_dwEnterKeyboardState & MK_RBUTTON)
103 m_dwKeyboardState |= MK_RBUTTON;
104 if (m_dwEnterKeyboardState & MK_LBUTTON)
105 m_dwKeyboardState |= MK_LBUTTON;
106 Info.SetKeyboardState(m_dwKeyboardState);
107 bRet = pTree->SendMessage(WM_APP_OLE_DD_DROP,(WPARAM)&Info);
109 bRet = pTree->GetParent()->SendMessage(WM_APP_OLE_DD_DROP,(WPARAM)&Info);
111 pTree->SelectDropTarget(NULL);
115 // During slow scroll, process every third message.
116 #define SLOWSCROLL_FREQUENCY 3
117 /////////////////////////////////////////////////////////////////////////////
120 CUIDragDropTree::CUIDragDropTree(bool bDragDrop): m_nUpperYCoor(0),
122 m_nSlowScrollTimeout(0),
128 m_bDragDrop(bDragDrop),
132 m_nScrollBarSize = GetSystemMetrics( SM_CYHSCROLL );
133 m_CopyMode = eDDCancel;
136 CUIDragDropTree::~CUIDragDropTree()
140 void CUIDragDropTree::OnDropFile(HTREEITEM hItem,LPCTSTR pszFile,UINT nFlags)
144 BEGIN_MESSAGE_MAP(CUIDragDropTree, CTreeCtrl)
145 //{{AFX_MSG_MAP(CUIDragDropTree)
146 ON_NOTIFY_REFLECT_EX(TVN_BEGINDRAG, OnBegindrag)
147 ON_NOTIFY_REFLECT_EX(TVN_BEGINRDRAG, OnBeginRDrag)
157 /////////////////////////////////////////////////////////////////////////////
158 // CUIDragDropTree message handlers
159 void CUIDragDropTree::OnDropFiles(HDROP hDropInfo)
161 UINT wNumFilesDropped = DragQueryFile(hDropInfo, 0XFFFFFFFF, NULL, 0);
162 TCHAR szFile[MAX_PATH];
166 ::DragQueryPoint(hDropInfo,&pt);
167 HTREEITEM hitemDrag = HitTest(CPoint(pt), &nFlags);
168 for(UINT i=0; i < wNumFilesDropped;i++)
170 nLen = DragQueryFile(hDropInfo,i,szFile,sizeof(szFile)/sizeof(TCHAR));
172 OnDropFile(hitemDrag,szFile,nFlags);
176 BOOL CUIDragDropTree::OnBeginRDrag(NMHDR* pNMHDR, LRESULT* pResult)
178 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
179 return StartDragDrop(pNMTreeView);
182 BOOL CUIDragDropTree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
184 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
185 return StartDragDrop(pNMTreeView);
188 BOOL CUIDragDropTree::StartDragDrop(NM_TREEVIEW* pNMTreeView)
190 if (GetDragDrop() == false || pNMTreeView->itemNew.hItem == GetRootItem())
192 // See if COM drag drop is implemented in this window or parent window
193 DWORD dwDragEffect = SendMessage(WM_APP_OLE_DD_DODRAGDROP,(WPARAM)pNMTreeView,(LPARAM)&m_OleDataSource);
194 if (dwDragEffect == 0)
195 dwDragEffect = GetParent()->SendMessage(WM_APP_OLE_DD_DODRAGDROP,(WPARAM)pNMTreeView,(LPARAM)&m_OleDataSource);
199 m_hitemDrag = pNMTreeView->itemNew.hItem;
200 // Define starting rect
201 GetItemRect(pNMTreeView->itemNew.hItem,rcDrag,TRUE);
202 // Start the DragDrop
203 DWORD dwEffect = m_OleDataSource.DoDragDrop(dwDragEffect,rcDrag);
205 m_OleDataSource.Empty();
207 else // otherwise start local drag drop
212 GetCursorPos(&ptAction);
213 ScreenToClient(&ptAction);
216 m_hitemDrag = HitTest(pNMTreeView->ptDrag, &nFlags);
219 // Create drag image and begin dragging
220 m_pImageList = CreateDragImage(m_hitemDrag);
221 m_pImageList->BeginDrag(0, CPoint(0,0));
222 m_pImageList->DragEnter(GetDesktopWindow(), ptAction);
230 void CUIDragDropTree::SetDragTimer()
232 // Set the timer to slow down the scrolling when the dragging cursor
233 // is close to the top/bottom border of the tree control
236 m_nTimerID = SetTimer(1, 75, NULL);
238 GetClientRect(&rect);
239 ClientToScreen(&rect);
240 m_nUpperYCoor = rect.top;
241 m_nLowerYCoor = rect.bottom;
245 void CUIDragDropTree::KillDragTimer()
249 KillTimer(m_nTimerID);
254 m_nSlowScrollTimeout = 0;
257 void CUIDragDropTree::OnTimer(UINT nIDEvent)
259 // TODO: Add your message handler code here and/or call default
260 if (nIDEvent != m_nTimerID)
262 CTreeCtrl::OnTimer(nIDEvent);
266 // Get the "current" mouse position
269 pMessage = GetCurrentMessage();
271 ptMouse = pMessage->pt;
273 // Move the ghost image
275 m_pImageList->DragMove(ptMouse);
277 // Find out if scrolling is needed.
278 // Scrolling is not needed for example, if the dragging cursor is
279 // within the tree control itself
280 if(!NeedToScroll(ptMouse))
282 CTreeCtrl::OnTimer(nIDEvent);
286 m_OleDropTarget.EraseOldImage();
288 // Refine the scrolling mode -
289 // Scroll Up/Down, Slow/Normal
290 int nScrollMode = RefineScrollMode(ptMouse);
295 case SCROLL_DOWN_SLOW:
296 if( m_nSlowScrollTimeout == 0)
297 SendMessage( WM_VSCROLL,
298 nScrollMode == SCROLL_UP_SLOW ? SB_LINEUP : SB_LINEDOWN);
299 m_nSlowScrollTimeout = ++m_nSlowScrollTimeout%SLOWSCROLL_FREQUENCY;
301 case SCROLL_UP_NORMAL:
302 case SCROLL_DOWN_NORMAL:
303 SendMessage( WM_VSCROLL,
304 nScrollMode == SCROLL_UP_NORMAL ? SB_LINEUP : SB_LINEDOWN);
313 // Select the drop target
315 m_pImageList->DragLeave(this);
316 HTREEITEM hitem = GetFirstVisibleItem();
318 switch( nScrollMode )
321 case SCROLL_UP_NORMAL:
323 SelectDropTarget(hitem);
327 case SCROLL_DOWN_SLOW:
328 case SCROLL_DOWN_NORMAL:
330 // Get the last visible item in the control
331 int nCount = GetVisibleCount();
332 for ( int i=0; i<nCount-1; ++i )
333 hitem = GetNextVisibleItem(hitem);
334 SelectDropTarget(hitem);
345 m_pImageList->DragEnter(this, ptMouse); CTreeCtrl::OnTimer(nIDEvent);
349 void CUIDragDropTree::OnRButtonUp(UINT nFlags, CPoint point)
353 // Kill the timer and reset all variables
356 VERIFY(menu.LoadMenu(IDR_POPUPMENU_DRAGDROP));
357 CMenu* pPopup = menu.GetSubMenu(0);
358 ASSERT(pPopup != NULL);
359 pPopup->SetDefaultItem(ID_POPUP_DD_MOVE);
361 ClientToScreen(&pts);
362 if (GetRDragMenu(pPopup))
364 UINT nCmd = pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD, pts.x, pts.y,this);
365 if (nCmd == ID_POPUP_DD_MOVE)
369 else if (nCmd == ID_POPUP_DD_COPY)
376 // otherwise just move
379 SelectDropTarget(NULL);
380 CTreeCtrl::OnRButtonUp(nFlags, point);
383 void CUIDragDropTree::OnMouseMove(UINT nFlags, CPoint point)
385 // TODO: Add your message handler code here and/or call default
391 CPoint ptScreen(point);
392 ClientToScreen(&ptScreen);
394 m_pImageList->DragMove(ptScreen);
396 m_pImageList->DragShowNolock(FALSE);
398 if ((hItem = HitTest(point, &flags)) != NULL)
400 CDD_OleDropTargetInfo Info(this->GetSafeHwnd(),point,NULL);
402 LRESULT lResult = SendMessage(WM_APP_OLE_DD_OVER,(WPARAM)&Info);
405 lResult = GetParent()->SendMessage(WM_APP_OLE_DD_OVER,(WPARAM)&Info);
409 SelectDropTarget(hItem);
413 m_pImageList->DragShowNolock(TRUE);
415 CTreeCtrl::OnMouseMove(nFlags, point);
418 void CUIDragDropTree::OnButtonUp(bool bMove)
423 if (m_hitemDrag && m_hitemDrag != m_hitemDrop && !IsChildNodeOf(m_hitemDrop, m_hitemDrag) &&
424 GetParentItem(m_hitemDrag) != m_hitemDrop)
426 CDD_OleDropTargetInfo Info(GetSafeHwnd(),CPoint(),NULL);
427 Info.SetDropEffect(bMove ? DROPEFFECT_MOVE : DROPEFFECT_COPY);
428 Info.SetItem(m_hitemDrop);
429 LRESULT lResult = SendMessage(WM_APP_OLE_DD_DROP,(WPARAM)&Info);
431 lResult = GetParent()->SendMessage(WM_APP_OLE_DD_DROP,(WPARAM)&Info);
435 void CUIDragDropTree::EndDragging()
442 m_pImageList->DragLeave(this);
443 m_pImageList->EndDrag();
451 void CUIDragDropTree::NewTransferItem(HTREEITEM hNewItem)
455 BOOL CUIDragDropTree::TransferItem(HTREEITEM hitemDrag, HTREEITEM hitemDrop)
457 TV_INSERTSTRUCT tvstruct;
458 TCHAR sztBuffer[256];
459 HTREEITEM hNewItem, hFirstChild;
461 // avoid an infinite recursion situation
462 tvstruct.item.hItem = hitemDrag;
463 tvstruct.item.cchTextMax = sizeof(sztBuffer)-1;
464 tvstruct.item.pszText = sztBuffer;
465 tvstruct.item.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
466 GetItem(&tvstruct.item);
467 tvstruct.hParent = hitemDrop;
468 tvstruct.hInsertAfter = TVI_SORT;
469 tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
470 hNewItem = InsertItem(&tvstruct);
471 NewTransferItem(hNewItem);
473 while ((hFirstChild = GetChildItem(hitemDrag)) != NULL)
475 // recursively transfer all the items
476 TransferItem(hFirstChild, hNewItem);
477 // delete the first child and all its children
478 DeleteItem(hFirstChild);
485 BOOL CUIDragDropTree::NeedToScroll( CPoint ptMouse )
489 return ptMouse.y < m_nUpperYCoor || ptMouse.y > m_nLowerYCoor;
493 return ptMouse.y < m_nUpperYCoor+10 || ptMouse.y > m_nLowerYCoor-30;
498 CUIDragDropTree::SCROLLMODE CUIDragDropTree::RefineScrollMode( CPoint ptMouse )
500 int nYCoor = ptMouse.y;
501 CUIDragDropTree::SCROLLMODE nScrollMode;
505 nScrollMode = nYCoor > m_nLowerYCoor + m_nScrollBarSize ? SCROLL_DOWN_NORMAL :
506 nYCoor > m_nLowerYCoor ? SCROLL_DOWN_SLOW :
507 nYCoor < m_nUpperYCoor - m_nScrollBarSize ? SCROLL_UP_NORMAL :
512 nScrollMode = nYCoor > m_nLowerYCoor-30 ? SCROLL_DOWN_NORMAL :
513 nYCoor < m_nUpperYCoor-10 ? SCROLL_UP_NORMAL : SCROLL_UP_NORMAL;
518 BOOL CUIDragDropTree::IsChildNodeOf(HTREEITEM hitemChild, HTREEITEM hitemSuspectedParent)
522 if (hitemChild == hitemSuspectedParent)
525 while ((hitemChild = GetParentItem(hitemChild)) != NULL);
527 return (hitemChild != NULL);
530 void CUIDragDropTree::RegisterDropTarget()
534 VERIFY(m_OleDropTarget.Register(this));
540 DROPEFFECT CUIDragDropTree::SelectCurrentTarget(CDD_OleDropTargetInfo *pInfo)
544 if ((hItem = HitTest(pInfo->GetPoint(), &flags)) != NULL)
547 SelectDropTarget(hItem);
550 // returns 1 if allowed
551 pInfo->SetItem(hItem);
552 LRESULT lResult = SendMessage(WM_APP_OLE_DD_OVER,(WPARAM)pInfo);
555 lResult = GetParent()->SendMessage(WM_APP_OLE_DD_OVER,(WPARAM)pInfo);
559 if (::GetKeyState(VK_LCONTROL) < 0)
561 if (ItemHasChildren(hItem))
563 Expand(hItem, TVE_EXPAND);
566 return pInfo->GetDropEffect();
569 return DROPEFFECT_NONE;
572 void CUIDragDropTree::OnDestroy()
575 CTreeCtrl::OnDestroy();
577 // TODO: Add your message handler code here
581 int CUIDragDropTree::OnCreate(LPCREATESTRUCT lpCreateStruct)
583 if (CTreeCtrl::OnCreate(lpCreateStruct) == -1)
586 // TODO: Add your specialized creation code here
587 RegisterDropTarget();
592 bool CUIDragDropTree::GetRDragMenu(CMenu *pMenu)
597 void CUIDragDropTree::OnDDMove()
599 m_CopyMode = eDDMove;
602 void CUIDragDropTree::OnDDCopy()
604 m_CopyMode = eDDCopy;
607 void CUIDragDropTree::OnDDCancel()
609 m_CopyMode = eDDCancel;