LibreOffice Module sc (master)  1
spellcheckcontext.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 
10 #include <spellcheckcontext.hxx>
11 #include <svl/sharedstring.hxx>
12 #include <editeng/eeitem.hxx>
13 #include <editeng/langitem.hxx>
14 #include <editeng/unolingu.hxx>
15 
16 #include <scitems.hxx>
17 #include <document.hxx>
18 #include <cellvalue.hxx>
19 #include <editutil.hxx>
20 #include <dpobject.hxx>
21 
22 #include <com/sun/star/linguistic2/XSpellChecker1.hpp>
23 
24 #include <o3tl/hash_combine.hxx>
25 
26 #include <unordered_map>
27 
28 using namespace css;
29 
31 
33 {
34  struct CellPos
35  {
36  struct Hash
37  {
38  size_t operator() (const CellPos& rPos) const
39  {
40  std::size_t seed = 0;
41  o3tl::hash_combine(seed, rPos.mnCol);
42  o3tl::hash_combine(seed, rPos.mnRow);
43  return seed;
44  }
45  };
46 
49 
50  CellPos(SCCOL nCol, SCROW nRow) : mnCol(nCol), mnRow(nRow) {}
51 
52  bool operator== (const CellPos& r) const
53  {
54  return mnCol == r.mnCol && mnRow == r.mnRow;
55  }
56 
57  };
58 
59  typedef std::vector<editeng::MisspellRanges> MisspellType;
60  typedef std::unordered_map<CellPos, std::unique_ptr<MisspellType>, CellPos::Hash> CellMapType;
61  typedef std::unordered_map<const rtl_uString*, std::unique_ptr<MisspellType>> SharedStringMapType;
62  typedef std::unordered_map<CellPos, LanguageType, CellPos::Hash> CellLangMapType;
63 
64  SharedStringMapType maStringMisspells;
65  CellMapType maEditTextMisspells;
66  CellLangMapType maCellLanguages;
68 
69 public:
70 
71  SpellCheckCache(LanguageType eDefaultCellLanguage) : meDefCellLanguage(eDefaultCellLanguage)
72  {
73  }
74 
75  bool query(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, MisspellType*& rpRanges) const
76  {
77  CellType eType = rCell.meType;
78  if (eType == CELLTYPE_STRING)
79  {
80  SharedStringMapType::const_iterator it = maStringMisspells.find(rCell.mpString->getData());
81  if (it == maStringMisspells.end())
82  return false; // Not available
83 
84  rpRanges = it->second.get();
85  return true;
86  }
87 
88  if (eType == CELLTYPE_EDIT)
89  {
90  CellMapType::const_iterator it = maEditTextMisspells.find(CellPos(nCol, nRow));
91  if (it == maEditTextMisspells.end())
92  return false; // Not available
93 
94  rpRanges = it->second.get();
95  return true;
96  }
97 
98  rpRanges = nullptr;
99  return true;
100  }
101 
102  void set(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, std::unique_ptr<MisspellType> pRanges)
103  {
104  CellType eType = rCell.meType;
105  if (eType == CELLTYPE_STRING)
106  maStringMisspells.insert_or_assign(rCell.mpString->getData(), std::move(pRanges));
107  else if (eType == CELLTYPE_EDIT)
108  maEditTextMisspells.insert_or_assign(CellPos(nCol, nRow), std::move(pRanges));
109  }
110 
112  {
113  CellLangMapType::const_iterator it = maCellLanguages.find(CellPos(nCol, nRow));
114  if (it == maCellLanguages.end())
115  return meDefCellLanguage;
116 
117  return it->second;
118  }
119 
120  void setLanguage(LanguageType eCellLang, SCCOL nCol, SCROW nRow)
121  {
122  if (eCellLang == meDefCellLanguage)
123  maCellLanguages.erase(CellPos(nCol, nRow));
124  else
125  maCellLanguages.insert_or_assign(CellPos(nCol, nRow), eCellLang);
126  }
127 
128  void clear(LanguageType eDefaultCellLanguage)
129  {
130  maStringMisspells.clear();
131  maEditTextMisspells.clear();
132  maCellLanguages.clear();
133  meDefCellLanguage = eDefaultCellLanguage;
134  }
135 
137  {
138  maEditTextMisspells.clear();
139  }
140 };
141 
143 {
145 
146  SpellCheckStatus() : mbModified(false) {};
147 
148  DECL_LINK( EventHdl, EditStatus&, void );
149 };
150 
152 {
153  EditStatusFlags nStatus = rStatus.GetStatusWord();
154  if (nStatus & EditStatusFlags::WRONGWORDCHANGED)
155  mbModified = true;
156 }
157 
159 {
162  const std::vector<editeng::MisspellRanges>* pRanges;
163 
164  SpellCheckResult() : mnCol(-1), mnRow(-1), pRanges(nullptr) {}
165 
166  void set(SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pMisspells)
167  {
168  mnCol = nCol;
169  mnRow = nRow;
170  pRanges = pMisspells;
171  }
172 
173  const std::vector<editeng::MisspellRanges>* query(SCCOL nCol, SCROW nRow) const
174  {
175  assert(mnCol == nCol);
176  assert(mnRow == nRow);
177  (void)nCol;
178  (void)nRow;
179  return pRanges;
180  }
181 
182  void clear()
183  {
184  mnCol = -1;
185  mnRow = -1;
186  pRanges = nullptr;
187  }
188 };
189 
190 SpellCheckContext::SpellCheckContext(ScDocument* pDocument, SCTAB nTab) :
191  pDoc(pDocument),
192  mnTab(nTab),
193  meLanguage(ScGlobal::GetEditDefaultLanguage())
194 {
195  // defer init of engine and cache till the first query/set
196 }
197 
198 SpellCheckContext::~SpellCheckContext()
199 {
200 }
201 
202 void SpellCheckContext::dispose()
203 {
204  mpEngine.reset();
205  mpCache.reset();
206  pDoc = nullptr;
207 }
208 
209 void SpellCheckContext::setTabNo(SCTAB nTab)
210 {
211  if (mnTab == nTab)
212  return;
213  mnTab = nTab;
214  reset();
215 }
216 
217 bool SpellCheckContext::isMisspelled(SCCOL nCol, SCROW nRow) const
218 {
219  const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
220  return mpResult->query(nCol, nRow);
221 }
222 
223 const std::vector<editeng::MisspellRanges>* SpellCheckContext::getMisspellRanges(
224  SCCOL nCol, SCROW nRow ) const
225 {
226  const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
227  return mpResult->query(nCol, nRow);
228 }
229 
230 void SpellCheckContext::setMisspellRanges(
231  SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pRanges )
232 {
233  if (!mpEngine || !mpCache)
234  reset();
235 
236  ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
237  CellType eType = aCell.meType;
238 
239  if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
240  return;
241 
242  typedef std::vector<editeng::MisspellRanges> MisspellType;
243  std::unique_ptr<MisspellType> pMisspells(pRanges ? new MisspellType(*pRanges) : nullptr);
244  mpCache->set(nCol, nRow, aCell, std::move(pMisspells));
245 }
246 
247 void SpellCheckContext::reset()
248 {
250  resetCache();
251  mpEngine.reset();
252  mpStatus.reset();
253 }
254 
255 void SpellCheckContext::resetForContentChange()
256 {
257  resetCache(true /* bContentChangeOnly */);
258 }
259 
260 void SpellCheckContext::ensureResults(SCCOL nCol, SCROW nRow)
261 {
262  if (!mpEngine || !mpCache ||
263  ScGlobal::GetEditDefaultLanguage() != meLanguage)
264  {
265  reset();
266  setup();
267  }
268 
269  // perhaps compute the pivot rangelist once in some pivot-table change handler ?
270  if (pDoc->HasPivotTable())
271  {
272  if (ScDPCollection* pDPs = pDoc->GetDPCollection())
273  {
274  ScRangeList aPivotRanges = pDPs->GetAllTableRanges(mnTab);
275  if (aPivotRanges.In(ScAddress(nCol, nRow, mnTab))) // Don't spell check within pivot tables
276  {
277  mpResult->set(nCol, nRow, nullptr);
278  return;
279  }
280  }
281  }
282 
283  ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
284  CellType eType = aCell.meType;
285 
286  if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
287  {
288  // No spell-check required.
289  mpResult->set(nCol, nRow, nullptr);
290  return;
291  }
292 
293 
294  // Cell content is either shared-string or EditTextObject
295 
296  // For spell-checking, we currently only use the primary
297  // language; not CJK nor CTL.
298  const ScPatternAttr* pPattern = pDoc->GetPattern(nCol, nRow, mnTab);
299  LanguageType eCellLang = pPattern->GetItem(ATTR_FONT_LANGUAGE).GetValue();
300 
301  if (eCellLang == LANGUAGE_SYSTEM)
302  eCellLang = meLanguage; // never use SYSTEM for spelling
303 
304  if (eCellLang == LANGUAGE_NONE)
305  {
306  mpResult->set(nCol, nRow, nullptr); // No need to spell check this cell.
307  return;
308  }
309 
310  typedef std::vector<editeng::MisspellRanges> MisspellType;
311 
312  LanguageType eCachedCellLang = mpCache->getLanguage(nCol, nRow);
313 
314  if (eCellLang != eCachedCellLang)
315  mpCache->setLanguage(eCellLang, nCol, nRow);
316 
317  else
318  {
319  MisspellType* pRanges = nullptr;
320  bool bFound = mpCache->query(nCol, nRow, aCell, pRanges);
321  if (bFound)
322  {
323  // Cache hit.
324  mpResult->set(nCol, nRow, pRanges);
325  return;
326  }
327  }
328 
329  // Cache miss, the cell needs spell-check..
330  mpEngine->SetDefaultItem(SvxLanguageItem(eCellLang, EE_CHAR_LANGUAGE));
331  if (eType == CELLTYPE_STRING)
332  mpEngine->SetText(aCell.mpString->getString());
333  else
334  mpEngine->SetText(*aCell.mpEditText);
335 
336  mpStatus->mbModified = false;
337  mpEngine->CompleteOnlineSpelling();
338  std::unique_ptr<MisspellType> pRanges;
339  if (mpStatus->mbModified)
340  {
341  pRanges.reset(new MisspellType);
342  mpEngine->GetAllMisspellRanges(*pRanges);
343 
344  if (pRanges->empty())
345  pRanges.reset(nullptr);
346  }
347  // else : No change in status for EditStatusFlags::WRONGWORDCHANGED => no spell errors (which is the default status).
348 
349  mpResult->set(nCol, nRow, pRanges.get());
350  mpCache->set(nCol, nRow, aCell, std::move(pRanges));
351 }
352 
353 void SpellCheckContext::resetCache(bool bContentChangeOnly)
354 {
355  if (!mpResult)
356  mpResult.reset(new SpellCheckResult());
357  else
358  mpResult->clear();
359 
360  if (!mpCache)
361  mpCache.reset(new SpellCheckCache(meLanguage));
362  else if (bContentChangeOnly)
363  mpCache->clearEditTextMap();
364  else
365  mpCache->clear(meLanguage);
366 }
367 
368 void SpellCheckContext::setup()
369 {
370  mpEngine.reset(new ScTabEditEngine(pDoc));
371  mpStatus.reset(new SpellCheckStatus());
372 
373  mpEngine->SetControlWord(
374  mpEngine->GetControlWord() | (EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS));
375  mpEngine->SetStatusEventHdl(LINK(mpStatus.get(), SpellCheckStatus, EventHdl));
376  // Delimiters here like in inputhdl.cxx !!!
377  mpEngine->SetWordDelimiters(
378  ScEditUtil::ModifyDelimiters(mpEngine->GetWordDelimiters()));
379 
380  uno::Reference<linguistic2::XSpellChecker1> xXSpellChecker1(LinguMgr::GetSpellChecker());
381  mpEngine->SetSpeller(xXSpellChecker1);
382  mpEngine->SetDefaultLanguage(meLanguage);
383 }
384 
385 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool operator==(const XclFontData &rLeft, const XclFontData &rRight)
Definition: xlstyle.cxx:519
void set(SCCOL nCol, SCROW nRow, const std::vector< editeng::MisspellRanges > *pMisspells)
const std::vector< editeng::MisspellRanges > * pRanges
Class shared between grid windows to cache spelling results.
constexpr TypedWhichId< SvxLanguageItem > ATTR_FONT_LANGUAGE(110)
bool query(SCCOL nCol, SCROW nRow, const ScRefCellValue &rCell, MisspellType *&rpRanges) const
LanguageType meLanguage
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:103
bool In(const ScRange &) const
Definition: rangelst.cxx:1086
SpellCheckCache(LanguageType eDefaultCellLanguage)
const SfxPoolItem & GetItem(sal_uInt16 nWhichP) const
Definition: patattr.hxx:70
DocumentType eType
static css::uno::Reference< css::linguistic2::XSpellChecker1 > GetSpellChecker()
std::unordered_map< const rtl_uString *, std::unique_ptr< MisspellType > > SharedStringMapType
IMPL_LINK(SpellCheckContext::SpellCheckStatus, EventHdl, EditStatus &, rStatus, void)
const svl::SharedString * mpString
Definition: cellvalue.hxx:108
sal_Int16 SCCOL
Definition: types.hxx:21
void setLanguage(LanguageType eCellLang, SCCOL nCol, SCROW nRow)
EditStatusFlags
const std::vector< editeng::MisspellRanges > * query(SCCOL nCol, SCROW nRow) const
std::enable_if_t<(sizeof(N)==4)> hash_combine(N &nSeed, T const *pValue, size_t nCount)
void clear(LanguageType eDefaultCellLanguage)
rtl_uString * getData()
static LanguageType GetEditDefaultLanguage()
Definition: global.cxx:861
CellType meType
Definition: cellvalue.hxx:105
sal_Int32 SCROW
Definition: types.hxx:17
LanguageType getLanguage(SCCOL nCol, SCROW nRow) const
CellType
Definition: global.hxx:281
static OUString ModifyDelimiters(const OUString &rOld)
Definition: editutil.cxx:64
std::vector< editeng::MisspellRanges > MisspellType
void set(SCCOL nCol, SCROW nRow, const ScRefCellValue &rCell, std::unique_ptr< MisspellType > pRanges)
std::unordered_map< CellPos, std::unique_ptr< MisspellType >, CellPos::Hash > CellMapType
std::unordered_map< CellPos, LanguageType, CellPos::Hash > CellLangMapType
exports com.sun.star. setup
sal_Int16 SCTAB
Definition: types.hxx:22
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo