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_editeng.hxx" 26 #include<rtl/ustring.hxx> 27 #include <tools/shl.hxx> 28 #include <vcl/wrkwin.hxx> 29 #include <vcl/svapp.hxx> 30 #include <vcl/msgbox.hxx> 31 #include <tools/debug.hxx> 32 #include <svtools/langtab.hxx> 33 34 #ifndef __RSC 35 #include <tools/errinf.hxx> 36 #endif 37 #include <editeng/unolingu.hxx> 38 #include <linguistic/lngprops.hxx> 39 #include <com/sun/star/frame/XStorable.hpp> 40 41 #include <map> 42 43 #include <editeng/svxenum.hxx> 44 #include <editeng/splwrap.hxx> // Der Wrapper 45 #include <editeng/edtdlg.hxx> 46 #include <editeng/eerdll.hxx> 47 #include <editeng/editrids.hrc> 48 #include <editeng/editids.hrc> 49 #include <editeng/editerr.hxx> 50 51 #define WAIT_ON() if(pWin != NULL) { pWin->EnterWait(); } 52 53 #define WAIT_OFF() if(pWin != NULL) { pWin->LeaveWait(); } 54 55 using namespace ::com::sun::star; 56 using namespace ::com::sun::star::uno; 57 using namespace ::com::sun::star::beans; 58 using namespace ::com::sun::star::linguistic2; 59 60 61 // misc functions --------------------------------------------- 62 63 void SvxPrepareAutoCorrect( String &rOldText, String &rNewText ) 64 { 65 // This function should be used to strip (or add) trailing '.' from 66 // the strings before passing them on to the autocorrect function in 67 // order that the autocorrect function will hopefully 68 // works properly with normal words and abbreviations (with trailing '.') 69 // independ of if they are at the end of the sentence or not. 70 // 71 // rOldText: text to be replaced 72 // rNewText: replacement text 73 74 xub_StrLen nOldLen = rOldText.Len(), 75 nNewLen = rNewText.Len(); 76 if (nOldLen && nNewLen) 77 { 78 sal_Bool bOldHasDot = sal_Unicode( '.' ) == rOldText.GetChar( nOldLen - 1 ), 79 bNewHasDot = sal_Unicode( '.' ) == rNewText.GetChar( nNewLen - 1 ); 80 if (bOldHasDot && !bNewHasDot 81 /*this is: !(bOldHasDot && bNewHasDot) && bOldHasDot*/) 82 rOldText.Erase( nOldLen - 1 ); 83 } 84 } 85 86 // ----------------------------------------------------------------------- 87 88 #define SVX_LANG_NEED_CHECK 0 89 #define SVX_LANG_OK 1 90 #define SVX_LANG_MISSING 2 91 #define SVX_LANG_MISSING_DO_WARN 3 92 93 #define SVX_FLAGS_NEW 94 95 96 struct lt_LanguageType 97 { 98 bool operator()( LanguageType n1, LanguageType n2 ) const 99 { 100 return n1 < n2; 101 } 102 }; 103 104 typedef std::map< LanguageType, sal_uInt16, lt_LanguageType > LangCheckState_map_t; 105 106 static LangCheckState_map_t & GetLangCheckState() 107 { 108 static LangCheckState_map_t aLangCheckState; 109 return aLangCheckState; 110 } 111 112 void SvxSpellWrapper::ShowLanguageErrors() 113 { 114 // display message boxes for languages not available for 115 // spellchecking or hyphenation 116 LangCheckState_map_t &rLCS = GetLangCheckState(); 117 LangCheckState_map_t::iterator aIt( rLCS.begin() ); 118 while (aIt != rLCS.end()) 119 { 120 LanguageType nLang = aIt->first; 121 sal_uInt16 nVal = aIt->second; 122 sal_uInt16 nTmpSpell = nVal & 0x00FF; 123 sal_uInt16 nTmpHyph = (nVal >> 8) & 0x00FF; 124 125 if (SVX_LANG_MISSING_DO_WARN == nTmpSpell) 126 { 127 String aErr( SvtLanguageTable::GetLanguageString( nLang ) ); 128 ErrorHandler::HandleError( 129 *new StringErrorInfo( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) ); 130 nTmpSpell = SVX_LANG_MISSING; 131 } 132 if (SVX_LANG_MISSING_DO_WARN == nTmpHyph) 133 { 134 String aErr( SvtLanguageTable::GetLanguageString( nLang ) ); 135 ErrorHandler::HandleError( 136 *new StringErrorInfo( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) ); 137 nTmpHyph = SVX_LANG_MISSING; 138 } 139 140 rLCS[ nLang ] = (nTmpHyph << 8) | nTmpSpell; 141 ++aIt; 142 } 143 144 } 145 146 SvxSpellWrapper::~SvxSpellWrapper() 147 { 148 } 149 150 /*-------------------------------------------------------------------- 151 * Beschreibung: Ctor, die Pruefreihenfolge wird festgelegt 152 * 153 * !bStart && !bOtherCntnt: BODY_END, BODY_START, OTHER 154 * !bStart && bOtherCntnt: OTHER, BODY 155 * bStart && !bOtherCntnt: BODY_END, OTHER 156 * bStart && bOtherCntnt: OTHER 157 * 158 --------------------------------------------------------------------*/ 159 160 SvxSpellWrapper::SvxSpellWrapper( Window* pWn, 161 Reference< XSpellChecker1 > &xSpellChecker, 162 const sal_Bool bStart, const sal_Bool bIsAllRight, 163 const sal_Bool bOther, const sal_Bool bRevAllow ) : 164 165 pWin ( pWn ), 166 xSpell ( xSpellChecker ), 167 bOtherCntnt ( bOther ), 168 bDialog ( sal_False ), 169 bHyphen ( sal_False ), 170 bAuto ( sal_False ), 171 bStartChk ( bOther ), 172 bRevAllowed ( bRevAllow ), 173 bAllRight ( bIsAllRight ) 174 { 175 Reference< beans::XPropertySet > xProp( SvxGetLinguPropertySet() ); 176 sal_Bool bWrapReverse = xProp.is() ? 177 *(sal_Bool*)xProp->getPropertyValue( 178 ::rtl::OUString::createFromAscii(UPN_IS_WRAP_REVERSE) ).getValue() 179 : sal_False; 180 bReverse = bRevAllow && bWrapReverse; 181 bStartDone = bOther || ( !bReverse && bStart ); 182 bEndDone = bReverse && bStart && !bOther; 183 } 184 185 // ----------------------------------------------------------------------- 186 187 SvxSpellWrapper::SvxSpellWrapper( Window* pWn, 188 Reference< XHyphenator > &xHyphenator, 189 const sal_Bool bStart, const sal_Bool bOther ) : 190 pWin ( pWn ), 191 xHyph ( xHyphenator ), 192 bOtherCntnt ( bOther ), 193 bDialog ( sal_False ), 194 bHyphen ( sal_False ), 195 bAuto ( sal_False ), 196 bReverse ( sal_False ), 197 bStartDone ( bOther || ( !bReverse && bStart ) ), 198 bEndDone ( bReverse && bStart && !bOther ), 199 bStartChk ( bOther ), 200 bRevAllowed ( sal_False ), 201 bAllRight ( sal_True ) 202 { 203 } 204 205 // ----------------------------------------------------------------------- 206 207 sal_Int16 SvxSpellWrapper::CheckSpellLang( 208 Reference< XSpellChecker1 > xSpell, sal_Int16 nLang) 209 { 210 LangCheckState_map_t &rLCS = GetLangCheckState(); 211 212 LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) ); 213 sal_uInt16 nVal = aIt == rLCS.end() ? SVX_LANG_NEED_CHECK : aIt->second; 214 215 if (aIt == rLCS.end()) 216 rLCS[ nLang ] = nVal; 217 218 if (SVX_LANG_NEED_CHECK == (nVal & 0x00FF)) 219 { 220 sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN; 221 if (xSpell.is() && xSpell->hasLanguage( nLang )) 222 nTmpVal = SVX_LANG_OK; 223 nVal &= 0xFF00; 224 nVal |= nTmpVal; 225 226 rLCS[ nLang ] = nVal; 227 } 228 229 return (sal_Int16) nVal; 230 } 231 232 sal_Int16 SvxSpellWrapper::CheckHyphLang( 233 Reference< XHyphenator > xHyph, sal_Int16 nLang) 234 { 235 LangCheckState_map_t &rLCS = GetLangCheckState(); 236 237 LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) ); 238 sal_uInt16 nVal = aIt == rLCS.end() ? 0 : aIt->second; 239 240 if (aIt == rLCS.end()) 241 rLCS[ nLang ] = nVal; 242 243 if (SVX_LANG_NEED_CHECK == ((nVal >> 8) & 0x00FF)) 244 { 245 sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN; 246 if (xHyph.is() && xHyph->hasLocale( SvxCreateLocale( nLang ) )) 247 nTmpVal = SVX_LANG_OK; 248 nVal &= 0x00FF; 249 nVal |= nTmpVal << 8; 250 251 rLCS[ nLang ] = nVal; 252 } 253 254 return (sal_Int16) nVal; 255 } 256 257 // ----------------------------------------------------------------------- 258 259 260 void SvxSpellWrapper::SpellStart( SvxSpellArea /*eSpell*/ ) 261 { // Hier muessen die notwendigen Vorbereitungen fuer SpellContinue 262 } // im uebergebenen Bereich getroffen werden. 263 264 // ----------------------------------------------------------------------- 265 266 267 sal_Bool SvxSpellWrapper::HasOtherCnt() 268 { 269 return sal_False; // Gibt es ueberhaupt einen Sonderbereich? 270 } 271 272 // ----------------------------------------------------------------------- 273 274 275 sal_Bool SvxSpellWrapper::SpellMore() 276 { 277 return sal_False; // Sollen weitere Dokumente geprueft werden? 278 } 279 280 // ----------------------------------------------------------------------- 281 282 283 void SvxSpellWrapper::SpellEnd() 284 { // Bereich ist abgeschlossen, ggf. Aufraeumen 285 286 // display error for last language not found 287 ShowLanguageErrors(); 288 } 289 290 // ----------------------------------------------------------------------- 291 292 293 sal_Bool SvxSpellWrapper::SpellContinue() 294 { 295 return sal_False; 296 } 297 298 // ----------------------------------------------------------------------- 299 300 void SvxSpellWrapper::AutoCorrect( const String&, const String& ) 301 { 302 } 303 304 // ----------------------------------------------------------------------- 305 306 307 void SvxSpellWrapper::ScrollArea() 308 { // Scrollarea einstellen 309 } 310 311 // ----------------------------------------------------------------------- 312 313 314 void SvxSpellWrapper::ChangeWord( const String&, const sal_uInt16 ) 315 { // Wort ersetzen 316 } 317 318 // ----------------------------------------------------------------------- 319 320 321 String SvxSpellWrapper::GetThesWord() 322 { 323 // Welches Wort soll nachgeschlagen werden? 324 return String(); 325 } 326 327 // ----------------------------------------------------------------------- 328 329 330 void SvxSpellWrapper::ChangeThesWord( const String& ) 331 { 332 // Wort wg. Thesaurus ersetzen 333 } 334 335 // ----------------------------------------------------------------------- 336 337 void SvxSpellWrapper::StartThesaurus( const String &rWord, sal_uInt16 nLanguage ) 338 { 339 Reference< XThesaurus > xThes( SvxGetThesaurus() ); 340 if (!xThes.is()) 341 { 342 InfoBox( pWin, EE_RESSTR( RID_SVXSTR_HMERR_THESAURUS ) ).Execute(); 343 return; 344 } 345 346 WAIT_ON(); // while looking up for initial word 347 EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create(); 348 AbstractThesaurusDialog* pDlg = pFact->CreateThesaurusDialog( pWin, xThes, rWord, nLanguage ); 349 WAIT_OFF(); 350 if ( pDlg->Execute()== RET_OK ) 351 { 352 ChangeThesWord( pDlg->GetWord() ); 353 } 354 delete pDlg; 355 } 356 357 // ----------------------------------------------------------------------- 358 359 void SvxSpellWrapper::ReplaceAll( const String &, sal_Int16 ) 360 { // Wort aus der Replace-Liste ersetzen 361 } 362 363 // ----------------------------------------------------------------------- 364 365 366 void SvxSpellWrapper::SetLanguage( const sal_uInt16 ) 367 { // Sprache aendern 368 } 369 370 // ----------------------------------------------------------------------- 371 372 373 void SvxSpellWrapper::InsertHyphen( const sal_uInt16 ) 374 { // Hyphen einfuegen bzw. loeschen 375 } 376 377 // ----------------------------------------------------------------------- 378 // Pruefung der Dokumentbereiche in der durch die Flags angegebenen Reihenfolge 379 380 381 void SvxSpellWrapper::SpellDocument( ) 382 { 383 if ( bOtherCntnt ) 384 { 385 bReverse = sal_False; 386 SpellStart( SVX_SPELL_OTHER ); 387 } 388 else 389 { 390 bStartChk = bReverse; 391 SpellStart( bReverse ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END ); 392 } 393 394 if ( FindSpellError() ) 395 { 396 Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY ); 397 Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY ); 398 399 Window *pOld = pWin; 400 bDialog = sal_True; 401 if (xHyphWord.is()) 402 { 403 EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create(); 404 AbstractHyphenWordDialog* pDlg = pFact->CreateHyphenWordDialog( pWin, 405 xHyphWord->getWord(), 406 SvxLocaleToLanguage( xHyphWord->getLocale() ), 407 xHyph, this ); 408 pWin = pDlg->GetWindow(); 409 pDlg->Execute(); 410 delete pDlg; 411 } 412 bDialog = sal_False; 413 pWin = pOld; 414 }; 415 } 416 417 // ----------------------------------------------------------------------- 418 // Naechsten Bereich auswaehlen 419 420 421 sal_Bool SvxSpellWrapper::SpellNext( ) 422 { 423 Reference< beans::XPropertySet > xProp( SvxGetLinguPropertySet() ); 424 sal_Bool bWrapReverse = xProp.is() ? 425 *(sal_Bool*)xProp->getPropertyValue( 426 ::rtl::OUString::createFromAscii(UPN_IS_WRAP_REVERSE) ).getValue() 427 : sal_False; 428 sal_Bool bActRev = bRevAllowed && bWrapReverse; 429 430 // bActRev ist die Richtung nach dem Spellen, bReverse die am Anfang. 431 if( bActRev == bReverse ) 432 { // Keine Richtungsaenderung, also ist 433 if( bStartChk ) // der gewuenschte Bereich ( bStartChk ) 434 bStartDone = sal_True; // vollstaendig abgearbeitet. 435 else 436 bEndDone = sal_True; 437 } 438 else if( bReverse == bStartChk ) // Bei einer Richtungsaenderung kann 439 { // u.U. auch ein Bereich abgearbeitet sein. 440 if( bStartChk ) // Sollte der vordere Teil rueckwaerts gespellt 441 bEndDone = sal_True; // werden und wir kehren unterwegs um, so ist 442 else // der hintere Teil abgearbeitet (und umgekehrt). 443 bStartDone = sal_True; 444 } 445 446 bReverse = bActRev; 447 if( bOtherCntnt && bStartDone && bEndDone ) // Dokument komplett geprueft? 448 { 449 if ( SpellMore() ) // ein weiteres Dokument pruefen? 450 { 451 bOtherCntnt = sal_False; 452 bStartDone = !bReverse; 453 bEndDone = bReverse; 454 SpellStart( SVX_SPELL_BODY ); 455 return sal_True; 456 } 457 return sal_False; 458 } 459 460 sal_Bool bGoOn = sal_False; 461 462 if ( bOtherCntnt ) 463 { 464 bStartChk = sal_False; 465 SpellStart( SVX_SPELL_BODY ); 466 bGoOn = sal_True; 467 } 468 else if ( bStartDone && bEndDone ) 469 { 470 sal_Bool bIsSpellSpecial = xProp.is() ? 471 *(sal_Bool*)xProp->getPropertyValue( 472 ::rtl::OUString::createFromAscii(UPN_IS_SPELL_SPECIAL) ).getValue() 473 : sal_False; 474 // Bodybereich erledigt, Frage nach Sonderbereich 475 if( !IsHyphen() && bIsSpellSpecial && HasOtherCnt() ) 476 { 477 SpellStart( SVX_SPELL_OTHER ); 478 bOtherCntnt = bGoOn = sal_True; 479 } 480 else if ( SpellMore() ) // ein weiteres Dokument pruefen? 481 { 482 bOtherCntnt = sal_False; 483 bStartDone = !bReverse; 484 bEndDone = bReverse; 485 SpellStart( SVX_SPELL_BODY ); 486 return sal_True; 487 } 488 } 489 else 490 { 491 // Ein BODY_Bereich erledigt, Frage nach dem anderen BODY_Bereich 492 WAIT_OFF(); 493 494 // Sobald im Dialog das DontWrapAround gesetzt werden kann, kann der 495 // folgende #ifdef-Zweig aktiviert werden ... 496 #ifdef USED 497 sal_Bool bDontWrapAround = IsHyphen() ? 498 pSpell->GetOptions() & DONT_WRAPAROUND : 499 pSpell->GetHyphOptions() & HYPH_DONT_WRAPAROUND; 500 if( bDontWrapAround ) 501 #else 502 sal_uInt16 nResId = bReverse ? RID_SVXQB_BW_CONTINUE : RID_SVXQB_CONTINUE; 503 QueryBox aBox( pWin, EditResId( nResId ) ); 504 if ( aBox.Execute() != RET_YES ) 505 #endif 506 507 { 508 // Verzicht auf den anderen Bereich, ggf. Frage nach Sonderbereich 509 WAIT_ON(); 510 bStartDone = bEndDone = sal_True; 511 return SpellNext(); 512 } 513 else 514 { 515 bStartChk = !bStartDone; 516 SpellStart( bStartChk ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END ); 517 bGoOn = sal_True; 518 } 519 WAIT_ON(); 520 } 521 return bGoOn; 522 } 523 524 // ----------------------------------------------------------------------- 525 526 Reference< XDictionary > SvxSpellWrapper::GetAllRightDic() const 527 { 528 Reference< XDictionary > xDic; 529 530 Reference< XDictionaryList > xDicList( SvxGetDictionaryList() ); 531 if (xDicList.is()) 532 { 533 Sequence< Reference< XDictionary > > aDics( xDicList->getDictionaries() ); 534 const Reference< XDictionary > *pDic = aDics.getConstArray(); 535 sal_Int32 nCount = aDics.getLength(); 536 537 sal_Int32 i = 0; 538 while (!xDic.is() && i < nCount) 539 { 540 Reference< XDictionary > xTmp( pDic[i], UNO_QUERY ); 541 if (xTmp.is()) 542 { 543 if ( xTmp->isActive() && 544 xTmp->getDictionaryType() != DictionaryType_NEGATIVE && 545 SvxLocaleToLanguage( xTmp->getLocale() ) == LANGUAGE_NONE ) 546 { 547 Reference< frame::XStorable > xStor( xTmp, UNO_QUERY ); 548 if (xStor.is() && xStor->hasLocation() && !xStor->isReadonly()) 549 { 550 xDic = xTmp; 551 } 552 } 553 } 554 ++i; 555 } 556 557 if (!xDic.is()) 558 { 559 xDic = SvxGetOrCreatePosDic( xDicList ); 560 if (xDic.is()) 561 xDic->setActive( sal_True ); 562 } 563 } 564 565 return xDic; 566 } 567 568 // ----------------------------------------------------------------------- 569 570 sal_Bool SvxSpellWrapper::FindSpellError() 571 { 572 ShowLanguageErrors(); 573 574 Reference< XInterface > xRef; 575 576 WAIT_ON(); 577 sal_Bool bSpell = sal_True; 578 579 Reference< XDictionary > xAllRightDic; 580 if (IsAllRight()) 581 xAllRightDic = GetAllRightDic(); 582 583 while ( bSpell ) 584 { 585 SpellContinue(); 586 587 Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY ); 588 Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY ); 589 590 if (xAlt.is()) 591 { 592 if (IsAllRight() && xAllRightDic.is()) 593 { 594 xAllRightDic->add( xAlt->getWord(), sal_False, ::rtl::OUString() ); 595 } 596 else 597 { 598 // look up in ChangeAllList for misspelled word 599 Reference< XDictionary > xChangeAllList( 600 SvxGetChangeAllList(), UNO_QUERY ); 601 Reference< XDictionaryEntry > xEntry; 602 if (xChangeAllList.is()) 603 xEntry = xChangeAllList->getEntry( xAlt->getWord() ); 604 605 if (xEntry.is()) 606 { 607 // replace word without asking 608 ReplaceAll( xEntry->getReplacementText(), 609 SvxLocaleToLanguage( xAlt->getLocale() ) ); 610 } 611 else 612 bSpell = sal_False; 613 } 614 } 615 else if (xHyphWord.is()) 616 bSpell = sal_False; 617 else 618 { 619 SpellEnd(); 620 bSpell = SpellNext(); 621 } 622 } 623 WAIT_OFF(); 624 return GetLast().is(); 625 } 626 627 628 629