1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: target.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_dtrans.hxx"
33 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
34 #include <com/sun/star/datatransfer/XTransferable.hpp>
35 #include <rtl/unload.h>
39 #include "idroptarget.hxx"
40 #include "globals.hxx"
41 #include "targetdropcontext.hxx"
42 #include "targetdragcontext.hxx"
43 #include <rtl/ustring.h>
47 using namespace com::sun::star::datatransfer
;
48 using namespace com::sun::star::datatransfer::dnd
;
49 using namespace com::sun::star::datatransfer::dnd::DNDConstants
;
51 #define WM_REGISTERDRAGDROP WM_USER + 1
52 #define WM_REVOKEDRAGDROP WM_USER + 2
54 extern Reference
< XTransferable
> g_XTransferable
;
58 extern rtl_StandardModuleCount g_moduleCount
;
59 DWORD WINAPI
DndTargetOleSTAFunc(LPVOID pParams
);
61 DropTarget::DropTarget( const Reference
<XMultiServiceFactory
>& sf
):
63 m_serviceFactory( sf
),
64 WeakComponentImplHelper3
<XInitialization
,XDropTarget
, XServiceInfo
>(m_mutex
),
66 m_nDefaultActions(ACTION_COPY
|ACTION_MOVE
|ACTION_LINK
|ACTION_DEFAULT
),
67 m_nCurrentDropAction( ACTION_NONE
),
77 g_moduleCount
.modCnt
.acquire( &g_moduleCount
.modCnt
);
81 DropTarget::~DropTarget()
83 g_moduleCount
.modCnt
.release( &g_moduleCount
.modCnt
);
86 // called from WeakComponentImplHelperX::dispose
87 // WeakComponentImplHelper calls disposing before it destroys
89 // NOTE: RevokeDragDrop decrements the ref count on the IDropTarget
90 // interface. (m_pDropTarget)
91 // If the HWND is invalid then it doesn't decrement and
92 // the IDropTarget object will live on. MEMORY LEAK
93 void SAL_CALL
DropTarget::disposing()
98 // Call RevokeDragDrop and wait for the OLE thread to die;
99 PostThreadMessage( m_threadIdTarget
, WM_REVOKEDRAGDROP
, (WPARAM
)this, 0);
100 WaitForSingleObject( m_hOleThread
, INFINITE
);
101 CloseHandle( m_hOleThread
);
102 //OSL_ENSURE( SUCCEEDED( hr), "HWND not valid!" );
106 hr
= RevokeDragDrop( m_hWnd
);
111 CoLockObjectExternal( m_pDropTarget
, FALSE
, TRUE
);
112 m_pDropTarget
->Release();
117 if( m_oleThreadId
== CoGetCurrentProcess() )
123 void SAL_CALL
DropTarget::initialize( const Sequence
< Any
>& aArguments
)
124 throw(Exception
, RuntimeException
)
126 // The window must be registered for Dnd by RegisterDragDrop. We must ensure
127 // that RegisterDragDrop is called from an STA ( OleInitialize) thread.
128 // As long as the window is registered we need to receive OLE messages in
129 // an OLE thread. That is to say, if DropTarget::initialize was called from an
130 // MTA thread then we create an OLE thread in which the window is registered.
131 // The thread will stay alive until aver RevokeDragDrop has been called.
133 // Additionally even if RegisterDragDrop is called from an STA thread we have
134 // to ensure that it is called from the same thread that created the Window
135 // otherwise meesages sent during DND won't reach the windows message queue.
136 // Calling AttachThreadInput first would resolve this problem but would block
137 // the message queue of the calling thread. So if the current thread
138 // (even if it's an STA thread) and the thread that created the window are not
139 // identical we need to create a new thread as we do when the calling thread is
142 if( aArguments
.getLength() > 0)
144 // Get the window handle from aArgument. It is needed for RegisterDragDrop.
145 m_hWnd
= *(HWND
*)aArguments
[0].getValue();
146 OSL_ASSERT( IsWindow( m_hWnd
) );
148 // Obtain the id of the thread that created the window
149 m_threadIdWindow
= GetWindowThreadProcessId( m_hWnd
, NULL
);
151 HRESULT hr
= OleInitialize( NULL
);
153 // Current thread is MTA or Current thread and Window thread are not identical
154 if( hr
== RPC_E_CHANGED_MODE
|| GetCurrentThreadId() != m_threadIdWindow
)
156 OSL_ENSURE( ! m_threadIdTarget
,"initialize was called twice");
157 // create the IDropTargetImplementation
158 m_pDropTarget
= new IDropTargetImpl( *static_cast<DropTarget
*>( this) );
159 m_pDropTarget
->AddRef();
162 // Obtain the id of the thread that created the window
163 m_threadIdWindow
= GetWindowThreadProcessId( m_hWnd
, NULL
);
164 // The event is set by the thread that we will create momentarily.
165 // It indicates that the thread is ready to receive messages.
166 HANDLE m_evtThreadReady
= CreateEvent( NULL
, FALSE
, FALSE
, NULL
);
168 m_hOleThread
= CreateThread( NULL
, 0, (LPTHREAD_START_ROUTINE
)DndTargetOleSTAFunc
,
169 &m_evtThreadReady
, 0, &m_threadIdTarget
);
170 WaitForSingleObject( m_evtThreadReady
, INFINITE
);
171 CloseHandle( m_evtThreadReady
);
172 PostThreadMessage( m_threadIdTarget
, WM_REGISTERDRAGDROP
, (WPARAM
)static_cast<DropTarget
*>(this), 0);
174 else if( hr
== S_OK
|| hr
== S_FALSE
)
176 // current thread is STA
177 // If OleInitialize has been called by the caller then we must not call
181 // caller did not call OleInitialize, so we call OleUninitialize
182 // remember the thread that will call OleUninitialize
183 m_oleThreadId
= CoGetCurrentProcess(); // get a unique thread id
186 // Get the window handle from aArgument. It is needed for RegisterDragDrop.
187 // create the IDropTargetImplementation
188 m_pDropTarget
= new IDropTargetImpl( *static_cast<DropTarget
*>( this) );
189 m_pDropTarget
->AddRef();
190 // CoLockObjectExternal is prescribed by the protocol. It bumps up the ref count
191 if( SUCCEEDED( CoLockObjectExternal( m_pDropTarget
, TRUE
, FALSE
)))
193 if( FAILED( RegisterDragDrop( m_hWnd
, m_pDropTarget
) ) )
195 // do clean up if drag and drop is not possible
196 CoLockObjectExternal( m_pDropTarget
, FALSE
, FALSE
);
197 m_pDropTarget
->Release();
208 // This function is called as extra thread from DragSource::startDrag.
209 // The function carries out a drag and drop operation by calling
210 // DoDragDrop. The thread also notifies all XSourceListener.
211 DWORD WINAPI
DndTargetOleSTAFunc(LPVOID pParams
)
213 HRESULT hr
= OleInitialize( NULL
);
217 // force the creation of a message queue
218 PeekMessage( &msg
, (HWND
)NULL
, 0, 0, PM_NOREMOVE
);
219 // Signal the creator ( DropTarget::initialize) that the thread is
220 // ready to receive messages.
221 SetEvent( *(HANDLE
*) pParams
);
222 // Thread id is needed for attaching this message queue to the one of the
223 // thread where the window was created.
224 DWORD threadId
= GetCurrentThreadId();
225 // We force the creation of a thread message queue. This is necessary
226 // for a later call to AttachThreadInput
227 while( GetMessage(&msg
, (HWND
)NULL
, 0, 0) )
229 if( msg
.message
== WM_REGISTERDRAGDROP
)
231 DropTarget
*pTarget
= (DropTarget
*)msg
.wParam
;
232 // This thread is attached to the thread that created the window. Hence
233 // this thread also receives all mouse and keyboard messages which are
235 AttachThreadInput( threadId
, pTarget
->m_threadIdWindow
, TRUE
);
237 if( SUCCEEDED( CoLockObjectExternal(pTarget
-> m_pDropTarget
, TRUE
, FALSE
)))
239 if( FAILED( RegisterDragDrop( pTarget
-> m_hWnd
, pTarget
-> m_pDropTarget
) ) )
241 // do clean up if drag and drop is not possible
242 CoLockObjectExternal( pTarget
->m_pDropTarget
, FALSE
, FALSE
);
243 pTarget
->m_pDropTarget
->Release();
244 pTarget
->m_hWnd
= NULL
;
248 else if( msg
.message
== WM_REVOKEDRAGDROP
)
250 DropTarget
*pTarget
= (DropTarget
*)msg
.wParam
;
251 RevokeDragDrop( pTarget
-> m_hWnd
);
252 // Detach this thread from the window thread
253 AttachThreadInput( threadId
, pTarget
->m_threadIdWindow
, FALSE
);
257 TranslateMessage( &msg
);
258 DispatchMessage( &msg
);
269 OUString SAL_CALL
DropTarget::getImplementationName( ) throw (RuntimeException
)
271 return OUString(RTL_CONSTASCII_USTRINGPARAM(DNDTARGET_IMPL_NAME
));;
274 sal_Bool SAL_CALL
DropTarget::supportsService( const OUString
& ServiceName
) throw (RuntimeException
)
276 if( ServiceName
.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(DNDTARGET_SERVICE_NAME
))))
281 Sequence
< OUString
> SAL_CALL
DropTarget::getSupportedServiceNames( ) throw (RuntimeException
)
283 OUString names
[1]= {OUString(RTL_CONSTASCII_USTRINGPARAM(DNDTARGET_SERVICE_NAME
))};
284 return Sequence
<OUString
>(names
, 1);
288 // XDropTarget ----------------------------------------------------------------
289 void SAL_CALL
DropTarget::addDropTargetListener( const Reference
< XDropTargetListener
>& dtl
)
290 throw(RuntimeException
)
292 rBHelper
.addListener( ::getCppuType( &dtl
), dtl
);
295 void SAL_CALL
DropTarget::removeDropTargetListener( const Reference
< XDropTargetListener
>& dtl
)
296 throw(RuntimeException
)
298 rBHelper
.removeListener( ::getCppuType( &dtl
), dtl
);
301 sal_Bool SAL_CALL
DropTarget::isActive( ) throw(RuntimeException
)
303 return m_bActive
; //m_bDropTargetRegistered;
307 void SAL_CALL
DropTarget::setActive( sal_Bool _b
) throw(RuntimeException
)
309 MutexGuard
g(m_mutex
);
314 sal_Int8 SAL_CALL
DropTarget::getDefaultActions( ) throw(RuntimeException
)
316 return m_nDefaultActions
;
319 void SAL_CALL
DropTarget::setDefaultActions( sal_Int8 actions
) throw(RuntimeException
)
321 OSL_ENSURE( actions
< 8, "No valid default actions");
322 m_nDefaultActions
= actions
;
326 HRESULT
DropTarget::DragEnter( IDataObject
*pDataObj
,
331 #if defined DBG_CONSOLE_OUT
332 printf("\nDropTarget::DragEnter state: %x effect %d", grfKeyState
, *pdwEffect
);
336 // Intersection of pdwEffect and the allowed actions ( setDefaultActions)
337 m_nCurrentDropAction
= getFilteredActions( grfKeyState
, *pdwEffect
);
338 // m_nLastDropAction has to be set by a listener. If no listener calls
339 //XDropTargetDragContext::acceptDrag and specifies an action then pdwEffect
340 // will be DROPEFFECT_NONE throughout
341 m_nLastDropAction
= ACTION_DEFAULT
| ACTION_MOVE
;
343 m_currentDragContext
= static_cast<XDropTargetDragContext
*>( new TargetDragContext(
344 static_cast<DropTarget
*>(this) ) );
349 if ( g_XTransferable
.is( ) )
350 m_currentData
= g_XTransferable
;
353 // Convert the IDataObject to a XTransferable
354 m_currentData
= m_aDataConverter
.createTransferableFromDataObj(
355 m_serviceFactory
, IDataObjectPtr(pDataObj
));
360 if( m_nCurrentDropAction
!= ACTION_NONE
)
362 DropTargetDragEnterEvent e
;
363 e
.SupportedDataFlavors
= m_currentData
->getTransferDataFlavors();
364 e
.DropAction
= m_nCurrentDropAction
;
365 e
.Source
= Reference
<XInterface
>( static_cast<XDropTarget
*>(this),UNO_QUERY
);
366 e
.Context
= m_currentDragContext
;
367 POINT point
={ pt
.x
, pt
.y
};
368 ScreenToClient( m_hWnd
, &point
);
369 e
.LocationX
= point
.x
;
370 e
.LocationY
= point
.y
;
371 e
.SourceActions
= dndOleDropEffectsToActions( *pdwEffect
);
374 // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set
375 // by the listener (m_nCurrentDropAction) is allowed by the source. Only a allowed action is set
376 // in pdwEffect. The listener notification is asynchron, that is we cannot expext that the listener
377 // has already reacted to the notification.
378 // If there is more then one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed
379 // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed.
380 // On drop the target should present the user a dialog from which the user may change the action.
381 sal_Int8 allowedActions
= dndOleDropEffectsToActions( *pdwEffect
);
382 *pdwEffect
= dndActionsToSingleDropEffect( m_nLastDropAction
& allowedActions
);
386 *pdwEffect
= DROPEFFECT_NONE
;
392 HRESULT
DropTarget::DragOver( DWORD grfKeyState
,
398 m_nCurrentDropAction
= getFilteredActions( grfKeyState
, *pdwEffect
);
400 if( m_nCurrentDropAction
)
402 DropTargetDragEvent e
;
403 e
.DropAction
= m_nCurrentDropAction
;
404 e
.Source
= Reference
<XInterface
>(static_cast<XDropTarget
*>(this),UNO_QUERY
);
405 e
.Context
= m_currentDragContext
;
406 POINT point
={ pt
.x
, pt
.y
};
407 ScreenToClient( m_hWnd
, &point
);
408 e
.LocationX
= point
.x
;
409 e
.LocationY
= point
.y
;
410 e
.SourceActions
= dndOleDropEffectsToActions( *pdwEffect
);
412 // if grfKeyState has changed since the last DragOver then fire events.
413 // A listener might change m_nCurrentDropAction by calling the
414 // XDropTargetDragContext::acceptDrag function. But this is not important
415 // because in the afterwards fired dragOver event the action reflects
416 // grgKeyState again.
417 if( m_nLastDropAction
!= m_nCurrentDropAction
)
418 fire_dropActionChanged( e
);
420 // The Event contains a XDropTargetDragContext implementation.
422 // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set
423 // by the listener (m_nCurrentDropAction) is allowed by the source. Only a allowed action is set
424 // in pdwEffect. The listener notification is asynchron, that is we cannot expext that the listener
425 // has already reacted to the notification.
426 // If there is more then one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed
427 // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed.
428 // On drop the target should present the user a dialog from which the user may change the action.
429 sal_Int8 allowedActions
= dndOleDropEffectsToActions( *pdwEffect
);
430 // set the last action to the current if listener has not changed the value yet
431 *pdwEffect
= dndActionsToSingleDropEffect( m_nLastDropAction
& allowedActions
);
435 *pdwEffect
= DROPEFFECT_NONE
;
438 #if defined DBG_CONSOLE_OUT
439 printf("\nDropTarget::DragOver %d", *pdwEffect
);
444 HRESULT
DropTarget::DragLeave( void)
446 #if defined DBG_CONSOLE_OUT
447 printf("\nDropTarget::DragLeave");
453 m_currentDragContext
= 0;
454 m_currentDropContext
= 0;
455 m_nLastDropAction
= 0;
457 if( m_nDefaultActions
!= ACTION_NONE
)
460 e
.Source
= static_cast<XDropTarget
*>(this);
468 HRESULT
DropTarget::Drop( IDataObject
* /*pDataObj*/,
473 #if defined DBG_CONSOLE_OUT
474 printf("\nDropTarget::Drop");
479 m_bDropComplete
= sal_False
;
481 m_nCurrentDropAction
= getFilteredActions( grfKeyState
, *pdwEffect
);
482 m_currentDropContext
= static_cast<XDropTargetDropContext
*>( new TargetDropContext( static_cast<DropTarget
*>(this )) );
483 if( m_nCurrentDropAction
)
485 DropTargetDropEvent e
;
486 e
.DropAction
= m_nCurrentDropAction
;
487 e
.Source
= Reference
<XInterface
>( static_cast<XDropTarget
*>(this), UNO_QUERY
);
488 e
.Context
= m_currentDropContext
;
489 POINT point
={ pt
.x
, pt
.y
};
490 ScreenToClient( m_hWnd
, &point
);
491 e
.LocationX
= point
.x
;
492 e
.LocationY
= point
.y
;
493 e
.SourceActions
= dndOleDropEffectsToActions( *pdwEffect
);
494 e
.Transferable
= m_currentData
;
497 //if fire_drop returns than a listener might have modified m_nCurrentDropAction
498 if( m_bDropComplete
== sal_True
)
500 sal_Int8 allowedActions
= dndOleDropEffectsToActions( *pdwEffect
);
501 *pdwEffect
= dndActionsToSingleDropEffect( m_nCurrentDropAction
& allowedActions
);
504 *pdwEffect
= DROPEFFECT_NONE
;
507 *pdwEffect
= DROPEFFECT_NONE
;
510 m_currentDragContext
= 0;
511 m_currentDropContext
= 0;
512 m_nLastDropAction
= 0;
519 void DropTarget::fire_drop( const DropTargetDropEvent
& dte
)
521 OInterfaceContainerHelper
* pContainer
= rBHelper
.getContainer( getCppuType( (Reference
<XDropTargetListener
>* )0 ) );
524 OInterfaceIteratorHelper
iter( *pContainer
);
525 while( iter
.hasMoreElements())
527 Reference
<XDropTargetListener
> listener( static_cast<XDropTargetListener
*>( iter
.next()));
528 listener
->drop( dte
);
533 void DropTarget::fire_dragEnter( const DropTargetDragEnterEvent
& e
)
535 OInterfaceContainerHelper
* pContainer
= rBHelper
.getContainer( getCppuType( (Reference
<XDropTargetListener
>* )0 ) );
538 OInterfaceIteratorHelper
iter( *pContainer
);
539 while( iter
.hasMoreElements())
541 Reference
<XDropTargetListener
> listener( static_cast<XDropTargetListener
*>( iter
.next()));
542 listener
->dragEnter( e
);
547 void DropTarget::fire_dragExit( const DropTargetEvent
& dte
)
549 OInterfaceContainerHelper
* pContainer
= rBHelper
.getContainer( getCppuType( (Reference
<XDropTargetListener
>* )0 ) );
553 OInterfaceIteratorHelper
iter( *pContainer
);
554 while( iter
.hasMoreElements())
556 Reference
<XDropTargetListener
> listener( static_cast<XDropTargetListener
*>( iter
.next()));
557 listener
->dragExit( dte
);
562 void DropTarget::fire_dragOver( const DropTargetDragEvent
& dtde
)
564 OInterfaceContainerHelper
* pContainer
= rBHelper
.getContainer( getCppuType( (Reference
<XDropTargetListener
>* )0 ) );
567 OInterfaceIteratorHelper
iter( *pContainer
);
568 while( iter
.hasMoreElements())
570 Reference
<XDropTargetListener
> listener( static_cast<XDropTargetListener
*>( iter
.next()));
571 listener
->dragOver( dtde
);
576 void DropTarget::fire_dropActionChanged( const DropTargetDragEvent
& dtde
)
578 OInterfaceContainerHelper
* pContainer
= rBHelper
.getContainer( getCppuType( (Reference
<XDropTargetListener
>* )0 ) );
581 OInterfaceIteratorHelper
iter( *pContainer
);
582 while( iter
.hasMoreElements())
584 Reference
<XDropTargetListener
> listener( static_cast<XDropTargetListener
*>( iter
.next()));
585 listener
->dropActionChanged( dtde
);
590 // Non - interface functions ============================================================
591 // DropTarget fires events to XDropTargetListeners. The event object contains an
592 // XDropTargetDropContext implementaion. When the listener calls on that interface
593 // then the calls are delegated from DropContext (XDropTargetDropContext) to these
595 // Only one listener which visible area is affected is allowed to call on
596 // XDropTargetDropContext
597 // Returning sal_False would cause the XDropTargetDropContext or ..DragContext implementation
598 // to throw an InvalidDNDOperationException, meaning that a Drag is not currently performed.
599 // return sal_False results in throwing a InvalidDNDOperationException in the caller.
601 void DropTarget::_acceptDrop(sal_Int8 dropOperation
, const Reference
<XDropTargetDropContext
>& context
)
603 if( context
== m_currentDropContext
)
605 m_nCurrentDropAction
= dropOperation
;
609 void DropTarget::_rejectDrop( const Reference
<XDropTargetDropContext
>& context
)
611 if( context
== m_currentDropContext
)
613 m_nCurrentDropAction
= ACTION_NONE
;
617 void DropTarget::_dropComplete(sal_Bool success
, const Reference
<XDropTargetDropContext
>& context
)
619 if(context
== m_currentDropContext
)
621 m_bDropComplete
= success
;
624 // --------------------------------------------------------------------------------------
625 // DropTarget fires events to XDropTargetListeners. The event object can contains an
626 // XDropTargetDragContext implementaion. When the listener calls on that interface
627 // then the calls are delegated from DragContext (XDropTargetDragContext) to these
629 // Only one listener which visible area is affected is allowed to call on
630 // XDropTargetDragContext
631 void DropTarget::_acceptDrag( sal_Int8 dragOperation
, const Reference
<XDropTargetDragContext
>& context
)
633 if( context
== m_currentDragContext
)
635 m_nLastDropAction
= dragOperation
;
639 void DropTarget::_rejectDrag( const Reference
<XDropTargetDragContext
>& context
)
641 if(context
== m_currentDragContext
)
643 m_nLastDropAction
= ACTION_NONE
;
648 //--------------------------------------------------------------------------------------
651 // This function determines the action dependend on the pressed
652 // key modifiers ( CTRL, SHIFT, ALT, Right Mouse Button). The result
653 // is then checked against the allowed actions which can be set through
654 // XDropTarget::setDefaultActions. Only those values which are also
655 // default actions are returned. If setDefaultActions has not been called
656 // beforehand the the default actions comprise all possible actions.
657 // params: grfKeyState - the modifier keys and mouse buttons currently pressed
658 inline sal_Int8
DropTarget::getFilteredActions( DWORD grfKeyState
, DWORD dwEffect
)
660 sal_Int8 actions
= dndOleKeysToAction( grfKeyState
, dndOleDropEffectsToActions( dwEffect
));
661 return actions
& m_nDefaultActions
;