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