xref: /AOO41X/main/sw/source/core/txtnode/ndhints.cxx (revision 69a743679e823ad8f875be547552acb607b8ada5)
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 
29 #include "txatbase.hxx"
30 #include "ndhints.hxx"
31 #include <txtatr.hxx>
32 
33 #ifndef PRODUCT
34 #include <pam.hxx>
35 #endif
36 
37 
_SV_IMPL_SORTAR_ALG(SwpHtStart,SwTxtAttr *)38 _SV_IMPL_SORTAR_ALG( SwpHtStart, SwTxtAttr* )
39 _SV_IMPL_SORTAR_ALG( SwpHtEnd, SwTxtAttr* )
40 
41 #ifdef NIE
42 
43 void DumpHints( const SwpHtStart &rHtStart,
44                 const SwpHtEnd &rHtEnd )
45 {
46 #ifdef DBG_UTIL
47     aDbstream << "DumpHints:" << endl;
48     (aDbstream << "\tStarts:" ).WriteNumber(rHtStart.Count()) << endl;
49     for( sal_uInt16 i = 0; i < rHtStart.Count(); ++i )
50     {
51         const SwTxtAttr *pHt = rHtStart[i];
52         ((((aDbstream << '\t').WriteNumber( i )<< " [").WriteNumber( pHt->Which() )
53             << ']' << '\t').WriteNumber( long( pHt ) )
54                   << '\t').WriteNumber( *pHt->GetStart() );
55         if( pHt->End() )
56             (aDbstream << " -> " ).WriteNumber( *pHt->End() );
57         aDbstream << endl;
58     }
59     (aDbstream << "\tEnds:").WriteNumber( rHtEnd.Count() )<< endl;
60     for( i = 0; i < rHtEnd.Count(); ++i )
61     {
62         const SwTxtAttr *pHt = rHtEnd[i];
63         (((aDbstream << '\t').WriteNumber( i )<< " [").WriteNumber( pHt->Which() )
64             << ']' << '\t' ).WriteNumber( long( pHt ) );
65         if( pHt->End() )
66             (aDbstream << '\t').WriteNumber( *pHt->End() )<< " <- ";
67         aDbstream.WriteNumber( *pHt->GetStart() )<< endl;
68     }
69     aDbstream << endl;
70 #endif
71 }
72 #else
73 inline void DumpHints(const SwpHtStart &, const SwpHtEnd &) { }
74 #endif
75 
76 /*************************************************************************
77  *                        inline IsEqual()
78  *************************************************************************/
79 
IsEqual(const SwTxtAttr & rHt1,const SwTxtAttr & rHt2)80 inline sal_Bool IsEqual( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 )
81 {
82     return (long)(&rHt1) == (long)(&rHt2);
83 }
84 
85 /*************************************************************************
86  *                      IsLessStart()
87  *************************************************************************/
88 
89 // SV_IMPL_OP_PTRARR_SORT( SwpHtStart, SwTxtAttr* )
90 // kein SV_IMPL_PTRARR_SORT( name,ArrElement )
91 // unser SEEK_PTR_TO_OBJECT_NOTL( name,ArrElement )
92 
93 // Sortierreihenfolge: Start, Ende (umgekehrt!), Which-Wert (umgekehrt!),
94 //                     als letztes die Adresse selbst
95 
lcl_IsLessStart(const SwTxtAttr & rHt1,const SwTxtAttr & rHt2)96 static sal_Bool lcl_IsLessStart( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 )
97 {
98     if ( *rHt1.GetStart() == *rHt2.GetStart() )
99     {
100         const xub_StrLen nHt1 = *rHt1.GetAnyEnd();
101         const xub_StrLen nHt2 = *rHt2.GetAnyEnd();
102         if ( nHt1 == nHt2 )
103         {
104             const sal_uInt16 nWhich1 = rHt1.Which();
105             const sal_uInt16 nWhich2 = rHt2.Which();
106             if ( nWhich1 == nWhich2 )
107             {
108                 if ( RES_TXTATR_CHARFMT == nWhich1 )
109                 {
110                     const sal_uInt16 nS1 = static_cast<const SwTxtCharFmt&>(rHt1).GetSortNumber();
111                     const sal_uInt16 nS2 = static_cast<const SwTxtCharFmt&>(rHt2).GetSortNumber();
112                     ASSERT( nS1 != nS2, "AUTOSTYLES: lcl_IsLessStart trouble" )
113                     if ( nS1 != nS2 ) // robust
114                         return nS1 < nS2;
115                 }
116 
117                 return (long)&rHt1 < (long)&rHt2;
118             }
119             // order is important! for requirements see hintids.hxx
120             return ( nWhich1 > nWhich2 );
121         }
122         return ( nHt1 > nHt2 );
123     }
124     return ( *rHt1.GetStart() < *rHt2.GetStart() );
125 }
126 
127 /*************************************************************************
128  *                      inline IsLessEnd()
129  *************************************************************************/
130 
131 // Zuerst nach Ende danach nach Ptr
lcl_IsLessEnd(const SwTxtAttr & rHt1,const SwTxtAttr & rHt2)132 static sal_Bool lcl_IsLessEnd( const SwTxtAttr &rHt1, const SwTxtAttr &rHt2 )
133 {
134     const xub_StrLen nHt1 = *rHt1.GetAnyEnd();
135     const xub_StrLen nHt2 = *rHt2.GetAnyEnd();
136     if ( nHt1 == nHt2 )
137     {
138         if ( *rHt1.GetStart() == *rHt2.GetStart() )
139         {
140             const sal_uInt16 nWhich1 = rHt1.Which();
141             const sal_uInt16 nWhich2 = rHt2.Which();
142             if ( nWhich1 == nWhich2 )
143             {
144                 if ( RES_TXTATR_CHARFMT == nWhich1 )
145                 {
146                     const sal_uInt16 nS1 = static_cast<const SwTxtCharFmt&>(rHt1).GetSortNumber();
147                     const sal_uInt16 nS2 = static_cast<const SwTxtCharFmt&>(rHt2).GetSortNumber();
148                     ASSERT( nS1 != nS2, "AUTOSTYLES: lcl_IsLessEnd trouble" )
149                     if ( nS1 != nS2 ) // robust
150                         return nS1 > nS2;
151                 }
152 
153                 return (long)&rHt1 > (long)&rHt2;
154             }
155             // order is important! for requirements see hintids.hxx
156             return ( nWhich1 < nWhich2 );
157         }
158         else
159             return ( *rHt1.GetStart() > *rHt2.GetStart() );
160     }
161     return ( nHt1 < nHt2 );
162 }
163 
164 /*************************************************************************
165  *                      SwpHtStart::Seek_Entry()
166  *************************************************************************/
167 
Seek_Entry(const SwTxtAttr * pElement,sal_uInt16 * pPos) const168 sal_Bool SwpHtStart::Seek_Entry( const SwTxtAttr *pElement, sal_uInt16 *pPos ) const
169 {
170     sal_uInt16 nOben = Count(), nMitte, nUnten = 0;
171     if( nOben > 0 )
172     {
173         nOben--;
174         while( nUnten <= nOben )
175         {
176             nMitte = nUnten + ( nOben - nUnten ) / 2;
177             const SwTxtAttr *pMitte = (*this)[nMitte];
178             if( IsEqual( *pMitte, *pElement ) )
179             {
180                 *pPos = nMitte;
181                 return sal_True;
182             }
183             else
184                 if( lcl_IsLessStart( *pMitte, *pElement ) )
185                     nUnten = nMitte + 1;
186                 else
187                     if( nMitte == 0 )
188                     {
189                         *pPos = nUnten;
190                         return sal_False;
191                     }
192                     else
193                         nOben = nMitte - 1;
194         }
195     }
196     *pPos = nUnten;
197     return sal_False;
198 }
199 
200 /*************************************************************************
201  *                      SwpHtEnd::Seek_Entry()
202  *************************************************************************/
203 
Seek_Entry(const SwTxtAttr * pElement,sal_uInt16 * pPos) const204 sal_Bool SwpHtEnd::Seek_Entry( const SwTxtAttr *pElement, sal_uInt16 *pPos ) const
205 {
206     sal_uInt16 nOben = Count(), nMitte, nUnten = 0;
207     if( nOben > 0 )
208     {
209         nOben--;
210         while( nUnten <= nOben )
211         {
212             nMitte = nUnten + ( nOben - nUnten ) / 2;
213             const SwTxtAttr *pMitte = (*this)[nMitte];
214             if( IsEqual( *pMitte, *pElement ) )
215             {
216                 *pPos = nMitte;
217                 return sal_True;
218             }
219             else
220                 if( lcl_IsLessEnd( *pMitte, *pElement ) )
221                     nUnten = nMitte + 1;
222                 else
223                     if( nMitte == 0 )
224                     {
225                         *pPos = nUnten;
226                         return sal_False;
227                     }
228                     else
229                         nOben = nMitte - 1;
230         }
231     }
232     *pPos = nUnten;
233     return sal_False;
234 }
235 
236 /*************************************************************************
237  *                      class SwpHintsArr
238  *************************************************************************/
239 
Insert(const SwTxtAttr * pHt)240 void SwpHintsArray::Insert( const SwTxtAttr *pHt )
241 {
242     Resort();
243 #ifdef DBG_UTIL
244     sal_uInt16 nPos;
245     ASSERT(!m_HintStarts.Seek_Entry( pHt, &nPos ),
246             "Insert: hint already in HtStart");
247     ASSERT(!m_HintEnds.Seek_Entry( pHt, &nPos ),
248             "Insert: hint already in HtEnd");
249 #endif
250     m_HintStarts.Insert( pHt );
251     m_HintEnds.Insert( pHt );
252 #ifdef DBG_UTIL
253 #ifdef NIE
254     (aDbstream << "Insert: " ).WriteNumber( long( pHt ) ) << endl;
255     DumpHints( m_HintStarts, m_HintEnds );
256 #endif
257 #endif
258 }
259 
DeleteAtPos(const sal_uInt16 nPos)260 void SwpHintsArray::DeleteAtPos( const sal_uInt16 nPos )
261 {
262     // optimization: nPos is the position in the Starts array
263     const SwTxtAttr *pHt = m_HintStarts[ nPos ];
264     m_HintStarts.Remove( nPos );
265 
266     Resort();
267 
268     sal_uInt16 nEndPos;
269     m_HintEnds.Seek_Entry( pHt, &nEndPos );
270     m_HintEnds.Remove( nEndPos );
271 #ifdef DBG_UTIL
272 #ifdef NIE
273     (aDbstream << "DeleteAtPos: " ).WriteNumber( long( pHt ) ) << endl;
274     DumpHints( m_HintStarts, m_HintEnds );
275 #endif
276 #endif
277 }
278 
279 #ifdef DBG_UTIL
280 
281 /*************************************************************************
282  *                      SwpHintsArray::Check()
283  *************************************************************************/
284 
285 
286 #define CHECK_ERR(cond, text) \
287         if(!(cond)) \
288         { \
289             ASSERT(!this, text); \
290             DumpHints(m_HintStarts, m_HintEnds); \
291             return !(const_cast<SwpHintsArray*>(this))->Resort(); \
292         }
293 
Check() const294 bool SwpHintsArray::Check() const
295 {
296     // 1) gleiche Anzahl in beiden Arrays
297     CHECK_ERR( m_HintStarts.Count() == m_HintEnds.Count(),
298         "HintsCheck: wrong sizes" );
299     xub_StrLen nLastStart = 0;
300     xub_StrLen nLastEnd   = 0;
301 
302     const SwTxtAttr *pLastStart = 0;
303     const SwTxtAttr *pLastEnd = 0;
304 
305     for( sal_uInt16 i = 0; i < Count(); ++i )
306     {
307         // --- Start-Kontrolle ---
308 
309         // 2a) gueltiger Pointer? vgl. DELETEFF
310         const SwTxtAttr *pHt = m_HintStarts[i];
311         CHECK_ERR( 0xFF != *(unsigned char*)pHt, "HintsCheck: start ptr was deleted" );
312 
313         // 3a) Stimmt die Start-Sortierung?
314         xub_StrLen nIdx = *pHt->GetStart();
315         CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
316 
317         // 4a) IsLessStart-Konsistenz
318         if( pLastStart )
319             CHECK_ERR( lcl_IsLessStart( *pLastStart, *pHt ), "HintsCheck: IsLastStart" );
320 
321         nLastStart = nIdx;
322         pLastStart = pHt;
323 
324         // --- End-Kontrolle ---
325 
326         // 2b) gueltiger Pointer? vgl. DELETEFF
327         const SwTxtAttr *pHtEnd = m_HintEnds[i];
328         CHECK_ERR( 0xFF != *(unsigned char*)pHtEnd, "HintsCheck: end ptr was deleted" );
329 
330         // 3b) Stimmt die End-Sortierung?
331         nIdx = *pHtEnd->GetAnyEnd();
332         CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
333         nLastEnd = nIdx;
334 
335         // 4b) IsLessEnd-Konsistenz
336         if( pLastEnd )
337             CHECK_ERR( lcl_IsLessEnd( *pLastEnd, *pHtEnd ), "HintsCheck: IsLastEnd" );
338 
339         nLastEnd = nIdx;
340         pLastEnd = pHtEnd;
341 
342         // --- Ueberkreuzungen ---
343 
344         // 5) gleiche Pointer in beiden Arrays
345         if( !m_HintStarts.Seek_Entry( pHt, &nIdx ) )
346             nIdx = STRING_LEN;
347 
348         CHECK_ERR( STRING_LEN != nIdx, "HintsCheck: no GetStartOf" );
349 
350         // 6) gleiche Pointer in beiden Arrays
351         if( !m_HintEnds.Seek_Entry( pHt, &nIdx ) )
352             nIdx = STRING_LEN;
353 
354         CHECK_ERR( STRING_LEN != nIdx, "HintsCheck: no GetEndOf" );
355 
356         // 7a) character attributes in array?
357         sal_uInt16 nWhich = pHt->Which();
358         CHECK_ERR( !isCHRATR(nWhich),
359                    "HintsCheck: Character attribute in start array" );
360 
361         // 7b) character attributes in array?
362         nWhich = pHtEnd->Which();
363         CHECK_ERR( !isCHRATR(nWhich),
364                    "HintsCheck: Character attribute in end array" );
365 
366         // 8) style portion check
367 #if OSL_DEBUG_LEVEL > 1
368         const SwTxtAttr* pHtThis = m_HintStarts[i];
369         const SwTxtAttr* pHtLast = i > 0 ? m_HintStarts[i-1] : 0;
370         CHECK_ERR( 0 == i ||
371                     ( RES_TXTATR_CHARFMT != pHtLast->Which() && RES_TXTATR_AUTOFMT != pHtLast->Which() ) ||
372                     ( RES_TXTATR_CHARFMT != pHtThis->Which() && RES_TXTATR_AUTOFMT != pHtThis->Which() ) ||
373                     ( *pHtThis->GetStart() >= *pHtLast->End() ) ||
374                     (   (   (   (*pHtThis->GetStart() == *pHtLast->GetStart())
375                             &&  (*pHtThis->End()   == *pHtLast->End())
376                             ) // same range
377                         ||  (*pHtThis->GetStart() == *pHtThis->End())
378                         )
379                     &&  (   (pHtThis->Which() != RES_TXTATR_AUTOFMT)
380                         ||  (pHtLast->Which() != RES_TXTATR_AUTOFMT)
381                         ) // never two AUTOFMT on same range
382                     ),
383                    "HintsCheck: Portion inconsistency. "
384                    "This can be temporarily ok during undo operations" );
385 
386         // 9) nesting portion check
387         if (pHtThis->IsNesting())
388         {
389             for ( sal_uInt16 j = 0; j < Count(); ++j )
390             {
391                 SwTxtAttr const * const pOther( m_HintStarts[j] );
392                 if ( pOther->IsNesting() &&  (i != j) )
393                 {
394                     SwComparePosition cmp = ComparePosition(
395                         *pHtThis->GetStart(), *pHtThis->End(),
396                         *pOther->GetStart(), *pOther->End());
397                     CHECK_ERR( (POS_OVERLAP_BEFORE != cmp) &&
398                                (POS_OVERLAP_BEHIND != cmp),
399                         "HintsCheck: overlapping nesting hints!!!" );
400                 }
401             }
402         }
403 
404         // 10) dummy char check (unfortunately cannot check SwTxtNode::m_Text)
405         if (pHtThis->HasDummyChar())
406         {
407             for ( sal_uInt16 j = 0; j < i; ++j )
408             {
409                 SwTxtAttr const * const pOther( m_HintStarts[j] );
410                 if (pOther->HasDummyChar())
411                 {
412                     CHECK_ERR( (*pOther->GetStart() != *pHtThis->GetStart()),
413                         "HintsCheck: multiple hints claim same CH_TXTATR!");
414                 }
415             }
416         }
417 #endif
418     }
419     return true;
420 }
421 
422 #endif      /* PRODUCT */
423 
424 /*************************************************************************
425  *                          SwpHintsArray::Resort()
426  *************************************************************************/
427 
428 // Resort() wird vor jedem Insert und Delete gerufen.
429 // Wenn Textmasse geloescht wird, so werden die Indizes in
430 // ndtxt.cxx angepasst. Leider erfolgt noch keine Neusortierung
431 // auf gleichen Positionen.
432 
Resort()433 bool SwpHintsArray::Resort()
434 {
435     bool bResort = false;
436     const SwTxtAttr *pLast = 0;
437     sal_uInt16 i;
438 
439     for ( i = 0; i < m_HintStarts.Count(); ++i )
440     {
441         const SwTxtAttr *pHt = m_HintStarts[i];
442         if( pLast && !lcl_IsLessStart( *pLast, *pHt ) )
443         {
444 #ifdef NIE
445 #ifdef DBG_UTIL
446 //            ASSERT( bResort, "!Resort/Start: correcting hints-array" );
447             aDbstream << "Resort: Starts" << endl;
448             DumpHints( m_HintStarts, m_HintEnds );
449 #endif
450 #endif
451             m_HintStarts.Remove( i );
452             m_HintStarts.Insert( pHt );
453             pHt = m_HintStarts[i];
454             if ( pHt != pLast )
455                 --i;
456             bResort = true;
457         }
458         pLast = pHt;
459     }
460 
461     pLast = 0;
462     for ( i = 0; i < m_HintEnds.Count(); ++i )
463     {
464         const SwTxtAttr *pHt = m_HintEnds[i];
465         if( pLast && !lcl_IsLessEnd( *pLast, *pHt ) )
466         {
467 #ifdef NIE
468 #ifdef DBG_UTIL
469             aDbstream << "Resort: Ends" << endl;
470             DumpHints( m_HintStarts, m_HintEnds );
471 #endif
472 #endif
473             m_HintEnds.Remove( i );
474             m_HintEnds.Insert( pHt );
475             pHt = m_HintEnds[i]; // normalerweise == pLast
476             // Wenn die Unordnung etwas groesser ist (24200),
477             // muessen wir Position i erneut vergleichen.
478             if ( pLast != pHt )
479                 --i;
480             bResort = true;
481         }
482         pLast = pHt;
483     }
484 #ifdef DBG_UTIL
485 #ifdef NIE
486     aDbstream << "Resorted:" << endl;
487     DumpHints( m_HintStarts, m_HintEnds );
488 #endif
489 #endif
490     return bResort;
491 }
492 
493 
494