LCOV - code coverage report
Current view: top level - editeng/source/misc - svxacorr.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 503 1461 34.4 %
Date: 2015-06-13 12:38:46 Functions: 48 100 48.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11