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