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 mpTextObj( NULL), 174 bAllRight ( bIsAllRight ) 175 { 176 Reference< beans::XPropertySet > xProp( SvxGetLinguPropertySet() ); 177 sal_Bool bWrapReverse = xProp.is() ? 178 *(sal_Bool*)xProp->getPropertyValue( 179 ::rtl::OUString::createFromAscii(UPN_IS_WRAP_REVERSE) ).getValue() 180 : sal_False; 181 bReverse = bRevAllow && bWrapReverse; 182 bStartDone = bOther || ( !bReverse && bStart ); 183 bEndDone = bReverse && bStart && !bOther; 184 } 185 186 // ----------------------------------------------------------------------- 187 188 SvxSpellWrapper::SvxSpellWrapper( Window* pWn, 189 Reference< XHyphenator > &xHyphenator, 190 const sal_Bool bStart, const sal_Bool bOther ) : 191 pWin ( pWn ), 192 xHyph ( xHyphenator ), 193 bOtherCntnt ( bOther ), 194 bDialog ( sal_False ), 195 bHyphen ( sal_False ), 196 bAuto ( sal_False ), 197 bReverse ( sal_False ), 198 bStartDone ( bOther || ( !bReverse && bStart ) ), 199 bEndDone ( bReverse && bStart && !bOther ), 200 bStartChk ( bOther ), 201 bRevAllowed ( sal_False ), 202 mpTextObj( NULL), 203 bAllRight ( sal_True ) 204 { 205 } 206 207 // ----------------------------------------------------------------------- 208 209 sal_Int16 SvxSpellWrapper::CheckSpellLang( 210 Reference< XSpellChecker1 > xSpell, sal_Int16 nLang) 211 { 212 LangCheckState_map_t &rLCS = GetLangCheckState(); 213 214 LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) ); 215 sal_uInt16 nVal = aIt == rLCS.end() ? SVX_LANG_NEED_CHECK : aIt->second; 216 217 if (aIt == rLCS.end()) 218 rLCS[ nLang ] = nVal; 219 220 if (SVX_LANG_NEED_CHECK == (nVal & 0x00FF)) 221 { 222 sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN; 223 if (xSpell.is() && xSpell->hasLanguage( nLang )) 224 nTmpVal = SVX_LANG_OK; 225 nVal &= 0xFF00; 226 nVal |= nTmpVal; 227 228 rLCS[ nLang ] = nVal; 229 } 230 231 return (sal_Int16) nVal; 232 } 233 234 sal_Int16 SvxSpellWrapper::CheckHyphLang( 235 Reference< XHyphenator > xHyph, sal_Int16 nLang) 236 { 237 LangCheckState_map_t &rLCS = GetLangCheckState(); 238 239 LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) ); 240 sal_uInt16 nVal = aIt == rLCS.end() ? 0 : aIt->second; 241 242 if (aIt == rLCS.end()) 243 rLCS[ nLang ] = nVal; 244 245 if (SVX_LANG_NEED_CHECK == ((nVal >> 8) & 0x00FF)) 246 { 247 sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN; 248 if (xHyph.is() && xHyph->hasLocale( SvxCreateLocale( nLang ) )) 249 nTmpVal = SVX_LANG_OK; 250 nVal &= 0x00FF; 251 nVal |= nTmpVal << 8; 252 253 rLCS[ nLang ] = nVal; 254 } 255 256 return (sal_Int16) nVal; 257 } 258 259 // ----------------------------------------------------------------------- 260 261 262 void SvxSpellWrapper::SpellStart( SvxSpellArea /*eSpell*/ ) 263 { // Hier muessen die notwendigen Vorbereitungen fuer SpellContinue 264 } // im uebergebenen Bereich getroffen werden. 265 266 // ----------------------------------------------------------------------- 267 268 269 sal_Bool SvxSpellWrapper::HasOtherCnt() 270 { 271 return sal_False; // Gibt es ueberhaupt einen Sonderbereich? 272 } 273 274 // ----------------------------------------------------------------------- 275 276 277 sal_Bool SvxSpellWrapper::SpellMore() 278 { 279 return sal_False; // Sollen weitere Dokumente geprueft werden? 280 } 281 282 // ----------------------------------------------------------------------- 283 284 285 void SvxSpellWrapper::SpellEnd() 286 { // Bereich ist abgeschlossen, ggf. Aufraeumen 287 288 // display error for last language not found 289 ShowLanguageErrors(); 290 } 291 292 // ----------------------------------------------------------------------- 293 294 295 sal_Bool SvxSpellWrapper::SpellContinue() 296 { 297 return sal_False; 298 } 299 300 // ----------------------------------------------------------------------- 301 302 void SvxSpellWrapper::AutoCorrect( const String&, const String& ) 303 { 304 } 305 306 // ----------------------------------------------------------------------- 307 308 309 void SvxSpellWrapper::ScrollArea() 310 { // Scrollarea einstellen 311 } 312 313 // ----------------------------------------------------------------------- 314 315 316 void SvxSpellWrapper::ChangeWord( const String&, const sal_uInt16 ) 317 { // Wort ersetzen 318 } 319 320 // ----------------------------------------------------------------------- 321 322 323 String SvxSpellWrapper::GetThesWord() 324 { 325 // Welches Wort soll nachgeschlagen werden? 326 return String(); 327 } 328 329 // ----------------------------------------------------------------------- 330 331 332 void SvxSpellWrapper::ChangeThesWord( const String& ) 333 { 334 // Wort wg. Thesaurus ersetzen 335 } 336 337 // ----------------------------------------------------------------------- 338 339 void SvxSpellWrapper::StartThesaurus( const String &rWord, sal_uInt16 nLanguage ) 340 { 341 Reference< XThesaurus > xThes( SvxGetThesaurus() ); 342 if (!xThes.is()) 343 { 344 InfoBox( pWin, EE_RESSTR( RID_SVXSTR_HMERR_THESAURUS ) ).Execute(); 345 return; 346 } 347 348 WAIT_ON(); // while looking up for initial word 349 EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create(); 350 AbstractThesaurusDialog* pDlg = pFact->CreateThesaurusDialog( pWin, xThes, rWord, nLanguage ); 351 WAIT_OFF(); 352 if ( pDlg->Execute()== RET_OK ) 353 { 354 ChangeThesWord( pDlg->GetWord() ); 355 } 356 delete pDlg; 357 } 358 359 // ----------------------------------------------------------------------- 360 361 void SvxSpellWrapper::ReplaceAll( const String &, sal_Int16 ) 362 { // Wort aus der Replace-Liste ersetzen 363 } 364 365 // ----------------------------------------------------------------------- 366 367 368 void SvxSpellWrapper::SetLanguage( const sal_uInt16 ) 369 { // Sprache aendern 370 } 371 372 // ----------------------------------------------------------------------- 373 374 375 void SvxSpellWrapper::InsertHyphen( const sal_uInt16 ) 376 { // Hyphen einfuegen bzw. loeschen 377 } 378 379 // ----------------------------------------------------------------------- 380 // Pruefung der Dokumentbereiche in der durch die Flags angegebenen Reihenfolge 381 382 383 void SvxSpellWrapper::SpellDocument( ) 384 { 385 if ( bOtherCntnt ) 386 { 387 bReverse = sal_False; 388 SpellStart( SVX_SPELL_OTHER ); 389 } 390 else 391 { 392 bStartChk = bReverse; 393 SpellStart( bReverse ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END ); 394 } 395 396 if ( FindSpellError() ) 397 { 398 Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY ); 399 Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY ); 400 401 Window *pOld = pWin; 402 bDialog = sal_True; 403 if (xHyphWord.is()) 404 { 405 EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create(); 406 AbstractHyphenWordDialog* pDlg = pFact->CreateHyphenWordDialog( pWin, 407 xHyphWord->getWord(), 408 SvxLocaleToLanguage( xHyphWord->getLocale() ), 409 xHyph, this ); 410 pWin = pDlg->GetWindow(); 411 pDlg->Execute(); 412 delete pDlg; 413 } 414 bDialog = sal_False; 415 pWin = pOld; 416 }; 417 } 418 419 // ----------------------------------------------------------------------- 420 // Naechsten Bereich auswaehlen 421 422 423 sal_Bool SvxSpellWrapper::SpellNext( ) 424 { 425 Reference< beans::XPropertySet > xProp( SvxGetLinguPropertySet() ); 426 sal_Bool bWrapReverse = xProp.is() ? 427 *(sal_Bool*)xProp->getPropertyValue( 428 ::rtl::OUString::createFromAscii(UPN_IS_WRAP_REVERSE) ).getValue() 429 : sal_False; 430 sal_Bool bActRev = bRevAllowed && bWrapReverse; 431 432 // bActRev ist die Richtung nach dem Spellen, bReverse die am Anfang. 433 if( bActRev == bReverse ) 434 { // Keine Richtungsaenderung, also ist 435 if( bStartChk ) // der gewuenschte Bereich ( bStartChk ) 436 bStartDone = sal_True; // vollstaendig abgearbeitet. 437 else 438 bEndDone = sal_True; 439 } 440 else if( bReverse == bStartChk ) // Bei einer Richtungsaenderung kann 441 { // u.U. auch ein Bereich abgearbeitet sein. 442 if( bStartChk ) // Sollte der vordere Teil rueckwaerts gespellt 443 bEndDone = sal_True; // werden und wir kehren unterwegs um, so ist 444 else // der hintere Teil abgearbeitet (und umgekehrt). 445 bStartDone = sal_True; 446 } 447 448 bReverse = bActRev; 449 if( bOtherCntnt && bStartDone && bEndDone ) // Dokument komplett geprueft? 450 { 451 if ( SpellMore() ) // ein weiteres Dokument pruefen? 452 { 453 bOtherCntnt = sal_False; 454 bStartDone = !bReverse; 455 bEndDone = bReverse; 456 SpellStart( SVX_SPELL_BODY ); 457 return sal_True; 458 } 459 return sal_False; 460 } 461 462 sal_Bool bGoOn = sal_False; 463 464 if ( bOtherCntnt ) 465 { 466 bStartChk = sal_False; 467 SpellStart( SVX_SPELL_BODY ); 468 bGoOn = sal_True; 469 } 470 else if ( bStartDone && bEndDone ) 471 { 472 sal_Bool bIsSpellSpecial = xProp.is() ? 473 *(sal_Bool*)xProp->getPropertyValue( 474 ::rtl::OUString::createFromAscii(UPN_IS_SPELL_SPECIAL) ).getValue() 475 : sal_False; 476 // Bodybereich erledigt, Frage nach Sonderbereich 477 if( !IsHyphen() && bIsSpellSpecial && HasOtherCnt() ) 478 { 479 SpellStart( SVX_SPELL_OTHER ); 480 bOtherCntnt = bGoOn = sal_True; 481 } 482 else if ( SpellMore() ) // ein weiteres Dokument pruefen? 483 { 484 bOtherCntnt = sal_False; 485 bStartDone = !bReverse; 486 bEndDone = bReverse; 487 SpellStart( SVX_SPELL_BODY ); 488 return sal_True; 489 } 490 } 491 else 492 { 493 // Ein BODY_Bereich erledigt, Frage nach dem anderen BODY_Bereich 494 WAIT_OFF(); 495 496 // Sobald im Dialog das DontWrapAround gesetzt werden kann, kann der 497 // folgende #ifdef-Zweig aktiviert werden ... 498 #ifdef USED 499 sal_Bool bDontWrapAround = IsHyphen() ? 500 pSpell->GetOptions() & DONT_WRAPAROUND : 501 pSpell->GetHyphOptions() & HYPH_DONT_WRAPAROUND; 502 if( bDontWrapAround ) 503 #else 504 sal_uInt16 nResId = bReverse ? RID_SVXQB_BW_CONTINUE : RID_SVXQB_CONTINUE; 505 QueryBox aBox( pWin, EditResId( nResId ) ); 506 if ( aBox.Execute() != RET_YES ) 507 #endif 508 509 { 510 // Verzicht auf den anderen Bereich, ggf. Frage nach Sonderbereich 511 WAIT_ON(); 512 bStartDone = bEndDone = sal_True; 513 return SpellNext(); 514 } 515 else 516 { 517 bStartChk = !bStartDone; 518 SpellStart( bStartChk ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END ); 519 bGoOn = sal_True; 520 } 521 WAIT_ON(); 522 } 523 return bGoOn; 524 } 525 526 // ----------------------------------------------------------------------- 527 528 Reference< XDictionary > SvxSpellWrapper::GetAllRightDic() const 529 { 530 Reference< XDictionary > xDic; 531 532 Reference< XDictionaryList > xDicList( SvxGetDictionaryList() ); 533 if (xDicList.is()) 534 { 535 Sequence< Reference< XDictionary > > aDics( xDicList->getDictionaries() ); 536 const Reference< XDictionary > *pDic = aDics.getConstArray(); 537 sal_Int32 nCount = aDics.getLength(); 538 539 sal_Int32 i = 0; 540 while (!xDic.is() && i < nCount) 541 { 542 Reference< XDictionary > xTmp( pDic[i], UNO_QUERY ); 543 if (xTmp.is()) 544 { 545 if ( xTmp->isActive() && 546 xTmp->getDictionaryType() != DictionaryType_NEGATIVE && 547 SvxLocaleToLanguage( xTmp->getLocale() ) == LANGUAGE_NONE ) 548 { 549 Reference< frame::XStorable > xStor( xTmp, UNO_QUERY ); 550 if (xStor.is() && xStor->hasLocation() && !xStor->isReadonly()) 551 { 552 xDic = xTmp; 553 } 554 } 555 } 556 ++i; 557 } 558 559 if (!xDic.is()) 560 { 561 xDic = SvxGetOrCreatePosDic( xDicList ); 562 if (xDic.is()) 563 xDic->setActive( sal_True ); 564 } 565 } 566 567 return xDic; 568 } 569 570 // ----------------------------------------------------------------------- 571 572 sal_Bool SvxSpellWrapper::FindSpellError() 573 { 574 ShowLanguageErrors(); 575 576 Reference< XInterface > xRef; 577 578 WAIT_ON(); 579 sal_Bool bSpell = sal_True; 580 581 Reference< XDictionary > xAllRightDic; 582 if (IsAllRight()) 583 xAllRightDic = GetAllRightDic(); 584 585 while ( bSpell ) 586 { 587 SpellContinue(); 588 589 Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY ); 590 Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY ); 591 592 if (xAlt.is()) 593 { 594 if (IsAllRight() && xAllRightDic.is()) 595 { 596 xAllRightDic->add( xAlt->getWord(), sal_False, ::rtl::OUString() ); 597 } 598 else 599 { 600 // look up in ChangeAllList for misspelled word 601 Reference< XDictionary > xChangeAllList( 602 SvxGetChangeAllList(), UNO_QUERY ); 603 Reference< XDictionaryEntry > xEntry; 604 if (xChangeAllList.is()) 605 xEntry = xChangeAllList->getEntry( xAlt->getWord() ); 606 607 if (xEntry.is()) 608 { 609 // replace word without asking 610 ReplaceAll( xEntry->getReplacementText(), 611 SvxLocaleToLanguage( xAlt->getLocale() ) ); 612 } 613 else 614 bSpell = sal_False; 615 } 616 } 617 else if (xHyphWord.is()) 618 bSpell = sal_False; 619 else 620 { 621 SpellEnd(); 622 bSpell = SpellNext(); 623 } 624 } 625 WAIT_OFF(); 626 return GetLast().is(); 627 } 628 629 630 631