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 #include <unotextmarkup.hxx> 27 28 #include <vos/mutex.hxx> 29 #include <vcl/svapp.hxx> 30 #include <SwSmartTagMgr.hxx> 31 #include <com/sun/star/text/TextMarkupType.hpp> 32 #include <com/sun/star/text/TextMarkupDescriptor.hpp> 33 #include <com/sun/star/container/XStringKeyMap.hpp> 34 #include <ndtxt.hxx> 35 #include <SwGrammarMarkUp.hxx> 36 37 #include <IGrammarContact.hxx> 38 39 #include <com/sun/star/lang/XUnoTunnel.hpp> 40 #include <com/sun/star/text/XTextRange.hpp> 41 42 #include <pam.hxx> 43 44 #include <unotextrange.hxx> 45 #include <unotextcursor.hxx> 46 47 48 using namespace ::com::sun::star; 49 50 /* 51 * SwXTextMarkup 52 */ 53 SwXTextMarkup::SwXTextMarkup( SwTxtNode& rTxtNode, const ModelToViewHelper::ConversionMap* pMap ) 54 : mpTxtNode( &rTxtNode ), mpConversionMap( pMap ) 55 { 56 // FME 2007-07-16 #i79641# SwXTextMarkup is allowed to be removed ... 57 SetIsAllowedToBeRemovedInModifyCall(true); 58 mpTxtNode->Add(this); 59 } 60 61 SwXTextMarkup::~SwXTextMarkup() 62 { 63 delete mpConversionMap; 64 } 65 66 uno::Reference< container::XStringKeyMap > SAL_CALL SwXTextMarkup::getMarkupInfoContainer() 67 { 68 vos::OGuard aGuard(Application::GetSolarMutex()); 69 70 uno::Reference< container::XStringKeyMap > xProp = new SwXStringKeyMap; 71 return xProp; 72 } 73 74 void SAL_CALL SwXTextMarkup::commitTextRangeMarkup(::sal_Int32 nType, const ::rtl::OUString & aIdentifier, const uno::Reference< text::XTextRange> & xRange, 75 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer) 76 { 77 vos::OGuard aGuard(Application::GetSolarMutex()); 78 79 uno::Reference<lang::XUnoTunnel> xRangeTunnel( xRange, uno::UNO_QUERY); 80 81 if(!xRangeTunnel.is()) return; 82 83 SwXTextRange* pRange = 0; 84 OTextCursorHelper* pCursor = 0; 85 86 if(xRangeTunnel.is()) 87 { 88 pRange = reinterpret_cast<SwXTextRange*>( sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething(SwXTextRange::getUnoTunnelId()))); 89 pCursor = reinterpret_cast<OTextCursorHelper*>( sal::static_int_cast< sal_IntPtr >( xRangeTunnel->getSomething(OTextCursorHelper::getUnoTunnelId()))); 90 } 91 92 if (pRange) 93 { 94 SwDoc* pDoc = reinterpret_cast<SwDoc*>(pRange->GetDoc()); 95 96 if (!pDoc) return; 97 98 SwUnoInternalPaM aPam(*pDoc); 99 100 ::sw::XTextRangeToSwPaM(aPam, xRange); 101 102 SwPosition* startPos = aPam.Start(); 103 SwPosition* endPos = aPam.End(); 104 105 commitStringMarkup (nType, aIdentifier, startPos->nContent.GetIndex(), endPos->nContent.GetIndex() - startPos->nContent.GetIndex(), xMarkupInfoContainer); 106 } 107 else if (pCursor) 108 { 109 SwPaM aPam(*pCursor->GetPaM()); 110 111 SwPosition* startPos = aPam.Start(); 112 SwPosition* endPos = aPam.End(); 113 114 commitStringMarkup (nType, aIdentifier, startPos->nContent.GetIndex(), endPos->nContent.GetIndex() - startPos->nContent.GetIndex(), xMarkupInfoContainer); 115 } 116 } 117 118 119 void SAL_CALL SwXTextMarkup::commitStringMarkup( 120 ::sal_Int32 nType, 121 const ::rtl::OUString & rIdentifier, 122 ::sal_Int32 nStart, 123 ::sal_Int32 nLength, 124 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer) 125 { 126 vos::OGuard aGuard(Application::GetSolarMutex()); 127 128 // paragraph already dead or modified? 129 if ( !mpTxtNode || nLength <= 0 ) 130 return; 131 132 if ( nType == text::TextMarkupType::SMARTTAG && 133 !SwSmartTagMgr::Get().IsSmartTagTypeEnabled( rIdentifier ) ) 134 return; 135 136 // get appropriate list to use... 137 SwWrongList* pWList = 0; 138 bool bRepaint = false; 139 if ( nType == text::TextMarkupType::SPELLCHECK ) 140 { 141 pWList = mpTxtNode->GetWrong(); 142 if ( !pWList ) 143 { 144 pWList = new SwWrongList( WRONGLIST_SPELL ); 145 mpTxtNode->SetWrong( pWList ); 146 } 147 } 148 else if ( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE ) 149 { 150 IGrammarContact *pGrammarContact = getGrammarContact( *mpTxtNode ); 151 if( pGrammarContact ) 152 { 153 pWList = pGrammarContact->getGrammarCheck( *mpTxtNode, true ); 154 ASSERT( pWList, "GrammarContact _has_ to deliver a wrong list" ) 155 } 156 else 157 { 158 pWList = mpTxtNode->GetGrammarCheck(); 159 if ( !pWList ) 160 { 161 mpTxtNode->SetGrammarCheck( new SwGrammarMarkUp() ); 162 pWList = mpTxtNode->GetGrammarCheck(); 163 } 164 } 165 bRepaint = pWList == mpTxtNode->GetGrammarCheck(); 166 if( pWList->GetBeginInv() < STRING_LEN ) 167 ((SwGrammarMarkUp*)pWList)->ClearGrammarList(); 168 } 169 else if ( nType == text::TextMarkupType::SMARTTAG ) 170 { 171 pWList = mpTxtNode->GetSmartTags(); 172 if ( !pWList ) 173 { 174 pWList = new SwWrongList( WRONGLIST_SMARTTAG ); 175 mpTxtNode->SetSmartTags( pWList ); 176 } 177 } 178 else 179 { 180 ASSERT( false, "Unknown mark-up type" ) 181 return; 182 } 183 184 185 const ModelToViewHelper::ModelPosition aStartPos = 186 ModelToViewHelper::ConvertToModelPosition( mpConversionMap, nStart ); 187 const ModelToViewHelper::ModelPosition aEndPos = 188 ModelToViewHelper::ConvertToModelPosition( mpConversionMap, nStart + nLength - 1); 189 190 const bool bStartInField = aStartPos.mbIsField; 191 const bool bEndInField = aEndPos.mbIsField; 192 bool bCommit = false; 193 194 if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos ) 195 { 196 nStart = aStartPos.mnSubPos; 197 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aStartPos.mnPos); 198 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); 199 200 SwWrongList* pSubList = pWList->SubList( nInsertPos ); 201 if ( !pSubList ) 202 { 203 if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE ) 204 pSubList = new SwGrammarMarkUp(); 205 else 206 pSubList = new SwWrongList( pWList->GetWrongListType() ); 207 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); 208 } 209 210 pWList = pSubList; 211 bCommit = true; 212 } 213 else if ( !bStartInField && !bEndInField ) 214 { 215 nStart = aStartPos.mnPos; 216 bCommit = true; 217 nLength = aEndPos.mnPos + 1 - aStartPos.mnPos; 218 } 219 else if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE ) 220 { 221 bCommit = true; 222 nStart = aStartPos.mnPos; 223 sal_Int32 nEnd = aEndPos.mnPos; 224 if( bStartInField && nType != text::TextMarkupType::SENTENCE ) 225 { 226 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aStartPos.mnPos); 227 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); 228 SwWrongList* pSubList = pWList->SubList( nInsertPos ); 229 if ( !pSubList ) 230 { 231 pSubList = new SwGrammarMarkUp(); 232 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); 233 } 234 const sal_uInt32 nTmpStart = ModelToViewHelper::ConvertToViewPosition( mpConversionMap, aStartPos.mnPos ); 235 const sal_uInt32 nTmpLen = ModelToViewHelper::ConvertToViewPosition( mpConversionMap, aStartPos.mnPos + 1 ) 236 - nTmpStart - aStartPos.mnSubPos; 237 if( nTmpLen > 0 ) 238 { 239 if( nType == text::TextMarkupType::SENTENCE ) 240 { 241 ((SwGrammarMarkUp*)pSubList)->setSentence( static_cast< xub_StrLen >(aStartPos.mnSubPos) ); 242 bCommit = false; 243 } 244 else 245 pSubList->Insert( rIdentifier, xMarkupInfoContainer, 246 static_cast< xub_StrLen >(aStartPos.mnSubPos), static_cast< xub_StrLen >(nTmpLen) ); 247 } 248 ++nStart; 249 } 250 if( bEndInField && nType != text::TextMarkupType::SENTENCE ) 251 { 252 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aEndPos.mnPos); 253 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); 254 SwWrongList* pSubList = pWList->SubList( nInsertPos ); 255 if ( !pSubList ) 256 { 257 pSubList = new SwGrammarMarkUp(); 258 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); 259 } 260 const sal_uInt32 nTmpLen = aEndPos.mnSubPos + 1; 261 pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, static_cast< xub_StrLen >(nTmpLen) ); 262 } 263 else 264 ++nEnd; 265 if( nEnd > nStart ) 266 nLength = nEnd - nStart; 267 else 268 bCommit = false; 269 } 270 271 if ( bCommit ) 272 { 273 if( nType == text::TextMarkupType::SENTENCE ) 274 ((SwGrammarMarkUp*)pWList)->setSentence( static_cast< xub_StrLen >(nStart) ); 275 else 276 pWList->Insert( rIdentifier, xMarkupInfoContainer, 277 static_cast< xub_StrLen >(nStart), static_cast< xub_StrLen >(nLength) ); 278 } 279 280 if( bRepaint ) 281 finishGrammarCheck( *mpTxtNode ); 282 } 283 284 285 void lcl_commitGrammarMarkUp( 286 const ModelToViewHelper::ConversionMap* pConversionMap, 287 SwGrammarMarkUp* pWList, 288 ::sal_Int32 nType, 289 const ::rtl::OUString & rIdentifier, 290 ::sal_Int32 nStart, 291 ::sal_Int32 nLength, 292 const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer) 293 { 294 ASSERT( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE, "Wrong mark-up type" ) 295 const ModelToViewHelper::ModelPosition aStartPos = 296 ModelToViewHelper::ConvertToModelPosition( pConversionMap, nStart ); 297 const ModelToViewHelper::ModelPosition aEndPos = 298 ModelToViewHelper::ConvertToModelPosition( pConversionMap, nStart + nLength - 1); 299 300 const bool bStartInField = aStartPos.mbIsField; 301 const bool bEndInField = aEndPos.mbIsField; 302 bool bCommit = false; 303 304 if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos ) 305 { 306 nStart = aStartPos.mnSubPos; 307 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aStartPos.mnPos); 308 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); 309 310 SwGrammarMarkUp* pSubList = (SwGrammarMarkUp*)pWList->SubList( nInsertPos ); 311 if ( !pSubList ) 312 { 313 pSubList = new SwGrammarMarkUp(); 314 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); 315 } 316 317 pWList = pSubList; 318 bCommit = true; 319 } 320 else if ( !bStartInField && !bEndInField ) 321 { 322 nStart = aStartPos.mnPos; 323 bCommit = true; 324 nLength = aEndPos.mnPos + 1 - aStartPos.mnPos; 325 } 326 else 327 { 328 bCommit = true; 329 nStart = aStartPos.mnPos; 330 sal_Int32 nEnd = aEndPos.mnPos; 331 if( bStartInField && nType != text::TextMarkupType::SENTENCE ) 332 { 333 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aStartPos.mnPos); 334 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); 335 SwGrammarMarkUp* pSubList = (SwGrammarMarkUp*)pWList->SubList( nInsertPos ); 336 if ( !pSubList ) 337 { 338 pSubList = new SwGrammarMarkUp(); 339 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); 340 } 341 const sal_uInt32 nTmpStart = ModelToViewHelper::ConvertToViewPosition( pConversionMap, aStartPos.mnPos ); 342 const sal_uInt32 nTmpLen = ModelToViewHelper::ConvertToViewPosition( pConversionMap, aStartPos.mnPos + 1 ) 343 - nTmpStart - aStartPos.mnSubPos; 344 if( nTmpLen > 0 ) 345 pSubList->Insert( rIdentifier, xMarkupInfoContainer, 346 static_cast< xub_StrLen >(aStartPos.mnSubPos), static_cast< xub_StrLen >(nTmpLen) ); 347 ++nStart; 348 } 349 if( bEndInField && nType != text::TextMarkupType::SENTENCE ) 350 { 351 const xub_StrLen nFieldPosModel = static_cast< xub_StrLen >(aEndPos.mnPos); 352 const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel ); 353 SwGrammarMarkUp* pSubList = (SwGrammarMarkUp*)pWList->SubList( nInsertPos ); 354 if ( !pSubList ) 355 { 356 pSubList = new SwGrammarMarkUp(); 357 pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList ); 358 } 359 const sal_uInt32 nTmpLen = aEndPos.mnSubPos + 1; 360 pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, static_cast< xub_StrLen >(nTmpLen) ); 361 } 362 else 363 ++nEnd; 364 if( nEnd > nStart ) 365 nLength = nEnd - nStart; 366 else 367 bCommit = false; 368 } 369 370 if ( bCommit ) 371 { 372 if( nType == text::TextMarkupType::SENTENCE ) 373 ((SwGrammarMarkUp*)pWList)->setSentence( static_cast< xub_StrLen >(nStart+nLength) ); 374 else 375 pWList->Insert( rIdentifier, xMarkupInfoContainer, 376 static_cast< xub_StrLen >(nStart), static_cast< xub_StrLen >(nLength) ); 377 } 378 } 379 380 381 void SAL_CALL SwXTextMarkup::commitMultiTextMarkup( 382 const uno::Sequence< text::TextMarkupDescriptor > &rMarkups ) 383 { 384 vos::OGuard aGuard(Application::GetSolarMutex()); 385 386 // paragraph already dead or modified? 387 if ( !mpTxtNode ) 388 return; 389 390 // check for equal length of all sequnces 391 sal_Int32 nLen = rMarkups.getLength(); 392 393 // for grammar checking there should be exactly one sentence markup 394 // and 0..n grammar markups. 395 // Different markups are not expected but may be applied anyway since 396 // that should be no problem... 397 // but it has to be implemented, at the moment only this function is for 398 // grammar markups and sentence markup only! 399 sal_Int32 nSentenceMarkUpIndex = -1; 400 const text::TextMarkupDescriptor *pMarkups = rMarkups.getConstArray(); 401 sal_Int32 i; 402 for( i = 0; i < nLen; ++i ) 403 { 404 if (pMarkups[i].nType == text::TextMarkupType::SENTENCE) 405 { 406 if (nSentenceMarkUpIndex == -1) 407 nSentenceMarkUpIndex = i; 408 else // there is already one sentence markup 409 throw lang::IllegalArgumentException(); 410 } 411 else if( pMarkups[i].nType != text::TextMarkupType::PROOFREADING ) 412 return; 413 } 414 415 if( nSentenceMarkUpIndex == -1 ) 416 return; 417 418 // get appropriate list to use... 419 SwGrammarMarkUp* pWList = 0; 420 bool bRepaint = false; 421 IGrammarContact *pGrammarContact = getGrammarContact( *mpTxtNode ); 422 if( pGrammarContact ) 423 { 424 pWList = pGrammarContact->getGrammarCheck( *mpTxtNode, true ); 425 ASSERT( pWList, "GrammarContact _has_ to deliver a wrong list" ) 426 } 427 else 428 { 429 pWList = mpTxtNode->GetGrammarCheck(); 430 if ( !pWList ) 431 { 432 mpTxtNode->SetGrammarCheck( new SwGrammarMarkUp() ); 433 pWList = mpTxtNode->GetGrammarCheck(); 434 pWList->SetInvalid( 0, STRING_LEN ); 435 } 436 } 437 bRepaint = pWList == mpTxtNode->GetGrammarCheck(); 438 439 bool bAcceptGrammarError = false; 440 if( pWList->GetBeginInv() < STRING_LEN ) 441 { 442 const ModelToViewHelper::ModelPosition aSentenceEnd = 443 ModelToViewHelper::ConvertToModelPosition( mpConversionMap, 444 pMarkups[nSentenceMarkUpIndex].nOffset + pMarkups[nSentenceMarkUpIndex].nLength ); 445 bAcceptGrammarError = (xub_StrLen)aSentenceEnd.mnPos > pWList->GetBeginInv(); 446 pWList->ClearGrammarList( (xub_StrLen)aSentenceEnd.mnPos ); 447 } 448 449 if( bAcceptGrammarError ) 450 { 451 for( i = 0; i < nLen; ++i ) 452 { 453 const text::TextMarkupDescriptor &rDesc = pMarkups[i]; 454 lcl_commitGrammarMarkUp( mpConversionMap, pWList, rDesc.nType, 455 rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer ); 456 } 457 } 458 else 459 { 460 bRepaint = false; 461 i = nSentenceMarkUpIndex; 462 const text::TextMarkupDescriptor &rDesc = pMarkups[i]; 463 lcl_commitGrammarMarkUp( mpConversionMap, pWList, rDesc.nType, 464 rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer ); 465 } 466 467 if( bRepaint ) 468 finishGrammarCheck( *mpTxtNode ); 469 470 return; 471 } 472 473 474 void SwXTextMarkup::Modify( const SfxPoolItem* /*pOld*/, const SfxPoolItem* /*pNew*/ ) 475 { 476 // FME 2007-07-16 #i79641# In my opinion this is perfectly legal, 477 // therefore I remove the assertion in SwModify::_Remove() 478 if ( GetRegisteredIn() ) 479 GetRegisteredInNonConst()->Remove( this ); 480 // <-- 481 482 vos::OGuard aGuard(Application::GetSolarMutex()); 483 mpTxtNode = 0; 484 } 485 486 /* 487 * SwXStringKeyMap 488 */ 489 SwXStringKeyMap::SwXStringKeyMap() 490 { 491 } 492 493 uno::Any SAL_CALL SwXStringKeyMap::getValue(const ::rtl::OUString & aKey) 494 { 495 std::map< rtl::OUString, uno::Any >::const_iterator aIter = maMap.find( aKey ); 496 if ( aIter == maMap.end() ) 497 throw container::NoSuchElementException(); 498 499 return (*aIter).second; 500 } 501 502 ::sal_Bool SAL_CALL SwXStringKeyMap::hasValue(const ::rtl::OUString & aKey) 503 { 504 return maMap.find( aKey ) != maMap.end(); 505 } 506 507 void SAL_CALL SwXStringKeyMap::insertValue(const ::rtl::OUString & aKey, const uno::Any & aValue) 508 { 509 std::map< rtl::OUString, uno::Any >::const_iterator aIter = maMap.find( aKey ); 510 if ( aIter != maMap.end() ) 511 throw container::ElementExistException(); 512 513 maMap[ aKey ] = aValue; 514 } 515 516 ::sal_Int32 SAL_CALL SwXStringKeyMap::getCount() 517 { 518 return maMap.size(); 519 } 520 521 ::rtl::OUString SAL_CALL SwXStringKeyMap::getKeyByIndex(::sal_Int32 nIndex) 522 { 523 if ( (sal_uInt32)nIndex >= maMap.size() ) 524 throw lang::IndexOutOfBoundsException(); 525 526 return ::rtl::OUString(); 527 } 528 529 uno::Any SAL_CALL SwXStringKeyMap::getValueByIndex(::sal_Int32 nIndex) 530 { 531 if ( (sal_uInt32)nIndex >= maMap.size() ) 532 throw lang::IndexOutOfBoundsException(); 533 534 return uno::Any(); 535 } 536