1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <dndeventdispatcher.hxx>
21 #include <dndlistenercontainer.hxx>
22 #include <sal/log.hxx>
23 
24 #include <osl/mutex.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/settings.hxx>
27 
28 using namespace ::cppu;
29 using namespace ::com::sun::star::uno;
30 using namespace ::com::sun::star::lang;
31 using namespace ::com::sun::star::datatransfer;
32 using namespace ::com::sun::star::datatransfer::dnd;
33 
DNDEventDispatcher(vcl::Window * pTopWindow)34 DNDEventDispatcher::DNDEventDispatcher( vcl::Window * pTopWindow ):
35     m_pTopWindow( pTopWindow ),
36     m_pCurrentWindow( nullptr )
37 {
38 }
39 
~DNDEventDispatcher()40 DNDEventDispatcher::~DNDEventDispatcher()
41 {
42     designate_currentwindow(nullptr);
43 }
44 
findTopLevelWindow(Point location)45 vcl::Window* DNDEventDispatcher::findTopLevelWindow(Point location)
46 {
47     SolarMutexGuard aSolarGuard;
48 
49     // find the window that is toplevel for this coordinates
50     // because those coordinates come from outside, they must be mirrored if RTL layout is active
51     if( AllSettings::GetLayoutRTL() )
52         m_pTopWindow->ImplMirrorFramePos( location );
53     vcl::Window * pChildWindow = m_pTopWindow->ImplFindWindow( location );
54 
55     if( nullptr == pChildWindow )
56         pChildWindow = m_pTopWindow;
57 
58     while( pChildWindow->ImplGetClientWindow() )
59         pChildWindow = pChildWindow->ImplGetClientWindow();
60 
61     if( pChildWindow->GetOutDev()->ImplIsAntiparallel() )
62     {
63         const OutputDevice *pChildWinOutDev = pChildWindow->GetOutDev();
64         pChildWinOutDev->ReMirror( location );
65     }
66 
67     return pChildWindow;
68 }
69 
IMPL_LINK(DNDEventDispatcher,WindowEventListener,VclWindowEvent &,rEvent,void)70 IMPL_LINK(DNDEventDispatcher, WindowEventListener, VclWindowEvent&, rEvent, void)
71 {
72     if (rEvent.GetId() == VclEventId::ObjectDying)
73     {
74         designate_currentwindow(nullptr);
75     }
76 }
77 
designate_currentwindow(vcl::Window * pWindow)78 void DNDEventDispatcher::designate_currentwindow(vcl::Window *pWindow)
79 {
80     if (m_pCurrentWindow)
81         m_pCurrentWindow->RemoveEventListener(LINK(this, DNDEventDispatcher, WindowEventListener));
82     m_pCurrentWindow = pWindow;
83     if (m_pCurrentWindow)
84         m_pCurrentWindow->AddEventListener(LINK(this, DNDEventDispatcher, WindowEventListener));
85 }
86 
drop(const DropTargetDropEvent & dtde)87 void SAL_CALL DNDEventDispatcher::drop( const DropTargetDropEvent& dtde )
88 {
89     osl::MutexGuard aImplGuard( m_aMutex );
90 
91     Point location( dtde.LocationX, dtde.LocationY );
92 
93     vcl::Window* pChildWindow = findTopLevelWindow(location);
94 
95     // handle the case that drop is in another vcl window than the last dragOver
96     if( pChildWindow != m_pCurrentWindow.get() )
97     {
98         // fire dragExit on listeners of previous window
99         fireDragExitEvent( m_pCurrentWindow );
100 
101         fireDragEnterEvent( pChildWindow, static_cast < XDropTargetDragContext * > (this),
102             dtde.DropAction, location, dtde.SourceActions, m_aDataFlavorList );
103     }
104 
105     // send drop event to the child window
106     sal_Int32 nListeners = fireDropEvent( pChildWindow, dtde.Context, dtde.DropAction,
107         location, dtde.SourceActions, dtde.Transferable );
108 
109     // reject drop if no listeners found
110     if( nListeners == 0 ) {
111         SAL_WARN( "vcl", "rejecting drop due to missing listeners." );
112         dtde.Context->rejectDrop();
113     }
114 
115     // this is a drop -> no further drag overs
116     designate_currentwindow(nullptr);
117     m_aDataFlavorList.realloc( 0 );
118 }
119 
dragEnter(const DropTargetDragEnterEvent & dtdee)120 void SAL_CALL DNDEventDispatcher::dragEnter( const DropTargetDragEnterEvent& dtdee )
121 {
122     osl::MutexGuard aImplGuard( m_aMutex );
123     Point location( dtdee.LocationX, dtdee.LocationY );
124 
125     vcl::Window * pChildWindow = findTopLevelWindow(location);
126 
127     // assume pointer write operation to be atomic
128     designate_currentwindow(pChildWindow);
129     m_aDataFlavorList = dtdee.SupportedDataFlavors;
130 
131     // fire dragEnter on listeners of current window
132     sal_Int32 nListeners = fireDragEnterEvent( pChildWindow, dtdee.Context, dtdee.DropAction, location,
133         dtdee.SourceActions, dtdee.SupportedDataFlavors );
134 
135     // reject drag if no listener found
136     if( nListeners == 0 ) {
137         SAL_WARN( "vcl", "rejecting drag enter due to missing listeners." );
138         dtdee.Context->rejectDrag();
139     }
140 
141 }
142 
dragExit(const DropTargetEvent &)143 void SAL_CALL DNDEventDispatcher::dragExit( const DropTargetEvent& /*dte*/ )
144 {
145     osl::MutexGuard aImplGuard( m_aMutex );
146 
147     fireDragExitEvent( m_pCurrentWindow );
148 
149     // reset member values
150     designate_currentwindow(nullptr);
151     m_aDataFlavorList.realloc( 0 );
152 }
153 
dragOver(const DropTargetDragEvent & dtde)154 void SAL_CALL DNDEventDispatcher::dragOver( const DropTargetDragEvent& dtde )
155 {
156     osl::MutexGuard aImplGuard( m_aMutex );
157 
158     Point location( dtde.LocationX, dtde.LocationY );
159     sal_Int32 nListeners;
160 
161     vcl::Window * pChildWindow = findTopLevelWindow(location);
162 
163     if( pChildWindow != m_pCurrentWindow.get() )
164     {
165         // fire dragExit on listeners of previous window
166         fireDragExitEvent( m_pCurrentWindow );
167 
168         // remember new window
169         designate_currentwindow(pChildWindow);
170 
171         // fire dragEnter on listeners of current window
172         nListeners = fireDragEnterEvent( pChildWindow, dtde.Context, dtde.DropAction, location,
173             dtde.SourceActions, m_aDataFlavorList );
174     }
175     else
176     {
177         // fire dragOver on listeners of current window
178         nListeners = fireDragOverEvent( pChildWindow, dtde.Context, dtde.DropAction, location,
179             dtde.SourceActions );
180     }
181 
182     // reject drag if no listener found
183     if( nListeners == 0 )
184     {
185         SAL_WARN( "vcl", "rejecting drag over due to missing listeners." );
186         dtde.Context->rejectDrag();
187     }
188 }
189 
dropActionChanged(const DropTargetDragEvent & dtde)190 void SAL_CALL DNDEventDispatcher::dropActionChanged( const DropTargetDragEvent& dtde )
191 {
192     osl::MutexGuard aImplGuard( m_aMutex );
193 
194     Point location( dtde.LocationX, dtde.LocationY );
195     sal_Int32 nListeners;
196 
197     vcl::Window* pChildWindow = findTopLevelWindow(location);
198 
199     if( pChildWindow != m_pCurrentWindow.get() )
200     {
201         // fire dragExit on listeners of previous window
202         fireDragExitEvent( m_pCurrentWindow );
203 
204         // remember new window
205         designate_currentwindow(pChildWindow);
206 
207         // fire dragEnter on listeners of current window
208         nListeners = fireDragEnterEvent( pChildWindow, dtde.Context, dtde.DropAction, location,
209             dtde.SourceActions, m_aDataFlavorList );
210     }
211     else
212     {
213         // fire dropActionChanged on listeners of current window
214         nListeners = fireDropActionChangedEvent( pChildWindow, dtde.Context, dtde.DropAction, location,
215             dtde.SourceActions );
216     }
217 
218     // reject drag if no listener found
219     if( nListeners == 0 )
220     {
221         SAL_WARN( "vcl", "rejecting dropActionChanged due to missing listeners." );
222         dtde.Context->rejectDrag();
223     }
224 }
225 
dragGestureRecognized(const DragGestureEvent & dge)226 void SAL_CALL DNDEventDispatcher::dragGestureRecognized( const DragGestureEvent& dge )
227 {
228     osl::MutexGuard aImplGuard( m_aMutex );
229 
230     Point origin( dge.DragOriginX, dge.DragOriginY );
231 
232     vcl::Window* pChildWindow = findTopLevelWindow(origin);
233 
234     fireDragGestureEvent( pChildWindow, dge.DragSource, dge.Event, origin, dge.DragAction );
235 }
236 
disposing(const EventObject &)237 void SAL_CALL DNDEventDispatcher::disposing( const EventObject& )
238 {
239 }
240 
acceptDrag(sal_Int8)241 void SAL_CALL DNDEventDispatcher::acceptDrag( sal_Int8 /*dropAction*/ )
242 {
243 }
244 
rejectDrag()245 void SAL_CALL DNDEventDispatcher::rejectDrag()
246 {
247 }
248 
fireDragEnterEvent(vcl::Window * pWindow,const Reference<XDropTargetDragContext> & xContext,const sal_Int8 nDropAction,const Point & rLocation,const sal_Int8 nSourceActions,const Sequence<DataFlavor> & aFlavorList)249 sal_Int32 DNDEventDispatcher::fireDragEnterEvent( vcl::Window *pWindow,
250     const Reference< XDropTargetDragContext >& xContext, const sal_Int8 nDropAction,
251     const Point& rLocation, const sal_Int8 nSourceActions, const Sequence< DataFlavor >& aFlavorList
252 )
253 {
254     sal_Int32 n = 0;
255 
256     if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
257     {
258         SolarMutexClearableGuard aSolarGuard;
259 
260         // query DropTarget from window
261         Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
262 
263         if( xDropTarget.is() )
264         {
265             // retrieve relative mouse position
266             Point relLoc = pWindow->ImplFrameToOutput( rLocation );
267             aSolarGuard.clear();
268 
269             n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDragEnterEvent(
270                 xContext, nDropAction, relLoc.X(), relLoc.Y(), nSourceActions, aFlavorList );
271         }
272     }
273 
274     return n;
275 }
276 
fireDragOverEvent(vcl::Window * pWindow,const Reference<XDropTargetDragContext> & xContext,const sal_Int8 nDropAction,const Point & rLocation,const sal_Int8 nSourceActions)277 sal_Int32 DNDEventDispatcher::fireDragOverEvent( vcl::Window *pWindow,
278     const Reference< XDropTargetDragContext >& xContext, const sal_Int8 nDropAction,
279     const Point& rLocation, const sal_Int8 nSourceActions
280 )
281 {
282     sal_Int32 n = 0;
283 
284     if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
285     {
286         SolarMutexClearableGuard aSolarGuard;
287 
288         // query DropTarget from window
289         Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
290 
291         if( xDropTarget.is() )
292         {
293             // retrieve relative mouse position
294             Point relLoc = pWindow->ImplFrameToOutput( rLocation );
295             aSolarGuard.clear();
296 
297             n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDragOverEvent(
298                 xContext, nDropAction, relLoc.X(), relLoc.Y(), nSourceActions );
299         }
300     }
301 
302     return n;
303 }
304 
fireDragExitEvent(vcl::Window * pWindow)305 sal_Int32 DNDEventDispatcher::fireDragExitEvent( vcl::Window *pWindow )
306 {
307     sal_Int32 n = 0;
308 
309     if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
310     {
311         SolarMutexClearableGuard aGuard;
312 
313         // query DropTarget from window
314         Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
315 
316         aGuard.clear();
317 
318         if( xDropTarget.is() )
319             n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDragExitEvent();
320     }
321 
322     return n;
323 }
324 
fireDropActionChangedEvent(vcl::Window * pWindow,const Reference<XDropTargetDragContext> & xContext,const sal_Int8 nDropAction,const Point & rLocation,const sal_Int8 nSourceActions)325 sal_Int32 DNDEventDispatcher::fireDropActionChangedEvent( vcl::Window *pWindow,
326     const Reference< XDropTargetDragContext >& xContext, const sal_Int8 nDropAction,
327     const Point& rLocation, const sal_Int8 nSourceActions
328 )
329 {
330     sal_Int32 n = 0;
331 
332     if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
333     {
334         SolarMutexClearableGuard aGuard;
335 
336         // query DropTarget from window
337         Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
338 
339         if( xDropTarget.is() )
340         {
341             // retrieve relative mouse position
342             Point relLoc = pWindow->ImplFrameToOutput( rLocation );
343             aGuard.clear();
344 
345             n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDropActionChangedEvent(
346                 xContext, nDropAction, relLoc.X(), relLoc.Y(), nSourceActions );
347         }
348     }
349 
350     return n;
351 }
352 
fireDropEvent(vcl::Window * pWindow,const Reference<XDropTargetDropContext> & xContext,const sal_Int8 nDropAction,const Point & rLocation,const sal_Int8 nSourceActions,const Reference<XTransferable> & xTransferable)353 sal_Int32 DNDEventDispatcher::fireDropEvent( vcl::Window *pWindow,
354     const Reference< XDropTargetDropContext >& xContext, const sal_Int8 nDropAction, const Point& rLocation,
355     const sal_Int8 nSourceActions, const Reference< XTransferable >& xTransferable
356 )
357 {
358     sal_Int32 n = 0;
359 
360     if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
361     {
362         SolarMutexClearableGuard aGuard;
363 
364         // query DropTarget from window
365         Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
366 
367         // window may be destroyed in drop event handler
368         VclPtr<vcl::Window> xPreventDelete = pWindow;
369 
370         if( xDropTarget.is() )
371         {
372             // retrieve relative mouse position
373             Point relLoc = pWindow->ImplFrameToOutput( rLocation );
374             aGuard.clear();
375 
376             n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDropEvent(
377                 xContext, nDropAction, relLoc.X(), relLoc.Y(), nSourceActions, xTransferable );
378         }
379     }
380 
381     return n;
382 }
383 
fireDragGestureEvent(vcl::Window * pWindow,const Reference<XDragSource> & xSource,const Any & event,const Point & rOrigin,const sal_Int8 nDragAction)384 sal_Int32 DNDEventDispatcher::fireDragGestureEvent( vcl::Window *pWindow,
385     const Reference< XDragSource >& xSource, const Any& event,
386     const Point& rOrigin, const sal_Int8 nDragAction
387 )
388 {
389     sal_Int32 n = 0;
390 
391     if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
392     {
393         SolarMutexClearableGuard aGuard;
394 
395         // query DropTarget from window
396         Reference< XDragGestureRecognizer > xDragGestureRecognizer = pWindow->GetDragGestureRecognizer();
397 
398         if( xDragGestureRecognizer.is() )
399         {
400             // retrieve relative mouse position
401             Point relLoc = pWindow->ImplFrameToOutput( rOrigin );
402             aGuard.clear();
403 
404             n = static_cast < DNDListenerContainer * > ( xDragGestureRecognizer.get() )->fireDragGestureEvent(
405                 nDragAction, relLoc.X(), relLoc.Y(), xSource, event );
406         }
407     }
408 
409     return n;
410 }
411 
412 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
413