formula/source/ui/dlg/funcpage.cxx    |   66 ++++++++++++++++-------
 formula/source/ui/dlg/funcpage.hxx    |    7 ++
 formula/uiconfig/ui/functionpage.ui   |   30 ++++++++--
 include/unotools/textsearch.hxx       |   27 +++++++++
 sc/source/ui/formdlg/dwfunctr.cxx     |   72 ++++++++++++++++---------
 sc/source/ui/inc/dwfunctr.hxx         |    5 +
 sc/uiconfig/scalc/ui/functionpanel.ui |   22 +++++++
 unotools/source/i18n/textsearch.cxx   |   97 ++++++++++++++++++++++++++++++++++
 8 files changed, 278 insertions(+), 48 deletions(-)

New commits:
commit d05e0be5f4ab2bdb31d6cf14528a9a8ee5ac965c
Author:     AhmedHamed <ahmedhamed3...@gmail.com>
AuthorDate: Fri Jul 5 21:41:26 2024 +0300
Commit:     Andreas Heinisch <andreas.heini...@yahoo.de>
CommitDate: Tue Sep 3 09:59:16 2024 +0200

    tdf#161543 Enhance the searching functionality in FD & FW
    
    Change-Id: I1a21595228f886c942ae46d90e41705443d31550
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170073
    Reviewed-by: Heiko Tietze <heiko.tie...@documentfoundation.org>
    Reviewed-by: Andreas Heinisch <andreas.heini...@yahoo.de>
    Tested-by: Jenkins

diff --git a/formula/source/ui/dlg/funcpage.cxx 
b/formula/source/ui/dlg/funcpage.cxx
index c844a29f612e..9724793928a5 100644
--- a/formula/source/ui/dlg/funcpage.cxx
+++ b/formula/source/ui/dlg/funcpage.cxx
@@ -25,6 +25,7 @@
 #include "funcpage.hxx"
 #include <unotools/syslocale.hxx>
 #include <unotools/charclass.hxx>
+#include <unotools/textsearch.hxx>
 
 namespace formula
 {
@@ -48,10 +49,10 @@ FuncPage::FuncPage(weld::Container* pParent, const 
IFunctionManager* _pFunctionM
     , m_xLbFunction(m_xBuilder->weld_tree_view(u"function"_ustr))
     , m_xScratchIter(m_xLbFunction->make_iterator())
     , m_xLbFunctionSearchString(m_xBuilder->weld_entry(u"search"_ustr))
+    , 
m_xSimilaritySearch(m_xBuilder->weld_check_button(u"similaritysearch"_ustr))
     , m_xHelpButton(m_xBuilder->weld_button(u"help"_ustr))
     , m_pFunctionManager(_pFunctionManager)
 {
-    m_xLbFunction->make_sorted();
     m_aHelpId = m_xLbFunction->get_help_id();
 
     m_pFunctionManager->fillLastRecentlyUsedFunctions(aLRUList);
@@ -76,6 +77,7 @@ FuncPage::FuncPage(weld::Container* pParent, const 
IFunctionManager* _pFunctionM
     m_xLbFunction->connect_row_activated(LINK(this, FuncPage, DblClkHdl));
     m_xLbFunction->connect_key_press(LINK(this, FuncPage, KeyInputHdl));
     m_xLbFunctionSearchString->connect_changed(LINK(this, FuncPage, 
ModifyHdl));
+    m_xSimilaritySearch->connect_toggled(LINK(this, FuncPage, 
SimilarityToggleHdl));
     m_xHelpButton->connect_clicked(LINK(this, FuncPage, SelHelpClickHdl));
 
     m_xHelpButton->set_sensitive(false);
@@ -117,12 +119,26 @@ void FuncPage::impl_addFunctions(const IFunctionCategory* 
_pCategory, bool bFill
     }
 }
 
+void FuncPage::SearchFunction(const OUString& rFuncName, const OUString& 
rSearchString,
+                              TFunctionDesc pDesc, const bool 
bSimilaritySearch)
+{
+    std::pair<sal_Int32, sal_Int32> score = std::make_pair(0, 0);
+    if (bSimilaritySearch && !utl::TextSearch::SimilaritySearch(rFuncName, 
rSearchString, score))
+        return;
+    if (!bSimilaritySearch && rFuncName.indexOf(rSearchString) < 0
+        && rSearchString.indexOf(rFuncName) < 0)
+        return;
+
+    sFuncScores.insert(std::make_pair(score, std::make_pair(rFuncName, 
pDesc)));
+}
+
 //aStr is non-empty when user types in the search box to search some function
 void FuncPage::UpdateFunctionList(const OUString& aStr)
 {
     m_xLbFunction->clear();
     m_xLbFunction->freeze();
     mCategories.clear();
+    sFuncScores.clear();
 
     const sal_Int32 nSelPos = m_xLbCategory->get_active();
     bool bCollapse = nSelPos == 1;
@@ -195,21 +211,23 @@ void FuncPage::UpdateFunctionList(const OUString& aStr)
             for (sal_uInt32 j = 0; j < nFunctionCount; ++j)
             {
                 TFunctionDesc pDesc(pCategory->getFunction(j));
-                // tdf#146781 - search for the desired function also in the 
description
-                if 
(rCharClass.uppercase(pDesc->getFunctionName()).indexOf(aSearchStr) >= 0
-                    || 
rCharClass.uppercase(pDesc->getDescription()).indexOf(aSearchStr) >= 0)
-                {
-                    if (!pDesc->isHidden())
-                    {
-                        OUString aFunction(pDesc->getFunctionName());
-                        OUString sId(weld::toId(pDesc));
-
-                        weld::TreeIter* pCategoryIter
-                            = FillCategoriesMap(pCategory->getName(), 
bCollapse);
-                        m_xLbFunction->insert(pCategoryIter, -1, &aFunction, 
&sId, nullptr, nullptr,
-                                              false, m_xScratchIter.get());
-                    }
-                }
+                const OUString 
aFunction(rCharClass.uppercase(pDesc->getFunctionName()));
+                SearchFunction(aFunction, aSearchStr, pDesc, 
m_xSimilaritySearch->get_active());
+            }
+        }
+
+        for (const auto& func : sFuncScores)
+        {
+            TFunctionDesc pDesc(func.second.second);
+            if (!pDesc->isHidden())
+            {
+                const OUString aCategory(pDesc->getCategory()->getName());
+                const OUString aFunction(func.second.first);
+                const OUString aFuncDescId(weld::toId(pDesc));
+                weld::TreeIter* pCategory = FillCategoriesMap(aCategory, 
bCollapse);
+
+                m_xLbFunction->insert(pCategory, -1, &aFunction, &aFuncDescId, 
nullptr, nullptr,
+                                      false, m_xScratchIter.get());
             }
         }
     }
@@ -231,11 +249,12 @@ void FuncPage::UpdateFunctionList(const OUString& aStr)
 
 IMPL_LINK_NOARG(FuncPage, SelComboBoxHdl, weld::ComboBox&, void)
 {
-    m_xLbFunctionSearchString->set_sensitive(m_xLbCategory->get_active() > 0);
+    if (m_xLbCategory->get_active() == 0)
+        m_xLbFunctionSearchString->set_text(u""_ustr);
+    m_xHelpButton->set_sensitive(false);
     OUString searchStr = m_xLbFunctionSearchString->get_text();
     m_xLbFunction->set_help_id(m_aHelpId);
     UpdateFunctionList(searchStr);
-    m_xHelpButton->set_sensitive(false);
 }
 
 IMPL_LINK_NOARG(FuncPage, SelTreeViewHdl, weld::TreeView&, void)
@@ -270,6 +289,17 @@ IMPL_LINK_NOARG(FuncPage, DblClkHdl, weld::TreeView&, bool)
 }
 
 IMPL_LINK_NOARG(FuncPage, ModifyHdl, weld::Entry&, void)
+{
+    if (m_xLbCategory->get_active() == 0)
+    {
+        m_xLbCategory->set_active(1);
+        m_xHelpButton->set_sensitive(false);
+    }
+    OUString searchStr = m_xLbFunctionSearchString->get_text();
+    UpdateFunctionList(searchStr);
+}
+
+IMPL_LINK_NOARG(FuncPage, SimilarityToggleHdl, weld::Toggleable&, void)
 {
     OUString searchStr = m_xLbFunctionSearchString->get_text();
     UpdateFunctionList(searchStr);
diff --git a/formula/source/ui/dlg/funcpage.hxx 
b/formula/source/ui/dlg/funcpage.hxx
index 0df77e1b156b..010bca872832 100644
--- a/formula/source/ui/dlg/funcpage.hxx
+++ b/formula/source/ui/dlg/funcpage.hxx
@@ -21,6 +21,7 @@
 
 #include <vcl/weld.hxx>
 #include <vector>
+#include <set>
 #include <unordered_map>
 
 namespace formula
@@ -42,6 +43,7 @@ private:
     std::unique_ptr<weld::TreeView> m_xLbFunction;
     std::unique_ptr<weld::TreeIter> m_xScratchIter;
     std::unique_ptr<weld::Entry> m_xLbFunctionSearchString;
+    std::unique_ptr<weld::CheckButton> m_xSimilaritySearch;
     std::unique_ptr<weld::Button> m_xHelpButton;
 
     Link<FuncPage&,void>     aDoubleClickLink;
@@ -50,6 +52,8 @@ private:
 
     ::std::vector< TFunctionDesc >  aLRUList;
     ::std::unordered_map<OUString, std::unique_ptr<weld::TreeIter>> 
mCategories;
+    ::std::set<std::pair<std::pair<sal_Int32, sal_Int32>, std::pair<OUString, 
TFunctionDesc>>>
+        sFuncScores;
     OUString    m_aHelpId;
 
     // tdf#104487 - remember last used function category
@@ -64,6 +68,7 @@ private:
     DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
     DECL_LINK(ModifyHdl, weld::Entry&, void);
     DECL_LINK(SelHelpClickHdl, weld::Button&, void);
+    DECL_LINK(SimilarityToggleHdl, weld::Toggleable&, void);
 
     void            UpdateFunctionList(const OUString&);
 
@@ -92,6 +97,8 @@ public:
     void            SetSelectHdl( const Link<FuncPage&,void>& rLink ) { 
aSelectionLink = rLink; }
 
     bool            IsVisible() const { return m_xContainer->get_visible(); }
+
+    void            SearchFunction(const OUString&, const OUString&, 
TFunctionDesc, const bool);
 };
 
 } // formula
diff --git a/formula/uiconfig/ui/functionpage.ui 
b/formula/uiconfig/ui/functionpage.ui
index 4ed38281cd1b..c48beb6918ac 100644
--- a/formula/uiconfig/ui/functionpage.ui
+++ b/formula/uiconfig/ui/functionpage.ui
@@ -52,6 +52,26 @@
         <property name="position">1</property>
       </packing>
     </child>
+    <child>
+      <object class="GtkCheckButton" id="similaritysearch">
+        <property name="label" translatable="yes" 
context="functionpage|similaritysearch">Similar</property>
+        <property name="visible">True</property>
+        <property name="can-focus">True</property>
+        <property name="receives-default">False</property>
+        <property name="use-underline">True</property>
+        <property name="draw-indicator">True</property>
+        <child internal-child="accessible">
+          <object class="AtkObject" id="similaritysearch-atkobject">
+            <property name="AtkObject::accessible-description" 
translatable="yes" context="functionpage|extended_tip|similaritysearch">Search 
and Sort functions by similarity</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">2</property>
+      </packing>
+    </child>
     <child>
       <object class="GtkLabel" id="label1">
         <property name="visible">True</property>
@@ -64,7 +84,7 @@
       <packing>
         <property name="expand">False</property>
         <property name="fill">True</property>
-        <property name="position">2</property>
+        <property name="position">3</property>
       </packing>
     </child>
     <child>
@@ -84,7 +104,7 @@
       <packing>
         <property name="expand">False</property>
         <property name="fill">True</property>
-        <property name="position">3</property>
+        <property name="position">4</property>
       </packing>
     </child>
     <child>
@@ -99,7 +119,7 @@
       <packing>
         <property name="expand">False</property>
         <property name="fill">True</property>
-        <property name="position">4</property>
+        <property name="position">5</property>
       </packing>
     </child>
     <child>
@@ -147,7 +167,7 @@
       <packing>
         <property name="expand">True</property>
         <property name="fill">True</property>
-        <property name="position">5</property>
+        <property name="position">6</property>
       </packing>
     </child>
     <child>
@@ -163,7 +183,7 @@
       <packing>
         <property name="expand">False</property>
         <property name="fill">True</property>
-        <property name="position">6</property>
+        <property name="position">7</property>
       </packing>
     </child>
     <child internal-child="accessible">
diff --git a/include/unotools/textsearch.hxx b/include/unotools/textsearch.hxx
index 753534166098..3b06c93549d5 100644
--- a/include/unotools/textsearch.hxx
+++ b/include/unotools/textsearch.hxx
@@ -27,6 +27,9 @@
 
 #include <ostream>
 
+#define WLD_THRESHOLD 3
+#define SMALL_STRING_THRESHOLD 4
+
 class CharClass;
 
 namespace com::sun::star::lang { struct Locale; }
@@ -209,6 +212,30 @@ public:
 
     /* replace back references in the replace string by the sub expressions 
from the search result */
     static void ReplaceBackReferences( OUString& rReplaceStr, 
std::u16string_view rStr, const css::util::SearchResult& rResult );
+
+    /**
+     * @brief Search for a string in a another one based on similarity
+     * @param rString The string we compare with
+     * @param rSearchString The search term
+     * @param rSimilarityScore The similarity score (sent by reference to be 
filled)
+     * @return True if the search term is found, false otherwise
+     */
+    static bool SimilaritySearch(const OUString& rString, const OUString& 
rSearchString,
+                                 ::std::pair<sal_Int32, sal_Int32>& 
rSimilarityScore);
+    /**
+     * @brief Get similarity score between two strings
+     *        according to the length of the common substring and its position
+     * @param rString The string we compare with
+     * @param rSearchString The search term
+     * @param nInitialScore The initial score
+     * @param bFromStart True if the search is from the start
+     * @return Score if the search term is found in the text, -1 otherwise
+     */
+    static sal_Int32 GetSubstringSimilarity(std::u16string_view rString,
+                                            std::u16string_view rSearchString,
+                                            sal_Int32& nInitialScore, const 
bool bFromStart);
+    static sal_Int32 GetWeightedLevenshteinDistance(const OUString& rString,
+                                                    const OUString& 
rSearchString);
 };
 
 }   // namespace utl
diff --git a/sc/source/ui/formdlg/dwfunctr.cxx 
b/sc/source/ui/formdlg/dwfunctr.cxx
index c4cf19cabf94..3ca34e1cc118 100644
--- a/sc/source/ui/formdlg/dwfunctr.cxx
+++ b/sc/source/ui/formdlg/dwfunctr.cxx
@@ -22,6 +22,7 @@
 #include <sfx2/viewsh.hxx>
 #include <formula/funcvarargs.h>
 #include <unotools/charclass.hxx>
+#include <unotools/textsearch.hxx>
 #include <vcl/svapp.hxx>
 #include <vcl/help.hxx>
 
@@ -55,6 +56,7 @@ ScFunctionWin::ScFunctionWin(weld::Widget* pParent)
     , xScratchIter(xFuncList->make_iterator())
     , xInsertButton(m_xBuilder->weld_button(u"insert"_ustr))
     , xHelpButton(m_xBuilder->weld_button(u"help"_ustr))
+    , 
xSimilaritySearch(m_xBuilder->weld_check_button(u"similaritysearch"_ustr))
     , xFiFuncDesc(m_xBuilder->weld_text_view(u"funcdesc"_ustr))
     , m_xSearchString(m_xBuilder->weld_entry(u"search"_ustr))
     , xConfigListener(new 
comphelper::ConfigurationListener(u"/org.openoffice.Office.Calc/Formula/Syntax"_ustr))
@@ -79,6 +81,7 @@ ScFunctionWin::ScFunctionWin(weld::Widget* pParent)
     xFuncList->connect_row_activated(LINK( this, ScFunctionWin, 
SetRowActivatedHdl));
     xInsertButton->connect_clicked(LINK( this, ScFunctionWin, 
SetSelectionClickHdl));
     xHelpButton->connect_clicked(LINK( this, ScFunctionWin, SetHelpClickHdl));
+    xSimilaritySearch->connect_toggled(LINK(this, ScFunctionWin, 
SetSimilarityToggleHdl));
 
     xCatBox->set_active(0);
 
@@ -109,6 +112,7 @@ ScFunctionWin::~ScFunctionWin()
     xFuncList.reset();
     xInsertButton.reset();
     xHelpButton.reset();
+    xSimilaritySearch.reset();
     xFiFuncDesc.reset();
 }
 
@@ -189,6 +193,19 @@ void ScFunctionWin::UpdateLRUList()
     }
 }
 
+void ScFunctionWin::SearchFunction(const OUString& rFuncName, const OUString& 
rSearchString,
+                                   const ScFuncDesc* pDesc, const bool 
bSimilaritySearch)
+{
+    std::pair<sal_Int32, sal_Int32> score = std::make_pair(0, 0);
+    if (bSimilaritySearch && !utl::TextSearch::SimilaritySearch(rFuncName, 
rSearchString, score))
+        return;
+    if (!bSimilaritySearch && rFuncName.indexOf(rSearchString) < 0
+        && rSearchString.indexOf(rFuncName) < 0)
+        return;
+
+    sFuncScores.insert(std::make_pair(score, std::make_pair(rFuncName, 
pDesc)));
+}
+
 /*************************************************************************
 #*  Member:     SetDescription
 #*------------------------------------------------------------------------
@@ -252,6 +269,7 @@ void ScFunctionWin::UpdateFunctionList(const OUString& 
rSearchString)
     xFuncList->clear();
     xFuncList->freeze();
     mCategories.clear();
+    sFuncScores.clear();
 
     bool bCollapse = nCategory == 0;
     bool bFilter = !rSearchString.isEmpty();
@@ -270,38 +288,31 @@ void ScFunctionWin::UpdateFunctionList(const OUString& 
rSearchString)
         const ScFuncDesc* pDesc = pFuncMgr->First(nCategory);
         while (pDesc)
         {
-            OUString aCategory = pDesc->getCategory()->getName();
-            OUString aFunction = pDesc->getFunctionName();
-            OUString aFuncDescId = weld::toId(pDesc);
+            const OUString aCategory(pDesc->getCategory()->getName());
+            const OUString 
aFunction(pCharClass->uppercase(pDesc->getFunctionName()));
+            const OUString aFuncDescId(weld::toId(pDesc));
 
-            if (!bFilter || 
(pCharClass->uppercase(aFunction).startsWith(aSearchStr)))
+            if (bFilter)
+                SearchFunction(aFunction, aSearchStr, pDesc, 
xSimilaritySearch->get_active());
+            else
             {
                 weld::TreeIter* pCategory = FillCategoriesMap(aCategory, 
bCollapse);
                 xFuncList->insert(pCategory, -1, &aFunction, &aFuncDescId, 
nullptr, nullptr,
-                        false, xScratchIter.get());
+                            false, xScratchIter.get());
             }
             pDesc = pFuncMgr->Next();
         }
 
-        // Now add the functions that have the search string in the middle of 
the function name
-        // Note that this will only be necessary if the search string is not 
empty
-        if (bFilter)
+        for (const auto& func : sFuncScores)
         {
-            pDesc = pFuncMgr->First( nCategory );
-            while ( pDesc )
-            {
-                OUString aCategory = pDesc->getCategory()->getName();
-                OUString aFunction = pDesc->getFunctionName();
-                OUString aFuncDescId = weld::toId(pDesc);
-
-                if (pCharClass->uppercase(aFunction).indexOf(aSearchStr) > 0)
-                {
-                    weld::TreeIter* pCategory = FillCategoriesMap(aCategory, 
bCollapse);
-                    xFuncList->insert(pCategory, -1, &aFunction, &aFuncDescId, 
nullptr, nullptr,
-                            false, xScratchIter.get());
-                }
-                pDesc = pFuncMgr->Next();
-            }
+            pDesc = func.second.second;
+            const OUString aCategory(pDesc->getCategory()->getName());
+            const OUString aFunction(func.second.first);
+            const OUString aFuncDescId(weld::toId(pDesc));
+            weld::TreeIter* pCategory = FillCategoriesMap(aCategory, 
bCollapse);
+
+            xFuncList->insert(pCategory, -1, &aFunction, &aFuncDescId, 
nullptr, nullptr, false,
+                              xScratchIter.get());
         }
     }
     else // LRU list
@@ -477,6 +488,11 @@ void ScFunctionWin::DoEnter(bool bDoubleOrEnter)
 
 IMPL_LINK_NOARG(ScFunctionWin, ModifyHdl, weld::Entry&, void)
 {
+    if (xCatBox->get_active() == 0)
+    {
+        xCatBox->set_active(1);
+        xHelpButton->set_sensitive(false);
+    }
     OUString searchStr = m_xSearchString->get_text();
     UpdateFunctionList(searchStr);
     SetDescription();
@@ -576,8 +592,9 @@ IMPL_LINK(ScFunctionWin, KeyInputHdl, const KeyEvent&, 
rEvent, bool)
 
 IMPL_LINK_NOARG(ScFunctionWin, SelComboHdl, weld::ComboBox&, void)
 {
+    if (xCatBox->get_active() == 0)
+        m_xSearchString->set_text(u""_ustr);
     xHelpButton->set_sensitive(xCatBox->get_active() != 1);
-    m_xSearchString->set_sensitive(xCatBox->get_active() > 0);
     OUString searchStr = m_xSearchString->get_text();
     UpdateFunctionList(searchStr);
     SetDescription();
@@ -638,6 +655,13 @@ IMPL_LINK_NOARG( ScFunctionWin, SetHelpClickHdl, 
weld::Button&, void )
     }
 }
 
+IMPL_LINK_NOARG(ScFunctionWin, SetSimilarityToggleHdl, weld::Toggleable&, void)
+{
+    OUString searchStr = m_xSearchString->get_text();
+    UpdateFunctionList(searchStr);
+    SetDescription();
+}
+
 IMPL_LINK_NOARG( ScFunctionWin, SetRowActivatedHdl, weld::TreeView&, bool )
 {
     DoEnter(true);      // saves the input
diff --git a/sc/source/ui/inc/dwfunctr.hxx b/sc/source/ui/inc/dwfunctr.hxx
index e393f2a69fcb..9c05491dac2d 100644
--- a/sc/source/ui/inc/dwfunctr.hxx
+++ b/sc/source/ui/inc/dwfunctr.hxx
@@ -49,6 +49,7 @@ private:
     std::unique_ptr<weld::TreeIter> xScratchIter;
     std::unique_ptr<weld::Button> xInsertButton;
     std::unique_ptr<weld::Button> xHelpButton;
+    std::unique_ptr<weld::CheckButton> xSimilaritySearch;
     std::unique_ptr<weld::TextView> xFiFuncDesc;
     std::unique_ptr<weld::Entry> m_xSearchString;
 
@@ -59,6 +60,8 @@ private:
     OUString m_aListHelpId;
     OUString m_aSearchHelpId;
 
+    ::std::set<std::pair<std::pair<sal_Int32, sal_Int32>, std::pair<OUString, 
const ScFuncDesc*>>>
+                     sFuncScores;
     ::std::vector< const formula::IFunctionDescription*> aLRUList;
     ::std::unordered_map<OUString, std::unique_ptr<weld::TreeIter>> 
mCategories;
 
@@ -70,6 +73,7 @@ private:
                     DECL_LINK( SetRowActivatedHdl, weld::TreeView&, bool );
                     DECL_LINK( SetSelectionClickHdl, weld::Button&, void );
                     DECL_LINK( SetHelpClickHdl, weld::Button&, void );
+                    DECL_LINK( SetSimilarityToggleHdl, weld::Toggleable&, void 
);
                     DECL_LINK( SelComboHdl, weld::ComboBox&, void );
                     DECL_LINK( SelTreeHdl, weld::TreeView&, void );
                     DECL_LINK( ModifyHdl, weld::Entry&, void );
@@ -82,6 +86,7 @@ public:
 
     void            InitLRUList();
     void            UpdateFunctionList(const OUString&);
+    void            SearchFunction(const OUString&, const OUString&, const 
ScFuncDesc*, const bool);
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/uiconfig/scalc/ui/functionpanel.ui 
b/sc/uiconfig/scalc/ui/functionpanel.ui
index 671aa7149270..f2bade14020e 100644
--- a/sc/uiconfig/scalc/ui/functionpanel.ui
+++ b/sc/uiconfig/scalc/ui/functionpanel.ui
@@ -145,7 +145,7 @@
               <object class="GtkBox">
                 <property name="visible">True</property>
                 <property name="can-focus">False</property>
-                <property name="orientation">vertical</property>
+                <property name="orientation">horizontal</property>
                 <property name="spacing">6</property>
                 <child>
                   <object class="GtkEntry" id="search">
@@ -166,6 +166,26 @@
                     <property name="position">1</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkCheckButton" id="similaritysearch">
+                    <property name="label" translatable="yes" 
context="functionpanel|similaritysearch">Similar</property>
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">False</property>
+                    <property name="use-underline">True</property>
+                    <property name="draw-indicator">True</property>
+                    <child internal-child="accessible">
+                      <object class="AtkObject" 
id="similaritysearch-atkobject">
+                        <property name="AtkObject::accessible-description" 
translatable="yes" context="functionpanel|extended_tip|similaritysearch">Search 
and Sort functions by similarity</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
               </object>
               <packing>
                 <property name="left-attach">0</property>
diff --git a/unotools/source/i18n/textsearch.cxx 
b/unotools/source/i18n/textsearch.cxx
index 9c4573c38538..5958240cda44 100644
--- a/unotools/source/i18n/textsearch.cxx
+++ b/unotools/source/i18n/textsearch.cxx
@@ -353,6 +353,103 @@ void TextSearch::ReplaceBackReferences( OUString& 
rReplaceStr, std::u16string_vi
     rReplaceStr = sBuff.makeStringAndClear();
 }
 
+bool TextSearch::SimilaritySearch(const OUString& rString, const OUString& 
rSearchString,
+                                  ::std::pair<sal_Int32, sal_Int32>& 
rSimilarityScore)
+{
+    sal_Int32 nScore = 0;
+    sal_Int32 nFirstScore = GetSubstringSimilarity(rString, rSearchString, 
nScore, true);
+    if (nFirstScore == -1)
+        nFirstScore = GetSubstringSimilarity(rString, rSearchString, nScore, 
false);
+    if (nFirstScore == -1)
+    {
+        if (rSearchString.getLength() == 1)
+        {
+            if (rString.startsWith(rSearchString))
+                nFirstScore = nScore;
+            else if (rString.endsWith(rSearchString))
+                nFirstScore = nScore + 1;
+            nScore += 2;
+        }
+        else if (rString.getLength() == 1 && rSearchString.getLength() < 
SMALL_STRING_THRESHOLD)
+        {
+            if (rSearchString.startsWith(rString))
+                nFirstScore = nScore;
+            else if (rSearchString.endsWith(rString))
+                nFirstScore = nScore + 1;
+            nScore += 2;
+        }
+    }
+    sal_Int32 nSecondScore = GetWeightedLevenshteinDistance(rString, 
rSearchString);
+
+    if (nFirstScore == -1 && nSecondScore >= WLD_THRESHOLD)
+        return false;
+
+    rSimilarityScore.first = (nFirstScore == -1) ? nScore : nFirstScore;
+    rSimilarityScore.second = nSecondScore;
+    return true;
+}
+
+sal_Int32 TextSearch::GetSubstringSimilarity(std::u16string_view rString,
+                                             std::u16string_view rSearchString,
+                                             sal_Int32& nInitialScore, const 
bool bFromStart)
+{
+    sal_Int32 nScore = -1;
+    for (sal_Int32 length = rSearchString.length(); length > 1; length--)
+    {
+        sal_Int32 nStartPos = bFromStart ? 0 : rSearchString.length() - length;
+        std::u16string_view rSearchSubString = rSearchString.substr(nStartPos, 
length);
+        if (rString.starts_with(rSearchSubString))
+        {
+            nScore = nInitialScore;
+            break;
+        }
+        else if (rString.ends_with(rSearchSubString))
+        {
+            nScore = nInitialScore + 1;
+            break;
+        }
+        else if (rString.find(rSearchSubString) != std::u16string_view::npos)
+        {
+            nScore = nInitialScore + 2;
+            break;
+        }
+        nInitialScore += 3;
+    }
+    return nScore;
+}
+
+sal_Int32 TextSearch::GetWeightedLevenshteinDistance(const OUString& rString,
+                                                     const OUString& 
rSearchString)
+{
+    sal_Int32 n = rString.getLength();
+    sal_Int32 m = rSearchString.getLength();
+    std::vector<std::vector<sal_Int32>> ScoreDP(n + 1, 
std::vector<sal_Int32>(m + 1));
+
+    for (sal_Int32 i = 0; i <= n; i++)
+    {
+        ScoreDP[i][0] = i;
+    }
+    for (sal_Int32 j = 0; j <= m; j++)
+    {
+        ScoreDP[0][j] = j;
+    }
+
+    for (sal_Int32 i = 1; i <= n; i++)
+    {
+        for (sal_Int32 j = 1; j <= m; j++)
+        {
+            sal_Int32& minE = ScoreDP[i][j];
+            minE = ScoreDP[i - 1][j] + 1;
+            minE = std::min(minE, ScoreDP[i][j - 1] + 1);
+            if (rString[i - 1] != rSearchString[j - 1])
+                minE = std::min(minE, ScoreDP[i - 1][j - 1] + 2);
+            else
+                minE = std::min(minE, ScoreDP[i - 1][j - 1]);
+        }
+    }
+    return ScoreDP[n][m];
+}
+
 }   // namespace utl
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to