// ==================================================================================== // Class Specification : // COXSHBDropSource & COXSHBDropTarget // ==================================================================================== // Header file : OXDragDropSupport.h // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. ////////////////////////////////////////////////////////////////////////////// #if !defined(_OX_DRAGDROPSUPPORT_) #define _OX_DRAGDROPSUPPORT_ #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000 #include "OXDllExt.h" #include "OXMainRes.h" #ifndef __AFXTEMPL_H__ #include #define __AFXTEMPL_H__ #endif #ifndef __AFXOLE_H__ #include // MFC OLE classes #define __AFXOLE_H__ #endif // All folowing notifications has to do with OLE drag and drop. Refer to the // documentation on COleDropSource and COleDropTarget to get full description of // drag and drop operation. // // COleDropTarget based notifications // // These notifications can be sent by child windows and shortcut bar. lParam of // NMSHORTCUTBAR always points to SHBDROPTARGETACTION structure which is described later. // If you handle these notifications and don't want to run the default implementation // of them then you must return non-zero value and set result element of // SHBDROPTARGETACTION structure to corresponding value which is depend on the // particular notification. // You can distinguish the sender of the notification by hGroup element of NMSHORTCUTBAR // structure: if sender is shortcut bar then hGroup will be set to NULL; if sender is // child window then hGroup will be set to corresponding child window parent group. // // // Sent to parent window to notify that the cursor is first dragged into the window. // Set result element of SHBDROPTARGETACTION to the effect that would result if a drop // were attempted at the location specified by point element of SHBDROPTARGETACTION. // It can be one or more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, dwKeyState, point // //SHBN_DRAGENTER // // Sent to parent window to notify that the cursor leaves the window while a dragging // operation is in effect. // Next elements of SHBDROPTARGETACTION are filled: pWnd // //SHBN_DRAGLEAVE // // Sent to parent window to notify that the cursor is dragged over the window. This // notification should be handled to allow drop operations to occur in the window. The // default implementation returns DROPEFFECT_NONE by default. Because this notification is // called frequently during a drag-and-drop operation, it should be optimized as much // as possible. Set result element of SHBDROPTARGETACTION to the effect that would // result if a drop were attempted at the location specified by point element of // SHBDROPTARGETACTION. It can be one or more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, dwKeyState, point // //SHBN_DRAGOVER // // Sent to parent window before sending SHBN_DRAGENTER or SHBN_DRAGOVER notifications // to determine whether point is in the scrolling region. Handle this notification when // you want to provide special behavior for this event. The default implementation // returns DROPEFFECT_NONE and scrolls the window when the cursor is dragged into the // scroll region inside the border of the window. Set result element of // SHBDROPTARGETACTION to the effect that would result if a drop were attempted at // the location specified by point element of SHBDROPTARGETACTION. It can be one or // more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, dwKeyState, point. // //SHBN_DRAGSCROLL // // Sent to parent window to notify that a drop operation is about to occur. Set result // element of SHBDROPTARGETACTION to the TRUE if drop operation was successful or // FALSE otherwise. // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, point, dropEffect // //SHBN_DROP // // Sent to parent window to notify that a drop operation is about to occur. This // notification will be sent before SHBDTM_DROP. If you does not handle it, then // SHBN_DROP notification will be sent. Typically, you will handle SHBN_DROPEX to // support right mouse-button drag and drop. Typically, SHBN_DROP notification is used // to handle the case of support for simple drag and drop. // By default, we simply return a dummy value to indicate the SHBN_DROP notification // should be sent. Set result element of SHBDROPTARGETACTION to the drop effects that // describe the action associated with a drop operation. See the following list of // drop effects: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, point, // dropEffect, dropList. //SHBN_DROPEX // // // COleDropSource based notifications // // These notifications can be sent only by child windows. lParam of NMSHORTCUTBAR // always points to SHBDROPSOURCEACTION structure which is described later. // If you handle these notifications and don't want to run the default implementation // of them then you must return non-zero value and set result element of // SHBDROPSOURCEACTION structure to corresponding value which is depend on the // particular notification. // // // Handle SHBN_GIVEFEEDBACK notification to provide feedback to the user about what // would happen if a drop occurred at this point. The default implementation uses // the OLE default cursors. For more information on drag-and-drop operations using // OLE, see the article Drag and Drop (OLE) in Visual C++ Programmer's Guide. // Set result element of SHBDROPSOURCEACTION to the DRAGDROP_S_USEDEFAULTCURSORS if // dragging is in progress, NOERROR if it is not. // Next elements of SHBDROPSOURCEACTION are filled: dropEffect. // //SHBN_GIVEFEEDBACK // // SHBN_BEGINDRAG notification is sent to the parent window when an event occurs that // could begin a drag operation, such as pressing the left mouse button. Handle this // notification if you want to modify the way the dragging process is started. The default // implementation captures the mouse and stays in drag mode until the user clicks the // left or right mouse button or hits ESC, at which time it releases the mouse. // Set result element of SHBDROPSOURCEACTION to TRUE if dragging is allowed, // otherwise FALSE. // Next elements of SHBDROPSOURCEACTION are filled: pWnd. // SHBN_BEGINDRAG // // SHBN_QUERYCONTINUEDRAG notification is sent to the parent window after dragging has // begun, this notification will be sent repeatedly until the drag operation is either // canceled or completed. Handle this notification if you want to change the point at // which dragging is canceled or a drop occurs. The default implementation initiates // the drop or cancels the drag as follows. It cancels a drag operation when the ESC // key or the right mouse button is pressed. It initiates a drop operation when the // left mouse button is raised after dragging has started. Otherwise, it returns // S_OK and performs no further operations. Because this notification is called frequently, // it should be optimized as much as possible. // Set result element of SHBDROPSOURCEACTION to DRAGDROP_S_CANCEL if the ESC key or // right button is pressed, or left button is raised before dragging starts, // to DRAGDROP_S_DROP if a drop operation should occur, or otherwise to S_OK. // Next elements of SHBDROPSOURCEACTION are filled: bEscapePressed, dwKeyState . //SHBN_QUERYCONTINUEDRAG // Structure that is used in messages sending from COXSHBDropTarget object // to its owner window // typedef struct _tagSHBDROPTARGETACTION { // // Points to the window the cursor is currently over CWnd* pWnd; // // Points to the data object that contains the data to be dropped COleDataObject* pDataObject; // // Contains the state of the modifier keys. This is a combination of any number of // the following: // MK_CONTROL // MK_SHIFT // MK_ALT // MK_LBUTTON // MK_MBUTTON // MK_RBUTTON // DWORD dwKeyState; // // Contains the current location of the cursor in client coordinates. CPoint point; // // The effect that the user chose for the drop operation. It can be one or more of // the following: // // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original // data would be established. // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_SCROLL Indicates that a drag scroll operation is about // to occur or is occurring in the target. // DROPEFFECT dropEffect; // // A list of the drop effects that the drop source supports. Drop effect values // can be combined using the bitwise OR (|) operation. Drop effects are discussed // above DROPEFFECT dropList; // // result of message handling (differ for different messages) LRESULT result; _tagSHBDROPTARGETACTION() { ZeroMemory(this,sizeof(_tagSHBDROPTARGETACTION)); } } SHBDROPTARGETACTION, * LPSHBDROPTARGETACTION; // Following messages are sent to any window that registered as drop target object // using our COXSHBDropTarget class. For every message wParam is set to NULL and // lParam points to SHBDROPTARGETACTION structure. Return TRUE if you handle the // message and FALSE if you want to run the default implementation. // // Sent to owner window to notify that the cursor is first dragged into the window. // Set result element of SHBDROPTARGETACTION to the effect that would result if a drop // were attempted at the location specified by point element of SHBDROPTARGETACTION. // It can be one or more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, dwKeyState, point // //SHBDTM_DRAGENTER // // Sent to owner window to notify that the cursor leaves the window while a dragging // operation is in effect. // Next elements of SHBDROPTARGETACTION are filled: pWnd // //SHBDTM_DRAGLEAVE // // Sent to owner window to notify that the cursor is dragged over the window. This // message should be handled to allow drop operations to occur in the window. The // default implementation returns DROPEFFECT_NONE by default. Because this message is // called frequently during a drag-and-drop operation, it should be optimized as much // as possible. Set result element of SHBDROPTARGETACTION to the effect that would // result if a drop were attempted at the location specified by point element of // SHBDROPTARGETACTION. It can be one or more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, dwKeyState, point // //SHBDTM_DRAGOVER // // Sent to owner window before sending SHBDTM_DRAGENTER or SHBDTM_DRAGOVER messages // to determine whether point is in the scrolling region. Handle this message when // you want to provide special behavior for this event. The default implementation // returns DROPEFFECT_NONE and scrolls the window when the cursor is dragged into the // scroll region inside the border of the window. Set result element of // SHBDROPTARGETACTION to the effect that would result if a drop were attempted at // the location specified by point element of SHBDROPTARGETACTION. It can be one or // more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, dwKeyState, point. // //SHBDTM_DRAGSCROLL // // Sent to owner window to notify that a drop operation is about to occur. Set result // element of SHBDROPTARGETACTION to the TRUE if drop operation was successful or // FALSE otherwise. // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, point, dropEffect // //SHBDTM_DROP // // Sent to owner window to notify that a drop operation is about to occur. This // message will be sent before SHBDTM_DROP. If you does not handle it, then // SHBDTM_DROP message will be sent. Typically, you will handle SHBDTM_DROPEX in // your child window class to support right mouse-button drag and drop. Typically, // SHBDTM_DROP message is used to handle the case of support for simple drag and drop. // By default, we simply return a dummy value to indicate the SHBDTM_DROP message // should be sent. Set result element of SHBDROPTARGETACTION to the drop effects that // describe the action associated with a drop operation. See the following list of // drop effects: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, point, // dropEffect, dropList. //SHBDTM_DROPEX // Structure that is used in messages sending from COXSHBDropSource object // to its owner window // typedef struct _tagSHBDROPSOURCEACTION { // // Points to the window that contains the selected data. CWnd* pWnd; // // Contains the state of the modifier keys. This is a combination of any number of // the following: // MK_CONTROL // MK_SHIFT // MK_ALT // MK_LBUTTON // MK_MBUTTON // MK_RBUTTON // DWORD dwKeyState; // // Used only with SHBDS_QUERYCONTINUEDRAG and states whether the ESC key has been // pressed since the last time this message was sent. BOOL bEscapePressed; // // The effect you would like to display to the user, usually indicating what // would happen if a drop occurred at this point with the selected data. It can // be one or more of the following: // // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original // data would be established. // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_SCROLL Indicates that a drag scroll operation is about // to occur or is occurring in the target. // DROPEFFECT dropEffect; // // result of message handling (differ for different messages) LRESULT result; _tagSHBDROPSOURCEACTION() { ZeroMemory(this,sizeof(_tagSHBDROPSOURCEACTION)); } } SHBDROPSOURCEACTION, * LPSHBDROPSOURCEACTION; // Following messages are sent to any window that uses COXSHBDropSource class in // its drag'n'drop operations. For every message wParam is set to NULL and // lParam points to SHBDROPSOURCEACTION structure. Return TRUE if you handle the // message and FALSE if you want to run the default implementation. // // Handle SHBDSM_GIVEFEEDBACK message to provide feedback to the user about what // would happen if a drop occurred at this point. The default implementation uses // the OLE default cursors. For more information on drag-and-drop operations using // OLE, see the article Drag and Drop (OLE) in Visual C++ Programmer's Guide. // Set result element of SHBDROPSOURCEACTION to the DRAGDROP_S_USEDEFAULTCURSORS if // dragging is in progress, NOERROR if it is not. // Next elements of SHBDROPSOURCEACTION are filled: dropEffect. // //SHBDSM_GIVEFEEDBACK // // SHBDSM_BEGINDRAG message is sent to the owner window when an event occurs that // could begin a drag operation, such as pressing the left mouse button. Handle this // message if you want to modify the way the dragging process is started. The default // implementation captures the mouse and stays in drag mode until the user clicks the // left or right mouse button or hits ESC, at which time it releases the mouse. // Set result element of SHBDROPSOURCEACTION to TRUE if dragging is allowed, // otherwise FALSE. // Next elements of SHBDROPSOURCEACTION are filled: pWnd. //SHBDSM_BEGINDRAG // // SHBDSM_QUERYCONTINUEDRAG message is sent to the owner window after dragging has // begun, this message will be sent repeatedly until the drag operation is either // canceled or completed. Handle this message if you want to change the point at // which dragging is canceled or a drop occurs. The default implementation initiates // the drop or cancels the drag as follows. It cancels a drag operation when the ESC // key or the right mouse button is pressed. It initiates a drop operation when the // left mouse button is raised after dragging has started. Otherwise, it returns // S_OK and performs no further operations. Because this message is called frequently, // it should be optimized as much as possible. // Set result element of SHBDROPSOURCEACTION to DRAGDROP_S_CANCEL if the ESC key or // right button is pressed, or left button is raised before dragging starts, // to DRAGDROP_S_DROP if a drop operation should occur, or otherwise to S_OK. // Next elements of SHBDROPSOURCEACTION are filled: bEscapePressed, dwKeyState . // //SHBDSM_QUERYCONTINUEDRAG ///////////////////////////////////////////////////////////////////////////// // COXSHBDropSource /* COXSHBDropSource is designed to provide a standard way of handling OLE drag and drop operation in any window. COXSHBDropSource is derived from standard COleDropSource class, and you can use it as common COleDropSource object in COleDataSource::DoDragDrop function. The only thing you have to do is to register the window that launch drag and drop operation as COXSHBDropSource owner window using COXSHBDropSource::SetOwner(CWnd* pWnd) function: CWnd* SetOwner(CWnd* pWnd); // --- In : pWnd - pointer to the window which will be the recipient // of all COXSHBDropSource specific messages // --- Out : // --- Returns: pointer to the previous recipient window // --- Effect : As long as window is registered as COXSHBDropSource owner and as soon as the drag and drop operation has been started our COXSHBDropSource class will send to this window (we call it recipient window) next messages: SHBDSM_GIVEFEEDBACK SHBDSM_BEGINDRAG SHBDSM_QUERYCONTINUEDRAG For every message wParam is set to NULL and lParam points to SHBDROPSOURCEACTION structure. Return TRUE if you handle the message and FALSE if you want to run the default implementation. Structure that is used in messages sending from COXSHBDropSource object is defined as follows: typedef struct _tagSHBDROPSOURCEACTION { // // Points to the window that contains the selected data. CWnd* pWnd; // // Contains the state of the modifier keys. This is a combination of any number of // the following: // MK_CONTROL // MK_SHIFT // MK_ALT // MK_LBUTTON // MK_MBUTTON // MK_RBUTTON // DWORD dwKeyState; // // Used only with SHBDS_QUERYCONTINUEDRAG and states whether the ESC key has been // pressed since the last time this message was sent. BOOL bEscapePressed; // // The effect you would like to display to the user, usually indicating what // would happen if a drop occurred at this point with the selected data. It can // be one or more of the following: // // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original // data would be established. // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_SCROLL Indicates that a drag scroll operation is about // to occur or is occurring in the target. // DROPEFFECT dropEffect; // // result of message handling (differ for different messages) LRESULT result; . . . . . . . . . . . . } SHBDROPSOURCEACTION, * LPSHBDROPSOURCEACTION; Below you will find detailed description of all messages sent by COXSHBDropSource: ///////////////////////// SHBDSM_GIVEFEEDBACK // // // Handle SHBDSM_GIVEFEEDBACK message to provide feedback to the user about what // would happen if a drop occurred at this point. The default implementation uses // the OLE default cursors. For more information on drag-and-drop operations using // OLE, see the article Drag and Drop (OLE) in Visual C++ Programmer's Guide. // Set result element of SHBDROPSOURCEACTION to the DRAGDROP_S_USEDEFAULTCURSORS if // dragging is in progress, NOERROR if it is not. // Next elements of SHBDROPSOURCEACTION are filled: dropEffect. ///////////////////////// SHBDSM_BEGINDRAG // // // SHBDSM_BEGINDRAG message is sent to the owner window when an event occurs that // could begin a drag operation, such as pressing the left mouse button. Handle this // message if you want to modify the way the dragging process is started. The default // implementation captures the mouse and stays in drag mode until the user clicks the // left or right mouse button or hits ESC, at which time it releases the mouse. // Set result element of SHBDROPSOURCEACTION to TRUE if dragging is allowed, // otherwise FALSE. // Next elements of SHBDROPSOURCEACTION are filled: pWnd. ///////////////////////// SHBDSM_QUERYCONTINUEDRAG // // // SHBDSM_QUERYCONTINUEDRAG message is sent to the owner window after dragging has // begun, this message will be sent repeatedly until the drag operation is either // canceled or completed. Handle this message if you want to change the point at // which dragging is canceled or a drop occurs. The default implementation initiates // the drop or cancels the drag as follows. It cancels a drag operation when the ESC // key or the right mouse button is pressed. It initiates a drop operation when the // left mouse button is raised after dragging has started. Otherwise, it returns // S_OK and performs no further operations. Because this message is called frequently, // it should be optimized as much as possible. // Set result element of SHBDROPSOURCEACTION to DRAGDROP_S_CANCEL if the ESC key or // right button is pressed, or left button is raised before dragging starts, // to DRAGDROP_S_DROP if a drop operation should occur, or otherwise to S_OK. // Next elements of SHBDROPSOURCEACTION are filled: bEscapePressed, dwKeyState . You can handle whatever messages you would like to, unhandled messages will be handled using default implementation. */ class OX_CLASS_DECL COXSHBDropSource : public COleDropSource { public: // --- In : // --- Out : // --- Returns: // --- Effect : Constructor of the object COXSHBDropSource() : m_pOwnerWnd(NULL) {}; protected: // pointer to the window that owns this object CWnd* m_pOwnerWnd; public: // --- In : pWnd - pointer to the window which will be the recipient // of all COXSHBDropSource specific messages // --- Out : // --- Returns: pointer to the previous recipient window // --- Effect : sets recipient window for drop operation CWnd* SetOwner(CWnd* pWnd) { ASSERT(pWnd); // owner window (recipient window) must exist at that moment ASSERT(::IsWindow(pWnd->GetSafeHwnd())); CWnd* pOldWnd=m_pOwnerWnd; m_pOwnerWnd=pWnd; // return old owner window return pOldWnd; } // --- In : // --- Out : // --- Returns: pointer to the recipient window // --- Effect : retrieves a pointer to the recipient window for drop operation inline CWnd* GetOwner() const { return m_pOwnerWnd; } // --- In : nMsgID - message ID to send to recipient window // pSHBDSAction - pointer to SHBDROPSOURCEACTION structure // --- Out : // --- Returns: result of handling of the message // --- Effect : Sends COXSHBDropSource specific message to its recipient window LRESULT SendSHBDSMessage(UINT nMsgID, LPSHBDROPSOURCEACTION pSHBDSAction) { ASSERT(m_pOwnerWnd); ASSERT(::IsWindow(m_pOwnerWnd->GetSafeHwnd())); ASSERT(pSHBDSAction!=NULL); // send message to drop target owner window using NMSHBDROPTARGET structure // as lParam. Let the owner (recipient) window to handle the message return m_pOwnerWnd->SendMessage(nMsgID,(WPARAM)0,(LPARAM)pSHBDSAction); } // Overrides // // See DRAG and DROP section of OLE classes reference and // COleDragTarget reference // virtual SCODE QueryContinueDrag(BOOL bEscapePressed, DWORD dwKeyState) { ASSERT_VALID(this); // send corresponding message to the owner window SHBDROPSOURCEACTION SHBDSAction; SHBDSAction.bEscapePressed=bEscapePressed; SHBDSAction.dwKeyState=dwKeyState; if(SendSHBDSMessage(SHBDSM_QUERYCONTINUEDRAG,&SHBDSAction)) return (SCODE)SHBDSAction.result; // Call the base class implementation return COleDropSource::QueryContinueDrag(bEscapePressed,dwKeyState); } virtual SCODE GiveFeedback(DROPEFFECT dropEffect) { ASSERT_VALID(this); // send corresponding message to the owner window SHBDROPSOURCEACTION SHBDSAction; SHBDSAction.dropEffect=dropEffect; if(SendSHBDSMessage(SHBDSM_GIVEFEEDBACK,&SHBDSAction)) return (SCODE)SHBDSAction.result; // Call the base class implementation return COleDropSource::GiveFeedback(dropEffect); } virtual BOOL OnBeginDrag(CWnd* pWnd) { ASSERT_VALID(this); // send corresponding message to the owner window SHBDROPSOURCEACTION SHBDSAction; SHBDSAction.pWnd=pWnd; if(SendSHBDSMessage(SHBDSM_BEGINDRAG,&SHBDSAction)) return (BOOL)SHBDSAction.result; // Call the base class implementation return COleDropSource::OnBeginDrag(pWnd); } // //////////////////////////////////////////////////////////////////// }; ///////////////////////////////////////////////////////////////////////////// // COXSHBDropTarget /* COXSHBDropTarget is designed to provide a standard way of handling OLE drag and drop operation in any window. COXSHBDropTarget is derived from standard COleDropTarget class, so the only thing you need to do to make it work for you is just to register whatever window you need as drop target using COleDropTarget::Register(CWnd* pWnd) function (you can do it for example in this window OnCreate function). As long as window is registered as drop target and as soon as any OLE object will be dragged on that window our COXSHBDropTarget class will send to this window( we call it owner window) next messages: SHBDTM_DRAGENTER SHBDTM_DRAGLEAVE SHBDTM_DRAGOVER SHBDTM_DRAGSCROLL SHBDTM_DROP SHBDTM_DROPEX For every message wParam is set to NULL and lParam points to SHBDROPTARGETACTION structure. Return TRUE if you handle the message and FALSE if you want to run the default implementation. Structure that is used in messages sending from COXSHBDropTarget object is defined as follows: typedef struct _tagSHBDROPTARGETACTION { // // Points to the window the cursor is currently over CWnd* pWnd; // // Points to the data object that contains the data to be dropped COleDataObject* pDataObject; // // Contains the state of the modifier keys. This is a combination of any number of // the following: // MK_CONTROL // MK_SHIFT // MK_ALT // MK_LBUTTON // MK_MBUTTON // MK_RBUTTON // DWORD dwKeyState; // // Contains the current location of the cursor in client coordinates. CPoint point; // // The effect that the user chose for the drop operation. It can be one or more of // the following: // // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original // data would be established. // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_SCROLL Indicates that a drag scroll operation is about // to occur or is occurring in the target. // DROPEFFECT dropEffect; // // A list of the drop effects that the drop source supports. Drop effect values // can be combined using the bitwise OR (|) operation. Drop effects are discussed // above DROPEFFECT dropList; // // result of message handling (differ for different messages) LRESULT result; . . . . . . . . . . . . . . . . . . . } SHBDROPTARGETACTION, * LPSHBDROPTARGETACTION; Below you will find detailed description of all messages sent by COXSHBDropTarget: ///////////////////////// SHBDTM_DRAGENTER // // // Sent to owner window to notify that the cursor is first dragged into the window. // Set result element of SHBDROPTARGETACTION to the effect that would result if a drop // were attempted at the location specified by point element of SHBDROPTARGETACTION. // It can be one or more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, dwKeyState, point ///////////////////////// SHBDTM_DRAGLEAVE // // // Sent to owner window to notify that the cursor leaves the window while a dragging // operation is in effect. // Next elements of SHBDROPTARGETACTION are filled: pWnd ///////////////////////// SHBDTM_DRAGOVER // // // Sent to owner window to notify that the cursor is dragged over the window. This // message should be handled to allow drop operations to occur in the window. The // default implementation returns DROPEFFECT_NONE by default. Because this message is // called frequently during a drag-and-drop operation, it should be optimized as much // as possible. Set result element of SHBDROPTARGETACTION to the effect that would // result if a drop were attempted at the location specified by point element of // SHBDROPTARGETACTION. It can be one or more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, dwKeyState, point ///////////////////////// SHBDTM_DRAGSCROLL // // // Sent to owner window before sending SHBDTM_DRAGENTER or SHBDTM_DRAGOVER messages // to determine whether point is in the scrolling region. Handle this message when // you want to provide special behavior for this event. The default implementation // returns DROPEFFECT_NONE and scrolls the window when the cursor is dragged into the // scroll region inside the border of the window. Set result element of // SHBDROPTARGETACTION to the effect that would result if a drop were attempted at // the location specified by point element of SHBDROPTARGETACTION. It can be one or // more of the following: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, dwKeyState, point. ///////////////////////// SHBDTM_DROP // // // Sent to owner window to notify that a drop operation is about to occur. Set result // element of SHBDROPTARGETACTION to the TRUE if drop operation was successful or // FALSE otherwise. // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, point, dropEffect ///////////////////////// SHBDTM_DROPEX // // // Sent to owner window to notify that a drop operation is about to occur. This // message will be sent before SHBDTM_DROP. If you does not handle it, then // SHBDTM_DROP message will be sent. Typically, you will handle SHBDTM_DROPEX in // your child window class to support right mouse-button drag and drop. Typically, // SHBDTM_DROP message is used to handle the case of support for simple drag and drop. // By default, we simply return a dummy value to indicate the SHBDTM_DROP message // should be sent. Set result element of SHBDROPTARGETACTION to the drop effects that // describe the action associated with a drop operation. See the following list of // drop effects: // // DROPEFFECT_NONE A drop would not be allowed. // DROPEFFECT_COPY A copy operation would be performed. // DROPEFFECT_MOVE A move operation would be performed. // DROPEFFECT_LINK A link from the dropped data to the original data would // be established. // DROPEFFECT_SCROLL A drag scroll operation is about to occur or is occurring // in the target. // // Next elements of SHBDROPTARGETACTION are filled: pWnd, pDataObject, point, // dropEffect, dropList. You can handle whatever messages you would like to, unhandled messages will be handled using default implementation. An example of using of this class can be found in the source code of our COXSHBListCtrl and COXShortcutBar classes. COXSHBListCtrl uses COXSHBDropTarget to provide drag and drop of its items and handle next messages: SHBDTM_DRAGENTER, SHBDTM_DRAGLEAVE, SHBDTM_DRAGOVER, SHBDTM_DROP COXShortcutBar uses COXSHBDropTarget to support any drag and drop operation and handle next messages: SHBDTM_DRAGLEAVE, SHBDTM_DRAGOVER */ class OX_CLASS_DECL COXSHBDropTarget : public COleDropTarget { // Construction public: // --- In : // --- Out : // --- Returns: // --- Effect : Constructor of the object COXSHBDropTarget() {}; // Implementation public: // --- In : // --- Out : // --- Returns: // --- Effect : Destructor of object virtual ~COXSHBDropTarget() {}; // --- In : nMsgID - message ID to send to owner window // pSHBDTAction - pointer to SHBDROPTARGETACTION structure // --- Out : // --- Returns: result of handling of the message // --- Effect : Sends COXSHBDropTarget specific message to its owner window LRESULT SendSHBDTMessage(UINT nMsgID, LPSHBDROPTARGETACTION pSHBDTAction) { ASSERT(pSHBDTAction->pWnd); ASSERT(::IsWindow(pSHBDTAction->pWnd->GetSafeHwnd())); ASSERT(pSHBDTAction!=NULL); // send message to the pSHBDTAction->pWnd window which is registered as drop // target using NMSHBDROPTARGET structure as lParam. return pSHBDTAction->pWnd->SendMessage(nMsgID,(WPARAM)0,(LPARAM)pSHBDTAction); } // Overridables // // These members can be overridden for an OLE drop target // See DRAG and DROP section of OLE classes reference and COleDragTarget reference // virtual DROPEFFECT OnDragScroll(CWnd* pWnd, DWORD dwKeyState, CPoint point) { ASSERT_VALID(this); // notify pWnd about DragScroll event SHBDROPTARGETACTION SHBDTAction; SHBDTAction.pWnd=pWnd; SHBDTAction.dwKeyState=dwKeyState; SHBDTAction.point=point; SendSHBDTMessage(SHBDTM_DRAGSCROLL,&SHBDTAction); // DROPEFFECT_SCROLL means do the default if((DROPEFFECT)SHBDTAction.result!=DROPEFFECT_SCROLL) { return (DROPEFFECT)SHBDTAction.result; } if(pWnd->IsKindOf(RUNTIME_CLASS(CView))) { return COleDropTarget::OnDragScroll(pWnd,dwKeyState,point); } // get client rectangle of destination window CRect rectClient; pWnd->GetClientRect(&rectClient); CRect rect=rectClient; // hit-test against inset region UINT nTimerID=MAKEWORD(-1, -1); rect.InflateRect(-nScrollInset, -nScrollInset); if(rectClient.PtInRect(point) && !rect.PtInRect(point)) { // check if need to scroll BOOL bHasScrollHorz=((pWnd->GetStyle()&WS_HSCROLL)==WS_HSCROLL); BOOL bHasScrollVert=((pWnd->GetStyle()&WS_VSCROLL)==WS_VSCROLL); SCROLLINFO scrollInfo={ sizeof(SCROLLINFO) }; if(pWnd->GetScrollInfo(SB_HORZ,&scrollInfo)) { if(bHasScrollHorz) { if(scrollInfo.nMax==0 || scrollInfo.nPage==0 || scrollInfo.nMax<(int)scrollInfo.nPage) { bHasScrollHorz=FALSE; } } else { if(scrollInfo.nMax>0 && scrollInfo.nPage>0 && scrollInfo.nMax>=(int)scrollInfo.nPage) { bHasScrollHorz=TRUE; } } } if(pWnd->GetScrollInfo(SB_VERT,&scrollInfo)) { if(bHasScrollVert) { if(scrollInfo.nMax==0 || scrollInfo.nPage==0 || scrollInfo.nMax<(int)scrollInfo.nPage) { bHasScrollVert=FALSE; } } else { if(scrollInfo.nMax>0 && scrollInfo.nPage>0 && scrollInfo.nMax>=(int)scrollInfo.nPage) { bHasScrollVert=TRUE; } } } // determine which way to scroll along both X & Y axis if(bHasScrollHorz && point.x=rect.right) { nTimerID=MAKEWORD(LOBYTE(nTimerID), SB_LINEDOWN); } if(bHasScrollVert && point.y=rect.bottom) { nTimerID=MAKEWORD(SB_LINEDOWN,HIBYTE(nTimerID)); } } if(nTimerID==MAKEWORD(-1,-1)) { if(m_nTimerID!=MAKEWORD(-1,-1)) { // send fake OnDragEnter when transition from scroll->normal COleDataObject dataObject; dataObject.Attach(m_lpDataObject,FALSE); OnDragEnter(pWnd,&dataObject,dwKeyState,point); m_nTimerID=MAKEWORD(-1,-1); } return DROPEFFECT_NONE; } // save tick count when timer ID changes DWORD dwTick=GetTickCount(); if(nTimerID!=m_nTimerID) { m_dwLastTick=dwTick; m_nScrollDelay=nScrollDelay; } // scroll if necessary if((dwTick-m_dwLastTick)>m_nScrollDelay) { BOOL bScrollHorz=(LOBYTE(nTimerID)==-1); if(bScrollHorz) { pWnd->SendMessage( WM_HSCROLL,MAKEWPARAM(HIBYTE(nTimerID),0),(LPARAM)NULL); } else { pWnd->SendMessage( WM_VSCROLL,MAKEWPARAM(LOBYTE(nTimerID),0),(LPARAM)NULL); } m_dwLastTick=dwTick; m_nScrollDelay=nScrollInterval; } if (m_nTimerID == MAKEWORD(-1, -1)) { // send fake OnDragLeave when transitioning from normal->scroll OnDragLeave(pWnd); } m_nTimerID=nTimerID; // check for force link if((SHBDTAction.dwKeyState & (MK_CONTROL|MK_SHIFT))==(MK_CONTROL|MK_SHIFT)) { SHBDTAction.result=(LRESULT)(DROPEFFECT_SCROLL|DROPEFFECT_LINK); } // check for force copy else if((SHBDTAction.dwKeyState & MK_CONTROL)==MK_CONTROL) { SHBDTAction.result=(LRESULT)(DROPEFFECT_SCROLL|DROPEFFECT_COPY); } // check for force move else if((SHBDTAction.dwKeyState & MK_ALT)==MK_ALT || (SHBDTAction.dwKeyState & MK_SHIFT)==MK_SHIFT) { SHBDTAction.result=(LRESULT)(DROPEFFECT_SCROLL|DROPEFFECT_MOVE); } // default else { SHBDTAction.result=(LRESULT)(DROPEFFECT_SCROLL|DROPEFFECT_NONE); } return (DROPEFFECT)SHBDTAction.result; } virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) { ASSERT_VALID(this); // notify pWnd about DragEnter event SHBDROPTARGETACTION SHBDTAction; SHBDTAction.pWnd=pWnd; SHBDTAction.pDataObject=pDataObject; SHBDTAction.dwKeyState=dwKeyState; SHBDTAction.point=point; SHBDTAction.dropEffect=DROPEFFECT_NONE; if(SendSHBDTMessage(SHBDTM_DRAGENTER,&SHBDTAction)) return (DROPEFFECT)SHBDTAction.result; // default implementation return DROPEFFECT_NONE; } virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) { ASSERT_VALID(this); // notify pWnd about DragOver event SHBDROPTARGETACTION SHBDTAction; SHBDTAction.pWnd=pWnd; SHBDTAction.pDataObject=pDataObject; SHBDTAction.dwKeyState=dwKeyState; SHBDTAction.point=point; SHBDTAction.dropEffect=DROPEFFECT_NONE; if(SendSHBDTMessage(SHBDTM_DRAGOVER,&SHBDTAction)) return (DROPEFFECT)SHBDTAction.result; // default implementation return DROPEFFECT_NONE; } virtual void OnDragLeave(CWnd* pWnd) { ASSERT_VALID(this); // notify pWnd about DragLeave event SHBDROPTARGETACTION SHBDTAction; SHBDTAction.pWnd=pWnd; if(SendSHBDTMessage(SHBDTM_DRAGLEAVE,&SHBDTAction)) return; // Call base class implementation COleDropTarget::OnDragLeave(pWnd); } virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point) { ASSERT_VALID(this); // notify pWnd about Drop event SHBDROPTARGETACTION SHBDTAction; SHBDTAction.pWnd=pWnd; SHBDTAction.pDataObject=pDataObject; SHBDTAction.point=point; SHBDTAction.dropEffect=dropEffect; if(SendSHBDTMessage(SHBDTM_DROP,&SHBDTAction)) return (BOOL)SHBDTAction.result; // default implementation return FALSE; } virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point) { ASSERT_VALID(this); // notify pWnd about DropEx event SHBDROPTARGETACTION SHBDTAction; SHBDTAction.pWnd=pWnd; SHBDTAction.pDataObject=pDataObject; SHBDTAction.point=point; SHBDTAction.dropEffect=dropDefault; SHBDTAction.dropList=dropList; if(SendSHBDTMessage(SHBDTM_DROPEX,&SHBDTAction)) return (DROPEFFECT)SHBDTAction.result; // Call base class implementation return COleDropTarget::OnDropEx(pWnd,pDataObject,dropDefault,dropList,point); } // ////////////////////////////////////////////////// }; ///////////////////////////////////////////////////////////////////////////// // COXBaseDragDropWnd window template class COXBaseDragDropWnd : public PARENTWND { // Construction public: COXBaseDragDropWnd(); // Attributes protected: // default COleDropSource for drag'n'drop operation COXSHBDropSource m_oleDropSource; // default COleDropTarget for drag'n'drop operation COXSHBDropTarget m_oleDropTarget; // flag that specifies if any drag and drop operation is undergoing BOOL m_bDragDropOperation; // flag that specifies whether this object launched current drag'n'drop // operation or not BOOL m_bDragDropOwner; // Operations public: // --- In : // --- Out : // --- Returns: A pointer to internal COleDropSource object // --- Effect : Retrieves a pointer to internal COleDropSource object that will // allow this object to handle drag'n'drop operation virtual COleDropSource* GetDropSource() { // owner window must exist at that moment ASSERT(::IsWindow(GetSafeHwnd())); m_oleDropSource.SetOwner(this); return &m_oleDropSource; } // --- In : // --- Out : // --- Returns: TRUE if this object has started current drag'n'drop operation; // otherwise FALSE // --- Effect : Retrieves the flag that specifies whether this object has // started current drag'n'drop operation inline BOOL IsDragDropOwner() const { return m_bDragDropOwner; } // --- In : // --- Out : // --- Returns: A pointer to internal COleDropTarget object // --- Effect : Retrieves a pointer to internal COleDropTarget object that will // allow this object to handle drag'n'drop operation virtual COleDropTarget* GetDropTarget() { return &m_oleDropTarget; } // registers control as drop target virtual void RegisterAsDropTarget(); protected: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(COXBaseDragDropWnd) //}}AFX_VIRTUAL // Implementation public: virtual ~COXBaseDragDropWnd(); protected: virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); // drag and drop support virtual LONG OnDragScroll(WPARAM wParam, LPARAM lParam); virtual LONG OnDragEnter(WPARAM wParam, LPARAM lParam); virtual LONG OnDragOver(WPARAM wParam, LPARAM lParam); virtual LONG OnDragLeave(WPARAM wParam, LPARAM lParam); virtual LONG OnDrop(WPARAM wParam, LPARAM lParam); // drop files support virtual void OnDropFiles(HDROP hDropInfo); }; //////////////////////////////////////////////////////////////////////////// template COXBaseDragDropWnd::COXBaseDragDropWnd() : m_bDragDropOperation(FALSE), m_bDragDropOwner(FALSE) { // has to be derived from CWnd or its derivates if(!IsKindOf(RUNTIME_CLASS(CWnd))) { TRACE(_T("COXBaseDragDropWnd has to be derived from CWnd or its derivates\n")); AfxThrowNotSupportedException(); } } template COXBaseDragDropWnd::~COXBaseDragDropWnd() { } template LRESULT COXBaseDragDropWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif LRESULT lResult=0; switch(message) { case SHBDTM_DRAGSCROLL: { lResult=OnDragScroll(wParam,lParam); } break; case SHBDTM_DRAGENTER: { // set flag that specifies that drag'n'drop operation is active m_bDragDropOperation=TRUE; lResult=OnDragEnter(wParam,lParam); } break; case SHBDTM_DRAGLEAVE: { lResult=OnDragLeave(wParam,lParam); // reset flag that specifies that drag'n'drop operation is active m_bDragDropOperation=FALSE; } break; case SHBDTM_DRAGOVER: { lResult=OnDragOver(wParam,lParam); } break; case SHBDTM_DROP: { lResult=OnDrop(wParam,lParam); if(!IsDragDropOwner()) { // reset flag that specifies that drag'n'drop operation is active m_bDragDropOperation=FALSE; } } break; case WM_DROPFILES: { OnDropFiles((HDROP)wParam); lResult=0; } break; default: { lResult=PARENTWND::WindowProc(message,wParam,lParam); } break; } return lResult; } template LONG COXBaseDragDropWnd::OnDragScroll(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); return FALSE; } template LONG COXBaseDragDropWnd::OnDragEnter(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); return FALSE; } template LONG COXBaseDragDropWnd::OnDragOver(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); return FALSE; } template LONG COXBaseDragDropWnd::OnDragLeave(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); return FALSE; } template LONG COXBaseDragDropWnd::OnDrop(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); return FALSE; } template void COXBaseDragDropWnd::RegisterAsDropTarget() { COleDropTarget* pOleDropTarget=GetDropTarget(); ASSERT(pOleDropTarget!=NULL); if(!pOleDropTarget->Register(this)) { TRACE(_T("COXBaseDragDropWnd::RegisterAsDropTarget: failed to register with COleDropTarget object. You've probably forgotten to initialize OLE libraries using AfxOleInit() function\n")); } } template void COXBaseDragDropWnd::OnDropFiles(HDROP hDropInfo) { UNREFERENCED_PARAMETER(hDropInfo); } ///////////////////////////////////////////////////////////////////// #ifndef OXDROPDOWNTREE_AUTOEXPAND_TIMER_ID #define OXDROPDOWNTREE_AUTOEXPAND_TIMER_ID 238 #endif #ifndef OXDROPDOWNTREE_AUTOEXPAND_TIMER_DELAY #define OXDROPDOWNTREE_AUTOEXPAND_TIMER_DELAY 1500 #endif ///////////////////////////////////////////////////////////////////////////// // COXBaseDragDropTree window template class COXBaseDragDropTree : public COXBaseDragDropWnd { // Construction public: COXBaseDragDropTree(); // Attributes protected: // timer id for autoexpansion as a result of drag'n'drop operation UINT m_nAutoExpandTimer; // Operations protected: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(COXBaseDragDropTree) //}}AFX_VIRTUAL // Implementation public: virtual ~COXBaseDragDropTree(); protected: // drag and drop support virtual LONG OnDragScroll(WPARAM wParam, LPARAM lParam); virtual LONG OnDragEnter(WPARAM wParam, LPARAM lParam); virtual LONG OnDragOver(WPARAM wParam, LPARAM lParam); virtual LONG OnDragLeave(WPARAM wParam, LPARAM lParam); virtual LONG OnDrop(WPARAM wParam, LPARAM lParam); // drop files support virtual void OnDropFiles(HDROP hDropInfo); // drop overridables virtual BOOL CanAcceptDrop(HTREEITEM hItem, HTREEITEM hParentItem, BOOL bDropAfter, LPSHBDROPTARGETACTION pSHBDTAction); virtual void AcceptDrop(HTREEITEM hItem, HTREEITEM hParentItem, BOOL bDropAfter, LPSHBDROPTARGETACTION pSHBDTAction); virtual BOOL CanAcceptDropFiles(HTREEITEM hItem, HTREEITEM hParentItem, BOOL bDropAfter, HDROP hDropInfo); virtual void AcceptDropFiles(HTREEITEM hItem, HTREEITEM hParentItem, BOOL bDropAfter, HDROP hDropInfo); protected: virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); }; //////////////////////////////////////////////////////////////////////////// template COXBaseDragDropTree::COXBaseDragDropTree() : m_nAutoExpandTimer(0) { } template COXBaseDragDropTree::~COXBaseDragDropTree() { } template LRESULT COXBaseDragDropTree::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { #if defined (_WINDLL) #if defined (_AFXDLL) AFX_MANAGE_STATE(AfxGetAppModuleState()); #else AFX_MANAGE_STATE(AfxGetStaticModuleState()); #endif #endif LRESULT lResult=0; switch(message) { case WM_TIMER: { if(wParam==m_nAutoExpandTimer) { HTREEITEM hDropItem=GetDropHilightItem(); if(hDropItem!=NULL) { // check if mouse is still over the drop highlighted item CPoint ptMouse; ::GetCursorPos(&ptMouse); ScreenToClient(&ptMouse); UINT uiFlags=0; HTREEITEM hHitTestItem=HitTest(ptMouse,&uiFlags); if(hHitTestItem==hDropItem && (uiFlags & TVHT_ONITEM )) { // reset timer VERIFY(KillTimer(m_nAutoExpandTimer)); m_nAutoExpandTimer=0; // expand node VERIFY(Expand(hDropItem,TVE_EXPAND)); } } lResult=0; } else { lResult=COXBaseDragDropWnd::WindowProc( message,wParam,lParam); } } break; default: { lResult=COXBaseDragDropWnd::WindowProc(message,wParam,lParam); } break; } return lResult; } template LONG COXBaseDragDropTree::OnDragScroll(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDragScroll(wParam,lParam)) { return TRUE; } // lParam is the pointer to SHBDROPTARGETACTION structure LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam; ASSERT(pSHBDTAction!=NULL); ASSERT(pSHBDTAction->pWnd); ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd()); pSHBDTAction->result=(LRESULT)DROPEFFECT_SCROLL; return TRUE; } template LONG COXBaseDragDropTree::OnDragEnter(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDragEnter(wParam,lParam)) { return TRUE; } return OnDragOver(wParam,lParam); } template LONG COXBaseDragDropTree::OnDragOver(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDragOver(wParam,lParam)) { return TRUE; } // lParam is the pointer to SHBDROPTARGETACTION structure LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam; ASSERT(pSHBDTAction!=NULL); ASSERT(pSHBDTAction->pWnd); ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd()); // Check if the control key was pressed if((pSHBDTAction->dwKeyState & MK_CONTROL)==MK_CONTROL) { pSHBDTAction->result=(LRESULT)DROPEFFECT_COPY; } else { pSHBDTAction->result=(LRESULT)DROPEFFECT_MOVE; } BOOL bAcceptDrop=FALSE; UINT uFlags=0; // get the item that is below cursor HTREEITEM hDropItem=HitTest(pSHBDTAction->point,&uFlags); if(hDropItem!=NULL) { HTREEITEM hOldDropItem=GetDropHilightItem(); if(hOldDropItem!=hDropItem) { // reset the timer for autoexpansion if(m_nAutoExpandTimer!=0) { KillTimer(m_nAutoExpandTimer); m_nAutoExpandTimer=0; } // highlight it SelectDropTarget(hDropItem); if((GetItemState(hDropItem,TVIS_EXPANDED) & TVIS_EXPANDED)==0 && ItemHasChildren(hDropItem)) { m_nAutoExpandTimer=SetTimer(OXDROPDOWNTREE_AUTOEXPAND_TIMER_ID, OXDROPDOWNTREE_AUTOEXPAND_TIMER_DELAY,NULL); ASSERT(m_nAutoExpandTimer==OXDROPDOWNTREE_AUTOEXPAND_TIMER_ID); } } // check if this item accepts drop operation HTREEITEM hParentItem=GetParentItem(hDropItem); CRect rectItem; GetItemRect(hDropItem,rectItem,FALSE); bAcceptDrop=CanAcceptDrop(hDropItem,hParentItem, (pSHBDTAction->point.y > rectItem.top+rectItem.Height()/2),pSHBDTAction); } else { // no item to drop on, ask root item to insert as last child bAcceptDrop=CanAcceptDrop(TVI_LAST,TVI_ROOT,TRUE,pSHBDTAction); } if(!bAcceptDrop) { pSHBDTAction->result=(LRESULT)DROPEFFECT_NONE; } return bAcceptDrop; } template LONG COXBaseDragDropTree::OnDragLeave(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDragLeave(wParam,lParam)) { return TRUE; } SelectDropTarget(NULL); return TRUE; } template LONG COXBaseDragDropTree::OnDrop(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDrop(wParam,lParam)) { return TRUE; } // lParam is the pointer to SHBDROPTARGETACTION structure LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam; ASSERT(pSHBDTAction!=NULL); ASSERT(pSHBDTAction->pWnd); ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd()); pSHBDTAction->result=(LRESULT)FALSE; // if dragged item is to be copied or moved if((pSHBDTAction->dropEffect&DROPEFFECT_COPY)!=0 || (pSHBDTAction->dropEffect&DROPEFFECT_MOVE)!=0) { UINT uFlags=0; HTREEITEM hDropItem=HitTest(pSHBDTAction->point,&uFlags); if(hDropItem!=NULL) { ASSERT(hDropItem==GetDropHilightItem()); // remove highlight SelectDropTarget(NULL); // check if this item accepts drop operation HTREEITEM hParentItem=GetParentItem(hDropItem); CRect rectItem; GetItemRect(hDropItem,rectItem,FALSE); if(CanAcceptDrop(hDropItem,hParentItem, (pSHBDTAction->point.y > rectItem.top+rectItem.Height()/2), pSHBDTAction)) { AcceptDrop(hDropItem,hParentItem, (pSHBDTAction->point.y > rectItem.top+rectItem.Height()/2), pSHBDTAction); } } else { // no item to drop on, ask root item to insert as last child if(CanAcceptDrop(TVI_LAST,TVI_ROOT,TRUE,pSHBDTAction)) { AcceptDrop(TVI_LAST,TVI_ROOT,TRUE,pSHBDTAction); } } } return TRUE; } template void COXBaseDragDropTree::OnDropFiles(HDROP hDropInfo) { // retrieve number of files UINT nFileCount=::DragQueryFile(hDropInfo,(UINT)-1,NULL,0); if(nFileCount>0) { CPoint ptDrop; if(::DragQueryPoint(hDropInfo,&ptDrop)) { // analyze drop position UINT uFlags=0; HTREEITEM hDropItem=HitTest(ptDrop,&uFlags); if(hDropItem!=NULL) { // check if this item accepts drop operation HTREEITEM hParentItem=GetParentItem(hDropItem); CRect rectItem; GetItemRect(hDropItem,rectItem,FALSE); if(CanAcceptDropFiles(hDropItem,hParentItem, (ptDrop.y > rectItem.top+rectItem.Height()/2),hDropInfo)) { AcceptDropFiles(hDropItem,hParentItem, (ptDrop.y > rectItem.top+rectItem.Height()/2),hDropInfo); } } else { // no item to drop on, ask root item to insert as last child if(CanAcceptDropFiles(TVI_LAST,TVI_ROOT,TRUE,hDropInfo)) { AcceptDropFiles(TVI_LAST,TVI_ROOT,TRUE,hDropInfo); } } } } ::DragFinish(hDropInfo); } template BOOL COXBaseDragDropTree::CanAcceptDrop(HTREEITEM hItem, HTREEITEM hParentItem, BOOL bDropAfter, LPSHBDROPTARGETACTION pSHBDTAction) { UNREFERENCED_PARAMETER(hItem); UNREFERENCED_PARAMETER(hParentItem); UNREFERENCED_PARAMETER(bDropAfter); UNREFERENCED_PARAMETER(pSHBDTAction); return FALSE; } template void COXBaseDragDropTree::AcceptDrop(HTREEITEM hItem, HTREEITEM hParentItem, BOOL bDropAfter, LPSHBDROPTARGETACTION pSHBDTAction) { UNREFERENCED_PARAMETER(hItem); UNREFERENCED_PARAMETER(hParentItem); UNREFERENCED_PARAMETER(bDropAfter); UNREFERENCED_PARAMETER(pSHBDTAction); } template BOOL COXBaseDragDropTree::CanAcceptDropFiles(HTREEITEM hItem, HTREEITEM hParentItem, BOOL bDropAfter, HDROP hDropInfo) { UNREFERENCED_PARAMETER(hItem); UNREFERENCED_PARAMETER(hParentItem); UNREFERENCED_PARAMETER(bDropAfter); UNREFERENCED_PARAMETER(hDropInfo); return FALSE; } template void COXBaseDragDropTree::AcceptDropFiles(HTREEITEM hItem, HTREEITEM hParentItem, BOOL bDropAfter, HDROP hDropInfo) { UNREFERENCED_PARAMETER(hItem); UNREFERENCED_PARAMETER(hParentItem); UNREFERENCED_PARAMETER(bDropAfter); UNREFERENCED_PARAMETER(hDropInfo); } ////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // COXBaseDragDropList window template class COXBaseDragDropList : public COXBaseDragDropWnd { // Construction public: COXBaseDragDropList(); // Attributes protected: // Operations protected: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(COXBaseDragDropList) //}}AFX_VIRTUAL // Implementation public: virtual ~COXBaseDragDropList(); protected: // drag and drop support virtual LONG OnDragScroll(WPARAM wParam, LPARAM lParam); virtual LONG OnDragEnter(WPARAM wParam, LPARAM lParam); virtual LONG OnDragOver(WPARAM wParam, LPARAM lParam); virtual LONG OnDragLeave(WPARAM wParam, LPARAM lParam); virtual LONG OnDrop(WPARAM wParam, LPARAM lParam); // drop files support virtual void OnDropFiles(HDROP hDropInfo); // drop overridables virtual BOOL CanAcceptDrop( int nItemIndex, BOOL bDropAfter, LPSHBDROPTARGETACTION pSHBDTAction); virtual void AcceptDrop( int nItemIndex, BOOL bDropAfter, LPSHBDROPTARGETACTION pSHBDTAction); virtual BOOL CanAcceptDropFiles( int nItemIndex, BOOL bDropAfter, HDROP hDropInfo); virtual void AcceptDropFiles( int nItemIndex, BOOL bDropAfter, HDROP hDropInfo); }; //////////////////////////////////////////////////////////////////////////// template COXBaseDragDropList::COXBaseDragDropList() { } template COXBaseDragDropList::~COXBaseDragDropList() { } template LONG COXBaseDragDropList::OnDragScroll(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDragScroll(wParam,lParam)) { return TRUE; } // lParam is the pointer to SHBDROPTARGETACTION structure LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam; ASSERT(pSHBDTAction!=NULL); ASSERT(pSHBDTAction->pWnd); ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd()); pSHBDTAction->result=(LRESULT)DROPEFFECT_SCROLL; return TRUE; } template LONG COXBaseDragDropList::OnDragEnter(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDragEnter(wParam,lParam)) { return TRUE; } return OnDragOver(wParam,lParam); } template LONG COXBaseDragDropList::OnDragOver(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDragOver(wParam,lParam)) { return TRUE; } // lParam is the pointer to SHBDROPTARGETACTION structure LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam; ASSERT(pSHBDTAction!=NULL); ASSERT(pSHBDTAction->pWnd); ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd()); // Check if the control key was pressed if((pSHBDTAction->dwKeyState & MK_CONTROL)==MK_CONTROL) { pSHBDTAction->result=(LRESULT)DROPEFFECT_COPY; } else { pSHBDTAction->result=(LRESULT)DROPEFFECT_MOVE; } BOOL bAcceptDrop=FALSE; UINT uFlags=0; // get the item that is below cursor int nItemIndex=HitTest(pSHBDTAction->point,&uFlags); if(nItemIndex!=-1) { int nDropItemIndex=GetNextItem(-1,LVNI_DROPHILITED); if(nDropItemIndex!=nItemIndex) { SetItemState(nDropItemIndex,0,LVIS_DROPHILITED); SetItemState(nItemIndex,LVIS_DROPHILITED,LVIS_DROPHILITED); } CRect rectItem; GetItemRect(nItemIndex,rectItem,LVIR_BOUNDS); bAcceptDrop=CanAcceptDrop(nItemIndex, (pSHBDTAction->point.y > rectItem.top+rectItem.Height()/2),pSHBDTAction); } else { // no item to drop on, ask to insert as last item bAcceptDrop=CanAcceptDrop(GetItemCount(),FALSE,pSHBDTAction); } if(!bAcceptDrop) { pSHBDTAction->result=(LRESULT)DROPEFFECT_NONE; } return bAcceptDrop; } template LONG COXBaseDragDropList::OnDragLeave(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDragLeave(wParam,lParam)) { return TRUE; } int nDropItemIndex=GetNextItem(-1,LVNI_DROPHILITED); if(nDropItemIndex!=-1) { // remove highlight SetItemState(nDropItemIndex,0,LVIS_DROPHILITED); } return TRUE; } template LONG COXBaseDragDropList::OnDrop(WPARAM wParam, LPARAM lParam) { if(COXBaseDragDropWnd::OnDrop(wParam,lParam)) { return TRUE; } // lParam is the pointer to SHBDROPTARGETACTION structure LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam; ASSERT(pSHBDTAction!=NULL); ASSERT(pSHBDTAction->pWnd); ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd()); pSHBDTAction->result=(LRESULT)FALSE; // if dragged item is to be copied or moved if((pSHBDTAction->dropEffect&DROPEFFECT_COPY)!=0 || (pSHBDTAction->dropEffect&DROPEFFECT_MOVE)!=0) { UINT uFlags=0; int nDropItemIndex=HitTest(pSHBDTAction->point,&uFlags); if(nDropItemIndex!=-1) { ASSERT(nDropItemIndex==GetNextItem(-1,LVNI_DROPHILITED)); // remove highlight SetItemState(nDropItemIndex,0,LVIS_DROPHILITED); // check if this item accepts drop operation CRect rectItem; GetItemRect(nDropItemIndex,rectItem,LVIR_BOUNDS); if(CanAcceptDrop(nDropItemIndex, (pSHBDTAction->point.y > rectItem.top+rectItem.Height()/2), pSHBDTAction)) { AcceptDrop(nDropItemIndex, (pSHBDTAction->point.y > rectItem.top+rectItem.Height()/2), pSHBDTAction); } } else { // no item to drop on, ask to insert as last item if(CanAcceptDrop(GetItemCount(),FALSE,pSHBDTAction)) { AcceptDrop(GetItemCount(),FALSE,pSHBDTAction); } } } return TRUE; } template void COXBaseDragDropList::OnDropFiles(HDROP hDropInfo) { // retrieve number of files UINT nFileCount=::DragQueryFile(hDropInfo,(UINT)-1,NULL,0); if(nFileCount>0) { CPoint ptDrop; if(::DragQueryPoint(hDropInfo,&ptDrop)) { // analyze drop position UINT uFlags=0; int nDropItemIndex=HitTest(ptDrop,&uFlags); if(nDropItemIndex!=-1) { // check if this item accepts drop operation CRect rectItem; GetItemRect(nDropItemIndex,rectItem,LVIR_BOUNDS); if(CanAcceptDropFiles(nDropItemIndex, (ptDrop.y > rectItem.top+rectItem.Height()/2),hDropInfo)) { AcceptDropFiles(nDropItemIndex, (ptDrop.y > rectItem.top+rectItem.Height()/2),hDropInfo); } } else { // no item to drop on, ask to insert as last item if(CanAcceptDropFiles(GetItemCount(),FALSE,hDropInfo)) { AcceptDropFiles(GetItemCount(),FALSE,hDropInfo); } } } } ::DragFinish(hDropInfo); } template BOOL COXBaseDragDropList::CanAcceptDrop(int nItemIndex, BOOL bDropAfter, LPSHBDROPTARGETACTION pSHBDTAction) { UNREFERENCED_PARAMETER(nItemIndex); UNREFERENCED_PARAMETER(bDropAfter); UNREFERENCED_PARAMETER(pSHBDTAction); return FALSE; } template void COXBaseDragDropList::AcceptDrop(int nItemIndex, BOOL bDropAfter, LPSHBDROPTARGETACTION pSHBDTAction) { UNREFERENCED_PARAMETER(nItemIndex); UNREFERENCED_PARAMETER(bDropAfter); UNREFERENCED_PARAMETER(pSHBDTAction); } template BOOL COXBaseDragDropList::CanAcceptDropFiles(int nItemIndex, BOOL bDropAfter, HDROP hDropInfo) { UNREFERENCED_PARAMETER(nItemIndex); UNREFERENCED_PARAMETER(bDropAfter); UNREFERENCED_PARAMETER(hDropInfo); return FALSE; } template void COXBaseDragDropList::AcceptDropFiles(int nItemIndex, BOOL bDropAfter, HDROP hDropInfo) { UNREFERENCED_PARAMETER(nItemIndex); UNREFERENCED_PARAMETER(bDropAfter); UNREFERENCED_PARAMETER(hDropInfo); } ////////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Developer Studio will insert additional declarations immediately before the previous line. #endif // !defined(_OX_DRAGDROPSUPPORT_)