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