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