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 != nullptr) { pWin->EnterWait(); }
43 :
44 : #define WAIT_OFF() if(pWin != nullptr) { 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 168 : static LangCheckState_map_t & GetLangCheckState()
93 : {
94 168 : static LangCheckState_map_t aLangCheckState;
95 168 : return aLangCheckState;
96 : }
97 :
98 168 : void SvxSpellWrapper::ShowLanguageErrors()
99 : {
100 : // display message boxes for languages not available for
101 : // spellchecking or hyphenation
102 168 : LangCheckState_map_t &rLCS = GetLangCheckState();
103 168 : LangCheckState_map_t::iterator aIt( rLCS.begin() );
104 336 : 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 168 : }
131 :
132 84 : SvxSpellWrapper::~SvxSpellWrapper()
133 : {
134 84 : }
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 84 : 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 84 : bAllRight ( bIsAllRight )
161 : {
162 84 : Reference< linguistic2::XLinguProperties > xProp( SvxGetLinguPropertySet() );
163 84 : bool bWrapReverse = xProp.is() && xProp->getIsWrapReverse();
164 84 : bReverse = bRevAllow && bWrapReverse;
165 84 : bStartDone = bOther || ( !bReverse && bStart );
166 84 : bEndDone = bReverse && bStart && !bOther;
167 84 : }
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 84 : void SvxSpellWrapper::SpellEnd()
269 : { // Area is complete, tidy up if necessary
270 :
271 : // display error for last language not found
272 84 : ShowLanguageErrors();
273 84 : }
274 :
275 0 : bool SvxSpellWrapper::SpellContinue()
276 : {
277 0 : return false;
278 : }
279 :
280 0 : void SvxSpellWrapper::AutoCorrect( const OUString&, const OUString& )
281 : {
282 0 : }
283 :
284 0 : void SvxSpellWrapper::ScrollArea()
285 : { // Set Scroll area
286 0 : }
287 :
288 0 : void SvxSpellWrapper::ChangeWord( const OUString&, const sal_uInt16 )
289 : { // Insert Word
290 0 : }
291 :
292 0 : void SvxSpellWrapper::ChangeThesWord( const OUString& )
293 : {
294 : // replace word due to Thesaurus.
295 0 : }
296 :
297 0 : void SvxSpellWrapper::ReplaceAll( const OUString &, sal_Int16 )
298 : { // Replace Word from the Replace list
299 0 : }
300 :
301 0 : void SvxSpellWrapper::InsertHyphen( const sal_Int32 )
302 : { // inserting and deleting Hyphae
303 0 : }
304 :
305 : // Testing of the document areas in the order specified by the flags
306 84 : void SvxSpellWrapper::SpellDocument( )
307 : {
308 84 : if ( bOtherCntnt )
309 : {
310 0 : bReverse = false;
311 0 : SpellStart( SVX_SPELL_OTHER );
312 : }
313 : else
314 : {
315 84 : bStartChk = bReverse;
316 84 : SpellStart( bReverse ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END );
317 : }
318 :
319 84 : if ( FindSpellError() )
320 : {
321 0 : Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY );
322 0 : Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY );
323 :
324 0 : vcl::Window *pOld = pWin;
325 0 : bDialog = true;
326 0 : if (xHyphWord.is())
327 : {
328 0 : EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
329 : boost::scoped_ptr<AbstractHyphenWordDialog> pDlg(pFact->CreateHyphenWordDialog( pWin,
330 0 : xHyphWord->getWord(),
331 0 : LanguageTag( xHyphWord->getLocale() ).getLanguageType(),
332 0 : xHyph, this ));
333 0 : pWin = pDlg->GetWindow();
334 0 : pDlg->Execute();
335 : }
336 0 : bDialog = false;
337 0 : pWin = pOld;
338 : }
339 84 : }
340 :
341 :
342 : // Select the next area
343 :
344 :
345 84 : bool SvxSpellWrapper::SpellNext( )
346 : {
347 84 : Reference< linguistic2::XLinguProperties > xProp( SvxGetLinguPropertySet() );
348 84 : bool bWrapReverse = xProp.is() && xProp->getIsWrapReverse();
349 84 : bool bActRev = bRevAllowed && bWrapReverse;
350 :
351 : // bActRev is the direction after Spell checking, bReverse is the one
352 : // at the beginning.
353 84 : if( bActRev == bReverse )
354 : { // No change of direction, thus is the
355 84 : if( bStartChk ) // desired area ( bStartChk )
356 0 : bStartDone = true; // completely processed.
357 : else
358 84 : bEndDone = true;
359 : }
360 0 : else if( bReverse == bStartChk ) //For a change of direction, an area can
361 : { // be processed during certain circumstances
362 0 : if( bStartChk ) // If the firdt part is spell checked in backwards
363 0 : bEndDone = true; // and this is reversed in the process, then
364 : else // then the end part is processed (and vice-versa).
365 0 : bStartDone = true;
366 : }
367 :
368 84 : bReverse = bActRev;
369 84 : if( bOtherCntnt && bStartDone && bEndDone ) // Document has been fully checked?
370 : {
371 0 : if ( SpellMore() ) // spell check another document?
372 : {
373 0 : bOtherCntnt = false;
374 0 : bStartDone = !bReverse;
375 0 : bEndDone = bReverse;
376 0 : SpellStart( SVX_SPELL_BODY );
377 0 : return true;
378 : }
379 0 : return false;
380 : }
381 :
382 84 : bool bGoOn = false;
383 :
384 84 : if ( bOtherCntnt )
385 : {
386 0 : bStartChk = false;
387 0 : SpellStart( SVX_SPELL_BODY );
388 0 : bGoOn = true;
389 : }
390 84 : else if ( bStartDone && bEndDone )
391 : {
392 84 : bool bIsSpellSpecial = xProp.is() && xProp->getIsSpellSpecial();
393 : // Body area done, ask for special area
394 84 : if( !IsHyphen() && bIsSpellSpecial && HasOtherCnt() )
395 : {
396 0 : SpellStart( SVX_SPELL_OTHER );
397 0 : bOtherCntnt = bGoOn = true;
398 : }
399 84 : else if ( SpellMore() ) // check another document?
400 : {
401 0 : bOtherCntnt = false;
402 0 : bStartDone = !bReverse;
403 0 : bEndDone = bReverse;
404 0 : SpellStart( SVX_SPELL_BODY );
405 0 : return true;
406 84 : }
407 : }
408 : else
409 : {
410 : // a BODY_area done, ask for the other BODY_area
411 0 : WAIT_OFF();
412 :
413 0 : sal_uInt16 nResId = bReverse ? RID_SVXSTR_QUERY_BW_CONTINUE : RID_SVXSTR_QUERY_CONTINUE;
414 0 : ScopedVclPtrInstance< MessageDialog > aBox(pWin, EditResId(nResId), VCL_MESSAGE_QUESTION, VCL_BUTTONS_YES_NO);
415 0 : if ( aBox->Execute() != RET_YES )
416 : {
417 : // sacrifice the other area if necessary ask for special area
418 0 : WAIT_ON();
419 0 : bStartDone = bEndDone = true;
420 0 : return SpellNext();
421 : }
422 : else
423 : {
424 0 : bStartChk = !bStartDone;
425 0 : SpellStart( bStartChk ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END );
426 0 : bGoOn = true;
427 : }
428 0 : WAIT_ON();
429 : }
430 84 : return bGoOn;
431 : }
432 :
433 :
434 :
435 0 : Reference< XDictionary > SvxSpellWrapper::GetAllRightDic()
436 : {
437 0 : Reference< XDictionary > xDic;
438 :
439 0 : Reference< XSearchableDictionaryList > xDicList( SvxGetDictionaryList() );
440 0 : if (xDicList.is())
441 : {
442 0 : Sequence< Reference< XDictionary > > aDics( xDicList->getDictionaries() );
443 0 : const Reference< XDictionary > *pDic = aDics.getConstArray();
444 0 : sal_Int32 nCount = aDics.getLength();
445 :
446 0 : sal_Int32 i = 0;
447 0 : while (!xDic.is() && i < nCount)
448 : {
449 0 : Reference< XDictionary > xTmp( pDic[i], UNO_QUERY );
450 0 : if (xTmp.is())
451 : {
452 0 : if ( xTmp->isActive() &&
453 0 : xTmp->getDictionaryType() != DictionaryType_NEGATIVE &&
454 0 : LanguageTag( xTmp->getLocale() ).getLanguageType() == LANGUAGE_NONE )
455 : {
456 0 : Reference< frame::XStorable > xStor( xTmp, UNO_QUERY );
457 0 : if (xStor.is() && xStor->hasLocation() && !xStor->isReadonly())
458 : {
459 0 : xDic = xTmp;
460 0 : }
461 : }
462 : }
463 0 : ++i;
464 0 : }
465 :
466 0 : if (!xDic.is())
467 : {
468 0 : xDic = SvxGetOrCreatePosDic( xDicList );
469 0 : if (xDic.is())
470 0 : xDic->setActive( sal_True );
471 0 : }
472 : }
473 :
474 0 : return xDic;
475 : }
476 :
477 :
478 :
479 84 : bool SvxSpellWrapper::FindSpellError()
480 : {
481 84 : ShowLanguageErrors();
482 :
483 84 : Reference< XInterface > xRef;
484 :
485 84 : WAIT_ON();
486 84 : bool bSpell = true;
487 :
488 168 : Reference< XDictionary > xAllRightDic;
489 84 : if (IsAllRight())
490 0 : xAllRightDic = GetAllRightDic();
491 :
492 252 : while ( bSpell )
493 : {
494 84 : SpellContinue();
495 :
496 84 : Reference< XSpellAlternatives > xAlt( GetLast(), UNO_QUERY );
497 168 : Reference< XHyphenatedWord > xHyphWord( GetLast(), UNO_QUERY );
498 :
499 84 : if (xAlt.is())
500 : {
501 0 : if (IsAllRight() && xAllRightDic.is())
502 : {
503 0 : xAllRightDic->add( xAlt->getWord(), sal_False, OUString() );
504 : }
505 : else
506 : {
507 : // look up in ChangeAllList for misspelled word
508 : Reference< XDictionary > xChangeAllList(
509 0 : SvxGetChangeAllList(), UNO_QUERY );
510 0 : Reference< XDictionaryEntry > xEntry;
511 0 : if (xChangeAllList.is())
512 0 : xEntry = xChangeAllList->getEntry( xAlt->getWord() );
513 :
514 0 : if (xEntry.is())
515 : {
516 : // replace word without asking
517 0 : ReplaceAll( xEntry->getReplacementText(),
518 0 : LanguageTag( xAlt->getLocale() ).getLanguageType() );
519 : }
520 : else
521 0 : bSpell = false;
522 : }
523 : }
524 84 : else if (xHyphWord.is())
525 0 : bSpell = false;
526 : else
527 : {
528 84 : SpellEnd();
529 84 : bSpell = SpellNext();
530 : }
531 84 : }
532 84 : WAIT_OFF();
533 168 : return GetLast().is();
534 444 : }
535 :
536 :
537 :
538 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|