1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_dtrans.hxx"
26 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
27 #include <com/sun/star/datatransfer/XTransferable.hpp>
28 #include <com/sun/star/awt/MouseButton.hpp>
29 #include <com/sun/star/awt/MouseEvent.hpp>
30 #include <rtl/unload.h>
31
32 #include <process.h>
33 #include <memory>
34
35 #include "source.hxx"
36 #include "globals.hxx"
37 #include "sourcecontext.hxx"
38 #include "../../inc/DtObjFactory.hxx"
39 #include <rtl/ustring.h>
40 #include <process.h>
41 #include <winuser.h>
42 #include <stdio.h>
43
44 #ifdef __MINGW32__
45 #define __uuidof(I) IID_##I
46 #endif
47
48 using namespace rtl;
49 using namespace cppu;
50 using namespace osl;
51 using namespace com::sun::star::datatransfer;
52 using namespace com::sun::star::datatransfer::dnd;
53 using namespace com::sun::star::datatransfer::dnd::DNDConstants;
54 using namespace com::sun::star::uno;
55 using namespace com::sun::star::awt::MouseButton;
56 using namespace com::sun::star::awt;
57 using namespace com::sun::star::lang;
58
59 extern rtl_StandardModuleCount g_moduleCount;
60
61 //--> TRA
62
63 extern Reference< XTransferable > g_XTransferable;
64
65 //<-- TRA
66
67 unsigned __stdcall DndOleSTAFunc(LPVOID pParams);
68
69 //----------------------------------------------------
70 /** Ctor
71 */
DragSource(const Reference<XMultiServiceFactory> & sf)72 DragSource::DragSource( const Reference<XMultiServiceFactory>& sf):
73 m_serviceFactory( sf),
74 WeakComponentImplHelper3< XDragSource, XInitialization, XServiceInfo >(m_mutex),
75 // m_pcurrentContext_impl(0),
76 m_hAppWindow(0),
77 m_MouseButton(0),
78 m_RunningDndOperationCount(0)
79 {
80 g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt );
81 }
82
83 //----------------------------------------------------
84 /** Dtor
85 */
~DragSource()86 DragSource::~DragSource()
87 {
88 g_moduleCount.modCnt.release( &g_moduleCount.modCnt );
89 }
90
91 //----------------------------------------------------
92 /** First start a new drag and drop thread if
93 the last one has finished
94
95 ????
96 Do we really need a separate thread for
97 every Dnd operation or only if the source
98 thread is an MTA thread
99 ????
100 */
StartDragImpl(const DragGestureEvent & trigger,sal_Int8 sourceActions,sal_Int32,sal_Int32,const Reference<XTransferable> & trans,const Reference<XDragSourceListener> & listener)101 void DragSource::StartDragImpl(
102 const DragGestureEvent& trigger,
103 sal_Int8 sourceActions,
104 sal_Int32 /*cursor*/,
105 sal_Int32 /*image*/,
106 const Reference<XTransferable >& trans,
107 const Reference<XDragSourceListener >& listener )
108 {
109 // The actions supported by the drag source
110 m_sourceActions= sourceActions;
111 // We need to know which mouse button triggered the operation.
112 // If it was the left one, then the drop occurs when that button
113 // has been released and if it was the right one then the drop
114 // occurs when the right button has been released. If the event is not
115 // set then we assume that the left button is pressed.
116 MouseEvent evtMouse;
117 trigger.Event >>= evtMouse;
118 m_MouseButton= evtMouse.Buttons;
119
120 // The SourceContext class administers the XDragSourceListener s and
121 // fires events to them. An instance only exists in the scope of this
122 // functions. However, the drag and drop operation causes callbacks
123 // to the IDropSource interface implemented in this class (but only
124 // while this function executes). The source context is also used
125 // in DragSource::QueryContinueDrag.
126 m_currentContext= static_cast<XDragSourceContext*>( new SourceContext(
127 static_cast<DragSource*>(this), listener ) );
128
129 // Convert the XTransferable data object into an IDataObject object;
130
131 //--> TRA
132 g_XTransferable = trans;
133 //<-- TRA
134
135 m_spDataObject= m_aDataConverter.createDataObjFromTransferable(
136 m_serviceFactory, trans);
137
138 // Obtain the id of the thread that created the window
139 DWORD processId;
140 m_threadIdWindow= GetWindowThreadProcessId( m_hAppWindow, &processId);
141
142 // hold the instance for the DnD thread, it's to late
143 // to acquire at the start of the thread procedure
144 // the thread procedure is responsible for the release
145 acquire();
146
147 // The thread accesses members of this instance but does not call acquire.
148 // Hopefully this instance is not destroyed before the thread has terminated.
149 unsigned threadId;
150 HANDLE hThread= reinterpret_cast<HANDLE>(_beginthreadex(
151 0, 0, DndOleSTAFunc, reinterpret_cast<void*>(this), 0, &threadId));
152
153 // detach from thread
154 CloseHandle(hThread);
155 }
156
157 // XInitialization
158
159 //----------------------------------------------------
160 /** aArguments contains a machine id
161 */
initialize(const Sequence<Any> & aArguments)162 void SAL_CALL DragSource::initialize( const Sequence< Any >& aArguments )
163 throw(Exception, RuntimeException)
164 {
165 if( aArguments.getLength() >=2)
166 m_hAppWindow= *(HWND*)aArguments[1].getValue();
167 OSL_ASSERT( IsWindow( m_hAppWindow) );
168 }
169
170 //----------------------------------------------------
171 /** XDragSource
172 */
isDragImageSupported()173 sal_Bool SAL_CALL DragSource::isDragImageSupported( )
174 throw(RuntimeException)
175 {
176 return 0;
177 }
178
179 //----------------------------------------------------
180 /**
181 */
getDefaultCursor(sal_Int8)182 sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ )
183 throw( IllegalArgumentException, RuntimeException)
184 {
185 return 0;
186 }
187
188 //----------------------------------------------------
189 /** Notifies the XDragSourceListener by
190 calling dragDropEnd
191 */
startDrag(const DragGestureEvent & trigger,sal_Int8 sourceActions,sal_Int32 cursor,sal_Int32 image,const Reference<XTransferable> & trans,const Reference<XDragSourceListener> & listener)192 void SAL_CALL DragSource::startDrag(
193 const DragGestureEvent& trigger,
194 sal_Int8 sourceActions,
195 sal_Int32 cursor,
196 sal_Int32 image,
197 const Reference<XTransferable >& trans,
198 const Reference<XDragSourceListener >& listener ) throw( RuntimeException)
199 {
200 // Allow only one running dnd operation at a time,
201 // see XDragSource documentation
202
203 long cnt = InterlockedIncrement(&m_RunningDndOperationCount);
204
205 if (1 == cnt)
206 {
207 StartDragImpl(trigger, sourceActions, cursor, image, trans, listener);
208 }
209 else
210 {
211 //OSL_ENSURE(false, "Overlapping Drag&Drop operation rejected!");
212
213 cnt = InterlockedDecrement(&m_RunningDndOperationCount);
214
215 DragSourceDropEvent dsde;
216
217 dsde.DropAction = ACTION_NONE;
218 dsde.DropSuccess = false;
219
220 try
221 {
222 listener->dragDropEnd(dsde);
223 }
224 catch(RuntimeException&)
225 {
226 OSL_ENSURE(false, "Runtime exception during event dispatching");
227 }
228 }
229 }
230
231 //----------------------------------------------------
232 /**IDropTarget
233 */
QueryInterface(REFIID riid,void ** ppvObject)234 HRESULT STDMETHODCALLTYPE DragSource::QueryInterface( REFIID riid, void **ppvObject)
235 {
236 if( !ppvObject)
237 return E_POINTER;
238 *ppvObject= NULL;
239
240 if( riid == __uuidof( IUnknown) )
241 *ppvObject= static_cast<IUnknown*>( this);
242 else if ( riid == __uuidof( IDropSource) )
243 *ppvObject= static_cast<IDropSource*>( this);
244
245 if(*ppvObject)
246 {
247 AddRef();
248 return S_OK;
249 }
250 else
251 return E_NOINTERFACE;
252
253 }
254
255 //----------------------------------------------------
256 /**
257 */
AddRef(void)258 ULONG STDMETHODCALLTYPE DragSource::AddRef( void)
259 {
260 acquire();
261 return (ULONG) m_refCount;
262 }
263
264 //----------------------------------------------------
265 /**
266 */
Release(void)267 ULONG STDMETHODCALLTYPE DragSource::Release( void)
268 {
269 ULONG ref= m_refCount;
270 release();
271 return --ref;
272 }
273
274 //----------------------------------------------------
275 /** IDropSource
276 */
QueryContinueDrag(BOOL fEscapePressed,DWORD grfKeyState)277 HRESULT STDMETHODCALLTYPE DragSource::QueryContinueDrag(
278 /* [in] */ BOOL fEscapePressed,
279 /* [in] */ DWORD grfKeyState)
280 {
281 #if defined DBG_CONSOLE_OUT
282 printf("\nDragSource::QueryContinueDrag");
283 #endif
284
285 HRESULT retVal= S_OK; // default continue DnD
286
287 if (fEscapePressed)
288 {
289 retVal= DRAGDROP_S_CANCEL;
290 }
291 else
292 {
293 if( ( m_MouseButton == MouseButton::RIGHT && !(grfKeyState & MK_RBUTTON) ) ||
294 ( m_MouseButton == MouseButton::MIDDLE && !(grfKeyState & MK_MBUTTON) ) ||
295 ( m_MouseButton == MouseButton::LEFT && !(grfKeyState & MK_LBUTTON) ) ||
296 ( m_MouseButton == 0 && !(grfKeyState & MK_LBUTTON) ) )
297 {
298 retVal= DRAGDROP_S_DROP;
299 }
300 }
301
302 // fire dropActionChanged event.
303 // this is actually done by the context, which also detects whether the action
304 // changed at all
305 sal_Int8 dropAction= fEscapePressed ? ACTION_NONE :
306 dndOleKeysToAction( grfKeyState, m_sourceActions);
307
308 sal_Int8 userAction= fEscapePressed ? ACTION_NONE :
309 dndOleKeysToAction( grfKeyState, -1 );
310
311 static_cast<SourceContext*>(m_currentContext.get())->fire_dropActionChanged(
312 dropAction, userAction);
313
314 return retVal;
315 }
316
317 //----------------------------------------------------
318 /**
319 */
GiveFeedback(DWORD dwEffect)320 HRESULT STDMETHODCALLTYPE DragSource::GiveFeedback(
321 /* [in] */ DWORD
322 #if defined DBG_CONSOLE_OUT
323 dwEffect
324 #endif
325 )
326 {
327 #if defined DBG_CONSOLE_OUT
328 printf("\nDragSource::GiveFeedback %d", dwEffect);
329 #endif
330
331 return DRAGDROP_S_USEDEFAULTCURSORS;
332 }
333
334 // XServiceInfo
getImplementationName()335 OUString SAL_CALL DragSource::getImplementationName( ) throw (RuntimeException)
336 {
337 return OUString(RTL_CONSTASCII_USTRINGPARAM(DNDSOURCE_IMPL_NAME));;
338 }
339 // XServiceInfo
supportsService(const OUString & ServiceName)340 sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName ) throw (RuntimeException)
341 {
342 if( ServiceName.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(DNDSOURCE_SERVICE_NAME ))))
343 return sal_True;
344 return sal_False;
345 }
346
getSupportedServiceNames()347 Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames( ) throw (RuntimeException)
348 {
349 OUString names[1]= {OUString(RTL_CONSTASCII_USTRINGPARAM(DNDSOURCE_SERVICE_NAME))};
350
351 return Sequence<OUString>(names, 1);
352 }
353
354 //----------------------------------------------------
355 /**This function is called as extra thread from
356 DragSource::executeDrag. The function
357 carries out a drag and drop operation by calling
358 DoDragDrop. The thread also notifies all
359 XSourceListener.
360 */
DndOleSTAFunc(LPVOID pParams)361 unsigned __stdcall DndOleSTAFunc(LPVOID pParams)
362 {
363 // The structure contains all arguments for DoDragDrop and other
364 DragSource *pSource= (DragSource*)pParams;
365
366 // Drag and drop only works in a thread in which OleInitialize is called.
367 HRESULT hr= OleInitialize( NULL);
368
369 if(SUCCEEDED(hr))
370 {
371 // We force the creation of a thread message queue. This is necessary
372 // for a later call to AttachThreadInput
373 MSG msgtemp;
374 PeekMessage( &msgtemp, NULL, WM_USER, WM_USER, PM_NOREMOVE);
375
376 DWORD threadId= GetCurrentThreadId();
377
378 // This thread is attached to the thread that created the window. Hence
379 // this thread also receives all mouse and keyboard messages which are
380 // needed by DoDragDrop
381 AttachThreadInput( threadId , pSource->m_threadIdWindow, TRUE );
382
383 DWORD dwEffect= 0;
384 hr= DoDragDrop(
385 pSource->m_spDataObject.get(),
386 static_cast<IDropSource*>(pSource),
387 dndActionsToDropEffects( pSource->m_sourceActions),
388 &dwEffect);
389
390 // #105428 detach my message queue from the other threads
391 // message queue before calling fire_dragDropEnd else
392 // the office may appear to hang sometimes
393 AttachThreadInput( threadId, pSource->m_threadIdWindow, FALSE);
394
395 //--> TRA
396 // clear the global transferable again
397 g_XTransferable = Reference< XTransferable >( );
398 //<-- TRA
399
400 OSL_ENSURE( hr != E_INVALIDARG, "IDataObject impl does not contain valid data");
401
402 //Fire event
403 sal_Int8 action= hr == DRAGDROP_S_DROP ? dndOleDropEffectsToActions( dwEffect) : ACTION_NONE;
404
405 static_cast<SourceContext*>(pSource->m_currentContext.get())->fire_dragDropEnd(
406 hr == DRAGDROP_S_DROP ? sal_True : sal_False, action);
407
408 // Destroy SourceContextslkfgj
409 pSource->m_currentContext= 0;
410 // Destroy the XTransferable wrapper
411 pSource->m_spDataObject=0;
412
413 OleUninitialize();
414 }
415
416 InterlockedDecrement(&pSource->m_RunningDndOperationCount);
417
418 // the DragSource was manually acquired by
419 // thread starting method DelayedStartDrag
420 pSource->release();
421
422 return 0;
423 }
424
425