/************************************************************************* ** ** OLE 2 Sample Code ** ** dragdrop.c ** ** This file contains the major interfaces, methods and related support ** functions for implementing Drag/Drop. The code contained in this ** file is used by BOTH the Container and Server (Object) versions ** of the Outline sample code. ** The Drag/Drop support includes the following implementation objects: ** ** OleDoc Object ** exposed interfaces: ** IDropSource ** IDropTarget ** ** (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved ** *************************************************************************/ #include "outline.h" OLEDBGDATA extern LPOUTLINEAPP g_lpApp; #if defined( USE_DRAGDROP ) /* OleDoc_QueryDrag * ---------------- * Check to see if Drag operation should be initiated. A Drag operation * should be initiated when the mouse in either the top 10 pixels of the * selected list box entry or in the bottom 10 pixels of the last selected * item. */ BOOL OleDoc_QueryDrag(LPOLEDOC lpOleDoc, int y) { LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList; LINERANGE LineRange; if ( LineList_GetSel( lpLL, (LPLINERANGE)&LineRange) ) { RECT rect; if (!LineList_GetLineRect(lpLL,LineRange.m_nStartLine,(LPRECT)&rect)) return FALSE ; if ( rect.top <= y && y <= rect.top + DD_SEL_THRESH ) return TRUE; LineList_GetLineRect( lpLL, LineRange.m_nEndLine, (LPRECT)&rect ); if ( rect.bottom >= y && y >= rect.bottom - DD_SEL_THRESH ) return TRUE; } return FALSE; } /* OleDoc_DoDragScroll * ------------------- * Check to see if Drag scroll operation should be initiated. A Drag scroll * operation should be initiated when the mouse has remained in the active * scroll area (11 pixels frame around border of window) for a specified * amount of time (50ms). */ BOOL OleDoc_DoDragScroll(LPOLEDOC lpOleDoc, POINTL pointl) { LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp; LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList; HWND hWndListBox = LineList_GetWindow(lpLL); DWORD dwScrollDir = SCROLLDIR_NULL; DWORD dwTime = GetCurrentTime(); int nScrollInset = lpOleApp->m_nScrollInset; int nScrollDelay = lpOleApp->m_nScrollDelay; int nScrollInterval = lpOleApp->m_nScrollInterval; POINT point; RECT rect; if ( lpLL->m_nNumLines == 0 ) return FALSE; point.x = (int)pointl.x; point.y = (int)pointl.y; ScreenToClient( hWndListBox, &point); GetClientRect ( hWndListBox, (LPRECT) &rect ); if (rect.top <= point.y && point.y<=(rect.top+nScrollInset)) dwScrollDir = SCROLLDIR_UP; else if ((rect.bottom-nScrollInset) <= point.y && point.y <= rect.bottom) dwScrollDir = SCROLLDIR_DOWN; if (lpOleDoc->m_dwTimeEnterScrollArea) { /* cursor was already in Scroll Area */ if (! dwScrollDir) { /* cusor moved OUT of scroll area. ** clear "EnterScrollArea" time. */ lpOleDoc->m_dwTimeEnterScrollArea = 0L; lpOleDoc->m_dwNextScrollTime = 0L; lpOleDoc->m_dwLastScrollDir = SCROLLDIR_NULL; } else if (dwScrollDir != lpOleDoc->m_dwLastScrollDir) { /* cusor moved into a different direction scroll area. ** reset "EnterScrollArea" time to start a new 50ms delay. */ lpOleDoc->m_dwTimeEnterScrollArea = dwTime; lpOleDoc->m_dwNextScrollTime = dwTime + (DWORD)nScrollDelay; lpOleDoc->m_dwLastScrollDir = dwScrollDir; } else if (dwTime && dwTime >= lpOleDoc->m_dwNextScrollTime) { LineList_Scroll ( lpLL, dwScrollDir ); // Scroll doc NOW lpOleDoc->m_dwNextScrollTime = dwTime + (DWORD)nScrollInterval; } } else { if (dwScrollDir) { /* cusor moved INTO a scroll area. ** reset "EnterScrollArea" time to start a new 50ms delay. */ lpOleDoc->m_dwTimeEnterScrollArea = dwTime; lpOleDoc->m_dwNextScrollTime = dwTime + (DWORD)nScrollDelay; lpOleDoc->m_dwLastScrollDir = dwScrollDir; } } return (dwScrollDir ? TRUE : FALSE); } /* OleDoc_QueryDrop ** ---------------- ** Check if the desired drop operation (identified by the given key ** state) is possible at the current mouse position (pointl). */ BOOL OleDoc_QueryDrop ( LPOLEDOC lpOleDoc, DWORD grfKeyState, POINTL pointl, BOOL fDragScroll, LPDWORD lpdwEffect ) { LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList; LINERANGE linerange; short nIndex = LineList_GetLineIndexFromPointl( lpLL, pointl ); DWORD dwScrollEffect = 0L; DWORD dwOKEffects = *lpdwEffect; /* check if the cursor is in the active scroll area, if so need the ** special scroll cursor. */ if (fDragScroll) dwScrollEffect = DROPEFFECT_SCROLL; /* if we have already determined that the source does NOT have any ** acceptable data for us, the return NO-DROP */ if (! lpOleDoc->m_fCanDropCopy && ! lpOleDoc->m_fCanDropLink) goto dropeffect_none; /* if the Drag/Drop is local to our document, we can NOT accept a ** drop in the middle of the current selection (which is the exact ** data that is being dragged!). */ if (lpOleDoc->m_fLocalDrag) { LineList_GetSel( lpLL, (LPLINERANGE)&linerange ); if (linerange.m_nStartLine <= nIndex && nIndexm_fCanDropCopy) *lpdwEffect = DROPEFFECT_MOVE; else if ((DROPEFFECT_COPY & dwOKEffects) && lpOleDoc->m_fCanDropCopy) *lpdwEffect = DROPEFFECT_COPY; else if ((DROPEFFECT_LINK & dwOKEffects) && lpOleDoc->m_fCanDropLink) *lpdwEffect = DROPEFFECT_LINK; else goto dropeffect_none; } else { /* OLE2NOTE: we should check if the drag source application allows ** the desired drop effect. */ if (!(*lpdwEffect & dwOKEffects)) goto dropeffect_none; if ((*lpdwEffect == DROPEFFECT_COPY || *lpdwEffect == DROPEFFECT_MOVE) && ! lpOleDoc->m_fCanDropCopy) goto dropeffect_none; if (*lpdwEffect == DROPEFFECT_LINK && ! lpOleDoc->m_fCanDropLink) goto dropeffect_none; } *lpdwEffect |= dwScrollEffect; return TRUE; dropeffect_none: *lpdwEffect = DROPEFFECT_NONE; return FALSE; } /* OleDoc_DoDragDrop * ----------------- * Actually perform a drag/drop operation with the current selection in * the source document (lpSrcOleDoc). * * returns the result effect of the drag/drop operation: * DROPEFFECT_NONE, * DROPEFFECT_COPY, * DROPEFFECT_MOVE, or * DROPEFFECT_LINK */ DWORD OleDoc_DoDragDrop (LPOLEDOC lpSrcOleDoc) { LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp; LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp; LPOUTLINEDOC lpSrcOutlineDoc = (LPOUTLINEDOC)lpSrcOleDoc; LPOLEDOC lpDragDoc; LPLINELIST lpSrcLL = (LPLINELIST)&((LPOUTLINEDOC)lpSrcOleDoc)->m_LineList; DWORD dwEffect = 0; LPLINE lplineStart, lplineEnd; LINERANGE linerange; BOOL fPrevEnable1; BOOL fPrevEnable2; HRESULT hrErr; OLEDBG_BEGIN3("OleDoc_DoDragDrop\r\n") /* squirrel away a copy of the current selection to the ClipboardDoc */ lpDragDoc = (LPOLEDOC)OutlineDoc_CreateDataTransferDoc(lpSrcOutlineDoc); if ( ! lpDragDoc) { dwEffect = DROPEFFECT_NONE; goto error; } /* OLE2NOTE: initially the DataTransferDoc is created with a 0 ref ** count. in order to have a stable Doc object during the drag/ ** drop operation, we intially AddRef the Doc ref cnt and later ** Release it. This AddRef is artificial; it is simply ** done to guarantee that a harmless QueryInterface followed by ** a Release does not inadvertantly force our object to destroy ** itself prematurely. */ OleDoc_AddRef(lpDragDoc); //NOTE: we need to keep the LPLINE pointers // rather than the indexes because the // indexes will not be the same after the // drop occurs -- the drop adds new // entries to the list thereby shifting // the whole list. LineList_GetSel( lpSrcLL, (LPLINERANGE)&linerange ); lplineStart = LineList_GetLine ( lpSrcLL, linerange.m_nStartLine ); lplineEnd = LineList_GetLine ( lpSrcLL, linerange.m_nEndLine ); if (! lplineStart || ! lplineEnd) { dwEffect = DROPEFFECT_NONE; goto error; } lpSrcOleDoc->m_fLocalDrop = FALSE; lpSrcOleDoc->m_fLocalDrag = TRUE; /* OLE2NOTE: it is VERY important to DISABLE the Busy/NotResponding ** dialogs BEFORE calling DoDragDrop. The DoDragDrop API starts ** a mouse capture modal loop. if the Busy/NotResponding comes ** up in the middle of this loop (eg. if one of the remoted ** calls like IDropTarget::DragOver call takes a long time, then ** the NotResponding dialog may want to come up), then the mouse ** capture is lost by OLE and things can get messed up. */ OleApp_DisableBusyDialogs(lpOleApp, &fPrevEnable1, &fPrevEnable2); OLEDBG_BEGIN2("DoDragDrop called\r\n") hrErr = DoDragDrop ( (LPDATAOBJECT) &lpDragDoc->m_DataObject, (LPDROPSOURCE) &lpSrcOleDoc->m_DropSource, DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK, (LPDWORD) &dwEffect ); OLEDBG_END2 // re-enable the Busy/NotResponding dialogs OleApp_EnableBusyDialogs(lpOleApp, fPrevEnable1, fPrevEnable2); #if defined( _DEBUG ) if (FAILED(hrErr)) OleDbgOutHResult("DoDragDrop returned", hrErr); #endif lpSrcOleDoc->m_fLocalDrag = FALSE; /* OLE2NOTE: we need to guard the lifetime of our lpSrcOleDoc ** object while we are deleting the lines that were drag ** moved. it is possible that deleting these lines could ** cause the deletion of a PseudoObj. the deletion of a ** PseudoObj will cause the Doc to be unlock ** (CoLockObjectExternal(FALSE,TRUE) called). each PseudoObj ** holds a strong lock on the Doc. It is always best to have ** a memory guard around such critical sections of code. in ** this case, it is particularly important if we were an ** in-place active server and this drag ended up in a drop ** in our outer container. this scenario will lead to a ** crash if we do not hold this memory guard. */ OleDoc_Lock(lpSrcOleDoc, TRUE, 0); /* if after the Drag/Drop modal (mouse capture) loop is finished ** and a drag MOVE operation was performed, then we must delete ** the lines that were dragged. */ if ( GetScode(hrErr) == DRAGDROP_S_DROP && (dwEffect & DROPEFFECT_MOVE) != 0 ) { int i,j,iEnd; LPLINE lplineFocusLine; /* disable repainting and sending data changed notifications ** until after all lines have been deleted. */ OutlineDoc_SetRedraw ( (LPOUTLINEDOC)lpSrcOleDoc, FALSE ); /* if the drop was local to our document, then we must take ** into account that the line indices of the original source ** of the drag could have shifted because the dropped lines ** have been inserted into our document. thus we will ** re-determine the source line indices. */ if (lpSrcOleDoc->m_fLocalDrop) { i = LineList_GetFocusLineIndex ( lpSrcLL ); lplineFocusLine = LineList_GetLine ( lpSrcLL, i ); } for ( i = j = LineList_GetLineIndex(lpSrcLL,lplineStart ) , iEnd = LineList_GetLineIndex(lpSrcLL,lplineEnd ) ; i <= iEnd ; i++ ) OutlineDoc_DeleteLine ((LPOUTLINEDOC)lpSrcOleDoc, j ); LineList_RecalcMaxLineWidthInHimetric(lpSrcLL, 0); if (lpSrcOleDoc->m_fLocalDrop) { i = LineList_GetLineIndex ( lpSrcLL, lplineFocusLine ); LineList_SetFocusLine ( lpSrcLL, (WORD)i ); } OutlineDoc_SetRedraw ( (LPOUTLINEDOC)lpSrcOleDoc, TRUE ); /* if it is a local Drag/Drop move, we need to balance the ** SetRedraw(FALSE) call that was made in the implementation ** of IDropTarget::Drop. */ if (lpSrcOleDoc->m_fLocalDrop) OutlineDoc_SetRedraw ( (LPOUTLINEDOC)lpSrcOleDoc, TRUE ); LineList_ForceRedraw ( lpSrcLL, FALSE ); } OleDoc_Release(lpDragDoc); // rel artificial AddRef above OleDoc_Lock(lpSrcOleDoc, FALSE, FALSE); // unlock artificial lock guard OLEDBG_END3 return dwEffect; error: OLEDBG_END3 return dwEffect; } /************************************************************************* ** OleDoc::IDropSource interface implementation *************************************************************************/ STDMETHODIMP OleDoc_DropSource_QueryInterface( LPDROPSOURCE lpThis, REFIID riid, LPVOID FAR* lplpvObj ) { LPOLEDOC lpOleDoc = ((struct CDocDropSourceImpl FAR*)lpThis)->lpOleDoc; return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj); } STDMETHODIMP_(ULONG) OleDoc_DropSource_AddRef( LPDROPSOURCE lpThis ) { LPOLEDOC lpOleDoc = ((struct CDocDropSourceImpl FAR*)lpThis)->lpOleDoc; OleDbgAddRefMethod(lpThis, "IDropSource"); return OleDoc_AddRef(lpOleDoc); } STDMETHODIMP_(ULONG) OleDoc_DropSource_Release ( LPDROPSOURCE lpThis) { LPOLEDOC lpOleDoc = ((struct CDocDropSourceImpl FAR*)lpThis)->lpOleDoc; OleDbgReleaseMethod(lpThis, "IDropSource"); return OleDoc_Release(lpOleDoc); } STDMETHODIMP OleDoc_DropSource_QueryContinueDrag ( LPDROPSOURCE lpThis, BOOL fEscapePressed, DWORD grfKeyState ){ if (fEscapePressed) return ResultFromScode(DRAGDROP_S_CANCEL); else if (!(grfKeyState & MK_LBUTTON)) return ResultFromScode(DRAGDROP_S_DROP); else return NOERROR; } STDMETHODIMP OleDoc_DropSource_GiveFeedback ( LPDROPSOURCE lpThis, DWORD dwEffect ) { // Tell OLE to use the standard drag/drop feedback cursors return ResultFromScode(DRAGDROP_S_USEDEFAULTCURSORS); #if defined( IF_SPECIAL_DD_CURSORS_NEEDED ) switch (dwEffect) { case DROPEFFECT_NONE: SetCursor ( ((LPOLEAPP)g_lpApp)->m_hcursorDragNone ); break; case DROPEFFECT_COPY: SetCursor ( ((LPOLEAPP)g_lpApp)->m_hcursorDragCopy ); break; case DROPEFFECT_MOVE: SetCursor ( ((LPOLEAPP)g_lpApp)->m_hcursorDragMove ); break; case DROPEFFECT_LINK: SetCursor ( ((LPOLEAPP)g_lpApp)->m_hcursorDragLink ); break; } return NOERROR; #endif } /************************************************************************* ** OleDoc::IDropTarget interface implementation *************************************************************************/ STDMETHODIMP OleDoc_DropTarget_QueryInterface( LPDROPTARGET lpThis, REFIID riid, LPVOID FAR* lplpvObj ) { LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl FAR*)lpThis)->lpOleDoc; return OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj); } STDMETHODIMP_(ULONG) OleDoc_DropTarget_AddRef(LPDROPTARGET lpThis) { LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl FAR*)lpThis)->lpOleDoc; OleDbgAddRefMethod(lpThis, "IDropTarget"); return OleDoc_AddRef(lpOleDoc); } STDMETHODIMP_(ULONG) OleDoc_DropTarget_Release ( LPDROPTARGET lpThis) { LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl FAR*)lpThis)->lpOleDoc; OleDbgReleaseMethod(lpThis, "IDropTarget"); return OleDoc_Release(lpOleDoc); } STDMETHODIMP OleDoc_DropTarget_DragEnter ( LPDROPTARGET lpThis, LPDATAOBJECT lpDataObj, DWORD grfKeyState, POINTL pointl, LPDWORD lpdwEffect ) { LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp; LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl FAR*)lpThis)->lpOleDoc; LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList; BOOL fDragScroll; OLEDBG_BEGIN2("OleDoc_DropTarget_DragEnter\r\n") lpOleDoc->m_fDragLeave = FALSE; lpOleDoc->m_dwTimeEnterScrollArea = 0; lpOleDoc->m_dwLastScrollDir = SCROLLDIR_NULL; /* Determine if the drag source data object offers a data format ** that we can copy and/or link to. */ lpOleDoc->m_fCanDropCopy = OleDoc_QueryPasteFromData( lpOleDoc, lpDataObj, FALSE /* fLink */ ); lpOleDoc->m_fCanDropLink = OleDoc_QueryPasteFromData( lpOleDoc, lpDataObj, TRUE /* fLink */ ); fDragScroll = OleDoc_DoDragScroll ( lpOleDoc, pointl ); if (OleDoc_QueryDrop(lpOleDoc,grfKeyState,pointl,fDragScroll,lpdwEffect)) LineList_SetDragOverLineFromPointl( lpLL, pointl ); OLEDBG_END2 return NOERROR; } STDMETHODIMP OleDoc_DropTarget_DragOver ( LPDROPTARGET lpThis, DWORD grfKeyState, POINTL pointl, LPDWORD lpdwEffect ) { LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp; LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl FAR*)lpThis)->lpOleDoc; LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList; BOOL fDragScroll; fDragScroll = OleDoc_DoDragScroll ( lpOleDoc, pointl ); if (OleDoc_QueryDrop(lpOleDoc,grfKeyState, pointl,fDragScroll,lpdwEffect)) LineList_SetDragOverLineFromPointl( lpLL, pointl ); else LineList_RestoreDragFeedback( lpLL ); return NOERROR; } STDMETHODIMP OleDoc_DropTarget_DragLeave ( LPDROPTARGET lpThis) { LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl FAR*)lpThis)->lpOleDoc; LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList; OLEDBG_BEGIN2("OleDoc_DropTarget_DragLeave\r\n") lpOleDoc->m_fDragLeave = TRUE; LineList_RestoreDragFeedback( lpLL ); OLEDBG_END2 return NOERROR; } STDMETHODIMP OleDoc_DropTarget_Drop ( LPDROPTARGET lpThis, LPDATAOBJECT lpDataObj, DWORD grfKeyState, POINTL pointl, LPDWORD lpdwEffect ) { LPOLEDOC lpOleDoc = ((struct CDocDropTargetImpl FAR*)lpThis)->lpOleDoc; LPLINELIST lpLL = (LPLINELIST)&((LPOUTLINEDOC)lpOleDoc)->m_LineList; OLEDBG_BEGIN2("OleDoc_DropTarget_Drop\r\n") lpOleDoc->m_fDragLeave = TRUE; lpOleDoc->m_fLocalDrop = TRUE; LineList_RestoreDragFeedback( lpLL ); SetFocus( LineList_GetWindow( lpLL) ); if (OleDoc_QueryDrop(lpOleDoc, grfKeyState, pointl, FALSE, lpdwEffect)) { BOOL fLink = (*lpdwEffect == DROPEFFECT_LINK); int iFocusLine = LineList_GetFocusLineIndex( lpLL ); BOOL fStatus; OutlineDoc_SetRedraw ( (LPOUTLINEDOC)lpOleDoc, FALSE ); LineList_SetFocusLineFromPointl ( lpLL, pointl ); fStatus = OleDoc_PasteFromData( lpOleDoc, lpDataObj, lpOleDoc->m_fLocalDrag, /* data source is local to app */ fLink ); // if drop was unsuccessfull, restore the original focus line if (! fStatus) LineList_SetFocusLine( lpLL, (WORD)iFocusLine ); #if defined( INPLACE_CNTR ) { LPCONTAINERDOC lpContainerDoc = (LPCONTAINERDOC)lpOleDoc; /* OLE2NOTE: if there is currently a UIActive OLE object, ** then we must tell it to UIDeactivate after ** the drop has completed. */ if (lpContainerDoc->m_lpLastUIActiveLine) { ContainerLine_UIDeactivate( lpContainerDoc->m_lpLastUIActiveLine); } } #endif #if defined( INPLACE_SVR ) { /* OLE2NOTE: if the drop was into a in-place visible ** (in-place active but NOT UIActive object), then we ** want to UIActivate the object after the drop is ** complete. */ ServerDoc_UIActivate((LPSERVERDOC) lpOleDoc); } #endif /* if it is a local Drag/Drop move, don't enable redraw. ** after the source is done deleting the moved lines, it ** will re-enable redraw */ if (! (lpOleDoc->m_fLocalDrag && (*lpdwEffect & DROPEFFECT_MOVE) != 0 )) OutlineDoc_SetRedraw ( (LPOUTLINEDOC)lpOleDoc, TRUE ); } OLEDBG_END2 return NOERROR; } #endif // USE_DRAGDROP