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 <com/sun/star/io/XStream.hpp>
21 : #include <com/sun/star/lang/Locale.hpp>
22 : #include <tools/urlobj.hxx>
23 : #include <i18nlangtag/mslangid.hxx>
24 : #include <vcl/svapp.hxx>
25 : #include <vcl/settings.hxx>
26 : #include <sot/storinfo.hxx>
27 : #include <svl/fstathelper.hxx>
28 : #include <svtools/helpopt.hxx>
29 : #include <svl/urihelper.hxx>
30 : #include <unotools/charclass.hxx>
31 : #include <com/sun/star/i18n/UnicodeType.hpp>
32 : #include <unotools/collatorwrapper.hxx>
33 : #include <com/sun/star/i18n/CollatorOptions.hpp>
34 : #include <com/sun/star/i18n/UnicodeScript.hpp>
35 : #include <com/sun/star/i18n/OrdinalSuffix.hpp>
36 : #include <unotools/localedatawrapper.hxx>
37 : #include <unotools/transliterationwrapper.hxx>
38 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
39 : #include <com/sun/star/io/XActiveDataSource.hpp>
40 : #include <comphelper/processfactory.hxx>
41 : #include <comphelper/storagehelper.hxx>
42 : #include <comphelper/string.hxx>
43 : #include <editeng/editids.hrc>
44 : #include <sot/storage.hxx>
45 : #include <editeng/udlnitem.hxx>
46 : #include <editeng/wghtitem.hxx>
47 : #include <editeng/escapementitem.hxx>
48 : #include <editeng/svxacorr.hxx>
49 : #include <editeng/unolingu.hxx>
50 : #include "vcl/window.hxx"
51 : #include <helpid.hrc>
52 : #include <com/sun/star/xml/sax/InputSource.hpp>
53 : #include <com/sun/star/xml/sax/FastParser.hpp>
54 : #include <com/sun/star/xml/sax/FastToken.hpp>
55 : #include <com/sun/star/xml/sax/Writer.hpp>
56 : #include <com/sun/star/xml/sax/FastTokenHandler.hpp>
57 : #include <unotools/streamwrap.hxx>
58 : #include <SvXMLAutoCorrectImport.hxx>
59 : #include <SvXMLAutoCorrectExport.hxx>
60 : #include <SvXMLAutoCorrectTokenHandler.hxx>
61 : #include <ucbhelper/content.hxx>
62 : #include <com/sun/star/ucb/XCommandEnvironment.hpp>
63 : #include <com/sun/star/ucb/TransferInfo.hpp>
64 : #include <com/sun/star/ucb/NameClash.hpp>
65 : #include <xmloff/xmltoken.hxx>
66 : #include <vcl/help.hxx>
67 : #include <set>
68 : #include <unordered_map>
69 :
70 : using namespace ::com::sun::star::ucb;
71 : using namespace ::com::sun::star::uno;
72 : using namespace ::com::sun::star::xml::sax;
73 : using namespace ::com::sun::star;
74 : using namespace ::xmloff::token;
75 : using namespace ::utl;
76 :
77 : static const int C_NONE = 0x00;
78 : static const int C_FULL_STOP = 0x01;
79 : static const int C_EXCLAMATION_MARK = 0x02;
80 : static const int C_QUESTION_MARK = 0x04;
81 : static const sal_Unicode cNonBreakingSpace = 0xA0;
82 :
83 : static const sal_Char pXMLImplWrdStt_ExcptLstStr[] = "WordExceptList.xml";
84 : static const sal_Char pXMLImplCplStt_ExcptLstStr[] = "SentenceExceptList.xml";
85 : static const sal_Char pXMLImplAutocorr_ListStr[] = "DocumentList.xml";
86 :
87 : static const sal_Char
88 : /* also at these beginnings - Brackets and all kinds of begin characters */
89 : sImplSttSkipChars[] = "\"\'([{\x83\x84\x89\x91\x92\x93\x94",
90 : /* also at these ends - Brackets and all kinds of begin characters */
91 : sImplEndSkipChars[] = "\"\')]}\x83\x84\x89\x91\x92\x93\x94";
92 :
93 : // These characters are allowed in words: (for FnCapitalStartSentence)
94 : static const sal_Char sImplWordChars[] = "-'";
95 :
96 : OUString EncryptBlockName_Imp(const OUString& rName);
97 :
98 0 : TYPEINIT0(SvxAutoCorrect)
99 :
100 : typedef SvxAutoCorrectLanguageLists* SvxAutoCorrectLanguageListsPtr;
101 :
102 470 : static inline bool IsWordDelim( const sal_Unicode c )
103 : {
104 417 : return ' ' == c || '\t' == c || 0x0a == c ||
105 887 : cNonBreakingSpace == c || 0x2011 == c || 0x1 == c;
106 : }
107 :
108 4 : static inline bool IsLowerLetter( sal_Int32 nCharType )
109 : {
110 8 : return CharClass::isLetterType( nCharType ) &&
111 8 : 0 == ( ::com::sun::star::i18n::KCharacterType::UPPER & nCharType);
112 : }
113 :
114 12 : static inline bool IsUpperLetter( sal_Int32 nCharType )
115 : {
116 24 : return CharClass::isLetterType( nCharType ) &&
117 24 : 0 == ( ::com::sun::star::i18n::KCharacterType::LOWER & nCharType);
118 : }
119 :
120 2 : bool lcl_IsUnsupportedUnicodeChar( CharClass& rCC, const OUString& rTxt,
121 : sal_Int32 nStt, sal_Int32 nEnd )
122 : {
123 20 : for( ; nStt < nEnd; ++nStt )
124 : {
125 18 : short nScript = rCC.getScript( rTxt, nStt );
126 18 : switch( nScript )
127 : {
128 : case ::com::sun::star::i18n::UnicodeScript_kCJKRadicalsSupplement:
129 : case ::com::sun::star::i18n::UnicodeScript_kHangulJamo:
130 : case ::com::sun::star::i18n::UnicodeScript_kCJKSymbolPunctuation:
131 : case ::com::sun::star::i18n::UnicodeScript_kHiragana:
132 : case ::com::sun::star::i18n::UnicodeScript_kKatakana:
133 : case ::com::sun::star::i18n::UnicodeScript_kHangulCompatibilityJamo:
134 : case ::com::sun::star::i18n::UnicodeScript_kEnclosedCJKLetterMonth:
135 : case ::com::sun::star::i18n::UnicodeScript_kCJKCompatibility:
136 : case ::com::sun::star::i18n::UnicodeScript_k_CJKUnifiedIdeographsExtensionA:
137 : case ::com::sun::star::i18n::UnicodeScript_kCJKUnifiedIdeograph:
138 : case ::com::sun::star::i18n::UnicodeScript_kHangulSyllable:
139 : case ::com::sun::star::i18n::UnicodeScript_kCJKCompatibilityIdeograph:
140 : case ::com::sun::star::i18n::UnicodeScript_kHalfwidthFullwidthForm:
141 0 : return true;
142 : default: ; //do nothing
143 : }
144 : }
145 2 : return false;
146 : }
147 :
148 5 : static bool lcl_IsSymbolChar( CharClass& rCC, const OUString& rTxt,
149 : sal_Int32 nStt, sal_Int32 nEnd )
150 : {
151 34 : for( ; nStt < nEnd; ++nStt )
152 : {
153 29 : if( ::com::sun::star::i18n::UnicodeType::PRIVATE_USE ==
154 29 : rCC.getType( rTxt, nStt ))
155 0 : return true;
156 : }
157 5 : return false;
158 : }
159 :
160 11 : static bool lcl_IsInAsciiArr( const sal_Char* pArr, const sal_Unicode c )
161 : {
162 11 : bool bRet = false;
163 111 : for( ; *pArr; ++pArr )
164 101 : if( *pArr == c )
165 : {
166 1 : bRet = true;
167 1 : break;
168 : }
169 11 : return bRet;
170 : }
171 :
172 6 : SvxAutoCorrDoc::~SvxAutoCorrDoc()
173 : {
174 6 : }
175 :
176 : // Called by the functions:
177 : // - FnCapitalStartWord
178 : // - FnCapitalStartSentence
179 : // after the exchange of characters. Then the words, if necessary, can be inserted
180 : // into the exception list.
181 4 : void SvxAutoCorrDoc::SaveCpltSttWord( sal_uLong, sal_Int32, const OUString&,
182 : sal_Unicode )
183 : {
184 4 : }
185 :
186 2 : LanguageType SvxAutoCorrDoc::GetLanguage( sal_Int32, bool ) const
187 : {
188 2 : return LANGUAGE_SYSTEM;
189 : }
190 :
191 62 : static const LanguageTag& GetAppLang()
192 : {
193 62 : return Application::GetSettings().GetLanguageTag();
194 : }
195 0 : static LocaleDataWrapper& GetLocaleDataWrapper( sal_uInt16 nLang )
196 : {
197 0 : static LocaleDataWrapper aLclDtWrp( GetAppLang() );
198 0 : LanguageTag aLcl( nLang );
199 0 : const LanguageTag& rLcl = aLclDtWrp.getLoadedLanguageTag();
200 0 : if( aLcl != rLcl )
201 0 : aLclDtWrp.setLanguageTag( aLcl );
202 0 : return aLclDtWrp;
203 : }
204 425 : static TransliterationWrapper& GetIgnoreTranslWrapper()
205 : {
206 : static int bIsInit = 0;
207 : static TransliterationWrapper aWrp( ::comphelper::getProcessComponentContext(),
208 : ::com::sun::star::i18n::TransliterationModules_IGNORE_KANA |
209 425 : ::com::sun::star::i18n::TransliterationModules_IGNORE_WIDTH );
210 425 : if( !bIsInit )
211 : {
212 1 : aWrp.loadModuleIfNeeded( GetAppLang().getLanguageType() );
213 1 : bIsInit = 1;
214 : }
215 425 : return aWrp;
216 : }
217 0 : static CollatorWrapper& GetCollatorWrapper()
218 : {
219 : static int bIsInit = 0;
220 0 : static CollatorWrapper aCollWrp( ::comphelper::getProcessComponentContext() );
221 0 : if( !bIsInit )
222 : {
223 0 : aCollWrp.loadDefaultCollator( GetAppLang().getLocale(), 0 );
224 0 : bIsInit = 1;
225 : }
226 0 : return aCollWrp;
227 : }
228 :
229 121 : static void lcl_ClearTable(boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists>& rLangTable)
230 : {
231 121 : rLangTable.clear();
232 121 : }
233 :
234 24 : bool SvxAutoCorrect::IsAutoCorrectChar( sal_Unicode cChar )
235 : {
236 24 : return cChar == '\0' || cChar == '\t' || cChar == 0x0a ||
237 24 : cChar == ' ' || cChar == '\'' || cChar == '\"' ||
238 24 : cChar == '*' || cChar == '_' || cChar == '%' ||
239 24 : cChar == '.' || cChar == ',' || cChar == ';' ||
240 24 : cChar == ':' || cChar == '?' || cChar == '!' ||
241 47 : cChar == '/' || cChar == '-';
242 : }
243 :
244 6 : bool SvxAutoCorrect::NeedsHardspaceAutocorr( sal_Unicode cChar )
245 : {
246 6 : return cChar == '%' || cChar == ';' || cChar == ':' || cChar == '?' || cChar == '!' ||
247 6 : cChar == '/' /*case for the urls exception*/;
248 : }
249 :
250 61 : long SvxAutoCorrect::GetDefaultFlags()
251 : {
252 : long nRet = Autocorrect
253 : | CapitalStartSentence
254 : | CapitalStartWord
255 : | ChgOrdinalNumber
256 : | ChgToEnEmDash
257 : | AddNonBrkSpace
258 : | ChgWeightUnderl
259 : | SetINetAttr
260 : | ChgQuotes
261 : | SaveWordCplSttLst
262 : | SaveWordWrdSttLst
263 61 : | CorrectCapsLock;
264 61 : LanguageType eLang = GetAppLang().getLanguageType();
265 61 : switch( eLang )
266 : {
267 : case LANGUAGE_ENGLISH:
268 : case LANGUAGE_ENGLISH_US:
269 : case LANGUAGE_ENGLISH_UK:
270 : case LANGUAGE_ENGLISH_AUS:
271 : case LANGUAGE_ENGLISH_CAN:
272 : case LANGUAGE_ENGLISH_NZ:
273 : case LANGUAGE_ENGLISH_EIRE:
274 : case LANGUAGE_ENGLISH_SAFRICA:
275 : case LANGUAGE_ENGLISH_JAMAICA:
276 : case LANGUAGE_ENGLISH_CARRIBEAN:
277 61 : nRet &= ~(ChgQuotes|ChgSglQuotes);
278 61 : break;
279 : }
280 61 : return nRet;
281 : }
282 :
283 :
284 61 : SvxAutoCorrect::SvxAutoCorrect( const OUString& rShareAutocorrFile,
285 : const OUString& rUserAutocorrFile )
286 : : sShareAutoCorrFile( rShareAutocorrFile )
287 : , sUserAutoCorrFile( rUserAutocorrFile )
288 61 : , pLangTable( new boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists> )
289 : , pCharClass( 0 )
290 : , bRunNext( false )
291 : , eCharClassLang( LANGUAGE_DONTKNOW )
292 61 : , nFlags(SvxAutoCorrect::GetDefaultFlags())
293 : , cStartDQuote( 0 )
294 : , cEndDQuote( 0 )
295 : , cStartSQuote( 0 )
296 : , cEndSQuote( 0 )
297 : , cEmDash( 0x2014 )
298 183 : , cEnDash( 0x2013)
299 : {
300 61 : }
301 :
302 60 : SvxAutoCorrect::SvxAutoCorrect( const SvxAutoCorrect& rCpy )
303 : : sShareAutoCorrFile( rCpy.sShareAutoCorrFile )
304 : , sUserAutoCorrFile( rCpy.sUserAutoCorrFile )
305 : , aSwFlags( rCpy.aSwFlags )
306 60 : , pLangTable( new boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists> )
307 : , pCharClass( 0 )
308 : , bRunNext( false )
309 : , eCharClassLang(rCpy.eCharClassLang)
310 60 : , nFlags( rCpy.nFlags & ~(ChgWordLstLoad|CplSttLstLoad|WrdSttLstLoad))
311 : , cStartDQuote( rCpy.cStartDQuote )
312 : , cEndDQuote( rCpy.cEndDQuote )
313 : , cStartSQuote( rCpy.cStartSQuote )
314 : , cEndSQuote( rCpy.cEndSQuote )
315 : , cEmDash( rCpy.cEmDash )
316 180 : , cEnDash( rCpy.cEnDash )
317 : {
318 60 : }
319 :
320 :
321 302 : SvxAutoCorrect::~SvxAutoCorrect()
322 : {
323 121 : lcl_ClearTable(*pLangTable);
324 121 : delete pLangTable;
325 121 : delete pCharClass;
326 181 : }
327 :
328 3 : void SvxAutoCorrect::_GetCharClass( LanguageType eLang )
329 : {
330 3 : delete pCharClass;
331 3 : pCharClass = new CharClass( LanguageTag( eLang));
332 3 : eCharClassLang = eLang;
333 3 : }
334 :
335 120 : void SvxAutoCorrect::SetAutoCorrFlag( long nFlag, bool bOn )
336 : {
337 120 : long nOld = nFlags;
338 60 : nFlags = bOn ? nFlags | nFlag
339 180 : : nFlags & ~nFlag;
340 :
341 120 : if( !bOn )
342 : {
343 60 : if( (nOld & CapitalStartSentence) != (nFlags & CapitalStartSentence) )
344 0 : nFlags &= ~CplSttLstLoad;
345 60 : if( (nOld & CapitalStartWord) != (nFlags & CapitalStartWord) )
346 0 : nFlags &= ~WrdSttLstLoad;
347 60 : if( (nOld & Autocorrect) != (nFlags & Autocorrect) )
348 0 : nFlags &= ~ChgWordLstLoad;
349 : }
350 120 : }
351 :
352 :
353 : // Two capital letters at the beginning of word?
354 2 : bool SvxAutoCorrect::FnCapitalStartWord( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
355 : sal_Int32 nSttPos, sal_Int32 nEndPos,
356 : LanguageType eLang )
357 : {
358 2 : bool bRet = false;
359 2 : CharClass& rCC = GetCharClass( eLang );
360 :
361 : // Delete all non alphanumeric. Test the characters at the beginning/end of
362 : // the word ( recognizes: "(min.", "/min.", and so on.)
363 2 : for( ; nSttPos < nEndPos; ++nSttPos )
364 2 : if( rCC.isLetterNumeric( rTxt, nSttPos ))
365 2 : break;
366 2 : for( ; nSttPos < nEndPos; --nEndPos )
367 2 : if( rCC.isLetterNumeric( rTxt, nEndPos - 1 ))
368 2 : break;
369 :
370 : // Is the word a compounded word separated by delimiters?
371 : // If so, keep track of all delimiters so each constituent
372 : // word can be checked for two initial capital letters.
373 2 : std::deque<sal_Int32> aDelimiters;
374 :
375 : // Always check for two capitals at the beginning
376 : // of the entire word, so start at nSttPos.
377 2 : aDelimiters.push_back(nSttPos);
378 :
379 : // Find all compound word delimiters
380 20 : for (sal_Int32 n = nSttPos; n < nEndPos; ++n)
381 : {
382 18 : if (IsAutoCorrectChar(rTxt[ n ]))
383 : {
384 2 : aDelimiters.push_back( n + 1 ); // Get position of char after delimiter
385 : }
386 : }
387 :
388 : // Decide where to put the terminating delimiter.
389 : // If the last AutoCorrect char was a newline, then the AutoCorrect
390 : // char will not be included in rTxt.
391 : // If the last AutoCorrect char was not a newline, then the AutoCorrect
392 : // character will be the last character in rTxt.
393 2 : if (!IsAutoCorrectChar(rTxt[nEndPos-1]))
394 2 : aDelimiters.push_back(nEndPos);
395 :
396 : // Iterate through the word and all words that compose it.
397 : // Two capital letters at the beginning of word?
398 6 : for (size_t nI = 0; nI < aDelimiters.size() - 1; ++nI)
399 : {
400 4 : nSttPos = aDelimiters[nI];
401 4 : nEndPos = aDelimiters[nI + 1];
402 :
403 12 : if( nSttPos+2 < nEndPos &&
404 12 : IsUpperLetter( rCC.getCharacterType( rTxt, nSttPos )) &&
405 12 : IsUpperLetter( rCC.getCharacterType( rTxt, ++nSttPos )) &&
406 : // Is the third character a lower case
407 8 : IsLowerLetter( rCC.getCharacterType( rTxt, nSttPos +1 )) &&
408 : // Do not replace special attributes
409 12 : 0x1 != rTxt[ nSttPos ] && 0x2 != rTxt[ nSttPos ])
410 : {
411 : // test if the word is in an exception list
412 4 : OUString sWord( rTxt.copy( nSttPos - 1, nEndPos - nSttPos + 1 ));
413 4 : if( !FindInWrdSttExceptList(eLang, sWord) )
414 : {
415 : // Check that word isn't correctly spelled before correcting:
416 : ::com::sun::star::uno::Reference<
417 : ::com::sun::star::linguistic2::XSpellChecker1 > xSpeller =
418 4 : SvxGetSpellChecker();
419 4 : if( xSpeller->hasLanguage(eLang) )
420 : {
421 0 : Sequence< ::com::sun::star::beans::PropertyValue > aEmptySeq;
422 0 : if (!xSpeller->spell(sWord, eLang, aEmptySeq).is())
423 : {
424 0 : return false;
425 0 : }
426 : }
427 4 : sal_Unicode cSave = rTxt[ nSttPos ];
428 8 : OUString sChar( cSave );
429 4 : sChar = rCC.lowercase( sChar );
430 4 : if( sChar[0] != cSave && rDoc.ReplaceRange( nSttPos, 1, sChar ))
431 : {
432 4 : if( SaveWordWrdSttLst & nFlags )
433 4 : rDoc.SaveCpltSttWord( CapitalStartWord, nSttPos, sWord, cSave );
434 4 : bRet = true;
435 4 : }
436 4 : }
437 : }
438 : }
439 2 : return bRet;
440 : }
441 :
442 :
443 2 : bool SvxAutoCorrect::FnChgOrdinalNumber(
444 : SvxAutoCorrDoc& rDoc, const OUString& rTxt,
445 : sal_Int32 nSttPos, sal_Int32 nEndPos,
446 : LanguageType eLang)
447 : {
448 : // 1st, 2nd, 3rd, 4 - 0th
449 : // 201th or 201st
450 : // 12th or 12nd
451 2 : bool bChg = false;
452 :
453 : // In some languages ordinal suffixes should never be
454 : // changed to superscript. Let's break for those languages.
455 2 : switch (eLang)
456 : {
457 : case LANGUAGE_SWEDISH:
458 : case LANGUAGE_SWEDISH_FINLAND:
459 0 : break;
460 : default:
461 2 : CharClass& rCC = GetCharClass(eLang);
462 :
463 2 : for (; nSttPos < nEndPos; ++nSttPos)
464 2 : if (!lcl_IsInAsciiArr(sImplSttSkipChars, rTxt[nSttPos]))
465 2 : break;
466 2 : for (; nSttPos < nEndPos; --nEndPos)
467 2 : if (!lcl_IsInAsciiArr(sImplEndSkipChars, rTxt[nEndPos - 1]))
468 2 : break;
469 :
470 :
471 : // Get the last number in the string to check
472 2 : sal_Int32 nNumEnd = nEndPos;
473 2 : bool foundEnd = false;
474 2 : bool validNumber = true;
475 2 : sal_Int32 i = nEndPos;
476 :
477 22 : while (i > nSttPos)
478 : {
479 18 : i--;
480 18 : bool isDigit = rCC.isDigit(rTxt, i);
481 18 : if (foundEnd)
482 0 : validNumber |= isDigit;
483 :
484 18 : if (isDigit && !foundEnd)
485 : {
486 0 : foundEnd = true;
487 0 : nNumEnd = i;
488 : }
489 : }
490 :
491 2 : if (foundEnd && validNumber) {
492 0 : sal_Int32 nNum = rTxt.copy(nSttPos, nNumEnd - nSttPos + 1).toInt32();
493 :
494 : // Check if the characters after that number correspond to the ordinal suffix
495 : uno::Reference< i18n::XOrdinalSuffix > xOrdSuffix
496 0 : = i18n::OrdinalSuffix::create(comphelper::getProcessComponentContext());
497 :
498 0 : uno::Sequence< OUString > aSuffixes = xOrdSuffix->getOrdinalSuffix(nNum, rCC.getLanguageTag().getLocale());
499 0 : for (sal_Int32 nSuff = 0; nSuff < aSuffixes.getLength(); nSuff++)
500 : {
501 0 : OUString sSuffix(aSuffixes[nSuff]);
502 0 : OUString sEnd = rTxt.copy(nNumEnd + 1, nEndPos - nNumEnd - 1);
503 :
504 0 : if (sSuffix == sEnd)
505 : {
506 : // Check if the ordinal suffix has to be set as super script
507 0 : if (rCC.isLetter(sSuffix))
508 : {
509 : // Do the change
510 : SvxEscapementItem aSvxEscapementItem(DFLT_ESC_AUTO_SUPER,
511 0 : DFLT_ESC_PROP, SID_ATTR_CHAR_ESCAPEMENT);
512 : rDoc.SetAttr(nNumEnd + 1, nEndPos,
513 : SID_ATTR_CHAR_ESCAPEMENT,
514 0 : aSvxEscapementItem);
515 0 : bChg = true;
516 : }
517 : }
518 0 : }
519 : }
520 : }
521 2 : return bChg;
522 : }
523 :
524 :
525 5 : bool SvxAutoCorrect::FnChgToEnEmDash(
526 : SvxAutoCorrDoc& rDoc, const OUString& rTxt,
527 : sal_Int32 nSttPos, sal_Int32 nEndPos,
528 : LanguageType eLang )
529 : {
530 5 : bool bRet = false;
531 5 : CharClass& rCC = GetCharClass( eLang );
532 5 : if (eLang == LANGUAGE_SYSTEM)
533 0 : eLang = GetAppLang().getLanguageType();
534 5 : bool bAlwaysUseEmDash = (cEmDash && (eLang == LANGUAGE_RUSSIAN || eLang == LANGUAGE_UKRAINIAN));
535 :
536 : // replace " - " or " --" with "enDash"
537 5 : if( cEnDash && 1 < nSttPos && 1 <= nEndPos - nSttPos )
538 : {
539 1 : sal_Unicode cCh = rTxt[ nSttPos ];
540 1 : if( '-' == cCh )
541 : {
542 0 : if( ' ' == rTxt[ nSttPos-1 ] &&
543 0 : '-' == rTxt[ nSttPos+1 ])
544 : {
545 : sal_Int32 n;
546 0 : for( n = nSttPos+2; n < nEndPos && lcl_IsInAsciiArr(
547 0 : sImplSttSkipChars,(cCh = rTxt[ n ]));
548 : ++n )
549 : ;
550 :
551 : // found: " --[<AnySttChars>][A-z0-9]
552 0 : if( rCC.isLetterNumeric( OUString(cCh) ) )
553 : {
554 0 : for( n = nSttPos-1; n && lcl_IsInAsciiArr(
555 0 : sImplEndSkipChars,(cCh = rTxt[ --n ])); )
556 : ;
557 :
558 : // found: "[A-z0-9][<AnyEndChars>] --[<AnySttChars>][A-z0-9]
559 0 : if( rCC.isLetterNumeric( OUString(cCh) ))
560 : {
561 0 : rDoc.Delete( nSttPos, nSttPos + 2 );
562 0 : rDoc.Insert( nSttPos, bAlwaysUseEmDash ? OUString(cEmDash) : OUString(cEnDash) );
563 0 : bRet = true;
564 : }
565 : }
566 : }
567 : }
568 2 : else if( 3 < nSttPos &&
569 2 : ' ' == rTxt[ nSttPos-1 ] &&
570 1 : '-' == rTxt[ nSttPos-2 ])
571 : {
572 0 : sal_Int32 n, nLen = 1, nTmpPos = nSttPos - 2;
573 0 : if( '-' == ( cCh = rTxt[ nTmpPos-1 ]) )
574 : {
575 0 : --nTmpPos;
576 0 : ++nLen;
577 0 : cCh = rTxt[ nTmpPos-1 ];
578 : }
579 0 : if( ' ' == cCh )
580 : {
581 0 : for( n = nSttPos; n < nEndPos && lcl_IsInAsciiArr(
582 0 : sImplSttSkipChars,(cCh = rTxt[ n ]));
583 : ++n )
584 : ;
585 :
586 : // found: " - [<AnySttChars>][A-z0-9]
587 0 : if( rCC.isLetterNumeric( OUString(cCh) ) )
588 : {
589 0 : cCh = ' ';
590 0 : for( n = nTmpPos-1; n && lcl_IsInAsciiArr(
591 0 : sImplEndSkipChars,(cCh = rTxt[ --n ])); )
592 : ;
593 : // found: "[A-z0-9][<AnyEndChars>] - [<AnySttChars>][A-z0-9]
594 0 : if( rCC.isLetterNumeric( OUString(cCh) ))
595 : {
596 0 : rDoc.Delete( nTmpPos, nTmpPos + nLen );
597 0 : rDoc.Insert( nTmpPos, bAlwaysUseEmDash ? OUString(cEmDash) : OUString(cEnDash) );
598 0 : bRet = true;
599 : }
600 : }
601 : }
602 : }
603 : }
604 :
605 : // Replace [A-z0-9]--[A-z0-9] double dash with "emDash" or "enDash"
606 : // [0-9]--[0-9] double dash always replaced with "enDash"
607 : // Finnish and Hungarian use enDash instead of emDash.
608 5 : bool bEnDash = (eLang == LANGUAGE_HUNGARIAN || eLang == LANGUAGE_FINNISH);
609 5 : if( ((cEmDash && !bEnDash) || (cEnDash && bEnDash)) && 4 <= nEndPos - nSttPos )
610 : {
611 4 : OUString sTmp( rTxt.copy( nSttPos, nEndPos - nSttPos ) );
612 4 : sal_Int32 nFndPos = sTmp.indexOf("--");
613 4 : if( nFndPos != -1 && nFndPos &&
614 0 : nFndPos + 2 < sTmp.getLength() &&
615 0 : ( rCC.isLetterNumeric( sTmp, nFndPos - 1 ) ||
616 4 : lcl_IsInAsciiArr( sImplEndSkipChars, rTxt[ nFndPos - 1 ] )) &&
617 0 : ( rCC.isLetterNumeric( sTmp, nFndPos + 2 ) ||
618 0 : lcl_IsInAsciiArr( sImplSttSkipChars, rTxt[ nFndPos + 2 ] )))
619 : {
620 0 : nSttPos = nSttPos + nFndPos;
621 0 : rDoc.Delete( nSttPos, nSttPos + 2 );
622 0 : rDoc.Insert( nSttPos, (bEnDash || (rCC.isDigit( sTmp, nFndPos - 1 ) &&
623 0 : rCC.isDigit( sTmp, nFndPos + 2 )) ? OUString(cEnDash) : OUString(cEmDash)) );
624 0 : bRet = true;
625 4 : }
626 : }
627 5 : return bRet;
628 : }
629 :
630 :
631 0 : bool SvxAutoCorrect::FnAddNonBrkSpace(
632 : SvxAutoCorrDoc& rDoc, const OUString& rTxt,
633 : sal_Int32, sal_Int32 nEndPos,
634 : LanguageType eLang )
635 : {
636 0 : bool bRet = false;
637 :
638 0 : CharClass& rCC = GetCharClass( eLang );
639 :
640 0 : if ( rCC.getLanguageTag().getLanguage() == "fr" )
641 : {
642 0 : bool bFrCA = (rCC.getLanguageTag().getCountry() == "CA");
643 0 : OUString allChars = ":;?!%";
644 0 : OUString chars( allChars );
645 0 : if ( bFrCA )
646 0 : chars = ":";
647 :
648 0 : sal_Unicode cChar = rTxt[ nEndPos ];
649 0 : bool bHasSpace = chars.indexOf( cChar ) != -1;
650 0 : bool bIsSpecial = allChars.indexOf( cChar ) != -1;
651 0 : if ( bIsSpecial )
652 : {
653 : // Get the last word delimiter position
654 0 : sal_Int32 nSttWdPos = nEndPos;
655 0 : bool bWasWordDelim = false;
656 0 : while( nSttWdPos && !(bWasWordDelim = IsWordDelim( rTxt[ --nSttWdPos ])))
657 : ;
658 :
659 : //See if the text is the start of a protocol string, e.g. have text of
660 : //"http" see if it is the start of "http:" and if so leave it alone
661 0 : sal_Int32 nIndex = nSttWdPos + (bWasWordDelim ? 1 : 0);
662 0 : sal_Int32 nProtocolLen = nEndPos - nSttWdPos + 1;
663 0 : if (nIndex + nProtocolLen <= rTxt.getLength())
664 : {
665 0 : if (INetURLObject::CompareProtocolScheme(rTxt.copy(nIndex, nProtocolLen)) != INetProtocol::NotValid)
666 0 : return false;
667 : }
668 :
669 : // Check the presence of "://" in the word
670 0 : sal_Int32 nStrPos = rTxt.indexOf( "://", nSttWdPos + 1 );
671 0 : if ( nStrPos == -1 && nEndPos > 0 )
672 : {
673 : // Check the previous char
674 0 : sal_Unicode cPrevChar = rTxt[ nEndPos - 1 ];
675 0 : if ( ( chars.indexOf( cPrevChar ) == -1 ) && cPrevChar != '\t' )
676 : {
677 : // Remove any previous normal space
678 0 : sal_Int32 nPos = nEndPos - 1;
679 0 : while ( cPrevChar == ' ' || cPrevChar == cNonBreakingSpace )
680 : {
681 0 : if ( nPos == 0 ) break;
682 0 : nPos--;
683 0 : cPrevChar = rTxt[ nPos ];
684 : }
685 :
686 0 : nPos++;
687 0 : if ( nEndPos - nPos > 0 )
688 0 : rDoc.Delete( nPos, nEndPos );
689 :
690 : // Add the non-breaking space at the end pos
691 0 : if ( bHasSpace )
692 0 : rDoc.Insert( nPos, OUString(cNonBreakingSpace) );
693 0 : bRunNext = true;
694 0 : bRet = true;
695 : }
696 0 : else if ( chars.indexOf( cPrevChar ) != -1 )
697 0 : bRunNext = true;
698 : }
699 : }
700 0 : else if ( cChar == '/' && nEndPos > 1 && rTxt.getLength() > (nEndPos - 1) )
701 : {
702 : // Remove the hardspace right before to avoid formatting URLs
703 0 : sal_Unicode cPrevChar = rTxt[ nEndPos - 1 ];
704 0 : sal_Unicode cMaybeSpaceChar = rTxt[ nEndPos - 2 ];
705 0 : if ( cPrevChar == ':' && cMaybeSpaceChar == cNonBreakingSpace )
706 : {
707 0 : rDoc.Delete( nEndPos - 2, nEndPos - 1 );
708 0 : bRet = true;
709 : }
710 0 : }
711 : }
712 :
713 0 : return bRet;
714 : }
715 :
716 :
717 2 : bool SvxAutoCorrect::FnSetINetAttr( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
718 : sal_Int32 nSttPos, sal_Int32 nEndPos,
719 : LanguageType eLang )
720 : {
721 : OUString sURL( URIHelper::FindFirstURLInText( rTxt, nSttPos, nEndPos,
722 2 : GetCharClass( eLang ) ));
723 2 : bool bRet = !sURL.isEmpty();
724 2 : if( bRet ) // also Attribut setzen:
725 0 : rDoc.SetINetAttr( nSttPos, nEndPos, sURL );
726 2 : return bRet;
727 : }
728 :
729 :
730 1 : bool SvxAutoCorrect::FnChgWeightUnderl( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
731 : sal_Int32 , sal_Int32 nEndPos,
732 : LanguageType eLang )
733 : {
734 : // Condition:
735 : // at the beginning: _ or * after Space with the folloeing !Space
736 : // at the end: _ or * before Space (word delimiter?)
737 :
738 1 : sal_Unicode c, cInsChar = rTxt[ nEndPos ]; // underline or bold
739 1 : if( ++nEndPos != rTxt.getLength() &&
740 0 : !IsWordDelim( rTxt[ nEndPos ] ) )
741 0 : return false;
742 :
743 1 : --nEndPos;
744 :
745 1 : bool bAlphaNum = false;
746 1 : sal_Int32 nPos = nEndPos;
747 1 : sal_Int32 nFndPos = -1;
748 1 : CharClass& rCC = GetCharClass( eLang );
749 :
750 6 : while( nPos )
751 : {
752 4 : switch( c = rTxt[ --nPos ] )
753 : {
754 : case '_':
755 : case '*':
756 1 : if( c == cInsChar )
757 : {
758 2 : if( bAlphaNum && nPos+1 < nEndPos && ( !nPos ||
759 2 : IsWordDelim( rTxt[ nPos-1 ])) &&
760 1 : !IsWordDelim( rTxt[ nPos+1 ]))
761 1 : nFndPos = nPos;
762 : else
763 : // Condition is not satisfied, so cancel
764 0 : nFndPos = -1;
765 1 : nPos = 0;
766 : }
767 1 : break;
768 : default:
769 3 : if( !bAlphaNum )
770 1 : bAlphaNum = rCC.isLetterNumeric( rTxt, nPos );
771 : }
772 : }
773 :
774 1 : if( -1 != nFndPos )
775 : {
776 : // first delete the Character at the end - this allows insertion
777 : // of an empty hint in SetAttr which would be removed by Delete
778 : // (fdo#62536, AUTOFMT in Writer)
779 1 : rDoc.Delete( nEndPos, nEndPos + 1 );
780 1 : rDoc.Delete( nFndPos, nFndPos + 1 );
781 : // Span the Attribute over the area
782 : // the end.
783 1 : if( '*' == cInsChar ) // Bold
784 : {
785 1 : SvxWeightItem aSvxWeightItem( WEIGHT_BOLD, SID_ATTR_CHAR_WEIGHT );
786 : rDoc.SetAttr( nFndPos, nEndPos - 1,
787 : SID_ATTR_CHAR_WEIGHT,
788 1 : aSvxWeightItem);
789 : }
790 : else // underline
791 : {
792 0 : SvxUnderlineItem aSvxUnderlineItem( UNDERLINE_SINGLE, SID_ATTR_CHAR_UNDERLINE );
793 : rDoc.SetAttr( nFndPos, nEndPos - 1,
794 : SID_ATTR_CHAR_UNDERLINE,
795 0 : aSvxUnderlineItem);
796 : }
797 : }
798 :
799 1 : return -1 != nFndPos;
800 : }
801 :
802 :
803 5 : bool SvxAutoCorrect::FnCapitalStartSentence( SvxAutoCorrDoc& rDoc,
804 : const OUString& rTxt, bool bNormalPos,
805 : sal_Int32 nSttPos, sal_Int32 nEndPos,
806 : LanguageType eLang )
807 : {
808 :
809 5 : if( rTxt.isEmpty() || nEndPos <= nSttPos )
810 1 : return false;
811 :
812 4 : CharClass& rCC = GetCharClass( eLang );
813 4 : OUString aText( rTxt );
814 4 : const sal_Unicode *pStart = aText.getStr(),
815 4 : *pStr = pStart + nEndPos,
816 4 : *pWordStt = 0,
817 4 : *pDelim = 0;
818 :
819 4 : bool bAtStart = false;
820 20 : do {
821 22 : --pStr;
822 22 : if (rCC.isLetter(aText, pStr - pStart))
823 : {
824 19 : if( !pWordStt )
825 4 : pDelim = pStr+1;
826 19 : pWordStt = pStr;
827 : }
828 3 : else if (pWordStt && !rCC.isDigit(aText, pStr - pStart))
829 : {
830 7 : if( lcl_IsInAsciiArr( sImplWordChars, *pStr ) &&
831 2 : pWordStt - 1 == pStr &&
832 : // Installation at beginning of paragraph. Replaced < by <= (#i38971#)
833 5 : (pStart + 1) <= pStr &&
834 1 : rCC.isLetter(aText, pStr-1 - pStart))
835 1 : pWordStt = --pStr;
836 : else
837 2 : break;
838 : }
839 : } while( ! ( bAtStart = (pStart == pStr) ) );
840 :
841 4 : if (!pWordStt)
842 0 : return false; // no character to be replaced
843 :
844 :
845 4 : if (rCC.isDigit(aText, pStr - pStart))
846 0 : return false; // already ok
847 :
848 4 : if (IsUpperLetter(rCC.getCharacterType(aText, pWordStt - pStart)))
849 3 : return false; // already ok
850 :
851 : //See if the text is the start of a protocol string, e.g. have text of
852 : //"http" see if it is the start of "http:" and if so leave it alone
853 1 : sal_Int32 nIndex = pWordStt - pStart;
854 1 : sal_Int32 nProtocolLen = pDelim - pWordStt + 1;
855 1 : if (nIndex + nProtocolLen <= rTxt.getLength())
856 : {
857 1 : if (INetURLObject::CompareProtocolScheme(rTxt.copy(nIndex, nProtocolLen)) != INetProtocol::NotValid)
858 0 : return false; // already ok
859 : }
860 :
861 1 : if (0x1 == *pWordStt || 0x2 == *pWordStt)
862 0 : return false; // already ok
863 :
864 1 : if( *pDelim && 2 >= pDelim - pWordStt &&
865 0 : lcl_IsInAsciiArr( ".-)>", *pDelim ) )
866 0 : return false;
867 :
868 1 : if( !bAtStart ) // Still no beginning of a paragraph?
869 : {
870 0 : if ( IsWordDelim( *pStr ) )
871 : {
872 0 : while( ! ( bAtStart = (pStart == pStr--) ) && IsWordDelim( *pStr ) )
873 : ;
874 : }
875 : // Asian full stop, full width full stop, full width exclamation mark
876 : // and full width question marks are treated as word delimiters
877 0 : else if ( 0x3002 != *pStr && 0xFF0E != *pStr && 0xFF01 != *pStr &&
878 0 : 0xFF1F != *pStr )
879 0 : return false; // no valid separator -> no replacement
880 : }
881 :
882 1 : if( bAtStart ) // at the beginning of a paragraph?
883 : {
884 : // Check out the previous paragraph, if it exists.
885 : // If so, then check to paragraph separator at the end.
886 1 : OUString const*const pPrevPara = rDoc.GetPrevPara(bNormalPos);
887 1 : if (!pPrevPara)
888 : {
889 : // valid separator -> replace
890 1 : OUString sChar( *pWordStt );
891 1 : sChar = rCC.titlecase(sChar); //see fdo#56740
892 2 : return !comphelper::string::equals(sChar, *pWordStt) &&
893 2 : rDoc.ReplaceRange( pWordStt - pStart, 1, sChar );
894 : }
895 :
896 0 : aText = *pPrevPara;
897 0 : bAtStart = false;
898 0 : pStart = aText.getStr();
899 0 : pStr = pStart + aText.getLength();
900 :
901 0 : do { // overwrite all blanks
902 0 : --pStr;
903 0 : if( !IsWordDelim( *pStr ))
904 0 : break;
905 : } while( ! ( bAtStart = (pStart == pStr) ) );
906 :
907 0 : if( bAtStart )
908 0 : return false; // no valid separator -> no replacement
909 : }
910 :
911 : // Found [ \t]+[A-Z0-9]+ until here. Test now on the paragraph separator.
912 : // all three can happen, but not more than once!
913 0 : const sal_Unicode* pExceptStt = 0;
914 0 : if( !bAtStart )
915 : {
916 0 : bool bContinue = true;
917 0 : int nFlag = C_NONE;
918 0 : do {
919 0 : switch( *pStr )
920 : {
921 : // Western and Asian full stop
922 : case '.':
923 : case 0x3002 :
924 : case 0xFF0E :
925 : {
926 0 : if (pStr >= pStart + 2 && *(pStr-2) == '.')
927 : {
928 : //e.g. text "f.o.o. word": Now currently considering
929 : //capitalizing word but second last character of
930 : //previous word is a . So probably last word is an
931 : //anagram that ends in . and not truly the end of a
932 : //previous sentence, so don't autocapitalize this word
933 0 : return false;
934 : }
935 0 : if( nFlag & C_FULL_STOP )
936 0 : return false; // no valid separator -> no replacement
937 0 : nFlag |= C_FULL_STOP;
938 0 : pExceptStt = pStr;
939 : }
940 0 : break;
941 : case '!':
942 : case 0xFF01 :
943 : {
944 0 : if( nFlag & C_EXCLAMATION_MARK )
945 0 : return false; // no valid separator -> no replacement
946 0 : nFlag |= C_EXCLAMATION_MARK;
947 : }
948 0 : break;
949 : case '?':
950 : case 0xFF1F :
951 : {
952 0 : if( nFlag & C_QUESTION_MARK)
953 0 : return false; // no valid separator -> no replacement
954 0 : nFlag |= C_QUESTION_MARK;
955 : }
956 0 : break;
957 : default:
958 0 : if( !nFlag )
959 0 : return false; // no valid separator -> no replacement
960 : else
961 0 : bContinue = false;
962 0 : break;
963 : }
964 :
965 0 : if( bContinue && pStr-- == pStart )
966 : {
967 0 : return false; // no valid separator -> no replacement
968 : }
969 : } while( bContinue );
970 0 : if( C_FULL_STOP != nFlag )
971 0 : pExceptStt = 0;
972 : }
973 :
974 0 : if( 2 > ( pStr - pStart ) )
975 0 : return false;
976 :
977 0 : if (!rCC.isLetterNumeric(aText, pStr-- - pStart))
978 : {
979 0 : bool bValid = false, bAlphaFnd = false;
980 0 : const sal_Unicode* pTmpStr = pStr;
981 0 : while( !bValid )
982 : {
983 0 : if( rCC.isDigit( aText, pTmpStr - pStart ) )
984 : {
985 0 : bValid = true;
986 0 : pStr = pTmpStr - 1;
987 : }
988 0 : else if( rCC.isLetter( aText, pTmpStr - pStart ) )
989 : {
990 0 : if( bAlphaFnd )
991 : {
992 0 : bValid = true;
993 0 : pStr = pTmpStr;
994 : }
995 : else
996 0 : bAlphaFnd = true;
997 : }
998 0 : else if( bAlphaFnd || IsWordDelim( *pTmpStr ) )
999 0 : break;
1000 :
1001 0 : if( pTmpStr == pStart )
1002 0 : break;
1003 :
1004 0 : --pTmpStr;
1005 : }
1006 :
1007 0 : if( !bValid )
1008 0 : return false; // no valid separator -> no replacement
1009 : }
1010 :
1011 0 : bool bNumericOnly = '0' <= *(pStr+1) && *(pStr+1) <= '9';
1012 :
1013 : // Search for the beginning of the word
1014 0 : while( !IsWordDelim( *pStr ))
1015 : {
1016 0 : if( bNumericOnly && rCC.isLetter( aText, pStr - pStart ) )
1017 0 : bNumericOnly = false;
1018 :
1019 0 : if( pStart == pStr )
1020 0 : break;
1021 :
1022 0 : --pStr;
1023 : }
1024 :
1025 0 : if( bNumericOnly ) // consists of only numbers, then not
1026 0 : return false;
1027 :
1028 0 : if( IsWordDelim( *pStr ))
1029 0 : ++pStr;
1030 :
1031 0 : OUString sWord;
1032 :
1033 : // check on the basis of the exception list
1034 0 : if( pExceptStt )
1035 : {
1036 0 : sWord = OUString(pStr, pExceptStt - pStr + 1);
1037 0 : if( FindInCplSttExceptList(eLang, sWord) )
1038 0 : return false;
1039 :
1040 : // Delete all non alphanumeric. Test the characters at the
1041 : // beginning/end of the word ( recognizes: "(min.", "/min.", and so on.)
1042 0 : OUString sTmp( sWord );
1043 0 : while( !sTmp.isEmpty() &&
1044 0 : !rCC.isLetterNumeric( sTmp, 0 ) )
1045 0 : sTmp = sTmp.copy(1);
1046 :
1047 : // Remove all non alphanumeric characters towards the end up until
1048 : // the last one.
1049 0 : sal_Int32 nLen = sTmp.getLength();
1050 0 : while( nLen && !rCC.isLetterNumeric( sTmp, nLen-1 ) )
1051 0 : --nLen;
1052 0 : if( nLen + 1 < sTmp.getLength() )
1053 0 : sTmp = sTmp.copy( 0, nLen + 1 );
1054 :
1055 0 : if( !sTmp.isEmpty() && sTmp.getLength() != sWord.getLength() &&
1056 0 : FindInCplSttExceptList(eLang, sTmp))
1057 0 : return false;
1058 :
1059 0 : if(FindInCplSttExceptList(eLang, sWord, true))
1060 0 : return false;
1061 : }
1062 :
1063 : // Ok, then replace
1064 0 : sal_Unicode cSave = *pWordStt;
1065 0 : nSttPos = pWordStt - rTxt.getStr();
1066 0 : OUString sChar( cSave );
1067 0 : sChar = rCC.titlecase(sChar); //see fdo#56740
1068 0 : bool bRet = sChar[0] != cSave && rDoc.ReplaceRange( nSttPos, 1, sChar );
1069 :
1070 : // Parahaps someone wants to have the word
1071 0 : if( bRet && SaveWordCplSttLst & nFlags )
1072 0 : rDoc.SaveCpltSttWord( CapitalStartSentence, nSttPos, sWord, cSave );
1073 :
1074 0 : return bRet;
1075 : }
1076 :
1077 0 : bool SvxAutoCorrect::FnCorrectCapsLock( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
1078 : sal_Int32 nSttPos, sal_Int32 nEndPos,
1079 : LanguageType eLang )
1080 : {
1081 0 : if (nEndPos - nSttPos < 2)
1082 : // string must be at least 2-character long.
1083 0 : return false;
1084 :
1085 0 : CharClass& rCC = GetCharClass( eLang );
1086 :
1087 : // Check the first 2 letters.
1088 0 : if ( !IsLowerLetter(rCC.getCharacterType(rTxt, nSttPos)) )
1089 0 : return false;
1090 :
1091 0 : if ( !IsUpperLetter(rCC.getCharacterType(rTxt, nSttPos+1)) )
1092 0 : return false;
1093 :
1094 0 : OUString aConverted;
1095 0 : aConverted += rCC.uppercase(OUString(rTxt[nSttPos]));
1096 0 : aConverted += rCC.lowercase(OUString(rTxt[nSttPos+1]));
1097 :
1098 0 : for( sal_Int32 i = nSttPos+2; i < nEndPos; ++i )
1099 : {
1100 0 : if ( IsLowerLetter(rCC.getCharacterType(rTxt, i)) )
1101 : // A lowercase letter disqualifies the whole text.
1102 0 : return false;
1103 :
1104 0 : if ( IsUpperLetter(rCC.getCharacterType(rTxt, i)) )
1105 : // Another uppercase letter. Convert it.
1106 0 : aConverted += rCC.lowercase(OUString(rTxt[i]));
1107 : else
1108 : // This is not an alphabetic letter. Leave it as-is.
1109 0 : aConverted += OUString( rTxt[i] );
1110 : }
1111 :
1112 : // Replace the word.
1113 0 : rDoc.Delete(nSttPos, nEndPos);
1114 0 : rDoc.Insert(nSttPos, aConverted);
1115 :
1116 0 : return true;
1117 : }
1118 :
1119 :
1120 0 : sal_Unicode SvxAutoCorrect::GetQuote( sal_Unicode cInsChar, bool bSttQuote,
1121 : LanguageType eLang ) const
1122 : {
1123 : sal_Unicode cRet = bSttQuote ? ( '\"' == cInsChar
1124 : ? GetStartDoubleQuote()
1125 : : GetStartSingleQuote() )
1126 : : ( '\"' == cInsChar
1127 : ? GetEndDoubleQuote()
1128 0 : : GetEndSingleQuote() );
1129 0 : if( !cRet )
1130 : {
1131 : // then through the Language find the right character
1132 0 : if( LANGUAGE_NONE == eLang )
1133 0 : cRet = cInsChar;
1134 : else
1135 : {
1136 0 : LocaleDataWrapper& rLcl = GetLocaleDataWrapper( eLang );
1137 : OUString sRet( bSttQuote
1138 : ? ( '\"' == cInsChar
1139 : ? rLcl.getDoubleQuotationMarkStart()
1140 : : rLcl.getQuotationMarkStart() )
1141 : : ( '\"' == cInsChar
1142 : ? rLcl.getDoubleQuotationMarkEnd()
1143 0 : : rLcl.getQuotationMarkEnd() ));
1144 0 : cRet = !sRet.isEmpty() ? sRet[0] : cInsChar;
1145 : }
1146 : }
1147 0 : return cRet;
1148 : }
1149 :
1150 0 : void SvxAutoCorrect::InsertQuote( SvxAutoCorrDoc& rDoc, sal_Int32 nInsPos,
1151 : sal_Unicode cInsChar, bool bSttQuote,
1152 : bool bIns )
1153 : {
1154 0 : LanguageType eLang = rDoc.GetLanguage( nInsPos, false );
1155 0 : sal_Unicode cRet = GetQuote( cInsChar, bSttQuote, eLang );
1156 :
1157 0 : OUString sChg( cInsChar );
1158 0 : if( bIns )
1159 0 : rDoc.Insert( nInsPos, sChg );
1160 : else
1161 0 : rDoc.Replace( nInsPos, sChg );
1162 :
1163 0 : sChg = OUString(cRet);
1164 :
1165 0 : if( '\"' == cInsChar )
1166 : {
1167 0 : if( LANGUAGE_SYSTEM == eLang )
1168 0 : eLang = GetAppLang().getLanguageType();
1169 0 : switch( eLang )
1170 : {
1171 : case LANGUAGE_FRENCH:
1172 : case LANGUAGE_FRENCH_BELGIAN:
1173 : case LANGUAGE_FRENCH_CANADIAN:
1174 : case LANGUAGE_FRENCH_SWISS:
1175 : case LANGUAGE_FRENCH_LUXEMBOURG:
1176 : {
1177 0 : OUString s( cNonBreakingSpace );
1178 : // UNICODE code for no break space
1179 0 : if( rDoc.Insert( bSttQuote ? nInsPos+1 : nInsPos, s ))
1180 : {
1181 0 : if( !bSttQuote )
1182 0 : ++nInsPos;
1183 0 : }
1184 : }
1185 0 : break;
1186 : }
1187 : }
1188 :
1189 0 : rDoc.Replace( nInsPos, sChg );
1190 0 : }
1191 :
1192 0 : OUString SvxAutoCorrect::GetQuote( SvxAutoCorrDoc& rDoc, sal_Int32 nInsPos,
1193 : sal_Unicode cInsChar, bool bSttQuote )
1194 : {
1195 0 : LanguageType eLang = rDoc.GetLanguage( nInsPos, false );
1196 0 : sal_Unicode cRet = GetQuote( cInsChar, bSttQuote, eLang );
1197 :
1198 0 : OUString sRet = OUString(cRet);
1199 :
1200 0 : if( '\"' == cInsChar )
1201 : {
1202 0 : if( LANGUAGE_SYSTEM == eLang )
1203 0 : eLang = GetAppLang().getLanguageType();
1204 0 : switch( eLang )
1205 : {
1206 : case LANGUAGE_FRENCH:
1207 : case LANGUAGE_FRENCH_BELGIAN:
1208 : case LANGUAGE_FRENCH_CANADIAN:
1209 : case LANGUAGE_FRENCH_SWISS:
1210 : case LANGUAGE_FRENCH_LUXEMBOURG:
1211 0 : if( bSttQuote )
1212 0 : sRet += " ";
1213 : else
1214 0 : sRet = " " + sRet;
1215 0 : break;
1216 : }
1217 : }
1218 0 : return sRet;
1219 : }
1220 :
1221 : sal_uLong
1222 6 : SvxAutoCorrect::DoAutoCorrect( SvxAutoCorrDoc& rDoc, const OUString& rTxt,
1223 : sal_Int32 nInsPos, sal_Unicode cChar,
1224 : bool bInsert, vcl::Window* pFrameWin )
1225 : {
1226 6 : sal_uLong nRet = 0;
1227 6 : bool bIsNextRun = bRunNext;
1228 6 : bRunNext = false; // if it was set, then it has to be turned off
1229 :
1230 : do{ // only for middle check loop !!
1231 6 : if( cChar )
1232 : {
1233 : // Prevent double space
1234 17 : if( nInsPos && ' ' == cChar &&
1235 11 : IsAutoCorrFlag( IgnoreDoubleSpace ) &&
1236 0 : ' ' == rTxt[ nInsPos - 1 ])
1237 : {
1238 0 : nRet = IgnoreDoubleSpace;
1239 4 : break;
1240 : }
1241 :
1242 6 : bool bSingle = '\'' == cChar;
1243 : bool bIsReplaceQuote =
1244 12 : (IsAutoCorrFlag( ChgQuotes ) && ('\"' == cChar )) ||
1245 12 : (IsAutoCorrFlag( ChgSglQuotes ) && bSingle );
1246 6 : if( bIsReplaceQuote )
1247 : {
1248 : sal_Unicode cPrev;
1249 0 : bool bSttQuote = !nInsPos ||
1250 0 : IsWordDelim( ( cPrev = rTxt[ nInsPos-1 ])) ||
1251 0 : lcl_IsInAsciiArr( "([{", cPrev ) ||
1252 0 : ( cEmDash && cEmDash == cPrev ) ||
1253 0 : ( cEnDash && cEnDash == cPrev );
1254 :
1255 0 : InsertQuote( rDoc, nInsPos, cChar, bSttQuote, bInsert );
1256 0 : nRet = bSingle ? ChgSglQuotes : ChgQuotes;
1257 0 : break;
1258 : }
1259 :
1260 6 : if( bInsert )
1261 6 : rDoc.Insert( nInsPos, OUString(cChar) );
1262 : else
1263 0 : rDoc.Replace( nInsPos, OUString(cChar) );
1264 :
1265 : // Hardspaces autocorrection
1266 6 : if ( IsAutoCorrFlag( AddNonBrkSpace ) )
1267 : {
1268 6 : if ( NeedsHardspaceAutocorr( cChar ) &&
1269 0 : FnAddNonBrkSpace( rDoc, rTxt, 0, nInsPos, rDoc.GetLanguage( nInsPos, false ) ) )
1270 : {
1271 0 : nRet = AddNonBrkSpace;
1272 : }
1273 6 : else if ( bIsNextRun && !IsAutoCorrectChar( cChar ) )
1274 : {
1275 : // Remove the NBSP if it wasn't an autocorrection
1276 0 : if ( nInsPos != 0 && NeedsHardspaceAutocorr( rTxt[ nInsPos - 1 ] ) &&
1277 0 : cChar != ' ' && cChar != '\t' && cChar != cNonBreakingSpace )
1278 : {
1279 : // Look for the last HARD_SPACE
1280 0 : sal_Int32 nPos = nInsPos - 1;
1281 0 : bool bContinue = true;
1282 0 : while ( bContinue )
1283 : {
1284 0 : const sal_Unicode cTmpChar = rTxt[ nPos ];
1285 0 : if ( cTmpChar == cNonBreakingSpace )
1286 : {
1287 0 : rDoc.Delete( nPos, nPos + 1 );
1288 0 : nRet = AddNonBrkSpace;
1289 0 : bContinue = false;
1290 : }
1291 0 : else if ( !NeedsHardspaceAutocorr( cTmpChar ) || nPos == 0 )
1292 0 : bContinue = false;
1293 0 : nPos--;
1294 : }
1295 : }
1296 : }
1297 : }
1298 : }
1299 :
1300 6 : if( !nInsPos )
1301 0 : break;
1302 :
1303 6 : sal_Int32 nPos = nInsPos - 1;
1304 :
1305 6 : if( IsWordDelim( rTxt[ nPos ]))
1306 0 : break;
1307 :
1308 : // Set bold or underline automatically?
1309 6 : if (('*' == cChar || '_' == cChar) && (nPos+1 < rTxt.getLength()))
1310 : {
1311 2 : if( IsAutoCorrFlag( ChgWeightUnderl ) &&
1312 1 : FnChgWeightUnderl( rDoc, rTxt, 0, nPos+1 ) )
1313 1 : nRet = ChgWeightUnderl;
1314 1 : break;
1315 : }
1316 :
1317 5 : while( nPos && !IsWordDelim( rTxt[ --nPos ]))
1318 : ;
1319 :
1320 : // Found a Paragraph-start or a Blank, search for the word shortcut in
1321 : // auto.
1322 5 : sal_Int32 nCapLttrPos = nPos+1; // on the 1st Character
1323 5 : if( !nPos && !IsWordDelim( rTxt[ 0 ]))
1324 3 : --nCapLttrPos; // Absatz Anfang und kein Blank !
1325 :
1326 5 : LanguageType eLang = rDoc.GetLanguage( nCapLttrPos, false );
1327 5 : if( LANGUAGE_SYSTEM == eLang )
1328 2 : eLang = MsLangId::getSystemLanguage();
1329 5 : CharClass& rCC = GetCharClass( eLang );
1330 :
1331 : // no symbol characters
1332 5 : if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nInsPos ))
1333 0 : break;
1334 :
1335 5 : if( IsAutoCorrFlag( Autocorrect ) )
1336 : {
1337 5 : OUString aPara;
1338 5 : OUString* pPara = IsAutoCorrFlag(CapitalStartSentence) ? &aPara : 0;
1339 :
1340 : // since LibO 4.1, '-' is a word separator
1341 : // fdo#67742 avoid "--" to be replaced by "–" if next is "-"
1342 5 : if( rTxt.endsWith( "---" ) )
1343 0 : break;
1344 : bool bChgWord = rDoc.ChgAutoCorrWord( nCapLttrPos, nInsPos,
1345 5 : *this, pPara );
1346 5 : if( !bChgWord )
1347 : {
1348 2 : sal_Int32 nCapLttrPos1 = nCapLttrPos, nInsPos1 = nInsPos;
1349 6 : while( nCapLttrPos1 < nInsPos &&
1350 2 : lcl_IsInAsciiArr( sImplSttSkipChars, rTxt[ nCapLttrPos1 ] )
1351 : )
1352 0 : ++nCapLttrPos1;
1353 6 : while( nCapLttrPos1 < nInsPos1 && nInsPos1 &&
1354 2 : lcl_IsInAsciiArr( sImplEndSkipChars, rTxt[ nInsPos1-1 ] )
1355 : )
1356 0 : --nInsPos1;
1357 :
1358 6 : if( (nCapLttrPos1 != nCapLttrPos || nInsPos1 != nInsPos ) &&
1359 2 : nCapLttrPos1 < nInsPos1 &&
1360 0 : rDoc.ChgAutoCorrWord( nCapLttrPos1, nInsPos1, *this, pPara ))
1361 : {
1362 0 : bChgWord = true;
1363 0 : nCapLttrPos = nCapLttrPos1;
1364 : }
1365 : }
1366 :
1367 5 : if( bChgWord )
1368 : {
1369 3 : nRet = Autocorrect;
1370 3 : if( !aPara.isEmpty() )
1371 : {
1372 3 : sal_Int32 nEnd = nCapLttrPos;
1373 24 : while( nEnd < aPara.getLength() &&
1374 10 : !IsWordDelim( aPara[ nEnd ]))
1375 8 : ++nEnd;
1376 :
1377 : // Capital letter at beginning of paragraph?
1378 6 : if( IsAutoCorrFlag( CapitalStartSentence ) &&
1379 : FnCapitalStartSentence( rDoc, aPara, false,
1380 3 : nCapLttrPos, nEnd, eLang ) )
1381 1 : nRet |= CapitalStartSentence;
1382 :
1383 6 : if( IsAutoCorrFlag( ChgToEnEmDash ) &&
1384 3 : FnChgToEnEmDash( rDoc, rTxt, nCapLttrPos, nEnd, eLang ) )
1385 0 : nRet |= ChgToEnEmDash;
1386 : }
1387 3 : break;
1388 2 : }
1389 : }
1390 :
1391 6 : if( ( IsAutoCorrFlag( nRet = ChgOrdinalNumber ) &&
1392 2 : (nInsPos >= 2 ) && // fdo#69762 avoid autocorrect for 2e-3
1393 2 : ( '-' != cChar || 'E' != toupper(rTxt[nInsPos-1]) || '0' > rTxt[nInsPos-2] || '9' < rTxt[nInsPos-2] ) &&
1394 6 : FnChgOrdinalNumber( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) ||
1395 4 : ( IsAutoCorrFlag( nRet = SetINetAttr ) &&
1396 2 : ( ' ' == cChar || '\t' == cChar || 0x0a == cChar || !cChar ) &&
1397 2 : FnSetINetAttr( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) )
1398 : ;
1399 : else
1400 : {
1401 2 : bool bLockKeyOn = pFrameWin && (pFrameWin->GetIndicatorState() & KeyIndicatorState::CAPSLOCK);
1402 2 : bool bUnsupported = lcl_IsUnsupportedUnicodeChar( rCC, rTxt, nCapLttrPos, nInsPos );
1403 :
1404 2 : nRet = 0;
1405 2 : if ( bLockKeyOn && IsAutoCorrFlag( CorrectCapsLock ) &&
1406 0 : FnCorrectCapsLock( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
1407 : {
1408 : // Correct accidental use of cAPS LOCK key (do this only when
1409 : // the caps or shift lock key is pressed). Turn off the caps
1410 : // lock afterwords.
1411 0 : nRet |= CorrectCapsLock;
1412 0 : pFrameWin->SimulateKeyPress( KEY_CAPSLOCK );
1413 : }
1414 :
1415 : // Capital letter at beginning of paragraph ?
1416 6 : if( !bUnsupported &&
1417 4 : IsAutoCorrFlag( CapitalStartSentence ) &&
1418 2 : FnCapitalStartSentence( rDoc, rTxt, true, nCapLttrPos, nInsPos, eLang ) )
1419 0 : nRet |= CapitalStartSentence;
1420 :
1421 : // Two capital letters at beginning of word ??
1422 6 : if( !bUnsupported &&
1423 4 : IsAutoCorrFlag( CapitalStartWord ) &&
1424 2 : FnCapitalStartWord( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
1425 2 : nRet |= CapitalStartWord;
1426 :
1427 4 : if( IsAutoCorrFlag( ChgToEnEmDash ) &&
1428 2 : FnChgToEnEmDash( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
1429 0 : nRet |= ChgToEnEmDash;
1430 : }
1431 :
1432 : } while( false );
1433 :
1434 6 : return nRet;
1435 : }
1436 :
1437 0 : SvxAutoCorrectLanguageLists& SvxAutoCorrect::_GetLanguageList(
1438 : LanguageType eLang )
1439 : {
1440 0 : LanguageTag aLanguageTag( eLang);
1441 0 : if (pLangTable->find(aLanguageTag) == pLangTable->end())
1442 0 : (void)CreateLanguageFile(aLanguageTag, true);
1443 0 : return *(pLangTable->find(aLanguageTag)->second);
1444 : }
1445 :
1446 0 : void SvxAutoCorrect::SaveCplSttExceptList( LanguageType eLang )
1447 : {
1448 0 : boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(LanguageTag(eLang));
1449 0 : if(nTmpVal != pLangTable->end() && nTmpVal->second)
1450 0 : nTmpVal->second->SaveCplSttExceptList();
1451 : #ifdef DBG_UTIL
1452 : else
1453 : {
1454 : SAL_WARN("editeng", "Save an empty list? ");
1455 : }
1456 : #endif
1457 0 : }
1458 :
1459 0 : void SvxAutoCorrect::SaveWrdSttExceptList(LanguageType eLang)
1460 : {
1461 0 : boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(LanguageTag(eLang));
1462 0 : if(nTmpVal != pLangTable->end() && nTmpVal->second)
1463 0 : nTmpVal->second->SaveWrdSttExceptList();
1464 : #ifdef DBG_UTIL
1465 : else
1466 : {
1467 : SAL_WARN("editeng", "Save an empty list? ");
1468 : }
1469 : #endif
1470 0 : }
1471 :
1472 : // Adds a single word. The list will immediately be written to the file!
1473 0 : bool SvxAutoCorrect::AddCplSttException( const OUString& rNew,
1474 : LanguageType eLang )
1475 : {
1476 0 : SvxAutoCorrectLanguageLists* pLists = 0;
1477 : // either the right language is present or it will be this in the general list
1478 0 : boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(LanguageTag(eLang));
1479 0 : if(nTmpVal != pLangTable->end())
1480 0 : pLists = nTmpVal->second;
1481 : else
1482 : {
1483 0 : LanguageTag aLangTagUndetermined( LANGUAGE_UNDETERMINED);
1484 0 : nTmpVal = pLangTable->find(aLangTagUndetermined);
1485 0 : if(nTmpVal != pLangTable->end())
1486 0 : pLists = nTmpVal->second;
1487 0 : else if(CreateLanguageFile(aLangTagUndetermined, true))
1488 0 : pLists = pLangTable->find(aLangTagUndetermined)->second;
1489 : }
1490 : OSL_ENSURE(pLists, "No auto correction data");
1491 0 : return pLists && pLists->AddToCplSttExceptList(rNew);
1492 : }
1493 :
1494 : // Adds a single word. The list will immediately be written to the file!
1495 0 : bool SvxAutoCorrect::AddWrtSttException( const OUString& rNew,
1496 : LanguageType eLang )
1497 : {
1498 0 : SvxAutoCorrectLanguageLists* pLists = 0;
1499 : //either the right language is present or it is set in the general list
1500 0 : boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(LanguageTag(eLang));
1501 0 : if(nTmpVal != pLangTable->end())
1502 0 : pLists = nTmpVal->second;
1503 : else
1504 : {
1505 0 : LanguageTag aLangTagUndetermined( LANGUAGE_UNDETERMINED);
1506 0 : nTmpVal = pLangTable->find(aLangTagUndetermined);
1507 0 : if(nTmpVal != pLangTable->end())
1508 0 : pLists = nTmpVal->second;
1509 0 : else if(CreateLanguageFile(aLangTagUndetermined, true))
1510 0 : pLists = pLangTable->find(aLangTagUndetermined)->second;
1511 : }
1512 : OSL_ENSURE(pLists, "No auto correction file!");
1513 0 : return pLists && pLists->AddToWrdSttExceptList(rNew);
1514 : }
1515 :
1516 0 : bool SvxAutoCorrect::GetPrevAutoCorrWord( SvxAutoCorrDoc& rDoc,
1517 : const OUString& rTxt, sal_Int32 nPos,
1518 : OUString& rWord ) const
1519 : {
1520 0 : if( !nPos )
1521 0 : return false;
1522 :
1523 0 : sal_Int32 nEnde = nPos;
1524 :
1525 : // it must be followed by a blank or tab!
1526 0 : if( ( nPos < rTxt.getLength() &&
1527 0 : !IsWordDelim( rTxt[ nPos ])) ||
1528 0 : IsWordDelim( rTxt[ --nPos ]))
1529 0 : return false;
1530 :
1531 0 : while( nPos && !IsWordDelim( rTxt[ --nPos ]))
1532 : ;
1533 :
1534 : // Found a Paragraph-start or a Blank, search for the word shortcut in
1535 : // auto.
1536 0 : sal_Int32 nCapLttrPos = nPos+1; // on the 1st Character
1537 0 : if( !nPos && !IsWordDelim( rTxt[ 0 ]))
1538 0 : --nCapLttrPos; // Beginning of pargraph and no Blank!
1539 :
1540 0 : while( lcl_IsInAsciiArr( sImplSttSkipChars, rTxt[ nCapLttrPos ]) )
1541 0 : if( ++nCapLttrPos >= nEnde )
1542 0 : return false;
1543 :
1544 0 : if( 3 > nEnde - nCapLttrPos )
1545 0 : return false;
1546 :
1547 0 : LanguageType eLang = rDoc.GetLanguage( nCapLttrPos, false );
1548 0 : if( LANGUAGE_SYSTEM == eLang )
1549 0 : eLang = MsLangId::getSystemLanguage();
1550 :
1551 0 : SvxAutoCorrect* pThis = const_cast<SvxAutoCorrect*>(this);
1552 0 : CharClass& rCC = pThis->GetCharClass( eLang );
1553 :
1554 0 : if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nEnde ))
1555 0 : return false;
1556 :
1557 0 : rWord = rTxt.copy( nCapLttrPos, nEnde - nCapLttrPos );
1558 0 : return true;
1559 : }
1560 :
1561 20 : bool SvxAutoCorrect::CreateLanguageFile( const LanguageTag& rLanguageTag, bool bNewFile )
1562 : {
1563 : OSL_ENSURE(pLangTable->find(rLanguageTag) == pLangTable->end(), "Language already exists ");
1564 :
1565 20 : OUString sUserDirFile( GetAutoCorrFileName( rLanguageTag, true, false, false ));
1566 40 : OUString sShareDirFile( sUserDirFile );
1567 :
1568 20 : SvxAutoCorrectLanguageListsPtr pLists = 0;
1569 :
1570 20 : tools::Time nMinTime( 0, 2 ), nAktTime( tools::Time::SYSTEM ), nLastCheckTime( tools::Time::EMPTY );
1571 :
1572 20 : std::map<LanguageTag, long>::iterator nFndPos = aLastFileTable.find(rLanguageTag);
1573 95 : if(nFndPos != aLastFileTable.end() &&
1574 90 : (nLastCheckTime.SetTime(nFndPos->second), nLastCheckTime < nAktTime) &&
1575 50 : nAktTime - nLastCheckTime < nMinTime)
1576 : {
1577 : // no need to test the file, because the last check is not older then
1578 : // 2 minutes.
1579 15 : if( bNewFile )
1580 : {
1581 0 : sShareDirFile = sUserDirFile;
1582 0 : pLists = new SvxAutoCorrectLanguageLists( *this, sShareDirFile, sUserDirFile );
1583 0 : LanguageTag aTmp(rLanguageTag); // this insert() needs a non-const reference
1584 0 : pLangTable->insert(aTmp, pLists);
1585 0 : aLastFileTable.erase(nFndPos);
1586 : }
1587 : }
1588 11 : else if(
1589 13 : ( FStatHelper::IsDocument( sUserDirFile ) ||
1590 17 : FStatHelper::IsDocument( sShareDirFile =
1591 12 : GetAutoCorrFileName( rLanguageTag, false, false, false ) ) ||
1592 13 : FStatHelper::IsDocument( sShareDirFile =
1593 4 : GetAutoCorrFileName( rLanguageTag, false, false, true) )
1594 13 : ) ||
1595 : ( sShareDirFile = sUserDirFile, bNewFile )
1596 : )
1597 : {
1598 2 : pLists = new SvxAutoCorrectLanguageLists( *this, sShareDirFile, sUserDirFile );
1599 2 : LanguageTag aTmp(rLanguageTag); // this insert() needs a non-const reference
1600 2 : pLangTable->insert(aTmp, pLists);
1601 2 : if (nFndPos != aLastFileTable.end())
1602 0 : aLastFileTable.erase(nFndPos);
1603 : }
1604 3 : else if( !bNewFile )
1605 : {
1606 3 : aLastFileTable[rLanguageTag] = nAktTime.GetTime();
1607 : }
1608 40 : return pLists != 0;
1609 : }
1610 :
1611 0 : bool SvxAutoCorrect::PutText( const OUString& rShort, const OUString& rLong,
1612 : LanguageType eLang )
1613 : {
1614 0 : LanguageTag aLanguageTag( eLang);
1615 0 : boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(aLanguageTag);
1616 0 : if(nTmpVal != pLangTable->end())
1617 0 : return nTmpVal->second->PutText(rShort, rLong);
1618 0 : if(CreateLanguageFile(aLanguageTag))
1619 0 : return pLangTable->find(aLanguageTag)->second->PutText(rShort, rLong);
1620 0 : return false;
1621 : }
1622 :
1623 0 : bool SvxAutoCorrect::MakeCombinedChanges( std::vector<SvxAutocorrWord>& aNewEntries,
1624 : std::vector<SvxAutocorrWord>& aDeleteEntries,
1625 : LanguageType eLang )
1626 : {
1627 0 : LanguageTag aLanguageTag( eLang);
1628 0 : boost::ptr_map<LanguageTag, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(aLanguageTag);
1629 0 : if(nTmpVal != pLangTable->end())
1630 : {
1631 0 : return nTmpVal->second->MakeCombinedChanges( aNewEntries, aDeleteEntries );
1632 : }
1633 0 : else if(CreateLanguageFile( aLanguageTag ))
1634 : {
1635 0 : return pLangTable->find( aLanguageTag )->second->MakeCombinedChanges( aNewEntries, aDeleteEntries );
1636 : }
1637 0 : return false;
1638 :
1639 : }
1640 :
1641 : // - return the replacement text (only for SWG-Format, all other
1642 : // can be taken from the word list!)
1643 0 : bool SvxAutoCorrect::GetLongText( const OUString&, OUString& )
1644 : {
1645 0 : return false;
1646 : }
1647 :
1648 0 : void SvxAutoCorrect::refreshBlockList( const uno::Reference< embed::XStorage >& )
1649 : {
1650 0 : }
1651 :
1652 : // Text with attribution (only the SWG - SWG format!)
1653 0 : bool SvxAutoCorrect::PutText( const com::sun::star::uno::Reference < com::sun::star::embed::XStorage >&,
1654 : const OUString&, const OUString&, SfxObjectShell&, OUString& )
1655 : {
1656 0 : return false;
1657 : }
1658 :
1659 0 : OUString EncryptBlockName_Imp(const OUString& rName)
1660 : {
1661 0 : OUStringBuffer aName;
1662 0 : aName.append('#').append(rName);
1663 0 : for (sal_Int32 nLen = rName.getLength(), nPos = 1; nPos < nLen; ++nPos)
1664 : {
1665 0 : if (lcl_IsInAsciiArr( "!/:.\\", aName[nPos]))
1666 0 : aName[nPos] &= 0x0f;
1667 : }
1668 0 : return aName.makeStringAndClear();
1669 : }
1670 :
1671 : /* This code is copied from SwXMLTextBlocks::GeneratePackageName */
1672 0 : static void GeneratePackageName ( const OUString& rShort, OUString& rPackageName )
1673 : {
1674 0 : OString sByte(OUStringToOString(rShort, RTL_TEXTENCODING_UTF7));
1675 0 : OUStringBuffer aBuf(OStringToOUString(sByte, RTL_TEXTENCODING_ASCII_US));
1676 :
1677 0 : for (sal_Int32 nPos = 0; nPos < aBuf.getLength(); ++nPos)
1678 : {
1679 0 : switch (aBuf[nPos])
1680 : {
1681 : case '!':
1682 : case '/':
1683 : case ':':
1684 : case '.':
1685 : case '\\':
1686 0 : aBuf[nPos] = '_';
1687 0 : break;
1688 : default:
1689 0 : break;
1690 : }
1691 : }
1692 :
1693 0 : rPackageName = aBuf.makeStringAndClear();
1694 0 : }
1695 :
1696 6 : static const SvxAutocorrWord* lcl_SearchWordsInList(
1697 : SvxAutoCorrectLanguageListsPtr pList, const OUString& rTxt,
1698 : sal_Int32& rStt, sal_Int32 nEndPos)
1699 : {
1700 6 : const SvxAutocorrWordList* pAutoCorrWordList = pList->GetAutocorrWordList();
1701 6 : return pAutoCorrWordList->SearchWordsInList( rTxt, rStt, nEndPos );
1702 : }
1703 :
1704 : // the search for the words in the substitution table
1705 5 : const SvxAutocorrWord* SvxAutoCorrect::SearchWordsInList(
1706 : const OUString& rTxt, sal_Int32& rStt, sal_Int32 nEndPos,
1707 : SvxAutoCorrDoc&, LanguageTag& rLang )
1708 : {
1709 5 : const SvxAutocorrWord* pRet = 0;
1710 5 : LanguageTag aLanguageTag( rLang);
1711 5 : if( aLanguageTag.isSystemLocale() )
1712 0 : aLanguageTag.reset( MsLangId::getSystemLanguage());
1713 :
1714 : /* TODO-BCP47: this is so ugly, should all maybe be a proper fallback
1715 : * list instead? */
1716 :
1717 : // First search for eLang, then US-English -> English
1718 : // and last in LANGUAGE_UNDETERMINED
1719 5 : if(pLangTable->find(aLanguageTag) != pLangTable->end() || CreateLanguageFile(aLanguageTag, false))
1720 : {
1721 : //the language is available - so bring it on
1722 3 : SvxAutoCorrectLanguageLists* pList = pLangTable->find(aLanguageTag)->second;
1723 3 : pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos );
1724 3 : if( pRet )
1725 : {
1726 0 : rLang = aLanguageTag;
1727 0 : return pRet;
1728 : }
1729 : }
1730 :
1731 : // If it still could not be found here, then keep on searching
1732 :
1733 5 : LanguageType eLang = aLanguageTag.getLanguageType();
1734 5 : LanguageType nTmpKey1 = eLang & 0x7ff, // the main language in many cases DE
1735 5 : nTmpKey2 = eLang & 0x3ff; // otherwise for example EN
1736 5 : if(nTmpKey1 != eLang && (pLangTable->find(aLanguageTag.reset(nTmpKey1)) != pLangTable->end() ||
1737 0 : CreateLanguageFile(aLanguageTag, false)))
1738 : {
1739 : //the language is available - so bring it on
1740 0 : SvxAutoCorrectLanguageLists* pList = pLangTable->find(aLanguageTag)->second;
1741 0 : pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos );
1742 0 : if( pRet )
1743 : {
1744 0 : rLang = aLanguageTag;
1745 0 : return pRet;
1746 : }
1747 : }
1748 :
1749 8 : if(nTmpKey2 != eLang && (pLangTable->find(aLanguageTag.reset(nTmpKey2)) != pLangTable->end() ||
1750 3 : CreateLanguageFile(aLanguageTag, false)))
1751 : {
1752 : //the language is available - so bring it on
1753 3 : SvxAutoCorrectLanguageLists* pList = pLangTable->find(aLanguageTag)->second;
1754 3 : pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos );
1755 3 : if( pRet )
1756 : {
1757 3 : rLang = aLanguageTag;
1758 3 : return pRet;
1759 : }
1760 : }
1761 :
1762 4 : if(pLangTable->find(aLanguageTag.reset(LANGUAGE_UNDETERMINED)) != pLangTable->end() ||
1763 2 : CreateLanguageFile(aLanguageTag, false))
1764 : {
1765 : //the language is available - so bring it on
1766 0 : SvxAutoCorrectLanguageLists* pList = pLangTable->find(aLanguageTag)->second;
1767 0 : pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos );
1768 0 : if( pRet )
1769 : {
1770 0 : rLang = aLanguageTag;
1771 0 : return pRet;
1772 : }
1773 : }
1774 2 : return 0;
1775 : }
1776 :
1777 4 : bool SvxAutoCorrect::FindInWrdSttExceptList( LanguageType eLang,
1778 : const OUString& sWord )
1779 : {
1780 4 : LanguageTag aLanguageTag( eLang);
1781 :
1782 : /* TODO-BCP47: again horrible uglyness */
1783 :
1784 : // First search for eLang, then US-English -> English
1785 : // and last in LANGUAGE_UNDETERMINED
1786 4 : LanguageType nTmpKey1 = eLang & 0x7ff, // the main language in many cases DE
1787 4 : nTmpKey2 = eLang & 0x3ff; // otherwise for example EN
1788 8 : OUString sTemp(sWord);
1789 :
1790 4 : if(pLangTable->find(aLanguageTag) != pLangTable->end() || CreateLanguageFile(aLanguageTag, false))
1791 : {
1792 : //the language is available - so bring it on
1793 0 : SvxAutoCorrectLanguageLists* pList = pLangTable->find(aLanguageTag)->second;
1794 0 : OUString _sTemp(sWord);
1795 0 : if(pList->GetWrdSttExceptList()->find(_sTemp) != pList->GetWrdSttExceptList()->end() )
1796 0 : return true;
1797 : }
1798 :
1799 : // If it still could not be found here, then keep on searching
1800 4 : if(nTmpKey1 != eLang && (pLangTable->find(aLanguageTag.reset(nTmpKey1)) != pLangTable->end() ||
1801 0 : CreateLanguageFile(aLanguageTag, false)))
1802 : {
1803 : //the language is available - so bring it on
1804 0 : SvxAutoCorrectLanguageLists* pList = pLangTable->find(aLanguageTag)->second;
1805 0 : if(pList->GetWrdSttExceptList()->find(sTemp) != pList->GetWrdSttExceptList()->end() )
1806 0 : return true;
1807 : }
1808 :
1809 8 : if(nTmpKey2 != eLang && (pLangTable->find(aLanguageTag.reset(nTmpKey2)) != pLangTable->end() ||
1810 4 : CreateLanguageFile(aLanguageTag, false)))
1811 : {
1812 : //the language is available - so bring it on
1813 0 : SvxAutoCorrectLanguageLists* pList = pLangTable->find(aLanguageTag)->second;
1814 0 : if(pList->GetWrdSttExceptList()->find(sTemp) != pList->GetWrdSttExceptList()->end() )
1815 0 : return true;
1816 : }
1817 :
1818 8 : if(pLangTable->find(aLanguageTag.reset(LANGUAGE_UNDETERMINED)) != pLangTable->end() ||
1819 4 : CreateLanguageFile(aLanguageTag, false))
1820 : {
1821 : //the language is available - so bring it on
1822 0 : SvxAutoCorrectLanguageLists* pList = pLangTable->find(aLanguageTag)->second;
1823 0 : if(pList->GetWrdSttExceptList()->find(sTemp) != pList->GetWrdSttExceptList()->end() )
1824 0 : return true;
1825 : }
1826 8 : return false;
1827 : }
1828 :
1829 0 : static bool lcl_FindAbbreviation(const SvStringsISortDtor* pList, const OUString& sWord)
1830 : {
1831 0 : OUString sAbk('~');
1832 0 : SvStringsISortDtor::const_iterator it = pList->find( sAbk );
1833 0 : sal_uInt16 nPos = it - pList->begin();
1834 0 : if( nPos < pList->size() )
1835 : {
1836 0 : OUString sLowerWord(sWord.toAsciiLowerCase());
1837 0 : OUString pAbk;
1838 0 : for( sal_uInt16 n = nPos;
1839 0 : n < pList->size() &&
1840 0 : '~' == ( pAbk = (*pList)[ n ])[ 0 ];
1841 : ++n )
1842 : {
1843 : // ~ and ~. are not allowed!
1844 0 : if( 2 < pAbk.getLength() && pAbk.getLength() - 1 <= sWord.getLength() )
1845 : {
1846 0 : OUString sLowerAbk(pAbk.toAsciiLowerCase());
1847 0 : for (sal_Int32 i = sLowerAbk.getLength(), ii = sLowerWord.getLength(); i;)
1848 : {
1849 0 : if( !--i ) // agrees
1850 0 : return true;
1851 :
1852 0 : if( sLowerAbk[i] != sLowerWord[--ii])
1853 0 : break;
1854 0 : }
1855 : }
1856 0 : }
1857 : }
1858 : OSL_ENSURE( !(nPos && '~' == (*pList)[ --nPos ][ 0 ] ),
1859 : "Wrongly sorted exception list?" );
1860 0 : return false;
1861 : }
1862 :
1863 0 : bool SvxAutoCorrect::FindInCplSttExceptList(LanguageType eLang,
1864 : const OUString& sWord, bool bAbbreviation)
1865 : {
1866 0 : LanguageTag aLanguageTag( eLang);
1867 :
1868 : /* TODO-BCP47: did I mention terrible horrible uglyness? */
1869 :
1870 : // First search for eLang, then US-English -> English
1871 : // and last in LANGUAGE_UNDETERMINED
1872 0 : LanguageType nTmpKey1 = eLang & 0x7ff, // the main language in many cases DE
1873 0 : nTmpKey2 = eLang & 0x3ff; // otherwise for example EN
1874 0 : OUString sTemp( sWord );
1875 :
1876 0 : if(pLangTable->find(aLanguageTag) != pLangTable->end() || CreateLanguageFile(aLanguageTag, false))
1877 : {
1878 : //the language is available - so bring it on
1879 0 : const SvStringsISortDtor* pList = pLangTable->find(aLanguageTag)->second->GetCplSttExceptList();
1880 0 : if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(sTemp) != pList->end() )
1881 0 : return true;
1882 : }
1883 :
1884 : // If it still could not be found here, then keep on searching
1885 0 : if(nTmpKey1 != eLang && (pLangTable->find(aLanguageTag.reset(nTmpKey1)) != pLangTable->end() ||
1886 0 : CreateLanguageFile(aLanguageTag, false)))
1887 : {
1888 0 : const SvStringsISortDtor* pList = pLangTable->find(aLanguageTag)->second->GetCplSttExceptList();
1889 0 : if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(sTemp) != pList->end() )
1890 0 : return true;
1891 : }
1892 :
1893 0 : if(nTmpKey2 != eLang && (pLangTable->find(aLanguageTag.reset(nTmpKey2)) != pLangTable->end() ||
1894 0 : CreateLanguageFile(aLanguageTag, false)))
1895 : {
1896 : //the language is available - so bring it on
1897 0 : const SvStringsISortDtor* pList = pLangTable->find(aLanguageTag)->second->GetCplSttExceptList();
1898 0 : if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(sTemp) != pList->end() )
1899 0 : return true;
1900 : }
1901 :
1902 0 : if(pLangTable->find(aLanguageTag.reset(LANGUAGE_UNDETERMINED)) != pLangTable->end() ||
1903 0 : CreateLanguageFile(aLanguageTag, false))
1904 : {
1905 : //the language is available - so bring it on
1906 0 : const SvStringsISortDtor* pList = pLangTable->find(aLanguageTag)->second->GetCplSttExceptList();
1907 0 : if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(sTemp) != pList->end() )
1908 0 : return true;
1909 : }
1910 0 : return false;
1911 : }
1912 :
1913 30 : OUString SvxAutoCorrect::GetAutoCorrFileName( const LanguageTag& rLanguageTag,
1914 : bool bNewFile, bool bTst, bool bUnlocalized ) const
1915 : {
1916 60 : OUString sRet, sExt( rLanguageTag.getBcp47() );
1917 30 : if (bUnlocalized)
1918 : {
1919 : // we don't want variant, so we'll take "fr" instead of "fr-CA" for example
1920 4 : ::std::vector< OUString > vecFallBackStrings = rLanguageTag.getFallbackStrings(false);
1921 4 : if (!vecFallBackStrings.empty())
1922 2 : sExt = vecFallBackStrings[0];
1923 : }
1924 :
1925 30 : sExt = "_" + sExt + ".dat";
1926 30 : if( bNewFile )
1927 20 : ( sRet = sUserAutoCorrFile ) += sExt;
1928 10 : else if( !bTst )
1929 8 : ( sRet = sShareAutoCorrFile ) += sExt;
1930 : else
1931 : {
1932 : // test first in the user directory - if not exist, then
1933 2 : ( sRet = sUserAutoCorrFile ) += sExt;
1934 2 : if( !FStatHelper::IsDocument( sRet ))
1935 0 : ( sRet = sShareAutoCorrFile ) += sExt;
1936 : }
1937 60 : return sRet;
1938 : }
1939 :
1940 2 : SvxAutoCorrectLanguageLists::SvxAutoCorrectLanguageLists(
1941 : SvxAutoCorrect& rParent,
1942 : const OUString& rShareAutoCorrectFile,
1943 : const OUString& rUserAutoCorrectFile)
1944 : : sShareAutoCorrFile( rShareAutoCorrectFile ),
1945 : sUserAutoCorrFile( rUserAutoCorrectFile ),
1946 : aModifiedDate( Date::EMPTY ),
1947 : aModifiedTime( tools::Time::EMPTY ),
1948 : aLastCheckTime( tools::Time::EMPTY ),
1949 : pCplStt_ExcptLst( 0 ),
1950 : pWrdStt_ExcptLst( 0 ),
1951 : pAutocorr_List( 0 ),
1952 : rAutoCorrect(rParent),
1953 2 : nFlags(0)
1954 : {
1955 2 : }
1956 :
1957 4 : SvxAutoCorrectLanguageLists::~SvxAutoCorrectLanguageLists()
1958 : {
1959 2 : delete pCplStt_ExcptLst;
1960 2 : delete pWrdStt_ExcptLst;
1961 2 : delete pAutocorr_List;
1962 2 : }
1963 :
1964 4 : bool SvxAutoCorrectLanguageLists::IsFileChanged_Imp()
1965 : {
1966 : // Access the file system only every 2 minutes to check the date stamp
1967 4 : bool bRet = false;
1968 :
1969 4 : tools::Time nMinTime( 0, 2 );
1970 4 : tools::Time nAktTime( tools::Time::SYSTEM );
1971 8 : if( aLastCheckTime > nAktTime || // overflow?
1972 4 : ( nAktTime -= aLastCheckTime ) > nMinTime ) // min time past
1973 : {
1974 0 : Date aTstDate( Date::EMPTY ); tools::Time aTstTime( tools::Time::EMPTY );
1975 0 : if( FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
1976 0 : &aTstDate, &aTstTime ) &&
1977 0 : ( aModifiedDate != aTstDate || aModifiedTime != aTstTime ))
1978 : {
1979 0 : bRet = true;
1980 : // then remove all the lists fast!
1981 0 : if( CplSttLstLoad & nFlags && pCplStt_ExcptLst )
1982 0 : delete pCplStt_ExcptLst, pCplStt_ExcptLst = 0;
1983 0 : if( WrdSttLstLoad & nFlags && pWrdStt_ExcptLst )
1984 0 : delete pWrdStt_ExcptLst, pWrdStt_ExcptLst = 0;
1985 0 : if( ChgWordLstLoad & nFlags && pAutocorr_List )
1986 0 : delete pAutocorr_List, pAutocorr_List = 0;
1987 0 : nFlags &= ~(CplSttLstLoad | WrdSttLstLoad | ChgWordLstLoad );
1988 : }
1989 0 : aLastCheckTime = tools::Time( tools::Time::SYSTEM );
1990 : }
1991 4 : return bRet;
1992 : }
1993 :
1994 0 : void SvxAutoCorrectLanguageLists::LoadXMLExceptList_Imp(
1995 : SvStringsISortDtor*& rpLst,
1996 : const sal_Char* pStrmName,
1997 : tools::SvRef<SotStorage>& rStg)
1998 : {
1999 0 : if( rpLst )
2000 0 : rpLst->clear();
2001 : else
2002 0 : rpLst = new SvStringsISortDtor;
2003 :
2004 : {
2005 0 : OUString sStrmName( pStrmName, strlen(pStrmName), RTL_TEXTENCODING_MS_1252 );
2006 0 : OUString sTmp( sStrmName );
2007 :
2008 0 : if( rStg.Is() && rStg->IsStream( sStrmName ) )
2009 : {
2010 : tools::SvRef<SotStorageStream> xStrm = rStg->OpenSotStream( sTmp,
2011 0 : ( StreamMode::READ | StreamMode::SHARE_DENYWRITE | StreamMode::NOCREATE ) );
2012 0 : if( SVSTREAM_OK != xStrm->GetError())
2013 : {
2014 0 : xStrm.Clear();
2015 0 : rStg.Clear();
2016 0 : RemoveStream_Imp( sStrmName );
2017 : }
2018 : else
2019 : {
2020 : uno::Reference< uno::XComponentContext > xContext =
2021 0 : comphelper::getProcessComponentContext();
2022 :
2023 0 : xml::sax::InputSource aParserInput;
2024 0 : aParserInput.sSystemId = sStrmName;
2025 :
2026 0 : xStrm->Seek( 0L );
2027 0 : xStrm->SetBufferSize( 8 * 1024 );
2028 0 : aParserInput.aInputStream = new utl::OInputStreamWrapper( *xStrm );
2029 :
2030 : // get filter
2031 0 : uno::Reference< xml::sax::XFastDocumentHandler > xFilter = new SvXMLExceptionListImport ( xContext, *rpLst );
2032 :
2033 : // connect parser and filter
2034 0 : uno::Reference< xml::sax::XFastParser > xParser = xml::sax::FastParser::create( xContext );
2035 0 : uno::Reference< xml::sax::XFastTokenHandler > xTokenHandler = static_cast< xml::sax::XFastTokenHandler* >( new SvXMLAutoCorrectTokenHandler );
2036 0 : xParser->setFastDocumentHandler( xFilter );
2037 0 : xParser->registerNamespace( "http://openoffice.org/2001/block-list", SvXMLAutoCorrectToken::NAMESPACE );
2038 0 : xParser->setTokenHandler( xTokenHandler );
2039 :
2040 : // parse
2041 : try
2042 : {
2043 0 : xParser->parseStream( aParserInput );
2044 : }
2045 0 : catch( const xml::sax::SAXParseException& )
2046 : {
2047 : // re throw ?
2048 : }
2049 0 : catch( const xml::sax::SAXException& )
2050 : {
2051 : // re throw ?
2052 : }
2053 0 : catch( const io::IOException& )
2054 : {
2055 : // re throw ?
2056 0 : }
2057 0 : }
2058 : }
2059 :
2060 : // Set time stamp
2061 : FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
2062 0 : &aModifiedDate, &aModifiedTime );
2063 0 : aLastCheckTime = tools::Time( tools::Time::SYSTEM );
2064 : }
2065 :
2066 0 : }
2067 :
2068 0 : void SvxAutoCorrectLanguageLists::SaveExceptList_Imp(
2069 : const SvStringsISortDtor& rLst,
2070 : const sal_Char* pStrmName,
2071 : tools::SvRef<SotStorage> &rStg,
2072 : bool bConvert )
2073 : {
2074 0 : if( rStg.Is() )
2075 : {
2076 0 : OUString sStrmName( pStrmName, strlen(pStrmName), RTL_TEXTENCODING_MS_1252 );
2077 0 : if( rLst.empty() )
2078 : {
2079 0 : rStg->Remove( sStrmName );
2080 0 : rStg->Commit();
2081 : }
2082 : else
2083 : {
2084 : tools::SvRef<SotStorageStream> xStrm = rStg->OpenSotStream( sStrmName,
2085 0 : ( StreamMode::READ | StreamMode::WRITE | StreamMode::SHARE_DENYWRITE ) );
2086 0 : if( xStrm.Is() )
2087 : {
2088 0 : xStrm->SetSize( 0 );
2089 0 : xStrm->SetBufferSize( 8192 );
2090 0 : OUString aMime( "text/xml" );
2091 0 : uno::Any aAny;
2092 0 : aAny <<= aMime;
2093 0 : xStrm->SetProperty( OUString("MediaType"), aAny );
2094 :
2095 :
2096 : uno::Reference< uno::XComponentContext > xContext =
2097 0 : comphelper::getProcessComponentContext();
2098 :
2099 0 : uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext);
2100 0 : uno::Reference < io::XOutputStream> xOut = new utl::OOutputStreamWrapper( *xStrm );
2101 0 : xWriter->setOutputStream(xOut);
2102 :
2103 0 : uno::Reference < xml::sax::XDocumentHandler > xHandler(xWriter, UNO_QUERY_THROW);
2104 0 : SvXMLExceptionListExport aExp( xContext, rLst, sStrmName, xHandler );
2105 :
2106 0 : aExp.exportDoc( XML_BLOCK_LIST );
2107 :
2108 0 : xStrm->Commit();
2109 0 : if( xStrm->GetError() == SVSTREAM_OK )
2110 : {
2111 0 : xStrm.Clear();
2112 0 : if (!bConvert)
2113 : {
2114 0 : rStg->Commit();
2115 0 : if( SVSTREAM_OK != rStg->GetError() )
2116 : {
2117 0 : rStg->Remove( sStrmName );
2118 0 : rStg->Commit();
2119 : }
2120 : }
2121 0 : }
2122 0 : }
2123 0 : }
2124 : }
2125 0 : }
2126 :
2127 2 : SvxAutocorrWordList* SvxAutoCorrectLanguageLists::LoadAutocorrWordList()
2128 : {
2129 2 : if( pAutocorr_List )
2130 0 : pAutocorr_List->DeleteAndDestroyAll();
2131 : else
2132 2 : pAutocorr_List = new SvxAutocorrWordList();
2133 :
2134 : try
2135 : {
2136 2 : uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetStorageFromURL( sShareAutoCorrFile, embed::ElementModes::READ );
2137 4 : OUString aXMLWordListName( pXMLImplAutocorr_ListStr, strlen(pXMLImplAutocorr_ListStr), RTL_TEXTENCODING_MS_1252 );
2138 4 : uno::Reference < io::XStream > xStrm = xStg->openStreamElement( aXMLWordListName, embed::ElementModes::READ );
2139 4 : uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
2140 :
2141 4 : xml::sax::InputSource aParserInput;
2142 2 : aParserInput.sSystemId = aXMLWordListName;
2143 2 : aParserInput.aInputStream = xStrm->getInputStream();
2144 :
2145 : // get parser
2146 4 : uno::Reference< xml::sax::XFastParser > xParser = xml::sax::FastParser::create(xContext);
2147 : SAL_INFO("editeng", "AutoCorrect Import" );
2148 4 : uno::Reference< xml::sax::XFastDocumentHandler > xFilter = new SvXMLAutoCorrectImport( xContext, pAutocorr_List, rAutoCorrect, xStg );
2149 4 : uno::Reference< xml::sax::XFastTokenHandler > xTokenHandler = static_cast< xml::sax::XFastTokenHandler* >( new SvXMLAutoCorrectTokenHandler );
2150 :
2151 : // connect parser and filter
2152 2 : xParser->setFastDocumentHandler( xFilter );
2153 2 : xParser->registerNamespace( "http://openoffice.org/2001/block-list", SvXMLAutoCorrectToken::NAMESPACE );
2154 2 : xParser->setTokenHandler(xTokenHandler);
2155 :
2156 : // parse
2157 4 : xParser->parseStream( aParserInput );
2158 : }
2159 0 : catch ( const uno::Exception& )
2160 : {
2161 : }
2162 :
2163 : // Set time stamp
2164 : FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
2165 2 : &aModifiedDate, &aModifiedTime );
2166 2 : aLastCheckTime = tools::Time( tools::Time::SYSTEM );
2167 :
2168 2 : return pAutocorr_List;
2169 : }
2170 :
2171 2 : void SvxAutoCorrectLanguageLists::SetAutocorrWordList( SvxAutocorrWordList* pList )
2172 : {
2173 2 : if( pAutocorr_List && pList != pAutocorr_List )
2174 0 : delete pAutocorr_List;
2175 2 : pAutocorr_List = pList;
2176 2 : if( !pAutocorr_List )
2177 : {
2178 : OSL_ENSURE( false, "No valid list" );
2179 0 : pAutocorr_List = new SvxAutocorrWordList();
2180 : }
2181 2 : nFlags |= ChgWordLstLoad;
2182 2 : }
2183 :
2184 6 : const SvxAutocorrWordList* SvxAutoCorrectLanguageLists::GetAutocorrWordList()
2185 : {
2186 6 : if( !( ChgWordLstLoad & nFlags ) || IsFileChanged_Imp() )
2187 2 : SetAutocorrWordList( LoadAutocorrWordList() );
2188 6 : return pAutocorr_List;
2189 : }
2190 :
2191 0 : SvStringsISortDtor* SvxAutoCorrectLanguageLists::GetCplSttExceptList()
2192 : {
2193 0 : if( !( CplSttLstLoad & nFlags ) || IsFileChanged_Imp() )
2194 0 : SetCplSttExceptList( LoadCplSttExceptList() );
2195 0 : return pCplStt_ExcptLst;
2196 : }
2197 :
2198 0 : bool SvxAutoCorrectLanguageLists::AddToCplSttExceptList(const OUString& rNew)
2199 : {
2200 0 : bool aRet = false;
2201 0 : if( !rNew.isEmpty() && GetCplSttExceptList()->insert( rNew ).second )
2202 : {
2203 0 : MakeUserStorage_Impl();
2204 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2205 :
2206 0 : SaveExceptList_Imp( *pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
2207 :
2208 0 : xStg = 0;
2209 : // Set time stamp
2210 : FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
2211 0 : &aModifiedDate, &aModifiedTime );
2212 0 : aLastCheckTime = tools::Time( tools::Time::SYSTEM );
2213 0 : aRet = true;
2214 : }
2215 0 : return aRet;
2216 : }
2217 :
2218 0 : bool SvxAutoCorrectLanguageLists::AddToWrdSttExceptList(const OUString& rNew)
2219 : {
2220 0 : bool aRet = false;
2221 0 : SvStringsISortDtor* pExceptList = LoadWrdSttExceptList();
2222 0 : if( !rNew.isEmpty() && pExceptList && pExceptList->insert( rNew ).second )
2223 : {
2224 0 : MakeUserStorage_Impl();
2225 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2226 :
2227 0 : SaveExceptList_Imp( *pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );
2228 :
2229 0 : xStg = 0;
2230 : // Set time stamp
2231 : FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
2232 0 : &aModifiedDate, &aModifiedTime );
2233 0 : aLastCheckTime = tools::Time( tools::Time::SYSTEM );
2234 0 : aRet = true;
2235 : }
2236 0 : return aRet;
2237 : }
2238 :
2239 0 : SvStringsISortDtor* SvxAutoCorrectLanguageLists::LoadCplSttExceptList()
2240 : {
2241 : try
2242 : {
2243 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sShareAutoCorrFile, StreamMode::READ | StreamMode::SHARE_DENYNONE );
2244 0 : OUString sTemp ( pXMLImplCplStt_ExcptLstStr );
2245 0 : if( xStg.Is() && xStg->IsContained( sTemp ) )
2246 0 : LoadXMLExceptList_Imp( pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
2247 : }
2248 0 : catch (const css::ucb::ContentCreationException&)
2249 : {
2250 : }
2251 0 : return pCplStt_ExcptLst;
2252 : }
2253 :
2254 0 : void SvxAutoCorrectLanguageLists::SaveCplSttExceptList()
2255 : {
2256 0 : MakeUserStorage_Impl();
2257 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2258 :
2259 0 : SaveExceptList_Imp( *pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
2260 :
2261 0 : xStg = 0;
2262 :
2263 : // Set time stamp
2264 : FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
2265 0 : &aModifiedDate, &aModifiedTime );
2266 0 : aLastCheckTime = tools::Time( tools::Time::SYSTEM );
2267 0 : }
2268 :
2269 0 : void SvxAutoCorrectLanguageLists::SetCplSttExceptList( SvStringsISortDtor* pList )
2270 : {
2271 0 : if( pCplStt_ExcptLst && pList != pCplStt_ExcptLst )
2272 0 : delete pCplStt_ExcptLst;
2273 :
2274 0 : pCplStt_ExcptLst = pList;
2275 0 : if( !pCplStt_ExcptLst )
2276 : {
2277 : OSL_ENSURE( false, "No valid list" );
2278 0 : pCplStt_ExcptLst = new SvStringsISortDtor;
2279 : }
2280 0 : nFlags |= CplSttLstLoad;
2281 0 : }
2282 :
2283 0 : SvStringsISortDtor* SvxAutoCorrectLanguageLists::LoadWrdSttExceptList()
2284 : {
2285 : try
2286 : {
2287 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sShareAutoCorrFile, StreamMode::READ | StreamMode::SHARE_DENYNONE );
2288 0 : OUString sTemp ( pXMLImplWrdStt_ExcptLstStr );
2289 0 : if( xStg.Is() && xStg->IsContained( sTemp ) )
2290 0 : LoadXMLExceptList_Imp( pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );
2291 : }
2292 0 : catch (const css::ucb::ContentCreationException &e)
2293 : {
2294 : SAL_WARN("editeng", "SvxAutoCorrectLanguageLists::LoadWrdSttExceptList: Caught exception: " << e.Message);
2295 : }
2296 0 : return pWrdStt_ExcptLst;
2297 : }
2298 :
2299 0 : void SvxAutoCorrectLanguageLists::SaveWrdSttExceptList()
2300 : {
2301 0 : MakeUserStorage_Impl();
2302 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2303 :
2304 0 : SaveExceptList_Imp( *pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );
2305 :
2306 0 : xStg = 0;
2307 : // Set time stamp
2308 : FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
2309 0 : &aModifiedDate, &aModifiedTime );
2310 0 : aLastCheckTime = tools::Time( tools::Time::SYSTEM );
2311 0 : }
2312 :
2313 0 : void SvxAutoCorrectLanguageLists::SetWrdSttExceptList( SvStringsISortDtor* pList )
2314 : {
2315 0 : if( pWrdStt_ExcptLst && pList != pWrdStt_ExcptLst )
2316 0 : delete pWrdStt_ExcptLst;
2317 0 : pWrdStt_ExcptLst = pList;
2318 0 : if( !pWrdStt_ExcptLst )
2319 : {
2320 : OSL_ENSURE( false, "No valid list" );
2321 0 : pWrdStt_ExcptLst = new SvStringsISortDtor;
2322 : }
2323 0 : nFlags |= WrdSttLstLoad;
2324 0 : }
2325 :
2326 0 : SvStringsISortDtor* SvxAutoCorrectLanguageLists::GetWrdSttExceptList()
2327 : {
2328 0 : if( !( WrdSttLstLoad & nFlags ) || IsFileChanged_Imp() )
2329 0 : SetWrdSttExceptList( LoadWrdSttExceptList() );
2330 0 : return pWrdStt_ExcptLst;
2331 : }
2332 :
2333 0 : void SvxAutoCorrectLanguageLists::RemoveStream_Imp( const OUString& rName )
2334 : {
2335 0 : if( sShareAutoCorrFile != sUserAutoCorrFile )
2336 : {
2337 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2338 0 : if( xStg.Is() && SVSTREAM_OK == xStg->GetError() &&
2339 0 : xStg->IsStream( rName ) )
2340 : {
2341 0 : xStg->Remove( rName );
2342 0 : xStg->Commit();
2343 :
2344 0 : xStg = 0;
2345 0 : }
2346 : }
2347 0 : }
2348 :
2349 0 : void SvxAutoCorrectLanguageLists::MakeUserStorage_Impl()
2350 : {
2351 : // The conversion needs to happen if the file is already in the user
2352 : // directory and is in the old format. Additionally it needs to
2353 : // happen when the file is being copied from share to user.
2354 :
2355 0 : bool bError = false, bConvert = false, bCopy = false;
2356 0 : INetURLObject aDest;
2357 0 : INetURLObject aSource;
2358 :
2359 0 : if (sUserAutoCorrFile != sShareAutoCorrFile )
2360 : {
2361 0 : aSource = INetURLObject ( sShareAutoCorrFile );
2362 0 : aDest = INetURLObject ( sUserAutoCorrFile );
2363 0 : if ( SotStorage::IsOLEStorage ( sShareAutoCorrFile ) )
2364 : {
2365 0 : aDest.SetExtension ( OUString("bak") );
2366 0 : bConvert = true;
2367 : }
2368 0 : bCopy = true;
2369 : }
2370 0 : else if ( SotStorage::IsOLEStorage ( sUserAutoCorrFile ) )
2371 : {
2372 0 : aSource = INetURLObject ( sUserAutoCorrFile );
2373 0 : aDest = INetURLObject ( sUserAutoCorrFile );
2374 0 : aDest.SetExtension ( OUString("bak") );
2375 0 : bCopy = bConvert = true;
2376 : }
2377 0 : if (bCopy)
2378 : {
2379 : try
2380 : {
2381 0 : OUString sMain(aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ));
2382 0 : sal_Unicode cSlash = '/';
2383 0 : sal_Int32 nSlashPos = sMain.lastIndexOf(cSlash);
2384 0 : sMain = sMain.copy(0, nSlashPos);
2385 0 : ::ucbhelper::Content aNewContent( sMain, uno::Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() );
2386 0 : Any aAny;
2387 0 : TransferInfo aInfo;
2388 0 : aInfo.NameClash = NameClash::OVERWRITE;
2389 0 : aInfo.NewTitle = aDest.GetName();
2390 0 : aInfo.SourceURL = aSource.GetMainURL( INetURLObject::DECODE_TO_IURI );
2391 0 : aInfo.MoveData = sal_False;
2392 0 : aAny <<= aInfo;
2393 0 : aNewContent.executeCommand( OUString ( "transfer" ), aAny);
2394 : }
2395 0 : catch (...)
2396 : {
2397 0 : bError = true;
2398 : }
2399 : }
2400 0 : if (bConvert && !bError)
2401 : {
2402 0 : tools::SvRef<SotStorage> xSrcStg = new SotStorage( aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ), StreamMode::READ );
2403 0 : tools::SvRef<SotStorage> xDstStg = new SotStorage( sUserAutoCorrFile, StreamMode::WRITE );
2404 :
2405 0 : if( xSrcStg.Is() && xDstStg.Is() )
2406 : {
2407 0 : OUString sXMLWord ( pXMLImplWrdStt_ExcptLstStr );
2408 0 : OUString sXMLSentence ( pXMLImplCplStt_ExcptLstStr );
2409 0 : SvStringsISortDtor *pTmpWordList = NULL;
2410 :
2411 0 : if (xSrcStg->IsContained( sXMLWord ) )
2412 0 : LoadXMLExceptList_Imp( pTmpWordList, pXMLImplWrdStt_ExcptLstStr, xSrcStg );
2413 :
2414 0 : if (pTmpWordList)
2415 : {
2416 0 : SaveExceptList_Imp( *pTmpWordList, pXMLImplWrdStt_ExcptLstStr, xDstStg, true );
2417 0 : pTmpWordList->clear();
2418 0 : pTmpWordList = NULL;
2419 : }
2420 :
2421 :
2422 0 : if (xSrcStg->IsContained( sXMLSentence ) )
2423 0 : LoadXMLExceptList_Imp( pTmpWordList, pXMLImplCplStt_ExcptLstStr, xSrcStg );
2424 :
2425 0 : if (pTmpWordList)
2426 : {
2427 0 : SaveExceptList_Imp( *pTmpWordList, pXMLImplCplStt_ExcptLstStr, xDstStg, true );
2428 0 : pTmpWordList->clear();
2429 : }
2430 :
2431 0 : GetAutocorrWordList();
2432 0 : MakeBlocklist_Imp( *xDstStg );
2433 0 : sShareAutoCorrFile = sUserAutoCorrFile;
2434 0 : xDstStg = 0;
2435 : try
2436 : {
2437 0 : ::ucbhelper::Content aContent ( aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ), uno::Reference < XCommandEnvironment >(), comphelper::getProcessComponentContext() );
2438 0 : aContent.executeCommand ( OUString( "delete" ), makeAny ( true ) );
2439 : }
2440 0 : catch (...)
2441 : {
2442 0 : }
2443 0 : }
2444 : }
2445 0 : else if( bCopy && !bError )
2446 0 : sShareAutoCorrFile = sUserAutoCorrFile;
2447 0 : }
2448 :
2449 0 : bool SvxAutoCorrectLanguageLists::MakeBlocklist_Imp( SotStorage& rStg )
2450 : {
2451 0 : OUString sStrmName( pXMLImplAutocorr_ListStr, strlen(pXMLImplAutocorr_ListStr), RTL_TEXTENCODING_MS_1252 );
2452 0 : bool bRet = true, bRemove = !pAutocorr_List || pAutocorr_List->empty();
2453 0 : if( !bRemove )
2454 : {
2455 : tools::SvRef<SotStorageStream> refList = rStg.OpenSotStream( sStrmName,
2456 0 : ( StreamMode::READ | StreamMode::WRITE | StreamMode::SHARE_DENYWRITE ) );
2457 0 : if( refList.Is() )
2458 : {
2459 0 : refList->SetSize( 0 );
2460 0 : refList->SetBufferSize( 8192 );
2461 0 : OUString aPropName( "MediaType" );
2462 0 : OUString aMime( "text/xml" );
2463 0 : uno::Any aAny;
2464 0 : aAny <<= aMime;
2465 0 : refList->SetProperty( aPropName, aAny );
2466 :
2467 : uno::Reference< uno::XComponentContext > xContext =
2468 0 : comphelper::getProcessComponentContext();
2469 :
2470 0 : uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext);
2471 0 : uno::Reference < io::XOutputStream> xOut = new utl::OOutputStreamWrapper( *refList );
2472 0 : xWriter->setOutputStream(xOut);
2473 :
2474 0 : uno::Reference<xml::sax::XDocumentHandler> xHandler(xWriter, uno::UNO_QUERY);
2475 0 : SvXMLAutoCorrectExport aExp( xContext, pAutocorr_List, sStrmName, xHandler );
2476 :
2477 0 : aExp.exportDoc( XML_BLOCK_LIST );
2478 :
2479 0 : refList->Commit();
2480 0 : bRet = SVSTREAM_OK == refList->GetError();
2481 0 : if( bRet )
2482 : {
2483 0 : refList.Clear();
2484 0 : rStg.Commit();
2485 0 : if( SVSTREAM_OK != rStg.GetError() )
2486 : {
2487 0 : bRemove = true;
2488 0 : bRet = false;
2489 : }
2490 0 : }
2491 : }
2492 : else
2493 0 : bRet = false;
2494 : }
2495 :
2496 0 : if( bRemove )
2497 : {
2498 0 : rStg.Remove( sStrmName );
2499 0 : rStg.Commit();
2500 : }
2501 :
2502 0 : return bRet;
2503 : }
2504 :
2505 0 : bool SvxAutoCorrectLanguageLists::MakeCombinedChanges( std::vector<SvxAutocorrWord>& aNewEntries, std::vector<SvxAutocorrWord>& aDeleteEntries )
2506 : {
2507 : // First get the current list!
2508 0 : GetAutocorrWordList();
2509 :
2510 0 : MakeUserStorage_Impl();
2511 0 : tools::SvRef<SotStorage> xStorage = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2512 :
2513 0 : bool bRet = xStorage.Is() && SVSTREAM_OK == xStorage->GetError();
2514 :
2515 0 : if( bRet )
2516 : {
2517 0 : for ( size_t i=0; i < aDeleteEntries.size(); i++ )
2518 : {
2519 0 : SvxAutocorrWord aWordToDelete = aDeleteEntries[i];
2520 0 : SvxAutocorrWord *pFoundEntry = pAutocorr_List->FindAndRemove( &aWordToDelete );
2521 0 : if( pFoundEntry )
2522 : {
2523 0 : if( !pFoundEntry->IsTextOnly() )
2524 : {
2525 0 : OUString aName( aWordToDelete.GetShort() );
2526 0 : if (xStorage->IsOLEStorage())
2527 0 : aName = EncryptBlockName_Imp(aName);
2528 : else
2529 0 : GeneratePackageName ( aWordToDelete.GetShort(), aName );
2530 :
2531 0 : if( xStorage->IsContained( aName ) )
2532 : {
2533 0 : xStorage->Remove( aName );
2534 0 : bRet = xStorage->Commit();
2535 0 : }
2536 : }
2537 0 : delete pFoundEntry;
2538 : }
2539 0 : }
2540 :
2541 0 : for ( size_t i=0; i < aNewEntries.size(); i++ )
2542 : {
2543 0 : SvxAutocorrWord *pWordToAdd = new SvxAutocorrWord( aNewEntries[i].GetShort(), aNewEntries[i].GetLong(), true );
2544 0 : SvxAutocorrWord *pRemoved = pAutocorr_List->FindAndRemove( pWordToAdd );
2545 0 : if( pRemoved )
2546 : {
2547 0 : if( !pRemoved->IsTextOnly() )
2548 : {
2549 : // Still have to remove the Storage
2550 0 : OUString sStorageName( pWordToAdd->GetShort() );
2551 0 : if (xStorage->IsOLEStorage())
2552 0 : sStorageName = EncryptBlockName_Imp(sStorageName);
2553 : else
2554 0 : GeneratePackageName ( pWordToAdd->GetShort(), sStorageName);
2555 :
2556 0 : if( xStorage->IsContained( sStorageName ) )
2557 0 : xStorage->Remove( sStorageName );
2558 : }
2559 0 : delete pRemoved;
2560 : }
2561 0 : bRet = pAutocorr_List->Insert( pWordToAdd );
2562 :
2563 0 : if ( !bRet )
2564 : {
2565 0 : delete pWordToAdd;
2566 0 : break;
2567 : }
2568 : }
2569 :
2570 0 : if ( bRet )
2571 : {
2572 0 : bRet = MakeBlocklist_Imp( *xStorage );
2573 : }
2574 : }
2575 0 : return bRet;
2576 : }
2577 :
2578 0 : bool SvxAutoCorrectLanguageLists::PutText( const OUString& rShort, const OUString& rLong )
2579 : {
2580 : // First get the current list!
2581 0 : GetAutocorrWordList();
2582 :
2583 0 : MakeUserStorage_Impl();
2584 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2585 :
2586 0 : bool bRet = xStg.Is() && SVSTREAM_OK == xStg->GetError();
2587 :
2588 : // Update the word list
2589 0 : if( bRet )
2590 : {
2591 0 : SvxAutocorrWord* pNew = new SvxAutocorrWord( rShort, rLong, true );
2592 0 : SvxAutocorrWord *pRemove = pAutocorr_List->FindAndRemove( pNew );
2593 0 : if( pRemove )
2594 : {
2595 0 : if( !pRemove->IsTextOnly() )
2596 : {
2597 : // Still have to remove the Storage
2598 0 : OUString sStgNm( rShort );
2599 0 : if (xStg->IsOLEStorage())
2600 0 : sStgNm = EncryptBlockName_Imp(sStgNm);
2601 : else
2602 0 : GeneratePackageName ( rShort, sStgNm);
2603 :
2604 0 : if( xStg->IsContained( sStgNm ) )
2605 0 : xStg->Remove( sStgNm );
2606 : }
2607 0 : delete pRemove;
2608 : }
2609 :
2610 0 : if( pAutocorr_List->Insert( pNew ) )
2611 : {
2612 0 : bRet = MakeBlocklist_Imp( *xStg );
2613 0 : xStg = 0;
2614 : }
2615 : else
2616 : {
2617 0 : delete pNew;
2618 0 : bRet = false;
2619 : }
2620 : }
2621 0 : return bRet;
2622 : }
2623 :
2624 0 : bool SvxAutoCorrectLanguageLists::PutText( const OUString& rShort,
2625 : SfxObjectShell& rShell )
2626 : {
2627 : // First get the current list!
2628 0 : GetAutocorrWordList();
2629 :
2630 0 : MakeUserStorage_Impl();
2631 :
2632 0 : bool bRet = false;
2633 0 : OUString sLong;
2634 : try
2635 : {
2636 0 : uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetStorageFromURL( sUserAutoCorrFile, embed::ElementModes::READWRITE );
2637 0 : bRet = rAutoCorrect.PutText( xStg, sUserAutoCorrFile, rShort, rShell, sLong );
2638 0 : xStg = 0;
2639 :
2640 : // Update the word list
2641 0 : if( bRet )
2642 : {
2643 0 : SvxAutocorrWord* pNew = new SvxAutocorrWord( rShort, sLong, false );
2644 0 : if( pAutocorr_List->Insert( pNew ) )
2645 : {
2646 0 : tools::SvRef<SotStorage> xStor = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2647 0 : MakeBlocklist_Imp( *xStor );
2648 : }
2649 : else
2650 0 : delete pNew;
2651 0 : }
2652 : }
2653 0 : catch ( const uno::Exception& )
2654 : {
2655 : }
2656 :
2657 0 : return bRet;
2658 : }
2659 :
2660 : // Delete an entry
2661 0 : bool SvxAutoCorrectLanguageLists::DeleteText( const OUString& rShort )
2662 : {
2663 : // First get the current list!
2664 0 : GetAutocorrWordList();
2665 :
2666 0 : MakeUserStorage_Impl();
2667 :
2668 0 : tools::SvRef<SotStorage> xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE );
2669 0 : bool bRet = xStg.Is() && SVSTREAM_OK == xStg->GetError();
2670 0 : if( bRet )
2671 : {
2672 0 : SvxAutocorrWord aTmp( rShort, rShort );
2673 0 : SvxAutocorrWord *pFnd = pAutocorr_List->FindAndRemove( &aTmp );
2674 0 : if( pFnd )
2675 : {
2676 0 : if( !pFnd->IsTextOnly() )
2677 : {
2678 0 : OUString aName( rShort );
2679 0 : if (xStg->IsOLEStorage())
2680 0 : aName = EncryptBlockName_Imp(aName);
2681 : else
2682 0 : GeneratePackageName ( rShort, aName );
2683 0 : if( xStg->IsContained( aName ) )
2684 : {
2685 0 : xStg->Remove( aName );
2686 0 : bRet = xStg->Commit();
2687 0 : }
2688 :
2689 : }
2690 0 : delete pFnd;
2691 0 : MakeBlocklist_Imp( *xStg );
2692 0 : xStg = 0;
2693 : }
2694 : else
2695 0 : bRet = false;
2696 : }
2697 0 : return bRet;
2698 : }
2699 :
2700 : // Keep the list sorted ...
2701 : struct CompareSvxAutocorrWordList
2702 : {
2703 0 : bool operator()( SvxAutocorrWord* const& lhs, SvxAutocorrWord* const& rhs ) const
2704 : {
2705 0 : CollatorWrapper& rCmp = ::GetCollatorWrapper();
2706 0 : return rCmp.compareString( lhs->GetShort(), rhs->GetShort() ) < 0;
2707 : }
2708 : };
2709 :
2710 : namespace {
2711 :
2712 : typedef std::set<SvxAutocorrWord*, CompareSvxAutocorrWordList> AutocorrWordSetType;
2713 : typedef std::unordered_map<OUString, SvxAutocorrWord*, OUStringHash> AutocorrWordHashType;
2714 :
2715 : }
2716 :
2717 4 : struct SvxAutocorrWordList::Impl
2718 : {
2719 :
2720 : // only one of these contains the data
2721 : mutable AutocorrWordSetType maSet;
2722 : mutable AutocorrWordHashType maHash; // key is 'Short'
2723 :
2724 2 : void DeleteAndDestroyAll()
2725 : {
2726 458 : for (AutocorrWordHashType::const_iterator it = maHash.begin(); it != maHash.end(); ++it)
2727 456 : delete it->second;
2728 2 : maHash.clear();
2729 :
2730 2 : for (AutocorrWordSetType::const_iterator it2 = maSet.begin(); it2 != maSet.end(); ++it2)
2731 0 : delete *it2;
2732 2 : maSet.clear();
2733 2 : }
2734 : };
2735 :
2736 2 : SvxAutocorrWordList::SvxAutocorrWordList() : mpImpl(new Impl) {}
2737 :
2738 2 : SvxAutocorrWordList::~SvxAutocorrWordList()
2739 : {
2740 2 : mpImpl->DeleteAndDestroyAll();
2741 2 : delete mpImpl;
2742 2 : }
2743 :
2744 0 : void SvxAutocorrWordList::DeleteAndDestroyAll()
2745 : {
2746 0 : mpImpl->DeleteAndDestroyAll();
2747 0 : }
2748 :
2749 : // returns true if inserted
2750 457 : bool SvxAutocorrWordList::Insert(SvxAutocorrWord *pWord) const
2751 : {
2752 457 : if ( mpImpl->maSet.empty() ) // use the hash
2753 : {
2754 457 : OUString aShort( pWord->GetShort() );
2755 457 : return mpImpl->maHash.insert( std::pair<OUString, SvxAutocorrWord *>( aShort, pWord ) ).second;
2756 : }
2757 : else
2758 0 : return mpImpl->maSet.insert( pWord ).second;
2759 : }
2760 :
2761 457 : void SvxAutocorrWordList::LoadEntry(const OUString& sWrong, const OUString& sRight, bool bOnlyTxt)
2762 : {
2763 457 : SvxAutocorrWord* pNew = new SvxAutocorrWord( sWrong, sRight, bOnlyTxt );
2764 457 : if( !Insert( pNew ) )
2765 1 : delete pNew;
2766 457 : }
2767 :
2768 0 : bool SvxAutocorrWordList::empty() const
2769 : {
2770 0 : return mpImpl->maHash.empty() && mpImpl->maSet.empty();
2771 : }
2772 :
2773 0 : SvxAutocorrWord *SvxAutocorrWordList::FindAndRemove(SvxAutocorrWord *pWord)
2774 : {
2775 0 : SvxAutocorrWord *pMatch = NULL;
2776 :
2777 0 : if ( mpImpl->maSet.empty() ) // use the hash
2778 : {
2779 0 : AutocorrWordHashType::iterator it = mpImpl->maHash.find( pWord->GetShort() );
2780 0 : if( it != mpImpl->maHash.end() )
2781 : {
2782 0 : pMatch = it->second;
2783 0 : mpImpl->maHash.erase (it);
2784 : }
2785 : }
2786 : else
2787 : {
2788 0 : AutocorrWordSetType::iterator it = mpImpl->maSet.find( pWord );
2789 0 : if( it != mpImpl->maSet.end() )
2790 : {
2791 0 : pMatch = *it;
2792 0 : mpImpl->maSet.erase (it);
2793 : }
2794 : }
2795 0 : return pMatch;
2796 : }
2797 :
2798 : // return the sorted contents - defer sorting until we have to.
2799 0 : SvxAutocorrWordList::Content SvxAutocorrWordList::getSortedContent() const
2800 : {
2801 0 : Content aContent;
2802 :
2803 : // convert from hash to set permanantly
2804 0 : if ( mpImpl->maSet.empty() )
2805 : {
2806 : // This beasty has some O(N log(N)) in a terribly slow ICU collate fn.
2807 0 : for (AutocorrWordHashType::const_iterator it = mpImpl->maHash.begin(); it != mpImpl->maHash.end(); ++it)
2808 0 : mpImpl->maSet.insert( it->second );
2809 0 : mpImpl->maHash.clear();
2810 : }
2811 0 : for (AutocorrWordSetType::const_iterator it = mpImpl->maSet.begin(); it != mpImpl->maSet.end(); ++it)
2812 0 : aContent.push_back( *it );
2813 :
2814 0 : return aContent;
2815 : }
2816 :
2817 1365 : const SvxAutocorrWord* SvxAutocorrWordList::WordMatches(const SvxAutocorrWord *pFnd,
2818 : const OUString &rTxt,
2819 : sal_Int32 &rStt,
2820 : sal_Int32 nEndPos) const
2821 : {
2822 1365 : const OUString& rChk = pFnd->GetShort();
2823 :
2824 1365 : sal_Int32 left_wildcard = rChk.startsWith( ".*" ) ? 2 : 0; // ".*word" pattern?
2825 1365 : sal_Int32 right_wildcard = rChk.endsWith( ".*" ) ? 2 : 0; // "word.*" pattern?
2826 1365 : sal_Int32 nSttWdPos = nEndPos;
2827 :
2828 : // direct replacement of keywords surrounded by colons (for example, ":name:")
2829 2730 : bool bColonNameColon = rTxt.getLength() > nEndPos &&
2830 1365 : rTxt[nEndPos] == ':' && rChk[0] == ':' && rChk.endsWith(":");
2831 1365 : if ( nEndPos + (bColonNameColon ? 1 : 0) >= rChk.getLength() - left_wildcard - right_wildcard )
2832 : {
2833 :
2834 1068 : bool bWasWordDelim = false;
2835 1068 : sal_Int32 nCalcStt = nEndPos - rChk.getLength() + left_wildcard;
2836 1068 : if (bColonNameColon)
2837 0 : nCalcStt++;
2838 2175 : if( !right_wildcard && ( !nCalcStt || nCalcStt == rStt || left_wildcard || bColonNameColon ||
2839 1080 : ( nCalcStt < rStt &&
2840 398 : IsWordDelim( rTxt[ nCalcStt - 1 ] ))) )
2841 : {
2842 425 : TransliterationWrapper& rCmp = GetIgnoreTranslWrapper();
2843 425 : OUString sWord = rTxt.copy(nCalcStt, rChk.getLength() - left_wildcard);
2844 425 : if( (!left_wildcard && rCmp.isEqual( rChk, sWord )) || (left_wildcard && rCmp.isEqual( rChk.copy(left_wildcard), sWord) ))
2845 : {
2846 3 : rStt = nCalcStt;
2847 3 : if (!left_wildcard)
2848 : {
2849 : // fdo#33899 avoid "1/2", "1/3".. to be replaced by fractions in dates, eg. 1/2/14
2850 3 : if (rTxt.getLength() > nEndPos && rTxt[nEndPos] == '/' && rChk.indexOf('/') != -1)
2851 3 : return NULL;
2852 3 : return pFnd;
2853 : }
2854 : // get the first word delimiter position before the matching ".*word" pattern
2855 0 : while( rStt && !(bWasWordDelim = IsWordDelim( rTxt[ --rStt ])))
2856 : ;
2857 0 : if (bWasWordDelim) rStt++;
2858 0 : OUString left_pattern = rTxt.copy(rStt, nEndPos - rStt - rChk.getLength() + left_wildcard);
2859 : // avoid double spaces before simple "word" replacement
2860 0 : left_pattern += (left_pattern.getLength() == 0 && pFnd->GetLong()[0] == 0x20) ? pFnd->GetLong().copy(1) : pFnd->GetLong();
2861 0 : SvxAutocorrWord* pNew = new SvxAutocorrWord(rTxt.copy(rStt, nEndPos - rStt), left_pattern);
2862 0 : if( Insert( pNew ) ) return pNew; else delete pNew;
2863 422 : }
2864 : } else
2865 : // match "word.*" or ".*word.*" patterns, eg. "i18n.*", ".*---.*", TODO: add transliteration support
2866 643 : if ( right_wildcard )
2867 : {
2868 :
2869 6 : OUString sTmp( rChk.copy( left_wildcard, rChk.getLength() - left_wildcard - right_wildcard ) );
2870 : // Get the last word delimiter position
2871 : bool not_suffix;
2872 :
2873 6 : while( nSttWdPos && !(bWasWordDelim = IsWordDelim( rTxt[ --nSttWdPos ])))
2874 : ;
2875 : // search the first occurrence (with a left word delimitation, if needed)
2876 6 : sal_Int32 nFndPos = -1;
2877 6 : do {
2878 6 : nFndPos = rTxt.indexOf( sTmp, nFndPos + 1);
2879 6 : not_suffix = (bWasWordDelim && (nSttWdPos >= nFndPos + sTmp.getLength()));
2880 6 : } while ( nFndPos != -1 && (!(left_wildcard || (!left_wildcard && (!nFndPos || IsWordDelim( rTxt[ nFndPos - 1 ])))) || not_suffix));
2881 :
2882 6 : if ( nFndPos != -1 )
2883 : {
2884 0 : sal_Int32 extra_repl = nFndPos + sTmp.getLength() > nEndPos ? 1: 0; // for patterns with terminating characters, eg. "a:"
2885 :
2886 0 : if ( left_wildcard )
2887 : {
2888 : // get the first word delimiter position before the matching ".*word.*" pattern
2889 0 : while( nFndPos && !(bWasWordDelim = IsWordDelim( rTxt[ --nFndPos ])))
2890 : ;
2891 0 : if (bWasWordDelim) nFndPos++;
2892 : }
2893 0 : if (nEndPos + extra_repl <= nFndPos)
2894 : {
2895 0 : return 0;
2896 : }
2897 : // store matching pattern and its replacement as a new list item, eg. "i18ns" -> "internationalizations"
2898 0 : OUString aShort = rTxt.copy(nFndPos, nEndPos - nFndPos + extra_repl);
2899 :
2900 0 : OUString aLong;
2901 0 : rStt = nFndPos;
2902 0 : if ( !left_wildcard )
2903 : {
2904 0 : sal_Int32 siz = nEndPos - nFndPos - sTmp.getLength();
2905 0 : aLong = pFnd->GetLong() + (siz > 0 ? rTxt.copy(nFndPos + sTmp.getLength(), siz) : "");
2906 : } else {
2907 0 : OUStringBuffer buf;
2908 0 : do {
2909 0 : nSttWdPos = rTxt.indexOf( sTmp, nFndPos);
2910 0 : if (nSttWdPos != -1)
2911 : {
2912 0 : buf.append(rTxt.copy(nFndPos, nSttWdPos - nFndPos)).append(pFnd->GetLong());
2913 0 : nFndPos = nSttWdPos + sTmp.getLength();
2914 : }
2915 : } while (nSttWdPos != -1);
2916 0 : if (nEndPos - nFndPos > extra_repl) buf.append(rTxt.copy(nFndPos, nEndPos - nFndPos));
2917 0 : aLong = buf.makeStringAndClear();
2918 : }
2919 0 : SvxAutocorrWord* pNew = new SvxAutocorrWord(aShort, aLong);
2920 0 : if ( Insert( pNew ) )
2921 : {
2922 0 : if ( IsWordDelim(rTxt[nEndPos]) ) return pNew;
2923 0 : } else delete pNew;
2924 6 : }
2925 : }
2926 : }
2927 1362 : return NULL;
2928 : }
2929 :
2930 6 : const SvxAutocorrWord* SvxAutocorrWordList::SearchWordsInList(const OUString& rTxt, sal_Int32& rStt,
2931 : sal_Int32 nEndPos) const
2932 : {
2933 1368 : for (AutocorrWordHashType::const_iterator it = mpImpl->maHash.begin(); it != mpImpl->maHash.end(); ++it)
2934 : {
2935 1365 : if( const SvxAutocorrWord *aTmp = WordMatches( it->second, rTxt, rStt, nEndPos ) )
2936 3 : return aTmp;
2937 : }
2938 :
2939 3 : for (AutocorrWordSetType::const_iterator it2 = mpImpl->maSet.begin(); it2 != mpImpl->maSet.end(); ++it2)
2940 : {
2941 0 : if( const SvxAutocorrWord *aTmp = WordMatches( *it2, rTxt, rStt, nEndPos ) )
2942 0 : return aTmp;
2943 : }
2944 3 : return 0;
2945 : }
2946 :
2947 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|