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 1594 struct TransliterationChgData 1595 { 1596 xub_StrLen nStart; 1597 xub_StrLen nLen; 1598 String sChanged; 1599 Sequence< sal_Int32 > aOffsets; 1600 }; 1601 1602 // change text to Upper/Lower/Hiragana/Katagana/... 1603 void SwTxtNode::TransliterateText( 1604 utl::TransliterationWrapper& rTrans, 1605 xub_StrLen nStt, xub_StrLen nEnd, 1606 SwUndoTransliterate* pUndo ) 1607 { 1608 if (nStt < nEnd && pBreakIt->GetBreakIter().is()) 1609 { 1610 // since we don't use Hiragana/Katakana or half-width/full-width transliterations here 1611 // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will 1612 // occasionaly miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES 1613 // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the 1614 // proper thing to do. 1615 const sal_Int16 nWordType = WordType::ANYWORD_IGNOREWHITESPACES; 1616 1617 //! In order to have less trouble with changing text size, e.g. because 1618 //! of ligatures or � (German small sz) being resolved, we need to process 1619 //! the text replacements from end to start. 1620 //! This way the offsets for the yet to be changed words will be 1621 //! left unchanged by the already replaced text. 1622 //! For this we temporarily save the changes to be done in this vector 1623 std::vector< TransliterationChgData > aChanges; 1624 TransliterationChgData aChgData; 1625 1626 if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::TITLE_CASE) 1627 { 1628 // for 'capitalize every word' we need to iterate over each word 1629 1630 Boundary aSttBndry; 1631 Boundary aEndBndry; 1632 aSttBndry = pBreakIt->GetBreakIter()->getWordBoundary( 1633 GetTxt(), nStt, 1634 pBreakIt->GetLocale( GetLang( nStt ) ), 1635 nWordType, 1636 sal_True /*prefer forward direction*/); 1637 aEndBndry = pBreakIt->GetBreakIter()->getWordBoundary( 1638 GetTxt(), nEnd, 1639 pBreakIt->GetLocale( GetLang( nEnd ) ), 1640 nWordType, 1641 sal_False /*prefer backward direction*/); 1642 1643 // prevent backtracking to the previous word if selection is at word boundary 1644 if (aSttBndry.endPos <= nStt) 1645 { 1646 aSttBndry = pBreakIt->GetBreakIter()->nextWord( 1647 GetTxt(), aSttBndry.endPos, 1648 pBreakIt->GetLocale( GetLang( aSttBndry.endPos ) ), 1649 nWordType); 1650 } 1651 // prevent advancing to the next word if selection is at word boundary 1652 if (aEndBndry.startPos >= nEnd) 1653 { 1654 aEndBndry = pBreakIt->GetBreakIter()->previousWord( 1655 GetTxt(), aEndBndry.startPos, 1656 pBreakIt->GetLocale( GetLang( aEndBndry.startPos ) ), 1657 nWordType); 1658 } 1659 1660 Boundary aCurWordBndry( aSttBndry ); 1661 while (aCurWordBndry.startPos <= aEndBndry.startPos) 1662 { 1663 nStt = (xub_StrLen)aCurWordBndry.startPos; 1664 nEnd = (xub_StrLen)aCurWordBndry.endPos; 1665 sal_Int32 nLen = nEnd - nStt; 1666 DBG_ASSERT( nLen > 0, "invalid word length of 0" ); 1667 #if OSL_DEBUG_LEVEL > 1 1668 String aText( GetTxt().Copy( nStt, nLen ) ); 1669 #endif 1670 1671 Sequence <sal_Int32> aOffsets; 1672 String sChgd( rTrans.transliterate( GetTxt(), GetLang( nStt ), nStt, nLen, &aOffsets )); 1673 1674 if (!m_Text.Equals( sChgd, nStt, nLen )) 1675 { 1676 aChgData.nStart = nStt; 1677 aChgData.nLen = nLen; 1678 aChgData.sChanged = sChgd; 1679 aChgData.aOffsets = aOffsets; 1680 aChanges.push_back( aChgData ); 1681 } 1682 1683 aCurWordBndry = pBreakIt->GetBreakIter()->nextWord( 1684 GetTxt(), nEnd, 1685 pBreakIt->GetLocale( GetLang( nEnd ) ), 1686 nWordType); 1687 } 1688 } 1689 else if (rTrans.getType() == (sal_uInt32)TransliterationModulesExtra::SENTENCE_CASE) 1690 { 1691 // for 'sentence case' we need to iterate sentence by sentence 1692 1693 sal_Int32 nLastStart = pBreakIt->GetBreakIter()->beginOfSentence( 1694 GetTxt(), nEnd, 1695 pBreakIt->GetLocale( GetLang( nEnd ) ) ); 1696 sal_Int32 nLastEnd = pBreakIt->GetBreakIter()->endOfSentence( 1697 GetTxt(), nLastStart, 1698 pBreakIt->GetLocale( GetLang( nLastStart ) ) ); 1699 1700 // extend nStt, nEnd to the current sentence boundaries 1701 sal_Int32 nCurrentStart = pBreakIt->GetBreakIter()->beginOfSentence( 1702 GetTxt(), nStt, 1703 pBreakIt->GetLocale( GetLang( nStt ) ) ); 1704 sal_Int32 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( 1705 GetTxt(), nCurrentStart, 1706 pBreakIt->GetLocale( GetLang( nCurrentStart ) ) ); 1707 1708 // prevent backtracking to the previous sentence if selection starts at end of a sentence 1709 if (nCurrentEnd <= nStt) 1710 { 1711 // now nCurrentStart is probably located on a non-letter word. (unless we 1712 // are in Asian text with no spaces...) 1713 // Thus to get the real sentence start we should locate the next real word, 1714 // that is one found by DICTIONARY_WORD 1715 i18n::Boundary aBndry = pBreakIt->GetBreakIter()->nextWord( 1716 GetTxt(), nCurrentEnd, 1717 pBreakIt->GetLocale( GetLang( nCurrentEnd ) ), 1718 i18n::WordType::DICTIONARY_WORD); 1719 1720 // now get new current sentence boundaries 1721 nCurrentStart = pBreakIt->GetBreakIter()->beginOfSentence( 1722 GetTxt(), aBndry.startPos, 1723 pBreakIt->GetLocale( GetLang( aBndry.startPos) ) ); 1724 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( 1725 GetTxt(), nCurrentStart, 1726 pBreakIt->GetLocale( GetLang( nCurrentStart) ) ); 1727 } 1728 // prevent advancing to the next sentence if selection ends at start of a sentence 1729 if (nLastStart >= nEnd) 1730 { 1731 // now nCurrentStart is probably located on a non-letter word. (unless we 1732 // are in Asian text with no spaces...) 1733 // Thus to get the real sentence start we should locate the previous real word, 1734 // that is one found by DICTIONARY_WORD 1735 i18n::Boundary aBndry = pBreakIt->GetBreakIter()->previousWord( 1736 GetTxt(), nLastStart, 1737 pBreakIt->GetLocale( GetLang( nLastStart) ), 1738 i18n::WordType::DICTIONARY_WORD); 1739 nLastEnd = pBreakIt->GetBreakIter()->endOfSentence( 1740 GetTxt(), aBndry.startPos, 1741 pBreakIt->GetLocale( GetLang( aBndry.startPos) ) ); 1742 if (nCurrentEnd > nLastEnd) 1743 nCurrentEnd = nLastEnd; 1744 } 1745 1746 while (nCurrentStart < nLastEnd) 1747 { 1748 sal_Int32 nLen = nCurrentEnd - nCurrentStart; 1749 DBG_ASSERT( nLen > 0, "invalid word length of 0" ); 1750 #if OSL_DEBUG_LEVEL > 1 1751 String aText( GetTxt().Copy( nCurrentStart, nLen ) ); 1752 #endif 1753 1754 Sequence <sal_Int32> aOffsets; 1755 String sChgd( rTrans.transliterate( GetTxt(), 1756 GetLang( nCurrentStart ), nCurrentStart, nLen, &aOffsets )); 1757 1758 if (!m_Text.Equals( sChgd, nStt, nLen )) 1759 { 1760 aChgData.nStart = nCurrentStart; 1761 aChgData.nLen = nLen; 1762 aChgData.sChanged = sChgd; 1763 aChgData.aOffsets = aOffsets; 1764 aChanges.push_back( aChgData ); 1765 } 1766 1767 Boundary aFirstWordBndry; 1768 aFirstWordBndry = pBreakIt->GetBreakIter()->nextWord( 1769 GetTxt(), nCurrentEnd, 1770 pBreakIt->GetLocale( GetLang( nCurrentEnd ) ), 1771 nWordType); 1772 nCurrentStart = aFirstWordBndry.startPos; 1773 nCurrentEnd = pBreakIt->GetBreakIter()->endOfSentence( 1774 GetTxt(), nCurrentStart, 1775 pBreakIt->GetLocale( GetLang( nCurrentStart ) ) ); 1776 } 1777 } 1778 else 1779 { 1780 // here we may transliterate over complete language portions... 1781 1782 SwLanguageIterator* pIter; 1783 if( rTrans.needLanguageForTheMode() ) 1784 pIter = new SwLanguageIterator( *this, nStt ); 1785 else 1786 pIter = 0; 1787 1788 xub_StrLen nEndPos; 1789 sal_uInt16 nLang; 1790 do { 1791 if( pIter ) 1792 { 1793 nLang = pIter->GetLanguage(); 1794 nEndPos = pIter->GetChgPos(); 1795 if( nEndPos > nEnd ) 1796 nEndPos = nEnd; 1797 } 1798 else 1799 { 1800 nLang = LANGUAGE_SYSTEM; 1801 nEndPos = nEnd; 1802 } 1803 xub_StrLen nLen = nEndPos - nStt; 1804 1805 Sequence <sal_Int32> aOffsets; 1806 String sChgd( rTrans.transliterate( m_Text, nLang, nStt, nLen, &aOffsets )); 1807 1808 if (!m_Text.Equals( sChgd, nStt, nLen )) 1809 { 1810 aChgData.nStart = nStt; 1811 aChgData.nLen = nLen; 1812 aChgData.sChanged = sChgd; 1813 aChgData.aOffsets = aOffsets; 1814 aChanges.push_back( aChgData ); 1815 } 1816 1817 nStt = nEndPos; 1818 } while( nEndPos < nEnd && pIter && pIter->Next() ); 1819 delete pIter; 1820 } 1821 1822 if (aChanges.size() > 0) 1823 { 1824 // now apply the changes from end to start to leave the offsets of the 1825 // yet unchanged text parts remain the same. 1826 for (size_t i = 0; i < aChanges.size(); ++i) 1827 { 1828 TransliterationChgData &rData = aChanges[ aChanges.size() - 1 - i ]; 1829 if (pUndo) 1830 pUndo->AddChanges( *this, rData.nStart, rData.nLen, rData.aOffsets ); 1831 ReplaceTextOnly( rData.nStart, rData.nLen, rData.sChanged, rData.aOffsets ); 1832 } 1833 } 1834 } 1835 } 1836 1837 1838 void SwTxtNode::ReplaceTextOnly( xub_StrLen nPos, xub_StrLen nLen, 1839 const XubString& rText, 1840 const Sequence<sal_Int32>& rOffsets ) 1841 { 1842 m_Text.Replace( nPos, nLen, rText ); 1843 1844 xub_StrLen nTLen = rText.Len(); 1845 const sal_Int32* pOffsets = rOffsets.getConstArray(); 1846 // now look for no 1-1 mapping -> move the indizies! 1847 xub_StrLen nI, nMyOff; 1848 for( nI = 0, nMyOff = nPos; nI < nTLen; ++nI, ++nMyOff ) 1849 { 1850 xub_StrLen nOff = (xub_StrLen)pOffsets[ nI ]; 1851 if( nOff < nMyOff ) 1852 { 1853 // something is inserted 1854 xub_StrLen nCnt = 1; 1855 while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] ) 1856 ++nCnt; 1857 1858 Update( SwIndex( this, nMyOff ), nCnt, sal_False ); 1859 nMyOff = nOff; 1860 //nMyOff -= nCnt; 1861 nI += nCnt - 1; 1862 } 1863 else if( nOff > nMyOff ) 1864 { 1865 // something is deleted 1866 Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, sal_True ); 1867 nMyOff = nOff; 1868 } 1869 } 1870 if( nMyOff < nLen ) 1871 // something is deleted at the end 1872 Update( SwIndex( this, nMyOff ), nLen - nMyOff, sal_True ); 1873 1874 // notify the layout! 1875 SwDelTxt aDelHint( nPos, nTLen ); 1876 NotifyClients( 0, &aDelHint ); 1877 1878 SwInsTxt aHint( nPos, nTLen ); 1879 NotifyClients( 0, &aHint ); 1880 } 1881 1882 void SwTxtNode::CountWords( SwDocStat& rStat, 1883 xub_StrLen nStt, xub_StrLen nEnd ) const 1884 { 1885 ++rStat.nAllPara; // #i93174#: count _all_ paragraphs 1886 if( nStt < nEnd ) 1887 { 1888 if ( !IsHidden() ) 1889 { 1890 ++rStat.nPara; 1891 sal_uLong nTmpWords = 0; 1892 sal_uLong nTmpChars = 0; 1893 1894 // Shortcut: Whole paragraph should be considered and cached values 1895 // are valid: 1896 if ( 0 == nStt && GetTxt().Len() == nEnd && !IsWordCountDirty() ) 1897 { 1898 nTmpWords = GetParaNumberOfWords(); 1899 nTmpChars = GetParaNumberOfChars(); 1900 } 1901 else 1902 { 1903 String aOldStr( m_Text ); 1904 String& rCastStr = const_cast<String&>(m_Text); 1905 1906 // fills the deleted redlines and hidden ranges with cChar: 1907 const xub_Unicode cChar(' '); 1908 const sal_uInt16 nNumOfMaskedChars = 1909 lcl_MaskRedlinesAndHiddenText( *this, rCastStr, nStt, nEnd, cChar, false ); 1910 1911 // expand fields 1912 rtl::OUString aExpandText; 1913 const ModelToViewHelper::ConversionMap* pConversionMap = 1914 BuildConversionMap( aExpandText ); 1915 1916 const sal_uInt32 nExpandBegin = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nStt ); 1917 const sal_uInt32 nExpandEnd = ModelToViewHelper::ConvertToViewPosition( pConversionMap, nEnd ); 1918 1919 const bool bCount = aExpandText.getLength() > 0; 1920 1921 // count words in 'regular' text: 1922 if( bCount && pBreakIt->GetBreakIter().is() ) 1923 { 1924 const String aScannerText( aExpandText ); 1925 SwScanner aScanner( *this, aScannerText, 0, pConversionMap, 1926 i18n::WordType::WORD_COUNT, 1927 (xub_StrLen)nExpandBegin, (xub_StrLen)nExpandEnd ); 1928 1929 const rtl::OUString aBreakWord( CH_TXTATR_BREAKWORD ); 1930 1931 while ( aScanner.NextWord() ) 1932 { 1933 if ( aScanner.GetLen() > 1 || 1934 CH_TXTATR_BREAKWORD != aExpandText.match(aBreakWord, aScanner.GetBegin() ) ) 1935 ++nTmpWords; 1936 } 1937 } 1938 1939 ASSERT( aExpandText.getLength() >= nNumOfMaskedChars, 1940 "More characters hidden that characters in string!" ) 1941 nTmpChars = nExpandEnd - nExpandBegin - nNumOfMaskedChars; 1942 1943 // count words in numbering string: 1944 if ( nStt == 0 && bCount ) 1945 { 1946 // add numbering label 1947 const String aNumString = GetNumString(); 1948 const xub_StrLen nNumStringLen = aNumString.Len(); 1949 if ( nNumStringLen > 0 ) 1950 { 1951 LanguageType aLanguage = GetLang( 0 ); 1952 1953 SwScanner aScanner( *this, aNumString, &aLanguage, 0, 1954 i18n::WordType::WORD_COUNT, 1955 0, nNumStringLen ); 1956 1957 while ( aScanner.NextWord() ) 1958 ++nTmpWords; 1959 1960 nTmpChars += nNumStringLen; 1961 } 1962 else if ( HasBullet() ) 1963 { 1964 ++nTmpWords; 1965 ++nTmpChars; 1966 } 1967 } 1968 1969 delete pConversionMap; 1970 1971 rCastStr = aOldStr; 1972 1973 // If the whole paragraph has been calculated, update cached 1974 // values: 1975 if ( 0 == nStt && GetTxt().Len() == nEnd ) 1976 { 1977 SetParaNumberOfWords( nTmpWords ); 1978 SetParaNumberOfChars( nTmpChars ); 1979 SetWordCountDirty( false ); 1980 } 1981 } 1982 1983 rStat.nWord += nTmpWords; 1984 rStat.nChar += nTmpChars; 1985 } 1986 } 1987 } 1988 1989 // 1990 // Paragraph statistics start 1991 // 1992 struct SwParaIdleData_Impl 1993 { 1994 SwWrongList* pWrong; // for spell checking 1995 SwGrammarMarkUp* pGrammarCheck; // for grammar checking / proof reading 1996 SwWrongList* pSmartTags; 1997 sal_uLong nNumberOfWords; 1998 sal_uLong nNumberOfChars; 1999 bool bWordCountDirty : 1; 2000 bool bWrongDirty : 1; // Ist das Wrong-Feld auf invalid? 2001 bool bGrammarCheckDirty : 1; 2002 bool bSmartTagDirty : 1; 2003 bool bAutoComplDirty : 1; // die ACompl-Liste muss angepasst werden 2004 2005 SwParaIdleData_Impl() : 2006 pWrong ( 0 ), 2007 pGrammarCheck ( 0 ), 2008 pSmartTags ( 0 ), 2009 nNumberOfWords ( 0 ), 2010 nNumberOfChars ( 0 ), 2011 bWordCountDirty ( true ), 2012 bWrongDirty ( true ), 2013 bGrammarCheckDirty ( true ), 2014 bSmartTagDirty ( true ), 2015 bAutoComplDirty ( true ) {}; 2016 }; 2017 2018 void SwTxtNode::InitSwParaStatistics( bool bNew ) 2019 { 2020 if ( bNew ) 2021 { 2022 m_pParaIdleData_Impl = new SwParaIdleData_Impl; 2023 } 2024 else if ( m_pParaIdleData_Impl ) 2025 { 2026 delete m_pParaIdleData_Impl->pWrong; 2027 delete m_pParaIdleData_Impl->pGrammarCheck; 2028 delete m_pParaIdleData_Impl->pSmartTags; 2029 delete m_pParaIdleData_Impl; 2030 m_pParaIdleData_Impl = 0; 2031 } 2032 } 2033 2034 void SwTxtNode::SetWrong( SwWrongList* pNew, bool bDelete ) 2035 { 2036 if ( m_pParaIdleData_Impl ) 2037 { 2038 if ( bDelete ) 2039 { 2040 delete m_pParaIdleData_Impl->pWrong; 2041 } 2042 m_pParaIdleData_Impl->pWrong = pNew; 2043 } 2044 } 2045 2046 SwWrongList* SwTxtNode::GetWrong() 2047 { 2048 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0; 2049 } 2050 2051 // --> OD 2008-05-27 #i71360# 2052 const SwWrongList* SwTxtNode::GetWrong() const 2053 { 2054 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong : 0; 2055 } 2056 // <-- 2057 2058 2059 void SwTxtNode::SetGrammarCheck( SwGrammarMarkUp* pNew, bool bDelete ) 2060 { 2061 if ( m_pParaIdleData_Impl ) 2062 { 2063 if ( bDelete ) 2064 { 2065 delete m_pParaIdleData_Impl->pGrammarCheck; 2066 } 2067 m_pParaIdleData_Impl->pGrammarCheck = pNew; 2068 } 2069 } 2070 2071 SwGrammarMarkUp* SwTxtNode::GetGrammarCheck() 2072 { 2073 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck : 0; 2074 } 2075 2076 void SwTxtNode::SetSmartTags( SwWrongList* pNew, bool bDelete ) 2077 { 2078 ASSERT( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(), 2079 "Weird - we have a smart tag list without any recognizers?" ) 2080 2081 if ( m_pParaIdleData_Impl ) 2082 { 2083 if ( bDelete ) 2084 { 2085 delete m_pParaIdleData_Impl->pSmartTags; 2086 } 2087 m_pParaIdleData_Impl->pSmartTags = pNew; 2088 } 2089 } 2090 2091 SwWrongList* SwTxtNode::GetSmartTags() 2092 { 2093 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags : 0; 2094 } 2095 2096 void SwTxtNode::SetParaNumberOfWords( sal_uLong nNew ) const 2097 { 2098 if ( m_pParaIdleData_Impl ) 2099 { 2100 m_pParaIdleData_Impl->nNumberOfWords = nNew; 2101 } 2102 } 2103 sal_uLong SwTxtNode::GetParaNumberOfWords() const 2104 { 2105 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfWords : 0; 2106 } 2107 void SwTxtNode::SetParaNumberOfChars( sal_uLong nNew ) const 2108 { 2109 if ( m_pParaIdleData_Impl ) 2110 { 2111 m_pParaIdleData_Impl->nNumberOfChars = nNew; 2112 } 2113 } 2114 sal_uLong SwTxtNode::GetParaNumberOfChars() const 2115 { 2116 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->nNumberOfChars : 0; 2117 } 2118 void SwTxtNode::SetWordCountDirty( bool bNew ) const 2119 { 2120 if ( m_pParaIdleData_Impl ) 2121 { 2122 m_pParaIdleData_Impl->bWordCountDirty = bNew; 2123 } 2124 } 2125 bool SwTxtNode::IsWordCountDirty() const 2126 { 2127 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWordCountDirty : 0; 2128 } 2129 void SwTxtNode::SetWrongDirty( bool bNew ) const 2130 { 2131 if ( m_pParaIdleData_Impl ) 2132 { 2133 m_pParaIdleData_Impl->bWrongDirty = bNew; 2134 } 2135 } 2136 bool SwTxtNode::IsWrongDirty() const 2137 { 2138 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bWrongDirty : 0; 2139 } 2140 void SwTxtNode::SetGrammarCheckDirty( bool bNew ) const 2141 { 2142 if ( m_pParaIdleData_Impl ) 2143 { 2144 m_pParaIdleData_Impl->bGrammarCheckDirty = bNew; 2145 } 2146 } 2147 bool SwTxtNode::IsGrammarCheckDirty() const 2148 { 2149 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bGrammarCheckDirty : 0; 2150 } 2151 void SwTxtNode::SetSmartTagDirty( bool bNew ) const 2152 { 2153 if ( m_pParaIdleData_Impl ) 2154 { 2155 m_pParaIdleData_Impl->bSmartTagDirty = bNew; 2156 } 2157 } 2158 bool SwTxtNode::IsSmartTagDirty() const 2159 { 2160 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bSmartTagDirty : 0; 2161 } 2162 void SwTxtNode::SetAutoCompleteWordDirty( bool bNew ) const 2163 { 2164 if ( m_pParaIdleData_Impl ) 2165 { 2166 m_pParaIdleData_Impl->bAutoComplDirty = bNew; 2167 } 2168 } 2169 bool SwTxtNode::IsAutoCompleteWordDirty() const 2170 { 2171 return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->bAutoComplDirty : 0; 2172 } 2173 // 2174 // Paragraph statistics end 2175 // 2176