include/sfx2/AccessibilityIssue.hxx | 1 sw/inc/IGrammarContact.hxx | 48 ++++-- sw/inc/doc.hxx | 6 sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx | 15 ++ sw/qa/core/accessibilitycheck/data/AccessibilityTests_NumberingCheck.odt |binary sw/source/core/access/AccessibilityCheck.cxx | 50 ++++-- sw/source/core/crsr/crsrsh.cxx | 7 sw/source/core/doc/docnew.cxx | 11 + sw/source/core/text/txtfrm.cxx | 2 sw/source/core/txtnode/SwGrammarContact.cxx | 73 ++-------- sw/source/core/unocore/unoflatpara.cxx | 2 sw/source/core/unocore/unotextmarkup.cxx | 4 12 files changed, 122 insertions(+), 97 deletions(-)
New commits: commit c7bcf51d3efe30047667fe026cbcd3cb87bdece3 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Wed Oct 12 21:36:52 2022 +0200 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Fri Oct 21 15:59:54 2022 +0200 sw: combine IGrammarContact and SwGrammarContact Change-Id: I4b9ab45ff8e21fa5091894f2cd5e3c7de57df425 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141431 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/sw/inc/IGrammarContact.hxx b/sw/inc/IGrammarContact.hxx index a2e4cecfeb2e..f2dc8ebe0ee2 100644 --- a/sw/inc/IGrammarContact.hxx +++ b/sw/inc/IGrammarContact.hxx @@ -19,29 +19,52 @@ #pragma once +#include <SwGrammarMarkUp.hxx> +#include <svl/listener.hxx> +#include <vcl/timer.hxx> + struct SwPosition; class SwTextNode; -class SwGrammarMarkUp; -/** Organizer of the contact between SwTextNodes and grammar checker -*/ -class IGrammarContact +namespace sw { +/** + * This class is responsible for the delayed display of grammar checks when a paragraph is edited + * It's a client of the paragraph the cursor points to. + * If the cursor position changes, updateCursorPosition has to be called + * If the grammar checker wants to set a grammar marker at a paragraph, he has to request + * the grammar list from this class. If the requested paragraph is not edited, it returns + * the normal grammar list. But if the paragraph is the active one, a proxy list will be returned and + * all changes are set in this proxy list. If the cursor leaves the paragraph the proxy list + * will replace the old list. If the grammar checker has completed the paragraph ('setChecked') + * then a timer is setup which replaces the old list as well. + */ +class GrammarContact final : public SvtListener +{ + Timer m_aTimer; + std::unique_ptr<SwGrammarMarkUp> m_pProxyList; + bool m_isFinished; + SwTextNode* m_pTextNode; + DECL_LINK(TimerRepaint, Timer*, void); + public: + GrammarContact(); + ~GrammarContact() { m_aTimer.Stop(); } + /** Update cursor position reacts to a change of the current input cursor As long as the cursor in inside a paragraph, the grammar checking does not show new grammar faults. When the cursor leaves the paragraph, these faults are shown. @returns void */ - virtual void updateCursorPosition(const SwPosition& rNewPos) = 0; + void updateCursorPosition(const SwPosition& rNewPos); /** getGrammarCheck checks if the given text node is blocked by the current cursor if not, the normal markup list is returned if blocked, it will return a markup list "proxy" @returns a markup list (grammar) for the given SwTextNode */ - virtual SwGrammarMarkUp* getGrammarCheck(SwTextNode& rTextNode, bool bCreate) = 0; + SwGrammarMarkUp* getGrammarCheck(SwTextNode& rTextNode, bool bCreate); /** finishGrammarCheck() has to be called if a grammar checking has been completed for a text node. If this text node has not been hidden by the current proxy list @@ -49,27 +72,22 @@ public: repaint will be triggered by a timer @returns void */ - virtual void finishGrammarCheck(SwTextNode& rTextNode) = 0; + void finishGrammarCheck(SwTextNode& rTextNode); -public: - virtual ~IGrammarContact() {} + void CheckBroadcaster(); }; -/** Factory for a grammar contact -@returns a new created grammar contact object -*/ -IGrammarContact* createGrammarContact(); - /* Helper functions */ /** getGrammarContact() delivers the grammar contact of the document (for a given textnode) @returns grammar contact */ -IGrammarContact* getGrammarContact(const SwTextNode&); +GrammarContact* getGrammarContact(const SwTextNode&); /** finishGrammarCheck() calls the same function of the grammar contact of the document (for a given textnode) @returns void */ void finishGrammarCheck(SwTextNode&); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx index f3662cb02b17..62785cb8bce8 100644 --- a/sw/inc/doc.hxx +++ b/sw/inc/doc.hxx @@ -112,7 +112,6 @@ struct SwSortOptions; struct SwDefTOXBase_Impl; class SwPrintUIOptions; struct SwConversionArgs; -class IGrammarContact; class SwRenderData; class IDocumentUndoRedo; class IDocumentSettingAccess; @@ -162,6 +161,7 @@ namespace sw { class DocumentLayoutManager; class DocumentStylePoolManager; class DocumentExternalDataManager; + class GrammarContact; } namespace com::sun::star { @@ -282,7 +282,7 @@ class SW_DLLPUBLIC SwDoc final std::unique_ptr<SwLayoutCache> mpLayoutCache; /**< Layout cache to read and save with the document for a faster formatting */ - std::unique_ptr<IGrammarContact> mpGrammarContact; //< for grammar checking in paragraphs during editing + std::unique_ptr<sw::GrammarContact> mpGrammarContact; //< for grammar checking in paragraphs during editing css::uno::Reference< css::script::vba::XVBAEventProcessor > mxVbaEvents; css::uno::Reference< ooo::vba::word::XFind > mxVbaFind; @@ -1559,7 +1559,7 @@ public: */ bool ContainsHiddenChars() const; - IGrammarContact* getGrammarContact() const { return mpGrammarContact.get(); } + std::unique_ptr<sw::GrammarContact> const& getGrammarContact() const { return mpGrammarContact; } /** Marks/Unmarks a list level of a certain list diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index d0a71e136d33..41a12ab26229 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -1502,9 +1502,10 @@ void SwCursorShell::UpdateCursorPos() &aTmpState ); pShellCursor->DeleteMark(); } - IGrammarContact *pGrammarContact = GetDoc() ? GetDoc()->getGrammarContact() : nullptr; - if( pGrammarContact ) - pGrammarContact->updateCursorPosition( *m_pCurrentCursor->GetPoint() ); + auto* pDoc = GetDoc(); + if (pDoc) + pDoc->getGrammarContact()->updateCursorPosition(*m_pCurrentCursor->GetPoint()); + --mnStartAction; if( aOldSz != GetDocSize() ) SizeChgNotify(); diff --git a/sw/source/core/doc/docnew.cxx b/sw/source/core/doc/docnew.cxx index 798a279b930a..55bb56da9dc7 100644 --- a/sw/source/core/doc/docnew.cxx +++ b/sw/source/core/doc/docnew.cxx @@ -247,7 +247,7 @@ SwDoc::SwDoc() mpNumberFormatter( nullptr ), mpNumRuleTable( new SwNumRuleTable ), mpExtInputRing( nullptr ), - mpGrammarContact(createGrammarContact()), + mpGrammarContact(new sw::GrammarContact), mpCellStyles(new SwCellStyleTable), mReferenceCount(0), mbDtor(false), @@ -808,14 +808,19 @@ void SwDoc::WriteLayoutCache( SvStream& rStream ) SwLayoutCache::Write( rStream, *this ); } -IGrammarContact* getGrammarContact( const SwTextNode& rTextNode ) +namespace sw +{ + +sw::GrammarContact* getGrammarContact(const SwTextNode& rTextNode) { const SwDoc& rDoc = rTextNode.GetDoc(); if (rDoc.IsInDtor()) return nullptr; - return rDoc.getGrammarContact(); + return rDoc.getGrammarContact().get(); } +} // end sw + ::sfx2::IXmlIdRegistry& SwDoc::GetXmlIdRegistry() { diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx index af3955a23e87..405d124c09e0 100644 --- a/sw/source/core/text/txtfrm.cxx +++ b/sw/source/core/text/txtfrm.cxx @@ -1801,7 +1801,7 @@ static void lcl_SetWrong( SwTextFrame& rFrame, SwTextNode const& rNode, if ( !rFrame.IsFollow() ) { SwTextNode* pTextNode = const_cast<SwTextNode*>(&rNode); - IGrammarContact* pGrammarContact = getGrammarContact( *pTextNode ); + sw::GrammarContact* pGrammarContact = getGrammarContact(*pTextNode); SwGrammarMarkUp* pWrongGrammar = pGrammarContact ? pGrammarContact->getGrammarCheck( *pTextNode, false ) : pTextNode->GetGrammarCheck(); diff --git a/sw/source/core/txtnode/SwGrammarContact.cxx b/sw/source/core/txtnode/SwGrammarContact.cxx index e2c1049f42ec..aaddc1406cfb 100644 --- a/sw/source/core/txtnode/SwGrammarContact.cxx +++ b/sw/source/core/txtnode/SwGrammarContact.cxx @@ -17,64 +17,32 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#include <vcl/timer.hxx> #include <IGrammarContact.hxx> #include <pam.hxx> #include <ndtxt.hxx> -#include <SwGrammarMarkUp.hxx> #include <txtfrm.hxx> -#include <svl/listener.hxx> -namespace { - -/* - * This class is responsible for the delayed display of grammar checks when a paragraph is edited - * It's a client of the paragraph the cursor points to. - * If the cursor position changes, updateCursorPosition has to be called - * If the grammar checker wants to set a grammar marker at a paragraph, he has to request - * the grammar list from this class. If the requested paragraph is not edited, it returns - * the normal grammar list. But if the paragraph is the active one, a proxy list will be returned and - * all changes are set in this proxy list. If the cursor leaves the paragraph the proxy list - * will replace the old list. If the grammar checker has completed the paragraph ('setChecked') - * then a timer is setup which replaces the old list as well. - */ -class SwGrammarContact final : public IGrammarContact, public SvtListener +namespace sw { - Timer m_aTimer; - std::unique_ptr<SwGrammarMarkUp> m_pProxyList; - bool m_isFinished; - SwTextNode* m_pTextNode; - DECL_LINK( TimerRepaint, Timer *, void ); - -public: - SwGrammarContact(); - virtual ~SwGrammarContact() override { m_aTimer.Stop(); } - - // (pure) virtual functions of IGrammarContact - virtual void updateCursorPosition( const SwPosition& rNewPos ) override; - virtual SwGrammarMarkUp* getGrammarCheck( SwTextNode& rTextNode, bool bCreate ) override; - virtual void finishGrammarCheck( SwTextNode& rTextNode ) override; - void CheckBroadcaster() - { - if(HasBroadcaster()) - return; - m_pTextNode = nullptr; - m_pProxyList.reset(); - } -}; - -} -SwGrammarContact::SwGrammarContact() +GrammarContact::GrammarContact() : m_aTimer( "sw::SwGrammarContact TimerRepaint" ), m_isFinished( false ), m_pTextNode(nullptr) { m_aTimer.SetTimeout( 2000 ); // Repaint of grammar check after 'setChecked' - m_aTimer.SetInvokeHandler( LINK(this, SwGrammarContact, TimerRepaint) ); + m_aTimer.SetInvokeHandler( LINK(this, GrammarContact, TimerRepaint) ); } -IMPL_LINK( SwGrammarContact, TimerRepaint, Timer *, pTimer, void ) +void GrammarContact::CheckBroadcaster() +{ + if (HasBroadcaster()) + return; + m_pTextNode = nullptr; + m_pProxyList.reset(); +} + +IMPL_LINK( GrammarContact, TimerRepaint, Timer *, pTimer, void ) { CheckBroadcaster(); if( pTimer ) @@ -89,7 +57,7 @@ IMPL_LINK( SwGrammarContact, TimerRepaint, Timer *, pTimer, void ) } /* I'm always a client of the current paragraph */ -void SwGrammarContact::updateCursorPosition( const SwPosition& rNewPos ) +void GrammarContact::updateCursorPosition( const SwPosition& rNewPos ) { CheckBroadcaster(); SwTextNode* pTextNode = rNewPos.GetNode().GetTextNode(); @@ -115,7 +83,7 @@ void SwGrammarContact::updateCursorPosition( const SwPosition& rNewPos ) } /* deliver a grammar check list for the given text node */ -SwGrammarMarkUp* SwGrammarContact::getGrammarCheck( SwTextNode& rTextNode, bool bCreate ) +SwGrammarMarkUp* GrammarContact::getGrammarCheck( SwTextNode& rTextNode, bool bCreate ) { SwGrammarMarkUp *pRet = nullptr; CheckBroadcaster(); @@ -155,7 +123,7 @@ SwGrammarMarkUp* SwGrammarContact::getGrammarCheck( SwTextNode& rTextNode, bool return pRet; } -void SwGrammarContact::finishGrammarCheck( SwTextNode& rTextNode ) +void GrammarContact::finishGrammarCheck( SwTextNode& rTextNode ) { CheckBroadcaster(); if( &rTextNode != m_pTextNode ) // not my paragraph @@ -175,16 +143,13 @@ void SwGrammarContact::finishGrammarCheck( SwTextNode& rTextNode ) } } -IGrammarContact* createGrammarContact() -{ - return new SwGrammarContact(); -} - void finishGrammarCheck( SwTextNode& rTextNode ) { - IGrammarContact* pGrammarContact = getGrammarContact( rTextNode ); - if( pGrammarContact ) + sw::GrammarContact* pGrammarContact = getGrammarContact( rTextNode ); + if (pGrammarContact) pGrammarContact->finishGrammarCheck( rTextNode ); } +} // end sw + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/unocore/unoflatpara.cxx b/sw/source/core/unocore/unoflatpara.cxx index 6fa124a824fd..4f2c8e385620 100644 --- a/sw/source/core/unocore/unoflatpara.cxx +++ b/sw/source/core/unocore/unoflatpara.cxx @@ -190,7 +190,7 @@ void SAL_CALL SwXFlatParagraph::setChecked( ::sal_Int32 nType, sal_Bool bVal ) { GetTextNode()->SetGrammarCheckDirty( !bVal ); if( bVal ) - ::finishGrammarCheck( *GetTextNode() ); + sw::finishGrammarCheck( *GetTextNode() ); } } diff --git a/sw/source/core/unocore/unotextmarkup.cxx b/sw/source/core/unocore/unotextmarkup.cxx index 8160da91a8ae..86add0f34c9c 100644 --- a/sw/source/core/unocore/unotextmarkup.cxx +++ b/sw/source/core/unocore/unotextmarkup.cxx @@ -157,7 +157,7 @@ void SAL_CALL SwXTextMarkup::commitStringMarkup( } else if ( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE ) { - IGrammarContact *pGrammarContact = getGrammarContact(*m_pImpl->m_pTextNode); + sw::GrammarContact* pGrammarContact = getGrammarContact(*m_pImpl->m_pTextNode); if( pGrammarContact ) { pWList = pGrammarContact->getGrammarCheck(*m_pImpl->m_pTextNode, true); @@ -411,7 +411,7 @@ void SAL_CALL SwXTextMarkup::commitMultiTextMarkup( // get appropriate list to use... SwGrammarMarkUp* pWList = nullptr; bool bRepaint = false; - IGrammarContact *pGrammarContact = getGrammarContact(*m_pImpl->m_pTextNode); + sw::GrammarContact* pGrammarContact = getGrammarContact(*m_pImpl->m_pTextNode); if( pGrammarContact ) { pWList = pGrammarContact->getGrammarCheck(*m_pImpl->m_pTextNode, true); commit d4c4e3053ce59dba96d7531fc4c1828c1366f419 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Wed Oct 12 21:38:52 2022 +0200 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Fri Oct 21 15:59:39 2022 +0200 sw: modify NumberingCheck to work independent of node order Previosuly the NumberingCheck assumed we iterated throught nodes and remembered the previous text node. This changes how the check works so that the we find the next text node and check is with the current node. Change-Id: I043da74ae90b6387bff273f62eb2589faa2d1d31 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141430 Tested-by: Tomaž Vajngerl <qui...@gmail.com> Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/include/sfx2/AccessibilityIssue.hxx b/include/sfx2/AccessibilityIssue.hxx index 60700bad8d35..7ca02ef3b7e9 100644 --- a/include/sfx2/AccessibilityIssue.hxx +++ b/include/sfx2/AccessibilityIssue.hxx @@ -31,6 +31,7 @@ enum class AccessibilityIssueID TEXT_FORMATTING, HYPERLINK_IS_TEXT, HYPERLINK_SHORT, + MANUAL_NUMBERING }; class SFX2_DLLPUBLIC AccessibilityIssue diff --git a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx index ead35acfb4ca..91d6be266a8c 100644 --- a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx +++ b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx @@ -116,6 +116,21 @@ CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testCheckHighlightedText) CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::TEXT_FORMATTING, aIssues[0]->m_eIssueID); } +CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testNumberingCheck) +{ + SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "AccessibilityTests_NumberingCheck.odt"); + CPPUNIT_ASSERT(pDoc); + sw::AccessibilityCheck aCheck(pDoc); + aCheck.check(); + auto& aIssues = aCheck.getIssueCollection().getIssues(); + CPPUNIT_ASSERT_EQUAL(size_t(5), aIssues.size()); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::MANUAL_NUMBERING, aIssues[0]->m_eIssueID); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::MANUAL_NUMBERING, aIssues[1]->m_eIssueID); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::MANUAL_NUMBERING, aIssues[2]->m_eIssueID); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::MANUAL_NUMBERING, aIssues[3]->m_eIssueID); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::MANUAL_NUMBERING, aIssues[4]->m_eIssueID); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/qa/core/accessibilitycheck/data/AccessibilityTests_NumberingCheck.odt b/sw/qa/core/accessibilitycheck/data/AccessibilityTests_NumberingCheck.odt new file mode 100644 index 000000000000..190ee91a8fde Binary files /dev/null and b/sw/qa/core/accessibilitycheck/data/AccessibilityTests_NumberingCheck.odt differ diff --git a/sw/source/core/access/AccessibilityCheck.cxx b/sw/source/core/access/AccessibilityCheck.cxx index 6d229fa40a16..0763ff0e90c5 100644 --- a/sw/source/core/access/AccessibilityCheck.cxx +++ b/sw/source/core/access/AccessibilityCheck.cxx @@ -42,6 +42,26 @@ namespace sw { namespace { +SwTextNode* lclSearchNextTextNode(SwNode* pCurrent) +{ + SwTextNode* pTextNode = nullptr; + + auto nIndex = pCurrent->GetIndex(); + auto nCount = pCurrent->GetNodes().Count(); + + nIndex++; // go to next node + + while (pTextNode == nullptr && nIndex < nCount) + { + auto pNode = pCurrent->GetNodes()[nIndex]; + if (pNode->IsTextNode()) + pTextNode = pNode->GetTextNode(); + nIndex++; + } + + return pTextNode; +} + std::shared_ptr<sw::AccessibilityIssue> lclAddIssue(sfx::AccessibilityIssueCollection& rIssueCollection, OUString const& rText, sfx::AccessibilityIssueID eIssue = sfx::AccessibilityIssueID::UNSPECIFIED) @@ -214,8 +234,6 @@ public: class NumberingCheck : public NodeCheck { private: - SwTextNode* m_pPreviousTextNode; - const std::vector<std::pair<OUString, OUString>> m_aNumberingCombinations{ { "1.", "2." }, { "(1)", "(2)" }, { "1)", "2)" }, { "a.", "b." }, { "(a)", "(b)" }, { "a)", "b)" }, { "A.", "B." }, { "(A)", "(B)" }, { "A)", "B)" } @@ -224,7 +242,6 @@ private: public: NumberingCheck(sfx::AccessibilityIssueCollection& rIssueCollection) : NodeCheck(rIssueCollection) - , m_pPreviousTextNode(nullptr) { } @@ -233,21 +250,24 @@ public: if (!pCurrent->IsTextNode()) return; - if (m_pPreviousTextNode) + SwTextNode* pCurrentTextNode = pCurrent->GetTextNode(); + SwTextNode* pNextTextNode = lclSearchNextTextNode(pCurrent); + + if (!pNextTextNode) + return; + + for (auto& rPair : m_aNumberingCombinations) { - for (auto& rPair : m_aNumberingCombinations) + if (pCurrentTextNode->GetText().startsWith(rPair.first) + && pNextTextNode->GetText().startsWith(rPair.second)) { - if (pCurrent->GetTextNode()->GetText().startsWith(rPair.second) - && m_pPreviousTextNode->GetText().startsWith(rPair.first)) - { - OUString sNumbering = rPair.first + " " + rPair.second + "..."; - OUString sIssueText - = SwResId(STR_FAKE_NUMBERING).replaceAll("%NUMBERING%", sNumbering); - lclAddIssue(m_rIssueCollection, sIssueText); - } + OUString sNumbering = rPair.first + " " + rPair.second + "..."; + OUString sIssueText + = SwResId(STR_FAKE_NUMBERING).replaceAll("%NUMBERING%", sNumbering); + lclAddIssue(m_rIssueCollection, sIssueText, + sfx::AccessibilityIssueID::MANUAL_NUMBERING); } } - m_pPreviousTextNode = pCurrent->GetTextNode(); } }; @@ -474,7 +494,6 @@ public: class TextFormattingCheck : public NodeCheck { -private: public: TextFormattingCheck(sfx::AccessibilityIssueCollection& rIssueCollection) : NodeCheck(rIssueCollection) @@ -566,6 +585,7 @@ public: pIssue->setStart(pTextAttr->GetStart()); pIssue->setEnd(pTextAttr->GetAnyEnd()); } + void check(SwNode* pCurrent) override { if (!pCurrent->IsTextNode())