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