// 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);
}
}
}
// !!! TODO - PaMs in tables and sections do not work here - // the same applies to PaMs in frames !!!
if (!::sw::XTextRangeToSwPaM(aStartCellPam, xStartRange) ||
!::sw::XTextRangeToSwPaM(aEndCellPam, xEndRange))
{ throw lang::IllegalArgumentException(
u"Start or End range cannot be resolved to a SwPaM"_ustr,
uno::Reference< text::XTextCopy >( this ), sal_Int16( 2 ) );
}
/** check the nodes between start and end itisallowedtohavepairsofStartNode/EndNodes
*/ if (aStartCellPam.Start()->GetNode() < aEndCellPam.End()->GetNode())
{ // increment on each StartNode and decrement on each EndNode // we must reach zero at the end and must not go below zero
tools::Long nOpenNodeBlock = 0;
SwNodeIndex aCellIndex(aStartCellPam.Start()->GetNode()); while (aCellIndex < aEndCellPam.End()->GetNodeIndex())
{ if (aCellIndex.GetNode().IsStartNode())
{
++nOpenNodeBlock;
} elseif (aCellIndex.GetNode().IsEndNode())
{
--nOpenNodeBlock;
} if (nOpenNodeBlock < 0)
{ throw lang::IllegalArgumentException();
}
++aCellIndex;
} if (nOpenNodeBlock != 0)
{ throw lang::IllegalArgumentException();
}
}
/** The vector<vector> NodeRanges has to contain consecutive nodes. InrTableRangestherangesdon'tneedtobefullparagraphsbut theyhavetofolloweachother.Toprocesstherangesthey havetobealignedonparagraphbordersbyinsertingparagraph breaks.Non-consecutiverangesmustinitiateanexception.
*/ if (!pLastCell) // first cell?
{ // align the beginning - if necessary if (aStartCellPam.Start()->GetContentIndex())
{
m_pDoc->getIDocumentContentOperations().SplitNode(*aStartCellPam.Start(), false);
}
} else
{ // check the predecessor const SwNodeOffset nStartCellNodeIndex =
aStartCellPam.Start()->GetNodeIndex(); const SwNodeOffset nLastNodeEndIndex = pLastCell->aEnd.GetIndex(); if (nLastNodeEndIndex == nStartCellNodeIndex)
{ // same node as predecessor then equal nContent? if (0 != aStartCellPam.Start()->GetContentIndex())
{ throw lang::IllegalArgumentException();
}
m_pDoc->getIDocumentContentOperations().SplitNode(*aStartCellPam.Start(), false);
SwNodeOffset const nNewIndex(aStartCellPam.Start()->GetNodeIndex()); if (nNewIndex != nStartCellNodeIndex)
{ // aStartCellPam now points to the 2nd node // the last cell may *also* point to 2nd node now - fix it!
assert(nNewIndex == nStartCellNodeIndex + 1); if (pLastCell->aEnd.GetIndex() == nNewIndex)
{
--pLastCell->aEnd; if (pLastCell->aStart.GetIndex() == nNewIndex)
{
--pLastCell->aStart;
}
}
}
} elseif (nStartCellNodeIndex == (nLastNodeEndIndex + 1))
{ // next paragraph - now the content index of the new should be 0 // and of the old one should be equal to the text length // but if it isn't we don't care - the cell is being inserted on // the node border anyway
} else
{ throw lang::IllegalArgumentException();
}
} // now check if there's a need to insert another paragraph break if (aEndCellPam.End()->GetContentIndex() <
aEndCellPam.End()->GetNode().GetTextNode()->Len())
{
m_pDoc->getIDocumentContentOperations().SplitNode(*aEndCellPam.End(), false); // take care that the new start/endcell is moved to the right position // aStartCellPam has to point to the start of the new (previous) node // aEndCellPam has to point to the end of the new (previous) node
aStartCellPam.DeleteMark();
aStartCellPam.Move(fnMoveBackward, GoInNode);
aStartCellPam.GetPoint()->SetContent(0);
aEndCellPam.DeleteMark();
aEndCellPam.Move(fnMoveBackward, GoInNode);
aEndCellPam.GetPoint()->SetContent(
aEndCellPam.GetPointNode().GetTextNode()->Len() );
}
staticvoid
lcl_ApplyCellProperties( const sal_Int32 nLeftPos, const uno::Sequence< beans::PropertyValue >& rCellProperties, const uno::Reference< uno::XInterface >& xCell,
std::vector<VerticallyMergedCell> & rMergedCells)
{ const uno::Reference< beans::XPropertySet > xCellPS(xCell, uno::UNO_QUERY); for (constauto& rCellProperty : rCellProperties)
{ const OUString & rName = rCellProperty.Name; const uno::Any & rValue = rCellProperty.Value; if ( rName == "VerticalMerge" )
{ // determine left border position // add the cell to a queue of merged cells bool bMerge = false;
rValue >>= bMerge; if (bMerge)
{ // 'close' all the cell with the same left position // if separate vertical merges in the same column exist for(auto& aMergedCell : rMergedCells)
{ if(lcl_SimilarPosition(aMergedCell.nLeftPosition, nLeftPos))
{
aMergedCell.bOpen = false;
}
} // add the new group of merged cells
rMergedCells.emplace_back(xCellPS, nLeftPos);
} else
{ bool bFound = false;
SAL_WARN_IF(rMergedCells.empty(), "sw.uno", "the first merged cell is missing"); for(auto& aMergedCell : rMergedCells)
{ if (aMergedCell.bOpen && lcl_SimilarPosition(aMergedCell.nLeftPosition, nLeftPos))
{
aMergedCell.aCells.push_back( xCellPS );
bFound = true;
}
}
SAL_WARN_IF(!bFound, "sw.uno", "couldn't find first vertically merged cell" );
}
} else
{ try
{ staticconst std::initializer_list<std::u16string_view> vDenylist = {
u"LeftMargin",
u"ParaTopBorder",
u"ParaTopBorderDistance",
u"ParaTopBorderComplexColor",
u"ParaLeftBorder",
u"ParaLeftBorderDistance",
u"ParaLeftBorderComplexColor",
u"ParaBottomBorder",
u"ParaBottomBorderDistance",
u"ParaBottomBorderComplexColor",
u"ParaRightBorder",
u"ParaRightBorderDistance",
u"ParaRightBorderComplexColor",
}; if (std::find(vDenylist.begin(), vDenylist.end(), rName) == vDenylist.end())
{
xCellPS->setPropertyValue(rName, rValue);
}
} catch (const uno::Exception&)
{
TOOLS_WARN_EXCEPTION( "sw.uno", "Exception when setting cell property " << rName );
}
}
}
}
staticvoid
lcl_MergeCells(std::vector<VerticallyMergedCell> & rMergedCells)
{ for(auto& aMergedCell : rMergedCells)
{ // the first of the cells gets the number of cells set as RowSpan // the others get the inverted number of remaining merged cells // (3,-2,-1)
sal_Int32 nCellCount = static_cast<sal_Int32>(aMergedCell.aCells.size()); if(nCellCount<2)
{
SAL_WARN("sw.uno", "incomplete vertical cell merge"); continue;
}
aMergedCell.aCells.front()->setPropertyValue(UNO_NAME_ROW_SPAN, uno::Any(nCellCount--));
nCellCount*=-1; for(auto pxPSet = aMergedCell.aCells.begin()+1; nCellCount<0; ++pxPSet, ++nCellCount)
{
(*pxPSet)->setPropertyValue(UNO_NAME_ROW_SPAN, uno::Any(nCellCount));
(*pxPSet)->setPropertyValue(u"VerticalMerge"_ustr, uno::Any(true));
}
}
}
// now that the cell properties are set the vertical merge values // have to be applied
lcl_MergeCells(aMergedCells);
} catch (const lang::WrappedTargetException&)
{
} catch (const lang::IndexOutOfBoundsException&)
{
}
SwNodeIndex rNdIndex( *GetStartNode( ), 1 );
SwPosition rPos( rNdIndex ); // tdf#112202 need SwXText because cursor cannot select table at the start
SwTextNode * pFirstNode;
{
SwPaM temp(*pSource->GetStartNode(), *pSource->GetStartNode()->EndOfSectionNode(), SwNodeOffset(+1), SwNodeOffset(-1));
pFirstNode = temp.GetMark()->GetNode().GetTextNode(); if (pFirstNode)
{
temp.GetMark()->AssignStartIndex(*pFirstNode);
} if (SwTextNode *const pNode = temp.GetPoint()->GetNode().GetTextNode())
{
temp.GetPoint()->AssignEndIndex(*pNode);
} // Explicitly request copy text mode, so // sw::DocumentContentOperationsManager::CopyFlyInFlyImpl() will copy shapes anchored to // us, even if we have only a single paragraph.
m_pDoc->getIDocumentContentOperations().CopyRange(temp, rPos, SwCopyFlags::CheckPosInFly);
}
}
// save current start node to be able to check if there is content // after the table - otherwise the cursor would be in the body text!
SwStartNode const*const pOwnStartNode = rNode.FindStartNodeByType(
(m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
if (!bIgnoreTables)
{ // is there a table here?
SwTableNode* pTableNode = rUnoCursor.GetPointNode().FindTableNode(); while (pTableNode)
{
rUnoCursor.GetPoint()->Assign(*pTableNode->EndOfSectionNode());
SwContentNode* pCont = SwNodes::GoNext(rUnoCursor.GetPoint());
pTableNode = pCont->FindTableNode();
}
}
SwStartNode const*const pNewStartNode = rUnoCursor.GetPointNode().FindStartNodeByType(
(m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode); if (!pNewStartNode || (pNewStartNode != pOwnStartNode))
{ throw uno::RuntimeException(u"no text available"_ustr);
} return pXCursor;
}
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.