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
SvxPrepareAutoCorrect(String & rOldText,String & rNewText)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 {
operator ()lt_LanguageType98 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
GetLangCheckState()106 static LangCheckState_map_t & GetLangCheckState()
107 {
108 static LangCheckState_map_t aLangCheckState;
109 return aLangCheckState;
110 }
111
ShowLanguageErrors()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
~SvxSpellWrapper()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
SvxSpellWrapper(Window * pWn,Reference<XSpellChecker1> & xSpellChecker,const sal_Bool bStart,const sal_Bool bIsAllRight,const sal_Bool bOther,const sal_Bool bRevAllow)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 mpTextObj( NULL),
168 bOtherCntnt ( bOther ),
169 bDialog ( sal_False ),
170 bHyphen ( sal_False ),
171 bAuto ( sal_False ),
172 bStartChk ( bOther ),
173 bRevAllowed ( bRevAllow ),
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
SvxSpellWrapper(Window * pWn,Reference<XHyphenator> & xHyphenator,const sal_Bool bStart,const sal_Bool bOther)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 mpTextObj( NULL),
194 bOtherCntnt ( bOther ),
195 bDialog ( sal_False ),
196 bHyphen ( sal_False ),
197 bAuto ( sal_False ),
198 bReverse ( sal_False ),
199 bStartDone ( bOther || ( !bReverse && bStart ) ),
200 bEndDone ( bReverse && bStart && !bOther ),
201 bStartChk ( bOther ),
202 bRevAllowed ( sal_False ),
203 bAllRight ( sal_True )
204 {
205 }
206
207 // -----------------------------------------------------------------------
208
CheckSpellLang(Reference<XSpellChecker1> xSpell,sal_Int16 nLang)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
CheckHyphLang(Reference<XHyphenator> xHyph,sal_Int16 nLang)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
SpellStart(SvxSpellArea)262 void SvxSpellWrapper::SpellStart( SvxSpellArea /*eSpell*/ )
263 { // Hier muessen die notwendigen Vorbereitungen fuer SpellContinue
264 } // im uebergebenen Bereich getroffen werden.
265
266 // -----------------------------------------------------------------------
267
268
HasOtherCnt()269 sal_Bool SvxSpellWrapper::HasOtherCnt()
270 {
271 return sal_False; // Gibt es ueberhaupt einen Sonderbereich?
272 }
273
274 // -----------------------------------------------------------------------
275
276
SpellMore()277 sal_Bool SvxSpellWrapper::SpellMore()
278 {
279 return sal_False; // Sollen weitere Dokumente geprueft werden?
280 }
281
282 // -----------------------------------------------------------------------
283
284
SpellEnd()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
SpellContinue()295 sal_Bool SvxSpellWrapper::SpellContinue()
296 {
297 return sal_False;
298 }
299
300 // -----------------------------------------------------------------------
301
AutoCorrect(const String &,const String &)302 void SvxSpellWrapper::AutoCorrect( const String&, const String& )
303 {
304 }
305
306 // -----------------------------------------------------------------------
307
308
ScrollArea()309 void SvxSpellWrapper::ScrollArea()
310 { // Scrollarea einstellen
311 }
312
313 // -----------------------------------------------------------------------
314
315
ChangeWord(const String &,const sal_uInt16)316 void SvxSpellWrapper::ChangeWord( const String&, const sal_uInt16 )
317 { // Wort ersetzen
318 }
319
320 // -----------------------------------------------------------------------
321
322
GetThesWord()323 String SvxSpellWrapper::GetThesWord()
324 {
325 // Welches Wort soll nachgeschlagen werden?
326 return String();
327 }
328
329 // -----------------------------------------------------------------------
330
331
ChangeThesWord(const String &)332 void SvxSpellWrapper::ChangeThesWord( const String& )
333 {
334 // Wort wg. Thesaurus ersetzen
335 }
336
337 // -----------------------------------------------------------------------
338
StartThesaurus(const String & rWord,sal_uInt16 nLanguage)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
ReplaceAll(const String &,sal_Int16)361 void SvxSpellWrapper::ReplaceAll( const String &, sal_Int16 )
362 { // Wort aus der Replace-Liste ersetzen
363 }
364
365 // -----------------------------------------------------------------------
366
367
SetLanguage(const sal_uInt16)368 void SvxSpellWrapper::SetLanguage( const sal_uInt16 )
369 { // Sprache aendern
370 }
371
372 // -----------------------------------------------------------------------
373
374
InsertHyphen(const sal_uInt16)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
SpellDocument()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
SpellNext()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
GetAllRightDic() const528 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
FindSpellError()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