summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sw/inc/ndhints.hxx2
-rw-r--r--sw/inc/swtypes.hxx2
-rw-r--r--sw/inc/txatbase.hxx6
-rw-r--r--sw/source/core/inc/rolbck.hxx2
-rw-r--r--sw/source/core/layout/frmtool.cxx3
-rw-r--r--sw/source/core/layout/laycache.cxx5
-rw-r--r--sw/source/core/text/itratr.cxx24
-rw-r--r--sw/source/core/txtnode/ndhints.cxx88
-rw-r--r--sw/source/core/txtnode/ndtxt.cxx44
-rw-r--r--sw/source/core/txtnode/thints.cxx213
-rw-r--r--sw/source/core/txtnode/txatbase.cxx2
-rw-r--r--sw/source/core/txtnode/txtedt.cxx4
-rw-r--r--sw/source/core/undo/rolbck.cxx14
13 files changed, 371 insertions, 38 deletions
diff --git a/sw/inc/ndhints.hxx b/sw/inc/ndhints.hxx
index 0d7a158ee889..d8b611535cbc 100644
--- a/sw/inc/ndhints.hxx
+++ b/sw/inc/ndhints.hxx
@@ -116,7 +116,7 @@ public:
inline sal_uInt16 Count() const { return m_HintStarts.size(); }
#ifdef DBG_UTIL
- bool Check() const;
+ bool Check(bool) const;
#endif
};
diff --git a/sw/inc/swtypes.hxx b/sw/inc/swtypes.hxx
index 187e070e8da8..f5ba633310ab 100644
--- a/sw/inc/swtypes.hxx
+++ b/sw/inc/swtypes.hxx
@@ -192,6 +192,8 @@ namespace nsSetAttrMode
const SetAttrMode SETATTR_DONTREPLACE = 0x0002; // Don't replace another text attribute.
const SetAttrMode SETATTR_NOTXTATRCHR = 0x0004; // Don't insert 0xFF at attributes with no end.
+ /// attention: NOHINTADJUST prevents MergePortions!
+ /// when using this need to pay attention to ignore start/end flags of hint
const SetAttrMode SETATTR_NOHINTADJUST = 0x0008; // No merging of ranges.
const SetAttrMode SETATTR_NOFORMATATTR = 0x0010; // Do not change into format attribute.
const SetAttrMode SETATTR_DONTCHGNUMRULE = 0x0020; // Do not change NumRule.
diff --git a/sw/inc/txatbase.hxx b/sw/inc/txatbase.hxx
index 3a250f068885..e69b08871fd3 100644
--- a/sw/inc/txatbase.hxx
+++ b/sw/inc/txatbase.hxx
@@ -55,6 +55,8 @@ private:
bool m_bDontExpandStart : 1; // don't expand start at paragraph start (ruby)
bool m_bNesting : 1; // SwTxtAttrNesting
bool m_bHasDummyChar : 1; // without end + meta
+ bool m_bFormatIgnoreStart : 1; ///< text formatting should ignore start
+ bool m_bFormatIgnoreEnd : 1; ///< text formatting should ignore end
protected:
SwTxtAttr( SfxPoolItem& rAttr, xub_StrLen nStart );
@@ -94,6 +96,10 @@ public:
bool IsDontExpandStartAttr() const { return m_bDontExpandStart; }
bool IsNesting() const { return m_bNesting; }
bool HasDummyChar() const { return m_bHasDummyChar; }
+ bool IsFormatIgnoreStart() const { return m_bFormatIgnoreStart; }
+ bool IsFormatIgnoreEnd () const { return m_bFormatIgnoreEnd ; }
+ void SetFormatIgnoreStart(bool bFlag) { m_bFormatIgnoreStart = bFlag; }
+ void SetFormatIgnoreEnd (bool bFlag) { m_bFormatIgnoreEnd = bFlag; }
inline const SfxPoolItem& GetAttr() const;
inline SfxPoolItem& GetAttr();
diff --git a/sw/source/core/inc/rolbck.hxx b/sw/source/core/inc/rolbck.hxx
index 62b01844f43c..ddff2a21e50f 100644
--- a/sw/source/core/inc/rolbck.hxx
+++ b/sw/source/core/inc/rolbck.hxx
@@ -122,6 +122,8 @@ class SwHistorySetTxt : public SwHistoryHint
const sal_uLong m_nNodeIndex;
const xub_StrLen m_nStart;
const xub_StrLen m_nEnd;
+ bool m_bFormatIgnoreStart : 1;
+ bool m_bFormatIgnoreEnd : 1;
public:
SwHistorySetTxt( SwTxtAttr* pTxtHt, sal_uLong nNode );
diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx
index f9337b13e268..91239873e537 100644
--- a/sw/source/core/layout/frmtool.cxx
+++ b/sw/source/core/layout/frmtool.cxx
@@ -1277,8 +1277,7 @@ void _InsertCnt( SwLayoutFrm *pLay, SwDoc *pDoc,
if ( pNd->IsCntntNode() )
{
SwCntntNode* pNode = (SwCntntNode*)pNd;
- pFrm = pNode->IsTxtNode() ? new SwTxtFrm( (SwTxtNode*)pNode, pLay ) :
- pNode->MakeFrm( pLay );
+ pFrm = pNode->MakeFrm(pLay);
if( pPageMaker )
pPageMaker->CheckInsert( nIndex );
diff --git a/sw/source/core/layout/laycache.cxx b/sw/source/core/layout/laycache.cxx
index 1753d8544e50..ebd9e722aa2e 100644
--- a/sw/source/core/layout/laycache.cxx
+++ b/sw/source/core/layout/laycache.cxx
@@ -843,8 +843,9 @@ bool SwLayHelper::CheckInsert( sal_uLong nNodeIndex )
}
else
{
- SwTxtFrm *pNew = new SwTxtFrm( ((SwTxtFrm*)rpFrm)->
- GetTxtNode(), rpFrm );
+ SwTxtFrm *const pNew = static_cast<SwTxtFrm*>(
+ static_cast<SwTxtFrm*>(rpFrm)
+ ->GetTxtNode()->MakeFrm(rpFrm));
pNew->ManipOfst( nOfst );
pNew->SetFollow( ((SwTxtFrm*)rpFrm)->GetFollow() );
((SwTxtFrm*)rpFrm)->SetFollow( pNew );
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx
index d95ee5500f49..fba4898bbf84 100644
--- a/sw/source/core/text/itratr.cxx
+++ b/sw/source/core/text/itratr.cxx
@@ -311,12 +311,26 @@ xub_StrLen SwAttrIter::GetNextAttr( ) const
xub_StrLen nNext = STRING_LEN;
if( pHints )
{
- if (pHints->GetStartCount() > nStartIndex) // Gibt es noch Starts?
- nNext = (*pHints->GetStart(nStartIndex)->GetStart());
- if (pHints->GetEndCount() > nEndIndex) // Gibt es noch Enden?
+ // are there attribute starts left?
+ for (sal_uInt16 i = nStartIndex; i < pHints->GetStartCount(); ++i)
{
- xub_StrLen nNextEnd = (*pHints->GetEnd(nEndIndex)->GetAnyEnd());
- if ( nNextEnd<nNext ) nNext = nNextEnd; // Wer ist naeher?
+ SwTxtAttr *const pAttr(pHints->GetStart(i));
+ if (!pAttr->IsFormatIgnoreStart())
+ {
+ nNext = *pAttr->GetStart();
+ break;
+ }
+ }
+ // are there attribute ends left?
+ for (sal_uInt16 i = nEndIndex; i < pHints->GetEndCount(); ++i)
+ {
+ SwTxtAttr *const pAttr(pHints->GetEnd(i));
+ if (!pAttr->IsFormatIgnoreEnd())
+ {
+ xub_StrLen const nNextEnd = *pAttr->GetAnyEnd();
+ nNext = std::min(nNext, nNextEnd); // pick nearest one
+ break;
+ }
}
}
if (m_pTxtNode!=NULL) {
diff --git a/sw/source/core/txtnode/ndhints.cxx b/sw/source/core/txtnode/ndhints.cxx
index 02ea3443a884..4d070e1fb57a 100644
--- a/sw/source/core/txtnode/ndhints.cxx
+++ b/sw/source/core/txtnode/ndhints.cxx
@@ -24,6 +24,8 @@
#ifdef DBG_UTIL
#include <pam.hxx>
+#include <fmtautofmt.hxx>
+#include <set>
#endif
@@ -171,7 +173,7 @@ sal_uInt16 SwpHintsArray::GetPos( const SwTxtAttr *pHt ) const
return false; \
}
-bool SwpHintsArray::Check() const
+bool SwpHintsArray::Check(bool bPortionsMerged) const
{
// 1) gleiche Anzahl in beiden Arrays
CHECK_ERR( m_HintStarts.size() == m_HintEnds.size(),
@@ -181,6 +183,23 @@ bool SwpHintsArray::Check() const
const SwTxtAttr *pLastStart = 0;
const SwTxtAttr *pLastEnd = 0;
+ std::set<SwTxtAttr const*> RsidOnlyAutoFmts;
+ if (bPortionsMerged)
+ {
+ for (sal_uInt16 i = 0; i < Count(); ++i)
+ {
+ SwTxtAttr const*const pHint(m_HintStarts[i]);
+ if (RES_TXTATR_AUTOFMT == pHint->Which())
+ {
+ boost::shared_ptr<SfxItemSet> const pSet(
+ pHint->GetAutoFmt().GetStyleHandle());
+ if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
+ {
+ RsidOnlyAutoFmts.insert(pHint);
+ }
+ }
+ }
+ }
for( sal_uInt16 i = 0; i < Count(); ++i )
{
@@ -270,6 +289,73 @@ bool SwpHintsArray::Check() const
"HintsCheck: Portion inconsistency. "
"This can be temporarily ok during undo operations" );
+ // 8 1/2) format ignore start/end flag check
+ // (problems because MergePortions buggy or not called)
+ if (bPortionsMerged)
+ {
+ if (RES_TXTATR_AUTOFMT == pHt->Which() ||
+ RES_TXTATR_CHARFMT == pHt->Which())
+ {
+ // mostly ignore the annoying no-length hints
+ // BuildPortions inserts these in the middle of an exsiting one
+ bool const bNoLength(*pHt->GetStart() == *pHt->GetEnd());
+ bool bNeedContinuation(!bNoLength && pHt->IsFormatIgnoreEnd());
+ bool bForbidContinuation(!bNoLength && !bNeedContinuation);
+ if (RES_TXTATR_AUTOFMT == pHt->Which())
+ {
+ if (RsidOnlyAutoFmts.find(pHt) != RsidOnlyAutoFmts.end())
+ {
+ assert(pHt->IsFormatIgnoreStart());
+ bNeedContinuation = false;
+ // don't forbid continuation - may be other hint here!
+ }
+ }
+ if (bNeedContinuation || bForbidContinuation)
+ {
+ bool bFound(false);
+ for (sal_uInt16 j = i + 1; j < Count(); ++j)
+ {
+ SwTxtAttr *const pOther(m_HintStarts[j]);
+ if (*pOther->GetStart() > *pHt->GetEnd())
+ {
+ break; // done
+ }
+ else if (*pOther->GetStart() == *pOther->GetAnyEnd())
+ {
+ continue; // empty hint: ignore
+ }
+ else if (*pOther->GetStart() == *pHt->GetEnd())
+ {
+ if (RES_TXTATR_AUTOFMT == pOther->Which() ||
+ RES_TXTATR_CHARFMT == pOther->Which())
+ { // multiple charfmt on same range must all match
+ if (bNeedContinuation)
+ {
+ assert(pOther->IsFormatIgnoreStart());
+ bFound = true;
+ }
+ else if (bForbidContinuation &&
+ (RsidOnlyAutoFmts.find(pOther) ==
+ RsidOnlyAutoFmts.end()))
+ {
+ assert(!pOther->IsFormatIgnoreStart());
+ }
+ }
+ }
+ }
+ if (bNeedContinuation)
+ {
+ assert(bFound); // ? can this happen temp. during undo?
+ }
+ }
+ }
+ else
+ {
+ assert(!pHt->IsFormatIgnoreStart());
+ assert(!pHt->IsFormatIgnoreEnd());
+ }
+ }
+
// 9) nesting portion check
if (pHtThis->IsNesting())
{
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index 8d4cdaf2f831..7b3b98dcc3a9 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -95,7 +95,7 @@ TYPEINIT1( SwTxtNode, SwCntntNode )
#ifdef DBG_UTIL
#define CHECK_SWPHINTS(pNd) { if( pNd->GetpSwpHints() && \
!pNd->GetDoc()->IsInReading() ) \
- pNd->GetpSwpHints()->Check(); }
+ pNd->GetpSwpHints()->Check(true); }
#else
#define CHECK_SWPHINTS(pNd)
#endif
@@ -263,6 +263,12 @@ SwTxtNode::~SwTxtNode()
SwCntntFrm *SwTxtNode::MakeFrm( SwFrm* pSib )
{
+ // fdo#52028: ODF file import does not result in MergePortions being called
+ // for every attribute, since that would be inefficient. So call it here.
+ if (m_pSwpHints)
+ {
+ m_pSwpHints->MergePortions(*this);
+ }
SwCntntFrm *pFrm = new SwTxtFrm( this, pSib );
return pFrm;
}
@@ -883,6 +889,7 @@ void SwTxtNode::Update( SwIndex const & rPos, const xub_StrLen nChangeLen,
{
bool bNoExp = false;
bool bResort = false;
+ bool bMergePortionsNeeded = false;
const sal_uInt16 coArrSz = static_cast<sal_uInt16>(RES_TXTATR_WITHEND_END) -
static_cast<sal_uInt16>(RES_CHRATR_BEGIN);
@@ -929,6 +936,11 @@ void SwTxtNode::Update( SwIndex const & rPos, const xub_StrLen nChangeLen,
{
pHint->SetDontExpand( false );
bResort = true;
+ // could have a continuation with IgnoreStart()...
+ if (pHint->IsFormatIgnoreEnd())
+ {
+ bMergePortionsNeeded = true;
+ }
if ( pHint->IsCharFmtAttr() )
{
bNoExp = true;
@@ -969,7 +981,11 @@ void SwTxtNode::Update( SwIndex const & rPos, const xub_StrLen nChangeLen,
}
}
}
- if ( bResort )
+ if (bMergePortionsNeeded)
+ {
+ m_pSwpHints->MergePortions(*this); // does Resort too
+ }
+ else if (bResort)
{
m_pSwpHints->Resort();
}
@@ -1745,6 +1761,7 @@ OUString SwTxtNode::InsertText( const XubString & rStr, const SwIndex & rIdx,
if ( HasHints() )
{
+ bool bMergePortionsNeeded(false);
for ( sal_uInt16 i = 0; i < m_pSwpHints->Count() &&
rIdx >= *(*m_pSwpHints)[i]->GetStart(); ++i )
{
@@ -1764,6 +1781,14 @@ OUString SwTxtNode::InsertText( const XubString & rStr, const SwIndex & rIdx,
*pHt->GetStart() = *pHt->GetStart() - nLen;
*pEndIdx = *pEndIdx - nLen;
m_pSwpHints->DeleteAtPos(i);
+ // could be that pHt has IsFormatIgnoreEnd set, and it's
+ // not a RSID-only hint - now we have the inserted text
+ // between pHt and its continuation... which we don't know.
+ // punt the job to MergePortions below.
+ if (pHt->IsFormatIgnoreEnd())
+ {
+ bMergePortionsNeeded = true;
+ }
InsertHint( pHt, nsSetAttrMode::SETATTR_NOHINTADJUST );
}
// empty hints at insert position?
@@ -1792,9 +1817,14 @@ OUString SwTxtNode::InsertText( const XubString & rStr, const SwIndex & rIdx,
// Kein Feld, am Absatzanfang, HintExpand
m_pSwpHints->DeleteAtPos(i);
*pHt->GetStart() = *pHt->GetStart() - nLen;
+ // no effect on format ignore flags here (para start)
InsertHint( pHt, nsSetAttrMode::SETATTR_NOHINTADJUST );
}
}
+ if (bMergePortionsNeeded)
+ {
+ m_pSwpHints->MergePortions(*this);
+ }
TryDeleteSwpHints();
}
@@ -2042,6 +2072,7 @@ void SwTxtNode::CutImpl( SwTxtNode * const pDest, const SwIndex & rDestStart,
// 2. Attribute verschieben
// durch das Attribute-Array, bis der Anfang des Geltungsbereiches
// des Attributs hinter dem zu verschiebenden Bereich liegt
+ bool bMergePortionsNeeded(false);
sal_uInt16 nAttrCnt = 0;
while ( m_pSwpHints && (nAttrCnt < m_pSwpHints->Count()) )
{
@@ -2085,6 +2116,10 @@ void SwTxtNode::CutImpl( SwTxtNode * const pDest, const SwIndex & rDestStart,
// Attribut verschieben
m_pSwpHints->Delete( pHt );
// die Start/End Indicies neu setzen
+ if (pHt->IsFormatIgnoreStart() || pHt->IsFormatIgnoreEnd())
+ {
+ bMergePortionsNeeded = true;
+ }
*pHt->GetStart() =
nDestStart + (nAttrStartIdx - nTxtStartIdx);
if( pEndIdx )
@@ -2164,6 +2199,11 @@ void SwTxtNode::CutImpl( SwTxtNode * const pDest, const SwIndex & rDestStart,
Update( rStart, nLen, sal_True, sal_True );
}
+ if (bMergePortionsNeeded)
+ {
+ m_pSwpHints->MergePortions(*this);
+ }
+
CHECK_SWPHINTS(this);
}
diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx
index 71da72eb18c2..9bcfcf426ea8 100644
--- a/sw/source/core/txtnode/thints.cxx
+++ b/sw/source/core/txtnode/thints.cxx
@@ -72,9 +72,11 @@
#include <map>
#ifdef DBG_UTIL
-#define CHECK Check();
+#define CHECK Check(true);
+#define CHECK_NOTMERGED Check(false);
#else
#define CHECK
+#define CHECK_NOTMERGED
#endif
using namespace ::com::sun::star::i18n;
@@ -649,7 +651,7 @@ void SwpHints::BuildPortions( SwTxtNode& rNode, SwTxtAttr& rNewHint,
#ifdef DBG_UTIL
if( !rNode.GetDoc()->IsInReading() )
- CHECK;
+ CHECK_NOTMERGED; // ignore flags not set properly yet, don't check them
#endif
//
@@ -1563,6 +1565,8 @@ void SwTxtNode::DeleteAttributes( const sal_uInt16 nWhich,
const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pTxtHt, RES_CHRATR_HIDDEN );
if ( pHiddenItem )
SetCalcHiddenCharFlags();
+ // for auto styles DeleteAttributes is only called from Undo
+ // so it shouldn't need to care about ignore start/end flags
}
xub_StrLen const * const pEndIdx = pTxtHt->GetEnd();
@@ -2306,6 +2310,9 @@ SwTxtNode::impl_FmtToTxtAttr(const SfxItemSet& i_rAttrSet)
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);
@@ -2497,8 +2504,9 @@ bool SwpHints::MergePortions( SwTxtNode& rNode )
SwpHintsArray::Resort();
bool bRet = false;
- typedef std::multimap< int, SwTxtAttr* > PortionMap;
+ typedef std::multimap< int, std::pair<SwTxtAttr*, bool> > PortionMap;
PortionMap aPortionMap;
+ std::map<int, bool> RsidOnlyAutoFmtFlagMap;
xub_StrLen nLastPorStart = STRING_LEN;
sal_uInt16 i = 0;
int nKey = 0;
@@ -2513,14 +2521,45 @@ bool SwpHints::MergePortions( SwTxtNode& rNode )
//RES_TXTATR_INETFMT != pHt->Which() )
continue;
+ bool isRsidOnlyAutoFmt(false);
+ // check for RSID-only AUTOFMT
+ if (RES_TXTATR_AUTOFMT == pHt->Which())
+ {
+ boost::shared_ptr<SfxItemSet> const pSet(
+ pHt->GetAutoFmt().GetStyleHandle());
+ if ((pSet->Count() == 1) && pSet->GetItem(RES_CHRATR_RSID, false))
+ {
+ // 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);
+ }
+ isRsidOnlyAutoFmt = 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;
+ }
+
const xub_StrLen nPorStart = *pHt->GetStart();
- if ( nPorStart != nLastPorStart && nLastPorStart != STRING_LEN )
+ if (nPorStart != nLastPorStart)
++nKey;
nLastPorStart = nPorStart;
- aPortionMap.insert( std::pair< const int, SwTxtAttr* >( nKey, pHt ) );
+ aPortionMap.insert(std::make_pair(nKey,
+ std::make_pair(pHt, isRsidOnlyAutoFmt)));
+ RsidOnlyAutoFmtFlagMap[nKey] = isRsidOnlyAutoFmt;
}
// 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
i = 0;
int j = i + 1;
while ( i <= nKey )
@@ -2530,37 +2569,128 @@ bool SwpHints::MergePortions( SwTxtNode& rNode )
PortionMap::iterator aIter1 = aRange1.first;
PortionMap::iterator aIter2 = aRange2.first;
- bool bMerge = true;
- const sal_uInt16 nAttributesInPor1 = static_cast<sal_uInt16>(std::distance( aRange1.first, aRange1.second ));
- const sal_uInt16 nAttributesInPor2 = static_cast<sal_uInt16>(std::distance( aRange2.first, aRange2.second ));
-
- if ( nAttributesInPor1 == nAttributesInPor2 && nAttributesInPor1 != 0 )
+ enum { MATCH, DIFFER_ONLY_RSID, DIFFER } eMerge(MATCH);
+ size_t const nAttributesInPor1 = std::distance(aRange1.first, aRange1.second);
+ size_t const nAttributesInPor2 = std::distance(aRange2.first, aRange2.second);
+ bool const isRsidOnlyAutoFmt1(RsidOnlyAutoFmtFlagMap[i]);
+ bool const isRsidOnlyAutoFmt2(RsidOnlyAutoFmtFlagMap[j]);
+
+ // if both have one they could be equal, but not if only one has it
+ bool const bSkipRsidOnlyAutoFmt(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 - ((isRsidOnlyAutoFmt1) ? 1 : 0)) ==
+ (nAttributesInPor2 - ((isRsidOnlyAutoFmt2) ? 1 : 0))
+ && (nAttributesInPor1 != 0 || nAttributesInPor2 != 0))
{
- while ( aIter1 != aRange1.second )
+ // _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)
{
- const SwTxtAttr* p1 = (*aIter1).second;
- const SwTxtAttr* p2 = (*aIter2).second;
- if ( *p1->GetEnd() < *p2->GetStart() || p1->Which() != p2->Which() || !(*p1 == *p2) )
+ // first of all test if there's no gap (before skipping stuff!)
+ if (aIter1 != aRange1.second && aIter2 != aRange2.second &&
+ *aIter1->second.first->GetEnd() < *aIter2->second.first->GetStart())
{
- bMerge = false;
+ eMerge = DIFFER;
break;
}
+ // skip it - cannot be equal if bSkipRsidOnlyAutoFmt is set
+ if (bSkipRsidOnlyAutoFmt
+ && aIter1 != aRange1.second && aIter1->second.second)
+ {
+ assert(DIFFER != eMerge);
+ eMerge = DIFFER_ONLY_RSID;
+ ++aIter1;
+ continue;
+ }
+ if (bSkipRsidOnlyAutoFmt
+ && aIter2 != aRange2.second && aIter2->second.second)
+ {
+ assert(DIFFER != eMerge);
+ eMerge = DIFFER_ONLY_RSID;
+ ++aIter2;
+ continue;
+ }
+ assert(aIter1 != aRange1.second && aIter2 != aRange2.second);
+ SwTxtAttr const*const p1 = aIter1->second.first;
+ SwTxtAttr const*const p2 = aIter2->second.first;
+ if (p1->Which() != p2->Which())
+ {
+ eMerge = DIFFER;
+ break;
+ }
+ 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())
+ {
+ SfxItemSet set1(*p1->GetAutoFmt().GetStyleHandle());
+ SfxItemSet set2(*p2->GetAutoFmt().GetStyleHandle());
+
+ set1.ClearItem(RES_CHRATR_RSID);
+ set2.ClearItem(RES_CHRATR_RSID);
+
+ // sadly SfxItemSet::operator== does not seem to work?
+ SfxItemIter iter1(set1);
+ SfxItemIter iter2(set2);
+ if (set1.Count() == set2.Count())
+ {
+ for (SfxPoolItem const* pItem1 = iter1.FirstItem(),
+ * pItem2 = iter2.FirstItem();
+ pItem1 && pItem2;
+ pItem1 = iter1.NextItem(),
+ pItem2 = iter2.NextItem())
+ {
+ if (pItem1 != pItem2 ||
+ pItem1->Which() != pItem2->Which() ||
+ *pItem1 != *pItem2)
+ {
+ eMerge = DIFFER;
+ break;
+ }
+ if (iter1.IsAtEnd())
+ {
+ assert(iter2.IsAtEnd());
+ eMerge = DIFFER_ONLY_RSID;
+ }
+ }
+ if (DIFFER == eMerge)
+ break; // outer loop too
+ }
+ else
+ {
+ eMerge = DIFFER;
+ break;
+ }
+ }
+ else
+ {
+ eMerge = DIFFER;
+ break;
+ }
+ }
++aIter1;
++aIter2;
}
}
else
{
- bMerge = false;
+ eMerge = DIFFER;
}
- if ( bMerge )
+ if (MATCH == eMerge)
{
+ // important: delete second range so any IgnoreStart on the first
+ // range is still valid
// erase all elements with key i + 1
xub_StrLen nNewPortionEnd = 0;
for ( aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 )
{
- SwTxtAttr* p2 = (*aIter2).second;
+ SwTxtAttr *const p2 = aIter2->second.first;
nNewPortionEnd = *p2->GetEnd();
const sal_uInt16 nCountBeforeDelete = Count();
@@ -2577,7 +2707,7 @@ bool SwpHints::MergePortions( SwTxtNode& rNode )
aRange1 = aPortionMap.equal_range( i );
for ( aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 )
{
- SwTxtAttr* p1 = (*aIter1).second;
+ SwTxtAttr *const p1 = aIter1->second.first;
NoteInHistory( p1 );
*p1->GetEnd() = nNewPortionEnd;
NoteInHistory( p1, true );
@@ -2586,8 +2716,37 @@ bool SwpHints::MergePortions( SwTxtNode& rNode )
}
else
{
- ++i;
- j = i + 1;
+ // 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 (aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1)
+ {
+ if (!aIter1->second.second) // already set above, don't change
+ {
+ SwTxtAttr *const pCurrent(aIter1->second.first);
+ if (pCurrent->IsFormatIgnoreEnd() != bSetIgnoreFlag)
+ {
+ NoteInHistory(pCurrent);
+ pCurrent->SetFormatIgnoreEnd(bSetIgnoreFlag);
+ NoteInHistory(pCurrent, true);
+ }
+ }
+ }
+ for (aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2)
+ {
+ if (!aIter2->second.second) // already set above, don't change
+ {
+ SwTxtAttr *const pCurrent(aIter2->second.first);
+ 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;
}
}
@@ -2676,6 +2835,16 @@ bool SwpHints::TryInsertHint( SwTxtAttr* const pHint, SwTxtNode &rNode,
// #i75430# Recalc hidden flags if necessary
case RES_TXTATR_AUTOFMT:
{
+ if (*pHint->GetStart() == *pHint->GetEnd())
+ {
+ boost::shared_ptr<SfxItemSet> const pSet(
+ pHint->GetAutoFmt().GetStyleHandle());
+ 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 = CharFmt::GetItem( *pHint, RES_CHRATR_HIDDEN );
if ( pHiddenItem )
@@ -2974,7 +3143,7 @@ void SwpHints::DeleteAtPos( const sal_uInt16 nPos )
}
CalcFlags();
- CHECK;
+ CHECK_NOTMERGED; // called from BuildPortions
}
// Ist der Hint schon bekannt, dann suche die Position und loesche ihn.
diff --git a/sw/source/core/txtnode/txatbase.cxx b/sw/source/core/txtnode/txatbase.cxx
index e0acb29d72f2..e59e969d78b6 100644
--- a/sw/source/core/txtnode/txatbase.cxx
+++ b/sw/source/core/txtnode/txatbase.cxx
@@ -34,6 +34,8 @@ SwTxtAttr::SwTxtAttr( SfxPoolItem& rAttr, xub_StrLen nStart )
, m_bDontExpandStart( false )
, m_bNesting( false )
, m_bHasDummyChar( false )
+ , m_bFormatIgnoreStart(false)
+ , m_bFormatIgnoreEnd(false)
{
}
diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx
index 691da301329c..016d56a5db02 100644
--- a/sw/source/core/txtnode/txtedt.cxx
+++ b/sw/source/core/txtnode/txtedt.cxx
@@ -584,8 +584,8 @@ void SwTxtNode::RstAttr(const SwIndex &rIdx, xub_StrLen nLen, sal_uInt16 nWhich,
if (bChanged)
{
if ( HasHints() )
- {
- m_pSwpHints->Resort();
+ { // possibly sometimes Resort would be sufficient, but...
+ m_pSwpHints->MergePortions(*this);
}
//TxtFrm's reagieren auf aHint, andere auf aNew
SwUpdateAttr aHint( nMin, nMax, 0 );
diff --git a/sw/source/core/undo/rolbck.cxx b/sw/source/core/undo/rolbck.cxx
index ced76e507c80..59cfec65def5 100644
--- a/sw/source/core/undo/rolbck.cxx
+++ b/sw/source/core/undo/rolbck.cxx
@@ -195,6 +195,8 @@ SwHistorySetTxt::SwHistorySetTxt( SwTxtAttr* pTxtHt, sal_uLong nNodePos )
, m_nNodeIndex( nNodePos )
, m_nStart( *pTxtHt->GetStart() )
, m_nEnd( *pTxtHt->GetAnyEnd() )
+ , m_bFormatIgnoreStart(pTxtHt->IsFormatIgnoreStart())
+ , m_bFormatIgnoreEnd (pTxtHt->IsFormatIgnoreEnd ())
{
// Caution: the following attributes generate no format attributes:
// - NoLineBreak, NoHypen, Inserted, Deleted
@@ -234,9 +236,19 @@ void SwHistorySetTxt::SetInDoc( SwDoc* pDoc, bool )
if ( pTxtNd )
{
- pTxtNd->InsertItem( *m_pAttr, m_nStart, m_nEnd,
+ SwTxtAttr *const pAttr = pTxtNd->InsertItem(*m_pAttr, m_nStart, m_nEnd,
nsSetAttrMode::SETATTR_NOTXTATRCHR |
nsSetAttrMode::SETATTR_NOHINTADJUST );
+ // shouldn't be possible to hit any error/merging path from here
+ assert(pAttr);
+ if (m_bFormatIgnoreStart)
+ {
+ pAttr->SetFormatIgnoreStart(true);
+ }
+ if (m_bFormatIgnoreEnd)
+ {
+ pAttr->SetFormatIgnoreEnd(true);
+ }
}
}