/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <svx/svdview.hxx>
#include <editeng/outliner.hxx>
#include <svx/svdobj.hxx>
#include <sot/exchange.hxx>
#include <sot/formats.hxx>
#include <sfx2/bindings.hxx>
#include <vcl/commandevent.hxx>
#include <osl/diagnose.h>
#include <sfx2/viewfrm.hxx>
#include <fmturl.hxx>
#include <frmfmt.hxx>
#include <wrtsh.hxx>
#include <edtdd.hxx>
#include <edtwin.hxx>
#include <view.hxx>
#include <viewopt.hxx>
#include <swdtflvr.hxx>
#include <swmodule.hxx>
#include <docsh.hxx>
#include <wdocsh.hxx>
using namespace ::com::sun::star;
// no include "dbgoutsw.hxx" here!!!!!!
bool g_bExecuteDrag = false;
void SwEditWin::StartDDTimer()
{
m_aTimer.SetInvokeHandler(LINK(this, SwEditWin, DDHandler));
m_aTimer.SetTimeout(480);
m_aTimer.Start();
g_bDDTimerStarted = true;
}
void SwEditWin::StopDDTimer(SwWrtShell *pSh, const Point &rPt)
{
m_aTimer.Stop();
g_bDDTimerStarted = false;
if(!pSh->IsSelFrameMode())
pSh->CallSetCursor(&rPt, false);
m_aTimer.SetInvokeHandler(LINK(this,SwEditWin, TimerHandler));
}
void SwEditWin::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
{
if (m_rView.GetObjectShell()->isContentExtractionLocked())
return;
SwWrtShell &rSh = m_rView.GetWrtShell();
if( rSh.GetDrawView() )
{
CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true );
if( rSh.GetDrawView()->Command( aDragEvent, this ) )
{
m_rView.GetViewFrame().GetBindings().InvalidateAll(false);
return; // Event evaluated by SdrView
}
}
if ( m_pApplyTempl || rSh.IsDrawCreate() || IsDrawAction())
return;
bool bStart = false, bDelSelect = false;
SdrObject *pObj = nullptr;
Point aDocPos( PixelToLogic( rPosPixel ) );
const bool bInSelect = rSh.IsInSelect();
if (!bInSelect && rSh.TestCurrPam(aDocPos, true))
//We are not selecting and aren't at a selection
bStart = true;
else if ( !g_bFrameDrag && rSh.IsSelFrameMode() &&
rSh.IsInsideSelectedObj( aDocPos ) &&
nullptr == m_pAnchorMarker)
{
//We are not dragging internally and are not at an
//object (frame, draw object)
// #i106131# *and* AnchorDrag is *not* active: When active,
// entering global drag mode will destroy the AnchorHdl but
// keep the now invalid ptr in place, next access will crash.
// It is indeed wrong to enter drag mode when AnchorDrag is
// already active
bStart = true;
}
else if( !g_bFrameDrag && m_rView.GetDocShell()->IsReadOnly() &&
OBJCNT_NONE != rSh.GetObjCntType( aDocPos, pObj ))
{
rSh.LockPaint(LockPaintReason::StartDrag);
if( rSh.SelectObj( aDocPos, 0, pObj ))
bStart = bDelSelect = true;
else
rSh.UnlockPaint();
}
else if (!bInSelect)// tdf#116384 only drag hyperlink if user's not currently setting the selection
{
SwContentAtPos aSwContentAtPos( IsAttrAtPos::InetAttr );
bStart = rSh.GetContentAtPos( aDocPos,
aSwContentAtPos );
}
if ( !bStart || m_bIsInDrag )
return;
// If the add selection mode has been pushed in the MouseButtonDown handler it needs to be
// popped or it will remain active and noticeable in the statusbar selection control until the
// next MouseButtonUp event after the DnD, since a MouseButtonUp event is not received by the
// edit window when DnD is done.
if (g_bModePushed)
{
rSh.PopMode();
g_bModePushed = false;
}
m_bMBPressed = false;
ReleaseMouse();
g_bFrameDrag = false;
g_bExecuteDrag = true;
SwEditWin::s_nDDStartPosY = aDocPos.Y();
SwEditWin::s_nDDStartPosX = aDocPos.X();
m_aMovePos = aDocPos;
StartExecuteDrag();
if( bDelSelect )
{
rSh.UnSelectFrame();
rSh.UnlockPaint();
}
}
void SwEditWin::StartExecuteDrag()
{
if( !g_bExecuteDrag || m_bIsInDrag )
return;
m_bIsInDrag = true;
rtl::Reference<SwTransferable> pTransfer = new SwTransferable( m_rView.GetWrtShell() );
pTransfer->StartDrag( this, m_aMovePos );
}
void SwEditWin::DragFinished()
{
DropCleanup();
m_aTimer.SetInvokeHandler( LINK(this,SwEditWin, TimerHandler) );
m_bIsInDrag = false;
}
void SwEditWin::DropCleanup()
{
SwWrtShell &rSh = m_rView.GetWrtShell();
// reset statuses
g_bNoInterrupt = false;
if ( m_bOldIdleSet )
{
rSh.GetViewOptions()->SetIdle( m_bOldIdle );
m_bOldIdleSet = false;
}
if ( m_pUserMarker )
CleanupDropUserMarker();
else
rSh.UnSetVisibleCursor();
}
void SwEditWin::CleanupDropUserMarker()
{
if ( m_pUserMarker )
{
m_pUserMarker.reset();
m_pUserMarkerObj = nullptr;
}
}
//exhibition hack (MA,MBA)
void SwView::SelectShellForDrop()
{
if ( !GetCurShell() )
SelectShell();
}
sal_Int8 SwEditWin::ExecuteDrop( const ExecuteDropEvent& rEvt )
{
GetView().SelectShellForDrop();
DropCleanup();
sal_Int8 nRet = DND_ACTION_NONE;
//A Drop to an open OutlinerView doesn't concern us (also see QueryDrop)
SwWrtShell &rSh = m_rView.GetWrtShell();
const Point aDocPt( PixelToLogic( rEvt.maPosPixel ));
SdrObject *pObj = nullptr;
OutlinerView* pOLV;
rSh.GetObjCntType( aDocPt, pObj );
if( pObj && nullptr != ( pOLV = rSh.GetDrawView()->GetTextEditOutlinerView() ))
{
tools::Rectangle aRect( pOLV->GetOutputArea() );
aRect.Union( pObj->GetLogicRect() );
const Point aPos = pOLV->GetWindow()->PixelToLogic(rEvt.maPosPixel);
if ( aRect.Contains(aPos) )
{
rSh.StartAllAction();
rSh.EndAllAction();
return nRet;
}
}
// There's a special treatment for file lists with a single
// element, that depends on the actual content of the
// Transferable to be accessible. Since the transferable
// may only be accessed after the drop has been accepted
// (according to KA due to Java D&D), we'll have to
// reevaluate the drop action once more _with_ the
// Transferable.
sal_uInt8 nEventAction;
sal_Int8 nUserOpt = rEvt.mbDefault ? EXCHG_IN_ACTION_DEFAULT
: rEvt.mnAction;
SotExchangeActionFlags nActionFlags;
m_nDropAction = SotExchange::GetExchangeAction(
GetDataFlavorExVector(),
m_nDropDestination,
rEvt.mnAction,
nUserOpt, m_nDropFormat, nEventAction, SotClipboardFormatId::NONE,
&rEvt.maDropEvent.Transferable,
&nActionFlags );
TransferableDataHelper aData( rEvt.maDropEvent.Transferable );
nRet = rEvt.mnAction;
if( !SwTransferable::PasteData( aData, rSh, m_nDropAction, nActionFlags, m_nDropFormat,
m_nDropDestination, false, rEvt.mbDefault, &aDocPt, nRet))
nRet = DND_ACTION_NONE;
else if ( SW_MOD()->m_pDragDrop )
//Don't clean up anymore at internal D&D!
SW_MOD()->m_pDragDrop->SetCleanUp( false );
return nRet;
}
SotExchangeDest SwEditWin::GetDropDestination( const Point& rPixPnt, SdrObject ** ppObj )
{
SwWrtShell &rSh = m_rView.GetWrtShell();
const Point aDocPt( PixelToLogic( rPixPnt ) );
if (rSh.IsOverReadOnlyPos(aDocPt) || rSh.DocPtInsideInputField(aDocPt))
return SotExchangeDest::NONE;
SdrObject *pObj = nullptr;
const ObjCntType eType = rSh.GetObjCntType( aDocPt, pObj );
//Drop to OutlinerView (TextEdit in Drawing) should decide it on its own!
if( pObj )
{
OutlinerView* pOLV = rSh.GetDrawView()->GetTextEditOutlinerView();
if ( pOLV )
{
tools::Rectangle aRect( pOLV->GetOutputArea() );
aRect.Union( pObj->GetLogicRect() );
const Point aPos = pOLV->GetWindow()->PixelToLogic( rPixPnt );
if( aRect.Contains( aPos ) )
return SotExchangeDest::NONE;
}
}
//What do we want to drop on now?
SotExchangeDest nDropDestination = SotExchangeDest::NONE;
//Did anything else arrive from the DrawingEngine?
if( OBJCNT_NONE != eType )
{
switch ( eType )
{
case OBJCNT_GRF:
{
bool bLink,
bIMap = nullptr != rSh.GetFormatFromObj( aDocPt )->GetURL().GetMap();
OUString aDummy;
rSh.GetGrfAtPos( aDocPt, aDummy, bLink );
if ( bLink && bIMap )
nDropDestination = SotExchangeDest::DOC_LNKD_GRAPH_W_IMAP;
else if ( bLink )
nDropDestination = SotExchangeDest::DOC_LNKD_GRAPHOBJ;
else if ( bIMap )
nDropDestination = SotExchangeDest::DOC_GRAPH_W_IMAP;
else
nDropDestination = SotExchangeDest::DOC_GRAPHOBJ;
}
break;
case OBJCNT_FLY:
if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr )
nDropDestination = SotExchangeDest::DOC_TEXTFRAME_WEB;
else
nDropDestination = SotExchangeDest::DOC_TEXTFRAME;
break;
case OBJCNT_OLE: nDropDestination = SotExchangeDest::DOC_OLEOBJ; break;
case OBJCNT_CONTROL: /* no Action avail */
case OBJCNT_SIMPLE: nDropDestination = SotExchangeDest::DOC_DRAWOBJ; break;
case OBJCNT_URLBUTTON: nDropDestination = SotExchangeDest::DOC_URLBUTTON; break;
case OBJCNT_GROUPOBJ: nDropDestination = SotExchangeDest::DOC_GROUPOBJ; break;
default: OSL_ENSURE( false, "new ObjectType?" );
}
}
if ( nDropDestination == SotExchangeDest::NONE )
{
if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr )
nDropDestination = SotExchangeDest::SWDOC_FREE_AREA_WEB;
else
nDropDestination = SotExchangeDest::SWDOC_FREE_AREA;
}
if( ppObj )
*ppObj = pObj;
return nDropDestination;
}
sal_Int8 SwEditWin::AcceptDrop( const AcceptDropEvent& rEvt )
{
if( rEvt.mbLeaving )
{
DropCleanup();
return rEvt.mnAction;
}
if( m_rView.GetDocShell()->IsReadOnly() )
return DND_ACTION_NONE;
SwWrtShell &rSh = m_rView.GetWrtShell();
Point aPixPt( rEvt.maPosPixel );
// If the cursor is near the inner boundary
// we attempt to scroll towards the desired direction.
tools::Rectangle aWin(Point(), GetOutputSizePixel());
const int nMargin = 10;
aWin.AdjustLeft(nMargin );
aWin.AdjustTop(nMargin );
aWin.AdjustRight( -nMargin );
aWin.AdjustBottom( -nMargin );
if(!aWin.Contains(aPixPt)) {
static sal_uInt64 last_tick = 0;
sal_uInt64 current_tick = tools::Time::GetSystemTicks();
if((current_tick-last_tick) > 500) {
last_tick = current_tick;
if(!m_bOldIdleSet) {
m_bOldIdle = rSh.GetViewOptions()->IsIdle();
rSh.GetViewOptions()->SetIdle(false);
m_bOldIdleSet = true;
}
CleanupDropUserMarker();
if(aPixPt.X() > aWin.Right()) aPixPt.AdjustX(nMargin );
if(aPixPt.X() < aWin.Left()) aPixPt.AdjustX( -nMargin );
if(aPixPt.Y() > aWin.Bottom()) aPixPt.AdjustY(nMargin );
if(aPixPt.Y() < aWin.Top()) aPixPt.AdjustY( -nMargin );
Point aDocPt(PixelToLogic(aPixPt));
SwRect rect(aDocPt,Size(1,1));
rSh.MakeVisible(rect);
}
}
if(m_bOldIdleSet) {
rSh.GetViewOptions()->SetIdle( m_bOldIdle );
m_bOldIdleSet = false;
}
SdrObject *pObj = nullptr;
m_nDropDestination = GetDropDestination( aPixPt, &pObj );
if( m_nDropDestination == SotExchangeDest::NONE )
return DND_ACTION_NONE;
sal_uInt8 nEventAction;
sal_Int8 nUserOpt = rEvt.mbDefault ? EXCHG_IN_ACTION_DEFAULT
: rEvt.mnAction;
m_nDropAction = SotExchange::GetExchangeAction(
GetDataFlavorExVector(),
m_nDropDestination,
rEvt.mnAction,
nUserOpt, m_nDropFormat, nEventAction );
if( EXCHG_INOUT_ACTION_NONE != m_nDropAction )
{
const Point aDocPt( PixelToLogic( aPixPt ) );
//With the default action we still want to have a say.
SwModule *pMod = SW_MOD();
if( pMod->m_pDragDrop )
{
bool bCleanup = false;
//Drawing objects in Headers/Footers are not allowed
SwWrtShell *pSrcSh = pMod->m_pDragDrop->GetShell();
if( (pSrcSh->GetSelFrameType() == FrameTypeFlags::DRAWOBJ) &&
pSrcSh->IsSelContainsControl() &&
(rSh.GetFrameType( &aDocPt, false ) & (FrameTypeFlags::HEADER|FrameTypeFlags::FOOTER)) )
{
bCleanup = true;
}
// don't more position protected objects!
else if( DND_ACTION_MOVE == rEvt.mnAction &&
pSrcSh->IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE )
{
bCleanup = true;
}
else if( rEvt.mbDefault )
{
// internal Drag&Drop: within same Doc a Move
// otherwise a Copy - Task 54974
nEventAction = pSrcSh->GetDoc() == rSh.GetDoc()
? DND_ACTION_MOVE
: DND_ACTION_COPY;
}
if ( bCleanup )
{
CleanupDropUserMarker();
rSh.UnSetVisibleCursor();
return DND_ACTION_NONE;
}
}
else
{
//D&D from outside of SW should be a Copy per default.
if( EXCHG_IN_ACTION_DEFAULT == nEventAction &&
DND_ACTION_MOVE == rEvt.mnAction )
nEventAction = DND_ACTION_COPY;
if( (SotClipboardFormatId::SBA_FIELDDATAEXCHANGE == m_nDropFormat &&
EXCHG_IN_ACTION_LINK == m_nDropAction) ||
SotClipboardFormatId::SBA_CTRLDATAEXCHANGE == m_nDropFormat )
{
SdrMarkView* pMView = rSh.GetDrawView();
if( pMView && !pMView->IsDesignMode() )
return DND_ACTION_NONE;
}
}
if ( EXCHG_IN_ACTION_DEFAULT != nEventAction )
nUserOpt = static_cast<sal_Int8>(nEventAction);
// show DropCursor or UserMarker ?
if( SotExchangeDest::SWDOC_FREE_AREA_WEB == m_nDropDestination ||
SotExchangeDest::SWDOC_FREE_AREA == m_nDropDestination )
{
CleanupDropUserMarker();
SwContentAtPos aCont( IsAttrAtPos::ContentCheck );
if(rSh.GetContentAtPos(aDocPt, aCont))
rSh.SwCursorShell::SetVisibleCursor( aDocPt );
}
else
{
rSh.UnSetVisibleCursor();
if ( m_pUserMarkerObj != pObj )
{
CleanupDropUserMarker();
m_pUserMarkerObj = pObj;
if(m_pUserMarkerObj)
{
m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), *m_pUserMarkerObj ));
}
}
}
return nUserOpt;
}
CleanupDropUserMarker();
rSh.UnSetVisibleCursor();
return DND_ACTION_NONE;
}
IMPL_LINK_NOARG(SwEditWin, DDHandler, Timer *, void)
{
g_bDDTimerStarted = false;
m_aTimer.Stop();
m_aTimer.SetTimeout(240);
m_bMBPressed = false;
ReleaseMouse();
g_bFrameDrag = false;
g_bExecuteDrag = true;
StartExecuteDrag();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.