Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sw/source/uibase/docvw/edtdd.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 <svx/svdview.hxx>
21
#include <editeng/outliner.hxx>
22
#include <svx/svdobj.hxx>
23
#include <sot/exchange.hxx>
24
#include <sot/formats.hxx>
25
#include <sfx2/bindings.hxx>
26
#include <vcl/commandevent.hxx>
27
#include <osl/diagnose.h>
28
29
#include <sfx2/viewfrm.hxx>
30
#include <fmturl.hxx>
31
#include <frmfmt.hxx>
32
#include <wrtsh.hxx>
33
#include <edtdd.hxx>
34
#include <edtwin.hxx>
35
#include <view.hxx>
36
#include <viewopt.hxx>
37
#include <swdtflvr.hxx>
38
#include <swmodule.hxx>
39
#include <docsh.hxx>
40
#include <wdocsh.hxx>
41
42
using namespace ::com::sun::star;
43
44
// no include "dbgoutsw.hxx" here!!!!!!
45
46
bool g_bExecuteDrag = false;
47
48
void SwEditWin::StartDDTimer()
49
0
{
50
0
    m_aTimer.SetInvokeHandler(LINK(this, SwEditWin, DDHandler));
51
0
    m_aTimer.SetTimeout(480);
52
0
    m_aTimer.Start();
53
0
    g_bDDTimerStarted = true;
54
0
}
55
56
void SwEditWin::StopDDTimer(SwWrtShell *pSh, const Point &rPt)
57
0
{
58
0
    m_aTimer.Stop();
59
0
    g_bDDTimerStarted = false;
60
0
    if(!pSh->IsSelFrameMode())
61
0
        pSh->CallSetCursor(&rPt, false);
62
0
    m_aTimer.SetInvokeHandler(LINK(this,SwEditWin, TimerHandler));
63
0
}
64
65
void SwEditWin::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
66
0
{
67
0
    if (m_rView.GetObjectShell()->isContentExtractionLocked()
68
0
        || !SwViewOption::IsAllowDragDropText())
69
0
        return;
70
71
0
    SwWrtShell &rSh = m_rView.GetWrtShell();
72
0
    if( rSh.GetDrawView() )
73
0
    {
74
0
        CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true );
75
0
        if( rSh.GetDrawView()->Command( aDragEvent, this ) )
76
0
        {
77
0
            m_rView.GetViewFrame().GetBindings().InvalidateAll(false);
78
0
            return; // Event evaluated by SdrView
79
0
        }
80
0
    }
81
82
0
    if ( m_pApplyTempl || rSh.IsDrawCreate() || IsDrawAction())
83
0
        return;
84
85
0
    bool bStartDragging = false, bDelSelect = false;
86
0
    SdrObject *pObj = nullptr;
87
0
    Point aDocPos( PixelToLogic( rPosPixel ) );
88
0
    const bool bInSelect = rSh.IsInSelect();
89
0
    if (!bInSelect && rSh.TestCurrPam(aDocPos, true))
90
        //We are not selecting and aren't at a selection
91
0
        bStartDragging = true;
92
0
    else if ( !g_bFrameDrag && rSh.IsSelFrameMode() &&
93
0
                rSh.IsInsideSelectedObj( aDocPos ) &&
94
0
                nullptr == m_pAnchorMarker)
95
0
    {
96
        //We are not dragging internally and are not at an
97
        //object (frame, draw object)
98
99
        // #i106131# *and* AnchorDrag is *not* active: When active,
100
        // entering global drag mode will destroy the AnchorHdl but
101
        // keep the now invalid ptr in place, next access will crash.
102
        // It is indeed wrong to enter drag mode when AnchorDrag is
103
        // already active
104
0
        bStartDragging = true;
105
0
    }
106
0
    else if( !g_bFrameDrag && m_rView.GetDocShell()->IsReadOnly() &&
107
0
            OBJCNT_NONE != rSh.GetObjCntType( aDocPos, pObj ))
108
0
    {
109
0
        rSh.LockPaint(LockPaintReason::StartDrag);
110
0
        if( rSh.SelectObj( aDocPos, 0, pObj ))
111
0
            bStartDragging = bDelSelect = true;
112
0
        else
113
0
            rSh.UnlockPaint();
114
0
    }
115
0
    else if (!bInSelect)// tdf#116384 only drag hyperlink if not currently setting the selection
116
0
    {
117
        // Prefer starting a selection (rather than a drag) when at the start/end of a hyperlink.
118
119
        // Use the cursor point, not the mouse point, to determine whether this is at the start/end.
120
0
        Point aCursorPoint = rSh.GetCharRect().Center();
121
122
0
        SwContentAtPos aSwContentAtPos( IsAttrAtPos::InetAttr );
123
0
        bStartDragging = rSh.GetContentAtPos(aCursorPoint, aSwContentAtPos);
124
125
        // If true (cursor is in hyperlink), perhaps the cursor is at the start of the hyperlink?
126
0
        if (bStartDragging)
127
0
        {
128
0
            aCursorPoint.AdjustX(-1);
129
0
            aSwContentAtPos = SwContentAtPos(IsAttrAtPos::InetAttr);
130
0
            bStartDragging = rSh.GetContentAtPos(aCursorPoint, aSwContentAtPos);
131
0
        }
132
0
    }
133
134
0
    if ( !bStartDragging || m_bIsInDrag )
135
0
        return;
136
137
    // If the add selection mode has been pushed in the MouseButtonDown handler it needs to be
138
    // popped or it will remain active and noticeable in the statusbar selection control until the
139
    // next MouseButtonUp event after the DnD, since a MouseButtonUp event is not received by the
140
    // edit window when DnD is done.
141
0
    if (g_bModePushed)
142
0
    {
143
0
        rSh.PopMode();
144
0
        g_bModePushed = false;
145
0
    }
146
147
0
    m_bMBPressed = false;
148
0
    ReleaseMouse();
149
0
    g_bFrameDrag = false;
150
0
    g_bExecuteDrag = true;
151
0
    SwEditWin::s_nDDStartPosY = aDocPos.Y();
152
0
    SwEditWin::s_nDDStartPosX = aDocPos.X();
153
0
    m_aMovePos = aDocPos;
154
0
    StartExecuteDrag();
155
0
    if( bDelSelect )
156
0
    {
157
0
        rSh.UnSelectFrame();
158
0
        rSh.UnlockPaint();
159
0
    }
160
0
}
161
162
void SwEditWin::StartExecuteDrag()
163
0
{
164
0
    if( !g_bExecuteDrag || m_bIsInDrag )
165
0
        return;
166
167
0
    m_bIsInDrag = true;
168
169
0
    rtl::Reference<SwTransferable> pTransfer = new SwTransferable( m_rView.GetWrtShell() );
170
171
0
    pTransfer->StartDrag( this, m_aMovePos );
172
0
}
173
174
void SwEditWin::DragFinished()
175
0
{
176
0
    DropCleanup();
177
0
    m_aTimer.SetInvokeHandler( LINK(this,SwEditWin, TimerHandler) );
178
0
    m_bIsInDrag = false;
179
0
}
180
181
void SwEditWin::DropCleanup()
182
0
{
183
0
    SwWrtShell &rSh =  m_rView.GetWrtShell();
184
185
    // reset statuses
186
0
    g_bNoInterrupt = false;
187
0
    if ( m_bOldIdleSet )
188
0
    {
189
0
        rSh.GetViewOptions()->SetIdle( m_bOldIdle );
190
0
        m_bOldIdleSet = false;
191
0
    }
192
0
    if ( m_pUserMarker )
193
0
        CleanupDropUserMarker();
194
0
    else
195
0
        rSh.UnSetVisibleCursor();
196
197
0
}
198
199
void SwEditWin::CleanupDropUserMarker()
200
0
{
201
0
    if ( m_pUserMarker )
202
0
    {
203
0
        m_pUserMarker.reset();
204
0
        m_pUserMarkerObj = nullptr;
205
0
    }
206
0
}
207
208
//exhibition hack (MA,MBA)
209
void SwView::SelectShellForDrop()
210
0
{
211
0
    if ( !GetCurShell() )
212
0
        SelectShell();
213
0
}
214
215
sal_Int8 SwEditWin::ExecuteDrop( const ExecuteDropEvent& rEvt )
216
0
{
217
0
    GetView().SelectShellForDrop();
218
0
    DropCleanup();
219
0
    sal_Int8 nRet = DND_ACTION_NONE;
220
221
    //A Drop to an open OutlinerView doesn't concern us (also see QueryDrop)
222
0
    SwWrtShell &rSh = m_rView.GetWrtShell();
223
0
    const Point aDocPt( PixelToLogic( rEvt.maPosPixel ));
224
0
    SdrObject *pObj = nullptr;
225
0
    OutlinerView* pOLV;
226
0
    rSh.GetObjCntType( aDocPt, pObj );
227
228
0
    if( pObj && nullptr != ( pOLV = rSh.GetDrawView()->GetTextEditOutlinerView() ))
229
0
    {
230
0
        tools::Rectangle aRect( pOLV->GetOutputArea() );
231
0
        aRect.Union( pObj->GetLogicRect() );
232
0
        const Point aPos = pOLV->GetWindow()->PixelToLogic(rEvt.maPosPixel);
233
0
        if ( aRect.Contains(aPos) )
234
0
        {
235
0
            rSh.StartAllAction();
236
0
            rSh.EndAllAction();
237
0
            return nRet;
238
0
        }
239
0
    }
240
241
    // There's a special treatment for file lists with a single
242
    // element, that depends on the actual content of the
243
    // Transferable to be accessible. Since the transferable
244
    // may only be accessed after the drop has been accepted
245
    // (according to KA due to Java D&D), we'll have to
246
    // reevaluate the drop action once more _with_ the
247
    // Transferable.
248
0
    sal_uInt8 nEventAction;
249
0
    sal_Int8 nUserOpt = rEvt.mbDefault ? EXCHG_IN_ACTION_DEFAULT
250
0
                                       : rEvt.mnAction;
251
0
    SotExchangeActionFlags nActionFlags;
252
0
    m_nDropAction = SotExchange::GetExchangeAction(
253
0
                                GetDataFlavorExVector(),
254
0
                                m_nDropDestination,
255
0
                                rEvt.mnAction,
256
0
                                nUserOpt, m_nDropFormat, nEventAction, SotClipboardFormatId::NONE,
257
0
                                &rEvt.maDropEvent.Transferable,
258
0
                                &nActionFlags );
259
260
0
    TransferableDataHelper aData( rEvt.maDropEvent.Transferable );
261
0
    nRet = rEvt.mnAction;
262
0
    if( !SwTransferable::PasteData( aData, rSh, m_nDropAction, nActionFlags, m_nDropFormat,
263
0
                                m_nDropDestination, false, rEvt.mbDefault, &aDocPt, nRet))
264
0
        nRet = DND_ACTION_NONE;
265
0
    else if (SwModule* mod = SwModule::get(); mod->m_pDragDrop)
266
        //Don't clean up anymore at internal D&D!
267
0
        mod->m_pDragDrop->SetCleanUp(false);
268
269
0
    return nRet;
270
0
}
271
272
SotExchangeDest SwEditWin::GetDropDestination( const Point& rPixPnt, SdrObject ** ppObj )
273
0
{
274
0
    SwWrtShell &rSh = m_rView.GetWrtShell();
275
0
    const Point aDocPt( PixelToLogic( rPixPnt ) );
276
0
    if (rSh.IsOverReadOnlyPos(aDocPt) || rSh.DocPtInsideInputField(aDocPt))
277
0
        return SotExchangeDest::NONE;
278
279
0
    SdrObject *pObj = nullptr;
280
0
    const ObjCntType eType = rSh.GetObjCntType( aDocPt, pObj );
281
282
    //Drop to OutlinerView (TextEdit in Drawing) should decide it on its own!
283
0
    if( pObj )
284
0
    {
285
0
        OutlinerView* pOLV = rSh.GetDrawView()->GetTextEditOutlinerView();
286
0
        if ( pOLV )
287
0
        {
288
0
            tools::Rectangle aRect( pOLV->GetOutputArea() );
289
0
            aRect.Union( pObj->GetLogicRect() );
290
0
            const Point aPos = pOLV->GetWindow()->PixelToLogic( rPixPnt );
291
0
            if( aRect.Contains( aPos ) )
292
0
                return SotExchangeDest::NONE;
293
0
        }
294
0
    }
295
296
    //What do we want to drop on now?
297
0
    SotExchangeDest nDropDestination = SotExchangeDest::NONE;
298
299
    //Did anything else arrive from the DrawingEngine?
300
0
    if( OBJCNT_NONE != eType )
301
0
    {
302
0
        switch ( eType )
303
0
        {
304
0
        case OBJCNT_GRF:
305
0
            {
306
0
                bool bLink,
307
0
                    bIMap = nullptr != rSh.GetFormatFromObj( aDocPt )->GetURL().GetMap();
308
0
                OUString aDummy;
309
0
                rSh.GetGrfAtPos( aDocPt, aDummy, bLink );
310
0
                if ( bLink && bIMap )
311
0
                    nDropDestination = SotExchangeDest::DOC_LNKD_GRAPH_W_IMAP;
312
0
                else if ( bLink )
313
0
                    nDropDestination = SotExchangeDest::DOC_LNKD_GRAPHOBJ;
314
0
                else if ( bIMap )
315
0
                    nDropDestination = SotExchangeDest::DOC_GRAPH_W_IMAP;
316
0
                else
317
0
                    nDropDestination = SotExchangeDest::DOC_GRAPHOBJ;
318
0
            }
319
0
            break;
320
0
        case OBJCNT_FLY:
321
0
            if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr  )
322
0
                nDropDestination = SotExchangeDest::DOC_TEXTFRAME_WEB;
323
0
            else
324
0
                nDropDestination = SotExchangeDest::DOC_TEXTFRAME;
325
0
            break;
326
0
        case OBJCNT_OLE:        nDropDestination = SotExchangeDest::DOC_OLEOBJ; break;
327
0
        case OBJCNT_CONTROL:    /* no Action avail */
328
0
        case OBJCNT_SIMPLE:     nDropDestination = SotExchangeDest::DOC_DRAWOBJ; break;
329
0
        case OBJCNT_URLBUTTON:  nDropDestination = SotExchangeDest::DOC_URLBUTTON; break;
330
0
        case OBJCNT_GROUPOBJ:   nDropDestination = SotExchangeDest::DOC_GROUPOBJ;     break;
331
332
0
        default: OSL_ENSURE( false, "new ObjectType?" );
333
0
        }
334
0
    }
335
0
    if ( nDropDestination == SotExchangeDest::NONE )
336
0
    {
337
0
        if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr  )
338
0
            nDropDestination = SotExchangeDest::SWDOC_FREE_AREA_WEB;
339
0
        else
340
0
            nDropDestination = SotExchangeDest::SWDOC_FREE_AREA;
341
0
    }
342
0
    if( ppObj )
343
0
        *ppObj = pObj;
344
0
    return nDropDestination;
345
0
}
346
347
sal_Int8 SwEditWin::AcceptDrop( const AcceptDropEvent& rEvt )
348
0
{
349
0
    if( rEvt.mbLeaving )
350
0
    {
351
0
        DropCleanup();
352
0
        return rEvt.mnAction;
353
0
    }
354
355
0
    if( m_rView.GetDocShell()->IsReadOnly() )
356
0
        return DND_ACTION_NONE;
357
358
0
    SwWrtShell &rSh = m_rView.GetWrtShell();
359
360
0
    Point aPixPt( rEvt.maPosPixel );
361
362
    // If the cursor is near the inner boundary
363
    // we attempt to scroll towards the desired direction.
364
0
    tools::Rectangle aWin(Point(), GetOutputSizePixel());
365
0
    const int nMargin = 10;
366
0
    aWin.AdjustLeft(nMargin );
367
0
    aWin.AdjustTop(nMargin );
368
0
    aWin.AdjustRight( -nMargin );
369
0
    aWin.AdjustBottom( -nMargin );
370
0
    if(!aWin.Contains(aPixPt)) {
371
0
        static sal_uInt64 last_tick = 0;
372
0
        sal_uInt64 current_tick = tools::Time::GetSystemTicks();
373
0
        if((current_tick-last_tick) > 500) {
374
0
            last_tick = current_tick;
375
0
            if(!m_bOldIdleSet) {
376
0
                m_bOldIdle = rSh.GetViewOptions()->IsIdle();
377
0
                rSh.GetViewOptions()->SetIdle(false);
378
0
                m_bOldIdleSet = true;
379
0
            }
380
0
            CleanupDropUserMarker();
381
0
            if(aPixPt.X() > aWin.Right()) aPixPt.AdjustX(nMargin );
382
0
            if(aPixPt.X() < aWin.Left()) aPixPt.AdjustX( -nMargin );
383
0
            if(aPixPt.Y() > aWin.Bottom()) aPixPt.AdjustY(nMargin );
384
0
            if(aPixPt.Y() < aWin.Top()) aPixPt.AdjustY( -nMargin );
385
0
            Point aDocPt(PixelToLogic(aPixPt));
386
0
            SwRect rect(aDocPt,Size(1,1));
387
0
            rSh.MakeVisible(rect, ScrollSizeMode::ScrollSizeTimer2);
388
0
        }
389
0
    }
390
391
0
    if(m_bOldIdleSet) {
392
0
        rSh.GetViewOptions()->SetIdle( m_bOldIdle );
393
0
        m_bOldIdleSet = false;
394
0
    }
395
396
0
    SdrObject *pObj = nullptr;
397
0
    m_nDropDestination = GetDropDestination( aPixPt, &pObj );
398
0
    if( m_nDropDestination == SotExchangeDest::NONE )
399
0
        return DND_ACTION_NONE;
400
401
0
    sal_uInt8 nEventAction;
402
0
    sal_Int8 nUserOpt = rEvt.mbDefault ? EXCHG_IN_ACTION_DEFAULT
403
0
                                       : rEvt.mnAction;
404
405
0
    m_nDropAction = SotExchange::GetExchangeAction(
406
0
                                GetDataFlavorExVector(),
407
0
                                m_nDropDestination,
408
0
                                rEvt.mnAction,
409
0
                                nUserOpt, m_nDropFormat, nEventAction );
410
411
0
    if( EXCHG_INOUT_ACTION_NONE != m_nDropAction )
412
0
    {
413
0
        const Point aDocPt( PixelToLogic( aPixPt ) );
414
415
        //With the default action we still want to have a say.
416
0
        SwModule* pMod = SwModule::get();
417
0
        if( pMod->m_pDragDrop )
418
0
        {
419
0
            bool bCleanup = false;
420
            //Drawing objects in Headers/Footers are not allowed
421
422
0
            SwWrtShell *pSrcSh = pMod->m_pDragDrop->GetShell();
423
0
            if( (pSrcSh->GetSelFrameType() == FrameTypeFlags::DRAWOBJ) &&
424
0
                pSrcSh->IsSelContainsControl() &&
425
0
                 (rSh.GetFrameType( &aDocPt, false ) & (FrameTypeFlags::HEADER|FrameTypeFlags::FOOTER)) )
426
0
            {
427
0
                bCleanup = true;
428
0
            }
429
            // don't more position protected objects!
430
0
            else if( DND_ACTION_MOVE == rEvt.mnAction &&
431
0
                     pSrcSh->IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE )
432
0
            {
433
0
                bCleanup = true;
434
0
            }
435
0
            else if( rEvt.mbDefault )
436
0
            {
437
                // internal Drag&Drop: within same Doc a Move
438
                // otherwise a Copy - Task 54974
439
0
                nEventAction = pSrcSh->GetDoc() == rSh.GetDoc()
440
0
                                    ? DND_ACTION_MOVE
441
0
                                    : DND_ACTION_COPY;
442
0
            }
443
0
            if ( bCleanup )
444
0
            {
445
0
                CleanupDropUserMarker();
446
0
                rSh.UnSetVisibleCursor();
447
0
                return DND_ACTION_NONE;
448
0
            }
449
0
        }
450
0
        else
451
0
        {
452
            //D&D from outside of SW should be a Copy per default.
453
0
            if( EXCHG_IN_ACTION_DEFAULT == nEventAction &&
454
0
                DND_ACTION_MOVE == rEvt.mnAction )
455
0
                nEventAction = DND_ACTION_COPY;
456
457
0
            if( (SotClipboardFormatId::SBA_FIELDDATAEXCHANGE == m_nDropFormat &&
458
0
                 EXCHG_IN_ACTION_LINK == m_nDropAction) ||
459
0
                 SotClipboardFormatId::SBA_CTRLDATAEXCHANGE == m_nDropFormat  )
460
0
            {
461
0
                SdrMarkView* pMView = rSh.GetDrawView();
462
0
                if( pMView && !pMView->IsDesignMode() )
463
0
                    return DND_ACTION_NONE;
464
0
            }
465
0
        }
466
467
0
        if ( EXCHG_IN_ACTION_DEFAULT != nEventAction )
468
0
            nUserOpt = static_cast<sal_Int8>(nEventAction);
469
470
        // show DropCursor or UserMarker ?
471
0
        if( SotExchangeDest::SWDOC_FREE_AREA_WEB == m_nDropDestination ||
472
0
            SotExchangeDest::SWDOC_FREE_AREA == m_nDropDestination )
473
0
        {
474
0
            CleanupDropUserMarker();
475
0
            SwContentAtPos aCont( IsAttrAtPos::ContentCheck );
476
0
            if(rSh.GetContentAtPos(aDocPt, aCont))
477
0
                rSh.SwCursorShell::SetVisibleCursor( aDocPt, ScrollSizeMode::ScrollSizeMouseSelection );
478
0
        }
479
0
        else
480
0
        {
481
0
            rSh.UnSetVisibleCursor();
482
483
0
            if ( m_pUserMarkerObj != pObj )
484
0
            {
485
0
                CleanupDropUserMarker();
486
0
                m_pUserMarkerObj = pObj;
487
488
0
                if(m_pUserMarkerObj)
489
0
                {
490
0
                    m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), *m_pUserMarkerObj ));
491
0
                }
492
0
            }
493
0
        }
494
0
        return nUserOpt;
495
0
    }
496
497
0
    CleanupDropUserMarker();
498
0
    rSh.UnSetVisibleCursor();
499
0
    return DND_ACTION_NONE;
500
0
}
501
502
IMPL_LINK_NOARG(SwEditWin, DDHandler, Timer *, void)
503
0
{
504
0
    g_bDDTimerStarted = false;
505
0
    m_aTimer.Stop();
506
0
    m_aTimer.SetTimeout(240);
507
0
    m_bMBPressed = false;
508
0
    ReleaseMouse();
509
0
    g_bFrameDrag = false;
510
0
    g_bExecuteDrag = true;
511
0
    StartExecuteDrag();
512
0
}
513
514
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */