summaryrefslogtreecommitdiff
path: root/sw/source/core/txtnode/thints.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/txtnode/thints.cxx')
-rw-r--r--sw/source/core/txtnode/thints.cxx3063
1 files changed, 3063 insertions, 0 deletions
diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx
new file mode 100644
index 000000000000..3ca7d01e1981
--- /dev/null
+++ b/sw/source/core/txtnode/thints.cxx
@@ -0,0 +1,3063 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sw.hxx"
+
+
+#include <hintids.hxx>
+#include <sot/factory.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <svl/whiter.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/stylepool.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/emphitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <txtinet.hxx>
+#include <txtflcnt.hxx>
+#include <fmtfld.hxx>
+#include <fmtanchr.hxx>
+#include <fmtinfmt.hxx>
+#include <txtatr.hxx>
+#include <fchrfmt.hxx>
+#include <fmtautofmt.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtftn.hxx>
+#include <txttxmrk.hxx>
+#include <txtrfmrk.hxx>
+#include <txtftn.hxx>
+#include <txtfld.hxx>
+#include <charatr.hxx>
+#include <charfmt.hxx>
+#include <frmfmt.hxx>
+#include <ftnidx.hxx>
+#include <fmtruby.hxx>
+#include <fmtmeta.hxx>
+#include <breakit.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <fldbas.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <rolbck.hxx> // fuer SwRegHistory
+#include <ddefld.hxx>
+#include <docufld.hxx>
+#include <expfld.hxx>
+#include <usrfld.hxx>
+#include <poolfmt.hxx>
+#include <swfont.hxx>
+#include <istyleaccess.hxx>
+// OD 26.06.2003 #108784#
+#include <dcontact.hxx>
+#include <docsh.hxx>
+#include <svl/smplhint.hxx>
+#include <algorithm>
+#include <map>
+
+#if OSL_DEBUG_LEVEL > 1
+#define CHECK Check();
+#else
+#define CHECK
+#endif
+
+using namespace ::com::sun::star::i18n;
+
+
+SwpHints::SwpHints()
+ : m_pHistory(0)
+ , m_bFontChange(true)
+ , m_bInSplitNode(false)
+ , m_bCalcHiddenParaField(false)
+ , m_bHasHiddenParaField(false)
+ , m_bFootnote(false)
+ , m_bDDEFields(false)
+{
+}
+
+struct TxtAttrDeleter
+{
+ SwAttrPool & m_rPool;
+ TxtAttrDeleter( SwDoc & rDoc ) : m_rPool( rDoc.GetAttrPool() ) { }
+ void operator() (SwTxtAttr * const pAttr)
+ {
+ if (RES_TXTATR_META == pAttr->Which() ||
+ RES_TXTATR_METAFIELD == pAttr->Which())
+ {
+ static_cast<SwTxtMeta *>(pAttr)->ChgTxtNode(0); // prevents ASSERT
+ }
+ SwTxtAttr::Destroy( pAttr, m_rPool );
+ }
+};
+
+struct TxtAttrContains
+{
+ xub_StrLen m_nPos;
+ TxtAttrContains( const xub_StrLen nPos ) : m_nPos( nPos ) { }
+ bool operator() (SwTxtAttrEnd * const pAttr)
+ {
+ return (*pAttr->GetStart() < m_nPos) && (m_nPos < *pAttr->GetEnd());
+ }
+};
+
+// 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 xub_StrLen nStart1, const xub_StrLen nEnd1,
+ const xub_StrLen nStart2, const xub_StrLen 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 xub_StrLen nStart1, const xub_StrLen nEnd1,
+ const xub_StrLen nStart2, const xub_StrLen 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));
+}
+
+static
+bool isSelfNestable(const sal_uInt16 nWhich)
+{
+ if ((RES_TXTATR_INETFMT == nWhich) ||
+ (RES_TXTATR_CJK_RUBY == nWhich))
+ return false;
+ OSL_ENSURE((RES_TXTATR_META == nWhich) ||
+ (RES_TXTATR_METAFIELD == nWhich), "???");
+ return true;
+}
+
+static
+bool isSplittable(const sal_uInt16 nWhich)
+{
+ if ((RES_TXTATR_INETFMT == nWhich) ||
+ (RES_TXTATR_CJK_RUBY == nWhich))
+ return true;
+ OSL_ENSURE((RES_TXTATR_META == nWhich) ||
+ (RES_TXTATR_METAFIELD == nWhich), "???");
+ return false;
+}
+
+enum Split_t { FAIL, SPLIT_NEW, SPLIT_OTHER };
+/**
+ Calculate splitting policy for overlapping hints, based on what kind of
+ hint is inserted, and what kind of existing hint overlaps.
+ */
+static Split_t
+splitPolicy(const sal_uInt16 nWhichNew, const sal_uInt16 nWhichOther)
+{
+ if (!isSplittable(nWhichOther))
+ {
+ if (!isSplittable(nWhichNew))
+ return FAIL;
+ else
+ return SPLIT_NEW;
+ }
+ else
+ {
+ if ((RES_TXTATR_INETFMT == nWhichNew) &&
+ (RES_TXTATR_CJK_RUBY == nWhichOther))
+ return SPLIT_NEW;
+ else
+ return SPLIT_OTHER;
+ }
+}
+
+void SwTxtINetFmt::InitINetFmt(SwTxtNode & rNode)
+{
+ ChgTxtNode(&rNode);
+ SwCharFmt * const pFmt(
+ rNode.GetDoc()->GetCharFmtFromPool(RES_POOLCHR_INET_NORMAL) );
+ pFmt->Add( this );
+}
+
+void SwTxtRuby::InitRuby(SwTxtNode & rNode)
+{
+ ChgTxtNode(&rNode);
+ SwCharFmt * const pFmt(
+ rNode.GetDoc()->GetCharFmtFromPool(RES_POOLCHR_RUBYTEXT) );
+ pFmt->Add( this );
+}
+
+/**
+ Create a new nesting text hint.
+ */
+static SwTxtAttrNesting *
+MakeTxtAttrNesting(SwTxtNode & rNode, SwTxtAttrNesting & rNesting,
+ const xub_StrLen nStart, const xub_StrLen nEnd)
+{
+ SwTxtAttr * const pNew( MakeTxtAttr(
+ *rNode.GetDoc(), rNesting.GetAttr(), nStart, nEnd ) );
+ switch (pNew->Which())
+ {
+ case RES_TXTATR_INETFMT:
+ {
+ static_cast<SwTxtINetFmt*>(pNew)->InitINetFmt(rNode);
+ break;
+ }
+ case RES_TXTATR_CJK_RUBY:
+ {
+ static_cast<SwTxtRuby*>(pNew)->InitRuby(rNode);
+ break;
+ }
+ default:
+ OSL_FAIL("MakeTxtAttrNesting: what the hell is that?");
+ break;
+ }
+ return static_cast<SwTxtAttrNesting*>(pNew);
+}
+
+typedef ::std::vector<SwTxtAttrNesting *> NestList_t;
+
+static void
+lcl_DoSplitNew(NestList_t & rSplits, SwTxtNode & rNode,
+ const xub_StrLen nNewStart,
+ const xub_StrLen nOtherStart, const xub_StrLen nOtherEnd, bool bOtherDummy)
+{
+ const bool bSplitAtStart(nNewStart < nOtherStart);
+ const xub_StrLen nSplitPos( (bSplitAtStart) ? nOtherStart : nOtherEnd );
+ // first find the portion that is split (not necessarily the last one!)
+ NestList_t::iterator const iter(
+ ::std::find_if( rSplits.begin(), rSplits.end(),
+ TxtAttrContains(nSplitPos) ) );
+ if (iter != rSplits.end()) // already split here?
+ {
+ const xub_StrLen nStartPos( // skip other's dummy character!
+ (bSplitAtStart && bOtherDummy) ? nSplitPos + 1 : nSplitPos );
+ SwTxtAttrNesting * const pNew( MakeTxtAttrNesting(
+ rNode, **iter, nStartPos, *(*iter)->GetEnd() ) );
+ *(*iter)->GetEnd() = nSplitPos;
+ rSplits.insert(iter + 1, pNew);
+ }
+}
+
+/**
+ Insert nesting hint into the hints array. Also calls NoteInHistory.
+ @param rNewHint the hint to be inserted (must not overlap existing!)
+ */
+void SwpHints::InsertNesting(SwTxtAttrNesting & rNewHint)
+{
+ SwpHintsArray::Insert(& rNewHint);
+ NoteInHistory( & rNewHint, true );
+}
+
+/**
+
+The following hints correspond to well-formed XML elements in ODF:
+RES_TXTATR_INETFMT, RES_TXTATR_CJK_RUBY, RES_TXTATR_META, RES_TXTATR_METAFIELD
+
+The writer core must ensure that these do not overlap; if they did,
+the document would not be storable as ODF.
+
+Also, a Hyperlink must not be nested within another Hyperlink,
+and a Ruby must not be nested within another Ruby.
+
+The ODF export in xmloff will only put a hyperlink into a ruby, never a ruby
+into a hyperlink.
+
+Unfortunately the UNO API for Hyperlink and Ruby consists of the properties
+Hyperlink* and Ruby* of the css.text.CharacterProperties service. In other
+words, they are treated as formatting attributes, not as content entites.
+Furthermore, for API users it is not possible to easily test whether a certain
+range would be overlapping with other nested attributes, and most importantly,
+<em>which ones</em>, so we can hardly refuse to insert these in cases of
+overlap.
+
+It is possible to split Hyperlink and Ruby into multiple portions, such that
+the result is properly nested.
+
+meta and meta-field must not be split, because they have xml:id.
+
+These constraints result in the following design:
+
+RES_TXTATR_INETFMT:
+ always succeeds
+ inserts n attributes split at RES_TXTATR_CJK_RUBY, RES_TXTATR_META,
+ RES_TXTATR_METAFIELD
+ may replace existing RES_TXTATR_INETFMT at overlap
+RES_TXTATR_CJK_RUBY:
+ always succeeds
+ inserts n attributes split at RES_TXTATR_META, RES_TXTATR_METAFIELD
+ may replace existing RES_TXTATR_CJK_RUBY at overlap
+ may split existing overlapping RES_TXTATR_INETFMT
+RES_TXTATR_META:
+ may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD
+ may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY
+ inserts 1 attribute
+RES_TXTATR_METAFIELD:
+ may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD
+ may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY
+ inserts 1 attribute
+
+The nesting is expressed by the position of the hints.
+RES_TXTATR_META and RES_TXTATR_METAFIELD have a CH_TXTATR, and there can
+only be one such hint starting and ending at a given position.
+Only RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY lack a CH_TXTATR.
+The interpretation given is that RES_TXTATR_CJK_RUBY is always around
+a RES_TXTATR_INETFMT at the same start and end position (which corresponds
+with the UNO API).
+Both of these are always around a nesting hint with CH_TXTATR at the same
+start and end position (if they should be inside, then the start should be
+after the CH_TXTATR).
+It would probably be a bad idea to add another nesting hint without
+CH_TXTATR; on the other hand, it would be difficult adding a CH_TXTATR to
+RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY, due to the overwriting and
+splitting of exising hints that is necessary for backward compatibility.
+
+ @param rNode the text node
+ @param rHint the hint to be inserted
+ @returns true iff hint was successfully inserted
+*/
+bool
+SwpHints::TryInsertNesting( SwTxtNode & rNode, SwTxtAttrNesting & rNewHint )
+{
+// INVARIANT: the nestable hints in the array are properly nested
+ const sal_uInt16 nNewWhich( rNewHint.Which() );
+ const xub_StrLen nNewStart( *rNewHint.GetStart() );
+ const xub_StrLen nNewEnd ( *rNewHint.GetEnd() );
+//??? const bool bNoLengthAttribute( nNewStart == nNewEnd );
+ const bool bNewSelfNestable( isSelfNestable(nNewWhich) );
+
+ OSL_ENSURE( (RES_TXTATR_INETFMT == nNewWhich) ||
+ (RES_TXTATR_CJK_RUBY == nNewWhich) ||
+ (RES_TXTATR_META == nNewWhich) ||
+ (RES_TXTATR_METAFIELD == nNewWhich),
+ "TryInsertNesting: Expecting INETFMT or RUBY or META or METAFIELD" );
+
+ 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 ( sal_uInt16 i = 0; i < GetEndCount(); ++i )
+ {
+ SwTxtAttr * const pOther = GetEnd(i);
+
+ if (pOther->IsNesting())
+ {
+ const sal_uInt16 nOtherWhich( pOther->Which() );
+ const xub_StrLen nOtherStart( *(pOther)->GetStart() );
+ const xub_StrLen nOtherEnd ( *(pOther)->GetEnd() );
+ if (isOverlap(nNewStart, nNewEnd, nOtherStart, nOtherEnd ))
+ {
+ switch (splitPolicy(nNewWhich, nOtherWhich))
+ {
+ case FAIL:
+ OSL_TRACE("cannot insert hint: overlap detected");
+ ::std::for_each(SplitNew.begin(), SplitNew.end(),
+ TxtAttrDeleter(*rNode.GetDoc()));
+ return false;
+ case SPLIT_NEW:
+ lcl_DoSplitNew(SplitNew, rNode, nNewStart,
+ nOtherStart, nOtherEnd, pOther->HasDummyChar());
+ break;
+ case SPLIT_OTHER:
+ OverlappingExisting.push_back(
+ static_cast<SwTxtAttrNesting*>(pOther));
+ break;
+ default:
+ OSL_FAIL("bad code monkey");
+ break;
+ }
+ }
+ else if (isNestedAny(nNewStart, nNewEnd, nOtherStart, nOtherEnd))
+ {
+ if (!bNewSelfNestable && (nNewWhich == nOtherWhich))
+ {
+ // ruby and hyperlink: if there is nesting, _overwrite_
+ OverwrittenExisting.push_back(
+ static_cast<SwTxtAttrNesting*>(pOther));
+ }
+ else if ((nNewStart == nOtherStart) && pOther->HasDummyChar())
+ {
+ if (rNewHint.HasDummyChar())
+ {
+ OSL_FAIL("ERROR: inserting duplicate CH_TXTATR hint");
+ return false;
+ } else if (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
+ xub_StrLen *const pStart(SplitNew.front()->GetStart());
+ OSL_ENSURE(*pStart == nNewStart, "how did that happen?");
+ *pStart = nNewStart + 1;
+ }
+ }
+ }
+ }
+ }
+
+ OSL_ENSURE(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 raison d'^etre for pass 4)
+ for (NestList_t::iterator itOther = OverlappingExisting.begin();
+ itOther != OverlappingExisting.end(); ++itOther)
+ {
+ const xub_StrLen nOtherStart( *(*itOther)->GetStart() );
+ const xub_StrLen nOtherEnd ( *(*itOther)->GetEnd() );
+
+ for (NestList_t::iterator itNew = SplitNew.begin();
+ itNew != SplitNew.end(); ++itNew)
+ {
+ const xub_StrLen nSplitNewStart( *(*itNew)->GetStart() );
+ const xub_StrLen nSplitNewEnd ( *(*itNew)->GetEnd() );
+ // 4 cases: within, around, overlap l, overlap r, (OTHER: no action)
+ const bool bRemoveOverlap(
+ !bNewSelfNestable && (nNewWhich == (*itOther)->Which()) );
+
+ switch (ComparePosition(nSplitNewStart, nSplitNewEnd,
+ nOtherStart, nOtherEnd))
+ {
+ case POS_INSIDE:
+ {
+ OSL_ENSURE(!bRemoveOverlap,
+ "this one should be in OverwrittenExisting?");
+ }
+ break;
+ case POS_OUTSIDE:
+ case POS_EQUAL:
+ {
+ OSL_FAIL("existing hint inside new hint: why?");
+ }
+ break;
+ case POS_OVERLAP_BEFORE:
+ {
+ Delete( *itOther ); // this also does NoteInHistory!
+ *(*itOther)->GetStart() = nSplitNewEnd;
+ InsertNesting( **itOther );
+ if (!bRemoveOverlap)
+ {
+ if ( USHRT_MAX == Count() )
+ {
+ OSL_FAIL("hints array full :-(");
+ return false;
+ }
+ SwTxtAttrNesting * const pOtherLeft(
+ MakeTxtAttrNesting( rNode, **itOther,
+ nOtherStart, nSplitNewEnd ) );
+ InsertNesting( *pOtherLeft );
+ }
+ }
+ break;
+ case POS_OVERLAP_BEHIND:
+ {
+ Delete( *itOther ); // this also does NoteInHistory!
+ *(*itOther)->GetEnd() = nSplitNewStart;
+ InsertNesting( **itOther );
+ if (!bRemoveOverlap)
+ {
+ if ( USHRT_MAX == Count() )
+ {
+ OSL_FAIL("hints array full :-(");
+ return false;
+ }
+ SwTxtAttrNesting * const pOtherRight(
+ MakeTxtAttrNesting( rNode, **itOther,
+ nSplitNewStart, nOtherEnd ) );
+ InsertNesting( *pOtherRight );
+ }
+ }
+ break;
+ default:
+ break; // overlap resolved by splitting new: nothing to do
+ }
+ }
+ }
+
+ if ( USHRT_MAX - SplitNew.size() <= Count() )
+ {
+ OSL_FAIL("hints array full :-(");
+ return false;
+ }
+
+ // pass 3: insert new hints
+ for (NestList_t::iterator iter = SplitNew.begin();
+ iter != SplitNew.end(); ++iter)
+ {
+ InsertNesting(**iter);
+ }
+
+ // pass 4: handle overwritten hints
+ // RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY should displace attributes
+ // of the same kind.
+ for (NestList_t::iterator itOther = OverwrittenExisting.begin();
+ itOther != OverwrittenExisting.end(); ++itOther)
+ {
+ const xub_StrLen nOtherStart( *(*itOther)->GetStart() );
+ const xub_StrLen nOtherEnd ( *(*itOther)->GetEnd() );
+
+ // overwritten portion is given by start/end of inserted hint
+ if ((nNewStart <= nOtherStart) && (nOtherEnd <= nNewEnd))
+ {
+ Delete(*itOther);
+ rNode.DestroyAttr( *itOther );
+ }
+ else
+ {
+ OSL_ENSURE((nOtherStart < nNewStart) && (nNewEnd < nOtherEnd), "huh?");
+ // scenario: there is a RUBY, and contained within that a META;
+ // now a RUBY is inserted within the META => the exising 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( *itOther ); // this also does NoteInHistory!
+ *(*itOther)->GetEnd() = nNewStart;
+ bool bSuccess( TryInsertNesting(rNode, **itOther) );
+ OSL_ENSURE(bSuccess, "recursive call 1 failed?");
+ SwTxtAttrNesting * const pOtherRight(
+ MakeTxtAttrNesting(
+ rNode, **itOther, nNewEnd, nOtherEnd ) );
+ bSuccess = TryInsertNesting(rNode, *pOtherRight);
+ OSL_ENSURE(bSuccess, "recursive call 2 failed?");
+ (void)bSuccess;
+ }
+
+ }
+
+ return true;
+}
+
+
+// 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.
+
+void SwpHints::BuildPortions( SwTxtNode& rNode, SwTxtAttr& rNewHint,
+ const SetAttrMode nMode )
+{
+ const sal_uInt16 nWhich = rNewHint.Which();
+
+ const xub_StrLen nThisStart = *rNewHint.GetStart();
+ const xub_StrLen nThisEnd = *rNewHint.GetEnd();
+ const bool bNoLengthAttribute = nThisStart == nThisEnd;
+
+ std::vector<SwTxtAttr*> aInsDelHints;
+ std::vector<SwTxtAttr*>::iterator aIter;
+
+ OSL_ENSURE( RES_TXTATR_CHARFMT == rNewHint.Which() ||
+ RES_TXTATR_AUTOFMT == rNewHint.Which(),
+ "Expecting CHARFMT or AUTOFMT" );
+
+ //
+ // 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 ( sal_uInt16 i = 0; i < Count(); ++i )
+ {
+ SwTxtAttr* pOther = GetTextHint(i);
+
+ if ( RES_TXTATR_CHARFMT != pOther->Which() &&
+ RES_TXTATR_AUTOFMT != pOther->Which() )
+ continue;
+
+ xub_StrLen nOtherStart = *pOther->GetStart();
+ const xub_StrLen nOtherEnd = *pOther->GetEnd();
+
+ // Check if start of new attribute overlaps with pOther:
+ // Split pOther if necessary:
+ if ( nOtherStart < nThisStart && nThisStart < nOtherEnd )
+ {
+ SwTxtAttr* pNewAttr = MakeTxtAttr( *rNode.GetDoc(),
+ pOther->GetAttr(), nOtherStart, nThisStart );
+ if ( RES_TXTATR_CHARFMT == pOther->Which() )
+ static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( static_cast<SwTxtCharFmt*>(pOther)->GetSortNumber() );
+ aInsDelHints.push_back( pNewAttr );
+
+ NoteInHistory( pOther );
+ *pOther->GetStart() = nThisStart;
+ NoteInHistory( pOther, true );
+
+ nOtherStart = nThisStart;
+ }
+
+ // Check if end of new attribute overlaps with pOther:
+ // Split pOther if necessary:
+ if ( nOtherStart < nThisEnd && nThisEnd < nOtherEnd )
+ {
+ SwTxtAttr* pNewAttr = MakeTxtAttr( *rNode.GetDoc(),
+ pOther->GetAttr(), nOtherStart, nThisEnd );
+ if ( RES_TXTATR_CHARFMT == pOther->Which() )
+ static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( static_cast<SwTxtCharFmt*>(pOther)->GetSortNumber() );
+ aInsDelHints.push_back( pNewAttr );
+
+ NoteInHistory( pOther );
+ *pOther->GetStart() = nThisEnd;
+ NoteInHistory( pOther, true );
+ }
+ }
+
+ // Insert the newly created attributes:
+ for ( aIter = aInsDelHints.begin(); aIter != aInsDelHints.end(); ++aIter )
+ {
+ SwpHintsArray::Insert( *aIter );
+ NoteInHistory( *aIter, true );
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ if( !rNode.GetDoc()->IsInReading() )
+ CHECK;
+#endif
+
+ //
+ // 4. Split rNewHint into 1 ... n new hints:
+ //
+ std::set<xub_StrLen> aBounds;
+ aBounds.insert( nThisStart );
+ aBounds.insert( nThisEnd );
+
+ if ( !bNoLengthAttribute ) // nothing to do for no length attributes
+ {
+ for ( sal_uInt16 i = 0; i < Count(); ++i )
+ {
+ const SwTxtAttr* pOther = GetTextHint(i);
+
+ if ( RES_TXTATR_CHARFMT != pOther->Which() &&
+ RES_TXTATR_AUTOFMT != pOther->Which() )
+ continue;
+
+ const xub_StrLen nOtherStart = *pOther->GetStart();
+ const xub_StrLen nOtherEnd = *pOther->GetEnd();
+
+ aBounds.insert( nOtherStart );
+ aBounds.insert( nOtherEnd );
+ }
+ }
+
+ std::set<xub_StrLen>::iterator aStartIter = aBounds.lower_bound( nThisStart );
+ std::set<xub_StrLen>::iterator aEndIter = aBounds.upper_bound( nThisEnd );
+ xub_StrLen nPorStart = *aStartIter;
+ ++aStartIter;
+ bool bDestroyHint = true;
+
+ //
+ // Insert the 1...n new parts of the new attribute:
+ //
+ while ( aStartIter != aEndIter || bNoLengthAttribute )
+ {
+ OSL_ENSURE( bNoLengthAttribute || nPorStart < *aStartIter, "AUTOSTYLES: BuildPortion trouble" );
+
+ const xub_StrLen nPorEnd = bNoLengthAttribute ? nPorStart : *aStartIter;
+ aInsDelHints.clear();
+
+ // Get all hints that are in [nPorStart, nPorEnd[:
+ for ( sal_uInt16 i = 0; i < Count(); ++i )
+ {
+ SwTxtAttr *pOther = GetTextHint(i);
+
+ if ( RES_TXTATR_CHARFMT != pOther->Which() &&
+ RES_TXTATR_AUTOFMT != pOther->Which() )
+ continue;
+
+ const xub_StrLen nOtherStart = *pOther->GetStart();
+
+ if ( nOtherStart > nPorStart )
+ break;
+
+ if ( pOther->GetEnd() && *pOther->GetEnd() == nPorEnd && nOtherStart == nPorStart )
+ {
+ OSL_ENSURE( *pOther->GetEnd() == nPorEnd, "AUTOSTYLES: BuildPortion trouble" );
+ aInsDelHints.push_back( pOther );
+ }
+ }
+
+ SwTxtAttr* pNewAttr = 0;
+ 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;
+ aIter = aInsDelHints.begin();
+ while ( aIter != aInsDelHints.end() )
+ {
+ if ( RES_TXTATR_CHARFMT == (*aIter)->Which() )
+ {
+ // --> FME 2007-02-16 #i74589#
+ const SwFmtCharFmt& rOtherCharFmt = (*aIter)->GetCharFmt();
+ const SwFmtCharFmt& rThisCharFmt = rNewHint.GetCharFmt();
+ const bool bSameCharFmt = rOtherCharFmt.GetCharFmt() == rThisCharFmt.GetCharFmt();
+ // <--
+
+ // --> OD 2009-03-24 #i90311#
+ // Do not remove existing character format hint during XML import
+ if ( !rNode.GetDoc()->IsInXMLImport() &&
+ ( !( nsSetAttrMode::SETATTR_DONTREPLACE & nMode ) ||
+ bNoLengthAttribute ||
+ bSameCharFmt ) )
+ // <--
+ {
+ // Remove old hint
+ Delete( *aIter );
+ rNode.DestroyAttr( *aIter );
+ }
+ else
+ ++nCharStyleCount;
+ }
+ else
+ {
+ // remove all attributes from auto styles, which are explicitely set in
+ // the new character format:
+ OSL_ENSURE( RES_TXTATR_AUTOFMT == (*aIter)->Which(), "AUTOSTYLES - Misc trouble" );
+ SwTxtAttr* pOther = *aIter;
+ boost::shared_ptr<SfxItemSet> pOldStyle = static_cast<const SwFmtAutoFmt&>(pOther->GetAttr()).GetStyleHandle();
+
+ // For each attribute in the automatic style check if it
+ // is also set the the new character style:
+ SfxItemSet aNewSet( *pOldStyle->GetPool(),
+ aCharAutoFmtSetRange);
+ SfxItemIter aItemIter( *pOldStyle );
+ const SfxPoolItem* pItem = aItemIter.GetCurItem();
+ while( sal_True )
+ {
+ if ( !CharFmt::IsItemIncluded( pItem->Which(), &rNewHint ) )
+ {
+ aNewSet.Put( *pItem );
+ }
+
+ if( aItemIter.IsAtEnd() )
+ break;
+
+ pItem = aItemIter.NextItem();
+ }
+
+ // Remove old hint
+ Delete( pOther );
+ rNode.DestroyAttr( pOther );
+
+ // Create new AutoStyle
+ if ( aNewSet.Count() )
+ {
+ pNewAttr = MakeTxtAttr( *rNode.GetDoc(),
+ aNewSet, nPorStart, nPorEnd );
+ SwpHintsArray::Insert( pNewAttr );
+ NoteInHistory( pNewAttr, true );
+ }
+ }
+ ++aIter;
+ }
+
+ // 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 = MakeTxtAttr( *rNode.GetDoc(), rNewHint.GetAttr(),
+ nPorStart, nPorEnd );
+ static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( nCharStyleCount );
+ }
+ }
+ else
+ {
+ // Find the current autostyle. Mix attributes if necessary.
+ SwTxtAttr* pCurrentAutoStyle = 0;
+ SwTxtAttr* pCurrentCharFmt = 0;
+ aIter = aInsDelHints.begin();
+ while ( aIter != aInsDelHints.end() )
+ {
+ if ( RES_TXTATR_AUTOFMT == (*aIter)->Which() )
+ pCurrentAutoStyle = *aIter;
+ else if ( RES_TXTATR_CHARFMT == (*aIter)->Which() )
+ pCurrentCharFmt = *aIter;
+ ++aIter;
+ }
+
+ boost::shared_ptr<SfxItemSet> pNewStyle = static_cast<const SwFmtAutoFmt&>(rNewHint.GetAttr()).GetStyleHandle();
+ if ( pCurrentAutoStyle )
+ {
+ boost::shared_ptr<SfxItemSet> pCurrentStyle = static_cast<const SwFmtAutoFmt&>(pCurrentAutoStyle->GetAttr()).GetStyleHandle();
+
+ // Merge attributes
+ SfxItemSet aNewSet( *pCurrentStyle );
+ aNewSet.Put( *pNewStyle );
+
+ // --> FME 2007-4-11 #i75750# Remove attributes already set at whole paragraph
+ // --> FME 2007-09-24 #i81764# This should not be applied for no length attributes!!! <--
+ if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && aNewSet.Count() )
+ {
+ SfxItemIter aIter2( aNewSet );
+ const SfxPoolItem* pItem = aIter2.GetCurItem();
+ const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet();
+
+ do
+ {
+ const SfxPoolItem* pTmpItem = 0;
+ if ( SFX_ITEM_SET == rWholeParaAttrSet.GetItemState( pItem->Which(), sal_False, &pTmpItem ) &&
+ pTmpItem == pItem )
+ {
+ // Do not clear item if the attribute is set in a character format:
+ if ( !pCurrentCharFmt || 0 == CharFmt::GetItem( *pCurrentCharFmt, pItem->Which() ) )
+ aNewSet.ClearItem( pItem->Which() );
+ }
+ }
+ while (!aIter2.IsAtEnd() && 0 != (pItem = aIter2.NextItem()));
+ }
+ // <--
+
+ // Remove old hint
+ Delete( pCurrentAutoStyle );
+ rNode.DestroyAttr( pCurrentAutoStyle );
+
+ // Create new AutoStyle
+ if ( aNewSet.Count() )
+ pNewAttr = MakeTxtAttr( *rNode.GetDoc(), aNewSet,
+ nPorStart, nPorEnd );
+ }
+ else
+ {
+ // Remove any attributes which are already set at the whole paragraph:
+ bool bOptimizeAllowed = true;
+
+ SfxItemSet* pNewSet = 0;
+ // --> FME 2007-4-11 #i75750# Remove attributes already set at whole paragraph
+ // --> FME 2007-09-24 #i81764# This should not be applied for no length attributes!!! <--
+ if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && pNewStyle->Count() )
+ {
+ SfxItemIter aIter2( *pNewStyle );
+ const SfxPoolItem* pItem = aIter2.GetCurItem();
+ const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet();
+
+ do
+ {
+ const SfxPoolItem* pTmpItem = 0;
+ if ( SFX_ITEM_SET == rWholeParaAttrSet.GetItemState( pItem->Which(), sal_False, &pTmpItem ) &&
+ pTmpItem == pItem )
+ {
+ // Do not clear item if the attribute is set in a character format:
+ if ( !pCurrentCharFmt || 0 == CharFmt::GetItem( *pCurrentCharFmt, pItem->Which() ) )
+ {
+ if ( !pNewSet )
+ pNewSet = pNewStyle->Clone( sal_True );
+ pNewSet->ClearItem( pItem->Which() );
+ }
+ }
+ }
+ while (!aIter2.IsAtEnd() && 0 != (pItem = aIter2.NextItem()));
+
+ if ( pNewSet )
+ {
+ bOptimizeAllowed = false;
+ if ( pNewSet->Count() )
+ pNewStyle = rNode.getIDocumentStyleAccess().getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR );
+ else
+ pNewStyle.reset();
+
+ delete pNewSet;
+ }
+ }
+ // <--
+
+ // 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;
+ }
+ else if ( pNewStyle.get() )
+ {
+ pNewAttr = MakeTxtAttr( *rNode.GetDoc(), *pNewStyle,
+ nPorStart, nPorEnd );
+ }
+ }
+ }
+
+ if ( pNewAttr )
+ {
+ SwpHintsArray::Insert( pNewAttr );
+// if ( bDestroyHint )
+ NoteInHistory( pNewAttr, true );
+ }
+
+ if ( !bNoLengthAttribute )
+ {
+ nPorStart = *aStartIter;
+ ++aStartIter;
+ }
+ else
+ break;
+ }
+
+ if ( bDestroyHint )
+ rNode.DestroyAttr( &rNewHint );
+}
+
+/*************************************************************************
+ * SwTxtNode::MakeTxtAttr()
+ *************************************************************************/
+
+SwTxtAttr* MakeRedlineTxtAttr( SwDoc & rDoc, SfxPoolItem & 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:
+ OSL_FAIL("unsupported redline attribute");
+ break;
+ }
+
+ // Put new attribute into pool
+ // FIXME: this const_cast is evil!
+ SfxPoolItem& rNew =
+ const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) );
+ return new SwTxtAttrEnd( rNew, 0, 0 );
+}
+
+// create new text attribute
+SwTxtAttr* MakeTxtAttr( SwDoc & rDoc, SfxPoolItem& rAttr,
+ xub_StrLen const nStt, xub_StrLen const nEnd,
+ CopyOrNew_t const bIsCopy, SwTxtNode *const pTxtNode)
+{
+ if ( isCHRATR(rAttr.Which()) )
+ {
+ // Somebody wants to build a SwTxtAttr for a character attribute.
+ // Sorry, this is not allowed any longer.
+ // You'll get a brand new autostyle attribute:
+ SfxItemSet aItemSet( rDoc.GetAttrPool(),
+ RES_CHRATR_BEGIN, RES_CHRATR_END );
+ aItemSet.Put( rAttr );
+ return MakeTxtAttr( rDoc, aItemSet, nStt, nEnd );
+ }
+ else if ( RES_TXTATR_AUTOFMT == rAttr.Which() &&
+ static_cast<const SwFmtAutoFmt&>(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 StylePool::SfxItemSet_Pointer_t pAutoStyle = static_cast<const SwFmtAutoFmt&>(rAttr).GetStyleHandle();
+ ::std::auto_ptr<const SfxItemSet> pNewSet(
+ pAutoStyle->SfxItemSet::Clone( sal_True, &rDoc.GetAttrPool() ));
+ SwTxtAttr* pNew = MakeTxtAttr( rDoc, *pNewSet, nStt, nEnd );
+ return pNew;
+ }
+
+ // Put new attribute into pool
+ // FIXME: this const_cast is evil!
+ SfxPoolItem& rNew =
+ const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) );
+
+ SwTxtAttr* pNew = 0;
+ switch( rNew.Which() )
+ {
+ case RES_TXTATR_CHARFMT:
+ {
+ SwFmtCharFmt &rFmtCharFmt = (SwFmtCharFmt&) rNew;
+ if( !rFmtCharFmt.GetCharFmt() )
+ {
+ rFmtCharFmt.SetCharFmt( rDoc.GetDfltCharFmt() );
+ }
+
+ pNew = new SwTxtCharFmt( rFmtCharFmt, nStt, nEnd );
+ }
+ break;
+ case RES_TXTATR_INETFMT:
+ pNew = new SwTxtINetFmt( (SwFmtINetFmt&)rNew, nStt, nEnd );
+ break;
+ case RES_TXTATR_FIELD:
+ pNew = new SwTxtFld( static_cast<SwFmtFld &>(rNew), nStt );
+ break;
+ case RES_TXTATR_FLYCNT:
+ {
+ // erst hier wird das Frame-Format kopiert (mit Inhalt) !!
+ pNew = new SwTxtFlyCnt( (SwFmtFlyCnt&)rNew, nStt );
+ // Kopie von einem Text-Attribut
+ if ( static_cast<const SwFmtFlyCnt &>(rAttr).GetTxtFlyCnt() )
+ {
+ // then the format must be copied
+ static_cast<SwTxtFlyCnt *>(pNew)->CopyFlyFmt( &rDoc );
+ }
+ }
+ break;
+ case RES_TXTATR_FTN:
+ pNew = new SwTxtFtn( (SwFmtFtn&)rNew, nStt );
+ // ggfs. SeqNo kopieren
+ if( ((SwFmtFtn&)rAttr).GetTxtFtn() )
+ ((SwTxtFtn*)pNew)->SetSeqNo( ((SwFmtFtn&)rAttr).GetTxtFtn()->GetSeqRefNo() );
+ break;
+ case RES_TXTATR_REFMARK:
+ pNew = nStt == nEnd
+ ? new SwTxtRefMark( (SwFmtRefMark&)rNew, nStt )
+ : new SwTxtRefMark( (SwFmtRefMark&)rNew, nStt, &nEnd );
+ break;
+ case RES_TXTATR_TOXMARK:
+ pNew = new SwTxtTOXMark( (SwTOXMark&)rNew, nStt, &nEnd );
+ break;
+ case RES_TXTATR_CJK_RUBY:
+ pNew = new SwTxtRuby( (SwFmtRuby&)rNew, nStt, nEnd );
+ break;
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ pNew = SwTxtMeta::CreateTxtMeta( rDoc.GetMetaFieldManager(), pTxtNode,
+ static_cast<SwFmtMeta&>(rNew), nStt, nEnd, bIsCopy );
+ break;
+ default:
+ OSL_ENSURE(RES_TXTATR_AUTOFMT == rNew.Which(), "unknown attribute");
+ pNew = new SwTxtAttrEnd( rNew, nStt, nEnd );
+ break;
+ }
+
+ return pNew;
+}
+
+SwTxtAttr* MakeTxtAttr( SwDoc & rDoc, const SfxItemSet& rSet,
+ xub_StrLen nStt, xub_StrLen nEnd )
+{
+ IStyleAccess& rStyleAccess = rDoc.GetIStyleAccess();
+ const StylePool::SfxItemSet_Pointer_t pAutoStyle = rStyleAccess.getAutomaticStyle( rSet, IStyleAccess::AUTO_STYLE_CHAR );
+ SwFmtAutoFmt aNewAutoFmt;
+ aNewAutoFmt.SetStyleHandle( pAutoStyle );
+ SwTxtAttr* pNew = MakeTxtAttr( rDoc, aNewAutoFmt, nStt, nEnd );
+ return pNew;
+}
+
+
+// loesche das Text-Attribut (muss beim Pool abgemeldet werden!)
+void SwTxtNode::DestroyAttr( SwTxtAttr* pAttr )
+{
+ if( pAttr )
+ {
+ // einige Sachen muessen vorm Loeschen der "Format-Attribute" erfolgen
+ SwDoc* pDoc = GetDoc();
+ sal_uInt16 nDelMsg = 0;
+ switch( pAttr->Which() )
+ {
+ case RES_TXTATR_FLYCNT:
+ {
+ // siehe auch die Anmerkung "Loeschen von Formaten
+ // zeichengebundener Frames" in fesh.cxx, SwFEShell::DelFmt()
+ SwFrmFmt* pFmt = pAttr->GetFlyCnt().GetFrmFmt();
+ if( pFmt ) // vom Undo auf 0 gesetzt ??
+ pDoc->DelLayoutFmt( (SwFlyFrmFmt*)pFmt );
+ }
+ break;
+
+ case RES_CHRATR_HIDDEN:
+ SetCalcHiddenCharFlags();
+ break;
+
+ case RES_TXTATR_FTN:
+ ((SwTxtFtn*)pAttr)->SetStartNode( 0 );
+ nDelMsg = RES_FOOTNOTE_DELETED;
+ break;
+
+ case RES_TXTATR_FIELD:
+ if( !pDoc->IsInDtor() )
+ {
+ // Wenn wir ein HiddenParaField sind, dann muessen wir
+ // ggf. fuer eine Neuberechnung des Visible-Flags sorgen.
+ const SwField* pFld = pAttr->GetFld().GetFld();
+
+ //JP 06-08-95: DDE-Felder bilden eine Ausnahme
+ OSL_ENSURE( RES_DDEFLD == pFld->GetTyp()->Which() ||
+ this == ((SwTxtFld*)pAttr)->GetpTxtNode(),
+ "Wo steht denn dieses Feld?" );
+
+ // bestimmte Felder mussen am Doc das Calculations-Flag updaten
+ switch( pFld->GetTyp()->Which() )
+ {
+ case RES_HIDDENPARAFLD:
+ SetCalcHiddenParaField();
+ // kein break !
+ case RES_DBSETNUMBERFLD:
+ case RES_GETEXPFLD:
+ case RES_DBFLD:
+ case RES_SETEXPFLD:
+ case RES_HIDDENTXTFLD:
+ case RES_DBNUMSETFLD:
+ case RES_DBNEXTSETFLD:
+ if( !pDoc->IsNewFldLst() && GetNodes().IsDocNodes() )
+ pDoc->InsDelFldInFldLst( sal_False, *(SwTxtFld*)pAttr );
+ break;
+ case RES_DDEFLD:
+ if( GetNodes().IsDocNodes() &&
+ ((SwTxtFld*)pAttr)->GetpTxtNode() )
+ ((SwDDEFieldType*)pFld->GetTyp())->DecRefCnt();
+ break;
+ case RES_POSTITFLD:
+ {
+ const_cast<SwFmtFld&>(pAttr->GetFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pAttr)->GetFld(), SWFMTFLD_REMOVED ) );
+ break;
+ }
+ }
+ }
+ nDelMsg = RES_FIELD_DELETED;
+ break;
+
+ case RES_TXTATR_TOXMARK:
+ static_cast<SwTOXMark&>(pAttr->GetAttr()).InvalidateTOXMark();
+ break;
+
+ case RES_TXTATR_REFMARK:
+ nDelMsg = RES_REFMARK_DELETED;
+ break;
+
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ static_cast<SwTxtMeta*>(pAttr)->ChgTxtNode(0);
+ break;
+
+ default:
+ break;
+ }
+
+ if( nDelMsg && !pDoc->IsInDtor() && GetNodes().IsDocNodes() )
+ {
+ SwPtrMsgPoolItem aMsgHint( nDelMsg, (void*)&pAttr->GetAttr() );
+ pDoc->GetUnoCallBack()->ModifyNotification( &aMsgHint, &aMsgHint );
+ }
+
+ SwTxtAttr::Destroy( pAttr, pDoc->GetAttrPool() );
+ }
+}
+
+/*************************************************************************
+ * SwTxtNode::Insert()
+ *************************************************************************/
+
+SwTxtAttr*
+SwTxtNode::InsertItem( SfxPoolItem& rAttr,
+ const xub_StrLen nStart, const xub_StrLen nEnd, const SetAttrMode nMode )
+{
+ // character attributes will be inserted as automatic styles:
+ OSL_ENSURE( !isCHRATR(rAttr.Which()), "AUTOSTYLES - "
+ "SwTxtNode::InsertItem should not be called with character attributes");
+
+ SwTxtAttr *const pNew = MakeTxtAttr( *GetDoc(), rAttr, nStart, nEnd,
+ (nMode & nsSetAttrMode::SETATTR_IS_COPY) ? COPY : NEW, this );
+
+ if ( pNew )
+ {
+ const bool 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 succesful
+ // insertion, and thus destroyed!
+ if (!bSuccess || ( USHRT_MAX == m_pSwpHints->GetPos( pNew ) ))
+ {
+ return 0;
+ }
+ }
+
+ return pNew;
+}
+
+// take ownership of pAttr; if insertion fails, delete pAttr
+bool SwTxtNode::InsertHint( SwTxtAttr * const pAttr, const SetAttrMode nMode )
+{
+ sal_Bool bHiddenPara = sal_False;
+
+ OSL_ENSURE( pAttr && *pAttr->GetStart() <= Len(), "StartIdx out of bounds!" );
+ OSL_ENSURE( !pAttr->GetEnd() || (*pAttr->GetEnd() <= Len()),
+ "EndIdx out of bounds!" );
+
+ // translate from SetAttrMode to InsertMode (for hints with CH_TXTATR)
+ const enum IDocumentContentOperations::InsertFlags nInsertFlags =
+ (nMode & nsSetAttrMode::SETATTR_FORCEHINTEXPAND)
+ ? static_cast<IDocumentContentOperations::InsertFlags>(
+ IDocumentContentOperations::INS_FORCEHINTEXPAND |
+ IDocumentContentOperations::INS_EMPTYEXPAND)
+ : IDocumentContentOperations::INS_EMPTYEXPAND;
+
+ // need this after TryInsertHint, when pAttr may be deleted
+ const xub_StrLen nStart( *pAttr->GetStart() );
+ const bool bDummyChar( pAttr->HasDummyChar() );
+ if (bDummyChar)
+ {
+ sal_uInt16 nInsMode = nMode;
+ switch( pAttr->Which() )
+ {
+ case RES_TXTATR_FLYCNT:
+ {
+ SwTxtFlyCnt *pFly = (SwTxtFlyCnt *)pAttr;
+ SwFrmFmt* pFmt = pAttr->GetFlyCnt().GetFrmFmt();
+ if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) )
+ {
+ // Wir muessen zuerst einfuegen, da in SetAnchor()
+ // dem FlyFrm GetStart() uebermittelt wird.
+ //JP 11.05.98: falls das Anker-Attribut schon richtig
+ // gesetzt ist, dann korrigiere dieses nach dem Einfuegen
+ // des Zeichens. Sonst muesste das immer ausserhalb
+ // erfolgen (Fehleranfaellig !)
+ const SwFmtAnchor* pAnchor = 0;
+ pFmt->GetItemState( RES_ANCHOR, sal_False,
+ (const SfxPoolItem**)&pAnchor );
+
+ SwIndex aIdx( this, *pAttr->GetStart() );
+ const sal_Unicode c = GetCharOfTxtAttr(*pAttr);
+ InsertText( c, aIdx, nInsertFlags );
+ nInsMode |= nsSetAttrMode::SETATTR_NOTXTATRCHR;
+
+ if (pAnchor &&
+ (FLY_AS_CHAR == pAnchor->GetAnchorId()) &&
+ pAnchor->GetCntntAnchor() &&
+ pAnchor->GetCntntAnchor()->nNode == *this &&
+ pAnchor->GetCntntAnchor()->nContent == aIdx )
+ {
+ const_cast<SwIndex&>(
+ pAnchor->GetCntntAnchor()->nContent)--;
+ }
+ }
+ pFly->SetAnchor( this );
+
+ // Format-Pointer kann sich im SetAnchor geaendert haben!
+ // (Kopieren in andere Docs!)
+ pFmt = pAttr->GetFlyCnt().GetFrmFmt();
+ SwDoc *pDoc = pFmt->GetDoc();
+
+ // OD 26.06.2003 #108784# - allow drawing objects in header/footer.
+ // But don't allow control objects in header/footer
+ if( RES_DRAWFRMFMT == pFmt->Which() &&
+ pDoc->IsInHeaderFooter( pFmt->GetAnchor().GetCntntAnchor()->nNode ) )
+ {
+ SwDrawContact* pDrawContact =
+ static_cast<SwDrawContact*>(pFmt->FindContactObj());
+ if ( pDrawContact &&
+ pDrawContact->GetMaster() &&
+ ::CheckControlLayer( pDrawContact->GetMaster() ) )
+ {
+ // das soll nicht meoglich sein; hier verhindern
+ // Der Dtor des TxtHints loescht nicht das Zeichen.
+ // Wenn ein CH_TXTATR_.. vorliegt, dann muss man
+ // dieses explizit loeschen
+ if( nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode )
+ {
+ // loesche das Zeichen aus dem String !
+ OSL_ENSURE( ( CH_TXTATR_BREAKWORD ==
+ m_Text.GetChar(*pAttr->GetStart() ) ||
+ CH_TXTATR_INWORD ==
+ m_Text.GetChar(*pAttr->GetStart())),
+ "where is my attribute character?" );
+ m_Text.Erase( *pAttr->GetStart(), 1 );
+ // Indizies Updaten
+ SwIndex aTmpIdx( this, *pAttr->GetStart() );
+ Update( aTmpIdx, 1, sal_True );
+ }
+ // do not record deletion of Format!
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+ DestroyAttr( pAttr );
+ return false;
+ }
+ }
+ break;
+ }
+
+ case RES_TXTATR_FTN :
+ {
+ // Fussnoten, man kommt an alles irgendwie heran.
+ // CntntNode erzeugen und in die Inserts-Section stellen
+ SwDoc *pDoc = GetDoc();
+ SwNodes &rNodes = pDoc->GetNodes();
+
+ // FussNote in nicht Content-/Redline-Bereich einfuegen ??
+ if( StartOfSectionIndex() < rNodes.GetEndOfAutotext().GetIndex() )
+ {
+ // das soll nicht meoglich sein; hier verhindern
+ // Der Dtor des TxtHints loescht nicht das Zeichen.
+ // Wenn ein CH_TXTATR_.. vorliegt, dann muss man
+ // dieses explizit loeschen
+ if( nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode )
+ {
+ // loesche das Zeichen aus dem String !
+ OSL_ENSURE( ( CH_TXTATR_BREAKWORD ==
+ m_Text.GetChar(*pAttr->GetStart() ) ||
+ CH_TXTATR_INWORD ==
+ m_Text.GetChar(*pAttr->GetStart())),
+ "where is my attribute character?" );
+ m_Text.Erase( *pAttr->GetStart(), 1 );
+ // Indizies Updaten
+ SwIndex aTmpIdx( this, *pAttr->GetStart() );
+ Update( aTmpIdx, 1, sal_True );
+ }
+ DestroyAttr( pAttr );
+ return false;
+ }
+
+ // wird eine neue Fussnote eingefuegt ??
+ sal_Bool bNewFtn = 0 == ((SwTxtFtn*)pAttr)->GetStartNode();
+ if( bNewFtn )
+ {
+ ((SwTxtFtn*)pAttr)->MakeNewTextSection( GetNodes() );
+ SwRegHistory* pHist = GetpSwpHints()
+ ? GetpSwpHints()->GetHistory() : 0;
+ if( pHist )
+ pHist->ChangeNodeIndex( GetIndex() );
+ }
+ else if ( !GetpSwpHints() || !GetpSwpHints()->IsInSplitNode() )
+ {
+ // loesche alle Frames der Section, auf die der StartNode zeigt
+ sal_uLong nSttIdx =
+ ((SwTxtFtn*)pAttr)->GetStartNode()->GetIndex();
+ sal_uLong nEndIdx = rNodes[ nSttIdx++ ]->EndOfSectionIndex();
+ SwCntntNode* pCNd;
+ for( ; nSttIdx < nEndIdx; ++nSttIdx )
+ if( 0 != ( pCNd = rNodes[ nSttIdx ]->GetCntntNode() ))
+ pCNd->DelFrms();
+ }
+
+ if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) )
+ {
+ // Wir muessen zuerst einfuegen, da sonst gleiche Indizes
+ // entstehen koennen und das Attribut im _SortArr_ am
+ // Dokument nicht eingetrage wird.
+ SwIndex aNdIdx( this, *pAttr->GetStart() );
+ const sal_Unicode c = GetCharOfTxtAttr(*pAttr);
+ InsertText( c, aNdIdx, nInsertFlags );
+ nInsMode |= nsSetAttrMode::SETATTR_NOTXTATRCHR;
+ }
+
+ // Wir tragen uns am FtnIdx-Array des Docs ein ...
+ SwTxtFtn* pTxtFtn = 0;
+ if( !bNewFtn )
+ {
+ // eine alte Ftn wird umgehaengt (z.B. SplitNode)
+ for( sal_uInt16 n = 0; n < pDoc->GetFtnIdxs().Count(); ++n )
+ if( pAttr == pDoc->GetFtnIdxs()[n] )
+ {
+ // neuen Index zuweisen, dafuer aus dem SortArray
+ // loeschen und neu eintragen
+ pTxtFtn = pDoc->GetFtnIdxs()[n];
+ pDoc->GetFtnIdxs().Remove( n );
+ break;
+ }
+ // wenn ueber Undo der StartNode gesetzt wurde, kann
+ // der Index noch gar nicht in der Verwaltung stehen !!
+ }
+ if( !pTxtFtn )
+ pTxtFtn = (SwTxtFtn*)pAttr;
+
+ // fuers Update der Nummern und zum Sortieren
+ // muss der Node gesetzt sein.
+ ((SwTxtFtn*)pAttr)->ChgTxtNode( this );
+
+ // FussNote im Redline-Bereich NICHT ins FtnArray einfuegen!
+ if( StartOfSectionIndex() > rNodes.GetEndOfRedlines().GetIndex() )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ const sal_Bool bSuccess =
+#endif
+ pDoc->GetFtnIdxs().Insert( pTxtFtn );
+#if OSL_DEBUG_LEVEL > 1
+ OSL_ENSURE( bSuccess, "FtnIdx nicht eingetragen." );
+#endif
+ }
+ SwNodeIndex aTmpIndex( *this );
+ pDoc->GetFtnIdxs().UpdateFtn( aTmpIndex);
+ ((SwTxtFtn*)pAttr)->SetSeqRefNo();
+ }
+ break;
+
+ case RES_TXTATR_FIELD:
+ {
+ // fuer HiddenParaFields Benachrichtigungsmechanismus
+ // anwerfen
+ if( RES_HIDDENPARAFLD ==
+ pAttr->GetFld().GetFld()->GetTyp()->Which() )
+ bHiddenPara = sal_True;
+ }
+ break;
+
+ }
+ // Fuer SwTxtHints ohne Endindex werden CH_TXTATR_..
+ // eingefuegt, aStart muss danach um einen zurueckgesetzt werden.
+ // Wenn wir im SwTxtNode::Copy stehen, so wurde das Zeichen bereits
+ // mitkopiert. In solchem Fall ist SETATTR_NOTXTATRCHR angegeben worden.
+ if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) )
+ {
+ SwIndex aIdx( this, *pAttr->GetStart() );
+ InsertText( GetCharOfTxtAttr(*pAttr), aIdx, nInsertFlags );
+
+ // adjust end of hint to account for inserted CH_TXTATR
+ xub_StrLen * const pEnd(pAttr->GetEnd());
+ if (pEnd)
+ {
+ *pEnd = *pEnd + 1;
+ }
+ }
+ }
+
+ GetOrCreateSwpHints();
+
+ // 4263: AttrInsert durch TextInsert => kein Adjust
+ const bool bRet = m_pSwpHints->TryInsertHint( pAttr, *this, nMode );
+
+ if (!bRet && bDummyChar)
+ {
+ // 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
+ if ( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nMode) )
+ {
+ OSL_ENSURE( ( CH_TXTATR_BREAKWORD == m_Text.GetChar(nStart) ||
+ CH_TXTATR_INWORD == m_Text.GetChar(nStart) ),
+ "where is my attribute character?" );
+ SwIndex aIdx( this, nStart );
+ EraseText( aIdx, 1 );
+ }
+ }
+
+ if ( bHiddenPara )
+ {
+ SetCalcHiddenParaField();
+ }
+
+ return bRet;
+}
+
+
+/*************************************************************************
+ * SwTxtNode::DeleteAttribute()
+ *************************************************************************/
+
+void SwTxtNode::DeleteAttribute( SwTxtAttr * const pAttr )
+{
+ if ( !HasHints() )
+ {
+ OSL_FAIL("DeleteAttribute called, but text node without hints?");
+ return;
+ }
+
+ if ( pAttr->HasDummyChar() )
+ {
+ // Unbedingt Copy-konstruieren!
+ const SwIndex aIdx( this, *pAttr->GetStart() );
+ // erase the CH_TXTATR, which will also delete pAttr
+ EraseText( aIdx, 1 );
+ }
+ else
+ {
+ // create MsgHint before start/end become invalid
+ SwUpdateAttr aHint(
+ *pAttr->GetStart(), *pAttr->GetEnd(), pAttr->Which() );
+ m_pSwpHints->Delete( pAttr );
+ SwTxtAttr::Destroy( pAttr, GetDoc()->GetAttrPool() );
+ NotifyClients( 0, &aHint );
+
+ TryDeleteSwpHints();
+ }
+}
+
+/*************************************************************************
+ * SwTxtNode::DeleteAttributes()
+ *************************************************************************/
+
+//FIXME: this does NOT respect SORT NUMBER (for CHARFMT)!
+void SwTxtNode::DeleteAttributes( const sal_uInt16 nWhich,
+ const xub_StrLen nStart, const xub_StrLen nEnd )
+{
+ if ( !HasHints() )
+ return;
+
+ for ( sal_uInt16 nPos = 0; m_pSwpHints && nPos < m_pSwpHints->Count(); nPos++ )
+ {
+ SwTxtAttr * const pTxtHt = m_pSwpHints->GetTextHint( nPos );
+ const xub_StrLen nHintStart = *(pTxtHt->GetStart());
+ if (nStart < nHintStart)
+ {
+ break; // sorted by start
+ }
+ else if ( (nStart == nHintStart) && (nWhich == pTxtHt->Which()) )
+ {
+ if ( nWhich == RES_CHRATR_HIDDEN )
+ {
+ OSL_FAIL("hey, that's a CHRATR! how did that get in?");
+ SetCalcHiddenCharFlags();
+ }
+ else if ( nWhich == RES_TXTATR_CHARFMT )
+ {
+ // Check if character format contains hidden attribute:
+ const SwCharFmt* pFmt = pTxtHt->GetCharFmt().GetCharFmt();
+ const SfxPoolItem* pItem;
+ if ( SFX_ITEM_SET == pFmt->GetItemState( RES_CHRATR_HIDDEN, sal_True, &pItem ) )
+ SetCalcHiddenCharFlags();
+ }
+ // --> FME 2007-03-16 #i75430# Recalc hidden flags if necessary
+ else if ( nWhich == RES_TXTATR_AUTOFMT )
+ {
+ // Check if auto style contains hidden attribute:
+ const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pTxtHt, RES_CHRATR_HIDDEN );
+ if ( pHiddenItem )
+ SetCalcHiddenCharFlags();
+ }
+ // <--
+
+ xub_StrLen const * const pEndIdx = pTxtHt->GetEnd();
+
+ if ( pTxtHt->HasDummyChar() )
+ {
+ // Unbedingt Copy-konstruieren!
+ const SwIndex aIdx( this, nStart );
+ // erase the CH_TXTATR, which will also delete pTxtHt
+ EraseText( aIdx, 1 );
+ }
+ else if( *pEndIdx == nEnd )
+ {
+ // den MsgHint jetzt fuettern, weil gleich sind
+ // Start und End weg.
+ // Das CalcVisibleFlag bei HiddenParaFields entfaellt,
+ // da dies das Feld im Dtor selbst erledigt.
+ SwUpdateAttr aHint( nStart, *pEndIdx, nWhich );
+ m_pSwpHints->DeleteAtPos( nPos ); // gefunden, loeschen,
+ SwTxtAttr::Destroy( pTxtHt, GetDoc()->GetAttrPool() );
+ NotifyClients( 0, &aHint );
+ }
+ }
+ }
+ TryDeleteSwpHints();
+}
+
+/*************************************************************************
+ * SwTxtNode::DelSoftHyph()
+ *************************************************************************/
+
+void SwTxtNode::DelSoftHyph( const xub_StrLen nStt, const xub_StrLen nEnd )
+{
+ xub_StrLen nFndPos = nStt, nEndPos = nEnd;
+ while( STRING_NOTFOUND !=
+ ( nFndPos = m_Text.Search( CHAR_SOFTHYPHEN, nFndPos )) &&
+ nFndPos < nEndPos )
+ {
+ const SwIndex aIdx( this, nFndPos );
+ EraseText( aIdx, 1 );
+ --nEndPos;
+ }
+}
+
+// setze diese Attribute am TextNode. Wird der gesamte Bereich umspannt,
+// dann setze sie nur im AutoAttrSet (SwCntntNode:: SetAttr)
+sal_Bool SwTxtNode::SetAttr( const SfxItemSet& rSet, xub_StrLen nStt,
+ xub_StrLen nEnd, const SetAttrMode nMode )
+{
+ if( !rSet.Count() )
+ return sal_False;
+
+ // teil die Sets auf (fuer Selektion in Nodes)
+ const SfxItemSet* pSet = &rSet;
+ SfxItemSet aTxtSet( *rSet.GetPool(), RES_TXTATR_BEGIN, RES_TXTATR_END-1 );
+
+ // gesamter Bereich
+ if ( !nStt && (nEnd == m_Text.Len()) &&
+ !(nMode & nsSetAttrMode::SETATTR_NOFORMATATTR ) )
+ {
+ // sind am Node schon Zeichenvorlagen gesetzt, muss man diese Attribute
+ // (rSet) immer als TextAttribute setzen, damit sie angezeigt werden.
+ int bHasCharFmts = sal_False;
+ if ( HasHints() )
+ {
+ for ( sal_uInt16 n = 0; n < m_pSwpHints->Count(); ++n )
+ {
+ if ( (*m_pSwpHints)[ n ]->IsCharFmtAttr() )
+ {
+ bHasCharFmts = sal_True;
+ break;
+ }
+ }
+ }
+
+ if( !bHasCharFmts )
+ {
+ aTxtSet.Put( rSet );
+ // If there are any character attributes in rSet,
+ // we want to set them at the paragraph:
+ if( aTxtSet.Count() != rSet.Count() )
+ {
+ sal_Bool bRet = SetAttr( rSet );
+ if( !aTxtSet.Count() )
+ return bRet;
+ }
+
+ // check for auto style:
+ const SfxPoolItem* pItem;
+ const bool bAutoStyle = SFX_ITEM_SET == aTxtSet.GetItemState( RES_TXTATR_AUTOFMT, sal_False, &pItem );
+ if ( bAutoStyle )
+ {
+ boost::shared_ptr<SfxItemSet> pAutoStyleSet = static_cast<const SwFmtAutoFmt*>(pItem)->GetStyleHandle();
+ sal_Bool bRet = SetAttr( *pAutoStyleSet );
+ if( 1 == aTxtSet.Count() )
+ return bRet;
+ }
+
+ // Continue with the text attributes:
+ pSet = &aTxtSet;
+ }
+ }
+
+ GetOrCreateSwpHints();
+
+ SfxItemSet aCharSet( *rSet.GetPool(), aCharAutoFmtSetRange );
+
+ sal_uInt16 nCount = 0;
+ SfxItemIter aIter( *pSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+
+ do
+ {
+ if ( pItem && (reinterpret_cast<SfxPoolItem*>(-1) != pItem))
+ {
+ const sal_uInt16 nWhich = pItem->Which();
+ OSL_ENSURE( isCHRATR(nWhich) || isTXTATR(nWhich),
+ "SwTxtNode::SetAttr(): unknown attribute" );
+ if ( isCHRATR(nWhich) || isTXTATR(nWhich) )
+ {
+ if ((RES_TXTATR_CHARFMT == nWhich) &&
+ (GetDoc()->GetDfltCharFmt() ==
+ static_cast<const SwFmtCharFmt*>(pItem)->GetCharFmt()))
+ {
+ SwIndex aIndex( this, nStt );
+ RstAttr( aIndex, nEnd - nStt, RES_TXTATR_CHARFMT, 0 );
+ DontExpandFmt( aIndex );
+ }
+ else
+ {
+ if (isCHRATR(nWhich) ||
+ (RES_TXTATR_UNKNOWN_CONTAINER == nWhich))
+ {
+ aCharSet.Put( *pItem );
+ }
+ else
+ {
+
+ SwTxtAttr *const pNew = MakeTxtAttr( *GetDoc(),
+ const_cast<SfxPoolItem&>(*pItem), nStt, nEnd );
+ if ( pNew )
+ {
+ if ( nEnd != nStt && !pNew->GetEnd() )
+ {
+ OSL_FAIL("Attribut without end, but area marked");
+ DestroyAttr( pNew ); // do not insert
+ }
+ else if ( InsertHint( pNew, nMode ) )
+ {
+ ++nCount;
+ }
+ }
+ }
+ }
+ }
+ }
+ if ( aIter.IsAtEnd() )
+ break;
+ pItem = aIter.NextItem();
+ } while( true );
+
+ if ( aCharSet.Count() )
+ {
+ SwTxtAttr* pTmpNew = MakeTxtAttr( *GetDoc(), aCharSet, nStt, nEnd );
+ if ( InsertHint( pTmpNew, nMode ) )
+ {
+ ++nCount;
+ }
+ }
+
+ TryDeleteSwpHints();
+
+ return nCount ? sal_True : sal_False;
+}
+
+void lcl_MergeAttr( SfxItemSet& rSet, const SfxPoolItem& rAttr )
+{
+ if ( RES_TXTATR_AUTOFMT == rAttr.Which() )
+ {
+ const SfxItemSet* pCFSet = CharFmt::GetItemSet( rAttr );
+ if ( !pCFSet )
+ return;
+ SfxWhichIter aIter( *pCFSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ if( ( nWhich < RES_CHRATR_END ||
+ RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) &&
+ ( SFX_ITEM_SET == pCFSet->GetItemState( nWhich, sal_True ) ) )
+ rSet.Put( pCFSet->Get( nWhich ) );
+ nWhich = aIter.NextWhich();
+ }
+ }
+ else
+ rSet.Put( rAttr );
+}
+
+void lcl_MergeAttr_ExpandChrFmt( SfxItemSet& rSet, const SfxPoolItem& rAttr )
+{
+ if( RES_TXTATR_CHARFMT == rAttr.Which() ||
+ RES_TXTATR_INETFMT == rAttr.Which() ||
+ RES_TXTATR_AUTOFMT == rAttr.Which() )
+ {
+ const SfxItemSet* pCFSet = CharFmt::GetItemSet( rAttr );
+
+ if ( pCFSet )
+ {
+ SfxWhichIter aIter( *pCFSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ if( ( nWhich < RES_CHRATR_END ||
+ ( RES_TXTATR_AUTOFMT == rAttr.Which() && RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) ) &&
+ ( SFX_ITEM_SET == pCFSet->GetItemState( nWhich, sal_True ) ) )
+ rSet.Put( pCFSet->Get( nWhich ) );
+ nWhich = aIter.NextWhich();
+ }
+ }
+ }
+
+ // aufnehmen als MergeWert (falls noch nicht gesetzt neu setzen!)
+
+/* wenn mehrere Attribute ueberlappen gewinnt der letze !!
+ z.B
+ 1234567890123456789
+ |------------| Font1
+ |------| Font2
+ ^ ^
+ |--| Abfragebereich: -> Gueltig ist Font2
+*/
+ rSet.Put( rAttr );
+}
+
+struct SwPoolItemEndPair
+{
+public:
+ const SfxPoolItem* mpItem;
+ xub_StrLen mnEndPos;
+
+ SwPoolItemEndPair() : mpItem( 0 ), mnEndPos( 0 ) {};
+};
+
+void lcl_MergeListLevelIndentAsLRSpaceItem( const SwTxtNode& rTxtNode,
+ SfxItemSet& rSet )
+{
+ if ( rTxtNode.AreListLevelIndentsApplicable() )
+ {
+ const SwNumRule* pRule = rTxtNode.GetNumRule();
+ if ( pRule && rTxtNode.GetActualListLevel() >= 0 )
+ {
+ const SwNumFmt& rFmt = pRule->Get(static_cast<sal_uInt16>(rTxtNode.GetActualListLevel()));
+ if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetTxtLeft( rFmt.GetIndentAt() );
+ aLR.SetTxtFirstLineOfst( static_cast<short>(rFmt.GetFirstLineIndent()) );
+ rSet.Put( aLR );
+ }
+ }
+ }
+}
+
+// erfrage die Attribute vom TextNode ueber den Bereich
+sal_Bool SwTxtNode::GetAttr( SfxItemSet& rSet, xub_StrLen nStt, xub_StrLen nEnd,
+ sal_Bool bOnlyTxtAttr, sal_Bool bGetFromChrFmt,
+ const bool bMergeIndentValuesOfNumRule ) const
+{
+ if( HasHints() )
+ {
+ /* stelle erstmal fest, welche Text-Attribut in dem Bereich gueltig
+ * sind. Dabei gibt es folgende Faelle:
+ * UnEindeutig wenn: (wenn != Format-Attribut)
+ * - das Attribut liegt vollstaendig im Bereich
+ * - das Attributende liegt im Bereich
+ * - der Attributanfang liegt im Bereich:
+ * Eindeutig (im Set mergen):
+ * - das Attrib umfasst den Bereich
+ * nichts tun:
+ * das Attribut liegt ausserhalb des Bereiches
+ */
+
+ void (*fnMergeAttr)( SfxItemSet&, const SfxPoolItem& )
+ = bGetFromChrFmt ? &lcl_MergeAttr_ExpandChrFmt
+ : &lcl_MergeAttr;
+
+ // dann besorge mal die Auto-(Fmt)Attribute
+ SfxItemSet aFmtSet( *rSet.GetPool(), rSet.GetRanges() );
+ if( !bOnlyTxtAttr )
+ {
+ SwCntntNode::GetAttr( aFmtSet );
+ if ( bMergeIndentValuesOfNumRule )
+ {
+ lcl_MergeListLevelIndentAsLRSpaceItem( *this, aFmtSet );
+ }
+ }
+
+ const sal_uInt16 nSize = m_pSwpHints->Count();
+
+ if( nStt == nEnd ) // kein Bereich:
+ {
+ for (sal_uInt16 n = 0; n < nSize; ++n)
+ {
+ const SwTxtAttr* pHt = (*m_pSwpHints)[n];
+ const xub_StrLen nAttrStart = *pHt->GetStart();
+ if( nAttrStart > nEnd ) // ueber den Bereich hinaus
+ break;
+
+ const xub_StrLen* pAttrEnd = pHt->GetEnd();
+ if ( ! pAttrEnd ) // no attributes without end
+ continue;
+
+ if( ( nAttrStart < nStt &&
+ ( pHt->DontExpand() ? nStt < *pAttrEnd
+ : nStt <= *pAttrEnd )) ||
+ ( nStt == nAttrStart &&
+ ( nAttrStart == *pAttrEnd || !nStt )))
+ (*fnMergeAttr)( rSet, pHt->GetAttr() );
+ }
+ }
+ else // es ist ein Bereich definiert
+ {
+ // --> FME 2007-03-13 #i75299#
+ ::std::auto_ptr< std::vector< SwPoolItemEndPair > > pAttrArr;
+ // <--
+
+ const sal_uInt16 coArrSz = static_cast<sal_uInt16>(RES_TXTATR_WITHEND_END) -
+ static_cast<sal_uInt16>(RES_CHRATR_BEGIN);
+
+ for (sal_uInt16 n = 0; n < nSize; ++n)
+ {
+ const SwTxtAttr* pHt = (*m_pSwpHints)[n];
+ const xub_StrLen nAttrStart = *pHt->GetStart();
+ if( nAttrStart > nEnd ) // ueber den Bereich hinaus
+ break;
+
+ const xub_StrLen* pAttrEnd = pHt->GetEnd();
+ if ( ! pAttrEnd ) // no attributes without end
+ continue;
+
+ sal_Bool bChkInvalid = sal_False;
+ if( nAttrStart <= nStt ) // vor oder genau Start
+ {
+ if( *pAttrEnd <= nStt ) // liegt davor
+ continue;
+
+ if( nEnd <= *pAttrEnd ) // hinter oder genau Ende
+ (*fnMergeAttr)( aFmtSet, pHt->GetAttr() );
+ else
+// else if( pHt->GetAttr() != aFmtSet.Get( pHt->Which() ) )
+ // uneindeutig
+ bChkInvalid = sal_True;
+ }
+ else if( nAttrStart < nEnd // reicht in den Bereich
+)// && pHt->GetAttr() != aFmtSet.Get( pHt->Which() ) )
+ bChkInvalid = sal_True;
+
+ if( bChkInvalid )
+ {
+ // uneindeutig ?
+ ::std::auto_ptr< SfxItemIter > pItemIter;
+ const SfxPoolItem* pItem = 0;
+
+ if ( RES_TXTATR_AUTOFMT == pHt->Which() )
+ {
+ const SfxItemSet* pAutoSet = CharFmt::GetItemSet( pHt->GetAttr() );
+ if ( pAutoSet )
+ {
+ pItemIter.reset( new SfxItemIter( *pAutoSet ) );
+ pItem = pItemIter->GetCurItem();
+ }
+ }
+ else
+ pItem = &pHt->GetAttr();
+
+ const sal_uInt16 nHintEnd = *pAttrEnd;
+
+ while ( pItem )
+ {
+ const sal_uInt16 nHintWhich = pItem->Which();
+ OSL_ENSURE(!isUNKNOWNATR(nHintWhich),
+ "SwTxtNode::GetAttr(): unknown attribute?");
+
+ if ( !pAttrArr.get() )
+ {
+ pAttrArr.reset(
+ new std::vector< SwPoolItemEndPair >(coArrSz));
+ }
+
+ std::vector< SwPoolItemEndPair >::iterator pPrev = pAttrArr->begin();
+ if (isCHRATR(nHintWhich) ||
+ isTXTATR_WITHEND(nHintWhich))
+ {
+ pPrev += nHintWhich - RES_CHRATR_BEGIN;
+ }
+ else
+ {
+ pPrev = pAttrArr->end();
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ SwPoolItemEndPair aTmp = *pPrev;
+#endif
+
+ if( pPrev != pAttrArr->end() )
+ {
+ if( !pPrev->mpItem )
+ {
+ if ( bOnlyTxtAttr || *pItem != aFmtSet.Get( nHintWhich ) )
+ {
+ if( nAttrStart > nStt )
+ {
+ rSet.InvalidateItem( nHintWhich );
+ pPrev->mpItem = (SfxPoolItem*)-1;
+ }
+ else
+ {
+ pPrev->mpItem = pItem;
+ pPrev->mnEndPos = nHintEnd;
+ }
+ }
+ }
+ else if( (SfxPoolItem*)-1 != pPrev->mpItem )
+ {
+ if( pPrev->mnEndPos == nAttrStart &&
+ *pPrev->mpItem == *pItem )
+ {
+ pPrev->mpItem = pItem;
+ pPrev->mnEndPos = nHintEnd;
+ }
+ else
+ {
+ rSet.InvalidateItem( nHintWhich );
+ pPrev->mpItem = (SfxPoolItem*)-1;
+ }
+ }
+ }
+
+ pItem = ( pItemIter.get() && !pItemIter->IsAtEnd() )
+ ? pItemIter->NextItem() : 0;
+ } // end while
+ }
+ }
+
+ if ( pAttrArr.get() )
+ {
+ for (sal_uInt16 n = 0; n < coArrSz; ++n)
+ {
+ const SwPoolItemEndPair& rItemPair = (*pAttrArr)[ n ];
+ if( (0 != rItemPair.mpItem) && ((SfxPoolItem*)-1 != rItemPair.mpItem) )
+ {
+ const sal_uInt16 nWh =
+ static_cast<sal_uInt16>(n + RES_CHRATR_BEGIN);
+
+ if( nEnd <= rItemPair.mnEndPos ) // hinter oder genau Ende
+ {
+ if( *rItemPair.mpItem != aFmtSet.Get( nWh ) )
+ (*fnMergeAttr)( rSet, *rItemPair.mpItem );
+ }
+ else
+ // uneindeutig
+ rSet.InvalidateItem( nWh );
+ }
+ }
+ }
+ }
+ if( aFmtSet.Count() )
+ {
+ // aus dem Format-Set alle entfernen, die im TextSet auch gesetzt sind
+ aFmtSet.Differentiate( rSet );
+ // jetzt alle zusammen "mergen"
+ rSet.Put( aFmtSet );
+ }
+ }
+ else if( !bOnlyTxtAttr )
+ {
+ // dann besorge mal die Auto-(Fmt)Attribute
+ SwCntntNode::GetAttr( rSet );
+ if ( bMergeIndentValuesOfNumRule )
+ {
+ lcl_MergeListLevelIndentAsLRSpaceItem( *this, rSet );
+ }
+ }
+
+ return rSet.Count() ? sal_True : sal_False;
+}
+
+
+namespace
+{
+
+typedef std::pair<sal_uInt16, sal_uInt16> AttrSpan_t;
+typedef std::multimap<AttrSpan_t, const SwTxtAttr*> AttrSpanMap_t;
+
+
+struct IsAutoStyle
+{
+ bool
+ operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
+ const
+ {
+ return i_rAttrSpan.second && i_rAttrSpan.second->Which() == RES_TXTATR_AUTOFMT;
+ }
+};
+
+
+/** Removes from io_rAttrSet all items that are set by style on the
+ given span.
+ */
+struct RemovePresentAttrs
+{
+ RemovePresentAttrs(SfxItemSet& io_rAttrSet)
+ : m_rAttrSet(io_rAttrSet)
+ {
+ }
+
+ void
+ operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
+ const
+ {
+ if (!i_rAttrSpan.second)
+ {
+ return;
+ }
+
+ const SwTxtAttr* const pAutoStyle(i_rAttrSpan.second);
+ SfxItemIter aIter(m_rAttrSet);
+ const SfxPoolItem* pItem(aIter.GetCurItem());
+ while (pItem)
+ {
+ const sal_uInt16 nWhich(pItem->Which());
+ if (CharFmt::IsItemIncluded(nWhich, pAutoStyle))
+ {
+ m_rAttrSet.ClearItem(nWhich);
+ }
+
+ if (aIter.IsAtEnd())
+ {
+ break;
+ }
+ pItem = aIter.NextItem();
+ }
+ }
+
+private:
+ SfxItemSet& m_rAttrSet;
+};
+
+
+/** Collects all style-covered spans from i_rHints to o_rSpanMap. In
+ addition inserts dummy spans with pointer to format equal to 0 for
+ all gaps (i.e. spans not covered by any style). This simplifies
+ creation of autostyles for all needed spans, but it means all code
+ that tries to access the pointer has to check if it's non-null!
+ */
+void
+lcl_CollectHintSpans(const SwpHints& i_rHints, const sal_uInt16 nLength,
+ AttrSpanMap_t& o_rSpanMap)
+{
+ sal_uInt16 nLastEnd(0);
+
+ for (sal_uInt16 i(0); i != i_rHints.Count(); ++i)
+ {
+ const SwTxtAttr* const pHint(i_rHints[i]);
+ const sal_uInt16 nWhich(pHint->Which());
+ if (nWhich == RES_TXTATR_CHARFMT || nWhich == RES_TXTATR_AUTOFMT)
+ {
+ const AttrSpan_t aSpan(*pHint->GetStart(), *pHint->GetEnd());
+ o_rSpanMap.insert(AttrSpanMap_t::value_type(aSpan, pHint));
+
+ if (aSpan.first != nLastEnd)
+ {
+ // insert dummy span covering the gap
+ o_rSpanMap.insert(AttrSpanMap_t::value_type(
+ AttrSpan_t(nLastEnd, aSpan.first), 0));
+ }
+
+ nLastEnd = aSpan.second;
+ }
+ }
+
+ // no hints at the end (special case: no hints at all in i_rHints)
+ if (nLastEnd != nLength && nLength != 0)
+ {
+ o_rSpanMap.insert(
+ AttrSpanMap_t::value_type(AttrSpan_t(nLastEnd, nLength), 0));
+ }
+}
+
+
+void
+lcl_FillWhichIds(const SfxItemSet& i_rAttrSet, std::vector<sal_uInt16>& o_rClearIds)
+{
+ o_rClearIds.reserve(i_rAttrSet.Count());
+ SfxItemIter aIter(i_rAttrSet);
+ const SfxPoolItem* pItem(aIter.GetCurItem());
+ while (pItem)
+ {
+ o_rClearIds.push_back(pItem->Which());
+
+ if (aIter.IsAtEnd())
+ {
+ break;
+ }
+ pItem = aIter.NextItem();
+ }
+}
+
+struct SfxItemSetClearer
+{
+ SfxItemSet & m_rItemSet;
+ SfxItemSetClearer(SfxItemSet & rItemSet) : m_rItemSet(rItemSet) { }
+ void operator()(sal_uInt16 const nWhich) { m_rItemSet.ClearItem(nWhich); }
+};
+
+}
+
+
+/** Does the hard work of SwTxtNode::FmtToTxtAttr: the real conversion
+ of items to automatic styles.
+ */
+void
+SwTxtNode::impl_FmtToTxtAttr(const SfxItemSet& i_rAttrSet)
+{
+ typedef AttrSpanMap_t::iterator AttrSpanMap_iterator_t;
+ AttrSpanMap_t aAttrSpanMap;
+
+ if (i_rAttrSet.Count() == 0)
+ {
+ return;
+ }
+
+ // 1. Identify all spans in hints' array
+
+ lcl_CollectHintSpans(*m_pSwpHints, m_Text.Len(), aAttrSpanMap);
+
+ // 2. Go through all spans and insert new attrs
+
+ AttrSpanMap_iterator_t aCurRange(aAttrSpanMap.begin());
+ const AttrSpanMap_iterator_t aEnd(aAttrSpanMap.end());
+ while (aCurRange != aEnd)
+ {
+ typedef std::pair<AttrSpanMap_iterator_t, AttrSpanMap_iterator_t>
+ AttrSpanMapRange_t;
+ AttrSpanMapRange_t aRange(aAttrSpanMap.equal_range(aCurRange->first));
+
+ // 2a. Collect attributes to insert
+
+ SfxItemSet aCurSet(i_rAttrSet);
+ std::for_each(aRange.first, aRange.second, RemovePresentAttrs(aCurSet));
+
+ // 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
+ SwTxtAttr* const pAutoStyle(const_cast<SwTxtAttr*>(aAutoStyleIt->second));
+ const boost::shared_ptr<SfxItemSet> pOldStyle(
+ static_cast<const SwFmtAutoFmt&>(
+ pAutoStyle->GetAttr()).GetStyleHandle());
+ aCurSet.Put(*pOldStyle);
+
+ // remove the old hint
+ m_pSwpHints->Delete(pAutoStyle);
+ DestroyAttr(pAutoStyle);
+ }
+ m_pSwpHints->Insert(
+ MakeTxtAttr(*GetDoc(), aCurSet,
+ aCurRange->first.first, aCurRange->first.second));
+ }
+
+ aCurRange = aRange.second;
+ }
+
+ // 3. Clear items from the node
+ std::vector<sal_uInt16> aClearedIds;
+ lcl_FillWhichIds(i_rAttrSet, aClearedIds);
+ ClearItemsFromAttrSet(aClearedIds);
+}
+
+void SwTxtNode::FmtToTxtAttr( SwTxtNode* pNd )
+{
+ SfxItemSet aThisSet( GetDoc()->GetAttrPool(), aCharFmtSetRange );
+ if( HasSwAttrSet() && GetpSwAttrSet()->Count() )
+ aThisSet.Put( *GetpSwAttrSet() );
+
+ GetOrCreateSwpHints();
+
+ if( pNd == this )
+ {
+ impl_FmtToTxtAttr(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
+
+ SfxItemSet aNdSet( pNd->GetDoc()->GetAttrPool(), aCharFmtSetRange );
+ if( pNd->HasSwAttrSet() && pNd->GetpSwAttrSet()->Count() )
+ aNdSet.Put( *pNd->GetpSwAttrSet() );
+
+ pNd->GetOrCreateSwpHints();
+
+ std::vector<sal_uInt16> aProcessedIds;
+
+ if( aThisSet.Count() )
+ {
+ SfxItemIter aIter( aThisSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem(), *pNdItem = 0;
+ SfxItemSet aConvertSet( GetDoc()->GetAttrPool(), aCharFmtSetRange );
+ std::vector<sal_uInt16> aClearWhichIds;
+
+ while( true )
+ {
+ if( SFX_ITEM_SET == aNdSet.GetItemState( pItem->Which(), sal_False, &pNdItem ) )
+ {
+ if (*pItem == *pNdItem) // 4
+ {
+ aClearWhichIds.push_back( pItem->Which() );
+ }
+ else // 5
+ {
+ aConvertSet.Put(*pItem);
+ }
+ aProcessedIds.push_back(pItem->Which());
+ }
+ else // 2
+ {
+ aConvertSet.Put(*pItem);
+ }
+
+ if( aIter.IsAtEnd() )
+ break;
+ pItem = aIter.NextItem();
+ }
+
+ // 4/ clear items of this that are set with the same value on pNd
+ ClearItemsFromAttrSet( aClearWhichIds );
+
+ // 2, 5/ convert all other items to attrs
+ impl_FmtToTxtAttr(aConvertSet);
+ }
+
+ {
+ std::for_each(aProcessedIds.begin(), aProcessedIds.end(),
+ SfxItemSetClearer(aNdSet));
+
+ // 3/ convert items to attrs
+ pNd->impl_FmtToTxtAttr(aNdSet);
+
+ if( aNdSet.Count() )
+ {
+ SwFmtChg aTmp1( pNd->GetFmtColl() );
+ pNd->NotifyClients( &aTmp1, &aTmp1 );
+ }
+ }
+ }
+
+ SetCalcHiddenCharFlags();
+
+ pNd->TryDeleteSwpHints();
+}
+
+/*************************************************************************
+ * SwpHints::CalcFlags()
+ *************************************************************************/
+
+void SwpHints::CalcFlags()
+{
+ m_bDDEFields = m_bFootnote = false;
+ const sal_uInt16 nSize = Count();
+ const SwTxtAttr* pAttr;
+ for( sal_uInt16 nPos = 0; nPos < nSize; ++nPos )
+ {
+ switch( ( pAttr = (*this)[ nPos ])->Which() )
+ {
+ case RES_TXTATR_FTN:
+ m_bFootnote = true;
+ if ( m_bDDEFields )
+ return;
+ break;
+ case RES_TXTATR_FIELD:
+ {
+ const SwField* pFld = pAttr->GetFld().GetFld();
+ if( RES_DDEFLD == pFld->GetTyp()->Which() )
+ {
+ m_bDDEFields = true;
+ if ( m_bFootnote )
+ return;
+ }
+ }
+ break;
+ }
+ }
+}
+
+/*************************************************************************
+ * SwpHints::CalcVisibleFlag()
+ *************************************************************************/
+
+bool SwpHints::CalcHiddenParaField()
+{
+ m_bCalcHiddenParaField = false;
+ bool bOldHasHiddenParaField = m_bHasHiddenParaField;
+ bool bNewHasHiddenParaField = false;
+ const sal_uInt16 nSize = Count();
+ const SwTxtAttr *pTxtHt;
+
+ for( sal_uInt16 nPos = 0; nPos < nSize; ++nPos )
+ {
+ pTxtHt = (*this)[ nPos ];
+ const sal_uInt16 nWhich = pTxtHt->Which();
+
+ if( RES_TXTATR_FIELD == nWhich )
+ {
+ const SwFmtFld& rFld = pTxtHt->GetFld();
+ if( RES_HIDDENPARAFLD == rFld.GetFld()->GetTyp()->Which() )
+ {
+ if( !((SwHiddenParaField*)rFld.GetFld())->IsHidden() )
+ {
+ SetHiddenParaField(false);
+ return bOldHasHiddenParaField != bNewHasHiddenParaField;
+ }
+ else
+ {
+ bNewHasHiddenParaField = true;
+ }
+ }
+ }
+ }
+ SetHiddenParaField( bNewHasHiddenParaField );
+ return bOldHasHiddenParaField != bNewHasHiddenParaField;
+}
+
+
+/*************************************************************************
+ * SwpHints::NoteInHistory()
+ *************************************************************************/
+
+void SwpHints::NoteInHistory( SwTxtAttr *pAttr, const bool bNew )
+{
+ if ( m_pHistory ) { m_pHistory->AddHint( pAttr, bNew ); }
+}
+
+/*************************************************************************
+ * SwpHints::MergePortions( )
+ *************************************************************************/
+
+bool SwpHints::MergePortions( SwTxtNode& rNode )
+{
+ if ( !Count() )
+ return false;
+
+ // sort before merging
+ SwpHintsArray::Resort();
+
+ bool bRet = false;
+ typedef std::multimap< int, SwTxtAttr* > PortionMap;
+ PortionMap aPortionMap;
+ xub_StrLen nLastPorStart = STRING_LEN;
+ sal_uInt16 i = 0;
+ int nKey = 0;
+
+ // get portions by start position:
+ for ( i = 0; i < Count(); ++i )
+ {
+ SwTxtAttr *pHt = GetTextHint( i );
+ if ( RES_TXTATR_CHARFMT != pHt->Which() &&
+ RES_TXTATR_AUTOFMT != pHt->Which() )
+ //&&
+ //RES_TXTATR_INETFMT != pHt->Which() )
+ continue;
+
+ const xub_StrLen nPorStart = *pHt->GetStart();
+ if ( nPorStart != nLastPorStart && nLastPorStart != STRING_LEN )
+ ++nKey;
+ nLastPorStart = nPorStart;
+ aPortionMap.insert( std::pair< const int, SwTxtAttr* >( nKey, pHt ) );
+ }
+
+ // check if portion i can be merged with portion i+1:
+ i = 0;
+ int j = i + 1;
+ while ( i <= nKey )
+ {
+ std::pair< PortionMap::iterator, PortionMap::iterator > aRange1 = aPortionMap.equal_range( i );
+ std::pair< PortionMap::iterator, PortionMap::iterator > aRange2 = aPortionMap.equal_range( j );
+ 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 )
+ {
+ while ( aIter1 != aRange1.second )
+ {
+ const SwTxtAttr* p1 = (*aIter1).second;
+ const SwTxtAttr* p2 = (*aIter2).second;
+ if ( *p1->GetEnd() < *p2->GetStart() || p1->Which() != p2->Which() || !(*p1 == *p2) )
+ {
+ bMerge = false;
+ break;
+ }
+ ++aIter1;
+ ++aIter2;
+ }
+ }
+ else
+ {
+ bMerge = false;
+ }
+
+ if ( bMerge )
+ {
+ // erase all elements with key i + 1
+ xub_StrLen nNewPortionEnd = 0;
+ for ( aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 )
+ {
+ SwTxtAttr* p2 = (*aIter2).second;
+ nNewPortionEnd = *p2->GetEnd();
+
+ const sal_uInt16 nCountBeforeDelete = Count();
+ Delete( p2 );
+
+ // 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 = aPortionMap.equal_range( i );
+ for ( aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 )
+ {
+ SwTxtAttr* p1 = (*aIter1).second;
+ NoteInHistory( p1 );
+ *p1->GetEnd() = nNewPortionEnd;
+ NoteInHistory( p1, true );
+ bRet = true;
+ }
+ }
+ else
+ {
+ ++i;
+ j = i + 1;
+ }
+ }
+
+ if ( bRet )
+ {
+ SwpHintsArray::Resort();
+ }
+
+ return bRet;
+}
+
+// check if there is already a character format and adjust the sort numbers
+void lcl_CheckSortNumber( const SwpHints& rHints, SwTxtCharFmt& rNewCharFmt )
+{
+ const xub_StrLen nHtStart = *rNewCharFmt.GetStart();
+ const xub_StrLen nHtEnd = *rNewCharFmt.GetEnd();
+ sal_uInt16 nSortNumber = 0;
+
+ for ( sal_uInt16 i = 0; i < rHints.Count(); ++i )
+ {
+ const SwTxtAttr* pOtherHt = rHints[i];
+
+ const xub_StrLen nOtherStart = *pOtherHt->GetStart();
+
+ if ( nOtherStart > nHtStart )
+ break;
+
+ if ( RES_TXTATR_CHARFMT == pOtherHt->Which() )
+ {
+ const xub_StrLen nOtherEnd = *pOtherHt->GetEnd();
+
+ if ( nOtherStart == nHtStart && nOtherEnd == nHtEnd )
+ {
+ const sal_uInt16 nOtherSortNum = static_cast<const SwTxtCharFmt*>(pOtherHt)->GetSortNumber();
+ nSortNumber = nOtherSortNum + 1;
+ }
+ }
+ }
+
+ if ( nSortNumber > 0 )
+ rNewCharFmt.SetSortNumber( nSortNumber );
+}
+
+/*************************************************************************
+ * SwpHints::Insert()
+ *************************************************************************/
+
+/*
+ * 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( SwTxtAttr* const pHint, SwTxtNode &rNode,
+ const SetAttrMode nMode )
+{
+ if ( USHRT_MAX == Count() ) // we're sorry, this flight is overbooked...
+ {
+ OSL_FAIL("hints array full :-(");
+ return false;
+ }
+
+ // Felder bilden eine Ausnahme:
+ // 1) Sie koennen nie ueberlappen
+ // 2) Wenn zwei Felder genau aneinander liegen,
+ // sollen sie nicht zu einem verschmolzen werden.
+ // Wir koennen also auf die while-Schleife verzichten
+
+ xub_StrLen *pHtEnd = pHint->GetEnd();
+ sal_uInt16 nWhich = pHint->Which();
+
+ switch( nWhich )
+ {
+ case RES_TXTATR_CHARFMT:
+ {
+ // Check if character format contains hidden attribute:
+ const SwCharFmt* pFmt = pHint->GetCharFmt().GetCharFmt();
+ const SfxPoolItem* pItem;
+ if ( SFX_ITEM_SET == pFmt->GetItemState( RES_CHRATR_HIDDEN, sal_True, &pItem ) )
+ rNode.SetCalcHiddenCharFlags();
+
+ ((SwTxtCharFmt*)pHint)->ChgTxtNode( &rNode );
+ break;
+ }
+ // --> FME 2007-03-16 #i75430# Recalc hidden flags if necessary
+ case RES_TXTATR_AUTOFMT:
+ {
+ // Check if auto style contains hidden attribute:
+ const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pHint, RES_CHRATR_HIDDEN );
+ if ( pHiddenItem )
+ rNode.SetCalcHiddenCharFlags();
+ break;
+ }
+ // <--
+ case RES_TXTATR_INETFMT:
+ static_cast<SwTxtINetFmt*>(pHint)->InitINetFmt(rNode);
+ break;
+ case RES_TXTATR_FIELD:
+ {
+ sal_Bool bDelFirst = 0 != ((SwTxtFld*)pHint)->GetpTxtNode();
+ ((SwTxtFld*)pHint)->ChgTxtNode( &rNode );
+ SwDoc* pDoc = rNode.GetDoc();
+ const SwField* pFld = ((SwTxtFld*)pHint)->GetFld().GetFld();
+
+ if( !pDoc->IsNewFldLst() )
+ {
+ // was fuer ein Feld ist es denn ??
+ // bestimmte Felder mussen am Doc das Calculations-Flag updaten
+ switch( pFld->GetTyp()->Which() )
+ {
+ case RES_DBFLD:
+ case RES_SETEXPFLD:
+ case RES_HIDDENPARAFLD:
+ case RES_HIDDENTXTFLD:
+ case RES_DBNUMSETFLD:
+ case RES_DBNEXTSETFLD:
+ {
+ if( bDelFirst )
+ pDoc->InsDelFldInFldLst( sal_False, *(SwTxtFld*)pHint );
+ if( rNode.GetNodes().IsDocNodes() )
+ pDoc->InsDelFldInFldLst( sal_True, *(SwTxtFld*)pHint );
+ }
+ break;
+ case RES_DDEFLD:
+ if( rNode.GetNodes().IsDocNodes() )
+ ((SwDDEFieldType*)pFld->GetTyp())->IncRefCnt();
+ break;
+ }
+ }
+
+ // gehts ins normale Nodes-Array?
+ if( rNode.GetNodes().IsDocNodes() )
+ {
+ sal_Bool bInsFldType = sal_False;
+ switch( pFld->GetTyp()->Which() )
+ {
+ case RES_SETEXPFLD:
+ bInsFldType = ((SwSetExpFieldType*)pFld->GetTyp())->IsDeleted();
+ if( nsSwGetSetExpType::GSE_SEQ & ((SwSetExpFieldType*)pFld->GetTyp())->GetType() )
+ {
+ // bevor die ReferenzNummer gesetzt wird, sollte
+ // das Feld am richtigen FeldTypen haengen!
+ SwSetExpFieldType* pFldType = (SwSetExpFieldType*)
+ pDoc->InsertFldType( *pFld->GetTyp() );
+ if( pFldType != pFld->GetTyp() )
+ {
+ SwFmtFld* pFmtFld = (SwFmtFld*)&((SwTxtFld*)pHint)
+ ->GetFld();
+ pFmtFld->RegisterToFieldType( *pFldType );
+ pFmtFld->GetFld()->ChgTyp( pFldType );
+ }
+ pFldType->SetSeqRefNo( *(SwSetExpField*)pFld );
+ }
+ break;
+ case RES_USERFLD:
+ bInsFldType = ((SwUserFieldType*)pFld->GetTyp())->IsDeleted();
+ break;
+
+ case RES_DDEFLD:
+ if( pDoc->IsNewFldLst() )
+ ((SwDDEFieldType*)pFld->GetTyp())->IncRefCnt();
+ bInsFldType = ((SwDDEFieldType*)pFld->GetTyp())->IsDeleted();
+ break;
+
+ case RES_POSTITFLD:
+ if ( pDoc->GetDocShell() )
+ pDoc->GetDocShell()->Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFld(), SWFMTFLD_INSERTED ) );
+ break;
+ }
+ if( bInsFldType )
+ pDoc->InsDeletedFldType( *pFld->GetTyp() );
+ }
+ }
+ break;
+ case RES_TXTATR_FTN :
+ ((SwTxtFtn*)pHint)->ChgTxtNode( &rNode );
+ break;
+ case RES_TXTATR_REFMARK:
+ ((SwTxtRefMark*)pHint)->ChgTxtNode( &rNode );
+ if( rNode.GetNodes().IsDocNodes() )
+ {
+ // search for a reference with the same name
+ SwTxtAttr* pTmpHt;
+ xub_StrLen *pTmpHtEnd, *pTmpHintEnd;
+ for( sal_uInt16 n = 0, nEnd = Count(); n < nEnd; ++n )
+ {
+ if (RES_TXTATR_REFMARK == (pTmpHt = GetTextHint(n))->Which() &&
+ pHint->GetAttr() == pTmpHt->GetAttr() &&
+ 0 != ( pTmpHtEnd = pTmpHt->GetEnd() ) &&
+ 0 != ( pTmpHintEnd = pHint->GetEnd() ) )
+ {
+ SwComparePosition eCmp = ::ComparePosition(
+ *pTmpHt->GetStart(), *pTmpHtEnd,
+ *pHint->GetStart(), *pTmpHintEnd );
+ sal_Bool bDelOld = sal_True, bChgStart = sal_False, bChgEnd = sal_False;
+ switch( eCmp )
+ {
+ case POS_BEFORE:
+ case POS_BEHIND: bDelOld = sal_False; break;
+
+ case POS_OUTSIDE: bChgStart = bChgEnd = sal_True; break;
+
+ case POS_COLLIDE_END:
+ case POS_OVERLAP_BEFORE: bChgStart = sal_True; break;
+ case POS_COLLIDE_START:
+ case POS_OVERLAP_BEHIND: bChgEnd = sal_True; break;
+ default: break;
+ }
+
+ if( bChgStart )
+ *pHint->GetStart() = *pTmpHt->GetStart();
+ if( bChgEnd )
+ *pTmpHintEnd = *pTmpHtEnd;
+
+ if( bDelOld )
+ {
+ NoteInHistory( pTmpHt );
+ rNode.DestroyAttr( Cut( n-- ) );
+ --nEnd;
+ }
+ }
+ }
+ }
+ break;
+ case RES_TXTATR_TOXMARK:
+ ((SwTxtTOXMark*)pHint)->ChgTxtNode( &rNode );
+ break;
+
+ case RES_TXTATR_CJK_RUBY:
+ static_cast<SwTxtRuby*>(pHint)->InitRuby(rNode);
+ break;
+
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ static_cast<SwTxtMeta *>(pHint)->ChgTxtNode( &rNode );
+ break;
+
+ case RES_CHRATR_HIDDEN:
+ rNode.SetCalcHiddenCharFlags();
+ break;
+ }
+
+ if( nsSetAttrMode::SETATTR_DONTEXPAND & nMode )
+ pHint->SetDontExpand( sal_True );
+
+ // SwTxtAttrs ohne Ende werden sonderbehandelt:
+ // Sie werden natuerlich in das Array insertet, aber sie werden nicht
+ // in die pPrev/Next/On/Off-Verkettung aufgenommen.
+ // Der Formatierer erkennt diese TxtHints an dem CH_TXTATR_.. im Text !
+ xub_StrLen nHtStart = *pHint->GetStart();
+ if( !pHtEnd )
+ {
+ SwpHintsArray::Insert( pHint );
+ CalcFlags();
+#if OSL_DEBUG_LEVEL > 1
+ if( !rNode.GetDoc()->IsInReading() )
+ CHECK;
+#endif
+ // ... und die Abhaengigen benachrichtigen
+ if ( rNode.GetDepends() )
+ {
+ SwUpdateAttr aHint( nHtStart, nHtStart, nWhich );
+ rNode.ModifyNotification( 0, &aHint );
+ }
+ return true;
+ }
+
+ // ----------------------------------------------------------------
+ // Ab hier gibt es nur noch pHint mit einem EndIdx !!!
+
+ if( *pHtEnd < nHtStart )
+ {
+ OSL_ENSURE( *pHtEnd >= nHtStart,
+ "+SwpHints::Insert: invalid hint, end < start" );
+
+ // Wir drehen den Quatsch einfach um:
+ *pHint->GetStart() = *pHtEnd;
+ *pHtEnd = nHtStart;
+ nHtStart = *pHint->GetStart();
+ }
+
+ // I need this value later on for notification but the pointer may become invalid
+ const xub_StrLen nHintEnd = *pHtEnd;
+ const bool bNoHintAdjustMode = (nsSetAttrMode::SETATTR_NOHINTADJUST & nMode);
+
+ // handle nesting attributes: inserting may fail due to overlap!
+ if (pHint->IsNesting())
+ {
+ const bool bRet(
+ TryInsertNesting(rNode, *static_cast<SwTxtAttrNesting*>(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 ) )
+ {
+ OSL_ENSURE( nWhich != RES_TXTATR_AUTOFMT ||
+ static_cast<const SwFmtAutoFmt&>(pHint->GetAttr()).GetStyleHandle()->GetPool() ==
+ &rNode.GetDoc()->GetAttrPool(),
+ "AUTOSTYLES - Pool mismatch" );
+
+ 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.
+ // Special case ruby portion: During import, the ruby attribute is set
+ // multiple times
+ // Special case hyperlink: During import, the ruby attribute is set
+ // multiple times
+ // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert
+ // character attributes directly
+ if ( ( RES_TXTATR_CHARFMT == nWhich && !bNoHintAdjustMode ) )
+ {
+ BuildPortions( rNode, *pHint, nMode );
+ }
+ else
+ {
+ // --> FME 2007-11-08 #i82989# Check sort numbers in NoHintAdjustMode
+ if ( RES_TXTATR_CHARFMT == nWhich )
+ lcl_CheckSortNumber( *this, *static_cast<SwTxtCharFmt*>(pHint) );
+ // <--
+
+ SwpHintsArray::Insert( pHint );
+ NoteInHistory( pHint, true );
+ }
+ }
+
+ // ... und die Abhaengigen benachrichtigen
+ if ( rNode.GetDepends() )
+ {
+ SwUpdateAttr aHint( nHtStart, nHtStart == nHintEnd ? nHintEnd + 1 : nHintEnd, nWhich );
+ rNode.ModifyNotification( 0, &aHint );
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ if( !bNoHintAdjustMode && !rNode.GetDoc()->IsInReading() )
+ CHECK;
+#endif
+
+ return true;
+}
+
+/*************************************************************************
+ * SwpHints::DeleteAtPos()
+ *************************************************************************/
+
+void SwpHints::DeleteAtPos( const sal_uInt16 nPos )
+{
+ SwTxtAttr *pHint = GetTextHint(nPos);
+ // ChainDelete( pHint );
+ NoteInHistory( pHint );
+ SwpHintsArray::DeleteAtPos( nPos );
+
+ if( RES_TXTATR_FIELD == pHint->Which() )
+ {
+ SwFieldType* pFldTyp = ((SwTxtFld*)pHint)->GetFld().GetFld()->GetTyp();
+ if( RES_DDEFLD == pFldTyp->Which() )
+ {
+ const SwTxtNode* pNd = ((SwTxtFld*)pHint)->GetpTxtNode();
+ if( pNd && pNd->GetNodes().IsDocNodes() )
+ ((SwDDEFieldType*)pFldTyp)->DecRefCnt();
+ ((SwTxtFld*)pHint)->ChgTxtNode( 0 );
+ }
+ else if( RES_POSTITFLD == pFldTyp->Which() )
+ {
+ const_cast<SwFmtFld&>(((SwTxtFld*)pHint)->GetFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFld(), SWFMTFLD_REMOVED ) );
+ }
+ else if ( m_bHasHiddenParaField &&
+ RES_HIDDENPARAFLD == pFldTyp->Which() )
+ {
+ m_bCalcHiddenParaField = true;
+ }
+ }
+
+ CalcFlags();
+ CHECK;
+}
+
+// Ist der Hint schon bekannt, dann suche die Position und loesche ihn.
+// Ist er nicht im Array, so gibt es ein OSL_ENSURE(!!
+
+void SwpHints::Delete( SwTxtAttr* pTxtHt )
+{
+ // Attr 2.0: SwpHintsArr::Delete( pTxtHt );
+ const sal_uInt16 nPos = GetStartOf( pTxtHt );
+ OSL_ENSURE( USHRT_MAX != nPos, "Attribut nicht im Attribut-Array!" );
+ if( USHRT_MAX != nPos )
+ DeleteAtPos( nPos );
+}
+
+void SwTxtNode::ClearSwpHintsArr( bool bDelFields )
+{
+ if ( HasHints() )
+ {
+ sal_uInt16 nPos = 0;
+ while ( nPos < m_pSwpHints->Count() )
+ {
+ SwTxtAttr* pDel = m_pSwpHints->GetTextHint( nPos );
+ bool bDel = false;
+
+ switch( pDel->Which() )
+ {
+ case RES_TXTATR_FLYCNT:
+ case RES_TXTATR_FTN:
+ break;
+
+ case RES_TXTATR_FIELD:
+ if( bDelFields )
+ bDel = true;
+ break;
+ default:
+ bDel = true; break;
+ }
+
+ if( bDel )
+ {
+ m_pSwpHints->SwpHintsArray::DeleteAtPos( nPos );
+ DestroyAttr( pDel );
+ }
+ else
+ ++nPos;
+ }
+ }
+}
+
+sal_uInt16 SwTxtNode::GetLang( const xub_StrLen nBegin, const xub_StrLen nLen,
+ sal_uInt16 nScript ) const
+{
+ sal_uInt16 nRet = LANGUAGE_DONTKNOW;
+
+ if ( ! nScript )
+ {
+ nScript = pBreakIt->GetRealScriptOfText( m_Text, nBegin );
+ }
+
+ // --> FME 2008-09-29 #i91465# hennerdrewes: Consider nScript if pSwpHints == 0
+ const sal_uInt16 nWhichId = GetWhichOfScript( RES_CHRATR_LANGUAGE, nScript );
+ // <--
+
+ if ( HasHints() )
+ {
+ const xub_StrLen nEnd = nBegin + nLen;
+ for ( sal_uInt16 i = 0, nSize = m_pSwpHints->Count(); i < nSize; ++i )
+ {
+ // ist der Attribut-Anfang schon groesser als der Idx ?
+ const SwTxtAttr *pHt = m_pSwpHints->operator[](i);
+ const xub_StrLen nAttrStart = *pHt->GetStart();
+ if( nEnd < nAttrStart )
+ break;
+
+ const sal_uInt16 nWhich = pHt->Which();
+
+ if( nWhichId == nWhich ||
+ ( ( pHt->IsCharFmtAttr() || RES_TXTATR_AUTOFMT == nWhich ) && CharFmt::IsItemIncluded( nWhichId, pHt ) ) )
+ {
+ const xub_StrLen *pEndIdx = pHt->GetEnd();
+ // Ueberlappt das Attribut den Bereich?
+
+ if( pEndIdx &&
+ nLen ? ( nAttrStart < nEnd && nBegin < *pEndIdx )
+ : (( nAttrStart < nBegin &&
+ ( pHt->DontExpand() ? nBegin < *pEndIdx
+ : nBegin <= *pEndIdx )) ||
+ ( nBegin == nAttrStart &&
+ ( nAttrStart == *pEndIdx || !nBegin ))) )
+ {
+ const SfxPoolItem* pItem = CharFmt::GetItem( *pHt, nWhichId );
+ sal_uInt16 nLng = ((SvxLanguageItem*)pItem)->GetLanguage();
+
+ // Umfasst das Attribut den Bereich komplett?
+ if( nAttrStart <= nBegin && nEnd <= *pEndIdx )
+ nRet = nLng;
+ else if( LANGUAGE_DONTKNOW == nRet )
+ nRet = nLng; // partielle Ueberlappung, der 1. gewinnt
+ }
+ }
+ }
+ }
+ if( LANGUAGE_DONTKNOW == nRet )
+ {
+ nRet = ((SvxLanguageItem&)GetSwAttrSet().Get( nWhichId )).GetLanguage();
+ if( LANGUAGE_DONTKNOW == nRet )
+ nRet = static_cast<sal_uInt16>(GetAppLanguage());
+ }
+ return nRet;
+}
+
+
+sal_Unicode GetCharOfTxtAttr( const SwTxtAttr& rAttr )
+{
+ sal_Unicode cRet = CH_TXTATR_BREAKWORD;
+ switch ( rAttr.Which() )
+ {
+ case RES_TXTATR_FTN:
+ case RES_TXTATR_REFMARK:
+ case RES_TXTATR_TOXMARK:
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ cRet = CH_TXTATR_INWORD;
+ break;
+
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_FLYCNT:
+ {
+ cRet = CH_TXTATR_BREAKWORD;
+
+ // #i78149: PostIt fields should not break words for spell and grammar checking
+ if (rAttr.Which() == RES_TXTATR_FIELD &&
+ RES_POSTITFLD == rAttr.GetFld().GetFld()->GetTyp()->Which())
+ cRet = CH_TXTATR_INWORD;
+ }
+ break;
+
+ default:
+ OSL_FAIL("GetCharOfTxtAttr: unknown attr");
+ break;
+ }
+ return cRet;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */