This commit was manufactured by cvs2svn to create branch 'captive'.
[reactos.git] / subsys / system / explorer / Seashell / SeaShellExt / UIDragDropTree.cpp
diff --git a/subsys/system/explorer/Seashell/SeaShellExt/UIDragDropTree.cpp b/subsys/system/explorer/Seashell/SeaShellExt/UIDragDropTree.cpp
new file mode 100644 (file)
index 0000000..39e434a
--- /dev/null
@@ -0,0 +1,610 @@
+//*******************************************************************************
+// COPYRIGHT NOTES
+// ---------------
+// You may use this source code, compile or redistribute it as part of your application 
+// for free. You cannot redistribute it as a part of a software development 
+// library without the agreement of the author. If the sources are 
+// distributed along with the application, you should leave the original 
+// copyright notes in the source code without any changes.
+// This code can be used WITHOUT ANY WARRANTIES at your own risk.
+// 
+// For the latest updates to this code, check this site:
+// http://www.masmex.com 
+// after Sept 2000
+// 
+// Copyright(C) 2000 Philip Oldaker <email: philip@masmex.com>
+//*******************************************************************************
+
+//////////////////////////////////////////////////////////////////////////////////////--*/
+// UIDragDropTree.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "UICtrl.h"
+#include "UIDragDropTree.h"
+#include "cbformats.h"
+#include "UIRes.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+IMPLEMENT_DYNAMIC(CUIDragDropTree,CTreeCtrl)
+/////////////////////////////////////////////////
+//
+// For OLE drag and drop 
+//
+/////////////////////////////////////////////////
+void CUI_TreeDropTarget::OnDragLeave(CWnd* pWnd)
+{
+       CUIDragDropTree *pTree = static_cast<CUIDragDropTree*>(pWnd);
+       if (pTree == NULL)
+               return;
+       pTree->KillDragTimer();
+       pTree->SelectDropTarget(NULL);
+       CDD_OleDropTargetInfo Info(pWnd->GetSafeHwnd());
+       Info.SetItem(pTree->GetDropHilightItem());
+       BOOL bRet = pTree->SendMessage(WM_APP_OLE_DD_LEAVE,(WPARAM)&Info);
+       if (bRet == FALSE)
+               bRet = pTree->GetParent()->SendMessage(WM_APP_OLE_DD_LEAVE,(WPARAM)&Info);      
+}
+
+DROPEFFECT CUI_TreeDropTarget::OnDragEnter( CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point )
+{
+//     ASSERT_KINDOF(CUIDragDropTree,pWnd);
+       CUIDragDropTree *pTree = static_cast<CUIDragDropTree*>(pWnd);
+       if (pTree == NULL)
+               return DROPEFFECT_NONE;
+       ((CUIDragDropTree*)pWnd)->SetDragTimer();
+       CDD_OleDropTargetInfo Info(pWnd->GetSafeHwnd(),point,pDataObject);
+       m_dwEnterKeyboardState = dwKeyState;
+       Info.SetKeyboardState(dwKeyState);
+       Info.SetItem(pTree->GetDropHilightItem());
+       BOOL bRet = pTree->SendMessage(WM_APP_OLE_DD_ENTER,(WPARAM)&Info);
+       if (bRet == FALSE)
+               bRet = pTree->GetParent()->SendMessage(WM_APP_OLE_DD_ENTER,(WPARAM)&Info);
+       return Info.GetDropEffect();
+}
+
+DROPEFFECT CUI_TreeDropTarget::OnDragOver( CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point )
+{
+       // disallow if the control key pressed
+       EraseOldImage();
+       CUIDragDropTree *pTree = static_cast<CUIDragDropTree*>(pWnd);
+       if (pTree == NULL)
+               return DROPEFFECT_NONE;
+//     ASSERT_KINDOF(CUIDragDropTree,pWnd);
+       CDD_OleDropTargetInfo Info(pWnd->GetSafeHwnd(),point,pDataObject);
+       m_dwKeyboardState = dwKeyState;
+       Info.SetKeyboardState(dwKeyState);
+       // WM_APP_OLE_DD_OVER message sent in SelectCurrentTarget
+       DROPEFFECT dropEffect = pTree->SelectCurrentTarget(&Info);
+       CUI_ImageDropTarget::OnDragOver(pWnd,pDataObject,dwKeyState,point);
+       return dropEffect;
+}
+
+BOOL CUI_TreeDropTarget::OnDrop( CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point )
+{
+       CUIDragDropTree *pTree = static_cast<CUIDragDropTree*>(pWnd);
+       if (pTree == NULL)
+               return FALSE;
+//     ASSERT_KINDOF(CUIDragDropTree,pWnd);
+       pTree->KillDragTimer();
+       HTREEITEM hItem = pTree->GetDropHilightItem();
+       BOOL bRet=FALSE;
+       if (hItem) 
+       {
+               CDD_OleDropTargetInfo Info(pWnd->GetSafeHwnd(),point,pDataObject);
+               Info.SetDropEffect(dropEffect);
+               Info.SetItem(hItem);
+        if (m_dwEnterKeyboardState & MK_RBUTTON)
+                       m_dwKeyboardState |= MK_RBUTTON;
+        if (m_dwEnterKeyboardState & MK_LBUTTON)
+                       m_dwKeyboardState |= MK_LBUTTON;
+               Info.SetKeyboardState(m_dwKeyboardState);
+               bRet = pTree->SendMessage(WM_APP_OLE_DD_DROP,(WPARAM)&Info);
+               if (bRet == FALSE)
+                       bRet = pTree->GetParent()->SendMessage(WM_APP_OLE_DD_DROP,(WPARAM)&Info);
+       }
+       pTree->SelectDropTarget(NULL);
+       return bRet;
+}
+
+// During slow scroll, process every third message.
+#define SLOWSCROLL_FREQUENCY 3
+/////////////////////////////////////////////////////////////////////////////
+// CUIDragDropTree
+
+CUIDragDropTree::CUIDragDropTree(bool bDragDrop): m_nUpperYCoor(0), 
+                                                               m_nLowerYCoor(0), 
+                                                               m_nSlowScrollTimeout(0),
+                                                               m_nTimerID(0),
+                                                               m_bDragging(FALSE),
+                                                               m_pImageList(NULL),
+                                                               m_hitemDrag(NULL),
+                                                               m_hitemDrop(NULL),
+                                                               m_bDragDrop(bDragDrop),
+                                                               m_bDropFiles(true)
+
+{
+       m_nScrollBarSize = GetSystemMetrics( SM_CYHSCROLL );
+       m_CopyMode = eDDCancel;
+}
+
+CUIDragDropTree::~CUIDragDropTree()
+{
+}
+
+void CUIDragDropTree::OnDropFile(HTREEITEM hItem,LPCTSTR pszFile,UINT nFlags)
+{
+}
+
+BEGIN_MESSAGE_MAP(CUIDragDropTree, CTreeCtrl)
+       //{{AFX_MSG_MAP(CUIDragDropTree)
+       ON_NOTIFY_REFLECT_EX(TVN_BEGINDRAG, OnBegindrag)
+       ON_NOTIFY_REFLECT_EX(TVN_BEGINRDRAG, OnBeginRDrag)
+       ON_WM_TIMER()
+       ON_WM_RBUTTONUP()
+       ON_WM_MOUSEMOVE()
+       ON_WM_DESTROY()
+       ON_WM_CREATE()
+       ON_WM_DROPFILES()
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CUIDragDropTree message handlers
+void CUIDragDropTree::OnDropFiles(HDROP hDropInfo)
+{
+       UINT wNumFilesDropped = DragQueryFile(hDropInfo, 0XFFFFFFFF, NULL, 0);
+       TCHAR szFile[MAX_PATH];
+       UINT nLen;
+       UINT nFlags;
+       POINT pt;
+       ::DragQueryPoint(hDropInfo,&pt);
+       HTREEITEM hitemDrag = HitTest(CPoint(pt), &nFlags);
+       for(UINT i=0; i < wNumFilesDropped;i++)
+       {
+               nLen = DragQueryFile(hDropInfo,i,szFile,sizeof(szFile)/sizeof(TCHAR));
+               if (nLen)
+                       OnDropFile(hitemDrag,szFile,nFlags);
+       }
+}
+
+BOOL CUIDragDropTree::OnBeginRDrag(NMHDR* pNMHDR, LRESULT* pResult) 
+{
+       NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
+       return StartDragDrop(pNMTreeView);
+}
+
+BOOL CUIDragDropTree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) 
+{
+       NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
+       return StartDragDrop(pNMTreeView);
+}
+
+BOOL CUIDragDropTree::StartDragDrop(NM_TREEVIEW* pNMTreeView)
+{
+       if (GetDragDrop() == false || pNMTreeView->itemNew.hItem == GetRootItem())
+               return FALSE;
+       // See if COM drag drop is implemented in this window or parent window
+       DWORD dwDragEffect = SendMessage(WM_APP_OLE_DD_DODRAGDROP,(WPARAM)pNMTreeView,(LPARAM)&m_OleDataSource);
+       if (dwDragEffect == 0)
+               dwDragEffect = GetParent()->SendMessage(WM_APP_OLE_DD_DODRAGDROP,(WPARAM)pNMTreeView,(LPARAM)&m_OleDataSource);
+       if (dwDragEffect)
+       {
+               CRect rcDrag; 
+               m_hitemDrag = pNMTreeView->itemNew.hItem;
+               // Define starting rect
+               GetItemRect(pNMTreeView->itemNew.hItem,rcDrag,TRUE);
+               // Start the DragDrop
+               DWORD dwEffect = m_OleDataSource.DoDragDrop(dwDragEffect,rcDrag);
+               // Clear the cache
+               m_OleDataSource.Empty();        
+       }
+       else // otherwise start local drag drop
+       {
+               CPoint          ptAction;
+               UINT            nFlags;
+
+               GetCursorPos(&ptAction);
+               ScreenToClient(&ptAction);
+               
+               m_bDragging = TRUE;
+               m_hitemDrag = HitTest(pNMTreeView->ptDrag, &nFlags);
+               m_hitemDrop = NULL;
+
+               // Create drag image and begin dragging
+               m_pImageList = CreateDragImage(m_hitemDrag);  
+               m_pImageList->BeginDrag(0, CPoint(0,0));
+               m_pImageList->DragEnter(GetDesktopWindow(), ptAction);
+               SetCapture();
+
+               SetDragTimer();
+       }
+       return TRUE;
+}
+
+void CUIDragDropTree::SetDragTimer()
+{
+       // Set the timer to slow down the scrolling when the dragging cursor
+       // is close to the top/bottom border of the tree control
+       if (!m_nTimerID) 
+       {
+               m_nTimerID = SetTimer(1, 75, NULL);
+               CRect rect;
+               GetClientRect(&rect);
+               ClientToScreen(&rect);
+               m_nUpperYCoor = rect.top;
+               m_nLowerYCoor = rect.bottom;
+       }
+}
+
+void CUIDragDropTree::KillDragTimer()
+{
+       if (m_nTimerID)
+       {
+               KillTimer(m_nTimerID);
+       }
+       m_nTimerID = 0;
+       m_nUpperYCoor = 0;
+       m_nLowerYCoor = 0;
+       m_nSlowScrollTimeout = 0;
+}
+
+void CUIDragDropTree::OnTimer(UINT nIDEvent) 
+{
+       // TODO: Add your message handler code here and/or call default
+       if (nIDEvent != m_nTimerID)
+       {
+               CTreeCtrl::OnTimer(nIDEvent);
+               return;
+       }
+       
+       // Get the "current" mouse position
+       const MSG* pMessage;
+       CPoint ptMouse;
+       pMessage = GetCurrentMessage();
+       ASSERT(pMessage);
+       ptMouse = pMessage->pt;
+
+       // Move the ghost image
+       if (m_pImageList)
+               m_pImageList->DragMove(ptMouse);
+
+       // Find out if scrolling is needed.
+       // Scrolling is not needed for example, if the dragging cursor is 
+       // within the tree control itself
+       if(!NeedToScroll(ptMouse))
+       {
+               CTreeCtrl::OnTimer(nIDEvent);
+               return;
+       }
+       if (!m_bDragging)
+               m_OleDropTarget.EraseOldImage();
+
+       // Refine the scrolling mode -
+       // Scroll Up/Down, Slow/Normal
+       int nScrollMode = RefineScrollMode(ptMouse);
+
+       switch(nScrollMode) 
+       {
+               case SCROLL_UP_SLOW:
+               case SCROLL_DOWN_SLOW:
+                       if( m_nSlowScrollTimeout == 0)
+                               SendMessage( WM_VSCROLL, 
+                                       nScrollMode == SCROLL_UP_SLOW ? SB_LINEUP : SB_LINEDOWN);
+                       m_nSlowScrollTimeout = ++m_nSlowScrollTimeout%SLOWSCROLL_FREQUENCY;
+                       break;
+               case SCROLL_UP_NORMAL:
+               case SCROLL_DOWN_NORMAL:
+                       SendMessage( WM_VSCROLL, 
+                               nScrollMode == SCROLL_UP_NORMAL ? SB_LINEUP : SB_LINEDOWN);
+                       break;
+               default:
+                       ASSERT(FALSE);
+                       return;
+                       break;
+       }       
+       if (m_bDragging)
+       {
+               // Select the drop target
+               if (m_pImageList)
+                       m_pImageList->DragLeave(this);
+               HTREEITEM hitem = GetFirstVisibleItem();
+
+               switch( nScrollMode )
+                 {
+                       case SCROLL_UP_SLOW:
+                       case SCROLL_UP_NORMAL:
+                         {
+                               SelectDropTarget(hitem);
+                               m_hitemDrop = hitem;
+                               break;
+                         }
+                       case SCROLL_DOWN_SLOW:
+                       case SCROLL_DOWN_NORMAL:
+                         {
+               // Get the last visible item in the control
+                               int nCount = GetVisibleCount();
+                               for ( int i=0; i<nCount-1; ++i )
+                                       hitem = GetNextVisibleItem(hitem);
+                               SelectDropTarget(hitem);
+                               m_hitemDrop = hitem;
+                               break;
+                         }
+                       default:
+                               ASSERT(FALSE);
+                               return;
+                               break;
+                 }
+
+               if (m_pImageList)
+                       m_pImageList->DragEnter(this, ptMouse); CTreeCtrl::OnTimer(nIDEvent);
+       }
+}
+
+void CUIDragDropTree::OnRButtonUp(UINT nFlags, CPoint point) 
+{
+       if (!m_bDragging)
+               return;
+       // Kill the timer and reset all variables
+       EndDragging();
+       CMenu menu;
+       VERIFY(menu.LoadMenu(IDR_POPUPMENU_DRAGDROP));
+       CMenu* pPopup = menu.GetSubMenu(0);
+       ASSERT(pPopup != NULL);
+       pPopup->SetDefaultItem(ID_POPUP_DD_MOVE);
+       CPoint pts(point);
+       ClientToScreen(&pts);
+       if (GetRDragMenu(pPopup))
+       {
+               UINT nCmd = pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_RETURNCMD, pts.x, pts.y,this);
+               if (nCmd == ID_POPUP_DD_MOVE)
+               {
+                       OnButtonUp(true);
+               }
+               else if (nCmd == ID_POPUP_DD_COPY)
+               {
+                       OnButtonUp(false);
+               }
+       }
+       else
+       {
+               // otherwise just move
+               OnButtonUp(true);
+       }       
+       SelectDropTarget(NULL);
+       CTreeCtrl::OnRButtonUp(nFlags, point);
+}
+
+void CUIDragDropTree::OnMouseMove(UINT nFlags, CPoint point) 
+{
+       // TODO: Add your message handler code here and/or call default
+       HTREEITEM                       hItem;
+       UINT                            flags;
+
+       if (!m_bDragging)
+               return;
+       CPoint ptScreen(point);
+       ClientToScreen(&ptScreen);
+
+       m_pImageList->DragMove(ptScreen);
+
+       m_pImageList->DragShowNolock(FALSE);
+
+       if ((hItem = HitTest(point, &flags)) != NULL)
+       {
+               CDD_OleDropTargetInfo Info(this->GetSafeHwnd(),point,NULL);
+               Info.SetItem(hItem);
+               LRESULT lResult = SendMessage(WM_APP_OLE_DD_OVER,(WPARAM)&Info);
+               if (lResult == 0)
+               {
+                       lResult = GetParent()->SendMessage(WM_APP_OLE_DD_OVER,(WPARAM)&Info);
+               }
+               if (lResult)
+               {
+                       SelectDropTarget(hItem);
+                       m_hitemDrop = hItem;
+               }
+       }
+       m_pImageList->DragShowNolock(TRUE);
+       
+       CTreeCtrl::OnMouseMove(nFlags, point);
+}
+
+void CUIDragDropTree::OnButtonUp(bool bMove)
+{
+       if (!m_bDragging)
+               return;
+       EndDragging();
+       if (m_hitemDrag && m_hitemDrag != m_hitemDrop && !IsChildNodeOf(m_hitemDrop, m_hitemDrag) && 
+                                                               GetParentItem(m_hitemDrag) != m_hitemDrop)
+       {
+               CDD_OleDropTargetInfo Info(GetSafeHwnd(),CPoint(),NULL);
+               Info.SetDropEffect(bMove ? DROPEFFECT_MOVE : DROPEFFECT_COPY);
+               Info.SetItem(m_hitemDrop);
+               LRESULT lResult = SendMessage(WM_APP_OLE_DD_DROP,(WPARAM)&Info);
+               if (lResult == 0)
+                       lResult = GetParent()->SendMessage(WM_APP_OLE_DD_DROP,(WPARAM)&Info);
+       }
+}
+
+void CUIDragDropTree::EndDragging()
+{
+       if (!m_bDragging)
+               return;
+       m_bDragging = FALSE;
+       if (m_pImageList)
+       {
+               m_pImageList->DragLeave(this);
+               m_pImageList->EndDrag();
+       }
+       delete m_pImageList;
+       m_pImageList = NULL;
+       ReleaseCapture();
+       KillDragTimer();
+}
+
+void CUIDragDropTree::NewTransferItem(HTREEITEM hNewItem)
+{
+}
+
+BOOL CUIDragDropTree::TransferItem(HTREEITEM hitemDrag, HTREEITEM hitemDrop)
+{
+       TV_INSERTSTRUCT         tvstruct;
+       TCHAR                           sztBuffer[256];
+       HTREEITEM                       hNewItem, hFirstChild;
+
+               // avoid an infinite recursion situation
+       tvstruct.item.hItem = hitemDrag;
+       tvstruct.item.cchTextMax = sizeof(sztBuffer)-1;
+       tvstruct.item.pszText = sztBuffer;
+       tvstruct.item.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
+       GetItem(&tvstruct.item);  
+       tvstruct.hParent = hitemDrop;
+       tvstruct.hInsertAfter = TVI_SORT;
+       tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
+       hNewItem = InsertItem(&tvstruct);
+       NewTransferItem(hNewItem);
+
+       while ((hFirstChild = GetChildItem(hitemDrag)) != NULL)
+       {
+               // recursively transfer all the items
+               TransferItem(hFirstChild, hNewItem);  
+               // delete the first child and all its children
+               DeleteItem(hFirstChild);                
+       }
+       return TRUE;
+}
+
+
+
+BOOL CUIDragDropTree::NeedToScroll( CPoint ptMouse )
+{
+       if (m_bDragging)
+       {
+               return ptMouse.y < m_nUpperYCoor || ptMouse.y > m_nLowerYCoor;
+       }
+       else
+       {
+               return ptMouse.y < m_nUpperYCoor+10 || ptMouse.y > m_nLowerYCoor-30;
+       }
+}
+
+
+CUIDragDropTree::SCROLLMODE CUIDragDropTree::RefineScrollMode( CPoint ptMouse )
+{
+       int nYCoor = ptMouse.y;
+       CUIDragDropTree::SCROLLMODE nScrollMode;
+
+       if (m_bDragging)
+       {
+               nScrollMode = nYCoor > m_nLowerYCoor + m_nScrollBarSize ? SCROLL_DOWN_NORMAL :
+                                         nYCoor > m_nLowerYCoor ? SCROLL_DOWN_SLOW :
+                                         nYCoor < m_nUpperYCoor - m_nScrollBarSize ? SCROLL_UP_NORMAL :
+                                         SCROLL_UP_SLOW;
+       }
+       else
+       {
+               nScrollMode = nYCoor > m_nLowerYCoor-30 ? SCROLL_DOWN_NORMAL : 
+                                       nYCoor < m_nUpperYCoor-10 ? SCROLL_UP_NORMAL : SCROLL_UP_NORMAL;
+       }
+       return nScrollMode;
+}
+
+BOOL CUIDragDropTree::IsChildNodeOf(HTREEITEM hitemChild, HTREEITEM hitemSuspectedParent)
+{
+       do
+       {
+               if (hitemChild == hitemSuspectedParent)
+                       break;
+       }
+       while ((hitemChild = GetParentItem(hitemChild)) != NULL);
+
+       return (hitemChild != NULL);
+}
+
+void CUIDragDropTree::RegisterDropTarget()
+{
+       if (GetDragDrop())
+       {
+               VERIFY(m_OleDropTarget.Register(this));
+               if (GetDropFiles())
+                       DragAcceptFiles();
+       }
+}
+
+DROPEFFECT CUIDragDropTree::SelectCurrentTarget(CDD_OleDropTargetInfo *pInfo)
+{
+       HTREEITEM hItem;
+       UINT flags;
+       if ((hItem = HitTest(pInfo->GetPoint(), &flags)) != NULL) 
+       {
+               // select it
+               SelectDropTarget(hItem);
+               // save it
+               m_hitemDrop = hItem;
+               // returns 1 if allowed
+               pInfo->SetItem(hItem);
+               LRESULT lResult = SendMessage(WM_APP_OLE_DD_OVER,(WPARAM)pInfo);
+               if (lResult == 0)
+               {
+                       lResult = GetParent()->SendMessage(WM_APP_OLE_DD_OVER,(WPARAM)pInfo);
+               }
+               if (lResult)
+               {
+                       if (::GetKeyState(VK_LCONTROL) < 0) 
+                       {
+                               if (ItemHasChildren(hItem))
+                               {
+                                       Expand(hItem, TVE_EXPAND);
+                               }
+                       }
+                       return pInfo->GetDropEffect();
+               }
+       }
+       return DROPEFFECT_NONE;
+}
+
+void CUIDragDropTree::OnDestroy() 
+{
+
+       CTreeCtrl::OnDestroy();
+       
+       // TODO: Add your message handler code here
+       
+}
+
+int CUIDragDropTree::OnCreate(LPCREATESTRUCT lpCreateStruct) 
+{
+       if (CTreeCtrl::OnCreate(lpCreateStruct) == -1)
+               return -1;
+       
+       // TODO: Add your specialized creation code here
+       RegisterDropTarget();
+       
+       return 0;
+}
+
+bool CUIDragDropTree::GetRDragMenu(CMenu *pMenu)
+{
+       return true;
+}
+
+void CUIDragDropTree::OnDDMove()
+{
+       m_CopyMode = eDDMove;
+}
+
+void CUIDragDropTree::OnDDCopy()
+{
+       m_CopyMode = eDDCopy;
+}
+
+void CUIDragDropTree::OnDDCancel()
+{
+       m_CopyMode = eDDCancel;
+}