1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/event.hxx>
21 #include <vcl/svapp.hxx>
22 #include <vcl/help.hxx>
23 #include <formula/IFunctionDescription.hxx>
25 #include "funcpage.hxx"
26 #include <unotools/syslocale.hxx>
27 #include <unotools/charclass.hxx>
28 #include <unotools/textsearch.hxx>
32 IMPL_LINK(FuncPage
, KeyInputHdl
, const KeyEvent
&, rKEvt
, bool)
34 if (rKEvt
.GetCharCode() == ' ')
36 aDoubleClickLink
.Call(*this);
42 // tdf#104487 - remember last used function category - set default to All category
43 sal_Int32
FuncPage::m_nRememberedFunctionCategory
= 1;
45 FuncPage::FuncPage(weld::Container
* pParent
, const IFunctionManager
* _pFunctionManager
)
46 : m_xBuilder(Application::CreateBuilder(pParent
, u
"formula/ui/functionpage.ui"_ustr
))
47 , m_xContainer(m_xBuilder
->weld_container(u
"FunctionPage"_ustr
))
48 , m_xLbCategory(m_xBuilder
->weld_combo_box(u
"category"_ustr
))
49 , m_xLbFunction(m_xBuilder
->weld_tree_view(u
"function"_ustr
))
50 , m_xScratchIter(m_xLbFunction
->make_iterator())
51 , m_xLbFunctionSearchString(m_xBuilder
->weld_entry(u
"search"_ustr
))
52 , m_xSimilaritySearch(m_xBuilder
->weld_check_button(u
"similaritysearch"_ustr
))
53 , m_xHelpButton(m_xBuilder
->weld_button(u
"help"_ustr
))
54 , m_pFunctionManager(_pFunctionManager
)
56 m_aHelpId
= m_xLbFunction
->get_help_id();
58 m_pFunctionManager
->fillLastRecentlyUsedFunctions(aLRUList
);
60 const sal_uInt32 nCategoryCount
= m_pFunctionManager
->getCount();
61 for (sal_uInt32 j
= 0; j
< nCategoryCount
; ++j
)
63 const IFunctionCategory
* pCategory
= m_pFunctionManager
->getCategory(j
);
64 OUString
sId(weld::toId(pCategory
));
65 m_xLbCategory
->append(sId
, pCategory
->getName());
68 // tdf#104487 - remember last used function category
69 m_xLbCategory
->set_active(m_nRememberedFunctionCategory
);
70 OUString searchStr
= m_xLbFunctionSearchString
->get_text();
71 UpdateFunctionList(searchStr
);
72 // lock to its initial size
73 m_xLbFunction
->set_size_request(m_xLbFunction
->get_preferred_size().Width(),
74 m_xLbFunction
->get_height_rows(15));
75 m_xLbCategory
->connect_changed(LINK(this, FuncPage
, SelComboBoxHdl
));
76 m_xLbFunction
->connect_selection_changed(LINK(this, FuncPage
, SelTreeViewHdl
));
77 m_xLbFunction
->connect_row_activated(LINK(this, FuncPage
, DblClkHdl
));
78 m_xLbFunction
->connect_key_press(LINK(this, FuncPage
, KeyInputHdl
));
79 m_xLbFunctionSearchString
->connect_changed(LINK(this, FuncPage
, ModifyHdl
));
80 m_xSimilaritySearch
->connect_toggled(LINK(this, FuncPage
, SimilarityToggleHdl
));
81 m_xHelpButton
->connect_clicked(LINK(this, FuncPage
, SelHelpClickHdl
));
83 m_xHelpButton
->set_sensitive(false);
84 m_xLbFunctionSearchString
->grab_focus();
87 FuncPage::~FuncPage() {}
89 weld::TreeIter
* FuncPage::FillCategoriesMap(const OUString
& aCategory
, bool bFill
)
94 if (mCategories
.find(aCategory
) == mCategories
.end())
96 mCategories
[aCategory
] = m_xLbFunction
->make_iterator();
97 m_xLbFunction
->insert(nullptr, -1, &aCategory
, nullptr, nullptr, nullptr, false,
98 mCategories
[aCategory
].get());
100 return mCategories
[aCategory
].get();
103 void FuncPage::impl_addFunctions(const IFunctionCategory
* _pCategory
, bool bFillCategories
)
105 weld::TreeIter
* pCategoryIter
= FillCategoriesMap(_pCategory
->getName(), bFillCategories
);
107 const sal_uInt32 nCount
= _pCategory
->getCount();
108 for (sal_uInt32 i
= 0; i
< nCount
; ++i
)
110 TFunctionDesc
pDesc(_pCategory
->getFunction(i
));
111 if (!pDesc
->isHidden())
113 OUString
aFunction(pDesc
->getFunctionName());
114 OUString
sId(weld::toId(pDesc
));
116 m_xLbFunction
->insert(pCategoryIter
, -1, &aFunction
, &sId
, nullptr, nullptr, false,
117 m_xScratchIter
.get());
122 void FuncPage::SearchFunction(const OUString
& rFuncName
, const OUString
& rSearchString
,
123 TFunctionDesc pDesc
, const bool bSimilaritySearch
)
125 std::pair
<sal_Int32
, sal_Int32
> score
= std::make_pair(0, 0);
126 if (bSimilaritySearch
&& !utl::TextSearch::SimilaritySearch(rFuncName
, rSearchString
, score
))
128 if (!bSimilaritySearch
&& rFuncName
.indexOf(rSearchString
) < 0
129 && rSearchString
.indexOf(rFuncName
) < 0)
132 sFuncScores
.insert(std::make_pair(score
, std::make_pair(rFuncName
, pDesc
)));
135 //aStr is non-empty when user types in the search box to search some function
136 void FuncPage::UpdateFunctionList(const OUString
& aStr
)
138 m_xLbFunction
->clear();
139 m_xLbFunction
->freeze();
143 const sal_Int32 nSelPos
= m_xLbCategory
->get_active();
144 bool bCollapse
= nSelPos
== 1;
145 bool bFilter
= !aStr
.isEmpty();
146 // tdf#104487 - remember last used function category
147 m_nRememberedFunctionCategory
= nSelPos
;
149 if (!bFilter
|| nSelPos
== 0)
151 const IFunctionCategory
* pCategory
152 = weld::fromId
<const IFunctionCategory
*>(m_xLbCategory
->get_id(nSelPos
));
156 if (pCategory
== nullptr)
158 const sal_uInt32 nCount
= m_pFunctionManager
->getCount();
159 for (sal_uInt32 i
= 0; i
< nCount
; ++i
)
161 impl_addFunctions(m_pFunctionManager
->getCategory(i
), bCollapse
);
166 impl_addFunctions(pCategory
, false);
171 for (auto const& elem
: aLRUList
)
173 if (elem
) // may be null if a function is no longer available
175 OUString
aFunction(elem
->getFunctionName());
176 OUString
sId(weld::toId(elem
));
178 m_xLbFunction
->insert(nullptr, -1, &aFunction
, &sId
, nullptr, nullptr, false,
179 m_xScratchIter
.get());
186 SvtSysLocale aSysLocale
;
187 const CharClass
& rCharClass
= aSysLocale
.GetCharClass();
188 const OUString
aSearchStr(rCharClass
.uppercase(aStr
));
190 const sal_uInt32 nCategoryCount
= m_pFunctionManager
->getCount();
191 // Category listbox holds additional entries for Last Used and All, so
192 // the offset should be two but hard coded numbers are ugly...
193 const sal_Int32 nCategoryOffset
= m_xLbCategory
->get_count() - nCategoryCount
;
194 // If a real category (not Last Used or All) is selected, list only
195 // functions of that category. Else list all, LRU is handled above.
196 sal_Int32 nCatBeg
= (nSelPos
== -1 ? -1 : nSelPos
- nCategoryOffset
);
201 nCatEnd
= nCategoryCount
;
205 nCatEnd
= nCatBeg
+ 1;
207 for (sal_uInt32 i
= nCatBeg
; i
< nCatEnd
; ++i
)
209 const IFunctionCategory
* pCategory
= m_pFunctionManager
->getCategory(i
);
210 const sal_uInt32 nFunctionCount
= pCategory
->getCount();
211 for (sal_uInt32 j
= 0; j
< nFunctionCount
; ++j
)
213 TFunctionDesc
pDesc(pCategory
->getFunction(j
));
214 const OUString
aFunction(rCharClass
.uppercase(pDesc
->getFunctionName()));
215 SearchFunction(aFunction
, aSearchStr
, pDesc
, m_xSimilaritySearch
->get_active());
219 for (const auto& func
: sFuncScores
)
221 TFunctionDesc
pDesc(func
.second
.second
);
222 if (!pDesc
->isHidden())
224 const OUString
aCategory(pDesc
->getCategory()->getName());
225 const OUString
aFunction(func
.second
.first
);
226 const OUString
aFuncDescId(weld::toId(pDesc
));
227 weld::TreeIter
* pCategory
= FillCategoriesMap(aCategory
, bCollapse
);
229 m_xLbFunction
->insert(pCategory
, -1, &aFunction
, &aFuncDescId
, nullptr, nullptr,
230 false, m_xScratchIter
.get());
235 m_xLbFunction
->thaw();
236 // Ensure no function is selected so the Next button doesn't overwrite a
237 // function that is not in the list with an arbitrary selected one.
238 m_xLbFunction
->unselect_all();
240 if (bCollapse
&& bFilter
)
242 for (const auto& category
: mCategories
)
243 m_xLbFunction
->expand_row(*category
.second
);
247 SelTreeViewHdl(*m_xLbFunction
);
250 IMPL_LINK_NOARG(FuncPage
, SelComboBoxHdl
, weld::ComboBox
&, void)
252 if (m_xLbCategory
->get_active() == 0)
253 m_xLbFunctionSearchString
->set_text(u
""_ustr
);
254 m_xHelpButton
->set_sensitive(false);
255 OUString searchStr
= m_xLbFunctionSearchString
->get_text();
256 m_xLbFunction
->set_help_id(m_aHelpId
);
257 UpdateFunctionList(searchStr
);
260 IMPL_LINK_NOARG(FuncPage
, SelTreeViewHdl
, weld::TreeView
&, void)
262 const IFunctionDescription
* pDesc
= GetFuncDesc();
265 const OUString sHelpId
= pDesc
->getHelpId();
266 if (!sHelpId
.isEmpty())
267 m_xLbFunction
->set_help_id(sHelpId
);
269 bool bSensitivity
= weld::fromId
<const IFunctionDescription
*>(m_xLbFunction
->get_selected_id());
270 m_xHelpButton
->set_sensitive(bSensitivity
);
271 aSelectionLink
.Call(*this);
274 IMPL_LINK_NOARG(FuncPage
, DblClkHdl
, weld::TreeView
&, bool)
276 const OUString aString
= m_xLbFunction
->get_selected_text();
277 if (mCategories
.find(aString
) != mCategories
.end())
279 const auto& categoryRow
= *(mCategories
[aString
]);
280 if (m_xLbFunction
->get_row_expanded(categoryRow
))
281 m_xLbFunction
->collapse_row(categoryRow
);
283 m_xLbFunction
->expand_row(categoryRow
);
286 m_xLbFunctionSearchString
->set_text(OUString());
287 aDoubleClickLink
.Call(*this);
291 IMPL_LINK_NOARG(FuncPage
, ModifyHdl
, weld::Entry
&, void)
293 if (m_xLbCategory
->get_active() == 0)
295 m_xLbCategory
->set_active(1);
296 m_xHelpButton
->set_sensitive(false);
298 OUString searchStr
= m_xLbFunctionSearchString
->get_text();
299 UpdateFunctionList(searchStr
);
302 IMPL_LINK_NOARG(FuncPage
, SimilarityToggleHdl
, weld::Toggleable
&, void)
304 OUString searchStr
= m_xLbFunctionSearchString
->get_text();
305 UpdateFunctionList(searchStr
);
308 IMPL_LINK_NOARG(FuncPage
, SelHelpClickHdl
, weld::Button
&, void)
311 = weld::fromId
<const IFunctionDescription
*>(m_xLbFunction
->get_selected_id()))
313 if (Help
* pHelp
= Application::GetHelp())
315 const OUString sHelpId
= pDesc
->getHelpId();
316 if (!sHelpId
.isEmpty())
318 pHelp
->Start(sHelpId
);
324 void FuncPage::SetCategory(sal_Int32 nCat
)
326 // tdf#104487 - remember last used function category
327 m_nRememberedFunctionCategory
= nCat
;
328 m_xLbCategory
->set_active(nCat
);
329 UpdateFunctionList(OUString());
332 sal_Int32
FuncPage::GetFuncPos(const IFunctionDescription
* _pDesc
)
334 return m_xLbFunction
->find_id(weld::toId(_pDesc
));
337 void FuncPage::SetFunction(sal_Int32 nFunc
)
340 m_xLbFunction
->unselect_all();
342 m_xLbFunction
->select(nFunc
);
345 void FuncPage::SetFocus() { m_xLbFunction
->grab_focus(); }
347 sal_Int32
FuncPage::GetCategory() const { return m_xLbCategory
->get_active(); }
349 sal_Int32
FuncPage::GetCategoryEntryCount() const { return m_xLbCategory
->get_count(); }
351 sal_Int32
FuncPage::GetFunction() const { return m_xLbFunction
->get_selected_index(); }
353 sal_Int32
FuncPage::GetFunctionEntryCount() const { return m_xLbFunction
->n_children(); }
355 OUString
FuncPage::GetSelFunctionName() const { return m_xLbFunction
->get_selected_text(); }
357 const IFunctionDescription
* FuncPage::GetFuncDesc() const
359 if (GetFunction() == -1)
361 // not pretty, but hopefully rare
362 return weld::fromId
<const IFunctionDescription
*>(m_xLbFunction
->get_selected_id());
367 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */