From 5ae6072774ab544fdfdfebf2364e97ca2fa2326a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= Date: Mon, 3 Nov 2014 09:49:37 +0000 Subject: [PATCH] Resolves: fdo#68347 fix word count with recorded changes also see fdo#46757 a) We need to ignore redline-deleted text, but count redline-added text b) each block of text is denoted by its end position in the model and where that maps to in the view so a hidden portion should record its end point not its starting point, and a non-hidden deleted portion should always record its end point c) when mapping a model position to the view we take the offset of the model pos arg from the block end and use that to offset the mapped block-end view pos to get the final view pos. But for hidden portions that won't make a whole lot of sense, and end up offsetting into prior portions, so map all positions within a hidden portion to the same block-end view pos add regression tests for these cases (cherry picked from commit fa430e6b4e6f5d096bdf59db26e5d7393ca2297b) Conflicts: sw/qa/core/uwriter.cxx Change-Id: I45c76bba47fd430bc3bccb5f919502660d415d9e Reviewed-on: https://gerrit.libreoffice.org/12219 Reviewed-by: Michael Stahl Tested-by: Michael Stahl --- sw/inc/modeltoviewhelper.hxx | 25 +++++++--- sw/qa/core/uwriter.cxx | 34 ++++++++++---- sw/source/core/text/porlay.cxx | 5 +- sw/source/core/txtnode/modeltoviewhelper.cxx | 70 +++++++++++++++++----------- sw/source/core/txtnode/txtedt.cxx | 2 +- 5 files changed, 93 insertions(+), 43 deletions(-) diff --git a/sw/inc/modeltoviewhelper.hxx b/sw/inc/modeltoviewhelper.hxx index 018b67c..d99c9db 100644 --- a/sw/inc/modeltoviewhelper.hxx +++ b/sw/inc/modeltoviewhelper.hxx @@ -65,19 +65,30 @@ class SwTxtNode; #define EXPANDFIELDS 0x0001 #define EXPANDFOOTNOTE 0x0002 #define HIDEINVISIBLE 0x0004 -#define HIDEREDLINED 0x0008 +#define HIDEDELETIONS 0x0008 /// do not expand to content, but replace with ZWSP #define REPLACEMODE 0x0010 class ModelToViewHelper { - /** For each field in the model string, there is an entry in the conversion - map. The first value of the ConversionMapEntry points to the field - position in the model string, the second value points to the associated - position in the view string. The last entry in the conversion map - denotes the lengths of the model resp. view string. + /** For each expanded/hidden portion in the model string, there is an entry in + the conversion map. The first value of the ConversionMapEntry points to + the start position in the model string, the second value points to the + associated start position in the view string. The last entry in the + conversion map denotes the lengths of the model resp. view string. */ - typedef std::pair< sal_Int32 , sal_Int32 > ConversionMapEntry; + struct ConversionMapEntry + { + ConversionMapEntry(sal_Int32 nModelPos, sal_Int32 nViewPos, bool bVisible) + : m_nModelPos(nModelPos) + , m_nViewPos(nViewPos) + , m_bVisible(bVisible) + { + } + sal_Int32 m_nModelPos; + sal_Int32 m_nViewPos; + bool m_bVisible; + }; typedef std::vector< ConversionMapEntry > ConversionMap; typedef std::vector Positions; diff --git a/sw/qa/core/uwriter.cxx b/sw/qa/core/uwriter.cxx index bd6e000..1297c65 100644 --- a/sw/qa/core/uwriter.cxx +++ b/sw/qa/core/uwriter.cxx @@ -321,7 +321,7 @@ void SwDocTest::testModelToViewHelper() } { - ModelToViewHelper aModelToViewHelper(*pTxtNode, HIDEREDLINED); + ModelToViewHelper aModelToViewHelper(*pTxtNode, HIDEDELETIONS); OUString sViewText = aModelToViewHelper.getViewText(); CPPUNIT_ASSERT_EQUAL( OUString("AAAABB " + OUString(CH_TXTATR_BREAKWORD) + " CCCCC " + OUString(CH_TXTATR_BREAKWORD) + " DDDDD"), @@ -349,14 +349,14 @@ void SwDocTest::testModelToViewHelper() } { - ModelToViewHelper aModelToViewHelper(*pTxtNode, EXPANDFIELDS | HIDEREDLINED | EXPANDFOOTNOTE); + ModelToViewHelper aModelToViewHelper(*pTxtNode, EXPANDFIELDS | HIDEDELETIONS | EXPANDFOOTNOTE); OUString sViewText = aModelToViewHelper.getViewText(); CPPUNIT_ASSERT_EQUAL( OUString("AAAABB foo CCCCC foo DDDDD"), sViewText); } { ModelToViewHelper aModelToViewHelper(*pTxtNode, - EXPANDFIELDS | HIDEREDLINED | EXPANDFOOTNOTE | REPLACEMODE); + EXPANDFIELDS | HIDEDELETIONS | EXPANDFOOTNOTE | REPLACEMODE); OUString sViewText = aModelToViewHelper.getViewText(); CPPUNIT_ASSERT_EQUAL( OUString("AAAABB " + OUString(CHAR_ZWSP) + " CCCCC " + OUString(CHAR_ZWSP) + " DDDDD"), @@ -372,7 +372,7 @@ void SwDocTest::testModelToViewHelper() } { - ModelToViewHelper aModelToViewHelper(*pTxtNode, HIDEINVISIBLE | HIDEREDLINED); + ModelToViewHelper aModelToViewHelper(*pTxtNode, HIDEINVISIBLE | HIDEDELETIONS); OUString sViewText = aModelToViewHelper.getViewText(); OUStringBuffer aBuffer; aBuffer.append("AAAACCCCC "); @@ -382,13 +382,13 @@ void SwDocTest::testModelToViewHelper() } { - ModelToViewHelper aModelToViewHelper(*pTxtNode, EXPANDFIELDS | HIDEINVISIBLE | HIDEREDLINED | EXPANDFOOTNOTE); + ModelToViewHelper aModelToViewHelper(*pTxtNode, EXPANDFIELDS | HIDEINVISIBLE | HIDEDELETIONS | EXPANDFOOTNOTE); OUString sViewText = aModelToViewHelper.getViewText(); CPPUNIT_ASSERT_EQUAL(OUString("AAAACCCCC foo DDDDD"), sViewText); } { ModelToViewHelper aModelToViewHelper(*pTxtNode, - EXPANDFIELDS | HIDEINVISIBLE | HIDEREDLINED | EXPANDFOOTNOTE | REPLACEMODE); + EXPANDFIELDS | HIDEINVISIBLE | HIDEDELETIONS | EXPANDFOOTNOTE | REPLACEMODE); OUString sViewText = aModelToViewHelper.getViewText(); CPPUNIT_ASSERT_EQUAL(sViewText, OUString("AAAACCCCC " + OUString(CHAR_ZWSP) + " DDDDD")); @@ -669,8 +669,26 @@ void SwDocTest::testSwScanner() aDocStat.Reset(); pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len()); //word-counting the text should only count the non-deleted text, and this whole chunk should be ignored - CPPUNIT_ASSERT_EQUAL(aDocStat.nWord, static_cast(0)); - CPPUNIT_ASSERT_EQUAL(aDocStat.nChar, static_cast(0)); + CPPUNIT_ASSERT_EQUAL(static_cast(0), aDocStat.nWord); + CPPUNIT_ASSERT_EQUAL(static_cast(0), aDocStat.nChar); + + // https://bugs.libreoffice.org/show_bug.cgi?id=68347 we do want to count + // redline *added* text though + m_pDoc->SetRedlineMode(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_DELETE|nsRedlineMode_t::REDLINE_SHOW_INSERT); + aPaM.DeleteMark(); + aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(), 0); + m_pDoc->InsertString(aPaM, "redline-new-text "); + aDocStat.Reset(); + pTxtNode = aPaM.GetNode()->GetTxtNode(); + pTxtNode->SetWordCountDirty(true); + pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len()); + CPPUNIT_ASSERT_EQUAL(static_cast(2), aDocStat.nWord); + //redline-new-text Lorem ipsum + //+++++++++++++++++ ------ + //select start of original text and part of deleted text + aDocStat.Reset(); + pTxtNode->CountWords(aDocStat, 17, 25); + CPPUNIT_ASSERT_EQUAL(static_cast(5), aDocStat.nChar); } //See https://bugs.libreoffice.org/show_bug.cgi?id=38983 diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx index 40e3a58..214bbe5 100644 --- a/sw/source/core/text/porlay.cxx +++ b/sw/source/core/text/porlay.cxx @@ -2107,9 +2107,12 @@ void SwScriptInfo::selectRedLineDeleted(const SwTxtNode& rNode, MultiSelection & { const SwRangeRedline* pRed = rIDRA.GetRedlineTbl()[ nAct ]; - if ( pRed->Start()->nNode > rNode.GetIndex() ) + if (pRed->Start()->nNode > rNode.GetIndex()) break; + if (pRed->GetType() != nsRedlineType_t::REDLINE_DELETE) + continue; + sal_Int32 nRedlStart; sal_Int32 nRedlnEnd; pRed->CalcStartEnd( rNode.GetIndex(), nRedlStart, nRedlnEnd ); diff --git a/sw/source/core/txtnode/modeltoviewhelper.cxx b/sw/source/core/txtnode/modeltoviewhelper.cxx index bd1a3ae..875ea1e 100644 --- a/sw/source/core/txtnode/modeltoviewhelper.cxx +++ b/sw/source/core/txtnode/modeltoviewhelper.cxx @@ -94,7 +94,7 @@ ModelToViewHelper::ModelToViewHelper(const SwTxtNode &rNode, sal_uInt16 eMode) if (eMode & HIDEINVISIBLE) SwScriptInfo::selectHiddenTextProperty(rNode, aHiddenMulti); - if (eMode & HIDEREDLINED) + if (eMode & HIDEDELETIONS) SwScriptInfo::selectRedLineDeleted(rNode, aHiddenMulti); std::vector aBlocks; @@ -203,43 +203,55 @@ ModelToViewHelper::ModelToViewHelper(const SwTxtNode &rNode, sal_uInt16 eMode) } } + //store the end of each range in the model and where that end of range + //maps to in the view sal_Int32 nOffset = 0; for (std::vector::iterator i = aBlocks.begin(); i != aBlocks.end(); ++i) { + const sal_Int32 nBlockLen = i->m_nLen; + if (!nBlockLen) + continue; + const sal_Int32 nBlockStart = i->m_nStart; + const sal_Int32 nBlockEnd = nBlockStart + nBlockLen; + if (!i->m_bVisible) { - const sal_Int32 nHiddenStart = i->m_nStart; - const sal_Int32 nHiddenLen = i->m_nLen; + sal_Int32 const modelBlockPos(nBlockEnd); + sal_Int32 const viewBlockPos(nBlockStart + nOffset); + m_aMap.push_back(ConversionMapEntry(modelBlockPos, viewBlockPos, false)); - m_aRetText = m_aRetText.replaceAt( nOffset + nHiddenStart, nHiddenLen, OUString() ); - m_aMap.push_back( ConversionMapEntry( nHiddenStart, nOffset + nHiddenStart ) ); - nOffset -= nHiddenLen; + m_aRetText = m_aRetText.replaceAt(nOffset + nBlockStart, nBlockLen, OUString()); + nOffset -= nBlockLen; } else { for (FieldResultSet::iterator j = i->m_aAttrs.begin(); j != i->m_aAttrs.end(); ++j) { - sal_Int32 const viewPos(nOffset + j->m_nFieldPos); - m_aRetText = m_aRetText.replaceAt(viewPos, 1, j->m_sExpand); - m_aMap.push_back( ConversionMapEntry(j->m_nFieldPos, viewPos) ); + sal_Int32 const modelFieldPos(j->m_nFieldPos); + sal_Int32 const viewFieldPos(j->m_nFieldPos + nOffset); + m_aMap.push_back( ConversionMapEntry(modelFieldPos, viewFieldPos, true) ); + + m_aRetText = m_aRetText.replaceAt(viewFieldPos, 1, j->m_sExpand); + nOffset += j->m_sExpand.getLength() - 1; + switch (j->m_eType) { case FieldResult::FIELD: - m_FieldPositions.push_back(viewPos); + m_FieldPositions.push_back(viewFieldPos); break; case FieldResult::FOOTNOTE: - m_FootnotePositions.push_back(viewPos); + m_FootnotePositions.push_back(viewFieldPos); break; case FieldResult::NONE: /*ignore*/ break; } - nOffset += j->m_sExpand.getLength() - 1; } + + sal_Int32 const modelEndBlock(nBlockEnd); + sal_Int32 const viewFieldPos(nBlockEnd + nOffset); + m_aMap.push_back(ConversionMapEntry(modelEndBlock, viewFieldPos, true)); } } - - if ( !m_aMap.empty() ) - m_aMap.push_back( ConversionMapEntry( rNodeText.getLength()+1, m_aRetText.getLength()+1 ) ); } /** Converts a model position into a view position @@ -248,15 +260,21 @@ sal_Int32 ModelToViewHelper::ConvertToViewPosition( sal_Int32 nModelPos ) const { // Search for entry after nPos: ConversionMap::const_iterator aIter; + for ( aIter = m_aMap.begin(); aIter != m_aMap.end(); ++aIter ) { - if ( (*aIter).first >= nModelPos ) + if (aIter->m_nModelPos >= nModelPos) { - const sal_Int32 nPosModel = (*aIter).first; - const sal_Int32 nPosExpand = (*aIter).second; - - const sal_Int32 nDistToNextModel = nPosModel - nModelPos; - return nPosExpand - nDistToNextModel; + //if it's an invisible portion, map all contained positions + //to the anchor viewpos + if (!aIter->m_bVisible) + return aIter->m_nViewPos; + + //if it's a visible portion, then the view position is the anchor + //viewpos - the offset of the input modelpos from the anchor + //modelpos + const sal_Int32 nOffsetFromEnd = aIter->m_nModelPos - nModelPos; + return aIter->m_nViewPos - nOffsetFromEnd; } } @@ -274,10 +292,10 @@ ModelToViewHelper::ModelPosition ModelToViewHelper::ConvertToModelPosition( sal_ ConversionMap::const_iterator aIter; for ( aIter = m_aMap.begin(); aIter != m_aMap.end(); ++aIter ) { - if ( (*aIter).second > nViewPos ) + if (aIter->m_nViewPos > nViewPos) { - const sal_Int32 nPosModel = (*aIter).first; - const sal_Int32 nPosExpand = (*aIter).second; + const sal_Int32 nPosModel = aIter->m_nModelPos; + const sal_Int32 nPosExpand = aIter->m_nViewPos; // If nViewPos is in front of first field, we are finished. if ( aIter == m_aMap.begin() ) @@ -286,8 +304,8 @@ ModelToViewHelper::ModelPosition ModelToViewHelper::ConvertToModelPosition( sal_ --aIter; // nPrevPosModel is the field position - const sal_Int32 nPrevPosModel = (*aIter).first; - const sal_Int32 nPrevPosExpand = (*aIter).second; + const sal_Int32 nPrevPosModel = aIter->m_nModelPos; + const sal_Int32 nPrevPosExpand = aIter->m_nViewPos; const sal_Int32 nLengthModel = nPosModel - nPrevPosModel; const sal_Int32 nLengthExpand = nPosExpand - nPrevPosExpand; diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx index aa6f7d6..f9f19e2 100644 --- a/sw/source/core/txtnode/txtedt.cxx +++ b/sw/source/core/txtnode/txtedt.cxx @@ -1986,7 +1986,7 @@ bool SwTxtNode::CountWords( SwDocStat& rStat, } // ConversionMap to expand fields, remove invisible and redline deleted text for scanner - const ModelToViewHelper aConversionMap(*this, EXPANDFIELDS | EXPANDFOOTNOTE | HIDEINVISIBLE | HIDEREDLINED); + const ModelToViewHelper aConversionMap(*this, EXPANDFIELDS | EXPANDFOOTNOTE | HIDEINVISIBLE | HIDEDELETIONS); OUString aExpandText = aConversionMap.getViewText(); if (aExpandText.isEmpty() && !bCountNumbering) -- 1.9.3