summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Francis <dennis.francis@collabora.com>2020-10-04 12:47:46 +0530
committerMichael Meeks <michael.meeks@collabora.com>2020-10-23 02:01:43 +0200
commit5001ae871df204035cbea77d7a5bcd849e133a2b (patch)
treeabc524e5e0d740fee3c3ade189c300baf3977b31
parentb6c60a98f4c0cb08107a221486fa52aef6ca4da7 (diff)
Improve spell checking performance and impl. in several ways:
* do synchronous spell checking, avoiding an idle handler * avoid continuous invalidations caused per-cell by spell-checking * cache spell-checking information for a given SharedString to avoid repeated checking of frequently recurring strings. Change-Id: Ie251f263a8932465297dd8bd66dfc4aa10984947 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103941 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Reviewed-by: Michael Meeks <michael.meeks@collabora.com>
-rw-r--r--sc/inc/scmod.hxx2
-rw-r--r--sc/inc/spellcheckcontext.hxx72
-rw-r--r--sc/source/ui/app/scmod.cxx37
-rw-r--r--sc/source/ui/inc/gridwin.hxx4
-rw-r--r--sc/source/ui/inc/output.hxx2
-rw-r--r--sc/source/ui/inc/tabview.hxx2
-rw-r--r--sc/source/ui/view/dbfunc.cxx2
-rw-r--r--sc/source/ui/view/gridwin.cxx200
-rw-r--r--sc/source/ui/view/gridwin2.cxx6
-rw-r--r--sc/source/ui/view/spellcheckcontext.cxx381
-rw-r--r--sc/source/ui/view/tabview.cxx18
-rw-r--r--sc/source/ui/view/viewfun2.cxx32
-rw-r--r--sc/source/ui/view/viewfun3.cxx15
-rw-r--r--sc/source/ui/view/viewfunc.cxx7
14 files changed, 447 insertions, 333 deletions
diff --git a/sc/inc/scmod.hxx b/sc/inc/scmod.hxx
index e214771bd2e4..868f3d77c73c 100644
--- a/sc/inc/scmod.hxx
+++ b/sc/inc/scmod.hxx
@@ -81,7 +81,6 @@ class SfxDialogController;
class SAL_DLLPUBLIC_RTTI ScModule final : public SfxModule, public SfxListener, public utl::ConfigurationListener
{
Timer m_aIdleTimer;
- Idle m_aSpellIdle;
std::unique_ptr<ScDragData> m_pDragData;
ScSelectionTransferObj* m_pSelTransfer;
ScMessagePool* m_pMessagePool;
@@ -131,7 +130,6 @@ public:
// moved by the application
DECL_LINK( IdleHandler, Timer*, void ); // Timer instead of idle
- DECL_LINK( SpellTimerHdl, Timer*, void );
DECL_LINK( CalcFieldValueHdl, EditFieldInfo*, void );
void Execute( SfxRequest& rReq );
diff --git a/sc/inc/spellcheckcontext.hxx b/sc/inc/spellcheckcontext.hxx
index 42ec80389af2..07cab368443b 100644
--- a/sc/inc/spellcheckcontext.hxx
+++ b/sc/inc/spellcheckcontext.hxx
@@ -10,50 +10,54 @@
#ifndef INCLUDED_SC_INC_SPELLCHECKCONTEXT_HXX
#define INCLUDED_SC_INC_SPELLCHECKCONTEXT_HXX
+#include <i18nlangtag/lang.h>
#include <editeng/misspellrange.hxx>
#include "types.hxx"
-#include <unordered_map>
+#include <memory>
#include <vector>
-namespace sc {
+class ScDocument;
+class ScTabEditEngine;
-struct SpellCheckContext
+namespace sc
{
- struct CellPos
- {
- struct Hash
- {
- size_t operator() (const CellPos& rPos) const;
- };
-
- SCCOL mnCol;
- SCROW mnRow;
-
- CellPos();
- CellPos(SCCOL nCol, SCROW nRow);
-
- void setInvalid();
- bool isValid() const;
- void reset();
-
- bool operator== (const CellPos& r) const;
- };
-
- typedef std::unordered_map<CellPos, std::vector<editeng::MisspellRanges>, CellPos::Hash> CellMapType;
-
- CellPos maPos;
- CellMapType maMisspellCells;
-
- SpellCheckContext();
-
- bool isMisspelled( SCCOL nCol, SCROW nRow ) const;
- const std::vector<editeng::MisspellRanges>* getMisspellRanges( SCCOL nCol, SCROW nRow ) const;
- void setMisspellRanges( SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pRanges );
+/**
+ * Class shared between grid windows to cache
+ * spelling results.
+ */
+class SpellCheckContext
+{
+ class SpellCheckCache;
+ struct SpellCheckStatus;
+ struct SpellCheckResult;
+
+ std::unique_ptr<SpellCheckCache> mpCache;
+ std::unique_ptr<SpellCheckResult> mpResult;
+ ScDocument* pDoc;
+ std::unique_ptr<ScTabEditEngine> mpEngine;
+ std::unique_ptr<SpellCheckStatus> mpStatus;
+ SCTAB mnTab;
+ LanguageType meLanguage;
+
+public:
+ SpellCheckContext(ScDocument* pDocument, SCTAB nTab);
+ ~SpellCheckContext();
+ void dispose();
+
+ bool isMisspelled(SCCOL nCol, SCROW nRow) const;
+ const std::vector<editeng::MisspellRanges>* getMisspellRanges(SCCOL nCol, SCROW nRow) const;
+ void setMisspellRanges(SCCOL nCol, SCROW nRow,
+ const std::vector<editeng::MisspellRanges>* pRanges);
void reset();
-};
+ void resetForContentChange();
+private:
+ void ensureResults(SCCOL nCol, SCROW nRow);
+ void resetCache(bool bContentChangeOnly = false);
+ void resetEngine();
+};
}
#endif
diff --git a/sc/source/ui/app/scmod.cxx b/sc/source/ui/app/scmod.cxx
index 59fc059bf5ef..fe71ab45b6b3 100644
--- a/sc/source/ui/app/scmod.cxx
+++ b/sc/source/ui/app/scmod.cxx
@@ -116,7 +116,6 @@ void ScModule::InitInterface_Impl()
ScModule::ScModule( SfxObjectFactory* pFact ) :
SfxModule("sc", {pFact}),
m_aIdleTimer("sc ScModule IdleTimer"),
- m_aSpellIdle("sc ScModule SpellIdle"),
m_pDragData(new ScDragData),
m_pSelTransfer( nullptr ),
m_pMessagePool( nullptr ),
@@ -143,8 +142,6 @@ ScModule::ScModule( SfxObjectFactory* pFact ) :
ErrCodeArea::Sc,
GetResLocale()) );
- m_aSpellIdle.SetInvokeHandler( LINK( this, ScModule, SpellTimerHdl ) );
-
m_aIdleTimer.SetTimeout(SC_IDLE_MIN);
m_aIdleTimer.SetInvokeHandler( LINK( this, ScModule, IdleHandler ) );
m_aIdleTimer.Start();
@@ -1820,16 +1817,11 @@ IMPL_LINK_NOARG(ScModule, IdleHandler, Timer *, void)
}
bool bMore = false;
- bool bAutoSpell = false;
ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(SfxObjectShell::Current());
if ( pDocSh )
{
ScDocument& rDoc = pDocSh->GetDocument();
- bAutoSpell = rDoc.GetDocOptions().IsAutoSpell();
- if (pDocSh->IsReadOnly())
- bAutoSpell = false;
-
sc::DocumentLinkManager& rLinkMgr = rDoc.GetDocLinkManager();
bool bLinks = rLinkMgr.idleCheckLinks();
bool bWidth = rDoc.IdleCalcTextWidth();
@@ -1842,19 +1834,6 @@ IMPL_LINK_NOARG(ScModule, IdleHandler, Timer *, void)
lcl_CheckNeedsRepaint( pDocSh );
}
- if (bAutoSpell)
- {
- ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
- if (pViewSh)
- {
- bool bSpell = pViewSh->ContinueOnlineSpelling();
- if (bSpell)
- {
- m_aSpellIdle.Start();
- bMore = true;
- }
- }
- }
sal_uInt64 nOldTime = m_aIdleTimer.GetTimeout();
sal_uInt64 nNewTime = nOldTime;
@@ -1882,22 +1861,6 @@ IMPL_LINK_NOARG(ScModule, IdleHandler, Timer *, void)
m_aIdleTimer.Start();
}
-IMPL_LINK_NOARG(ScModule, SpellTimerHdl, Timer *, void)
-{
- if ( Application::AnyInput( VclInputFlags::KEYBOARD ) )
- {
- m_aSpellIdle.Start();
- return; // Later again ...
- }
-
- ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
- if (pViewSh)
- {
- if (pViewSh->ContinueOnlineSpelling())
- m_aSpellIdle.Start();
- }
-}
-
/**
* Virtual methods for the OptionsDialog
*/
diff --git a/sc/source/ui/inc/gridwin.hxx b/sc/source/ui/inc/gridwin.hxx
index 3a2a41d556ab..5623a0c92d11 100644
--- a/sc/source/ui/inc/gridwin.hxx
+++ b/sc/source/ui/inc/gridwin.hxx
@@ -36,7 +36,7 @@ namespace editeng {
}
namespace sc {
- struct SpellCheckContext;
+ class SpellCheckContext;
}
namespace sdr { namespace overlay { class OverlayManager; } }
@@ -428,9 +428,9 @@ public:
void CursorChanged();
void DrawLayerCreated();
- bool ContinueOnlineSpelling();
void EnableAutoSpell( bool bEnable );
void ResetAutoSpell();
+ void ResetAutoSpellForContentChange();
void SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges );
const std::vector<editeng::MisspellRanges>* GetAutoSpellData( SCCOL nPosX, SCROW nPosY );
bool InsideVisibleRange( SCCOL nPosX, SCROW nPosY );
diff --git a/sc/source/ui/inc/output.hxx b/sc/source/ui/inc/output.hxx
index fbdd991962cc..0053055b1265 100644
--- a/sc/source/ui/inc/output.hxx
+++ b/sc/source/ui/inc/output.hxx
@@ -30,7 +30,7 @@
#include <o3tl/deleter.hxx>
namespace sc {
- struct SpellCheckContext;
+ class SpellCheckContext;
}
namespace editeng {
diff --git a/sc/source/ui/inc/tabview.hxx b/sc/source/ui/inc/tabview.hxx
index 8d75d5137b92..287513af1e00 100644
--- a/sc/source/ui/inc/tabview.hxx
+++ b/sc/source/ui/inc/tabview.hxx
@@ -598,9 +598,9 @@ public:
void SetDrawBrushSet( std::unique_ptr<SfxItemSet> pNew, bool bLock );
void ResetBrushDocument();
- bool ContinueOnlineSpelling();
void EnableAutoSpell( bool bEnable );
void ResetAutoSpell();
+ void ResetAutoSpellForContentChange();
void SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges );
/// @see ScModelObj::getRowColumnHeaders().
OUString getRowColumnHeaders(const tools::Rectangle& rRectangle);
diff --git a/sc/source/ui/view/dbfunc.cxx b/sc/source/ui/view/dbfunc.cxx
index 5e0a686a8c09..8280699888f9 100644
--- a/sc/source/ui/view/dbfunc.cxx
+++ b/sc/source/ui/view/dbfunc.cxx
@@ -225,7 +225,7 @@ void ScDBFunc::Sort( const ScSortParam& rSortParam, bool bRecord, bool bPaint )
MarkRange( aDestRange );
}
- ResetAutoSpell();
+ ResetAutoSpellForContentChange();
}
// filters
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 074799d44445..569395c99d6b 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -500,6 +500,8 @@ void ScGridWindow::dispose()
mpDPFieldPopup.disposeAndClear();
aComboButton.SetOutputDevice(nullptr);
+ mpSpellCheckCxt.reset();
+
vcl::Window::dispose();
}
@@ -5526,191 +5528,10 @@ void ScGridWindow::DrawLayerCreated()
ImpCreateOverlayObjects();
}
-namespace {
-
-struct SpellCheckStatus
-{
- bool mbModified;
-
- SpellCheckStatus() : mbModified(false) {};
-
- DECL_LINK( EventHdl, EditStatus&, void );
-};
-
-IMPL_LINK(SpellCheckStatus, EventHdl, EditStatus&, rStatus, void)
-{
- EditStatusFlags nStatus = rStatus.GetStatusWord();
- if (nStatus & EditStatusFlags::WRONGWORDCHANGED)
- mbModified = true;
-}
-
-}
-
-bool ScGridWindow::ContinueOnlineSpelling()
-{
- if (!mpSpellCheckCxt)
- return false;
-
- if (!mpSpellCheckCxt->maPos.isValid())
- return false;
-
- ScDocument* pDoc = pViewData->GetDocument();
- ScDPCollection* pDPs = nullptr;
- if (pDoc->HasPivotTable())
- pDPs = pDoc->GetDPCollection();
-
- SCTAB nTab = pViewData->GetTabNo();
- SpellCheckStatus aStatus;
-
- ScHorizontalCellIterator aIter(
- pDoc, nTab, maVisibleRange.mnCol1, mpSpellCheckCxt->maPos.mnRow, maVisibleRange.mnCol2, maVisibleRange.mnRow2);
-
- ScRangeList aPivotRanges = pDPs ? pDPs->GetAllTableRanges(nTab) : ScRangeList();
-
- SCCOL nCol;
- SCROW nRow;
- ScRefCellValue* pCell = aIter.GetNext(nCol, nRow);
- SCROW nEndRow = 0;
- bool bHidden = pCell && pDoc->RowHidden(nRow, nTab, nullptr, &nEndRow);
- bool bSkip = pCell && (nRow < mpSpellCheckCxt->maPos.mnRow || bHidden);
- while (bSkip)
- {
- pCell = aIter.GetNext(nCol, nRow);
- if (pCell && nRow > nEndRow)
- {
- bHidden = pDoc->RowHidden(nRow, nTab, nullptr, &nEndRow);
- }
- bSkip = pCell && (nRow < mpSpellCheckCxt->maPos.mnRow || bHidden);
- }
-
- SCCOL nEndCol = 0;
- bHidden = pCell && pDoc->ColHidden(nCol, nTab, nullptr, &nEndCol);
- bSkip = pCell && (nCol < mpSpellCheckCxt->maPos.mnCol || bHidden);
- while (bSkip)
- {
- pCell = aIter.GetNext(nCol, nRow);
- if (pCell && nCol > nEndCol)
- {
- bHidden = pDoc->ColHidden(nCol, nTab, nullptr, &nEndCol);
- }
- bSkip = pCell && (nCol < mpSpellCheckCxt->maPos.mnCol || bHidden);
- }
-
- std::unique_ptr<ScTabEditEngine> pEngine;
-
- // Check only up to 256 cells at a time.
- size_t nTotalCellCount = 0;
- size_t nTextCellCount = 0;
- bool bSpellCheckPerformed = false;
-
- while (pCell)
- {
- ++nTotalCellCount;
-
- if (aPivotRanges.In(ScAddress(nCol, nRow, nTab)))
- {
- // Don't spell check within pivot tables.
- if (nTotalCellCount >= 255)
- break;
-
- pCell = aIter.GetNext(nCol, nRow);
- continue;
- }
-
- CellType eType = pCell->meType;
- if (eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT)
- {
- ++nTextCellCount;
-
- // NB: For spell-checking, we currently only use the primary
- // language; not CJK nor CTL.
- const ScPatternAttr* pPattern = pDoc->GetPattern(nCol, nRow, nTab);
- LanguageType nCellLang = pPattern->GetItem(ATTR_FONT_LANGUAGE).GetValue();
-
- if (nCellLang == LANGUAGE_SYSTEM)
- nCellLang = Application::GetSettings().GetLanguageTag().getLanguageType(); // never use SYSTEM for spelling
-
- if (nCellLang == LANGUAGE_NONE)
- {
- // No need to spell check this cell.
- pCell = aIter.GetNext(nCol, nRow);
- continue;
- }
-
- if (!pEngine)
- {
- // ScTabEditEngine is needed
- // because MapMode must be set for some old documents
- pEngine.reset(new ScTabEditEngine(pDoc));
- pEngine->SetControlWord(
- pEngine->GetControlWord() | (EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS));
- pEngine->SetStatusEventHdl(LINK(&aStatus, SpellCheckStatus, EventHdl));
- // Delimiters here like in inputhdl.cxx !!!
- pEngine->SetWordDelimiters(
- ScEditUtil::ModifyDelimiters(pEngine->GetWordDelimiters()));
-
- uno::Reference<linguistic2::XSpellChecker1> xXSpellChecker1(LinguMgr::GetSpellChecker());
- pEngine->SetSpeller(xXSpellChecker1);
- pEngine->SetDefaultLanguage(ScGlobal::GetEditDefaultLanguage());
- }
-
- pEngine->SetDefaultItem(SvxLanguageItem(nCellLang, EE_CHAR_LANGUAGE));
-
- if (eType == CELLTYPE_STRING)
- pEngine->SetText(pCell->mpString->getString());
- else
- pEngine->SetText(*pCell->mpEditText);
-
- aStatus.mbModified = false;
- pEngine->CompleteOnlineSpelling();
- if (aStatus.mbModified)
- {
- std::vector<editeng::MisspellRanges> aRanges;
- pEngine->GetAllMisspellRanges(aRanges);
- if (!aRanges.empty())
- {
- sc::SpellCheckContext::CellPos aPos(nCol, nRow);
- mpSpellCheckCxt->maMisspellCells.emplace(aPos, aRanges);
- }
-
- // Broadcast for re-paint.
- ScPaintHint aHint(ScRange(nCol, nRow, nTab), PaintPartFlags::Grid);
- aHint.SetPrintFlag(false);
- pDoc->GetDocumentShell()->Broadcast(aHint);
- }
-
- bSpellCheckPerformed = true;
- }
-
- if (nTotalCellCount >= 255 || nTextCellCount >= 1)
- break;
-
- pCell = aIter.GetNext(nCol, nRow);
- }
-
- if (pCell)
- // Move to the next cell position for the next iteration.
- pCell = aIter.GetNext(nCol, nRow);
-
- if (pCell)
- {
- // This will become the first cell position for the next time.
- mpSpellCheckCxt->maPos.mnCol = nCol;
- mpSpellCheckCxt->maPos.mnRow = nRow;
- }
- else
- {
- // No more cells to spell check.
- mpSpellCheckCxt->maPos.setInvalid();
- }
-
- return bSpellCheckPerformed;
-}
-
void ScGridWindow::EnableAutoSpell( bool bEnable )
{
if (bEnable)
- mpSpellCheckCxt.reset(new sc::SpellCheckContext);
+ mpSpellCheckCxt.reset(new sc::SpellCheckContext(pViewData->GetDocument(), pViewData->GetTabNo()));
else
mpSpellCheckCxt.reset();
}
@@ -5718,11 +5539,13 @@ void ScGridWindow::EnableAutoSpell( bool bEnable )
void ScGridWindow::ResetAutoSpell()
{
if (mpSpellCheckCxt)
- {
mpSpellCheckCxt->reset();
- mpSpellCheckCxt->maPos.mnCol = maVisibleRange.mnCol1;
- mpSpellCheckCxt->maPos.mnRow = maVisibleRange.mnRow1;
- }
+}
+
+void ScGridWindow::ResetAutoSpellForContentChange()
+{
+ if (mpSpellCheckCxt)
+ mpSpellCheckCxt->resetForContentChange();
}
void ScGridWindow::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector<editeng::MisspellRanges>* pRanges )
@@ -5730,9 +5553,6 @@ void ScGridWindow::SetAutoSpellData( SCCOL nPosX, SCROW nPosY, const std::vector
if (!mpSpellCheckCxt)
return;
- if (!maVisibleRange.isInside(nPosX, nPosY))
- return;
-
mpSpellCheckCxt->setMisspellRanges(nPosX, nPosY, pRanges);
}
@@ -6100,7 +5920,7 @@ void ScGridWindow::updateOtherKitSelections() const
return;
const ScGridWindow *pGrid = pOther->GetViewData().GetActiveWin();
- assert(pGrid);
+ assert(pGrid); (void)pGrid;
// Fetch pixels & convert for each view separately.
tools::Rectangle aBoundingBox;
diff --git a/sc/source/ui/view/gridwin2.cxx b/sc/source/ui/view/gridwin2.cxx
index 0dfe3ef20fab..429e4648a2bf 100644
--- a/sc/source/ui/view/gridwin2.cxx
+++ b/sc/source/ui/view/gridwin2.cxx
@@ -636,11 +636,7 @@ bool ScGridWindow::UpdateVisibleRange()
}
// Store the current visible range.
- bool bChanged = maVisibleRange.set(nPosX, nPosY, nXRight, nYBottom);
- if (bChanged)
- ResetAutoSpell();
-
- return bChanged;
+ return maVisibleRange.set(nPosX, nPosY, nXRight, nYBottom);
}
void ScGridWindow::DPMouseMove( const MouseEvent& rMEvt )
diff --git a/sc/source/ui/view/spellcheckcontext.cxx b/sc/source/ui/view/spellcheckcontext.cxx
index 867dc26f5738..4b90bdb0c099 100644
--- a/sc/source/ui/view/spellcheckcontext.cxx
+++ b/sc/source/ui/view/spellcheckcontext.cxx
@@ -8,88 +8,389 @@
*/
#include <spellcheckcontext.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <svl/sharedstring.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/unolingu.hxx>
+
+#include <scitems.hxx>
+#include <attarray.hxx>
+#include <document.hxx>
+#include <cellvalue.hxx>
+#include <editutil.hxx>
+#include <dpobject.hxx>
+
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+
#include <boost/functional/hash.hpp>
-namespace sc {
+#include <unordered_map>
+
+using namespace css;
-size_t SpellCheckContext::CellPos::Hash::operator() (const CellPos& rPos) const
+using sc::SpellCheckContext;
+
+class SpellCheckContext::SpellCheckCache
{
- std::size_t seed = 0;
- boost::hash_combine(seed, rPos.mnCol);
- boost::hash_combine(seed, rPos.mnRow);
- return seed;
-}
+ struct CellPos
+ {
+ struct Hash
+ {
+ size_t operator() (const CellPos& rPos) const
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, rPos.mnCol);
+ boost::hash_combine(seed, rPos.mnRow);
+ return seed;
+ }
+ };
+
+ SCCOL mnCol;
+ SCROW mnRow;
+
+ CellPos() : mnCol(0), mnRow(0) {}
+ CellPos(SCCOL nCol, SCROW nRow) : mnCol(nCol), mnRow(nRow) {}
+
+ void setInvalid()
+ {
+ mnCol = -1;
+ mnRow = -1;
+ }
+
+ bool isValid() const
+ {
+ return mnCol >= 0 && mnRow >= 0;
+ }
+
+ void reset()
+ {
+ mnCol = 0;
+ mnRow = 0;
+ }
+
+ bool operator== (const CellPos& r) const
+ {
+ return mnCol == r.mnCol && mnRow == r.mnRow;
+ }
+
+ };
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+ typedef std::unordered_map<CellPos, std::unique_ptr<MisspellType>, CellPos::Hash> CellMapType;
+ typedef std::unordered_map<const rtl_uString*, std::unique_ptr<MisspellType>> SharedStringMapType;
+ typedef std::unordered_map<CellPos, LanguageType, CellPos::Hash> CellLangMapType;
+
+ SharedStringMapType maStringMisspells;
+ CellMapType maEditTextMisspells;
+ CellLangMapType maCellLanguages;
+ LanguageType meDefCellLanguage;
+
+public:
+
+ SpellCheckCache(LanguageType eDefaultCellLanguage) : meDefCellLanguage(eDefaultCellLanguage)
+ {
+ }
+
+ bool query(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, MisspellType*& rpRanges) const
+ {
+ CellType eType = rCell.meType;
+ if (eType == CELLTYPE_STRING)
+ {
+ SharedStringMapType::const_iterator it = maStringMisspells.find(rCell.mpString->getData());
+ if (it == maStringMisspells.end())
+ return false; // Not available
+
+ rpRanges = it->second.get();
+ return true;
+ }
+
+ if (eType == CELLTYPE_EDIT)
+ {
+ CellMapType::const_iterator it = maEditTextMisspells.find(CellPos(nCol, nRow));
+ if (it == maEditTextMisspells.end())
+ return false; // Not available
+
+ rpRanges = it->second.get();
+ return true;
+ }
+
+ rpRanges = nullptr;
+ return true;
+ }
+
+ void set(SCCOL nCol, SCROW nRow, const ScRefCellValue& rCell, std::unique_ptr<MisspellType> pRanges)
+ {
+ CellType eType = rCell.meType;
+ if (eType == CELLTYPE_STRING)
+ maStringMisspells.insert_or_assign(rCell.mpString->getData(), std::move(pRanges));
+ else if (eType == CELLTYPE_EDIT)
+ maEditTextMisspells.insert_or_assign(CellPos(nCol, nRow), std::move(pRanges));
+ }
+
+ LanguageType getLanguage(SCCOL nCol, SCROW nRow) const
+ {
+ CellLangMapType::const_iterator it = maCellLanguages.find(CellPos(nCol, nRow));
+ if (it == maCellLanguages.end())
+ return meDefCellLanguage;
+
+ return it->second;
+ }
+
+ void setLanguage(LanguageType eCellLang, SCCOL nCol, SCROW nRow)
+ {
+ if (eCellLang == meDefCellLanguage)
+ maCellLanguages.erase(CellPos(nCol, nRow));
+ else
+ maCellLanguages.insert_or_assign(CellPos(nCol, nRow), eCellLang);
+ }
+
+ void clear(LanguageType eDefaultCellLanguage)
+ {
+ maStringMisspells.clear();
+ maEditTextMisspells.clear();
+ maCellLanguages.clear();
+ meDefCellLanguage = eDefaultCellLanguage;
+ }
-SpellCheckContext::CellPos::CellPos() : mnCol(0), mnRow(0) {}
-SpellCheckContext::CellPos::CellPos(SCCOL nCol, SCROW nRow) : mnCol(nCol), mnRow(nRow) {}
+ void clearEditTextMap()
+ {
+ maEditTextMisspells.clear();
+ }
+};
-void SpellCheckContext::CellPos::setInvalid()
+struct SpellCheckContext::SpellCheckStatus
{
- mnCol = -1;
- mnRow = -1;
-}
+ bool mbModified;
+
+ SpellCheckStatus() : mbModified(false) {};
+
+ DECL_LINK( EventHdl, EditStatus&, void );
+};
-bool SpellCheckContext::CellPos::isValid() const
+IMPL_LINK(SpellCheckContext::SpellCheckStatus, EventHdl, EditStatus&, rStatus, void)
{
- return mnCol >= 0 && mnRow >= 0;
+ EditStatusFlags nStatus = rStatus.GetStatusWord();
+ if (nStatus & EditStatusFlags::WRONGWORDCHANGED)
+ mbModified = true;
}
-void SpellCheckContext::CellPos::reset()
+struct SpellCheckContext::SpellCheckResult
{
- mnCol = 0;
- mnRow = 0;
+ SCCOL mnCol;
+ SCROW mnRow;
+ const std::vector<editeng::MisspellRanges>* pRanges;
+
+ SpellCheckResult() : mnCol(-1), mnRow(-1), pRanges(nullptr) {}
+
+ void set(SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pMisspells)
+ {
+ mnCol = nCol;
+ mnRow = nRow;
+ pRanges = pMisspells;
+ }
+
+ const std::vector<editeng::MisspellRanges>* query(SCCOL nCol, SCROW nRow) const
+ {
+ assert(mnCol == nCol);
+ assert(mnRow == nRow);
+ (void)(nCol);
+ (void)(nRow);
+ return pRanges;
+ }
+
+ void clear()
+ {
+ mnCol = -1;
+ mnRow = -1;
+ pRanges = nullptr;
+ }
+};
+
+SpellCheckContext::SpellCheckContext(ScDocument* pDocument, SCTAB nTab) :
+ pDoc(pDocument),
+ mnTab(nTab),
+ meLanguage(ScGlobal::GetEditDefaultLanguage())
+{
+ // defer init of engine and cache till the first query/set
}
-bool SpellCheckContext::CellPos::operator== (const CellPos& r) const
+SpellCheckContext::~SpellCheckContext()
{
- return mnCol == r.mnCol && mnRow == r.mnRow;
}
-SpellCheckContext::SpellCheckContext()
+void SpellCheckContext::dispose()
{
+ mpEngine.reset();
+ mpCache.reset();
+ pDoc = nullptr;
}
-bool SpellCheckContext::isMisspelled( SCCOL nCol, SCROW nRow ) const
+bool SpellCheckContext::isMisspelled(SCCOL nCol, SCROW nRow) const
{
- return maMisspellCells.count(CellPos(nCol, nRow)) > 0;
+ const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
+ return mpResult->query(nCol, nRow);
}
const std::vector<editeng::MisspellRanges>* SpellCheckContext::getMisspellRanges(
SCCOL nCol, SCROW nRow ) const
{
- CellMapType::const_iterator it = maMisspellCells.find(CellPos(nCol,nRow));
- if (it == maMisspellCells.end())
- return nullptr;
-
- return &it->second;
+ const_cast<SpellCheckContext*>(this)->ensureResults(nCol, nRow);
+ return mpResult->query(nCol, nRow);
}
void SpellCheckContext::setMisspellRanges(
SCCOL nCol, SCROW nRow, const std::vector<editeng::MisspellRanges>* pRanges )
{
- CellPos aPos(nCol, nRow);
- CellMapType::iterator it = maMisspellCells.find(aPos);
+ if (!mpEngine || !mpCache)
+ reset();
+
+ ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
+ CellType eType = aCell.meType;
+
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ return;
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+ std::unique_ptr<MisspellType> pMisspells(pRanges ? new MisspellType(*pRanges) : nullptr);
+ mpCache->set(nCol, nRow, aCell, std::move(pMisspells));
+}
+
+void SpellCheckContext::reset()
+{
+ meLanguage = ScGlobal::GetEditDefaultLanguage();
+ resetCache();
+ resetEngine();
+}
+
+void SpellCheckContext::resetForContentChange()
+{
+ resetCache(true /* bContentChangeOnly */);
+}
+
+void SpellCheckContext::ensureResults(SCCOL nCol, SCROW nRow)
+{
+ if (!mpEngine || !mpCache)
+ reset();
- if (pRanges)
+ // perhaps compute the pivot rangelist once in some pivot-table change handler ?
+ if (pDoc->HasPivotTable())
{
- if (it == maMisspellCells.end())
- maMisspellCells.emplace(aPos, *pRanges);
- else
- it->second = *pRanges;
+ if (ScDPCollection* pDPs = pDoc->GetDPCollection())
+ {
+ ScRangeList aPivotRanges = pDPs->GetAllTableRanges(mnTab);
+ if (aPivotRanges.In(ScAddress(nCol, nRow, mnTab))) // Don't spell check within pivot tables
+ {
+ mpResult->set(nCol, nRow, nullptr);
+ return;
+ }
+ }
+ }
+
+ ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, mnTab));
+ CellType eType = aCell.meType;
+
+ if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT)
+ {
+ // No spell-check required.
+ mpResult->set(nCol, nRow, nullptr);
+ return;
}
+
+ if (ScGlobal::GetEditDefaultLanguage() != meLanguage)
+ reset();
+
+ // Cell content is either shared-string or EditTextObject
+
+ // For spell-checking, we currently only use the primary
+ // language; not CJK nor CTL.
+ const ScPatternAttr* pPattern = pDoc->GetPattern(nCol, nRow, mnTab);
+ LanguageType eCellLang = pPattern->GetItem(ATTR_FONT_LANGUAGE).GetValue();
+
+ if (eCellLang == LANGUAGE_SYSTEM)
+ eCellLang = meLanguage; // never use SYSTEM for spelling
+
+ if (eCellLang == LANGUAGE_NONE)
+ {
+ mpResult->set(nCol, nRow, nullptr); // No need to spell check this cell.
+ return;
+ }
+
+ typedef std::vector<editeng::MisspellRanges> MisspellType;
+
+ LanguageType eCachedCellLang = mpCache->getLanguage(nCol, nRow);
+
+ if (eCellLang != eCachedCellLang)
+ mpCache->setLanguage(eCellLang, nCol, nRow);
+
else
{
- if (it != maMisspellCells.end())
- maMisspellCells.erase(it);
+ MisspellType* pRanges = nullptr;
+ bool bFound = mpCache->query(nCol, nRow, aCell, pRanges);
+ if (bFound)
+ {
+ // Cache hit.
+ mpResult->set(nCol, nRow, pRanges);
+ return;
+ }
}
+
+ // Cache miss, the cell needs spell-check..
+ mpEngine->SetDefaultItem(SvxLanguageItem(eCellLang, EE_CHAR_LANGUAGE));
+ if (eType == CELLTYPE_STRING)
+ mpEngine->SetText(aCell.mpString->getString());
+ else
+ mpEngine->SetText(*aCell.mpEditText);
+
+ mpStatus->mbModified = false;
+ mpEngine->CompleteOnlineSpelling();
+ std::unique_ptr<MisspellType> pRanges;
+ if (mpStatus->mbModified)
+ {
+ pRanges.reset(new MisspellType());
+ mpEngine->GetAllMisspellRanges(*pRanges);
+
+ if (pRanges->empty())
+ pRanges.reset(nullptr);
+ }
+ // else : No change in status for EditStatusFlags::WRONGWORDCHANGED => no spell errors (which is the default status).
+
+ mpResult->set(nCol, nRow, pRanges.get());
+ mpCache->set(nCol, nRow, aCell, std::move(pRanges));
+
}
-void SpellCheckContext::reset()
+void SpellCheckContext::resetCache(bool bContentChangeOnly)
{
- maPos.reset();
- maMisspellCells.clear();
+ if (!mpResult)
+ mpResult.reset(new SpellCheckResult());
+ else
+ mpResult->clear();
+
+ if (!mpCache)
+ mpCache.reset(new SpellCheckCache(meLanguage));
+ else if (bContentChangeOnly)
+ mpCache->clearEditTextMap();
+ else
+ mpCache->clear(meLanguage);
}
+void SpellCheckContext::resetEngine()
+{
+ mpEngine.reset(new ScTabEditEngine(pDoc));
+ mpStatus.reset(new SpellCheckStatus());
+
+ mpEngine->SetControlWord(
+ mpEngine->GetControlWord() | (EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS));
+ mpEngine->SetStatusEventHdl(LINK(mpStatus.get(), SpellCheckStatus, EventHdl));
+ // Delimiters here like in inputhdl.cxx !!!
+ mpEngine->SetWordDelimiters(
+ ScEditUtil::ModifyDelimiters(mpEngine->GetWordDelimiters()));
+
+ uno::Reference<linguistic2::XSpellChecker1> xXSpellChecker1(LinguMgr::GetSpellChecker());
+ mpEngine->SetSpeller(xXSpellChecker1);
+ mpEngine->SetDefaultLanguage(meLanguage);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx
index c1981307563a..2c6dd86e368d 100644
--- a/sc/source/ui/view/tabview.cxx
+++ b/sc/source/ui/view/tabview.cxx
@@ -2235,40 +2235,36 @@ void ScTabView::EnableRefInput(bool bFlag)
p->EnableInput(bFlag, false);
}
-bool ScTabView::ContinueOnlineSpelling()
+void ScTabView::EnableAutoSpell( bool bEnable )
{
- bool bChanged = false;
for (VclPtr<ScGridWindow> & pWin : pGridWin)
{
- if (!pWin || !pWin->IsVisible())
+ if (!pWin)
continue;
- if (pWin->ContinueOnlineSpelling())
- bChanged = true;
+ pWin->EnableAutoSpell(bEnable);
}
-
- return bChanged;
}
-void ScTabView::EnableAutoSpell( bool bEnable )
+void ScTabView::ResetAutoSpell()
{
for (VclPtr<ScGridWindow> & pWin : pGridWin)
{
if (!pWin)
continue;
- pWin->EnableAutoSpell(bEnable);
+ pWin->ResetAutoSpell();
}
}
-void ScTabView::ResetAutoSpell()
+void ScTabView::ResetAutoSpellForContentChange()
{
for (VclPtr<ScGridWindow> & pWin : pGridWin)
{
if (!pWin)
continue;
- pWin->ResetAutoSpell();
+ pWin->ResetAutoSpellForContentChange();
}
}
diff --git a/sc/source/ui/view/viewfun2.cxx b/sc/source/ui/view/viewfun2.cxx
index d675e3dca9a7..5b631f209fee 100644
--- a/sc/source/ui/view/viewfun2.cxx
+++ b/sc/source/ui/view/viewfun2.cxx
@@ -1464,6 +1464,10 @@ void ScViewFunc::FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
{
+ const ScDocument* pDoc = GetViewData().GetDocument();
+ SCTAB nTab = GetViewData().GetTabNo();
+ CellType eCellType;
+
ScGridWindow* pWin = GetActiveWin();
if ( pWin->InsideVisibleRange(nStartCol, nStartRow) && pWin->InsideVisibleRange(nEndCol, nEndRow) )
{
@@ -1474,6 +1478,10 @@ void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartR
case FILL_TO_BOTTOM:
for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
{
+ pDoc->GetCellType(nColItr, nStartRow, nTab, eCellType); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nStartRow);
if ( !pRanges )
continue;
@@ -1484,6 +1492,10 @@ void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartR
case FILL_TO_TOP:
for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
{
+ pDoc->GetCellType(nColItr, nEndRow, nTab, eCellType); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nEndRow);
if ( !pRanges )
continue;
@@ -1494,6 +1506,10 @@ void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartR
case FILL_TO_RIGHT:
for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
{
+ pDoc->GetCellType(nStartCol, nRowItr, nTab, eCellType); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nStartCol, nRowItr);
if ( !pRanges )
continue;
@@ -1504,6 +1520,10 @@ void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartR
case FILL_TO_LEFT:
for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
{
+ pDoc->GetCellType(nEndCol, nRowItr, nTab, eCellType); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nEndCol, nRowItr);
if ( !pRanges )
continue;
@@ -1520,11 +1540,19 @@ void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartR
SCCOL nColRepeatSize = nEndCol - nStartCol + 1;
SCROW nTillRow = 0;
SCCOL nTillCol = 0;
- std::vector<std::vector<MisspellRangesType>> aSourceSpellRanges(nRowRepeatSize, std::vector<MisspellRangesType>(nColRepeatSize));
+ std::vector<std::vector<MisspellRangesType>> aSourceSpellRanges(nRowRepeatSize, std::vector<MisspellRangesType>(nColRepeatSize, nullptr));
for ( SCROW nRowIdx = 0; nRowIdx < nRowRepeatSize; ++nRowIdx )
+ {
for ( SCROW nColIdx = 0; nColIdx < nColRepeatSize; ++nColIdx )
+ {
+ pDoc->GetCellType(nStartCol + nColIdx, nStartRow + nRowIdx, nTab, eCellType); // We need this optimization only for EditTextObject source cells
+ if (eCellType != CELLTYPE_EDIT)
+ continue;
+
aSourceSpellRanges[nRowIdx][nColIdx] = pWin->GetAutoSpellData( nStartCol + nColIdx, nStartRow + nRowIdx );
+ }
+ }
switch( eDir )
{
@@ -1590,7 +1618,7 @@ void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartR
}
}
else
- pWin->ResetAutoSpell();
+ pWin->ResetAutoSpellForContentChange();
}
diff --git a/sc/source/ui/view/viewfun3.cxx b/sc/source/ui/view/viewfun3.cxx
index 7a39f5037fa2..a1656d15c0eb 100644
--- a/sc/source/ui/view/viewfun3.cxx
+++ b/sc/source/ui/view/viewfun3.cxx
@@ -958,6 +958,9 @@ bool ScViewFunc::PasteFromClip( InsertDeleteFlags nFlags, ScDocument* pClipDoc,
ScDrawLayer::SetGlobalDrawPersist(nullptr);
}
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
SCCOL nStartCol;
SCROW nStartRow;
SCTAB nStartTab;
@@ -1449,7 +1452,6 @@ bool ScViewFunc::PasteFromClip( InsertDeleteFlags nFlags, ScDocument* pClipDoc,
nPaint, nExtFlags);
// AdjustBlockHeight has already been called above
- ResetAutoSpell();
aModificator.SetDocumentModified();
PostPasteFromClip(aUserRange, rMark);
@@ -1547,6 +1549,9 @@ bool ScViewFunc::PasteMultiRangesFromClip(
return false;
}
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
bool bRowInfo = ( aMarkedRange.aStart.Col()==0 && aMarkedRange.aEnd.Col()==pClipDoc->MaxCol() );
ScDocumentUniquePtr pUndoDoc;
if (pDoc->IsUndoEnabled())
@@ -1627,7 +1632,6 @@ bool ScViewFunc::PasteMultiRangesFromClip(
pUndoMgr->LeaveListAction();
}
- ResetAutoSpell();
aModificator.SetDocumentModified();
PostPasteFromClip(aMarkedRange, aMark);
return true;
@@ -1699,6 +1703,9 @@ bool ScViewFunc::PasteFromClipToMultiRanges(
return false;
}
+ // TODO: position this call better for performance.
+ ResetAutoSpellForContentChange();
+
ScDocumentUniquePtr pUndoDoc;
if (pDoc->IsUndoEnabled())
{
@@ -1787,7 +1794,6 @@ bool ScViewFunc::PasteFromClipToMultiRanges(
pUndoMgr->LeaveListAction();
}
- ResetAutoSpell();
aModificator.SetDocumentModified();
PostPasteFromClip(aRanges, aMark);
@@ -1831,6 +1837,8 @@ bool ScViewFunc::MoveBlockTo( const ScRange& rSource, const ScAddress& rDestPos,
ScDocShell* pDocSh = GetViewData().GetDocShell();
HideAllCursors();
+ ResetAutoSpellForContentChange();
+
bool bSuccess = true;
SCTAB nDestTab = rDestPos.Tab();
const ScMarkData& rMark = GetViewData().GetMarkData();
@@ -1902,7 +1910,6 @@ bool ScViewFunc::MoveBlockTo( const ScRange& rSource, const ScAddress& rDestPos,
pDocSh->UpdateOle(&GetViewData());
SelectionChanged();
- ResetAutoSpell();
}
return bSuccess;
}
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
index 4a7e05ccd3b3..e8ac749a9ae8 100644
--- a/sc/source/ui/view/viewfunc.cxx
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -1612,12 +1612,12 @@ bool ScViewFunc::InsertCells( InsCellCmd eCmd, bool bRecord, bool bPartOfPaste )
bool bSuccess = pDocSh->GetDocFunc().InsertCells( aRange, &rMark, eCmd, bRecord, false, bPartOfPaste );
if (bSuccess)
{
+ ResetAutoSpellForContentChange();
bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
pDocSh->UpdateOle(&GetViewData());
CellContentChanged();
- ResetAutoSpell();
if ( bInsertCols || bInsertRows )
{
@@ -1689,9 +1689,9 @@ void ScViewFunc::DeleteCells( DelCellCmd eCmd )
pDocSh->GetDocFunc().DeleteCells( aRange, &rMark, eCmd, false );
}
+ ResetAutoSpellForContentChange();
pDocSh->UpdateOle(&GetViewData());
CellContentChanged();
- ResetAutoSpell();
if ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols )
{
@@ -1836,6 +1836,8 @@ void ScViewFunc::DeleteMulti( bool bRows )
WaitObject aWait( GetFrameWin() ); // important for TrackFormulas in UpdateReference
+ ResetAutoSpellForContentChange();
+
ScDocumentUniquePtr pUndoDoc;
std::unique_ptr<ScRefUndoData> pUndoData;
if (bRecord)
@@ -1919,7 +1921,6 @@ void ScViewFunc::DeleteMulti( bool bRows )
}
}
- ResetAutoSpell();
aModificator.SetDocumentModified();
CellContentChanged();