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