Blame SOURCES/0068-bnc-467459-fix-editeng-text-search-with-expanded-fie.patch

f085be
From 8220b70fe2dc270188751950ac6d872320db1aa2 Mon Sep 17 00:00:00 2001
f085be
From: Michael Meeks <michael.meeks@collabora.com>
f085be
Date: Sun, 27 Jul 2014 00:21:50 -0400
f085be
Subject: [PATCH 068/137] bnc#467459 - fix editeng text search with expanded
f085be
 fields.
f085be
MIME-Version: 1.0
f085be
Content-Type: text/plain; charset=UTF-8
f085be
Content-Transfer-Encoding: 8bit
f085be
f085be
(cherry picked from commit 274b628a2b523eb45e297352a85f0177c6e747f0)
f085be
f085be
Signed-off-by: Matúš Kukan <matus.kukan@collabora.com>
f085be
f085be
Conflicts:
f085be
	editeng/source/editeng/editdoc.cxx
f085be
	editeng/source/editeng/editdoc.hxx
f085be
f085be
Change-Id: If59d0e2f886e94148b81cb6cfcad067733fcb918
f085be
---
f085be
 editeng/CppunitTest_editeng_core.mk |   1 +
f085be
 editeng/qa/unit/core-test.cxx       | 100 +++++++++++++++++++--
f085be
 editeng/source/editeng/editdoc.cxx  | 173 ++++++++++++++++++++++++------------
f085be
 editeng/source/editeng/editdoc.hxx  |  12 ++-
f085be
 editeng/source/editeng/impedit4.cxx |   6 +-
f085be
 5 files changed, 226 insertions(+), 66 deletions(-)
f085be
f085be
diff --git a/editeng/CppunitTest_editeng_core.mk b/editeng/CppunitTest_editeng_core.mk
f085be
index 301c760..962fd8f 100644
f085be
--- a/editeng/CppunitTest_editeng_core.mk
f085be
+++ b/editeng/CppunitTest_editeng_core.mk
f085be
@@ -61,6 +61,7 @@ $(eval $(call gb_CppunitTest_use_components,editeng_core,\
f085be
     configmgr/source/configmgr \
f085be
     framework/util/fwk \
f085be
     i18npool/util/i18npool \
f085be
+	i18npool/source/search/i18nsearch \
f085be
     linguistic/source/lng \
f085be
     sfx2/util/sfx \
f085be
     ucb/source/core/ucb1 \
f085be
diff --git a/editeng/qa/unit/core-test.cxx b/editeng/qa/unit/core-test.cxx
f085be
index 338a6cb7..4e3da9b 100644
f085be
--- a/editeng/qa/unit/core-test.cxx
f085be
+++ b/editeng/qa/unit/core-test.cxx
f085be
@@ -25,6 +25,9 @@
f085be
 #include "editeng/postitem.hxx"
f085be
 #include "editeng/section.hxx"
f085be
 #include "editeng/editobj.hxx"
f085be
+#include "editeng/flditem.hxx"
f085be
+#include "svl/srchitem.hxx"
f085be
+#include "rtl/strbuf.hxx"
f085be
 
f085be
 #include <com/sun/star/text/textfield/Type.hpp>
f085be
 
f085be
@@ -44,22 +47,22 @@ public:
f085be
 
f085be
     void testConstruction();
f085be
 
f085be
-    /**
f085be
-     * Test UNO service class that implements text field items.
f085be
-     */
f085be
+    /// Test UNO service class that implements text field items.
f085be
     void testUnoTextFields();
f085be
 
f085be
-    /**
f085be
-     * AutoCorrect tests
f085be
-     */
f085be
+    /// AutoCorrect tests
f085be
     void testAutocorrect();
f085be
 
f085be
+    /// Test hyperlinks
f085be
+    void testHyperlinkSearch();
f085be
+
f085be
     void testSectionAttributes();
f085be
 
f085be
     CPPUNIT_TEST_SUITE(Test);
f085be
     CPPUNIT_TEST(testConstruction);
f085be
     CPPUNIT_TEST(testUnoTextFields);
f085be
     CPPUNIT_TEST(testAutocorrect);
f085be
+    CPPUNIT_TEST(testHyperlinkSearch);
f085be
     CPPUNIT_TEST(testSectionAttributes);
f085be
     CPPUNIT_TEST_SUITE_END();
f085be
 
f085be
@@ -340,6 +343,91 @@ void Test::testAutocorrect()
f085be
     }
f085be
 }
f085be
 
f085be
+namespace {
f085be
+    class UrlEditEngine : public EditEngine
f085be
+    {
f085be
+    public:
f085be
+        UrlEditEngine(SfxItemPool *pPool) : EditEngine(pPool) {}
f085be
+
f085be
+        virtual OUString CalcFieldValue( const SvxFieldItem&, sal_Int32, sal_uInt16, Color*&, Color*& )
f085be
+        {
f085be
+            return OUString("jim@bob.com"); // a sophisticated view of value:
f085be
+        }
f085be
+    };
f085be
+}
f085be
+
f085be
+// Odd accounting for hyperlink position & size etc.
f085be
+// https://bugzilla.novell.com/show_bug.cgi?id=467459
f085be
+void Test::testHyperlinkSearch()
f085be
+{
f085be
+    UrlEditEngine aEngine(mpItemPool);
f085be
+    EditDoc &rDoc = aEngine.GetEditDoc();
f085be
+
f085be
+    OUString aSampleText = "Please write email to . if you find a fish(not a dog).";
f085be
+    aEngine.SetText(aSampleText);
f085be
+
f085be
+    CPPUNIT_ASSERT_MESSAGE("set text", rDoc.GetParaAsString(sal_Int32(0)) == aSampleText);
f085be
+
f085be
+    ContentNode *pNode = rDoc.GetObject(0);
f085be
+    EditSelection aSel(EditPaM(pNode, 22), EditPaM(pNode, 22));
f085be
+    SvxURLField aURLField("mailto:///jim@bob.com", "jim@bob.com",
f085be
+                          SVXURLFORMAT_REPR);
f085be
+    SvxFieldItem aField(aURLField, EE_FEATURE_FIELD);
f085be
+
f085be
+    aEngine.InsertField(aSel, aField);
f085be
+    aEngine.UpdateFields();
f085be
+
f085be
+    OUString aContent = pNode->GetExpandedText();
f085be
+    CPPUNIT_ASSERT_MESSAGE("get text", aContent ==
f085be
+                           "Please write email to jim@bob.com. if you find a fish(not a dog).");
f085be
+    CPPUNIT_ASSERT_MESSAGE("wrong length", rDoc.GetTextLen() == (sal_uLong)aContent.getLength());
f085be
+
f085be
+    // Check expansion and positioning re-work
f085be
+    CPPUNIT_ASSERT_MESSAGE("wrong length", pNode->GetExpandedLen() ==
f085be
+                           (sal_uLong)aContent.getLength());
f085be
+    for (sal_Int32 n = 0; n < aContent.getLength(); n++)
f085be
+    {
f085be
+        sal_Int32 nStart = n, nEnd = n;
f085be
+        pNode->UnExpandPositions(nStart,nEnd);
f085be
+        CPPUNIT_ASSERT_MESSAGE("out of bound start", nStart < pNode->Len());
f085be
+        CPPUNIT_ASSERT_MESSAGE("out of bound end", nEnd <= pNode->Len());
f085be
+    }
f085be
+
f085be
+    static const struct {
f085be
+        sal_Int32 mnStart, mnEnd;
f085be
+        sal_Int32 mnNewStart, mnNewEnd;
f085be
+    } aTrickyOnes[] = {
f085be
+        {  0,  1, /* -> */  0, 1 },
f085be
+        { 21, 25, /* -> */ 21, 23 }, // the field is really just one char
f085be
+        { 25, 27, /* -> */ 22, 23 },
f085be
+        { 50, 56, /* -> */ 40, 46 }
f085be
+    };
f085be
+    for (size_t n = 0; n < SAL_N_ELEMENTS(aTrickyOnes); n++)
f085be
+    {
f085be
+        sal_Int32 nStart = aTrickyOnes[n].mnStart;
f085be
+        sal_Int32 nEnd = aTrickyOnes[n].mnEnd;
f085be
+        pNode->UnExpandPositions(nStart,nEnd);
f085be
+
f085be
+        rtl::OStringBuffer aBuf;
f085be
+        aBuf = "bound check start is ";
f085be
+        aBuf.append(nStart).append(" but should be ").append(aTrickyOnes[n].mnNewStart);
f085be
+        aBuf.append(" in row ").append((sal_Int32)n);
f085be
+        CPPUNIT_ASSERT_MESSAGE(aBuf.getStr(), nStart == aTrickyOnes[n].mnNewStart);
f085be
+        aBuf = "bound check end is ";
f085be
+        aBuf.append(nEnd).append(" but should be ").append(aTrickyOnes[n].mnNewEnd);
f085be
+        aBuf.append(" in row ").append((sal_Int32)n);
f085be
+        CPPUNIT_ASSERT_MESSAGE(aBuf.getStr(), nEnd == aTrickyOnes[n].mnNewEnd);
f085be
+    }
f085be
+
f085be
+    SvxSearchItem aItem(1); //SID_SEARCH_ITEM);
f085be
+    aItem.SetBackward(false);
f085be
+    aItem.SetSelection(false);
f085be
+    aItem.SetSearchString("fish");
f085be
+    CPPUNIT_ASSERT_MESSAGE("no fish", aEngine.HasText(aItem));
f085be
+    aItem.SetSearchString("dog");
f085be
+    CPPUNIT_ASSERT_MESSAGE("no dog", aEngine.HasText(aItem));
f085be
+}
f085be
+
f085be
 bool hasBold(const editeng::Section& rSecAttr)
f085be
 {
f085be
     std::vector<const SfxPoolItem*>::const_iterator it = rSecAttr.maAttributes.begin(), itEnd = rSecAttr.maAttributes.end();
f085be
diff --git a/editeng/source/editeng/editdoc.cxx b/editeng/source/editeng/editdoc.cxx
f085be
index 7eb4398..6b5d6f1 100644
f085be
--- a/editeng/source/editeng/editdoc.cxx
f085be
+++ b/editeng/source/editeng/editdoc.cxx
f085be
@@ -1699,6 +1699,119 @@ const OUString& ContentNode::GetString() const
f085be
     return maString;
f085be
 }
f085be
 
f085be
+sal_uLong ContentNode::GetExpandedLen() const
f085be
+{
f085be
+    sal_uLong nLen = maString.getLength();
f085be
+
f085be
+    // Fields can be longer than the placeholder in the Node
f085be
+    const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
f085be
+    for (sal_Int32 nAttr = rAttrs.size(); nAttr; )
f085be
+    {
f085be
+        const EditCharAttrib& rAttr = rAttrs[--nAttr];
f085be
+        if (rAttr.Which() == EE_FEATURE_FIELD)
f085be
+        {
f085be
+            nLen += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
f085be
+            --nLen; // Standalone, to avoid corner cases when previous getLength() returns 0
f085be
+        }
f085be
+    }
f085be
+
f085be
+    return nLen;
f085be
+}
f085be
+
f085be
+OUString ContentNode::GetExpandedText(sal_Int32 nStartPos, sal_Int32 nEndPos, bool bResolveFields) const
f085be
+{
f085be
+    if ( nEndPos < 0 || nEndPos > Len() )
f085be
+        nEndPos = Len();
f085be
+
f085be
+    DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" );
f085be
+
f085be
+    sal_Int32 nIndex = nStartPos;
f085be
+    OUString aStr;
f085be
+    const EditCharAttrib* pNextFeature = GetCharAttribs().FindFeature( nIndex );
f085be
+    while ( nIndex < nEndPos )
f085be
+    {
f085be
+        sal_Int32 nEnd = nEndPos;
f085be
+        if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) )
f085be
+            nEnd = pNextFeature->GetStart();
f085be
+        else
f085be
+            pNextFeature = 0;   // Feature does not interest the below
f085be
+
f085be
+        DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" );
f085be
+        //!! beware of sub string length  of -1
f085be
+        if (nEnd > nIndex)
f085be
+            aStr += GetString().copy(nIndex, nEnd - nIndex);
f085be
+
f085be
+        if ( pNextFeature )
f085be
+        {
f085be
+            switch ( pNextFeature->GetItem()->Which() )
f085be
+            {
f085be
+                case EE_FEATURE_TAB:    aStr += "\t";
f085be
+                break;
f085be
+                case EE_FEATURE_LINEBR: aStr += "\x0A";
f085be
+                break;
f085be
+                case EE_FEATURE_FIELD:
f085be
+                    if ( bResolveFields )
f085be
+                        aStr += static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue();
f085be
+                break;
f085be
+                default:    OSL_FAIL( "What feature?" );
f085be
+            }
f085be
+            pNextFeature = GetCharAttribs().FindFeature( ++nEnd );
f085be
+        }
f085be
+        nIndex = nEnd;
f085be
+    }
f085be
+    return aStr;
f085be
+}
f085be
+
f085be
+void ContentNode::UnExpandPosition( sal_Int32 &rPos, bool bBiasStart )
f085be
+{
f085be
+    sal_Int32 nOffset = 0;
f085be
+
f085be
+    const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
f085be
+    for (size_t nAttr = 0; nAttr < rAttrs.size(); ++nAttr )
f085be
+    {
f085be
+        const EditCharAttrib& rAttr = rAttrs[nAttr];
f085be
+        assert (!(nAttr < rAttrs.size() - 1) ||
f085be
+                rAttrs[nAttr].GetStart() < rAttrs[nAttr + 1].GetStart());
f085be
+
f085be
+        nOffset = rAttr.GetStart();
f085be
+
f085be
+        if (nOffset >= rPos) // happens after the position
f085be
+            return;
f085be
+
f085be
+        sal_Int32 nChunk = 0;
f085be
+        if (rAttr.Which() == EE_FEATURE_FIELD)
f085be
+        {
f085be
+            nChunk += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
f085be
+            nChunk--; // Character representing the field in the string
f085be
+
f085be
+            if (nOffset + nChunk >= rPos) // we're inside the field
f085be
+            {
f085be
+                if (bBiasStart)
f085be
+                    rPos = rAttr.GetStart();
f085be
+                else
f085be
+                    rPos = rAttr.GetEnd();
f085be
+                return;
f085be
+            }
f085be
+            // Adjust for the position
f085be
+            rPos -= nChunk;
f085be
+        }
f085be
+    }
f085be
+    assert (rPos <= Len());
f085be
+}
f085be
+
f085be
+/*
f085be
+ * Fields are represented by a single character in the underlying string
f085be
+ * and/or selection, however, they can be expanded to the full value of
f085be
+ * the field. When we're dealing with selection / offsets however we need
f085be
+ * to deal in character positions inside the real (unexpanded) string.
f085be
+ * This method maps us back to character offsets.
f085be
+ */
f085be
+void ContentNode::UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos )
f085be
+{
f085be
+    UnExpandPosition( rStartPos, true );
f085be
+    UnExpandPosition( rEndPos, false );
f085be
+}
f085be
+
f085be
 void ContentNode::SetChar(sal_uInt16 nPos, sal_Unicode c)
f085be
 {
f085be
     maString = maString.replaceAt(nPos, 1, OUString(c));
f085be
@@ -2141,49 +2254,9 @@ OUString EditDoc::GetParaAsString( sal_Int32 nNode ) const
f085be
 }
f085be
 
f085be
 OUString EditDoc::GetParaAsString(
f085be
-    const ContentNode* pNode, sal_uInt16 nStartPos, sal_uInt16 nEndPos, bool bResolveFields) const
f085be
+    const ContentNode* pNode, sal_Int32 nStartPos, sal_Int32 nEndPos, bool bResolveFields) const
f085be
 {
f085be
-    if ( nEndPos > pNode->Len() )
f085be
-        nEndPos = pNode->Len();
f085be
-
f085be
-    DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" );
f085be
-
f085be
-    sal_uInt16 nIndex = nStartPos;
f085be
-    OUString aStr;
f085be
-    const EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( nIndex );
f085be
-    while ( nIndex < nEndPos )
f085be
-    {
f085be
-        sal_uInt16 nEnd = nEndPos;
f085be
-        if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) )
f085be
-            nEnd = pNextFeature->GetStart();
f085be
-        else
f085be
-            pNextFeature = 0;   // Feature does not interest the below
f085be
-
f085be
-        DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" );
f085be
-        //!! beware of sub string length  of -1 which is also defined as STRING_LEN and
f085be
-        //!! thus would result in adding the whole sub string up to the end of the node !!
f085be
-        if (nEnd > nIndex)
f085be
-            aStr += pNode->GetString().copy(nIndex, nEnd - nIndex);
f085be
-
f085be
-        if ( pNextFeature )
f085be
-        {
f085be
-            switch ( pNextFeature->GetItem()->Which() )
f085be
-            {
f085be
-                case EE_FEATURE_TAB:    aStr += "\t";
f085be
-                break;
f085be
-                case EE_FEATURE_LINEBR: aStr += "\x0A";
f085be
-                break;
f085be
-                case EE_FEATURE_FIELD:
f085be
-                    if ( bResolveFields )
f085be
-                        aStr += static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue();
f085be
-                break;
f085be
-                default:    OSL_FAIL( "What feature?" );
f085be
-            }
f085be
-            pNextFeature = pNode->GetCharAttribs().FindFeature( ++nEnd );
f085be
-        }
f085be
-        nIndex = nEnd;
f085be
-    }
f085be
-    return aStr;
f085be
+    return pNode->GetExpandedText(nStartPos, nEndPos, bResolveFields);
f085be
 }
f085be
 
f085be
 EditPaM EditDoc::GetStartPaM() const
f085be
@@ -2204,21 +2277,7 @@ sal_uLong EditDoc::GetTextLen() const
f085be
     for ( sal_Int32 nNode = 0; nNode < Count(); nNode++ )
f085be
     {
f085be
         const ContentNode* pNode = GetObject( nNode );
f085be
-        nLen += pNode->Len();
f085be
-        // Fields can be longer than the placeholder in the Node
f085be
-        const CharAttribList::AttribsType& rAttrs = pNode->GetCharAttribs().GetAttribs();
f085be
-        for (size_t nAttr = rAttrs.size(); nAttr; )
f085be
-        {
f085be
-            const EditCharAttrib& rAttr = rAttrs[--nAttr];
f085be
-            if (rAttr.Which() == EE_FEATURE_FIELD)
f085be
-            {
f085be
-                sal_Int32 nFieldLen = static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
f085be
-                if ( !nFieldLen )
f085be
-                    nLen--;
f085be
-                else
f085be
-                    nLen += nFieldLen-1;
f085be
-            }
f085be
-        }
f085be
+        nLen += pNode->GetExpandedLen();
f085be
     }
f085be
     return nLen;
f085be
 }
f085be
diff --git a/editeng/source/editeng/editdoc.hxx b/editeng/source/editeng/editdoc.hxx
f085be
index dd04503..aba2a07 100644
f085be
--- a/editeng/source/editeng/editdoc.hxx
f085be
+++ b/editeng/source/editeng/editdoc.hxx
f085be
@@ -247,6 +247,8 @@ private:
f085be
     CharAttribList  aCharAttribList;
f085be
     boost::scoped_ptr<WrongList> mpWrongList;
f085be
 
f085be
+    void UnExpandPosition( sal_Int32 &rStartPos, bool bBiasStart );
f085be
+
f085be
 public:
f085be
                     ContentNode( SfxItemPool& rItemPool );
f085be
                     ContentNode( const OUString& rStr, const ContentAttribs& rContentAttribs );
f085be
@@ -282,8 +284,16 @@ public:
f085be
     sal_uInt16 Len() const;
f085be
     const OUString& GetString() const;
f085be
 
f085be
+    /// return length including expanded fields
f085be
+    sal_uLong GetExpandedLen() const;
f085be
+    /// return content including expanded fields
f085be
+    OUString  GetExpandedText(sal_Int32 nStartPos = 0, sal_Int32 nEndPos = -1, bool bResolveFields = true) const;
f085be
+    /// re-write offsets in the expanded text to string offsets
f085be
+    void      UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos );
f085be
+
f085be
     void SetChar(sal_uInt16 nPos, sal_Unicode c);
f085be
     void Insert(const OUString& rStr, sal_uInt16 nPos);
f085be
+
f085be
     void Append(const OUString& rStr);
f085be
     void Erase(sal_uInt16 nPos);
f085be
     void Erase(sal_uInt16 nPos, sal_uInt16 nCount);
f085be
@@ -769,7 +779,7 @@ public:
f085be
     sal_uLong       GetTextLen() const;
f085be
 
f085be
     OUString       GetParaAsString( sal_Int32 nNode ) const;
f085be
-    OUString       GetParaAsString(const ContentNode* pNode, sal_uInt16 nStartPos = 0, sal_uInt16 nEndPos = 0xFFFF, bool bResolveFields = true) const;
f085be
+    OUString       GetParaAsString(const ContentNode* pNode, sal_Int32 nStartPos = 0, sal_Int32 nEndPos = -1, bool bResolveFields = true) const;
f085be
 
f085be
     EditPaM GetStartPaM() const;
f085be
     EditPaM GetEndPaM() const;
f085be
diff --git a/editeng/source/editeng/impedit4.cxx b/editeng/source/editeng/impedit4.cxx
f085be
index b4ff56e..f4a9953 100644
f085be
--- a/editeng/source/editeng/impedit4.cxx
f085be
+++ b/editeng/source/editeng/impedit4.cxx
f085be
@@ -2647,7 +2647,7 @@ sal_Bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem,
f085be
         ContentNode* pNode = aEditDoc.GetObject( nNode );
f085be
 
f085be
         sal_Int32 nStartPos = 0;
f085be
-        sal_Int32 nEndPos = pNode->Len();
f085be
+        sal_Int32 nEndPos = pNode->GetExpandedLen();
f085be
         if ( nNode == nStartNode )
f085be
         {
f085be
             if ( bBack )
f085be
@@ -2664,7 +2664,7 @@ sal_Bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem,
f085be
         }
f085be
 
f085be
         // Searching ...
f085be
-        OUString aParaStr( GetEditDoc().GetParaAsString( pNode ) );
f085be
+        OUString aParaStr( pNode->GetExpandedText() );
f085be
         bool bFound = false;
f085be
         if ( bBack )
f085be
         {
f085be
@@ -2681,6 +2681,8 @@ sal_Bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem,
f085be
         }
f085be
         if ( bFound )
f085be
         {
f085be
+            pNode->UnExpandPositions( nStartPos, nEndPos );
f085be
+
f085be
             rFoundSel.Min().SetNode( pNode );
f085be
             rFoundSel.Min().SetIndex( nStartPos );
f085be
             rFoundSel.Max().SetNode( pNode );
f085be
-- 
f085be
1.9.3
f085be