/* -*- 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 .
*/
// belongs the range in the text ? insert it then. void SAL_CALL
SwXText::insertString(const uno::Reference< text::XTextRange >& xTextRange, const OUString& rString, sal_Bool bAbsorb)
{
SolarMutexGuard aGuard;
comphelper::ProfileZone aZone("SwXText::insertString");
if (bAbsorb && aPam.HasMark())
{
m_pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
aPam.DeleteMark();
}
sal_Unicode cIns = 0; switch (nControlCharacter)
{ case text::ControlCharacter::PARAGRAPH_BREAK : // a table cell now becomes an ordinary text cell!
m_pDoc->ClearBoxNumAttrs(aPam.GetPoint()->GetNode());
m_pDoc->getIDocumentContentOperations().SplitNode(*aPam.GetPoint(), false); break; case text::ControlCharacter::APPEND_PARAGRAPH:
{
m_pDoc->ClearBoxNumAttrs(aPam.GetPoint()->GetNode());
m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPam.GetPoint());
// first test if the range is at the right position, then call // xContent->attach const SwStartNode* pOwnStartNode = GetStartNode();
SwStartNodeType eSearchNodeType = SwNormalStartNode; switch (m_eType)
{ case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break; case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break; case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break; case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break; case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break; //case CURSOR_INVALID: //case CursorType::Body: default: break;
}
// ignore SectionNodes while (pTmp && pTmp->IsSectionNode())
{
pTmp = pTmp->StartOfSectionNode();
} // if the document starts with a section while (pOwnStartNode && pOwnStartNode->IsSectionNode())
{
pOwnStartNode = pOwnStartNode->StartOfSectionNode();
} // this checks if (this) and xRange are in the same text::XText interface if (pOwnStartNode != pTmp) throw uno::RuntimeException(u"text interface and cursor not related"_ustr);
GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr); //insert an empty paragraph at the start and at the end to ensure that //all tables and sections can be removed by the selecting text::XTextCursor if (CursorType::Meta != m_eType)
{
SwPosition aStartPos(*pStartNode); const SwEndNode* pEnd = pStartNode->EndOfSectionNode();
SwNodeIndex aEndIdx(*pEnd);
--aEndIdx; //the inserting of nodes should only be done if really necessary //to prevent #97924# (removes paragraph attributes when setting the text //e.g. of a table cell bool bInsertNodes = false;
SwNodeIndex aStartIdx(*pStartNode); do
{
++aStartIdx;
SwNode& rCurrentNode = aStartIdx.GetNode(); if(rCurrentNode.GetNodeType() == SwNodeType::Section
||rCurrentNode.GetNodeType() == SwNodeType::Table)
{
bInsertNodes = true; break;
}
} while(aStartIdx < aEndIdx); if(bInsertNodes)
{
GetDoc()->getIDocumentContentOperations().AppendTextNode( aStartPos );
SwPaM aPam(aEndIdx.GetNode());
GetDoc()->getIDocumentContentOperations().AppendTextNode( *aPam.Start() );
}
}
//FIXME why is CheckForOwnMember duplicated in some insert methods? // Description: Checks if pRange/pCursor are member of the same text interface. // Only one of the pointers has to be set! bool SwXText::CheckForOwnMember(const SwPaM & rPaM)
{ const rtl::Reference< SwXTextCursor > xOwnCursor(createXTextCursor()); const SwStartNode* pOwnStartNode =
xOwnCursor->GetPaM()->GetPointNode().StartOfSectionNode();
SwStartNodeType eSearchNodeType = SwNormalStartNode; switch (m_eType)
{ case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break; case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break; case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break; case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break; case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break; //case CURSOR_INVALID: //case CursorType::Body: default:
;
}
if (!::sw::XTextRangeToSwPaM(aPam1, xPos1) ||
!::sw::XTextRangeToSwPaM(aPam2, xPos2))
{ throw lang::IllegalArgumentException();
} // The UNO API documentation for this method says we have to throw // if either aPam1 or aPam2 is not within this text, and some // extensions rely on that behaviour. if (!CheckForOwnMember(aPam1) || !CheckForOwnMember(aPam2))
{ throw lang::IllegalArgumentException();
}
rtl::Reference<SwXParagraph> xRet; bool bIllegalException = false; bool bRuntimeException = false;
OUString sMessage;
m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::START , nullptr); // find end node, go backward - don't skip tables because the new // paragraph has to be the last node //aPam.Move( fnMoveBackward, GoInNode );
SwPaM aPam(*pStartNode->EndOfSectionNode(), SwNodeOffset(-1)); // If we got a position reference, then the insert point is not the end of // the document. if (xInsertPosition.is())
{
SwUnoInternalPaM aStartPam(*GetDoc());
::sw::XTextRangeToSwPaM(aStartPam, xInsertPosition);
aPam = aStartPam;
aPam.SetMark();
}
m_pDoc->getIDocumentContentOperations().AppendTextNode( *aPam.GetPoint() ); // remove attributes from the previous paragraph
m_pDoc->ResetAttrs(aPam); // in case of finishParagraph the PaM needs to be moved to the // previous paragraph
aPam.Move( fnMoveBackward, GoInNode );
// Append text portions at the end of the last paragraph of the text interface. // Support of import filters.
uno::Reference< text::XTextRange > SAL_CALL
SwXText::appendTextPortion( const OUString& rText, const uno::Sequence< beans::PropertyValue > &
rCharacterAndParagraphProperties)
{
SolarMutexGuard aGuard;
rtl::Reference< SwXTextCursor > xInsertPosition = getEndImpl(aGuard);
rtl::Reference< SwXTextRange > xRange = insertTextPortionImpl(aGuard, rText, rCharacterAndParagraphProperties, xInsertPosition); return xRange;
}
// enable inserting/appending text contents like graphic objects, shapes and so on to // support import filters
uno::Reference< text::XTextRange > SAL_CALL
SwXText::insertTextContentWithProperties( const uno::Reference< text::XTextContent >& xTextContent, const uno::Sequence< beans::PropertyValue >&
rCharacterAndParagraphProperties, const uno::Reference< text::XTextRange >& xInsertPosition)
{
SolarMutexGuard aGuard;
if (!IsValid())
{ throw uno::RuntimeException();
}
// Any direct formatting ending at the insert position (xRange) should not // be expanded to cover the inserted content (xContent) // (insertTextContent() shouldn't do this, only ...WithProperties()!)
GetDoc()->DontExpandFormat( *aPam.Start() );
// now attach the text content here
insertTextContent( xInsertPosition, xTextContent, false ); // now apply the properties to the anchor if (rCharacterAndParagraphProperties.hasElements())
{ try
{ const uno::Reference< beans::XPropertySet > xAnchor(
xTextContent->getAnchor(), uno::UNO_QUERY); if (xAnchor.is())
{ for (constauto& rProperty : rCharacterAndParagraphProperties)
{
xAnchor->setPropertyValue(rProperty.Name, rProperty.Value);
}
}
} catch (const uno::Exception& e)
{
css::uno::Any anyEx = cppu::getCaughtException();
m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, &aRewriter); throw lang::WrappedTargetRuntimeException( e.Message,
uno::Reference< uno::XInterface >(), anyEx );
}
}
m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, &aRewriter); return xInsertPosition;
}
uno::Reference< text::XTextRange > SAL_CALL
SwXText::appendTextContent( const uno::Reference< text::XTextContent >& xTextContent, const uno::Sequence< beans::PropertyValue >& rCharacterAndParagraphProperties
)
{ // Right now this doesn't need a guard, as it's just calling the insert // version, that has it already.
uno::Reference<text::XTextRange> xInsertPosition = getEnd(); return insertTextContentWithProperties(xTextContent, rCharacterAndParagraphProperties, xInsertPosition);
}
// determine whether SwFrameFormat is a graphic node staticbool isGraphicNode(const SwFrameFormat* pFrameFormat)
{ // safety if( !pFrameFormat->GetContent().GetContentIdx() )
{ returnfalse;
} auto index = *pFrameFormat->GetContent().GetContentIdx(); // consider the next node -> there is the graphic stored
++index; return index.GetNode().IsGrfNode();
}
/// Determines if the at-para rAnchor is anchored at the start or end of rAnchorCheckPam. staticbool IsAtParaMatch(const SwPaM& rAnchorCheckPam, const SwFormatAnchor& rAnchor)
{ if (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PARA)
{ returnfalse;
}
if (rAnchorCheckPam.Start()->GetNode() == *rAnchor.GetAnchorNode())
{ returntrue;
}
if (rAnchorCheckPam.End()->GetNode() == *rAnchor.GetAnchorNode())
{
SwTextNode* pEndTextNode = rAnchorCheckPam.End()->GetNode().GetTextNode(); if (pEndTextNode && rAnchorCheckPam.End()->GetContentIndex() == pEndTextNode->Len())
{ // rAnchorCheckPam covers the entire last text node, rAnchor is at-para, consider this // as "inside pam" rather than "at the end of pam". returnfalse;
} returntrue;
}
returnfalse;
}
// move previously appended paragraphs into a text frames // to support import filters
uno::Reference< text::XTextContent > SAL_CALL
SwXText::convertToTextFrame( const uno::Reference< text::XTextRange >& xStart, const uno::Reference< text::XTextRange >& xEnd, const uno::Sequence< beans::PropertyValue >& rFrameProperties)
{
SolarMutexGuard aGuard;
if(!IsValid())
{ throw uno::RuntimeException();
} // tdf#143384 recognize dummy property, that was set to make createTextCursor // to not ignore tables. // It is enough to use this hack only for the range start, // because as far as I know, the range cannot end with table when this property is set.
::sw::TextRangeMode eMode = ::sw::TextRangeMode::RequireTextNode; for (constauto& rCellProperty : rFrameProperties)
{ if (rCellProperty.Name == "CursorNotIgnoreTables")
{ bool bAllowNonTextNode = false;
rCellProperty.Value >>= bAllowNonTextNode; if (bAllowNonTextNode)
eMode = ::sw::TextRangeMode::AllowTableNode; break;
}
}
uno::Reference< text::XTextContent > xRet;
std::optional<SwUnoInternalPaM> pTempStartPam(*GetDoc());
std::optional<SwUnoInternalPaM> pEndPam(*GetDoc()); if (!::sw::XTextRangeToSwPaM(*pTempStartPam, xStart, eMode)
|| !::sw::XTextRangeToSwPaM(*pEndPam, xEnd))
{ throw lang::IllegalArgumentException();
}
auto pStartPam(GetDoc()->CreateUnoCursor(*pTempStartPam->GetPoint())); if (pTempStartPam->HasMark())
{
pStartPam->SetMark();
*pStartPam->GetMark() = *pTempStartPam->GetMark();
}
pTempStartPam.reset();
SwXTextRange *const pStartRange = dynamic_cast<SwXTextRange*>(xStart.get());
SwXTextRange *const pEndRange = dynamic_cast<SwXTextRange*>(xEnd.get()); // bookmarks have to be removed before the referenced text node // is deleted in DelFullPara if (pStartRange)
{
pStartRange->Invalidate();
} if (pEndRange)
{
pEndRange->Invalidate();
}
m_pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr ); bool bIllegalException = false; bool bRuntimeException = false;
OUString sMessage;
SwStartNode* pStartStartNode = pStartPam->GetPointNode().StartOfSectionNode(); while (pStartStartNode && pStartStartNode->IsSectionNode())
{
pStartStartNode = pStartStartNode->StartOfSectionNode();
}
SwStartNode* pEndStartNode = pEndPam->GetPointNode().StartOfSectionNode(); while (pEndStartNode && pEndStartNode->IsSectionNode())
{
pEndStartNode = pEndStartNode->StartOfSectionNode();
} bool bParaAfterInserted = false; bool bParaBeforeInserted = false;
::std::optional<SwPaM> oAnchorCheckPam;
oAnchorCheckPam.emplace(*pStartPam->Start(), *pEndPam->End()); if (
pStartStartNode && pEndStartNode &&
(pStartStartNode != pEndStartNode || pStartStartNode != GetStartNode())
)
{ // todo: if the start/end is in a table then insert a paragraph // before/after, move the start/end nodes, then convert and // remove the additional paragraphs in the end
SwTableNode * pStartTableNode(nullptr); if (pStartStartNode->GetStartNodeType() == SwTableBoxStartNode)
{
pStartTableNode = pStartStartNode->FindTableNode(); // Is it the same table start node than the end?
SwTableNode *const pEndStartTableNode(pEndStartNode->FindTableNode()); while (pEndStartTableNode && pStartTableNode &&
pEndStartTableNode->GetIndex() < pStartTableNode->GetIndex())
{
SwStartNode* pStartStartTableNode = pStartTableNode->StartOfSectionNode();
pStartTableNode = pStartStartTableNode->FindTableNode();
}
} if (pStartTableNode)
{ const SwNodeIndex aTableIdx( *pStartTableNode, -1 );
SwPosition aBefore(aTableIdx);
bParaBeforeInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
pStartPam->DeleteMark();
*pStartPam->GetPoint() = std::move(aBefore);
pStartStartNode = pStartPam->GetPointNode().StartOfSectionNode();
} if (pEndStartNode->GetStartNodeType() == SwTableBoxStartNode)
{
SwTableNode *const pEndTableNode = pEndStartNode->FindTableNode();
SwEndNode *const pTableEnd = pEndTableNode->EndOfSectionNode();
SwPosition aTableEnd(*pTableEnd);
bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aTableEnd );
pEndPam->DeleteMark();
*pEndPam->GetPoint() = std::move(aTableEnd);
pEndStartNode = pEndPam->GetPointNode().StartOfSectionNode();
} // now we should have the positions in the same hierarchy if ((pStartStartNode != pEndStartNode) ||
(pStartStartNode != GetStartNode()))
{ // if not - remove the additional paragraphs and throw
oAnchorCheckPam.reset(); // clear SwContentIndex before deleting nodes if (bParaBeforeInserted)
{
SwCursor aDelete(*pStartPam->GetPoint(), nullptr);
*pStartPam->GetPoint() = // park it because node is deleted
SwPosition(GetDoc()->GetNodes().GetEndOfContent());
aDelete.MovePara(GoCurrPara, fnParaStart);
aDelete.SetMark();
aDelete.MovePara(GoCurrPara, fnParaEnd);
GetDoc()->getIDocumentContentOperations().DelFullPara(aDelete);
} if (bParaAfterInserted)
{
SwCursor aDelete(*pEndPam->GetPoint(), nullptr);
*pEndPam->GetPoint() = // park it because node is deleted
SwPosition(GetDoc()->GetNodes().GetEndOfContent());
aDelete.MovePara(GoCurrPara, fnParaStart);
aDelete.SetMark();
aDelete.MovePara(GoCurrPara, fnParaEnd);
GetDoc()->getIDocumentContentOperations().DelFullPara(aDelete);
} throw lang::IllegalArgumentException();
}
}
// make a selection from pStartPam to pEndPam // If there is no content in the frame the shape is in // it gets deleted in the DelFullPara call below, // In this case insert a tmp text node ( we delete it later ) if (pStartPam->Start()->GetNode() == pEndPam->Start()->GetNode()
&& pStartPam->End()->GetNode() == pEndPam->End()->GetNode())
{
SwPosition aEnd(*pStartPam->End());
bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd );
pEndPam->DeleteMark();
*pEndPam->GetPoint() = aEnd;
*oAnchorCheckPam->End() = std::move(aEnd);
}
pStartPam->SetMark();
*pStartPam->End() = *pEndPam->End();
pEndPam.reset();
// see if there are frames already anchored to this node // we have to work with the SdrObjects, as unique name is not guaranteed in their frame format // tdf#115094: do nothing if we have a graphic node
o3tl::sorted_vector<const SdrObject*> aAnchoredObjectsByPtr;
std::set<UIName> aAnchoredObjectsByName; for (size_t i = 0; i < m_pDoc->GetSpzFrameFormats()->size(); ++i)
{ const SwFrameFormat* pFrameFormat = (*m_pDoc->GetSpzFrameFormats())[i]; const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor(); // note: Word can do at-char anchors in text frames - sometimes! // see testFlyInFly for why this checks only the edges of the selection, // and testFloatingTablesAnchor for why it excludes pre/post table // added nodes // TODO: isGraphicNode here looks dubious; see also tdf#47036 fix; // this needs more investigation when exactly Word considers something // anchored in text frame vs. anchored in body. if (!isGraphicNode(pFrameFormat)
&& (IsAtParaMatch(*oAnchorCheckPam, rAnchor)
|| (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()
&& ( *oAnchorCheckPam->Start() == *rAnchor.GetContentAnchor()
|| *oAnchorCheckPam->End() == *rAnchor.GetContentAnchor()))))
{ if (pFrameFormat->GetName().isEmpty())
{
aAnchoredObjectsByPtr.insert(pFrameFormat->FindSdrObject());
} else
{
aAnchoredObjectsByName.insert(pFrameFormat->GetName());
}
}
}
oAnchorCheckPam.reset(); // clear SwContentIndex before deleting nodes
{ // has to be in a block to remove the SwContentIndexes before // DelFullPara is called const uno::Reference< text::XTextRange> xInsertTextRange = new SwXTextRange(*pStartPam, this);
assert(xNewFrame->IsDescriptor());
xNewFrame->attachToRange(xInsertTextRange, pStartPam.get());
assert(!xNewFrame->getName().isEmpty());
}
SwTextNode *const pTextNode(pStartPam->GetPointNode().GetTextNode());
assert(pTextNode); if (!pTextNode || !pTextNode->Len()) // don't remove if it contains text!
{ bool bDel = false;
{ // has to be in a block to remove the SwContentIndexes before // DelFullPara is called
SwPaM aMovePam( pStartPam->GetPointNode() ); if (aMovePam.Move( fnMoveForward, GoInContent ))
{ // move the anchor to the next paragraph
SwFormatAnchor aNewAnchor(xNewFrame->GetFrameFormat()->GetAnchor());
aNewAnchor.SetAnchor( aMovePam.Start() );
m_pDoc->SetAttr(
aNewAnchor, *xNewFrame->GetFrameFormat() );
// also move frames anchored to us for (size_t i = 0; i < m_pDoc->GetSpzFrameFormats()->size(); ++i)
{
SwFrameFormat* pFrameFormat = (*m_pDoc->GetSpzFrameFormats())[i]; if ((!pFrameFormat->GetName().isEmpty() && aAnchoredObjectsByName.find(pFrameFormat->GetName()) != aAnchoredObjectsByName.end() ) ||
( pFrameFormat->GetName().isEmpty() && aAnchoredObjectsByPtr.find(pFrameFormat->FindSdrObject()) != aAnchoredObjectsByPtr.end()) )
{ // copy the anchor to the next paragraph
SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
aAnchor.SetAnchor(aMovePam.Start());
m_pDoc->SetAttr(aAnchor, *pFrameFormat);
} else
{ // if this frame is a textbox of a shape anchored to us, move this textbox too. constauto& pTextBoxes = pFrameFormat->GetOtherTextBoxFormats(); if (pFrameFormat->Which() == RES_FLYFRMFMT && pTextBoxes
&& pTextBoxes->GetOwnerShape())
{ constauto& rShapeAnchor = pTextBoxes->GetOwnerShape()->GetAnchor(); if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
&& rShapeAnchor.GetContentAnchor() && pFrameFormat->GetAnchor().GetContentAnchor()
&& pStartPam->ContainsPosition(*pFrameFormat->GetAnchor().GetContentAnchor()))
{ const SwNode& rAnchorNode
= *pFrameFormat->GetAnchor().GetAnchorNode(); if (!(rAnchorNode.FindFooterStartNode() || rAnchorNode.FindHeaderStartNode()))
{
SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
aAnchor.SetAnchor(aMovePam.Start());
m_pDoc->SetAttr(aAnchor, *pFrameFormat);
}
}
}
}
}
bDel = true; // Only delete the ex-anchor, if the frame is moved successfully
}
} if (bDel)
m_pDoc->getIDocumentContentOperations().DelFullPara(*pStartPam);
}
} catch (const lang::IllegalArgumentException& rIllegal)
{
sMessage = rIllegal.Message;
bIllegalException = true;
} catch (const uno::RuntimeException& rRuntime)
{
sMessage = rRuntime.Message;
bRuntimeException = true;
}
xRet = static_cast<SwXFrame*>(xNewFrame.get()); if (bParaBeforeInserted || bParaAfterInserted)
{ const rtl::Reference<SwXTextCursor> xFrameTextCursor =
xNewFrame->createXTextCursor(); if (bParaBeforeInserted)
{ // todo: remove paragraph before frame
m_pDoc->getIDocumentContentOperations().DelFullPara(*xFrameTextCursor->GetPaM());
} if (bParaAfterInserted)
{
xFrameTextCursor->gotoEnd(false); if (!bParaBeforeInserted)
m_pDoc->getIDocumentContentOperations().DelFullPara(*xFrameTextCursor->GetPaM()); else
{ // In case the frame has a table only, the cursor points to the end of the first cell of the table.
SwPaM aPaM(*xFrameTextCursor->GetPaM()->GetPointNode().FindStartNodeByType(SwFlyStartNode)->EndOfSectionNode()); // Now we have the end of the frame -- the node before that will be the paragraph we want to remove.
aPaM.GetPoint()->Adjust(SwNodeOffset(-1));
m_pDoc->getIDocumentContentOperations().DelFullPara(aPaM);
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.