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