diff options
Diffstat (limited to 'sw/source/core/frmedt')
-rw-r--r-- | sw/source/core/frmedt/fecopy.cxx | 1629 | ||||
-rw-r--r-- | sw/source/core/frmedt/fedesc.cxx | 245 | ||||
-rw-r--r-- | sw/source/core/frmedt/fefly1.cxx | 2134 | ||||
-rw-r--r-- | sw/source/core/frmedt/feflyole.cxx | 123 | ||||
-rw-r--r-- | sw/source/core/frmedt/feshview.cxx | 3360 | ||||
-rw-r--r-- | sw/source/core/frmedt/fetab.cxx | 2438 | ||||
-rw-r--r-- | sw/source/core/frmedt/fews.cxx | 1332 | ||||
-rw-r--r-- | sw/source/core/frmedt/tblsel.cxx | 2614 |
8 files changed, 13875 insertions, 0 deletions
diff --git a/sw/source/core/frmedt/fecopy.cxx b/sw/source/core/frmedt/fecopy.cxx new file mode 100644 index 000000000..591451480 --- /dev/null +++ b/sw/source/core/frmedt/fecopy.cxx @@ -0,0 +1,1629 @@ +/* -*- 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 <memory> +#include <hintids.hxx> + +#include <vcl/graph.hxx> +#include <sot/formats.hxx> +#include <svx/xfillit0.hxx> +#include <svx/svdocapt.hxx> +#include <svx/svdouno.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdoole2.hxx> +#include <svx/fmmodel.hxx> +#include <svx/unomodel.hxx> +#include <svx/svditer.hxx> +#include <svx/svdograf.hxx> +#include <tools/stream.hxx> +#include <unotools/streamwrap.hxx> +#include <osl/diagnose.h> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <fmtornt.hxx> +#include <fmtflcnt.hxx> +#include <frmfmt.hxx> +#include <txtfrm.hxx> +#include <txtflcnt.hxx> +#include <fesh.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <DocumentFieldsManager.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <rootfrm.hxx> +#include <ndtxt.hxx> +#include <pam.hxx> +#include <tblsel.hxx> +#include <swtable.hxx> +#include <flyfrm.hxx> +#include <pagefrm.hxx> +#include <fldbas.hxx> +#include <swundo.hxx> +#include <viewimp.hxx> +#include <dview.hxx> +#include <dcontact.hxx> +#include <dflyobj.hxx> +#include <docsh.hxx> +#include <pagedesc.hxx> +#include <mvsave.hxx> +#include <textboxhelper.hxx> +#include <frameformats.hxx> +#include <vcl/virdev.hxx> +#include <svx/svdundo.hxx> + +using namespace ::com::sun::star; + +// Copy for the internal clipboard. Copies all selections to the clipboard. +void SwFEShell::Copy( SwDoc& rClpDoc, const OUString* pNewClpText ) +{ + rClpDoc.GetIDocumentUndoRedo().DoUndo(false); // always false! + + // delete content if ClpDocument contains content + SwNodeIndex aSttIdx( rClpDoc.GetNodes().GetEndOfExtras(), 2 ); + SwNodeIndex aEndNdIdx( *aSttIdx.GetNode().EndOfSectionNode() ); + SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode(); + if (!pTextNd || !pTextNd->GetText().isEmpty() || + aSttIdx.GetIndex()+1 != rClpDoc.GetNodes().GetEndOfContent().GetIndex() ) + { + rClpDoc.GetNodes().Delete( aSttIdx, + rClpDoc.GetNodes().GetEndOfContent().GetIndex() - aSttIdx.GetIndex() ); + pTextNd = rClpDoc.GetNodes().MakeTextNode( aSttIdx, + rClpDoc.GetDfltTextFormatColl() ); + --aSttIdx; + } + + // also delete surrounding FlyFrames if any + for( const auto pFly : *rClpDoc.GetSpzFrameFormats() ) + { + SwFormatAnchor const*const pAnchor = &pFly->GetAnchor(); + SwPosition const*const pAPos = pAnchor->GetContentAnchor(); + if (pAPos && + ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) && + aSttIdx <= pAPos->nNode && pAPos->nNode <= aEndNdIdx ) + { + rClpDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly ); + } + } + + rClpDoc.GetDocumentFieldsManager().GCFieldTypes(); // delete the FieldTypes + + // if a string was passed, copy it to the clipboard- + // document. Then also the Calculator can use the internal + // clipboard + if( pNewClpText ) + { + pTextNd->InsertText( *pNewClpText, SwIndex( pTextNd ) ); + return; // that's it + } + + rClpDoc.getIDocumentFieldsAccess().LockExpFields(); + rClpDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::DeleteRedlines ); + + // do we want to copy a FlyFrame? + if( IsFrameSelected() ) + { + // get the FlyFormat + SwFlyFrame* pFly = GetSelectedFlyFrame(); + SwFrameFormat* pFlyFormat = pFly->GetFormat(); + SwFormatAnchor aAnchor( pFlyFormat->GetAnchor() ); + + if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())) + { + SwPosition aPos( aSttIdx ); + if ( RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId() ) + { + aPos.nContent.Assign( pTextNd, 0 ); + } + aAnchor.SetAnchor( &aPos ); + } + pFlyFormat = rClpDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFlyFormat, aAnchor, true, true ); + + // assure the "RootFormat" is the first element in Spz-Array + // (if necessary Flys were copied in Flys) + SwFrameFormats& rSpzFrameFormats = *rClpDoc.GetSpzFrameFormats(); + if( rSpzFrameFormats[ 0 ] != pFlyFormat ) + { +#ifndef NDEBUG + bool inserted = +#endif + rSpzFrameFormats.newDefault( pFlyFormat ); + assert( !inserted && "Fly not contained in Spz-Array" ); + } + + if ( RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId() ) + { + // JP 13.02.99 Bug 61863: if a frameselection is passed to the + // clipboard, it should be found at pasting. Therefore + // the copied TextAttribut should be removed in the node + // otherwise it will be recognised as TextSelektion + const SwIndex& rIdx = pFlyFormat->GetAnchor().GetContentAnchor()->nContent; + SwTextFlyCnt *const pTextFly = static_cast<SwTextFlyCnt *>( + pTextNd->GetTextAttrForCharAt( + rIdx.GetIndex(), RES_TXTATR_FLYCNT)); + if( pTextFly ) + { + const_cast<SwFormatFlyCnt&>(pTextFly->GetFlyCnt()).SetFlyFormat(); + pTextNd->EraseText( rIdx, 1 ); + } + } + } + else if ( IsObjSelected() ) + { + SwPosition aPos( aSttIdx, SwIndex( pTextNd, 0 )); + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + + if( Imp()->GetDrawView()->IsGroupEntered() || + ( !pObj->GetUserCall() && pObj->getParentSdrObjectFromSdrObject()) ) + { + SfxItemSet aSet( rClpDoc.GetAttrPool(), aFrameFormatSetRange ); + + SwFormatAnchor aAnchor( RndStdIds::FLY_AT_PARA ); + aAnchor.SetAnchor( &aPos ); + aSet.Put( aAnchor ); + + SdrObject *const pNew = + rClpDoc.CloneSdrObj( *pObj ); + + SwPaM aTemp(aPos); + rClpDoc.getIDocumentContentOperations().InsertDrawObj(aTemp, *pNew, aSet ); + } + else + { + SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall( pObj )); + SwFrameFormat *pFormat = pContact->GetFormat(); + SwFormatAnchor aAnchor( pFormat->GetAnchor() ); + if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())) + { + aAnchor.SetAnchor( &aPos ); + } + + rClpDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true ); + } + } + } + else + CopySelToDoc(rClpDoc); // copy the selections + + rClpDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE ); + rClpDoc.getIDocumentFieldsAccess().UnlockExpFields(); + if( !rClpDoc.getIDocumentFieldsAccess().IsExpFieldsLocked() ) + rClpDoc.getIDocumentFieldsAccess().UpdateExpFields(nullptr, true); +} + +static const Point &lcl_FindBasePos( const SwFrame *pFrame, const Point &rPt ) +{ + const SwFrame *pF = pFrame; + while ( pF && !pF->getFrameArea().Contains( rPt ) ) + { + if ( pF->IsContentFrame() ) + pF = static_cast<const SwContentFrame*>(pF)->GetFollow(); + else + pF = nullptr; + } + if ( pF ) + return pF->getFrameArea().Pos(); + else + return pFrame->getFrameArea().Pos(); +} + +static bool lcl_SetAnchor( const SwPosition& rPos, const SwNode& rNd, SwFlyFrame const * pFly, + const Point& rInsPt, SwFEShell const & rDestShell, SwFormatAnchor& rAnchor, + Point& rNewPos, bool bCheckFlyRecur ) +{ + bool bRet = true; + rAnchor.SetAnchor( &rPos ); + std::pair<Point, bool> const tmp(rInsPt, false); + SwContentFrame *const pTmpFrame = rNd.GetContentNode()->getLayoutFrame( + rDestShell.GetLayout(), nullptr, &tmp); + SwFlyFrame *pTmpFly = pTmpFrame->FindFlyFrame(); + if( pTmpFly && bCheckFlyRecur && pFly->IsUpperOf( *pTmpFly ) ) + { + bRet = false; + } + else if ( RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId() ) + { + if( pTmpFly ) + { + const SwNodeIndex& rIdx = *pTmpFly->GetFormat()->GetContent().GetContentIdx(); + SwPosition aPos( rIdx ); + rAnchor.SetAnchor( &aPos ); + rNewPos = pTmpFly->getFrameArea().Pos(); + } + else + { + rAnchor.SetType( RndStdIds::FLY_AT_PAGE ); + rAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) ); + const SwFrame *pPg = pTmpFrame->FindPageFrame(); + rNewPos = pPg->getFrameArea().Pos(); + } + } + else + rNewPos = ::lcl_FindBasePos( pTmpFrame, rInsPt ); + return bRet; +} + +bool SwFEShell::CopyDrawSel( SwFEShell& rDestShell, const Point& rSttPt, + const Point& rInsPt, bool bIsMove, bool bSelectInsert ) +{ + bool bRet = true; + + // The list should be copied, because below new objects will be selected + const SdrMarkList aMrkList( Imp()->GetDrawView()->GetMarkedObjectList() ); + const size_t nMarkCount = aMrkList.GetMarkCount(); + if( !rDestShell.Imp()->GetDrawView() ) + // should create it now + rDestShell.MakeDrawView(); + else if( bSelectInsert ) + rDestShell.Imp()->GetDrawView()->UnmarkAll(); + + SdrPageView *pDestPgView = rDestShell.Imp()->GetPageView(), + *pSrcPgView = Imp()->GetPageView(); + SwDrawView *pDestDrwView = rDestShell.Imp()->GetDrawView(), + *pSrcDrwView = Imp()->GetDrawView(); + SwDoc* pDestDoc = rDestShell.GetDoc(); + + Size aSiz( rInsPt.X() - rSttPt.X(), rInsPt.Y() - rSttPt.Y() ); + for( size_t i = 0; i < nMarkCount; ++i ) + { + SdrObject *pObj = aMrkList.GetMark( i )->GetMarkedSdrObj(); + + SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall( pObj )); + SwFrameFormat *pFormat = pContact->GetFormat(); + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + + bool bInsWithFormat = true; + + if( pDestDrwView->IsGroupEntered() ) + { + // insert into the group, when it belongs to an entered group + // or when the object is not anchored as a character + if( pSrcDrwView->IsGroupEntered() || + (RndStdIds::FLY_AS_CHAR != rAnchor.GetAnchorId()) ) + + { + SdrObject* pNew = pDestDoc->CloneSdrObj( *pObj, bIsMove && + GetDoc() == pDestDoc, false ); + pNew->NbcMove( aSiz ); + pDestDrwView->InsertObjectAtView( pNew, *pDestPgView ); + bInsWithFormat = false; + } + } + + if( bInsWithFormat ) + { + SwFormatAnchor aAnchor( rAnchor ); + Point aNewAnch; + + if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())) + { + if ( this == &rDestShell ) + { + // same shell? Then request the position + // from the passed DocumentPosition + SwPosition aPos( *GetCursor()->GetPoint() ); + Point aPt( rInsPt ); + aPt -= rSttPt - pObj->GetSnapRect().TopLeft(); + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aState ); + const SwNode *pNd; + if( (pNd = &aPos.nNode.GetNode())->IsNoTextNode() ) + bRet = false; + else + bRet = ::lcl_SetAnchor( aPos, *pNd, nullptr, rInsPt, + rDestShell, aAnchor, aNewAnch, false ); + } + else + { + SwPaM *pCursor = rDestShell.GetCursor(); + if( pCursor->GetNode().IsNoTextNode() ) + bRet = false; + else + bRet = ::lcl_SetAnchor( *pCursor->GetPoint(), + pCursor->GetNode(), nullptr, rInsPt, + rDestShell, aAnchor, + aNewAnch, false ); + } + } + else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() ) + { + aAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) ); + const SwRootFrame* pTmpRoot = rDestShell.GetLayout(); + const SwFrame* pPg = pTmpRoot->GetPageAtPos( rInsPt, nullptr, true ); + if ( pPg ) + aNewAnch = pPg->getFrameArea().Pos(); + } + + if( bRet ) + { + if( pSrcDrwView->IsGroupEntered() || + ( !pObj->GetUserCall() && pObj->getParentSdrObjectFromSdrObject()) ) + { + SfxItemSet aSet( pDestDoc->GetAttrPool(),aFrameFormatSetRange); + aSet.Put( aAnchor ); + SdrObject* pNew = pDestDoc->CloneSdrObj( *pObj, bIsMove && + GetDoc() == pDestDoc ); + pFormat = pDestDoc->getIDocumentContentOperations().InsertDrawObj( *rDestShell.GetCursor(), *pNew, aSet ); + } + else + pFormat = pDestDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true ); + + // Can be 0, as Draws are not allowed in Headers/Footers + if ( pFormat ) + { + // #tdf33692 - drawing object has to be made visible on ctrl+drag copy. + pFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREPPASTING)); + SdrObject* pNew = pFormat->FindSdrObject(); + if ( RndStdIds::FLY_AS_CHAR != aAnchor.GetAnchorId() ) + { + Point aPos( rInsPt ); + aPos -= aNewAnch; + aPos -= rSttPt - pObj->GetSnapRect().TopLeft(); + // OD 2004-04-05 #i26791# - change attributes instead of + // direct positioning + pFormat->SetFormatAttr( SwFormatHoriOrient( aPos.getX(), text::HoriOrientation::NONE, text::RelOrientation::FRAME ) ); + pFormat->SetFormatAttr( SwFormatVertOrient( aPos.getY(), text::VertOrientation::NONE, text::RelOrientation::FRAME ) ); + // #i47455# - notify draw frame format + // that position attributes are already set. + if (SwDrawFrameFormat *pDrawFormat = dynamic_cast<SwDrawFrameFormat*>(pFormat)) + pDrawFormat->PosAttrSet(); + } + if (SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, pObj)) + { + SwTextBoxHelper::syncFlyFrameAttr(*pFormat, pFormat->GetAttrSet(), pObj); + } + + if( bSelectInsert ) + pDestDrwView->MarkObj( pNew, pDestPgView ); + } + } + } + } + + if ( bIsMove && bRet ) + { + if( &rDestShell == this ) + { + const SdrMarkList aList( pSrcDrwView->GetMarkedObjectList() ); + pSrcDrwView->UnmarkAll(); + + for ( size_t i = 0, nMrkCnt = aMrkList.GetMarkCount(); i < nMrkCnt; ++i ) + { + SdrObject *pObj = aMrkList.GetMark( i )->GetMarkedSdrObj(); + pSrcDrwView->MarkObj( pObj, pSrcPgView ); + } + DelSelectedObj(); + for ( size_t i = 0, nMrkCnt = aList.GetMarkCount(); i < nMrkCnt; ++i ) + { + SdrObject *pObj = aList.GetMark( i )->GetMarkedSdrObj(); + pSrcDrwView->MarkObj( pObj, pSrcPgView ); + } + } + else + DelSelectedObj(); + } + + return bRet; +} + +bool SwFEShell::Copy( SwFEShell& rDestShell, const Point& rSttPt, + const Point& rInsPt, bool bIsMove, bool bSelectInsert ) +{ + bool bRet = false; + + OSL_ENSURE( this == &rDestShell || !rDestShell.IsObjSelected(), + "Dest-Shell cannot be in Obj-Mode" ); + + CurrShell aCurr( &rDestShell ); + + rDestShell.StartAllAction(); + rDestShell.GetDoc()->getIDocumentFieldsAccess().LockExpFields(); + + // Shift references + bool bCopyIsMove = mxDoc->IsCopyIsMove(); + if( bIsMove ) + // set a flag in Doc, handled in TextNodes + mxDoc->SetCopyIsMove( true ); + + RedlineFlags eOldRedlMode = rDestShell.GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(); + rDestShell.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOldRedlMode | RedlineFlags::DeleteRedlines ); + + // If there are table formulas in the area, then display the table first + // so that the table formula can calculate a new value first + // (individual boxes in the area are retrieved via the layout) + SwFieldType* pTableFieldTyp = rDestShell.GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Table ); + + if( IsFrameSelected() ) + { + SwFlyFrame* pFly = GetSelectedFlyFrame(); + SwFrameFormat* pFlyFormat = pFly->GetFormat(); + SwFormatAnchor aAnchor( pFlyFormat->GetAnchor() ); + bRet = true; + Point aNewAnch; + + if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())) + { + if ( this == &rDestShell ) + { + // same shell? Then request the position + // from the passed DocumentPosition + SwPosition aPos( *GetCursor()->GetPoint() ); + Point aPt( rInsPt ); + aPt -= rSttPt - pFly->getFrameArea().Pos(); + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aState ); + const SwNode *pNd; + if( (pNd = &aPos.nNode.GetNode())->IsNoTextNode() ) + bRet = false; + else + { + // do not copy in itself + const SwNodeIndex *pTmp = pFlyFormat->GetContent().GetContentIdx(); + if ( aPos.nNode > *pTmp && aPos.nNode < + pTmp->GetNode().EndOfSectionIndex() ) + { + bRet = false; + } + else + bRet = ::lcl_SetAnchor( aPos, *pNd, pFly, rInsPt, + rDestShell, aAnchor, aNewAnch, true ); + } + } + else + { + const SwPaM *pCursor = rDestShell.GetCursor(); + if( pCursor->GetNode().IsNoTextNode() ) + bRet = false; + else + bRet = ::lcl_SetAnchor( *pCursor->GetPoint(), pCursor->GetNode(), + pFly, rInsPt, rDestShell, aAnchor, + aNewAnch, GetDoc() == rDestShell.GetDoc()); + } + } + else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() ) + { + aAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) ); + const SwRootFrame* pTmpRoot = rDestShell.GetLayout(); + const SwFrame* pPg = pTmpRoot->GetPageAtPos( rInsPt, nullptr, true ); + if ( pPg ) + aNewAnch = pPg->getFrameArea().Pos(); + } + else { + OSL_ENSURE( false, "what anchor is it?" ); + } + + if( bRet ) + { + SwFrameFormat *pOldFormat = pFlyFormat; + pFlyFormat = rDestShell.GetDoc()->getIDocumentLayoutAccess().CopyLayoutFormat( *pFlyFormat, aAnchor, true, true ); + + if ( RndStdIds::FLY_AS_CHAR != aAnchor.GetAnchorId() ) + { + Point aPos( rInsPt ); + aPos -= aNewAnch; + aPos -= rSttPt - pFly->getFrameArea().Pos(); + pFlyFormat->SetFormatAttr( SwFormatHoriOrient( aPos.getX(),text::HoriOrientation::NONE, text::RelOrientation::FRAME ) ); + pFlyFormat->SetFormatAttr( SwFormatVertOrient( aPos.getY(),text::VertOrientation::NONE, text::RelOrientation::FRAME ) ); + } + + const Point aPt( rDestShell.GetCursorDocPos() ); + + if( bIsMove ) + GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pOldFormat ); + + // only select if it can be shifted/copied in the same shell + if( bSelectInsert ) + { + SwFlyFrame* pFlyFrame = static_cast<SwFlyFrameFormat*>(pFlyFormat)->GetFrame( &aPt ); + if( pFlyFrame ) + { + //JP 12.05.98: should this be in SelectFlyFrame??? + rDestShell.Imp()->GetDrawView()->UnmarkAll(); + rDestShell.SelectFlyFrame( *pFlyFrame ); + } + } + + if (this != &rDestShell && !rDestShell.HasShellFocus()) + rDestShell.Imp()->GetDrawView()->hideMarkHandles(); + } + } + else if ( IsObjSelected() ) + bRet = CopyDrawSel( rDestShell, rSttPt, rInsPt, bIsMove, bSelectInsert ); + else if( IsTableMode() ) + { + // Copy parts from a table: create a table with the same + // width as the original and copy the selected boxes. + // Sizes will be corrected by percentage. + + // find boxes via the layout + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes ); + SwTableNode const*const pTableNd( + aBoxes.empty() ? nullptr : aBoxes[0]->GetSttNd()->FindTableNode()); + if (nullptr != pTableNd) + { + std::unique_ptr<SwPosition> pDstPos; + if( this == &rDestShell ) + { + // same shell? Then create new Cursor at the + // DocumentPosition passed + pDstPos.reset(new SwPosition( *GetCursor()->GetPoint() )); + Point aPt( rInsPt ); + GetLayout()->GetModelPositionForViewPoint( pDstPos.get(), aPt ); + if( !pDstPos->nNode.GetNode().IsNoTextNode() ) + bRet = true; + } + else if( !rDestShell.GetCursor()->GetNode().IsNoTextNode() ) + { + pDstPos.reset(new SwPosition( *rDestShell.GetCursor()->GetPoint() )); + bRet = true; + } + + if( bRet ) + { + if( GetDoc() == rDestShell.GetDoc() ) + ParkTableCursor(); + + bRet = rDestShell.GetDoc()->InsCopyOfTable( *pDstPos, aBoxes,nullptr, + bIsMove && this == &rDestShell && + aBoxes.size() == pTableNd->GetTable(). + GetTabSortBoxes().size(), + this != &rDestShell ); + + if( this != &rDestShell ) + *rDestShell.GetCursor()->GetPoint() = *pDstPos; + + // create all parked Cursor? + if( GetDoc() == rDestShell.GetDoc() ) + GetCursor(); + + // JP 16.04.99: Bug 64908 - Set InsPos, to assure the parked + // Cursor is positioned at the insert position + if( this == &rDestShell ) + GetCursorDocPos() = rInsPt; + } + } + } + else + { + bRet = true; + if( this == &rDestShell ) + { + // same shell? then request the position + // at the passed document position + SwPosition aPos( *GetCursor()->GetPoint() ); + Point aPt( rInsPt ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aPt ); + bRet = !aPos.nNode.GetNode().IsNoTextNode(); + } + else if( rDestShell.GetCursor()->GetNode().IsNoTextNode() ) + bRet = false; + + if( bRet ) + bRet = SwEditShell::Copy( rDestShell ); + } + + rDestShell.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOldRedlMode ); + mxDoc->SetCopyIsMove( bCopyIsMove ); + + // have new table formulas been inserted? + if( pTableFieldTyp->HasWriterListeners() ) + { + // finish old actions: the table frames are created and + // a selection can be made + sal_uInt16 nActCnt; + for( nActCnt = 0; rDestShell.ActionPend(); ++nActCnt ) + rDestShell.EndAllAction(); + + for( ; nActCnt; --nActCnt ) + rDestShell.StartAllAction(); + } + rDestShell.GetDoc()->getIDocumentFieldsAccess().UnlockExpFields(); + rDestShell.GetDoc()->getIDocumentFieldsAccess().UpdateFields(false); + + rDestShell.EndAllAction(); + return bRet; +} + +// Paste for the internal clipboard. Copy the content of the clipboard +// in the document +namespace { + typedef std::shared_ptr<SwPaM> PaMPtr; + typedef std::shared_ptr<SwPosition> PositionPtr; + typedef std::pair< PaMPtr, PositionPtr > Insertion; + + bool PamHasSelection(const SwPaM& rPaM) + { + return rPaM.HasMark() && *rPaM.GetPoint() != *rPaM.GetMark(); + } + + /// Is pFormat anchored in a fly frame which has an associated draw format? + bool IsInTextBox(const SwFrameFormat* pFormat) + { + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + const SwPosition* pPosition = rAnchor.GetContentAnchor(); + if (!pPosition) + { + return false; + } + + const SwStartNode* pFlyNode = pPosition->nNode.GetNode().FindFlyStartNode(); + if (!pFlyNode) + { + return false; + } + + for ( const auto& pSpzFormat : *pFormat->GetDoc()->GetSpzFrameFormats() ) + { + if (pSpzFormat->Which() != RES_FLYFRMFMT) + { + continue; + } + + const SwNodeIndex* pIdx = pSpzFormat->GetContent().GetContentIdx(); + if (!pIdx || pFlyNode != &pIdx->GetNode()) + { + continue; + } + + return SwTextBoxHelper::isTextBox(pSpzFormat, RES_FLYFRMFMT); + } + + return false; + } +} + +namespace { + SwFrameFormat* lcl_PasteFlyOrDrawFormat(SwPaM& rPaM, SwFrameFormat* pCpyFormat, SwFEShell& rSh) + { + auto& rImp = *rSh.Imp(); + auto& rDoc = *rSh.GetDoc(); + auto& rDrawView = *rImp.GetDrawView(); + if(rDrawView.IsGroupEntered() && + RES_DRAWFRMFMT == pCpyFormat->Which() && + (RndStdIds::FLY_AS_CHAR != pCpyFormat->GetAnchor().GetAnchorId())) + { + const SdrObject* pSdrObj = pCpyFormat->FindSdrObject(); + if(pSdrObj) + { + SdrObject* pNew = rDoc.CloneSdrObj(*pSdrObj, false, false); + // Insert object sets any anchor position to 0. + // Therefore we calculate the absolute position here + // and after the insert the anchor of the object + // is set to the anchor of the group object. + tools::Rectangle aSnapRect = pNew->GetSnapRect(); + if(pNew->GetAnchorPos().X() || pNew->GetAnchorPos().Y()) + { + const Point aPoint(0, 0); + // OD 2004-04-05 #i26791# - direct drawing object + // positioning for group members + pNew->NbcSetAnchorPos(aPoint); + pNew->NbcSetSnapRect(aSnapRect); + } + + rDrawView.InsertObjectAtView(pNew, *rImp.GetPageView()); + + Point aGrpAnchor(0, 0); + SdrObjList* pList = pNew->getParentSdrObjListFromSdrObject(); + if(pList) + { + SdrObjGroup* pOwner(dynamic_cast<SdrObjGroup*>(pList->getSdrObjectFromSdrObjList())); + + if(nullptr != pOwner) + aGrpAnchor = pOwner->GetAnchorPos(); + } + + // OD 2004-04-05 #i26791# - direct drawing object + // positioning for group members + pNew->NbcSetAnchorPos(aGrpAnchor); + pNew->SetSnapRect(aSnapRect); + return nullptr; + } + } + SwFormatAnchor aAnchor(pCpyFormat->GetAnchor()); + if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) || + (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())) + { + SwPosition* pPos = rPaM.GetPoint(); + // allow shapes (no controls) in header/footer + if(RES_DRAWFRMFMT == pCpyFormat->Which() && rDoc.IsInHeaderFooter(pPos->nNode)) + { + const SdrObject *pCpyObj = pCpyFormat->FindSdrObject(); + if(pCpyObj && CheckControlLayer(pCpyObj)) + return nullptr; + } + else if(pCpyFormat->Which() == RES_FLYFRMFMT && IsInTextBox(pCpyFormat)) + { + // This is a fly frame which is anchored in a TextBox, ignore it as + // it's already copied as part of copying the content of the + // TextBox. + return nullptr; + } + // Ignore TextBoxes, they are already handled in sw::DocumentLayoutManager::CopyLayoutFormat(). + if(SwTextBoxHelper::isTextBox(pCpyFormat, RES_FLYFRMFMT)) + return nullptr; + aAnchor.SetAnchor(pPos); + } + else if(RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId()) + { + aAnchor.SetPageNum(rSh.GetPhyPageNum()); + } + else if(RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) + { + Point aPt; + (void)lcl_SetAnchor(*rPaM.GetPoint(), rPaM.GetNode(), nullptr, aPt, rSh, aAnchor, aPt, false); + } + + SwFrameFormat* pNew = rDoc.getIDocumentLayoutAccess().CopyLayoutFormat(*pCpyFormat, aAnchor, true, true); + return pNew; + } + + void lcl_SelectFlyFormat(SwFrameFormat *const pNew, SwFEShell& rSh) + { + if(!pNew) + return; + switch(pNew->Which()) + { + case RES_FLYFRMFMT: + { + assert(dynamic_cast<SwFlyFrameFormat*>(pNew)); + const Point aPt(rSh.GetCursorDocPos()); + SwFlyFrame* pFlyFrame = static_cast<SwFlyFrameFormat*>(pNew)->GetFrame(&aPt); + if(pFlyFrame) + rSh.SelectFlyFrame(*pFlyFrame); + break; + } + case RES_DRAWFRMFMT: + { + auto& rDrawView = *rSh.Imp()->GetDrawView(); + assert(dynamic_cast<SwDrawFrameFormat*>(pNew)); + SwDrawFrameFormat* pDrawFormat = static_cast<SwDrawFrameFormat*>(pNew); + // #i52780# - drawing object has to be made visible on paste. + pDrawFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREPPASTING)); + SdrObject* pObj = pDrawFormat->FindSdrObject(); + rDrawView.MarkObj(pObj, rDrawView.GetSdrPageView()); + // #i47455# - notify draw frame format + // that position attributes are already set. + pDrawFormat->PosAttrSet(); + break; + } + default: + SAL_WARN("sw.core", "unknown fly type"); + } + } +} + +bool SwFEShell::Paste(SwDoc& rClpDoc, bool bNestedTable) +{ + CurrShell aCurr( this ); + // then till end of the nodes array + SwNodeIndex aIdx( rClpDoc.GetNodes().GetEndOfExtras(), 2 ); + // select content section, whatever it may contain + SwPaM aCpyPam(aIdx, SwNodeIndex(rClpDoc.GetNodes().GetEndOfContent(), -1)); + if (SwContentNode *const pAtEnd = aCpyPam.GetNode(true).GetContentNode()) + { + pAtEnd->MakeEndIndex(&aCpyPam.GetPoint()->nContent); + } + + // If there are table formulas in the area, then display the table first + // so that the table formula can calculate a new value first + // (individual boxes in the area are retrieved via the layout) + SwFieldType* pTableFieldTyp = GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Table ); + + SwTableNode *const pSrcNd = aCpyPam.GetNode(false).GetTableNode(); + + bool bRet = true; + StartAllAction(); + GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSGLOSSARY, nullptr ); + GetDoc()->getIDocumentFieldsAccess().LockExpFields(); + + // When the clipboard content has been created by a rectangular selection + // the pasting is more sophisticated: + // every paragraph will be inserted into another position. + // The first positions are given by the actual cursor ring, + // if there are more text portions to insert than cursor in this ring, + // the additional insert positions will be created by moving the last + // cursor position into the next line (like pressing the cursor down key) + if( rClpDoc.IsColumnSelection() && !IsTableMode() ) + { + // Creation of the list of insert positions + std::vector< Insertion > aCopyVector; + // The number of text portions of the rectangular selection + const SwNodeOffset nSelCount = aCpyPam.GetPoint()->nNode.GetIndex() + - aCpyPam.GetMark()->nNode.GetIndex(); + SwNodeOffset nCount = nSelCount; + SwNodeIndex aClpIdx( aIdx ); + SwPaM* pStartCursor = GetCursor(); + SwPaM* pCurrCursor = pStartCursor; + SwNodeOffset nCursorCount( pStartCursor->GetRingContainer().size() ); + // If the target selection is a multi-selection, often the last and first + // cursor of the ring points to identical document positions. Then + // we should avoid double insertion of text portions... + while( nCursorCount > SwNodeOffset(1) && *pCurrCursor->GetPoint() == + *(pCurrCursor->GetPrev()->GetPoint()) ) + { + --nCursorCount; + pCurrCursor = pCurrCursor->GetNext(); + pStartCursor = pCurrCursor; + } + SwPosition aStartPos( *pStartCursor->GetPoint() ); + SwPosition aInsertPos( aStartPos ); // first insertion position + bool bCompletePara = false; + sal_uInt16 nMove = 0; + while( nCount ) + { + --nCount; + OSL_ENSURE( aIdx.GetNode().GetContentNode(), "Who filled the clipboard?!" ); + if( aIdx.GetNode().GetContentNode() ) // robust + { + Insertion aInsertion( std::make_shared<SwPaM>( aIdx ), + std::make_shared<SwPosition>( aInsertPos ) ); + ++aIdx; + aInsertion.first->SetMark(); + if( pStartCursor == pCurrCursor->GetNext() ) + { // Now we have to look for insertion positions... + if( !nMove ) // Annotate the last given insert position + aStartPos = aInsertPos; + SwCursor aCursor( aStartPos, nullptr); + // Check if we find another insert position by moving + // down the last given position + if (aCursor.UpDown(false, ++nMove, nullptr, 0, *GetLayout())) + aInsertPos = *aCursor.GetPoint(); + else // if there is no paragraph we have to create it + bCompletePara = nCount > SwNodeOffset(0); + nCursorCount = SwNodeOffset(0); + } + else // as long as we find more insert positions in the cursor ring + { // we'll take them + pCurrCursor = pCurrCursor->GetNext(); + aInsertPos = *pCurrCursor->GetPoint(); + --nCursorCount; + } + // If there are no more paragraphs e.g. at the end of a document, + // we insert complete paragraphs instead of text portions + if( bCompletePara ) + aInsertion.first->GetPoint()->nNode = aIdx; + else + aInsertion.first->GetPoint()->nContent = + aInsertion.first->GetContentNode()->Len(); + aCopyVector.push_back( aInsertion ); + } + // If there are no text portions left but there are some more + // cursor positions to fill we have to restart with the first + // text portion + if( !nCount && nCursorCount ) + { + nCount = min( nSelCount, nCursorCount ); + aIdx = aClpIdx; // Start of clipboard content + } + } + for (auto const& item : aCopyVector) + { + SwPosition& rInsPos = *item.second; + SwPaM& rCopy = *item.first; + const SwStartNode* pBoxNd = rInsPos.nNode.GetNode().FindTableBoxStartNode(); + if( pBoxNd && SwNodeOffset(2) == pBoxNd->EndOfSectionIndex() - pBoxNd->GetIndex() && + rCopy.GetPoint()->nNode != rCopy.GetMark()->nNode ) + { + // if more than one node will be copied into a cell + // the box attributes have to be removed + GetDoc()->ClearBoxNumAttrs( rInsPos.nNode ); + } + { + SwNodeIndex aIndexBefore(rInsPos.nNode); + --aIndexBefore; + rClpDoc.getIDocumentContentOperations().CopyRange(rCopy, rInsPos, SwCopyFlags::CheckPosInFly); + { + ++aIndexBefore; + SwPaM aPaM(SwPosition(aIndexBefore), + SwPosition(rInsPos.nNode)); + aPaM.GetDoc().MakeUniqueNumRules(aPaM); + } + } + SaveTableBoxContent( &rInsPos ); + } + } + else + { + bool bDelTable = true; + + for(SwPaM& rPaM : GetCursor()->GetRingContainer()) + { + + SwTableNode *const pDestNd(GetDoc()->IsIdxInTable(rPaM.GetPoint()->nNode)); + if (pSrcNd && nullptr != pDestNd && + // not a forced nested table insertion + !bNestedTable && + // Heuristics to allow copying table rows or nesting tables without + // using Edit -> Paste Special -> Paste as Nested Table: + // Using table cursor, or if the text selection starts in the + // first paragraph, or if there is no selection and the text cursor + // is there in the first paragraph, overwrite content of the cell(s) + // (else insert a nested table later, i.e. if nothing selected and + // the cursor is not in the first paragraph, or the selected text + // doesn't contain the first paragraph of the cell) + rPaM.GetNode().GetIndex() == rPaM.GetNode().FindTableBoxStartNode()->GetIndex() + 1) + { + SwPosition aDestPos( *rPaM.GetPoint() ); + + bool bParkTableCursor = false; + const SwStartNode* pSttNd = rPaM.GetNode().FindTableBoxStartNode(); + + // TABLE IN TABLE: copy table in table + // search boxes via the layout + SwSelBoxes aBoxes; + if( IsTableMode() ) // table selection? + { + GetTableSel( *this, aBoxes ); + ParkTableCursor(); + bParkTableCursor = true; + } + else if( !PamHasSelection(rPaM) && rPaM.GetNext() == &rPaM && + ( !pSrcNd->GetTable().IsTableComplex() || + pDestNd->GetTable().IsNewModel() ) ) + { + // make relative table copy + SwTableBox* pBox = pDestNd->GetTable().GetTableBox( + pSttNd->GetIndex() ); + OSL_ENSURE( pBox, "Box is not in this table" ); + aBoxes.insert( pBox ); + } + + SwNodeIndex aNdIdx( *pDestNd->EndOfSectionNode()); + if( !bParkTableCursor ) + { + // exit first the complete table + // ???? what about only table in a frame ????? + SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aNdIdx ); + SwPosition aPos( aNdIdx, SwIndex( pCNd, 0 )); + // #i59539: Don't remove all redline + SwPaM const tmpPaM(*pDestNd, *pDestNd->EndOfSectionNode()); + ::PaMCorrAbs(tmpPaM, aPos); + } + + bRet = GetDoc()->InsCopyOfTable( aDestPos, aBoxes, &pSrcNd->GetTable() ); + + if( bParkTableCursor ) + GetCursor(); + else + { + // return to the box + aNdIdx = *pSttNd; + SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aNdIdx ); + SwPosition aPos( aNdIdx, SwIndex( pCNd, 0 )); + // #i59539: Don't remove all redline + SwNode & rNode(rPaM.GetPoint()->nNode.GetNode()); + SwContentNode *const pContentNode( rNode.GetContentNode() ); + SwPaM const tmpPam(rNode, 0, + rNode, pContentNode ? pContentNode->Len() : 0); + ::PaMCorrAbs(tmpPam, aPos); + } + + break; // exit the "while-loop" + } + else if(*aCpyPam.GetPoint() == *aCpyPam.GetMark() && !rClpDoc.GetSpzFrameFormats()->empty()) + { + // we need a DrawView + if(!Imp()->GetDrawView()) + MakeDrawView(); + ::std::vector<SwFrameFormat*> inserted; + for (auto const pFlyFormat : *rClpDoc.GetSpzFrameFormats()) + { + // if anchored inside other fly, will be copied when copying + // top-level fly, so skip here! (other non-body anchor + // shouldn't happen here) + SwFormatAnchor const& rAnchor(pFlyFormat->GetAnchor()); + if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId() + || rClpDoc.GetNodes().GetEndOfExtras().GetIndex() < rAnchor.GetContentAnchor()->nNode.GetIndex()) + { + inserted.emplace_back( + lcl_PasteFlyOrDrawFormat(rPaM, pFlyFormat, *this)); + } + } + for (auto const pFlyFormat : inserted) + { + lcl_SelectFlyFormat(pFlyFormat, *this); + } + } + else + { + if( bDelTable && IsTableMode() ) + { + SwEditShell::Delete(false); + bDelTable = false; + } + + SwPosition& rInsPos = *rPaM.GetPoint(); + const SwStartNode* pBoxNd = rInsPos.nNode.GetNode(). + FindTableBoxStartNode(); + if( pBoxNd && SwNodeOffset(2) == pBoxNd->EndOfSectionIndex() - + pBoxNd->GetIndex() && + aCpyPam.GetPoint()->nNode != aCpyPam.GetMark()->nNode ) + { + // Copy more than 1 node in the current box. But + // then the BoxAttribute should be removed + GetDoc()->ClearBoxNumAttrs( rInsPos.nNode ); + } + + // ** + // ** Update SwDoc::Append, if you change the following code ** + // ** + { + SwNodeIndex aIndexBefore(rInsPos.nNode); + + --aIndexBefore; + + rClpDoc.getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CheckPosInFly); + // Note: aCpyPam is invalid now + + ++aIndexBefore; + SwPaM aPaM(SwPosition(aIndexBefore), + SwPosition(rInsPos.nNode)); + + aPaM.GetDoc().MakeUniqueNumRules(aPaM); + + // Update the rsid of each pasted text node. + SwNodes &rDestNodes = GetDoc()->GetNodes(); + SwNodeOffset const nEndIdx = aPaM.End()->nNode.GetIndex(); + + for (SwNodeOffset nIdx = aPaM.Start()->nNode.GetIndex(); + nIdx <= nEndIdx; ++nIdx) + { + SwTextNode *const pTextNode = rDestNodes[nIdx]->GetTextNode(); + if ( pTextNode ) + { + GetDoc()->UpdateParRsid( pTextNode ); + } + } + } + + SaveTableBoxContent( &rInsPos ); + } + } + } + + GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSGLOSSARY, nullptr ); + + // have new table formulas been inserted? + if( pTableFieldTyp->HasWriterListeners() ) + { + // finish old action: table-frames have been created + // a selection can be made now + sal_uInt16 nActCnt; + for( nActCnt = 0; ActionPend(); ++nActCnt ) + EndAllAction(); + + for( ; nActCnt; --nActCnt ) + StartAllAction(); + } + GetDoc()->getIDocumentFieldsAccess().UnlockExpFields(); + GetDoc()->getIDocumentFieldsAccess().UpdateFields(false); + EndAllAction(); + + return bRet; +} + +void SwFEShell::PastePages( SwFEShell& rToFill, sal_uInt16 nStartPage, sal_uInt16 nEndPage) +{ + Push(); + if(!GotoPage(nStartPage)) + { + Pop(PopMode::DeleteCurrent); + return; + } + MovePage( GetThisFrame, GetFirstSub ); + ::std::optional<SwPaM> oSourcePam( *GetCursor()->GetPoint() ); + OUString sStartingPageDesc = GetPageDesc( GetCurPageDesc()).GetName(); + SwPageDesc* pDesc = rToFill.FindPageDescByName( sStartingPageDesc, true ); + if( pDesc ) + rToFill.ChgCurPageDesc( *pDesc ); + + if(!GotoPage(nEndPage)) + { + Pop(PopMode::DeleteCurrent); + return; + } + //if the page starts with a table a paragraph has to be inserted before + SwNode *const pTableNode = oSourcePam->GetNode().FindTableNode(); + if(pTableNode) + { + //insert a paragraph + StartUndo(SwUndoId::INSERT); + SwNodeIndex aTableIdx( *pTableNode, -1 ); + SwPosition aBefore(aTableIdx); + if(GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore )) + { + SwPaM aTmp(aBefore); + *oSourcePam = aTmp; + } + EndUndo(SwUndoId::INSERT); + } + + MovePage( GetThisFrame, GetLastSub ); + oSourcePam->SetMark(); + *oSourcePam->GetMark() = *GetCursor()->GetPoint(); + + CurrShell aCurr( this ); + + StartAllAction(); + GetDoc()->getIDocumentFieldsAccess().LockExpFields(); + SetSelection(*oSourcePam); + // copy the text of the selection + SwEditShell::Copy(rToFill); + oSourcePam.reset(); // delete it because Undo will remove its node! + + if(pTableNode) + { + //remove the inserted paragraph + Undo(); + //remove the paragraph in the second doc, too + SwNodeIndex aIdx( rToFill.GetDoc()->GetNodes().GetEndOfExtras(), 2 ); + SwPaM aPara( aIdx ); //DocStart + rToFill.GetDoc()->getIDocumentContentOperations().DelFullPara(aPara); + } + // now the page bound objects + // additionally copy page bound frames + if( !GetDoc()->GetSpzFrameFormats()->empty() ) + { + // create a draw view if necessary + if( !rToFill.Imp()->GetDrawView() ) + rToFill.MakeDrawView(); + + for ( auto pCpyFormat : *GetDoc()->GetSpzFrameFormats() ) + { + SwFormatAnchor aAnchor( pCpyFormat->GetAnchor() ); + if ((RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId()) && + aAnchor.GetPageNum() >= nStartPage && aAnchor.GetPageNum() <= nEndPage) + { + aAnchor.SetPageNum( aAnchor.GetPageNum() - nStartPage + 1); + } + else + continue; + rToFill.GetDoc()->getIDocumentLayoutAccess().CopyLayoutFormat( *pCpyFormat, aAnchor, true, true ); + } + } + GetDoc()->getIDocumentFieldsAccess().UnlockExpFields(); + GetDoc()->getIDocumentFieldsAccess().UpdateFields(false); + Pop(PopMode::DeleteCurrent); + EndAllAction(); +} + +comphelper::OInterfaceContainerHelper3<css::text::XPasteListener>& SwFEShell::GetPasteListeners() { return m_aPasteListeners; } + +bool SwFEShell::GetDrawObjGraphic( SotClipboardFormatId nFormat, Graphic& rGrf ) const +{ + OSL_ENSURE( Imp()->HasDrawView(), "GetDrawObjGraphic without DrawView?" ); + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + bool bConvert = true; + if( rMrkList.GetMarkCount() ) + { + if( rMrkList.GetMarkCount() == 1 && + dynamic_cast< const SwVirtFlyDrawObj* >(rMrkList.GetMark( 0 )->GetMarkedSdrObj()) != nullptr ) + { + // select frame + if( CNT_GRF == GetCntType() ) + { + const Graphic* pGrf( GetGraphic() ); + if ( pGrf ) + { + Graphic aGrf( *pGrf ); + if( SotClipboardFormatId::GDIMETAFILE == nFormat ) + { + if( GraphicType::Bitmap != aGrf.GetType() ) + { + rGrf = aGrf; + bConvert = false; + } + else if( GetWin() ) + { + Size aSz; + Point aPt; + GetGrfSize( aSz ); + + ScopedVclPtrInstance< VirtualDevice > pVirtDev; + pVirtDev->EnableOutput( false ); + + MapMode aTmp( GetWin()->GetMapMode() ); + aTmp.SetOrigin( aPt ); + pVirtDev->SetMapMode( aTmp ); + + GDIMetaFile aMtf; + aMtf.Record( pVirtDev.get() ); + aGrf.Draw(*pVirtDev, aPt, aSz); + aMtf.Stop(); + aMtf.SetPrefMapMode( aTmp ); + aMtf.SetPrefSize( aSz ); + rGrf = aMtf; + } + } + else if( GraphicType::Bitmap == aGrf.GetType() ) + { + rGrf = aGrf; + bConvert = false; + } + else + { + // Not the original size, but the current one. + // Otherwise it could happen that for vector graphics + // many MB's of memory are allocated. + const Size aSz( GetSelectedFlyFrame()->getFramePrintArea().SSize() ); + ScopedVclPtrInstance< VirtualDevice > pVirtDev(*GetWin()->GetOutDev()); + + MapMode aTmp( MapUnit::MapTwip ); + pVirtDev->SetMapMode( aTmp ); + if( pVirtDev->SetOutputSize( aSz ) ) + { + aGrf.Draw(*pVirtDev, Point(), aSz); + rGrf = pVirtDev->GetBitmapEx( Point(), aSz ); + } + else + { + rGrf = aGrf; + bConvert = false; + } + } + } + } + } + else if( SotClipboardFormatId::GDIMETAFILE == nFormat ) + rGrf = Imp()->GetDrawView()->GetMarkedObjMetaFile(); + else if( SotClipboardFormatId::BITMAP == nFormat || SotClipboardFormatId::PNG == nFormat ) + rGrf = Imp()->GetDrawView()->GetMarkedObjBitmapEx(); + } + return bConvert; +} + +// #i50824# +// replace method <lcl_RemoveOleObjsFromSdrModel> by <lcl_ConvertSdrOle2ObjsToSdrGrafObjs> +static void lcl_ConvertSdrOle2ObjsToSdrGrafObjs( SdrModel& _rModel ) +{ + for ( sal_uInt16 nPgNum = 0; nPgNum < _rModel.GetPageCount(); ++nPgNum ) + { + // setup object iterator in order to iterate through all objects + // including objects in group objects, but exclusive group objects. + SdrObjListIter aIter(_rModel.GetPage(nPgNum)); + while( aIter.IsMore() ) + { + SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( aIter.Next() ); + if( pOle2Obj ) + { + // found an ole2 shape + SdrObjList* pObjList = pOle2Obj->getParentSdrObjListFromSdrObject(); + + // get its graphic + Graphic aGraphic; + pOle2Obj->Connect(); + const Graphic* pGraphic = pOle2Obj->GetGraphic(); + if( pGraphic ) + aGraphic = *pGraphic; + pOle2Obj->Disconnect(); + + // create new graphic shape with the ole graphic and shape size + SdrGrafObj* pGraphicObj = new SdrGrafObj( + _rModel, + aGraphic, + pOle2Obj->GetCurrentBoundRect()); + // apply layer of ole2 shape at graphic shape + pGraphicObj->SetLayer( pOle2Obj->GetLayer() ); + + // replace ole2 shape with the new graphic object and delete the ol2 shape + SdrObject* pRemovedObject = pObjList->ReplaceObject( pGraphicObj, pOle2Obj->GetOrdNum() ); + SdrObject::Free( pRemovedObject ); + } + } + } +} + +void SwFEShell::Paste( SvStream& rStrm, SwPasteSdr nAction, const Point* pPt ) +{ + CurrShell aCurr( this ); + StartAllAction(); + StartUndo(); + + std::unique_ptr< FmFormModel > pModel( + new FmFormModel( + nullptr, + GetDoc()->GetDocShell())); + + pModel->GetItemPool().FreezeIdRanges(); + + rStrm.Seek(0); + + uno::Reference< io::XInputStream > xInputStream( new utl::OInputStreamWrapper( rStrm ) ); + SvxDrawingLayerImport( pModel.get(), xInputStream ); + + if ( !Imp()->HasDrawView() ) + Imp()->MakeDrawView(); + + Point aPos( pPt ? *pPt : GetCharRect().Pos() ); + SdrView *pView = Imp()->GetDrawView(); + + // drop on the existing object: replace object or apply new attributes + if( pModel->GetPageCount() > 0 && + 1 == pModel->GetPage(0)->GetObjCount() && + 1 == pView->GetMarkedObjectList().GetMarkCount() ) + { + // replace a marked 'virtual' drawing object + // by its corresponding 'master' drawing object in the mark list. + SwDrawView::ReplaceMarkedDrawVirtObjs( *pView ); + + SdrObject* pClpObj = pModel->GetPage(0)->GetObj(0); + SdrObject* pOldObj = pView->GetMarkedObjectList().GetMark( 0 )->GetMarkedSdrObj(); + + if( SwPasteSdr::SetAttr == nAction && dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) != nullptr ) + nAction = SwPasteSdr::Replace; + + switch( nAction ) + { + case SwPasteSdr::Replace: + { + const SwFrameFormat* pFormat(nullptr); + const SwFrame* pAnchor(nullptr); + if( dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) != nullptr ) + { + pFormat = FindFrameFormat( pOldObj ); + + Point aNullPt; + SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrameFormat*>(pFormat)->GetFrame( &aNullPt ); + pAnchor = pFlyFrame ? pFlyFrame->GetAnchorFrame() : nullptr; + + if (!pAnchor || pAnchor->FindFooterOrHeader()) + { + // if there is a textframe in the header/footer: + // do not replace but insert + nAction = SwPasteSdr::Insert; + break; + } + } + + SdrObject* pNewObj(pClpObj->CloneSdrObject(pOldObj->getSdrModelFromSdrObject())); + tools::Rectangle aOldObjRect( pOldObj->GetCurrentBoundRect() ); + Size aOldObjSize( aOldObjRect.GetSize() ); + tools::Rectangle aNewRect( pNewObj->GetCurrentBoundRect() ); + Size aNewSize( aNewRect.GetSize() ); + + Fraction aScaleWidth( aOldObjSize.Width(), aNewSize.Width() ); + Fraction aScaleHeight( aOldObjSize.Height(), aNewSize.Height()); + pNewObj->NbcResize( aNewRect.TopLeft(), aScaleWidth, aScaleHeight); + + Point aVec = aOldObjRect.TopLeft() - aNewRect.TopLeft(); + pNewObj->NbcMove(Size(aVec.getX(), aVec.getY())); + + if( dynamic_cast<const SdrUnoObj*>( pNewObj) != nullptr ) + pNewObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetControlsId() ); + else if( dynamic_cast<const SdrUnoObj*>( pOldObj) != nullptr ) + pNewObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetHeavenId() ); + else + pNewObj->SetLayer( pOldObj->GetLayer() ); + + if( dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) != nullptr ) + { + // store attributes, then set SdrObject + SfxItemSetFixed<RES_SURROUND, RES_ANCHOR> aFrameSet( mxDoc->GetAttrPool() ); + aFrameSet.Set( pFormat->GetAttrSet() ); + + Point aNullPt; + if( pAnchor->IsTextFrame() && static_cast<const SwTextFrame*>(pAnchor)->IsFollow() ) + { + const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnchor); + do { + pTmp = pTmp->FindMaster(); + OSL_ENSURE( pTmp, "Where's my Master?" ); + } while( pTmp->IsFollow() ); + pAnchor = pTmp; + } + if( auto pCaptionObj = dynamic_cast<SdrCaptionObj*>( pOldObj)) + aNullPt = pCaptionObj->GetTailPos(); + else + aNullPt = aOldObjRect.TopLeft(); + + Point aNewAnchor = pAnchor->GetFrameAnchorPos( ::HasWrap( pOldObj ) ); + // OD 2004-04-05 #i26791# - direct positioning of Writer + // fly frame object for <SwDoc::Insert(..)> + pNewObj->NbcSetRelativePos( aNullPt - aNewAnchor ); + pNewObj->NbcSetAnchorPos( aNewAnchor ); + + pOldObj->GetOrdNum(); + + DelSelectedObj(); + + GetDoc()->getIDocumentContentOperations().InsertDrawObj( *GetCursor(), *pNewObj, aFrameSet ); + } + else + { + // #i123922# for handling MasterObject and virtual ones correctly, SW + // wants us to call ReplaceObject at the page, but that also + // triggers the same assertion (I tried it), so stay at the view method + pView->ReplaceObjectAtView(pOldObj, *Imp()->GetPageView(), pNewObj); + } + } + break; + + case SwPasteSdr::SetAttr: + { + SfxItemSet aSet( GetAttrPool() ); + const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pClpObj); + + if(pSdrGrafObj) + { + SdrObject* pTarget = nullptr; + + if(0 != pView->GetMarkedObjectList().GetMarkCount()) + { + // try to get target (if it's at least one, take first) + SdrMark* pMark = pView->GetMarkedObjectList().GetMark(0); + + if(pMark) + { + pTarget = pMark->GetMarkedSdrObj(); + } + } + + if(pTarget) + { + // copy ItemSet from target + aSet.Set(pTarget->GetMergedItemSet()); + } + + // for SdrGrafObj, use the graphic as fill style argument + const Graphic& rGraphic = pSdrGrafObj->GetGraphic(); + + if(GraphicType::NONE != rGraphic.GetType() && GraphicType::Default != rGraphic.GetType()) + { + aSet.Put(XFillBitmapItem(OUString(), rGraphic)); + aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP)); + } + } + else + { + aSet.Put(pClpObj->GetMergedItemSet()); + } + + pView->SetAttributes( aSet ); + } + break; + + default: + nAction = SwPasteSdr::Insert; + break; + } + } + else + nAction = SwPasteSdr::Insert; + + if( SwPasteSdr::Insert == nAction ) + { + ::sw::DrawUndoGuard drawUndoGuard(GetDoc()->GetIDocumentUndoRedo()); + + bool bDesignMode = pView->IsDesignMode(); + if( !bDesignMode ) + pView->SetDesignMode(); + + // #i50824# + // method <lcl_RemoveOleObjsFromSdrModel> replaced by <lcl_ConvertSdrOle2ObjsToSdrGrafObjs> + lcl_ConvertSdrOle2ObjsToSdrGrafObjs(*pModel); + pView->Paste(*pModel, aPos, nullptr, SdrInsertFlags::NONE); + + const size_t nCnt = pView->GetMarkedObjectList().GetMarkCount(); + if( nCnt ) + { + const Point aNull( 0, 0 ); + for( size_t i=0; i < nCnt; ++i ) + { + SdrObject *pObj = pView->GetMarkedObjectList().GetMark(i)->GetMarkedSdrObj(); + pObj->ImpSetAnchorPos( aNull ); + } + + pView->SetCurrentObj( SdrObjKind::Group ); + if ( nCnt > 1 ) + pView->GroupMarked(); + SdrObject *pObj = pView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if( dynamic_cast<const SdrUnoObj*>( pObj) != nullptr ) + { + pObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetControlsId() ); + bDesignMode = true; + } + else + pObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetHeavenId() ); + const tools::Rectangle &rSnap = pObj->GetSnapRect(); + const Size aDiff( rSnap.GetWidth()/2, rSnap.GetHeight()/2 ); + pView->MoveMarkedObj( aDiff ); + ImpEndCreate(); + if( !bDesignMode ) + pView->SetDesignMode( false ); + } + } + EndUndo(); + EndAllAction(); +} + +bool SwFEShell::Paste(const Graphic &rGrf, const OUString& rURL) +{ + CurrShell aCurr( this ); + SdrObject* pObj = nullptr; + SdrView *pView = Imp()->GetDrawView(); + + bool bRet = 1 == pView->GetMarkedObjectList().GetMarkCount(); + if (bRet) + { + pObj = pView->GetMarkedObjectList().GetMark( 0 )->GetMarkedSdrObj(); + bRet = pObj->IsClosedObj() && dynamic_cast<const SdrOle2Obj*>( pObj) == nullptr; + } + + if( bRet && pObj ) + { + // #i123922# added code to handle the two cases of SdrGrafObj and a fillable, non- + // OLE object in focus + SdrObject* pResult = pObj; + + if(dynamic_cast< SdrGrafObj* >(pObj)) + { + SdrGrafObj* pNewGrafObj(static_cast<SdrGrafObj*>(pObj->CloneSdrObject(pObj->getSdrModelFromSdrObject()))); + + pNewGrafObj->SetGraphic(rGrf); + + // #i123922# for handling MasterObject and virtual ones correctly, SW + // wants us to call ReplaceObject at the page, but that also + // triggers the same assertion (I tried it), so stay at the view method + pView->ReplaceObjectAtView(pObj, *pView->GetSdrPageView(), pNewGrafObj); + + // set in all cases - the Clone() will have copied an existing link (!) + pNewGrafObj->SetGraphicLink(rURL); + + pResult = pNewGrafObj; + } + else + { + pView->AddUndo(std::make_unique<SdrUndoAttrObj>(*pObj)); + + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLBITMAP> aSet(pView->GetModel()->GetItemPool()); + + aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP)); + aSet.Put(XFillBitmapItem(OUString(), rGrf)); + pObj->SetMergedItemSetAndBroadcast(aSet); + } + + // we are done; mark the modified/new object + pView->MarkObj(pResult, pView->GetSdrPageView()); + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/frmedt/fedesc.cxx b/sw/source/core/frmedt/fedesc.cxx new file mode 100644 index 000000000..f9f58949c --- /dev/null +++ b/sw/source/core/frmedt/fedesc.cxx @@ -0,0 +1,245 @@ +/* -*- 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 <fesh.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentContentOperations.hxx> +#include <IDocumentStylePoolAccess.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <cntfrm.hxx> +#include <txtfrm.hxx> +#include <notxtfrm.hxx> +#include <pam.hxx> +#include <fmtpdsc.hxx> +#include <pagedesc.hxx> +#include <tabfrm.hxx> +#include <SwStyleNameMapper.hxx> +#include <ndtxt.hxx> +#include <osl/diagnose.h> + +size_t SwFEShell::GetPageDescCnt() const +{ + return GetDoc()->GetPageDescCnt(); +} + +void SwFEShell::ChgCurPageDesc( const SwPageDesc& rDesc ) +{ +#if OSL_DEBUG_LEVEL > 0 + // SS does not change PageDesc, but only sets the attribute. + // The Pagedesc should be available in the document + bool bFound = false; + for ( size_t nTst = 0; nTst < GetPageDescCnt(); ++nTst ) + if ( &rDesc == &GetPageDesc( nTst ) ) + bFound = true; + OSL_ENSURE( bFound, "ChgCurPageDesc with invalid descriptor." ); +#endif + + StartAllAction(); + + SwPageFrame *pPage = GetCurrFrame()->FindPageFrame(); + const SwFrame *pFlow = nullptr; + ::std::optional<sal_uInt16> oPageNumOffset; + + OSL_ENSURE( !GetCursor()->HasMark(), "ChgCurPageDesc only without selection!"); + + CurrShell aCurr( this ); + while ( pPage ) + { + pFlow = pPage->FindFirstBodyContent(); + if ( pFlow ) + { + if ( pFlow->IsInTab() ) + pFlow = pFlow->FindTabFrame(); + const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem(); + if( rPgDesc.GetPageDesc() ) + { + // we found the culprit + oPageNumOffset = rPgDesc.GetNumOffset(); + break; + } + } + pPage = static_cast<SwPageFrame*>( pPage->GetPrev() ); + } + if ( !pPage ) + { + pPage = static_cast<SwPageFrame*>(GetLayout()->Lower()); + pFlow = pPage->FindFirstBodyContent(); + if ( !pFlow ) + { + pPage = static_cast<SwPageFrame*>(pPage->GetNext()); + pFlow = pPage->FindFirstBodyContent(); + OSL_ENSURE( pFlow, "Document without content?!?" ); + } + } + + // use pagenumber + SwFormatPageDesc aNew( &rDesc ); + aNew.SetNumOffset( oPageNumOffset ); + + if ( pFlow->IsInTab() ) + GetDoc()->SetAttr( aNew, *const_cast<SwFormat*>(static_cast<SwFormat const *>(pFlow->FindTabFrame()->GetFormat())) ); + else + { + assert(pFlow->IsContentFrame()); + SwPaM aPaM( pFlow->IsTextFrame() + ? *static_cast<SwTextFrame const*>(pFlow)->GetTextNodeFirst() // first, for PAGEDESC + : *static_cast<const SwNoTextFrame*>(pFlow)->GetNode() ); + GetDoc()->getIDocumentContentOperations().InsertPoolItem( + aPaM, aNew, SetAttrMode::DEFAULT, GetLayout()); + } + EndAllActionAndCall(); +} + +void SwFEShell::ChgPageDesc( size_t i, const SwPageDesc &rChged ) +{ + StartAllAction(); + CurrShell aCurr( this ); + //Fix i64842: because Undo has a very special way to handle header/footer content + // we have to copy the page descriptor before calling ChgPageDesc. + SwPageDesc aDesc( rChged ); + { + ::sw::UndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo()); + GetDoc()->CopyPageDesc(rChged, aDesc); + } + GetDoc()->ChgPageDesc( i, aDesc ); + EndAllActionAndCall(); +} + +const SwPageDesc& SwFEShell::GetPageDesc( size_t i ) const +{ + return GetDoc()->GetPageDesc( i ); +} + +SwPageDesc* SwFEShell::FindPageDescByName( const OUString& rName, + bool bGetFromPool, + size_t* pPos ) +{ + SwPageDesc* pDesc = GetDoc()->FindPageDesc(rName, pPos); + if( !pDesc && bGetFromPool ) + { + sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( rName, SwGetPoolIdFromName::PageDesc ); + if( USHRT_MAX != nPoolId && + nullptr != (pDesc = GetDoc()->getIDocumentStylePoolAccess().GetPageDescFromPool( nPoolId )) + && pPos ) + // appended always + *pPos = GetDoc()->GetPageDescCnt() - 1 ; + } + return pDesc; +} + +size_t SwFEShell::GetMousePageDesc( const Point &rPt ) const +{ + if( GetLayout() ) + { + const SwPageFrame* pPage = + static_cast<const SwPageFrame*>( GetLayout()->Lower() ); + if( pPage ) + { + while( pPage->GetNext() && rPt.Y() > pPage->getFrameArea().Bottom() ) + pPage = static_cast<const SwPageFrame*>( pPage->GetNext() ); + SwDoc *pMyDoc = GetDoc(); + size_t nPos; + if (pMyDoc->ContainsPageDesc( pPage->GetPageDesc(), &nPos ) ) + return nPos; + } + } + return 0; +} + +size_t SwFEShell::GetCurPageDesc( const bool bCalcFrame ) const +{ + const SwFrame *pFrame = GetCurrFrame( bCalcFrame ); + if ( pFrame ) + { + const SwPageFrame *pPage = pFrame->FindPageFrame(); + if ( pPage ) + { + size_t nPos; + if (GetDoc()->ContainsPageDesc( pPage->GetPageDesc(), &nPos )) + return nPos; + } + } + return 0; +} + +// if inside all selection only one PageDesc, return this. +// Otherwise return 0 pointer +const SwPageDesc* SwFEShell::GetSelectedPageDescs() const +{ + const SwContentNode* pCNd; + const SwFrame* pMkFrame, *pPtFrame; + const SwPageDesc* pFnd, *pRetDesc = reinterpret_cast<SwPageDesc*>(sal_IntPtr(-1)); + const Point aNulPt; + std::pair<Point, bool> const tmp(aNulPt, false); + + for(const SwPaM& rPaM : GetCursor()->GetRingContainer()) + { + + if( nullptr != (pCNd = rPaM.GetContentNode() ) && + nullptr != (pPtFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp))) + pPtFrame = pPtFrame->FindPageFrame(); + else + pPtFrame = nullptr; + + if( rPaM.HasMark() && + nullptr != (pCNd = rPaM.GetContentNode( false ) ) && + nullptr != (pMkFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp))) + pMkFrame = pMkFrame->FindPageFrame(); + else + pMkFrame = pPtFrame; + + if( !pMkFrame || !pPtFrame ) + pFnd = nullptr; + else if( pMkFrame == pPtFrame ) + pFnd = static_cast<const SwPageFrame*>(pMkFrame)->GetPageDesc(); + else + { + // swap pointer if PtFrame before MkFrame + if( static_cast<const SwPageFrame*>(pMkFrame)->GetPhyPageNum() > + static_cast<const SwPageFrame*>(pPtFrame)->GetPhyPageNum() ) + { + const SwFrame* pTmp = pMkFrame; pMkFrame = pPtFrame; pPtFrame = pTmp; + } + + // now check from MkFrame to PtFrame for equal PageDescs + pFnd = static_cast<const SwPageFrame*>(pMkFrame)->GetPageDesc(); + while( pFnd && pMkFrame != pPtFrame ) + { + pMkFrame = pMkFrame->GetNext(); + if( !pMkFrame || pFnd != static_cast<const SwPageFrame*>(pMkFrame)->GetPageDesc() ) + pFnd = nullptr; + } + } + + if( reinterpret_cast<SwPageDesc*>(sal_IntPtr(-1)) == pRetDesc ) + pRetDesc = pFnd; + else if( pFnd != pRetDesc ) + { + pRetDesc = nullptr; + break; + } + + } + + return pRetDesc; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/frmedt/fefly1.cxx b/sw/source/core/frmedt/fefly1.cxx new file mode 100644 index 000000000..323816ef2 --- /dev/null +++ b/sw/source/core/frmedt/fefly1.cxx @@ -0,0 +1,2134 @@ +/* -*- 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 <hintids.hxx> +#include <o3tl/any.hxx> +#include <svl/itemiter.hxx> +#include <vcl/imapobj.hxx> +#include <editeng/protitem.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdouno.hxx> +#include <tools/globname.hxx> +#include <sot/exchange.hxx> +#include <com/sun/star/form/FormButtonType.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <comphelper/types.hxx> +#include <osl/diagnose.h> +#include <fmtanchr.hxx> +#include <fmtcntnt.hxx> +#include <fmtornt.hxx> +#include <fmturl.hxx> +#include <fmtfsize.hxx> +#include <fesh.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <cntfrm.hxx> +#include <txtfrm.hxx> +#include <viewimp.hxx> +#include <viscrs.hxx> +#include <doc.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentState.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <redline.hxx> +#include <dview.hxx> +#include <dflyobj.hxx> +#include <dcontact.hxx> +#include <frmfmt.hxx> +#include <flyfrm.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <ndgrf.hxx> +#include <flyfrms.hxx> +#include <fldbas.hxx> +#include <fmtfld.hxx> +#include <swundo.hxx> +#include <txatbase.hxx> +#include <frame.hxx> +#include <notxtfrm.hxx> +#include <HandleAnchorNodeChg.hxx> +#include <frmatr.hxx> +#include <fmtsrnd.hxx> +#include <ndole.hxx> +#include <fefly.hxx> +#include <fmtcnct.hxx> +#include <frameformats.hxx> +#include <textboxhelper.hxx> + + +using namespace ::com::sun::star; + +// Based on the request, changes to the specific layouts will be made, to +// fit to the format +static bool lcl_SetNewFlyPos( const SwNode& rNode, SwFormatAnchor& rAnchor, + const Point& rPt ) +{ + bool bRet = false; + const SwStartNode* pStNode = rNode.FindFlyStartNode(); + if( pStNode ) + { + SwPosition aPos( *pStNode ); + rAnchor.SetAnchor( &aPos ); + bRet = true; + } + else + { + const SwContentNode *pCntNd = rNode.GetContentNode(); + std::pair<Point, bool> const tmp(rPt, false); + const SwContentFrame* pCFrame = pCntNd ? pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp) : nullptr; + const SwPageFrame *pPg = pCFrame ? pCFrame->FindPageFrame() : nullptr; + + rAnchor.SetPageNum( pPg ? pPg->GetPhyPageNum() : 1 ); + rAnchor.SetType( RndStdIds::FLY_AT_PAGE ); + } + return bRet; +} + +static bool lcl_FindAnchorPos( + SwDoc& rDoc, + const Point& rPt, + const SwFrame& rFrame, + SfxItemSet& rSet ) +{ + bool bRet = true; + SwFormatAnchor aNewAnch( rSet.Get( RES_ANCHOR ) ); + RndStdIds nNew = aNewAnch.GetAnchorId(); + const SwFrame *pNewAnch; + + //determine new anchor + Point aTmpPnt( rPt ); + switch( nNew ) + { + case RndStdIds::FLY_AS_CHAR: // also include this? + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL + { + // starting from the upper-left corner of the Fly, + // search nearest ContentFrame + const SwFrame* pFrame = rFrame.IsFlyFrame() ? static_cast<const SwFlyFrame&>(rFrame).GetAnchorFrame() + : &rFrame; + pNewAnch = ::FindAnchor( pFrame, aTmpPnt ); + if( pNewAnch->IsProtected() ) + { + bRet = false; + break; + } + SwPosition aPos( pNewAnch->IsTextFrame() + ? *static_cast<SwTextFrame const*>(pNewAnch)->GetTextNodeForParaProps() + : *static_cast<const SwNoTextFrame*>(pNewAnch)->GetNode() ); + if ((RndStdIds::FLY_AT_CHAR == nNew) || (RndStdIds::FLY_AS_CHAR == nNew)) + { + // textnode should be found, as only in those + // a content bound frame can be anchored + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + aTmpPnt.setX(aTmpPnt.getX() - 1); // do not land in the fly! + if( !pNewAnch->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState ) ) + { + assert(pNewAnch->IsTextFrame()); // because AT_CHAR/AS_CHAR + SwTextFrame const*const pTextFrame( + static_cast<SwTextFrame const*>(pNewAnch)); + if( pNewAnch->getFrameArea().Bottom() < aTmpPnt.Y() ) + { + aPos = pTextFrame->MapViewToModelPos(TextFrameIndex(0)); + } + else + { + aPos = pTextFrame->MapViewToModelPos( + TextFrameIndex(pTextFrame->GetText().getLength())); + } + } + else + { + if ( SwCursorShell::PosInsideInputField( aPos ) ) + { + aPos.nContent = SwCursorShell::StartOfInputFieldAtPos( aPos ); + } + } + } + aNewAnch.SetAnchor( &aPos ); + } + break; + + case RndStdIds::FLY_AT_FLY: // LAYER_IMPL + { + // starting from the upper-left corner of the Fly + // search nearest SwFlyFrame + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + SwPosition aPos( rDoc.GetNodes() ); + aTmpPnt.setX(aTmpPnt.getX() - 1); // do not land in the fly! + rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState ); + pNewAnch = ::FindAnchor( + aPos.nNode.GetNode().GetContentNode()->getLayoutFrame(rFrame.getRootFrame(), nullptr, nullptr), + aTmpPnt )->FindFlyFrame(); + + if( pNewAnch && &rFrame != pNewAnch && !pNewAnch->IsProtected() ) + { + aPos.nNode = *static_cast<const SwFlyFrame*>(pNewAnch)->GetFormat()->GetContent(). + GetContentIdx(); + aNewAnch.SetAnchor( &aPos ); + break; + } + } + + nNew = RndStdIds::FLY_AT_PAGE; + aNewAnch.SetType( nNew ); + [[fallthrough]]; + + case RndStdIds::FLY_AT_PAGE: + pNewAnch = rFrame.FindPageFrame(); + aNewAnch.SetPageNum( pNewAnch->GetPhyPageNum() ); + break; + + default: + OSL_ENSURE( false, "Wrong Id for new anchor." ); + } + + rSet.Put( aNewAnch ); + return bRet; +} + +//! also used in unoframe.cxx + +bool sw_ChkAndSetNewAnchor( + const SwFlyFrame& rFly, + SfxItemSet& rSet ) +{ + const SwFrameFormat& rFormat = *rFly.GetFormat(); + const SwFormatAnchor &rOldAnch = rFormat.GetAnchor(); + const RndStdIds nOld = rOldAnch.GetAnchorId(); + + RndStdIds nNew = rSet.Get( RES_ANCHOR ).GetAnchorId(); + + if( nOld == nNew ) + return false; + + SwDoc* pDoc = const_cast<SwDoc*>(rFormat.GetDoc()); + +#if OSL_DEBUG_LEVEL > 0 + OSL_ENSURE( !(nNew == RndStdIds::FLY_AT_PAGE && + (RndStdIds::FLY_AT_PARA==nOld || RndStdIds::FLY_AT_CHAR==nOld || RndStdIds::FLY_AS_CHAR==nOld ) && + pDoc->IsInHeaderFooter( rOldAnch.GetContentAnchor()->nNode )), + "forbidden anchor change in Head/Foot." ); +#endif + + return ::lcl_FindAnchorPos( *pDoc, rFly.getFrameArea().Pos(), rFly, rSet ); +} + +void SwFEShell::SelectFlyFrame( SwFlyFrame& rFrame ) +{ + CurrShell aCurr( this ); + + // The frame is new, thus select it. + // !! Always select the frame, if it's not selected. + // - it could be a new "old" one because the anchor was changed + // - "old" frames have had to be selected previously otherwise they could + // not have been changed + // The frames should not be selected by the document position, because + // it should have been selected! + SwViewShellImp *pImpl = Imp(); + if( !GetWin() ) + return; + + OSL_ENSURE( rFrame.IsFlyFrame(), "SelectFlyFrame wants a Fly" ); + + // nothing to be done if the Fly already was selected + if (GetSelectedFlyFrame() == &rFrame) + return; + + // assure the anchor is drawn + if( rFrame.IsFlyInContentFrame() && rFrame.GetAnchorFrame() ) + rFrame.GetAnchorFrame()->SetCompletePaint(); + + if( pImpl->GetDrawView()->AreObjectsMarked() ) + pImpl->GetDrawView()->UnmarkAll(); + + pImpl->GetDrawView()->MarkObj( rFrame.GetVirtDrawObj(), + pImpl->GetPageView() ); + + rFrame.SelectionHasChanged(this); + + KillPams(); + ClearMark(); + SelFlyGrabCursor(); +} + +// Get selected fly +SwFlyFrame* SwFEShell::GetSelectedFlyFrame() const +{ + if ( Imp()->HasDrawView() ) + { + // A Fly is only accessible if it is selected + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + if( rMrkList.GetMarkCount() != 1 ) + return nullptr; + + SdrObject *pO = rMrkList.GetMark( 0 )->GetMarkedSdrObj(); + + SwVirtFlyDrawObj *pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pO); + + return pFlyObj ? pFlyObj->GetFlyFrame() : nullptr; + } + return nullptr; +} + +// Get current fly in which the cursor is positioned +SwFlyFrame* SwFEShell::GetCurrFlyFrame(const bool bCalcFrame) const +{ + SwContentFrame *pContent = GetCurrFrame(bCalcFrame); + return pContent ? pContent->FindFlyFrame() : nullptr; +} + +// Get selected fly, but if none Get current fly in which the cursor is positioned +SwFlyFrame* SwFEShell::GetSelectedOrCurrFlyFrame() const +{ + SwFlyFrame *pFly = GetSelectedFlyFrame(); + if (pFly) + return pFly; + return GetCurrFlyFrame(); +} + +// Returns non-null pointer, if the current Fly could be anchored to another one (so it is inside) +const SwFrameFormat* SwFEShell::IsFlyInFly() +{ + CurrShell aCurr( this ); + + if ( !Imp()->HasDrawView() ) + return nullptr; + + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + if ( !rMrkList.GetMarkCount() ) + { + SwFlyFrame *pFly = GetCurrFlyFrame(false); + if (!pFly) + return nullptr; + return pFly->GetFormat(); + } + else if ( rMrkList.GetMarkCount() != 1 || + !GetUserCall(rMrkList.GetMark( 0 )->GetMarkedSdrObj()) ) + return nullptr; + + SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj(); + + SwFrameFormat *pFormat = FindFrameFormat( pObj ); + if( pFormat && RndStdIds::FLY_AT_FLY == pFormat->GetAnchor().GetAnchorId() ) + { + const SwFrame* pFly; + if (SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj *>(pObj)) + { + pFly = pFlyObj->GetFlyFrame()->GetAnchorFrame(); + } + else + { + pFly = static_cast<SwDrawContact*>(GetUserCall(pObj))->GetAnchorFrame(pObj); + } + + OSL_ENSURE( pFly, "IsFlyInFly: Where's my anchor?" ); + OSL_ENSURE( pFly->IsFlyFrame(), "IsFlyInFly: Funny anchor!" ); + return static_cast<const SwFlyFrame*>(pFly)->GetFormat(); + } + + Point aTmpPos = pObj->GetCurrentBoundRect().TopLeft(); + + SwFrame *pTextFrame; + { + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + SwNodeIndex aSwNodeIndex( GetDoc()->GetNodes() ); + SwPosition aPos( aSwNodeIndex ); + Point aPoint( aTmpPos ); + aPoint.setX(aPoint.getX() - 1); //do not land in the fly!! + GetLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState ); + // determine text frame by left-top-corner of object + SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode(); + std::pair<Point, bool> const tmp(aTmpPos, false); + pTextFrame = pNd ? pNd->getLayoutFrame(GetLayout(), nullptr, &tmp) : nullptr; + } + const SwFrame *pTmp = pTextFrame ? ::FindAnchor(pTextFrame, aTmpPos) : nullptr; + const SwFlyFrame *pFly = pTmp ? pTmp->FindFlyFrame() : nullptr; + if( pFly ) + return pFly->GetFormat(); + return nullptr; +} + +void SwFEShell::SetFlyPos( const Point& rAbsPos ) +{ + CurrShell aCurr( this ); + + // Determine reference point in document coordinates + SwFlyFrame *pFly = GetCurrFlyFrame(false); + if (!pFly) + return; + + //SwSaveHdl aSaveX( Imp() ); + + // Set an anchor starting from the absolute position for paragraph bound Flys + // Anchor and new RelPos will be calculated and set by the Fly + if ( pFly->IsFlyAtContentFrame() ) + { + if(pFly->IsFlyFreeFrame() && static_cast< SwFlyFreeFrame* >(pFly)->isTransformableSwFrame()) + { + // RotateFlyFrame3: When we have a change and are in transformed state (e.g. rotation used), + // we need to correct the absolute position (rAbsPos) which was created in + // transformed coordinates to untransformed state + TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(pFly)->getTransformableSwFrame()); + const SwRect aUntransformedFrameArea(pTransformableSwFrame->getUntransformedFrameArea()); + const Point aNewAbsPos( + rAbsPos.X() + aUntransformedFrameArea.Left() - pFly->getFrameArea().Left(), + rAbsPos.Y() + aUntransformedFrameArea.Top() - pFly->getFrameArea().Top()); + static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos(aNewAbsPos); + } + else + { + static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos( rAbsPos ); + } + } + else + { + const SwFrame *pAnch = pFly->GetAnchorFrame(); + Point aOrient( pAnch->getFrameArea().Pos() ); + + if ( pFly->IsFlyInContentFrame() ) + aOrient.setX(rAbsPos.getX()); + + // calculate RelPos. + aOrient.setX(rAbsPos.getX() - aOrient.getX()); + aOrient.setY(rAbsPos.getY() - aOrient.getY()); + pFly->ChgRelPos( aOrient ); + } + CallChgLnk(); // call the AttrChangeNotify on the UI-side. +} + +Point SwFEShell::FindAnchorPos( const Point& rAbsPos, bool bMoveIt ) +{ + Point aRet; + + CurrShell aCurr( this ); + + if ( !Imp()->HasDrawView() ) + return aRet; + + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + if ( rMrkList.GetMarkCount() != 1 || + !GetUserCall(rMrkList.GetMark( 0 )->GetMarkedSdrObj()) ) + return aRet; + + SdrObject* pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj(); + // #i28701# + SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat(); + const RndStdIds nAnchorId = rFormat.GetAnchor().GetAnchorId(); + + if ( RndStdIds::FLY_AS_CHAR == nAnchorId ) + return aRet; + + bool bFlyFrame = dynamic_cast<SwVirtFlyDrawObj *>(pObj) != nullptr; + + bool bTextBox = false; + if (rFormat.Which() == RES_DRAWFRMFMT) + { + bTextBox = SwTextBoxHelper::isTextBox(&rFormat, RES_DRAWFRMFMT, pObj); + } + + SwFlyFrame* pFly = nullptr; + const SwFrame* pFooterOrHeader = nullptr; + + if( bFlyFrame ) + { + // Calculate reference point in document coordinates + SwContentFrame *pContent = GetCurrFrame( false ); + if( !pContent ) + return aRet; + pFly = pContent->FindFlyFrame(); + if ( !pFly ) + return aRet; + const SwFrame* pOldAnch = pFly->GetAnchorFrame(); + if( !pOldAnch ) + return aRet; + if ( RndStdIds::FLY_AT_PAGE != nAnchorId ) + { + pFooterOrHeader = pContent->FindFooterOrHeader(); + } + } + else if (bTextBox) + { + auto pFlyFormat + = dynamic_cast<const SwFlyFrameFormat*>(SwTextBoxHelper::getOtherTextBoxFormat( + &rFormat, RES_DRAWFRMFMT, pObj ? pObj : rFormat.FindRealSdrObject())); + if (pFlyFormat) + { + pFly = pFlyFormat->GetFrame(); + } + } + + // set <pFooterOrHeader> also for drawing + // objects, but not for control objects. + // Necessary for moving 'anchor symbol' at the user interface inside header/footer. + else if ( !::CheckControlLayer( pObj ) ) + { + SwContentFrame *pContent = GetCurrFrame( false ); + if( !pContent ) + return aRet; + pFooterOrHeader = pContent->FindFooterOrHeader(); + } + + // Search nearest SwFlyFrame starting from the upper-left corner + // of the fly + SwContentFrame *pTextFrame = nullptr; + { + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + SwPosition aPos( GetDoc()->GetNodes().GetEndOfExtras() ); + Point aTmpPnt( rAbsPos ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState ); + if (aPos.nNode != GetDoc()->GetNodes().GetEndOfExtras().GetIndex() + && (nAnchorId != RndStdIds::FLY_AT_CHAR || !PosInsideInputField(aPos))) + { + SwContentNode* pCNode = aPos.nNode.GetNode().GetContentNode(); + assert(pCNode); + pTextFrame = pCNode->getLayoutFrame(GetLayout(), &aPos, nullptr); + } + } + const SwFrame *pNewAnch = nullptr; + if( pTextFrame != nullptr ) + { + if ( RndStdIds::FLY_AT_PAGE == nAnchorId ) + { + pNewAnch = pTextFrame->FindPageFrame(); + } + else + { + pNewAnch = ::FindAnchor( pTextFrame, rAbsPos ); + + if( RndStdIds::FLY_AT_FLY == nAnchorId ) // LAYER_IMPL + { + pNewAnch = pNewAnch->FindFlyFrame(); + } + } + } + + if( pNewAnch && !pNewAnch->IsProtected() ) + { + const SwFlyFrame* pCheck = (bFlyFrame || bTextBox) ? pNewAnch->FindFlyFrame() : nullptr; + // If we land inside the frame, make sure + // that the frame does not land inside its own content + while( pCheck ) + { + if( pCheck == pFly ) + break; + const SwFrame *pTmp = pCheck->GetAnchorFrame(); + pCheck = pTmp ? pTmp->FindFlyFrame() : nullptr; + } + + // Do not switch from header/footer to another area, + // do not switch to a header/footer + if( !pCheck && + pFooterOrHeader == pNewAnch->FindFooterOrHeader() ) + { + aRet = pNewAnch->GetFrameAnchorPos( ::HasWrap( pObj ) ); + + if ( bMoveIt || (nAnchorId == RndStdIds::FLY_AT_CHAR) ) + { + SwFormatAnchor aAnch( rFormat.GetAnchor() ); + switch ( nAnchorId ) + { + case RndStdIds::FLY_AT_PARA: + { + SwPosition pos = *aAnch.GetContentAnchor(); + pos.nNode = pTextFrame->IsTextFrame() + ? *static_cast<SwTextFrame const*>(pTextFrame)->GetTextNodeForParaProps() + : *static_cast<const SwNoTextFrame*>(pTextFrame)->GetNode(); + pos.nContent.Assign(nullptr,0); + aAnch.SetAnchor( &pos ); + break; + } + case RndStdIds::FLY_AT_PAGE: + { + aAnch.SetPageNum( static_cast<const SwPageFrame*>(pNewAnch)-> + GetPhyPageNum() ); + break; + } + + case RndStdIds::FLY_AT_FLY: + { + SwPosition aPos( *static_cast<const SwFlyFrame*>(pNewAnch)->GetFormat()-> + GetContent().GetContentIdx() ); + aAnch.SetAnchor( &aPos ); + break; + } + + case RndStdIds::FLY_AT_CHAR: + { + SwPosition pos = *aAnch.GetContentAnchor(); + Point aTmpPnt( rAbsPos ); + if( pTextFrame->GetModelPositionForViewPoint( &pos, aTmpPnt ) ) + { + SwRect aTmpRect; + pTextFrame->GetCharRect( aTmpRect, pos ); + aRet = aTmpRect.Pos(); + } + else + { + pos = static_cast<SwTextFrame const*>(pTextFrame)->MapViewToModelPos(TextFrameIndex(0)); + } + aAnch.SetAnchor( &pos ); + break; + } + default: + break; + + } + + if( bMoveIt ) + { + StartAllAction(); + // --> handle change of anchor node: + // if count of the anchor frame also change, the fly frames have to be + // re-created. Thus, delete all fly frames except the <this> before the + // anchor attribute is change and re-create them afterwards. + { + std::unique_ptr<SwHandleAnchorNodeChg> pHandleAnchorNodeChg; + SwFlyFrameFormat* pFlyFrameFormat( dynamic_cast<SwFlyFrameFormat*>(&rFormat) ); + if ( pFlyFrameFormat ) + { + pHandleAnchorNodeChg.reset( + new SwHandleAnchorNodeChg( *pFlyFrameFormat, aAnch )); + } + rFormat.GetDoc()->SetAttr( aAnch, rFormat ); + if (SwTextBoxHelper::getOtherTextBoxFormat(&rFormat, RES_DRAWFRMFMT, + pObj ? pObj : rFormat.FindRealSdrObject())) + { + if (pObj->getChildrenOfSdrObject()) + { + for (size_t i = 0; + i < pObj->getChildrenOfSdrObject()->GetObjCount(); ++i) + SwTextBoxHelper::changeAnchor( + &rFormat, pObj->getChildrenOfSdrObject()->GetObj(i)); + } + else + SwTextBoxHelper::syncFlyFrameAttr( + rFormat, rFormat.GetAttrSet(), + pObj ? pObj : rFormat.FindRealSdrObject()); + } + } + // #i28701# - no call of method + // <CheckCharRectAndTopOfLine()> for to-character anchored + // Writer fly frame needed. This method call can cause a + // format of the anchor frame, which is no longer intended. + // Instead clear the anchor character rectangle and + // the top of line values for all to-character anchored objects. + pAnchoredObj->ClearCharRectAndTopOfLine(); + EndAllAction(); + } + } + + SwRect aTmpRect( aRet, rAbsPos ); + if( aTmpRect.HasArea() ) + MakeVisible( aTmpRect ); +#if OSL_DEBUG_LEVEL > 0 + //TODO: That doesn't seem to be intended + if( COL_TRANSPARENT != GetOut()->GetLineColor() ) + { + OSL_FAIL( "Hey, Joe: Where's my Null Pen?" ); + GetOut()->SetLineColor( COL_TRANSPARENT ); + } +#endif + } + } + + return aRet; +} + +const SwFrameFormat *SwFEShell::NewFlyFrame( const SfxItemSet& rSet, bool bAnchValid, + SwFrameFormat *pParent ) +{ + CurrShell aCurr( this ); + StartAllAction(); + + SwPaM* pCursor = GetCursor(); + const Point aPt( GetCursorDocPos() ); + + SwSelBoxes aBoxes; + bool bMoveContent = true; + if( IsTableMode() ) + { + GetTableSel( *this, aBoxes ); + if( !aBoxes.empty() ) + { + // Cursor should be removed from the removal area. + // Always put it after/on the table; via the + // document position they will be set to the old + // position + ParkCursor( SwNodeIndex( *aBoxes[0]->GetSttNd() )); + + // #i127787# pCurrentCursor will be deleted in ParkCursor, + // we better get the current pCurrentCursor instead of working with the + // deleted one: + pCursor = GetCursor(); + } + else + bMoveContent = false; + } + else if( !pCursor->HasMark() && !pCursor->IsMultiSelection() ) + bMoveContent = false; + + const SwPosition& rPos = *pCursor->Start(); + + SwFormatAnchor& rAnch = const_cast<SwFormatAnchor&>(rSet.Get( RES_ANCHOR )); + RndStdIds eRndId = rAnch.GetAnchorId(); + switch( eRndId ) + { + case RndStdIds::FLY_AT_PAGE: + if( !rAnch.GetPageNum() ) //HotFix: Bug in UpdateByExample + rAnch.SetPageNum( 1 ); + break; + + case RndStdIds::FLY_AT_FLY: + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_CHAR: + case RndStdIds::FLY_AS_CHAR: + if( !bAnchValid ) + { + if( RndStdIds::FLY_AT_FLY != eRndId ) + { + rAnch.SetAnchor( &rPos ); + } + else if( lcl_SetNewFlyPos( rPos.nNode.GetNode(), rAnch, aPt ) ) + { + eRndId = RndStdIds::FLY_AT_PAGE; + } + } + break; + + default: + OSL_ENSURE( false, "What is the purpose of this Fly?" ); + break; + } + + SwFlyFrameFormat *pRet; + if( bMoveContent ) + { + GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSLAYFMT, nullptr ); + std::unique_ptr<SwFormatAnchor> pOldAnchor; + bool bHOriChgd = false, bVOriChgd = false; + std::shared_ptr<SwFormatVertOrient> aOldV; + std::shared_ptr<SwFormatHoriOrient> aOldH; + + if ( RndStdIds::FLY_AT_PAGE != eRndId ) + { + // First as with page link. Paragraph/character link on if + // everything was shifted. Then the position is valid! + // JP 13.05.98: if necessary also convert the horizontal/vertical + // orientation, to prevent correction during re-anchoring + pOldAnchor.reset(new SwFormatAnchor( rAnch )); + const_cast<SfxItemSet&>(rSet).Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE, 1 ) ); + + const SwFormatHoriOrient* pHoriOrientItem; + if( (pHoriOrientItem = rSet.GetItemIfSet( RES_HORI_ORIENT, false )) + && text::HoriOrientation::NONE == pHoriOrientItem->GetHoriOrient() ) + { + bHOriChgd = true; + aOldH.reset(pHoriOrientItem->Clone()); + const_cast<SfxItemSet&>(rSet).Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT ) ); + } + const SwFormatVertOrient* pVertOrientItem; + if( (pVertOrientItem = rSet.GetItemIfSet( RES_VERT_ORIENT, false )) + && text::VertOrientation::NONE == pVertOrientItem->GetVertOrient() ) + { + bVOriChgd = true; + aOldV.reset(pVertOrientItem->Clone()); + const_cast<SfxItemSet&>(rSet).Put( SwFormatVertOrient( 0, text::VertOrientation::TOP ) ); + } + } + + pRet = GetDoc()->MakeFlyAndMove( *pCursor, rSet, &aBoxes, pParent ); + + KillPams(); + + if( pOldAnchor ) + { + if( pRet ) + { + // calculate new position + // JP 24.03.97: also go via page links + // anchor should not lie in the shifted area + pRet->DelFrames(); + + const SwFrame* pAnch = ::FindAnchor( GetLayout(), aPt ); + SwPosition aPos( pAnch->IsTextFrame() + ? *static_cast<SwTextFrame const*>(pAnch)->GetTextNodeForParaProps() + : *static_cast<const SwNoTextFrame*>(pAnch)->GetNode() ); + + if ( RndStdIds::FLY_AS_CHAR == eRndId ) + { + assert(pAnch->IsTextFrame()); + aPos = static_cast<SwTextFrame const*>(pAnch)->MapViewToModelPos(TextFrameIndex(0)); + } + pOldAnchor->SetAnchor( &aPos ); + + // shifting of table selection is not Undo-capable. therefore + // changing the anchors should not be recorded + bool const bDoesUndo = + GetDoc()->GetIDocumentUndoRedo().DoesUndo(); + SwUndoId nLastUndoId(SwUndoId::EMPTY); + if (bDoesUndo && + GetDoc()->GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, + & nLastUndoId)) + { + if (SwUndoId::INSLAYFMT == nLastUndoId) + { + GetDoc()->GetIDocumentUndoRedo().DoUndo(false); + } + } + + const_cast<SfxItemSet&>(rSet).Put( std::move(pOldAnchor) ); + + if( bHOriChgd ) + const_cast<SfxItemSet&>(rSet).Put( *aOldH ); + if( bVOriChgd ) + const_cast<SfxItemSet&>(rSet).Put( *aOldV ); + + GetDoc()->SetFlyFrameAttr( *pRet, const_cast<SfxItemSet&>(rSet) ); + GetDoc()->GetIDocumentUndoRedo().DoUndo(bDoesUndo); + } + } + GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSLAYFMT, nullptr ); + } + else + /* If called from a shell try to propagate an + existing adjust item from rPos to the content node of the + new frame. */ + pRet = GetDoc()->MakeFlySection( eRndId, &rPos, &rSet, pParent, true ); + + if( pRet ) + { + SwFlyFrame* pFrame = pRet->GetFrame( &aPt ); + if( pFrame ) + SelectFlyFrame( *pFrame ); + else + { + GetLayout()->SetAssertFlyPages(); + pRet = nullptr; + } + } + EndAllActionAndCall(); + + return pRet; +} + +void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName, + const Graphic* pGraphic, + const SfxItemSet* pFlyAttrSet ) +{ + SwFlyFrameFormat* pFormat = nullptr; + CurrShell aCurr( this ); + StartAllAction(); + SwShellCursor *pStartCursor = dynamic_cast<SwShellCursor*>(GetCursor()); + SwShellCursor *pCursor = pStartCursor; + do + { + if (!pCursor) + break; + + // Has the anchor not been set or been set incompletely? + if( pFlyAttrSet ) + { + if( const SwFormatAnchor* pItem = pFlyAttrSet->GetItemIfSet( RES_ANCHOR, false ) ) + { + SwFormatAnchor* pAnchor = const_cast<SwFormatAnchor*>(pItem); + switch( pAnchor->GetAnchorId()) + { + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL + case RndStdIds::FLY_AS_CHAR: + if( !pAnchor->GetContentAnchor() ) + { + pAnchor->SetAnchor( pCursor->GetPoint() ); + } + break; + case RndStdIds::FLY_AT_FLY: + if( !pAnchor->GetContentAnchor() ) + { + lcl_SetNewFlyPos( pCursor->GetNode(), + *pAnchor, GetCursorDocPos() ); + } + break; + case RndStdIds::FLY_AT_PAGE: + if( !pAnchor->GetPageNum() ) + { + pAnchor->SetPageNum( pCursor->GetPageNum( + true, &pCursor->GetPtPos() ) ); + } + break; + default : + break; + } + } + } + pFormat = GetDoc()->getIDocumentContentOperations().InsertGraphic( + *pCursor, rGrfName, + rFltName, pGraphic, + pFlyAttrSet, + nullptr, nullptr ); + OSL_ENSURE(pFormat, "IDocumentContentOperations::InsertGraphic failed."); + + pCursor = pCursor->GetNext(); + } while( pCursor != pStartCursor ); + + EndAllAction(); + + if( !pFormat ) + return; + + const Point aPt( GetCursorDocPos() ); + SwFlyFrame* pFrame = pFormat->GetFrame( &aPt ); + + if( pFrame ) + { + // add a redline to the anchor point at tracked insertion of picture + if ( IsRedlineOn() ) + { + SwPosition aPos(*pFormat->GetAnchor().GetContentAnchor()); + SwPaM aPaM(aPos.nNode.GetNode(), aPos.nContent.GetIndex(), + aPos.nNode.GetNode(), aPos.nContent.GetIndex() + 1); + GetDoc()->getIDocumentRedlineAccess().AppendRedline( + new SwRangeRedline( RedlineType::Insert, aPaM ), true); + } + + // fdo#36681: Invalidate the content and layout to refresh + // the picture anchoring properly + SwPageFrame* pPageFrame = pFrame->FindPageFrameOfAnchor(); + pPageFrame->InvalidateFlyLayout(); + pPageFrame->InvalidateContent(); + + SelectFlyFrame( *pFrame ); + } + else + GetLayout()->SetAssertFlyPages(); +} + +SwFlyFrameFormat* SwFEShell::InsertObject( const svt::EmbeddedObjectRef& xObj, + SfxItemSet* pFlyAttrSet ) +{ + SwFlyFrameFormat* pFormat = nullptr; + CurrShell aCurr( this ); + StartAllAction(); + { + for(const SwPaM& rPaM : GetCursor()->GetRingContainer()) + { + pFormat = GetDoc()->getIDocumentContentOperations().InsertEmbObject( + rPaM, xObj, pFlyAttrSet ); + OSL_ENSURE(pFormat, "IDocumentContentOperations::InsertEmbObject failed."); + } + } + EndAllAction(); + + if( pFormat ) + { + const Point aPt( GetCursorDocPos() ); + SwFlyFrame* pFrame = pFormat->GetFrame( &aPt ); + + if( pFrame ) + SelectFlyFrame( *pFrame ); + else + GetLayout()->SetAssertFlyPages(); + } + + return pFormat; +} + +void SwFEShell::InsertDrawObj( SdrObject& rDrawObj, + const Point& rInsertPosition ) +{ + CurrShell aCurr( this ); + + SfxItemSet rFlyAttrSet( GetDoc()->GetAttrPool(), aFrameFormatSetRange ); + rFlyAttrSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA )); + // #i89920# + rFlyAttrSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) ); + rDrawObj.SetLayer( getIDocumentDrawModelAccess().GetHeavenId() ); + + // find anchor position + SwPaM aPam( mxDoc->GetNodes() ); + { + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + Point aTmpPt( rInsertPosition ); + GetLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aTmpPt, &aState ); + const SwFrame* pFrame = aPam.GetContentNode()->getLayoutFrame(GetLayout(), nullptr, nullptr); + const Point aRelPos( rInsertPosition.X() - pFrame->getFrameArea().Left(), + rInsertPosition.Y() - pFrame->getFrameArea().Top() ); + rDrawObj.SetRelativePos( aRelPos ); + ::lcl_FindAnchorPos( *GetDoc(), rInsertPosition, *pFrame, rFlyAttrSet ); + } + // insert drawing object into the document creating a new <SwDrawFrameFormat> instance + SwDrawFrameFormat* pFormat = GetDoc()->getIDocumentContentOperations().InsertDrawObj( aPam, rDrawObj, rFlyAttrSet ); + + // move object to visible layer + SwContact* pContact = static_cast<SwContact*>(rDrawObj.GetUserCall()); + if ( pContact ) + { + pContact->MoveObjToVisibleLayer( &rDrawObj ); + } + + if (pFormat) + { + pFormat->SetName(rDrawObj.GetName()); + // select drawing object + Imp()->GetDrawView()->MarkObj( &rDrawObj, Imp()->GetPageView() ); + } + else + { + GetLayout()->SetAssertFlyPages(); + } +} + +void SwFEShell::GetPageObjs( std::vector<SwFrameFormat*>& rFillArr ) +{ + rFillArr.clear(); + + for( auto pFormat : *mxDoc->GetSpzFrameFormats() ) + { + if (RndStdIds::FLY_AT_PAGE == pFormat->GetAnchor().GetAnchorId()) + { + rFillArr.push_back( pFormat ); + } + } +} + +void SwFEShell::SetPageObjsNewPage( std::vector<SwFrameFormat*>& rFillArr ) +{ + if( rFillArr.empty() ) + return; + + StartAllAction(); + StartUndo(); + + SwRootFrame* pTmpRootFrame = GetLayout(); + sal_uInt16 nMaxPage = pTmpRootFrame->GetPageNum(); + bool bTmpAssert = false; + for( auto pFormat : rFillArr ) + { + if (mxDoc->GetSpzFrameFormats()->IsAlive(pFormat)) + { + // FlyFormat is still valid, therefore process + + SwFormatAnchor aNewAnchor( pFormat->GetAnchor() ); + if (RndStdIds::FLY_AT_PAGE != aNewAnchor.GetAnchorId()) + // Anchor has been changed, therefore: do not change! + continue; + sal_uInt16 nNewPage = aNewAnchor.GetPageNum() + 1; + if (nNewPage > nMaxPage) + { + if ( RES_DRAWFRMFMT == pFormat->Which() ) + pFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PAGE_OUT_OF_BOUNDS)); + else + pFormat->DelFrames(); + bTmpAssert = true; + } + aNewAnchor.SetPageNum(nNewPage); + mxDoc->SetAttr( aNewAnchor, *pFormat ); + } + } + + if( bTmpAssert ) + pTmpRootFrame->SetAssertFlyPages(); + + EndUndo(); + EndAllAction(); +} + +// All attributes in the "baskets" will be filled with the attributes of the +// current FlyFrames. Attributes which cannot be filled due to being at the +// wrong place or which are ambiguous (multiple selections) will be removed. +bool SwFEShell::GetFlyFrameAttr( SfxItemSet &rSet ) const +{ + SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame(); + if (!pFly) + { + OSL_ENSURE( false, "GetFlyFrameAttr, no Fly selected." ); + return false; + } + + CurrShell aCurr( const_cast<SwFEShell*>(this) ); + + if( !rSet.Set( pFly->GetFormat()->GetAttrSet() ) ) + return false; + + // now examine all attributes. Remove forbidden attributes, then + // get all remaining attributes and enter them + + if( const SwFormatAnchor* pAnchor = rSet.GetItemIfSet( RES_ANCHOR, false ) ) + { + RndStdIds eType = pAnchor->GetAnchorId(); + + if ( RndStdIds::FLY_AT_PAGE != eType ) + { + // OD 12.11.2003 #i22341# - content anchor of anchor item is needed. + // Thus, don't overwrite anchor item by default constructed anchor item. + if ( RndStdIds::FLY_AS_CHAR == eType ) + { + rSet.ClearItem( RES_OPAQUE ); + rSet.ClearItem( RES_SURROUND ); + } + } + } + rSet.SetParent( pFly->GetFormat()->GetAttrSet().GetParent() ); + // attributes must be removed + rSet.ClearItem( RES_FILL_ORDER ); + rSet.ClearItem( RES_CNTNT ); + //MA: remove first (Template by example etc.) + rSet.ClearItem( RES_CHAIN ); + return true; +} + +// Attributes of the current fly will change. +bool SwFEShell::SetFlyFrameAttr( SfxItemSet& rSet ) +{ + CurrShell aCurr( this ); + bool bRet = false; + + if( rSet.Count() ) + { + SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame(); + OSL_ENSURE(pFly, "SetFlyFrameAttr, no Fly selected."); + if (pFly) + { + StartAllAction(); + const Point aPt( pFly->getFrameArea().Pos() ); + + if( SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false )) + sw_ChkAndSetNewAnchor( *pFly, rSet ); + SwFlyFrameFormat* pFlyFormat = pFly->GetFormat(); + + if( GetDoc()->SetFlyFrameAttr( *pFlyFormat, rSet )) + { + bRet = true; + SwFlyFrame* pFrame = pFlyFormat->GetFrame( &aPt ); + if( pFrame ) + SelectFlyFrame( *pFrame ); + else + GetLayout()->SetAssertFlyPages(); + } + + EndAllActionAndCall(); + } + } + return bRet; +} + +SfxItemSetFixed<RES_VERT_ORIENT, RES_ANCHOR> SwFEShell::makeItemSetFromFormatAnchor(SfxItemPool& rPool, const SwFormatAnchor &rAnchor) +{ + // The set also includes VERT/HORI_ORIENT, because the align + // shall be changed in FEShell::SetFlyFrameAttr/SetFlyFrameAnchor, + // possibly as a result of the anchor change. + SfxItemSetFixed<RES_VERT_ORIENT, RES_ANCHOR> aSet(rPool); + aSet.Put(rAnchor); + return aSet; +} + +bool SwFEShell::SetDrawingAttr( SfxItemSet& rSet ) +{ + bool bRet = false; + CurrShell aCurr( this ); + if ( !rSet.Count() || + !Imp()->HasDrawView() ) + return bRet; + + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + if ( rMrkList.GetMarkCount() != 1 ) + return bRet; + + StartUndo(); + SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj(); + SwFrameFormat *pFormat = FindFrameFormat( pObj ); + StartAllAction(); + if( SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false )) + { + RndStdIds nNew = rSet.Get( RES_ANCHOR ).GetAnchorId(); + if ( nNew != pFormat->GetAnchor().GetAnchorId() ) + { + ChgAnchor( nNew ); + // #i26791# - clear anchor attribute in item set, + // because method <ChgAnchor(..)> takes care of it. + rSet.ClearItem( RES_ANCHOR ); + } + } + + if( GetDoc()->SetFlyFrameAttr( *pFormat, rSet )) + { + bRet = true; + SelectObj( Point(), 0, pObj ); + } + EndAllActionAndCall(); + EndUndo(); + return bRet; +} + +// Reset attributes contained in the set. +void SwFEShell::ResetFlyFrameAttr( const SfxItemSet* pSet ) +{ + CurrShell aCurr( this ); + + SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame(); + OSL_ENSURE( pFly, "SetFlyFrameAttr, no Fly selected." ); + if( !pFly ) + return; + + StartAllAction(); + + SfxItemIter aIter( *pSet ); + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + if( !IsInvalidItem( pItem ) ) + { + sal_uInt16 nWhich = pItem->Which(); + if( RES_ANCHOR != nWhich && RES_CHAIN != nWhich && RES_CNTNT != nWhich ) + pFly->GetFormat()->ResetFormatAttr( nWhich ); + } + } + + EndAllActionAndCall(); + GetDoc()->getIDocumentState().SetModified(); +} + +// Returns frame-format if frame, otherwise 0 +SwFrameFormat* SwFEShell::GetSelectedFrameFormat() const +{ + SwFrameFormat* pRet = nullptr; + SwLayoutFrame *pFly = GetSelectedFlyFrame(); + if( pFly && ( pRet = static_cast<SwFrameFormat*>(pFly->GetFormat()->DerivedFrom()) ) == + GetDoc()->GetDfltFrameFormat() ) + pRet = nullptr; + return pRet; +} + +void SwFEShell::SetFrameFormat( SwFrameFormat *pNewFormat, bool bKeepOrient, Point const * pDocPos ) +{ + SwFlyFrame *pFly = nullptr; + if(pDocPos) + { + const SwFrameFormat* pFormat = GetFormatFromObj( *pDocPos ); + + if (const SwFlyFrameFormat* pFlyFormat = dynamic_cast<const SwFlyFrameFormat*>(pFormat)) + pFly = pFlyFormat->GetFrame(); + } + else + pFly = GetSelectedFlyFrame(); + OSL_ENSURE( pFly, "SetFrameFormat: no frame" ); + if( !pFly ) + return; + + StartAllAction(); + CurrShell aCurr( this ); + + SwFlyFrameFormat* pFlyFormat = pFly->GetFormat(); + const Point aPt( pFly->getFrameArea().Pos() ); + + std::optional<SfxItemSet> oSet; + const SfxPoolItem* pItem; + if( SfxItemState::SET == pNewFormat->GetItemState( RES_ANCHOR, false, &pItem )) + { + oSet.emplace( GetDoc()->GetAttrPool(), aFrameFormatSetRange ); + oSet->Put( *pItem ); + if( !sw_ChkAndSetNewAnchor( *pFly, *oSet )) + { + oSet.reset(); + } + } + + if( GetDoc()->SetFrameFormatToFly( *pFlyFormat, *pNewFormat, oSet ? &*oSet : nullptr, bKeepOrient )) + { + SwFlyFrame* pFrame = pFlyFormat->GetFrame( &aPt ); + if( pFrame ) + SelectFlyFrame( *pFrame ); + else + GetLayout()->SetAssertFlyPages(); + } + oSet.reset(); + + EndAllActionAndCall(); +} + +const SwFrameFormat* SwFEShell::GetFlyFrameFormat() const +{ + const SwFlyFrame* pFly = GetSelectedOrCurrFlyFrame(); + if (pFly) + return pFly->GetFormat(); + return nullptr; +} + +SwFrameFormat* SwFEShell::GetFlyFrameFormat() +{ + SwFlyFrame* pFly = GetSelectedOrCurrFlyFrame(); + if (pFly) + return pFly->GetFormat(); + return nullptr; +} + +SwRect SwFEShell::GetFlyRect() const +{ + SwFlyFrame *pFly = GetCurrFlyFrame(false); + if (!pFly) + { + SwRect aRect; + return aRect; + } + else + return pFly->getFrameArea(); +} + +SwRect SwFEShell::GetObjRect() const +{ + if( Imp()->HasDrawView() ) + return SwRect(Imp()->GetDrawView()->GetAllMarkedRect()); + else + { + SwRect aRect; + return aRect; + } +} + +void SwFEShell::SetObjRect( const SwRect& rRect ) +{ + if ( Imp()->HasDrawView() ) + { + Imp()->GetDrawView()->SetAllMarkedRect( rRect.SVRect() ); + CallChgLnk(); // call AttrChangeNotify on the UI-side. + } +} + +Size SwFEShell::RequestObjectResize( const SwRect &rRect, const uno::Reference < embed::XEmbeddedObject >& xObj ) +{ + Size aResult; + + SwFlyFrame *pFly = FindFlyFrame( xObj ); + if ( !pFly ) + { + aResult = rRect.SSize(); + return aResult; + } + + aResult = pFly->getFramePrintArea().SSize(); + + bool bPosProt = pFly->GetFormat()->GetProtect().IsPosProtected(); + bool bSizeProt = pFly->GetFormat()->GetProtect().IsSizeProtected(); + + StartAllAction(); + + // MA we do not allow to clip the Fly, as the OLE server can + // request various wishes. Clipping is done via the formatting. + // Correct display is done by scaling. + // Scaling is done by SwNoTextFrame::Format by calling + // SwWrtShell::CalcAndSetScale() + if ( rRect.SSize() != pFly->getFramePrintArea().SSize() && !bSizeProt ) + { + Size aSz( rRect.SSize() ); + + //JP 28.02.2001: Task 74707 - ask for fly in fly with automatic size + + const SwFrame* pAnchor; + const SwFormatFrameSize& rFrameSz = pFly->GetFormat()->GetFrameSize(); + if (m_bCheckForOLEInCaption && + 0 != rFrameSz.GetWidthPercent() && + nullptr != (pAnchor = pFly->GetAnchorFrame()) && + pAnchor->IsTextFrame() && + !pAnchor->GetNext() && !pAnchor->GetPrev() && + pAnchor->GetUpper()->IsFlyFrame()) + { + // search for a sequence field: + sw::MergedAttrIter iter(*static_cast<SwTextFrame const*>(pAnchor)); + for (SwTextAttr const* pHint = iter.NextAttr(); pHint; pHint = iter.NextAttr()) + { + const SfxPoolItem* pItem = &pHint->GetAttr(); + if( RES_TXTATR_FIELD == pItem->Which() + && SwFieldTypesEnum::Sequence == static_cast<const SwFormatField*>(pItem)->GetField()->GetTypeId() ) + { + // sequence field found + SwFlyFrame* pChgFly = const_cast<SwFlyFrame*>(static_cast<const SwFlyFrame*>(pAnchor->GetUpper())); + // calculate the changed size: + // width must change, height can change + Size aNewSz( aSz.Width() + pChgFly->getFrameArea().Width() - + pFly->getFramePrintArea().Width(), aSz.Height() ); + + SwFrameFormat *pFormat = pChgFly->GetFormat(); + SwFormatFrameSize aFrameSz( pFormat->GetFrameSize() ); + aFrameSz.SetWidth( aNewSz.Width() ); + if( SwFrameSize::Minimum != aFrameSz.GetHeightSizeType() ) + { + aNewSz.AdjustHeight(pChgFly->getFrameArea().Height() - + pFly->getFramePrintArea().Height() ); + if( std::abs( aNewSz.Height() - pChgFly->getFrameArea().Height()) > 1 ) + aFrameSz.SetHeight( aNewSz.Height() ); + } + // via Doc for the Undo! + pFormat->GetDoc()->SetAttr( aFrameSz, *pFormat ); + break; + } + } + } + + // set the new Size at the fly themself + if ( !pFly->getFramePrintArea().IsEmpty() ) + { + aSz.AdjustWidth(pFly->getFrameArea().Width() - pFly->getFramePrintArea().Width() ); + aSz.AdjustHeight(pFly->getFrameArea().Height()- pFly->getFramePrintArea().Height() ); + } + aResult = pFly->ChgSize( aSz ); + + // if the object changes, the contour is outside the object + assert(pFly->Lower()->IsNoTextFrame()); + SwNoTextNode *pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetNoTextNode(); + assert(pNd); + pNd->SetContour( nullptr ); + ClrContourCache(); + } + + // if only the size is to be adjusted, a position is transported with + // allocated values + Point aPt( pFly->getFramePrintArea().Pos() ); + aPt += pFly->getFrameArea().Pos(); + if ( rRect.Top() != LONG_MIN && rRect.Pos() != aPt && !bPosProt ) + { + aPt = rRect.Pos(); + aPt.setX(aPt.getX() - pFly->getFramePrintArea().Left()); + aPt.setY(aPt.getY() - pFly->getFramePrintArea().Top()); + + // in case of paragraph-bound Flys, starting from the new position, + // a new anchor is to be set. The anchor and the new RelPos are + // calculated by the Fly and set + if( pFly->IsFlyAtContentFrame() ) + static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos( aPt ); + else + { + const SwFrameFormat *pFormat = pFly->GetFormat(); + const SwFormatVertOrient &rVert = pFormat->GetVertOrient(); + const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient(); + const tools::Long lXDiff = aPt.getX() - pFly->getFrameArea().Left(); + const tools::Long lYDiff = aPt.getY() - pFly->getFrameArea().Top(); + const Point aTmp( rHori.GetPos() + lXDiff, + rVert.GetPos() + lYDiff ); + pFly->ChgRelPos( aTmp ); + } + } + + SwFlyFrameFormat *pFlyFrameFormat = pFly->GetFormat(); + OSL_ENSURE( pFlyFrameFormat, "fly frame format missing!" ); + if ( pFlyFrameFormat ) + pFlyFrameFormat->SetLastFlyFramePrtRectPos( pFly->getFramePrintArea().Pos() ); //stores the value of last Prt rect + + EndAllAction(); + + return aResult; +} + +SwFrameFormat* SwFEShell::WizardGetFly() +{ + // do not search the Fly via the layout. Now we can delete a frame + // without a valid layout. ( e.g. for the wizards ) + SwFrameFormats& rSpzArr = *mxDoc->GetSpzFrameFormats(); + if( !rSpzArr.empty() ) + { + SwNodeIndex& rCursorNd = GetCursor()->GetPoint()->nNode; + if( rCursorNd.GetIndex() > mxDoc->GetNodes().GetEndOfExtras().GetIndex() ) + // Cursor is in the body area! + return nullptr; + + for( auto pFormat : rSpzArr ) + { + const SwNodeIndex* pIdx = pFormat->GetContent( false ).GetContentIdx(); + SwStartNode* pSttNd; + if( pIdx && + nullptr != ( pSttNd = pIdx->GetNode().GetStartNode() ) && + pSttNd->GetIndex() < rCursorNd.GetIndex() && + rCursorNd.GetIndex() < pSttNd->EndOfSectionIndex() ) + { + // found: return immediately + return pFormat; + } + } + } + return nullptr; +} + +void SwFEShell::SetFlyName( const OUString& rName ) +{ + SwLayoutFrame *pFly = GetSelectedFlyFrame(); + if( pFly ) + GetDoc()->SetFlyName( *static_cast<SwFlyFrameFormat*>(pFly->GetFormat()), rName ); + else { + OSL_ENSURE( false, "no FlyFrame selected" ); + } +} + +OUString SwFEShell::GetFlyName() const +{ + SwLayoutFrame *pFly = GetSelectedFlyFrame(); + if( pFly ) + return pFly->GetFormat()->GetName(); + + OSL_ENSURE( false, "no FlyFrame selected" ); + return OUString(); +} + +uno::Reference < embed::XEmbeddedObject > SwFEShell::GetOleRef() const +{ + uno::Reference < embed::XEmbeddedObject > xObj; + SwFlyFrame * pFly = GetSelectedFlyFrame(); + if (pFly && pFly->Lower() && pFly->Lower()->IsNoTextFrame()) + { + SwOLENode *pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetOLENode(); + if (pNd) + xObj = pNd->GetOLEObj().GetOleRef(); + } + return xObj; +} + +OUString SwFEShell::GetUniqueGrfName() const +{ + return GetDoc()->GetUniqueGrfName(); +} + +const SwFrameFormat* SwFEShell::IsURLGrfAtPos( const Point& rPt, OUString* pURL, + OUString *pTargetFrameName, + OUString *pDescription ) const +{ + if( !Imp()->HasDrawView() ) + return nullptr; + + SdrPageView* pPV; + const SwFrameFormat* pRet = nullptr; + SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView()); + + const auto nOld = pDView->GetHitTolerancePixel(); + pDView->SetHitTolerancePixel( 2 ); + + SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO); + SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj); + if (pFlyObj) + { + SwFlyFrame *pFly = pFlyObj->GetFlyFrame(); + const SwFormatURL &rURL = pFly->GetFormat()->GetURL(); + if( !rURL.GetURL().isEmpty() || rURL.GetMap() ) + { + bool bSetTargetFrameName = pTargetFrameName != nullptr; + bool bSetDescription = pDescription != nullptr; + if ( rURL.GetMap() ) + { + IMapObject *pObject = pFly->GetFormat()->GetIMapObject( rPt, pFly ); + if ( pObject && !pObject->GetURL().isEmpty() ) + { + if( pURL ) + *pURL = pObject->GetURL(); + if ( bSetTargetFrameName && !pObject->GetTarget().isEmpty() ) + { + bSetTargetFrameName = false; + *pTargetFrameName = pObject->GetTarget(); + } + if ( bSetDescription ) + { + bSetDescription = false; + *pDescription = pObject->GetAltText(); + } + pRet = pFly->GetFormat(); + } + } + else + { + if( pURL ) + { + *pURL = rURL.GetURL(); + if( rURL.IsServerMap() ) + { + // append the relative pixel position !! + Point aPt( rPt ); + aPt -= pFly->getFrameArea().Pos(); + // without MapMode-Offset, without Offset, o ... !!!!! + aPt = GetOut()->LogicToPixel( + aPt, MapMode( MapUnit::MapTwip ) ); + *pURL = *pURL + "?" + OUString::number( aPt.getX() ) + + "," + OUString::number(aPt.getY() ); + } + } + pRet = pFly->GetFormat(); + } + if ( bSetTargetFrameName ) + *pTargetFrameName = rURL.GetTargetFrameName(); + if ( bSetDescription ) + *pDescription = pFly->GetFormat()->GetName(); + } + } + pDView->SetHitTolerancePixel( nOld ); + return pRet; +} + +const Graphic *SwFEShell::GetGrfAtPos( const Point &rPt, + OUString &rName, bool &rbLink ) const +{ + if( !Imp()->HasDrawView() ) + return nullptr; + + SdrPageView* pPV; + SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView()); + + SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV); + SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj); + if (pFlyObj) + { + SwFlyFrame *pFly = pFlyObj->GetFlyFrame(); + if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() ) + { + SwGrfNode *const pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetGrfNode(); + if ( pNd ) + { + if ( pNd->IsGrfLink() ) + { + // halfway ready graphic? + ::sfx2::SvLinkSource* pLnkObj = pNd->GetLink()->GetObj(); + if( pLnkObj && pLnkObj->IsPending() ) + return nullptr; + rbLink = true; + } + + pNd->GetFileFilterNms( &rName, nullptr ); + if ( rName.isEmpty() ) + rName = pFly->GetFormat()->GetName(); + return &pNd->GetGrf(true); + } + } + } + return nullptr; +} + +const SwFrameFormat* SwFEShell::GetFormatFromObj( const Point& rPt, SwRect** pRectToFill ) const +{ + SwFrameFormat* pRet = nullptr; + + if( Imp()->HasDrawView() ) + { + SdrPageView* pPView; + + SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView()); + + const auto nOld = pDView->GetHitTolerancePixel(); + // tolerance for Drawing-SS + pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 ); + + SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE); + if (pObj) + { + // first check it: + if (SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj)) + pRet = pFlyObj->GetFormat(); + else if ( pObj->GetUserCall() ) //not for group objects + pRet = static_cast<SwDrawContact*>(pObj->GetUserCall())->GetFormat(); + if(pRet && pRectToFill) + **pRectToFill = SwRect(pObj->GetCurrentBoundRect()); + } + pDView->SetHitTolerancePixel( nOld ); + } + return pRet; +} + +// returns a format too, if the point is over the text of any fly +const SwFrameFormat* SwFEShell::GetFormatFromAnyObj( const Point& rPt ) const +{ + const SwFrameFormat* pRet = GetFormatFromObj( rPt ); + if( !pRet || RES_FLYFRMFMT == pRet->Which() ) + { + SwPosition aPos( *GetCursor()->GetPoint() ); + Point aPt( rPt ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aPt ); + SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode(); + std::pair<Point, bool> const tmp(rPt, false); + SwFrame* pFrame = pNd->getLayoutFrame(GetLayout(), nullptr, &tmp)->FindFlyFrame(); + pRet = pFrame ? static_cast<SwLayoutFrame*>(pFrame)->GetFormat() : nullptr; + } + return pRet; +} + +ObjCntType SwFEShell::GetObjCntType( const SdrObject& rObj ) +{ + ObjCntType eType = OBJCNT_NONE; + + // investigate 'master' drawing object, if method + // is called for a 'virtual' drawing object. + const SdrObject* pInvestigatedObj; + if (const SwDrawVirtObj* pDrawVirtObj = dynamic_cast<const SwDrawVirtObj*>( &rObj)) + { + pInvestigatedObj = &(pDrawVirtObj->GetReferencedObj()); + } + else + { + pInvestigatedObj = &rObj; + } + + if( SdrInventor::FmForm == pInvestigatedObj->GetObjInventor() ) + { + eType = OBJCNT_CONTROL; + uno::Reference< awt::XControlModel > xModel = + static_cast<const SdrUnoObj&>(*pInvestigatedObj).GetUnoControlModel(); + if( xModel.is() ) + { + uno::Any aVal; + OUString sName("ButtonType"); + uno::Reference< beans::XPropertySet > xSet(xModel, uno::UNO_QUERY); + + uno::Reference< beans::XPropertySetInfo > xInfo = xSet->getPropertySetInfo(); + if(xInfo->hasPropertyByName( sName )) + { + aVal = xSet->getPropertyValue( sName ); + if( aVal.hasValue() && form::FormButtonType_URL == *o3tl::doAccess<form::FormButtonType>(aVal) ) + eType = OBJCNT_URLBUTTON; + } + } + } + else if (const SwVirtFlyDrawObj *pFlyObj = dynamic_cast<const SwVirtFlyDrawObj*>(pInvestigatedObj)) + { + const SwFlyFrame *pFly = pFlyObj->GetFlyFrame(); + if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() ) + { + if (static_cast<const SwNoTextFrame*>(pFly->Lower())->GetNode()->GetGrfNode()) + eType = OBJCNT_GRF; + else + eType = OBJCNT_OLE; + } + else + eType = OBJCNT_FLY; + } + else if ( dynamic_cast<const SdrObjGroup*>( pInvestigatedObj) != nullptr ) + { + SwDrawContact* pDrawContact( dynamic_cast<SwDrawContact*>(GetUserCall( pInvestigatedObj ) ) ); + if ( !pDrawContact ) + { + OSL_FAIL( "<SwFEShell::GetObjCntType(..)> - missing draw contact object" ); + eType = OBJCNT_NONE; + } + else + { + SwFrameFormat* pFrameFormat( pDrawContact->GetFormat() ); + if ( !pFrameFormat ) + { + OSL_FAIL( "<SwFEShell::GetObjCntType(..)> - missing frame format" ); + eType = OBJCNT_NONE; + } + else if ( RndStdIds::FLY_AS_CHAR != pFrameFormat->GetAnchor().GetAnchorId() ) + { + eType = OBJCNT_GROUPOBJ; + } + } + } + else + eType = OBJCNT_SIMPLE; + return eType; +} + +ObjCntType SwFEShell::GetObjCntType( const Point &rPt, SdrObject *&rpObj ) const +{ + ObjCntType eType = OBJCNT_NONE; + + if( Imp()->HasDrawView() ) + { + SdrPageView* pPView; + + SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView()); + + const auto nOld = pDView->GetHitTolerancePixel(); + // tolerance for Drawing-SS + pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 ); + + SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE); + if (pObj) + { + rpObj = pObj; + eType = GetObjCntType( *rpObj ); + } + + pDView->SetHitTolerancePixel( nOld ); + } + return eType; +} + +ObjCntType SwFEShell::GetObjCntTypeOfSelection() const +{ + ObjCntType eType = OBJCNT_NONE; + + if( Imp()->HasDrawView() ) + { + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for( size_t i = 0, nE = rMrkList.GetMarkCount(); i < nE; ++i ) + { + SdrObject* pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + if( !pObj ) + continue; + ObjCntType eTmp = GetObjCntType( *pObj ); + if( !i ) + { + eType = eTmp; + } + else if( eTmp != eType ) + { + eType = OBJCNT_DONTCARE; + // once DontCare, always DontCare! + break; + } + } + } + return eType; +} + +void SwFEShell::ReplaceSdrObj( const OUString& rGrfName, const Graphic* pGrf ) +{ + CurrShell aCurr( this ); + + const SdrMarkList *pMrkList; + if( !(Imp()->HasDrawView() && 1 == + ( pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList())->GetMarkCount()) ) + return; + + SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj(); + SwFrameFormat *pFormat = FindFrameFormat( pObj ); + + // store attributes, then set the graphic + SfxItemSet aFrameSet( mxDoc->GetAttrPool(), + pFormat->GetAttrSet().GetRanges() ); + aFrameSet.Set( pFormat->GetAttrSet() ); + + // set size and position? + if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr ) + { + // then let's do it: + const tools::Rectangle &rBound = pObj->GetSnapRect(); + Point aRelPos( pObj->GetRelativePos() ); + + const tools::Long nWidth = rBound.Right() - rBound.Left(); + const tools::Long nHeight= rBound.Bottom() - rBound.Top(); + aFrameSet.Put( SwFormatFrameSize( SwFrameSize::Minimum, + std::max( nWidth, tools::Long(MINFLY) ), + std::max( nHeight, tools::Long(MINFLY) ))); + + if( SfxItemState::SET != aFrameSet.GetItemState( RES_HORI_ORIENT )) + aFrameSet.Put( SwFormatHoriOrient( aRelPos.getX(), text::HoriOrientation::NONE, text::RelOrientation::FRAME )); + + if( SfxItemState::SET != aFrameSet.GetItemState( RES_VERT_ORIENT )) + aFrameSet.Put( SwFormatVertOrient( aRelPos.getY(), text::VertOrientation::NONE, text::RelOrientation::FRAME )); + + } + + pObj->GetOrdNum(); + + StartAllAction(); + StartUndo(); + + // delete "Sdr-Object", insert the graphic instead + DelSelectedObj(); + + GetDoc()->getIDocumentContentOperations().InsertGraphic( + *GetCursor(), rGrfName, "", pGrf, &aFrameSet, nullptr, nullptr); + + EndUndo(); + EndAllAction(); +} + +static sal_uInt16 SwFormatGetPageNum(const SwFlyFrameFormat * pFormat) +{ + OSL_ENSURE(pFormat != nullptr, "invalid argument"); + + SwFlyFrame * pFrame = pFormat->GetFrame(); + + sal_uInt16 aResult; + + if (pFrame != nullptr) + aResult = pFrame->GetPhyPageNum(); + else + aResult = pFormat->GetAnchor().GetPageNum(); + + return aResult; +} + +void SwFEShell::GetConnectableFrameFormats(SwFrameFormat & rFormat, + const OUString & rReference, + bool bSuccessors, + std::vector< OUString > & aPrevPageVec, + std::vector< OUString > & aThisPageVec, + std::vector< OUString > & aNextPageVec, + std::vector< OUString > & aRestVec) +{ + StartAction(); + + SwFormatChain rChain = rFormat.GetChain(); + SwFrameFormat * pOldChainNext = rChain.GetNext(); + SwFrameFormat * pOldChainPrev = rChain.GetPrev(); + + if (pOldChainNext) + mxDoc->Unchain(rFormat); + + if (pOldChainPrev) + mxDoc->Unchain(*pOldChainPrev); + + const size_t nCnt = mxDoc->GetFlyCount(FLYCNTTYPE_FRM); + + /* potential successors resp. predecessors */ + std::vector< const SwFrameFormat * > aTmpSpzArray; + + mxDoc->FindFlyByName(rReference); + + for (size_t n = 0; n < nCnt; ++n) + { + const SwFrameFormat & rFormat1 = *(mxDoc->GetFlyNum(n, FLYCNTTYPE_FRM)); + + /* + pFormat is a potential successor of rFormat if it is chainable after + rFormat. + + pFormat is a potential predecessor of rFormat if rFormat is chainable + after pFormat. + */ + + SwChainRet nChainState; + + if (bSuccessors) + nChainState = mxDoc->Chainable(rFormat, rFormat1); + else + nChainState = mxDoc->Chainable(rFormat1, rFormat); + + if (nChainState == SwChainRet::OK) + { + aTmpSpzArray.push_back(&rFormat1); + + } + + } + + if (!aTmpSpzArray.empty()) + { + aPrevPageVec.clear(); + aThisPageVec.clear(); + aNextPageVec.clear(); + aRestVec.clear(); + + /* number of page rFormat resides on */ + sal_uInt16 nPageNum = SwFormatGetPageNum(static_cast<SwFlyFrameFormat *>(&rFormat)); + + for (const auto& rpFormat : aTmpSpzArray) + { + const OUString aString = rpFormat->GetName(); + + /* rFormat is not a valid successor or predecessor of + itself */ + if (aString != rReference && aString != rFormat.GetName()) + { + sal_uInt16 nNum1 = + SwFormatGetPageNum(static_cast<const SwFlyFrameFormat *>(rpFormat)); + + if (nNum1 == nPageNum -1) + aPrevPageVec.push_back(aString); + else if (nNum1 == nPageNum) + aThisPageVec.push_back(aString); + else if (nNum1 == nPageNum + 1) + aNextPageVec.push_back(aString); + else + aRestVec.push_back(aString); + } + } + + } + + if (pOldChainNext) + mxDoc->Chain(rFormat, *pOldChainNext); + + if (pOldChainPrev) + mxDoc->Chain(*pOldChainPrev, rFormat); + + EndAction(); +} + +// #i73249# +OUString SwFEShell::GetObjTitle() const +{ + if ( Imp()->HasDrawView() ) + { + const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + if ( pMrkList->GetMarkCount() == 1 ) + { + const SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj(); + const SwFrameFormat* pFormat = FindFrameFormat( pObj ); + if ( pFormat->Which() == RES_FLYFRMFMT ) + { + return static_cast<const SwFlyFrameFormat*>(pFormat)->GetObjTitle(); + } + return pObj->GetTitle(); + } + } + + return OUString(); +} + +void SwFEShell::SetObjTitle( const OUString& rTitle ) +{ + if ( !Imp()->HasDrawView() ) + return; + + const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + if ( pMrkList->GetMarkCount() != 1 ) + return; + + SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj(); + SwFrameFormat* pFormat = FindFrameFormat( pObj ); + if ( pFormat->Which() == RES_FLYFRMFMT ) + { + GetDoc()->SetFlyFrameTitle( dynamic_cast<SwFlyFrameFormat&>(*pFormat), + rTitle ); + } + else + { + pObj->SetTitle( rTitle ); + } +} + +OUString SwFEShell::GetObjDescription() const +{ + if ( Imp()->HasDrawView() ) + { + const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + if ( pMrkList->GetMarkCount() == 1 ) + { + const SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj(); + const SwFrameFormat* pFormat = FindFrameFormat( pObj ); + if ( pFormat->Which() == RES_FLYFRMFMT ) + { + return dynamic_cast<const SwFlyFrameFormat&>(*pFormat).GetObjDescription(); + } + return pObj->GetDescription(); + } + } + + return OUString(); +} + +void SwFEShell::SetObjDescription( const OUString& rDescription ) +{ + if ( !Imp()->HasDrawView() ) + return; + + const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + if ( pMrkList->GetMarkCount() != 1 ) + return; + + SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj(); + SwFrameFormat* pFormat = FindFrameFormat( pObj ); + if ( pFormat->Which() == RES_FLYFRMFMT ) + { + GetDoc()->SetFlyFrameDescription(dynamic_cast<SwFlyFrameFormat&>(*pFormat), + rDescription); + } + else + { + pObj->SetDescription( rDescription ); + } +} + +void SwFEShell::AlignFormulaToBaseline( const uno::Reference < embed::XEmbeddedObject >& xObj ) +{ +#if OSL_DEBUG_LEVEL > 0 + SvGlobalName aCLSID( xObj->getClassID() ); + const bool bStarMath = ( SotExchange::IsMath( aCLSID ) != 0 ); + OSL_ENSURE( bStarMath, "AlignFormulaToBaseline should only be called for Math objects" ); + + if ( !bStarMath ) + return; +#endif + + SwFlyFrame * pFly = FindFlyFrame( xObj ); + OSL_ENSURE( pFly , "No fly frame!" ); + SwFrameFormat * pFrameFormat = pFly ? pFly->GetFormat() : nullptr; + + // baseline to baseline alignment should only be applied to formulas anchored as char + if ( !pFly || !pFrameFormat || RndStdIds::FLY_AS_CHAR != pFrameFormat->GetAnchor().GetAnchorId() ) + return; + + // get baseline from Math object + uno::Any aBaseline; + if( svt::EmbeddedObjectRef::TryRunningState( xObj ) ) + { + uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY ); + if ( xSet.is() ) + { + try + { + aBaseline = xSet->getPropertyValue("BaseLine"); + } + catch ( uno::Exception& ) + { + OSL_FAIL( "Baseline could not be retrieved from Starmath!" ); + } + } + } + + sal_Int32 nBaseline = ::comphelper::getINT32(aBaseline); + nBaseline = o3tl::toTwips( nBaseline, o3tl::Length::mm100 ); + + OSL_ENSURE( nBaseline > 0, "Wrong value of Baseline while retrieving from Starmath!" ); + //nBaseline must be moved by aPrt position + const SwFlyFrameFormat *pFlyFrameFormat = pFly->GetFormat(); + OSL_ENSURE( pFlyFrameFormat, "fly frame format missing!" ); + if ( pFlyFrameFormat ) + nBaseline += pFlyFrameFormat->GetLastFlyFramePrtRectPos().Y(); + + const SwFormatVertOrient &rVert = pFrameFormat->GetVertOrient(); + SwFormatVertOrient aVert( rVert ); + aVert.SetPos( -nBaseline ); + aVert.SetVertOrient( css::text::VertOrientation::NONE ); + + pFrameFormat->LockModify(); + pFrameFormat->SetFormatAttr( aVert ); + pFrameFormat->UnlockModify(); + pFly->InvalidatePos(); + +} + +void SwFEShell::AlignAllFormulasToBaseline() +{ + StartAllAction(); + + SwStartNode *pStNd; + SwNodeIndex aIdx( *GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 ); + while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) ) + { + ++aIdx; + SwOLENode *pOleNode = dynamic_cast< SwOLENode * >( &aIdx.GetNode() ); + if ( pOleNode ) + { + const uno::Reference < embed::XEmbeddedObject > & xObj( pOleNode->GetOLEObj().GetOleRef() ); + if (xObj.is()) + { + SvGlobalName aCLSID( xObj->getClassID() ); + if ( SotExchange::IsMath( aCLSID ) ) + AlignFormulaToBaseline( xObj ); + } + } + + aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 ); + } + + EndAllAction(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/frmedt/feflyole.cxx b/sw/source/core/frmedt/feflyole.cxx new file mode 100644 index 000000000..efcbaaa17 --- /dev/null +++ b/sw/source/core/frmedt/feflyole.cxx @@ -0,0 +1,123 @@ +/* -*- 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 <sfx2/ipclient.hxx> +#include <sfx2/viewsh.hxx> +#include <osl/diagnose.h> + +#include <fesh.hxx> +#include <cntfrm.hxx> +#include <flyfrm.hxx> +#include <doc.hxx> +#include <notxtfrm.hxx> +#include <ndole.hxx> +#include <swcli.hxx> +#include <docsh.hxx> +#include <IDocumentLinksAdministration.hxx> +#include <sfx2/linkmgr.hxx> + +using namespace com::sun::star; + +SwFlyFrame *SwFEShell::FindFlyFrame( const uno::Reference < embed::XEmbeddedObject >& xObj ) const +{ + SwFlyFrame *pFly = GetSelectedFlyFrame(); + if ( pFly && pFly->Lower() && pFly->Lower()->IsNoTextFrame() ) + { + SwOLENode *pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetOLENode(); + if ( !pNd || pNd->GetOLEObj().GetOleRef() != xObj ) + pFly = nullptr; + } + else + pFly = nullptr; + + if ( !pFly ) + { + // No or wrong fly selected: we have to search. + bool bExist = false; + SwStartNode *pStNd; + SwNodeOffset nSttIdx = GetNodes().GetEndOfAutotext().StartOfSectionIndex() + 1, + nEndIdx = GetNodes().GetEndOfAutotext().GetIndex(); + while( nSttIdx < nEndIdx && + nullptr != (pStNd = GetNodes()[ nSttIdx ]->GetStartNode()) ) + { + SwNode *pNd = GetNodes()[ nSttIdx+1 ]; + if ( pNd->IsOLENode() && + static_cast<SwOLENode*>(pNd)->GetOLEObj().GetOleRef() == xObj ) + { + bExist = true; + SwFrame *pFrame = static_cast<SwOLENode*>(pNd)->getLayoutFrame( GetLayout() ); + if ( pFrame ) + pFly = pFrame->FindFlyFrame(); + break; + } + nSttIdx = pStNd->EndOfSectionIndex() + 1; + } + + OSL_ENSURE( bExist, "OLE-Object unknown and FlyFrame not found." ); + } + return pFly; +} + +OUString SwFEShell::GetUniqueOLEName() const +{ + return GetDoc()->GetUniqueOLEName(); +} + +OUString SwFEShell::GetUniqueFrameName() const +{ + return GetDoc()->GetUniqueFrameName(); +} + +bool SwFEShell::FinishOLEObj() // Server is terminated +{ + SfxInPlaceClient* pIPClient = GetSfxViewShell()->GetIPClient(); + if ( !pIPClient ) + return false; + + bool bRet = pIPClient->IsObjectInPlaceActive(); + if( bRet ) + { + if( CNT_OLE == GetCntType() ) + ClearAutomaticContour(); + + if( static_cast<SwOleClient*>(pIPClient)->IsCheckForOLEInCaption() != + IsCheckForOLEInCaption() ) + SetCheckForOLEInCaption( !IsCheckForOLEInCaption() ); + + // enable update of the link preview + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = GetDoc()->GetDocShell()->getEmbeddedObjectContainer(); + const bool aUserAllowsLinkUpdate = rEmbeddedObjectContainer.getUserAllowsLinkUpdate(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true); + + // leave UIActive state + pIPClient->DeactivateObject(); + + // if we have more than one link let's update them too + sfx2::LinkManager& rLinkManager = GetDoc()->getIDocumentLinksAdministration().GetLinkManager(); + if (rLinkManager.GetLinks().size() > 1) + rLinkManager.UpdateAllLinks(false, false, nullptr); + + // return back original value of the "update of the link preview" flag + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(aUserAllowsLinkUpdate); + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx new file mode 100644 index 000000000..8369004ac --- /dev/null +++ b/sw/source/core/frmedt/feshview.cxx @@ -0,0 +1,3360 @@ +/* -*- 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 <hintids.hxx> +#include <svx/strings.hrc> +#include <svx/sdrobjectfilter.hxx> +#include <svx/svddrgmt.hxx> +#include <svx/svditer.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdouno.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdocirc.hxx> +#include <svx/svdopath.hxx> +#include <svx/sxciaitm.hxx> +#include <svx/svdocapt.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/svdomeas.hxx> +#include <svx/sdtagitm.hxx> +#include <svx/sdtacitm.hxx> +#include <svx/sdtaaitm.hxx> +#include <editeng/opaqitem.hxx> +#include <editeng/protitem.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdpagv.hxx> +#include <svx/dialmgr.hxx> +#include <tools/globname.hxx> +#include <sot/exchange.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <IDocumentSettingAccess.hxx> +#include <DocumentSettingManager.hxx> +#include <IDocumentState.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <drawdoc.hxx> +#include <textboxhelper.hxx> +#include <frmfmt.hxx> +#include <frmatr.hxx> +#include <frmtool.hxx> +#include <fmtfsize.hxx> +#include <fmtanchr.hxx> +#include <fmtornt.hxx> +#include <fmtsrnd.hxx> +#include <fmtcntnt.hxx> +#include <fmtflcnt.hxx> +#include <fmtcnct.hxx> +#include <swmodule.hxx> +#include <fesh.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <sectfrm.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <dview.hxx> +#include <dflyobj.hxx> +#include <dcontact.hxx> +#include <viewimp.hxx> +#include <flyfrm.hxx> +#include <pam.hxx> +#include <ndole.hxx> +#include <ndgrf.hxx> +#include <ndtxt.hxx> +#include <viewopt.hxx> +#include <swundo.hxx> +#include <notxtfrm.hxx> +#include <txtfrm.hxx> +#include <mdiexp.hxx> +#include <sortedobjs.hxx> +#include <HandleAnchorNodeChg.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <calbck.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <svx/svxids.hrc> +#include <osl/diagnose.h> + +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <svx/srchdlg.hxx> + +#define SCROLLVAL 75 + +using namespace com::sun::star; + +/** + * set line starts and ends for the object to be created + */ + +namespace { + +::basegfx::B2DPolyPolygon getPolygon(TranslateId pResId, const SdrModel& rModel) +{ + ::basegfx::B2DPolyPolygon aRetval; + XLineEndListRef pLineEndList(rModel.GetLineEndList()); + + if( pLineEndList.is() ) + { + OUString aArrowName( SvxResId(pResId) ); + tools::Long nCount = pLineEndList->Count(); + tools::Long nIndex; + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + const XLineEndEntry* pEntry = pLineEndList->GetLineEnd(nIndex); + if( pEntry->GetName() == aArrowName ) + { + aRetval = pEntry->GetLineEnd(); + break; + } + } + } + + return aRetval; +} + +} + +SwFlyFrame *GetFlyFromMarked( const SdrMarkList *pLst, SwViewShell *pSh ) +{ + if ( !pLst ) + pLst = pSh->HasDrawView() ? &pSh->Imp()->GetDrawView()->GetMarkedObjectList():nullptr; + + if ( pLst && pLst->GetMarkCount() == 1 ) + { + SdrObject *pO = pLst->GetMark( 0 )->GetMarkedSdrObj(); + if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pO)) + return pVirtO->GetFlyFrame(); + } + return nullptr; +} + +static void lcl_GrabCursor( SwFEShell* pSh, SwFlyFrame* pOldSelFly) +{ + const SwFrameFormat *pFlyFormat = pSh->SelFlyGrabCursor(); + if( pFlyFormat && !pSh->ActionPend() && + (!pOldSelFly || pOldSelFly->GetFormat() != pFlyFormat) ) + { + // now call set macro if applicable + pSh->GetFlyMacroLnk().Call( static_cast<const SwFlyFrameFormat*>(pFlyFormat) ); + // if a dialog was started inside a macro, then + // MouseButtonUp arrives at macro and not to us. Therefore + // flag is always set here and will never be switched to + // respective Shell !!!!!!! + + g_bNoInterrupt = false; + } + else if( !pFlyFormat || RES_DRAWFRMFMT == pFlyFormat->Which() ) + { + // --> assure consistent cursor + pSh->KillPams(); + pSh->ClearMark(); + pSh->SetCursor( pSh->Imp()->GetDrawView()->GetAllMarkedRect().TopLeft(), true); + } +} + +bool SwFEShell::SelectObj( const Point& rPt, sal_uInt8 nFlag, SdrObject *pObj ) +{ + SwDrawView *pDView = Imp()->GetDrawView(); + if(!pDView) + return false; + CurrShell aCurr( this ); + StartAction(); // action is necessary to assure only one AttrChgdNotify + // (e.g. due to Unmark->MarkListHasChgd) arrives + + const SdrMarkList &rMrkList = pDView->GetMarkedObjectList(); + const bool bHadSelection = rMrkList.GetMarkCount(); + const bool bAddSelect = 0 != (SW_ADD_SELECT & nFlag); + const bool bEnterGroup = 0 != (SW_ENTER_GROUP & nFlag); + SwFlyFrame* pOldSelFly = nullptr; + const Point aOldPos( pDView->GetAllMarkedRect().TopLeft() ); + + if( bHadSelection ) + { + // call Unmark when !bAddSelect or if fly was selected + bool bUnmark = !bAddSelect; + + if ( rMrkList.GetMarkCount() == 1 ) + { + // if fly was selected, deselect it first + pOldSelFly = ::GetFlyFromMarked( &rMrkList, this ); + if ( pOldSelFly ) + { + const sal_uInt16 nType = GetCntType(); + if( nType != CNT_TXT || (SW_LEAVE_FRAME & nFlag) || + ( pOldSelFly->GetFormat()->GetProtect().IsContentProtected() + && !IsReadOnlyAvailable() )) + { + // If a fly is deselected, which contains graphic, OLE or + // otherwise, the cursor should be removed from it. + // Similar if a fly with protected content is deselected. + // For simplicity we put the cursor next to the upper-left + // corner. + Point aPt( pOldSelFly->getFrameArea().Pos() ); + aPt.setX(aPt.getX() - 1); + bool bUnLockView = !IsViewLocked(); + LockView( true ); + SetCursor( aPt, true ); + if( bUnLockView ) + LockView( false ); + } + if ( nType & CNT_GRF && + static_cast<SwNoTextFrame*>(pOldSelFly->Lower())->HasAnimation() ) + { + GetWin()->Invalidate( pOldSelFly->getFrameArea().SVRect() ); + } + + // Cancel crop mode + if ( SdrDragMode::Crop == GetDragMode() ) + SetDragMode( SdrDragMode::Move ); + + bUnmark = true; + } + } + if ( bUnmark ) + { + pDView->UnmarkAll(); + if (pOldSelFly) + pOldSelFly->SelectionHasChanged(this); + } + } + else + { + KillPams(); + ClearMark(); + } + + if ( pObj ) + { + OSL_ENSURE( !bEnterGroup, "SW_ENTER_GROUP is not supported" ); + pDView->MarkObj( pObj, Imp()->GetPageView() ); + } + else + { + // tolerance limit of Drawing-SS + const auto nHdlSizePixel = Imp()->GetDrawView()->GetMarkHdlSizePixel(); + const short nMinMove = static_cast<short>(GetOut()->PixelToLogic(Size(nHdlSizePixel/2, 0)).Width()); + pDView->MarkObj( rPt, nMinMove, bAddSelect, bEnterGroup ); + } + + const bool bRet = 0 != rMrkList.GetMarkCount(); + + if ( rMrkList.GetMarkCount() > 1 ) + { + // It sucks if Drawing objects were selected and now + // additionally a fly is selected. + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject *pTmpObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + bool bForget = dynamic_cast<const SwVirtFlyDrawObj*>( pTmpObj) != nullptr; + if( bForget ) + { + pDView->UnmarkAll(); + pDView->MarkObj( pTmpObj, Imp()->GetPageView(), bAddSelect, bEnterGroup ); + break; + } + } + } + + if ( rMrkList.GetMarkCount() == 1 ) + { + SwFlyFrame *pSelFly = ::GetFlyFromMarked( &rMrkList, this ); + if (pSelFly) + pSelFly->SelectionHasChanged(this); + } + + if (!(nFlag & SW_ALLOW_TEXTBOX)) + { + // If the fly frame is a textbox of a shape, then select the shape instead. + for (size_t i = 0; i < rMrkList.GetMarkCount(); ++i) + { + SdrObject* pObject = rMrkList.GetMark(i)->GetMarkedSdrObj(); + SwContact* pContact = GetUserCall(pObject); + if (!pContact) + { + continue; + } + + SwFrameFormat* pFormat = pContact->GetFormat(); + if (SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_FLYFRMFMT)) + { + SdrObject* pShape = pShapeFormat->FindSdrObject(); + pDView->UnmarkAll(); + pDView->MarkObj(pShape, Imp()->GetPageView(), bAddSelect, bEnterGroup); + break; + } + } + } + + if ( bRet ) + { + ::lcl_GrabCursor(this, pOldSelFly); + if ( GetCntType() & CNT_GRF ) + { + const SwFlyFrame *pTmp = GetFlyFromMarked( &rMrkList, this ); + OSL_ENSURE( pTmp, "Graphic without Fly" ); + if ( pTmp && static_cast<const SwNoTextFrame*>(pTmp->Lower())->HasAnimation() ) + static_cast<const SwNoTextFrame*>(pTmp->Lower())->StopAnimation( GetOut() ); + } + } + else if ( !pOldSelFly && bHadSelection ) + SetCursor( aOldPos, true); + + if( bRet || !bHadSelection ) + CallChgLnk(); + + // update status line + ::FrameNotify( this, bRet ? FLY_DRAG_START : FLY_DRAG_END ); + + EndAction(); + return bRet; +} + +/* + * Description: MoveAnchor( nDir ) looked for an another Anchor for + * the selected drawing object (or fly frame) in the given direction. + * An object "as character" doesn't moves anyway. + * A page bounded object could move to the previous/next page with up/down, + * an object bounded "at paragraph" moves to the previous/next paragraph, too. + * An object bounded "at character" moves to the previous/next paragraph + * with up/down and to the previous/next character with left/right. + * If the anchor for at paragraph/character bounded objects has vertical or + * right_to_left text direction, the directions for up/down/left/right will + * interpreted accordingly. + * An object bounded "at fly" takes the center of the actual anchor and looks + * for the nearest fly frame in the given direction. + */ + +static bool LessX( Point const & aPt1, Point const & aPt2, bool bOld ) +{ + return aPt1.getX() < aPt2.getX() + || ( aPt1.getX() == aPt2.getX() + && ( aPt1.getY() < aPt2.getY() + || ( aPt1.getY() == aPt2.getY() && bOld ) ) ); +} +static bool LessY( Point const & aPt1, Point const & aPt2, bool bOld ) +{ + return aPt1.getY() < aPt2.getY() + || ( aPt1.getY() == aPt2.getY() + && ( aPt1.getX() < aPt2.getX() + || ( aPt1.getX() == aPt2.getX() && bOld ) ) ); +} + +bool SwFEShell::MoveAnchor( SwMove nDir ) +{ + if (!Imp()->GetDrawView()) + return false; + const SdrMarkList& pMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + if (1 != pMrkList.GetMarkCount()) + return false; + SwFrame* pOld; + SwFlyFrame* pFly = nullptr; + SdrObject *pObj = pMrkList.GetMark( 0 )->GetMarkedSdrObj(); + if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj)) + { + pFly = pVirtO->GetFlyFrame(); + pOld = pFly->AnchorFrame(); + } + else + pOld = static_cast<SwDrawContact*>(GetUserCall(pObj))->GetAnchorFrame( pObj ); + bool bRet = false; + if( pOld ) + { + SwFrame* pNew = pOld; + // #i28701# + SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat(); + SwFormatAnchor aAnch( rFormat.GetAnchor() ); + RndStdIds nAnchorId = aAnch.GetAnchorId(); + if ( RndStdIds::FLY_AS_CHAR == nAnchorId ) + return false; + if( pOld->IsVertical() ) + { + if( pOld->IsTextFrame() ) + { + switch( nDir ) { + case SwMove::UP: nDir = SwMove::LEFT; break; + case SwMove::DOWN: nDir = SwMove::RIGHT; break; + case SwMove::LEFT: nDir = SwMove::DOWN; break; + case SwMove::RIGHT: nDir = SwMove::UP; break; + } + if( pOld->IsRightToLeft() ) + { + if( nDir == SwMove::LEFT ) + nDir = SwMove::RIGHT; + else if( nDir == SwMove::RIGHT ) + nDir = SwMove::LEFT; + } + } + } + switch ( nAnchorId ) { + case RndStdIds::FLY_AT_PAGE: + { + OSL_ENSURE( pOld->IsPageFrame(), "Wrong anchor, page expected." ); + if( SwMove::UP == nDir ) + pNew = pOld->GetPrev(); + else if( SwMove::DOWN == nDir ) + pNew = pOld->GetNext(); + if( pNew && pNew != pOld ) + { + aAnch.SetPageNum( static_cast<SwPageFrame*>(pNew)->GetPhyPageNum() ); + bRet = true; + } + break; + } + case RndStdIds::FLY_AT_CHAR: + { + OSL_ENSURE(pOld->IsTextFrame(), "Wrong anchor, text frame expected."); + if( SwMove::LEFT == nDir || SwMove::RIGHT == nDir ) + { + SwPosition pos = *aAnch.GetContentAnchor(); + SwTextFrame *const pOldFrame(static_cast<SwTextFrame*>(pOld)); + TextFrameIndex const nAct(pOldFrame->MapModelToViewPos(pos)); + if( SwMove::LEFT == nDir ) + { + bRet = true; + if( nAct ) + { + pos = pOldFrame->MapViewToModelPos(nAct - TextFrameIndex(1)); + } + else + nDir = SwMove::UP; + } + else + { + TextFrameIndex const nMax(pOldFrame->GetText().getLength()); + if( nAct < nMax ) + { + bRet = true; + pos = pOldFrame->MapViewToModelPos(nAct + TextFrameIndex(1)); + } + else + nDir = SwMove::DOWN; + } + if( pos != *aAnch.GetContentAnchor()) + aAnch.SetAnchor( &pos ); + } + [[fallthrough]]; + } + case RndStdIds::FLY_AT_PARA: + { + OSL_ENSURE(pOld->IsTextFrame(), "Wrong anchor, text frame expected."); + if( SwMove::UP == nDir ) + pNew = pOld->FindPrev(); + else if( SwMove::DOWN == nDir ) + pNew = pOld->FindNext(); + if( pNew && pNew != pOld && pNew->IsContentFrame() ) + { + SwTextFrame *const pNewFrame(static_cast<SwTextFrame*>(pNew)); + SwPosition const pos = pNewFrame->MapViewToModelPos( + TextFrameIndex( + (bRet && pNewFrame->GetText().getLength() != 0) + ? pNewFrame->GetText().getLength() - 1 + : 0)); + aAnch.SetAnchor( &pos ); + bRet = true; + } + else if( SwMove::UP == nDir || SwMove::DOWN == nDir ) + bRet = false; + break; + } + case RndStdIds::FLY_AT_FLY: + { + OSL_ENSURE( pOld->IsFlyFrame(), "Wrong anchor, fly frame expected."); + SwPageFrame* pPage = pOld->FindPageFrame(); + OSL_ENSURE( pPage, "Where's my page?" ); + SwFlyFrame* pNewFly = nullptr; + if( pPage->GetSortedObjs() ) + { + bool bOld = false; + Point aCenter( pOld->getFrameArea().Left() + pOld->getFrameArea().Width()/2, + pOld->getFrameArea().Top() + pOld->getFrameArea().Height()/2 ); + Point aBest; + for(SwAnchoredObject* pAnchObj : *pPage->GetSortedObjs()) + { + if( auto pTmp = pAnchObj->DynCastFlyFrame() ) + { + if( pTmp == pOld ) + bOld = true; + else + { + const SwFlyFrame* pCheck = pFly ? pTmp : nullptr; + while( pCheck ) + { + if( pCheck == pFly ) + break; + const SwFrame *pNxt = pCheck->GetAnchorFrame(); + pCheck = pNxt ? pNxt->FindFlyFrame() : nullptr; + } + if( pCheck || pTmp->IsProtected() ) + continue; + Point aNew( pTmp->getFrameArea().Left() + + pTmp->getFrameArea().Width()/2, + pTmp->getFrameArea().Top() + + pTmp->getFrameArea().Height()/2 ); + bool bAccept = false; + switch( nDir ) { + case SwMove::RIGHT: + { + bAccept = LessX( aCenter, aNew, bOld ) + && ( !pNewFly || + LessX( aNew, aBest, false ) ); + break; + } + case SwMove::LEFT: + { + bAccept = LessX( aNew, aCenter, !bOld ) + && ( !pNewFly || + LessX( aBest, aNew, true ) ); + break; + } + case SwMove::UP: + { + bAccept = LessY( aNew, aCenter, !bOld ) + && ( !pNewFly || + LessY( aBest, aNew, true ) ); + break; + } + case SwMove::DOWN: + { + bAccept = LessY( aCenter, aNew, bOld ) + && ( !pNewFly || + LessY( aNew, aBest, false ) ); + break; + } + } + if( bAccept ) + { + pNewFly = pTmp; + aBest = aNew; + } + } + } + } + } + + if( pNewFly ) + { + SwPosition aPos( *pNewFly->GetFormat()-> + GetContent().GetContentIdx()); + aAnch.SetAnchor( &aPos ); + bRet = true; + } + break; + } + default: break; + } + if( bRet ) + { + StartAllAction(); + // --> handle change of anchor node: + // if count of the anchor frame also change, the fly frames have to be + // re-created. Thus, delete all fly frames except the <this> before the + // anchor attribute is change and re-create them afterwards. + { + std::unique_ptr<SwHandleAnchorNodeChg> pHandleAnchorNodeChg; + SwFlyFrameFormat* pFlyFrameFormat( dynamic_cast<SwFlyFrameFormat*>(&rFormat) ); + if ( pFlyFrameFormat ) + { + pHandleAnchorNodeChg.reset( + new SwHandleAnchorNodeChg( *pFlyFrameFormat, aAnch )); + } + rFormat.GetDoc()->SetAttr( aAnch, rFormat ); + } + // #i28701# - no call of method + // <CheckCharRectAndTopOfLine()> for to-character anchored + // Writer fly frame needed. This method call can cause a + // format of the anchor frame, which is no longer intended. + // Instead clear the anchor character rectangle and + // the top of line values for all to-character anchored objects. + pAnchoredObj->ClearCharRectAndTopOfLine(); + EndAllAction(); + } + } + return bRet; +} + +const SdrMarkList* SwFEShell::GetMarkList_() const +{ + const SdrMarkList* pMarkList = nullptr; + if( Imp()->GetDrawView() != nullptr ) + pMarkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + return pMarkList; +} + +FrameTypeFlags SwFEShell::GetSelFrameType() const +{ + FrameTypeFlags eType; + + // get marked frame list, and check if anything is selected + const SdrMarkList* pMarkList = GetMarkList_(); + if( pMarkList == nullptr || pMarkList->GetMarkCount() == 0 ) + eType = FrameTypeFlags::NONE; + else + { + // obtain marked item as fly frame; if no fly frame, it must + // be a draw object + const SwFlyFrame* pFly = ::GetFlyFromMarked(pMarkList, const_cast<SwFEShell*>(this)); + if ( pFly != nullptr ) + { + if( pFly->IsFlyLayFrame() ) + eType = FrameTypeFlags::FLY_FREE; + else if( pFly->IsFlyAtContentFrame() ) + eType = FrameTypeFlags::FLY_ATCNT; + else + { + OSL_ENSURE( pFly->IsFlyInContentFrame(), "New frametype?" ); + eType = FrameTypeFlags::FLY_INCNT; + } + } + else + eType = FrameTypeFlags::DRAWOBJ; + } + + return eType; +} + +// does the draw selection contain a control? +bool SwFEShell::IsSelContainsControl() const +{ + bool bRet = false; + + // basically, copy the mechanism from GetSelFrameType(), but call + // CheckControl... if you get a drawing object + const SdrMarkList* pMarkList = GetMarkList_(); + if( pMarkList != nullptr && pMarkList->GetMarkCount() == 1 ) + { + // if we have one marked object, get the SdrObject and check + // whether it contains a control + const SdrObject* pSdrObject = pMarkList->GetMark( 0 )->GetMarkedSdrObj(); + bRet = pSdrObject && ::CheckControlLayer( pSdrObject ); + } + return bRet; +} + +void SwFEShell::ScrollTo( const Point &rPt ) +{ + const SwRect aRect( rPt, rPt ); + if ( IsScrollMDI( this, aRect ) && + (!Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() || + Imp()->IsDragPossible( rPt )) ) + { + ScrollMDI( this, aRect, SCROLLVAL, SCROLLVAL ); + } +} + +void SwFEShell::SetDragMode( SdrDragMode eDragMode ) +{ + if ( Imp()->HasDrawView() ) + Imp()->GetDrawView()->SetDragMode( eDragMode ); +} + +SdrDragMode SwFEShell::GetDragMode() const +{ + SdrDragMode nRet = SdrDragMode(0); + if ( Imp()->HasDrawView() ) + { + nRet = Imp()->GetDrawView()->GetDragMode(); + } + return nRet; +} + +void SwFEShell::StartCropImage() +{ + if ( !Imp()->HasDrawView() ) + { + return; + } + SdrView *pView = Imp()->GetDrawView(); + if (!pView) return; + + const SdrMarkList &rMarkList = pView->GetMarkedObjectList(); + if( 0 == rMarkList.GetMarkCount() ) { + // No object selected + return; + } + + // If more than a single SwVirtFlyDrawObj is selected, select only the first SwVirtFlyDrawObj + if ( rMarkList.GetMarkCount() > 1 ) + { + for ( size_t i = 0; i < rMarkList.GetMarkCount(); ++i ) + { + SdrObject *pTmpObj = rMarkList.GetMark( i )->GetMarkedSdrObj(); + bool bForget = dynamic_cast<const SwVirtFlyDrawObj*>( pTmpObj) != nullptr; + if( bForget ) + { + pView->UnmarkAll(); + pView->MarkObj( pTmpObj, Imp()->GetPageView() ); + break; + } + } + } + + // Activate CROP mode + pView->SetEditMode( SdrViewEditMode::Edit ); + SetDragMode( SdrDragMode::Crop ); +} + +void SwFEShell::BeginDrag( const Point* pPt, bool bIsShift) +{ + SdrView *pView = Imp()->GetDrawView(); + if ( pView && pView->AreObjectsMarked() ) + { + m_pChainFrom.reset(); + m_pChainTo.reset(); + SdrHdl* pHdl = pView->PickHandle( *pPt ); + if (pView->BegDragObj( *pPt, nullptr, pHdl )) + pView->GetDragMethod()->SetShiftPressed( bIsShift ); + ::FrameNotify( this ); + } +} + +void SwFEShell::Drag( const Point *pPt, bool ) +{ + OSL_ENSURE( Imp()->HasDrawView(), "Drag without DrawView?" ); + if ( HasDrawViewDrag() ) + { + ScrollTo( *pPt ); + Imp()->GetDrawView()->MovDragObj( *pPt ); + Imp()->GetDrawView()->ShowDragAnchor(); + ::FrameNotify( this ); + } +} + +void SwFEShell::EndDrag() +{ + OSL_ENSURE( Imp()->HasDrawView(), "EndDrag without DrawView?" ); + SdrView *pView = Imp()->GetDrawView(); + if ( !pView->IsDragObj() ) + return; + + for(SwViewShell& rSh : GetRingContainer()) + rSh.StartAction(); + + StartUndo( SwUndoId::START ); + + // #50778# Bug during dragging: In StartAction a HideShowXor is called. + // In EndDragObj() this is reversed, for no reason and even wrong. + // To restore consistency we should bring up the Xor again. + + // Reanimation from the hack #50778 to fix bug #97057 + // May be not the best solution, but the one with lowest risc at the moment. + // pView->ShowShownXor( GetOut() ); + + pView->EndDragObj(); + + // DrawUndo on to flyframes are not stored + // The flys change the flag. + GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(true); + ChgAnchor( RndStdIds::FLY_AT_PARA, true ); + + EndUndo( SwUndoId::END ); + + for(SwViewShell& rSh : GetRingContainer()) + { + rSh.EndAction(); + if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rSh) ) + pCursorShell->CallChgLnk(); + } + + GetDoc()->getIDocumentState().SetModified(); + ::FrameNotify( this ); +} + +void SwFEShell::BreakDrag() +{ + OSL_ENSURE( Imp()->HasDrawView(), "BreakDrag without DrawView?" ); + if( HasDrawViewDrag() ) + Imp()->GetDrawView()->BrkDragObj(); + SetChainMarker(); +} + +// If a fly is selected, pulls the crsr in the first ContentFrame +const SwFrameFormat* SwFEShell::SelFlyGrabCursor() +{ + if ( Imp()->HasDrawView() ) + { + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + SwFlyFrame *pFly = ::GetFlyFromMarked( &rMrkList, this ); + + if( pFly ) + { + SwContentFrame *pCFrame = pFly->ContainsContent(); + if ( pCFrame ) + { + // --> assure, that the cursor is consistent. + KillPams(); + ClearMark(); + SwPaM *pCursor = GetCursor(); + + if (pCFrame->IsTextFrame()) + { + *pCursor->GetPoint() = static_cast<SwTextFrame *>(pCFrame) + ->MapViewToModelPos(TextFrameIndex(0)); + } + else + { + assert(pCFrame->IsNoTextFrame()); + SwContentNode *const pCNode = static_cast<SwNoTextFrame *>(pCFrame)->GetNode(); + pCursor->GetPoint()->nNode = *pCNode; + pCursor->GetPoint()->nContent.Assign( pCNode, 0 ); + } + + SwRect& rChrRect = const_cast<SwRect&>(GetCharRect()); + rChrRect = pFly->getFramePrintArea(); + rChrRect.Pos() += pFly->getFrameArea().Pos(); + GetCursorDocPos() = rChrRect.Pos(); + } + return pFly->GetFormat(); + } + } + return nullptr; +} + +// Selection to above/below (Z-Order) +static void lcl_NotifyNeighbours( const SdrMarkList *pLst ) +{ + // Rules for evasion have changed. + // 1. The environment of the fly and everything inside should be notified + // 2. The content of the frame itself has to be notified + // 3. Frames displaced by the frame have to be notified + // 4. Also Drawing objects can displace frames + for( size_t j = 0; j < pLst->GetMarkCount(); ++j ) + { + SwPageFrame *pPage; + bool bCheckNeighbours = false; + sal_Int16 aHori = text::HoriOrientation::NONE; + SwRect aRect; + SdrObject *pO = pLst->GetMark( j )->GetMarkedSdrObj(); + if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pO)) + { + SwFlyFrame *pFly = pVirtO->GetFlyFrame(); + + const SwFormatHoriOrient &rHori = pFly->GetFormat()->GetHoriOrient(); + aHori = rHori.GetHoriOrient(); + if( text::HoriOrientation::NONE != aHori && text::HoriOrientation::CENTER != aHori && + pFly->IsFlyAtContentFrame() ) + { + bCheckNeighbours = true; + pFly->InvalidatePos(); + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly); + aFrm.Pos().AdjustY(1 ); + } + + pPage = pFly->FindPageFrame(); + aRect = pFly->getFrameArea(); + } + else + { + SwFrame* pAnch = static_cast<SwDrawContact*>( GetUserCall(pO) )->GetAnchorFrame( pO ); + if( !pAnch ) + continue; + pPage = pAnch->FindPageFrame(); + // #i68520# - naming changed + aRect = GetBoundRectOfAnchoredObj( pO ); + } + + const size_t nCount = pPage->GetSortedObjs() ? pPage->GetSortedObjs()->size() : 0; + for ( size_t i = 0; i < nCount; ++i ) + { + SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i]; + SwFlyFrame* pAct = pAnchoredObj->DynCastFlyFrame(); + if ( !pAct ) + continue; + + SwRect aTmpCalcPnt( pAct->getFramePrintArea() ); + aTmpCalcPnt += pAct->getFrameArea().Pos(); + if ( aRect.Overlaps( aTmpCalcPnt ) ) + { + SwContentFrame *pCnt = pAct->ContainsContent(); + while ( pCnt ) + { + aTmpCalcPnt = pCnt->getFramePrintArea(); + aTmpCalcPnt += pCnt->getFrameArea().Pos(); + if ( aRect.Overlaps( aTmpCalcPnt ) ) + static_cast<SwFrame*>(pCnt)->Prepare( PrepareHint::FlyFrameAttributesChanged ); + pCnt = pCnt->GetNextContentFrame(); + } + } + if ( bCheckNeighbours && pAct->IsFlyAtContentFrame() ) + { + const SwFormatHoriOrient &rH = pAct->GetFormat()->GetHoriOrient(); + if ( rH.GetHoriOrient() == aHori && + pAct->getFrameArea().Top() <= aRect.Bottom() && + pAct->getFrameArea().Bottom() >= aRect.Top() ) + { + pAct->InvalidatePos(); + SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pAct); + aFrm.Pos().AdjustY(1 ); + } + } + } + } +} + +void SwFEShell::SetLineEnds(SfxItemSet& rAttr, SdrObject const & rObj, sal_uInt16 nSlotId) +{ + SdrModel& rModel(rObj.getSdrModelFromSdrObject()); + + if ( !(nSlotId == SID_LINE_ARROW_START || + nSlotId == SID_LINE_ARROW_END || + nSlotId == SID_LINE_ARROWS || + nSlotId == SID_LINE_ARROW_CIRCLE || + nSlotId == SID_LINE_CIRCLE_ARROW || + nSlotId == SID_LINE_ARROW_SQUARE || + nSlotId == SID_LINE_SQUARE_ARROW || + nSlotId == SID_DRAW_MEASURELINE) ) + return; + + // set attributes of line start and ends + + // arrowhead + ::basegfx::B2DPolyPolygon aArrow( getPolygon( RID_SVXSTR_ARROW, rModel ) ); + if( !aArrow.count() ) + { + ::basegfx::B2DPolygon aNewArrow; + aNewArrow.append(::basegfx::B2DPoint(10.0, 0.0)); + aNewArrow.append(::basegfx::B2DPoint(0.0, 30.0)); + aNewArrow.append(::basegfx::B2DPoint(20.0, 30.0)); + aNewArrow.setClosed(true); + aArrow.append(aNewArrow); + } + + // Circles + ::basegfx::B2DPolyPolygon aCircle( getPolygon( RID_SVXSTR_CIRCLE, rModel ) ); + if( !aCircle.count() ) + { + ::basegfx::B2DPolygon aNewCircle = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 250.0, 250.0); + aNewCircle.setClosed(true); + aCircle.append(aNewCircle); + } + + // Square + ::basegfx::B2DPolyPolygon aSquare( getPolygon( RID_SVXSTR_SQUARE, rModel ) ); + if( !aSquare.count() ) + { + ::basegfx::B2DPolygon aNewSquare; + aNewSquare.append(::basegfx::B2DPoint(0.0, 0.0)); + aNewSquare.append(::basegfx::B2DPoint(10.0, 0.0)); + aNewSquare.append(::basegfx::B2DPoint(10.0, 10.0)); + aNewSquare.append(::basegfx::B2DPoint(0.0, 10.0)); + aNewSquare.setClosed(true); + aSquare.append(aNewSquare); + } + + SfxItemSet aSet( rModel.GetItemPool() ); + tools::Long nWidth = 100; // (1/100th mm) + + // determine line width and calculate with it the line end width + if( aSet.GetItemState( XATTR_LINEWIDTH ) != SfxItemState::DONTCARE ) + { + tools::Long nValue = aSet.Get( XATTR_LINEWIDTH ).GetValue(); + if( nValue > 0 ) + nWidth = nValue * 3; + } + + switch (nSlotId) + { + case SID_LINE_ARROWS: + case SID_DRAW_MEASURELINE: + { + // connector with arrow ends + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineStartWidthItem(nWidth)); + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_ARROW_SQUARE: + { + // connector with arrow start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + + case SID_LINE_ARROW_END: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_SQUARE_ARROW: + { + // connector with arrow end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + } + + // and again, for the still missing ends + switch (nSlotId) + { + case SID_LINE_ARROW_CIRCLE: + { + // circle end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_LINE_CIRCLE_ARROW: + { + // circle start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + + case SID_LINE_ARROW_SQUARE: + { + // square end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_SQUARE), aSquare)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_LINE_SQUARE_ARROW: + { + // square start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_SQUARE), aSquare)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + } + +} + +void SwFEShell::SelectionToTop( bool bTop ) +{ + OSL_ENSURE( Imp()->HasDrawView(), "SelectionToTop without DrawView?" ); + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + OSL_ENSURE( rMrkList.GetMarkCount(), "No object selected." ); + + SwFlyFrame *pFly = ::GetFlyFromMarked( &rMrkList, this ); + if ( pFly && pFly->IsFlyInContentFrame() ) + return; + + StartAllAction(); + if ( bTop ) + Imp()->GetDrawView()->PutMarkedToTop(); + else + Imp()->GetDrawView()->MovMarkedToTop(); + ::lcl_NotifyNeighbours( &rMrkList ); + + // Does the selection contain a textbox? + for (size_t i = 0; i < rMrkList.GetMarkCount(); i++) + if (auto pObj = rMrkList.GetMark(i)->GetMarkedSdrObj()) + // Get the textbox-shape + if (auto pFormat = FindFrameFormat(pObj)) + { + // If it has not textframe skip... + if (!SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT, pObj)) + continue; + // If it has a textframe so it is a textbox, get its page + if (auto pDrwModel + = pFormat->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()) + // Not really understood why everything is on page 0... + // but it is easier to handle sdrobjects, that's true + if (auto pPage = pDrwModel->GetPage(0)) + { + // nShift: it means how many layers the pObj have to be shifted up, + // in order not to interfere with other shapes and textboxes. + // Situations: + // - The next shape has textframe: This shape have to shifted with + // two layers. + // - The next shape has not got textframe: This shape have to be + // shifted only one layer up. + // - The next shape is null: + // - This shape is already at heaven: Only the textframe have + // to be adjusted. + sal_uInt32 nShift = 0; + // Get the one level higher object (note: can be nullptr!) + const auto pNextObj = pPage->SetObjectOrdNum(pObj->GetOrdNum() + 1, pObj->GetOrdNum() + 1); + // If there is a higher object (not null)... + if (pNextObj) + { + // One level shift is necessary + nShift++; + // If this object is a textbox, two level increasing needed + // (one for the shape and one for the frame) + if (auto pNextFormat = FindFrameFormat(pNextObj)) + if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT, pNextObj) + || SwTextBoxHelper::isTextBox(pNextFormat, RES_FLYFRMFMT)) + nShift++; + } + // Set the new z-order. + pPage->SetObjectOrdNum(pObj->GetOrdNum(), pObj->GetOrdNum() + nShift); + } + // The shape is on the right level, correct the layer of the frame + SwTextBoxHelper::DoTextBoxZOrderCorrection(pFormat, pObj); + } + + GetDoc()->getIDocumentState().SetModified(); + EndAllAction(); +} + +void SwFEShell::SelectionToBottom( bool bBottom ) +{ + OSL_ENSURE( Imp()->HasDrawView(), "SelectionToBottom without DrawView?" ); + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + OSL_ENSURE( rMrkList.GetMarkCount(), "No object selected." ); + + SwFlyFrame *pFly = ::GetFlyFromMarked( &rMrkList, this ); + if ( pFly && pFly->IsFlyInContentFrame() ) + return; + + StartAllAction(); + if ( bBottom ) + Imp()->GetDrawView()->PutMarkedToBtm(); + else + Imp()->GetDrawView()->MovMarkedToBtm(); + ::lcl_NotifyNeighbours( &rMrkList ); + + // If the selection has textbox + for(size_t i = 0; i < rMrkList.GetMarkCount(); i++) + if (auto pObj = rMrkList.GetMark(i)->GetMarkedSdrObj()) + // Get the shape of the textbox + if (auto pFormat = FindFrameFormat(pObj)) + { + // If the shape has not textframes skip. + if (!SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT, pObj)) + continue; + // If has, move the shape to correct level with... + if (auto pDrwModel + = pFormat->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()) + if (auto pPage = pDrwModel->GetPage(0)) + { + const auto pNextObj = pPage->SetObjectOrdNum(pObj->GetOrdNum() - 1, pObj->GetOrdNum() - 1); + // If there is a lower object (not null)... + if (pNextObj) + { + // If the lower has no textframe, just do nothing, else move by one lower + if (auto pNextFormat = FindFrameFormat(pNextObj)) + if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT, pNextObj) + || SwTextBoxHelper::isTextBox(pNextFormat, RES_FLYFRMFMT)) + pPage->SetObjectOrdNum(pObj->GetOrdNum(), pObj->GetOrdNum() - 1); + } + } + // And set correct layer for the selected textbox. + SwTextBoxHelper::DoTextBoxZOrderCorrection(pFormat, pObj); + } + + GetDoc()->getIDocumentState().SetModified(); + EndAllAction(); +} + +// Object above/below the document? 2 Controls, 1 Heaven, 0 Hell, +// SDRLAYER_NOTFOUND Ambiguous +SdrLayerID SwFEShell::GetLayerId() const +{ + if ( !Imp()->HasDrawView() ) + return SDRLAYER_NOTFOUND; + + SdrLayerID nRet = SDRLAYER_NOTFOUND; + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + const SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + if( !pObj ) + continue; + if ( nRet == SDRLAYER_NOTFOUND ) + nRet = pObj->GetLayer(); + else if ( nRet != pObj->GetLayer() ) + { + return SDRLAYER_NOTFOUND; + } + } + return nRet; +} + +// Object above/below the document +// Note: only visible objects can be marked. Thus, objects with invisible +// layer IDs have not to be considered. +// If <SwFEShell> exists, layout exists!! +void SwFEShell::ChangeOpaque( SdrLayerID nLayerId ) +{ + if ( !Imp()->HasDrawView() ) + return; + + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + const IDocumentDrawModelAccess& rIDDMA = getIDocumentDrawModelAccess(); + // correct type of <nControls> + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject* pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + if( !pObj ) + continue; + // or group objects containing controls. + // --> #i113730# + // consider that a member of a drawing group has been selected. + const SwContact* pContact = ::GetUserCall( pObj ); + OSL_ENSURE( pContact && pContact->GetMaster(), "<SwFEShell::ChangeOpaque(..)> - missing contact or missing master object at contact!" ); + const bool bControlObj = ( pContact && pContact->GetMaster() ) + ? ::CheckControlLayer( pContact->GetMaster() ) + : ::CheckControlLayer( pObj ); + if ( !bControlObj && pObj->GetLayer() != nLayerId ) + { + pObj->SetLayer( nLayerId ); + InvalidateWindows( SwRect( pObj->GetCurrentBoundRect() ) ); + if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj)) + { + SwFormat *pFormat = pVirtO->GetFlyFrame()->GetFormat(); + SvxOpaqueItem aOpa( pFormat->GetOpaque() ); + aOpa.SetValue( nLayerId == rIDDMA.GetHellId() ); + pFormat->SetFormatAttr( aOpa ); + // If pObj has textframe, put its textframe to the right level + if (auto pTextBx = FindFrameFormat(pObj)) + SwTextBoxHelper::DoTextBoxZOrderCorrection(pTextBx, pObj); + } + } + } + GetDoc()->getIDocumentState().SetModified(); +} + +void SwFEShell::SelectionToHeaven() +{ + ChangeOpaque( getIDocumentDrawModelAccess().GetHeavenId() ); +} + +void SwFEShell::SelectionToHell() +{ + ChangeOpaque( getIDocumentDrawModelAccess().GetHellId() ); +} + +size_t SwFEShell::IsObjSelected() const +{ + if ( IsFrameSelected() || !Imp()->HasDrawView() ) + return 0; + + return Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount(); +} + +bool SwFEShell::IsFrameSelected() const +{ + if ( !Imp()->HasDrawView() ) + return false; + else + return nullptr != ::GetFlyFromMarked( &Imp()->GetDrawView()->GetMarkedObjectList(), + const_cast<SwFEShell*>(this) ); +} + +bool SwFEShell::IsObjSelected( const SdrObject& rObj ) const +{ + if ( IsFrameSelected() || !Imp()->HasDrawView() ) + return false; + else + return Imp()->GetDrawView()->IsObjMarked( &rObj ); +} + +bool SwFEShell::IsRotationOfSwGrfNodePossible() const +{ + // RotGrfFlyFrame: check if RotationMode is possible + const SdrView *pSdrView = Imp()->GetDrawView(); + + if(pSdrView) + { + const SdrMarkList& rList(pSdrView->GetMarkedObjectList()); + + if(1 == rList.GetMarkCount()) + { + const SwVirtFlyDrawObj* pVirtFlyDraw(dynamic_cast< const SwVirtFlyDrawObj* >(rList.GetMark(0)->GetMarkedSdrObj())); + + if(nullptr != pVirtFlyDraw) + { + return pVirtFlyDraw->ContainsSwGrfNode(); + } + } + } + + return false; +} + +bool SwFEShell::IsObjSameLevelWithMarked(const SdrObject* pObj) const +{ + if (pObj) + { + const SdrMarkList& aMarkList = Imp()->GetDrawView()->GetMarkedObjectList(); + if (aMarkList.GetMarkCount() == 0) + { + return true; + } + SdrMark* pM=aMarkList.GetMark(0); + if (pM) + { + SdrObject* pMarkObj = pM->GetMarkedSdrObj(); + if (pMarkObj && pMarkObj->getParentSdrObjectFromSdrObject() == pObj->getParentSdrObjectFromSdrObject()) + return true; + } + } + return false; +} + +void SwFEShell::EndTextEdit() +{ + // Terminate the TextEditMode. If required (default if the object + // does not contain any more text and does not carry attributes) the object + // is deleted. All other objects marked are preserved. + + OSL_ENSURE( Imp()->HasDrawView() && Imp()->GetDrawView()->IsTextEdit(), + "EndTextEdit a no Object" ); + + StartAllAction(); + SdrView *pView = Imp()->GetDrawView(); + SdrObject *pObj = pView->GetTextEditObject(); + SdrObjUserCall* pUserCall = GetUserCall(pObj); + if( nullptr != pUserCall ) + { + SdrObject *pTmp = static_cast<SwContact*>(pUserCall)->GetMaster(); + if( !pTmp ) + pTmp = pObj; + pUserCall->Changed( *pTmp, SdrUserCallType::Resize, pTmp->GetLastBoundRect() ); + } + if ( !pObj->getParentSdrObjectFromSdrObject() ) + { + if ( SdrEndTextEditKind::ShouldBeDeleted == pView->SdrEndTextEdit(true) ) + { + if ( pView->GetMarkedObjectList().GetMarkCount() > 1 ) + { + SdrMarkList aSave( pView->GetMarkedObjectList() ); + aSave.DeleteMark( aSave.FindObject( pObj ) ); + if ( aSave.GetMarkCount() ) + { + pView->UnmarkAll(); + pView->MarkObj( pObj, Imp()->GetPageView() ); + } + DelSelectedObj(); + for ( size_t i = 0; i < aSave.GetMarkCount(); ++i ) + pView->MarkObj( aSave.GetMark( i )->GetMarkedSdrObj(), Imp()->GetPageView() ); + } + else + DelSelectedObj(); + } + } + else + pView->SdrEndTextEdit(); + + if (comphelper::LibreOfficeKit::isActive()) + SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_LOCK, "rectangle", "EMPTY"); + + EndAllAction(); +} + +bool SwFEShell::IsInsideSelectedObj( const Point &rPt ) +{ + if( Imp()->HasDrawView() ) + { + SwDrawView *pDView = Imp()->GetDrawView(); + + if( pDView->GetMarkedObjectList().GetMarkCount() && + pDView->IsMarkedObjHit( rPt ) ) + { + return true; + } + } + return false; +} + +bool SwFEShell::IsObjSelectable( const Point& rPt ) +{ + CurrShell aCurr(this); + SwDrawView *pDView = Imp()->GetDrawView(); + bool bRet = false; + if( pDView ) + { + SdrPageView* pPV; + const auto nOld = pDView->GetHitTolerancePixel(); + pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 ); + + bRet = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV, SdrSearchOptions::PICKMARKABLE) != nullptr; + pDView->SetHitTolerancePixel( nOld ); + } + return bRet; +} + +SdrObject* SwFEShell::GetObjAt( const Point& rPt ) +{ + SdrObject* pRet = nullptr; + CurrShell aCurr(this); + SwDrawView *pDView = Imp()->GetDrawView(); + if( pDView ) + { + SdrPageView* pPV; + const auto nOld = pDView->GetHitTolerancePixel(); + pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 ); + + pRet = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV, SdrSearchOptions::PICKMARKABLE); + pDView->SetHitTolerancePixel( nOld ); + } + return pRet; +} + +// Test if there is an object at that position and if it should be selected. +bool SwFEShell::ShouldObjectBeSelected(const Point& rPt) +{ + CurrShell aCurr(this); + SwDrawView *pDrawView = Imp()->GetDrawView(); + bool bRet(false); + + if(pDrawView) + { + SdrPageView* pPV; + const auto nOld(pDrawView->GetHitTolerancePixel()); + + pDrawView->SetHitTolerancePixel(pDrawView->GetMarkHdlSizePixel()/2); + SdrObject* pObj = pDrawView->PickObj(rPt, pDrawView->getHitTolLog(), pPV, SdrSearchOptions::PICKMARKABLE); + pDrawView->SetHitTolerancePixel(nOld); + + if (pObj) + { + bRet = true; + const IDocumentDrawModelAccess& rIDDMA = getIDocumentDrawModelAccess(); + // #i89920# + // Do not select object in background which is overlapping this text + // at the given position. + bool bObjInBackground( false ); + { + if ( pObj->GetLayer() == rIDDMA.GetHellId() ) + { + const SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj ); + const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat(); + const SwFormatSurround& rSurround = rFormat.GetSurround(); + if ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) + { + bObjInBackground = true; + } + } + } + if ( bObjInBackground ) + { + const SwPageFrame* pPageFrame = GetLayout()->GetPageAtPos( rPt ); + if( pPageFrame ) + { + const SwContentFrame* pContentFrame( pPageFrame->ContainsContent() ); + while ( pContentFrame ) + { + if ( pContentFrame->UnionFrame().Contains( rPt ) ) + { + const SwTextFrame* pTextFrame = pContentFrame->DynCastTextFrame(); + if ( pTextFrame ) + { + SwPosition aPos(GetDoc()->GetNodes()); + Point aTmpPt( rPt ); + if (pTextFrame->GetKeyCursorOfst(&aPos, aTmpPt)) + { + SwRect aCursorCharRect; + if (pTextFrame->GetCharRect(aCursorCharRect, + aPos)) + { + if ( aCursorCharRect.Overlaps( SwRect( pObj->GetLastBoundRect() ) ) ) + { + bRet = false; + } + } + } + } + else + { + bRet = false; + } + break; + } + + pContentFrame = pContentFrame->GetNextContentFrame(); + } + } + } + + // Don't select header / footer objects in body edition and vice-versa + SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall()); + if (pContact && !pContact->ObjAnchoredAtPage() ) + { + const SwPosition& rPos = pContact->GetContentAnchor(); + bool bInHdrFtr = GetDoc()->IsInHeaderFooter( rPos.nNode ); + if (IsHeaderFooterEdit() != bInHdrFtr) + { + bRet = false; + } + } + + if ( bRet ) + { + const SdrPage* pPage = rIDDMA.GetDrawModel()->GetPage(0); + for(size_t a = pObj->GetOrdNum()+1; bRet && a < pPage->GetObjCount(); ++a) + { + SdrObject *pCandidate = pPage->GetObj(a); + + SwVirtFlyDrawObj* pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pCandidate); + if (pDrawObj && pDrawObj->GetCurrentBoundRect().Contains(rPt)) + { + bRet = false; + } + } + } + } + } + + return bRet; +} + +/* + * If an object was selected, we assume its upper-left corner + * otherwise the middle of the current CharRects. + * Does the object include a control or groups, + * which comprise only controls + */ +static bool lcl_IsControlGroup( const SdrObject *pObj ) +{ + bool bRet = false; + if(dynamic_cast<const SdrUnoObj*>( pObj) != nullptr) + bRet = true; + else if( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObj) ) + { + bRet = true; + const SdrObjList *pLst = pObjGroup->GetSubList(); + for ( size_t i = 0; i < pLst->GetObjCount(); ++i ) + if( !::lcl_IsControlGroup( pLst->GetObj( i ) ) ) + return false; + } + return bRet; +} + +namespace +{ + class MarkableObjectsOnly : public svx::ISdrObjectFilter + { + public: + explicit MarkableObjectsOnly( SdrPageView* i_pPV ) + :m_pPV( i_pPV ) + { + } + + virtual bool includeObject( const SdrObject& i_rObject ) const override + { + return m_pPV && m_pPV->GetView().IsObjMarkable( &i_rObject, m_pPV ); + } + + private: + SdrPageView* m_pPV; + }; +} + +const SdrObject* SwFEShell::GetBestObject(bool bNext, GotoObjFlags eType, bool bFlat, + const svx::ISdrObjectFilter* pFilter, + bool* pbWrapped) +{ + if (pbWrapped) + *pbWrapped = false; + + if( !Imp()->HasDrawView() ) + return nullptr; + + const SdrObject *pBest = nullptr, + *pTop = nullptr; + + const tools::Long nTmp = bNext ? LONG_MAX : 0; + Point aBestPos( nTmp, nTmp ); + Point aTopPos( nTmp, nTmp ); + Point aCurPos; + Point aPos; + bool bNoDraw((GotoObjFlags::DrawAny & eType) == GotoObjFlags::NONE); + bool bNoFly((GotoObjFlags::FlyAny & eType) == GotoObjFlags::NONE); + + if( !bNoFly && bNoDraw ) + { + SwFlyFrame *pFly = GetCurrFrame( false )->FindFlyFrame(); + if( pFly ) + pBest = pFly->GetVirtDrawObj(); + } + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + SdrPageView* pPV = Imp()->GetDrawView()->GetSdrPageView(); + + MarkableObjectsOnly aDefaultFilter( pPV ); + if ( !pFilter ) + pFilter = &aDefaultFilter; + + if( !pBest || rMrkList.GetMarkCount() == 1 ) + { + // Determine starting point + SdrObjList* pList = nullptr; + if ( rMrkList.GetMarkCount() ) + { + const SdrObject* pStartObj = rMrkList.GetMark(0)->GetMarkedSdrObj(); + if( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>( pStartObj) ) + aPos = pVirtFlyDrawObj->GetFlyFrame()->getFrameArea().Pos(); + else + aPos = pStartObj->GetSnapRect().TopLeft(); + + // If an object inside a group is selected, we want to + // iterate over the group members. + if ( ! pStartObj->GetUserCall() ) + pList = pStartObj->getParentSdrObjListFromSdrObject(); + } + else + { + // If no object is selected, we check if we just entered a group. + // In this case we want to iterate over the group members. + aPos = GetCharRect().Center(); + const SdrObject* pStartObj = pPV ? pPV->GetCurrentGroup() : nullptr; + if ( dynamic_cast<const SdrObjGroup*>( pStartObj) ) + pList = pStartObj->GetSubList(); + } + + if ( ! pList ) + { + // Here we are if + // A No object has been selected and no group has been entered or + // B An object has been selected and it is not inside a group + pList = getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 ); + } + + OSL_ENSURE( pList, "No object list to iterate" ); + + SdrObjListIter aObjIter( pList, bFlat ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups ); + while ( aObjIter.IsMore() ) + { + SdrObject* pObj = aObjIter.Next(); + SwVirtFlyDrawObj *pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj); + if( ( bNoFly && pVirtO ) || + ( bNoDraw && !pVirtO ) || + // Ignore TextBoxes of draw shapes here, so that + // SwFEShell::SelectObj() won't jump back on this list, meaning + // we never jump to the next draw shape. + (pVirtO && pVirtO->IsTextBox()) || + ( eType == GotoObjFlags::DrawSimple && lcl_IsControlGroup( pObj ) ) || + ( eType == GotoObjFlags::DrawControl && !lcl_IsControlGroup( pObj ) ) || + !pFilter->includeObject( *pObj ) ) + continue; + if (pVirtO) + { + SwFlyFrame *pFly = pVirtO->GetFlyFrame(); + if( GotoObjFlags::FlyAny != ( GotoObjFlags::FlyAny & eType ) ) + { + switch ( eType ) + { + case GotoObjFlags::FlyFrame: + if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() ) + continue; + break; + case GotoObjFlags::FlyGrf: + if ( pFly->Lower() && + (!pFly->Lower()->IsNoTextFrame() || + !static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetGrfNode())) + continue; + break; + case GotoObjFlags::FlyOLE: + if ( pFly->Lower() && + (!pFly->Lower()->IsNoTextFrame() || + !static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetOLENode())) + continue; + break; + default: break; + } + } + aCurPos = pFly->getFrameArea().Pos(); + } + else + aCurPos = pObj->GetSnapRect().TopLeft(); + + // Special case if another object is on same Y. + if( aCurPos != aPos && // only when it is not me + aCurPos.getY() == aPos.getY() && // Y positions equal + (bNext? (aCurPos.getX() > aPos.getX()) : // lies next to me + (aCurPos.getX() < aPos.getX())) ) // " reverse + { + aBestPos = Point( nTmp, nTmp ); + SdrObjListIter aTmpIter( pList, bFlat ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups ); + while ( aTmpIter.IsMore() ) + { + SdrObject* pTmpObj = aTmpIter.Next(); + pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pTmpObj); + if( ( bNoFly && pVirtO ) || ( bNoDraw && !pVirtO ) ) + continue; + if (pVirtO) + { + aCurPos = pVirtO->GetFlyFrame()->getFrameArea().Pos(); + } + else + aCurPos = pTmpObj->GetCurrentBoundRect().TopLeft(); + + if( aCurPos != aPos && aCurPos.Y() == aPos.Y() && + (bNext? (aCurPos.getX() > aPos.getX()) : // lies next to me + (aCurPos.getX() < aPos.getX())) && // " reverse + (bNext? (aCurPos.getX() < aBestPos.getX()) : // better as best + (aCurPos.getX() > aBestPos.getX())) ) // " reverse + { + aBestPos = aCurPos; + pBest = pTmpObj; + } + } + break; + } + + if( ( + (bNext? (aPos.getY() < aCurPos.getY()) : // only below me + (aPos.getY() > aCurPos.getY())) && // " reverse + (bNext? (aBestPos.getY() > aCurPos.getY()) : // closer below + (aBestPos.getY() < aCurPos.getY())) + ) || // " reverse + (aBestPos.getY() == aCurPos.getY() && + (bNext? (aBestPos.getX() > aCurPos.getX()) : // further left + (aBestPos.getX() < aCurPos.getX())))) // " reverse + + { + aBestPos = aCurPos; + pBest = pObj; + } + + if( (bNext? (aTopPos.getY() > aCurPos.getY()) : // higher as best + (aTopPos.getY() < aCurPos.getY())) || // " reverse + (aTopPos.getY() == aCurPos.getY() && + (bNext? (aTopPos.getX() > aCurPos.getX()) : // further left + (aTopPos.getX() < aCurPos.getX())))) // " reverse + { + aTopPos = aCurPos; + pTop = pObj; + } + } + // unfortunately nothing found + if( bNext ? (aBestPos.getX() == LONG_MAX) : (aBestPos.getX() == 0) ) + { + pBest = pTop; + if (pbWrapped && pBest) + *pbWrapped = true; + } + } + + return pBest; +} + +bool SwFEShell::GotoObj( bool bNext, GotoObjFlags eType ) +{ + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); + + bool bWrapped(false); + const SdrObject* pBest = GetBestObject(bNext, eType, true, nullptr, &bWrapped); + + if ( !pBest ) + { + SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); + return false; + } + + const SwVirtFlyDrawObj *pVirtO = dynamic_cast<const SwVirtFlyDrawObj*>(pBest); + if (pVirtO) + { + const SwRect& rFrame = pVirtO->GetFlyFrame()->getFrameArea(); + SelectObj( rFrame.Pos(), 0, const_cast<SdrObject*>(pBest) ); + if( !ActionPend() ) + MakeVisible( rFrame ); + } + else + { + SelectObj( Point(), 0, const_cast<SdrObject*>(pBest) ); + if( !ActionPend() ) + MakeVisible( SwRect(pBest->GetCurrentBoundRect()) ); + } + CallChgLnk(); + + if (bWrapped) + SvxSearchDialogWrapper::SetSearchLabel(bNext ? SearchLabel::EndWrapped : + SearchLabel::StartWrapped); + + return true; +} + +bool SwFEShell::BeginCreate( SdrObjKind eSdrObjectKind, const Point &rPos ) +{ + bool bRet = false; + + if ( !Imp()->HasDrawView() ) + Imp()->MakeDrawView(); + + if ( GetPageNumber( rPos ) ) + { + Imp()->GetDrawView()->SetCurrentObj( eSdrObjectKind ); + if ( eSdrObjectKind == SdrObjKind::Caption ) + bRet = Imp()->GetDrawView()->BegCreateCaptionObj( + rPos, Size( lMinBorder - MINFLY, lMinBorder - MINFLY ), + GetOut() ); + else + bRet = Imp()->GetDrawView()->BegCreateObj( rPos, GetOut() ); + } + if ( bRet ) + { + ::FrameNotify( this, FLY_DRAG_START ); + } + return bRet; +} + +bool SwFEShell::BeginCreate( SdrObjKind eSdrObjectKind, SdrInventor eObjInventor, + const Point &rPos ) +{ + bool bRet = false; + + if ( !Imp()->HasDrawView() ) + Imp()->MakeDrawView(); + + if ( GetPageNumber( rPos ) ) + { + Imp()->GetDrawView()->SetCurrentObj( eSdrObjectKind, eObjInventor ); + bRet = Imp()->GetDrawView()->BegCreateObj( rPos, GetOut() ); + } + if ( bRet ) + ::FrameNotify( this, FLY_DRAG_START ); + return bRet; +} + +void SwFEShell::MoveCreate( const Point &rPos ) +{ + OSL_ENSURE( Imp()->HasDrawView(), "MoveCreate without DrawView?" ); + if ( GetPageNumber( rPos ) ) + { + ScrollTo( rPos ); + Imp()->GetDrawView()->MovCreateObj( rPos ); + ::FrameNotify( this ); + } +} + +bool SwFEShell::EndCreate( SdrCreateCmd eSdrCreateCmd ) +{ + // To assure undo-object from the DrawEngine is not stored, + // (we create our own undo-object!), temporarily switch-off Undo + OSL_ENSURE( Imp()->HasDrawView(), "EndCreate without DrawView?" ); + if( !Imp()->GetDrawView()->IsGroupEntered() ) + { + GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false); + } + bool bCreate = Imp()->GetDrawView()->EndCreateObj( eSdrCreateCmd ); + GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(true); + + if ( !bCreate ) + { + ::FrameNotify( this, FLY_DRAG_END ); + return false; + } + + if ( eSdrCreateCmd == SdrCreateCmd::NextPoint ) + { + ::FrameNotify( this ); + return true; + } + return ImpEndCreate(); +} + +bool SwFEShell::ImpEndCreate() +{ + if (Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() == 0) + return false; + + SdrObject& rSdrObj = *Imp()->GetDrawView()->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + + if( rSdrObj.GetSnapRect().IsEmpty() ) + { + // preferably we forget the object, only gives problems + Imp()->GetDrawView()->DeleteMarked(); + Imp()->GetDrawView()->UnmarkAll(); + ::FrameNotify( this, FLY_DRAG_END ); + return false; + } + + if( rSdrObj.getParentSdrObjectFromSdrObject() ) + { + Point aTmpPos( rSdrObj.GetSnapRect().TopLeft() ); + Point aNewAnchor( rSdrObj.getParentSdrObjectFromSdrObject()->GetAnchorPos() ); + // OD 2004-04-05 #i26791# - direct object positioning for group members + rSdrObj.NbcSetRelativePos( aTmpPos - aNewAnchor ); + rSdrObj.NbcSetAnchorPos( aNewAnchor ); + ::FrameNotify( this ); + return true; + } + + LockPaint(); + StartAllAction(); + + Imp()->GetDrawView()->UnmarkAll(); + + const tools::Rectangle &rBound = rSdrObj.GetSnapRect(); + Point aPt( rBound.TopRight() ); + + // default for controls character bound, otherwise paragraph bound. + SwFormatAnchor aAnch; + const SwFrame *pAnch = nullptr; + bool bCharBound = false; + if( dynamic_cast<const SdrUnoObj*>( &rSdrObj) != nullptr ) + { + SwPosition aPos( GetDoc()->GetNodes() ); + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + Point aPoint( aPt.getX(), aPt.getY() + rBound.GetHeight()/2 ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState ); + + // characterbinding not allowed in readonly-content + if( !aPos.nNode.GetNode().IsProtect() ) + { + std::pair<Point, bool> const tmp(aPoint, true); + pAnch = aPos.nNode.GetNode().GetContentNode()->getLayoutFrame(GetLayout(), &aPos, &tmp); + SwRect aTmp; + pAnch->GetCharRect( aTmp, aPos ); + + // The crsr should not be too far away + bCharBound = true; + constexpr tools::Long constTwips_1cm = o3tl::toTwips(1, o3tl::Length::cm); + tools::Rectangle aRect( aTmp.SVRect() ); + // Extend by 1 cm in each direction + aRect.AdjustLeft(-constTwips_1cm); + aRect.AdjustTop(-constTwips_1cm); + aRect.AdjustRight(constTwips_1cm); + aRect.AdjustBottom(constTwips_1cm); + + if( !aRect.Overlaps( rBound ) && !::GetHtmlMode( GetDoc()->GetDocShell() )) + bCharBound = false; + + // anchor in header/footer also not allowed. + if( bCharBound ) + bCharBound = !GetDoc()->IsInHeaderFooter( aPos.nNode ); + + if( bCharBound ) + { + aAnch.SetType( RndStdIds::FLY_AS_CHAR ); + aAnch.SetAnchor( &aPos ); + } + } + } + + if( !bCharBound ) + { + // allow native drawing objects in header/footer. + // Thus, set <bBodyOnly> to <false> for these objects using value + // of <nIdent> - value <0xFFFF> indicates control objects, which aren't + // allowed in header/footer. + //bool bBodyOnly = OBJ_NONE != nIdent; + bool bBodyOnly = SdrInventor::Default != rSdrObj.GetObjInventor(); + bool bAtPage = false; + const SwFrame* pPage = nullptr; + SwCursorMoveState aState( CursorMoveState::SetOnlyText ); + Point aPoint( aPt ); + SwPosition aPos( GetDoc()->GetNodes() ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState ); + + // do not set in ReadnOnly-content + if (aPos.nNode.GetNode().IsProtect()) + { + // then only page bound. Or should we + // search the next not-readonly position? + bAtPage = true; + } + + SwContentNode* pCNode = aPos.nNode.GetNode().GetContentNode(); + std::pair<Point, bool> const tmp(aPoint, false); + pAnch = pCNode ? pCNode->getLayoutFrame(GetLayout(), nullptr, &tmp) : nullptr; + if (!pAnch) + { + // Hidden content. Anchor to the page instead + bAtPage = true; + } + + if( !bAtPage ) + { + const SwFlyFrame *pTmp = pAnch->FindFlyFrame(); + if( pTmp ) + { + const SwFrame* pTmpFrame = pAnch; + SwRect aBound( rBound ); + while( pTmp ) + { + if( pTmp->getFrameArea().Contains( aBound ) ) + { + if( !bBodyOnly || !pTmp->FindFooterOrHeader() ) + pPage = pTmpFrame; + break; + } + pTmp = pTmp->GetAnchorFrame() + ? pTmp->GetAnchorFrame()->FindFlyFrame() + : nullptr; + pTmpFrame = pTmp; + } + } + + if( !pPage ) + pPage = pAnch->FindPageFrame(); + + // Always via FindAnchor, to assure the frame will be bound + // to the previous. With GetCrsOfst we can also reach the next. THIS IS WRONG. + pAnch = ::FindAnchor( pPage, aPt, bBodyOnly ); + if (pAnch->IsTextFrame()) + { + std::pair<SwTextNode const*, sal_Int32> const pos( + static_cast<SwTextFrame const*>(pAnch)->MapViewToModel(TextFrameIndex(0))); + aPos.nNode = *pos.first; + } + else + { + aPos.nNode = *static_cast<const SwNoTextFrame*>(pAnch)->GetNode(); + } + + // do not set in ReadnOnly-content + if( aPos.nNode.GetNode().IsProtect() ) + // then only page bound. Or should we + // search the next not-readonly position? + bAtPage = true; + else + { + aAnch.SetType( RndStdIds::FLY_AT_PARA ); + aAnch.SetAnchor( &aPos ); + } + } + + if( bAtPage ) + { + pPage = pAnch ? pAnch->FindPageFrame() : GetLayout()->GetPageAtPos(aPoint); + + aAnch.SetType( RndStdIds::FLY_AT_PAGE ); + aAnch.SetPageNum( pPage->GetPhyPageNum() ); + pAnch = pPage; // page becomes an anchor + } + } + + SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE, + RES_SURROUND, RES_ANCHOR> aSet( GetDoc()->GetAttrPool() ); + aSet.Put( aAnch ); + + // OD 2004-03-30 #i26791# - determine relative object position + SwTwips nXOffset; + SwTwips nYOffset = rBound.Top() - pAnch->getFrameArea().Top(); + { + if( pAnch->IsVertical() ) + { + nXOffset = nYOffset; + nYOffset = pAnch->getFrameArea().Left()+pAnch->getFrameArea().Width()-rBound.Right(); + } + else if( pAnch->IsRightToLeft() ) + nXOffset = pAnch->getFrameArea().Left()+pAnch->getFrameArea().Width()-rBound.Right(); + else + nXOffset = rBound.Left() - pAnch->getFrameArea().Left(); + if (pAnch->IsTextFrame()) + { + const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnch); + if (pTmp->IsFollow()) + { + do { + pTmp = pTmp->FindMaster(); + OSL_ENSURE(pTmp, "Where's my Master?"); + // OD 2004-03-30 #i26791# - correction: add frame area height + // of master frames. + nYOffset += pTmp->IsVertical() ? + pTmp->getFrameArea().Width() : pTmp->getFrameArea().Height(); + } while (pTmp->IsFollow()); + } + + nYOffset -= pTmp->GetBaseVertOffsetForFly(false); + } + } + + if( SdrInventor::Default == rSdrObj.GetObjInventor() && rSdrObj.GetObjIdentifier() == SdrObjKind::NONE ) + { + // For OBJ_NONE a fly is inserted. + const tools::Long nWidth = rBound.Right() - rBound.Left(); + const tools::Long nHeight= rBound.Bottom() - rBound.Top(); + aSet.Put( SwFormatFrameSize( SwFrameSize::Minimum, std::max( nWidth, tools::Long(MINFLY) ), + std::max( nHeight, tools::Long(MINFLY) ))); + + SwFormatHoriOrient aHori( nXOffset, text::HoriOrientation::NONE, text::RelOrientation::FRAME ); + SwFormatVertOrient aVert( nYOffset, text::VertOrientation::NONE, text::RelOrientation::FRAME ); + aSet.Put( SwFormatSurround( css::text::WrapTextMode_PARALLEL ) ); + aSet.Put( aHori ); + aSet.Put( aVert ); + + // Quickly store the square + const SwRect aFlyRect( rBound ); + + // Throw away generated object, now the fly can nicely + // via the available SS be generated. + GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false); // see above + // #i52858# - method name changed + SdrPage *pPg = getIDocumentDrawModelAccess().GetOrCreateDrawModel()->GetPage( 0 ); + if( !pPg ) + { + SdrModel* pTmpSdrModel = getIDocumentDrawModelAccess().GetDrawModel(); + auto pNewPage = pTmpSdrModel->AllocPage( false ); + pTmpSdrModel->InsertPage( pNewPage.get() ); + pPg = pNewPage.get(); + } + pPg->RecalcObjOrdNums(); + SdrObject* pRemovedObject = pPg->RemoveObject( rSdrObj.GetOrdNumDirect() ); + SdrObject::Free( pRemovedObject ); + GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(true); + + SwFlyFrame* pFlyFrame; + if( NewFlyFrame( aSet, true ) && + ::GetHtmlMode( GetDoc()->GetDocShell() ) && + nullptr != ( pFlyFrame = GetSelectedFlyFrame() )) + { + SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> aHtmlSet( GetDoc()->GetAttrPool() ); + // horizontal orientation: + const bool bLeftFrame = aFlyRect.Left() < + pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Left(), + bLeftPrt = aFlyRect.Left() + aFlyRect.Width() < + pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width()/2; + if( bLeftFrame || bLeftPrt ) + { + aHori.SetHoriOrient( text::HoriOrientation::LEFT ); + aHori.SetRelationOrient( bLeftFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA ); + } + else + { + const bool bRightFrame = aFlyRect.Left() > + pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width(); + aHori.SetHoriOrient( text::HoriOrientation::RIGHT ); + aHori.SetRelationOrient( bRightFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA ); + } + aHtmlSet.Put( aHori ); + aVert.SetVertOrient( text::VertOrientation::TOP ); + aVert.SetRelationOrient( text::RelOrientation::PRINT_AREA ); + aHtmlSet.Put( aVert ); + + GetDoc()->SetAttr( aHtmlSet, *pFlyFrame->GetFormat() ); + } + } + else + { + if (rSdrObj.GetName().isEmpty()) + { + bool bRestore = GetDoc()->GetIDocumentUndoRedo().DoesDrawUndo(); + GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false); + rSdrObj.MakeNameUnique(); + GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(bRestore); + } + + aSet.Put( aAnch ); + aSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) ); + // OD 2004-03-30 #i26791# - set horizontal position + SwFormatHoriOrient aHori( nXOffset, text::HoriOrientation::NONE, text::RelOrientation::FRAME ); + aSet.Put( aHori ); + // OD 2004-03-30 #i26791# - set vertical position + if( pAnch->IsTextFrame() && static_cast<const SwTextFrame*>(pAnch)->IsFollow() ) + { + const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnch); + do { + pTmp = pTmp->FindMaster(); + assert(pTmp && "Where's my Master?"); + nYOffset += pTmp->IsVertical() ? + pTmp->getFramePrintArea().Width() : pTmp->getFramePrintArea().Height(); + } while ( pTmp->IsFollow() ); + } + SwFormatVertOrient aVert( nYOffset, text::VertOrientation::NONE, text::RelOrientation::FRAME ); + aSet.Put( aVert ); + SwDrawFrameFormat* pFormat = static_cast<SwDrawFrameFormat*>(getIDocumentLayoutAccess().MakeLayoutFormat( RndStdIds::DRAW_OBJECT, &aSet )); + // #i36010# - set layout direction of the position + pFormat->SetPositionLayoutDir( + text::PositionLayoutDir::PositionInLayoutDirOfAnchor ); + // #i44344#, #i44681# - positioning attributes already set + pFormat->PosAttrSet(); + pFormat->SetName(rSdrObj.GetName()); + + SwDrawContact *pContact = new SwDrawContact( pFormat, &rSdrObj ); + // #i35635# + pContact->MoveObjToVisibleLayer( &rSdrObj ); + if( bCharBound ) + { + OSL_ENSURE( aAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR, "wrong AnchorType" ); + SwTextNode *pNd = aAnch.GetContentAnchor()->nNode.GetNode().GetTextNode(); + SwFormatFlyCnt aFormat( pFormat ); + pNd->InsertItem(aFormat, + aAnch.GetContentAnchor()->nContent.GetIndex(), 0 ); + SwFormatVertOrient aVertical( pFormat->GetVertOrient() ); + aVertical.SetVertOrient( text::VertOrientation::LINE_CENTER ); + pFormat->SetFormatAttr( aVertical ); + } + if( pAnch->IsTextFrame() && static_cast<const SwTextFrame*>(pAnch)->IsFollow() ) + { + const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnch); + do { + pTmp = pTmp->FindMaster(); + OSL_ENSURE( pTmp, "Where's my Master?" ); + } while( pTmp->IsFollow() ); + pAnch = pTmp; + } + + pContact->ConnectToLayout(); + + // mark object at frame the object is inserted at. + { + SdrObject* pMarkObj = pContact->GetDrawObjectByAnchorFrame( *pAnch ); + if ( pMarkObj ) + { + Imp()->GetDrawView()->MarkObj( pMarkObj, Imp()->GetPageView() ); + } + else + { + Imp()->GetDrawView()->MarkObj( &rSdrObj, Imp()->GetPageView() ); + } + } + } + + GetDoc()->getIDocumentState().SetModified(); + + KillPams(); + EndAllActionAndCall(); + UnlockPaint(); + return true; +} + +void SwFEShell::BreakCreate() +{ + OSL_ENSURE( Imp()->HasDrawView(), "BreakCreate without DrawView?" ); + Imp()->GetDrawView()->BrkCreateObj(); + ::FrameNotify( this, FLY_DRAG_END ); +} + +bool SwFEShell::IsDrawCreate() const +{ + return Imp()->HasDrawView() && Imp()->GetDrawView()->IsCreateObj(); +} + +bool SwFEShell::BeginMark( const Point &rPos ) +{ + if ( !Imp()->HasDrawView() ) + Imp()->MakeDrawView(); + + if ( GetPageNumber( rPos ) ) + { + SwDrawView* pDView = Imp()->GetDrawView(); + + if (pDView->HasMarkablePoints()) + return pDView->BegMarkPoints( rPos ); + else + { + pDView->BegMarkObj( rPos ); + return true; + } + } + else + return false; +} + +void SwFEShell::MoveMark( const Point &rPos ) +{ + OSL_ENSURE( Imp()->HasDrawView(), "MoveMark without DrawView?" ); + + if ( GetPageNumber( rPos ) ) + { + ScrollTo( rPos ); + SwDrawView* pDView = Imp()->GetDrawView(); + + if (pDView->IsInsObjPoint()) + pDView->MovInsObjPoint( rPos ); + else if (pDView->IsMarkPoints()) + pDView->MovMarkPoints( rPos ); + else + pDView->MovAction( rPos ); + } +} + +bool SwFEShell::EndMark() +{ + bool bRet = false; + OSL_ENSURE( Imp()->HasDrawView(), "EndMark without DrawView?" ); + + if (Imp()->GetDrawView()->IsMarkObj()) + { + bRet = Imp()->GetDrawView()->EndMarkObj(); + + if ( bRet ) + { + bool bShowHdl = false; + SwDrawView* pDView = Imp()->GetDrawView(); + // frames are not selected this way, except when + // it is only one frame + SdrMarkList &rMrkList = const_cast<SdrMarkList&>(pDView->GetMarkedObjectList()); + SwFlyFrame* pOldSelFly = ::GetFlyFromMarked( &rMrkList, this ); + + if ( rMrkList.GetMarkCount() > 1 ) + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) != nullptr ) + { + if ( !bShowHdl ) + { + bShowHdl = true; + } + rMrkList.DeleteMark( i ); + --i; // no exceptions + } + } + + if( bShowHdl ) + { + pDView->MarkListHasChanged(); + pDView->AdjustMarkHdl(); + } + + if ( rMrkList.GetMarkCount() ) + ::lcl_GrabCursor(this, pOldSelFly); + else + bRet = false; + } + if ( bRet ) + ::FrameNotify( this, FLY_DRAG_START ); + } + else + { + if (Imp()->GetDrawView()->IsMarkPoints()) + bRet = Imp()->GetDrawView()->EndMarkPoints(); + } + + SetChainMarker(); + return bRet; +} + +RndStdIds SwFEShell::GetAnchorId() const +{ + RndStdIds nRet = RndStdIds(SHRT_MAX); + if ( Imp()->HasDrawView() ) + { + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) != nullptr ) + { + nRet = RndStdIds::UNKNOWN; + break; + } + SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj)); + RndStdIds nId = pContact->GetFormat()->GetAnchor().GetAnchorId(); + if ( nRet == RndStdIds(SHRT_MAX) ) + nRet = nId; + else if ( nRet != nId ) + { + nRet = RndStdIds::UNKNOWN; + break; + } + } + } + if ( nRet == RndStdIds(SHRT_MAX) ) + nRet = RndStdIds::UNKNOWN; + return nRet; +} + +void SwFEShell::ChgAnchor( RndStdIds eAnchorId, bool bSameOnly, bool bPosCorr ) +{ + OSL_ENSURE( Imp()->HasDrawView(), "ChgAnchor without DrawView?" ); + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + if( rMrkList.GetMarkCount() && + !rMrkList.GetMark( 0 )->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject() ) + { + StartAllAction(); + + if( GetDoc()->ChgAnchor( rMrkList, eAnchorId, bSameOnly, bPosCorr )) + Imp()->GetDrawView()->UnmarkAll(); + + EndAllAction(); + + ::FrameNotify( this ); + } +} + +void SwFEShell::DelSelectedObj() +{ + OSL_ENSURE( Imp()->HasDrawView(), "DelSelectedObj(), no DrawView available" ); + if ( Imp()->HasDrawView() ) + { + StartAllAction(); + Imp()->GetDrawView()->DeleteMarked(); + EndAllAction(); + ::FrameNotify( this, FLY_DRAG_END ); + } +} + +// For the statusline to request the current conditions +Size SwFEShell::GetObjSize() const +{ + tools::Rectangle aRect; + if ( Imp()->HasDrawView() ) + { + if ( Imp()->GetDrawView()->IsAction() ) + Imp()->GetDrawView()->TakeActionRect( aRect ); + else + aRect = Imp()->GetDrawView()->GetAllMarkedRect(); + } + return aRect.GetSize(); +} + +Point SwFEShell::GetAnchorObjDiff() const +{ + const SdrView *pView = Imp()->GetDrawView(); + OSL_ENSURE( pView, "GetAnchorObjDiff without DrawView?" ); + + tools::Rectangle aRect; + if ( Imp()->GetDrawView()->IsAction() ) + Imp()->GetDrawView()->TakeActionRect( aRect ); + else + aRect = Imp()->GetDrawView()->GetAllMarkedRect(); + + Point aRet( aRect.TopLeft() ); + + if ( IsFrameSelected() ) + { + SwFlyFrame *pFly = GetSelectedFlyFrame(); + aRet -= pFly->GetAnchorFrame()->getFrameArea().Pos(); + } + else + { + const SdrObject *pObj = pView->GetMarkedObjectList().GetMarkCount() == 1 ? + pView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj() : nullptr; + if ( pObj ) + aRet -= pObj->GetAnchorPos(); + } + + return aRet; +} + +Point SwFEShell::GetObjAbsPos() const +{ + OSL_ENSURE( Imp()->GetDrawView(), "GetObjAbsPos() without DrawView?" ); + return Imp()->GetDrawView()->GetDragStat().GetActionRect().TopLeft(); +} + +bool SwFEShell::IsGroupSelected(bool bAllowDiagams) +{ + if ( IsObjSelected() ) + { + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + // consider 'virtual' drawing objects. + // Thus, use corresponding method instead of checking type. + if ( pObj->IsGroupObject() && + // --> #i38505# No ungroup allowed for 3d objects + !pObj->Is3DObj() && + RndStdIds::FLY_AS_CHAR != static_cast<SwDrawContact*>(GetUserCall(pObj))-> + GetFormat()->GetAnchor().GetAnchorId() ) + { + if(!bAllowDiagams) + { + // Don't allow enter Diagrams + if(pObj->isDiagram()) + { + return false; + } + } + + return true; + } + } + } + return false; +} + +namespace +{ + bool HasSuitableGroupingAnchor(const SdrObject* pObj) + { + bool bSuitable = true; + SwFrameFormat* pFrameFormat(::FindFrameFormat(const_cast<SdrObject*>(pObj))); + if (!pFrameFormat) + { + OSL_FAIL( "<HasSuitableGroupingAnchor> - missing frame format" ); + bSuitable = false; + } + else if (RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId()) + { + bSuitable = false; + } + return bSuitable; + } +} + +// Change return type. +// Adjustments for drawing objects in header/footer: +// allow group, only if all selected objects are in the same header/footer +// or not in header/footer. +bool SwFEShell::IsGroupAllowed() const +{ + bool bIsGroupAllowed = false; + if ( IsObjSelected() > 1 ) + { + bIsGroupAllowed = true; + const SdrObject* pUpGroup = nullptr; + const SwFrame* pHeaderFooterFrame = nullptr; + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for ( size_t i = 0; bIsGroupAllowed && i < rMrkList.GetMarkCount(); ++i ) + { + const SdrObject* pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + if ( i ) + bIsGroupAllowed = pObj->getParentSdrObjectFromSdrObject() == pUpGroup; + else + pUpGroup = pObj->getParentSdrObjectFromSdrObject(); + + if ( bIsGroupAllowed ) + bIsGroupAllowed = HasSuitableGroupingAnchor(pObj); + + // check, if all selected objects are in the + // same header/footer or not in header/footer. + if ( bIsGroupAllowed ) + { + const SwFrame* pAnchorFrame = nullptr; + if ( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>( pObj) ) + { + const SwFlyFrame* pFlyFrame = pVirtFlyDrawObj->GetFlyFrame(); + if ( pFlyFrame ) + { + pAnchorFrame = pFlyFrame->GetAnchorFrame(); + } + } + else + { + SwDrawContact* pDrawContact = static_cast<SwDrawContact*>(GetUserCall( pObj )); + if ( pDrawContact ) + { + pAnchorFrame = pDrawContact->GetAnchorFrame( pObj ); + } + } + if ( pAnchorFrame ) + { + if ( i ) + { + bIsGroupAllowed = + ( pAnchorFrame->FindFooterOrHeader() == pHeaderFooterFrame ); + } + else + { + pHeaderFooterFrame = pAnchorFrame->FindFooterOrHeader(); + } + } + } + } + } + + return bIsGroupAllowed; +} + +bool SwFEShell::IsUnGroupAllowed() const +{ + bool bIsUnGroupAllowed = false; + + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for (size_t i = 0; i < rMrkList.GetMarkCount(); ++i) + { + const SdrObject* pObj = rMrkList.GetMark(i)->GetMarkedSdrObj(); + bIsUnGroupAllowed = HasSuitableGroupingAnchor(pObj); + if (!bIsUnGroupAllowed) + break; + } + + return bIsUnGroupAllowed; +} + +// The group gets the anchor and the contactobject of the first in the selection +void SwFEShell::GroupSelection() +{ + if ( IsGroupAllowed() ) + { + StartAllAction(); + StartUndo( SwUndoId::START ); + + GetDoc()->GroupSelection( *Imp()->GetDrawView() ); + + EndUndo( SwUndoId::END ); + EndAllAction(); + } +} + +// The individual objects get a copy of the anchor and the contactobject of the group +void SwFEShell::UnGroupSelection() +{ + if ( IsGroupSelected(true) ) + { + StartAllAction(); + StartUndo( SwUndoId::START ); + + GetDoc()->UnGroupSelection( *Imp()->GetDrawView() ); + + EndUndo( SwUndoId::END ); + EndAllAction(); + } +} + +void SwFEShell::MirrorSelection( bool bHorizontal ) +{ + SdrView *pView = Imp()->GetDrawView(); + if ( IsObjSelected() && pView->IsMirrorAllowed() ) + { + if ( bHorizontal ) + pView->MirrorAllMarkedHorizontal(); + else + pView->MirrorAllMarkedVertical(); + } +} + +// jump to named frame (Graphic/OLE) + +bool SwFEShell::GotoFly( const OUString& rName, FlyCntType eType, bool bSelFrame ) +{ + bool bRet = false; + static SwNodeType const aChkArr[ 4 ] = { + /* FLYCNTTYPE_ALL */ SwNodeType::NONE, + /* FLYCNTTYPE_FRM */ SwNodeType::Text, + /* FLYCNTTYPE_GRF */ SwNodeType::Grf, + /* FLYCNTTYPE_OLE */ SwNodeType::Ole + }; + + const SwFlyFrameFormat* pFlyFormat = mxDoc->FindFlyByName( rName, aChkArr[ eType]); + if( pFlyFormat ) + { + CurrShell aCurr( this ); + + SwFlyFrame* pFrame = SwIterator<SwFlyFrame,SwFormat>( *pFlyFormat ).First(); + if( pFrame ) + { + if( bSelFrame ) + { + // first make visible, to get a11y events in proper order + if (!ActionPend()) + MakeVisible( pFrame->getFrameArea() ); + SelectObj( pFrame->getFrameArea().Pos(), 0, pFrame->GetVirtDrawObj() ); + } + else + { + SwContentFrame *pCFrame = pFrame->ContainsContent(); + if ( pCFrame ) + { + ClearMark(); + SwPaM* pCursor = GetCursor(); + + if (pCFrame->IsTextFrame()) + { + *pCursor->GetPoint() = static_cast<SwTextFrame *>(pCFrame) + ->MapViewToModelPos(TextFrameIndex(0)); + } + else + { + assert(pCFrame->IsNoTextFrame()); + SwContentNode *const pCNode = static_cast<SwNoTextFrame *>(pCFrame)->GetNode(); + + pCursor->GetPoint()->nNode = *pCNode; + pCursor->GetPoint()->nContent.Assign( pCNode, 0 ); + } + + SwRect& rChrRect = const_cast<SwRect&>(GetCharRect()); + rChrRect = pFrame->getFramePrintArea(); + rChrRect.Pos() += pFrame->getFrameArea().Pos(); + GetCursorDocPos() = rChrRect.Pos(); + } + } + bRet = true; + } + } + return bRet; +} + +size_t SwFEShell::GetFlyCount( FlyCntType eType, bool bIgnoreTextBoxes ) const +{ + return GetDoc()->GetFlyCount(eType, bIgnoreTextBoxes); +} + +const SwFrameFormat* SwFEShell::GetFlyNum(size_t nIdx, FlyCntType eType, bool bIgnoreTextBoxes ) const +{ + return GetDoc()->GetFlyNum(nIdx, eType, bIgnoreTextBoxes); +} + +std::vector<SwFrameFormat const*> SwFEShell::GetFlyFrameFormats( + FlyCntType const eType, bool const bIgnoreTextBoxes) +{ + return GetDoc()->GetFlyFrameFormats(eType, bIgnoreTextBoxes); +} + +// show the current selected object +void SwFEShell::MakeSelVisible() +{ + if ( Imp()->HasDrawView() && + Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() ) + { + GetCurrFrame(); // just to trigger formatting in case the selected object is not formatted. + MakeVisible( SwRect(Imp()->GetDrawView()->GetAllMarkedRect()) ); + } + else + SwCursorShell::MakeSelVisible(); +} + +// how is the selected object protected? +FlyProtectFlags SwFEShell::IsSelObjProtected( FlyProtectFlags eType ) const +{ + FlyProtectFlags nChk = FlyProtectFlags::NONE; + const bool bParent(eType & FlyProtectFlags::Parent); + if( Imp()->HasDrawView() ) + { + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for( size_t i = rMrkList.GetMarkCount(); i; ) + { + SdrObject *pObj = rMrkList.GetMark( --i )->GetMarkedSdrObj(); + if (!pObj) + { + continue; + } + + if( !bParent ) + { + nChk |= ( pObj->IsMoveProtect() ? FlyProtectFlags::Pos : FlyProtectFlags::NONE ) | + ( pObj->IsResizeProtect()? FlyProtectFlags::Size : FlyProtectFlags::NONE ); + + if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj)) + { + SwFlyFrame *pFly = pVirtO->GetFlyFrame(); + if ( (FlyProtectFlags::Content & eType) && pFly->GetFormat()->GetProtect().IsContentProtected() ) + nChk |= FlyProtectFlags::Content; + + if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() ) + { + SwOLENode *pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetOLENode(); + uno::Reference < embed::XEmbeddedObject > xObj( pNd ? pNd->GetOLEObj().GetOleRef() : nullptr ); + if ( xObj.is() ) + { + // TODO/LATER: use correct aspect + const bool bNeverResize = (embed::EmbedMisc::EMBED_NEVERRESIZE & xObj->getStatus( embed::Aspects::MSOLE_CONTENT )); + if ( ( (FlyProtectFlags::Content & eType) || (FlyProtectFlags::Size & eType) ) && bNeverResize ) + { + nChk |= FlyProtectFlags::Size; + nChk |= FlyProtectFlags::Fixed; + } + + // set FlyProtectFlags::Pos if it is a Math object anchored 'as char' and baseline alignment is activated + const bool bProtectMathPos = SotExchange::IsMath( xObj->getClassID() ) + && RndStdIds::FLY_AS_CHAR == pFly->GetFormat()->GetAnchor().GetAnchorId() + && mxDoc->GetDocumentSettingManager().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT ); + if ((FlyProtectFlags::Pos & eType) && bProtectMathPos) + nChk |= FlyProtectFlags::Pos; + } + } + } + nChk &= eType; + if( nChk == eType ) + return eType; + } + const SwFrame* pAnch; + if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj)) + pAnch = pVirtO->GetFlyFrame()->GetAnchorFrame(); + else + { + SwDrawContact* pTmp = static_cast<SwDrawContact*>(GetUserCall(pObj)); + pAnch = pTmp ? pTmp->GetAnchorFrame( pObj ) : nullptr; + } + if( pAnch && pAnch->IsProtected() ) + return eType; + } + } + return nChk; +} + +bool SwFEShell::GetObjAttr( SfxItemSet &rSet ) const +{ + if ( !IsObjSelected() ) + return false; + + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj)); + // --> make code robust + OSL_ENSURE( pContact, "<SwFEShell::GetObjAttr(..)> - missing <pContact>." ); + if ( pContact ) + { + if ( i ) + rSet.MergeValues( pContact->GetFormat()->GetAttrSet() ); + else + rSet.Put( pContact->GetFormat()->GetAttrSet() ); + } + } + return true; +} + +void SwFEShell::SetObjAttr( const SfxItemSet& rSet ) +{ + CurrShell aCurr( this ); + + if ( !rSet.Count() ) + { + OSL_ENSURE( false, "SetObjAttr, empty set." ); + return; + } + + StartAllAction(); + StartUndo( SwUndoId::INSATTR ); + + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj)); + GetDoc()->SetAttr( rSet, *pContact->GetFormat() ); + } + + EndUndo( SwUndoId::INSATTR ); + EndAllActionAndCall(); + GetDoc()->getIDocumentState().SetModified(); +} + +bool SwFEShell::IsAlignPossible() const +{ + return Imp()->GetDrawView()->IsAlignPossible(); +} + +void SwFEShell::CheckUnboundObjects() +{ + CurrShell aCurr( this ); + + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj(); + if ( !GetUserCall(pObj) ) + { + const tools::Rectangle &rBound = pObj->GetSnapRect(); + const Point aPt( rBound.TopLeft() ); + const SwFrame *pPage = GetLayout()->Lower(); + const SwFrame *pLast = pPage; + while ( pPage && !pPage->getFrameArea().Contains( aPt ) ) + { + if ( aPt.Y() > pPage->getFrameArea().Bottom() ) + pLast = pPage; + pPage = pPage->GetNext(); + } + if ( !pPage ) + pPage = pLast; + OSL_ENSURE( pPage, "Page not found." ); + + SwFormatAnchor aAnch; + { + const SwContentFrame *const pAnch = ::FindAnchor(pPage, aPt, true); + SwPosition aPos( pAnch->IsTextFrame() + ? *static_cast<SwTextFrame const*>(pAnch)->GetTextNodeForParaProps() + : *static_cast<SwNoTextFrame const*>(pAnch)->GetNode() ); + aAnch.SetType( RndStdIds::FLY_AT_PARA ); + aAnch.SetAnchor( &aPos ); + const_cast<SwRect&>(GetCharRect()).Pos() = aPt; + } + + // First the action here, to assure GetCharRect delivers current values. + StartAllAction(); + + SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE, + RES_SURROUND, RES_ANCHOR> aSet( GetAttrPool() ); + aSet.Put( aAnch ); + aSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) ); + SwFrameFormat* pFormat = getIDocumentLayoutAccess().MakeLayoutFormat( RndStdIds::DRAW_OBJECT, &aSet ); + + SwDrawContact *pContact = new SwDrawContact( + static_cast<SwDrawFrameFormat*>(pFormat), pObj ); + + // #i35635# + pContact->MoveObjToVisibleLayer( pObj ); + pContact->ConnectToLayout(); + + EndAllAction(); + } + } +} + +void SwFEShell::SetCalcFieldValueHdl(Outliner* pOutliner) +{ + GetDoc()->SetCalcFieldValueHdl(pOutliner); +} + +SwChainRet SwFEShell::Chainable( SwRect &rRect, const SwFrameFormat &rSource, + const Point &rPt ) const +{ + rRect.Clear(); + + // The source is not allowed to have a follow. + const SwFormatChain &rChain = rSource.GetChain(); + if ( rChain.GetNext() ) + return SwChainRet::SOURCE_CHAINED; + + SwChainRet nRet = SwChainRet::NOT_FOUND; + if( Imp()->HasDrawView() ) + { + SdrPageView* pPView; + SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView()); + const auto nOld = pDView->GetHitTolerancePixel(); + pDView->SetHitTolerancePixel( 0 ); + SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE); + SwVirtFlyDrawObj* pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj); + if (pDrawObj) + { + SwFlyFrame *pFly = pDrawObj->GetFlyFrame(); + rRect = pFly->getFrameArea(); + + // Target and source should not be equal and the list + // should not be cyclic + SwFrameFormat *pFormat = pFly->GetFormat(); + nRet = GetDoc()->Chainable(rSource, *pFormat); + } + pDView->SetHitTolerancePixel( nOld ); + } + return nRet; +} + +void SwFEShell::Chain( SwFrameFormat &rSource, const SwFrameFormat &rDest ) +{ + GetDoc()->Chain(rSource, rDest); +} + +SwChainRet SwFEShell::Chain( SwFrameFormat &rSource, const Point &rPt ) +{ + SwRect aDummy; + SwChainRet nErr = Chainable( aDummy, rSource, rPt ); + if ( nErr == SwChainRet::OK ) + { + StartAllAction(); + SdrPageView* pPView; + SwDrawView *pDView = Imp()->GetDrawView(); + const auto nOld = pDView->GetHitTolerancePixel(); + pDView->SetHitTolerancePixel( 0 ); + SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE); + pDView->SetHitTolerancePixel( nOld ); + SwFlyFrame *pFly = static_cast<SwVirtFlyDrawObj*>(pObj)->GetFlyFrame(); + + SwFlyFrameFormat *pFormat = pFly->GetFormat(); + GetDoc()->Chain(rSource, *pFormat); + EndAllAction(); + SetChainMarker(); + } + return nErr; +} + +void SwFEShell::Unchain( SwFrameFormat &rFormat ) +{ + StartAllAction(); + GetDoc()->Unchain(rFormat); + EndAllAction(); +} + +void SwFEShell::HideChainMarker() +{ + m_pChainFrom.reset(); + m_pChainTo.reset(); +} + +void SwFEShell::SetChainMarker() +{ + bool bDelFrom = true, + bDelTo = true; + if ( IsFrameSelected() ) + { + SwFlyFrame *pFly = GetSelectedFlyFrame(); + + if ( pFly->GetPrevLink() ) + { + bDelFrom = false; + const SwFrame *pPre = pFly->GetPrevLink(); + + Point aStart( pPre->getFrameArea().Right(), pPre->getFrameArea().Bottom()); + Point aEnd(pFly->getFrameArea().Pos()); + + if (!m_pChainFrom) + { + m_pChainFrom.reset( + new SdrDropMarkerOverlay( *GetDrawView(), aStart, aEnd )); + } + } + if ( pFly->GetNextLink() ) + { + bDelTo = false; + const SwFlyFrame *pNxt = pFly->GetNextLink(); + + Point aStart( pFly->getFrameArea().Right(), pFly->getFrameArea().Bottom()); + Point aEnd(pNxt->getFrameArea().Pos()); + + if (!m_pChainTo) + { + m_pChainTo.reset( + new SdrDropMarkerOverlay( *GetDrawView(), aStart, aEnd )); + } + } + } + + if ( bDelFrom ) + { + m_pChainFrom.reset(); + } + + if ( bDelTo ) + { + m_pChainTo.reset(); + } +} + +tools::Long SwFEShell::GetSectionWidth( SwFormat const & rFormat ) const +{ + SwFrame *pFrame = GetCurrFrame(); + // Is the cursor at this moment in a SectionFrame? + if( pFrame && pFrame->IsInSct() ) + { + SwSectionFrame* pSect = pFrame->FindSctFrame(); + do + { + // Is it the right one? + if( pSect->KnowsFormat( rFormat ) ) + return pSect->getFrameArea().Width(); + // for nested areas + pSect = pSect->GetUpper()->FindSctFrame(); + } + while( pSect ); + } + SwIterator<SwSectionFrame,SwFormat> aIter( rFormat ); + for ( SwSectionFrame* pSct = aIter.First(); pSct; pSct = aIter.Next() ) + { + if( !pSct->IsFollow() ) + { + return pSct->getFrameArea().Width(); + } + } + return 0; +} + +void SwFEShell::CreateDefaultShape( SdrObjKind eSdrObjectKind, const tools::Rectangle& rRect, + sal_uInt16 nSlotId) +{ + SdrView* pDrawView = GetDrawView(); + SdrModel* pDrawModel = pDrawView->GetModel(); + SdrObject* pObj = SdrObjFactory::MakeNewObject( + *pDrawModel, + SdrInventor::Default, + eSdrObjectKind); + + if(pObj) + { + tools::Rectangle aRect(rRect); + if(SdrObjKind::CircleArc == eSdrObjectKind || SdrObjKind::CircleCut == eSdrObjectKind) + { + // force quadratic + if(aRect.GetWidth() > aRect.GetHeight()) + { + aRect = tools::Rectangle( + Point(aRect.Left() + ((aRect.GetWidth() - aRect.GetHeight()) / 2), aRect.Top()), + Size(aRect.GetHeight(), aRect.GetHeight())); + } + else + { + aRect = tools::Rectangle( + Point(aRect.Left(), aRect.Top() + ((aRect.GetHeight() - aRect.GetWidth()) / 2)), + Size(aRect.GetWidth(), aRect.GetWidth())); + } + } + pObj->SetLogicRect(aRect); + + Point aStart = aRect.TopLeft(); + Point aEnd = aRect.BottomRight(); + + if(dynamic_cast<const SdrCircObj*>( pObj) != nullptr) + { + SfxItemSet aAttr(pDrawModel->GetItemPool()); + aAttr.Put(makeSdrCircStartAngleItem(9000_deg100)); + aAttr.Put(makeSdrCircEndAngleItem(0_deg100)); + pObj->SetMergedItemSet(aAttr); + } + else if(auto pPathObj = dynamic_cast<SdrPathObj*>( pObj)) + { + basegfx::B2DPolyPolygon aPoly; + + switch(eSdrObjectKind) + { + case SdrObjKind::PathLine: + case SdrObjKind::PathFill: + { + basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(basegfx::B2DPoint(aRect.Left(), aRect.Bottom())); + + const basegfx::B2DPoint aCenterBottom(aRect.Center().getX(), aRect.Bottom()); + aInnerPoly.appendBezierSegment( + aCenterBottom, + aCenterBottom, + basegfx::B2DPoint(aRect.Center().getX(), aRect.Center().getY())); + + const basegfx::B2DPoint aCenterTop(aRect.Center().getX(), aRect.Top()); + aInnerPoly.appendBezierSegment( + aCenterTop, + aCenterTop, + basegfx::B2DPoint(aRect.Right(), aRect.Top())); + + aInnerPoly.setClosed(true); + aPoly.append(aInnerPoly); + } + break; + case SdrObjKind::FreehandLine: + case SdrObjKind::FreehandFill: + { + basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(basegfx::B2DPoint(aRect.Left(), aRect.Bottom())); + + aInnerPoly.appendBezierSegment( + basegfx::B2DPoint(aRect.Left(), aRect.Top()), + basegfx::B2DPoint(aRect.Center().getX(), aRect.Top()), + basegfx::B2DPoint(aRect.Center().getX(), aRect.Center().getY())); + + aInnerPoly.appendBezierSegment( + basegfx::B2DPoint(aRect.Center().getX(), aRect.Bottom()), + basegfx::B2DPoint(aRect.Right(), aRect.Bottom()), + basegfx::B2DPoint(aRect.Right(), aRect.Top())); + + aInnerPoly.append(basegfx::B2DPoint(aRect.Right(), aRect.Bottom())); + aInnerPoly.setClosed(true); + aPoly.append(aInnerPoly); + } + break; + case SdrObjKind::Polygon: + case SdrObjKind::PolyLine: + { + basegfx::B2DPolygon aInnerPoly; + sal_Int32 nWdt(aRect.GetWidth()); + sal_Int32 nHgt(aRect.GetHeight()); + + aInnerPoly.append(basegfx::B2DPoint(aRect.Left(), aRect.Bottom())); + aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + (nWdt * 30) / 100, aRect.Top() + (nHgt * 70) / 100)); + aInnerPoly.append(basegfx::B2DPoint(aRect.Left(), aRect.Top() + (nHgt * 15) / 100)); + aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + (nWdt * 65) / 100, aRect.Top())); + aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + nWdt, aRect.Top() + (nHgt * 30) / 100)); + aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + (nWdt * 80) / 100, aRect.Top() + (nHgt * 50) / 100)); + aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + (nWdt * 80) / 100, aRect.Top() + (nHgt * 75) / 100)); + aInnerPoly.append(basegfx::B2DPoint(aRect.Bottom(), aRect.Right())); + + if(SdrObjKind::PolyLine == eSdrObjectKind) + { + aInnerPoly.append(basegfx::B2DPoint(aRect.Center().getX(), aRect.Bottom())); + } + else + { + aInnerPoly.setClosed(true); + } + + aPoly.append(aInnerPoly); + } + break; + case SdrObjKind::Line : + { + sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2); + basegfx::B2DPolygon aTempPoly; + aTempPoly.append(basegfx::B2DPoint(aRect.TopLeft().getX(), nYMiddle)); + aTempPoly.append(basegfx::B2DPoint(aRect.BottomRight().getX(), nYMiddle)); + aPoly.append(aTempPoly); + + SfxItemSet aAttr(pObj->getSdrModelFromSdrObject().GetItemPool()); + SetLineEnds(aAttr, *pObj, nSlotId); + pObj->SetMergedItemSet(aAttr); + } + break; + default: + break; + } + + pPathObj->SetPathPoly(aPoly); + } + else if(auto pMeasureObj = dynamic_cast<SdrMeasureObj*>( pObj)) + { + sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2); + pMeasureObj->SetPoint(Point(aStart.X(), nYMiddle), 0); + pMeasureObj->SetPoint(Point(aEnd.X(), nYMiddle), 1); + + SfxItemSet aAttr(pObj->getSdrModelFromSdrObject().GetItemPool()); + SetLineEnds(aAttr, *pObj, nSlotId); + pObj->SetMergedItemSet(aAttr); + } + else if(auto pCaptionObj = dynamic_cast<SdrCaptionObj*>( pObj)) + { + bool bVerticalText = ( SID_DRAW_TEXT_VERTICAL == nSlotId || + SID_DRAW_CAPTION_VERTICAL == nSlotId ); + pCaptionObj->SetVerticalWriting(bVerticalText); + if(bVerticalText) + { + SfxItemSet aSet(pObj->GetMergedItemSet()); + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + pObj->SetMergedItemSet(aSet); + } + + pCaptionObj->SetLogicRect(aRect); + pCaptionObj->SetTailPos( + aRect.TopLeft() - Point(aRect.GetWidth() / 2, aRect.GetHeight() / 2)); + } + else if(auto pText = dynamic_cast<SdrTextObj*>( pObj)) + { + pText->SetLogicRect(aRect); + + bool bVertical = (SID_DRAW_TEXT_VERTICAL == nSlotId); + bool bMarquee = (SID_DRAW_TEXT_MARQUEE == nSlotId); + + pText->SetVerticalWriting(bVertical); + + if(bVertical) + { + SfxItemSet aSet(pDrawModel->GetItemPool()); + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + pText->SetMergedItemSet(aSet); + } + + if(bMarquee) + { + SfxItemSetFixed<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST> aSet(pDrawModel->GetItemPool()); + aSet.Put( makeSdrTextAutoGrowWidthItem( false ) ); + aSet.Put( makeSdrTextAutoGrowHeightItem( false ) ); + aSet.Put( SdrTextAniKindItem( SdrTextAniKind::Slide ) ); + aSet.Put( SdrTextAniDirectionItem( SdrTextAniDirection::Left ) ); + aSet.Put( SdrTextAniCountItem( 1 ) ); + aSet.Put( SdrTextAniAmountItem( static_cast<sal_Int16>(GetWin()->PixelToLogic(Size(2,1)).Width())) ); + pObj->SetMergedItemSetAndBroadcast(aSet); + } + } + SdrPageView* pPageView = pDrawView->GetSdrPageView(); + SdrCreateView::SetupObjLayer(pPageView, pDrawView->GetActiveLayer(), pObj); + // switch undo off or this combined with ImpEndCreate will cause two undos + // see comment made in SwFEShell::EndCreate (we create our own undo-object!) + const bool bUndo(GetDoc()->GetIDocumentUndoRedo().DoesUndo()); + GetDoc()->GetIDocumentUndoRedo().DoUndo(false); + pDrawView->InsertObjectAtView(pObj, *pPageView); + GetDoc()->GetIDocumentUndoRedo().DoUndo(bUndo); + } + ImpEndCreate(); +} + +/** SwFEShell::GetShapeBackground + method determines background color of the page the selected drawing + object is on and returns this color. + If no color is found, because no drawing object is selected or ..., + color COL_BLACK (default color on constructing object of class Color) + is returned. + + @returns an object of class Color +*/ +Color SwFEShell::GetShapeBackground() const +{ + Color aRetColor; + + // check, if a draw view exists + OSL_ENSURE( Imp()->GetDrawView(), "wrong usage of SwFEShell::GetShapeBackground - no draw view!"); + if( Imp()->GetDrawView() ) + { + // determine list of selected objects + const SdrMarkList* pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + // check, if exactly one object is selected. + OSL_ENSURE( pMrkList->GetMarkCount() == 1, "wrong usage of SwFEShell::GetShapeBackground - no selected object!"); + if ( pMrkList->GetMarkCount() == 1) + { + // get selected object + const SdrObject *pSdrObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj(); + // check, if selected object is a shape (drawing object) + OSL_ENSURE( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr, "wrong usage of SwFEShell::GetShapeBackground - selected object is not a drawing object!"); + if ( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr ) + { + // determine page frame of the frame the shape is anchored. + const SwFrame* pAnchorFrame = + static_cast<SwDrawContact*>(GetUserCall(pSdrObj))->GetAnchorFrame( pSdrObj ); + OSL_ENSURE( pAnchorFrame, "inconsistent model - no anchor at shape!"); + if ( pAnchorFrame ) + { + const SwPageFrame* pPageFrame = pAnchorFrame->FindPageFrame(); + OSL_ENSURE( pPageFrame, "inconsistent model - no page!"); + if ( pPageFrame ) + { + aRetColor = pPageFrame->GetDrawBackgroundColor(); + } + } + } + } + } + + return aRetColor; +} + +/** Is default horizontal text direction for selected drawing object right-to-left + Because drawing objects only painted for each page only, the default + horizontal text direction of a drawing object is given by the corresponding + page property. + + @returns boolean, indicating, if the horizontal text direction of the + page, the selected drawing object is on, is right-to-left. +*/ +bool SwFEShell::IsShapeDefaultHoriTextDirR2L() const +{ + bool bRet = false; + + // check, if a draw view exists + OSL_ENSURE( Imp()->GetDrawView(), "wrong usage of SwFEShell::GetShapeBackground - no draw view!"); + if( Imp()->GetDrawView() ) + { + // determine list of selected objects + const SdrMarkList* pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList(); + // check, if exactly one object is selected. + OSL_ENSURE( pMrkList->GetMarkCount() == 1, "wrong usage of SwFEShell::GetShapeBackground - no selected object!"); + if ( pMrkList->GetMarkCount() == 1) + { + // get selected object + const SdrObject *pSdrObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj(); + // check, if selected object is a shape (drawing object) + OSL_ENSURE( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr, "wrong usage of SwFEShell::GetShapeBackground - selected object is not a drawing object!"); + if ( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr ) + { + // determine page frame of the frame the shape is anchored. + const SwFrame* pAnchorFrame = + static_cast<SwDrawContact*>(GetUserCall(pSdrObj))->GetAnchorFrame( pSdrObj ); + OSL_ENSURE( pAnchorFrame, "inconsistent model - no anchor at shape!"); + if ( pAnchorFrame ) + { + const SwPageFrame* pPageFrame = pAnchorFrame->FindPageFrame(); + OSL_ENSURE( pPageFrame, "inconsistent model - no page!"); + if ( pPageFrame ) + { + bRet = pPageFrame->IsRightToLeft(); + } + } + } + } + } + + return bRet; +} + +Point SwFEShell::GetRelativePagePosition(const Point& rDocPos) +{ + Point aRet(-1, -1); + const SwFrame *pPage = GetLayout()->Lower(); + while ( pPage && !pPage->getFrameArea().Contains( rDocPos ) ) + { + pPage = pPage->GetNext(); + } + if(pPage) + { + aRet = rDocPos - pPage->getFrameArea().TopLeft(); + } + return aRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/frmedt/fetab.cxx b/sw/source/core/frmedt/fetab.cxx new file mode 100644 index 000000000..a77d19e02 --- /dev/null +++ b/sw/source/core/frmedt/fetab.cxx @@ -0,0 +1,2438 @@ +/* -*- 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 <memory> +#include <hintids.hxx> + +#include <vcl/errinf.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <editeng/protitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <svtools/ruler.hxx> +#include <osl/diagnose.h> +#include <swwait.hxx> +#include <fmtfsize.hxx> +#include <fmtornt.hxx> +#include <frmatr.hxx> +#include <fesh.hxx> +#include <wrtsh.hxx> +#include <doc.hxx> +#include <docsh.hxx> +#include <IDocumentState.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <cntfrm.hxx> +#include <txtfrm.hxx> +#include <notxtfrm.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <tabfrm.hxx> +#include <rowfrm.hxx> +#include <cellfrm.hxx> +#include <flyfrm.hxx> +#include <swtable.hxx> +#include <swddetbl.hxx> +#include <ndtxt.hxx> +#include <calc.hxx> +#include <dialoghelp.hxx> +#include <tabcol.hxx> +#include <tblafmt.hxx> +#include <cellatr.hxx> +#include <pam.hxx> +#include <viscrs.hxx> +#include <tblsel.hxx> +#include <swerror.h> +#include <swundo.hxx> +#include <frmtool.hxx> +#include <fmtrowsplt.hxx> +#include <node.hxx> +#include <sortedobjs.hxx> + +using namespace ::com::sun::star; + +// also see swtable.cxx +#define COLFUZZY 20L + +static bool IsSame( tools::Long nA, tools::Long nB ) { return std::abs(nA-nB) <= COLFUZZY; } + +namespace { + +class TableWait +{ + const std::unique_ptr<SwWait> m_pWait; + // this seems really fishy: do some locking, if an arbitrary number of lines is exceeded + static const size_t our_kLineLimit = 20; + static bool ShouldWait(size_t nCnt, SwFrame *pFrame, size_t nCnt2) + { return our_kLineLimit < nCnt || our_kLineLimit < nCnt2 || (pFrame && our_kLineLimit < pFrame->ImplFindTabFrame()->GetTable()->GetTabLines().size()); } +public: + TableWait(size_t nCnt, SwFrame *pFrame, SwDocShell &rDocShell, size_t nCnt2 = 0) + : m_pWait( ShouldWait(nCnt, pFrame, nCnt2) ? std::make_unique<SwWait>( rDocShell, true ) : nullptr ) + { } +}; + +} + +void SwFEShell::ParkCursorInTab() +{ + SwCursor * pSwCursor = GetCursor(); + + OSL_ENSURE(pSwCursor, "no SwCursor"); + + SwPosition aStartPos = *pSwCursor->GetPoint(), aEndPos = aStartPos; + + /* Search least and greatest position in current cursor ring. + */ + for(SwPaM& rTmpCursor : pSwCursor->GetRingContainer()) + { + SwCursor* pTmpCursor = static_cast<SwCursor *>(&rTmpCursor); + const SwPosition * pPt = pTmpCursor->GetPoint(), + * pMk = pTmpCursor->GetMark(); + + if (*pPt < aStartPos) + aStartPos = *pPt; + + if (*pPt > aEndPos) + aEndPos = *pPt; + + if (*pMk < aStartPos) + aStartPos = *pMk; + + if (*pMk > aEndPos) + aEndPos = *pMk; + + } + + KillPams(); + + /* @@@ semantic: SwCursor::operator=() is not implemented @@@ */ + + /* Set cursor to end of selection to ensure IsLastCellInRow works + properly. */ + { + SwCursor aTmpCursor( aEndPos, nullptr ); + *pSwCursor = aTmpCursor; + } + + /* Move the cursor out of the columns to delete and stay in the + same row. If the table has only one column the cursor will + stay in the row and the shell will take care of it. */ + if (IsLastCellInRow()) + { + /* If the cursor is in the last row of the table, first + try to move it to the previous cell. If that fails move + it to the next cell. */ + + { + SwCursor aTmpCursor( aStartPos, nullptr ); + *pSwCursor = aTmpCursor; + } + + if (! pSwCursor->GoPrevCell()) + { + SwCursor aTmpCursor( aEndPos, nullptr ); + *pSwCursor = aTmpCursor; + pSwCursor->GoNextCell(); + } + } + else + { + /* If the cursor is not in the last row of the table, first + try to move it to the next cell. If that fails move it + to the previous cell. */ + + { + SwCursor aTmpCursor( aEndPos, nullptr ); + *pSwCursor = aTmpCursor; + } + + if (! pSwCursor->GoNextCell()) + { + SwCursor aTmpCursor( aStartPos, nullptr ); + *pSwCursor = aTmpCursor; + pSwCursor->GoPrevCell(); + } + } +} + +void SwFEShell::InsertRow( sal_uInt16 nCnt, bool bBehind ) +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + + CurrShell aCurr( this ); + StartAllAction(); + + // search boxes via the layout + SwSelBoxes aBoxes; + bool bSelectAll = StartsWithTable() && ExtendedSelectedAll(); + if (bSelectAll) + { + // Set the end of the selection to the last paragraph of the last cell of the table. + SwPaM* pPaM = getShellCursor(false); + SwNode* pNode = pPaM->Start()->nNode.GetNode().FindTableNode()->EndOfSectionNode(); + // pNode is the end node of the table, we want the last node before the end node of the last cell. + pPaM->End()->nNode = pNode->GetIndex() - 2; + pPaM->End()->nContent.Assign(pPaM->End()->nNode.GetNode().GetContentNode(), 0); + } + GetTableSel( *this, aBoxes, SwTableSearchType::Row ); + + TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() ); + + if ( !aBoxes.empty() ) + GetDoc()->InsertRow( aBoxes, nCnt, bBehind ); + + EndAllActionAndCall(); +} + +void SwFEShell::InsertCol( sal_uInt16 nCnt, bool bBehind ) +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + + CurrShell aCurr( this ); + + if( !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::Col ) ) + { + ErrorHandler::HandleError( ERR_TBLINSCOL_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + + StartAllAction(); + // search boxes via the layout + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes, SwTableSearchType::Col ); + + TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() ); + + if( !aBoxes.empty() ) + GetDoc()->InsertCol( aBoxes, nCnt, bBehind ); + + EndAllActionAndCall(); +} + +// Determines if the current cursor is in the last row of the table. +bool SwFEShell::IsLastCellInRow() const +{ + SwTabCols aTabCols; + GetTabCols( aTabCols ); + bool bResult = false; + + if (IsTableRightToLeft()) + /* If the table is right-to-left the last row is the most left one. */ + bResult = 0 == GetCurTabColNum(); + else + /* If the table is left-to-right the last row is the most right one. */ + bResult = aTabCols.Count() == GetCurTabColNum(); + + return bResult; +} + +bool SwFEShell::DeleteCol() +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return false; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return false; + } + + CurrShell aCurr( this ); + StartAllAction(); + + // search boxes via the layout + bool bRet; + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes, SwTableSearchType::Col ); + if ( !aBoxes.empty() ) + { + TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() ); + + // remove crsr from the deletion area. + // Put them behind/on the table; via the + // document position they will be put to the old position + while( !pFrame->IsCellFrame() ) + pFrame = pFrame->GetUpper(); + + ParkCursorInTab(); + + // then delete the column + StartUndo(SwUndoId::COL_DELETE); + bRet = GetDoc()->DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn); + EndUndo(SwUndoId::COL_DELETE); + } + else + bRet = false; + + EndAllActionAndCall(); + return bRet; +} + +void SwFEShell::DeleteTable() +{ + DeleteRow(true); +} + +bool SwFEShell::DeleteRow(bool bCompleteTable) +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return false; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return false; + } + + CurrShell aCurr( this ); + + bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording(); + bool bRecordAndHideChanges = bRecordChanges && + GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines(); + + // tracked deletion: all rows have already had tracked row change in the table selection + if ( bRecordChanges && !SwDoc::HasRowNotTracked( *getShellCursor( false ) ) ) + return false; + + if ( bRecordChanges ) + StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + + StartAllAction(); + + // tracked deletion: remove only textbox content, + // and set IsNoTracked table line property to false + if ( bRecordChanges ) + { + SvxPrintItem aNotTracked(RES_PRINT, false); + GetDoc()->SetRowNotTracked( *getShellCursor( false ), aNotTracked ); + + if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) ) + pWrtShell->SelectTableRow(); + + // don't need to remove the row frames in Show Changes mode + if ( !bRecordAndHideChanges ) + { + if (SwEditShell* pEditShell = GetDoc()->GetEditShell()) + pEditShell->Delete(false); + + EndAllActionAndCall(); + EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + + return true; + } + } + + // search for boxes via the layout + bool bRet; + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes, SwTableSearchType::Row ); + + if( !aBoxes.empty() ) + { + TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() ); + + // Delete cursors from the deletion area. + // Then the cursor is: + // 1. the following row, if there is another row after this + // 2. the preceding row, if there is another row before this + // 3. otherwise below the table + { + SwTableNode* pTableNd = pFrame->IsTextFrame() + ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode() + : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode(); + + // search all boxes / lines + FndBox_ aFndBox( nullptr, nullptr ); + { + FndPara aPara( aBoxes, &aFndBox ); + ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara ); + } + + if( aFndBox.GetLines().empty() ) + { + EndAllActionAndCall(); + return false; + } + + KillPams(); + + FndBox_* pFndBox = &aFndBox; + while( 1 == pFndBox->GetLines().size() && + 1 == pFndBox->GetLines().front()->GetBoxes().size()) + { + FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get(); + if( pTmp->GetBox()->GetSttNd() ) + break; // otherwise too far + pFndBox = pTmp; + } + + SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine(); + SwTableBox* pDelBox = pDelLine->GetTabBoxes().back(); + while( !pDelBox->GetSttNd() ) + { + SwTableLine* pLn = pDelBox->GetTabLines().back(); + pDelBox = pLn->GetTabBoxes().back(); + } + SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(), + pDelBox ); + // skip deleted lines in Hide Changes mode with enabled change tracking + if ( bRecordAndHideChanges ) + { + SwRedlineTable::size_type nRedlinePos = 0; + while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) ) + pNextBox = pNextBox->GetUpper()->FindNextBox( pTableNd->GetTable(), + pNextBox->GetUpper()->GetTabBoxes().back() ); + } + + // skip protected cells + while( pNextBox && + pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox ); + + if( !pNextBox ) // no next? then the previous + { + pDelLine = pFndBox->GetLines().front()->GetLine(); + pDelBox = pDelLine->GetTabBoxes()[ 0 ]; + while( !pDelBox->GetSttNd() ) + pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0]; + pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(), + pDelBox ); + // skip previous deleted lines in Hide Changes mode with enabled change tracking + if ( bRecordAndHideChanges ) + { + SwRedlineTable::size_type nRedlinePos = 0; + while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) ) + { + pNextBox = pNextBox->GetUpper()->FindPreviousBox( pTableNd->GetTable(), + pNextBox->GetUpper()->GetTabBoxes()[0] ); + nRedlinePos = 0; + } + } + + // skip previous protected cells + while( pNextBox && + pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox ); + } + + // delete row content in Hide Changes mode + if ( bRecordAndHideChanges ) + { + SwEditShell* pEditShell = GetDoc()->GetEditShell(); + + // select the rows deleted with change tracking + if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) ) + { + pWrtShell->SelectTableRow(); + SwShellTableCursor* pTableCursor = GetTableCursor(); + auto pStt = aBoxes[0]; + auto pEnd = aBoxes.back(); + pTableCursor->DeleteMark(); + + // set start and end of the selection + pTableCursor->GetPoint()->nNode = *pEnd->GetSttNd(); + pTableCursor->Move( fnMoveForward, GoInContent ); + pTableCursor->SetMark(); + pTableCursor->GetPoint()->nNode = *pStt->GetSttNd()->EndOfSectionNode(); + pTableCursor->Move( fnMoveBackward, GoInContent ); + pWrtShell->UpdateCursor(); + } + + if (pEditShell) + pEditShell->Delete(false); + } + + SwNodeOffset nIdx; + if( pNextBox ) // put cursor here + nIdx = pNextBox->GetSttIdx() + 1; + else // otherwise below the table + nIdx = pTableNd->EndOfSectionIndex() + 1; + + SwNodeIndex aIdx( GetDoc()->GetNodes(), nIdx ); + SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = GetDoc()->GetNodes().GoNext( &aIdx ); + + // remove row frames in Hide Changes mode (and table frames, if needed) + if ( bRecordAndHideChanges ) + { + // remove all frames of the table, and make them again without the deleted ones + // TODO remove only the deleted frames + pTableNd->DelFrames(); + if ( !pTableNd->GetTable().IsDeleted() ) + { + SwNodeIndex aTableIdx( *pTableNd->EndOfSectionNode(), 1 ); + pTableNd->MakeOwnFrames(&aTableIdx); + } + + EndAllActionAndCall(); + + // put cursor + SwPaM* pPam = GetCursor(); + pPam->GetPoint()->nNode = aIdx; + pPam->GetPoint()->nContent.Assign( pCNd, 0 ); + pPam->SetMark(); // both want something + pPam->DeleteMark(); + if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) ) + pWrtShell->UpdateCursor(); + + EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + return true; + } + else if( pCNd ) + { + // put cursor + SwPaM* pPam = GetCursor(); + pPam->GetPoint()->nNode = aIdx; + pPam->GetPoint()->nContent.Assign( pCNd, 0 ); + pPam->SetMark(); // both want something + pPam->DeleteMark(); + } + } + + // now delete the lines + StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + bRet = GetDoc()->DeleteRowCol( aBoxes ); + EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE); + } + else + bRet = false; + + EndAllActionAndCall(); + return bRet; +} + +TableMergeErr SwFEShell::MergeTab() +{ + // check if Point/Mark of current cursor are in a table + TableMergeErr nRet = TableMergeErr::NoSelection; + if( IsTableMode() ) + { + SwShellTableCursor* pTableCursor = GetTableCursor(); + const SwTableNode* pTableNd = pTableCursor->GetNode().FindTableNode(); + if( dynamic_cast< const SwDDETable* >(&pTableNd->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + } + else + { + CurrShell aCurr( this ); + StartAllAction(); + + TableWait aWait(pTableCursor->GetSelectedBoxesCount(), nullptr, + *GetDoc()->GetDocShell(), + pTableNd->GetTable().GetTabLines().size() ); + + nRet = GetDoc()->MergeTable( *pTableCursor ); + + KillPams(); + + EndAllActionAndCall(); + } + } + return nRet; +} + +void SwFEShell::SplitTab( bool bVert, sal_uInt16 nCnt, bool bSameHeight ) +{ + // check if Point/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + + CurrShell aCurr( this ); + + if( bVert && !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::NONE ) ) + { + ErrorHandler::HandleError( ERR_TBLSPLIT_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return; + } + StartAllAction(); + // search boxes via the layout + SwSelBoxes aBoxes; + GetTableSel( *this, aBoxes ); + if( !aBoxes.empty() ) + { + TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() ); + + // now delete the columns + GetDoc()->SplitTable( aBoxes, bVert, nCnt, bSameHeight ); + + ClearFEShellTabCols(*GetDoc(), nullptr); + } + EndAllActionAndCall(); +} + +void SwFEShell::GetTabCols_(SwTabCols &rToFill, const SwFrame *pBox) const +{ + const SwTabFrame *pTab = pBox->FindTabFrame(); + if (m_pColumnCache) + { + bool bDel = true; + if (m_pColumnCache->pLastTable == pTab->GetTable()) + { + bDel = false; + SwRectFnSet aRectFnSet(pTab); + + const SwPageFrame* pPage = pTab->FindPageFrame(); + const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) - + aRectFnSet.GetLeft(pPage->getFrameArea()); + const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) - + aRectFnSet.GetLeft(pPage->getFrameArea()); + + if (m_pColumnCache->pLastTabFrame != pTab) + { + // if TabFrame was changed, we only shift a little bit + // as the width is the same + SwRectFnSet fnRectX(m_pColumnCache->pLastTabFrame); + if (fnRectX.GetWidth(m_pColumnCache->pLastTabFrame->getFrameArea()) == + aRectFnSet.GetWidth(pTab->getFrameArea()) ) + { + m_pColumnCache->pLastCols->SetLeftMin( nLeftMin ); + + m_pColumnCache->pLastTabFrame = pTab; + } + else + bDel = true; + } + + if ( !bDel && + m_pColumnCache->pLastCols->GetLeftMin () == o3tl::narrowing<sal_uInt16>(nLeftMin) && + m_pColumnCache->pLastCols->GetLeft () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetLeft(pTab->getFramePrintArea())) && + m_pColumnCache->pLastCols->GetRight () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetRight(pTab->getFramePrintArea()))&& + m_pColumnCache->pLastCols->GetRightMax() == o3tl::narrowing<sal_uInt16>(nRightMax) - m_pColumnCache->pLastCols->GetLeftMin() ) + { + if (m_pColumnCache->pLastCellFrame != pBox) + { + pTab->GetTable()->GetTabCols( *m_pColumnCache->pLastCols, + static_cast<const SwCellFrame*>(pBox)->GetTabBox(), true); + m_pColumnCache->pLastCellFrame = pBox; + } + rToFill = *m_pColumnCache->pLastCols; + } + else + bDel = true; + } + if ( bDel ) + m_pColumnCache.reset(); + } + if (!m_pColumnCache) + { + SwDoc::GetTabCols( rToFill, static_cast<const SwCellFrame*>(pBox) ); + + m_pColumnCache.reset(new SwColCache); + m_pColumnCache->pLastCols.reset(new SwTabCols(rToFill)); + m_pColumnCache->pLastTable = pTab->GetTable(); + m_pColumnCache->pLastTabFrame = pTab; + m_pColumnCache->pLastCellFrame = pBox; + } +} + +void SwFEShell::GetTabRows_(SwTabCols &rToFill, const SwFrame *pBox) const +{ + const SwTabFrame *pTab = pBox->FindTabFrame(); + if (m_pRowCache) + { + bool bDel = true; + if (m_pRowCache->pLastTable == pTab->GetTable()) + { + bDel = false; + SwRectFnSet aRectFnSet(pTab); + const SwPageFrame* pPage = pTab->FindPageFrame(); + const tools::Long nLeftMin = ( aRectFnSet.IsVert() ? + pTab->GetPrtLeft() - pPage->getFrameArea().Left() : + pTab->GetPrtTop() - pPage->getFrameArea().Top() ); + const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0; + const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea()); + const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX; + + if (m_pRowCache->pLastTabFrame != pTab || m_pRowCache->pLastCellFrame != pBox) + bDel = true; + + if ( !bDel && + m_pRowCache->pLastCols->GetLeftMin () == nLeftMin && + m_pRowCache->pLastCols->GetLeft () == nLeft && + m_pRowCache->pLastCols->GetRight () == nRight && + m_pRowCache->pLastCols->GetRightMax() == nRightMax ) + { + rToFill = *m_pRowCache->pLastCols; + } + else + bDel = true; + } + if ( bDel ) + m_pRowCache.reset(); + } + if (!m_pRowCache) + { + SwDoc::GetTabRows( rToFill, static_cast<const SwCellFrame*>(pBox) ); + + m_pRowCache.reset(new SwColCache); + m_pRowCache->pLastCols.reset(new SwTabCols(rToFill)); + m_pRowCache->pLastTable = pTab->GetTable(); + m_pRowCache->pLastTabFrame = pTab; + m_pRowCache->pLastCellFrame = pBox; + } +} + +void SwFEShell::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly ) +{ + SwFrame *pBox = GetCurrFrame(); + if( !pBox || !pBox->IsInTab() ) + return; + + CurrShell aCurr( this ); + StartAllAction(); + + do + { + pBox = pBox->GetUpper(); + } while (pBox && !pBox->IsCellFrame()); + + GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<SwCellFrame*>(pBox) ); + EndAllActionAndCall(); +} + +void SwFEShell::GetTabCols( SwTabCols &rToFill ) const +{ + const SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + do + { + pFrame = pFrame->GetUpper(); + } + while (pFrame && !pFrame->IsCellFrame()); + + if (!pFrame) + return; + + GetTabCols_( rToFill, pFrame ); +} + +void SwFEShell::GetTabRows( SwTabCols &rToFill ) const +{ + const SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + do + { + pFrame = pFrame->GetUpper(); + } while (pFrame && !pFrame->IsCellFrame()); + + if (!pFrame) + return; + + GetTabRows_( rToFill, pFrame ); +} + +void SwFEShell::SetTabRows( const SwTabCols &rNew, bool bCurColOnly ) +{ + SwFrame *pBox = GetCurrFrame(); + if( !pBox || !pBox->IsInTab() ) + return; + + CurrShell aCurr( this ); + StartAllAction(); + + do + { + pBox = pBox->GetUpper(); + } while (pBox && !pBox->IsCellFrame()); + + GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<SwCellFrame*>(pBox) ); + EndAllActionAndCall(); +} + +void SwFEShell::GetMouseTabRows( SwTabCols &rToFill, const Point &rPt ) const +{ + const SwFrame *pBox = GetBox( rPt ); + if ( pBox ) + GetTabRows_( rToFill, pBox ); +} + +void SwFEShell::SetMouseTabRows( const SwTabCols &rNew, bool bCurColOnly, const Point &rPt ) +{ + const SwFrame *pBox = GetBox( rPt ); + if( pBox ) + { + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<const SwCellFrame*>(pBox) ); + EndAllActionAndCall(); + } +} + +void SwFEShell::SetRowSplit( const SwFormatRowSplit& rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetRowSplit( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +std::unique_ptr<SwFormatRowSplit> SwFEShell::GetRowSplit() const +{ + return SwDoc::GetRowSplit( *getShellCursor( false ) ); +} + +void SwFEShell::SetRowHeight( const SwFormatFrameSize &rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetRowHeight( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +std::unique_ptr<SwFormatFrameSize> SwFEShell::GetRowHeight() const +{ + return SwDoc::GetRowHeight( *getShellCursor( false ) ); +} + +bool SwFEShell::BalanceRowHeight( bool bTstOnly, const bool bOptimize ) +{ + CurrShell aCurr( this ); + if( !bTstOnly ) + StartAllAction(); + bool bRet = GetDoc()->BalanceRowHeight( *getShellCursor( false ), bTstOnly, bOptimize ); + if( !bTstOnly ) + EndAllActionAndCall(); + return bRet; +} + +void SwFEShell::SetRowBackground( const SvxBrushItem &rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetRowBackground( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +bool SwFEShell::GetRowBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const +{ + return SwDoc::GetRowBackground( *getShellCursor( false ), rToFill ); +} + +void SwFEShell::SetTabBorders( const SfxItemSet& rSet ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetTabBorders( *getShellCursor( false ), rSet ); + EndAllActionAndCall(); +} + +void SwFEShell::SetTabLineStyle( const Color* pColor, bool bSetLine, + const editeng::SvxBorderLine* pBorderLine ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetTabLineStyle( *getShellCursor( false ), + pColor, bSetLine, pBorderLine ); + EndAllActionAndCall(); +} + +void SwFEShell::GetTabBorders( SfxItemSet& rSet ) const +{ + SwDoc::GetTabBorders( *getShellCursor( false ), rSet ); +} + +void SwFEShell::SetBoxBackground( const SvxBrushItem &rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +bool SwFEShell::GetBoxBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const +{ + std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill); + bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp)); + rToFill.reset(static_cast<SvxBrushItem*>(aTemp.release())); + return bRetval; +} + +void SwFEShell::SetBoxDirection( const SvxFrameDirectionItem& rNew ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew ); + EndAllActionAndCall(); +} + +bool SwFEShell::GetBoxDirection( std::unique_ptr<SvxFrameDirectionItem>& rToFill ) const +{ + std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill); + bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp)); + rToFill.reset(static_cast<SvxFrameDirectionItem*>(aTemp.release())); + return bRetval; +} + +void SwFEShell::SetBoxAlign( sal_uInt16 nAlign ) +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetBoxAlign( *getShellCursor( false ), nAlign ); + EndAllActionAndCall(); +} + +sal_uInt16 SwFEShell::GetBoxAlign() const +{ + return SwDoc::GetBoxAlign( *getShellCursor( false ) ); +} + +void SwFEShell::SetTabBackground( const SvxBrushItem &rNew ) +{ + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetAttr( rNew, *pFrame->ImplFindTabFrame()->GetFormat() ); + EndAllAction(); // no call, nothing changes! + GetDoc()->getIDocumentState().SetModified(); +} + +void SwFEShell::GetTabBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const +{ + SwFrame *pFrame = GetCurrFrame(); + if( pFrame && pFrame->IsInTab() ) + rToFill = pFrame->ImplFindTabFrame()->GetFormat()->makeBackgroundBrushItem(); +} + +bool SwFEShell::HasWholeTabSelection() const +{ + // whole table selected? + if ( IsTableMode() ) + { + SwSelBoxes aBoxes; + ::GetTableSelCrs( *this, aBoxes ); + if( !aBoxes.empty() ) + { + const SwTableNode *pTableNd = IsCursorInTable(); + return pTableNd && + aBoxes[0]->GetSttIdx() - 1 == pTableNd->EndOfSectionNode()->StartOfSectionIndex() && + aBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1 == pTableNd->EndOfSectionIndex(); + } + } + return false; +} + +bool SwFEShell::HasBoxSelection() const +{ + if(!IsCursorInTable()) + return false; + // whole table selected? + if( IsTableMode() ) + return true; + SwPaM* pPam = GetCursor(); + // empty boxes are also selected as the absence of selection + bool bChg = false; + if( pPam->GetPoint() == pPam->End()) + { + bChg = true; + pPam->Exchange(); + } + SwNode* pNd; + if( pPam->GetPoint()->nNode.GetIndex() -1 == + ( pNd = &pPam->GetNode())->StartOfSectionIndex() && + !pPam->GetPoint()->nContent.GetIndex() && + pPam->GetMark()->nNode.GetIndex() + 1 == + pNd->EndOfSectionIndex()) + { + SwNodeIndex aIdx( *pNd->EndOfSectionNode(), -1 ); + SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + { + pCNd = SwNodes::GoPrevious( &aIdx ); + OSL_ENSURE( pCNd, "no ContentNode in box ??" ); + } + if( pPam->GetMark()->nContent == pCNd->Len() ) + { + if( bChg ) + pPam->Exchange(); + return true; + } + } + if( bChg ) + pPam->Exchange(); + return false; +} + +void SwFEShell::ProtectCells() +{ + SvxProtectItem aProt( RES_PROTECT ); + aProt.SetContentProtect( true ); + + CurrShell aCurr( this ); + StartAllAction(); + + GetDoc()->SetBoxAttr( *getShellCursor( false ), aProt ); + + if( !IsCursorReadonly() ) + { + if( IsTableMode() ) + ClearMark(); + ParkCursorInTab(); + } + EndAllActionAndCall(); +} + +// cancel table selection +void SwFEShell::UnProtectCells() +{ + CurrShell aCurr( this ); + StartAllAction(); + + SwSelBoxes aBoxes; + if( IsTableMode() ) + ::GetTableSelCrs( *this, aBoxes ); + else + { + SwFrame *pFrame = GetCurrFrame(); + do { + pFrame = pFrame->GetUpper(); + } while ( pFrame && !pFrame->IsCellFrame() ); + if( pFrame ) + { + SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()); + aBoxes.insert( pBox ); + } + } + + if( !aBoxes.empty() ) + GetDoc()->UnProtectCells( aBoxes ); + + EndAllActionAndCall(); +} + +void SwFEShell::UnProtectTables() +{ + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->UnProtectTables( *GetCursor() ); + EndAllActionAndCall(); +} + +bool SwFEShell::HasTableAnyProtection( const OUString* pTableName, + bool* pFullTableProtection ) +{ + return GetDoc()->HasTableAnyProtection( GetCursor()->GetPoint(), pTableName, + pFullTableProtection ); +} + +bool SwFEShell::CanUnProtectCells() const +{ + bool bUnProtectAvailable = false; + const SwTableNode *pTableNd = IsCursorInTable(); + if( pTableNd && !pTableNd->IsProtect() ) + { + SwSelBoxes aBoxes; + if( IsTableMode() ) + ::GetTableSelCrs( *this, aBoxes ); + else + { + SwFrame *pFrame = GetCurrFrame(); + do { + pFrame = pFrame->GetUpper(); + } while ( pFrame && !pFrame->IsCellFrame() ); + if( pFrame ) + { + SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()); + aBoxes.insert( pBox ); + } + } + if( !aBoxes.empty() ) + bUnProtectAvailable = ::HasProtectedCells( aBoxes ); + } + return bUnProtectAvailable; +} + +sal_uInt16 SwFEShell::GetRowsToRepeat() const +{ + const SwFrame *pFrame = GetCurrFrame(); + const SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr; + if( pTab ) + return pTab->GetTable()->GetRowsToRepeat(); + return 0; +} + +void SwFEShell::SetRowsToRepeat( sal_uInt16 nSet ) +{ + SwFrame *pFrame = GetCurrFrame(); + SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr; + if( pTab && pTab->GetTable()->GetRowsToRepeat() != nSet ) + { + SwWait aWait( *GetDoc()->GetDocShell(), true ); + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetRowsToRepeat( *pTab->GetTable(), nSet ); + EndAllActionAndCall(); + } +} + +// returns the number of rows consecutively selected from top +static sal_uInt16 lcl_GetRowNumber( const SwPosition& rPos ) +{ + Point aTmpPt; + const SwContentNode *pNd; + const SwContentFrame *pFrame; + + std::pair<Point, bool> const tmp(aTmpPt, false); + pNd = rPos.nNode.GetNode().GetContentNode(); + if( nullptr != pNd ) + pFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp); + else + pFrame = nullptr; + + const SwFrame* pRow = (pFrame && pFrame->IsInTab()) ? pFrame->GetUpper() : nullptr; + + while (pRow && (!pRow->GetUpper() || !pRow->GetUpper()->IsTabFrame())) + pRow = pRow->GetUpper(); + + if (!pRow) + return USHRT_MAX; + + const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper()); + const SwTableLine* pTabLine = static_cast<const SwRowFrame*>(pRow)->GetTabLine(); + sal_uInt16 nRet = USHRT_MAX; + sal_uInt16 nI = 0; + while ( sal::static_int_cast<SwTableLines::size_type>(nI) < pTabFrame->GetTable()->GetTabLines().size() ) + { + if ( pTabFrame->GetTable()->GetTabLines()[ nI ] == pTabLine ) + { + nRet = nI; + break; + } + ++nI; + } + + return nRet; +} + +sal_uInt16 SwFEShell::GetRowSelectionFromTop() const +{ + sal_uInt16 nRet = 0; + const SwPaM* pPaM = IsTableMode() ? GetTableCursor() : GetCursor_(); + const sal_uInt16 nPtLine = lcl_GetRowNumber( *pPaM->GetPoint() ); + + if ( !IsTableMode() ) + { + nRet = 0 == nPtLine ? 1 : 0; + } + else + { + const sal_uInt16 nMkLine = lcl_GetRowNumber( *pPaM->GetMark() ); + + if ( ( nPtLine == 0 && nMkLine != USHRT_MAX ) || + ( nMkLine == 0 && nPtLine != USHRT_MAX ) ) + { + nRet = std::max( nPtLine, nMkLine ) + 1; + } + } + + return nRet; +} + +/* + * 1. case: bRepeat = true + * returns true if the current frame is located inside a table headline in + * a follow frame + * + * 2. case: bRepeat = false + * returns true if the current frame is located inside a table headline OR + * inside the first line of a table!!! + */ +bool SwFEShell::CheckHeadline( bool bRepeat ) const +{ + bool bRet = false; + if ( !IsTableMode() ) + { + SwFrame *pFrame = GetCurrFrame(); // DONE MULTIIHEADER + SwTabFrame* pTab = (pFrame && pFrame->IsInTab()) ? pFrame->FindTabFrame() : nullptr; + if (pTab) + { + if ( bRepeat ) + { + bRet = pTab->IsFollow() && pTab->IsInHeadline( *pFrame ); + } + else + { + bRet = static_cast<SwLayoutFrame*>(pTab->Lower())->IsAnLower( pFrame ) || + pTab->IsInHeadline( *pFrame ); + } + } + } + return bRet; +} + +void SwFEShell::AdjustCellWidth( const bool bBalance, const bool bNoShrink ) +{ + CurrShell aCurr( this ); + StartAllAction(); + + // switch on wait-cursor, as we do not know how + // much content is affected + TableWait aWait(std::numeric_limits<size_t>::max(), nullptr, + *GetDoc()->GetDocShell()); + + GetDoc()->AdjustCellWidth( *getShellCursor( false ), bBalance, bNoShrink ); + EndAllActionAndCall(); +} + +bool SwFEShell::IsAdjustCellWidthAllowed( bool bBalance ) const +{ + // at least one row with content should be contained in the selection + + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return false; + + SwSelBoxes aBoxes; + ::GetTableSelCrs( *this, aBoxes ); + + if ( bBalance ) + return aBoxes.size() > 1; + + if ( aBoxes.empty() ) + { + do + { + pFrame = pFrame->GetUpper(); + } + while (pFrame && !pFrame->IsCellFrame()); + + if (!pFrame) + return false; + + SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()); + aBoxes.insert( pBox ); + } + + for (size_t i = 0; i < aBoxes.size(); ++i) + { + SwTableBox *pBox = aBoxes[i]; + if ( pBox->GetSttNd() ) + { + SwNodeIndex aIdx( *pBox->GetSttNd(), 1 ); + SwTextNode* pCNd = aIdx.GetNode().GetTextNode(); + if( !pCNd ) + pCNd = static_cast<SwTextNode*>(GetDoc()->GetNodes().GoNext( &aIdx )); + + while ( pCNd ) + { + if (!pCNd->GetText().isEmpty()) + return true; + ++aIdx; + pCNd = aIdx.GetNode().GetTextNode(); + } + } + } + return false; +} + +void SwFEShell::SetTableStyle(const OUString& rStyleName) +{ + // make sure SwDoc has the style + SwTableAutoFormat *pTableFormat = GetDoc()->GetTableStyles().FindAutoFormat(rStyleName); + if (!pTableFormat) + return; + + SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable()); + if (!pTableNode) + return; + + // set the name & update + UpdateTableStyleFormatting(pTableNode, false, &rStyleName); +} + + // AutoFormat for the table/table selection +bool SwFEShell::SetTableStyle(const SwTableAutoFormat& rStyle) +{ + // make sure SwDoc has the style + GetDoc()->GetTableStyles().AddAutoFormat(rStyle); + + SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable()); + if (!pTableNode) + return false; + + // set the name & update + return UpdateTableStyleFormatting(pTableNode, false, &rStyle.GetName()); +} + +bool SwFEShell::UpdateTableStyleFormatting(SwTableNode *pTableNode, + bool bResetDirect, OUString const*const pStyleName) +{ + if (!pTableNode) + { + pTableNode = const_cast<SwTableNode*>(IsCursorInTable()); + if (!pTableNode || pTableNode->GetTable().IsTableComplex()) + return false; + } + + OUString const aTableStyleName(pStyleName + ? *pStyleName + : pTableNode->GetTable().GetTableStyleName()); + SwTableAutoFormat* pTableStyle = GetDoc()->GetTableStyles().FindAutoFormat(aTableStyleName); + if (!pTableStyle) + return false; + + SwSelBoxes aBoxes; + + // whole table or only current selection + if( IsTableMode() ) + ::GetTableSelCrs( *this, aBoxes ); + else + { + const SwTableSortBoxes& rTBoxes = pTableNode->GetTable().GetTabSortBoxes(); + for (size_t n = 0; n < rTBoxes.size(); ++n) + { + SwTableBox* pBox = rTBoxes[ n ]; + aBoxes.insert( pBox ); + } + } + + bool bRet; + if( !aBoxes.empty() ) + { + CurrShell aCurr( this ); + StartAllAction(); + bRet = GetDoc()->SetTableAutoFormat( + aBoxes, *pTableStyle, bResetDirect, pStyleName != nullptr); + ClearFEShellTabCols(*GetDoc(), nullptr); + EndAllActionAndCall(); + } + else + bRet = false; + return bRet; +} + +bool SwFEShell::GetTableAutoFormat( SwTableAutoFormat& rGet ) +{ + const SwTableNode *pTableNd = IsCursorInTable(); + if( !pTableNd || pTableNd->GetTable().IsTableComplex() ) + return false; + + SwSelBoxes aBoxes; + + if ( !IsTableMode() ) // if cursor are not current + GetCursor(); + + // whole table or only current selection + if( IsTableMode() ) + ::GetTableSelCrs( *this, aBoxes ); + else + { + const SwTableSortBoxes& rTBoxes = pTableNd->GetTable().GetTabSortBoxes(); + for (size_t n = 0; n < rTBoxes.size(); ++n) + { + SwTableBox* pBox = rTBoxes[ n ]; + aBoxes.insert( pBox ); + } + } + + return GetDoc()->GetTableAutoFormat( aBoxes, rGet ); +} + +bool SwFEShell::DeleteTableSel() +{ + // check if SPoint/Mark of current cursor are in a table + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return false; + + if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr ) + { + ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()), + DialogMask::MessageInfo | DialogMask::ButtonsOk ); + return false; + } + + CurrShell aCurr( this ); + StartAllAction(); + + // search boxes via the layout + bool bRet; + SwSelBoxes aBoxes; + GetTableSelCrs( *this, aBoxes ); + if( !aBoxes.empty() ) + { + TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() ); + + // cursor should be removed from deletion area. + // Put them behind/on the table; via the document + // position they'll be set to the old position + while( !pFrame->IsCellFrame() ) + pFrame = pFrame->GetUpper(); + ParkCursor( SwNodeIndex( *static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetSttNd() )); + + bRet = GetDoc()->DeleteRowCol( aBoxes ); + + ClearFEShellTabCols(*GetDoc(), nullptr); + } + else + bRet = false; + EndAllActionAndCall(); + return bRet; +} + +size_t SwFEShell::GetCurTabColNum() const +{ + //!!!GetCurMouseTabColNum() mitpflegen!!!! + SwFrame *pFrame = GetCurrFrame(); + OSL_ENSURE( pFrame, "Cursor parked?" ); + + // check if SPoint/Mark of current cursor are in a table + if (!pFrame || !pFrame->IsInTab()) + return 0; + + do + { + // JP 26.09.95: why compare with ContentFrame + // and not with CellFrame ???? + pFrame = pFrame->GetUpper(); + } while (pFrame && !pFrame->IsCellFrame()); + + if (!pFrame) + return 0; + + size_t nRet = 0; + + SwRectFnSet aRectFnSet(pFrame); + + const SwPageFrame* pPage = pFrame->FindPageFrame(); + + // get TabCols, as only via these we get to the position + SwTabCols aTabCols; + GetTabCols( aTabCols ); + + if( pFrame->FindTabFrame()->IsRightToLeft() ) + { + tools::Long nX = aRectFnSet.GetRight(pFrame->getFrameArea()) - aRectFnSet.GetLeft(pPage->getFrameArea()); + + const tools::Long nRight = aTabCols.GetLeftMin() + aTabCols.GetRight(); + + if ( !::IsSame( nX, nRight ) ) + { + nX = nRight - nX + aTabCols.GetLeft(); + for ( size_t i = 0; i < aTabCols.Count(); ++i ) + if ( ::IsSame( nX, aTabCols[i] ) ) + { + nRet = i + 1; + break; + } + } + } + else + { + const tools::Long nX = aRectFnSet.GetLeft(pFrame->getFrameArea()) - + aRectFnSet.GetLeft(pPage->getFrameArea()); + + const tools::Long nLeft = aTabCols.GetLeftMin(); + + if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) ) + { + for ( size_t i = 0; i < aTabCols.Count(); ++i ) + if ( ::IsSame( nX, nLeft + aTabCols[i] ) ) + { + nRet = i + 1; + break; + } + } + } + return nRet; +} + +static const SwFrame *lcl_FindFrameInTab( const SwLayoutFrame *pLay, const Point &rPt, SwTwips nFuzzy ) +{ + const SwFrame *pFrame = pLay->Lower(); + + while( pFrame && pLay->IsAnLower( pFrame ) ) + { + if ( pFrame->getFrameArea().IsNear( rPt, nFuzzy ) ) + { + if ( pFrame->IsLayoutFrame() ) + { + const SwFrame *pTmp = ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), rPt, nFuzzy ); + if ( pTmp ) + return pTmp; + } + + return pFrame; + } + + pFrame = pFrame->FindNext(); + } + + return nullptr; +} + +static const SwCellFrame *lcl_FindFrame( const SwLayoutFrame *pLay, const Point &rPt, + SwTwips nFuzzy, bool* pbRow, bool* pbCol ) +{ + // bMouseMoveRowCols : + // Method is called for + // - Moving columns/rows with the mouse or + // - Enhanced table selection + const bool bMouseMoveRowCols = nullptr == pbCol; + + bool bCloseToRow = false; + bool bCloseToCol = false; + + const SwFrame *pFrame = pLay->ContainsContent(); + const SwFrame* pRet = nullptr; + + if ( pFrame ) + { + do + { + if ( pFrame->IsInTab() ) + pFrame = const_cast<SwFrame*>(pFrame)->ImplFindTabFrame(); + + if (!pFrame) + break; + + if ( pFrame->IsTabFrame() ) + { + Point aPt( rPt ); + bool bSearchForFrameInTab = true; + SwTwips nTmpFuzzy = nFuzzy; + + if ( !bMouseMoveRowCols ) + { + // We ignore nested tables for the enhanced table selection: + while ( pFrame->GetUpper()->IsInTab() ) + pFrame = pFrame->GetUpper()->FindTabFrame(); + + // We first check if the given point is 'close' to the left or top + // border of the table frame: + OSL_ENSURE( pFrame, "Nested table frame without outer table" ); + SwRectFnSet aRectFnSet(pFrame); + const bool bRTL = pFrame->IsRightToLeft(); + + SwRect aTabRect = pFrame->getFramePrintArea(); + aTabRect.Pos() += pFrame->getFrameArea().Pos(); + + const SwTwips nLeft = bRTL ? + aRectFnSet.GetRight(aTabRect) : + aRectFnSet.GetLeft(aTabRect); + const SwTwips nTop = aRectFnSet.GetTop(aTabRect); + + SwTwips const rPointX = aRectFnSet.IsVert() ? aPt.Y() : aPt.X(); + SwTwips const rPointY = aRectFnSet.IsVert() ? aPt.X() : aPt.Y(); + + const SwTwips nXDiff = aRectFnSet.XDiff( nLeft, rPointX ) * ( bRTL ? -1 : 1 ); + const SwTwips nYDiff = aRectFnSet.YDiff( nTop, rPointY ); + + bCloseToRow = nXDiff >= 0 && nXDiff < nFuzzy; + bCloseToCol = nYDiff >= 0 && nYDiff < nFuzzy; + + if ( bCloseToCol && 2 * nYDiff > nFuzzy ) + { + const SwFrame* pPrev = pFrame->GetPrev(); + if ( pPrev ) + { + SwRect aPrevRect = pPrev->getFramePrintArea(); + aPrevRect.Pos() += pPrev->getFrameArea().Pos(); + + if( aPrevRect.Contains( rPt ) ) + { + bCloseToCol = false; + } + } + + } + + // If we found the point to be 'close' to the left or top border + // of the table frame, we adjust the point to be on that border: + if ( bCloseToRow && bCloseToCol ) + aPt = bRTL ? aTabRect.TopRight() : aRectFnSet.GetPos(aTabRect); + else if ( bCloseToRow ) + aRectFnSet.IsVert() ? aPt.setY(nLeft) : aPt.setX(nLeft); + else if ( bCloseToCol ) + aRectFnSet.IsVert() ? aPt.setX(nTop) : aPt.setY(nTop); + + if ( !bCloseToRow && !bCloseToCol ) + bSearchForFrameInTab = false; + + // Since the point has been adjusted, we call lcl_FindFrameInTab() + // with a fuzzy value of 1: + nTmpFuzzy = 1; + } + + const SwFrame* pTmp = bSearchForFrameInTab ? + ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), aPt, nTmpFuzzy ) : + nullptr; + + if ( pTmp ) + { + pFrame = pTmp; + break; + } + } + pFrame = pFrame->FindNextCnt(); + + } while ( pFrame && pLay->IsAnLower( pFrame ) ); + } + + if ( pFrame && pFrame->IsInTab() && pLay->IsAnLower( pFrame ) ) + { + do + { + // We allow mouse drag of table borders within nested tables, + // but disallow hotspot selection of nested tables. + if ( bMouseMoveRowCols ) + { + // find the next cell frame + while ( pFrame && !pFrame->IsCellFrame() ) + pFrame = pFrame->GetUpper(); + } + else + { + // find the most upper cell frame: + while ( pFrame && + ( !pFrame->IsCellFrame() || + !pFrame->GetUpper()->GetUpper()->IsTabFrame() || + pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) ) + pFrame = pFrame->GetUpper(); + } + + if ( pFrame ) // Note: this condition should be the same like the while condition!!! + { + // #i32329# Enhanced table selection + // used for hotspot selection of tab/cols/rows + if ( !bMouseMoveRowCols ) + { + + OSL_ENSURE( pbCol && pbRow, "pbCol or pbRow missing" ); + + if ( bCloseToRow || bCloseToCol ) + { + *pbRow = bCloseToRow; + *pbCol = bCloseToCol; + pRet = pFrame; + break; + } + } + else + { + // used for mouse move of columns/rows + const SwTabFrame* pTabFrame = pFrame->FindTabFrame(); + SwRect aTabRect = pTabFrame->getFramePrintArea(); + aTabRect.Pos() += pTabFrame->getFrameArea().Pos(); + + SwRectFnSet aRectFnSet(pTabFrame); + + const SwTwips nTabTop = aRectFnSet.GetTop(aTabRect); + const SwTwips nMouseTop = aRectFnSet.IsVert() ? rPt.X() : rPt.Y(); + + // Do not allow to drag upper table border: + if ( !::IsSame( nTabTop, nMouseTop ) ) + { + if ( ::IsSame( pFrame->getFrameArea().Left(), rPt.X() ) || + ::IsSame( pFrame->getFrameArea().Right(),rPt.X() ) ) + { + if ( pbRow ) *pbRow = false; + pRet = pFrame; + break; + } + if ( ::IsSame( pFrame->getFrameArea().Top(), rPt.Y() ) || + ::IsSame( pFrame->getFrameArea().Bottom(),rPt.Y() ) ) + { + if ( pbRow ) *pbRow = true; + pRet = pFrame; + break; + } + } + } + + pFrame = pFrame->GetUpper(); + } + } while ( pFrame ); + } + + // robust: + OSL_ENSURE( !pRet || pRet->IsCellFrame(), "lcl_FindFrame() is supposed to find a cell frame!" ); + return pRet && pRet->IsCellFrame() ? static_cast<const SwCellFrame*>(pRet) : nullptr; +} + +// pbCol = 0 => Used for moving table rows/cols with mouse +// pbCol != 0 => Used for selecting table/rows/cols + +#define ENHANCED_TABLE_SELECTION_FUZZY 10 + +const SwFrame* SwFEShell::GetBox( const Point &rPt, bool* pbRow, bool* pbCol ) const +{ + const SwPageFrame *pPage = static_cast<SwPageFrame*>(GetLayout()->Lower()); + vcl::Window* pOutWin = GetWin(); + SwTwips nFuzzy = COLFUZZY; + if( pOutWin ) + { + // #i32329# Enhanced table selection + SwTwips nSize = pbCol ? ENHANCED_TABLE_SELECTION_FUZZY : RULER_MOUSE_MARGINWIDTH; + Size aTmp( nSize, nSize ); + aTmp = pOutWin->PixelToLogic( aTmp ); + nFuzzy = aTmp.Width(); + } + + while ( pPage && !pPage->getFrameArea().IsNear( rPt, nFuzzy ) ) + pPage = static_cast<const SwPageFrame*>(pPage->GetNext()); + + const SwCellFrame *pFrame = nullptr; + if ( pPage ) + { + // We cannot search the box by GetModelPositionForViewPoint or GetContentPos. + // This would lead to a performance collapse for documents + // with a lot of paragraphs/tables on one page + //(BrowseMode!) + + // check flys first + if ( pPage->GetSortedObjs() ) + { + for ( size_t i = 0; !pFrame && i < pPage->GetSortedObjs()->size(); ++i ) + { + SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i]; + if ( auto pFlyFrame = pObj->DynCastFlyFrame() ) + { + pFrame = lcl_FindFrame( pFlyFrame, rPt, nFuzzy, pbRow, pbCol ); + } + } + } + const SwLayoutFrame *pLay = static_cast<const SwLayoutFrame*>(pPage->Lower()); + while ( pLay && !pFrame ) + { + pFrame = lcl_FindFrame( pLay, rPt, nFuzzy, pbRow, pbCol ); + pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext()); + } + } + return pFrame; +} + +/* Helper function*/ +/* calculated the distance between Point rC and Line Segment (rA, rB) */ +static double lcl_DistancePoint2Segment( const Point& rA, const Point& rB, const Point& rC ) +{ + double nRet = 0; + + const basegfx::B2DVector aBC( rC.X() - rB.X(), rC.Y() - rB.Y() ); + const basegfx::B2DVector aAB( rB.X() - rA.X(), rB.Y() - rA.Y() ); + const double nDot1 = aBC.scalar( aAB ); + + if ( nDot1 > 0 ) // check outside case 1 + nRet = aBC.getLength(); + else + { + const basegfx::B2DVector aAC( rC.X() - rA.X(), rC.Y() - rA.Y() ); + const basegfx::B2DVector aBA( rA.X() - rB.X(), rA.Y() - rB.Y() ); + const double nDot2 = aAC.scalar( aBA ); + + if ( nDot2 > 0 ) // check outside case 2 + nRet = aAC.getLength(); + else + { + const double nDiv = aAB.getLength(); + nRet = nDiv ? aAB.cross( aAC ) / nDiv : 0; + } + } + + return std::abs(nRet); +} + +/* Helper function*/ +static Point lcl_ProjectOntoClosestTableFrame( const SwTabFrame& rTab, const Point& rPoint, bool bRowDrag ) +{ + Point aRet( rPoint ); + const SwTabFrame* pCurrentTab = &rTab; + const bool bVert = pCurrentTab->IsVertical(); + const bool bRTL = pCurrentTab->IsRightToLeft(); + + // Western Layout: + // bRowDrag = true => compare to left border of table + // bRowDrag = false => compare to top border of table + + // Asian Layout: + // bRowDrag = true => compare to right border of table + // bRowDrag = false => compare to top border of table + + // RTL Layout: + // bRowDrag = true => compare to right border of table + // bRowDrag = false => compare to top border of table + bool bLeft = false; + bool bRight = false; + + if ( bRowDrag ) + { + if ( bVert || bRTL ) + bRight = true; + else + bLeft = true; + } + + // used to find the minimal distance + double nMin = -1; + Point aMin1; + Point aMin2; + + Point aS1; + Point aS2; + + while ( pCurrentTab ) + { + SwRect aTabRect( pCurrentTab->getFramePrintArea() ); + aTabRect += pCurrentTab->getFrameArea().Pos(); + + if ( bLeft ) + { + // distance to left table border + aS1 = aTabRect.TopLeft(); + aS2 = aTabRect.BottomLeft(); + } + else if ( bRight ) + { + // distance to right table border + aS1 = aTabRect.TopRight(); + aS2 = aTabRect.BottomRight(); + } + else //if ( bTop ) + { + // distance to top table border + aS1 = aTabRect.TopLeft(); + aS2 = aTabRect.TopRight(); + } + + const double nDist = lcl_DistancePoint2Segment( aS1, aS2, rPoint ); + + if ( nDist < nMin || -1 == nMin ) + { + aMin1 = aS1; + aMin2 = aS2; + nMin = nDist; + } + + pCurrentTab = pCurrentTab->GetFollow(); + } + + // project onto closest line: + if ( bLeft || bRight ) + { + aRet.setX(aMin1.getX()); + if ( aRet.getY() > aMin2.getY() ) + aRet.setY(aMin2.getY()); + else if ( aRet.getY() < aMin1.getY() ) + aRet.setY(aMin1.getY()); + } + else + { + aRet.setY(aMin1.getY()); + if ( aRet.getX() > aMin2.getX() ) + aRet.setX(aMin2.getX()); + else if ( aRet.getX() < aMin1.getX() ) + aRet.setX(aMin1.getX()); + } + + return aRet; +} + +// #i32329# Enhanced table selection +bool SwFEShell::SelTableRowCol( const Point& rPt, const Point* pEnd, bool bRowDrag ) +{ + bool bRet = false; + Point aEndPt; + if ( pEnd ) + aEndPt = *pEnd; + + SwPosition* ppPos[2] = { nullptr, nullptr }; + Point paPt [2] = { rPt, aEndPt }; + bool pbRow[2] = { false, false }; + bool pbCol[2] = { false, false }; + + // pEnd is set during dragging. + for ( sal_uInt16 i = 0; i < ( pEnd ? 2 : 1 ); ++i ) + { + const SwCellFrame* pFrame = + static_cast<const SwCellFrame*>(GetBox( paPt[i], &pbRow[i], &pbCol[i] ) ); + + if( pFrame ) + { + while( pFrame && pFrame->Lower() && pFrame->Lower()->IsRowFrame() ) + pFrame = static_cast<const SwCellFrame*>( static_cast<const SwLayoutFrame*>( pFrame->Lower() )->Lower() ); + if( pFrame && pFrame->GetTabBox()->GetSttNd() && + pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() ) + pFrame = nullptr; + } + + if ( pFrame ) + { + const SwContentFrame* pContent = ::GetCellContent( *pFrame ); + + if ( pContent && pContent->IsTextFrame() ) + { + + ppPos[i] = new SwPosition(static_cast<SwTextFrame const*>(pContent)->MapViewToModelPos(TextFrameIndex(0))); + + // paPt[i] will not be used any longer, now we use it to store + // a position inside the content frame + paPt[i] = pContent->getFrameArea().Center(); + } + } + + // no calculation of end frame if start frame has not been found. + if ( 1 == i || !ppPos[0] || !pEnd || !pFrame ) + break; + + // find 'closest' table frame to pEnd: + const SwTabFrame* pCurrentTab = pFrame->FindTabFrame(); + if ( pCurrentTab->IsFollow() ) + pCurrentTab = pCurrentTab->FindMaster( true ); + + const Point aProjection = lcl_ProjectOntoClosestTableFrame( *pCurrentTab, *pEnd, bRowDrag ); + paPt[1] = aProjection; + } + + if ( ppPos[0] ) + { + SwShellCursor* pCursor = GetCursor_(); + SwCursorSaveState aSaveState( *pCursor ); + SwPosition aOldPos( *pCursor->GetPoint() ); + + pCursor->DeleteMark(); + *pCursor->GetPoint() = *ppPos[0]; + pCursor->GetPtPos() = paPt[0]; + + if ( !pCursor->IsInProtectTable() ) + { + bool bNewSelection = true; + + if ( ppPos[1] ) + { + if ( ppPos[1]->nNode.GetNode().StartOfSectionNode() != + aOldPos.nNode.GetNode().StartOfSectionNode() ) + { + pCursor->SetMark(); + SwCursorSaveState aSaveState2( *pCursor ); + *pCursor->GetPoint() = *ppPos[1]; + pCursor->GetPtPos() = paPt[1]; + + if ( pCursor->IsInProtectTable( false, false ) ) + { + pCursor->RestoreSavePos(); + bNewSelection = false; + } + } + else + { + pCursor->RestoreSavePos(); + bNewSelection = false; + } + } + + if ( bNewSelection ) + { + // #i35543# SelTableRowCol should remove any existing + // table cursor: + if ( IsTableMode() ) + TableCursorToCursor(); + + if ( pbRow[0] && pbCol[0] ) + bRet = SwCursorShell::SelTable(); + else if ( pbRow[0] ) + bRet = SwCursorShell::SelTableRowOrCol( true, true ); + else if ( pbCol[0] ) + bRet = SwCursorShell::SelTableRowOrCol( false, true ); + } + else + bRet = true; + } + + delete ppPos[0]; + delete ppPos[1]; + } + + return bRet; +} + +SwTab SwFEShell::WhichMouseTabCol( const Point &rPt ) const +{ + SwTab nRet = SwTab::COL_NONE; + bool bRow = false; + bool bCol = false; + bool bSelect = false; + + // First try: Do we get the row/col move cursor? + const SwCellFrame* pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow )); + + if ( !pFrame ) + { + // Second try: Do we get the row/col/tab selection cursor? + pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow, &bCol )); + bSelect = true; + } + + if( pFrame ) + { + while( pFrame && pFrame->Lower() && pFrame->Lower()->IsRowFrame() ) + pFrame = static_cast<const SwCellFrame*>(static_cast<const SwLayoutFrame*>(pFrame->Lower())->Lower()); + if( pFrame && pFrame->GetTabBox()->GetSttNd() && + pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() ) + pFrame = nullptr; + } + + if( pFrame ) + { + if ( !bSelect ) + { + if ( pFrame->IsVertical() ) + nRet = bRow ? SwTab::COL_VERT : SwTab::ROW_VERT; + else + nRet = bRow ? SwTab::ROW_HORI : SwTab::COL_HORI; + } + else + { + const SwTabFrame* pTabFrame = pFrame->FindTabFrame(); + if ( pTabFrame->IsVertical() ) + { + if ( bRow && bCol ) + { + nRet = SwTab::SEL_VERT; + } + else if ( bRow ) + { + nRet = SwTab::ROWSEL_VERT; + } + else if ( bCol ) + { + nRet = SwTab::COLSEL_VERT; + } + } + else + { + if ( bRow && bCol ) + { + nRet = pTabFrame->IsRightToLeft() ? + SwTab::SEL_HORI_RTL : + SwTab::SEL_HORI; + } + else if ( bRow ) + { + nRet = pTabFrame->IsRightToLeft() ? + SwTab::ROWSEL_HORI_RTL : + SwTab::ROWSEL_HORI; + } + else if ( bCol ) + { + nRet = SwTab::COLSEL_HORI; + } + } + } + } + + return nRet; +} + +// -> #i23726# +SwTextNode * SwFEShell::GetNumRuleNodeAtPos( const Point &rPt) +{ + SwTextNode * pResult = nullptr; + + SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel); + + if( GetContentAtPos(rPt, aContentAtPos) && aContentAtPos.aFnd.pNode) + pResult = aContentAtPos.aFnd.pNode->GetTextNode(); + + return pResult; +} + +bool SwFEShell::IsNumLabel( const Point &rPt, int nMaxOffset ) +{ + bool bResult = false; + + SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel); + + if( GetContentAtPos(rPt, aContentAtPos)) + { + if ((nMaxOffset >= 0 && aContentAtPos.nDist <= nMaxOffset) || + (nMaxOffset < 0)) + bResult = true; + } + + return bResult; +} +// <- #i23726# + +// #i42921# +bool SwFEShell::IsVerticalModeAtNdAndPos( const SwTextNode& _rTextNode, + const Point& _rDocPos ) +{ + bool bRet( false ); + + const SvxFrameDirection nTextDir = + _rTextNode.GetTextDirection( SwPosition(_rTextNode), &_rDocPos ); + switch ( nTextDir ) + { + case SvxFrameDirection::Unknown: + case SvxFrameDirection::Horizontal_RL_TB: + case SvxFrameDirection::Horizontal_LR_TB: + { + bRet = false; + } + break; + case SvxFrameDirection::Vertical_LR_TB: + case SvxFrameDirection::Vertical_RL_TB: + { + bRet = true; + } + break; + default: break; + } + + return bRet; +} + +void SwFEShell::GetMouseTabCols( SwTabCols &rToFill, const Point &rPt ) const +{ + const SwFrame *pBox = GetBox( rPt ); + if ( pBox ) + GetTabCols_( rToFill, pBox ); +} + +void SwFEShell::SetMouseTabCols( const SwTabCols &rNew, bool bCurRowOnly, + const Point &rPt ) +{ + const SwFrame *pBox = GetBox( rPt ); + if( pBox ) + { + CurrShell aCurr( this ); + StartAllAction(); + GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<const SwCellFrame*>(pBox) ); + EndAllActionAndCall(); + } +} + +sal_uInt16 SwFEShell::GetCurMouseColNum( const Point &rPt ) const +{ + return GetCurColNum_( GetBox( rPt ), nullptr ); +} + +size_t SwFEShell::GetCurMouseTabColNum( const Point &rPt ) const +{ + //!!!GetCurTabColNum() mitpflegen!!!! + size_t nRet = 0; + + const SwFrame *pFrame = GetBox( rPt ); + OSL_ENSURE( pFrame, "Table not found" ); + if( pFrame ) + { + const tools::Long nX = pFrame->getFrameArea().Left(); + + // get TabCols, only via these we get the position + SwTabCols aTabCols; + GetMouseTabCols( aTabCols, rPt ); + + const tools::Long nLeft = aTabCols.GetLeftMin(); + + if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) ) + { + for ( size_t i = 0; i < aTabCols.Count(); ++i ) + if ( ::IsSame( nX, nLeft + aTabCols[i] ) ) + { + nRet = i + 1; + break; + } + } + } + return nRet; +} + +void ClearFEShellTabCols(SwDoc & rDoc, SwTabFrame const*const pFrame) +{ + auto const pShell(rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()); + if (pShell) + { + for (SwViewShell& rCurrentShell : pShell->GetRingContainer()) + { + if (auto const pFE = dynamic_cast<SwFEShell *>(&rCurrentShell)) + { + pFE->ClearColumnRowCache(pFrame); + } + } + } +} + +void SwFEShell::ClearColumnRowCache(SwTabFrame const*const pFrame) +{ + if (m_pColumnCache) + { + if (pFrame == nullptr || pFrame == m_pColumnCache->pLastTabFrame) + { + m_pColumnCache.reset(); + } + } + if (m_pRowCache) + { + if (pFrame == nullptr || pFrame == m_pRowCache->pLastTabFrame) + { + m_pRowCache.reset(); + } + } +} + +void SwFEShell::GetTableAttr( SfxItemSet &rSet ) const +{ + SwFrame *pFrame = GetCurrFrame(); + if( pFrame && pFrame->IsInTab() ) + rSet.Put( pFrame->ImplFindTabFrame()->GetFormat()->GetAttrSet() ); +} + +void SwFEShell::SetTableAttr( const SfxItemSet &rNew ) +{ + SwFrame *pFrame = GetCurrFrame(); + if( pFrame && pFrame->IsInTab() ) + { + CurrShell aCurr( this ); + StartAllAction(); + SwTabFrame *pTab = pFrame->FindTabFrame(); + pTab->GetTable()->SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); + GetDoc()->SetAttr( rNew, *pTab->GetFormat() ); + GetDoc()->getIDocumentState().SetModified(); + EndAllActionAndCall(); + } +} + +// change a cell width/cell height/column width/row height +void SwFEShell::SetColRowWidthHeight( TableChgWidthHeightType eType, sal_uInt16 nDiff ) +{ + SwFrame *pFrame = GetCurrFrame(); + if( !pFrame || !pFrame->IsInTab() ) + return; + + CurrShell aCurr( this ); + StartAllAction(); + + do { + pFrame = pFrame->GetUpper(); + } while( !pFrame->IsCellFrame() ); + + SwTabFrame *pTab = pFrame->ImplFindTabFrame(); + + // if the table is in relative values (USHRT_MAX) + // then it should be recalculated to absolute values now + const SwFormatFrameSize& rTableFrameSz = pTab->GetFormat()->GetFrameSize(); + SwRectFnSet aRectFnSet(pTab); + tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea()); + TableChgWidthHeightType eTypePos = extractPosition(eType); + if( TableChgMode::VarWidthChangeAbs == pTab->GetTable()->GetTableChgMode() && + ( eTypePos == TableChgWidthHeightType::ColLeft || eTypePos == TableChgWidthHeightType::ColRight ) && + text::HoriOrientation::NONE == pTab->GetFormat()->GetHoriOrient().GetHoriOrient() && + nPrtWidth != rTableFrameSz.GetWidth() ) + { + SwFormatFrameSize aSz( rTableFrameSz ); + aSz.SetWidth( pTab->getFramePrintArea().Width() ); + pTab->GetFormat()->SetFormatAttr( aSz ); + } + + SwTwips nLogDiff = nDiff; + nLogDiff *= pTab->GetFormat()->GetFrameSize().GetWidth(); + nLogDiff /= nPrtWidth; + + /** The cells are destroyed in here */ + GetDoc()->SetColRowWidthHeight( + *const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()), + eType, nDiff, nLogDiff ); + + ClearFEShellTabCols(*GetDoc(), nullptr); + EndAllActionAndCall(); +} + +static bool lcl_IsFormulaSelBoxes( const SwTable& rTable, const SwTableBoxFormula& rFormula, + SwCellFrames& rCells ) +{ + SwTableBoxFormula aTmp( rFormula ); + SwSelBoxes aBoxes; + aTmp.GetBoxesOfFormula(rTable, aBoxes); + for (size_t nSelBoxes = aBoxes.size(); nSelBoxes; ) + { + SwTableBox* pBox = aBoxes[ --nSelBoxes ]; + + if( std::none_of(rCells.begin(), rCells.end(), [&pBox](SwCellFrame* pFrame) { return pFrame->GetTabBox() == pBox; }) ) + return false; + } + + return true; +} + + // ask formula for auto-sum +void SwFEShell::GetAutoSum( OUString& rFormula ) const +{ + SwFrame *pFrame = GetCurrFrame(); + SwTabFrame *pTab = pFrame ? pFrame->ImplFindTabFrame() : nullptr; + if( !pTab ) + return; + + SwCellFrames aCells; + OUString sFields; + if( ::GetAutoSumSel( *this, aCells )) + { + sal_uInt16 nW = 0; + for( size_t n = aCells.size(); n; ) + { + SwCellFrame* pCFrame = aCells[ --n ]; + sal_uInt16 nBoxW = pCFrame->GetTabBox()->IsFormulaOrValueBox(); + if( !nBoxW ) + break; + + if( !nW ) + { + if( USHRT_MAX == nBoxW ) + continue; // skip space at beginning + + // formula only if box is contained + if( RES_BOXATR_FORMULA == nBoxW && + !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame-> + GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells)) + { + nW = RES_BOXATR_VALUE; + // restore previous spaces! + for( size_t i = aCells.size(); n+1 < i; ) + { + sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">" + + sFields; + } + } + else + nW = nBoxW; + } + else if( RES_BOXATR_VALUE == nW ) + { + // search for values, Value/Formula/Text found -> include + if( RES_BOXATR_FORMULA == nBoxW && + ::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame-> + GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells )) + break; + else if( USHRT_MAX != nBoxW ) + sFields = OUStringChar(cListDelim) + sFields; + else + break; + } + else if( RES_BOXATR_FORMULA == nW ) + { + // only continue search when the current formula points to + // all boxes contained in the selection + if( RES_BOXATR_FORMULA == nBoxW ) + { + if( !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame-> + GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells )) + { + // redo only for values! + + nW = RES_BOXATR_VALUE; + sFields.clear(); + // restore previous spaces! + for( size_t i = aCells.size(); n+1 < i; ) + { + sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">" + + sFields; + } + } + else + sFields = OUStringChar(cListDelim) + sFields; + } + else if( USHRT_MAX == nBoxW ) + break; + else + continue; // ignore this box + } + else + // all other stuff terminates the loop + // possibly allow texts?? + break; + + sFields = "<" + pCFrame->GetTabBox()->GetName() + ">" + sFields; + } + } + + rFormula = OUString::createFromAscii( sCalc_Sum ); + if (!sFields.isEmpty()) + { + rFormula += "(" + sFields + ")"; + } +} + +bool SwFEShell::IsTableRightToLeft() const +{ + SwFrame *pFrame = GetCurrFrame(); + SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr; + if (!pTab) + return false; + return pTab->IsRightToLeft(); +} + +bool SwFEShell::IsMouseTableRightToLeft(const Point &rPt) const +{ + SwFrame *pFrame = const_cast<SwFrame *>(GetBox( rPt )); + const SwTabFrame* pTabFrame = pFrame ? pFrame->ImplFindTabFrame() : nullptr; + OSL_ENSURE( pTabFrame, "Table not found" ); + return pTabFrame && pTabFrame->IsRightToLeft(); +} + +bool SwFEShell::IsTableVertical() const +{ + SwFrame *pFrame = GetCurrFrame(); + SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr; + if (!pTab) + return false; + return pTab->IsVertical(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/frmedt/fews.cxx b/sw/source/core/frmedt/fews.cxx new file mode 100644 index 000000000..0e9a0a2ca --- /dev/null +++ b/sw/source/core/frmedt/fews.cxx @@ -0,0 +1,1332 @@ +/* -*- 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/svdobj.hxx> +#include <osl/diagnose.h> +#include <comphelper/lok.hxx> +#include <init.hxx> +#include <fesh.hxx> +#include <tabcol.hxx> +#include <pagefrm.hxx> +#include <rootfrm.hxx> +#include <cntfrm.hxx> +#include <doc.hxx> +#include <frmtool.hxx> +#include <swtable.hxx> +#include <viewimp.hxx> +#include <dview.hxx> +#include <flyfrm.hxx> +#include <node.hxx> +#include <pam.hxx> +#include <sectfrm.hxx> +#include <fmtpdsc.hxx> +#include <fmtsrnd.hxx> +#include <fmtcntnt.hxx> +#include <fmtfsize.hxx> +#include <tabfrm.hxx> +#include <flyfrms.hxx> +#include <txtfrm.hxx> +#include <mdiexp.hxx> +#include <pagedesc.hxx> +#include <fmtanchr.hxx> +#include <environmentofanchoredobject.hxx> +#include <ndtxt.hxx> +#include <dflyobj.hxx> +#include <dcontact.hxx> +#include <UndoInsert.hxx> + +using namespace com::sun::star; + +namespace +{ +/** + * This mutex is only used for the paste listeners, where the solar mutex can't + * be used. + */ +osl::Mutex& GetPasteMutex() +{ + static osl::Mutex aMutex; + return aMutex; +} +} + +void SwFEShell::EndAllActionAndCall() +{ + for(SwViewShell& rCurrentShell : GetRingContainer()) + { + if( dynamic_cast<const SwCursorShell*>( &rCurrentShell) != nullptr ) + { + static_cast<SwFEShell*>(&rCurrentShell)->EndAction(); + static_cast<SwFEShell*>(&rCurrentShell)->CallChgLnk(); + } + else + rCurrentShell.EndAction(); + } +} + +// Determine the Content's nearest to the point +Point SwFEShell::GetContentPos( const Point& rPoint, bool bNext ) const +{ + CurrShell aCurr( const_cast<SwFEShell*>(this) ); + return GetLayout()->GetNextPrevContentPos( rPoint, bNext ); +} + +const SwRect& SwFEShell::GetAnyCurRect( CurRectType eType, const Point* pPt, + const uno::Reference < embed::XEmbeddedObject >& xObj ) const +{ + const SwFrame *pFrame = Imp()->HasDrawView() + ? ::GetFlyFromMarked( &Imp()->GetDrawView()->GetMarkedObjectList(), + const_cast<SwFEShell*>(this)) + : nullptr; + + if( !pFrame ) + { + if( pPt ) + { + SwPosition aPos( *GetCursor()->GetPoint() ); + Point aPt( *pPt ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aPt ); + SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode(); + std::pair<Point, bool> const tmp(*pPt, true); + pFrame = pNd->getLayoutFrame(GetLayout(), nullptr, &tmp); + } + else + { + const bool bOldCallbackActionEnabled = GetLayout()->IsCallbackActionEnabled(); + if( bOldCallbackActionEnabled ) + GetLayout()->SetCallbackActionEnabled( false ); + pFrame = GetCurrFrame(); + if( bOldCallbackActionEnabled ) + GetLayout()->SetCallbackActionEnabled( true ); + } + } + + if( !pFrame ) + return GetLayout()->getFrameArea(); + + bool bFrame = true; + switch ( eType ) + { + case CurRectType::PagePrt: bFrame = false; + [[fallthrough]]; + case CurRectType::Page : pFrame = pFrame->FindPageFrame(); + break; + + case CurRectType::PageCalc: + { + DisableCallbackAction a(const_cast<SwRootFrame&>(*pFrame->getRootFrame())); + pFrame->Calc(Imp()->GetShell()->GetOut()); + pFrame = pFrame->FindPageFrame(); + pFrame->Calc(Imp()->GetShell()->GetOut()); + } + break; + + case CurRectType::FlyEmbeddedPrt: + bFrame = false; + [[fallthrough]]; + case CurRectType::FlyEmbedded: + { + const SwFrame *pFlyFrame = xObj.is() ? FindFlyFrame(xObj) : nullptr; + pFrame = pFlyFrame ? pFlyFrame + : pFrame->IsFlyFrame() + ? pFrame + : pFrame->FindFlyFrame(); + break; + } + case CurRectType::SectionOutsideTable : + if( pFrame->IsInTab() ) + pFrame = pFrame->FindTabFrame(); + else { + OSL_FAIL( "Missing Table" ); + } + [[fallthrough]]; + case CurRectType::SectionPrt: + case CurRectType::Section: + if( pFrame->IsInSct() ) + pFrame = pFrame->FindSctFrame(); + else { + OSL_FAIL( "Missing section" ); + } + + if( CurRectType::SectionPrt == eType ) + bFrame = false; + break; + + case CurRectType::HeaderFooter: + pFrame = pFrame->FindFooterOrHeader(); + if( nullptr == pFrame ) + return GetLayout()->getFrameArea(); + break; + + case CurRectType::PagesArea: + return GetLayout()->GetPagesArea(); + + default: break; + } + return bFrame ? pFrame->getFrameArea() : pFrame->getFramePrintArea(); +} + +sal_uInt16 SwFEShell::GetPageNumber( const Point &rPoint ) const +{ + const SwFrame *pPage = GetLayout()->Lower(); + while ( pPage && !pPage->getFrameArea().Contains( rPoint ) ) + pPage = pPage->GetNext(); + if ( pPage ) + return static_cast<const SwPageFrame*>(pPage)->GetPhyPageNum(); + else + return 0; +} + +bool SwFEShell::GetPageNumber( tools::Long nYPos, bool bAtCursorPos, sal_uInt16& rPhyNum, sal_uInt16& rVirtNum, OUString &rDisplay) const +{ + const SwFrame *pPage; + + if ( bAtCursorPos ) // get page of Cursor + { + pPage = GetCurrFrame( false ); + if ( pPage ) + pPage = pPage->FindPageFrame(); + } + else if ( nYPos > -1 ) // determine page via the position + { + pPage = GetLayout()->Lower(); + while( pPage && (pPage->getFrameArea().Bottom() < nYPos || + nYPos < pPage->getFrameArea().Top() ) ) + pPage = pPage->GetNext(); + } + else // first visible page + { + pPage = Imp()->GetFirstVisPage(GetOut()); + if ( pPage && static_cast<const SwPageFrame*>(pPage)->IsEmptyPage() ) + pPage = pPage->GetNext(); + } + + if( pPage ) + { + rPhyNum = static_cast<const SwPageFrame*>(pPage)->GetPhyPageNum(); + rVirtNum = static_cast<const SwPageFrame*>(pPage)->GetVirtPageNum(); + const SvxNumberType& rNum = static_cast<const SwPageFrame*>(pPage)->GetPageDesc()->GetNumType(); + rDisplay = rNum.GetNumStr( rVirtNum ); + } + + return nullptr != pPage; +} + +bool SwFEShell::IsDirectlyInSection() const +{ + SwFrame* pFrame = GetCurrFrame( false ); + return pFrame && pFrame->GetUpper() && pFrame->GetUpper()->IsSctFrame(); +} + +FrameTypeFlags SwFEShell::GetFrameType( const Point *pPt, bool bStopAtFly ) const +{ + FrameTypeFlags nReturn = FrameTypeFlags::NONE; + const SwFrame *pFrame; + if ( pPt ) + { + SwPosition aPos( *GetCursor()->GetPoint() ); + Point aPt( *pPt ); + GetLayout()->GetModelPositionForViewPoint( &aPos, aPt ); + SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode(); + std::pair<Point, bool> const tmp(*pPt, true); + pFrame = pNd->getLayoutFrame(GetLayout(), nullptr, &tmp); + } + else + pFrame = GetCurrFrame( false ); + while ( pFrame ) + { + switch ( pFrame->GetType() ) + { + case SwFrameType::Column: + if( pFrame->GetUpper()->IsSctFrame() ) + { + // Check, if isn't not only a single column + // from a section with footnotes at the end. + if( pFrame->GetNext() || pFrame->GetPrev() ) + // Sectioncolumns + nReturn |= ( nReturn & FrameTypeFlags::TABLE ) ? + FrameTypeFlags::COLSECTOUTTAB : FrameTypeFlags::COLSECT; + } + else // only pages and frame columns + nReturn |= FrameTypeFlags::COLUMN; + break; + case SwFrameType::Page: + nReturn |= FrameTypeFlags::PAGE; + if( static_cast<const SwPageFrame*>(pFrame)->IsFootnotePage() ) + nReturn |= FrameTypeFlags::FTNPAGE; + break; + case SwFrameType::Header: nReturn |= FrameTypeFlags::HEADER; break; + case SwFrameType::Footer: nReturn |= FrameTypeFlags::FOOTER; break; + case SwFrameType::Body: + if( pFrame->GetUpper()->IsPageFrame() ) // not for ColumnFrames + nReturn |= FrameTypeFlags::BODY; + break; + case SwFrameType::Ftn: nReturn |= FrameTypeFlags::FOOTNOTE; break; + case SwFrameType::Fly: + if( static_cast<const SwFlyFrame*>(pFrame)->IsFlyLayFrame() ) + nReturn |= FrameTypeFlags::FLY_FREE; + else if ( static_cast<const SwFlyFrame*>(pFrame)->IsFlyAtContentFrame() ) + nReturn |= FrameTypeFlags::FLY_ATCNT; + else + { + OSL_ENSURE( static_cast<const SwFlyFrame*>(pFrame)->IsFlyInContentFrame(), + "New frametype?" ); + nReturn |= FrameTypeFlags::FLY_INCNT; + } + nReturn |= FrameTypeFlags::FLY_ANY; + if( bStopAtFly ) + return nReturn; + break; + case SwFrameType::Tab: + case SwFrameType::Row: + case SwFrameType::Cell: nReturn |= FrameTypeFlags::TABLE; break; + default: /* do nothing */ break; + } + if ( pFrame->IsFlyFrame() ) + pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(); + else + pFrame = pFrame->GetUpper(); + } + return nReturn; +} + +void SwFEShell::ShellGetFocus() +{ + ::SetShell( this ); + SwCursorShell::ShellGetFocus(); + + if ( HasDrawView() ) + { + if (!comphelper::LibreOfficeKit::isActive()) + Imp()->GetDrawView()->showMarkHandles(); + if ( Imp()->GetDrawView()->AreObjectsMarked() ) + FrameNotify( this, FLY_DRAG_START ); + } +} + +void SwFEShell::ShellLoseFocus() +{ + SwCursorShell::ShellLoseFocus(); + + if ( HasDrawView() && Imp()->GetDrawView()->AreObjectsMarked() ) + { + if (!comphelper::LibreOfficeKit::isActive()) + Imp()->GetDrawView()->hideMarkHandles(); + FrameNotify( this, FLY_DRAG_END ); + } +} + +sal_uInt16 SwFEShell::GetPhyPageNum() const +{ + SwFrame *pFrame = GetCurrFrame(); + if ( pFrame ) + return pFrame->GetPhyPageNum(); + return 0; +} + +sal_uInt16 SwFEShell::GetVirtPageNum() const +{ + SwFrame *pFrame = GetCurrFrame(); + if ( pFrame ) + return pFrame->GetVirtPageNum(); + return 0; +} + +static void lcl_SetAPageOffset( sal_uInt16 nOffset, SwPageFrame* pPage, SwFEShell* pThis ) +{ + pThis->StartAllAction(); + OSL_ENSURE( pPage->FindFirstBodyContent(), + "SwFEShell _SetAPageOffset() without ContentFrame" ); + + SwFormatPageDesc aDesc( pPage->GetPageDesc() ); + aDesc.SetNumOffset( nOffset ); + + SwFrame *pFrame = pThis->GetCurrFrame( false ); + if ( pFrame->IsInTab() ) + pThis->GetDoc()->SetAttr( aDesc, *pFrame->FindTabFrame()->GetFormat() ); + else + { + pThis->GetDoc()->getIDocumentContentOperations().InsertPoolItem( + *pThis->GetCursor(), aDesc, SetAttrMode::DEFAULT, pThis->GetLayout()); + } + + pThis->EndAllAction(); +} + +void SwFEShell::SetNewPageOffset( sal_uInt16 nOffset ) +{ + GetLayout()->SetVirtPageNum( true ); + const SwPageFrame *pPage = GetCurrFrame( false )->FindPageFrame(); + lcl_SetAPageOffset( nOffset, const_cast<SwPageFrame*>(pPage), this ); +} + +void SwFEShell::SetPageOffset( sal_uInt16 nOffset ) +{ + const SwPageFrame *pPage = GetCurrFrame( false )->FindPageFrame(); + const SwRootFrame* pDocLayout = GetLayout(); + while ( pPage ) + { + const SwFrame *pFlow = pPage->FindFirstBodyContent(); + if ( pFlow ) + { + if ( pFlow->IsInTab() ) + pFlow = pFlow->FindTabFrame(); + const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem(); + if ( rPgDesc.GetNumOffset() ) + { + pDocLayout->SetVirtPageNum( true ); + lcl_SetAPageOffset( nOffset, const_cast<SwPageFrame*>(pPage), this ); + break; + } + } + pPage = static_cast<const SwPageFrame*>(pPage->GetPrev()); + } +} + +sal_uInt16 SwFEShell::GetPageOffset() const +{ + const SwPageFrame *pPage = GetCurrFrame()->FindPageFrame(); + while ( pPage ) + { + const SwFrame *pFlow = pPage->FindFirstBodyContent(); + if ( pFlow ) + { + if ( pFlow->IsInTab() ) + pFlow = pFlow->FindTabFrame(); + ::std::optional<sal_uInt16> oNumOffset = pFlow->GetPageDescItem().GetNumOffset(); + if ( oNumOffset ) + return *oNumOffset; + } + pPage = static_cast<const SwPageFrame*>(pPage->GetPrev()); + } + return 0; +} + +void SwFEShell::InsertLabel( const SwLabelType eType, const OUString &rText, const OUString& rSeparator, + const OUString& rNumberSeparator, + const bool bBefore, const sal_uInt16 nId, + const OUString& rCharacterStyle, + const bool bCpyBrd ) +{ + // get node index of cursor position, SwDoc can do everything else itself + SwContentFrame *pCnt = SwLabelType::Draw==eType ? nullptr : GetCurrFrame( false ); + if( SwLabelType::Draw!=eType && !pCnt ) + return; + + StartAllAction(); + SwRewriter aRewriter(SwUndoInsertLabel::CreateRewriter(rText)); + StartUndo(SwUndoId::INSERTLABEL, &aRewriter); + + SwNodeOffset nIdx(0); + bool bInnerCntIsFly = false; + SwFlyFrameFormat* pFlyFormat = nullptr; + switch( eType ) + { + case SwLabelType::Object: + case SwLabelType::Fly: + bInnerCntIsFly = pCnt->IsInFly(); + if (bInnerCntIsFly) + { + // pass down index to the startnode for flys + nIdx = pCnt->FindFlyFrame()-> + GetFormat()->GetContent().GetContentIdx()->GetIndex(); + } + break; + case SwLabelType::Table: + if( pCnt->IsInTab() ) + { + // pass down index to the TableNode for tables + const SwTable& rTable = *pCnt->FindTabFrame()->GetTable(); + nIdx = rTable.GetTabSortBoxes()[ 0 ] + ->GetSttNd()->FindTableNode()->GetIndex(); + } + break; + case SwLabelType::Draw: + if( Imp()->GetDrawView() ) + { + SwDrawView *pDView = Imp()->GetDrawView(); + const SdrMarkList& rMrkList = pDView->GetMarkedObjectList(); + + // copy marked drawing objects to + // local list to perform the corresponding action for each object + std::vector<SdrObject*> aDrawObjs; + { + for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i ) + { + SdrObject* pDrawObj = rMrkList.GetMark(i)->GetMarkedSdrObj(); + if( pDrawObj ) + aDrawObjs.push_back( pDrawObj ); + } + } + // loop on marked drawing objects + while ( !aDrawObjs.empty() ) + { + SdrObject* pDrawObj = aDrawObjs.back(); + if ( dynamic_cast<const SwVirtFlyDrawObj*>( pDrawObj) == nullptr && + dynamic_cast<const SwFlyDrawObj*>( pDrawObj) == nullptr ) + { + SwFlyFrameFormat *pFormat = + GetDoc()->InsertDrawLabel( rText, rSeparator, rNumberSeparator, nId, rCharacterStyle, *pDrawObj ); + if( !pFlyFormat ) + pFlyFormat = pFormat; + } + + aDrawObjs.pop_back(); + } + + } + break; + default: + OSL_ENSURE( false, "Cursor neither in table nor in fly." ); + } + + if( nIdx ) + { + pFlyFormat = GetDoc()->InsertLabel(eType, rText, rSeparator, + rNumberSeparator, bBefore, nId, + nIdx, rCharacterStyle, bCpyBrd); + } + + if (pFlyFormat) + { + const Point aPt(GetCursorDocPos()); + if (SwFlyFrame* pFrame = pFlyFormat->GetFrame(&aPt)) + SelectFlyFrame(*pFrame); + } + EndUndo(); + EndAllActionAndCall(); + +} + +bool SwFEShell::Sort(const SwSortOptions& rOpt) +{ + if( !HasSelection() ) + return false; + + CurrShell aCurr( this ); + bool bRet = false; + StartAllAction(); + if(IsTableMode()) + { + // Sort table + // check if Point/Mark of current Cursor are in one table + SwFrame *pFrame = GetCurrFrame( false ); + OSL_ENSURE( pFrame->FindTabFrame(), "Cursor not in table." ); + + // search boxes via the layout + SwSelBoxes aBoxes; + GetTableSel(*this, aBoxes); + + // The Cursor should be removed from the deletion area. + // Always put them behind/on the table; via the + // document position they will always be set to the old position + while( !pFrame->IsCellFrame() ) + pFrame = pFrame->GetUpper(); + { + /* ParkCursor->ParkCursorTab */ + ParkCursorInTab(); + } + + // call sorting on document + bRet = mxDoc->SortTable(aBoxes, rOpt); + } + else + { + // Sort text nothing else + for(SwPaM& rPaM : GetCursor()->GetRingContainer()) + { + SwPaM* pPam = &rPaM; + + SwPosition* pStart = pPam->Start(); + SwPosition* pEnd = pPam->End(); + + SwNodeIndex aPrevIdx( pStart->nNode, -1 ); + SwNodeOffset nOffset = pEnd->nNode.GetIndex() - pStart->nNode.GetIndex(); + const sal_Int32 nCntStt = pStart->nContent.GetIndex(); + + // Sorting + bRet = mxDoc->SortText(*pPam, rOpt); + + // put selection again + pPam->DeleteMark(); + pPam->GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 ); + SwContentNode* pCNd = pPam->GetContentNode(); + sal_Int32 nLen = pCNd->Len(); + if( nLen > nCntStt ) + nLen = nCntStt; + pPam->GetPoint()->nContent.Assign(pCNd, nLen ); + pPam->SetMark(); + + pPam->GetPoint()->nNode += nOffset; + pCNd = pPam->GetContentNode(); + pPam->GetPoint()->nContent.Assign( pCNd, pCNd->Len() ); + } + } + + EndAllAction(); + return bRet; +} + +bool SwFEShell::IsColRightToLeft() const +{ + SwFrame* pFrame = GetCurrFrame(); + while (pFrame) + { + pFrame = pFrame->GetUpper(); + if (pFrame && pFrame->IsColumnFrame()) + { + return pFrame->IsRightToLeft(); + } + } + return false; +} + +sal_uInt16 SwFEShell::GetCurColNum_( const SwFrame *pFrame, + SwGetCurColNumPara* pPara ) +{ + sal_uInt16 nRet = 0; + while ( pFrame ) + { + pFrame = pFrame->GetUpper(); + if( pFrame && pFrame->IsColumnFrame() ) + { + const SwFrame *pCurFrame = pFrame; + do { + ++nRet; + pFrame = pFrame->GetPrev(); + } while ( pFrame ); + + if( pPara ) + { + // now search the format, determining the columness + pFrame = pCurFrame->GetUpper(); + while( pFrame ) + { + if( ( SwFrameType::Page | SwFrameType::Fly | SwFrameType::Section ) & pFrame->GetType() ) + { + pPara->pFrameFormat = static_cast<const SwLayoutFrame*>(pFrame)->GetFormat(); + pPara->pPrtRect = &pFrame->getFramePrintArea(); + break; + } + pFrame = pFrame->GetUpper(); + } + if( !pFrame ) + { + pPara->pFrameFormat = nullptr; + pPara->pPrtRect = nullptr; + } + } + break; + } + } + return nRet; +} + +sal_uInt16 SwFEShell::GetCurColNum( SwGetCurColNumPara* pPara ) const +{ + OSL_ENSURE( GetCurrFrame(), "Cursor parked?" ); + return GetCurColNum_( GetCurrFrame(), pPara ); +} + +sal_uInt16 SwFEShell::GetCurOutColNum() const +{ + sal_uInt16 nRet = 0; + SwFrame* pFrame = GetCurrFrame(); + OSL_ENSURE( pFrame, "Cursor parked?" ); + if( pFrame ) + { + pFrame = pFrame->IsInTab() ? static_cast<SwFrame*>(pFrame->FindTabFrame()) + : static_cast<SwFrame*>(pFrame->FindSctFrame()); + OSL_ENSURE( pFrame, "No Tab, no Sect" ); + if( pFrame ) + nRet = GetCurColNum_( pFrame, nullptr ); + } + return nRet; +} + +SwFEShell::SwFEShell( SwDoc& rDoc, vcl::Window *pWindow, const SwViewOption *pOptions ) + : SwEditShell( rDoc, pWindow, pOptions ) + , m_bCheckForOLEInCaption(false) + , m_aPasteListeners(GetPasteMutex()) + , m_eTableInsertMode(SwTable::SEARCH_NONE) + , m_bTableCopied(false) +{ +} + +SwFEShell::SwFEShell( SwEditShell& rShell, vcl::Window *pWindow ) + : SwEditShell( rShell, pWindow ) + , m_bCheckForOLEInCaption(false) + , m_aPasteListeners(GetPasteMutex()) + , m_eTableInsertMode(SwTable::SEARCH_NONE) + , m_bTableCopied(false) +{ +} + +SwFEShell::~SwFEShell() +{ +} + +// #i17567# - adjustments for allowing +// negative vertical positions for fly frames anchored to paragraph/to character. +// #i22305# - adjustments for option 'Follow text flow' +// for to frame anchored objects. +// #i22341# - adjustments for vertical alignment at top of line +// for to character anchored objects. +void SwFEShell::CalcBoundRect( SwRect& _orRect, + const RndStdIds _nAnchorId, + const sal_Int16 _eHoriRelOrient, + const sal_Int16 _eVertRelOrient, + const SwPosition* _pToCharContentPos, + const bool _bFollowTextFlow, + bool _bMirror, + Point* _opRef, + Size* _opPercent, + const SwFormatFrameSize* pFormatFrameSize) const +{ + const SwFrame* pFrame; + const SwFlyFrame* pFly; + if( _opRef ) + { + pFrame = GetCurrFrame(); + pFly = pFrame->FindFlyFrame(); + if( nullptr != pFly ) + pFrame = pFly->GetAnchorFrame(); + } + else + { + pFly = GetSelectedFlyFrame(); + pFrame = pFly ? pFly->GetAnchorFrame() : GetCurrFrame(); + } + + bool bWrapThrough = false; + if ( pFly ) + { + SwFlyFrameFormat* pFormat = const_cast<SwFlyFrameFormat*>(pFly->GetFormat()); + const SwFormatSurround& rSurround = pFormat->GetSurround(); + bWrapThrough = rSurround.GetSurround() == css::text::WrapTextMode_THROUGH; + } + + const SwPageFrame* pPage = pFrame->FindPageFrame(); + _bMirror = _bMirror && !pPage->OnRightPage(); + + Point aPos; + bool bVertic = false; + bool bRTL = false; + bool bVerticalL2R = false; + + if ((RndStdIds::FLY_AT_PAGE == _nAnchorId) || (RndStdIds::FLY_AT_FLY == _nAnchorId)) // LAYER_IMPL + { + const SwFrame* pTmp = pFrame; + // #i22305# + if ((RndStdIds::FLY_AT_PAGE == _nAnchorId) || + ((RndStdIds::FLY_AT_FLY == _nAnchorId) && !_bFollowTextFlow)) + { + pFrame = pPage; + } + else + { + pFrame = pFrame->FindFlyFrame(); + } + if ( !pFrame ) + pFrame = pTmp; + _orRect = pFrame->getFrameArea(); + SwRectFnSet aRectFnSet(pFrame); + bRTL = pFrame->IsRightToLeft(); + if ( bRTL ) + aPos = pFrame->getFrameArea().TopRight(); + else + aPos = aRectFnSet.GetPos(pFrame->getFrameArea()); + + if( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() ) + { + bVertic = aRectFnSet.IsVert(); + bVerticalL2R = aRectFnSet.IsVertL2R(); + _bMirror = false; // no mirroring in vertical environment + switch ( _eHoriRelOrient ) + { + case text::RelOrientation::PAGE_RIGHT: + case text::RelOrientation::FRAME_RIGHT: aPos.AdjustY(pFrame->getFramePrintArea().Height() ); + [[fallthrough]]; + case text::RelOrientation::PRINT_AREA: + case text::RelOrientation::PAGE_PRINT_AREA: aPos.AdjustY(pFrame->getFramePrintArea().Top() ); break; + default: break; + } + } + else if ( _bMirror ) + { + switch ( _eHoriRelOrient ) + { + case text::RelOrientation::PRINT_AREA: + case text::RelOrientation::PAGE_PRINT_AREA: aPos.AdjustX(pFrame->getFramePrintArea().Width() ); + [[fallthrough]]; + case text::RelOrientation::PAGE_RIGHT: + case text::RelOrientation::FRAME_RIGHT: aPos.AdjustX(pFrame->getFramePrintArea().Left() ); break; + default: aPos.AdjustX(pFrame->getFrameArea().Width() ); + } + } + else if ( bRTL ) + { + switch ( _eHoriRelOrient ) + { + case text::RelOrientation::PRINT_AREA: + case text::RelOrientation::PAGE_PRINT_AREA: aPos.AdjustX(pFrame->getFramePrintArea().Width() ); + [[fallthrough]]; + case text::RelOrientation::PAGE_LEFT: + case text::RelOrientation::FRAME_LEFT: aPos.AdjustX(pFrame->getFramePrintArea().Left() - + pFrame->getFrameArea().Width() ); break; + default: break; + } + } + else + { + switch ( _eHoriRelOrient ) + { + case text::RelOrientation::PAGE_RIGHT: + case text::RelOrientation::FRAME_RIGHT: aPos.AdjustX(pFrame->getFramePrintArea().Width() ); + [[fallthrough]]; + case text::RelOrientation::PRINT_AREA: + case text::RelOrientation::PAGE_PRINT_AREA: aPos.AdjustX(pFrame->getFramePrintArea().Left() ); break; + default:break; + } + } + + if ( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() ) + { + switch ( _eVertRelOrient ) + { + case text::RelOrientation::PRINT_AREA: + case text::RelOrientation::PAGE_PRINT_AREA: + { + aPos.AdjustX( -(pFrame->GetRightMargin()) ); + } + break; + } + } + else if ( aRectFnSet.IsVertL2R() ) + { + switch ( _eVertRelOrient ) + { + case text::RelOrientation::PRINT_AREA: + case text::RelOrientation::PAGE_PRINT_AREA: + { + aPos.AdjustX(pFrame->GetLeftMargin() ); + } + break; + } + } + else + { + switch ( _eVertRelOrient ) + { + case text::RelOrientation::PRINT_AREA: + case text::RelOrientation::PAGE_PRINT_AREA: + { + if ( pFrame->IsPageFrame() ) + { + aPos.setY( + static_cast<const SwPageFrame*>(pFrame)->PrtWithoutHeaderAndFooter().Top() ); + } + else + { + aPos.AdjustY(pFrame->getFramePrintArea().Top() ); + } + } + break; + } + } + if ( _opPercent ) + *_opPercent = pFrame->getFramePrintArea().SSize(); + } + else + { + const SwFrame* pUpper = ( pFrame->IsPageFrame() || pFrame->IsFlyFrame() ) ? + pFrame : pFrame->GetUpper(); + SwRectFnSet aRectFnSet(pUpper); + if ( _opPercent ) + { + // If the size is relative from page, then full size should be counted from the page frame. + if (pFormatFrameSize && pFormatFrameSize->GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME) + _opPercent->setWidth(pPage->getFrameArea().Width()); + else + _opPercent->setWidth(pUpper->getFramePrintArea().Width()); + + if (pFormatFrameSize && pFormatFrameSize->GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME) + // If the size is relative from page, then full size should be counted from the page frame. + _opPercent->setHeight(pPage->getFrameArea().Height()); + else + _opPercent->setHeight(pUpper->getFramePrintArea().Height()); + } + + bRTL = pFrame->IsRightToLeft(); + if ( bRTL ) + aPos = pFrame->getFrameArea().TopRight(); + else + aPos = aRectFnSet.GetPos(pFrame->getFrameArea()); + // #i17567# - allow negative positions + // for fly frames anchor to paragraph/to character. + if ((_nAnchorId == RndStdIds::FLY_AT_PARA) || (_nAnchorId == RndStdIds::FLY_AT_CHAR)) + { + // The rectangle, the fly frame can be positioned in, is determined + // horizontally by the frame area of the horizontal environment + // and vertically by the printing area of the vertical environment, + // if the object follows the text flow, or by the frame area of the + // vertical environment, if the object doesn't follow the text flow. + // new class <SwEnvironmentOfAnchoredObject> + objectpositioning::SwEnvironmentOfAnchoredObject aEnvOfObj( + _bFollowTextFlow ); + const SwLayoutFrame& rHoriEnvironLayFrame = + aEnvOfObj.GetHoriEnvironmentLayoutFrame( *pFrame ); + const SwLayoutFrame& rVertEnvironLayFrame = + aEnvOfObj.GetVertEnvironmentLayoutFrame( *pFrame ); + const SwRect& aHoriEnvironRect( rHoriEnvironLayFrame.getFrameArea() ); + SwRect aVertEnvironRect; + if ( _bFollowTextFlow ) + { + aVertEnvironRect = rVertEnvironLayFrame.getFramePrintArea(); + aVertEnvironRect.Pos() += rVertEnvironLayFrame.getFrameArea().Pos(); + // #i18732# - adjust vertical 'virtual' anchor position + // (<aPos.Y()> respectively <aPos.X()>), if object is vertical aligned + // to page areas. + if ( _eVertRelOrient == text::RelOrientation::PAGE_FRAME || _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA ) + { + if ( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() ) + { + aPos.setX( aVertEnvironRect.Right() ); + } + else if ( aRectFnSet.IsVertL2R() ) + { + aPos.setX( aVertEnvironRect.Left() ); + } + else + { + aPos.setY( aVertEnvironRect.Top() ); + } + } + } + else + { + OSL_ENSURE( rVertEnvironLayFrame.IsPageFrame(), + "<SwFEShell::CalcBoundRect(..)> - not following text flow, but vertical environment *not* page!" ); + aVertEnvironRect = rVertEnvironLayFrame.getFrameArea(); + // #i18732# - adjustment vertical 'virtual' anchor position + // (<aPos.Y()> respectively <aPos.X()>), if object is vertical aligned + // to page areas. + if (_eVertRelOrient == text::RelOrientation::PAGE_FRAME + || _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA + || _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM) + { + if ( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() ) + { + aPos.setX( aVertEnvironRect.Right() ); + if ( _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA ) + { + aPos.setX(aPos.getX() - rVertEnvironLayFrame.GetRightMargin()); + } + } + else if ( aRectFnSet.IsVertL2R() ) + { + aPos.setX( aVertEnvironRect.Left() ); + if ( _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA ) + { + aPos.setX(aPos.getX() + rVertEnvironLayFrame.GetLeftMargin()); + } + } + else + { + aPos.setY( aVertEnvironRect.Top() ); + if ( _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA ) + { + aPos.setY(aPos.getY() + rVertEnvironLayFrame.GetTopMargin()); + // add height of page header + const SwFrame* pTmpFrame = rVertEnvironLayFrame.Lower(); + if ( pTmpFrame->IsHeaderFrame() ) + { + aPos.setY(aPos.getY() + pTmpFrame->getFrameArea().Height()); + } + } + else if (_eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM) + { + if (rVertEnvironLayFrame.IsPageFrame()) + { + auto& rPageFrame = static_cast<const SwPageFrame&>(rVertEnvironLayFrame); + aPos.setY(rPageFrame.PrtWithoutHeaderAndFooter().Bottom()); + } + else + { + aPos.AdjustY(rVertEnvironLayFrame.getFramePrintArea().Bottom()); + } + } + } + } + } + + // #i22341# - adjust vertical 'virtual' anchor position + // (<aPos.Y()> respectively <aPos.X()>), if object is anchored to + // character and vertical aligned at character or top of line + // <pFrame>, which is the anchor frame or the proposed anchor frame, + // doesn't have to be a text frame (e.g. edit a to-page anchored + // fly frame). Thus, assure this. + const SwTextFrame* pTextFrame = pFrame->DynCastTextFrame(); + if ( pTextFrame && + (_nAnchorId == RndStdIds::FLY_AT_CHAR) && + ( _eVertRelOrient == text::RelOrientation::CHAR || + _eVertRelOrient == text::RelOrientation::TEXT_LINE ) ) + { + SwTwips nTop = 0; + if ( _eVertRelOrient == text::RelOrientation::CHAR ) + { + SwRect aChRect; + if ( _pToCharContentPos ) + { + pTextFrame->GetAutoPos( aChRect, *_pToCharContentPos ); + } + else + { + // No content position provided. Thus, use a default one. + SwPosition aDefaultContentPos(*(pTextFrame->GetTextNodeFirst())); + pTextFrame->GetAutoPos( aChRect, aDefaultContentPos ); + } + nTop = aRectFnSet.GetBottom(aChRect); + } + else + { + if ( _pToCharContentPos ) + { + pTextFrame->GetTopOfLine( nTop, *_pToCharContentPos ); + } + else + { + // No content position provided. Thus, use a default one. + SwPosition aDefaultContentPos(*(pTextFrame->GetTextNodeFirst())); + pTextFrame->GetTopOfLine( nTop, aDefaultContentPos ); + } + } + if ( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() ) + { + aPos.setX(nTop); + } + else + { + aPos.setY(nTop); + } + } + + // #i26945# - adjust horizontal 'virtual' anchor + // position (<aPos.X()> respectively <aPos.Y()>), if object is + // anchored to character and horizontal aligned at character. + if ( pTextFrame && + (_nAnchorId == RndStdIds::FLY_AT_CHAR) && + _eHoriRelOrient == text::RelOrientation::CHAR ) + { + SwTwips nLeft = 0; + SwRect aChRect; + if ( _pToCharContentPos ) + { + pTextFrame->GetAutoPos( aChRect, *_pToCharContentPos ); + } + else + { + // No content position provided. Thus, use a default one. + SwPosition aDefaultContentPos(*(pTextFrame->GetTextNodeFirst())); + pTextFrame->GetAutoPos( aChRect, aDefaultContentPos ); + } + nLeft = aRectFnSet.GetLeft(aChRect); + if ( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() ) + { + aPos.setY(nLeft); + } + else + { + aPos.setX(nLeft); + } + } + if ( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() ) + { + _orRect = SwRect( aVertEnvironRect.Left(), + aHoriEnvironRect.Top(), + aVertEnvironRect.Width(), + aHoriEnvironRect.Height() ); + } + else + { + _orRect = SwRect( aHoriEnvironRect.Left(), + aVertEnvironRect.Top(), + aHoriEnvironRect.Width(), + aVertEnvironRect.Height() ); + } + } + else + { + if( _opRef && pFly && pFly->IsFlyInContentFrame() ) + *_opRef = static_cast<const SwFlyInContentFrame*>( pFly )->GetRefPoint(); + + _orRect = pUpper->getFrameArea(); + if( !pUpper->IsBodyFrame() ) + { + _orRect += pUpper->getFramePrintArea().Pos(); + _orRect.SSize( pUpper->getFramePrintArea().SSize() ); + if ( pUpper->IsCellFrame() )//MA_FLY_HEIGHT + { + const SwFrame* pTab = pUpper->FindTabFrame(); + tools::Long nBottom = aRectFnSet.GetPrtBottom(*pTab->GetUpper()); + aRectFnSet.SetBottom( _orRect, nBottom ); + } + } + // only use 90% of height for character bound + { + if( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() ) + _orRect.Width( (_orRect.Width()*9)/10 ); + else + _orRect.Height( (_orRect.Height()*9)/10 ); + } + } + + const SwTwips nBaseOfstForFly = ( pFrame->IsTextFrame() && pFly ) ? + static_cast<const SwTextFrame*>(pFrame)->GetBaseOffsetForFly( !bWrapThrough ) : + 0; + if( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() ) + { + bVertic = aRectFnSet.IsVert(); + bVerticalL2R = aRectFnSet.IsVertL2R(); + _bMirror = false; + + switch ( _eHoriRelOrient ) + { + case text::RelOrientation::FRAME_RIGHT: + { + aPos.setY(aPos.getY() + pFrame->getFramePrintArea().Height()); + aPos += aRectFnSet.GetPos(pFrame->getFramePrintArea()); + break; + } + case text::RelOrientation::PRINT_AREA: + { + aPos += aRectFnSet.GetPos(pFrame->getFramePrintArea()); + aPos.setY(aPos.getY() + nBaseOfstForFly); + break; + } + case text::RelOrientation::PAGE_RIGHT: + { + aPos.setY(pPage->getFrameArea().Top() + pPage->getFramePrintArea().Bottom()); + break; + } + case text::RelOrientation::PAGE_PRINT_AREA: + { + aPos.setY(pPage->getFrameArea().Top() + pPage->getFramePrintArea().Top()); + break; + } + case text::RelOrientation::PAGE_LEFT: + case text::RelOrientation::PAGE_FRAME: + { + aPos.setY(pPage->getFrameArea().Top()); + break; + } + case text::RelOrientation::FRAME: + { + aPos.setY(aPos.getY() + nBaseOfstForFly); + break; + } + default: break; + } + } + else if( _bMirror ) + { + switch ( _eHoriRelOrient ) + { + case text::RelOrientation::FRAME_RIGHT: aPos.setX(aPos.getX() + pFrame->getFramePrintArea().Left()); break; + case text::RelOrientation::FRAME: + case text::RelOrientation::FRAME_LEFT: aPos.setX(aPos.getX() + pFrame->getFrameArea().Width()); break; + case text::RelOrientation::PRINT_AREA: aPos.setX(aPos.getX() + pFrame->getFramePrintArea().Right()); break; + case text::RelOrientation::PAGE_LEFT: + case text::RelOrientation::PAGE_FRAME: aPos.setX(pPage->getFrameArea().Right()); break; + case text::RelOrientation::PAGE_PRINT_AREA: aPos.setX(pPage->getFrameArea().Left() + + pPage->getFramePrintArea().Left()); break; + default: break; + } + } + else if ( bRTL ) + { + switch ( _eHoriRelOrient ) + { + case text::RelOrientation::FRAME_LEFT: + aPos.setX(pFrame->getFrameArea().Left() + + pFrame->getFramePrintArea().Left()); + break; + + case text::RelOrientation::PRINT_AREA: + aPos.setX(pFrame->getFrameArea().Left() + pFrame->getFramePrintArea().Left() + + pFrame->getFramePrintArea().Width()); + aPos.setX(aPos.getX() + nBaseOfstForFly); + break; + + case text::RelOrientation::PAGE_LEFT: + aPos.setX(pPage->getFrameArea().Left() + pPage->getFramePrintArea().Left()); + break; + + case text::RelOrientation::PAGE_PRINT_AREA: + aPos.setX(pPage->getFrameArea().Left() + pPage->getFramePrintArea().Left() + + pPage->getFramePrintArea().Width()); + break; + + case text::RelOrientation::PAGE_RIGHT: + case text::RelOrientation::PAGE_FRAME: + aPos.setX(pPage->getFrameArea().Right()); + break; + + case text::RelOrientation::FRAME: + aPos.setX(aPos.getX() + nBaseOfstForFly); + break; + default: break; + } + } + else + { + switch ( _eHoriRelOrient ) + { + case text::RelOrientation::FRAME_RIGHT: + aPos.AdjustX(pFrame->getFramePrintArea().Width() ); + aPos += pFrame->getFramePrintArea().Pos(); + break; + case text::RelOrientation::PRINT_AREA: + aPos += pFrame->getFramePrintArea().Pos(); + aPos.setX(aPos.getX() + nBaseOfstForFly); + break; + case text::RelOrientation::PAGE_RIGHT: + aPos.setX(pPage->getFrameArea().Left() + pPage->getFramePrintArea().Right()); + break; + case text::RelOrientation::PAGE_PRINT_AREA: + aPos.setX(pPage->getFrameArea().Left() + pPage->getFramePrintArea().Left()); + break; + case text::RelOrientation::PAGE_LEFT: + case text::RelOrientation::PAGE_FRAME: + aPos.setX(pPage->getFrameArea().Left()); + break; + case text::RelOrientation::FRAME: + aPos.setX(aPos.getX() + nBaseOfstForFly); + break; + default: break; + } + } + + } + if( _opRef ) + return; + + if( bVertic && !bVerticalL2R ) + _orRect.Pos( aPos.getX() - _orRect.Width() - _orRect.Left(), _orRect.Top() - aPos.getY() ); + else if( bVerticalL2R ) + _orRect.Pos( _orRect.Left() - aPos.getX(), _orRect.Top() - aPos.getY() ); + else if ( bRTL ) + _orRect.Pos( - ( _orRect.Right() - aPos.getX() ), _orRect.Top() - aPos.getY() ); + else + _orRect.Pos( _orRect.Left() - aPos.getX(), _orRect.Top() - aPos.getY() ); + if( _bMirror ) + _orRect.Pos( -_orRect.Right(), _orRect.Top() ); +} + +Size SwFEShell::GetGraphicDefaultSize() const +{ + Size aRet; + SwFlyFrame *pFly = GetSelectedFlyFrame(); + if ( pFly ) + { + // #i32951# - due to issue #i28701# no format of a + // newly inserted Writer fly frame or its anchor frame is performed + // any more. Thus, it could be possible (e.g. on insert of a horizontal + // line) that the anchor frame isn't formatted and its printing area + // size is (0,0). If this is the case the printing area of the upper + // of the anchor frame is taken. + const SwFrame* pAnchorFrame = pFly->GetAnchorFrame(); + aRet = pAnchorFrame->getFramePrintArea().SSize(); + if ( aRet.IsEmpty() && pAnchorFrame->GetUpper() ) + { + aRet = pAnchorFrame->GetUpper()->getFramePrintArea().SSize(); + } + + SwRect aBound; + CalcBoundRect( aBound, pFly->GetFormat()->GetAnchor().GetAnchorId()); + if ( pFly->GetAnchorFrame()->IsVertical() ) + aRet.setWidth( aBound.Width() ); + else + aRet.setHeight( aBound.Height() ); + } + return aRet; +} + +bool SwFEShell::IsFrameVertical(const bool bEnvironment, bool& bRTL, bool& bVertL2R) const +{ + bool bVert = false; + bRTL = false; + bVertL2R = false; + + if ( Imp()->HasDrawView() ) + { + const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList(); + if( rMrkList.GetMarkCount() != 1 ) + return bVert; + + SdrObject* pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj(); + if ( !pObj ) + { + OSL_FAIL( "<SwFEShell::IsFrameVertical(..)> - missing SdrObject instance in marked object list -> This is a serious situation" ); + return bVert; + } + // #i26791# + SwContact* pContact = GetUserCall( pObj ); + if ( !pContact ) + { + OSL_FAIL( "<SwFEShell::IsFrameVertical(..)> - missing SwContact instance at marked object -> This is a serious situation" ); + return bVert; + } + const SwFrame* pRef = pContact->GetAnchoredObj( pObj )->GetAnchorFrame(); + if ( !pRef ) + { + OSL_FAIL( "<SwFEShell::IsFrameVertical(..)> - missing anchor frame at marked object -> This is a serious situation" ); + return bVert; + } + + if ( !bEnvironment ) + if ( auto pVirtFly = dynamic_cast<const SwVirtFlyDrawObj*>( pObj) ) + pRef = pVirtFly->GetFlyFrame(); + + bVert = pRef->IsVertical(); + bRTL = pRef->IsRightToLeft(); + bVertL2R = pRef->IsVertLR(); + } + + return bVert; +} + +void SwFEShell::MoveObjectIfActive( svt::EmbeddedObjectRef&, const Point& ) +{ + // does not do anything, only avoids crash if the method is used for wrong shell +} + +void SwFEShell::ToggleHeaderFooterEdit() +{ + // Clear objects selection + if ( Imp()->GetDrawView()->AreObjectsMarked() ) + { + Imp()->GetDrawView()->UnmarkAll(); + ClearMark(); + } + + SwCursorShell::ToggleHeaderFooterEdit(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/frmedt/tblsel.cxx b/sw/source/core/frmedt/tblsel.cxx new file mode 100644 index 000000000..45fc820b2 --- /dev/null +++ b/sw/source/core/frmedt/tblsel.cxx @@ -0,0 +1,2614 @@ +/* -*- 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 <editeng/boxitem.hxx> +#include <editeng/protitem.hxx> +#include <osl/diagnose.h> + +#include <hintids.hxx> +#include <fmtanchr.hxx> +#include <fmtfsize.hxx> +#include <frmatr.hxx> +#include <tblsel.hxx> +#include <crsrsh.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <pam.hxx> +#include <ndtxt.hxx> +#include <swtable.hxx> +#include <cntfrm.hxx> +#include <tabfrm.hxx> +#include <rowfrm.hxx> +#include <cellfrm.hxx> +#include <rootfrm.hxx> +#include <viscrs.hxx> +#include <swtblfmt.hxx> +#include <UndoTable.hxx> +#include <sectfrm.hxx> +#include <frmtool.hxx> +#include <calbck.hxx> +#include <frameformats.hxx> +#include <deque> +#include <memory> + +// see also swtable.cxx +#define COLFUZZY 20L + +// macros, determining how table boxes are merged: +// - 1. remove empty lines, all boxes separated with blanks, +// all lines separated with ParaBreak +// - 2. remove all empty lines and remove all empty boxes at beginning and end, +// all boxes separated with Blank, +// all lines separated with ParaBreak +// - 3. remove all empty boxes, all boxes separated with blanks, +// all lines separated with ParaBreak + +#undef DEL_ONLY_EMPTY_LINES +#undef DEL_EMPTY_BOXES_AT_START_AND_END + +namespace { + +struct CmpLPt +{ + Point aPos; + const SwTableBox* pSelBox; + bool bVert; + + CmpLPt( const Point& rPt, const SwTableBox* pBox, bool bVertical ); + + bool operator<( const CmpLPt& rCmp ) const + { + if ( bVert ) + return X() > rCmp.X() || ( X() == rCmp.X() && Y() < rCmp.Y() ); + else + return Y() < rCmp.Y() || ( Y() == rCmp.Y() && X() < rCmp.X() ); + } + + tools::Long X() const { return aPos.X(); } + tools::Long Y() const { return aPos.Y(); } +}; + +} + +typedef o3tl::sorted_vector<CmpLPt> MergePos; + +namespace { + +struct Sort_CellFrame +{ + const SwCellFrame* pFrame; + + explicit Sort_CellFrame( const SwCellFrame& rCFrame ) + : pFrame( &rCFrame ) {} +}; + +} + +static const SwLayoutFrame *lcl_FindCellFrame( const SwLayoutFrame *pLay ) +{ + while ( pLay && !pLay->IsCellFrame() ) + pLay = pLay->GetUpper(); + return pLay; +} + +static const SwLayoutFrame *lcl_FindNextCellFrame( const SwLayoutFrame *pLay ) +{ + // ensure we leave the cell (sections) + const SwLayoutFrame *pTmp = pLay; + do { + pTmp = pTmp->GetNextLayoutLeaf(); + } while( pLay->IsAnLower( pTmp ) ); + + while( pTmp && !pTmp->IsCellFrame() ) + pTmp = pTmp->GetUpper(); + return pTmp; +} + +void GetTableSelCrs( const SwCursorShell &rShell, SwSelBoxes& rBoxes ) +{ + rBoxes.clear(); + if( rShell.IsTableMode() && const_cast<SwCursorShell&>(rShell).UpdateTableSelBoxes()) + { + rBoxes.insert(rShell.GetTableCursor()->GetSelectedBoxes()); + } +} + +void GetTableSelCrs( const SwTableCursor& rTableCursor, SwSelBoxes& rBoxes ) +{ + rBoxes.clear(); + + if (rTableCursor.IsChgd() || !rTableCursor.GetSelectedBoxesCount()) + { + SwTableCursor* pTCursor = const_cast<SwTableCursor*>(&rTableCursor); + pTCursor->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout()->MakeTableCursors( *pTCursor ); + } + + if (rTableCursor.GetSelectedBoxesCount()) + { + rBoxes.insert(rTableCursor.GetSelectedBoxes()); + } +} + +void GetTableSel( const SwCursorShell& rShell, SwSelBoxes& rBoxes, + const SwTableSearchType eSearchType ) +{ + // get start and end cell + if ( !rShell.IsTableMode() ) + rShell.GetCursor(); + + GetTableSel( *rShell.getShellCursor(false), rBoxes, eSearchType ); +} + +void GetTableSel( const SwCursor& rCursor, SwSelBoxes& rBoxes, + const SwTableSearchType eSearchType ) +{ + // get start and end cell + OSL_ENSURE( rCursor.GetContentNode() && rCursor.GetContentNode( false ), + "Tabselection not on Cnt." ); + + // Row-selection: + // Check for complex tables. If Yes, search selected boxes via + // the layout. Otherwise via the table structure (for macros !!) + const SwContentNode* pContentNd = rCursor.GetNode().GetContentNode(); + const SwTableNode* pTableNd = pContentNd ? pContentNd->FindTableNode() : nullptr; + if( pTableNd && pTableNd->GetTable().IsNewModel() ) + { + SwTable::SearchType eSearch; + switch( SwTableSearchType::Col & eSearchType ) + { + case SwTableSearchType::Row: eSearch = SwTable::SEARCH_ROW; break; + case SwTableSearchType::Col: eSearch = SwTable::SEARCH_COL; break; + default: eSearch = SwTable::SEARCH_NONE; break; + } + const bool bChkP( SwTableSearchType::Protect & eSearchType ); + pTableNd->GetTable().CreateSelection( rCursor, rBoxes, eSearch, bChkP ); + return; + } + if( SwTableSearchType::Row == ((~SwTableSearchType::Protect ) & eSearchType ) && + pTableNd && !pTableNd->GetTable().IsTableComplex() ) + { + const SwTable& rTable = pTableNd->GetTable(); + const SwTableLines& rLines = rTable.GetTabLines(); + + const SwNode& rMarkNode = rCursor.GetNode( false ); + const SwNodeOffset nMarkSectionStart = rMarkNode.StartOfSectionIndex(); + const SwTableBox* pMarkBox = rTable.GetTableBox( nMarkSectionStart ); + + OSL_ENSURE( pMarkBox, "Point in table, mark outside?" ); + + const SwTableLine* pLine = pMarkBox ? pMarkBox->GetUpper() : nullptr; + sal_uInt16 nSttPos = rLines.GetPos( pLine ); + OSL_ENSURE( USHRT_MAX != nSttPos, "Where is my row in the table?" ); + pLine = rTable.GetTableBox( rCursor.GetNode().StartOfSectionIndex() )->GetUpper(); + sal_uInt16 nEndPos = rLines.GetPos( pLine ); + OSL_ENSURE( USHRT_MAX != nEndPos, "Where is my row in the table?" ); + // pb: #i20193# if tableintable then nSttPos == nEndPos == USHRT_MAX + if ( nSttPos != USHRT_MAX && nEndPos != USHRT_MAX ) + { + if( nEndPos < nSttPos ) // exchange + { + sal_uInt16 nTmp = nSttPos; nSttPos = nEndPos; nEndPos = nTmp; + } + + bool bChkProtected( SwTableSearchType::Protect & eSearchType ); + for( ; nSttPos <= nEndPos; ++nSttPos ) + { + pLine = rLines[ nSttPos ]; + for( auto n = pLine->GetTabBoxes().size(); n ; ) + { + SwTableBox* pBox = pLine->GetTabBoxes()[ --n ]; + // check for cell protection?? + if( !bChkProtected || + !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + rBoxes.insert( pBox ); + } + } + } + } + else + { + Point aPtPos, aMkPos; + const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor); + if( pShCursor ) + { + aPtPos = pShCursor->GetPtPos(); + aMkPos = pShCursor->GetMkPos(); + } + const SwContentNode *pCntNd = rCursor.GetContentNode(); + std::pair<Point, bool> tmp(aPtPos, true); + const SwLayoutFrame *pStart = pCntNd ? + pCntNd->getLayoutFrame(pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)->GetUpper() : nullptr; + pCntNd = rCursor.GetContentNode(false); + tmp.first = aMkPos; + const SwLayoutFrame *pEnd = pCntNd ? + pCntNd->getLayoutFrame(pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)->GetUpper() : nullptr; + if( pStart && pEnd ) + GetTableSel( pStart, pEnd, rBoxes, nullptr, eSearchType ); + } +} + +void GetTableSel( const SwLayoutFrame* pStart, const SwLayoutFrame* pEnd, + SwSelBoxes& rBoxes, SwCellFrames* pCells, + const SwTableSearchType eSearchType ) +{ + const SwTabFrame* pStartTab = pStart->FindTabFrame(); + if ( !pStartTab ) + { + OSL_FAIL( "GetTableSel without start table" ); + return; + } + + bool bChkProtected( SwTableSearchType::Protect & eSearchType ); + + // #i55421# Reduced value 10 + int nLoopMax = 10; + + do { + bool bTableIsValid = true; + + // First, compute tables and rectangles + SwSelUnions aUnions; + ::MakeSelUnions( aUnions, pStart, pEnd, eSearchType ); + + Point aCurrentTopLeft( LONG_MAX, LONG_MAX ); + Point aCurrentTopRight( 0, LONG_MAX ); + Point aCurrentBottomLeft( LONG_MAX, 0 ); + Point aCurrentBottomRight( 0, 0 ); + const SwCellFrame* pCurrentTopLeftFrame = nullptr; + const SwCellFrame* pCurrentTopRightFrame = nullptr; + const SwCellFrame* pCurrentBottomLeftFrame = nullptr; + const SwCellFrame* pCurrentBottomRightFrame = nullptr; + + // Now find boxes for each entry and emit + for (size_t i = 0; i < aUnions.size() && bTableIsValid; ++i) + { + SwSelUnion *pUnion = &aUnions[i]; + const SwTabFrame *pTable = pUnion->GetTable(); + + if( !pTable->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while( pRow && bTableIsValid ) + { + if( !pRow->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + if ( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while (pCell && pRow->IsAnLower(pCell)) + { + if( !pCell->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + OSL_ENSURE( pCell->IsCellFrame(), "Frame without Cell" ); + if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) ) + { + SwTableBox* pBox = const_cast<SwTableBox*>( + static_cast<const SwCellFrame*>(pCell)->GetTabBox()); + // check for cell protection?? + if( !bChkProtected || + !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) + rBoxes.insert( pBox ); + + if ( pCells ) + { + const Point aTopLeft( pCell->getFrameArea().TopLeft() ); + const Point aTopRight( pCell->getFrameArea().TopRight() ); + const Point aBottomLeft( pCell->getFrameArea().BottomLeft() ); + const Point aBottomRight( pCell->getFrameArea().BottomRight() ); + + if ( aTopLeft.getY() < aCurrentTopLeft.getY() || + ( aTopLeft.getY() == aCurrentTopLeft.getY() && + aTopLeft.getX() < aCurrentTopLeft.getX() ) ) + { + aCurrentTopLeft = aTopLeft; + pCurrentTopLeftFrame = static_cast<const SwCellFrame*>( pCell ); + } + + if ( aTopRight.getY() < aCurrentTopRight.getY() || + ( aTopRight.getY() == aCurrentTopRight.getY() && + aTopRight.getX() > aCurrentTopRight.getX() ) ) + { + aCurrentTopRight = aTopRight; + pCurrentTopRightFrame = static_cast<const SwCellFrame*>( pCell ); + } + + if ( aBottomLeft.getY() > aCurrentBottomLeft.getY() || + ( aBottomLeft.getY() == aCurrentBottomLeft.getY() && + aBottomLeft.getX() < aCurrentBottomLeft.getX() ) ) + { + aCurrentBottomLeft = aBottomLeft; + pCurrentBottomLeftFrame = static_cast<const SwCellFrame*>( pCell ); + } + + if ( aBottomRight.getY() > aCurrentBottomRight.getY() || + ( aBottomRight.getY() == aCurrentBottomRight.getY() && + aBottomRight.getX() > aCurrentBottomRight.getX() ) ) + { + aCurrentBottomRight = aBottomRight; + pCurrentBottomRightFrame = static_cast<const SwCellFrame*>( pCell ); + } + + } + } + if ( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + + if ( pCells ) + { + pCells->clear(); + pCells->push_back( const_cast< SwCellFrame* >(pCurrentTopLeftFrame) ); + pCells->push_back( const_cast< SwCellFrame* >(pCurrentTopRightFrame) ); + pCells->push_back( const_cast< SwCellFrame* >(pCurrentBottomLeftFrame) ); + pCells->push_back( const_cast< SwCellFrame* >(pCurrentBottomRightFrame) ); + } + + if( bTableIsValid ) + break; + + SwDeletionChecker aDelCheck( pStart ); + + // otherwise quickly "calculate" the table layout and start over + SwTabFrame *pTable = aUnions.front().GetTable(); + while( pTable ) + { + if( pTable->isFrameAreaDefinitionValid() ) + { + pTable->InvalidatePos(); + } + + pTable->SetONECalcLowers(); + pTable->Calc(pTable->getRootFrame()->GetCurrShell()->GetOut()); + pTable->SetCompletePaint(); + + pTable = pTable->GetFollow(); + if( nullptr == pTable ) + break; + } + + // --> Make code robust, check if pStart has + // been deleted due to the formatting of the table: + if ( aDelCheck.HasBeenDeleted() ) + { + OSL_FAIL( "Current box has been deleted during GetTableSel()" ); + break; + } + + rBoxes.clear(); + --nLoopMax; + + } while( true ); + OSL_ENSURE( nLoopMax, "Table layout is still invalid!" ); +} + +bool ChkChartSel( const SwNode& rSttNd, const SwNode& rEndNd ) +{ + const SwTableNode* pTNd = rSttNd.FindTableNode(); + if( !pTNd ) + return false; + + Point aNullPos; + SwNodeIndex aIdx( rSttNd ); + const SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = aIdx.GetNodes().GoNextSection( &aIdx, false, false ); + + // if table is invisible, return + // (layout needed for forming table selection further down, so we can't + // continue with invisible tables) + // #i22135# - Also the content of the table could be + // invisible - e.g. in a hidden section + // Robust: check, if content was found (e.g. empty table cells) + if ( !pCNd || pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) == nullptr ) + return false; + + std::pair<Point, bool> tmp(aNullPos, true); + const SwLayoutFrame *const pStart = pCNd->getLayoutFrame( + pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + OSL_ENSURE( pStart, "without frame nothing works" ); + + aIdx = rEndNd; + pCNd = aIdx.GetNode().GetContentNode(); + if( !pCNd ) + pCNd = aIdx.GetNodes().GoNextSection( &aIdx, false, false ); + + // #i22135# - Robust: check, if content was found and if it's visible + if ( !pCNd || pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) == nullptr ) + { + return false; + } + + const SwLayoutFrame *const pEnd = pCNd->getLayoutFrame( + pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + OSL_ENSURE( pEnd, "without frame nothing works" ); + + bool bValidChartSel; + // #i55421# Reduced value 10 + int nLoopMax = 10; //JP 28.06.99: max 100 loops - Bug 67292 + + do { + bool bTableIsValid = true; + bValidChartSel = true; + + sal_uInt16 nRowCells = USHRT_MAX; + + // First, compute tables and rectangles + SwSelUnions aUnions; + ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::NoUnionCorrect ); + + // find boxes for each entry and emit + for( auto & rSelUnion : aUnions ) + { + if (!bTableIsValid || !bValidChartSel) + break; + + SwSelUnion *pUnion = &rSelUnion; + const SwTabFrame *pTable = pUnion->GetTable(); + + SwRectFnSet aRectFnSet(pTable); + bool bRTL = pTable->IsRightToLeft(); + + if( !pTable->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + std::deque< Sort_CellFrame > aCellFrames; + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while( pRow && bTableIsValid && bValidChartSel ) + { + if( !pRow->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while (pCell && pRow->IsAnLower(pCell)) + { + if( !pCell->isFrameAreaDefinitionValid() && nLoopMax ) + { + bTableIsValid = false; + break; + } + + OSL_ENSURE( pCell->IsCellFrame(), "Frame without Cell" ); + const SwRect& rUnion = pUnion->GetUnion(), + & rFrameRect = pCell->getFrameArea(); + + const tools::Long nUnionRight = rUnion.Right(); + const tools::Long nUnionBottom = rUnion.Bottom(); + const tools::Long nFrameRight = rFrameRect.Right(); + const tools::Long nFrameBottom = rFrameRect.Bottom(); + + // ignore if FrameRect is outside the union + + const tools::Long nXFuzzy = aRectFnSet.IsVert() ? 0 : 20; + const tools::Long nYFuzzy = aRectFnSet.IsVert() ? 20 : 0; + + if( !( rUnion.Top() + nYFuzzy > nFrameBottom || + nUnionBottom < rFrameRect.Top() + nYFuzzy || + rUnion.Left() + nXFuzzy > nFrameRight || + nUnionRight < rFrameRect.Left() + nXFuzzy )) + { + // ok, rUnion is _not_ completely outside of rFrameRect + + // if not completely inside the union, then + // for Chart it is an invalid selection + if( rUnion.Left() <= rFrameRect.Left() + nXFuzzy && + rFrameRect.Left() <= nUnionRight && + rUnion.Left() <= nFrameRight && + nFrameRight <= nUnionRight + nXFuzzy && + rUnion.Top() <= rFrameRect.Top() + nYFuzzy && + rFrameRect.Top() <= nUnionBottom && + rUnion.Top() <= nFrameBottom && + nFrameBottom <= nUnionBottom+ nYFuzzy ) + + aCellFrames.emplace_back( *static_cast<const SwCellFrame*>(pCell) ); + else + { + bValidChartSel = false; + break; + } + } + if ( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + + if( !bValidChartSel ) + break; + + // all cells of the (part) table together. Now check if + // they're all adjacent + size_t n; + sal_uInt16 nCellCnt = 0; + tools::Long nYPos = LONG_MAX; + tools::Long nXPos = 0; + tools::Long nHeight = 0; + + for( n = 0 ; n < aCellFrames.size(); ++n ) + { + const Sort_CellFrame& rCF = aCellFrames[ n ]; + if( aRectFnSet.GetTop(rCF.pFrame->getFrameArea()) != nYPos ) + { + // new row + if( n ) + { + if( USHRT_MAX == nRowCells ) // 1. row change + nRowCells = nCellCnt; + else if( nRowCells != nCellCnt ) + { + bValidChartSel = false; + break; + } + } + nCellCnt = 1; + nYPos = aRectFnSet.GetTop(rCF.pFrame->getFrameArea()); + nHeight = aRectFnSet.GetHeight(rCF.pFrame->getFrameArea()); + + nXPos = bRTL ? + aRectFnSet.GetLeft(rCF.pFrame->getFrameArea()) : + aRectFnSet.GetRight(rCF.pFrame->getFrameArea()); + } + else if( nXPos == ( bRTL ? + aRectFnSet.GetRight(rCF.pFrame->getFrameArea()) : + aRectFnSet.GetLeft(rCF.pFrame->getFrameArea()) ) && + nHeight == aRectFnSet.GetHeight(rCF.pFrame->getFrameArea()) ) + { + nXPos += ( bRTL ? -1 : 1 ) * + aRectFnSet.GetWidth(rCF.pFrame->getFrameArea()); + ++nCellCnt; + } + else + { + bValidChartSel = false; + break; + } + } + if( bValidChartSel ) + { + if( USHRT_MAX == nRowCells ) + nRowCells = nCellCnt; + else if( nRowCells != nCellCnt ) + bValidChartSel = false; + } + } + + if( bTableIsValid ) + break; + + // otherwise quickly "calculate" table layout and start over + SwTabFrame *pTable = aUnions.front().GetTable(); + + for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i ) + { + if( pTable->isFrameAreaDefinitionValid() ) + { + pTable->InvalidatePos(); + } + + pTable->SetONECalcLowers(); + pTable->Calc(pTable->getRootFrame()->GetCurrShell()->GetOut()); + pTable->SetCompletePaint(); + + pTable = pTable->GetFollow(); + if( nullptr == pTable ) + break; + } + --nLoopMax; + } while( true ); + + OSL_ENSURE( nLoopMax, "table layout is still invalid!" ); + + return bValidChartSel; +} + +bool IsFrameInTableSel( const SwRect& rUnion, const SwFrame* pCell ) +{ + OSL_ENSURE( pCell->IsCellFrame(), "Frame without Gazelle" ); + + if( pCell->FindTabFrame()->IsVertical() ) + return rUnion.Right() >= pCell->getFrameArea().Right() && + rUnion.Left() <= pCell->getFrameArea().Left() && + (( rUnion.Top() <= pCell->getFrameArea().Top()+20 && + rUnion.Bottom() > pCell->getFrameArea().Top() ) || + ( rUnion.Top() >= pCell->getFrameArea().Top() && + rUnion.Bottom() < pCell->getFrameArea().Bottom() )); + + return + rUnion.Top() <= pCell->getFrameArea().Top() && + rUnion.Bottom() >= pCell->getFrameArea().Bottom() && + + (( rUnion.Left() <= pCell->getFrameArea().Left()+20 && + rUnion.Right() > pCell->getFrameArea().Left() ) || + + ( rUnion.Left() >= pCell->getFrameArea().Left() && + rUnion.Right() < pCell->getFrameArea().Right() )); +} + +bool GetAutoSumSel( const SwCursorShell& rShell, SwCellFrames& rBoxes ) +{ + SwShellCursor* pCursor = rShell.m_pCurrentCursor; + if ( rShell.IsTableMode() ) + pCursor = rShell.m_pTableCursor; + + std::pair<Point, bool> tmp(pCursor->GetPtPos(), true); + const SwLayoutFrame *const pStart = pCursor->GetContentNode()->getLayoutFrame( + rShell.GetLayout(), nullptr, &tmp)->GetUpper(); + tmp.first = pCursor->GetMkPos(); + const SwLayoutFrame *const pEnd = pCursor->GetContentNode(false)->getLayoutFrame( + rShell.GetLayout(), nullptr, &tmp)->GetUpper(); + + const SwLayoutFrame* pSttCell = pStart; + while( pSttCell && !pSttCell->IsCellFrame() ) + pSttCell = pSttCell->GetUpper(); + + // First, compute tables and rectangles + SwSelUnions aUnions; + + // by default, first test above and then to the left + ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::Col ); + + bool bTstRow = true, bFound = false; + + // 1. check if box above contains value/formula + for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i ) + { + SwSelUnion *pUnion = &aUnions[i]; + const SwTabFrame *pTable = pUnion->GetTable(); + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while( pRow ) + { + if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) ) + { + const SwCellFrame* pUpperCell = nullptr; + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while( pCell && pRow->IsAnLower( pCell ) ) + { + if( pCell == pSttCell ) + { + sal_uInt16 nWhichId = 0; + for( size_t n = rBoxes.size(); n; ) + { + nWhichId = rBoxes[ --n ]->GetTabBox()->IsFormulaOrValueBox(); + if( USHRT_MAX != nWhichId ) + break; + } + + // all boxes together, do not check the + // row, if a formula or value was found + bTstRow = 0 == nWhichId || USHRT_MAX == nWhichId; + bFound = true; + break; + } + + OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" ); + if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) ) + pUpperCell = static_cast<const SwCellFrame*>(pCell); + + if( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + + if( pUpperCell ) + rBoxes.push_back( const_cast< SwCellFrame* >(pUpperCell) ); + } + if( bFound ) + { + i = aUnions.size(); + break; + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + + // 2. check if box on left contains value/formula + if( bTstRow ) + { + bFound = false; + + rBoxes.clear(); + aUnions.clear(); + ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::Row ); + + for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i ) + { + SwSelUnion *pUnion = &aUnions[i]; + const SwTabFrame *pTable = pUnion->GetTable(); + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while( pRow ) + { + if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while( pCell && pRow->IsAnLower( pCell ) ) + { + if( pCell == pSttCell ) + { + sal_uInt16 nWhichId = 0; + for( size_t n = rBoxes.size(); n; ) + { + nWhichId = rBoxes[ --n ] + ->GetTabBox()->IsFormulaOrValueBox(); + if( USHRT_MAX != nWhichId ) + break; + } + + // all boxes together, do not check the + // row if a formula or value was found + bFound = 0 != nWhichId && USHRT_MAX != nWhichId; + bTstRow = false; + break; + } + + OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" ); + if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) ) + { + SwCellFrame* pC = const_cast<SwCellFrame*>(static_cast<const SwCellFrame*>(pCell)); + rBoxes.push_back( pC ); + } + if( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + if( !bTstRow ) + { + i = aUnions.size(); + break; + } + + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + } + + return bFound; +} + +bool HasProtectedCells( const SwSelBoxes& rBoxes ) +{ + bool bRet = false; + for (size_t n = 0; n < rBoxes.size(); ++n) + { + if( rBoxes[ n ]->GetFrameFormat()->GetProtect().IsContentProtected() ) + { + bRet = true; + break; + } + } + return bRet; +} + +CmpLPt::CmpLPt( const Point& rPt, const SwTableBox* pBox, bool bVertical ) + : aPos( rPt ), pSelBox( pBox ), bVert( bVertical ) +{} + +static void lcl_InsTableBox( SwTableNode* pTableNd, SwDoc* pDoc, SwTableBox* pBox, + sal_uInt16 nInsPos, sal_uInt16 nCnt = 1 ) +{ + OSL_ENSURE( pBox->GetSttNd(), "Box without Start-Node" ); + SwContentNode* pCNd = pDoc->GetNodes()[ pBox->GetSttIdx() + 1 ] + ->GetContentNode(); + if( pCNd && pCNd->IsTextNode() ) + pDoc->GetNodes().InsBoxen( pTableNd, pBox->GetUpper(), + static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()), + static_cast<SwTextNode*>(pCNd)->GetTextColl(), + pCNd->GetpSwAttrSet(), + nInsPos, nCnt ); + else + pDoc->GetNodes().InsBoxen( pTableNd, pBox->GetUpper(), + static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()), + pDoc->GetDfltTextFormatColl(), nullptr, + nInsPos, nCnt ); +} + +bool IsEmptyBox( const SwTableBox& rBox, SwPaM& rPam ) +{ + rPam.GetPoint()->nNode = *rBox.GetSttNd()->EndOfSectionNode(); + rPam.Move( fnMoveBackward, GoInContent ); + rPam.SetMark(); + rPam.GetPoint()->nNode = *rBox.GetSttNd(); + rPam.Move( fnMoveForward, GoInContent ); + bool bRet = *rPam.GetMark() == *rPam.GetPoint() + && ( rBox.GetSttNd()->GetIndex() + 1 == rPam.GetPoint()->nNode.GetIndex() ); + + if( bRet ) + { + // now check for paragraph bound flies + const SwFrameFormats& rFormats = *rPam.GetDoc().GetSpzFrameFormats(); + SwNodeOffset nSttIdx = rPam.GetPoint()->nNode.GetIndex(), + nEndIdx = rBox.GetSttNd()->EndOfSectionIndex(), + nIdx; + + for( auto pFormat : rFormats ) + { + const SwFormatAnchor& rAnchor = pFormat->GetAnchor(); + const SwPosition* pAPos = rAnchor.GetContentAnchor(); + if (pAPos && + ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) || + (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) && + nSttIdx <= ( nIdx = pAPos->nNode.GetIndex() ) && + nIdx < nEndIdx ) + { + bRet = false; + break; + } + } + } + return bRet; +} + +void GetMergeSel( const SwPaM& rPam, SwSelBoxes& rBoxes, + SwTableBox** ppMergeBox, SwUndoTableMerge* pUndo ) +{ + rBoxes.clear(); + + OSL_ENSURE( rPam.GetContentNode() && rPam.GetContentNode( false ), + "Tabselection not on Cnt." ); + +//JP 24.09.96: Merge with repeating TableHeadLines does not work properly. +// Why not use point 0,0? Then it is assured the first +// headline is contained. + Point aPt( 0, 0 ); + + const SwContentNode* pCntNd = rPam.GetContentNode(); + std::pair<Point, bool> const tmp(aPt, true); + const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + pCntNd = rPam.GetContentNode(false); + const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + + // First, compute tables and rectangles + SwSelUnions aUnions; + ::MakeSelUnions( aUnions, pStart, pEnd ); + if( aUnions.empty() ) + return; + + const SwTable *pTable = aUnions.front().GetTable()->GetTable(); + SwDoc* pDoc = const_cast<SwDoc*>(pStart->GetFormat()->GetDoc()); + SwTableNode* pTableNd = const_cast<SwTableNode*>(pTable->GetTabSortBoxes()[ 0 ]-> + GetSttNd()->FindTableNode()); + + MergePos aPosArr; // Sort-Array with the frame positions + tools::Long nWidth; + SwTableBox* pLastBox = nullptr; + + SwRectFnSet aRectFnSet(pStart->GetUpper()); + + for ( auto & rSelUnion : aUnions ) + { + const SwTabFrame *pTabFrame = rSelUnion.GetTable(); + + SwRect &rUnion = rSelUnion.GetUnion(); + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTabFrame->IsFollow() ? + pTabFrame->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTabFrame->Lower()); + + while ( pRow ) + { + if ( pRow->getFrameArea().Overlaps( rUnion ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while ( pCell && pRow->IsAnLower( pCell ) ) + { + OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" ); + // overlap in full width? + if( rUnion.Top() <= pCell->getFrameArea().Top() && + rUnion.Bottom() >= pCell->getFrameArea().Bottom() ) + { + SwTableBox* pBox = const_cast<SwTableBox*>(static_cast<const SwCellFrame*>(pCell)->GetTabBox()); + + // only overlap to the right? + if( ( rUnion.Left() - COLFUZZY ) <= pCell->getFrameArea().Left() && + ( rUnion.Right() - COLFUZZY ) > pCell->getFrameArea().Left() ) + { + if( ( rUnion.Right() + COLFUZZY ) < pCell->getFrameArea().Right() ) + { + sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1; + lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos ); + pBox->ClaimFrameFormat(); + SwFormatFrameSize aNew( + pBox->GetFrameFormat()->GetFrameSize() ); + nWidth = rUnion.Right() - pCell->getFrameArea().Left(); + nWidth = nWidth * aNew.GetWidth() / + pCell->getFrameArea().Width(); + tools::Long nTmpWidth = aNew.GetWidth() - nWidth; + aNew.SetWidth( nWidth ); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + // this box is selected + pLastBox = pBox; + rBoxes.insert( pBox ); + aPosArr.insert( + CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()), + pBox, aRectFnSet.IsVert() ) ); + + pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ]; + aNew.SetWidth( nTmpWidth ); + pBox->ClaimFrameFormat(); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + if( pUndo ) + pUndo->AddNewBox( pBox->GetSttIdx() ); + } + else + { + // this box is selected + pLastBox = pBox; + rBoxes.insert( pBox ); + aPosArr.insert( + CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()), + pBox, aRectFnSet.IsVert() ) ); + } + } + // overlapping on left- or right-side + else if( ( rUnion.Left() - COLFUZZY ) >= pCell->getFrameArea().Left() && + ( rUnion.Right() + COLFUZZY ) < pCell->getFrameArea().Right() ) + { + sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1; + lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos, 2 ); + pBox->ClaimFrameFormat(); + SwFormatFrameSize aNew( + pBox->GetFrameFormat()->GetFrameSize() ); + tools::Long nLeft = rUnion.Left() - pCell->getFrameArea().Left(); + nLeft = nLeft * aNew.GetWidth() / + pCell->getFrameArea().Width(); + tools::Long nRight = pCell->getFrameArea().Right() - rUnion.Right(); + nRight = nRight * aNew.GetWidth() / + pCell->getFrameArea().Width(); + nWidth = aNew.GetWidth() - nLeft - nRight; + + aNew.SetWidth( nLeft ); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + if( const SvxBoxItem* pItem = pBox->GetFrameFormat()->GetAttrSet() + .GetItemIfSet( RES_BOX, false )) + { + SvxBoxItem aBox( *pItem ); + aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT ); + pBox->GetFrameFormat()->SetFormatAttr( aBox ); + } + + pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ]; + aNew.SetWidth( nWidth ); + pBox->ClaimFrameFormat(); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + if( pUndo ) + pUndo->AddNewBox( pBox->GetSttIdx() ); + + // this box is selected + pLastBox = pBox; + rBoxes.insert( pBox ); + aPosArr.insert( + CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()), + pBox, aRectFnSet.IsVert() ) ); + + pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos+1 ]; + aNew.SetWidth( nRight ); + pBox->ClaimFrameFormat(); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + if( pUndo ) + pUndo->AddNewBox( pBox->GetSttIdx() ); + } + // is right side of box part of the selected area? + else if( ( pCell->getFrameArea().Right() - COLFUZZY ) < rUnion.Right() && + ( pCell->getFrameArea().Right() - COLFUZZY ) > rUnion.Left() && + ( pCell->getFrameArea().Left() + COLFUZZY ) < rUnion.Left() ) + { + // then we should insert a new box and adjust the widths + sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1; + lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos ); + + SwFormatFrameSize aNew(pBox->GetFrameFormat()->GetFrameSize() ); + tools::Long nLeft = rUnion.Left() - pCell->getFrameArea().Left(), + nRight = pCell->getFrameArea().Right() - rUnion.Left(); + + nLeft = nLeft * aNew.GetWidth() / + pCell->getFrameArea().Width(); + nRight = nRight * aNew.GetWidth() / + pCell->getFrameArea().Width(); + + aNew.SetWidth( nLeft ); + pBox->ClaimFrameFormat()->SetFormatAttr( aNew ); + + // this box is selected + pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ]; + aNew.SetWidth( nRight ); + pBox->ClaimFrameFormat(); + pBox->GetFrameFormat()->SetFormatAttr( aNew ); + + pLastBox = pBox; + rBoxes.insert( pBox ); + aPosArr.insert( CmpLPt( Point( rUnion.Left(), + pCell->getFrameArea().Top()), pBox, aRectFnSet.IsVert() )); + + if( pUndo ) + pUndo->AddNewBox( pBox->GetSttIdx() ); + } + } + if ( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + // --> Check if table cell is not empty + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + + // no SSelection / no boxes found + if( 1 >= rBoxes.size() ) + return; + + // now search all horizontally adjacent boxes and connect + // their contents with blanks. All vertically adjacent will be tied + // together as paragraphs + + // 1. Solution: map array and all on same Y-level + // are separated with blanks + // all others are separated with paragraphs + bool bCalcWidth = true; + const SwTableBox* pFirstBox = aPosArr[ 0 ].pSelBox; + + // JP 27.03.98: Optimise - if boxes on one row are empty, + // then do not insert blanks or carriage returns + //Block to assure SwPaM, SwPosition are deleted from stack + { + SwPaM aPam( pDoc->GetNodes() ); + +#if defined( DEL_ONLY_EMPTY_LINES ) + nWidth = pFirstBox->GetFrameFormat()->GetFrameSize().GetWidth(); + bool bEmptyLine = sal_True; + sal_uInt16 n, nSttPos = 0; + + for( n = 0; n < aPosArr.Count(); ++n ) + { + const CmpLPt& rPt = aPosArr[ n ]; + if( n && aPosArr[ n - 1 ].Y() == rPt.Y() ) // same Y level? + { + if( bEmptyLine && !IsEmptyBox( *rPt.pSelBox, aPam )) + bEmptyLine = sal_False; + if( bCalcWidth ) + nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth(); + } + else + { + if( bCalcWidth && n ) + bCalcWidth = false; // one line is ready + + if( bEmptyLine && nSttPos < n ) + { + // now complete line is empty and should not + // be filled with blanks and be inserted as paragraph + if( pUndo ) + for( sal_uInt16 i = nSttPos; i < n; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + aPosArr.Remove( nSttPos, n - nSttPos ); + n = nSttPos; + } + else + nSttPos = n; + + bEmptyLine = IsEmptyBox( *aPosArr[n].pSelBox, aPam ); + } + } + if( bEmptyLine && nSttPos < n ) + { + if( pUndo ) + for( sal_uInt16 i = nSttPos; i < n; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + aPosArr.Remove( nSttPos, n - nSttPos ); + } +#elif defined( DEL_EMPTY_BOXES_AT_START_AND_END ) + + nWidth = pFirstBox->GetFrameFormat()->GetFrameSize().GetWidth(); + sal_uInt16 n, nSttPos = 0, nSEndPos = 0, nESttPos = 0; + + for( n = 0; n < aPosArr.Count(); ++n ) + { + const CmpLPt& rPt = aPosArr[ n ]; + if( n && aPosArr[ n - 1 ].Y() == rPt.Y() ) // same Y level? + { + bool bEmptyBox = IsEmptyBox( *rPt.pSelBox, aPam ); + if( bEmptyBox ) + { + if( nSEndPos == n ) // beginning is empty + nESttPos = ++nSEndPos; + } + else // end could be empty + nESttPos = n+1; + + if( bCalcWidth ) + nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth(); + } + else + { + if( bCalcWidth && n ) + bCalcWidth = false; // one line ready + + // first those at the beginning + if( nSttPos < nSEndPos ) + { + // now the beginning of the line is empty and should + // not be filled with blanks + if( pUndo ) + for( sal_uInt16 i = nSttPos; i < nSEndPos; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + sal_uInt16 nCnt = nSEndPos - nSttPos; + aPosArr.Remove( nSttPos, nCnt ); + nESttPos -= nCnt; + n -= nCnt; + } + + if( nESttPos < n ) + { + // now the beginning of the line is empty and should + // not be filled with blanks + if( pUndo ) + for( sal_uInt16 i = nESttPos; i < n; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + sal_uInt16 nCnt = n - nESttPos; + aPosArr.Remove( nESttPos, nCnt ); + n -= nCnt; + } + + nSttPos = nSEndPos = nESttPos = n; + if( IsEmptyBox( *aPosArr[n].pSelBox, aPam )) + ++nSEndPos; + else + ++nESttPos; + } + } + + // first those at the beginning + if( nSttPos < nSEndPos ) + { + // now the beginning of the line is empty and should + // not be filled with blanks + if( pUndo ) + for( sal_uInt16 i = nSttPos; i < nSEndPos; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + sal_uInt16 nCnt = nSEndPos - nSttPos; + aPosArr.Remove( nSttPos, nCnt ); + nESttPos -= nCnt; + n -= nCnt; + } + if( nESttPos < n ) + { + // now the beginning of the line is empty and should + // not be filled with blanks + if( pUndo ) + for( sal_uInt16 i = nESttPos; i < n; ++i ) + pUndo->SaveCollection( *aPosArr[ i ].pSelBox ); + + sal_uInt16 nCnt = n - nESttPos; + aPosArr.Remove( nESttPos, nCnt ); + } +#else +// DEL_ALL_EMPTY_BOXES + + nWidth = 0; + tools::Long nY = !aPosArr.empty() ? + ( aRectFnSet.IsVert() ? + aPosArr[ 0 ].X() : + aPosArr[ 0 ].Y() ) : + 0; + + for( MergePos::size_type n = 0; n < aPosArr.size(); ++n ) + { + const CmpLPt& rPt = aPosArr[ n ]; + if( bCalcWidth ) + { + if( nY == ( aRectFnSet.IsVert() ? rPt.X() : rPt.Y() ) ) // same Y level? + nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth(); + else + bCalcWidth = false; // one line ready + } + + if( IsEmptyBox( *rPt.pSelBox, aPam ) ) + { + if( pUndo ) + pUndo->SaveCollection( *rPt.pSelBox ); + + aPosArr.erase( aPosArr.begin() + n ); + --n; + } + } +#endif + } + + // first create new box + { + SwTableBox* pTmpBox = rBoxes[0]; + SwTableLine* pInsLine = pTmpBox->GetUpper(); + sal_uInt16 nInsPos = pInsLine->GetBoxPos( pTmpBox ); + + lcl_InsTableBox( pTableNd, pDoc, pTmpBox, nInsPos ); + (*ppMergeBox) = pInsLine->GetTabBoxes()[ nInsPos ]; + pInsLine->GetTabBoxes().erase( pInsLine->GetTabBoxes().begin() + nInsPos ); // remove again + (*ppMergeBox)->SetUpper( nullptr ); + (*ppMergeBox)->ClaimFrameFormat(); + + // define the border: the upper/left side of the first box, + // the lower/right side of the last box: + if( pLastBox && pFirstBox ) + { + SvxBoxItem aBox( pFirstBox->GetFrameFormat()->GetBox() ); + const SvxBoxItem& rBox = pLastBox->GetFrameFormat()->GetBox(); + aBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT ); + aBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM ); + if( aBox.GetLeft() || aBox.GetTop() || + aBox.GetRight() || aBox.GetBottom() ) + (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( aBox ); + } + } + + //Block to delete SwPaM, SwPosition from stack + if( !aPosArr.empty() ) + { + SwPosition aInsPos( *(*ppMergeBox)->GetSttNd() ); + SwNodeIndex& rInsPosNd = aInsPos.nNode; + + SwPaM aPam( aInsPos ); + + for( const auto &rPt : aPosArr ) + { + aPam.GetPoint()->nNode.Assign( *rPt.pSelBox->GetSttNd()-> + EndOfSectionNode(), SwNodeOffset(-1) ); + SwContentNode* pCNd = aPam.GetContentNode(); + aPam.GetPoint()->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 ); + + SwNodeIndex aSttNdIdx( *rPt.pSelBox->GetSttNd(), 1 ); + // one node should be kept in the box (otherwise the + // section would be deleted during a move) + bool const bUndo(pDoc->GetIDocumentUndoRedo().DoesUndo()); + if( pUndo ) + { + pDoc->GetIDocumentUndoRedo().DoUndo(false); + } + pDoc->getIDocumentContentOperations().AppendTextNode( *aPam.GetPoint() ); + if( pUndo ) + { + pDoc->GetIDocumentUndoRedo().DoUndo(bUndo); + } + SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode ); + ++rInsPosNd; + if( pUndo ) + pUndo->MoveBoxContent( *pDoc, aRg, rInsPosNd ); + else + { + pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, rInsPosNd, + SwMoveFlags::DEFAULT ); + } + // where is now aInsPos ?? + + if( bCalcWidth ) + bCalcWidth = false; // one line is ready + + // skip the first TextNode + rInsPosNd.Assign( pDoc->GetNodes(), + rInsPosNd.GetNode().EndOfSectionIndex() - 2 ); + SwTextNode* pTextNd = rInsPosNd.GetNode().GetTextNode(); + if( pTextNd ) + aInsPos.nContent.Assign(pTextNd, pTextNd->GetText().getLength()); + } + + // the MergeBox should contain the complete text + // now erase the initial TextNode + OSL_ENSURE( (*ppMergeBox)->GetSttIdx()+2 < + (*ppMergeBox)->GetSttNd()->EndOfSectionIndex(), + "empty box" ); + SwNodeIndex aIdx( *(*ppMergeBox)->GetSttNd()->EndOfSectionNode(), -1 ); + pDoc->GetNodes().Delete( aIdx ); + } + + // set width of the box + (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 )); + if( pUndo ) + pUndo->AddNewBox( (*ppMergeBox)->GetSttIdx() ); +} + +static bool lcl_CheckCol(FndBox_ const&, bool* pPara); + +static bool lcl_CheckRow( const FndLine_& rFndLine, bool* pPara ) +{ + for (auto const& it : rFndLine.GetBoxes()) + { + lcl_CheckCol(*it, pPara); + } + return *pPara; +} + +static bool lcl_CheckCol( FndBox_ const& rFndBox, bool* pPara ) +{ + if (!rFndBox.GetBox()->GetSttNd()) + { + if (rFndBox.GetLines().size() != + rFndBox.GetBox()->GetTabLines().size()) + { + *pPara = false; + } + else + { + for (auto const& rpFndLine : rFndBox.GetLines()) + { + lcl_CheckRow( *rpFndLine, pPara ); + } + } + } + // is box protected ?? + else if (rFndBox.GetBox()->GetFrameFormat()->GetProtect().IsContentProtected()) + *pPara = false; + return *pPara; +} + +TableMergeErr CheckMergeSel( const SwPaM& rPam ) +{ + SwSelBoxes aBoxes; +//JP 24.09.96: Merge with repeating TableHeadLines does not work properly. +// Why not use point 0,0? Then it is assured the first +// headline is contained. + + Point aPt; + const SwContentNode* pCntNd = rPam.GetContentNode(); + std::pair<Point, bool> tmp(aPt, true); + const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + pCntNd = rPam.GetContentNode(false); + const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + GetTableSel( pStart, pEnd, aBoxes, nullptr ); + return CheckMergeSel( aBoxes ); +} + +TableMergeErr CheckMergeSel( const SwSelBoxes& rBoxes ) +{ + TableMergeErr eRet = TableMergeErr::NoSelection; + if( !rBoxes.empty() ) + { + eRet = TableMergeErr::Ok; + + FndBox_ aFndBox( nullptr, nullptr ); + FndPara aPara( rBoxes, &aFndBox ); + const SwTableNode* pTableNd = aPara.rBoxes[0]->GetSttNd()->FindTableNode(); + ForEach_FndLineCopyCol( const_cast<SwTableLines&>(pTableNd->GetTable().GetTabLines()), &aPara ); + if( !aFndBox.GetLines().empty() ) + { + bool bMergeSelOk = true; + FndBox_* pFndBox = &aFndBox; + FndLine_* pFndLine = nullptr; + while( pFndBox && 1 == pFndBox->GetLines().size() ) + { + pFndLine = pFndBox->GetLines().front().get(); + if( 1 == pFndLine->GetBoxes().size() ) + pFndBox = pFndLine->GetBoxes().front().get(); + else + pFndBox = nullptr; + } + if( pFndBox ) + { + for (auto const& it : pFndBox->GetLines()) + { + lcl_CheckRow(*it, &bMergeSelOk); + } + } + else if( pFndLine ) + { + for (auto const& it : pFndLine->GetBoxes()) + { + lcl_CheckCol(*it, &bMergeSelOk); + } + } + if( !bMergeSelOk ) + eRet = TableMergeErr::TooComplex; + } + else + eRet = TableMergeErr::NoSelection; + } + return eRet; +} + +static SwTwips lcl_CalcWish( const SwLayoutFrame *pCell, tools::Long nWish, + const tools::Long nAct ) +{ + const SwLayoutFrame *pTmp = pCell; + if ( !nWish ) + nWish = 1; + + const bool bRTL = pCell->IsRightToLeft(); + SwTwips nRet = bRTL ? + nAct - pCell->getFrameArea().Width() : + 0; + + while ( pTmp ) + { + while ( pTmp->GetPrev() ) + { + pTmp = static_cast<const SwLayoutFrame*>(pTmp->GetPrev()); + sal_Int64 nTmp = pTmp->GetFormat()->GetFrameSize().GetWidth(); + // multiply in 64-bit to avoid overflow here! + nRet += ( bRTL ? -1 : 1 ) * nTmp * nAct / nWish; + } + pTmp = pTmp->GetUpper()->GetUpper(); + if ( pTmp && !pTmp->IsCellFrame() ) + pTmp = nullptr; + } + return nRet; +} + +static void lcl_FindStartEndRow( const SwLayoutFrame *&rpStart, + const SwLayoutFrame *&rpEnd, + const bool bChkProtected ) +{ + // Put Start at beginning of a row. + // Put End at the end of its row. + rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetUpper()->Lower()); + while ( rpEnd->GetNext() ) + rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetNext()); + + std::deque<const SwLayoutFrame *> aSttArr, aEndArr; + const SwLayoutFrame *pTmp; + for( pTmp = rpStart; (SwFrameType::Cell|SwFrameType::Row) & pTmp->GetType(); + pTmp = pTmp->GetUpper() ) + { + aSttArr.push_front( pTmp ); + } + for( pTmp = rpEnd; (SwFrameType::Cell|SwFrameType::Row) & pTmp->GetType(); + pTmp = pTmp->GetUpper() ) + { + aEndArr.push_front( pTmp ); + } + + for( std::deque<const SwLayoutFrame *>::size_type n = 0; n < aEndArr.size() && n < aSttArr.size(); ++n ) + if( aSttArr[ n ] != aEndArr[ n ] ) + { + // first unequal line or box - all odds are + if( n & 1 ) // 1, 3, 5, ... are boxes + { + rpStart = aSttArr[ n ]; + rpEnd = aEndArr[ n ]; + } + else // 0, 2, 4, ... are lines + { + // check if start & end line are the first & last Line of the + // box. If not return these cells. + // Else the whole line with all Boxes has to be deleted. + rpStart = aSttArr[ n+1 ]; + rpEnd = aEndArr[ n+1 ]; + if( n ) + { + const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(aSttArr[ n-1 ]); + const SwTableLines& rLns = pCellFrame-> + GetTabBox()->GetTabLines(); + if( rLns[ 0 ] == static_cast<const SwRowFrame*>(aSttArr[ n ])->GetTabLine() && + rLns[ rLns.size() - 1 ] == + static_cast<const SwRowFrame*>(aEndArr[ n ])->GetTabLine() ) + { + rpStart = rpEnd = pCellFrame; + while ( rpStart->GetPrev() ) + rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetPrev()); + while ( rpEnd->GetNext() ) + rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetNext()); + } + } + } + break; + } + + if( !bChkProtected ) // protected cell ? + return; + + // Beginning and end should not be in protected cells + while ( rpStart->GetFormat()->GetProtect().IsContentProtected() ) + rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetNext()); + while ( rpEnd->GetFormat()->GetProtect().IsContentProtected() ) + rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetPrev()); +} + +static void lcl_FindStartEndCol( const SwLayoutFrame *&rpStart, + const SwLayoutFrame *&rpEnd, + const bool bChkProtected ) +{ + // Beginning and end vertical till the border of the table; + // Consider the whole table, including master and follows. + // In order to start we need the mother-tableFrame + if( !rpStart ) + return; + const SwTabFrame *pOrg = rpStart->FindTabFrame(); + const SwTabFrame *pTab = pOrg; + + SwRectFnSet aRectFnSet(pTab); + + bool bRTL = pTab->IsRightToLeft(); + const tools::Long nTmpWish = pOrg->GetFormat()->GetFrameSize().GetWidth(); + const tools::Long nWish = ( nTmpWish > 0 ) ? nTmpWish : 1; + + while ( pTab->IsFollow() ) + { + const SwFrame *pTmp = pTab->FindPrev(); + OSL_ENSURE( pTmp->IsTabFrame(), "Predecessor of Follow is not Master." ); + pTab = static_cast<const SwTabFrame*>(pTmp); + } + + SwTwips nSX = 0; + SwTwips nSX2 = 0; + + if ( pTab->GetTable()->IsNewModel() ) + { + nSX = aRectFnSet.GetLeft(rpStart->getFrameArea()); + nSX2 = aRectFnSet.GetRight(rpStart->getFrameArea()); + } + else + { + const SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea()); + nSX = ::lcl_CalcWish( rpStart, nWish, nPrtWidth ) + aRectFnSet.GetPrtLeft(*pTab); + nSX2 = nSX + (rpStart->GetFormat()->GetFrameSize().GetWidth() * nPrtWidth / nWish); + } + + const SwLayoutFrame *pTmp = pTab->FirstCell(); + + while ( pTmp && + (!pTmp->IsCellFrame() || + ( ( ! bRTL && aRectFnSet.GetLeft(pTmp->getFrameArea()) < nSX && + aRectFnSet.GetRight(pTmp->getFrameArea())< nSX2 ) || + ( bRTL && aRectFnSet.GetLeft(pTmp->getFrameArea()) > nSX && + aRectFnSet.GetRight(pTmp->getFrameArea())> nSX2 ) ) ) ) + pTmp = pTmp->GetNextLayoutLeaf(); + + if ( pTmp ) + rpStart = pTmp; + + pTab = pOrg; + + const SwTabFrame* pLastValidTab = pTab; + while ( pTab->GetFollow() ) + { + + // Check if pTab->GetFollow() is a valid follow table: + // Only follow tables with at least on non-FollowFlowLine + // should be considered. + + if ( pTab->HasFollowFlowLine() ) + { + pTab = pTab->GetFollow(); + const SwFrame* pTmpRow = pTab->GetFirstNonHeadlineRow(); + if ( pTmpRow && pTmpRow->GetNext() ) + pLastValidTab = pTab; + } + else + pLastValidTab = pTab = pTab->GetFollow(); + } + pTab = pLastValidTab; + + SwTwips nEX = 0; + + if ( pTab->GetTable()->IsNewModel() ) + { + nEX = aRectFnSet.GetLeft(rpEnd->getFrameArea()); + } + else + { + const SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea()); + nEX = ::lcl_CalcWish( rpEnd, nWish, nPrtWidth ) + aRectFnSet.GetPrtLeft(*pTab); + } + + SwFrame const*const pLastContent = pTab->FindLastContentOrTable(); + rpEnd = pLastContent ? pLastContent->GetUpper() : nullptr; + // --> Made code robust. If pTab does not have a lower, + // we would crash here. + if ( !pLastContent ) return; + + while( !rpEnd->IsCellFrame() ) + rpEnd = rpEnd->GetUpper(); + + while ( ( bRTL && aRectFnSet.GetLeft(rpEnd->getFrameArea()) < nEX ) || + ( ! bRTL && aRectFnSet.GetLeft(rpEnd->getFrameArea()) > nEX ) ) + { + const SwLayoutFrame* pTmpLeaf = rpEnd->GetPrevLayoutLeaf(); + if( !pTmpLeaf || !pTab->IsAnLower( pTmpLeaf ) ) + break; + rpEnd = pTmpLeaf; + } + + if( !bChkProtected ) // check for protected cell ? + return; + + // Beginning and end should not be in protected cells. + // If necessary we should search backwards again + while ( rpStart->GetFormat()->GetProtect().IsContentProtected() ) + { + const SwLayoutFrame *pTmpLeaf = rpStart->GetNextLayoutLeaf(); + while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) > nEX ) // first skip line + pTmpLeaf = pTmpLeaf->GetNextLayoutLeaf(); + while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) < nSX && + aRectFnSet.GetRight(pTmpLeaf->getFrameArea())< nSX2 ) + pTmpLeaf = pTmpLeaf->GetNextLayoutLeaf(); + const SwTabFrame *pTmpTab = rpStart->FindTabFrame(); + if ( !pTmpTab->IsAnLower( pTmpLeaf ) ) + { + pTmpTab = pTmpTab->GetFollow(); + rpStart = pTmpTab->FirstCell(); + while ( rpStart && + aRectFnSet.GetLeft(rpStart->getFrameArea()) < nSX && + aRectFnSet.GetRight(rpStart->getFrameArea())< nSX2 ) + rpStart = rpStart->GetNextLayoutLeaf(); + } + else + rpStart = pTmpLeaf; + } + while ( rpEnd->GetFormat()->GetProtect().IsContentProtected() ) + { + const SwLayoutFrame *pTmpLeaf = rpEnd->GetPrevLayoutLeaf(); + while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) < nEX ) // skip the line for now + pTmpLeaf = pTmpLeaf->GetPrevLayoutLeaf(); + while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) > nEX ) + pTmpLeaf = pTmpLeaf->GetPrevLayoutLeaf(); + const SwTabFrame *pTmpTab = rpEnd->FindTabFrame(); + if ( !pTmpLeaf || !pTmpTab->IsAnLower( pTmpLeaf ) ) + { + pTmpTab = static_cast<const SwTabFrame*>(pTmpTab->FindPrev()); + OSL_ENSURE( pTmpTab->IsTabFrame(), "Predecessor of Follow not Master."); + rpEnd = pTmpTab->FindLastContentOrTable()->GetUpper(); + while( !rpEnd->IsCellFrame() ) + rpEnd = rpEnd->GetUpper(); + while ( aRectFnSet.GetLeft(rpEnd->getFrameArea()) > nEX ) + rpEnd = rpEnd->GetPrevLayoutLeaf(); + } + else + rpEnd = pTmpLeaf; + } +} + +void MakeSelUnions( SwSelUnions& rUnions, const SwLayoutFrame *pStart, + const SwLayoutFrame *pEnd, const SwTableSearchType eSearchType ) +{ + while ( pStart && !pStart->IsCellFrame() ) + pStart = pStart->GetUpper(); + while ( pEnd && !pEnd->IsCellFrame() ) + pEnd = pEnd->GetUpper(); + + if ( !pStart || !pEnd ) + { + OSL_FAIL( "MakeSelUnions with pStart or pEnd not in CellFrame" ); + return; + } + + const SwTabFrame *pTable = pStart->FindTabFrame(); + const SwTabFrame *pEndTable = pEnd->FindTabFrame(); + if( !pTable || !pEndTable ) + return; + bool bExchange = false; + + if ( pTable != pEndTable ) + { + if ( !pTable->IsAnFollow( pEndTable ) ) + { + OSL_ENSURE( pEndTable->IsAnFollow( pTable ), "Tabchain in knots." ); + bExchange = true; + } + } + else + { + SwRectFnSet aRectFnSet(pTable); + tools::Long nSttTop = aRectFnSet.GetTop(pStart->getFrameArea()); + tools::Long nEndTop = aRectFnSet.GetTop(pEnd->getFrameArea()); + if( nSttTop == nEndTop ) + { + if( aRectFnSet.GetLeft(pStart->getFrameArea()) > + aRectFnSet.GetLeft(pEnd->getFrameArea()) ) + bExchange = true; + } + else if( aRectFnSet.IsVert() == ( nSttTop < nEndTop ) ) + bExchange = true; + } + if ( bExchange ) + { + const SwLayoutFrame *pTmp = pStart; + pStart = pEnd; + pEnd = pTmp; + // do no resort pTable and pEndTable, set new below + // MA: 28. Dec. 93 Bug: 5190 + } + + // Beginning and end now nicely sorted, if required we + // should move them + if( SwTableSearchType::Row == ((~SwTableSearchType::Protect ) & eSearchType ) ) + ::lcl_FindStartEndRow( pStart, pEnd, bool(SwTableSearchType::Protect & eSearchType) ); + else if( SwTableSearchType::Col == ((~SwTableSearchType::Protect ) & eSearchType ) ) + ::lcl_FindStartEndCol( pStart, pEnd, bool(SwTableSearchType::Protect & eSearchType) ); + + if ( !pEnd || !pStart ) return; // Made code robust. + + // retrieve again, as they have been moved + pTable = pStart->FindTabFrame(); + pEndTable = pEnd->FindTabFrame(); + + const tools::Long nStSz = pStart->GetFormat()->GetFrameSize().GetWidth(); + const tools::Long nEdSz = pEnd->GetFormat()->GetFrameSize().GetWidth(); + const tools::Long nWish = std::max( tools::Long(1), pTable->GetFormat()->GetFrameSize().GetWidth() ); + while ( pTable ) + { + SwRectFnSet aRectFnSet(pTable); + const tools::Long nOfst = aRectFnSet.GetPrtLeft(*pTable); + const tools::Long nPrtWidth = aRectFnSet.GetWidth(pTable->getFramePrintArea()); + tools::Long nSt1 = ::lcl_CalcWish( pStart, nWish, nPrtWidth ) + nOfst; + tools::Long nEd1 = ::lcl_CalcWish( pEnd, nWish, nPrtWidth ) + nOfst; + + if ( nSt1 <= nEd1 ) + nEd1 += static_cast<tools::Long>((nEdSz * nPrtWidth) / nWish) - 1; + else + nSt1 += static_cast<tools::Long>((nStSz * nPrtWidth) / nWish) - 1; + + tools::Long nSt2; + tools::Long nEd2; + if( pTable->IsAnLower( pStart ) ) + nSt2 = aRectFnSet.GetTop(pStart->getFrameArea()); + else + nSt2 = aRectFnSet.GetTop(pTable->getFrameArea()); + if( pTable->IsAnLower( pEnd ) ) + nEd2 = aRectFnSet.GetBottom(pEnd->getFrameArea()); + else + nEd2 = aRectFnSet.GetBottom(pTable->getFrameArea()); + Point aSt, aEd; + if( nSt1 > nEd1 ) + { + tools::Long nTmp = nSt1; + nSt1 = nEd1; + nEd1 = nTmp; + } + if( nSt2 > nEd2 ) + { + tools::Long nTmp = nSt2; + nSt2 = nEd2; + nEd2 = nTmp; + } + if( aRectFnSet.IsVert() ) + { + aSt = Point( nSt2, nSt1 ); + aEd = Point( nEd2, nEd1 ); + } + else + { + aSt = Point( nSt1, nSt2 ); + aEd = Point( nEd1, nEd2 ); + } + + const Point aDiff( aEd - aSt ); + SwRect aUnion( aSt, Size( aDiff.X(), aDiff.Y() ) ); + aUnion.Justify(); + + if( !(SwTableSearchType::NoUnionCorrect & eSearchType )) + { + // Unfortunately the union contains rounding errors now, therefore + // erroneous results could occur during split/merge. + // To prevent these we will determine the first and last row + // within the union and use their values for a new union + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while ( pRow && !pRow->getFrameArea().Overlaps( aUnion ) ) + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + + // #i31976# + // A follow flow row may contain empty cells. These are not + // considered by FirstCell(). Therefore we have to find + // the first cell manually: + const SwFrame* pTmpCell = nullptr; + if ( pTable->IsFollow() && pRow && pRow->IsInFollowFlowRow() ) + { + const SwFrame* pTmpRow = pRow; + while ( pTmpRow && pTmpRow->IsRowFrame() ) + { + pTmpCell = static_cast<const SwRowFrame*>(pTmpRow)->Lower(); + pTmpRow = static_cast<const SwCellFrame*>(pTmpCell)->Lower(); + } + OSL_ENSURE( !pTmpCell || pTmpCell->IsCellFrame(), "Lower of rowframe != cellframe?!" ); + } + + const SwLayoutFrame* pFirst = pTmpCell ? + static_cast<const SwLayoutFrame*>(pTmpCell) : + pRow ? + pRow->FirstCell() : + nullptr; + + while ( pFirst && !::IsFrameInTableSel( aUnion, pFirst ) ) + { + if ( pFirst->GetNext() ) + { + pFirst = static_cast<const SwLayoutFrame*>(pFirst->GetNext()); + if ( pFirst->Lower() && pFirst->Lower()->IsRowFrame() ) + pFirst = pFirst->FirstCell(); + } + else + pFirst = ::lcl_FindNextCellFrame( pFirst ); + } + const SwLayoutFrame* pLast = nullptr; + SwFrame const*const pLastContent = pTable->FindLastContentOrTable(); + if ( pLastContent ) + pLast = ::lcl_FindCellFrame( pLastContent->GetUpper() ); + + while ( pLast && !::IsFrameInTableSel( aUnion, pLast ) ) + pLast = ::lcl_FindCellFrame( pLast->GetPrevLayoutLeaf() ); + + if ( pFirst && pLast ) //Robust + { + aUnion = pFirst->getFrameArea(); + aUnion.Union( pLast->getFrameArea() ); + } + else + aUnion.Width( 0 ); + } + + if( aRectFnSet.GetWidth(aUnion) ) + { + rUnions.emplace_back(aUnion, const_cast<SwTabFrame*>(pTable)); + } + + pTable = pTable->GetFollow(); + if ( pTable != pEndTable && pEndTable->IsAnFollow( pTable ) ) + pTable = nullptr; + } +} + +bool CheckSplitCells( const SwCursorShell& rShell, sal_uInt16 nDiv, + const SwTableSearchType eSearchType ) +{ + if( !rShell.IsTableMode() ) + rShell.GetCursor(); + + return CheckSplitCells( *rShell.getShellCursor(false), nDiv, eSearchType ); +} + +bool CheckSplitCells( const SwCursor& rCursor, sal_uInt16 nDiv, + const SwTableSearchType eSearchType ) +{ + if( 1 >= nDiv ) + return false; + + sal_uInt16 nMinValue = nDiv * MINLAY; + + // Get start and end cell + Point aPtPos, aMkPos; + const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor); + if( pShCursor ) + { + aPtPos = pShCursor->GetPtPos(); + aMkPos = pShCursor->GetMkPos(); + } + + const SwContentNode* pCntNd = rCursor.GetContentNode(); + std::pair<Point, bool> tmp(aPtPos, true); + const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + pCntNd = rCursor.GetContentNode(false); + tmp.first = aMkPos; + const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame( + pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), + nullptr, &tmp)->GetUpper(); + + SwRectFnSet aRectFnSet(pStart->GetUpper()); + + // First, compute tables and rectangles + SwSelUnions aUnions; + + ::MakeSelUnions( aUnions, pStart, pEnd, eSearchType ); + + // now search boxes for each entry and emit + for ( const auto& rSelUnion : aUnions ) + { + const SwTabFrame *pTable = rSelUnion.GetTable(); + + // Skip any repeated headlines in the follow: + const SwLayoutFrame* pRow = pTable->IsFollow() ? + pTable->GetFirstNonHeadlineRow() : + static_cast<const SwLayoutFrame*>(pTable->Lower()); + + while ( pRow ) + { + if ( pRow->getFrameArea().Overlaps( rSelUnion.GetUnion() ) ) + { + const SwLayoutFrame *pCell = pRow->FirstCell(); + + while ( pCell && pRow->IsAnLower( pCell ) ) + { + OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" ); + if( ::IsFrameInTableSel( rSelUnion.GetUnion(), pCell ) ) + { + if( aRectFnSet.GetWidth(pCell->getFrameArea()) < nMinValue ) + return false; + } + + if ( pCell->GetNext() ) + { + pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext()); + if ( pCell->Lower() && pCell->Lower()->IsRowFrame() ) + pCell = pCell->FirstCell(); + } + else + pCell = ::lcl_FindNextCellFrame( pCell ); + } + } + pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext()); + } + } + return true; +} + +// These Classes copy the current table selections (rBoxes), +// into a new structure, retaining the table structure +// new: SS for targeted erasing/restoring of the layout + +static void lcl_InsertRow( SwTableLine const &rLine, SwLayoutFrame *pUpper, SwFrame *pSibling ) +{ + SwRowFrame *pRow = new SwRowFrame( rLine, pUpper ); + if ( pUpper->IsTabFrame() && static_cast<SwTabFrame*>(pUpper)->IsFollow() ) + { + SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pUpper); + pTabFrame->FindMaster()->InvalidatePos(); //can absorb the line + + if ( pSibling && pTabFrame->IsInHeadline( *pSibling ) ) + { + // Skip any repeated headlines in the follow: + pSibling = pTabFrame->GetFirstNonHeadlineRow(); + } + } + pRow->Paste( pUpper, pSibling ); + pRow->RegistFlys(); +} + +static void FndBoxCopyCol( SwTableBox* pBox, FndPara* pFndPara ) +{ + std::unique_ptr<FndBox_> pFndBox(new FndBox_( pBox, pFndPara->pFndLine )); + if( !pBox->GetTabLines().empty() ) + { + FndPara aPara( *pFndPara, pFndBox.get() ); + ForEach_FndLineCopyCol( pFndBox->GetBox()->GetTabLines(), &aPara ); + if( pFndBox->GetLines().empty() ) + { + return; + } + } + else + { + if( pFndPara->rBoxes.find( pBox ) == pFndPara->rBoxes.end()) + { + return; + } + } + pFndPara->pFndLine->GetBoxes().push_back( std::move(pFndBox) ); +} + +static void FndLineCopyCol( SwTableLine* pLine, FndPara* pFndPara ) +{ + std::unique_ptr<FndLine_> pFndLine(new FndLine_(pLine, pFndPara->pFndBox)); + FndPara aPara(*pFndPara, pFndLine.get()); + for( auto& rpBox : pFndLine->GetLine()->GetTabBoxes() ) + FndBoxCopyCol(rpBox, &aPara ); + if( !pFndLine->GetBoxes().empty() ) + { + pFndPara->pFndBox->GetLines().push_back( std::move(pFndLine) ); + } +} + +void ForEach_FndLineCopyCol(SwTableLines& rLines, FndPara* pFndPara ) +{ + for( SwTableLines::iterator it = rLines.begin(); it != rLines.end(); ++it ) + FndLineCopyCol( *it, pFndPara ); +} + +void FndBox_::SetTableLines( const SwSelBoxes &rBoxes, const SwTable &rTable ) +{ + // Set pointers to lines before and after the area to process. + // If the first/last lines are contained in the area, then the pointers + // are 0. We first search for the positions of the first/last affected + // lines in array of the SwTable. In order to use 0 for 'no line' + // we adjust the positions by 1. + + sal_uInt16 nStPos = USHRT_MAX; + sal_uInt16 nEndPos= 0; + + for (size_t i = 0; i < rBoxes.size(); ++i) + { + SwTableLine *pLine = rBoxes[i]->GetUpper(); + while ( pLine->GetUpper() ) + pLine = pLine->GetUpper()->GetUpper(); + const sal_uInt16 nPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(pLine) ) + 1; + + OSL_ENSURE( nPos != USHRT_MAX, "TableLine not found." ); + + if( nStPos > nPos ) + nStPos = nPos; + + if( nEndPos < nPos ) + nEndPos = nPos; + } + if (USHRT_MAX != nStPos && nStPos > 1) + m_pLineBefore = rTable.GetTabLines()[nStPos - 2]; + if ( nEndPos < rTable.GetTabLines().size() ) + m_pLineBehind = rTable.GetTabLines()[nEndPos]; +} + +void FndBox_::SetTableLines( const SwTable &rTable ) +{ + // Set pointers to lines before and after the area to process. + // If the first/last lines are contained in the area, then the pointers + // are 0. The positions of the first/last affected lines in the array + // of the SwTable are in FndBox. In order to use 0 for 'no line' + // we adjust the positions by 1. + + if( GetLines().empty() ) + return; + + SwTableLine* pTmpLine = GetLines().front()->GetLine(); + sal_uInt16 nPos = rTable.GetTabLines().GetPos( pTmpLine ); + OSL_ENSURE( USHRT_MAX != nPos, "Line is not in table" ); + if( nPos ) + m_pLineBefore = rTable.GetTabLines()[ nPos - 1 ]; + + pTmpLine = GetLines().back()->GetLine(); + nPos = rTable.GetTabLines().GetPos( pTmpLine ); + OSL_ENSURE( USHRT_MAX != nPos, "Line is not in the table" ); + if( ++nPos < rTable.GetTabLines().size() ) + m_pLineBehind = rTable.GetTabLines()[nPos]; +} + +inline void UnsetFollow( SwFlowFrame *pTab ) +{ + pTab->m_pPrecede = nullptr; +} + +void FndBox_::DelFrames( SwTable &rTable ) +{ + // All lines between pLineBefore and pLineBehind should be cut + // from the layout and erased. + // If this creates empty Follows we should destroy these. + // If a master is destroyed, the follow should become master. + // Always a TabFrame should remain. + + sal_uInt16 nStPos = 0; + sal_uInt16 nEndPos= rTable.GetTabLines().size() - 1; + if( rTable.IsNewModel() && m_pLineBefore ) + rTable.CheckRowSpan( m_pLineBefore, true ); + if ( m_pLineBefore ) + { + nStPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(m_pLineBefore) ); + OSL_ENSURE( nStPos != USHRT_MAX, "The fox stole the line!" ); + ++nStPos; + } + if( rTable.IsNewModel() && m_pLineBehind ) + rTable.CheckRowSpan( m_pLineBehind, false ); + if ( m_pLineBehind ) + { + nEndPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(m_pLineBehind) ); + OSL_ENSURE( nEndPos != USHRT_MAX, "The fox stole the line!" ); + if (nEndPos != 0) + --nEndPos; + } + + for ( sal_uInt16 i = nStPos; i <= nEndPos; ++i) + { + SwFrameFormat *pFormat = rTable.GetTabLines()[i]->GetFrameFormat(); + SwIterator<SwRowFrame,SwFormat> aIter( *pFormat ); + for ( SwRowFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() ) + { + if ( pFrame->GetTabLine() == rTable.GetTabLines()[i] ) + { + bool bDel = true; + SwTabFrame *pUp = !pFrame->GetPrev() && !pFrame->GetNext() ? + static_cast<SwTabFrame*>(pFrame->GetUpper()) : nullptr; + if ( !pUp ) + { + const sal_uInt16 nRepeat = + static_cast<SwTabFrame*>(pFrame->GetUpper())->GetTable()->GetRowsToRepeat(); + if ( nRepeat > 0 && + static_cast<SwTabFrame*>(pFrame->GetUpper())->IsFollow() ) + { + if ( !pFrame->GetNext() ) + { + SwRowFrame* pFirstNonHeadline = + static_cast<SwTabFrame*>(pFrame->GetUpper())->GetFirstNonHeadlineRow(); + if ( pFirstNonHeadline == pFrame ) + { + pUp = static_cast<SwTabFrame*>(pFrame->GetUpper()); + } + } + } + } + if ( pUp ) + { + SwTabFrame *pFollow = pUp->GetFollow(); + SwTabFrame *pPrev = pUp->IsFollow() ? pUp : nullptr; + if ( pPrev ) + { + SwFrame *pTmp = pPrev->FindPrev(); + OSL_ENSURE( pTmp->IsTabFrame(), + "Predecessor of Follow is no Master."); + pPrev = static_cast<SwTabFrame*>(pTmp); + } + if ( pPrev ) + { + pPrev->SetFollow( pFollow ); + // #i60340# Do not transfer the + // flag from pUp to pPrev. pUp may still have the + // flag set although there is not more follow flow + // line associated with pUp. + pPrev->SetFollowFlowLine( false ); + } + else if ( pFollow ) + ::UnsetFollow( pFollow ); + + // A TableFrame should always remain! + if ( pPrev || pFollow ) + { + // OD 26.08.2003 #i18103# - if table is in a section, + // lock the section, to avoid its delete. + { + SwSectionFrame* pSctFrame = pUp->FindSctFrame(); + bool bOldSectLock = false; + if ( pSctFrame ) + { + bOldSectLock = pSctFrame->IsColLocked(); + pSctFrame->ColLock(); + } + pUp->Cut(); + if ( pSctFrame && !bOldSectLock ) + { + pSctFrame->ColUnlock(); + } + } + SwFrame::DestroyFrame(pUp); + bDel = false; // Row goes to /dev/null. + } + } + if ( bDel ) + { + SwFrame* pTabFrame = pFrame->GetUpper(); + if ( pTabFrame->IsTabFrame() && + !pFrame->GetNext() && + static_cast<SwTabFrame*>(pTabFrame)->GetFollow() ) + { + // We do not delete the follow flow line, + // this will be done automatically in the + // next turn. + static_cast<SwTabFrame*>(pTabFrame)->SetFollowFlowLine( false ); + } + pFrame->Cut(); + SwFrame::DestroyFrame(pFrame); + } + } + } + } +} + +static bool lcl_IsLineOfTableFrame( const SwTabFrame& rTable, const SwFrame& rChk ) +{ + const SwTabFrame* pTableFrame = rChk.FindTabFrame(); + if( pTableFrame->IsFollow() ) + pTableFrame = pTableFrame->FindMaster( true ); + return &rTable == pTableFrame; +} + +static void lcl_UpdateRepeatedHeadlines( SwTabFrame& rTabFrame, bool bCalcLowers ) +{ + OSL_ENSURE( rTabFrame.IsFollow(), "lcl_UpdateRepeatedHeadlines called for non-follow tab" ); + + // Delete remaining headlines: + SwRowFrame* pLower = nullptr; + while ( nullptr != ( pLower = static_cast<SwRowFrame*>(rTabFrame.Lower()) ) && pLower->IsRepeatedHeadline() ) + { + pLower->Cut(); + SwFrame::DestroyFrame(pLower); + } + + // Insert fresh set of headlines: + pLower = static_cast<SwRowFrame*>(rTabFrame.Lower()); + SwTable& rTable = *rTabFrame.GetTable(); + const sal_uInt16 nRepeat = rTable.GetRowsToRepeat(); + for ( sal_uInt16 nIdx = 0; nIdx < nRepeat; ++nIdx ) + { + SwRowFrame* pHeadline = new SwRowFrame( *rTable.GetTabLines()[ nIdx ], &rTabFrame ); + pHeadline->SetRepeatedHeadline( true ); + pHeadline->Paste( &rTabFrame, pLower ); + pHeadline->RegistFlys(); + } + + if ( bCalcLowers ) + rTabFrame.SetCalcLowers(); +} + +void FndBox_::MakeFrames( SwTable &rTable ) +{ + // All lines between pLineBefore and pLineBehind should be re-generated in layout. + // And this for all instances of a table (for example in header/footer). + sal_uInt16 nStPos = 0; + sal_uInt16 nEndPos= rTable.GetTabLines().size() - 1; + SwRootFrame* pLayout = + rTable.GetFrameFormat()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(); + bool bHideChanges = pLayout && pLayout->IsHideRedlines(); + + if ( m_pLineBefore ) + { + nStPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(m_pLineBefore) ); + OSL_ENSURE( nStPos != USHRT_MAX, "Fox stole the line!" ); + ++nStPos; + + } + if ( m_pLineBehind ) + { + nEndPos = rTable.GetTabLines().GetPos( + const_cast<const SwTableLine*&>(m_pLineBehind) ); + OSL_ENSURE( nEndPos != USHRT_MAX, "Fox stole the line!" ); + --nEndPos; + } + // now big insert operation for all tables. + SwIterator<SwTabFrame,SwFormat> aTabIter( *rTable.GetFrameFormat() ); + for ( SwTabFrame *pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() ) + { + if ( !pTable->IsFollow() ) + { + SwRowFrame *pSibling = nullptr; + SwFrame *pUpperFrame = nullptr; + int i; + for ( i = rTable.GetTabLines().size()-1; + i >= 0 && !pSibling; --i ) + { + SwTableLine *pLine = m_pLineBehind ? m_pLineBehind : + rTable.GetTabLines()[o3tl::narrowing<sal_uInt16>(i)]; + SwIterator<SwRowFrame,SwFormat> aIter( *pLine->GetFrameFormat() ); + pSibling = aIter.First(); + while ( pSibling && ( + pSibling->GetTabLine() != pLine || + !lcl_IsLineOfTableFrame( *pTable, *pSibling ) || + pSibling->IsRepeatedHeadline() || + // #i53647# If !pLineBehind, + // IsInSplitTableRow() should be checked. + ( m_pLineBehind && pSibling->IsInFollowFlowRow() ) || + (!m_pLineBehind && pSibling->IsInSplitTableRow() ) ) ) + { + pSibling = aIter.Next(); + } + } + if ( pSibling ) + { + pUpperFrame = pSibling->GetUpper(); + if ( !m_pLineBehind ) + pSibling = nullptr; + } + else +// ???? or is this the last Follow of the table ???? + pUpperFrame = pTable; + + SwRedlineTable::size_type nRedlinePos = 0; + for ( sal_uInt16 j = nStPos; j <= nEndPos; ++j ) + { + SwTableLine * pLine = rTable.GetTabLines()[j]; + if ( !bHideChanges || !pLine->IsDeleted(nRedlinePos) ) + ::lcl_InsertRow( *pLine, + static_cast<SwLayoutFrame*>(pUpperFrame), pSibling ); + } + if ( pUpperFrame->IsTabFrame() ) + static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers(); + } + else if ( rTable.GetRowsToRepeat() > 0 ) + { + // Insert new headlines: + lcl_UpdateRepeatedHeadlines( *pTable, true ); + } + } +} + +void FndBox_::MakeNewFrames( SwTable &rTable, const sal_uInt16 nNumber, + const bool bBehind ) +{ + // Create Frames for newly inserted lines + // bBehind == true: before pLineBehind + // == false: after pLineBefore + const sal_uInt16 nBfPos = m_pLineBefore ? + rTable.GetTabLines().GetPos( const_cast<const SwTableLine*&>(m_pLineBefore) ) : + USHRT_MAX; + const sal_uInt16 nBhPos = m_pLineBehind ? + rTable.GetTabLines().GetPos( const_cast<const SwTableLine*&>(m_pLineBehind) ) : + USHRT_MAX; + + //nNumber: how often did we insert + //nCnt: how many were inserted nNumber times + + const sal_uInt16 nCnt = + ((nBhPos != USHRT_MAX ? nBhPos : rTable.GetTabLines().size()) - + (nBfPos != USHRT_MAX ? nBfPos + 1 : 0)) / (nNumber + 1); + + // search the Master-TabFrame + SwIterator<SwTabFrame,SwFormat> aTabIter( *rTable.GetFrameFormat() ); + SwTabFrame *pTable; + for ( pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() ) + { + if( !pTable->IsFollow() ) + { + SwRowFrame* pSibling = nullptr; + SwLayoutFrame *pUpperFrame = nullptr; + if ( bBehind ) + { + if ( m_pLineBehind ) + { + SwIterator<SwRowFrame,SwFormat> aIter( *m_pLineBehind->GetFrameFormat() ); + pSibling = aIter.First(); + while ( pSibling && ( + // only consider row frames associated with pLineBehind: + pSibling->GetTabLine() != m_pLineBehind || + // only consider row frames that are in pTables Master-Follow chain: + !lcl_IsLineOfTableFrame( *pTable, *pSibling ) || + // only consider row frames that are not repeated headlines: + pSibling->IsRepeatedHeadline() || + // only consider row frames that are not follow flow rows + pSibling->IsInFollowFlowRow() ) ) + { + pSibling = aIter.Next(); + } + } + if ( pSibling ) + pUpperFrame = pSibling->GetUpper(); + else + { + while( pTable->GetFollow() ) + pTable = pTable->GetFollow(); + pUpperFrame = pTable; + } + const sal_uInt16 nMax = nBhPos != USHRT_MAX ? + nBhPos : rTable.GetTabLines().size(); + + sal_uInt16 i = nBfPos != USHRT_MAX ? nBfPos + 1 + nCnt : nCnt; + + for ( ; i < nMax; ++i ) + ::lcl_InsertRow( *rTable.GetTabLines()[i], pUpperFrame, pSibling ); + if ( pUpperFrame->IsTabFrame() ) + static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers(); + } + else // insert before + { + sal_uInt16 i; + + // We are looking for the frame that is behind the row frame + // that should be inserted. + for ( i = 0; !pSibling; ++i ) + { + SwTableLine* pLine = m_pLineBefore ? m_pLineBefore : rTable.GetTabLines()[i]; + + SwIterator<SwRowFrame,SwFormat> aIter( *pLine->GetFrameFormat() ); + pSibling = aIter.First(); + + while ( pSibling && ( + // only consider row frames associated with pLineBefore: + pSibling->GetTabLine() != pLine || + // only consider row frames that are in pTables Master-Follow chain: + !lcl_IsLineOfTableFrame( *pTable, *pSibling ) || + // only consider row frames that are not repeated headlines: + pSibling->IsRepeatedHeadline() || + // 1. case: pLineBefore == 0: + // only consider row frames that are not follow flow rows + // 2. case: pLineBefore != 0: + // only consider row frames that are not split table rows + // #i37476# If !pLineBefore, + // check IsInFollowFlowRow instead of IsInSplitTableRow. + ( ( !m_pLineBefore && pSibling->IsInFollowFlowRow() ) || + ( m_pLineBefore && pSibling->IsInSplitTableRow() ) ) ) ) + { + pSibling = aIter.Next(); + } + } + + pUpperFrame = pSibling->GetUpper(); + if ( m_pLineBefore ) + pSibling = static_cast<SwRowFrame*>( pSibling->GetNext() ); + + sal_uInt16 nMax = nBhPos != USHRT_MAX ? + nBhPos - nCnt : + rTable.GetTabLines().size() - nCnt; + + i = nBfPos != USHRT_MAX ? nBfPos + 1 : 0; + for ( ; i < nMax; ++i ) + ::lcl_InsertRow( *rTable.GetTabLines()[i], + pUpperFrame, pSibling ); + if ( pUpperFrame->IsTabFrame() ) + static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers(); + } + } + } + + // If necessary headlines should be processed. In order to + // not to fragment good code, we iterate once more. + const sal_uInt16 nRowsToRepeat = rTable.GetRowsToRepeat(); + if ( !(nRowsToRepeat > 0 && + ( ( !bBehind && ( nBfPos == USHRT_MAX || nBfPos + 1 < nRowsToRepeat ) ) || + ( bBehind && ( ( nBfPos == USHRT_MAX && nRowsToRepeat > 1 ) || nBfPos + 2 < nRowsToRepeat ) ) )) ) + return; + + for ( pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() ) + { + if ( pTable->Lower() ) + { + if ( pTable->IsFollow() ) + { + lcl_UpdateRepeatedHeadlines( *pTable, true ); + } + + OSL_ENSURE( static_cast<SwRowFrame*>(pTable->Lower())->GetTabLine() == + rTable.GetTabLines()[0], "MakeNewFrames: Table corruption!" ); + } + } +} + +bool FndBox_::AreLinesToRestore( const SwTable &rTable ) const +{ + // Should we call MakeFrames here? + + if ( !m_pLineBefore && !m_pLineBehind && !rTable.GetTabLines().empty() ) + return true; + + sal_uInt16 nBfPos; + if(m_pLineBefore) + { + const SwTableLine* rLBefore = const_cast<const SwTableLine*>(m_pLineBefore); + nBfPos = rTable.GetTabLines().GetPos( rLBefore ); + } + else + nBfPos = USHRT_MAX; + + sal_uInt16 nBhPos; + if(m_pLineBehind) + { + const SwTableLine* rLBehind = const_cast<const SwTableLine*>(m_pLineBehind); + nBhPos = rTable.GetTabLines().GetPos( rLBehind ); + } + else + nBhPos = USHRT_MAX; + + if ( nBfPos == nBhPos ) // Should never occur. + { + OSL_FAIL( "Table, erase but not on any area !?!" ); + return false; + } + + if ( rTable.GetRowsToRepeat() > 0 ) + { + // oops: should the repeated headline have been deleted?? + SwIterator<SwTabFrame,SwFormat> aIter( *rTable.GetFrameFormat() ); + for( SwTabFrame* pTable = aIter.First(); pTable; pTable = aIter.Next() ) + { + if( pTable->IsFollow() ) + { + // Insert new headlines: + lcl_UpdateRepeatedHeadlines( *pTable, false ); + } + } + } + + // Some adjacent lines at the beginning of the table have been deleted: + if ( nBfPos == USHRT_MAX && nBhPos == 0 ) + return false; + + // Some adjacent lines at the end of the table have been deleted: + if ( nBhPos == USHRT_MAX && nBfPos == (rTable.GetTabLines().size() - 1) ) + return false; + + // Some adjacent lines in the middle of the table have been deleted: + if ( nBfPos != USHRT_MAX && nBhPos != USHRT_MAX && (nBfPos + 1) == nBhPos ) + return false; + + // The structure of the deleted lines is more complex due to split lines. + // A call of MakeFrames() is necessary. + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |