xref: /trunk/main/sw/source/core/txtnode/thints.cxx (revision 9f813b30)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 
28 #include <hintids.hxx>
29 #include <sot/factory.hxx>
30 #include <editeng/xmlcnitm.hxx>
31 #include <svl/whiter.hxx>
32 #include <svl/itemiter.hxx>
33 #include <svl/stylepool.hxx>
34 #include <editeng/fontitem.hxx>
35 #include <editeng/langitem.hxx>
36 #include <editeng/emphitem.hxx>
37 #include <editeng/charscaleitem.hxx>
38 #include <editeng/charrotateitem.hxx>
39 // --> OD 2008-01-16 #newlistlevelattrs#
40 #include <editeng/lrspitem.hxx>
41 // <--
42 #include <txtinet.hxx>
43 #include <txtflcnt.hxx>
44 #include <fmtfld.hxx>
45 #include <fmtanchr.hxx>
46 #include <fmtinfmt.hxx>
47 #include <txtatr.hxx>
48 #include <fchrfmt.hxx>
49 #include <fmtautofmt.hxx>
50 #include <fmtflcnt.hxx>
51 #include <fmtftn.hxx>
52 #include <txttxmrk.hxx>
53 #include <txtrfmrk.hxx>
54 #include <txtftn.hxx>
55 #include <txtfld.hxx>
56 #include <txtannotationfld.hxx>
57 #include <charatr.hxx>
58 #include <charfmt.hxx>
59 #include <frmfmt.hxx>
60 #include <ftnidx.hxx>
61 #include <fmtruby.hxx>
62 #include <fmtmeta.hxx>
63 #include <breakit.hxx>
64 #include <doc.hxx>
65 #include <IDocumentUndoRedo.hxx>
66 #include <errhdl.hxx>
67 #include <fldbas.hxx>
68 #include <pam.hxx>
69 #include <ndtxt.hxx>
70 #include <txtfrm.hxx>
71 #include <rolbck.hxx>			// fuer	SwRegHistory
72 #include <ddefld.hxx>
73 #include <docufld.hxx>
74 #include <expfld.hxx>
75 #include <usrfld.hxx>
76 #include <poolfmt.hxx>
77 #include <swfont.hxx>
78 #include <istyleaccess.hxx>
79 // OD 26.06.2003 #108784#
80 #include <dcontact.hxx>
81 #include <docsh.hxx>
82 #include <svl/smplhint.hxx>
83 #include <algorithm>
84 #include <map>
85 
86 #ifdef DBG_UTIL
87 #define CHECK    Check();
88 #else
89 #define CHECK
90 #endif
91 
92 using namespace ::com::sun::star::i18n;
93 
94 
SwpHints()95 SwpHints::SwpHints()
96     : m_pHistory(0)
97     , m_bFontChange(true)
98     , m_bInSplitNode(false)
99     , m_bCalcHiddenParaField(false)
100     , m_bHasHiddenParaField(false)
101     , m_bFootnote(false)
102     , m_bDDEFields(false)
103 {
104 }
105 
106 struct TxtAttrDeleter
107 {
108     SwAttrPool & m_rPool;
TxtAttrDeleterTxtAttrDeleter109     TxtAttrDeleter( SwDoc & rDoc ) : m_rPool( rDoc.GetAttrPool() ) { }
operator ()TxtAttrDeleter110     void operator() (SwTxtAttr * const pAttr)
111     {
112         if (RES_TXTATR_META == pAttr->Which() ||
113             RES_TXTATR_METAFIELD == pAttr->Which())
114         {
115             static_cast<SwTxtMeta *>(pAttr)->ChgTxtNode(0); // prevents ASSERT
116         }
117         SwTxtAttr::Destroy( pAttr, m_rPool );
118     }
119 };
120 
121 struct TxtAttrContains
122 {
123     xub_StrLen m_nPos;
TxtAttrContainsTxtAttrContains124     TxtAttrContains( const xub_StrLen nPos ) : m_nPos( nPos ) { }
operator ()TxtAttrContains125     bool operator() (SwTxtAttrEnd * const pAttr)
126     {
127         return (*pAttr->GetStart() < m_nPos) && (m_nPos < *pAttr->End());
128     }
129 };
130 
131 // a:       |-----|
132 // b:
133 //    |---|               => valid: b before a
134 //    |-----|             => valid: start == end; b before a
135 //    |---------|         => invalid: overlap (1)
136 //    |-----------|       => valid: same end; b around a
137 //    |-----------------| => valid: b around a
138 //          |---|         => valid; same start; b within a
139 //          |-----|       => valid; same start and end; b around or within a?
140 //          |-----------| => valid: same start: b around a
141 //            |-|         => valid: b within a
142 //            |---|       => valid: same end; b within a
143 //            |---------| => invalid: overlap (2)
144 //                |-----| => valid: end == start; b after a
145 //                  |---| => valid: b after a
146 // ===> 2 invalid overlap cases
147 static
isOverlap(const xub_StrLen nStart1,const xub_StrLen nEnd1,const xub_StrLen nStart2,const xub_StrLen nEnd2)148 bool isOverlap(const xub_StrLen nStart1, const xub_StrLen nEnd1,
149                const xub_StrLen nStart2, const xub_StrLen nEnd2)
150 {
151     return
152         ((nStart1 > nStart2) && (nStart1 < nEnd2) && (nEnd1 > nEnd2))  // (1)
153      || ((nStart1 < nStart2) && (nStart2 < nEnd1) && (nEnd1 < nEnd2)); // (2)
154 }
155 
156 /// #i106930#: now asymmetric: empty hint1 is _not_ nested, but empty hint2 is
157 static
isNestedAny(const xub_StrLen nStart1,const xub_StrLen nEnd1,const xub_StrLen nStart2,const xub_StrLen nEnd2)158 bool isNestedAny(const xub_StrLen nStart1, const xub_StrLen nEnd1,
159                  const xub_StrLen nStart2, const xub_StrLen nEnd2)
160 {
161     return ((nStart1 == nStart2) || (nEnd1 == nEnd2))
162         // same start/end: nested except if hint1 empty and hint2 not empty
163         ? (nStart1 != nEnd1) || (nStart2 == nEnd2)
164         : ((nStart1 < nStart2) ? (nEnd1 >= nEnd2) : (nEnd1 <= nEnd2));
165 }
166 
167 static
isSelfNestable(const sal_uInt16 nWhich)168 bool isSelfNestable(const sal_uInt16 nWhich)
169 {
170     if ((RES_TXTATR_INETFMT  == nWhich) ||
171         (RES_TXTATR_CJK_RUBY == nWhich) ||
172         (RES_TXTATR_INPUTFIELD == nWhich))
173         return false;
174     ASSERT((RES_TXTATR_META  == nWhich) ||
175            (RES_TXTATR_METAFIELD  == nWhich), "???");
176     return true;
177 }
178 
179 static
isSplittable(const sal_uInt16 nWhich)180 bool isSplittable(const sal_uInt16 nWhich)
181 {
182     if ((RES_TXTATR_INETFMT  == nWhich) ||
183         (RES_TXTATR_CJK_RUBY == nWhich))
184         return true;
185     ASSERT((RES_TXTATR_META  == nWhich) ||
186            (RES_TXTATR_METAFIELD  == nWhich) ||
187            (RES_TXTATR_INPUTFIELD  == nWhich), "???");
188     return false;
189 }
190 
191 enum Split_t { FAIL, SPLIT_NEW, SPLIT_OTHER };
192 /**
193   Calculate splitting policy for overlapping hints, based on what kind of
194   hint is inserted, and what kind of existing hint overlaps.
195   */
196 static Split_t
splitPolicy(const sal_uInt16 nWhichNew,const sal_uInt16 nWhichOther)197 splitPolicy(const sal_uInt16 nWhichNew, const sal_uInt16 nWhichOther)
198 {
199     if (!isSplittable(nWhichOther))
200     {
201         if (!isSplittable(nWhichNew))
202             return FAIL;
203         else
204             return SPLIT_NEW;
205     }
206     else
207     {
208         if ( RES_TXTATR_INPUTFIELD == nWhichNew )
209             return FAIL;
210         else if ( (RES_TXTATR_INETFMT  == nWhichNew) &&
211                   (RES_TXTATR_CJK_RUBY == nWhichOther) )
212             return SPLIT_NEW;
213         else
214             return SPLIT_OTHER;
215     }
216 }
217 
InitINetFmt(SwTxtNode & rNode)218 void SwTxtINetFmt::InitINetFmt(SwTxtNode & rNode)
219 {
220     ChgTxtNode(&rNode);
221     SwCharFmt * const pFmt(
222          rNode.GetDoc()->GetCharFmtFromPool(RES_POOLCHR_INET_NORMAL) );
223     pFmt->Add( this );
224 }
225 
InitRuby(SwTxtNode & rNode)226 void SwTxtRuby::InitRuby(SwTxtNode & rNode)
227 {
228     ChgTxtNode(&rNode);
229     SwCharFmt * const pFmt(
230         rNode.GetDoc()->GetCharFmtFromPool(RES_POOLCHR_RUBYTEXT) );
231     pFmt->Add( this );
232 }
233 
234 /**
235   Create a new nesting text hint.
236  */
237 static SwTxtAttrNesting *
MakeTxtAttrNesting(SwTxtNode & rNode,SwTxtAttrNesting & rNesting,const xub_StrLen nStart,const xub_StrLen nEnd)238 MakeTxtAttrNesting(SwTxtNode & rNode, SwTxtAttrNesting & rNesting,
239         const xub_StrLen nStart, const xub_StrLen nEnd)
240 {
241     SwTxtAttr * const pNew( MakeTxtAttr(
242             *rNode.GetDoc(), rNesting.GetAttr(), nStart, nEnd ) );
243     switch (pNew->Which())
244     {
245         case RES_TXTATR_INETFMT:
246         {
247             static_cast<SwTxtINetFmt*>(pNew)->InitINetFmt(rNode);
248             break;
249         }
250         case RES_TXTATR_CJK_RUBY:
251         {
252             static_cast<SwTxtRuby*>(pNew)->InitRuby(rNode);
253             break;
254         }
255         default:
256             ASSERT(false, "MakeTxtAttrNesting: what the hell is that?");
257             break;
258     }
259     return static_cast<SwTxtAttrNesting*>(pNew);
260 }
261 
262 typedef ::std::vector<SwTxtAttrNesting *> NestList_t;
263 
264 static void
lcl_DoSplitNew(NestList_t & rSplits,SwTxtNode & rNode,const xub_StrLen nNewStart,const xub_StrLen nOtherStart,const xub_StrLen nOtherEnd,bool bOtherDummy)265 lcl_DoSplitNew(NestList_t & rSplits, SwTxtNode & rNode,
266     const xub_StrLen nNewStart,
267     const xub_StrLen nOtherStart, const xub_StrLen nOtherEnd, bool bOtherDummy)
268 {
269     const bool bSplitAtStart(nNewStart < nOtherStart);
270     const xub_StrLen nSplitPos( (bSplitAtStart) ? nOtherStart : nOtherEnd );
271     // first find the portion that is split (not necessarily the last one!)
272     NestList_t::iterator const iter(
273         ::std::find_if( rSplits.begin(), rSplits.end(),
274             TxtAttrContains(nSplitPos) ) );
275     if (iter != rSplits.end()) // already split here?
276     {
277         const xub_StrLen nStartPos( // skip other's dummy character!
278             (bSplitAtStart && bOtherDummy) ? nSplitPos + 1 : nSplitPos );
279         SwTxtAttrNesting * const pNew( MakeTxtAttrNesting(
280                 rNode, **iter, nStartPos, *(*iter)->GetEnd() ) );
281         *(*iter)->GetEnd() = nSplitPos;
282         rSplits.insert(iter + 1, pNew);
283     }
284 }
285 
286 /**
287   Insert nesting hint into the hints array. Also calls NoteInHistory.
288   @param    rNewHint    the hint to be inserted (must not overlap existing!)
289  */
InsertNesting(SwTxtAttrNesting & rNewHint)290 void SwpHints::InsertNesting(SwTxtAttrNesting & rNewHint)
291 {
292     SwpHintsArray::Insert(& rNewHint);
293     NoteInHistory( & rNewHint, true );
294 }
295 
296 /**
297 
298 The following hints correspond to well-formed XML elements in ODF:
299 RES_TXTATR_INETFMT, RES_TXTATR_CJK_RUBY, RES_TXTATR_META, RES_TXTATR_METAFIELD
300 
301 The writer core must ensure that these do not overlap; if they did,
302 the document would not be storable as ODF.
303 
304 Also, a Hyperlink must not be nested within another Hyperlink,
305 and a Ruby must not be nested within another Ruby.
306 
307 The ODF export in xmloff will only put a hyperlink into a ruby, never a ruby
308 into a hyperlink.
309 
310 Unfortunately the UNO API for Hyperlink and Ruby consists of the properties
311 Hyperlink* and Ruby* of the css.text.CharacterProperties service.  In other
312 words, they are treated as formatting attributes, not as content entites.
313 Furthermore, for API users it is not possible to easily test whether a certain
314 range would be overlapping with other nested attributes, and most importantly,
315 <em>which ones</em>, so we can hardly refuse to insert these in cases of
316 overlap.
317 
318 It is possible to split Hyperlink and Ruby into multiple portions, such that
319 the result is properly nested.
320 
321 meta and meta-field must not be split, because they have xml:id.
322 
323 These constraints result in the following design:
324 
325 RES_TXTATR_INETFMT:
326     always succeeds
327     inserts n attributes split at RES_TXTATR_CJK_RUBY, RES_TXTATR_META,
328         RES_TXTATR_METAFIELD
329     may replace existing RES_TXTATR_INETFMT at overlap
330 RES_TXTATR_CJK_RUBY:
331     always succeeds
332     inserts n attributes split at RES_TXTATR_META, RES_TXTATR_METAFIELD
333     may replace existing RES_TXTATR_CJK_RUBY at overlap
334     may split existing overlapping RES_TXTATR_INETFMT
335 RES_TXTATR_META:
336     may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD
337     may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY
338     inserts 1 attribute
339 RES_TXTATR_METAFIELD:
340     may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD
341     may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY
342     inserts 1 attribute
343 
344 The nesting is expressed by the position of the hints.
345 RES_TXTATR_META and RES_TXTATR_METAFIELD have a CH_TXTATR, and there can
346 only be one such hint starting and ending at a given position.
347 Only RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY lack a CH_TXTATR.
348 The interpretation given is that RES_TXTATR_CJK_RUBY is always around
349 a RES_TXTATR_INETFMT at the same start and end position (which corresponds
350 with the UNO API).
351 Both of these are always around a nesting hint with CH_TXTATR at the same
352 start and end position (if they should be inside, then the start should be
353 after the CH_TXTATR).
354 It would probably be a bad idea to add another nesting hint without
355 CH_TXTATR; on the other hand, it would be difficult adding a CH_TXTATR to
356 RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY, due to the overwriting and
357 splitting of exising hints that is necessary for backward compatibility.
358 
359     @param rNode    the text node
360     @param rHint    the hint to be inserted
361     @returns        true iff hint was successfully inserted
362 */
363 bool
TryInsertNesting(SwTxtNode & rNode,SwTxtAttrNesting & rNewHint)364 SwpHints::TryInsertNesting( SwTxtNode & rNode, SwTxtAttrNesting & rNewHint )
365 {
366 //    INVARIANT:  the nestable hints in the array are properly nested
367     const sal_uInt16 nNewWhich( rNewHint.Which() );
368     const xub_StrLen nNewStart( *rNewHint.GetStart() );
369     const xub_StrLen nNewEnd  ( *rNewHint.GetEnd()   );
370     const bool bNewSelfNestable( isSelfNestable(nNewWhich) );
371 
372     ASSERT( (RES_TXTATR_INETFMT   == nNewWhich) ||
373             (RES_TXTATR_CJK_RUBY  == nNewWhich) ||
374             (RES_TXTATR_META      == nNewWhich) ||
375             (RES_TXTATR_METAFIELD == nNewWhich) ||
376             (RES_TXTATR_INPUTFIELD == nNewWhich),
377         "TryInsertNesting: Expecting INETFMT or RUBY or META or METAFIELD or INPUTFIELD" );
378 
379     NestList_t OverlappingExisting; // existing hints to be split
380     NestList_t OverwrittenExisting; // existing hints to be replaced
381     NestList_t SplitNew;            // new hints to be inserted
382 
383     SplitNew.push_back(& rNewHint);
384 
385     // pass 1: split the inserted hint into fragments if necessary
386     for ( sal_uInt16 i = 0; i < GetEndCount(); ++i )
387     {
388         SwTxtAttr * const pOther = GetEnd(i);
389 
390         if (pOther->IsNesting())
391         {
392             const sal_uInt16 nOtherWhich( pOther->Which() );
393             const xub_StrLen nOtherStart( *(pOther)->GetStart() );
394             const xub_StrLen nOtherEnd  ( *(pOther)->GetEnd()   );
395             if (isOverlap(nNewStart, nNewEnd, nOtherStart, nOtherEnd ))
396             {
397                 switch (splitPolicy(nNewWhich, nOtherWhich))
398                 {
399                     case FAIL:
400                         OSL_TRACE("cannot insert hint: overlap detected");
401                         ::std::for_each(SplitNew.begin(), SplitNew.end(),
402                             TxtAttrDeleter(*rNode.GetDoc()));
403                         return false;
404                     case SPLIT_NEW:
405                         lcl_DoSplitNew(SplitNew, rNode, nNewStart,
406                             nOtherStart, nOtherEnd, pOther->HasDummyChar());
407                         break;
408                     case SPLIT_OTHER:
409                         OverlappingExisting.push_back(
410                             static_cast<SwTxtAttrNesting*>(pOther));
411                         break;
412                     default:
413                         ASSERT(false, "bad code monkey");
414                         break;
415                 }
416             }
417             else if (isNestedAny(nNewStart, nNewEnd, nOtherStart, nOtherEnd))
418             {
419                 if (!bNewSelfNestable && (nNewWhich == nOtherWhich))
420                 {
421                 // ruby and hyperlink: if there is nesting, _overwrite_
422                 OverwrittenExisting.push_back(
423                     static_cast<SwTxtAttrNesting*>(pOther));
424                 }
425                 else if ((nNewStart == nOtherStart) && pOther->HasDummyChar())
426                 {
427                     if (rNewHint.HasDummyChar())
428                     {
429                         ASSERT(false,
430                                 "ERROR: inserting duplicate CH_TXTATR hint");
431                         return false;
432                     } else if (nNewEnd < nOtherEnd) {
433                         // other has dummy char, new is inside other, but
434                         // new contains the other's dummy char?
435                         // should be corrected because it may lead to problems
436                         // in SwXMeta::createEnumeration
437                         // SplitNew is sorted, so this is the first split
438                         xub_StrLen *const pStart(SplitNew.front()->GetStart());
439                         ASSERT(*pStart == nNewStart, "how did that happen?");
440                         *pStart = nNewStart + 1;
441                     }
442                 }
443             }
444         }
445     }
446 
447     ASSERT (isSplittable(nNewWhich) || SplitNew.size() == 1,
448             "splitting the unsplittable ???");
449 
450     // pass 2: split existing hints that overlap/nest with new hint
451     // do not iterate over hints array, but over remembered set of overlapping
452     // hints, to keep things simple w.r.t. insertion/removal
453     // N.B: if there is a hint that splits the inserted hint, then
454     // that hint would also have already split any hint in OverlappingExisting
455     // so any hint in OverlappingExisting can be split at most by one hint
456     // in SplitNew, or even not at all (this is not true for existing hints
457     // that go _around_ new hint, which is the raison d'^etre for pass 4)
458     for (NestList_t::iterator itOther = OverlappingExisting.begin();
459             itOther != OverlappingExisting.end(); ++itOther)
460     {
461         const xub_StrLen nOtherStart( *(*itOther)->GetStart() );
462         const xub_StrLen nOtherEnd  ( *(*itOther)->GetEnd()   );
463 
464         for (NestList_t::iterator itNew = SplitNew.begin();
465                 itNew != SplitNew.end(); ++itNew)
466         {
467             const xub_StrLen nSplitNewStart( *(*itNew)->GetStart() );
468             const xub_StrLen nSplitNewEnd  ( *(*itNew)->GetEnd()   );
469             // 4 cases: within, around, overlap l, overlap r, (OTHER: no action)
470             const bool bRemoveOverlap(
471                 !bNewSelfNestable && (nNewWhich == (*itOther)->Which()) );
472 
473             switch (ComparePosition(nSplitNewStart, nSplitNewEnd,
474                                     nOtherStart,    nOtherEnd))
475             {
476                 case POS_INSIDE:
477                     {
478                         ASSERT(!bRemoveOverlap,
479                             "this one should be in OverwrittenExisting?");
480                     }
481                     break;
482                 case POS_OUTSIDE:
483                 case POS_EQUAL:
484                     {
485                         ASSERT(false, "existing hint inside new hint: why?");
486                     }
487                     break;
488                 case POS_OVERLAP_BEFORE:
489                     {
490                         Delete( *itOther ); // this also does NoteInHistory!
491                         *(*itOther)->GetStart() = nSplitNewEnd;
492                         InsertNesting( **itOther );
493                         if (!bRemoveOverlap)
494                         {
495                             if ( USHRT_MAX == Count() )
496                             {
497                                 ASSERT(false, "hints array full :-(");
498                                 return false;
499                             }
500                             SwTxtAttrNesting * const pOtherLeft(
501                                 MakeTxtAttrNesting( rNode, **itOther,
502                                     nOtherStart, nSplitNewEnd ) );
503                             InsertNesting( *pOtherLeft );
504                         }
505                     }
506                     break;
507                 case POS_OVERLAP_BEHIND:
508                     {
509                         Delete( *itOther ); // this also does NoteInHistory!
510                         *(*itOther)->GetEnd() = nSplitNewStart;
511                         InsertNesting( **itOther );
512                         if (!bRemoveOverlap)
513                         {
514                             if ( USHRT_MAX == Count() )
515                             {
516                                 ASSERT(false, "hints array full :-(");
517                                 return false;
518                             }
519                             SwTxtAttrNesting * const pOtherRight(
520                                 MakeTxtAttrNesting( rNode, **itOther,
521                                     nSplitNewStart, nOtherEnd ) );
522                             InsertNesting( *pOtherRight );
523                         }
524                     }
525                     break;
526                 default:
527                     break; // overlap resolved by splitting new: nothing to do
528             }
529         }
530     }
531 
532     if ( USHRT_MAX - SplitNew.size() <= Count() )
533     {
534         ASSERT(false, "hints array full :-(");
535         return false;
536     }
537 
538     // pass 3: insert new hints
539     for (NestList_t::iterator iter = SplitNew.begin();
540             iter != SplitNew.end(); ++iter)
541     {
542         InsertNesting(**iter);
543     }
544 
545     // pass 4: handle overwritten hints
546     // RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY should displace attributes
547     // of the same kind.
548     for (NestList_t::iterator itOther = OverwrittenExisting.begin();
549             itOther != OverwrittenExisting.end(); ++itOther)
550     {
551         const xub_StrLen nOtherStart( *(*itOther)->GetStart() );
552         const xub_StrLen nOtherEnd  ( *(*itOther)->GetEnd()   );
553 
554         // overwritten portion is given by start/end of inserted hint
555         if ((nNewStart <= nOtherStart) && (nOtherEnd <= nNewEnd))
556         {
557             Delete(*itOther);
558             rNode.DestroyAttr( *itOther );
559         }
560         else
561         {
562             ASSERT((nOtherStart < nNewStart) && (nNewEnd < nOtherEnd), "huh?");
563         // scenario: there is a RUBY, and contained within that a META;
564         // now a RUBY is inserted within the META => the exising RUBY is split:
565         // here it is not possible to simply insert the left/right fragment
566         // of the existing RUBY because they <em>overlap</em> with the META!
567             Delete( *itOther ); // this also does NoteInHistory!
568             *(*itOther)->GetEnd() = nNewStart;
569             bool bSuccess( TryInsertNesting(rNode, **itOther) );
570             ASSERT(bSuccess, "recursive call 1 failed?");
571             SwTxtAttrNesting * const pOtherRight(
572                 MakeTxtAttrNesting(
573                     rNode, **itOther, nNewEnd, nOtherEnd ) );
574             bSuccess = TryInsertNesting(rNode, *pOtherRight);
575             ASSERT(bSuccess, "recursive call 2 failed?");
576         }
577 
578     }
579 
580     return true;
581 }
582 
583 
584 // This function takes care for the following text attribute:
585 // RES_TXTATR_CHARFMT, RES_TXTATR_AUTOFMT
586 // These attributes have to be handled in a special way (Portion building).
587 //
588 // The new attribute will be split by any existing RES_TXTATR_AUTOFMT or
589 // RES_TXTATR_CHARFMT. The new attribute itself will
590 // split any existing RES_TXTATR_AUTOFMT or RES_TXTATR_CHARFMT.
591 
BuildPortions(SwTxtNode & rNode,SwTxtAttr & rNewHint,const SetAttrMode nMode)592 void SwpHints::BuildPortions( SwTxtNode& rNode, SwTxtAttr& rNewHint,
593         const SetAttrMode nMode )
594 {
595     const sal_uInt16 nWhich = rNewHint.Which();
596 
597     const xub_StrLen nThisStart = *rNewHint.GetStart();
598     const xub_StrLen nThisEnd =   *rNewHint.GetEnd();
599     const bool bNoLengthAttribute = nThisStart == nThisEnd;
600 
601     std::vector<SwTxtAttr*> aInsDelHints;
602     std::vector<SwTxtAttr*>::iterator aIter;
603 
604     ASSERT( RES_TXTATR_CHARFMT == rNewHint.Which() ||
605             RES_TXTATR_AUTOFMT == rNewHint.Which(),
606             "Expecting CHARFMT or AUTOFMT" );
607 
608     //
609     // 2. Find the hints which cover the start and end position
610     // of the new hint. These hints have to be split into two portions:
611     //
612     if ( !bNoLengthAttribute ) // nothing to do for no length attributes
613     {
614         for ( sal_uInt16 i = 0; i < Count(); ++i )
615         {
616             SwTxtAttr* pOther = GetTextHint(i);
617 
618             if ( RES_TXTATR_CHARFMT != pOther->Which() &&
619                  RES_TXTATR_AUTOFMT != pOther->Which() )
620                 continue;
621 
622             xub_StrLen nOtherStart = *pOther->GetStart();
623             const xub_StrLen nOtherEnd = *pOther->GetEnd();
624 
625             // Check if start of new attribute overlaps with pOther:
626             // Split pOther if necessary:
627             if ( nOtherStart < nThisStart && nThisStart < nOtherEnd )
628             {
629                 SwTxtAttr* pNewAttr = MakeTxtAttr( *rNode.GetDoc(),
630                         pOther->GetAttr(), nOtherStart, nThisStart );
631                 if ( RES_TXTATR_CHARFMT == pOther->Which() )
632                     static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( static_cast<SwTxtCharFmt*>(pOther)->GetSortNumber() );
633                 aInsDelHints.push_back( pNewAttr );
634 
635                 NoteInHistory( pOther );
636                 *pOther->GetStart() = nThisStart;
637                 NoteInHistory( pOther, true );
638 
639                 nOtherStart = nThisStart;
640             }
641 
642             // Check if end of new attribute overlaps with pOther:
643             // Split pOther if necessary:
644             if ( nOtherStart < nThisEnd && nThisEnd < nOtherEnd )
645             {
646                 SwTxtAttr* pNewAttr = MakeTxtAttr( *rNode.GetDoc(),
647                         pOther->GetAttr(), nOtherStart, nThisEnd );
648                 if ( RES_TXTATR_CHARFMT == pOther->Which() )
649                     static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( static_cast<SwTxtCharFmt*>(pOther)->GetSortNumber() );
650                 aInsDelHints.push_back( pNewAttr );
651 
652                 NoteInHistory( pOther );
653                 *pOther->GetStart() = nThisEnd;
654                 NoteInHistory( pOther, true );
655             }
656         }
657 
658         // Insert the newly created attributes:
659         for ( aIter = aInsDelHints.begin(); aIter != aInsDelHints.end(); ++aIter )
660         {
661             SwpHintsArray::Insert( *aIter );
662             NoteInHistory( *aIter, true );
663         }
664     }
665 
666 #ifdef DBG_UTIL
667     if( !rNode.GetDoc()->IsInReading() )
668         CHECK;
669 #endif
670 
671     //
672     // 4. Split rNewHint into 1 ... n new hints:
673     //
674     std::set<xub_StrLen> aBounds;
675     aBounds.insert( nThisStart );
676     aBounds.insert( nThisEnd );
677 
678     if ( !bNoLengthAttribute ) // nothing to do for no length attributes
679     {
680         for ( sal_uInt16 i = 0; i < Count(); ++i )
681         {
682             const SwTxtAttr* pOther = GetTextHint(i);
683 
684             if ( RES_TXTATR_CHARFMT != pOther->Which() &&
685                  RES_TXTATR_AUTOFMT != pOther->Which() )
686                 continue;
687 
688             const xub_StrLen nOtherStart = *pOther->GetStart();
689             const xub_StrLen nOtherEnd = *pOther->End();
690 
691             aBounds.insert( nOtherStart );
692             aBounds.insert( nOtherEnd );
693         }
694     }
695 
696     std::set<xub_StrLen>::iterator aStartIter = aBounds.lower_bound( nThisStart );
697     std::set<xub_StrLen>::iterator aEndIter = aBounds.upper_bound( nThisEnd );
698     xub_StrLen nPorStart = *aStartIter;
699     ++aStartIter;
700     bool bDestroyHint = true;
701 
702     //
703     // Insert the 1...n new parts of the new attribute:
704     //
705     while ( aStartIter != aEndIter || bNoLengthAttribute )
706     {
707         ASSERT( bNoLengthAttribute || nPorStart < *aStartIter, "AUTOSTYLES: BuildPortion trouble" )
708 
709         const xub_StrLen nPorEnd = bNoLengthAttribute ? nPorStart : *aStartIter;
710         aInsDelHints.clear();
711 
712         // Get all hints that are in [nPorStart, nPorEnd[:
713         for ( sal_uInt16 i = 0; i < Count(); ++i )
714         {
715             SwTxtAttr *pOther = GetTextHint(i);
716 
717             if ( RES_TXTATR_CHARFMT != pOther->Which() &&
718                  RES_TXTATR_AUTOFMT != pOther->Which() )
719                 continue;
720 
721             const xub_StrLen nOtherStart = *pOther->GetStart();
722 
723             if ( nOtherStart > nPorStart )
724                 break;
725 
726             if ( pOther->GetEnd() && *pOther->GetEnd() == nPorEnd && nOtherStart == nPorStart )
727             {
728                 ASSERT( *pOther->GetEnd() == nPorEnd, "AUTOSTYLES: BuildPortion trouble" )
729                 aInsDelHints.push_back( pOther );
730             }
731         }
732 
733         SwTxtAttr* pNewAttr = 0;
734         if ( RES_TXTATR_CHARFMT == nWhich )
735         {
736             // pNewHint can be inserted after calculating the sort value.
737             // This should ensure, that pNewHint comes behind the already present
738             // character style
739             sal_uInt16 nCharStyleCount = 0;
740             aIter = aInsDelHints.begin();
741             while ( aIter != aInsDelHints.end() )
742             {
743                 if ( RES_TXTATR_CHARFMT == (*aIter)->Which() )
744                 {
745                     // --> FME 2007-02-16 #i74589#
746                     const SwFmtCharFmt& rOtherCharFmt = (*aIter)->GetCharFmt();
747                     const SwFmtCharFmt& rThisCharFmt = rNewHint.GetCharFmt();
748                     const bool bSameCharFmt = rOtherCharFmt.GetCharFmt() == rThisCharFmt.GetCharFmt();
749                     // <--
750 
751                     // --> OD 2009-03-24 #i90311#
752                     // Do not remove existing character format hint during XML import
753                     if ( !rNode.GetDoc()->IsInXMLImport() &&
754                          ( !( nsSetAttrMode::SETATTR_DONTREPLACE & nMode ) ||
755                            bNoLengthAttribute ||
756                            bSameCharFmt ) )
757                     // <--
758                     {
759                         // Remove old hint
760                         Delete( *aIter );
761                         rNode.DestroyAttr( *aIter );
762                     }
763                     else
764                         ++nCharStyleCount;
765                 }
766                 else
767                 {
768                     // remove all attributes from auto styles, which are explicitly set in
769                     // the new character format:
770                     ASSERT( RES_TXTATR_AUTOFMT == (*aIter)->Which(), "AUTOSTYLES - Misc trouble" )
771                     SwTxtAttr* pOther = *aIter;
772                     boost::shared_ptr<SfxItemSet> pOldStyle = static_cast<const SwFmtAutoFmt&>(pOther->GetAttr()).GetStyleHandle();
773 
774                     // For each attribute in the automatic style check if it
775                     // is also set to the new character style:
776                     SfxItemSet aNewSet( *pOldStyle->GetPool(),
777                         aCharAutoFmtSetRange);
778                     SfxItemIter aItemIter( *pOldStyle );
779                     const SfxPoolItem* pItem = aItemIter.GetCurItem();
780                     while( sal_True )
781                     {
782                         if ( !CharFmt::IsItemIncluded( pItem->Which(), &rNewHint ) )
783                         {
784                             aNewSet.Put( *pItem );
785                         }
786 
787                         if( aItemIter.IsAtEnd() )
788                             break;
789 
790                         pItem = aItemIter.NextItem();
791                     }
792 
793                     // Remove old hint
794                     Delete( pOther );
795                     rNode.DestroyAttr( pOther );
796 
797                     // Create new AutoStyle
798                     if ( aNewSet.Count() )
799                     {
800                         pNewAttr = MakeTxtAttr( *rNode.GetDoc(),
801                                 aNewSet, nPorStart, nPorEnd );
802                         SwpHintsArray::Insert( pNewAttr );
803                         NoteInHistory( pNewAttr, true );
804                     }
805                 }
806                 ++aIter;
807             }
808 
809             // If there is no current hint and start and end of rNewHint
810             // is ok, we do not need to create a new txtattr.
811             if ( nPorStart == nThisStart &&
812                  nPorEnd == nThisEnd &&
813                  !nCharStyleCount )
814             {
815                 pNewAttr = &rNewHint;
816                 bDestroyHint = false;
817             }
818             else
819             {
820                 pNewAttr = MakeTxtAttr( *rNode.GetDoc(), rNewHint.GetAttr(),
821                         nPorStart, nPorEnd );
822                 static_cast<SwTxtCharFmt*>(pNewAttr)->SetSortNumber( nCharStyleCount );
823             }
824         }
825         else
826         {
827             // Find the current autostyle. Mix attributes if necessary.
828             SwTxtAttr* pCurrentAutoStyle = 0;
829             SwTxtAttr* pCurrentCharFmt = 0;
830             aIter = aInsDelHints.begin();
831             while ( aIter != aInsDelHints.end() )
832             {
833                 if ( RES_TXTATR_AUTOFMT == (*aIter)->Which() )
834                     pCurrentAutoStyle = *aIter;
835                 else if ( RES_TXTATR_CHARFMT == (*aIter)->Which() )
836                     pCurrentCharFmt = *aIter;
837                 ++aIter;
838             }
839 
840             boost::shared_ptr<SfxItemSet> pNewStyle = static_cast<const SwFmtAutoFmt&>(rNewHint.GetAttr()).GetStyleHandle();
841             if ( pCurrentAutoStyle )
842             {
843                 boost::shared_ptr<SfxItemSet> pCurrentStyle = static_cast<const SwFmtAutoFmt&>(pCurrentAutoStyle->GetAttr()).GetStyleHandle();
844 
845                 // Merge attributes
846                 SfxItemSet aNewSet( *pCurrentStyle );
847                 aNewSet.Put( *pNewStyle );
848 
849                 // --> FME 2007-4-11 #i75750# Remove attributes already set at whole paragraph
850                 // --> FME 2007-09-24 #i81764# This should not be applied for no length attributes!!! <--
851                 if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && aNewSet.Count() )
852                 {
853                     SfxItemIter aIter2( aNewSet );
854                     const SfxPoolItem* pItem = aIter2.GetCurItem();
855                     const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet();
856 
857                     do
858                     {
859                         const SfxPoolItem* pTmpItem = 0;
860                         if ( SFX_ITEM_SET == rWholeParaAttrSet.GetItemState( pItem->Which(), sal_False, &pTmpItem ) &&
861                              pTmpItem == pItem )
862                         {
863                             // Do not clear item if the attribute is set in a character format:
864                             if ( !pCurrentCharFmt || 0 == CharFmt::GetItem( *pCurrentCharFmt, pItem->Which() ) )
865                                 aNewSet.ClearItem( pItem->Which() );
866                         }
867                     }
868                     while (!aIter2.IsAtEnd() && 0 != (pItem = aIter2.NextItem()));
869                 }
870                 // <--
871 
872                 // Remove old hint
873                 Delete( pCurrentAutoStyle );
874                 rNode.DestroyAttr( pCurrentAutoStyle );
875 
876                 // Create new AutoStyle
877                 if ( aNewSet.Count() )
878                     pNewAttr = MakeTxtAttr( *rNode.GetDoc(), aNewSet,
879                             nPorStart, nPorEnd );
880             }
881             else
882             {
883                 // Remove any attributes which are already set at the whole paragraph:
884                 bool bOptimizeAllowed = true;
885 
886                 SfxItemSet* pNewSet = 0;
887                 // --> FME 2007-4-11 #i75750# Remove attributes already set at whole paragraph
888                 // --> FME 2007-09-24 #i81764# This should not be applied for no length attributes!!! <--
889                 if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && pNewStyle->Count() )
890                 {
891                     SfxItemIter aIter2( *pNewStyle );
892                     const SfxPoolItem* pItem = aIter2.GetCurItem();
893                     const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet();
894 
895                     do
896                     {
897                         const SfxPoolItem* pTmpItem = 0;
898                         if ( SFX_ITEM_SET == rWholeParaAttrSet.GetItemState( pItem->Which(), sal_False, &pTmpItem ) &&
899                              pTmpItem == pItem )
900                         {
901                             // Do not clear item if the attribute is set in a character format:
902                             if ( !pCurrentCharFmt || 0 == CharFmt::GetItem( *pCurrentCharFmt, pItem->Which() ) )
903                             {
904                                 if ( !pNewSet )
905                                     pNewSet = pNewStyle->Clone( sal_True );
906                                 pNewSet->ClearItem( pItem->Which() );
907                             }
908                         }
909                     }
910                     while (!aIter2.IsAtEnd() && 0 != (pItem = aIter2.NextItem()));
911 
912                     if ( pNewSet )
913                     {
914                         bOptimizeAllowed = false;
915                         if ( pNewSet->Count() )
916                             pNewStyle = rNode.getIDocumentStyleAccess().getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR );
917                         else
918                             pNewStyle.reset();
919 
920                         delete pNewSet;
921                     }
922                 }
923                 // <--
924 
925                 // Create new AutoStyle
926                 // If there is no current hint and start and end of rNewHint
927                 // is ok, we do not need to create a new txtattr.
928                 if ( bOptimizeAllowed &&
929                      nPorStart == nThisStart &&
930                      nPorEnd == nThisEnd )
931                 {
932                     pNewAttr = &rNewHint;
933                     bDestroyHint = false;
934                 }
935                 else if ( pNewStyle.get() )
936                 {
937                     pNewAttr = MakeTxtAttr( *rNode.GetDoc(), *pNewStyle,
938                             nPorStart, nPorEnd );
939                 }
940             }
941         }
942 
943         if ( pNewAttr )
944         {
945             SwpHintsArray::Insert( pNewAttr );
946 //            if ( bDestroyHint )
947                 NoteInHistory( pNewAttr, true );
948         }
949 
950         if ( !bNoLengthAttribute )
951         {
952             nPorStart = *aStartIter;
953             ++aStartIter;
954         }
955         else
956             break;
957     }
958 
959     if ( bDestroyHint )
960         rNode.DestroyAttr( &rNewHint );
961 }
962 
963 /*************************************************************************
964  *						SwTxtNode::MakeTxtAttr()
965  *************************************************************************/
966 
MakeRedlineTxtAttr(SwDoc & rDoc,SfxPoolItem & rAttr)967 SwTxtAttr* MakeRedlineTxtAttr( SwDoc & rDoc, SfxPoolItem & rAttr )
968 {
969     // this is intended _only_ for special-purpose redline attributes!
970     switch (rAttr.Which())
971     {
972         case RES_CHRATR_COLOR:
973         case RES_CHRATR_WEIGHT:
974         case RES_CHRATR_CJK_WEIGHT:
975         case RES_CHRATR_CTL_WEIGHT:
976         case RES_CHRATR_POSTURE:
977         case RES_CHRATR_CJK_POSTURE:
978         case RES_CHRATR_CTL_POSTURE:
979         case RES_CHRATR_UNDERLINE:
980         case RES_CHRATR_CROSSEDOUT:
981         case RES_CHRATR_CASEMAP:
982         case RES_CHRATR_BACKGROUND:
983             break;
984         default:
985             ASSERT(false, "unsupported redline attribute");
986             break;
987     }
988 
989     // Put new attribute into pool
990     // FIXME: this const_cast is evil!
991     SfxPoolItem& rNew =
992         const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) );
993     return new SwTxtAttrEnd( rNew, 0, 0 );
994 }
995 
996 // create new text attribute
MakeTxtAttr(SwDoc & rDoc,SfxPoolItem & rAttr,xub_StrLen const nStt,xub_StrLen const nEnd,CopyOrNew_t const bIsCopy,SwTxtNode * const pTxtNode)997 SwTxtAttr* MakeTxtAttr(
998     SwDoc & rDoc,
999     SfxPoolItem& rAttr,
1000     xub_StrLen const nStt,
1001     xub_StrLen const nEnd,
1002     CopyOrNew_t const bIsCopy,
1003     SwTxtNode *const pTxtNode )
1004 {
1005     if ( isCHRATR(rAttr.Which()) )
1006     {
1007         // Somebody wants to build a SwTxtAttr for a character attribute.
1008         // Sorry, this is not allowed any longer.
1009         // You'll get a brand new autostyle attribute:
1010         SfxItemSet aItemSet( rDoc.GetAttrPool(),
1011                 RES_CHRATR_BEGIN, RES_CHRATR_END );
1012         aItemSet.Put( rAttr );
1013         return MakeTxtAttr( rDoc, aItemSet, nStt, nEnd );
1014     }
1015     else if ( RES_TXTATR_AUTOFMT == rAttr.Which() &&
1016               static_cast<const SwFmtAutoFmt&>(rAttr).GetStyleHandle()->
1017                 GetPool() != &rDoc.GetAttrPool() )
1018     {
1019         // If the attribute is an auto-style which refers to a pool that is
1020         // different from rDoc's pool, we have to correct this:
1021         const StylePool::SfxItemSet_Pointer_t pAutoStyle = static_cast<const SwFmtAutoFmt&>(rAttr).GetStyleHandle();
1022         ::std::auto_ptr<const SfxItemSet> pNewSet(
1023                 pAutoStyle->SfxItemSet::Clone( sal_True, &rDoc.GetAttrPool() ));
1024         SwTxtAttr* pNew = MakeTxtAttr( rDoc, *pNewSet, nStt, nEnd );
1025         return pNew;
1026     }
1027 
1028     // Put new attribute into pool
1029     // FIXME: this const_cast is evil!
1030     SfxPoolItem& rNew =
1031         const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) );
1032 
1033 	SwTxtAttr* pNew = 0;
1034 	switch( rNew.Which() )
1035 	{
1036     case RES_TXTATR_CHARFMT:
1037 		{
1038 			SwFmtCharFmt &rFmtCharFmt = (SwFmtCharFmt&) rNew;
1039 			if( !rFmtCharFmt.GetCharFmt() )
1040             {
1041                 rFmtCharFmt.SetCharFmt( rDoc.GetDfltCharFmt() );
1042             }
1043 
1044 			pNew = new SwTxtCharFmt( rFmtCharFmt, nStt, nEnd );
1045 		}
1046 		break;
1047 	case RES_TXTATR_INETFMT:
1048 		pNew = new SwTxtINetFmt( (SwFmtINetFmt&)rNew, nStt, nEnd );
1049 		break;
1050 
1051     case RES_TXTATR_FIELD:
1052         pNew =
1053             new SwTxtFld( static_cast<SwFmtFld &>(rNew), nStt, rDoc.IsClipBoard() );
1054         break;
1055 
1056     case RES_TXTATR_ANNOTATION:
1057         {
1058             pNew =
1059                 new SwTxtAnnotationFld( static_cast<SwFmtFld &>(rNew), nStt, rDoc.IsClipBoard() );
1060             if ( bIsCopy == COPY )
1061             {
1062                 // On copy of the annotation field do not keep the annotated text range by removing
1063                 // the relation to its annotation mark (relation established via annotation field's name).
1064                 // If the annotation mark is also copied, the relation and thus the annotated text range will be reestablished,
1065                 // when the annotation mark is created and inserted into the document.
1066                 const_cast<SwPostItField*>(dynamic_cast< const SwPostItField* >(pNew->GetFmtFld().GetField()))->SetName( String() );
1067             }
1068         }
1069         break;
1070 
1071     case RES_TXTATR_INPUTFIELD:
1072         pNew =
1073             new SwTxtInputFld( static_cast<SwFmtFld &>(rNew), nStt, nEnd, rDoc.IsClipBoard() );
1074         break;
1075 
1076 	case RES_TXTATR_FLYCNT:
1077 		{
1078 			// erst hier wird das Frame-Format kopiert (mit Inhalt) !!
1079 			pNew = new SwTxtFlyCnt( (SwFmtFlyCnt&)rNew, nStt );
1080 			// Kopie von einem Text-Attribut
1081             if ( static_cast<const SwFmtFlyCnt &>(rAttr).GetTxtFlyCnt() )
1082             {
1083                 // then the format must be copied
1084                 static_cast<SwTxtFlyCnt *>(pNew)->CopyFlyFmt( &rDoc );
1085             }
1086         }
1087         break;
1088 	case RES_TXTATR_FTN:
1089 		pNew = new SwTxtFtn( (SwFmtFtn&)rNew, nStt );
1090 		// ggfs. SeqNo kopieren
1091 		if( ((SwFmtFtn&)rAttr).GetTxtFtn() )
1092 			((SwTxtFtn*)pNew)->SetSeqNo( ((SwFmtFtn&)rAttr).GetTxtFtn()->GetSeqRefNo() );
1093 		break;
1094 	case RES_TXTATR_REFMARK:
1095 		pNew = nStt == nEnd
1096 				? new SwTxtRefMark( (SwFmtRefMark&)rNew, nStt )
1097 				: new SwTxtRefMark( (SwFmtRefMark&)rNew, nStt, &nEnd );
1098 		break;
1099 	case RES_TXTATR_TOXMARK:
1100 		pNew = new SwTxtTOXMark( (SwTOXMark&)rNew, nStt, &nEnd );
1101 		break;
1102 	case RES_TXTATR_CJK_RUBY:
1103 		pNew = new SwTxtRuby( (SwFmtRuby&)rNew, nStt, nEnd );
1104 		break;
1105     case RES_TXTATR_META:
1106     case RES_TXTATR_METAFIELD:
1107         pNew = SwTxtMeta::CreateTxtMeta( rDoc.GetMetaFieldManager(), pTxtNode,
1108                 static_cast<SwFmtMeta&>(rNew), nStt, nEnd, bIsCopy );
1109         break;
1110     default:
1111         ASSERT(RES_TXTATR_AUTOFMT == rNew.Which(), "unknown attribute");
1112         pNew = new SwTxtAttrEnd( rNew, nStt, nEnd );
1113         break;
1114     }
1115 
1116     return pNew;
1117 }
1118 
MakeTxtAttr(SwDoc & rDoc,const SfxItemSet & rSet,xub_StrLen nStt,xub_StrLen nEnd)1119 SwTxtAttr* MakeTxtAttr( SwDoc & rDoc, const SfxItemSet& rSet,
1120                         xub_StrLen nStt, xub_StrLen nEnd )
1121 {
1122     IStyleAccess& rStyleAccess = rDoc.GetIStyleAccess();
1123     const StylePool::SfxItemSet_Pointer_t pAutoStyle = rStyleAccess.getAutomaticStyle( rSet, IStyleAccess::AUTO_STYLE_CHAR );
1124     SwFmtAutoFmt aNewAutoFmt;
1125     aNewAutoFmt.SetStyleHandle( pAutoStyle );
1126     SwTxtAttr* pNew = MakeTxtAttr( rDoc, aNewAutoFmt, nStt, nEnd );
1127     return pNew;
1128 }
1129 
1130 
1131 // loesche das Text-Attribut (muss beim Pool abgemeldet werden!)
DestroyAttr(SwTxtAttr * pAttr)1132 void SwTxtNode::DestroyAttr( SwTxtAttr* pAttr )
1133 {
1134 	if( pAttr )
1135 	{
1136 		// einige Sachen muessen vorm Loeschen der "Format-Attribute" erfolgen
1137 		SwDoc* pDoc = GetDoc();
1138 		sal_uInt16 nDelMsg = 0;
1139 		switch( pAttr->Which() )
1140 		{
1141 		case RES_TXTATR_FLYCNT:
1142 			{
1143 				// siehe auch die Anmerkung "Loeschen von Formaten
1144 				// zeichengebundener Frames" in fesh.cxx, SwFEShell::DelFmt()
1145 				SwFrmFmt* pFmt = pAttr->GetFlyCnt().GetFrmFmt();
1146 				if( pFmt )		// vom Undo auf 0 gesetzt ??
1147 					pDoc->DelLayoutFmt( (SwFlyFrmFmt*)pFmt );
1148 			}
1149 			break;
1150 
1151         case RES_CHRATR_HIDDEN:
1152             SetCalcHiddenCharFlags();
1153             break;
1154 
1155 		case RES_TXTATR_FTN:
1156 			((SwTxtFtn*)pAttr)->SetStartNode( 0 );
1157 			nDelMsg = RES_FOOTNOTE_DELETED;
1158 			break;
1159 
1160 		case RES_TXTATR_FIELD:
1161 		case RES_TXTATR_ANNOTATION:
1162 		case RES_TXTATR_INPUTFIELD:
1163 			if( !pDoc->IsInDtor() )
1164 			{
1165 				// Wenn wir ein HiddenParaField sind, dann muessen wir
1166 				// ggf. fuer eine Neuberechnung des Visible-Flags sorgen.
1167 				const SwField* pFld = pAttr->GetFmtFld().GetField();
1168 
1169 				//JP 06-08-95: DDE-Felder bilden eine Ausnahme
1170 				ASSERT( RES_DDEFLD == pFld->GetTyp()->Which() ||
1171 						this == ((SwTxtFld*)pAttr)->GetpTxtNode(),
1172 						"Wo steht denn dieses Feld?" )
1173 
1174 				// bestimmte Felder mussen am Doc das Calculations-Flag updaten
1175 				switch( pFld->GetTyp()->Which() )
1176 				{
1177 				case RES_HIDDENPARAFLD:
1178                     SetCalcHiddenParaField();
1179 					// kein break !
1180 				case RES_DBSETNUMBERFLD:
1181 				case RES_GETEXPFLD:
1182 				case RES_DBFLD:
1183 				case RES_SETEXPFLD:
1184 				case RES_HIDDENTXTFLD:
1185 				case RES_DBNUMSETFLD:
1186 				case RES_DBNEXTSETFLD:
1187 					if( !pDoc->IsNewFldLst() && GetNodes().IsDocNodes() )
1188 						pDoc->InsDelFldInFldLst( sal_False, *(SwTxtFld*)pAttr );
1189 					break;
1190 				case RES_DDEFLD:
1191 					if( GetNodes().IsDocNodes() &&
1192 						((SwTxtFld*)pAttr)->GetpTxtNode() )
1193 						((SwDDEFieldType*)pFld->GetTyp())->DecRefCnt();
1194 					break;
1195 				case RES_POSTITFLD:
1196 					{
1197 						const_cast<SwFmtFld&>(pAttr->GetFmtFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pAttr)->GetFmtFld(), SWFMTFLD_REMOVED ) );
1198 						break;
1199 					}
1200 				}
1201 			}
1202 			nDelMsg = RES_FIELD_DELETED;
1203 			break;
1204 
1205 		case RES_TXTATR_TOXMARK:
1206             static_cast<SwTOXMark&>(pAttr->GetAttr()).InvalidateTOXMark();
1207 			break;
1208 
1209 		case RES_TXTATR_REFMARK:
1210 			nDelMsg = RES_REFMARK_DELETED;
1211 			break;
1212 
1213         case RES_TXTATR_META:
1214         case RES_TXTATR_METAFIELD:
1215             static_cast<SwTxtMeta*>(pAttr)->ChgTxtNode(0);
1216             break;
1217 
1218         default:
1219             break;
1220 		}
1221 
1222 		if( nDelMsg && !pDoc->IsInDtor() && GetNodes().IsDocNodes() )
1223 		{
1224 			SwPtrMsgPoolItem aMsgHint( nDelMsg, (void*)&pAttr->GetAttr() );
1225 			pDoc->GetUnoCallBack()->ModifyNotification( &aMsgHint, &aMsgHint );
1226 		}
1227 
1228         SwTxtAttr::Destroy( pAttr, pDoc->GetAttrPool() );
1229     }
1230 }
1231 
1232 /*************************************************************************
1233  *						SwTxtNode::Insert()
1234  *************************************************************************/
1235 
InsertItem(SfxPoolItem & rAttr,const xub_StrLen nStart,const xub_StrLen nEnd,const SetAttrMode nMode)1236 SwTxtAttr* SwTxtNode::InsertItem(
1237     SfxPoolItem& rAttr,
1238     const xub_StrLen nStart,
1239     const xub_StrLen nEnd,
1240     const SetAttrMode nMode )
1241 {
1242    // character attributes will be inserted as automatic styles:
1243     ASSERT( !isCHRATR(rAttr.Which()), "AUTOSTYLES - "
1244         "SwTxtNode::InsertItem should not be called with character attributes");
1245 
1246     SwTxtAttr *const pNew =
1247         MakeTxtAttr(
1248             *GetDoc(),
1249             rAttr,
1250             nStart,
1251             nEnd,
1252             (nMode & nsSetAttrMode::SETATTR_IS_COPY) ? COPY : NEW,
1253             this );
1254 
1255     if ( pNew )
1256     {
1257         const bool bSuccess( InsertHint( pNew, nMode ) );
1258         // N.B.: also check that the hint is actually in the hints array,
1259         // because hints of certain types may be merged after successful
1260         // insertion, and thus destroyed!
1261         if (!bSuccess || ( USHRT_MAX == m_pSwpHints->GetPos( pNew ) ))
1262         {
1263             return 0;
1264         }
1265     }
1266 
1267     return pNew;
1268 }
1269 
1270 // take ownership of pAttr; if insertion fails, delete pAttr
InsertHint(SwTxtAttr * const pAttr,const SetAttrMode nMode)1271 bool SwTxtNode::InsertHint( SwTxtAttr * const pAttr, const SetAttrMode nMode )
1272 {
1273     bool bHiddenPara = false;
1274 
1275     ASSERT( pAttr && *pAttr->GetStart() <= Len(), "StartIdx out of bounds!" );
1276     ASSERT( !pAttr->GetEnd() || (*pAttr->GetEnd() <= Len()),
1277             "EndIdx out of bounds!" );
1278 
1279     // translate from SetAttrMode to InsertMode (for hints with CH_TXTATR)
1280     const enum IDocumentContentOperations::InsertFlags nInsertFlags =
1281         (nMode & nsSetAttrMode::SETATTR_FORCEHINTEXPAND)
1282         ? static_cast<IDocumentContentOperations::InsertFlags>(
1283                 IDocumentContentOperations::INS_FORCEHINTEXPAND |
1284                 IDocumentContentOperations::INS_EMPTYEXPAND)
1285         : IDocumentContentOperations::INS_EMPTYEXPAND;
1286 
1287     // need this after TryInsertHint, when pAttr may be deleted
1288     const xub_StrLen nStart( *pAttr->GetStart() );
1289     const bool bDummyChar( pAttr->HasDummyChar() );
1290     if (bDummyChar)
1291     {
1292         sal_uInt16 nInsMode = nMode;
1293         switch( pAttr->Which() )
1294         {
1295         case RES_TXTATR_FLYCNT:
1296             {
1297                 SwTxtFlyCnt *pFly = (SwTxtFlyCnt *)pAttr;
1298                 SwFrmFmt* pFmt = pAttr->GetFlyCnt().GetFrmFmt();
1299                 if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) )
1300                 {
1301                     // Wir muessen zuerst einfuegen, da in SetAnchor()
1302                     // dem FlyFrm GetStart() uebermittelt wird.
1303                     //JP 11.05.98: falls das Anker-Attribut schon richtig
1304                     // gesetzt ist, dann korrigiere dieses nach dem Einfuegen
1305                     // des Zeichens. Sonst muesste das immer  ausserhalb
1306                     // erfolgen (Fehleranfaellig !)
1307                     const SwFmtAnchor* pAnchor = 0;
1308                     pFmt->GetItemState( RES_ANCHOR, sal_False,
1309                         (const SfxPoolItem**)&pAnchor );
1310 
1311                     SwIndex aIdx( this, *pAttr->GetStart() );
1312                     const sal_Unicode c = GetCharOfTxtAttr(*pAttr);
1313                     InsertText( c, aIdx, nInsertFlags );
1314                     nInsMode |= nsSetAttrMode::SETATTR_NOTXTATRCHR;
1315 
1316                     if (pAnchor &&
1317                         (FLY_AS_CHAR == pAnchor->GetAnchorId()) &&
1318                         pAnchor->GetCntntAnchor() &&
1319                         pAnchor->GetCntntAnchor()->nNode == *this &&
1320                         pAnchor->GetCntntAnchor()->nContent == aIdx )
1321                     {
1322                         const_cast<SwIndex&>(
1323                             pAnchor->GetCntntAnchor()->nContent)--;
1324                     }
1325                 }
1326                 pFly->SetAnchor( this );
1327 
1328                 // Format-Pointer kann sich im SetAnchor geaendert haben!
1329                 // (Kopieren in andere Docs!)
1330                 pFmt = pAttr->GetFlyCnt().GetFrmFmt();
1331                 SwDoc *pDoc = pFmt->GetDoc();
1332 
1333                 // OD 26.06.2003 #108784# - allow drawing objects in header/footer.
1334                 // But don't allow control objects in header/footer
1335                 if( RES_DRAWFRMFMT == pFmt->Which() &&
1336                     pDoc->IsInHeaderFooter( pFmt->GetAnchor().GetCntntAnchor()->nNode ) )
1337                 {
1338                     SwDrawContact* pDrawContact =
1339                         static_cast<SwDrawContact*>(pFmt->FindContactObj());
1340                     if ( pDrawContact &&
1341                          pDrawContact->GetMaster() &&
1342                          ::CheckControlLayer( pDrawContact->GetMaster() ) )
1343                     {
1344                         // das soll nicht meoglich sein; hier verhindern
1345                         // Der Dtor des TxtHints loescht nicht das Zeichen.
1346                         // Wenn ein CH_TXTATR_.. vorliegt, dann muss man
1347                         // dieses explizit loeschen
1348                         if( nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode )
1349                         {
1350                             // loesche das Zeichen aus dem String !
1351                             ASSERT( ( CH_TXTATR_BREAKWORD ==
1352                                         m_Text.GetChar(*pAttr->GetStart() ) ||
1353                                       CH_TXTATR_INWORD ==
1354                                         m_Text.GetChar(*pAttr->GetStart())),
1355                                     "where is my attribute character?" );
1356                             m_Text.Erase( *pAttr->GetStart(), 1 );
1357                             // Indizies Updaten
1358                             SwIndex aTmpIdx( this, *pAttr->GetStart() );
1359                             Update( aTmpIdx, 1, sal_True );
1360                         }
1361                         // do not record deletion of Format!
1362                         ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
1363                         DestroyAttr( pAttr );
1364                         return false;
1365                     }
1366                 }
1367                 break;
1368             }
1369 
1370         case RES_TXTATR_FTN :
1371             {
1372                 // Fussnoten, man kommt an alles irgendwie heran.
1373                 // CntntNode erzeugen und in die Inserts-Section stellen
1374                 SwDoc *pDoc = GetDoc();
1375                 SwNodes &rNodes = pDoc->GetNodes();
1376 
1377                 // FussNote in nicht Content-/Redline-Bereich einfuegen ??
1378                 if( StartOfSectionIndex() < rNodes.GetEndOfAutotext().GetIndex() )
1379                 {
1380                     // das soll nicht meoglich sein; hier verhindern
1381                     // Der Dtor des TxtHints loescht nicht das Zeichen.
1382                     // Wenn ein CH_TXTATR_.. vorliegt, dann muss man
1383                     // dieses explizit loeschen
1384                     if( nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode )
1385                     {
1386                         // loesche das Zeichen aus dem String !
1387                         ASSERT( ( CH_TXTATR_BREAKWORD ==
1388                             m_Text.GetChar(*pAttr->GetStart() ) ||
1389                             CH_TXTATR_INWORD ==
1390                             m_Text.GetChar(*pAttr->GetStart())),
1391                             "where is my attribute character?" );
1392                         m_Text.Erase( *pAttr->GetStart(), 1 );
1393                         // Indizies Updaten
1394                         SwIndex aTmpIdx( this, *pAttr->GetStart() );
1395                         Update( aTmpIdx, 1, sal_True );
1396                     }
1397                     DestroyAttr( pAttr );
1398                     return false;
1399                 }
1400 
1401                 // wird eine neue Fussnote eingefuegt ??
1402                 sal_Bool bNewFtn = 0 == ((SwTxtFtn*)pAttr)->GetStartNode();
1403                 if( bNewFtn )
1404                 {
1405                     ((SwTxtFtn*)pAttr)->MakeNewTextSection( GetNodes() );
1406                     SwRegHistory* pHist = GetpSwpHints()
1407                         ? GetpSwpHints()->GetHistory() : 0;
1408                     if( pHist )
1409                         pHist->ChangeNodeIndex( GetIndex() );
1410                 }
1411                 else if ( !GetpSwpHints() || !GetpSwpHints()->IsInSplitNode() )
1412                 {
1413                     // loesche alle Frames der Section, auf die der StartNode zeigt
1414                     sal_uLong nSttIdx =
1415                         ((SwTxtFtn*)pAttr)->GetStartNode()->GetIndex();
1416                     sal_uLong nEndIdx = rNodes[ nSttIdx++ ]->EndOfSectionIndex();
1417                     SwCntntNode* pCNd;
1418                     for( ; nSttIdx < nEndIdx; ++nSttIdx )
1419                         if( 0 != ( pCNd = rNodes[ nSttIdx ]->GetCntntNode() ))
1420                             pCNd->DelFrms();
1421                 }
1422 
1423                 if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) )
1424                 {
1425                     // Wir muessen zuerst einfuegen, da sonst gleiche Indizes
1426                     // entstehen koennen und das Attribut im _SortArr_ am
1427                     // Dokument nicht eingetrage wird.
1428                     SwIndex aNdIdx( this, *pAttr->GetStart() );
1429                     const sal_Unicode c = GetCharOfTxtAttr(*pAttr);
1430                     InsertText( c, aNdIdx, nInsertFlags );
1431                     nInsMode |= nsSetAttrMode::SETATTR_NOTXTATRCHR;
1432                 }
1433 
1434                 // Wir tragen uns am FtnIdx-Array des Docs ein ...
1435                 SwTxtFtn* pTxtFtn = 0;
1436                 if( !bNewFtn )
1437                 {
1438                     // eine alte Ftn wird umgehaengt (z.B. SplitNode)
1439                     for( sal_uInt16 n = 0; n < pDoc->GetFtnIdxs().Count(); ++n )
1440                         if( pAttr == pDoc->GetFtnIdxs()[n] )
1441                         {
1442                             // neuen Index zuweisen, dafuer aus dem SortArray
1443                             // loeschen und neu eintragen
1444                             pTxtFtn = pDoc->GetFtnIdxs()[n];
1445                             pDoc->GetFtnIdxs().Remove( n );
1446                             break;
1447                         }
1448                         // wenn ueber Undo der StartNode gesetzt wurde, kann
1449                         // der Index noch gar nicht in der Verwaltung stehen !!
1450                 }
1451                 if( !pTxtFtn )
1452                     pTxtFtn = (SwTxtFtn*)pAttr;
1453 
1454                 // fuers Update der Nummern und zum Sortieren
1455                 // muss der Node gesetzt sein.
1456                 ((SwTxtFtn*)pAttr)->ChgTxtNode( this );
1457 
1458                 // FussNote im Redline-Bereich NICHT ins FtnArray einfuegen!
1459                 if( StartOfSectionIndex() > rNodes.GetEndOfRedlines().GetIndex() )
1460                 {
1461 #ifdef DBG_UTIL
1462                     const sal_Bool bSuccess =
1463 #endif
1464                         pDoc->GetFtnIdxs().Insert( pTxtFtn );
1465 #ifdef DBG_UTIL
1466                     ASSERT( bSuccess, "FtnIdx nicht eingetragen." );
1467 #endif
1468                 }
1469                 SwNodeIndex aTmpIndex( *this );
1470                 pDoc->GetFtnIdxs().UpdateFtn( aTmpIndex);
1471                 ((SwTxtFtn*)pAttr)->SetSeqRefNo();
1472             }
1473             break;
1474 
1475             case RES_TXTATR_FIELD:
1476                 {
1477                     // fuer HiddenParaFields Benachrichtigungsmechanismus
1478                     // anwerfen
1479                     if( RES_HIDDENPARAFLD == pAttr->GetFmtFld().GetField()->GetTyp()->Which() )
1480                     {
1481                         bHiddenPara = true;
1482                     }
1483                 }
1484                 break;
1485 
1486         }
1487         // Fuer SwTxtHints ohne Endindex werden CH_TXTATR_..
1488         // eingefuegt, aStart muss danach um einen zurueckgesetzt werden.
1489         // Wenn wir im SwTxtNode::Copy stehen, so wurde das Zeichen bereits
1490         // mitkopiert. In solchem Fall ist SETATTR_NOTXTATRCHR angegeben worden.
1491         if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nInsMode) )
1492         {
1493             SwIndex aIdx( this, *pAttr->GetStart() );
1494             InsertText( GetCharOfTxtAttr(*pAttr), aIdx, nInsertFlags );
1495 
1496             // adjust end of hint to account for inserted CH_TXTATR
1497             xub_StrLen * const pEnd(pAttr->GetEnd());
1498             if (pEnd)
1499             {
1500                 *pEnd = *pEnd + 1;
1501             }
1502         }
1503     }
1504 
1505     // handle attributes which provide content
1506     xub_StrLen nEnd = nStart;
1507     bool bInputFieldStartCharInserted = false;
1508     bool bInputFieldEndCharInserted = false;
1509     const bool bHasContent( pAttr->HasContent() );
1510     if ( bHasContent )
1511     {
1512         switch( pAttr->Which() )
1513         {
1514         case RES_TXTATR_INPUTFIELD:
1515             {
1516                 SwTxtInputFld* pTxtInputFld = dynamic_cast<SwTxtInputFld*>(pAttr);
1517                 if ( pTxtInputFld )
1518                 {
1519                     if( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nMode) )
1520                     {
1521                         SwIndex aIdx( this, *pAttr->GetStart() );
1522                         InsertText( CH_TXT_ATR_INPUTFIELDSTART, aIdx, nInsertFlags );
1523                         const String aContent = pTxtInputFld->GetFieldContent();
1524                         InsertText( aContent, aIdx, nInsertFlags );
1525                         InsertText( CH_TXT_ATR_INPUTFIELDEND, aIdx, nInsertFlags );
1526 
1527                         xub_StrLen * const pEnd(pAttr->GetEnd());
1528                         ASSERT( pEnd != NULL, "<SwTxtNode::InsertHint(..)> - missing end of RES_TXTATR_INPUTFIELD!" );
1529                         if ( pEnd != NULL )
1530                         {
1531                             *pEnd = *pEnd + 2 + aContent.Len();
1532                             nEnd = *pEnd;
1533                         }
1534                     }
1535                     else
1536                     {
1537                         // assure that CH_TXT_ATR_INPUTFIELDSTART and CH_TXT_ATR_INPUTFIELDEND are inserted.
1538                         if ( m_Text.GetChar( *(pAttr->GetStart()) ) != CH_TXT_ATR_INPUTFIELDSTART )
1539                         {
1540                             SwIndex aIdx( this, *pAttr->GetStart() );
1541                             InsertText( CH_TXT_ATR_INPUTFIELDSTART, aIdx, nInsertFlags );
1542                             bInputFieldStartCharInserted = true;
1543                             xub_StrLen * const pEnd(pAttr->GetEnd());
1544                             ASSERT( pEnd != NULL, "<SwTxtNode::InsertHint(..)> - missing end of RES_TXTATR_INPUTFIELD!" );
1545                             if ( pEnd != NULL )
1546                             {
1547                                 *pEnd = *pEnd + 1;
1548                                 nEnd = *pEnd;
1549                             }
1550                         }
1551 
1552                         xub_StrLen * const pEnd(pAttr->GetEnd());
1553                         ASSERT( pEnd != NULL, "<SwTxtNode::InsertHint(..)> - missing end of RES_TXTATR_INPUTFIELD!" );
1554                         if ( pEnd != NULL
1555                              && m_Text.GetChar( *(pEnd) - 1 ) != CH_TXT_ATR_INPUTFIELDEND )
1556                         {
1557                             SwIndex aIdx( this, *(pEnd) );
1558                             InsertText( CH_TXT_ATR_INPUTFIELDEND, aIdx, nInsertFlags );
1559                             bInputFieldEndCharInserted = true;
1560                             *pEnd = *pEnd + 1;
1561                             nEnd = *pEnd;
1562                         }
1563                     }
1564                 }
1565             }
1566             break;
1567         default:
1568             break;
1569         }
1570     }
1571 
1572     GetOrCreateSwpHints();
1573 
1574     // handle overlap with an existing InputField
1575     bool bInsertHint = true;
1576     {
1577         const SwTxtInputFld* pTxtInputFld = GetOverlappingInputFld( *pAttr );
1578         if ( pTxtInputFld != NULL )
1579         {
1580             if ( pAttr->End() == NULL )
1581             {
1582                 bInsertHint = false;
1583             }
1584             else
1585             {
1586                 if ( *(pAttr->GetStart()) > *(pTxtInputFld->GetStart()) )
1587                 {
1588                     *(pAttr->GetStart()) = *(pTxtInputFld->GetStart());
1589                 }
1590                 if ( *(pAttr->End()) < *(pTxtInputFld->End()) )
1591                 {
1592                     *(pAttr->GetEnd()) = *(pTxtInputFld->End());
1593                 }
1594             }
1595         }
1596     }
1597 
1598     // 4263: AttrInsert durch TextInsert => kein Adjust
1599     const bool bRet = bInsertHint
1600                       ? m_pSwpHints->TryInsertHint( pAttr, *this, nMode )
1601                       : false;
1602 
1603     if ( !bRet )
1604     {
1605         if ( bDummyChar
1606              && !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nMode) )
1607         {
1608             // undo insertion of dummy character
1609             // N.B. cannot insert the dummy character after inserting the hint,
1610             // because if the hint has no extent it will be moved in InsertText,
1611             // resulting in infinite recursion
1612             ASSERT( ( CH_TXTATR_BREAKWORD == m_Text.GetChar(nStart) ||
1613                 CH_TXTATR_INWORD    == m_Text.GetChar(nStart) ),
1614                 "where is my attribute character?" );
1615             SwIndex aIdx( this, nStart );
1616             EraseText( aIdx, 1 );
1617         }
1618 
1619         if ( bHasContent )
1620         {
1621             if ( !(nsSetAttrMode::SETATTR_NOTXTATRCHR & nMode)
1622                  && (nEnd - nStart) > 0 )
1623             {
1624                 SwIndex aIdx( this, nStart );
1625                 EraseText( aIdx, (nEnd - nStart) );
1626             }
1627             else
1628             {
1629                 if ( bInputFieldEndCharInserted
1630                      && (nEnd - nStart) > 0 )
1631                 {
1632                     SwIndex aIdx( this, nEnd - 1 );
1633                     EraseText( aIdx, 1 );
1634                 }
1635 
1636                 if ( bInputFieldStartCharInserted )
1637                 {
1638                     SwIndex aIdx( this, nStart );
1639                     EraseText( aIdx, 1 );
1640                 }
1641             }
1642         }
1643     }
1644 
1645     if ( bHiddenPara )
1646     {
1647         SetCalcHiddenParaField();
1648     }
1649 
1650     return bRet;
1651 }
1652 
1653 
1654 /*************************************************************************
1655  *                        SwTxtNode::DeleteAttribute()
1656  *************************************************************************/
1657 
DeleteAttribute(SwTxtAttr * const pAttr)1658 void SwTxtNode::DeleteAttribute( SwTxtAttr * const pAttr )
1659 {
1660     if ( !HasHints() )
1661     {
1662         ASSERT(false, "DeleteAttribute called, but text node without hints?");
1663         return;
1664     }
1665 
1666     if ( pAttr->HasDummyChar() )
1667     {
1668         // Unbedingt Copy-konstruieren!
1669         const SwIndex aIdx( this, *pAttr->GetStart() );
1670         // erase the CH_TXTATR, which will also delete pAttr
1671         EraseText( aIdx, 1 );
1672     }
1673     else if ( pAttr->HasContent() )
1674     {
1675         const SwIndex aIdx( this, *pAttr->GetStart() );
1676         ASSERT( pAttr->End() != NULL, "<SwTxtNode::DeleteAttribute(..)> - missing End() at <SwTxtAttr> instance which has content" );
1677         EraseText( aIdx, *pAttr->End() - *pAttr->GetStart() );
1678     }
1679     else
1680     {
1681         // create MsgHint before start/end become invalid
1682         SwUpdateAttr aHint(
1683             *pAttr->GetStart(),
1684             *pAttr->GetEnd(),
1685             pAttr->Which());
1686 
1687         m_pSwpHints->Delete( pAttr );
1688         SwTxtAttr::Destroy( pAttr, GetDoc()->GetAttrPool() );
1689         NotifyClients( 0, &aHint );
1690 
1691         TryDeleteSwpHints();
1692     }
1693 }
1694 
1695 /*************************************************************************
1696  *                        SwTxtNode::DeleteAttributes()
1697  *************************************************************************/
1698 
1699 //FIXME: this does NOT respect SORT NUMBER (for CHARFMT)!
DeleteAttributes(const sal_uInt16 nWhich,const xub_StrLen nStart,const xub_StrLen nEnd)1700 void SwTxtNode::DeleteAttributes(
1701     const sal_uInt16 nWhich,
1702     const xub_StrLen nStart,
1703     const xub_StrLen nEnd )
1704 {
1705     if ( !HasHints() )
1706         return;
1707 
1708     for ( sal_uInt16 nPos = 0; m_pSwpHints && nPos < m_pSwpHints->Count(); nPos++ )
1709     {
1710         SwTxtAttr * const pTxtHt = m_pSwpHints->GetTextHint( nPos );
1711         const xub_StrLen nHintStart = *(pTxtHt->GetStart());
1712         if (nStart < nHintStart)
1713         {
1714             break; // sorted by start
1715         }
1716         else if ( (nStart == nHintStart) && (nWhich == pTxtHt->Which()) )
1717         {
1718             if ( nWhich == RES_CHRATR_HIDDEN  )
1719             {
1720                 ASSERT(false, "hey, that's a CHRATR! how did that get in?");
1721                 SetCalcHiddenCharFlags();
1722             }
1723             else if ( nWhich == RES_TXTATR_CHARFMT )
1724             {
1725                 // Check if character format contains hidden attribute:
1726                 const SwCharFmt* pFmt = pTxtHt->GetCharFmt().GetCharFmt();
1727                 const SfxPoolItem* pItem;
1728                 if ( SFX_ITEM_SET == pFmt->GetItemState( RES_CHRATR_HIDDEN, sal_True, &pItem ) )
1729                     SetCalcHiddenCharFlags();
1730             }
1731             // Recalc hidden flags if necessary
1732             else if ( nWhich == RES_TXTATR_AUTOFMT )
1733             {
1734                 // Check if auto style contains hidden attribute:
1735                 const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pTxtHt, RES_CHRATR_HIDDEN );
1736                 if ( pHiddenItem )
1737                     SetCalcHiddenCharFlags();
1738             }
1739 
1740             xub_StrLen const * const pEndIdx = pTxtHt->GetEnd();
1741 
1742             if ( pTxtHt->HasDummyChar() )
1743             {
1744                 // Unbedingt Copy-konstruieren!
1745                 const SwIndex aIdx( this, nStart );
1746                 // erase the CH_TXTATR, which will also delete pTxtHt
1747                 EraseText( aIdx, 1 );
1748             }
1749             else if ( pTxtHt->HasContent() )
1750             {
1751                 const SwIndex aIdx( this, nStart );
1752                 ASSERT( pTxtHt->End() != NULL, "<SwTxtNode::DeleteAttributes(..)> - missing End() at <SwTxtAttr> instance which has content" );
1753                 EraseText( aIdx, *pTxtHt->End() - nStart );
1754             }
1755             else if( *pEndIdx == nEnd )
1756             {
1757                 // den MsgHint jetzt fuettern, weil gleich sind
1758                 // Start und End weg.
1759                 // Das CalcVisibleFlag bei HiddenParaFields entfaellt,
1760                 // da dies das Feld im Dtor selbst erledigt.
1761                 SwUpdateAttr aHint(
1762                     nStart,
1763                     *pEndIdx,
1764                     nWhich);
1765 
1766                 m_pSwpHints->DeleteAtPos( nPos );    // gefunden, loeschen,
1767                 SwTxtAttr::Destroy( pTxtHt, GetDoc()->GetAttrPool() );
1768                 NotifyClients( 0, &aHint );
1769             }
1770         }
1771     }
1772     TryDeleteSwpHints();
1773 }
1774 
1775 /*************************************************************************
1776  *						SwTxtNode::DelSoftHyph()
1777  *************************************************************************/
1778 
DelSoftHyph(const xub_StrLen nStt,const xub_StrLen nEnd)1779 void SwTxtNode::DelSoftHyph( const xub_StrLen nStt, const xub_StrLen nEnd )
1780 {
1781     xub_StrLen nFndPos = nStt, nEndPos = nEnd;
1782 	while( STRING_NOTFOUND !=
1783             ( nFndPos = m_Text.Search( CHAR_SOFTHYPHEN, nFndPos )) &&
1784 			nFndPos < nEndPos )
1785 	{
1786 		const SwIndex aIdx( this, nFndPos );
1787         EraseText( aIdx, 1 );
1788 		--nEndPos;
1789 	}
1790 }
1791 
1792 //Modify here for #119405, by easyfan, 2012-05-24
1793 //In MS Word, the font underline setting of the paragraph end position won't affect the formatting of numbering, escapement, etc, so we ignore them
lcl_IsIgnoredCharFmtForNumbering(const sal_uInt16 nWhich)1794 bool lcl_IsIgnoredCharFmtForNumbering(const sal_uInt16 nWhich)
1795 {
1796 	return (nWhich == RES_CHRATR_UNDERLINE || nWhich == RES_CHRATR_ESCAPEMENT);
1797 }
1798 
1799 //In MS Word, following properties of the paragraph end position won't affect the formatting of bullets, so we ignore them:
1800 //Font underline;
1801 //Font Italic of Western, CJK and CTL;
1802 //Font Bold of Wertern, CJK and CTL;
lcl_IsIgnoredCharFmtForBullets(const sal_uInt16 nWhich)1803 bool lcl_IsIgnoredCharFmtForBullets(const sal_uInt16 nWhich)
1804 {
1805 	return (nWhich == RES_CHRATR_UNDERLINE || nWhich == RES_CHRATR_POSTURE || nWhich == RES_CHRATR_WEIGHT
1806 		|| nWhich == RES_CHRATR_CJK_POSTURE || nWhich == RES_CHRATR_CJK_WEIGHT
1807 		|| nWhich == RES_CHRATR_CTL_POSTURE || nWhich == RES_CHRATR_CTL_WEIGHT);
1808 }
1809 
1810 //Condition for expanding char set to character style of specified number rule level:
1811 //The item inside the set should not conflict to any exist and non-default item inside paragraph properties set (SwCntntNode::SwPAttrSet);
1812 //The node should have applied a number rule;
1813 //The node should be counted in a list, if not, make it to be;
1814 //The item should not conflict to any exist and non-default item inside the character of specified number rule level;
1815 //The item should not be ignored depend on the exact number rule type;
TryCharSetExpandToNum(const SfxItemSet & aCharSet)1816 bool SwTxtNode::TryCharSetExpandToNum(const SfxItemSet& aCharSet)
1817 {
1818 	bool bRet = false;
1819 	SfxItemIter aIter( aCharSet );
1820         const SfxPoolItem* pItem = aIter.FirstItem();
1821         const sal_uInt16 nWhich = pItem->Which();
1822 
1823 	const SfxPoolItem& rInnerItem = GetAttr(nWhich,false);
1824 
1825 	if (!IsDefaultItem(&rInnerItem) &&  !IsInvalidItem(&rInnerItem))
1826 		return bRet;
1827 
1828 	if ( !IsInList() && GetNumRule() && GetListId().Len() > 0 )
1829 	{
1830 		return bRet;
1831 	}
1832 
1833 	SwNumRule* pCurrNum = GetNumRule(false);
1834 
1835 	int nLevel = GetActualListLevel();
1836 
1837 	if (nLevel != -1 && pCurrNum)
1838 	{
1839 		const SwNumFmt* pCurrNumFmt = pCurrNum->GetNumFmt(static_cast<sal_uInt16>(nLevel));
1840 		if (pCurrNumFmt)
1841 		{
1842 			if (pCurrNumFmt->IsItemize() && lcl_IsIgnoredCharFmtForBullets(nWhich))
1843 				return bRet;
1844 			if (pCurrNumFmt->IsEnumeration() && lcl_IsIgnoredCharFmtForNumbering(nWhich))
1845 				return bRet;
1846 			SwCharFmt* pCurrCharFmt =pCurrNumFmt->GetCharFmt();
1847 
1848 			if (pCurrCharFmt && pCurrCharFmt->GetItemState(nWhich,false) != SFX_ITEM_SET)
1849 			{
1850 				pCurrCharFmt->SetFmtAttr(*pItem);
1851 				SwNumFmt aNewNumFmt(*pCurrNumFmt);
1852 				aNewNumFmt.SetCharFmt(pCurrCharFmt);
1853 				pCurrNum->Set(nLevel,aNewNumFmt);
1854 				bRet = true;
1855 			}
1856 		}
1857 	}
1858 
1859 
1860 	return bRet;
1861 }
1862 //End of modification, by easyfan
1863 
1864 // setze diese Attribute am TextNode. Wird der gesamte Bereich umspannt,
1865 // dann setze sie nur im AutoAttrSet (SwCntntNode:: SetAttr)
SetAttr(const SfxItemSet & rSet,const xub_StrLen nStt,const xub_StrLen nEnd,const SetAttrMode nMode)1866 sal_Bool SwTxtNode::SetAttr(
1867     const SfxItemSet& rSet,
1868     const xub_StrLen nStt,
1869     const xub_StrLen nEnd,
1870     const SetAttrMode nMode )
1871 {
1872 	if( !rSet.Count() )
1873 		return sal_False;
1874 
1875 	// teil die Sets auf (fuer Selektion in Nodes)
1876 	const SfxItemSet* pSet = &rSet;
1877 	SfxItemSet aTxtSet( *rSet.GetPool(), RES_TXTATR_BEGIN, RES_TXTATR_END-1 );
1878 
1879 	// gesamter Bereich
1880     if ( !nStt && (nEnd == m_Text.Len()) &&
1881          !(nMode & nsSetAttrMode::SETATTR_NOFORMATATTR ) )
1882 	{
1883 		// sind am Node schon Zeichenvorlagen gesetzt, muss man diese Attribute
1884 		// (rSet) immer als TextAttribute setzen, damit sie angezeigt werden.
1885 		int bHasCharFmts = sal_False;
1886         if ( HasHints() )
1887         {
1888             for ( sal_uInt16 n = 0; n < m_pSwpHints->Count(); ++n )
1889             {
1890                 if ( (*m_pSwpHints)[ n ]->IsCharFmtAttr() )
1891                 {
1892 					bHasCharFmts = sal_True;
1893 					break;
1894                 }
1895             }
1896         }
1897 
1898 		if( !bHasCharFmts )
1899 		{
1900             aTxtSet.Put( rSet );
1901             // If there are any character attributes in rSet,
1902             // we want to set them at the paragraph:
1903 			if( aTxtSet.Count() != rSet.Count() )
1904 			{
1905                 sal_Bool bRet = SetAttr( rSet );
1906           		if( !aTxtSet.Count() )
1907 		    		return bRet;
1908 			}
1909 
1910             // check for auto style:
1911             const SfxPoolItem* pItem;
1912             const bool bAutoStyle = SFX_ITEM_SET == aTxtSet.GetItemState( RES_TXTATR_AUTOFMT, sal_False, &pItem );
1913             if ( bAutoStyle )
1914             {
1915                 boost::shared_ptr<SfxItemSet> pAutoStyleSet = static_cast<const SwFmtAutoFmt*>(pItem)->GetStyleHandle();
1916                 sal_Bool bRet = SetAttr( *pAutoStyleSet );
1917           		if( 1 == aTxtSet.Count() )
1918 		    		return bRet;
1919             }
1920 
1921             // Continue with the text attributes:
1922 			pSet = &aTxtSet;
1923 		}
1924 	}
1925 
1926     GetOrCreateSwpHints();
1927 
1928     SfxItemSet aCharSet( *rSet.GetPool(), aCharAutoFmtSetRange );
1929 
1930     sal_uInt16 nCount = 0;
1931 	SfxItemIter aIter( *pSet );
1932 	const SfxPoolItem* pItem = aIter.GetCurItem();
1933 
1934     do
1935     {
1936         if ( pItem && (reinterpret_cast<SfxPoolItem*>(-1) != pItem))
1937         {
1938             const sal_uInt16 nWhich = pItem->Which();
1939             ASSERT( isCHRATR(nWhich) || isTXTATR(nWhich),
1940                     "SwTxtNode::SetAttr(): unknown attribute" );
1941             if ( isCHRATR(nWhich) || isTXTATR(nWhich) )
1942             {
1943                 if ((RES_TXTATR_CHARFMT == nWhich) &&
1944                     (GetDoc()->GetDfltCharFmt() ==
1945                      static_cast<const SwFmtCharFmt*>(pItem)->GetCharFmt()))
1946                 {
1947                     SwIndex aIndex( this, nStt );
1948                     RstTxtAttr( aIndex, nEnd - nStt, RES_TXTATR_CHARFMT, 0 );
1949                     DontExpandFmt( aIndex );
1950                 }
1951                 else
1952                 {
1953                     if (isCHRATR(nWhich) ||
1954                         (RES_TXTATR_UNKNOWN_CONTAINER == nWhich))
1955                     {
1956                         aCharSet.Put( *pItem );
1957                     }
1958                     else
1959                     {
1960 
1961                         SwTxtAttr *const pNew = MakeTxtAttr( *GetDoc(),
1962                                 const_cast<SfxPoolItem&>(*pItem), nStt, nEnd );
1963                         if ( pNew )
1964                         {
1965                             if ( nEnd != nStt && !pNew->GetEnd() )
1966                             {
1967                                 ASSERT(false,
1968                                     "Attribut without end, but area marked");
1969                                 DestroyAttr( pNew ); // do not insert
1970                             }
1971                             else if ( InsertHint( pNew, nMode ) )
1972                             {
1973                                 ++nCount;
1974                             }
1975                         }
1976                     }
1977                 }
1978             }
1979         }
1980         if ( aIter.IsAtEnd() )
1981             break;
1982         pItem = aIter.NextItem();
1983     } while( true );
1984 
1985     if ( aCharSet.Count() )
1986     {
1987         SwTxtAttr* pTmpNew = MakeTxtAttr( *GetDoc(), aCharSet, nStt, nEnd );
1988         if ( InsertHint( pTmpNew, nMode ) )
1989         {
1990             ++nCount;
1991         }
1992     }
1993 
1994     TryDeleteSwpHints();
1995 
1996 	return nCount ? sal_True : sal_False;
1997 }
1998 
lcl_MergeAttr(SfxItemSet & rSet,const SfxPoolItem & rAttr)1999 void lcl_MergeAttr( SfxItemSet& rSet, const SfxPoolItem& rAttr )
2000 {
2001     if ( RES_TXTATR_AUTOFMT == rAttr.Which() )
2002 	{
2003         const SfxItemSet* pCFSet = CharFmt::GetItemSet( rAttr );
2004         if ( !pCFSet )
2005             return;
2006         SfxWhichIter aIter( *pCFSet );
2007         sal_uInt16 nWhich = aIter.FirstWhich();
2008         while( nWhich )
2009         {
2010             if( ( nWhich < RES_CHRATR_END ||
2011                   RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) &&
2012                 ( SFX_ITEM_SET == pCFSet->GetItemState( nWhich, sal_True ) ) )
2013                 rSet.Put( pCFSet->Get( nWhich ) );
2014             nWhich = aIter.NextWhich();
2015         }
2016     }
2017     else
2018     	rSet.Put( rAttr );
2019 }
2020 
lcl_MergeAttr_ExpandChrFmt(SfxItemSet & rSet,const SfxPoolItem & rAttr)2021 void lcl_MergeAttr_ExpandChrFmt( SfxItemSet& rSet, const SfxPoolItem& rAttr )
2022 {
2023 	if( RES_TXTATR_CHARFMT == rAttr.Which() ||
2024 		RES_TXTATR_INETFMT == rAttr.Which() ||
2025         RES_TXTATR_AUTOFMT == rAttr.Which() )
2026 	{
2027         const SfxItemSet* pCFSet = CharFmt::GetItemSet( rAttr );
2028 
2029         if ( pCFSet )
2030         {
2031             SfxWhichIter aIter( *pCFSet );
2032             sal_uInt16 nWhich = aIter.FirstWhich();
2033             while( nWhich )
2034             {
2035                 if( ( nWhich < RES_CHRATR_END ||
2036                       ( RES_TXTATR_AUTOFMT == rAttr.Which() && RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) ) &&
2037                     ( SFX_ITEM_SET == pCFSet->GetItemState( nWhich, sal_True ) ) )
2038                     rSet.Put( pCFSet->Get( nWhich ) );
2039                 nWhich = aIter.NextWhich();
2040             }
2041         }
2042     }
2043 
2044 	// aufnehmen als MergeWert (falls noch nicht gesetzt neu setzen!)
2045 
2046 /* wenn mehrere Attribute ueberlappen gewinnt der letze !!
2047  z.B
2048 			1234567890123456789
2049 			  |------------| 		Font1
2050 				 |------|			Font2
2051 					^  ^
2052 					|--|		Abfragebereich: -> Gueltig ist Font2
2053 */
2054     rSet.Put( rAttr );
2055 }
2056 
2057 struct SwPoolItemEndPair
2058 {
2059 public:
2060     const SfxPoolItem* mpItem;
2061     xub_StrLen mnEndPos;
2062 
SwPoolItemEndPairSwPoolItemEndPair2063     SwPoolItemEndPair() : mpItem( 0 ), mnEndPos( 0 ) {};
2064 };
2065 
2066 // --> OD 2008-01-16 #newlistlevelattrs#
lcl_MergeListLevelIndentAsLRSpaceItem(const SwTxtNode & rTxtNode,SfxItemSet & rSet)2067 void lcl_MergeListLevelIndentAsLRSpaceItem( const SwTxtNode& rTxtNode,
2068                                             SfxItemSet& rSet )
2069 {
2070     if ( rTxtNode.AreListLevelIndentsApplicable() )
2071     {
2072         const SwNumRule* pRule = rTxtNode.GetNumRule();
2073         if ( pRule && rTxtNode.GetActualListLevel() >= 0 )
2074         {
2075             const SwNumFmt& rFmt = pRule->Get(static_cast<sal_uInt16>(rTxtNode.GetActualListLevel()));
2076             if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
2077             {
2078                 SvxLRSpaceItem aLR( RES_LR_SPACE );
2079                 aLR.SetTxtLeft( rFmt.GetIndentAt() );
2080                 aLR.SetTxtFirstLineOfst( static_cast<short>(rFmt.GetFirstLineIndent()) );
2081                 rSet.Put( aLR );
2082             }
2083         }
2084     }
2085 }
2086 
2087 // erfrage die Attribute vom TextNode ueber den Bereich
2088 // --> OD 2008-01-16 #newlistlevelattrs#
GetAttr(SfxItemSet & rSet,xub_StrLen nStt,xub_StrLen nEnd,sal_Bool bOnlyTxtAttr,sal_Bool bGetFromChrFmt,const bool bMergeIndentValuesOfNumRule) const2089 sal_Bool SwTxtNode::GetAttr( SfxItemSet& rSet, xub_StrLen nStt, xub_StrLen nEnd,
2090                          sal_Bool bOnlyTxtAttr, sal_Bool bGetFromChrFmt,
2091                          const bool bMergeIndentValuesOfNumRule ) const
2092 {
2093     if( HasHints() )
2094     {
2095 		/* stelle erstmal fest, welche Text-Attribut in dem Bereich gueltig
2096 		 * sind. Dabei gibt es folgende Faelle:
2097 		 * 	UnEindeutig wenn: (wenn != Format-Attribut)
2098 		 * 		- das Attribut liegt vollstaendig im Bereich
2099 		 * 		- das Attributende liegt im Bereich
2100 		 * 		- der Attributanfang liegt im Bereich:
2101 		 * Eindeutig (im Set mergen):
2102 		 *		- das Attrib umfasst den Bereich
2103 		 * nichts tun:
2104 		 * 		das Attribut liegt ausserhalb des Bereiches
2105 		 */
2106 
2107 		void (*fnMergeAttr)( SfxItemSet&, const SfxPoolItem& )
2108 			= bGetFromChrFmt ? &lcl_MergeAttr_ExpandChrFmt
2109 							 : &lcl_MergeAttr;
2110 
2111 		// dann besorge mal die Auto-(Fmt)Attribute
2112 		SfxItemSet aFmtSet( *rSet.GetPool(), rSet.GetRanges() );
2113 		if( !bOnlyTxtAttr )
2114         {
2115 			SwCntntNode::GetAttr( aFmtSet );
2116             // --> OD 2008-01-16 #newlistlevelattrs#
2117             if ( bMergeIndentValuesOfNumRule )
2118             {
2119                 lcl_MergeListLevelIndentAsLRSpaceItem( *this, aFmtSet );
2120             }
2121             // <--
2122         }
2123 
2124         const sal_uInt16 nSize = m_pSwpHints->Count();
2125 
2126         if( nStt == nEnd )             // kein Bereich:
2127 		{
2128             for (sal_uInt16 n = 0; n < nSize; ++n)
2129             {
2130                 const SwTxtAttr* pHt = (*m_pSwpHints)[n];
2131                 const xub_StrLen nAttrStart = *pHt->GetStart();
2132 				if( nAttrStart > nEnd ) 		// ueber den Bereich hinaus
2133 					break;
2134 
2135                 const xub_StrLen* pAttrEnd = pHt->End();
2136                 if ( ! pAttrEnd ) // no attributes without end
2137 					continue;
2138 
2139                 if( ( nAttrStart < nStt &&
2140                         ( pHt->DontExpand() ? nStt < *pAttrEnd
2141                                             : nStt <= *pAttrEnd )) ||
2142                     ( nStt == nAttrStart &&
2143                         ( nAttrStart == *pAttrEnd || !nStt )))
2144 					(*fnMergeAttr)( rSet, pHt->GetAttr() );
2145 			}
2146 		}
2147 		else							// es ist ein Bereich definiert
2148 		{
2149             // --> FME 2007-03-13 #i75299#
2150             ::std::auto_ptr< std::vector< SwPoolItemEndPair > > pAttrArr;
2151             // <--
2152 
2153 			const sal_uInt16 coArrSz = static_cast<sal_uInt16>(RES_TXTATR_WITHEND_END) -
2154                                    static_cast<sal_uInt16>(RES_CHRATR_BEGIN);
2155 
2156             for (sal_uInt16 n = 0; n < nSize; ++n)
2157             {
2158                 const SwTxtAttr* pHt = (*m_pSwpHints)[n];
2159                 const xub_StrLen nAttrStart = *pHt->GetStart();
2160 				if( nAttrStart > nEnd ) 		// ueber den Bereich hinaus
2161 					break;
2162 
2163                 const xub_StrLen* pAttrEnd = pHt->End();
2164                 if ( ! pAttrEnd ) // no attributes without end
2165 					continue;
2166 
2167 				sal_Bool bChkInvalid = sal_False;
2168                 if( nAttrStart <= nStt )       // vor oder genau Start
2169 				{
2170                     if( *pAttrEnd <= nStt )    // liegt davor
2171 						continue;
2172 
2173 					if( nEnd <= *pAttrEnd )		// hinter oder genau Ende
2174 						(*fnMergeAttr)( aFmtSet, pHt->GetAttr() );
2175 					else
2176 //					else if( pHt->GetAttr() != aFmtSet.Get( pHt->Which() ) )
2177 						// uneindeutig
2178 						bChkInvalid = sal_True;
2179 				}
2180 				else if( nAttrStart < nEnd 		// reicht in den Bereich
2181 )//						 && pHt->GetAttr() != aFmtSet.Get( pHt->Which() ) )
2182 					bChkInvalid = sal_True;
2183 
2184 				if( bChkInvalid )
2185 				{
2186 					// uneindeutig ?
2187                     ::std::auto_ptr< SfxItemIter > pItemIter;
2188                     const SfxPoolItem* pItem = 0;
2189 
2190                     if ( RES_TXTATR_AUTOFMT == pHt->Which() )
2191                     {
2192                         const SfxItemSet* pAutoSet = CharFmt::GetItemSet( pHt->GetAttr() );
2193                         if ( pAutoSet )
2194                         {
2195                             pItemIter.reset( new SfxItemIter( *pAutoSet ) );
2196                             pItem = pItemIter->GetCurItem();
2197                         }
2198                     }
2199                     else
2200                         pItem = &pHt->GetAttr();
2201 
2202                     const sal_uInt16 nHintEnd = *pAttrEnd;
2203 
2204                     while ( pItem )
2205                     {
2206                         const sal_uInt16 nHintWhich = pItem->Which();
2207                         ASSERT(!isUNKNOWNATR(nHintWhich),
2208                                 "SwTxtNode::GetAttr(): unknown attribute?");
2209 
2210                         if ( !pAttrArr.get() )
2211                         {
2212                             pAttrArr.reset(
2213                                 new std::vector< SwPoolItemEndPair >(coArrSz));
2214                         }
2215 
2216                         std::vector< SwPoolItemEndPair >::iterator pPrev = pAttrArr->begin();
2217                         if (isCHRATR(nHintWhich) ||
2218                             isTXTATR_WITHEND(nHintWhich))
2219                         {
2220                             pPrev += nHintWhich - RES_CHRATR_BEGIN;
2221                         }
2222                         else
2223                         {
2224                             pPrev = pAttrArr->end();
2225                         }
2226 
2227 #if OSL_DEBUG_LEVEL > 1
2228                         SwPoolItemEndPair aTmp = *pPrev;
2229 #endif
2230 
2231                         if( pPrev != pAttrArr->end() )
2232                         {
2233                             if( !pPrev->mpItem )
2234                             {
2235                                 if ( bOnlyTxtAttr || *pItem != aFmtSet.Get( nHintWhich ) )
2236                                 {
2237                                     if( nAttrStart > nStt )
2238                                     {
2239                                         rSet.InvalidateItem( nHintWhich );
2240                                         pPrev->mpItem = (SfxPoolItem*)-1;
2241                                     }
2242                                     else
2243                                     {
2244                                         pPrev->mpItem = pItem;
2245                                         pPrev->mnEndPos = nHintEnd;
2246                                     }
2247                                 }
2248                             }
2249                             else if( (SfxPoolItem*)-1 != pPrev->mpItem )
2250                             {
2251                                 if( pPrev->mnEndPos == nAttrStart &&
2252                                     *pPrev->mpItem == *pItem )
2253                                 {
2254                                     pPrev->mpItem = pItem;
2255                                     pPrev->mnEndPos = nHintEnd;
2256                                 }
2257                                 else
2258                                 {
2259                                     rSet.InvalidateItem( nHintWhich );
2260                                     pPrev->mpItem = (SfxPoolItem*)-1;
2261                                 }
2262                             }
2263                         }
2264 
2265                         pItem = ( pItemIter.get() && !pItemIter->IsAtEnd() )
2266                                     ? pItemIter->NextItem() : 0;
2267                     } // end while
2268                 }
2269 			}
2270 
2271             if ( pAttrArr.get() )
2272 			{
2273                 for (sal_uInt16 n = 0; n < coArrSz; ++n)
2274                 {
2275                     const SwPoolItemEndPair& rItemPair = (*pAttrArr)[ n ];
2276                     if( (0 != rItemPair.mpItem) && ((SfxPoolItem*)-1 != rItemPair.mpItem) )
2277                     {
2278                         const sal_uInt16 nWh =
2279                             static_cast<sal_uInt16>(n + RES_CHRATR_BEGIN);
2280 
2281                         if( nEnd <= rItemPair.mnEndPos ) // hinter oder genau Ende
2282                         {
2283                             if( *rItemPair.mpItem != aFmtSet.Get( nWh ) )
2284                                 (*fnMergeAttr)( rSet, *rItemPair.mpItem );
2285                         }
2286                         else
2287                             // uneindeutig
2288                             rSet.InvalidateItem( nWh );
2289                     }
2290                 }
2291 			}
2292 		}
2293 		if( aFmtSet.Count() )
2294 		{
2295 			// aus dem Format-Set alle entfernen, die im TextSet auch gesetzt sind
2296 			aFmtSet.Differentiate( rSet );
2297 			// jetzt alle zusammen "mergen"
2298 			rSet.Put( aFmtSet );
2299 		}
2300 	}
2301 	else if( !bOnlyTxtAttr )
2302     {
2303 		// dann besorge mal die Auto-(Fmt)Attribute
2304 		SwCntntNode::GetAttr( rSet );
2305         // --> OD 2008-01-16 #newlistlevelattrs#
2306         if ( bMergeIndentValuesOfNumRule )
2307         {
2308             lcl_MergeListLevelIndentAsLRSpaceItem( *this, rSet );
2309         }
2310         // <--
2311     }
2312 
2313 	return rSet.Count() ? sal_True : sal_False;
2314 }
2315 
2316 
2317 namespace
2318 {
2319 
2320 typedef std::pair<sal_uInt16, sal_uInt16> AttrSpan_t;
2321 typedef std::multimap<AttrSpan_t, const SwTxtAttr*> AttrSpanMap_t;
2322 
2323 
2324 struct IsAutoStyle
2325 {
2326     bool
operator ()__anond32bdb4f0111::IsAutoStyle2327     operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
2328     const
2329     {
2330         return i_rAttrSpan.second && i_rAttrSpan.second->Which() == RES_TXTATR_AUTOFMT;
2331     }
2332 };
2333 
2334 
2335 /** Removes from io_rAttrSet all items that are set by style on the
2336     given span.
2337   */
2338 struct RemovePresentAttrs
2339 {
RemovePresentAttrs__anond32bdb4f0111::RemovePresentAttrs2340     RemovePresentAttrs(SfxItemSet& io_rAttrSet)
2341         : m_rAttrSet(io_rAttrSet)
2342     {
2343     }
2344 
2345     void
operator ()__anond32bdb4f0111::RemovePresentAttrs2346     operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
2347     const
2348     {
2349         if (!i_rAttrSpan.second)
2350         {
2351             return;
2352         }
2353 
2354         const SwTxtAttr* const pAutoStyle(i_rAttrSpan.second);
2355         SfxItemIter aIter(m_rAttrSet);
2356         const SfxPoolItem* pItem(aIter.GetCurItem());
2357         while (pItem)
2358         {
2359             const sal_uInt16 nWhich(pItem->Which());
2360             if (CharFmt::IsItemIncluded(nWhich, pAutoStyle))
2361             {
2362                 m_rAttrSet.ClearItem(nWhich);
2363             }
2364 
2365             if (aIter.IsAtEnd())
2366             {
2367                 break;
2368             }
2369             pItem = aIter.NextItem();
2370         }
2371     }
2372 
2373 private:
2374     SfxItemSet& m_rAttrSet;
2375 };
2376 
2377 
2378 /** Collects all style-covered spans from i_rHints to o_rSpanMap. In
2379     addition inserts dummy spans with pointer to format equal to 0 for
2380     all gaps (i.e. spans not covered by any style). This simplifies
2381     creation of autostyles for all needed spans, but it means all code
2382     that tries to access the pointer has to check if it's non-null!
2383  */
2384 void
lcl_CollectHintSpans(const SwpHints & i_rHints,const sal_uInt16 nLength,AttrSpanMap_t & o_rSpanMap)2385 lcl_CollectHintSpans(const SwpHints& i_rHints, const sal_uInt16 nLength,
2386         AttrSpanMap_t& o_rSpanMap)
2387 {
2388     sal_uInt16 nLastEnd(0);
2389 
2390     for (sal_uInt16 i(0); i != i_rHints.Count(); ++i)
2391     {
2392         const SwTxtAttr* const pHint(i_rHints[i]);
2393         const sal_uInt16 nWhich(pHint->Which());
2394         if (nWhich == RES_TXTATR_CHARFMT || nWhich == RES_TXTATR_AUTOFMT)
2395         {
2396             const AttrSpan_t aSpan(*pHint->GetStart(), *pHint->End());
2397             o_rSpanMap.insert(AttrSpanMap_t::value_type(aSpan, pHint));
2398 
2399             // < not != because there may be multiple CHARFMT at same range
2400             if (nLastEnd < aSpan.first)
2401             {
2402                 // insert dummy span covering the gap
2403                 o_rSpanMap.insert(AttrSpanMap_t::value_type(
2404                             AttrSpan_t(nLastEnd, aSpan.first), 0));
2405             }
2406 
2407             nLastEnd = aSpan.second;
2408         }
2409     }
2410 
2411     // no hints at the end (special case: no hints at all in i_rHints)
2412     if (nLastEnd != nLength && nLength != 0)
2413     {
2414         o_rSpanMap.insert(
2415             AttrSpanMap_t::value_type(AttrSpan_t(nLastEnd, nLength), 0));
2416     }
2417 }
2418 
2419 
2420 void
lcl_FillWhichIds(const SfxItemSet & i_rAttrSet,std::vector<sal_uInt16> & o_rClearIds)2421 lcl_FillWhichIds(const SfxItemSet& i_rAttrSet, std::vector<sal_uInt16>& o_rClearIds)
2422 {
2423     o_rClearIds.reserve(i_rAttrSet.Count());
2424     SfxItemIter aIter(i_rAttrSet);
2425     const SfxPoolItem* pItem(aIter.GetCurItem());
2426     while (true)
2427     {
2428         o_rClearIds.push_back(pItem->Which());
2429 
2430         if (aIter.IsAtEnd())
2431         {
2432             break;
2433         }
2434         pItem = aIter.NextItem();
2435     }
2436 }
2437 
2438 struct SfxItemSetClearer
2439 {
2440     SfxItemSet & m_rItemSet;
SfxItemSetClearer__anond32bdb4f0111::SfxItemSetClearer2441     SfxItemSetClearer(SfxItemSet & rItemSet) : m_rItemSet(rItemSet) { }
operator ()__anond32bdb4f0111::SfxItemSetClearer2442     void operator()(sal_uInt16 const nWhich) { m_rItemSet.ClearItem(nWhich); }
2443 };
2444 
2445 }
2446 
2447 
2448 /** Does the hard work of SwTxtNode::FmtToTxtAttr: the real conversion
2449     of items to automatic styles.
2450  */
2451 void
impl_FmtToTxtAttr(const SfxItemSet & i_rAttrSet)2452 SwTxtNode::impl_FmtToTxtAttr(const SfxItemSet& i_rAttrSet)
2453 {
2454     typedef AttrSpanMap_t::iterator AttrSpanMap_iterator_t;
2455     AttrSpanMap_t aAttrSpanMap;
2456 
2457     if (i_rAttrSet.Count() == 0)
2458     {
2459         return;
2460     }
2461 
2462     // 1. Identify all spans in hints' array
2463 
2464     lcl_CollectHintSpans(*m_pSwpHints, m_Text.Len(), aAttrSpanMap);
2465 
2466     // 2. Go through all spans and insert new attrs
2467 
2468     AttrSpanMap_iterator_t aCurRange(aAttrSpanMap.begin());
2469     const AttrSpanMap_iterator_t aEnd(aAttrSpanMap.end());
2470     while (aCurRange != aEnd)
2471     {
2472         typedef std::pair<AttrSpanMap_iterator_t, AttrSpanMap_iterator_t>
2473             AttrSpanMapRange_t;
2474         AttrSpanMapRange_t aRange(aAttrSpanMap.equal_range(aCurRange->first));
2475 
2476         // 2a. Collect attributes to insert
2477 
2478         SfxItemSet aCurSet(i_rAttrSet);
2479         std::for_each(aRange.first, aRange.second, RemovePresentAttrs(aCurSet));
2480 
2481         // 2b. Insert automatic style containing the collected attributes
2482 
2483         if (aCurSet.Count() != 0)
2484         {
2485             AttrSpanMap_iterator_t aAutoStyleIt(
2486                     std::find_if(aRange.first, aRange.second, IsAutoStyle()));
2487             if (aAutoStyleIt != aRange.second)
2488             {
2489                 // there already is an automatic style on that span:
2490                 // create new one and remove the original one
2491                 SwTxtAttr* const pAutoStyle(const_cast<SwTxtAttr*>(aAutoStyleIt->second));
2492                 const boost::shared_ptr<SfxItemSet> pOldStyle(
2493                         static_cast<const SwFmtAutoFmt&>(
2494                             pAutoStyle->GetAttr()).GetStyleHandle());
2495                 aCurSet.Put(*pOldStyle);
2496 
2497                 // remove the old hint
2498                 m_pSwpHints->Delete(pAutoStyle);
2499                 DestroyAttr(pAutoStyle);
2500             }
2501             m_pSwpHints->Insert(
2502                     MakeTxtAttr(*GetDoc(), aCurSet,
2503                         aCurRange->first.first, aCurRange->first.second));
2504         }
2505 
2506         aCurRange = aRange.second;
2507     }
2508 
2509     // 3. Clear items from the node
2510     std::vector<sal_uInt16> aClearedIds;
2511     lcl_FillWhichIds(i_rAttrSet, aClearedIds);
2512     ClearItemsFromAttrSet(aClearedIds);
2513 }
2514 
FmtToTxtAttr(SwTxtNode * pNd)2515 void SwTxtNode::FmtToTxtAttr( SwTxtNode* pNd )
2516 {
2517 	SfxItemSet aThisSet( GetDoc()->GetAttrPool(), aCharFmtSetRange );
2518     if( HasSwAttrSet() && GetpSwAttrSet()->Count() )
2519 		aThisSet.Put( *GetpSwAttrSet() );
2520 
2521     GetOrCreateSwpHints();
2522 
2523 	if( pNd == this )
2524 	{
2525         impl_FmtToTxtAttr(aThisSet);
2526     }
2527 	else
2528 	{
2529         // There are five possible combinations of items from this and
2530         // pNd (pNd is the 'main' node):
2531         //
2532         //  case    pNd     this     action
2533         //  ----------------------------------------------------
2534         //     1     -       -      do nothing
2535         //     2     -       a      convert item to attr of this
2536         //     3     a       -      convert item to attr of pNd
2537         //     4     a       a      clear item in this
2538         //     5     a       b      convert item to attr of this
2539 
2540 		SfxItemSet aNdSet( pNd->GetDoc()->GetAttrPool(), aCharFmtSetRange );
2541         if( pNd->HasSwAttrSet() && pNd->GetpSwAttrSet()->Count() )
2542 			aNdSet.Put( *pNd->GetpSwAttrSet() );
2543 
2544         pNd->GetOrCreateSwpHints();
2545 
2546         std::vector<sal_uInt16> aProcessedIds;
2547 
2548 		if( aThisSet.Count() )
2549 		{
2550 			SfxItemIter aIter( aThisSet );
2551 			const SfxPoolItem* pItem = aIter.GetCurItem(), *pNdItem = 0;
2552             SfxItemSet aConvertSet( GetDoc()->GetAttrPool(), aCharFmtSetRange );
2553             std::vector<sal_uInt16> aClearWhichIds;
2554 
2555 			while( true )
2556 			{
2557 				if( SFX_ITEM_SET == aNdSet.GetItemState( pItem->Which(), sal_False, &pNdItem ) )
2558                 {
2559                     if (*pItem == *pNdItem) // 4
2560                     {
2561                         aClearWhichIds.push_back( pItem->Which() );
2562                     }
2563                     else    // 5
2564                     {
2565                         aConvertSet.Put(*pItem);
2566                     }
2567                     aProcessedIds.push_back(pItem->Which());
2568                 }
2569                 else    // 2
2570                 {
2571                     aConvertSet.Put(*pItem);
2572                 }
2573 
2574 				if( aIter.IsAtEnd() )
2575 					break;
2576 				pItem = aIter.NextItem();
2577 			}
2578 
2579             // 4/ clear items of this that are set with the same value on pNd
2580             ClearItemsFromAttrSet( aClearWhichIds );
2581 
2582             // 2, 5/ convert all other items to attrs
2583             impl_FmtToTxtAttr(aConvertSet);
2584         }
2585 
2586         {
2587             std::for_each(aProcessedIds.begin(), aProcessedIds.end(),
2588                     SfxItemSetClearer(aNdSet));
2589 
2590             // 3/ convert items to attrs
2591             pNd->impl_FmtToTxtAttr(aNdSet);
2592 
2593             if( aNdSet.Count() )
2594             {
2595                 SwFmtChg aTmp1( pNd->GetFmtColl() );
2596                 pNd->NotifyClients( &aTmp1, &aTmp1 );
2597             }
2598         }
2599     }
2600 
2601     SetCalcHiddenCharFlags();
2602 
2603     pNd->TryDeleteSwpHints();
2604 }
2605 
2606 /*************************************************************************
2607  *						SwpHints::CalcFlags()
2608  *************************************************************************/
2609 
CalcFlags()2610 void SwpHints::CalcFlags()
2611 {
2612     m_bDDEFields = m_bFootnote = false;
2613     const sal_uInt16 nSize = Count();
2614     const SwTxtAttr* pAttr;
2615     for( sal_uInt16 nPos = 0; nPos < nSize; ++nPos )
2616     {
2617         switch( ( pAttr = (*this)[ nPos ])->Which() )
2618         {
2619         case RES_TXTATR_FTN:
2620             m_bFootnote = true;
2621             if ( m_bDDEFields )
2622                 return;
2623             break;
2624         case RES_TXTATR_FIELD:
2625             {
2626                 const SwField* pFld = pAttr->GetFmtFld().GetField();
2627                 if( RES_DDEFLD == pFld->GetTyp()->Which() )
2628                 {
2629                     m_bDDEFields = true;
2630                     if ( m_bFootnote )
2631                         return;
2632                 }
2633             }
2634             break;
2635         }
2636     }
2637 }
2638 
2639 /*************************************************************************
2640  *						SwpHints::CalcVisibleFlag()
2641  *************************************************************************/
2642 
CalcHiddenParaField()2643 bool SwpHints::CalcHiddenParaField()
2644 {
2645     m_bCalcHiddenParaField = false;
2646     bool bOldHasHiddenParaField = m_bHasHiddenParaField;
2647     bool bNewHasHiddenParaField  = false;
2648     const sal_uInt16	nSize = Count();
2649     const SwTxtAttr *pTxtHt;
2650 
2651     for( sal_uInt16 nPos = 0; nPos < nSize; ++nPos )
2652     {
2653         pTxtHt = (*this)[ nPos ];
2654         const sal_uInt16 nWhich = pTxtHt->Which();
2655 
2656         if( RES_TXTATR_FIELD == nWhich )
2657         {
2658             const SwFmtFld& rFld = pTxtHt->GetFmtFld();
2659             if( RES_HIDDENPARAFLD == rFld.GetField()->GetTyp()->Which() )
2660             {
2661                 if( !((SwHiddenParaField*)rFld.GetField())->IsHidden() )
2662                 {
2663                     SetHiddenParaField(false);
2664                     return bOldHasHiddenParaField != bNewHasHiddenParaField;
2665                 }
2666                 else
2667                 {
2668                     bNewHasHiddenParaField = true;
2669                 }
2670             }
2671         }
2672     }
2673     SetHiddenParaField( bNewHasHiddenParaField );
2674     return bOldHasHiddenParaField != bNewHasHiddenParaField;
2675 }
2676 
2677 
2678 /*************************************************************************
2679  *						SwpHints::NoteInHistory()
2680  *************************************************************************/
2681 
NoteInHistory(SwTxtAttr * pAttr,const bool bNew)2682 void SwpHints::NoteInHistory( SwTxtAttr *pAttr, const bool bNew )
2683 {
2684     if ( m_pHistory ) { m_pHistory->AddHint( pAttr, bNew ); }
2685 }
2686 
2687 /*************************************************************************
2688  *                      SwpHints::MergePortions( )
2689  *************************************************************************/
2690 
MergePortions(SwTxtNode & rNode)2691 bool SwpHints::MergePortions( SwTxtNode& rNode )
2692 {
2693     if ( !Count() )
2694         return false;
2695 
2696     // sort before merging
2697     SwpHintsArray::Resort();
2698 
2699     bool bRet = false;
2700     typedef std::multimap< int, SwTxtAttr* > PortionMap;
2701     PortionMap aPortionMap;
2702     xub_StrLen nLastPorStart = STRING_LEN;
2703     sal_uInt16 i = 0;
2704     int nKey = 0;
2705 
2706     // get portions by start position:
2707     for ( i = 0; i < Count(); ++i )
2708     {
2709         SwTxtAttr *pHt = GetTextHint( i );
2710         if ( RES_TXTATR_CHARFMT != pHt->Which() &&
2711              RES_TXTATR_AUTOFMT != pHt->Which() )
2712              //&&
2713              //RES_TXTATR_INETFMT != pHt->Which() )
2714             continue;
2715 
2716         const xub_StrLen nPorStart = *pHt->GetStart();
2717         if ( nPorStart != nLastPorStart && nLastPorStart != STRING_LEN )
2718             ++nKey;
2719         nLastPorStart = nPorStart;
2720         aPortionMap.insert( std::pair< const int, SwTxtAttr* >( nKey, pHt ) );
2721     }
2722 
2723     // check if portion i can be merged with portion i+1:
2724     i = 0;
2725     int j = i + 1;
2726     while ( i <= nKey )
2727     {
2728         std::pair< PortionMap::iterator, PortionMap::iterator > aRange1 = aPortionMap.equal_range( i );
2729         std::pair< PortionMap::iterator, PortionMap::iterator > aRange2 = aPortionMap.equal_range( j );
2730         PortionMap::iterator aIter1 = aRange1.first;
2731         PortionMap::iterator aIter2 = aRange2.first;
2732 
2733         bool bMerge = true;
2734         const sal_uInt16 nAttributesInPor1  = static_cast<sal_uInt16>(std::distance( aRange1.first, aRange1.second ));
2735         const sal_uInt16 nAttributesInPor2  = static_cast<sal_uInt16>(std::distance( aRange2.first, aRange2.second ));
2736 
2737         if ( nAttributesInPor1 == nAttributesInPor2 && nAttributesInPor1 != 0 )
2738         {
2739             while ( aIter1 != aRange1.second )
2740             {
2741                 const SwTxtAttr* p1 = (*aIter1).second;
2742                 const SwTxtAttr* p2 = (*aIter2).second;
2743                 if ( *p1->End() < *p2->GetStart() || p1->Which() != p2->Which() || !(*p1 == *p2) )
2744                 {
2745                     bMerge = false;
2746                     break;
2747                 }
2748                 ++aIter1;
2749                 ++aIter2;
2750             }
2751         }
2752         else
2753         {
2754             bMerge = false;
2755         }
2756 
2757         if ( bMerge )
2758         {
2759             // erase all elements with key i + 1
2760             xub_StrLen nNewPortionEnd = 0;
2761             for ( aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 )
2762             {
2763                 SwTxtAttr* p2 = (*aIter2).second;
2764                 nNewPortionEnd = *p2->GetEnd();
2765 
2766                 const sal_uInt16 nCountBeforeDelete = Count();
2767                 Delete( p2 );
2768 
2769                 // robust: check if deletion actually took place before destroying attribute:
2770                 if ( Count() < nCountBeforeDelete )
2771                     rNode.DestroyAttr( p2 );
2772             }
2773             aPortionMap.erase( aRange2.first, aRange2.second );
2774             ++j;
2775 
2776             // change all attributes with key i
2777             aRange1 = aPortionMap.equal_range( i );
2778             for ( aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 )
2779             {
2780                 SwTxtAttr* p1 = (*aIter1).second;
2781                 NoteInHistory( p1 );
2782                 *p1->GetEnd() = nNewPortionEnd;
2783                 NoteInHistory( p1, true );
2784                 bRet = true;
2785             }
2786         }
2787         else
2788         {
2789             ++i;
2790             j = i + 1;
2791         }
2792     }
2793 
2794     if ( bRet )
2795     {
2796         SwpHintsArray::Resort();
2797     }
2798 
2799     return bRet;
2800 }
2801 
2802 // check if there is already a character format and adjust the sort numbers
lcl_CheckSortNumber(const SwpHints & rHints,SwTxtCharFmt & rNewCharFmt)2803 void lcl_CheckSortNumber( const SwpHints& rHints, SwTxtCharFmt& rNewCharFmt )
2804 {
2805     const xub_StrLen nHtStart = *rNewCharFmt.GetStart();
2806     const xub_StrLen nHtEnd   = *rNewCharFmt.GetEnd();
2807     sal_uInt16 nSortNumber = 0;
2808 
2809     for ( sal_uInt16 i = 0; i < rHints.Count(); ++i )
2810     {
2811         const SwTxtAttr* pOtherHt = rHints[i];
2812 
2813         const xub_StrLen nOtherStart = *pOtherHt->GetStart();
2814 
2815         if ( nOtherStart > nHtStart )
2816             break;
2817 
2818         if ( RES_TXTATR_CHARFMT == pOtherHt->Which() )
2819         {
2820             const xub_StrLen nOtherEnd = *pOtherHt->End();
2821 
2822             if ( nOtherStart == nHtStart && nOtherEnd == nHtEnd )
2823             {
2824                 const sal_uInt16 nOtherSortNum = static_cast<const SwTxtCharFmt*>(pOtherHt)->GetSortNumber();
2825                 nSortNumber = nOtherSortNum + 1;
2826             }
2827         }
2828     }
2829 
2830     if ( nSortNumber > 0 )
2831         rNewCharFmt.SetSortNumber( nSortNumber );
2832 }
2833 
2834 /*************************************************************************
2835  *						SwpHints::Insert()
2836  *************************************************************************/
2837 
2838 /*
2839  * Try to insert the new hint.
2840  * Depending on the type of the hint, this either always succeeds, or may fail.
2841  * Depending on the type of the hint, other hints may be deleted or
2842  * overwritten.
2843  * The return value indicates successful insertion.
2844  */
TryInsertHint(SwTxtAttr * const pHint,SwTxtNode & rNode,const SetAttrMode nMode)2845 bool SwpHints::TryInsertHint(
2846     SwTxtAttr* const pHint,
2847     SwTxtNode &rNode,
2848     const SetAttrMode nMode )
2849 {
2850     if ( USHRT_MAX == Count() ) // we're sorry, this flight is overbooked...
2851     {
2852         ASSERT(false, "hints array full :-(");
2853         return false;
2854     }
2855 
2856 	// Felder bilden eine Ausnahme:
2857 	// 1) Sie koennen nie ueberlappen
2858 	// 2) Wenn zwei Felder genau aneinander liegen,
2859 	//	  sollen sie nicht zu einem verschmolzen werden.
2860 	// Wir koennen also auf die while-Schleife verzichten
2861 
2862     xub_StrLen *pHtEnd = pHint->GetEnd();
2863     sal_uInt16 nWhich = pHint->Which();
2864 
2865 	switch( nWhich )
2866 	{
2867 	case RES_TXTATR_CHARFMT:
2868     {
2869         // Check if character format contains hidden attribute:
2870         const SwCharFmt* pFmt = pHint->GetCharFmt().GetCharFmt();
2871         const SfxPoolItem* pItem;
2872         if ( SFX_ITEM_SET == pFmt->GetItemState( RES_CHRATR_HIDDEN, sal_True, &pItem ) )
2873             rNode.SetCalcHiddenCharFlags();
2874 
2875         ((SwTxtCharFmt*)pHint)->ChgTxtNode( &rNode );
2876 		break;
2877     }
2878     // --> FME 2007-03-16 #i75430# Recalc hidden flags if necessary
2879     case RES_TXTATR_AUTOFMT:
2880     {
2881         // Check if auto style contains hidden attribute:
2882         const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pHint, RES_CHRATR_HIDDEN );
2883         if ( pHiddenItem )
2884             rNode.SetCalcHiddenCharFlags();
2885         break;
2886     }
2887     // <--
2888 
2889     case RES_TXTATR_INETFMT:
2890         static_cast<SwTxtINetFmt*>(pHint)->InitINetFmt(rNode);
2891         break;
2892 
2893 	case RES_TXTATR_FIELD:
2894 	case RES_TXTATR_ANNOTATION:
2895 	case RES_TXTATR_INPUTFIELD:
2896 		{
2897 			sal_Bool bDelFirst = 0 != ((SwTxtFld*)pHint)->GetpTxtNode();
2898 			((SwTxtFld*)pHint)->ChgTxtNode( &rNode );
2899 			SwDoc* pDoc = rNode.GetDoc();
2900 			const SwField* pFld = ((SwTxtFld*)pHint)->GetFmtFld().GetField();
2901 
2902 			if( !pDoc->IsNewFldLst() )
2903 			{
2904 				// was fuer ein Feld ist es denn ??
2905 				// bestimmte Felder mussen am Doc das Calculations-Flag updaten
2906 				switch( pFld->GetTyp()->Which() )
2907 				{
2908 				case RES_DBFLD:
2909 				case RES_SETEXPFLD:
2910 				case RES_HIDDENPARAFLD:
2911 				case RES_HIDDENTXTFLD:
2912 				case RES_DBNUMSETFLD:
2913 				case RES_DBNEXTSETFLD:
2914 					{
2915 						if( bDelFirst )
2916 							pDoc->InsDelFldInFldLst( sal_False, *(SwTxtFld*)pHint );
2917 						if( rNode.GetNodes().IsDocNodes() )
2918 							pDoc->InsDelFldInFldLst( sal_True, *(SwTxtFld*)pHint );
2919 					}
2920 					break;
2921 				case RES_DDEFLD:
2922 					if( rNode.GetNodes().IsDocNodes() )
2923 						((SwDDEFieldType*)pFld->GetTyp())->IncRefCnt();
2924 					break;
2925 				}
2926 			}
2927 
2928             // gehts ins normale Nodes-Array?
2929             if( rNode.GetNodes().IsDocNodes() )
2930             {
2931                 sal_Bool bInsFldType = sal_False;
2932                 switch( pFld->GetTyp()->Which() )
2933                 {
2934                 case RES_SETEXPFLD:
2935                     bInsFldType = ((SwSetExpFieldType*)pFld->GetTyp())->IsDeleted();
2936                     if( nsSwGetSetExpType::GSE_SEQ & ((SwSetExpFieldType*)pFld->GetTyp())->GetType() )
2937                     {
2938                         // bevor die ReferenzNummer gesetzt wird, sollte
2939                         // das Feld am richtigen FeldTypen haengen!
2940                         SwSetExpFieldType* pFldType = (SwSetExpFieldType*)
2941                                     pDoc->InsertFldType( *pFld->GetTyp() );
2942                         if( pFldType != pFld->GetTyp() )
2943                         {
2944                             SwFmtFld* pFmtFld = (SwFmtFld*)&((SwTxtFld*)pHint)->GetFmtFld();
2945                             pFmtFld->RegisterToFieldType( *pFldType );
2946                             pFmtFld->GetField()->ChgTyp( pFldType );
2947                         }
2948                         pFldType->SetSeqRefNo( *(SwSetExpField*)pFld );
2949                     }
2950                     break;
2951                 case RES_USERFLD:
2952                     bInsFldType = ((SwUserFieldType*)pFld->GetTyp())->IsDeleted();
2953                     break;
2954 
2955                 case RES_DDEFLD:
2956                     if( pDoc->IsNewFldLst() )
2957                         ((SwDDEFieldType*)pFld->GetTyp())->IncRefCnt();
2958                     bInsFldType = ((SwDDEFieldType*)pFld->GetTyp())->IsDeleted();
2959                     break;
2960 
2961                 case RES_POSTITFLD:
2962                     if ( pDoc->GetDocShell() )
2963                         pDoc->GetDocShell()->Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFmtFld(), SWFMTFLD_INSERTED ) );
2964                     break;
2965                 }
2966                 if( bInsFldType )
2967                     pDoc->InsDeletedFldType( *pFld->GetTyp() );
2968             }
2969         }
2970         break;
2971     case RES_TXTATR_FTN :
2972 		((SwTxtFtn*)pHint)->ChgTxtNode( &rNode );
2973 		break;
2974 	case RES_TXTATR_REFMARK:
2975 		((SwTxtRefMark*)pHint)->ChgTxtNode( &rNode );
2976 		if( rNode.GetNodes().IsDocNodes() )
2977 		{
2978             // search for a reference with the same name
2979 			SwTxtAttr* pTmpHt;
2980 			xub_StrLen *pTmpHtEnd, *pTmpHintEnd;
2981 			for( sal_uInt16 n = 0, nEnd = Count(); n < nEnd; ++n )
2982             {
2983                 if (RES_TXTATR_REFMARK == (pTmpHt = GetTextHint(n))->Which() &&
2984 					pHint->GetAttr() == pTmpHt->GetAttr() &&
2985 					0 != ( pTmpHtEnd = pTmpHt->GetEnd() ) &&
2986 					0 != ( pTmpHintEnd = pHint->GetEnd() ) )
2987 				{
2988 					SwComparePosition eCmp = ::ComparePosition(
2989 							*pTmpHt->GetStart(), *pTmpHtEnd,
2990 							*pHint->GetStart(), *pTmpHintEnd );
2991 					sal_Bool bDelOld = sal_True, bChgStart = sal_False, bChgEnd = sal_False;
2992 					switch( eCmp )
2993 					{
2994 					case POS_BEFORE:
2995 					case POS_BEHIND:	bDelOld = sal_False; break;
2996 
2997 					case POS_OUTSIDE:	bChgStart = bChgEnd = sal_True; break;
2998 
2999 					case POS_COLLIDE_END:
3000 					case POS_OVERLAP_BEFORE:	bChgStart = sal_True; break;
3001 					case POS_COLLIDE_START:
3002 					case POS_OVERLAP_BEHIND:    bChgEnd = sal_True; break;
3003                     default: break;
3004 					}
3005 
3006 					if( bChgStart )
3007 						*pHint->GetStart() = *pTmpHt->GetStart();
3008 					if( bChgEnd )
3009 						*pTmpHintEnd = *pTmpHtEnd;
3010 
3011 					if( bDelOld )
3012                     {
3013                         NoteInHistory( pTmpHt );
3014 						rNode.DestroyAttr( Cut( n-- ) );
3015 						--nEnd;
3016 					}
3017 				}
3018             }
3019 		}
3020 		break;
3021 	case RES_TXTATR_TOXMARK:
3022 		((SwTxtTOXMark*)pHint)->ChgTxtNode( &rNode );
3023 		break;
3024 
3025 	case RES_TXTATR_CJK_RUBY:
3026         static_cast<SwTxtRuby*>(pHint)->InitRuby(rNode);
3027 		break;
3028 
3029     case RES_TXTATR_META:
3030     case RES_TXTATR_METAFIELD:
3031         static_cast<SwTxtMeta *>(pHint)->ChgTxtNode( &rNode );
3032         break;
3033 
3034     case RES_CHRATR_HIDDEN:
3035         rNode.SetCalcHiddenCharFlags();
3036         break;
3037 	}
3038 
3039 	if( nsSetAttrMode::SETATTR_DONTEXPAND & nMode )
3040 		pHint->SetDontExpand( sal_True );
3041 
3042 	// SwTxtAttrs ohne Ende werden sonderbehandelt:
3043 	// Sie werden natuerlich in das Array insertet, aber sie werden nicht
3044 	// in die pPrev/Next/On/Off-Verkettung aufgenommen.
3045 	// Der Formatierer erkennt diese TxtHints an dem CH_TXTATR_.. im Text !
3046 	xub_StrLen nHtStart = *pHint->GetStart();
3047 	if( !pHtEnd )
3048     {
3049         SwpHintsArray::Insert( pHint );
3050 		CalcFlags();
3051 #ifdef DBG_UTIL
3052         if( !rNode.GetDoc()->IsInReading() )
3053             CHECK;
3054 #endif
3055         // ... und die Abhaengigen benachrichtigen
3056         if(rNode.GetDepends())
3057         {
3058             SwUpdateAttr aHint(
3059                 nHtStart,
3060                 nHtStart,
3061                 nWhich);
3062 
3063             rNode.ModifyNotification(0,&aHint);
3064         }
3065 
3066         return true;
3067     }
3068 
3069 	// ----------------------------------------------------------------
3070 	// Ab hier gibt es nur noch pHint mit einem EndIdx !!!
3071 
3072     if( *pHtEnd < nHtStart )
3073 	{
3074 		ASSERT( *pHtEnd >= nHtStart,
3075 					"+SwpHints::Insert: invalid hint, end < start" );
3076 
3077 		// Wir drehen den Quatsch einfach um:
3078 		*pHint->GetStart() = *pHtEnd;
3079 		*pHtEnd = nHtStart;
3080 		nHtStart = *pHint->GetStart();
3081 	}
3082 
3083     // I need this value later on for notification but the pointer may become invalid
3084     const xub_StrLen nHintEnd = *pHtEnd;
3085     const bool bNoHintAdjustMode = (nsSetAttrMode::SETATTR_NOHINTADJUST & nMode);
3086 
3087     // handle nesting attributes: inserting may fail due to overlap!
3088     if (pHint->IsNesting())
3089     {
3090         const bool bRet(
3091             TryInsertNesting(rNode, *static_cast<SwTxtAttrNesting*>(pHint)));
3092         if (!bRet) return false;
3093     }
3094     // Currently REFMARK and TOXMARK have OverlapAllowed set to true.
3095     // These attributes may be inserted directly.
3096     // Also attributes without length may be inserted directly.
3097     // SETATTR_NOHINTADJUST is set e.g., during undo.
3098     // Portion building in not necessary during XML import.
3099     else
3100     if ( !bNoHintAdjustMode &&
3101          !pHint->IsOverlapAllowedAttr() &&
3102          !rNode.GetDoc()->IsInXMLImport() &&
3103          ( RES_TXTATR_AUTOFMT == nWhich ||
3104            RES_TXTATR_CHARFMT == nWhich ) )
3105     {
3106         ASSERT( nWhich != RES_TXTATR_AUTOFMT ||
3107                 static_cast<const SwFmtAutoFmt&>(pHint->GetAttr()).GetStyleHandle()->GetPool() ==
3108                 &rNode.GetDoc()->GetAttrPool(),
3109                 "AUTOSTYLES - Pool mismatch" )
3110 
3111         BuildPortions( rNode, *pHint, nMode );
3112 
3113         if ( nHtStart < nHintEnd ) // skip merging for 0-length attributes
3114             MergePortions( rNode );
3115     }
3116     else
3117     {
3118         // There may be more than one character style at the current position.
3119         // Take care of the sort number.
3120         // Special case ruby portion: During import, the ruby attribute is set
3121         // multiple times
3122         // Special case hyperlink: During import, the ruby attribute is set
3123         // multiple times
3124         // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert
3125         // character attributes directly
3126         if ( ( RES_TXTATR_CHARFMT  == nWhich && !bNoHintAdjustMode ) )
3127         {
3128             BuildPortions( rNode, *pHint, nMode );
3129         }
3130         else
3131         {
3132             // --> FME 2007-11-08 #i82989# Check sort numbers in NoHintAdjustMode
3133             if ( RES_TXTATR_CHARFMT == nWhich )
3134                 lcl_CheckSortNumber( *this, *static_cast<SwTxtCharFmt*>(pHint) );
3135             // <--
3136 
3137             SwpHintsArray::Insert( pHint );
3138             NoteInHistory( pHint, true );
3139         }
3140     }
3141 
3142     // ... und die Abhaengigen benachrichtigen
3143     if ( rNode.GetDepends() )
3144     {
3145         SwUpdateAttr aHint(
3146             // rNode.GetDoc()->GetAttrPool(),
3147             nHtStart,
3148             nHtStart == nHintEnd ? nHintEnd + 1 : nHintEnd,
3149             nWhich);
3150 
3151         rNode.ModifyNotification( 0, &aHint );
3152     }
3153 
3154 #ifdef DBG_UTIL
3155     if( !bNoHintAdjustMode && !rNode.GetDoc()->IsInReading() )
3156         CHECK;
3157 #endif
3158 
3159     return true;
3160 }
3161 
3162 /*************************************************************************
3163  *						SwpHints::DeleteAtPos()
3164  *************************************************************************/
3165 
DeleteAtPos(const sal_uInt16 nPos)3166 void SwpHints::DeleteAtPos( const sal_uInt16 nPos )
3167 {
3168     SwTxtAttr *pHint = GetTextHint(nPos);
3169     // ChainDelete( pHint );
3170     NoteInHistory( pHint );
3171     SwpHintsArray::DeleteAtPos( nPos );
3172 
3173     if( pHint->Which() == RES_TXTATR_FIELD )
3174     {
3175         const SwFieldType* pFldTyp = ((SwTxtFld*)pHint)->GetFmtFld().GetField()->GetTyp();
3176         if( RES_DDEFLD == pFldTyp->Which() )
3177         {
3178             const SwTxtNode* pNd = ((SwTxtFld*)pHint)->GetpTxtNode();
3179             if( pNd && pNd->GetNodes().IsDocNodes() )
3180                 ((SwDDEFieldType*)pFldTyp)->DecRefCnt();
3181             ((SwTxtFld*)pHint)->ChgTxtNode( 0 );
3182         }
3183         else if ( m_bHasHiddenParaField &&
3184                  RES_HIDDENPARAFLD == pFldTyp->Which() )
3185         {
3186             m_bCalcHiddenParaField = true;
3187         }
3188     }
3189     else if ( pHint->Which() == RES_TXTATR_ANNOTATION )
3190     {
3191         const_cast<SwFmtFld&>(((SwTxtFld*)pHint)->GetFmtFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFmtFld(), SWFMTFLD_REMOVED ) );
3192     }
3193 
3194     CalcFlags();
3195     CHECK;
3196 }
3197 
3198 // Ist der Hint schon bekannt, dann suche die Position und loesche ihn.
3199 // Ist er nicht im Array, so gibt es ein ASSERT !!
3200 
Delete(SwTxtAttr * pTxtHt)3201 void SwpHints::Delete( SwTxtAttr* pTxtHt )
3202 {
3203 	// Attr 2.0: SwpHintsArr::Delete( pTxtHt );
3204 	const sal_uInt16 nPos = GetStartOf( pTxtHt );
3205 	ASSERT( USHRT_MAX != nPos, "Attribut nicht im Attribut-Array!" );
3206 	if( USHRT_MAX != nPos )
3207 		DeleteAtPos( nPos );
3208 }
3209 
ClearSwpHintsArr(bool bDelFields)3210 void SwTxtNode::ClearSwpHintsArr( bool bDelFields )
3211 {
3212     if ( HasHints() )
3213     {
3214         sal_uInt16 nPos = 0;
3215         while ( nPos < m_pSwpHints->Count() )
3216         {
3217             SwTxtAttr* pDel = m_pSwpHints->GetTextHint( nPos );
3218             bool bDel = false;
3219 
3220             switch( pDel->Which() )
3221             {
3222             case RES_TXTATR_FLYCNT:
3223             case RES_TXTATR_FTN:
3224                 break;
3225 
3226             case RES_TXTATR_FIELD:
3227             case RES_TXTATR_ANNOTATION:
3228             case RES_TXTATR_INPUTFIELD:
3229                 if( bDelFields )
3230                     bDel = true;
3231                 break;
3232             default:
3233                 bDel = true; break;
3234             }
3235 
3236             if( bDel )
3237             {
3238                 m_pSwpHints->SwpHintsArray::DeleteAtPos( nPos );
3239 				DestroyAttr( pDel );
3240 			}
3241 			else
3242 				++nPos;
3243 		}
3244 	}
3245 }
3246 
GetLang(const xub_StrLen nBegin,const xub_StrLen nLen,sal_uInt16 nScript) const3247 sal_uInt16 SwTxtNode::GetLang( const xub_StrLen nBegin, const xub_StrLen nLen,
3248                            sal_uInt16 nScript ) const
3249 {
3250 	sal_uInt16 nRet = LANGUAGE_DONTKNOW;
3251 
3252     if ( ! nScript )
3253     {
3254         nScript = pBreakIt->GetRealScriptOfText( m_Text, nBegin );
3255     }
3256 
3257     // --> FME 2008-09-29 #i91465# hennerdrewes: Consider nScript if pSwpHints == 0
3258     const sal_uInt16 nWhichId = GetWhichOfScript( RES_CHRATR_LANGUAGE, nScript );
3259     // <--
3260 
3261     if ( HasHints() )
3262     {
3263         const xub_StrLen nEnd = nBegin + nLen;
3264         for ( sal_uInt16 i = 0, nSize = m_pSwpHints->Count(); i < nSize; ++i )
3265         {
3266 			// ist der Attribut-Anfang schon groesser als der Idx ?
3267             const SwTxtAttr *pHt = m_pSwpHints->operator[](i);
3268             const xub_StrLen nAttrStart = *pHt->GetStart();
3269 			if( nEnd < nAttrStart )
3270 				break;
3271 
3272 			const sal_uInt16 nWhich = pHt->Which();
3273 
3274             if( nWhichId == nWhich ||
3275                     ( ( pHt->IsCharFmtAttr() || RES_TXTATR_AUTOFMT == nWhich ) && CharFmt::IsItemIncluded( nWhichId, pHt ) ) )
3276             {
3277 				const xub_StrLen *pEndIdx = pHt->End();
3278 				// Ueberlappt das Attribut den Bereich?
3279 
3280 				if( pEndIdx &&
3281 					nLen ? ( nAttrStart < nEnd && nBegin < *pEndIdx )
3282 					 	: (( nAttrStart < nBegin &&
3283 								( pHt->DontExpand() ? nBegin < *pEndIdx
3284 													: nBegin <= *pEndIdx )) ||
3285 							( nBegin == nAttrStart &&
3286 								( nAttrStart == *pEndIdx || !nBegin ))) )
3287 				{
3288                     const SfxPoolItem* pItem = CharFmt::GetItem( *pHt, nWhichId );
3289                     sal_uInt16 nLng = ((SvxLanguageItem*)pItem)->GetLanguage();
3290 
3291 					// Umfasst das Attribut den Bereich komplett?
3292 					if( nAttrStart <= nBegin && nEnd <= *pEndIdx )
3293 						nRet = nLng;
3294 					else if( LANGUAGE_DONTKNOW == nRet )
3295 						nRet = nLng; // partielle Ueberlappung, der 1. gewinnt
3296 				}
3297 			}
3298 		}
3299 	}
3300 	if( LANGUAGE_DONTKNOW == nRet )
3301 	{
3302 		nRet = ((SvxLanguageItem&)GetSwAttrSet().Get( nWhichId )).GetLanguage();
3303 		if( LANGUAGE_DONTKNOW == nRet )
3304             nRet = static_cast<sal_uInt16>(GetAppLanguage());
3305 	}
3306 	return nRet;
3307 }
3308 
3309 
GetCharOfTxtAttr(const SwTxtAttr & rAttr)3310 sal_Unicode GetCharOfTxtAttr( const SwTxtAttr& rAttr )
3311 {
3312     sal_Unicode cRet = CH_TXTATR_BREAKWORD;
3313     switch ( rAttr.Which() )
3314     {
3315         case RES_TXTATR_FTN:
3316         case RES_TXTATR_REFMARK:
3317         case RES_TXTATR_TOXMARK:
3318         case RES_TXTATR_META:
3319         case RES_TXTATR_METAFIELD:
3320         case RES_TXTATR_ANNOTATION:
3321             cRet = CH_TXTATR_INWORD;
3322         break;
3323 
3324         case RES_TXTATR_FIELD:
3325         case RES_TXTATR_FLYCNT:
3326         {
3327             cRet = CH_TXTATR_BREAKWORD;
3328         }
3329         break;
3330 
3331         default:
3332             ASSERT(false, "GetCharOfTxtAttr: unknown attr");
3333             break;
3334     }
3335     return cRet;
3336 }
3337 
3338 
3339