bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / win / dtrans / target.cxx
blob077aeeb0d51473755657afd411f582722437c3bd
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>
22 #include <cppuhelper/supportsservice.hxx>
23 #include <o3tl/any.hxx>
25 #include <stdio.h>
26 #include <win/dnd_target.hxx>
27 #include "idroptarget.hxx"
28 #include "globals.hxx"
29 #include "targetdropcontext.hxx"
30 #include "targetdragcontext.hxx"
31 #include <rtl/ustring.h>
32 #include <osl/thread.h>
33 #include <sal/log.hxx>
34 #include <comphelper/windowserrorstring.hxx>
36 #include "DOTransferable.hxx"
38 using namespace cppu;
39 using namespace osl;
40 using namespace com::sun::star::datatransfer;
41 using namespace com::sun::star::datatransfer::dnd;
42 using namespace com::sun::star::datatransfer::dnd::DNDConstants;
44 #define WM_REGISTERDRAGDROP WM_USER + 1
45 #define WM_REVOKEDRAGDROP WM_USER + 2
47 DWORD WINAPI DndTargetOleSTAFunc(LPVOID pParams);
49 DropTarget::DropTarget( const Reference<XComponentContext>& rxContext):
50 WeakComponentImplHelper<XInitialization,XDropTarget, XServiceInfo>(m_aMutex),
51 m_hWnd( nullptr),
52 m_threadIdWindow(0),
53 m_threadIdTarget(0),
54 m_hOleThread(nullptr),
55 m_oleThreadId( 0),
56 m_pDropTarget( nullptr),
57 m_xContext( rxContext ),
58 m_bActive(true),
59 m_nDefaultActions(ACTION_COPY|ACTION_MOVE|ACTION_LINK|ACTION_DEFAULT),
60 m_nCurrentDropAction( ACTION_NONE),
61 m_nLastDropAction(0),
62 m_bDropComplete(false)
66 DropTarget::~DropTarget()
69 // called from WeakComponentImplHelperX::dispose
70 // WeakComponentImplHelper calls disposing before it destroys
71 // itself.
72 // NOTE: RevokeDragDrop decrements the ref count on the IDropTarget
73 // interface. (m_pDropTarget)
74 // If the HWND is invalid then it doesn't decrement and
75 // the IDropTarget object will live on. MEMORY LEAK
76 void SAL_CALL DropTarget::disposing()
78 if( m_threadIdTarget)
80 // Call RevokeDragDrop and wait for the OLE thread to die;
81 PostThreadMessageW( m_threadIdTarget, WM_REVOKEDRAGDROP, reinterpret_cast<WPARAM>(this), 0);
82 WaitForSingleObject( m_hOleThread, INFINITE);
83 CloseHandle( m_hOleThread);
84 //OSL_ENSURE( SUCCEEDED( hr), "HWND not valid!" );
86 else
88 RevokeDragDrop( m_hWnd);
89 m_hWnd= nullptr;
91 if( m_pDropTarget)
93 CoLockObjectExternal( m_pDropTarget, FALSE, TRUE);
94 m_pDropTarget->Release();
95 m_pDropTarget = nullptr;
98 if( m_oleThreadId)
100 if( m_oleThreadId == CoGetCurrentProcess() )
101 OleUninitialize();
106 void SAL_CALL DropTarget::initialize( const Sequence< Any >& aArguments )
108 // The window must be registered for Dnd by RegisterDragDrop. We must ensure
109 // that RegisterDragDrop is called from an STA ( OleInitialize) thread.
110 // As long as the window is registered we need to receive OLE messages in
111 // an OLE thread. That is to say, if DropTarget::initialize was called from an
112 // MTA thread then we create an OLE thread in which the window is registered.
113 // The thread will stay alive until aver RevokeDragDrop has been called.
115 // Additionally even if RegisterDragDrop is called from an STA thread we have
116 // to ensure that it is called from the same thread that created the Window
117 // otherwise messages sent during DND won't reach the windows message queue.
118 // Calling AttachThreadInput first would resolve this problem but would block
119 // the message queue of the calling thread. So if the current thread
120 // (even if it's an STA thread) and the thread that created the window are not
121 // identical we need to create a new thread as we do when the calling thread is
122 // an MTA thread.
124 if( aArguments.getLength() > 0)
126 // Get the window handle from aArgument. It is needed for RegisterDragDrop.
127 m_hWnd= reinterpret_cast<HWND>(static_cast<sal_uIntPtr>(*o3tl::doAccess<sal_uInt64>(aArguments[0])));
128 OSL_ASSERT( IsWindow( m_hWnd) );
130 // Obtain the id of the thread that created the window
131 m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, nullptr);
133 HRESULT hr= OleInitialize( nullptr);
135 // Current thread is MTA or Current thread and Window thread are not identical
136 if( hr == RPC_E_CHANGED_MODE || GetCurrentThreadId() != m_threadIdWindow )
138 OSL_ENSURE( ! m_threadIdTarget,"initialize was called twice");
139 // create the IDropTargetImplementation
140 m_pDropTarget= new IDropTargetImpl( *this );
141 m_pDropTarget->AddRef();
143 // Obtain the id of the thread that created the window
144 m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, nullptr);
145 // The event is set by the thread that we will create momentarily.
146 // It indicates that the thread is ready to receive messages.
147 HANDLE m_evtThreadReady= CreateEventW( nullptr, FALSE, FALSE, nullptr);
149 m_hOleThread= CreateThread( nullptr, 0, DndTargetOleSTAFunc,
150 &m_evtThreadReady, 0, &m_threadIdTarget);
151 WaitForSingleObject( m_evtThreadReady, INFINITE);
152 CloseHandle( m_evtThreadReady);
153 PostThreadMessageW( m_threadIdTarget, WM_REGISTERDRAGDROP, reinterpret_cast<WPARAM>(this), 0);
155 else if( hr == S_OK || hr == S_FALSE)
157 // current thread is STA
158 // If OleInitialize has been called by the caller then we must not call
159 // OleUninitialize
160 if( hr == S_OK)
162 // caller did not call OleInitialize, so we call OleUninitialize
163 // remember the thread that will call OleUninitialize
164 m_oleThreadId= CoGetCurrentProcess(); // get a unique thread id
167 // Get the window handle from aArgument. It is needed for RegisterDragDrop.
168 // create the IDropTargetImplementation
169 m_pDropTarget= new IDropTargetImpl( *this );
170 m_pDropTarget->AddRef();
171 // CoLockObjectExternal is prescribed by the protocol. It bumps up the ref count
172 if( SUCCEEDED( CoLockObjectExternal( m_pDropTarget, TRUE, FALSE)))
174 if( FAILED( RegisterDragDrop( m_hWnd, m_pDropTarget) ) )
176 // do clean up if drag and drop is not possible
177 CoLockObjectExternal( m_pDropTarget, FALSE, FALSE);
178 m_pDropTarget->Release();
179 m_pDropTarget = nullptr;
180 m_hWnd= nullptr;
184 else
185 throw Exception("OleInitialize failed with " + OUString::number(hr), nullptr);
190 // This function is called as extra thread from DragSource::startDrag.
191 // The function carries out a drag and drop operation by calling
192 // DoDragDrop. The thread also notifies all XSourceListener.
193 DWORD WINAPI DndTargetOleSTAFunc(LPVOID pParams)
195 osl_setThreadName("DropTarget DndTargetOleSTAFunc");
197 HRESULT hr= OleInitialize( nullptr);
198 if( SUCCEEDED( hr) )
200 MSG msg;
201 // force the creation of a message queue
202 PeekMessageW( &msg, nullptr, 0, 0, PM_NOREMOVE);
203 // Signal the creator ( DropTarget::initialize) that the thread is
204 // ready to receive messages.
205 SetEvent( *static_cast<HANDLE*>(pParams));
206 // Thread id is needed for attaching this message queue to the one of the
207 // thread where the window was created.
208 DWORD threadId= GetCurrentThreadId();
209 // We force the creation of a thread message queue. This is necessary
210 // for a later call to AttachThreadInput
211 for (;;)
213 int const bRet = GetMessageW(&msg, nullptr, 0, 0);
214 if (bRet == 0)
216 break;
218 if (-1 == bRet)
220 SAL_WARN("vcl.win.dtrans", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
221 break;
223 if( msg.message == WM_REGISTERDRAGDROP)
225 DropTarget *pTarget= reinterpret_cast<DropTarget*>(msg.wParam);
226 // This thread is attached to the thread that created the window. Hence
227 // this thread also receives all mouse and keyboard messages which are
228 // needed
229 AttachThreadInput( threadId , pTarget->m_threadIdWindow, TRUE );
231 if( SUCCEEDED( CoLockObjectExternal(pTarget-> m_pDropTarget, TRUE, FALSE)))
233 if( FAILED( RegisterDragDrop( pTarget-> m_hWnd, pTarget-> m_pDropTarget) ) )
235 // do clean up if drag and drop is not possible
236 CoLockObjectExternal( pTarget->m_pDropTarget, FALSE, FALSE);
237 pTarget->m_pDropTarget->Release();
238 pTarget->m_pDropTarget = nullptr;
239 pTarget->m_hWnd= nullptr;
243 else if( msg.message == WM_REVOKEDRAGDROP)
245 DropTarget *pTarget= reinterpret_cast<DropTarget*>(msg.wParam);
246 RevokeDragDrop( pTarget-> m_hWnd);
247 // Detach this thread from the window thread
248 AttachThreadInput( threadId, pTarget->m_threadIdWindow, FALSE);
249 pTarget->m_hWnd= nullptr;
250 break;
252 TranslateMessage( &msg);
253 DispatchMessageW( &msg);
255 OleUninitialize();
257 return 0;
260 // XServiceInfo
261 OUString SAL_CALL DropTarget::getImplementationName( )
263 return "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1";
265 // XServiceInfo
266 sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName )
268 return cppu::supportsService(this, ServiceName);
271 Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames( )
273 return { "com.sun.star.datatransfer.dnd.OleDropTarget" };
276 // XDropTarget
277 void SAL_CALL DropTarget::addDropTargetListener( const Reference< XDropTargetListener >& dtl )
279 rBHelper.addListener( cppu::UnoType<decltype(dtl)>::get(), dtl );
282 void SAL_CALL DropTarget::removeDropTargetListener( const Reference< XDropTargetListener >& dtl )
284 rBHelper.removeListener( cppu::UnoType<decltype(dtl)>::get(), dtl );
287 sal_Bool SAL_CALL DropTarget::isActive( )
289 return m_bActive; //m_bDropTargetRegistered;
292 void SAL_CALL DropTarget::setActive( sal_Bool _b )
294 MutexGuard g(m_aMutex);
295 m_bActive= _b;
298 sal_Int8 SAL_CALL DropTarget::getDefaultActions( )
300 return m_nDefaultActions;
303 void SAL_CALL DropTarget::setDefaultActions( sal_Int8 actions )
305 OSL_ENSURE( actions < 8, "No valid default actions");
306 m_nDefaultActions= actions;
309 HRESULT DropTarget::DragEnter( IDataObject *pDataObj,
310 DWORD grfKeyState,
311 POINTL pt,
312 DWORD *pdwEffect)
314 #if defined DBG_CONSOLE_OUT
315 printf("\nDropTarget::DragEnter state: %x effect %d", grfKeyState, *pdwEffect);
316 #endif
317 if( m_bActive )
319 // Intersection of pdwEffect and the allowed actions ( setDefaultActions)
320 m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
321 // m_nLastDropAction has to be set by a listener. If no listener calls
322 //XDropTargetDragContext::acceptDrag and specifies an action then pdwEffect
323 // will be DROPEFFECT_NONE throughout
324 m_nLastDropAction= ACTION_DEFAULT | ACTION_MOVE;
326 m_currentDragContext = new TargetDragContext(this);
328 //--> TRA
330 // shortcut
331 if ( g_XTransferable.is( ) )
332 m_currentData = g_XTransferable;
333 else
335 // Convert the IDataObject to a XTransferable
336 m_currentData = new CDOTransferable(m_xContext, IDataObjectPtr(pDataObj));
339 //<-- TRA
341 if( m_nCurrentDropAction != ACTION_NONE)
343 DropTargetDragEnterEvent e;
344 e.SupportedDataFlavors= m_currentData->getTransferDataFlavors();
345 e.DropAction= m_nCurrentDropAction;
346 e.Source.set( static_cast<XDropTarget*>(this),UNO_QUERY);
347 e.Context= m_currentDragContext;
348 POINT point={ pt.x, pt.y};
349 ScreenToClient( m_hWnd, &point);
350 e.LocationX= point.x;
351 e.LocationY= point.y;
352 e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
354 fire_dragEnter( e);
355 // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set
356 // by the listener (m_nCurrentDropAction) is allowed by the source. Only an allowed action is set
357 // in pdwEffect. The listener notification is asynchronous, that is we cannot expect that the listener
358 // has already reacted to the notification.
359 // If there is more than one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed
360 // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed.
361 // On drop the target should present the user a dialog from which the user may change the action.
362 sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
363 *pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions);
365 else
367 *pdwEffect= DROPEFFECT_NONE;
370 return S_OK;
373 HRESULT DropTarget::DragOver( DWORD grfKeyState,
374 POINTL pt,
375 DWORD *pdwEffect)
377 if( m_bActive)
379 m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
381 if( m_nCurrentDropAction)
383 DropTargetDragEvent e;
384 e.DropAction= m_nCurrentDropAction;
385 e.Source.set(static_cast<XDropTarget*>(this),UNO_QUERY);
386 e.Context= m_currentDragContext;
387 POINT point={ pt.x, pt.y};
388 ScreenToClient( m_hWnd, &point);
389 e.LocationX= point.x;
390 e.LocationY= point.y;
391 e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
393 // if grfKeyState has changed since the last DragOver then fire events.
394 // A listener might change m_nCurrentDropAction by calling the
395 // XDropTargetDragContext::acceptDrag function. But this is not important
396 // because in the afterwards fired dragOver event the action reflects
397 // grgKeyState again.
398 if( m_nLastDropAction != m_nCurrentDropAction)
399 fire_dropActionChanged( e);
401 // The Event contains a XDropTargetDragContext implementation.
402 fire_dragOver( e);
403 // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set
404 // by the listener (m_nCurrentDropAction) is allowed by the source. Only an allowed action is set
405 // in pdwEffect. The listener notification is asynchronous, that is we cannot expect that the listener
406 // has already reacted to the notification.
407 // If there is more than one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed
408 // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed.
409 // On drop the target should present the user a dialog from which the user may change the action.
410 sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
411 // set the last action to the current if listener has not changed the value yet
412 *pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions);
414 else
416 *pdwEffect= DROPEFFECT_NONE;
419 #if defined DBG_CONSOLE_OUT
420 printf("\nDropTarget::DragOver %d", *pdwEffect );
421 #endif
422 return S_OK;
425 HRESULT DropTarget::DragLeave()
427 #if defined DBG_CONSOLE_OUT
428 printf("\nDropTarget::DragLeave");
429 #endif
430 if( m_bActive)
433 m_currentData=nullptr;
434 m_currentDragContext= nullptr;
435 m_currentDropContext= nullptr;
436 m_nLastDropAction= 0;
438 if( m_nDefaultActions != ACTION_NONE)
440 DropTargetEvent e;
441 e.Source= static_cast<XDropTarget*>(this);
443 fire_dragExit( e);
446 return S_OK;
449 HRESULT DropTarget::Drop( IDataObject * /*pDataObj*/,
450 DWORD grfKeyState,
451 POINTL pt,
452 DWORD *pdwEffect)
454 #if defined DBG_CONSOLE_OUT
455 printf("\nDropTarget::Drop");
456 #endif
457 if( m_bActive)
460 m_bDropComplete= false;
462 m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
463 m_currentDropContext = new TargetDropContext(this);
464 if( m_nCurrentDropAction)
466 DropTargetDropEvent e;
467 e.DropAction= m_nCurrentDropAction;
468 e.Source.set( static_cast<XDropTarget*>(this), UNO_QUERY);
469 e.Context= m_currentDropContext;
470 POINT point={ pt.x, pt.y};
471 ScreenToClient( m_hWnd, &point);
472 e.LocationX= point.x;
473 e.LocationY= point.y;
474 e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
475 e.Transferable= m_currentData;
476 fire_drop( e);
478 //if fire_drop returns than a listener might have modified m_nCurrentDropAction
479 if( m_bDropComplete )
481 sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
482 *pdwEffect= dndActionsToSingleDropEffect( m_nCurrentDropAction & allowedActions);
484 else
485 *pdwEffect= DROPEFFECT_NONE;
487 else
488 *pdwEffect= DROPEFFECT_NONE;
490 m_currentData= nullptr;
491 m_currentDragContext= nullptr;
492 m_currentDropContext= nullptr;
493 m_nLastDropAction= 0;
495 return S_OK;
498 void DropTarget::fire_drop( const DropTargetDropEvent& dte)
500 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
501 if( pContainer)
503 OInterfaceIteratorHelper iter( *pContainer);
504 while( iter.hasMoreElements())
506 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
507 listener->drop( dte);
512 void DropTarget::fire_dragEnter( const DropTargetDragEnterEvent& e )
514 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
515 if( pContainer)
517 OInterfaceIteratorHelper iter( *pContainer);
518 while( iter.hasMoreElements())
520 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
521 listener->dragEnter( e);
526 void DropTarget::fire_dragExit( const DropTargetEvent& dte )
528 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
530 if( pContainer)
532 OInterfaceIteratorHelper iter( *pContainer);
533 while( iter.hasMoreElements())
535 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
536 listener->dragExit( dte);
541 void DropTarget::fire_dragOver( const DropTargetDragEvent& dtde )
543 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
544 if( pContainer)
546 OInterfaceIteratorHelper iter( *pContainer );
547 while( iter.hasMoreElements())
549 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
550 listener->dragOver( dtde);
555 void DropTarget::fire_dropActionChanged( const DropTargetDragEvent& dtde )
557 OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
558 if( pContainer)
560 OInterfaceIteratorHelper iter( *pContainer);
561 while( iter.hasMoreElements())
563 Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
564 listener->dropActionChanged( dtde);
569 // Non - interface functions
570 // DropTarget fires events to XDropTargetListeners. The event object contains an
571 // XDropTargetDropContext implementation. When the listener calls on that interface
572 // then the calls are delegated from DropContext (XDropTargetDropContext) to these
573 // functions.
574 // Only one listener which visible area is affected is allowed to call on
575 // XDropTargetDropContext
576 // Returning sal_False would cause the XDropTargetDropContext or ..DragContext implementation
577 // to throw an InvalidDNDOperationException, meaning that a Drag is not currently performed.
578 // return sal_False results in throwing an InvalidDNDOperationException in the caller.
580 void DropTarget::_acceptDrop(sal_Int8 dropOperation, const Reference<XDropTargetDropContext>& context)
582 if( context == m_currentDropContext)
584 m_nCurrentDropAction= dropOperation;
588 void DropTarget::_rejectDrop( const Reference<XDropTargetDropContext>& context)
590 if( context == m_currentDropContext)
592 m_nCurrentDropAction= ACTION_NONE;
596 void DropTarget::_dropComplete(bool success, const Reference<XDropTargetDropContext>& context)
598 if(context == m_currentDropContext)
600 m_bDropComplete= success;
604 // DropTarget fires events to XDropTargetListeners. The event object can contains an
605 // XDropTargetDragContext implementation. When the listener calls on that interface
606 // then the calls are delegated from DragContext (XDropTargetDragContext) to these
607 // functions.
608 // Only one listener which visible area is affected is allowed to call on
609 // XDropTargetDragContext
610 void DropTarget::_acceptDrag( sal_Int8 dragOperation, const Reference<XDropTargetDragContext>& context)
612 if( context == m_currentDragContext)
614 m_nLastDropAction= dragOperation;
618 void DropTarget::_rejectDrag( const Reference<XDropTargetDragContext>& context)
620 if(context == m_currentDragContext)
622 m_nLastDropAction= ACTION_NONE;
626 // This function determines the action dependent on the pressed
627 // key modifiers ( CTRL, SHIFT, ALT, Right Mouse Button). The result
628 // is then checked against the allowed actions which can be set through
629 // XDropTarget::setDefaultActions. Only those values which are also
630 // default actions are returned. If setDefaultActions has not been called
631 // beforehand the default actions comprise all possible actions.
632 // params: grfKeyState - the modifier keys and mouse buttons currently pressed
633 inline sal_Int8 DropTarget::getFilteredActions( DWORD grfKeyState, DWORD dwEffect)
635 sal_Int8 actions= dndOleKeysToAction( grfKeyState, dndOleDropEffectsToActions( dwEffect));
636 return actions & m_nDefaultActions;
639 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
640 dtrans_DropTarget_get_implementation(
641 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
643 return cppu::acquire(new DropTarget(context));
646 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */