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