| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_dtrans.hxx" |
| #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> |
| #include <com/sun/star/datatransfer/XTransferable.hpp> |
| #include <com/sun/star/awt/MouseButton.hpp> |
| #include <com/sun/star/awt/MouseEvent.hpp> |
| #include <rtl/unload.h> |
| |
| #include <process.h> |
| #include <memory> |
| |
| #include "source.hxx" |
| #include "globals.hxx" |
| #include "sourcecontext.hxx" |
| #include "../../inc/DtObjFactory.hxx" |
| #include <rtl/ustring.h> |
| #include <process.h> |
| #include <winuser.h> |
| #include <stdio.h> |
| |
| #ifdef __MINGW32__ |
| #define __uuidof(I) IID_##I |
| #endif |
| |
| using namespace rtl; |
| using namespace cppu; |
| using namespace osl; |
| using namespace com::sun::star::datatransfer; |
| using namespace com::sun::star::datatransfer::dnd; |
| using namespace com::sun::star::datatransfer::dnd::DNDConstants; |
| using namespace com::sun::star::uno; |
| using namespace com::sun::star::awt::MouseButton; |
| using namespace com::sun::star::awt; |
| using namespace com::sun::star::lang; |
| |
| extern rtl_StandardModuleCount g_moduleCount; |
| |
| //--> TRA |
| |
| extern Reference< XTransferable > g_XTransferable; |
| |
| //<-- TRA |
| |
| unsigned __stdcall DndOleSTAFunc(LPVOID pParams); |
| |
| //---------------------------------------------------- |
| /** Ctor |
| */ |
| DragSource::DragSource( const Reference<XMultiServiceFactory>& sf): |
| m_serviceFactory( sf), |
| WeakComponentImplHelper3< XDragSource, XInitialization, XServiceInfo >(m_mutex), |
| // m_pcurrentContext_impl(0), |
| m_hAppWindow(0), |
| m_MouseButton(0), |
| m_RunningDndOperationCount(0) |
| { |
| g_moduleCount.modCnt.acquire( &g_moduleCount.modCnt ); |
| } |
| |
| //---------------------------------------------------- |
| /** Dtor |
| */ |
| DragSource::~DragSource() |
| { |
| g_moduleCount.modCnt.release( &g_moduleCount.modCnt ); |
| } |
| |
| //---------------------------------------------------- |
| /** First start a new drag and drop thread if |
| the last one has finished |
| |
| ???? |
| Do we really need a separate thread for |
| every Dnd opeartion or only if the source |
| thread is an MTA thread |
| ???? |
| */ |
| void DragSource::StartDragImpl( |
| const DragGestureEvent& trigger, |
| sal_Int8 sourceActions, |
| sal_Int32 /*cursor*/, |
| sal_Int32 /*image*/, |
| const Reference<XTransferable >& trans, |
| const Reference<XDragSourceListener >& listener ) |
| { |
| // The actions supported by the drag source |
| m_sourceActions= sourceActions; |
| // We need to know which mouse button triggered the operation. |
| // If it was the left one, then the drop occurs when that button |
| // has been released and if it was the right one then the drop |
| // occurs when the right button has been released. If the event is not |
| // set then we assume that the left button is pressed. |
| MouseEvent evtMouse; |
| trigger.Event >>= evtMouse; |
| m_MouseButton= evtMouse.Buttons; |
| |
| // The SourceContext class administers the XDragSourceListener s and |
| // fires events to them. An instance only exists in the scope of this |
| // functions. However, the drag and drop operation causes callbacks |
| // to the IDropSource interface implemented in this class (but only |
| // while this function executes). The source context is also used |
| // in DragSource::QueryContinueDrag. |
| m_currentContext= static_cast<XDragSourceContext*>( new SourceContext( |
| static_cast<DragSource*>(this), listener ) ); |
| |
| // Convert the XTransferable data object into an IDataObject object; |
| |
| //--> TRA |
| g_XTransferable = trans; |
| //<-- TRA |
| |
| m_spDataObject= m_aDataConverter.createDataObjFromTransferable( |
| m_serviceFactory, trans); |
| |
| // Obtain the id of the thread that created the window |
| DWORD processId; |
| m_threadIdWindow= GetWindowThreadProcessId( m_hAppWindow, &processId); |
| |
| // hold the instance for the DnD thread, it's to late |
| // to acquire at the start of the thread procedure |
| // the thread procedure is responsible for the release |
| acquire(); |
| |
| // The thread acccesses members of this instance but does not call acquire. |
| // Hopefully this instance is not destroyed before the thread has terminated. |
| unsigned threadId; |
| HANDLE hThread= reinterpret_cast<HANDLE>(_beginthreadex( |
| 0, 0, DndOleSTAFunc, reinterpret_cast<void*>(this), 0, &threadId)); |
| |
| // detach from thread |
| CloseHandle(hThread); |
| } |
| |
| // XInitialization |
| |
| //---------------------------------------------------- |
| /** aArguments contains a machine id |
| */ |
| void SAL_CALL DragSource::initialize( const Sequence< Any >& aArguments ) |
| throw(Exception, RuntimeException) |
| { |
| if( aArguments.getLength() >=2) |
| m_hAppWindow= *(HWND*)aArguments[1].getValue(); |
| OSL_ASSERT( IsWindow( m_hAppWindow) ); |
| } |
| |
| //---------------------------------------------------- |
| /** XDragSource |
| */ |
| sal_Bool SAL_CALL DragSource::isDragImageSupported( ) |
| throw(RuntimeException) |
| { |
| return 0; |
| } |
| |
| //---------------------------------------------------- |
| /** |
| */ |
| sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ ) |
| throw( IllegalArgumentException, RuntimeException) |
| { |
| return 0; |
| } |
| |
| //---------------------------------------------------- |
| /** Notifies the XDragSourceListener by |
| calling dragDropEnd |
| */ |
| void SAL_CALL DragSource::startDrag( |
| const DragGestureEvent& trigger, |
| sal_Int8 sourceActions, |
| sal_Int32 cursor, |
| sal_Int32 image, |
| const Reference<XTransferable >& trans, |
| const Reference<XDragSourceListener >& listener ) throw( RuntimeException) |
| { |
| // Allow only one running dnd operation at a time, |
| // see XDragSource documentation |
| |
| long cnt = InterlockedIncrement(&m_RunningDndOperationCount); |
| |
| if (1 == cnt) |
| { |
| StartDragImpl(trigger, sourceActions, cursor, image, trans, listener); |
| } |
| else |
| { |
| //OSL_ENSURE(false, "Overlapping Drag&Drop operation rejected!"); |
| |
| cnt = InterlockedDecrement(&m_RunningDndOperationCount); |
| |
| DragSourceDropEvent dsde; |
| |
| dsde.DropAction = ACTION_NONE; |
| dsde.DropSuccess = false; |
| |
| try |
| { |
| listener->dragDropEnd(dsde); |
| } |
| catch(RuntimeException&) |
| { |
| OSL_ENSURE(false, "Runtime exception during event dispatching"); |
| } |
| } |
| } |
| |
| //---------------------------------------------------- |
| /**IDropTarget |
| */ |
| HRESULT STDMETHODCALLTYPE DragSource::QueryInterface( REFIID riid, void **ppvObject) |
| { |
| if( !ppvObject) |
| return E_POINTER; |
| *ppvObject= NULL; |
| |
| if( riid == __uuidof( IUnknown) ) |
| *ppvObject= static_cast<IUnknown*>( this); |
| else if ( riid == __uuidof( IDropSource) ) |
| *ppvObject= static_cast<IDropSource*>( this); |
| |
| if(*ppvObject) |
| { |
| AddRef(); |
| return S_OK; |
| } |
| else |
| return E_NOINTERFACE; |
| |
| } |
| |
| //---------------------------------------------------- |
| /** |
| */ |
| ULONG STDMETHODCALLTYPE DragSource::AddRef( void) |
| { |
| acquire(); |
| return (ULONG) m_refCount; |
| } |
| |
| //---------------------------------------------------- |
| /** |
| */ |
| ULONG STDMETHODCALLTYPE DragSource::Release( void) |
| { |
| ULONG ref= m_refCount; |
| release(); |
| return --ref; |
| } |
| |
| //---------------------------------------------------- |
| /** IDropSource |
| */ |
| HRESULT STDMETHODCALLTYPE DragSource::QueryContinueDrag( |
| /* [in] */ BOOL fEscapePressed, |
| /* [in] */ DWORD grfKeyState) |
| { |
| #if defined DBG_CONSOLE_OUT |
| printf("\nDragSource::QueryContinueDrag"); |
| #endif |
| |
| HRESULT retVal= S_OK; // default continue DnD |
| |
| if (fEscapePressed) |
| { |
| retVal= DRAGDROP_S_CANCEL; |
| } |
| else |
| { |
| if( ( m_MouseButton == MouseButton::RIGHT && !(grfKeyState & MK_RBUTTON) ) || |
| ( m_MouseButton == MouseButton::MIDDLE && !(grfKeyState & MK_MBUTTON) ) || |
| ( m_MouseButton == MouseButton::LEFT && !(grfKeyState & MK_LBUTTON) ) || |
| ( m_MouseButton == 0 && !(grfKeyState & MK_LBUTTON) ) ) |
| { |
| retVal= DRAGDROP_S_DROP; |
| } |
| } |
| |
| // fire dropActionChanged event. |
| // this is actually done by the context, which also detects whether the action |
| // changed at all |
| sal_Int8 dropAction= fEscapePressed ? ACTION_NONE : |
| dndOleKeysToAction( grfKeyState, m_sourceActions); |
| |
| sal_Int8 userAction= fEscapePressed ? ACTION_NONE : |
| dndOleKeysToAction( grfKeyState, -1 ); |
| |
| static_cast<SourceContext*>(m_currentContext.get())->fire_dropActionChanged( |
| dropAction, userAction); |
| |
| return retVal; |
| } |
| |
| //---------------------------------------------------- |
| /** |
| */ |
| HRESULT STDMETHODCALLTYPE DragSource::GiveFeedback( |
| /* [in] */ DWORD |
| #if defined DBG_CONSOLE_OUT |
| dwEffect |
| #endif |
| ) |
| { |
| #if defined DBG_CONSOLE_OUT |
| printf("\nDragSource::GiveFeedback %d", dwEffect); |
| #endif |
| |
| return DRAGDROP_S_USEDEFAULTCURSORS; |
| } |
| |
| // XServiceInfo |
| OUString SAL_CALL DragSource::getImplementationName( ) throw (RuntimeException) |
| { |
| return OUString(RTL_CONSTASCII_USTRINGPARAM(DNDSOURCE_IMPL_NAME));; |
| } |
| // XServiceInfo |
| sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName ) throw (RuntimeException) |
| { |
| if( ServiceName.equals(OUString(RTL_CONSTASCII_USTRINGPARAM(DNDSOURCE_SERVICE_NAME )))) |
| return sal_True; |
| return sal_False; |
| } |
| |
| Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames( ) throw (RuntimeException) |
| { |
| OUString names[1]= {OUString(RTL_CONSTASCII_USTRINGPARAM(DNDSOURCE_SERVICE_NAME))}; |
| |
| return Sequence<OUString>(names, 1); |
| } |
| |
| //---------------------------------------------------- |
| /**This function is called as extra thread from |
| DragSource::executeDrag. The function |
| carries out a drag and drop operation by calling |
| DoDragDrop. The thread also notifies all |
| XSourceListener. |
| */ |
| unsigned __stdcall DndOleSTAFunc(LPVOID pParams) |
| { |
| // The structure contains all arguments for DoDragDrop and other |
| DragSource *pSource= (DragSource*)pParams; |
| |
| // Drag and drop only works in a thread in which OleInitialize is called. |
| HRESULT hr= OleInitialize( NULL); |
| |
| if(SUCCEEDED(hr)) |
| { |
| // We force the creation of a thread message queue. This is necessary |
| // for a later call to AttachThreadInput |
| MSG msgtemp; |
| PeekMessage( &msgtemp, NULL, WM_USER, WM_USER, PM_NOREMOVE); |
| |
| DWORD threadId= GetCurrentThreadId(); |
| |
| // This thread is attached to the thread that created the window. Hence |
| // this thread also receives all mouse and keyboard messages which are |
| // needed by DoDragDrop |
| AttachThreadInput( threadId , pSource->m_threadIdWindow, TRUE ); |
| |
| DWORD dwEffect= 0; |
| hr= DoDragDrop( |
| pSource->m_spDataObject.get(), |
| static_cast<IDropSource*>(pSource), |
| dndActionsToDropEffects( pSource->m_sourceActions), |
| &dwEffect); |
| |
| // #105428 detach my message queue from the other threads |
| // message queue before calling fire_dragDropEnd else |
| // the office may appear to hang sometimes |
| AttachThreadInput( threadId, pSource->m_threadIdWindow, FALSE); |
| |
| //--> TRA |
| // clear the global transferable again |
| g_XTransferable = Reference< XTransferable >( ); |
| //<-- TRA |
| |
| OSL_ENSURE( hr != E_INVALIDARG, "IDataObject impl does not contain valid data"); |
| |
| //Fire event |
| sal_Int8 action= hr == DRAGDROP_S_DROP ? dndOleDropEffectsToActions( dwEffect) : ACTION_NONE; |
| |
| static_cast<SourceContext*>(pSource->m_currentContext.get())->fire_dragDropEnd( |
| hr == DRAGDROP_S_DROP ? sal_True : sal_False, action); |
| |
| // Destroy SourceContextslkfgj |
| pSource->m_currentContext= 0; |
| // Destroy the XTransferable wrapper |
| pSource->m_spDataObject=0; |
| |
| OleUninitialize(); |
| } |
| |
| InterlockedDecrement(&pSource->m_RunningDndOperationCount); |
| |
| // the DragSource was manually acquired by |
| // thread starting method DelayedStartDrag |
| pSource->release(); |
| |
| return 0; |
| } |
| |
| |
| |
| |