xref: /aoo4110/main/sw/source/core/txtnode/thints.cxx (revision b1cdbd2c)
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 explicitely 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 the 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 succesful
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(), *pAttr->GetEnd(), pAttr->Which() );
1684         m_pSwpHints->Delete( pAttr );
1685         SwTxtAttr::Destroy( pAttr, GetDoc()->GetAttrPool() );
1686         NotifyClients( 0, &aHint );
1687 
1688         TryDeleteSwpHints();
1689     }
1690 }
1691 
1692 /*************************************************************************
1693  *                        SwTxtNode::DeleteAttributes()
1694  *************************************************************************/
1695 
1696 //FIXME: this does NOT respect SORT NUMBER (for CHARFMT)!
DeleteAttributes(const sal_uInt16 nWhich,const xub_StrLen nStart,const xub_StrLen nEnd)1697 void SwTxtNode::DeleteAttributes(
1698     const sal_uInt16 nWhich,
1699     const xub_StrLen nStart,
1700     const xub_StrLen nEnd )
1701 {
1702     if ( !HasHints() )
1703         return;
1704 
1705     for ( sal_uInt16 nPos = 0; m_pSwpHints && nPos < m_pSwpHints->Count(); nPos++ )
1706     {
1707         SwTxtAttr * const pTxtHt = m_pSwpHints->GetTextHint( nPos );
1708         const xub_StrLen nHintStart = *(pTxtHt->GetStart());
1709         if (nStart < nHintStart)
1710         {
1711             break; // sorted by start
1712         }
1713         else if ( (nStart == nHintStart) && (nWhich == pTxtHt->Which()) )
1714         {
1715             if ( nWhich == RES_CHRATR_HIDDEN  )
1716             {
1717                 ASSERT(false, "hey, that's a CHRATR! how did that get in?");
1718                 SetCalcHiddenCharFlags();
1719             }
1720             else if ( nWhich == RES_TXTATR_CHARFMT )
1721             {
1722                 // Check if character format contains hidden attribute:
1723                 const SwCharFmt* pFmt = pTxtHt->GetCharFmt().GetCharFmt();
1724                 const SfxPoolItem* pItem;
1725                 if ( SFX_ITEM_SET == pFmt->GetItemState( RES_CHRATR_HIDDEN, sal_True, &pItem ) )
1726                     SetCalcHiddenCharFlags();
1727             }
1728             // Recalc hidden flags if necessary
1729             else if ( nWhich == RES_TXTATR_AUTOFMT )
1730             {
1731                 // Check if auto style contains hidden attribute:
1732                 const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pTxtHt, RES_CHRATR_HIDDEN );
1733                 if ( pHiddenItem )
1734                     SetCalcHiddenCharFlags();
1735             }
1736 
1737             xub_StrLen const * const pEndIdx = pTxtHt->GetEnd();
1738 
1739             if ( pTxtHt->HasDummyChar() )
1740             {
1741                 // Unbedingt Copy-konstruieren!
1742                 const SwIndex aIdx( this, nStart );
1743                 // erase the CH_TXTATR, which will also delete pTxtHt
1744                 EraseText( aIdx, 1 );
1745             }
1746             else if ( pTxtHt->HasContent() )
1747             {
1748                 const SwIndex aIdx( this, nStart );
1749                 ASSERT( pTxtHt->End() != NULL, "<SwTxtNode::DeleteAttributes(..)> - missing End() at <SwTxtAttr> instance which has content" );
1750                 EraseText( aIdx, *pTxtHt->End() - nStart );
1751             }
1752             else if( *pEndIdx == nEnd )
1753             {
1754                 // den MsgHint jetzt fuettern, weil gleich sind
1755                 // Start und End weg.
1756                 // Das CalcVisibleFlag bei HiddenParaFields entfaellt,
1757                 // da dies das Feld im Dtor selbst erledigt.
1758                 SwUpdateAttr aHint( nStart, *pEndIdx, nWhich );
1759                 m_pSwpHints->DeleteAtPos( nPos );    // gefunden, loeschen,
1760                 SwTxtAttr::Destroy( pTxtHt, GetDoc()->GetAttrPool() );
1761                 NotifyClients( 0, &aHint );
1762             }
1763         }
1764     }
1765     TryDeleteSwpHints();
1766 }
1767 
1768 /*************************************************************************
1769  *						SwTxtNode::DelSoftHyph()
1770  *************************************************************************/
1771 
DelSoftHyph(const xub_StrLen nStt,const xub_StrLen nEnd)1772 void SwTxtNode::DelSoftHyph( const xub_StrLen nStt, const xub_StrLen nEnd )
1773 {
1774     xub_StrLen nFndPos = nStt, nEndPos = nEnd;
1775 	while( STRING_NOTFOUND !=
1776             ( nFndPos = m_Text.Search( CHAR_SOFTHYPHEN, nFndPos )) &&
1777 			nFndPos < nEndPos )
1778 	{
1779 		const SwIndex aIdx( this, nFndPos );
1780         EraseText( aIdx, 1 );
1781 		--nEndPos;
1782 	}
1783 }
1784 
1785 //Modify here for #119405, by easyfan, 2012-05-24
1786 //In MS Word, the font underline setting of the paragraph end position wont affect the formatting of numbering, so we ignore it
lcl_IsIgnoredCharFmtForNumbering(const sal_uInt16 nWhich)1787 bool lcl_IsIgnoredCharFmtForNumbering(const sal_uInt16 nWhich)
1788 {
1789 	return (nWhich ==  RES_CHRATR_UNDERLINE);
1790 }
1791 
1792 //In MS Word, following properties of the paragraph end position wont affect the formatting of bullets, so we ignore them:
1793 //Font underline;
1794 //Font Italic of Western, CJK and CTL;
1795 //Font Bold of Wertern, CJK and CTL;
lcl_IsIgnoredCharFmtForBullets(const sal_uInt16 nWhich)1796 bool lcl_IsIgnoredCharFmtForBullets(const sal_uInt16 nWhich)
1797 {
1798 	return (nWhich ==  RES_CHRATR_UNDERLINE || nWhich ==  RES_CHRATR_POSTURE || nWhich ==  RES_CHRATR_WEIGHT
1799 		|| nWhich == RES_CHRATR_CJK_POSTURE || nWhich == RES_CHRATR_CJK_WEIGHT
1800 		|| nWhich == RES_CHRATR_CTL_POSTURE || nWhich == RES_CHRATR_CTL_WEIGHT);
1801 }
1802 
1803 //Condition for expanding char set to character style of specified number rule level:
1804 //The item inside the set should not conflict to any exist and non-default item inside paragraph properties set (SwCntntNode::SwPAttrSet);
1805 //The node should have applied a number rule;
1806 //The node should be counted in a list, if not, make it to be;
1807 //The item should not conflict to any exist and non-default item inside the character of specified number rule level;
1808 //The item should not be ignored depend on the exact number rule type;
TryCharSetExpandToNum(const SfxItemSet & aCharSet)1809 bool SwTxtNode::TryCharSetExpandToNum(const SfxItemSet& aCharSet)
1810 {
1811 	bool bRet = false;
1812 	SfxItemIter aIter( aCharSet );
1813         const SfxPoolItem* pItem = aIter.FirstItem();
1814         const sal_uInt16 nWhich = pItem->Which();
1815 
1816 	const SfxPoolItem& rInnerItem = GetAttr(nWhich,false);
1817 
1818 	if (!IsDefaultItem(&rInnerItem) &&  !IsInvalidItem(&rInnerItem))
1819 		return bRet;
1820 
1821 	if ( !IsInList() && GetNumRule() && GetListId().Len() > 0 )
1822 	{
1823 		return bRet;
1824 	}
1825 
1826 	SwNumRule* pCurrNum = GetNumRule(false);
1827 
1828 	int nLevel = GetActualListLevel();
1829 
1830 	if (nLevel != -1 && pCurrNum)
1831 	{
1832 		const SwNumFmt* pCurrNumFmt = pCurrNum->GetNumFmt(static_cast<sal_uInt16>(nLevel));
1833 		if (pCurrNumFmt)
1834 		{
1835 			if (pCurrNumFmt->IsItemize() && lcl_IsIgnoredCharFmtForBullets(nWhich))
1836 				return bRet;
1837 			if (pCurrNumFmt->IsEnumeration() && lcl_IsIgnoredCharFmtForNumbering(nWhich))
1838 				return bRet;
1839 			SwCharFmt* pCurrCharFmt =pCurrNumFmt->GetCharFmt();
1840 
1841 			if (pCurrCharFmt && pCurrCharFmt->GetItemState(nWhich,false) != SFX_ITEM_SET)
1842 			{
1843 				pCurrCharFmt->SetFmtAttr(*pItem);
1844 				SwNumFmt aNewNumFmt(*pCurrNumFmt);
1845 				aNewNumFmt.SetCharFmt(pCurrCharFmt);
1846 				pCurrNum->Set(nLevel,aNewNumFmt);
1847 				bRet = true;
1848 			}
1849 		}
1850 	}
1851 
1852 
1853 	return bRet;
1854 }
1855 //End of modification, by easyfan
1856 
1857 // setze diese Attribute am TextNode. Wird der gesamte Bereich umspannt,
1858 // dann setze sie nur im AutoAttrSet (SwCntntNode:: SetAttr)
SetAttr(const SfxItemSet & rSet,const xub_StrLen nStt,const xub_StrLen nEnd,const SetAttrMode nMode)1859 sal_Bool SwTxtNode::SetAttr(
1860     const SfxItemSet& rSet,
1861     const xub_StrLen nStt,
1862     const xub_StrLen nEnd,
1863     const SetAttrMode nMode )
1864 {
1865 	if( !rSet.Count() )
1866 		return sal_False;
1867 
1868 	// teil die Sets auf (fuer Selektion in Nodes)
1869 	const SfxItemSet* pSet = &rSet;
1870 	SfxItemSet aTxtSet( *rSet.GetPool(), RES_TXTATR_BEGIN, RES_TXTATR_END-1 );
1871 
1872 	// gesamter Bereich
1873     if ( !nStt && (nEnd == m_Text.Len()) &&
1874          !(nMode & nsSetAttrMode::SETATTR_NOFORMATATTR ) )
1875 	{
1876 		// sind am Node schon Zeichenvorlagen gesetzt, muss man diese Attribute
1877 		// (rSet) immer als TextAttribute setzen, damit sie angezeigt werden.
1878 		int bHasCharFmts = sal_False;
1879         if ( HasHints() )
1880         {
1881             for ( sal_uInt16 n = 0; n < m_pSwpHints->Count(); ++n )
1882             {
1883                 if ( (*m_pSwpHints)[ n ]->IsCharFmtAttr() )
1884                 {
1885 					bHasCharFmts = sal_True;
1886 					break;
1887                 }
1888             }
1889         }
1890 
1891 		if( !bHasCharFmts )
1892 		{
1893             aTxtSet.Put( rSet );
1894             // If there are any character attributes in rSet,
1895             // we want to set them at the paragraph:
1896 			if( aTxtSet.Count() != rSet.Count() )
1897 			{
1898                 sal_Bool bRet = SetAttr( rSet );
1899           		if( !aTxtSet.Count() )
1900 		    		return bRet;
1901 			}
1902 
1903             // check for auto style:
1904             const SfxPoolItem* pItem;
1905             const bool bAutoStyle = SFX_ITEM_SET == aTxtSet.GetItemState( RES_TXTATR_AUTOFMT, sal_False, &pItem );
1906             if ( bAutoStyle )
1907             {
1908                 boost::shared_ptr<SfxItemSet> pAutoStyleSet = static_cast<const SwFmtAutoFmt*>(pItem)->GetStyleHandle();
1909                 sal_Bool bRet = SetAttr( *pAutoStyleSet );
1910           		if( 1 == aTxtSet.Count() )
1911 		    		return bRet;
1912             }
1913 
1914             // Continue with the text attributes:
1915 			pSet = &aTxtSet;
1916 		}
1917 	}
1918 
1919     GetOrCreateSwpHints();
1920 
1921     SfxItemSet aCharSet( *rSet.GetPool(), aCharAutoFmtSetRange );
1922 
1923     sal_uInt16 nCount = 0;
1924 	SfxItemIter aIter( *pSet );
1925 	const SfxPoolItem* pItem = aIter.GetCurItem();
1926 
1927     do
1928     {
1929         if ( pItem && (reinterpret_cast<SfxPoolItem*>(-1) != pItem))
1930         {
1931             const sal_uInt16 nWhich = pItem->Which();
1932             ASSERT( isCHRATR(nWhich) || isTXTATR(nWhich),
1933                     "SwTxtNode::SetAttr(): unknown attribute" );
1934             if ( isCHRATR(nWhich) || isTXTATR(nWhich) )
1935             {
1936                 if ((RES_TXTATR_CHARFMT == nWhich) &&
1937                     (GetDoc()->GetDfltCharFmt() ==
1938                      static_cast<const SwFmtCharFmt*>(pItem)->GetCharFmt()))
1939                 {
1940                     SwIndex aIndex( this, nStt );
1941                     RstTxtAttr( aIndex, nEnd - nStt, RES_TXTATR_CHARFMT, 0 );
1942                     DontExpandFmt( aIndex );
1943                 }
1944                 else
1945                 {
1946                     if (isCHRATR(nWhich) ||
1947                         (RES_TXTATR_UNKNOWN_CONTAINER == nWhich))
1948                     {
1949                         aCharSet.Put( *pItem );
1950                     }
1951                     else
1952                     {
1953 
1954                         SwTxtAttr *const pNew = MakeTxtAttr( *GetDoc(),
1955                                 const_cast<SfxPoolItem&>(*pItem), nStt, nEnd );
1956                         if ( pNew )
1957                         {
1958                             if ( nEnd != nStt && !pNew->GetEnd() )
1959                             {
1960                                 ASSERT(false,
1961                                     "Attribut without end, but area marked");
1962                                 DestroyAttr( pNew ); // do not insert
1963                             }
1964                             else if ( InsertHint( pNew, nMode ) )
1965                             {
1966                                 ++nCount;
1967                             }
1968                         }
1969                     }
1970                 }
1971             }
1972         }
1973         if ( aIter.IsAtEnd() )
1974             break;
1975         pItem = aIter.NextItem();
1976     } while( true );
1977 
1978     if ( aCharSet.Count() )
1979     {
1980         SwTxtAttr* pTmpNew = MakeTxtAttr( *GetDoc(), aCharSet, nStt, nEnd );
1981         if ( InsertHint( pTmpNew, nMode ) )
1982         {
1983             ++nCount;
1984         }
1985     }
1986 
1987     TryDeleteSwpHints();
1988 
1989 	return nCount ? sal_True : sal_False;
1990 }
1991 
lcl_MergeAttr(SfxItemSet & rSet,const SfxPoolItem & rAttr)1992 void lcl_MergeAttr( SfxItemSet& rSet, const SfxPoolItem& rAttr )
1993 {
1994     if ( RES_TXTATR_AUTOFMT == rAttr.Which() )
1995 	{
1996         const SfxItemSet* pCFSet = CharFmt::GetItemSet( rAttr );
1997         if ( !pCFSet )
1998             return;
1999         SfxWhichIter aIter( *pCFSet );
2000         sal_uInt16 nWhich = aIter.FirstWhich();
2001         while( nWhich )
2002         {
2003             if( ( nWhich < RES_CHRATR_END ||
2004                   RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) &&
2005                 ( SFX_ITEM_SET == pCFSet->GetItemState( nWhich, sal_True ) ) )
2006                 rSet.Put( pCFSet->Get( nWhich ) );
2007             nWhich = aIter.NextWhich();
2008         }
2009     }
2010     else
2011     	rSet.Put( rAttr );
2012 }
2013 
lcl_MergeAttr_ExpandChrFmt(SfxItemSet & rSet,const SfxPoolItem & rAttr)2014 void lcl_MergeAttr_ExpandChrFmt( SfxItemSet& rSet, const SfxPoolItem& rAttr )
2015 {
2016 	if( RES_TXTATR_CHARFMT == rAttr.Which() ||
2017 		RES_TXTATR_INETFMT == rAttr.Which() ||
2018         RES_TXTATR_AUTOFMT == rAttr.Which() )
2019 	{
2020         const SfxItemSet* pCFSet = CharFmt::GetItemSet( rAttr );
2021 
2022         if ( pCFSet )
2023         {
2024             SfxWhichIter aIter( *pCFSet );
2025             sal_uInt16 nWhich = aIter.FirstWhich();
2026             while( nWhich )
2027             {
2028                 if( ( nWhich < RES_CHRATR_END ||
2029                       ( RES_TXTATR_AUTOFMT == rAttr.Which() && RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) ) &&
2030                     ( SFX_ITEM_SET == pCFSet->GetItemState( nWhich, sal_True ) ) )
2031                     rSet.Put( pCFSet->Get( nWhich ) );
2032                 nWhich = aIter.NextWhich();
2033             }
2034         }
2035     }
2036 
2037 	// aufnehmen als MergeWert (falls noch nicht gesetzt neu setzen!)
2038 
2039 /* wenn mehrere Attribute ueberlappen gewinnt der letze !!
2040  z.B
2041 			1234567890123456789
2042 			  |------------| 		Font1
2043 				 |------|			Font2
2044 					^  ^
2045 					|--|		Abfragebereich: -> Gueltig ist Font2
2046 */
2047     rSet.Put( rAttr );
2048 }
2049 
2050 struct SwPoolItemEndPair
2051 {
2052 public:
2053     const SfxPoolItem* mpItem;
2054     xub_StrLen mnEndPos;
2055 
SwPoolItemEndPairSwPoolItemEndPair2056     SwPoolItemEndPair() : mpItem( 0 ), mnEndPos( 0 ) {};
2057 };
2058 
2059 // --> OD 2008-01-16 #newlistlevelattrs#
lcl_MergeListLevelIndentAsLRSpaceItem(const SwTxtNode & rTxtNode,SfxItemSet & rSet)2060 void lcl_MergeListLevelIndentAsLRSpaceItem( const SwTxtNode& rTxtNode,
2061                                             SfxItemSet& rSet )
2062 {
2063     if ( rTxtNode.AreListLevelIndentsApplicable() )
2064     {
2065         const SwNumRule* pRule = rTxtNode.GetNumRule();
2066         if ( pRule && rTxtNode.GetActualListLevel() >= 0 )
2067         {
2068             const SwNumFmt& rFmt = pRule->Get(static_cast<sal_uInt16>(rTxtNode.GetActualListLevel()));
2069             if ( rFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
2070             {
2071                 SvxLRSpaceItem aLR( RES_LR_SPACE );
2072                 aLR.SetTxtLeft( rFmt.GetIndentAt() );
2073                 aLR.SetTxtFirstLineOfst( static_cast<short>(rFmt.GetFirstLineIndent()) );
2074                 rSet.Put( aLR );
2075             }
2076         }
2077     }
2078 }
2079 
2080 // erfrage die Attribute vom TextNode ueber den Bereich
2081 // --> OD 2008-01-16 #newlistlevelattrs#
GetAttr(SfxItemSet & rSet,xub_StrLen nStt,xub_StrLen nEnd,sal_Bool bOnlyTxtAttr,sal_Bool bGetFromChrFmt,const bool bMergeIndentValuesOfNumRule) const2082 sal_Bool SwTxtNode::GetAttr( SfxItemSet& rSet, xub_StrLen nStt, xub_StrLen nEnd,
2083                          sal_Bool bOnlyTxtAttr, sal_Bool bGetFromChrFmt,
2084                          const bool bMergeIndentValuesOfNumRule ) const
2085 {
2086     if( HasHints() )
2087     {
2088 		/* stelle erstmal fest, welche Text-Attribut in dem Bereich gueltig
2089 		 * sind. Dabei gibt es folgende Faelle:
2090 		 * 	UnEindeutig wenn: (wenn != Format-Attribut)
2091 		 * 		- das Attribut liegt vollstaendig im Bereich
2092 		 * 		- das Attributende liegt im Bereich
2093 		 * 		- der Attributanfang liegt im Bereich:
2094 		 * Eindeutig (im Set mergen):
2095 		 *		- das Attrib umfasst den Bereich
2096 		 * nichts tun:
2097 		 * 		das Attribut liegt ausserhalb des Bereiches
2098 		 */
2099 
2100 		void (*fnMergeAttr)( SfxItemSet&, const SfxPoolItem& )
2101 			= bGetFromChrFmt ? &lcl_MergeAttr_ExpandChrFmt
2102 							 : &lcl_MergeAttr;
2103 
2104 		// dann besorge mal die Auto-(Fmt)Attribute
2105 		SfxItemSet aFmtSet( *rSet.GetPool(), rSet.GetRanges() );
2106 		if( !bOnlyTxtAttr )
2107         {
2108 			SwCntntNode::GetAttr( aFmtSet );
2109             // --> OD 2008-01-16 #newlistlevelattrs#
2110             if ( bMergeIndentValuesOfNumRule )
2111             {
2112                 lcl_MergeListLevelIndentAsLRSpaceItem( *this, aFmtSet );
2113             }
2114             // <--
2115         }
2116 
2117         const sal_uInt16 nSize = m_pSwpHints->Count();
2118 
2119         if( nStt == nEnd )             // kein Bereich:
2120 		{
2121             for (sal_uInt16 n = 0; n < nSize; ++n)
2122             {
2123                 const SwTxtAttr* pHt = (*m_pSwpHints)[n];
2124                 const xub_StrLen nAttrStart = *pHt->GetStart();
2125 				if( nAttrStart > nEnd ) 		// ueber den Bereich hinaus
2126 					break;
2127 
2128                 const xub_StrLen* pAttrEnd = pHt->End();
2129                 if ( ! pAttrEnd ) // no attributes without end
2130 					continue;
2131 
2132                 if( ( nAttrStart < nStt &&
2133                         ( pHt->DontExpand() ? nStt < *pAttrEnd
2134                                             : nStt <= *pAttrEnd )) ||
2135                     ( nStt == nAttrStart &&
2136                         ( nAttrStart == *pAttrEnd || !nStt )))
2137 					(*fnMergeAttr)( rSet, pHt->GetAttr() );
2138 			}
2139 		}
2140 		else							// es ist ein Bereich definiert
2141 		{
2142             // --> FME 2007-03-13 #i75299#
2143             ::std::auto_ptr< std::vector< SwPoolItemEndPair > > pAttrArr;
2144             // <--
2145 
2146 			const sal_uInt16 coArrSz = static_cast<sal_uInt16>(RES_TXTATR_WITHEND_END) -
2147                                    static_cast<sal_uInt16>(RES_CHRATR_BEGIN);
2148 
2149             for (sal_uInt16 n = 0; n < nSize; ++n)
2150             {
2151                 const SwTxtAttr* pHt = (*m_pSwpHints)[n];
2152                 const xub_StrLen nAttrStart = *pHt->GetStart();
2153 				if( nAttrStart > nEnd ) 		// ueber den Bereich hinaus
2154 					break;
2155 
2156                 const xub_StrLen* pAttrEnd = pHt->End();
2157                 if ( ! pAttrEnd ) // no attributes without end
2158 					continue;
2159 
2160 				sal_Bool bChkInvalid = sal_False;
2161                 if( nAttrStart <= nStt )       // vor oder genau Start
2162 				{
2163                     if( *pAttrEnd <= nStt )    // liegt davor
2164 						continue;
2165 
2166 					if( nEnd <= *pAttrEnd )		// hinter oder genau Ende
2167 						(*fnMergeAttr)( aFmtSet, pHt->GetAttr() );
2168 					else
2169 //					else if( pHt->GetAttr() != aFmtSet.Get( pHt->Which() ) )
2170 						// uneindeutig
2171 						bChkInvalid = sal_True;
2172 				}
2173 				else if( nAttrStart < nEnd 		// reicht in den Bereich
2174 )//						 && pHt->GetAttr() != aFmtSet.Get( pHt->Which() ) )
2175 					bChkInvalid = sal_True;
2176 
2177 				if( bChkInvalid )
2178 				{
2179 					// uneindeutig ?
2180                     ::std::auto_ptr< SfxItemIter > pItemIter;
2181                     const SfxPoolItem* pItem = 0;
2182 
2183                     if ( RES_TXTATR_AUTOFMT == pHt->Which() )
2184                     {
2185                         const SfxItemSet* pAutoSet = CharFmt::GetItemSet( pHt->GetAttr() );
2186                         if ( pAutoSet )
2187                         {
2188                             pItemIter.reset( new SfxItemIter( *pAutoSet ) );
2189                             pItem = pItemIter->GetCurItem();
2190                         }
2191                     }
2192                     else
2193                         pItem = &pHt->GetAttr();
2194 
2195                     const sal_uInt16 nHintEnd = *pAttrEnd;
2196 
2197                     while ( pItem )
2198                     {
2199                         const sal_uInt16 nHintWhich = pItem->Which();
2200                         ASSERT(!isUNKNOWNATR(nHintWhich),
2201                                 "SwTxtNode::GetAttr(): unkonwn attribute?");
2202 
2203                         if ( !pAttrArr.get() )
2204                         {
2205                             pAttrArr.reset(
2206                                 new std::vector< SwPoolItemEndPair >(coArrSz));
2207                         }
2208 
2209                         std::vector< SwPoolItemEndPair >::iterator pPrev = pAttrArr->begin();
2210                         if (isCHRATR(nHintWhich) ||
2211                             isTXTATR_WITHEND(nHintWhich))
2212                         {
2213                             pPrev += nHintWhich - RES_CHRATR_BEGIN;
2214                         }
2215                         else
2216                         {
2217                             pPrev = pAttrArr->end();
2218                         }
2219 
2220 #if OSL_DEBUG_LEVEL > 1
2221                         SwPoolItemEndPair aTmp = *pPrev;
2222 #endif
2223 
2224                         if( pPrev != pAttrArr->end() )
2225                         {
2226                             if( !pPrev->mpItem )
2227                             {
2228                                 if ( bOnlyTxtAttr || *pItem != aFmtSet.Get( nHintWhich ) )
2229                                 {
2230                                     if( nAttrStart > nStt )
2231                                     {
2232                                         rSet.InvalidateItem( nHintWhich );
2233                                         pPrev->mpItem = (SfxPoolItem*)-1;
2234                                     }
2235                                     else
2236                                     {
2237                                         pPrev->mpItem = pItem;
2238                                         pPrev->mnEndPos = nHintEnd;
2239                                     }
2240                                 }
2241                             }
2242                             else if( (SfxPoolItem*)-1 != pPrev->mpItem )
2243                             {
2244                                 if( pPrev->mnEndPos == nAttrStart &&
2245                                     *pPrev->mpItem == *pItem )
2246                                 {
2247                                     pPrev->mpItem = pItem;
2248                                     pPrev->mnEndPos = nHintEnd;
2249                                 }
2250                                 else
2251                                 {
2252                                     rSet.InvalidateItem( nHintWhich );
2253                                     pPrev->mpItem = (SfxPoolItem*)-1;
2254                                 }
2255                             }
2256                         }
2257 
2258                         pItem = ( pItemIter.get() && !pItemIter->IsAtEnd() )
2259                                     ? pItemIter->NextItem() : 0;
2260                     } // end while
2261                 }
2262 			}
2263 
2264             if ( pAttrArr.get() )
2265 			{
2266                 for (sal_uInt16 n = 0; n < coArrSz; ++n)
2267                 {
2268                     const SwPoolItemEndPair& rItemPair = (*pAttrArr)[ n ];
2269                     if( (0 != rItemPair.mpItem) && ((SfxPoolItem*)-1 != rItemPair.mpItem) )
2270                     {
2271                         const sal_uInt16 nWh =
2272                             static_cast<sal_uInt16>(n + RES_CHRATR_BEGIN);
2273 
2274                         if( nEnd <= rItemPair.mnEndPos ) // hinter oder genau Ende
2275                         {
2276                             if( *rItemPair.mpItem != aFmtSet.Get( nWh ) )
2277                                 (*fnMergeAttr)( rSet, *rItemPair.mpItem );
2278                         }
2279                         else
2280                             // uneindeutig
2281                             rSet.InvalidateItem( nWh );
2282                     }
2283                 }
2284 			}
2285 		}
2286 		if( aFmtSet.Count() )
2287 		{
2288 			// aus dem Format-Set alle entfernen, die im TextSet auch gesetzt sind
2289 			aFmtSet.Differentiate( rSet );
2290 			// jetzt alle zusammen "mergen"
2291 			rSet.Put( aFmtSet );
2292 		}
2293 	}
2294 	else if( !bOnlyTxtAttr )
2295     {
2296 		// dann besorge mal die Auto-(Fmt)Attribute
2297 		SwCntntNode::GetAttr( rSet );
2298         // --> OD 2008-01-16 #newlistlevelattrs#
2299         if ( bMergeIndentValuesOfNumRule )
2300         {
2301             lcl_MergeListLevelIndentAsLRSpaceItem( *this, rSet );
2302         }
2303         // <--
2304     }
2305 
2306 	return rSet.Count() ? sal_True : sal_False;
2307 }
2308 
2309 
2310 namespace
2311 {
2312 
2313 typedef std::pair<sal_uInt16, sal_uInt16> AttrSpan_t;
2314 typedef std::multimap<AttrSpan_t, const SwTxtAttr*> AttrSpanMap_t;
2315 
2316 
2317 struct IsAutoStyle
2318 {
2319     bool
operator ()__anon7c106b800111::IsAutoStyle2320     operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
2321     const
2322     {
2323         return i_rAttrSpan.second && i_rAttrSpan.second->Which() == RES_TXTATR_AUTOFMT;
2324     }
2325 };
2326 
2327 
2328 /** Removes from io_rAttrSet all items that are set by style on the
2329     given span.
2330   */
2331 struct RemovePresentAttrs
2332 {
RemovePresentAttrs__anon7c106b800111::RemovePresentAttrs2333     RemovePresentAttrs(SfxItemSet& io_rAttrSet)
2334         : m_rAttrSet(io_rAttrSet)
2335     {
2336     }
2337 
2338     void
operator ()__anon7c106b800111::RemovePresentAttrs2339     operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
2340     const
2341     {
2342         if (!i_rAttrSpan.second)
2343         {
2344             return;
2345         }
2346 
2347         const SwTxtAttr* const pAutoStyle(i_rAttrSpan.second);
2348         SfxItemIter aIter(m_rAttrSet);
2349         const SfxPoolItem* pItem(aIter.GetCurItem());
2350         while (pItem)
2351         {
2352             const sal_uInt16 nWhich(pItem->Which());
2353             if (CharFmt::IsItemIncluded(nWhich, pAutoStyle))
2354             {
2355                 m_rAttrSet.ClearItem(nWhich);
2356             }
2357 
2358             if (aIter.IsAtEnd())
2359             {
2360                 break;
2361             }
2362             pItem = aIter.NextItem();
2363         }
2364     }
2365 
2366 private:
2367     SfxItemSet& m_rAttrSet;
2368 };
2369 
2370 
2371 /** Collects all style-covered spans from i_rHints to o_rSpanMap. In
2372     addition inserts dummy spans with pointer to format equal to 0 for
2373     all gaps (i.e. spans not covered by any style). This simplifies
2374     creation of autostyles for all needed spans, but it means all code
2375     that tries to access the pointer has to check if it's non-null!
2376  */
2377 void
lcl_CollectHintSpans(const SwpHints & i_rHints,const sal_uInt16 nLength,AttrSpanMap_t & o_rSpanMap)2378 lcl_CollectHintSpans(const SwpHints& i_rHints, const sal_uInt16 nLength,
2379         AttrSpanMap_t& o_rSpanMap)
2380 {
2381     sal_uInt16 nLastEnd(0);
2382 
2383     for (sal_uInt16 i(0); i != i_rHints.Count(); ++i)
2384     {
2385         const SwTxtAttr* const pHint(i_rHints[i]);
2386         const sal_uInt16 nWhich(pHint->Which());
2387         if (nWhich == RES_TXTATR_CHARFMT || nWhich == RES_TXTATR_AUTOFMT)
2388         {
2389             const AttrSpan_t aSpan(*pHint->GetStart(), *pHint->End());
2390             o_rSpanMap.insert(AttrSpanMap_t::value_type(aSpan, pHint));
2391 
2392             // < not != because there may be multiple CHARFMT at same range
2393             if (nLastEnd < aSpan.first)
2394             {
2395                 // insert dummy span covering the gap
2396                 o_rSpanMap.insert(AttrSpanMap_t::value_type(
2397                             AttrSpan_t(nLastEnd, aSpan.first), 0));
2398             }
2399 
2400             nLastEnd = aSpan.second;
2401         }
2402     }
2403 
2404     // no hints at the end (special case: no hints at all in i_rHints)
2405     if (nLastEnd != nLength && nLength != 0)
2406     {
2407         o_rSpanMap.insert(
2408             AttrSpanMap_t::value_type(AttrSpan_t(nLastEnd, nLength), 0));
2409     }
2410 }
2411 
2412 
2413 void
lcl_FillWhichIds(const SfxItemSet & i_rAttrSet,std::vector<sal_uInt16> & o_rClearIds)2414 lcl_FillWhichIds(const SfxItemSet& i_rAttrSet, std::vector<sal_uInt16>& o_rClearIds)
2415 {
2416     o_rClearIds.reserve(i_rAttrSet.Count());
2417     SfxItemIter aIter(i_rAttrSet);
2418     const SfxPoolItem* pItem(aIter.GetCurItem());
2419     while (true)
2420     {
2421         o_rClearIds.push_back(pItem->Which());
2422 
2423         if (aIter.IsAtEnd())
2424         {
2425             break;
2426         }
2427         pItem = aIter.NextItem();
2428     }
2429 }
2430 
2431 struct SfxItemSetClearer
2432 {
2433     SfxItemSet & m_rItemSet;
SfxItemSetClearer__anon7c106b800111::SfxItemSetClearer2434     SfxItemSetClearer(SfxItemSet & rItemSet) : m_rItemSet(rItemSet) { }
operator ()__anon7c106b800111::SfxItemSetClearer2435     void operator()(sal_uInt16 const nWhich) { m_rItemSet.ClearItem(nWhich); }
2436 };
2437 
2438 }
2439 
2440 
2441 /** Does the hard work of SwTxtNode::FmtToTxtAttr: the real conversion
2442     of items to automatic styles.
2443  */
2444 void
impl_FmtToTxtAttr(const SfxItemSet & i_rAttrSet)2445 SwTxtNode::impl_FmtToTxtAttr(const SfxItemSet& i_rAttrSet)
2446 {
2447     typedef AttrSpanMap_t::iterator AttrSpanMap_iterator_t;
2448     AttrSpanMap_t aAttrSpanMap;
2449 
2450     if (i_rAttrSet.Count() == 0)
2451     {
2452         return;
2453     }
2454 
2455     // 1. Identify all spans in hints' array
2456 
2457     lcl_CollectHintSpans(*m_pSwpHints, m_Text.Len(), aAttrSpanMap);
2458 
2459     // 2. Go through all spans and insert new attrs
2460 
2461     AttrSpanMap_iterator_t aCurRange(aAttrSpanMap.begin());
2462     const AttrSpanMap_iterator_t aEnd(aAttrSpanMap.end());
2463     while (aCurRange != aEnd)
2464     {
2465         typedef std::pair<AttrSpanMap_iterator_t, AttrSpanMap_iterator_t>
2466             AttrSpanMapRange_t;
2467         AttrSpanMapRange_t aRange(aAttrSpanMap.equal_range(aCurRange->first));
2468 
2469         // 2a. Collect attributes to insert
2470 
2471         SfxItemSet aCurSet(i_rAttrSet);
2472         std::for_each(aRange.first, aRange.second, RemovePresentAttrs(aCurSet));
2473 
2474         // 2b. Insert automatic style containing the collected attributes
2475 
2476         if (aCurSet.Count() != 0)
2477         {
2478             AttrSpanMap_iterator_t aAutoStyleIt(
2479                     std::find_if(aRange.first, aRange.second, IsAutoStyle()));
2480             if (aAutoStyleIt != aRange.second)
2481             {
2482                 // there already is an automatic style on that span:
2483                 // create new one and remove the original one
2484                 SwTxtAttr* const pAutoStyle(const_cast<SwTxtAttr*>(aAutoStyleIt->second));
2485                 const boost::shared_ptr<SfxItemSet> pOldStyle(
2486                         static_cast<const SwFmtAutoFmt&>(
2487                             pAutoStyle->GetAttr()).GetStyleHandle());
2488                 aCurSet.Put(*pOldStyle);
2489 
2490                 // remove the old hint
2491                 m_pSwpHints->Delete(pAutoStyle);
2492                 DestroyAttr(pAutoStyle);
2493             }
2494             m_pSwpHints->Insert(
2495                     MakeTxtAttr(*GetDoc(), aCurSet,
2496                         aCurRange->first.first, aCurRange->first.second));
2497         }
2498 
2499         aCurRange = aRange.second;
2500     }
2501 
2502     // 3. Clear items from the node
2503     std::vector<sal_uInt16> aClearedIds;
2504     lcl_FillWhichIds(i_rAttrSet, aClearedIds);
2505     ClearItemsFromAttrSet(aClearedIds);
2506 }
2507 
FmtToTxtAttr(SwTxtNode * pNd)2508 void SwTxtNode::FmtToTxtAttr( SwTxtNode* pNd )
2509 {
2510 	SfxItemSet aThisSet( GetDoc()->GetAttrPool(), aCharFmtSetRange );
2511     if( HasSwAttrSet() && GetpSwAttrSet()->Count() )
2512 		aThisSet.Put( *GetpSwAttrSet() );
2513 
2514     GetOrCreateSwpHints();
2515 
2516 	if( pNd == this )
2517 	{
2518         impl_FmtToTxtAttr(aThisSet);
2519     }
2520 	else
2521 	{
2522         // There are five possible combinations of items from this and
2523         // pNd (pNd is the 'main' node):
2524         //
2525         //  case    pNd     this     action
2526         //  ----------------------------------------------------
2527         //     1     -       -      do nothing
2528         //     2     -       a      convert item to attr of this
2529         //     3     a       -      convert item to attr of pNd
2530         //     4     a       a      clear item in this
2531         //     5     a       b      convert item to attr of this
2532 
2533 		SfxItemSet aNdSet( pNd->GetDoc()->GetAttrPool(), aCharFmtSetRange );
2534         if( pNd->HasSwAttrSet() && pNd->GetpSwAttrSet()->Count() )
2535 			aNdSet.Put( *pNd->GetpSwAttrSet() );
2536 
2537         pNd->GetOrCreateSwpHints();
2538 
2539         std::vector<sal_uInt16> aProcessedIds;
2540 
2541 		if( aThisSet.Count() )
2542 		{
2543 			SfxItemIter aIter( aThisSet );
2544 			const SfxPoolItem* pItem = aIter.GetCurItem(), *pNdItem = 0;
2545             SfxItemSet aConvertSet( GetDoc()->GetAttrPool(), aCharFmtSetRange );
2546             std::vector<sal_uInt16> aClearWhichIds;
2547 
2548 			while( true )
2549 			{
2550 				if( SFX_ITEM_SET == aNdSet.GetItemState( pItem->Which(), sal_False, &pNdItem ) )
2551                 {
2552                     if (*pItem == *pNdItem) // 4
2553                     {
2554                         aClearWhichIds.push_back( pItem->Which() );
2555                     }
2556                     else    // 5
2557                     {
2558                         aConvertSet.Put(*pItem);
2559                     }
2560                     aProcessedIds.push_back(pItem->Which());
2561                 }
2562                 else    // 2
2563                 {
2564                     aConvertSet.Put(*pItem);
2565                 }
2566 
2567 				if( aIter.IsAtEnd() )
2568 					break;
2569 				pItem = aIter.NextItem();
2570 			}
2571 
2572             // 4/ clear items of this that are set with the same value on pNd
2573             ClearItemsFromAttrSet( aClearWhichIds );
2574 
2575             // 2, 5/ convert all other items to attrs
2576             impl_FmtToTxtAttr(aConvertSet);
2577         }
2578 
2579         {
2580             std::for_each(aProcessedIds.begin(), aProcessedIds.end(),
2581                     SfxItemSetClearer(aNdSet));
2582 
2583             // 3/ convert items to attrs
2584             pNd->impl_FmtToTxtAttr(aNdSet);
2585 
2586             if( aNdSet.Count() )
2587             {
2588                 SwFmtChg aTmp1( pNd->GetFmtColl() );
2589                 pNd->NotifyClients( &aTmp1, &aTmp1 );
2590             }
2591         }
2592     }
2593 
2594     SetCalcHiddenCharFlags();
2595 
2596     pNd->TryDeleteSwpHints();
2597 }
2598 
2599 /*************************************************************************
2600  *						SwpHints::CalcFlags()
2601  *************************************************************************/
2602 
CalcFlags()2603 void SwpHints::CalcFlags()
2604 {
2605     m_bDDEFields = m_bFootnote = false;
2606     const sal_uInt16 nSize = Count();
2607     const SwTxtAttr* pAttr;
2608     for( sal_uInt16 nPos = 0; nPos < nSize; ++nPos )
2609     {
2610         switch( ( pAttr = (*this)[ nPos ])->Which() )
2611         {
2612         case RES_TXTATR_FTN:
2613             m_bFootnote = true;
2614             if ( m_bDDEFields )
2615                 return;
2616             break;
2617         case RES_TXTATR_FIELD:
2618             {
2619                 const SwField* pFld = pAttr->GetFmtFld().GetField();
2620                 if( RES_DDEFLD == pFld->GetTyp()->Which() )
2621                 {
2622                     m_bDDEFields = true;
2623                     if ( m_bFootnote )
2624                         return;
2625                 }
2626             }
2627             break;
2628         }
2629     }
2630 }
2631 
2632 /*************************************************************************
2633  *						SwpHints::CalcVisibleFlag()
2634  *************************************************************************/
2635 
CalcHiddenParaField()2636 bool SwpHints::CalcHiddenParaField()
2637 {
2638     m_bCalcHiddenParaField = false;
2639     bool bOldHasHiddenParaField = m_bHasHiddenParaField;
2640     bool bNewHasHiddenParaField  = false;
2641     const sal_uInt16	nSize = Count();
2642     const SwTxtAttr *pTxtHt;
2643 
2644     for( sal_uInt16 nPos = 0; nPos < nSize; ++nPos )
2645     {
2646         pTxtHt = (*this)[ nPos ];
2647         const sal_uInt16 nWhich = pTxtHt->Which();
2648 
2649         if( RES_TXTATR_FIELD == nWhich )
2650         {
2651             const SwFmtFld& rFld = pTxtHt->GetFmtFld();
2652             if( RES_HIDDENPARAFLD == rFld.GetField()->GetTyp()->Which() )
2653             {
2654                 if( !((SwHiddenParaField*)rFld.GetField())->IsHidden() )
2655                 {
2656                     SetHiddenParaField(false);
2657                     return bOldHasHiddenParaField != bNewHasHiddenParaField;
2658                 }
2659                 else
2660                 {
2661                     bNewHasHiddenParaField = true;
2662                 }
2663             }
2664         }
2665     }
2666     SetHiddenParaField( bNewHasHiddenParaField );
2667     return bOldHasHiddenParaField != bNewHasHiddenParaField;
2668 }
2669 
2670 
2671 /*************************************************************************
2672  *						SwpHints::NoteInHistory()
2673  *************************************************************************/
2674 
NoteInHistory(SwTxtAttr * pAttr,const bool bNew)2675 void SwpHints::NoteInHistory( SwTxtAttr *pAttr, const bool bNew )
2676 {
2677     if ( m_pHistory ) { m_pHistory->AddHint( pAttr, bNew ); }
2678 }
2679 
2680 /*************************************************************************
2681  *                      SwpHints::MergePortions( )
2682  *************************************************************************/
2683 
MergePortions(SwTxtNode & rNode)2684 bool SwpHints::MergePortions( SwTxtNode& rNode )
2685 {
2686     if ( !Count() )
2687         return false;
2688 
2689     // sort before merging
2690     SwpHintsArray::Resort();
2691 
2692     bool bRet = false;
2693     typedef std::multimap< int, SwTxtAttr* > PortionMap;
2694     PortionMap aPortionMap;
2695     xub_StrLen nLastPorStart = STRING_LEN;
2696     sal_uInt16 i = 0;
2697     int nKey = 0;
2698 
2699     // get portions by start position:
2700     for ( i = 0; i < Count(); ++i )
2701     {
2702         SwTxtAttr *pHt = GetTextHint( i );
2703         if ( RES_TXTATR_CHARFMT != pHt->Which() &&
2704              RES_TXTATR_AUTOFMT != pHt->Which() )
2705              //&&
2706              //RES_TXTATR_INETFMT != pHt->Which() )
2707             continue;
2708 
2709         const xub_StrLen nPorStart = *pHt->GetStart();
2710         if ( nPorStart != nLastPorStart && nLastPorStart != STRING_LEN )
2711             ++nKey;
2712         nLastPorStart = nPorStart;
2713         aPortionMap.insert( std::pair< const int, SwTxtAttr* >( nKey, pHt ) );
2714     }
2715 
2716     // check if portion i can be merged with portion i+1:
2717     i = 0;
2718     int j = i + 1;
2719     while ( i <= nKey )
2720     {
2721         std::pair< PortionMap::iterator, PortionMap::iterator > aRange1 = aPortionMap.equal_range( i );
2722         std::pair< PortionMap::iterator, PortionMap::iterator > aRange2 = aPortionMap.equal_range( j );
2723         PortionMap::iterator aIter1 = aRange1.first;
2724         PortionMap::iterator aIter2 = aRange2.first;
2725 
2726         bool bMerge = true;
2727         const sal_uInt16 nAttributesInPor1  = static_cast<sal_uInt16>(std::distance( aRange1.first, aRange1.second ));
2728         const sal_uInt16 nAttributesInPor2  = static_cast<sal_uInt16>(std::distance( aRange2.first, aRange2.second ));
2729 
2730         if ( nAttributesInPor1 == nAttributesInPor2 && nAttributesInPor1 != 0 )
2731         {
2732             while ( aIter1 != aRange1.second )
2733             {
2734                 const SwTxtAttr* p1 = (*aIter1).second;
2735                 const SwTxtAttr* p2 = (*aIter2).second;
2736                 if ( *p1->End() < *p2->GetStart() || p1->Which() != p2->Which() || !(*p1 == *p2) )
2737                 {
2738                     bMerge = false;
2739                     break;
2740                 }
2741                 ++aIter1;
2742                 ++aIter2;
2743             }
2744         }
2745         else
2746         {
2747             bMerge = false;
2748         }
2749 
2750         if ( bMerge )
2751         {
2752             // erase all elements with key i + 1
2753             xub_StrLen nNewPortionEnd = 0;
2754             for ( aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 )
2755             {
2756                 SwTxtAttr* p2 = (*aIter2).second;
2757                 nNewPortionEnd = *p2->GetEnd();
2758 
2759                 const sal_uInt16 nCountBeforeDelete = Count();
2760                 Delete( p2 );
2761 
2762                 // robust: check if deletion actually took place before destroying attribute:
2763                 if ( Count() < nCountBeforeDelete )
2764                     rNode.DestroyAttr( p2 );
2765             }
2766             aPortionMap.erase( aRange2.first, aRange2.second );
2767             ++j;
2768 
2769             // change all attributes with key i
2770             aRange1 = aPortionMap.equal_range( i );
2771             for ( aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 )
2772             {
2773                 SwTxtAttr* p1 = (*aIter1).second;
2774                 NoteInHistory( p1 );
2775                 *p1->GetEnd() = nNewPortionEnd;
2776                 NoteInHistory( p1, true );
2777                 bRet = true;
2778             }
2779         }
2780         else
2781         {
2782             ++i;
2783             j = i + 1;
2784         }
2785     }
2786 
2787     if ( bRet )
2788     {
2789         SwpHintsArray::Resort();
2790     }
2791 
2792     return bRet;
2793 }
2794 
2795 // check if there is already a character format and adjust the sort numbers
lcl_CheckSortNumber(const SwpHints & rHints,SwTxtCharFmt & rNewCharFmt)2796 void lcl_CheckSortNumber( const SwpHints& rHints, SwTxtCharFmt& rNewCharFmt )
2797 {
2798     const xub_StrLen nHtStart = *rNewCharFmt.GetStart();
2799     const xub_StrLen nHtEnd   = *rNewCharFmt.GetEnd();
2800     sal_uInt16 nSortNumber = 0;
2801 
2802     for ( sal_uInt16 i = 0; i < rHints.Count(); ++i )
2803     {
2804         const SwTxtAttr* pOtherHt = rHints[i];
2805 
2806         const xub_StrLen nOtherStart = *pOtherHt->GetStart();
2807 
2808         if ( nOtherStart > nHtStart )
2809             break;
2810 
2811         if ( RES_TXTATR_CHARFMT == pOtherHt->Which() )
2812         {
2813             const xub_StrLen nOtherEnd = *pOtherHt->End();
2814 
2815             if ( nOtherStart == nHtStart && nOtherEnd == nHtEnd )
2816             {
2817                 const sal_uInt16 nOtherSortNum = static_cast<const SwTxtCharFmt*>(pOtherHt)->GetSortNumber();
2818                 nSortNumber = nOtherSortNum + 1;
2819             }
2820         }
2821     }
2822 
2823     if ( nSortNumber > 0 )
2824         rNewCharFmt.SetSortNumber( nSortNumber );
2825 }
2826 
2827 /*************************************************************************
2828  *						SwpHints::Insert()
2829  *************************************************************************/
2830 
2831 /*
2832  * Try to insert the new hint.
2833  * Depending on the type of the hint, this either always succeeds, or may fail.
2834  * Depending on the type of the hint, other hints may be deleted or
2835  * overwritten.
2836  * The return value indicates successful insertion.
2837  */
TryInsertHint(SwTxtAttr * const pHint,SwTxtNode & rNode,const SetAttrMode nMode)2838 bool SwpHints::TryInsertHint(
2839     SwTxtAttr* const pHint,
2840     SwTxtNode &rNode,
2841     const SetAttrMode nMode )
2842 {
2843     if ( USHRT_MAX == Count() ) // we're sorry, this flight is overbooked...
2844     {
2845         ASSERT(false, "hints array full :-(");
2846         return false;
2847     }
2848 
2849 	// Felder bilden eine Ausnahme:
2850 	// 1) Sie koennen nie ueberlappen
2851 	// 2) Wenn zwei Felder genau aneinander liegen,
2852 	//	  sollen sie nicht zu einem verschmolzen werden.
2853 	// Wir koennen also auf die while-Schleife verzichten
2854 
2855     xub_StrLen *pHtEnd = pHint->GetEnd();
2856     sal_uInt16 nWhich = pHint->Which();
2857 
2858 	switch( nWhich )
2859 	{
2860 	case RES_TXTATR_CHARFMT:
2861     {
2862         // Check if character format contains hidden attribute:
2863         const SwCharFmt* pFmt = pHint->GetCharFmt().GetCharFmt();
2864         const SfxPoolItem* pItem;
2865         if ( SFX_ITEM_SET == pFmt->GetItemState( RES_CHRATR_HIDDEN, sal_True, &pItem ) )
2866             rNode.SetCalcHiddenCharFlags();
2867 
2868         ((SwTxtCharFmt*)pHint)->ChgTxtNode( &rNode );
2869 		break;
2870     }
2871     // --> FME 2007-03-16 #i75430# Recalc hidden flags if necessary
2872     case RES_TXTATR_AUTOFMT:
2873     {
2874         // Check if auto style contains hidden attribute:
2875         const SfxPoolItem* pHiddenItem = CharFmt::GetItem( *pHint, RES_CHRATR_HIDDEN );
2876         if ( pHiddenItem )
2877             rNode.SetCalcHiddenCharFlags();
2878         break;
2879     }
2880     // <--
2881 
2882     case RES_TXTATR_INETFMT:
2883         static_cast<SwTxtINetFmt*>(pHint)->InitINetFmt(rNode);
2884         break;
2885 
2886 	case RES_TXTATR_FIELD:
2887 	case RES_TXTATR_ANNOTATION:
2888 	case RES_TXTATR_INPUTFIELD:
2889 		{
2890 			sal_Bool bDelFirst = 0 != ((SwTxtFld*)pHint)->GetpTxtNode();
2891 			((SwTxtFld*)pHint)->ChgTxtNode( &rNode );
2892 			SwDoc* pDoc = rNode.GetDoc();
2893 			const SwField* pFld = ((SwTxtFld*)pHint)->GetFmtFld().GetField();
2894 
2895 			if( !pDoc->IsNewFldLst() )
2896 			{
2897 				// was fuer ein Feld ist es denn ??
2898 				// bestimmte Felder mussen am Doc das Calculations-Flag updaten
2899 				switch( pFld->GetTyp()->Which() )
2900 				{
2901 				case RES_DBFLD:
2902 				case RES_SETEXPFLD:
2903 				case RES_HIDDENPARAFLD:
2904 				case RES_HIDDENTXTFLD:
2905 				case RES_DBNUMSETFLD:
2906 				case RES_DBNEXTSETFLD:
2907 					{
2908 						if( bDelFirst )
2909 							pDoc->InsDelFldInFldLst( sal_False, *(SwTxtFld*)pHint );
2910 						if( rNode.GetNodes().IsDocNodes() )
2911 							pDoc->InsDelFldInFldLst( sal_True, *(SwTxtFld*)pHint );
2912 					}
2913 					break;
2914 				case RES_DDEFLD:
2915 					if( rNode.GetNodes().IsDocNodes() )
2916 						((SwDDEFieldType*)pFld->GetTyp())->IncRefCnt();
2917 					break;
2918 				}
2919 			}
2920 
2921             // gehts ins normale Nodes-Array?
2922             if( rNode.GetNodes().IsDocNodes() )
2923             {
2924                 sal_Bool bInsFldType = sal_False;
2925                 switch( pFld->GetTyp()->Which() )
2926                 {
2927                 case RES_SETEXPFLD:
2928                     bInsFldType = ((SwSetExpFieldType*)pFld->GetTyp())->IsDeleted();
2929                     if( nsSwGetSetExpType::GSE_SEQ & ((SwSetExpFieldType*)pFld->GetTyp())->GetType() )
2930                     {
2931                         // bevor die ReferenzNummer gesetzt wird, sollte
2932                         // das Feld am richtigen FeldTypen haengen!
2933                         SwSetExpFieldType* pFldType = (SwSetExpFieldType*)
2934                                     pDoc->InsertFldType( *pFld->GetTyp() );
2935                         if( pFldType != pFld->GetTyp() )
2936                         {
2937                             SwFmtFld* pFmtFld = (SwFmtFld*)&((SwTxtFld*)pHint)->GetFmtFld();
2938                             pFmtFld->RegisterToFieldType( *pFldType );
2939                             pFmtFld->GetField()->ChgTyp( pFldType );
2940                         }
2941                         pFldType->SetSeqRefNo( *(SwSetExpField*)pFld );
2942                     }
2943                     break;
2944                 case RES_USERFLD:
2945                     bInsFldType = ((SwUserFieldType*)pFld->GetTyp())->IsDeleted();
2946                     break;
2947 
2948                 case RES_DDEFLD:
2949                     if( pDoc->IsNewFldLst() )
2950                         ((SwDDEFieldType*)pFld->GetTyp())->IncRefCnt();
2951                     bInsFldType = ((SwDDEFieldType*)pFld->GetTyp())->IsDeleted();
2952                     break;
2953 
2954                 case RES_POSTITFLD:
2955                     if ( pDoc->GetDocShell() )
2956                         pDoc->GetDocShell()->Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFmtFld(), SWFMTFLD_INSERTED ) );
2957                     break;
2958                 }
2959                 if( bInsFldType )
2960                     pDoc->InsDeletedFldType( *pFld->GetTyp() );
2961             }
2962         }
2963         break;
2964     case RES_TXTATR_FTN :
2965 		((SwTxtFtn*)pHint)->ChgTxtNode( &rNode );
2966 		break;
2967 	case RES_TXTATR_REFMARK:
2968 		((SwTxtRefMark*)pHint)->ChgTxtNode( &rNode );
2969 		if( rNode.GetNodes().IsDocNodes() )
2970 		{
2971             // search for a reference with the same name
2972 			SwTxtAttr* pTmpHt;
2973 			xub_StrLen *pTmpHtEnd, *pTmpHintEnd;
2974 			for( sal_uInt16 n = 0, nEnd = Count(); n < nEnd; ++n )
2975             {
2976                 if (RES_TXTATR_REFMARK == (pTmpHt = GetTextHint(n))->Which() &&
2977 					pHint->GetAttr() == pTmpHt->GetAttr() &&
2978 					0 != ( pTmpHtEnd = pTmpHt->GetEnd() ) &&
2979 					0 != ( pTmpHintEnd = pHint->GetEnd() ) )
2980 				{
2981 					SwComparePosition eCmp = ::ComparePosition(
2982 							*pTmpHt->GetStart(), *pTmpHtEnd,
2983 							*pHint->GetStart(), *pTmpHintEnd );
2984 					sal_Bool bDelOld = sal_True, bChgStart = sal_False, bChgEnd = sal_False;
2985 					switch( eCmp )
2986 					{
2987 					case POS_BEFORE:
2988 					case POS_BEHIND:	bDelOld = sal_False; break;
2989 
2990 					case POS_OUTSIDE:	bChgStart = bChgEnd = sal_True; break;
2991 
2992 					case POS_COLLIDE_END:
2993 					case POS_OVERLAP_BEFORE:	bChgStart = sal_True; break;
2994 					case POS_COLLIDE_START:
2995 					case POS_OVERLAP_BEHIND:    bChgEnd = sal_True; break;
2996                     default: break;
2997 					}
2998 
2999 					if( bChgStart )
3000 						*pHint->GetStart() = *pTmpHt->GetStart();
3001 					if( bChgEnd )
3002 						*pTmpHintEnd = *pTmpHtEnd;
3003 
3004 					if( bDelOld )
3005                     {
3006                         NoteInHistory( pTmpHt );
3007 						rNode.DestroyAttr( Cut( n-- ) );
3008 						--nEnd;
3009 					}
3010 				}
3011             }
3012 		}
3013 		break;
3014 	case RES_TXTATR_TOXMARK:
3015 		((SwTxtTOXMark*)pHint)->ChgTxtNode( &rNode );
3016 		break;
3017 
3018 	case RES_TXTATR_CJK_RUBY:
3019         static_cast<SwTxtRuby*>(pHint)->InitRuby(rNode);
3020 		break;
3021 
3022     case RES_TXTATR_META:
3023     case RES_TXTATR_METAFIELD:
3024         static_cast<SwTxtMeta *>(pHint)->ChgTxtNode( &rNode );
3025         break;
3026 
3027     case RES_CHRATR_HIDDEN:
3028         rNode.SetCalcHiddenCharFlags();
3029         break;
3030 	}
3031 
3032 	if( nsSetAttrMode::SETATTR_DONTEXPAND & nMode )
3033 		pHint->SetDontExpand( sal_True );
3034 
3035 	// SwTxtAttrs ohne Ende werden sonderbehandelt:
3036 	// Sie werden natuerlich in das Array insertet, aber sie werden nicht
3037 	// in die pPrev/Next/On/Off-Verkettung aufgenommen.
3038 	// Der Formatierer erkennt diese TxtHints an dem CH_TXTATR_.. im Text !
3039 	xub_StrLen nHtStart = *pHint->GetStart();
3040 	if( !pHtEnd )
3041     {
3042         SwpHintsArray::Insert( pHint );
3043 		CalcFlags();
3044 #ifdef DBG_UTIL
3045         if( !rNode.GetDoc()->IsInReading() )
3046             CHECK;
3047 #endif
3048 		// ... und die Abhaengigen benachrichtigen
3049 		if ( rNode.GetDepends() )
3050 		{
3051 			SwUpdateAttr aHint( nHtStart, nHtStart, nWhich );
3052 			rNode.ModifyNotification( 0, &aHint );
3053         }
3054         return true;
3055     }
3056 
3057 	// ----------------------------------------------------------------
3058 	// Ab hier gibt es nur noch pHint mit einem EndIdx !!!
3059 
3060     if( *pHtEnd < nHtStart )
3061 	{
3062 		ASSERT( *pHtEnd >= nHtStart,
3063 					"+SwpHints::Insert: invalid hint, end < start" );
3064 
3065 		// Wir drehen den Quatsch einfach um:
3066 		*pHint->GetStart() = *pHtEnd;
3067 		*pHtEnd = nHtStart;
3068 		nHtStart = *pHint->GetStart();
3069 	}
3070 
3071     // I need this value later on for notification but the pointer may become invalid
3072     const xub_StrLen nHintEnd = *pHtEnd;
3073     const bool bNoHintAdjustMode = (nsSetAttrMode::SETATTR_NOHINTADJUST & nMode);
3074 
3075     // handle nesting attributes: inserting may fail due to overlap!
3076     if (pHint->IsNesting())
3077     {
3078         const bool bRet(
3079             TryInsertNesting(rNode, *static_cast<SwTxtAttrNesting*>(pHint)));
3080         if (!bRet) return false;
3081     }
3082     // Currently REFMARK and TOXMARK have OverlapAllowed set to true.
3083     // These attributes may be inserted directly.
3084     // Also attributes without length may be inserted directly.
3085     // SETATTR_NOHINTADJUST is set e.g., during undo.
3086     // Portion building in not necessary during XML import.
3087     else
3088     if ( !bNoHintAdjustMode &&
3089          !pHint->IsOverlapAllowedAttr() &&
3090          !rNode.GetDoc()->IsInXMLImport() &&
3091          ( RES_TXTATR_AUTOFMT == nWhich ||
3092            RES_TXTATR_CHARFMT == nWhich ) )
3093     {
3094         ASSERT( nWhich != RES_TXTATR_AUTOFMT ||
3095                 static_cast<const SwFmtAutoFmt&>(pHint->GetAttr()).GetStyleHandle()->GetPool() ==
3096                 &rNode.GetDoc()->GetAttrPool(),
3097                 "AUTOSTYLES - Pool mismatch" )
3098 
3099         BuildPortions( rNode, *pHint, nMode );
3100 
3101         if ( nHtStart < nHintEnd ) // skip merging for 0-length attributes
3102             MergePortions( rNode );
3103     }
3104     else
3105     {
3106         // There may be more than one character style at the current position.
3107         // Take care of the sort number.
3108         // Special case ruby portion: During import, the ruby attribute is set
3109         // multiple times
3110         // Special case hyperlink: During import, the ruby attribute is set
3111         // multiple times
3112         // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert
3113         // character attributes directly
3114         if ( ( RES_TXTATR_CHARFMT  == nWhich && !bNoHintAdjustMode ) )
3115         {
3116             BuildPortions( rNode, *pHint, nMode );
3117         }
3118         else
3119         {
3120             // --> FME 2007-11-08 #i82989# Check sort numbers in NoHintAdjustMode
3121             if ( RES_TXTATR_CHARFMT == nWhich )
3122                 lcl_CheckSortNumber( *this, *static_cast<SwTxtCharFmt*>(pHint) );
3123             // <--
3124 
3125             SwpHintsArray::Insert( pHint );
3126             NoteInHistory( pHint, true );
3127         }
3128     }
3129 
3130     // ... und die Abhaengigen benachrichtigen
3131 	if ( rNode.GetDepends() )
3132 	{
3133 		SwUpdateAttr aHint( nHtStart, nHtStart == nHintEnd ? nHintEnd + 1 : nHintEnd, nWhich );
3134 		rNode.ModifyNotification( 0, &aHint );
3135 	}
3136 
3137 #ifdef DBG_UTIL
3138     if( !bNoHintAdjustMode && !rNode.GetDoc()->IsInReading() )
3139         CHECK;
3140 #endif
3141 
3142     return true;
3143 }
3144 
3145 /*************************************************************************
3146  *						SwpHints::DeleteAtPos()
3147  *************************************************************************/
3148 
DeleteAtPos(const sal_uInt16 nPos)3149 void SwpHints::DeleteAtPos( const sal_uInt16 nPos )
3150 {
3151     SwTxtAttr *pHint = GetTextHint(nPos);
3152     // ChainDelete( pHint );
3153     NoteInHistory( pHint );
3154     SwpHintsArray::DeleteAtPos( nPos );
3155 
3156     if( pHint->Which() == RES_TXTATR_FIELD )
3157     {
3158         const SwFieldType* pFldTyp = ((SwTxtFld*)pHint)->GetFmtFld().GetField()->GetTyp();
3159         if( RES_DDEFLD == pFldTyp->Which() )
3160         {
3161             const SwTxtNode* pNd = ((SwTxtFld*)pHint)->GetpTxtNode();
3162             if( pNd && pNd->GetNodes().IsDocNodes() )
3163                 ((SwDDEFieldType*)pFldTyp)->DecRefCnt();
3164             ((SwTxtFld*)pHint)->ChgTxtNode( 0 );
3165         }
3166         else if ( m_bHasHiddenParaField &&
3167                  RES_HIDDENPARAFLD == pFldTyp->Which() )
3168         {
3169             m_bCalcHiddenParaField = true;
3170         }
3171     }
3172     else if ( pHint->Which() == RES_TXTATR_ANNOTATION )
3173     {
3174         const_cast<SwFmtFld&>(((SwTxtFld*)pHint)->GetFmtFld()).Broadcast( SwFmtFldHint( &((SwTxtFld*)pHint)->GetFmtFld(), SWFMTFLD_REMOVED ) );
3175     }
3176 
3177     CalcFlags();
3178     CHECK;
3179 }
3180 
3181 // Ist der Hint schon bekannt, dann suche die Position und loesche ihn.
3182 // Ist er nicht im Array, so gibt es ein ASSERT !!
3183 
Delete(SwTxtAttr * pTxtHt)3184 void SwpHints::Delete( SwTxtAttr* pTxtHt )
3185 {
3186 	// Attr 2.0: SwpHintsArr::Delete( pTxtHt );
3187 	const sal_uInt16 nPos = GetStartOf( pTxtHt );
3188 	ASSERT( USHRT_MAX != nPos, "Attribut nicht im Attribut-Array!" );
3189 	if( USHRT_MAX != nPos )
3190 		DeleteAtPos( nPos );
3191 }
3192 
ClearSwpHintsArr(bool bDelFields)3193 void SwTxtNode::ClearSwpHintsArr( bool bDelFields )
3194 {
3195     if ( HasHints() )
3196     {
3197         sal_uInt16 nPos = 0;
3198         while ( nPos < m_pSwpHints->Count() )
3199         {
3200             SwTxtAttr* pDel = m_pSwpHints->GetTextHint( nPos );
3201             bool bDel = false;
3202 
3203             switch( pDel->Which() )
3204             {
3205             case RES_TXTATR_FLYCNT:
3206             case RES_TXTATR_FTN:
3207                 break;
3208 
3209             case RES_TXTATR_FIELD:
3210             case RES_TXTATR_ANNOTATION:
3211             case RES_TXTATR_INPUTFIELD:
3212                 if( bDelFields )
3213                     bDel = true;
3214                 break;
3215             default:
3216                 bDel = true; break;
3217             }
3218 
3219             if( bDel )
3220             {
3221                 m_pSwpHints->SwpHintsArray::DeleteAtPos( nPos );
3222 				DestroyAttr( pDel );
3223 			}
3224 			else
3225 				++nPos;
3226 		}
3227 	}
3228 }
3229 
GetLang(const xub_StrLen nBegin,const xub_StrLen nLen,sal_uInt16 nScript) const3230 sal_uInt16 SwTxtNode::GetLang( const xub_StrLen nBegin, const xub_StrLen nLen,
3231                            sal_uInt16 nScript ) const
3232 {
3233 	sal_uInt16 nRet = LANGUAGE_DONTKNOW;
3234 
3235     if ( ! nScript )
3236     {
3237         nScript = pBreakIt->GetRealScriptOfText( m_Text, nBegin );
3238     }
3239 
3240     // --> FME 2008-09-29 #i91465# hennerdrewes: Consider nScript if pSwpHints == 0
3241     const sal_uInt16 nWhichId = GetWhichOfScript( RES_CHRATR_LANGUAGE, nScript );
3242     // <--
3243 
3244     if ( HasHints() )
3245     {
3246         const xub_StrLen nEnd = nBegin + nLen;
3247         for ( sal_uInt16 i = 0, nSize = m_pSwpHints->Count(); i < nSize; ++i )
3248         {
3249 			// ist der Attribut-Anfang schon groesser als der Idx ?
3250             const SwTxtAttr *pHt = m_pSwpHints->operator[](i);
3251             const xub_StrLen nAttrStart = *pHt->GetStart();
3252 			if( nEnd < nAttrStart )
3253 				break;
3254 
3255 			const sal_uInt16 nWhich = pHt->Which();
3256 
3257             if( nWhichId == nWhich ||
3258                     ( ( pHt->IsCharFmtAttr() || RES_TXTATR_AUTOFMT == nWhich ) && CharFmt::IsItemIncluded( nWhichId, pHt ) ) )
3259             {
3260 				const xub_StrLen *pEndIdx = pHt->End();
3261 				// Ueberlappt das Attribut den Bereich?
3262 
3263 				if( pEndIdx &&
3264 					nLen ? ( nAttrStart < nEnd && nBegin < *pEndIdx )
3265 					 	: (( nAttrStart < nBegin &&
3266 								( pHt->DontExpand() ? nBegin < *pEndIdx
3267 													: nBegin <= *pEndIdx )) ||
3268 							( nBegin == nAttrStart &&
3269 								( nAttrStart == *pEndIdx || !nBegin ))) )
3270 				{
3271                     const SfxPoolItem* pItem = CharFmt::GetItem( *pHt, nWhichId );
3272                     sal_uInt16 nLng = ((SvxLanguageItem*)pItem)->GetLanguage();
3273 
3274 					// Umfasst das Attribut den Bereich komplett?
3275 					if( nAttrStart <= nBegin && nEnd <= *pEndIdx )
3276 						nRet = nLng;
3277 					else if( LANGUAGE_DONTKNOW == nRet )
3278 						nRet = nLng; // partielle Ueberlappung, der 1. gewinnt
3279 				}
3280 			}
3281 		}
3282 	}
3283 	if( LANGUAGE_DONTKNOW == nRet )
3284 	{
3285 		nRet = ((SvxLanguageItem&)GetSwAttrSet().Get( nWhichId )).GetLanguage();
3286 		if( LANGUAGE_DONTKNOW == nRet )
3287             nRet = static_cast<sal_uInt16>(GetAppLanguage());
3288 	}
3289 	return nRet;
3290 }
3291 
3292 
GetCharOfTxtAttr(const SwTxtAttr & rAttr)3293 sal_Unicode GetCharOfTxtAttr( const SwTxtAttr& rAttr )
3294 {
3295     sal_Unicode cRet = CH_TXTATR_BREAKWORD;
3296     switch ( rAttr.Which() )
3297     {
3298         case RES_TXTATR_FTN:
3299         case RES_TXTATR_REFMARK:
3300         case RES_TXTATR_TOXMARK:
3301         case RES_TXTATR_META:
3302         case RES_TXTATR_METAFIELD:
3303         case RES_TXTATR_ANNOTATION:
3304             cRet = CH_TXTATR_INWORD;
3305         break;
3306 
3307         case RES_TXTATR_FIELD:
3308         case RES_TXTATR_FLYCNT:
3309         {
3310             cRet = CH_TXTATR_BREAKWORD;
3311         }
3312         break;
3313 
3314         default:
3315             ASSERT(false, "GetCharOfTxtAttr: unknown attr");
3316             break;
3317     }
3318     return cRet;
3319 }
3320 
3321 
3322