Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include<rtl/ustring.hxx>
21 : #include <tools/shl.hxx>
22 : #include <vcl/wrkwin.hxx>
23 : #include <vcl/svapp.hxx>
24 : #include <vcl/msgbox.hxx>
25 : #include <svtools/langtab.hxx>
26 :
27 : #ifndef __RSC
28 : #include <tools/errinf.hxx>
29 : #endif
30 : #include <editeng/unolingu.hxx>
31 : #include <linguistic/lngprops.hxx>
32 : #include <com/sun/star/frame/XStorable.hpp>
33 :
34 : #include <map>
35 :
36 : #include <editeng/svxenum.hxx>
37 : #include <editeng/splwrap.hxx>
38 : #include <editeng/edtdlg.hxx>
39 : #include <editeng/eerdll.hxx>
40 : #include <editeng/editrids.hrc>
41 : #include <editeng/editids.hrc>
42 : #include <editeng/editerr.hxx>
43 :
44 : #define WAIT_ON() if(pWin != NULL) { pWin->EnterWait(); }
45 :
46 : #define WAIT_OFF() if(pWin != NULL) { pWin->LeaveWait(); }
47 :
48 : using namespace ::com::sun::star;
49 : using namespace ::com::sun::star::uno;
50 : using namespace ::com::sun::star::beans;
51 : using namespace ::com::sun::star::linguistic2;
52 :
53 :
54 : // misc functions ---------------------------------------------
55 :
56 0 : void SvxPrepareAutoCorrect( String &rOldText, String &rNewText )
57 : {
58 : // This function should be used to strip (or add) trailing '.' from
59 : // the strings before passing them on to the autocorrect function in
60 : // order that the autocorrect function will hopefully
61 : // works properly with normal words and abbreviations (with trailing '.')
62 : // independ of if they are at the end of the sentence or not.
63 : //
64 : // rOldText: text to be replaced
65 : // rNewText: replacement text
66 :
67 0 : xub_StrLen nOldLen = rOldText.Len(),
68 0 : nNewLen = rNewText.Len();
69 0 : if (nOldLen && nNewLen)
70 : {
71 0 : sal_Bool bOldHasDot = sal_Unicode( '.' ) == rOldText.GetChar( nOldLen - 1 ),
72 0 : bNewHasDot = sal_Unicode( '.' ) == rNewText.GetChar( nNewLen - 1 );
73 0 : if (bOldHasDot && !bNewHasDot
74 : /*this is: !(bOldHasDot && bNewHasDot) && bOldHasDot*/)
75 0 : rOldText.Erase( nOldLen - 1 );
76 : }
77 0 : }
78 :
79 : #define SVX_LANG_NEED_CHECK 0
80 : #define SVX_LANG_OK 1
81 : #define SVX_LANG_MISSING 2
82 : #define SVX_LANG_MISSING_DO_WARN 3
83 :
84 : struct lt_LanguageType
85 : {
86 0 : bool operator()( LanguageType n1, LanguageType n2 ) const
87 : {
88 0 : return n1 < n2;
89 : }
90 : };
91 :
92 : typedef std::map< LanguageType, sal_uInt16, lt_LanguageType > LangCheckState_map_t;
93 :
94 12 : static LangCheckState_map_t & GetLangCheckState()
95 : {
96 12 : static LangCheckState_map_t aLangCheckState;
97 12 : return aLangCheckState;
98 : }
99 :
100 12 : void SvxSpellWrapper::ShowLanguageErrors()
101 : {
102 : // display message boxes for languages not available for
103 : // spellchecking or hyphenation
104 12 : LangCheckState_map_t &rLCS = GetLangCheckState();
105 12 : LangCheckState_map_t::iterator aIt( rLCS.begin() );
106 24 : while (aIt != rLCS.end())
107 : {
108 0 : LanguageType nLang = aIt->first;
109 0 : sal_uInt16 nVal = aIt->second;
110 0 : sal_uInt16 nTmpSpell = nVal & 0x00FF;
111 0 : sal_uInt16 nTmpHyph = (nVal >> 8) & 0x00FF;
112 :
113 0 : if (SVX_LANG_MISSING_DO_WARN == nTmpSpell)
114 : {
115 0 : String aErr( SvtLanguageTable::GetLanguageString( nLang ) );
116 : ErrorHandler::HandleError(
117 0 : *new StringErrorInfo( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) );
118 0 : nTmpSpell = SVX_LANG_MISSING;
119 : }
120 0 : if (SVX_LANG_MISSING_DO_WARN == nTmpHyph)
121 : {
122 0 : String aErr( SvtLanguageTable::GetLanguageString( nLang ) );
123 : ErrorHandler::HandleError(
124 0 : *new StringErrorInfo( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) );
125 0 : nTmpHyph = SVX_LANG_MISSING;
126 : }
127 :
128 0 : rLCS[ nLang ] = (nTmpHyph << 8) | nTmpSpell;
129 0 : ++aIt;
130 : }
131 :
132 12 : }
133 :
134 6 : SvxSpellWrapper::~SvxSpellWrapper()
135 : {
136 6 : }
137 :
138 : /*--------------------------------------------------------------------
139 : * Description: Constructor, the test sequence is determined
140 : *
141 : * !bStart && !bOtherCntnt: BODY_END, BODY_START, OTHER
142 : * !bStart && bOtherCntnt: OTHER, BODY
143 : * bStart && !bOtherCntnt: BODY_END, OTHER
144 : * bStart && bOtherCntnt: OTHER
145 : *
146 : --------------------------------------------------------------------*/
147 :
148 6 : SvxSpellWrapper::SvxSpellWrapper( Window* pWn,
149 : Reference< XSpellChecker1 > &xSpellChecker,
150 : const sal_Bool bStart, const sal_Bool bIsAllRight,
151 : const sal_Bool bOther, const sal_Bool bRevAllow ) :
152 :
153 : pWin ( pWn ),
154 : xSpell ( xSpellChecker ),
155 : bOtherCntnt ( bOther ),
156 : bDialog ( sal_False ),
157 : bHyphen ( sal_False ),
158 : bAuto ( sal_False ),
159 : bStartChk ( bOther ),
160 : bRevAllowed ( bRevAllow ),
161 6 : bAllRight ( bIsAllRight )
162 : {
163 6 : Reference< beans::XPropertySet > xProp( SvxGetLinguPropertySet() );
164 6 : sal_Bool bWrapReverse = xProp.is() ?
165 6 : *(sal_Bool*)xProp->getPropertyValue(
166 18 : ::rtl::OUString(UPN_IS_WRAP_REVERSE) ).getValue()
167 18 : : sal_False;
168 6 : bReverse = bRevAllow && bWrapReverse;
169 6 : bStartDone = bOther || ( !bReverse && bStart );
170 6 : bEndDone = bReverse && bStart && !bOther;
171 6 : }
172 :
173 : // -----------------------------------------------------------------------
174 :
175 0 : SvxSpellWrapper::SvxSpellWrapper( Window* pWn,
176 : Reference< XHyphenator > &xHyphenator,
177 : const sal_Bool bStart, const sal_Bool bOther ) :
178 : pWin ( pWn ),
179 : xHyph ( xHyphenator ),
180 : bOtherCntnt ( bOther ),
181 : bDialog ( sal_False ),
182 : bHyphen ( sal_False ),
183 : bAuto ( sal_False ),
184 : bReverse ( sal_False ),
185 0 : bStartDone ( bOther || ( !bReverse && bStart ) ),
186 : bEndDone ( bReverse && bStart && !bOther ),
187 : bStartChk ( bOther ),
188 : bRevAllowed ( sal_False ),
189 0 : bAllRight ( sal_True )
190 : {
191 0 : }
192 :
193 : // -----------------------------------------------------------------------
194 :
195 0 : sal_Int16 SvxSpellWrapper::CheckSpellLang(
196 : Reference< XSpellChecker1 > xSpell, sal_Int16 nLang)
197 : {
198 0 : LangCheckState_map_t &rLCS = GetLangCheckState();
199 :
200 0 : LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) );
201 0 : sal_uInt16 nVal = aIt == rLCS.end() ? SVX_LANG_NEED_CHECK : aIt->second;
202 :
203 0 : if (aIt == rLCS.end())
204 0 : rLCS[ nLang ] = nVal;
205 :
206 0 : if (SVX_LANG_NEED_CHECK == (nVal & 0x00FF))
207 : {
208 0 : sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN;
209 0 : if (xSpell.is() && xSpell->hasLanguage( nLang ))
210 0 : nTmpVal = SVX_LANG_OK;
211 0 : nVal &= 0xFF00;
212 0 : nVal |= nTmpVal;
213 :
214 0 : rLCS[ nLang ] = nVal;
215 : }
216 :
217 0 : return (sal_Int16) nVal;
218 : }
219 :
220 0 : sal_Int16 SvxSpellWrapper::CheckHyphLang(
221 : Reference< XHyphenator > xHyph, sal_Int16 nLang)
222 : {
223 0 : LangCheckState_map_t &rLCS = GetLangCheckState();
224 :
225 0 : LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) );
226 0 : sal_uInt16 nVal = aIt == rLCS.end() ? 0 : aIt->second;
227 :
228 0 : if (aIt == rLCS.end())
229 0 : rLCS[ nLang ] = nVal;
230 :
231 0 : if (SVX_LANG_NEED_CHECK == ((nVal >> 8) & 0x00FF))
232 : {
233 0 : sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN;
234 0 : if (xHyph.is() && xHyph->hasLocale( LanguageTag( nLang ).getLocale() ))
235 0 : nTmpVal = SVX_LANG_OK;
236 0 : nVal &= 0x00FF;
237 0 : nVal |= nTmpVal << 8;
238 :
239 0 : rLCS[ nLang ] = nVal;
240 : }
241 :
242 0 : return (sal_Int16) nVal;
243 : }
244 :
245 : // -----------------------------------------------------------------------
246 :
247 :
248 0 : void SvxSpellWrapper::SpellStart( SvxSpellArea /*eSpell*/ )
249 : { // Here, the necessary preparations be made for SpellContinue in the
250 0 : } // given area.
251 :
252 : // -----------------------------------------------------------------------
253 :
254 :
255 0 : sal_Bool SvxSpellWrapper::HasOtherCnt()
256 : {
257 0 : return sal_False; // Is there a special area?
258 : }
259 :
260 : // -----------------------------------------------------------------------
261 :
262 :
263 0 : sal_Bool SvxSpellWrapper::SpellMore()
264 : {
265 0 : return sal_False; // Should additional documents be examined?
266 : }
267 :
268 : // -----------------------------------------------------------------------
269 :
270 :
271 6 : void SvxSpellWrapper::SpellEnd()
272 : { // Area is complete, tidy up if necessary
273 :
274 : // display error for last language not found
275 6 : ShowLanguageErrors();
276 6 : }
277 :
278 : // -----------------------------------------------------------------------
279 :
280 :
281 0 : sal_Bool SvxSpellWrapper::SpellContinue()
282 : {
283 0 : return sal_False;
284 : }
285 :
286 : // -----------------------------------------------------------------------
287 :
288 0 : void SvxSpellWrapper::AutoCorrect( const String&, const String& )
289 : {
290 0 : }
291 :
292 : // -----------------------------------------------------------------------
293 :
294 :
295 0 : void SvxSpellWrapper::ScrollArea()
296 : { // Set Scroll area
297 0 : }
298 :
299 : // -----------------------------------------------------------------------
300 :
301 :
302 0 : void SvxSpellWrapper::ChangeWord( const String&, const sal_uInt16 )
303 : { // Insert Word
304 0 : }
305 :
306 : // -----------------------------------------------------------------------
307 :
308 :
309 0 : String SvxSpellWrapper::GetThesWord()
310 : {
311 : // What word should be looked up?
312 0 : return String();
313 : }
314 :
315 : // -----------------------------------------------------------------------
316 :
317 :
318 0 : void SvxSpellWrapper::ChangeThesWord( const String& )
319 : {
320 : // replace word due to Thesaurus.
321 0 : }
322 :
323 : // -----------------------------------------------------------------------
324 :
325 0 : void SvxSpellWrapper::StartThesaurus( const String &rWord, sal_uInt16 nLanguage )
326 : {
327 0 : Reference< XThesaurus > xThes( SvxGetThesaurus() );
328 0 : if (!xThes.is())
329 : {
330 0 : InfoBox( pWin, EE_RESSTR( RID_SVXSTR_HMERR_THESAURUS ) ).Execute();
331 0 : return;
332 : }
333 :
334 0 : WAIT_ON(); // while looking up for initial word
335 0 : EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
336 0 : AbstractThesaurusDialog* pDlg = pFact->CreateThesaurusDialog( pWin, xThes, rWord, nLanguage );
337 0 : WAIT_OFF();
338 0 : if ( pDlg->Execute()== RET_OK )
339 : {
340 0 : ChangeThesWord( pDlg->GetWord() );
341 : }
342 0 : delete pDlg;
343 : }
344 :
345 : // -----------------------------------------------------------------------
346 :
347 0 : void SvxSpellWrapper::ReplaceAll( const String &, sal_Int16 )
348 : { // Replace Word from the the Replace list
349 0 : }
350 :
351 : // -----------------------------------------------------------------------
352 :
353 :
354 0 : void SvxSpellWrapper::SetLanguage( const sal_uInt16 )
355 : { // Set Language
356 0 : }
357 :
358 : // -----------------------------------------------------------------------
359 :
360 :
361 0 : void SvxSpellWrapper::InsertHyphen( const sal_uInt16 )
362 : { // inserting and deleting Hyphae
363 0 : }
364 :
365 : // -----------------------------------------------------------------------
366 : // Testing of the document areas in the order specified by the flags
367 :
368 :
369 6 : void SvxSpellWrapper::SpellDocument( )
370 : {
371 6 : if ( bOtherCntnt )
372 : {
373 0 : bReverse = sal_False;
374 0 : SpellStart( SVX_SPELL_OTHER );
375 : }
376 : else
377 : {
378 6 : bStartChk = bReverse;
379 6 : SpellStart( bReverse ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END );
380 : }
381 :
382 6 : if ( FindSpellError() )
383 : {
384 0 : Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY );
385 0 : Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY );
386 :
387 0 : Window *pOld = pWin;
388 0 : bDialog = sal_True;
389 0 : if (xHyphWord.is())
390 : {
391 0 : EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
392 : AbstractHyphenWordDialog* pDlg = pFact->CreateHyphenWordDialog( pWin,
393 0 : xHyphWord->getWord(),
394 0 : LanguageTag( xHyphWord->getLocale() ).getLanguageType(),
395 0 : xHyph, this );
396 0 : pWin = pDlg->GetWindow();
397 0 : pDlg->Execute();
398 0 : delete pDlg;
399 : }
400 0 : bDialog = sal_False;
401 0 : pWin = pOld;
402 : };
403 6 : }
404 :
405 : // -----------------------------------------------------------------------
406 : // Select the next area
407 :
408 :
409 6 : sal_Bool SvxSpellWrapper::SpellNext( )
410 : {
411 6 : Reference< beans::XPropertySet > xProp( SvxGetLinguPropertySet() );
412 6 : sal_Bool bWrapReverse = xProp.is() ?
413 6 : *(sal_Bool*)xProp->getPropertyValue(
414 18 : ::rtl::OUString(UPN_IS_WRAP_REVERSE) ).getValue()
415 18 : : sal_False;
416 6 : sal_Bool bActRev = bRevAllowed && bWrapReverse;
417 :
418 : // bActRev is the direction after Spell checking, bReverse is the one
419 : // at the beginning.
420 6 : if( bActRev == bReverse )
421 : { // No change of direction, thus is the
422 6 : if( bStartChk ) // desired area ( bStartChk )
423 0 : bStartDone = sal_True; // completely processed.
424 : else
425 6 : bEndDone = sal_True;
426 : }
427 0 : else if( bReverse == bStartChk ) //For a change of direction, an area can
428 : { // be processed during certain circumstances
429 0 : if( bStartChk ) // If the firdt part is spell checked in backwards
430 0 : bEndDone = sal_True; // and this is reversed in the process, then
431 : else // then the end part is processed (and vice-versa).
432 0 : bStartDone = sal_True;
433 : }
434 :
435 6 : bReverse = bActRev;
436 6 : if( bOtherCntnt && bStartDone && bEndDone ) // Document has been fully checked?
437 : {
438 0 : if ( SpellMore() ) // spell check another document?
439 : {
440 0 : bOtherCntnt = sal_False;
441 0 : bStartDone = !bReverse;
442 0 : bEndDone = bReverse;
443 0 : SpellStart( SVX_SPELL_BODY );
444 0 : return sal_True;
445 : }
446 0 : return sal_False;
447 : }
448 :
449 6 : sal_Bool bGoOn = sal_False;
450 :
451 6 : if ( bOtherCntnt )
452 : {
453 0 : bStartChk = sal_False;
454 0 : SpellStart( SVX_SPELL_BODY );
455 0 : bGoOn = sal_True;
456 : }
457 6 : else if ( bStartDone && bEndDone )
458 : {
459 6 : sal_Bool bIsSpellSpecial = xProp.is() ?
460 6 : *(sal_Bool*)xProp->getPropertyValue(
461 18 : ::rtl::OUString(UPN_IS_SPELL_SPECIAL) ).getValue()
462 18 : : sal_False;
463 : // Body area done, ask for special area
464 6 : if( !IsHyphen() && bIsSpellSpecial && HasOtherCnt() )
465 : {
466 0 : SpellStart( SVX_SPELL_OTHER );
467 0 : bOtherCntnt = bGoOn = sal_True;
468 : }
469 6 : else if ( SpellMore() ) // check another document?
470 : {
471 0 : bOtherCntnt = sal_False;
472 0 : bStartDone = !bReverse;
473 0 : bEndDone = bReverse;
474 0 : SpellStart( SVX_SPELL_BODY );
475 0 : return sal_True;
476 6 : }
477 : }
478 : else
479 : {
480 : // a BODY_area done, ask for the other BODY_area
481 0 : WAIT_OFF();
482 :
483 0 : sal_uInt16 nResId = bReverse ? RID_SVXQB_BW_CONTINUE : RID_SVXQB_CONTINUE;
484 0 : QueryBox aBox( pWin, EditResId( nResId ) );
485 0 : if ( aBox.Execute() != RET_YES )
486 : {
487 : // sacrifice the other area if necessary ask for special area
488 0 : WAIT_ON();
489 0 : bStartDone = bEndDone = sal_True;
490 0 : return SpellNext();
491 : }
492 : else
493 : {
494 0 : bStartChk = !bStartDone;
495 0 : SpellStart( bStartChk ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END );
496 0 : bGoOn = sal_True;
497 : }
498 0 : WAIT_ON();
499 : }
500 6 : return bGoOn;
501 : }
502 :
503 : // -----------------------------------------------------------------------
504 :
505 0 : Reference< XDictionary > SvxSpellWrapper::GetAllRightDic() const
506 : {
507 0 : Reference< XDictionary > xDic;
508 :
509 0 : Reference< XDictionaryList > xDicList( SvxGetDictionaryList() );
510 0 : if (xDicList.is())
511 : {
512 0 : Sequence< Reference< XDictionary > > aDics( xDicList->getDictionaries() );
513 0 : const Reference< XDictionary > *pDic = aDics.getConstArray();
514 0 : sal_Int32 nCount = aDics.getLength();
515 :
516 0 : sal_Int32 i = 0;
517 0 : while (!xDic.is() && i < nCount)
518 : {
519 0 : Reference< XDictionary > xTmp( pDic[i], UNO_QUERY );
520 0 : if (xTmp.is())
521 : {
522 0 : if ( xTmp->isActive() &&
523 0 : xTmp->getDictionaryType() != DictionaryType_NEGATIVE &&
524 0 : LanguageTag( xTmp->getLocale() ).getLanguageType() == LANGUAGE_NONE )
525 : {
526 0 : Reference< frame::XStorable > xStor( xTmp, UNO_QUERY );
527 0 : if (xStor.is() && xStor->hasLocation() && !xStor->isReadonly())
528 : {
529 0 : xDic = xTmp;
530 0 : }
531 : }
532 : }
533 0 : ++i;
534 0 : }
535 :
536 0 : if (!xDic.is())
537 : {
538 0 : xDic = SvxGetOrCreatePosDic( xDicList );
539 0 : if (xDic.is())
540 0 : xDic->setActive( sal_True );
541 0 : }
542 : }
543 :
544 0 : return xDic;
545 : }
546 :
547 : // -----------------------------------------------------------------------
548 :
549 6 : sal_Bool SvxSpellWrapper::FindSpellError()
550 : {
551 6 : ShowLanguageErrors();
552 :
553 6 : Reference< XInterface > xRef;
554 :
555 6 : WAIT_ON();
556 6 : sal_Bool bSpell = sal_True;
557 :
558 6 : Reference< XDictionary > xAllRightDic;
559 6 : if (IsAllRight())
560 0 : xAllRightDic = GetAllRightDic();
561 :
562 18 : while ( bSpell )
563 : {
564 6 : SpellContinue();
565 :
566 6 : Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY );
567 6 : Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY );
568 :
569 6 : if (xAlt.is())
570 : {
571 0 : if (IsAllRight() && xAllRightDic.is())
572 : {
573 0 : xAllRightDic->add( xAlt->getWord(), sal_False, ::rtl::OUString() );
574 : }
575 : else
576 : {
577 : // look up in ChangeAllList for misspelled word
578 : Reference< XDictionary > xChangeAllList(
579 0 : SvxGetChangeAllList(), UNO_QUERY );
580 0 : Reference< XDictionaryEntry > xEntry;
581 0 : if (xChangeAllList.is())
582 0 : xEntry = xChangeAllList->getEntry( xAlt->getWord() );
583 :
584 0 : if (xEntry.is())
585 : {
586 : // replace word without asking
587 0 : ReplaceAll( xEntry->getReplacementText(),
588 0 : LanguageTag( xAlt->getLocale() ).getLanguageType() );
589 : }
590 : else
591 0 : bSpell = sal_False;
592 : }
593 : }
594 6 : else if (xHyphWord.is())
595 0 : bSpell = sal_False;
596 : else
597 : {
598 6 : SpellEnd();
599 6 : bSpell = SpellNext();
600 : }
601 6 : }
602 6 : WAIT_OFF();
603 6 : return GetLast().is();
604 : }
605 :
606 :
607 :
608 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|