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