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