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