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 // So kann man die Linguistik-Statistik ( (Tmp-Path)\swlingu.stk ) aktivieren: 28 //#define LINGU_STATISTIK 29 #ifdef LINGU_STATISTIK 30 #include <stdio.h> // in SwLinguStatistik::DTOR 31 #include <stdlib.h> // getenv() 32 #include <time.h> // clock() 33 #include <tools/stream.hxx> 34 #endif 35 36 #include <hintids.hxx> 37 #include <vcl/svapp.hxx> 38 #include <svl/itemiter.hxx> 39 #include <editeng/splwrap.hxx> 40 #include <editeng/langitem.hxx> 41 #include <editeng/fontitem.hxx> 42 #include <editeng/scripttypeitem.hxx> 43 #include <editeng/hangulhanja.hxx> 44 #include <SwSmartTagMgr.hxx> 45 #include <linguistic/lngprops.hxx> 46 #include <unotools/transliterationwrapper.hxx> 47 #include <unotools/charclass.hxx> 48 #include <dlelstnr.hxx> 49 #include <swmodule.hxx> 50 #include <splargs.hxx> 51 #include <viewopt.hxx> 52 #include <acmplwrd.hxx> 53 #include <doc.hxx> // GetDoc() 54 #include <docsh.hxx> 55 #include <txtfld.hxx> 56 #include <fmtfld.hxx> 57 #include <txatbase.hxx> 58 #include <charatr.hxx> 59 #include <fldbas.hxx> 60 #include <pam.hxx> 61 #include <hints.hxx> 62 #include <ndtxt.hxx> 63 #include <txtfrm.hxx> 64 #include <SwGrammarMarkUp.hxx> 65 66 #include <txttypes.hxx> 67 #include <breakit.hxx> 68 #include <crstate.hxx> 69 #include <UndoOverwrite.hxx> 70 #include <txatritr.hxx> 71 #include <redline.hxx> // SwRedline 72 #include <docary.hxx> // SwRedlineTbl 73 #include <scriptinfo.hxx> 74 #include <docstat.hxx> 75 #include <editsh.hxx> 76 #include <unotextmarkup.hxx> 77 #include <txtatr.hxx> 78 #include <fmtautofmt.hxx> 79 #include <istyleaccess.hxx> 80 81 #include <unomid.h> 82 83 #include <com/sun/star/beans/XPropertySet.hpp> 84 #include <com/sun/star/i18n/WordType.hdl> 85 #include <com/sun/star/i18n/ScriptType.hdl> 86 #include <com/sun/star/i18n/TransliterationModules.hpp> 87 #include <com/sun/star/i18n/TransliterationModulesExtra.hpp> 88 89 #include <vector> 90 91 #include <unotextrange.hxx> 92 93 using rtl::OUString; 94 using namespace ::com::sun::star; 95 using namespace ::com::sun::star::frame; 96 using namespace ::com::sun::star::i18n; 97 using namespace ::com::sun::star::beans; 98 using namespace ::com::sun::star::uno; 99 using namespace ::com::sun::star::linguistic2; 100 using namespace ::com::sun::star::smarttags; 101 102 // Wir ersparen uns in Hyphenate ein GetFrm() 103 // Achtung: in edlingu.cxx stehen die Variablen! 104 extern const SwTxtNode *pLinguNode; 105 extern SwTxtFrm *pLinguFrm; 106 107 bool lcl_IsSkippableWhiteSpace( xub_Unicode cCh ) 108 { 109 return 0x3000 == cCh || 110 ' ' == cCh || 111 '\t' == cCh || 112 0x0a == cCh; 113 } 114 115 /* 116 * This has basically the same function as SwScriptInfo::MaskHiddenRanges, 117 * only for deleted redlines 118 */ 119 120 sal_uInt16 lcl_MaskRedlines( const SwTxtNode& rNode, XubString& rText, 121 const xub_StrLen nStt, const xub_StrLen nEnd, 122 const xub_Unicode cChar ) 123 { 124 sal_uInt16 nNumOfMaskedRedlines = 0; 125 126 const SwDoc& rDoc = *rNode.GetDoc(); 127 sal_uInt16 nAct = rDoc.GetRedlinePos( rNode, USHRT_MAX ); 128 129 for ( ; nAct < rDoc.GetRedlineTbl().Count(); nAct++ ) 130 { 131 const SwRedline* pRed = rDoc.GetRedlineTbl()[ nAct ]; 132 133 if ( pRed->Start()->nNode > rNode.GetIndex() ) 134 break; 135 136 if( nsRedlineType_t::REDLINE_DELETE == pRed->GetType() ) 137 { 138 xub_StrLen nRedlineEnd; 139 xub_StrLen nRedlineStart; 140 141 pRed->CalcStartEnd( rNode.GetIndex(), nRedlineStart, nRedlineEnd ); 142 143 if ( nRedlineEnd < nStt || nRedlineStart > nEnd ) 144 continue; 145 146 while ( nRedlineStart < nRedlineEnd && nRedlineStart < nEnd ) 147 { 148 if ( nRedlineStart >= nStt && nRedlineStart < nEnd ) 149 { 150 rText.SetChar( nRedlineStart, cChar ); 151 ++nNumOfMaskedRedlines; 152 } 153 ++nRedlineStart; 154 } 155 } 156 } 157 158 return nNumOfMaskedRedlines; 159 } 160 161 /* 162 * Used for spell checking. Deleted redlines and hidden characters are masked 163 */ 164 165 sal_uInt16 lcl_MaskRedlinesAndHiddenText( const SwTxtNode& rNode, XubString& rText, 166 const xub_StrLen nStt, const xub_StrLen nEnd, 167 const xub_Unicode cChar = CH_TXTATR_INWORD, 168 bool bCheckShowHiddenChar = true ) 169 { 170 sal_uInt16 nRedlinesMasked = 0; 171 sal_uInt16 nHiddenCharsMasked = 0; 172 173 const SwDoc& rDoc = *rNode.GetDoc(); 174 const bool bShowChg = 0 != IDocumentRedlineAccess::IsShowChanges( rDoc.GetRedlineMode() ); 175 176 // If called from word count or from spell checking, deleted redlines 177 // should be masked: 178 if ( bShowChg ) 179 { 180 nRedlinesMasked = lcl_MaskRedlines( rNode, rText, nStt, nEnd, cChar ); 181 } 182 183 const bool bHideHidden = !SW_MOD()->GetViewOption(rDoc.get(IDocumentSettingAccess::HTML_MODE))->IsShowHiddenChar(); 184 185 // If called from word count, we want to mask the hidden ranges even 186 // if they are visible: 187 if ( !bCheckShowHiddenChar || bHideHidden ) 188 { 189 nHiddenCharsMasked = 190 SwScriptInfo::MaskHiddenRanges( rNode, rText, nStt, nEnd, cChar ); 191 } 192 193 return nRedlinesMasked + nHiddenCharsMasked; 194 } 195 196 /* 197 * Used for spell checking. Calculates a rectangle for repaint. 198 */ 199 200 static SwRect lcl_CalculateRepaintRect( SwTxtFrm& rTxtFrm, xub_StrLen nChgStart, xub_StrLen nChgEnd ) 201 { 202 SwRect aRect; 203 204 SwTxtNode *pNode = rTxtFrm.GetTxtNode(); 205 206 SwNodeIndex aNdIdx( *pNode ); 207 SwPosition aPos( aNdIdx, SwIndex( pNode, nChgEnd ) ); 208 SwCrsrMoveState aTmpState( MV_NONE ); 209 aTmpState.b2Lines = sal_True; 210 rTxtFrm.GetCharRect( aRect, aPos, &aTmpState ); 211 // information about end of repaint area 212 Sw2LinesPos* pEnd2Pos = aTmpState.p2Lines; 213 214 const SwTxtFrm *pEndFrm = &rTxtFrm; 215 216 while( pEndFrm->HasFollow() && 217 nChgEnd >= pEndFrm->GetFollow()->GetOfst() ) 218 pEndFrm = pEndFrm->GetFollow(); 219 220 if ( pEnd2Pos ) 221 { 222 // we are inside a special portion, take left border 223 SWRECTFN( pEndFrm ) 224 (aRect.*fnRect->fnSetTop)( (pEnd2Pos->aLine.*fnRect->fnGetTop)() ); 225 if ( pEndFrm->IsRightToLeft() ) 226 (aRect.*fnRect->fnSetLeft)( (pEnd2Pos->aPortion.*fnRect->fnGetLeft)() ); 227 else 228 (aRect.*fnRect->fnSetLeft)( (pEnd2Pos->aPortion.*fnRect->fnGetRight)() ); 229 (aRect.*fnRect->fnSetWidth)( 1 ); 230 (aRect.*fnRect->fnSetHeight)( (pEnd2Pos->aLine.*fnRect->fnGetHeight)() ); 231 delete pEnd2Pos; 232 } 233 234 aTmpState.p2Lines = NULL; 235 SwRect aTmp; 236 aPos = SwPosition( aNdIdx, SwIndex( pNode, nChgStart ) ); 237 rTxtFrm.GetCharRect( aTmp, aPos, &aTmpState ); 238 239 // i63141: GetCharRect(..) could cause a formatting, 240 // during the formatting SwTxtFrms could be joined, deleted, created... 241 // => we have to reinit pStartFrm and pEndFrm after the formatting 242 const SwTxtFrm* pStartFrm = &rTxtFrm; 243 while( pStartFrm->HasFollow() && 244 nChgStart >= pStartFrm->GetFollow()->GetOfst() ) 245 pStartFrm = pStartFrm->GetFollow(); 246 pEndFrm = pStartFrm; 247 while( pEndFrm->HasFollow() && 248 nChgEnd >= pEndFrm->GetFollow()->GetOfst() ) 249 pEndFrm = pEndFrm->GetFollow(); 250 251 // information about start of repaint area 252 Sw2LinesPos* pSt2Pos = aTmpState.p2Lines; 253 if ( pSt2Pos ) 254 { 255 // we are inside a special portion, take right border 256 SWRECTFN( pStartFrm ) 257 (aTmp.*fnRect->fnSetTop)( (pSt2Pos->aLine.*fnRect->fnGetTop)() ); 258 if ( pStartFrm->IsRightToLeft() ) 259 (aTmp.*fnRect->fnSetLeft)( (pSt2Pos->aPortion.*fnRect->fnGetRight)() ); 260 else 261 (aTmp.*fnRect->fnSetLeft)( (pSt2Pos->aPortion.*fnRect->fnGetLeft)() ); 262 (aTmp.*fnRect->fnSetWidth)( 1 ); 263 (aTmp.*fnRect->fnSetHeight)( (pSt2Pos->aLine.*fnRect->fnGetHeight)() ); 264 delete pSt2Pos; 265 } 266 267 sal_Bool bSameFrame = sal_True; 268 269 if( rTxtFrm.HasFollow() ) 270 { 271 if( pEndFrm != pStartFrm ) 272 { 273 bSameFrame = sal_False; 274 SwRect aStFrm( pStartFrm->PaintArea() ); 275 { 276 SWRECTFN( pStartFrm ) 277 (aTmp.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); 278 (aTmp.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); 279 (aTmp.*fnRect->fnSetBottom)( (aStFrm.*fnRect->fnGetBottom)() ); 280 } 281 aStFrm = pEndFrm->PaintArea(); 282 { 283 SWRECTFN( pEndFrm ) 284 (aRect.*fnRect->fnSetTop)( (aStFrm.*fnRect->fnGetTop)() ); 285 (aRect.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); 286 (aRect.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); 287 } 288 aRect.Union( aTmp ); 289 while( sal_True ) 290 { 291 pStartFrm = pStartFrm->GetFollow(); 292 if( pStartFrm == pEndFrm ) 293 break; 294 aRect.Union( pStartFrm->PaintArea() ); 295 } 296 } 297 } 298 if( bSameFrame ) 299 { 300 SWRECTFN( pStartFrm ) 301 if( (aTmp.*fnRect->fnGetTop)() == (aRect.*fnRect->fnGetTop)() ) 302 (aRect.*fnRect->fnSetLeft)( (aTmp.*fnRect->fnGetLeft)() ); 303 else 304 { 305 SwRect aStFrm( pStartFrm->PaintArea() ); 306 (aRect.*fnRect->fnSetLeft)( (aStFrm.*fnRect->fnGetLeft)() ); 307 (aRect.*fnRect->fnSetRight)( (aStFrm.*fnRect->fnGetRight)() ); 308 (aRect.*fnRect->fnSetTop)( (aTmp.*fnRect->fnGetTop)() ); 309 } 310 311 if( aTmp.Height() > aRect.Height() ) 312 aRect.Height( aTmp.Height() ); 313 } 314 315 return aRect; 316 } 317 318 /* 319 * Used for automatic styles. Used during RstAttr. 320 */ 321 322 static bool lcl_HaveCommonAttributes( IStyleAccess& rStyleAccess, 323 const SfxItemSet* pSet1, 324 sal_uInt16 nWhichId, 325 const SfxItemSet& rSet2, 326 boost::shared_ptr<SfxItemSet>& pStyleHandle ) 327 { 328 bool bRet = false; 329 330 SfxItemSet* pNewSet = 0; 331 332 if ( !pSet1 ) 333 { 334 ASSERT( nWhichId, "lcl_HaveCommonAttributes not used correctly" ) 335 if ( SFX_ITEM_SET == rSet2.GetItemState( nWhichId, sal_False ) ) 336 { 337 pNewSet = rSet2.Clone( sal_True ); 338 pNewSet->ClearItem( nWhichId ); 339 } 340 } 341 else if ( pSet1->Count() ) 342 { 343 SfxItemIter aIter( *pSet1 ); 344 const SfxPoolItem* pItem = aIter.GetCurItem(); 345 while( sal_True ) 346 { 347 if ( SFX_ITEM_SET == rSet2.GetItemState( pItem->Which(), sal_False ) ) 348 { 349 if ( !pNewSet ) 350 pNewSet = rSet2.Clone( sal_True ); 351 pNewSet->ClearItem( pItem->Which() ); 352 } 353 354 if( aIter.IsAtEnd() ) 355 break; 356 357 pItem = aIter.NextItem(); 358 } 359 } 360 361 if ( pNewSet ) 362 { 363 if ( pNewSet->Count() ) 364 pStyleHandle = rStyleAccess.getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR ); 365 delete pNewSet; 366 bRet = true; 367 } 368 369 return bRet; 370 } 371 372 inline sal_Bool InRange(xub_StrLen nIdx, xub_StrLen nStart, xub_StrLen nEnd) { 373 return ((nIdx >=nStart) && (nIdx <= nEnd)); 374 } 375 376 /* 377 * void SwTxtNode::RstAttr(const SwIndex &rIdx, sal_uInt16 nLen) 378 * 379 * Deletes all attributes, starting at position rIdx, for length nLen. 380 */ 381 382 /* 5 cases: 383 * 1) The attribute is completely in the deletion range: 384 * -> delete it 385 * 2) The end of the attribute is in the deletion range: 386 * -> delete it, then re-insert it with new end 387 * 3) The start of the attribute is in the deletion range: 388 * -> delete it, then re-insert it with new start 389 * 4) The attribute contains the deletion range: 390 * Split, i.e., 391 * -> Delete, re-insert from old start to start of deletion range 392 * -> insert new attribute from end of deletion range to old end 393 * 5) The attribute is outside the deletion range 394 * -> nothing to do 395 */ 396 397 void SwTxtNode::RstAttr( 398 const SwIndex &rIdx, 399 const xub_StrLen nLen, 400 const sal_uInt16 nWhich, 401 const SfxItemSet* pSet, 402 const sal_Bool bInclRefToxMark ) 403 { 404 if ( !GetpSwpHints() ) 405 return; 406 407 const xub_StrLen nStt = rIdx.GetIndex(); 408 const xub_StrLen nEnd = nStt + nLen; 409 410 bool bChanged = false; 411 412 // nMin and nMax initialized to maximum / minimum (inverse) 413 xub_StrLen nMin = m_Text.Len(); 414 xub_StrLen nMax = nStt; 415 const bool bNoLen = nMin == 0; 416 417 // We have to remember the "new" attributes, which have 418 // been introduced by splitting surrounding attributes (case 4). 419 // They may not be forgotten inside the "Forget" function 420 //std::vector< const SwTxtAttr* > aNewAttributes; 421 422 // iterate over attribute array until start of attribute is behind 423 // deletion range 424 sal_uInt16 i = 0; 425 xub_StrLen nAttrStart; 426 SwTxtAttr *pHt = NULL; 427 while ((i < m_pSwpHints->Count()) && 428 ((( nAttrStart = *(*m_pSwpHints)[i]->GetStart()) < nEnd ) || nLen==0) ) 429 { 430 pHt = m_pSwpHints->GetTextHint(i); 431 432 // attributes without end stay in! 433 // but consider <bInclRefToxMark> used by Undo 434 xub_StrLen* const pAttrEnd = pHt->GetEnd(); 435 const bool bKeepAttrWithoutEnd = 436 pAttrEnd == NULL 437 && ( !bInclRefToxMark 438 || ( RES_TXTATR_REFMARK != pHt->Which() 439 && RES_TXTATR_TOXMARK != pHt->Which() 440 && RES_TXTATR_META != pHt->Which() 441 && RES_TXTATR_METAFIELD != pHt->Which() ) ); 442 if ( bKeepAttrWithoutEnd ) 443 { 444 445 i++; 446 continue; 447 } 448 449 // Default behavior is to process all attributes: 450 bool bSkipAttr = false;; 451 boost::shared_ptr<SfxItemSet> pStyleHandle; 452 453 // 1. case: We want to reset only the attributes listed in pSet: 454 if ( pSet ) 455 { 456 bSkipAttr = SFX_ITEM_SET != pSet->GetItemState( pHt->Which(), sal_False ); 457 if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() ) 458 { 459 // if the current attribute is an autostyle, we have to check if the autostyle 460 // and pSet have any attributes in common. If so, pStyleHandle will contain 461 // a handle to AutoStyle / pSet: 462 bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), pSet, 0, *static_cast<const SwFmtAutoFmt&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle ); 463 } 464 } 465 else if ( nWhich ) 466 { 467 // 2. case: We want to reset only the attributes with WhichId nWhich: 468 bSkipAttr = nWhich != pHt->Which(); 469 if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() ) 470 { 471 bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), 0, nWhich, *static_cast<const SwFmtAutoFmt&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle ); 472 } 473 } 474 else if ( !bInclRefToxMark ) 475 { 476 // 3. case: Reset all attributes except from ref/toxmarks: 477 // skip hints with CH_TXTATR here 478 // (deleting those is ONLY allowed for UNDO!) 479 bSkipAttr = RES_TXTATR_REFMARK == pHt->Which() 480 || RES_TXTATR_TOXMARK == pHt->Which() 481 || RES_TXTATR_META == pHt->Which() 482 || RES_TXTATR_METAFIELD == pHt->Which(); 483 } 484 485 if ( bSkipAttr ) 486 { 487 i++; 488 continue; 489 } 490 491 492 if( nStt <= nAttrStart ) // Faelle: 1,3,5 493 { 494 const xub_StrLen nAttrEnd = pAttrEnd != NULL 495 ? *pAttrEnd 496 : nAttrStart; 497 if( nEnd > nAttrStart 498 || ( nEnd == nAttrEnd && nEnd == nAttrStart ) ) 499 { 500 // Faelle: 1,3 501 if ( nMin > nAttrStart ) 502 nMin = nAttrStart; 503 if ( nMax < nAttrEnd ) 504 nMax = nAttrEnd; 505 // Falls wir nur ein nichtaufgespanntes Attribut entfernen, 506 // tun wir mal so, als ob sich nichts geaendert hat. 507 bChanged = bChanged || nEnd > nAttrStart || bNoLen; 508 if( nAttrEnd <= nEnd ) // Fall: 1 509 { 510 m_pSwpHints->DeleteAtPos(i); 511 DestroyAttr( pHt ); 512 513 if ( pStyleHandle.get() ) 514 { 515 SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), 516 *pStyleHandle, nAttrStart, nAttrEnd ); 517 InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); 518 } 519 520 // if the last attribute is a Field, the HintsArray is deleted! 521 if ( !m_pSwpHints ) 522 break; 523 524 //JP 26.11.96: 525 // beim DeleteAtPos wird ein Resort ausgefuehrt!! 526 // darum muessen wir wieder bei 0 anfangen!!! 527 // ueber den Fall 3 koennen Attribute nach hinten 528 // verschoben worden sein; damit stimmt jetzt das i 529 // nicht mehr!!! 530 i = 0; 531 532 continue; 533 } 534 else // Fall: 3 535 { 536 m_pSwpHints->NoteInHistory( pHt ); 537 *pHt->GetStart() = nEnd; 538 m_pSwpHints->NoteInHistory( pHt, sal_True ); 539 540 if ( pStyleHandle.get() && nAttrStart < nEnd ) 541 { 542 SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), 543 *pStyleHandle, nAttrStart, nEnd ); 544 InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); 545 } 546 547 bChanged = true; 548 } 549 } 550 } 551 else if ( pAttrEnd != NULL ) // Faelle: 2,4,5 552 { 553 if( *pAttrEnd > nStt ) // Faelle: 2,4 554 { 555 if( *pAttrEnd < nEnd ) // Fall: 2 556 { 557 if ( nMin > nAttrStart ) 558 nMin = nAttrStart; 559 if ( nMax < *pAttrEnd ) 560 nMax = *pAttrEnd; 561 bChanged = true; 562 563 const xub_StrLen nAttrEnd = *pAttrEnd; 564 565 m_pSwpHints->NoteInHistory( pHt ); 566 *pAttrEnd = nStt; 567 m_pSwpHints->NoteInHistory( pHt, sal_True ); 568 569 if ( pStyleHandle.get() ) 570 { 571 SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), 572 *pStyleHandle, nStt, nAttrEnd ); 573 InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); 574 } 575 } 576 else if( nLen ) // Fall: 4 577 { 578 // bei Lange 0 werden beide Hints vom Insert(Ht) 579 // wieder zu einem zusammengezogen !!!! 580 if ( nMin > nAttrStart ) 581 nMin = nAttrStart; 582 if ( nMax < *pAttrEnd ) 583 nMax = *pAttrEnd; 584 bChanged = true; 585 xub_StrLen nTmpEnd = *pAttrEnd; 586 m_pSwpHints->NoteInHistory( pHt ); 587 *pAttrEnd = nStt; 588 m_pSwpHints->NoteInHistory( pHt, sal_True ); 589 590 if ( pStyleHandle.get() && nStt < nEnd ) 591 { 592 SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), 593 *pStyleHandle, nStt, nEnd ); 594 InsertHint( pNew, nsSetAttrMode::SETATTR_NOHINTADJUST ); 595 } 596 597 if( nEnd < nTmpEnd ) 598 { 599 SwTxtAttr* pNew = MakeTxtAttr( *GetDoc(), 600 pHt->GetAttr(), nEnd, nTmpEnd ); 601 if ( pNew ) 602 { 603 SwTxtCharFmt* pCharFmt = dynamic_cast<SwTxtCharFmt*>(pHt); 604 if ( pCharFmt ) 605 static_cast<SwTxtCharFmt*>(pNew)->SetSortNumber( pCharFmt->GetSortNumber() ); 606 607 InsertHint( pNew, 608 nsSetAttrMode::SETATTR_NOHINTADJUST ); 609 } 610 611 612 // jetzt kein i+1, weil das eingefuegte Attribut 613 // ein anderes auf die Position geschoben hat ! 614 continue; 615 } 616 } 617 } 618 ++i; 619 } 620 } 621 622 TryDeleteSwpHints(); 623 if (bChanged) 624 { 625 if ( HasHints() ) 626 { 627 m_pSwpHints->Resort(); 628 } 629 //TxtFrm's reagieren auf aHint, andere auf aNew 630 SwUpdateAttr aHint( nMin, nMax, 0 ); 631 NotifyClients( 0, &aHint ); 632 SwFmtChg aNew( GetFmtColl() ); 633 NotifyClients( 0, &aNew ); 634 } 635 } 636 637 638 639 /************************************************************************* 640 * SwTxtNode::GetCurWord() 641 * 642 * Aktuelles Wort zurueckliefern: 643 * Wir suchen immer von links nach rechts, es wird also das Wort 644 * vor nPos gesucht. Es sei denn, wir befinden uns am Anfang des 645 * Absatzes, dann wird das erste Wort zurueckgeliefert. 646 * Wenn dieses erste Wort nur aus Whitespaces besteht, returnen wir 647 * einen leeren String. 648 *************************************************************************/ 649 650 XubString SwTxtNode::GetCurWord( xub_StrLen nPos ) const 651 { 652 ASSERT( nPos <= m_Text.Len(), "SwTxtNode::GetCurWord: invalid index." ); 653 654 if (!m_Text.Len()) 655 return m_Text; 656 657 Boundary aBndry; 658 const uno::Reference< XBreakIterator > &rxBreak = pBreakIt->GetBreakIter(); 659 if (rxBreak.is()) 660 { 661 sal_Int16 nWordType = WordType::DICTIONARY_WORD; 662 lang::Locale aLocale( pBreakIt->GetLocale( GetLang( nPos ) ) ); 663 #ifdef DEBUG 664 sal_Bool bBegin = rxBreak->isBeginWord( m_Text, nPos, aLocale, nWordType ); 665 sal_Bool bEnd = rxBreak->isEndWord ( m_Text, nPos, aLocale, nWordType ); 666 (void)bBegin; 667 (void)bEnd; 668 #endif 669 aBndry = 670 rxBreak->getWordBoundary( m_Text, nPos, aLocale, nWordType, sal_True ); 671 672 // if no word was found use previous word (if any) 673 if (aBndry.startPos == aBndry.endPos) 674 { 675 aBndry = rxBreak->previousWord( m_Text, nPos, aLocale, nWordType ); 676 } 677 } 678 679 // check if word was found and if it uses a symbol font, if so 680 // enforce returning an empty string 681 if (aBndry.endPos != aBndry.startPos && IsSymbol( (xub_StrLen)aBndry.startPos )) 682 aBndry.endPos = aBndry.startPos; 683 684 return m_Text.Copy( static_cast<xub_StrLen>(aBndry.startPos), 685 static_cast<xub_StrLen>(aBndry.endPos - aBndry.startPos) ); 686 } 687 688 SwScanner::SwScanner( const SwTxtNode& rNd, const String& rTxt, const LanguageType* pLang, 689 const ModelToViewHelper::ConversionMap* pConvMap, 690 sal_uInt16 nType, xub_StrLen nStart, xub_StrLen nEnde, sal_Bool bClp ) 691 : rNode( rNd ), rText( rTxt), pLanguage( pLang ), pConversionMap( pConvMap ), nLen( 0 ), nWordType( nType ), bClip( bClp ) 692 { 693 ASSERT( rText.Len(), "SwScanner: EmptyString" ); 694 nStartPos = nBegin = nStart; 695 nEndPos = nEnde; 696 697 if ( pLanguage ) 698 { 699 aCurrLang = *pLanguage; 700 } 701 else 702 { 703 ModelToViewHelper::ModelPosition aModelBeginPos = ModelToViewHelper::ConvertToModelPosition( pConversionMap, nBegin ); 704 const xub_StrLen nModelBeginPos = (xub_StrLen)aModelBeginPos.mnPos; 705 aCurrLang = rNd.GetLang( nModelBeginPos ); 706 } 707 } 708 709 sal_Bool SwScanner::NextWord() 710 { 711 nBegin = nBegin + nLen; 712 Boundary aBound; 713 714 CharClass& rCC = GetAppCharClass(); 715 lang::Locale aOldLocale = rCC.getLocale(); 716 717 while ( true ) 718 { 719 // skip non-letter characters: 720 while ( nBegin < rText.Len() ) 721 { 722 if ( !lcl_IsSkippableWhiteSpace( rText.GetChar( nBegin ) ) ) 723 { 724 if ( !pLanguage ) 725 { 726 const sal_uInt16 nNextScriptType = pBreakIt->GetBreakIter()->getScriptType( rText, nBegin ); 727 ModelToViewHelper::ModelPosition aModelBeginPos = ModelToViewHelper::ConvertToModelPosition( pConversionMap, nBegin ); 728 const xub_StrLen nBeginModelPos = (xub_StrLen)aModelBeginPos.mnPos; 729 aCurrLang = rNode.GetLang( nBeginModelPos, 1, nNextScriptType ); 730 } 731 732 if ( nWordType != i18n::WordType::WORD_COUNT ) 733 { 734 rCC.setLocale( pBreakIt->GetLocale( aCurrLang ) ); 735 if ( rCC.isLetterNumeric( rText.GetChar( nBegin ) ) ) 736 break; 737 } 738 else 739 break; 740 } 741 ++nBegin; 742 } 743 744 if ( nBegin >= rText.Len() || nBegin >= nEndPos ) 745 return sal_False; 746 747 // get the word boundaries 748 aBound = pBreakIt->GetBreakIter()->getWordBoundary( rText, nBegin, 749 pBreakIt->GetLocale( aCurrLang ), nWordType, sal_True ); 750 ASSERT( aBound.endPos >= aBound.startPos, "broken aBound result" ); 751 752 //no word boundaries could be found 753 if(aBound.endPos == aBound.startPos) 754 return sal_False; 755 756 //if a word before is found it has to be searched for the next 757 if(aBound.endPos == nBegin) 758 ++nBegin; 759 else 760 break; 761 } // end while( true ) 762 763 rCC.setLocale( aOldLocale ); 764 765 // #i89042, as discussed with HDU: don't evaluate script changes for word count. Use whole word. 766 if ( nWordType == i18n::WordType::WORD_COUNT ) 767 { 768 nBegin = Max( static_cast< xub_StrLen >(aBound.startPos), nBegin ); 769 nLen = 0; 770 if (static_cast< xub_StrLen >(aBound.endPos) > nBegin) 771 nLen = static_cast< xub_StrLen >(aBound.endPos) - nBegin; 772 } 773 else 774 { 775 // we have to differenciate between these cases: 776 if ( aBound.startPos <= nBegin ) 777 { 778 ASSERT( aBound.endPos >= nBegin, "Unexpected aBound result" ) 779 780 // restrict boundaries to script boundaries and nEndPos 781 const sal_uInt16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( rText, nBegin ); 782 XubString aTmpWord = rText.Copy( nBegin, static_cast<xub_StrLen>(aBound.endPos - nBegin) ); 783 const sal_Int32 nScriptEnd = nBegin + 784 pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript ); 785 const sal_Int32 nEnd = Min( aBound.endPos, nScriptEnd ); 786 787 // restrict word start to last script change position 788 sal_Int32 nScriptBegin = 0; 789 if ( aBound.startPos < nBegin ) 790 { 791 // search from nBegin backwards until the next script change 792 aTmpWord = rText.Copy( static_cast<xub_StrLen>(aBound.startPos), 793 static_cast<xub_StrLen>(nBegin - aBound.startPos + 1) ); 794 nScriptBegin = aBound.startPos + 795 pBreakIt->GetBreakIter()->beginOfScript( aTmpWord, nBegin - aBound.startPos, 796 nCurrScript ); 797 } 798 799 nBegin = (xub_StrLen)Max( aBound.startPos, nScriptBegin ); 800 nLen = (xub_StrLen)(nEnd - nBegin); 801 } 802 else 803 { 804 const sal_uInt16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( rText, aBound.startPos ); 805 XubString aTmpWord = rText.Copy( static_cast<xub_StrLen>(aBound.startPos), 806 static_cast<xub_StrLen>(aBound.endPos - aBound.startPos) ); 807 const sal_Int32 nScriptEnd = aBound.startPos + 808 pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript ); 809 const sal_Int32 nEnd = Min( aBound.endPos, nScriptEnd ); 810 nBegin = (xub_StrLen)aBound.startPos; 811 nLen = (xub_StrLen)(nEnd - nBegin); 812 } 813 } 814 815 // optionally clip the result of getWordBoundaries: 816 if ( bClip ) 817 { 818 aBound.startPos = Max( (xub_StrLen)aBound.startPos, nStartPos ); 819 aBound.endPos = Min( (xub_StrLen)aBound.endPos, nEndPos ); 820 nBegin = (xub_StrLen)aBound.startPos; 821 nLen = (xub_StrLen)(aBound.endPos - nBegin); 822 } 823 824 if( ! nLen ) 825 return sal_False; 826 827 aWord = rText.Copy( nBegin, nLen ); 828 829 return sal_True; 830 } 831 832 833 sal_uInt16 SwTxtNode::Spell(SwSpellArgs* pArgs) 834 { 835 // Die Aehnlichkeiten zu SwTxtFrm::_AutoSpell sind beabsichtigt ... 836 // ACHTUNG: Ev. Bugs in beiden Routinen fixen! 837 838 uno::Reference<beans::XPropertySet> xProp( GetLinguPropertySet() ); 839 840 xub_StrLen nBegin, nEnd; 841 842 // modify string according to redline information and hidden text 843 const XubString aOldTxt( m_Text ); 844 const bool bRestoreString = 845 lcl_MaskRedlinesAndHiddenText( *this, m_Text, 0, m_Text.Len() ) > 0; 846 847 if ( pArgs->pStartNode != this ) 848 nBegin = 0; 849 else 850 nBegin = pArgs->pStartIdx->GetIndex(); 851 852 nEnd = ( pArgs->pEndNode != this ) 853 ? m_Text.Len() 854 : pArgs->pEndIdx->GetIndex(); 855 856 pArgs->xSpellAlt = NULL; 857 858 // 4 cases: 859 // 860 // 1. IsWrongDirty = 0 and GetWrong = 0 861 // Everything is checked and correct 862 // 2. IsWrongDirty = 0 and GetWrong = 1 863 // Everything is checked and errors are identified in the wrong list 864 // 3. IsWrongDirty = 1 and GetWrong = 0 865 // Nothing has been checked 866 // 4. IsWrongDirty = 1 and GetWrong = 1 867 // Text has been checked but there is an invalid range in the wrong list 868 // 869 // Nothing has to be done for case 1. 870 if ( ( IsWrongDirty() || GetWrong() ) && m_Text.Len() ) 871 { 872 if ( nBegin > m_Text.Len() ) 873 { 874 nBegin = m_Text.Len(); 875 } 876 if ( nEnd > m_Text.Len() ) 877 { 878 nEnd = m_Text.Len(); 879 } 880 // 881 if(!IsWrongDirty()) 882 { 883 xub_StrLen nTemp = GetWrong()->NextWrong( nBegin ); 884 if(nTemp > nEnd) 885 { 886 // reset original text 887 if ( bRestoreString ) 888 { 889 m_Text = aOldTxt; 890 } 891 return 0; 892 } 893 if(nTemp > nBegin) 894 nBegin = nTemp; 895 896 } 897 898 // In case 2. we pass the wrong list to the scanned, because only 899 // the words in the wrong list have to be checked 900 SwScanner aScanner( *this, m_Text, 0, 0, 901 WordType::DICTIONARY_WORD, 902 nBegin, nEnd ); 903 while( !pArgs->xSpellAlt.is() && aScanner.NextWord() ) 904 { 905 const XubString& rWord = aScanner.GetWord(); 906 907 // get next language for next word, consider language attributes 908 // within the word 909 LanguageType eActLang = aScanner.GetCurrentLanguage(); 910 911 if( rWord.Len() > 0 && LANGUAGE_NONE != eActLang ) 912 { 913 if (pArgs->xSpeller.is()) 914 { 915 SvxSpellWrapper::CheckSpellLang( pArgs->xSpeller, eActLang ); 916 pArgs->xSpellAlt = pArgs->xSpeller->spell( rWord, eActLang, 917 Sequence< PropertyValue >() ); 918 } 919 if( (pArgs->xSpellAlt).is() ) 920 { 921 if( IsSymbol( aScanner.GetBegin() ) ) 922 { 923 pArgs->xSpellAlt = NULL; 924 } 925 else 926 { 927 // make sure the selection build later from the 928 // data below does not include footnotes and other 929 // "in word" character to the left and right in order 930 // to preserve those. Therefore count those "in words" 931 // in order to modify the selection accordingly. 932 const sal_Unicode* pChar = rWord.GetBuffer(); 933 xub_StrLen nLeft = 0; 934 while (pChar && *pChar++ == CH_TXTATR_INWORD) 935 ++nLeft; 936 pChar = rWord.Len() ? rWord.GetBuffer() + rWord.Len() - 1 : 0; 937 xub_StrLen nRight = 0; 938 while (pChar && *pChar-- == CH_TXTATR_INWORD) 939 ++nRight; 940 941 pArgs->pStartNode = this; 942 pArgs->pEndNode = this; 943 pArgs->pStartIdx->Assign(this, aScanner.GetEnd() - nRight ); 944 pArgs->pEndIdx->Assign(this, aScanner.GetBegin() + nLeft ); 945 } 946 } 947 } 948 } 949 } 950 951 // reset original text 952 if ( bRestoreString ) 953 { 954 m_Text = aOldTxt; 955 } 956 957 return pArgs->xSpellAlt.is() ? 1 : 0; 958 } 959 960 961 void SwTxtNode::SetLanguageAndFont( const SwPaM &rPaM, 962 LanguageType nLang, sal_uInt16 nLangWhichId, 963 const Font *pFont, sal_uInt16 nFontWhichId ) 964 { 965 sal_uInt16 aRanges[] = { 966 nLangWhichId, nLangWhichId, 967 nFontWhichId, nFontWhichId, 968 0, 0, 0 }; 969 if (!pFont) 970 aRanges[2] = aRanges[3] = 0; // clear entries with font WhichId 971 972 SwEditShell *pEditShell = GetDoc()->GetEditShell(); 973 SfxItemSet aSet( pEditShell->GetAttrPool(), aRanges ); 974 aSet.Put( SvxLanguageItem( nLang, nLangWhichId ) ); 975 976 DBG_ASSERT( pFont, "target font missing?" ); 977 if (pFont) 978 { 979 SvxFontItem aFontItem = (SvxFontItem&) aSet.Get( nFontWhichId ); 980 aFontItem.SetFamilyName( pFont->GetName()); 981 aFontItem.SetFamily( pFont->GetFamily()); 982 aFontItem.SetStyleName( pFont->GetStyleName()); 983 aFontItem.SetPitch( pFont->GetPitch()); 984 aFontItem.SetCharSet( pFont->GetCharSet() ); 985 aSet.Put( aFontItem ); 986 } 987 988 GetDoc()->InsertItemSet( rPaM, aSet, 0 ); 989 // SetAttr( aSet ); <- Does not set language attribute of empty paragraphs correctly, 990 // <- because since there is no selection the flag to garbage 991 // <- collect all attributes is set, and therefore attributes spanned 992 // <- over empty selection are removed. 993 994 } 995 996 997 sal_uInt16 SwTxtNode::Convert( SwConversionArgs &rArgs ) 998 { 999 // get range of text within node to be converted 1000 // (either all the text or the the text within the selection 1001 // when the conversion was started) 1002 xub_StrLen nTextBegin, nTextEnd; 1003 // 1004 if ( rArgs.pStartNode != this ) 1005 { 1006 nTextBegin = 0; 1007 } 1008 else 1009 nTextBegin = rArgs.pStartIdx->GetIndex(); 1010 if (nTextBegin > m_Text.Len()) 1011 { 1012 nTextBegin = m_Text.Len(); 1013 } 1014 1015 nTextEnd = ( rArgs.pEndNode != this ) 1016 ? m_Text.Len() 1017 : ::std::min( rArgs.pEndIdx->GetIndex(), m_Text.Len() ); 1018 1019 rArgs.aConvText = rtl::OUString(); 1020 1021 // modify string according to redline information and hidden text 1022 const XubString aOldTxt( m_Text ); 1023 const bool bRestoreString = 1024 lcl_MaskRedlinesAndHiddenText( *this, m_Text, 0, m_Text.Len() ) > 0; 1025 1026 sal_Bool bFound = sal_False; 1027 xub_StrLen nBegin = nTextBegin; 1028 xub_StrLen nLen = 0; 1029 LanguageType nLangFound = LANGUAGE_NONE; 1030 if (!m_Text.Len()) 1031 { 1032 if (rArgs.bAllowImplicitChangesForNotConvertibleText) 1033 { 1034 // create SwPaM with mark & point spanning empty paragraph 1035 //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different 1036 SwPaM aCurPaM( *this, 0 ); 1037 1038 SetLanguageAndFont( aCurPaM, 1039 rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE, 1040 rArgs.pTargetFont, RES_CHRATR_CJK_FONT ); 1041 } 1042 } 1043 else 1044 { 1045 SwLanguageIterator aIter( *this, nBegin ); 1046 1047 // find non zero length text portion of appropriate language 1048 do { 1049 nLangFound = aIter.GetLanguage(); 1050 sal_Bool bLangOk = (nLangFound == rArgs.nConvSrcLang) || 1051 (editeng::HangulHanjaConversion::IsChinese( nLangFound ) && 1052 editeng::HangulHanjaConversion::IsChinese( rArgs.nConvSrcLang )); 1053 1054 xub_StrLen nChPos = aIter.GetChgPos(); 1055 // the position at the end of the paragraph returns -1 1056 // which becomes 65535 when converted to xub_StrLen, 1057 // and thus must be cut to the end of the actual string. 1058 if (nChPos == (xub_StrLen) -1) 1059 { 1060 nChPos = m_Text.Len(); 1061 } 1062 1063 nLen = nChPos - nBegin; 1064 bFound = bLangOk && nLen > 0; 1065 if (!bFound) 1066 { 1067 // create SwPaM with mark & point spanning the attributed text 1068 //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different 1069 SwPaM aCurPaM( *this, nBegin ); 1070 aCurPaM.SetMark(); 1071 aCurPaM.GetPoint()->nContent = nBegin + nLen; 1072 1073 // check script type of selected text 1074 SwEditShell *pEditShell = GetDoc()->GetEditShell(); 1075 pEditShell->Push(); // save current cursor on stack 1076 pEditShell->SetSelection( aCurPaM ); 1077 sal_Bool bIsAsianScript = (SCRIPTTYPE_ASIAN == pEditShell->GetScriptType()); 1078 pEditShell->Pop( sal_False ); // restore cursor from stack 1079 1080 if (!bIsAsianScript && rArgs.bAllowImplicitChangesForNotConvertibleText) 1081 { 1082 SetLanguageAndFont( aCurPaM, 1083 rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE, 1084 rArgs.pTargetFont, RES_CHRATR_CJK_FONT ); 1085 } 1086 nBegin = nChPos; // start of next language portion 1087 } 1088 } while (!bFound && aIter.Next()); /* loop while nothing was found and still sth is left to be searched */ 1089 } 1090 1091 // keep resulting text within selection / range of text to be converted 1092 if (nBegin < nTextBegin) 1093 nBegin = nTextBegin; 1094 if (nBegin + nLen > nTextEnd) 1095 nLen = nTextEnd - nBegin; 1096 sal_Bool bInSelection = nBegin < nTextEnd; 1097 1098 if (bFound && bInSelection) // convertible text found within selection/range? 1099 { 1100 const XubString aTxtPortion = m_Text.Copy( nBegin, nLen ); 1101 DBG_ASSERT( m_Text.Len() > 0, "convertible text portion missing!" ); 1102 rArgs.aConvText = m_Text.Copy( nBegin, nLen ); 1103 rArgs.nConvTextLang = nLangFound; 1104 1105 // position where to start looking in next iteration (after current ends) 1106 rArgs.pStartNode = this; 1107 rArgs.pStartIdx->Assign(this, nBegin + nLen ); 1108 // end position (when we have travelled over the whole document) 1109 rArgs.pEndNode = this; 1110 rArgs.pEndIdx->Assign(this, nBegin ); 1111 } 1112 1113 // restore original text 1114 if ( bRestoreString ) 1115 { 1116 m_Text = aOldTxt; 1117 } 1118 1119 return rArgs.aConvText.getLength() ? 1 : 0; 1120 } 1121 1122 // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ... 1123 // ACHTUNG: Ev. Bugs in beiden Routinen fixen! 1124 SwRect SwTxtFrm::_AutoSpell( const SwCntntNode* pActNode, const SwViewOption& rViewOpt, xub_StrLen nActPos ) 1125 { 1126 SwRect aRect; 1127 #if OSL_DEBUG_LEVEL > 1 1128 static sal_Bool bStop = sal_False; 1129 if ( bStop ) 1130 return aRect; 1131 #endif 1132 // Die Aehnlichkeiten zu SwTxtNode::Spell sind beabsichtigt ... 1133 // ACHTUNG: Ev. Bugs in beiden Routinen fixen! 1134 SwTxtNode *pNode = GetTxtNode(); 1135 if( pNode != pActNode || !nActPos ) 1136 nActPos = STRING_LEN; 1137 1138 SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords(); 1139 1140 // modify string according to redline information and hidden text 1141 const XubString aOldTxt( pNode->GetTxt() ); 1142 const bool bRestoreString = 1143 lcl_MaskRedlinesAndHiddenText( *pNode, pNode->m_Text, 1144 0, pNode->GetTxt().Len() ) > 0; 1145 1146 // a change of data indicates that at least one word has been modified 1147 const sal_Bool bRedlineChg = 1148 ( pNode->GetTxt().GetBuffer() != aOldTxt.GetBuffer() ); 1149 1150 xub_StrLen nBegin = 0; 1151 xub_StrLen nEnd = pNode->GetTxt().Len(); 1152 xub_StrLen nInsertPos = 0; 1153 xub_StrLen nChgStart = STRING_LEN; 1154 xub_StrLen nChgEnd = 0; 1155 xub_StrLen nInvStart = STRING_LEN; 1156 xub_StrLen nInvEnd = 0; 1157 1158 const sal_Bool bAddAutoCmpl = pNode->IsAutoCompleteWordDirty() && 1159 rViewOpt.IsAutoCompleteWords(); 1160 1161 if( pNode->GetWrong() ) 1162 { 1163 nBegin = pNode->GetWrong()->GetBeginInv(); 1164 if( STRING_LEN != nBegin ) 1165 { 1166 nEnd = pNode->GetWrong()->GetEndInv(); 1167 if ( nEnd > pNode->GetTxt().Len() ) 1168 { 1169 nEnd = pNode->GetTxt().Len(); 1170 } 1171 } 1172 1173 // get word around nBegin, we start at nBegin - 1 1174 if ( STRING_LEN != nBegin ) 1175 { 1176 if ( nBegin ) 1177 --nBegin; 1178 1179 LanguageType eActLang = pNode->GetLang( nBegin ); 1180 Boundary aBound = 1181 pBreakIt->GetBreakIter()->getWordBoundary( pNode->GetTxt(), nBegin, 1182 pBreakIt->GetLocale( eActLang ), 1183 WordType::DICTIONARY_WORD, sal_True ); 1184 nBegin = xub_StrLen(aBound.startPos); 1185 } 1186 1187 // get the position in the wrong list 1188 nInsertPos = pNode->GetWrong()->GetWrongPos( nBegin ); 1189 1190 // sometimes we have to skip one entry 1191 if( nInsertPos < pNode->GetWrong()->Count() && 1192 nBegin == pNode->GetWrong()->Pos( nInsertPos ) + 1193 pNode->GetWrong()->Len( nInsertPos ) ) 1194 nInsertPos++; 1195 } 1196 1197 sal_Bool bFresh = nBegin < nEnd; 1198 1199 if( nBegin < nEnd ) 1200 { 1201 //! register listener to LinguServiceEvents now in order to get 1202 //! notified about relevant changes in the future 1203 SwModule *pModule = SW_MOD(); 1204 if (!pModule->GetLngSvcEvtListener().is()) 1205 pModule->CreateLngSvcEvtListener(); 1206 1207 uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() ); 1208 SwDoc* pDoc = pNode->GetDoc(); 1209 1210 SwScanner aScanner( *pNode, pNode->GetTxt(), 0, 0, 1211 WordType::DICTIONARY_WORD, nBegin, nEnd); 1212 1213 while( aScanner.NextWord() ) 1214 { 1215 const XubString& rWord = aScanner.GetWord(); 1216 nBegin = aScanner.GetBegin(); 1217 xub_StrLen nLen = aScanner.GetLen(); 1218 1219 // get next language for next word, consider language attributes 1220 // within the word 1221 LanguageType eActLang = aScanner.GetCurrentLanguage(); 1222 1223 sal_Bool bSpell = sal_True; 1224 bSpell = xSpell.is() ? xSpell->hasLanguage( eActLang ) : sal_False; 1225 if( bSpell && rWord.Len() > 0 ) 1226 { 1227 // check for: bAlter => xHyphWord.is() 1228 DBG_ASSERT(!bSpell || xSpell.is(), "NULL pointer"); 1229 1230 if( !xSpell->isValid( rWord, eActLang, Sequence< PropertyValue >() ) ) 1231 { 1232 xub_StrLen nSmartTagStt = nBegin; 1233 xub_StrLen nDummy = 1; 1234 if ( !pNode->GetSmartTags() || !pNode->GetSmartTags()->InWrongWord( nSmartTagStt, nDummy ) ) 1235 { 1236 if( !pNode->GetWrong() ) 1237 { 1238 pNode->SetWrong( new SwWrongList( WRONGLIST_SPELL ) ); 1239 pNode->GetWrong()->SetInvalid( 0, nEnd ); 1240 } 1241 if( pNode->GetWrong()->Fresh( nChgStart, nChgEnd, 1242 nBegin, nLen, nInsertPos, nActPos ) ) 1243 pNode->GetWrong()->Insert( rtl::OUString(), 0, nBegin, nLen, nInsertPos++ ); 1244 else 1245 { 1246 nInvStart = nBegin; 1247 nInvEnd = nBegin + nLen; 1248 } 1249 } 1250 } 1251 else if( bAddAutoCmpl && rACW.GetMinWordLen() <= rWord.Len() ) 1252 { 1253 if ( bRedlineChg ) 1254 { 1255 XubString rNewWord( rWord ); 1256 rACW.InsertWord( rNewWord, *pDoc ); 1257 } 1258 else 1259 rACW.InsertWord( rWord, *pDoc ); 1260 } 1261 } 1262 } 1263 } 1264 1265 // reset original text 1266 // i63141 before calling GetCharRect(..) with formatting! 1267 if ( bRestoreString ) 1268 { 1269 pNode->m_Text = aOldTxt; 1270 } 1271 if( pNode->GetWrong() ) 1272 { 1273 if( bFresh ) 1274 pNode->GetWrong()->Fresh( nChgStart, nChgEnd, 1275 nEnd, 0, nInsertPos, nActPos ); 1276 1277 // 1278 // Calculate repaint area: 1279 // 1280 if( nChgStart < nChgEnd ) 1281 { 1282 aRect = lcl_CalculateRepaintRect( *this, nChgStart, nChgEnd ); 1283 } 1284 1285 pNode->GetWrong()->SetInvalid( nInvStart, nInvEnd ); 1286 pNode->SetWrongDirty( STRING_LEN != pNode->GetWrong()->GetBeginInv() ); 1287 if( !pNode->GetWrong()->Count() && ! pNode->IsWrongDirty() ) 1288 pNode->SetWrong( NULL ); 1289 } 1290 else 1291 pNode->SetWrongDirty( false ); 1292 1293 if( bAddAutoCmpl ) 1294 pNode->SetAutoCompleteWordDirty( false ); 1295 1296 return aRect; 1297 } 1298 1299 /** Function: SmartTagScan 1300 1301 Function scans words in current text and checks them in the 1302 smarttag libraries. If the check returns true to bounds of the 1303 recognized words are stored into a list which is used later for drawing 1304 the underline. 1305 1306 @param SwCntntNode* pActNode 1307 1308 @param xub_StrLen nActPos 1309 1310 @return SwRect: Repaint area 1311 */ 1312 SwRect SwTxtFrm::SmartTagScan( SwCntntNode* /*pActNode*/, xub_StrLen /*nActPos*/ ) 1313 { 1314 SwRect aRet; 1315 SwTxtNode *pNode = GetTxtNode(); 1316 const rtl::OUString& rText = pNode->GetTxt(); 1317 1318 // Iterate over language portions 1319 SmartTagMgr& rSmartTagMgr = SwSmartTagMgr::Get(); 1320 1321 SwWrongList* pSmartTagList = pNode->GetSmartTags(); 1322 1323 xub_StrLen nBegin = 0; 1324 xub_StrLen nEnd = static_cast< xub_StrLen >(rText.getLength()); 1325 1326 if ( pSmartTagList ) 1327 { 1328 if ( pSmartTagList->GetBeginInv() != STRING_LEN ) 1329 { 1330 nBegin = pSmartTagList->GetBeginInv(); 1331 nEnd = Min( pSmartTagList->GetEndInv(), (xub_StrLen)rText.getLength() ); 1332 1333 if ( nBegin < nEnd ) 1334 { 1335 const LanguageType aCurrLang = pNode->GetLang( nBegin ); 1336 const com::sun::star::lang::Locale aCurrLocale = pBreakIt->GetLocale( aCurrLang ); 1337 nBegin = static_cast< xub_StrLen >(pBreakIt->GetBreakIter()->beginOfSentence( rText, nBegin, aCurrLocale )); 1338 nEnd = static_cast< xub_StrLen >(Min( rText.getLength(), pBreakIt->GetBreakIter()->endOfSentence( rText, nEnd, aCurrLocale ) )); 1339 } 1340 } 1341 } 1342 1343 const sal_uInt16 nNumberOfEntries = pSmartTagList ? pSmartTagList->Count() : 0; 1344 sal_uInt16 nNumberOfRemovedEntries = 0; 1345 sal_uInt16 nNumberOfInsertedEntries = 0; 1346 1347 // clear smart tag list between nBegin and nEnd: 1348 if ( 0 != nNumberOfEntries ) 1349 { 1350 xub_StrLen nChgStart = STRING_LEN; 1351 xub_StrLen nChgEnd = 0; 1352 const sal_uInt16 nCurrentIndex = pSmartTagList->GetWrongPos( nBegin ); 1353 pSmartTagList->Fresh( nChgStart, nChgEnd, nBegin, nEnd - nBegin, nCurrentIndex, STRING_LEN ); 1354 nNumberOfRemovedEntries = nNumberOfEntries - pSmartTagList->Count(); 1355 } 1356 1357 if ( nBegin < nEnd ) 1358 { 1359 // Expand the string: 1360 rtl::OUString aExpandText; 1361 const ModelToViewHelper::ConversionMap* pConversionMap = 1362 pNode->BuildConversionMap( aExpandText ); 1363 1364 // Ownership ov ConversionMap is passed to SwXTextMarkup object! 1365 Reference< com::sun::star::text::XTextMarkup > xTextMarkup = 1366 new SwXTextMarkup( *pNode, pConversionMap ); 1367 1368 Reference< ::com::sun::star::frame::XController > xController = pNode->GetDoc()->GetDocShell()->GetController(); 1369 1370 SwPosition start(*pNode, nBegin); 1371 SwPosition end (*pNode, nEnd); 1372 Reference< ::com::sun::star::text::XTextRange > xRange = SwXTextRange::CreateXTextRange(*pNode->GetDoc(), start, &end); 1373 1374 rSmartTagMgr.RecognizeTextRange(xRange, xTextMarkup, xController); 1375 1376 1377 xub_StrLen nLangBegin = nBegin; 1378 xub_StrLen nLangEnd = nEnd; 1379 1380 // smart tag recognization has to be done for each language portion: 1381 SwLanguageIterator aIter( *pNode, nLangBegin ); 1382 1383 do 1384 { 1385 const LanguageType nLang = aIter.GetLanguage(); 1386 const com::sun::star::lang::Locale aLocale = pBreakIt->GetLocale( nLang ); 1387 nLangEnd = Min( nEnd, aIter.GetChgPos() ); 1388 1389 const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nLangBegin ); 1390 const sal_uInt32 nExpandEnd = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nLangEnd ); 1391 1392 rSmartTagMgr.RecognizeString(aExpandText, xTextMarkup, xController, aLocale, nExpandBegin, nExpandEnd - nExpandBegin ); 1393 1394 nLangBegin = nLangEnd; 1395 } 1396 while ( aIter.Next() && nLangEnd < nEnd ); 1397 1398 pSmartTagList = pNode->GetSmartTags(); 1399 1400 const sal_uInt16 nNumberOfEntriesAfterRecognize = pSmartTagList ? pSmartTagList->Count() : 0; 1401 nNumberOfInsertedEntries = nNumberOfEntriesAfterRecognize - ( nNumberOfEntries - nNumberOfRemovedEntries ); 1402 } 1403 1404 if( pSmartTagList ) 1405 { 1406 // 1407 // Update WrongList stuff 1408 // 1409 pSmartTagList->SetInvalid( STRING_LEN, 0 ); 1410 pNode->SetSmartTagDirty( STRING_LEN != pSmartTagList->GetBeginInv() ); 1411 1412 if( !pSmartTagList->Count() && !pNode->IsSmartTagDirty() ) 1413 pNode->SetSmartTags( NULL ); 1414 1415 // 1416 // Calculate repaint area: 1417 // 1418 #if OSL_DEBUG_LEVEL > 1 1419 const sal_uInt16 nNumberOfEntriesAfterRecognize2 = pSmartTagList->Count(); 1420 (void) nNumberOfEntriesAfterRecognize2; 1421 #endif 1422 if ( nBegin < nEnd && ( 0 != nNumberOfRemovedEntries || 1423 0 != nNumberOfInsertedEntries ) ) 1424 { 1425 aRet = lcl_CalculateRepaintRect( *this, nBegin, nEnd ); 1426 } 1427 } 1428 else 1429 pNode->SetSmartTagDirty( false ); 1430 1431 return aRet; 1432 } 1433 1434 1435 // Wird vom CollectAutoCmplWords gerufen 1436 void SwTxtFrm::CollectAutoCmplWrds( SwCntntNode* pActNode, xub_StrLen nActPos ) 1437 { 1438 SwTxtNode *pNode = GetTxtNode(); 1439 if( pNode != pActNode || !nActPos ) 1440 nActPos = STRING_LEN; 1441 1442 SwDoc* pDoc = pNode->GetDoc(); 1443 SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords(); 1444 1445 xub_StrLen nBegin = 0; 1446 xub_StrLen nEnd = pNode->GetTxt().Len(); 1447 xub_StrLen nLen; 1448 sal_Bool bACWDirty = sal_False, bAnyWrd = sal_False; 1449 1450 1451 if( nBegin < nEnd ) 1452 { 1453 sal_uInt16 nCnt = 200; 1454 SwScanner aScanner( *pNode, pNode->GetTxt(), 0, 0, 1455 WordType::DICTIONARY_WORD, nBegin, nEnd ); 1456 while( aScanner.NextWord() ) 1457 { 1458 nBegin = aScanner.GetBegin(); 1459 nLen = aScanner.GetLen(); 1460 if( rACW.GetMinWordLen() <= nLen ) 1461 { 1462 const XubString& rWord = aScanner.GetWord(); 1463 1464 if( nActPos < nBegin || ( nBegin + nLen ) < nActPos ) 1465 { 1466 if( rACW.GetMinWordLen() <= rWord.Len() ) 1467 rACW.InsertWord( rWord, *pDoc ); 1468 bAnyWrd = sal_True; 1469 } 1470 else 1471 bACWDirty = sal_True; 1472 } 1473 if( !--nCnt ) 1474 { 1475 if ( Application::AnyInput( INPUT_ANY ) ) 1476 return; 1477 nCnt = 100; 1478 } 1479 } 1480 } 1481 1482 if( bAnyWrd && !bACWDirty ) 1483 pNode->SetAutoCompleteWordDirty( sal_False ); 1484 } 1485 1486 1487 /************************************************************************* 1488 * SwTxtNode::Hyphenate 1489 *************************************************************************/ 1490 // Findet den TxtFrm und sucht dessen CalcHyph 1491 1492 sal_Bool SwTxtNode::Hyphenate( SwInterHyphInfo &rHyphInf ) 1493 { 1494 // Abkuerzung: am Absatz ist keine Sprache eingestellt: 1495 if ( LANGUAGE_NONE == sal_uInt16( GetSwAttrSet().GetLanguage().GetLanguage() ) 1496 && USHRT_MAX == GetLang( 0, m_Text.Len() ) ) 1497 { 1498 if( !rHyphInf.IsCheck() ) 1499 rHyphInf.SetNoLang( sal_True ); 1500 return sal_False; 1501 } 1502 1503 if( pLinguNode != this ) 1504 { 1505 pLinguNode = this; 1506 pLinguFrm = (SwTxtFrm*)getLayoutFrm( GetDoc()->GetCurrentLayout(), (Point*)(rHyphInf.GetCrsrPos()) ); 1507 } 1508 SwTxtFrm *pFrm = pLinguFrm; 1509 if( pFrm ) 1510 pFrm = &(pFrm->GetFrmAtOfst( rHyphInf.nStart )); 1511 else 1512 { 1513 // 4935: Seit der Trennung ueber Sonderbereiche sind Faelle 1514 // moeglich, in denen kein Frame zum Node vorliegt. 1515 // Also kein ASSERT! 1516 #if OSL_DEBUG_LEVEL > 1 1517 ASSERT( pFrm, "!SwTxtNode::Hyphenate: can't find any frame" ); 1518 #endif 1519 return sal_False; 1520 } 1521 1522 while( pFrm ) 1523 { 1524 if( pFrm->Hyphenate( rHyphInf ) ) 1525 { 1526 // Das Layout ist nicht robust gegen "Direktformatierung" 1527 // (7821, 7662, 7408); vgl. layact.cxx, 1528 // SwLayAction::_TurboAction(), if( !pCnt->IsValid() ... 1529 pFrm->SetCompletePaint(); 1530 return sal_True; 1531 } 1532 pFrm = (SwTxtFrm*)(pFrm->GetFollow()); 1533 if( pFrm ) 1534 { 1535 rHyphInf.nLen = rHyphInf.nLen - (pFrm->GetOfst() - rHyphInf.nStart); 1536 rHyphInf.nStart = pFrm->GetOfst(); 1537 } 1538 } 1539 return sal_False; 1540 } 1541 1542 #ifdef LINGU_STATISTIK 1543 1544 // globale Variable 1545 SwLinguStatistik aSwLinguStat; 1546 1547 1548 void SwLinguStatistik::Flush() 1549 { 1550 if ( !nWords ) 1551 return ; 1552 1553 static char *pLogName = 0; 1554 const sal_Bool bFirstOpen = pLogName ? sal_False : sal_True; 1555 if( bFirstOpen ) 1556 { 1557 char *pPath = getenv( "TEMP" ); 1558 char *pName = "swlingu.stk"; 1559 if( !pPath ) 1560 pLogName = pName; 1561 else 1562 { 1563 const int nLen = strlen(pPath); 1564 // fuer dieses new wird es kein delete geben. 1565 pLogName = new char[nLen + strlen(pName) + 3]; 1566 if(nLen && (pPath[nLen-1] == '\\') || (pPath[nLen-1] == '/')) 1567 snprintf( pLogName, sizeof(pLogName), "%s%s", pPath, pName ); 1568 else 1569 snprintf( pLogName, sizeof(pLogName), "%s/%s", pPath, pName ); 1570 } 1571 } 1572 SvFileStream aStream( String::CreateFromAscii(pLogName), (bFirstOpen 1573 ? STREAM_WRITE | STREAM_TRUNC 1574 : STREAM_WRITE )); 1575 1576 if( !aStream.GetError() ) 1577 { 1578 if ( bFirstOpen ) 1579 aStream << "\nLinguistik-Statistik\n"; 1580 aStream << endl << ++nFlushCnt << ". Messung\n"; 1581 aStream << "Rechtschreibung\n"; 1582 aStream << "gepruefte Worte: \t" << nWords << endl; 1583 aStream << "als fehlerhaft erkannt:\t" << nWrong << endl; 1584 aStream << "Alternativvorschlaege:\t" << nAlter << endl; 1585 if ( nWrong ) 1586 aStream << "Durchschnitt:\t\t" << nAlter*1.0 / nWrong << endl; 1587 aStream << "Dauer (msec):\t\t" << nSpellTime << endl; 1588 aStream << "\nThesaurus\n"; 1589 aStream << "Synonyme gesamt:\t" << nSynonym << endl; 1590 if ( nSynonym ) 1591 aStream << "Synonym-Durchschnitt:\t" << 1592 nSynonym*1.0 / ( nWords - nNoSynonym ) << endl; 1593 aStream << "ohne Synonyme:\t\t" << nNoSynonym << endl; 1594 aStream << "Bedeutungen gesamt:\t" << nSynonym << endl; 1595 aStream << "keine Bedeutungen:\t"<< nNoSynonym << endl; 1596 aStream << "Dauer (msec):\t\t" << nTheTime << endl; 1597 aStream << "\nHyphenator\n"; 1598 aStream << "Trennstellen gesamt:\t" << nHyphens << endl; 1599 if ( nHyphens ) 1600 aStream << "Hyphen-Durchschnitt:\t" << 1601 nHyphens*1.0 / ( nWords - nNoHyph - nHyphErr ) << endl; 1602 aStream << "keine Trennstellen:\t" << nNoHyph << endl; 1603 aStream << "Trennung verweigert:\t" << nHyphErr << endl; 1604 aStream << "Dauer (msec):\t\t" << nHyphTime << endl; 1605 aStream << "---------------------------------------------\n"; 1606 } 1607 nWords = nWrong = nAlter = nSynonym = nNoSynonym = 1608 nHyphens = nNoHyph = nHyphErr = nSpellTime = nTheTime = 1609 nHyphTime = 0; 1610 //pThes = NULL; 1611 } 1612 1613 #endif 1614 1615 namespace sw // #i120045# namespace to avoid XCode template-misoptimization 1616 { 1617 struct TransliterationChgData 1618 { 1619 xub_StrLen nStart; 1620 xub_StrLen nLen; 1621 String sChanged; 1622 Sequence< sal_Int32 > aOffsets; 1623 }; 1624 } 1625 using sw::TransliterationChgData; 1626 1627 // change text to Upper/Lower/Hiragana/Katagana/... 1628 void SwTxtNode::TransliterateText( 1629 utl::TransliterationWrapper& rTrans, 1630 xub_StrLen nStt, xub_StrLen nEnd, 1631 SwUndoTransliterate* pUndo ) 1632 { 1633 if (nStt < nEnd && pBreakIt->GetBreakIter().is()) 1634 { 1635 // since we don't use Hiragana/Katakana or half-width/full-width transliterations here 1636 // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will 1637 // occasionaly miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES 1638 // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the 1639 // proper thing to do. 1640 const sal_Int16 nWordType = WordType::ANYWORD_IGNOREWHITESPACES; 1641 1642 //! In order to have less trouble with changing text size, e.g. because 1643 //! of ligatures or � (German small sz) being resolved, we need to process 1644 //! the text replacements from end to start. 1645 //! This way the offsets for the yet to be changed words will be 1646 //! left unchanged by the already replaced text. 1647 //! For this we temporarily save the changes to be done in this vector 1648 std::vector< TransliterationChgData > aChanges; 1649 TransliterationChgData aChgData; 1650 1651 if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::TITLE_CASE) 1652 { 1653 // for 'capitalize every word' we need to iterate over each word 1654 1655 Boundary aSttBndry; 1656 Boundary aEndBndry; 1657 aSttBndry = pBreakIt->GetBreakIter()->getWordBoundary( 1658 GetTxt(), nStt, 1659 pBreakIt->GetLocale( GetLang( nStt ) ), 1660 nWordType, 1661 sal_True /*prefer forward direction*/); 1662 aEndBndry = pBreakIt->GetBreakIter()->getWordBoundary( 1663 GetTxt(), nEnd, 1664 pBreakIt->GetLocale( GetLang( nEnd ) ), 1665 nWordType, 1666 sal_False /*prefer backward direction*/); 1667 1668 // prevent backtracking to the previous word if selection is at word boundary 1669 if (aSttBndry.endPos <= nStt) 1670 { 1671 aSttBndry = pBreakIt->GetBreakIter()->nextWord( 1672 GetTxt(), aSttBndry.endPos, 1673 pBreakIt->GetLocale( GetLang( aSttBndry.endPos ) ), 1674 nWordType); 1675 } 1676 // prevent advancing to the next word if selection is at word boundary 1677 if (aEndBndry.startPos >= nEnd) 1678 { 1679 aEndBndry = pBreakIt->GetBreakIter()->previousWord( 1680 GetTxt(), aEndBndry.startPos, 1681 pBreakIt->GetLocale( GetLang( aEndBndry.startPos ) ), 1682 nWordType); 1683 } 1684 1685 Boundary aCurWordBndry( aSttBndry ); 1686 while (aCurWordBndry.startPos <= aEndBndry.startPos) 1687 { 1688 nStt = (xub_StrLen)aCurWordBndry.startPos; 1689 nEnd = (xub_StrLen)aCurWordBndry.endPos; 1690 sal_Int32 nLen = nEnd - nStt; 1691 DBG_ASSERT( nLen > 0, "invalid word length of 0" ); 1692 #if OSL_DEBUG_LEVEL > 1 1693 String aText( GetTxt().Copy( nStt, nLen ) ); 1694 #endif 1695 1696 Sequence <sal_Int32> aOffsets; 1697 String sChgd( rTrans.transliterate( GetTxt(), GetLang( nStt ), nStt, nLen, &aOffsets )); 1698 1699 if (!m_Text.Equals( sChgd, nStt, nLen )) 1700 { 1701 aChgData.nStart = nStt; 1702 aChgData.nLen = nLen; 1703 aChgData.sChanged = sChgd; 1704 aChgData.aOffsets = aOffsets; 1705 aChanges.push_back( aChgData ); 1706 } 1707 1708 aCurWordBndry = pBreakIt->GetBreakIter()->nextWord( 1709 GetTxt(), nEnd, 1710 pBreakIt->GetLocale( GetLang( nEnd ) ), 1711 nWordType); 1712 } 1713 } 1714 else if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::SENTENCE_CASE) 1715 { 1716 // for 'sentence case' we need to iterate sentence by sentence 1717 1718 sal_Int32 nLastStart = pBreakIt->GetBreakIter()->beginOfSentence( 1719 GetTxt(), nEnd, 1720 pBreakIt->GetLocale( GetLang( nEnd ) ) ); 1721 sal_Int32 nLastEnd = pBreakIt->GetBreakIter()->endOfSentence( 1722 GetTxt(), nLastStart, 1723 pBreakIt->GetLocale( GetLang( nLastStart ) ) ); 1724 1725 // extend nStt, nEnd to the current sentence boundaries 1726 sal_Int32 nCurrentStart = pBreakIt->GetBreakIter()->beginOfSentence( 1727 GetTxt(), nStt, 1728 pBreakIt->GetLocale( GetLang( nStt ) ) ); 1729 sal_Int32 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( 1730 GetTxt(), nCurrentStart, 1731 pBreakIt->GetLocale( GetLang( nCurrentStart ) ) ); 1732 1733 // prevent backtracking to the previous sentence if selection starts at end of a sentence 1734 if (nCurrentEnd <= nStt) 1735 { 1736 // now nCurrentStart is probably located on a non-letter word. (unless we 1737 // are in Asian text with no spaces...) 1738 // Thus to get the real sentence start we should locate the next real word, 1739 // that is one found by DICTIONARY_WORD 1740 i18n::Boundary aBndry = pBreakIt->GetBreakIter()->nextWord( 1741 GetTxt(), nCurrentEnd, 1742 pBreakIt->GetLocale( GetLang( nCurrentEnd ) ), 1743 i18n::WordType::DICTIONARY_WORD); 1744 1745 // now get new current sentence boundaries 1746 nCurrentStart = pBreakIt->GetBreakIter()->beginOfSentence( 1747 GetTxt(), aBndry.startPos, 1748 pBreakIt->GetLocale( GetLang( aBndry.startPos) ) ); 1749 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( 1750 GetTxt(), nCurrentStart, 1751 pBreakIt->GetLocale( GetLang( nCurrentStart) ) ); 1752 } 1753 // prevent advancing to the next sentence if selection ends at start of a sentence 1754 if (nLastStart >= nEnd) 1755 { 1756 // now nCurrentStart is probably located on a non-letter word. (unless we 1757 // are in Asian text with no spaces...) 1758 // Thus to get the real sentence start we should locate the previous real word, 1759 // that is one found by DICTIONARY_WORD 1760 i18n::Boundary aBndry = pBreakIt->GetBreakIter()->previousWord( 1761 GetTxt(), nLastStart, 1762 pBreakIt->GetLocale( GetLang( nLastStart) ), 1763 i18n::WordType::DICTIONARY_WORD); 1764 nLastEnd = pBreakIt->GetBreakIter()->endOfSentence( 1765 GetTxt(), aBndry.startPos, 1766 pBreakIt->GetLocale( GetLang( aBndry.startPos) ) ); 1767 if (nCurrentEnd > nLastEnd) 1768 nCurrentEnd = nLastEnd; 1769 } 1770 1771 while (nCurrentStart < nLastEnd) 1772 { 1773 sal_Int32 nLen = nCurrentEnd - nCurrentStart; 1774 DBG_ASSERT( nLen > 0, "invalid word length of 0" ); 1775 #if OSL_DEBUG_LEVEL > 1 1776 String aText( GetTxt().Copy( nCurrentStart, nLen ) ); 1777 #endif 1778 1779 Sequence <sal_Int32> aOffsets; 1780 String sChgd( rTrans.transliterate( GetTxt(), 1781 GetLang( nCurrentStart ), nCurrentStart, nLen, &aOffsets )); 1782 1783 if (!m_Text.Equals( sChgd, nStt, nLen )) 1784 { 1785 aChgData.nStart = nCurrentStart; 1786 aChgData.nLen = nLen; 1787 aChgData.sChanged = sChgd; 1788 aChgData.aOffsets = aOffsets; 1789 aChanges.push_back( aChgData ); 1790 } 1791 1792 Boundary aFirstWordBndry; 1793 aFirstWordBndry = pBreakIt->GetBreakIter()->nextWord( 1794 GetTxt(), nCurrentEnd, 1795 pBreakIt->GetLocale( GetLang( nCurrentEnd ) ), 1796 nWordType); 1797 nCurrentStart = aFirstWordBndry.startPos; 1798 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( 1799 GetTxt(), nCurrentStart, 1800 pBreakIt->GetLocale( GetLang( nCurrentStart ) ) ); 1801 } 1802 } 1803 else 1804 { 1805 // here we may transliterate over complete language portions... 1806 1807 SwLanguageIterator* pIter; 1808 if( rTrans.needLanguageForTheMode() ) 1809 pIter = new SwLanguageIterator( *this, nStt ); 1810 else 1811 pIter = 0; 1812 1813 xub_StrLen nEndPos; 1814 sal_uInt16 nLang; 1815 do { 1816 if( pIter ) 1817 { 1818 nLang = pIter->GetLanguage(); 1819 nEndPos = pIter->GetChgPos(); 1820 if( nEndPos > nEnd ) 1821 nEndPos = nEnd; 1822 } 1823 else 1824 { 1825 nLang = LANGUAGE_SYSTEM; 1826 nEndPos = nEnd; 1827 } 1828 xub_StrLen nLen = nEndPos - nStt; 1829 1830 Sequence <sal_Int32> aOffsets; 1831 String sChgd( rTrans.transliterate( m_Text, nLang, nStt, nLen, &aOffsets )); 1832 1833 if (!m_Text.Equals( sChgd, nStt, nLen )) 1834 { 1835 aChgData.nStart = nStt; 1836 aChgData.nLen = nLen; 1837 aChgData.sChanged = sChgd; 1838 aChgData.aOffsets = aOffsets; 1839 aChanges.push_back( aChgData ); 1840 } 1841 1842 nStt = nEndPos; 1843 } while( nEndPos < nEnd && pIter && pIter->Next() ); 1844 delete pIter; 1845 } 1846 1847 if (aChanges.size() > 0) 1848 { 1849 // now apply the changes from end to start to leave the offsets of the 1850 // yet unchanged text parts remain the same. 1851 for (size_t i = 0; i < aChanges.size(); ++i) 1852 { 1853 TransliterationChgData &rData = aChanges[ aChanges.size() - 1 - i ]; 1854 if (pUndo) 1855 pUndo->AddChanges( *this, rData.nStart, rData.nLen, rData.aOffsets ); 1856 ReplaceTextOnly( rData.nStart, rData.nLen, rData.sChanged, rData.aOffsets ); 1857 } 1858 } 1859 } 1860 } 1861 1862 1863 void SwTxtNode::ReplaceTextOnly( xub_StrLen nPos, xub_StrLen nLen, 1864 const XubString& rText, 1865 const Sequence<sal_Int32>& rOffsets ) 1866 { 1867 m_Text.Replace( nPos, nLen, rText ); 1868 1869 xub_StrLen nTLen = rText.Len(); 1870 const sal_Int32* pOffsets = rOffsets.getConstArray(); 1871 // now look for no 1-1 mapping -> move the indizies! 1872 xub_StrLen nI, nMyOff; 1873 for( nI = 0, nMyOff = nPos; nI < nTLen; ++nI, ++nMyOff ) 1874 { 1875 xub_StrLen nOff = (xub_StrLen)pOffsets[ nI ]; 1876 if( nOff < nMyOff ) 1877 { 1878 // something is inserted 1879 xub_StrLen nCnt = 1; 1880 while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] ) 1881 ++nCnt; 1882 1883 Update( SwIndex( this, nMyOff ), nCnt, sal_False ); 1884 nMyOff = nOff; 1885 //nMyOff -= nCnt; 1886 nI += nCnt - 1; 1887 } 1888 else if( nOff > nMyOff ) 1889 { 1890 // something is deleted 1891 Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, sal_True ); 1892 nMyOff = nOff; 1893 } 1894 } 1895 if( nMyOff < nLen ) 1896 // something is deleted at the end 1897 Update( SwIndex( this, nMyOff ), nLen - nMyOff, sal_True ); 1898 1899 // notify the layout! 1900 SwDelTxt aDelHint( nPos, nTLen ); 1901 NotifyClients( 0, &aDelHint ); 1902 1903 SwInsTxt aHint( nPos, nTLen ); 1904 NotifyClients( 0, &aHint ); 1905 } 1906 1907 void SwTxtNode::CountWords( SwDocStat& rStat, 1908 xub_StrLen nStt, xub_StrLen nEnd ) const 1909 { 1910 ++rStat.nAllPara; // #i93174#: count _all_ paragraphs 1911 if( nStt < nEnd ) 1912 { 1913 if ( !IsHidden() ) 1914 { 1915 ++rStat.nPara; 1916 sal_uLong nTmpWords = 0; 1917 sal_uLong nTmpChars = 0; 1918 1919 // Shortcut: Whole paragraph should be considered and cached values 1920 // are valid: 1921 if ( 0 == nStt && GetTxt().Len() == nEnd && !IsWordCountDirty() ) 1922 { 1923 nTmpWords = GetParaNumberOfWords(); 1924 nTmpChars = GetParaNumberOfChars(); 1925 } 1926 else 1927 { 1928 String aOldStr( m_Text ); 1929 String& rCastStr = const_cast<String&>(m_Text); 1930 1931 // fills the deleted redlines and hidden ranges with cChar: 1932 const xub_Unicode cChar(' '); 1933 const sal_uInt16 nNumOfMaskedChars = 1934 lcl_MaskRedlinesAndHiddenText( *this, rCastStr, nStt, nEnd, cChar, false ); 1935 1936 // expand fields 1937 rtl::OUString aExpandText; 1938 const ModelToViewHelper::ConversionMap* pConversionMap = 1939 BuildConversionMap( aExpandText ); 1940 1941 const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nStt ); 1942 const sal_uInt32 nExpandEnd = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nEnd ); 1943 aExpandText = aExpandText.copy( nExpandBegin, nExpandEnd - nExpandBegin ); 1944 1945 const bool bCount = aExpandText.getLength() > 0; 1946 1947 // count words in 'regular' text: 1948 if( bCount && pBreakIt->GetBreakIter().is() ) 1949 { 1950 // split into different script languages 1951 sal_Int32 nScriptBegin = 0; 1952 while ( nScriptBegin < aExpandText.getLength() ) 1953 { 1954 const sal_Int16 nCurrScript = pBreakIt->GetBreakIter()->getScriptType( aExpandText, nScriptBegin ); 1955 const sal_Int32 nScriptEnd = pBreakIt->GetBreakIter()->endOfScript( aExpandText, nScriptBegin, nCurrScript ); 1956 rtl::OUString aScriptText = aExpandText.copy( nScriptBegin, nScriptEnd - nScriptBegin ); 1957 1958 // Asian languages count words as characters 1959 if ( nCurrScript == ::com::sun::star::i18n::ScriptType::ASIAN ) 1960 { 1961 // substract white spaces 1962 sal_Int32 nSpaceCount = 0; 1963 sal_Int32 nSpacePos = 0; 1964 1965 // substract normal white spaces 1966 nSpacePos = -1; 1967 while ( ( nSpacePos = aScriptText.indexOf( ' ', nSpacePos + 1 ) ) != -1 ) 1968 { 1969 nSpaceCount++; 1970 } 1971 // substract Asian full-width white spaces 1972 nSpacePos = -1; 1973 while ( ( nSpacePos = aScriptText.indexOf( 12288, nSpacePos + 1 ) ) != -1 ) 1974 { 1975 nSpaceCount++; 1976 } 1977 nTmpWords += nScriptEnd - nScriptBegin - nSpaceCount; 1978 } 1979 else 1980 { 1981 const String aScannerText( aScriptText ); 1982 SwScanner aScanner( *this, aScannerText, 0, pConversionMap, 1983 i18n::WordType::WORD_COUNT, 1984 (xub_StrLen)0, (xub_StrLen)aScriptText.getLength() ); 1985 1986 const rtl::OUString aBreakWord( CH_TXTATR_BREAKWORD ); 1987 1988 while ( aScanner.NextWord() ) 1989 { 1990 if ( aScanner.GetLen() > 1 || 1991 CH_TXTATR_BREAKWORD != aScriptText.match(aBreakWord, aScanner.GetBegin() ) ) 1992 ++nTmpWords; 1993 } 1994 } 1995 nScriptBegin = nScriptEnd; 1996 } 1997 } 1998 1999 ASSERT( aExpandText.getLength() >= nNumOfMaskedChars, 2000 "More characters hidden that characters in string!" ) 2001 nTmpChars = nExpandEnd - nExpandBegin - nNumOfMaskedChars; 2002 2003 // count words in numbering string: 2004 if ( nStt == 0 && bCount ) 2005 { 2006 // add numbering label 2007 const String aNumString = GetNumString(); 2008 const xub_StrLen nNumStringLen = aNumString.Len(); 2009 if ( nNumStringLen > 0 ) 2010 { 2011 LanguageType aLanguage = GetLang( 0 ); 2012 2013 SwScanner aScanner( *this, aNumString, &aLanguage, 0, 2014 i18n::WordType::WORD_COUNT, 2015 0, nNumStringLen ); 2016 2017 while ( aScanner.NextWord() ) 2018 ++nTmpWords; 2019 2020 nTmpChars += nNumStringLen; 2021 } 2022 else if ( HasBullet() ) 2023 { 2024 ++nTmpWords; 2025 ++nTmpChars; 2026 } 2027 } 2028 2029 delete pConversionMap; 2030 2031 rCastStr = aOldStr; 2032 2033 // If the whole paragraph has been calculated, update cached 2034 // values: 2035 if ( 0 == nStt && GetTxt().Len() == nEnd ) 2036 { 2037 SetParaNumberOfWords( nTmpWords ); 2038 SetParaNumberOfChars( nTmpChars ); 2039 SetWordCountDirty( false ); 2040 } 2041 } 2042 2043 rStat.nWord += nTmpWords; 2044 rStat.nChar += nTmpChars; 2045 } 2046 } 2047 } 2048 2049 // 2050 // Paragraph statistics start 2051 // 2052 struct SwParaIdleData_Impl 2053 { 2054 SwWrongList* pWrong; // for spell checking 2055 SwGrammarMarkUp* pGrammarCheck; // for grammar checking / proof reading 2056 SwWrongList* pSmartTags; 2057 sal_uLong nNumberOfWords; 2058 sal_uLong nNumberOfChars; 2059 bool bWordCountDirty : 1; 2060 bool bWrongDirty : 1; // Ist das Wrong-Feld auf invalid? 2061 bool bGrammarCheckDirty : 1; 2062 bool bSmartTagDirty : 1; 2063 bool bAutoComplDirty : 1; // die ACompl-Liste muss angepasst werden 2064 2065 SwParaIdleData_Impl() : 2066 pWrong ( 0 ), 2067 pGrammarCheck ( 0 ), 2068 pSmartTags ( 0 ), 2069 nNumberOfWords ( 0 ), 2070 nNumberOfChars ( 0 ), 2071 bWordCountDirty ( true ), 2072 bWrongDirty ( true ), 2073 bGrammarCheckDirty ( true ), 2074 bSmartTagDirty ( true ), 2075 bAutoComplDirty ( true ) {}; 2076 }; 2077 2078 void SwTxtNode::InitSwParaStatistics( bool bNew ) 2079 { 2080 if ( bNew ) 2081 { 2082 m_pParaIdleData_Impl = new SwParaIdleData_Impl; 2083 } 2084 else if ( m_pParaIdleData_Impl ) 2085 { 2086 delete m_pParaIdleData_Impl->pWrong; 2087 delete m_pParaIdleData_Impl->pGrammarCheck; 2088 delete m_pParaIdleData_Impl->pSmartTags; 2089 delete m_pParaIdleData_Impl; 2090 m_pParaIdleData_Impl = 0; 2091 } 2092 } 2093 2094 void SwTxtNode::SetWrong( SwWrongList* pNew, bool bDelete ) 2095 { 2096 if ( m_pParaIdleData_Impl ) 2097 { 2098 if ( bDelete ) 2099 { 2100 delete m_pParaIdleData_Impl->pWrong; 2101 } 2102 m_pParaIdleData_Impl->pWrong = pNew; 2103 } 2104 } 2105 2106 SwWrongList* SwTxtNode::GetWrong() 2107 { 2108 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0; 2109 } 2110 2111 // --> OD 2008-05-27 #i71360# 2112 const SwWrongList* SwTxtNode::GetWrong() const 2113 { 2114 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0; 2115 } 2116 // <-- 2117 2118 2119 void SwTxtNode::SetGrammarCheck( SwGrammarMarkUp* pNew, bool bDelete ) 2120 { 2121 if ( m_pParaIdleData_Impl ) 2122 { 2123 if ( bDelete ) 2124 { 2125 delete m_pParaIdleData_Impl->pGrammarCheck; 2126 } 2127 m_pParaIdleData_Impl->pGrammarCheck = pNew; 2128 } 2129 } 2130 2131 SwGrammarMarkUp* SwTxtNode::GetGrammarCheck() 2132 { 2133 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : 0; 2134 } 2135 2136 void SwTxtNode::SetSmartTags( SwWrongList* pNew, bool bDelete ) 2137 { 2138 ASSERT( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(), 2139 "Weird - we have a smart tag list without any recognizers?" ) 2140 2141 if ( m_pParaIdleData_Impl ) 2142 { 2143 if ( bDelete ) 2144 { 2145 delete m_pParaIdleData_Impl->pSmartTags; 2146 } 2147 m_pParaIdleData_Impl->pSmartTags = pNew; 2148 } 2149 } 2150 2151 SwWrongList* SwTxtNode::GetSmartTags() 2152 { 2153 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : 0; 2154 } 2155 2156 void SwTxtNode::SetParaNumberOfWords( sal_uLong nNew ) const 2157 { 2158 if ( m_pParaIdleData_Impl ) 2159 { 2160 m_pParaIdleData_Impl->nNumberOfWords = nNew; 2161 } 2162 } 2163 sal_uLong SwTxtNode::GetParaNumberOfWords() const 2164 { 2165 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfWords : 0; 2166 } 2167 void SwTxtNode::SetParaNumberOfChars( sal_uLong nNew ) const 2168 { 2169 if ( m_pParaIdleData_Impl ) 2170 { 2171 m_pParaIdleData_Impl->nNumberOfChars = nNew; 2172 } 2173 } 2174 sal_uLong SwTxtNode::GetParaNumberOfChars() const 2175 { 2176 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfChars : 0; 2177 } 2178 void SwTxtNode::SetWordCountDirty( bool bNew ) const 2179 { 2180 if ( m_pParaIdleData_Impl ) 2181 { 2182 m_pParaIdleData_Impl->bWordCountDirty = bNew; 2183 } 2184 } 2185 bool SwTxtNode::IsWordCountDirty() const 2186 { 2187 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWordCountDirty : 0; 2188 } 2189 void SwTxtNode::SetWrongDirty( bool bNew ) const 2190 { 2191 if ( m_pParaIdleData_Impl ) 2192 { 2193 m_pParaIdleData_Impl->bWrongDirty = bNew; 2194 } 2195 } 2196 bool SwTxtNode::IsWrongDirty() const 2197 { 2198 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWrongDirty : 0; 2199 } 2200 void SwTxtNode::SetGrammarCheckDirty( bool bNew ) const 2201 { 2202 if ( m_pParaIdleData_Impl ) 2203 { 2204 m_pParaIdleData_Impl->bGrammarCheckDirty = bNew; 2205 } 2206 } 2207 bool SwTxtNode::IsGrammarCheckDirty() const 2208 { 2209 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bGrammarCheckDirty : 0; 2210 } 2211 void SwTxtNode::SetSmartTagDirty( bool bNew ) const 2212 { 2213 if ( m_pParaIdleData_Impl ) 2214 { 2215 m_pParaIdleData_Impl->bSmartTagDirty = bNew; 2216 } 2217 } 2218 bool SwTxtNode::IsSmartTagDirty() const 2219 { 2220 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bSmartTagDirty : 0; 2221 } 2222 void SwTxtNode::SetAutoCompleteWordDirty( bool bNew ) const 2223 { 2224 if ( m_pParaIdleData_Impl ) 2225 { 2226 m_pParaIdleData_Impl->bAutoComplDirty = bNew; 2227 } 2228 } 2229 bool SwTxtNode::IsAutoCompleteWordDirty() const 2230 { 2231 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bAutoComplDirty : 0; 2232 } 2233 // 2234 // Paragraph statistics end 2235 // 2236 2237 //Bug 120881:Modify here for Directly Page Numbering 2238 sal_Bool SwTxtFrm::HasPageNumberField() 2239 { 2240 return GetRegisteredIn()?((SwTxtNode*)GetRegisteredIn())->HasPageNumberField():false; 2241 } 2242 //Bug 120881(End) 2243 2244