Coverage Report

Created: 2026-03-31 11:00

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