bump product version to 4.1.6.2
[LibreOffice.git] / dtrans / source / win32 / dnd / target.cxx
blobe127b3d5e7fdae3a8f0b1d13d813e51f6a6d3c5d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
21 #include <com/sun/star/datatransfer/XTransferable.hpp>
23 #include <stdio.h>
24 #include "target.hxx"
25 #include "idroptarget.hxx"
26 #include "globals.hxx"
27 #include "targetdropcontext.hxx"
28 #include "targetdragcontext.hxx"
29 #include <rtl/ustring.h>
30 using namespace cppu;
31 using namespace osl;
32 using namespace com::sun::star::datatransfer;
33 using namespace com::sun::star::datatransfer::dnd;
34 using namespace com::sun::star::datatransfer::dnd::DNDConstants;
37 #define WM_REGISTERDRAGDROP WM_USER + 1
38 #define WM_REVOKEDRAGDROP WM_USER + 2
39 extern Reference< XTransferable > g_XTransferable;
41 DWORD WINAPI DndTargetOleSTAFunc(LPVOID pParams);
43 DropTarget::DropTarget( const Reference<XComponentContext>& rxContext):
44 WeakComponentImplHelper3<XInitialization,XDropTarget, XServiceInfo>(m_mutex),
45 m_hWnd( NULL),
46 m_threadIdWindow(0),
47 m_threadIdTarget(0),
48 m_hOleThread(0),
49 m_oleThreadId( 0),
50 m_pDropTarget( NULL),
51 m_xContext( rxContext ),
52 m_bActive(sal_True),
53 m_nDefaultActions(ACTION_COPY|ACTION_MOVE|ACTION_LINK|ACTION_DEFAULT),
54 m_nCurrentDropAction( ACTION_NONE),
55 m_nLastDropAction(0),
56 m_bDropComplete(false)
60 DropTarget::~DropTarget()
63 // called from WeakComponentImplHelperX::dispose
64 // WeakComponentImplHelper calls disposing before it destroys
65 // itself.
66 // NOTE: RevokeDragDrop decrements the ref count on the IDropTarget
67 // interface. (m_pDropTarget)
68 // If the HWND is invalid then it doesn't decrement and
69 // the IDropTarget object will live on. MEMORY LEAK
70 void SAL_CALL DropTarget::disposing()
72 if( m_threadIdTarget)
74 // Call RevokeDragDrop and wait for the OLE thread to die;
75 PostThreadMessage( m_threadIdTarget, WM_REVOKEDRAGDROP, (WPARAM)this, 0);
76 WaitForSingleObject( m_hOleThread, INFINITE);
77 CloseHandle( m_hOleThread);
78 //OSL_ENSURE( SUCCEEDED( hr), "HWND not valid!" );
80 else
82 RevokeDragDrop( m_hWnd);
83 m_hWnd= 0;
85 if( m_pDropTarget)
87 CoLockObjectExternal( m_pDropTarget, FALSE, TRUE);
88 m_pDropTarget->Release();
91 if( m_oleThreadId)
93 if( m_oleThreadId == CoGetCurrentProcess() )
94 OleUninitialize();
99 void SAL_CALL DropTarget::initialize( const Sequence< Any >& aArguments )
100 throw(Exception, RuntimeException)
102 // The window must be registered for Dnd by RegisterDragDrop. We must ensure
103 // that RegisterDragDrop is called from an STA ( OleInitialize) thread.
104 // As long as the window is registered we need to receive OLE messages in
105 // an OLE thread. That is to say, if DropTarget::initialize was called from an
106 // MTA thread then we create an OLE thread in which the window is registered.
107 // The thread will stay alive until aver RevokeDragDrop has been called.
109 // Additionally even if RegisterDragDrop is called from an STA thread we have
110 // to ensure that it is called from the same thread that created the Window
111 // otherwise meesages sent during DND won't reach the windows message queue.
112 // Calling AttachThreadInput first would resolve this problem but would block
113 // the message queue of the calling thread. So if the current thread
114 // (even if it's an STA thread) and the thread that created the window are not
115 // identical we need to create a new thread as we do when the calling thread is
116 // an MTA thread.
118 if( aArguments.getLength() > 0)
120 // Get the window handle from aArgument. It is needed for RegisterDragDrop.
121 m_hWnd= *(HWND*)aArguments[0].getValue();
122 OSL_ASSERT( IsWindow( m_hWnd) );
124 // Obtain the id of the thread that created the window
125 m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, NULL);
127 HRESULT hr= OleInitialize( NULL);
129 // Current thread is MTA or Current thread and Window thread are not identical
130 if( hr == RPC_E_CHANGED_MODE || GetCurrentThreadId() != m_threadIdWindow )
132 OSL_ENSURE( ! m_threadIdTarget,"initialize was called twice");
133 // create the IDropTargetImplementation
134 m_pDropTarget= new IDropTargetImpl( *static_cast<DropTarget*>( this) );
135 m_pDropTarget->AddRef();
138 // Obtain the id of the thread that created the window
139 m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, NULL);
140 // The event is set by the thread that we will create momentarily.
141 // It indicates that the thread is ready to receive messages.
142 HANDLE m_evtThreadReady= CreateEvent( NULL, FALSE, FALSE, NULL);
144 m_hOleThread= CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)DndTargetOleSTAFunc,
145 &m_evtThreadReady, 0, &m_threadIdTarget);
146 WaitForSingleObject( m_evtThreadReady, INFINITE);
147 CloseHandle( m_evtThreadReady);
148 PostThreadMessage( m_threadIdTarget, WM_REGISTERDRAGDROP, (WPARAM)static_cast<DropTarget*>(this), 0);
150 else if( hr == S_OK || hr == S_FALSE)
152 // current thread is STA
153 // If OleInitialize has been called by the caller then we must not call
154 // OleUninitialize
155 if( hr == S_OK)
157 // caller did not call OleInitialize, so we call OleUninitialize
158 // remember the thread that will call OleUninitialize
159 m_oleThreadId= CoGetCurrentProcess(); // get a unique thread id
162 // Get the window handle from aArgument. It is needed for RegisterDragDrop.
163 // create the IDropTargetImplementation
164 m_pDropTarget= new IDropTargetImpl( *static_cast<DropTarget*>( this) );
165 m_pDropTarget->AddRef();
166 // CoLockObjectExternal is prescribed by the protocol. It bumps up the ref count
167 if( SUCCEEDED( CoLockObjectExternal( m_pDropTarget, TRUE, FALSE)))
169 if( FAILED( RegisterDragDrop( m_hWnd, m_pDropTarget) ) )
171 // do clean up if drag and drop is not possible
172 CoLockObjectExternal( m_pDropTarget, FALSE, FALSE);
173 m_pDropTarget->Release();
174 m_hWnd= NULL;
178 else
179 throw Exception();
184 // This function is called as extra thread from DragSource::startDrag.
185 // The function carries out a drag and drop operation by calling
186 // DoDragDrop. The thread also notifies all XSourceListener.
187 DWORD WINAPI DndTargetOleSTAFunc(LPVOID pParams)
189 HRESULT hr= OleInitialize( NULL);
190 if( SUCCEEDED( hr) )
192 MSG msg;
193 // force the creation of a message queue
194 PeekMessage( &msg, (HWND)NULL, 0, 0, PM_NOREMOVE);
195 // Signal the creator ( DropTarget::initialize) that the thread is
196 // ready to receive messages.
197 SetEvent( *(HANDLE*) pParams);
198 // Thread id is needed for attaching this message queue to the one of the
199 // thread where the window was created.
200 DWORD threadId= GetCurrentThreadId();
201 // We force the creation of a thread message queue. This is necessary
202 // for a later call to AttachThreadInput
203 while( GetMessage(&msg, (HWND)NULL, 0, 0) )
205 if( msg.message == WM_REGISTERDRAGDROP)
207 DropTarget *pTarget= (DropTarget*)msg.wParam;
208 // This thread is attached to the thread that created the window. Hence
209 // this thread also receives all mouse and keyboard messages which are
210 // needed
211 AttachThreadInput( threadId , pTarget->m_threadIdWindow, TRUE );
213 if( SUCCEEDED( CoLockObjectExternal(pTarget-> m_pDropTarget, TRUE, FALSE)))
215 if( FAILED( RegisterDragDrop( pTarget-> m_hWnd, pTarget-> m_pDropTarget) ) )
217 // do clean up if drag and drop is not possible
218 CoLockObjectExternal( pTarget->m_pDropTarget, FALSE, FALSE);
219 pTarget->m_pDropTarget->Release();
220 pTarget->m_hWnd= NULL;
224 else if( msg.message == WM_REVOKEDRAGDROP)
226 DropTarget *pTarget= (DropTarget*)msg.wParam;
227 RevokeDragDrop( pTarget-> m_hWnd);
228 // Detach this thread from the window thread
229 AttachThreadInput( threadId, pTarget->m_threadIdWindow, FALSE);
230 pTarget->m_hWnd= 0;
231 break;
233 TranslateMessage( &msg);
234 DispatchMessage( &msg);
236 OleUninitialize();
238 return 0;
244 // XServiceInfo
245 OUString SAL_CALL DropTarget::getImplementationName( ) throw (RuntimeException)
247 return OUString(RTL_CONSTASCII_USTRINGPARAM(DNDTARGET_IMPL_NAME));
249 // XServiceInfo
250 sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName ) throw (RuntimeException)
252 if( ServiceName == DNDTARGET_SERVICE_NAME )
253 return sal_True;
254 return sal_False;
257 Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames( ) throw (RuntimeException)
259 OUString names[1]= {OUString(RTL_CONSTASCII_USTRINGPARAM(DNDTARGET_SERVICE_NAME))};
260 return Sequence<OUString>(names, 1);
264 // XDropTarget ----------------------------------------------------------------
265 void SAL_CALL DropTarget::addDropTargetListener( const Reference< XDropTargetListener >& dtl )
266 throw(RuntimeException)
268 rBHelper.addListener( ::getCppuType( &dtl ), dtl );
271 void SAL_CALL DropTarget::removeDropTargetListener( const Reference< XDropTargetListener >& dtl )
272 throw(RuntimeException)
274 rBHelper.removeListener( ::getCppuType( &dtl ), dtl );
277 sal_Bool SAL_CALL DropTarget::isActive( ) throw(RuntimeException)
279 return m_bActive; //m_bDropTargetRegistered;
283 void SAL_CALL DropTarget::setActive( sal_Bool _b ) throw(RuntimeException)
285 MutexGuard g(m_mutex);
286 m_bActive= _b;
290 sal_Int8 SAL_CALL DropTarget::getDefaultActions( ) throw(RuntimeException)
292 return m_nDefaultActions;
295 void SAL_CALL DropTarget::setDefaultActions( sal_Int8 actions ) throw(RuntimeException)
297 OSL_ENSURE( actions < 8, "No valid default actions");
298 m_nDefaultActions= actions;
302 HRESULT DropTarget::DragEnter( IDataObject *pDataObj,
303 DWORD grfKeyState,
304 POINTL pt,
305 DWORD *pdwEffect)
307 #if defined DBG_CONSOLE_OUT
308 printf("\nDropTarget::DragEnter state: %x effect %d", grfKeyState, *pdwEffect);
309 #endif
310 if( m_bActive )
312 // Intersection of pdwEffect and the allowed actions ( setDefaultActions)
313 m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
314 // m_nLastDropAction has to be set by a listener. If no listener calls
315 //XDropTargetDragContext::acceptDrag and specifies an action then pdwEffect
316 // will be DROPEFFECT_NONE throughout
317 m_nLastDropAction= ACTION_DEFAULT | ACTION_MOVE;
319 m_currentDragContext= static_cast<XDropTargetDragContext*>( new TargetDragContext(
320 static_cast<DropTarget*>(this) ) );
322 //--> TRA
324 // shortcut
325 if ( g_XTransferable.is( ) )
326 m_currentData = g_XTransferable;
327 else
329 // Convert the IDataObject to a XTransferable
330 m_currentData= m_aDataConverter.createTransferableFromDataObj(
331 m_xContext, IDataObjectPtr(pDataObj));
334 //<-- TRA
336 if( m_nCurrentDropAction != ACTION_NONE)
338 DropTargetDragEnterEvent e;
339 e.SupportedDataFlavors= m_currentData->getTransferDataFlavors();
340 e.DropAction= m_nCurrentDropAction;
341 e.Source= Reference<XInterface>( static_cast<XDropTarget*>(this),UNO_QUERY);
342 e.Context= m_currentDragContext;
343 POINT point={ pt.x, pt.y};
344 ScreenToClient( m_hWnd, &point);
345 e.LocationX= point.x;
346 e.LocationY= point.y;
347 e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
349 fire_dragEnter( e);
350 // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set
351 // by the listener (m_nCurrentDropAction) is allowed by the source. Only a allowed action is set
352 // in pdwEffect. The listener notification is asynchron, that is we cannot expext that the listener
353 // has already reacted to the notification.
354 // If there is more then one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed
355 // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed.
356 // On drop the target should present the user a dialog from which the user may change the action.
357 sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
358 *pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions);
360 else
362 *pdwEffect= DROPEFFECT_NONE;
365 return S_OK;
368 HRESULT DropTarget::DragOver( DWORD grfKeyState,
369 POINTL pt,
370 DWORD *pdwEffect)
372 if( m_bActive)
374 m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
376 if( m_nCurrentDropAction)
378 DropTargetDragEvent e;
379 e.DropAction= m_nCurrentDropAction;
380 e.Source= Reference<XInterface>(static_cast<XDropTarget*>(this),UNO_QUERY);
381 e.Context= m_currentDragContext;
382 POINT point={ pt.x, pt.y};
383 ScreenToClient( m_hWnd, &point);
384 e.LocationX= point.x;
385 e.LocationY= point.y;
386 e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
388 // if grfKeyState has changed since the last DragOver then fire events.
389 // A listener might change m_nCurrentDropAction by calling the
390 // XDropTargetDragContext::acceptDrag function. But this is not important
391 // because in the afterwards fired dragOver event the action reflects
392 // grgKeyState again.
393 if( m_nLastDropAction != m_nCurrentDropAction)
394 fire_dropActionChanged( e);
396 // The Event contains a XDropTargetDragContext implementation.
397 fire_dragOver( e);
398 // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set
399 // by the listener (m_nCurrentDropAction) is allowed by the source. Only a allowed action is set
400 // in pdwEffect. The listener notification is asynchron, that is we cannot expext that the listener
401 // has already reacted to the notification.
402 // If there is more then one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed
403 // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed.
404 // On drop the target should present the user a dialog from which the user may change the action.
405 sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
406 // set the last action to the current if listener has not changed the value yet
407 *pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions);
409 else
411 *pdwEffect= DROPEFFECT_NONE;
414 #if defined DBG_CONSOLE_OUT
415 printf("\nDropTarget::DragOver %d", *pdwEffect );
416 #endif
417 return S_OK;
420 HRESULT DropTarget::DragLeave( void)
422 #if defined DBG_CONSOLE_OUT
423 printf("\nDropTarget::DragLeave");
424 #endif
425 if( m_bActive)
428 m_currentData=0;
429 m_currentDragContext= 0;
430 m_currentDropContext= 0;
431 m_nLastDropAction= 0;
433 if( m_nDefaultActions != ACTION_NONE)
435 DropTargetEvent e;
436 e.Source= static_cast<XDropTarget*>(this);
438 fire_dragExit( e);
441 return S_OK;
444 HRESULT DropTarget::Drop( IDataObject * /*pDataObj*/,
445 DWORD grfKeyState,
446 POINTL pt,
447 DWORD *pdwEffect)
449 #if defined DBG_CONSOLE_OUT
450 printf("\nDropTarget::Drop");
451 #endif
452 if( m_bActive)
455 m_bDropComplete= sal_False;
457 m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
458 m_currentDropContext= static_cast<XDropTargetDropContext*>( new TargetDropContext( static_cast<DropTarget*>(this )) );
459 if( m_nCurrentDropAction)
461 DropTargetDropEvent e;
462 e.DropAction= m_nCurrentDropAction;
463 e.Source= Reference<XInterface>( static_cast<XDropTarget*>(this), UNO_QUERY);
464 e.Context= m_currentDropContext;
465 POINT point={ pt.x, pt.y};
466 ScreenToClient( m_hWnd, &point);
467 e.LocationX= point.x;
468 e.LocationY= point.y;
469 e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
470 e.Transferable= m_currentData;
471 fire_drop( e);
473 //if fire_drop returns than a listener might have modified m_nCurrentDropAction
474 if( m_bDropComplete == sal_True)
476 sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
477 *pdwEffect= dndActionsToSingleDropEffect( m_nCurrentDropAction & allowedActions);
479 else
480 *pdwEffect= DROPEFFECT_NONE;
482 else
483 *pdwEffect= DROPEFFECT_NONE;
485 m_currentData= 0;
486 m_currentDragContext= 0;
487 m_currentDropContext= 0;
488 m_nLastDropAction= 0;
490 return S_OK;
495 void DropTarget::fire_drop( const DropTargetDropEvent& dte)
497 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) );
498 if( pContainer)
500 OInterfaceIteratorHelper iter( *pContainer);
501 while( iter.hasMoreElements())
503 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
504 listener->drop( dte);
509 void DropTarget::fire_dragEnter( const DropTargetDragEnterEvent& e )
511 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) );
512 if( pContainer)
514 OInterfaceIteratorHelper iter( *pContainer);
515 while( iter.hasMoreElements())
517 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
518 listener->dragEnter( e);
523 void DropTarget::fire_dragExit( const DropTargetEvent& dte )
525 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) );
527 if( pContainer)
529 OInterfaceIteratorHelper iter( *pContainer);
530 while( iter.hasMoreElements())
532 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
533 listener->dragExit( dte);
538 void DropTarget::fire_dragOver( const DropTargetDragEvent& dtde )
540 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) );
541 if( pContainer)
543 OInterfaceIteratorHelper iter( *pContainer );
544 while( iter.hasMoreElements())
546 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
547 listener->dragOver( dtde);
552 void DropTarget::fire_dropActionChanged( const DropTargetDragEvent& dtde )
554 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( getCppuType( (Reference<XDropTargetListener>* )0 ) );
555 if( pContainer)
557 OInterfaceIteratorHelper iter( *pContainer);
558 while( iter.hasMoreElements())
560 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
561 listener->dropActionChanged( dtde);
566 // Non - interface functions ============================================================
567 // DropTarget fires events to XDropTargetListeners. The event object contains an
568 // XDropTargetDropContext implementaion. When the listener calls on that interface
569 // then the calls are delegated from DropContext (XDropTargetDropContext) to these
570 // functions.
571 // Only one listener which visible area is affected is allowed to call on
572 // XDropTargetDropContext
573 // Returning sal_False would cause the XDropTargetDropContext or ..DragContext implementation
574 // to throw an InvalidDNDOperationException, meaning that a Drag is not currently performed.
575 // return sal_False results in throwing a InvalidDNDOperationException in the caller.
577 void DropTarget::_acceptDrop(sal_Int8 dropOperation, const Reference<XDropTargetDropContext>& context)
579 if( context == m_currentDropContext)
581 m_nCurrentDropAction= dropOperation;
585 void DropTarget::_rejectDrop( const Reference<XDropTargetDropContext>& context)
587 if( context == m_currentDropContext)
589 m_nCurrentDropAction= ACTION_NONE;
593 void DropTarget::_dropComplete(sal_Bool success, const Reference<XDropTargetDropContext>& context)
595 if(context == m_currentDropContext)
597 m_bDropComplete= success;
600 // --------------------------------------------------------------------------------------
601 // DropTarget fires events to XDropTargetListeners. The event object can contains an
602 // XDropTargetDragContext implementaion. When the listener calls on that interface
603 // then the calls are delegated from DragContext (XDropTargetDragContext) to these
604 // functions.
605 // Only one listener which visible area is affected is allowed to call on
606 // XDropTargetDragContext
607 void DropTarget::_acceptDrag( sal_Int8 dragOperation, const Reference<XDropTargetDragContext>& context)
609 if( context == m_currentDragContext)
611 m_nLastDropAction= dragOperation;
615 void DropTarget::_rejectDrag( const Reference<XDropTargetDragContext>& context)
617 if(context == m_currentDragContext)
619 m_nLastDropAction= ACTION_NONE;
624 //--------------------------------------------------------------------------------------
627 // This function determines the action dependend on the pressed
628 // key modifiers ( CTRL, SHIFT, ALT, Right Mouse Button). The result
629 // is then checked against the allowed actions which can be set through
630 // XDropTarget::setDefaultActions. Only those values which are also
631 // default actions are returned. If setDefaultActions has not been called
632 // beforehand the default actions comprise all possible actions.
633 // params: grfKeyState - the modifier keys and mouse buttons currently pressed
634 inline sal_Int8 DropTarget::getFilteredActions( DWORD grfKeyState, DWORD dwEffect)
636 sal_Int8 actions= dndOleKeysToAction( grfKeyState, dndOleDropEffectsToActions( dwEffect));
637 return actions & m_nDefaultActions;
641 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */