// a: |-----| // b: // |---| => valid: b before a // |-----| => valid: start == end; b before a // |---------| => invalid: overlap (1) // |-----------| => valid: same end; b around a // |-----------------| => valid: b around a // |---| => valid; same start; b within a // |-----| => valid; same start and end; b around or within a? // |-----------| => valid: same start: b around a // |-| => valid: b within a // |---| => valid: same end; b within a // |---------| => invalid: overlap (2) // |-----| => valid: end == start; b after a // |---| => valid: b after a // ===> 2 invalid overlap cases static bool isOverlap(const sal_Int32 nStart1, const sal_Int32 nEnd1, const sal_Int32 nStart2, const sal_Int32 nEnd2)
{ return
((nStart1 > nStart2) && (nStart1 < nEnd2) && (nEnd1 > nEnd2)) // (1)
|| ((nStart1 < nStart2) && (nStart2 < nEnd1) && (nEnd1 < nEnd2)); // (2)
}
/// #i106930#: now asymmetric: empty hint1 is _not_ nested, but empty hint2 is static bool isNestedAny(const sal_Int32 nStart1, const sal_Int32 nEnd1, const sal_Int32 nStart2, const sal_Int32 nEnd2)
{ return ((nStart1 == nStart2) || (nEnd1 == nEnd2)) // same start/end: nested except if hint1 empty and hint2 not empty
? (nStart1 != nEnd1) || (nStart2 == nEnd2)
: ((nStart1 < nStart2) ? (nEnd1 >= nEnd2) : (nEnd1 <= nEnd2));
}
NestList_t OverlappingExisting; // existing hints to be split
NestList_t OverwrittenExisting; // existing hints to be replaced
NestList_t SplitNew; // new hints to be inserted
SplitNew.push_back(& rNewHint);
// pass 1: split the inserted hint into fragments if necessary for ( size_t i = 0; i < Count(); ++i )
{
SwTextAttr * const pOther = GetSortedByEnd(i);
if (pOther->IsNesting())
{ const sal_uInt16 nOtherWhich( pOther->Which() ); const sal_Int32 nOtherStart( pOther->GetStart() ); const sal_Int32 nOtherEnd ( *pOther->GetEnd() ); if (isOverlap(nNewStart, nNewEnd, nOtherStart, nOtherEnd ))
{ switch (splitPolicy(nNewWhich, nOtherWhich))
{ case FAIL:
SAL_INFO("sw.core", "cannot insert hint: overlap"); for (constauto& aSplit : SplitNew)
TextAttrDelete(aSplit); returnfalse; case SPLIT_NEW:
lcl_DoSplitNew(SplitNew, rNode, nNewStart,
nOtherStart, nOtherEnd, pOther->HasDummyChar()); break; case SPLIT_OTHER:
OverlappingExisting.push_back(
static_txtattr_cast<SwTextAttrNesting*>(pOther)); break; default:
assert(!"bad code monkey"); break;
}
} elseif (isNestedAny(nNewStart, nNewEnd, nOtherStart, nOtherEnd))
{ if (!bNewSelfNestable && (nNewWhich == nOtherWhich))
{ // ruby and hyperlink: if there is nesting, _overwrite_
OverwrittenExisting.push_back(
static_txtattr_cast<SwTextAttrNesting*>(pOther));
} elseif ((nNewStart == nOtherStart) && pOther->HasDummyChar())
{ if (rNewHint.HasDummyChar())
{
assert(!"ERROR: inserting duplicate CH_TXTATR hint"); returnfalse;
} elseif (nNewEnd < nOtherEnd) { // other has dummy char, new is inside other, but // new contains the other's dummy char? // should be corrected because it may lead to problems // in SwXMeta::createEnumeration // SplitNew is sorted, so this is the first split
assert(SplitNew.front()->GetStart() == nNewStart);
SplitNew.front()->SetStart(nNewStart + 1);
}
}
}
}
}
// pass 1b: tragically need to check for fieldmarks here too for (auto iter = SplitNew.begin(); iter != SplitNew.end(); ++iter)
{
SwPaM const temp(rNode, (*iter)->GetStart(), rNode, *(*iter)->GetEnd());
std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
sw::CalcBreaks(Breaks, temp, true); if (!Breaks.empty())
{ if (!isSplittable(nNewWhich))
{
SAL_INFO("sw.core", "cannot insert hint: fieldmark overlap");
assert(SplitNew.size() == 1);
TextAttrDelete(&rNewHint); returnfalse;
} else
{ for (autoconst& rPos : Breaks)
{
assert(rPos.first == rNode.GetIndex());
iter = lcl_DoSplitImpl(SplitNew, rNode, iter,
rPos.second, true, true);
}
}
}
}
assert((isSplittable(nNewWhich) || SplitNew.size() == 1) && "splitting the unsplittable ???");
// pass 2: split existing hints that overlap/nest with new hint // do not iterate over hints array, but over remembered set of overlapping // hints, to keep things simple w.r.t. insertion/removal // N.B: if there is a hint that splits the inserted hint, then // that hint would also have already split any hint in OverlappingExisting // so any hint in OverlappingExisting can be split at most by one hint // in SplitNew, or even not at all (this is not true for existing hints // that go _around_ new hint, which is the reason d'^etre for pass 4) for (auto& rpOther : OverlappingExisting)
{ const sal_Int32 nOtherStart( rpOther->GetStart() ); const sal_Int32 nOtherEnd ( *rpOther->GetEnd() );
switch (ComparePosition(nSplitNewStart, nSplitNewEnd,
nOtherStart, nOtherEnd))
{ case SwComparePosition::Inside:
{
assert(!bRemoveOverlap && "this one should be in OverwrittenExisting?");
} break; case SwComparePosition::Outside: case SwComparePosition::Equal:
{
assert(!"existing hint inside new hint: why?");
} break; case SwComparePosition::OverlapBefore:
{ Delete( rpOther ); // this also does NoteInHistory!
rpOther->SetStart(nSplitNewEnd);
InsertNesting( *rpOther ); if (!bRemoveOverlap)
{ if ( MAX_HINTS <= Count() )
{
SAL_INFO("sw.core", "hints array full :-("); returnfalse;
}
SwTextAttrNesting * const pOtherLeft(
MakeTextAttrNesting( rNode, *rpOther,
nOtherStart, nSplitNewEnd ) );
InsertNesting( *pOtherLeft );
}
} break; case SwComparePosition::OverlapBehind:
{ Delete( rpOther ); // this also does NoteInHistory!
rpOther->SetEnd(nSplitNewStart);
InsertNesting( *rpOther ); if (!bRemoveOverlap)
{ if ( MAX_HINTS <= Count() )
{
SAL_INFO("sw.core", "hints array full :-("); returnfalse;
}
SwTextAttrNesting * const pOtherRight(
MakeTextAttrNesting( rNode, *rpOther,
nSplitNewStart, nOtherEnd ) );
InsertNesting( *pOtherRight );
}
} break; default: break; // overlap resolved by splitting new: nothing to do
}
}
}
if ( MAX_HINTS <= Count() || MAX_HINTS - Count() <= SplitNew.size() )
{
SAL_INFO("sw.core", "hints array full :-("); returnfalse;
}
// pass 3: insert new hints for (constauto& rpHint : SplitNew)
{
InsertNesting(*rpHint);
}
// pass 4: handle overwritten hints // RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY should displace attributes // of the same kind. for (auto& rpOther : OverwrittenExisting)
{ const sal_Int32 nOtherStart( rpOther->GetStart() ); const sal_Int32 nOtherEnd ( *rpOther->GetEnd() );
// overwritten portion is given by start/end of inserted hint if ((nNewStart <= nOtherStart) && (nOtherEnd <= nNewEnd))
{ Delete(rpOther);
rNode.DestroyAttr( rpOther );
} else
{
assert((nOtherStart < nNewStart) || (nNewEnd < nOtherEnd)); // scenario: there is a RUBY, and contained within that a META; // now a RUBY is inserted within the META => the existing RUBY is split: // here it is not possible to simply insert the left/right fragment // of the existing RUBY because they <em>overlap</em> with the META! Delete( rpOther ); // this also does NoteInHistory! if (nNewEnd < nOtherEnd)
{
SwTextAttrNesting * const pOtherRight(
MakeTextAttrNesting(
rNode, *rpOther, nNewEnd, nOtherEnd ) ); boolconst bSuccess( TryInsertNesting(rNode, *pOtherRight) );
SAL_WARN_IF(!bSuccess, "sw.core", "recursive call 1 failed?");
} if (nOtherStart < nNewStart)
{
rpOther->SetEnd(nNewStart); boolconst bSuccess( TryInsertNesting(rNode, *rpOther) );
SAL_WARN_IF(!bSuccess, "sw.core", "recursive call 2 failed?");
} else
{
rNode.DestroyAttr(rpOther);
}
}
}
returntrue;
}
// This function takes care for the following text attribute: // RES_TXTATR_CHARFMT, RES_TXTATR_AUTOFMT // These attributes have to be handled in a special way (Portion building).
// The new attribute will be split by any existing RES_TXTATR_AUTOFMT or // RES_TXTATR_CHARFMT. The new attribute itself will // split any existing RES_TXTATR_AUTOFMT or RES_TXTATR_CHARFMT.
// 2. Find the hints which cover the start and end position // of the new hint. These hints have to be split into two portions:
if ( !bNoLengthAttribute ) // nothing to do for no length attributes
{ for ( size_t i = 0, nCnt = m_HintsByStart.size(); i < nCnt; ++i )
{ // we're modifying stuff here which affects the sorting, and we // don't want it changing underneath us
SwTextAttr* pOther = m_HintsByStart[i];
if ( !bNoLengthAttribute ) // nothing to do for no length attributes
{ for ( size_t i = 0, nCnt = m_HintsByStart.size(); i < nCnt; ++i )
{ const SwTextAttr* pOther = m_HintsByStart[i];
// Get all hints that are in [nPorStart, nPorEnd[: for ( size_t i = 0, nCnt = m_HintsByStart.size(); i < nCnt; ++i )
{ // we get called from TryInsertHint, which changes ordering
SwTextAttr *pOther = m_HintsByStart[i];
SwTextAttr* pNewAttr = nullptr; if ( RES_TXTATR_CHARFMT == nWhich )
{ // pNewHint can be inserted after calculating the sort value. // This should ensure, that pNewHint comes behind the already present // character style
sal_uInt16 nCharStyleCount = 0; for ( constauto& rpHint : aInsDelHints )
{ if ( RES_TXTATR_CHARFMT == rpHint->Which() )
{ // #i74589# const SwFormatCharFormat& rOtherCharFormat = rpHint->GetCharFormat(); const SwFormatCharFormat& rThisCharFormat = rNewHint.GetCharFormat(); constbool bSameCharFormat = rOtherCharFormat.GetCharFormat() == rThisCharFormat.GetCharFormat();
// #i90311# // Do not remove existing character format hint during XML import if ( !rNode.GetDoc().IsInXMLImport() &&
( !( SetAttrMode::DONTREPLACE & nMode ) ||
bNoLengthAttribute ||
bSameCharFormat ) )
{ // Remove old hint Delete( rpHint );
rNode.DestroyAttr( rpHint );
} else
++nCharStyleCount;
} else
{ // remove all attributes from auto styles, which are explicitly set in // the new character format:
OSL_ENSURE( RES_TXTATR_AUTOFMT == rpHint->Which(), "AUTOSTYLES - Misc trouble" );
SwTextAttr* pOther = rpHint; const std::shared_ptr<SfxItemSet> & pOldStyle = static_cast<const SwFormatAutoFormat&>(pOther->GetAttr()).GetStyleHandle();
// For each attribute in the automatic style check if it // is also set the new character style:
SfxItemSet aNewSet( *pOldStyle->GetPool(),
aCharAutoFormatSetRange);
SfxItemIter aItemIter( *pOldStyle ); const SfxPoolItem* pItem = aItemIter.GetCurItem(); do
{ if ( !CharFormat::IsItemIncluded( pItem->Which(), &rNewHint ) )
{
aNewSet.Put( *pItem );
}
pItem = aItemIter.NextItem();
} while (pItem);
// Remove old hint Delete( pOther );
rNode.DestroyAttr( pOther );
// If there is no current hint and start and end of rNewHint // is ok, we do not need to create a new txtattr. if ( nPorStart == nThisStart &&
nPorEnd == nThisEnd &&
!nCharStyleCount )
{
pNewAttr = &rNewHint;
bDestroyHint = false;
} else
{
pNewAttr = MakeTextAttr( rNode.GetDoc(), rNewHint.GetAttr(),
nPorStart, nPorEnd );
static_txtattr_cast<SwTextCharFormat*>(pNewAttr)->SetSortNumber(nCharStyleCount);
}
} else
{ // Find the current autostyle. Mix attributes if necessary.
SwTextAttr* pCurrentAutoStyle = nullptr;
SwTextAttr* pCurrentCharFormat = nullptr; for ( constauto& rpHint : aInsDelHints )
{ if ( RES_TXTATR_AUTOFMT == rpHint->Which() )
pCurrentAutoStyle = rpHint; elseif ( RES_TXTATR_CHARFMT == rpHint->Which() )
pCurrentCharFormat = rpHint;
}
// #i75750# Remove attributes already set at whole paragraph // #i81764# This should not be applied for no length attributes!!! <-- if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && aNewSet.Count() )
{ const SfxItemSet& rWholeParaAttrSet(rNode.GetSwAttrSet());
std::vector<sal_uInt16> aDeleteWhichIDs;
for (SfxItemIter aIter(aNewSet); !aIter.IsAtEnd(); aIter.NextItem())
{ const SfxPoolItem* pGet(nullptr); if (SfxItemState::SET == rWholeParaAttrSet.GetItemState(aIter.GetCurWhich(), false, &pGet) &&
SfxPoolItem::areSame(pGet, aIter.GetCurItem()))
{ // Do not clear item if the attribute is set in a character format: if (!pCurrentCharFormat || nullptr == CharFormat::GetItem(*pCurrentCharFormat, aIter.GetCurWhich()))
aDeleteWhichIDs.push_back(aIter.GetCurWhich());
}
}
for (auto nDelWhich : aDeleteWhichIDs)
aNewSet.ClearItem(nDelWhich);
}
// Remove old hint Delete( pCurrentAutoStyle );
rNode.DestroyAttr( pCurrentAutoStyle );
// Create new AutoStyle if ( aNewSet.Count() )
pNewAttr = MakeTextAttr( rNode.GetDoc(), aNewSet,
nPorStart, nPorEnd );
} else
{ // Remove any attributes which are already set at the whole paragraph: bool bOptimizeAllowed = true;
// #i75750# Remove attributes already set at whole paragraph // #i81764# This should not be applied for no length attributes!!! <-- if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && pNewStyle->Count() )
{
std::unique_ptr<SfxItemSet> pNewSet;
do
{ const SfxPoolItem* pTmpItem = nullptr; // here direct SfxPoolItem ptr comp was wrong, found using SfxPoolItem::areSame if ( SfxItemState::SET == rWholeParaAttrSet.GetItemState( pItem->Which(), false, &pTmpItem ) &&
SfxPoolItem::areSame(pTmpItem, pItem) )
{ // Do not clear item if the attribute is set in a character format: if ( !pCurrentCharFormat || nullptr == CharFormat::GetItem( *pCurrentCharFormat, pItem->Which() ) )
{ if ( !pNewSet )
pNewSet = pNewStyle->Clone();
pNewSet->ClearItem( pItem->Which() );
}
}
} while ((pItem = aIter2.NextItem()));
// Create new AutoStyle // If there is no current hint and start and end of rNewHint // is ok, we do not need to create a new txtattr. if ( bOptimizeAllowed &&
nPorStart == nThisStart &&
nPorEnd == nThisEnd )
{
pNewAttr = &rNewHint;
bDestroyHint = false;
} elseif ( pNewStyle )
{
pNewAttr = MakeTextAttr( rNode.GetDoc(), *pNewStyle,
nPorStart, nPorEnd );
}
}
}
if ( pNewAttr )
{
Insert( pNewAttr ); // if ( bDestroyHint )
NoteInHistory( pNewAttr, true );
}
if ( bDestroyHint )
rNode.DestroyAttr( &rNewHint );
}
SwTextAttr* MakeRedlineTextAttr( SwDoc & rDoc, SfxPoolItem const & rAttr )
{ // this is intended _only_ for special-purpose redline attributes! switch (rAttr.Which())
{ case RES_CHRATR_COLOR: case RES_CHRATR_WEIGHT: case RES_CHRATR_CJK_WEIGHT: case RES_CHRATR_CTL_WEIGHT: case RES_CHRATR_POSTURE: case RES_CHRATR_CJK_POSTURE: case RES_CHRATR_CTL_POSTURE: case RES_CHRATR_UNDERLINE: case RES_CHRATR_CROSSEDOUT: case RES_CHRATR_CASEMAP: case RES_CHRATR_BACKGROUND: break; default:
assert(!"unsupported redline attribute"); break;
}
// create a SfxPoolItemHolder and return it (will move Item to referenced mode) returnnew SwTextAttrEnd(SfxPoolItemHolder(rDoc.GetAttrPool(), &rAttr), 0, 0);
}
// create new text attribute
SwTextAttr* MakeTextAttr(
SwDoc & rDoc,
SfxPoolItem& rAttr,
sal_Int32 const nStt,
sal_Int32 const nEnd,
CopyOrNewType const bIsCopy,
SwTextNode *const pTextNode )
{ if ( isCHRATR(rAttr.Which()) )
{ // Somebody wants to build a SwTextAttr for a character attribute. // Sorry, this is not allowed any longer. // You'll get a brand new autostyle attribute:
SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aItemSet( rDoc.GetAttrPool() );
aItemSet.Put( rAttr ); return MakeTextAttr( rDoc, aItemSet, nStt, nEnd );
} elseif ( RES_TXTATR_AUTOFMT == rAttr.Which() && static_cast<const SwFormatAutoFormat&>(rAttr).GetStyleHandle()->
GetPool() != &rDoc.GetAttrPool() )
{ // If the attribute is an auto-style which refers to a pool that is // different from rDoc's pool, we have to correct this: const std::shared_ptr<SfxItemSet> & pAutoStyle = static_cast<const SwFormatAutoFormat&>(rAttr).GetStyleHandle();
std::unique_ptr<const SfxItemSet> pNewSet(
pAutoStyle->SfxItemSet::Clone( true, &rDoc.GetAttrPool() ));
SwTextAttr* pNew = MakeTextAttr( rDoc, *pNewSet, nStt, nEnd ); return pNew;
}
// create a SfxPoolItemHolder and use it (will move Item to referenced mode) const SfxPoolItemHolder aHolder(rDoc.GetAttrPool(), &rAttr);
SfxPoolItem& rNew(const_cast<SfxPoolItem&>(*aHolder.getItem()));
pNew = new SwTextCharFormat( aHolder, nStt, nEnd );
} break; case RES_TXTATR_INETFMT:
pNew = new SwTextINetFormat( aHolder, nStt, nEnd ); break;
case RES_TXTATR_FIELD:
pNew = new SwTextField( aHolder, nStt,
rDoc.IsClipBoard() ); break;
case RES_TXTATR_ANNOTATION:
{
pNew = new SwTextAnnotationField( aHolder, nStt, rDoc.IsClipBoard() ); if (bIsCopy == CopyOrNewType::Copy)
{ // On copy of the annotation field do not keep the annotated text range by removing // the relation to its annotation mark (relation established via annotation field's name). // If the annotation mark is also copied, the relation and thus the annotated text range will be reestablished, // when the annotation mark is created and inserted into the document. auto& pField = const_cast<SwPostItField&>(dynamic_cast<const SwPostItField&>(*(pNew->GetFormatField().GetField())));
if (!rDoc.IsInWriterfilterImport())
{ // We set the name here to make the object referencable.
pField.SetName(sw::mark::MarkBase::GenerateNewName(u"__Annotation__"));
} else// Keep the previous behaviour while loading the file.
pField.SetName(SwMarkName());
pField.SetPostItId();
}
} break;
case RES_TXTATR_INPUTFIELD:
pNew = new SwTextInputField( aHolder, nStt, nEnd,
rDoc.IsClipBoard() ); break;
case RES_TXTATR_FLYCNT:
{ // finally, copy the frame format (with content)
pNew = new SwTextFlyCnt( aHolder, nStt ); if ( static_cast<const SwFormatFlyCnt &>(rAttr).GetTextFlyCnt() )
{ // if it has an existing attr then the format must be copied static_cast<SwTextFlyCnt *>(pNew)->CopyFlyFormat( rDoc );
}
} break; case RES_TXTATR_FTN:
pNew = new SwTextFootnote( aHolder, nStt ); // copy note's SeqNo if( static_cast<SwFormatFootnote&>(rAttr).GetTextFootnote() ) static_cast<SwTextFootnote*>(pNew)->SetSeqNo( static_cast<SwFormatFootnote&>(rAttr).GetTextFootnote()->GetSeqRefNo() ); break; case RES_TXTATR_REFMARK:
pNew = nStt == nEnd
? new SwTextRefMark( aHolder, nStt )
: new SwTextRefMark( aHolder, nStt, &nEnd ); break; case RES_TXTATR_TOXMARK:
{
SwTOXMark& rMark = static_cast<SwTOXMark&>(rNew);
// tdf#98868 if the SwTOXType is from a different document that the // target, re-register the TOXMark against a matching SwTOXType from // the target document instead const SwTOXType* pTOXType = rMark.GetTOXType(); if (pTOXType && &pTOXType->GetDoc() != &rDoc)
{
SwTOXType* pToxType = SwHistorySetTOXMark::GetSwTOXType(rDoc, pTOXType->GetType(),
pTOXType->GetTypeName());
rMark.RegisterToTOXType(*pToxType);
}
pNew = new SwTextTOXMark(aHolder, nStt, &nEnd); break;
} case RES_TXTATR_CJK_RUBY:
pNew = new SwTextRuby( aHolder, nStt, nEnd ); break; case RES_TXTATR_META: case RES_TXTATR_METAFIELD:
pNew = SwTextMeta::CreateTextMeta( rDoc.GetMetaFieldManager(), pTextNode,
aHolder,
nStt, nEnd, bIsCopy == CopyOrNewType::Copy ); break; case RES_TXTATR_LINEBREAK:
pNew = new SwTextLineBreak(aHolder, nStt); break; case RES_TXTATR_CONTENTCONTROL:
pNew = SwTextContentControl::CreateTextContentControl(
rDoc, pTextNode,
aHolder,
nStt, nEnd,
bIsCopy == CopyOrNewType::Copy); break; default:
assert(RES_TXTATR_AUTOFMT == rNew.Which());
pNew = new SwTextAttrEnd( aHolder, nStt, nEnd ); break;
}
// delete the text attribute and unregister its item at the pool void SwTextNode::DestroyAttr( SwTextAttr* pAttr )
{ if( !pAttr ) return;
// some things need to be done before deleting the formatting attribute
SwDoc& rDoc = GetDoc(); switch( pAttr->Which() )
{ case RES_TXTATR_FLYCNT:
{
SwFrameFormat* pFormat = pAttr->GetFlyCnt().GetFrameFormat(); if( pFormat ) // set to 0 by Undo?
rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
} break;
case RES_CHRATR_HIDDEN:
SetCalcHiddenCharFlags(); break;
case RES_TXTATR_FTN: static_cast<SwTextFootnote*>(pAttr)->SetStartNode( nullptr ); static_cast<SwFormatFootnote&>(pAttr->GetAttr()).InvalidateFootnote(); break;
case RES_TXTATR_FIELD: case RES_TXTATR_ANNOTATION: case RES_TXTATR_INPUTFIELD: if( !rDoc.IsInDtor() )
{
SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pAttr));
SwFieldType* pFieldType = pAttr->GetFormatField().GetField()->GetTyp();
if (SwFieldIds::Dde != pFieldType->Which()
&& !pTextField->GetpTextNode())
{ break; // was not yet inserted
}
//JP 06-08-95: DDE-fields are an exception
assert(SwFieldIds::Dde == pFieldType->Which() || this == pTextField->GetpTextNode());
// certain fields must update the SwDoc's calculation flags
// Certain fields (like HiddenParaField) must trigger recalculation of visible flag if (GetDoc().FieldCanHideParaWeight(pFieldType->Which()))
SetCalcHiddenParaField();
switch( pFieldType->Which() )
{ case SwFieldIds::HiddenPara: case SwFieldIds::DbSetNumber: case SwFieldIds::GetExp: case SwFieldIds::Database: case SwFieldIds::SetExp: case SwFieldIds::HiddenText: case SwFieldIds::DbNumSet: case SwFieldIds::DbNextSet: if( !rDoc.getIDocumentFieldsAccess().IsNewFieldLst() && GetNodes().IsDocNodes() )
rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField); break; case SwFieldIds::Dde: if (GetNodes().IsDocNodes() && pTextField->GetpTextNode()) static_cast<SwDDEFieldType*>(pFieldType)->DecRefCnt(); break; case SwFieldIds::Postit:
{ const_cast<SwFormatField&>(pAttr->GetFormatField()).Broadcast(
SwFormatFieldHint(&pTextField->GetFormatField(), SwFormatFieldHintWhich::REMOVED)); break;
} default: break;
}
} static_cast<SwFormatField&>(pAttr->GetAttr()).InvalidateField(); break;
case RES_TXTATR_TOXMARK: static_cast<SwTOXMark&>(pAttr->GetAttr()).InvalidateTOXMark(); break;
case RES_TXTATR_REFMARK: static_cast<SwFormatRefMark&>(pAttr->GetAttr()).InvalidateRefMark(); break;
case RES_TXTATR_META: case RES_TXTATR_METAFIELD:
{ auto pTextMeta = static_txtattr_cast<SwTextMeta*>(pAttr);
SwFormatMeta & rFormatMeta( static_cast<SwFormatMeta &>(pTextMeta->GetAttr()) ); if (::sw::Meta* pMeta = rFormatMeta.GetMeta())
{ if (SwDocShell* pDocSh = rDoc.GetDocShell())
{ static constexpr OUStringLiteral metaNS(u"urn:bails"); const css::uno::Reference<css::rdf::XResource> xSubject = pMeta->MakeUnoObject();
rtl::Reference<SwXTextDocument> xModel = pDocSh->GetBaseModel();
SwRDFHelper::clearStatements(xModel, metaNS, xSubject);
}
}
static_txtattr_cast<SwTextMeta*>(pAttr)->ChgTextNode(nullptr);
} break; case RES_TXTATR_CONTENTCONTROL:
{
static_txtattr_cast<SwTextContentControl*>(pAttr)->ChgTextNode(nullptr); break;
}
default: break;
}
SwTextAttr::Destroy( pAttr );
}
SwTextAttr* SwTextNode::InsertItem(
SfxPoolItem& rAttr, const sal_Int32 nStart, const sal_Int32 nEnd, const SetAttrMode nMode )
{ // character attributes will be inserted as automatic styles:
assert( !isCHRATR(rAttr.Which()) && "AUTOSTYLES - " "SwTextNode::InsertItem should not be called with character attributes");
if ( pNew )
{ constbool bSuccess( InsertHint( pNew, nMode ) ); // N.B.: also check that the hint is actually in the hints array, // because hints of certain types may be merged after successful // insertion, and thus destroyed! if (!bSuccess || !m_pSwpHints->Contains( pNew ))
{ return nullptr;
}
}
return pNew;
}
// take ownership of pAttr; if insertion fails, delete pAttr bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode )
{ bool bHiddenPara = false;
// translate from SetAttrMode to InsertMode (for hints with CH_TXTATR) const SwInsertFlags nInsertFlags =
(nMode & SetAttrMode::NOHINTEXPAND)
? SwInsertFlags::NOHINTEXPAND
: (nMode & SetAttrMode::FORCEHINTEXPAND)
? (SwInsertFlags::FORCEHINTEXPAND | SwInsertFlags::EMPTYEXPAND)
: SwInsertFlags::EMPTYEXPAND;
// need this after TryInsertHint, when pAttr may be deleted const sal_Int32 nStart( pAttr->GetStart() ); constbool bDummyChar( pAttr->HasDummyChar() ); if (bDummyChar)
{
SetAttrMode nInsMode = nMode; switch( pAttr->Which() )
{ case RES_TXTATR_FLYCNT:
{
SwTextFlyCnt *pFly = static_cast<SwTextFlyCnt *>(pAttr);
SwFrameFormat* pFormat = pAttr->GetFlyCnt().GetFrameFormat(); if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
{ // Need to insert char first, because SetAnchor() reads // GetStart(). //JP 11.05.98: if the anchor is already set correctly, // fix it after inserting the char, so that clients don't // have to worry about it. const SwFormatAnchor* pAnchor = pFormat->GetItemIfSet( RES_ANCHOR, false );
SwContentIndex aIdx( this, pAttr->GetStart() ); const OUString c(GetCharOfTextAttr(*pAttr));
OUString const ins( InsertText(c, aIdx, nInsertFlags) ); if (ins.isEmpty())
{ // do not record deletion of Format!
::sw::UndoGuard const ug(
pFormat->GetDoc().GetIDocumentUndoRedo());
DestroyAttr(pAttr); returnfalse; // text node full :(
}
nInsMode |= SetAttrMode::NOTXTATRCHR;
// format pointer could have changed in SetAnchor, // when copying to other docs!
pFormat = pAttr->GetFlyCnt().GetFrameFormat();
SwDoc& rDoc = pFormat->GetDoc();
// OD 26.06.2003 - allow drawing objects in header/footer. // But don't allow control objects in header/footer if( RES_DRAWFRMFMT == pFormat->Which() &&
rDoc.IsInHeaderFooter( *pFormat->GetAnchor().GetAnchorNode() ) )
{ bool bCheckControlLayer = false;
pFormat->CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer)); if( bCheckControlLayer )
{ // This should not be allowed, prevent it here. // The dtor of the SwTextAttr does not delete the // char, so delete it explicitly here. if( SetAttrMode::NOTXTATRCHR & nInsMode )
{ // delete the char from the string
assert(CH_TXTATR_BREAKWORD == m_Text[pAttr->GetStart()]
|| CH_TXTATR_INWORD == m_Text[pAttr->GetStart()]);
m_Text = m_Text.replaceAt(pAttr->GetStart(), 1, u""); // Update SwContentIndexes
SwContentIndex aTmpIdx( this, pAttr->GetStart() );
Update(aTmpIdx, 1, UpdateMode::Negative);
} // do not record deletion of Format!
::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
DestroyAttr( pAttr ); returnfalse;
}
} break;
}
case RES_TXTATR_FTN :
{ // Footnotes: create text node and put it into Inserts-section
SwDoc& rDoc = GetDoc();
SwNodes &rNodes = rDoc.GetNodes();
// check that footnote is inserted into body or redline section bool bSplitFly = false; if (StartOfSectionIndex() < rNodes.GetEndOfAutotext().GetIndex()
&& StartOfSectionIndex() >= rNodes.GetEndOfInserts().GetIndex())
{ // This is a frame, header or footer. Check if it's a split frame.
SwFrameFormat* pFlyFormat = StartOfSectionNode()->GetFlyFormat();
bSplitFly = pFlyFormat && pFlyFormat->GetFlySplit().GetValue();
}
if (StartOfSectionIndex() < rNodes.GetEndOfAutotext().GetIndex() && !bSplitFly)
{ // This should not be allowed, prevent it here. // The dtor of the SwTextAttr does not delete the // char, so delete it explicitly here. if( SetAttrMode::NOTXTATRCHR & nInsMode )
{ // delete the char from the string
assert(CH_TXTATR_BREAKWORD == m_Text[pAttr->GetStart()]
|| CH_TXTATR_INWORD == m_Text[pAttr->GetStart()]);
m_Text = m_Text.replaceAt(pAttr->GetStart(), 1, u""); // Update SwContentIndexes
SwContentIndex aTmpIdx( this, pAttr->GetStart() );
Update(aTmpIdx, 1, UpdateMode::Negative);
}
DestroyAttr( pAttr ); returnfalse;
}
if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
{ // must insert first, to prevent identical indexes // that could later prevent insertion into SwDoc's // footnote array
SwContentIndex aNdIdx( this, pAttr->GetStart() ); const OUString c(GetCharOfTextAttr(*pAttr));
OUString const ins( InsertText(c, aNdIdx, nInsertFlags) ); if (ins.isEmpty())
{
DestroyAttr(pAttr); returnfalse; // text node full :(
}
nInsMode |= SetAttrMode::NOTXTATRCHR;
}
// insert into SwDoc's footnote index array
SwTextFootnote* pTextFootnote = nullptr; if( !bNewFootnote )
{ // moving an existing footnote (e.g. SplitNode) for( size_t n = 0; n < rDoc.GetFootnoteIdxs().size(); ++n ) if( pAttr == rDoc.GetFootnoteIdxs()[n] )
{ // assign new index by removing and re-inserting
pTextFootnote = rDoc.GetFootnoteIdxs()[n];
rDoc.GetFootnoteIdxs().erase( rDoc.GetFootnoteIdxs().begin() + n ); break;
} // if the Undo set the StartNode, the Index isn't // in the doc's array yet!
} if( !pTextFootnote )
pTextFootnote = static_cast<SwTextFootnote*>(pAttr);
// to update the numbers and for sorting, the Node must be set static_cast<SwTextFootnote*>(pAttr)->ChgTextNode( this );
// do not insert footnote in redline section into footnote array if (StartOfSectionIndex() > rNodes.GetEndOfRedlines().GetIndex() || bSplitFly)
{ constbool bSuccess = rDoc.GetFootnoteIdxs().insert(pTextFootnote).second;
OSL_ENSURE( bSuccess, "FootnoteIdx not inserted." );
}
rDoc.GetFootnoteIdxs().UpdateFootnote( *this ); static_cast<SwTextFootnote*>(pAttr)->SetSeqRefNo();
} break;
case RES_TXTATR_FIELD:
{ // trigger notification for relevant fields, like HiddenParaFields if (GetDoc().FieldCanHideParaWeight(
pAttr->GetFormatField().GetField()->GetTyp()->Which()))
{
bHiddenPara = true;
}
} break; case RES_TXTATR_LINEBREAK :
{ static_cast<SwTextLineBreak*>(pAttr)->SetTextNode(this);
} break;
} // CH_TXTATR_* are inserted for SwTextHints without EndIndex // If the caller is SwTextNode::Copy, the char has already been copied, // and SETATTR_NOTXTATRCHR prevents inserting it again here. if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
{
SwContentIndex aIdx( this, pAttr->GetStart() );
OUString const ins( InsertText(OUString(GetCharOfTextAttr(*pAttr)),
aIdx, nInsertFlags) ); if (ins.isEmpty())
{
DestroyAttr(pAttr); returnfalse; // text node full :(
}
// adjust end of hint to account for inserted CH_TXTATR const sal_Int32* pEnd(pAttr->GetEnd()); if (pEnd)
{
pAttr->SetEnd(*pEnd + 1);
}
if (pAttr->Which() == RES_TXTATR_CONTENTCONTROL)
{ // Content controls have a dummy character at their end as well.
SwContentIndex aEndIdx(this, *pAttr->GetEnd());
OUString aEnd
= InsertText(OUString(GetCharOfTextAttr(*pAttr)), aEndIdx, nInsertFlags); if (aEnd.isEmpty())
{
DestroyAttr(pAttr); returnfalse;
}
if (bInsertHint)
{ // Handle the invariant that a plain text content control has the same character formatting // for all of its content. auto* pTextContentControl = static_txtattr_cast<SwTextContentControl*>(
GetTextAttrAt(pAttr->GetStart(), RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent)); // If the caller is SwTextNode::CopyText, we just copy an existing attribute, no need to // correct it. if (pTextContentControl && !(nMode & SetAttrMode::NOTXTATRCHR))
{ auto& rFormatContentControl
= static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
std::shared_ptr<SwContentControl> pContentControl
= rFormatContentControl.GetContentControl(); if (pAttr->End() != nullptr && pContentControl->GetPlainText())
{ if (pAttr->GetStart() > pTextContentControl->GetStart())
{
pAttr->SetStart(pTextContentControl->GetStart());
} if (*pAttr->End() < *pTextContentControl->End())
{
pAttr->SetEnd(*pTextContentControl->End());
}
}
}
}
if ( !bRet )
{ if ( bDummyChar
&& !(SetAttrMode::NOTXTATRCHR & nMode) )
{ // undo insertion of dummy character // N.B. cannot insert the dummy character after inserting the hint, // because if the hint has no extent it will be moved in InsertText, // resulting in infinite recursion
assert((CH_TXTATR_BREAKWORD == m_Text[nStart] ||
CH_TXTATR_INWORD == m_Text[nStart] ));
SwContentIndex aIdx( this, nStart );
EraseText( aIdx, 1 );
}
bool SwTextNode::IsIgnoredCharFormatForNumbering(const sal_uInt16 nWhich, bool bIsCharStyle)
{ // LO can save the char background as either shading or highlight, so check which mode is currently chosen. // Shading does not affect the numbering. Highlighting does (but isn't allowed in a char style). if (nWhich == RES_CHRATR_BACKGROUND) return bIsCharStyle || !officecfg::Office::Common::Filter::Microsoft::Export::CharBackgroundToHighlighting::get();
// Set these attributes on SwTextNode. If they apply to the entire paragraph // text, set them in the SwTextNode's item set (SwContentNode::SetAttr). bool SwTextNode::SetAttr( const SfxItemSet& rSet, const sal_Int32 nStt, const sal_Int32 nEnd, const SetAttrMode nMode,
SwTextAttr **ppNewTextAttr )
{ if( !rSet.Count() ) returnfalse;
// entire paragraph if ( !nStt && (nEnd == m_Text.getLength()) &&
!(nMode & SetAttrMode::NOFORMATATTR ) )
{ // if the node already has CharFormat hints, the new attributes must // be set as hints too to override those. bool bHasCharFormats = false; if ( HasHints() )
{ for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
{ if ( m_pSwpHints->Get( n )->IsCharFormatAttr() )
{
bHasCharFormats = true; break;
}
}
}
if( !bHasCharFormats )
{
aTextSet.Put( rSet ); // If there are any character attributes in rSet, // we want to set them at the paragraph: if( aTextSet.Count() != rSet.Count() )
{ constbool bRet = SetAttr( rSet ); if( !aTextSet.Count() ) return bRet;
}
SwTextAttr *const pNew = MakeTextAttr( GetDoc(), const_cast<SfxPoolItem&>(*pItem), nStt, nEnd ); if ( pNew )
{ // store the first one we create into the pp if (ppNewTextAttr && !*ppNewTextAttr)
*ppNewTextAttr = pNew; if ( nEnd != nStt && !pNew->GetEnd() )
{
OSL_FAIL("Attribute without end, but area marked");
DestroyAttr( pNew ); // do not insert
} elseif ( InsertHint( pNew, nMode ) )
{
++nCount;
}
}
}
}
}
}
pItem = aIter.NextItem();
} while(pItem);
/* If multiple attributes overlap, the last one wins! ProbablythiscanonlyhappenbetweenaRES_TXTATR_INETFMTandoneofthe otherhints,becauseBuildPortionsensuresthatCHARFMT/AUTOFMTdon't overlap.ButtheremaybemultipleCHARFMT/AUTOFMTwithexactlythesame start/end,sortedbyBuildPortions,inwhichcasethesamelogicapplies.
1234567890123456789 |------------|Font1 |------|Font2 ^^ |--|queryrange:->Font2
*/ // merge into set
rSet.Put( rAttr );
}
// request the attributes of the TextNode at the range bool SwTextNode::GetParaAttr(SfxItemSet& rSet, sal_Int32 nStt, sal_Int32 nEnd, constbool bOnlyTextAttr, constbool bGetFromChrFormat, constbool bMergeIndentValuesOfNumRule,
SwRootFrame const*const pLayout) const
{
assert(!rSet.Count()); // handled inconsistently, typically an error?
if (pLayout && pLayout->HasMergedParas())
{ if (GetRedlineMergeFlag() == SwNode::Merge::Hidden)
{ returnfalse; // ignore deleted node
}
}
// get the node's automatic attributes
SfxItemSet aFormatSet( *rSet.GetPool(), rSet.GetRanges() ); if (!bOnlyTextAttr)
{
SwTextNode const& rParaPropsNode(
sw::GetAttrMerged(aFormatSet, *this, pLayout)); if (bMergeIndentValuesOfNumRule)
{
lcl_MergeListLevelIndentAsLRSpaceItem(rParaPropsNode, aFormatSet);
}
}
if( HasHints() )
{ // First, check which text attributes are valid in the range. // cases: // Ambiguous, if // * the attribute is wholly contained in the range // * the attribute end is in the range // * the attribute start is in the range // Unambiguous (merge into set), if // * the attribute wholly contains the range // Ignored, if // * the attribute is wholly outside the range
if (nStt == nEnd) // no range:
{ for (size_t n = 0; n < nSize; ++n)
{ const SwTextAttr* pHt = m_pSwpHints->Get(n); const sal_Int32 nAttrStart = pHt->GetStart(); if (nAttrStart > nEnd) // behind the range break;
const sal_Int32* pAttrEnd = pHt->End(); if ( ! pAttrEnd ) // no attributes without end continue;
if (pAttrArr)
{ for (size_t n = 0; n < coArrSz; ++n)
{ const SwPoolItemEndPair& rItemPair = (*pAttrArr)[ n ]; if( rItemPair.mpItem && !IsInvalidItem(rItemPair.mpItem) )
{ const sal_uInt16 nWh =
o3tl::narrowing<sal_uInt16>(n + RES_CHRATR_BEGIN);
if (nEnd <= rItemPair.mnEndPos) // behind or exactly end
{ if( *rItemPair.mpItem != aFormatSet.Get( nWh ) )
(*fnMergeAttr)( rSet, *rItemPair.mpItem );
} else // ambiguous
rSet.InvalidateItem( nWh );
}
}
}
} if( aFormatSet.Count() )
{ // remove all from the format-set that are also set in the text-set
aFormatSet.Differentiate( rSet );
}
}
if (aFormatSet.Count())
{ // now "merge" everything
rSet.Put( aFormatSet );
}
/** Removes from io_rAttrSet all items that are set by style on the givenspan.
*/ struct RemovePresentAttrs
{ explicit RemovePresentAttrs(SfxItemSet& io_rAttrSet)
: m_rAttrSet(io_rAttrSet)
{
}
// ITEM: SfxItemIter and removing SfxPoolItems:
std::vector<sal_uInt16> aDeleteWhichIDs;
for (SfxItemIter aIter(m_rAttrSet); !aIter.IsAtEnd(); aIter.NextItem())
{ if (CharFormat::IsItemIncluded(aIter.GetCurWhich(), pAutoStyle))
{
aDeleteWhichIDs.push_back(aIter.GetCurWhich());
}
}
for (auto nDelWhich : aDeleteWhichIDs)
m_rAttrSet.ClearItem(nDelWhich);
}
private:
SfxItemSet& m_rAttrSet;
};
/** Collects all style-covered spans from i_rHints to o_rSpanMap. In additioninsertsdummyspanswithpointertoformatequalto0for allgaps(i.e.spansnotcoveredbyanystyle).Thissimplifies creationofautostylesforallneededspans,butitmeansallcode thattriestoaccessthepointerhastocheckifit'snon-null!
*/ void
lcl_CollectHintSpans(const SwpHints& i_rHints, const sal_Int32 nLength,
AttrSpanMap_t& o_rSpanMap)
{
sal_Int32 nLastEnd(0);
for (size_t i = 0; i < i_rHints.Count(); ++i)
{ const SwTextAttr* pHint = i_rHints.Get(i); const sal_uInt16 nWhich(pHint->Which()); if (nWhich == RES_TXTATR_CHARFMT || nWhich == RES_TXTATR_AUTOFMT)
{ const AttrSpan_t aSpan(pHint->GetStart(), *pHint->End());
o_rSpanMap.emplace(aSpan, pHint);
// < not != because there may be multiple CHARFMT at same range if (nLastEnd < aSpan.first)
{ // insert dummy span covering the gap
o_rSpanMap.emplace( AttrSpan_t(nLastEnd, aSpan.first), nullptr );
}
nLastEnd = aSpan.second;
}
}
// no hints at the end (special case: no hints at all in i_rHints) if (nLastEnd != nLength && nLength != 0)
{
o_rSpanMap.emplace(AttrSpan_t(nLastEnd, nLength), nullptr);
}
}
/** Does the hard work of SwTextNode::FormatToTextAttr: the real conversion ofitemstoautomaticstyles.
*/ void
SwTextNode::impl_FormatToTextAttr(const SfxItemSet& i_rAttrSet)
{ typedef AttrSpanMap_t::iterator AttrSpanMap_iterator_t;
AttrSpanMap_t aAttrSpanMap;
// 2b. Insert automatic style containing the collected attributes
if (aCurSet.Count() != 0)
{
AttrSpanMap_iterator_t aAutoStyleIt(
std::find_if(aRange.first, aRange.second, IsAutoStyle())); if (aAutoStyleIt != aRange.second)
{ // there already is an automatic style on that span: // create new one and remove the original one
SwTextAttr* const pAutoStyle(const_cast<SwTextAttr*>(aAutoStyleIt->second)); const std::shared_ptr<SfxItemSet> pOldStyle( static_cast<const SwFormatAutoFormat&>(
pAutoStyle->GetAttr()).GetStyleHandle());
aCurSet.Put(*pOldStyle);
// remove the old hint
m_pSwpHints->Delete(pAutoStyle);
DestroyAttr(pAutoStyle);
}
m_pSwpHints->Insert(
MakeTextAttr(GetDoc(), aCurSet,
aCurRange->first.first, aCurRange->first.second));
}
aCurRange = aRange.second;
}
// hints were directly inserted, so need to fix the Ignore flags now
m_pSwpHints->MergePortions(*this);
// 3. Clear items from the node
std::vector<sal_uInt16> aClearedIds;
lcl_FillWhichIds(i_rAttrSet, aClearedIds);
ClearItemsFromAttrSet(aClearedIds);
}
if( pNd == this )
{
impl_FormatToTextAttr(aThisSet);
} else
{ // There are five possible combinations of items from this and // pNd (pNd is the 'main' node):
// case pNd this action
// 1 - - do nothing // 2 - a convert item to attr of this // 3 a - convert item to attr of pNd // 4 a a clear item in this // 5 a b convert item to attr of this
if (RES_TXTATR_FIELD == nWhich)
{
// see also SwTextFrame::IsHiddenNow()
const SwFormatField& rField = pTextHt->GetFormatField();
int nCurWeight = m_rParent.GetDoc().FieldCanHideParaWeight(rField.GetField()->GetTyp()->Which());
if (nCurWeight > nNewResultWeight)
{
nNewResultWeight = nCurWeight;
bNewHiddenByParaField = m_rParent.GetDoc().FieldHidesPara(*rField.GetField());
}
else if (nCurWeight == nNewResultWeight && bNewHiddenByParaField)
{
// Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
// takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
// care about fields of higher weight.
bNewHiddenByParaField = m_rParent.GetDoc().FieldHidesPara(*rField.GetField());
}
}
}
SetHiddenByParaField(bNewHiddenByParaField);
return bOldHiddenByParaField != bNewHiddenByParaField;
}
// get portions by start position:
for ( size_t i = 0; i < Count(); ++i )
{
SwTextAttr *pHt = Get( i );
if ( RES_TXTATR_CHARFMT != pHt->Which() &&
RES_TXTATR_AUTOFMT != pHt->Which() )
//&&
//RES_TXTATR_INETFMT != pHt->Which() )
continue;
bool isRsidOnlyAutoFormat(false);
// check for RSID-only AUTOFMT
if (RES_TXTATR_AUTOFMT == pHt->Which())
{
std::shared_ptr<SfxItemSet> const & pSet(
pHt->GetAutoFormat().GetStyleHandle());
if ((pSet->Count() == 1) && pSet->GetItem(RES_CHRATR_RSID, false))
{
// fdo#70201: eliminate no-extent RSID-only AUTOFMT
// could be produced by ReplaceText or (maybe?) RstAttr
if (pHt->GetStart() == *pHt->GetEnd())
{
DeleteAtPos(i); // kill it without History!
SwTextAttr::Destroy(pHt);
--i;
continue;
}
// fdo#52028: this one has _only_ RSID => ignore it completely
if (!pHt->IsFormatIgnoreStart() || !pHt->IsFormatIgnoreEnd())
{
NoteInHistory(pHt);
pHt->SetFormatIgnoreStart(true);
pHt->SetFormatIgnoreEnd (true);
NoteInHistory(pHt, true);
}
isRsidOnlyAutoFormat = true;
}
}
if (pHt->GetStart() == *pHt->GetEnd())
{
// no-length hints are a disease. ignore them here.
// the SwAttrIter::SeekFwd will not call Rst/Chg for them.
continue;
}
// we add data strictly in-order, so we can forward-search the vector
auto equal_range = [](PortionMap::const_iterator startIt, PortionMap::const_iterator endIt, int i)
{
auto it1 = startIt;
while (it1 != endIt && it1->nKey < i)
++it1;
auto it2 = it1;
while (it2 != endIt && it2->nKey == i)
++it2;
return std::pair<PortionMap::const_iterator, PortionMap::const_iterator>{ it1, it2 };
};
// check if portion i can be merged with portion i+1:
// note: need to include i=0 to set IgnoreStart and j=nKey+1 to reset
// IgnoreEnd at first / last portion
int i = 0;
int j = i + 1;
// Store this outside the loop, because we limit the search area on subsequent searches.
std::pair< PortionMap::const_iterator, PortionMap::const_iterator > aRange1 { aPortionMap.begin(), aPortionMap.begin() + aPortionMap.size() };
while ( i <= nKey )
{
aRange1 = equal_range( aRange1.first, aPortionMap.begin() + aPortionMap.size(), i );
// start the search for this one from where the first search ended.
std::pair< PortionMap::const_iterator, PortionMap::const_iterator > aRange2
= equal_range( aRange1.second, aPortionMap.begin() + aPortionMap.size(), j );
if (MATCH == eMerge)
{
// important: delete second range so any IgnoreStart on the first
// range is still valid
// erase all elements with key i + 1
sal_Int32 nNewPortionEnd = 0;
for ( auto aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 )
{
SwTextAttr *const p2 = aIter2->pTextAttr;
nNewPortionEnd = *p2->GetEnd();
// robust: check if deletion actually took place before destroying attribute:
if ( Count() < nCountBeforeDelete )
rNode.DestroyAttr( p2 );
}
aPortionMap.erase( aRange2.first, aRange2.second );
++j;
// change all attributes with key i
aRange1 = equal_range( aRange1.first, aPortionMap.begin() + aPortionMap.size(), i );
for ( auto aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 )
{
SwTextAttr *const p1 = aIter1->pTextAttr;
NoteInHistory( p1 );
p1->SetEnd(nNewPortionEnd);
NoteInHistory( p1, true );
bRet = true;
}
if (bRet)
{
Resort();
}
}
else
{
// when not merging the ignore flags need to be either set or reset
// (reset too in case one of the autofmts was recently changed)
bool const bSetIgnoreFlag(DIFFER_ONLY_RSID == eMerge);
for (auto aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1)
{
if (!aIter1->isRsidOnlyAutoFormat) // already set above, don't change
{
SwTextAttr *const pCurrent(aIter1->pTextAttr);
if (pCurrent->IsFormatIgnoreEnd() != bSetIgnoreFlag)
{
NoteInHistory(pCurrent);
pCurrent->SetFormatIgnoreEnd(bSetIgnoreFlag);
NoteInHistory(pCurrent, true);
}
}
}
for (auto aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2)
{
if (!aIter2->isRsidOnlyAutoFormat) // already set above, don't change
{
SwTextAttr *const pCurrent(aIter2->pTextAttr);
if (pCurrent->IsFormatIgnoreStart() != bSetIgnoreFlag)
{
NoteInHistory(pCurrent);
pCurrent->SetFormatIgnoreStart(bSetIgnoreFlag);
NoteInHistory(pCurrent, true);
}
}
}
i = j; // ++i not enough: i + 1 may have been deleted (MATCH)!
++j;
}
}
return bRet;
}
static MergeResult lcl_Compare_Attributes(
int i, int j,
const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange1,
const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange2,
std::vector<bool>& RsidOnlyAutoFormatFlagMap)
{
PortionMap::const_iterator aIter1 = aRange1.first;
PortionMap::const_iterator aIter2 = aRange2.first;
// if both have one they could be equal, but not if only one has it
bool const bSkipRsidOnlyAutoFormat(nAttributesInPor1 != nAttributesInPor2);
// this loop needs to handle the case where one has a CHARFMT and the
// other CHARFMT + RSID-only AUTOFMT, so...
// want to skip over RSID-only AUTOFMT here, hence the -1
if (!((nAttributesInPor1 - (isRsidOnlyAutoFormat1 ? 1 : 0)) ==
(nAttributesInPor2 - (isRsidOnlyAutoFormat2 ? 1 : 0))
&& (nAttributesInPor1 != 0 || nAttributesInPor2 != 0)))
{
return DIFFER;
}
MergeResult eMerge(MATCH);
// _if_ there is one element more either in aRange1 or aRange2
// it _must_ be an RSID-only AUTOFMT, which can be ignored here...
// But if both have RSID-only AUTOFMT they could be equal, no skip!
while (aIter1 != aRange1.second || aIter2 != aRange2.second)
{
// first of all test if there's no gap (before skipping stuff!)
if (aIter1 != aRange1.second && aIter2 != aRange2.second &&
*aIter1->pTextAttr->End() < aIter2->pTextAttr->GetStart())
{
return DIFFER;
}
// skip it - cannot be equal if bSkipRsidOnlyAutoFormat is set
if (bSkipRsidOnlyAutoFormat
&& aIter1 != aRange1.second && aIter1->isRsidOnlyAutoFormat)
{
assert(DIFFER != eMerge);
eMerge = DIFFER_ONLY_RSID;
++aIter1;
continue;
}
if (bSkipRsidOnlyAutoFormat
&& aIter2 != aRange2.second && aIter2->isRsidOnlyAutoFormat)
{
assert(DIFFER != eMerge);
eMerge = DIFFER_ONLY_RSID;
++aIter2;
continue;
}
assert(aIter1 != aRange1.second && aIter2 != aRange2.second);
SwTextAttr const*const p1 = aIter1->pTextAttr;
SwTextAttr const*const p2 = aIter2->pTextAttr;
if (p1->Which() != p2->Which())
{
return DIFFER;
}
if (!(*p1 == *p2))
{
// fdo#52028: for auto styles, check if they differ only
// in the RSID, which should have no effect on text layout
if (RES_TXTATR_AUTOFMT != p1->Which())
return DIFFER;
// check if there is already a character format and adjust the sort numbers
static void lcl_CheckSortNumber( const SwpHints& rHints, SwTextCharFormat& rNewCharFormat )
{
const sal_Int32 nHtStart = rNewCharFormat.GetStart();
const sal_Int32 nHtEnd = *rNewCharFormat.GetEnd();
sal_uInt16 nSortNumber = 0;
for ( size_t i = 0; i < rHints.Count(); ++i )
{
const SwTextAttr* pOtherHt = rHints.Get(i);
if ( nSortNumber > 0 )
rNewCharFormat.SetSortNumber( nSortNumber );
}
/*
* Try to insert the new hint.
* Depending on the type of the hint, this either always succeeds, or may fail.
* Depending on the type of the hint, other hints may be deleted or
* overwritten.
* The return value indicates successful insertion.
*/
bool SwpHints::TryInsertHint(
SwTextAttr* const pHint,
SwTextNode &rNode,
const SetAttrMode nMode )
{
if ( MAX_HINTS <= Count() ) // we're sorry, this flight is overbooked...
{
OSL_FAIL("hints array full :-(");
rNode.DestroyAttr(pHint);
return false;
}
switch( nWhich )
{
case RES_TXTATR_CHARFMT:
{
// Check if character format contains hidden attribute:
const SwCharFormat* pFormat = pHint->GetCharFormat().GetCharFormat();
if ( SfxItemState::SET == pFormat->GetItemState( RES_CHRATR_HIDDEN ) )
rNode.SetCalcHiddenCharFlags();
static_txtattr_cast<SwTextCharFormat*>(pHint)->ChgTextNode( &rNode );
break;
}
// #i75430# Recalc hidden flags if necessary
case RES_TXTATR_AUTOFMT:
{
std::shared_ptr<SfxItemSet> const & pSet( pHint->GetAutoFormat().GetStyleHandle() );
if (pHint->GetStart() == *pHint->GetEnd())
{
if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
{ // empty range RSID-only hints could cause trouble, there's no
rNode.DestroyAttr(pHint); // need for them so don't insert
return false;
}
}
// Check if auto style contains hidden attribute:
const SfxPoolItem* pHiddenItem = CharFormat::GetItem( *pHint, RES_CHRATR_HIDDEN );
if ( pHiddenItem )
rNode.SetCalcHiddenCharFlags();
// fdo#71556: populate aWhichFormatAttr member of SwMsgPoolItem
pSet->CollectHasItems(aWhichSublist);
break;
}
case RES_TXTATR_INETFMT:
static_txtattr_cast<SwTextINetFormat*>(pHint)->InitINetFormat(rNode);
break;
case RES_TXTATR_FIELD:
case RES_TXTATR_ANNOTATION:
case RES_TXTATR_INPUTFIELD:
{
SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
bool bDelFirst = nullptr != pTextField->GetpTextNode();
pTextField->ChgTextNode( &rNode );
SwDoc& rDoc = rNode.GetDoc();
const SwField* pField = pTextField->GetFormatField().GetField();
if( !rDoc.getIDocumentFieldsAccess().IsNewFieldLst() )
{
// certain fields must update the SwDoc's calculation flags
switch( pField->GetTyp()->Which() )
{
case SwFieldIds::Database:
case SwFieldIds::SetExp:
case SwFieldIds::HiddenPara:
case SwFieldIds::HiddenText:
case SwFieldIds::DbNumSet:
case SwFieldIds::DbNextSet:
{
if( bDelFirst )
rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField);
if( rNode.GetNodes().IsDocNodes() )
rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(true, *pTextField);
}
break;
case SwFieldIds::Dde:
if( rNode.GetNodes().IsDocNodes() )
static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt();
break;
default: break;
}
}
// insert into real document's nodes-array?
if( rNode.GetNodes().IsDocNodes() )
{
bool bInsFieldType = false;
switch( pField->GetTyp()->Which() )
{
case SwFieldIds::SetExp:
bInsFieldType = static_cast<SwSetExpFieldType*>(pField->GetTyp())->IsDeleted();
if( nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pField->GetTyp())->GetType() )
{
// register the field at its FieldType before setting
// the sequence reference number!
SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(
rDoc.getIDocumentFieldsAccess().InsertFieldType( *pField->GetTyp() ) );
if( pFieldType != pField->GetTyp() )
{
SwFormatField* pFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
pFormatField->RegisterToFieldType( *pFieldType );
pFormatField->GetField()->ChgTyp( pFieldType );
}
pFieldType->SetSeqRefNo( *const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField)) );
}
break;
case SwFieldIds::User:
bInsFieldType = static_cast<SwUserFieldType*>(pField->GetTyp())->IsDeleted();
break;
case SwFieldIds::Dde:
if( rDoc.getIDocumentFieldsAccess().IsNewFieldLst() )
static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt();
bInsFieldType = static_cast<SwDDEFieldType*>(pField->GetTyp())->IsDeleted();
break;
case SwFieldIds::Postit:
if ( rDoc.GetDocShell() )
{
rDoc.GetDocShell()->Broadcast( SwFormatFieldHint(
&pTextField->GetFormatField(), SwFormatFieldHintWhich::INSERTED));
}
break;
default: break;
}
if( bInsFieldType )
rDoc.getIDocumentFieldsAccess().InsDeletedFieldType( *pField->GetTyp() );
}
}
break;
case RES_TXTATR_FTN :
static_cast<SwTextFootnote*>(pHint)->ChgTextNode( &rNode );
break;
case RES_TXTATR_REFMARK:
static_txtattr_cast<SwTextRefMark*>(pHint)->ChgTextNode( &rNode );
if( rNode.GetNodes().IsDocNodes() )
{
// search for a reference with the same name
SwTextAttr* pTmpHt;
size_t n = 0, nEnd = Count();
while (n < nEnd)
{
const sal_Int32 *pTmpHtEnd;
const sal_Int32 *pTmpHintEnd;
if (RES_TXTATR_REFMARK == (pTmpHt = Get(n))->Which() &&
pHint->GetAttr() == pTmpHt->GetAttr() &&
nullptr != ( pTmpHtEnd = pTmpHt->GetEnd() ) &&
nullptr != ( pTmpHintEnd = pHint->GetEnd() ) )
{
SwComparePosition eCmp = ::ComparePosition(
pTmpHt->GetStart(), *pTmpHtEnd,
pHint->GetStart(), *pTmpHintEnd );
bool bDelOld = true, bChgStart = false, bChgEnd = false;
switch( eCmp )
{
case SwComparePosition::Before:
case SwComparePosition::Behind: bDelOld = false; break;
case SwComparePosition::Outside: bChgStart = bChgEnd = true; break;
case SwComparePosition::CollideEnd:
case SwComparePosition::OverlapBefore: bChgStart = true; break;
case SwComparePosition::CollideStart:
case SwComparePosition::OverlapBehind: bChgEnd = true; break;
default: break;
}
// special handling for SwTextAttrs without end:
// 1) they cannot overlap
// 2) if two fields are adjacent, they must not be merged into one
// this is guaranteed by inserting a CH_TXTATR_* into the paragraph text!
sal_Int32 nHtStart = pHint->GetStart();
if( !pHtEnd )
{
Insert( pHint );
NoteInHistory(pHint, true);
CalcFlags();
#ifdef DBG_UTIL
if( !rNode.GetDoc().IsInReading() )
CHECK;
#endif
// ... and notify listeners
if(rNode.HasWriterListeners())
{
SwUpdateAttr aHint(
nHtStart,
nHtStart,
nWhich);
// just swap the nonsense:
pHint->SetStart(*pHtEnd);
pHint->SetEnd(nHtStart);
nHtStart = pHint->GetStart();
}
// I need this value later on for notification but the pointer may become invalid
const sal_Int32 nHintEnd = *pHtEnd;
const bool bNoHintAdjustMode = bool(SetAttrMode::NOHINTADJUST & nMode);
// handle nesting attributes: inserting may fail due to overlap!
if (pHint->IsNesting())
{
const bool bRet(
TryInsertNesting(rNode, *static_txtattr_cast<SwTextAttrNesting*>(pHint)));
if (!bRet) return false;
}
// Currently REFMARK and TOXMARK have OverlapAllowed set to true.
// These attributes may be inserted directly.
// Also attributes without length may be inserted directly.
// SETATTR_NOHINTADJUST is set e.g., during undo.
// Portion building in not necessary during XML import.
else if ( !bNoHintAdjustMode &&
!pHint->IsOverlapAllowedAttr() &&
!rNode.GetDoc().IsInXMLImport() &&
( RES_TXTATR_AUTOFMT == nWhich ||
RES_TXTATR_CHARFMT == nWhich ) )
{
assert( nWhich != RES_TXTATR_AUTOFMT ||
static_cast<const SwFormatAutoFormat&>(pHint->GetAttr()).GetStyleHandle()->GetPool() ==
&rNode.GetDoc().GetAttrPool());
BuildPortions( rNode, *pHint, nMode );
if ( nHtStart < nHintEnd ) // skip merging for 0-length attributes
MergePortions( rNode );
}
else
{
// There may be more than one character style at the current position.
// Take care of the sort number.
// FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert
// character attributes directly
if (!bNoHintAdjustMode
&& ( (RES_TXTATR_CHARFMT == nWhich)
// tdf#149978 also for import of automatic styles, could be produced by non-LO application
|| (RES_TXTATR_AUTOFMT == nWhich && rNode.GetDoc().IsInXMLImport())))
{
BuildPortions( rNode, *pHint, nMode );
}
else
{
// #i82989# Check sort numbers in NoHintAdjustMode
if ( RES_TXTATR_CHARFMT == nWhich )
lcl_CheckSortNumber(*this, *static_txtattr_cast<SwTextCharFormat*>(pHint));
void SwpHints::DeleteAtPos( const size_t nPos )
{
assert(m_StartMapNeedsSortingRange.first == SAL_MAX_INT32 && "deleting at pos and the list needs sorting?");
// optimization: nPos is the position in the Starts array
SwTextAttr *pHt = m_HintsByStart[ nPos ];
m_HintsByStart.erase( m_HintsByStart.begin() + nPos );
if (m_StartMapNeedsSortingRange.first != SAL_MAX_INT32)
ResortStartMap();
if (m_EndMapNeedsSortingRange.first != SAL_MAX_INT32)
ResortEndMap();
if (m_WhichMapNeedsSortingRange.first.first != SAL_MAX_INT32)
ResortWhichMap();
if ( RES_CHRATR_NOHYPHEN == nWhichId )
{
// bNoneIfNoHyphenation = true: return with LANGUAGE_NONE,
// if the hyphenation is disabled by character formatting
if ( static_cast<const SvxNoHyphenItem*>(pItem)->GetValue() )
return LANGUAGE_NONE;
continue;
}
sal_Unicode GetCharOfTextAttr( const SwTextAttr& rAttr )
{
sal_Unicode cRet = CH_TXTATR_BREAKWORD;
switch ( rAttr.Which() )
{
case RES_TXTATR_REFMARK:
case RES_TXTATR_TOXMARK:
case RES_TXTATR_ANNOTATION:
cRet = CH_TXTATR_INWORD;
break;
case RES_TXTATR_FIELD:
case RES_TXTATR_FLYCNT:
case RES_TXTATR_FTN:
case RES_TXTATR_META:
case RES_TXTATR_METAFIELD:
case RES_TXTATR_CONTENTCONTROL:
{
cRet = CH_TXTATR_BREAKWORD;
}
break;
case RES_TXTATR_LINEBREAK:
{
cRet = CH_TXTATR_NEWLINE;
}
break;
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.