diff options
author | Michael Meeks <michael.meeks@collabora.com> | 2014-07-27 00:21:50 -0400 |
---|---|---|
committer | Michael Meeks <michael.meeks@collabora.com> | 2014-07-27 17:06:06 -0400 |
commit | 274b628a2b523eb45e297352a85f0177c6e747f0 (patch) | |
tree | ea72bf5dcf65cbb50564e48ac5c44a96d03a16f3 /editeng | |
parent | a3fc7f20089062afa4f778e70ba8be84032a30a7 (diff) |
bnc#467459 - fix editeng text search with expanded fields.
Change-Id: If59d0e2f886e94148b81cb6cfcad067733fcb918
Diffstat (limited to 'editeng')
-rw-r--r-- | editeng/CppunitTest_editeng_core.mk | 1 | ||||
-rw-r--r-- | editeng/qa/unit/core-test.cxx | 100 | ||||
-rw-r--r-- | editeng/source/editeng/editdoc.cxx | 169 | ||||
-rw-r--r-- | editeng/source/editeng/editdoc.hxx | 9 | ||||
-rw-r--r-- | editeng/source/editeng/impedit4.cxx | 6 |
5 files changed, 224 insertions, 61 deletions
diff --git a/editeng/CppunitTest_editeng_core.mk b/editeng/CppunitTest_editeng_core.mk index 171a03fb46b5..488a5d039eba 100644 --- a/editeng/CppunitTest_editeng_core.mk +++ b/editeng/CppunitTest_editeng_core.mk @@ -62,6 +62,7 @@ $(eval $(call gb_CppunitTest_use_components,editeng_core,\ configmgr/source/configmgr \ framework/util/fwk \ i18npool/util/i18npool \ + i18npool/source/search/i18nsearch \ linguistic/source/lng \ sfx2/util/sfx \ ucb/source/core/ucb1 \ diff --git a/editeng/qa/unit/core-test.cxx b/editeng/qa/unit/core-test.cxx index fb3d354e7b5d..4e9b82b30ee5 100644 --- a/editeng/qa/unit/core-test.cxx +++ b/editeng/qa/unit/core-test.cxx @@ -25,6 +25,9 @@ #include "editeng/postitem.hxx" #include "editeng/section.hxx" #include "editeng/editobj.hxx" +#include "editeng/flditem.hxx" +#include "svl/srchitem.hxx" +#include "rtl/strbuf.hxx" #include <com/sun/star/text/textfield/Type.hpp> @@ -44,22 +47,22 @@ public: void testConstruction(); - /** - * Test UNO service class that implements text field items. - */ + /// Test UNO service class that implements text field items. void testUnoTextFields(); - /** - * AutoCorrect tests - */ + /// AutoCorrect tests void testAutocorrect(); + /// Test hyperlinks + void testHyperlinkSearch(); + void testSectionAttributes(); CPPUNIT_TEST_SUITE(Test); CPPUNIT_TEST(testConstruction); CPPUNIT_TEST(testUnoTextFields); CPPUNIT_TEST(testAutocorrect); + CPPUNIT_TEST(testHyperlinkSearch); CPPUNIT_TEST(testSectionAttributes); CPPUNIT_TEST_SUITE_END(); @@ -340,6 +343,91 @@ void Test::testAutocorrect() } } +namespace { + class UrlEditEngine : public EditEngine + { + public: + UrlEditEngine(SfxItemPool *pPool) : EditEngine(pPool) {} + + virtual OUString CalcFieldValue( const SvxFieldItem&, sal_Int32, sal_Int32, Color*&, Color*& ) + { + return OUString("jim@bob.com"); // a sophisticated view of value: + } + }; +} + +// Odd accounting for hyperlink position & size etc. +// https://bugzilla.novell.com/show_bug.cgi?id=467459 +void Test::testHyperlinkSearch() +{ + UrlEditEngine aEngine(mpItemPool); + EditDoc &rDoc = aEngine.GetEditDoc(); + + OUString aSampleText = "Please write email to . if you find a fish(not a dog)."; + aEngine.SetText(aSampleText); + + CPPUNIT_ASSERT_MESSAGE("set text", rDoc.GetParaAsString(sal_Int32(0)) == aSampleText); + + ContentNode *pNode = rDoc.GetObject(0); + EditSelection aSel(EditPaM(pNode, 22), EditPaM(pNode, 22)); + SvxURLField aURLField("mailto:///jim@bob.com", "jim@bob.com", + SVXURLFORMAT_REPR); + SvxFieldItem aField(aURLField, EE_FEATURE_FIELD); + + aEngine.InsertField(aSel, aField); + aEngine.UpdateFields(); + + OUString aContent = pNode->GetExpandedText(); + CPPUNIT_ASSERT_MESSAGE("get text", aContent == + "Please write email to jim@bob.com. if you find a fish(not a dog)."); + CPPUNIT_ASSERT_MESSAGE("wrong length", rDoc.GetTextLen() == (sal_uLong)aContent.getLength()); + + // Check expansion and positioning re-work + CPPUNIT_ASSERT_MESSAGE("wrong length", pNode->GetExpandedLen() == + (sal_uLong)aContent.getLength()); + for (sal_Int32 n = 0; n < aContent.getLength(); n++) + { + sal_Int32 nStart = n, nEnd = n; + pNode->UnExpandPositions(nStart,nEnd); + CPPUNIT_ASSERT_MESSAGE("out of bound start", nStart < pNode->Len()); + CPPUNIT_ASSERT_MESSAGE("out of bound end", nEnd <= pNode->Len()); + } + + static const struct { + sal_Int32 mnStart, mnEnd; + sal_Int32 mnNewStart, mnNewEnd; + } aTrickyOnes[] = { + { 0, 1, /* -> */ 0, 1 }, + { 21, 25, /* -> */ 21, 23 }, // the field is really just one char + { 25, 27, /* -> */ 22, 23 }, + { 50, 56, /* -> */ 40, 46 } + }; + for (size_t n = 0; n < SAL_N_ELEMENTS(aTrickyOnes); n++) + { + sal_Int32 nStart = aTrickyOnes[n].mnStart; + sal_Int32 nEnd = aTrickyOnes[n].mnEnd; + pNode->UnExpandPositions(nStart,nEnd); + + rtl::OStringBuffer aBuf; + aBuf = "bound check start is "; + aBuf.append(nStart).append(" but should be ").append(aTrickyOnes[n].mnNewStart); + aBuf.append(" in row ").append((sal_Int32)n); + CPPUNIT_ASSERT_MESSAGE(aBuf.getStr(), nStart == aTrickyOnes[n].mnNewStart); + aBuf = "bound check end is "; + aBuf.append(nEnd).append(" but should be ").append(aTrickyOnes[n].mnNewEnd); + aBuf.append(" in row ").append((sal_Int32)n); + CPPUNIT_ASSERT_MESSAGE(aBuf.getStr(), nEnd == aTrickyOnes[n].mnNewEnd); + } + + SvxSearchItem aItem(1); //SID_SEARCH_ITEM); + aItem.SetBackward(false); + aItem.SetSelection(false); + aItem.SetSearchString("fish"); + CPPUNIT_ASSERT_MESSAGE("no fish", aEngine.HasText(aItem)); + aItem.SetSearchString("dog"); + CPPUNIT_ASSERT_MESSAGE("no dog", aEngine.HasText(aItem)); +} + bool hasBold(const editeng::Section& rSecAttr) { std::vector<const SfxPoolItem*>::const_iterator it = rSecAttr.maAttributes.begin(), itEnd = rSecAttr.maAttributes.end(); diff --git a/editeng/source/editeng/editdoc.cxx b/editeng/source/editeng/editdoc.cxx index 586556747602..24369ca9857c 100644 --- a/editeng/source/editeng/editdoc.cxx +++ b/editeng/source/editeng/editdoc.cxx @@ -1650,6 +1650,118 @@ sal_Int32 ContentNode::Len() const return maString.getLength(); } +sal_uLong ContentNode::GetExpandedLen() const +{ + sal_uLong nLen = maString.getLength(); + + // Fields can be longer than the placeholder in the Node + const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs(); + for (sal_Int32 nAttr = rAttrs.size(); nAttr; ) + { + const EditCharAttrib& rAttr = rAttrs[--nAttr]; + if (rAttr.Which() == EE_FEATURE_FIELD) + { + nLen += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength(); + --nLen; // Standalone, to avoid corner cases when previous getLength() returns 0 + } + } + + return nLen; +} + +OUString ContentNode::GetExpandedText(sal_Int32 nStartPos, sal_Int32 nEndPos, bool bResolveFields) const +{ + if ( nEndPos < 0 || nEndPos > Len() ) + nEndPos = Len(); + + DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" ); + + sal_Int32 nIndex = nStartPos; + OUString aStr; + const EditCharAttrib* pNextFeature = GetCharAttribs().FindFeature( nIndex ); + while ( nIndex < nEndPos ) + { + sal_Int32 nEnd = nEndPos; + if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) ) + nEnd = pNextFeature->GetStart(); + else + pNextFeature = 0; // Feature does not interest the below + + DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" ); + //!! beware of sub string length of -1 + if (nEnd > nIndex) + aStr += GetString().copy(nIndex, nEnd - nIndex); + + if ( pNextFeature ) + { + switch ( pNextFeature->GetItem()->Which() ) + { + case EE_FEATURE_TAB: aStr += "\t"; + break; + case EE_FEATURE_LINEBR: aStr += "\x0A"; + break; + case EE_FEATURE_FIELD: + if ( bResolveFields ) + aStr += static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue(); + break; + default: OSL_FAIL( "What feature?" ); + } + pNextFeature = GetCharAttribs().FindFeature( ++nEnd ); + } + nIndex = nEnd; + } + return aStr; +} + +void ContentNode::UnExpandPosition( sal_Int32 &rPos, bool bBiasStart ) +{ + sal_Int32 nOffset = 0; + + const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs(); + for (size_t nAttr = 0; nAttr < rAttrs.size(); ++nAttr ) + { + const EditCharAttrib& rAttr = rAttrs[nAttr]; + assert (!(nAttr < rAttrs.size() - 1) || + rAttrs[nAttr].GetStart() < rAttrs[nAttr + 1].GetStart()); + + nOffset = rAttr.GetStart(); + + if (nOffset >= rPos) // happens after the position + return; + + sal_Int32 nChunk = 0; + if (rAttr.Which() == EE_FEATURE_FIELD) + { + nChunk += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength(); + nChunk--; // Character representing the field in the string + + if (nOffset + nChunk >= rPos) // we're inside the field + { + if (bBiasStart) + rPos = rAttr.GetStart(); + else + rPos = rAttr.GetEnd(); + return; + } + // Adjust for the position + rPos -= nChunk; + } + } + assert (rPos <= Len()); +} + +/* + * Fields are represented by a single character in the underlying string + * and/or selection, however, they can be expanded to the full value of + * the field. When we're dealing with selection / offsets however we need + * to deal in character positions inside the real (unexpanded) string. + * This method maps us back to character offsets. + */ +void ContentNode::UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos ) +{ + UnExpandPosition( rStartPos, true ); + UnExpandPosition( rEndPos, false ); +} void ContentNode::SetChar(sal_Int32 nPos, sal_Unicode c) { @@ -2081,48 +2193,10 @@ OUString EditDoc::GetParaAsString( sal_Int32 nNode ) const } OUString EditDoc::GetParaAsString( - const ContentNode* pNode, sal_Int32 nStartPos, sal_Int32 nEndPos, bool bResolveFields) const + const ContentNode* pNode, sal_Int32 nStartPos, sal_Int32 nEndPos, + bool bResolveFields) const { - if ( nEndPos < 0 || nEndPos > pNode->Len() ) - nEndPos = pNode->Len(); - - DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" ); - - sal_Int32 nIndex = nStartPos; - OUString aStr; - const EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( nIndex ); - while ( nIndex < nEndPos ) - { - sal_Int32 nEnd = nEndPos; - if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) ) - nEnd = pNextFeature->GetStart(); - else - pNextFeature = 0; // Feature does not interest the below - - DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" ); - //!! beware of sub string length of -1 - if (nEnd > nIndex) - aStr += pNode->GetString().copy(nIndex, nEnd - nIndex); - - if ( pNextFeature ) - { - switch ( pNextFeature->GetItem()->Which() ) - { - case EE_FEATURE_TAB: aStr += "\t"; - break; - case EE_FEATURE_LINEBR: aStr += "\x0A"; - break; - case EE_FEATURE_FIELD: - if ( bResolveFields ) - aStr += static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue(); - break; - default: OSL_FAIL( "What feature?" ); - } - pNextFeature = pNode->GetCharAttribs().FindFeature( ++nEnd ); - } - nIndex = nEnd; - } - return aStr; + return pNode->GetExpandedText(nStartPos, nEndPos, bResolveFields); } EditPaM EditDoc::GetStartPaM() const @@ -2143,18 +2217,7 @@ sal_uLong EditDoc::GetTextLen() const for ( sal_Int32 nNode = 0; nNode < Count(); nNode++ ) { const ContentNode* pNode = GetObject( nNode ); - nLen += pNode->Len(); - // Fields can be longer than the placeholder in the Node - const CharAttribList::AttribsType& rAttrs = pNode->GetCharAttribs().GetAttribs(); - for (sal_Int32 nAttr = rAttrs.size(); nAttr; ) - { - const EditCharAttrib& rAttr = rAttrs[--nAttr]; - if (rAttr.Which() == EE_FEATURE_FIELD) - { - nLen += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength(); - --nLen; // Standalone, to avoid corner cases when previous getLength() returns 0 - } - } + nLen += pNode->GetExpandedLen(); } return nLen; } diff --git a/editeng/source/editeng/editdoc.hxx b/editeng/source/editeng/editdoc.hxx index 3db2ac3e88c9..1aa1c5846456 100644 --- a/editeng/source/editeng/editdoc.hxx +++ b/editeng/source/editeng/editdoc.hxx @@ -246,6 +246,8 @@ private: CharAttribList aCharAttribList; boost::scoped_ptr<WrongList> mpWrongList; + void UnExpandPosition( sal_Int32 &rStartPos, bool bBiasStart ); + public: ContentNode( SfxItemPool& rItemPool ); ContentNode( const OUString& rStr, const ContentAttribs& rContentAttribs ); @@ -281,6 +283,13 @@ public: sal_Int32 Len() const; const OUString& GetString() const { return maString;} + /// return length including expanded fields + sal_uLong GetExpandedLen() const; + /// return content including expanded fields + OUString GetExpandedText(sal_Int32 nStartPos = 0, sal_Int32 nEndPos = -1, bool bResolveFields = true) const; + /// re-write offsets in the expanded text to string offsets + void UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos ); + void SetChar(sal_Int32 nPos, sal_Unicode c); void Insert(const OUString& rStr, sal_Int32 nPos); void Append(const OUString& rStr); diff --git a/editeng/source/editeng/impedit4.cxx b/editeng/source/editeng/impedit4.cxx index 22ee40d04130..fd11281ce858 100644 --- a/editeng/source/editeng/impedit4.cxx +++ b/editeng/source/editeng/impedit4.cxx @@ -2643,7 +2643,7 @@ bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem, ContentNode* pNode = aEditDoc.GetObject( nNode ); sal_Int32 nStartPos = 0; - sal_Int32 nEndPos = pNode->Len(); + sal_Int32 nEndPos = pNode->GetExpandedLen(); if ( nNode == nStartNode ) { if ( bBack ) @@ -2660,7 +2660,7 @@ bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem, } // Searching ... - OUString aParaStr( GetEditDoc().GetParaAsString( pNode ) ); + OUString aParaStr( pNode->GetExpandedText() ); bool bFound = false; if ( bBack ) { @@ -2677,6 +2677,8 @@ bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem, } if ( bFound ) { + pNode->UnExpandPositions( nStartPos, nEndPos ); + rFoundSel.Min().SetNode( pNode ); rFoundSel.Min().SetIndex( nStartPos ); rFoundSel.Max().SetNode( pNode ); |