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