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
28using 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
68
69public:
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.getType();
79 {
80 SharedStringMapType::const_iterator it = maStringMisspells.find(rCell.getSharedString()->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.getType();
105 if (eType == CELLTYPE_STRING)
106 maStringMisspells.insert_or_assign(rCell.getSharedString()->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
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
190SpellCheckContext::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
198SpellCheckContext::~SpellCheckContext()
199{
200}
201
202void SpellCheckContext::dispose()
203{
204 mpEngine.reset();
205 mpCache.reset();
206 pDoc = nullptr;
207}
208
209void SpellCheckContext::setTabNo(SCTAB nTab)
210{
211 if (mnTab == nTab)
212 return;
213 mnTab = nTab;
214 reset();
215}
216
217bool SpellCheckContext::isMisspelled(SCCOL nCol, SCROW nRow) const
218{
219 const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
220 return mpResult->query(nCol, nRow);
221}
222
223const 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
230void 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.getType();
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
247void SpellCheckContext::reset()
248{
250 resetCache();
251 mpEngine.reset();
252 mpStatus.reset();
253}
254
255void SpellCheckContext::resetForContentChange()
256{
257 resetCache(true /* bContentChangeOnly */);
258}
259
260void 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.Contains(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.getType();
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 if (eType == CELLTYPE_STRING)
331 mpEngine->SetText(aCell.getSharedString()->getString());
332 else
333 mpEngine->SetText(*aCell.getEditText());
334
335 // it has to happen after we set text
336 mpEngine->SetDefaultItem(SvxLanguageItem(eCellLang, EE_CHAR_LANGUAGE));
337
338 mpStatus->mbModified = false;
339 mpEngine->CompleteOnlineSpelling();
340 std::unique_ptr<MisspellType> pRanges;
341 if (mpStatus->mbModified)
342 {
343 pRanges.reset(new MisspellType);
344 mpEngine->GetAllMisspellRanges(*pRanges);
345
346 if (pRanges->empty())
347 pRanges.reset(nullptr);
348 }
349 // else : No change in status for EditStatusFlags::WRONGWORDCHANGED => no spell errors (which is the default status).
350
351 mpResult->set(nCol, nRow, pRanges.get());
352 mpCache->set(nCol, nRow, aCell, std::move(pRanges));
353}
354
355void SpellCheckContext::resetCache(bool bContentChangeOnly)
356{
357 if (!mpResult)
358 mpResult.reset(new SpellCheckResult());
359 else
360 mpResult->clear();
361
362 if (!mpCache)
363 mpCache.reset(new SpellCheckCache(meLanguage));
364 else if (bContentChangeOnly)
365 mpCache->clearEditTextMap();
366 else
367 mpCache->clear(meLanguage);
368}
369
370void SpellCheckContext::setup()
371{
372 mpEngine.reset(new ScTabEditEngine(pDoc));
373 mpStatus.reset(new SpellCheckStatus());
374
375 mpEngine->SetControlWord(
376 mpEngine->GetControlWord() | (EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS));
377 mpEngine->SetStatusEventHdl(LINK(mpStatus.get(), SpellCheckStatus, EventHdl));
378 // Delimiters here like in inputhdl.cxx !!!
379 mpEngine->SetWordDelimiters(
380 ScEditUtil::ModifyDelimiters(mpEngine->GetWordDelimiters()));
381
382 uno::Reference<linguistic2::XSpellChecker1> xXSpellChecker1(LinguMgr::GetSpellChecker());
383 mpEngine->SetSpeller(xXSpellChecker1);
384 mpEngine->SetDefaultLanguage(meLanguage);
385}
386
387/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static css::uno::Reference< css::linguistic2::XSpellChecker1 > GetSpellChecker()
static OUString ModifyDelimiters(const OUString &rOld)
Definition: editutil.cxx:65
static LanguageType GetEditDefaultLanguage()
Definition: global.cxx:907
const SfxPoolItem & GetItem(sal_uInt16 nWhichP) const
Definition: patattr.hxx:72
bool Contains(const ScRange &) const
Definition: rangelst.cxx:1082
std::unordered_map< CellPos, std::unique_ptr< MisspellType >, CellPos::Hash > CellMapType
std::vector< editeng::MisspellRanges > MisspellType
bool query(SCCOL nCol, SCROW nRow, const ScRefCellValue &rCell, MisspellType *&rpRanges) const
void clear(LanguageType eDefaultCellLanguage)
void setLanguage(LanguageType eCellLang, SCCOL nCol, SCROW nRow)
void set(SCCOL nCol, SCROW nRow, const ScRefCellValue &rCell, std::unique_ptr< MisspellType > pRanges)
std::unordered_map< CellPos, LanguageType, CellPos::Hash > CellLangMapType
std::unordered_map< const rtl_uString *, std::unique_ptr< MisspellType > > SharedStringMapType
SpellCheckCache(LanguageType eDefaultCellLanguage)
LanguageType getLanguage(SCCOL nCol, SCROW nRow) const
Class shared between grid windows to cache spelling results.
rtl_uString * getData()
LanguageType meLanguage
EditStatusFlags
DocumentType eType
CellType
Definition: global.hxx:271
@ CELLTYPE_EDIT
Definition: global.hxx:276
@ CELLTYPE_STRING
Definition: global.hxx:274
std::enable_if_t<(sizeof(N)==4)> hash_combine(N &nSeed, T const *pValue, size_t nCount)
constexpr TypedWhichId< SvxLanguageItem > ATTR_FONT_LANGUAGE(110)
IMPL_LINK(SpellCheckContext::SpellCheckStatus, EventHdl, EditStatus &, rStatus, void)
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:108
const svl::SharedString * getSharedString() const
Definition: cellvalue.hxx:135
CellType getType() const
Definition: cellvalue.hxx:133
void set(SCCOL nCol, SCROW nRow, const std::vector< editeng::MisspellRanges > *pMisspells)
const std::vector< editeng::MisspellRanges > * pRanges
const std::vector< editeng::MisspellRanges > * query(SCCOL nCol, SCROW nRow) const
DECL_LINK(EventHdl, EditStatus &, void)
bool mbModified
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17