/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* -*- 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 .
*/
namespace
{ enumclass BkmType { // The order is important: BookmarkCompareStruct::operator () depends on it. // When different bookmarks' starts/ends appear at one position, by default (when there's no // frames at the position - see lcl_ExportBookmark), first previous bookmarks close, then // collapsed ones appear, then new bookmarks open.
End, StartEnd, Start
};
SwXBookmarkPortion_Impl(rtl::Reference<SwXBookmark> xMark, const BkmType nType, SwPosition _aPosition)
: xBookmark (std::move( xMark ))
, nBkmType ( nType )
, aPosition (std::move( _aPosition ))
{
}
sal_Int32 getIndex () const
{ return aPosition.GetContentIndex();
}
}; typedef std::shared_ptr < SwXBookmarkPortion_Impl > SwXBookmarkPortion_ImplSharedPtr; struct BookmarkCompareStruct
{ booloperator () ( const SwXBookmarkPortion_ImplSharedPtr &r1, const SwXBookmarkPortion_ImplSharedPtr &r2 ) const
{ // #i16896# for bookmark portions at the same position, the start should // always precede the end. Hence compare positions, and use bookmark type // as tie-breaker for same position. // return ( r1->nIndex == r2->nIndex ) // ? ( r1->nBkmType < r2->nBkmType ) // : ( r1->nIndex < r2->nIndex );
// Note that the above code does not correctly handle // the case when one bookmark ends, and another begins in the same // position. When this occurs, the above code will return the // start of the 2nd bookmark BEFORE the end of the first bookmark // See bug #i58438# for more details. The below code is correct and // fixes both #i58438 and #i16896# return std::make_pair(r1->aPosition, r1->nBkmType)
< std::make_pair(r2->aPosition, r2->nBkmType);
}
}; typedef std::multiset < SwXBookmarkPortion_ImplSharedPtr, BookmarkCompareStruct > SwXBookmarkPortion_ImplList;
/// Inserts pBkmk to rBkmArr in case it starts or ends at rOwnNode void lcl_FillBookmark(sw::mark::MarkBase* const pBkmk, const SwNode& rOwnNode, SwDoc& rDoc, SwXBookmarkPortion_ImplList& rBkmArr)
{ boolconst isExpanded = pBkmk->IsExpanded(); auto [/*const SwPosition&*/ rStartPos, rEndPos] = pBkmk->GetMarkStartEnd(); // A bookmark where the text was deleted becomes collapsed boolconst hasOther = isExpanded && rStartPos != rEndPos; boolconst bStartPosInNode = rStartPos.GetNode() == rOwnNode; boolconst bEndPosInNode = rEndPos.GetNode() == rOwnNode; // tdf#160700: Crossrefbookmarks only need separate start and end, when the start // isn't in the end position (so in empty nodes, no need to handle them specially)
sw::mark::CrossRefBookmark* const pCrossRefMark
= !isExpanded && bStartPosInNode
&& rStartPos.GetContentIndex() < rStartPos.GetContentNode()->Len()
? dynamic_cast<sw::mark::CrossRefBookmark*>(pBkmk)
: nullptr;
if (bStartPosInNode)
{ // #i109272#: cross reference marks: need special handling!
BkmType const nType = (hasOther || pCrossRefMark)
? BkmType::Start : BkmType::StartEnd;
rBkmArr.insert(std::make_shared<SwXBookmarkPortion_Impl>(
SwXBookmark::CreateXBookmark(rDoc, pBkmk),
nType, rStartPos));
}
if (!bEndPosInNode) return;
std::optional<SwPosition> oCrossRefEndPos; const SwPosition* pEndPos = nullptr; if(hasOther)
{
pEndPos = &rEndPos;
} elseif (pCrossRefMark)
{ // Crossrefbookmarks only remember the start position but have to span the whole paragraph
SwTextNode& rEndNd = *rEndPos.GetNode().GetTextNode();
oCrossRefEndPos.emplace(rEndNd, rEndNd.Len());
pEndPos = &*oCrossRefEndPos;
} if(pEndPos)
{
rBkmArr.insert(std::make_shared<SwXBookmarkPortion_Impl>(
SwXBookmark::CreateXBookmark(rDoc, pBkmk),
BkmType::End, *pEndPos));
}
}
SwTextNode* pTextNode = rUnoCursor.GetPoint()->GetNode().GetTextNode();
assert(pTextNode); // A text node already knows its marks via its SwContentIndexes.
o3tl::sorted_vector<const sw::mark::MarkBase*> aSeenMarks; for (const SwContentIndex* pIndex = pTextNode->GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
{ if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark) continue; // Need a non-cost mark here, as we'll create a UNO wrapper around it.
sw::mark::MarkBase* pBkmk = static_cast<sw::mark::MarkBase*>(pIndex->GetOwner()); if (!pBkmk) continue;
IDocumentMarkAccess::MarkType eType = IDocumentMarkAccess::GetType(*pBkmk); // These are the types stored in the container otherwise accessible via getBookmarks*() if (eType != IDocumentMarkAccess::MarkType::BOOKMARK && eType != IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK &&
eType != IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK) continue; // Only handle bookmarks once, if they start and end at this node as well. if (!aSeenMarks.insert(pBkmk).second) continue;
lcl_FillBookmark(pBkmk, *pTextNode, rDoc, rBkmArr);
}
}
// search for all annotation marks that have its start position in this paragraph const SwNode& rOwnNode = rUnoCursor.GetPoint()->GetNode(); constauto pCandidatesEnd = pMarkAccess->getAnnotationMarksEnd(); for( auto ppMark = pMarkAccess->findFirstAnnotationMarkNotStartsBefore(rOwnNode);
ppMark != pCandidatesEnd;
++ppMark )
{
::sw::mark::AnnotationMark* const pAnnotationMark = *ppMark;
assert(pAnnotationMark); const SwPosition& rStartPos = pAnnotationMark->GetMarkStart(); // no need to consider annotation marks starting after if (rStartPos.GetNode() > rOwnNode) break; if (rStartPos.GetNode() != rOwnNode) continue;
const SwFormatField* pAnnotationFormatField = pAnnotationMark->GetAnnotationFormatField(); if (!pAnnotationFormatField)
{
SAL_WARN("sw.core", "missing annotation format field"); continue;
}
// find all frames, graphics and OLEs that are bound AT character in para
FrameClientSortList_t frames;
::CollectFrameAtNode(m_pUnoCursor->GetPoint()->GetNode(), frames, true);
lcl_CreatePortions(m_Portions, xParentText, &*m_pUnoCursor, frames, nStart, nEnd, bOnlyTextFields);
}
static rtl::Reference<SwXTextPortion>
lcl_ExportFieldMark(
uno::Reference< SwXText > const & i_xParentText,
SwUnoCursor * const pUnoCursor, const SwTextNode * const pTextNode )
{
rtl::Reference<SwXTextPortion> pPortion;
SwDoc& rDoc = pUnoCursor->GetDoc(); // maybe it's a good idea to add a special hint to the hints array and rely on the hint segmentation... const sal_Int32 start = pUnoCursor->Start()->GetContentIndex();
OSL_ENSURE(pUnoCursor->End()->GetContentIndex() == start, "hmm --- why is this different");
/// Creates a text portion that has a non-empty ContentControl property. static rtl::Reference<SwXTextPortion>
lcl_CreateContentControlPortion(const css::uno::Reference<SwXText>& xParent, const SwUnoCursor* pUnoCursor, SwTextAttr& rAttr,
std::unique_ptr<const TextRangeList_t>&& pPortions)
{
rtl::Reference<SwXContentControl> xContentControl = SwXContentControl::CreateXContentControl(
*static_cast<SwFormatContentControl&>(rAttr.GetAttr()).GetContentControl(), xParent,
std::move(pPortions));
rtl::Reference<SwXTextPortion> pPortion;
pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_CONTENT_CONTROL);
pPortion->SetContentControl(xContentControl); return pPortion;
}
/** * Exports all bookmarks from rBkmArr into rPortions that have the same start * or end position as nIndex. * * @param rBkmArr the array of bookmarks. * * @param rFramePositions the list of positions where there is an at-char / * anchored frame. * Collapsed (BkmType::StartEnd) bookmarks, as well as bookmarks that start/end * at the frame anchor position, are considered as wrapping the frames, if any * (i.e., starts are output before the frames; ends are output after frames). * When there's no frame here, bookmarks are expected to not overlap (#i58438): * first, non-collapsed bookmarks' ends are output; then collapsed bookmarks; * then non-collapsed bookmarks' starts. * * @param stage Case before_frames: if there is a frame at this index, output * starts of both collapsed and non-collapsed bookmarks (remove non-collapsed * starts from rBkmArr, convert collapsed ones to ends); if there's no frame, * doesn't output anything. * Case after_frames: outputs (and removes from rBkmArr) everything (left) at * this index, in the order of occurrence in rBkmArr (see #i58438).
*/ staticvoid lcl_ExportBookmark(
TextRangeList_t & rPortions,
uno::Reference<SwXText> const& xParent, const SwUnoCursor * const pUnoCursor,
SwXBookmarkPortion_ImplList& rBkmArr, const sal_Int32 nIndex, const o3tl::sorted_vector<sal_Int32>& rFramePositions,
ExportBookmarkPass stage)
{ for ( SwXBookmarkPortion_ImplList::iterator aIter = rBkmArr.begin(), aEnd = rBkmArr.end(); aIter != aEnd; )
{ const SwXBookmarkPortion_ImplSharedPtr& pPtr = *aIter; if ( nIndex > pPtr->getIndex() )
{ // We may get here, if SwXTextPortionEnumeration ctor was called with nStart greater // than this bookmark's index. Just drop it.
aIter = rBkmArr.erase(aIter); continue;
} if ( nIndex < pPtr->getIndex() ) break;
if (stage == ExportBookmarkPass::before_frames)
{ if (rFramePositions.find(nIndex) == rFramePositions.end()) // No frames at this index break; // Do nothing; everything will be output at after_frames pass
if (pPtr->nBkmType == BkmType::End)
{
++aIter; continue; // Only consider BkmType::Start and BkmType::StartEnd in this pass
}
}
// At this we create a text portion, due to one of these // reasons: // - this is the real start of a non-collapsed bookmark // - this is the real end of a non-collapsed bookmark // - this is the real position of a collapsed bookmark // - this is the start or end of a collapsed bookmark at the same position as an at-char // anchored frame const SwTextPortionType portionType
= pPtr->nBkmType == BkmType::End ? PORTION_BOOKMARK_END : PORTION_BOOKMARK_START; constbool collapsed
= pPtr->nBkmType == BkmType::StartEnd && stage == ExportBookmarkPass::after_frames;
rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(pUnoCursor, xParent, portionType);
rPortions.emplace_back(pPortion);
pPortion->SetBookmark(pPtr->xBookmark);
pPortion->SetCollapsed(collapsed);
// next bookmark if (pPtr->nBkmType == BkmType::StartEnd && stage == ExportBookmarkPass::before_frames)
{ // This is a collapsed bookmark around a frame, and its start portion was just emitted; // turn it into an end bookmark to process after_frames
pPtr->nBkmType = BkmType::End;
++aIter;
} else
aIter = rBkmArr.erase(aIter);
}
}
case RES_TXTATR_TOXMARK: case RES_TXTATR_REFMARK:
{ bool bIsPoint = !(pAttr->GetEnd()); if (!bRightMoveForbidden || !bIsPoint)
{ if (bIsPoint)
{
pUnoCursor->Right(1);
}
rtl::Reference<SwXTextPortion> xTmp =
(RES_TXTATR_REFMARK == nAttrWhich)
? lcl_CreateRefMarkPortion(
xParent, pUnoCursor, *pAttr, false)
: lcl_CreateTOXMarkPortion(
xParent, pUnoCursor, *pAttr, false); if (bIsPoint) // consume CH_TXTATR!
{
pUnoCursor->Normalize(false);
pUnoCursor->DeleteMark();
pPortion = std::move(xTmp);
} else// just insert it
{
rPortionStack.top().first->push_back(xTmp);
}
}
} break; case RES_TXTATR_CJK_RUBY: //#i91534# GetEnd() == 0 mixes the order of ruby start/end if(pAttr->GetEnd() && (*pAttr->GetEnd() != pAttr->GetStart()))
{
lcl_InsertRubyPortion( *rPortionStack.top().first,
xParent, pUnoCursor, *pAttr, false);
} break; case RES_TXTATR_META: case RES_TXTATR_METAFIELD: case RES_TXTATR_CONTENTCONTROL: if (pAttr->GetStart() != *pAttr->GetEnd())
{ if (!bRightMoveForbidden)
{
pUnoCursor->Right(1);
o_rbCursorMoved = true; // only if the end is included in selection! if ((i_nEndPos < 0) ||
(*pAttr->GetEnd() <= i_nEndPos))
{
rPortionStack.push( std::make_pair( new TextRangeList_t, pAttr ));
}
}
} break; case RES_TXTATR_LINEBREAK: if (!bRightMoveForbidden)
{
pUnoCursor->Right(1); if (*pUnoCursor->GetMark() == *pUnoCursor->GetPoint()) break;
pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_LINEBREAK);
rtl::Reference<SwXLineBreak> xLineBreak
= SwXLineBreak::CreateXLineBreak(
&const_cast<SwFormatLineBreak&>(pAttr->GetLineBreak()));
pPortion->SetLineBreak(xLineBreak);
} break; case RES_TXTATR_AUTOFMT: case RES_TXTATR_INETFMT: case RES_TXTATR_CHARFMT: break; // these are handled as properties of a "Text" portion default:
OSL_FAIL("unknown attribute"); break;
}
}
nStartIndex++;
}
if (pPortion.is()) // implies that we have moved the cursor
{
o_rbCursorMoved = true;
} if (!o_rbCursorMoved)
{ // search for attribute changes behind the current cursor position // break up at frames, bookmarks, redlines
// We want this loop to iterate over all red lines in this // array. We will only insert the ones with index matches for ( SwXRedlinePortion_ImplList::iterator aIter = rRedlineArr.begin(), aEnd = rRedlineArr.end();
aIter != aEnd; )
{
SwXRedlinePortion_ImplSharedPtr pPtr = *aIter;
sal_Int32 nRealIndex = pPtr->getRealIndex(); // If there are elements before nIndex, remove them if ( nIndex > nRealIndex )
aIter = rRedlineArr.erase(aIter); // If the elements match, and them to the list elseif ( nIndex == nRealIndex )
{
rPortions.push_back( new SwXRedlinePortion(
*pPtr->m_pRedline, pUnoCursor, xParent, pPtr->m_bStart));
aIter = rRedlineArr.erase(aIter);
} // If we've iterated past nIndex, exit the loop else break;
}
}
if (stage == ExportBookmarkPass::before_frames) // Only exporting the start of some collapsed bookmarks: no export of // other arrays. return;
if (!rRedlineArr.empty())
lcl_ExportRedline(rPortions, xParent, pUnoCursor, rRedlineArr, nIndex);
if (!rBreakArr.empty())
lcl_ExportSoftPageBreak(rPortions, xParent, pUnoCursor, rBreakArr, nIndex);
}
/** * Exports all start annotation marks from rAnnotationStartArr into rPortions that have the same * start position as nIndex. * * @param rAnnotationStartArr the array of annotation marks. Consumed entries are removed. * * @param rFramePositions the list of positions where there is an at-char anchored frame. * * @param bOnlyFrame If true: export only the start of annotation marks which cover an at-char * anchored frame. If false: export everything else.
*/ staticvoid lcl_ExportAnnotationStarts(
TextRangeList_t & rPortions,
uno::Reference<SwXText> const & xParent, const SwUnoCursor * const pUnoCursor,
SwAnnotationStartPortion_ImplList& rAnnotationStartArr, const sal_Int32 nIndex, const o3tl::sorted_vector<sal_Int32>& rFramePositions, bool bOnlyFrame)
{ for ( SwAnnotationStartPortion_ImplList::iterator aIter = rAnnotationStartArr.begin(), aEnd = rAnnotationStartArr.end();
aIter != aEnd; )
{
SwAnnotationStartPortion_ImplSharedPtr pPtr = *aIter; if ( nIndex > pPtr->getIndex() )
{
aIter = rAnnotationStartArr.erase(aIter); continue;
} if ( pPtr->getIndex() > nIndex )
{ break;
}
/** * Exports at-char anchored frames. * * @param i_rFrames the frames for this paragraph, frames at <= i_nCurrentIndex * are removed from the container.
*/ static sal_Int32 lcl_ExportFrames(
TextRangeList_t & rPortions,
uno::Reference<SwXText> const & i_xParent,
SwUnoCursor const * const i_pUnoCursor,
FrameClientSortList_t & i_rFrames,
sal_Int32 const i_nCurrentIndex)
{ // Ignore frames which are not exported, as we are exporting a selection // and they are anchored before the start of the selection. while (!i_rFrames.empty() && i_rFrames.front().nIndex < i_nCurrentIndex)
i_rFrames.pop_front();
// find first Frame in (sorted) i_rFrames at current position while (!i_rFrames.empty() && (i_rFrames.front().nIndex == i_nCurrentIndex)) // do not check for i_nEnd here; this is done implicitly by lcl_MoveCursor
{ auto pFrame = static_cast<SwFrameFormat*>(i_rFrames.front().pFrameClient->GetRegisteredIn()); if (pFrame) // Frame could be disposed
{
rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(i_pUnoCursor, i_xParent, *pFrame );
rPortions.emplace_back(pPortion);
}
i_rFrames.pop_front();
}
// set the start if a selection should be exported if ((i_nStartPos > 0) &&
(pUnoCursor->Start()->GetContentIndex() != i_nStartPos))
{
pUnoCursor->DeleteMark();
OSL_ENSURE(pUnoCursor->Start()->GetNode().GetTextNode() &&
(i_nStartPos <= pUnoCursor->Start()->GetNode().GetTextNode()->
GetText().getLength()), "Incorrect start position" ); // ??? should this be i_nStartPos - current position ?
pUnoCursor->Right(i_nStartPos);
}
SwDoc& rDoc = pUnoCursor->GetDoc();
std::deque<sal_Int32> FieldMarks; if (!bOnlyTextFields)
lcl_FillFieldMarkArray(FieldMarks, *pUnoCursor, i_nStartPos);
SwXBookmarkPortion_ImplList Bookmarks; if (!bOnlyTextFields)
lcl_FillBookmarkArray(rDoc, *pUnoCursor, Bookmarks);
SwXRedlinePortion_ImplList Redlines; if (!bOnlyTextFields)
lcl_FillRedlineArray(rDoc, *pUnoCursor, Redlines);
SwSoftPageBreakList SoftPageBreaks; if (!bOnlyTextFields)
lcl_FillSoftPageBreakArray(*pUnoCursor, SoftPageBreaks);
SwAnnotationStartPortion_ImplList AnnotationStarts; if (!bOnlyTextFields)
lcl_FillAnnotationStartArray( rDoc, *pUnoCursor, AnnotationStarts );
bool bAtEnd( false ); while (!bAtEnd) // every iteration consumes at least current character!
{ if (pUnoCursor->HasMark())
{
pUnoCursor->Normalize(false);
pUnoCursor->DeleteMark();
}
SwTextNode * const pTextNode = pUnoCursor->GetPointNode().GetTextNode(); if (!pTextNode)
{
OSL_FAIL("lcl_CreatePortions: no TextNode - what now ?"); return;
}
SwpHints * const pHints = pTextNode->GetpSwpHints(); const sal_Int32 nCurrentIndex =
pUnoCursor->GetPoint()->GetContentIndex(); // this contains the portion which consumes the character in the // text at nCurrentIndex; i.e. it must be set _once_ per iteration
rtl::Reference<SwXTextPortion> xRef;
SwUnoCursorHelper::SelectPam(*pUnoCursor, true); // set mark
// First remember the frame positions.
o3tl::sorted_vector<sal_Int32> aFramePositions;
lcl_ExtractFramePositions(i_rFrames, nCurrentIndex, aFramePositions);
// Then export start of collapsed bookmarks which "cover" at-char // anchored frames.
lcl_ExportBkmAndRedline( *PortionStack.top().first, i_xParentText,
pUnoCursor, Bookmarks, Redlines, SoftPageBreaks, nCurrentIndex, aFramePositions, ExportBookmarkPass::before_frames );
// Export ends of the previously started collapsed bookmarks + all // other bookmarks, redlines, etc.
lcl_ExportBkmAndRedline( *PortionStack.top().first, i_xParentText,
pUnoCursor, Bookmarks, Redlines, SoftPageBreaks, nCurrentIndex, aFramePositions, ExportBookmarkPass::after_frames );
xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
} elseif (bAtEnd && !xRef.is() && !pTextNode->Len())
{ // special case: for an empty paragraph, we better put out a // text portion because there may be a hyperlink attribute
xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
} elseif (bAtEnd && !xRef.is() && pHints)
{ // See if there is an empty autofmt at the paragraph end. If so, export it, since that // affects the formatting of number portions. for (size_t i = 0; i < pHints->Count(); ++i)
{ const SwTextAttr* pHint = pHints->GetSortedByEnd(i); if (pHint->GetStart() < pTextNode->Len())
{ break;
} if (pHint->Which() == RES_TXTATR_AUTOFMT && pHint->GetEnd()
&& pHint->GetStart() == *pHint->GetEnd()
&& pHint->GetStart() == pTextNode->Len())
{
xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT); break;
}
}
}
if (xRef.is())
{
PortionStack.top().first->push_back(xRef);
}
}
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.