summaryrefslogtreecommitdiffstats
path: root/sw/source/core/frmedt
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/frmedt
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/frmedt')
-rw-r--r--sw/source/core/frmedt/fecopy.cxx1625
-rw-r--r--sw/source/core/frmedt/fedesc.cxx245
-rw-r--r--sw/source/core/frmedt/fefly1.cxx2239
-rw-r--r--sw/source/core/frmedt/feflyole.cxx123
-rw-r--r--sw/source/core/frmedt/feshview.cxx3240
-rw-r--r--sw/source/core/frmedt/fetab.cxx2530
-rw-r--r--sw/source/core/frmedt/fews.cxx1347
-rw-r--r--sw/source/core/frmedt/tblsel.cxx2605
8 files changed, 13954 insertions, 0 deletions
diff --git a/sw/source/core/frmedt/fecopy.cxx b/sw/source/core/frmedt/fecopy.cxx
new file mode 100644
index 0000000000..1d8b0ef680
--- /dev/null
+++ b/sw/source/core/frmedt/fecopy.cxx
@@ -0,0 +1,1625 @@
+/* -*- 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.GetNode(),
+ rClpDoc.GetDfltTextFormatColl() );
+ --aSttIdx;
+ }
+
+ // also delete surrounding FlyFrames if any
+ for(sw::SpzFrameFormat* pFly : *rClpDoc.GetSpzFrameFormats() )
+ {
+ SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
+ SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ aSttIdx <= *pAnchorNode && *pAnchorNode <= aEndNdIdx.GetNode() )
+ {
+ 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, SwContentIndex( 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.SetContent( 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)
+ sw::SpzFrameFormats& rSpzFrameFormats = *rClpDoc.GetSpzFrameFormats();
+ if( rSpzFrameFormats[ 0 ] != pFlyFormat )
+ {
+#ifndef NDEBUG
+ bool inserted =
+#endif
+ rSpzFrameFormats.newDefault(static_cast<sw::SpzFrameFormat*>(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 TextAttribute should be removed in the node
+ // otherwise it will be recognised as TextSelection
+ const SwPosition& rPos = *pFlyFormat->GetAnchor().GetContentAnchor();
+ SwTextFlyCnt *const pTextFly = static_cast<SwTextFlyCnt *>(
+ pTextNd->GetTextAttrForCharAt(
+ rPos.GetContentIndex(), RES_TXTATR_FLYCNT));
+ if( pTextFly )
+ {
+ const_cast<SwFormatFlyCnt&>(pTextFly->GetFlyCnt()).SetFlyFormat();
+ pTextNd->EraseText( rPos, 1 );
+ }
+ }
+ }
+ else if ( IsObjSelected() )
+ {
+ SwPosition aPos( aSttIdx, 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 );
+
+ rtl::Reference<SdrObject> xNew = rClpDoc.CloneSdrObj( *pObj );
+
+ SwPaM aTemp(aPos);
+ rClpDoc.getIDocumentContentOperations().InsertDrawObj(aTemp, *xNew, 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()) )
+
+ {
+ rtl::Reference<SdrObject> xNew = pDestDoc->CloneSdrObj( *pObj, bIsMove &&
+ GetDoc() == pDestDoc, false );
+ xNew->NbcMove( aSiz );
+ pDestDrwView->InsertObjectAtView( xNew.get(), *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.GetNode())->IsNoTextNode() )
+ bRet = false;
+ else
+ bRet = ::lcl_SetAnchor( aPos, *pNd, nullptr, rInsPt,
+ rDestShell, aAnchor, aNewAnch, false );
+ }
+ else
+ {
+ SwPaM *pCursor = rDestShell.GetCursor();
+ if( pCursor->GetPointNode().IsNoTextNode() )
+ bRet = false;
+ else
+ bRet = ::lcl_SetAnchor( *pCursor->GetPoint(),
+ pCursor->GetPointNode(), 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 );
+ rtl::Reference<SdrObject> xNew = pDestDoc->CloneSdrObj( *pObj, bIsMove &&
+ GetDoc() == pDestDoc );
+ pFormat = pDestDoc->getIDocumentContentOperations().InsertDrawObj( *rDestShell.GetCursor(), *xNew, 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.GetNode())->IsNoTextNode() )
+ bRet = false;
+ else
+ {
+ // do not copy in itself
+ const SwNodeIndex *pTmp = pFlyFormat->GetContent().GetContentIdx();
+ if ( aPos.GetNodeIndex() > pTmp->GetIndex() &&
+ aPos.GetNodeIndex() < 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->GetPointNode().IsNoTextNode() )
+ bRet = false;
+ else
+ bRet = ::lcl_SetAnchor( *pCursor->GetPoint(), pCursor->GetPointNode(),
+ 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::optional<SwPosition> oDstPos;
+ if( this == &rDestShell )
+ {
+ // same shell? Then create new Cursor at the
+ // DocumentPosition passed
+ oDstPos.emplace( *GetCursor()->GetPoint() );
+ Point aPt( rInsPt );
+ GetLayout()->GetModelPositionForViewPoint( &*oDstPos, aPt );
+ if( !oDstPos->GetNode().IsNoTextNode() )
+ bRet = true;
+ }
+ else if( !rDestShell.GetCursor()->GetPointNode().IsNoTextNode() )
+ {
+ oDstPos.emplace( *rDestShell.GetCursor()->GetPoint() );
+ bRet = true;
+ }
+
+ if( bRet )
+ {
+ if( GetDoc() == rDestShell.GetDoc() )
+ ParkTableCursor();
+
+ bRet = rDestShell.GetDoc()->InsCopyOfTable( *oDstPos, aBoxes,nullptr,
+ bIsMove && this == &rDestShell &&
+ aBoxes.size() == pTableNd->GetTable().
+ GetTabSortBoxes().size(),
+ this != &rDestShell );
+
+ if( this != &rDestShell )
+ *rDestShell.GetCursor()->GetPoint() = *oDstPos;
+
+ // 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.GetNode().IsNoTextNode();
+ }
+ else if( rDestShell.GetCursor()->GetPointNode().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 SwNode* pAnchorNode = rAnchor.GetAnchorNode();
+ if (!pAnchorNode)
+ {
+ return false;
+ }
+
+ const SwStartNode* pFlyNode = pAnchorNode->FindFlyStartNode();
+ if (!pFlyNode)
+ {
+ return false;
+ }
+
+ for(const sw::SpzFrameFormat* 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)
+ {
+ rtl::Reference<SdrObject> xNew = 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 = xNew->GetSnapRect();
+ if(xNew->GetAnchorPos().X() || xNew->GetAnchorPos().Y())
+ {
+ const Point aPoint(0, 0);
+ // OD 2004-04-05 #i26791# - direct drawing object
+ // positioning for group members
+ xNew->NbcSetAnchorPos(aPoint);
+ xNew->NbcSetSnapRect(aSnapRect);
+ }
+
+ rDrawView.InsertObjectAtView(xNew.get(), *rImp.GetPageView());
+
+ Point aGrpAnchor(0, 0);
+ SdrObjList* pList = xNew->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
+ xNew->NbcSetAnchorPos(aGrpAnchor);
+ xNew->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->GetNode()))
+ {
+ 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.GetPointNode(), 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.GetPointNode().GetContentNode())
+ {
+ aCpyPam.GetPoint()->AssignEndIndex(*pAtEnd);
+ }
+
+ // 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.GetMarkNode().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()->GetNodeIndex()
+ - aCpyPam.GetMark()->GetNodeIndex();
+ 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()->Assign(aIdx);
+ else
+ aInsertion.first->GetPoint()->SetContent(
+ aInsertion.first->GetPointContentNode()->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.GetNode().FindTableBoxStartNode();
+ if( pBoxNd && SwNodeOffset(2) == pBoxNd->EndOfSectionIndex() - pBoxNd->GetIndex() &&
+ rCopy.GetPoint()->GetNode() != rCopy.GetMark()->GetNode() )
+ {
+ // if more than one node will be copied into a cell
+ // the box attributes have to be removed
+ GetDoc()->ClearBoxNumAttrs( rInsPos.GetNode() );
+ }
+ {
+ SwNodeIndex aIndexBefore(rInsPos.GetNode());
+ --aIndexBefore;
+ rClpDoc.getIDocumentContentOperations().CopyRange(rCopy, rInsPos, SwCopyFlags::CheckPosInFly);
+ {
+ ++aIndexBefore;
+ SwPaM aPaM(SwPosition(aIndexBefore),
+ SwPosition(rInsPos.GetNode()));
+ aPaM.GetDoc().MakeUniqueNumRules(aPaM);
+ }
+ }
+ SaveTableBoxContent( &rInsPos );
+ }
+ }
+ else
+ {
+ bool bDelTable = true;
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+
+ SwTableNode *const pDestNd(SwDoc::IsInTable(rPaM.GetPoint()->GetNode()));
+ 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.GetPointNode().GetIndex() == rPaM.GetPointNode().FindTableBoxStartNode()->GetIndex() + 1)
+ {
+ SwPosition aDestPos( *rPaM.GetPoint() );
+
+ bool bParkTableCursor = false;
+ const SwStartNode* pSttNd = rPaM.GetPointNode().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, 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, pCNd, 0 );
+ // #i59539: Don't remove all redline
+ SwNode & rNode(rPaM.GetPoint()->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 (sw::SpzFrameFormat* 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.GetAnchorNode()->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.GetNode().
+ FindTableBoxStartNode();
+ if( pBoxNd && SwNodeOffset(2) == pBoxNd->EndOfSectionIndex() -
+ pBoxNd->GetIndex() &&
+ aCpyPam.GetPoint()->GetNode() != aCpyPam.GetMark()->GetNode() )
+ {
+ // Copy more than 1 node in the current box. But
+ // then the BoxAttribute should be removed
+ GetDoc()->ClearBoxNumAttrs( rInsPos.GetNode() );
+ }
+
+ // **
+ // ** Update SwDoc::Append, if you change the following code **
+ // **
+ {
+ SwNodeIndex aIndexBefore(rInsPos.GetNode());
+
+ --aIndexBefore;
+
+ rClpDoc.getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CheckPosInFly);
+ // Note: aCpyPam is invalid now
+
+ ++aIndexBefore;
+ SwPaM aPaM(aIndexBefore.GetNode(), rInsPos.GetNode());
+
+ aPaM.GetDoc().MakeUniqueNumRules(aPaM);
+
+ // Update the rsid of each pasted text node.
+ SwNodes &rDestNodes = GetDoc()->GetNodes();
+ SwNodeOffset const nEndIdx = aPaM.End()->GetNodeIndex();
+
+ for (SwNodeOffset nIdx = aPaM.Start()->GetNodeIndex();
+ 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->GetPointNode().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
+ SwPaM aPara( rToFill.GetDoc()->GetNodes().GetEndOfExtras(), SwNodeOffset(2) ); //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(sw::SpzFrameFormat* 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
+ rtl::Reference<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
+ pObjList->ReplaceObject( pGraphicObj.get(), pOle2Obj->GetOrdNum() );
+ }
+ }
+ }
+}
+
+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;
+ }
+ }
+
+ rtl::Reference<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.get()) != 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.get());
+ }
+ }
+ 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(auto pGrafObj = dynamic_cast< SdrGrafObj* >(pObj))
+ {
+ rtl::Reference<SdrGrafObj> pNewGrafObj = SdrObject::Clone(*pGrafObj, pGrafObj->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.get());
+
+ // set in all cases - the Clone() will have copied an existing link (!)
+ pNewGrafObj->SetGraphicLink(rURL);
+
+ pResult = pNewGrafObj.get();
+ }
+ 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 0000000000..44720560ad
--- /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.GetPointContentNode() ) &&
+ nullptr != (pPtFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp)))
+ pPtFrame = pPtFrame->FindPageFrame();
+ else
+ pPtFrame = nullptr;
+
+ if( rPaM.HasMark() &&
+ nullptr != (pCNd = rPaM.GetMarkContentNode() ) &&
+ 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 0000000000..9e2fedcc38
--- /dev/null
+++ b/sw/source/core/frmedt/fefly1.cxx
@@ -0,0 +1,2239 @@
+/* -*- 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 <comphelper/scopeguard.hxx>
+#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.SetContent( 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.GetNode().GetContentNode()->getLayoutFrame(rFrame.getRootFrame(), nullptr, nullptr),
+ aTmpPnt )->FindFlyFrame();
+
+ if( pNewAnch && &rFrame != pNewAnch && !pNewAnch->IsProtected() )
+ {
+ aPos.Assign( *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.GetAnchorNode() )),
+ "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();
+}
+
+void SwFEShell::UnfloatFlyFrame()
+{
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::DELLAYFMT, nullptr);
+ comphelper::ScopeGuard g([this]
+ { GetIDocumentUndoRedo().EndUndo(SwUndoId::DELLAYFMT, nullptr); });
+
+ SwFlyFrame* pFly = GetSelectedFlyFrame();
+ if (!pFly)
+ {
+ return;
+ }
+
+ SwFrameFormat& rFlyFormat = pFly->GetFrameFormat();
+ const SwFormatContent& rContent = rFlyFormat.GetContent();
+ const SwNodeIndex* pFlyStart = rContent.GetContentIdx();
+ if (!pFlyStart)
+ {
+ return;
+ }
+
+ const SwEndNode* pFlyEnd = pFlyStart->GetNode().EndOfSectionNode();
+ if (!pFlyEnd)
+ {
+ return;
+ }
+
+ // Create an empty paragraph after the table, so the frame's SwNodes section is non-empty after
+ // MoveNodeRange(). Undo would ensure it's non-empty and then node offsets won't match.
+ IDocumentContentOperations& rIDCO = GetDoc()->getIDocumentContentOperations();
+ {
+ SwNodeIndex aInsertIndex(*pFlyEnd);
+ --aInsertIndex;
+ SwPosition aInsertPos(aInsertIndex);
+ StartAllAction();
+ rIDCO.AppendTextNode(aInsertPos);
+ // Make sure that a layout frame is created for the node, so the fly frame is not deleted,
+ // during MoveNodeRange(), either.
+ EndAllAction();
+ }
+
+ SwNodeRange aRange(pFlyStart->GetNode(), SwNodeOffset(1), *pFlyEnd, SwNodeOffset(-1));
+ const SwFormatAnchor& rAnchor = rFlyFormat.GetAnchor();
+ SwNode* pAnchor = rAnchor.GetAnchorNode();
+ if (!pAnchor)
+ {
+ return;
+ }
+
+ // Move the content outside of the text frame.
+ SwNodeIndex aInsertPos(*pAnchor);
+ rIDCO.MoveNodeRange(aRange, aInsertPos.GetNode(), SwMoveFlags::CREATEUNDOOBJ);
+
+ // Remove the fly frame frame.
+ IDocumentLayoutAccess& rIDLA = rFlyFormat.getIDocumentLayoutAccess();
+ rIDLA.DelLayoutFormat(&rFlyFormat);
+}
+
+// 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.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)
+ return aRet;
+
+ SdrObject* pObj = rMrkList.GetMark(0)->GetMarkedSdrObj();
+
+ if (!GetUserCall(pObj))
+ return aRet;
+
+ // #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));
+ 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.GetNode() != GetDoc()->GetNodes().GetEndOfExtras()
+ && (nAnchorId != RndStdIds::FLY_AT_CHAR || !PosInsideInputField(aPos)))
+ {
+ SwContentNode* pCNode = aPos.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(pTextFrame->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pTextFrame)->GetTextNodeForParaProps()
+ : *static_cast<const SwNoTextFrame*>(pTextFrame)->GetNode());
+ 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))
+ {
+ if (SdrObjList* pObjList = pObj->getChildrenOfSdrObject())
+ {
+ for (const rtl::Reference<SdrObject>& pChild : *pObjList)
+ SwTextBoxHelper::changeAnchor(&rFormat, pChild.get());
+ }
+ else
+ SwTextBoxHelper::syncFlyFrameAttr(
+ rFormat, rFormat.GetAttrSet(), pObj);
+ }
+ }
+ // #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( *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.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->GetAnchorNode() )
+ {
+ pAnchor->SetAnchor( pCursor->GetPoint() );
+ }
+ break;
+ case RndStdIds::FLY_AT_FLY:
+ if( !pAnchor->GetAnchorNode() )
+ {
+ lcl_SetNewFlyPos( pCursor->GetPointNode(),
+ *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() )
+ {
+ const SwPosition & rPos(*pFormat->GetAnchor().GetContentAnchor());
+ SwPaM aPaM(rPos.GetNode(), rPos.GetContentIndex(),
+ rPos.GetNode(), rPos.GetContentIndex() + 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.GetPointContentNode()->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->SetFormatName(rDrawObj.GetName());
+ // select drawing object
+ Imp()->GetDrawView()->MarkObj( &rDrawObj, Imp()->GetPageView() );
+ }
+ else
+ {
+ GetLayout()->SetAssertFlyPages();
+ }
+}
+
+void SwFEShell::GetPageObjs( std::vector<SwFrameFormat*>& rFillArr )
+{
+ rFillArr.clear();
+
+ for(sw::SpzFrameFormat* 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(static_cast<sw::SpzFrameFormat*>(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 )
+ sw::SpzFrameFormats& rSpzArr = *mxDoc->GetSpzFrameFormats();
+ if( !rSpzArr.empty() )
+ {
+ SwNode& rCursorNd = GetCursor()->GetPoint()->GetNode();
+ if( rCursorNd > mxDoc->GetNodes().GetEndOfExtras() )
+ // 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 < rCursorNd &&
+ rCursorNd < *pSttNd->EndOfSectionNode() )
+ {
+ // 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.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 );
+ }
+}
+
+bool SwFEShell::IsObjDecorative() const
+{
+ if (!Imp()->HasDrawView())
+ {
+ return false;
+ }
+
+ SdrMarkList const& rMarkList(Imp()->GetDrawView()->GetMarkedObjectList());
+ if (rMarkList.GetMarkCount() != 1)
+ {
+ return false;
+ }
+
+ SdrObject const*const pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
+ SwFrameFormat const*const pFormat(FindFrameFormat(pObj));
+ if (pFormat->Which() == RES_FLYFRMFMT)
+ {
+ return dynamic_cast<const SwFlyFrameFormat&>(*pFormat).GetAttrSet().Get(RES_DECORATIVE).GetValue();
+ }
+ return pObj->IsDecorative();
+}
+
+void SwFEShell::SetObjDecorative(bool const isDecorative)
+{
+ if (!Imp()->HasDrawView())
+ {
+ return;
+ }
+
+ SdrMarkList const& rMarkList(Imp()->GetDrawView()->GetMarkedObjectList());
+ if (rMarkList.GetMarkCount() != 1)
+ {
+ return;
+ }
+
+ SdrObject *const pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
+ SwFrameFormat *const pFormat(FindFrameFormat(pObj));
+ if (pFormat->Which() == RES_FLYFRMFMT)
+ {
+ GetDoc()->SetFlyFrameDecorative(dynamic_cast<SwFlyFrameFormat&>(*pFormat),
+ isDecorative);
+ }
+ else
+ {
+ pObj->SetDecorative(isDecorative);
+ }
+}
+
+
+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 = aIdx.GetNode().GetOLENode();
+ 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 0000000000..efcbaaa172
--- /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 0000000000..9a18e7934c
--- /dev/null
+++ b/sw/source/core/frmedt/feshview.cxx
@@ -0,0 +1,3240 @@
+/* -*- 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/constructhelper.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 <flyfrms.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;
+
+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, SwFrameFormat* pNewDrawFormat = nullptr)
+{
+ 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();
+ if (pNewDrawFormat)
+ {
+ // If we selected a draw shape format, move the cursor to its anchor position.
+ // SetCursor() may pick something inside, which is not wanted: code later assumes that
+ // the cursor is at the anchor point if a shape is selected.
+ const SwPosition* pContentAnchor = pNewDrawFormat->GetAnchor().GetContentAnchor();
+ if (pContentAnchor)
+ {
+ pSh->SetSelection(SwPaM(*pContentAnchor));
+ }
+ }
+ else
+ {
+ 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->IsFlySplitAllowed())
+ {
+ auto pMaster = static_cast<SwFlyAtContentFrame*>(pSelFly);
+ while (pMaster->IsFollow())
+ {
+ pMaster = pMaster->GetPrecede();
+ }
+ if (pMaster != pSelFly)
+ {
+ // A follow fly frame is selected, select the master instead. Selection of a follow
+ // would not be ideal, since one can't customize its vertical position (always
+ // starts at the top of the page).
+ pDView->UnmarkAll();
+ pDView->MarkObj(pMaster->DrawObj(), Imp()->GetPageView(), bAddSelect, bEnterGroup);
+ }
+ }
+ }
+
+ if ( rMrkList.GetMarkCount() == 1 )
+ {
+ SwFlyFrame *pSelFly = ::GetFlyFromMarked( &rMrkList, this );
+ if (pSelFly)
+ pSelFly->SelectionHasChanged(this);
+ }
+
+ SwFrameFormat* pNewDrawFormat = nullptr;
+ 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);
+ // Remember that this frame format was marked for selection.
+ pNewDrawFormat = pShapeFormat;
+ break;
+ }
+ }
+ }
+
+ if ( bRet )
+ {
+ ::lcl_GrabCursor(this, pOldSelFly, pNewDrawFormat);
+ 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()->Assign(*pCNode);
+ }
+
+ 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)
+{
+ ConstructHelper::SetLineEnds(rAttr, rObj, nSlotId, 100);
+}
+
+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"_ostr);
+
+ 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 SwNode& rAnchorNode = pContact->GetAnchorNode();
+ bool bInHdrFtr = GetDoc()->IsInHeaderFooter( rAnchorNode );
+ if (IsHeaderFooterEdit() != bInHdrFtr)
+ {
+ bRet = false;
+ }
+ }
+
+ if ( bRet )
+ {
+ const SdrPage* pPage = rIDDMA.GetDrawModel()->GetPage(0);
+ for(auto it = pPage->begin() + pObj->GetOrdNum() + 1; it != pPage->end(); ++it)
+ {
+ SdrObject *pCandidate = it->get();
+
+ SwVirtFlyDrawObj* pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pCandidate);
+ if (pDrawObj && pDrawObj->GetCurrentBoundRect().Contains(rPt))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ 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 (const rtl::Reference<SdrObject>& pChildObj : *pLst)
+ if( !::lcl_IsControlGroup( pChildObj.get() ) )
+ 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(LockPaintReason::EndSdrCreate);
+ 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.GetNode().IsProtect() )
+ {
+ std::pair<Point, bool> const tmp(aPoint, true);
+ pAnch = aPos.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.GetNode() );
+
+ 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.GetNode().IsProtect())
+ {
+ // then only page bound. Or should we
+ // search the next not-readonly position?
+ bAtPage = true;
+ }
+
+ SwContentNode* pCNode = aPos.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.Assign( *pos.first );
+ }
+ else
+ {
+ aPos.Assign( *static_cast<const SwNoTextFrame*>(pAnch)->GetNode() );
+ }
+
+ // do not set in ReadnOnly-content
+ if( aPos.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::NewFrame)
+ {
+ // 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();
+ pPg->RemoveObject( rSdrObj.GetOrdNumDirect() );
+ 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->SetFormatName(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.GetAnchorNode()->GetTextNode();
+ SwFormatFlyCnt aFormat( pFormat );
+ pNd->InsertItem(aFormat,
+ aAnch.GetAnchorContentOffset(), 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()->Assign(*pCNode);
+ }
+
+ 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& rDrawModel = pDrawView->GetModel();
+ rtl::Reference<SdrObject> pObj = SdrObjFactory::MakeNewObject(
+ rDrawModel,
+ 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.get()) != nullptr)
+ {
+ SfxItemSet aAttr(rDrawModel.GetItemPool());
+ aAttr.Put(makeSdrCircStartAngleItem(9000_deg100));
+ aAttr.Put(makeSdrCircEndAngleItem(0_deg100));
+ pObj->SetMergedItemSet(aAttr);
+ }
+ else if(auto pPathObj = dynamic_cast<SdrPathObj*>( pObj.get()))
+ {
+ 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.get()))
+ {
+ 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.get()))
+ {
+ 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 = DynCastSdrTextObj( pObj.get()))
+ {
+ pText->SetLogicRect(aRect);
+
+ bool bVertical = (SID_DRAW_TEXT_VERTICAL == nSlotId);
+ bool bMarquee = (SID_DRAW_TEXT_MARQUEE == nSlotId);
+
+ pText->SetVerticalWriting(bVertical);
+
+ if(bVertical)
+ {
+ SfxItemSet aSet(rDrawModel.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(rDrawModel.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.get());
+ // 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.get(), *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
+{
+ // check, if a draw view exists
+ OSL_ENSURE(Imp()->GetDrawView(), "wrong usage of SwFEShell::GetShapeBackground - no draw view!");
+ if (!Imp()->GetDrawView())
+ return false;
+
+ // determine list of selected objects
+ const SdrMarkList& rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+
+ // check, if exactly one object is selected.
+ OSL_ENSURE(rMrkList.GetMarkCount() == 1, "wrong usage of SwFEShell::GetShapeBackground - no selected object!");
+ if (rMrkList.GetMarkCount() != 1)
+ return false;
+
+ // get selected object
+ const SdrObject *pSdrObj = rMrkList.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)
+ return false;
+
+ // determine page frame of the frame the shape is anchored.
+ const SwContact* pContact = GetUserCall(pSdrObj);
+ OSL_ENSURE(pContact, "<SwFEShell::IsShapeDefaultHoriTextDirR2L(..)> - missing contact!");
+ if (!pContact)
+ return false;
+
+ const SwFrame* pAnchorFrame = static_cast<const SwDrawContact*>(pContact)->GetAnchorFrame(pSdrObj);
+ OSL_ENSURE(pAnchorFrame, "inconsistent model - no anchor at shape!");
+ if (!pAnchorFrame)
+ return false;
+
+ const SwPageFrame* pPageFrame = pAnchorFrame->FindPageFrame();
+ OSL_ENSURE(pPageFrame, "inconsistent model - no page!");
+ if (!pPageFrame)
+ return false;
+
+ return pPageFrame->IsRightToLeft();
+}
+
+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 0000000000..79f5eb6b5e
--- /dev/null
+++ b/sw/source/core/frmedt/fetab.cxx
@@ -0,0 +1,2530 @@
+/* -*- 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 <IDocumentRedlineAccess.hxx>
+#include <IDocumentUndoRedo.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;
+ }
+
+ // pending drag & drop?
+ bool bAction = ActionPend();
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ // search boxes via the layout
+ SwSelBoxes aBoxes;
+ bool bSelectAll = StartsWith_() == StartsWith::Table && 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()->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()->Assign( pNode->GetIndex() - 2 );
+ }
+ GetTableSel( *this, aBoxes, SwTableSearchType::Row );
+
+ TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
+
+ if ( !aBoxes.empty() )
+ GetDoc()->InsertRow( aBoxes, nCnt, bBehind, /*bInsertDummy=*/!bAction );
+
+ 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;
+ }
+
+ // pending drag & drop?
+ bool bAction = ActionPend();
+
+ 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, /*bInsertDummy=*/!bAction );
+
+ 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 );
+
+ bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording();
+ bool bRecordAndHideChanges = bRecordChanges &&
+ GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines();
+
+ // tracked deletion: remove only textbox content,
+ // and set IsNoTracked table box property to false
+ if ( bRecordChanges )
+ {
+ StartUndo(SwUndoId::COL_DELETE);
+ StartAllAction();
+
+ if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
+ pWrtShell->SelectTableCol();
+
+ // search boxes via the layout
+ SwSelBoxes aBoxes;
+ GetTableSel( *this, aBoxes, SwTableSearchType::Col );
+
+ TableWait aWait( 20, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
+
+ SwTableNode* pTableNd = pFrame->IsTextFrame()
+ ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode()
+ : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode();
+
+ for (size_t i = 0; i < aBoxes.size(); ++i)
+ {
+ SwTableBox *pBox = aBoxes[i];
+ if ( pBox->GetSttNd() )
+ {
+ SwNodeIndex aIdx( *pBox->GetSttNd(), 1 );
+ SwCursor aCursor( SwPosition(aIdx), nullptr );
+ SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
+ GetDoc()->SetBoxAttr( aCursor, aHasTextChangesOnly );
+
+ // add dummy text content to the empty box for change tracking
+ if ( pBox->IsEmpty() )
+ {
+ IDocumentContentOperations& rIDCO = GetDoc()->getIDocumentContentOperations();
+ IDocumentRedlineAccess& rIDRA = GetDoc()->getIDocumentRedlineAccess();
+ RedlineFlags eOld = rIDRA.GetRedlineFlags();
+ rIDRA.SetRedlineFlags_intern(RedlineFlags::NONE);
+ rIDCO.InsertString( aCursor, OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+ aCursor.SetMark();
+ aCursor.GetMark()->SetContent(0);
+ rIDRA.SetRedlineFlags_intern( eOld );
+ rIDCO.DeleteAndJoin( aCursor );
+ }
+
+ }
+ }
+
+ SwEditShell* pEditShell = GetDoc()->GetEditShell();
+ pEditShell->Delete();
+
+ // remove cell 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() )
+ {
+ pTableNd->MakeOwnFrames();
+ }
+ }
+
+ EndAllActionAndCall();
+ EndUndo(SwUndoId::COL_DELETE);
+ return true;
+ }
+
+ StartAllAction();
+
+ // search boxes via the layout
+ bool bRet;
+ SwSelBoxes aBoxes;
+ SwTableSearchType eSearchType = SwTableSearchType::Col;
+
+ // NewModel tables already ExpandColumnSelection, so don't do it here also.
+ const SwContentNode* pContentNd = getShellCursor(false)->GetPointNode().GetContentNode();
+ const SwTableNode* pTableNd = pContentNd ? pContentNd->FindTableNode() : nullptr;
+ if (pTableNd && pTableNd->GetTable().IsNewModel())
+ eSearchType = SwTableSearchType::NONE;
+
+ GetTableSel(*this, aBoxes, eSearchType);
+ 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 HasTextChangesOnly table line property to false
+ SwEditShell* pEditShell = nullptr;
+ if ( bRecordChanges )
+ {
+ pEditShell = GetDoc()->GetEditShell();
+ SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
+ GetDoc()->SetRowNotTracked( *getShellCursor( false ), aHasTextChangesOnly );
+
+ if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
+ pWrtShell->SelectTableRow();
+
+ // don't need to remove the row frames in Show Changes mode
+ if ( !bRecordAndHideChanges )
+ {
+ if ( pEditShell )
+ 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 ( pEditShell && bRecordAndHideChanges )
+ {
+ // select the row deleted with change tracking cell by cell to handle
+ // the already deleted cells
+ SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this);
+ for (SwSelBoxes::size_type nBox = 0; pWrtShell && nBox < aBoxes.size(); ++nBox)
+ {
+ pWrtShell->SelectTableRow();
+ SwCursor* pTableCursor = static_cast<SwCursor*>(GetTableCursor());
+ auto pStt = aBoxes[nBox];
+ if ( !pTableCursor )
+ pTableCursor = GetCursor(true);
+
+ if ( pTableCursor )
+ {
+ // set start and end of the selection
+ pTableCursor->DeleteMark();
+ pTableCursor->GetPoint()->Assign( *pStt->GetSttNd()->EndOfSectionNode() );
+ pTableCursor->Move( fnMoveBackward, GoInContent );
+ pWrtShell->UpdateCursor();
+ }
+
+ 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() )
+ {
+ pTableNd->MakeOwnFrames();
+ }
+
+ EndAllActionAndCall();
+
+ // put cursor
+ SwPaM* pPam = GetCursor();
+ pPam->GetPoint()->Assign( *pCNd, 0 );
+ pPam->SetMark(); // both want something
+ pPam->DeleteMark();
+ if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
+ {
+ pWrtShell->UpdateCursor();
+ // tdf#150578 enable the disabled table toolbar by (zero) cursor moving
+ pWrtShell->Right( SwCursorSkipMode::Chars, false, 0, false );
+ }
+
+ EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
+ return true;
+ }
+ else if( pCNd )
+ {
+ // put cursor
+ SwPaM* pPam = GetCursor();
+ pPam->GetPoint()->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->GetPointNode().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()->GetNodeIndex() -1 ==
+ ( pNd = &pPam->GetPointNode())->StartOfSectionIndex() &&
+ !pPam->GetPoint()->GetContentIndex() &&
+ pPam->GetMark()->GetNodeIndex() + 1 ==
+ pNd->EndOfSectionIndex())
+ {
+ SwNodeIndex aIdx( *pNd->EndOfSectionNode(), -1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ {
+ pCNd = SwNodes::GoPrevious( &aIdx );
+ assert(pCNd && "no ContentNode in box ??");
+ }
+ if( pPam->GetMark()->GetContentIndex() == 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 {
+ if ( pFrame )
+ 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.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( *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]->GetNode().StartOfSectionNode() !=
+ aOldPos.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 0000000000..bce8e2bb97
--- /dev/null
+++ b/sw/source/core/frmedt/fews.cxx
@@ -0,0 +1,1347 @@
+/* -*- 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>
+#include <formatflysplit.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.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.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
+ SwTabFrame* pTabFrame = pCnt->FindTabFrame();
+ const SwTable& rTable = *pTabFrame->GetTable();
+ nIdx = rTable.GetTabSortBoxes()[ 0 ]
+ ->GetSttNd()->FindTableNode()->GetIndex();
+
+ SwFlyFrame* pFly = pTabFrame->FindFlyFrame();
+ if (pFly && pFly->IsFlySplitAllowed())
+ {
+ // This table is in a split fly, but we will insert a label, which means this is not
+ // a floating table anymore, disable the "can split" bit, it'll be hidden on the UI
+ // anyway.
+ SwFrameFormat& rFlyFormat = pFly->GetFrameFormat();
+ SfxItemSetFixed<RES_FLY_SPLIT, RES_FLY_SPLIT> aSet(GetDoc()->GetAttrPool());
+ SwFormatFlySplit aSplit(false);
+ aSet.Put(aSplit);
+ // SwUndoFormatAttr is created for us.
+ GetDoc()->SetFlyFrameAttr(rFlyFormat, aSet);
+ }
+ }
+ 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;
+
+ auto [pStart, pEnd] = pPam->StartEnd(); // SwPosition*
+
+ SwNodeIndex aPrevIdx( pStart->GetNode(), -1 );
+ SwNodeOffset nOffset = pEnd->GetNodeIndex() - pStart->GetNodeIndex();
+ const sal_Int32 nCntStt = pStart->GetContentIndex();
+
+ // Sorting
+ bRet = mxDoc->SortText(*pPam, rOpt);
+
+ // put selection again
+ pPam->DeleteMark();
+ pPam->GetPoint()->Assign( aPrevIdx.GetNode(), SwNodeOffset(+1) );
+ SwContentNode* pCNd = pPam->GetPointContentNode();
+ sal_Int32 nLen = pCNd->Len();
+ if( nLen > nCntStt )
+ nLen = nCntStt;
+ pPam->GetPoint()->SetContent(nLen );
+ pPam->SetMark();
+
+ pPam->GetPoint()->Adjust(nOffset);
+ pCNd = pPam->GetPointContentNode();
+ pPam->GetPoint()->SetContent( 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 SwFormatAnchor* _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 = GetSelectedOrCurrFlyFrame();
+ 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->GetContentAnchor() );
+ }
+ 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->GetContentAnchor() );
+ }
+ 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->GetContentAnchor() );
+ }
+ 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 0000000000..ec56a52856
--- /dev/null
+++ b/sw/source/core/frmedt/tblsel.cxx
@@ -0,0 +1,2605 @@
+/* -*- 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.GetPointContentNode() && rCursor.GetMarkContentNode(),
+ "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.GetPointNode().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.GetMarkNode();
+ 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.GetPointNode().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
+ std::swap( nSttPos, nEndPos );
+
+ 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.GetPointContentNode();
+ std::pair<Point, bool> tmp(aPtPos, true);
+ const SwLayoutFrame *pStart = pCntNd ?
+ pCntNd->getLayoutFrame(pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)->GetUpper() : nullptr;
+ pCntNd = rCursor.GetMarkContentNode();
+ 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->GetPointContentNode()->getLayoutFrame(
+ rShell.GetLayout(), nullptr, &tmp)->GetUpper();
+ tmp.first = pCursor->GetMkPos();
+ const SwLayoutFrame *const pEnd = pCursor->GetMarkContentNode()->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()->Assign( *rBox.GetSttNd()->EndOfSectionNode() );
+ rPam.Move( fnMoveBackward, GoInContent );
+ rPam.SetMark();
+ rPam.GetPoint()->Assign( *rBox.GetSttNd() );
+ rPam.Move( fnMoveForward, GoInContent );
+ bool bRet = *rPam.GetMark() == *rPam.GetPoint()
+ && ( rBox.GetSttNd()->GetIndex() + 1 == rPam.GetPoint()->GetNodeIndex() );
+
+ if( bRet )
+ {
+ // now check for paragraph bound flies
+ const sw::SpzFrameFormats& rFormats = *rPam.GetDoc().GetSpzFrameFormats();
+ SwNodeOffset nSttIdx = rPam.GetPoint()->GetNodeIndex(),
+ nEndIdx = rBox.GetSttNd()->EndOfSectionIndex(),
+ nIdx;
+
+ for( auto pFormat : rFormats )
+ {
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
+ if (pAnchorNode &&
+ ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
+ nSttIdx <= ( nIdx = pAnchorNode->GetIndex() ) &&
+ nIdx < nEndIdx )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ return bRet;
+}
+
+void GetMergeSel( const SwPaM& rPam, SwSelBoxes& rBoxes,
+ SwTableBox** ppMergeBox, SwUndoTableMerge* pUndo )
+{
+ rBoxes.clear();
+
+ OSL_ENSURE( rPam.GetPointContentNode() && rPam.GetMarkContentNode(),
+ "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.GetPointContentNode();
+ std::pair<Point, bool> const tmp(aPt, true);
+ const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ pCntNd = rPam.GetMarkContentNode();
+ 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() );
+
+ SwPaM aPam( aInsPos );
+
+ for( const auto &rPt : aPosArr )
+ {
+ aPam.GetPoint()->Assign( *rPt.pSelBox->GetSttNd()->
+ EndOfSectionNode(), SwNodeOffset(-1) );
+ SwContentNode* pCNd = aPam.GetPointContentNode();
+ if( pCNd )
+ aPam.GetPoint()->SetContent( pCNd->Len() );
+
+ 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.GetNode(), aPam.GetPoint()->GetNode() );
+ aInsPos.Adjust(SwNodeOffset(1));
+ if( pUndo )
+ pUndo->MoveBoxContent( *pDoc, aRg, aInsPos.GetNode() );
+ else
+ {
+ pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aInsPos.GetNode(),
+ SwMoveFlags::DEFAULT );
+ }
+ // where is now aInsPos ??
+
+ if( bCalcWidth )
+ bCalcWidth = false; // one line is ready
+
+ // skip the first TextNode
+ aInsPos.Assign( *pDoc->GetNodes()[ aInsPos.GetNode().EndOfSectionIndex() - 2] );
+ SwTextNode* pTextNd = aInsPos.GetNode().GetTextNode();
+ if( pTextNd )
+ aInsPos.SetContent( 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.GetPointContentNode();
+ std::pair<Point, bool> tmp(aPt, true);
+ const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ pCntNd = rPam.GetMarkContentNode();
+ 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 )
+ std::swap( nSt1, nEd1 );
+ if( nSt2 > nEd2 )
+ std::swap( nSt2, nEd2 );
+ 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.GetPointContentNode();
+ std::pair<Point, bool> tmp(aPtPos, true);
+ const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ pCntNd = rCursor.GetMarkContentNode();
+ 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) ) &&
+ // tdf#159026 fix Undo crash with floating tables
+ pUpperFrame->GetUpper() )
+ ::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: */