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