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 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 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 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 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 168 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 204 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 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 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 294 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 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