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