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