Bump version to 4.1-6
[LibreOffice.git] / editeng / source / misc / svxacorr.cxx
bloba5e227902f8509c7e09de8c5069392c84678637a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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 <sot/storinfo.hxx>
26 #include <svl/fstathelper.hxx>
27 #include <svtools/helpopt.hxx>
28 #include <svl/urihelper.hxx>
29 #include <unotools/charclass.hxx>
30 #include <com/sun/star/i18n/UnicodeType.hpp>
31 #include <unotools/collatorwrapper.hxx>
32 #include <com/sun/star/i18n/CollatorOptions.hpp>
33 #include <com/sun/star/i18n/UnicodeScript.hpp>
34 #include <com/sun/star/i18n/OrdinalSuffix.hpp>
35 #include <unotools/localedatawrapper.hxx>
36 #include <unotools/transliterationwrapper.hxx>
37 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
38 #include <com/sun/star/io/XActiveDataSource.hpp>
39 #include <comphelper/componentcontext.hxx>
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 #include <rtl/logfile.hxx>
66 using namespace ::com::sun::star::ucb;
67 using namespace ::com::sun::star::uno;
68 using namespace ::com::sun::star;
69 using namespace ::xmloff::token;
70 using namespace ::rtl;
71 using namespace ::utl;
73 static const int C_NONE = 0x00;
74 static const int C_FULL_STOP = 0x01;
75 static const int C_EXCLAMATION_MARK = 0x02;
76 static const int C_QUESTION_MARK = 0x04;
77 static const sal_Unicode cNonBreakingSpace = 0xA0;
79 static const sal_Char pImplAutocorr_ListStr[] = "DocumentList";
80 static const sal_Char pXMLImplWrdStt_ExcptLstStr[] = "WordExceptList.xml";
81 static const sal_Char pXMLImplCplStt_ExcptLstStr[] = "SentenceExceptList.xml";
82 static const sal_Char pXMLImplAutocorr_ListStr[] = "DocumentList.xml";
84 static const sal_Char
85 /* also at these beginnings - Brackets and all kinds of begin characters */
86 sImplSttSkipChars[] = "\"\'([{\x83\x84\x89\x91\x92\x93\x94",
87 /* also at these ends - Brackets and all kinds of begin characters */
88 sImplEndSkipChars[] = "\"\')]}\x83\x84\x89\x91\x92\x93\x94";
90 // These characters are allowed in words: (for FnCptlSttSntnc)
91 static const sal_Char sImplWordChars[] = "-'";
93 void EncryptBlockName_Imp( String& rName );
95 TYPEINIT0(SvxAutoCorrect)
97 typedef SvxAutoCorrectLanguageLists* SvxAutoCorrectLanguageListsPtr;
99 static inline int IsWordDelim( const sal_Unicode c )
101 return ' ' == c || '\t' == c || 0x0a == c ||
102 cNonBreakingSpace == c || 0x2011 == c || 0x1 == c;
105 static inline int IsLowerLetter( sal_Int32 nCharType )
107 return CharClass::isLetterType( nCharType ) &&
108 0 == ( ::com::sun::star::i18n::KCharacterType::UPPER & nCharType);
111 static inline int IsUpperLetter( sal_Int32 nCharType )
113 return CharClass::isLetterType( nCharType ) &&
114 0 == ( ::com::sun::star::i18n::KCharacterType::LOWER & nCharType);
117 bool lcl_IsUnsupportedUnicodeChar( CharClass& rCC, const String& rTxt,
118 xub_StrLen nStt, xub_StrLen nEnd )
120 for( ; nStt < nEnd; ++nStt )
122 short nScript = rCC.getScript( rTxt, nStt );
123 switch( nScript )
125 case ::com::sun::star::i18n::UnicodeScript_kCJKRadicalsSupplement:
126 case ::com::sun::star::i18n::UnicodeScript_kHangulJamo:
127 case ::com::sun::star::i18n::UnicodeScript_kCJKSymbolPunctuation:
128 case ::com::sun::star::i18n::UnicodeScript_kHiragana:
129 case ::com::sun::star::i18n::UnicodeScript_kKatakana:
130 case ::com::sun::star::i18n::UnicodeScript_kHangulCompatibilityJamo:
131 case ::com::sun::star::i18n::UnicodeScript_kEnclosedCJKLetterMonth:
132 case ::com::sun::star::i18n::UnicodeScript_kCJKCompatibility:
133 case ::com::sun::star::i18n::UnicodeScript_k_CJKUnifiedIdeographsExtensionA:
134 case ::com::sun::star::i18n::UnicodeScript_kCJKUnifiedIdeograph:
135 case ::com::sun::star::i18n::UnicodeScript_kHangulSyllable:
136 case ::com::sun::star::i18n::UnicodeScript_kCJKCompatibilityIdeograph:
137 case ::com::sun::star::i18n::UnicodeScript_kHalfwidthFullwidthForm:
138 return true;
139 default: ; //do nothing
142 return false;
145 static sal_Bool lcl_IsSymbolChar( CharClass& rCC, const String& rTxt,
146 xub_StrLen nStt, xub_StrLen nEnd )
148 for( ; nStt < nEnd; ++nStt )
150 if( ::com::sun::star::i18n::UnicodeType::PRIVATE_USE ==
151 rCC.getType( rTxt, nStt ))
152 return sal_True;
154 return sal_False;
157 static sal_Bool lcl_IsInAsciiArr( const sal_Char* pArr, const sal_Unicode c )
159 sal_Bool bRet = sal_False;
160 for( ; *pArr; ++pArr )
161 if( *pArr == c )
163 bRet = sal_True;
164 break;
166 return bRet;
169 SvxAutoCorrDoc::~SvxAutoCorrDoc()
173 // Called by the functions:
174 // - FnCptlSttWrd
175 // - FnCptlSttSntnc
176 // after the exchange of characters. Then the words, if necessary, can be inserted
177 // into the exception list.
178 void SvxAutoCorrDoc::SaveCpltSttWord( sal_uLong, xub_StrLen, const String&,
179 sal_Unicode )
183 LanguageType SvxAutoCorrDoc::GetLanguage( xub_StrLen , sal_Bool ) const
185 return LANGUAGE_SYSTEM;
188 static const LanguageTag& GetAppLang()
190 return Application::GetSettings().GetLanguageTag();
192 static LocaleDataWrapper& GetLocaleDataWrapper( sal_uInt16 nLang )
194 static LocaleDataWrapper aLclDtWrp( GetAppLang() );
195 LanguageTag aLcl( nLang );
196 const LanguageTag& rLcl = aLclDtWrp.getLoadedLanguageTag();
197 if( aLcl != rLcl )
198 aLclDtWrp.setLanguageTag( aLcl );
199 return aLclDtWrp;
201 static TransliterationWrapper& GetIgnoreTranslWrapper()
203 static int bIsInit = 0;
204 static TransliterationWrapper aWrp( ::comphelper::getProcessComponentContext(),
205 ::com::sun::star::i18n::TransliterationModules_IGNORE_KANA |
206 ::com::sun::star::i18n::TransliterationModules_IGNORE_WIDTH );
207 if( !bIsInit )
209 aWrp.loadModuleIfNeeded( GetAppLang().getLanguageType() );
210 bIsInit = 1;
212 return aWrp;
214 static CollatorWrapper& GetCollatorWrapper()
216 static int bIsInit = 0;
217 static CollatorWrapper aCollWrp( ::comphelper::getProcessComponentContext() );
218 if( !bIsInit )
220 aCollWrp.loadDefaultCollator( GetAppLang().getLocale(), 0 );
221 bIsInit = 1;
223 return aCollWrp;
226 static void lcl_ClearTable(boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists>& rLangTable)
228 rLangTable.clear();
231 sal_Bool SvxAutoCorrect::IsAutoCorrectChar( sal_Unicode cChar )
233 return cChar == '\0' || cChar == '\t' || cChar == 0x0a ||
234 cChar == ' ' || cChar == '\'' || cChar == '\"' ||
235 cChar == '*' || cChar == '_' || cChar == '%' ||
236 cChar == '.' || cChar == ',' || cChar == ';' ||
237 cChar == ':' || cChar == '?' || cChar == '!' ||
238 cChar == '/' || cChar == '-';
241 sal_Bool SvxAutoCorrect::NeedsHardspaceAutocorr( sal_Unicode cChar )
243 return cChar == '%' || cChar == ';' || cChar == ':' || cChar == '?' || cChar == '!' ||
244 cChar == '/' /*case for the urls exception*/;
247 long SvxAutoCorrect::GetDefaultFlags()
249 long nRet = Autocorrect
250 | CptlSttSntnc
251 | CptlSttWrd
252 | ChgOrdinalNumber
253 | ChgToEnEmDash
254 | AddNonBrkSpace
255 | ChgWeightUnderl
256 | SetINetAttr
257 | ChgQuotes
258 | SaveWordCplSttLst
259 | SaveWordWrdSttLst
260 | CorrectCapsLock;
261 LanguageType eLang = GetAppLang().getLanguageType();
262 switch( eLang )
264 case LANGUAGE_ENGLISH:
265 case LANGUAGE_ENGLISH_US:
266 case LANGUAGE_ENGLISH_UK:
267 case LANGUAGE_ENGLISH_AUS:
268 case LANGUAGE_ENGLISH_CAN:
269 case LANGUAGE_ENGLISH_NZ:
270 case LANGUAGE_ENGLISH_EIRE:
271 case LANGUAGE_ENGLISH_SAFRICA:
272 case LANGUAGE_ENGLISH_JAMAICA:
273 case LANGUAGE_ENGLISH_CARRIBEAN:
274 nRet &= ~(ChgQuotes|ChgSglQuotes);
275 break;
277 return nRet;
281 SvxAutoCorrect::SvxAutoCorrect( const String& rShareAutocorrFile,
282 const String& rUserAutocorrFile )
283 : sShareAutoCorrFile( rShareAutocorrFile ),
284 sUserAutoCorrFile( rUserAutocorrFile ),
285 pLangTable( new boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists> ),
286 pCharClass( 0 ), bRunNext( false ),
287 cStartDQuote( 0 ), cEndDQuote( 0 ), cStartSQuote( 0 ), cEndSQuote( 0 )
289 nFlags = SvxAutoCorrect::GetDefaultFlags();
291 cEmDash = 0x2014;
292 cEnDash = 0x2013;
295 SvxAutoCorrect::SvxAutoCorrect( const SvxAutoCorrect& rCpy )
296 : sShareAutoCorrFile( rCpy.sShareAutoCorrFile ),
297 sUserAutoCorrFile( rCpy.sUserAutoCorrFile ),
299 aSwFlags( rCpy.aSwFlags ),
301 pLangTable( new boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists> ),
302 pCharClass( 0 ), bRunNext( false ),
304 nFlags( rCpy.nFlags & ~(ChgWordLstLoad|CplSttLstLoad|WrdSttLstLoad)),
305 cStartDQuote( rCpy.cStartDQuote ), cEndDQuote( rCpy.cEndDQuote ),
306 cStartSQuote( rCpy.cStartSQuote ), cEndSQuote( rCpy.cEndSQuote ),
307 cEmDash( rCpy.cEmDash ), cEnDash( rCpy.cEnDash )
312 SvxAutoCorrect::~SvxAutoCorrect()
314 lcl_ClearTable(*pLangTable);
315 delete pLangTable;
316 delete pCharClass;
319 void SvxAutoCorrect::_GetCharClass( LanguageType eLang )
321 delete pCharClass;
322 pCharClass = new CharClass( LanguageTag( eLang ));
323 eCharClassLang = eLang;
326 void SvxAutoCorrect::SetAutoCorrFlag( long nFlag, sal_Bool bOn )
328 long nOld = nFlags;
329 nFlags = bOn ? nFlags | nFlag
330 : nFlags & ~nFlag;
332 if( !bOn )
334 if( (nOld & CptlSttSntnc) != (nFlags & CptlSttSntnc) )
335 nFlags &= ~CplSttLstLoad;
336 if( (nOld & CptlSttWrd) != (nFlags & CptlSttWrd) )
337 nFlags &= ~WrdSttLstLoad;
338 if( (nOld & Autocorrect) != (nFlags & Autocorrect) )
339 nFlags &= ~ChgWordLstLoad;
344 // Two capital letters at the beginning of word?
345 sal_Bool SvxAutoCorrect::FnCptlSttWrd( SvxAutoCorrDoc& rDoc, const String& rTxt,
346 xub_StrLen nSttPos, xub_StrLen nEndPos,
347 LanguageType eLang )
349 sal_Bool bRet = sal_False;
350 CharClass& rCC = GetCharClass( eLang );
352 // Delete all non alphanumeric. Test the characters at the beginning/end of
353 // the word ( recognizes: "(min.", "/min.", and so on.)
354 for( ; nSttPos < nEndPos; ++nSttPos )
355 if( rCC.isLetterNumeric( rTxt, nSttPos ))
356 break;
357 for( ; nSttPos < nEndPos; --nEndPos )
358 if( rCC.isLetterNumeric( rTxt, nEndPos - 1 ))
359 break;
361 // Is the word a compounded word separated by delimiters?
362 // If so, keep track of all delimiters so each constituent
363 // word can be checked for two initial capital letters.
364 xub_StrLen n = 0;
365 std::deque<xub_StrLen> aDelimiters;
367 // Always check for two capitals at the beginning
368 // of the entire word, so start at nSttPos.
369 aDelimiters.push_back(nSttPos);
371 // Find all compound word delimiters
372 for (n = nSttPos; n < nEndPos; n++)
374 if (IsAutoCorrectChar(rTxt.GetChar( n )))
376 aDelimiters.push_back( n + 1 ); // Get position of char after delimiter
381 // Decide where to put the terminating delimiter.
382 // If the last AutoCorrect char was a newline, then the AutoCorrect
383 // char will not be included in rTxt.
384 // If the last AutoCorrect char was not a newline, then the AutoCorrect
385 // character will be the last character in rTxt.
386 if (!IsAutoCorrectChar(rTxt.GetChar(nEndPos-1)))
387 aDelimiters.push_back(nEndPos);
389 // Iterate through the word and all words that compose it.
390 n = aDelimiters.size();
392 // Two capital letters at the beginning of word?
393 for(n = 0; n < aDelimiters.size() - 1; n++)
395 nSttPos = aDelimiters[n];
396 nEndPos = aDelimiters[n + 1];
398 if( nSttPos+2 < nEndPos &&
399 IsUpperLetter( rCC.getCharacterType( rTxt, nSttPos )) &&
400 IsUpperLetter( rCC.getCharacterType( rTxt, ++nSttPos )) &&
401 // Is the third character a lower case
402 IsLowerLetter( rCC.getCharacterType( rTxt, nSttPos +1 )) &&
403 // Do not replace special attributes
404 0x1 != rTxt.GetChar( nSttPos ) && 0x2 != rTxt.GetChar( nSttPos ))
406 // test if the word is in an exception list
407 String sWord( rTxt.Copy( nSttPos - 1, nEndPos - nSttPos + 1 ));
408 if( !FindInWrdSttExceptList(eLang, sWord) )
410 // Check that word isn't correctly spelled before correcting:
411 ::com::sun::star::uno::Reference<
412 ::com::sun::star::linguistic2::XSpellChecker1 > xSpeller =
413 SvxGetSpellChecker();
414 if( xSpeller->hasLanguage(eLang) )
416 Sequence< ::com::sun::star::beans::PropertyValue > aEmptySeq;
417 if (!xSpeller->spell(sWord, eLang, aEmptySeq).is())
419 return false;
422 sal_Unicode cSave = rTxt.GetChar( nSttPos );
423 OUString sChar( cSave );
424 sChar = rCC.lowercase( sChar );
425 if( sChar[0] != cSave && rDoc.ReplaceRange( nSttPos, 1, sChar ))
427 if( SaveWordWrdSttLst & nFlags )
428 rDoc.SaveCpltSttWord( CptlSttWrd, nSttPos, sWord, cSave );
429 bRet = sal_True;
434 return bRet;
438 sal_Bool SvxAutoCorrect::FnChgOrdinalNumber(
439 SvxAutoCorrDoc& rDoc, const String& rTxt,
440 xub_StrLen nSttPos, xub_StrLen nEndPos,
441 LanguageType eLang )
443 // 1st, 2nd, 3rd, 4 - 0th
444 // 201th or 201st
445 // 12th or 12nd
446 CharClass& rCC = GetCharClass( eLang );
447 sal_Bool bChg = sal_False;
449 for( ; nSttPos < nEndPos; ++nSttPos )
450 if( !lcl_IsInAsciiArr( sImplSttSkipChars, rTxt.GetChar( nSttPos ) ))
451 break;
452 for( ; nSttPos < nEndPos; --nEndPos )
453 if( !lcl_IsInAsciiArr( sImplEndSkipChars, rTxt.GetChar( nEndPos - 1 ) ))
454 break;
457 // Get the last number in the string to check
458 xub_StrLen nNumEnd = nEndPos;
459 bool foundEnd = false;
460 bool validNumber = true;
461 xub_StrLen i = nEndPos;
463 while ( i > nSttPos )
465 i--;
466 bool isDigit = rCC.isDigit( rTxt, i );
467 if ( foundEnd )
468 validNumber |= isDigit;
470 if ( isDigit && !foundEnd )
472 foundEnd = true;
473 nNumEnd = i;
477 if ( foundEnd && validNumber ) {
478 sal_Int32 nNum = rTxt.Copy( nSttPos, nNumEnd - nSttPos + 1 ).ToInt32( );
480 // Check if the characters after that number correspond to the ordinal suffix
481 uno::Reference< i18n::XOrdinalSuffix > xOrdSuffix
482 = i18n::OrdinalSuffix::create( comphelper::getProcessComponentContext() );
484 uno::Sequence< OUString > aSuffixes = xOrdSuffix->getOrdinalSuffix( nNum, rCC.getLanguageTag().getLocale( ) );
485 for ( sal_Int32 nSuff = 0; nSuff < aSuffixes.getLength(); nSuff++ )
487 String sSuffix( aSuffixes[ nSuff ] );
488 String sEnd = rTxt.Copy( nNumEnd + 1, nEndPos - nNumEnd - 1 );
490 if ( sSuffix == sEnd )
492 // Check if the ordinal suffix has to be set as super script
493 if ( rCC.isLetter( sSuffix ) )
495 // Do the change
496 SvxEscapementItem aSvxEscapementItem( DFLT_ESC_AUTO_SUPER,
497 DFLT_ESC_PROP, SID_ATTR_CHAR_ESCAPEMENT );
498 rDoc.SetAttr( nNumEnd + 1 , nEndPos,
499 SID_ATTR_CHAR_ESCAPEMENT,
500 aSvxEscapementItem);
505 return bChg;
509 sal_Bool SvxAutoCorrect::FnChgToEnEmDash(
510 SvxAutoCorrDoc& rDoc, const String& rTxt,
511 xub_StrLen nSttPos, xub_StrLen nEndPos,
512 LanguageType eLang )
514 sal_Bool bRet = sal_False;
515 CharClass& rCC = GetCharClass( eLang );
516 if (eLang == LANGUAGE_SYSTEM)
517 eLang = GetAppLang().getLanguageType();
518 bool bAlwaysUseEmDash = (cEmDash && (eLang == LANGUAGE_RUSSIAN || eLang == LANGUAGE_UKRAINIAN));
520 // replace " - " or " --" with "enDash"
521 if( cEnDash && 1 < nSttPos && 1 <= nEndPos - nSttPos )
523 sal_Unicode cCh = rTxt.GetChar( nSttPos );
524 if( '-' == cCh )
526 if( ' ' == rTxt.GetChar( nSttPos-1 ) &&
527 '-' == rTxt.GetChar( nSttPos+1 ))
529 xub_StrLen n;
530 for( n = nSttPos+2; n < nEndPos && lcl_IsInAsciiArr(
531 sImplSttSkipChars,(cCh = rTxt.GetChar( n )));
532 ++n )
535 // found: " --[<AnySttChars>][A-z0-9]
536 if( rCC.isLetterNumeric( OUString(cCh) ) )
538 for( n = nSttPos-1; n && lcl_IsInAsciiArr(
539 sImplEndSkipChars,(cCh = rTxt.GetChar( --n ))); )
542 // found: "[A-z0-9][<AnyEndChars>] --[<AnySttChars>][A-z0-9]
543 if( rCC.isLetterNumeric( OUString(cCh) ))
545 rDoc.Delete( nSttPos, nSttPos + 2 );
546 rDoc.Insert( nSttPos, bAlwaysUseEmDash ? OUString(cEmDash) : OUString(cEnDash) );
547 bRet = sal_True;
552 else if( 3 < nSttPos &&
553 ' ' == rTxt.GetChar( nSttPos-1 ) &&
554 '-' == rTxt.GetChar( nSttPos-2 ))
556 xub_StrLen n, nLen = 1, nTmpPos = nSttPos - 2;
557 if( '-' == ( cCh = rTxt.GetChar( nTmpPos-1 )) )
559 --nTmpPos;
560 ++nLen;
561 cCh = rTxt.GetChar( nTmpPos-1 );
563 if( ' ' == cCh )
565 for( n = nSttPos; n < nEndPos && lcl_IsInAsciiArr(
566 sImplSttSkipChars,(cCh = rTxt.GetChar( n )));
567 ++n )
570 // found: " - [<AnySttChars>][A-z0-9]
571 if( rCC.isLetterNumeric( OUString(cCh) ) )
573 cCh = ' ';
574 for( n = nTmpPos-1; n && lcl_IsInAsciiArr(
575 sImplEndSkipChars,(cCh = rTxt.GetChar( --n ))); )
577 // found: "[A-z0-9][<AnyEndChars>] - [<AnySttChars>][A-z0-9]
578 if( rCC.isLetterNumeric( OUString(cCh) ))
580 rDoc.Delete( nTmpPos, nTmpPos + nLen );
581 rDoc.Insert( nTmpPos, bAlwaysUseEmDash ? OUString(cEmDash) : OUString(cEnDash) );
582 bRet = sal_True;
589 // Replace [A-z0-9]--[A-z0-9] double dash with "emDash" or "enDash".
590 // Finnish and Hungarian use enDash instead of emDash.
591 bool bEnDash = (eLang == LANGUAGE_HUNGARIAN || eLang == LANGUAGE_FINNISH);
592 if( ((cEmDash && !bEnDash) || (cEnDash && bEnDash)) && 4 <= nEndPos - nSttPos )
594 String sTmp( rTxt.Copy( nSttPos, nEndPos - nSttPos ) );
595 xub_StrLen nFndPos = sTmp.SearchAscii( "--" );
596 if( STRING_NOTFOUND != nFndPos && nFndPos &&
597 nFndPos + 2 < sTmp.Len() &&
598 ( rCC.isLetterNumeric( sTmp, nFndPos - 1 ) ||
599 lcl_IsInAsciiArr( sImplEndSkipChars, rTxt.GetChar( nFndPos - 1 ) )) &&
600 ( rCC.isLetterNumeric( sTmp, nFndPos + 2 ) ||
601 lcl_IsInAsciiArr( sImplSttSkipChars, rTxt.GetChar( nFndPos + 2 ) )))
603 nSttPos = nSttPos + nFndPos;
604 rDoc.Delete( nSttPos, nSttPos + 2 );
605 rDoc.Insert( nSttPos, (bEnDash ? OUString(cEnDash) : OUString(cEmDash)) );
606 bRet = sal_True;
609 return bRet;
612 #ifdef _MSC_VER
613 #pragma warning(push)
614 #pragma warning(disable: 4706) // assignment within conditional expression
615 #endif
617 sal_Bool SvxAutoCorrect::FnAddNonBrkSpace(
618 SvxAutoCorrDoc& rDoc, const String& rTxt,
619 xub_StrLen, xub_StrLen nEndPos,
620 LanguageType eLang )
622 bool bRet = false;
624 CharClass& rCC = GetCharClass( eLang );
625 const lang::Locale rLocale = rCC.getLanguageTag().getLocale( );
627 if ( rLocale.Language == OUString( "fr" ) )
629 bool bFrCA = rLocale.Country == OUString( "CA" );
630 OUString allChars = OUString( ":;?!%" );
631 OUString chars( allChars );
632 if ( bFrCA )
633 chars = OUString( ":" );
635 sal_Unicode cChar = rTxt.GetChar( nEndPos );
636 bool bHasSpace = chars.indexOf( cChar ) != -1;
637 bool bIsSpecial = allChars.indexOf( cChar ) != -1;
638 if ( bIsSpecial )
640 // Get the last word delimiter position
641 xub_StrLen nSttWdPos = nEndPos;
642 bool bWasWordDelim = false;
643 while( nSttWdPos && !(bWasWordDelim = IsWordDelim( rTxt.GetChar( --nSttWdPos ))))
646 if(INetURLObject::CompareProtocolScheme(rTxt.Copy(nSttWdPos + (bWasWordDelim ? 1 : 0), nEndPos - nSttWdPos + 1)) != INET_PROT_NOT_VALID) {
647 return sal_False;
651 // Check the presence of "://" in the word
652 xub_StrLen nStrPos = rTxt.Search( OUString( "://" ), nSttWdPos + 1 );
653 if ( STRING_NOTFOUND == nStrPos && nEndPos > 0 )
655 // Check the previous char
656 sal_Unicode cPrevChar = rTxt.GetChar( nEndPos - 1 );
657 if ( ( chars.indexOf( cPrevChar ) == -1 ) && cPrevChar != '\t' )
659 // Remove any previous normal space
660 xub_StrLen nPos = nEndPos - 1;
661 while ( cPrevChar == ' ' || cPrevChar == cNonBreakingSpace )
663 if ( nPos == 0 ) break;
664 nPos--;
665 cPrevChar = rTxt.GetChar( nPos );
668 nPos++;
669 if ( nEndPos - nPos > 0 )
670 rDoc.Delete( nPos, nEndPos );
672 // Add the non-breaking space at the end pos
673 if ( bHasSpace )
674 rDoc.Insert( nPos, OUString(cNonBreakingSpace) );
675 bRunNext = true;
676 bRet = true;
678 else if ( chars.indexOf( cPrevChar ) != -1 )
679 bRunNext = true;
682 else if ( cChar == '/' && nEndPos > 1 && rTxt.Len() > (nEndPos - 1) )
684 // Remove the hardspace right before to avoid formatting URLs
685 sal_Unicode cPrevChar = rTxt.GetChar( nEndPos - 1 );
686 sal_Unicode cMaybeSpaceChar = rTxt.GetChar( nEndPos - 2 );
687 if ( cPrevChar == ':' && cMaybeSpaceChar == cNonBreakingSpace )
689 rDoc.Delete( nEndPos - 2, nEndPos - 1 );
690 bRet = true;
695 return bRet;
698 #ifdef _MSC_VER
699 #pragma warning(pop)
700 #endif
702 sal_Bool SvxAutoCorrect::FnSetINetAttr( SvxAutoCorrDoc& rDoc, const String& rTxt,
703 xub_StrLen nSttPos, xub_StrLen nEndPos,
704 LanguageType eLang )
706 sal_Int32 nStart(nSttPos);
707 sal_Int32 nEnd(nEndPos);
709 String sURL( URIHelper::FindFirstURLInText( rTxt, nStart, nEnd,
710 GetCharClass( eLang ) ));
711 nSttPos = (xub_StrLen)nStart;
712 nEndPos = (xub_StrLen)nEnd;
713 sal_Bool bRet = 0 != sURL.Len();
714 if( bRet ) // also Attribut setzen:
715 rDoc.SetINetAttr( nSttPos, nEndPos, sURL );
716 return bRet;
720 sal_Bool SvxAutoCorrect::FnChgWeightUnderl( SvxAutoCorrDoc& rDoc, const String& rTxt,
721 xub_StrLen, xub_StrLen nEndPos,
722 LanguageType eLang )
724 // Condition:
725 // at the beginning: _ or * after Space with the folloeing !Space
726 // at the end: _ or * before Space (word delimiter?)
728 sal_Unicode c, cInsChar = rTxt.GetChar( nEndPos ); // underline or bold
729 if( ++nEndPos != rTxt.Len() &&
730 !IsWordDelim( rTxt.GetChar( nEndPos ) ) )
731 return sal_False;
733 --nEndPos;
735 sal_Bool bAlphaNum = sal_False;
736 xub_StrLen nPos = nEndPos, nFndPos = STRING_NOTFOUND;
737 CharClass& rCC = GetCharClass( eLang );
739 while( nPos )
741 switch( c = rTxt.GetChar( --nPos ) )
743 case '_':
744 case '*':
745 if( c == cInsChar )
747 if( bAlphaNum && nPos+1 < nEndPos && ( !nPos ||
748 IsWordDelim( rTxt.GetChar( nPos-1 ))) &&
749 !IsWordDelim( rTxt.GetChar( nPos+1 )))
750 nFndPos = nPos;
751 else
752 // Condition is not satisfied, so cancel
753 nFndPos = STRING_NOTFOUND;
754 nPos = 0;
756 break;
757 default:
758 if( !bAlphaNum )
759 bAlphaNum = rCC.isLetterNumeric( rTxt, nPos );
763 if( STRING_NOTFOUND != nFndPos )
765 // first delete the Character at the end - this allows insertion
766 // of an empty hint in SetAttr which would be removed by Delete
767 // (fdo#62536, AUTOFMT in Writer)
768 rDoc.Delete( nEndPos, nEndPos + 1 );
769 rDoc.Delete( nFndPos, nFndPos + 1 );
770 // Span the Attribute over the area
771 // the end.
772 if( '*' == cInsChar ) // Bold
774 SvxWeightItem aSvxWeightItem( WEIGHT_BOLD, SID_ATTR_CHAR_WEIGHT );
775 rDoc.SetAttr( nFndPos, nEndPos - 1,
776 SID_ATTR_CHAR_WEIGHT,
777 aSvxWeightItem);
779 else // underline
781 SvxUnderlineItem aSvxUnderlineItem( UNDERLINE_SINGLE, SID_ATTR_CHAR_UNDERLINE );
782 rDoc.SetAttr( nFndPos, nEndPos - 1,
783 SID_ATTR_CHAR_UNDERLINE,
784 aSvxUnderlineItem);
788 return STRING_NOTFOUND != nFndPos;
792 sal_Bool SvxAutoCorrect::FnCptlSttSntnc( SvxAutoCorrDoc& rDoc,
793 const String& rTxt, sal_Bool bNormalPos,
794 xub_StrLen nSttPos, xub_StrLen nEndPos,
795 LanguageType eLang )
798 if( !rTxt.Len() || nEndPos <= nSttPos )
799 return sal_False;
801 CharClass& rCC = GetCharClass( eLang );
802 String aText( rTxt );
803 const sal_Unicode *pStart = aText.GetBuffer(),
804 *pStr = pStart + nEndPos,
805 *pWordStt = 0,
806 *pDelim = 0;
808 sal_Bool bAtStart = sal_False;
809 do {
810 --pStr;
811 if( rCC.isLetter(
812 aText, sal::static_int_cast< xub_StrLen >( pStr - pStart ) ) )
814 if( !pWordStt )
815 pDelim = pStr+1;
816 pWordStt = pStr;
818 else if( pWordStt &&
819 !rCC.isDigit(
820 aText,
821 sal::static_int_cast< xub_StrLen >( pStr - pStart ) ) )
823 if( lcl_IsInAsciiArr( sImplWordChars, *pStr ) &&
824 pWordStt - 1 == pStr &&
825 // Installation at beginning of paragraph. Replaced < by <= (#i38971#)
826 (long)(pStart + 1) <= (long)pStr &&
827 rCC.isLetter(
828 aText,
829 sal::static_int_cast< xub_StrLen >( pStr-1 - pStart ) ) )
830 pWordStt = --pStr;
831 else
832 break;
834 } while( 0 == ( bAtStart = (pStart == pStr)) );
836 if( !pWordStt ||
837 rCC.isDigit(
838 aText, sal::static_int_cast< xub_StrLen >( pStr - pStart ) ) ||
839 IsUpperLetter(
840 rCC.getCharacterType(
841 aText,
842 sal::static_int_cast< xub_StrLen >( pWordStt - pStart ) ) ) ||
843 INetURLObject::CompareProtocolScheme(rTxt.Copy(pWordStt - pStart, pDelim - pWordStt + 1)) != INET_PROT_NOT_VALID ||
844 0x1 == *pWordStt || 0x2 == *pWordStt )
845 return sal_False; // no character to be replaced, or already ok
847 if( *pDelim && 2 >= pDelim - pWordStt &&
848 lcl_IsInAsciiArr( ".-)>", *pDelim ) )
849 return sal_False;
851 if( !bAtStart ) // Still no beginning of a paragraph?
853 if ( IsWordDelim( *pStr ) )
855 while( 0 == ( bAtStart = (pStart == pStr--) ) && IsWordDelim( *pStr ))
858 // Asian full stop, full width full stop, full width exclamation mark
859 // and full width question marks are treated as word delimiters
860 else if ( 0x3002 != *pStr && 0xFF0E != *pStr && 0xFF01 != *pStr &&
861 0xFF1F != *pStr )
862 return sal_False; // no valid separator -> no replacement
865 if( bAtStart ) // at the beginning of a paragraph?
867 // Check out the previous paragraph, if it exists.
868 // If so, then check to paragraph separator at the end.
869 const String* pPrevPara = rDoc.GetPrevPara( bNormalPos );
870 if( !pPrevPara )
872 // valid separator -> replace
873 OUString sChar( *pWordStt );
874 sChar = rCC.titlecase(sChar); //see fdo#56740
875 return !comphelper::string::equals(sChar, *pWordStt) &&
876 rDoc.ReplaceRange( xub_StrLen( pWordStt - pStart ), 1, sChar );
879 aText = *pPrevPara;
880 bAtStart = sal_False;
881 pStart = aText.GetBuffer();
882 pStr = pStart + aText.Len();
884 do { // overwrite all blanks
885 --pStr;
886 if( !IsWordDelim( *pStr ))
887 break;
888 } while( 0 == ( bAtStart = (pStart == pStr)) );
890 if( bAtStart )
891 return sal_False; // no valid separator -> no replacement
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 const sal_Unicode* pExceptStt = 0;
897 if( !bAtStart )
899 sal_Bool bWeiter = sal_True;
900 int nFlag = C_NONE;
901 do {
902 switch( *pStr )
904 // Western and Asian full stop
905 case '.':
906 case 0x3002 :
907 case 0xFF0E :
909 if (pStr >= pStart + 2 && *(pStr-2) == '.')
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 return sal_False;
918 if( nFlag & C_FULL_STOP )
919 return sal_False; // no valid separator -> no replacement
920 nFlag |= C_FULL_STOP;
921 pExceptStt = pStr;
923 break;
924 case '!':
925 case 0xFF01 :
927 if( nFlag & C_EXCLAMATION_MARK )
928 return sal_False; // no valid separator -> no replacement
929 nFlag |= C_EXCLAMATION_MARK;
931 break;
932 case '?':
933 case 0xFF1F :
935 if( nFlag & C_QUESTION_MARK)
936 return sal_False; // no valid separator -> no replacement
937 nFlag |= C_QUESTION_MARK;
939 break;
940 default:
941 if( !nFlag )
942 return sal_False; // no valid separator -> no replacement
943 else
944 bWeiter = sal_False;
945 break;
948 if( bWeiter && pStr-- == pStart )
950 return sal_False; // no valid separator -> no replacement
952 } while( bWeiter );
953 if( C_FULL_STOP != nFlag )
954 pExceptStt = 0;
957 if( 2 > ( pStr - pStart ) )
958 return sal_False;
960 if( !rCC.isLetterNumeric(
961 aText, sal::static_int_cast< xub_StrLen >( pStr-- - pStart ) ) )
963 sal_Bool bValid = sal_False, bAlphaFnd = sal_False;
964 const sal_Unicode* pTmpStr = pStr;
965 while( !bValid )
967 if( rCC.isDigit(
968 aText,
969 sal::static_int_cast< xub_StrLen >( pTmpStr - pStart ) ) )
971 bValid = sal_True;
972 pStr = pTmpStr - 1;
974 else if( rCC.isLetter(
975 aText,
976 sal::static_int_cast< xub_StrLen >(
977 pTmpStr - pStart ) ) )
979 if( bAlphaFnd )
981 bValid = sal_True;
982 pStr = pTmpStr;
984 else
985 bAlphaFnd = sal_True;
987 else if( bAlphaFnd || IsWordDelim( *pTmpStr ) )
988 break;
990 if( pTmpStr == pStart )
991 break;
993 --pTmpStr;
996 if( !bValid )
997 return sal_False; // no valid separator -> no replacement
1000 sal_Bool bNumericOnly = '0' <= *(pStr+1) && *(pStr+1) <= '9';
1002 // Search for the beginning of the word
1003 while( !IsWordDelim( *pStr ))
1005 if( bNumericOnly &&
1006 rCC.isLetter(
1007 aText, sal::static_int_cast< xub_StrLen >( pStr - pStart ) ) )
1008 bNumericOnly = sal_False;
1010 if( pStart == pStr )
1011 break;
1013 --pStr;
1016 if( bNumericOnly ) // consists of only numbers, then not
1017 return sal_False;
1019 if( IsWordDelim( *pStr ))
1020 ++pStr;
1022 String sWord;
1024 // check on the basis of the exception list
1025 if( pExceptStt )
1027 sWord = OUString(pStr, pExceptStt - pStr + 1);
1028 if( FindInCplSttExceptList(eLang, sWord) )
1029 return sal_False;
1031 // Delete all non alphanumeric. Test the characters at the
1032 // beginning/end of the word ( recognizes: "(min.", "/min.", and so on.)
1033 String sTmp( sWord );
1034 while( sTmp.Len() &&
1035 !rCC.isLetterNumeric( sTmp, 0 ) )
1036 sTmp.Erase( 0, 1 );
1038 // Remove all non alphanumeric characters towards the end up until
1039 // the last one.
1040 xub_StrLen nLen = sTmp.Len();
1041 while( nLen && !rCC.isLetterNumeric( sTmp, nLen-1 ) )
1042 --nLen;
1043 if( nLen + 1 < sTmp.Len() )
1044 sTmp.Erase( nLen + 1 );
1046 if( sTmp.Len() && sTmp.Len() != sWord.Len() &&
1047 FindInCplSttExceptList(eLang, sTmp))
1048 return sal_False;
1050 if(FindInCplSttExceptList(eLang, sWord, sal_True))
1051 return sal_False;
1054 // Ok, then replace
1055 sal_Unicode cSave = *pWordStt;
1056 nSttPos = sal::static_int_cast< xub_StrLen >( pWordStt - rTxt.GetBuffer() );
1057 OUString sChar( cSave );
1058 sChar = rCC.titlecase(sChar); //see fdo#56740
1059 sal_Bool bRet = sChar[0] != cSave && rDoc.ReplaceRange( nSttPos, 1, sChar );
1061 // Parahaps someone wants to have the word
1062 if( bRet && SaveWordCplSttLst & nFlags )
1063 rDoc.SaveCpltSttWord( CptlSttSntnc, nSttPos, sWord, cSave );
1065 return bRet;
1068 bool SvxAutoCorrect::FnCorrectCapsLock( SvxAutoCorrDoc& rDoc, const String& rTxt,
1069 xub_StrLen nSttPos, xub_StrLen nEndPos,
1070 LanguageType eLang )
1072 if (nEndPos - nSttPos < 2)
1073 // string must be at least 2-character long.
1074 return false;
1076 CharClass& rCC = GetCharClass( eLang );
1078 // Check the first 2 letters.
1079 if ( !IsLowerLetter(rCC.getCharacterType(rTxt, nSttPos)) )
1080 return false;
1082 if ( !IsUpperLetter(rCC.getCharacterType(rTxt, nSttPos+1)) )
1083 return false;
1085 String aConverted;
1086 aConverted.Append( rCC.uppercase(OUString(rTxt.GetChar(nSttPos))) );
1087 aConverted.Append( rCC.lowercase(OUString(rTxt.GetChar(nSttPos+1))) );
1089 for (xub_StrLen i = nSttPos+2; i < nEndPos; ++i)
1091 if ( IsLowerLetter(rCC.getCharacterType(rTxt, i)) )
1092 // A lowercase letter disqualifies the whole text.
1093 return false;
1095 if ( IsUpperLetter(rCC.getCharacterType(rTxt, i)) )
1096 // Another uppercase letter. Convert it.
1097 aConverted.Append(rCC.lowercase(OUString(rTxt.GetChar(i))));
1098 else
1099 // This is not an alphabetic letter. Leave it as-is.
1100 aConverted.Append(rTxt.GetChar(i));
1103 // Replace the word.
1104 rDoc.Delete(nSttPos, nEndPos);
1105 rDoc.Insert(nSttPos, aConverted);
1107 return true;
1111 sal_Unicode SvxAutoCorrect::GetQuote( sal_Unicode cInsChar, sal_Bool bSttQuote,
1112 LanguageType eLang ) const
1114 sal_Unicode cRet = bSttQuote ? ( '\"' == cInsChar
1115 ? GetStartDoubleQuote()
1116 : GetStartSingleQuote() )
1117 : ( '\"' == cInsChar
1118 ? GetEndDoubleQuote()
1119 : GetEndSingleQuote() );
1120 if( !cRet )
1122 // then through the Language find the right character
1123 if( LANGUAGE_NONE == eLang )
1124 cRet = cInsChar;
1125 else
1127 LocaleDataWrapper& rLcl = GetLocaleDataWrapper( eLang );
1128 String sRet( bSttQuote
1129 ? ( '\"' == cInsChar
1130 ? rLcl.getDoubleQuotationMarkStart()
1131 : rLcl.getQuotationMarkStart() )
1132 : ( '\"' == cInsChar
1133 ? rLcl.getDoubleQuotationMarkEnd()
1134 : rLcl.getQuotationMarkEnd() ));
1135 cRet = sRet.Len() ? sRet.GetChar( 0 ) : cInsChar;
1138 return cRet;
1141 void SvxAutoCorrect::InsertQuote( SvxAutoCorrDoc& rDoc, xub_StrLen nInsPos,
1142 sal_Unicode cInsChar, sal_Bool bSttQuote,
1143 sal_Bool bIns )
1145 LanguageType eLang = rDoc.GetLanguage( nInsPos, sal_False );
1146 sal_Unicode cRet = GetQuote( cInsChar, bSttQuote, eLang );
1148 OUString sChg( cInsChar );
1149 if( bIns )
1150 rDoc.Insert( nInsPos, sChg );
1151 else
1152 rDoc.Replace( nInsPos, sChg );
1154 sChg = OUString(cRet);
1156 if( '\"' == cInsChar )
1158 if( LANGUAGE_SYSTEM == eLang )
1159 eLang = GetAppLang().getLanguageType();
1160 switch( eLang )
1162 case LANGUAGE_FRENCH:
1163 case LANGUAGE_FRENCH_BELGIAN:
1164 case LANGUAGE_FRENCH_CANADIAN:
1165 case LANGUAGE_FRENCH_SWISS:
1166 case LANGUAGE_FRENCH_LUXEMBOURG:
1168 OUString s( cNonBreakingSpace );
1169 // UNICODE code for no break space
1170 if( rDoc.Insert( bSttQuote ? nInsPos+1 : nInsPos, s ))
1172 if( !bSttQuote )
1173 ++nInsPos;
1176 break;
1180 rDoc.Replace( nInsPos, sChg );
1183 String SvxAutoCorrect::GetQuote( SvxAutoCorrDoc& rDoc, xub_StrLen nInsPos,
1184 sal_Unicode cInsChar, sal_Bool bSttQuote )
1186 LanguageType eLang = rDoc.GetLanguage( nInsPos, sal_False );
1187 sal_Unicode cRet = GetQuote( cInsChar, bSttQuote, eLang );
1189 String sRet = OUString(cRet);
1191 if( '\"' == cInsChar )
1193 if( LANGUAGE_SYSTEM == eLang )
1194 eLang = GetAppLang().getLanguageType();
1195 switch( eLang )
1197 case LANGUAGE_FRENCH:
1198 case LANGUAGE_FRENCH_BELGIAN:
1199 case LANGUAGE_FRENCH_CANADIAN:
1200 case LANGUAGE_FRENCH_SWISS:
1201 case LANGUAGE_FRENCH_LUXEMBOURG:
1202 if( bSttQuote )
1203 sRet += ' ';
1204 else
1205 sRet.Insert( ' ', 0 );
1206 break;
1209 return sRet;
1212 sal_uLong
1213 SvxAutoCorrect::DoAutoCorrect( SvxAutoCorrDoc& rDoc, const String& rTxt,
1214 xub_StrLen nInsPos, sal_Unicode cChar,
1215 sal_Bool bInsert, Window* pFrameWin )
1217 sal_uLong nRet = 0;
1218 bool bIsNextRun = bRunNext;
1219 bRunNext = false; // if it was set, then it has to be turned off
1221 do{ // only for middle check loop !!
1222 if( cChar )
1224 // Prevent double space
1225 if( nInsPos && ' ' == cChar &&
1226 IsAutoCorrFlag( IgnoreDoubleSpace ) &&
1227 ' ' == rTxt.GetChar( nInsPos - 1 ) )
1229 nRet = IgnoreDoubleSpace;
1230 break;
1233 sal_Bool bSingle = '\'' == cChar;
1234 sal_Bool bIsReplaceQuote =
1235 (IsAutoCorrFlag( ChgQuotes ) && ('\"' == cChar )) ||
1236 (IsAutoCorrFlag( ChgSglQuotes ) && bSingle );
1237 if( bIsReplaceQuote )
1239 sal_Unicode cPrev;
1240 sal_Bool bSttQuote = !nInsPos ||
1241 IsWordDelim( ( cPrev = rTxt.GetChar( nInsPos-1 ))) ||
1242 lcl_IsInAsciiArr( "([{", cPrev ) ||
1243 ( cEmDash && cEmDash == cPrev ) ||
1244 ( cEnDash && cEnDash == cPrev );
1246 InsertQuote( rDoc, nInsPos, cChar, bSttQuote, bInsert );
1247 nRet = bSingle ? ChgSglQuotes : ChgQuotes;
1248 break;
1251 if( bInsert )
1252 rDoc.Insert( nInsPos, OUString(cChar) );
1253 else
1254 rDoc.Replace( nInsPos, OUString(cChar) );
1256 // Hardspaces autocorrection
1257 if ( IsAutoCorrFlag( AddNonBrkSpace ) )
1259 if ( NeedsHardspaceAutocorr( cChar ) &&
1260 FnAddNonBrkSpace( rDoc, rTxt, 0, nInsPos, rDoc.GetLanguage( nInsPos, sal_False ) ) )
1262 nRet = AddNonBrkSpace;
1264 else if ( bIsNextRun && !IsAutoCorrectChar( cChar ) )
1266 // Remove the NBSP if it wasn't an autocorrection
1267 if ( nInsPos != 0 && NeedsHardspaceAutocorr( rTxt.GetChar( nInsPos - 1 ) ) &&
1268 cChar != ' ' && cChar != '\t' && cChar != cNonBreakingSpace )
1270 // Look for the last HARD_SPACE
1271 xub_StrLen nPos = nInsPos - 1;
1272 bool bContinue = true;
1273 while ( bContinue )
1275 const sal_Unicode cTmpChar = rTxt.GetChar( nPos );
1276 if ( cTmpChar == cNonBreakingSpace )
1278 rDoc.Delete( nPos, nPos + 1 );
1279 nRet = AddNonBrkSpace;
1280 bContinue = false;
1282 else if ( !NeedsHardspaceAutocorr( cTmpChar ) || nPos == 0 )
1283 bContinue = false;
1284 nPos--;
1291 if( !nInsPos )
1292 break;
1294 xub_StrLen nPos = nInsPos - 1;
1296 if( IsWordDelim( rTxt.GetChar( nPos )))
1297 break;
1299 // Set bold or underline automatically?
1300 if( '*' == cChar || '_' == cChar )
1302 if( IsAutoCorrFlag( ChgWeightUnderl ) &&
1303 FnChgWeightUnderl( rDoc, rTxt, 0, nPos+1 ) )
1304 nRet = ChgWeightUnderl;
1305 break;
1308 while( nPos && !IsWordDelim( rTxt.GetChar( --nPos )))
1311 // Found a Paragraph-start or a Blank, search for the word shortcut in
1312 // auto.
1313 xub_StrLen nCapLttrPos = nPos+1; // on the 1st Character
1314 if( !nPos && !IsWordDelim( rTxt.GetChar( 0 )))
1315 --nCapLttrPos; // Absatz Anfang und kein Blank !
1317 LanguageType eLang = rDoc.GetLanguage( nCapLttrPos, sal_False );
1318 if( LANGUAGE_SYSTEM == eLang )
1319 eLang = MsLangId::getSystemLanguage();
1320 CharClass& rCC = GetCharClass( eLang );
1322 // no symbol characters
1323 if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nInsPos ))
1324 break;
1326 if( IsAutoCorrFlag( Autocorrect ) )
1328 const String* pPara = 0;
1329 const String** ppPara = IsAutoCorrFlag(CptlSttSntnc) ? &pPara : 0;
1331 sal_Bool bChgWord = rDoc.ChgAutoCorrWord( nCapLttrPos, nInsPos,
1332 *this, ppPara );
1333 // since LibO 4.1, '-' is a word separator
1334 // fdo#67742 avoid "--" to be replaced by "–" if next is "-"
1335 if( rTxt.Len() >= 3 &&
1336 rTxt.Equals( String("---"), rTxt.Len()-3, 3 ) )
1337 break;
1338 if( !bChgWord )
1340 xub_StrLen nCapLttrPos1 = nCapLttrPos, nInsPos1 = nInsPos;
1341 while( nCapLttrPos1 < nInsPos &&
1342 lcl_IsInAsciiArr( sImplSttSkipChars, rTxt.GetChar( nCapLttrPos1 ) )
1344 ++nCapLttrPos1;
1345 while( nCapLttrPos1 < nInsPos1 && nInsPos1 &&
1346 lcl_IsInAsciiArr( sImplEndSkipChars, rTxt.GetChar( nInsPos1-1 ) )
1348 --nInsPos1;
1350 if( (nCapLttrPos1 != nCapLttrPos || nInsPos1 != nInsPos ) &&
1351 nCapLttrPos1 < nInsPos1 &&
1352 rDoc.ChgAutoCorrWord( nCapLttrPos1, nInsPos1, *this, ppPara ))
1354 bChgWord = sal_True;
1355 nCapLttrPos = nCapLttrPos1;
1359 if( bChgWord )
1361 nRet = Autocorrect;
1362 if( pPara )
1364 xub_StrLen nEnd = nCapLttrPos;
1365 while( nEnd < pPara->Len() &&
1366 !IsWordDelim( pPara->GetChar( nEnd )))
1367 ++nEnd;
1369 // Capital letter at beginning of paragraph?
1370 if( IsAutoCorrFlag( CptlSttSntnc ) &&
1371 FnCptlSttSntnc( rDoc, *pPara, sal_False,
1372 nCapLttrPos, nEnd, eLang ) )
1373 nRet |= CptlSttSntnc;
1375 if( IsAutoCorrFlag( ChgToEnEmDash ) &&
1376 FnChgToEnEmDash( rDoc, rTxt, nCapLttrPos, nEnd, eLang ) )
1377 nRet |= ChgToEnEmDash;
1379 break;
1383 if( ( IsAutoCorrFlag( nRet = ChgOrdinalNumber ) &&
1384 (nInsPos >= 2 ) && // fdo#69762 avoid autocorrect for 2e-3
1385 ( '-' != cChar || 'E' != toupper(rTxt.GetChar(nInsPos-1)) ||
1386 '0' > rTxt.GetChar(nInsPos-2) || '9' < rTxt.GetChar(nInsPos-2) ) &&
1387 FnChgOrdinalNumber( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) ||
1388 ( IsAutoCorrFlag( nRet = SetINetAttr ) &&
1389 ( ' ' == cChar || '\t' == cChar || 0x0a == cChar || !cChar ) &&
1390 FnSetINetAttr( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) ) )
1392 else
1394 bool bLockKeyOn = pFrameWin && (pFrameWin->GetIndicatorState() & INDICATOR_CAPSLOCK);
1395 bool bUnsupported = lcl_IsUnsupportedUnicodeChar( rCC, rTxt, nCapLttrPos, nInsPos );
1397 nRet = 0;
1398 if ( bLockKeyOn && IsAutoCorrFlag( CorrectCapsLock ) &&
1399 FnCorrectCapsLock( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
1401 // Correct accidental use of cAPS LOCK key (do this only when
1402 // the caps or shift lock key is pressed). Turn off the caps
1403 // lock afterwords.
1404 nRet |= CorrectCapsLock;
1405 pFrameWin->SimulateKeyPress( KEY_CAPSLOCK );
1408 // Capital letter at beginning of paragraph ?
1409 if( !bUnsupported &&
1410 IsAutoCorrFlag( CptlSttSntnc ) &&
1411 FnCptlSttSntnc( rDoc, rTxt, sal_True, nCapLttrPos, nInsPos, eLang ) )
1412 nRet |= CptlSttSntnc;
1414 // Two capital letters at beginning of word ??
1415 if( !bUnsupported &&
1416 IsAutoCorrFlag( CptlSttWrd ) &&
1417 FnCptlSttWrd( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
1418 nRet |= CptlSttWrd;
1420 if( IsAutoCorrFlag( ChgToEnEmDash ) &&
1421 FnChgToEnEmDash( rDoc, rTxt, nCapLttrPos, nInsPos, eLang ) )
1422 nRet |= ChgToEnEmDash;
1425 } while( false );
1427 return nRet;
1430 SvxAutoCorrectLanguageLists& SvxAutoCorrect::_GetLanguageList(
1431 LanguageType eLang )
1433 if(pLangTable->find(eLang) == pLangTable->end())
1434 CreateLanguageFile(eLang, sal_True);
1435 return *(pLangTable->find(eLang)->second);
1438 void SvxAutoCorrect::SaveCplSttExceptList( LanguageType eLang )
1440 boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(eLang);
1441 if(nTmpVal != pLangTable->end() && nTmpVal->second)
1442 nTmpVal->second->SaveCplSttExceptList();
1443 #ifdef DBG_UTIL
1444 else
1446 OSL_FAIL("Save an empty list? ");
1448 #endif
1451 void SvxAutoCorrect::SaveWrdSttExceptList(LanguageType eLang)
1453 boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(eLang);
1454 if(nTmpVal != pLangTable->end() && nTmpVal->second)
1455 nTmpVal->second->SaveWrdSttExceptList();
1456 #ifdef DBG_UTIL
1457 else
1459 OSL_FAIL("Save an empty list? ");
1461 #endif
1464 // Adds a single word. The list will immediately be written to the file!
1465 sal_Bool SvxAutoCorrect::AddCplSttException( const String& rNew,
1466 LanguageType eLang )
1468 SvxAutoCorrectLanguageLists* pLists = 0;
1469 // either the right language is present or it will be this in the general list
1470 boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(eLang);
1471 if(nTmpVal != pLangTable->end())
1472 pLists = nTmpVal->second;
1473 else
1475 nTmpVal = pLangTable->find(LANGUAGE_UNDETERMINED);
1476 if(nTmpVal != pLangTable->end())
1477 pLists = nTmpVal->second;
1478 else if(CreateLanguageFile(LANGUAGE_UNDETERMINED, sal_True))
1479 pLists = pLangTable->find(LANGUAGE_UNDETERMINED)->second;
1481 OSL_ENSURE(pLists, "No auto correction data");
1482 return pLists->AddToCplSttExceptList(rNew);
1485 // Adds a single word. The list will immediately be written to the file!
1486 sal_Bool SvxAutoCorrect::AddWrtSttException( const String& rNew,
1487 LanguageType eLang )
1489 SvxAutoCorrectLanguageLists* pLists = 0;
1490 //either the right language is present or it is set in the general list
1491 boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(eLang);
1492 if(nTmpVal != pLangTable->end())
1493 pLists = nTmpVal->second;
1494 else
1496 nTmpVal = pLangTable->find(LANGUAGE_UNDETERMINED);
1497 if(nTmpVal != pLangTable->end())
1498 pLists = nTmpVal->second;
1499 else if(CreateLanguageFile(LANGUAGE_UNDETERMINED, sal_True))
1500 pLists = pLangTable->find(LANGUAGE_UNDETERMINED)->second;
1502 OSL_ENSURE(pLists, "No auto correction file!");
1503 return pLists->AddToWrdSttExceptList(rNew);
1506 sal_Bool SvxAutoCorrect::GetPrevAutoCorrWord( SvxAutoCorrDoc& rDoc,
1507 const String& rTxt, xub_StrLen nPos,
1508 String& rWord ) const
1510 if( !nPos )
1511 return sal_False;
1513 xub_StrLen nEnde = nPos;
1515 // it must be followed by a blank or tab!
1516 if( ( nPos < rTxt.Len() &&
1517 !IsWordDelim( rTxt.GetChar( nPos ))) ||
1518 IsWordDelim( rTxt.GetChar( --nPos )))
1519 return sal_False;
1521 while( nPos && !IsWordDelim( rTxt.GetChar( --nPos )))
1524 // Found a Paragraph-start or a Blank, search for the word shortcut in
1525 // auto.
1526 xub_StrLen nCapLttrPos = nPos+1; // on the 1st Character
1527 if( !nPos && !IsWordDelim( rTxt.GetChar( 0 )))
1528 --nCapLttrPos; // Beginning of pargraph and no Blank!
1530 while( lcl_IsInAsciiArr( sImplSttSkipChars, rTxt.GetChar( nCapLttrPos )) )
1531 if( ++nCapLttrPos >= nEnde )
1532 return sal_False;
1534 if( 3 > nEnde - nCapLttrPos )
1535 return sal_False;
1537 LanguageType eLang = rDoc.GetLanguage( nCapLttrPos, sal_False );
1538 if( LANGUAGE_SYSTEM == eLang )
1539 eLang = MsLangId::getSystemLanguage();
1541 SvxAutoCorrect* pThis = (SvxAutoCorrect*)this;
1542 CharClass& rCC = pThis->GetCharClass( eLang );
1544 if( lcl_IsSymbolChar( rCC, rTxt, nCapLttrPos, nEnde ))
1545 return sal_False;
1547 rWord = rTxt.Copy( nCapLttrPos, nEnde - nCapLttrPos );
1548 return sal_True;
1551 sal_Bool SvxAutoCorrect::CreateLanguageFile( LanguageType eLang, sal_Bool bNewFile )
1553 OSL_ENSURE(pLangTable->find(eLang) == pLangTable->end(), "Language already exists ");
1555 OUString sUserDirFile( GetAutoCorrFileName( eLang, sal_True, sal_False ));
1556 OUString sShareDirFile( sUserDirFile );
1558 SvxAutoCorrectLanguageListsPtr pLists = 0;
1560 Time nMinTime( 0, 2 ), nAktTime( Time::SYSTEM ), nLastCheckTime( Time::EMPTY );
1562 std::map<LanguageType, long>::iterator nFndPos = aLastFileTable.find(eLang);
1563 if(nFndPos != aLastFileTable.end() &&
1564 (nLastCheckTime.SetTime(nFndPos->second), nLastCheckTime < nAktTime) &&
1565 nAktTime - nLastCheckTime < nMinTime)
1567 // no need to test the file, because the last check is not older then
1568 // 2 minutes.
1569 if( bNewFile )
1571 sShareDirFile = sUserDirFile;
1572 pLists = new SvxAutoCorrectLanguageLists( *this, sShareDirFile, sUserDirFile );
1573 pLangTable->insert(eLang, pLists);
1574 aLastFileTable.erase(nFndPos);
1577 else if( ( FStatHelper::IsDocument( sUserDirFile ) ||
1578 FStatHelper::IsDocument( sShareDirFile =
1579 GetAutoCorrFileName( eLang, sal_False, sal_False ) ) ) ||
1580 ( sShareDirFile = sUserDirFile, bNewFile ))
1582 pLists = new SvxAutoCorrectLanguageLists( *this, sShareDirFile, sUserDirFile );
1583 pLangTable->insert(eLang, pLists);
1584 if (nFndPos != aLastFileTable.end())
1585 aLastFileTable.erase(nFndPos);
1587 else if( !bNewFile )
1589 aLastFileTable[eLang] = nAktTime.GetTime();
1591 return pLists != 0;
1594 sal_Bool SvxAutoCorrect::PutText( const String& rShort, const String& rLong,
1595 LanguageType eLang )
1597 boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(eLang);
1598 if(nTmpVal != pLangTable->end())
1599 return nTmpVal->second->PutText(rShort, rLong);
1600 if(CreateLanguageFile(eLang))
1601 return pLangTable->find(eLang)->second->PutText(rShort, rLong);
1602 return sal_False;
1605 sal_Bool SvxAutoCorrect::MakeCombinedChanges( std::vector<SvxAutocorrWord>& aNewEntries,
1606 std::vector<SvxAutocorrWord>& aDeleteEntries,
1607 LanguageType eLang )
1609 boost::ptr_map<LanguageType, SvxAutoCorrectLanguageLists>::iterator nTmpVal = pLangTable->find(eLang);
1610 if(nTmpVal != pLangTable->end())
1612 return nTmpVal->second->MakeCombinedChanges( aNewEntries, aDeleteEntries );
1614 else if(CreateLanguageFile( eLang ))
1616 return pLangTable->find( eLang )->second->MakeCombinedChanges( aNewEntries, aDeleteEntries );
1618 return sal_False;
1623 // - return the replacement text (only for SWG-Format, all other
1624 // can be taken from the word list!)
1625 sal_Bool SvxAutoCorrect::GetLongText( const com::sun::star::uno::Reference < com::sun::star::embed::XStorage >&, const String&, const String& , String& )
1627 return sal_False;
1630 // Text with attribution (only the SWG - SWG format!)
1631 sal_Bool SvxAutoCorrect::PutText( const com::sun::star::uno::Reference < com::sun::star::embed::XStorage >&, const String&, const String&, SfxObjectShell&,
1632 String& )
1634 return sal_False;
1637 void EncryptBlockName_Imp( String& rName )
1639 xub_StrLen nLen, nPos = 1;
1640 rName.Insert( '#', 0 );
1641 sal_Unicode* pName = rName.GetBufferAccess();
1642 for ( nLen = rName.Len(), ++pName; nPos < nLen; ++nPos, ++pName )
1644 if( lcl_IsInAsciiArr( "!/:.\\", *pName ))
1645 *pName &= 0x0f;
1649 /* This code is copied from SwXMLTextBlocks::GeneratePackageName */
1650 static void GeneratePackageName ( const String& rShort, String& rPackageName )
1652 rPackageName = rShort;
1653 xub_StrLen nPos = 0;
1654 sal_Unicode pDelims[] = { '!', '/', ':', '.', '\\', 0 };
1655 OString sByte(OUStringToOString(rPackageName, RTL_TEXTENCODING_UTF7));
1656 rPackageName = OStringToOUString(sByte, RTL_TEXTENCODING_ASCII_US);
1657 while( STRING_NOTFOUND != ( nPos = rPackageName.SearchChar( pDelims, nPos )))
1659 rPackageName.SetChar( nPos, '_' );
1660 ++nPos;
1664 static const SvxAutocorrWord* lcl_SearchWordsInList(
1665 SvxAutoCorrectLanguageListsPtr pList, const String& rTxt,
1666 xub_StrLen& rStt, xub_StrLen nEndPos)
1668 const SvxAutocorrWordList* pAutoCorrWordList = pList->GetAutocorrWordList();
1669 return pAutoCorrWordList->SearchWordsInList( rTxt, rStt, nEndPos );
1672 // the search for the words in the substitution table
1673 const SvxAutocorrWord* SvxAutoCorrect::SearchWordsInList(
1674 const String& rTxt, xub_StrLen& rStt, xub_StrLen nEndPos,
1675 SvxAutoCorrDoc&, LanguageType& rLang )
1677 LanguageType eLang = rLang;
1678 const SvxAutocorrWord* pRet = 0;
1679 if( LANGUAGE_SYSTEM == eLang )
1680 eLang = MsLangId::getSystemLanguage();
1682 // First search for eLang, then US-English -> English
1683 // and last in LANGUAGE_UNDETERMINED
1684 if(pLangTable->find(eLang) != pLangTable->end() || CreateLanguageFile(eLang, sal_False))
1686 //the language is available - so bring it on
1687 SvxAutoCorrectLanguageLists* pList = pLangTable->find(eLang)->second;
1688 pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos );
1689 if( pRet )
1691 rLang = eLang;
1692 return pRet;
1696 // If it still could not be found here, then keep on searching
1698 LanguageType nTmpKey1 = eLang & 0x7ff, // the main language in many cases DE
1699 nTmpKey2 = eLang & 0x3ff; // otherwise for example EN
1700 if(nTmpKey1 != eLang && (pLangTable->find(nTmpKey1) != pLangTable->end() || CreateLanguageFile(nTmpKey1, sal_False)))
1702 //the language is available - so bring it on
1703 SvxAutoCorrectLanguageLists* pList = pLangTable->find(nTmpKey1)->second;
1704 pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos );
1705 if( pRet )
1707 rLang = nTmpKey1;
1708 return pRet;
1712 if(nTmpKey2 != eLang && (pLangTable->find(nTmpKey2) != pLangTable->end() || CreateLanguageFile(nTmpKey2, sal_False)))
1714 //the language is available - so bring it on
1715 SvxAutoCorrectLanguageLists* pList = pLangTable->find(nTmpKey2)->second;
1716 pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos );
1717 if( pRet )
1719 rLang = nTmpKey2;
1720 return pRet;
1724 if(pLangTable->find(LANGUAGE_UNDETERMINED) != pLangTable->end() || CreateLanguageFile(LANGUAGE_UNDETERMINED, sal_False))
1726 //the language is available - so bring it on
1727 SvxAutoCorrectLanguageLists* pList = pLangTable->find(LANGUAGE_UNDETERMINED)->second;
1728 pRet = lcl_SearchWordsInList( pList, rTxt, rStt, nEndPos );
1729 if( pRet )
1731 rLang = LANGUAGE_UNDETERMINED;
1732 return pRet;
1735 return 0;
1738 sal_Bool SvxAutoCorrect::FindInWrdSttExceptList( LanguageType eLang,
1739 const String& sWord )
1741 // First search for eLang, then US-English -> English
1742 // and last in LANGUAGE_UNDETERMINED
1743 LanguageType nTmpKey1 = eLang & 0x7ff, // the main language in many cases DE
1744 nTmpKey2 = eLang & 0x3ff; // otherwise for example EN
1745 String sTemp(sWord);
1747 if(pLangTable->find(eLang) != pLangTable->end() || CreateLanguageFile(eLang, sal_False))
1749 //the language is available - so bring it on
1750 SvxAutoCorrectLanguageLists* pList = pLangTable->find(eLang)->second;
1751 String _sTemp(sWord);
1752 if(pList->GetWrdSttExceptList()->find(&_sTemp) != pList->GetWrdSttExceptList()->end() )
1753 return sal_True;
1756 // If it still could not be found here, then keep on searching
1757 if(nTmpKey1 != eLang && (pLangTable->find(nTmpKey1) != pLangTable->end() || CreateLanguageFile(nTmpKey1, sal_False)))
1759 //the language is available - so bring it on
1760 SvxAutoCorrectLanguageLists* pList = pLangTable->find(nTmpKey1)->second;
1761 if(pList->GetWrdSttExceptList()->find(&sTemp) != pList->GetWrdSttExceptList()->end() )
1762 return sal_True;
1765 if(nTmpKey2 != eLang && (pLangTable->find(nTmpKey2) != pLangTable->end() || CreateLanguageFile(nTmpKey2, sal_False)))
1767 //the language is available - so bring it on
1768 SvxAutoCorrectLanguageLists* pList = pLangTable->find(nTmpKey2)->second;
1769 if(pList->GetWrdSttExceptList()->find(&sTemp) != pList->GetWrdSttExceptList()->end() )
1770 return sal_True;
1773 if(pLangTable->find(LANGUAGE_UNDETERMINED) != pLangTable->end() || CreateLanguageFile(LANGUAGE_UNDETERMINED, sal_False))
1775 //the language is available - so bring it on
1776 SvxAutoCorrectLanguageLists* pList = pLangTable->find(LANGUAGE_UNDETERMINED)->second;
1777 if(pList->GetWrdSttExceptList()->find(&sTemp) != pList->GetWrdSttExceptList()->end() )
1778 return sal_True;
1780 return sal_False;
1783 static sal_Bool lcl_FindAbbreviation( const SvStringsISortDtor* pList, const String& sWord)
1785 String sAbk(OUString('~'));
1786 SvStringsISortDtor::const_iterator it = pList->find( &sAbk );
1787 sal_uInt16 nPos = it - pList->begin();
1788 if( nPos < pList->size() )
1790 String sLowerWord( sWord ); sLowerWord.ToLowerAscii();
1791 const String* pAbk;
1792 for( sal_uInt16 n = nPos;
1793 n < pList->size() &&
1794 '~' == ( pAbk = (*pList)[ n ])->GetChar( 0 );
1795 ++n )
1797 // ~ and ~. are not allowed!
1798 if( 2 < pAbk->Len() && pAbk->Len() - 1 <= sWord.Len() )
1800 String sLowerAbk( *pAbk ); sLowerAbk.ToLowerAscii();
1801 for( xub_StrLen i = sLowerAbk.Len(), ii = sLowerWord.Len(); i; )
1803 if( !--i ) // agrees
1804 return sal_True;
1806 if( sLowerAbk.GetChar( i ) != sLowerWord.GetChar( --ii ))
1807 break;
1812 OSL_ENSURE( !(nPos && '~' == (*pList)[ --nPos ]->GetChar( 0 ) ),
1813 "Wrongly sorted exception list?" );
1814 return sal_False;
1817 sal_Bool SvxAutoCorrect::FindInCplSttExceptList(LanguageType eLang,
1818 const String& sWord, sal_Bool bAbbreviation)
1820 // First search for eLang, then US-English -> English
1821 // and last in LANGUAGE_UNDETERMINED
1822 LanguageType nTmpKey1 = eLang & 0x7ff, // the main language in many cases DE
1823 nTmpKey2 = eLang & 0x3ff; // otherwise for example EN
1824 String sTemp( sWord );
1826 if(pLangTable->find(eLang) != pLangTable->end() || CreateLanguageFile(eLang, sal_False))
1828 //the language is available - so bring it on
1829 const SvStringsISortDtor* pList = pLangTable->find(eLang)->second->GetCplSttExceptList();
1830 if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(&sTemp) != pList->end() )
1831 return sal_True;
1834 // If it still could not be found here, then keep on searching
1835 if(nTmpKey1 != eLang && (pLangTable->find(nTmpKey1) != pLangTable->end() || CreateLanguageFile(nTmpKey1, sal_False)))
1837 const SvStringsISortDtor* pList = pLangTable->find(nTmpKey1)->second->GetCplSttExceptList();
1838 if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(&sTemp) != pList->end() )
1839 return sal_True;
1842 if(nTmpKey2 != eLang && (pLangTable->find(nTmpKey2) != pLangTable->end() || CreateLanguageFile(nTmpKey2, sal_False)))
1844 //the language is available - so bring it on
1845 const SvStringsISortDtor* pList = pLangTable->find(nTmpKey2)->second->GetCplSttExceptList();
1846 if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(&sTemp) != pList->end() )
1847 return sal_True;
1850 if(pLangTable->find(LANGUAGE_UNDETERMINED) != pLangTable->end() || CreateLanguageFile(LANGUAGE_UNDETERMINED, sal_False))
1852 //the language is available - so bring it on
1853 const SvStringsISortDtor* pList = pLangTable->find(LANGUAGE_UNDETERMINED)->second->GetCplSttExceptList();
1854 if(bAbbreviation ? lcl_FindAbbreviation(pList, sWord) : pList->find(&sTemp) != pList->end() )
1855 return sal_True;
1857 return sal_False;
1860 OUString SvxAutoCorrect::GetAutoCorrFileName( LanguageType eLang,
1861 sal_Bool bNewFile, sal_Bool bTst ) const
1863 OUString sRet, sExt( LanguageTag( eLang ).getBcp47() );
1865 sExt = "_" + sExt + ".dat";
1866 if( bNewFile )
1867 ( sRet = sUserAutoCorrFile ) += sExt;
1868 else if( !bTst )
1869 ( sRet = sShareAutoCorrFile ) += sExt;
1870 else
1872 // test first in the user directory - if not exist, then
1873 ( sRet = sUserAutoCorrFile ) += sExt;
1874 if( !FStatHelper::IsDocument( sRet ))
1875 ( sRet = sShareAutoCorrFile ) += sExt;
1877 return sRet;
1880 SvxAutoCorrectLanguageLists::SvxAutoCorrectLanguageLists(
1881 SvxAutoCorrect& rParent,
1882 const String& rShareAutoCorrectFile,
1883 const String& rUserAutoCorrectFile)
1884 : sShareAutoCorrFile( rShareAutoCorrectFile ),
1885 sUserAutoCorrFile( rUserAutoCorrectFile ),
1886 aModifiedDate( Date::EMPTY ),
1887 aModifiedTime( Time::EMPTY ),
1888 aLastCheckTime( Time::EMPTY ),
1889 pCplStt_ExcptLst( 0 ),
1890 pWrdStt_ExcptLst( 0 ),
1891 pAutocorr_List( 0 ),
1892 rAutoCorrect(rParent),
1893 nFlags(0)
1897 SvxAutoCorrectLanguageLists::~SvxAutoCorrectLanguageLists()
1899 delete pCplStt_ExcptLst;
1900 delete pWrdStt_ExcptLst;
1901 delete pAutocorr_List;
1904 sal_Bool SvxAutoCorrectLanguageLists::IsFileChanged_Imp()
1906 // Access the file system only every 2 minutes to check the date stamp
1907 sal_Bool bRet = sal_False;
1909 Time nMinTime( 0, 2 );
1910 Time nAktTime( Time::SYSTEM );
1911 if( aLastCheckTime > nAktTime || // overflow?
1912 ( nAktTime -= aLastCheckTime ) > nMinTime ) // min time past
1914 Date aTstDate( Date::EMPTY ); Time aTstTime( Time::EMPTY );
1915 if( FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
1916 &aTstDate, &aTstTime ) &&
1917 ( aModifiedDate != aTstDate || aModifiedTime != aTstTime ))
1919 bRet = sal_True;
1920 // then remove all the lists fast!
1921 if( CplSttLstLoad & nFlags && pCplStt_ExcptLst )
1922 delete pCplStt_ExcptLst, pCplStt_ExcptLst = 0;
1923 if( WrdSttLstLoad & nFlags && pWrdStt_ExcptLst )
1924 delete pWrdStt_ExcptLst, pWrdStt_ExcptLst = 0;
1925 if( ChgWordLstLoad & nFlags && pAutocorr_List )
1926 delete pAutocorr_List, pAutocorr_List = 0;
1927 nFlags &= ~(CplSttLstLoad | WrdSttLstLoad | ChgWordLstLoad );
1929 aLastCheckTime = Time( Time::SYSTEM );
1931 return bRet;
1934 void SvxAutoCorrectLanguageLists::LoadXMLExceptList_Imp(
1935 SvStringsISortDtor*& rpLst,
1936 const sal_Char* pStrmName,
1937 SotStorageRef& rStg)
1939 if( rpLst )
1940 rpLst->DeleteAndDestroyAll();
1941 else
1942 rpLst = new SvStringsISortDtor;
1945 String sStrmName( pStrmName, RTL_TEXTENCODING_MS_1252 );
1946 String sTmp( sStrmName );
1948 if( rStg.Is() && rStg->IsStream( sStrmName ) )
1950 SvStorageStreamRef xStrm = rStg->OpenSotStream( sTmp,
1951 ( STREAM_READ | STREAM_SHARE_DENYWRITE | STREAM_NOCREATE ) );
1952 if( SVSTREAM_OK != xStrm->GetError())
1954 xStrm.Clear();
1955 rStg.Clear();
1956 RemoveStream_Imp( sStrmName );
1958 else
1960 uno::Reference< uno::XComponentContext > xContext =
1961 comphelper::getProcessComponentContext();
1963 xml::sax::InputSource aParserInput;
1964 aParserInput.sSystemId = sStrmName;
1966 xStrm->Seek( 0L );
1967 xStrm->SetBufferSize( 8 * 1024 );
1968 aParserInput.aInputStream = new utl::OInputStreamWrapper( *xStrm );
1970 // get filter
1971 uno::Reference< xml::sax::XDocumentHandler > xFilter = new SvXMLExceptionListImport ( xContext, *rpLst );
1973 // connect parser and filter
1974 uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create( xContext );
1975 xParser->setDocumentHandler( xFilter );
1977 // parse
1980 xParser->parseStream( aParserInput );
1982 catch( const xml::sax::SAXParseException& )
1984 // re throw ?
1986 catch( const xml::sax::SAXException& )
1988 // re throw ?
1990 catch( const io::IOException& )
1992 // re throw ?
1997 // Set time stamp
1998 FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
1999 &aModifiedDate, &aModifiedTime );
2000 aLastCheckTime = Time( Time::SYSTEM );
2005 void SvxAutoCorrectLanguageLists::SaveExceptList_Imp(
2006 const SvStringsISortDtor& rLst,
2007 const sal_Char* pStrmName,
2008 SotStorageRef &rStg,
2009 sal_Bool bConvert )
2011 if( rStg.Is() )
2013 String sStrmName( pStrmName, RTL_TEXTENCODING_MS_1252 );
2014 if( rLst.empty() )
2016 rStg->Remove( sStrmName );
2017 rStg->Commit();
2019 else
2021 SotStorageStreamRef xStrm = rStg->OpenSotStream( sStrmName,
2022 ( STREAM_READ | STREAM_WRITE | STREAM_SHARE_DENYWRITE ) );
2023 if( xStrm.Is() )
2025 xStrm->SetSize( 0 );
2026 xStrm->SetBufferSize( 8192 );
2027 OUString aMime( "text/xml" );
2028 uno::Any aAny;
2029 aAny <<= aMime;
2030 xStrm->SetProperty( OUString("MediaType"), aAny );
2033 uno::Reference< uno::XComponentContext > xContext =
2034 comphelper::getProcessComponentContext();
2036 uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext);
2037 uno::Reference < io::XOutputStream> xOut = new utl::OOutputStreamWrapper( *xStrm );
2038 xWriter->setOutputStream(xOut);
2040 uno::Reference < xml::sax::XDocumentHandler > xHandler(xWriter, UNO_QUERY_THROW);
2041 SvXMLExceptionListExport aExp( xContext, rLst, sStrmName, xHandler );
2043 aExp.exportDoc( XML_BLOCK_LIST );
2045 xStrm->Commit();
2046 if( xStrm->GetError() == SVSTREAM_OK )
2048 xStrm.Clear();
2049 if (!bConvert)
2051 rStg->Commit();
2052 if( SVSTREAM_OK != rStg->GetError() )
2054 rStg->Remove( sStrmName );
2055 rStg->Commit();
2064 SvxAutocorrWordList* SvxAutoCorrectLanguageLists::LoadAutocorrWordList()
2066 if( pAutocorr_List )
2067 pAutocorr_List->DeleteAndDestroyAll();
2068 else
2069 pAutocorr_List = new SvxAutocorrWordList();
2073 uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetStorageFromURL( sShareAutoCorrFile, embed::ElementModes::READ );
2074 String aXMLWordListName( pXMLImplAutocorr_ListStr, RTL_TEXTENCODING_MS_1252 );
2075 uno::Reference < io::XStream > xStrm = xStg->openStreamElement( aXMLWordListName, embed::ElementModes::READ );
2076 uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
2078 xml::sax::InputSource aParserInput;
2079 aParserInput.sSystemId = aXMLWordListName;
2080 aParserInput.aInputStream = xStrm->getInputStream();
2082 // get parser
2083 uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext);
2084 RTL_LOGFILE_PRODUCT_CONTEXT( aLog, "AutoCorrect Import" );
2085 uno::Reference< xml::sax::XDocumentHandler > xFilter = new SvXMLAutoCorrectImport( xContext, pAutocorr_List, rAutoCorrect, xStg );
2087 // connect parser and filter
2088 xParser->setDocumentHandler( xFilter );
2090 // parse
2091 xParser->parseStream( aParserInput );
2093 catch ( const uno::Exception& )
2097 // Set time stamp
2098 FStatHelper::GetModifiedDateTimeOfFile( sShareAutoCorrFile,
2099 &aModifiedDate, &aModifiedTime );
2100 aLastCheckTime = Time( Time::SYSTEM );
2102 return pAutocorr_List;
2105 void SvxAutoCorrectLanguageLists::SetAutocorrWordList( SvxAutocorrWordList* pList )
2107 if( pAutocorr_List && pList != pAutocorr_List )
2108 delete pAutocorr_List;
2109 pAutocorr_List = pList;
2110 if( !pAutocorr_List )
2112 OSL_ENSURE( !this, "No valid list" );
2113 pAutocorr_List = new SvxAutocorrWordList();
2115 nFlags |= ChgWordLstLoad;
2118 const SvxAutocorrWordList* SvxAutoCorrectLanguageLists::GetAutocorrWordList()
2120 if( !( ChgWordLstLoad & nFlags ) || IsFileChanged_Imp() )
2121 SetAutocorrWordList( LoadAutocorrWordList() );
2122 return pAutocorr_List;
2125 SvStringsISortDtor* SvxAutoCorrectLanguageLists::GetCplSttExceptList()
2127 if( !( CplSttLstLoad & nFlags ) || IsFileChanged_Imp() )
2128 SetCplSttExceptList( LoadCplSttExceptList() );
2129 return pCplStt_ExcptLst;
2132 sal_Bool SvxAutoCorrectLanguageLists::AddToCplSttExceptList(const String& rNew)
2134 String* pNew = new String( rNew );
2135 if( rNew.Len() && GetCplSttExceptList()->insert( pNew ).second )
2137 MakeUserStorage_Impl();
2138 SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2140 SaveExceptList_Imp( *pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
2142 xStg = 0;
2143 // Set time stamp
2144 FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
2145 &aModifiedDate, &aModifiedTime );
2146 aLastCheckTime = Time( Time::SYSTEM );
2148 else
2149 delete pNew, pNew = 0;
2150 return 0 != pNew;
2153 sal_Bool SvxAutoCorrectLanguageLists::AddToWrdSttExceptList(const String& rNew)
2155 String* pNew = new String( rNew );
2156 SvStringsISortDtor* pExceptList = LoadWrdSttExceptList();
2157 if( rNew.Len() && pExceptList && pExceptList->insert( pNew ).second )
2159 MakeUserStorage_Impl();
2160 SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2162 SaveExceptList_Imp( *pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );
2164 xStg = 0;
2165 // Set time stamp
2166 FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
2167 &aModifiedDate, &aModifiedTime );
2168 aLastCheckTime = Time( Time::SYSTEM );
2170 else
2171 delete pNew, pNew = 0;
2172 return 0 != pNew;
2175 SvStringsISortDtor* SvxAutoCorrectLanguageLists::LoadCplSttExceptList()
2177 SotStorageRef xStg = new SotStorage( sShareAutoCorrFile, STREAM_READ | STREAM_SHARE_DENYNONE, sal_True );
2178 String sTemp ( RTL_CONSTASCII_USTRINGPARAM ( pXMLImplCplStt_ExcptLstStr ) );
2179 if( xStg.Is() && xStg->IsContained( sTemp ) )
2180 LoadXMLExceptList_Imp( pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
2182 return pCplStt_ExcptLst;
2185 void SvxAutoCorrectLanguageLists::SaveCplSttExceptList()
2187 MakeUserStorage_Impl();
2188 SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2190 SaveExceptList_Imp( *pCplStt_ExcptLst, pXMLImplCplStt_ExcptLstStr, xStg );
2192 xStg = 0;
2194 // Set time stamp
2195 FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
2196 &aModifiedDate, &aModifiedTime );
2197 aLastCheckTime = Time( Time::SYSTEM );
2200 void SvxAutoCorrectLanguageLists::SetCplSttExceptList( SvStringsISortDtor* pList )
2202 if( pCplStt_ExcptLst && pList != pCplStt_ExcptLst )
2203 delete pCplStt_ExcptLst;
2205 pCplStt_ExcptLst = pList;
2206 if( !pCplStt_ExcptLst )
2208 OSL_ENSURE( !this, "No valid list" );
2209 pCplStt_ExcptLst = new SvStringsISortDtor;
2211 nFlags |= CplSttLstLoad;
2214 SvStringsISortDtor* SvxAutoCorrectLanguageLists::LoadWrdSttExceptList()
2216 SotStorageRef xStg = new SotStorage( sShareAutoCorrFile, STREAM_READ | STREAM_SHARE_DENYNONE, sal_True );
2217 String sTemp ( RTL_CONSTASCII_USTRINGPARAM ( pXMLImplWrdStt_ExcptLstStr ) );
2218 if( xStg.Is() && xStg->IsContained( sTemp ) )
2219 LoadXMLExceptList_Imp( pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );
2220 return pWrdStt_ExcptLst;
2223 void SvxAutoCorrectLanguageLists::SaveWrdSttExceptList()
2225 MakeUserStorage_Impl();
2226 SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2228 SaveExceptList_Imp( *pWrdStt_ExcptLst, pXMLImplWrdStt_ExcptLstStr, xStg );
2230 xStg = 0;
2231 // Set time stamp
2232 FStatHelper::GetModifiedDateTimeOfFile( sUserAutoCorrFile,
2233 &aModifiedDate, &aModifiedTime );
2234 aLastCheckTime = Time( Time::SYSTEM );
2237 void SvxAutoCorrectLanguageLists::SetWrdSttExceptList( SvStringsISortDtor* pList )
2239 if( pWrdStt_ExcptLst && pList != pWrdStt_ExcptLst )
2240 delete pWrdStt_ExcptLst;
2241 pWrdStt_ExcptLst = pList;
2242 if( !pWrdStt_ExcptLst )
2244 OSL_ENSURE( !this, "No valid list" );
2245 pWrdStt_ExcptLst = new SvStringsISortDtor;
2247 nFlags |= WrdSttLstLoad;
2250 SvStringsISortDtor* SvxAutoCorrectLanguageLists::GetWrdSttExceptList()
2252 if( !( WrdSttLstLoad & nFlags ) || IsFileChanged_Imp() )
2253 SetWrdSttExceptList( LoadWrdSttExceptList() );
2254 return pWrdStt_ExcptLst;
2257 void SvxAutoCorrectLanguageLists::RemoveStream_Imp( const String& rName )
2259 if( sShareAutoCorrFile != sUserAutoCorrFile )
2261 SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2262 if( xStg.Is() && SVSTREAM_OK == xStg->GetError() &&
2263 xStg->IsStream( rName ) )
2265 xStg->Remove( rName );
2266 xStg->Commit();
2268 xStg = 0;
2273 void SvxAutoCorrectLanguageLists::MakeUserStorage_Impl()
2275 // The conversion needs to happen if the file is already in the user
2276 // directory and is in the old format. Additionally it needs to
2277 // happen when the file is being copied from share to user.
2279 sal_Bool bError = sal_False, bConvert = sal_False, bCopy = sal_False;
2280 INetURLObject aDest;
2281 INetURLObject aSource;
2283 if (sUserAutoCorrFile != sShareAutoCorrFile )
2285 aSource = INetURLObject ( sShareAutoCorrFile );
2286 aDest = INetURLObject ( sUserAutoCorrFile );
2287 if ( SotStorage::IsOLEStorage ( sShareAutoCorrFile ) )
2289 aDest.SetExtension ( OUString("bak") );
2290 bConvert = sal_True;
2292 bCopy = sal_True;
2294 else if ( SotStorage::IsOLEStorage ( sUserAutoCorrFile ) )
2296 aSource = INetURLObject ( sUserAutoCorrFile );
2297 aDest = INetURLObject ( sUserAutoCorrFile );
2298 aDest.SetExtension ( OUString("bak") );
2299 bCopy = bConvert = sal_True;
2301 if (bCopy)
2305 String sMain(aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ));
2306 sal_Unicode cSlash = '/';
2307 xub_StrLen nSlashPos = sMain.SearchBackward(cSlash);
2308 sMain.Erase(nSlashPos);
2309 ::ucbhelper::Content aNewContent( sMain, uno::Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() );
2310 Any aAny;
2311 TransferInfo aInfo;
2312 aInfo.NameClash = NameClash::OVERWRITE;
2313 aInfo.NewTitle = aDest.GetName();
2314 aInfo.SourceURL = aSource.GetMainURL( INetURLObject::DECODE_TO_IURI );
2315 aInfo.MoveData = sal_False;
2316 aAny <<= aInfo;
2317 aNewContent.executeCommand( OUString ( "transfer" ), aAny);
2319 catch (...)
2321 bError = sal_True;
2324 if (bConvert && !bError)
2326 SotStorageRef xSrcStg = new SotStorage( aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ), STREAM_READ, sal_True );
2327 SotStorageRef xDstStg = new SotStorage( sUserAutoCorrFile, STREAM_WRITE, sal_True );
2329 if( xSrcStg.Is() && xDstStg.Is() )
2331 String sXMLWord ( RTL_CONSTASCII_USTRINGPARAM ( pXMLImplWrdStt_ExcptLstStr ) );
2332 String sXMLSentence ( RTL_CONSTASCII_USTRINGPARAM ( pXMLImplCplStt_ExcptLstStr ) );
2333 SvStringsISortDtor *pTmpWordList = NULL;
2335 if (xSrcStg->IsContained( sXMLWord ) )
2336 LoadXMLExceptList_Imp( pTmpWordList, pXMLImplWrdStt_ExcptLstStr, xSrcStg );
2338 if (pTmpWordList)
2340 SaveExceptList_Imp( *pTmpWordList, pXMLImplWrdStt_ExcptLstStr, xDstStg, sal_True );
2341 pTmpWordList->DeleteAndDestroyAll();
2342 pTmpWordList = NULL;
2346 if (xSrcStg->IsContained( sXMLSentence ) )
2347 LoadXMLExceptList_Imp( pTmpWordList, pXMLImplCplStt_ExcptLstStr, xSrcStg );
2349 if (pTmpWordList)
2351 SaveExceptList_Imp( *pTmpWordList, pXMLImplCplStt_ExcptLstStr, xDstStg, sal_True );
2352 pTmpWordList->DeleteAndDestroyAll();
2355 GetAutocorrWordList();
2356 MakeBlocklist_Imp( *xDstStg );
2357 sShareAutoCorrFile = sUserAutoCorrFile;
2358 xDstStg = 0;
2361 ::ucbhelper::Content aContent ( aDest.GetMainURL( INetURLObject::DECODE_TO_IURI ), uno::Reference < XCommandEnvironment >(), comphelper::getProcessComponentContext() );
2362 aContent.executeCommand ( OUString( "delete" ), makeAny ( sal_Bool (sal_True ) ) );
2364 catch (...)
2369 else if( bCopy && !bError )
2370 sShareAutoCorrFile = sUserAutoCorrFile;
2373 sal_Bool SvxAutoCorrectLanguageLists::MakeBlocklist_Imp( SvStorage& rStg )
2375 String sStrmName( pXMLImplAutocorr_ListStr, RTL_TEXTENCODING_MS_1252 );
2376 sal_Bool bRet = sal_True, bRemove = !pAutocorr_List || pAutocorr_List->empty();
2377 if( !bRemove )
2379 SvStorageStreamRef refList = rStg.OpenSotStream( sStrmName,
2380 ( STREAM_READ | STREAM_WRITE | STREAM_SHARE_DENYWRITE ) );
2381 if( refList.Is() )
2383 refList->SetSize( 0 );
2384 refList->SetBufferSize( 8192 );
2385 String aPropName( OUString( "MediaType" ) );
2386 OUString aMime( "text/xml" );
2387 uno::Any aAny;
2388 aAny <<= aMime;
2389 refList->SetProperty( aPropName, aAny );
2391 uno::Reference< uno::XComponentContext > xContext =
2392 comphelper::getProcessComponentContext();
2394 uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext);
2395 uno::Reference < io::XOutputStream> xOut = new utl::OOutputStreamWrapper( *refList );
2396 xWriter->setOutputStream(xOut);
2398 uno::Reference<xml::sax::XDocumentHandler> xHandler(xWriter, uno::UNO_QUERY);
2399 SvXMLAutoCorrectExport aExp( xContext, pAutocorr_List, sStrmName, xHandler );
2401 aExp.exportDoc( XML_BLOCK_LIST );
2403 refList->Commit();
2404 bRet = SVSTREAM_OK == refList->GetError();
2405 if( bRet )
2407 refList.Clear();
2408 rStg.Commit();
2409 if( SVSTREAM_OK != rStg.GetError() )
2411 bRemove = sal_True;
2412 bRet = sal_False;
2416 else
2417 bRet = sal_False;
2420 if( bRemove )
2422 rStg.Remove( sStrmName );
2423 rStg.Commit();
2426 return bRet;
2429 sal_Bool SvxAutoCorrectLanguageLists::MakeCombinedChanges( std::vector<SvxAutocorrWord>& aNewEntries, std::vector<SvxAutocorrWord>& aDeleteEntries )
2431 // First get the current list!
2432 GetAutocorrWordList();
2434 MakeUserStorage_Impl();
2435 SotStorageRef xStorage = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2437 sal_Bool bRet = xStorage.Is() && SVSTREAM_OK == xStorage->GetError();
2439 if( bRet )
2441 for ( sal_uInt32 i=0; i < aDeleteEntries.size(); i++ )
2443 SvxAutocorrWord aWordToDelete = aDeleteEntries[i];
2444 SvxAutocorrWord *pFoundEntry = pAutocorr_List->FindAndRemove( &aWordToDelete );
2445 if( pFoundEntry )
2447 if( !pFoundEntry->IsTextOnly() )
2449 String aName( aWordToDelete.GetShort() );
2450 if (xStorage->IsOLEStorage())
2451 EncryptBlockName_Imp( aName );
2452 else
2453 GeneratePackageName ( aWordToDelete.GetShort(), aName );
2455 if( xStorage->IsContained( aName ) )
2457 xStorage->Remove( aName );
2458 bRet = xStorage->Commit();
2461 delete pFoundEntry;
2465 for ( sal_uInt32 i=0; i < aNewEntries.size(); i++ )
2467 SvxAutocorrWord *pWordToAdd = new SvxAutocorrWord( aNewEntries[i].GetShort(), aNewEntries[i].GetLong(), sal_True );
2468 SvxAutocorrWord *pRemoved = pAutocorr_List->FindAndRemove( pWordToAdd );
2469 if( pRemoved )
2471 if( !pRemoved->IsTextOnly() )
2473 // Still have to remove the Storage
2474 String sStorageName( pWordToAdd->GetShort() );
2475 if (xStorage->IsOLEStorage())
2476 EncryptBlockName_Imp( sStorageName );
2477 else
2478 GeneratePackageName ( pWordToAdd->GetShort(), sStorageName);
2480 if( xStorage->IsContained( sStorageName ) )
2481 xStorage->Remove( sStorageName );
2483 delete pRemoved;
2485 bRet = pAutocorr_List->Insert( pWordToAdd );
2487 if ( !bRet )
2489 delete pWordToAdd;
2490 break;
2494 if ( bRet )
2496 bRet = MakeBlocklist_Imp( *xStorage );
2499 return bRet;
2502 sal_Bool SvxAutoCorrectLanguageLists::PutText( const String& rShort, const String& rLong )
2504 // First get the current list!
2505 GetAutocorrWordList();
2507 MakeUserStorage_Impl();
2508 SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2510 sal_Bool bRet = xStg.Is() && SVSTREAM_OK == xStg->GetError();
2512 // Update the word list
2513 if( bRet )
2515 SvxAutocorrWord* pNew = new SvxAutocorrWord( rShort, rLong, sal_True );
2516 SvxAutocorrWord *pRemove = pAutocorr_List->FindAndRemove( pNew );
2517 if( pRemove )
2519 if( !pRemove->IsTextOnly() )
2521 // Still have to remove the Storage
2522 String sStgNm( rShort );
2523 if (xStg->IsOLEStorage())
2524 EncryptBlockName_Imp( sStgNm );
2525 else
2526 GeneratePackageName ( rShort, sStgNm);
2528 if( xStg->IsContained( sStgNm ) )
2529 xStg->Remove( sStgNm );
2531 delete pRemove;
2534 if( pAutocorr_List->Insert( pNew ) )
2536 bRet = MakeBlocklist_Imp( *xStg );
2537 xStg = 0;
2539 else
2541 delete pNew;
2542 bRet = sal_False;
2545 return bRet;
2548 sal_Bool SvxAutoCorrectLanguageLists::PutText( const String& rShort,
2549 SfxObjectShell& rShell )
2551 // First get the current list!
2552 GetAutocorrWordList();
2554 MakeUserStorage_Impl();
2556 sal_Bool bRet = sal_False;
2557 String sLong;
2560 uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetStorageFromURL( sUserAutoCorrFile, embed::ElementModes::READWRITE );
2561 bRet = rAutoCorrect.PutText( xStg, sUserAutoCorrFile, rShort, rShell, sLong );
2562 xStg = 0;
2564 // Update the word list
2565 if( bRet )
2567 SvxAutocorrWord* pNew = new SvxAutocorrWord( rShort, sLong, sal_False );
2568 if( pAutocorr_List->Insert( pNew ) )
2570 SotStorageRef xStor = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2571 MakeBlocklist_Imp( *xStor );
2573 else
2574 delete pNew;
2577 catch ( const uno::Exception& )
2581 return bRet;
2584 // Delete an entry
2585 sal_Bool SvxAutoCorrectLanguageLists::DeleteText( const String& rShort )
2587 // First get the current list!
2588 GetAutocorrWordList();
2590 MakeUserStorage_Impl();
2592 SotStorageRef xStg = new SotStorage( sUserAutoCorrFile, STREAM_READWRITE, sal_True );
2593 sal_Bool bRet = xStg.Is() && SVSTREAM_OK == xStg->GetError();
2594 if( bRet )
2596 SvxAutocorrWord aTmp( rShort, rShort );
2597 SvxAutocorrWord *pFnd = pAutocorr_List->FindAndRemove( &aTmp );
2598 if( pFnd )
2600 if( !pFnd->IsTextOnly() )
2602 String aName( rShort );
2603 if (xStg->IsOLEStorage())
2604 EncryptBlockName_Imp( aName );
2605 else
2606 GeneratePackageName ( rShort, aName );
2607 if( xStg->IsContained( aName ) )
2609 xStg->Remove( aName );
2610 bRet = xStg->Commit();
2614 delete pFnd;
2615 MakeBlocklist_Imp( *xStg );
2616 xStg = 0;
2618 else
2619 bRet = sal_False;
2621 return bRet;
2624 // Keep the list sorted ...
2625 bool CompareSvxAutocorrWordList::operator()( SvxAutocorrWord* const& lhs, SvxAutocorrWord* const& rhs ) const
2627 CollatorWrapper& rCmp = ::GetCollatorWrapper();
2628 return rCmp.compareString( lhs->GetShort(), rhs->GetShort() ) < 0;
2631 SvxAutocorrWordList::~SvxAutocorrWordList()
2633 DeleteAndDestroyAll();
2636 void SvxAutocorrWordList::DeleteAndDestroyAll()
2638 for( SvxAutocorrWordList_Hash::const_iterator it = maHash.begin(); it != maHash.end(); ++it )
2639 delete it->second;
2640 maHash.clear();
2642 for( SvxAutocorrWordList_Set::const_iterator it2 = maSet.begin(); it2 != maSet.end(); ++it2 )
2643 delete *it2;
2644 maSet.clear();
2647 // returns true if inserted
2648 bool SvxAutocorrWordList::Insert(SvxAutocorrWord *pWord)
2650 if ( maSet.empty() ) // use the hash
2652 OUString aShort( pWord->GetShort() );
2653 return maHash.insert( std::pair<OUString, SvxAutocorrWord *>( aShort, pWord ) ).second;
2655 else
2656 return maSet.insert( pWord ).second;
2659 void SvxAutocorrWordList::LoadEntry(String sWrong, String sRight, sal_Bool bOnlyTxt)
2661 SvxAutocorrWord* pNew = new SvxAutocorrWord( sWrong, sRight, bOnlyTxt );
2662 if( !Insert( pNew ) )
2663 delete pNew;
2666 bool SvxAutocorrWordList::empty() const
2668 return maHash.empty() && maSet.empty();
2671 SvxAutocorrWord *SvxAutocorrWordList::FindAndRemove(SvxAutocorrWord *pWord)
2673 SvxAutocorrWord *pMatch = NULL;
2675 if ( maSet.empty() ) // use the hash
2677 SvxAutocorrWordList_Hash::iterator it = maHash.find( pWord->GetShort() );
2678 if( it != maHash.end() )
2680 pMatch = it->second;
2681 maHash.erase (it);
2684 else
2686 SvxAutocorrWordList_Set::iterator it = maSet.find( pWord );
2687 if( it != maSet.end() )
2689 pMatch = *it;
2690 maSet.erase (it);
2693 return pMatch;
2696 // return the sorted contents - defer sorting until we have to.
2697 SvxAutocorrWordList::Content SvxAutocorrWordList::getSortedContent() const
2699 Content aContent;
2701 // convert from hash to set permanantly
2702 if ( maSet.empty() )
2704 // This beasty has some O(N log(N)) in a terribly slow ICU collate fn.
2705 for( SvxAutocorrWordList_Hash::const_iterator it = maHash.begin(); it != maHash.end(); ++it )
2706 maSet.insert( it->second );
2707 maHash.clear();
2709 for( SvxAutocorrWordList_Set::const_iterator it = maSet.begin(); it != maSet.end(); ++it )
2710 aContent.push_back( *it );
2712 return aContent;
2715 bool SvxAutocorrWordList::WordMatches(const SvxAutocorrWord *pFnd,
2716 const String &rTxt,
2717 xub_StrLen &rStt,
2718 xub_StrLen nEndPos) const
2720 const String& rChk = pFnd->GetShort();
2721 if( nEndPos >= rChk.Len() )
2723 xub_StrLen nCalcStt = nEndPos - rChk.Len();
2724 if( ( !nCalcStt || nCalcStt == rStt ||
2725 ( nCalcStt < rStt &&
2726 IsWordDelim( rTxt.GetChar( nCalcStt - 1 ) ))) )
2728 TransliterationWrapper& rCmp = GetIgnoreTranslWrapper();
2730 OUString sWord(rTxt.GetBuffer() + nCalcStt, rChk.Len());
2731 if( rCmp.isEqual( rChk, sWord ))
2733 rStt = nCalcStt;
2734 return true;
2738 return false;
2741 const SvxAutocorrWord* SvxAutocorrWordList::SearchWordsInList(const String& rTxt, xub_StrLen& rStt,
2742 xub_StrLen nEndPos) const
2744 for( SvxAutocorrWordList_Hash::const_iterator it = maHash.begin(); it != maHash.end(); ++it )
2746 if( WordMatches( it->second, rTxt, rStt, nEndPos ) )
2747 return it->second;
2750 for( SvxAutocorrWordList_Set::const_iterator it2 = maSet.begin(); it2 != maSet.end(); ++it2 )
2752 if( WordMatches( *it2, rTxt, rStt, nEndPos ) )
2753 return *it2;
2755 return 0;
2758 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */