/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 .
*/
DropTarget::~DropTarget()
{
} // called from WeakComponentImplHelperX::dispose // WeakComponentImplHelper calls disposing before it destroys // itself. // NOTE: RevokeDragDrop decrements the ref count on the IDropTarget // interface. (m_pDropTarget) // If the HWND is invalid then it doesn't decrement and // the IDropTarget object will live on. MEMORY LEAK void SAL_CALL DropTarget::disposing()
{ if( m_threadIdTarget)
{ // Call RevokeDragDrop and wait for the OLE thread to die;
PostThreadMessageW( m_threadIdTarget, WM_REVOKEDRAGDROP, reinterpret_cast<WPARAM>(this), 0);
WaitForSingleObject( m_hOleThread, INFINITE);
CloseHandle( m_hOleThread); //OSL_ENSURE( SUCCEEDED( hr), "HWND not valid!" );
} else
{
RevokeDragDrop( m_hWnd);
m_hWnd= nullptr;
} if( m_pDropTarget)
{
CoLockObjectExternal( m_pDropTarget, FALSE, TRUE);
m_pDropTarget->Release();
m_pDropTarget = nullptr;
}
void SAL_CALL DropTarget::initialize( const Sequence< Any >& aArguments )
{ // The window must be registered for Dnd by RegisterDragDrop. We must ensure // that RegisterDragDrop is called from an STA ( OleInitialize) thread. // As long as the window is registered we need to receive OLE messages in // an OLE thread. That is to say, if DropTarget::initialize was called from an // MTA thread then we create an OLE thread in which the window is registered. // The thread will stay alive until aver RevokeDragDrop has been called.
// Additionally even if RegisterDragDrop is called from an STA thread we have // to ensure that it is called from the same thread that created the Window // otherwise messages sent during DND won't reach the windows message queue. // Calling AttachThreadInput first would resolve this problem but would block // the message queue of the calling thread. So if the current thread // (even if it's an STA thread) and the thread that created the window are not // identical we need to create a new thread as we do when the calling thread is // an MTA thread.
if( aArguments.getLength() > 0)
{ // Get the window handle from aArgument. It is needed for RegisterDragDrop.
m_hWnd= reinterpret_cast<HWND>(static_cast<sal_uIntPtr>(*o3tl::doAccess<sal_uInt64>(aArguments[0])));
OSL_ASSERT( IsWindow( m_hWnd) );
// Obtain the id of the thread that created the window
m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, nullptr);
HRESULT hr= OleInitialize( nullptr);
// Current thread is MTA or Current thread and Window thread are not identical if( hr == RPC_E_CHANGED_MODE || GetCurrentThreadId() != m_threadIdWindow )
{
OSL_ENSURE( ! m_threadIdTarget,"initialize was called twice"); // create the IDropTargetImplementation
m_pDropTarget= new IDropTargetImpl( *this );
m_pDropTarget->AddRef();
// Obtain the id of the thread that created the window
m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, nullptr); // The event is set by the thread that we will create momentarily. // It indicates that the thread is ready to receive messages.
HANDLE m_evtThreadReady= CreateEventW( nullptr, FALSE, FALSE, nullptr);
m_hOleThread = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, DndTargetOleSTAFunc,
&m_evtThreadReady, 0, &m_threadIdTarget));
WaitForSingleObject( m_evtThreadReady, INFINITE);
CloseHandle( m_evtThreadReady);
PostThreadMessageW( m_threadIdTarget, WM_REGISTERDRAGDROP, reinterpret_cast<WPARAM>(this), 0);
} elseif( hr == S_OK || hr == S_FALSE)
{ // current thread is STA // If OleInitialize has been called by the caller then we must not call // OleUninitialize if( hr == S_OK)
{ // caller did not call OleInitialize, so we call OleUninitialize // remember the thread that will call OleUninitialize
m_oleThreadId= CoGetCurrentProcess(); // get a unique thread id
}
// Get the window handle from aArgument. It is needed for RegisterDragDrop. // create the IDropTargetImplementation
m_pDropTarget= new IDropTargetImpl( *this );
m_pDropTarget->AddRef(); // CoLockObjectExternal is prescribed by the protocol. It bumps up the ref count if( SUCCEEDED( CoLockObjectExternal( m_pDropTarget, TRUE, FALSE)))
{ if( FAILED( RegisterDragDrop( m_hWnd, m_pDropTarget) ) )
{ // do clean up if drag and drop is not possible
CoLockObjectExternal( m_pDropTarget, FALSE, FALSE);
m_pDropTarget->Release();
m_pDropTarget = nullptr;
m_hWnd= nullptr;
}
}
} else throw Exception("OleInitialize failed with " + OUString::number(hr), nullptr);
}
}
// This function is called as extra thread from DragSource::startDrag. // The function carries out a drag and drop operation by calling // DoDragDrop. The thread also notifies all XSourceListener. unsigned __stdcall DndTargetOleSTAFunc(void* pParams)
{
osl_setThreadName("DropTarget DndTargetOleSTAFunc");
HRESULT hr= OleInitialize( nullptr); if( SUCCEEDED( hr) )
{
MSG msg; // force the creation of a message queue
PeekMessageW( &msg, nullptr, 0, 0, PM_NOREMOVE); // Signal the creator ( DropTarget::initialize) that the thread is // ready to receive messages.
SetEvent( *static_cast<HANDLE*>(pParams)); // Thread id is needed for attaching this message queue to the one of the // thread where the window was created.
DWORD threadId= GetCurrentThreadId(); // We force the creation of a thread message queue. This is necessary // for a later call to AttachThreadInput for (;;)
{ intconst bRet = GetMessageW(&msg, nullptr, 0, 0); if (bRet == 0)
{ break;
} if (-1 == bRet)
{
SAL_WARN("vcl.win.dtrans", "GetMessageW failed: " << comphelper::WindowsErrorString(GetLastError())); break;
} if( msg.message == WM_REGISTERDRAGDROP)
{
DropTarget *pTarget= reinterpret_cast<DropTarget*>(msg.wParam); // This thread is attached to the thread that created the window. Hence // this thread also receives all mouse and keyboard messages which are // needed
AttachThreadInput( threadId , pTarget->m_threadIdWindow, TRUE );
if( SUCCEEDED( CoLockObjectExternal(pTarget-> m_pDropTarget, TRUE, FALSE)))
{ if( FAILED( RegisterDragDrop( pTarget-> m_hWnd, pTarget-> m_pDropTarget) ) )
{ // do clean up if drag and drop is not possible
CoLockObjectExternal( pTarget->m_pDropTarget, FALSE, FALSE);
pTarget->m_pDropTarget->Release();
pTarget->m_pDropTarget = nullptr;
pTarget->m_hWnd= nullptr;
}
}
} elseif( msg.message == WM_REVOKEDRAGDROP)
{
DropTarget *pTarget= reinterpret_cast<DropTarget*>(msg.wParam);
RevokeDragDrop( pTarget-> m_hWnd); // Detach this thread from the window thread
AttachThreadInput( threadId, pTarget->m_threadIdWindow, FALSE);
pTarget->m_hWnd= nullptr; break;
}
TranslateMessage( &msg);
DispatchMessageW( &msg);
}
OleUninitialize();
} return 0;
}
HRESULT DropTarget::DragEnter( IDataObject *pDataObj,
DWORD grfKeyState,
POINTL pt,
DWORD *pdwEffect)
{ #ifdefined DBG_CONSOLE_OUT
printf("\nDropTarget::DragEnter state: %x effect %d", grfKeyState, *pdwEffect); #endif if( m_bActive )
{ // Intersection of pdwEffect and the allowed actions ( setDefaultActions)
m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect); // m_nLastDropAction has to be set by a listener. If no listener calls //XDropTargetDragContext::acceptDrag and specifies an action then pdwEffect // will be DROPEFFECT_NONE throughout
m_nLastDropAction= ACTION_DEFAULT | ACTION_MOVE;
m_currentDragContext = new TargetDragContext(this);
//--> TRA
// shortcut if ( g_XTransferable.is( ) )
m_currentData = g_XTransferable; else
{ // Convert the IDataObject to a XTransferable
m_currentData = new CDOTransferable(m_xContext, IDataObjectPtr(pDataObj));
}
fire_dragEnter( e); // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set // by the listener (m_nCurrentDropAction) is allowed by the source. Only an allowed action is set // in pdwEffect. The listener notification is asynchronous, that is we cannot expect that the listener // has already reacted to the notification. // If there is more than one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed. // On drop the target should present the user a dialog from which the user may change the action.
sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
*pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions);
} else
{
*pdwEffect= DROPEFFECT_NONE;
}
} return S_OK;
}
// if grfKeyState has changed since the last DragOver then fire events. // A listener might change m_nCurrentDropAction by calling the // XDropTargetDragContext::acceptDrag function. But this is not important // because in the afterwards fired dragOver event the action reflects // grgKeyState again. if( m_nLastDropAction != m_nCurrentDropAction)
fire_dropActionChanged( e);
// The Event contains a XDropTargetDragContext implementation.
fire_dragOver( e); // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set // by the listener (m_nCurrentDropAction) is allowed by the source. Only an allowed action is set // in pdwEffect. The listener notification is asynchronous, that is we cannot expect that the listener // has already reacted to the notification. // If there is more than one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed. // On drop the target should present the user a dialog from which the user may change the action.
sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect); // set the last action to the current if listener has not changed the value yet
*pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions);
} else
{
*pdwEffect= DROPEFFECT_NONE;
}
} #ifdefined DBG_CONSOLE_OUT
printf("\nDropTarget::DragOver %d", *pdwEffect ); #endif return S_OK;
}
// Non - interface functions // DropTarget fires events to XDropTargetListeners. The event object contains an // XDropTargetDropContext implementation. When the listener calls on that interface // then the calls are delegated from DropContext (XDropTargetDropContext) to these // functions. // Only one listener which visible area is affected is allowed to call on // XDropTargetDropContext // Returning sal_False would cause the XDropTargetDropContext or ..DragContext implementation // to throw an InvalidDNDOperationException, meaning that a Drag is not currently performed. // return sal_False results in throwing an InvalidDNDOperationException in the caller.
// DropTarget fires events to XDropTargetListeners. The event object can contains an // XDropTargetDragContext implementation. When the listener calls on that interface // then the calls are delegated from DragContext (XDropTargetDragContext) to these // functions. // Only one listener which visible area is affected is allowed to call on // XDropTargetDragContext void DropTarget::_acceptDrag( sal_Int8 dragOperation, const Reference<XDropTargetDragContext>& context)
{ if( context == m_currentDragContext)
{
m_nLastDropAction= dragOperation;
}
}
// This function determines the action dependent on the pressed // key modifiers ( CTRL, SHIFT, ALT, Right Mouse Button). The result // is then checked against the allowed actions which can be set through // XDropTarget::setDefaultActions. Only those values which are also // default actions are returned. If setDefaultActions has not been called // beforehand the default actions comprise all possible actions. // params: grfKeyState - the modifier keys and mouse buttons currently pressed inline sal_Int8 DropTarget::getFilteredActions( DWORD grfKeyState, DWORD dwEffect)
{
sal_Int8 actions= dndOleKeysToAction( grfKeyState, dndOleDropEffectsToActions( dwEffect)); return actions & m_nDefaultActions;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.