formula/source/ui/dlg/formula.cxx | 8 -- formula/source/ui/dlg/funcpage.cxx | 73 ++++++++++++++++++---- formula/source/ui/dlg/funcpage.hxx | 8 +- formula/uiconfig/ui/functionpage.ui | 2 sc/source/ui/formdlg/dwfunctr.cxx | 112 +++++++++++++++++++++++++++------- sc/source/ui/inc/dwfunctr.hxx | 6 + sc/uiconfig/scalc/ui/functionpanel.ui | 2 7 files changed, 168 insertions(+), 43 deletions(-)
New commits: commit 5bd284583fe1f891bac3192d8e20083a57830a0a Author: AhmedHamed <ahmedhamed3...@gmail.com> AuthorDate: Thu Jun 27 15:07:24 2024 +0300 Commit: Andreas Heinisch <andreas.heini...@yahoo.de> CommitDate: Fri Jul 19 07:50:55 2024 +0200 tdf#161296 Convert functions list in FD & FW into collapsible sections Change-Id: I9211c0cf02be8d21e343d57f53d14e50d7b5fd0d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169639 Reviewed-by: Andreas Heinisch <andreas.heini...@yahoo.de> Tested-by: Jenkins diff --git a/formula/source/ui/dlg/formula.cxx b/formula/source/ui/dlg/formula.cxx index 0edc5b78420c..1d1b3c6c624e 100644 --- a/formula/source/ui/dlg/formula.cxx +++ b/formula/source/ui/dlg/formula.cxx @@ -1034,7 +1034,7 @@ IMPL_LINK(FormulaDlg_Impl, BtnHdl, weld::Button&, rBtn, void) const IFunctionDescription* pDesc; sal_Int32 nSelFunc = m_xFuncPage->GetFunction(); if (nSelFunc != -1) - pDesc = m_xFuncPage->GetFuncDesc( nSelFunc ); + pDesc = m_xFuncPage->GetFuncDesc(); else { // Do not overwrite the selected formula expression, just edit the @@ -1064,10 +1064,8 @@ IMPL_LINK(FormulaDlg_Impl, BtnHdl, weld::Button&, rBtn, void) IMPL_LINK_NOARG( FormulaDlg_Impl, DblClkHdl, FuncPage&, void) { - sal_Int32 nFunc = m_xFuncPage->GetFunction(); - // ex-UpdateLRUList - const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc(nFunc); + const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc(); m_pHelper->insertEntryToLRUList(pDesc); OUString aFuncName = m_xFuncPage->GetSelFunctionName() + "()"; @@ -1672,7 +1670,7 @@ IMPL_LINK_NOARG( FormulaDlg_Impl, FuncSelHdl, FuncPage&, void) if ( (m_xFuncPage->GetFunctionEntryCount() > 0) && (m_xFuncPage->GetFunction() != -1) ) { - const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc( m_xFuncPage->GetFunction() ); + const IFunctionDescription* pDesc = m_xFuncPage->GetFuncDesc(); if (pDesc != m_pFuncDesc) m_xBtnForward->set_sensitive(true); //new diff --git a/formula/source/ui/dlg/funcpage.cxx b/formula/source/ui/dlg/funcpage.cxx index c66e5f0cfb36..c844a29f612e 100644 --- a/formula/source/ui/dlg/funcpage.cxx +++ b/formula/source/ui/dlg/funcpage.cxx @@ -46,6 +46,7 @@ FuncPage::FuncPage(weld::Container* pParent, const IFunctionManager* _pFunctionM , m_xContainer(m_xBuilder->weld_container(u"FunctionPage"_ustr)) , m_xLbCategory(m_xBuilder->weld_combo_box(u"category"_ustr)) , 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_xHelpButton(m_xBuilder->weld_button(u"help"_ustr)) , m_pFunctionManager(_pFunctionManager) @@ -83,16 +84,35 @@ FuncPage::FuncPage(weld::Container* pParent, const IFunctionManager* _pFunctionM FuncPage::~FuncPage() {} -void FuncPage::impl_addFunctions(const IFunctionCategory* _pCategory) +weld::TreeIter* FuncPage::FillCategoriesMap(const OUString& aCategory, bool bFill) { + if (!bFill) + return nullptr; + + if (mCategories.find(aCategory) == mCategories.end()) + { + mCategories[aCategory] = m_xLbFunction->make_iterator(); + m_xLbFunction->insert(nullptr, -1, &aCategory, nullptr, nullptr, nullptr, false, + mCategories[aCategory].get()); + } + return mCategories[aCategory].get(); +} + +void FuncPage::impl_addFunctions(const IFunctionCategory* _pCategory, bool bFillCategories) +{ + weld::TreeIter* pCategoryIter = FillCategoriesMap(_pCategory->getName(), bFillCategories); + const sal_uInt32 nCount = _pCategory->getCount(); for (sal_uInt32 i = 0; i < nCount; ++i) { TFunctionDesc pDesc(_pCategory->getFunction(i)); if (!pDesc->isHidden()) { + OUString aFunction(pDesc->getFunctionName()); OUString sId(weld::toId(pDesc)); - m_xLbFunction->append(sId, pDesc->getFunctionName()); + + m_xLbFunction->insert(pCategoryIter, -1, &aFunction, &sId, nullptr, nullptr, false, + m_xScratchIter.get()); } } } @@ -102,12 +122,15 @@ void FuncPage::UpdateFunctionList(const OUString& aStr) { m_xLbFunction->clear(); m_xLbFunction->freeze(); + mCategories.clear(); const sal_Int32 nSelPos = m_xLbCategory->get_active(); + bool bCollapse = nSelPos == 1; + bool bFilter = !aStr.isEmpty(); // tdf#104487 - remember last used function category m_nRememberedFunctionCategory = nSelPos; - if (aStr.isEmpty() || nSelPos == 0) + if (!bFilter || nSelPos == 0) { const IFunctionCategory* pCategory = weld::fromId<const IFunctionCategory*>(m_xLbCategory->get_id(nSelPos)); @@ -119,12 +142,12 @@ void FuncPage::UpdateFunctionList(const OUString& aStr) const sal_uInt32 nCount = m_pFunctionManager->getCount(); for (sal_uInt32 i = 0; i < nCount; ++i) { - impl_addFunctions(m_pFunctionManager->getCategory(i)); + impl_addFunctions(m_pFunctionManager->getCategory(i), bCollapse); } } else { - impl_addFunctions(pCategory); + impl_addFunctions(pCategory, false); } } else // LRU-List @@ -133,8 +156,11 @@ void FuncPage::UpdateFunctionList(const OUString& aStr) { if (elem) // may be null if a function is no longer available { + OUString aFunction(elem->getFunctionName()); OUString sId(weld::toId(elem)); - m_xLbFunction->append(sId, elem->getFunctionName()); + + m_xLbFunction->insert(nullptr, -1, &aFunction, &sId, nullptr, nullptr, false, + m_xScratchIter.get()); } } } @@ -175,8 +201,13 @@ void FuncPage::UpdateFunctionList(const OUString& aStr) { if (!pDesc->isHidden()) { + OUString aFunction(pDesc->getFunctionName()); OUString sId(weld::toId(pDesc)); - m_xLbFunction->append(sId, pDesc->getFunctionName()); + + weld::TreeIter* pCategoryIter + = FillCategoriesMap(pCategory->getName(), bCollapse); + m_xLbFunction->insert(pCategoryIter, -1, &aFunction, &sId, nullptr, nullptr, + false, m_xScratchIter.get()); } } } @@ -188,12 +219,19 @@ void FuncPage::UpdateFunctionList(const OUString& aStr) // function that is not in the list with an arbitrary selected one. m_xLbFunction->unselect_all(); + if (bCollapse && bFilter) + { + for (const auto& category : mCategories) + m_xLbFunction->expand_row(*category.second); + } + if (IsVisible()) SelTreeViewHdl(*m_xLbFunction); } IMPL_LINK_NOARG(FuncPage, SelComboBoxHdl, weld::ComboBox&, void) { + m_xLbFunctionSearchString->set_sensitive(m_xLbCategory->get_active() > 0); OUString searchStr = m_xLbFunctionSearchString->get_text(); m_xLbFunction->set_help_id(m_aHelpId); UpdateFunctionList(searchStr); @@ -202,7 +240,7 @@ IMPL_LINK_NOARG(FuncPage, SelComboBoxHdl, weld::ComboBox&, void) IMPL_LINK_NOARG(FuncPage, SelTreeViewHdl, weld::TreeView&, void) { - const IFunctionDescription* pDesc = GetFuncDesc(GetFunction()); + const IFunctionDescription* pDesc = GetFuncDesc(); if (pDesc) { const OUString sHelpId = pDesc->getHelpId(); @@ -216,14 +254,23 @@ IMPL_LINK_NOARG(FuncPage, SelTreeViewHdl, weld::TreeView&, void) IMPL_LINK_NOARG(FuncPage, DblClkHdl, weld::TreeView&, bool) { + const OUString aString = m_xLbFunction->get_selected_text(); + if (mCategories.find(aString) != mCategories.end()) + { + const auto& categoryRow = *(mCategories[aString]); + if (m_xLbFunction->get_row_expanded(categoryRow)) + m_xLbFunction->collapse_row(categoryRow); + else + m_xLbFunction->expand_row(categoryRow); + return true; + } + m_xLbFunctionSearchString->set_text(OUString()); aDoubleClickLink.Call(*this); return true; } IMPL_LINK_NOARG(FuncPage, ModifyHdl, weld::Entry&, void) { - // While typing select All category. - m_xLbCategory->set_active(1); OUString searchStr = m_xLbFunctionSearchString->get_text(); UpdateFunctionList(searchStr); } @@ -277,12 +324,12 @@ sal_Int32 FuncPage::GetFunctionEntryCount() const { return m_xLbFunction->n_chil OUString FuncPage::GetSelFunctionName() const { return m_xLbFunction->get_selected_text(); } -const IFunctionDescription* FuncPage::GetFuncDesc(sal_Int32 nPos) const +const IFunctionDescription* FuncPage::GetFuncDesc() const { - if (nPos == -1) + if (GetFunction() == -1) return nullptr; // not pretty, but hopefully rare - return weld::fromId<const IFunctionDescription*>(m_xLbFunction->get_id(nPos)); + return weld::fromId<const IFunctionDescription*>(m_xLbFunction->get_selected_id()); } } // formula diff --git a/formula/source/ui/dlg/funcpage.hxx b/formula/source/ui/dlg/funcpage.hxx index 45d5d19b75f9..0df77e1b156b 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 <unordered_map> namespace formula { @@ -39,6 +40,7 @@ private: std::unique_ptr<weld::ComboBox> m_xLbCategory; 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::Button> m_xHelpButton; @@ -47,12 +49,14 @@ private: const IFunctionManager* m_pFunctionManager; ::std::vector< TFunctionDesc > aLRUList; + ::std::unordered_map<OUString, std::unique_ptr<weld::TreeIter>> mCategories; OUString m_aHelpId; // tdf#104487 - remember last used function category static sal_Int32 m_nRememberedFunctionCategory; - void impl_addFunctions(const IFunctionCategory* _pCategory); + void impl_addFunctions(const IFunctionCategory*, bool); + weld::TreeIter* FillCategoriesMap(const OUString&, bool); DECL_LINK(SelComboBoxHdl, weld::ComboBox&, void); DECL_LINK(SelTreeViewHdl, weld::TreeView&, void); @@ -80,7 +84,7 @@ public: static sal_Int32 GetRememeberdFunctionCategory() { return m_nRememberedFunctionCategory; }; sal_Int32 GetFuncPos(const IFunctionDescription* _pDesc); - const IFunctionDescription* GetFuncDesc( sal_Int32 nPos ) const; + const IFunctionDescription* GetFuncDesc() const; OUString GetSelFunctionName() const; void SetDoubleClickHdl( const Link<FuncPage&,void>& rLink ) { aDoubleClickLink = rLink; } diff --git a/formula/uiconfig/ui/functionpage.ui b/formula/uiconfig/ui/functionpage.ui index aa1e2ce08e3b..247e4c4fce4d 100644 --- a/formula/uiconfig/ui/functionpage.ui +++ b/formula/uiconfig/ui/functionpage.ui @@ -120,7 +120,7 @@ <property name="model">liststore1</property> <property name="headers-visible">False</property> <property name="search-column">0</property> - <property name="show-expanders">False</property> + <property name="show-expanders">True</property> <child internal-child="selection"> <object class="GtkTreeSelection"/> </child> diff --git a/sc/source/ui/formdlg/dwfunctr.cxx b/sc/source/ui/formdlg/dwfunctr.cxx index ba59abccee9f..f2665e017831 100644 --- a/sc/source/ui/formdlg/dwfunctr.cxx +++ b/sc/source/ui/formdlg/dwfunctr.cxx @@ -51,6 +51,7 @@ ScFunctionWin::ScFunctionWin(weld::Widget* pParent) : PanelLayout(pParent, u"FunctionPanel"_ustr, u"modules/scalc/ui/functionpanel.ui"_ustr) , xCatBox(m_xBuilder->weld_combo_box(u"category"_ustr)) , xFuncList(m_xBuilder->weld_tree_view(u"funclist"_ustr)) + , xScratchIter(xFuncList->make_iterator()) , xInsertButton(m_xBuilder->weld_button(u"insert"_ustr)) , xHelpButton(m_xBuilder->weld_button(u"help"_ustr)) , xFiFuncDesc(m_xBuilder->weld_text_view(u"funcdesc"_ustr)) @@ -135,6 +136,35 @@ void ScFunctionWin::InitLRUList() UpdateFunctionList(u""_ustr); } + +/************************************************************************* +#* Member: FillCategoriesMap +#*------------------------------------------------------------------------ +#* +#* Class: ScFunctionWin +#* +#* Function: Fills the categories map. +#* +#* Input: --- +#* +#* Output: --- +#* +#************************************************************************/ + +weld::TreeIter* ScFunctionWin::FillCategoriesMap(const OUString& aCategory, bool bFill) +{ + if (!bFill) + return nullptr; + + if (mCategories.find(aCategory) == mCategories.end()) + { + mCategories[aCategory] = xFuncList->make_iterator(); + xFuncList->insert(nullptr, -1, &aCategory, nullptr, nullptr, nullptr, false, + mCategories[aCategory].get()); + } + return mCategories[aCategory].get(); +} + /************************************************************************* #* Member: UpdateLRUList #*------------------------------------------------------------------------ @@ -220,7 +250,10 @@ void ScFunctionWin::UpdateFunctionList(const OUString& rSearchString) xFuncList->clear(); xFuncList->freeze(); + mCategories.clear(); + bool bCollapse = nCategory == 0; + bool bFilter = !rSearchString.isEmpty(); if ( nSelPos > 0 ) { ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr(); @@ -228,26 +261,40 @@ void ScFunctionWin::UpdateFunctionList(const OUString& rSearchString) SvtSysLocale aSysLocale; const CharClass& rCharClass = aSysLocale.GetCharClass(); const OUString aSearchStr(rCharClass.uppercase(rSearchString)); + const ScFuncDesc* pDesc = pFuncMgr->First(nCategory); - // First add the functions that start with the search string - const ScFuncDesc* pDesc = pFuncMgr->First( nCategory ); - while ( pDesc ) + while (pDesc) { - if (rSearchString.isEmpty() - || (rCharClass.uppercase(pDesc->getFunctionName()).startsWith(aSearchStr))) - xFuncList->append(weld::toId(pDesc), *(pDesc->mxFuncName)); + OUString aCategory = pDesc->getCategory()->getName(); + OUString aFunction = pDesc->getFunctionName(); + OUString aFuncDescId = weld::toId(pDesc); + + if (!bFilter || (rCharClass.uppercase(aFunction).startsWith(aSearchStr))) + { + weld::TreeIter* pCategory = FillCategoriesMap(aCategory, bCollapse); + xFuncList->insert(pCategory, -1, &aFunction, &aFuncDescId, nullptr, nullptr, + 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 (!rSearchString.isEmpty()) + if (bFilter) { pDesc = pFuncMgr->First( nCategory ); while ( pDesc ) { - if (rCharClass.uppercase(pDesc->getFunctionName()).indexOf(aSearchStr) > 0) - xFuncList->append(weld::toId(pDesc), *(pDesc->mxFuncName)); + OUString aCategory = pDesc->getCategory()->getName(); + OUString aFunction = pDesc->getFunctionName(); + OUString aFuncDescId = weld::toId(pDesc); + + if (rCharClass.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(); } } @@ -258,13 +305,23 @@ void ScFunctionWin::UpdateFunctionList(const OUString& rSearchString) { if (pDesc) { - xFuncList->append(weld::toId(pDesc), pDesc->getFunctionName()); + OUString aFunction = pDesc->getFunctionName(); + OUString aFuncDescId = weld::toId(pDesc); + + xFuncList->insert(nullptr, -1, &aFunction, &aFuncDescId, nullptr, nullptr, + false, xScratchIter.get()); } } } xFuncList->thaw(); + if (bCollapse && bFilter) + { + for (const auto& category : mCategories) + xFuncList->expand_row(*category.second); + } + if (xFuncList->n_children() > 0) { xFuncList->set_sensitive(true); @@ -285,16 +342,30 @@ void ScFunctionWin::UpdateFunctionList(const OUString& rSearchString) #* Function: Save input into document. Is called after clicking the #* Apply button or a double-click on the function list. #* -#* Input: --- +#* Input: Boolean to know if I double-clicked/press-enter or not #* #* Output: --- #* #************************************************************************/ -void ScFunctionWin::DoEnter() +void ScFunctionWin::DoEnter(bool bDoubleOrEnter) { - OUStringBuffer aArgStr; OUString aString=xFuncList->get_selected_text(); + const bool isCategory = mCategories.find(aString) != mCategories.end(); + if (isCategory && !bDoubleOrEnter) + return; + + if (isCategory) + { + const auto& categoryRow = *(mCategories[aString]); + if (xFuncList->get_row_expanded(categoryRow)) + xFuncList->collapse_row(categoryRow); + else + xFuncList->expand_row(categoryRow); + return; + } + + OUStringBuffer aArgStr; SfxViewShell* pCurSh = SfxViewShell::Current(); nArgs=0; @@ -401,8 +472,6 @@ void ScFunctionWin::DoEnter() IMPL_LINK_NOARG(ScFunctionWin, ModifyHdl, weld::Entry&, void) { - // Switch to the "All" category when searching a function - xCatBox->set_active(1); OUString searchStr = m_xSearchString->get_text(); UpdateFunctionList(searchStr); SetDescription(); @@ -426,7 +495,7 @@ IMPL_LINK(ScFunctionWin, KeyInputHdl, const KeyEvent&, rEvent, bool) { case KEY_RETURN: { - DoEnter(); + DoEnter(true); bHandled = true; } break; @@ -502,14 +571,17 @@ IMPL_LINK(ScFunctionWin, KeyInputHdl, const KeyEvent&, rEvent, bool) IMPL_LINK_NOARG(ScFunctionWin, SelComboHdl, weld::ComboBox&, void) { - UpdateFunctionList(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(); - m_xSearchString->set_text(u""_ustr); - m_xSearchString->grab_focus(); } IMPL_LINK_NOARG(ScFunctionWin, SelTreeHdl, weld::TreeView&, void) { + bool bSensitivity = weld::fromId<const ScFuncDesc*>(xFuncList->get_selected_id()); + xHelpButton->set_sensitive(bSensitivity); SetDescription(); } @@ -563,7 +635,7 @@ IMPL_LINK_NOARG( ScFunctionWin, SetHelpClickHdl, weld::Button&, void ) IMPL_LINK_NOARG( ScFunctionWin, SetRowActivatedHdl, weld::TreeView&, bool ) { - DoEnter(); // saves the input + DoEnter(true); // saves the input return true; } diff --git a/sc/source/ui/inc/dwfunctr.hxx b/sc/source/ui/inc/dwfunctr.hxx index cc854fa317a1..e393f2a69fcb 100644 --- a/sc/source/ui/inc/dwfunctr.hxx +++ b/sc/source/ui/inc/dwfunctr.hxx @@ -20,6 +20,7 @@ #include <comphelper/configurationlistener.hxx> #include <sfx2/sidebar/PanelLayout.hxx> +#include <unordered_map> class ScFuncDesc; namespace formula { class IFunctionDescription; } @@ -45,6 +46,7 @@ class ScFunctionWin : public PanelLayout private: std::unique_ptr<weld::ComboBox> xCatBox; std::unique_ptr<weld::TreeView> xFuncList; + std::unique_ptr<weld::TreeIter> xScratchIter; std::unique_ptr<weld::Button> xInsertButton; std::unique_ptr<weld::Button> xHelpButton; std::unique_ptr<weld::TextView> xFiFuncDesc; @@ -58,10 +60,12 @@ private: OUString m_aSearchHelpId; ::std::vector< const formula::IFunctionDescription*> aLRUList; + ::std::unordered_map<OUString, std::unique_ptr<weld::TreeIter>> mCategories; void UpdateLRUList(); - void DoEnter(); + void DoEnter(bool bDouble_or_Enter = false); void SetDescription(); + weld::TreeIter* FillCategoriesMap(const OUString&, bool); DECL_LINK( SetRowActivatedHdl, weld::TreeView&, bool ); DECL_LINK( SetSelectionClickHdl, weld::Button&, void ); diff --git a/sc/uiconfig/scalc/ui/functionpanel.ui b/sc/uiconfig/scalc/ui/functionpanel.ui index f67bd55a352d..671aa7149270 100644 --- a/sc/uiconfig/scalc/ui/functionpanel.ui +++ b/sc/uiconfig/scalc/ui/functionpanel.ui @@ -114,7 +114,7 @@ <property name="headers-visible">False</property> <property name="headers-clickable">False</property> <property name="search-column">0</property> - <property name="show-expanders">False</property> + <property name="show-expanders">True</property> <child internal-child="selection"> <object class="GtkTreeSelection"/> </child>