xref: /trunk/main/dtrans/source/win32/dnd/source.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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