| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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> |
| // --> OD 2008-01-16 #newlistlevelattrs# |
| #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 <txtannotationfld.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 <errhdl.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> |
| |
| #ifdef DBG_UTIL |
| #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->End()); |
| } |
| }; |
| |
| // 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) || |
| (RES_TXTATR_INPUTFIELD == nWhich)) |
| return false; |
| ASSERT((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; |
| ASSERT((RES_TXTATR_META == nWhich) || |
| (RES_TXTATR_METAFIELD == nWhich) || |
| (RES_TXTATR_INPUTFIELD == 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_INPUTFIELD == nWhichNew ) |
| return FAIL; |
| 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: |
| ASSERT(false, "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 bNewSelfNestable( isSelfNestable(nNewWhich) ); |
| |
| ASSERT( (RES_TXTATR_INETFMT == nNewWhich) || |
| (RES_TXTATR_CJK_RUBY == nNewWhich) || |
| (RES_TXTATR_META == nNewWhich) || |
| (RES_TXTATR_METAFIELD == nNewWhich) || |
| (RES_TXTATR_INPUTFIELD == nNewWhich), |
| "TryInsertNesting: Expecting INETFMT or RUBY or META or METAFIELD or INPUTFIELD" ); |
| |
| 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: |
| ASSERT(false, "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()) |
| { |
| ASSERT(false, |
| "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()); |
| ASSERT(*pStart == nNewStart, "how did that happen?"); |
| *pStart = nNewStart + 1; |
| } |
| } |
| } |
| } |
| } |
| |
| ASSERT (isSplittable(nNewWhich) || SplitNew.size() == 1, |
| "splitting the unsplittable ???"); |
| |
| // pass 2: split existing hints that overlap/nest with new hint |
| // do not iterate over hints array, but over remembered set of overlapping |
| // hints, to keep things simple w.r.t. insertion/removal |
| // N.B: if there is a hint that splits the inserted hint, then |
| // that hint would also have already split any hint in OverlappingExisting |
| // so any hint in OverlappingExisting can be split at most by one hint |
| // in SplitNew, or even not at all (this is not true for existing hints |
| // that go _around_ new hint, which is the 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: |
| { |
| ASSERT(!bRemoveOverlap, |
| "this one should be in OverwrittenExisting?"); |
| } |
| break; |
| case POS_OUTSIDE: |
| case POS_EQUAL: |
| { |
| ASSERT(false, "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() ) |
| { |
| ASSERT(false, "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() ) |
| { |
| ASSERT(false, "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() ) |
| { |
| ASSERT(false, "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 |
| { |
| ASSERT((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) ); |
| ASSERT(bSuccess, "recursive call 1 failed?"); |
| SwTxtAttrNesting * const pOtherRight( |
| MakeTxtAttrNesting( |
| rNode, **itOther, nNewEnd, nOtherEnd ) ); |
| bSuccess = TryInsertNesting(rNode, *pOtherRight); |
| ASSERT(bSuccess, "recursive call 2 failed?"); |
| } |
| |
| } |
| |
| 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; |
| |
| ASSERT( 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 ); |
| } |
| } |
| |
| #ifdef DBG_UTIL |
| 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->End(); |
| |
| 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 ) |
| { |
| ASSERT( 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 ) |
| { |
| ASSERT( *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: |
| ASSERT( 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: |
| ASSERT(false, "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_ANNOTATION: |
| { |
| pNew = new SwTxtAnnotationFld( static_cast<SwFmtFld &>(rNew), nStt ); |
| if ( bIsCopy == COPY ) |
| { |
| // On copy of the annotation field do not keep the annotated text range by removing |
| // the relation to its annotation mark (relation established via annotation field's name). |
| // If the annotation mark is also copied, the relation and thus the annotated text range will be reestablished, |
| // when the annotation mark is created and inserted into the document. |
| const_cast<SwPostItField*>(dynamic_cast< const SwPostItField* >(pNew->GetFmtFld().GetField()))->SetName( String() ); |
| } |
| } |
| break; |
| |
| case RES_TXTATR_INPUTFIELD: |
| pNew = new SwTxtInputFld( static_cast<SwFmtFld &>(rNew), nStt, nEnd ); |
| 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: |
| ASSERT(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: |
| case RES_TXTATR_ANNOTATION: |
| case RES_TXTATR_INPUTFIELD: |
| if( !pDoc->IsInDtor() ) |
| { |
| // Wenn wir ein HiddenParaField sind, dann muessen wir |
| // ggf. fuer eine Neuberechnung des Visible-Flags sorgen. |
| const SwField* pFld = pAttr->GetFmtFld().GetField(); |
| |
| //JP 06-08-95: DDE-Felder bilden eine Ausnahme |
| ASSERT( 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->GetFmtFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pAttr)->GetFmtFld(), 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: |
| ASSERT( !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 ) |
| { |
| bool bHiddenPara = false; |
| |
| ASSERT( pAttr && *pAttr->GetStart() <= Len(), "StartIdx out of bounds!" ); |
| ASSERT( !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 ! |
| ASSERT( ( 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 ! |
| ASSERT( ( 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() ) |
| { |
| #ifdef DBG_UTIL |
| const sal_Bool bSuccess = |
| #endif |
| pDoc->GetFtnIdxs().Insert( pTxtFtn ); |
| #ifdef DBG_UTIL |
| ASSERT( 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->GetFmtFld().GetField()->GetTyp()->Which() ) |
| { |
| bHiddenPara = 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; |
| } |
| } |
| } |
| |
| // handle attributes which provide content |
| xub_StrLen nEnd = nStart; |
| bool bInputFieldStartCharInserted = false; |
| bool bInputFieldEndCharInserted = false; |
| const bool bHasContent( pAttr->HasContent() ); |
| if ( bHasContent ) |
| { |
| switch( pAttr->Which() ) |
| { |
| case RES_TXTATR_INPUTFIELD: |
| { |
| SwTxtInputFld* pTxtInputFld = dynamic_cast<SwTxtInputFld*>(pAttr); |
| if ( pTxtInputFld ) |
| { |
| if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nMode) ) |
| { |
| SwIndex aIdx( this, *pAttr->GetStart() ); |
| InsertText( CH_TXT_ATR_INPUTFIELDSTART, aIdx, nInsertFlags ); |
| const String aContent = pTxtInputFld->GetFieldContent(); |
| InsertText( aContent, aIdx, nInsertFlags ); |
| InsertText( CH_TXT_ATR_INPUTFIELDEND, aIdx, nInsertFlags ); |
| |
| xub_StrLen * const pEnd(pAttr->GetEnd()); |
| ASSERT( pEnd != NULL, "<SwTxtNode::InsertHint(..)> - missing end of RES_TXTATR_INPUTFIELD!" ); |
| if ( pEnd != NULL ) |
| { |
| *pEnd = *pEnd + 2 + aContent.Len(); |
| nEnd = *pEnd; |
| } |
| } |
| else |
| { |
| // assure that CH_TXT_ATR_INPUTFIELDSTART and CH_TXT_ATR_INPUTFIELDEND are inserted. |
| if ( m_Text.GetChar( *(pAttr->GetStart()) ) != CH_TXT_ATR_INPUTFIELDSTART ) |
| { |
| SwIndex aIdx( this, *pAttr->GetStart() ); |
| InsertText( CH_TXT_ATR_INPUTFIELDSTART, aIdx, nInsertFlags ); |
| bInputFieldStartCharInserted = true; |
| xub_StrLen * const pEnd(pAttr->GetEnd()); |
| ASSERT( pEnd != NULL, "<SwTxtNode::InsertHint(..)> - missing end of RES_TXTATR_INPUTFIELD!" ); |
| if ( pEnd != NULL ) |
| { |
| *pEnd = *pEnd + 1; |
| nEnd = *pEnd; |
| } |
| } |
| |
| xub_StrLen * const pEnd(pAttr->GetEnd()); |
| ASSERT( pEnd != NULL, "<SwTxtNode::InsertHint(..)> - missing end of RES_TXTATR_INPUTFIELD!" ); |
| if ( pEnd != NULL |
| && m_Text.GetChar( *(pEnd) - 1 ) != CH_TXT_ATR_INPUTFIELDEND ) |
| { |
| SwIndex aIdx( this, *(pEnd) ); |
| InsertText( CH_TXT_ATR_INPUTFIELDEND, aIdx, nInsertFlags ); |
| bInputFieldEndCharInserted = true; |
| *pEnd = *pEnd + 1; |
| nEnd = *pEnd; |
| } |
| } |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| GetOrCreateSwpHints(); |
| |
| // handle overlap with an existing InputField |
| bool bInsertHint = true; |
| { |
| const SwTxtInputFld* pTxtInputFld = GetOverlappingInputFld( *pAttr ); |
| if ( pTxtInputFld != NULL ) |
| { |
| if ( pAttr->End() == NULL ) |
| { |
| bInsertHint = false; |
| } |
| else |
| { |
| if ( *(pAttr->GetStart()) > *(pTxtInputFld->GetStart()) ) |
| { |
| *(pAttr->GetStart()) = *(pTxtInputFld->GetStart()); |
| } |
| if ( *(pAttr->End()) < *(pTxtInputFld->End()) ) |
| { |
| *(pAttr->GetEnd()) = *(pTxtInputFld->End()); |
| } |
| } |
| } |
| } |
| |
| // 4263: AttrInsert durch TextInsert => kein Adjust |
| const bool bRet = bInsertHint |
| ? m_pSwpHints->TryInsertHint( pAttr, *this, nMode ) |
| : false; |
| |
| if ( !bRet ) |
| { |
| if ( bDummyChar |
| && !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nMode) ) |
| { |
| // undo insertion of dummy character |
| // N.B. cannot insert the dummy character after inserting the hint, |
| // because if the hint has no extent it will be moved in InsertText, |
| // resulting in infinite recursion |
| ASSERT( ( CH_TXTATR_BREAKWORD == m_Text.GetChar(nStart) || |
| CH_TXTATR_INWORD == m_Text.GetChar(nStart) ), |
| "where is my attribute character?" ); |
| SwIndex aIdx( this, nStart ); |
| EraseText( aIdx, 1 ); |
| } |
| |
| if ( bHasContent ) |
| { |
| if ( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nMode) |
| && (nEnd - nStart) > 0 ) |
| { |
| SwIndex aIdx( this, nStart ); |
| EraseText( aIdx, (nEnd - nStart) ); |
| } |
| else |
| { |
| if ( bInputFieldEndCharInserted |
| && (nEnd - nStart) > 0 ) |
| { |
| SwIndex aIdx( this, nEnd - 1 ); |
| EraseText( aIdx, 1 ); |
| } |
| |
| if ( bInputFieldStartCharInserted ) |
| { |
| SwIndex aIdx( this, nStart ); |
| EraseText( aIdx, 1 ); |
| } |
| } |
| } |
| } |
| |
| if ( bHiddenPara ) |
| { |
| SetCalcHiddenParaField(); |
| } |
| |
| return bRet; |
| } |
| |
| |
| /************************************************************************* |
| * SwTxtNode::DeleteAttribute() |
| *************************************************************************/ |
| |
| void SwTxtNode::DeleteAttribute( SwTxtAttr * const pAttr ) |
| { |
| if ( !HasHints() ) |
| { |
| ASSERT(false, "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 if ( pAttr->HasContent() ) |
| { |
| const SwIndex aIdx( this, *pAttr->GetStart() ); |
| ASSERT( pAttr->End() != NULL, "<SwTxtNode::DeleteAttribute(..)> - missing End() at <SwTxtAttr> instance which has content" ); |
| EraseText( aIdx, *pAttr->End() - *pAttr->GetStart() ); |
| } |
| 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 ) |
| { |
| ASSERT(false, "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(); |
| } |
| // 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 ( pTxtHt->HasContent() ) |
| { |
| const SwIndex aIdx( this, nStart ); |
| ASSERT( pTxtHt->End() != NULL, "<SwTxtNode::DeleteAttributes(..)> - missing End() at <SwTxtAttr> instance which has content" ); |
| EraseText( aIdx, *pTxtHt->End() - nStart ); |
| } |
| 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; |
| } |
| } |
| |
| //Modify here for #119405, by easyfan, 2012-05-24 |
| //In MS Word, the font underline setting of the paragraph end position wont affect the formatting of numbering, so we ignore it |
| bool lcl_IsIgnoredCharFmtForNumbering(const sal_uInt16 nWhich) |
| { |
| return (nWhich == RES_CHRATR_UNDERLINE); |
| } |
| |
| //In MS Word, following properties of the paragraph end position wont affect the formatting of bullets, so we ignore them: |
| //Font underline; |
| //Font Italic of Western, CJK and CTL; |
| //Font Bold of Wertern, CJK and CTL; |
| bool lcl_IsIgnoredCharFmtForBullets(const sal_uInt16 nWhich) |
| { |
| return (nWhich == RES_CHRATR_UNDERLINE || nWhich == RES_CHRATR_POSTURE || nWhich == RES_CHRATR_WEIGHT |
| || nWhich == RES_CHRATR_CJK_POSTURE || nWhich == RES_CHRATR_CJK_WEIGHT |
| || nWhich == RES_CHRATR_CTL_POSTURE || nWhich == RES_CHRATR_CTL_WEIGHT); |
| } |
| |
| //Condition for expanding char set to character style of specified number rule level: |
| //The item inside the set should not conflict to any exist and non-default item inside paragraph properties set (SwCntntNode::SwPAttrSet); |
| //The node should have applied a number rule; |
| //The node should be counted in a list, if not, make it to be; |
| //The item should not conflict to any exist and non-default item inside the character of specified number rule level; |
| //The item should not be ignored depend on the exact number rule type; |
| bool SwTxtNode::TryCharSetExpandToNum(const SfxItemSet& aCharSet) |
| { |
| bool bRet = false; |
| SfxItemIter aIter( aCharSet ); |
| const SfxPoolItem* pItem = aIter.FirstItem(); |
| const sal_uInt16 nWhich = pItem->Which(); |
| |
| const SfxPoolItem& rInnerItem = GetAttr(nWhich,false); |
| |
| if (!IsDefaultItem(&rInnerItem) && !IsInvalidItem(&rInnerItem)) |
| return bRet; |
| |
| if ( !IsInList() && GetNumRule() && GetListId().Len() > 0 ) |
| { |
| return bRet; |
| } |
| |
| SwNumRule* pCurrNum = GetNumRule(false); |
| |
| int nLevel = GetActualListLevel(); |
| |
| if (nLevel != -1 && pCurrNum) |
| { |
| const SwNumFmt* pCurrNumFmt = pCurrNum->GetNumFmt(static_cast<sal_uInt16>(nLevel)); |
| if (pCurrNumFmt) |
| { |
| if (pCurrNumFmt->IsItemize() && lcl_IsIgnoredCharFmtForBullets(nWhich)) |
| return bRet; |
| if (pCurrNumFmt->IsEnumeration() && lcl_IsIgnoredCharFmtForNumbering(nWhich)) |
| return bRet; |
| SwCharFmt* pCurrCharFmt =pCurrNumFmt->GetCharFmt(); |
| |
| if (pCurrCharFmt && pCurrCharFmt->GetItemState(nWhich,false) != SFX_ITEM_SET) |
| { |
| pCurrCharFmt->SetFmtAttr(*pItem); |
| SwNumFmt aNewNumFmt(*pCurrNumFmt); |
| aNewNumFmt.SetCharFmt(pCurrCharFmt); |
| pCurrNum->Set(nLevel,aNewNumFmt); |
| bRet = true; |
| } |
| } |
| } |
| |
| |
| return bRet; |
| } |
| //End of modification, by easyfan |
| |
| // 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, |
| const xub_StrLen nStt, |
| const 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(); |
| ASSERT( 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 ); |
| RstTxtAttr( 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() ) |
| { |
| ASSERT(false, |
| "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 ) {}; |
| }; |
| |
| // --> OD 2008-01-16 #newlistlevelattrs# |
| 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 |
| // --> OD 2008-01-16 #newlistlevelattrs# |
| 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 ); |
| // --> OD 2008-01-16 #newlistlevelattrs# |
| 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->End(); |
| 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->End(); |
| 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(); |
| ASSERT(!isUNKNOWNATR(nHintWhich), |
| "SwTxtNode::GetAttr(): unkonwn 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 ); |
| // --> OD 2008-01-16 #newlistlevelattrs# |
| 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->End()); |
| o_rSpanMap.insert(AttrSpanMap_t::value_type(aSpan, pHint)); |
| |
| // < not != because there may be multiple CHARFMT at same range |
| if (nLastEnd < aSpan.first) |
| { |
| // 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 (true) |
| { |
| 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->GetFmtFld().GetField(); |
| 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->GetFmtFld(); |
| if( RES_HIDDENPARAFLD == rFld.GetField()->GetTyp()->Which() ) |
| { |
| if( !((SwHiddenParaField*)rFld.GetField())->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->End() < *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->End(); |
| |
| 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... |
| { |
| ASSERT(false, "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: |
| case RES_TXTATR_ANNOTATION: |
| case RES_TXTATR_INPUTFIELD: |
| { |
| sal_Bool bDelFirst = 0 != ((SwTxtFld*)pHint)->GetpTxtNode(); |
| ((SwTxtFld*)pHint)->ChgTxtNode( &rNode ); |
| SwDoc* pDoc = rNode.GetDoc(); |
| const SwField* pFld = ((SwTxtFld*)pHint)->GetFmtFld().GetField(); |
| |
| 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)->GetFmtFld(); |
| pFmtFld->RegisterToFieldType( *pFldType ); |
| pFmtFld->GetField()->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)->GetFmtFld(), 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(); |
| #ifdef DBG_UTIL |
| 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 ) |
| { |
| ASSERT( *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 ) ) |
| { |
| ASSERT( 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 ); |
| } |
| |
| #ifdef DBG_UTIL |
| 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( pHint->Which() == RES_TXTATR_FIELD ) |
| { |
| const SwFieldType* pFldTyp = ((SwTxtFld*)pHint)->GetFmtFld().GetField()->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 ( m_bHasHiddenParaField && |
| RES_HIDDENPARAFLD == pFldTyp->Which() ) |
| { |
| m_bCalcHiddenParaField = true; |
| } |
| } |
| else if ( pHint->Which() == RES_TXTATR_ANNOTATION ) |
| { |
| const_cast<SwFmtFld&>(((SwTxtFld*)pHint)->GetFmtFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFmtFld(), SWFMTFLD_REMOVED ) ); |
| } |
| |
| CalcFlags(); |
| CHECK; |
| } |
| |
| // Ist der Hint schon bekannt, dann suche die Position und loesche ihn. |
| // Ist er nicht im Array, so gibt es ein ASSERT !! |
| |
| void SwpHints::Delete( SwTxtAttr* pTxtHt ) |
| { |
| // Attr 2.0: SwpHintsArr::Delete( pTxtHt ); |
| const sal_uInt16 nPos = GetStartOf( pTxtHt ); |
| ASSERT( 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: |
| case RES_TXTATR_ANNOTATION: |
| case RES_TXTATR_INPUTFIELD: |
| 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->End(); |
| // 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: |
| case RES_TXTATR_ANNOTATION: |
| cRet = CH_TXTATR_INWORD; |
| break; |
| |
| case RES_TXTATR_FIELD: |
| case RES_TXTATR_FLYCNT: |
| { |
| cRet = CH_TXTATR_BREAKWORD; |
| } |
| break; |
| |
| default: |
| ASSERT(false, "GetCharOfTxtAttr: unknown attr"); |
| break; |
| } |
| return cRet; |
| } |
| |
| |