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