tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / view / spellcheckcontext.cxx
blob890940531874b273f062d06a3624d38729d21ef4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <spellcheckcontext.hxx>
11 #include <svl/sharedstring.hxx>
12 #include <editeng/eeitem.hxx>
13 #include <editeng/langitem.hxx>
14 #include <editeng/unolingu.hxx>
16 #include <scitems.hxx>
17 #include <document.hxx>
18 #include <cellvalue.hxx>
19 #include <editutil.hxx>
20 #include <dpobject.hxx>
22 #include <com/sun/star/linguistic2/XSpellChecker1.hpp>
24 #include <o3tl/hash_combine.hxx>
26 #include <unordered_map>
28 using namespace css;
30 using sc::SpellCheckContext;
32 class SpellCheckContext::SpellCheckCache
34 struct CellPos
36 struct Hash
38 size_t operator() (const CellPos& rPos) const
40 std::size_t seed = 0;
41 o3tl::hash_combine(seed, rPos.mnCol);
42 o3tl::hash_combine(seed, rPos.mnRow);
43 return seed;
47 SCCOL mnCol;
48 SCROW mnRow;
50 CellPos(SCCOL nCol, SCROW nRow) : mnCol(nCol), mnRow(nRow) {}
52 bool operator== (const CellPos& r) const
54 return mnCol == r.mnCol && mnRow == r.mnRow;
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;
64 SharedStringMapType maStringMisspells;
65 CellMapType maEditTextMisspells;
66 CellLangMapType maCellLanguages;
67 LanguageType meDefCellLanguage;
69 public:
71 SpellCheckCache(LanguageType eDefaultCellLanguage) : meDefCellLanguage(eDefaultCellLanguage)
75 bool query(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, MisspellType*& rpRanges) const
77 CellType eType = rCell.getType();
78 if (eType == CELLTYPE_STRING)
80 SharedStringMapType::const_iterator it = maStringMisspells.find(rCell.getSharedString()->getData());
81 if (it == maStringMisspells.end())
82 return false; // Not available
84 rpRanges = it->second.get();
85 return true;
88 if (eType == CELLTYPE_EDIT)
90 CellMapType::const_iterator it = maEditTextMisspells.find(CellPos(nCol, nRow));
91 if (it == maEditTextMisspells.end())
92 return false; // Not available
94 rpRanges = it->second.get();
95 return true;
98 rpRanges = nullptr;
99 return true;
102 void set(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, std::unique_ptr<MisspellType> pRanges)
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));
111 LanguageType getLanguage(SCCOL nCol, SCROW nRow) const
113 CellLangMapType::const_iterator it = maCellLanguages.find(CellPos(nCol, nRow));
114 if (it == maCellLanguages.end())
115 return meDefCellLanguage;
117 return it->second;
120 void setLanguage(LanguageType eCellLang, SCCOL nCol, SCROW nRow)
122 if (eCellLang == meDefCellLanguage)
123 maCellLanguages.erase(CellPos(nCol, nRow));
124 else
125 maCellLanguages.insert_or_assign(CellPos(nCol, nRow), eCellLang);
128 void clear(LanguageType eDefaultCellLanguage)
130 maStringMisspells.clear();
131 maEditTextMisspells.clear();
132 maCellLanguages.clear();
133 meDefCellLanguage = eDefaultCellLanguage;
136 void clearEditTextMap()
138 maEditTextMisspells.clear();
142 struct SpellCheckContext::SpellCheckStatus
144 bool mbModified;
146 SpellCheckStatus() : mbModified(false) {};
148 DECL_LINK( EventHdl, EditStatus&, void );
151 IMPL_LINK(SpellCheckContext::SpellCheckStatus, EventHdl, EditStatus&, rStatus, void)
153 EditStatusFlags nStatus = rStatus.GetStatusWord();
154 if (nStatus & EditStatusFlags::WRONGWORDCHANGED)
155 mbModified = true;
158 struct SpellCheckContext::SpellCheckResult
160 SCCOL mnCol;
161 SCROW mnRow;
162 const std::vector<editeng::MisspellRanges>* pRanges;
164 SpellCheckResult() : mnCol(-1), mnRow(-1), pRanges(nullptr) {}
166 void set(SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pMisspells)
168 mnCol = nCol;
169 mnRow = nRow;
170 pRanges = pMisspells;
173 const std::vector<editeng::MisspellRanges>* query(SCCOL nCol, SCROW nRow) const
175 assert(mnCol == nCol);
176 assert(mnRow == nRow);
177 (void)nCol;
178 (void)nRow;
179 return pRanges;
182 void clear()
184 mnCol = -1;
185 mnRow = -1;
186 pRanges = nullptr;
190 SpellCheckContext::SpellCheckContext(ScDocument* pDocument, SCTAB nTab) :
191 pDoc(pDocument),
192 mnTab(nTab),
193 meLanguage(ScGlobal::GetEditDefaultLanguage())
195 // defer init of engine and cache till the first query/set
198 SpellCheckContext::~SpellCheckContext()
202 void SpellCheckContext::dispose()
204 mpEngine.reset();
205 mpCache.reset();
206 pDoc = nullptr;
209 void SpellCheckContext::setTabNo(SCTAB nTab)
211 if (mnTab == nTab)
212 return;
213 mnTab = nTab;
214 reset();
217 bool SpellCheckContext::isMisspelled(SCCOL nCol, SCROW nRow) const
219 const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
220 return mpResult->query(nCol, nRow);
223 const std::vector<editeng::MisspellRanges>* SpellCheckContext::getMisspellRanges(
224 SCCOL nCol, SCROW nRow ) const
226 const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
227 return mpResult->query(nCol, nRow);
230 void SpellCheckContext::setMisspellRanges(
231 SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pRanges )
233 if (!mpEngine || !mpCache)
234 reset();
236 ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
237 CellType eType = aCell.getType();
239 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
240 return;
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));
247 void SpellCheckContext::reset()
249 meLanguage = ScGlobal::GetEditDefaultLanguage();
250 resetCache();
251 mpEngine.reset();
252 mpStatus.reset();
255 void SpellCheckContext::resetForContentChange()
257 resetCache(true /* bContentChangeOnly */);
260 void SpellCheckContext::ensureResults(SCCOL nCol, SCROW nRow)
262 if (!mpEngine || !mpCache ||
263 ScGlobal::GetEditDefaultLanguage() != meLanguage)
265 reset();
266 setup();
269 // perhaps compute the pivot rangelist once in some pivot-table change handler ?
270 if (pDoc->HasPivotTable())
272 if (ScDPCollection* pDPs = pDoc->GetDPCollection())
274 ScRangeList aPivotRanges = pDPs->GetAllTableRanges(mnTab);
275 if (aPivotRanges.Contains(ScRange(ScAddress(nCol, nRow, mnTab)))) // Don't spell check within pivot tables
277 mpResult->set(nCol, nRow, nullptr);
278 return;
283 ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
284 CellType eType = aCell.getType();
286 if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
288 // No spell-check required.
289 mpResult->set(nCol, nRow, nullptr);
290 return;
294 // Cell content is either shared-string or EditTextObject
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();
301 if (eCellLang == LANGUAGE_SYSTEM)
302 eCellLang = meLanguage; // never use SYSTEM for spelling
304 if (eCellLang == LANGUAGE_NONE)
306 mpResult->set(nCol, nRow, nullptr); // No need to spell check this cell.
307 return;
310 typedef std::vector<editeng::MisspellRanges> MisspellType;
312 LanguageType eCachedCellLang = mpCache->getLanguage(nCol, nRow);
314 if (eCellLang != eCachedCellLang)
315 mpCache->setLanguage(eCellLang, nCol, nRow);
317 else
319 MisspellType* pRanges = nullptr;
320 bool bFound = mpCache->query(nCol, nRow, aCell, pRanges);
321 if (bFound)
323 // Cache hit.
324 mpResult->set(nCol, nRow, pRanges);
325 return;
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());
335 // it has to happen after we set text
336 mpEngine->SetDefaultItem(SvxLanguageItem(eCellLang, EE_CHAR_LANGUAGE));
338 mpStatus->mbModified = false;
339 mpEngine->CompleteOnlineSpelling();
340 std::unique_ptr<MisspellType> pRanges;
341 if (mpStatus->mbModified)
343 pRanges.reset(new MisspellType);
344 mpEngine->GetAllMisspellRanges(*pRanges);
346 if (pRanges->empty())
347 pRanges.reset(nullptr);
349 // else : No change in status for EditStatusFlags::WRONGWORDCHANGED => no spell errors (which is the default status).
351 mpResult->set(nCol, nRow, pRanges.get());
352 mpCache->set(nCol, nRow, aCell, std::move(pRanges));
355 void SpellCheckContext::resetCache(bool bContentChangeOnly)
357 if (!mpResult)
358 mpResult.reset(new SpellCheckResult());
359 else
360 mpResult->clear();
362 if (!mpCache)
363 mpCache.reset(new SpellCheckCache(meLanguage));
364 else if (bContentChangeOnly)
365 mpCache->clearEditTextMap();
366 else
367 mpCache->clear(meLanguage);
370 void SpellCheckContext::setup()
372 mpEngine.reset(new ScTabEditEngine(pDoc));
373 mpStatus.reset(new SpellCheckStatus());
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()));
382 uno::Reference<linguistic2::XSpellChecker1> xXSpellChecker1(LinguMgr::GetSpellChecker());
383 mpEngine->SetSpeller(xXSpellChecker1);
384 mpEngine->SetDefaultLanguage(meLanguage);
387 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */